162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * ipheth.c - Apple iPhone USB Ethernet driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2009 Diego Giagio <diego@giagio.com> 562306a36Sopenharmony_ci * All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 862306a36Sopenharmony_ci * modification, are permitted provided that the following conditions 962306a36Sopenharmony_ci * are met: 1062306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 1162306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 1262306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 1362306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 1462306a36Sopenharmony_ci * documentation and/or other materials provided with the distribution. 1562306a36Sopenharmony_ci * 3. Neither the name of GIAGIO.COM nor the names of its contributors 1662306a36Sopenharmony_ci * may be used to endorse or promote products derived from this software 1762306a36Sopenharmony_ci * without specific prior written permission. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Alternatively, provided that this notice is retained in full, this 2062306a36Sopenharmony_ci * software may be distributed under the terms of the GNU General 2162306a36Sopenharmony_ci * Public License ("GPL") version 2, in which case the provisions of the 2262306a36Sopenharmony_ci * GPL apply INSTEAD OF those given above. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * The provided data structures and external interfaces from this code 2562306a36Sopenharmony_ci * are not restricted to be used by modules with a GPL compatible license. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2862306a36Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2962306a36Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 3062306a36Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 3162306a36Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3262306a36Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3362306a36Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3462306a36Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3562306a36Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3662306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3762306a36Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 3862306a36Sopenharmony_ci * DAMAGE. 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * Attention: iPhone device must be paired, otherwise it won't respond to our 4262306a36Sopenharmony_ci * driver. For more info: http://giagio.com/wiki/moin.cgi/iPhoneEthernetDriver 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#include <linux/kernel.h> 4762306a36Sopenharmony_ci#include <linux/errno.h> 4862306a36Sopenharmony_ci#include <linux/slab.h> 4962306a36Sopenharmony_ci#include <linux/module.h> 5062306a36Sopenharmony_ci#include <linux/netdevice.h> 5162306a36Sopenharmony_ci#include <linux/etherdevice.h> 5262306a36Sopenharmony_ci#include <linux/ethtool.h> 5362306a36Sopenharmony_ci#include <linux/usb.h> 5462306a36Sopenharmony_ci#include <linux/workqueue.h> 5562306a36Sopenharmony_ci#include <linux/usb/cdc.h> 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define USB_VENDOR_APPLE 0x05ac 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define IPHETH_USBINTF_CLASS 255 6062306a36Sopenharmony_ci#define IPHETH_USBINTF_SUBCLASS 253 6162306a36Sopenharmony_ci#define IPHETH_USBINTF_PROTO 1 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define IPHETH_IP_ALIGN 2 /* padding at front of URB */ 6462306a36Sopenharmony_ci#define IPHETH_NCM_HEADER_SIZE (12 + 96) /* NCMH + NCM0 */ 6562306a36Sopenharmony_ci#define IPHETH_TX_BUF_SIZE ETH_FRAME_LEN 6662306a36Sopenharmony_ci#define IPHETH_RX_BUF_SIZE_LEGACY (IPHETH_IP_ALIGN + ETH_FRAME_LEN) 6762306a36Sopenharmony_ci#define IPHETH_RX_BUF_SIZE_NCM 65536 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define IPHETH_TX_TIMEOUT (5 * HZ) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define IPHETH_INTFNUM 2 7262306a36Sopenharmony_ci#define IPHETH_ALT_INTFNUM 1 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define IPHETH_CTRL_ENDP 0x00 7562306a36Sopenharmony_ci#define IPHETH_CTRL_BUF_SIZE 0x40 7662306a36Sopenharmony_ci#define IPHETH_CTRL_TIMEOUT (5 * HZ) 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci#define IPHETH_CMD_GET_MACADDR 0x00 7962306a36Sopenharmony_ci#define IPHETH_CMD_ENABLE_NCM 0x04 8062306a36Sopenharmony_ci#define IPHETH_CMD_CARRIER_CHECK 0x45 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define IPHETH_CARRIER_CHECK_TIMEOUT round_jiffies_relative(1 * HZ) 8362306a36Sopenharmony_ci#define IPHETH_CARRIER_ON 0x04 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic const struct usb_device_id ipheth_table[] = { 8662306a36Sopenharmony_ci { USB_VENDOR_AND_INTERFACE_INFO(USB_VENDOR_APPLE, IPHETH_USBINTF_CLASS, 8762306a36Sopenharmony_ci IPHETH_USBINTF_SUBCLASS, 8862306a36Sopenharmony_ci IPHETH_USBINTF_PROTO) }, 8962306a36Sopenharmony_ci { } 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, ipheth_table); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistruct ipheth_device { 9462306a36Sopenharmony_ci struct usb_device *udev; 9562306a36Sopenharmony_ci struct usb_interface *intf; 9662306a36Sopenharmony_ci struct net_device *net; 9762306a36Sopenharmony_ci struct urb *tx_urb; 9862306a36Sopenharmony_ci struct urb *rx_urb; 9962306a36Sopenharmony_ci unsigned char *tx_buf; 10062306a36Sopenharmony_ci unsigned char *rx_buf; 10162306a36Sopenharmony_ci unsigned char *ctrl_buf; 10262306a36Sopenharmony_ci u8 bulk_in; 10362306a36Sopenharmony_ci u8 bulk_out; 10462306a36Sopenharmony_ci struct delayed_work carrier_work; 10562306a36Sopenharmony_ci bool confirmed_pairing; 10662306a36Sopenharmony_ci int (*rcvbulk_callback)(struct urb *urb); 10762306a36Sopenharmony_ci size_t rx_buf_len; 10862306a36Sopenharmony_ci}; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic int ipheth_rx_submit(struct ipheth_device *dev, gfp_t mem_flags); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic int ipheth_alloc_urbs(struct ipheth_device *iphone) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct urb *tx_urb = NULL; 11562306a36Sopenharmony_ci struct urb *rx_urb = NULL; 11662306a36Sopenharmony_ci u8 *tx_buf = NULL; 11762306a36Sopenharmony_ci u8 *rx_buf = NULL; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci tx_urb = usb_alloc_urb(0, GFP_KERNEL); 12062306a36Sopenharmony_ci if (tx_urb == NULL) 12162306a36Sopenharmony_ci goto error_nomem; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci rx_urb = usb_alloc_urb(0, GFP_KERNEL); 12462306a36Sopenharmony_ci if (rx_urb == NULL) 12562306a36Sopenharmony_ci goto free_tx_urb; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci tx_buf = usb_alloc_coherent(iphone->udev, IPHETH_TX_BUF_SIZE, 12862306a36Sopenharmony_ci GFP_KERNEL, &tx_urb->transfer_dma); 12962306a36Sopenharmony_ci if (tx_buf == NULL) 13062306a36Sopenharmony_ci goto free_rx_urb; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci rx_buf = usb_alloc_coherent(iphone->udev, iphone->rx_buf_len, 13362306a36Sopenharmony_ci GFP_KERNEL, &rx_urb->transfer_dma); 13462306a36Sopenharmony_ci if (rx_buf == NULL) 13562306a36Sopenharmony_ci goto free_tx_buf; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci iphone->tx_urb = tx_urb; 13962306a36Sopenharmony_ci iphone->rx_urb = rx_urb; 14062306a36Sopenharmony_ci iphone->tx_buf = tx_buf; 14162306a36Sopenharmony_ci iphone->rx_buf = rx_buf; 14262306a36Sopenharmony_ci return 0; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cifree_tx_buf: 14562306a36Sopenharmony_ci usb_free_coherent(iphone->udev, IPHETH_TX_BUF_SIZE, tx_buf, 14662306a36Sopenharmony_ci tx_urb->transfer_dma); 14762306a36Sopenharmony_cifree_rx_urb: 14862306a36Sopenharmony_ci usb_free_urb(rx_urb); 14962306a36Sopenharmony_cifree_tx_urb: 15062306a36Sopenharmony_ci usb_free_urb(tx_urb); 15162306a36Sopenharmony_cierror_nomem: 15262306a36Sopenharmony_ci return -ENOMEM; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void ipheth_free_urbs(struct ipheth_device *iphone) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci usb_free_coherent(iphone->udev, iphone->rx_buf_len, iphone->rx_buf, 15862306a36Sopenharmony_ci iphone->rx_urb->transfer_dma); 15962306a36Sopenharmony_ci usb_free_coherent(iphone->udev, IPHETH_TX_BUF_SIZE, iphone->tx_buf, 16062306a36Sopenharmony_ci iphone->tx_urb->transfer_dma); 16162306a36Sopenharmony_ci usb_free_urb(iphone->rx_urb); 16262306a36Sopenharmony_ci usb_free_urb(iphone->tx_urb); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic void ipheth_kill_urbs(struct ipheth_device *dev) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci usb_kill_urb(dev->tx_urb); 16862306a36Sopenharmony_ci usb_kill_urb(dev->rx_urb); 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic int ipheth_consume_skb(char *buf, int len, struct ipheth_device *dev) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct sk_buff *skb; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci skb = dev_alloc_skb(len); 17662306a36Sopenharmony_ci if (!skb) { 17762306a36Sopenharmony_ci dev->net->stats.rx_dropped++; 17862306a36Sopenharmony_ci return -ENOMEM; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci skb_put_data(skb, buf, len); 18262306a36Sopenharmony_ci skb->dev = dev->net; 18362306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev->net); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci dev->net->stats.rx_packets++; 18662306a36Sopenharmony_ci dev->net->stats.rx_bytes += len; 18762306a36Sopenharmony_ci netif_rx(skb); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int ipheth_rcvbulk_callback_legacy(struct urb *urb) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct ipheth_device *dev; 19562306a36Sopenharmony_ci char *buf; 19662306a36Sopenharmony_ci int len; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci dev = urb->context; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (urb->actual_length <= IPHETH_IP_ALIGN) { 20162306a36Sopenharmony_ci dev->net->stats.rx_length_errors++; 20262306a36Sopenharmony_ci return -EINVAL; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci len = urb->actual_length - IPHETH_IP_ALIGN; 20562306a36Sopenharmony_ci buf = urb->transfer_buffer + IPHETH_IP_ALIGN; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return ipheth_consume_skb(buf, len, dev); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int ipheth_rcvbulk_callback_ncm(struct urb *urb) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct usb_cdc_ncm_nth16 *ncmh; 21362306a36Sopenharmony_ci struct usb_cdc_ncm_ndp16 *ncm0; 21462306a36Sopenharmony_ci struct usb_cdc_ncm_dpe16 *dpe; 21562306a36Sopenharmony_ci struct ipheth_device *dev; 21662306a36Sopenharmony_ci int retval = -EINVAL; 21762306a36Sopenharmony_ci char *buf; 21862306a36Sopenharmony_ci int len; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci dev = urb->context; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (urb->actual_length < IPHETH_NCM_HEADER_SIZE) { 22362306a36Sopenharmony_ci dev->net->stats.rx_length_errors++; 22462306a36Sopenharmony_ci return retval; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ncmh = urb->transfer_buffer; 22862306a36Sopenharmony_ci if (ncmh->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH16_SIGN) || 22962306a36Sopenharmony_ci le16_to_cpu(ncmh->wNdpIndex) >= urb->actual_length) { 23062306a36Sopenharmony_ci dev->net->stats.rx_errors++; 23162306a36Sopenharmony_ci return retval; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci ncm0 = urb->transfer_buffer + le16_to_cpu(ncmh->wNdpIndex); 23562306a36Sopenharmony_ci if (ncm0->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN) || 23662306a36Sopenharmony_ci le16_to_cpu(ncmh->wHeaderLength) + le16_to_cpu(ncm0->wLength) >= 23762306a36Sopenharmony_ci urb->actual_length) { 23862306a36Sopenharmony_ci dev->net->stats.rx_errors++; 23962306a36Sopenharmony_ci return retval; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci dpe = ncm0->dpe16; 24362306a36Sopenharmony_ci while (le16_to_cpu(dpe->wDatagramIndex) != 0 && 24462306a36Sopenharmony_ci le16_to_cpu(dpe->wDatagramLength) != 0) { 24562306a36Sopenharmony_ci if (le16_to_cpu(dpe->wDatagramIndex) >= urb->actual_length || 24662306a36Sopenharmony_ci le16_to_cpu(dpe->wDatagramIndex) + 24762306a36Sopenharmony_ci le16_to_cpu(dpe->wDatagramLength) > urb->actual_length) { 24862306a36Sopenharmony_ci dev->net->stats.rx_length_errors++; 24962306a36Sopenharmony_ci return retval; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci buf = urb->transfer_buffer + le16_to_cpu(dpe->wDatagramIndex); 25362306a36Sopenharmony_ci len = le16_to_cpu(dpe->wDatagramLength); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci retval = ipheth_consume_skb(buf, len, dev); 25662306a36Sopenharmony_ci if (retval != 0) 25762306a36Sopenharmony_ci return retval; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci dpe++; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic void ipheth_rcvbulk_callback(struct urb *urb) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct ipheth_device *dev; 26862306a36Sopenharmony_ci int retval, status; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci dev = urb->context; 27162306a36Sopenharmony_ci if (dev == NULL) 27262306a36Sopenharmony_ci return; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci status = urb->status; 27562306a36Sopenharmony_ci switch (status) { 27662306a36Sopenharmony_ci case -ENOENT: 27762306a36Sopenharmony_ci case -ECONNRESET: 27862306a36Sopenharmony_ci case -ESHUTDOWN: 27962306a36Sopenharmony_ci case -EPROTO: 28062306a36Sopenharmony_ci return; 28162306a36Sopenharmony_ci case 0: 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci default: 28462306a36Sopenharmony_ci dev_err(&dev->intf->dev, "%s: urb status: %d\n", 28562306a36Sopenharmony_ci __func__, status); 28662306a36Sopenharmony_ci return; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (urb->actual_length <= IPHETH_IP_ALIGN) { 29062306a36Sopenharmony_ci dev->net->stats.rx_length_errors++; 29162306a36Sopenharmony_ci return; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* RX URBs starting with 0x00 0x01 do not encapsulate Ethernet frames, 29562306a36Sopenharmony_ci * but rather are control frames. Their purpose is not documented, and 29662306a36Sopenharmony_ci * they don't affect driver functionality, okay to drop them. 29762306a36Sopenharmony_ci * There is usually just one 4-byte control frame as the very first 29862306a36Sopenharmony_ci * URB received from the bulk IN endpoint. 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_ci if (unlikely 30162306a36Sopenharmony_ci (((char *)urb->transfer_buffer)[0] == 0 && 30262306a36Sopenharmony_ci ((char *)urb->transfer_buffer)[1] == 1)) 30362306a36Sopenharmony_ci goto rx_submit; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci retval = dev->rcvbulk_callback(urb); 30662306a36Sopenharmony_ci if (retval != 0) { 30762306a36Sopenharmony_ci dev_err(&dev->intf->dev, "%s: callback retval: %d\n", 30862306a36Sopenharmony_ci __func__, retval); 30962306a36Sopenharmony_ci return; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cirx_submit: 31362306a36Sopenharmony_ci dev->confirmed_pairing = true; 31462306a36Sopenharmony_ci ipheth_rx_submit(dev, GFP_ATOMIC); 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic void ipheth_sndbulk_callback(struct urb *urb) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci struct ipheth_device *dev; 32062306a36Sopenharmony_ci int status = urb->status; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci dev = urb->context; 32362306a36Sopenharmony_ci if (dev == NULL) 32462306a36Sopenharmony_ci return; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (status != 0 && 32762306a36Sopenharmony_ci status != -ENOENT && 32862306a36Sopenharmony_ci status != -ECONNRESET && 32962306a36Sopenharmony_ci status != -ESHUTDOWN) 33062306a36Sopenharmony_ci dev_err(&dev->intf->dev, "%s: urb status: %d\n", 33162306a36Sopenharmony_ci __func__, status); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (status == 0) 33462306a36Sopenharmony_ci netif_wake_queue(dev->net); 33562306a36Sopenharmony_ci else 33662306a36Sopenharmony_ci // on URB error, trigger immediate poll 33762306a36Sopenharmony_ci schedule_delayed_work(&dev->carrier_work, 0); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic int ipheth_carrier_set(struct ipheth_device *dev) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct usb_device *udev; 34362306a36Sopenharmony_ci int retval; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (!dev->confirmed_pairing) 34662306a36Sopenharmony_ci return 0; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci udev = dev->udev; 34962306a36Sopenharmony_ci retval = usb_control_msg(udev, 35062306a36Sopenharmony_ci usb_rcvctrlpipe(udev, IPHETH_CTRL_ENDP), 35162306a36Sopenharmony_ci IPHETH_CMD_CARRIER_CHECK, /* request */ 35262306a36Sopenharmony_ci 0xc0, /* request type */ 35362306a36Sopenharmony_ci 0x00, /* value */ 35462306a36Sopenharmony_ci 0x02, /* index */ 35562306a36Sopenharmony_ci dev->ctrl_buf, IPHETH_CTRL_BUF_SIZE, 35662306a36Sopenharmony_ci IPHETH_CTRL_TIMEOUT); 35762306a36Sopenharmony_ci if (retval < 0) { 35862306a36Sopenharmony_ci dev_err(&dev->intf->dev, "%s: usb_control_msg: %d\n", 35962306a36Sopenharmony_ci __func__, retval); 36062306a36Sopenharmony_ci return retval; 36162306a36Sopenharmony_ci } 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (dev->ctrl_buf[0] == IPHETH_CARRIER_ON) { 36462306a36Sopenharmony_ci netif_carrier_on(dev->net); 36562306a36Sopenharmony_ci if (dev->tx_urb->status != -EINPROGRESS) 36662306a36Sopenharmony_ci netif_wake_queue(dev->net); 36762306a36Sopenharmony_ci } else { 36862306a36Sopenharmony_ci netif_carrier_off(dev->net); 36962306a36Sopenharmony_ci netif_stop_queue(dev->net); 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci return 0; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic void ipheth_carrier_check_work(struct work_struct *work) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct ipheth_device *dev = container_of(work, struct ipheth_device, 37762306a36Sopenharmony_ci carrier_work.work); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci ipheth_carrier_set(dev); 38062306a36Sopenharmony_ci schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT); 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int ipheth_get_macaddr(struct ipheth_device *dev) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci struct usb_device *udev = dev->udev; 38662306a36Sopenharmony_ci struct net_device *net = dev->net; 38762306a36Sopenharmony_ci int retval; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci retval = usb_control_msg(udev, 39062306a36Sopenharmony_ci usb_rcvctrlpipe(udev, IPHETH_CTRL_ENDP), 39162306a36Sopenharmony_ci IPHETH_CMD_GET_MACADDR, /* request */ 39262306a36Sopenharmony_ci 0xc0, /* request type */ 39362306a36Sopenharmony_ci 0x00, /* value */ 39462306a36Sopenharmony_ci 0x02, /* index */ 39562306a36Sopenharmony_ci dev->ctrl_buf, 39662306a36Sopenharmony_ci IPHETH_CTRL_BUF_SIZE, 39762306a36Sopenharmony_ci IPHETH_CTRL_TIMEOUT); 39862306a36Sopenharmony_ci if (retval < 0) { 39962306a36Sopenharmony_ci dev_err(&dev->intf->dev, "%s: usb_control_msg: %d\n", 40062306a36Sopenharmony_ci __func__, retval); 40162306a36Sopenharmony_ci } else if (retval < ETH_ALEN) { 40262306a36Sopenharmony_ci dev_err(&dev->intf->dev, 40362306a36Sopenharmony_ci "%s: usb_control_msg: short packet: %d bytes\n", 40462306a36Sopenharmony_ci __func__, retval); 40562306a36Sopenharmony_ci retval = -EINVAL; 40662306a36Sopenharmony_ci } else { 40762306a36Sopenharmony_ci eth_hw_addr_set(net, dev->ctrl_buf); 40862306a36Sopenharmony_ci retval = 0; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return retval; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic int ipheth_enable_ncm(struct ipheth_device *dev) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci struct usb_device *udev = dev->udev; 41762306a36Sopenharmony_ci int retval; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci retval = usb_control_msg(udev, 42062306a36Sopenharmony_ci usb_sndctrlpipe(udev, IPHETH_CTRL_ENDP), 42162306a36Sopenharmony_ci IPHETH_CMD_ENABLE_NCM, /* request */ 42262306a36Sopenharmony_ci 0x41, /* request type */ 42362306a36Sopenharmony_ci 0x00, /* value */ 42462306a36Sopenharmony_ci 0x02, /* index */ 42562306a36Sopenharmony_ci NULL, 42662306a36Sopenharmony_ci 0, 42762306a36Sopenharmony_ci IPHETH_CTRL_TIMEOUT); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci dev_info(&dev->intf->dev, "%s: usb_control_msg: %d\n", 43062306a36Sopenharmony_ci __func__, retval); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return retval; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int ipheth_rx_submit(struct ipheth_device *dev, gfp_t mem_flags) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct usb_device *udev = dev->udev; 43862306a36Sopenharmony_ci int retval; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci usb_fill_bulk_urb(dev->rx_urb, udev, 44162306a36Sopenharmony_ci usb_rcvbulkpipe(udev, dev->bulk_in), 44262306a36Sopenharmony_ci dev->rx_buf, dev->rx_buf_len, 44362306a36Sopenharmony_ci ipheth_rcvbulk_callback, 44462306a36Sopenharmony_ci dev); 44562306a36Sopenharmony_ci dev->rx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci retval = usb_submit_urb(dev->rx_urb, mem_flags); 44862306a36Sopenharmony_ci if (retval) 44962306a36Sopenharmony_ci dev_err(&dev->intf->dev, "%s: usb_submit_urb: %d\n", 45062306a36Sopenharmony_ci __func__, retval); 45162306a36Sopenharmony_ci return retval; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic int ipheth_open(struct net_device *net) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct ipheth_device *dev = netdev_priv(net); 45762306a36Sopenharmony_ci struct usb_device *udev = dev->udev; 45862306a36Sopenharmony_ci int retval = 0; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci usb_set_interface(udev, IPHETH_INTFNUM, IPHETH_ALT_INTFNUM); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci retval = ipheth_carrier_set(dev); 46362306a36Sopenharmony_ci if (retval) 46462306a36Sopenharmony_ci return retval; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci retval = ipheth_rx_submit(dev, GFP_KERNEL); 46762306a36Sopenharmony_ci if (retval) 46862306a36Sopenharmony_ci return retval; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT); 47162306a36Sopenharmony_ci return retval; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic int ipheth_close(struct net_device *net) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct ipheth_device *dev = netdev_priv(net); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci cancel_delayed_work_sync(&dev->carrier_work); 47962306a36Sopenharmony_ci netif_stop_queue(net); 48062306a36Sopenharmony_ci return 0; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic netdev_tx_t ipheth_tx(struct sk_buff *skb, struct net_device *net) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci struct ipheth_device *dev = netdev_priv(net); 48662306a36Sopenharmony_ci struct usb_device *udev = dev->udev; 48762306a36Sopenharmony_ci int retval; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* Paranoid */ 49062306a36Sopenharmony_ci if (skb->len > IPHETH_TX_BUF_SIZE) { 49162306a36Sopenharmony_ci WARN(1, "%s: skb too large: %d bytes\n", __func__, skb->len); 49262306a36Sopenharmony_ci dev->net->stats.tx_dropped++; 49362306a36Sopenharmony_ci dev_kfree_skb_any(skb); 49462306a36Sopenharmony_ci return NETDEV_TX_OK; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci memcpy(dev->tx_buf, skb->data, skb->len); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci usb_fill_bulk_urb(dev->tx_urb, udev, 50062306a36Sopenharmony_ci usb_sndbulkpipe(udev, dev->bulk_out), 50162306a36Sopenharmony_ci dev->tx_buf, skb->len, 50262306a36Sopenharmony_ci ipheth_sndbulk_callback, 50362306a36Sopenharmony_ci dev); 50462306a36Sopenharmony_ci dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci netif_stop_queue(net); 50762306a36Sopenharmony_ci retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC); 50862306a36Sopenharmony_ci if (retval) { 50962306a36Sopenharmony_ci dev_err(&dev->intf->dev, "%s: usb_submit_urb: %d\n", 51062306a36Sopenharmony_ci __func__, retval); 51162306a36Sopenharmony_ci dev->net->stats.tx_errors++; 51262306a36Sopenharmony_ci dev_kfree_skb_any(skb); 51362306a36Sopenharmony_ci netif_wake_queue(net); 51462306a36Sopenharmony_ci } else { 51562306a36Sopenharmony_ci dev->net->stats.tx_packets++; 51662306a36Sopenharmony_ci dev->net->stats.tx_bytes += skb->len; 51762306a36Sopenharmony_ci dev_consume_skb_any(skb); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci return NETDEV_TX_OK; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic void ipheth_tx_timeout(struct net_device *net, unsigned int txqueue) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct ipheth_device *dev = netdev_priv(net); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci dev_err(&dev->intf->dev, "%s: TX timeout\n", __func__); 52862306a36Sopenharmony_ci dev->net->stats.tx_errors++; 52962306a36Sopenharmony_ci usb_unlink_urb(dev->tx_urb); 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic u32 ipheth_ethtool_op_get_link(struct net_device *net) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct ipheth_device *dev = netdev_priv(net); 53562306a36Sopenharmony_ci return netif_carrier_ok(dev->net); 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic const struct ethtool_ops ops = { 53962306a36Sopenharmony_ci .get_link = ipheth_ethtool_op_get_link 54062306a36Sopenharmony_ci}; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic const struct net_device_ops ipheth_netdev_ops = { 54362306a36Sopenharmony_ci .ndo_open = ipheth_open, 54462306a36Sopenharmony_ci .ndo_stop = ipheth_close, 54562306a36Sopenharmony_ci .ndo_start_xmit = ipheth_tx, 54662306a36Sopenharmony_ci .ndo_tx_timeout = ipheth_tx_timeout, 54762306a36Sopenharmony_ci}; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic int ipheth_probe(struct usb_interface *intf, 55062306a36Sopenharmony_ci const struct usb_device_id *id) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 55362306a36Sopenharmony_ci struct usb_host_interface *hintf; 55462306a36Sopenharmony_ci struct usb_endpoint_descriptor *endp; 55562306a36Sopenharmony_ci struct ipheth_device *dev; 55662306a36Sopenharmony_ci struct net_device *netdev; 55762306a36Sopenharmony_ci int i; 55862306a36Sopenharmony_ci int retval; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci netdev = alloc_etherdev(sizeof(struct ipheth_device)); 56162306a36Sopenharmony_ci if (!netdev) 56262306a36Sopenharmony_ci return -ENOMEM; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci netdev->netdev_ops = &ipheth_netdev_ops; 56562306a36Sopenharmony_ci netdev->watchdog_timeo = IPHETH_TX_TIMEOUT; 56662306a36Sopenharmony_ci strscpy(netdev->name, "eth%d", sizeof(netdev->name)); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci dev = netdev_priv(netdev); 56962306a36Sopenharmony_ci dev->udev = udev; 57062306a36Sopenharmony_ci dev->net = netdev; 57162306a36Sopenharmony_ci dev->intf = intf; 57262306a36Sopenharmony_ci dev->confirmed_pairing = false; 57362306a36Sopenharmony_ci dev->rx_buf_len = IPHETH_RX_BUF_SIZE_LEGACY; 57462306a36Sopenharmony_ci dev->rcvbulk_callback = ipheth_rcvbulk_callback_legacy; 57562306a36Sopenharmony_ci /* Set up endpoints */ 57662306a36Sopenharmony_ci hintf = usb_altnum_to_altsetting(intf, IPHETH_ALT_INTFNUM); 57762306a36Sopenharmony_ci if (hintf == NULL) { 57862306a36Sopenharmony_ci retval = -ENODEV; 57962306a36Sopenharmony_ci dev_err(&intf->dev, "Unable to find alternate settings interface\n"); 58062306a36Sopenharmony_ci goto err_endpoints; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci for (i = 0; i < hintf->desc.bNumEndpoints; i++) { 58462306a36Sopenharmony_ci endp = &hintf->endpoint[i].desc; 58562306a36Sopenharmony_ci if (usb_endpoint_is_bulk_in(endp)) 58662306a36Sopenharmony_ci dev->bulk_in = endp->bEndpointAddress; 58762306a36Sopenharmony_ci else if (usb_endpoint_is_bulk_out(endp)) 58862306a36Sopenharmony_ci dev->bulk_out = endp->bEndpointAddress; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci if (!(dev->bulk_in && dev->bulk_out)) { 59162306a36Sopenharmony_ci retval = -ENODEV; 59262306a36Sopenharmony_ci dev_err(&intf->dev, "Unable to find endpoints\n"); 59362306a36Sopenharmony_ci goto err_endpoints; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci dev->ctrl_buf = kmalloc(IPHETH_CTRL_BUF_SIZE, GFP_KERNEL); 59762306a36Sopenharmony_ci if (dev->ctrl_buf == NULL) { 59862306a36Sopenharmony_ci retval = -ENOMEM; 59962306a36Sopenharmony_ci goto err_alloc_ctrl_buf; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci retval = ipheth_get_macaddr(dev); 60362306a36Sopenharmony_ci if (retval) 60462306a36Sopenharmony_ci goto err_get_macaddr; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci retval = ipheth_enable_ncm(dev); 60762306a36Sopenharmony_ci if (!retval) { 60862306a36Sopenharmony_ci dev->rx_buf_len = IPHETH_RX_BUF_SIZE_NCM; 60962306a36Sopenharmony_ci dev->rcvbulk_callback = ipheth_rcvbulk_callback_ncm; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci INIT_DELAYED_WORK(&dev->carrier_work, ipheth_carrier_check_work); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci retval = ipheth_alloc_urbs(dev); 61562306a36Sopenharmony_ci if (retval) { 61662306a36Sopenharmony_ci dev_err(&intf->dev, "error allocating urbs: %d\n", retval); 61762306a36Sopenharmony_ci goto err_alloc_urbs; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci usb_set_intfdata(intf, dev); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci SET_NETDEV_DEV(netdev, &intf->dev); 62362306a36Sopenharmony_ci netdev->ethtool_ops = &ops; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci retval = register_netdev(netdev); 62662306a36Sopenharmony_ci if (retval) { 62762306a36Sopenharmony_ci dev_err(&intf->dev, "error registering netdev: %d\n", retval); 62862306a36Sopenharmony_ci retval = -EIO; 62962306a36Sopenharmony_ci goto err_register_netdev; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci // carrier down and transmit queues stopped until packet from device 63262306a36Sopenharmony_ci netif_carrier_off(netdev); 63362306a36Sopenharmony_ci netif_tx_stop_all_queues(netdev); 63462306a36Sopenharmony_ci dev_info(&intf->dev, "Apple iPhone USB Ethernet device attached\n"); 63562306a36Sopenharmony_ci return 0; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cierr_register_netdev: 63862306a36Sopenharmony_ci ipheth_free_urbs(dev); 63962306a36Sopenharmony_cierr_alloc_urbs: 64062306a36Sopenharmony_cierr_get_macaddr: 64162306a36Sopenharmony_ci kfree(dev->ctrl_buf); 64262306a36Sopenharmony_cierr_alloc_ctrl_buf: 64362306a36Sopenharmony_cierr_endpoints: 64462306a36Sopenharmony_ci free_netdev(netdev); 64562306a36Sopenharmony_ci return retval; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic void ipheth_disconnect(struct usb_interface *intf) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci struct ipheth_device *dev; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci dev = usb_get_intfdata(intf); 65362306a36Sopenharmony_ci if (dev != NULL) { 65462306a36Sopenharmony_ci unregister_netdev(dev->net); 65562306a36Sopenharmony_ci ipheth_kill_urbs(dev); 65662306a36Sopenharmony_ci ipheth_free_urbs(dev); 65762306a36Sopenharmony_ci kfree(dev->ctrl_buf); 65862306a36Sopenharmony_ci free_netdev(dev->net); 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci usb_set_intfdata(intf, NULL); 66162306a36Sopenharmony_ci dev_info(&intf->dev, "Apple iPhone USB Ethernet now disconnected\n"); 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_cistatic struct usb_driver ipheth_driver = { 66562306a36Sopenharmony_ci .name = "ipheth", 66662306a36Sopenharmony_ci .probe = ipheth_probe, 66762306a36Sopenharmony_ci .disconnect = ipheth_disconnect, 66862306a36Sopenharmony_ci .id_table = ipheth_table, 66962306a36Sopenharmony_ci .disable_hub_initiated_lpm = 1, 67062306a36Sopenharmony_ci}; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cimodule_usb_driver(ipheth_driver); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ciMODULE_AUTHOR("Diego Giagio <diego@giagio.com>"); 67562306a36Sopenharmony_ciMODULE_DESCRIPTION("Apple iPhone USB Ethernet driver"); 67662306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 677