Android项目复盘4

2020-07-01  本文已影响0人  cg1991

个人主页:https://chengang.plus/

UVC协议文档网址:https://www.usb.org/documents?search=&type%5B0%5D=55&items_per_page=50

主要下载USB Video Class 1_5,关注下载zip包中的UVC 1.5 Class specification.pdf文件,里面有接口相关的解释。

Android盒子控制摄像头项目已经差不多4年了,很多知识点已经忘记,现在重新温固一遍,下面两篇文章可以帮助回顾。

https://my.oschina.net/u/2007478/blog/968470

https://blog.csdn.net/go_str/article/details/80844175

下边两个网址中可以找到代码中各种结构体的解释:

https://www.kernel.org/doc/html/v4.13/media/v4l-drivers/uvcvideo.html

https://www.linuxtv.org/downloads/legacy/video4linux/API/V4L2_API/spec-single/v4l2.html

1、Linux kernel下的UVC

我们先从Android官网git clone一下kernel的源码:

https://android.googlesource.com/kernel/goldfish/

git clone https://android.googlesource.com/kernel/goldfish

clone到本地之后就可以通过Source Insight查看源码了。查看源码之前先project -> rebuild project,这样代码中各对象之间可以点击跳转。

1.1 初始化

goldfish\drivers\media\usb\uvc\uvc_driver.c

static int __init uvc_init(void)
{
    int ret;
    ret = usb_register(&uvc_driver.driver);
    return 0;
}

struct uvc_driver uvc_driver = {
    .driver = {
        .name       = "uvcvideo",
        .probe      = uvc_probe,
        .disconnect = uvc_disconnect,
        .suspend    = uvc_suspend,
        .resume     = uvc_resume,
        .reset_resume   = uvc_reset_resume,
        .id_table   = uvc_ids,
        .supports_autosuspend = 1,
    },
};

在入口函数uvc_init中,核心的一行是usb_register,也就是注册USB设备,在注册完成之后会调用uvc_probe函数。

goldfish\include\linux\usb.h

struct usb_driver {
    const char *name;

    int (*probe) (struct usb_interface *intf,
              const struct usb_device_id *id);

看看这个uvc_probe函数:

goldfish\drivers\media\usb\uvc\uvc_driver.c

static int uvc_probe(struct usb_interface *intf,
             const struct usb_device_id *id)
{
    struct usb_device *udev = interface_to_usbdev(intf);
    struct uvc_device *dev;
    int ret;

    if (id->idVendor && id->idProduct)
        uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
                "(%04x:%04x)\n", udev->devpath, id->idVendor,
                id->idProduct);
    else
        uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
                udev->devpath);
    
    uvc_parse_control(dev);//1
    v4l2_device_register(&intf->dev, &dev->vdev);//2
    uvc_ctrl_init_device(dev);//3
    uvc_scan_device(dev);
    uvc_register_chains(dev);
    usb_set_intfdata(intf, dev);
    ret = uvc_status_init(dev);
    usb_enable_autosuspend(udev);
}

1.2 初始化设备控制

这里是我们需要重点关注的。可以先跟踪一下这个调用栈。

goldfish\drivers\media\usb\uvc\uvc_ctrl.c

int uvc_ctrl_init_device(struct uvc_device *dev)
{
    list_for_each_entry(entity, &dev->entities, list) {
        struct uvc_control *ctrl;
        unsigned int bControlSize = 0, ncontrols;
        __u8 *bmControls = NULL;
        //第一部分
        if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT) {
            bmControls = entity->extension.bmControls;
            bControlSize = entity->extension.bControlSize;
        } else if (UVC_ENTITY_TYPE(entity) == UVC_VC_PROCESSING_UNIT) {
            bmControls = entity->processing.bmControls;
            bControlSize = entity->processing.bControlSize;
        } else if (UVC_ENTITY_TYPE(entity) == UVC_ITT_CAMERA) {
            bmControls = entity->camera.bmControls;
            bControlSize = entity->camera.bControlSize;
        }
        //第二部分
        /* Initialize all supported controls */
        ctrl = entity->controls;
        for (i = 0; i < bControlSize * 8; ++i) {
            if (uvc_test_bit(bmControls, i) == 0)
                continue;

            ctrl->entity = entity;
            ctrl->index = i;

            uvc_ctrl_init_ctrl(dev, ctrl);
            ctrl++;
        }
    }
}

1.2.1 ENTITY类型过滤

用于区分终端类型,重点关注UVC_ITT_CAMERA类型,看看这个类型在UVC协议文档里面的定义:

image

摄像头传感器,仅用于描述摄像头终端。那么代码里面的描述是:

goldfish\include\uapi\linux\usb\video.h

/* B.2. Input Terminal Types */
#define UVC_ITT_VENDOR_SPECIFIC             0x0200
#define UVC_ITT_CAMERA                  0x0201
#define UVC_ITT_MEDIA_TRANSPORT_INPUT           0x0202

1.2.2 初始化uvc_control

从第一部分中取出camera.bmControlscamera.bControlSize,这两个变量是干嘛的呢,还是看协议文档:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fiCTUia0-1593603543308)(https://ftp.bmp.ovh/imgs/2020/06/8968c359b0b5fc9b.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zcNwSHhb-1593603543310)(https://ftp.bmp.ovh/imgs/2020/06/fdf441b15a72d2f9.png)]

bControlSize是对应着bmControls的位大小;
bmControls对应着摄像头支持的控制参数,如果控制参数位置为1,表示支持该控制。

1.2.2.1 方法uvc_ctrl_init_ctrl

接下来调用uvc_ctrl_init_ctrl方法:

goldfish\drivers\media\usb\uvc\uvc_ctrl.c

static void uvc_ctrl_init_ctrl(struct uvc_device *dev, struct uvc_control *ctrl)
{
    const struct uvc_control_info *info = uvc_ctrls;
    const struct uvc_control_info *iend = info + ARRAY_SIZE(uvc_ctrls);
    const struct uvc_control_mapping *mapping = uvc_ctrl_mappings;
    const struct uvc_control_mapping *mend = mapping + ARRAY_SIZE(uvc_ctrl_mappings);
    
    for (; info < iend; ++info) {
        if (uvc_entity_match_guid(ctrl->entity, info->entity) &&
            ctrl->index == info->index) {
            uvc_ctrl_add_info(dev, ctrl, info);
            break;
         }
    }

    if (!ctrl->initialized)
        return;

    for (; mapping < mend; ++mapping) {
        if (uvc_entity_match_guid(ctrl->entity, mapping->entity) &&
            ctrl->info.selector == mapping->selector)
            __uvc_ctrl_add_mapping(dev, ctrl, mapping);
    }
}
1.2.2.2 结构体uvc_control_info
static struct uvc_control_info uvc_ctrls[] = {
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
        .index      = 11,
        .size       = 8,
        .flags      = UVC_CTRL_FLAG_SET_CUR
                | UVC_CTRL_FLAG_GET_RANGE
                | UVC_CTRL_FLAG_RESTORE
                | UVC_CTRL_FLAG_AUTO_UPDATE,
    },
    {
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_PANTILT_RELATIVE_CONTROL,
        .index      = 12,
        .size       = 4,
        .flags      = UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_MIN
                | UVC_CTRL_FLAG_GET_MAX | UVC_CTRL_FLAG_GET_RES
                | UVC_CTRL_FLAG_GET_DEF
                | UVC_CTRL_FLAG_AUTO_UPDATE,
    },
}

/* Bit index in bmControls */

image

对于PanTilt Absolute来说,高四位代表着左右的角度,低四位代表着上下的角度,都是有符号整数,总共八位,所以size为8。

对于PanTilt Relative来说,总共四位,每一位代表不同的控制属性,第一位表示左右相对;第二位表示左右控制的速度;第三位表示上下相对;第四位表示上下的速度。所以size为4。

1.2.2.3 结构体uvc_control_mapping

uvc_ctrl_mappingsuvc_control_mapping类型的结构体变量,也是一个静态的结构体,简略看下里面定义了啥:

static struct uvc_control_mapping uvc_ctrl_mappings[] = {
    {
        .id     = V4L2_CID_PAN_ABSOLUTE,
        .name       = "Pan (Absolute)",
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
        .size       = 32,
        .offset     = 0,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_UNSIGNED,
    },
    {
        .id     = V4L2_CID_TILT_ABSOLUTE,
        .name       = "Tilt (Absolute)",
        .entity     = UVC_GUID_UVC_CAMERA,
        .selector   = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
        .size       = 32,
        .offset     = 32,
        .v4l2_type  = V4L2_CTRL_TYPE_INTEGER,
        .data_type  = UVC_CTRL_DATA_TYPE_UNSIGNED,
    },
}

可以看到这里的id开头都是V4L2,而entityselector都对应着uvc_control_info uvc_ctrls中定义的entityselector

另外v4l2_type对应着设置的数据类型,data_type则定义了数据为有符号还是无符号。

这个结构体从我的理解来看,就是将UVC定义的控制,映射到v4l2,并建立两者之间的关系。

1.2.2.4 方法uvc_ctrl_add_info

这个方法核心就一行代码:

goldfish\drivers\media\usb\uvc\uvc_ctrl.c

static int uvc_ctrl_add_info(struct uvc_device *dev, struct uvc_control *ctrl,
    const struct uvc_control_info *info)
{
    ctrl->info = *info;
}

将上一步uvc_control_info变量给到uvc_control的info。uvc_control的定义如下:

goldfish\drivers\media\usb\uvc\uvcvideo.h

struct uvc_control {
    struct uvc_entity *entity;
    struct uvc_control_info info;

    __u8 index; /* Used to match the uvc_control entry with a
               uvc_control_info. */
    __u8 dirty:1,
         loaded:1,
         modified:1,
         cached:1,
         initialized:1;

    __u8 *uvc_data;
};
1.2.2.5 方法__uvc_ctrl_add_mapping

同样的,将uvc_control_mapping数据赋值到uvc_control对象中:

goldfish\drivers\media\usb\uvc\uvc_ctrl.c

static int __uvc_ctrl_add_mapping(struct uvc_device *dev,
    struct uvc_control *ctrl, const struct uvc_control_mapping *mapping)
{
    struct uvc_control_mapping *map;
    map = kmemdup(mapping, sizeof(*mapping), GFP_KERNEL);
    map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL);
    list_add_tail(&map->list, &ctrl->info.mappings);
}

uvc_control_infomappings作为链表头,将map->list添加到后面。

1.3 总结

在初始化的过程中可以将UVC协议的文档跟代码建立联系,以帮助理解代码的逻辑。待理解了各种数据类型定义的原理及流程之后,发现其实现了UVC与V4L2的连接,这样下一步的工作就比较好开展了。

UVC初始化的部分到这里告一段落,接下来要根据具体需求做一些定制的工作。

上一篇下一篇

猜你喜欢

热点阅读