mm_camera_sock

  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; }

 

posted @ 2018-01-20 12:40  rain-dot  阅读(336)  评论(1编辑  收藏  举报