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

Phonet: USB CDC Phonet function for gadget framework

This implements the Nokia vendor-specific communication device class
function to exchange Phonet messages over USB. This function is already
found in the "PC suite" USB profile of (non-Linux) Nokia handsets.

Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Rémi Denis-Courmont and committed by
David S. Miller
9732d523 893873f3

+642
+621
drivers/usb/gadget/f_phonet.c
··· 1 + /* 2 + * f_phonet.c -- USB CDC Phonet function 3 + * 4 + * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved. 5 + * 6 + * Author: Rémi Denis-Courmont 7 + * 8 + * This program is free software; you can redistribute it and/or 9 + * modify it under the terms of the GNU General Public License 10 + * version 2 as published by the Free Software Foundation. 11 + * 12 + * This program is distributed in the hope that it will be useful, but 13 + * WITHOUT ANY WARRANTY; without even the implied warranty of 14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 15 + * General Public License for more details. 16 + * 17 + * You should have received a copy of the GNU General Public License 18 + * along with this program; if not, write to the Free Software 19 + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 20 + * 02110-1301 USA 21 + */ 22 + 23 + #include <linux/kernel.h> 24 + #include <linux/device.h> 25 + 26 + #include <linux/netdevice.h> 27 + #include <linux/if_ether.h> 28 + #include <linux/if_phonet.h> 29 + #include <linux/if_arp.h> 30 + 31 + #include <linux/usb/ch9.h> 32 + #include <linux/usb/cdc.h> 33 + #include <linux/usb/composite.h> 34 + 35 + #include "u_phonet.h" 36 + 37 + #define PN_MEDIA_USB 0x1B 38 + 39 + /*-------------------------------------------------------------------------*/ 40 + 41 + struct phonet_port { 42 + struct f_phonet *usb; 43 + spinlock_t lock; 44 + }; 45 + 46 + struct f_phonet { 47 + struct usb_function function; 48 + struct net_device *dev; 49 + struct usb_ep *in_ep, *out_ep; 50 + 51 + struct usb_request *in_req; 52 + struct usb_request *out_reqv[0]; 53 + }; 54 + 55 + static int phonet_rxq_size = 2; 56 + 57 + static inline struct f_phonet *func_to_pn(struct usb_function *f) 58 + { 59 + return container_of(f, struct f_phonet, function); 60 + } 61 + 62 + /*-------------------------------------------------------------------------*/ 63 + 64 + #define USB_CDC_SUBCLASS_PHONET 0xfe 65 + #define USB_CDC_PHONET_TYPE 0xab 66 + 67 + static struct usb_interface_descriptor 68 + pn_control_intf_desc = { 69 + .bLength = sizeof pn_control_intf_desc, 70 + .bDescriptorType = USB_DT_INTERFACE, 71 + 72 + /* .bInterfaceNumber = DYNAMIC, */ 73 + .bInterfaceClass = USB_CLASS_COMM, 74 + .bInterfaceSubClass = USB_CDC_SUBCLASS_PHONET, 75 + }; 76 + 77 + static const struct usb_cdc_header_desc 78 + pn_header_desc = { 79 + .bLength = sizeof pn_header_desc, 80 + .bDescriptorType = USB_DT_CS_INTERFACE, 81 + .bDescriptorSubType = USB_CDC_HEADER_TYPE, 82 + .bcdCDC = __constant_cpu_to_le16(0x0110), 83 + }; 84 + 85 + static const struct usb_cdc_header_desc 86 + pn_phonet_desc = { 87 + .bLength = sizeof pn_phonet_desc, 88 + .bDescriptorType = USB_DT_CS_INTERFACE, 89 + .bDescriptorSubType = USB_CDC_PHONET_TYPE, 90 + .bcdCDC = __constant_cpu_to_le16(0x1505), /* ??? */ 91 + }; 92 + 93 + static struct usb_cdc_union_desc 94 + pn_union_desc = { 95 + .bLength = sizeof pn_union_desc, 96 + .bDescriptorType = USB_DT_CS_INTERFACE, 97 + .bDescriptorSubType = USB_CDC_UNION_TYPE, 98 + 99 + /* .bMasterInterface0 = DYNAMIC, */ 100 + /* .bSlaveInterface0 = DYNAMIC, */ 101 + }; 102 + 103 + static struct usb_interface_descriptor 104 + pn_data_nop_intf_desc = { 105 + .bLength = sizeof pn_data_nop_intf_desc, 106 + .bDescriptorType = USB_DT_INTERFACE, 107 + 108 + /* .bInterfaceNumber = DYNAMIC, */ 109 + .bAlternateSetting = 0, 110 + .bNumEndpoints = 0, 111 + .bInterfaceClass = USB_CLASS_CDC_DATA, 112 + }; 113 + 114 + static struct usb_interface_descriptor 115 + pn_data_intf_desc = { 116 + .bLength = sizeof pn_data_intf_desc, 117 + .bDescriptorType = USB_DT_INTERFACE, 118 + 119 + /* .bInterfaceNumber = DYNAMIC, */ 120 + .bAlternateSetting = 1, 121 + .bNumEndpoints = 2, 122 + .bInterfaceClass = USB_CLASS_CDC_DATA, 123 + }; 124 + 125 + static struct usb_endpoint_descriptor 126 + pn_fs_sink_desc = { 127 + .bLength = USB_DT_ENDPOINT_SIZE, 128 + .bDescriptorType = USB_DT_ENDPOINT, 129 + 130 + .bEndpointAddress = USB_DIR_OUT, 131 + .bmAttributes = USB_ENDPOINT_XFER_BULK, 132 + }; 133 + 134 + static struct usb_endpoint_descriptor 135 + pn_hs_sink_desc = { 136 + .bLength = USB_DT_ENDPOINT_SIZE, 137 + .bDescriptorType = USB_DT_ENDPOINT, 138 + 139 + .bEndpointAddress = USB_DIR_OUT, 140 + .bmAttributes = USB_ENDPOINT_XFER_BULK, 141 + .wMaxPacketSize = __constant_cpu_to_le16(512), 142 + }; 143 + 144 + static struct usb_endpoint_descriptor 145 + pn_fs_source_desc = { 146 + .bLength = USB_DT_ENDPOINT_SIZE, 147 + .bDescriptorType = USB_DT_ENDPOINT, 148 + 149 + .bEndpointAddress = USB_DIR_IN, 150 + .bmAttributes = USB_ENDPOINT_XFER_BULK, 151 + }; 152 + 153 + static struct usb_endpoint_descriptor 154 + pn_hs_source_desc = { 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 *fs_pn_function[] = { 164 + (struct usb_descriptor_header *) &pn_control_intf_desc, 165 + (struct usb_descriptor_header *) &pn_header_desc, 166 + (struct usb_descriptor_header *) &pn_phonet_desc, 167 + (struct usb_descriptor_header *) &pn_union_desc, 168 + (struct usb_descriptor_header *) &pn_data_nop_intf_desc, 169 + (struct usb_descriptor_header *) &pn_data_intf_desc, 170 + (struct usb_descriptor_header *) &pn_fs_sink_desc, 171 + (struct usb_descriptor_header *) &pn_fs_source_desc, 172 + NULL, 173 + }; 174 + 175 + static struct usb_descriptor_header *hs_pn_function[] = { 176 + (struct usb_descriptor_header *) &pn_control_intf_desc, 177 + (struct usb_descriptor_header *) &pn_header_desc, 178 + (struct usb_descriptor_header *) &pn_phonet_desc, 179 + (struct usb_descriptor_header *) &pn_union_desc, 180 + (struct usb_descriptor_header *) &pn_data_nop_intf_desc, 181 + (struct usb_descriptor_header *) &pn_data_intf_desc, 182 + (struct usb_descriptor_header *) &pn_hs_sink_desc, 183 + (struct usb_descriptor_header *) &pn_hs_source_desc, 184 + NULL, 185 + }; 186 + 187 + /*-------------------------------------------------------------------------*/ 188 + 189 + static int pn_net_open(struct net_device *dev) 190 + { 191 + if (netif_carrier_ok(dev)) 192 + netif_wake_queue(dev); 193 + return 0; 194 + } 195 + 196 + static int pn_net_close(struct net_device *dev) 197 + { 198 + netif_stop_queue(dev); 199 + return 0; 200 + } 201 + 202 + static void pn_tx_complete(struct usb_ep *ep, struct usb_request *req) 203 + { 204 + struct f_phonet *fp = ep->driver_data; 205 + struct net_device *dev = fp->dev; 206 + struct sk_buff *skb = req->context; 207 + 208 + switch (req->status) { 209 + case 0: 210 + dev->stats.tx_packets++; 211 + dev->stats.tx_bytes += skb->len; 212 + break; 213 + 214 + case -ESHUTDOWN: /* disconnected */ 215 + case -ECONNRESET: /* disabled */ 216 + dev->stats.tx_aborted_errors++; 217 + default: 218 + dev->stats.tx_errors++; 219 + } 220 + 221 + dev_kfree_skb_any(skb); 222 + if (netif_carrier_ok(dev)) 223 + netif_wake_queue(dev); 224 + } 225 + 226 + static int pn_net_xmit(struct sk_buff *skb, struct net_device *dev) 227 + { 228 + struct phonet_port *port = netdev_priv(dev); 229 + struct f_phonet *fp; 230 + struct usb_request *req; 231 + unsigned long flags; 232 + 233 + if (skb->protocol != htons(ETH_P_PHONET)) 234 + goto out; 235 + 236 + spin_lock_irqsave(&port->lock, flags); 237 + fp = port->usb; 238 + if (unlikely(!fp)) /* race with carrier loss */ 239 + goto out_unlock; 240 + 241 + req = fp->in_req; 242 + req->buf = skb->data; 243 + req->length = skb->len; 244 + req->complete = pn_tx_complete; 245 + req->zero = 1; 246 + req->context = skb; 247 + 248 + if (unlikely(usb_ep_queue(fp->in_ep, req, GFP_ATOMIC))) 249 + goto out_unlock; 250 + 251 + netif_stop_queue(dev); 252 + skb = NULL; 253 + 254 + out_unlock: 255 + spin_unlock_irqrestore(&port->lock, flags); 256 + out: 257 + if (unlikely(skb)) { 258 + dev_kfree_skb_any(skb); 259 + dev->stats.tx_dropped++; 260 + } 261 + return 0; 262 + } 263 + 264 + static int pn_net_mtu(struct net_device *dev, int new_mtu) 265 + { 266 + struct phonet_port *port = netdev_priv(dev); 267 + unsigned long flags; 268 + int err = -EBUSY; 269 + 270 + if ((new_mtu < PHONET_MIN_MTU) || (new_mtu > PHONET_MAX_MTU)) 271 + return -EINVAL; 272 + 273 + spin_lock_irqsave(&port->lock, flags); 274 + if (!netif_carrier_ok(dev)) { 275 + dev->mtu = new_mtu; 276 + err = 0; 277 + } 278 + spin_unlock_irqrestore(&port->lock, flags); 279 + return err; 280 + } 281 + 282 + static void pn_net_setup(struct net_device *dev) 283 + { 284 + dev->features = 0; 285 + dev->type = ARPHRD_PHONET; 286 + dev->flags = IFF_POINTOPOINT | IFF_NOARP; 287 + dev->mtu = PHONET_DEV_MTU; 288 + dev->hard_header_len = 1; 289 + dev->dev_addr[0] = PN_MEDIA_USB; 290 + dev->addr_len = 1; 291 + dev->tx_queue_len = 1; 292 + 293 + dev->destructor = free_netdev; 294 + dev->header_ops = &phonet_header_ops; 295 + dev->open = pn_net_open; 296 + dev->stop = pn_net_close; 297 + dev->hard_start_xmit = pn_net_xmit; /* mandatory */ 298 + dev->change_mtu = pn_net_mtu; 299 + } 300 + 301 + /*-------------------------------------------------------------------------*/ 302 + 303 + /* 304 + * Queue buffer for data from the host 305 + */ 306 + static int 307 + pn_rx_submit(struct f_phonet *fp, struct usb_request *req, gfp_t gfp_flags) 308 + { 309 + struct sk_buff *skb; 310 + const size_t size = fp->dev->mtu; 311 + int err; 312 + 313 + skb = alloc_skb(size, gfp_flags); 314 + if (!skb) 315 + return -ENOMEM; 316 + 317 + req->buf = skb->data; 318 + req->length = size; 319 + req->context = skb; 320 + 321 + err = usb_ep_queue(fp->out_ep, req, gfp_flags); 322 + if (unlikely(err)) 323 + dev_kfree_skb_any(skb); 324 + return err; 325 + } 326 + 327 + static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req) 328 + { 329 + struct f_phonet *fp = ep->driver_data; 330 + struct net_device *dev = fp->dev; 331 + struct sk_buff *skb = req->context; 332 + int status = req->status; 333 + 334 + switch (status) { 335 + case 0: 336 + if (unlikely(!netif_running(dev))) 337 + break; 338 + if (unlikely(req->actual < 1)) 339 + break; 340 + skb_put(skb, req->actual); 341 + skb->protocol = htons(ETH_P_PHONET); 342 + skb_reset_mac_header(skb); 343 + __skb_pull(skb, 1); 344 + skb->dev = dev; 345 + dev->stats.rx_packets++; 346 + dev->stats.rx_bytes += skb->len; 347 + 348 + netif_rx(skb); 349 + skb = NULL; 350 + break; 351 + 352 + /* Do not resubmit in these cases: */ 353 + case -ESHUTDOWN: /* disconnect */ 354 + case -ECONNABORTED: /* hw reset */ 355 + case -ECONNRESET: /* dequeued (unlink or netif down) */ 356 + req = NULL; 357 + break; 358 + 359 + /* Do resubmit in these cases: */ 360 + case -EOVERFLOW: /* request buffer overflow */ 361 + dev->stats.rx_over_errors++; 362 + default: 363 + dev->stats.rx_errors++; 364 + break; 365 + } 366 + 367 + if (skb) 368 + dev_kfree_skb_any(skb); 369 + if (req) 370 + pn_rx_submit(fp, req, GFP_ATOMIC); 371 + } 372 + 373 + /*-------------------------------------------------------------------------*/ 374 + 375 + static void __pn_reset(struct usb_function *f) 376 + { 377 + struct f_phonet *fp = func_to_pn(f); 378 + struct net_device *dev = fp->dev; 379 + struct phonet_port *port = netdev_priv(dev); 380 + 381 + netif_carrier_off(dev); 382 + netif_stop_queue(dev); 383 + port->usb = NULL; 384 + 385 + usb_ep_disable(fp->out_ep); 386 + usb_ep_disable(fp->in_ep); 387 + } 388 + 389 + static int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt) 390 + { 391 + struct f_phonet *fp = func_to_pn(f); 392 + struct usb_gadget *gadget = fp->function.config->cdev->gadget; 393 + 394 + if (intf == pn_control_intf_desc.bInterfaceNumber) 395 + /* control interface, no altsetting */ 396 + return (alt > 0) ? -EINVAL : 0; 397 + 398 + if (intf == pn_data_intf_desc.bInterfaceNumber) { 399 + struct net_device *dev = fp->dev; 400 + struct phonet_port *port = netdev_priv(dev); 401 + 402 + /* data intf (0: inactive, 1: active) */ 403 + if (alt > 1) 404 + return -EINVAL; 405 + 406 + spin_lock(&port->lock); 407 + __pn_reset(f); 408 + if (alt == 1) { 409 + struct usb_endpoint_descriptor *out, *in; 410 + int i; 411 + 412 + out = ep_choose(gadget, 413 + &pn_hs_sink_desc, 414 + &pn_fs_sink_desc); 415 + in = ep_choose(gadget, 416 + &pn_hs_source_desc, 417 + &pn_fs_source_desc); 418 + usb_ep_enable(fp->out_ep, out); 419 + usb_ep_enable(fp->in_ep, in); 420 + 421 + port->usb = fp; 422 + fp->out_ep->driver_data = fp; 423 + fp->in_ep->driver_data = fp; 424 + 425 + netif_carrier_on(dev); 426 + if (netif_running(dev)) 427 + netif_wake_queue(dev); 428 + for (i = 0; i < phonet_rxq_size; i++) 429 + pn_rx_submit(fp, fp->out_reqv[i], GFP_ATOMIC); 430 + } 431 + spin_unlock(&port->lock); 432 + return 0; 433 + } 434 + 435 + return -EINVAL; 436 + } 437 + 438 + static int pn_get_alt(struct usb_function *f, unsigned intf) 439 + { 440 + struct f_phonet *fp = func_to_pn(f); 441 + 442 + if (intf == pn_control_intf_desc.bInterfaceNumber) 443 + return 0; 444 + 445 + if (intf == pn_data_intf_desc.bInterfaceNumber) { 446 + struct phonet_port *port = netdev_priv(fp->dev); 447 + u8 alt; 448 + 449 + spin_lock(&port->lock); 450 + alt = port->usb != NULL; 451 + spin_unlock(&port->lock); 452 + return alt; 453 + } 454 + 455 + return -EINVAL; 456 + } 457 + 458 + static void pn_disconnect(struct usb_function *f) 459 + { 460 + struct f_phonet *fp = func_to_pn(f); 461 + struct phonet_port *port = netdev_priv(fp->dev); 462 + unsigned long flags; 463 + 464 + /* remain disabled until set_alt */ 465 + spin_lock_irqsave(&port->lock, flags); 466 + __pn_reset(f); 467 + spin_unlock_irqrestore(&port->lock, flags); 468 + } 469 + 470 + /*-------------------------------------------------------------------------*/ 471 + 472 + static __init 473 + int pn_bind(struct usb_configuration *c, struct usb_function *f) 474 + { 475 + struct usb_composite_dev *cdev = c->cdev; 476 + struct usb_gadget *gadget = cdev->gadget; 477 + struct f_phonet *fp = func_to_pn(f); 478 + struct usb_ep *ep; 479 + int status, i; 480 + 481 + /* Reserve interface IDs */ 482 + status = usb_interface_id(c, f); 483 + if (status < 0) 484 + goto err; 485 + pn_control_intf_desc.bInterfaceNumber = status; 486 + pn_union_desc.bMasterInterface0 = status; 487 + 488 + status = usb_interface_id(c, f); 489 + if (status < 0) 490 + goto err; 491 + pn_data_nop_intf_desc.bInterfaceNumber = status; 492 + pn_data_intf_desc.bInterfaceNumber = status; 493 + pn_union_desc.bSlaveInterface0 = status; 494 + 495 + /* Reserve endpoints */ 496 + status = -ENODEV; 497 + ep = usb_ep_autoconfig(gadget, &pn_fs_sink_desc); 498 + if (!ep) 499 + goto err; 500 + fp->out_ep = ep; 501 + ep->driver_data = fp; /* Claim */ 502 + 503 + ep = usb_ep_autoconfig(gadget, &pn_fs_source_desc); 504 + if (!ep) 505 + goto err; 506 + fp->in_ep = ep; 507 + ep->driver_data = fp; /* Claim */ 508 + 509 + pn_hs_sink_desc.bEndpointAddress = 510 + pn_fs_sink_desc.bEndpointAddress; 511 + pn_hs_source_desc.bEndpointAddress = 512 + pn_fs_source_desc.bEndpointAddress; 513 + 514 + /* Do not try to bind Phonet twice... */ 515 + fp->function.descriptors = fs_pn_function; 516 + fp->function.hs_descriptors = hs_pn_function; 517 + 518 + /* Incoming USB requests */ 519 + status = -ENOMEM; 520 + for (i = 0; i < phonet_rxq_size; i++) { 521 + struct usb_request *req; 522 + 523 + req = usb_ep_alloc_request(fp->out_ep, GFP_KERNEL); 524 + if (!req) 525 + goto err; 526 + 527 + req->complete = pn_rx_complete; 528 + fp->out_reqv[i] = req; 529 + } 530 + 531 + /* Outgoing USB requests */ 532 + fp->in_req = usb_ep_alloc_request(fp->in_ep, GFP_KERNEL); 533 + if (!fp->in_req) 534 + goto err; 535 + 536 + INFO(cdev, "USB CDC Phonet function\n"); 537 + INFO(cdev, "using %s, OUT %s, IN %s\n", cdev->gadget->name, 538 + fp->out_ep->name, fp->in_ep->name); 539 + return 0; 540 + 541 + err: 542 + if (fp->out_ep) 543 + fp->out_ep->driver_data = NULL; 544 + if (fp->in_ep) 545 + fp->in_ep->driver_data = NULL; 546 + ERROR(cdev, "USB CDC Phonet: cannot autoconfigure\n"); 547 + return status; 548 + } 549 + 550 + static void 551 + pn_unbind(struct usb_configuration *c, struct usb_function *f) 552 + { 553 + struct f_phonet *fp = func_to_pn(f); 554 + int i; 555 + 556 + /* We are already disconnected */ 557 + if (fp->in_req) 558 + usb_ep_free_request(fp->in_ep, fp->in_req); 559 + for (i = 0; i < phonet_rxq_size; i++) 560 + if (fp->out_reqv[i]) 561 + usb_ep_free_request(fp->out_ep, fp->out_reqv[i]); 562 + 563 + kfree(fp); 564 + } 565 + 566 + /*-------------------------------------------------------------------------*/ 567 + 568 + static struct net_device *dev; 569 + 570 + int __init phonet_bind_config(struct usb_configuration *c) 571 + { 572 + struct f_phonet *fp; 573 + int err; 574 + 575 + fp = kzalloc(sizeof(*fp), GFP_KERNEL); 576 + if (!fp) 577 + return -ENOMEM; 578 + 579 + fp->dev = dev; 580 + fp->function.name = "phonet"; 581 + fp->function.bind = pn_bind; 582 + fp->function.unbind = pn_unbind; 583 + fp->function.set_alt = pn_set_alt; 584 + fp->function.get_alt = pn_get_alt; 585 + fp->function.disable = pn_disconnect; 586 + 587 + err = usb_add_function(c, &fp->function); 588 + if (err) 589 + kfree(fp); 590 + return err; 591 + } 592 + 593 + int __init gphonet_setup(struct usb_gadget *gadget) 594 + { 595 + struct phonet_port *port; 596 + int err; 597 + 598 + /* Create net device */ 599 + BUG_ON(dev); 600 + dev = alloc_netdev(sizeof(*port) 601 + + (phonet_rxq_size * sizeof(struct usb_request *)), 602 + "upnlink%d", pn_net_setup); 603 + if (!dev) 604 + return -ENOMEM; 605 + 606 + port = netdev_priv(dev); 607 + spin_lock_init(&port->lock); 608 + netif_carrier_off(dev); 609 + netif_stop_queue(dev); 610 + SET_NETDEV_DEV(dev, &gadget->dev); 611 + 612 + err = register_netdev(dev); 613 + if (err) 614 + free_netdev(dev); 615 + return err; 616 + } 617 + 618 + void gphonet_cleanup(void) 619 + { 620 + unregister_netdev(dev); 621 + }
+21
drivers/usb/gadget/u_phonet.h
··· 1 + /* 2 + * u_phonet.h - interface to Phonet 3 + * 4 + * Copyright (C) 2007-2008 by Nokia Corporation 5 + * 6 + * This software is distributed under the terms of the GNU General 7 + * Public License ("GPL") as published by the Free Software Foundation, 8 + * either version 2 of that License or (at your option) any later version. 9 + */ 10 + 11 + #ifndef __U_PHONET_H 12 + #define __U_PHONET_H 13 + 14 + #include <linux/usb/composite.h> 15 + #include <linux/usb/cdc.h> 16 + 17 + int gphonet_setup(struct usb_gadget *gadget); 18 + int phonet_bind_config(struct usb_configuration *c); 19 + void gphonet_cleanup(void); 20 + 21 + #endif /* __U_PHONET_H */