19f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani/* 29f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * USB CDC EEM network interface driver 39f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * Copyright (C) 2009 Oberthur Technologies 49f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * by Omar Laazimani, Olivier Condemine 59f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * 69f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * This program is free software; you can redistribute it and/or modify 79f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * it under the terms of the GNU General Public License as published by 89f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * the Free Software Foundation; either version 2 of the License, or 99f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * (at your option) any later version. 109f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * 119f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * This program is distributed in the hope that it will be useful, 129f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * but WITHOUT ANY WARRANTY; without even the implied warranty of 139f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 149f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * GNU General Public License for more details. 159f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * 169f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * You should have received a copy of the GNU General Public License 179f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * along with this program; if not, write to the Free Software 189f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 199f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani */ 209f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 219f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani#include <linux/module.h> 229f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani#include <linux/init.h> 239f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani#include <linux/netdevice.h> 249f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani#include <linux/etherdevice.h> 259f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani#include <linux/ctype.h> 269f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani#include <linux/ethtool.h> 279f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani#include <linux/workqueue.h> 289f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani#include <linux/mii.h> 299f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani#include <linux/usb.h> 309f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani#include <linux/crc32.h> 319f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani#include <linux/usb/cdc.h> 329f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani#include <linux/usb/usbnet.h> 335a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/gfp.h> 349f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 359f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 369f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani/* 379f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * This driver is an implementation of the CDC "Ethernet Emulation 389f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * Model" (EEM) specification, which encapsulates Ethernet frames 399f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * for transport over USB using a simpler USB device model than the 409f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * previous CDC "Ethernet Control Model" (ECM, or "CDC Ethernet"). 419f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * 429f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * For details, see www.usb.org/developers/devclass_docs/CDC_EEM10.pdf 439f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * 449f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * This version has been tested with GIGAntIC WuaoW SIM Smart Card on 2.6.24, 459f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * 2.6.27 and 2.6.30rc2 kernel. 469f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * It has also been validated on Openmoko Om 2008.12 (based on 2.6.24 kernel). 479f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * build on 23-April-2009 489f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani */ 499f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 509f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani#define EEM_HEAD 2 /* 2 byte header */ 519f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 529f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani/*-------------------------------------------------------------------------*/ 539f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 549f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimanistatic void eem_linkcmd_complete(struct urb *urb) 559f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani{ 569f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani dev_kfree_skb(urb->context); 579f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani usb_free_urb(urb); 589f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani} 599f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 609f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimanistatic void eem_linkcmd(struct usbnet *dev, struct sk_buff *skb) 619f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani{ 629f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani struct urb *urb; 639f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani int status; 649f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 659f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani urb = usb_alloc_urb(0, GFP_ATOMIC); 669f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (!urb) 679f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani goto fail; 689f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 699f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani usb_fill_bulk_urb(urb, dev->udev, dev->out, 709f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani skb->data, skb->len, eem_linkcmd_complete, skb); 719f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 729f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani status = usb_submit_urb(urb, GFP_ATOMIC); 739f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (status) { 749f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani usb_free_urb(urb); 759f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimanifail: 769f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani dev_kfree_skb(skb); 7760b86755929e1a7e9038c8d860a8491cfdf8d93aJoe Perches netdev_warn(dev->net, "link cmd failure\n"); 789f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani return; 799f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani } 809f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani} 819f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 829f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimanistatic int eem_bind(struct usbnet *dev, struct usb_interface *intf) 839f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani{ 849f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani int status = 0; 859f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 869f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani status = usbnet_get_endpoints(dev, intf); 879f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (status < 0) { 889f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani usb_set_intfdata(intf, NULL); 899f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani usb_driver_release_interface(driver_of(intf), intf); 909f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani return status; 919f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani } 929f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 939f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* no jumbogram (16K) support for now */ 949f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 959f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani dev->net->hard_header_len += EEM_HEAD + ETH_FCS_LEN; 9678fb72f7936c01d5b426c03a691eca082b03f2b9Rabin Vincent dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; 979f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 989f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani return 0; 999f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani} 1009f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 1019f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani/* 1029f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * EEM permits packing multiple Ethernet frames into USB transfers 1039f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * (a "bundle"), but for TX we don't try to do that. 1049f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani */ 1059f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimanistatic struct sk_buff *eem_tx_fixup(struct usbnet *dev, struct sk_buff *skb, 1069f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani gfp_t flags) 1079f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani{ 1089f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani struct sk_buff *skb2 = NULL; 1099f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani u16 len = skb->len; 1109f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani u32 crc = 0; 1119f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani int padlen = 0; 1129f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 1139f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* When ((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket) is 1149f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * zero, stick two bytes of zero length EEM packet on the end. 1159f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * Else the framework would add invalid single byte padding, 1169f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * since it can't know whether ZLPs will be handled right by 1179f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * all the relevant hardware and software. 1189f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani */ 1199f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (!((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket)) 1209f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani padlen += 2; 1219f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 1229f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (!skb_cloned(skb)) { 1239f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani int headroom = skb_headroom(skb); 1249f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani int tailroom = skb_tailroom(skb); 1259f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 1268e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches if ((tailroom >= ETH_FCS_LEN + padlen) && 1278e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches (headroom >= EEM_HEAD)) 1289f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani goto done; 1299f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 1309f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if ((headroom + tailroom) 1319f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani > (EEM_HEAD + ETH_FCS_LEN + padlen)) { 1329f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani skb->data = memmove(skb->head + 1339f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani EEM_HEAD, 1349f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani skb->data, 1359f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani skb->len); 1369f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani skb_set_tail_pointer(skb, len); 1379f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani goto done; 1389f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani } 1399f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani } 1409f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 1419f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani skb2 = skb_copy_expand(skb, EEM_HEAD, ETH_FCS_LEN + padlen, flags); 1429f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (!skb2) 1439f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani return NULL; 1449f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 1459f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani dev_kfree_skb_any(skb); 1469f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani skb = skb2; 1479f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 1489f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimanidone: 1499f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* we don't use the "no Ethernet CRC" option */ 1509f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani crc = crc32_le(~0, skb->data, skb->len); 1519f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani crc = ~crc; 1529f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 1539f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani put_unaligned_le32(crc, skb_put(skb, 4)); 1549f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 1559f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* EEM packet header format: 1569f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * b0..13: length of ethernet frame 1579f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * b14: bmCRC (1 == valid Ethernet CRC) 1589f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * b15: bmType (0 == data) 1599f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani */ 1609f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani len = skb->len; 1619f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani put_unaligned_le16(BIT(14) | len, skb_push(skb, 2)); 1629f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 1639f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* Bundle a zero length EEM packet if needed */ 1649f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (padlen) 1659f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani put_unaligned_le16(0, skb_put(skb, 2)); 1669f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 1679f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani return skb; 1689f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani} 1699f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 1709f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimanistatic int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 1719f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani{ 1729f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* 1739f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * Our task here is to strip off framing, leaving skb with one 1749f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * data frame for the usbnet framework code to process. But we 1759f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * may have received multiple EEM payloads, or command payloads. 1769f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * So we must process _everything_ as if it's a header, except 1779f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * maybe the last data payload 1789f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * 1799f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * REVISIT the framework needs updating so that when we consume 1809f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * all payloads (the last or only message was a command, or a 1819f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * zero length EEM packet) that is not accounted as an rx_error. 1829f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani */ 1839f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani do { 1849f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani struct sk_buff *skb2 = NULL; 1859f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani u16 header; 1869f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani u16 len = 0; 1879f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 1889f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* incomplete EEM header? */ 1899f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (skb->len < EEM_HEAD) 1909f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani return 0; 1919f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 1929f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* 1939f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * EEM packet header format: 19425985edcedea6396277003854657b5f3cb31a628Lucas De Marchi * b0..14: EEM type dependent (Data or Command) 1959f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * b15: bmType 1969f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani */ 1979f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani header = get_unaligned_le16(skb->data); 1989f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani skb_pull(skb, EEM_HEAD); 1999f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 2009f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* 2019f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * The bmType bit helps to denote when EEM 2029f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * packet is data or command : 2039f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * bmType = 0 : EEM data payload 2049f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * bmType = 1 : EEM (link) command 2059f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani */ 2069f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (header & BIT(15)) { 2079f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani u16 bmEEMCmd; 2089f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 2099f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* 2109f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * EEM (link) command packet: 2119f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * b0..10: bmEEMCmdParam 2129f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * b11..13: bmEEMCmd 2139f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * b14: bmReserved (must be 0) 2149f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * b15: 1 (EEM command) 2159f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani */ 2169f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (header & BIT(14)) { 21760b86755929e1a7e9038c8d860a8491cfdf8d93aJoe Perches netdev_dbg(dev->net, "reserved command %04x\n", 21860b86755929e1a7e9038c8d860a8491cfdf8d93aJoe Perches header); 2199f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani continue; 2209f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani } 2219f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 2229f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani bmEEMCmd = (header >> 11) & 0x7; 2239f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani switch (bmEEMCmd) { 2249f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 2259f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* Responding to echo requests is mandatory. */ 2269f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani case 0: /* Echo command */ 2279f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani len = header & 0x7FF; 2289f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 2299f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* bogus command? */ 2309f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (skb->len < len) 2319f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani return 0; 2329f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 2339f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani skb2 = skb_clone(skb, GFP_ATOMIC); 2349f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (unlikely(!skb2)) 2359f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani goto next; 2369f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani skb_trim(skb2, len); 2379f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani put_unaligned_le16(BIT(15) | (1 << 11) | len, 2389f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani skb_push(skb2, 2)); 2399f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani eem_linkcmd(dev, skb2); 2409f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani break; 2419f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 2429f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* 2439f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * Host may choose to ignore hints. 2449f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * - suspend: peripheral ready to suspend 2459f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * - response: suggest N millisec polling 2469f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * - response complete: suggest N sec polling 2479f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani */ 2489f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani case 2: /* Suspend hint */ 2499f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani case 3: /* Response hint */ 2509f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani case 4: /* Response complete hint */ 2519f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani continue; 2529f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 2539f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* 2549f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * Hosts should never receive host-to-peripheral 2559f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * or reserved command codes; or responses to an 2569f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * echo command we didn't send. 2579f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani */ 2589f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani case 1: /* Echo response */ 2599f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani case 5: /* Tickle */ 2609f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani default: /* reserved */ 26160b86755929e1a7e9038c8d860a8491cfdf8d93aJoe Perches netdev_warn(dev->net, 26260b86755929e1a7e9038c8d860a8491cfdf8d93aJoe Perches "unexpected link command %d\n", 26360b86755929e1a7e9038c8d860a8491cfdf8d93aJoe Perches bmEEMCmd); 2649f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani continue; 2659f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani } 2669f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 2679f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani } else { 2689f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani u32 crc, crc2; 2699f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani int is_last; 2709f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 2719f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* zero length EEM packet? */ 2729f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (header == 0) 2739f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani continue; 2749f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 2759f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* 2769f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * EEM data packet header : 2779f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * b0..13: length of ethernet frame 2789f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * b14: bmCRC 2799f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * b15: 0 (EEM data) 2809f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani */ 2819f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani len = header & 0x3FFF; 2829f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 2839f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* bogus EEM payload? */ 2849f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (skb->len < len) 2859f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani return 0; 2869f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 2879f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* bogus ethernet frame? */ 2889f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (len < (ETH_HLEN + ETH_FCS_LEN)) 2899f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani goto next; 2909f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 2919f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* 2929f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * Treat the last payload differently: framework 2939f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * code expects our "fixup" to have stripped off 2949f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * headers, so "skb" is a data packet (or error). 2959f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * Else if it's not the last payload, keep "skb" 2969f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * for further processing. 2979f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani */ 2989f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani is_last = (len == skb->len); 2999f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (is_last) 3009f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani skb2 = skb; 3019f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani else { 3029f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani skb2 = skb_clone(skb, GFP_ATOMIC); 3039f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (unlikely(!skb2)) 3049f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani return 0; 3059f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani } 3069f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 3079f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* 3089f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * The bmCRC helps to denote when the CRC field in 3099f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * the Ethernet frame contains a calculated CRC: 3109f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * bmCRC = 1 : CRC is calculated 3119f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani * bmCRC = 0 : CRC = 0xDEADBEEF 3129f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani */ 3139ca33a0f1abdefea3811666d9e87af11fd0af6c6Brian Niebuhr if (header & BIT(14)) { 3149ca33a0f1abdefea3811666d9e87af11fd0af6c6Brian Niebuhr crc = get_unaligned_le32(skb2->data 3159ca33a0f1abdefea3811666d9e87af11fd0af6c6Brian Niebuhr + len - ETH_FCS_LEN); 3169ca33a0f1abdefea3811666d9e87af11fd0af6c6Brian Niebuhr crc2 = ~crc32_le(~0, skb2->data, skb2->len 3179ca33a0f1abdefea3811666d9e87af11fd0af6c6Brian Niebuhr - ETH_FCS_LEN); 3189ca33a0f1abdefea3811666d9e87af11fd0af6c6Brian Niebuhr } else { 3199ca33a0f1abdefea3811666d9e87af11fd0af6c6Brian Niebuhr crc = get_unaligned_be32(skb2->data 3209ca33a0f1abdefea3811666d9e87af11fd0af6c6Brian Niebuhr + len - ETH_FCS_LEN); 3219f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani crc2 = 0xdeadbeef; 3229ca33a0f1abdefea3811666d9e87af11fd0af6c6Brian Niebuhr } 3239ca33a0f1abdefea3811666d9e87af11fd0af6c6Brian Niebuhr skb_trim(skb2, len - ETH_FCS_LEN); 3249f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 3259f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (is_last) 3269f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani return crc == crc2; 3279f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 3289f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani if (unlikely(crc != crc2)) { 329eaea43abf30c8ccb447c190e7c94b46b5f75eae6Herbert Xu dev->net->stats.rx_errors++; 3309f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani dev_kfree_skb_any(skb2); 3319f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani } else 3329f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani usbnet_skb_return(dev, skb2); 3339f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani } 3349f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 3359f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimaninext: 3369f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani skb_pull(skb, len); 3379f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani } while (skb->len); 3389f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 3399f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani return 1; 3409f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani} 3419f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 3429f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimanistatic const struct driver_info eem_info = { 3439f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani .description = "CDC EEM Device", 344c261344d3ce3edac781f9d3c7eabe2e96d8e8fe8Arnd Bergmann .flags = FLAG_ETHER | FLAG_POINTTOPOINT, 3459f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani .bind = eem_bind, 3469f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani .rx_fixup = eem_rx_fixup, 3479f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani .tx_fixup = eem_tx_fixup, 3489f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani}; 3499f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 3509f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani/*-------------------------------------------------------------------------*/ 3519f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 3529f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimanistatic const struct usb_device_id products[] = { 3539f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani{ 3549f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_EEM, 3559f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani USB_CDC_PROTO_EEM), 3569f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani .driver_info = (unsigned long) &eem_info, 3579f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani}, 3589f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani{ 3599f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani /* EMPTY == end of list */ 3609f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani}, 3619f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani}; 3629f722c0978b04acba209f8ca1896ad05814bc3a3Omar LaazimaniMODULE_DEVICE_TABLE(usb, products); 3639f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 3649f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimanistatic struct usb_driver eem_driver = { 3659f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani .name = "cdc_eem", 3669f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani .id_table = products, 3679f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani .probe = usbnet_probe, 3689f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani .disconnect = usbnet_disconnect, 3699f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani .suspend = usbnet_suspend, 3709f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani .resume = usbnet_resume, 3719f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani}; 3729f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 373d632eb1bf22e11def74e4e53cc47d790fbdba105Greg Kroah-Hartmanmodule_usb_driver(eem_driver); 3749f722c0978b04acba209f8ca1896ad05814bc3a3Omar Laazimani 3759f722c0978b04acba209f8ca1896ad05814bc3a3Omar LaazimaniMODULE_AUTHOR("Omar Laazimani <omar.oberthur@gmail.com>"); 3769f722c0978b04acba209f8ca1896ad05814bc3a3Omar LaazimaniMODULE_DESCRIPTION("USB CDC EEM"); 3779f722c0978b04acba209f8ca1896ad05814bc3a3Omar LaazimaniMODULE_LICENSE("GPL"); 378