linux usb hid 工具,Linux下 USB HID device driver研究(三)--probe

作者: Sam(甄峰) sam_code@hotmail.com

1. 解读hiddevice 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 probecalled for ifnum %dn",

intf->altsetting->desc.bInterfaceNumber);

if (!(hid =usb_hid_configure(intf)))

return -ENODEV;

usbhid_init_reports(hid);

hid_dump_device(hid);

if (hid->quirks &HID_QUIRK_RESET_LEDS)

usbhid_set_leds(hid);

if(!hidinput_connect(hid))

hid->claimed |=HID_CLAIMED_INPUT;

if (!hiddev_connect(hid))

hid->claimed |=HID_CLAIMED_HIDDEV;

usb_set_intfdata(intf,hid);

if(!hid->claimed) {

printk ("HID device not claimedby input or hiddevn");

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_altsetting->desc.bInterfaceNumber);

printk(KERN_INFO);

if(hid->claimed &HID_CLAIMED_INPUT)

printk("input");

if (hid->claimed ==(HID_CLAIMED_INPUT | HID_CLAIMED_HIDDEV))

printk(",");

if (hid->claimed &HID_CLAIMED_HIDDEV)

printk("hiddev%d",hid->minor);

c = "Device";

for (i = 0; i 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 HIDv%x.%02x %s [%s] on %sn",

hid->version>> 8, hid->version& 0xff, c, hid->name,path);

return 0;

}

1.1:usb_hid_configure(intf)解读:

从字面来看,它是指配置hid。

static struct hid_device*usb_hid_configure(struct usb_interface *intf)

{

struct usb_host_interface *interface =intf->cur_altsetting;

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;

//得到对应vid,pid的quriks.如果没有,则返回0

quirks =usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor),

le16_to_cpu(dev->descriptor.idProduct));

//如果为boot设备且为keyboard或mouse.则quirks=HID_QUIRK_NOGET

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;

}

//如果quirks显示要忽略,则退出probe

if (quirks& HID_QUIRK_IGNORE)

return NULL;

//HID_QUIRK_IGNORE_MOUSE表示如果为mouse,则忽略。

if ((quirks& HID_QUIRK_IGNORE_MOUSE)&&

(interface->desc.bInterfaceProtocol== USB_INTERFACE_PROTOCOL_MOUSE))

returnNULL;

//如果interface扩展描述符中没有类型为HID_DT_HID条目,或者interface包含的endpoint数目为0,又或者interfaceendpoint中扩展描述符中没有类型为HID_DT_HID的条目。则退出。

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 notpresentn");

return NULL;

}

//得到描述符长度:

for (n = 0; n< hdesc->bNumDescriptors; n++)

if(hdesc->desc[n].bDescriptorType ==HID_DT_REPORT)

rsize =le16_to_cpu(hdesc->desc[n].wDescriptorLength);

//如果描述符长度不对,则退出

if (!rsize || rsize> HID_MAX_DESCRIPTOR_SIZE) {

dbg_hid("weird size of reportdescriptor (%u)n", rsize);

return NULL;

}

//创建此长度内存空间

if (!(rdesc =kmalloc(rsize, GFP_KERNEL))) {

dbg_hid("couldn't allocaterdesc memoryn");

return NULL;

}

//向dev的endpoint发送HID_REQ_SET_IDLErequest.

hid_set_idle(dev,interface->desc.bInterfaceNumber, 0, 0);

//取得reportdescription的详细信息,放到rdesc中。

if ((n =hid_get_class_descriptor(dev,interface->desc.bInterfaceNumber, HID_DT_REPORT,rdesc, rsize)) < 0) {

dbg_hid("reading reportdescriptor failedn");

kfree(rdesc);

return NULL;

}

usbhid_fixup_report_descriptor(le16_to_cpu(dev->descriptor.idVendor),

le16_to_cpu(dev->descriptor.idProduct),rdesc,

rsize,rdesc_quirks_param);

dbg_hid("reportdescriptor (size %u, read %d) = ", rsize, n);

for (n = 0; n < rsize; n++)

dbg_hid_line(" %02x", (unsignedchar) rdesc[n]);

dbg_hid_line("n");

//解析report。并建立hid_device.返回给hid.

if (!(hid =hid_parse_report(rdesc, n))) {

dbg_hid("parsing reportdescriptor failedn");

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;

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)) {

hid_free_buffers(dev,hid);

goto fail;

}

for (n = 0; n< interface->desc.bNumEndpoints; n++){

structusb_endpoint_descriptor *endpoint;

int pipe;

int interval;

endpoint =&interface->endpoint[n].desc;

if((endpoint->bmAttributes & 3) !=3)

continue;

interval =endpoint->bInterval;

if(hid->collection->usage ==HID_GD_MOUSE &&hid_mousepoll_interval > 0)

interval =hid_mousepoll_interval;

if(usb_endpoint_dir_in(endpoint)) {

if(usbhid->urbin)

continue;

if(!(usbhid->urbin = usb_alloc_urb(0,GFP_KERNEL)))

gotofail;

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;

} else {

if(usbhid->urbout)

continue;

if(!(usbhid->urbout = usb_alloc_urb(0,GFP_KERNEL)))

gotofail;

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 inputinterrupt endpoint");

goto fail;

}

init_waitqueue_head(&hid->wait);

INIT_WORK(&usbhid->reset_work,hid_reset);

setup_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 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->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->setup_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_close =usbhid_close;

#ifdef CONFIG_USB_HIDDEV

hid->hiddev_hid_event =hiddev_hid_event;

hid->hiddev_report_event =hiddev_report_event;

#endif

return hid;

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;

}

1.1.1:usbhid_lookup_quirk(pid,vid)解析:

分别使用usbhid_exists_dquirk(pid,vid),usbhid_exists_squirk(pid,vid)来查看动态和静态的quirks-list.如果有,则返回此quirks.没有,则返回0。

1.1.2:usb_get_extra_descriptor(descriptor, type,&hdesc)解析:

它调用__usb_get_extra_descriptor()来解析参数一(interface或endpoint描述符)中的扩展描述符区--extra。看是否有类型为参数二(type)的条目,然后把地址交给参数三。

Sam未经证明的猜测:HID设备中,interface描述符中包含的这个扩展描述符。其中存放的都是HID信息,以hid_descriptor结构存放。

对于HID设备来说,在interface description之后会附加一个hid description, hiddescription中的最后部份包含有Report description或者PhysicalDescriptors的长度.

hid_descriptor->usb_descriptor_header->bDescriptorType为HID_DT_HID

后面描述符(包括它自己)hid_descriptor->hid_class_descriptor->bDescriptorType为HID_DT_REPORT

1.1.3:hid_set_idle(structusb_device *dev, int ifnum, int report, intidle):

它简单的调用usb_control_msg() 发送或接收一个usb控制消息。

具体到这个例子中:

usb_control_msg(dev, usb_sndctrlpipe(dev, 0),

HID_REQ_SET_IDLE,USB_TYPE_CLASS | USB_RECIP_INTERFACE, (idle<< 8) | report,

ifnum, NULL, 0,USB_CTRL_SET_TIMEOUT);

usb_sndctrlpipe(dev,0):指出将dev中endpoint 0 设为发送control管道。

HID_REQ_SET_IDLE:此控制消息的Request。

USB_TYPE_CLASS |USB_RECIP_INTERFACE:请求类型.

调用成功才会返回。

1.1.4:hid_get_class_descriptor(dev,interface->desc.bInterfaceNumber, HID_DT_REPORT,rdesc, rsize)

也是通过调用usb_control_msg() 发送或接收一个usb控制消息。

发送USB_REQ_GET_DESCRIPTOR,将得到的report descriptor放到rdesc中。

1.1.5: hid_parse_report(__u8*start, unsigned size)

参数一:通过usb_control_msg发送request(USB_REQ_GET_DESCRIPTOR)从设备得到的report descriptor.

参数二:是此report Descriptor的长度。

1).首先创建hid_device类型设备。

2).将参数一中数据保存在hid_device->rdesc中。长度保存在hid_device->rsize中。

3). 使用fetch_item(__u8 *start, __u8 *end, struct hid_item*item)得到report description的下一项数据。放到item中。

4). 根据不同的item.type.分别调用

hid_parser_main,

hid_parser_global,

hid_parser_local,

hid_parser_reserved

来处理。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值