《Linux总线、设备与驱动》USB设备发现机制
2018-02-28 15:44
375 查看
转载地址:http://blog.csdn.net/tankai19880619/article/details/11639185
说明:本分析基于mstar801平台Linux2.6.35.11内核,其他内核版本仅供参考。
一、程序在内核中的位置
1.usb host做为pci总线下的一个设备存在(嵌入式系统中有可能也会直接挂在CPU上);这部分驱动由厂家实现,本分析以mstar为例。
2.USB总线驱动
kernel/drivers/usb/core/driver.c
[cpp] view
plain copy
EXPORT_SYMBOL_GPL(usb_register_driver);
EXPORT_SYMBOL_GPL(usb_deregister);
EXPORT_SYMBOL_GPL(usb_register_device_driver);
EXPORT_SYMBOL_GPL(usb_deregister_device_driver);
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
};
kernel/drivers/usb/core/usb.c
[cpp] view
plain copy
static int __init usb_init(void){
bus_register(&usb_bus_type);
usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
}
3.uvc camera设备驱动
kernel/drivers/media/video/uvc/uvc_driver.c
[cpp] view
plain copy
usb_register(&uvc_driver.driver);
二、所有总线、设备和驱动的注册函数
1.设备注册
kernel/drivers/base/core.c
[cpp] view
plain copy
int device_register(struct device *dev){
device_initialize(dev);
return device_add(dev);
}
int device_add(struct device *dev){ //所有的设备注册都需要走这里!!!!!!
error = bus_add_device(dev);
kobject_uevent(&dev->kobj, KOBJ_ADD); //上报uevent事件
bus_probe_device(dev); //添加到总线
}
2.驱动注册
kernel/drivers/base/driver.c
[cpp] view
plain copy
int driver_register(struct device_driver *drv){ //所有的驱动注册都要走这里!!!!!!!
ret = bus_add_driver(drv); //添加到总线
}
3.总线注册
kernel/drivers/base/bus.c
[cpp] view
plain copy
int bus_register(struct bus_type *bus);
三、具体分析
情况一:当插入USB设备时USB host会检测到这一事件;然后通过USB core去匹配驱动。
当守护程序第一次运行(特殊USB设备USB hub就是这种情况)或usb port上状态发生变化(其余所有USB设备插入都是这种情况)守护进程被唤醒时,会运行hub_events函数、USB的枚举过程就是由它完成。
1.USB host部分代码
说明:从硬件层面来看,ehci主控器从PCI总线桥接,是PCI驱动程序实例。
kernel/drivers/usb/host/ehci-hcd.c
[cpp] view
plain copy
module_init(ehci_hcd_init);
#define PCI_DRIVER ehci_pci_driver //利用pci中断
#define PLATFORM_DRIVER ehci_hcd_mstar_driver //利用定时器轮询
static int __init ehci_hcd_init(void){
#ifdef PLATFORM_DRIVER
platform_driver_register(&PLATFORM_DRIVER);
#endif
#ifdef PCI_DRIVER
pci_register_driver(&PCI_DRIVER);
#endif
}
下边分两种情况:
==============================================
定时器轮询:
kernel/drivers/usb/host/ehci-mstar.c
[cpp] view
plain copy
static struct platform_driver ehci_hcd_mstar_driver = {
.probe = ehci_hcd_mstar_drv_probe,
};
static int ehci_hcd_mstar_drv_probe(struct platform_device *pdev){
usb_ehci_mstar_probe(&ehci_mstar_hc_driver, &hcd, pdev);
}
int usb_ehci_mstar_probe(const struct hc_driver *driver,struct usb_hcd **hcd_out, struct platform_device *dev){
usb_create_hcd(driver, &dev->dev, "mstar");
}
kernel/drivers/usb/core/hcd.c
[cpp] view
plain copy
struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name){
return usb_create_shared_hcd(driver, dev, bus_name, NULL);
}
struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name, struct usb_hcd *primary_hcd){
init_timer(&hcd->rh_timer);
hcd->rh_timer.function = rh_timer_func;
}
static void rh_timer_func (unsigned long _hcd) {
usb_hcd_poll_rh_status((struct usb_hcd *) _hcd);
}
void usb_hcd_poll_rh_status(struct usb_hcd *hcd){
hcd->driver->hub_status_data(hcd, buffer);
usb_hcd_giveback_urb(hcd, urb, 0);
}
===================
当有pci中断发生后:
kernel/drivers/usb/host/ehci-pci.c
[cpp] view
plain copy
static struct pci_driver ehci_pci_driver = {
.id_table = pci_ids,
}
static const struct pci_device_id pci_ids [] = { {
.driver_data = (unsigned long) &ehci_pci_hc_driver,
}
}
static const struct hc_driver ehci_pci_hc_driver = {
.irq = ehci_irq, //中断
.hub_status_data = ehci_hub_status_data,
.urb_enqueue = ehci_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue,
}
kernel/drivers/usb/host/ehci-hcd.c
[cpp] view
plain copy
static irqreturn_t ehci_irq (struct usb_hcd *hcd){
usb_hcd_poll_rh_status(hcd);
}
kernel/drivers/usb/core/hcd.c
[cpp] view
plain copy
void usb_hcd_poll_rh_status(struct usb_hcd *hcd){
hcd->driver->hub_status_data(hcd, buffer);
usb_hcd_giveback_urb(hcd, urb, 0);
}
kernel/drivers/usb/host/ehci-hub.c
[cpp] view
plain copy
static int ehci_hub_status_data (struct usb_hcd *hcd, char *buf){
}
=====================================================================
从以上分析可以看出;不论是定时器轮询还是pci中断,最终都会执行usb_hcd_giveback_urb函数:
kernel/drivers/usb/core/hcd.c
[cpp] view
plain copy
void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status){
urb->complete (urb);
}
而上处urv->complete函数其实就是如下的hub_irq函数,后边会分析:
kernel/drivers/usb/core/hub.c
[cpp] view
plain copy
static void hub_irq(struct urb *urb){
kick_khubd(hub);
}
2.USB core即USB总线部分代码——可以看到hub是第一个USB设备而且与USB总线密切相关
kernel/drivers/usb/core/usb.c
[cpp] view
plain copy
subsys_initcall(usb_init);
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
};
static int __init usb_init(void){
bus_register(&usb_bus_type);
usb_register_device_driver(&usb_generic_driver, THIS_MODULE); //USB设备驱动,在没有root hub时使用
usb_hub_init();
}
kernel/drivers/usb/core/hub.c
[cpp] view
plain copy
static struct usb_driver hub_driver = {
.name = "hub",
.probe = hub_probe,
};
int usb_hub_init(void){
usb_register(&hub_driver); //USB设备驱动,第一个USB设备—root hub
kthread_run(hub_thread, NULL, "khubd");
}
=====================================
插句话:下边就是之前我们说的urv->complete被赋为hub_irq函数的过程;
这里说明一下:hub的探测函数的执行是在守护线程第一次运行时的情况;为什么不需要USB总线轮询后或PCI总线中断后就执行?我们会在后边hub创建线程处看到。
[cpp] view
plain copy
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id){
hub_configure(hub, endpoint);
}
static int hub_configure(struct usb_hub *hub,struct usb_endpoint_descriptor *endpoint){
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, hub, endpoint->bInterval);
}
kernel/include/linux/usb.h
[cpp] view
plain copy
static inline void usb_fill_int_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer, int buffer_length,
usb_complete_t complete_fn, void *context, int interval){
urb->complete = complete_fn;
}
=============================================
kernel/drivers/usb/core/hub.c
这里特别强调:hub设备是第一个USB设备,也是必须的USB设备;它不需要通过USB总线定时器轮询或PCI总线中断来触发。从下边代码也可以看出,在执行第一次hub_events之后(hub驱动的probe函数被执行、urv->complete被赋值hub_irq),该线程才会睡眠!
[cpp] view
plain copy
static int hub_thread(void *__unused){
do {
hub_events(); //重要!最核心部分
wait_event_freezable(khubd_wait,!list_empty(&hub_event_list) || kthread_should_stop());
} while (!kthread_should_stop() || !list_empty(&hub_event_list));
}
//内核守护线程khubd,它被kick_khubd唤醒(当prot上状态发生变化时,USB host会调用usb_hcd_poll_rh_status去查询usb root hub port状态,并调用hub中的interrupt urb的回调函数hub_irq,最终去唤醒usb内核守护线程)、通过自身调用wait_event_freezable进入睡眠。
static void hub_events(void){
if (connect_change) hub_port_connect_change(hub, i, portstatus, portchange);
}
static void hub_port_connect_change(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange){
status = hub_port_init(hub, udev, port1, i);
status = usb_new_device(udev);
}
int usb_new_device(struct usb_device *udev){
err = device_add(&udev->dev);
(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
/*
kernel/drivers/usb/core/endpoint.c
int usb_create_ep_devs(struct device *parent,struct usb_host_endpoint *endpoint,struct usb_device *udev){
device_register(&ep_dev->dev);
}
*/
}
kernel/drivers/base/core.c
[cpp] view
plain copy
int device_add(struct device *dev){ //所有的设备注册都需要走这里!!!!!!
error = bus_add_device(dev);
kobject_uevent(&dev->kobj, KOBJ_ADD); //上报uevent事件
bus_probe_device(dev);
}
kernel/drivers/base/bus.c
[cpp] view
plain copy
void bus_probe_device(struct device *dev){
ret = device_attach(dev);
}
kernel/drivers/base/dd.c
[cpp] view
plain copy
int device_attach(struct device *dev){
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
}
kernel/drivers/base/bus.c
[cpp] view
plain copy
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,void *data, int (*fn)(struct device_driver *, void *)){
while ((drv = next_driver(&i)) && !error)
error = fn(drv, data);
}
kernel/drivers/base/dd.c
[cpp] view
plain copy
static int __device_attach(struct device_driver *drv, void *data){
if (!driver_match_device(drv, dev))
return 0;
/*
kernel/drivers/base/base.h
static inline int driver_match_device(struct device_driver *drv,struct device *dev){
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
kernel/drivers/usb/core/driver.c
static int usb_device_match(struct device *dev, struct device_driver *drv){
intf = to_usb_interface(dev);
usb_drv = to_usb_driver(drv);
if (id) return 1;
id = usb_match_dynamic_id(intf, usb_drv);
if (id) return 1;
return 0;
}
*/
return driver_probe_device(drv, dev);
}
int driver_probe_device(struct device_driver *drv, struct device *dev){
ret = really_probe(dev, drv);
}
static int really_probe(struct device *dev, struct device_driver *drv){
dev->driver = drv;
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret) goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret) goto probe_failed;
}
}
情况二:当加入USB设备驱动时,也会通过USB core调用mattch函数去匹配设备。
kernel/drivers/media/video/uvc/uvc_driver.c
[cpp] view
plain copy
struct uvc_driver uvc_driver = {
.driver = {
.name = "uvcvideo",
.probe = uvc_probe,
.disconnect = uvc_disconnect,
.suspend = uvc_suspend,
.resume = uvc_resume,
.reset_resume = uvc_reset_resume,
.id_table = uvc_ids,
.supports_autosuspend = 1,
},
};
module_init(uvc_init);
static int __init uvc_init(void){
result = usb_register(&uvc_driver.driver);
}
kernel/include/linux/usb.h
[cpp] view
plain copy
static inline int usb_register(struct usb_driver *driver){
return usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);
}
kernel/drivers/usb/core/driver.c
[cpp] view
plain copy
int usb_register_driver(struct usb_driver *new_driver, struct module *owner, const char *mod_name){
retval = driver_register(&new_driver->drvwrap.driver);
}
kernel/drivers/base/driver.c
[cpp] view
plain copy
int driver_register(struct device_driver *drv){ //所有的驱动注册都要走这里!!!!!!!
ret = bus_add_driver(drv);
}
kernel/drivers/base/bus.c
[cpp] view
plain copy
int bus_add_driver(struct device_driver *drv){
error = driver_attach(drv);
kobject_uevent(&priv->kobj, KOBJ_ADD);
}
kernel/drivers/base/dd.c
[cpp] view
plain copy
int driver_attach(struct device_driver *drv){
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
kernel/drivers/base/bus.c
[cpp] view
plain copy
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *)){
while ((dev = next_device(&i)) && !error) error = fn(dev, data);
}
kernel/drivers/base/dd.c
[cpp] view
plain copy
static int __driver_attach(struct device *dev, void *data){
if (!driver_match_device(drv, dev)) return 0;
/*
kernel/drivers/base/base.h
static inline int driver_match_device(struct device_driver *drv,struct device *dev){
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
kernel/drivers/usb/core/driver.c
static int usb_device_match(struct device *dev, struct device_driver *drv){
intf = to_usb_interface(dev);
usb_drv = to_usb_driver(drv);
if (id) return 1;
id = usb_match_dynamic_id(intf, usb_drv);
if (id) return 1;
return 0;
}
*/
if (!dev->driver) driver_probe_device(drv, dev);
}
int driver_probe_device(struct device_driver *drv, struct device *dev){
ret = really_probe(dev, drv);
}
static int really_probe(struct device *dev, struct device_driver *drv){
dev->driver = drv;
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret) goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret) goto probe_failed;
}
}
3.总结
经过分析,总结:
(1).当总线上插入设备、总线会调用设备注册函数device_add/device_register;
(2).当insmod设备驱动、module_init函数里边一定有driver_register;
(3).通过上边分析,如上两个函数最终都会调用到总线驱动的match函数、进行匹配;如USB的总线match函数如下:
kernel/drivers/usb/core/driver.c
[cpp] view
plain copy
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
.pm = &usb_bus_pm_ops,
};
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
/* devices and interfaces are handled separately */
if (is_usb_device(dev)) {
/* interface drivers never match devices */
if (!is_usb_device_driver(drv))
return 0;
/* TODO: Add real matching code */
return 1;
} else if (is_usb_interface(dev)) {
struct usb_interface *intf;
struct usb_driver *usb_drv;
const struct usb_device_id *id;
/* device drivers never match interfaces */
if (is_usb_device_driver(drv))
return 0;
intf = to_usb_interface(dev);
usb_drv = to_usb_driver(drv);
id = usb_match_id(intf, usb_drv->id_table);//USB是匹配驱动中的id_table
if (id)
return 1;
id = usb_match_dynamic_id(intf, usb_drv);
if (id)
return 1;
}
return 0;
}
下边也看看UVC Camera驱动的id_table:
kernel/drivers/media/video/uvc/uvc_driver.c
[cpp] view
plain copy
struct uvc_driver uvc_driver = {
.driver = {
.name = "uvcvideo",
.probe = uvc_probe,
.disconnect = uvc_disconnect,
.suspend = uvc_suspend,
.resume = uvc_resume,
.reset_resume = uvc_reset_resume,
.id_table = uvc_ids,
.supports_autosuspend = 1,
},
};
static struct usb_device_id uvc_ids[] = {
/* Microsoft Lifecam NX-6000 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x045e,
.idProduct = 0x00f8,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX },
/* Microsoft Lifecam VX-7000 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x045e,
.idProduct = 0x0723,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX },
/* Logitech Quickcam Fusion */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x046d,
.idProduct = 0x08c1,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0 },
/* Logitech Quickcam Orbit MP */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x046d,
.idProduct = 0x08c2,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0 },
/* Logitech Quickcam Pro for Notebook */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x046d,
.idProduct = 0x08c3,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0 },
/* Logitech Quickcam Pro 5000 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x046d,
.idProduct = 0x08c5,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0 },
/* Logitech Quickcam OEM Dell Notebook */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x046d,
.idProduct = 0x08c6,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0 },
/* Logitech Quickcam OEM Cisco VT Camera II */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x046d,
.idProduct = 0x08c7,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0 },
/* Apple Built-In iSight */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x05ac,
.idProduct = 0x8501,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX
| UVC_QUIRK_BUILTIN_ISIGHT },
/* Genesys Logic USB 2.0 PC Camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x05e3,
.idProduct = 0x0505,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
/* MT6227 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0e8d,
.idProduct = 0x0004,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX },
/* Syntek (HP Spartan) */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x174f,
.idProduct = 0x5212,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Syntek (Samsung Q310) */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x174f,
.idProduct = 0x5931,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Asus F9SG */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x174f,
.idProduct = 0x8a31,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Syntek (Asus U3S) */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x174f,
.idProduct = 0x8a33,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Lenovo Thinkpad SL500 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x17ef,
.idProduct = 0x480b,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Ecamm Pico iMage */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x18cd,
.idProduct = 0xcafe,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS },
/* Bodelin ProScopeHR */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_DEV_HI
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x19ab,
.idProduct = 0x1000,
.bcdDevice_hi = 0x0126,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STATUS_INTERVAL },
/* SiGma Micro USB Web Camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x1c4f,
.idProduct = 0x3000,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX
| UVC_QUIRK_IGNORE_SELECTOR_UNIT
| UVC_QUIRK_PRUNE_CONTROLS },
/* Generic USB Video Class */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
{}
};
(4).如果匹配成功,会执行设备驱动的probe函数。我们关心的设备节点的创建也是在设备驱动的探测函数中被创建(因为这时的设备注册会附带主次设备号,内核通过netlink上报uevent事件后、用户空间的udevd服务会执行mknod创建设备节点)详见Linux驱动中uevent、netlink及kobject初探——kobject部分 和 Linux驱动中uevent、netlink及kobject初探——ueventd部分。
三、usb相关结构说明
1.设备描述符
[plain] view
plain copy
struct usb_device_descriptor {
__u8 bLength; --描述符长度
__u8 bDescriptorType; --描述符类型:设备描述符0x01
__le16 bcdUSB; --usb规范版本号
__u8 bDeviceClass; --类代码
__u8 bDeviceSubClass; --子类代码
__u8 bDeviceProtocol; --协议代码
__u8 bMaxPacketSize0; --端点0支持最大数
__le16 idVendor; --供应商ID
__le16 idProduct; --产品ID
__le16 bcdDevice; --设备版本号
__u8 iManufacturer; --供应商字符串描述符的索引值
__u8 iProduct; --产品字符串描述符的索引值
__u8 iSerialNumber; --设备序列号
__u8 bNumConfigurations; --所支持的配置数
} __attribute__ ((packed)); --结构体字符类型对齐
2.配置描述符
[plain] view
plain copy
struct usb_config_descriptor {
__u8 bLength; --描述符长度
__u8 bDescriptorType; --描述符类型
__le16 wTotalLength; --配置信息的总长度
__u8 bNumInterfaces; --所支持的接口数
__u8 bConfigurationValue; --配置值
__u8 iConfiguration; --字符串描述符的索引值
__u8 bmAttributes; --配置特征
__u8 bMaxPower; --所需最大的总线电流
} __attribute__ ((packed));
3.接口描述符
[plain] view
plain copy
struct usb_interface_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bInterfaceNumber; --接口编号
__u8 bAlternateSetting; --备用接口标号
__u8 bNumEndpoints; --接口数目
__u8 bInterfaceClass; --接口类型
__u8 bInterfaceSubClass; --接口子类型
__u8 bInterfaceProtocol; --接口所用协议
__u8 iInterface; --接口索引字符串数值
} __attribute__ ((packed));
4.端点描述符
[plain] view
plain copy
struct usb_endpoint_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bEndpointAddress; --端点号包括传输方向
__u8 bmAttributes; --端点属性
__le16 wMaxPacketSize; --最大数据包长度
__u8 bInterval; --访问间隔
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));
usb总线驱动中对于设备和设备驱动的匹配函数,其实就是上述1和3的匹配过程
见:kernel/drivers/usb/core/driver.c中usb_device_match函数,这部分可以进一步分析;在此、我不再分析。
大致会匹配设备所属类(Input设备?Camera设备?Audio设备?或显示设备等)和VID、PID。
五、urb数据传输分析
未完待续
说明:本分析基于mstar801平台Linux2.6.35.11内核,其他内核版本仅供参考。
一、程序在内核中的位置
1.usb host做为pci总线下的一个设备存在(嵌入式系统中有可能也会直接挂在CPU上);这部分驱动由厂家实现,本分析以mstar为例。
2.USB总线驱动
kernel/drivers/usb/core/driver.c
[cpp] view
plain copy
EXPORT_SYMBOL_GPL(usb_register_driver);
EXPORT_SYMBOL_GPL(usb_deregister);
EXPORT_SYMBOL_GPL(usb_register_device_driver);
EXPORT_SYMBOL_GPL(usb_deregister_device_driver);
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
};
kernel/drivers/usb/core/usb.c
[cpp] view
plain copy
static int __init usb_init(void){
bus_register(&usb_bus_type);
usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
}
3.uvc camera设备驱动
kernel/drivers/media/video/uvc/uvc_driver.c
[cpp] view
plain copy
usb_register(&uvc_driver.driver);
二、所有总线、设备和驱动的注册函数
1.设备注册
kernel/drivers/base/core.c
[cpp] view
plain copy
int device_register(struct device *dev){
device_initialize(dev);
return device_add(dev);
}
int device_add(struct device *dev){ //所有的设备注册都需要走这里!!!!!!
error = bus_add_device(dev);
kobject_uevent(&dev->kobj, KOBJ_ADD); //上报uevent事件
bus_probe_device(dev); //添加到总线
}
2.驱动注册
kernel/drivers/base/driver.c
[cpp] view
plain copy
int driver_register(struct device_driver *drv){ //所有的驱动注册都要走这里!!!!!!!
ret = bus_add_driver(drv); //添加到总线
}
3.总线注册
kernel/drivers/base/bus.c
[cpp] view
plain copy
int bus_register(struct bus_type *bus);
三、具体分析
情况一:当插入USB设备时USB host会检测到这一事件;然后通过USB core去匹配驱动。
当守护程序第一次运行(特殊USB设备USB hub就是这种情况)或usb port上状态发生变化(其余所有USB设备插入都是这种情况)守护进程被唤醒时,会运行hub_events函数、USB的枚举过程就是由它完成。
1.USB host部分代码
说明:从硬件层面来看,ehci主控器从PCI总线桥接,是PCI驱动程序实例。
kernel/drivers/usb/host/ehci-hcd.c
[cpp] view
plain copy
module_init(ehci_hcd_init);
#define PCI_DRIVER ehci_pci_driver //利用pci中断
#define PLATFORM_DRIVER ehci_hcd_mstar_driver //利用定时器轮询
static int __init ehci_hcd_init(void){
#ifdef PLATFORM_DRIVER
platform_driver_register(&PLATFORM_DRIVER);
#endif
#ifdef PCI_DRIVER
pci_register_driver(&PCI_DRIVER);
#endif
}
下边分两种情况:
==============================================
定时器轮询:
kernel/drivers/usb/host/ehci-mstar.c
[cpp] view
plain copy
static struct platform_driver ehci_hcd_mstar_driver = {
.probe = ehci_hcd_mstar_drv_probe,
};
static int ehci_hcd_mstar_drv_probe(struct platform_device *pdev){
usb_ehci_mstar_probe(&ehci_mstar_hc_driver, &hcd, pdev);
}
int usb_ehci_mstar_probe(const struct hc_driver *driver,struct usb_hcd **hcd_out, struct platform_device *dev){
usb_create_hcd(driver, &dev->dev, "mstar");
}
kernel/drivers/usb/core/hcd.c
[cpp] view
plain copy
struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name){
return usb_create_shared_hcd(driver, dev, bus_name, NULL);
}
struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, struct device *dev, const char *bus_name, struct usb_hcd *primary_hcd){
init_timer(&hcd->rh_timer);
hcd->rh_timer.function = rh_timer_func;
}
static void rh_timer_func (unsigned long _hcd) {
usb_hcd_poll_rh_status((struct usb_hcd *) _hcd);
}
void usb_hcd_poll_rh_status(struct usb_hcd *hcd){
hcd->driver->hub_status_data(hcd, buffer);
usb_hcd_giveback_urb(hcd, urb, 0);
}
===================
当有pci中断发生后:
kernel/drivers/usb/host/ehci-pci.c
[cpp] view
plain copy
static struct pci_driver ehci_pci_driver = {
.id_table = pci_ids,
}
static const struct pci_device_id pci_ids [] = { {
.driver_data = (unsigned long) &ehci_pci_hc_driver,
}
}
static const struct hc_driver ehci_pci_hc_driver = {
.irq = ehci_irq, //中断
.hub_status_data = ehci_hub_status_data,
.urb_enqueue = ehci_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue,
}
kernel/drivers/usb/host/ehci-hcd.c
[cpp] view
plain copy
static irqreturn_t ehci_irq (struct usb_hcd *hcd){
usb_hcd_poll_rh_status(hcd);
}
kernel/drivers/usb/core/hcd.c
[cpp] view
plain copy
void usb_hcd_poll_rh_status(struct usb_hcd *hcd){
hcd->driver->hub_status_data(hcd, buffer);
usb_hcd_giveback_urb(hcd, urb, 0);
}
kernel/drivers/usb/host/ehci-hub.c
[cpp] view
plain copy
static int ehci_hub_status_data (struct usb_hcd *hcd, char *buf){
}
=====================================================================
从以上分析可以看出;不论是定时器轮询还是pci中断,最终都会执行usb_hcd_giveback_urb函数:
kernel/drivers/usb/core/hcd.c
[cpp] view
plain copy
void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status){
urb->complete (urb);
}
而上处urv->complete函数其实就是如下的hub_irq函数,后边会分析:
kernel/drivers/usb/core/hub.c
[cpp] view
plain copy
static void hub_irq(struct urb *urb){
kick_khubd(hub);
}
2.USB core即USB总线部分代码——可以看到hub是第一个USB设备而且与USB总线密切相关
kernel/drivers/usb/core/usb.c
[cpp] view
plain copy
subsys_initcall(usb_init);
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
};
static int __init usb_init(void){
bus_register(&usb_bus_type);
usb_register_device_driver(&usb_generic_driver, THIS_MODULE); //USB设备驱动,在没有root hub时使用
usb_hub_init();
}
kernel/drivers/usb/core/hub.c
[cpp] view
plain copy
static struct usb_driver hub_driver = {
.name = "hub",
.probe = hub_probe,
};
int usb_hub_init(void){
usb_register(&hub_driver); //USB设备驱动,第一个USB设备—root hub
kthread_run(hub_thread, NULL, "khubd");
}
=====================================
插句话:下边就是之前我们说的urv->complete被赋为hub_irq函数的过程;
这里说明一下:hub的探测函数的执行是在守护线程第一次运行时的情况;为什么不需要USB总线轮询后或PCI总线中断后就执行?我们会在后边hub创建线程处看到。
[cpp] view
plain copy
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id){
hub_configure(hub, endpoint);
}
static int hub_configure(struct usb_hub *hub,struct usb_endpoint_descriptor *endpoint){
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq, hub, endpoint->bInterval);
}
kernel/include/linux/usb.h
[cpp] view
plain copy
static inline void usb_fill_int_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, void *transfer_buffer, int buffer_length,
usb_complete_t complete_fn, void *context, int interval){
urb->complete = complete_fn;
}
=============================================
kernel/drivers/usb/core/hub.c
这里特别强调:hub设备是第一个USB设备,也是必须的USB设备;它不需要通过USB总线定时器轮询或PCI总线中断来触发。从下边代码也可以看出,在执行第一次hub_events之后(hub驱动的probe函数被执行、urv->complete被赋值hub_irq),该线程才会睡眠!
[cpp] view
plain copy
static int hub_thread(void *__unused){
do {
hub_events(); //重要!最核心部分
wait_event_freezable(khubd_wait,!list_empty(&hub_event_list) || kthread_should_stop());
} while (!kthread_should_stop() || !list_empty(&hub_event_list));
}
//内核守护线程khubd,它被kick_khubd唤醒(当prot上状态发生变化时,USB host会调用usb_hcd_poll_rh_status去查询usb root hub port状态,并调用hub中的interrupt urb的回调函数hub_irq,最终去唤醒usb内核守护线程)、通过自身调用wait_event_freezable进入睡眠。
static void hub_events(void){
if (connect_change) hub_port_connect_change(hub, i, portstatus, portchange);
}
static void hub_port_connect_change(struct usb_hub *hub, int port1, u16 portstatus, u16 portchange){
status = hub_port_init(hub, udev, port1, i);
status = usb_new_device(udev);
}
int usb_new_device(struct usb_device *udev){
err = device_add(&udev->dev);
(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
/*
kernel/drivers/usb/core/endpoint.c
int usb_create_ep_devs(struct device *parent,struct usb_host_endpoint *endpoint,struct usb_device *udev){
device_register(&ep_dev->dev);
}
*/
}
kernel/drivers/base/core.c
[cpp] view
plain copy
int device_add(struct device *dev){ //所有的设备注册都需要走这里!!!!!!
error = bus_add_device(dev);
kobject_uevent(&dev->kobj, KOBJ_ADD); //上报uevent事件
bus_probe_device(dev);
}
kernel/drivers/base/bus.c
[cpp] view
plain copy
void bus_probe_device(struct device *dev){
ret = device_attach(dev);
}
kernel/drivers/base/dd.c
[cpp] view
plain copy
int device_attach(struct device *dev){
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
}
kernel/drivers/base/bus.c
[cpp] view
plain copy
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,void *data, int (*fn)(struct device_driver *, void *)){
while ((drv = next_driver(&i)) && !error)
error = fn(drv, data);
}
kernel/drivers/base/dd.c
[cpp] view
plain copy
static int __device_attach(struct device_driver *drv, void *data){
if (!driver_match_device(drv, dev))
return 0;
/*
kernel/drivers/base/base.h
static inline int driver_match_device(struct device_driver *drv,struct device *dev){
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
kernel/drivers/usb/core/driver.c
static int usb_device_match(struct device *dev, struct device_driver *drv){
intf = to_usb_interface(dev);
usb_drv = to_usb_driver(drv);
if (id) return 1;
id = usb_match_dynamic_id(intf, usb_drv);
if (id) return 1;
return 0;
}
*/
return driver_probe_device(drv, dev);
}
int driver_probe_device(struct device_driver *drv, struct device *dev){
ret = really_probe(dev, drv);
}
static int really_probe(struct device *dev, struct device_driver *drv){
dev->driver = drv;
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret) goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret) goto probe_failed;
}
}
情况二:当加入USB设备驱动时,也会通过USB core调用mattch函数去匹配设备。
kernel/drivers/media/video/uvc/uvc_driver.c
[cpp] view
plain copy
struct uvc_driver uvc_driver = {
.driver = {
.name = "uvcvideo",
.probe = uvc_probe,
.disconnect = uvc_disconnect,
.suspend = uvc_suspend,
.resume = uvc_resume,
.reset_resume = uvc_reset_resume,
.id_table = uvc_ids,
.supports_autosuspend = 1,
},
};
module_init(uvc_init);
static int __init uvc_init(void){
result = usb_register(&uvc_driver.driver);
}
kernel/include/linux/usb.h
[cpp] view
plain copy
static inline int usb_register(struct usb_driver *driver){
return usb_register_driver(driver, THIS_MODULE, KBUILD_MODNAME);
}
kernel/drivers/usb/core/driver.c
[cpp] view
plain copy
int usb_register_driver(struct usb_driver *new_driver, struct module *owner, const char *mod_name){
retval = driver_register(&new_driver->drvwrap.driver);
}
kernel/drivers/base/driver.c
[cpp] view
plain copy
int driver_register(struct device_driver *drv){ //所有的驱动注册都要走这里!!!!!!!
ret = bus_add_driver(drv);
}
kernel/drivers/base/bus.c
[cpp] view
plain copy
int bus_add_driver(struct device_driver *drv){
error = driver_attach(drv);
kobject_uevent(&priv->kobj, KOBJ_ADD);
}
kernel/drivers/base/dd.c
[cpp] view
plain copy
int driver_attach(struct device_driver *drv){
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
kernel/drivers/base/bus.c
[cpp] view
plain copy
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *)){
while ((dev = next_device(&i)) && !error) error = fn(dev, data);
}
kernel/drivers/base/dd.c
[cpp] view
plain copy
static int __driver_attach(struct device *dev, void *data){
if (!driver_match_device(drv, dev)) return 0;
/*
kernel/drivers/base/base.h
static inline int driver_match_device(struct device_driver *drv,struct device *dev){
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
kernel/drivers/usb/core/driver.c
static int usb_device_match(struct device *dev, struct device_driver *drv){
intf = to_usb_interface(dev);
usb_drv = to_usb_driver(drv);
if (id) return 1;
id = usb_match_dynamic_id(intf, usb_drv);
if (id) return 1;
return 0;
}
*/
if (!dev->driver) driver_probe_device(drv, dev);
}
int driver_probe_device(struct device_driver *drv, struct device *dev){
ret = really_probe(dev, drv);
}
static int really_probe(struct device *dev, struct device_driver *drv){
dev->driver = drv;
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret) goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret) goto probe_failed;
}
}
3.总结
经过分析,总结:
(1).当总线上插入设备、总线会调用设备注册函数device_add/device_register;
(2).当insmod设备驱动、module_init函数里边一定有driver_register;
(3).通过上边分析,如上两个函数最终都会调用到总线驱动的match函数、进行匹配;如USB的总线match函数如下:
kernel/drivers/usb/core/driver.c
[cpp] view
plain copy
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
.pm = &usb_bus_pm_ops,
};
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
/* devices and interfaces are handled separately */
if (is_usb_device(dev)) {
/* interface drivers never match devices */
if (!is_usb_device_driver(drv))
return 0;
/* TODO: Add real matching code */
return 1;
} else if (is_usb_interface(dev)) {
struct usb_interface *intf;
struct usb_driver *usb_drv;
const struct usb_device_id *id;
/* device drivers never match interfaces */
if (is_usb_device_driver(drv))
return 0;
intf = to_usb_interface(dev);
usb_drv = to_usb_driver(drv);
id = usb_match_id(intf, usb_drv->id_table);//USB是匹配驱动中的id_table
if (id)
return 1;
id = usb_match_dynamic_id(intf, usb_drv);
if (id)
return 1;
}
return 0;
}
下边也看看UVC Camera驱动的id_table:
kernel/drivers/media/video/uvc/uvc_driver.c
[cpp] view
plain copy
struct uvc_driver uvc_driver = {
.driver = {
.name = "uvcvideo",
.probe = uvc_probe,
.disconnect = uvc_disconnect,
.suspend = uvc_suspend,
.resume = uvc_resume,
.reset_resume = uvc_reset_resume,
.id_table = uvc_ids,
.supports_autosuspend = 1,
},
};
static struct usb_device_id uvc_ids[] = {
/* Microsoft Lifecam NX-6000 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x045e,
.idProduct = 0x00f8,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX },
/* Microsoft Lifecam VX-7000 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x045e,
.idProduct = 0x0723,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX },
/* Logitech Quickcam Fusion */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x046d,
.idProduct = 0x08c1,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0 },
/* Logitech Quickcam Orbit MP */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x046d,
.idProduct = 0x08c2,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0 },
/* Logitech Quickcam Pro for Notebook */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x046d,
.idProduct = 0x08c3,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0 },
/* Logitech Quickcam Pro 5000 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x046d,
.idProduct = 0x08c5,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0 },
/* Logitech Quickcam OEM Dell Notebook */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x046d,
.idProduct = 0x08c6,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0 },
/* Logitech Quickcam OEM Cisco VT Camera II */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x046d,
.idProduct = 0x08c7,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0 },
/* Apple Built-In iSight */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x05ac,
.idProduct = 0x8501,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX
| UVC_QUIRK_BUILTIN_ISIGHT },
/* Genesys Logic USB 2.0 PC Camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x05e3,
.idProduct = 0x0505,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
/* MT6227 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x0e8d,
.idProduct = 0x0004,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX },
/* Syntek (HP Spartan) */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x174f,
.idProduct = 0x5212,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Syntek (Samsung Q310) */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x174f,
.idProduct = 0x5931,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Asus F9SG */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x174f,
.idProduct = 0x8a31,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Syntek (Asus U3S) */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x174f,
.idProduct = 0x8a33,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Lenovo Thinkpad SL500 */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x17ef,
.idProduct = 0x480b,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STREAM_NO_FID },
/* Ecamm Pico iMage */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x18cd,
.idProduct = 0xcafe,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS },
/* Bodelin ProScopeHR */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_DEV_HI
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x19ab,
.idProduct = 0x1000,
.bcdDevice_hi = 0x0126,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_STATUS_INTERVAL },
/* SiGma Micro USB Web Camera */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = 0x1c4f,
.idProduct = 0x3000,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX
| UVC_QUIRK_IGNORE_SELECTOR_UNIT
| UVC_QUIRK_PRUNE_CONTROLS },
/* Generic USB Video Class */
{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },
{}
};
(4).如果匹配成功,会执行设备驱动的probe函数。我们关心的设备节点的创建也是在设备驱动的探测函数中被创建(因为这时的设备注册会附带主次设备号,内核通过netlink上报uevent事件后、用户空间的udevd服务会执行mknod创建设备节点)详见Linux驱动中uevent、netlink及kobject初探——kobject部分 和 Linux驱动中uevent、netlink及kobject初探——ueventd部分。
三、usb相关结构说明
1.设备描述符
[plain] view
plain copy
struct usb_device_descriptor {
__u8 bLength; --描述符长度
__u8 bDescriptorType; --描述符类型:设备描述符0x01
__le16 bcdUSB; --usb规范版本号
__u8 bDeviceClass; --类代码
__u8 bDeviceSubClass; --子类代码
__u8 bDeviceProtocol; --协议代码
__u8 bMaxPacketSize0; --端点0支持最大数
__le16 idVendor; --供应商ID
__le16 idProduct; --产品ID
__le16 bcdDevice; --设备版本号
__u8 iManufacturer; --供应商字符串描述符的索引值
__u8 iProduct; --产品字符串描述符的索引值
__u8 iSerialNumber; --设备序列号
__u8 bNumConfigurations; --所支持的配置数
} __attribute__ ((packed)); --结构体字符类型对齐
2.配置描述符
[plain] view
plain copy
struct usb_config_descriptor {
__u8 bLength; --描述符长度
__u8 bDescriptorType; --描述符类型
__le16 wTotalLength; --配置信息的总长度
__u8 bNumInterfaces; --所支持的接口数
__u8 bConfigurationValue; --配置值
__u8 iConfiguration; --字符串描述符的索引值
__u8 bmAttributes; --配置特征
__u8 bMaxPower; --所需最大的总线电流
} __attribute__ ((packed));
3.接口描述符
[plain] view
plain copy
struct usb_interface_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bInterfaceNumber; --接口编号
__u8 bAlternateSetting; --备用接口标号
__u8 bNumEndpoints; --接口数目
__u8 bInterfaceClass; --接口类型
__u8 bInterfaceSubClass; --接口子类型
__u8 bInterfaceProtocol; --接口所用协议
__u8 iInterface; --接口索引字符串数值
} __attribute__ ((packed));
4.端点描述符
[plain] view
plain copy
struct usb_endpoint_descriptor {
__u8 bLength;
__u8 bDescriptorType;
__u8 bEndpointAddress; --端点号包括传输方向
__u8 bmAttributes; --端点属性
__le16 wMaxPacketSize; --最大数据包长度
__u8 bInterval; --访问间隔
__u8 bRefresh;
__u8 bSynchAddress;
} __attribute__ ((packed));
usb总线驱动中对于设备和设备驱动的匹配函数,其实就是上述1和3的匹配过程
见:kernel/drivers/usb/core/driver.c中usb_device_match函数,这部分可以进一步分析;在此、我不再分析。
大致会匹配设备所属类(Input设备?Camera设备?Audio设备?或显示设备等)和VID、PID。
五、urb数据传输分析
未完待续
相关文章推荐
- 《Linux总线、设备与驱动》USB设备发现机制
- 《Linux总线、设备与驱动》USB设备发现机制
- 《Linux总线、设备与驱动》USB设备发现机制
- 《Linux总线、设备与驱动》USB设备发现机制
- 《Linux总线、设备与驱动》USB设备发现机制
- 很好转《Linux总线、设备与驱动》USB设备发现机制
- 《Linux总线、设备与驱动》USB设备发现机制
- 和菜鸟一起学linux总线驱动之初识USB设备枚举
- 和菜鸟一起学linux总线驱动之初识USB设备枚举
- 《Linux总线、设备与驱动》USBHID设备驱动
- 《Linux总线、设备与驱动》USBHID设备驱动
- 《Linux总线、设备与驱动》USBHID设备驱动
- 《Linux总线、设备与驱动》USBHID设备驱动
- stm32usb功能设备以及在linux下的USB相关总线、设备驱动笔记
- linux 驱动机制之设备,驱动,总线
- Linux I2C核心、总线与设备驱动
- Linux下的硬件驱动——USB设备(转载)
- Linux设备驱动之usb设备驱动详解
- linux设备驱动之platform总线驱动学习
- LINUX设备驱动之serio总线(一)