下面將給出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 待進入內核源代碼目錄后,執行以下操作 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_defconfig 10 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_DEVICE 2 | 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 #endif 75 if (v4l2_device_register(&intf->dev, &dev->vdev) < 0) 76 goto error; 77 78 /* Initialize controls. */ 79 if (uvc_ctrl_init_device(dev) < 0) 80 goto error; 81 82 /* Scan the device for video chains. */ 83 if (uvc_scan_device(dev) < 0) 84 goto error; 85 86 /* Register video device nodes. */ 87 if (uvc_register_chains(dev) < 0) 88 goto error; 89 90 #ifdef CONFIG_MEDIA_CONTROLLER 91 /* Register the media device node */ 92 if (media_device_register(&dev->mdev) < 0) 93 goto error; 94 #endif 95 /* Save our data pointer in the interface data. */ 96 usb_set_intfdata(intf, dev); 97 98 /* Initialize the interrupt URB. */ 99 if ((ret = uvc_status_init(dev)) < 0) { 100 uvc_printk(KERN_INFO, "Unable to initialize the status " 101 "endpoint (%d), status interrupt will not be " 102 "supported.\n", ret); 103 } 104 105 uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n"); 106 usb_enable_autosuspend(udev); 107 return 0; 108 109 error: 110 uvc_unregister_video(dev); 111 return -ENODEV; 112 }對于初始kernel內核USB驅動應該是加載了一部分支持 UVC 攝像頭的驅動,在默認的uvc_driver 下,是沒有我們攝像頭 046d 0825 ID 號的,需要我們手動添加,當內核發現與 uvc_ids 匹配的 USB 攝像頭就會呼叫 uvc_probe 函式,一旦內核發現插入的 USB 攝像頭被匹配后,最終就會呼叫 uvc_probe 函式, USB攝像頭驅動其實就是一個字符設備驅動,重點關注v4l2_fops結構體,如下所示:
1 static const struct file_operations v4l2_fops = { 2 .owner = THIS_MODULE, 3 .read = v4l2_read, 4 .write = v4l2_write, 5 .open = v4l2_open, 6 .get_unmapped_area = v4l2_get_unmapped_area, 7 .mmap = v4l2_mmap, 8 .unlocked_ioctl = v4l2_ioctl, 9 #ifdef CONFIG_COMPAT 10 .compat_ioctl = v4l2_compat_ioctl32, 11 #endif 12 .release = v4l2_release, 13 .poll = v4l2_poll, 14 .llseek = no_llseek, 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
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/259126.html
標籤:其他
