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