最近在海思hi3536平台上开发usb设备检测的驱动,其中有一项功能是usb摄像头相关的,通过usb分析仪确定该usb摄像头有四个接口。我主要关注前两个接口,一个是uvc control,一个是uvc stream,这两个是uvc正常工作必须的接口。
然后再看uvc_driver驱动的usb_device_id列表,里面只有uvc control这一接口会在总线match函数中匹配,那么问题来了,第二个接口没有和总线匹配,那么它是如何和uvc驱动关联起来的,难道是驱动的id列表忘了这一个接口? 于是我手动在usb_device_id增加了第二个接口对应的相关id,接下来看看会发生什么。
重新加载修改过的驱动后发现还是只调用了一次uvc_probe函数,这是为什么呢?接下来只能从usb相关驱动源码上找一找原因了。
首先看usb_set_configuration函数,
for (i = 0; i < nintf; ++i) {
struct usb_interface_cache *intfc;
struct usb_interface *intf;
struct usb_host_interface *alt;
cp->interface[i] = intf = new_interfaces[i];
intfc = cp->intf_cache[i];
intf->altsetting = intfc->altsetting;
intf->num_altsetting = intfc->num_altsetting;
kref_get(&intfc->ref);
alt = usb_altnum_to_altsetting(intf, 0);
/* No altsetting 0? We'll assume the first altsetting.
* We could use a GetInterface call, but if a device is
* so non-compliant that it doesn't have altsetting 0
* then I wouldn't trust its reply anyway.
*/
if (!alt)
alt = &intf->altsetting[0];
intf->intf_assoc =
find_iad(dev, cp, alt->desc.bInterfaceNumber);
intf->cur_altsetting = alt;
usb_enable_interface(dev, intf, true);
intf->dev.parent = &dev->dev;
intf->dev.driver = NULL;
intf->dev.bus = &usb_bus_type;
intf->dev.type = &usb_if_device_type;
intf->dev.groups = usb_interface_groups;
intf->dev.dma_mask = dev->dev.dma_mask;
INIT_WORK(&intf->reset_ws, __usb_queue_reset_device);
intf->minor = -1;
device_initialize(&intf->dev);
pm_runtime_no_callbacks(&intf->dev);
dev_set_name(&intf->dev, "%d-%s:%d.%d",
dev->bus->busnum, dev->devpath,
configuration, alt->desc.bInterfaceNumber);
}
for (i = 0; i < nintf; ++i) {
struct usb_interface *intf = cp->interface[i];
dev_dbg(&dev->dev,
"adding %s (config #%d, interface %d)\n",
dev_name(&intf->dev), configuration,
intf->cur_altsetting->desc.bInterfaceNumber);
device_enable_async_suspend(&intf->dev);
ret = device_add(&intf->dev);
if (ret != 0) {
dev_err(&dev->dev, "device_add(%s) --> %d\n",
dev_name(&intf->dev), ret);
continue;
}
create_intf_ep_devs(intf);
}
该函数功能主要是根据usb设备的配置描述符给接口描述符分配资源,初始化各个接口描述符,然后调用device_add函数注册usb接口设备。
具体到uvc驱动的话,此处应该是分配并初始化了四个usb interface,调用device_add注册了四个usb interface设备,遇到的问题是,注册第二个usb接口时发现没有调用uvc_probe,进一步跟踪发现在注册第二个接口时intf->dev.driver已经不是NULL了(理论上似乎应该时NULL才对),所以在device_attach中提前返回了。猜测只能是uvc_probe函数中对第二个usb 接口的intf->dev.driver赋值了,将uvc_probe函数中的代码注释掉,再次重新加载驱动,此时注册第二个usb接口时发现intf->dev.driver变成了NULL,因此可以确定是uvc_probe函数中对第二个usb接口的intf->dev.driver赋值了。
走读一遍uvc_probe函数,最终发现是在uvc_parse_standard_control函数中的第一个switch分支中对对第二个usb接口的intf->dev.driver赋值的。
通过上面的追踪得到了一个结论,对于有些usb组合设备并不一定需要每个接口都匹配不同的驱动。以前做过usb键盘、鼠标、U盘的组合设备,在这个设备中因为三个接口各个功能独立,因此需要加载三个不同的驱动。