Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * USB CDC EEM network interface driver
0004  * Copyright (C) 2009 Oberthur Technologies
0005  * by Omar Laazimani, Olivier Condemine
0006  */
0007 
0008 #include <linux/module.h>
0009 #include <linux/netdevice.h>
0010 #include <linux/etherdevice.h>
0011 #include <linux/ctype.h>
0012 #include <linux/ethtool.h>
0013 #include <linux/workqueue.h>
0014 #include <linux/mii.h>
0015 #include <linux/usb.h>
0016 #include <linux/crc32.h>
0017 #include <linux/usb/cdc.h>
0018 #include <linux/usb/usbnet.h>
0019 #include <linux/gfp.h>
0020 #include <linux/if_vlan.h>
0021 
0022 
0023 /*
0024  * This driver is an implementation of the CDC "Ethernet Emulation
0025  * Model" (EEM) specification, which encapsulates Ethernet frames
0026  * for transport over USB using a simpler USB device model than the
0027  * previous CDC "Ethernet Control Model" (ECM, or "CDC Ethernet").
0028  *
0029  * For details, see https://usb.org/sites/default/files/CDC_EEM10.pdf
0030  *
0031  * This version has been tested with GIGAntIC WuaoW SIM Smart Card on 2.6.24,
0032  * 2.6.27 and 2.6.30rc2 kernel.
0033  * It has also been validated on Openmoko Om 2008.12 (based on 2.6.24 kernel).
0034  * build on 23-April-2009
0035  */
0036 
0037 #define EEM_HEAD    2       /* 2 byte header */
0038 
0039 /*-------------------------------------------------------------------------*/
0040 
0041 static void eem_linkcmd_complete(struct urb *urb)
0042 {
0043     dev_kfree_skb(urb->context);
0044     usb_free_urb(urb);
0045 }
0046 
0047 static void eem_linkcmd(struct usbnet *dev, struct sk_buff *skb)
0048 {
0049     struct urb      *urb;
0050     int         status;
0051 
0052     urb = usb_alloc_urb(0, GFP_ATOMIC);
0053     if (!urb)
0054         goto fail;
0055 
0056     usb_fill_bulk_urb(urb, dev->udev, dev->out,
0057             skb->data, skb->len, eem_linkcmd_complete, skb);
0058 
0059     status = usb_submit_urb(urb, GFP_ATOMIC);
0060     if (status) {
0061         usb_free_urb(urb);
0062 fail:
0063         dev_kfree_skb(skb);
0064         netdev_warn(dev->net, "link cmd failure\n");
0065         return;
0066     }
0067 }
0068 
0069 static int eem_bind(struct usbnet *dev, struct usb_interface *intf)
0070 {
0071     int status = 0;
0072 
0073     status = usbnet_get_endpoints(dev, intf);
0074     if (status < 0)
0075         return status;
0076 
0077     /* no jumbogram (16K) support for now */
0078 
0079     dev->net->hard_header_len += EEM_HEAD + ETH_FCS_LEN + VLAN_HLEN;
0080     dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
0081 
0082     return 0;
0083 }
0084 
0085 /*
0086  * EEM permits packing multiple Ethernet frames into USB transfers
0087  * (a "bundle"), but for TX we don't try to do that.
0088  */
0089 static struct sk_buff *eem_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
0090                        gfp_t flags)
0091 {
0092     struct sk_buff  *skb2 = NULL;
0093     u16     len = skb->len;
0094     u32     crc = 0;
0095     int     padlen = 0;
0096 
0097     /* When ((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket) is
0098      * zero, stick two bytes of zero length EEM packet on the end.
0099      * Else the framework would add invalid single byte padding,
0100      * since it can't know whether ZLPs will be handled right by
0101      * all the relevant hardware and software.
0102      */
0103     if (!((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket))
0104         padlen += 2;
0105 
0106     if (!skb_cloned(skb)) {
0107         int headroom = skb_headroom(skb);
0108         int tailroom = skb_tailroom(skb);
0109 
0110         if ((tailroom >= ETH_FCS_LEN + padlen) &&
0111             (headroom >= EEM_HEAD))
0112             goto done;
0113 
0114         if ((headroom + tailroom)
0115                 > (EEM_HEAD + ETH_FCS_LEN + padlen)) {
0116             skb->data = memmove(skb->head +
0117                     EEM_HEAD,
0118                     skb->data,
0119                     skb->len);
0120             skb_set_tail_pointer(skb, len);
0121             goto done;
0122         }
0123     }
0124 
0125     skb2 = skb_copy_expand(skb, EEM_HEAD, ETH_FCS_LEN + padlen, flags);
0126     dev_kfree_skb_any(skb);
0127     if (!skb2)
0128         return NULL;
0129 
0130     skb = skb2;
0131 
0132 done:
0133     /* we don't use the "no Ethernet CRC" option */
0134     crc = crc32_le(~0, skb->data, skb->len);
0135     crc = ~crc;
0136 
0137     put_unaligned_le32(crc, skb_put(skb, 4));
0138 
0139     /* EEM packet header format:
0140      * b0..13:  length of ethernet frame
0141      * b14:     bmCRC (1 == valid Ethernet CRC)
0142      * b15:     bmType (0 == data)
0143      */
0144     len = skb->len;
0145     put_unaligned_le16(BIT(14) | len, skb_push(skb, 2));
0146 
0147     /* Bundle a zero length EEM packet if needed */
0148     if (padlen)
0149         put_unaligned_le16(0, skb_put(skb, 2));
0150 
0151     return skb;
0152 }
0153 
0154 static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
0155 {
0156     /*
0157      * Our task here is to strip off framing, leaving skb with one
0158      * data frame for the usbnet framework code to process.  But we
0159      * may have received multiple EEM payloads, or command payloads.
0160      * So we must process _everything_ as if it's a header, except
0161      * maybe the last data payload
0162      *
0163      * REVISIT the framework needs updating so that when we consume
0164      * all payloads (the last or only message was a command, or a
0165      * zero length EEM packet) that is not accounted as an rx_error.
0166      */
0167     do {
0168         struct sk_buff  *skb2 = NULL;
0169         u16     header;
0170         u16     len = 0;
0171 
0172         /* incomplete EEM header? */
0173         if (skb->len < EEM_HEAD)
0174             return 0;
0175 
0176         /*
0177          * EEM packet header format:
0178          * b0..14:  EEM type dependent (Data or Command)
0179          * b15:     bmType
0180          */
0181         header = get_unaligned_le16(skb->data);
0182         skb_pull(skb, EEM_HEAD);
0183 
0184         /*
0185          * The bmType bit helps to denote when EEM
0186          * packet is data or command :
0187          *  bmType = 0  : EEM data payload
0188          *  bmType = 1  : EEM (link) command
0189          */
0190         if (header & BIT(15)) {
0191             u16 bmEEMCmd;
0192 
0193             /*
0194              * EEM (link) command packet:
0195              * b0..10:  bmEEMCmdParam
0196              * b11..13: bmEEMCmd
0197              * b14:     bmReserved (must be 0)
0198              * b15:     1 (EEM command)
0199              */
0200             if (header & BIT(14)) {
0201                 netdev_dbg(dev->net, "reserved command %04x\n",
0202                        header);
0203                 continue;
0204             }
0205 
0206             bmEEMCmd = (header >> 11) & 0x7;
0207             switch (bmEEMCmd) {
0208 
0209             /* Responding to echo requests is mandatory. */
0210             case 0:     /* Echo command */
0211                 len = header & 0x7FF;
0212 
0213                 /* bogus command? */
0214                 if (skb->len < len)
0215                     return 0;
0216 
0217                 skb2 = skb_clone(skb, GFP_ATOMIC);
0218                 if (unlikely(!skb2))
0219                     goto next;
0220                 skb_trim(skb2, len);
0221                 put_unaligned_le16(BIT(15) | BIT(11) | len,
0222                         skb_push(skb2, 2));
0223                 eem_linkcmd(dev, skb2);
0224                 break;
0225 
0226             /*
0227              * Host may choose to ignore hints.
0228              *  - suspend: peripheral ready to suspend
0229              *  - response: suggest N millisec polling
0230              *  - response complete: suggest N sec polling
0231              *
0232              * Suspend is reported and maybe heeded.
0233              */
0234             case 2:     /* Suspend hint */
0235                 usbnet_device_suggests_idle(dev);
0236                 continue;
0237             case 3:     /* Response hint */
0238             case 4:     /* Response complete hint */
0239                 continue;
0240 
0241             /*
0242              * Hosts should never receive host-to-peripheral
0243              * or reserved command codes; or responses to an
0244              * echo command we didn't send.
0245              */
0246             case 1:     /* Echo response */
0247             case 5:     /* Tickle */
0248             default:    /* reserved */
0249                 netdev_warn(dev->net,
0250                         "unexpected link command %d\n",
0251                         bmEEMCmd);
0252                 continue;
0253             }
0254 
0255         } else {
0256             u32 crc, crc2;
0257             int is_last;
0258 
0259             /* zero length EEM packet? */
0260             if (header == 0)
0261                 continue;
0262 
0263             /*
0264              * EEM data packet header :
0265              * b0..13:  length of ethernet frame
0266              * b14:     bmCRC
0267              * b15:     0 (EEM data)
0268              */
0269             len = header & 0x3FFF;
0270 
0271             /* bogus EEM payload? */
0272             if (skb->len < len)
0273                 return 0;
0274 
0275             /* bogus ethernet frame? */
0276             if (len < (ETH_HLEN + ETH_FCS_LEN))
0277                 goto next;
0278 
0279             /*
0280              * Treat the last payload differently: framework
0281              * code expects our "fixup" to have stripped off
0282              * headers, so "skb" is a data packet (or error).
0283              * Else if it's not the last payload, keep "skb"
0284              * for further processing.
0285              */
0286             is_last = (len == skb->len);
0287             if (is_last)
0288                 skb2 = skb;
0289             else {
0290                 skb2 = skb_clone(skb, GFP_ATOMIC);
0291                 if (unlikely(!skb2))
0292                     return 0;
0293             }
0294 
0295             /*
0296              * The bmCRC helps to denote when the CRC field in
0297              * the Ethernet frame contains a calculated CRC:
0298              *  bmCRC = 1   : CRC is calculated
0299              *  bmCRC = 0   : CRC = 0xDEADBEEF
0300              */
0301             if (header & BIT(14)) {
0302                 crc = get_unaligned_le32(skb2->data
0303                         + len - ETH_FCS_LEN);
0304                 crc2 = ~crc32_le(~0, skb2->data, skb2->len
0305                         - ETH_FCS_LEN);
0306             } else {
0307                 crc = get_unaligned_be32(skb2->data
0308                         + len - ETH_FCS_LEN);
0309                 crc2 = 0xdeadbeef;
0310             }
0311             skb_trim(skb2, len - ETH_FCS_LEN);
0312 
0313             if (is_last)
0314                 return crc == crc2;
0315 
0316             if (unlikely(crc != crc2)) {
0317                 dev->net->stats.rx_errors++;
0318                 dev_kfree_skb_any(skb2);
0319             } else
0320                 usbnet_skb_return(dev, skb2);
0321         }
0322 
0323 next:
0324         skb_pull(skb, len);
0325     } while (skb->len);
0326 
0327     return 1;
0328 }
0329 
0330 static const struct driver_info eem_info = {
0331     .description =  "CDC EEM Device",
0332     .flags =    FLAG_ETHER | FLAG_POINTTOPOINT,
0333     .bind =     eem_bind,
0334     .rx_fixup = eem_rx_fixup,
0335     .tx_fixup = eem_tx_fixup,
0336 };
0337 
0338 /*-------------------------------------------------------------------------*/
0339 
0340 static const struct usb_device_id products[] = {
0341 {
0342     USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_EEM,
0343             USB_CDC_PROTO_EEM),
0344     .driver_info = (unsigned long) &eem_info,
0345 },
0346 {
0347     /* EMPTY == end of list */
0348 },
0349 };
0350 MODULE_DEVICE_TABLE(usb, products);
0351 
0352 static struct usb_driver eem_driver = {
0353     .name =     "cdc_eem",
0354     .id_table = products,
0355     .probe =    usbnet_probe,
0356     .disconnect =   usbnet_disconnect,
0357     .suspend =  usbnet_suspend,
0358     .resume =   usbnet_resume,
0359     .disable_hub_initiated_lpm = 1,
0360 };
0361 
0362 module_usb_driver(eem_driver);
0363 
0364 MODULE_AUTHOR("Omar Laazimani <omar.oberthur@gmail.com>");
0365 MODULE_DESCRIPTION("USB CDC EEM");
0366 MODULE_LICENSE("GPL");