Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Felipe Balbi and committed by
Greg Kroah-Hartman
3086775a 60beed95

+479 -2
+3
Documentation/DocBook/gadget.tmpl
··· 557 557 </para> 558 558 559 559 !Edrivers/usb/gadget/f_acm.c 560 + !Edrivers/usb/gadget/f_ecm.c 561 + !Edrivers/usb/gadget/f_subset.c 562 + !Edrivers/usb/gadget/f_obex.c 560 563 !Edrivers/usb/gadget/f_serial.c 561 564 562 565 </sect1>
+6 -2
drivers/usb/gadget/Kconfig
··· 576 576 normal operation. 577 577 578 578 config USB_G_SERIAL 579 - tristate "Serial Gadget (with CDC ACM support)" 579 + tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" 580 580 help 581 581 The Serial Gadget talks to the Linux-USB generic serial driver. 582 582 This driver supports a CDC-ACM module option, which can be used 583 583 to interoperate with MS-Windows hosts or with the Linux-USB 584 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 590 Say "y" to link the driver statically, or "m" to build a 587 591 dynamically linked module called "g_serial". 588 592 589 593 For more information, see Documentation/usb/gadget_serial.txt 590 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 593 597 config USB_MIDI_GADGET 594 598 tristate "MIDI Gadget (EXPERIMENTAL)"
+446
drivers/usb/gadget/f_obex.c
··· 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 + 47 + struct obex_ep_descs { 48 + struct usb_endpoint_descriptor *obex_in; 49 + struct usb_endpoint_descriptor *obex_out; 50 + }; 51 + 52 + struct 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 + 62 + static 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 + 72 + static 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 + 78 + static struct usb_gadget_strings obex_string_table = { 79 + .language = 0x0409, /* en-US */ 80 + .strings = obex_string_defs, 81 + }; 82 + 83 + static struct usb_gadget_strings *obex_strings[] = { 84 + &obex_string_table, 85 + NULL, 86 + }; 87 + 88 + /*-------------------------------------------------------------------------*/ 89 + 90 + static 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 + 101 + static 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 + 111 + static 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 + 121 + static 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 + 128 + static 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 + 136 + static 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 + 145 + static 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 + 154 + static 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 + 163 + static 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 + 178 + static 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 + 186 + static 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 + 194 + static 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 + 209 + static 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 + 247 + fail: 248 + return -EINVAL; 249 + } 250 + 251 + static 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 + 261 + static 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 + 272 + static int __init 273 + obex_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 + 348 + fail: 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 + 360 + static void 361 + obex_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 ... */ 370 + static 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 + */ 396 + int __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 + 445 + MODULE_AUTHOR("Felipe Balbi"); 446 + MODULE_LICENSE("GPL");
+14
drivers/usb/gadget/serial.c
··· 43 43 #include "epautoconf.c" 44 44 45 45 #include "f_acm.c" 46 + #include "f_obex.c" 46 47 #include "f_serial.c" 47 48 #include "u_serial.c" 48 49 ··· 57 56 #define GS_VENDOR_ID 0x0525 /* NetChip */ 58 57 #define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget */ 59 58 #define GS_CDC_PRODUCT_ID 0xa4a7 /* ... as CDC-ACM */ 59 + #define GS_CDC_OBEX_PRODUCT_ID 0xa4a9 /* ... as CDC-OBEX */ 60 60 61 61 /* string IDs are assigned dynamically */ 62 62 ··· 127 125 module_param(use_acm, bool, 0); 128 126 MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes"); 129 127 128 + static int use_obex = false; 129 + module_param(use_obex, bool, 0); 130 + MODULE_PARM_DESC(use_obex, "Use CDC OBEX, default=no"); 131 + 130 132 static unsigned n_ports = 1; 131 133 module_param(n_ports, uint, 0); 132 134 MODULE_PARM_DESC(n_ports, "number of ports to create, default=1"); ··· 145 139 for (i = 0; i < n_ports && status == 0; i++) { 146 140 if (use_acm) 147 141 status = acm_bind_config(c, i); 142 + else if (use_obex) 143 + status = obex_bind_config(c, i); 148 144 else 149 145 status = gser_bind_config(c, i); 150 146 } ··· 257 249 device_desc.bDeviceClass = USB_CLASS_COMM; 258 250 device_desc.idProduct = 259 251 __constant_cpu_to_le16(GS_CDC_PRODUCT_ID); 252 + } else if (use_obex) { 253 + serial_config_driver.label = "CDC OBEX config"; 254 + serial_config_driver.bConfigurationValue = 3; 255 + device_desc.bDeviceClass = USB_CLASS_COMM; 256 + device_desc.idProduct = 257 + __constant_cpu_to_le16(GS_CDC_OBEX_PRODUCT_ID); 260 258 } else { 261 259 serial_config_driver.label = "Generic Serial config"; 262 260 serial_config_driver.bConfigurationValue = 1;
+1
drivers/usb/gadget/u_serial.h
··· 62 62 /* functions are bound to configurations by a config or gadget driver */ 63 63 int acm_bind_config(struct usb_configuration *c, u8 port_num); 64 64 int gser_bind_config(struct usb_configuration *c, u8 port_num); 65 + int obex_bind_config(struct usb_configuration *c, u8 port_num); 65 66 66 67 #endif /* __U_SERIAL_H */
+9
include/linux/usb/cdc.h
··· 160 160 __u8 bDetailData[0]; 161 161 } __attribute__ ((packed)); 162 162 163 + /* "OBEX Control Model Functional Descriptor" */ 164 + struct usb_cdc_obex_desc { 165 + __u8 bLength; 166 + __u8 bDescriptorType; 167 + __u8 bDescriptorSubType; 168 + 169 + __le16 bcdVersion; 170 + } __attribute__ ((packed)); 171 + 163 172 /*-------------------------------------------------------------------------*/ 164 173 165 174 /*