USB驱动之Android usb鼠标驱动

1. 前言

        HID是Human Interface Devices的缩写,翻译成中文即为人机交互设备。这里的人机交互设备是一个宏观上面的概念,任何设备只要符合HID spec都可以称之为HID设备,常见的HID设备有鼠标键盘,游戏操纵杆等等。

        usb鼠标在android代码中没有使用linux中常用的drivers/hid/usbhid/usbmouse.c驱动,而是使用了hid-generic驱动【注:从内核配置可知,内核选项配置了CONFIG_HID,CONFIG_USB_HID,CONFIG_HID_GENERIC,但是没有配置CONFIG_USB_KBD,CONFIG_USB_MOUSE选项】。

        注意有两个hid-core.c文件,分别为hid/hid-core.c和hid/usbhid/hid-core.c文件。前者注册hid总线,后者注册hid device。

2. hid bus

        在内核启动时,注册了hid总线驱动,在drivers/hid/hid-core.c中注册了一个名为hid_bus_type的hid总线。

static int __init hid_init(void)
{
	int ret;
	ret = bus_register(&hid_bus_type);//---->
	ret = hidraw_init();
	hid_debug_init();
	return 0;
}

static struct bus_type hid_bus_type = {
	.name		= "hid",
	.dev_groups	= hid_dev_groups,
	.drv_groups	= hid_drv_groups,
	.match		= hid_bus_match,
	.probe		= hid_device_probe,
	.remove		= hid_device_remove,
	.uevent		= hid_uevent,
};

3. hid driver

        在hid-generic.c中定义了module_hid_driver(hid_generic),这个宏实际上是调用__hid_register_driver(drivers/hid/)接口注册一个hid_driver,并把它挂接在hid_bus_type总线驱动链表上。

static const struct hid_device_id hid_table[] = {
	{ HID_DEVICE(HID_BUS_ANY, HID_GROUP_GENERIC, HID_ANY_ID, HID_ANY_ID) },
	{ }
};
static struct hid_driver hid_generic = {
	.name = "hid-generic",
	.id_table = hid_table,
};
module_hid_driver(hid_generic);
//usb鼠标将和hid_generic匹配

4. hid device

        在drivers/hid/usbhid/hid-core.c中定义了hid_driver,它是个usb_driver,并且调用usb_register_driver接口注册到了usb总线(usb_bus_type)上。

static int __init hid_init(void)
{
	int retval = -ENOMEM;
	retval = usbhid_quirks_init(quirks_param);
	retval = usb_register(&hid_driver);	//---->
	return 0;
}

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 const struct usb_device_id hid_usb_ids[] = {
	{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
		.bInterfaceClass = USB_INTERFACE_CLASS_HID },//所有ClassID为USB_INTERFACE_CLASS_HID的设备都会被这个驱动所匹配.所以,所有USB HID设备都会由这个module来驱动。
	{ }						/* Terminating entry */
};

        当hid设备插入usb口以后会调用usb_driver的probe函数,这一点由《USB驱动》可知。

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

    //分配一个struct hid_device设备
	hid = hid_allocate_device();

    //把这个接口和hid设备关联
	usb_set_intfdata(intf, hid);
    //安装hid底层驱动,其实是个回调usb hid的回调驱动函数集,具体硬件操作依靠它来实现,hid core层(而不是usb hid core层)回调它。 
	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设备,同时也是一个hid_device,继承hid_device
	usbhid = kzalloc(sizeof(*usbhid), GFP_KERNEL);

	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核心层添加一个hid_device设备,同时也会获取HID report报告描述符 
	ret = hid_add_device(hid);
	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;

	if (WARN_ON(hdev->status & HID_STAT_ADDED))
		return -EBUSY;

	/* we need to kill them here, otherwise they will stay allocated to
	 * 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));
   //这里会调用hid_bus的hid_device_probe函数
	ret = device_add(&hdev->dev);
	if (!ret)
		hdev->status |= HID_STAT_ADDED;
	else
		hid_debug_unregister(hdev);

	return ret;
}

static int hid_device_probe(struct device *dev)
{
	struct hid_driver *hdrv = to_hid_driver(dev->driver);
	struct hid_device *hdev = to_hid_device(dev);
	const struct hid_device_id *id;
	int ret = 0;

	if (down_interruptible(&hdev->driver_input_lock)) {
		ret = -EINTR;
		goto end;
	}
	hdev->io_started = false;

   //第1次添加hid_device时,一定是为空 
	if (!hdev->driver) {
           //在注册hid_device时就会调用hid_bus_type总线的match函数,这里再调用一次
		id = hid_match_device(hdev, hdrv);
		if (id == NULL) {
			ret = -ENODEV;
			goto unlock;
		}

		hdev->driver = hdrv;
          //hid_generic驱动没有probe函数
		if (hdrv->probe) {
			ret = hdrv->probe(hdev, id);
		} else { /* default probe 会调用这个函数,这里会对hid_add_device函数中获取到的HID report描述符进行解析*/
			ret = hid_open_report(hdev);
			if (!ret)
                           //调用hid_ll_driver相关接口
				ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);	//---->
		}
		if (ret) {
			hid_close_report(hdev);
			hdev->driver = NULL;
		}
	}
unlock:
	if (!hdev->io_started)
		up(&hdev->driver_input_lock);
end:

	return ret;
}
int hid_hw_start(struct hid_device *hdev, unsigned int connect_mask)
{
	int error;

	error = hdev->ll_driver->start(hdev); //启动设备,
	if (error)
		return error;

	if (connect_mask) {
		error = hid_connect(hdev, connect_mask); //将设备与HID框架关联起来---->
		if (error) {
			hdev->ll_driver->stop(hdev);
			return error;
		}
	}

	return 0;
}

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) //如果不是USB总线,那么去掉HID_CONNECT_HIDDEV标记
		connect_mask &= ~HID_CONNECT_HIDDEV;
	if (hid_hiddev(hdev)) //匹配某些特定vendorID和productID
		connect_mask |= HID_CONNECT_HIDDEV_FORCE;

	if ((connect_mask & HID_CONNECT_HIDINPUT) && !hidinput_connect(hdev, //input设备--->
				connect_mask & HID_CONNECT_HIDINPUT_FORCE))
		hdev->claimed |= HID_CLAIMED_INPUT;

	if ((connect_mask & HID_CONNECT_HIDDEV) && hdev->hiddev_connect && 
			!hdev->hiddev_connect(hdev, //hiddev设备
				connect_mask & HID_CONNECT_HIDDEV_FORCE))
		hdev->claimed |= HID_CLAIMED_HIDDEV;
	if ((connect_mask & HID_CONNECT_HIDRAW) && !hidraw_connect(hdev)) //hidraw设备
		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 connect
	 * 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 ? "," : "",
				((struct hiddev *)hdev->hiddev)->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);

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

        HID中最常用的是input设备,使用hidinput_connect登记到系统。hidinput_connect的主要作用是对hiddev中的每一个report,都建立一个input_dev设备,并登记到input框架中。

int hidinput_connect(struct hid_device *hid, unsigned int force)
{
	struct hid_driver *drv = hid->driver;
	struct hid_report *report;
	struct hid_input *next, *hidinput = NULL;
	int i, k;

	INIT_LIST_HEAD(&hid->inputs);
	INIT_WORK(&hid->led_work, hidinput_led_worker);

	if (!force) {
		for (i = 0; i < hid->maxcollection; i++) {
			struct hid_collection *col = &hid->collection[i];
			if (col->type == HID_COLLECTION_APPLICATION ||
					col->type == HID_COLLECTION_PHYSICAL)
				if (IS_INPUT_APPLICATION(col->usage))
					break;
		}

		if (i == hid->maxcollection)
			return -1;
	}

	report_features(hid);

   //对每一个report,建立一个input设备 
	for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
		if (k == HID_OUTPUT_REPORT &&
			hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORTS)
			continue;

		list_for_each_entry(report, &hid->report_enum[k].report_list, list) {

			if (!report->maxfield)
				continue;

			/*
			 * Find the previous hidinput report attached
			 * to this report id.
			 */
			if (hid->quirks & HID_QUIRK_MULTI_INPUT)
				hidinput = hidinput_match(report);

			if (!hidinput) {
				hidinput = hidinput_allocate(hid);
				if (!hidinput)
					goto out_unwind;
			}

			hidinput_configure_usages(hidinput, report);	//---->

			if (hid->quirks & HID_QUIRK_MULTI_INPUT)
				hidinput->report = report;
		}
	}

	list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
		if ((hid->quirks & HID_QUIRK_NO_EMPTY_INPUT) &&
		    !hidinput_has_been_populated(hidinput)) {
			/* no need to register an input device not populated */
			hidinput_cleanup_hidinput(hid, hidinput);
			continue;
		}

		if (drv->input_configured &&
		    drv->input_configured(hid, hidinput))
			goto out_unwind;
		if (input_register_device(hidinput->input)) //将设备注册到Input子系统
			goto out_unwind;
		hidinput->registered = true;
	}

	if (list_empty(&hid->inputs)) {
		hid_err(hid, "No inputs registered, leaving\n");
		goto out_unwind;
	}

	return 0;

out_unwind:
	/* unwind the ones we already registered */
	hidinput_disconnect(hid);

	return -1;
}

        hidinput是一个将hid数据转为input格式的协议转换者,转换完毕之后,将数据发送给hidinput->input 这个真正的连接到mousedev_handler驱动上input_dev设备, 这样当hid设备上传数据,引发irq中断

        drivers/hid/usbhid/hid-core.c

//hid->ll_driver = &usb_hid_driver
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,
};
//启动HID设备
static int usbhid_start(struct hid_device *hid)
{
        struct usb_interface *intf = to_usb_interface(hid->dev.parent);
        struct usb_host_interface *interface = intf->cur_altsetting;
        struct usb_device *dev = interface_to_usbdev(intf);
        struct usbhid_device *usbhid = hid->driver_data;
        unsigned int n, insize = 0;
        int ret;

        clear_bit(HID_DISCONNECTED, &usbhid->iofl);

        usbhid->bufsize = HID_MIN_BUFFER_SIZE;
        hid_find_max_report(hid, HID_INPUT_REPORT, &usbhid->bufsize);
        hid_find_max_report(hid, HID_OUTPUT_REPORT, &usbhid->bufsize);
        hid_find_max_report(hid, HID_FEATURE_REPORT, &usbhid->bufsize);

        if (usbhid->bufsize > HID_MAX_BUFFER_SIZE)
                usbhid->bufsize = HID_MAX_BUFFER_SIZE;

        hid_find_max_report(hid, HID_INPUT_REPORT, &insize);

        if (insize > HID_MAX_BUFFER_SIZE)
                insize = HID_MAX_BUFFER_SIZE;

        if (hid_alloc_buffers(dev, hid)) {
                ret = -ENOMEM;
                goto fail;
        }

        for (n = 0; n < interface->desc.bNumEndpoints; n++) {
                struct usb_endpoint_descriptor *endpoint;
                int pipe;
                int interval;

                endpoint = &interface->endpoint[n].desc;
                if (!usb_endpoint_xfer_int(endpoint))
                        continue;

                interval = endpoint->bInterval;

                /* Some vendors give fullspeed interval on highspeed devides */
                if (hid->quirks & HID_QUIRK_FULLSPEED_INTERVAL &&
                    dev->speed == USB_SPEED_HIGH) {
                        interval = fls(endpoint->bInterval*8);
                        pr_info("%s: Fixing fullspeed to highspeed interval: %d -> %d\n",
                                hid->name, endpoint->bInterval, interval);
                }

                /* Change the polling interval of mice and joysticks. */
                switch (hid->collection->usage) {
                case HID_GD_MOUSE:
                        if (hid_mousepoll_interval > 0)
                                interval = hid_mousepoll_interval;
                        break;
                case HID_GD_JOYSTICK:
                        if (hid_jspoll_interval > 0)
                                interval = hid_jspoll_interval;
                        break;
                }

                ret = -ENOMEM;
                if (usb_endpoint_dir_in(endpoint)) {
                        if (usbhid->urbin)
                                continue;
                        if (!(usbhid->urbin = usb_alloc_urb(0, GFP_KERNEL)))
                                goto fail;
                        pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
                        usb_fill_int_urb(usbhid->urbin, dev, pipe, usbhid->inbuf, insize,
                                         hid_irq_in, hid, interval); //中断函数hid_irq_in
                        usbhid->urbin->transfer_dma = usbhid->inbuf_dma;
                        usbhid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
                } else {
                        if (usbhid->urbout)
                                continue;
                        if (!(usbhid->urbout = usb_alloc_urb(0, GFP_KERNEL)))
                                goto fail;
                        pipe = usb_sndintpipe(dev, endpoint->bEndpointAddress);
                        usb_fill_int_urb(usbhid->urbout, dev, pipe, usbhid->outbuf, 0,
                                         hid_irq_out, hid, interval);
                        usbhid->urbout->transfer_dma = usbhid->outbuf_dma;
                        usbhid->urbout->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
                }
        }

        usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
        if (!usbhid->urbctrl) {
                ret = -ENOMEM;
                goto fail;
        }

        usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
                             usbhid->ctrlbuf, 1, hid_ctrl, hid);
        usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
        usbhid->urbctrl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;

        set_bit(HID_STARTED, &usbhid->iofl);

        if (hid->quirks & HID_QUIRK_ALWAYS_POLL) {
                ret = usb_autopm_get_interface(usbhid->intf);
                if (ret)
                        goto fail;
                set_bit(HID_IN_POLLING, &usbhid->iofl);
                usbhid->intf->needs_remote_wakeup = 1;
                ret = hid_start_in(hid);
                if (ret) {
                        dev_err(&hid->dev,
                                "failed to start in urb: %d\n", ret);
                }
                usb_autopm_put_interface(usbhid->intf);
        }

        /* Some keyboards don't work until their LEDs have been set.
         * Since BIOSes do set the LEDs, it must be safe for any device
         * that supports the keyboard boot protocol.
         * In addition, enable remote wakeup by default for all keyboard
         * devices supporting the boot protocol.
         */
        if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT &&
                        interface->desc.bInterfaceProtocol ==
                                USB_INTERFACE_PROTOCOL_KEYBOARD) {
                usbhid_set_leds(hid);
                device_set_wakeup_enable(&dev->dev, 1);
        }
        return 0;

fail:
        usb_free_urb(usbhid->urbin);
        usb_free_urb(usbhid->urbout);
        usb_free_urb(usbhid->urbctrl);
        usbhid->urbin = NULL;
        usbhid->urbout = NULL;
        usbhid->urbctrl = NULL;
        hid_free_buffers(dev, hid);
        return ret;
}

/*
 * Input interrupt completion handler.
 */
static void hid_irq_in(struct urb *urb)
{
        struct hid_device       *hid = urb->context;
        struct usbhid_device    *usbhid = hid->driver_data;
        int                     status;

        switch (urb->status) {
        case 0:                 /* success */
                usbhid->retry_delay = 0;
                if (!test_bit(HID_OPENED, &usbhid->iofl))
                        break;
                usbhid_mark_busy(usbhid);
                if (!test_bit(HID_RESUME_RUNNING, &usbhid->iofl)) {
                        //hid_input_report--->
                        hid_input_report(urb->context, HID_INPUT_REPORT,
                                         urb->transfer_buffer,
                                         urb->actual_length, 1);
                        /*
                         * autosuspend refused while keys are pressed
                         * because most keyboards don't wake up when
                         * a key is released
                         */
                        if (hid_check_keys_pressed(hid))
                                set_bit(HID_KEYS_PRESSED, &usbhid->iofl);
                        else
                                clear_bit(HID_KEYS_PRESSED, &usbhid->iofl);
                }
                break;
        case -EPIPE:            /* stall */
                usbhid_mark_busy(usbhid);
                clear_bit(HID_IN_RUNNING, &usbhid->iofl);
                set_bit(HID_CLEAR_HALT, &usbhid->iofl);
                schedule_work(&usbhid->reset_work);
                return;
        case -ECONNRESET:       /* unlink */
        case -ENOENT:
        case -ESHUTDOWN:        /* unplug */
                clear_bit(HID_IN_RUNNING, &usbhid->iofl);
                return;
        case -EILSEQ:           /* protocol error or unplug */
        case -EPROTO:           /* protocol error or unplug */
        case -ETIME:            /* protocol error or unplug */
        case -ETIMEDOUT:        /* Should never happen, but... */
                usbhid_mark_busy(usbhid);
                clear_bit(HID_IN_RUNNING, &usbhid->iofl);
                hid_io_error(hid);
                return;
        default:                /* error */
                hid_warn(urb->dev, "input irq status %d received\n",
                         urb->status);
        }    

        status = usb_submit_urb(urb, GFP_ATOMIC);
        if (status) {
                clear_bit(HID_IN_RUNNING, &usbhid->iofl);
                if (status != -EPERM) {
                        hid_err(hid, "can't resubmit intr, %s-%s/input%d, status %d\n",
                                hid_to_usb_dev(hid)->bus->bus_name,
                                hid_to_usb_dev(hid)->devpath,
                                usbhid->ifnum, status);
                        hid_io_error(hid);
                }    
        }    
}

int hid_input_report(struct hid_device *hid, int type, u8 *data, u32 size, int interrupt)
{
	struct hid_report_enum *report_enum;
	struct hid_driver *hdrv;
	struct hid_report *report;
	int ret = 0;

	if (!hid)
		return -ENODEV;

	if (down_trylock(&hid->driver_input_lock))
		return -EBUSY;

	if (!hid->driver) {
		ret = -ENODEV;
		goto unlock;
	}
	report_enum = hid->report_enum + type;
	hdrv = hid->driver;

	if (!size) {
		dbg_hid("empty report\n");
		ret = -1;
		goto unlock;
	}

	/* Avoid unnecessary overhead if debugfs is disabled */
	if (!list_empty(&hid->debug_list))
		hid_dump_report(hid, type, data, size);

	report = hid_get_report(report_enum, data);

	if (!report) {
		ret = -1;
		goto unlock;
	}

	if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) {
		ret = hdrv->raw_event(hid, report, data, size);
		if (ret < 0)
			goto unlock;
	}

	ret = hid_report_raw_event(hid, type, data, size, interrupt);//--->

unlock:
	up(&hid->driver_input_lock);
	return ret;
}

int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
		int interrupt)
{
	struct hid_report_enum *report_enum = hid->report_enum + type;
	struct hid_report *report;
	struct hid_driver *hdrv;
	unsigned int a;
	u32 rsize, csize = size;
	u8 *cdata = data;
	int ret = 0;

	report = hid_get_report(report_enum, data);
	if (!report)
		goto out;

	if (report_enum->numbered) {
		cdata++;
		csize--;
	}

	rsize = ((report->size - 1) >> 3) + 1;

	if (rsize > HID_MAX_BUFFER_SIZE)
		rsize = HID_MAX_BUFFER_SIZE;

	if (csize < rsize) {
		dbg_hid("report %d is too short, (%d < %d)\n", report->id,
				csize, rsize);
		memset(cdata + csize, 0, rsize - csize);
	}

	if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)
		hid->hiddev_report_event(hid, report);
	if (hid->claimed & HID_CLAIMED_HIDRAW) {
		ret = hidraw_report_event(hid, data, size);
		if (ret)
			goto out;
	}

	if (hid->claimed != HID_CLAIMED_HIDRAW && report->maxfield) {
		for (a = 0; a < report->maxfield; a++)
			hid_input_field(hid, report->field[a], cdata, interrupt);
		hdrv = hid->driver;
		if (hdrv && hdrv->report)
			hdrv->report(hid, report);
	}

	if (hid->claimed & HID_CLAIMED_INPUT)
		hidinput_report_event(hid, report); //--->
out:
	return ret;
}

void hidinput_report_event(struct hid_device *hid, struct hid_report *report)
{
	struct hid_input *hidinput;

	if (hid->quirks & HID_QUIRK_NO_INPUT_SYNC)
		return;

	list_for_each_entry(hidinput, &hid->inputs, list)
		input_sync(hidinput->input); //上报数据至input-core层
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值