uvc摄像头代码解析6

来源:互联网 发布:vscode git 记住密码 编辑:程序博客网 时间:2024/04/29 18:16

10.扫描视频设备链和注册视频设备

10.1 uvc视频链

struct uvc_video_chain {//uvc视频链struct uvc_device *dev;//uvc设备struct list_head list;//uvc视频链链表头struct list_head entities;//uvc实体链表头struct uvc_entity *processing;//处理Unit实体struct uvc_entity *selector;//选择器Unit实体struct mutex ctrl_mutex;/* Protects ctrl.info */};

10.2 uvc扫描设备

static int uvc_scan_device(struct uvc_device *dev){struct uvc_video_chain *chain;//uvc视频链struct uvc_entity *term;//uvc实体list_for_each_entry(term, &dev->entities, list) {//遍历全局实体链表if (!UVC_ENTITY_IS_OTERM(term))//获取实体链表中的输出Terminal实体continue;if (term->chain.next || term->chain.prev)//已经添加到uvc视频链中了continue;chain = kzalloc(sizeof(*chain), GFP_KERNEL);//分配uvc视频链内存(有多少个输入Terminal就有多少个uvc_video_chain)if (chain == NULL)return -ENOMEM;INIT_LIST_HEAD(&chain->entities);//初始化视频链entities(实体)链表mutex_init(&chain->ctrl_mutex);chain->dev = dev;//捆绑uvc视频链和uvc设备if (uvc_scan_chain(chain, term) < 0) {//扫描uvc视频链(处理所有相关的输入pin)kfree(chain);continue;}uvc_trace(UVC_TRACE_PROBE, "Found a valid video chain (%s).\n",uvc_print_chain(chain));list_add_tail(&chain->list, &dev->chains);//添加到uvc设备的uvc视频链链表}if (list_empty(&dev->chains)) {uvc_printk(KERN_INFO, "No valid video chain found.\n");return -1;}return 0;}

10.3 uvc扫描视频链

static int uvc_scan_chain(struct uvc_video_chain *chain,struct uvc_entity *term){struct uvc_entity *entity, *prev;uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:");entity = term;//获取实体prev = NULL;//前一个实体while (entity != NULL) {/* Entity must not be part of an existing chain */if (entity->chain.next || entity->chain.prev) {//已经添加到uvc视频链中了uvc_trace(UVC_TRACE_DESCR, "Found reference to entity %d already in chain.\n", entity->id);return -EINVAL;}/* Process entity */if (uvc_scan_chain_entity(chain, entity) < 0)//扫描当前实体return -EINVAL;/* Forward scan */if (uvc_scan_chain_forward(chain, entity, prev) < 0)//向前扫描实体return -EINVAL;/* Backward scan */prev = entity;//当前实体作为下一次while循环的前一个实体if (uvc_scan_chain_backward(chain, &entity) < 0)//向后扫描实体return -EINVAL;}return 0;}

将uvc视频链的输入实体添加到uvc视频链的entities链表中
将uvc视频链添加到uvc设备的chains链表中

10.3.1 扫描当前实体

static int uvc_scan_chain_entity(struct uvc_video_chain *chain,struct uvc_entity *entity){switch (UVC_ENTITY_TYPE(entity)) {case UVC_VC_EXTENSION_UNIT://扩展Unitif (uvc_trace_param & UVC_TRACE_PROBE)printk(" <- XU %d", entity->id);if (entity->bNrInPins != 1) {uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more than 1 input pin.\n", entity->id);return -1;}break;case UVC_VC_PROCESSING_UNIT://处理Unitif (uvc_trace_param & UVC_TRACE_PROBE)printk(" <- PU %d", entity->id);if (chain->processing != NULL) {uvc_trace(UVC_TRACE_DESCR, "Found multiple Processing Units in chain.\n");return -1;}chain->processing = entity;//如果是处理Unit则设置其为uvc视频链的processing对象break;case UVC_VC_SELECTOR_UNIT://选择器Unitif (uvc_trace_param & UVC_TRACE_PROBE)printk(" <- SU %d", entity->id);/* Single-input selector units are ignored. */if (entity->bNrInPins == 1)break;if (chain->selector != NULL) {uvc_trace(UVC_TRACE_DESCR, "Found multiple Selector Units in chain.\n");return -1;}chain->selector = entity;//如果是选择器Unit则设置其为uvc视频链的selector对象break;case UVC_ITT_VENDOR_SPECIFIC://厂商特殊case UVC_ITT_CAMERA://输入Terminal cameracase UVC_ITT_MEDIA_TRANSPORT_INPUT://输入Terminal Media transportif (uvc_trace_param & UVC_TRACE_PROBE)printk(" <- IT %d\n", entity->id);break;case UVC_TT_STREAMING://输入Terminal streamif (UVC_ENTITY_IS_ITERM(entity)) {if (uvc_trace_param & UVC_TRACE_PROBE)printk(" <- IT %d\n", entity->id);} else {if (uvc_trace_param & UVC_TRACE_PROBE)printk(" OT %d", entity->id);}break;default:uvc_trace(UVC_TRACE_DESCR, "Unsupported entity type 0x%04x found in chain.\n", UVC_ENTITY_TYPE(entity));return -1;}list_add_tail(&entity->chain, &chain->entities);//添加到uvc视频链的实体链表return 0;}

10.3.2 向前扫描实体

static int uvc_scan_chain_forward(struct uvc_video_chain *chain,struct uvc_entity *entity, struct uvc_entity *prev){struct uvc_entity *forward;int found;/* Forward scan */forward = NULL;found = 0;while (1) {//获取实体前面的所以实体处理直到前面的实体forward=NULL为止跳出死循环forward = uvc_entity_by_reference(chain->dev, entity->id,forward);//获取前一个实体if (forward == NULL)break;if (forward == prev)continue;switch (UVC_ENTITY_TYPE(forward)) {case UVC_VC_EXTENSION_UNIT://扩展Unitif (forward->bNrInPins != 1) {uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more than 1 input pin.\n",entity->id);return -EINVAL;}list_add_tail(&forward->chain, &chain->entities);//添加uvc实体到uvc视频链的entities中if (uvc_trace_param & UVC_TRACE_PROBE) {if (!found)printk(" (->");printk(" XU %d", forward->id);found = 1;}break;case UVC_OTT_VENDOR_SPECIFIC://厂商特殊case UVC_OTT_DISPLAY://输出Termianl displaycase UVC_OTT_MEDIA_TRANSPORT_OUTPUT://输出Terminal media transportcase UVC_TT_STREAMING: //输出Terminal streamif (UVC_ENTITY_IS_ITERM(forward)) {uvc_trace(UVC_TRACE_DESCR, "Unsupported input terminal %u.\n", forward->id);return -EINVAL;}list_add_tail(&forward->chain, &chain->entities);//添加uvc实体到uvc视频链的entities中if (uvc_trace_param & UVC_TRACE_PROBE) {if (!found)printk(" (->");printk(" OT %d", forward->id);found = 1;}break;}}if (found)printk(")");return 0;}

10.3.3 向后扫描实体

static int uvc_scan_chain_backward(struct uvc_video_chain *chain,struct uvc_entity **_entity){struct uvc_entity *entity = *_entity;struct uvc_entity *term;int id = -EINVAL, i;switch (UVC_ENTITY_TYPE(entity)) {case UVC_VC_EXTENSION_UNIT://扩展Unitcase UVC_VC_PROCESSING_UNIT://处理Unit处理Unit的输入Terminal个数只能为1id = entity->baSourceID[0];//获取输入pin(Unit/Terminal)的IDbreak;case UVC_VC_SELECTOR_UNIT://选择器实体/* Single-input selector units are ignored. */if (entity->bNrInPins == 1) {//若输入pin个数为1id = entity->baSourceID[0];//获取输入in(Unit/Terminal)的IDbreak;}if (uvc_trace_param & UVC_TRACE_PROBE)printk(" <- IT");chain->selector = entity;//uvc视频链的selector对象指向uvc实体for (i = 0; i < entity->bNrInPins; ++i) {//总共有多少个输入pinid = entity->baSourceID[i];//获取输入in(Unit/Terminal)的IDterm = uvc_entity_by_id(chain->dev, id);//获取对应的输入pin实体if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) {uvc_trace(UVC_TRACE_DESCR, "Selector unit %d input %d isn't connected to an input terminal\n", entity->id, i);return -1;}if (uvc_trace_param & UVC_TRACE_PROBE)printk(" %d", term->id);list_add_tail(&term->chain, &chain->entities);//添加uvc实体到uvc视频链的entities链表uvc_scan_chain_forward(chain, term, entity);//向前扫描实体}if (uvc_trace_param & UVC_TRACE_PROBE)printk("\n");id = 0;break;case UVC_ITT_VENDOR_SPECIFIC:case UVC_ITT_CAMERA:case UVC_ITT_MEDIA_TRANSPORT_INPUT:case UVC_OTT_VENDOR_SPECIFIC:case UVC_OTT_DISPLAY:case UVC_OTT_MEDIA_TRANSPORT_OUTPUT:case UVC_TT_STREAMING:id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0;break;}if (id <= 0) {*_entity = NULL;return id;}entity = uvc_entity_by_id(chain->dev, id);if (entity == NULL) {uvc_trace(UVC_TRACE_DESCR, "Found reference to unknown entity %d.\n", id);return -EINVAL;}*_entity = entity;return 0;}

注意到trace打印的语句会发现有一条

uvcvideo: Scanning UVC chain: OT 2 <- XU 5 <- XU 4 <- PU 3 <- IT 1

可以看到这些Unit和Terminal是如何组建起来的

这里补充一下:

1.打开trace:echo 0xffff > /sys/module/uvcvideo/par 消息用dmesg查看,清除dmesg信息带上-c参数就行

2.留意之前lsusb打印出来的描述符表,对应的bTerminalID就是trace打印信息中对应的Unit或Terminal的数字,而baSourceID则是它的前一级Unit或Terminal的ID号

10.3.4 添加链表

list_add_tail(&entity->chain, &chain->entities);



11.注册uvc视频链

11.1 uvc注册视频链

static int uvc_register_chains(struct uvc_device *dev){struct uvc_video_chain *chain;int ret;list_for_each_entry(chain, &dev->chains, list) {//遍历uvc设备的uvc视频链链表ret = uvc_register_terms(dev, chain);//注册uvc视频链if (ret < 0)return ret;}return 0;}

11.2 uvc注册实体

static int uvc_register_terms(struct uvc_device *dev,struct uvc_video_chain *chain){struct uvc_streaming *stream;struct uvc_entity *term;int ret;list_for_each_entry(term, &chain->entities, chain) {//遍历uvc视频链的uvc实体链表if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING)//不是输入Terminal streaming类型continue;stream = uvc_stream_by_id(dev, term->id);//获取uvc视频流if (stream == NULL) {uvc_printk(KERN_INFO, "No streaming interface found for terminal %u.", term->id);continue;}stream->chain = chain;//捆绑uvc视频流和uvc视频链ret = uvc_register_video(dev, stream);//注册uvc视频流if (ret < 0)return ret;}return 0;}

11.3 uvc注册视频

static int uvc_register_video(struct uvc_device *dev,struct uvc_streaming *stream){struct video_device *vdev;int ret;/* Initialize the streaming interface with default streaming parameters.*/ret = uvc_video_init(stream);//13.uvc视频初始化if (ret < 0) {uvc_printk(KERN_ERR, "Failed to initialize the device (%d).\n", ret);return ret;}/* Register the device with V4L. */vdev = video_device_alloc();//分配v4l2设备内存if (vdev == NULL) {uvc_printk(KERN_ERR, "Failed to allocate video device (%d).\n",ret);return -ENOMEM;}vdev->parent = &dev->intf->dev;//v4l2设备的父设备为usb接口设备vdev->fops = &uvc_fops;//v4l2操作函数集vdev->release = uvc_release;//释放方法strlcpy(vdev->name, dev->name, sizeof vdev->name);//设置名字stream->vdev = vdev;//捆绑uvc视频流和v4l2设备video_set_drvdata(vdev, stream);//将uvc视频流作为v4l2设备的驱动数据ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);//注册v4l2设备if (ret < 0) {uvc_printk(KERN_ERR, "Failed to register video device (%d).\n",ret);stream->vdev = NULL;video_device_release(vdev);return ret;}atomic_inc(&dev->nstreams);return 0;}

12.uvc设备状态初始化

uvc状态的处理由中断端点来控制处理

int uvc_status_init(struct uvc_device *dev){struct usb_host_endpoint *ep = dev->int_ep;//获取usb_host_endpointunsigned int pipe;int interval;if (ep == NULL)return 0;uvc_input_init(dev);//初始化uvc输入设备dev->status = kzalloc(UVC_MAX_STATUS_SIZE, GFP_KERNEL);//分配uvc设备状态内存if (dev->status == NULL)return -ENOMEM;dev->int_urb = usb_alloc_urb(0, GFP_KERNEL);//分配urbif (dev->int_urb == NULL) {kfree(dev->status);return -ENOMEM;}pipe = usb_rcvintpipe(dev->udev, ep->desc.bEndpointAddress);//中断输入端点interval = ep->desc.bInterval;//获取间隔if (interval > 16 && dev->udev->speed == USB_SPEED_HIGH &&(dev->quirks & UVC_QUIRK_STATUS_INTERVAL))//高速设备interval = fls(interval) - 1;usb_fill_int_urb(dev->int_urb, dev->udev, pipe,dev->status, UVC_MAX_STATUS_SIZE, uvc_status_complete,dev, interval);//填充中断urbreturn 0;}

这里只填充了urb信息,urb的提交请参看14.2.2.1 uvc_status_start启动状态,在打开uvc的V4L2设备方法时调用

12.1 初始化uvc输入事件

static int uvc_input_init(struct uvc_device *dev){struct input_dev *input;int ret;input = input_allocate_device();//分配input设备内存if (input == NULL)return -ENOMEM;usb_make_path(dev->udev, dev->input_phys, sizeof(dev->input_phys));//设备节点路径strlcat(dev->input_phys, "/button", sizeof(dev->input_phys));input->name = dev->name;//输入设备名input->phys = dev->input_phys;//输入设备节点路径usb_to_input_id(dev->udev, &input->id);input->dev.parent = &dev->intf->dev;//输入设备的父设备为usb接口设备__set_bit(EV_KEY, input->evbit);//设置输入事件类型__set_bit(KEY_CAMERA, input->keybit);//设置按钮if ((ret = input_register_device(input)) < 0)//注册input设备goto error;dev->input = input;//uvc设备捆绑输入设备return 0;error:input_free_device(input);return ret;}

12.2 urb回调函数

static void uvc_status_complete(struct urb *urb){struct uvc_device *dev = urb->context;int len, ret;switch (urb->status) {case 0:break;case -ENOENT:/* usb_kill_urb() called. */case -ECONNRESET:/* usb_unlink_urb() called. */case -ESHUTDOWN:/* The endpoint is being disabled. */case -EPROTO:/* Device is disconnected (reported by some host controller). */return;default:uvc_printk(KERN_WARNING, "Non-zero status (%d) in status completion handler.\n", urb->status);return;}len = urb->actual_length;if (len > 0) {switch (dev->status[0] & 0x0f) {case UVC_STATUS_TYPE_CONTROL://VC事件uvc_event_control(dev, dev->status, len);//Table 2-2 Status Packet Format (VideoControl Interface as the Originator)break;case UVC_STATUS_TYPE_STREAMING://VS事件uvc_event_streaming(dev, dev->status, len);//Table 2-3 Status Packet Format (VideoStreaming Interface as the Originator)break;defaultuvc_trace(UVC_TRACE_STATUS, "Unknown status event type %u.\n", dev->status[0]);break;}}/* Resubmit the URB. */urb->interval = dev->int_ep->desc.bInterval;if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {//提交urbuvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n",ret);}}

12.2.1 VC状态变化事件处理

static void uvc_event_control(struct uvc_device *dev, __u8 *data, int len){char *attrs[3] = { "value", "info", "failure" };if (len < 6 || data[2] != 0 || data[4] > 2) {//长度应该为6,且data[2](bEvent)为0表示(Control Change),data[4]大于2部分为保留值uvc_trace(UVC_TRACE_STATUS, "Invalid control status event received.\n");return;}uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d.\n",data[1], data[3], attrs[data[4]], len);}

12.2.2 VS状态变化事件处理

static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len){if (len < 3) {//长度等于4uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event received.\n");return;}if (data[2] == 0) {//data[2](bevent)--0x00(Button Press)摄像头上的按钮按下if (len < 4)return;uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n",data[1], data[3] ? "pressed" : "released", len);uvc_input_report_key(dev, KEY_CAMERA, data[3]);//上报按键事件} else {uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x %02x ""len %d.\n", data[1], data[2], data[3], len);}}

 

原创粉丝点击