Linux设备驱动之HID驱动---非常全面而且深刻

更新时间:2023-05-09 06:15:15 阅读: 评论:0

Linux设备驱动之HID驱动---⾮常全⾯⽽且深刻
本⽂系本站原创,欢迎转载!
转载请注明出处:/
------------------------------------------
⼀:前⾔
继前⾯分析过UHCI和HUB驱动之后,接下来以HID设备驱动为例来做⼀个具体的USB设备驱动分析的例⼦.HID是Human Interface Devices的缩写.翻译成中⽂即为⼈机交互设备.这⾥的⼈机交互设备是⼀个宏观上⾯的概念,任何设备,只要符合HID spec,都⼆:HID驱动⼊⼝分析
USB HID设备驱动⼊⼝位于linux-2.6.25/drivers/hid/usbhid/hid-core.c中.该module的⼊⼝为hid_init().代码如下:
static int __init hid_init(void)
{
int retval;
retval = usbhid_quirks_init(quirks_param);
if (retval)
goto usbhid_quirks_init_fail;
retval = hiddev_init();
if (retval)
goto hiddev_init_fail;
retval = usb_register(&hid_driver);
if (retval)
goto usb_register_fail;
info(DRIVER_VERSION ":" DRIVER_DESC);
return0;
usb_register_fail:
hiddev_exit();
hiddev_init_fail:
usbhid_quirks_exit();
usbhid_quirks_init_fail:
return retval;
}
⾸先来看usbhid_quirks_init()函数.quirks我们在分析UHCI和HUB的时候也接触过,表⽰需要做某种修正的设备.该函数调⽤的参数是quirks_param.定义如下:
static char *quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL };
module_param_array_named(quirks, quirks_param, charp, NULL, 0444);
从此可以看出, quirks_param是MAX_USBHID_BOOT_QUIRKS元素的字符串数组.并且在加载module的时候,可以动态的指定这些值.
分析到这⾥.有⼈可以反应过来了,usbhid_quirks_init()是⼀种动态进⾏HID设备修正的⽅式.具体要修正哪些设备,要修正设备的那些⽅⾯,都可以由加载模块是所带参数来决定.
usbhid_quirks_init()的代码如下:
int usbhid_quirks_init(char **quirks_param)
{
u16 idVendor, idProduct;
u32 quirks;
int n = 0, m;
for (; quirks_param[n] && n < MAX_USBHID_BOOT_QUIRKS; n++) {
m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x",
&idVendor, &idProduct, &quirks);
if (m != 3 ||
usbhid_modify_dquirk(idVendor, idProduct, quirks) != 0) {
printk(KERN_WARNING
"Could not par HID quirk module param %s\n",
quirks_param[n]);
}
}
return0;
}
由此可以看出, quirks_param数组中的每⼀项可以分为三个部份,分别是要修正设备的VendorID,ProductID和要修正的功能.⽐如0x1000 0x0001 0x0004就表⽰:要忽略掉VendorID为0x1000,ProductID为0x0004的设备.(在代码中,有#define HID_QUIRK_跟进usbhid_modify_dquirk()函数,代码如下:
int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct,
const u32 quirks)
{
struct quirks_list_struct *q_new, *q;
int list_edited = 0;
if (!idVendor) {
dbg_hid("Cannot add a quirk with idVendor = 0\n");
return -EINVAL;
}
q_new = kmalloc(sizeof(struct quirks_list_struct), GFP_KERNEL);
if (!q_new) {
dbg_hid("Could not allocate quirks_list_struct\n");
return -ENOMEM;
}
q_new->hid_bl_item.idVendor = idVendor;
q_new->hid_bl_item.idProduct = idProduct;
q_new->hid_bl_item.quirks = quirks;
down_write(&dquirks_rwm);
list_for_each_entry(q, &dquirks_list, node) {
if (q->hid_bl_item.idVendor == idVendor &&
q->hid_bl_item.idProduct == idProduct) {
list_replace(&q->node, &q_new->node);
kfree(q);
list_edited = 1;
break;
}
}
if (!list_edited)
list_add_tail(&q_new->node, &dquirks_list);
up_write(&dquirks_rwm);
return0;
}
这个函数⽐较简单,就把quirks_param数组项中的三个部份存⼊⼀个封装结构.然后将其结构挂载到dquirks_list表.如果dquirks_list有重复的VendorId和ProductID就更新其quirks信息.
经过usbhid_quirks_init()之后,所有要修正的设备的相关操作都会存放在dquirks_list中.
返回到hid_init(),继续往下⾯分析.
hiddev_init()是⼀个⽆关的操作,不会影响到后⾯的操作.忽略
后⾯就是我们今天要分析的重点了,如下:
retval = usb_register(&hid_driver);
通过前⾯对HUB的驱动分析,相信对usb_redister()应该很熟悉了.hid_driver定义如下:
static struct usb_driver hid_driver = {
.name =    "usbhid",
.probe =    hid_probe,
.disconnect =  hid_disconnect,
.suspend =  hid_suspend,
.resume =  hid_resume,
.ret_resume = hid_post_ret,
.pre_ret =    hid_pre_ret,
.post_ret =  hid_post_ret,
.id_table = hid_usb_ids,
.supports_autosuspend = 1,
};
其中,id_table的结构为hid_usb_ids.定义如下:
static struct usb_device_id hid_usb_ids [] = {
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
.bInterfaceClass = USB_INTERFACE_CLASS_HID },
{ }                    /* Terminating entry */
};
也就是说,该驱动会匹配interface的ClassID,所有ClassID为USB_INTERFACE_CLASS_HID的设备都会被这个驱动所匹配.所以,所有USB HID设备都会由这个module来驱动.
三:HID驱动的probe过程
从上⾯的分析可看到,probe接⼝为hid_probe().定义如下:
static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct hid_device *hid;
char path[64];
int i;
char *c;
dbg_hid("HID probe called for ifnum %d\n",
intf->alttting->desc.bInterfaceNumber);
//config the hid device
if (!(hid = usb_hid_configure(intf)))
return -ENODEV;
usbhid_init_reports(hid);
hid_dump_device(hid);
if (hid->quirks & HID_QUIRK_RESET_LEDS)
usbhid_t_leds(hid);
if (!hidinput_connect(hid))
hid->claimed |= HID_CLAIMED_INPUT;
if (!hiddev_connect(hid))
hid->claimed |= HID_CLAIMED_HIDDEV;
if (!hidraw_connect(hid))
hid->claimed |= HID_CLAIMED_HIDRAW;
usb_t_intfdata(intf, hid);
if (!hid->claimed) {
printk ("HID device claimed by neither input, hiddev nor hidraw\n");
hid_disconnect(intf);
return -ENODEV;
}
if ((hid->claimed & HID_CLAIMED_INPUT))
hid_ff_init(hid);
if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER)
hid_fixup_sony_ps3_controller(interface_to_usbdev(intf),
intf->cur_alttting->desc.bInterfaceNumber);
printk(KERN_INFO);
if (hid->claimed & HID_CLAIMED_INPUT)
printk("input");
if ((hid->claimed & HID_CLAIMED_INPUT) && ((hid->claimed & HID_CLAIMED_HIDDEV) ||
hid->claimed & HID_CLAIMED_HIDRAW))
printk(",");
if (hid->claimed & HID_CLAIMED_HIDDEV)
printk("hiddev%d", hid->minor);
if ((hid->claimed & HID_CLAIMED_INPUT) && (hid->claimed & HID_CLAIMED_HIDDEV) &&
(hid->claimed & HID_CLAIMED_HIDRAW))
printk(",");
if (hid->claimed & HID_CLAIMED_HIDRAW)
printk("hidraw%d", ((struct hidraw*)hid->hidraw)->minor);
c = "Device";
for (i = 0; i < hid->maxcollection; i++) {
if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
(hid->collection[i].usage & HID_USAGE_PAGE) == HID_UP_GENDESK &&
(hid->collection[i].usage & 0xffff) < ARRAY_SIZE(hid_types)) {
c = hid_types[hid->collection[i].usage & 0xffff];
break;
}
}
usb_make_path(interface_to_usbdev(intf), path, 63);
printk(": USB HID v%x.%02x %s [%s] on %s\n",
hid->version >> 8, hid->version & 0xff, c, hid->name, path);
return0;
}
这个函数看起来是不是让⼈⼼慌慌?其实这个函数的最后⼀部份就是打印出⼀个Debug信息,我们根本就不需要去看. hiddev_connect()和hidraw_connect()是⼀个选择编译的操作,也不可以不要去理会.然后,剩下的就没多少了.
3.1:usb_hid_configure()函数分析
先来看usb_hid_configure().顾名思义,该接⼝⽤来配置hid设备.怎么配置呢?还是深⼊到代码来分析,该函数有⼀点长,分段分析如下:
static struct hid_device *usb_hid_configure(struct usb_interface *intf)
{
struct usb_host_interface *interface = intf->cur_alttting;
struct usb_device *dev = interface_to_usbdev (intf);
struct hid_descriptor *hdesc;
struct hid_device *hid;
u32 quirks = 0;
unsigned rsize = 0;
char *rdesc;
int n, len, insize = 0;
struct usbhid_device *usbhid;
quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct));
/* Many keyboards and mice don't like to be polled for reports,
* so we will always t the HID_QUIRK_NOGET flag for them. */
//如果是boot设备,跳出.不由此驱动处理
if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) {
if (interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_KEYBOARD ||
interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE)
quirks |= HID_QUIRK_NOGET;
}
//如果是要忽略的
if (quirks & HID_QUIRK_IGNORE)
return NULL;
if ((quirks & HID_QUIRK_IGNORE_MOUSE) &&
(interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE))
return NULL;
⾸先找到该接⼝需要修正的操作,也就是上⾯代码中的quirks值,如果没有修正操作,则quirks为0.另外,根据usb hid spec中的定义,subclass如果为1,则说明该设备是⼀个boot阶段使⽤的hid设备,然后Protocol Code为1和2时分别代表Keyboard和Mou. 如
//get hid descriptors
if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&
(!interface->desc.bNumEndpoints ||
usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
dbg_hid("class descriptor not prent\n");
return NULL;
}
//bNumDescriptors:⽀持的附属描述符数⽬
for (n = 0; n < hdesc->bNumDescriptors; n++)
if (hdesc->desc[n].bDescriptorType == HID_DT_REPORT)
rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength);
//如果Report_Descriptors长度不合法
if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
dbg_hid("weird size of report descriptor (%u)\n", rsize);
return NULL;
}
if (!(rdesc = kmalloc(rsize, GFP_KERNEL))) {
dbg_hid("couldn't allocate rdesc memory\n");
return NULL;
}
/
/Set idle_time = 0
hid_t_idle(dev, interface->desc.bInterfaceNumber, 0, 0);
//Get Report_Descriptors
if ((n = hid_get_class_descriptor(dev, interface->desc.bInterfaceNumber, HID_DT_REPORT, rdesc, rsize)) < 0) {
dbg_hid("reading report descriptor failed\n");
kfree(rdesc);
return NULL;
}
//是否属于fixup?
usbhid_fixup_report_descriptor(le16_to_cpu(dev->descriptor.idVendor),
le16_to_cpu(dev->descriptor.idProduct), rdesc,
rsize, rdesc_quirks_param);
dbg_hid("report descriptor (size %u, read %d) = ", rsize, n);
for (n = 0; n < rsize; n++)
dbg_hid_line(" %02x", (unsigned char) rdesc[n]);
dbg_hid_line("\n");
对于HID设备来说,在interface description之后会附加⼀个hid description, hid description中的最后部份包含有Report description或者Physical Descriptors的长度.
在上⾯的代码中,⾸先取得附加在interface description之后的hid description,然后,再从hid description中取得report description的长度.最后,取得report description的详细信息.
在这⾥,还会将idle时间设备为0,表⽰⽆限时,即,从上⼀次报表传输后,只有在报表发⽣改变时,才会传送此报表内容,否则,传送NAK.
这段代码的最后⼀部份是相关的fixup操作,不做详细分析.
/
/pasr the report_descriptor
if (!(hid = hid_par_report(rdesc, n))) {
dbg_hid("parsing report descriptor failed\n");
kfree(rdesc);
return NULL;
}
kfree(rdesc);
hid->quirks = quirks;
if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL)))
goto fail_no_usbhid;
hid->driver_data = usbhid;
usbhid->hid = hid;
解析获得的report description,解析之后的信息,存放在hid_device->collection和hid_device->report_enum[ ]中,这个解析过程之后会做详细分析.然后,初始化⼀个usbhid_device结构,使usbhid_device->hid指向刚解析report description获得的hid_device.同    usbhid->bufsize = HID_MIN_BUFFER_SIZE;
//计算各传输⽅向的最⼤buffer
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;
//in⽅向的传输最⼤值
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)) {
hid_free_buffers(dev, hid);
goto fail;
}
计算传输数据的最⼤缓存区,并以这个⼤⼩为了hid设备的urb传输分配空间.另外,这⾥有⼀个最⼩值限制即代码中所看到的HID_MIN_BUFFER_SIZE,为64, 即⼀个⾼速设备的⼀个端点⼀次传输的数据量.在这⾥定义最⼩值为64是为了照顾低速
然后,调⽤hid_alloc_buffers()为hid的urb传输初始化传输缓冲区.
另外,需要注意的是,insize为INPUT⽅向的最⼤数据传输量.
// 初始化usbhid->urbin和usbhid->usbout
for (n = 0; n < interface->desc.bNumEndpoints; n++) {
struct usb_endpoint_descriptor *endpoint;
int pipe;
int interval;
endpoint = &interface->endpoint[n].desc;
//不是中断传输退出
if ((endpoint->bmAttributes & 3) != 3)      /* Not an interrupt endpoint */
continue;
interval = endpoint->bInterval;
/* Change the polling interval of mice. */
/
/修正⿏标的双击时间
if (hid->collection->usage == HID_GD_MOUSE && hid_moupoll_interval > 0)
interval = hid_moupoll_interval;
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);
usbhid->urbin->transfer_dma = usbhid->inbuf_dma;
usbhid->urbin->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
} el {
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;
}
}
if (!usbhid->urbin) {
err_hid("couldn't find an input interrupt endpoint");
goto fail;
}
遍历接⼝中的所有endpoint,并初始化in中断传输⽅向和out中断⽅向的urb.如果⼀个hid设备没有in⽅向的中断传输,⾮法.
另外,在这⾥要值得注意的是, 在为OUT⽅向urb初始化的时候,它的传输缓存区⼤⼩被设为了0.IN⽅向的中断传输缓存区⼤⼩被设为了insize,传输缓存区⼤⼩在submit的时候会修正的.    init_waitqueue_head(&hid->wait);
INIT_WORK(&usbhid->ret_work, hid_ret);
tup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
spin_lock_init(&usbhid->inlock);
spin_lock_init(&usbhid->outlock);
spin_lock_init(&usbhid->ctrllock);
hid->version = le16_to_cpu(hdesc->bcdHID);
hid->country = hdesc->bCountryCode;
hid->dev = &intf->dev;
usbhid->intf = intf;
usbhid->ifnum = interface->desc.bInterfaceNumber;
hid->name[0] = 0;
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));
hid->bus = BUS_USB;
hid->vendor = le16_to_cpu(dev->descriptor.idVendor);
hid->product = 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->alttting[0].desc.bInterfaceNumber);
if (usb_string(dev, dev->descriptor.iSerialNumber, hid->uniq, 64) <= 0)
hid->uniq[0] = 0;
初始化hid的相关信息.
//初始化hid 的ctrl传输
usbhid->urbctrl = usb_alloc_urb(0, GFP_KERNEL);
if (!usbhid->urbctrl)
goto fail;
usb_fill_control_urb(usbhid->urbctrl, dev, 0, (void *) usbhid->cr,
usbhid->ctrlbuf, 1, hid_ctrl, hid);
usbhid->urbctrl->tup_dma = usbhid->cr_dma;
usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
hid->hidinput_input_event = usb_hidinput_input_event;
hid->hid_open = usbhid_open;
hid->hid_clo = usbhid_clo;
#ifdef CONFIG_USB_HIDDEV
hid->hiddev_hid_event = hiddev_hid_event;
hid->hiddev_report_event = hiddev_report_event;
#endif
hid->hid_output_raw_report = usbhid_output_raw_report;
return hid;
初始化usbhid的控制传输urb,之后⼜初始化了usbhid的⼏个操作函数.这个操作有什么⽤途,等⽤到的时候再来进⾏分析.
fail:
usb_free_urb(usbhid->urbin);
usb_free_urb(usbhid->urbout);
usb_free_urb(usbhid->urbctrl);
hid_free_buffers(dev, hid);
kfree(usbhid);
fail_no_usbhid:
hid_free_device(hid);
return NULL;
}
经过上⾯的分析之后,我们对这个函数的⼤概操作有了⼀定的了解.现在分析⾥⾯调⽤的⼀些重要的⼦调函数.等这些⼦函数全部分析完了之后,不妨回过头看下这个函数.
3.1.1:hid_par_report()分析
第⼀个要分析的函数是hid_par_report().该函数⽤来解析report description.
解析report description是⼀个繁杂的过程,对这个描述符不太清楚的,仔细看⼀下spec.在这⾥我们只会做代码上的分析.
代码如下:
struct hid_device *hid_par_report(__u8 *start, unsigned size)
{
struct hid_device *device;
struct hid_parr *parr;
struct hid_item item;
__u8 *end;
unsigned i;
static int (*dispatch_type[])(struct hid_parr *parr,
struct hid_item *item) = {
hid_parr_main,
hid_parr_global,
hid_parr_local,
hid_parr_rerved
};
if (!(device = kzalloc(sizeof(struct hid_device), GFP_KERNEL)))
return NULL;
//默认HID_DEFAULT_NUM_COLLECTIONS 项
if (!(device->collection = kzalloc(sizeof(struct hid_collection) *
HID_DEFAULT_NUM_COLLECTIONS, GFP_KERNEL))) {
kfree(device);
return NULL;
}
//hid_device->collection_size: collection的项数
device->collection_size = HID_DEFAULT_NUM_COLLECTIONS;
for (i = 0; i < HID_REPORT_TYPES; i++)
INIT_LIST_HEAD(&device->report_enum[i].report_list);
if (!(device->rdesc = kmalloc(size, GFP_KERNEL))) {
kfree(device->collection);
kfree(device);
return NULL;
}
/
/hid_device->rdesc存放report_descriptor,hid_device->size存放这个描述符的⼤⼩
memcpy(device->rdesc, start, size);
device->rsize = size;
if (!(parr = vmalloc(sizeof(struct hid_parr)))) {
kfree(device->rdesc);
kfree(device->collection);
kfree(device);
return NULL;
}
memt(parr, 0, sizeof(struct hid_parr));
parr->device = device;
end = start + size;
while ((start = fetch_item(start, end, &item)) != NULL) {
//long item在这⾥暂不做par
if (item.format != HID_ITEM_FORMAT_SHORT) {
dbg_hid("unexpected long global item\n");
hid_free_device(device);
vfree(parr);
return NULL;
}
//par the short item
if (dispatch_pe](parr, &item)) {
dbg_hid("item %u %u %u %u parsing failed\n",
item.format, (unsigned)item.size, (pe, (unsigned)item.tag);
hid_free_device(device);
vfree(parr);
return NULL;
}
//如果全部解析完了
if (start == end) {
if (parr->collection_stack_ptr) {
dbg_hid("unbalanced collection at end of report description\n");
hid_free_device(device);
vfree(parr);
return NULL;
}
if (parr->local.delimiter_depth) {
dbg_hid("unbalanced delimiter at end of report description\n");
hid_free_device(device);
vfree(parr);
return NULL;
}
vfree(parr);
return device;
}
}
dbg_hid("item fetching failed at offt %d\n", (int)(end - start));
hid_free_device(device);
vfree(parr);
return NULL;
}
进⼊到这个函数,我们⾸先看到的是Main,Globa,Local标签的解析函数.然后,分配并初始化了hid_device结构和hid_ parr.在代码中我们看到,hid_ parr-> device指向了hid_device.后hid_device没有任何域指向hid_parr. 实际上hid_parr只是⼀个辅另外,hid_device-> rdesc保存了⼀份report description副本.
然后,就开始对report description的解析.函数fetch_item()⽤来取出report description的⼀项数据.代码如下:
static u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item)
{
u8 b;
//合法性检测
if ((end - start) <= 0)
return NULL;
//取前⾯⼀个字节.对于短项.它的⾸个字节定义了bsize,bType,bTag.⽽对于长项,它的值为0xFE
b = *start++;
item->type = (b >> 2) & 3;
item->tag  = (b >> 4) & 15;
//如果为长项.它的Type和Tag在其后的⼆个字节中.item->data.longdata指向数据的起始位置
if (item->tag == HID_ITEM_TAG_LONG) {
item->format = HID_ITEM_FORMAT_LONG;
if ((end - start) < 2)
return NULL;
item->size = *start++;
item->tag  = *start++;
if ((end - start) < item->size)
return NULL;
item->data.longdata = start;
start += item->size;
return start;
}
//对于短项的情况.取得size值.并根据size值取得它的data域
item->format = HID_ITEM_FORMAT_SHORT;
item->size = b & 3;
switch (item->size) {
ca0:
return start;
ca1:
if ((end - start) < 1)
return NULL;
item->data.u8 = *start++;
return start;
ca2:
if ((end - start) < 2)
return NULL;
item->data.u16 = le16_to_cpu(get_unaligned((__le16*)start));
start = (__u8 *)((__le16 *)start + 1);
return start;
ca3:
item->size++;

本文发布于:2023-05-09 06:15:15,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/90/101653.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:设备   函数   分析   传输   驱动   操作
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图