11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 29f722c09SOmar Laazimani /* 39f722c09SOmar Laazimani * USB CDC EEM network interface driver 49f722c09SOmar Laazimani * Copyright (C) 2009 Oberthur Technologies 59f722c09SOmar Laazimani * by Omar Laazimani, Olivier Condemine 69f722c09SOmar Laazimani */ 79f722c09SOmar Laazimani 89f722c09SOmar Laazimani #include <linux/module.h> 99f722c09SOmar Laazimani #include <linux/netdevice.h> 109f722c09SOmar Laazimani #include <linux/etherdevice.h> 119f722c09SOmar Laazimani #include <linux/ctype.h> 129f722c09SOmar Laazimani #include <linux/ethtool.h> 139f722c09SOmar Laazimani #include <linux/workqueue.h> 149f722c09SOmar Laazimani #include <linux/mii.h> 159f722c09SOmar Laazimani #include <linux/usb.h> 169f722c09SOmar Laazimani #include <linux/crc32.h> 179f722c09SOmar Laazimani #include <linux/usb/cdc.h> 189f722c09SOmar Laazimani #include <linux/usb/usbnet.h> 195a0e3ad6STejun Heo #include <linux/gfp.h> 20a66fe165SIan Coolidge #include <linux/if_vlan.h> 219f722c09SOmar Laazimani 229f722c09SOmar Laazimani 239f722c09SOmar Laazimani /* 249f722c09SOmar Laazimani * This driver is an implementation of the CDC "Ethernet Emulation 259f722c09SOmar Laazimani * Model" (EEM) specification, which encapsulates Ethernet frames 269f722c09SOmar Laazimani * for transport over USB using a simpler USB device model than the 279f722c09SOmar Laazimani * previous CDC "Ethernet Control Model" (ECM, or "CDC Ethernet"). 289f722c09SOmar Laazimani * 29b81ac784SJonathan Davies * For details, see https://usb.org/sites/default/files/CDC_EEM10.pdf 309f722c09SOmar Laazimani * 319f722c09SOmar Laazimani * This version has been tested with GIGAntIC WuaoW SIM Smart Card on 2.6.24, 329f722c09SOmar Laazimani * 2.6.27 and 2.6.30rc2 kernel. 339f722c09SOmar Laazimani * It has also been validated on Openmoko Om 2008.12 (based on 2.6.24 kernel). 349f722c09SOmar Laazimani * build on 23-April-2009 359f722c09SOmar Laazimani */ 369f722c09SOmar Laazimani 379f722c09SOmar Laazimani #define EEM_HEAD 2 /* 2 byte header */ 389f722c09SOmar Laazimani 399f722c09SOmar Laazimani /*-------------------------------------------------------------------------*/ 409f722c09SOmar Laazimani 419f722c09SOmar Laazimani static void eem_linkcmd_complete(struct urb *urb) 429f722c09SOmar Laazimani { 439f722c09SOmar Laazimani dev_kfree_skb(urb->context); 449f722c09SOmar Laazimani usb_free_urb(urb); 459f722c09SOmar Laazimani } 469f722c09SOmar Laazimani 479f722c09SOmar Laazimani static void eem_linkcmd(struct usbnet *dev, struct sk_buff *skb) 489f722c09SOmar Laazimani { 499f722c09SOmar Laazimani struct urb *urb; 509f722c09SOmar Laazimani int status; 519f722c09SOmar Laazimani 529f722c09SOmar Laazimani urb = usb_alloc_urb(0, GFP_ATOMIC); 539f722c09SOmar Laazimani if (!urb) 549f722c09SOmar Laazimani goto fail; 559f722c09SOmar Laazimani 569f722c09SOmar Laazimani usb_fill_bulk_urb(urb, dev->udev, dev->out, 579f722c09SOmar Laazimani skb->data, skb->len, eem_linkcmd_complete, skb); 589f722c09SOmar Laazimani 599f722c09SOmar Laazimani status = usb_submit_urb(urb, GFP_ATOMIC); 609f722c09SOmar Laazimani if (status) { 619f722c09SOmar Laazimani usb_free_urb(urb); 629f722c09SOmar Laazimani fail: 639f722c09SOmar Laazimani dev_kfree_skb(skb); 6460b86755SJoe Perches netdev_warn(dev->net, "link cmd failure\n"); 659f722c09SOmar Laazimani return; 669f722c09SOmar Laazimani } 679f722c09SOmar Laazimani } 689f722c09SOmar Laazimani 699f722c09SOmar Laazimani static int eem_bind(struct usbnet *dev, struct usb_interface *intf) 709f722c09SOmar Laazimani { 719f722c09SOmar Laazimani int status = 0; 729f722c09SOmar Laazimani 739f722c09SOmar Laazimani status = usbnet_get_endpoints(dev, intf); 74bb5441fcSJohan Hovold if (status < 0) 759f722c09SOmar Laazimani return status; 769f722c09SOmar Laazimani 779f722c09SOmar Laazimani /* no jumbogram (16K) support for now */ 789f722c09SOmar Laazimani 79a66fe165SIan Coolidge dev->net->hard_header_len += EEM_HEAD + ETH_FCS_LEN + VLAN_HLEN; 8078fb72f7SRabin Vincent dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; 819f722c09SOmar Laazimani 829f722c09SOmar Laazimani return 0; 839f722c09SOmar Laazimani } 849f722c09SOmar Laazimani 859f722c09SOmar Laazimani /* 869f722c09SOmar Laazimani * EEM permits packing multiple Ethernet frames into USB transfers 879f722c09SOmar Laazimani * (a "bundle"), but for TX we don't try to do that. 889f722c09SOmar Laazimani */ 899f722c09SOmar Laazimani static struct sk_buff *eem_tx_fixup(struct usbnet *dev, struct sk_buff *skb, 909f722c09SOmar Laazimani gfp_t flags) 919f722c09SOmar Laazimani { 929f722c09SOmar Laazimani struct sk_buff *skb2 = NULL; 939f722c09SOmar Laazimani u16 len = skb->len; 949f722c09SOmar Laazimani u32 crc = 0; 959f722c09SOmar Laazimani int padlen = 0; 969f722c09SOmar Laazimani 979f722c09SOmar Laazimani /* When ((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket) is 989f722c09SOmar Laazimani * zero, stick two bytes of zero length EEM packet on the end. 999f722c09SOmar Laazimani * Else the framework would add invalid single byte padding, 1009f722c09SOmar Laazimani * since it can't know whether ZLPs will be handled right by 1019f722c09SOmar Laazimani * all the relevant hardware and software. 1029f722c09SOmar Laazimani */ 1039f722c09SOmar Laazimani if (!((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket)) 1049f722c09SOmar Laazimani padlen += 2; 1059f722c09SOmar Laazimani 1069f722c09SOmar Laazimani if (!skb_cloned(skb)) { 1079f722c09SOmar Laazimani int headroom = skb_headroom(skb); 1089f722c09SOmar Laazimani int tailroom = skb_tailroom(skb); 1099f722c09SOmar Laazimani 1108e95a202SJoe Perches if ((tailroom >= ETH_FCS_LEN + padlen) && 1118e95a202SJoe Perches (headroom >= EEM_HEAD)) 1129f722c09SOmar Laazimani goto done; 1139f722c09SOmar Laazimani 1149f722c09SOmar Laazimani if ((headroom + tailroom) 1159f722c09SOmar Laazimani > (EEM_HEAD + ETH_FCS_LEN + padlen)) { 1169f722c09SOmar Laazimani skb->data = memmove(skb->head + 1179f722c09SOmar Laazimani EEM_HEAD, 1189f722c09SOmar Laazimani skb->data, 1199f722c09SOmar Laazimani skb->len); 1209f722c09SOmar Laazimani skb_set_tail_pointer(skb, len); 1219f722c09SOmar Laazimani goto done; 1229f722c09SOmar Laazimani } 1239f722c09SOmar Laazimani } 1249f722c09SOmar Laazimani 1259f722c09SOmar Laazimani skb2 = skb_copy_expand(skb, EEM_HEAD, ETH_FCS_LEN + padlen, flags); 126c3b26fdfSLinyu Yuan dev_kfree_skb_any(skb); 1279f722c09SOmar Laazimani if (!skb2) 1289f722c09SOmar Laazimani return NULL; 1299f722c09SOmar Laazimani 1309f722c09SOmar Laazimani skb = skb2; 1319f722c09SOmar Laazimani 1329f722c09SOmar Laazimani done: 1339f722c09SOmar Laazimani /* we don't use the "no Ethernet CRC" option */ 1349f722c09SOmar Laazimani crc = crc32_le(~0, skb->data, skb->len); 1359f722c09SOmar Laazimani crc = ~crc; 1369f722c09SOmar Laazimani 1379f722c09SOmar Laazimani put_unaligned_le32(crc, skb_put(skb, 4)); 1389f722c09SOmar Laazimani 1399f722c09SOmar Laazimani /* EEM packet header format: 1409f722c09SOmar Laazimani * b0..13: length of ethernet frame 1419f722c09SOmar Laazimani * b14: bmCRC (1 == valid Ethernet CRC) 1429f722c09SOmar Laazimani * b15: bmType (0 == data) 1439f722c09SOmar Laazimani */ 1449f722c09SOmar Laazimani len = skb->len; 1459f722c09SOmar Laazimani put_unaligned_le16(BIT(14) | len, skb_push(skb, 2)); 1469f722c09SOmar Laazimani 1479f722c09SOmar Laazimani /* Bundle a zero length EEM packet if needed */ 1489f722c09SOmar Laazimani if (padlen) 1499f722c09SOmar Laazimani put_unaligned_le16(0, skb_put(skb, 2)); 1509f722c09SOmar Laazimani 1519f722c09SOmar Laazimani return skb; 1529f722c09SOmar Laazimani } 1539f722c09SOmar Laazimani 1549f722c09SOmar Laazimani static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 1559f722c09SOmar Laazimani { 1569f722c09SOmar Laazimani /* 1579f722c09SOmar Laazimani * Our task here is to strip off framing, leaving skb with one 1589f722c09SOmar Laazimani * data frame for the usbnet framework code to process. But we 1599f722c09SOmar Laazimani * may have received multiple EEM payloads, or command payloads. 1609f722c09SOmar Laazimani * So we must process _everything_ as if it's a header, except 1619f722c09SOmar Laazimani * maybe the last data payload 1629f722c09SOmar Laazimani * 1639f722c09SOmar Laazimani * REVISIT the framework needs updating so that when we consume 1649f722c09SOmar Laazimani * all payloads (the last or only message was a command, or a 1659f722c09SOmar Laazimani * zero length EEM packet) that is not accounted as an rx_error. 1669f722c09SOmar Laazimani */ 1679f722c09SOmar Laazimani do { 1689f722c09SOmar Laazimani struct sk_buff *skb2 = NULL; 1699f722c09SOmar Laazimani u16 header; 1709f722c09SOmar Laazimani u16 len = 0; 1719f722c09SOmar Laazimani 1729f722c09SOmar Laazimani /* incomplete EEM header? */ 1739f722c09SOmar Laazimani if (skb->len < EEM_HEAD) 1749f722c09SOmar Laazimani return 0; 1759f722c09SOmar Laazimani 1769f722c09SOmar Laazimani /* 1779f722c09SOmar Laazimani * EEM packet header format: 17825985edcSLucas De Marchi * b0..14: EEM type dependent (Data or Command) 1799f722c09SOmar Laazimani * b15: bmType 1809f722c09SOmar Laazimani */ 1819f722c09SOmar Laazimani header = get_unaligned_le16(skb->data); 1829f722c09SOmar Laazimani skb_pull(skb, EEM_HEAD); 1839f722c09SOmar Laazimani 1849f722c09SOmar Laazimani /* 1859f722c09SOmar Laazimani * The bmType bit helps to denote when EEM 1869f722c09SOmar Laazimani * packet is data or command : 1879f722c09SOmar Laazimani * bmType = 0 : EEM data payload 1889f722c09SOmar Laazimani * bmType = 1 : EEM (link) command 1899f722c09SOmar Laazimani */ 1909f722c09SOmar Laazimani if (header & BIT(15)) { 1919f722c09SOmar Laazimani u16 bmEEMCmd; 1929f722c09SOmar Laazimani 1939f722c09SOmar Laazimani /* 1949f722c09SOmar Laazimani * EEM (link) command packet: 1959f722c09SOmar Laazimani * b0..10: bmEEMCmdParam 1969f722c09SOmar Laazimani * b11..13: bmEEMCmd 1979f722c09SOmar Laazimani * b14: bmReserved (must be 0) 1989f722c09SOmar Laazimani * b15: 1 (EEM command) 1999f722c09SOmar Laazimani */ 2009f722c09SOmar Laazimani if (header & BIT(14)) { 20160b86755SJoe Perches netdev_dbg(dev->net, "reserved command %04x\n", 20260b86755SJoe Perches header); 2039f722c09SOmar Laazimani continue; 2049f722c09SOmar Laazimani } 2059f722c09SOmar Laazimani 2069f722c09SOmar Laazimani bmEEMCmd = (header >> 11) & 0x7; 2079f722c09SOmar Laazimani switch (bmEEMCmd) { 2089f722c09SOmar Laazimani 2099f722c09SOmar Laazimani /* Responding to echo requests is mandatory. */ 2109f722c09SOmar Laazimani case 0: /* Echo command */ 2119f722c09SOmar Laazimani len = header & 0x7FF; 2129f722c09SOmar Laazimani 2139f722c09SOmar Laazimani /* bogus command? */ 2149f722c09SOmar Laazimani if (skb->len < len) 2159f722c09SOmar Laazimani return 0; 2169f722c09SOmar Laazimani 2179f722c09SOmar Laazimani skb2 = skb_clone(skb, GFP_ATOMIC); 2189f722c09SOmar Laazimani if (unlikely(!skb2)) 2199f722c09SOmar Laazimani goto next; 2209f722c09SOmar Laazimani skb_trim(skb2, len); 221*7fa2d170SOliver Neukum put_unaligned_le16(BIT(15) | BIT(11) | len, 2229f722c09SOmar Laazimani skb_push(skb2, 2)); 2239f722c09SOmar Laazimani eem_linkcmd(dev, skb2); 2249f722c09SOmar Laazimani break; 2259f722c09SOmar Laazimani 2269f722c09SOmar Laazimani /* 2279f722c09SOmar Laazimani * Host may choose to ignore hints. 2289f722c09SOmar Laazimani * - suspend: peripheral ready to suspend 2299f722c09SOmar Laazimani * - response: suggest N millisec polling 2309f722c09SOmar Laazimani * - response complete: suggest N sec polling 2315d9d01a3SOliver Neukum * 2325d9d01a3SOliver Neukum * Suspend is reported and maybe heeded. 2339f722c09SOmar Laazimani */ 2349f722c09SOmar Laazimani case 2: /* Suspend hint */ 2355d9d01a3SOliver Neukum usbnet_device_suggests_idle(dev); 2365d9d01a3SOliver Neukum continue; 2379f722c09SOmar Laazimani case 3: /* Response hint */ 2389f722c09SOmar Laazimani case 4: /* Response complete hint */ 2399f722c09SOmar Laazimani continue; 2409f722c09SOmar Laazimani 2419f722c09SOmar Laazimani /* 2429f722c09SOmar Laazimani * Hosts should never receive host-to-peripheral 2439f722c09SOmar Laazimani * or reserved command codes; or responses to an 2449f722c09SOmar Laazimani * echo command we didn't send. 2459f722c09SOmar Laazimani */ 2469f722c09SOmar Laazimani case 1: /* Echo response */ 2479f722c09SOmar Laazimani case 5: /* Tickle */ 2489f722c09SOmar Laazimani default: /* reserved */ 24960b86755SJoe Perches netdev_warn(dev->net, 25060b86755SJoe Perches "unexpected link command %d\n", 2519f722c09SOmar Laazimani bmEEMCmd); 2529f722c09SOmar Laazimani continue; 2539f722c09SOmar Laazimani } 2549f722c09SOmar Laazimani 2559f722c09SOmar Laazimani } else { 2569f722c09SOmar Laazimani u32 crc, crc2; 2579f722c09SOmar Laazimani int is_last; 2589f722c09SOmar Laazimani 2599f722c09SOmar Laazimani /* zero length EEM packet? */ 2609f722c09SOmar Laazimani if (header == 0) 2619f722c09SOmar Laazimani continue; 2629f722c09SOmar Laazimani 2639f722c09SOmar Laazimani /* 2649f722c09SOmar Laazimani * EEM data packet header : 2659f722c09SOmar Laazimani * b0..13: length of ethernet frame 2669f722c09SOmar Laazimani * b14: bmCRC 2679f722c09SOmar Laazimani * b15: 0 (EEM data) 2689f722c09SOmar Laazimani */ 2699f722c09SOmar Laazimani len = header & 0x3FFF; 2709f722c09SOmar Laazimani 2719f722c09SOmar Laazimani /* bogus EEM payload? */ 2729f722c09SOmar Laazimani if (skb->len < len) 2739f722c09SOmar Laazimani return 0; 2749f722c09SOmar Laazimani 2759f722c09SOmar Laazimani /* bogus ethernet frame? */ 2769f722c09SOmar Laazimani if (len < (ETH_HLEN + ETH_FCS_LEN)) 2779f722c09SOmar Laazimani goto next; 2789f722c09SOmar Laazimani 2799f722c09SOmar Laazimani /* 2809f722c09SOmar Laazimani * Treat the last payload differently: framework 2819f722c09SOmar Laazimani * code expects our "fixup" to have stripped off 2829f722c09SOmar Laazimani * headers, so "skb" is a data packet (or error). 2839f722c09SOmar Laazimani * Else if it's not the last payload, keep "skb" 2849f722c09SOmar Laazimani * for further processing. 2859f722c09SOmar Laazimani */ 2869f722c09SOmar Laazimani is_last = (len == skb->len); 2879f722c09SOmar Laazimani if (is_last) 2889f722c09SOmar Laazimani skb2 = skb; 2899f722c09SOmar Laazimani else { 2909f722c09SOmar Laazimani skb2 = skb_clone(skb, GFP_ATOMIC); 2919f722c09SOmar Laazimani if (unlikely(!skb2)) 2929f722c09SOmar Laazimani return 0; 2939f722c09SOmar Laazimani } 2949f722c09SOmar Laazimani 2959f722c09SOmar Laazimani /* 2969f722c09SOmar Laazimani * The bmCRC helps to denote when the CRC field in 2979f722c09SOmar Laazimani * the Ethernet frame contains a calculated CRC: 2989f722c09SOmar Laazimani * bmCRC = 1 : CRC is calculated 2999f722c09SOmar Laazimani * bmCRC = 0 : CRC = 0xDEADBEEF 3009f722c09SOmar Laazimani */ 3019ca33a0fSBrian Niebuhr if (header & BIT(14)) { 3029ca33a0fSBrian Niebuhr crc = get_unaligned_le32(skb2->data 3039ca33a0fSBrian Niebuhr + len - ETH_FCS_LEN); 3049ca33a0fSBrian Niebuhr crc2 = ~crc32_le(~0, skb2->data, skb2->len 3059ca33a0fSBrian Niebuhr - ETH_FCS_LEN); 3069ca33a0fSBrian Niebuhr } else { 3079ca33a0fSBrian Niebuhr crc = get_unaligned_be32(skb2->data 3089ca33a0fSBrian Niebuhr + len - ETH_FCS_LEN); 3099f722c09SOmar Laazimani crc2 = 0xdeadbeef; 3109ca33a0fSBrian Niebuhr } 3119ca33a0fSBrian Niebuhr skb_trim(skb2, len - ETH_FCS_LEN); 3129f722c09SOmar Laazimani 3139f722c09SOmar Laazimani if (is_last) 3149f722c09SOmar Laazimani return crc == crc2; 3159f722c09SOmar Laazimani 3169f722c09SOmar Laazimani if (unlikely(crc != crc2)) { 317eaea43abSHerbert Xu dev->net->stats.rx_errors++; 3189f722c09SOmar Laazimani dev_kfree_skb_any(skb2); 3199f722c09SOmar Laazimani } else 3209f722c09SOmar Laazimani usbnet_skb_return(dev, skb2); 3219f722c09SOmar Laazimani } 3229f722c09SOmar Laazimani 3239f722c09SOmar Laazimani next: 3249f722c09SOmar Laazimani skb_pull(skb, len); 3259f722c09SOmar Laazimani } while (skb->len); 3269f722c09SOmar Laazimani 3279f722c09SOmar Laazimani return 1; 3289f722c09SOmar Laazimani } 3299f722c09SOmar Laazimani 3309f722c09SOmar Laazimani static const struct driver_info eem_info = { 3319f722c09SOmar Laazimani .description = "CDC EEM Device", 332c261344dSArnd Bergmann .flags = FLAG_ETHER | FLAG_POINTTOPOINT, 3339f722c09SOmar Laazimani .bind = eem_bind, 3349f722c09SOmar Laazimani .rx_fixup = eem_rx_fixup, 3359f722c09SOmar Laazimani .tx_fixup = eem_tx_fixup, 3369f722c09SOmar Laazimani }; 3379f722c09SOmar Laazimani 3389f722c09SOmar Laazimani /*-------------------------------------------------------------------------*/ 3399f722c09SOmar Laazimani 3409f722c09SOmar Laazimani static const struct usb_device_id products[] = { 3419f722c09SOmar Laazimani { 3429f722c09SOmar Laazimani USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_EEM, 3439f722c09SOmar Laazimani USB_CDC_PROTO_EEM), 3449f722c09SOmar Laazimani .driver_info = (unsigned long) &eem_info, 3459f722c09SOmar Laazimani }, 3469f722c09SOmar Laazimani { 3479f722c09SOmar Laazimani /* EMPTY == end of list */ 3489f722c09SOmar Laazimani }, 3499f722c09SOmar Laazimani }; 3509f722c09SOmar Laazimani MODULE_DEVICE_TABLE(usb, products); 3519f722c09SOmar Laazimani 3529f722c09SOmar Laazimani static struct usb_driver eem_driver = { 3539f722c09SOmar Laazimani .name = "cdc_eem", 3549f722c09SOmar Laazimani .id_table = products, 3559f722c09SOmar Laazimani .probe = usbnet_probe, 3569f722c09SOmar Laazimani .disconnect = usbnet_disconnect, 3579f722c09SOmar Laazimani .suspend = usbnet_suspend, 3589f722c09SOmar Laazimani .resume = usbnet_resume, 359e1f12eb6SSarah Sharp .disable_hub_initiated_lpm = 1, 3609f722c09SOmar Laazimani }; 3619f722c09SOmar Laazimani 362d632eb1bSGreg Kroah-Hartman module_usb_driver(eem_driver); 3639f722c09SOmar Laazimani 3649f722c09SOmar Laazimani MODULE_AUTHOR("Omar Laazimani <omar.oberthur@gmail.com>"); 3659f722c09SOmar Laazimani MODULE_DESCRIPTION("USB CDC EEM"); 3669f722c09SOmar Laazimani MODULE_LICENSE("GPL"); 367