Add initial implementation of smsc95xx usb ethernet driver
on raspberry pi 3 device.
-- [Patchset 1] May/16/2017
Brought up the device but not actual send/recv packets.
You can see the device named sms0 by typing,
XVisor# net ports
-- [Patchset 2] May/17/2017
Now you can use network device through sms0 from the guest.
To test type below command with CONFIG_NET_STACK_LWIP config.
Make sure the device should under a dhcp-supported router.
XVisor# ipconfig dhcp
XVisor# ping 8.8.8.8
---
.../board/generic/dts/bcm2837/bcm2837-rpi-3-b.dtsi | 5 +
arch/arm/board/generic/dts/bcm2837/bcm2837.dtsi | 4 +-
.../generic/dts/bcm2837/bcm283x-rpi-smsc9514.dtsi | 20 +
arch/arm/configs/generic-v8-defconfig | 2 +
drivers/usb/devices/smsc95xx.c | 1083 ++++++++++++++++++++
drivers/usb/devices/smsc95xx.h | 364 +++++++
drivers/usb/storage/
objects.mk | 2 +-
drivers/usb/storage/openconf.cfg | 12 +
emulators/net/virtio_net.c | 5 +
9 files changed, 1495 insertions(+), 2 deletions(-)
create mode 100644 arch/arm/board/generic/dts/bcm2837/bcm283x-rpi-smsc9514.dtsi
create mode 100644 drivers/usb/devices/smsc95xx.c
create mode 100644 drivers/usb/devices/smsc95xx.h
diff --git a/arch/arm/board/generic/dts/bcm2837/bcm2837-rpi-3-b.dtsi b/arch/arm/board/generic/dts/bcm2837/bcm2837-rpi-3-b.dtsi
index 4d37c11..b46b923 100644
--- a/arch/arm/board/generic/dts/bcm2837/bcm2837-rpi-3-b.dtsi
+++ b/arch/arm/board/generic/dts/bcm2837/bcm2837-rpi-3-b.dtsi
@@ -1,5 +1,6 @@
/include/ "bcm2837.dtsi"
+/include/ "bcm283x-rpi-smsc9514.dtsi"
/ {
compatible = "raspberrypi,3-model-b", "brcm,bcm2837";
@@ -27,3 +28,7 @@
status = "okay";
bus-width = <4>;
};
+
+&usb {
+ status = "okay";
+};
diff --git a/arch/arm/board/generic/dts/bcm2837/bcm2837.dtsi b/arch/arm/board/generic/dts/bcm2837/bcm2837.dtsi
index 33560ee..b04d976 100644
--- a/arch/arm/board/generic/dts/bcm2837/bcm2837.dtsi
+++ b/arch/arm/board/generic/dts/bcm2837/bcm2837.dtsi
@@ -161,7 +161,9 @@
usb: usb@3f980000 {
compatible = "brcm,bcm2836-usb";
reg = <0x3f980000 0x10000>;
- interrupts = <2 9>;
+ interrupts = <1 9>;
+ #address-cells = <1>;
+ #size-cells = <0>;
status = "disabled";
};
};
diff --git a/arch/arm/board/generic/dts/bcm2837/bcm283x-rpi-smsc9514.dtsi b/arch/arm/board/generic/dts/bcm2837/bcm283x-rpi-smsc9514.dtsi
new file mode 100644
index 0000000..cd934c0
--- /dev/null
+++ b/arch/arm/board/generic/dts/bcm2837/bcm283x-rpi-smsc9514.dtsi
@@ -0,0 +1,20 @@
+/ {
+ aliases {
+ ethernet = ðernet;
+ };
+};
+
+&usb {
+ usb1@1 {
+ compatible = "usb424,9514";
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethernet: usbether@1 {
+ compatible = "usb424,ec00";
+ reg = <1>;
+ switch = "br0";
+ };
+ };
+};
diff --git a/arch/arm/configs/generic-v8-defconfig b/arch/arm/configs/generic-v8-defconfig
index 1e80f66..d38fe5a 100644
--- a/arch/arm/configs/generic-v8-defconfig
+++ b/arch/arm/configs/generic-v8-defconfig
@@ -48,6 +48,8 @@ CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_BCM2835=y
CONFIG_USB=y
CONFIG_USB_STORAGE=y
+CONFIG_USB_HOST_ETHER=y
+CONFIG_USB_ETHER_SMSC95XX=y
CONFIG_USB_DWC2_HCD=y
CONFIG_INPUT=y
CONFIG_FB=y
diff --git a/drivers/usb/devices/smsc95xx.c b/drivers/usb/devices/smsc95xx.c
new file mode 100644
index 0000000..fdf96ef
--- /dev/null
+++ b/drivers/usb/devices/smsc95xx.c
@@ -0,0 +1,1083 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ * Copyright (c) 2011 The Chromium OS Authors.
+ * Copyright (C) 2009 NVIDIA, Corporation
+ * Copyright (C) 2007-2008 SMSC (Steve Glendinning)
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <vmm_modules.h>
+#include <vmm_stdio.h>
+#include <vmm_wallclock.h>
+#include <vmm_delay.h>
+#include <net/vmm_protocol.h>
+#include <libs/unaligned.h>
+
+#include <arch_cache.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/mii.h>
+#include <linux/workqueue.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/of_net.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+#include <linux/mii.h>
+#include <drv/usb.h>
+#include <smsc95xx.h>
+
+#define MODULE_DESC "smsc95xx ethernet Driver"
+#define MODULE_AUTHOR "Wayne Kim"
+#define MODULE_LICENSE "GPL"
+#define MODULE_IPRIORITY (USB_CORE_IPRIORITY + 2)
+#define MODULE_INIT smsc95xx_eth_init
+#define MODULE_EXIT smsc95xx_eth_exit
+
+#undef DEBUG
+
+#if DEBUG
+#define debug(fmt, args...) vmm_printf("%s: " fmt, __FUNCTION__ , ## args)
+#else
+#define debug(fmt, args...)
+#endif
+
+/* (WDK) Adaptation */
+#define CONFIG_DM_ETH 1
+
+typedef unsigned long int uintptr_t;
+#define ARCH_DMA_MINALIGN ARCH_CACHE_LINE_SIZE
+
+#define ROUND(a,b) (((a) + (b) - 1) & ~((b) - 1))
+#define PAD_COUNT(s, pad) (((s) - 1) / (pad) + 1)
+#define PAD_SIZE(s, pad) (PAD_COUNT(s, pad) * pad)
+#define ALLOC_ALIGN_BUFFER_PAD(type, name, size, align, pad) \
+ char __##name[ROUND(PAD_SIZE((size) * sizeof(type), pad), align) \
+ + (align - 1)]; \
+ \
+ type *name = (type *)ALIGN((uintptr_t)__##name, align)
+#define ALLOC_ALIGN_BUFFER(type, name, size, align) \
+ ALLOC_ALIGN_BUFFER_PAD(type, name, size, align, 1)
+#define ALLOC_CACHE_ALIGN_BUFFER_PAD(type, name, size, pad) \
+ ALLOC_ALIGN_BUFFER_PAD(type, name, size, ARCH_DMA_MINALIGN, pad)
+#define ALLOC_CACHE_ALIGN_BUFFER(type, name, size) \
+ ALLOC_ALIGN_BUFFER(type, name, size, ARCH_DMA_MINALIGN)
+
+struct ueth_data {
+ /* eth info */
+ int phy_id; /* mii phy id */
+ struct vmm_thread *rx_thread; /* rx poll thread */
+
+ /* 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 */
+};
+
+/* Some extra defines */
+#define HS_USB_PKT_SIZE 512
+#define FS_USB_PKT_SIZE 64
+
+/* 5/33 is lower limit for BURST_CAP to work */
+#define DEFAULT_HS_BURST_CAP_SIZE (5 * HS_USB_PKT_SIZE)
+#define DEFAULT_FS_BURST_CAP_SIZE (33 * FS_USB_PKT_SIZE)
+#define DEFAULT_BULK_IN_DELAY 0x00002000
+#define MAX_SINGLE_PACKET_SIZE 2048
+#define EEPROM_MAC_OFFSET 0x01
+#define SMSC95XX_INTERNAL_PHY_ID 1
+#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */
+
+/* local defines */
+#define SMSC95XX_BASE_NAME "sms"
+#define USB_CTRL_SET_TIMEOUT 5000
+#define USB_CTRL_GET_TIMEOUT 5000
+#define USB_BULK_SEND_TIMEOUT 5000
+#define USB_BULK_RECV_TIMEOUT 5000
+
+#define RX_URB_SIZE DEFAULT_HS_BURST_CAP_SIZE
+#define PHY_CONNECT_TIMEOUT 5000
+
+/* driver private */
+struct smsc95xx_private {
+#ifdef CONFIG_DM_ETH
+ struct ueth_data ueth;
+#endif
+ size_t rx_urb_size; /* maximum USB URB size */
+ u32 mac_cr; /* MAC control register value */
+ int have_hwaddr; /* 1 if we have a hardware MAC address */
+};
+
+/*
+ * Smsc95xx infrastructure commands
+ */
+static int smsc95xx_write_reg(struct usb_device *udev, u32 index, u32 data)
+{
+ int ret, len;
+ ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1);
+
+ tmpbuf[0] = cpu_to_le32(data);
+
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ USB_VENDOR_REQUEST_WRITE_REGISTER,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, tmpbuf, sizeof(data), &len,
+ USB_CTRL_SET_TIMEOUT);
+
+ if (ret != VMM_OK || len != sizeof(data)) {
+ debug("smsc95xx_write_reg failed: index=%d, data=%d, len=%d\n",
+ index, data, len);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int smsc95xx_read_reg(struct usb_device *udev, u32 index, u32 *data)
+{
+ int ret, len;
+ ALLOC_CACHE_ALIGN_BUFFER(u32, tmpbuf, 1);
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ USB_VENDOR_REQUEST_READ_REGISTER,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, tmpbuf, sizeof(*data), &len,
+ USB_CTRL_GET_TIMEOUT);
+
+ if (ret != VMM_OK || len != sizeof(*data)) {
+ debug("smsc95xx_read_reg failed: index=%d, len=%d\n",
+ index, len);
+ return -EIO;
+ }
+
+ *data = le32_to_cpu(tmpbuf[0]);
+ return 0;
+}
+
+/* Loop until the read is completed with timeout */
+static int smsc95xx_phy_wait_not_busy(struct usb_device *udev)
+{
+ struct vmm_timeval limit, now;
+ u32 val;
+
+ vmm_wallclock_get_local_time(&now);
+ limit = vmm_timeval_add(now, (struct vmm_timeval){1, 0});
+
+ do {
+ smsc95xx_read_reg(udev, MII_ADDR, &val);
+ if (!(val & MII_BUSY_))
+ return 0;
+
+ vmm_wallclock_get_local_time(&now);
+ } while (vmm_timeval_compare(&limit, &now) > 0);
+
+ return -ETIMEDOUT;
+}
+
+static int smsc95xx_mdio_read(struct usb_device *udev, int phy_id, int idx)
+{
+ u32 val, addr;
+
+ /* confirm MII not busy */
+ if (smsc95xx_phy_wait_not_busy(udev)) {
+ debug("MII is busy in smsc95xx_mdio_read\n");
+ return -ETIMEDOUT;
+ }
+
+ /* set the address, index & direction (read from PHY) */
+ addr = (phy_id << 11) | (idx << 6) | MII_READ_;
+ smsc95xx_write_reg(udev, MII_ADDR, addr);
+
+ if (smsc95xx_phy_wait_not_busy(udev)) {
+ debug("Timed out reading MII reg %02X\n", idx);
+ return -ETIMEDOUT;
+ }
+
+ smsc95xx_read_reg(udev, MII_DATA, &val);
+
+ return (u16)(val & 0xFFFF);
+}
+
+static void smsc95xx_mdio_write(struct usb_device *udev, int phy_id, int idx,
+ int regval)
+{
+ u32 val, addr;
+
+ /* confirm MII not busy */
+ if (smsc95xx_phy_wait_not_busy(udev)) {
+ debug("MII is busy in smsc95xx_mdio_write\n");
+ return;
+ }
+
+ val = regval;
+ smsc95xx_write_reg(udev, MII_DATA, val);
+
+ /* set the address, index & direction (write to PHY) */
+ addr = (phy_id << 11) | (idx << 6) | MII_WRITE_;
+ smsc95xx_write_reg(udev, MII_ADDR, addr);
+
+ if (smsc95xx_phy_wait_not_busy(udev))
+ debug("Timed out writing MII reg %02X\n", idx);
+}
+
+static int smsc95xx_eeprom_confirm_not_busy(struct usb_device *udev)
+{
+ struct vmm_timeval limit, now;
+ u32 val;
+
+ vmm_wallclock_get_local_time(&now);
+ limit = vmm_timeval_add(now, (struct vmm_timeval){10, 0});
+ do {
+ smsc95xx_read_reg(udev, E2P_CMD, &val);
+ if (!(val & E2P_CMD_BUSY_))
+ return 0;
+
+ vmm_usleep(40);
+ vmm_wallclock_get_local_time(&now);
+ } while (vmm_timeval_compare(&limit, &now) > 0);
+
+ debug("EEPROM is busy\n");
+ return -ETIMEDOUT;
+}
+
+static int smsc95xx_wait_eeprom(struct usb_device *udev)
+{
+ struct vmm_timeval limit, now;
+ u32 val;
+
+ vmm_wallclock_get_local_time(&now);
+ limit = vmm_timeval_add(now, (struct vmm_timeval){10, 0});
+ do {
+ smsc95xx_read_reg(udev, E2P_CMD, &val);
+ if (!(val & E2P_CMD_BUSY_) || (val & E2P_CMD_TIMEOUT_))
+ break;
+
+ vmm_usleep(40);
+ vmm_wallclock_get_local_time(&now);
+ } while (vmm_timeval_compare(&limit, &now) > 0);
+
+ if (val & (E2P_CMD_TIMEOUT_ | E2P_CMD_BUSY_)) {
+ debug("EEPROM read operation timeout\n");
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+static int smsc95xx_read_eeprom(struct usb_device *udev, u32 offset, u32 length,
+ u8 *data)
+{
+ u32 val;
+ int i, ret;
+
+ ret = smsc95xx_eeprom_confirm_not_busy(udev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < length; i++) {
+ val = E2P_CMD_BUSY_ | E2P_CMD_READ_ | (offset & E2P_CMD_ADDR_);
+ smsc95xx_write_reg(udev, E2P_CMD, val);
+
+ ret = smsc95xx_wait_eeprom(udev);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(udev, E2P_DATA, &val);
+ if (ret < 0)
+ return ret;
+
+ data[i] = val & 0xFF;
+ offset++;
+ }
+ return 0;
+}
+
+/*
+ * mii_nway_restart - restart NWay (autonegotiation) for this interface
+ *
+ * Returns 0 on success, negative on error.
+ */
+static int smsc95xx_mii_nway_restart(struct usb_device *udev, struct ueth_data *dev)
+{
+ int bmcr;
+ int r = -1;
+
+ /* if autoneg is off, it's an error */
+ bmcr = smsc95xx_mdio_read(udev, dev->phy_id, MII_BMCR);
+
+ if (bmcr & BMCR_ANENABLE) {
+ bmcr |= BMCR_ANRESTART;
+ smsc95xx_mdio_write(udev, dev->phy_id, MII_BMCR, bmcr);
+ r = 0;
+ }
+ return r;
+}
+
+static int smsc95xx_phy_initialize(struct usb_device *udev,
+ struct ueth_data *dev)
+{
+ smsc95xx_mdio_write(udev, dev->phy_id, MII_BMCR, BMCR_RESET);
+ smsc95xx_mdio_write(udev, dev->phy_id, MII_ADVERTISE,
+ ADVERTISE_ALL | ADVERTISE_CSMA |
+ ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+
+ /* read to clear */
+ smsc95xx_mdio_read(udev, dev->phy_id, PHY_INT_SRC);
+
+ smsc95xx_mdio_write(udev, dev->phy_id, PHY_INT_MASK,
+ PHY_INT_MASK_DEFAULT_);
+ smsc95xx_mii_nway_restart(udev, dev);
+
+ debug("phy initialised succesfully\n");
+ return 0;
+}
+
+static int smsc95xx_init_mac_address(unsigned char *enetaddr,
+ struct usb_device *udev)
+{
+ const uint8_t *mac_addr;
+
+ /* maybe the boot loader passed the MAC address in devicetree */
+ mac_addr = of_get_mac_address(udev->dev.of_node);
+ if (mac_addr) {
+ memcpy(enetaddr, mac_addr, ETH_ALEN);
+ debug("MAC address read from OF_NODE\n");
+ return VMM_OK;
+ }
+
+ /* try reading mac address from EEPROM */
+ if (smsc95xx_read_eeprom(udev, EEPROM_MAC_OFFSET, ETH_ALEN,
+ enetaddr) == 0) {
+ if (is_valid_ether_addr(enetaddr)) {
+ /* eeprom values are valid so use them */
+ debug("MAC address read from EEPROM\n");
+ return VMM_OK;
+ }
+ }
+
+ /* no useful static MAC address found. generate a random one */
+ random_ether_addr(enetaddr);
+ debug("MAC address generated randomly\n");
+ return VMM_OK;
+}
+
+static int smsc95xx_write_hwaddr_common(struct usb_device *udev,
+ struct smsc95xx_private *priv,
+ unsigned char *enetaddr)
+{
+ u32 addr_lo = get_unaligned_le32(&enetaddr[0]);
+ u32 addr_hi = get_unaligned_le16(&enetaddr[4]);
+ int ret;
+
+ /* set hardware address */
+ debug("** %s()\n", __func__);
+ ret = smsc95xx_write_reg(udev, ADDRL, addr_lo);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_write_reg(udev, ADDRH, addr_hi);
+ if (ret < 0)
+ return ret;
+
+ debug("MAC %pM\n", enetaddr);
+ priv->have_hwaddr = 1;
+
+ return 0;
+}
+
+/* Enable or disable Tx & Rx checksum offload engines */
+static int smsc95xx_set_csums(struct usb_device *udev, int use_tx_csum,
+ int use_rx_csum)
+{
+ u32 read_buf;
+ int ret = smsc95xx_read_reg(udev, COE_CR, &read_buf);
+ if (ret < 0)
+ return ret;
+
+ if (use_tx_csum)
+ read_buf |= Tx_COE_EN_;
+ else
+ read_buf &= ~Tx_COE_EN_;
+
+ if (use_rx_csum)
+ read_buf |= Rx_COE_EN_;
+ else
+ read_buf &= ~Rx_COE_EN_;
+
+ ret = smsc95xx_write_reg(udev, COE_CR, read_buf);
+ if (ret < 0)
+ return ret;
+
+ debug("COE_CR = 0x%08x\n", read_buf);
+ return 0;
+}
+
+static void smsc95xx_set_multicast(struct smsc95xx_private *priv)
+{
+ /* Enable promiscuous mode for debugging */
+ priv->mac_cr |= (MAC_CR_PRMS_);
+ priv->mac_cr &= ~(MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+
+ /* No multicast in u-boot */
+ //priv->mac_cr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+}
+
+/* starts the TX path */
+static void smsc95xx_start_tx_path(struct usb_device *udev,
+ struct smsc95xx_private *priv)
+{
+ u32 reg_val;
+
+ /* Enable Tx at MAC */
+ priv->mac_cr |= MAC_CR_TXEN_;
+
+ smsc95xx_write_reg(udev, MAC_CR, priv->mac_cr);
+
+ /* Enable Tx at SCSRs */
+ reg_val = TX_CFG_ON_;
+ smsc95xx_write_reg(udev, TX_CFG, reg_val);
+}
+
+/* Starts the Receive path */
+static void smsc95xx_start_rx_path(struct usb_device *udev,
+ struct smsc95xx_private *priv)
+{
+ priv->mac_cr |= MAC_CR_RXEN_;
+ smsc95xx_write_reg(udev, MAC_CR, priv->mac_cr);
+}
+
+static int smsc95xx_init_common(struct usb_device *udev, struct ueth_data *dev,
+ struct smsc95xx_private *priv,
+ unsigned char *enetaddr)
+{
+ int ret;
+ u32 write_buf;
+ u32 read_buf;
+ u32 burst_cap;
+ int timeout;
+#define TIMEOUT_RESOLUTION 50 /* ms */
+ int link_detected;
+
+ debug("** %s()\n", __func__);
+ dev->phy_id = SMSC95XX_INTERNAL_PHY_ID; /* fixed phy id */
+
+ write_buf = HW_CFG_LRST_;
+ ret = smsc95xx_write_reg(udev, HW_CFG, write_buf);
+ if (ret < 0)
+ return ret;
+
+ timeout = 0;
+ do {
+ ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf);
+ if (ret < 0)
+ return ret;
+ vmm_udelay(10 * 1000);
+ timeout++;
+ } while ((read_buf & HW_CFG_LRST_) && (timeout < 100));
+
+ if (timeout >= 100) {
+ debug("timeout waiting for completion of Lite Reset\n");
+ return -ETIMEDOUT;
+ }
+
+ write_buf = PM_CTL_PHY_RST_;
+ ret = smsc95xx_write_reg(udev, PM_CTRL, write_buf);
+ if (ret < 0)
+ return ret;
+
+ timeout = 0;
+ do {
+ ret = smsc95xx_read_reg(udev, PM_CTRL, &read_buf);
+ if (ret < 0)
+ return ret;
+ vmm_udelay(10 * 1000);
+ timeout++;
+ } while ((read_buf & PM_CTL_PHY_RST_) && (timeout < 100));
+
+ if (timeout >= 100) {
+ debug("timeout waiting for PHY Reset\n");
+ return -ETIMEDOUT;
+ }
+
+ if (!priv->have_hwaddr) {
+ ret = smsc95xx_init_mac_address(enetaddr, udev);
+ priv->have_hwaddr = !ret;
+ if (ret != 0)
+ {
+ debug("Error: SMSC95xx: No MAC address set - set usbethaddr\n");
+ return -ENODATA;
+ }
+ }
+
+ ret = smsc95xx_write_hwaddr_common(udev, priv, enetaddr);
+ if (ret < 0)
+ return ret;
+
+#ifdef TURBO_MODE
+ if (dev->pusb_dev->speed == USB_SPEED_HIGH) {
+ burst_cap = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE;
+ priv->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE;
+ } else {
+ burst_cap = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE;
+ priv->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE;
+ }
+#else
+ burst_cap = 0;
+ priv->rx_urb_size = MAX_SINGLE_PACKET_SIZE;
+#endif
+ debug("rx_urb_size=%ld\n", (ulong)priv->rx_urb_size);
+
+ ret = smsc95xx_write_reg(udev, BURST_CAP, burst_cap);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(udev, BURST_CAP, &read_buf);
+ if (ret < 0)
+ return ret;
+ debug("Read Value from BURST_CAP after writing: 0x%08x\n", read_buf);
+
+ read_buf = DEFAULT_BULK_IN_DELAY;
+ ret = smsc95xx_write_reg(udev, BULK_IN_DLY, read_buf);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(udev, BULK_IN_DLY, &read_buf);
+ if (ret < 0)
+ return ret;
+ debug("Read Value from BULK_IN_DLY after writing: "
+ "0x%08x\n", read_buf);
+
+ ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf);
+ if (ret < 0)
+ return ret;
+ debug("Read Value from HW_CFG: 0x%08x\n", read_buf);
+
+#ifdef TURBO_MODE
+ read_buf |= (HW_CFG_MEF_ | HW_CFG_BCE_);
+#endif
+ read_buf &= ~HW_CFG_RXDOFF_;
+
+#ifdef NET_IP_ALIGN
+#undef NET_IP_ALIGN
+#endif
+#define NET_IP_ALIGN 0
+ read_buf |= NET_IP_ALIGN << 9;
+
+ ret = smsc95xx_write_reg(udev, HW_CFG, read_buf);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(udev, HW_CFG, &read_buf);
+ if (ret < 0)
+ return ret;
+ debug("Read Value from HW_CFG after writing: 0x%08x\n", read_buf);
+
+ write_buf = 0xFFFFFFFF;
+ ret = smsc95xx_write_reg(udev, INT_STS, write_buf);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(udev, ID_REV, &read_buf);
+ if (ret < 0)
+ return ret;
+ debug("ID_REV = 0x%08x\n", read_buf);
+
+ /* Configure GPIO pins as LED outputs */
+ write_buf = LED_GPIO_CFG_SPD_LED | LED_GPIO_CFG_LNK_LED |
+ LED_GPIO_CFG_FDX_LED;
+ ret = smsc95xx_write_reg(udev, LED_GPIO_CFG, write_buf);
+ if (ret < 0)
+ return ret;
+ debug("LED_GPIO_CFG set\n");
+
+ /* Init Tx */
+ write_buf = 0;
+ ret = smsc95xx_write_reg(udev, FLOW, write_buf);
+ if (ret < 0)
+ return ret;
+
+ read_buf = AFC_CFG_DEFAULT;
+ ret = smsc95xx_write_reg(udev, AFC_CFG, read_buf);
+ if (ret < 0)
+ return ret;
+
+ ret = smsc95xx_read_reg(udev, MAC_CR, &priv->mac_cr);
+ if (ret < 0)
+ return ret;
+
+ /* Init Rx. Set Vlan */
+ write_buf = (u32)ETH_P_8021Q;
+ ret = smsc95xx_write_reg(udev, VLAN1, write_buf);
+ if (ret < 0)
+ return ret;
+
+ /* Disable checksum offload engines */
+ ret = smsc95xx_set_csums(udev, 0, 0);
+ if (ret < 0) {
+ debug("Failed to set csum offload: %d\n", ret);
+ return ret;
+ }
+
+ smsc95xx_set_multicast(priv);
+
+ ret = smsc95xx_phy_initialize(udev, dev);
+ if (ret < 0)
+ return ret;
+ ret = smsc95xx_read_reg(udev, INT_EP_CTL, &read_buf);
+ if (ret < 0)
+ return ret;
+
+ /* enable PHY interrupts */
+ read_buf |= INT_EP_CTL_PHY_INT_;
+
+ ret = smsc95xx_write_reg(udev, INT_EP_CTL, read_buf);
+ if (ret < 0)
+ return ret;
+
+ smsc95xx_start_tx_path(udev, priv);
+ smsc95xx_start_rx_path(udev, priv);
+
+ timeout = 0;
+ do {
+ link_detected = smsc95xx_mdio_read(udev, dev->phy_id, MII_BMSR)
+ & BMSR_LSTATUS;
+ if (!link_detected) {
+ if (timeout == 0)
+ debug("Waiting for Ethernet connection... \n");
+ vmm_udelay(TIMEOUT_RESOLUTION * 1000);
+ timeout += TIMEOUT_RESOLUTION;
+ }
+ } while (!link_detected && timeout < PHY_CONNECT_TIMEOUT);
+ if (link_detected) {
+ if (timeout != 0)
+ debug("done.\n");
+ } else {
+ debug("unable to connect.\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static int smsc95xx_send_common(struct ueth_data *dev, void *packet, int length)
+{
+ int err;
+ int actual_len;
+ u32 tx_cmd_a;
+ u32 tx_cmd_b;
+ ALLOC_CACHE_ALIGN_BUFFER(unsigned char, msg,
+ ETH_FRAME_LEN + sizeof(tx_cmd_a) + sizeof(tx_cmd_b));
+
+ debug("** %s(), len %d, buf %#x\n", __func__, length,
+ (unsigned int)(ulong)msg);
+
+ if (length > ETH_FRAME_LEN)
+ return -ENOSPC;
+
+ tx_cmd_a = cpu_to_le32((u32)length | TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_);
+ tx_cmd_b = cpu_to_le32((u32)length);
+
+ /* prepend cmd_a and cmd_b */
+ memcpy(msg, &tx_cmd_a, sizeof(tx_cmd_a));
+ memcpy(msg + sizeof(tx_cmd_a), &tx_cmd_b, sizeof(tx_cmd_b));
+ memcpy(msg + sizeof(tx_cmd_a) + sizeof(tx_cmd_b), (void *)packet,
+ length);
+
+ err = usb_bulk_msg(dev->pusb_dev,
+ usb_sndbulkpipe(dev->pusb_dev, dev->ep_out),
+ (void *)msg,
+ length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b),
+ &actual_len,
+ USB_BULK_SEND_TIMEOUT);
+
+ debug("Tx: len = %u, actual = %u, err = %d\n",
+ (unsigned int)(length + sizeof(tx_cmd_a) + sizeof(tx_cmd_b)),
+ (unsigned int)actual_len, err);
+
+ return err;
+}
+
+/*
+ * Smsc95xx callbacks
+ */
+static int smsc95xx_init(struct net_device *netdev)
+{
+ struct smsc95xx_private *priv = netdev_priv(netdev);
+ struct usb_device *udev = priv->ueth.pusb_dev;
+ unsigned char enetaddr[ETH_ALEN];
+
+ /* Driver-model Ethernet ensures we have this */
+ priv->have_hwaddr = 1;
+
+ return smsc95xx_init_common(udev, &priv->ueth, priv, enetaddr);
+}
+
+
+static int smsc95xx_send(struct sk_buff *skb, struct net_device *netdev)
+{
+ struct smsc95xx_private *priv = netdev_priv(netdev);
+ char* payload = skb_data(skb);
+ int paylen = skb_len(skb);
+ int ret = VMM_OK;
+
+ skb_dma_ensure(skb);
+
+ ret = smsc95xx_send_common(&priv->ueth, payload, paylen);
+ if (VMM_OK == ret)
+ {
+ netdev->stats.tx_bytes += paylen;
+ netdev->stats.tx_packets++;
+ return NETDEV_TX_OK;
+ }
+
+ return NETDEV_TX_BUSY;
+}
+
+static int smsc95xx_recv(struct net_device *netdev)
+{
+ struct smsc95xx_private *priv = netdev_priv(netdev);
+ struct ueth_data *dev = &priv->ueth;
+ struct sk_buff *skb;
+ uint32_t pad;
+ int totlen, err;
+ uint32_t msghdr;
+ uint16_t pktlen;
+
+ skb = dev_alloc_skb(RX_URB_SIZE);
+ if (unlikely(skb == NULL)) {
+ debug("%s: Low memory, packet dropped.\n", netdev->name);
+ netdev->stats.rx_dropped++;
+ return -ENOMEM;
+ }
+
+ // Align on the cache line size
+ pad = ALIGN((unsigned long)skb_data(skb), VMM_CACHE_LINE_SIZE) - (unsigned long)skb_data(skb);
+ skb_reserve(skb, pad);
+
+ err = usb_bulk_msg(dev->pusb_dev,
+ usb_rcvbulkpipe(dev->pusb_dev, dev->ep_in),
+ (void *)skb_data(skb), (RX_URB_SIZE - pad), &totlen,
+ USB_BULK_RECV_TIMEOUT);
+
+ if (VMM_OK != err) {
+ debug("Rx: failed to receive\n");
+ dev_kfree_skb_any(skb);
+ return -err;
+ }
+
+ debug("Rx: len = %u, actual = %u, err = %d\n", RX_URB_SIZE - pad,
+ totlen, err);
+
+ if (totlen > RX_URB_SIZE) {
+ debug("Rx: received too many bytes %d\n", totlen);
+ dev_kfree_skb_any(skb);
+ return -ENOSPC;
+ }
+
+ memcpy(&msghdr, skb_data(skb), sizeof(msghdr));
+ msghdr = le32_to_cpu(msghdr);
+ pktlen = ((msghdr & RX_STS_FL_) >> 16);
+
+ if (msghdr & RX_STS_ES_) {
+ debug("Rx: Error header=%#x", pktlen);
+ netdev->stats.rx_errors++;
+ dev_kfree_skb_any(skb);
+ return -EIO;
+ }
+
+ if (pktlen > (uint16_t)totlen - sizeof(msghdr)) {
+ debug("Rx: too large packet: %d\n", pktlen);
+ netdev->stats.rx_errors++;
+ dev_kfree_skb_any(skb);
+ return -EIO;
+ }
+
+ /*
+ * 1st 4 bytes contain the length of the actual data plus error
+ * info. Extract data length.
+ */
+ skb_reserve(skb, sizeof(msghdr));
+ skb_put(skb, pktlen);
+ //PRINT_PKT(skb_data(skb), pktlen);
+
+ if (msghdr & RX_STS_MF_) {
+ netdev->stats.multicast++;
+ }
+
+ netdev->stats.rx_packets++;
+ netdev->stats.rx_bytes += pktlen;
+
+ return netif_rx(skb, netdev);
+}
+
+static int smsc95xx_recv_worker(void *data)
+{
+ int rc = VMM_OK;
+ struct smsc95xx_private *priv = netdev_priv(data);
+ struct ueth_data *dev = &priv->ueth;
+ uint32_t rx_bytes = 0;
+
+ while (1) {
+ rc = smsc95xx_read_reg(dev->pusb_dev, RX_FIFO_INF, &rx_bytes);
+ if (rc != VMM_OK || (rx_bytes & 0xFFFF) == 0)
+ {
+ vmm_usleep(DEFAULT_BULK_IN_DELAY);
+ continue;
+ }
+
+ smsc95xx_recv(data);
+ }
+
+ return rc;
+}
+
+#if 0
+static int smsc95xx_write_hwaddr(struct eth_device *eth)
+{
+ struct ueth_data *dev = eth->priv;
+ struct usb_device *udev = dev->pusb_dev;
+ struct smsc95xx_private *priv = dev->dev_priv;
+
+ return smsc95xx_write_hwaddr_common(udev, priv, eth->enetaddr);
+}
+#endif
+
+struct smsc95xx_dongle {
+ unsigned short vendor;
+ unsigned short product;
+};
+
+static const struct smsc95xx_dongle smsc95xx_dongles[] = {
+ { 0x0424, 0xec00 }, /* LAN9512/LAN9514 Ethernet */
+ { 0x0424, 0x9500 }, /* LAN9500 Ethernet */
+ { 0x0424, 0x9730 }, /* LAN9730 Ethernet (HSIC) */
+ { 0x0424, 0x9900 }, /* SMSC9500 USB Ethernet Device (SAL10) */
+ { 0x0424, 0x9e00 }, /* LAN9500A Ethernet */
+ { 0x0000, 0x0000 } /* END - Do not remove */
+};
+
+static int smsc95xx_open(struct net_device *netdev)
+{
+ struct smsc95xx_private *priv = netdev_priv(netdev);
+
+ debug("** TRACE **");
+
+ priv->ueth.rx_thread = vmm_threads_create(netdev->name, smsc95xx_recv_worker,
+ netdev, VMM_THREAD_DEF_PRIORITY, VMM_THREAD_DEF_TIME_SLICE);
+
+ netif_start_queue(netdev);
+ vmm_threads_start(priv->ueth.rx_thread);
+ return 0;
+
+fail:
+ vmm_threads_stop(priv->ueth.rx_thread);
+ vmm_threads_destroy(priv->ueth.rx_thread);
+ return -EIO;
+}
+
+
+static int smsc95xx_stop(struct net_device *netdev)
+{
+ struct smsc95xx_private *priv = netdev_priv(netdev);
+
+ debug("** TRACE **");
+
+ vmm_threads_stop(priv->ueth.rx_thread);
+ vmm_threads_destroy(priv->ueth.rx_thread);
+ netif_stop_queue(netdev);
+ return 0;
+}
+
+static const struct net_device_ops smsc95xx_netdev_ops = {
+ .ndo_init = smsc95xx_init,
+ .ndo_open = smsc95xx_open,
+ .ndo_stop = smsc95xx_stop,
+ .ndo_start_xmit = smsc95xx_send,
+#if 0
+ .ndo_validate_addr = NULL,
+ .ndo_tx_timeout = NULL,
+ .ndo_do_ioctl = NULL,
+ .ndo_change_mtu = NULL,
+#endif
+};
+
+/* Probe to see if a new device is actually an SMSC device */
+static int __smsc95xx_eth_probe(struct usb_interface *iface,
+ const struct usb_device_id *id, struct ueth_data *ss)
+{
+ struct usb_device *dev;
+ struct usb_interface_descriptor *iface_desc;
+ int i;
+
+ if (NULL == iface || NULL == id || NULL == ss)
+ return 0;
+
+ /* let's examine the device now */
+ dev = interface_to_usbdev(iface);
+ iface_desc = &iface->desc;
+
+ for (i = 0; smsc95xx_dongles[i].vendor != 0; i++) {
+ if (id->idVendor == smsc95xx_dongles[i].vendor &&
+ id->idProduct == smsc95xx_dongles[i].product)
+ /* Found a supported dongle */
+ break;
+ }
+
+ if (smsc95xx_dongles[i].vendor == 0)
+ return 0;
+
+ /* At this point, we know we've got a live one */
+ debug("USB Ethernet device detected\n");
+ memset(ss, 0, sizeof(struct ueth_data));
+
+ /* Initialize the ueth_data structure with some useful info */
+ ss->ifnum = 0;
+ ss->pusb_dev = interface_to_usbdev(iface);
+ ss->subclass = iface_desc->bInterfaceSubClass;
+ ss->protocol = iface_desc->bInterfaceProtocol;
+
+ /*
+ * We are expecting a minimum of 3 endpoints - in, out (bulk), and int.
+ * We will ignore any others.
+ */
+ for (i = 0; i < iface_desc->bNumEndpoints; i++) {
+ /* is it an BULK endpoint? */
+ if ((iface->ep_desc[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
+ if (iface->ep_desc[i].bEndpointAddress & USB_DIR_IN)
+ ss->ep_in =
+ iface->ep_desc[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ else
+ ss->ep_out =
+ iface->ep_desc[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ }
+
+ /* is it an interrupt endpoint? */
+ if ((iface->ep_desc[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
+ ss->ep_int = iface->ep_desc[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ ss->irqinterval = iface->ep_desc[i].bInterval;
+ }
+ }
+ debug("Endpoints In %d Out %d Int %d\n",
+ ss->ep_in, ss->ep_out, ss->ep_int);
+
+ /* Do some basic sanity checks, and bail if we find a problem */
+ if (usb_set_interface(dev, iface_desc->bInterfaceNumber, 0) ||
+ !ss->ep_in || !ss->ep_out || !ss->ep_int) {
+ debug("Problems with device\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static int smsc95xx_eth_probe(struct usb_interface *iface,
+ const struct usb_device_id *id)
+{
+ int rc = VMM_OK;
+ struct usb_device *usbdev;
+ struct net_device *netdev;
+ struct smsc95xx_private *priv;
+
+ usbdev = interface_to_usbdev(iface);
+ netdev = alloc_etherdev(sizeof(struct smsc95xx_private));
+ if (!netdev) {
+ vmm_printf("%s Failed to allocate netdev for %s\n",
+ __func__, usbdev->product);
+ rc = VMM_EFAIL;
+ goto done;
+ }
+
+ SET_NETDEV_DEV(netdev, &iface->dev);
+ vmm_devdrv_set_data(&iface->dev, netdev);
+ vmm_snprintf(netdev->name, MAX_NETDEV_NAME_LEN, SMSC95XX_BASE_NAME "%d", (usbdev->portnum - 1));
+
+ priv = netdev_priv(netdev);
+ if (!__smsc95xx_eth_probe(iface, id, &priv->ueth)) {
+ rc = VMM_EFAIL;
+ goto free_netdev;
+ }
+
+ /* Fill in the fields of the device structure with ethernet values. */
+ ether_setup(netdev);
+
+ netdev->netdev_ops = &smsc95xx_netdev_ops;
+ rc = register_netdev(netdev);
+ if (VMM_OK == rc)
+ goto done;
+
+free_netdev:
+ vmm_devdrv_set_data(&iface->dev, NULL);
+ vmm_free(netdev);
+
+done:
+ return rc;
+}
+
+static void smsc95xx_eth_remove(struct usb_interface *iface)
+{
+ struct usb_device *dev;
+ struct ueth_data *ss;
+
+ dev = interface_to_usbdev(iface);
+ if (!dev) {
+ return;
+ }
+
+ ss = interface_get_data(iface);
+ if (!ss) {
+ return;
+ }
+
+ /* Free the private */
+ vmm_free(ss->pusb_dev->dev.priv);
+ vmm_free(ss);
+
+ /* Free the interface ueth */
+ interface_set_data(iface, NULL);
+ return;
+}
+
+static const struct usb_device_id smsc95xx_eth_id_table[] = {
+ { USB_DEVICE(0x05ac, 0x1402) },
+ { USB_DEVICE(0x0424, 0xec00) }, /* LAN9512/LAN9514 Ethernet */
+ { USB_DEVICE(0x0424, 0x9500) }, /* LAN9500 Ethernet */
+ { USB_DEVICE(0x0424, 0x9730) }, /* LAN9730 Ethernet (HSIC) */
+ { USB_DEVICE(0x0424, 0x9900) }, /* SMSC9500 USB Ethernet (SAL10) */
+ { USB_DEVICE(0x0424, 0x9e00) }, /* LAN9500A Ethernet */
+ { } /* Terminating entry */
+};
+
+static struct usb_driver smsc95xx_eth = {
+ .name = "smsc95xx_eth",
+ .id_table = smsc95xx_eth_id_table,
+ .probe = smsc95xx_eth_probe,
+ .disconnect = smsc95xx_eth_remove
+};
+
+static int __init smsc95xx_eth_init(void)
+{
+ return usb_register(&smsc95xx_eth);
+}
+
+static void __exit smsc95xx_eth_exit(void)
+{
+ usb_deregister(&smsc95xx_eth);
+}
+
+VMM_DECLARE_MODULE(MODULE_DESC,
+ MODULE_AUTHOR,
+ MODULE_LICENSE,
+ MODULE_IPRIORITY,
+ MODULE_INIT,
+ MODULE_EXIT);
diff --git a/drivers/usb/devices/smsc95xx.h b/drivers/usb/devices/smsc95xx.h
new file mode 100644
index 0000000..cfc704f
--- /dev/null
+++ b/drivers/usb/devices/smsc95xx.h
@@ -0,0 +1,364 @@
+ /***************************************************************************
+ *
+ * Copyright (C) 2007-2008 SMSC
+ *
+ * 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, see <
http://www.gnu.org/licenses/>.
+ *
+ *****************************************************************************/
+
+#ifndef _SMSC95XX_H
+#define _SMSC95XX_H
+
+/* Tx command words */
+#define TX_CMD_A_DATA_OFFSET_ (0x001F0000) /* Data Start Offset */
+#define TX_CMD_A_FIRST_SEG_ (0x00002000) /* First Segment */
+#define TX_CMD_A_LAST_SEG_ (0x00001000) /* Last Segment */
+#define TX_CMD_A_BUF_SIZE_ (0x000007FF) /* Buffer Size */
+
+#define TX_CMD_B_CSUM_ENABLE (0x00004000) /* TX Checksum Enable */
+#define TX_CMD_B_ADD_CRC_DIS_ (0x00002000) /* Add CRC Disable */
+#define TX_CMD_B_DIS_PADDING_ (0x00001000) /* Disable Frame Padding */
+#define TX_CMD_B_FRAME_LENGTH_ (0x000007FF) /* Frame Length (bytes) */
+
+/* Rx status word */
+#define RX_STS_FF_ (0x40000000) /* Filter Fail */
+#define RX_STS_FL_ (0x3FFF0000) /* Frame Length */
+#define RX_STS_ES_ (0x00008000) /* Error Summary */
+#define RX_STS_BF_ (0x00002000) /* Broadcast Frame */
+#define RX_STS_LE_ (0x00001000) /* Length Error */
+#define RX_STS_RF_ (0x00000800) /* Runt Frame */
+#define RX_STS_MF_ (0x00000400) /* Multicast Frame */
+#define RX_STS_TL_ (0x00000080) /* Frame too long */
+#define RX_STS_CS_ (0x00000040) /* Collision Seen */
+#define RX_STS_FT_ (0x00000020) /* Frame Type */
+#define RX_STS_RW_ (0x00000010) /* Receive Watchdog */
+#define RX_STS_ME_ (0x00000008) /* MII Error */
+#define RX_STS_DB_ (0x00000004) /* Dribbling */
+#define RX_STS_CRC_ (0x00000002) /* CRC Error */
+
+/* SCSRs - System Control and Status Registers */
+/* Device ID and Revision Register */
+#define ID_REV (0x00)
+#define ID_REV_CHIP_ID_MASK_ (0xFFFF0000)
+#define ID_REV_CHIP_REV_MASK_ (0x0000FFFF)
+#define ID_REV_CHIP_ID_9500_ (0x9500)
+#define ID_REV_CHIP_ID_9500A_ (0x9E00)
+#define ID_REV_CHIP_ID_9512_ (0xEC00)
+#define ID_REV_CHIP_ID_9530_ (0x9530)
+#define ID_REV_CHIP_ID_89530_ (0x9E08)
+#define ID_REV_CHIP_ID_9730_ (0x9730)
+
+/* Interrupt Status Register */
+#define INT_STS (0x08)
+#define INT_STS_MAC_RTO_ (0x00040000) /* MAC Reset Time Out */
+#define INT_STS_TX_STOP_ (0x00020000) /* TX Stopped */
+#define INT_STS_RX_STOP_ (0x00010000) /* RX Stopped */
+#define INT_STS_PHY_INT_ (0x00008000) /* PHY Interrupt */
+#define INT_STS_TXE_ (0x00004000) /* Transmitter Error */
+#define INT_STS_TDFU_ (0x00002000) /* TX Data FIFO Underrun */
+#define INT_STS_TDFO_ (0x00001000) /* TX Data FIFO Overrun */
+#define INT_STS_RXDF_ (0x00000800) /* RX Dropped Frame */
+#define INT_STS_GPIOS_ (0x000007FF) /* GPIOs Interrupts */
+#define INT_STS_CLEAR_ALL_ (0xFFFFFFFF)
+
+/* Receive Configuration Register */
+#define RX_CFG (0x0C)
+#define RX_FIFO_FLUSH_ (0x00000001) /* Receive FIFO Flush */
+
+/* Transmit Configuration Register */
+#define TX_CFG (0x10)
+#define TX_CFG_ON_ (0x00000004) /* Transmitter Enable */
+#define TX_CFG_STOP_ (0x00000002) /* Stop Transmitter */
+#define TX_CFG_FIFO_FLUSH_ (0x00000001) /* Transmit FIFO Flush */
+
+/* Hardware Configuration Register */
+#define HW_CFG (0x14)
+#define HW_CFG_BIR_ (0x00001000) /* Bulk In Empty Response */
+#define HW_CFG_LEDB_ (0x00000800) /* Activity LED 80ms Bypass */
+#define HW_CFG_RXDOFF_ (0x00000600) /* RX Data Offset */
+#define HW_CFG_SBP_ (0x00000100) /* Stall Bulk Out Pipe Dis. */
+#define HW_CFG_IME_ (0x00000080) /* Internal MII Visi. Enable */
+#define HW_CFG_DRP_ (0x00000040) /* Discard Errored RX Frame */
+#define HW_CFG_MEF_ (0x00000020) /* Mult. ETH Frames/USB pkt */
+#define HW_CFG_ETC_ (0x00000010) /* EEPROM Timeout Control */
+#define HW_CFG_LRST_ (0x00000008) /* Soft Lite Reset */
+#define HW_CFG_PSEL_ (0x00000004) /* External PHY Select */
+#define HW_CFG_BCE_ (0x00000002) /* Burst Cap Enable */
+#define HW_CFG_SRST_ (0x00000001) /* Soft Reset */
+
+/* Receive FIFO Information Register */
+#define RX_FIFO_INF (0x18)
+#define RX_FIFO_INF_USED_ (0x0000FFFF) /* RX Data FIFO Used Space */
+
+/* Transmit FIFO Information Register */
+#define TX_FIFO_INF (0x1C)
+#define TX_FIFO_INF_FREE_ (0x0000FFFF) /* TX Data FIFO Free Space */
+
+/* Power Management Control Register */
+#define PM_CTRL (0x20)
+#define PM_CTL_RES_CLR_WKP_STS (0x00000200) /* Resume Clears Wakeup STS */
+#define PM_CTL_RES_CLR_WKP_EN (0x00000100) /* Resume Clears Wkp Enables */
+#define PM_CTL_DEV_RDY_ (0x00000080) /* Device Ready */
+#define PM_CTL_SUS_MODE_ (0x00000060) /* Suspend Mode */
+#define PM_CTL_SUS_MODE_0 (0x00000000)
+#define PM_CTL_SUS_MODE_1 (0x00000020)
+#define PM_CTL_SUS_MODE_2 (0x00000040)
+#define PM_CTL_SUS_MODE_3 (0x00000060)
+#define PM_CTL_PHY_RST_ (0x00000010) /* PHY Reset */
+#define PM_CTL_WOL_EN_ (0x00000008) /* Wake On Lan Enable */
+#define PM_CTL_ED_EN_ (0x00000004) /* Energy Detect Enable */
+#define PM_CTL_WUPS_ (0x00000003) /* Wake Up Status */
+#define PM_CTL_WUPS_NO_ (0x00000000) /* No Wake Up Event Detected */
+#define PM_CTL_WUPS_ED_ (0x00000001) /* Energy Detect */
+#define PM_CTL_WUPS_WOL_ (0x00000002) /* Wake On Lan */
+#define PM_CTL_WUPS_MULTI_ (0x00000003) /* Multiple Events Occurred */
+
+/* LED General Purpose IO Configuration Register */
+#define LED_GPIO_CFG (0x24)
+#define LED_GPIO_CFG_SPD_LED (0x01000000) /* GPIOz as Speed LED */
+#define LED_GPIO_CFG_LNK_LED (0x00100000) /* GPIOy as Link LED */
+#define LED_GPIO_CFG_FDX_LED (0x00010000) /* GPIOx as Full Duplex LED */
+
+/* General Purpose IO Configuration Register */
+#define GPIO_CFG (0x28)
+
+/* Automatic Flow Control Configuration Register */
+#define AFC_CFG (0x2C)
+#define AFC_CFG_HI_ (0x00FF0000) /* Auto Flow Ctrl High Level */
+#define AFC_CFG_LO_ (0x0000FF00) /* Auto Flow Ctrl Low Level */
+#define AFC_CFG_BACK_DUR_ (0x000000F0) /* Back Pressure Duration */
+#define AFC_CFG_FC_MULT_ (0x00000008) /* Flow Ctrl on Mcast Frame */
+#define AFC_CFG_FC_BRD_ (0x00000004) /* Flow Ctrl on Bcast Frame */
+#define AFC_CFG_FC_ADD_ (0x00000002) /* Flow Ctrl on Addr. Decode */
+#define AFC_CFG_FC_ANY_ (0x00000001) /* Flow Ctrl on Any Frame */
+/* Hi watermark = 15.5Kb (~10 mtu pkts) */
+/* low watermark = 3k (~2 mtu pkts) */
+/* backpressure duration = ~ 350us */
+/* Apply FC on any frame. */
+#define AFC_CFG_DEFAULT (0x00F830A1)
+
+/* EEPROM Command Register */
+#define E2P_CMD (0x30)
+#define E2P_CMD_BUSY_ (0x80000000) /* E2P Controller Busy */
+#define E2P_CMD_MASK_ (0x70000000) /* Command Mask (see below) */
+#define E2P_CMD_READ_ (0x00000000) /* Read Location */
+#define E2P_CMD_EWDS_ (0x10000000) /* Erase/Write Disable */
+#define E2P_CMD_EWEN_ (0x20000000) /* Erase/Write Enable */
+#define E2P_CMD_WRITE_ (0x30000000) /* Write Location */
+#define E2P_CMD_WRAL_ (0x40000000) /* Write All */
+#define E2P_CMD_ERASE_ (0x50000000) /* Erase Location */
+#define E2P_CMD_ERAL_ (0x60000000) /* Erase All */
+#define E2P_CMD_RELOAD_ (0x70000000) /* Data Reload */
+#define E2P_CMD_TIMEOUT_ (0x00000400) /* Set if no resp within 30ms */
+#define E2P_CMD_LOADED_ (0x00000200) /* Valid EEPROM found */
+#define E2P_CMD_ADDR_ (0x000001FF) /* Byte aligned address */
+
+#define MAX_EEPROM_SIZE (512)
+
+/* EEPROM Data Register */
+#define E2P_DATA (0x34)
+#define E2P_DATA_MASK_ (0x000000FF) /* EEPROM Data Mask */
+
+/* Burst Cap Register */
+#define BURST_CAP (0x38)
+#define BURST_CAP_MASK_ (0x000000FF) /* Max burst sent by the UTX */
+
+/* Configuration Straps Status Register */
+#define STRAP_STATUS (0x3C)
+#define STRAP_STATUS_PWR_SEL_ (0x00000020) /* Device self-powered */
+#define STRAP_STATUS_AMDIX_EN_ (0x00000010) /* Auto-MDIX Enabled */
+#define STRAP_STATUS_PORT_SWAP_ (0x00000008) /* USBD+/USBD- Swapped */
+#define STRAP_STATUS_EEP_SIZE_ (0x00000004) /* EEPROM Size */
+#define STRAP_STATUS_RMT_WKP_ (0x00000002) /* Remote Wkp supported */
+#define STRAP_STATUS_EEP_DISABLE_ (0x00000001) /* EEPROM Disabled */
+
+/* Data Port Select Register */
+#define DP_SEL (0x40)
+
+/* Data Port Command Register */
+#define DP_CMD (0x44)
+
+/* Data Port Address Register */
+#define DP_ADDR (0x48)
+
+/* Data Port Data 0 Register */
+#define DP_DATA0 (0x4C)
+
+/* Data Port Data 1 Register */
+#define DP_DATA1 (0x50)
+
+/* General Purpose IO Wake Enable and Polarity Register */
+#define GPIO_WAKE (0x64)
+
+/* Interrupt Endpoint Control Register */
+#define INT_EP_CTL (0x68)
+#define INT_EP_CTL_INTEP_ (0x80000000) /* Always TX Interrupt PKT */
+#define INT_EP_CTL_MAC_RTO_ (0x00080000) /* MAC Reset Time Out */
+#define INT_EP_CTL_RX_FIFO_ (0x00040000) /* RX FIFO Has Frame */
+#define INT_EP_CTL_TX_STOP_ (0x00020000) /* TX Stopped */
+#define INT_EP_CTL_RX_STOP_ (0x00010000) /* RX Stopped */
+#define INT_EP_CTL_PHY_INT_ (0x00008000) /* PHY Interrupt */
+#define INT_EP_CTL_TXE_ (0x00004000) /* TX Error */
+#define INT_EP_CTL_TDFU_ (0x00002000) /* TX Data FIFO Underrun */
+#define INT_EP_CTL_TDFO_ (0x00001000) /* TX Data FIFO Overrun */
+#define INT_EP_CTL_RXDF_ (0x00000800) /* RX Dropped Frame */
+#define INT_EP_CTL_GPIOS_ (0x000007FF) /* GPIOs Interrupt Enable */
+
+/* Bulk In Delay Register (units of 16.667ns, until ~1092µs) */
+#define BULK_IN_DLY (0x6C)
+
+/* MAC CSRs - MAC Control and Status Registers */
+/* MAC Control Register */
+#define MAC_CR (0x100)
+#define MAC_CR_RXALL_ (0x80000000) /* Receive All Mode */
+#define MAC_CR_RCVOWN_ (0x00800000) /* Disable Receive Own */
+#define MAC_CR_LOOPBK_ (0x00200000) /* Loopback Operation Mode */
+#define MAC_CR_FDPX_ (0x00100000) /* Full Duplex Mode */
+#define MAC_CR_MCPAS_ (0x00080000) /* Pass All Multicast */
+#define MAC_CR_PRMS_ (0x00040000) /* Promiscuous Mode */
+#define MAC_CR_INVFILT_ (0x00020000) /* Inverse Filtering */
+#define MAC_CR_PASSBAD_ (0x00010000) /* Pass Bad Frames */
+#define MAC_CR_HFILT_ (0x00008000) /* Hash Only Filtering Mode */
+#define MAC_CR_HPFILT_ (0x00002000) /* Hash/Perfect Filt. Mode */
+#define MAC_CR_LCOLL_ (0x00001000) /* Late Collision Control */
+#define MAC_CR_BCAST_ (0x00000800) /* Disable Broadcast Frames */
+#define MAC_CR_DISRTY_ (0x00000400) /* Disable Retry */
+#define MAC_CR_PADSTR_ (0x00000100) /* Automatic Pad Stripping */
+#define MAC_CR_BOLMT_MASK (0x000000C0) /* BackOff Limit */
+#define MAC_CR_DFCHK_ (0x00000020) /* Deferral Check */
+#define MAC_CR_TXEN_ (0x00000008) /* Transmitter Enable */
+#define MAC_CR_RXEN_ (0x00000004) /* Receiver Enable */
+
+/* MAC Address High Register */
+#define ADDRH (0x104)
+
+/* MAC Address Low Register */
+#define ADDRL (0x108)
+
+/* Multicast Hash Table High Register */
+#define HASHH (0x10C)
+
+/* Multicast Hash Table Low Register */
+#define HASHL (0x110)
+
+/* MII Access Register */
+#define MII_ADDR (0x114)
+#define MII_WRITE_ (0x02)
+#define MII_BUSY_ (0x01)
+#define MII_READ_ (0x00) /* ~of MII Write bit */
+
+/* MII Data Register */
+#define MII_DATA (0x118)
+
+/* Flow Control Register */
+#define FLOW (0x11C)
+#define FLOW_FCPT_ (0xFFFF0000) /* Pause Time */
+#define FLOW_FCPASS_ (0x00000004) /* Pass Control Frames */
+#define FLOW_FCEN_ (0x00000002) /* Flow Control Enable */
+#define FLOW_FCBSY_ (0x00000001) /* Flow Control Busy */
+
+/* VLAN1 Tag Register */
+#define VLAN1 (0x120)
+
+/* VLAN2 Tag Register */
+#define VLAN2 (0x124)
+
+/* Wake Up Frame Filter Register */
+#define WUFF (0x128)
+#define LAN9500_WUFF_NUM (4)
+#define LAN9500A_WUFF_NUM (8)
+
+/* Wake Up Control and Status Register */
+#define WUCSR (0x12C)
+#define WUCSR_WFF_PTR_RST_ (0x80000000) /* WFrame Filter Pointer Rst */
+#define WUCSR_GUE_ (0x00000200) /* Global Unicast Enable */
+#define WUCSR_WUFR_ (0x00000040) /* Wakeup Frame Received */
+#define WUCSR_MPR_ (0x00000020) /* Magic Packet Received */
+#define WUCSR_WAKE_EN_ (0x00000004) /* Wakeup Frame Enable */
+#define WUCSR_MPEN_ (0x00000002) /* Magic Packet Enable */
+
+/* Checksum Offload Engine Control Register */
+#define COE_CR (0x130)
+#define Tx_COE_EN_ (0x00010000) /* TX Csum Offload Enable */
+#define Rx_COE_MODE_ (0x00000002) /* RX Csum Offload Mode */
+#define Rx_COE_EN_ (0x00000001) /* RX Csum Offload Enable */
+
+/* Vendor-specific PHY Definitions (via MII access) */
+/* EDPD NLP / crossover time configuration (LAN9500A only) */
+#define PHY_EDPD_CONFIG (16)
+#define PHY_EDPD_CONFIG_TX_NLP_EN_ ((u16)0x8000)
+#define PHY_EDPD_CONFIG_TX_NLP_1000_ ((u16)0x0000)
+#define PHY_EDPD_CONFIG_TX_NLP_768_ ((u16)0x2000)
+#define PHY_EDPD_CONFIG_TX_NLP_512_ ((u16)0x4000)
+#define PHY_EDPD_CONFIG_TX_NLP_256_ ((u16)0x6000)
+#define PHY_EDPD_CONFIG_RX_1_NLP_ ((u16)0x1000)
+#define PHY_EDPD_CONFIG_RX_NLP_64_ ((u16)0x0000)
+#define PHY_EDPD_CONFIG_RX_NLP_256_ ((u16)0x0400)
+#define PHY_EDPD_CONFIG_RX_NLP_512_ ((u16)0x0800)
+#define PHY_EDPD_CONFIG_RX_NLP_1000_ ((u16)0x0C00)
+#define PHY_EDPD_CONFIG_EXT_CROSSOVER_ ((u16)0x0001)
+#define PHY_EDPD_CONFIG_DEFAULT (PHY_EDPD_CONFIG_TX_NLP_EN_ | \
+ PHY_EDPD_CONFIG_TX_NLP_768_ | \
+ PHY_EDPD_CONFIG_RX_1_NLP_)
+
+/* Mode Control/Status Register */
+#define PHY_MODE_CTRL_STS (17)
+#define MODE_CTRL_STS_EDPWRDOWN_ ((u16)0x2000)
+#define MODE_CTRL_STS_ENERGYON_ ((u16)0x0002)
+
+/* Control/Status Indication Register */
+#define SPECIAL_CTRL_STS (27)
+#define SPECIAL_CTRL_STS_OVRRD_AMDIX_ ((u16)0x8000)
+#define SPECIAL_CTRL_STS_AMDIX_ENABLE_ ((u16)0x4000)
+#define SPECIAL_CTRL_STS_AMDIX_STATE_ ((u16)0x2000)
+
+/* Interrupt Source Register */
+#define PHY_INT_SRC (29)
+#define PHY_INT_SRC_ENERGY_ON_ ((u16)0x0080)
+#define PHY_INT_SRC_ANEG_COMP_ ((u16)0x0040)
+#define PHY_INT_SRC_REMOTE_FAULT_ ((u16)0x0020)
+#define PHY_INT_SRC_LINK_DOWN_ ((u16)0x0010)
+
+/* Interrupt Mask Register */
+#define PHY_INT_MASK (30)
+#define PHY_INT_MASK_ENERGY_ON_ ((u16)0x0080)
+#define PHY_INT_MASK_ANEG_COMP_ ((u16)0x0040)
+#define PHY_INT_MASK_REMOTE_FAULT_ ((u16)0x0020)
+#define PHY_INT_MASK_LINK_DOWN_ ((u16)0x0010)
+#define PHY_INT_MASK_DEFAULT_ (PHY_INT_MASK_ANEG_COMP_ | \
+ PHY_INT_MASK_LINK_DOWN_)
+/* PHY Special Control/Status Register */
+#define PHY_SPECIAL (31)
+#define PHY_SPECIAL_SPD_ ((u16)0x001C)
+#define PHY_SPECIAL_SPD_10HALF_ ((u16)0x0004)
+#define PHY_SPECIAL_SPD_10FULL_ ((u16)0x0014)
+#define PHY_SPECIAL_SPD_100HALF_ ((u16)0x0008)
+#define PHY_SPECIAL_SPD_100FULL_ ((u16)0x0018)
+
+/* USB Vendor Requests */
+#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0
+#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1
+#define USB_VENDOR_REQUEST_GET_STATS 0xA2
+
+/* Interrupt Endpoint status word bitfields */
+#define INT_ENP_MAC_RTO_ ((u32)BIT(18)) /* MAC Reset Time Out */
+#define INT_ENP_TX_STOP_ ((u32)BIT(17)) /* TX Stopped */
+#define INT_ENP_RX_STOP_ ((u32)BIT(16)) /* RX Stopped */
+#define INT_ENP_PHY_INT_ ((u32)BIT(15)) /* PHY Interrupt */
+#define INT_ENP_TXE_ ((u32)BIT(14)) /* TX Error */
+#define INT_ENP_TDFU_ ((u32)BIT(13)) /* TX FIFO Underrun */
+#define INT_ENP_TDFO_ ((u32)BIT(12)) /* TX FIFO Overrun */
+#define INT_ENP_RXDF_ ((u32)BIT(11)) /* RX Dropped Frame */
+
+#endif /* _SMSC95XX_H */
diff --git a/drivers/usb/storage/
objects.mk b/drivers/usb/storage/
objects.mk
index f6fc310..35980bc 100644
--- a/drivers/usb/storage/
objects.mk
+++ b/drivers/usb/storage/
objects.mk
@@ -22,4 +22,4 @@
# */
drivers-objs-$(CONFIG_USB_STORAGE)+= usb/storage/usb_storage.o
-
+drivers-objs-$(CONFIG_USB_ETHER_SMSC95XX)+= usb/devices/smsc95xx.o
diff --git a/drivers/usb/storage/openconf.cfg b/drivers/usb/storage/openconf.cfg
index 9ba238e..47ff51e 100644
--- a/drivers/usb/storage/openconf.cfg
+++ b/drivers/usb/storage/openconf.cfg
@@ -33,3 +33,15 @@ config CONFIG_USB_STORAGE
help
USB mass storage device driver.
+config CONFIG_USB_HOST_ETHER
+ tristate "USB ethernet support"
+ depends on CONFIG_USB && CONFIG_NET
+ default n
+
+if CONFIG_USB_HOST_ETHER
+config CONFIG_USB_ETHER_SMSC95XX
+ tristate "SMSC95xx device driver"
+ depends on CONFIG_USB_HOST_ETHER
+ default n
+endif
+
diff --git a/emulators/net/virtio_net.c b/emulators/net/virtio_net.c
index ad88086..9f92461 100644
--- a/emulators/net/virtio_net.c
+++ b/emulators/net/virtio_net.c
@@ -406,6 +406,11 @@ static int virtio_net_connect(struct vmm_virtio_device *dev,
}
}
+ attr = of_get_mac_address(dev->edev->node);
+ if (attr) {
+ memcpy(vmm_netport_mac(ndev->port), attr, ETH_ALEN);
+ }
+
rc = vmm_netport_register(ndev->port);
if (rc) {
vmm_free(ndev->vqs);
--
2.1.4