434 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * phonet.c -- USB CDC Phonet host driver4 *5 * Copyright (C) 2008-2009 Nokia Corporation. All rights reserved.6 *7 * Author: Rémi Denis-Courmont8 */9 10#include <linux/kernel.h>11#include <linux/mm.h>12#include <linux/module.h>13#include <linux/gfp.h>14#include <linux/usb.h>15#include <linux/usb/cdc.h>16#include <linux/netdevice.h>17#include <linux/if_arp.h>18#include <linux/if_phonet.h>19#include <linux/phonet.h>20 21#define PN_MEDIA_USB 0x1B22 23static const unsigned rxq_size = 17;24 25struct usbpn_dev {26 struct net_device *dev;27 28 struct usb_interface *intf, *data_intf;29 struct usb_device *usb;30 unsigned int tx_pipe, rx_pipe;31 u8 active_setting;32 u8 disconnected;33 34 unsigned tx_queue;35 spinlock_t tx_lock;36 37 spinlock_t rx_lock;38 struct sk_buff *rx_skb;39 struct urb *urbs[];40};41 42static void tx_complete(struct urb *req);43static void rx_complete(struct urb *req);44 45/*46 * Network device callbacks47 */48static netdev_tx_t usbpn_xmit(struct sk_buff *skb, struct net_device *dev)49{50 struct usbpn_dev *pnd = netdev_priv(dev);51 struct urb *req = NULL;52 unsigned long flags;53 int err;54 55 if (skb->protocol != htons(ETH_P_PHONET))56 goto drop;57 58 req = usb_alloc_urb(0, GFP_ATOMIC);59 if (!req)60 goto drop;61 usb_fill_bulk_urb(req, pnd->usb, pnd->tx_pipe, skb->data, skb->len,62 tx_complete, skb);63 req->transfer_flags = URB_ZERO_PACKET;64 err = usb_submit_urb(req, GFP_ATOMIC);65 if (err) {66 usb_free_urb(req);67 goto drop;68 }69 70 spin_lock_irqsave(&pnd->tx_lock, flags);71 pnd->tx_queue++;72 if (pnd->tx_queue >= dev->tx_queue_len)73 netif_stop_queue(dev);74 spin_unlock_irqrestore(&pnd->tx_lock, flags);75 return NETDEV_TX_OK;76 77drop:78 dev_kfree_skb(skb);79 dev->stats.tx_dropped++;80 return NETDEV_TX_OK;81}82 83static void tx_complete(struct urb *req)84{85 struct sk_buff *skb = req->context;86 struct net_device *dev = skb->dev;87 struct usbpn_dev *pnd = netdev_priv(dev);88 int status = req->status;89 unsigned long flags;90 91 switch (status) {92 case 0:93 dev->stats.tx_bytes += skb->len;94 break;95 96 case -ENOENT:97 case -ECONNRESET:98 case -ESHUTDOWN:99 dev->stats.tx_aborted_errors++;100 fallthrough;101 default:102 dev->stats.tx_errors++;103 dev_dbg(&dev->dev, "TX error (%d)\n", status);104 }105 dev->stats.tx_packets++;106 107 spin_lock_irqsave(&pnd->tx_lock, flags);108 pnd->tx_queue--;109 netif_wake_queue(dev);110 spin_unlock_irqrestore(&pnd->tx_lock, flags);111 112 dev_kfree_skb_any(skb);113 usb_free_urb(req);114}115 116static int rx_submit(struct usbpn_dev *pnd, struct urb *req, gfp_t gfp_flags)117{118 struct net_device *dev = pnd->dev;119 struct page *page;120 int err;121 122 page = __dev_alloc_page(gfp_flags | __GFP_NOMEMALLOC);123 if (!page)124 return -ENOMEM;125 126 usb_fill_bulk_urb(req, pnd->usb, pnd->rx_pipe, page_address(page),127 PAGE_SIZE, rx_complete, dev);128 req->transfer_flags = 0;129 err = usb_submit_urb(req, gfp_flags);130 if (unlikely(err)) {131 dev_dbg(&dev->dev, "RX submit error (%d)\n", err);132 put_page(page);133 }134 return err;135}136 137static void rx_complete(struct urb *req)138{139 struct net_device *dev = req->context;140 struct usbpn_dev *pnd = netdev_priv(dev);141 struct page *page = virt_to_page(req->transfer_buffer);142 struct sk_buff *skb;143 unsigned long flags;144 int status = req->status;145 146 switch (status) {147 case 0:148 spin_lock_irqsave(&pnd->rx_lock, flags);149 skb = pnd->rx_skb;150 if (!skb) {151 skb = pnd->rx_skb = netdev_alloc_skb(dev, 12);152 if (likely(skb)) {153 /* Can't use pskb_pull() on page in IRQ */154 skb_put_data(skb, page_address(page), 1);155 skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,156 page, 1, req->actual_length,157 PAGE_SIZE);158 page = NULL;159 }160 } else {161 skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,162 page, 0, req->actual_length,163 PAGE_SIZE);164 page = NULL;165 }166 if (req->actual_length < PAGE_SIZE)167 pnd->rx_skb = NULL; /* Last fragment */168 else169 skb = NULL;170 spin_unlock_irqrestore(&pnd->rx_lock, flags);171 if (skb) {172 skb->protocol = htons(ETH_P_PHONET);173 skb_reset_mac_header(skb);174 __skb_pull(skb, 1);175 skb->dev = dev;176 dev->stats.rx_packets++;177 dev->stats.rx_bytes += skb->len;178 179 netif_rx(skb);180 }181 goto resubmit;182 183 case -ENOENT:184 case -ECONNRESET:185 case -ESHUTDOWN:186 req = NULL;187 break;188 189 case -EOVERFLOW:190 dev->stats.rx_over_errors++;191 dev_dbg(&dev->dev, "RX overflow\n");192 break;193 194 case -EILSEQ:195 dev->stats.rx_crc_errors++;196 break;197 }198 199 dev->stats.rx_errors++;200resubmit:201 if (page)202 put_page(page);203 if (req)204 rx_submit(pnd, req, GFP_ATOMIC);205}206 207static int usbpn_close(struct net_device *dev);208 209static int usbpn_open(struct net_device *dev)210{211 struct usbpn_dev *pnd = netdev_priv(dev);212 int err;213 unsigned i;214 unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber;215 216 err = usb_set_interface(pnd->usb, num, pnd->active_setting);217 if (err)218 return err;219 220 for (i = 0; i < rxq_size; i++) {221 struct urb *req = usb_alloc_urb(0, GFP_KERNEL);222 223 if (!req || rx_submit(pnd, req, GFP_KERNEL)) {224 usb_free_urb(req);225 usbpn_close(dev);226 return -ENOMEM;227 }228 pnd->urbs[i] = req;229 }230 231 netif_wake_queue(dev);232 return 0;233}234 235static int usbpn_close(struct net_device *dev)236{237 struct usbpn_dev *pnd = netdev_priv(dev);238 unsigned i;239 unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber;240 241 netif_stop_queue(dev);242 243 for (i = 0; i < rxq_size; i++) {244 struct urb *req = pnd->urbs[i];245 246 if (!req)247 continue;248 usb_kill_urb(req);249 usb_free_urb(req);250 pnd->urbs[i] = NULL;251 }252 253 return usb_set_interface(pnd->usb, num, !pnd->active_setting);254}255 256static int usbpn_siocdevprivate(struct net_device *dev, struct ifreq *ifr,257 void __user *data, int cmd)258{259 struct if_phonet_req *req = (struct if_phonet_req *)ifr;260 261 switch (cmd) {262 case SIOCPNGAUTOCONF:263 req->ifr_phonet_autoconf.device = PN_DEV_PC;264 return 0;265 }266 return -ENOIOCTLCMD;267}268 269static const struct net_device_ops usbpn_ops = {270 .ndo_open = usbpn_open,271 .ndo_stop = usbpn_close,272 .ndo_start_xmit = usbpn_xmit,273 .ndo_siocdevprivate = usbpn_siocdevprivate,274};275 276static void usbpn_setup(struct net_device *dev)277{278 const u8 addr = PN_MEDIA_USB;279 280 dev->features = 0;281 dev->netdev_ops = &usbpn_ops;282 dev->header_ops = &phonet_header_ops;283 dev->type = ARPHRD_PHONET;284 dev->flags = IFF_POINTOPOINT | IFF_NOARP;285 dev->mtu = PHONET_MAX_MTU;286 dev->min_mtu = PHONET_MIN_MTU;287 dev->max_mtu = PHONET_MAX_MTU;288 dev->hard_header_len = 1;289 dev->addr_len = 1;290 dev_addr_set(dev, &addr);291 dev->tx_queue_len = 3;292 293 dev->needs_free_netdev = true;294}295 296/*297 * USB driver callbacks298 */299static const struct usb_device_id usbpn_ids[] = {300 {301 .match_flags = USB_DEVICE_ID_MATCH_VENDOR302 | USB_DEVICE_ID_MATCH_INT_CLASS303 | USB_DEVICE_ID_MATCH_INT_SUBCLASS,304 .idVendor = 0x0421, /* Nokia */305 .bInterfaceClass = USB_CLASS_COMM,306 .bInterfaceSubClass = 0xFE,307 },308 { },309};310 311MODULE_DEVICE_TABLE(usb, usbpn_ids);312 313static struct usb_driver usbpn_driver;314 315static int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id)316{317 static const char ifname[] = "usbpn%d";318 const struct usb_cdc_union_desc *union_header = NULL;319 const struct usb_host_interface *data_desc;320 struct usb_interface *data_intf;321 struct usb_device *usbdev = interface_to_usbdev(intf);322 struct net_device *dev;323 struct usbpn_dev *pnd;324 u8 *data;325 int phonet = 0;326 int len, err;327 struct usb_cdc_parsed_header hdr;328 329 data = intf->altsetting->extra;330 len = intf->altsetting->extralen;331 cdc_parse_cdc_header(&hdr, intf, data, len);332 union_header = hdr.usb_cdc_union_desc;333 phonet = hdr.phonet_magic_present;334 335 if (!union_header || !phonet)336 return -EINVAL;337 338 data_intf = usb_ifnum_to_if(usbdev, union_header->bSlaveInterface0);339 if (data_intf == NULL)340 return -ENODEV;341 /* Data interface has one inactive and one active setting */342 if (data_intf->num_altsetting != 2)343 return -EINVAL;344 if (data_intf->altsetting[0].desc.bNumEndpoints == 0 &&345 data_intf->altsetting[1].desc.bNumEndpoints == 2)346 data_desc = data_intf->altsetting + 1;347 else348 if (data_intf->altsetting[0].desc.bNumEndpoints == 2 &&349 data_intf->altsetting[1].desc.bNumEndpoints == 0)350 data_desc = data_intf->altsetting;351 else352 return -EINVAL;353 354 dev = alloc_netdev(struct_size(pnd, urbs, rxq_size), ifname,355 NET_NAME_UNKNOWN, usbpn_setup);356 if (!dev)357 return -ENOMEM;358 359 pnd = netdev_priv(dev);360 SET_NETDEV_DEV(dev, &intf->dev);361 362 pnd->dev = dev;363 pnd->usb = usbdev;364 pnd->intf = intf;365 pnd->data_intf = data_intf;366 spin_lock_init(&pnd->tx_lock);367 spin_lock_init(&pnd->rx_lock);368 /* Endpoints */369 if (usb_pipein(data_desc->endpoint[0].desc.bEndpointAddress)) {370 pnd->rx_pipe = usb_rcvbulkpipe(usbdev,371 data_desc->endpoint[0].desc.bEndpointAddress);372 pnd->tx_pipe = usb_sndbulkpipe(usbdev,373 data_desc->endpoint[1].desc.bEndpointAddress);374 } else {375 pnd->rx_pipe = usb_rcvbulkpipe(usbdev,376 data_desc->endpoint[1].desc.bEndpointAddress);377 pnd->tx_pipe = usb_sndbulkpipe(usbdev,378 data_desc->endpoint[0].desc.bEndpointAddress);379 }380 pnd->active_setting = data_desc - data_intf->altsetting;381 382 err = usb_driver_claim_interface(&usbpn_driver, data_intf, pnd);383 if (err)384 goto out;385 386 /* Force inactive mode until the network device is brought UP */387 usb_set_interface(usbdev, union_header->bSlaveInterface0,388 !pnd->active_setting);389 usb_set_intfdata(intf, pnd);390 391 err = register_netdev(dev);392 if (err) {393 /* Set disconnected flag so that disconnect() returns early. */394 pnd->disconnected = 1;395 usb_driver_release_interface(&usbpn_driver, data_intf);396 goto out;397 }398 399 dev_dbg(&dev->dev, "USB CDC Phonet device found\n");400 return 0;401 402out:403 usb_set_intfdata(intf, NULL);404 free_netdev(dev);405 return err;406}407 408static void usbpn_disconnect(struct usb_interface *intf)409{410 struct usbpn_dev *pnd = usb_get_intfdata(intf);411 412 if (pnd->disconnected)413 return;414 415 pnd->disconnected = 1;416 usb_driver_release_interface(&usbpn_driver,417 (pnd->intf == intf) ? pnd->data_intf : pnd->intf);418 unregister_netdev(pnd->dev);419}420 421static struct usb_driver usbpn_driver = {422 .name = "cdc_phonet",423 .probe = usbpn_probe,424 .disconnect = usbpn_disconnect,425 .id_table = usbpn_ids,426 .disable_hub_initiated_lpm = 1,427};428 429module_usb_driver(usbpn_driver);430 431MODULE_AUTHOR("Remi Denis-Courmont");432MODULE_DESCRIPTION("USB CDC Phonet host interface");433MODULE_LICENSE("GPL");434