linux/drivers/net/usb/cdc_eem.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * USB CDC EEM network interface driver
   4 * Copyright (C) 2009 Oberthur Technologies
   5 * by Omar Laazimani, Olivier Condemine
   6 */
   7
   8#include <linux/module.h>
   9#include <linux/netdevice.h>
  10#include <linux/etherdevice.h>
  11#include <linux/ctype.h>
  12#include <linux/ethtool.h>
  13#include <linux/workqueue.h>
  14#include <linux/mii.h>
  15#include <linux/usb.h>
  16#include <linux/crc32.h>
  17#include <linux/usb/cdc.h>
  18#include <linux/usb/usbnet.h>
  19#include <linux/gfp.h>
  20#include <linux/if_vlan.h>
  21
  22
  23/*
  24 * This driver is an implementation of the CDC "Ethernet Emulation
  25 * Model" (EEM) specification, which encapsulates Ethernet frames
  26 * for transport over USB using a simpler USB device model than the
  27 * previous CDC "Ethernet Control Model" (ECM, or "CDC Ethernet").
  28 *
  29 * For details, see www.usb.org/developers/devclass_docs/CDC_EEM10.pdf
  30 *
  31 * This version has been tested with GIGAntIC WuaoW SIM Smart Card on 2.6.24,
  32 * 2.6.27 and 2.6.30rc2 kernel.
  33 * It has also been validated on Openmoko Om 2008.12 (based on 2.6.24 kernel).
  34 * build on 23-April-2009
  35 */
  36
  37#define EEM_HEAD        2               /* 2 byte header */
  38
  39/*-------------------------------------------------------------------------*/
  40
  41static void eem_linkcmd_complete(struct urb *urb)
  42{
  43        dev_kfree_skb(urb->context);
  44        usb_free_urb(urb);
  45}
  46
  47static void eem_linkcmd(struct usbnet *dev, struct sk_buff *skb)
  48{
  49        struct urb              *urb;
  50        int                     status;
  51
  52        urb = usb_alloc_urb(0, GFP_ATOMIC);
  53        if (!urb)
  54                goto fail;
  55
  56        usb_fill_bulk_urb(urb, dev->udev, dev->out,
  57                        skb->data, skb->len, eem_linkcmd_complete, skb);
  58
  59        status = usb_submit_urb(urb, GFP_ATOMIC);
  60        if (status) {
  61                usb_free_urb(urb);
  62fail:
  63                dev_kfree_skb(skb);
  64                netdev_warn(dev->net, "link cmd failure\n");
  65                return;
  66        }
  67}
  68
  69static int eem_bind(struct usbnet *dev, struct usb_interface *intf)
  70{
  71        int status = 0;
  72
  73        status = usbnet_get_endpoints(dev, intf);
  74        if (status < 0)
  75                return status;
  76
  77        /* no jumbogram (16K) support for now */
  78
  79        dev->net->hard_header_len += EEM_HEAD + ETH_FCS_LEN + VLAN_HLEN;
  80        dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
  81
  82        return 0;
  83}
  84
  85/*
  86 * EEM permits packing multiple Ethernet frames into USB transfers
  87 * (a "bundle"), but for TX we don't try to do that.
  88 */
  89static struct sk_buff *eem_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
  90                                       gfp_t flags)
  91{
  92        struct sk_buff  *skb2 = NULL;
  93        u16             len = skb->len;
  94        u32             crc = 0;
  95        int             padlen = 0;
  96
  97        /* When ((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket) is
  98         * zero, stick two bytes of zero length EEM packet on the end.
  99         * Else the framework would add invalid single byte padding,
 100         * since it can't know whether ZLPs will be handled right by
 101         * all the relevant hardware and software.
 102         */
 103        if (!((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket))
 104                padlen += 2;
 105
 106        if (!skb_cloned(skb)) {
 107                int     headroom = skb_headroom(skb);
 108                int     tailroom = skb_tailroom(skb);
 109
 110                if ((tailroom >= ETH_FCS_LEN + padlen) &&
 111                    (headroom >= EEM_HEAD))
 112                        goto done;
 113
 114                if ((headroom + tailroom)
 115                                > (EEM_HEAD + ETH_FCS_LEN + padlen)) {
 116                        skb->data = memmove(skb->head +
 117                                        EEM_HEAD,
 118                                        skb->data,
 119                                        skb->len);
 120                        skb_set_tail_pointer(skb, len);
 121                        goto done;
 122                }
 123        }
 124
 125        skb2 = skb_copy_expand(skb, EEM_HEAD, ETH_FCS_LEN + padlen, flags);
 126        if (!skb2)
 127                return NULL;
 128
 129        dev_kfree_skb_any(skb);
 130        skb = skb2;
 131
 132done:
 133        /* we don't use the "no Ethernet CRC" option */
 134        crc = crc32_le(~0, skb->data, skb->len);
 135        crc = ~crc;
 136
 137        put_unaligned_le32(crc, skb_put(skb, 4));
 138
 139        /* EEM packet header format:
 140         * b0..13:      length of ethernet frame
 141         * b14:         bmCRC (1 == valid Ethernet CRC)
 142         * b15:         bmType (0 == data)
 143         */
 144        len = skb->len;
 145        put_unaligned_le16(BIT(14) | len, skb_push(skb, 2));
 146
 147        /* Bundle a zero length EEM packet if needed */
 148        if (padlen)
 149                put_unaligned_le16(0, skb_put(skb, 2));
 150
 151        return skb;
 152}
 153
 154static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
 155{
 156        /*
 157         * Our task here is to strip off framing, leaving skb with one
 158         * data frame for the usbnet framework code to process.  But we
 159         * may have received multiple EEM payloads, or command payloads.
 160         * So we must process _everything_ as if it's a header, except
 161         * maybe the last data payload
 162         *
 163         * REVISIT the framework needs updating so that when we consume
 164         * all payloads (the last or only message was a command, or a
 165         * zero length EEM packet) that is not accounted as an rx_error.
 166         */
 167        do {
 168                struct sk_buff  *skb2 = NULL;
 169                u16             header;
 170                u16             len = 0;
 171
 172                /* incomplete EEM header? */
 173                if (skb->len < EEM_HEAD)
 174                        return 0;
 175
 176                /*
 177                 * EEM packet header format:
 178                 * b0..14:      EEM type dependent (Data or Command)
 179                 * b15:         bmType
 180                 */
 181                header = get_unaligned_le16(skb->data);
 182                skb_pull(skb, EEM_HEAD);
 183
 184                /*
 185                 * The bmType bit helps to denote when EEM
 186                 * packet is data or command :
 187                 *      bmType = 0      : EEM data payload
 188                 *      bmType = 1      : EEM (link) command
 189                 */
 190                if (header & BIT(15)) {
 191                        u16     bmEEMCmd;
 192
 193                        /*
 194                         * EEM (link) command packet:
 195                         * b0..10:      bmEEMCmdParam
 196                         * b11..13:     bmEEMCmd
 197                         * b14:         bmReserved (must be 0)
 198                         * b15:         1 (EEM command)
 199                         */
 200                        if (header & BIT(14)) {
 201                                netdev_dbg(dev->net, "reserved command %04x\n",
 202                                           header);
 203                                continue;
 204                        }
 205
 206                        bmEEMCmd = (header >> 11) & 0x7;
 207                        switch (bmEEMCmd) {
 208
 209                        /* Responding to echo requests is mandatory. */
 210                        case 0:         /* Echo command */
 211                                len = header & 0x7FF;
 212
 213                                /* bogus command? */
 214                                if (skb->len < len)
 215                                        return 0;
 216
 217                                skb2 = skb_clone(skb, GFP_ATOMIC);
 218                                if (unlikely(!skb2))
 219                                        goto next;
 220                                skb_trim(skb2, len);
 221                                put_unaligned_le16(BIT(15) | (1 << 11) | len,
 222                                                skb_push(skb2, 2));
 223                                eem_linkcmd(dev, skb2);
 224                                break;
 225
 226                        /*
 227                         * Host may choose to ignore hints.
 228                         *  - suspend: peripheral ready to suspend
 229                         *  - response: suggest N millisec polling
 230                         *  - response complete: suggest N sec polling
 231                         *
 232                         * Suspend is reported and maybe heeded.
 233                         */
 234                        case 2:         /* Suspend hint */
 235                                usbnet_device_suggests_idle(dev);
 236                                continue;
 237                        case 3:         /* Response hint */
 238                        case 4:         /* Response complete hint */
 239                                continue;
 240
 241                        /*
 242                         * Hosts should never receive host-to-peripheral
 243                         * or reserved command codes; or responses to an
 244                         * echo command we didn't send.
 245                         */
 246                        case 1:         /* Echo response */
 247                        case 5:         /* Tickle */
 248                        default:        /* reserved */
 249                                netdev_warn(dev->net,
 250                                            "unexpected link command %d\n",
 251                                            bmEEMCmd);
 252                                continue;
 253                        }
 254
 255                } else {
 256                        u32     crc, crc2;
 257                        int     is_last;
 258
 259                        /* zero length EEM packet? */
 260                        if (header == 0)
 261                                continue;
 262
 263                        /*
 264                         * EEM data packet header :
 265                         * b0..13:      length of ethernet frame
 266                         * b14:         bmCRC
 267                         * b15:         0 (EEM data)
 268                         */
 269                        len = header & 0x3FFF;
 270
 271                        /* bogus EEM payload? */
 272                        if (skb->len < len)
 273                                return 0;
 274
 275                        /* bogus ethernet frame? */
 276                        if (len < (ETH_HLEN + ETH_FCS_LEN))
 277                                goto next;
 278
 279                        /*
 280                         * Treat the last payload differently: framework
 281                         * code expects our "fixup" to have stripped off
 282                         * headers, so "skb" is a data packet (or error).
 283                         * Else if it's not the last payload, keep "skb"
 284                         * for further processing.
 285                         */
 286                        is_last = (len == skb->len);
 287                        if (is_last)
 288                                skb2 = skb;
 289                        else {
 290                                skb2 = skb_clone(skb, GFP_ATOMIC);
 291                                if (unlikely(!skb2))
 292                                        return 0;
 293                        }
 294
 295                        /*
 296                         * The bmCRC helps to denote when the CRC field in
 297                         * the Ethernet frame contains a calculated CRC:
 298                         *      bmCRC = 1       : CRC is calculated
 299                         *      bmCRC = 0       : CRC = 0xDEADBEEF
 300                         */
 301                        if (header & BIT(14)) {
 302                                crc = get_unaligned_le32(skb2->data
 303                                                + len - ETH_FCS_LEN);
 304                                crc2 = ~crc32_le(~0, skb2->data, skb2->len
 305                                                - ETH_FCS_LEN);
 306                        } else {
 307                                crc = get_unaligned_be32(skb2->data
 308                                                + len - ETH_FCS_LEN);
 309                                crc2 = 0xdeadbeef;
 310                        }
 311                        skb_trim(skb2, len - ETH_FCS_LEN);
 312
 313                        if (is_last)
 314                                return crc == crc2;
 315
 316                        if (unlikely(crc != crc2)) {
 317                                dev->net->stats.rx_errors++;
 318                                dev_kfree_skb_any(skb2);
 319                        } else
 320                                usbnet_skb_return(dev, skb2);
 321                }
 322
 323next:
 324                skb_pull(skb, len);
 325        } while (skb->len);
 326
 327        return 1;
 328}
 329
 330static const struct driver_info eem_info = {
 331        .description =  "CDC EEM Device",
 332        .flags =        FLAG_ETHER | FLAG_POINTTOPOINT,
 333        .bind =         eem_bind,
 334        .rx_fixup =     eem_rx_fixup,
 335        .tx_fixup =     eem_tx_fixup,
 336};
 337
 338/*-------------------------------------------------------------------------*/
 339
 340static const struct usb_device_id products[] = {
 341{
 342        USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_EEM,
 343                        USB_CDC_PROTO_EEM),
 344        .driver_info = (unsigned long) &eem_info,
 345},
 346{
 347        /* EMPTY == end of list */
 348},
 349};
 350MODULE_DEVICE_TABLE(usb, products);
 351
 352static struct usb_driver eem_driver = {
 353        .name =         "cdc_eem",
 354        .id_table =     products,
 355        .probe =        usbnet_probe,
 356        .disconnect =   usbnet_disconnect,
 357        .suspend =      usbnet_suspend,
 358        .resume =       usbnet_resume,
 359        .disable_hub_initiated_lpm = 1,
 360};
 361
 362module_usb_driver(eem_driver);
 363
 364MODULE_AUTHOR("Omar Laazimani <omar.oberthur@gmail.com>");
 365MODULE_DESCRIPTION("USB CDC EEM");
 366MODULE_LICENSE("GPL");
 367