Linux usb hid驱动分析
HID是Human Interface Devices的缩写.翻译成中文即为人机交互设备.这里的人机交互设备是一个宏观上面的概念,任何设备,只要符合HID spec,都可以称之为HID设备。
下面分析USB HID驱动的实现:
1.HID虚拟总线驱动加载
在drivers/hid/hid-core.c文件中,调用hid_init()函数,实现HID虚拟总线驱动的加载。
static int __init hid_init(void)
{
//注册HID虚拟总线
ret = bus_register(&hid_bus_type);
if (ret) {
pr_err("can't register hid bus\n");
goto err;
}
ret = hidraw_init();
if (ret)
goto err_bus;
hid_debug_init();
return 0;
}
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,
};
2.USBHID驱动加载
在drivers/hid/usbhid/hid-core.c文件中,调用hid_init()函数实现USBHID驱动的加载,USBHID驱动同时包含USB总线设备驱动和HID总线设备驱动。
static int __init hid_init(void)
{
int retval = -ENOMEM;
retval = usbhid_quirks_init(quirks_param);
if (retval)
goto usbhid_quirks_init_fail;
//注册USB总线设备驱动
retval = usb_register(&hid_driver);
if (retval)
goto usb_register_fail;
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
return 0;
}
//USB设备驱动方法
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,
};
hid_init首先调用usbhid_quirks_init(),其实就是查找insmod 时给的pid,vid参数在quirks列表中是否有,如果有,就替换。没有就创建。
3.USBHID设备探测
在hid/hid-core.c文件中,调用usbhid_probe()函数进行USB总线的设备探测并注册HID设备。
static int usbhid_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_host_interface *interface = intf->cur_altsetting;
struct usb_device *dev = interface_to_usbdev(intf);
struct usbhid_device *usbhid;
struct hid_device *hid;
unsigned int n, has_in = 0;
size_t len;
int ret;
dbg_hid("HID probe called for ifnum %d\n",
intf->altsetting->desc.bInterfaceNumber);
for (n = 0; n < interface->desc.bNumEndpoints; n++)
if (usb_endpoint_is_int_in(&interface->endpoint[n].desc))
has_in++;
if (!has_in) {
hid_err(intf, "couldn't find an input interrupt endpoint\n");
return -ENODEV;
}
//申请HID设备
hid = hid_allocate_device();
if (IS_ERR(hid))
return PTR_ERR(hid);
usb_set_intfdata(intf, hid);
hid->ll_driver = &usb_hid_driver;
hid->ff_init = hid_pidff_init;
#ifdef CONFIG_USB_HIDDEV
hid->hiddev_connect = hiddev_connect;
hid->hiddev_disconnect = hiddev_disconnect;
hid->hiddev_hid_event = hiddev_hid_event;
hid->hiddev_report_event = hiddev_report_event;
#endif
hid->dev.parent = &intf->dev;
hid->bus = BUS_USB;
hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
hid->product = le16_to_cpu(dev->descriptor.idProduct);
hid->name[0] = 0;
hid->quirks = usbhid_lookup_quirk(hid->vendor, hid->product);
if (intf->cur_altsetting->desc.bInterfaceProtocol ==
USB_INTERFACE_PROTOCOL_MOUSE)
hid->type = HID_TYPE_USBMOUSE;
else if (intf->cur_altsetting->desc.bInterfaceProtocol == 0)
hid->type = HID_TYPE_USBNONE;
if (dev->manufacturer)
strlcpy(hid->name, dev->manufacturer, sizeof(hid->name));
if (dev->product) {
if (dev->manufacturer)
strlcat(hid->name, " ", sizeof(hid->name));
strlcat(hid->name, dev->product, sizeof(hid->name));
}
if (!strlen(hid->name))
snprintf(hid->name, sizeof(hid->name), "HID %04x:%04x",
le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
usb_make_path(dev, hid->phys, sizeof(hid->phys));
strlcat(hid->phys, "/input", sizeof(hid->phys));
len = strlen(hid->phys);
if (len < sizeof(hid->phys) - 1)
snprintf(hid->phys + len, sizeof(hid->phys) - len,
"%d",intf->altsetting[0].desc.bInterfaceNumber);
if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
hid->uniq[0] = 0;
usbhid = kzalloc(sizeof(*usbhid), GFP_KERNEL);
if (usbhid == NULL) {
ret = -ENOMEM;
goto err;
}
hid->driver_data = usbhid;
usbhid->hid = hid;
usbhid->intf = intf;
usbhid->ifnum = interface->desc.bInterfaceNumber;
init_waitqueue_head(&usbhid->wait);
INIT_WORK(&usbhid->reset_work, hid_reset);
setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
spin_lock_init(&usbhid->lock);
//注册HID设备
ret = hid_add_device(hid);
if (ret) {
if (ret != -ENODEV)
hid_err(intf, "can't add hid device: %d\n", ret);
goto err_free;
}
return 0;
err_free:
kfree(usbhid);
err:
hid_destroy_device(hid);
return ret;
}
int hid_add_device(struct hid_device *hdev)
{
static atomic_t id = ATOMIC_INIT(0);
int ret;
/* we need to kill them here, otherwise they will stay allocated
wait for coming driver */
if (hid_ignore(hdev))
return -ENODEV;
/*
* Check for the mandatory transport channel.
*/
if (!hdev->ll_driver->raw_request) {
hid_err(hdev, "transport driver missing .raw_request()\n");
return -EINVAL;
}
/*
* 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) {
hdev->group = HID_GROUP_GENERIC;
} else if (!hdev->group &&
!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));
ret = device_add(&hdev->dev);
if (!ret)
hdev->status |= HID_STAT_ADDED;
else
hid_debug_unregister(hdev);
return ret;
}
static struct hid_ll_driver usb_hid_driver = {
.parse = usbhid_parse,
.start = usbhid_start,
.stop = usbhid_stop,
.open = usbhid_open,
.close = usbhid_close,
.power = usbhid_power,
.request = usbhid_request,
.wait = usbhid_wait_io,
.raw_request = usbhid_raw_request,
.output_report = usbhid_output_report,
.idle = usbhid_idle,
};
usb_hid_configure(intf); 首先查看quirks.使用usbhid_lookup_quirk()从静态和动态quirks list中查是否此device包含在其中。
如果不是忽略特殊驱动的情况,将去匹配特殊驱动,在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的设备匹配的驱动。查找到的话返回id,也就是特殊驱动支持的设备,便可知该驱动与设备匹配。
const struct hid_device_id *hid_match_id(struct hid_device *hdev,
const struct hid_device_id *id)
{
for (; id->bus; id++)
if (hid_match_one_id(hdev, id))
return id;
return NULL;
}
hid_match_one_id()函数中就是对比匹配的实现,注意:形参id就是特殊驱动支持的设备,其值可能为几个xxx_ANY_xxx,是通用的意思。有id为ANY或与add的设备相等的就返回1.
static bool hid_match_one_id(struct hid_device *hdev,
const struct hid_device_id *id)
{
return (id->bus == HID_BUS_ANY || id->bus == hdev->bus) &&
(id->group == HID_GROUP_ANY || id->group == hdev->group) &&
(id->vendor == HID_ANY_ID || id->vendor == hdev->vendor) &&
(id->product == HID_ANY_ID || id->product == hdev->product);
}
除了上面的工作,还调用了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函数。
4.HID总线的设备探测
在/driver/hid/hid-core.c文件中,探测HID总线的设备。
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匹配。
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;
//判断是否有驱动成员存在,也就是是否匹配到
if (!hdev->driver) {
id = hid_match_device(hdev, hdrv);
if (id == NULL) {
ret = -ENODEV;
goto unlock;
}
//匹配上的话,结构体成员driver赋值
hdev->driver = hdrv;
//驱动有probe则调用,否则调用默认值
if (hdrv->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描述符。
如果解析成功返回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值也就是进行处理。
int hid_connect(struct hid_device *hdev, unsigned int connect_mask)
{
static const char *types[] = { "Device", "Pointer", "Mouse", "Device","Joystick", "Gamepad", "Keyboard", "Keypad",
"Multi-Axis Controller"
};
const char *type, *bus;
char buf[64] = "";
unsigned int i;
int len;
int ret;
if (hdev->quirks & HID_QUIRK_HIDDEV_FORCE)
connect_mask |= (HID_CONNECT_HIDDEV_FORCE | HID_CONNECT_HIDDEV);
if (hdev->quirks & HID_QUIRK_HIDINPUT_FORCE)
connect_mask |= HID_CONNECT_HIDINPUT_FORCE;
if (hdev->bus != BUS_USB)
connect_mask &= ~HID_CONNECT_HIDDEV;
if (hid_hiddev(hdev))
connect_mask |= HID_CONNECT_HIDDEV_FORCE;
if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev,
connect_mask & HID_CONNECT_HIDINPUT_FORCE))
hdev->claimed |= HID_CLAIMED_INPUT;
if ((connect_mask & HID_CONNECT_HIDDEV) && hdev->hiddev_connect &&
!hdev->hiddev_connect(hdev,
connect_mask &HID_CONNECT_HIDDEV_FORCE))
hdev->claimed |= HID_CLAIMED_HIDDEV;
if ((connect_mask & HID_CONNECT_HIDRAW) && !hidraw_connect(hdev))
hdev->claimed |= HID_CLAIMED_HIDRAW;
if (connect_mask & HID_CONNECT_DRIVER)
hdev->claimed |= HID_CLAIMED_DRIVER;
/* Drivers with the ->raw_event callback set are not required to
* to any other listener. */
if (!hdev->claimed && !hdev->driver->raw_event) {
hid_err(hdev, "device has no listeners, quitting\n");
return -ENODEV;
}
if ((hdev->claimed & HID_CLAIMED_INPUT) &&
(connect_mask & HID_CONNECT_FF) && hdev->ff_init)
hdev->ff_init(hdev);
len = 0;
if (hdev->claimed & HID_CLAIMED_INPUT)
len += sprintf(buf + len, "input");
if (hdev->claimed & HID_CLAIMED_HIDDEV)
len += sprintf(buf + len, "%shiddev%d", len "," : "",
hdev->minor);
if (hdev->claimed & HID_CLAIMED_HIDRAW)
len += sprintf(buf + len, "%shidraw%d", len "," : "",
((struct hidraw *)hdev->hidraw)->minor);
type = "Device";
for (i = 0; i < hdev->maxcollection; i++) {
struct hid_collection *col = &hdev->collection[i];
if (col->type == HID_COLLECTION_APPLICATION &&
(col->usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
(col->usage & 0xffff) < ARRAY_SIZE(types)) {
type = types[col->usage & 0xffff];
break;
}
}
switch (hdev->bus) {
case BUS_USB:
bus = "USB";
break;
case BUS_BLUETOOTH:
bus = "BLUETOOTH";
break;
case BUS_I2C:
bus = "I2C";
break;
default:
bus = "<UNKNOWN>";
}
ret = device_create_file(&hdev->dev, &dev_attr_country);
if (ret)
hid_warn(hdev,
"can't create sysfs country code attribute err: %d\n", ret);
ret = device_create_bin_file(&hdev->dev, &dev_bin_attr_report_desc);
if (ret)
hid_warn(hdev,
"can't create sysfs report descriptor attribute err: %d\n", ret);
hid_info(hdev, "%s: %s HID v%x.%02x %s [%s] on %s\n",
buf, bus, hdev->version >> 8, hdev->version & 0xff,
type, hdev->name, hdev->phys);
return 0;
}
首先看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()函数。
int hiddev_connect(struct hid_device *hid, unsigned int force)
{
struct hiddev *hiddev;
struct usbhid_device *usbhid = hid->driver_data;
int retval;
if (!force) {
unsigned int i;
for (i = 0; i < hid->maxcollection; i++)
if (hid->collection[i].type ==
HID_COLLECTION_APPLICATION &&
!IS_INPUT_APPLICATION(hid->collection[i].usage))
break;
if (i == hid->maxcollection)
return -1;
}
if (!(hiddev = kzalloc(sizeof(struct hiddev), GFP_KERNEL)))
return -1;
init_waitqueue_head(&hiddev->wait);
INIT_LIST_HEAD(&hiddev->list);
spin_lock_init(&hiddev->list_lock);
mutex_init(&hiddev->existancelock);
hid->hiddev = hiddev;
hiddev->hid = hid;
hiddev->exist = 1;
retval = usb_register_dev(usbhid->intf, &hiddev_class);
return 0;
}
将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和设备号注册到系统后,为了能够自动产生驱动对应的设备文件,需要调用device_create()函数。
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有一系列函数,也就是可以通过设备文件访问驱动的方法。
5. 匹配usbhid传输驱动
在driver/usb/core/driver.c文件中,匹配函数是usb_device_match,调用时间是usb设备插入,usb 总线上add_device后调用usb core中match函数来为设备接口匹配驱动。
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
};
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
/* devices and interfaces are handled separately */
if (is_usb_device(dev)) {
/* 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)) {
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;
//设备接口usb_interface
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;
}
这里只看为接口匹配驱动。
最终调用一些列函数后,实现原理是用了排除法,按照匹配规则排除,有下面几种情况:
int usb_match_one_id_intf(struct usb_device *dev,
struct usb_host_interface *intf,
const struct usb_device_id *id)
{
/* 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;
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;
}
match_flags是接口class,但是接口class id不同则匹配不上,返回0,认为这个驱动不支持这个设备,进行下一次遍历中的驱动匹配;这里我们的hidusb就是该规则匹配,必须要通过才能匹配到usbhid传输驱动。按照匹配规则排除完了,走到最后,就是匹配上了,该驱动支持该设备。
6、移除hiddev设备
在drivers/hid/usbhid/hid-core.c文件中,调用usbhid_disconnect()函数实现USB总线设备移除USBHID设备时,触发USBHID中USB驱动部分调用。
retval = usb_register(&hid_driver);
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 void usbhid_disconnect(struct usb_interface *intf)
{
usbhid = hid->driver_data;
spin_lock_irq(&usbhid->lock);
set_bit(HID_DISCONNECTED, &usbhid->iofl);
spin_unlock_irq(&usbhid->lock);
hid_destroy_device(hid); //调用HID总线的设备注销函数
kfree(usbhid);
}
在drivers/hid/hid-core.c文件中,调用hid_distory_device()函数来移除hid设备。
void hid_destroy_device(struct hid_device *hdev)
{
hid_remove_device(hdev);
put_device(&hdev->dev);
}
static void hid_remove_device(struct hid_device *hdev)
{
if (hdev->status & HID_STAT_ADDED) {
device_del(&hdev->dev);
hid_debug_unregister(hdev);
hdev->status &= ~HID_STAT_ADDED;
}
kfree(hdev->dev_rdesc);
hdev->dev_rdesc = NULL;
hdev->dev_rsize = 0;
}
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_remove(struct device *dev)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct hid_driver *hdrv;
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;
hdrv = hdev->driver;
if (hdrv) {
if (hdrv->remove)
hdrv->remove(hdev);
else /* default remove */
hid_hw_stop(hdev);
hid_close_report(hdev);
hdev->driver = NULL;
}
if (!hdev->io_started)
up(&hdev->driver_input_lock);
unlock_driver_lock:
up(&hdev->driver_lock);
return ret;
}
void hid_disconnect(struct hid_device *hdev)
{
device_remove_file(&hdev->dev, &dev_attr_country);
device_remove_bin_file(&hdev->dev, &dev_bin_attr_report_desc);
if (hdev->claimed & HID_CLAIMED_INPUT) //HIDINPUT设备
hidinput_disconnect(hdev);
if (hdev->claimed & HID_CLAIMED_HIDDEV) // HIDDEV设备
hdev->hiddev_disconnect(hdev);
if (hdev->claimed & HID_CLAIMED_HIDRAW) // HIDRAW设备
hidraw_disconnect(hdev);
}