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). ",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. "); 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. ", 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: //扩展Unit if (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. ", entity->id); return -1; } break; case UVC_VC_PROCESSING_UNIT: //处理Unit if (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. "); return -1; } chain->processing = entity; //如果是处理Unit则设置其为uvc视频链的processing对象 break; case UVC_VC_SELECTOR_UNIT: //选择器Unit if (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. "); return -1; } chain->selector = entity; //如果是选择器Unit则设置其为uvc视频链的selector对象 break; case UVC_ITT_VENDOR_SPECIFIC: //厂商特殊 case UVC_ITT_CAMERA: //输入Terminal camera case UVC_ITT_MEDIA_TRANSPORT_INPUT: //输入Terminal Media transport if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- IT %d ", entity->id); break; case UVC_TT_STREAMING: //输入Terminal stream if (UVC_ENTITY_IS_ITERM(entity)) { if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- IT %d ", 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. ", 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: //扩展Unit if (forward->bNrInPins != 1) { uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more than 1 input pin. ",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 display case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: //输出Terminal media transport case UVC_TT_STREAMING: //输出Terminal stream if (UVC_ENTITY_IS_ITERM(forward)) { uvc_trace(UVC_TRACE_DESCR, "Unsupported input terminal %u. ", 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: //扩展Unit case UVC_VC_PROCESSING_UNIT: //处理Unit处理Unit的输入Terminal个数只能为1 id = entity->baSourceID[0]; //获取输入pin(Unit/Terminal)的ID break; case UVC_VC_SELECTOR_UNIT: //选择器实体 /* Single-input selector units are ignored. */ if (entity->bNrInPins == 1) { //若输入pin个数为1 id = entity->baSourceID[0]; //获取输入in(Unit/Terminal)的ID break; } if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- IT"); chain->selector = entity; //uvc视频链的selector对象指向uvc实体 for (i = 0; i < entity->bNrInPins; ++i) { //总共有多少个输入pin id = entity->baSourceID[i]; //获取输入in(Unit/Terminal)的ID term = 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 ", 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(" "); 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. ", 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). ", 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). ",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). ",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_endpoint unsigned 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); //分配urb if (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); //填充中断urb return 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. ", 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; default uvc_trace(UVC_TRACE_STATUS, "Unknown status event type %u. ", dev->status[0]); break; } } /* Resubmit the URB. */ urb->interval = dev->int_ep->desc.bInterval; if ((ret = usb_submit_urb(urb, GFP_ATOMIC)) < 0) { //提交urb uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d). ",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. "); return; } uvc_trace(UVC_TRACE_STATUS, "Control %u/%u %s change len %d. ",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) { //长度等于4 uvc_trace(UVC_TRACE_STATUS, "Invalid streaming status event received. "); 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 ",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. ", data[1], data[2], data[3], len); } }