USB HID设备驱动加载过程

  本文以linux-3.12内核来分析usb键盘插入系统后系统如何识别usb键盘的过程。内核选项配置CONFIG_HID,CONFIG_USB_HID,CONFIG_HID_GENERIC,但是没有配置CONFIG_USB_KBD,CONFIG_USB_MOUSE选项。

  1)在内核启动时,注册了hid总线驱动,在drivers/hid/hid-core.c中

   hid_init---->bus_register(&hid_bus_type)

注册了一个名为hid_bus_type的hid总线。

  2)在hid-generic.c中定义了module_hid_driver(hid_generic),这个宏实际上是调用__hid_register_driver(drivers/hid/)接口注册一个hid_driver,并把它挂接在hid_bus_type总线驱动链表上。

static struct hid_driver hid_generic = {
.name = "hid-generic",
.id_table = hid_table,
};

driver_register(struct device_driver *drv)-->bus_add_driver-->klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);

这里看到struct device_driver是内核各种xxx_driver”父类“,注册按“父类”对象注册,典型的面向对象思想,然后每个子系统比如usb,mmc子系统核心层都会从这个device_driver派生”子类“。device_register和driver_register负责初始化并把父类struct device和struct device_driver关联起来。

我们再看一下mmc子系统:

struct mmc_driver {
struct device_driver drv;
int (*probe)(struct mmc_card *);
void (*remove)(struct mmc_card *);
int (*suspend)(struct mmc_card *);
int (*resume)(struct mmc_card *);
void (*shutdown)(struct mmc_card *);
};

/**
* mmc_register_driver - register a media driver
* @drv: MMC media driver
*/
int mmc_register_driver(struct mmc_driver *drv)
{
drv->drv.bus = &mmc_bus_type;

return driver_register(&drv->drv);
}

static struct bus_type mmc_bus_type = {
.name = "mmc",
.dev_attrs = mmc_dev_attrs,
.match = mmc_bus_match,
.uevent = mmc_bus_uevent,
.probe = mmc_bus_probe,
.remove = mmc_bus_remove,
.shutdown = mmc_bus_shutdown,
.pm = &mmc_bus_pm_ops,
};

int mmc_register_bus(void)
{
return bus_register(&mmc_bus_type);
}

/*
* Allocate and initialise a new MMC card structure.
*/
struct mmc_card *mmc_alloc_card(struct mmc_host *host, struct device_type *type)
{
struct mmc_card *card;

card = kzalloc(sizeof(struct mmc_card), GFP_KERNEL);
if (!card)
return ERR_PTR(-ENOMEM);

card->host = host;
/* 初始化struct device父对象部分 */
device_initialize(&card->dev);

card->dev.parent = mmc_classdev(host);
card->dev.bus = &mmc_bus_type;
card->dev.release = mmc_release_card;
card->dev.type = type;

return card;
}

在mmc_add_card函数中会调用device_add函数向总线设备列表添加设备,device_add-->bus_add_device-->klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices)。

3)在drivers/usb/core/usb.c中调用usb_register_device_driver(&usb_generic_driver, THIS_MODULE)这个接口是注册usb_device_driver的,向usb核心注册这个usb_device_driver驱动。整个usb子系统只定义了一个struct usb_device_driver驱动,它定义在drivers/usb/core/generic.c文件中。

struct usb_device_driver usb_generic_driver = {
.name = "usb",
.probe = generic_probe,
.disconnect = generic_disconnect,
#ifdef CONFIG_PM
.suspend = generic_suspend,
.resume = generic_resume,
#endif
.supports_autosuspend = 1,
};
这个驱动主要作用是什么?

它主要是用来枚举所有的struct usb_device使用的,也就是枚举专用的。下面从枚举开始讲讲,如何调用这个probe函数。

当有usb键盘插入,hub(hub.c中)会检测到某个端口有变化,如果是有设备连上某个端口,则调用usb_alloc_dev创建一个usb_device设备,然后调用usb_new_device-->usb_enumerate_device-->usb_get_configuration-->usb_parse_configuration-->usb_parse_interface(会分配struct usb_interface_cache)-->usb_parse_endpoint分析设备各描述符,最后usb_new_device会调用device_add 这里把这个usb_device注册到usb总线下。

当注册这个usb_device的时候usb总线会调用usb_generic_driver驱动的probe函数,这个函数主要为这个usb_device选择一个合适的
配置configuration,并且设置这个configuration,即usb_choose_configuration和usb_set_configuration(位于drivers/usb/core/message.c)。
usb_set_configuration这个函数为这个配置的每个接口初始化为usb_if_device_type类型的设备。可以看到
usb_interface设备是挂载usb_device下面的,并且这个usb_device可能有多个usb_interface功能设备。

                                                                    usb_device
                                                                     /  /  |  \  \
                                                                   if  if  if  if  if    (if = usb_interface)

最后在注册这些interface时,注册函数会调用usb总线的match函数匹配usb_interface和usb_driver,但是usb_bus_type总线只有match函数,没有probe函数
需要调用usb_driver的probe函数usb_probe_interface(drivers/usb/core/driver.c),这个函数会真正的调用具体usb_driver的probe函数会被调用。

值得一提的而是root hub或者hub也都是usb_device设备,usb_device下面包含usb_interface功能(function)。

另外usb_bus_type总线的match函数也对两类设备区别对待,usb_device<===>usb_device_driver配对,而usb_interface<====>usb_driver配对。

static int usb_device_match(struct device *dev, struct device_driver *drv)
{
/* devices and interfaces are handled separately */
if (is_usb_device(dev)) {/* hub创建的usb_device和 usb_generic_driver 匹配走这个分支*/


/* 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)) {/* usb_interface 和 usb_driver匹配走这个分支  */
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);
if (id)
return 1;


id = usb_match_dynamic_id(intf, usb_drv);
if (id)
return 1;
}


return 0;
}

在drivers/hid/usbhid/hid-core.c中定义了hid_driver,它是个usb_driver,并且调用usb_register_driver接口注册到了usb总线(usb_bus_type)上,假定hid_driver不是以模块的方式加载,那应该在内核启动时就已经加载完毕。对应的hid_driver驱动就是上面的hid_driver,然后调用hid bus的match函数。

/* 这里match的ID表主要查看设备是否为HID类 */

static const struct usb_device_id hid_usb_ids[] = {
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
.bInterfaceClass = USB_INTERFACE_CLASS_HID },
{ } /* Terminating entry */
};
static struct usb_driver hid_driver = {

.name = "usbhid",
.probe = usbhid_probe,
.disconnect = usbhid_disconnect,
#ifdef CONFIG_PM
.suspend = hid_suspend,
.resume = hid_resume,
.reset_resume =hid_reset_resume,
#endif
.pre_reset = hid_pre_reset,
.post_reset = hid_post_reset,
.id_table = hid_usb_ids,
.supports_autosuspend = 1,
};

下面需要调用函数,分析它主要作用:

static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
......
/* 分配一个struct hid_device设备 */
hid = hid_allocate_device();
if (IS_ERR(hid))
return PTR_ERR(hid);


/* 把这个接口和hid设备关联 */
usb_set_intfdata(intf, hid);


/* 安装hid底层驱动,其实是个回调usb hid的回调驱动函数集,具体硬件操作依靠它来实现,hid core层(而不是usb hid core层)回调它。

         */
hid->ll_driver = &usb_hid_driver;

......

/* 分配一个usbhid设备,同时也是一个hid_device,继承hid_device */
usbhid = kzalloc(sizeof(*usbhid), GFP_KERNEL);

/* 向hid核心层添加一个hid_device设备,同时也会获取HID report报告描述符 */
ret = hid_add_device(hid);
if (ret) {
if (ret != -ENODEV)
hid_err(intf, "can't add hid device: %d\n", ret);
goto err_free;
}
.......
}

usbhid_probe函数会调用hid_add_device函数注册一个hid_device设备。

int hid_add_device(struct hid_device *hdev)
{
static atomic_t id = ATOMIC_INIT(0);
int ret;


if (WARN_ON(hdev->status & HID_STAT_ADDED))
return -EBUSY;


/* we need to kill them here, otherwise they will stay allocated to
* wait for coming driver */
if (hid_ignore(hdev))
return -ENODEV;


/*
* Read the device report descriptor once and use as template
* for the driver-specific modifications.
*/
ret = hdev->ll_driver->parse(hdev);
if (ret)
return ret;
if (!hdev->dev_rdesc)
return -ENODEV;


/*
* Scan generic devices for group information
*/
if (hid_ignore_special_drivers ||
   !hid_match_id(hdev, hid_have_special_driver)) {
ret = hid_scan_report(hdev);
if (ret)
hid_warn(hdev, "bad device descriptor (%d)\n", ret);
}


/* XXX hack, any other cleaner solution after the driver core
* is converted to allow more than 20 bytes as the device name? */
dev_set_name(&hdev->dev, "%04X:%04X:%04X.%04X", hdev->bus,
    hdev->vendor, hdev->product, atomic_inc_return(&id));


hid_debug_register(hdev, dev_name(&hdev->dev));
/* 这里会调用hid_bus的hid_device_probe函数*/
ret = device_add(&hdev->dev);
if (!ret)
hdev->status |= HID_STAT_ADDED;
else
hid_debug_unregister(hdev);


return ret;
}

由于上面第2步分析中已经注册了hid_driver驱动了,这个时候会发生hid_device和hid_driver匹配,由于hid_device和hid_driver都没有probe函数,所以会调用总线hid_bus_type 的probe函数 hid_device_probe。

static struct bus_type hid_bus_type = {
.name = "hid",
.dev_groups = hid_dev_groups,
.match = hid_bus_match,
.probe = hid_device_probe,
.remove = hid_device_remove,
.uevent = hid_uevent,
};

static int hid_device_probe(struct device *dev)
{
struct hid_driver *hdrv = container_of(dev->driver,
struct hid_driver, driver);
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
const struct hid_device_id *id;
int ret = 0;


if (down_interruptible(&hdev->driver_lock))
return -EINTR;
if (down_interruptible(&hdev->driver_input_lock)) {
ret = -EINTR;
goto unlock_driver_lock;
}
hdev->io_started = false;


/* 第1次添加hid_device时,一定是为空 */
if (!hdev->driver) {
/* 在注册hid_device时就会调用hid_bus_type总线的match函数,这里再调用一次 */
id = hid_match_device(hdev, hdrv);
if (id == NULL) {
ret = -ENODEV;
goto unlock;
}


hdev->driver = hdrv;
/* hid_generic驱动没有probe函数 */
if (hdrv->probe) {
ret = hdrv->probe(hdev, id);
} else { /* default probe */
/* 会调用这个函数,这里会对hid_add_device函数中获取到的HID report描述符进行解析

                        */
ret = hid_open_report(hdev);
if (!ret)

/* 调用hid_ll_driver相关接口  */

ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
}
if (ret) {
hid_close_report(hdev);
hdev->driver = NULL;
}
}
unlock:
if (!hdev->io_started)
up(&hdev->driver_input_lock);
unlock_driver_lock:
up(&hdev->driver_lock);
return ret;
}

总结:层次关系

        设备                                                                                      驱动

struct device                    <==>                                          strcut device_driver

      \|/  派生                                                                               \|/ 派生

struct usb_device           <==>                                           struct usb_device_driver

      \|/ 包含                                                                                 \|/ 派生

struct usb_interface(派生自device)            <==>             struct usb_driver

-------------------------------------------------------------------------------------------------------------

                                                  struct usbhid_device(是组合设备)

                                                         /                     \           (/ \表示组合)

struct hid_device(派生自device)                        struct usb_interface(派生自device)  

----------------------------------------------------------------------------------------------------------------

        设备                                                                                     驱动

struct hid_device                         <==>                                            struct hid_driver

----------------------------------------------------------------------------------------------------------------   


  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值