2021-02-12

Hi3559AV100外接UVC/MJPEG相机实时采图设计(一):Linux USB摄像头驱动分析

  下面将给出Hi3559AV100外接UVC/MJPEG相机实时采图设计的整体流程,主要实现是通过V4L2接口将UVC/MJPEG相机采集的数据送入至MPP平台,经过VDEC、VPSS、VO最后通过HDMI的输出,首先给出(一)Linux USB摄像头驱动加载与分析。

板载平台:BOXER-8410AI

芯片型号:Hi3559AV100

相机型号:Logitch c270

开发环境:VM15.5+ubuntu16.04+Hilinux

1、确定USB摄像头支持UVC

  首先,可以把USB摄像头插在PC端,然后通过设备管理器找到相机,右键选择属性,选择详细信息,更改属性一栏,选择硬件ID,从中可以看到USB摄像头的VID和PID,比如Logitech c270的ID号为:046d:0825,之后通过这个网页  来查看是否支持 UVC,这个网站是 USB Video Class Linux device driver 的主页,里面有 UVC 的详细的介绍。根据前面的打印信息,根据自己的 ID 号, 这里是搜索 USB 摄像头的 VID 号:046d 和 PID 号:0825,主页如下所示:

  通过摄像头的 ID,可以看到该摄像头是否支持 UVC 和其他信息。绿勾代表支持。

2、配置与相机型号匹配的USB host驱动

  目前Hilinux系统自带了部分型号的usb摄像头驱动,但并不是支持所有市面上usb摄像头,像Logitch c270这一款usb摄像头就不支持,如果说linux kernel驱动中不支持,需要我们重新配置该驱动,或者需要进行裁剪等操作,而这个过程需要我们进行手动配置,配置过程如下:在内核目录下,输入如下命令(以emmc启动为例):

 1   待进入内核源代码目录后,执行以下操作 2  3   cp arch/arm64/configs/hi3559av100_arm64_big_little_emmc_defconfig .config 4  5  6   make ARCH=arm64 CROSS_COMPILE=aarch64-himix100-linux- menuconfig 7  8  9   cp .config arch/arm64/configs/hi3559av100_arm64_big_little_emmc_defconfig10 11 12   osdrv顶层目录下执行:make BOOT_MEDIA=emmc AMP_TYPE=linux atf

 

执行 make ARCH=arm64 CROSS_COMPILE=aarch64-himix100-linux- menuconfig 后,弹出如下窗口:

之后进行驱动配置,打开UVC驱动等等。

  在配置好之后,弹出menuconfig窗口后,记得保存,保存完之后在手动修改usb驱动代码:修改位置如下:

linux-xxx\drivers\media\usb\uvc\uvc_driver.c

  设备插入时调用probe将会按默认的id_table来加载驱动,也就是这个uvc_ids末尾说的Generic USB Video Class,具体如下所示:

1  /* Generic USB Video Class */2  { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_UNDEFINED) },3  { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_15) },4  {}

  在struct usb_device_id uvc_ids[]中模仿之前的加上自己的USB设备信息:

1  { .match_flags  = USB_DEVICE_ID_MATCH_DEVICE2     | USB_DEVICE_ID_MATCH_INT_INFO,3  .idVendor  = 0x046d,4  .idProduct  = 0x0825,5  .bInterfaceClass = USB_CLASS_VIDEO,6  .bInterfaceSubClass = 1,7  .bInterfaceProtocol = 0,8  .driver_info  = UVC_QUIRK_RESTORE_CTRLS_ON_INIT },

  注意一下这个driver_info的赋值,可以用来限制帧率,UVC_QUIRK_RESTORE_CTRLS_ON_INIT的值是0x400,这个设置好像是跟带宽有关系,没有深入了解,如果设的过小,将导致无法出图。而且USB2.0的带宽上限也只有480Mbit/s,连一个摄像头都够呛了。修改完之后,还需要重新编译内核。

  之后将摄像头插在板载上,终端出现如下:

   也可以通过命令ls /dev/video*查看video设备,如下所示, 到此驱动部分添加完成。

 1 /dev/video0                                                      

 3、UVC driver的研究

  上述终端显示的信息是由uvc_probe()函数输出,对应函数位置为linux-xxx\drivers\media\usb\uvc\uvc_driver.c,函数具体内容如下:

 1 static int uvc_probe(struct usb_interface *intf, 2    const struct usb_device_id *id) 3 { 4  struct usb_device *udev = interface_to_usbdev(intf); 5  struct uvc_device *dev; 6  int ret; 7  8  if (id->idVendor && id->idProduct) 9   uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s " 10     "(%04x:%04x)\n", udev->devpath, id->idVendor, 11     id->idProduct); 12  else 13   uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n", 14     udev->devpath); 15  16  /* Allocate memory for the device and initialize it. */ 17  if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL) 18   return -ENOMEM; 19  20  INIT_LIST_HEAD(&dev->entities); 21  INIT_LIST_HEAD(&dev->chains); 22  INIT_LIST_HEAD(&dev->streams); 23  atomic_set(&dev->nstreams, 0); 24  atomic_set(&dev->nmappings, 0); 25  mutex_init(&dev->lock); 26  27  dev->udev = usb_get_dev(udev); 28  dev->intf = usb_get_intf(intf); 29  dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; 30  dev->quirks = (uvc_quirks_param == -1) 31    ? id->driver_info : uvc_quirks_param; 32  33  if (udev->product != NULL) 34   strlcpy(dev->name, udev->product, sizeof dev->name); 35  else 36   snprintf(dev->name, sizeof dev->name, 37    "UVC Camera (%04x:%04x)", 38    le16_to_cpu(udev->descriptor.idVendor), 39    le16_to_cpu(udev->descriptor.idProduct)); 40  41  /* Parse the Video Class control descriptor. */ 42  if (uvc_parse_control(dev) < 0) { 43   uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC " 44    "descriptors.\n"); 45   goto error; 46  } 47  48  uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n", 49   dev->uvc_version >> 8, dev->uvc_version & 0xff, 50   udev->product ? udev->product : "<unnamed>", 51   le16_to_cpu(udev->descriptor.idVendor), 52   le16_to_cpu(udev->descriptor.idProduct)); 53  54  if (dev->quirks != id->driver_info) { 55   uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module " 56    "parameter for testing purpose.\n", dev->quirks); 57   uvc_printk(KERN_INFO, "Please report required quirks to the " 58    "linux-uvc-devel mailing list.\n"); 59  } 60  61  /* Initialize the media device and register the V4L2 device. */ 62 #ifdef CONFIG_MEDIA_CONTROLLER 63  dev->mdev.dev = &intf->dev; 64  strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model)); 65  if (udev->serial) 66   strlcpy(dev->mdev.serial, udev->serial, 67    sizeof(dev->mdev.serial)); 68  strcpy(dev->mdev.bus_info, udev->devpath); 69  dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice); 70  dev->mdev.driver_version = LINUX_VERSION_CODE; 71  media_device_init(&dev->mdev); 72  73  dev->vdev.mdev = &dev->mdev; 74 

No comments:

Post a Comment