QCamera 中 是需要进程间通讯的。因为 Camera Sensor 和 ISP Driver 在另一个Demon进程中,CameraHAL进程是需要和Demon进程通讯,那么使用的通讯方式是 UNIX Domain Socket IPC.
Domain Socket : http://blog.csdn.net/wzy_1988/article/details/44928691
一个Domain Socket示例:
/* server.c */ #include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <string.h> #include <sys/stat.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/unistd.h> #include <errno.h> // the max connection number of the server #define MAX_CONNECTION_NUMBER 5 typedef enum { UNIX_SOCK_TYPE_UDP, UNIX_SOCK_TYPE_TCP, } unix_sock_type_t; int unix_socket_create(const char *servername, unix_sock_type_t sock_type) { int socket_fd; struct sockaddr_un addr_un; int sktype; int rc; switch (sock_type) { case UNIX_SOCK_TYPE_UDP: sktype = SOCK_DGRAM; break; case UNIX_SOCK_TYPE_TCP: sktype = SOCK_STREAM; break; default: printf("[ %s, %d ] unknow socket type = %d\n", __func__, __LINE__, sock_type); return -1; } socket_fd = socket(AF_UNIX, sktype, 0); if (socket_fd < 0) { printf("[ %s, %d ] create socket fd = %d failed.\n", __func__, __LINE__, socket_fd); return socket_fd; } memset(&addr_un, 0, sizeof(addr_un)); addr_un.sun_family = AF_UNIX; strncpy(addr_un.sun_path, servername, sizeof(addr_un.sun_path)-1); unlink(servername); // in case it already exists. printf("addr_un.sun_path: %s\n", addr_un.sun_path); rc = bind(socket_fd, (struct sockaddr *) &addr_un, sizeof(addr_un)); if (rc < 0) { close(socket_fd); socket_fd = -1; printf("[ %s, %d ] bind error : %s ", __func__, __LINE__, strerror(errno)); } return socket_fd; } void unix_socket_close(int fd) { if (fd >= 0) { close(fd); } } int unix_socket_listen(int fd) { return listen(fd, MAX_CONNECTION_NUMBER); } int main(void) { int socket_fd = 0; int accept_socket_fd = 0; int rc = 0; char read_buf[256]; ssize_t read_length; // 1. create domain socket and bind socket_fd = unix_socket_create("test.socket", UNIX_SOCK_TYPE_TCP); if (socket_fd < 0) { printf("create domain socket error.\n"); exit(0); } // 2. listen rc = unix_socket_listen(socket_fd); if (rc < 0) { printf("listen domain socket error.\n"); goto err; } // 3. accept and read while (1) { accept_socket_fd = accept(socket_fd, NULL, NULL); if (accept_socket_fd < 0) { printf("accept error.\n"); usleep(1000 * 50); continue; } while ((read_length = read(accept_socket_fd, read_buf, sizeof(read_buf))) > 0) { printf("read %ld bytes: %s\n", read_length, read_buf); } if (read_length == -1) { printf("read error.\n"); exit(-1); } else if (read_length == 0) { printf("END...\n"); break; } } err: unix_socket_close(socket_fd); return 0; }
/* client.c */ #include <stdio.h> #include <stddef.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/unistd.h> #include <errno.h> #include <string.h> typedef enum { UNIX_SOCK_TYPE_UDP, UNIX_SOCK_TYPE_TCP, } unix_sock_type_t; int unix_socket_create(const char *servername, unix_sock_type_t sock_type) { int socket_fd; struct sockaddr_un addr_un; int sktype; int rc; switch (sock_type) { case UNIX_SOCK_TYPE_UDP: sktype = SOCK_DGRAM; break; case UNIX_SOCK_TYPE_TCP: sktype = SOCK_STREAM; break; default: printf("[ %s, %d ] unknow socket type = %d\n", __func__, __LINE__, sock_type); return -1; } socket_fd = socket(AF_UNIX, sktype, 0); if (socket_fd < 0) { printf("[ %s, %d ] create socket fd = %d failed.\n", __func__, __LINE__, socket_fd); return socket_fd; } memset(&addr_un, 0, sizeof(addr_un)); addr_un.sun_family = AF_UNIX; strncpy(addr_un.sun_path, servername, sizeof(addr_un.sun_path)-1); rc = connect(socket_fd, (struct sockaddr *)&addr_un, sizeof(addr_un)); if (0 != rc) { close(socket_fd); socket_fd = -1; printf("[ %s, %d ] bind error : %s ", __func__, __LINE__, strerror(errno)); } return socket_fd; } void unix_socket_close(int fd) { close(fd); } int main(void) { int socket_fd = 0; int rc = 0; char buf[256]; // 1. create domain socket and bind socket_fd = unix_socket_create("test.socket", UNIX_SOCK_TYPE_TCP); if (socket_fd < 0) { printf("create domain socket error.\n"); exit(0); } while (scanf("%s", buf) != EOF) { if (write(socket_fd, buf, sizeof(buf)) < 0) { printf("write error\n"); exit(-1); } } return 0; }
下边看 mm_camera_sock 相关方法,该部分为 UNIX Domain Socket 的 Client端。
/* 重要结构体和用户API */ typedef enum { MM_CAMERA_SOCK_TYPE_UDP, MM_CAMERA_SOCK_TYPE_TCP, } mm_camera_sock_type_t; typedef union { struct sockaddr addr; struct sockaddr_un addr_un; } mm_camera_sock_addr_t; int mm_camera_socket_create(int cam_id, mm_camera_sock_type_t sock_type); // 创建 socket 并且建立连接 int mm_camera_socket_sendmsg( int fd, void *msg, size_t buf_size, int sendfd); int mm_camera_socket_bundle_sendmsg( // 发送消息 int fd, void *msg, size_t buf_size, int sendfds[CAM_MAX_NUM_BUFS_PER_STREAM], int num_fds); int mm_camera_socket_recvmsg( // 接收消息 int fd, void *msg, uint32_t buf_size, int *rcvdfd); void mm_camera_socket_close(int fd); // 关闭 socket 连接
int mm_camera_socket_create(int cam_id, mm_camera_sock_type_t sock_type) { int socket_fd; mm_camera_sock_addr_t sock_addr; int sktype; int rc; switch (sock_type) { case MM_CAMERA_SOCK_TYPE_UDP: sktype = SOCK_DGRAM; break; case MM_CAMERA_SOCK_TYPE_TCP: sktype = SOCK_STREAM; break; default: CDBG_ERROR("%s: unknown socket type =%d", __func__, sock_type); return -1; } socket_fd = socket(AF_UNIX, sktype, 0); // 创建 socket if (socket_fd < 0) { CDBG_ERROR("%s: error create socket fd =%d", __func__, socket_fd); return socket_fd; } memset(&sock_addr, 0, sizeof(sock_addr)); sock_addr.addr_un.sun_family = AF_UNIX; snprintf(sock_addr.addr_un.sun_path, UNIX_PATH_MAX, QCAMERA_DUMP_FRM_LOCATION"cam_socket%d", cam_id); rc = connect(socket_fd, &sock_addr.addr, sizeof(sock_addr.addr_un)); // 建立连接 if (0 != rc) { close(socket_fd); socket_fd = -1; CDBG_ERROR("%s: socket_fd=%d %s ", __func__, socket_fd, strerror(errno)); } CDBG("%s: socket_fd=%d %s", __func__, socket_fd, sock_addr.addr_un.sun_path); return socket_fd; } void mm_camera_socket_close(int fd) { if (fd >= 0) { close(fd); // 关闭 socket } }
在了解 mm_camera_socket_sendmsg ,mm_camera_socket_bundle_sendmsg, mm_camera_socket_recvmsg 之前,要说明2个重要的函数: sendmsg 和 recvmsg
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
在这两个函数中,对于数据的封装使用结构体 struct msghdr ,所以有必要看下 struct msghdr 的定义及意义。
struct iovec { /* Scatter/gather array items */ void *iov_base; /* Starting address */ size_t iov_len; /* Number of bytes to transfer */ }; struct msghdr { void *msg_name; /* optional address */ socklen_t msg_namelen; /* size of address */ struct iovec *msg_iov; /* scatter/gather array */ size_t msg_iovlen; /* # elements in msg_iov */ void *msg_control; /* ancillary data, see below */ size_t msg_controllen; /* ancillary data buffer len */ int msg_flags; /* flags on received message */ };
1. msg_name : 在 unconnected socket 中用于为 datagram (数据报文) 指定 target address. 在 connected socket 中,该字段没有意义,应该置NULL,msg_nameleng 为0
2. msg_control : 用于封装 control-related messages or miscellaneous ancillary data. (杂项辅助资料)
control message 封装:
struct cmsghdr { socklen_t cmsg_len; /* data byte count, including hdr */ int cmsg_level; /* originating protocol */ int cmsg_type; /* protocol-specific type */ /* followed by unsigned char cmsg_data[]; */ }; cmghdr 访问接口: CMSG_FIRSTHDR() returns a pointer to the first cmsghdr in the ancillary data buffer associated with the passed msghdr. CMSG_NXTHDR() returns the next valid cmsghdr after the passed cmsghdr. It returns NULL when there isn‘t enough space left in the buffer. CMSG_ALIGN(), given a length, returns it including the required alignment. This is a constant expression. CMSG_SPACE() returns the number of bytes an ancillary element with payload of the passed data length occupies. This is a constant expression. CMSG_DATA() returns a pointer to the data portion of a cmsghdr. CMSG_LEN() returns the value to store in the cmsg_len member of the cmsghdr structure, taking into account any necessary alignment. It takes the data length as an argument. This is a constant expression.
对于上述接口,细节部分可以参考 man 手册.int mm_camera_socket_sendmsg(
int fd, // socket fd void *msg, // msg size_t buf_size, // msg size int sendfd) // 需要发送的 fd,即该 fd 作为消息的一部分需要发送。发送 fd 和发送普通消息的方式当然是不一样的。 { struct msghdr msgh; struct iovec iov[1]; struct cmsghdr * cmsghp = NULL; // ancillary data(辅助控制杂项消息) 的封装结构体 char control[CMSG_SPACE(sizeof(int))]; // control 用来存放 sendfd ,即控制消息 if (msg == NULL) { CDBG("%s: msg is NULL", __func__); return -1; } memset(&msgh, 0, sizeof(msgh)); msgh.msg_name = NULL; msgh.msg_namelen = 0; iov[0].iov_base = msg; iov[0].iov_len = buf_size; msgh.msg_iov = iov; msgh.msg_iovlen = 1; // 将普通消息封装好 CDBG("%s: iov_len=%llu", __func__, (unsigned long long int)iov[0].iov_len); msgh.msg_control = NULL; msgh.msg_controllen = 0; /* if sendfd is valid, we need to pass it through control msg */ if( sendfd >= 0) { // 如何 sendfd 有效,需要通过 socket 发送,但是是通过 control msg ,而不是普通消息 msgh.msg_control = control; msgh.msg_controllen = sizeof(control); cmsghp = CMSG_FIRSTHDR(&msgh); // CMSG_FIRSTHDR 返回指向 ancillary data buffer 的第一个 cmsghdr 的指针。注意传入的参数 if (cmsghp != NULL) { CDBG("%s: Got ctrl msg pointer", __func__); cmsghp->cmsg_level = SOL_SOCKET; cmsghp->cmsg_type = SCM_RIGHTS; cmsghp->cmsg_len = CMSG_LEN(sizeof(int)); *((int *)CMSG_DATA(cmsghp)) = sendfd; // CMSG_DATA 返回 cmsghdr 的数据域指针。 CDBG("%s: cmsg data=%d", __func__, *((int *) CMSG_DATA(cmsghp))); } else { CDBG("%s: ctrl msg NULL", __func__); return -1; } } return sendmsg(fd, &(msgh), 0); // 发送消息 }
// 和上一个函数的区别就是要发送多个 sendfds。
int mm_camera_socket_bundle_sendmsg( int fd, void *msg, size_t buf_size, int sendfds[CAM_MAX_NUM_BUFS_PER_STREAM], int numfds) { struct msghdr msgh; struct iovec iov[1]; struct cmsghdr * cmsghp = NULL; char control[CMSG_SPACE(sizeof(int) * numfds)]; int *fds_ptr = NULL; if (msg == NULL) { CDBG("%s: msg is NULL", __func__); return -1; } memset(&msgh, 0, sizeof(msgh)); msgh.msg_name = NULL; msgh.msg_namelen = 0; iov[0].iov_base = msg; iov[0].iov_len = buf_size; msgh.msg_iov = iov; msgh.msg_iovlen = 1; CDBG("%s: iov_len=%llu", __func__, (unsigned long long int)iov[0].iov_len); msgh.msg_control = NULL; msgh.msg_controllen = 0; /* if numfds is valid, we need to pass it through control msg */ if (numfds > 0) { msgh.msg_control = control; msgh.msg_controllen = sizeof(control); cmsghp = CMSG_FIRSTHDR(&msgh); if (cmsghp != NULL) { cmsghp->cmsg_level = SOL_SOCKET; cmsghp->cmsg_type = SCM_RIGHTS; cmsghp->cmsg_len = CMSG_LEN(sizeof(int) * numfds); fds_ptr = (int*) CMSG_DATA(cmsghp); memcpy(fds_ptr, sendfds, sizeof(int) * numfds); } else { CDBG_ERROR("%s: ctrl msg NULL", __func__); return -1; } } return sendmsg(fd, &(msgh), 0); }
// 和 sendmsg 类似,不再分析
int mm_camera_socket_recvmsg( int fd, void *msg, uint32_t buf_size, int *rcvdfd) { struct msghdr msgh; struct iovec iov[1]; struct cmsghdr *cmsghp = NULL; char control[CMSG_SPACE(sizeof(int))]; int rcvd_fd = -1; int rcvd_len = 0; if ( (msg == NULL) || (buf_size <= 0) ) { CDBG_ERROR(" %s: msg buf is NULL", __func__); return -1; } memset(&msgh, 0, sizeof(msgh)); msgh.msg_name = NULL; msgh.msg_namelen = 0; msgh.msg_control = control; msgh.msg_controllen = sizeof(control); iov[0].iov_base = msg; iov[0].iov_len = buf_size; msgh.msg_iov = iov; msgh.msg_iovlen = 1; if ( (rcvd_len = recvmsg(fd, &(msgh), 0)) <= 0) { CDBG_ERROR(" %s: recvmsg failed", __func__); return rcvd_len; } CDBG("%s: msg_ctrl %p len %zd", __func__, msgh.msg_control, msgh.msg_controllen); if( ((cmsghp = CMSG_FIRSTHDR(&msgh)) != NULL) && (cmsghp->cmsg_len == CMSG_LEN(sizeof(int))) ) { if (cmsghp->cmsg_level == SOL_SOCKET && cmsghp->cmsg_type == SCM_RIGHTS) { CDBG("%s: CtrlMsg is valid", __func__); rcvd_fd = *((int *) CMSG_DATA(cmsghp)); CDBG("%s: Receieved fd=%d", __func__, rcvd_fd); } else { CDBG_ERROR("%s: Unexpected Control Msg. Line=%d", __func__, __LINE__); } } if (rcvdfd) { *rcvdfd = rcvd_fd; } return rcvd_len; }