704 lines · 18.7 KB · C
RawDownload
/* * ipheth.c - Apple iPhone USB Ethernet driver * * Copyright (c) 2009 Diego Giagio <diego@giagio.com> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. Neither the name of GIAGIO.COM nor the names of its contributors *    may be used to endorse or promote products derived from this software *    without specific prior written permission. * * Alternatively, provided that this notice is retained in full, this * software may be distributed under the terms of the GNU General * Public License ("GPL") version 2, in which case the provisions of the * GPL apply INSTEAD OF those given above. * * The provided data structures and external interfaces from this code * are not restricted to be used by modules with a GPL compatible license. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. * * * Attention: iPhone device must be paired, otherwise it won't respond to our * driver. For more info: http://giagio.com/wiki/moin.cgi/iPhoneEthernetDriver * */ #include <linux/kernel.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/usb.h>#include <linux/workqueue.h>#include <linux/usb/cdc.h> #define USB_VENDOR_APPLE        0x05ac #define IPHETH_USBINTF_CLASS    255#define IPHETH_USBINTF_SUBCLASS 253#define IPHETH_USBINTF_PROTO    1 #define IPHETH_IP_ALIGN		2	/* padding at front of URB *//* On iOS devices, NCM headers in RX have a fixed size regardless of DPE count: * - NTH16 (NCMH): 12 bytes, as per CDC NCM 1.0 spec * - NDP16 (NCM0): 96 bytes, of which *    - NDP16 fixed header: 8 bytes *    - maximum of 22 DPEs (21 datagrams + trailer), 4 bytes each */#define IPHETH_NDP16_MAX_DPE	22#define IPHETH_NDP16_HEADER_SIZE (sizeof(struct usb_cdc_ncm_ndp16) + \				  IPHETH_NDP16_MAX_DPE * \				  sizeof(struct usb_cdc_ncm_dpe16))#define IPHETH_NCM_HEADER_SIZE	(sizeof(struct usb_cdc_ncm_nth16) + \				 IPHETH_NDP16_HEADER_SIZE)#define IPHETH_TX_BUF_SIZE      ETH_FRAME_LEN#define IPHETH_RX_BUF_SIZE_LEGACY (IPHETH_IP_ALIGN + ETH_FRAME_LEN)#define IPHETH_RX_BUF_SIZE_NCM	65536 #define IPHETH_TX_TIMEOUT       (5 * HZ) #define IPHETH_INTFNUM          2#define IPHETH_ALT_INTFNUM      1 #define IPHETH_CTRL_ENDP        0x00#define IPHETH_CTRL_BUF_SIZE    0x40#define IPHETH_CTRL_TIMEOUT     (5 * HZ) #define IPHETH_CMD_GET_MACADDR   0x00#define IPHETH_CMD_ENABLE_NCM    0x04#define IPHETH_CMD_CARRIER_CHECK 0x45 #define IPHETH_CARRIER_CHECK_TIMEOUT round_jiffies_relative(1 * HZ)#define IPHETH_CARRIER_ON       0x04 static const struct usb_device_id ipheth_table[] = {	{ USB_VENDOR_AND_INTERFACE_INFO(USB_VENDOR_APPLE, IPHETH_USBINTF_CLASS,					IPHETH_USBINTF_SUBCLASS,					IPHETH_USBINTF_PROTO) },	{ }};MODULE_DEVICE_TABLE(usb, ipheth_table); struct ipheth_device {	struct usb_device *udev;	struct usb_interface *intf;	struct net_device *net;	struct urb *tx_urb;	struct urb *rx_urb;	unsigned char *tx_buf;	unsigned char *rx_buf;	unsigned char *ctrl_buf;	u8 bulk_in;	u8 bulk_out;	struct delayed_work carrier_work;	bool confirmed_pairing;	int (*rcvbulk_callback)(struct urb *urb);	size_t rx_buf_len;}; static int ipheth_rx_submit(struct ipheth_device *dev, gfp_t mem_flags); static int ipheth_alloc_urbs(struct ipheth_device *iphone){	struct urb *tx_urb = NULL;	struct urb *rx_urb = NULL;	u8 *tx_buf = NULL;	u8 *rx_buf = NULL; 	tx_urb = usb_alloc_urb(0, GFP_KERNEL);	if (tx_urb == NULL)		goto error_nomem; 	rx_urb = usb_alloc_urb(0, GFP_KERNEL);	if (rx_urb == NULL)		goto free_tx_urb; 	tx_buf = usb_alloc_coherent(iphone->udev, IPHETH_TX_BUF_SIZE,				    GFP_KERNEL, &tx_urb->transfer_dma);	if (tx_buf == NULL)		goto free_rx_urb; 	rx_buf = usb_alloc_coherent(iphone->udev, iphone->rx_buf_len,				    GFP_KERNEL, &rx_urb->transfer_dma);	if (rx_buf == NULL)		goto free_tx_buf;  	iphone->tx_urb = tx_urb;	iphone->rx_urb = rx_urb;	iphone->tx_buf = tx_buf;	iphone->rx_buf = rx_buf;	return 0; free_tx_buf:	usb_free_coherent(iphone->udev, IPHETH_TX_BUF_SIZE, tx_buf,			  tx_urb->transfer_dma);free_rx_urb:	usb_free_urb(rx_urb);free_tx_urb:	usb_free_urb(tx_urb);error_nomem:	return -ENOMEM;} static void ipheth_free_urbs(struct ipheth_device *iphone){	usb_free_coherent(iphone->udev, iphone->rx_buf_len, iphone->rx_buf,			  iphone->rx_urb->transfer_dma);	usb_free_coherent(iphone->udev, IPHETH_TX_BUF_SIZE, iphone->tx_buf,			  iphone->tx_urb->transfer_dma);	usb_free_urb(iphone->rx_urb);	usb_free_urb(iphone->tx_urb);} static void ipheth_kill_urbs(struct ipheth_device *dev){	usb_kill_urb(dev->tx_urb);	usb_kill_urb(dev->rx_urb);} static int ipheth_consume_skb(char *buf, int len, struct ipheth_device *dev){	struct sk_buff *skb; 	skb = dev_alloc_skb(len);	if (!skb) {		dev->net->stats.rx_dropped++;		return -ENOMEM;	} 	skb_put_data(skb, buf, len);	skb->dev = dev->net;	skb->protocol = eth_type_trans(skb, dev->net); 	dev->net->stats.rx_packets++;	dev->net->stats.rx_bytes += len;	netif_rx(skb); 	return 0;} static int ipheth_rcvbulk_callback_legacy(struct urb *urb){	struct ipheth_device *dev;	char *buf;	int len; 	dev = urb->context; 	if (urb->actual_length <= IPHETH_IP_ALIGN) {		dev->net->stats.rx_length_errors++;		return -EINVAL;	}	len = urb->actual_length - IPHETH_IP_ALIGN;	buf = urb->transfer_buffer + IPHETH_IP_ALIGN; 	return ipheth_consume_skb(buf, len, dev);} /* In "NCM mode", the iOS device encapsulates RX (phone->computer) traffic * in NCM Transfer Blocks (similarly to CDC NCM). However, unlike reverse * tethering (handled by the `cdc_ncm` driver), regular tethering is not * compliant with the CDC NCM spec, as the device is missing the necessary * descriptors, and TX (computer->phone) traffic is not encapsulated * at all. Thus `ipheth` implements a very limited subset of the spec with * the sole purpose of parsing RX URBs. */static int ipheth_rcvbulk_callback_ncm(struct urb *urb){	struct usb_cdc_ncm_nth16 *ncmh;	struct usb_cdc_ncm_ndp16 *ncm0;	struct usb_cdc_ncm_dpe16 *dpe;	struct ipheth_device *dev;	u16 dg_idx, dg_len;	int retval = -EINVAL;	char *buf; 	dev = urb->context; 	if (urb->actual_length < IPHETH_NCM_HEADER_SIZE) {		dev->net->stats.rx_length_errors++;		return retval;	} 	ncmh = urb->transfer_buffer;	if (ncmh->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH16_SIGN) ||	    /* On iOS, NDP16 directly follows NTH16 */	    ncmh->wNdpIndex != cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16)))		goto rx_error; 	ncm0 = urb->transfer_buffer + sizeof(struct usb_cdc_ncm_nth16);	if (ncm0->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN))		goto rx_error; 	dpe = ncm0->dpe16;	for (int dpe_i = 0; dpe_i < IPHETH_NDP16_MAX_DPE; ++dpe_i, ++dpe) {		dg_idx = le16_to_cpu(dpe->wDatagramIndex);		dg_len = le16_to_cpu(dpe->wDatagramLength); 		/* Null DPE must be present after last datagram pointer entry		 * (3.3.1 USB CDC NCM spec v1.0)		 */		if (dg_idx == 0 && dg_len == 0)			return 0; 		if (dg_idx < IPHETH_NCM_HEADER_SIZE ||		    dg_idx >= urb->actual_length ||		    dg_len > urb->actual_length - dg_idx) {			dev->net->stats.rx_length_errors++;			return retval;		} 		buf = urb->transfer_buffer + dg_idx; 		retval = ipheth_consume_skb(buf, dg_len, dev);		if (retval != 0)			return retval;	} rx_error:	dev->net->stats.rx_errors++;	return retval;} static void ipheth_rcvbulk_callback(struct urb *urb){	struct ipheth_device *dev;	int retval, status; 	dev = urb->context;	if (dev == NULL)		return; 	status = urb->status;	switch (status) {	case -ENOENT:	case -ECONNRESET:	case -ESHUTDOWN:	case -EPROTO:		return;	case 0:		break;	default:		dev_err(&dev->intf->dev, "%s: urb status: %d\n",			__func__, status);		return;	} 	/* iPhone may periodically send URBs with no payload	 * on the "bulk in" endpoint. It is safe to ignore them.	 */	if (urb->actual_length == 0)		goto rx_submit; 	/* RX URBs starting with 0x00 0x01 do not encapsulate Ethernet frames,	 * but rather are control frames. Their purpose is not documented, and	 * they don't affect driver functionality, okay to drop them.	 * There is usually just one 4-byte control frame as the very first	 * URB received from the bulk IN endpoint.	 */	if (unlikely		(urb->actual_length == 4 &&		 ((char *)urb->transfer_buffer)[0] == 0 &&		 ((char *)urb->transfer_buffer)[1] == 1))		goto rx_submit; 	retval = dev->rcvbulk_callback(urb);	if (retval != 0) {		dev_err(&dev->intf->dev, "%s: callback retval: %d\n",			__func__, retval);	} rx_submit:	dev->confirmed_pairing = true;	ipheth_rx_submit(dev, GFP_ATOMIC);} static void ipheth_sndbulk_callback(struct urb *urb){	struct ipheth_device *dev;	int status = urb->status; 	dev = urb->context;	if (dev == NULL)		return; 	if (status != 0 &&	    status != -ENOENT &&	    status != -ECONNRESET &&	    status != -ESHUTDOWN)		dev_err(&dev->intf->dev, "%s: urb status: %d\n",		__func__, status); 	if (status == 0)		netif_wake_queue(dev->net);	else		// on URB error, trigger immediate poll		schedule_delayed_work(&dev->carrier_work, 0);} static int ipheth_carrier_set(struct ipheth_device *dev){	struct usb_device *udev;	int retval; 	if (!dev->confirmed_pairing)		return 0; 	udev = dev->udev;	retval = usb_control_msg(udev,			usb_rcvctrlpipe(udev, IPHETH_CTRL_ENDP),			IPHETH_CMD_CARRIER_CHECK, /* request */			0xc0, /* request type */			0x00, /* value */			0x02, /* index */			dev->ctrl_buf, IPHETH_CTRL_BUF_SIZE,			IPHETH_CTRL_TIMEOUT);	if (retval <= 0) {		dev_err(&dev->intf->dev, "%s: usb_control_msg: %d\n",			__func__, retval);		return retval;	} 	if ((retval == 1 && dev->ctrl_buf[0] == IPHETH_CARRIER_ON) ||	    (retval >= 2 && dev->ctrl_buf[1] == IPHETH_CARRIER_ON)) {		netif_carrier_on(dev->net);		if (dev->tx_urb->status != -EINPROGRESS)			netif_wake_queue(dev->net);	} else {		netif_carrier_off(dev->net);		netif_stop_queue(dev->net);	}	return 0;} static void ipheth_carrier_check_work(struct work_struct *work){	struct ipheth_device *dev = container_of(work, struct ipheth_device,						 carrier_work.work); 	ipheth_carrier_set(dev);	schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT);} static int ipheth_get_macaddr(struct ipheth_device *dev){	struct usb_device *udev = dev->udev;	struct net_device *net = dev->net;	int retval; 	retval = usb_control_msg(udev,				 usb_rcvctrlpipe(udev, IPHETH_CTRL_ENDP),				 IPHETH_CMD_GET_MACADDR, /* request */				 0xc0, /* request type */				 0x00, /* value */				 0x02, /* index */				 dev->ctrl_buf,				 IPHETH_CTRL_BUF_SIZE,				 IPHETH_CTRL_TIMEOUT);	if (retval < 0) {		dev_err(&dev->intf->dev, "%s: usb_control_msg: %d\n",			__func__, retval);	} else if (retval < ETH_ALEN) {		dev_err(&dev->intf->dev,			"%s: usb_control_msg: short packet: %d bytes\n",			__func__, retval);		retval = -EINVAL;	} else {		eth_hw_addr_set(net, dev->ctrl_buf);		retval = 0;	} 	return retval;} static int ipheth_enable_ncm(struct ipheth_device *dev){	struct usb_device *udev = dev->udev;	int retval; 	retval = usb_control_msg(udev,				 usb_sndctrlpipe(udev, IPHETH_CTRL_ENDP),				 IPHETH_CMD_ENABLE_NCM, /* request */				 0x41, /* request type */				 0x00, /* value */				 0x02, /* index */				 NULL,				 0,				 IPHETH_CTRL_TIMEOUT); 	dev_info(&dev->intf->dev, "%s: usb_control_msg: %d\n",		 __func__, retval); 	return retval;} static int ipheth_rx_submit(struct ipheth_device *dev, gfp_t mem_flags){	struct usb_device *udev = dev->udev;	int retval; 	usb_fill_bulk_urb(dev->rx_urb, udev,			  usb_rcvbulkpipe(udev, dev->bulk_in),			  dev->rx_buf, dev->rx_buf_len,			  ipheth_rcvbulk_callback,			  dev);	dev->rx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 	retval = usb_submit_urb(dev->rx_urb, mem_flags);	if (retval)		dev_err(&dev->intf->dev, "%s: usb_submit_urb: %d\n",			__func__, retval);	return retval;} static int ipheth_open(struct net_device *net){	struct ipheth_device *dev = netdev_priv(net);	struct usb_device *udev = dev->udev;	int retval = 0; 	usb_set_interface(udev, IPHETH_INTFNUM, IPHETH_ALT_INTFNUM); 	retval = ipheth_carrier_set(dev);	if (retval)		return retval; 	retval = ipheth_rx_submit(dev, GFP_KERNEL);	if (retval)		return retval; 	enable_delayed_work(&dev->carrier_work);	schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT);	return retval;} static int ipheth_close(struct net_device *net){	struct ipheth_device *dev = netdev_priv(net);

Lines 1–500 of 704.