USB驱动之Android usb鼠标驱动

news/2024/11/7 12:45:42/

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


http://www.ppmy.cn/news/211203.html

相关文章

编写USB鼠标驱动程序

编写USB鼠标驱动程序 文章目录 编写USB鼠标驱动程序参考资料&#xff1a;1. 目标2. 编程2.1 驱动框架2.2 实现usb_driver2.2.1 id_table2.2.2 probe函数 2.3 实现输入设备2.4 实现数据传输 3. 上机实验 致谢 参考资料&#xff1a; Linux内核源码&#xff1a;include\linux\usb.…

usb鼠标驱动(一)

Linux USB 鼠标驱动程序详解 注册一个usb driver&#xff0c;这个drvier不是usb设备driver&#xff0c;而是接口driver。 /* use a define to avoid include chaining to get THIS_MODULE & friends */ #define usb_register(driver) \usb_register_driver(driver, THIS_M…

设计模式入门:策略模式

现有一套模拟鸭子游戏&#xff0c;可以一边游泳&#xff0c;一边呱呱叫。 每种鸭子都会呱呱叫和游泳&#xff0c;只是外观不同。因此&#xff0c;quack和swim放在父类中&#xff0c;display放在子类中实现。 增加新的功能&#xff1a;鸭子飞翔。 1 我们可能想到直接在父类中增…

条码软件如何制作GS1-128条形码

GS1是国际物品编码组织的英文名称&#xff0c;全称为GS1全球统一标识系统。GS1是整个一个系统&#xff0c;或者说架构&#xff0c;其架构内部所包含的条码&#xff0c;可以有很多种&#xff0c;比如内部的条码是Code 128的&#xff0c;叫做GS1-128&#xff0c;而内部是DataBar的…

亚马逊条码标签(SSCC/FBA)的制作打印

近日&#xff0c;亚马逊对所有商家提出统一要求&#xff0c;产品须按照亚马逊制定的新规范来打印条码标签。做亚马逊外贸的商户都清楚&#xff0c;货品进入亚马逊仓库都必须通过扫描入库&#xff0c;否则无法进行上市销售。 有很多客户最近联系我们请求提供技术指导&#xff0c…

条码标签软件快速生成序列号标签

在做条码标签的时候&#xff0c;各行各业应该都会制作一种可变数据标签——序列号标签。其实输入序列号很简单&#xff0c;如果数量不多&#xff0c;手工输入都是可以的&#xff0c;可是如果需要100个、1000个、甚至10000个序列号的时候&#xff0c;难道还手工输入吗&#xff1…

gt800打印测试软件,zebra GT800 高级桌面条码标签打印机

zebra GT800 高级桌面条码标签打印机 zebra GT800 热敏/热传印桌面标签打印机以极具竞争优势的价格实现了先进性能和可靠性&#xff0c;提供一系列功能&#xff0c;可满足各类中、低容量的打印应用。GT800 能容纳 300 米碳带&#xff0c;增加了灵活性和打印机正常运行时间&…

条码标签里的数据源如何使用

很多小伙伴在刚开始使用条码软件制作标签时&#xff0c;会对于数据源这个概念有些陌生&#xff0c;也不知道该在什么时候使用数据源。其实这个很好判断&#xff0c;基本宗旨就是如果一个元素是固定不变的&#xff0c;那么就不需要使用数据源&#xff0c;如果一个元素的内容是变…