diff options
Diffstat (limited to 'drivers/usb/gadget/f_hid.c')
-rw-r--r-- | drivers/usb/gadget/f_hid.c | 208 |
1 files changed, 165 insertions, 43 deletions
diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c index 3b3932c55361..16a8b1c15c62 100644 --- a/drivers/usb/gadget/f_hid.c +++ b/drivers/usb/gadget/f_hid.c | |||
@@ -26,6 +26,12 @@ static struct class *hidg_class; | |||
26 | /*-------------------------------------------------------------------------*/ | 26 | /*-------------------------------------------------------------------------*/ |
27 | /* HID gadget struct */ | 27 | /* HID gadget struct */ |
28 | 28 | ||
29 | struct f_hidg_req_list { | ||
30 | struct usb_request *req; | ||
31 | unsigned int pos; | ||
32 | struct list_head list; | ||
33 | }; | ||
34 | |||
29 | struct f_hidg { | 35 | struct f_hidg { |
30 | /* configuration */ | 36 | /* configuration */ |
31 | unsigned char bInterfaceSubClass; | 37 | unsigned char bInterfaceSubClass; |
@@ -35,10 +41,10 @@ struct f_hidg { | |||
35 | unsigned short report_length; | 41 | unsigned short report_length; |
36 | 42 | ||
37 | /* recv report */ | 43 | /* recv report */ |
38 | char *set_report_buff; | 44 | struct list_head completed_out_req; |
39 | unsigned short set_report_length; | ||
40 | spinlock_t spinlock; | 45 | spinlock_t spinlock; |
41 | wait_queue_head_t read_queue; | 46 | wait_queue_head_t read_queue; |
47 | unsigned int qlen; | ||
42 | 48 | ||
43 | /* send report */ | 49 | /* send report */ |
44 | struct mutex lock; | 50 | struct mutex lock; |
@@ -49,7 +55,9 @@ struct f_hidg { | |||
49 | int minor; | 55 | int minor; |
50 | struct cdev cdev; | 56 | struct cdev cdev; |
51 | struct usb_function func; | 57 | struct usb_function func; |
58 | |||
52 | struct usb_ep *in_ep; | 59 | struct usb_ep *in_ep; |
60 | struct usb_ep *out_ep; | ||
53 | }; | 61 | }; |
54 | 62 | ||
55 | static inline struct f_hidg *func_to_hidg(struct usb_function *f) | 63 | static inline struct f_hidg *func_to_hidg(struct usb_function *f) |
@@ -65,7 +73,7 @@ static struct usb_interface_descriptor hidg_interface_desc = { | |||
65 | .bDescriptorType = USB_DT_INTERFACE, | 73 | .bDescriptorType = USB_DT_INTERFACE, |
66 | /* .bInterfaceNumber = DYNAMIC */ | 74 | /* .bInterfaceNumber = DYNAMIC */ |
67 | .bAlternateSetting = 0, | 75 | .bAlternateSetting = 0, |
68 | .bNumEndpoints = 1, | 76 | .bNumEndpoints = 2, |
69 | .bInterfaceClass = USB_CLASS_HID, | 77 | .bInterfaceClass = USB_CLASS_HID, |
70 | /* .bInterfaceSubClass = DYNAMIC */ | 78 | /* .bInterfaceSubClass = DYNAMIC */ |
71 | /* .bInterfaceProtocol = DYNAMIC */ | 79 | /* .bInterfaceProtocol = DYNAMIC */ |
@@ -96,10 +104,23 @@ static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = { | |||
96 | */ | 104 | */ |
97 | }; | 105 | }; |
98 | 106 | ||
107 | static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = { | ||
108 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
109 | .bDescriptorType = USB_DT_ENDPOINT, | ||
110 | .bEndpointAddress = USB_DIR_OUT, | ||
111 | .bmAttributes = USB_ENDPOINT_XFER_INT, | ||
112 | /*.wMaxPacketSize = DYNAMIC */ | ||
113 | .bInterval = 4, /* FIXME: Add this field in the | ||
114 | * HID gadget configuration? | ||
115 | * (struct hidg_func_descriptor) | ||
116 | */ | ||
117 | }; | ||
118 | |||
99 | static struct usb_descriptor_header *hidg_hs_descriptors[] = { | 119 | static struct usb_descriptor_header *hidg_hs_descriptors[] = { |
100 | (struct usb_descriptor_header *)&hidg_interface_desc, | 120 | (struct usb_descriptor_header *)&hidg_interface_desc, |
101 | (struct usb_descriptor_header *)&hidg_desc, | 121 | (struct usb_descriptor_header *)&hidg_desc, |
102 | (struct usb_descriptor_header *)&hidg_hs_in_ep_desc, | 122 | (struct usb_descriptor_header *)&hidg_hs_in_ep_desc, |
123 | (struct usb_descriptor_header *)&hidg_hs_out_ep_desc, | ||
103 | NULL, | 124 | NULL, |
104 | }; | 125 | }; |
105 | 126 | ||
@@ -117,10 +138,23 @@ static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = { | |||
117 | */ | 138 | */ |
118 | }; | 139 | }; |
119 | 140 | ||
141 | static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = { | ||
142 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
143 | .bDescriptorType = USB_DT_ENDPOINT, | ||
144 | .bEndpointAddress = USB_DIR_OUT, | ||
145 | .bmAttributes = USB_ENDPOINT_XFER_INT, | ||
146 | /*.wMaxPacketSize = DYNAMIC */ | ||
147 | .bInterval = 10, /* FIXME: Add this field in the | ||
148 | * HID gadget configuration? | ||
149 | * (struct hidg_func_descriptor) | ||
150 | */ | ||
151 | }; | ||
152 | |||
120 | static struct usb_descriptor_header *hidg_fs_descriptors[] = { | 153 | static struct usb_descriptor_header *hidg_fs_descriptors[] = { |
121 | (struct usb_descriptor_header *)&hidg_interface_desc, | 154 | (struct usb_descriptor_header *)&hidg_interface_desc, |
122 | (struct usb_descriptor_header *)&hidg_desc, | 155 | (struct usb_descriptor_header *)&hidg_desc, |
123 | (struct usb_descriptor_header *)&hidg_fs_in_ep_desc, | 156 | (struct usb_descriptor_header *)&hidg_fs_in_ep_desc, |
157 | (struct usb_descriptor_header *)&hidg_fs_out_ep_desc, | ||
124 | NULL, | 158 | NULL, |
125 | }; | 159 | }; |
126 | 160 | ||
@@ -130,9 +164,11 @@ static struct usb_descriptor_header *hidg_fs_descriptors[] = { | |||
130 | static ssize_t f_hidg_read(struct file *file, char __user *buffer, | 164 | static ssize_t f_hidg_read(struct file *file, char __user *buffer, |
131 | size_t count, loff_t *ptr) | 165 | size_t count, loff_t *ptr) |
132 | { | 166 | { |
133 | struct f_hidg *hidg = file->private_data; | 167 | struct f_hidg *hidg = file->private_data; |
134 | char *tmp_buff = NULL; | 168 | struct f_hidg_req_list *list; |
135 | unsigned long flags; | 169 | struct usb_request *req; |
170 | unsigned long flags; | ||
171 | int ret; | ||
136 | 172 | ||
137 | if (!count) | 173 | if (!count) |
138 | return 0; | 174 | return 0; |
@@ -142,8 +178,9 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer, | |||
142 | 178 | ||
143 | spin_lock_irqsave(&hidg->spinlock, flags); | 179 | spin_lock_irqsave(&hidg->spinlock, flags); |
144 | 180 | ||
145 | #define READ_COND (hidg->set_report_buff != NULL) | 181 | #define READ_COND (!list_empty(&hidg->completed_out_req)) |
146 | 182 | ||
183 | /* wait for at least one buffer to complete */ | ||
147 | while (!READ_COND) { | 184 | while (!READ_COND) { |
148 | spin_unlock_irqrestore(&hidg->spinlock, flags); | 185 | spin_unlock_irqrestore(&hidg->spinlock, flags); |
149 | if (file->f_flags & O_NONBLOCK) | 186 | if (file->f_flags & O_NONBLOCK) |
@@ -155,19 +192,34 @@ static ssize_t f_hidg_read(struct file *file, char __user *buffer, | |||
155 | spin_lock_irqsave(&hidg->spinlock, flags); | 192 | spin_lock_irqsave(&hidg->spinlock, flags); |
156 | } | 193 | } |
157 | 194 | ||
158 | 195 | /* pick the first one */ | |
159 | count = min_t(unsigned, count, hidg->set_report_length); | 196 | list = list_first_entry(&hidg->completed_out_req, |
160 | tmp_buff = hidg->set_report_buff; | 197 | struct f_hidg_req_list, list); |
161 | hidg->set_report_buff = NULL; | 198 | req = list->req; |
162 | 199 | count = min_t(unsigned int, count, req->actual - list->pos); | |
163 | spin_unlock_irqrestore(&hidg->spinlock, flags); | 200 | spin_unlock_irqrestore(&hidg->spinlock, flags); |
164 | 201 | ||
165 | if (tmp_buff != NULL) { | 202 | /* copy to user outside spinlock */ |
166 | /* copy to user outside spinlock */ | 203 | count -= copy_to_user(buffer, req->buf + list->pos, count); |
167 | count -= copy_to_user(buffer, tmp_buff, count); | 204 | list->pos += count; |
168 | kfree(tmp_buff); | 205 | |
169 | } else | 206 | /* |
170 | count = -ENOMEM; | 207 | * if this request is completely handled and transfered to |
208 | * userspace, remove its entry from the list and requeue it | ||
209 | * again. Otherwise, we will revisit it again upon the next | ||
210 | * call, taking into account its current read position. | ||
211 | */ | ||
212 | if (list->pos == req->actual) { | ||
213 | spin_lock_irqsave(&hidg->spinlock, flags); | ||
214 | list_del(&list->list); | ||
215 | kfree(list); | ||
216 | spin_unlock_irqrestore(&hidg->spinlock, flags); | ||
217 | |||
218 | req->length = hidg->report_length; | ||
219 | ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL); | ||
220 | if (ret < 0) | ||
221 | return ret; | ||
222 | } | ||
171 | 223 | ||
172 | return count; | 224 | return count; |
173 | } | 225 | } |
@@ -282,28 +334,37 @@ static int f_hidg_open(struct inode *inode, struct file *fd) | |||
282 | /*-------------------------------------------------------------------------*/ | 334 | /*-------------------------------------------------------------------------*/ |
283 | /* usb_function */ | 335 | /* usb_function */ |
284 | 336 | ||
285 | static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req) | 337 | static struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep, unsigned length) |
286 | { | 338 | { |
287 | struct f_hidg *hidg = (struct f_hidg *)req->context; | 339 | struct usb_request *req; |
288 | 340 | ||
289 | if (req->status != 0 || req->buf == NULL || req->actual == 0) { | 341 | req = usb_ep_alloc_request(ep, GFP_ATOMIC); |
290 | ERROR(hidg->func.config->cdev, "%s FAILED\n", __func__); | 342 | if (req) { |
291 | return; | 343 | req->length = length; |
344 | req->buf = kmalloc(length, GFP_ATOMIC); | ||
345 | if (!req->buf) { | ||
346 | usb_ep_free_request(ep, req); | ||
347 | req = NULL; | ||
348 | } | ||
292 | } | 349 | } |
350 | return req; | ||
351 | } | ||
293 | 352 | ||
294 | spin_lock(&hidg->spinlock); | 353 | static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req) |
295 | 354 | { | |
296 | hidg->set_report_buff = krealloc(hidg->set_report_buff, | 355 | struct f_hidg *hidg = (struct f_hidg *) req->context; |
297 | req->actual, GFP_ATOMIC); | 356 | struct f_hidg_req_list *req_list; |
357 | unsigned long flags; | ||
298 | 358 | ||
299 | if (hidg->set_report_buff == NULL) { | 359 | req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC); |
300 | spin_unlock(&hidg->spinlock); | 360 | if (!req_list) |
301 | return; | 361 | return; |
302 | } | ||
303 | hidg->set_report_length = req->actual; | ||
304 | memcpy(hidg->set_report_buff, req->buf, req->actual); | ||
305 | 362 | ||
306 | spin_unlock(&hidg->spinlock); | 363 | req_list->req = req; |
364 | |||
365 | spin_lock_irqsave(&hidg->spinlock, flags); | ||
366 | list_add_tail(&req_list->list, &hidg->completed_out_req); | ||
367 | spin_unlock_irqrestore(&hidg->spinlock, flags); | ||
307 | 368 | ||
308 | wake_up(&hidg->read_queue); | 369 | wake_up(&hidg->read_queue); |
309 | } | 370 | } |
@@ -344,9 +405,7 @@ static int hidg_setup(struct usb_function *f, | |||
344 | case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 | 405 | case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 |
345 | | HID_REQ_SET_REPORT): | 406 | | HID_REQ_SET_REPORT): |
346 | VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength); | 407 | VDBG(cdev, "set_report | wLenght=%d\n", ctrl->wLength); |
347 | req->context = hidg; | 408 | goto stall; |
348 | req->complete = hidg_set_report_complete; | ||
349 | goto respond; | ||
350 | break; | 409 | break; |
351 | 410 | ||
352 | case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 | 411 | case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 |
@@ -403,16 +462,25 @@ respond: | |||
403 | static void hidg_disable(struct usb_function *f) | 462 | static void hidg_disable(struct usb_function *f) |
404 | { | 463 | { |
405 | struct f_hidg *hidg = func_to_hidg(f); | 464 | struct f_hidg *hidg = func_to_hidg(f); |
465 | struct f_hidg_req_list *list, *next; | ||
406 | 466 | ||
407 | usb_ep_disable(hidg->in_ep); | 467 | usb_ep_disable(hidg->in_ep); |
408 | hidg->in_ep->driver_data = NULL; | 468 | hidg->in_ep->driver_data = NULL; |
469 | |||
470 | usb_ep_disable(hidg->out_ep); | ||
471 | hidg->out_ep->driver_data = NULL; | ||
472 | |||
473 | list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) { | ||
474 | list_del(&list->list); | ||
475 | kfree(list); | ||
476 | } | ||
409 | } | 477 | } |
410 | 478 | ||
411 | static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) | 479 | static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) |
412 | { | 480 | { |
413 | struct usb_composite_dev *cdev = f->config->cdev; | 481 | struct usb_composite_dev *cdev = f->config->cdev; |
414 | struct f_hidg *hidg = func_to_hidg(f); | 482 | struct f_hidg *hidg = func_to_hidg(f); |
415 | int status = 0; | 483 | int i, status = 0; |
416 | 484 | ||
417 | VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt); | 485 | VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt); |
418 | 486 | ||
@@ -429,11 +497,55 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) | |||
429 | } | 497 | } |
430 | status = usb_ep_enable(hidg->in_ep); | 498 | status = usb_ep_enable(hidg->in_ep); |
431 | if (status < 0) { | 499 | if (status < 0) { |
432 | ERROR(cdev, "Enable endpoint FAILED!\n"); | 500 | ERROR(cdev, "Enable IN endpoint FAILED!\n"); |
433 | goto fail; | 501 | goto fail; |
434 | } | 502 | } |
435 | hidg->in_ep->driver_data = hidg; | 503 | hidg->in_ep->driver_data = hidg; |
436 | } | 504 | } |
505 | |||
506 | |||
507 | if (hidg->out_ep != NULL) { | ||
508 | /* restart endpoint */ | ||
509 | if (hidg->out_ep->driver_data != NULL) | ||
510 | usb_ep_disable(hidg->out_ep); | ||
511 | |||
512 | status = config_ep_by_speed(f->config->cdev->gadget, f, | ||
513 | hidg->out_ep); | ||
514 | if (status) { | ||
515 | ERROR(cdev, "config_ep_by_speed FAILED!\n"); | ||
516 | goto fail; | ||
517 | } | ||
518 | status = usb_ep_enable(hidg->out_ep); | ||
519 | if (status < 0) { | ||
520 | ERROR(cdev, "Enable IN endpoint FAILED!\n"); | ||
521 | goto fail; | ||
522 | } | ||
523 | hidg->out_ep->driver_data = hidg; | ||
524 | |||
525 | /* | ||
526 | * allocate a bunch of read buffers and queue them all at once. | ||
527 | */ | ||
528 | for (i = 0; i < hidg->qlen && status == 0; i++) { | ||
529 | struct usb_request *req = | ||
530 | hidg_alloc_ep_req(hidg->out_ep, | ||
531 | hidg->report_length); | ||
532 | if (req) { | ||
533 | req->complete = hidg_set_report_complete; | ||
534 | req->context = hidg; | ||
535 | status = usb_ep_queue(hidg->out_ep, req, | ||
536 | GFP_ATOMIC); | ||
537 | if (status) | ||
538 | ERROR(cdev, "%s queue req --> %d\n", | ||
539 | hidg->out_ep->name, status); | ||
540 | } else { | ||
541 | usb_ep_disable(hidg->out_ep); | ||
542 | hidg->out_ep->driver_data = NULL; | ||
543 | status = -ENOMEM; | ||
544 | goto fail; | ||
545 | } | ||
546 | } | ||
547 | } | ||
548 | |||
437 | fail: | 549 | fail: |
438 | return status; | 550 | return status; |
439 | } | 551 | } |
@@ -470,13 +582,18 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) | |||
470 | ep->driver_data = c->cdev; /* claim */ | 582 | ep->driver_data = c->cdev; /* claim */ |
471 | hidg->in_ep = ep; | 583 | hidg->in_ep = ep; |
472 | 584 | ||
585 | ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc); | ||
586 | if (!ep) | ||
587 | goto fail; | ||
588 | ep->driver_data = c->cdev; /* claim */ | ||
589 | hidg->out_ep = ep; | ||
590 | |||
473 | /* preallocate request and buffer */ | 591 | /* preallocate request and buffer */ |
474 | status = -ENOMEM; | 592 | status = -ENOMEM; |
475 | hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL); | 593 | hidg->req = usb_ep_alloc_request(hidg->in_ep, GFP_KERNEL); |
476 | if (!hidg->req) | 594 | if (!hidg->req) |
477 | goto fail; | 595 | goto fail; |
478 | 596 | ||
479 | |||
480 | hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL); | 597 | hidg->req->buf = kmalloc(hidg->report_length, GFP_KERNEL); |
481 | if (!hidg->req->buf) | 598 | if (!hidg->req->buf) |
482 | goto fail; | 599 | goto fail; |
@@ -486,12 +603,12 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) | |||
486 | hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol; | 603 | hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol; |
487 | hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); | 604 | hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); |
488 | hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); | 605 | hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); |
606 | hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); | ||
607 | hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); | ||
489 | hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT; | 608 | hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT; |
490 | hidg_desc.desc[0].wDescriptorLength = | 609 | hidg_desc.desc[0].wDescriptorLength = |
491 | cpu_to_le16(hidg->report_desc_length); | 610 | cpu_to_le16(hidg->report_desc_length); |
492 | 611 | ||
493 | hidg->set_report_buff = NULL; | ||
494 | |||
495 | /* copy descriptors */ | 612 | /* copy descriptors */ |
496 | f->descriptors = usb_copy_descriptors(hidg_fs_descriptors); | 613 | f->descriptors = usb_copy_descriptors(hidg_fs_descriptors); |
497 | if (!f->descriptors) | 614 | if (!f->descriptors) |
@@ -500,6 +617,8 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) | |||
500 | if (gadget_is_dualspeed(c->cdev->gadget)) { | 617 | if (gadget_is_dualspeed(c->cdev->gadget)) { |
501 | hidg_hs_in_ep_desc.bEndpointAddress = | 618 | hidg_hs_in_ep_desc.bEndpointAddress = |
502 | hidg_fs_in_ep_desc.bEndpointAddress; | 619 | hidg_fs_in_ep_desc.bEndpointAddress; |
620 | hidg_hs_out_ep_desc.bEndpointAddress = | ||
621 | hidg_fs_out_ep_desc.bEndpointAddress; | ||
503 | f->hs_descriptors = usb_copy_descriptors(hidg_hs_descriptors); | 622 | f->hs_descriptors = usb_copy_descriptors(hidg_hs_descriptors); |
504 | if (!f->hs_descriptors) | 623 | if (!f->hs_descriptors) |
505 | goto fail; | 624 | goto fail; |
@@ -509,6 +628,7 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f) | |||
509 | spin_lock_init(&hidg->spinlock); | 628 | spin_lock_init(&hidg->spinlock); |
510 | init_waitqueue_head(&hidg->write_queue); | 629 | init_waitqueue_head(&hidg->write_queue); |
511 | init_waitqueue_head(&hidg->read_queue); | 630 | init_waitqueue_head(&hidg->read_queue); |
631 | INIT_LIST_HEAD(&hidg->completed_out_req); | ||
512 | 632 | ||
513 | /* create char device */ | 633 | /* create char device */ |
514 | cdev_init(&hidg->cdev, &f_hidg_fops); | 634 | cdev_init(&hidg->cdev, &f_hidg_fops); |
@@ -553,7 +673,6 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) | |||
553 | usb_free_descriptors(f->descriptors); | 673 | usb_free_descriptors(f->descriptors); |
554 | 674 | ||
555 | kfree(hidg->report_desc); | 675 | kfree(hidg->report_desc); |
556 | kfree(hidg->set_report_buff); | ||
557 | kfree(hidg); | 676 | kfree(hidg); |
558 | } | 677 | } |
559 | 678 | ||
@@ -624,6 +743,9 @@ int __init hidg_bind_config(struct usb_configuration *c, | |||
624 | hidg->func.disable = hidg_disable; | 743 | hidg->func.disable = hidg_disable; |
625 | hidg->func.setup = hidg_setup; | 744 | hidg->func.setup = hidg_setup; |
626 | 745 | ||
746 | /* this could me made configurable at some point */ | ||
747 | hidg->qlen = 4; | ||
748 | |||
627 | status = usb_add_function(c, &hidg->func); | 749 | status = usb_add_function(c, &hidg->func); |
628 | if (status) | 750 | if (status) |
629 | kfree(hidg); | 751 | kfree(hidg); |