linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Staging: Add new driver ipheth
@ 2010-03-30 21:34 L. Alberto Giménez
  2010-03-30 21:34 ` L. Alberto Giménez
                   ` (4 more replies)
  0 siblings, 5 replies; 34+ messages in thread
From: L. Alberto Giménez @ 2010-03-30 21:34 UTC (permalink / raw)
  To: devel; +Cc: gregkh, wfp5p, bzolnier, alan, devel, linux-kernel

Hi,

I'm submitting this new driver to the staging tree so it can be reviewed. I
didn't write it by myself, but I've tested it and it works just fine (at least
with a 3G iPhone). The original maintainers are not very responsive and the
codebase has not been updated (apart from minor fixes) since January.

Anyway, before submitting it I've fixed (most) checkpatch issues. Only two
over-80-characters lines issues are standing, but looking at the code it did
make sense for me letting those 3-4 characters to overflow to keep the code
readable.

More than that, I have no real experience with kernel programming (read LDD,
articles, files inside Documentation/ directory and so), so if you see obvious
bugs or something that should be fixed, please let me know with a pointer to the
documentation that may help me to undertand why it's wrong and how should I fix
it.

L. Alberto Giménez (1):
  Staging: Add new driver ipheth

 drivers/staging/Kconfig         |    2 +
 drivers/staging/Makefile        |    1 +
 drivers/staging/ipheth/Kconfig  |   11 +
 drivers/staging/ipheth/Makefile |    1 +
 drivers/staging/ipheth/ipheth.c |  573 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 588 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/ipheth/Kconfig
 create mode 100644 drivers/staging/ipheth/Makefile
 create mode 100644 drivers/staging/ipheth/ipheth.c


^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH] Staging: Add new driver ipheth
  2010-03-30 21:34 [PATCH] Staging: Add new driver ipheth L. Alberto Giménez
@ 2010-03-30 21:34 ` L. Alberto Giménez
  2010-03-30 21:45   ` Greg KH
  2010-03-30 23:01   ` [PATCH] drivers/net/usb: " L. Alberto Giménez
  2010-03-31 19:42 ` [PATCHv3] " L. Alberto Giménez
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 34+ messages in thread
From: L. Alberto Giménez @ 2010-03-30 21:34 UTC (permalink / raw)
  To: devel; +Cc: gregkh, wfp5p, bzolnier, alan, devel, linux-kernel

Add new driver to use tethering with an iPhone device to the staging tree.

Signed-off-by: L. Alberto Giménez <agimenez@sysvalve.es>
---
 drivers/staging/Kconfig         |    2 +
 drivers/staging/Makefile        |    1 +
 drivers/staging/ipheth/Kconfig  |   11 +
 drivers/staging/ipheth/Makefile |    1 +
 drivers/staging/ipheth/ipheth.c |  573 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 588 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/ipheth/Kconfig
 create mode 100644 drivers/staging/ipheth/Makefile
 create mode 100644 drivers/staging/ipheth/ipheth.c

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 7696a66..28e2191 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -139,5 +139,7 @@ source "drivers/staging/dt3155/Kconfig"
 
 source "drivers/staging/crystalhd/Kconfig"
 
+source "drivers/staging/ipheth/Kconfig"
+
 endif # !STAGING_EXCLUDE_BUILD
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index ea2e70e..87800f4 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -51,3 +51,4 @@ obj-$(CONFIG_PCMCIA_NETWAVE)	+= netwave/
 obj-$(CONFIG_FB_SM7XX)		+= sm7xx/
 obj-$(CONFIG_DT3155)		+= dt3155/
 obj-$(CONFIG_CRYSTALHD)		+= crystalhd/
+obj-$(CONFIG_IPHETH)		+= ipheth/
diff --git a/drivers/staging/ipheth/Kconfig b/drivers/staging/ipheth/Kconfig
new file mode 100644
index 0000000..6a67c40
--- /dev/null
+++ b/drivers/staging/ipheth/Kconfig
@@ -0,0 +1,11 @@
+config IPHETH
+	tristate "Apple iPhone USB Ethernet driver"
+	depends on USB && m
+	---help---
+	  Module used to share Internet connection (tethering) from your
+	  iPhone (Original, 3G and 3GS) to your system.
+	  Note that you need userspace libraries and programs that are needed
+	  to pair your device with your system and that understand the iPhone
+	  protocol.
+
+	  For more information: http://giagio.com/wiki/moin.cgi/iPhoneEthernetDriver
diff --git a/drivers/staging/ipheth/Makefile b/drivers/staging/ipheth/Makefile
new file mode 100644
index 0000000..f6a7ce5
--- /dev/null
+++ b/drivers/staging/ipheth/Makefile
@@ -0,0 +1 @@
+obj-m += ipheth.o
diff --git a/drivers/staging/ipheth/ipheth.c b/drivers/staging/ipheth/ipheth.c
new file mode 100644
index 0000000..6616c9b
--- /dev/null
+++ b/drivers/staging/ipheth/ipheth.c
@@ -0,0 +1,573 @@
+/*
+ * 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/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+
+#define USB_VENDOR_APPLE        0x05ac
+#define USB_PRODUCT_IPHETH     0x1290
+#define USB_PRODUCT_IPHETH_3G   0x1292
+#define USB_PRODUCT_IPHETH_3GS  0x1294
+
+#define IPHETH_USBINTF_CLASS    255
+#define IPHETH_USBINTF_SUBCLASS 253
+
+#define IPHETH_BUF_SIZE         1516
+#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_CARRIER_CHECK 0x45
+
+#define IPHETH_CARRIER_CHECK_TIMEOUT (1 * HZ)
+#define IPHETH_CARRIER_ON       0x04
+
+static struct usb_device_id ipheth_table[] = {
+       { USB_DEVICE(USB_VENDOR_APPLE, USB_PRODUCT_IPHETH) },
+	{ USB_DEVICE(USB_VENDOR_APPLE, USB_PRODUCT_IPHETH_3G) },
+	{ USB_DEVICE(USB_VENDOR_APPLE, USB_PRODUCT_IPHETH_3GS) },
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, ipheth_table);
+
+struct ipheth_device {
+	struct usb_device *udev;
+	struct usb_interface *intf;
+	struct net_device *net;
+	struct net_device_stats stats;
+	struct sk_buff *tx_skb;
+	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;
+};
+
+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;
+
+	rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (rx_urb == NULL)
+		goto error;
+
+	tx_buf = usb_buffer_alloc(iphone->udev,
+				  IPHETH_BUF_SIZE,
+				  GFP_KERNEL,
+				  &tx_urb->transfer_dma);
+	if (tx_buf == NULL)
+		goto error;
+
+	rx_buf = usb_buffer_alloc(iphone->udev,
+				  IPHETH_BUF_SIZE,
+				  GFP_KERNEL,
+				  &rx_urb->transfer_dma);
+	if (rx_buf == NULL)
+		goto error;
+
+
+	iphone->tx_urb = tx_urb;
+	iphone->rx_urb = rx_urb;
+	iphone->tx_buf = tx_buf;
+	iphone->rx_buf = rx_buf;
+	return 0;
+
+error:
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, rx_buf,
+			rx_urb->transfer_dma);
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, tx_buf,
+			tx_urb->transfer_dma);
+	usb_free_urb(rx_urb);
+	usb_free_urb(tx_urb);
+	return -ENOMEM;
+}
+
+static void ipheth_free_urbs(struct ipheth_device *iphone)
+{
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, iphone->rx_buf,
+			iphone->rx_urb->transfer_dma);
+	usb_buffer_free(iphone->udev, IPHETH_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_unlink_urbs(struct ipheth_device *dev)
+{
+	usb_kill_urb(dev->tx_urb);
+	usb_kill_urb(dev->rx_urb);
+}
+
+static void ipheth_rcvbulk_callback(struct urb *urb)
+{
+	struct ipheth_device *dev;
+	struct sk_buff *skb;
+	int status;
+	char *buf;
+	int len;
+
+	dev = urb->context;
+	if (dev == NULL)
+		return;
+
+	status = urb->status;
+	switch (status) {
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		return;
+	case 0:
+		break;
+	default:
+		err("%s: urb status: %d", __func__, urb->status);
+		return;
+	}
+
+	len = urb->actual_length;
+	buf = urb->transfer_buffer;
+
+	skb = dev_alloc_skb(NET_IP_ALIGN + len);
+	if (!skb) {
+		err("%s: dev_alloc_skb: -ENOMEM", __func__);
+		dev->stats.rx_dropped++;
+		return;
+	}
+
+	skb_reserve(skb, NET_IP_ALIGN);
+	memcpy(skb_put(skb, len), buf + NET_IP_ALIGN, len - NET_IP_ALIGN);
+	skb->dev = dev->net;
+	skb->protocol = eth_type_trans(skb, dev->net);
+
+	dev->stats.rx_packets++;
+	dev->stats.rx_bytes += len;
+
+	netif_rx(skb);
+	ipheth_rx_submit(dev, GFP_ATOMIC);
+}
+
+static void ipheth_sndbulk_callback(struct urb *urb)
+{
+	struct ipheth_device *dev;
+
+	dev = urb->context;
+	if (dev == NULL)
+		return;
+
+	if (urb->status != 0 &&
+	    urb->status != -ENOENT &&
+	    urb->status != -ECONNRESET &&
+	    urb->status != -ESHUTDOWN)
+		err("%s: urb status: %d", __func__, urb->status);
+
+	netif_wake_queue(dev->net);
+	dev_kfree_skb_irq(dev->tx_skb);
+}
+
+static int ipheth_carrier_set(struct ipheth_device *dev)
+{
+	struct usb_device *udev = dev->udev;
+	int retval;
+
+	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) {
+		err("%s: usb_control_msg: %d", __func__, retval);
+		return retval;
+	}
+
+	if (dev->ctrl_buf[0] == IPHETH_CARRIER_ON)
+		netif_carrier_on(dev->net);
+	else
+		netif_carrier_off(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) {
+		err("%s: usb_control_msg: %d", __func__, retval);
+	} else if (retval < ETH_ALEN) {
+		err("%s: usb_control_msg: short packet: %d bytes",
+						__func__, retval);
+		retval = -EINVAL;
+	} else {
+		memcpy(net->dev_addr, dev->ctrl_buf, ETH_ALEN);
+		retval = 0;
+	}
+
+	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, IPHETH_BUF_SIZE,
+			  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)
+		err("%s: usb_submit_urb: %d", __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);
+	usb_clear_halt(udev, usb_rcvbulkpipe(udev, dev->bulk_in));
+	usb_clear_halt(udev, usb_sndbulkpipe(udev, dev->bulk_out));
+
+	retval = ipheth_carrier_set(dev);
+	if (retval)
+		goto error;
+
+	retval = ipheth_rx_submit(dev, GFP_KERNEL);
+	if (retval)
+		goto error;
+
+	schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT);
+	netif_start_queue(net);
+error:
+	return retval;
+}
+
+static int ipheth_close(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+
+	cancel_delayed_work_sync(&dev->carrier_work);
+	netif_stop_queue(net);
+	return 0;
+}
+
+static int ipheth_tx(struct sk_buff *skb, struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	struct usb_device *udev = dev->udev;
+	int retval;
+
+	/* Paranoid */
+	if (skb->len > IPHETH_BUF_SIZE) {
+		err("%s: skb too large: %d bytes", __func__, skb->len);
+		dev->stats.tx_dropped++;
+		dev_kfree_skb_irq(skb);
+		goto exit;
+	}
+
+	memset(dev->tx_buf, 0, IPHETH_BUF_SIZE);
+	memcpy(dev->tx_buf, skb->data, skb->len);
+
+	usb_fill_bulk_urb(dev->tx_urb, udev,
+			  usb_sndbulkpipe(udev, dev->bulk_out),
+			  dev->tx_buf, IPHETH_BUF_SIZE,
+			  ipheth_sndbulk_callback,
+			  dev);
+	dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC);
+	if (retval) {
+		err("%s: usb_submit_urb: %d", __func__, retval);
+		dev->stats.tx_errors++;
+		dev_kfree_skb_irq(skb);
+	} else {
+		net->trans_start = jiffies;
+		dev->tx_skb = skb;
+
+		dev->stats.tx_packets++;
+		dev->stats.tx_bytes += skb->len;
+		netif_stop_queue(net);
+	}
+exit:
+	return NETDEV_TX_OK;
+}
+
+static void ipheth_tx_timeout(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+
+	err("%s: TX timeout", __func__);
+	dev->stats.tx_errors++;
+	usb_unlink_urb(dev->tx_urb);
+}
+
+static struct net_device_stats *ipheth_stats(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	return &dev->stats;
+}
+
+static u32 ipheth_ethtool_op_get_link(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	return netif_carrier_ok(dev->net);
+}
+
+static struct ethtool_ops ops = {
+	.get_link = ipheth_ethtool_op_get_link
+};
+
+#ifdef HAVE_NET_DEVICE_OPS
+static const struct net_device_ops ipheth_netdev_ops = {
+       .ndo_open = &ipheth_open,
+       .ndo_stop = &ipheth_close,
+       .ndo_start_xmit = &ipheth_tx,
+       .ndo_tx_timeout = &ipheth_tx_timeout,
+       .ndo_get_stats = &ipheth_stats,
+};
+#endif
+
+static int ipheth_probe(struct usb_interface *intf,
+			 const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct usb_host_interface *hintf;
+	struct usb_endpoint_descriptor *endp;
+	struct ipheth_device *dev;
+	struct net_device *netdev;
+	int i;
+	int retval;
+
+	/* Ensure we are probing the right interface */
+	if (intf->cur_altsetting->desc.bInterfaceClass != IPHETH_USBINTF_CLASS ||
+	    intf->cur_altsetting->desc.bInterfaceSubClass != IPHETH_USBINTF_SUBCLASS)
+		return -ENODEV;
+
+	netdev = alloc_etherdev(sizeof(struct ipheth_device));
+	if (!netdev)
+		return -ENOMEM;
+
+#ifdef HAVE_NET_DEVICE_OPS
+	netdev->netdev_ops = &ipheth_netdev_ops;
+#else /* CONFIG_COMPAT_NET_DEV_OPS */
+	netdev->open = &ipheth_open;
+	netdev->stop = &ipheth_close;
+	netdev->hard_start_xmit = &ipheth_tx;
+	netdev->tx_timeout = &ipheth_tx_timeout;
+	netdev->get_stats = &ipheth_stats;
+#endif
+	netdev->watchdog_timeo = IPHETH_TX_TIMEOUT;
+
+	dev = netdev_priv(netdev);
+	dev->udev = udev;
+	dev->net = netdev;
+	dev->intf = intf;
+
+	/* Set up endpoints */
+	hintf = usb_altnum_to_altsetting(intf, IPHETH_ALT_INTFNUM);
+	if (hintf == NULL) {
+		retval = -ENODEV;
+		err("Unable to find alternate settings interface");
+		goto err_endpoints;
+	}
+
+	for (i = 0; i < hintf->desc.bNumEndpoints; i++) {
+		endp = &hintf->endpoint[i].desc;
+		if (usb_endpoint_is_bulk_in(endp))
+			dev->bulk_in = endp->bEndpointAddress;
+		else if (usb_endpoint_is_bulk_out(endp))
+			dev->bulk_out = endp->bEndpointAddress;
+	}
+	if (!(dev->bulk_in && dev->bulk_out)) {
+		retval = -ENODEV;
+		err("Unable to find endpoints");
+		goto err_endpoints;
+	}
+
+	dev->ctrl_buf = kmalloc(IPHETH_CTRL_BUF_SIZE, GFP_KERNEL);
+	if (dev->ctrl_buf == NULL) {
+		retval = -ENOMEM;
+		goto err_alloc_ctrl_buf;
+	}
+
+	retval = ipheth_get_macaddr(dev);
+	if (retval)
+		goto err_get_macaddr;
+
+	INIT_DELAYED_WORK(&dev->carrier_work, ipheth_carrier_check_work);
+
+	retval = ipheth_alloc_urbs(dev);
+	if (retval) {
+		err("error allocating urbs: %d", retval);
+		goto err_alloc_urbs;
+	}
+
+	usb_set_intfdata(intf, dev);
+
+	SET_NETDEV_DEV(netdev, &intf->dev);
+	SET_ETHTOOL_OPS(netdev, &ops);
+
+	retval = register_netdev(netdev);
+	if (retval) {
+		err("error registering netdev: %d", retval);
+		retval = -EIO;
+		goto err_register_netdev;
+	}
+
+	dev_info(&intf->dev, "Apple iPhone USB Ethernet device attached\n");
+	return 0;
+
+err_register_netdev:
+	ipheth_free_urbs(dev);
+err_alloc_urbs:
+err_get_macaddr:
+err_alloc_ctrl_buf:
+	kfree(dev->ctrl_buf);
+err_endpoints:
+	free_netdev(netdev);
+	return retval;
+}
+
+static void ipheth_disconnect(struct usb_interface *intf)
+{
+	struct ipheth_device *dev;
+
+	dev = usb_get_intfdata(intf);
+	if (dev != NULL) {
+		unregister_netdev(dev->net);
+		ipheth_unlink_urbs(dev);
+		ipheth_free_urbs(dev);
+		kfree(dev->ctrl_buf);
+		free_netdev(dev->net);
+	}
+	usb_set_intfdata(intf, NULL);
+	dev_info(&intf->dev, "Apple iPhone USB Ethernet now disconnected\n");
+}
+
+static struct usb_driver ipheth_driver = {
+	.name =		"ipheth",
+	.probe =	ipheth_probe,
+	.disconnect =	ipheth_disconnect,
+	.id_table =	ipheth_table,
+	.supports_autosuspend = 0,
+};
+
+static int __init ipheth_init(void)
+{
+	int retval;
+
+	retval = usb_register(&ipheth_driver);
+	if (retval) {
+		err("usb_register failed: %d", retval);
+		return retval;
+	}
+	return 0;
+}
+
+static void __exit ipheth_exit(void)
+{
+	usb_deregister(&ipheth_driver);
+}
+
+module_init(ipheth_init);
+module_exit(ipheth_exit);
+
+MODULE_AUTHOR("Diego Giagio <diego@giagio.com>");
+MODULE_DESCRIPTION("Apple iPhone USB Ethernet driver");
+MODULE_LICENSE("Dual BSD/GPL");
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH] Staging: Add new driver ipheth
  2010-03-30 21:34 ` L. Alberto Giménez
@ 2010-03-30 21:45   ` Greg KH
  2010-03-30 21:58     ` "L. Alberto Giménez"
  2010-03-30 23:01   ` [PATCH] drivers/net/usb: " L. Alberto Giménez
  1 sibling, 1 reply; 34+ messages in thread
From: Greg KH @ 2010-03-30 21:45 UTC (permalink / raw)
  To: L. Alberto Gim??nez; +Cc: devel, wfp5p, bzolnier, alan, devel, linux-kernel

On Tue, Mar 30, 2010 at 11:34:24PM +0200, L. Alberto Gim??nez wrote:
> Add new driver to use tethering with an iPhone device to the staging tree.

Why the staging tree?

What is wrong with the driver that it needs to go there and not to the
main kernel tree?

Any driver added to the staging tree needs a TODO file describing what
needs to be fixed with it, and who to send the patches to.

Did you write this driver?  If not, please put a "From:" line that shows
who did write it.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH] Staging: Add new driver ipheth
  2010-03-30 21:45   ` Greg KH
@ 2010-03-30 21:58     ` "L. Alberto Giménez"
  2010-03-30 22:11       ` Greg KH
  2010-03-31 14:33       ` Pavel Machek
  0 siblings, 2 replies; 34+ messages in thread
From: "L. Alberto Giménez" @ 2010-03-30 21:58 UTC (permalink / raw)
  To: Greg KH; +Cc: bzolnier, linux-kernel, devel, alan

On 03/30/2010 11:45 PM, Greg KH wrote:
> On Tue, Mar 30, 2010 at 11:34:24PM +0200, L. Alberto Gim??nez wrote:
>> Add new driver to use tethering with an iPhone device to the staging tree.
> 
> Why the staging tree?
> 
> What is wrong with the driver that it needs to go there and not to the
> main kernel tree?
> 

Sorry, I thought that the first step was getting into staging. I ignored
the fact that it needed a TODO file. I'm not either sure about where
should it go since it uses the USB interface but in fact is used as a
network device (drivers/net? drivers/usb?). My guess is drivers/net, but
I'm pretty sure I'm wrong :)

> Did you write this driver?  If not, please put a "From:" line that shows
> who did write it.

Should I just add a "From: " email-like header in the patch, in the
commit or in the body? I'm not sure how to do it.

Regards,
-- 
L. Alberto Giménez
JabberID agimenez@jabber.sysvalve.es
GnuPG key ID 0x3BAABDE1

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH] Staging: Add new driver ipheth
  2010-03-30 21:58     ` "L. Alberto Giménez"
@ 2010-03-30 22:11       ` Greg KH
  2010-03-31 14:33       ` Pavel Machek
  1 sibling, 0 replies; 34+ messages in thread
From: Greg KH @ 2010-03-30 22:11 UTC (permalink / raw)
  To: L. Alberto Gim?nez; +Cc: bzolnier, linux-kernel, devel, alan

On Tue, Mar 30, 2010 at 11:58:22PM +0200, "L. Alberto Gim?nez" wrote:
> On 03/30/2010 11:45 PM, Greg KH wrote:
> > On Tue, Mar 30, 2010 at 11:34:24PM +0200, L. Alberto Gim??nez wrote:
> >> Add new driver to use tethering with an iPhone device to the staging tree.
> > 
> > Why the staging tree?
> > 
> > What is wrong with the driver that it needs to go there and not to the
> > main kernel tree?
> > 
> 
> Sorry, I thought that the first step was getting into staging.

For the majority of the drivers out there, no, this is not the first
step.  Especially ones as clean as this one.  Staging is only for the
"crap" code out there :)

> I ignored the fact that it needed a TODO file. I'm not either sure
> about where should it go since it uses the USB interface but in fact
> is used as a network device (drivers/net? drivers/usb?). My guess is
> drivers/net, but I'm pretty sure I'm wrong :)

drivers/net/usb/ would be the proper place for it.

> > Did you write this driver?  If not, please put a "From:" line that shows
> > who did write it.
> 
> Should I just add a "From: " email-like header in the patch, in the
> commit or in the body? I'm not sure how to do it.

Put it as the first line of the changelog comment in the email body, and
then when git applies it, the proper author will be credited.

care to try it again?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH] drivers/net/usb: Add new driver ipheth
  2010-03-30 21:34 ` L. Alberto Giménez
  2010-03-30 21:45   ` Greg KH
@ 2010-03-30 23:01   ` L. Alberto Giménez
  2010-03-30 23:25     ` Greg KH
  1 sibling, 1 reply; 34+ messages in thread
From: L. Alberto Giménez @ 2010-03-30 23:01 UTC (permalink / raw)
  To: devel; +Cc: gregkh, wfp5p, bzolnier, alan, linux-kernel

From: Diego Giagio <diego@giagio.com>

Add new driver to use tethering with an iPhone device to the staging tree.

Signed-off-by: L. Alberto Giménez <agimenez@sysvalve.es>
---
 drivers/net/usb/Kconfig  |   12 +
 drivers/net/usb/Makefile |    1 +
 drivers/net/usb/ipheth.c |  573 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 586 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/usb/ipheth.c

diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index ba56ce4..63be4ca 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -385,4 +385,16 @@ config USB_CDC_PHONET
 	  cellular modem, as found on most Nokia handsets with the
 	  "PC suite" USB profile.
 
+config USB_IPHETH
+	tristate "Apple iPhone USB Ethernet driver"
+	default n
+	---help---
+	  Module used to share Internet connection (tethering) from your
+	  iPhone (Original, 3G and 3GS) to your system.
+	  Note that you need userspace libraries and programs that are needed
+	  to pair your device with your system and that understand the iPhone
+	  protocol.
+
+	  For more information: http://giagio.com/wiki/moin.cgi/iPhoneEthernetDriver
+
 endmenu
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 82ea629..edb09c0 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -23,4 +23,5 @@ obj-$(CONFIG_USB_NET_MCS7830)	+= mcs7830.o
 obj-$(CONFIG_USB_USBNET)	+= usbnet.o
 obj-$(CONFIG_USB_NET_INT51X1)	+= int51x1.o
 obj-$(CONFIG_USB_CDC_PHONET)	+= cdc-phonet.o
+obj-$(CONFIG_USB_IPHETH)	+= ipheth.o
 
diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c
new file mode 100644
index 0000000..3f93da1
--- /dev/null
+++ b/drivers/net/usb/ipheth.c
@@ -0,0 +1,573 @@
+/*
+ * 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/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+
+#define USB_VENDOR_APPLE        0x05ac
+#define USB_PRODUCT_IPHETH     0x1290
+#define USB_PRODUCT_IPHETH_3G   0x1292
+#define USB_PRODUCT_IPHETH_3GS  0x1294
+
+#define IPHETH_USBINTF_CLASS    255
+#define IPHETH_USBINTF_SUBCLASS 253
+
+#define IPHETH_BUF_SIZE         1516
+#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_CARRIER_CHECK 0x45
+
+#define IPHETH_CARRIER_CHECK_TIMEOUT (1 * HZ)
+#define IPHETH_CARRIER_ON       0x04
+
+static struct usb_device_id ipheth_table[] = {
+	{ USB_DEVICE(USB_VENDOR_APPLE, USB_PRODUCT_IPHETH) },
+	{ USB_DEVICE(USB_VENDOR_APPLE, USB_PRODUCT_IPHETH_3G) },
+	{ USB_DEVICE(USB_VENDOR_APPLE, USB_PRODUCT_IPHETH_3GS) },
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, ipheth_table);
+
+struct ipheth_device {
+	struct usb_device *udev;
+	struct usb_interface *intf;
+	struct net_device *net;
+	struct net_device_stats stats;
+	struct sk_buff *tx_skb;
+	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;
+};
+
+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;
+
+	rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (rx_urb == NULL)
+		goto error;
+
+	tx_buf = usb_buffer_alloc(iphone->udev,
+				  IPHETH_BUF_SIZE,
+				  GFP_KERNEL,
+				  &tx_urb->transfer_dma);
+	if (tx_buf == NULL)
+		goto error;
+
+	rx_buf = usb_buffer_alloc(iphone->udev,
+				  IPHETH_BUF_SIZE,
+				  GFP_KERNEL,
+				  &rx_urb->transfer_dma);
+	if (rx_buf == NULL)
+		goto error;
+
+
+	iphone->tx_urb = tx_urb;
+	iphone->rx_urb = rx_urb;
+	iphone->tx_buf = tx_buf;
+	iphone->rx_buf = rx_buf;
+	return 0;
+
+error:
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, rx_buf,
+			rx_urb->transfer_dma);
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, tx_buf,
+			tx_urb->transfer_dma);
+	usb_free_urb(rx_urb);
+	usb_free_urb(tx_urb);
+	return -ENOMEM;
+}
+
+static void ipheth_free_urbs(struct ipheth_device *iphone)
+{
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, iphone->rx_buf,
+			iphone->rx_urb->transfer_dma);
+	usb_buffer_free(iphone->udev, IPHETH_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_unlink_urbs(struct ipheth_device *dev)
+{
+	usb_kill_urb(dev->tx_urb);
+	usb_kill_urb(dev->rx_urb);
+}
+
+static void ipheth_rcvbulk_callback(struct urb *urb)
+{
+	struct ipheth_device *dev;
+	struct sk_buff *skb;
+	int status;
+	char *buf;
+	int len;
+
+	dev = urb->context;
+	if (dev == NULL)
+		return;
+
+	status = urb->status;
+	switch (status) {
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		return;
+	case 0:
+		break;
+	default:
+		err("%s: urb status: %d", __func__, urb->status);
+		return;
+	}
+
+	len = urb->actual_length;
+	buf = urb->transfer_buffer;
+
+	skb = dev_alloc_skb(NET_IP_ALIGN + len);
+	if (!skb) {
+		err("%s: dev_alloc_skb: -ENOMEM", __func__);
+		dev->stats.rx_dropped++;
+		return;
+	}
+
+	skb_reserve(skb, NET_IP_ALIGN);
+	memcpy(skb_put(skb, len), buf + NET_IP_ALIGN, len - NET_IP_ALIGN);
+	skb->dev = dev->net;
+	skb->protocol = eth_type_trans(skb, dev->net);
+
+	dev->stats.rx_packets++;
+	dev->stats.rx_bytes += len;
+
+	netif_rx(skb);
+	ipheth_rx_submit(dev, GFP_ATOMIC);
+}
+
+static void ipheth_sndbulk_callback(struct urb *urb)
+{
+	struct ipheth_device *dev;
+
+	dev = urb->context;
+	if (dev == NULL)
+		return;
+
+	if (urb->status != 0 &&
+	    urb->status != -ENOENT &&
+	    urb->status != -ECONNRESET &&
+	    urb->status != -ESHUTDOWN)
+		err("%s: urb status: %d", __func__, urb->status);
+
+	netif_wake_queue(dev->net);
+	dev_kfree_skb_irq(dev->tx_skb);
+}
+
+static int ipheth_carrier_set(struct ipheth_device *dev)
+{
+	struct usb_device *udev = dev->udev;
+	int retval;
+
+	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) {
+		err("%s: usb_control_msg: %d", __func__, retval);
+		return retval;
+	}
+
+	if (dev->ctrl_buf[0] == IPHETH_CARRIER_ON)
+		netif_carrier_on(dev->net);
+	else
+		netif_carrier_off(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) {
+		err("%s: usb_control_msg: %d", __func__, retval);
+	} else if (retval < ETH_ALEN) {
+		err("%s: usb_control_msg: short packet: %d bytes",
+						__func__, retval);
+		retval = -EINVAL;
+	} else {
+		memcpy(net->dev_addr, dev->ctrl_buf, ETH_ALEN);
+		retval = 0;
+	}
+
+	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, IPHETH_BUF_SIZE,
+			  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)
+		err("%s: usb_submit_urb: %d", __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);
+	usb_clear_halt(udev, usb_rcvbulkpipe(udev, dev->bulk_in));
+	usb_clear_halt(udev, usb_sndbulkpipe(udev, dev->bulk_out));
+
+	retval = ipheth_carrier_set(dev);
+	if (retval)
+		goto error;
+
+	retval = ipheth_rx_submit(dev, GFP_KERNEL);
+	if (retval)
+		goto error;
+
+	schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT);
+	netif_start_queue(net);
+error:
+	return retval;
+}
+
+static int ipheth_close(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+
+	cancel_delayed_work_sync(&dev->carrier_work);
+	netif_stop_queue(net);
+	return 0;
+}
+
+static int ipheth_tx(struct sk_buff *skb, struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	struct usb_device *udev = dev->udev;
+	int retval;
+
+	/* Paranoid */
+	if (skb->len > IPHETH_BUF_SIZE) {
+		err("%s: skb too large: %d bytes", __func__, skb->len);
+		dev->stats.tx_dropped++;
+		dev_kfree_skb_irq(skb);
+		goto exit;
+	}
+
+	memset(dev->tx_buf, 0, IPHETH_BUF_SIZE);
+	memcpy(dev->tx_buf, skb->data, skb->len);
+
+	usb_fill_bulk_urb(dev->tx_urb, udev,
+			  usb_sndbulkpipe(udev, dev->bulk_out),
+			  dev->tx_buf, IPHETH_BUF_SIZE,
+			  ipheth_sndbulk_callback,
+			  dev);
+	dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC);
+	if (retval) {
+		err("%s: usb_submit_urb: %d", __func__, retval);
+		dev->stats.tx_errors++;
+		dev_kfree_skb_irq(skb);
+	} else {
+		net->trans_start = jiffies;
+		dev->tx_skb = skb;
+
+		dev->stats.tx_packets++;
+		dev->stats.tx_bytes += skb->len;
+		netif_stop_queue(net);
+	}
+exit:
+	return NETDEV_TX_OK;
+}
+
+static void ipheth_tx_timeout(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+
+	err("%s: TX timeout", __func__);
+	dev->stats.tx_errors++;
+	usb_unlink_urb(dev->tx_urb);
+}
+
+static struct net_device_stats *ipheth_stats(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	return &dev->stats;
+}
+
+static u32 ipheth_ethtool_op_get_link(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	return netif_carrier_ok(dev->net);
+}
+
+static struct ethtool_ops ops = {
+	.get_link = ipheth_ethtool_op_get_link
+};
+
+#ifdef HAVE_NET_DEVICE_OPS
+static const struct net_device_ops ipheth_netdev_ops = {
+       .ndo_open = &ipheth_open,
+       .ndo_stop = &ipheth_close,
+       .ndo_start_xmit = &ipheth_tx,
+       .ndo_tx_timeout = &ipheth_tx_timeout,
+       .ndo_get_stats = &ipheth_stats,
+};
+#endif
+
+static int ipheth_probe(struct usb_interface *intf,
+			 const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct usb_host_interface *hintf;
+	struct usb_endpoint_descriptor *endp;
+	struct ipheth_device *dev;
+	struct net_device *netdev;
+	int i;
+	int retval;
+
+	/* Ensure we are probing the right interface */
+	if (intf->cur_altsetting->desc.bInterfaceClass != IPHETH_USBINTF_CLASS ||
+	    intf->cur_altsetting->desc.bInterfaceSubClass != IPHETH_USBINTF_SUBCLASS)
+		return -ENODEV;
+
+	netdev = alloc_etherdev(sizeof(struct ipheth_device));
+	if (!netdev)
+		return -ENOMEM;
+
+#ifdef HAVE_NET_DEVICE_OPS
+	netdev->netdev_ops = &ipheth_netdev_ops;
+#else /* CONFIG_COMPAT_NET_DEV_OPS */
+	netdev->open = &ipheth_open;
+	netdev->stop = &ipheth_close;
+	netdev->hard_start_xmit = &ipheth_tx;
+	netdev->tx_timeout = &ipheth_tx_timeout;
+	netdev->get_stats = &ipheth_stats;
+#endif
+	netdev->watchdog_timeo = IPHETH_TX_TIMEOUT;
+
+	dev = netdev_priv(netdev);
+	dev->udev = udev;
+	dev->net = netdev;
+	dev->intf = intf;
+
+	/* Set up endpoints */
+	hintf = usb_altnum_to_altsetting(intf, IPHETH_ALT_INTFNUM);
+	if (hintf == NULL) {
+		retval = -ENODEV;
+		err("Unable to find alternate settings interface");
+		goto err_endpoints;
+	}
+
+	for (i = 0; i < hintf->desc.bNumEndpoints; i++) {
+		endp = &hintf->endpoint[i].desc;
+		if (usb_endpoint_is_bulk_in(endp))
+			dev->bulk_in = endp->bEndpointAddress;
+		else if (usb_endpoint_is_bulk_out(endp))
+			dev->bulk_out = endp->bEndpointAddress;
+	}
+	if (!(dev->bulk_in && dev->bulk_out)) {
+		retval = -ENODEV;
+		err("Unable to find endpoints");
+		goto err_endpoints;
+	}
+
+	dev->ctrl_buf = kmalloc(IPHETH_CTRL_BUF_SIZE, GFP_KERNEL);
+	if (dev->ctrl_buf == NULL) {
+		retval = -ENOMEM;
+		goto err_alloc_ctrl_buf;
+	}
+
+	retval = ipheth_get_macaddr(dev);
+	if (retval)
+		goto err_get_macaddr;
+
+	INIT_DELAYED_WORK(&dev->carrier_work, ipheth_carrier_check_work);
+
+	retval = ipheth_alloc_urbs(dev);
+	if (retval) {
+		err("error allocating urbs: %d", retval);
+		goto err_alloc_urbs;
+	}
+
+	usb_set_intfdata(intf, dev);
+
+	SET_NETDEV_DEV(netdev, &intf->dev);
+	SET_ETHTOOL_OPS(netdev, &ops);
+
+	retval = register_netdev(netdev);
+	if (retval) {
+		err("error registering netdev: %d", retval);
+		retval = -EIO;
+		goto err_register_netdev;
+	}
+
+	dev_info(&intf->dev, "Apple iPhone USB Ethernet device attached\n");
+	return 0;
+
+err_register_netdev:
+	ipheth_free_urbs(dev);
+err_alloc_urbs:
+err_get_macaddr:
+err_alloc_ctrl_buf:
+	kfree(dev->ctrl_buf);
+err_endpoints:
+	free_netdev(netdev);
+	return retval;
+}
+
+static void ipheth_disconnect(struct usb_interface *intf)
+{
+	struct ipheth_device *dev;
+
+	dev = usb_get_intfdata(intf);
+	if (dev != NULL) {
+		unregister_netdev(dev->net);
+		ipheth_unlink_urbs(dev);
+		ipheth_free_urbs(dev);
+		kfree(dev->ctrl_buf);
+		free_netdev(dev->net);
+	}
+	usb_set_intfdata(intf, NULL);
+	dev_info(&intf->dev, "Apple iPhone USB Ethernet now disconnected\n");
+}
+
+static struct usb_driver ipheth_driver = {
+	.name =		"ipheth",
+	.probe =	ipheth_probe,
+	.disconnect =	ipheth_disconnect,
+	.id_table =	ipheth_table,
+	.supports_autosuspend = 0,
+};
+
+static int __init ipheth_init(void)
+{
+	int retval;
+
+	retval = usb_register(&ipheth_driver);
+	if (retval) {
+		err("usb_register failed: %d", retval);
+		return retval;
+	}
+	return 0;
+}
+
+static void __exit ipheth_exit(void)
+{
+	usb_deregister(&ipheth_driver);
+}
+
+module_init(ipheth_init);
+module_exit(ipheth_exit);
+
+MODULE_AUTHOR("Diego Giagio <diego@giagio.com>");
+MODULE_DESCRIPTION("Apple iPhone USB Ethernet driver");
+MODULE_LICENSE("Dual BSD/GPL");
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH] drivers/net/usb: Add new driver ipheth
  2010-03-30 23:01   ` [PATCH] drivers/net/usb: " L. Alberto Giménez
@ 2010-03-30 23:25     ` Greg KH
  0 siblings, 0 replies; 34+ messages in thread
From: Greg KH @ 2010-03-30 23:25 UTC (permalink / raw)
  To: L. Alberto Gim??nez; +Cc: devel, wfp5p, bzolnier, alan, linux-kernel

On Wed, Mar 31, 2010 at 01:01:38AM +0200, L. Alberto Gim??nez wrote:

Patches sent to drivers/net/usb/ need to be sent to the correct people.

Run scripts/get_maintainer.pl to determine who that is (and the proper
list to send them to.)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH] Staging: Add new driver ipheth
  2010-03-30 21:58     ` "L. Alberto Giménez"
  2010-03-30 22:11       ` Greg KH
@ 2010-03-31 14:33       ` Pavel Machek
  2010-03-31 14:44         ` Matthew Garrett
  2010-03-31 19:47         ` "L. Alberto Giménez"
  1 sibling, 2 replies; 34+ messages in thread
From: Pavel Machek @ 2010-03-31 14:33 UTC (permalink / raw)
  To: L. Alberto Gim?nez; +Cc: Greg KH, bzolnier, linux-kernel, devel, alan

On Tue 2010-03-30 23:58:22, "L. Alberto Gim?nez" wrote:
> On 03/30/2010 11:45 PM, Greg KH wrote:
> > On Tue, Mar 30, 2010 at 11:34:24PM +0200, L. Alberto Gim??nez wrote:
> >> Add new driver to use tethering with an iPhone device to the staging tree.

Does it work out of the box, or is it some kind of driver for
jailbroken iphones?

> > Why the staging tree?
> > 
> > What is wrong with the driver that it needs to go there and not to the
> > main kernel tree?
> > 
> 
> Sorry, I thought that the first step was getting into staging. I ignored
> the fact that it needed a TODO file. I'm not either sure about where
> should it go since it uses the USB interface but in fact is used as a
> network device (drivers/net? drivers/usb?). My guess is drivers/net, but
> I'm pretty sure I'm wrong :)

drivers/net/usb seems like a place.

								Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH] Staging: Add new driver ipheth
  2010-03-31 14:33       ` Pavel Machek
@ 2010-03-31 14:44         ` Matthew Garrett
  2010-03-31 19:47         ` "L. Alberto Giménez"
  1 sibling, 0 replies; 34+ messages in thread
From: Matthew Garrett @ 2010-03-31 14:44 UTC (permalink / raw)
  To: Pavel Machek
  Cc: L. Alberto Gim?nez, Greg KH, bzolnier, linux-kernel, devel, alan

On Wed, Mar 31, 2010 at 04:33:27PM +0200, Pavel Machek wrote:

> Does it work out of the box, or is it some kind of driver for
> jailbroken iphones?

Works out of the box.

-- 
Matthew Garrett | mjg59@srcf.ucam.org

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCHv3] drivers/net/usb: Add new driver ipheth
  2010-03-30 21:34 [PATCH] Staging: Add new driver ipheth L. Alberto Giménez
  2010-03-30 21:34 ` L. Alberto Giménez
@ 2010-03-31 19:42 ` L. Alberto Giménez
  2010-03-31 20:33   ` Oliver Neukum
  2010-03-31 23:18   ` Ben Hutchings
  2010-04-07 22:11 ` [PATCH Resubmission] " L. Alberto Giménez
                   ` (2 subsequent siblings)
  4 siblings, 2 replies; 34+ messages in thread
From: L. Alberto Giménez @ 2010-03-31 19:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: netdev, linux-usb, oliver, linville, j.dumon, steve.glendinning,
	davem, gregkh, dgiagio, dborca

From: Diego Giagio <diego@giagio.com>

Add new driver to use tethering with an iPhone device.

Signed-off-by: L. Alberto Giménez <agimenez@sysvalve.es>
---
 drivers/net/usb/Kconfig  |   12 +
 drivers/net/usb/Makefile |    1 +
 drivers/net/usb/ipheth.c |  573 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 586 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/usb/ipheth.c

diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index ba56ce4..63be4ca 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -385,4 +385,16 @@ config USB_CDC_PHONET
 	  cellular modem, as found on most Nokia handsets with the
 	  "PC suite" USB profile.
 
+config USB_IPHETH
+	tristate "Apple iPhone USB Ethernet driver"
+	default n
+	---help---
+	  Module used to share Internet connection (tethering) from your
+	  iPhone (Original, 3G and 3GS) to your system.
+	  Note that you need userspace libraries and programs that are needed
+	  to pair your device with your system and that understand the iPhone
+	  protocol.
+
+	  For more information: http://giagio.com/wiki/moin.cgi/iPhoneEthernetDriver
+
 endmenu
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 82ea629..edb09c0 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -23,4 +23,5 @@ obj-$(CONFIG_USB_NET_MCS7830)	+= mcs7830.o
 obj-$(CONFIG_USB_USBNET)	+= usbnet.o
 obj-$(CONFIG_USB_NET_INT51X1)	+= int51x1.o
 obj-$(CONFIG_USB_CDC_PHONET)	+= cdc-phonet.o
+obj-$(CONFIG_USB_IPHETH)	+= ipheth.o
 
diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c
new file mode 100644
index 0000000..3f93da1
--- /dev/null
+++ b/drivers/net/usb/ipheth.c
@@ -0,0 +1,573 @@
+/*
+ * 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/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+
+#define USB_VENDOR_APPLE        0x05ac
+#define USB_PRODUCT_IPHETH     0x1290
+#define USB_PRODUCT_IPHETH_3G   0x1292
+#define USB_PRODUCT_IPHETH_3GS  0x1294
+
+#define IPHETH_USBINTF_CLASS    255
+#define IPHETH_USBINTF_SUBCLASS 253
+
+#define IPHETH_BUF_SIZE         1516
+#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_CARRIER_CHECK 0x45
+
+#define IPHETH_CARRIER_CHECK_TIMEOUT (1 * HZ)
+#define IPHETH_CARRIER_ON       0x04
+
+static struct usb_device_id ipheth_table[] = {
+	{ USB_DEVICE(USB_VENDOR_APPLE, USB_PRODUCT_IPHETH) },
+	{ USB_DEVICE(USB_VENDOR_APPLE, USB_PRODUCT_IPHETH_3G) },
+	{ USB_DEVICE(USB_VENDOR_APPLE, USB_PRODUCT_IPHETH_3GS) },
+	{ }
+};
+MODULE_DEVICE_TABLE(usb, ipheth_table);
+
+struct ipheth_device {
+	struct usb_device *udev;
+	struct usb_interface *intf;
+	struct net_device *net;
+	struct net_device_stats stats;
+	struct sk_buff *tx_skb;
+	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;
+};
+
+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;
+
+	rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (rx_urb == NULL)
+		goto error;
+
+	tx_buf = usb_buffer_alloc(iphone->udev,
+				  IPHETH_BUF_SIZE,
+				  GFP_KERNEL,
+				  &tx_urb->transfer_dma);
+	if (tx_buf == NULL)
+		goto error;
+
+	rx_buf = usb_buffer_alloc(iphone->udev,
+				  IPHETH_BUF_SIZE,
+				  GFP_KERNEL,
+				  &rx_urb->transfer_dma);
+	if (rx_buf == NULL)
+		goto error;
+
+
+	iphone->tx_urb = tx_urb;
+	iphone->rx_urb = rx_urb;
+	iphone->tx_buf = tx_buf;
+	iphone->rx_buf = rx_buf;
+	return 0;
+
+error:
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, rx_buf,
+			rx_urb->transfer_dma);
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, tx_buf,
+			tx_urb->transfer_dma);
+	usb_free_urb(rx_urb);
+	usb_free_urb(tx_urb);
+	return -ENOMEM;
+}
+
+static void ipheth_free_urbs(struct ipheth_device *iphone)
+{
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, iphone->rx_buf,
+			iphone->rx_urb->transfer_dma);
+	usb_buffer_free(iphone->udev, IPHETH_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_unlink_urbs(struct ipheth_device *dev)
+{
+	usb_kill_urb(dev->tx_urb);
+	usb_kill_urb(dev->rx_urb);
+}
+
+static void ipheth_rcvbulk_callback(struct urb *urb)
+{
+	struct ipheth_device *dev;
+	struct sk_buff *skb;
+	int status;
+	char *buf;
+	int len;
+
+	dev = urb->context;
+	if (dev == NULL)
+		return;
+
+	status = urb->status;
+	switch (status) {
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		return;
+	case 0:
+		break;
+	default:
+		err("%s: urb status: %d", __func__, urb->status);
+		return;
+	}
+
+	len = urb->actual_length;
+	buf = urb->transfer_buffer;
+
+	skb = dev_alloc_skb(NET_IP_ALIGN + len);
+	if (!skb) {
+		err("%s: dev_alloc_skb: -ENOMEM", __func__);
+		dev->stats.rx_dropped++;
+		return;
+	}
+
+	skb_reserve(skb, NET_IP_ALIGN);
+	memcpy(skb_put(skb, len), buf + NET_IP_ALIGN, len - NET_IP_ALIGN);
+	skb->dev = dev->net;
+	skb->protocol = eth_type_trans(skb, dev->net);
+
+	dev->stats.rx_packets++;
+	dev->stats.rx_bytes += len;
+
+	netif_rx(skb);
+	ipheth_rx_submit(dev, GFP_ATOMIC);
+}
+
+static void ipheth_sndbulk_callback(struct urb *urb)
+{
+	struct ipheth_device *dev;
+
+	dev = urb->context;
+	if (dev == NULL)
+		return;
+
+	if (urb->status != 0 &&
+	    urb->status != -ENOENT &&
+	    urb->status != -ECONNRESET &&
+	    urb->status != -ESHUTDOWN)
+		err("%s: urb status: %d", __func__, urb->status);
+
+	netif_wake_queue(dev->net);
+	dev_kfree_skb_irq(dev->tx_skb);
+}
+
+static int ipheth_carrier_set(struct ipheth_device *dev)
+{
+	struct usb_device *udev = dev->udev;
+	int retval;
+
+	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) {
+		err("%s: usb_control_msg: %d", __func__, retval);
+		return retval;
+	}
+
+	if (dev->ctrl_buf[0] == IPHETH_CARRIER_ON)
+		netif_carrier_on(dev->net);
+	else
+		netif_carrier_off(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) {
+		err("%s: usb_control_msg: %d", __func__, retval);
+	} else if (retval < ETH_ALEN) {
+		err("%s: usb_control_msg: short packet: %d bytes",
+						__func__, retval);
+		retval = -EINVAL;
+	} else {
+		memcpy(net->dev_addr, dev->ctrl_buf, ETH_ALEN);
+		retval = 0;
+	}
+
+	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, IPHETH_BUF_SIZE,
+			  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)
+		err("%s: usb_submit_urb: %d", __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);
+	usb_clear_halt(udev, usb_rcvbulkpipe(udev, dev->bulk_in));
+	usb_clear_halt(udev, usb_sndbulkpipe(udev, dev->bulk_out));
+
+	retval = ipheth_carrier_set(dev);
+	if (retval)
+		goto error;
+
+	retval = ipheth_rx_submit(dev, GFP_KERNEL);
+	if (retval)
+		goto error;
+
+	schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT);
+	netif_start_queue(net);
+error:
+	return retval;
+}
+
+static int ipheth_close(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+
+	cancel_delayed_work_sync(&dev->carrier_work);
+	netif_stop_queue(net);
+	return 0;
+}
+
+static int ipheth_tx(struct sk_buff *skb, struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	struct usb_device *udev = dev->udev;
+	int retval;
+
+	/* Paranoid */
+	if (skb->len > IPHETH_BUF_SIZE) {
+		err("%s: skb too large: %d bytes", __func__, skb->len);
+		dev->stats.tx_dropped++;
+		dev_kfree_skb_irq(skb);
+		goto exit;
+	}
+
+	memset(dev->tx_buf, 0, IPHETH_BUF_SIZE);
+	memcpy(dev->tx_buf, skb->data, skb->len);
+
+	usb_fill_bulk_urb(dev->tx_urb, udev,
+			  usb_sndbulkpipe(udev, dev->bulk_out),
+			  dev->tx_buf, IPHETH_BUF_SIZE,
+			  ipheth_sndbulk_callback,
+			  dev);
+	dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC);
+	if (retval) {
+		err("%s: usb_submit_urb: %d", __func__, retval);
+		dev->stats.tx_errors++;
+		dev_kfree_skb_irq(skb);
+	} else {
+		net->trans_start = jiffies;
+		dev->tx_skb = skb;
+
+		dev->stats.tx_packets++;
+		dev->stats.tx_bytes += skb->len;
+		netif_stop_queue(net);
+	}
+exit:
+	return NETDEV_TX_OK;
+}
+
+static void ipheth_tx_timeout(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+
+	err("%s: TX timeout", __func__);
+	dev->stats.tx_errors++;
+	usb_unlink_urb(dev->tx_urb);
+}
+
+static struct net_device_stats *ipheth_stats(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	return &dev->stats;
+}
+
+static u32 ipheth_ethtool_op_get_link(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	return netif_carrier_ok(dev->net);
+}
+
+static struct ethtool_ops ops = {
+	.get_link = ipheth_ethtool_op_get_link
+};
+
+#ifdef HAVE_NET_DEVICE_OPS
+static const struct net_device_ops ipheth_netdev_ops = {
+       .ndo_open = &ipheth_open,
+       .ndo_stop = &ipheth_close,
+       .ndo_start_xmit = &ipheth_tx,
+       .ndo_tx_timeout = &ipheth_tx_timeout,
+       .ndo_get_stats = &ipheth_stats,
+};
+#endif
+
+static int ipheth_probe(struct usb_interface *intf,
+			 const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct usb_host_interface *hintf;
+	struct usb_endpoint_descriptor *endp;
+	struct ipheth_device *dev;
+	struct net_device *netdev;
+	int i;
+	int retval;
+
+	/* Ensure we are probing the right interface */
+	if (intf->cur_altsetting->desc.bInterfaceClass != IPHETH_USBINTF_CLASS ||
+	    intf->cur_altsetting->desc.bInterfaceSubClass != IPHETH_USBINTF_SUBCLASS)
+		return -ENODEV;
+
+	netdev = alloc_etherdev(sizeof(struct ipheth_device));
+	if (!netdev)
+		return -ENOMEM;
+
+#ifdef HAVE_NET_DEVICE_OPS
+	netdev->netdev_ops = &ipheth_netdev_ops;
+#else /* CONFIG_COMPAT_NET_DEV_OPS */
+	netdev->open = &ipheth_open;
+	netdev->stop = &ipheth_close;
+	netdev->hard_start_xmit = &ipheth_tx;
+	netdev->tx_timeout = &ipheth_tx_timeout;
+	netdev->get_stats = &ipheth_stats;
+#endif
+	netdev->watchdog_timeo = IPHETH_TX_TIMEOUT;
+
+	dev = netdev_priv(netdev);
+	dev->udev = udev;
+	dev->net = netdev;
+	dev->intf = intf;
+
+	/* Set up endpoints */
+	hintf = usb_altnum_to_altsetting(intf, IPHETH_ALT_INTFNUM);
+	if (hintf == NULL) {
+		retval = -ENODEV;
+		err("Unable to find alternate settings interface");
+		goto err_endpoints;
+	}
+
+	for (i = 0; i < hintf->desc.bNumEndpoints; i++) {
+		endp = &hintf->endpoint[i].desc;
+		if (usb_endpoint_is_bulk_in(endp))
+			dev->bulk_in = endp->bEndpointAddress;
+		else if (usb_endpoint_is_bulk_out(endp))
+			dev->bulk_out = endp->bEndpointAddress;
+	}
+	if (!(dev->bulk_in && dev->bulk_out)) {
+		retval = -ENODEV;
+		err("Unable to find endpoints");
+		goto err_endpoints;
+	}
+
+	dev->ctrl_buf = kmalloc(IPHETH_CTRL_BUF_SIZE, GFP_KERNEL);
+	if (dev->ctrl_buf == NULL) {
+		retval = -ENOMEM;
+		goto err_alloc_ctrl_buf;
+	}
+
+	retval = ipheth_get_macaddr(dev);
+	if (retval)
+		goto err_get_macaddr;
+
+	INIT_DELAYED_WORK(&dev->carrier_work, ipheth_carrier_check_work);
+
+	retval = ipheth_alloc_urbs(dev);
+	if (retval) {
+		err("error allocating urbs: %d", retval);
+		goto err_alloc_urbs;
+	}
+
+	usb_set_intfdata(intf, dev);
+
+	SET_NETDEV_DEV(netdev, &intf->dev);
+	SET_ETHTOOL_OPS(netdev, &ops);
+
+	retval = register_netdev(netdev);
+	if (retval) {
+		err("error registering netdev: %d", retval);
+		retval = -EIO;
+		goto err_register_netdev;
+	}
+
+	dev_info(&intf->dev, "Apple iPhone USB Ethernet device attached\n");
+	return 0;
+
+err_register_netdev:
+	ipheth_free_urbs(dev);
+err_alloc_urbs:
+err_get_macaddr:
+err_alloc_ctrl_buf:
+	kfree(dev->ctrl_buf);
+err_endpoints:
+	free_netdev(netdev);
+	return retval;
+}
+
+static void ipheth_disconnect(struct usb_interface *intf)
+{
+	struct ipheth_device *dev;
+
+	dev = usb_get_intfdata(intf);
+	if (dev != NULL) {
+		unregister_netdev(dev->net);
+		ipheth_unlink_urbs(dev);
+		ipheth_free_urbs(dev);
+		kfree(dev->ctrl_buf);
+		free_netdev(dev->net);
+	}
+	usb_set_intfdata(intf, NULL);
+	dev_info(&intf->dev, "Apple iPhone USB Ethernet now disconnected\n");
+}
+
+static struct usb_driver ipheth_driver = {
+	.name =		"ipheth",
+	.probe =	ipheth_probe,
+	.disconnect =	ipheth_disconnect,
+	.id_table =	ipheth_table,
+	.supports_autosuspend = 0,
+};
+
+static int __init ipheth_init(void)
+{
+	int retval;
+
+	retval = usb_register(&ipheth_driver);
+	if (retval) {
+		err("usb_register failed: %d", retval);
+		return retval;
+	}
+	return 0;
+}
+
+static void __exit ipheth_exit(void)
+{
+	usb_deregister(&ipheth_driver);
+}
+
+module_init(ipheth_init);
+module_exit(ipheth_exit);
+
+MODULE_AUTHOR("Diego Giagio <diego@giagio.com>");
+MODULE_DESCRIPTION("Apple iPhone USB Ethernet driver");
+MODULE_LICENSE("Dual BSD/GPL");
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH] Staging: Add new driver ipheth
  2010-03-31 14:33       ` Pavel Machek
  2010-03-31 14:44         ` Matthew Garrett
@ 2010-03-31 19:47         ` "L. Alberto Giménez"
  1 sibling, 0 replies; 34+ messages in thread
From: "L. Alberto Giménez" @ 2010-03-31 19:47 UTC (permalink / raw)
  To: Pavel Machek; +Cc: Greg KH, bzolnier, linux-kernel, devel, alan

On 03/31/2010 04:33 PM, Pavel Machek wrote:
> On Tue 2010-03-30 23:58:22, "L. Alberto Gim?nez" wrote:
>> On 03/30/2010 11:45 PM, Greg KH wrote:
>>> On Tue, Mar 30, 2010 at 11:34:24PM +0200, L. Alberto Gim??nez wrote:
>>>> Add new driver to use tethering with an iPhone device to the staging tree.
> 
> Does it work out of the box, or is it some kind of driver for
> jailbroken iphones?

Hi Pavel, it works out of the box. You need some userspace tools used
also to access jailbroken devices, but this one does not need
specifically that your iPhone is jailbroken (mine is not, and it works
perfectly).

Regards,
-- 
L. Alberto Giménez
JabberID agimenez@jabber.sysvalve.es
GnuPG key ID 0x3BAABDE1

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCHv3] drivers/net/usb: Add new driver ipheth
  2010-03-31 19:42 ` [PATCHv3] " L. Alberto Giménez
@ 2010-03-31 20:33   ` Oliver Neukum
  2010-03-31 21:38     ` "L. Alberto Giménez"
  2010-04-02 18:23     ` "L. Alberto Giménez"
  2010-03-31 23:18   ` Ben Hutchings
  1 sibling, 2 replies; 34+ messages in thread
From: Oliver Neukum @ 2010-03-31 20:33 UTC (permalink / raw)
  To: L. Alberto Giménez
  Cc: linux-kernel, netdev, linux-usb, linville, j.dumon,
	steve.glendinning, davem, gregkh, dgiagio, dborca

Am Mittwoch, 31. März 2010 21:42:07 schrieb L. Alberto Giménez:

Hi,

a few comments below.

	Regards
		Oliver

> +
> +static struct usb_device_id ipheth_table[] = {
> +	{ USB_DEVICE(USB_VENDOR_APPLE, USB_PRODUCT_IPHETH) },
> +	{ USB_DEVICE(USB_VENDOR_APPLE, USB_PRODUCT_IPHETH_3G) },
> +	{ USB_DEVICE(USB_VENDOR_APPLE, USB_PRODUCT_IPHETH_3GS) },

Please use the macros specifying the interface.

> +	{ }
> +};
> +MODULE_DEVICE_TABLE(usb, ipheth_table);


> +static void ipheth_unlink_urbs(struct ipheth_device *dev)

The naming might be misleading.

> +{
> +	usb_kill_urb(dev->tx_urb);
> +	usb_kill_urb(dev->rx_urb);
> +}

> +
> +static void ipheth_sndbulk_callback(struct urb *urb)
> +{
> +	struct ipheth_device *dev;
> +
> +	dev = urb->context;
> +	if (dev == NULL)
> +		return;
> +
> +	if (urb->status != 0 &&
> +	    urb->status != -ENOENT &&
> +	    urb->status != -ECONNRESET &&
> +	    urb->status != -ESHUTDOWN)
> +		err("%s: urb status: %d", __func__, urb->status);
> +
> +	netif_wake_queue(dev->net);
> +	dev_kfree_skb_irq(dev->tx_skb);

Race condition. You must free the skb before you wake the queue.

> +}

> +
> +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);
> +	usb_clear_halt(udev, usb_rcvbulkpipe(udev, dev->bulk_in));
> +	usb_clear_halt(udev, usb_sndbulkpipe(udev, dev->bulk_out));

Is this really needed? If so, please add a comment.

> +
> +	retval = ipheth_carrier_set(dev);
> +	if (retval)
> +		goto error;
> +
> +	retval = ipheth_rx_submit(dev, GFP_KERNEL);
> +	if (retval)
> +		goto error;
> +
> +	schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT);

Does it make sense to start rx while you have no carrier?

> +	netif_start_queue(net);
> +error:
> +	return retval;
> +}

> +static int ipheth_tx(struct sk_buff *skb, struct net_device *net)
> +{
> +	struct ipheth_device *dev = netdev_priv(net);
> +	struct usb_device *udev = dev->udev;
> +	int retval;
> +
> +	/* Paranoid */
> +	if (skb->len > IPHETH_BUF_SIZE) {
> +		err("%s: skb too large: %d bytes", __func__, skb->len);
> +		dev->stats.tx_dropped++;
> +		dev_kfree_skb_irq(skb);
> +		goto exit;
> +	}
> +
> +	memset(dev->tx_buf, 0, IPHETH_BUF_SIZE);
> +	memcpy(dev->tx_buf, skb->data, skb->len);

a bit wasteful

> +static int ipheth_probe(struct usb_interface *intf,
> +			 const struct usb_device_id *id)
> +{
> +	struct usb_device *udev = interface_to_usbdev(intf);
> +	struct usb_host_interface *hintf;
> +	struct usb_endpoint_descriptor *endp;
> +	struct ipheth_device *dev;
> +	struct net_device *netdev;
> +	int i;
> +	int retval;
> +
> +	/* Ensure we are probing the right interface */
> +	if (intf->cur_altsetting->desc.bInterfaceClass != IPHETH_USBINTF_CLASS ||
> +	    intf->cur_altsetting->desc.bInterfaceSubClass != IPHETH_USBINTF_SUBCLASS)
> +		return -ENODEV;

Please use the correct macro to specify the interface you are interested in

> +static void ipheth_disconnect(struct usb_interface *intf)
> +{
> +	struct ipheth_device *dev;
> +
> +	dev = usb_get_intfdata(intf);
> +	if (dev != NULL) {

is this check needed?

> +static struct usb_driver ipheth_driver = {
> +	.name =		"ipheth",
> +	.probe =	ipheth_probe,
> +	.disconnect =	ipheth_disconnect,
> +	.id_table =	ipheth_table,
> +	.supports_autosuspend = 0,

redundant

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCHv3] drivers/net/usb: Add new driver ipheth
  2010-03-31 20:33   ` Oliver Neukum
@ 2010-03-31 21:38     ` "L. Alberto Giménez"
  2010-04-02 18:23     ` "L. Alberto Giménez"
  1 sibling, 0 replies; 34+ messages in thread
From: "L. Alberto Giménez" @ 2010-03-31 21:38 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: linux-kernel, netdev, linux-usb, linville, j.dumon,
	steve.glendinning, davem, gregkh, dgiagio, dborca

On 03/31/2010 10:33 PM, Oliver Neukum wrote:
> Am Mittwoch, 31. März 2010 21:42:07 schrieb L. Alberto Giménez:
> 
> Hi,
> 
> a few comments below.

Hi Oliver,

I'll try to fix all the problems that you spotted as soon as I know how
to do that (documentation pointers more than the obvious ones are
welcome). In the meantime, please be patient :)

Regards,
-- 
L. Alberto Giménez
JabberID agimenez@jabber.sysvalve.es
GnuPG key ID 0x3BAABDE1

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCHv3] drivers/net/usb: Add new driver ipheth
  2010-03-31 19:42 ` [PATCHv3] " L. Alberto Giménez
  2010-03-31 20:33   ` Oliver Neukum
@ 2010-03-31 23:18   ` Ben Hutchings
  2010-03-31 23:25     ` Greg KH
  2010-04-02 17:15     ` "L. Alberto Giménez"
  1 sibling, 2 replies; 34+ messages in thread
From: Ben Hutchings @ 2010-03-31 23:18 UTC (permalink / raw)
  To: L. Alberto Giménez
  Cc: linux-kernel, netdev, linux-usb, oliver, linville, j.dumon,
	steve.glendinning, davem, gregkh, dgiagio, dborca

On Wed, 2010-03-31 at 21:42 +0200, L. Alberto Giménez wrote:
[...]
> --- /dev/null
> +++ b/drivers/net/usb/ipheth.c
[...]
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/ethtool.h>
> +#include <linux/uaccess.h>

I don't think you need this header.

> +#include <linux/usb.h>
> +#include <linux/workqueue.h>
> +
> +#define USB_VENDOR_APPLE        0x05ac
> +#define USB_PRODUCT_IPHETH     0x1290
> +#define USB_PRODUCT_IPHETH_3G   0x1292
> +#define USB_PRODUCT_IPHETH_3GS  0x1294

Apple doesn't assign device ids to ipheth so either the names are
incorrect or you should get proper device ids.  I believe the Linux
Foundation has a USB vendor id and could assign device ids under that.

> +struct ipheth_device {
> +	struct usb_device *udev;
> +	struct usb_interface *intf;
> +	struct net_device *net;
> +	struct net_device_stats stats;

You can store stats in the net_device.

> +	struct sk_buff *tx_skb;
> +	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;

No need for the double-underscores; that's a convention for use in
definitions shared with user-space.

> +	struct delayed_work carrier_work;
> +};
[...]
> +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);
> +	usb_clear_halt(udev, usb_rcvbulkpipe(udev, dev->bulk_in));
> +	usb_clear_halt(udev, usb_sndbulkpipe(udev, dev->bulk_out));
> +
> +	retval = ipheth_carrier_set(dev);
> +	if (retval)
> +		goto error;
> +
> +	retval = ipheth_rx_submit(dev, GFP_KERNEL);
> +	if (retval)
> +		goto error;
> +
> +	schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT);
> +	netif_start_queue(net);
> +error:
> +	return retval;
> +}

There is no cleanup here (and none appears to be needed) so you could
take out the label and replace the gotos with returns.

> +static int ipheth_tx(struct sk_buff *skb, struct net_device *net)
> +{
> +	struct ipheth_device *dev = netdev_priv(net);
> +	struct usb_device *udev = dev->udev;
> +	int retval;
> +
> +	/* Paranoid */
> +	if (skb->len > IPHETH_BUF_SIZE) {
> +		err("%s: skb too large: %d bytes", __func__, skb->len);
> +		dev->stats.tx_dropped++;
> +		dev_kfree_skb_irq(skb);
> +		goto exit;
> +	}

Use WARN here as this would indicate a bug.

Again the goto is unnecessary.

> +	memset(dev->tx_buf, 0, IPHETH_BUF_SIZE);
> +	memcpy(dev->tx_buf, skb->data, skb->len);

This is wasteful.  If you really must pad the buffer then do so, but
don't clear the area that is going to be filled with real data.

> +	usb_fill_bulk_urb(dev->tx_urb, udev,
> +			  usb_sndbulkpipe(udev, dev->bulk_out),
> +			  dev->tx_buf, IPHETH_BUF_SIZE,
> +			  ipheth_sndbulk_callback,
> +			  dev);
> +	dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> +
> +	retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC);
> +	if (retval) {
> +		err("%s: usb_submit_urb: %d", __func__, retval);
> +		dev->stats.tx_errors++;
> +		dev_kfree_skb_irq(skb);
> +	} else {
> +		net->trans_start = jiffies;

No longer needed.

> +		dev->tx_skb = skb;
> +
> +		dev->stats.tx_packets++;
> +		dev->stats.tx_bytes += skb->len;
> +		netif_stop_queue(net);
> +	}
> +exit:
> +	return NETDEV_TX_OK;
> +}
[...]
> +#ifdef HAVE_NET_DEVICE_OPS
> +static const struct net_device_ops ipheth_netdev_ops = {
> +       .ndo_open = &ipheth_open,
> +       .ndo_stop = &ipheth_close,
> +       .ndo_start_xmit = &ipheth_tx,
> +       .ndo_tx_timeout = &ipheth_tx_timeout,
> +       .ndo_get_stats = &ipheth_stats,
> +};
> +#endif

Remove the #ifdef, there is no question whether we have net_device_ops.

> +static int ipheth_probe(struct usb_interface *intf,
> +			 const struct usb_device_id *id)
> +{
[...]
> +#ifdef HAVE_NET_DEVICE_OPS
> +	netdev->netdev_ops = &ipheth_netdev_ops;
> +#else /* CONFIG_COMPAT_NET_DEV_OPS */
> +	netdev->open = &ipheth_open;
> +	netdev->stop = &ipheth_close;
> +	netdev->hard_start_xmit = &ipheth_tx;
> +	netdev->tx_timeout = &ipheth_tx_timeout;
> +	netdev->get_stats = &ipheth_stats;
> +#endif
[...]

Similarly here.

As a general point, you should now be using netdev_err(), netdev_info(),
etc. for logging messages related to net devices.

I have no idea about USB so I haven't checked the USB API usage at all.

Ben.

-- 
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCHv3] drivers/net/usb: Add new driver ipheth
  2010-03-31 23:18   ` Ben Hutchings
@ 2010-03-31 23:25     ` Greg KH
  2010-03-31 23:28       ` Ben Hutchings
  2010-04-02 17:15     ` "L. Alberto Giménez"
  1 sibling, 1 reply; 34+ messages in thread
From: Greg KH @ 2010-03-31 23:25 UTC (permalink / raw)
  To: Ben Hutchings
  Cc: L. Alberto Gim?nez, linux-kernel, netdev, linux-usb, oliver,
	linville, j.dumon, steve.glendinning, davem, dgiagio, dborca

On Thu, Apr 01, 2010 at 12:18:58AM +0100, Ben Hutchings wrote:
> On Wed, 2010-03-31 at 21:42 +0200, L. Alberto Gim??nez wrote:
> [...]
> > --- /dev/null
> > +++ b/drivers/net/usb/ipheth.c
> [...]
> > +#include <linux/kernel.h>
> > +#include <linux/errno.h>
> > +#include <linux/init.h>
> > +#include <linux/slab.h>
> > +#include <linux/module.h>
> > +#include <linux/netdevice.h>
> > +#include <linux/etherdevice.h>
> > +#include <linux/ethtool.h>
> > +#include <linux/uaccess.h>
> 
> I don't think you need this header.
> 
> > +#include <linux/usb.h>
> > +#include <linux/workqueue.h>
> > +
> > +#define USB_VENDOR_APPLE        0x05ac
> > +#define USB_PRODUCT_IPHETH     0x1290
> > +#define USB_PRODUCT_IPHETH_3G   0x1292
> > +#define USB_PRODUCT_IPHETH_3GS  0x1294
> 
> Apple doesn't assign device ids to ipheth so either the names are
> incorrect or you should get proper device ids.  I believe the Linux
> Foundation has a USB vendor id and could assign device ids under that.

You mean there is an application on the iphone that sets the device id
here?  I thought this was the iphone device ids already assigned to
apple.

If we need a linux-specific device id, I can assign them from the Linux
foundations pool of device ids, but so far I've only done that for
devices that either run Linux (usb gadgets), or for the usb root hubs.
But hey, if it's controlable from software, let me know and I'll dole
one out.  In that case, why would you need 3 different device ids?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCHv3] drivers/net/usb: Add new driver ipheth
  2010-03-31 23:25     ` Greg KH
@ 2010-03-31 23:28       ` Ben Hutchings
  0 siblings, 0 replies; 34+ messages in thread
From: Ben Hutchings @ 2010-03-31 23:28 UTC (permalink / raw)
  To: Greg KH
  Cc: L. Alberto Gim?nez, linux-kernel, netdev, linux-usb, oliver,
	linville, j.dumon, steve.glendinning, davem, dgiagio, dborca

On Wed, 2010-03-31 at 16:25 -0700, Greg KH wrote:
> On Thu, Apr 01, 2010 at 12:18:58AM +0100, Ben Hutchings wrote:
> > On Wed, 2010-03-31 at 21:42 +0200, L. Alberto Gim??nez wrote:
[...]
> > > +#include <linux/usb.h>
> > > +#include <linux/workqueue.h>
> > > +
> > > +#define USB_VENDOR_APPLE        0x05ac
> > > +#define USB_PRODUCT_IPHETH     0x1290
> > > +#define USB_PRODUCT_IPHETH_3G   0x1292
> > > +#define USB_PRODUCT_IPHETH_3GS  0x1294
> > 
> > Apple doesn't assign device ids to ipheth so either the names are
> > incorrect or you should get proper device ids.  I believe the Linux
> > Foundation has a USB vendor id and could assign device ids under that.
> 
> You mean there is an application on the iphone that sets the device id
> here?  I thought this was the iphone device ids already assigned to
> apple.
[...]

That's why my first suggestion was that the names are incorrect; they
should probably include IPHONE not IPHETH.

Ben.

-- 
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCHv3] drivers/net/usb: Add new driver ipheth
  2010-03-31 23:18   ` Ben Hutchings
  2010-03-31 23:25     ` Greg KH
@ 2010-04-02 17:15     ` "L. Alberto Giménez"
  2010-04-02 17:21       ` Ben Hutchings
  1 sibling, 1 reply; 34+ messages in thread
From: "L. Alberto Giménez" @ 2010-04-02 17:15 UTC (permalink / raw)
  To: Ben Hutchings
  Cc: linux-kernel, netdev, linux-usb, oliver, linville, j.dumon,
	steve.glendinning, davem, gregkh, dgiagio, dborca

On 04/01/2010 01:18 AM, Ben Hutchings wrote:
> On Wed, 2010-03-31 at 21:42 +0200, L. Alberto Giménez wrote:
> [...]
>> --- /dev/null
>> +++ b/drivers/net/usb/ipheth.c
> [...]

Hi Ben,

Upstream has fixed several errors pointed out by you and Oliver (thanks
for that), but some of them are still pending.

I will send patches on top of my last driver submission (if the proper
way would be resubmit the whole code, please tell me. Anyway I need to
clarify some doubts...

>> +	usb_fill_bulk_urb(dev->tx_urb, udev,
>> +			  usb_sndbulkpipe(udev, dev->bulk_out),
>> +			  dev->tx_buf, IPHETH_BUF_SIZE,
>> +			  ipheth_sndbulk_callback,
>> +			  dev);
>> +	dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
>> +
>> +	retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC);
>> +	if (retval) {
>> +		err("%s: usb_submit_urb: %d", __func__, retval);
>> +		dev->stats.tx_errors++;
>> +		dev_kfree_skb_irq(skb);
>> +	} else {
>> +		net->trans_start = jiffies;
> 
> No longer needed.

What is not longer needed? The assignment, the whole "else" branch? If
the assignment is what is not needed, can I just remove that line, right?

> [...]
>> +#ifdef HAVE_NET_DEVICE_OPS
>> +static const struct net_device_ops ipheth_netdev_ops = {
>> +       .ndo_open = &ipheth_open,
>> +       .ndo_stop = &ipheth_close,
>> +       .ndo_start_xmit = &ipheth_tx,
>> +       .ndo_tx_timeout = &ipheth_tx_timeout,
>> +       .ndo_get_stats = &ipheth_stats,
>> +};
>> +#endif
> 
> Remove the #ifdef, there is no question whether we have net_device_ops.

Ok, I will just remove both #ifdefs, but why is that? Maybe in previous
versions of the kernel the net_device_ops struct was introduced and now
it's present no matter how you configure your kernel?

> I have no idea about USB so I haven't checked the USB API usage at all.

I think that Greg is the maintainer for the USB subsystem, so if he has
no further commets, I will try to submit fixes for both your and
Oliver's comments along with the upstream developers.

Thanks for your comments.


Best regards,
-- 
L. Alberto Giménez
JabberID agimenez@jabber.sysvalve.es
GnuPG key ID 0x3BAABDE1

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCHv3] drivers/net/usb: Add new driver ipheth
  2010-04-02 17:15     ` "L. Alberto Giménez"
@ 2010-04-02 17:21       ` Ben Hutchings
  2010-04-02 17:53         ` "L. Alberto Giménez"
  0 siblings, 1 reply; 34+ messages in thread
From: Ben Hutchings @ 2010-04-02 17:21 UTC (permalink / raw)
  To: "L. Alberto Giménez"
  Cc: linux-kernel, netdev, linux-usb, oliver, linville, j.dumon,
	steve.glendinning, davem, gregkh, dgiagio, dborca

On Fri, 2010-04-02 at 19:15 +0200, "L. Alberto Giménez" wrote:
> On 04/01/2010 01:18 AM, Ben Hutchings wrote:
> > On Wed, 2010-03-31 at 21:42 +0200, L. Alberto Giménez wrote:
> > [...]
> >> --- /dev/null
> >> +++ b/drivers/net/usb/ipheth.c
> > [...]
> 
> Hi Ben,
> 
> Upstream has fixed several errors pointed out by you and Oliver (thanks
> for that), but some of them are still pending.
> 
> I will send patches on top of my last driver submission (if the proper
> way would be resubmit the whole code, please tell me. Anyway I need to
> clarify some doubts...

Since David Miller has not merged your original patch, you should send a
single new patch with the changes incorporated.

> >> +	usb_fill_bulk_urb(dev->tx_urb, udev,
> >> +			  usb_sndbulkpipe(udev, dev->bulk_out),
> >> +			  dev->tx_buf, IPHETH_BUF_SIZE,
> >> +			  ipheth_sndbulk_callback,
> >> +			  dev);
> >> +	dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> >> +
> >> +	retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC);
> >> +	if (retval) {
> >> +		err("%s: usb_submit_urb: %d", __func__, retval);
> >> +		dev->stats.tx_errors++;
> >> +		dev_kfree_skb_irq(skb);
> >> +	} else {
> >> +		net->trans_start = jiffies;
> > 
> > No longer needed.
> 
> What is not longer needed? The assignment, the whole "else" branch? If
> the assignment is what is not needed, can I just remove that line, right?

The assignment is not needed.

> > [...]
> >> +#ifdef HAVE_NET_DEVICE_OPS
> >> +static const struct net_device_ops ipheth_netdev_ops = {
> >> +       .ndo_open = &ipheth_open,
> >> +       .ndo_stop = &ipheth_close,
> >> +       .ndo_start_xmit = &ipheth_tx,
> >> +       .ndo_tx_timeout = &ipheth_tx_timeout,
> >> +       .ndo_get_stats = &ipheth_stats,
> >> +};
> >> +#endif
> > 
> > Remove the #ifdef, there is no question whether we have net_device_ops.
> 
> Ok, I will just remove both #ifdefs, but why is that? Maybe in previous
> versions of the kernel the net_device_ops struct was introduced and now
> it's present no matter how you configure your kernel?
[...]

Correct, it is now mandatory.

Ben.

-- 
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCHv3] drivers/net/usb: Add new driver ipheth
  2010-04-02 17:21       ` Ben Hutchings
@ 2010-04-02 17:53         ` "L. Alberto Giménez"
  2010-04-02 18:35           ` Ben Hutchings
  0 siblings, 1 reply; 34+ messages in thread
From: "L. Alberto Giménez" @ 2010-04-02 17:53 UTC (permalink / raw)
  To: Ben Hutchings
  Cc: "L. Alberto Giménez",
	linux-kernel, netdev, linux-usb, oliver, linville, j.dumon,
	steve.glendinning, davem, gregkh, dgiagio, Daniel Borca

On 04/02/2010 07:21 PM, Ben Hutchings wrote:
> On Fri, 2010-04-02 at 19:15 +0200, "L. Alberto Giménez" wrote:
>> On 04/01/2010 01:18 AM, Ben Hutchings wrote:
>>> On Wed, 2010-03-31 at 21:42 +0200, L. Alberto Giménez wrote:
>>> [...]
>>>> --- /dev/null
>>>> +++ b/drivers/net/usb/ipheth.c
>>> [...]
>>>> +	usb_fill_bulk_urb(dev->tx_urb, udev,
>>>> +			  usb_sndbulkpipe(udev, dev->bulk_out),
>>>> +			  dev->tx_buf, IPHETH_BUF_SIZE,
>>>> +			  ipheth_sndbulk_callback,
>>>> +			  dev);
>>>> +	dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
>>>> +
>>>> +	retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC);
>>>> +	if (retval) {
>>>> +		err("%s: usb_submit_urb: %d", __func__, retval);
>>>> +		dev->stats.tx_errors++;
>>>> +		dev_kfree_skb_irq(skb);
>>>> +	} else {
>>>> +		net->trans_start = jiffies;
>>> No longer needed.
>> What is not longer needed? The assignment, the whole "else" branch? If
>> the assignment is what is not needed, can I just remove that line, right?
> 
> The assignment is not needed.

Hi,

I've been looking into this and it seems that the net_device.trans_start
field is now deprecated in favor of net_device.rx_queue.trans_start
-rx_queue is a struct netdev_queue- (file include/linux/netdevice.h), as
states the comment:

512         /*
513          * please use this field instead of dev->trans_start
514          */
515         unsigned long           trans_start;

Reading LDD3 book, it says that the driver is reponsible for updating
trans_start (as well as trans_rx, but we're not talking about that one).

So, I guess that the LDD book is outdated (again ;) ), but what I don't
understand at all is wether the driver should keep updating the right
field (dev->rx_queue.trans_start) or if the fact that it's inside a
"queue" implies that the code that is responsible for that queue would
update the trans_start field by itself?

Thanks in advance.

Best regards,
-- 
L. Alberto Giménez
JabberID agimenez@jabber.sysvalve.es
GnuPG key ID 0x3BAABDE1

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCHv3] drivers/net/usb: Add new driver ipheth
  2010-03-31 20:33   ` Oliver Neukum
  2010-03-31 21:38     ` "L. Alberto Giménez"
@ 2010-04-02 18:23     ` "L. Alberto Giménez"
  2010-04-04  7:24       ` Oliver Neukum
  1 sibling, 1 reply; 34+ messages in thread
From: "L. Alberto Giménez" @ 2010-04-02 18:23 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: linux-kernel, netdev, linux-usb, linville, j.dumon,
	steve.glendinning, davem, gregkh, dgiagio, dborca

On 03/31/2010 10:33 PM, Oliver Neukum wrote:
> Am Mittwoch, 31. März 2010 21:42:07 schrieb L. Alberto Giménez:

Hi Oliver,

Just like with Ben's comments I still have a couple of doubts about your
comments.


>> +
>> +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);
>> +	usb_clear_halt(udev, usb_rcvbulkpipe(udev, dev->bulk_in));
>> +	usb_clear_halt(udev, usb_sndbulkpipe(udev, dev->bulk_out));
> 
> Is this really needed? If so, please add a comment.

I understand that usb_clear_halt is only needed when the device has
transmitted data, and as it is "open" time, we can assume that no
transmissions ere made, so we don't need to clear anything (aka: remove
both lines), am I right?


>> +
>> +	retval = ipheth_carrier_set(dev);
>> +	if (retval)
>> +		goto error;
>> +
>> +	retval = ipheth_rx_submit(dev, GFP_KERNEL);
>> +	if (retval)
>> +		goto error;
>> +
>> +	schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT);
> 
> Does it make sense to start rx while you have no carrier?

Well, I have no clue about this one. I think that upstream developers
should take a look into this (Dario, Daniel, could you?) since I don't
have the knowledge to decide what to do about it.

But I assume that as with the previous one, we have just opened the
device and we aren't (yet) doing anything with it, so we shouldn't start rx?

>> +static void ipheth_disconnect(struct usb_interface *intf)
>> +{
>> +	struct ipheth_device *dev;
>> +
>> +	dev = usb_get_intfdata(intf);
>> +	if (dev != NULL) {
> 
> is this check needed?

Does usb_get_infdata always return not NULL? I haven't found anything
about it (just manual pages for the function, but can't spot if it
cannot return NULL). We disconnected the device, but I understand that
the kernel still has the information and the allocated memory, so the
cleanup code is still needed, isn't it?


> 
>> +static struct usb_driver ipheth_driver = {
>> +	.name =		"ipheth",
>> +	.probe =	ipheth_probe,
>> +	.disconnect =	ipheth_disconnect,
>> +	.id_table =	ipheth_table,
>> +	.supports_autosuspend = 0,
> 
> redundant

Why?

Thanks a lot in advance.


Best regards,
-- 
L. Alberto Giménez
JabberID agimenez@jabber.sysvalve.es
GnuPG key ID 0x3BAABDE1

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCHv3] drivers/net/usb: Add new driver ipheth
  2010-04-02 17:53         ` "L. Alberto Giménez"
@ 2010-04-02 18:35           ` Ben Hutchings
  0 siblings, 0 replies; 34+ messages in thread
From: Ben Hutchings @ 2010-04-02 18:35 UTC (permalink / raw)
  To: "L. Alberto Giménez"
  Cc: linux-kernel, netdev, linux-usb, oliver, linville, j.dumon,
	steve.glendinning, davem, gregkh, dgiagio, Daniel Borca

On Fri, 2010-04-02 at 19:53 +0200, "L. Alberto Giménez" wrote:
> On 04/02/2010 07:21 PM, Ben Hutchings wrote:
> > On Fri, 2010-04-02 at 19:15 +0200, "L. Alberto Giménez" wrote:
> >> On 04/01/2010 01:18 AM, Ben Hutchings wrote:
> >>> On Wed, 2010-03-31 at 21:42 +0200, L. Alberto Giménez wrote:
> >>> [...]
> >>>> --- /dev/null
> >>>> +++ b/drivers/net/usb/ipheth.c
> >>> [...]
> >>>> +	usb_fill_bulk_urb(dev->tx_urb, udev,
> >>>> +			  usb_sndbulkpipe(udev, dev->bulk_out),
> >>>> +			  dev->tx_buf, IPHETH_BUF_SIZE,
> >>>> +			  ipheth_sndbulk_callback,
> >>>> +			  dev);
> >>>> +	dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> >>>> +
> >>>> +	retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC);
> >>>> +	if (retval) {
> >>>> +		err("%s: usb_submit_urb: %d", __func__, retval);
> >>>> +		dev->stats.tx_errors++;
> >>>> +		dev_kfree_skb_irq(skb);
> >>>> +	} else {
> >>>> +		net->trans_start = jiffies;
> >>> No longer needed.
> >> What is not longer needed? The assignment, the whole "else" branch? If
> >> the assignment is what is not needed, can I just remove that line, right?
> > 
> > The assignment is not needed.
> 
> Hi,
> 
> I've been looking into this and it seems that the net_device.trans_start
> field is now deprecated in favor of net_device.rx_queue.trans_start
[...]

The networking core updates netdev_queue::trans_start by calling
txq_trans_update() where necessary.  Drivers should not touch either
net_device::trans_start or netdev_queue::trans_start any more.

Ben.

-- 
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCHv3] drivers/net/usb: Add new driver ipheth
  2010-04-02 18:23     ` "L. Alberto Giménez"
@ 2010-04-04  7:24       ` Oliver Neukum
  2010-04-05 18:51         ` "L. Alberto Giménez"
  0 siblings, 1 reply; 34+ messages in thread
From: Oliver Neukum @ 2010-04-04  7:24 UTC (permalink / raw)
  To: L. Alberto Giménez
  Cc: linux-kernel, netdev, linux-usb, linville, j.dumon,
	steve.glendinning, davem, gregkh, dgiagio, dborca

Am Freitag, 2. April 2010 20:23:21 schrieb L. Alberto Giménez:
> On 03/31/2010 10:33 PM, Oliver Neukum wrote:
> > Am Mittwoch, 31. März 2010 21:42:07 schrieb L. Alberto Giménez:
> 
> Hi Oliver,
> 
> Just like with Ben's comments I still have a couple of doubts about your
> comments.
> 
> 
> >> +
> >> +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);
> >> +	usb_clear_halt(udev, usb_rcvbulkpipe(udev, dev->bulk_in));
> >> +	usb_clear_halt(udev, usb_sndbulkpipe(udev, dev->bulk_out));
> > 
> > Is this really needed? If so, please add a comment.
> 
> I understand that usb_clear_halt is only needed when the device has
> transmitted data, and as it is "open" time, we can assume that no
> transmissions ere made, so we don't need to clear anything (aka: remove
> both lines), am I right?

Clearing a halt is necessary only when a device has stalled due to an
error condition. Unless the device is buggy and produces errors for
no good reason you don't need these lines.
 
> >> +
> >> +	retval = ipheth_carrier_set(dev);
> >> +	if (retval)
> >> +		goto error;
> >> +
> >> +	retval = ipheth_rx_submit(dev, GFP_KERNEL);
> >> +	if (retval)
> >> +		goto error;
> >> +
> >> +	schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT);
> > 
> > Does it make sense to start rx while you have no carrier?
> 
> Well, I have no clue about this one. I think that upstream developers
> should take a look into this (Dario, Daniel, could you?) since I don't
> have the knowledge to decide what to do about it.
> 
> But I assume that as with the previous one, we have just opened the
> device and we aren't (yet) doing anything with it, so we shouldn't start rx?

Your code as is is correct, I just wondered whether it could be made more
efficient.

> >> +static void ipheth_disconnect(struct usb_interface *intf)
> >> +{
> >> +	struct ipheth_device *dev;
> >> +
> >> +	dev = usb_get_intfdata(intf);
> >> +	if (dev != NULL) {
> > 
> > is this check needed?
> 
> Does usb_get_infdata always return not NULL? I haven't found anything

It returns what you gave it with  usb_set_intfdata().

> about it (just manual pages for the function, but can't spot if it
> cannot return NULL). We disconnected the device, but I understand that
> the kernel still has the information and the allocated memory, so the
> cleanup code is still needed, isn't it?

It is definitely needed.

> >> +static struct usb_driver ipheth_driver = {
> >> +	.name =		"ipheth",
> >> +	.probe =	ipheth_probe,
> >> +	.disconnect =	ipheth_disconnect,
> >> +	.id_table =	ipheth_table,
> >> +	.supports_autosuspend = 0,
> > 
> > redundant
> 
> Why?

0 is the default.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCHv3] drivers/net/usb: Add new driver ipheth
  2010-04-04  7:24       ` Oliver Neukum
@ 2010-04-05 18:51         ` "L. Alberto Giménez"
  0 siblings, 0 replies; 34+ messages in thread
From: "L. Alberto Giménez" @ 2010-04-05 18:51 UTC (permalink / raw)
  To: Oliver Neukum
  Cc: linux-kernel, netdev, linux-usb, linville, j.dumon,
	steve.glendinning, davem, gregkh, dgiagio, dborca

On 04/04/2010 09:24 AM, Oliver Neukum wrote:
> Am Freitag, 2. April 2010 20:23:21 schrieb L. Alberto Giménez:
>> On 03/31/2010 10:33 PM, Oliver Neukum wrote:
>>> Am Mittwoch, 31. März 2010 21:42:07 schrieb L. Alberto Giménez:
>>>> +static struct usb_driver ipheth_driver = {
>>>> +	.name =		"ipheth",
>>>> +	.probe =	ipheth_probe,
>>>> +	.disconnect =	ipheth_disconnect,
>>>> +	.id_table =	ipheth_table,
>>>> +	.supports_autosuspend = 0,
>>> redundant
>> Why?
> 
> 0 is the default.

Heh, I thought that you meant that the whole struct was redundant, and
that puzzled me a little bit. Now everything is clear (my fault for not
realizing the redundant initialization to 0).

Regards,
-- 
L. Alberto Giménez
JabberID agimenez@jabber.sysvalve.es
GnuPG key ID 0x3BAABDE1

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH Resubmission] drivers/net/usb: Add new driver ipheth
  2010-03-30 21:34 [PATCH] Staging: Add new driver ipheth L. Alberto Giménez
  2010-03-30 21:34 ` L. Alberto Giménez
  2010-03-31 19:42 ` [PATCHv3] " L. Alberto Giménez
@ 2010-04-07 22:11 ` L. Alberto Giménez
  2010-04-07 22:37   ` Roland Dreier
                     ` (2 more replies)
  2010-04-15 19:46 ` [PATCH Resubmission v2] " L. Alberto Giménez
  2010-04-18 18:35 ` [PATCH] " L. Alberto Giménez
  4 siblings, 3 replies; 34+ messages in thread
From: L. Alberto Giménez @ 2010-04-07 22:11 UTC (permalink / raw)
  To: linux-kernel
  Cc: dgiagio, dborca, Greg Kroah-Hartman, David S. Miller,
	Jonas Sjöquist, Steve Glendinning, Torgny Johansson,
	David Brownell, Omar Laazimani, L. Alberto Giménez,
	linux-usb, netdev

From: dborca@yahoo.com

Add new driver to use tethering with an iPhone device. After initial submission,
apply fixes to fit the new driver into the kernel standards.

There are still a couple of minor (almost cosmetic-level) issues, but the driver
is fully functional right now.

Signed-off-by: L. Alberto Giménez <agimenez@sysvalve.es>
---
 drivers/net/usb/Kconfig  |   12 +
 drivers/net/usb/Makefile |    1 +
 drivers/net/usb/ipheth.c |  562 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 575 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/usb/ipheth.c

diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index ba56ce4..63be4ca 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -385,4 +385,16 @@ config USB_CDC_PHONET
 	  cellular modem, as found on most Nokia handsets with the
 	  "PC suite" USB profile.
 
+config USB_IPHETH
+	tristate "Apple iPhone USB Ethernet driver"
+	default n
+	---help---
+	  Module used to share Internet connection (tethering) from your
+	  iPhone (Original, 3G and 3GS) to your system.
+	  Note that you need userspace libraries and programs that are needed
+	  to pair your device with your system and that understand the iPhone
+	  protocol.
+
+	  For more information: http://giagio.com/wiki/moin.cgi/iPhoneEthernetDriver
+
 endmenu
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 82ea629..edb09c0 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -23,4 +23,5 @@ obj-$(CONFIG_USB_NET_MCS7830)	+= mcs7830.o
 obj-$(CONFIG_USB_USBNET)	+= usbnet.o
 obj-$(CONFIG_USB_NET_INT51X1)	+= int51x1.o
 obj-$(CONFIG_USB_CDC_PHONET)	+= cdc-phonet.o
+obj-$(CONFIG_USB_IPHETH)	+= ipheth.o
 
diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c
new file mode 100644
index 0000000..677ae4a
--- /dev/null
+++ b/drivers/net/usb/ipheth.c
@@ -0,0 +1,562 @@
+/*
+ * 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/init.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>
+
+#define USB_VENDOR_APPLE        0x05ac
+#define USB_PRODUCT_IPHONE      0x1290
+#define USB_PRODUCT_IPHONE_3G   0x1292
+#define USB_PRODUCT_IPHONE_3GS  0x1294
+
+#define IPHETH_USBINTF_CLASS    255
+#define IPHETH_USBINTF_SUBCLASS 253
+#define IPHETH_USBINTF_PROTO    1
+
+#define IPHETH_BUF_SIZE         1516
+#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_CARRIER_CHECK 0x45
+
+#define IPHETH_CARRIER_CHECK_TIMEOUT (1 * HZ)
+#define IPHETH_CARRIER_ON       0x04
+
+static struct usb_device_id ipheth_table[] = {
+	{ USB_DEVICE_AND_INTERFACE_INFO(
+		USB_VENDOR_APPLE, USB_PRODUCT_IPHONE,
+		IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
+		IPHETH_USBINTF_PROTO) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(
+		USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_3G,
+		IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
+		IPHETH_USBINTF_PROTO) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(
+		USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_3GS,
+		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 sk_buff *tx_skb;
+	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;
+};
+
+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;
+
+	rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (rx_urb == NULL)
+		goto error;
+
+	tx_buf = usb_buffer_alloc(iphone->udev,
+				  IPHETH_BUF_SIZE,
+				  GFP_KERNEL,
+				  &tx_urb->transfer_dma);
+	if (tx_buf == NULL)
+		goto error;
+
+	rx_buf = usb_buffer_alloc(iphone->udev,
+				  IPHETH_BUF_SIZE,
+				  GFP_KERNEL,
+				  &rx_urb->transfer_dma);
+	if (rx_buf == NULL)
+		goto error;
+
+
+	iphone->tx_urb = tx_urb;
+	iphone->rx_urb = rx_urb;
+	iphone->tx_buf = tx_buf;
+	iphone->rx_buf = rx_buf;
+	return 0;
+
+error:
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, rx_buf,
+			rx_urb->transfer_dma);
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, tx_buf,
+			tx_urb->transfer_dma);
+	usb_free_urb(rx_urb);
+	usb_free_urb(tx_urb);
+	return -ENOMEM;
+}
+
+static void ipheth_free_urbs(struct ipheth_device *iphone)
+{
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, iphone->rx_buf,
+			iphone->rx_urb->transfer_dma);
+	usb_buffer_free(iphone->udev, IPHETH_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 void ipheth_rcvbulk_callback(struct urb *urb)
+{
+	struct ipheth_device *dev;
+	struct sk_buff *skb;
+	int status;
+	char *buf;
+	int len;
+
+	dev = urb->context;
+	if (dev == NULL)
+		return;
+
+	status = urb->status;
+	switch (status) {
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		return;
+	case 0:
+		break;
+	default:
+		err("%s: urb status: %d", __func__, urb->status);
+		return;
+	}
+
+	len = urb->actual_length;
+	buf = urb->transfer_buffer;
+
+	skb = dev_alloc_skb(NET_IP_ALIGN + len);
+	if (!skb) {
+		err("%s: dev_alloc_skb: -ENOMEM", __func__);
+		dev->net->stats.rx_dropped++;
+		return;
+	}
+
+	skb_reserve(skb, NET_IP_ALIGN);
+	memcpy(skb_put(skb, len), buf + NET_IP_ALIGN, len - NET_IP_ALIGN);
+	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);
+	ipheth_rx_submit(dev, GFP_ATOMIC);
+}
+
+static void ipheth_sndbulk_callback(struct urb *urb)
+{
+	struct ipheth_device *dev;
+
+	dev = urb->context;
+	if (dev == NULL)
+		return;
+
+	if (urb->status != 0 &&
+	    urb->status != -ENOENT &&
+	    urb->status != -ECONNRESET &&
+	    urb->status != -ESHUTDOWN)
+		err("%s: urb status: %d", __func__, urb->status);
+
+	dev_kfree_skb_irq(dev->tx_skb);
+	netif_wake_queue(dev->net);
+}
+
+static int ipheth_carrier_set(struct ipheth_device *dev)
+{
+	struct usb_device *udev = dev->udev;
+	int retval;
+
+	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) {
+		err("%s: usb_control_msg: %d", __func__, retval);
+		return retval;
+	}
+
+	if (dev->ctrl_buf[0] == IPHETH_CARRIER_ON)
+		netif_carrier_on(dev->net);
+	else
+		netif_carrier_off(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) {
+		err("%s: usb_control_msg: %d", __func__, retval);
+	} else if (retval < ETH_ALEN) {
+		err("%s: usb_control_msg: short packet: %d bytes",
+			__func__, retval);
+		retval = -EINVAL;
+	} else {
+		memcpy(net->dev_addr, dev->ctrl_buf, ETH_ALEN);
+		retval = 0;
+	}
+
+	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, IPHETH_BUF_SIZE,
+			  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)
+		err("%s: usb_submit_urb: %d", __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;
+
+	schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT);
+	netif_start_queue(net);
+	return retval;
+}
+
+static int ipheth_close(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+
+	cancel_delayed_work_sync(&dev->carrier_work);
+	netif_stop_queue(net);
+	return 0;
+}
+
+static int ipheth_tx(struct sk_buff *skb, struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	struct usb_device *udev = dev->udev;
+	int retval;
+
+	/* Paranoid */
+	if (skb->len > IPHETH_BUF_SIZE) {
+		WARN(1, "%s: skb too large: %d bytes", __func__, skb->len);
+		dev->net->stats.tx_dropped++;
+		dev_kfree_skb_irq(skb);
+		return NETDEV_TX_OK;
+	}
+
+	memcpy(dev->tx_buf, skb->data, skb->len);
+	if (skb->len < IPHETH_BUF_SIZE)
+		memset(dev->tx_buf + skb->len, 0, IPHETH_BUF_SIZE - skb->len);
+
+	usb_fill_bulk_urb(dev->tx_urb, udev,
+			  usb_sndbulkpipe(udev, dev->bulk_out),
+			  dev->tx_buf, IPHETH_BUF_SIZE,
+			  ipheth_sndbulk_callback,
+			  dev);
+	dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC);
+	if (retval) {
+		err("%s: usb_submit_urb: %d", __func__, retval);
+		dev->net->stats.tx_errors++;
+		dev_kfree_skb_irq(skb);
+	} else {
+		dev->tx_skb = skb;
+
+		dev->net->stats.tx_packets++;
+		dev->net->stats.tx_bytes += skb->len;
+		netif_stop_queue(net);
+	}
+
+	return NETDEV_TX_OK;
+}
+
+static void ipheth_tx_timeout(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+
+	err("%s: TX timeout", __func__);
+	dev->net->stats.tx_errors++;
+	usb_unlink_urb(dev->tx_urb);
+}
+
+static struct net_device_stats *ipheth_stats(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	return &dev->net->stats;
+}
+
+static u32 ipheth_ethtool_op_get_link(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	return netif_carrier_ok(dev->net);
+}
+
+static struct ethtool_ops ops = {
+	.get_link = ipheth_ethtool_op_get_link
+};
+
+static const struct net_device_ops ipheth_netdev_ops = {
+	.ndo_open = &ipheth_open,
+	.ndo_stop = &ipheth_close,
+	.ndo_start_xmit = &ipheth_tx,
+	.ndo_tx_timeout = &ipheth_tx_timeout,
+	.ndo_get_stats = &ipheth_stats,
+};
+
+static int ipheth_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct usb_host_interface *hintf;
+	struct usb_endpoint_descriptor *endp;
+	struct ipheth_device *dev;
+	struct net_device *netdev;
+	int i;
+	int retval;
+
+	netdev = alloc_etherdev(sizeof(struct ipheth_device));
+	if (!netdev)
+		return -ENOMEM;
+
+	netdev->netdev_ops = &ipheth_netdev_ops;
+	netdev->watchdog_timeo = IPHETH_TX_TIMEOUT;
+
+	dev = netdev_priv(netdev);
+	dev->udev = udev;
+	dev->net = netdev;
+	dev->intf = intf;
+
+	/* Set up endpoints */
+	hintf = usb_altnum_to_altsetting(intf, IPHETH_ALT_INTFNUM);
+	if (hintf == NULL) {
+		retval = -ENODEV;
+		err("Unable to find alternate settings interface");
+		goto err_endpoints;
+	}
+
+	for (i = 0; i < hintf->desc.bNumEndpoints; i++) {
+		endp = &hintf->endpoint[i].desc;
+		if (usb_endpoint_is_bulk_in(endp))
+			dev->bulk_in = endp->bEndpointAddress;
+		else if (usb_endpoint_is_bulk_out(endp))
+			dev->bulk_out = endp->bEndpointAddress;
+	}
+	if (!(dev->bulk_in && dev->bulk_out)) {
+		retval = -ENODEV;
+		err("Unable to find endpoints");
+		goto err_endpoints;
+	}
+
+	dev->ctrl_buf = kmalloc(IPHETH_CTRL_BUF_SIZE, GFP_KERNEL);
+	if (dev->ctrl_buf == NULL) {
+		retval = -ENOMEM;
+		goto err_alloc_ctrl_buf;
+	}
+
+	retval = ipheth_get_macaddr(dev);
+	if (retval)
+		goto err_get_macaddr;
+
+	INIT_DELAYED_WORK(&dev->carrier_work, ipheth_carrier_check_work);
+
+	retval = ipheth_alloc_urbs(dev);
+	if (retval) {
+		err("error allocating urbs: %d", retval);
+		goto err_alloc_urbs;
+	}
+
+	usb_set_intfdata(intf, dev);
+
+	SET_NETDEV_DEV(netdev, &intf->dev);
+	SET_ETHTOOL_OPS(netdev, &ops);
+
+	retval = register_netdev(netdev);
+	if (retval) {
+		err("error registering netdev: %d", retval);
+		retval = -EIO;
+		goto err_register_netdev;
+	}
+
+	dev_info(&intf->dev, "Apple iPhone USB Ethernet device attached\n");
+	return 0;
+
+err_register_netdev:
+	ipheth_free_urbs(dev);
+err_alloc_urbs:
+err_get_macaddr:
+err_alloc_ctrl_buf:
+	kfree(dev->ctrl_buf);
+err_endpoints:
+	free_netdev(netdev);
+	return retval;
+}
+
+static void ipheth_disconnect(struct usb_interface *intf)
+{
+	struct ipheth_device *dev;
+
+	dev = usb_get_intfdata(intf);
+	if (dev != NULL) {
+		unregister_netdev(dev->net);
+		ipheth_kill_urbs(dev);
+		ipheth_free_urbs(dev);
+		kfree(dev->ctrl_buf);
+		free_netdev(dev->net);
+	}
+	usb_set_intfdata(intf, NULL);
+	dev_info(&intf->dev, "Apple iPhone USB Ethernet now disconnected\n");
+}
+
+static struct usb_driver ipheth_driver = {
+	.name =		"ipheth",
+	.probe =	ipheth_probe,
+	.disconnect =	ipheth_disconnect,
+	.id_table =	ipheth_table,
+};
+
+static int __init ipheth_init(void)
+{
+	int retval;
+
+	retval = usb_register(&ipheth_driver);
+	if (retval) {
+		err("usb_register failed: %d", retval);
+		return retval;
+	}
+	return 0;
+}
+
+static void __exit ipheth_exit(void)
+{
+	usb_deregister(&ipheth_driver);
+}
+
+module_init(ipheth_init);
+module_exit(ipheth_exit);
+
+MODULE_AUTHOR("Diego Giagio <diego@giagio.com>");
+MODULE_DESCRIPTION("Apple iPhone USB Ethernet driver");
+MODULE_LICENSE("Dual BSD/GPL");
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH Resubmission] drivers/net/usb: Add new driver ipheth
  2010-04-07 22:11 ` [PATCH Resubmission] " L. Alberto Giménez
@ 2010-04-07 22:37   ` Roland Dreier
  2010-04-08  6:35   ` Oliver Neukum
  2010-04-13  8:15   ` David Miller
  2 siblings, 0 replies; 34+ messages in thread
From: Roland Dreier @ 2010-04-07 22:37 UTC (permalink / raw)
  To: L. Alberto Giménez
  Cc: linux-kernel, dgiagio, dborca, Greg Kroah-Hartman,
	David S. Miller, Jonas Sjöquist, Steve Glendinning,
	Torgny Johansson, David Brownell, Omar Laazimani, linux-usb,
	netdev

These are a few of the cosmetic issues alluded to, and by no means merge
blockers, but:

 > +	schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT);

Seems this might as well be round_jiffies_relative(IPHETH_CARRIER_CHECK_TIMEOUT)
to avoid extra wakeups.

 > +	netdev = alloc_etherdev(sizeof(struct ipheth_device));

This means that the interface will get an ethX name and look to
networkmanager et al like an ethernet device.  Seems we would maybe want
to make this a "wwan" type device (cf drivers/net/usb/usbnet.c and how
it handles FLAG_WWAN)?

 - R.
-- 
Roland Dreier <rolandd@cisco.com> || For corporate legal information go to:
http://www.cisco.com/web/about/doing_business/legal/cri/index.html

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH Resubmission] drivers/net/usb: Add new driver ipheth
  2010-04-07 22:11 ` [PATCH Resubmission] " L. Alberto Giménez
  2010-04-07 22:37   ` Roland Dreier
@ 2010-04-08  6:35   ` Oliver Neukum
  2010-04-13  8:15   ` David Miller
  2 siblings, 0 replies; 34+ messages in thread
From: Oliver Neukum @ 2010-04-08  6:35 UTC (permalink / raw)
  To: L. Alberto Giménez
  Cc: linux-kernel, dgiagio, dborca, Greg Kroah-Hartman,
	David S. Miller, Jonas Sjöquist, Steve Glendinning,
	Torgny Johansson, David Brownell, Omar Laazimani, linux-usb,
	netdev

Am Donnerstag, 8. April 2010 00:11:20 schrieb L. Alberto Giménez:
> Add new driver to use tethering with an iPhone device. After initial submission,
> apply fixes to fit the new driver into the kernel standards.

Looks good to me.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH Resubmission] drivers/net/usb: Add new driver ipheth
  2010-04-07 22:11 ` [PATCH Resubmission] " L. Alberto Giménez
  2010-04-07 22:37   ` Roland Dreier
  2010-04-08  6:35   ` Oliver Neukum
@ 2010-04-13  8:15   ` David Miller
  2010-04-13 19:03     ` "L. Alberto Giménez"
  2 siblings, 1 reply; 34+ messages in thread
From: David Miller @ 2010-04-13  8:15 UTC (permalink / raw)
  To: agimenez
  Cc: linux-kernel, dgiagio, dborca, gregkh, jonas.sjoquist,
	steve.glendinning, torgny.johansson, dbrownell, omar.oberthur,
	linux-usb, netdev

From: L. Alberto Giménez <agimenez@sysvalve.es>
Date: Thu,  8 Apr 2010 00:11:20 +0200

> From: dborca@yahoo.com
> 
> Add new driver to use tethering with an iPhone device. After initial submission,
> apply fixes to fit the new driver into the kernel standards.
> 
> There are still a couple of minor (almost cosmetic-level) issues, but the driver
> is fully functional right now.
> 
> Signed-off-by: L. Alberto Giménez <agimenez@sysvalve.es>

I can't apply this.

Unless you add a rule to drivers/net/Makefile, the build won't
actually get to your driver unless one of the other USB networking
devices are configured.

Please fix this up and resubmit.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH Resubmission] drivers/net/usb: Add new driver ipheth
  2010-04-13  8:15   ` David Miller
@ 2010-04-13 19:03     ` "L. Alberto Giménez"
  2010-04-13 21:29       ` David Miller
  0 siblings, 1 reply; 34+ messages in thread
From: "L. Alberto Giménez" @ 2010-04-13 19:03 UTC (permalink / raw)
  To: David Miller
  Cc: linux-kernel, dgiagio, dborca, gregkh, jonas.sjoquist,
	steve.glendinning, torgny.johansson, dbrownell, omar.oberthur,
	linux-usb, netdev

David Miller wrote:
> Unless you add a rule to drivers/net/Makefile, the build won't
> actually get to your driver unless one of the other USB networking
> devices are configured.
>   

Hi David,

Thanks for the info. I didn't know that I had to add an entry on the 
upper level Makefile. I guess that something like 
obj-$(CONFIG_USB_IPHETH) += usb/ should be enough? (I got it from the 
other USB net drivers).

I won't be able to work on it today, I've been very busy today and can't 
look into this, but I've not given up :)

> Please fix this up and resubmit. 

I have also in my queue a patch sent from upstream to fix the latest 
issues pointed out by Roland Dreier, and I need to test it a little bit 
more.

Thanks for your comments and patience!


Best regards,
L. Alberto Giménez

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH Resubmission] drivers/net/usb: Add new driver ipheth
  2010-04-13 19:03     ` "L. Alberto Giménez"
@ 2010-04-13 21:29       ` David Miller
  0 siblings, 0 replies; 34+ messages in thread
From: David Miller @ 2010-04-13 21:29 UTC (permalink / raw)
  To: agimenez
  Cc: linux-kernel, dgiagio, dborca, gregkh, jonas.sjoquist,
	steve.glendinning, torgny.johansson, dbrownell, omar.oberthur,
	linux-usb, netdev

From: "L. Alberto Giménez" <agimenez@sysvalve.es>
Date: Tue, 13 Apr 2010 21:03:25 +0200

> Thanks for the info. I didn't know that I had to add an entry on the
> upper level Makefile. I guess that something like
> obj-$(CONFIG_USB_IPHETH) += usb/ should be enough? (I got it from the
> other USB net drivers).

Yes.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH Resubmission v2] drivers/net/usb: Add new driver ipheth
  2010-03-30 21:34 [PATCH] Staging: Add new driver ipheth L. Alberto Giménez
                   ` (2 preceding siblings ...)
  2010-04-07 22:11 ` [PATCH Resubmission] " L. Alberto Giménez
@ 2010-04-15 19:46 ` L. Alberto Giménez
  2010-04-16  6:44   ` David Miller
  2010-04-18 18:35 ` [PATCH] " L. Alberto Giménez
  4 siblings, 1 reply; 34+ messages in thread
From: L. Alberto Giménez @ 2010-04-15 19:46 UTC (permalink / raw)
  Cc: dgiagio, dborca, David S. Miller, James Bottomley, Ralf Baechle,
	Greg Kroah-Hartman, Jonas Sjöquist, Torgny Johansson,
	Steve Glendinning, David Brownell, Omar Laazimani,
	Rémi Denis-Courmont, L. Alberto Giménez, netdev,
	linux-kernel, linux-usb

From: dborca@yahoo.com

Add new driver to use tethering with an iPhone device. After initial submission,
apply fixes to fit the new driver into the kernel standards.

There are still a couple of minor (almost cosmetic-level) issues, but the driver
is fully functional right now.

Signed-off-by: L. Alberto Giménez <agimenez@sysvalve.es>
---
 drivers/net/Makefile     |    1 +
 drivers/net/usb/Kconfig  |   12 +
 drivers/net/usb/Makefile |    1 +
 drivers/net/usb/ipheth.c |  568 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 582 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/usb/ipheth.c

diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index a583b50..12b280a 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -273,6 +273,7 @@ obj-$(CONFIG_USB_RTL8150)       += usb/
 obj-$(CONFIG_USB_HSO)		+= usb/
 obj-$(CONFIG_USB_USBNET)        += usb/
 obj-$(CONFIG_USB_ZD1201)        += usb/
+obj-$(CONFIG_USB_IPHETH)        += usb/
 
 obj-y += wireless/
 obj-$(CONFIG_NET_TULIP) += tulip/
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index ba56ce4..63be4ca 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -385,4 +385,16 @@ config USB_CDC_PHONET
 	  cellular modem, as found on most Nokia handsets with the
 	  "PC suite" USB profile.
 
+config USB_IPHETH
+	tristate "Apple iPhone USB Ethernet driver"
+	default n
+	---help---
+	  Module used to share Internet connection (tethering) from your
+	  iPhone (Original, 3G and 3GS) to your system.
+	  Note that you need userspace libraries and programs that are needed
+	  to pair your device with your system and that understand the iPhone
+	  protocol.
+
+	  For more information: http://giagio.com/wiki/moin.cgi/iPhoneEthernetDriver
+
 endmenu
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 82ea629..edb09c0 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -23,4 +23,5 @@ obj-$(CONFIG_USB_NET_MCS7830)	+= mcs7830.o
 obj-$(CONFIG_USB_USBNET)	+= usbnet.o
 obj-$(CONFIG_USB_NET_INT51X1)	+= int51x1.o
 obj-$(CONFIG_USB_CDC_PHONET)	+= cdc-phonet.o
+obj-$(CONFIG_USB_IPHETH)	+= ipheth.o
 
diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c
new file mode 100644
index 0000000..fd10331
--- /dev/null
+++ b/drivers/net/usb/ipheth.c
@@ -0,0 +1,568 @@
+/*
+ * 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/init.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>
+
+#define USB_VENDOR_APPLE        0x05ac
+#define USB_PRODUCT_IPHONE      0x1290
+#define USB_PRODUCT_IPHONE_3G   0x1292
+#define USB_PRODUCT_IPHONE_3GS  0x1294
+
+#define IPHETH_USBINTF_CLASS    255
+#define IPHETH_USBINTF_SUBCLASS 253
+#define IPHETH_USBINTF_PROTO    1
+
+#define IPHETH_BUF_SIZE         1516
+#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_CARRIER_CHECK 0x45
+
+#define IPHETH_CARRIER_CHECK_TIMEOUT round_jiffies_relative(1 * HZ)
+#define IPHETH_CARRIER_ON       0x04
+
+static struct usb_device_id ipheth_table[] = {
+	{ USB_DEVICE_AND_INTERFACE_INFO(
+		USB_VENDOR_APPLE, USB_PRODUCT_IPHONE,
+		IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
+		IPHETH_USBINTF_PROTO) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(
+		USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_3G,
+		IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
+		IPHETH_USBINTF_PROTO) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(
+		USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_3GS,
+		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 sk_buff *tx_skb;
+	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;
+};
+
+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;
+
+	rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (rx_urb == NULL)
+		goto error;
+
+	tx_buf = usb_buffer_alloc(iphone->udev,
+				  IPHETH_BUF_SIZE,
+				  GFP_KERNEL,
+				  &tx_urb->transfer_dma);
+	if (tx_buf == NULL)
+		goto error;
+
+	rx_buf = usb_buffer_alloc(iphone->udev,
+				  IPHETH_BUF_SIZE,
+				  GFP_KERNEL,
+				  &rx_urb->transfer_dma);
+	if (rx_buf == NULL)
+		goto error;
+
+
+	iphone->tx_urb = tx_urb;
+	iphone->rx_urb = rx_urb;
+	iphone->tx_buf = tx_buf;
+	iphone->rx_buf = rx_buf;
+	return 0;
+
+error:
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, rx_buf,
+			rx_urb->transfer_dma);
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, tx_buf,
+			tx_urb->transfer_dma);
+	usb_free_urb(rx_urb);
+	usb_free_urb(tx_urb);
+	return -ENOMEM;
+}
+
+static void ipheth_free_urbs(struct ipheth_device *iphone)
+{
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, iphone->rx_buf,
+			iphone->rx_urb->transfer_dma);
+	usb_buffer_free(iphone->udev, IPHETH_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 void ipheth_rcvbulk_callback(struct urb *urb)
+{
+	struct ipheth_device *dev;
+	struct sk_buff *skb;
+	int status;
+	char *buf;
+	int len;
+
+	dev = urb->context;
+	if (dev == NULL)
+		return;
+
+	status = urb->status;
+	switch (status) {
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		return;
+	case 0:
+		break;
+	default:
+		err("%s: urb status: %d", __func__, urb->status);
+		return;
+	}
+
+	len = urb->actual_length;
+	buf = urb->transfer_buffer;
+
+	skb = dev_alloc_skb(NET_IP_ALIGN + len);
+	if (!skb) {
+		err("%s: dev_alloc_skb: -ENOMEM", __func__);
+		dev->net->stats.rx_dropped++;
+		return;
+	}
+
+	skb_reserve(skb, NET_IP_ALIGN);
+	memcpy(skb_put(skb, len), buf + NET_IP_ALIGN, len - NET_IP_ALIGN);
+	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);
+	ipheth_rx_submit(dev, GFP_ATOMIC);
+}
+
+static void ipheth_sndbulk_callback(struct urb *urb)
+{
+	struct ipheth_device *dev;
+
+	dev = urb->context;
+	if (dev == NULL)
+		return;
+
+	if (urb->status != 0 &&
+	    urb->status != -ENOENT &&
+	    urb->status != -ECONNRESET &&
+	    urb->status != -ESHUTDOWN)
+		err("%s: urb status: %d", __func__, urb->status);
+
+	dev_kfree_skb_irq(dev->tx_skb);
+	netif_wake_queue(dev->net);
+}
+
+static int ipheth_carrier_set(struct ipheth_device *dev)
+{
+	struct usb_device *udev = dev->udev;
+	int retval;
+
+	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) {
+		err("%s: usb_control_msg: %d", __func__, retval);
+		return retval;
+	}
+
+	if (dev->ctrl_buf[0] == IPHETH_CARRIER_ON)
+		netif_carrier_on(dev->net);
+	else
+		netif_carrier_off(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) {
+		err("%s: usb_control_msg: %d", __func__, retval);
+	} else if (retval < ETH_ALEN) {
+		err("%s: usb_control_msg: short packet: %d bytes",
+			__func__, retval);
+		retval = -EINVAL;
+	} else {
+		memcpy(net->dev_addr, dev->ctrl_buf, ETH_ALEN);
+		retval = 0;
+	}
+
+	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, IPHETH_BUF_SIZE,
+			  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)
+		err("%s: usb_submit_urb: %d", __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;
+
+	schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT);
+	netif_start_queue(net);
+	return retval;
+}
+
+static int ipheth_close(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+
+	cancel_delayed_work_sync(&dev->carrier_work);
+	netif_stop_queue(net);
+	return 0;
+}
+
+static int ipheth_tx(struct sk_buff *skb, struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	struct usb_device *udev = dev->udev;
+	int retval;
+
+	/* Paranoid */
+	if (skb->len > IPHETH_BUF_SIZE) {
+		WARN(1, "%s: skb too large: %d bytes", __func__, skb->len);
+		dev->net->stats.tx_dropped++;
+		dev_kfree_skb_irq(skb);
+		return NETDEV_TX_OK;
+	}
+
+	memcpy(dev->tx_buf, skb->data, skb->len);
+	if (skb->len < IPHETH_BUF_SIZE)
+		memset(dev->tx_buf + skb->len, 0, IPHETH_BUF_SIZE - skb->len);
+
+	usb_fill_bulk_urb(dev->tx_urb, udev,
+			  usb_sndbulkpipe(udev, dev->bulk_out),
+			  dev->tx_buf, IPHETH_BUF_SIZE,
+			  ipheth_sndbulk_callback,
+			  dev);
+	dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC);
+	if (retval) {
+		err("%s: usb_submit_urb: %d", __func__, retval);
+		dev->net->stats.tx_errors++;
+		dev_kfree_skb_irq(skb);
+	} else {
+		dev->tx_skb = skb;
+
+		dev->net->stats.tx_packets++;
+		dev->net->stats.tx_bytes += skb->len;
+		netif_stop_queue(net);
+	}
+
+	return NETDEV_TX_OK;
+}
+
+static void ipheth_tx_timeout(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+
+	err("%s: TX timeout", __func__);
+	dev->net->stats.tx_errors++;
+	usb_unlink_urb(dev->tx_urb);
+}
+
+static struct net_device_stats *ipheth_stats(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	return &dev->net->stats;
+}
+
+static u32 ipheth_ethtool_op_get_link(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	return netif_carrier_ok(dev->net);
+}
+
+static struct ethtool_ops ops = {
+	.get_link = ipheth_ethtool_op_get_link
+};
+
+static const struct net_device_ops ipheth_netdev_ops = {
+	.ndo_open = &ipheth_open,
+	.ndo_stop = &ipheth_close,
+	.ndo_start_xmit = &ipheth_tx,
+	.ndo_tx_timeout = &ipheth_tx_timeout,
+	.ndo_get_stats = &ipheth_stats,
+};
+
+static struct device_type ipheth_type = {
+	.name	= "wwan",
+};
+
+static int ipheth_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct usb_host_interface *hintf;
+	struct usb_endpoint_descriptor *endp;
+	struct ipheth_device *dev;
+	struct net_device *netdev;
+	int i;
+	int retval;
+
+	netdev = alloc_etherdev(sizeof(struct ipheth_device));
+	if (!netdev)
+		return -ENOMEM;
+
+	netdev->netdev_ops = &ipheth_netdev_ops;
+	netdev->watchdog_timeo = IPHETH_TX_TIMEOUT;
+	strcpy(netdev->name, "wwan%d");
+
+	dev = netdev_priv(netdev);
+	dev->udev = udev;
+	dev->net = netdev;
+	dev->intf = intf;
+
+	/* Set up endpoints */
+	hintf = usb_altnum_to_altsetting(intf, IPHETH_ALT_INTFNUM);
+	if (hintf == NULL) {
+		retval = -ENODEV;
+		err("Unable to find alternate settings interface");
+		goto err_endpoints;
+	}
+
+	for (i = 0; i < hintf->desc.bNumEndpoints; i++) {
+		endp = &hintf->endpoint[i].desc;
+		if (usb_endpoint_is_bulk_in(endp))
+			dev->bulk_in = endp->bEndpointAddress;
+		else if (usb_endpoint_is_bulk_out(endp))
+			dev->bulk_out = endp->bEndpointAddress;
+	}
+	if (!(dev->bulk_in && dev->bulk_out)) {
+		retval = -ENODEV;
+		err("Unable to find endpoints");
+		goto err_endpoints;
+	}
+
+	dev->ctrl_buf = kmalloc(IPHETH_CTRL_BUF_SIZE, GFP_KERNEL);
+	if (dev->ctrl_buf == NULL) {
+		retval = -ENOMEM;
+		goto err_alloc_ctrl_buf;
+	}
+
+	retval = ipheth_get_macaddr(dev);
+	if (retval)
+		goto err_get_macaddr;
+
+	INIT_DELAYED_WORK(&dev->carrier_work, ipheth_carrier_check_work);
+
+	retval = ipheth_alloc_urbs(dev);
+	if (retval) {
+		err("error allocating urbs: %d", retval);
+		goto err_alloc_urbs;
+	}
+
+	usb_set_intfdata(intf, dev);
+
+	SET_NETDEV_DEV(netdev, &intf->dev);
+	SET_ETHTOOL_OPS(netdev, &ops);
+	SET_NETDEV_DEVTYPE(netdev, &ipheth_type);
+
+	retval = register_netdev(netdev);
+	if (retval) {
+		err("error registering netdev: %d", retval);
+		retval = -EIO;
+		goto err_register_netdev;
+	}
+
+	dev_info(&intf->dev, "Apple iPhone USB Ethernet device attached\n");
+	return 0;
+
+err_register_netdev:
+	ipheth_free_urbs(dev);
+err_alloc_urbs:
+err_get_macaddr:
+err_alloc_ctrl_buf:
+	kfree(dev->ctrl_buf);
+err_endpoints:
+	free_netdev(netdev);
+	return retval;
+}
+
+static void ipheth_disconnect(struct usb_interface *intf)
+{
+	struct ipheth_device *dev;
+
+	dev = usb_get_intfdata(intf);
+	if (dev != NULL) {
+		unregister_netdev(dev->net);
+		ipheth_kill_urbs(dev);
+		ipheth_free_urbs(dev);
+		kfree(dev->ctrl_buf);
+		free_netdev(dev->net);
+	}
+	usb_set_intfdata(intf, NULL);
+	dev_info(&intf->dev, "Apple iPhone USB Ethernet now disconnected\n");
+}
+
+static struct usb_driver ipheth_driver = {
+	.name =		"ipheth",
+	.probe =	ipheth_probe,
+	.disconnect =	ipheth_disconnect,
+	.id_table =	ipheth_table,
+};
+
+static int __init ipheth_init(void)
+{
+	int retval;
+
+	retval = usb_register(&ipheth_driver);
+	if (retval) {
+		err("usb_register failed: %d", retval);
+		return retval;
+	}
+	return 0;
+}
+
+static void __exit ipheth_exit(void)
+{
+	usb_deregister(&ipheth_driver);
+}
+
+module_init(ipheth_init);
+module_exit(ipheth_exit);
+
+MODULE_AUTHOR("Diego Giagio <diego@giagio.com>");
+MODULE_DESCRIPTION("Apple iPhone USB Ethernet driver");
+MODULE_LICENSE("Dual BSD/GPL");
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH Resubmission v2] drivers/net/usb: Add new driver ipheth
  2010-04-15 19:46 ` [PATCH Resubmission v2] " L. Alberto Giménez
@ 2010-04-16  6:44   ` David Miller
  0 siblings, 0 replies; 34+ messages in thread
From: David Miller @ 2010-04-16  6:44 UTC (permalink / raw)
  To: agimenez
  Cc: dgiagio, dborca, James.Bottomley, ralf, gregkh, jonas.sjoquist,
	torgny.johansson, steve.glendinning, dbrownell, omar.oberthur,
	remi.denis-courmont, netdev, linux-kernel, linux-usb

From: L. Alberto Giménez <agimenez@sysvalve.es>
Date: Thu, 15 Apr 2010 21:46:29 +0200

> From: dborca@yahoo.com
> 
> Add new driver to use tethering with an iPhone device. After initial submission,
> apply fixes to fit the new driver into the kernel standards.
> 
> There are still a couple of minor (almost cosmetic-level) issues, but the driver
> is fully functional right now.
> 
> Signed-off-by: L. Alberto Giménez <agimenez@sysvalve.es>

I'm very confused about the authorship of this driver.

Who wrote it?

You added a "From: " line using specifying Daniel Borca (btw,
when you add these "From: " lines you  need to specify it in
the form "From: NAME <EMAIL>" not just "From: EMAIL" so in
this case we want to see "From: Daniel Borca <dborca@yahoo.com>")

The code itself gives copyright to Diego Giagio <diego@giagio.com>
and he is also the one listed in the MODULE_AUTHOR().

And you're the one submitting the code, and also the only person
actually giving a signoff in the commit message.

It's too confusing and ambiguous, and if there are any problems
down the road the last thing we need is for the authorship to
be ambiguous.

I would really appreciate it if the authorship was clearly stated, and
the actual author of the code actually gives a "Signed-off-by: " line
in the commit message for this inclusions of this driver.

Please fix this up and resubmit, thank you.

Thanks.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH] drivers/net/usb: Add new driver ipheth
  2010-03-30 21:34 [PATCH] Staging: Add new driver ipheth L. Alberto Giménez
                   ` (3 preceding siblings ...)
  2010-04-15 19:46 ` [PATCH Resubmission v2] " L. Alberto Giménez
@ 2010-04-18 18:35 ` L. Alberto Giménez
  2010-04-21 14:15   ` Diego Giagio
  4 siblings, 1 reply; 34+ messages in thread
From: L. Alberto Giménez @ 2010-04-18 18:35 UTC (permalink / raw)
  To: linux-kernel
  Cc: dgiagio, dborca, David S. Miller, James Bottomley, Ralf Baechle,
	Greg Kroah-Hartman, Jonas Sjöquist, Torgny Johansson,
	Steve Glendinning, David Brownell, Omar Laazimani,
	Rémi Denis-Courmont, L. Alberto Giménez, netdev,
	linux-usb

From: Diego Giagio <diego@giagio.com>

Add new driver to use tethering with an iPhone device. After initial submission,
apply fixes to fit the new driver into the kernel standards.

There are still a couple of minor (almost cosmetic-level) issues, but the driver
is fully functional right now.

Signed-off-by: L. Alberto Giménez <agimenez@sysvalve.es>
---
This time I've cleared the authorship as requested by David. Diego is the
original author of the code, although Daniel Borca has contributed a lot of
changes as well (I did the kernel-tree integration and fixed checkpatch issues
as well as some of the problems pointed out in the list on previous
submissions).

Diego and/or Daniel: please sign-off (specially Diego since he is the one who
will be officially attributed as the driver author) this patch so the authorship
and DCO is clear.

 drivers/net/Makefile     |    1 +
 drivers/net/usb/Kconfig  |   12 +
 drivers/net/usb/Makefile |    1 +
 drivers/net/usb/ipheth.c |  568 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 582 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/usb/ipheth.c

diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index a583b50..12b280a 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -273,6 +273,7 @@ obj-$(CONFIG_USB_RTL8150)       += usb/
 obj-$(CONFIG_USB_HSO)		+= usb/
 obj-$(CONFIG_USB_USBNET)        += usb/
 obj-$(CONFIG_USB_ZD1201)        += usb/
+obj-$(CONFIG_USB_IPHETH)        += usb/
 
 obj-y += wireless/
 obj-$(CONFIG_NET_TULIP) += tulip/
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index ba56ce4..63be4ca 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -385,4 +385,16 @@ config USB_CDC_PHONET
 	  cellular modem, as found on most Nokia handsets with the
 	  "PC suite" USB profile.
 
+config USB_IPHETH
+	tristate "Apple iPhone USB Ethernet driver"
+	default n
+	---help---
+	  Module used to share Internet connection (tethering) from your
+	  iPhone (Original, 3G and 3GS) to your system.
+	  Note that you need userspace libraries and programs that are needed
+	  to pair your device with your system and that understand the iPhone
+	  protocol.
+
+	  For more information: http://giagio.com/wiki/moin.cgi/iPhoneEthernetDriver
+
 endmenu
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 82ea629..edb09c0 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -23,4 +23,5 @@ obj-$(CONFIG_USB_NET_MCS7830)	+= mcs7830.o
 obj-$(CONFIG_USB_USBNET)	+= usbnet.o
 obj-$(CONFIG_USB_NET_INT51X1)	+= int51x1.o
 obj-$(CONFIG_USB_CDC_PHONET)	+= cdc-phonet.o
+obj-$(CONFIG_USB_IPHETH)	+= ipheth.o
 
diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c
new file mode 100644
index 0000000..fd10331
--- /dev/null
+++ b/drivers/net/usb/ipheth.c
@@ -0,0 +1,568 @@
+/*
+ * 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/init.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>
+
+#define USB_VENDOR_APPLE        0x05ac
+#define USB_PRODUCT_IPHONE      0x1290
+#define USB_PRODUCT_IPHONE_3G   0x1292
+#define USB_PRODUCT_IPHONE_3GS  0x1294
+
+#define IPHETH_USBINTF_CLASS    255
+#define IPHETH_USBINTF_SUBCLASS 253
+#define IPHETH_USBINTF_PROTO    1
+
+#define IPHETH_BUF_SIZE         1516
+#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_CARRIER_CHECK 0x45
+
+#define IPHETH_CARRIER_CHECK_TIMEOUT round_jiffies_relative(1 * HZ)
+#define IPHETH_CARRIER_ON       0x04
+
+static struct usb_device_id ipheth_table[] = {
+	{ USB_DEVICE_AND_INTERFACE_INFO(
+		USB_VENDOR_APPLE, USB_PRODUCT_IPHONE,
+		IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
+		IPHETH_USBINTF_PROTO) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(
+		USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_3G,
+		IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
+		IPHETH_USBINTF_PROTO) },
+	{ USB_DEVICE_AND_INTERFACE_INFO(
+		USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_3GS,
+		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 sk_buff *tx_skb;
+	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;
+};
+
+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;
+
+	rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (rx_urb == NULL)
+		goto error;
+
+	tx_buf = usb_buffer_alloc(iphone->udev,
+				  IPHETH_BUF_SIZE,
+				  GFP_KERNEL,
+				  &tx_urb->transfer_dma);
+	if (tx_buf == NULL)
+		goto error;
+
+	rx_buf = usb_buffer_alloc(iphone->udev,
+				  IPHETH_BUF_SIZE,
+				  GFP_KERNEL,
+				  &rx_urb->transfer_dma);
+	if (rx_buf == NULL)
+		goto error;
+
+
+	iphone->tx_urb = tx_urb;
+	iphone->rx_urb = rx_urb;
+	iphone->tx_buf = tx_buf;
+	iphone->rx_buf = rx_buf;
+	return 0;
+
+error:
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, rx_buf,
+			rx_urb->transfer_dma);
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, tx_buf,
+			tx_urb->transfer_dma);
+	usb_free_urb(rx_urb);
+	usb_free_urb(tx_urb);
+	return -ENOMEM;
+}
+
+static void ipheth_free_urbs(struct ipheth_device *iphone)
+{
+	usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, iphone->rx_buf,
+			iphone->rx_urb->transfer_dma);
+	usb_buffer_free(iphone->udev, IPHETH_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 void ipheth_rcvbulk_callback(struct urb *urb)
+{
+	struct ipheth_device *dev;
+	struct sk_buff *skb;
+	int status;
+	char *buf;
+	int len;
+
+	dev = urb->context;
+	if (dev == NULL)
+		return;
+
+	status = urb->status;
+	switch (status) {
+	case -ENOENT:
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		return;
+	case 0:
+		break;
+	default:
+		err("%s: urb status: %d", __func__, urb->status);
+		return;
+	}
+
+	len = urb->actual_length;
+	buf = urb->transfer_buffer;
+
+	skb = dev_alloc_skb(NET_IP_ALIGN + len);
+	if (!skb) {
+		err("%s: dev_alloc_skb: -ENOMEM", __func__);
+		dev->net->stats.rx_dropped++;
+		return;
+	}
+
+	skb_reserve(skb, NET_IP_ALIGN);
+	memcpy(skb_put(skb, len), buf + NET_IP_ALIGN, len - NET_IP_ALIGN);
+	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);
+	ipheth_rx_submit(dev, GFP_ATOMIC);
+}
+
+static void ipheth_sndbulk_callback(struct urb *urb)
+{
+	struct ipheth_device *dev;
+
+	dev = urb->context;
+	if (dev == NULL)
+		return;
+
+	if (urb->status != 0 &&
+	    urb->status != -ENOENT &&
+	    urb->status != -ECONNRESET &&
+	    urb->status != -ESHUTDOWN)
+		err("%s: urb status: %d", __func__, urb->status);
+
+	dev_kfree_skb_irq(dev->tx_skb);
+	netif_wake_queue(dev->net);
+}
+
+static int ipheth_carrier_set(struct ipheth_device *dev)
+{
+	struct usb_device *udev = dev->udev;
+	int retval;
+
+	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) {
+		err("%s: usb_control_msg: %d", __func__, retval);
+		return retval;
+	}
+
+	if (dev->ctrl_buf[0] == IPHETH_CARRIER_ON)
+		netif_carrier_on(dev->net);
+	else
+		netif_carrier_off(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) {
+		err("%s: usb_control_msg: %d", __func__, retval);
+	} else if (retval < ETH_ALEN) {
+		err("%s: usb_control_msg: short packet: %d bytes",
+			__func__, retval);
+		retval = -EINVAL;
+	} else {
+		memcpy(net->dev_addr, dev->ctrl_buf, ETH_ALEN);
+		retval = 0;
+	}
+
+	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, IPHETH_BUF_SIZE,
+			  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)
+		err("%s: usb_submit_urb: %d", __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;
+
+	schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT);
+	netif_start_queue(net);
+	return retval;
+}
+
+static int ipheth_close(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+
+	cancel_delayed_work_sync(&dev->carrier_work);
+	netif_stop_queue(net);
+	return 0;
+}
+
+static int ipheth_tx(struct sk_buff *skb, struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	struct usb_device *udev = dev->udev;
+	int retval;
+
+	/* Paranoid */
+	if (skb->len > IPHETH_BUF_SIZE) {
+		WARN(1, "%s: skb too large: %d bytes", __func__, skb->len);
+		dev->net->stats.tx_dropped++;
+		dev_kfree_skb_irq(skb);
+		return NETDEV_TX_OK;
+	}
+
+	memcpy(dev->tx_buf, skb->data, skb->len);
+	if (skb->len < IPHETH_BUF_SIZE)
+		memset(dev->tx_buf + skb->len, 0, IPHETH_BUF_SIZE - skb->len);
+
+	usb_fill_bulk_urb(dev->tx_urb, udev,
+			  usb_sndbulkpipe(udev, dev->bulk_out),
+			  dev->tx_buf, IPHETH_BUF_SIZE,
+			  ipheth_sndbulk_callback,
+			  dev);
+	dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+	retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC);
+	if (retval) {
+		err("%s: usb_submit_urb: %d", __func__, retval);
+		dev->net->stats.tx_errors++;
+		dev_kfree_skb_irq(skb);
+	} else {
+		dev->tx_skb = skb;
+
+		dev->net->stats.tx_packets++;
+		dev->net->stats.tx_bytes += skb->len;
+		netif_stop_queue(net);
+	}
+
+	return NETDEV_TX_OK;
+}
+
+static void ipheth_tx_timeout(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+
+	err("%s: TX timeout", __func__);
+	dev->net->stats.tx_errors++;
+	usb_unlink_urb(dev->tx_urb);
+}
+
+static struct net_device_stats *ipheth_stats(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	return &dev->net->stats;
+}
+
+static u32 ipheth_ethtool_op_get_link(struct net_device *net)
+{
+	struct ipheth_device *dev = netdev_priv(net);
+	return netif_carrier_ok(dev->net);
+}
+
+static struct ethtool_ops ops = {
+	.get_link = ipheth_ethtool_op_get_link
+};
+
+static const struct net_device_ops ipheth_netdev_ops = {
+	.ndo_open = &ipheth_open,
+	.ndo_stop = &ipheth_close,
+	.ndo_start_xmit = &ipheth_tx,
+	.ndo_tx_timeout = &ipheth_tx_timeout,
+	.ndo_get_stats = &ipheth_stats,
+};
+
+static struct device_type ipheth_type = {
+	.name	= "wwan",
+};
+
+static int ipheth_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct usb_host_interface *hintf;
+	struct usb_endpoint_descriptor *endp;
+	struct ipheth_device *dev;
+	struct net_device *netdev;
+	int i;
+	int retval;
+
+	netdev = alloc_etherdev(sizeof(struct ipheth_device));
+	if (!netdev)
+		return -ENOMEM;
+
+	netdev->netdev_ops = &ipheth_netdev_ops;
+	netdev->watchdog_timeo = IPHETH_TX_TIMEOUT;
+	strcpy(netdev->name, "wwan%d");
+
+	dev = netdev_priv(netdev);
+	dev->udev = udev;
+	dev->net = netdev;
+	dev->intf = intf;
+
+	/* Set up endpoints */
+	hintf = usb_altnum_to_altsetting(intf, IPHETH_ALT_INTFNUM);
+	if (hintf == NULL) {
+		retval = -ENODEV;
+		err("Unable to find alternate settings interface");
+		goto err_endpoints;
+	}
+
+	for (i = 0; i < hintf->desc.bNumEndpoints; i++) {
+		endp = &hintf->endpoint[i].desc;
+		if (usb_endpoint_is_bulk_in(endp))
+			dev->bulk_in = endp->bEndpointAddress;
+		else if (usb_endpoint_is_bulk_out(endp))
+			dev->bulk_out = endp->bEndpointAddress;
+	}
+	if (!(dev->bulk_in && dev->bulk_out)) {
+		retval = -ENODEV;
+		err("Unable to find endpoints");
+		goto err_endpoints;
+	}
+
+	dev->ctrl_buf = kmalloc(IPHETH_CTRL_BUF_SIZE, GFP_KERNEL);
+	if (dev->ctrl_buf == NULL) {
+		retval = -ENOMEM;
+		goto err_alloc_ctrl_buf;
+	}
+
+	retval = ipheth_get_macaddr(dev);
+	if (retval)
+		goto err_get_macaddr;
+
+	INIT_DELAYED_WORK(&dev->carrier_work, ipheth_carrier_check_work);
+
+	retval = ipheth_alloc_urbs(dev);
+	if (retval) {
+		err("error allocating urbs: %d", retval);
+		goto err_alloc_urbs;
+	}
+
+	usb_set_intfdata(intf, dev);
+
+	SET_NETDEV_DEV(netdev, &intf->dev);
+	SET_ETHTOOL_OPS(netdev, &ops);
+	SET_NETDEV_DEVTYPE(netdev, &ipheth_type);
+
+	retval = register_netdev(netdev);
+	if (retval) {
+		err("error registering netdev: %d", retval);
+		retval = -EIO;
+		goto err_register_netdev;
+	}
+
+	dev_info(&intf->dev, "Apple iPhone USB Ethernet device attached\n");
+	return 0;
+
+err_register_netdev:
+	ipheth_free_urbs(dev);
+err_alloc_urbs:
+err_get_macaddr:
+err_alloc_ctrl_buf:
+	kfree(dev->ctrl_buf);
+err_endpoints:
+	free_netdev(netdev);
+	return retval;
+}
+
+static void ipheth_disconnect(struct usb_interface *intf)
+{
+	struct ipheth_device *dev;
+
+	dev = usb_get_intfdata(intf);
+	if (dev != NULL) {
+		unregister_netdev(dev->net);
+		ipheth_kill_urbs(dev);
+		ipheth_free_urbs(dev);
+		kfree(dev->ctrl_buf);
+		free_netdev(dev->net);
+	}
+	usb_set_intfdata(intf, NULL);
+	dev_info(&intf->dev, "Apple iPhone USB Ethernet now disconnected\n");
+}
+
+static struct usb_driver ipheth_driver = {
+	.name =		"ipheth",
+	.probe =	ipheth_probe,
+	.disconnect =	ipheth_disconnect,
+	.id_table =	ipheth_table,
+};
+
+static int __init ipheth_init(void)
+{
+	int retval;
+
+	retval = usb_register(&ipheth_driver);
+	if (retval) {
+		err("usb_register failed: %d", retval);
+		return retval;
+	}
+	return 0;
+}
+
+static void __exit ipheth_exit(void)
+{
+	usb_deregister(&ipheth_driver);
+}
+
+module_init(ipheth_init);
+module_exit(ipheth_exit);
+
+MODULE_AUTHOR("Diego Giagio <diego@giagio.com>");
+MODULE_DESCRIPTION("Apple iPhone USB Ethernet driver");
+MODULE_LICENSE("Dual BSD/GPL");
-- 
1.7.0


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH] drivers/net/usb: Add new driver ipheth
  2010-04-18 18:35 ` [PATCH] " L. Alberto Giménez
@ 2010-04-21 14:15   ` Diego Giagio
  2010-04-22  5:44     ` David Miller
  0 siblings, 1 reply; 34+ messages in thread
From: Diego Giagio @ 2010-04-21 14:15 UTC (permalink / raw)
  To: L. Alberto Giménez
  Cc: linux-kernel, dborca, David S. Miller, James Bottomley,
	Ralf Baechle, Greg Kroah-Hartman, Jonas Sjöquist,
	Torgny Johansson, Steve Glendinning, David Brownell,
	Omar Laazimani, Rémi Denis-Courmont, netdev, linux-usb

On Sun, Apr 18, 2010 at 3:35 PM, L. Alberto Giménez
<agimenez@sysvalve.es> wrote:
> From: Diego Giagio <diego@giagio.com>
>
> Add new driver to use tethering with an iPhone device. After initial submission,
> apply fixes to fit the new driver into the kernel standards.
>
> There are still a couple of minor (almost cosmetic-level) issues, but the driver
> is fully functional right now.
>

Signed-off-by: Diego Giagio <diego@giagio.com>
Cc: Daniel Borca <dborca@yahoo.com>

>
>  drivers/net/Makefile     |    1 +
>  drivers/net/usb/Kconfig  |   12 +
>  drivers/net/usb/Makefile |    1 +
>  drivers/net/usb/ipheth.c |  568 ++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 582 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/net/usb/ipheth.c
>
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index a583b50..12b280a 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -273,6 +273,7 @@ obj-$(CONFIG_USB_RTL8150)       += usb/
>  obj-$(CONFIG_USB_HSO)          += usb/
>  obj-$(CONFIG_USB_USBNET)        += usb/
>  obj-$(CONFIG_USB_ZD1201)        += usb/
> +obj-$(CONFIG_USB_IPHETH)        += usb/
>
>  obj-y += wireless/
>  obj-$(CONFIG_NET_TULIP) += tulip/
> diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
> index ba56ce4..63be4ca 100644
> --- a/drivers/net/usb/Kconfig
> +++ b/drivers/net/usb/Kconfig
> @@ -385,4 +385,16 @@ config USB_CDC_PHONET
>          cellular modem, as found on most Nokia handsets with the
>          "PC suite" USB profile.
>
> +config USB_IPHETH
> +       tristate "Apple iPhone USB Ethernet driver"
> +       default n
> +       ---help---
> +         Module used to share Internet connection (tethering) from your
> +         iPhone (Original, 3G and 3GS) to your system.
> +         Note that you need userspace libraries and programs that are needed
> +         to pair your device with your system and that understand the iPhone
> +         protocol.
> +
> +         For more information: http://giagio.com/wiki/moin.cgi/iPhoneEthernetDriver
> +
>  endmenu
> diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
> index 82ea629..edb09c0 100644
> --- a/drivers/net/usb/Makefile
> +++ b/drivers/net/usb/Makefile
> @@ -23,4 +23,5 @@ obj-$(CONFIG_USB_NET_MCS7830) += mcs7830.o
>  obj-$(CONFIG_USB_USBNET)       += usbnet.o
>  obj-$(CONFIG_USB_NET_INT51X1)  += int51x1.o
>  obj-$(CONFIG_USB_CDC_PHONET)   += cdc-phonet.o
> +obj-$(CONFIG_USB_IPHETH)       += ipheth.o
>
> diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c
> new file mode 100644
> index 0000000..fd10331
> --- /dev/null
> +++ b/drivers/net/usb/ipheth.c
> @@ -0,0 +1,568 @@
> +/*
> + * 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/init.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>
> +
> +#define USB_VENDOR_APPLE        0x05ac
> +#define USB_PRODUCT_IPHONE      0x1290
> +#define USB_PRODUCT_IPHONE_3G   0x1292
> +#define USB_PRODUCT_IPHONE_3GS  0x1294
> +
> +#define IPHETH_USBINTF_CLASS    255
> +#define IPHETH_USBINTF_SUBCLASS 253
> +#define IPHETH_USBINTF_PROTO    1
> +
> +#define IPHETH_BUF_SIZE         1516
> +#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_CARRIER_CHECK 0x45
> +
> +#define IPHETH_CARRIER_CHECK_TIMEOUT round_jiffies_relative(1 * HZ)
> +#define IPHETH_CARRIER_ON       0x04
> +
> +static struct usb_device_id ipheth_table[] = {
> +       { USB_DEVICE_AND_INTERFACE_INFO(
> +               USB_VENDOR_APPLE, USB_PRODUCT_IPHONE,
> +               IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
> +               IPHETH_USBINTF_PROTO) },
> +       { USB_DEVICE_AND_INTERFACE_INFO(
> +               USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_3G,
> +               IPHETH_USBINTF_CLASS, IPHETH_USBINTF_SUBCLASS,
> +               IPHETH_USBINTF_PROTO) },
> +       { USB_DEVICE_AND_INTERFACE_INFO(
> +               USB_VENDOR_APPLE, USB_PRODUCT_IPHONE_3GS,
> +               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 sk_buff *tx_skb;
> +       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;
> +};
> +
> +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;
> +
> +       rx_urb = usb_alloc_urb(0, GFP_KERNEL);
> +       if (rx_urb == NULL)
> +               goto error;
> +
> +       tx_buf = usb_buffer_alloc(iphone->udev,
> +                                 IPHETH_BUF_SIZE,
> +                                 GFP_KERNEL,
> +                                 &tx_urb->transfer_dma);
> +       if (tx_buf == NULL)
> +               goto error;
> +
> +       rx_buf = usb_buffer_alloc(iphone->udev,
> +                                 IPHETH_BUF_SIZE,
> +                                 GFP_KERNEL,
> +                                 &rx_urb->transfer_dma);
> +       if (rx_buf == NULL)
> +               goto error;
> +
> +
> +       iphone->tx_urb = tx_urb;
> +       iphone->rx_urb = rx_urb;
> +       iphone->tx_buf = tx_buf;
> +       iphone->rx_buf = rx_buf;
> +       return 0;
> +
> +error:
> +       usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, rx_buf,
> +                       rx_urb->transfer_dma);
> +       usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, tx_buf,
> +                       tx_urb->transfer_dma);
> +       usb_free_urb(rx_urb);
> +       usb_free_urb(tx_urb);
> +       return -ENOMEM;
> +}
> +
> +static void ipheth_free_urbs(struct ipheth_device *iphone)
> +{
> +       usb_buffer_free(iphone->udev, IPHETH_BUF_SIZE, iphone->rx_buf,
> +                       iphone->rx_urb->transfer_dma);
> +       usb_buffer_free(iphone->udev, IPHETH_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 void ipheth_rcvbulk_callback(struct urb *urb)
> +{
> +       struct ipheth_device *dev;
> +       struct sk_buff *skb;
> +       int status;
> +       char *buf;
> +       int len;
> +
> +       dev = urb->context;
> +       if (dev == NULL)
> +               return;
> +
> +       status = urb->status;
> +       switch (status) {
> +       case -ENOENT:
> +       case -ECONNRESET:
> +       case -ESHUTDOWN:
> +               return;
> +       case 0:
> +               break;
> +       default:
> +               err("%s: urb status: %d", __func__, urb->status);
> +               return;
> +       }
> +
> +       len = urb->actual_length;
> +       buf = urb->transfer_buffer;
> +
> +       skb = dev_alloc_skb(NET_IP_ALIGN + len);
> +       if (!skb) {
> +               err("%s: dev_alloc_skb: -ENOMEM", __func__);
> +               dev->net->stats.rx_dropped++;
> +               return;
> +       }
> +
> +       skb_reserve(skb, NET_IP_ALIGN);
> +       memcpy(skb_put(skb, len), buf + NET_IP_ALIGN, len - NET_IP_ALIGN);
> +       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);
> +       ipheth_rx_submit(dev, GFP_ATOMIC);
> +}
> +
> +static void ipheth_sndbulk_callback(struct urb *urb)
> +{
> +       struct ipheth_device *dev;
> +
> +       dev = urb->context;
> +       if (dev == NULL)
> +               return;
> +
> +       if (urb->status != 0 &&
> +           urb->status != -ENOENT &&
> +           urb->status != -ECONNRESET &&
> +           urb->status != -ESHUTDOWN)
> +               err("%s: urb status: %d", __func__, urb->status);
> +
> +       dev_kfree_skb_irq(dev->tx_skb);
> +       netif_wake_queue(dev->net);
> +}
> +
> +static int ipheth_carrier_set(struct ipheth_device *dev)
> +{
> +       struct usb_device *udev = dev->udev;
> +       int retval;
> +
> +       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) {
> +               err("%s: usb_control_msg: %d", __func__, retval);
> +               return retval;
> +       }
> +
> +       if (dev->ctrl_buf[0] == IPHETH_CARRIER_ON)
> +               netif_carrier_on(dev->net);
> +       else
> +               netif_carrier_off(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) {
> +               err("%s: usb_control_msg: %d", __func__, retval);
> +       } else if (retval < ETH_ALEN) {
> +               err("%s: usb_control_msg: short packet: %d bytes",
> +                       __func__, retval);
> +               retval = -EINVAL;
> +       } else {
> +               memcpy(net->dev_addr, dev->ctrl_buf, ETH_ALEN);
> +               retval = 0;
> +       }
> +
> +       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, IPHETH_BUF_SIZE,
> +                         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)
> +               err("%s: usb_submit_urb: %d", __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;
> +
> +       schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT);
> +       netif_start_queue(net);
> +       return retval;
> +}
> +
> +static int ipheth_close(struct net_device *net)
> +{
> +       struct ipheth_device *dev = netdev_priv(net);
> +
> +       cancel_delayed_work_sync(&dev->carrier_work);
> +       netif_stop_queue(net);
> +       return 0;
> +}
> +
> +static int ipheth_tx(struct sk_buff *skb, struct net_device *net)
> +{
> +       struct ipheth_device *dev = netdev_priv(net);
> +       struct usb_device *udev = dev->udev;
> +       int retval;
> +
> +       /* Paranoid */
> +       if (skb->len > IPHETH_BUF_SIZE) {
> +               WARN(1, "%s: skb too large: %d bytes", __func__, skb->len);
> +               dev->net->stats.tx_dropped++;
> +               dev_kfree_skb_irq(skb);
> +               return NETDEV_TX_OK;
> +       }
> +
> +       memcpy(dev->tx_buf, skb->data, skb->len);
> +       if (skb->len < IPHETH_BUF_SIZE)
> +               memset(dev->tx_buf + skb->len, 0, IPHETH_BUF_SIZE - skb->len);
> +
> +       usb_fill_bulk_urb(dev->tx_urb, udev,
> +                         usb_sndbulkpipe(udev, dev->bulk_out),
> +                         dev->tx_buf, IPHETH_BUF_SIZE,
> +                         ipheth_sndbulk_callback,
> +                         dev);
> +       dev->tx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> +
> +       retval = usb_submit_urb(dev->tx_urb, GFP_ATOMIC);
> +       if (retval) {
> +               err("%s: usb_submit_urb: %d", __func__, retval);
> +               dev->net->stats.tx_errors++;
> +               dev_kfree_skb_irq(skb);
> +       } else {
> +               dev->tx_skb = skb;
> +
> +               dev->net->stats.tx_packets++;
> +               dev->net->stats.tx_bytes += skb->len;
> +               netif_stop_queue(net);
> +       }
> +
> +       return NETDEV_TX_OK;
> +}
> +
> +static void ipheth_tx_timeout(struct net_device *net)
> +{
> +       struct ipheth_device *dev = netdev_priv(net);
> +
> +       err("%s: TX timeout", __func__);
> +       dev->net->stats.tx_errors++;
> +       usb_unlink_urb(dev->tx_urb);
> +}
> +
> +static struct net_device_stats *ipheth_stats(struct net_device *net)
> +{
> +       struct ipheth_device *dev = netdev_priv(net);
> +       return &dev->net->stats;
> +}
> +
> +static u32 ipheth_ethtool_op_get_link(struct net_device *net)
> +{
> +       struct ipheth_device *dev = netdev_priv(net);
> +       return netif_carrier_ok(dev->net);
> +}
> +
> +static struct ethtool_ops ops = {
> +       .get_link = ipheth_ethtool_op_get_link
> +};
> +
> +static const struct net_device_ops ipheth_netdev_ops = {
> +       .ndo_open = &ipheth_open,
> +       .ndo_stop = &ipheth_close,
> +       .ndo_start_xmit = &ipheth_tx,
> +       .ndo_tx_timeout = &ipheth_tx_timeout,
> +       .ndo_get_stats = &ipheth_stats,
> +};
> +
> +static struct device_type ipheth_type = {
> +       .name   = "wwan",
> +};
> +
> +static int ipheth_probe(struct usb_interface *intf,
> +                       const struct usb_device_id *id)
> +{
> +       struct usb_device *udev = interface_to_usbdev(intf);
> +       struct usb_host_interface *hintf;
> +       struct usb_endpoint_descriptor *endp;
> +       struct ipheth_device *dev;
> +       struct net_device *netdev;
> +       int i;
> +       int retval;
> +
> +       netdev = alloc_etherdev(sizeof(struct ipheth_device));
> +       if (!netdev)
> +               return -ENOMEM;
> +
> +       netdev->netdev_ops = &ipheth_netdev_ops;
> +       netdev->watchdog_timeo = IPHETH_TX_TIMEOUT;
> +       strcpy(netdev->name, "wwan%d");
> +
> +       dev = netdev_priv(netdev);
> +       dev->udev = udev;
> +       dev->net = netdev;
> +       dev->intf = intf;
> +
> +       /* Set up endpoints */
> +       hintf = usb_altnum_to_altsetting(intf, IPHETH_ALT_INTFNUM);
> +       if (hintf == NULL) {
> +               retval = -ENODEV;
> +               err("Unable to find alternate settings interface");
> +               goto err_endpoints;
> +       }
> +
> +       for (i = 0; i < hintf->desc.bNumEndpoints; i++) {
> +               endp = &hintf->endpoint[i].desc;
> +               if (usb_endpoint_is_bulk_in(endp))
> +                       dev->bulk_in = endp->bEndpointAddress;
> +               else if (usb_endpoint_is_bulk_out(endp))
> +                       dev->bulk_out = endp->bEndpointAddress;
> +       }
> +       if (!(dev->bulk_in && dev->bulk_out)) {
> +               retval = -ENODEV;
> +               err("Unable to find endpoints");
> +               goto err_endpoints;
> +       }
> +
> +       dev->ctrl_buf = kmalloc(IPHETH_CTRL_BUF_SIZE, GFP_KERNEL);
> +       if (dev->ctrl_buf == NULL) {
> +               retval = -ENOMEM;
> +               goto err_alloc_ctrl_buf;
> +       }
> +
> +       retval = ipheth_get_macaddr(dev);
> +       if (retval)
> +               goto err_get_macaddr;
> +
> +       INIT_DELAYED_WORK(&dev->carrier_work, ipheth_carrier_check_work);
> +
> +       retval = ipheth_alloc_urbs(dev);
> +       if (retval) {
> +               err("error allocating urbs: %d", retval);
> +               goto err_alloc_urbs;
> +       }
> +
> +       usb_set_intfdata(intf, dev);
> +
> +       SET_NETDEV_DEV(netdev, &intf->dev);
> +       SET_ETHTOOL_OPS(netdev, &ops);
> +       SET_NETDEV_DEVTYPE(netdev, &ipheth_type);
> +
> +       retval = register_netdev(netdev);
> +       if (retval) {
> +               err("error registering netdev: %d", retval);
> +               retval = -EIO;
> +               goto err_register_netdev;
> +       }
> +
> +       dev_info(&intf->dev, "Apple iPhone USB Ethernet device attached\n");
> +       return 0;
> +
> +err_register_netdev:
> +       ipheth_free_urbs(dev);
> +err_alloc_urbs:
> +err_get_macaddr:
> +err_alloc_ctrl_buf:
> +       kfree(dev->ctrl_buf);
> +err_endpoints:
> +       free_netdev(netdev);
> +       return retval;
> +}
> +
> +static void ipheth_disconnect(struct usb_interface *intf)
> +{
> +       struct ipheth_device *dev;
> +
> +       dev = usb_get_intfdata(intf);
> +       if (dev != NULL) {
> +               unregister_netdev(dev->net);
> +               ipheth_kill_urbs(dev);
> +               ipheth_free_urbs(dev);
> +               kfree(dev->ctrl_buf);
> +               free_netdev(dev->net);
> +       }
> +       usb_set_intfdata(intf, NULL);
> +       dev_info(&intf->dev, "Apple iPhone USB Ethernet now disconnected\n");
> +}
> +
> +static struct usb_driver ipheth_driver = {
> +       .name =         "ipheth",
> +       .probe =        ipheth_probe,
> +       .disconnect =   ipheth_disconnect,
> +       .id_table =     ipheth_table,
> +};
> +
> +static int __init ipheth_init(void)
> +{
> +       int retval;
> +
> +       retval = usb_register(&ipheth_driver);
> +       if (retval) {
> +               err("usb_register failed: %d", retval);
> +               return retval;
> +       }
> +       return 0;
> +}
> +
> +static void __exit ipheth_exit(void)
> +{
> +       usb_deregister(&ipheth_driver);
> +}
> +
> +module_init(ipheth_init);
> +module_exit(ipheth_exit);
> +
> +MODULE_AUTHOR("Diego Giagio <diego@giagio.com>");
> +MODULE_DESCRIPTION("Apple iPhone USB Ethernet driver");
> +MODULE_LICENSE("Dual BSD/GPL");
> --
> 1.7.0
>
>



-- 
Diego Giagio
diego@giagio.com / diego@FreeBSD.org

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH] drivers/net/usb: Add new driver ipheth
  2010-04-21 14:15   ` Diego Giagio
@ 2010-04-22  5:44     ` David Miller
  0 siblings, 0 replies; 34+ messages in thread
From: David Miller @ 2010-04-22  5:44 UTC (permalink / raw)
  To: diego
  Cc: agimenez, linux-kernel, dborca, James.Bottomley, ralf, gregkh,
	jonas.sjoquist, torgny.johansson, steve.glendinning, dbrownell,
	omar.oberthur, remi.denis-courmont, netdev, linux-usb

From: Diego Giagio <diego@giagio.com>
Date: Wed, 21 Apr 2010 11:15:15 -0300

> On Sun, Apr 18, 2010 at 3:35 PM, L. Alberto Giménez
> <agimenez@sysvalve.es> wrote:
>> From: Diego Giagio <diego@giagio.com>
>>
>> Add new driver to use tethering with an iPhone device. After initial submission,
>> apply fixes to fit the new driver into the kernel standards.
>>
>> There are still a couple of minor (almost cosmetic-level) issues, but the driver
>> is fully functional right now.
>>
> 
> Signed-off-by: Diego Giagio <diego@giagio.com>
> Cc: Daniel Borca <dborca@yahoo.com>

Applied, thanks everyone.

^ permalink raw reply	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2010-04-22  5:44 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-03-30 21:34 [PATCH] Staging: Add new driver ipheth L. Alberto Giménez
2010-03-30 21:34 ` L. Alberto Giménez
2010-03-30 21:45   ` Greg KH
2010-03-30 21:58     ` "L. Alberto Giménez"
2010-03-30 22:11       ` Greg KH
2010-03-31 14:33       ` Pavel Machek
2010-03-31 14:44         ` Matthew Garrett
2010-03-31 19:47         ` "L. Alberto Giménez"
2010-03-30 23:01   ` [PATCH] drivers/net/usb: " L. Alberto Giménez
2010-03-30 23:25     ` Greg KH
2010-03-31 19:42 ` [PATCHv3] " L. Alberto Giménez
2010-03-31 20:33   ` Oliver Neukum
2010-03-31 21:38     ` "L. Alberto Giménez"
2010-04-02 18:23     ` "L. Alberto Giménez"
2010-04-04  7:24       ` Oliver Neukum
2010-04-05 18:51         ` "L. Alberto Giménez"
2010-03-31 23:18   ` Ben Hutchings
2010-03-31 23:25     ` Greg KH
2010-03-31 23:28       ` Ben Hutchings
2010-04-02 17:15     ` "L. Alberto Giménez"
2010-04-02 17:21       ` Ben Hutchings
2010-04-02 17:53         ` "L. Alberto Giménez"
2010-04-02 18:35           ` Ben Hutchings
2010-04-07 22:11 ` [PATCH Resubmission] " L. Alberto Giménez
2010-04-07 22:37   ` Roland Dreier
2010-04-08  6:35   ` Oliver Neukum
2010-04-13  8:15   ` David Miller
2010-04-13 19:03     ` "L. Alberto Giménez"
2010-04-13 21:29       ` David Miller
2010-04-15 19:46 ` [PATCH Resubmission v2] " L. Alberto Giménez
2010-04-16  6:44   ` David Miller
2010-04-18 18:35 ` [PATCH] " L. Alberto Giménez
2010-04-21 14:15   ` Diego Giagio
2010-04-22  5:44     ` David Miller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).