brintos

brintos / linux-shallow public Read only

0
0
Text · 8.7 KiB · baa9b14 Raw
367 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * USB CDC EEM network interface driver4 * Copyright (C) 2009 Oberthur Technologies5 * by Omar Laazimani, Olivier Condemine6 */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 Emulation25 * Model" (EEM) specification, which encapsulates Ethernet frames26 * for transport over USB using a simpler USB device model than the27 * previous CDC "Ethernet Control Model" (ECM, or "CDC Ethernet").28 *29 * For details, see https://usb.org/sites/default/files/CDC_EEM10.pdf30 *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-200935 */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 transfers87 * (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) is98	 * 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 by101	 * 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 frame141	 * 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 one158	 * data frame for the usbnet framework code to process.  But we159	 * may have received multiple EEM payloads, or command payloads.160	 * So we must process _everything_ as if it's a header, except161	 * maybe the last data payload162	 *163	 * REVISIT the framework needs updating so that when we consume164	 * all payloads (the last or only message was a command, or a165	 * 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:		bmType180		 */181		header = get_unaligned_le16(skb->data);182		skb_pull(skb, EEM_HEAD);183 184		/*185		 * The bmType bit helps to denote when EEM186		 * packet is data or command :187		 *	bmType = 0	: EEM data payload188		 *	bmType = 1	: EEM (link) command189		 */190		if (header & BIT(15)) {191			u16	bmEEMCmd;192 193			/*194			 * EEM (link) command packet:195			 * b0..10:	bmEEMCmdParam196			 * b11..13:	bmEEMCmd197			 * 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) | BIT(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 suspend229			 *  - response: suggest N millisec polling230			 *  - response complete: suggest N sec polling231			 *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-peripheral243			 * or reserved command codes; or responses to an244			 * 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 frame266			 * b14:		bmCRC267			 * 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: framework281			 * code expects our "fixup" to have stripped off282			 * 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 in297			 * the Ethernet frame contains a calculated CRC:298			 *	bmCRC = 1	: CRC is calculated299			 *	bmCRC = 0	: CRC = 0xDEADBEEF300			 */301			if (header & BIT(14)) {302				crc = get_unaligned_le32(skb2->data303						+ len - ETH_FCS_LEN);304				crc2 = ~crc32_le(~0, skb2->data, skb2->len305						- ETH_FCS_LEN);306			} else {307				crc = get_unaligned_be32(skb2->data308						+ 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			} else320				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