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: //扩展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.\n", 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.\n");
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.\n");
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\n", 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\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: //扩展Unit
if (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 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.\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: //扩展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\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);

uvc摄像头代码解析6-LMLPHP

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_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.\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;
default
uvc_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) { //提交urb
uvc_printk(KERN_ERR, "Failed to resubmit status URB (%d).\n",ret);
}
}

12.2.1 VC状态变化事件处理

uvc摄像头代码解析6-LMLPHP

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状态变化事件处理

uvc摄像头代码解析6-LMLPHP

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.\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);
}
}
04-15 08:57