diff options
Diffstat (limited to 'drivers/usb/gadget/f_accessory.c')
-rw-r--r-- | drivers/usb/gadget/f_accessory.c | 788 |
1 files changed, 788 insertions, 0 deletions
diff --git a/drivers/usb/gadget/f_accessory.c b/drivers/usb/gadget/f_accessory.c new file mode 100644 index 00000000000..ae65faaf3d7 --- /dev/null +++ b/drivers/usb/gadget/f_accessory.c | |||
@@ -0,0 +1,788 @@ | |||
1 | /* | ||
2 | * Gadget Function Driver for Android USB accessories | ||
3 | * | ||
4 | * Copyright (C) 2011 Google, Inc. | ||
5 | * Author: Mike Lockwood <lockwood@android.com> | ||
6 | * | ||
7 | * This software is licensed under the terms of the GNU General Public | ||
8 | * License version 2, as published by the Free Software Foundation, and | ||
9 | * may be copied, distributed, and modified under those terms. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | /* #define DEBUG */ | ||
19 | /* #define VERBOSE_DEBUG */ | ||
20 | |||
21 | #include <linux/module.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/poll.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/wait.h> | ||
26 | #include <linux/err.h> | ||
27 | #include <linux/interrupt.h> | ||
28 | #include <linux/kthread.h> | ||
29 | #include <linux/freezer.h> | ||
30 | |||
31 | #include <linux/types.h> | ||
32 | #include <linux/file.h> | ||
33 | #include <linux/device.h> | ||
34 | #include <linux/miscdevice.h> | ||
35 | |||
36 | #include <linux/usb.h> | ||
37 | #include <linux/usb/ch9.h> | ||
38 | #include <linux/usb/f_accessory.h> | ||
39 | |||
40 | #define BULK_BUFFER_SIZE 16384 | ||
41 | #define ACC_STRING_SIZE 256 | ||
42 | |||
43 | #define PROTOCOL_VERSION 1 | ||
44 | |||
45 | /* String IDs */ | ||
46 | #define INTERFACE_STRING_INDEX 0 | ||
47 | |||
48 | /* number of tx and rx requests to allocate */ | ||
49 | #define TX_REQ_MAX 4 | ||
50 | #define RX_REQ_MAX 2 | ||
51 | |||
52 | struct acc_dev { | ||
53 | struct usb_function function; | ||
54 | struct usb_composite_dev *cdev; | ||
55 | spinlock_t lock; | ||
56 | |||
57 | struct usb_ep *ep_in; | ||
58 | struct usb_ep *ep_out; | ||
59 | |||
60 | /* set to 1 when we connect */ | ||
61 | int online:1; | ||
62 | /* Set to 1 when we disconnect. | ||
63 | * Not cleared until our file is closed. | ||
64 | */ | ||
65 | int disconnected:1; | ||
66 | |||
67 | /* strings sent by the host */ | ||
68 | char manufacturer[ACC_STRING_SIZE]; | ||
69 | char model[ACC_STRING_SIZE]; | ||
70 | char description[ACC_STRING_SIZE]; | ||
71 | char version[ACC_STRING_SIZE]; | ||
72 | char uri[ACC_STRING_SIZE]; | ||
73 | char serial[ACC_STRING_SIZE]; | ||
74 | |||
75 | /* for acc_complete_set_string */ | ||
76 | int string_index; | ||
77 | |||
78 | /* set to 1 if we have a pending start request */ | ||
79 | int start_requested; | ||
80 | |||
81 | /* synchronize access to our device file */ | ||
82 | atomic_t open_excl; | ||
83 | |||
84 | struct list_head tx_idle; | ||
85 | |||
86 | wait_queue_head_t read_wq; | ||
87 | wait_queue_head_t write_wq; | ||
88 | struct usb_request *rx_req[RX_REQ_MAX]; | ||
89 | int rx_done; | ||
90 | struct delayed_work work; | ||
91 | }; | ||
92 | |||
93 | static struct usb_interface_descriptor acc_interface_desc = { | ||
94 | .bLength = USB_DT_INTERFACE_SIZE, | ||
95 | .bDescriptorType = USB_DT_INTERFACE, | ||
96 | .bInterfaceNumber = 0, | ||
97 | .bNumEndpoints = 2, | ||
98 | .bInterfaceClass = USB_CLASS_VENDOR_SPEC, | ||
99 | .bInterfaceSubClass = USB_SUBCLASS_VENDOR_SPEC, | ||
100 | .bInterfaceProtocol = 0, | ||
101 | }; | ||
102 | |||
103 | static struct usb_endpoint_descriptor acc_highspeed_in_desc = { | ||
104 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
105 | .bDescriptorType = USB_DT_ENDPOINT, | ||
106 | .bEndpointAddress = USB_DIR_IN, | ||
107 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
108 | .wMaxPacketSize = __constant_cpu_to_le16(512), | ||
109 | }; | ||
110 | |||
111 | static struct usb_endpoint_descriptor acc_highspeed_out_desc = { | ||
112 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
113 | .bDescriptorType = USB_DT_ENDPOINT, | ||
114 | .bEndpointAddress = USB_DIR_OUT, | ||
115 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
116 | .wMaxPacketSize = __constant_cpu_to_le16(512), | ||
117 | }; | ||
118 | |||
119 | static struct usb_endpoint_descriptor acc_fullspeed_in_desc = { | ||
120 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
121 | .bDescriptorType = USB_DT_ENDPOINT, | ||
122 | .bEndpointAddress = USB_DIR_IN, | ||
123 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
124 | }; | ||
125 | |||
126 | static struct usb_endpoint_descriptor acc_fullspeed_out_desc = { | ||
127 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
128 | .bDescriptorType = USB_DT_ENDPOINT, | ||
129 | .bEndpointAddress = USB_DIR_OUT, | ||
130 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
131 | }; | ||
132 | |||
133 | static struct usb_descriptor_header *fs_acc_descs[] = { | ||
134 | (struct usb_descriptor_header *) &acc_interface_desc, | ||
135 | (struct usb_descriptor_header *) &acc_fullspeed_in_desc, | ||
136 | (struct usb_descriptor_header *) &acc_fullspeed_out_desc, | ||
137 | NULL, | ||
138 | }; | ||
139 | |||
140 | static struct usb_descriptor_header *hs_acc_descs[] = { | ||
141 | (struct usb_descriptor_header *) &acc_interface_desc, | ||
142 | (struct usb_descriptor_header *) &acc_highspeed_in_desc, | ||
143 | (struct usb_descriptor_header *) &acc_highspeed_out_desc, | ||
144 | NULL, | ||
145 | }; | ||
146 | |||
147 | static struct usb_string acc_string_defs[] = { | ||
148 | [INTERFACE_STRING_INDEX].s = "Android Accessory Interface", | ||
149 | { }, /* end of list */ | ||
150 | }; | ||
151 | |||
152 | static struct usb_gadget_strings acc_string_table = { | ||
153 | .language = 0x0409, /* en-US */ | ||
154 | .strings = acc_string_defs, | ||
155 | }; | ||
156 | |||
157 | static struct usb_gadget_strings *acc_strings[] = { | ||
158 | &acc_string_table, | ||
159 | NULL, | ||
160 | }; | ||
161 | |||
162 | /* temporary variable used between acc_open() and acc_gadget_bind() */ | ||
163 | static struct acc_dev *_acc_dev; | ||
164 | |||
165 | static inline struct acc_dev *func_to_dev(struct usb_function *f) | ||
166 | { | ||
167 | return container_of(f, struct acc_dev, function); | ||
168 | } | ||
169 | |||
170 | static struct usb_request *acc_request_new(struct usb_ep *ep, int buffer_size) | ||
171 | { | ||
172 | struct usb_request *req = usb_ep_alloc_request(ep, GFP_KERNEL); | ||
173 | if (!req) | ||
174 | return NULL; | ||
175 | |||
176 | /* now allocate buffers for the requests */ | ||
177 | req->buf = kmalloc(buffer_size, GFP_KERNEL); | ||
178 | if (!req->buf) { | ||
179 | usb_ep_free_request(ep, req); | ||
180 | return NULL; | ||
181 | } | ||
182 | |||
183 | return req; | ||
184 | } | ||
185 | |||
186 | static void acc_request_free(struct usb_request *req, struct usb_ep *ep) | ||
187 | { | ||
188 | if (req) { | ||
189 | kfree(req->buf); | ||
190 | usb_ep_free_request(ep, req); | ||
191 | } | ||
192 | } | ||
193 | |||
194 | /* add a request to the tail of a list */ | ||
195 | static void req_put(struct acc_dev *dev, struct list_head *head, | ||
196 | struct usb_request *req) | ||
197 | { | ||
198 | unsigned long flags; | ||
199 | |||
200 | spin_lock_irqsave(&dev->lock, flags); | ||
201 | list_add_tail(&req->list, head); | ||
202 | spin_unlock_irqrestore(&dev->lock, flags); | ||
203 | } | ||
204 | |||
205 | /* remove a request from the head of a list */ | ||
206 | static struct usb_request *req_get(struct acc_dev *dev, struct list_head *head) | ||
207 | { | ||
208 | unsigned long flags; | ||
209 | struct usb_request *req; | ||
210 | |||
211 | spin_lock_irqsave(&dev->lock, flags); | ||
212 | if (list_empty(head)) { | ||
213 | req = 0; | ||
214 | } else { | ||
215 | req = list_first_entry(head, struct usb_request, list); | ||
216 | list_del(&req->list); | ||
217 | } | ||
218 | spin_unlock_irqrestore(&dev->lock, flags); | ||
219 | return req; | ||
220 | } | ||
221 | |||
222 | static void acc_set_disconnected(struct acc_dev *dev) | ||
223 | { | ||
224 | dev->online = 0; | ||
225 | dev->disconnected = 1; | ||
226 | } | ||
227 | |||
228 | static void acc_complete_in(struct usb_ep *ep, struct usb_request *req) | ||
229 | { | ||
230 | struct acc_dev *dev = _acc_dev; | ||
231 | |||
232 | if (req->status != 0) | ||
233 | acc_set_disconnected(dev); | ||
234 | |||
235 | req_put(dev, &dev->tx_idle, req); | ||
236 | |||
237 | wake_up(&dev->write_wq); | ||
238 | } | ||
239 | |||
240 | static void acc_complete_out(struct usb_ep *ep, struct usb_request *req) | ||
241 | { | ||
242 | struct acc_dev *dev = _acc_dev; | ||
243 | |||
244 | dev->rx_done = 1; | ||
245 | if (req->status != 0) | ||
246 | acc_set_disconnected(dev); | ||
247 | |||
248 | wake_up(&dev->read_wq); | ||
249 | } | ||
250 | |||
251 | static void acc_complete_set_string(struct usb_ep *ep, struct usb_request *req) | ||
252 | { | ||
253 | struct acc_dev *dev = ep->driver_data; | ||
254 | char *string_dest = NULL; | ||
255 | int length = req->actual; | ||
256 | |||
257 | if (req->status != 0) { | ||
258 | pr_err("acc_complete_set_string, err %d\n", req->status); | ||
259 | return; | ||
260 | } | ||
261 | |||
262 | switch (dev->string_index) { | ||
263 | case ACCESSORY_STRING_MANUFACTURER: | ||
264 | string_dest = dev->manufacturer; | ||
265 | break; | ||
266 | case ACCESSORY_STRING_MODEL: | ||
267 | string_dest = dev->model; | ||
268 | break; | ||
269 | case ACCESSORY_STRING_DESCRIPTION: | ||
270 | string_dest = dev->description; | ||
271 | break; | ||
272 | case ACCESSORY_STRING_VERSION: | ||
273 | string_dest = dev->version; | ||
274 | break; | ||
275 | case ACCESSORY_STRING_URI: | ||
276 | string_dest = dev->uri; | ||
277 | break; | ||
278 | case ACCESSORY_STRING_SERIAL: | ||
279 | string_dest = dev->serial; | ||
280 | break; | ||
281 | } | ||
282 | if (string_dest) { | ||
283 | unsigned long flags; | ||
284 | |||
285 | if (length >= ACC_STRING_SIZE) | ||
286 | length = ACC_STRING_SIZE - 1; | ||
287 | |||
288 | spin_lock_irqsave(&dev->lock, flags); | ||
289 | memcpy(string_dest, req->buf, length); | ||
290 | /* ensure zero termination */ | ||
291 | string_dest[length] = 0; | ||
292 | spin_unlock_irqrestore(&dev->lock, flags); | ||
293 | } else { | ||
294 | pr_err("unknown accessory string index %d\n", | ||
295 | dev->string_index); | ||
296 | } | ||
297 | } | ||
298 | |||
299 | static int __init create_bulk_endpoints(struct acc_dev *dev, | ||
300 | struct usb_endpoint_descriptor *in_desc, | ||
301 | struct usb_endpoint_descriptor *out_desc) | ||
302 | { | ||
303 | struct usb_composite_dev *cdev = dev->cdev; | ||
304 | struct usb_request *req; | ||
305 | struct usb_ep *ep; | ||
306 | int i; | ||
307 | |||
308 | DBG(cdev, "create_bulk_endpoints dev: %p\n", dev); | ||
309 | |||
310 | ep = usb_ep_autoconfig(cdev->gadget, in_desc); | ||
311 | if (!ep) { | ||
312 | DBG(cdev, "usb_ep_autoconfig for ep_in failed\n"); | ||
313 | return -ENODEV; | ||
314 | } | ||
315 | DBG(cdev, "usb_ep_autoconfig for ep_in got %s\n", ep->name); | ||
316 | ep->driver_data = dev; /* claim the endpoint */ | ||
317 | dev->ep_in = ep; | ||
318 | |||
319 | ep = usb_ep_autoconfig(cdev->gadget, out_desc); | ||
320 | if (!ep) { | ||
321 | DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); | ||
322 | return -ENODEV; | ||
323 | } | ||
324 | DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); | ||
325 | ep->driver_data = dev; /* claim the endpoint */ | ||
326 | dev->ep_out = ep; | ||
327 | |||
328 | ep = usb_ep_autoconfig(cdev->gadget, out_desc); | ||
329 | if (!ep) { | ||
330 | DBG(cdev, "usb_ep_autoconfig for ep_out failed\n"); | ||
331 | return -ENODEV; | ||
332 | } | ||
333 | DBG(cdev, "usb_ep_autoconfig for ep_out got %s\n", ep->name); | ||
334 | ep->driver_data = dev; /* claim the endpoint */ | ||
335 | dev->ep_out = ep; | ||
336 | |||
337 | /* now allocate requests for our endpoints */ | ||
338 | for (i = 0; i < TX_REQ_MAX; i++) { | ||
339 | req = acc_request_new(dev->ep_in, BULK_BUFFER_SIZE); | ||
340 | if (!req) | ||
341 | goto fail; | ||
342 | req->complete = acc_complete_in; | ||
343 | req_put(dev, &dev->tx_idle, req); | ||
344 | } | ||
345 | for (i = 0; i < RX_REQ_MAX; i++) { | ||
346 | req = acc_request_new(dev->ep_out, BULK_BUFFER_SIZE); | ||
347 | if (!req) | ||
348 | goto fail; | ||
349 | req->complete = acc_complete_out; | ||
350 | dev->rx_req[i] = req; | ||
351 | } | ||
352 | |||
353 | return 0; | ||
354 | |||
355 | fail: | ||
356 | printk(KERN_ERR "acc_bind() could not allocate requests\n"); | ||
357 | while ((req = req_get(dev, &dev->tx_idle))) | ||
358 | acc_request_free(req, dev->ep_in); | ||
359 | for (i = 0; i < RX_REQ_MAX; i++) | ||
360 | acc_request_free(dev->rx_req[i], dev->ep_out); | ||
361 | return -1; | ||
362 | } | ||
363 | |||
364 | static ssize_t acc_read(struct file *fp, char __user *buf, | ||
365 | size_t count, loff_t *pos) | ||
366 | { | ||
367 | struct acc_dev *dev = fp->private_data; | ||
368 | struct usb_request *req; | ||
369 | int r = count, xfer; | ||
370 | int ret = 0; | ||
371 | |||
372 | pr_debug("acc_read(%d)\n", count); | ||
373 | |||
374 | if (dev->disconnected) | ||
375 | return -ENODEV; | ||
376 | |||
377 | if (count > BULK_BUFFER_SIZE) | ||
378 | count = BULK_BUFFER_SIZE; | ||
379 | |||
380 | /* we will block until we're online */ | ||
381 | pr_debug("acc_read: waiting for online\n"); | ||
382 | ret = wait_event_interruptible(dev->read_wq, dev->online); | ||
383 | if (ret < 0) { | ||
384 | r = ret; | ||
385 | goto done; | ||
386 | } | ||
387 | |||
388 | requeue_req: | ||
389 | /* queue a request */ | ||
390 | req = dev->rx_req[0]; | ||
391 | req->length = count; | ||
392 | dev->rx_done = 0; | ||
393 | ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL); | ||
394 | if (ret < 0) { | ||
395 | r = -EIO; | ||
396 | goto done; | ||
397 | } else { | ||
398 | pr_debug("rx %p queue\n", req); | ||
399 | } | ||
400 | |||
401 | /* wait for a request to complete */ | ||
402 | ret = wait_event_interruptible(dev->read_wq, dev->rx_done); | ||
403 | if (ret < 0) { | ||
404 | r = ret; | ||
405 | usb_ep_dequeue(dev->ep_out, req); | ||
406 | goto done; | ||
407 | } | ||
408 | if (dev->online) { | ||
409 | /* If we got a 0-len packet, throw it back and try again. */ | ||
410 | if (req->actual == 0) | ||
411 | goto requeue_req; | ||
412 | |||
413 | pr_debug("rx %p %d\n", req, req->actual); | ||
414 | xfer = (req->actual < count) ? req->actual : count; | ||
415 | r = xfer; | ||
416 | if (copy_to_user(buf, req->buf, xfer)) | ||
417 | r = -EFAULT; | ||
418 | } else | ||
419 | r = -EIO; | ||
420 | |||
421 | done: | ||
422 | pr_debug("acc_read returning %d\n", r); | ||
423 | return r; | ||
424 | } | ||
425 | |||
426 | static ssize_t acc_write(struct file *fp, const char __user *buf, | ||
427 | size_t count, loff_t *pos) | ||
428 | { | ||
429 | struct acc_dev *dev = fp->private_data; | ||
430 | struct usb_request *req = 0; | ||
431 | int r = count, xfer; | ||
432 | int ret; | ||
433 | |||
434 | pr_debug("acc_write(%d)\n", count); | ||
435 | |||
436 | if (!dev->online || dev->disconnected) | ||
437 | return -ENODEV; | ||
438 | |||
439 | while (count > 0) { | ||
440 | if (!dev->online) { | ||
441 | pr_debug("acc_write dev->error\n"); | ||
442 | r = -EIO; | ||
443 | break; | ||
444 | } | ||
445 | |||
446 | /* get an idle tx request to use */ | ||
447 | req = 0; | ||
448 | ret = wait_event_interruptible(dev->write_wq, | ||
449 | ((req = req_get(dev, &dev->tx_idle)) || !dev->online)); | ||
450 | if (!req) { | ||
451 | r = ret; | ||
452 | break; | ||
453 | } | ||
454 | |||
455 | if (count > BULK_BUFFER_SIZE) | ||
456 | xfer = BULK_BUFFER_SIZE; | ||
457 | else | ||
458 | xfer = count; | ||
459 | if (copy_from_user(req->buf, buf, xfer)) { | ||
460 | r = -EFAULT; | ||
461 | break; | ||
462 | } | ||
463 | |||
464 | req->length = xfer; | ||
465 | ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL); | ||
466 | if (ret < 0) { | ||
467 | pr_debug("acc_write: xfer error %d\n", ret); | ||
468 | r = -EIO; | ||
469 | break; | ||
470 | } | ||
471 | |||
472 | buf += xfer; | ||
473 | count -= xfer; | ||
474 | |||
475 | /* zero this so we don't try to free it on error exit */ | ||
476 | req = 0; | ||
477 | } | ||
478 | |||
479 | if (req) | ||
480 | req_put(dev, &dev->tx_idle, req); | ||
481 | |||
482 | pr_debug("acc_write returning %d\n", r); | ||
483 | return r; | ||
484 | } | ||
485 | |||
486 | static long acc_ioctl(struct file *fp, unsigned code, unsigned long value) | ||
487 | { | ||
488 | struct acc_dev *dev = fp->private_data; | ||
489 | char *src = NULL; | ||
490 | int ret; | ||
491 | |||
492 | switch (code) { | ||
493 | case ACCESSORY_GET_STRING_MANUFACTURER: | ||
494 | src = dev->manufacturer; | ||
495 | break; | ||
496 | case ACCESSORY_GET_STRING_MODEL: | ||
497 | src = dev->model; | ||
498 | break; | ||
499 | case ACCESSORY_GET_STRING_DESCRIPTION: | ||
500 | src = dev->description; | ||
501 | break; | ||
502 | case ACCESSORY_GET_STRING_VERSION: | ||
503 | src = dev->version; | ||
504 | break; | ||
505 | case ACCESSORY_GET_STRING_URI: | ||
506 | src = dev->uri; | ||
507 | break; | ||
508 | case ACCESSORY_GET_STRING_SERIAL: | ||
509 | src = dev->serial; | ||
510 | break; | ||
511 | case ACCESSORY_IS_START_REQUESTED: | ||
512 | return dev->start_requested; | ||
513 | } | ||
514 | if (!src) | ||
515 | return -EINVAL; | ||
516 | |||
517 | ret = strlen(src) + 1; | ||
518 | if (copy_to_user((void __user *)value, src, ret)) | ||
519 | ret = -EFAULT; | ||
520 | return ret; | ||
521 | } | ||
522 | |||
523 | static int acc_open(struct inode *ip, struct file *fp) | ||
524 | { | ||
525 | printk(KERN_INFO "acc_open\n"); | ||
526 | if (atomic_xchg(&_acc_dev->open_excl, 1)) | ||
527 | return -EBUSY; | ||
528 | |||
529 | _acc_dev->disconnected = 0; | ||
530 | fp->private_data = _acc_dev; | ||
531 | return 0; | ||
532 | } | ||
533 | |||
534 | static int acc_release(struct inode *ip, struct file *fp) | ||
535 | { | ||
536 | printk(KERN_INFO "acc_release\n"); | ||
537 | |||
538 | WARN_ON(!atomic_xchg(&_acc_dev->open_excl, 0)); | ||
539 | _acc_dev->disconnected = 0; | ||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | /* file operations for /dev/acc_usb */ | ||
544 | static const struct file_operations acc_fops = { | ||
545 | .owner = THIS_MODULE, | ||
546 | .read = acc_read, | ||
547 | .write = acc_write, | ||
548 | .unlocked_ioctl = acc_ioctl, | ||
549 | .open = acc_open, | ||
550 | .release = acc_release, | ||
551 | }; | ||
552 | |||
553 | static struct miscdevice acc_device = { | ||
554 | .minor = MISC_DYNAMIC_MINOR, | ||
555 | .name = "usb_accessory", | ||
556 | .fops = &acc_fops, | ||
557 | }; | ||
558 | |||
559 | |||
560 | static int acc_ctrlrequest(struct usb_composite_dev *cdev, | ||
561 | const struct usb_ctrlrequest *ctrl) | ||
562 | { | ||
563 | struct acc_dev *dev = _acc_dev; | ||
564 | int value = -EOPNOTSUPP; | ||
565 | u8 b_requestType = ctrl->bRequestType; | ||
566 | u8 b_request = ctrl->bRequest; | ||
567 | u16 w_index = le16_to_cpu(ctrl->wIndex); | ||
568 | u16 w_value = le16_to_cpu(ctrl->wValue); | ||
569 | u16 w_length = le16_to_cpu(ctrl->wLength); | ||
570 | |||
571 | /* | ||
572 | printk(KERN_INFO "acc_ctrlrequest " | ||
573 | "%02x.%02x v%04x i%04x l%u\n", | ||
574 | b_requestType, b_request, | ||
575 | w_value, w_index, w_length); | ||
576 | */ | ||
577 | |||
578 | if (b_requestType == (USB_DIR_OUT | USB_TYPE_VENDOR)) { | ||
579 | if (b_request == ACCESSORY_START) { | ||
580 | dev->start_requested = 1; | ||
581 | schedule_delayed_work( | ||
582 | &dev->work, msecs_to_jiffies(10)); | ||
583 | value = 0; | ||
584 | } else if (b_request == ACCESSORY_SEND_STRING) { | ||
585 | dev->string_index = w_index; | ||
586 | cdev->gadget->ep0->driver_data = dev; | ||
587 | cdev->req->complete = acc_complete_set_string; | ||
588 | value = w_length; | ||
589 | } | ||
590 | } else if (b_requestType == (USB_DIR_IN | USB_TYPE_VENDOR)) { | ||
591 | if (b_request == ACCESSORY_GET_PROTOCOL) { | ||
592 | *((u16 *)cdev->req->buf) = PROTOCOL_VERSION; | ||
593 | value = sizeof(u16); | ||
594 | |||
595 | /* clear any strings left over from a previous session */ | ||
596 | memset(dev->manufacturer, 0, sizeof(dev->manufacturer)); | ||
597 | memset(dev->model, 0, sizeof(dev->model)); | ||
598 | memset(dev->description, 0, sizeof(dev->description)); | ||
599 | memset(dev->version, 0, sizeof(dev->version)); | ||
600 | memset(dev->uri, 0, sizeof(dev->uri)); | ||
601 | memset(dev->serial, 0, sizeof(dev->serial)); | ||
602 | dev->start_requested = 0; | ||
603 | } | ||
604 | } | ||
605 | |||
606 | if (value >= 0) { | ||
607 | cdev->req->zero = 0; | ||
608 | cdev->req->length = value; | ||
609 | value = usb_ep_queue(cdev->gadget->ep0, cdev->req, GFP_ATOMIC); | ||
610 | if (value < 0) | ||
611 | ERROR(cdev, "%s setup response queue error\n", | ||
612 | __func__); | ||
613 | } | ||
614 | |||
615 | if (value == -EOPNOTSUPP) | ||
616 | VDBG(cdev, | ||
617 | "unknown class-specific control req " | ||
618 | "%02x.%02x v%04x i%04x l%u\n", | ||
619 | ctrl->bRequestType, ctrl->bRequest, | ||
620 | w_value, w_index, w_length); | ||
621 | return value; | ||
622 | } | ||
623 | |||
624 | static int | ||
625 | acc_function_bind(struct usb_configuration *c, struct usb_function *f) | ||
626 | { | ||
627 | struct usb_composite_dev *cdev = c->cdev; | ||
628 | struct acc_dev *dev = func_to_dev(f); | ||
629 | int id; | ||
630 | int ret; | ||
631 | |||
632 | DBG(cdev, "acc_function_bind dev: %p\n", dev); | ||
633 | |||
634 | dev->start_requested = 0; | ||
635 | |||
636 | /* allocate interface ID(s) */ | ||
637 | id = usb_interface_id(c, f); | ||
638 | if (id < 0) | ||
639 | return id; | ||
640 | acc_interface_desc.bInterfaceNumber = id; | ||
641 | |||
642 | /* allocate endpoints */ | ||
643 | ret = create_bulk_endpoints(dev, &acc_fullspeed_in_desc, | ||
644 | &acc_fullspeed_out_desc); | ||
645 | if (ret) | ||
646 | return ret; | ||
647 | |||
648 | /* support high speed hardware */ | ||
649 | if (gadget_is_dualspeed(c->cdev->gadget)) { | ||
650 | acc_highspeed_in_desc.bEndpointAddress = | ||
651 | acc_fullspeed_in_desc.bEndpointAddress; | ||
652 | acc_highspeed_out_desc.bEndpointAddress = | ||
653 | acc_fullspeed_out_desc.bEndpointAddress; | ||
654 | } | ||
655 | |||
656 | DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", | ||
657 | gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", | ||
658 | f->name, dev->ep_in->name, dev->ep_out->name); | ||
659 | return 0; | ||
660 | } | ||
661 | |||
662 | static void | ||
663 | acc_function_unbind(struct usb_configuration *c, struct usb_function *f) | ||
664 | { | ||
665 | struct acc_dev *dev = func_to_dev(f); | ||
666 | struct usb_request *req; | ||
667 | int i; | ||
668 | |||
669 | while ((req = req_get(dev, &dev->tx_idle))) | ||
670 | acc_request_free(req, dev->ep_in); | ||
671 | for (i = 0; i < RX_REQ_MAX; i++) | ||
672 | acc_request_free(dev->rx_req[i], dev->ep_out); | ||
673 | } | ||
674 | |||
675 | static void acc_work(struct work_struct *data) | ||
676 | { | ||
677 | char *envp[2] = { "ACCESSORY=START", NULL }; | ||
678 | kobject_uevent_env(&acc_device.this_device->kobj, KOBJ_CHANGE, envp); | ||
679 | } | ||
680 | |||
681 | static int acc_function_set_alt(struct usb_function *f, | ||
682 | unsigned intf, unsigned alt) | ||
683 | { | ||
684 | struct acc_dev *dev = func_to_dev(f); | ||
685 | struct usb_composite_dev *cdev = f->config->cdev; | ||
686 | int ret; | ||
687 | |||
688 | DBG(cdev, "acc_function_set_alt intf: %d alt: %d\n", intf, alt); | ||
689 | config_ep_by_speed(cdev->gadget, f, dev->ep_in); | ||
690 | ret = usb_ep_enable(dev->ep_in); | ||
691 | if (ret) | ||
692 | return ret; | ||
693 | config_ep_by_speed(cdev->gadget, f, dev->ep_out); | ||
694 | ret = usb_ep_enable(dev->ep_out); | ||
695 | if (ret) { | ||
696 | usb_ep_disable(dev->ep_in); | ||
697 | return ret; | ||
698 | } | ||
699 | |||
700 | dev->online = 1; | ||
701 | |||
702 | /* readers may be blocked waiting for us to go online */ | ||
703 | wake_up(&dev->read_wq); | ||
704 | return 0; | ||
705 | } | ||
706 | |||
707 | static void acc_function_disable(struct usb_function *f) | ||
708 | { | ||
709 | struct acc_dev *dev = func_to_dev(f); | ||
710 | struct usb_composite_dev *cdev = dev->cdev; | ||
711 | |||
712 | DBG(cdev, "acc_function_disable\n"); | ||
713 | acc_set_disconnected(dev); | ||
714 | usb_ep_disable(dev->ep_in); | ||
715 | usb_ep_disable(dev->ep_out); | ||
716 | |||
717 | /* readers may be blocked waiting for us to go online */ | ||
718 | wake_up(&dev->read_wq); | ||
719 | |||
720 | VDBG(cdev, "%s disabled\n", dev->function.name); | ||
721 | } | ||
722 | |||
723 | static int acc_bind_config(struct usb_configuration *c) | ||
724 | { | ||
725 | struct acc_dev *dev = _acc_dev; | ||
726 | int ret; | ||
727 | |||
728 | printk(KERN_INFO "acc_bind_config\n"); | ||
729 | |||
730 | /* allocate a string ID for our interface */ | ||
731 | if (acc_string_defs[INTERFACE_STRING_INDEX].id == 0) { | ||
732 | ret = usb_string_id(c->cdev); | ||
733 | if (ret < 0) | ||
734 | return ret; | ||
735 | acc_string_defs[INTERFACE_STRING_INDEX].id = ret; | ||
736 | acc_interface_desc.iInterface = ret; | ||
737 | } | ||
738 | |||
739 | dev->cdev = c->cdev; | ||
740 | dev->function.name = "accessory"; | ||
741 | dev->function.strings = acc_strings, | ||
742 | dev->function.descriptors = fs_acc_descs; | ||
743 | dev->function.hs_descriptors = hs_acc_descs; | ||
744 | dev->function.bind = acc_function_bind; | ||
745 | dev->function.unbind = acc_function_unbind; | ||
746 | dev->function.set_alt = acc_function_set_alt; | ||
747 | dev->function.disable = acc_function_disable; | ||
748 | |||
749 | return usb_add_function(c, &dev->function); | ||
750 | } | ||
751 | |||
752 | static int acc_setup(void) | ||
753 | { | ||
754 | struct acc_dev *dev; | ||
755 | int ret; | ||
756 | |||
757 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); | ||
758 | if (!dev) | ||
759 | return -ENOMEM; | ||
760 | |||
761 | spin_lock_init(&dev->lock); | ||
762 | init_waitqueue_head(&dev->read_wq); | ||
763 | init_waitqueue_head(&dev->write_wq); | ||
764 | atomic_set(&dev->open_excl, 0); | ||
765 | INIT_LIST_HEAD(&dev->tx_idle); | ||
766 | INIT_DELAYED_WORK(&dev->work, acc_work); | ||
767 | |||
768 | /* _acc_dev must be set before calling usb_gadget_register_driver */ | ||
769 | _acc_dev = dev; | ||
770 | |||
771 | ret = misc_register(&acc_device); | ||
772 | if (ret) | ||
773 | goto err; | ||
774 | |||
775 | return 0; | ||
776 | |||
777 | err: | ||
778 | kfree(dev); | ||
779 | printk(KERN_ERR "USB accessory gadget driver failed to initialize\n"); | ||
780 | return ret; | ||
781 | } | ||
782 | |||
783 | static void acc_cleanup(void) | ||
784 | { | ||
785 | misc_deregister(&acc_device); | ||
786 | kfree(_acc_dev); | ||
787 | _acc_dev = NULL; | ||
788 | } | ||