美文网首页
相机——解决多摄像头id错乱的问题

相机——解决多摄像头id错乱的问题

作者: 王志强_9380 | 来源:发表于2020-07-07 16:26 被阅读0次

    我们在之前的文章中,通过相机的打开流程,知道,相机的id号,就是我们Camera.open(cameraid)里的这个id,实际不是与设备固定对应的。
    当我们机器里有多个设像头的时候,如果其中一个坏了,那么机器启动的时候,它还是按照0、1、2、3这种顺序去对应摄像头驱动的设备文件(就是之前的vedio2,vedio3,vedio4等),那么实际上,这个id号就不知道打开的是哪个摄像头了。
    如果要固定对应id号,我们就需要底层告诉我们那些vedio对应的是哪个硬件
    我们看下之前的初始化代码

    hardware\qcom\camera\QCamera2\stack\mm-camera-interface\src\mm_camera_interface.c
    
    uint8_t get_num_of_cameras()
    {
        ......省略代码......   
        while (1) {
            uint32_t num_entities = 1U;
            char dev_name[32];
    
            snprintf(dev_name, sizeof(dev_name), "/dev/media%d", num_media_devices);
            dev_fd = open(dev_name, O_RDWR | O_NONBLOCK);
            if (dev_fd < 0) {
                LOGD("Done discovering media devices\n");
                break;
            }
            num_media_devices++;
            rc = ioctl(dev_fd, MEDIA_IOC_DEVICE_INFO, &mdev_info);
            ......省略代码......   
            while (1) {
                ......省略代码......   
                rc = ioctl(dev_fd, MEDIA_IOC_ENUM_ENTITIES, &entity);
                if (rc < 0) {
                    LOGD("Done enumerating media entities");
                    rc = 0;
                    break;
                }
                LOGD("entity name %s type %d group id %d",
                    entity.name, entity.type, entity.group_id);
                if (entity.type == MEDIA_ENT_T_V4L2_SUBDEV &&
                    entity.group_id == MSM_CAMERA_SUBDEV_SENSOR_INIT) {
                    snprintf(subdev_name, sizeof(dev_name), "/dev/%s", entity.name);
                    break;
                }
            }
            close(dev_fd);
            dev_fd = -1;
        }
    
        /* Open sensor_init subdev */
        sd_fd = open(subdev_name, O_RDWR);
        ......省略代码......   
        if (ioctl(sd_fd, VIDIOC_MSM_SENSOR_INIT_CFG, &cfg) < 0) {
            LOGE("failed");
        }
        ......省略代码......   
        while (1) {
            ......省略代码......   
            dev_fd = open(dev_name, O_RDWR | O_NONBLOCK);
            ......省略代码......   
            rc = ioctl(dev_fd, MEDIA_IOC_DEVICE_INFO, &mdev_info);
            ......省略代码......   
            while (1) {
                ......省略代码......   
                rc = ioctl(dev_fd, MEDIA_IOC_ENUM_ENTITIES, &entity);
                ......省略代码......   
                    strlcpy(g_cam_ctrl.video_dev_name[num_cameras],
                         entity.name, sizeof(entity.name));
                ......省略代码......   
            }
            ......省略代码......   
        }
        ......省略代码......   
    }
    

    把上面的代码简化一下

    snprintf(dev_name, sizeof(dev_name), "/dev/media%d", num_media_devices);
    dev_fd = open(dev_name, O_RDWR | O_NONBLOCK);
    ioctl(dev_fd, MEDIA_IOC_DEVICE_INFO, &mdev_info);
    ioctl(dev_fd, MEDIA_IOC_ENUM_ENTITIES, &entity);
    snprintf(subdev_name, sizeof(dev_name), "/dev/%s", entity.name);
    sd_fd = open(subdev_name, O_RDWR);
    ioctl(sd_fd, VIDIOC_MSM_SENSOR_INIT_CFG, &cfg)
    dev_fd = open(dev_name, O_RDWR | O_NONBLOCK);
    ioctl(dev_fd, MEDIA_IOC_DEVICE_INFO, &mdev_info)
    ioctl(dev_fd, MEDIA_IOC_ENUM_ENTITIES, &entity)

    首先我们来看下这个ioctl是在哪里定义的

    MEDIA_IOC_DEVICE_INFO

    搜索一下关键字MEDIA_IOC_DEVICE_INFO
    发现在media-device.c中有用到

    kernel\msm-3.18\drivers\media\media-device.c
    
    int __must_check __media_device_register(struct media_device *mdev,
                         struct module *owner)
    {
        ......省略代码......   
        /* Register the device node. */
        mdev->devnode.fops = &media_device_fops;
        ......省略代码......   
    }
    
    static const struct media_file_operations media_device_fops = {
        .owner = THIS_MODULE,
        .open = media_device_open,
            //调用ioctl实际是调用方法中的media_device_ioctl
        .ioctl = media_device_ioctl, 
    #ifdef CONFIG_COMPAT
        .compat_ioctl = media_device_compat_ioctl,
    #endif /* CONFIG_COMPAT */
        .release = media_device_close,
    };
    
    static long media_device_ioctl(struct file *filp, unsigned int cmd,
                       unsigned long arg)
    {
        struct media_devnode *devnode = media_devnode_data(filp);
        struct media_device *dev = to_media_device(devnode);
        long ret;
    
        switch (cmd) {
        case MEDIA_IOC_DEVICE_INFO:
            ret = media_device_get_info(dev,
                    (struct media_device_info __user *)arg);
            break;
    
        case MEDIA_IOC_ENUM_ENTITIES:
            ret = media_device_enum_entities(dev,
                    (struct media_entity_desc __user *)arg);
            break;
    ......省略代码......
    }
    
    static int media_device_get_info(struct media_device *dev,
                     struct media_device_info __user *__info)
    {
        struct media_device_info info;
    
        memset(&info, 0, sizeof(info));
    
        strlcpy(info.driver, dev->dev->driver->name, sizeof(info.driver));
        strlcpy(info.model, dev->model, sizeof(info.model));
        strlcpy(info.serial, dev->serial, sizeof(info.serial));
        strlcpy(info.bus_info, dev->bus_info, sizeof(info.bus_info));
    
        info.media_version = MEDIA_API_VERSION;
        info.hw_revision = dev->hw_revision;
        info.driver_version = dev->driver_version;
    
        if (copy_to_user(__info, &info, sizeof(*__info)))
            return -EFAULT;
        return 0;
    }
    
    static long media_device_enum_entities(struct media_device *mdev,
                           struct media_entity_desc __user *uent)
    {
        struct media_entity *ent;
        struct media_entity_desc u_ent;
    
        memset(&u_ent, 0, sizeof(u_ent));
        if (copy_from_user(&u_ent.id, &uent->id, sizeof(u_ent.id)))
            return -EFAULT;
    
        ent = find_entity(mdev, u_ent.id);
    
        if (ent == NULL)
            return -EINVAL;
    
        u_ent.id = ent->id;
        if (ent->name)
            strlcpy(u_ent.name, ent->name, sizeof(u_ent.name));
        u_ent.type = ent->type;
        u_ent.revision = ent->revision;
        u_ent.flags = ent->flags;
        u_ent.group_id = ent->group_id;
        u_ent.pads = ent->num_pads;
        u_ent.links = ent->num_links - ent->num_backlinks;
        memcpy(&u_ent.raw, &ent->info, sizeof(ent->info));
        if (copy_to_user(uent, &u_ent, sizeof(u_ent)))
            return -EFAULT;
        return 0;
    }
    
    kernel\msm-3.18\include\uapi\linux\media.h
    struct media_entity_desc {
        ......省略代码......
    };
    
    media_device_get_info

    代码中看到,把dev中的内容赋值给__info,实际上就是赋值给ioctl(dev_fd, MEDIA_IOC_DEVICE_INFO, &mdev_info);中的mdev_info,看下这个字段的作用

    #define MSM_CONFIGURATION_NAME     "msm_config"
    
    if (strncmp(mdev_info.model, MSM_CONFIGURATION_NAME,
      sizeof(mdev_info.model)) != 0) {
        close(dev_fd);
        dev_fd = -1;
        continue;
    }
    

    也就是说,这个mdev_info.model是 "msm_config"时就继续往下走,否则就打开下一个media文件,后面再看原因

    media_device_enum_entities

    我们看到如果传进来的uent没有id值则报参数错误,然后通过find_entity找到设备的entity信息,实际上最终就是给传进来的uent填充信息,我们接着看

    static struct media_entity *find_entity(struct media_device *mdev, u32 id)
    {
        struct media_entity *entity;
        int next = id & MEDIA_ENT_ID_FLAG_NEXT;
    
        id &= ~MEDIA_ENT_ID_FLAG_NEXT;
    
        spin_lock(&mdev->lock);
    
        media_device_for_each_entity(entity, mdev) {
            if ((entity->id == id && !next) ||
                (entity->id > id && next)) {
                spin_unlock(&mdev->lock);
                return entity;
            }
        }
    
        spin_unlock(&mdev->lock);
    
        return NULL;
    }
    

    看下media_device_for_each_entity定义

    kernel\msm-3.18\include\media\media-device.h
    struct media_device {
        ......省略代码......  
        u32 entity_id;
        struct list_head entities;
        ......省略代码......  
    };
    #define media_device_for_each_entity(entity, mdev) list_for_each_entry(entity, &(mdev)->entities, list)
    

    可以看到,就是遍历了media_device 中的list_head,然后找到这个entity返回去。现在我们就是要找这个media_device 是怎么赋值的,在上面的代码中我们看到传进来的dev是这么初始化的

    struct media_devnode *devnode = media_devnode_data(filp);
    struct media_device *dev = to_media_device(devnode);

    kernel\msm-3.18\include\media\media-devnode.h
    struct media_devnode {
        /* device ops */
        const struct media_file_operations *fops;
    
        /* sysfs */
        struct device dev;      /* media device */
        struct cdev cdev;       /* character device */
        struct device *parent;      /* device parent */
    
        /* device info */
        int minor;
        unsigned long flags;        /* Use bitops to access flags */
    
        /* callbacks */
        void (*release)(struct media_devnode *mdev);
    };
    static inline struct media_devnode *media_devnode_data(struct file *filp)
    {
        return filp->private_data;
    }
    
    kernel\msm-3.18\include\media\media-device.h
    /* media_devnode to media_device */
    #define to_media_device(node) container_of(node, struct media_device, devnode)
    

    结果到头来还是通过这个文件句柄来初始化的,这个后面再看
    这个方法赋值了ioctl(dev_fd, MEDIA_IOC_ENUM_ENTITIES, &entity);中的entity,看下这个变量的作用

    if (entity.type == MEDIA_ENT_T_V4L2_SUBDEV &&
        entity.group_id == MSM_CAMERA_SUBDEV_SENSOR_INIT) {
        snprintf(subdev_name, sizeof(dev_name), "/dev/%s", entity.name);
        break;
    }
    

    看到,如果这个设备的group_id为MSM_CAMERA_SUBDEV_SENSOR_INIT,就得到subdev_name,并跳出大循环,通过打印,看到这个subdev_name是 /dev/v4l-subdev13。

    结合上面循环的代码,可以得出,前部分这段代码,就是先循环查找 /dev/media* 文件,找到model为MSM_CONFIGURATION_NAME的media文件,然后通过ioctl去读取这个media文件的entity,如果满足type == MEDIA_ENT_T_V4L2_SUBDEV && group_id == MSM_CAMERA_SUBDEV_SENSOR_INIT。就取出entity.name,我们接着往后看

    sd_fd = open(subdev_name, O_RDWR);
    ioctl(sd_fd, VIDIOC_MSM_SENSOR_INIT_CFG, &cfg)
    搜索发现VIDIOC_MSM_SENSOR_INIT_CFG在msm_sensor_init.c中被使用

    VIDIOC_MSM_SENSOR_INIT_CFG

    kernel\msm-3.18\drivers\media\platform\msm\camera_v2\sensor\msm_sensor_init.c
    static struct v4l2_subdev_core_ops msm_sensor_init_subdev_core_ops = {
        .ioctl = msm_sensor_init_subdev_ioctl,
    };
    
    static long msm_sensor_init_subdev_ioctl(struct v4l2_subdev *sd,
        unsigned int cmd, void *arg)
    {
        long rc = 0;
        struct msm_sensor_init_t *s_init = v4l2_get_subdevdata(sd);
        CDBG("Enter");
    
        /* Validate input parameters */
        if (!s_init) {
            pr_err("failed: s_init %pK", s_init);
            return -EINVAL;
        }
    
        switch (cmd) {
        case VIDIOC_MSM_SENSOR_INIT_CFG:
            rc = msm_sensor_driver_cmd(s_init, arg);
            break;
    
        default:
            pr_err_ratelimited("default\n");
            break;
        }
    
        return rc;
    }
    
    static int32_t msm_sensor_driver_cmd(struct msm_sensor_init_t *s_init,
        void *arg)
    {
        int32_t                      rc = 0;
        struct sensor_init_cfg_data *cfg = (struct sensor_init_cfg_data *)arg;
    
        /* Validate input parameters */
        if (!s_init || !cfg) {
            pr_err("failed: s_init %pK cfg %pK", s_init, cfg);
            return -EINVAL;
        }
    
        switch (cfg->cfgtype) {
        case CFG_SINIT_PROBE:
            mutex_lock(&s_init->imutex);
            s_init->module_init_status = 0;
            rc = msm_sensor_driver_probe(cfg->cfg.setting,
                &cfg->probed_info,
                cfg->entity_name);
            mutex_unlock(&s_init->imutex);
            if (rc < 0)
                pr_err("%s failed (non-fatal) rc %d", __func__, rc);
            break;
    
        case CFG_SINIT_PROBE_DONE:
            s_init->module_init_status = 1;
            wake_up(&s_init->state_wait);
            break;
    
        case CFG_SINIT_PROBE_WAIT_DONE:
            rc = msm_sensor_wait_for_probe_done(s_init);
            break;
    
        default:
            pr_err("default");
            break;
        }
    
        return rc;
    }
    

    我们之前传进去的参数是CFG_SINIT_PROBE_WAIT_DONE,看下这个

    static int msm_sensor_wait_for_probe_done(struct msm_sensor_init_t *s_init)
    {
        int rc;
        int tm = 20000;
        if (s_init->module_init_status == 1) {
            CDBG("msm_cam_get_module_init_status -2\n");
            return 0;
        }
        rc = wait_event_timeout(s_init->state_wait,
            (s_init->module_init_status == 1), msecs_to_jiffies(tm));
        if (rc == 0) {
            pr_err("%s:%d wait timeout\n", __func__, __LINE__);
            rc = -1;
        }
    
        return rc;
    }
    

    很明显是在等待某些操作的完成,看下这个s_init是怎么获取的
    struct msm_sensor_init_t *s_init = v4l2_get_subdevdata(sd);

    kernel\msm-3.18\include\media\v4l2-subdev.h
    static inline void *v4l2_get_subdevdata(const struct v4l2_subdev *sd)
    {
        return sd->dev_priv;
    }
    

    猜测是等待CFG_SINIT_PROBE执行完成,这个命令是在sensor_init.c中执行的

    vendor\qcom\proprietary\mm-camera\mm-camera2\media-controller\modules\sensors\module\sensor_init.c
    
    static boolean sensor_probe(module_sensor_ctrl_t *module_ctrl, int32_t fd,
      const char *sensor_name, char *path, struct xmlCameraConfigInfo *xmlConfig)
    {
      ......省略代码......  
      cfg.cfgtype = CFG_SINIT_PROBE;
      cfg.cfg.setting = slave_info;
      if (ioctl(fd, VIDIOC_MSM_SENSOR_INIT_CFG, &cfg) < 0) {
        SERR("[%s]CFG_SINIT_PROBE failed",sensor_name);
        ret = FALSE;
        goto ERROR;
      }
      ......省略代码......  
    }
    
    

    这里就是初始化的sensor,涉及到驱动方面,不深究,我们往后看CFG_SINIT_PROBE,调用的msm_sensor_driver_probe

    
    static int32_t msm_sensor_driver_create_i2c_v4l_subdev
                (struct msm_sensor_ctrl_t *s_ctrl)
    {
        struct i2c_client *client = s_ctrl->sensor_i2c_client->client;
        ......省略代码......  
        rc = camera_init_v4l2(&client->dev, &session_id);
        ......省略代码......  
    }
    
    static int32_t msm_sensor_driver_create_v4l_subdev
                (struct msm_sensor_ctrl_t *s_ctrl)
    {
        int32_t rc = 0;
        uint32_t session_id = 0;
    
        rc = camera_init_v4l2(&s_ctrl->pdev->dev, &session_id);
        ......省略代码......    
    }
    
    int32_t msm_sensor_driver_probe(void *setting,
        struct msm_sensor_info_t *probed_info, char *entity_name)
    {
        ......省略代码......    
        struct msm_camera_sensor_slave_info32 *slave_info32 =
            kzalloc(sizeof(*slave_info32), GFP_KERNEL);
        if (!slave_info32) {
            pr_err("failed: no memory for slave_info32 %pK\n",
                slave_info32);
            rc = -ENOMEM;
            goto free_slave_info;
        }
        if (copy_from_user((void *)slave_info32, setting,
            sizeof(*slave_info32))) {
                pr_err("failed: copy_from_user");
                rc = -EFAULT;
                kfree(slave_info32);
                goto free_slave_info;
            }
    
        ......省略代码......    
        slave_info->addr_type = slave_info32->addr_type;
            //这个是底层传上来的设备id,这个是固定的
        slave_info->camera_id = slave_info32->camera_id;
        ......省略代码......
        s_ctrl = g_sctrl[slave_info->camera_id];    
        ......省略代码......  
        if (s_ctrl->sensor_device_type == MSM_CAMERA_PLATFORM_DEVICE)
            rc = msm_sensor_driver_create_v4l_subdev(s_ctrl);
        else
            rc = msm_sensor_driver_create_i2c_v4l_subdev(s_ctrl);
    }
    

    上面的代码可以看到,最终都调用了camera_init_v4l2

    kernel\msm-3.18\drivers\media\platform\msm\camera_v2\camera\camera.c
    
    int camera_init_v4l2(struct device *dev, unsigned int *session, enum msm_sensor_camera_id_t camera_id)
    {
        ......省略代码......  
    
        strlcpy(v4l2_dev->mdev->model, MSM_CAMERA_NAME,
                    sizeof(v4l2_dev->mdev->model));
    
        v4l2_dev->mdev->dev = dev;
    
        rc = media_device_register(v4l2_dev->mdev);
    
        ......省略代码......  
    
        pvdev->vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
        pvdev->vdev->entity.group_id = QCAMERA_VNODE_GROUP_ID;
    
        v4l2_dev->notify = NULL;
        pvdev->vdev->v4l2_dev = v4l2_dev;
    
        rc = v4l2_device_register(dev, pvdev->vdev->v4l2_dev);
    
        strlcpy(pvdev->vdev->name, "msm-sensor", sizeof(pvdev->vdev->name));
        pvdev->vdev->release  = video_device_release;
        pvdev->vdev->fops     = &camera_v4l2_fops;
        pvdev->vdev->ioctl_ops = &camera_v4l2_ioctl_ops;
        pvdev->vdev->minor     = -1;
        pvdev->vdev->vfl_type  = VFL_TYPE_GRABBER;
        rc = video_register_device(pvdev->vdev,
            VFL_TYPE_GRABBER, -1);
    
        ......省略代码......  
    }
    

    上面列出了一些主要代码,我们依次来看

    media_device_register 多媒体设备注册
    kernel\msm-3.18\drivers\media\media-device.c
    
    int __must_check __media_device_register(struct media_device *mdev,
                         struct module *owner)
    {
        int ret;
    
        if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0))
            return -EINVAL;
    
        mdev->entity_id = 1;
        INIT_LIST_HEAD(&mdev->entities);
        spin_lock_init(&mdev->lock);
        mutex_init(&mdev->graph_mutex);
    
        /* Register the device node. */
        mdev->devnode.fops = &media_device_fops;  
        /*下面是绑定父设备 */
        mdev->devnode.parent = mdev->dev;
        mdev->devnode.release = media_device_release;
        ret = media_devnode_register(&mdev->devnode, owner);
        if (ret < 0)
            return ret;
        /*创建了属性文件,我们可以直接通过属性文件来操作这个文件节点 */
        ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);
        if (ret < 0) {
            media_devnode_unregister(&mdev->devnode);
            return ret;
        }
    
        return 0;
    }
    
    v4l2_device_register v4l2设备注册
    int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
    {
        if (v4l2_dev == NULL)
            return -EINVAL;
    
        INIT_LIST_HEAD(&v4l2_dev->subdevs);
        spin_lock_init(&v4l2_dev->lock);
        mutex_init(&v4l2_dev->ioctl_lock);
        v4l2_prio_init(&v4l2_dev->prio);
        kref_init(&v4l2_dev->ref);
        get_device(dev);
        /*下面将当前设备的对象,赋值给v4l2_dev->dev中,这样的话当前的设备就成了v4l2设备,由于当前设备已经注册过了设备文件,所以后续不需要在重新注册设备文件。*/
        v4l2_dev->dev = dev;
        if (dev == NULL) {
            /* If dev == NULL, then name must be filled in by the caller */
            if (WARN_ON(!v4l2_dev->name[0]))
                return -EINVAL;
            return 0;
        }
    
        /* Set name to driver name + device name if it is empty. */
        if (!v4l2_dev->name[0])
            snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
                dev->driver->name, dev_name(dev));
        /*将v4l2设备对象,保存到dev设备中,这个dev可以是platform、char、block等设,我们可以使用dev_get_drvdata(dev)获取到v4l2对象。*/
        if (!dev_get_drvdata(dev))
            dev_set_drvdata(dev, v4l2_dev);
        return 0;
    }
    

    上面方法的作用就是将当前设备驱动和v4l2对象进行捆绑,便于挂接子设备

    video_register_device
    kernel\msm-3.18\drivers\media\v4l2-core\v4l2-dev.c
    
    int __video_register_device(struct video_device *vdev, int type, int nr,
            int warn_if_nr_in_use, struct module *owner)
    {
        
        ......省略代码......    
        /* Part 1: check device type */
        switch (type) {
        case VFL_TYPE_GRABBER:
            name_base = "video";
            break;
        case VFL_TYPE_VBI:
            name_base = "vbi";
            break;
        case VFL_TYPE_RADIO:
            name_base = "radio";
            break;
        case VFL_TYPE_SUBDEV:
            name_base = "v4l-subdev";
            break;
        case VFL_TYPE_SDR:
            /* Use device name 'swradio' because 'sdr' was already taken. */
            name_base = "swradio";
            break;
        default:
            printk(KERN_ERR "%s called with unknown type: %d\n",
                   __func__, type);
            return -EINVAL;
        }
        ......省略代码......    
        /* 查找空闲次设备号*/
        nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
        if (nr == minor_cnt)
            nr = devnode_find(vdev, 0, minor_cnt);
        ......省略代码......    
        /* 查找到第一个空闲的次设备号*/
        for (i = 0; i < VIDEO_NUM_DEVICES; i++)
            if (video_device[i] == NULL)
                break;
        ......省略代码......    
        /* minor_offset一般是0,i就是查找到的空闲次设备号,这里总的次设备支持到256个*/
        vdev->minor = i + minor_offset;
        vdev->num = nr;
        ......省略代码......   
        /* 这里根据次设备号将当前video_device保存到video_device[]数组中*/
        video_device[vdev->minor] = vdev;
        mutex_unlock(&videodev_lock);
    
        if (vdev->ioctl_ops)
            determine_valid_ioctls(vdev);
    
        /* Part 3: Initialize the character device */
        /* 分配字符设备文件*/
        vdev->cdev = cdev_alloc();
        if (vdev->cdev == NULL) {
            ret = -ENOMEM;
            goto cleanup;
        }
        /* 后面子设备中的ioctl都是通过这里查找的*/
        vdev->cdev->ops = &v4l2_fops;
        vdev->cdev->owner = owner;
        ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
        if (ret < 0) {
            printk(KERN_ERR "%s: cdev_add failed\n", __func__);
            kfree(vdev->cdev);
            vdev->cdev = NULL;
            goto cleanup;
        }
    
        /* Part 4: register the device with sysfs */
        vdev->dev.class = &video_class;
        /*主设备号为81 */
        vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
        /*这里一般是v4l2_device对象 */
        vdev->dev.parent = vdev->dev_parent;
        dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
        /*注册该video_device到kernel中 */
        ret = device_register(&vdev->dev);
        ......省略代码......   
        /* Increase v4l2_device refcount */
        /*增加v4l2设备对象引用计数*/
        if (vdev->v4l2_dev)
            v4l2_device_get(vdev->v4l2_dev);
    
    #if defined(CONFIG_MEDIA_CONTROLLER)
        /* Part 5: Register the entity. */
        if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
            vdev->vfl_type != VFL_TYPE_SUBDEV) {
            vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
            vdev->entity.name = vdev->name;
            vdev->entity.info.v4l.major = VIDEO_MAJOR;
            vdev->entity.info.v4l.minor = vdev->minor;
            ret = media_device_register_entity(vdev->v4l2_dev->mdev,
                &vdev->entity);
            if (ret < 0)
                printk(KERN_WARNING
                       "%s: media_device_register_entity failed\n",
                       __func__);
        }
    #endif
    
        ......省略代码......   
    }
    

    调用的时候传入的是VFL_TYPE_GRABBER,所以创建的字符设备是vedio*,

    ioctl(sd_fd, VIDIOC_MSM_SENSOR_INIT_CFG, &cfg)这个方法实际是为了等设备初始化完成,该创建的字符设备都创建了,就往下走
    我们继续回到get_num_of_cameras往下走
    进入下一个while循环

    rc = ioctl(dev_fd, MEDIA_IOC_DEVICE_INFO, &mdev_info);
    
    if(strncmp(mdev_info.model, MSM_CAMERA_NAME, sizeof(mdev_info.model)) != 0) {
        close(dev_fd);
        dev_fd = -1;
        continue;
    }
    

    这个去media里找model是MSM_CAMERA_NAME的设备,我们在之前camera_init_v4l2中有看到

    strlcpy(v4l2_dev->mdev->model, MSM_CAMERA_NAME,
    sizeof(v4l2_dev->mdev->model));

    所以这里就是要查找camera设备
    接着看下
    rc = ioctl(dev_fd, MEDIA_IOC_ENUM_ENTITIES, &entity);
    和上面一样的MEDIA_IOC_ENUM_ENTITIES,这里就判断
    type == MEDIA_ENT_T_DEVNODE_V4L && group_id == QCAMERA_VNODE_GROUP_ID为相机设备,然后拿到entity.name,实际就是vedio*

    相关文章

      网友评论

          本文标题:相机——解决多摄像头id错乱的问题

          本文链接:https://www.haomeiwen.com/subject/ywkdqktx.html