cdc_eem.c (8881B)
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 https://usb.org/sites/default/files/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 dev_kfree_skb_any(skb); 127 if (!skb2) 128 return NULL; 129 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");