Linux hid驱动分析记录
该函数是在传输驱动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函数,这里没有看源码。
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。
继续上面的分析流程,也就是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有一系列函数,也就是可以通过设备文件访问驱动的方法。
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;