Add USB host ethernet adapter support
authorSimon Glass <sjg@chromium.org>
Wed, 16 Feb 2011 19:14:33 +0000 (11:14 -0800)
committerRemy Bohmer <linux@bohmer.net>
Sat, 19 Feb 2011 19:32:36 +0000 (20:32 +0100)
This adds support for using USB Ethernet dongles in host mode. This is just
the framework - drivers will come later. A new config option called
CONFIG_USB_HOST_ETHER can be defined in board config files to switch this
on.

The was originally written by NVIDIA and was cleaned up for release by the
Chromium authors.

Signed-off-by: Simon Glass <sjg@chromium.org>
Makefile
common/cmd_usb.c
common/usb.c
doc/README.usb
drivers/usb/eth/Makefile [new file with mode: 0644]
drivers/usb/eth/usb_ether.c [new file with mode: 0644]
include/usb.h
include/usb_ether.h [new file with mode: 0644]
net/eth.c

index 61331604094d2b8206fadc0f2df8e783919121c8..dc2e3d8b3144b63bc66c57fc4fdcc4b472e1fd60 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -235,6 +235,7 @@ endif
 LIBS += drivers/rtc/librtc.o
 LIBS += drivers/serial/libserial.o
 LIBS += drivers/twserial/libtws.o
+LIBS += drivers/usb/eth/libusb_eth.a
 LIBS += drivers/usb/gadget/libusb_gadget.o
 LIBS += drivers/usb/host/libusb_host.o
 LIBS += drivers/usb/musb/libusb_musb.o
index b04a8df764db9bde494ed42bbfd7cd279eeb856b..b5731a7bb8d2235df31e607f74ac514df1bcd71b 100644 (file)
@@ -34,6 +34,9 @@
 #ifdef CONFIG_USB_STORAGE
 static int usb_stor_curr_dev = -1; /* current device */
 #endif
+#ifdef CONFIG_USB_HOST_ETHER
+static int usb_ether_curr_dev = -1; /* current ethernet device */
+#endif
 
 /* some display routines (info command) */
 char *usb_get_class_desc(unsigned char dclass)
@@ -522,11 +525,16 @@ int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
                usb_stop();
                printf("(Re)start USB...\n");
                i = usb_init();
+               if (i >= 0) {
 #ifdef CONFIG_USB_STORAGE
-               /* try to recognize storage devices immediately */
-               if (i >= 0)
+                       /* try to recognize storage devices immediately */
                        usb_stor_curr_dev = usb_stor_scan(1);
 #endif
+#ifdef CONFIG_USB_HOST_ETHER
+                       /* try to recognize ethernet devices immediately */
+                       usb_ether_curr_dev = usb_host_eth_scan(1);
+#endif
+               }
                return 0;
        }
        if (strncmp(argv[1], "stop", 4) == 0) {
index 44a435af6e67ed7de122a55e770c3d09f7ec95c5..4f7c520b344d7676d20a604be5c63d96b672efac 100644 (file)
@@ -145,10 +145,14 @@ int usb_stop(void)
 /*
  * disables the asynch behaviour of the control message. This is used for data
  * transfers that uses the exclusiv access to the control and bulk messages.
+ * Returns the old value so it can be restored later.
  */
-void usb_disable_asynch(int disable)
+int usb_disable_asynch(int disable)
 {
+       int old_value = asynch_allowed;
+
        asynch_allowed = !disable;
+       return old_value;
 }
 
 
index b3bcb91f40681ef67decade555f061058d996409..9aa4f62ddfae0c738c423beb5fa2a5f90d49ae31 100644 (file)
@@ -28,7 +28,8 @@ USB Support for PIP405 and MIP405 (UHCI)
 The USB support is implemented on the base of the UHCI Host
 controller.
 
-Currently supported are USB Hubs, USB Keyboards and USB Floppys.
+Currently supported are USB Hubs, USB Keyboards, USB Floppys, USB
+flash sticks and USB network adaptors.
 Tested with a TEAC Floppy TEAC FD-05PUB and Chicony KU-8933 Keyboard.
 
 How it works:
@@ -78,3 +79,4 @@ CONFIG_USB_UHCI           defines the lowlevel part.A lowlevel part must be defined
                    if using CONFIG_CMD_USB
 CONFIG_USB_KEYBOARD enables the USB Keyboard
 CONFIG_USB_STORAGE  enables the USB storage devices
+CONFIG_USB_HOST_ETHER  enables USB ethernet dongle support
diff --git a/drivers/usb/eth/Makefile b/drivers/usb/eth/Makefile
new file mode 100644 (file)
index 0000000..a0f5676
--- /dev/null
@@ -0,0 +1,45 @@
+#
+# Copyright (c) 2011 The Chromium OS Authors.
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+LIB    := $(obj)libusb_eth.a
+
+# new USB host ethernet layer dependencies
+COBJS-$(CONFIG_USB_HOST_ETHER) += usb_ether.o
+
+COBJS  := $(COBJS-y)
+SRCS   := $(COBJS:.o=.c)
+OBJS   := $(addprefix $(obj),$(COBJS))
+
+all:   $(LIB)
+
+$(LIB):        $(obj).depend $(OBJS)
+       $(AR) $(ARFLAGS) $@ $(OBJS)
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/drivers/usb/eth/usb_ether.c b/drivers/usb/eth/usb_ether.c
new file mode 100644 (file)
index 0000000..c2342ed
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <usb.h>
+
+#include "usb_ether.h"
+
+typedef void (*usb_eth_before_probe)(void);
+typedef int (*usb_eth_probe)(struct usb_device *dev, unsigned int ifnum,
+                       struct ueth_data *ss);
+typedef int (*usb_eth_get_info)(struct usb_device *dev, struct ueth_data *ss,
+                       struct eth_device *dev_desc);
+
+struct usb_eth_prob_dev {
+       usb_eth_before_probe    before_probe; /* optional */
+       usb_eth_probe                   probe;
+       usb_eth_get_info                get_info;
+};
+
+/* driver functions go here, each bracketed by #ifdef CONFIG_USB_ETHER_xxx */
+static const struct usb_eth_prob_dev prob_dev[] = {
+       { },            /* END */
+};
+
+static int usb_max_eth_dev; /* number of highest available usb eth device */
+static struct ueth_data usb_eth[USB_MAX_ETH_DEV];
+
+/*******************************************************************************
+ * tell if current ethernet device is a usb dongle
+ */
+int is_eth_dev_on_usb_host(void)
+{
+       int i;
+       struct eth_device *dev = eth_get_dev();
+
+       if (dev) {
+               for (i = 0; i < usb_max_eth_dev; i++)
+                       if (&usb_eth[i].eth_dev == dev)
+                               return 1;
+       }
+       return 0;
+}
+
+/*
+ * Given a USB device, ask each driver if it can support it, and attach it
+ * to the first driver that says 'yes'
+ */
+static void probe_valid_drivers(struct usb_device *dev)
+{
+       int j;
+
+       for (j = 0; prob_dev[j].probe && prob_dev[j].get_info; j++) {
+               if (!prob_dev[j].probe(dev, 0, &usb_eth[usb_max_eth_dev]))
+                       continue;
+               /*
+                * ok, it is a supported eth device. Get info and fill it in
+                */
+               if (prob_dev[j].get_info(dev,
+                       &usb_eth[usb_max_eth_dev],
+                       &usb_eth[usb_max_eth_dev].eth_dev)) {
+                       /* found proper driver */
+                       /* register with networking stack */
+                       usb_max_eth_dev++;
+
+                       /*
+                        * usb_max_eth_dev must be incremented prior to this
+                        * call since eth_current_changed (internally called)
+                        * relies on it
+                        */
+                       eth_register(&usb_eth[usb_max_eth_dev - 1].eth_dev);
+                       break;
+                       }
+               }
+       }
+
+/*******************************************************************************
+ * scan the usb and reports device info
+ * to the user if mode = 1
+ * returns current device or -1 if no
+ */
+int usb_host_eth_scan(int mode)
+{
+       int i, old_async;
+       struct usb_device *dev;
+
+
+       if (mode == 1)
+               printf("       scanning bus for ethernet devices... ");
+
+       old_async = usb_disable_asynch(1); /* asynch transfer not allowed */
+
+       for (i = 0; i < USB_MAX_ETH_DEV; i++)
+               memset(&usb_eth[i], 0, sizeof(usb_eth[i]));
+
+       for (i = 0; prob_dev[i].probe; i++) {
+               if (prob_dev[i].before_probe)
+                       prob_dev[i].before_probe();
+       }
+
+       usb_max_eth_dev = 0;
+       for (i = 0; i < USB_MAX_DEVICE; i++) {
+               dev = usb_get_dev_index(i); /* get device */
+               debug("i=%d\n", i);
+               if (dev == NULL)
+                       break; /* no more devices avaiable */
+
+               /* find valid usb_ether driver for this device, if any */
+               probe_valid_drivers(dev);
+
+               /* check limit */
+               if (usb_max_eth_dev == USB_MAX_ETH_DEV) {
+                       printf("max USB Ethernet Device reached: %d stopping\n",
+                               usb_max_eth_dev);
+                       break;
+               }
+       } /* for */
+
+       usb_disable_asynch(old_async); /* restore asynch value */
+       printf("%d Ethernet Device(s) found\n", usb_max_eth_dev);
+       if (usb_max_eth_dev > 0)
+               return 0;
+       return -1;
+}
+
index 98576b73a41dc707bd7563d9ff6a066bda10bb6c..53603a5582256bd90ae47f2dbc7cccab5b3d0010 100644 (file)
@@ -168,6 +168,13 @@ int usb_stor_info(void);
 
 #endif
 
+#ifdef CONFIG_USB_HOST_ETHER
+
+#define USB_MAX_ETH_DEV 5
+int usb_host_eth_scan(int mode);
+
+#endif
+
 #ifdef CONFIG_USB_KEYBOARD
 
 int drv_usb_kbd_init(void);
@@ -191,7 +198,7 @@ int usb_bulk_msg(struct usb_device *dev, unsigned int pipe,
                        void *data, int len, int *actual_length, int timeout);
 int usb_submit_int_msg(struct usb_device *dev, unsigned long pipe,
                        void *buffer, int transfer_len, int interval);
-void usb_disable_asynch(int disable);
+int usb_disable_asynch(int disable);
 int usb_maxpacket(struct usb_device *dev, unsigned long pipe);
 inline void wait_ms(unsigned long ms);
 int usb_get_configuration_no(struct usb_device *dev, unsigned char *buffer,
diff --git a/include/usb_ether.h b/include/usb_ether.h
new file mode 100644 (file)
index 0000000..31cbc8d
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __USB_ETHER_H__
+#define __USB_ETHER_H__
+
+#include <net.h>
+
+/*
+ *     IEEE 802.3 Ethernet magic constants.  The frame sizes omit the preamble
+ *     and FCS/CRC (frame check sequence).
+ */
+#define ETH_ALEN       6               /* Octets in one ethernet addr   */
+#define ETH_HLEN       14              /* Total octets in header.       */
+#define ETH_ZLEN       60              /* Min. octets in frame sans FCS */
+#define ETH_DATA_LEN   1500            /* Max. octets in payload        */
+#define ETH_FRAME_LEN  PKTSIZE_ALIGN   /* Max. octets in frame sans FCS */
+#define ETH_FCS_LEN    4               /* Octets in the FCS             */
+
+struct ueth_data {
+       /* eth info */
+       struct eth_device eth_dev;              /* used with eth_register */
+       int phy_id;                                             /* mii phy id */
+
+       /* usb info */
+       struct usb_device *pusb_dev;    /* this usb_device */
+       unsigned char   ifnum;                  /* interface number */
+       unsigned char   ep_in;                  /* in endpoint */
+       unsigned char   ep_out;                 /* out ....... */
+       unsigned char   ep_int;                 /* interrupt . */
+       unsigned char   subclass;               /* as in overview */
+       unsigned char   protocol;               /* .............. */
+       unsigned char   irqinterval;    /* Intervall for IRQ Pipe */
+
+       /* private fields for each driver can go here if needed */
+};
+
+/*
+ * Function definitions for each USB ethernet driver go here, bracketed by
+ * #ifdef CONFIG_USB_ETHER_xxx...#endif
+ */
+
+#endif /* __USB_ETHER_H__ */
index 6082c9007278d8a742702a17c3e4e81d86582164..cec0387891280530ebdadec14de146d442e224cf 100644 (file)
--- a/net/eth.c
+++ b/net/eth.c
@@ -166,20 +166,33 @@ int eth_get_dev_index (void)
        return (0);
 }
 
-int eth_register(struct eth_device* dev)
+static void eth_current_changed(void)
 {
-       struct eth_device *d;
-
-       if (!eth_devices) {
-               eth_current = eth_devices = dev;
 #ifdef CONFIG_NET_MULTI
+       {
+               char *act = getenv("ethact");
                /* update current ethernet name */
+               if (eth_current)
                {
-                       char *act = getenv("ethact");
                        if (act == NULL || strcmp(act, eth_current->name) != 0)
                                setenv("ethact", eth_current->name);
                }
+               /*
+                * remove the variable completely if there is no active
+                * interface
+                */
+               else if (act != NULL)
+                       setenv("ethact", NULL);
+       }
 #endif
+}
+
+int eth_register(struct eth_device *dev)
+{
+       struct eth_device *d;
+       if (!eth_devices) {
+               eth_current = eth_devices = dev;
+               eth_current_changed();
        } else {
                for (d=eth_devices; d->next!=eth_devices; d=d->next)
                        ;
@@ -271,14 +284,7 @@ int eth_initialize(bd_t *bis)
                        dev = dev->next;
                } while(dev != eth_devices);
 
-               /* update current ethernet name */
-               if (eth_current) {
-                       char *act = getenv("ethact");
-                       if (act == NULL || strcmp(act, eth_current->name) != 0)
-                               setenv("ethact", eth_current->name);
-               } else
-                       setenv("ethact", NULL);
-
+               eth_current_changed();
                putc ('\n');
        }
 
@@ -466,10 +472,7 @@ void eth_try_another(int first_restart)
 
        eth_current = eth_current->next;
 
-       /* update current ethernet name */
-       act = getenv("ethact");
-       if (act == NULL || strcmp(act, eth_current->name) != 0)
-               setenv("ethact", eth_current->name);
+       eth_current_changed();
 
        if (first_failed == eth_current) {
                NetRestartWrap = 1;
@@ -500,7 +503,7 @@ void eth_set_current(void)
                } while (old_current != eth_current);
        }
 
-       setenv("ethact", eth_current->name);
+       eth_current_changed();
 }
 
 char *eth_get_name (void)