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_HIDDEVhid->hiddev_connect = hiddev_connect;hid->hiddev_disconnect = hiddev_disconnect;hid->hiddev_hid_event = hiddev_hid_event;hid->hiddev_report_event = hiddev_report_event;
#endifhid->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_deviceusbhid = 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;elsehid_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和productIDconnect_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_inusbhid->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);elseclear_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鼠标驱动相关推荐

  1. USB学习6---Linux Android USB软件架构设计

    下面学习针对高通平台的HS-USB(HS:high speed高速)堆栈(stack)软件架构设计和源代码布局的细节. Android HS-USB堆栈基于下面几点: Gadget driver fr ...

  2. Android usb 权限广播,[Android]USB开发

    第一:请求权限和请求权限回调(通过广播回调) 注册一个广播接收器用于接收USB权限被同意或拒绝后发出的广播 //注册USB设备权限管理广播 IntentFilter filter = new Inte ...

  3. android usb存储模式,Android USB连接说明:MTP,PTP和USB大容量存储器

    为什么现代Android设备不支持USB海量存储 USB大容量存储(也称为" USB大容量存储设备类",USB MSC或UMS )是旧版Android将它存储暴露给计算机的方式,当 ...

  4. usb hub芯片 android,usb hub芯片GL850G详解

    GL850G拥有低耗电.温度低及接脚数减少等产品特性. 它支援4个下游连接埠,可完全支援USB2.0/1.1规格,因此无论是与主机端或是与其他USB设备介面的传输连接(高速/全速/低速设备传输)皆能完 ...

  5. android usb host hid,Android USB Host与HID通讯

    前端时间捣鼓一个HID的硬件, 需要和android通信, 网上搜索了一圈,收获不小. 其中代码之处有些地方需要注意的, 特此注明一下: /*** USB HOST 连接 HID *@authorIV ...

  6. android usb otg 查看,android USB OTG功能如何打开及实现

    一.检查HW原理图,确认是否支持OTG功能(vbus是否供上电,IDDIG pin连接是否正确)二.若HW确认支持OTG功能,则按照以下方法分别打开USB OTG功能及实现挂载:如 一.检查HW原理图 ...

  7. usb转网口 android,USB转接头拆解,小心入坑,安卓USB(Micro USB)转换成Type C口

    这么个小东西没有什么商标,品牌.成本价格应该在1元钱左右吧,购买价格7元,算上物流费,估计也没有多少利润.这个小小的东西不知道有多少人买. 从图上看一头是插入Micro USB的手机,另外一端为typ ...

  8. usb hub芯片 android,USB HUB芯片和导电软垫 的故事

    USB HUB芯片和导电软垫 的故事 [复制链接] 本帖最后由 574433742 于 2015-11-6 16:19 编辑 关键词: Cube i10 无损win10刷入Remix OS     F ...

  9. android驱动测试,Android: 通过 cucumber 驱动 monkey 做稳定性测试

    主要内容 稳定性测试是什么 Monkey 介绍 自动化 Monkey 稳定性测试是什么 通过随机点击屏幕一段时间,看看 app 会不会奔溃,能不能维持正常运行. Monkey 介绍 Monkey 是一 ...

最新文章

  1. 陕西活性炭需求分析_20212027年中国粉末活性炭行业市场发展现状调研与投资趋势前景分析报告...
  2. SAP ABAP编辑器里的Code Completion(代码自动完成)的等待时间设置
  3. System.Windows.Forms.Timer与System.Timers.Timer的区别
  4. IntelliJ IDEA修改项目的包名_修改包名
  5. 基于Sanic的微服务基础架构
  6. 【python】yaml文件操作
  7. myEclipse配置java版本(环境、项目、编译)
  8. 16张精美的数据可视化,带你走进它的前世今生
  9. 好几个div(元素)找到最后一个
  10. 计算机三角函数习惯原创的音乐,三角函数 UNIT版
  11. 微信公众号音频在线下载工具 微信公众号音频下载工具 微信公众号视频下载工具
  12. 半次元热门图片,各种好看的cosplay小姐姐,统统爬取收藏
  13. 科比投篮预测——数据处理与分析
  14. 研究生入门,如何高效阅读论文
  15. C语言基础-判断质数(素数)
  16. 【李航统计学习方法】感知机模型
  17. Altera timing analyzer理解
  18. 迷阵突围(dijkstra求次短路径)
  19. 中国阀门喷射式喷墨打印机市场深度研究分析报告
  20. 使用网络调试助手连接阿里云平台

热门文章

  1. Dev C++单步调试/debug详细图解
  2. Nimbus线上AMA内容记录-第二期
  3. 做企业,就要做的象长虹,创新不止
  4. 2021年美容师(高级)考试内容及美容师(高级)找解析
  5. 前端开发app,关于apicloud与dcloud的我的一些看法
  6. android对象命名的基本规则,类,对象,命名规则
  7. C++文件操作之eof函数问题
  8. ipcs ipcrm
  9. meminfo 解释
  10. linux df命令none,linux磁盘命令中df命令的作用