2020-10-06

本文详细剖析了Linux系统中USB HID驱动的工作流程,包括HID虚拟总线驱动加载、USB HID驱动加载、USBHID设备探测、HID总线设备探测以及设备的匹配和移除过程。在设备探测阶段,分析了hid_core、usbhid和hiddev如何相互作用,以识别和连接USB HID设备,并注册到系统中。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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);
}
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值