2024年物联网嵌入式最新USB HID设备驱动加载过程_hid-generic,附赠复习资料

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
img
img

如果你需要这些资料,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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;

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

转存中…(img-Nu8qyjZQ-1715660481811)]
[外链图片转存中…(img-j8ZqYS1f-1715660481812)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值