]>
Commit | Line | Data |
---|---|---|
5fd54ace | 1 | // SPDX-License-Identifier: GPL-2.0+ |
71adf118 FC |
2 | /* |
3 | * f_hid.c -- USB HID function driver | |
4 | * | |
5 | * Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.com> | |
71adf118 FC |
6 | */ |
7 | ||
8 | #include <linux/kernel.h> | |
71adf118 FC |
9 | #include <linux/module.h> |
10 | #include <linux/hid.h> | |
cb382536 | 11 | #include <linux/idr.h> |
71adf118 FC |
12 | #include <linux/cdev.h> |
13 | #include <linux/mutex.h> | |
14 | #include <linux/poll.h> | |
71adf118 FC |
15 | #include <linux/uaccess.h> |
16 | #include <linux/wait.h> | |
721e2e91 | 17 | #include <linux/sched.h> |
71adf118 FC |
18 | #include <linux/usb/g_hid.h> |
19 | ||
1efd54ea | 20 | #include "u_f.h" |
cb382536 AP |
21 | #include "u_hid.h" |
22 | ||
23 | #define HIDG_MINORS 4 | |
1efd54ea | 24 | |
71adf118 FC |
25 | static int major, minors; |
26 | static struct class *hidg_class; | |
cb382536 AP |
27 | static DEFINE_IDA(hidg_ida); |
28 | static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */ | |
71adf118 FC |
29 | |
30 | /*-------------------------------------------------------------------------*/ | |
31 | /* HID gadget struct */ | |
32 | ||
99c51500 DM |
33 | struct f_hidg_req_list { |
34 | struct usb_request *req; | |
35 | unsigned int pos; | |
36 | struct list_head list; | |
37 | }; | |
38 | ||
71adf118 FC |
39 | struct f_hidg { |
40 | /* configuration */ | |
41 | unsigned char bInterfaceSubClass; | |
42 | unsigned char bInterfaceProtocol; | |
b3c4ec71 | 43 | unsigned char protocol; |
71adf118 FC |
44 | unsigned short report_desc_length; |
45 | char *report_desc; | |
46 | unsigned short report_length; | |
47 | ||
48 | /* recv report */ | |
99c51500 | 49 | struct list_head completed_out_req; |
33e4c1a9 | 50 | spinlock_t read_spinlock; |
71adf118 | 51 | wait_queue_head_t read_queue; |
99c51500 | 52 | unsigned int qlen; |
71adf118 FC |
53 | |
54 | /* send report */ | |
33e4c1a9 | 55 | spinlock_t write_spinlock; |
71adf118 FC |
56 | bool write_pending; |
57 | wait_queue_head_t write_queue; | |
58 | struct usb_request *req; | |
59 | ||
60 | int minor; | |
61 | struct cdev cdev; | |
62 | struct usb_function func; | |
99c51500 | 63 | |
71adf118 | 64 | struct usb_ep *in_ep; |
99c51500 | 65 | struct usb_ep *out_ep; |
71adf118 FC |
66 | }; |
67 | ||
68 | static inline struct f_hidg *func_to_hidg(struct usb_function *f) | |
69 | { | |
70 | return container_of(f, struct f_hidg, func); | |
71 | } | |
72 | ||
73 | /*-------------------------------------------------------------------------*/ | |
74 | /* Static descriptors */ | |
75 | ||
76 | static struct usb_interface_descriptor hidg_interface_desc = { | |
77 | .bLength = sizeof hidg_interface_desc, | |
78 | .bDescriptorType = USB_DT_INTERFACE, | |
79 | /* .bInterfaceNumber = DYNAMIC */ | |
80 | .bAlternateSetting = 0, | |
99c51500 | 81 | .bNumEndpoints = 2, |
71adf118 FC |
82 | .bInterfaceClass = USB_CLASS_HID, |
83 | /* .bInterfaceSubClass = DYNAMIC */ | |
84 | /* .bInterfaceProtocol = DYNAMIC */ | |
85 | /* .iInterface = DYNAMIC */ | |
86 | }; | |
87 | ||
88 | static struct hid_descriptor hidg_desc = { | |
89 | .bLength = sizeof hidg_desc, | |
90 | .bDescriptorType = HID_DT_HID, | |
91 | .bcdHID = 0x0101, | |
92 | .bCountryCode = 0x00, | |
93 | .bNumDescriptors = 0x1, | |
94 | /*.desc[0].bDescriptorType = DYNAMIC */ | |
95 | /*.desc[0].wDescriptorLenght = DYNAMIC */ | |
96 | }; | |
97 | ||
dbf499cf JD |
98 | /* Super-Speed Support */ |
99 | ||
100 | static struct usb_endpoint_descriptor hidg_ss_in_ep_desc = { | |
101 | .bLength = USB_DT_ENDPOINT_SIZE, | |
102 | .bDescriptorType = USB_DT_ENDPOINT, | |
103 | .bEndpointAddress = USB_DIR_IN, | |
104 | .bmAttributes = USB_ENDPOINT_XFER_INT, | |
105 | /*.wMaxPacketSize = DYNAMIC */ | |
106 | .bInterval = 4, /* FIXME: Add this field in the | |
107 | * HID gadget configuration? | |
108 | * (struct hidg_func_descriptor) | |
109 | */ | |
110 | }; | |
111 | ||
112 | static struct usb_ss_ep_comp_descriptor hidg_ss_in_comp_desc = { | |
113 | .bLength = sizeof(hidg_ss_in_comp_desc), | |
114 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, | |
115 | ||
116 | /* .bMaxBurst = 0, */ | |
117 | /* .bmAttributes = 0, */ | |
118 | /* .wBytesPerInterval = DYNAMIC */ | |
119 | }; | |
120 | ||
121 | static struct usb_endpoint_descriptor hidg_ss_out_ep_desc = { | |
122 | .bLength = USB_DT_ENDPOINT_SIZE, | |
123 | .bDescriptorType = USB_DT_ENDPOINT, | |
124 | .bEndpointAddress = USB_DIR_OUT, | |
125 | .bmAttributes = USB_ENDPOINT_XFER_INT, | |
126 | /*.wMaxPacketSize = DYNAMIC */ | |
127 | .bInterval = 4, /* FIXME: Add this field in the | |
128 | * HID gadget configuration? | |
129 | * (struct hidg_func_descriptor) | |
130 | */ | |
131 | }; | |
132 | ||
133 | static struct usb_ss_ep_comp_descriptor hidg_ss_out_comp_desc = { | |
134 | .bLength = sizeof(hidg_ss_out_comp_desc), | |
135 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, | |
136 | ||
137 | /* .bMaxBurst = 0, */ | |
138 | /* .bmAttributes = 0, */ | |
139 | /* .wBytesPerInterval = DYNAMIC */ | |
140 | }; | |
141 | ||
142 | static struct usb_descriptor_header *hidg_ss_descriptors[] = { | |
143 | (struct usb_descriptor_header *)&hidg_interface_desc, | |
144 | (struct usb_descriptor_header *)&hidg_desc, | |
145 | (struct usb_descriptor_header *)&hidg_ss_in_ep_desc, | |
146 | (struct usb_descriptor_header *)&hidg_ss_in_comp_desc, | |
147 | (struct usb_descriptor_header *)&hidg_ss_out_ep_desc, | |
148 | (struct usb_descriptor_header *)&hidg_ss_out_comp_desc, | |
149 | NULL, | |
150 | }; | |
151 | ||
71adf118 FC |
152 | /* High-Speed Support */ |
153 | ||
154 | static struct usb_endpoint_descriptor hidg_hs_in_ep_desc = { | |
155 | .bLength = USB_DT_ENDPOINT_SIZE, | |
156 | .bDescriptorType = USB_DT_ENDPOINT, | |
157 | .bEndpointAddress = USB_DIR_IN, | |
158 | .bmAttributes = USB_ENDPOINT_XFER_INT, | |
159 | /*.wMaxPacketSize = DYNAMIC */ | |
160 | .bInterval = 4, /* FIXME: Add this field in the | |
161 | * HID gadget configuration? | |
162 | * (struct hidg_func_descriptor) | |
163 | */ | |
164 | }; | |
165 | ||
99c51500 DM |
166 | static struct usb_endpoint_descriptor hidg_hs_out_ep_desc = { |
167 | .bLength = USB_DT_ENDPOINT_SIZE, | |
168 | .bDescriptorType = USB_DT_ENDPOINT, | |
169 | .bEndpointAddress = USB_DIR_OUT, | |
170 | .bmAttributes = USB_ENDPOINT_XFER_INT, | |
171 | /*.wMaxPacketSize = DYNAMIC */ | |
172 | .bInterval = 4, /* FIXME: Add this field in the | |
173 | * HID gadget configuration? | |
174 | * (struct hidg_func_descriptor) | |
175 | */ | |
176 | }; | |
177 | ||
71adf118 FC |
178 | static struct usb_descriptor_header *hidg_hs_descriptors[] = { |
179 | (struct usb_descriptor_header *)&hidg_interface_desc, | |
180 | (struct usb_descriptor_header *)&hidg_desc, | |
181 | (struct usb_descriptor_header *)&hidg_hs_in_ep_desc, | |
99c51500 | 182 | (struct usb_descriptor_header *)&hidg_hs_out_ep_desc, |
71adf118 FC |
183 | NULL, |
184 | }; | |
185 | ||
186 | /* Full-Speed Support */ | |
187 | ||
188 | static struct usb_endpoint_descriptor hidg_fs_in_ep_desc = { | |
189 | .bLength = USB_DT_ENDPOINT_SIZE, | |
190 | .bDescriptorType = USB_DT_ENDPOINT, | |
191 | .bEndpointAddress = USB_DIR_IN, | |
192 | .bmAttributes = USB_ENDPOINT_XFER_INT, | |
193 | /*.wMaxPacketSize = DYNAMIC */ | |
194 | .bInterval = 10, /* FIXME: Add this field in the | |
195 | * HID gadget configuration? | |
196 | * (struct hidg_func_descriptor) | |
197 | */ | |
198 | }; | |
199 | ||
99c51500 DM |
200 | static struct usb_endpoint_descriptor hidg_fs_out_ep_desc = { |
201 | .bLength = USB_DT_ENDPOINT_SIZE, | |
202 | .bDescriptorType = USB_DT_ENDPOINT, | |
203 | .bEndpointAddress = USB_DIR_OUT, | |
204 | .bmAttributes = USB_ENDPOINT_XFER_INT, | |
205 | /*.wMaxPacketSize = DYNAMIC */ | |
206 | .bInterval = 10, /* FIXME: Add this field in the | |
207 | * HID gadget configuration? | |
208 | * (struct hidg_func_descriptor) | |
209 | */ | |
210 | }; | |
211 | ||
71adf118 FC |
212 | static struct usb_descriptor_header *hidg_fs_descriptors[] = { |
213 | (struct usb_descriptor_header *)&hidg_interface_desc, | |
214 | (struct usb_descriptor_header *)&hidg_desc, | |
215 | (struct usb_descriptor_header *)&hidg_fs_in_ep_desc, | |
99c51500 | 216 | (struct usb_descriptor_header *)&hidg_fs_out_ep_desc, |
71adf118 FC |
217 | NULL, |
218 | }; | |
219 | ||
cb382536 AP |
220 | /*-------------------------------------------------------------------------*/ |
221 | /* Strings */ | |
222 | ||
223 | #define CT_FUNC_HID_IDX 0 | |
224 | ||
225 | static struct usb_string ct_func_string_defs[] = { | |
226 | [CT_FUNC_HID_IDX].s = "HID Interface", | |
227 | {}, /* end of list */ | |
228 | }; | |
229 | ||
230 | static struct usb_gadget_strings ct_func_string_table = { | |
231 | .language = 0x0409, /* en-US */ | |
232 | .strings = ct_func_string_defs, | |
233 | }; | |
234 | ||
235 | static struct usb_gadget_strings *ct_func_strings[] = { | |
236 | &ct_func_string_table, | |
237 | NULL, | |
238 | }; | |
239 | ||
71adf118 FC |
240 | /*-------------------------------------------------------------------------*/ |
241 | /* Char Device */ | |
242 | ||
243 | static ssize_t f_hidg_read(struct file *file, char __user *buffer, | |
244 | size_t count, loff_t *ptr) | |
245 | { | |
99c51500 DM |
246 | struct f_hidg *hidg = file->private_data; |
247 | struct f_hidg_req_list *list; | |
248 | struct usb_request *req; | |
249 | unsigned long flags; | |
250 | int ret; | |
71adf118 FC |
251 | |
252 | if (!count) | |
253 | return 0; | |
254 | ||
255 | if (!access_ok(VERIFY_WRITE, buffer, count)) | |
256 | return -EFAULT; | |
257 | ||
33e4c1a9 | 258 | spin_lock_irqsave(&hidg->read_spinlock, flags); |
71adf118 | 259 | |
99c51500 | 260 | #define READ_COND (!list_empty(&hidg->completed_out_req)) |
71adf118 | 261 | |
99c51500 | 262 | /* wait for at least one buffer to complete */ |
71adf118 | 263 | while (!READ_COND) { |
33e4c1a9 | 264 | spin_unlock_irqrestore(&hidg->read_spinlock, flags); |
71adf118 FC |
265 | if (file->f_flags & O_NONBLOCK) |
266 | return -EAGAIN; | |
267 | ||
268 | if (wait_event_interruptible(hidg->read_queue, READ_COND)) | |
269 | return -ERESTARTSYS; | |
270 | ||
33e4c1a9 | 271 | spin_lock_irqsave(&hidg->read_spinlock, flags); |
71adf118 FC |
272 | } |
273 | ||
99c51500 DM |
274 | /* pick the first one */ |
275 | list = list_first_entry(&hidg->completed_out_req, | |
276 | struct f_hidg_req_list, list); | |
aa65d11a KO |
277 | |
278 | /* | |
279 | * Remove this from list to protect it from beign free() | |
280 | * while host disables our function | |
281 | */ | |
282 | list_del(&list->list); | |
283 | ||
99c51500 DM |
284 | req = list->req; |
285 | count = min_t(unsigned int, count, req->actual - list->pos); | |
33e4c1a9 | 286 | spin_unlock_irqrestore(&hidg->read_spinlock, flags); |
71adf118 | 287 | |
99c51500 DM |
288 | /* copy to user outside spinlock */ |
289 | count -= copy_to_user(buffer, req->buf + list->pos, count); | |
290 | list->pos += count; | |
291 | ||
292 | /* | |
293 | * if this request is completely handled and transfered to | |
294 | * userspace, remove its entry from the list and requeue it | |
295 | * again. Otherwise, we will revisit it again upon the next | |
296 | * call, taking into account its current read position. | |
297 | */ | |
298 | if (list->pos == req->actual) { | |
99c51500 | 299 | kfree(list); |
99c51500 DM |
300 | |
301 | req->length = hidg->report_length; | |
302 | ret = usb_ep_queue(hidg->out_ep, req, GFP_KERNEL); | |
aa65d11a KO |
303 | if (ret < 0) { |
304 | free_ep_req(hidg->out_ep, req); | |
99c51500 | 305 | return ret; |
aa65d11a KO |
306 | } |
307 | } else { | |
33e4c1a9 | 308 | spin_lock_irqsave(&hidg->read_spinlock, flags); |
aa65d11a | 309 | list_add(&list->list, &hidg->completed_out_req); |
33e4c1a9 | 310 | spin_unlock_irqrestore(&hidg->read_spinlock, flags); |
aa65d11a KO |
311 | |
312 | wake_up(&hidg->read_queue); | |
99c51500 | 313 | } |
71adf118 FC |
314 | |
315 | return count; | |
316 | } | |
317 | ||
318 | static void f_hidg_req_complete(struct usb_ep *ep, struct usb_request *req) | |
319 | { | |
320 | struct f_hidg *hidg = (struct f_hidg *)ep->driver_data; | |
33e4c1a9 | 321 | unsigned long flags; |
71adf118 FC |
322 | |
323 | if (req->status != 0) { | |
324 | ERROR(hidg->func.config->cdev, | |
325 | "End Point Request ERROR: %d\n", req->status); | |
326 | } | |
327 | ||
33e4c1a9 | 328 | spin_lock_irqsave(&hidg->write_spinlock, flags); |
71adf118 | 329 | hidg->write_pending = 0; |
33e4c1a9 | 330 | spin_unlock_irqrestore(&hidg->write_spinlock, flags); |
71adf118 FC |
331 | wake_up(&hidg->write_queue); |
332 | } | |
333 | ||
334 | static ssize_t f_hidg_write(struct file *file, const char __user *buffer, | |
335 | size_t count, loff_t *offp) | |
336 | { | |
fd63b10b | 337 | struct f_hidg *hidg = file->private_data; |
749494b6 | 338 | struct usb_request *req; |
33e4c1a9 | 339 | unsigned long flags; |
71adf118 FC |
340 | ssize_t status = -ENOMEM; |
341 | ||
342 | if (!access_ok(VERIFY_READ, buffer, count)) | |
343 | return -EFAULT; | |
344 | ||
33e4c1a9 | 345 | spin_lock_irqsave(&hidg->write_spinlock, flags); |
71adf118 FC |
346 | |
347 | #define WRITE_COND (!hidg->write_pending) | |
749494b6 | 348 | try_again: |
71adf118 FC |
349 | /* write queue */ |
350 | while (!WRITE_COND) { | |
33e4c1a9 | 351 | spin_unlock_irqrestore(&hidg->write_spinlock, flags); |
71adf118 FC |
352 | if (file->f_flags & O_NONBLOCK) |
353 | return -EAGAIN; | |
354 | ||
355 | if (wait_event_interruptible_exclusive( | |
356 | hidg->write_queue, WRITE_COND)) | |
357 | return -ERESTARTSYS; | |
358 | ||
33e4c1a9 | 359 | spin_lock_irqsave(&hidg->write_spinlock, flags); |
71adf118 FC |
360 | } |
361 | ||
33e4c1a9 | 362 | hidg->write_pending = 1; |
749494b6 | 363 | req = hidg->req; |
71adf118 | 364 | count = min_t(unsigned, count, hidg->report_length); |
33e4c1a9 KO |
365 | |
366 | spin_unlock_irqrestore(&hidg->write_spinlock, flags); | |
25cd9721 | 367 | status = copy_from_user(req->buf, buffer, count); |
71adf118 FC |
368 | |
369 | if (status != 0) { | |
370 | ERROR(hidg->func.config->cdev, | |
371 | "copy_from_user error\n"); | |
33e4c1a9 KO |
372 | status = -EINVAL; |
373 | goto release_write_pending; | |
71adf118 FC |
374 | } |
375 | ||
749494b6 KO |
376 | spin_lock_irqsave(&hidg->write_spinlock, flags); |
377 | ||
25cd9721 | 378 | /* when our function has been disabled by host */ |
749494b6 | 379 | if (!hidg->req) { |
25cd9721 | 380 | free_ep_req(hidg->in_ep, req); |
749494b6 KO |
381 | /* |
382 | * TODO | |
383 | * Should we fail with error here? | |
384 | */ | |
385 | goto try_again; | |
386 | } | |
387 | ||
388 | req->status = 0; | |
389 | req->zero = 0; | |
390 | req->length = count; | |
391 | req->complete = f_hidg_req_complete; | |
392 | req->context = hidg; | |
71adf118 | 393 | |
25cd9721 | 394 | status = usb_ep_queue(hidg->in_ep, req, GFP_ATOMIC); |
71adf118 FC |
395 | if (status < 0) { |
396 | ERROR(hidg->func.config->cdev, | |
397 | "usb_ep_queue error on int endpoint %zd\n", status); | |
749494b6 | 398 | goto release_write_pending_unlocked; |
71adf118 FC |
399 | } else { |
400 | status = count; | |
401 | } | |
749494b6 | 402 | spin_unlock_irqrestore(&hidg->write_spinlock, flags); |
71adf118 | 403 | |
33e4c1a9 KO |
404 | return status; |
405 | release_write_pending: | |
406 | spin_lock_irqsave(&hidg->write_spinlock, flags); | |
749494b6 | 407 | release_write_pending_unlocked: |
33e4c1a9 KO |
408 | hidg->write_pending = 0; |
409 | spin_unlock_irqrestore(&hidg->write_spinlock, flags); | |
410 | ||
411 | wake_up(&hidg->write_queue); | |
71adf118 FC |
412 | |
413 | return status; | |
414 | } | |
415 | ||
afc9a42b | 416 | static __poll_t f_hidg_poll(struct file *file, poll_table *wait) |
71adf118 | 417 | { |
fd63b10b | 418 | struct f_hidg *hidg = file->private_data; |
afc9a42b | 419 | __poll_t ret = 0; |
71adf118 FC |
420 | |
421 | poll_wait(file, &hidg->read_queue, wait); | |
422 | poll_wait(file, &hidg->write_queue, wait); | |
423 | ||
424 | if (WRITE_COND) | |
425 | ret |= POLLOUT | POLLWRNORM; | |
426 | ||
427 | if (READ_COND) | |
428 | ret |= POLLIN | POLLRDNORM; | |
429 | ||
430 | return ret; | |
431 | } | |
432 | ||
433 | #undef WRITE_COND | |
434 | #undef READ_COND | |
435 | ||
436 | static int f_hidg_release(struct inode *inode, struct file *fd) | |
437 | { | |
438 | fd->private_data = NULL; | |
439 | return 0; | |
440 | } | |
441 | ||
442 | static int f_hidg_open(struct inode *inode, struct file *fd) | |
443 | { | |
444 | struct f_hidg *hidg = | |
445 | container_of(inode->i_cdev, struct f_hidg, cdev); | |
446 | ||
447 | fd->private_data = hidg; | |
448 | ||
449 | return 0; | |
450 | } | |
451 | ||
452 | /*-------------------------------------------------------------------------*/ | |
453 | /* usb_function */ | |
454 | ||
1efd54ea AP |
455 | static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep, |
456 | unsigned length) | |
71adf118 | 457 | { |
aadbe812 | 458 | return alloc_ep_req(ep, length); |
99c51500 | 459 | } |
71adf118 | 460 | |
99c51500 DM |
461 | static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req) |
462 | { | |
463 | struct f_hidg *hidg = (struct f_hidg *) req->context; | |
20d2ca95 | 464 | struct usb_composite_dev *cdev = hidg->func.config->cdev; |
99c51500 DM |
465 | struct f_hidg_req_list *req_list; |
466 | unsigned long flags; | |
71adf118 | 467 | |
20d2ca95 KO |
468 | switch (req->status) { |
469 | case 0: | |
470 | req_list = kzalloc(sizeof(*req_list), GFP_ATOMIC); | |
471 | if (!req_list) { | |
472 | ERROR(cdev, "Unable to allocate mem for req_list\n"); | |
473 | goto free_req; | |
474 | } | |
71adf118 | 475 | |
20d2ca95 | 476 | req_list->req = req; |
99c51500 | 477 | |
33e4c1a9 | 478 | spin_lock_irqsave(&hidg->read_spinlock, flags); |
20d2ca95 | 479 | list_add_tail(&req_list->list, &hidg->completed_out_req); |
33e4c1a9 | 480 | spin_unlock_irqrestore(&hidg->read_spinlock, flags); |
71adf118 | 481 | |
20d2ca95 KO |
482 | wake_up(&hidg->read_queue); |
483 | break; | |
484 | default: | |
485 | ERROR(cdev, "Set report failed %d\n", req->status); | |
486 | /* FALLTHROUGH */ | |
487 | case -ECONNABORTED: /* hardware forced ep reset */ | |
488 | case -ECONNRESET: /* request dequeued */ | |
489 | case -ESHUTDOWN: /* disconnect from host */ | |
490 | free_req: | |
491 | free_ep_req(ep, req); | |
492 | return; | |
493 | } | |
71adf118 FC |
494 | } |
495 | ||
496 | static int hidg_setup(struct usb_function *f, | |
497 | const struct usb_ctrlrequest *ctrl) | |
498 | { | |
499 | struct f_hidg *hidg = func_to_hidg(f); | |
500 | struct usb_composite_dev *cdev = f->config->cdev; | |
501 | struct usb_request *req = cdev->req; | |
502 | int status = 0; | |
503 | __u16 value, length; | |
504 | ||
505 | value = __le16_to_cpu(ctrl->wValue); | |
506 | length = __le16_to_cpu(ctrl->wLength); | |
507 | ||
c9b3bde0 JL |
508 | VDBG(cdev, |
509 | "%s crtl_request : bRequestType:0x%x bRequest:0x%x Value:0x%x\n", | |
510 | __func__, ctrl->bRequestType, ctrl->bRequest, value); | |
71adf118 FC |
511 | |
512 | switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { | |
513 | case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 | |
514 | | HID_REQ_GET_REPORT): | |
515 | VDBG(cdev, "get_report\n"); | |
516 | ||
517 | /* send an empty report */ | |
518 | length = min_t(unsigned, length, hidg->report_length); | |
519 | memset(req->buf, 0x0, length); | |
520 | ||
521 | goto respond; | |
522 | break; | |
523 | ||
524 | case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 | |
525 | | HID_REQ_GET_PROTOCOL): | |
526 | VDBG(cdev, "get_protocol\n"); | |
b3c4ec71 AM |
527 | length = min_t(unsigned int, length, 1); |
528 | ((u8 *) req->buf)[0] = hidg->protocol; | |
529 | goto respond; | |
71adf118 FC |
530 | break; |
531 | ||
532 | case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 | |
533 | | HID_REQ_SET_REPORT): | |
6774def6 | 534 | VDBG(cdev, "set_report | wLength=%d\n", ctrl->wLength); |
99c51500 | 535 | goto stall; |
71adf118 FC |
536 | break; |
537 | ||
538 | case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 | |
539 | | HID_REQ_SET_PROTOCOL): | |
540 | VDBG(cdev, "set_protocol\n"); | |
b3c4ec71 AM |
541 | if (value > HID_REPORT_PROTOCOL) |
542 | goto stall; | |
543 | length = 0; | |
544 | /* | |
545 | * We assume that programs implementing the Boot protocol | |
546 | * are also compatible with the Report Protocol | |
547 | */ | |
548 | if (hidg->bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) { | |
549 | hidg->protocol = value; | |
550 | goto respond; | |
551 | } | |
71adf118 FC |
552 | goto stall; |
553 | break; | |
554 | ||
555 | case ((USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE) << 8 | |
556 | | USB_REQ_GET_DESCRIPTOR): | |
557 | switch (value >> 8) { | |
c240d78a | 558 | case HID_DT_HID: |
f286d487 KO |
559 | { |
560 | struct hid_descriptor hidg_desc_copy = hidg_desc; | |
561 | ||
c240d78a | 562 | VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: HID\n"); |
f286d487 KO |
563 | hidg_desc_copy.desc[0].bDescriptorType = HID_DT_REPORT; |
564 | hidg_desc_copy.desc[0].wDescriptorLength = | |
565 | cpu_to_le16(hidg->report_desc_length); | |
566 | ||
c240d78a | 567 | length = min_t(unsigned short, length, |
f286d487 KO |
568 | hidg_desc_copy.bLength); |
569 | memcpy(req->buf, &hidg_desc_copy, length); | |
c240d78a SB |
570 | goto respond; |
571 | break; | |
f286d487 | 572 | } |
71adf118 FC |
573 | case HID_DT_REPORT: |
574 | VDBG(cdev, "USB_REQ_GET_DESCRIPTOR: REPORT\n"); | |
575 | length = min_t(unsigned short, length, | |
576 | hidg->report_desc_length); | |
577 | memcpy(req->buf, hidg->report_desc, length); | |
578 | goto respond; | |
579 | break; | |
580 | ||
581 | default: | |
1c1301dd | 582 | VDBG(cdev, "Unknown descriptor request 0x%x\n", |
71adf118 FC |
583 | value >> 8); |
584 | goto stall; | |
585 | break; | |
586 | } | |
587 | break; | |
588 | ||
589 | default: | |
590 | VDBG(cdev, "Unknown request 0x%x\n", | |
591 | ctrl->bRequest); | |
592 | goto stall; | |
593 | break; | |
594 | } | |
595 | ||
596 | stall: | |
597 | return -EOPNOTSUPP; | |
598 | ||
599 | respond: | |
600 | req->zero = 0; | |
601 | req->length = length; | |
602 | status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); | |
603 | if (status < 0) | |
604 | ERROR(cdev, "usb_ep_queue error on ep0 %d\n", value); | |
605 | return status; | |
606 | } | |
607 | ||
608 | static void hidg_disable(struct usb_function *f) | |
609 | { | |
610 | struct f_hidg *hidg = func_to_hidg(f); | |
99c51500 | 611 | struct f_hidg_req_list *list, *next; |
aa65d11a | 612 | unsigned long flags; |
71adf118 FC |
613 | |
614 | usb_ep_disable(hidg->in_ep); | |
99c51500 | 615 | usb_ep_disable(hidg->out_ep); |
99c51500 | 616 | |
33e4c1a9 | 617 | spin_lock_irqsave(&hidg->read_spinlock, flags); |
99c51500 | 618 | list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) { |
aa65d11a | 619 | free_ep_req(hidg->out_ep, list->req); |
99c51500 DM |
620 | list_del(&list->list); |
621 | kfree(list); | |
622 | } | |
33e4c1a9 | 623 | spin_unlock_irqrestore(&hidg->read_spinlock, flags); |
749494b6 KO |
624 | |
625 | spin_lock_irqsave(&hidg->write_spinlock, flags); | |
626 | if (!hidg->write_pending) { | |
627 | free_ep_req(hidg->in_ep, hidg->req); | |
628 | hidg->write_pending = 1; | |
629 | } | |
630 | ||
631 | hidg->req = NULL; | |
632 | spin_unlock_irqrestore(&hidg->write_spinlock, flags); | |
71adf118 FC |
633 | } |
634 | ||
635 | static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) | |
636 | { | |
637 | struct usb_composite_dev *cdev = f->config->cdev; | |
638 | struct f_hidg *hidg = func_to_hidg(f); | |
749494b6 KO |
639 | struct usb_request *req_in = NULL; |
640 | unsigned long flags; | |
99c51500 | 641 | int i, status = 0; |
71adf118 FC |
642 | |
643 | VDBG(cdev, "hidg_set_alt intf:%d alt:%d\n", intf, alt); | |
644 | ||
645 | if (hidg->in_ep != NULL) { | |
646 | /* restart endpoint */ | |
2516a680 | 647 | usb_ep_disable(hidg->in_ep); |
71adf118 | 648 | |
ea2a1df7 TB |
649 | status = config_ep_by_speed(f->config->cdev->gadget, f, |
650 | hidg->in_ep); | |
651 | if (status) { | |
652 | ERROR(cdev, "config_ep_by_speed FAILED!\n"); | |
653 | goto fail; | |
654 | } | |
72c973dd | 655 | status = usb_ep_enable(hidg->in_ep); |
71adf118 | 656 | if (status < 0) { |
99c51500 | 657 | ERROR(cdev, "Enable IN endpoint FAILED!\n"); |
71adf118 FC |
658 | goto fail; |
659 | } | |
660 | hidg->in_ep->driver_data = hidg; | |
749494b6 KO |
661 | |
662 | req_in = hidg_alloc_ep_req(hidg->in_ep, hidg->report_length); | |
663 | if (!req_in) { | |
664 | status = -ENOMEM; | |
665 | goto disable_ep_in; | |
666 | } | |
71adf118 | 667 | } |
99c51500 DM |
668 | |
669 | ||
670 | if (hidg->out_ep != NULL) { | |
671 | /* restart endpoint */ | |
2516a680 | 672 | usb_ep_disable(hidg->out_ep); |
99c51500 DM |
673 | |
674 | status = config_ep_by_speed(f->config->cdev->gadget, f, | |
675 | hidg->out_ep); | |
676 | if (status) { | |
677 | ERROR(cdev, "config_ep_by_speed FAILED!\n"); | |
749494b6 | 678 | goto free_req_in; |
99c51500 DM |
679 | } |
680 | status = usb_ep_enable(hidg->out_ep); | |
681 | if (status < 0) { | |
43aef5c2 | 682 | ERROR(cdev, "Enable OUT endpoint FAILED!\n"); |
749494b6 | 683 | goto free_req_in; |
99c51500 DM |
684 | } |
685 | hidg->out_ep->driver_data = hidg; | |
686 | ||
687 | /* | |
688 | * allocate a bunch of read buffers and queue them all at once. | |
689 | */ | |
690 | for (i = 0; i < hidg->qlen && status == 0; i++) { | |
691 | struct usb_request *req = | |
692 | hidg_alloc_ep_req(hidg->out_ep, | |
693 | hidg->report_length); | |
694 | if (req) { | |
695 | req->complete = hidg_set_report_complete; | |
696 | req->context = hidg; | |
697 | status = usb_ep_queue(hidg->out_ep, req, | |
698 | GFP_ATOMIC); | |
749494b6 | 699 | if (status) { |
99c51500 DM |
700 | ERROR(cdev, "%s queue req --> %d\n", |
701 | hidg->out_ep->name, status); | |
749494b6 KO |
702 | free_ep_req(hidg->out_ep, req); |
703 | } | |
99c51500 | 704 | } else { |
99c51500 | 705 | status = -ENOMEM; |
749494b6 | 706 | goto disable_out_ep; |
99c51500 DM |
707 | } |
708 | } | |
709 | } | |
710 | ||
749494b6 KO |
711 | if (hidg->in_ep != NULL) { |
712 | spin_lock_irqsave(&hidg->write_spinlock, flags); | |
713 | hidg->req = req_in; | |
714 | hidg->write_pending = 0; | |
715 | spin_unlock_irqrestore(&hidg->write_spinlock, flags); | |
716 | ||
717 | wake_up(&hidg->write_queue); | |
718 | } | |
719 | return 0; | |
720 | disable_out_ep: | |
721 | usb_ep_disable(hidg->out_ep); | |
722 | free_req_in: | |
723 | if (req_in) | |
724 | free_ep_req(hidg->in_ep, req_in); | |
725 | ||
726 | disable_ep_in: | |
727 | if (hidg->in_ep) | |
728 | usb_ep_disable(hidg->in_ep); | |
729 | ||
71adf118 FC |
730 | fail: |
731 | return status; | |
732 | } | |
733 | ||
7a3cc461 | 734 | static const struct file_operations f_hidg_fops = { |
71adf118 FC |
735 | .owner = THIS_MODULE, |
736 | .open = f_hidg_open, | |
737 | .release = f_hidg_release, | |
738 | .write = f_hidg_write, | |
739 | .read = f_hidg_read, | |
740 | .poll = f_hidg_poll, | |
6038f373 | 741 | .llseek = noop_llseek, |
71adf118 FC |
742 | }; |
743 | ||
cb382536 | 744 | static int hidg_bind(struct usb_configuration *c, struct usb_function *f) |
71adf118 FC |
745 | { |
746 | struct usb_ep *ep; | |
747 | struct f_hidg *hidg = func_to_hidg(f); | |
5ca8d3ec | 748 | struct usb_string *us; |
63406087 | 749 | struct device *device; |
71adf118 FC |
750 | int status; |
751 | dev_t dev; | |
752 | ||
cb382536 | 753 | /* maybe allocate device-global string IDs, and patch descriptors */ |
5ca8d3ec AP |
754 | us = usb_gstrings_attach(c->cdev, ct_func_strings, |
755 | ARRAY_SIZE(ct_func_string_defs)); | |
756 | if (IS_ERR(us)) | |
757 | return PTR_ERR(us); | |
758 | hidg_interface_desc.iInterface = us[CT_FUNC_HID_IDX].id; | |
cb382536 | 759 | |
71adf118 FC |
760 | /* allocate instance-specific interface IDs, and patch descriptors */ |
761 | status = usb_interface_id(c, f); | |
762 | if (status < 0) | |
763 | goto fail; | |
764 | hidg_interface_desc.bInterfaceNumber = status; | |
765 | ||
71adf118 FC |
766 | /* allocate instance-specific endpoints */ |
767 | status = -ENODEV; | |
768 | ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_in_ep_desc); | |
769 | if (!ep) | |
770 | goto fail; | |
71adf118 FC |
771 | hidg->in_ep = ep; |
772 | ||
99c51500 DM |
773 | ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc); |
774 | if (!ep) | |
775 | goto fail; | |
99c51500 DM |
776 | hidg->out_ep = ep; |
777 | ||
71adf118 FC |
778 | /* set descriptor dynamic values */ |
779 | hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass; | |
780 | hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol; | |
b3c4ec71 | 781 | hidg->protocol = HID_REPORT_PROTOCOL; |
dbf499cf JD |
782 | hidg_ss_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); |
783 | hidg_ss_in_comp_desc.wBytesPerInterval = | |
784 | cpu_to_le16(hidg->report_length); | |
71adf118 FC |
785 | hidg_hs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); |
786 | hidg_fs_in_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); | |
dbf499cf JD |
787 | hidg_ss_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); |
788 | hidg_ss_out_comp_desc.wBytesPerInterval = | |
789 | cpu_to_le16(hidg->report_length); | |
99c51500 DM |
790 | hidg_hs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); |
791 | hidg_fs_out_ep_desc.wMaxPacketSize = cpu_to_le16(hidg->report_length); | |
f286d487 KO |
792 | /* |
793 | * We can use hidg_desc struct here but we should not relay | |
794 | * that its content won't change after returning from this function. | |
795 | */ | |
71adf118 FC |
796 | hidg_desc.desc[0].bDescriptorType = HID_DT_REPORT; |
797 | hidg_desc.desc[0].wDescriptorLength = | |
798 | cpu_to_le16(hidg->report_desc_length); | |
799 | ||
10287bae SAS |
800 | hidg_hs_in_ep_desc.bEndpointAddress = |
801 | hidg_fs_in_ep_desc.bEndpointAddress; | |
802 | hidg_hs_out_ep_desc.bEndpointAddress = | |
803 | hidg_fs_out_ep_desc.bEndpointAddress; | |
71adf118 | 804 | |
dbf499cf JD |
805 | hidg_ss_in_ep_desc.bEndpointAddress = |
806 | hidg_fs_in_ep_desc.bEndpointAddress; | |
807 | hidg_ss_out_ep_desc.bEndpointAddress = | |
808 | hidg_fs_out_ep_desc.bEndpointAddress; | |
809 | ||
10287bae | 810 | status = usb_assign_descriptors(f, hidg_fs_descriptors, |
dbf499cf | 811 | hidg_hs_descriptors, hidg_ss_descriptors, NULL); |
10287bae SAS |
812 | if (status) |
813 | goto fail; | |
71adf118 | 814 | |
33e4c1a9 | 815 | spin_lock_init(&hidg->write_spinlock); |
749494b6 KO |
816 | hidg->write_pending = 1; |
817 | hidg->req = NULL; | |
33e4c1a9 | 818 | spin_lock_init(&hidg->read_spinlock); |
71adf118 FC |
819 | init_waitqueue_head(&hidg->write_queue); |
820 | init_waitqueue_head(&hidg->read_queue); | |
99c51500 | 821 | INIT_LIST_HEAD(&hidg->completed_out_req); |
71adf118 FC |
822 | |
823 | /* create char device */ | |
824 | cdev_init(&hidg->cdev, &f_hidg_fops); | |
825 | dev = MKDEV(major, hidg->minor); | |
826 | status = cdev_add(&hidg->cdev, dev, 1); | |
827 | if (status) | |
d12a8727 | 828 | goto fail_free_descs; |
71adf118 | 829 | |
63406087 AP |
830 | device = device_create(hidg_class, NULL, dev, NULL, |
831 | "%s%d", "hidg", hidg->minor); | |
832 | if (IS_ERR(device)) { | |
833 | status = PTR_ERR(device); | |
834 | goto del; | |
835 | } | |
71adf118 FC |
836 | |
837 | return 0; | |
63406087 AP |
838 | del: |
839 | cdev_del(&hidg->cdev); | |
d12a8727 PM |
840 | fail_free_descs: |
841 | usb_free_all_descriptors(f); | |
71adf118 FC |
842 | fail: |
843 | ERROR(f->config->cdev, "hidg_bind FAILED\n"); | |
14794d71 FT |
844 | if (hidg->req != NULL) |
845 | free_ep_req(hidg->in_ep, hidg->req); | |
71adf118 | 846 | |
71adf118 FC |
847 | return status; |
848 | } | |
849 | ||
cb382536 | 850 | static inline int hidg_get_minor(void) |
71adf118 | 851 | { |
cb382536 | 852 | int ret; |
71adf118 | 853 | |
cb382536 | 854 | ret = ida_simple_get(&hidg_ida, 0, 0, GFP_KERNEL); |
774cf72f AP |
855 | if (ret >= HIDG_MINORS) { |
856 | ida_simple_remove(&hidg_ida, ret); | |
857 | ret = -ENODEV; | |
858 | } | |
71adf118 | 859 | |
cb382536 AP |
860 | return ret; |
861 | } | |
71adf118 | 862 | |
21a9476a AP |
863 | static inline struct f_hid_opts *to_f_hid_opts(struct config_item *item) |
864 | { | |
865 | return container_of(to_config_group(item), struct f_hid_opts, | |
866 | func_inst.group); | |
71adf118 FC |
867 | } |
868 | ||
21a9476a AP |
869 | static void hid_attr_release(struct config_item *item) |
870 | { | |
871 | struct f_hid_opts *opts = to_f_hid_opts(item); | |
71adf118 | 872 | |
21a9476a AP |
873 | usb_put_function_instance(&opts->func_inst); |
874 | } | |
71adf118 | 875 | |
21a9476a AP |
876 | static struct configfs_item_operations hidg_item_ops = { |
877 | .release = hid_attr_release, | |
71adf118 FC |
878 | }; |
879 | ||
21a9476a | 880 | #define F_HID_OPT(name, prec, limit) \ |
da4e527c | 881 | static ssize_t f_hid_opts_##name##_show(struct config_item *item, char *page)\ |
21a9476a | 882 | { \ |
da4e527c | 883 | struct f_hid_opts *opts = to_f_hid_opts(item); \ |
21a9476a AP |
884 | int result; \ |
885 | \ | |
886 | mutex_lock(&opts->lock); \ | |
887 | result = sprintf(page, "%d\n", opts->name); \ | |
888 | mutex_unlock(&opts->lock); \ | |
889 | \ | |
890 | return result; \ | |
891 | } \ | |
892 | \ | |
da4e527c | 893 | static ssize_t f_hid_opts_##name##_store(struct config_item *item, \ |
21a9476a AP |
894 | const char *page, size_t len) \ |
895 | { \ | |
da4e527c | 896 | struct f_hid_opts *opts = to_f_hid_opts(item); \ |
21a9476a AP |
897 | int ret; \ |
898 | u##prec num; \ | |
899 | \ | |
900 | mutex_lock(&opts->lock); \ | |
901 | if (opts->refcnt) { \ | |
902 | ret = -EBUSY; \ | |
903 | goto end; \ | |
904 | } \ | |
905 | \ | |
906 | ret = kstrtou##prec(page, 0, &num); \ | |
907 | if (ret) \ | |
908 | goto end; \ | |
909 | \ | |
910 | if (num > limit) { \ | |
911 | ret = -EINVAL; \ | |
912 | goto end; \ | |
913 | } \ | |
914 | opts->name = num; \ | |
915 | ret = len; \ | |
916 | \ | |
917 | end: \ | |
918 | mutex_unlock(&opts->lock); \ | |
919 | return ret; \ | |
920 | } \ | |
921 | \ | |
da4e527c | 922 | CONFIGFS_ATTR(f_hid_opts_, name) |
21a9476a AP |
923 | |
924 | F_HID_OPT(subclass, 8, 255); | |
925 | F_HID_OPT(protocol, 8, 255); | |
39a2ac27 | 926 | F_HID_OPT(report_length, 16, 65535); |
21a9476a | 927 | |
da4e527c | 928 | static ssize_t f_hid_opts_report_desc_show(struct config_item *item, char *page) |
21a9476a | 929 | { |
da4e527c | 930 | struct f_hid_opts *opts = to_f_hid_opts(item); |
21a9476a AP |
931 | int result; |
932 | ||
933 | mutex_lock(&opts->lock); | |
934 | result = opts->report_desc_length; | |
935 | memcpy(page, opts->report_desc, opts->report_desc_length); | |
936 | mutex_unlock(&opts->lock); | |
937 | ||
938 | return result; | |
939 | } | |
940 | ||
da4e527c | 941 | static ssize_t f_hid_opts_report_desc_store(struct config_item *item, |
21a9476a AP |
942 | const char *page, size_t len) |
943 | { | |
da4e527c | 944 | struct f_hid_opts *opts = to_f_hid_opts(item); |
21a9476a AP |
945 | int ret = -EBUSY; |
946 | char *d; | |
947 | ||
948 | mutex_lock(&opts->lock); | |
949 | ||
950 | if (opts->refcnt) | |
951 | goto end; | |
952 | if (len > PAGE_SIZE) { | |
953 | ret = -ENOSPC; | |
954 | goto end; | |
955 | } | |
956 | d = kmemdup(page, len, GFP_KERNEL); | |
957 | if (!d) { | |
958 | ret = -ENOMEM; | |
959 | goto end; | |
960 | } | |
961 | kfree(opts->report_desc); | |
962 | opts->report_desc = d; | |
963 | opts->report_desc_length = len; | |
964 | opts->report_desc_alloc = true; | |
965 | ret = len; | |
966 | end: | |
967 | mutex_unlock(&opts->lock); | |
968 | return ret; | |
969 | } | |
970 | ||
da4e527c | 971 | CONFIGFS_ATTR(f_hid_opts_, report_desc); |
21a9476a | 972 | |
ed6fe1f5 JB |
973 | static ssize_t f_hid_opts_dev_show(struct config_item *item, char *page) |
974 | { | |
975 | struct f_hid_opts *opts = to_f_hid_opts(item); | |
976 | ||
977 | return sprintf(page, "%d:%d\n", major, opts->minor); | |
978 | } | |
979 | ||
980 | CONFIGFS_ATTR_RO(f_hid_opts_, dev); | |
981 | ||
21a9476a | 982 | static struct configfs_attribute *hid_attrs[] = { |
da4e527c CH |
983 | &f_hid_opts_attr_subclass, |
984 | &f_hid_opts_attr_protocol, | |
985 | &f_hid_opts_attr_report_length, | |
986 | &f_hid_opts_attr_report_desc, | |
ed6fe1f5 | 987 | &f_hid_opts_attr_dev, |
71adf118 FC |
988 | NULL, |
989 | }; | |
990 | ||
97363902 | 991 | static const struct config_item_type hid_func_type = { |
21a9476a AP |
992 | .ct_item_ops = &hidg_item_ops, |
993 | .ct_attrs = hid_attrs, | |
994 | .ct_owner = THIS_MODULE, | |
995 | }; | |
71adf118 | 996 | |
cb382536 | 997 | static inline void hidg_put_minor(int minor) |
71adf118 | 998 | { |
cb382536 AP |
999 | ida_simple_remove(&hidg_ida, minor); |
1000 | } | |
71adf118 | 1001 | |
cb382536 AP |
1002 | static void hidg_free_inst(struct usb_function_instance *f) |
1003 | { | |
1004 | struct f_hid_opts *opts; | |
71adf118 | 1005 | |
cb382536 AP |
1006 | opts = container_of(f, struct f_hid_opts, func_inst); |
1007 | ||
1008 | mutex_lock(&hidg_ida_lock); | |
1009 | ||
1010 | hidg_put_minor(opts->minor); | |
99c49407 | 1011 | if (ida_is_empty(&hidg_ida)) |
cb382536 AP |
1012 | ghid_cleanup(); |
1013 | ||
1014 | mutex_unlock(&hidg_ida_lock); | |
1015 | ||
1016 | if (opts->report_desc_alloc) | |
1017 | kfree(opts->report_desc); | |
1018 | ||
1019 | kfree(opts); | |
1020 | } | |
1021 | ||
1022 | static struct usb_function_instance *hidg_alloc_inst(void) | |
1023 | { | |
1024 | struct f_hid_opts *opts; | |
1025 | struct usb_function_instance *ret; | |
1026 | int status = 0; | |
1027 | ||
1028 | opts = kzalloc(sizeof(*opts), GFP_KERNEL); | |
1029 | if (!opts) | |
1030 | return ERR_PTR(-ENOMEM); | |
21a9476a | 1031 | mutex_init(&opts->lock); |
cb382536 AP |
1032 | opts->func_inst.free_func_inst = hidg_free_inst; |
1033 | ret = &opts->func_inst; | |
1034 | ||
1035 | mutex_lock(&hidg_ida_lock); | |
1036 | ||
99c49407 | 1037 | if (ida_is_empty(&hidg_ida)) { |
cb382536 AP |
1038 | status = ghid_setup(NULL, HIDG_MINORS); |
1039 | if (status) { | |
1040 | ret = ERR_PTR(status); | |
1041 | kfree(opts); | |
1042 | goto unlock; | |
1043 | } | |
1044 | } | |
1045 | ||
1046 | opts->minor = hidg_get_minor(); | |
1047 | if (opts->minor < 0) { | |
1048 | ret = ERR_PTR(opts->minor); | |
1049 | kfree(opts); | |
99c49407 | 1050 | if (ida_is_empty(&hidg_ida)) |
cb382536 | 1051 | ghid_cleanup(); |
828f6148 | 1052 | goto unlock; |
71adf118 | 1053 | } |
21a9476a | 1054 | config_group_init_type_name(&opts->func_inst.group, "", &hid_func_type); |
cb382536 AP |
1055 | |
1056 | unlock: | |
1057 | mutex_unlock(&hidg_ida_lock); | |
1058 | return ret; | |
1059 | } | |
1060 | ||
1061 | static void hidg_free(struct usb_function *f) | |
1062 | { | |
1063 | struct f_hidg *hidg; | |
21a9476a | 1064 | struct f_hid_opts *opts; |
cb382536 AP |
1065 | |
1066 | hidg = func_to_hidg(f); | |
21a9476a | 1067 | opts = container_of(f->fi, struct f_hid_opts, func_inst); |
cb382536 AP |
1068 | kfree(hidg->report_desc); |
1069 | kfree(hidg); | |
21a9476a AP |
1070 | mutex_lock(&opts->lock); |
1071 | --opts->refcnt; | |
1072 | mutex_unlock(&opts->lock); | |
cb382536 AP |
1073 | } |
1074 | ||
1075 | static void hidg_unbind(struct usb_configuration *c, struct usb_function *f) | |
1076 | { | |
1077 | struct f_hidg *hidg = func_to_hidg(f); | |
1078 | ||
1079 | device_destroy(hidg_class, MKDEV(major, hidg->minor)); | |
1080 | cdev_del(&hidg->cdev); | |
1081 | ||
cb382536 AP |
1082 | usb_free_all_descriptors(f); |
1083 | } | |
1084 | ||
0fc57ea0 | 1085 | static struct usb_function *hidg_alloc(struct usb_function_instance *fi) |
cb382536 AP |
1086 | { |
1087 | struct f_hidg *hidg; | |
1088 | struct f_hid_opts *opts; | |
71adf118 FC |
1089 | |
1090 | /* allocate and initialize one new instance */ | |
cb382536 | 1091 | hidg = kzalloc(sizeof(*hidg), GFP_KERNEL); |
71adf118 | 1092 | if (!hidg) |
cb382536 AP |
1093 | return ERR_PTR(-ENOMEM); |
1094 | ||
1095 | opts = container_of(fi, struct f_hid_opts, func_inst); | |
1096 | ||
21a9476a AP |
1097 | mutex_lock(&opts->lock); |
1098 | ++opts->refcnt; | |
1099 | ||
cb382536 AP |
1100 | hidg->minor = opts->minor; |
1101 | hidg->bInterfaceSubClass = opts->subclass; | |
1102 | hidg->bInterfaceProtocol = opts->protocol; | |
1103 | hidg->report_length = opts->report_length; | |
1104 | hidg->report_desc_length = opts->report_desc_length; | |
1105 | if (opts->report_desc) { | |
1106 | hidg->report_desc = kmemdup(opts->report_desc, | |
1107 | opts->report_desc_length, | |
1108 | GFP_KERNEL); | |
1109 | if (!hidg->report_desc) { | |
1110 | kfree(hidg); | |
21a9476a | 1111 | mutex_unlock(&opts->lock); |
cb382536 AP |
1112 | return ERR_PTR(-ENOMEM); |
1113 | } | |
71adf118 FC |
1114 | } |
1115 | ||
21a9476a AP |
1116 | mutex_unlock(&opts->lock); |
1117 | ||
71adf118 | 1118 | hidg->func.name = "hid"; |
71adf118 FC |
1119 | hidg->func.bind = hidg_bind; |
1120 | hidg->func.unbind = hidg_unbind; | |
1121 | hidg->func.set_alt = hidg_set_alt; | |
1122 | hidg->func.disable = hidg_disable; | |
1123 | hidg->func.setup = hidg_setup; | |
cb382536 | 1124 | hidg->func.free_func = hidg_free; |
71adf118 | 1125 | |
99c51500 DM |
1126 | /* this could me made configurable at some point */ |
1127 | hidg->qlen = 4; | |
1128 | ||
cb382536 | 1129 | return &hidg->func; |
71adf118 FC |
1130 | } |
1131 | ||
cb382536 AP |
1132 | DECLARE_USB_FUNCTION_INIT(hid, hidg_alloc_inst, hidg_alloc); |
1133 | MODULE_LICENSE("GPL"); | |
1134 | MODULE_AUTHOR("Fabien Chouteau"); | |
1135 | ||
cb382536 | 1136 | int ghid_setup(struct usb_gadget *g, int count) |
71adf118 FC |
1137 | { |
1138 | int status; | |
1139 | dev_t dev; | |
1140 | ||
1141 | hidg_class = class_create(THIS_MODULE, "hidg"); | |
06529407 | 1142 | if (IS_ERR(hidg_class)) { |
0448d38c | 1143 | status = PTR_ERR(hidg_class); |
06529407 | 1144 | hidg_class = NULL; |
0448d38c | 1145 | return status; |
06529407 | 1146 | } |
71adf118 FC |
1147 | |
1148 | status = alloc_chrdev_region(&dev, 0, count, "hidg"); | |
0448d38c DC |
1149 | if (status) { |
1150 | class_destroy(hidg_class); | |
1151 | hidg_class = NULL; | |
1152 | return status; | |
71adf118 FC |
1153 | } |
1154 | ||
0448d38c DC |
1155 | major = MAJOR(dev); |
1156 | minors = count; | |
1157 | ||
1158 | return 0; | |
71adf118 FC |
1159 | } |
1160 | ||
1161 | void ghid_cleanup(void) | |
1162 | { | |
1163 | if (major) { | |
1164 | unregister_chrdev_region(MKDEV(major, 0), minors); | |
1165 | major = minors = 0; | |
1166 | } | |
1167 | ||
1168 | class_destroy(hidg_class); | |
1169 | hidg_class = NULL; | |
1170 | } |