Linux 4.1最新内核usb与hid驱动分析记录

Linux hid驱动分析记录

 
1、hid_add_device实现,在hid/hid-core.c中,匹配特殊驱动相关部分分析

该函数是在传输驱动probe中调用,例如usbhid中,也就是说usb总线匹配到usbhid传输驱动,然后它的probe函数被调用,就会调用hid core的hid_add_device

 

2691         if (hid_ignore_special_drivers) {

2692                 hdev->group = HID_GROUP_GENERIC;

2693         } else if (!hdev->group &&

2694                    !hid_match_id(hdev, hid_have_special_driver)) {

2695                 ret = hid_scan_report(hdev);

2696                 if (ret)

2697                         hid_warn(hdev, "bad device descriptor (%d)\n", ret);

2698         }

首先如果不是忽略特殊驱动的情况,将去匹配特殊驱动,在hid目录下有好多特殊的hid驱动,这里匹配的方法是hid_match_id,需要注意第二个参数 hid_have_special_driver是一个hid_device_id的结构体数组,里面记录了所有特殊hid驱动支持的设备。

 

在特殊驱动中id_table指定了支持的设备

 

驱动的匹配是按照驱动是否支持设备判断,其中规则有下面一些

/* Some useful macros to use to create struct usb_device_id */

#define USB_DEVICE_ID_MATCH_VENDOR 0x0001

#define USB_DEVICE_ID_MATCH_PRODUCT 0x0002

#define USB_DEVICE_ID_MATCH_DEV_LO 0x0004

#define USB_DEVICE_ID_MATCH_DEV_HI 0x0008

#define USB_DEVICE_ID_MATCH_DEV_CLASS 0x0010

#define USB_DEVICE_ID_MATCH_DEV_SUBCLASS 0x0020

#define USB_DEVICE_ID_MATCH_DEV_PROTOCOL 0x0040

#define USB_DEVICE_ID_MATCH_INT_CLASS 0x0080

#define USB_DEVICE_ID_MATCH_INT_SUBCLASS 0x0100

#define USB_DEVICE_ID_MATCH_INT_PROTOCOL 0x0200

#define USB_DEVICE_ID_MATCH_INT_NUMBER 0x0400

 

#define HID_ANY_ID (~0)

#define HID_BUS_ANY 0xffff

#define HID_GROUP_ANY 0x0000

 

如果是hid的usb传输驱动则在他的id_table中 是依照接口类型为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 */

};

 

hid_match_id函数,第一个参数是需要add的设备结构体,而第二个参数就是上面传进来的特殊驱动支持的设备集合

函数中遍历该集合,依次调用hid_match_one_id,目的是查找和需要add的设备匹配的驱动。

 

1594         for (; id->bus; id++)

1595                 if (hid_match_one_id(hdev, id))

1596                         return id;

 

查找到的话返回id,也就是特殊驱动支持的设备,便可知该驱动与设备匹配。

 

下面hid_match_one_id中就是对比匹配的实现,注意:形参id就是特殊驱动支持的设备,其值可能为几个xxx_ANY_xxx,应该是通用的意思。

有id为ANY或与add的设备相等的就返回1.

1582 static bool hid_match_one_id(struct hid_device *hdev,                                                                                                                               

1583                 const struct hid_device_id *id)

1584 {

1585         return (id->bus == HID_BUS_ANY || id->bus == hdev->bus) &&

1586                 (id->group == HID_GROUP_ANY || id->group == hdev->group) &&

1587                 (id->vendor == HID_ANY_ID || id->vendor == hdev->vendor) &&

1588                 (id->product == HID_ANY_ID || id->product == hdev->product);

1589 }

 

 

函数依次返回,这里奇怪的是看hid_add_device中没有处理返回的id,(没接受返回值),仅仅做了是否匹配的判断。继续看。

 

除了上面的工作,还调用了add_device将设备添加到hid bus上,add_device是在driver/base/core.c中统一实现。Hid bus是在hid core初始化时将自己注册为一条总线,这些分析过了。

 

device_add中会调用match 和probe,match调用的是hid总线的match方法;查看资料,hid一般都是调用hid-core的probe函数,这里没有看源码。

 
2、hid-core match 和probe实现, /driver/hid/hid-core.c中

 

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,

};

 

Hid core的match 函数为hid_bus_match

Hid core的probe函数为hid_device_probe

 

add_device中,先调用match然后调用probe

 

Match具体实现hid_bus_match

匹配调用了hid_match_device --------hid_match_one_id

通过查找驱动支持的所有设备来完成匹配,VID  PID

 

probe具体实现hid_device_probe

这里又做了一次匹配

struct hid_driver *hdrv = to_hid_driver(dev->driver);

struct hid_device *hdev = to_hid_device(dev);

 

if (!hdev->driver) { //判断是否有驱动成员存在,也就是是否匹配到

id = hid_match_device(hdev, hdrv); // 去匹配

if (id == NULL) {

ret = -ENODEV;

goto unlock;

}

 

hdev->driver = hrv;  //匹配上的话,结构体成员driver赋值

if (hdrv->probe) {    //驱动有probe则调用

ret = hdrv->probe(hdev, id);

} else { /* default probe */    //默认的操作

ret = hid_open_report(hdev);

if (!ret)

ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);

}

if (ret) {

hid_close_report(hdev);

hdev->driver = NULL;

}

}

默认的probe操作是执行hid_open_report解析report描述符,这里和2.6内核有差异,2.6中直接调用了hid_parse_report,实现一样的。

 

如果解析成功返回0,继续调用hid_hw_start,调用时第二个参数为下面,后面会用到

#define HID_CONNECT_DEFAULT (HID_CONNECT_HIDINPUT|HID_CONNECT_HIDRAW| \

HID_CONNECT_HIDDEV|HID_CONNECT_FF)

 

这个函数调用了传输驱动中实现的一些函数,当时是将传输驱动中的函数指针附加到设备结构体的ll_driver中了,现在调用了start方法,传输驱动一层层向下通信。

int ret = hdev->ll_driver->start(hdev);

如果start成功,调用hid-core中hid_connect。
3、hid_connect

继续上面的分析流程,也就是hid-core的probe函数中解析完report描述符后执行hid_hw_start--->传输驱动中start---->hid-core中hid_connect

 

在hid_connect中,根据hid_hw_start第二个参数传入的mask值也就是进行处理。

//为input

   if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev,

connect_mask & HID_CONNECT_HIDINPUT_FORCE))

hdev->claimed |= HID_CLAIMED_INPUT;

//为hiddev

if ((connect_mask & HID_CONNECT_HIDDEV) && hdev->hiddev_connect &&

!hdev->hiddev_connect(hdev,

connect_mask & HID_CONNECT_HIDDEV_FORCE))

hdev->claimed |= HID_CLAIMED_HIDDEV;

//hidraw

if ((connect_mask & HID_CONNECT_HIDRAW) && !hidraw_connect(hdev))

hdev->claimed |= HID_CLAIMED_HIDRAW;

 

首先看hiddev的情况,在if语句中调用了hdev->hiddev_connect(hdev,

connect_mask & HID_CONNECT_HIDDEV_FORCE)

hdev结构体对象的hiddev_connect对象是函数指针,在usbhid传输驱动的probe函数创建了结构体hid_device,对成员hiddev_connect赋值,值为hiddev中函数hiddev_connect。然后是通过调用hid-core的add_hid_device函数将结构体传递到hid-core中。

 

因此这里调用hdev->hiddev_connect就是调用了hiddev中的hiddev_connect函数。

 

hiddev_connect的实现

struct usbhid_device *usbhid = hid->driver_data;

retval = usb_register_dev(usbhid->intf, &hiddev_class);

将hiddev驱动与设备hid接口注册到usb总线上了。这样usb设备的hid接口就有了对应的驱动hiddev。

 

static struct usb_class_driver hiddev_class = {

.name = "hiddev%d",

.devnode = hiddev_devnode,

.fops = &hiddev_fops,

.minor_base = HIDDEV_MINOR_BASE,

};

看注册时传入的hiddev驱动结构体,名字hiddev%d会根据不同设备附加123等值,接下来在usb总线上的过程会将struct file_operations和设备号注册到系统后,为了能够自动产生驱动对应的设备文件,需要调用class_create和device_create,并通过uevent机制调用udev来调用mknod创建设备文件。

static const struct file_operations hiddev_fops = {

.owner = THIS_MODULE,

.read = hiddev_read,

.write = hiddev_write,

.poll = hiddev_poll,

.open = hiddev_open,

.release = hiddev_release,

.unlocked_ioctl = hiddev_ioctl,

.fasync = hiddev_fasync,

#ifdef CONFIG_COMPAT

.compat_ioctl = hiddev_compat_ioctl,

#endif

.llseek = noop_llseek,

};

可以看到驱动指定的hiddev_fops有一系列函数,也就是可以通过设备文件访问驱动的方法。
4.usb core中对usbhid传输驱动的匹配

 

struct bus_type usb_bus_type = {

.name = "usb",

.match = usb_device_match,

.uevent = usb_uevent,

};

匹配函数是usb_device_match,调用时间是usb设备插入,usb 总线上add_device后调用usb core中match函数来为设备接口匹配驱动。

 

usb_device_match的实现在driver/usb/core/driver.c中

 

} 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_interface

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;

}

这里只看为接口匹配驱动

 

最终调用一些列函数后,实现原理是用了排除法,按照匹配规则排除,有下面几种情况

/* The interface class, subclass, protocol and number should never be

 * checked for a match if the device class is Vendor Specific,

 * unless the match record specifies the Vendor ID. */

if (dev->descriptor.bDeviceClass == USB_CLASS_VENDOR_SPEC &&

!(id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&

(id->match_flags & (USB_DEVICE_ID_MATCH_INT_CLASS |

USB_DEVICE_ID_MATCH_INT_SUBCLASS |

USB_DEVICE_ID_MATCH_INT_PROTOCOL |

USB_DEVICE_ID_MATCH_INT_NUMBER)))

return 0;

/*match_flags是接口class,但是接口class id不同则匹配不上,返回0,认为这个驱动不支持这个设备,进行下一次遍历中的驱动匹配;这里我们的hidusb就是该规则匹配,必须要通过才能匹配到usbhid传输驱动。 */

if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&

    (id->bInterfaceClass != intf->desc.bInterfaceClass))

return 0;

 

if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&

    (id->bInterfaceSubClass != intf->desc.bInterfaceSubClass))

return 0;

 

if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&

    (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))

return 0;

 

if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER) &&

    (id->bInterfaceNumber != intf->desc.bInterfaceNumber))

return 0;

//按照匹配规则排除完了,还能走到这里,就是匹配上了,该驱动支持该设备。

return 1;
---------------------
作者:李高峰
来源:CSDN
原文:https://blog.csdn.net/u012759483/article/details/71627283
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本PDF电子书包含上下两册,共1576页,带目录,高清非扫描版本。 作者: 毛德操 胡希明 丛书名: Linux内核源代码情景分析 出版社:浙江大学出版社 目录 第1章 预备知识 1.1 Linux内核简介. 1.2 Intel X86 CPU系列的寻址方式 1.3 i386的页式内存管理机制 1.4 Linux内核源代码中的C语言代码 1.5 Linux内核源代码中的汇编语言代码 第2章 存储管理 2.1 Linux内存管理的基本框架 2.2 地址映射的全过程 2.3 几个重要的数据结构和函数 2.4 越界访问 2.5 用户堆栈的扩展 2.6 物理页面的使用和周转 2.7 物理页面的分配 2.8 页面的定期换出 2.9 页面的换入 2.10 内核缓冲区的管理 2.11 外部设备存储空间的地址映射 2.12 系统调用brk() 2.13 系统调用mmap() 第3章 中断、异常和系统调用 3.1 X86 CPU对中断的硬件支持 3.2 中断向量表IDT的初始化 3.3 中断请求队列的初始化 3.4 中断的响应和服务 3.5 软中断与Bottom Half 3.6 页面异常的进入和返回 3.7 时钟中断 3.8 系统调用 3.9 系统调用号与跳转表 第4章 进程与进程调度 4.1 进程四要素 4.2 进程三部曲:创建、执行与消亡 4.3 系统调用fork()、vfork()与clone() 4.4 系统调用execve() 4.5 系统调用exit()与wait4() 4.6 进程的调度与切换 4.7 强制性调度 4.8 系统调用nanosleep()和pause() 4.9 内核中的互斥操作 第5章 文件系统 5.1 概述 5.2 从路径名到目标节点 5.3 访问权限与文件安全性 5.4 文件系统的安装和拆卸 5.5 文件的打开与关闭 5.6 文件的写与读 5.7 其他文件操作 5.8 特殊文件系统/proc 第6章 传统的Unix进程间通信 6.1 概述 6.2 管道和系统调用pipe() 6.3 命名管道 6.4 信号 6.5 系统调用ptrace()和进程跟踪 6.6 报文传递 6.7 共享内存 6.8 信号量 第7章基于socket的进程间通信 7.1系统调用socket() 7.2函数sys—socket()——创建插口 7.3函数sys—bind()——指定插口地址 7.4函数sys—listen()——设定server插口 7.5函数sys—accept()——接受连接请求 7.6函数sys—connect()——请求连接 7.7报文的接收与发送 7.8插口的关闭 7.9其他 第8章设备驱动 8.1概述 8.2系统调用mknod() 8.3可安装模块 8.4PCI总线 8.5块设备的驱动 8.6字符设备驱动概述 8.7终端设备与汉字信息处理 8.8控制台的驱动 8.9通用串行外部总线USB 8.10系统调用select()以及异步输入/输出 8.11设备文件系统devfs 第9章多处理器SMP系统结构 9.1概述 9.2SMP结构中的互斥问题 9.3高速缓存与内存的一致性 9.4SMP结构中的中断机制 9.5SMP结构中的进程调度 9.6SMP系统的引导 第10章系统引导和初始化 10.1系统引导过程概述 10.2系统初始化(第一阶段) 10.3系统初始化(第二阶段) 10.4系统初始化(第三阶段) 10.5系统的关闭和重引导
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值