diff options
Diffstat (limited to 'drivers/usb/gadget/function/f_obex.c')
-rw-r--r-- | drivers/usb/gadget/function/f_obex.c | 533 |
1 files changed, 533 insertions, 0 deletions
diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c new file mode 100644 index 000000000000..aebae1853bce --- /dev/null +++ b/drivers/usb/gadget/function/f_obex.c | |||
@@ -0,0 +1,533 @@ | |||
1 | /* | ||
2 | * f_obex.c -- USB CDC OBEX function driver | ||
3 | * | ||
4 | * Copyright (C) 2008 Nokia Corporation | ||
5 | * Contact: Felipe Balbi <felipe.balbi@nokia.com> | ||
6 | * | ||
7 | * Based on f_acm.c by Al Borchers and David Brownell. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | */ | ||
14 | |||
15 | /* #define VERBOSE_DEBUG */ | ||
16 | |||
17 | #include <linux/slab.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/device.h> | ||
20 | #include <linux/module.h> | ||
21 | |||
22 | #include "u_serial.h" | ||
23 | #include "gadget_chips.h" | ||
24 | |||
25 | |||
26 | /* | ||
27 | * This CDC OBEX function support just packages a TTY-ish byte stream. | ||
28 | * A user mode server will put it into "raw" mode and handle all the | ||
29 | * relevant protocol details ... this is just a kernel passthrough. | ||
30 | * When possible, we prevent gadget enumeration until that server is | ||
31 | * ready to handle the commands. | ||
32 | */ | ||
33 | |||
34 | struct f_obex { | ||
35 | struct gserial port; | ||
36 | u8 ctrl_id; | ||
37 | u8 data_id; | ||
38 | u8 port_num; | ||
39 | u8 can_activate; | ||
40 | }; | ||
41 | |||
42 | static inline struct f_obex *func_to_obex(struct usb_function *f) | ||
43 | { | ||
44 | return container_of(f, struct f_obex, port.func); | ||
45 | } | ||
46 | |||
47 | static inline struct f_obex *port_to_obex(struct gserial *p) | ||
48 | { | ||
49 | return container_of(p, struct f_obex, port); | ||
50 | } | ||
51 | |||
52 | /*-------------------------------------------------------------------------*/ | ||
53 | |||
54 | #define OBEX_CTRL_IDX 0 | ||
55 | #define OBEX_DATA_IDX 1 | ||
56 | |||
57 | static struct usb_string obex_string_defs[] = { | ||
58 | [OBEX_CTRL_IDX].s = "CDC Object Exchange (OBEX)", | ||
59 | [OBEX_DATA_IDX].s = "CDC OBEX Data", | ||
60 | { }, /* end of list */ | ||
61 | }; | ||
62 | |||
63 | static struct usb_gadget_strings obex_string_table = { | ||
64 | .language = 0x0409, /* en-US */ | ||
65 | .strings = obex_string_defs, | ||
66 | }; | ||
67 | |||
68 | static struct usb_gadget_strings *obex_strings[] = { | ||
69 | &obex_string_table, | ||
70 | NULL, | ||
71 | }; | ||
72 | |||
73 | /*-------------------------------------------------------------------------*/ | ||
74 | |||
75 | static struct usb_interface_descriptor obex_control_intf = { | ||
76 | .bLength = sizeof(obex_control_intf), | ||
77 | .bDescriptorType = USB_DT_INTERFACE, | ||
78 | .bInterfaceNumber = 0, | ||
79 | |||
80 | .bAlternateSetting = 0, | ||
81 | .bNumEndpoints = 0, | ||
82 | .bInterfaceClass = USB_CLASS_COMM, | ||
83 | .bInterfaceSubClass = USB_CDC_SUBCLASS_OBEX, | ||
84 | }; | ||
85 | |||
86 | static struct usb_interface_descriptor obex_data_nop_intf = { | ||
87 | .bLength = sizeof(obex_data_nop_intf), | ||
88 | .bDescriptorType = USB_DT_INTERFACE, | ||
89 | .bInterfaceNumber = 1, | ||
90 | |||
91 | .bAlternateSetting = 0, | ||
92 | .bNumEndpoints = 0, | ||
93 | .bInterfaceClass = USB_CLASS_CDC_DATA, | ||
94 | }; | ||
95 | |||
96 | static struct usb_interface_descriptor obex_data_intf = { | ||
97 | .bLength = sizeof(obex_data_intf), | ||
98 | .bDescriptorType = USB_DT_INTERFACE, | ||
99 | .bInterfaceNumber = 2, | ||
100 | |||
101 | .bAlternateSetting = 1, | ||
102 | .bNumEndpoints = 2, | ||
103 | .bInterfaceClass = USB_CLASS_CDC_DATA, | ||
104 | }; | ||
105 | |||
106 | static struct usb_cdc_header_desc obex_cdc_header_desc = { | ||
107 | .bLength = sizeof(obex_cdc_header_desc), | ||
108 | .bDescriptorType = USB_DT_CS_INTERFACE, | ||
109 | .bDescriptorSubType = USB_CDC_HEADER_TYPE, | ||
110 | .bcdCDC = cpu_to_le16(0x0120), | ||
111 | }; | ||
112 | |||
113 | static struct usb_cdc_union_desc obex_cdc_union_desc = { | ||
114 | .bLength = sizeof(obex_cdc_union_desc), | ||
115 | .bDescriptorType = USB_DT_CS_INTERFACE, | ||
116 | .bDescriptorSubType = USB_CDC_UNION_TYPE, | ||
117 | .bMasterInterface0 = 1, | ||
118 | .bSlaveInterface0 = 2, | ||
119 | }; | ||
120 | |||
121 | static struct usb_cdc_obex_desc obex_desc = { | ||
122 | .bLength = sizeof(obex_desc), | ||
123 | .bDescriptorType = USB_DT_CS_INTERFACE, | ||
124 | .bDescriptorSubType = USB_CDC_OBEX_TYPE, | ||
125 | .bcdVersion = cpu_to_le16(0x0100), | ||
126 | }; | ||
127 | |||
128 | /* High-Speed Support */ | ||
129 | |||
130 | static struct usb_endpoint_descriptor obex_hs_ep_out_desc = { | ||
131 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
132 | .bDescriptorType = USB_DT_ENDPOINT, | ||
133 | |||
134 | .bEndpointAddress = USB_DIR_OUT, | ||
135 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
136 | .wMaxPacketSize = cpu_to_le16(512), | ||
137 | }; | ||
138 | |||
139 | static struct usb_endpoint_descriptor obex_hs_ep_in_desc = { | ||
140 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
141 | .bDescriptorType = USB_DT_ENDPOINT, | ||
142 | |||
143 | .bEndpointAddress = USB_DIR_IN, | ||
144 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
145 | .wMaxPacketSize = cpu_to_le16(512), | ||
146 | }; | ||
147 | |||
148 | static struct usb_descriptor_header *hs_function[] = { | ||
149 | (struct usb_descriptor_header *) &obex_control_intf, | ||
150 | (struct usb_descriptor_header *) &obex_cdc_header_desc, | ||
151 | (struct usb_descriptor_header *) &obex_desc, | ||
152 | (struct usb_descriptor_header *) &obex_cdc_union_desc, | ||
153 | |||
154 | (struct usb_descriptor_header *) &obex_data_nop_intf, | ||
155 | (struct usb_descriptor_header *) &obex_data_intf, | ||
156 | (struct usb_descriptor_header *) &obex_hs_ep_in_desc, | ||
157 | (struct usb_descriptor_header *) &obex_hs_ep_out_desc, | ||
158 | NULL, | ||
159 | }; | ||
160 | |||
161 | /* Full-Speed Support */ | ||
162 | |||
163 | static struct usb_endpoint_descriptor obex_fs_ep_in_desc = { | ||
164 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
165 | .bDescriptorType = USB_DT_ENDPOINT, | ||
166 | |||
167 | .bEndpointAddress = USB_DIR_IN, | ||
168 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
169 | }; | ||
170 | |||
171 | static struct usb_endpoint_descriptor obex_fs_ep_out_desc = { | ||
172 | .bLength = USB_DT_ENDPOINT_SIZE, | ||
173 | .bDescriptorType = USB_DT_ENDPOINT, | ||
174 | |||
175 | .bEndpointAddress = USB_DIR_OUT, | ||
176 | .bmAttributes = USB_ENDPOINT_XFER_BULK, | ||
177 | }; | ||
178 | |||
179 | static struct usb_descriptor_header *fs_function[] = { | ||
180 | (struct usb_descriptor_header *) &obex_control_intf, | ||
181 | (struct usb_descriptor_header *) &obex_cdc_header_desc, | ||
182 | (struct usb_descriptor_header *) &obex_desc, | ||
183 | (struct usb_descriptor_header *) &obex_cdc_union_desc, | ||
184 | |||
185 | (struct usb_descriptor_header *) &obex_data_nop_intf, | ||
186 | (struct usb_descriptor_header *) &obex_data_intf, | ||
187 | (struct usb_descriptor_header *) &obex_fs_ep_in_desc, | ||
188 | (struct usb_descriptor_header *) &obex_fs_ep_out_desc, | ||
189 | NULL, | ||
190 | }; | ||
191 | |||
192 | /*-------------------------------------------------------------------------*/ | ||
193 | |||
194 | static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt) | ||
195 | { | ||
196 | struct f_obex *obex = func_to_obex(f); | ||
197 | struct usb_composite_dev *cdev = f->config->cdev; | ||
198 | |||
199 | if (intf == obex->ctrl_id) { | ||
200 | if (alt != 0) | ||
201 | goto fail; | ||
202 | /* NOP */ | ||
203 | DBG(cdev, "reset obex ttyGS%d control\n", obex->port_num); | ||
204 | |||
205 | } else if (intf == obex->data_id) { | ||
206 | if (alt > 1) | ||
207 | goto fail; | ||
208 | |||
209 | if (obex->port.in->driver_data) { | ||
210 | DBG(cdev, "reset obex ttyGS%d\n", obex->port_num); | ||
211 | gserial_disconnect(&obex->port); | ||
212 | } | ||
213 | |||
214 | if (!obex->port.in->desc || !obex->port.out->desc) { | ||
215 | DBG(cdev, "init obex ttyGS%d\n", obex->port_num); | ||
216 | if (config_ep_by_speed(cdev->gadget, f, | ||
217 | obex->port.in) || | ||
218 | config_ep_by_speed(cdev->gadget, f, | ||
219 | obex->port.out)) { | ||
220 | obex->port.out->desc = NULL; | ||
221 | obex->port.in->desc = NULL; | ||
222 | goto fail; | ||
223 | } | ||
224 | } | ||
225 | |||
226 | if (alt == 1) { | ||
227 | DBG(cdev, "activate obex ttyGS%d\n", obex->port_num); | ||
228 | gserial_connect(&obex->port, obex->port_num); | ||
229 | } | ||
230 | |||
231 | } else | ||
232 | goto fail; | ||
233 | |||
234 | return 0; | ||
235 | |||
236 | fail: | ||
237 | return -EINVAL; | ||
238 | } | ||
239 | |||
240 | static int obex_get_alt(struct usb_function *f, unsigned intf) | ||
241 | { | ||
242 | struct f_obex *obex = func_to_obex(f); | ||
243 | |||
244 | if (intf == obex->ctrl_id) | ||
245 | return 0; | ||
246 | |||
247 | return obex->port.in->driver_data ? 1 : 0; | ||
248 | } | ||
249 | |||
250 | static void obex_disable(struct usb_function *f) | ||
251 | { | ||
252 | struct f_obex *obex = func_to_obex(f); | ||
253 | struct usb_composite_dev *cdev = f->config->cdev; | ||
254 | |||
255 | DBG(cdev, "obex ttyGS%d disable\n", obex->port_num); | ||
256 | gserial_disconnect(&obex->port); | ||
257 | } | ||
258 | |||
259 | /*-------------------------------------------------------------------------*/ | ||
260 | |||
261 | static void obex_connect(struct gserial *g) | ||
262 | { | ||
263 | struct f_obex *obex = port_to_obex(g); | ||
264 | struct usb_composite_dev *cdev = g->func.config->cdev; | ||
265 | int status; | ||
266 | |||
267 | if (!obex->can_activate) | ||
268 | return; | ||
269 | |||
270 | status = usb_function_activate(&g->func); | ||
271 | if (status) | ||
272 | DBG(cdev, "obex ttyGS%d function activate --> %d\n", | ||
273 | obex->port_num, status); | ||
274 | } | ||
275 | |||
276 | static void obex_disconnect(struct gserial *g) | ||
277 | { | ||
278 | struct f_obex *obex = port_to_obex(g); | ||
279 | struct usb_composite_dev *cdev = g->func.config->cdev; | ||
280 | int status; | ||
281 | |||
282 | if (!obex->can_activate) | ||
283 | return; | ||
284 | |||
285 | status = usb_function_deactivate(&g->func); | ||
286 | if (status) | ||
287 | DBG(cdev, "obex ttyGS%d function deactivate --> %d\n", | ||
288 | obex->port_num, status); | ||
289 | } | ||
290 | |||
291 | /*-------------------------------------------------------------------------*/ | ||
292 | |||
293 | /* Some controllers can't support CDC OBEX ... */ | ||
294 | static inline bool can_support_obex(struct usb_configuration *c) | ||
295 | { | ||
296 | /* Since the first interface is a NOP, we can ignore the | ||
297 | * issue of multi-interface support on most controllers. | ||
298 | * | ||
299 | * Altsettings are mandatory, however... | ||
300 | */ | ||
301 | if (!gadget_supports_altsettings(c->cdev->gadget)) | ||
302 | return false; | ||
303 | |||
304 | /* everything else is *probably* fine ... */ | ||
305 | return true; | ||
306 | } | ||
307 | |||
308 | static int obex_bind(struct usb_configuration *c, struct usb_function *f) | ||
309 | { | ||
310 | struct usb_composite_dev *cdev = c->cdev; | ||
311 | struct f_obex *obex = func_to_obex(f); | ||
312 | struct usb_string *us; | ||
313 | int status; | ||
314 | struct usb_ep *ep; | ||
315 | |||
316 | if (!can_support_obex(c)) | ||
317 | return -EINVAL; | ||
318 | |||
319 | us = usb_gstrings_attach(cdev, obex_strings, | ||
320 | ARRAY_SIZE(obex_string_defs)); | ||
321 | if (IS_ERR(us)) | ||
322 | return PTR_ERR(us); | ||
323 | obex_control_intf.iInterface = us[OBEX_CTRL_IDX].id; | ||
324 | obex_data_nop_intf.iInterface = us[OBEX_DATA_IDX].id; | ||
325 | obex_data_intf.iInterface = us[OBEX_DATA_IDX].id; | ||
326 | |||
327 | /* allocate instance-specific interface IDs, and patch descriptors */ | ||
328 | |||
329 | status = usb_interface_id(c, f); | ||
330 | if (status < 0) | ||
331 | goto fail; | ||
332 | obex->ctrl_id = status; | ||
333 | |||
334 | obex_control_intf.bInterfaceNumber = status; | ||
335 | obex_cdc_union_desc.bMasterInterface0 = status; | ||
336 | |||
337 | status = usb_interface_id(c, f); | ||
338 | if (status < 0) | ||
339 | goto fail; | ||
340 | obex->data_id = status; | ||
341 | |||
342 | obex_data_nop_intf.bInterfaceNumber = status; | ||
343 | obex_data_intf.bInterfaceNumber = status; | ||
344 | obex_cdc_union_desc.bSlaveInterface0 = status; | ||
345 | |||
346 | /* allocate instance-specific endpoints */ | ||
347 | |||
348 | status = -ENODEV; | ||
349 | ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_in_desc); | ||
350 | if (!ep) | ||
351 | goto fail; | ||
352 | obex->port.in = ep; | ||
353 | ep->driver_data = cdev; /* claim */ | ||
354 | |||
355 | ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_out_desc); | ||
356 | if (!ep) | ||
357 | goto fail; | ||
358 | obex->port.out = ep; | ||
359 | ep->driver_data = cdev; /* claim */ | ||
360 | |||
361 | /* support all relevant hardware speeds... we expect that when | ||
362 | * hardware is dual speed, all bulk-capable endpoints work at | ||
363 | * both speeds | ||
364 | */ | ||
365 | |||
366 | obex_hs_ep_in_desc.bEndpointAddress = | ||
367 | obex_fs_ep_in_desc.bEndpointAddress; | ||
368 | obex_hs_ep_out_desc.bEndpointAddress = | ||
369 | obex_fs_ep_out_desc.bEndpointAddress; | ||
370 | |||
371 | status = usb_assign_descriptors(f, fs_function, hs_function, NULL); | ||
372 | if (status) | ||
373 | goto fail; | ||
374 | |||
375 | /* Avoid letting this gadget enumerate until the userspace | ||
376 | * OBEX server is active. | ||
377 | */ | ||
378 | status = usb_function_deactivate(f); | ||
379 | if (status < 0) | ||
380 | WARNING(cdev, "obex ttyGS%d: can't prevent enumeration, %d\n", | ||
381 | obex->port_num, status); | ||
382 | else | ||
383 | obex->can_activate = true; | ||
384 | |||
385 | |||
386 | DBG(cdev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n", | ||
387 | obex->port_num, | ||
388 | gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", | ||
389 | obex->port.in->name, obex->port.out->name); | ||
390 | |||
391 | return 0; | ||
392 | |||
393 | fail: | ||
394 | usb_free_all_descriptors(f); | ||
395 | /* we might as well release our claims on endpoints */ | ||
396 | if (obex->port.out) | ||
397 | obex->port.out->driver_data = NULL; | ||
398 | if (obex->port.in) | ||
399 | obex->port.in->driver_data = NULL; | ||
400 | |||
401 | ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status); | ||
402 | |||
403 | return status; | ||
404 | } | ||
405 | |||
406 | static inline struct f_serial_opts *to_f_serial_opts(struct config_item *item) | ||
407 | { | ||
408 | return container_of(to_config_group(item), struct f_serial_opts, | ||
409 | func_inst.group); | ||
410 | } | ||
411 | |||
412 | CONFIGFS_ATTR_STRUCT(f_serial_opts); | ||
413 | static ssize_t f_obex_attr_show(struct config_item *item, | ||
414 | struct configfs_attribute *attr, | ||
415 | char *page) | ||
416 | { | ||
417 | struct f_serial_opts *opts = to_f_serial_opts(item); | ||
418 | struct f_serial_opts_attribute *f_serial_opts_attr = | ||
419 | container_of(attr, struct f_serial_opts_attribute, attr); | ||
420 | ssize_t ret = 0; | ||
421 | |||
422 | if (f_serial_opts_attr->show) | ||
423 | ret = f_serial_opts_attr->show(opts, page); | ||
424 | |||
425 | return ret; | ||
426 | } | ||
427 | |||
428 | static void obex_attr_release(struct config_item *item) | ||
429 | { | ||
430 | struct f_serial_opts *opts = to_f_serial_opts(item); | ||
431 | |||
432 | usb_put_function_instance(&opts->func_inst); | ||
433 | } | ||
434 | |||
435 | static struct configfs_item_operations obex_item_ops = { | ||
436 | .release = obex_attr_release, | ||
437 | .show_attribute = f_obex_attr_show, | ||
438 | }; | ||
439 | |||
440 | static ssize_t f_obex_port_num_show(struct f_serial_opts *opts, char *page) | ||
441 | { | ||
442 | return sprintf(page, "%u\n", opts->port_num); | ||
443 | } | ||
444 | |||
445 | static struct f_serial_opts_attribute f_obex_port_num = | ||
446 | __CONFIGFS_ATTR_RO(port_num, f_obex_port_num_show); | ||
447 | |||
448 | static struct configfs_attribute *acm_attrs[] = { | ||
449 | &f_obex_port_num.attr, | ||
450 | NULL, | ||
451 | }; | ||
452 | |||
453 | static struct config_item_type obex_func_type = { | ||
454 | .ct_item_ops = &obex_item_ops, | ||
455 | .ct_attrs = acm_attrs, | ||
456 | .ct_owner = THIS_MODULE, | ||
457 | }; | ||
458 | |||
459 | static void obex_free_inst(struct usb_function_instance *f) | ||
460 | { | ||
461 | struct f_serial_opts *opts; | ||
462 | |||
463 | opts = container_of(f, struct f_serial_opts, func_inst); | ||
464 | gserial_free_line(opts->port_num); | ||
465 | kfree(opts); | ||
466 | } | ||
467 | |||
468 | static struct usb_function_instance *obex_alloc_inst(void) | ||
469 | { | ||
470 | struct f_serial_opts *opts; | ||
471 | int ret; | ||
472 | |||
473 | opts = kzalloc(sizeof(*opts), GFP_KERNEL); | ||
474 | if (!opts) | ||
475 | return ERR_PTR(-ENOMEM); | ||
476 | |||
477 | opts->func_inst.free_func_inst = obex_free_inst; | ||
478 | ret = gserial_alloc_line(&opts->port_num); | ||
479 | if (ret) { | ||
480 | kfree(opts); | ||
481 | return ERR_PTR(ret); | ||
482 | } | ||
483 | config_group_init_type_name(&opts->func_inst.group, "", | ||
484 | &obex_func_type); | ||
485 | |||
486 | return &opts->func_inst; | ||
487 | } | ||
488 | |||
489 | static void obex_free(struct usb_function *f) | ||
490 | { | ||
491 | struct f_obex *obex; | ||
492 | |||
493 | obex = func_to_obex(f); | ||
494 | kfree(obex); | ||
495 | } | ||
496 | |||
497 | static void obex_unbind(struct usb_configuration *c, struct usb_function *f) | ||
498 | { | ||
499 | usb_free_all_descriptors(f); | ||
500 | } | ||
501 | |||
502 | static struct usb_function *obex_alloc(struct usb_function_instance *fi) | ||
503 | { | ||
504 | struct f_obex *obex; | ||
505 | struct f_serial_opts *opts; | ||
506 | |||
507 | /* allocate and initialize one new instance */ | ||
508 | obex = kzalloc(sizeof(*obex), GFP_KERNEL); | ||
509 | if (!obex) | ||
510 | return ERR_PTR(-ENOMEM); | ||
511 | |||
512 | opts = container_of(fi, struct f_serial_opts, func_inst); | ||
513 | |||
514 | obex->port_num = opts->port_num; | ||
515 | |||
516 | obex->port.connect = obex_connect; | ||
517 | obex->port.disconnect = obex_disconnect; | ||
518 | |||
519 | obex->port.func.name = "obex"; | ||
520 | /* descriptors are per-instance copies */ | ||
521 | obex->port.func.bind = obex_bind; | ||
522 | obex->port.func.unbind = obex_unbind; | ||
523 | obex->port.func.set_alt = obex_set_alt; | ||
524 | obex->port.func.get_alt = obex_get_alt; | ||
525 | obex->port.func.disable = obex_disable; | ||
526 | obex->port.func.free_func = obex_free; | ||
527 | |||
528 | return &obex->port.func; | ||
529 | } | ||
530 | |||
531 | DECLARE_USB_FUNCTION_INIT(obex, obex_alloc_inst, obex_alloc); | ||
532 | MODULE_AUTHOR("Felipe Balbi"); | ||
533 | MODULE_LICENSE("GPL"); | ||