| Omar Laazimani | 9f722c0 | 2009-05-04 12:01:43 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  * USB CDC EEM network interface driver | 
 | 3 |  * Copyright (C) 2009 Oberthur Technologies | 
 | 4 |  * by Omar Laazimani, Olivier Condemine | 
 | 5 |  * | 
 | 6 |  * This program is free software; you can redistribute it and/or modify | 
 | 7 |  * it under the terms of the GNU General Public License as published by | 
 | 8 |  * the Free Software Foundation; either version 2 of the License, or | 
 | 9 |  * (at your option) any later version. | 
 | 10 |  * | 
 | 11 |  * This program is distributed in the hope that it will be useful, | 
 | 12 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | 13 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 | 14 |  * GNU General Public License for more details. | 
 | 15 |  * | 
 | 16 |  * You should have received a copy of the GNU General Public License | 
 | 17 |  * along with this program; if not, write to the Free Software | 
 | 18 |  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
 | 19 |  */ | 
 | 20 |  | 
 | 21 | #include <linux/module.h> | 
 | 22 | #include <linux/init.h> | 
 | 23 | #include <linux/netdevice.h> | 
 | 24 | #include <linux/etherdevice.h> | 
 | 25 | #include <linux/ctype.h> | 
 | 26 | #include <linux/ethtool.h> | 
 | 27 | #include <linux/workqueue.h> | 
 | 28 | #include <linux/mii.h> | 
 | 29 | #include <linux/usb.h> | 
 | 30 | #include <linux/crc32.h> | 
 | 31 | #include <linux/usb/cdc.h> | 
 | 32 | #include <linux/usb/usbnet.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 33 | #include <linux/gfp.h> | 
| Omar Laazimani | 9f722c0 | 2009-05-04 12:01:43 -0700 | [diff] [blame] | 34 |  | 
 | 35 |  | 
 | 36 | /* | 
 | 37 |  * This driver is an implementation of the CDC "Ethernet Emulation | 
 | 38 |  * Model" (EEM) specification, which encapsulates Ethernet frames | 
 | 39 |  * for transport over USB using a simpler USB device model than the | 
 | 40 |  * previous CDC "Ethernet Control Model" (ECM, or "CDC Ethernet"). | 
 | 41 |  * | 
 | 42 |  * For details, see www.usb.org/developers/devclass_docs/CDC_EEM10.pdf | 
 | 43 |  * | 
 | 44 |  * This version has been tested with GIGAntIC WuaoW SIM Smart Card on 2.6.24, | 
 | 45 |  * 2.6.27 and 2.6.30rc2 kernel. | 
 | 46 |  * It has also been validated on Openmoko Om 2008.12 (based on 2.6.24 kernel). | 
 | 47 |  * build on 23-April-2009 | 
 | 48 |  */ | 
 | 49 |  | 
 | 50 | #define EEM_HEAD	2		/* 2 byte header */ | 
 | 51 |  | 
 | 52 | /*-------------------------------------------------------------------------*/ | 
 | 53 |  | 
 | 54 | static void eem_linkcmd_complete(struct urb *urb) | 
 | 55 | { | 
 | 56 | 	dev_kfree_skb(urb->context); | 
 | 57 | 	usb_free_urb(urb); | 
 | 58 | } | 
 | 59 |  | 
 | 60 | static void eem_linkcmd(struct usbnet *dev, struct sk_buff *skb) | 
 | 61 | { | 
 | 62 | 	struct urb		*urb; | 
 | 63 | 	int			status; | 
 | 64 |  | 
 | 65 | 	urb = usb_alloc_urb(0, GFP_ATOMIC); | 
 | 66 | 	if (!urb) | 
 | 67 | 		goto fail; | 
 | 68 |  | 
 | 69 | 	usb_fill_bulk_urb(urb, dev->udev, dev->out, | 
 | 70 | 			skb->data, skb->len, eem_linkcmd_complete, skb); | 
 | 71 |  | 
 | 72 | 	status = usb_submit_urb(urb, GFP_ATOMIC); | 
 | 73 | 	if (status) { | 
 | 74 | 		usb_free_urb(urb); | 
 | 75 | fail: | 
 | 76 | 		dev_kfree_skb(skb); | 
| Joe Perches | 60b8675 | 2010-02-17 10:30:23 +0000 | [diff] [blame] | 77 | 		netdev_warn(dev->net, "link cmd failure\n"); | 
| Omar Laazimani | 9f722c0 | 2009-05-04 12:01:43 -0700 | [diff] [blame] | 78 | 		return; | 
 | 79 | 	} | 
 | 80 | } | 
 | 81 |  | 
 | 82 | static int eem_bind(struct usbnet *dev, struct usb_interface *intf) | 
 | 83 | { | 
 | 84 | 	int status = 0; | 
 | 85 |  | 
 | 86 | 	status = usbnet_get_endpoints(dev, intf); | 
 | 87 | 	if (status < 0) { | 
 | 88 | 		usb_set_intfdata(intf, NULL); | 
 | 89 | 		usb_driver_release_interface(driver_of(intf), intf); | 
 | 90 | 		return status; | 
 | 91 | 	} | 
 | 92 |  | 
 | 93 | 	/* no jumbogram (16K) support for now */ | 
 | 94 |  | 
 | 95 | 	dev->net->hard_header_len += EEM_HEAD + ETH_FCS_LEN; | 
 | 96 |  | 
 | 97 | 	return 0; | 
 | 98 | } | 
 | 99 |  | 
 | 100 | /* | 
 | 101 |  * EEM permits packing multiple Ethernet frames into USB transfers | 
 | 102 |  * (a "bundle"), but for TX we don't try to do that. | 
 | 103 |  */ | 
 | 104 | static struct sk_buff *eem_tx_fixup(struct usbnet *dev, struct sk_buff *skb, | 
 | 105 | 				       gfp_t flags) | 
 | 106 | { | 
 | 107 | 	struct sk_buff	*skb2 = NULL; | 
 | 108 | 	u16		len = skb->len; | 
 | 109 | 	u32		crc = 0; | 
 | 110 | 	int		padlen = 0; | 
 | 111 |  | 
 | 112 | 	/* When ((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket) is | 
 | 113 | 	 * zero, stick two bytes of zero length EEM packet on the end. | 
 | 114 | 	 * Else the framework would add invalid single byte padding, | 
 | 115 | 	 * since it can't know whether ZLPs will be handled right by | 
 | 116 | 	 * all the relevant hardware and software. | 
 | 117 | 	 */ | 
 | 118 | 	if (!((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket)) | 
 | 119 | 		padlen += 2; | 
 | 120 |  | 
 | 121 | 	if (!skb_cloned(skb)) { | 
 | 122 | 		int	headroom = skb_headroom(skb); | 
 | 123 | 		int	tailroom = skb_tailroom(skb); | 
 | 124 |  | 
| Joe Perches | 8e95a20 | 2009-12-03 07:58:21 +0000 | [diff] [blame] | 125 | 		if ((tailroom >= ETH_FCS_LEN + padlen) && | 
 | 126 | 		    (headroom >= EEM_HEAD)) | 
| Omar Laazimani | 9f722c0 | 2009-05-04 12:01:43 -0700 | [diff] [blame] | 127 | 			goto done; | 
 | 128 |  | 
 | 129 | 		if ((headroom + tailroom) | 
 | 130 | 				> (EEM_HEAD + ETH_FCS_LEN + padlen)) { | 
 | 131 | 			skb->data = memmove(skb->head + | 
 | 132 | 					EEM_HEAD, | 
 | 133 | 					skb->data, | 
 | 134 | 					skb->len); | 
 | 135 | 			skb_set_tail_pointer(skb, len); | 
 | 136 | 			goto done; | 
 | 137 | 		} | 
 | 138 | 	} | 
 | 139 |  | 
 | 140 | 	skb2 = skb_copy_expand(skb, EEM_HEAD, ETH_FCS_LEN + padlen, flags); | 
 | 141 | 	if (!skb2) | 
 | 142 | 		return NULL; | 
 | 143 |  | 
 | 144 | 	dev_kfree_skb_any(skb); | 
 | 145 | 	skb = skb2; | 
 | 146 |  | 
 | 147 | done: | 
 | 148 | 	/* we don't use the "no Ethernet CRC" option */ | 
 | 149 | 	crc = crc32_le(~0, skb->data, skb->len); | 
 | 150 | 	crc = ~crc; | 
 | 151 |  | 
 | 152 | 	put_unaligned_le32(crc, skb_put(skb, 4)); | 
 | 153 |  | 
 | 154 | 	/* EEM packet header format: | 
 | 155 | 	 * b0..13:	length of ethernet frame | 
 | 156 | 	 * b14:		bmCRC (1 == valid Ethernet CRC) | 
 | 157 | 	 * b15:		bmType (0 == data) | 
 | 158 | 	 */ | 
 | 159 | 	len = skb->len; | 
 | 160 | 	put_unaligned_le16(BIT(14) | len, skb_push(skb, 2)); | 
 | 161 |  | 
 | 162 | 	/* Bundle a zero length EEM packet if needed */ | 
 | 163 | 	if (padlen) | 
 | 164 | 		put_unaligned_le16(0, skb_put(skb, 2)); | 
 | 165 |  | 
 | 166 | 	return skb; | 
 | 167 | } | 
 | 168 |  | 
 | 169 | static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb) | 
 | 170 | { | 
 | 171 | 	/* | 
 | 172 | 	 * Our task here is to strip off framing, leaving skb with one | 
 | 173 | 	 * data frame for the usbnet framework code to process.  But we | 
 | 174 | 	 * may have received multiple EEM payloads, or command payloads. | 
 | 175 | 	 * So we must process _everything_ as if it's a header, except | 
 | 176 | 	 * maybe the last data payload | 
 | 177 | 	 * | 
 | 178 | 	 * REVISIT the framework needs updating so that when we consume | 
 | 179 | 	 * all payloads (the last or only message was a command, or a | 
 | 180 | 	 * zero length EEM packet) that is not accounted as an rx_error. | 
 | 181 | 	 */ | 
 | 182 | 	do { | 
 | 183 | 		struct sk_buff	*skb2 = NULL; | 
 | 184 | 		u16		header; | 
 | 185 | 		u16		len = 0; | 
 | 186 |  | 
 | 187 | 		/* incomplete EEM header? */ | 
 | 188 | 		if (skb->len < EEM_HEAD) | 
 | 189 | 			return 0; | 
 | 190 |  | 
 | 191 | 		/* | 
 | 192 | 		 * EEM packet header format: | 
| Lucas De Marchi | 25985ed | 2011-03-30 22:57:33 -0300 | [diff] [blame] | 193 | 		 * b0..14:	EEM type dependent (Data or Command) | 
| Omar Laazimani | 9f722c0 | 2009-05-04 12:01:43 -0700 | [diff] [blame] | 194 | 		 * b15:		bmType | 
 | 195 | 		 */ | 
 | 196 | 		header = get_unaligned_le16(skb->data); | 
 | 197 | 		skb_pull(skb, EEM_HEAD); | 
 | 198 |  | 
 | 199 | 		/* | 
 | 200 | 		 * The bmType bit helps to denote when EEM | 
 | 201 | 		 * packet is data or command : | 
 | 202 | 		 *	bmType = 0	: EEM data payload | 
 | 203 | 		 *	bmType = 1	: EEM (link) command | 
 | 204 | 		 */ | 
 | 205 | 		if (header & BIT(15)) { | 
 | 206 | 			u16	bmEEMCmd; | 
 | 207 |  | 
 | 208 | 			/* | 
 | 209 | 			 * EEM (link) command packet: | 
 | 210 | 			 * b0..10:	bmEEMCmdParam | 
 | 211 | 			 * b11..13:	bmEEMCmd | 
 | 212 | 			 * b14:		bmReserved (must be 0) | 
 | 213 | 			 * b15:		1 (EEM command) | 
 | 214 | 			 */ | 
 | 215 | 			if (header & BIT(14)) { | 
| Joe Perches | 60b8675 | 2010-02-17 10:30:23 +0000 | [diff] [blame] | 216 | 				netdev_dbg(dev->net, "reserved command %04x\n", | 
 | 217 | 					   header); | 
| Omar Laazimani | 9f722c0 | 2009-05-04 12:01:43 -0700 | [diff] [blame] | 218 | 				continue; | 
 | 219 | 			} | 
 | 220 |  | 
 | 221 | 			bmEEMCmd = (header >> 11) & 0x7; | 
 | 222 | 			switch (bmEEMCmd) { | 
 | 223 |  | 
 | 224 | 			/* Responding to echo requests is mandatory. */ | 
 | 225 | 			case 0:		/* Echo command */ | 
 | 226 | 				len = header & 0x7FF; | 
 | 227 |  | 
 | 228 | 				/* bogus command? */ | 
 | 229 | 				if (skb->len < len) | 
 | 230 | 					return 0; | 
 | 231 |  | 
 | 232 | 				skb2 = skb_clone(skb, GFP_ATOMIC); | 
 | 233 | 				if (unlikely(!skb2)) | 
 | 234 | 					goto next; | 
 | 235 | 				skb_trim(skb2, len); | 
 | 236 | 				put_unaligned_le16(BIT(15) | (1 << 11) | len, | 
 | 237 | 						skb_push(skb2, 2)); | 
 | 238 | 				eem_linkcmd(dev, skb2); | 
 | 239 | 				break; | 
 | 240 |  | 
 | 241 | 			/* | 
 | 242 | 			 * Host may choose to ignore hints. | 
 | 243 | 			 *  - suspend: peripheral ready to suspend | 
 | 244 | 			 *  - response: suggest N millisec polling | 
 | 245 | 			 *  - response complete: suggest N sec polling | 
 | 246 | 			 */ | 
 | 247 | 			case 2:		/* Suspend hint */ | 
 | 248 | 			case 3:		/* Response hint */ | 
 | 249 | 			case 4:		/* Response complete hint */ | 
 | 250 | 				continue; | 
 | 251 |  | 
 | 252 | 			/* | 
 | 253 | 			 * Hosts should never receive host-to-peripheral | 
 | 254 | 			 * or reserved command codes; or responses to an | 
 | 255 | 			 * echo command we didn't send. | 
 | 256 | 			 */ | 
 | 257 | 			case 1:		/* Echo response */ | 
 | 258 | 			case 5:		/* Tickle */ | 
 | 259 | 			default:	/* reserved */ | 
| Joe Perches | 60b8675 | 2010-02-17 10:30:23 +0000 | [diff] [blame] | 260 | 				netdev_warn(dev->net, | 
 | 261 | 					    "unexpected link command %d\n", | 
 | 262 | 					    bmEEMCmd); | 
| Omar Laazimani | 9f722c0 | 2009-05-04 12:01:43 -0700 | [diff] [blame] | 263 | 				continue; | 
 | 264 | 			} | 
 | 265 |  | 
 | 266 | 		} else { | 
 | 267 | 			u32	crc, crc2; | 
 | 268 | 			int	is_last; | 
 | 269 |  | 
 | 270 | 			/* zero length EEM packet? */ | 
 | 271 | 			if (header == 0) | 
 | 272 | 				continue; | 
 | 273 |  | 
 | 274 | 			/* | 
 | 275 | 			 * EEM data packet header : | 
 | 276 | 			 * b0..13:	length of ethernet frame | 
 | 277 | 			 * b14:		bmCRC | 
 | 278 | 			 * b15:		0 (EEM data) | 
 | 279 | 			 */ | 
 | 280 | 			len = header & 0x3FFF; | 
 | 281 |  | 
 | 282 | 			/* bogus EEM payload? */ | 
 | 283 | 			if (skb->len < len) | 
 | 284 | 				return 0; | 
 | 285 |  | 
 | 286 | 			/* bogus ethernet frame? */ | 
 | 287 | 			if (len < (ETH_HLEN + ETH_FCS_LEN)) | 
 | 288 | 				goto next; | 
 | 289 |  | 
 | 290 | 			/* | 
 | 291 | 			 * Treat the last payload differently: framework | 
 | 292 | 			 * code expects our "fixup" to have stripped off | 
 | 293 | 			 * headers, so "skb" is a data packet (or error). | 
 | 294 | 			 * Else if it's not the last payload, keep "skb" | 
 | 295 | 			 * for further processing. | 
 | 296 | 			 */ | 
 | 297 | 			is_last = (len == skb->len); | 
 | 298 | 			if (is_last) | 
 | 299 | 				skb2 = skb; | 
 | 300 | 			else { | 
 | 301 | 				skb2 = skb_clone(skb, GFP_ATOMIC); | 
 | 302 | 				if (unlikely(!skb2)) | 
 | 303 | 					return 0; | 
 | 304 | 			} | 
 | 305 |  | 
| Omar Laazimani | 9f722c0 | 2009-05-04 12:01:43 -0700 | [diff] [blame] | 306 | 			/* | 
 | 307 | 			 * The bmCRC helps to denote when the CRC field in | 
 | 308 | 			 * the Ethernet frame contains a calculated CRC: | 
 | 309 | 			 *	bmCRC = 1	: CRC is calculated | 
 | 310 | 			 *	bmCRC = 0	: CRC = 0xDEADBEEF | 
 | 311 | 			 */ | 
| Brian Niebuhr | 9ca33a0 | 2009-08-10 16:46:59 -0500 | [diff] [blame] | 312 | 			if (header & BIT(14)) { | 
 | 313 | 				crc = get_unaligned_le32(skb2->data | 
 | 314 | 						+ len - ETH_FCS_LEN); | 
 | 315 | 				crc2 = ~crc32_le(~0, skb2->data, skb2->len | 
 | 316 | 						- ETH_FCS_LEN); | 
 | 317 | 			} else { | 
 | 318 | 				crc = get_unaligned_be32(skb2->data | 
 | 319 | 						+ len - ETH_FCS_LEN); | 
| Omar Laazimani | 9f722c0 | 2009-05-04 12:01:43 -0700 | [diff] [blame] | 320 | 				crc2 = 0xdeadbeef; | 
| Brian Niebuhr | 9ca33a0 | 2009-08-10 16:46:59 -0500 | [diff] [blame] | 321 | 			} | 
 | 322 | 			skb_trim(skb2, len - ETH_FCS_LEN); | 
| Omar Laazimani | 9f722c0 | 2009-05-04 12:01:43 -0700 | [diff] [blame] | 323 |  | 
 | 324 | 			if (is_last) | 
 | 325 | 				return crc == crc2; | 
 | 326 |  | 
 | 327 | 			if (unlikely(crc != crc2)) { | 
| Herbert Xu | eaea43a | 2009-06-29 16:49:40 +0000 | [diff] [blame] | 328 | 				dev->net->stats.rx_errors++; | 
| Omar Laazimani | 9f722c0 | 2009-05-04 12:01:43 -0700 | [diff] [blame] | 329 | 				dev_kfree_skb_any(skb2); | 
 | 330 | 			} else | 
 | 331 | 				usbnet_skb_return(dev, skb2); | 
 | 332 | 		} | 
 | 333 |  | 
 | 334 | next: | 
 | 335 | 		skb_pull(skb, len); | 
 | 336 | 	} while (skb->len); | 
 | 337 |  | 
 | 338 | 	return 1; | 
 | 339 | } | 
 | 340 |  | 
 | 341 | static const struct driver_info eem_info = { | 
 | 342 | 	.description =	"CDC EEM Device", | 
| Arnd Bergmann | c261344 | 2011-04-01 20:12:02 -0700 | [diff] [blame] | 343 | 	.flags =	FLAG_ETHER | FLAG_POINTTOPOINT, | 
| Omar Laazimani | 9f722c0 | 2009-05-04 12:01:43 -0700 | [diff] [blame] | 344 | 	.bind =		eem_bind, | 
 | 345 | 	.rx_fixup =	eem_rx_fixup, | 
 | 346 | 	.tx_fixup =	eem_tx_fixup, | 
 | 347 | }; | 
 | 348 |  | 
 | 349 | /*-------------------------------------------------------------------------*/ | 
 | 350 |  | 
 | 351 | static const struct usb_device_id products[] = { | 
 | 352 | { | 
 | 353 | 	USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_EEM, | 
 | 354 | 			USB_CDC_PROTO_EEM), | 
 | 355 | 	.driver_info = (unsigned long) &eem_info, | 
 | 356 | }, | 
 | 357 | { | 
 | 358 | 	/* EMPTY == end of list */ | 
 | 359 | }, | 
 | 360 | }; | 
 | 361 | MODULE_DEVICE_TABLE(usb, products); | 
 | 362 |  | 
 | 363 | static struct usb_driver eem_driver = { | 
 | 364 | 	.name =		"cdc_eem", | 
 | 365 | 	.id_table =	products, | 
 | 366 | 	.probe =	usbnet_probe, | 
 | 367 | 	.disconnect =	usbnet_disconnect, | 
 | 368 | 	.suspend =	usbnet_suspend, | 
 | 369 | 	.resume =	usbnet_resume, | 
 | 370 | }; | 
 | 371 |  | 
 | 372 |  | 
 | 373 | static int __init eem_init(void) | 
 | 374 | { | 
 | 375 | 	return usb_register(&eem_driver); | 
 | 376 | } | 
 | 377 | module_init(eem_init); | 
 | 378 |  | 
 | 379 | static void __exit eem_exit(void) | 
 | 380 | { | 
 | 381 | 	usb_deregister(&eem_driver); | 
 | 382 | } | 
 | 383 | module_exit(eem_exit); | 
 | 384 |  | 
 | 385 | MODULE_AUTHOR("Omar Laazimani <omar.oberthur@gmail.com>"); | 
 | 386 | MODULE_DESCRIPTION("USB CDC EEM"); | 
 | 387 | MODULE_LICENSE("GPL"); |