18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * ipheth.c - Apple iPhone USB Ethernet driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2009 Diego Giagio <diego@giagio.com> 58c2ecf20Sopenharmony_ci * All rights reserved. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 88c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 98c2ecf20Sopenharmony_ci * are met: 108c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 118c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 128c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 138c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 148c2ecf20Sopenharmony_ci * documentation and/or other materials provided with the distribution. 158c2ecf20Sopenharmony_ci * 3. Neither the name of GIAGIO.COM nor the names of its contributors 168c2ecf20Sopenharmony_ci * may be used to endorse or promote products derived from this software 178c2ecf20Sopenharmony_ci * without specific prior written permission. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Alternatively, provided that this notice is retained in full, this 208c2ecf20Sopenharmony_ci * software may be distributed under the terms of the GNU General 218c2ecf20Sopenharmony_ci * Public License ("GPL") version 2, in which case the provisions of the 228c2ecf20Sopenharmony_ci * GPL apply INSTEAD OF those given above. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * The provided data structures and external interfaces from this code 258c2ecf20Sopenharmony_ci * are not restricted to be used by modules with a GPL compatible license. 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 288c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 298c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 308c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 318c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 328c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 338c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 348c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 358c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 368c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 378c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 388c2ecf20Sopenharmony_ci * DAMAGE. 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * Attention: iPhone device must be paired, otherwise it won't respond to our 428c2ecf20Sopenharmony_ci * driver. For more info: http://giagio.com/wiki/moin.cgi/iPhoneEthernetDriver 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#include <linux/kernel.h> 478c2ecf20Sopenharmony_ci#include <linux/errno.h> 488c2ecf20Sopenharmony_ci#include <linux/slab.h> 498c2ecf20Sopenharmony_ci#include <linux/module.h> 508c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 518c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 528c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 538c2ecf20Sopenharmony_ci#include <linux/usb.h> 548c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define USB_VENDOR_APPLE 0x05ac 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define IPHETH_USBINTF_CLASS 255 598c2ecf20Sopenharmony_ci#define IPHETH_USBINTF_SUBCLASS 253 608c2ecf20Sopenharmony_ci#define IPHETH_USBINTF_PROTO 1 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define IPHETH_BUF_SIZE 1514 638c2ecf20Sopenharmony_ci#define IPHETH_IP_ALIGN 2 /* padding at front of URB */ 648c2ecf20Sopenharmony_ci#define IPHETH_TX_TIMEOUT (5 * HZ) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define IPHETH_INTFNUM 2 678c2ecf20Sopenharmony_ci#define IPHETH_ALT_INTFNUM 1 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define IPHETH_CTRL_ENDP 0x00 708c2ecf20Sopenharmony_ci#define IPHETH_CTRL_BUF_SIZE 0x40 718c2ecf20Sopenharmony_ci#define IPHETH_CTRL_TIMEOUT (5 * HZ) 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define IPHETH_CMD_GET_MACADDR 0x00 748c2ecf20Sopenharmony_ci#define IPHETH_CMD_CARRIER_CHECK 0x45 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define IPHETH_CARRIER_CHECK_TIMEOUT round_jiffies_relative(1 * HZ) 778c2ecf20Sopenharmony_ci#define IPHETH_CARRIER_ON 0x04 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic const struct usb_device_id ipheth_table[] = { 808c2ecf20Sopenharmony_ci { USB_VENDOR_AND_INTERFACE_INFO(USB_VENDOR_APPLE, IPHETH_USBINTF_CLASS, 818c2ecf20Sopenharmony_ci IPHETH_USBINTF_SUBCLASS, 828c2ecf20Sopenharmony_ci IPHETH_USBINTF_PROTO) }, 838c2ecf20Sopenharmony_ci { } 848c2ecf20Sopenharmony_ci}; 858c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, ipheth_table); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistruct ipheth_device { 888c2ecf20Sopenharmony_ci struct usb_device *udev; 898c2ecf20Sopenharmony_ci struct usb_interface *intf; 908c2ecf20Sopenharmony_ci struct net_device *net; 918c2ecf20Sopenharmony_ci struct urb *tx_urb; 928c2ecf20Sopenharmony_ci struct urb *rx_urb; 938c2ecf20Sopenharmony_ci unsigned char *tx_buf; 948c2ecf20Sopenharmony_ci unsigned char *rx_buf; 958c2ecf20Sopenharmony_ci unsigned char *ctrl_buf; 968c2ecf20Sopenharmony_ci u8 bulk_in; 978c2ecf20Sopenharmony_ci u8 bulk_out; 988c2ecf20Sopenharmony_ci struct delayed_work carrier_work; 998c2ecf20Sopenharmony_ci bool confirmed_pairing; 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic int ipheth_rx_submit(struct ipheth_device *dev, gfp_t mem_flags); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int ipheth_alloc_urbs(struct ipheth_device *iphone) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct urb *tx_urb = NULL; 1078c2ecf20Sopenharmony_ci struct urb *rx_urb = NULL; 1088c2ecf20Sopenharmony_ci u8 *tx_buf = NULL; 1098c2ecf20Sopenharmony_ci u8 *rx_buf = NULL; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci tx_urb = usb_alloc_urb(0, GFP_KERNEL); 1128c2ecf20Sopenharmony_ci if (tx_urb == NULL) 1138c2ecf20Sopenharmony_ci goto error_nomem; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci rx_urb = usb_alloc_urb(0, GFP_KERNEL); 1168c2ecf20Sopenharmony_ci if (rx_urb == NULL) 1178c2ecf20Sopenharmony_ci goto free_tx_urb; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci tx_buf = usb_alloc_coherent(iphone->udev, IPHETH_BUF_SIZE, 1208c2ecf20Sopenharmony_ci GFP_KERNEL, &tx_urb->transfer_dma); 1218c2ecf20Sopenharmony_ci if (tx_buf == NULL) 1228c2ecf20Sopenharmony_ci goto free_rx_urb; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci rx_buf = usb_alloc_coherent(iphone->udev, IPHETH_BUF_SIZE + IPHETH_IP_ALIGN, 1258c2ecf20Sopenharmony_ci GFP_KERNEL, &rx_urb->transfer_dma); 1268c2ecf20Sopenharmony_ci if (rx_buf == NULL) 1278c2ecf20Sopenharmony_ci goto free_tx_buf; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci iphone->tx_urb = tx_urb; 1318c2ecf20Sopenharmony_ci iphone->rx_urb = rx_urb; 1328c2ecf20Sopenharmony_ci iphone->tx_buf = tx_buf; 1338c2ecf20Sopenharmony_ci iphone->rx_buf = rx_buf; 1348c2ecf20Sopenharmony_ci return 0; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cifree_tx_buf: 1378c2ecf20Sopenharmony_ci usb_free_coherent(iphone->udev, IPHETH_BUF_SIZE, tx_buf, 1388c2ecf20Sopenharmony_ci tx_urb->transfer_dma); 1398c2ecf20Sopenharmony_cifree_rx_urb: 1408c2ecf20Sopenharmony_ci usb_free_urb(rx_urb); 1418c2ecf20Sopenharmony_cifree_tx_urb: 1428c2ecf20Sopenharmony_ci usb_free_urb(tx_urb); 1438c2ecf20Sopenharmony_cierror_nomem: 1448c2ecf20Sopenharmony_ci return -ENOMEM; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic void ipheth_free_urbs(struct ipheth_device *iphone) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci usb_free_coherent(iphone->udev, IPHETH_BUF_SIZE + IPHETH_IP_ALIGN, iphone->rx_buf, 1508c2ecf20Sopenharmony_ci iphone->rx_urb->transfer_dma); 1518c2ecf20Sopenharmony_ci usb_free_coherent(iphone->udev, IPHETH_BUF_SIZE, iphone->tx_buf, 1528c2ecf20Sopenharmony_ci iphone->tx_urb->transfer_dma); 1538c2ecf20Sopenharmony_ci usb_free_urb(iphone->rx_urb); 1548c2ecf20Sopenharmony_ci usb_free_urb(iphone->tx_urb); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic void ipheth_kill_urbs(struct ipheth_device *dev) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci usb_kill_urb(dev->tx_urb); 1608c2ecf20Sopenharmony_ci usb_kill_urb(dev->rx_urb); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic void ipheth_rcvbulk_callback(struct urb *urb) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct ipheth_device *dev; 1668c2ecf20Sopenharmony_ci struct sk_buff *skb; 1678c2ecf20Sopenharmony_ci int status; 1688c2ecf20Sopenharmony_ci char *buf; 1698c2ecf20Sopenharmony_ci int len; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci dev = urb->context; 1728c2ecf20Sopenharmony_ci if (dev == NULL) 1738c2ecf20Sopenharmony_ci return; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci status = urb->status; 1768c2ecf20Sopenharmony_ci switch (status) { 1778c2ecf20Sopenharmony_ci case -ENOENT: 1788c2ecf20Sopenharmony_ci case -ECONNRESET: 1798c2ecf20Sopenharmony_ci case -ESHUTDOWN: 1808c2ecf20Sopenharmony_ci case -EPROTO: 1818c2ecf20Sopenharmony_ci return; 1828c2ecf20Sopenharmony_ci case 0: 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci default: 1858c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, "%s: urb status: %d\n", 1868c2ecf20Sopenharmony_ci __func__, status); 1878c2ecf20Sopenharmony_ci return; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (urb->actual_length <= IPHETH_IP_ALIGN) { 1918c2ecf20Sopenharmony_ci dev->net->stats.rx_length_errors++; 1928c2ecf20Sopenharmony_ci return; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci len = urb->actual_length - IPHETH_IP_ALIGN; 1958c2ecf20Sopenharmony_ci buf = urb->transfer_buffer + IPHETH_IP_ALIGN; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci skb = dev_alloc_skb(len); 1988c2ecf20Sopenharmony_ci if (!skb) { 1998c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, "%s: dev_alloc_skb: -ENOMEM\n", 2008c2ecf20Sopenharmony_ci __func__); 2018c2ecf20Sopenharmony_ci dev->net->stats.rx_dropped++; 2028c2ecf20Sopenharmony_ci return; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci skb_put_data(skb, buf, len); 2068c2ecf20Sopenharmony_ci skb->dev = dev->net; 2078c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev->net); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci dev->net->stats.rx_packets++; 2108c2ecf20Sopenharmony_ci dev->net->stats.rx_bytes += len; 2118c2ecf20Sopenharmony_ci dev->confirmed_pairing = true; 2128c2ecf20Sopenharmony_ci netif_rx(skb); 2138c2ecf20Sopenharmony_ci ipheth_rx_submit(dev, GFP_ATOMIC); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic void ipheth_sndbulk_callback(struct urb *urb) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct ipheth_device *dev; 2198c2ecf20Sopenharmony_ci int status = urb->status; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci dev = urb->context; 2228c2ecf20Sopenharmony_ci if (dev == NULL) 2238c2ecf20Sopenharmony_ci return; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (status != 0 && 2268c2ecf20Sopenharmony_ci status != -ENOENT && 2278c2ecf20Sopenharmony_ci status != -ECONNRESET && 2288c2ecf20Sopenharmony_ci status != -ESHUTDOWN) 2298c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, "%s: urb status: %d\n", 2308c2ecf20Sopenharmony_ci __func__, status); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (status == 0) 2338c2ecf20Sopenharmony_ci netif_wake_queue(dev->net); 2348c2ecf20Sopenharmony_ci else 2358c2ecf20Sopenharmony_ci // on URB error, trigger immediate poll 2368c2ecf20Sopenharmony_ci schedule_delayed_work(&dev->carrier_work, 0); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic int ipheth_carrier_set(struct ipheth_device *dev) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct usb_device *udev; 2428c2ecf20Sopenharmony_ci int retval; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (!dev->confirmed_pairing) 2458c2ecf20Sopenharmony_ci return 0; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci udev = dev->udev; 2488c2ecf20Sopenharmony_ci retval = usb_control_msg(udev, 2498c2ecf20Sopenharmony_ci usb_rcvctrlpipe(udev, IPHETH_CTRL_ENDP), 2508c2ecf20Sopenharmony_ci IPHETH_CMD_CARRIER_CHECK, /* request */ 2518c2ecf20Sopenharmony_ci 0xc0, /* request type */ 2528c2ecf20Sopenharmony_ci 0x00, /* value */ 2538c2ecf20Sopenharmony_ci 0x02, /* index */ 2548c2ecf20Sopenharmony_ci dev->ctrl_buf, IPHETH_CTRL_BUF_SIZE, 2558c2ecf20Sopenharmony_ci IPHETH_CTRL_TIMEOUT); 2568c2ecf20Sopenharmony_ci if (retval < 0) { 2578c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, "%s: usb_control_msg: %d\n", 2588c2ecf20Sopenharmony_ci __func__, retval); 2598c2ecf20Sopenharmony_ci return retval; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci if (dev->ctrl_buf[0] == IPHETH_CARRIER_ON) { 2638c2ecf20Sopenharmony_ci netif_carrier_on(dev->net); 2648c2ecf20Sopenharmony_ci if (dev->tx_urb->status != -EINPROGRESS) 2658c2ecf20Sopenharmony_ci netif_wake_queue(dev->net); 2668c2ecf20Sopenharmony_ci } else { 2678c2ecf20Sopenharmony_ci netif_carrier_off(dev->net); 2688c2ecf20Sopenharmony_ci netif_stop_queue(dev->net); 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic void ipheth_carrier_check_work(struct work_struct *work) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct ipheth_device *dev = container_of(work, struct ipheth_device, 2768c2ecf20Sopenharmony_ci carrier_work.work); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci ipheth_carrier_set(dev); 2798c2ecf20Sopenharmony_ci schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic int ipheth_get_macaddr(struct ipheth_device *dev) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct usb_device *udev = dev->udev; 2858c2ecf20Sopenharmony_ci struct net_device *net = dev->net; 2868c2ecf20Sopenharmony_ci int retval; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci retval = usb_control_msg(udev, 2898c2ecf20Sopenharmony_ci usb_rcvctrlpipe(udev, IPHETH_CTRL_ENDP), 2908c2ecf20Sopenharmony_ci IPHETH_CMD_GET_MACADDR, /* request */ 2918c2ecf20Sopenharmony_ci 0xc0, /* request type */ 2928c2ecf20Sopenharmony_ci 0x00, /* value */ 2938c2ecf20Sopenharmony_ci 0x02, /* index */ 2948c2ecf20Sopenharmony_ci dev->ctrl_buf, 2958c2ecf20Sopenharmony_ci IPHETH_CTRL_BUF_SIZE, 2968c2ecf20Sopenharmony_ci IPHETH_CTRL_TIMEOUT); 2978c2ecf20Sopenharmony_ci if (retval < 0) { 2988c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, "%s: usb_control_msg: %d\n", 2998c2ecf20Sopenharmony_ci __func__, retval); 3008c2ecf20Sopenharmony_ci } else if (retval < ETH_ALEN) { 3018c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, 3028c2ecf20Sopenharmony_ci "%s: usb_control_msg: short packet: %d bytes\n", 3038c2ecf20Sopenharmony_ci __func__, retval); 3048c2ecf20Sopenharmony_ci retval = -EINVAL; 3058c2ecf20Sopenharmony_ci } else { 3068c2ecf20Sopenharmony_ci memcpy(net->dev_addr, dev->ctrl_buf, ETH_ALEN); 3078c2ecf20Sopenharmony_ci retval = 0; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return retval; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int ipheth_rx_submit(struct ipheth_device *dev, gfp_t mem_flags) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct usb_device *udev = dev->udev; 3168c2ecf20Sopenharmony_ci int retval; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci usb_fill_bulk_urb(dev->rx_urb, udev, 3198c2ecf20Sopenharmony_ci usb_rcvbulkpipe(udev, dev->bulk_in), 3208c2ecf20Sopenharmony_ci dev->rx_buf, IPHETH_BUF_SIZE + IPHETH_IP_ALIGN, 3218c2ecf20Sopenharmony_ci ipheth_rcvbulk_callback, 3228c2ecf20Sopenharmony_ci dev); 3238c2ecf20Sopenharmony_ci dev->rx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci retval = usb_submit_urb(dev->rx_urb, mem_flags); 3268c2ecf20Sopenharmony_ci if (retval) 3278c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, "%s: usb_submit_urb: %d\n", 3288c2ecf20Sopenharmony_ci __func__, retval); 3298c2ecf20Sopenharmony_ci return retval; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic int ipheth_open(struct net_device *net) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct ipheth_device *dev = netdev_priv(net); 3358c2ecf20Sopenharmony_ci struct usb_device *udev = dev->udev; 3368c2ecf20Sopenharmony_ci int retval = 0; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci usb_set_interface(udev, IPHETH_INTFNUM, IPHETH_ALT_INTFNUM); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci retval = ipheth_carrier_set(dev); 3418c2ecf20Sopenharmony_ci if (retval) 3428c2ecf20Sopenharmony_ci return retval; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci retval = ipheth_rx_submit(dev, GFP_KERNEL); 3458c2ecf20Sopenharmony_ci if (retval) 3468c2ecf20Sopenharmony_ci return retval; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT); 3498c2ecf20Sopenharmony_ci return retval; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic int ipheth_close(struct net_device *net) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci struct ipheth_device *dev = netdev_priv(net); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&dev->carrier_work); 3578c2ecf20Sopenharmony_ci netif_stop_queue(net); 3588c2ecf20Sopenharmony_ci return 0; 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic netdev_tx_t ipheth_tx(struct sk_buff *skb, struct net_device *net) 3628c2ecf20Sopenharmony_ci{ 3638c2ecf20Sopenharmony_ci struct ipheth_device *dev = netdev_priv(net); 3648c2ecf20Sopenharmony_ci struct usb_device *udev = dev->udev; 3658c2ecf20Sopenharmony_ci int retval; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* Paranoid */ 3688c2ecf20Sopenharmony_ci if (skb->len > IPHETH_BUF_SIZE) { 3698c2ecf20Sopenharmony_ci WARN(1, "%s: skb too large: %d bytes\n", __func__, skb->len); 3708c2ecf20Sopenharmony_ci dev->net->stats.tx_dropped++; 3718c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 3728c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci memcpy(dev->tx_buf, skb->data, skb->len); 3768c2ecf20Sopenharmony_ci if (skb->len < IPHETH_BUF_SIZE) 3778c2ecf20Sopenharmony_ci memset(dev->tx_buf + skb->len, 0, IPHETH_BUF_SIZE - skb->len); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci usb_fill_bulk_urb(dev->tx_urb, udev, 3808c2ecf20Sopenharmony_ci usb_sndbulkpipe(udev, dev->bulk_out), 3818c2ecf20Sopenharmony_ci dev->tx_buf, IPHETH_BUF_SIZE, 3828c2ecf20Sopenharmony_ci ipheth_sndbulk_callback, 3838c2ecf20Sopenharmony_ci dev); 3848c2ecf20Sopenharmony_ci dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci netif_stop_queue(net); 3878c2ecf20Sopenharmony_ci retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC); 3888c2ecf20Sopenharmony_ci if (retval) { 3898c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, "%s: usb_submit_urb: %d\n", 3908c2ecf20Sopenharmony_ci __func__, retval); 3918c2ecf20Sopenharmony_ci dev->net->stats.tx_errors++; 3928c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 3938c2ecf20Sopenharmony_ci netif_wake_queue(net); 3948c2ecf20Sopenharmony_ci } else { 3958c2ecf20Sopenharmony_ci dev->net->stats.tx_packets++; 3968c2ecf20Sopenharmony_ci dev->net->stats.tx_bytes += skb->len; 3978c2ecf20Sopenharmony_ci dev_consume_skb_any(skb); 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic void ipheth_tx_timeout(struct net_device *net, unsigned int txqueue) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct ipheth_device *dev = netdev_priv(net); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci dev_err(&dev->intf->dev, "%s: TX timeout\n", __func__); 4088c2ecf20Sopenharmony_ci dev->net->stats.tx_errors++; 4098c2ecf20Sopenharmony_ci usb_unlink_urb(dev->tx_urb); 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic u32 ipheth_ethtool_op_get_link(struct net_device *net) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct ipheth_device *dev = netdev_priv(net); 4158c2ecf20Sopenharmony_ci return netif_carrier_ok(dev->net); 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic const struct ethtool_ops ops = { 4198c2ecf20Sopenharmony_ci .get_link = ipheth_ethtool_op_get_link 4208c2ecf20Sopenharmony_ci}; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic const struct net_device_ops ipheth_netdev_ops = { 4238c2ecf20Sopenharmony_ci .ndo_open = ipheth_open, 4248c2ecf20Sopenharmony_ci .ndo_stop = ipheth_close, 4258c2ecf20Sopenharmony_ci .ndo_start_xmit = ipheth_tx, 4268c2ecf20Sopenharmony_ci .ndo_tx_timeout = ipheth_tx_timeout, 4278c2ecf20Sopenharmony_ci}; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic int ipheth_probe(struct usb_interface *intf, 4308c2ecf20Sopenharmony_ci const struct usb_device_id *id) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 4338c2ecf20Sopenharmony_ci struct usb_host_interface *hintf; 4348c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *endp; 4358c2ecf20Sopenharmony_ci struct ipheth_device *dev; 4368c2ecf20Sopenharmony_ci struct net_device *netdev; 4378c2ecf20Sopenharmony_ci int i; 4388c2ecf20Sopenharmony_ci int retval; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci netdev = alloc_etherdev(sizeof(struct ipheth_device)); 4418c2ecf20Sopenharmony_ci if (!netdev) 4428c2ecf20Sopenharmony_ci return -ENOMEM; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci netdev->netdev_ops = &ipheth_netdev_ops; 4458c2ecf20Sopenharmony_ci netdev->watchdog_timeo = IPHETH_TX_TIMEOUT; 4468c2ecf20Sopenharmony_ci strcpy(netdev->name, "eth%d"); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci dev = netdev_priv(netdev); 4498c2ecf20Sopenharmony_ci dev->udev = udev; 4508c2ecf20Sopenharmony_ci dev->net = netdev; 4518c2ecf20Sopenharmony_ci dev->intf = intf; 4528c2ecf20Sopenharmony_ci dev->confirmed_pairing = false; 4538c2ecf20Sopenharmony_ci /* Set up endpoints */ 4548c2ecf20Sopenharmony_ci hintf = usb_altnum_to_altsetting(intf, IPHETH_ALT_INTFNUM); 4558c2ecf20Sopenharmony_ci if (hintf == NULL) { 4568c2ecf20Sopenharmony_ci retval = -ENODEV; 4578c2ecf20Sopenharmony_ci dev_err(&intf->dev, "Unable to find alternate settings interface\n"); 4588c2ecf20Sopenharmony_ci goto err_endpoints; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci for (i = 0; i < hintf->desc.bNumEndpoints; i++) { 4628c2ecf20Sopenharmony_ci endp = &hintf->endpoint[i].desc; 4638c2ecf20Sopenharmony_ci if (usb_endpoint_is_bulk_in(endp)) 4648c2ecf20Sopenharmony_ci dev->bulk_in = endp->bEndpointAddress; 4658c2ecf20Sopenharmony_ci else if (usb_endpoint_is_bulk_out(endp)) 4668c2ecf20Sopenharmony_ci dev->bulk_out = endp->bEndpointAddress; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci if (!(dev->bulk_in && dev->bulk_out)) { 4698c2ecf20Sopenharmony_ci retval = -ENODEV; 4708c2ecf20Sopenharmony_ci dev_err(&intf->dev, "Unable to find endpoints\n"); 4718c2ecf20Sopenharmony_ci goto err_endpoints; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci dev->ctrl_buf = kmalloc(IPHETH_CTRL_BUF_SIZE, GFP_KERNEL); 4758c2ecf20Sopenharmony_ci if (dev->ctrl_buf == NULL) { 4768c2ecf20Sopenharmony_ci retval = -ENOMEM; 4778c2ecf20Sopenharmony_ci goto err_alloc_ctrl_buf; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci retval = ipheth_get_macaddr(dev); 4818c2ecf20Sopenharmony_ci if (retval) 4828c2ecf20Sopenharmony_ci goto err_get_macaddr; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&dev->carrier_work, ipheth_carrier_check_work); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci retval = ipheth_alloc_urbs(dev); 4878c2ecf20Sopenharmony_ci if (retval) { 4888c2ecf20Sopenharmony_ci dev_err(&intf->dev, "error allocating urbs: %d\n", retval); 4898c2ecf20Sopenharmony_ci goto err_alloc_urbs; 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci usb_set_intfdata(intf, dev); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci SET_NETDEV_DEV(netdev, &intf->dev); 4958c2ecf20Sopenharmony_ci netdev->ethtool_ops = &ops; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci retval = register_netdev(netdev); 4988c2ecf20Sopenharmony_ci if (retval) { 4998c2ecf20Sopenharmony_ci dev_err(&intf->dev, "error registering netdev: %d\n", retval); 5008c2ecf20Sopenharmony_ci retval = -EIO; 5018c2ecf20Sopenharmony_ci goto err_register_netdev; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci // carrier down and transmit queues stopped until packet from device 5048c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 5058c2ecf20Sopenharmony_ci netif_tx_stop_all_queues(netdev); 5068c2ecf20Sopenharmony_ci dev_info(&intf->dev, "Apple iPhone USB Ethernet device attached\n"); 5078c2ecf20Sopenharmony_ci return 0; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_cierr_register_netdev: 5108c2ecf20Sopenharmony_ci ipheth_free_urbs(dev); 5118c2ecf20Sopenharmony_cierr_alloc_urbs: 5128c2ecf20Sopenharmony_cierr_get_macaddr: 5138c2ecf20Sopenharmony_cierr_alloc_ctrl_buf: 5148c2ecf20Sopenharmony_ci kfree(dev->ctrl_buf); 5158c2ecf20Sopenharmony_cierr_endpoints: 5168c2ecf20Sopenharmony_ci free_netdev(netdev); 5178c2ecf20Sopenharmony_ci return retval; 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic void ipheth_disconnect(struct usb_interface *intf) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci struct ipheth_device *dev; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci dev = usb_get_intfdata(intf); 5258c2ecf20Sopenharmony_ci if (dev != NULL) { 5268c2ecf20Sopenharmony_ci unregister_netdev(dev->net); 5278c2ecf20Sopenharmony_ci ipheth_kill_urbs(dev); 5288c2ecf20Sopenharmony_ci ipheth_free_urbs(dev); 5298c2ecf20Sopenharmony_ci kfree(dev->ctrl_buf); 5308c2ecf20Sopenharmony_ci free_netdev(dev->net); 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 5338c2ecf20Sopenharmony_ci dev_info(&intf->dev, "Apple iPhone USB Ethernet now disconnected\n"); 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic struct usb_driver ipheth_driver = { 5378c2ecf20Sopenharmony_ci .name = "ipheth", 5388c2ecf20Sopenharmony_ci .probe = ipheth_probe, 5398c2ecf20Sopenharmony_ci .disconnect = ipheth_disconnect, 5408c2ecf20Sopenharmony_ci .id_table = ipheth_table, 5418c2ecf20Sopenharmony_ci .disable_hub_initiated_lpm = 1, 5428c2ecf20Sopenharmony_ci}; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cimodule_usb_driver(ipheth_driver); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ciMODULE_AUTHOR("Diego Giagio <diego@giagio.com>"); 5478c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Apple iPhone USB Ethernet driver"); 5488c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 549