aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorFelipe Balbi <felipe.balbi@nokia.com>2008-08-18 20:39:30 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2008-10-17 17:40:53 -0400
commit3086775a4916b0fe128d924d83f4e7d7c39e4d0e (patch)
treec4e7825ba74da1272d15ad0d61a311b84392b8b6 /drivers/usb
parent60beed95e38793c0baff7f94433c1f639d8d5efd (diff)
usb gadget: cdc obex glue
The following patch introduces a new f_obex.c function driver. It allows userspace obex servers to use usb as transport layer for their messages. [ dbrownell@users.sourceforge.net: various fixes and cleanups ] Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/gadget/Kconfig8
-rw-r--r--drivers/usb/gadget/f_obex.c446
-rw-r--r--drivers/usb/gadget/serial.c14
-rw-r--r--drivers/usb/gadget/u_serial.h1
4 files changed, 467 insertions, 2 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 5dda2dc708db..80a7c02dc951 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -576,19 +576,23 @@ config USB_FILE_STORAGE_TEST
576 normal operation. 576 normal operation.
577 577
578config USB_G_SERIAL 578config USB_G_SERIAL
579 tristate "Serial Gadget (with CDC ACM support)" 579 tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
580 help 580 help
581 The Serial Gadget talks to the Linux-USB generic serial driver. 581 The Serial Gadget talks to the Linux-USB generic serial driver.
582 This driver supports a CDC-ACM module option, which can be used 582 This driver supports a CDC-ACM module option, which can be used
583 to interoperate with MS-Windows hosts or with the Linux-USB 583 to interoperate with MS-Windows hosts or with the Linux-USB
584 "cdc-acm" driver. 584 "cdc-acm" driver.
585 585
586 This driver also supports a CDC-OBEX option. You will need a
587 user space OBEX server talking to /dev/ttyGS*, since the kernel
588 itself doesn't implement the OBEX protocol.
589
586 Say "y" to link the driver statically, or "m" to build a 590 Say "y" to link the driver statically, or "m" to build a
587 dynamically linked module called "g_serial". 591 dynamically linked module called "g_serial".
588 592
589 For more information, see Documentation/usb/gadget_serial.txt 593 For more information, see Documentation/usb/gadget_serial.txt
590 which includes instructions and a "driver info file" needed to 594 which includes instructions and a "driver info file" needed to
591 make MS-Windows work with this driver. 595 make MS-Windows work with CDC ACM.
592 596
593config USB_MIDI_GADGET 597config USB_MIDI_GADGET
594 tristate "MIDI Gadget (EXPERIMENTAL)" 598 tristate "MIDI Gadget (EXPERIMENTAL)"
diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c
new file mode 100644
index 000000000000..86241b2ca8f4
--- /dev/null
+++ b/drivers/usb/gadget/f_obex.c
@@ -0,0 +1,446 @@
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 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* #define VERBOSE_DEBUG */
25
26#include <linux/kernel.h>
27#include <linux/utsname.h>
28#include <linux/device.h>
29
30#include "u_serial.h"
31#include "gadget_chips.h"
32
33
34/*
35 * This CDC OBEX function support just packages a TTY-ish byte stream.
36 * A user mode server will put it into "raw" mode and handle all the
37 * relevant protocol details ... this is just a kernel passthrough.
38 *
39 * REVISIT this driver shouldn't actually activate before that user mode
40 * server is ready to respond! When the "serial gadget" utility code
41 * adds open/close notifications, this driver should use them with new
42 * (TBS) composite gadget hooks that wrap usb_gadget_disconnect() and
43 * usb_gadget_connect() calls with refcounts ... disconnect() when we
44 * bind, then connect() when the user server code is ready to respond.
45 */
46
47struct obex_ep_descs {
48 struct usb_endpoint_descriptor *obex_in;
49 struct usb_endpoint_descriptor *obex_out;
50};
51
52struct f_obex {
53 struct gserial port;
54 u8 ctrl_id;
55 u8 data_id;
56 u8 port_num;
57
58 struct obex_ep_descs fs;
59 struct obex_ep_descs hs;
60};
61
62static inline struct f_obex *func_to_obex(struct usb_function *f)
63{
64 return container_of(f, struct f_obex, port.func);
65}
66
67/*-------------------------------------------------------------------------*/
68
69#define OBEX_CTRL_IDX 0
70#define OBEX_DATA_IDX 1
71
72static struct usb_string obex_string_defs[] = {
73 [OBEX_CTRL_IDX].s = "CDC Object Exchange (OBEX)",
74 [OBEX_DATA_IDX].s = "CDC OBEX Data",
75 { }, /* end of list */
76};
77
78static struct usb_gadget_strings obex_string_table = {
79 .language = 0x0409, /* en-US */
80 .strings = obex_string_defs,
81};
82
83static struct usb_gadget_strings *obex_strings[] = {
84 &obex_string_table,
85 NULL,
86};
87
88/*-------------------------------------------------------------------------*/
89
90static struct usb_interface_descriptor obex_control_intf __initdata = {
91 .bLength = sizeof(obex_control_intf),
92 .bDescriptorType = USB_DT_INTERFACE,
93 .bInterfaceNumber = 0,
94
95 .bAlternateSetting = 0,
96 .bNumEndpoints = 0,
97 .bInterfaceClass = USB_CLASS_COMM,
98 .bInterfaceSubClass = USB_CDC_SUBCLASS_OBEX,
99};
100
101static struct usb_interface_descriptor obex_data_nop_intf __initdata = {
102 .bLength = sizeof(obex_data_nop_intf),
103 .bDescriptorType = USB_DT_INTERFACE,
104 .bInterfaceNumber = 1,
105
106 .bAlternateSetting = 0,
107 .bNumEndpoints = 0,
108 .bInterfaceClass = USB_CLASS_CDC_DATA,
109};
110
111static struct usb_interface_descriptor obex_data_intf __initdata = {
112 .bLength = sizeof(obex_data_intf),
113 .bDescriptorType = USB_DT_INTERFACE,
114 .bInterfaceNumber = 2,
115
116 .bAlternateSetting = 1,
117 .bNumEndpoints = 2,
118 .bInterfaceClass = USB_CLASS_CDC_DATA,
119};
120
121static struct usb_cdc_header_desc obex_cdc_header_desc __initdata = {
122 .bLength = sizeof(obex_cdc_header_desc),
123 .bDescriptorType = USB_DT_CS_INTERFACE,
124 .bDescriptorSubType = USB_CDC_HEADER_TYPE,
125 .bcdCDC = __constant_cpu_to_le16(0x0120),
126};
127
128static struct usb_cdc_union_desc obex_cdc_union_desc __initdata = {
129 .bLength = sizeof(obex_cdc_union_desc),
130 .bDescriptorType = USB_DT_CS_INTERFACE,
131 .bDescriptorSubType = USB_CDC_UNION_TYPE,
132 .bMasterInterface0 = 1,
133 .bSlaveInterface0 = 2,
134};
135
136static struct usb_cdc_obex_desc obex_desc __initdata = {
137 .bLength = sizeof(obex_desc),
138 .bDescriptorType = USB_DT_CS_INTERFACE,
139 .bDescriptorSubType = USB_CDC_OBEX_TYPE,
140 .bcdVersion = __constant_cpu_to_le16(0x0100),
141};
142
143/* High-Speed Support */
144
145static struct usb_endpoint_descriptor obex_hs_ep_out_desc __initdata = {
146 .bLength = USB_DT_ENDPOINT_SIZE,
147 .bDescriptorType = USB_DT_ENDPOINT,
148
149 .bEndpointAddress = USB_DIR_OUT,
150 .bmAttributes = USB_ENDPOINT_XFER_BULK,
151 .wMaxPacketSize = __constant_cpu_to_le16(512),
152};
153
154static struct usb_endpoint_descriptor obex_hs_ep_in_desc __initdata = {
155 .bLength = USB_DT_ENDPOINT_SIZE,
156 .bDescriptorType = USB_DT_ENDPOINT,
157
158 .bEndpointAddress = USB_DIR_IN,
159 .bmAttributes = USB_ENDPOINT_XFER_BULK,
160 .wMaxPacketSize = __constant_cpu_to_le16(512),
161};
162
163static struct usb_descriptor_header *hs_function[] __initdata = {
164 (struct usb_descriptor_header *) &obex_control_intf,
165 (struct usb_descriptor_header *) &obex_cdc_header_desc,
166 (struct usb_descriptor_header *) &obex_desc,
167 (struct usb_descriptor_header *) &obex_cdc_union_desc,
168
169 (struct usb_descriptor_header *) &obex_data_nop_intf,
170 (struct usb_descriptor_header *) &obex_data_intf,
171 (struct usb_descriptor_header *) &obex_hs_ep_in_desc,
172 (struct usb_descriptor_header *) &obex_hs_ep_out_desc,
173 NULL,
174};
175
176/* Full-Speed Support */
177
178static struct usb_endpoint_descriptor obex_fs_ep_in_desc __initdata = {
179 .bLength = USB_DT_ENDPOINT_SIZE,
180 .bDescriptorType = USB_DT_ENDPOINT,
181
182 .bEndpointAddress = USB_DIR_IN,
183 .bmAttributes = USB_ENDPOINT_XFER_BULK,
184};
185
186static struct usb_endpoint_descriptor obex_fs_ep_out_desc __initdata = {
187 .bLength = USB_DT_ENDPOINT_SIZE,
188 .bDescriptorType = USB_DT_ENDPOINT,
189
190 .bEndpointAddress = USB_DIR_OUT,
191 .bmAttributes = USB_ENDPOINT_XFER_BULK,
192};
193
194static struct usb_descriptor_header *fs_function[] __initdata = {
195 (struct usb_descriptor_header *) &obex_control_intf,
196 (struct usb_descriptor_header *) &obex_cdc_header_desc,
197 (struct usb_descriptor_header *) &obex_desc,
198 (struct usb_descriptor_header *) &obex_cdc_union_desc,
199
200 (struct usb_descriptor_header *) &obex_data_nop_intf,
201 (struct usb_descriptor_header *) &obex_data_intf,
202 (struct usb_descriptor_header *) &obex_fs_ep_in_desc,
203 (struct usb_descriptor_header *) &obex_fs_ep_out_desc,
204 NULL,
205};
206
207/*-------------------------------------------------------------------------*/
208
209static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
210{
211 struct f_obex *obex = func_to_obex(f);
212 struct usb_composite_dev *cdev = f->config->cdev;
213
214 if (intf == obex->ctrl_id) {
215 if (alt != 0)
216 goto fail;
217 /* NOP */
218 DBG(cdev, "reset obex ttyGS%d control\n", obex->port_num);
219
220 } else if (intf == obex->data_id) {
221 if (alt > 1)
222 goto fail;
223
224 if (obex->port.in->driver_data) {
225 DBG(cdev, "reset obex ttyGS%d\n", obex->port_num);
226 gserial_disconnect(&obex->port);
227 }
228
229 if (!obex->port.in_desc) {
230 DBG(cdev, "init obex ttyGS%d\n", obex->port_num);
231 obex->port.in_desc = ep_choose(cdev->gadget,
232 obex->hs.obex_in, obex->fs.obex_in);
233 obex->port.out_desc = ep_choose(cdev->gadget,
234 obex->hs.obex_out, obex->fs.obex_out);
235 }
236
237 if (alt == 1) {
238 DBG(cdev, "activate obex ttyGS%d\n", obex->port_num);
239 gserial_connect(&obex->port, obex->port_num);
240 }
241
242 } else
243 goto fail;
244
245 return 0;
246
247fail:
248 return -EINVAL;
249}
250
251static int obex_get_alt(struct usb_function *f, unsigned intf)
252{
253 struct f_obex *obex = func_to_obex(f);
254
255 if (intf == obex->ctrl_id)
256 return 0;
257
258 return obex->port.in->driver_data ? 1 : 0;
259}
260
261static void obex_disable(struct usb_function *f)
262{
263 struct f_obex *obex = func_to_obex(f);
264 struct usb_composite_dev *cdev = f->config->cdev;
265
266 DBG(cdev, "obex ttyGS%d disable\n", obex->port_num);
267 gserial_disconnect(&obex->port);
268}
269
270/*-------------------------------------------------------------------------*/
271
272static int __init
273obex_bind(struct usb_configuration *c, struct usb_function *f)
274{
275 struct usb_composite_dev *cdev = c->cdev;
276 struct f_obex *obex = func_to_obex(f);
277 int status;
278 struct usb_ep *ep;
279
280 /* allocate instance-specific interface IDs, and patch descriptors */
281
282 status = usb_interface_id(c, f);
283 if (status < 0)
284 goto fail;
285 obex->ctrl_id = status;
286
287 obex_control_intf.bInterfaceNumber = status;
288 obex_cdc_union_desc.bMasterInterface0 = status;
289
290 status = usb_interface_id(c, f);
291 if (status < 0)
292 goto fail;
293 obex->data_id = status;
294
295 obex_data_nop_intf.bInterfaceNumber = status;
296 obex_data_intf.bInterfaceNumber = status;
297 obex_cdc_union_desc.bSlaveInterface0 = status;
298
299 /* allocate instance-specific endpoints */
300
301 ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_in_desc);
302 if (!ep)
303 goto fail;
304 obex->port.in = ep;
305 ep->driver_data = cdev; /* claim */
306
307 ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_out_desc);
308 if (!ep)
309 goto fail;
310 obex->port.out = ep;
311 ep->driver_data = cdev; /* claim */
312
313 /* copy descriptors, and track endpoint copies */
314 f->descriptors = usb_copy_descriptors(fs_function);
315
316 obex->fs.obex_in = usb_find_endpoint(fs_function,
317 f->descriptors, &obex_fs_ep_in_desc);
318 obex->fs.obex_out = usb_find_endpoint(fs_function,
319 f->descriptors, &obex_fs_ep_out_desc);
320
321 /* support all relevant hardware speeds... we expect that when
322 * hardware is dual speed, all bulk-capable endpoints work at
323 * both speeds
324 */
325 if (gadget_is_dualspeed(c->cdev->gadget)) {
326
327 obex_hs_ep_in_desc.bEndpointAddress =
328 obex_fs_ep_in_desc.bEndpointAddress;
329 obex_hs_ep_out_desc.bEndpointAddress =
330 obex_fs_ep_out_desc.bEndpointAddress;
331
332 /* copy descriptors, and track endpoint copies */
333 f->hs_descriptors = usb_copy_descriptors(hs_function);
334
335 obex->hs.obex_in = usb_find_endpoint(hs_function,
336 f->descriptors, &obex_hs_ep_in_desc);
337 obex->hs.obex_out = usb_find_endpoint(hs_function,
338 f->descriptors, &obex_hs_ep_out_desc);
339 }
340
341 DBG(cdev, "obex ttyGS%d: %s speed IN/%s OUT/%s\n",
342 obex->port_num,
343 gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
344 obex->port.in->name, obex->port.out->name);
345
346 return 0;
347
348fail:
349 /* we might as well release our claims on endpoints */
350 if (obex->port.out)
351 obex->port.out->driver_data = NULL;
352 if (obex->port.in)
353 obex->port.in->driver_data = NULL;
354
355 ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
356
357 return status;
358}
359
360static void
361obex_unbind(struct usb_configuration *c, struct usb_function *f)
362{
363 if (gadget_is_dualspeed(c->cdev->gadget))
364 usb_free_descriptors(f->hs_descriptors);
365 usb_free_descriptors(f->descriptors);
366 kfree(func_to_obex(f));
367}
368
369/* Some controllers can't support CDC OBEX ... */
370static inline bool can_support_obex(struct usb_configuration *c)
371{
372 /* Since the first interface is a NOP, we can ignore the
373 * issue of multi-interface support on most controllers.
374 *
375 * Altsettings are mandatory, however...
376 */
377 if (!gadget_supports_altsettings(c->cdev->gadget))
378 return false;
379
380 /* everything else is *probably* fine ... */
381 return true;
382}
383
384/**
385 * obex_bind_config - add a CDC OBEX function to a configuration
386 * @c: the configuration to support the CDC OBEX instance
387 * @port_num: /dev/ttyGS* port this interface will use
388 * Context: single threaded during gadget setup
389 *
390 * Returns zero on success, else negative errno.
391 *
392 * Caller must have called @gserial_setup() with enough ports to
393 * handle all the ones it binds. Caller is also responsible
394 * for calling @gserial_cleanup() before module unload.
395 */
396int __init obex_bind_config(struct usb_configuration *c, u8 port_num)
397{
398 struct f_obex *obex;
399 int status;
400
401 if (!can_support_obex(c))
402 return -EINVAL;
403
404 /* maybe allocate device-global string IDs, and patch descriptors */
405 if (obex_string_defs[OBEX_CTRL_IDX].id == 0) {
406 status = usb_string_id(c->cdev);
407 if (status < 0)
408 return status;
409 obex_string_defs[OBEX_CTRL_IDX].id = status;
410
411 obex_control_intf.iInterface = status;
412
413 status = usb_string_id(c->cdev);
414 if (status < 0)
415 return status;
416 obex_string_defs[OBEX_DATA_IDX].id = status;
417
418 obex_data_nop_intf.iInterface =
419 obex_data_intf.iInterface = status;
420 }
421
422 /* allocate and initialize one new instance */
423 obex = kzalloc(sizeof *obex, GFP_KERNEL);
424 if (!obex)
425 return -ENOMEM;
426
427 obex->port_num = port_num;
428
429 obex->port.func.name = "obex";
430 obex->port.func.strings = obex_strings;
431 /* descriptors are per-instance copies */
432 obex->port.func.bind = obex_bind;
433 obex->port.func.unbind = obex_unbind;
434 obex->port.func.set_alt = obex_set_alt;
435 obex->port.func.get_alt = obex_get_alt;
436 obex->port.func.disable = obex_disable;
437
438 status = usb_add_function(c, &obex->port.func);
439 if (status)
440 kfree(obex);
441
442 return status;
443}
444
445MODULE_AUTHOR("Felipe Balbi");
446MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index 3faa7a7022df..2dee848b2f59 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/serial.c
@@ -43,6 +43,7 @@
43#include "epautoconf.c" 43#include "epautoconf.c"
44 44
45#include "f_acm.c" 45#include "f_acm.c"
46#include "f_obex.c"
46#include "f_serial.c" 47#include "f_serial.c"
47#include "u_serial.c" 48#include "u_serial.c"
48 49
@@ -56,6 +57,7 @@
56#define GS_VENDOR_ID 0x0525 /* NetChip */ 57#define GS_VENDOR_ID 0x0525 /* NetChip */
57#define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget */ 58#define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget */
58#define GS_CDC_PRODUCT_ID 0xa4a7 /* ... as CDC-ACM */ 59#define GS_CDC_PRODUCT_ID 0xa4a7 /* ... as CDC-ACM */
60#define GS_CDC_OBEX_PRODUCT_ID 0xa4a9 /* ... as CDC-OBEX */
59 61
60/* string IDs are assigned dynamically */ 62/* string IDs are assigned dynamically */
61 63
@@ -125,6 +127,10 @@ static int use_acm = true;
125module_param(use_acm, bool, 0); 127module_param(use_acm, bool, 0);
126MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes"); 128MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes");
127 129
130static int use_obex = false;
131module_param(use_obex, bool, 0);
132MODULE_PARM_DESC(use_obex, "Use CDC OBEX, default=no");
133
128static unsigned n_ports = 1; 134static unsigned n_ports = 1;
129module_param(n_ports, uint, 0); 135module_param(n_ports, uint, 0);
130MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); 136MODULE_PARM_DESC(n_ports, "number of ports to create, default=1");
@@ -139,6 +145,8 @@ static int __init serial_bind_config(struct usb_configuration *c)
139 for (i = 0; i < n_ports && status == 0; i++) { 145 for (i = 0; i < n_ports && status == 0; i++) {
140 if (use_acm) 146 if (use_acm)
141 status = acm_bind_config(c, i); 147 status = acm_bind_config(c, i);
148 else if (use_obex)
149 status = obex_bind_config(c, i);
142 else 150 else
143 status = gser_bind_config(c, i); 151 status = gser_bind_config(c, i);
144 } 152 }
@@ -249,6 +257,12 @@ static int __init init(void)
249 device_desc.bDeviceClass = USB_CLASS_COMM; 257 device_desc.bDeviceClass = USB_CLASS_COMM;
250 device_desc.idProduct = 258 device_desc.idProduct =
251 __constant_cpu_to_le16(GS_CDC_PRODUCT_ID); 259 __constant_cpu_to_le16(GS_CDC_PRODUCT_ID);
260 } else if (use_obex) {
261 serial_config_driver.label = "CDC OBEX config";
262 serial_config_driver.bConfigurationValue = 3;
263 device_desc.bDeviceClass = USB_CLASS_COMM;
264 device_desc.idProduct =
265 __constant_cpu_to_le16(GS_CDC_OBEX_PRODUCT_ID);
252 } else { 266 } else {
253 serial_config_driver.label = "Generic Serial config"; 267 serial_config_driver.label = "Generic Serial config";
254 serial_config_driver.bConfigurationValue = 1; 268 serial_config_driver.bConfigurationValue = 1;
diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h
index af3910d01aea..300f0ed9475d 100644
--- a/drivers/usb/gadget/u_serial.h
+++ b/drivers/usb/gadget/u_serial.h
@@ -62,5 +62,6 @@ void gserial_disconnect(struct gserial *);
62/* functions are bound to configurations by a config or gadget driver */ 62/* functions are bound to configurations by a config or gadget driver */
63int acm_bind_config(struct usb_configuration *c, u8 port_num); 63int acm_bind_config(struct usb_configuration *c, u8 port_num);
64int gser_bind_config(struct usb_configuration *c, u8 port_num); 64int gser_bind_config(struct usb_configuration *c, u8 port_num);
65int obex_bind_config(struct usb_configuration *c, u8 port_num);
65 66
66#endif /* __U_SERIAL_H */ 67#endif /* __U_SERIAL_H */