经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » 编程经验 » 查看文章
Hi3559AV100外接UVC/MJPEG相机实时采图设计(一):Linux USB摄像头驱动分析
来源:cnblogs  作者:流星斩月  时间:2021/2/18 15:28:31  对本文有异议

  下面将给出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,之后通过这个网页 http://www.ideasonboard.org/uvc/ 来查看是否支持 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. 1 待进入内核源代码目录后,执行以下操作
  2. 2
  3. 3 cp arch/arm64/configs/hi3559av100_arm64_big_little_emmc_defconfig .config
  4. 4
  5. 5
  6. 6 make ARCH=arm64 CROSS_COMPILE=aarch64-himix100-linux- menuconfig
  7. 7
  8. 8
  9. 9 cp .config arch/arm64/configs/hi3559av100_arm64_big_little_emmc_defconfig
  10. 10
  11. 11
  12. 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. 1 /* Generic USB Video Class */
  2. 2 { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_UNDEFINED) },
  3. 3 { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, UVC_PC_PROTOCOL_15) },
  4. 4 {}

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

  1. 1 { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
  2. 2 | USB_DEVICE_ID_MATCH_INT_INFO,
  3. 3 .idVendor = 0x046d,
  4. 4 .idProduct = 0x0825,
  5. 5 .bInterfaceClass = USB_CLASS_VIDEO,
  6. 6 .bInterfaceSubClass = 1,
  7. 7 .bInterfaceProtocol = 0,
  8. 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. 1 static int uvc_probe(struct usb_interface *intf,
  2. 2 const struct usb_device_id *id)
  3. 3 {
  4. 4 struct usb_device *udev = interface_to_usbdev(intf);
  5. 5 struct uvc_device *dev;
  6. 6 int ret;
  7. 7
  8. 8 if (id->idVendor && id->idProduct)
  9. 9 uvc_trace(UVC_TRACE_PROBE, "Probing known UVC device %s "
  10. 10 "(%04x:%04x)\n", udev->devpath, id->idVendor,
  11. 11 id->idProduct);
  12. 12 else
  13. 13 uvc_trace(UVC_TRACE_PROBE, "Probing generic UVC device %s\n",
  14. 14 udev->devpath);
  15. 15
  16. 16 /* Allocate memory for the device and initialize it. */
  17. 17 if ((dev = kzalloc(sizeof *dev, GFP_KERNEL)) == NULL)
  18. 18 return -ENOMEM;
  19. 19
  20. 20 INIT_LIST_HEAD(&dev->entities);
  21. 21 INIT_LIST_HEAD(&dev->chains);
  22. 22 INIT_LIST_HEAD(&dev->streams);
  23. 23 atomic_set(&dev->nstreams, 0);
  24. 24 atomic_set(&dev->nmappings, 0);
  25. 25 mutex_init(&dev->lock);
  26. 26
  27. 27 dev->udev = usb_get_dev(udev);
  28. 28 dev->intf = usb_get_intf(intf);
  29. 29 dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber;
  30. 30 dev->quirks = (uvc_quirks_param == -1)
  31. 31 ? id->driver_info : uvc_quirks_param;
  32. 32
  33. 33 if (udev->product != NULL)
  34. 34 strlcpy(dev->name, udev->product, sizeof dev->name);
  35. 35 else
  36. 36 snprintf(dev->name, sizeof dev->name,
  37. 37 "UVC Camera (%04x:%04x)",
  38. 38 le16_to_cpu(udev->descriptor.idVendor),
  39. 39 le16_to_cpu(udev->descriptor.idProduct));
  40. 40
  41. 41 /* Parse the Video Class control descriptor. */
  42. 42 if (uvc_parse_control(dev) < 0) {
  43. 43 uvc_trace(UVC_TRACE_PROBE, "Unable to parse UVC "
  44. 44 "descriptors.\n");
  45. 45 goto error;
  46. 46 }
  47. 47
  48. 48 uvc_printk(KERN_INFO, "Found UVC %u.%02x device %s (%04x:%04x)\n",
  49. 49 dev->uvc_version >> 8, dev->uvc_version & 0xff,
  50. 50 udev->product ? udev->product : "<unnamed>",
  51. 51 le16_to_cpu(udev->descriptor.idVendor),
  52. 52 le16_to_cpu(udev->descriptor.idProduct));
  53. 53
  54. 54 if (dev->quirks != id->driver_info) {
  55. 55 uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module "
  56. 56 "parameter for testing purpose.\n", dev->quirks);
  57. 57 uvc_printk(KERN_INFO, "Please report required quirks to the "
  58. 58 "linux-uvc-devel mailing list.\n");
  59. 59 }
  60. 60
  61. 61 /* Initialize the media device and register the V4L2 device. */
  62. 62 #ifdef CONFIG_MEDIA_CONTROLLER
  63. 63 dev->mdev.dev = &intf->dev;
  64. 64 strlcpy(dev->mdev.model, dev->name, sizeof(dev->mdev.model));
  65. 65 if (udev->serial)
  66. 66 strlcpy(dev->mdev.serial, udev->serial,
  67. 67 sizeof(dev->mdev.serial));
  68. 68 strcpy(dev->mdev.bus_info, udev->devpath);
  69. 69 dev->mdev.hw_revision = le16_to_cpu(udev->descriptor.bcdDevice);
  70. 70 dev->mdev.driver_version = LINUX_VERSION_CODE;
  71. 71 media_device_init(&dev->mdev);
  72. 72
  73. 73 dev->vdev.mdev = &dev->mdev;
  74. 74 #endif
  75. 75 if (v4l2_device_register(&intf->dev, &dev->vdev) < 0)
  76. 76 goto error;
  77. 77
  78. 78 /* Initialize controls. */
  79. 79 if (uvc_ctrl_init_device(dev) < 0)
  80. 80 goto error;
  81. 81
  82. 82 /* Scan the device for video chains. */
  83. 83 if (uvc_scan_device(dev) < 0)
  84. 84 goto error;
  85. 85
  86. 86 /* Register video device nodes. */
  87. 87 if (uvc_register_chains(dev) < 0)
  88. 88 goto error;
  89. 89
  90. 90 #ifdef CONFIG_MEDIA_CONTROLLER
  91. 91 /* Register the media device node */
  92. 92 if (media_device_register(&dev->mdev) < 0)
  93. 93 goto error;
  94. 94 #endif
  95. 95 /* Save our data pointer in the interface data. */
  96. 96 usb_set_intfdata(intf, dev);
  97. 97
  98. 98 /* Initialize the interrupt URB. */
  99. 99 if ((ret = uvc_status_init(dev)) < 0) {
  100. 100 uvc_printk(KERN_INFO, "Unable to initialize the status "
  101. 101 "endpoint (%d), status interrupt will not be "
  102. 102 "supported.\n", ret);
  103. 103 }
  104. 104
  105. 105 uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
  106. 106 usb_enable_autosuspend(udev);
  107. 107 return 0;
  108. 108
  109. 109 error:
  110. 110 uvc_unregister_video(dev);
  111. 111 return -ENODEV;
  112. 112 }
  对于初始kernel内核USB驱动应该是加载了一部分支持 UVC 摄像头的驱动,在默认的uvc_driver 下,是没有我们摄像头 046d 0825 ID 号的,需要我们手动添加。当内核发现与 uvc_ids 匹配的 USB 摄像头就会调用 uvc_probe 函数,一旦内核发现插入的 USB 摄像头被匹配后,最终就会调用 uvc_probe 函数。
  USB摄像头驱动其实就是一个字符设备驱动,重点关注v4l2_fops结构体,如下所示:
  1. 1 static const struct file_operations v4l2_fops = {
  2. 2 .owner = THIS_MODULE,
  3. 3 .read = v4l2_read,
  4. 4 .write = v4l2_write,
  5. 5 .open = v4l2_open,
  6. 6 .get_unmapped_area = v4l2_get_unmapped_area,
  7. 7 .mmap = v4l2_mmap,
  8. 8 .unlocked_ioctl = v4l2_ioctl,
  9. 9 #ifdef CONFIG_COMPAT
  10. 10 .compat_ioctl = v4l2_compat_ioctl32,
  11. 11 #endif
  12. 12 .release = v4l2_release,
  13. 13 .poll = v4l2_poll,
  14. 14 .llseek = no_llseek,
  15. 15 };
  而USB摄像头结合V4L2接口时,当应用程序调用 open 函数,例如: open("/dev/video0",....),首先就会调用到 v4l2 核心里的 open 函数,也就是 v4l2_open 函数(v4l2-dev.c)。而在v4l2-open 函数中调用了 vdev->fops->open(filp),相当于调用 uvc_v4l2_open()函数。这个函数的实现在\drivers\media\usb\uvc\uvc_v4l2.c 里。同样地,vdev->fops->unlocked_ioctl(filp,cmd,arg)(v4l2-dev.c);最后相当于调用 uvc_v4l2_ioctl()函数,它又调用 video_usercopy(file,cmd,arg,uvc_v4l2_do_ioctl);函数,video_usercopy()函数的作用从名字上可以猜测,它是根据用户空间传递过来的 cmd 命令,调用 uvc_v4l2_do_ioctl()函数来解析 arg 参数。
  ctrl 属性的函数调用流程
  uvc_probe
    uvc_register_chains
      uvc_register_terms
        uvc_register_video
          video_register_device__video_register_device
            determine_valid_ioctls
  这些 ctrl 属性就是 USB 摄像头的各种属性,比如亮度的调节,打开、关闭STREAM 等等操作,这些是 v4l2 核心最最复杂的工作了,没有之一。
参考: \drivers\media\v4l2-core\v4l2-dev.c

原文链接:http://www.cnblogs.com/iFrank/p/14399421.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号