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

Linux hid驱动分析记录

 

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

该函数是在传输驱动probe中调用,例如usbhid中,也就是说usb总线匹配到usbhid传输驱动,然后它的probe函数被调用,就会调用hid corehid_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

 

如果是hidusb传输驱动则在他的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,应该是通用的意思。

idANY或与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 probematch调用的是hid总线的match方法;查看资料,hid一般都是调用hid-coreprobe函数,这里没有看源码。

 

2hid-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 corematch 函数为hid_bus_match

Hid coreprobe函数为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-corehid_connect

3hid_connect

继续上面的分析流程,也就是hid-coreprobe函数中解析完report描述符后执行hid_hw_start--->传输驱动中start---->hid-corehid_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-coreadd_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_createdevice_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 corematch函数来为设备接口匹配驱动。

 

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;

 

 

 

 

 

 



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值