USB驱动之Android usb鼠标驱动
Posted 四季帆
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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层
以上是关于USB驱动之Android usb鼠标驱动的主要内容,如果未能解决你的问题,请参考以下文章
Linux USB 驱动开发—— USB 鼠标驱动注解及测试