summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNarendra Damahe <ndamahe@nvidia.com>2010-06-07 16:51:50 -0700
committerGary King <gking@nvidia.com>2010-06-07 21:41:01 -0700
commit0a4afffd67bccf9fb9c771cf2579c6e7c4451dec (patch)
tree9e72080b9ffd7f5de55d7ff282c64d5bd94a1f48
parent23e0a4cb652c025381a2bd6d7e35753bef006a47 (diff)
USBNET:adding smsc power(suspend/resume)aware driver
Bug 693439 Change-Id: Id251d6be11e1b251c45e811173ba770683f11c48 Reviewed-by: Narendra Damahe <ndamahe@nvidia.com> Tested-by: Narendra Damahe <ndamahe@nvidia.com> Reviewed-by: Gary King <gking@nvidia.com>
-rw-r--r--drivers/net/usb/Makefile7
-rw-r--r--drivers/net/usb/ioctl_9500.h189
-rw-r--r--drivers/net/usb/smsc9500.c5078
-rw-r--r--drivers/net/usb/smsc9500.h775
-rw-r--r--drivers/net/usb/smscusbnet.c2075
-rw-r--r--drivers/net/usb/smscusbnet.h318
-rw-r--r--drivers/net/usb/version.h6
7 files changed, 8447 insertions, 1 deletions
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index e17afb78f372..35403cd87532 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -11,7 +11,12 @@ obj-$(CONFIG_USB_NET_AX8817X) += asix.o
obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o
obj-$(CONFIG_USB_NET_CDC_EEM) += cdc_eem.o
obj-$(CONFIG_USB_NET_DM9601) += dm9601.o
-obj-$(CONFIG_USB_NET_SMSC95XX) += smsc95xx.o
+ifeq ($(CONFIG_PM),y)
+obj-$(CONFIG_USB_NET_SMSC95XX) += smsc9500.o
+obj-$(CONFIG_USB_NET_SMSC95XX) += smscusbnet.o
+else
+obj-$(CONFIG_USB_NET_SMSC95XX) += smsc95xx.o
+endif
obj-$(CONFIG_USB_NET_GL620A) += gl620a.o
obj-$(CONFIG_USB_NET_NET1080) += net1080.o
obj-$(CONFIG_USB_NET_PLUSB) += plusb.o
diff --git a/drivers/net/usb/ioctl_9500.h b/drivers/net/usb/ioctl_9500.h
new file mode 100644
index 000000000000..5f0ba965ad58
--- /dev/null
+++ b/drivers/net/usb/ioctl_9500.h
@@ -0,0 +1,189 @@
+#ifndef IOCTL_9500_H_
+#define IOCTL_9500_H_
+
+/***************************************************************************
+
+ *
+
+ * Copyright (C) 2008-2009 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, write to the Free Software
+
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+ *
+
+ ***************************************************************************
+
+ * File: ioctl_500.h
+
+ */
+
+
+#define SMSC9500_DRIVER_SIGNATURE (0x82745BACUL+DRIVER_VERSION)
+
+#define SMSC9500_APP_SIGNATURE (0x987BEF28UL+DRIVER_VERSION)
+
+
+
+#define SMSC9500_IOCTL (SIOCDEVPRIVATE + 0xB)
+
+
+enum{
+ COMMAND_BASE = 0x974FB832UL,
+ COMMAND_GET_SIGNATURE,
+ COMMAND_LAN_GET_REG,
+ COMMAND_LAN_SET_REG,
+ COMMAND_MAC_GET_REG,
+ COMMAND_MAC_SET_REG,
+ COMMAND_PHY_GET_REG,
+ COMMAND_PHY_SET_REG,
+ COMMAND_DUMP_LAN_REGS,
+ COMMAND_DUMP_MAC_REGS,
+ COMMAND_DUMP_PHY_REGS,
+ COMMAND_DUMP_EEPROM,
+ COMMAND_GET_MAC_ADDRESS,
+ COMMAND_SET_MAC_ADDRESS,
+ COMMAND_LOAD_MAC_ADDRESS,
+ COMMAND_SAVE_MAC_ADDRESS,
+ COMMAND_SET_DEBUG_MODE,
+ COMMAND_SET_POWER_MODE,
+ COMMAND_GET_POWER_MODE,
+ COMMAND_SET_LINK_MODE,
+ COMMAND_GET_LINK_MODE,
+ COMMAND_GET_CONFIGURATION,
+ COMMAND_DUMP_TEMP,
+ COMMAND_READ_BYTE,
+ COMMAND_READ_WORD,
+ COMMAND_READ_DWORD,
+ COMMAND_WRITE_BYTE,
+ COMMAND_WRITE_WORD,
+ COMMAND_WRITE_DWORD,
+ COMMAND_CHECK_LINK,
+ COMMAND_GET_ERRORS,
+ COMMAND_SET_EEPROM,
+ COMMAND_GET_EEPROM,
+ COMMAND_WRITE_EEPROM_FROM_FILE,
+ COMMAND_WRITE_EEPROM_TO_FILE,
+ COMMAND_VERIFY_EEPROM_WITH_FILE,
+
+//the following codes are intended for cmd9500 only
+// they are not intended to have any use in the driver
+
+ COMMAND_RUN_SERVER,
+ COMMAND_RUN_TUNER,
+ COMMAND_GET_FLOW_PARAMS,
+ COMMAND_SET_FLOW_PARAMS,
+ COMMAND_SET_AMDIX_STS,
+ COMMAND_GET_AMDIX_STS
+};
+
+enum{
+ LAN_REG_ID_REV,
+ LAN_REG_FPGA_REV,
+ LAN_REG_INT_STS,
+ LAN_REG_RX_CFG,
+ LAN_REG_TX_CFG,
+ LAN_REG_HW_CFG,
+ LAN_REG_RX_FIFO_INF,
+ LAN_REG_TX_FIFO_INF,
+ LAN_REG_PMT_CTRL,
+ LAN_REG_LED_GPIO_CFG,
+ LAN_REG_GPIO_CFG,
+ LAN_REG_AFC_CFG,
+ LAN_REG_E2P_CMD,
+ LAN_REG_E2P_DATA,
+ LAN_REG_BURST_CAP,
+ LAN_REG_STRAP_DBG,
+ LAN_REG_DP_SEL,
+ LAN_REG_DP_CMD,
+ LAN_REG_DP_ADDR,
+ LAN_REG_DP_DATA0,
+ LAN_REG_DP_DATA1,
+ LAN_REG_GPIO_WAKE,
+ LAN_REG_INT_EP_CTL,
+ LAN_REG_BULK_IN_DLY,
+ MAX_LAN_REG_NUM
+};
+
+enum{
+ MAC_REG_MAC_CR,
+ MAC_REG_ADDRH,
+ MAC_REG_ADDRL,
+ MAC_REG_HASHH,
+ MAC_REG_HASHL,
+ MAC_REG_MII_ADDR,
+ MAC_REG_MII_DATA,
+ MAC_REG_FLOW,
+ MAC_REG_VLAN1,
+ MAC_REG_VLAN2,
+ MAC_REG_WUFF,
+ MAC_REG_WUCSR,
+ MAC_REG_COE_CR,
+ MAX_MAC_REG_NUM
+};
+
+enum{
+ PHY_REG_BCR,
+ PHY_REG_BSR,
+ PHY_REG_ID1,
+ PHY_REG_ID2,
+ PHY_REG_ANEG_ADV,
+ PHY_REG_ANEG_LPA,
+ PHY_REG_ANEG_ER,
+ PHY_REG_SILICON_REV,
+ PHY_REG_MODE_CTRL_STS,
+ PHY_REG_SPECIAL_MODES,
+ PHY_REG_TSTCNTL,
+ PHY_REG_TSTREAD1,
+ PHY_REG_TSTREAD2,
+ PHY_REG_TSTWRITE,
+ PHY_REG_SPECIAL_CTRL_STS,
+ PHY_REG_SITC,
+ PHY_REG_INT_SRC,
+ PHY_REG_INT_MASK,
+ PHY_REG_SPECIAL,
+ MAX_PHY_REG_NUM
+};
+
+
+typedef struct _SMSC9500_IOCTL_DATA {
+
+ unsigned long dwSignature;
+
+ unsigned long dwCommand;
+
+ unsigned long Data[0x90];
+
+ char Strng1[30];
+
+ char Strng2[10];
+
+} SMSC9500_IOCTL_DATA, *PSMSC9500_IOCTL_DATA;
+
+
+#endif /*IOCTL_9500_H_*/
diff --git a/drivers/net/usb/smsc9500.c b/drivers/net/usb/smsc9500.c
new file mode 100644
index 000000000000..2e2e2f5e70e5
--- /dev/null
+++ b/drivers/net/usb/smsc9500.c
@@ -0,0 +1,5078 @@
+
+ /***************************************************************************
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ***************************************************************************
+ * File: smsc9500.c
+ ***************************************************************************
+ * History:
+ * Vlad Lyalikov, 10/20/2009
+ * Added bulkin_delay parameter for testing pusposes
+ * Vlad Lyalikov, 01/26/2010
+ * Support for ndo framework
+ *****************************************************************************/
+#ifndef __KERNEL__
+# define __KERNEL__
+#endif
+
+#include <linux/version.h>
+
+#define TX_SKB_FORCE_COPY
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13))
+#include <linux/config.h>
+#endif
+
+#include <linux/module.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+#include <linux/moduleparam.h>
+#endif
+
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+
+#include "version.h"
+#include "smscusbnet.h"
+#include "smsc9500.h"
+#include "ioctl_9500.h"
+
+#define CHECK_RETURN_STATUS(A) { if((A) < 0){ goto DONE;} }
+
+unsigned int debug_mode = DBG_WARNING | DBG_INIT | DBG_LINK_CHANGE;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ module_param(debug_mode, uint, 0);
+#else
+ MODULE_PARM(debug_mode,"i");
+#endif
+MODULE_PARM_DESC(debug_mode,"bit 0 enables trace points, bit 1 enables warning points, bit 2 enables eth gpios, bit 3 enables gen gpios");
+
+u32 link_mode=0x7fUL;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ module_param(link_mode, uint, 0);
+#else
+ MODULE_PARM(link_mode,"i");
+#endif
+MODULE_PARM_DESC(link_mode,"Set Link speed and Duplex, 1=10HD,2=10FD,4=100HD,8=100FD,default=0xF");
+
+u32 auto_mdix=0x3U;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ module_param(auto_mdix, uint, 0);
+#else
+ MODULE_PARM(auto_mdix,"i");
+#endif
+MODULE_PARM_DESC(auto_mdix,"Set Auto-MDIX state, 0=StraightCable,1=CrossOver,2=Enable AMDIX,3=controlled by Strap");
+
+u32 mac_addr_hi16=0xFFFFFFFFUL;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ module_param(mac_addr_hi16, uint, 0);
+#else
+ MODULE_PARM(mac_addr_hi16,"i");
+#endif
+MODULE_PARM_DESC(mac_addr_hi16,"Specifies the high 16 bits of the mac address");
+
+u32 mac_addr_lo32=0xFFFFFFFFUL;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ module_param(mac_addr_lo32, uint, 0);
+#else
+ MODULE_PARM(mac_addr_lo32,"i");
+#endif
+MODULE_PARM_DESC(mac_addr_lo32,"Specifies the low 32 bits of the mac address");
+
+u32 phy_addr=0xFFFFFFFFUL;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ module_param(phy_addr, uint, 0);
+#else
+ MODULE_PARM(phy_addr,"i");
+#endif
+MODULE_PARM_DESC(phy_addr,"phy_addr, only valid if it is external phy set by strap; 0-31=external phy with specified address, else autodetect external phy addr");
+
+int scatter_gather=FALSE;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ module_param(scatter_gather,bool, 0);
+#else
+MODULE_PARM(scatter_gather,"bool");
+#endif
+MODULE_PARM_DESC(scatter_gather,"Enable Scatter Gather");
+
+int tx_Csum=FALSE;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ module_param(tx_Csum,bool, 0);
+#else
+ MODULE_PARM(tx_Csum,"bool");
+#endif
+MODULE_PARM_DESC(tx_Csum,"Enable Tx Hardware Checksum Offload");
+
+int rx_Csum=FALSE;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ module_param(rx_Csum,bool, 0);
+#else
+ MODULE_PARM(rx_Csum,"bool");
+#endif
+MODULE_PARM_DESC(tx_Csum,"Enable Rx Hardware Checksum Offload");
+
+u32 bulkin_delay=DEFAULT_BULK_IN_DELAY;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ module_param(bulkin_delay,uint, 0);
+#else
+ MODULE_PARM(bulkin_delay,"i");
+#endif
+MODULE_PARM_DESC(bulkin_delay,"16 bit value in units of 16ns to delay UTX sending data");
+
+int TurboMode=TRUE;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ module_param(TurboMode,bool, 0);
+#else
+ MODULE_PARM(TurboMode,"bool");
+#endif
+MODULE_PARM_DESC(TurboMode,"Enable Turbo Mode");
+
+int LinkActLedCfg=FALSE;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ module_param(LinkActLedCfg,bool, 0);
+#else
+ MODULE_PARM(LinkActLedCfg,"bool");
+#endif
+MODULE_PARM_DESC(LinkActLedCfg,"Enables separate Link and Activity LEDs in LAN9500A");
+
+u32 LinkLedOnGpio=11;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ module_param(LinkLedOnGpio, uint, 0);
+#else
+ MODULE_PARM(LinkLedOnGpio,"i");
+#endif
+MODULE_PARM_DESC(LinkLedOnGpio,"Enable separate Link and Activity LEDs in LAN9500 and specifies gpio port for link status");
+
+u32 LinkLedBufType=0;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ module_param(LinkLedBufType, bool, 0);
+#else
+ MODULE_PARM(LinkLedBufType,"bool");
+#endif
+MODULE_PARM_DESC(LinkLedBufType,"Specifies gpio buffer type for link led");
+
+u32 LinkLedPolarity = 0;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ module_param(LinkLedPolarity, bool, 0);
+#else
+ MODULE_PARM(LinkLedPolarity,"bool");
+#endif
+MODULE_PARM_DESC(LinkLedPolarity,"Specifies active level on gpio port");
+
+/*
+linkdownsuspend = 0----> Disabled
+linkdownsuspend = 1----> Enabled, wake up on auto-negotiation complete, device is in suspend0.
+linkdownsuspend = 2----> Enabled, wake up on energy detection, device is in suspend1.
+*/
+static int linkdownsuspend=2;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+ module_param(linkdownsuspend, uint, 0);
+#else
+ MODULE_PARM(linkdownsuspend,"i");
+#endif
+MODULE_PARM_DESC(linkdownsuspend,"Suspend device when link is down");
+
+static int dynamicsuspend=0;
+module_param(dynamicsuspend,bool, 0);
+MODULE_PARM_DESC(dynamicsuspend,"Enable dynamic autosuspend mode");
+
+static int smartdetach=0;
+module_param(smartdetach,bool, 0);
+MODULE_PARM_DESC(smartdetach,"Enable smart detach mode, for LAN9500A only");
+
+/********static function and variable declartion****************/
+static int smsc9500_reset(struct usbnet *dev);
+static int smsc9500_get_stats(struct usbnet *dev, void *data);
+static int smsc9500_private_ioctl(PADAPTER_DATA privateData, struct usbnet *dev, PSMSC9500_IOCTL_DATA ioctlData);
+static int smsc9500_device_recovery(struct usbnet *dev);
+static int SetGpo(struct usbnet * dev, u32 Gpo, u32 State);
+static int Smsc9500SystemSuspend (struct usb_interface *intf, pm_message_t state);
+static int Smsc9500SystemResume(struct usb_interface *intf);
+static u16 CalculateCrc16(const BYTE * bpData,const u32 dwLen, const BOOLEAN fBitReverse);
+static int SetLinkDownWakeupEvents(struct usbnet *dev, int wakeUpMode);
+static int ResetLinkDownWakeupEvents(struct usbnet *dev);
+static int Smsc9500AutoSuspend (struct usb_interface *intf, pm_message_t state);
+static int Smsc9500AutoResume(struct usb_interface *intf);
+static int EnablePHYWakeupInterrupt(struct usbnet *dev, u32 interrupt);
+static int DisablePHYWakeupInterrupt(struct usbnet *dev, u32 interrupt);
+
+static u32 LanRegMap[MAX_LAN_REG_NUM];
+static u32 MacRegMap[MAX_MAC_REG_NUM];
+static u32 PhyRegMap[MAX_PHY_REG_NUM];
+/***************************************************************/
+
+enum{
+ SMSC9500_FAIL = -1,
+ SMSC9500_SUCCESS = 0
+};
+
+/***************************************************************/
+static int smsc9500_read_reg(struct usbnet *dev, u32 index, u32 *data)
+{
+ int ret = 0;
+ u32 *buf = NULL;
+ u16 retry_count = 0;
+
+ BUG_ON(!dev);
+
+//The heap buffer should be used for usb_control_msg, because the stack might not be DMA-mappable
+//Control message is very slow so it really isn't big deal to dynamically allocate the data
+ buf = kmalloc (sizeof(u32), GFP_KERNEL);
+ if(buf == NULL){
+ return SMSC9500_FAIL;
+ }
+
+ do{
+ ret=usb_control_msg(
+ dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0),
+ USB_VENDOR_REQUEST_READ_REGISTER,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 00,
+ index,
+ (void*)buf,
+ sizeof(u32),
+ USB_CTRL_GET_TIMEOUT);
+ }while((ret < 0) && (retry_count++ < 3));
+
+ if (ret<0){
+ SMSC_WARNING("Failed to read register index 0x%08x, set flag to recover", index);
+ set_bit (EVENT_DEV_RECOVERY, &dev->flags);
+ }else{
+ le32_to_cpus(buf);
+ *data = *buf;
+ }
+
+ kfree(buf);
+
+ return ret;
+}
+
+static int smsc9500_write_reg(struct usbnet *dev, u32 index, u32 data)
+{
+ int ret = 0;
+ u32* buf = NULL;
+ u16 retry_count = 0;
+
+ BUG_ON(!dev);
+
+//The heap buffer should be used for usb_control_msg, because the stack might not be DMA-mappable
+//Control message is very slow so it really isn't big deal to dynamically allocate the data
+ buf = kmalloc (sizeof(u32), GFP_KERNEL);
+ if(buf == NULL){
+ return SMSC9500_FAIL;
+ }
+ *buf = data;
+
+ cpu_to_le32s(buf);
+
+ do{
+ ret=usb_control_msg(
+ dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ USB_VENDOR_REQUEST_WRITE_REGISTER,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 00,
+ index,
+ buf,
+ sizeof(u32),
+ USB_CTRL_SET_TIMEOUT);
+ }while((ret < 0) && (retry_count++ < 3));
+
+ if (ret<0){
+ SMSC_WARNING("Failed to write register index 0x%08x, set flag to recover", index);
+ set_bit (EVENT_DEV_RECOVERY, &dev->flags);
+ }
+
+ kfree(buf);
+
+ return ret;
+}
+
+static int smsc9500_set_feature(struct usbnet *dev, u32 feature)
+{
+ BUG_ON(!dev);
+
+ cpu_to_le32s((u32*)&feature);
+
+ return usb_control_msg(
+ dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ USB_REQ_SET_FEATURE,
+ USB_RECIP_DEVICE,
+ feature,
+ 0,
+ NULL,
+ 0,
+ USB_CTRL_SET_TIMEOUT);
+
+}
+
+static int smsc9500_clear_feature(struct usbnet *dev, u32 feature)
+{
+ BUG_ON(!dev);
+
+ cpu_to_le32s((u32*)&feature);
+
+ return usb_control_msg(
+ dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ USB_REQ_CLEAR_FEATURE,
+ USB_RECIP_DEVICE,
+ feature,
+ 0,
+ NULL,
+ 0,
+ USB_CTRL_SET_TIMEOUT);
+}
+
+
+static int smsc9500_read_phy(struct usbnet *dev, u32 Register, u32 *pValue32 )
+
+{
+ int ret = SMSC9500_FAIL;
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+ u32 dwValue,dwAddr;
+ int Count;
+
+ BUG_ON(!dev);
+
+ if(down_interruptible(&adapterData->phy_mutex)){
+ return -EINTR;
+ }
+ // confirm MII not busy
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, MII_ADDR, &dwValue));
+
+ if ((dwValue & MII_BUSY_) != 0UL)
+ {
+ SMSC_WARNING("MII is busy in smsc9500_read_phy\n");
+ goto DONE;
+ }
+
+ // set the address, index & direction (read from PHY)
+ dwAddr = ((adapterData->dwPhyAddress & 0x1FUL)<<11) | ((Register & 0x1FUL)<<6)|MII_READ_;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, MII_ADDR, dwAddr));
+
+ // Loop until the read is completed w/timeout
+ for(Count=1;Count<100;Count++)
+ {
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, MII_ADDR, &dwValue));
+
+ if(!(dwValue & MII_BUSY_))
+ break;
+ udelay(1);
+
+ }
+
+ if (Count < 100)
+ {
+ ret = smsc9500_read_reg(dev, MII_DATA, pValue32);
+ }
+ else
+ {
+ SMSC_WARNING ("Timed out reading MII register %08X\n",Register & 0x1f);
+
+ }
+DONE:
+ up(&adapterData->phy_mutex);
+ return ret;
+
+} /* smsc9500_read_phy */
+
+
+
+static int smsc9500_write_phy(struct usbnet *dev, u32 Register, u32 pValue32)
+{
+
+ int ret = SMSC9500_FAIL;
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+ u32 dwValue,dwAddr;
+ int Count;
+
+ BUG_ON(!dev);
+
+ if(down_interruptible(&adapterData->phy_mutex)){
+ return -EINTR;
+ }
+
+ if(Register==0) {
+ if(((LOWORD(pValue32))&0x1200)==0x1200) {
+ adapterData->wLastADVatRestart=adapterData->wLastADV;
+ }
+ }
+ if(Register==4) {
+ adapterData->wLastADV=LOWORD(pValue32);
+ }
+
+ // confirm MII not busy
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, MII_ADDR, &dwValue));
+
+ if ((dwValue & MII_BUSY_) != 0UL)
+ {
+ SMSC_WARNING ("MII is busy in smsc9500_read_phy\n");
+ goto DONE;
+ }
+
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, MII_DATA, pValue32));
+
+ // set the address, index & direction (read from PHY)
+ dwAddr = ((adapterData->dwPhyAddress & 0x1FUL)<<11) | ((Register & 0x1FUL)<<6)|MII_WRITE_;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, MII_ADDR, dwAddr));
+
+ // Loop until the read is completed w/timeout
+ for(Count=1;Count<100;Count++)
+ {
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, MII_ADDR, &dwValue));
+ if(!(dwValue & MII_BUSY_))
+ break;
+ udelay(1);
+
+ }
+
+ if (Count < 100)
+ {
+ ret=0;
+
+ }
+ else
+ {
+ SMSC_WARNING("Timed out writing MII register %08X\n",Register & 0x1f);
+
+ }
+DONE:
+ up(&adapterData->phy_mutex);
+ return ret;
+
+} /* smsc9500_write_phy */
+
+static int smsc9500_eeprom_IsBusy(struct usbnet *dev)
+{
+ int retVal = 0;
+ u32 dwValue;
+ int Count;
+
+ BUG_ON(!dev);
+
+ for(Count=0;Count<1000;Count++) //40ms
+ {
+ if(smsc9500_read_reg(dev, E2P_CMD, &dwValue) < 0){
+ return SMSC9500_FAIL;
+ }
+ if (!(dwValue & E2P_CMD_BUSY_) || (dwValue & E2P_CMD_TIMEOUT_))
+ {
+ break;
+ }
+ udelay(40);
+ }
+ if ((dwValue & E2P_CMD_TIMEOUT_) || (dwValue & E2P_CMD_BUSY_)){
+ SMSC_WARNING("EEPROM read operation timeout");
+ retVal = SMSC9500_FAIL;
+ }
+
+ return retVal;
+}
+
+/* Read EEPROM data
+ * pbValue: buffer pointer for data
+ * */
+static int smsc9500_read_eeprom(struct usbnet *dev, u32 dwOffset, u32 dwLength, BYTE* pbValue)
+{
+
+ int ret = SMSC9500_FAIL;
+ u32 dwValue,dwAddr;
+ int Count, i;
+ PADAPTER_DATA adapterData;
+
+ BUG_ON(!dev);
+ BUG_ON(!pbValue);
+
+ adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+ if(dwOffset + dwLength > adapterData->eepromSize){
+ SMSC_WARNING("EEPROM: out of eeprom space range, offset = %d, dwLength = %d", dwOffset, dwLength);
+ }
+
+ if(down_interruptible(&adapterData->eeprom_mutex)){
+ return -EINTR;
+ }
+
+ // confirm eeprom not busy
+ for(Count=0;Count<100;Count++)
+ {
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, E2P_CMD, &dwValue));
+ if (!(dwValue & E2P_CMD_BUSY_) || !(dwValue & E2P_CMD_LOADED_))
+ {
+ break;
+ }
+ udelay(40);
+ }
+ if (!(dwValue & E2P_CMD_LOADED_))
+ {
+ SMSC_WARNING("No EEPROM present");
+ goto DONE;
+ }
+ if ((dwValue & E2P_CMD_BUSY_) != 0UL)
+ {
+ SMSC_WARNING("EEPROM is busy ");
+ goto DONE;
+ }
+ dwAddr = dwOffset;
+ for(i=0; i<dwLength; i++){
+ //Isuue command
+ dwValue = E2P_CMD_BUSY_ | E2P_CMD_READ_ | (dwAddr & E2P_CMD_ADDR_);
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, E2P_CMD, dwValue));
+ CHECK_RETURN_STATUS(smsc9500_eeprom_IsBusy(dev));
+
+ //Read data when ready
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, E2P_DATA, &dwValue));
+
+ pbValue[i] = dwValue & 0xFF;
+ dwAddr++;
+ }
+ ret = 0;
+DONE:
+ up(&adapterData->eeprom_mutex);
+ return ret;
+
+} /* smsc9500_read_eeprom */
+
+static int smsc9500_write_eeprom(struct usbnet *dev, u32 dwOffset, u32 dwLength, BYTE* pbValue)
+{
+ int ret = SMSC9500_FAIL;
+ u32 dwValue,dwAddr;
+ int Count, i;
+ PADAPTER_DATA adapterData;
+
+ BUG_ON(!dev);
+ BUG_ON(!pbValue);
+
+ adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+ if(dwOffset + dwLength > adapterData->eepromSize){
+ SMSC_WARNING("EEPROM: out of eeprom space range");
+ }
+
+ if(down_interruptible(&adapterData->eeprom_mutex)){
+ return -EINTR;
+ }
+
+ // confirm eeprom not busy
+ for(Count=0;Count<100;Count++)
+ {
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, E2P_CMD, &dwValue));
+
+ if (!(dwValue & E2P_CMD_BUSY_))
+ {
+ break;
+ }
+ udelay(40);
+ }
+
+ if ((dwValue & E2P_CMD_BUSY_) != 0UL)
+ {
+ SMSC_WARNING("EEPROM is busy ");
+ goto DONE;
+ }
+
+ //Iuuse write/erase enable command
+ dwValue = E2P_CMD_BUSY_ | E2P_CMD_EWEN_;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, E2P_CMD, dwValue));
+ CHECK_RETURN_STATUS(smsc9500_eeprom_IsBusy(dev));
+
+ dwAddr = dwOffset;
+ for(i=0; i<dwLength; i++){
+
+ //Fill data register
+ dwValue = pbValue[i];
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, E2P_DATA, dwValue));
+
+ //Send "write" command
+ dwValue = E2P_CMD_BUSY_ | E2P_CMD_WRITE_ | (dwAddr & E2P_CMD_ADDR_);
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, E2P_CMD, dwValue));
+ CHECK_RETURN_STATUS(smsc9500_eeprom_IsBusy(dev));
+
+ dwAddr++;
+ }
+
+ ret = SMSC9500_SUCCESS;
+DONE:
+
+ up(&adapterData->eeprom_mutex);
+ return ret;
+
+} /* smsc9500_write_eeprom */
+
+static int IsDataPortReady(struct usbnet *dev){
+ int ret = FALSE;
+ int count = 0;
+ u32 dwValue;
+
+// confirm data port is not busy
+ for(count=0; count<100; count++)
+ {
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, DP_SEL, &dwValue));
+ if (dwValue & DP_SEL_DPRDY){
+ ret = TRUE;
+ break;
+ }
+ udelay(40);
+ }
+
+ if (ret == FALSE)
+ {
+ SMSC_WARNING("Data port is busy ");
+ }
+DONE:
+ return ret;
+}
+
+/* Read data from internal RAM
+ * ramSel: Choose which internal RAM to access.
+ * startAddr: The first offset to access.
+ * length: Data length in DWORD.
+ * valLow: Low 32 bits buffer pointer.
+ * valHigh: High 5 bits buffer pointer. If null, will ignore.
+ * */
+static int ReadDataPort(struct usbnet *dev, int ramSel, u32 startAddr, u32 length, u32 *valLow, u32 *valHigh)
+{
+ u32 dwValue;
+ int ret = SMSC9500_FAIL;
+ int i;
+ PADAPTER_DATA adapterData;
+
+ BUG_ON(!dev);
+ adapterData = (PADAPTER_DATA)(dev->data[0]);
+ BUG_ON(!adapterData);
+
+ if(down_interruptible(&adapterData->internal_ram_mutex)){
+ return -EINTR;
+ }
+
+ // confirm data port not busy
+ if(!IsDataPortReady(dev))goto DONE;
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, DP_SEL, &dwValue));
+ dwValue &= ~DP_SEL_RSEL;
+ switch(ramSel){
+ case RAMSEL_FCT: dwValue |= DP_SEL_RSEL_FCT; break;
+ case RAMSEL_EEPROM: dwValue |= DP_SEL_RSEL_EEPROM; break;
+ case RAMSEL_TXTLI: dwValue |= DP_SEL_RSEL_TXTLI; break;
+ case RAMSEL_RXTLI: dwValue |= DP_SEL_RSEL_RXTLI; break;
+ }
+ dwValue |= DP_SEL_TESTEN;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, DP_SEL, dwValue));
+
+ for(i=0; i<length; i++){
+ //Set device ram address
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, DP_ADDR, startAddr + i));
+ //Enable reading
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, DP_CMD, DP_CMD_READ));
+
+ if(!IsDataPortReady(dev))goto DONE;
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, DP_DATA0, valLow + i));
+ if(valHigh){
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, DP_DATA1, valHigh + i));
+ }
+ }
+
+ ret = SMSC9500_SUCCESS;
+DONE:
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, DP_SEL, &dwValue));
+ dwValue &= ~DP_SEL_TESTEN;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, DP_SEL, dwValue));
+
+ up(&adapterData->internal_ram_mutex);
+
+ return ret;
+}
+
+static void smsc9500_status(struct usbnet *dev, struct urb *urb)
+{
+ struct smsc9500_int_data *event;
+ int hasFrame;
+
+ BUG_ON(!dev);
+ BUG_ON(!urb);
+
+ SMSC_TRACE(DBG_INTR,"---->in smsc9500_status\n");
+
+ if (urb->actual_length < 4) {
+ SMSC_WARNING("urb->actual_length= %d",urb->actual_length);
+ return;
+ }
+
+ event = urb->transfer_buffer;
+
+ le32_to_cpus((u32*)&event->IntEndPoint);
+
+ SMSC_TRACE(DBG_INTR, "event->IntEndPoint= 0x%08x\n", event->IntEndPoint);
+ hasFrame = event->IntEndPoint &INT_END_RXFIFO_HAS_FRAME_;
+
+ if (hasFrame) {
+ dev->StopSummitUrb=0;
+ tasklet_schedule (&dev->bh);
+ }
+
+ SMSC_TRACE(DBG_INTR,"<----out of smsc9500_status\n");
+}
+
+
+static int smsc9500_get_stats(struct usbnet *dev, void *data)
+{
+ int ret = 0;
+ void* buf;
+ u16 retry_count = 0;
+
+ BUG_ON(!dev);
+ BUG_ON(!data);
+
+ SMSC_TRACE(DBG_RX, "in smsc9500_get_stats\n");
+
+ buf = kmalloc (sizeof(SMSC9500_RX_STATS), GFP_KERNEL);
+ if(buf == NULL){
+ return SMSC9500_FAIL;
+ }
+
+ do{
+ ret=usb_control_msg(
+ dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0),
+ USB_VENDOR_REQUEST_GET_STATS,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 00,
+ 0,
+ buf,
+ sizeof(SMSC9500_RX_STATS),
+ USB_CTRL_SET_TIMEOUT);
+ }while((ret < 0) && (retry_count++ < 3));
+
+ if (ret < 0){
+ SMSC_WARNING("Failed to get status, set flag to recover");
+ set_bit (EVENT_DEV_RECOVERY, &dev->flags);
+ }else{
+ memcpy(data, buf, sizeof(SMSC9500_RX_STATS));
+ }
+
+ kfree(buf);
+
+ return ret;
+}
+
+#if 0 /* commenting as it was causing panic on harmony platform */
+
+static void smsc9500_async_cmd_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct USB_CONTEXT * usb_context = (struct USB_CONTEXT *)urb->context;
+
+ if (urb->status < 0)
+ SMSC_WARNING("smsc9500_async_cmd_callback() failed with %d\n",urb->status);
+
+ complete((struct completion *)&usb_context->notify);
+
+ kfree(&usb_context->req);
+ usb_free_urb(urb);
+}
+
+static int
+smsc9500_read_reg_async(struct usbnet *dev, u32 index, void *data, int wait)
+{
+ int ret = ASYNC_RW_SUCCESS, expire;
+ struct USB_CONTEXT * usb_context;
+ int status;
+ struct urb *urb;
+ u32 size=4;
+
+ BUG_ON(!dev);
+ BUG_ON(!data);
+
+ if ((urb = usb_alloc_urb(0, GFP_ATOMIC)) == NULL) {
+ SMSC_WARNING("Error allocating URB in write_cmd_async!");
+ return ASYNC_RW_FAIL;
+ }
+
+ if ((usb_context = kmalloc(sizeof(struct USB_CONTEXT), GFP_ATOMIC)) == NULL) {
+ SMSC_WARNING( "Failed to allocate memory for control request");
+ usb_free_urb(urb);
+ return ASYNC_RW_FAIL;
+ }
+
+ usb_context->req.bRequestType = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ usb_context->req.bRequest = USB_VENDOR_REQUEST_READ_REGISTER;
+ usb_context->req.wValue = 00;
+ usb_context->req.wIndex = cpu_to_le32(index);
+ usb_context->req.wLength = cpu_to_le32(size);
+ init_completion(&usb_context->notify);
+
+ usb_fill_control_urb(urb, dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0),
+ (void *)&usb_context->req, data, size,
+ (usb_complete_t)smsc9500_async_cmd_callback, (void*)usb_context);
+
+ if((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+ SMSC_WARNING( "Error submitting the control message: status=%d", status);
+ kfree(usb_context);
+ usb_free_urb(urb);
+ }
+
+ if(wait){
+//wait_for_completion_timeout only implemented in 2.6.11 and higher kernel version
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,11))
+ expire = msecs_to_jiffies(USB_CTRL_SET_TIMEOUT);
+ if (!wait_for_completion_timeout(&usb_context->notify, expire)) {
+
+ ret = ASYNC_RW_TIMEOUT;
+ SMSC_TRACE(DBG_WARNING,"urb timeout \n");
+ kfree(usb_context);
+ usb_free_urb(urb);
+ }
+#endif
+ }
+
+ return ret;
+
+}
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22))
+static void CalculateTxChecksumOffset(
+ struct sk_buff *skb,
+ int *csum_start_offset
+ )
+{
+ unsigned int skbFragCnt;
+ int i;
+ u32 offset;
+
+ skbFragCnt = skb_shinfo(skb)->nr_frags + 1;
+
+ // Initialize csum offset locations as if it was single frag.
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22))
+ SMSC_ASSERT(skb->h.raw);
+ #else
+ SMSC_ASSERT(skb->transport_header); // Should never happen for a CHECKSUM_HW packet.
+ #endif
+
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22))
+ *csum_start_offset = skb->h.raw - skb->data;
+ #else
+ *csum_start_offset = (unsigned long)skb->transport_header - (unsigned long)skb->data;
+ #endif
+
+ offset = (skbFragCnt == 1) ? skb->len : (skb->len - skb->data_len);
+
+ // Process all fragments
+ for(i=0;i<(skbFragCnt-1);i++)
+ {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ unsigned char *frag_addr = (unsigned char *) (page_address(frag->page) + frag->page_offset);
+
+ // Find if transport header start belongs to this fragment and if so calculate offset from start of packet.
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22))
+ if((frag_addr <= skb->h.raw) && ((frag_addr + frag->size) >=skb->h.raw))
+ {
+ *csum_start_offset = offset + ((u32)skb->h.raw) - ((u32)frag_addr);
+ }
+ #else
+ if((frag_addr <= (unsigned char *)((unsigned long)skb->transport_header)) &&
+ ((frag_addr + frag->size) >= (unsigned char *)((unsigned long)skb->transport_header)))
+ {
+ *csum_start_offset = offset + ((unsigned long)skb->transport_header) - ((unsigned long)frag_addr);
+ }
+ #endif
+
+ SMSC_ASSERT((offset + frag->size) <= skb->len);
+
+ offset += frag->size;
+ }
+
+}
+#endif
+
+static void Tx_StopQueue(
+ struct usbnet *dev,u32 dwSource)
+{
+ unsigned long intFlags=0;
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+ spin_lock_irqsave(&(adapterData->TxQueueLock),intFlags);
+ if(adapterData->dwTxQueueDisableMask==0) {
+ netif_stop_queue(dev->net);
+ }
+ adapterData->dwTxQueueDisableMask|=dwSource;
+ spin_unlock_irqrestore(&(adapterData->TxQueueLock),intFlags);
+}
+
+static void Tx_WakeQueue(
+ struct usbnet *dev,u32 dwSource)
+{
+ unsigned long intFlags=0;
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+ spin_lock_irqsave(&(adapterData->TxQueueLock),intFlags);
+ adapterData->dwTxQueueDisableMask&=(~dwSource);
+ if(adapterData->dwTxQueueDisableMask==0) {
+ netif_wake_queue(dev->net);
+ }
+ spin_unlock_irqrestore(&(adapterData->TxQueueLock),intFlags);
+}
+
+
+//returns hash bit number for given MAC address
+//example:
+// 01 00 5E 00 00 01 -> returns bit number 31
+static u32 Rx_Hash(BYTE addr[6])
+{
+ int i;
+ u32 crc=0xFFFFFFFFUL;
+ u32 poly=0xEDB88320UL;
+ u32 result=0;
+ for(i=0;i<6;i++)
+ {
+ int bit;
+ u32 data=((u32)addr[i]);
+ for(bit=0;bit<8;bit++)
+ {
+ u32 p = (crc^((u32)data))&1UL;
+ crc >>= 1;
+ if(p!=0) crc ^= poly;
+ data >>=1;
+ }
+ }
+ result=((crc&0x01UL)<<5)|
+ ((crc&0x02UL)<<3)|
+ ((crc&0x04UL)<<1)|
+ ((crc&0x08UL)>>1)|
+ ((crc&0x10UL)>>3)|
+ ((crc&0x20UL)>>5);
+ return result;
+}
+
+static int smsc9500_rx_setmulticastlist(struct usbnet *dev)
+{
+
+ u32 local_MACCR, dwHashHi,dwHashLo;
+ u32 ret = SMSC9500_FAIL;
+
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+ if (dev->suspendFlag) {
+ return 0;
+ }
+ if(down_interruptible(&adapterData->RxFilterLock)){
+ return -EINTR;
+ }
+
+ SMSC_TRACE(DBG_MCAST, "---------->in smsc9500_set_multicast\n");
+
+ if(dev->net->flags & IFF_PROMISC) {
+ SMSC_TRACE(DBG_MCAST,"Promiscuous Mode Enabled");
+ adapterData->set_bits_mask = MAC_CR_PRMS_;
+ adapterData->clear_bits_mask = (MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+
+ adapterData->HashHi = 0UL;
+ adapterData->HashLo = 0UL;
+ goto PREPARE;
+ }
+
+ if(dev->net->flags & IFF_ALLMULTI) {
+ SMSC_TRACE(DBG_MCAST, "Receive all Multicast Enabled");
+ adapterData->set_bits_mask = MAC_CR_MCPAS_;
+ adapterData->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_HPFILT_);
+
+ adapterData->HashHi = 0UL;
+ adapterData->HashLo = 0UL;
+ goto PREPARE;
+ }
+
+
+ if(dev->net->mc_count>0) {
+ u32 dwHashH=0;
+ u32 dwHashL=0;
+ u32 dwCount=0;
+ struct dev_mc_list *mc_list=dev->net->mc_list;
+
+ adapterData->set_bits_mask = MAC_CR_HPFILT_;
+ adapterData->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_MCPAS_);
+
+ while(mc_list!=NULL) {
+ dwCount++;
+ if((mc_list->dmi_addrlen)==6) {
+ u32 dwMask=0x01UL;
+ u32 dwBitNum=Rx_Hash(mc_list->dmi_addr);
+
+ dwMask<<=(dwBitNum&0x1FUL);
+ if(dwBitNum&0x20UL) {
+ dwHashH|=dwMask;
+ } else {
+ dwHashL|=dwMask;
+ }
+ } else {
+ SMSC_WARNING("dmi_addrlen!=6");
+ }
+ mc_list=mc_list->next;
+ }
+ if(dwCount!=((u32)(dev->net->mc_count))) {
+ SMSC_WARNING("dwCount!=dev->net->mc_count");
+ }
+ SMSC_TRACE(DBG_MCAST, "Multicast: HASHH=0x%08X,HASHL=0x%08X",dwHashH,dwHashL);
+ adapterData->HashHi = dwHashH;
+ adapterData->HashLo = dwHashL;
+ }
+ else
+ {
+ adapterData->set_bits_mask = 0L;
+ adapterData->clear_bits_mask = (MAC_CR_PRMS_ | MAC_CR_MCPAS_ | MAC_CR_HPFILT_);
+
+ SMSC_TRACE(DBG_MCAST, "Receive own packets only.");
+ adapterData->HashHi = 0UL;
+ adapterData->HashLo = 0UL;
+ }
+
+
+PREPARE:
+ up(&adapterData->RxFilterLock);
+
+ dwHashHi=adapterData->HashHi;
+ dwHashLo=adapterData->HashLo;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev,HASHH,dwHashHi));
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev,HASHL,dwHashLo));
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev,MAC_CR,&local_MACCR));
+
+ local_MACCR |= adapterData->set_bits_mask;
+ local_MACCR &= ~(adapterData->clear_bits_mask);
+
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev,MAC_CR,local_MACCR));
+
+
+ SMSC_TRACE(DBG_MCAST, "<---------out of smsc9500_set_multicast");
+ ret = 0;
+DONE:
+ return ret;
+}
+
+static void smsc9500_set_multicast(struct net_device *netdev)
+{
+ struct usbnet *dev=netdev_priv(netdev);
+ smscusbnet_defer_myevent(dev, EVENT_SET_MULTICAST);
+
+}
+
+static int Phy_GetLinkMode(struct usbnet *dev)
+{
+ u32 dwTemp, dwValue, result=LINK_OFF;
+ u16 wRegBcr=0;
+ u16 wRegBSR,wRegLPA;
+ int ret = SMSC9500_FAIL;
+
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+ SMSC_TRACE(DBG_LINK, "---------->in Phy_GetLinkMode");
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_BSR,&dwTemp));
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_BSR,&dwTemp));
+
+ wRegBSR=LOWORD(dwTemp);
+
+ adapterData->dwLinkSettings=LINK_OFF;
+
+ if(wRegBSR&PHY_BSR_LINK_STATUS_) {
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_BCR,&dwValue));
+ wRegBcr=LOWORD(dwValue);
+
+ if(wRegBcr & PHY_BCR_AUTO_NEG_ENABLE_) {
+ u32 linkSettings=LINK_AUTO_NEGOTIATE;
+ u16 wRegADV=adapterData->wLastADVatRestart;
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_ANEG_LPA,&dwTemp));
+ wRegLPA=LOWORD(dwTemp);
+
+ if(wRegADV & PHY_ANEG_ADV_ASYMP_) {
+ linkSettings |= LINK_ASYMMETRIC_PAUSE;
+ }
+ if(wRegADV & PHY_ANEG_ADV_SYMP_) {
+ linkSettings |= LINK_SYMMETRIC_PAUSE;
+ }
+ if(wRegADV & PHY_ANEG_LPA_100FDX_) {
+ linkSettings |= LINK_SPEED_100FD;
+ }
+ if(wRegADV & PHY_ANEG_LPA_100HDX_) {
+ linkSettings |= LINK_SPEED_100HD;
+ }
+ if(wRegADV & PHY_ANEG_LPA_10FDX_) {
+ linkSettings |= LINK_SPEED_10FD;
+ }
+ if(wRegADV & PHY_ANEG_LPA_10HDX_) {
+ linkSettings |= LINK_SPEED_10HD;
+ }
+ adapterData->dwLinkSettings=linkSettings;
+
+ wRegLPA &= wRegADV;
+ if(wRegLPA & PHY_ANEG_LPA_100FDX_) {
+ result = LINK_SPEED_100FD;
+ } else if(wRegLPA & PHY_ANEG_LPA_100HDX_) {
+ result = LINK_SPEED_100HD;
+ } else if(wRegLPA & PHY_ANEG_LPA_10FDX_) {
+ result = LINK_SPEED_10FD;
+ } else if(wRegLPA & PHY_ANEG_LPA_10HDX_) {
+ result = LINK_SPEED_10HD;
+ }
+ } else {
+ if(wRegBcr & PHY_BCR_SPEED_SELECT_) {
+ if(wRegBcr & PHY_BCR_DUPLEX_MODE_) {
+ adapterData->dwLinkSettings=result=LINK_SPEED_100FD;
+ } else {
+ adapterData->dwLinkSettings=result=LINK_SPEED_100HD;
+ }
+ } else {
+ if(wRegBcr & PHY_BCR_DUPLEX_MODE_) {
+ adapterData->dwLinkSettings=result=LINK_SPEED_10FD;
+ } else {
+ adapterData->dwLinkSettings=result=LINK_SPEED_10HD;
+ }
+ }
+ }
+ }
+ adapterData->dwLinkSpeed=result;
+ SMSC_TRACE(DBG_LINK,"<----------out of Phy_GetLinkMode");
+
+ ret = 0;
+DONE:
+ return ret;
+}
+
+static int Phy_UpdateLinkMode(struct usbnet *dev)
+{
+
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+ int ret = SMSC9500_FAIL;
+ u32 dwOldLinkSpeed=adapterData->dwLinkSpeed;
+ u32 dwTemp,dwValue;
+
+ SMSC_TRACE(DBG_LINK,"---------->in Phy_UpdateLinkMode");
+
+ Phy_GetLinkMode(dev);
+
+ if(dwOldLinkSpeed!=(adapterData->dwLinkSpeed)) {
+ if(adapterData->dwLinkSpeed!=LINK_OFF) {
+ u32 dwRegVal=0;
+ switch(adapterData->dwLinkSpeed) {
+ case LINK_SPEED_10HD:
+ SMSC_TRACE(DBG_LINK_CHANGE,"Link is now UP at 10Mbps HD");
+ break;
+ case LINK_SPEED_10FD:
+ SMSC_TRACE(DBG_LINK_CHANGE,"Link is now UP at 10Mbps FD");
+ break;
+ case LINK_SPEED_100HD:
+ SMSC_TRACE(DBG_LINK_CHANGE,"Link is now UP at 100Mbps HD");
+ break;
+ case LINK_SPEED_100FD:
+ SMSC_TRACE(DBG_LINK_CHANGE,"Link is now UP at 100Mbps FD");
+ break;
+ default:
+ SMSC_TRACE(DBG_LINK_CHANGE,"Link is now UP at Unknown Link Speed, dwLinkSpeed=0x%08X",
+ adapterData->dwLinkSpeed);
+ break;
+ }
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, MAC_CR,&dwRegVal));
+ dwRegVal&=~(MAC_CR_FDPX_|MAC_CR_RCVOWN_);
+ switch(adapterData->dwLinkSpeed) {
+ case LINK_SPEED_10HD:
+ case LINK_SPEED_100HD:
+ dwRegVal|=MAC_CR_RCVOWN_;
+ break;
+ case LINK_SPEED_10FD:
+ case LINK_SPEED_100FD:
+ dwRegVal|=MAC_CR_FDPX_;
+ break;
+ default:break;
+ }
+
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, MAC_CR, dwRegVal));
+
+ if(adapterData->dwLinkSettings&LINK_AUTO_NEGOTIATE) {
+ u16 linkPartner=0;
+ u16 localLink=0;
+
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev, PHY_ANEG_ADV, &dwTemp));
+ localLink=LOWORD(dwTemp);
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev, PHY_ANEG_LPA, &dwTemp));
+ linkPartner=LOWORD(dwTemp);
+ switch(adapterData->dwLinkSpeed) {
+ case LINK_SPEED_10FD:
+ case LINK_SPEED_100FD:
+ if(((localLink&linkPartner)&((u16)PHY_ANEG_ADV_SYMP_)) != ((u16)0U)) {
+ //Enable PAUSE receive and transmit
+ dwTemp=0xFFFF0002UL;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev,FLOW, dwTemp));
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev,AFC_CFG,&dwValue));
+ dwValue=dwValue|0x0000000FUL;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, AFC_CFG,dwValue));
+
+ } else if(((localLink&((u16)0x0C00U))==((u16)0x0C00U)) &&
+ ((linkPartner&((u16)0x0C00U))==((u16)0x0800U)))
+ {
+ //Enable PAUSE receive, disable PAUSE transmit
+ dwTemp=0xFFFF0002UL;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, FLOW, dwTemp));
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, AFC_CFG,&dwValue));
+ dwValue=dwValue&(~0x0000000FUL);
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, AFC_CFG, dwValue));
+
+ } else {
+ //Disable PAUSE receive and transmit
+ dwTemp=0x0UL;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, FLOW, dwTemp));
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, AFC_CFG,&dwValue));
+ dwValue=dwValue&(~0x0000000FUL);
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, AFC_CFG, dwValue));
+
+ };break;
+ case LINK_SPEED_10HD:
+ case LINK_SPEED_100HD:
+
+ dwTemp=0x0UL;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, FLOW, dwTemp));
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, AFC_CFG,&dwValue));
+ dwValue=dwValue|0x0000000FUL;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, AFC_CFG, dwValue));
+
+ break;
+ default:break;
+ }
+ SMSC_TRACE(DBG_LINK_CHANGE,"LAN9500: %s,%s,%s,%s,%s,%s",
+ (localLink&PHY_ANEG_ADV_ASYMP_)?"ASYMP":" ",
+ (localLink&PHY_ANEG_ADV_SYMP_)?"SYMP ":" ",
+ (localLink&PHY_ANEG_ADV_100F_)?"100FD":" ",
+ (localLink&PHY_ANEG_ADV_100H_)?"100HD":" ",
+ (localLink&PHY_ANEG_ADV_10F_)?"10FD ":" ",
+ (localLink&PHY_ANEG_ADV_10H_)?"10HD ":" ");
+
+ SMSC_TRACE(DBG_LINK_CHANGE,"Partner: %s,%s,%s,%s,%s,%s",
+ (linkPartner&PHY_ANEG_LPA_ASYMP_)?"ASYMP":" ",
+ (linkPartner&PHY_ANEG_LPA_SYMP_)?"SYMP ":" ",
+ (linkPartner&PHY_ANEG_LPA_100FDX_)?"100FD":" ",
+ (linkPartner&PHY_ANEG_LPA_100HDX_)?"100HD":" ",
+ (linkPartner&PHY_ANEG_LPA_10FDX_)?"10FD ":" ",
+ (linkPartner&PHY_ANEG_LPA_10HDX_)?"10HD ":" ");
+ } else {
+ switch(adapterData->dwLinkSpeed) {
+ case LINK_SPEED_10HD:
+ case LINK_SPEED_100HD:
+
+ dwTemp=0x0UL;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, FLOW, dwTemp));
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, AFC_CFG,&dwValue));
+ dwValue=dwValue|0x0000000FUL;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, AFC_CFG, dwValue));
+ break;
+ default:
+
+ dwTemp=0x0UL;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, FLOW, dwTemp));
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, AFC_CFG,&dwValue));
+ dwValue=dwValue&(~0x0000000FUL);
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, AFC_CFG, dwValue));
+ break;
+ }
+ }
+ netif_carrier_on(dev->net);
+ Tx_WakeQueue(dev,0x01);
+ SetGpo(dev, adapterData->LinkLedOnGpio, !adapterData->LinkLedOnGpioPolarity);
+
+ } else {
+ SMSC_TRACE(DBG_LINK_CHANGE,"Link is now DOWN");
+ Tx_StopQueue(dev,0x01);
+ netif_carrier_off(dev->net);
+ SetGpo(dev, adapterData->LinkLedOnGpio, adapterData->LinkLedOnGpioPolarity);
+
+ dwTemp=0x0UL;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, FLOW, dwTemp));
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, AFC_CFG,&dwValue));
+ dwValue=dwValue& (~0x0000000FUL);
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, AFC_CFG, dwValue));
+ }
+ }
+ SMSC_TRACE(DBG_LINK,"<----------out of Phy_UpdateLinkMode");
+
+ ret = 0;
+DONE:
+ return ret;
+}
+
+static int Phy_CheckLink(void * ptr)
+{
+ struct usbnet *dev = ptr;
+ SMSC9500_RX_STATS rx_stats;
+ u32 dwValue, droppedFrame = 0;
+ int ret = SMSC9500_FAIL;
+
+ BUG_ON(!dev);
+ SMSC_TRACE(DBG_LINK,"-------->in Phy_CheckLink");
+
+ if(Phy_UpdateLinkMode(dev) < 0)return ret;
+
+ if(dev->suspendFlag & AUTOSUSPEND_DETACH){
+ if(!netif_carrier_ok(dev->net)){//Link is down, detach device
+ //Set wakeup event
+ SetLinkDownWakeupEvents(dev, WAKEPHY_ENERGY);
+
+ //Enable smart detach
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, HW_CFG,&dwValue));
+ dwValue |= HW_CFG_SMDET_EN;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, HW_CFG, dwValue));
+ dev->suspendFlag &= ~AUTOSUSPEND_DETACH;
+ ret = SMSC9500_SUCCESS;
+ goto DONE;
+ }
+ }
+
+ if(smsc9500_get_stats(dev, (void*)&rx_stats) > 0){
+ le32_to_cpus((u32*)&rx_stats.RxFifoDroppedFrames);
+ rx_stats.RxFifoDroppedFrames &= 0xFFFFF; //This counter has 20 bits.
+ if(dev->chipDependFeatures[FEATURE_NEWSTATIS_CNT]){//Statistics counters are rollover ones in LAN9500A
+ if(rx_stats.RxFifoDroppedFrames >= dev->preRxFifoDroppedFrame){
+ droppedFrame = rx_stats.RxFifoDroppedFrames - dev->preRxFifoDroppedFrame;
+ }else{//Rollover
+ droppedFrame = 0x100000 - dev->preRxFifoDroppedFrame + rx_stats.RxFifoDroppedFrames;
+ }
+ dev->preRxFifoDroppedFrame = rx_stats.RxFifoDroppedFrames;
+ }else{
+ droppedFrame = rx_stats.RxFifoDroppedFrames;
+ }
+ dev->stats.rx_dropped += droppedFrame;
+ }
+
+ if( (!(dev->StopLinkPolling)) && (!timer_pending(&dev->LinkPollingTimer))) {
+ dev->LinkPollingTimer.expires=jiffies+HZ;
+ add_timer(&(dev->LinkPollingTimer));
+ }
+ SMSC_TRACE(DBG_LINK,"<---------out of Phy_CheckLink");
+ ret = SMSC9500_SUCCESS;
+DONE:
+ return ret;
+}
+
+
+static int phy_SetLink(struct usbnet *dev, u32 dwLinkRequest)
+{
+ u32 dwValue;
+ u16 wTemp=0;
+ int ret = SMSC9500_FAIL;
+
+ SMSC_TRACE(DBG_LINK,"--------->in phy_SetLink");
+
+ if(dwLinkRequest&LINK_AUTO_NEGOTIATE) {
+
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev, PHY_ANEG_ADV,&dwValue));
+ wTemp=LOWORD(dwValue);
+ wTemp&=~PHY_ANEG_ADV_PAUSE_;
+ if(dwLinkRequest&LINK_ASYMMETRIC_PAUSE) {
+ wTemp|=PHY_ANEG_ADV_ASYMP_;
+ }
+ if(dwLinkRequest&LINK_SYMMETRIC_PAUSE) {
+ wTemp|=PHY_ANEG_ADV_SYMP_;
+ }
+ wTemp&=~PHY_ANEG_ADV_SPEED_;
+ if(dwLinkRequest&LINK_SPEED_10HD) {
+ wTemp|=PHY_ANEG_ADV_10H_;
+ }
+ if(dwLinkRequest&LINK_SPEED_10FD) {
+ wTemp|=PHY_ANEG_ADV_10F_;
+ }
+
+ if((dwLinkRequest&LINK_SPEED_100HD) && (dev->udev->speed == USB_SPEED_HIGH)) {
+ wTemp|=PHY_ANEG_ADV_100H_;
+ }
+ if((dwLinkRequest&LINK_SPEED_100FD) && (dev->udev->speed == USB_SPEED_HIGH)) {
+ wTemp|=PHY_ANEG_ADV_100F_;
+ }
+
+ dwValue=(u32)(wTemp) &0x0000FFFFUL;
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev,PHY_ANEG_ADV, dwValue));
+
+ // begin to establish link
+ wTemp=PHY_BCR_AUTO_NEG_ENABLE_|PHY_BCR_RESTART_AUTO_NEG_;
+ dwValue=(u32)(wTemp) &0x0000FFFFUL;
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev,PHY_BCR, dwValue));
+ } else {
+
+ if(dwLinkRequest&(LINK_SPEED_100FD)) {
+ dwLinkRequest=LINK_SPEED_100FD;
+ } else if(dwLinkRequest&(LINK_SPEED_100HD)) {
+ dwLinkRequest=LINK_SPEED_100HD;
+ } else if(dwLinkRequest&(LINK_SPEED_10FD)) {
+ dwLinkRequest=LINK_SPEED_10FD;
+ } else if(dwLinkRequest&(LINK_SPEED_10HD)) {
+ dwLinkRequest=LINK_SPEED_10HD;
+ }
+ if(dwLinkRequest&(LINK_SPEED_10FD|LINK_SPEED_100FD)) {
+ wTemp|=PHY_BCR_DUPLEX_MODE_;
+ }
+ if(dwLinkRequest&(LINK_SPEED_100HD|LINK_SPEED_100FD)) {
+ wTemp|=PHY_BCR_SPEED_SELECT_;
+ }
+ dwValue=(u32)(wTemp) &0x0000FFFFUL;
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev,PHY_BCR, dwValue));
+ }
+ SMSC_TRACE(DBG_LINK,"<---------out of phy_SetLink");
+ ret = 0;
+DONE:
+ return ret;
+}
+
+static int Phy_SetAutoMdix(
+ struct usbnet *dev,
+ u16 wAutoMdix
+ )
+{
+ u32 SpecialCtrlSts=0U;
+ int ret = SMSC9500_FAIL;
+
+ if (wAutoMdix > 2)
+ {
+ SMSC_WARNING("LAN9500 Auto MDIX feature controlled by hardware strap\n");
+ }
+ else
+ {
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev, PHY_SPECIAL_CTRL_STS, &SpecialCtrlSts));
+
+ SpecialCtrlSts = (((wAutoMdix+4) << 13) | (SpecialCtrlSts&0x1FFF));
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev, PHY_SPECIAL_CTRL_STS, SpecialCtrlSts));
+
+ if (wAutoMdix & AMDIX_ENABLE)
+ {
+ SMSC_WARNING("LAN9500 Auto MDIX hardware strap was overiden by driver. AutoMdix is enabled");
+ }
+ else if (wAutoMdix & AMDIX_DISABLE_CROSSOVER)
+ {
+ SMSC_WARNING("LAN9500 Auto MDIX hardware strap was overiden by driver. AutoMdix is disabled, use crossover cable.");
+ }
+ else
+ {
+ SMSC_WARNING("LAN9500 Auto MDIX hardware strap was overiden by driver. AutoMdix is disabled, use straight cable.");
+ }
+ }
+
+ ret = 0;
+DONE:
+ return ret;
+}
+
+static BOOLEAN Phy_Initialize(
+ struct usbnet *dev,
+ u32 dwPhyAddr,
+ u32 dwLinkRequest)
+{
+ BOOLEAN result=FALSE, bConfigureAutoMdix = FALSE;
+ u32 dwTemp=0,dwValue, address;
+ u32 dwLoopCount=0;
+ u32 phy_id_1, phy_id_2;
+
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+ SMSC_TRACE(DBG_INIT,"-->Phy_Initialize");
+ SMSC_ASSERT(dwLinkRequest<=0x7FUL);
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, HW_CFG, &dwValue));
+
+ if(dwValue & HW_CFG_PSEL_){
+ SMSC_TRACE(DBG_INIT,"using external PHY ");
+
+ if(dwPhyAddr <= 31){
+ // Using external PHY board on MII connector
+ // First isolate all PHYs we can find...
+
+ for (address=0;address<=31;address++)
+ {
+ adapterData->dwPhyAddress = address;
+ dwValue=(u32)PHY_BCR_ISOLATE;
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev, PHY_BCR, dwValue));
+ }
+
+ // Now put the PHY at the registry parsed address out of isolation.
+ adapterData->dwPhyAddress = dwPhyAddr;
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev, PHY_BCR, 0x0UL));
+ }else{//Auto detect PHY
+
+ phy_id_1 = 0xFFFFU;
+ phy_id_2 = 0xFFFFU;
+ for (address=0; address<=31; address++)
+ {
+ adapterData->dwPhyAddress = address;
+
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev, PHY_ID_1, &phy_id_1));
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev, PHY_ID_2, &phy_id_2));
+ if((phy_id_1 != 0x7FFFU) && (phy_id_1 != 0xFFFFU) && (phy_id_1 != 0x0000U)){
+ SMSC_TRACE(DBG_INIT,"Deteced Phy at address = 0x%02X", address);
+ break;
+ }
+ }
+ }
+ SMSC_TRACE(DBG_INIT,"using external PHY ");
+
+ } else {
+//USE_INTERNAL_PHY
+ SMSC_TRACE(DBG_INIT,"using internal PHY ");
+ adapterData->dwPhyAddress=1;
+
+ bConfigureAutoMdix = TRUE;
+ }
+
+ {
+ u32 dwPhyBcr;
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_ID_2,&dwTemp));
+ adapterData->bPhyRev=((BYTE)(dwTemp&(0x0FUL)));
+ adapterData->bPhyModel=((BYTE)((dwTemp>>4)&(0x3FUL)));
+ adapterData->dwPhyId=((dwTemp&(0xFC00UL))<<8);
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_ID_1,&dwTemp));
+ adapterData->dwPhyId|=((dwTemp&(0x0000FFFFUL))<<2);
+
+
+ SMSC_TRACE(DBG_INIT,"dwPhyId==0x%08X,bPhyModel==0x%02X,bPhyRev==0x%02X",
+ adapterData->dwPhyId,
+ adapterData->bPhyModel,
+ adapterData->bPhyRev);
+
+ adapterData->dwLinkSpeed = LINK_INIT;
+ adapterData->dwLinkSettings=LINK_INIT;
+ //reset the PHY
+ dwPhyBcr=(u32)PHY_BCR_RESET_ ;
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev,PHY_BCR, dwPhyBcr));
+
+ dwLoopCount = 20;
+ do {
+
+ mdelay(100);
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_BCR,&dwPhyBcr));
+
+ dwLoopCount--;
+ } while((dwLoopCount>0) && ((u16)dwPhyBcr&PHY_BCR_RESET_));
+
+
+ if((u16)dwPhyBcr&PHY_BCR_RESET_) {
+ SMSC_WARNING("PHY reset failed to complete.");
+ goto DONE;
+ }
+ else
+ SMSC_TRACE(DBG_INIT,"PHY reset!!!");
+ }
+
+ if (bConfigureAutoMdix)
+ {
+ Phy_SetAutoMdix(dev, (u16)auto_mdix);
+ }
+ phy_SetLink(dev,dwLinkRequest);
+
+ result=TRUE;
+DONE:
+ SMSC_TRACE(DBG_INIT,"<--Phy_Initialize, result=%s\n",result?"TRUE":"FALSE");
+ return result;
+}
+
+
+
+static int smsc9500_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+ cmd->supported=
+ SUPPORTED_10baseT_Half |
+ SUPPORTED_10baseT_Full |
+ SUPPORTED_100baseT_Half |
+ SUPPORTED_100baseT_Full |
+ SUPPORTED_Autoneg |
+ SUPPORTED_MII;
+ cmd->advertising=ADVERTISED_MII;
+
+ if(adapterData->dwLinkSettings & LINK_SPEED_10HD)
+ cmd->advertising|=ADVERTISED_10baseT_Half;
+ if(adapterData->dwLinkSettings & LINK_SPEED_10FD)
+ cmd->advertising|=ADVERTISED_10baseT_Full;
+ if(adapterData->dwLinkSettings & LINK_SPEED_100HD)
+ cmd->advertising|=ADVERTISED_100baseT_Half;
+ if(adapterData->dwLinkSettings & LINK_SPEED_100FD)
+ cmd->advertising|=ADVERTISED_100baseT_Full;
+ if(adapterData->dwLinkSettings & LINK_AUTO_NEGOTIATE) {
+ cmd->advertising|=ADVERTISED_Autoneg;
+ cmd->autoneg=AUTONEG_ENABLE;
+ } else cmd->autoneg=AUTONEG_DISABLE;
+ if(adapterData->dwLinkSpeed & (LINK_SPEED_100HD|LINK_SPEED_100FD))
+ cmd->speed=SPEED_100;
+ else cmd->speed=SPEED_10;
+ if(adapterData->dwLinkSpeed & (LINK_SPEED_10FD|LINK_SPEED_100FD))
+ cmd->duplex=DUPLEX_FULL;
+ else cmd->duplex=DUPLEX_HALF;
+ cmd->port=PORT_MII;
+ cmd->phy_address=(u8)adapterData->dwPhyAddress;
+ cmd->transceiver=XCVR_INTERNAL;
+ cmd->maxtxpkt=0;
+ cmd->maxrxpkt=0;
+
+
+ return 0;
+
+}
+
+
+static int smsc9500_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+ struct usbnet *dev = netdev_priv(net);
+ int result=-EFAULT;
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+
+ u16 speed=0;
+ u8 duplex=0;
+ u8 autoneg=0;
+
+ if(adapterData->dwLinkSettings&LINK_AUTO_NEGOTIATE) {
+ autoneg=AUTONEG_ENABLE;
+ } else {
+ autoneg=AUTONEG_DISABLE;
+ }
+ if(adapterData->dwLinkSpeed&(LINK_SPEED_100HD|LINK_SPEED_100FD))
+ {
+ speed=SPEED_100;
+ } else {
+ speed=SPEED_10;
+ }
+ if(adapterData->dwLinkSpeed&(LINK_SPEED_10FD|LINK_SPEED_100FD))
+ {
+ duplex=DUPLEX_FULL;
+ } else {
+ duplex=DUPLEX_HALF;
+ }
+ if((cmd->speed!=100)&&(cmd->speed!=10)) {
+ result=-EOPNOTSUPP;
+ goto DONE;
+ }
+ if((cmd->duplex!=DUPLEX_FULL)&&(cmd->duplex!=DUPLEX_HALF)) {
+ result=-EOPNOTSUPP;
+ goto DONE;
+ }
+ if((cmd->autoneg!=AUTONEG_ENABLE)&&(cmd->autoneg!=AUTONEG_DISABLE)) {
+ result=-EOPNOTSUPP;
+ goto DONE;
+ }
+ if((cmd->autoneg!=autoneg)||
+ (cmd->speed!=speed)||
+ (cmd->duplex!=duplex))
+ {
+ if(cmd->autoneg==AUTONEG_ENABLE) {
+ u32 dwBcrValue;
+
+ dwBcrValue=PHY_BCR_AUTO_NEG_ENABLE_|PHY_BCR_RESTART_AUTO_NEG_;
+ if(smsc9500_write_phy(dev,PHY_BCR, dwBcrValue) < 0)goto DONE;
+
+ } else {
+ u32 dwBcrValue;
+ u16 wBcr;
+ if(smsc9500_read_phy(dev,PHY_BCR,&dwBcrValue) < 0)goto DONE;
+ wBcr=(u16)dwBcrValue;
+ if(cmd->speed==SPEED_100) {
+ wBcr|=PHY_BCR_SPEED_SELECT_;
+ } else {
+ wBcr&=(~PHY_BCR_SPEED_SELECT_);
+ }
+ if(cmd->duplex==DUPLEX_FULL) {
+ wBcr|=PHY_BCR_DUPLEX_MODE_;
+ } else {
+ wBcr&=(~PHY_BCR_DUPLEX_MODE_);
+ }
+ wBcr &= ~PHY_BCR_AUTO_NEG_ENABLE_;
+ if(smsc9500_write_phy(dev,PHY_BCR,wBcr) < 0)goto DONE;
+ }
+ }
+ result=0;
+
+DONE:
+ return result;
+}
+
+void smsc9500_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+ strcpy(info->driver,"Smsc9500");
+ memset(&info->version,0,sizeof(info->version));
+ sprintf(info->version,"%lX.%02lX.%02lX",
+ (DRIVER_VERSION>>16),(DRIVER_VERSION>>8)&0xFF,(DRIVER_VERSION&0xFFUL));
+ memset(&info->fw_version,0,sizeof(info->fw_version));
+ sprintf(info->fw_version,"%lu",(adapterData->dwIdRev)&0xFFFFUL);
+ memset(&info->bus_info,0,sizeof(info->bus_info));
+ memset(&info->reserved1,0,sizeof(info->reserved1));
+ memset(&info->reserved2,0,sizeof(info->reserved2));
+ info->n_stats=0;
+ info->testinfo_len=0;
+ info->eedump_len=0;
+ info->regdump_len=0;
+
+}
+
+static u32 smsc9500_get_link (struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+
+ if(adapterData->dwLinkSpeed!=LINK_OFF)
+ return 1;
+ else
+ return 0;
+
+}
+
+static u32 smsc9500_get_msglevel (struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ return dev->msg_enable;
+}
+
+
+static void smsc9500_set_msglevel (struct net_device *net, u32 level)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ dev->msg_enable = level;
+}
+
+
+static void
+smsc9500_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+ wolinfo->supported=(WAKE_PHY | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_MAGIC);
+ wolinfo->wolopts= adapterData->WolWakeupOpts;
+}
+
+static int
+smsc9500_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+ struct usbnet *dev = netdev_priv(net);
+ int result=-EFAULT;
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+ adapterData->WolWakeupOpts = wolinfo->wolopts;
+ result=0;
+
+ return result;
+}
+
+static int smsc9500_get_eeprom_len(struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+ return adapterData->eepromSize;
+}
+
+static int smsc9500_get_eeprom(struct net_device *netdev, struct ethtool_eeprom *ee, u8 *data)
+{
+ struct usbnet *dev=netdev_priv(netdev);
+ int offset = ee->offset;
+ int len = ee->len;
+ int result = 0;
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+ ee->magic = LAN9500_EEPROM_MAGIC;
+
+ if(len == 0){
+ return 0;
+ }
+
+ if(offset + len > adapterData->eepromSize){
+ SMSC_WARNING("EEPROM address is out of range");
+ result = -EINVAL;
+ }else{
+ if(smsc9500_read_eeprom(dev, offset, len, data) < 0){
+ result = -EFAULT;
+ }
+ }
+
+ return result;
+}
+
+static int smsc9500_set_eeprom(struct net_device *netdev, struct ethtool_eeprom *ee, u8 *data)
+{
+ struct usbnet *dev=netdev_priv(netdev);
+ int offset = ee->offset;
+ int len = ee->len;
+ int result = 0;
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+ if(len == 0){
+ return 0;
+ }
+
+ if(offset + len > adapterData->eepromSize){
+ SMSC_WARNING("EEPROM address is out of range");
+ result = -EINVAL;
+ return result;
+ }
+
+ if(ee->magic != LAN9500_EEPROM_MAGIC){
+ SMSC_WARNING("EEPROM: magic value mismatch, writing fail, magic = 0x%x", ee->magic);
+ result = -EFAULT;
+ return result;
+ }
+
+ if(smsc9500_write_eeprom(dev, offset, len, data) < 0){
+ result=-EFAULT;
+ }
+
+ return result;
+}
+
+static int smsc9500_eeprom_size(struct usbnet *dev)
+{
+#define CHECK_SIZE 4
+ u32 dwValue;
+ int size = 0;
+ int i;
+ char save[CHECK_SIZE+1];
+ char saveEach[CHECK_SIZE+1];
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, E2P_CMD, &dwValue));
+ if (!(dwValue & E2P_CMD_LOADED_))
+ {
+ size = 0;
+ goto DONE;
+ }
+
+ if(smsc9500_read_eeprom(dev, 0, CHECK_SIZE, save) < 0){//Save first 4 bytes
+ goto DONE;
+ }
+
+ for(i=128; i<=MAX_EEPROM_SIZE; i+=128){
+ if(smsc9500_read_eeprom(dev, i, CHECK_SIZE, saveEach) < 0){
+ goto DONE;
+ }
+ if(!strncmp(save, saveEach, CHECK_SIZE)){
+ size = i;
+ break;
+ }
+ }
+
+DONE:
+ return size;
+
+}
+
+/* We need to override some ethtool_ops so we require our
+ own structure so we don't interfere with other usbnet
+ devices that may be connected at the same time. */
+static struct ethtool_ops smsc9500_ethtool_ops = {
+ .get_drvinfo = smsc9500_get_drvinfo,
+ .get_link = smsc9500_get_link,
+ .get_msglevel = smsc9500_get_msglevel,
+ .set_msglevel = smsc9500_set_msglevel,
+ .get_wol = smsc9500_get_wol,
+ .set_wol = smsc9500_set_wol,
+ .get_settings = smsc9500_get_settings,
+ .set_settings = smsc9500_set_settings,
+ .get_eeprom_len = smsc9500_get_eeprom_len,
+ .get_eeprom = smsc9500_get_eeprom,
+ .set_eeprom = smsc9500_set_eeprom,
+};
+
+static int Smsc9500_do_ioctl(
+ struct net_device *netdev,
+ struct ifreq *ifr,
+ int cmd)
+{
+ int result=0;
+ struct usbnet *dev=netdev_priv(netdev);
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+ void __user *userAddr=NULL;
+
+ SMSC_TRACE(DBG_IOCTL,"---->Smsc9500_do_ioctl");
+ if(netdev==NULL) {
+ SMSC_WARNING("netdev==NULL");
+ result=-EFAULT;
+ goto DONE;
+ }
+
+ if(ifr==NULL) {
+ SMSC_WARNING("ifr==NULL");
+ result=-EFAULT;
+ goto DONE;
+ }
+ userAddr=ifr->ifr_data;
+
+ switch(cmd) {
+
+ case SIOCGMIIPHY:
+ case SIOCDEVPRIVATE:
+ SMSC_TRACE(DBG_IOCTL,"SIOCGMIIPHY");
+ if(adapterData->LanInitialized) {
+ struct mii_ioctl_data *miiData=
+ (struct mii_ioctl_data *)&(ifr->ifr_data);
+ miiData->phy_id=1;
+ };
+ break;
+
+ case SIOCGMIIREG:
+ case SIOCDEVPRIVATE+1:
+ SMSC_TRACE(DBG_IOCTL,"SIOCGMIIREG");
+ if(adapterData->LanInitialized) {
+ struct mii_ioctl_data *miiData=
+ (struct mii_ioctl_data *)&(ifr->ifr_data);
+ {
+ u32 dwValue;
+ if(smsc9500_read_phy(dev,miiData->reg_num,&dwValue) < 0){
+ result = -EFAULT;
+ }
+ miiData->val_out=(u16)dwValue;
+ }
+ };break;
+
+ case SIOCSMIIREG:
+ case SIOCDEVPRIVATE+2:
+ SMSC_TRACE(DBG_IOCTL,"SIOCSMIIREG");
+ if(adapterData->LanInitialized) {
+ struct mii_ioctl_data *miiData=
+ (struct mii_ioctl_data *)&(ifr->ifr_data);
+ {
+ u32 dwValue;
+ dwValue=miiData->val_in;
+ if(smsc9500_write_phy(dev,miiData->reg_num, dwValue) < 0){
+ result = -EFAULT;
+ }
+ }
+ };break;
+
+ case SMSC9500_IOCTL:
+ result=smsc9500_private_ioctl(adapterData, dev, (PSMSC9500_IOCTL_DATA)userAddr);
+ break;
+
+ default:
+ SMSC_WARNING("unknown cmd = 0x%08X",cmd);
+ result=-EOPNOTSUPP;
+ break;
+ }
+
+DONE:
+
+ SMSC_TRACE(DBG_IOCTL,"<--Smsc9500_do_ioctl");
+ return result;
+}
+
+static int smsc9500_private_ioctl(PADAPTER_DATA privateData, struct usbnet *dev, PSMSC9500_IOCTL_DATA ioctlData)
+{
+ BOOLEAN success=FALSE;
+ int i;
+ u32 dwBuf;
+ u32 offset;
+ if(ioctlData->dwSignature!=SMSC9500_APP_SIGNATURE) {
+ goto DONE;
+ }
+
+ switch(ioctlData->dwCommand) {
+ case COMMAND_GET_SIGNATURE:
+ success=TRUE;
+ break;
+ case COMMAND_GET_CONFIGURATION:
+ ioctlData->Data[0]=DRIVER_VERSION;
+ ioctlData->Data[1]=link_mode;
+ ioctlData->Data[2] = privateData->macAddrHi16;
+ ioctlData->Data[3] = privateData->MmacAddrLo32;
+ ioctlData->Data[4]=debug_mode;
+ ioctlData->Data[5]=privateData->dwIdRev;
+ ioctlData->Data[6]=privateData->dwFpgaRev;
+ ioctlData->Data[7] = 1;
+ ioctlData->Data[8]=privateData->dwPhyId;
+ ioctlData->Data[9]=privateData->bPhyModel;
+ ioctlData->Data[10]=privateData->bPhyRev;
+ ioctlData->Data[11]=privateData->dwLinkSpeed;
+ ioctlData->Data[12] = privateData->eepromSize / 128; //Unit is 128B
+ sprintf(ioctlData->Strng1,"%s, %s",__DATE__,__TIME__);
+
+ success=TRUE;
+ break;
+ case COMMAND_LAN_GET_REG:
+ offset = ioctlData->Data[0];
+
+ if((ioctlData->Data[0] <= LAN_REGISTER_RANGE) && ((ioctlData->Data[0]&0x3UL)==0))
+ {
+ if(smsc9500_read_reg(dev, offset, &dwBuf) >= 0){
+ ioctlData->Data[1] = dwBuf;
+ success=TRUE;
+ }
+ } else {
+ SMSC_WARNING("Reading LAN9500 Mem Map Failed");
+ goto MEM_MAP_ACCESS_FAILED;
+ }
+ break;
+ case COMMAND_LAN_SET_REG:
+ if((ioctlData->Data[0] <= LAN_REGISTER_RANGE) && ((ioctlData->Data[0]&0x3UL)==0))
+ {
+ offset = ioctlData->Data[0];
+ dwBuf = ioctlData->Data[1];
+ if(smsc9500_write_reg(dev, offset, dwBuf) >= 0){
+ success=TRUE;
+ }
+ } else {
+ SMSC_WARNING("Writing LAN9500 Mem Map Failed");
+MEM_MAP_ACCESS_FAILED:
+ SMSC_WARNING(" Invalid offset == 0x%08lX",ioctlData->Data[0]);
+ if(ioctlData->Data[0] > LAN_REGISTER_RANGE) {
+ SMSC_WARNING(" Out of range");
+ }
+ if(ioctlData->Data[0]&0x3UL) {
+ SMSC_WARNING(" Not u32 aligned");
+ }
+ }
+ break;
+ case COMMAND_MAC_GET_REG:
+ if((ioctlData->Data[0] >= MAC_REGISTER_RANGE_MIN)&& (ioctlData->Data[0] <= MAC_REGISTER_RANGE_MAX) && ((ioctlData->Data[0]&0x3UL)==0)) {
+ offset = ioctlData->Data[0];
+ if(smsc9500_read_reg(dev, offset, &dwBuf) >= 0){
+ ioctlData->Data[1] = dwBuf;
+ success=TRUE;
+ }
+ } else {
+ SMSC_WARNING("Reading Mac Register Failed");
+ goto MAC_ACCESS_FAILURE;
+ }
+ break;
+ case COMMAND_MAC_SET_REG:
+ if((ioctlData->Data[0] >= MAC_REGISTER_RANGE_MIN)&& (ioctlData->Data[0] <= MAC_REGISTER_RANGE_MAX) && ((ioctlData->Data[0]&0x3UL)==0)) {
+ offset = ioctlData->Data[0];
+ dwBuf = ioctlData->Data[1];
+ if(smsc9500_write_reg(dev, offset, dwBuf) >= 0){
+ ioctlData->Data[1] = dwBuf;
+ success=TRUE;
+ }
+ } else {
+ SMSC_WARNING("Writing Mac Register Failed");
+MAC_ACCESS_FAILURE:
+ if(!(privateData->LanInitialized)) {
+
+ SMSC_WARNING(" LAN Not Initialized,");
+ SMSC_WARNING(" Use ifconfig to bring interface UP");
+ }
+ if(!((ioctlData->Data[0] >= MAC_REGISTER_RANGE_MIN)&& (ioctlData->Data[0] <= MAC_REGISTER_RANGE_MAX))) {
+ SMSC_WARNING(" Invalid index == 0x%08lX",ioctlData->Data[0]);
+ }
+ }
+ break;
+ case COMMAND_PHY_GET_REG:
+ if((ioctlData->Data[0]<32)&&(privateData->LanInitialized)) {
+ offset = ioctlData->Data[0];
+ if(smsc9500_read_phy(dev,offset, &dwBuf) >= 0){
+ success=TRUE;
+ ioctlData->Data[1] = dwBuf;
+ }
+ } else {
+ SMSC_WARNING("Reading Phy Register Failed");
+ goto PHY_ACCESS_FAILURE;
+ }
+ break;
+ case COMMAND_PHY_SET_REG:
+ if((ioctlData->Data[0]<32)&&(privateData->LanInitialized)) {
+ offset = ioctlData->Data[0];
+ dwBuf = ioctlData->Data[1];
+ if(smsc9500_write_phy(dev,offset, dwBuf) >= 0){
+ success=TRUE;
+ }
+ } else {
+ SMSC_WARNING("Writing Phy Register Failed");
+PHY_ACCESS_FAILURE:
+ if(!(privateData->LanInitialized)) {
+ SMSC_WARNING(" Lan Not Initialized,");
+ SMSC_WARNING(" Use ifconfig to bring interface UP");
+ }
+ if(!(ioctlData->Data[0]<32)) {
+ SMSC_WARNING(" Invalid index == 0x%ld",ioctlData->Data[0]);
+ }
+ }
+ break;
+ case COMMAND_GET_EEPROM:
+ {
+ BYTE cBuf;
+ offset = ioctlData->Data[0];
+ if(offset < privateData->eepromSize) {
+ if(smsc9500_read_eeprom(dev,offset, 1, &cBuf) >= 0){
+ success=TRUE;
+ ioctlData->Data[1] = cBuf;
+ }
+ } else {
+ SMSC_WARNING("Reading EEPROM Failed");
+ goto PHY_ACCESS_FAILURE;
+ }
+ }
+ break;
+ case COMMAND_SET_EEPROM:
+ {
+ BYTE cBuf;
+ offset = ioctlData->Data[0];
+ cBuf = (BYTE)ioctlData->Data[1];
+ if(offset < privateData->eepromSize) {
+ if(smsc9500_write_eeprom(dev, offset, 1, &cBuf) >= 0){
+ success=TRUE;
+ }
+ } else {
+ SMSC_WARNING("Writing EEPROM Failed");
+ if(!(offset < privateData->eepromSize)) {
+ SMSC_WARNING(" Invalid eeprom offset == 0x%d",offset);
+ }
+ }
+ }
+ break;
+
+ case COMMAND_DUMP_LAN_REGS:
+
+ success=TRUE;
+ for(i=0; i<MAX_LAN_REG_NUM; i++){
+ if(smsc9500_read_reg(dev, LanRegMap[i], &dwBuf) < 0){
+ SMSC_WARNING("Failed to read LAN reg 0x%x", (unsigned int)LanRegMap[i]);
+ success = FALSE;
+ }else{
+ ioctlData->Data[i] = dwBuf;
+ }
+ }
+ break;
+ case COMMAND_DUMP_MAC_REGS:
+ if(privateData->LanInitialized) {
+ success=TRUE;
+ for(i=0; i<MAX_MAC_REG_NUM; i++){
+ if(smsc9500_read_reg(dev, MacRegMap[i], &dwBuf) < 0){
+ SMSC_WARNING("Failed to read MAC reg 0x%x", (unsigned int)MacRegMap[i]);
+ success = FALSE;
+ }else{
+ ioctlData->Data[i] = dwBuf;
+ }
+ }
+ } else {
+ SMSC_WARNING("Mac Not Initialized,");
+ SMSC_WARNING(" Use ifconfig to bring interface UP");
+ }
+ break;
+ case COMMAND_DUMP_PHY_REGS:
+ if(privateData->LanInitialized) {
+ success=TRUE;
+ for(i=0; i<MAX_PHY_REG_NUM; i++){
+ if(smsc9500_read_phy(dev, PhyRegMap[i], &dwBuf) < 0){
+ SMSC_WARNING("Failed to read PHY reg 0x%x", (unsigned int)PhyRegMap[i]);
+ success = FALSE;
+ }else{
+ ioctlData->Data[i] = dwBuf;
+ }
+ }
+ } else {
+ SMSC_WARNING("Phy Not Initialized,");
+ SMSC_WARNING(" Use ifconfig to bring interface UP");
+ }
+ break;
+ case COMMAND_DUMP_EEPROM:
+ {
+ success=TRUE;
+
+ if(smsc9500_read_eeprom(dev, 0, privateData->eepromSize, (BYTE*)ioctlData->Data) < 0){
+ success=FALSE;
+ }
+ };break;
+ case COMMAND_GET_MAC_ADDRESS:
+
+ if(privateData->LanInitialized) {
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, ADDRH, &dwBuf));
+ ioctlData->Data[0] = dwBuf;
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, ADDRL, &dwBuf));
+ ioctlData->Data[1] = dwBuf;
+ success=TRUE;
+ } else {
+ SMSC_WARNING("Lan Not Initialized,");
+ SMSC_WARNING(" Use ifconfig to bring interface UP");
+ }
+ break;
+
+ case COMMAND_SET_MAC_ADDRESS:
+ if(privateData->LanInitialized)
+ {
+ u32 dwLow32=ioctlData->Data[1];
+ u32 dwHigh16=ioctlData->Data[0];
+
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, ADDRH, dwHigh16));
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, ADDRL, dwLow32));
+
+ dev->net->dev_addr[0]=LOBYTE(LOWORD(dwLow32));
+ dev->net->dev_addr[1]=HIBYTE(LOWORD(dwLow32));
+ dev->net->dev_addr[2]=LOBYTE(HIWORD(dwLow32));
+ dev->net->dev_addr[3]=HIBYTE(HIWORD(dwLow32));
+ dev->net->dev_addr[4]=LOBYTE(LOWORD(dwHigh16));
+ dev->net->dev_addr[5]=HIBYTE(LOWORD(dwHigh16));
+
+ success=TRUE;
+ } else {
+ SMSC_WARNING("Lan Not Initialized,");
+ SMSC_WARNING(" Use ifconfig to bring interface UP");
+ };break;
+
+ case COMMAND_LOAD_MAC_ADDRESS:
+ if(privateData->LanInitialized) {
+ if(smsc9500_read_eeprom(dev, EEPROM_MAC_OFFSET, 6, dev->net->dev_addr) == 0){
+ dwBuf = dev->net->dev_addr[0] | dev->net->dev_addr[1] << 8 | dev->net->dev_addr[2] << 16 | dev->net->dev_addr[3] << 24;
+ ioctlData->Data[1] = dwBuf;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, ADDRL, ioctlData->Data[1]));
+ dwBuf = dev->net->dev_addr[4] | dev->net->dev_addr[5] << 8;
+ ioctlData->Data[0] = dwBuf;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, ADDRH, ioctlData->Data[0]));
+
+ success=TRUE;
+ } else {
+ SMSC_WARNING("Failed to Load Mac Address");
+ }
+ } else {
+ SMSC_WARNING("Lan Not Initialized,");
+ SMSC_WARNING(" Use ifconfig to bring interface UP");
+ };break;
+ case COMMAND_SAVE_MAC_ADDRESS:
+ if(privateData->LanInitialized) {
+ u32 dwLow32 = ioctlData->Data[1];
+ u32 dwHigh16 = ioctlData->Data[0];
+
+ cpu_to_le32s((u32*)&dwLow32);
+ cpu_to_le32s((u32*)&dwHigh16);
+
+ if((smsc9500_write_eeprom(dev, EEPROM_MAC_OFFSET, 4, (BYTE*)&dwLow32) == 0) &&
+ (smsc9500_write_eeprom(dev, EEPROM_MAC_OFFSET+4, 2, (BYTE*)&dwHigh16) == 0)){
+ success=TRUE;
+ }
+ } else {
+ SMSC_WARNING("Lan Not Initialized,");
+ SMSC_WARNING(" Use ifconfig to bring interface UP");
+ };break;
+
+ case COMMAND_SET_DEBUG_MODE:
+ debug_mode=ioctlData->Data[0];
+ if(debug_mode&0x04UL) {
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, GPIO_CFG, 0x00670700UL));
+ success=TRUE;
+ } else {
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, GPIO_CFG, 0x70070000));
+ success=TRUE;
+ }
+ success=TRUE;
+ break;
+ case COMMAND_SET_LINK_MODE:
+ link_mode=(ioctlData->Data[0]&0x7FUL);
+ if(privateData->LanInitialized) {
+ phy_SetLink(dev,link_mode);
+ }
+ success=TRUE;
+ break;
+ case COMMAND_GET_LINK_MODE:
+ ioctlData->Data[0]=link_mode;
+ success=TRUE;
+ break;
+ case COMMAND_CHECK_LINK:
+ Phy_UpdateLinkMode(dev);
+ success=TRUE;
+ break;
+
+ case COMMAND_GET_ERRORS:
+
+ ioctlData->Data[0] = dev->extra_error_cnts.tx_epipe;
+ ioctlData->Data[1] = dev->extra_error_cnts.tx_eproto;
+ ioctlData->Data[2] = dev->extra_error_cnts.tx_etimeout;
+ ioctlData->Data[3] = dev->extra_error_cnts.tx_eilseq;
+
+ ioctlData->Data[4] = dev->extra_error_cnts.rx_epipe;
+ ioctlData->Data[5] = dev->extra_error_cnts.rx_eproto;
+ ioctlData->Data[6] = dev->extra_error_cnts.rx_etimeout;
+ ioctlData->Data[7] = dev->extra_error_cnts.rx_eilseq;
+ ioctlData->Data[8] = dev->extra_error_cnts.rx_eoverflow;
+
+ success = TRUE;
+ break;
+ case COMMAND_READ_BYTE:
+ ioctlData->Data[1]=(*((volatile BYTE *)(ioctlData->Data[0])));
+ success=TRUE;
+ break;
+ case COMMAND_READ_WORD:
+ ioctlData->Data[1]=(*((volatile u16 *)(ioctlData->Data[0])));
+ success=TRUE;
+ break;
+ case COMMAND_READ_DWORD:
+ ioctlData->Data[1]=(*((volatile u32 *)(ioctlData->Data[0])));
+ success=TRUE;
+ break;
+ case COMMAND_WRITE_BYTE:
+ (*((volatile BYTE *)(ioctlData->Data[0])))=
+ ((BYTE)(ioctlData->Data[1]));
+ success=TRUE;
+ break;
+ case COMMAND_WRITE_WORD:
+ (*((volatile u16 *)(ioctlData->Data[0])))=
+ ((u16)(ioctlData->Data[1]));
+ success=TRUE;
+ break;
+ case COMMAND_WRITE_DWORD:
+ (*((volatile u32 *)(ioctlData->Data[0])))=
+ ((u32)(ioctlData->Data[1]));
+ success=TRUE;
+ break;
+ case COMMAND_SET_AMDIX_STS:
+ auto_mdix=(ioctlData->Data[0]);
+ if(privateData->LanInitialized) {
+ Phy_SetAutoMdix(dev, (u16)auto_mdix);
+ }
+ success=TRUE;
+ break;
+ case COMMAND_GET_AMDIX_STS:
+ ioctlData->Data[0]=auto_mdix;
+ success=TRUE;
+ break;
+
+ default:break;//make lint happy
+ }
+
+DONE:
+ if((success)&&(ioctlData!=NULL)) {
+ ioctlData->dwSignature=SMSC9500_DRIVER_SIGNATURE;
+ return SMSC9500_SUCCESS;
+ }
+ return SMSC9500_FAIL;
+
+}
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29))
+static const struct net_device_ops smsc95xx_netdev_ops =
+{
+ .ndo_open = smscusbnet_open,
+ .ndo_stop = smscusbnet_stop,
+ .ndo_start_xmit = smscusbnet_start_xmit,
+ .ndo_tx_timeout = smscusbnet_tx_timeout,
+ .ndo_change_mtu = smscusbnet_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_do_ioctl = Smsc9500_do_ioctl,
+ .ndo_set_multicast_list = smsc9500_set_multicast,
+ .ndo_get_stats = smscusbnet_get_stats,
+};
+#endif //linux 2.6.29
+
+static int smsc9500_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+ int ret=0;
+ PADAPTER_DATA adapterData=NULL;
+ u32 dwBuf;
+ char version[15];
+
+ SMSC_TRACE(DBG_INIT,"---------->in smsc9500_bind\n");
+
+ //Init system control and status regsiter map
+ LanRegMap[LAN_REG_ID_REV] = ID_REV;
+ LanRegMap[LAN_REG_FPGA_REV] = FPGA_REV;
+ LanRegMap[LAN_REG_INT_STS] = INT_STS;
+ LanRegMap[LAN_REG_RX_CFG] = RX_CFG;
+ LanRegMap[LAN_REG_TX_CFG] = TX_CFG;
+ LanRegMap[LAN_REG_HW_CFG] = HW_CFG;
+ LanRegMap[LAN_REG_RX_FIFO_INF] = RX_FIFO_INF;
+ LanRegMap[LAN_REG_TX_FIFO_INF] = TX_FIFO_INF;
+ LanRegMap[LAN_REG_PMT_CTRL] = PM_CTRL;
+ LanRegMap[LAN_REG_LED_GPIO_CFG] = LED_GPIO_CFG;
+ LanRegMap[LAN_REG_GPIO_CFG] = GPIO_CFG;
+ LanRegMap[LAN_REG_AFC_CFG] = AFC_CFG;
+ LanRegMap[LAN_REG_E2P_CMD] = E2P_CMD;
+ LanRegMap[LAN_REG_E2P_DATA] = E2P_DATA;
+ LanRegMap[LAN_REG_BURST_CAP] = BURST_CAP;
+ LanRegMap[LAN_REG_STRAP_DBG] = STRAP_DBG;
+ LanRegMap[LAN_REG_DP_SEL] = DP_SEL;
+ LanRegMap[LAN_REG_DP_CMD] = DP_CMD;
+ LanRegMap[LAN_REG_DP_ADDR] = DP_ADDR;
+ LanRegMap[LAN_REG_DP_DATA0] = DP_DATA0;
+ LanRegMap[LAN_REG_DP_DATA1] = DP_DATA1;
+ LanRegMap[LAN_REG_GPIO_WAKE] = GPIO_WAKE;
+ LanRegMap[LAN_REG_INT_EP_CTL] = INT_EP_CTL;
+ LanRegMap[LAN_REG_BULK_IN_DLY] = BULK_IN_DLY;
+
+ //Init MAC register map
+ MacRegMap[MAC_REG_MAC_CR] = MAC_CR;
+ MacRegMap[MAC_REG_ADDRH] = ADDRH;
+ MacRegMap[MAC_REG_ADDRL] = ADDRL;
+ MacRegMap[MAC_REG_HASHH] = HASHH;
+ MacRegMap[MAC_REG_HASHL] = HASHL;
+ MacRegMap[MAC_REG_MII_ADDR] = MII_ADDR;
+ MacRegMap[MAC_REG_MII_DATA] = MII_DATA;
+ MacRegMap[MAC_REG_FLOW] = FLOW;
+ MacRegMap[MAC_REG_VLAN1] = VLAN1;
+ MacRegMap[MAC_REG_VLAN2] = VLAN2;
+ MacRegMap[MAC_REG_WUFF] = WUFF;
+ MacRegMap[MAC_REG_WUCSR] = WUCSR;
+ MacRegMap[MAC_REG_COE_CR] = COE_CR;
+
+ //Init PHY map
+ PhyRegMap[PHY_REG_BCR] = PHY_BCR;
+ PhyRegMap[PHY_REG_BSR] = PHY_BSR;
+ PhyRegMap[PHY_REG_ID1] = PHY_ID_1;
+ PhyRegMap[PHY_REG_ID2] = PHY_ID_2;
+ PhyRegMap[PHY_REG_ANEG_ADV] = PHY_ANEG_ADV;
+ PhyRegMap[PHY_REG_ANEG_LPA] = PHY_ANEG_LPA;
+ PhyRegMap[PHY_REG_ANEG_ER] = PHY_ANEG_REG;
+ PhyRegMap[PHY_REG_SILICON_REV] = PHY_SILICON_REV;
+ PhyRegMap[PHY_REG_MODE_CTRL_STS] = PHY_MODE_CTRL_STS;
+ PhyRegMap[PHY_REG_SPECIAL_MODES] = PHY_SPECIAL_MODES;
+ PhyRegMap[PHY_REG_TSTCNTL] = PHY_TSTCNTL;
+ PhyRegMap[PHY_REG_TSTREAD1] = PHY_TSTREAD1;
+ PhyRegMap[PHY_REG_TSTREAD2] = PHY_TSTREAD2;
+ PhyRegMap[PHY_REG_TSTWRITE] = PHY_TSTWRITE;
+ PhyRegMap[PHY_REG_SPECIAL_CTRL_STS] = PHY_SPECIAL_CTRL_STS;
+ PhyRegMap[PHY_REG_SITC] = PHY_SITC;
+ PhyRegMap[PHY_REG_INT_SRC] = PHY_INT_SRC;
+ PhyRegMap[PHY_REG_INT_MASK] = PHY_INT_MASK;
+ PhyRegMap[PHY_REG_SPECIAL] = PHY_SPECIAL;
+
+ sprintf(version,"%lX.%02lX.%02lX",
+ (DRIVER_VERSION>>16),(DRIVER_VERSION>>8)&0xFF,(DRIVER_VERSION&0xFFUL));
+ SMSC_TRACE(DBG_INIT,"Driver smsc9500.ko verison %s, built on %s, %s",version, __TIME__, __DATE__);
+
+ ret=smscusbnet_get_endpoints(dev,intf);
+ if (ret<0)
+ goto out1;
+
+ dev->data[0]=(unsigned long) kmalloc(sizeof(ADAPTER_DATA),GFP_KERNEL);
+
+ if((PADAPTER_DATA)dev->data[0]==NULL) {
+ SMSC_WARNING("Unable to allocate ADAPTER_DATA");
+ ret=-ENOMEM;
+ goto out1;
+ }
+ memset((PADAPTER_DATA)dev->data[0],0,sizeof(ADAPTER_DATA));
+ adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+ init_MUTEX(&adapterData->phy_mutex);
+ init_MUTEX(&adapterData->eeprom_mutex);
+ init_MUTEX(&adapterData->internal_ram_mutex);
+ init_MUTEX(&adapterData->RxFilterLock);
+
+ if ((ret = smsc9500_read_reg(dev,HW_CFG,&dwBuf)< 0)) {
+ SMSC_WARNING("Failed to read HW_CFG: %d", ret);
+ return ret;
+ }
+ if(dwBuf & HW_CFG_SMDET_STS){
+ SMSC_TRACE(DBG_INIT,"Come back from smart detach");
+ }
+
+ adapterData->macAddrHi16 = mac_addr_hi16;
+ adapterData->MmacAddrLo32 = mac_addr_lo32;
+
+ adapterData->eepromSize = MAX_EEPROM_SIZE + 128; //Set a initial value
+ adapterData->eepromSize = smsc9500_eeprom_size(dev);
+ SMSC_TRACE(DBG_INIT,"EEPROM size: %d bytes", adapterData->eepromSize);
+
+ //Init all registers
+ ret = smsc9500_reset(dev);
+ if(ret < 0)goto out1;
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29))
+ dev->net->do_ioctl = Smsc9500_do_ioctl;
+ dev->net->set_multicast_list = smsc9500_set_multicast;
+#else
+ dev->net->netdev_ops = &smsc95xx_netdev_ops;
+#endif //2.6.29
+ dev->net->ethtool_ops = &smsc9500_ethtool_ops;
+ dev->net->flags|=IFF_MULTICAST;
+
+ dev->linkDownSuspend = linkdownsuspend;
+ dev->dynamicSuspend = dynamicsuspend;
+#ifndef CONFIG_PM
+ if(dev->dynamicSuspend || dev->linkDownSuspend){
+ SMSC_WARNING("Power management has to be enabled in the kernel configuration to support dynamicsuspend and linkdownsuspend");
+ dev->dynamicSuspend = dev->linkDownSuspend = 0;
+ }
+#endif //CONFIG_PM
+#ifndef CONFIG_USB_SUSPEND
+ if(dev->dynamicSuspend || dev->linkDownSuspend){
+ SMSC_WARNING("Usb suspend has to be enabled in the kernel configuration to support dynamicsuspend and linkdownsuspend");
+ dev->dynamicSuspend = dev->linkDownSuspend = 0;
+ }
+#endif //CONFIG_USB_SUSPEND
+
+ if(dev->chipDependFeatures[FEATURE_SMARTDETACH]){
+ dev->smartDetach = smartdetach;
+ //If smart detach is enabled, link down suspend should be disabled
+ if(dev->smartDetach)dev->linkDownSuspend = 0;
+ }
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
+#ifdef CONFIG_PM
+ if(dev->dynamicSuspend || dev->linkDownSuspend){
+ if(dev->udev->autosuspend_disabled){
+ SMSC_WARNING("Autosuspend should be enabled by shell cmd \"echo auto > /sys/bus/usb/devices/X-XX/power/level\"");
+ }
+ }
+#endif //CONFIG_PM
+#endif
+ adapterData->UseScatterGather=scatter_gather;
+ adapterData->UseTxCsum=tx_Csum;
+ adapterData->UseRxCsum=rx_Csum;
+ if (scatter_gather)
+ SMSC_TRACE(DBG_INIT,"Tx Scatter-Gather");
+ if (tx_Csum)
+ SMSC_TRACE(DBG_INIT,"Tx HW Checksum");
+ if (rx_Csum)
+ SMSC_TRACE(DBG_INIT,"Rx HW Checksum");
+
+
+ if(adapterData->UseScatterGather) {
+
+ if(adapterData->UseTxCsum)
+ dev->net->features = (NETIF_F_HW_CSUM | NETIF_F_SG | NETIF_F_FRAGLIST);
+ else
+ dev->net->features = (NETIF_F_SG | NETIF_F_FRAGLIST); // Kernel will turn off SG in this case.
+ }
+
+ else {
+
+ if(adapterData->UseTxCsum)
+ dev->net->features = (NETIF_F_HW_CSUM);
+ else
+ dev->net->features = 0;
+ }
+
+ adapterData->dwTxQueueDisableMask=0;
+ spin_lock_init(&(adapterData->TxQueueLock));
+ adapterData->TxInitialized=TRUE;
+
+ adapterData->WolWakeupOpts= 0;
+
+ adapterData->LinkActLedCfg = LinkActLedCfg;
+ adapterData->LinkLedOnGpio = LinkLedOnGpio;
+ adapterData->LinkLedOnGpioBufType = LinkLedBufType;
+ adapterData->LinkLedOnGpioPolarity = LinkLedPolarity;
+
+ adapterData->LanInitialized=TRUE;
+
+ SMSC_TRACE(DBG_INIT,"<--------out of bind, return 0\n");
+ return 0;
+
+ if (adapterData != NULL){
+ kfree(adapterData);
+ adapterData=NULL;
+ }
+out1:
+ SMSC_TRACE(DBG_INIT,"<--------bind out1, return %d\n",ret);
+ return ret;
+}
+
+
+static void smsc9500_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+ SMSC_TRACE(DBG_CLOSE,"------->in smsc9500_unbind\n");
+
+ if (adapterData != NULL){
+ SMSC_TRACE(DBG_CLOSE,"free adapterData\n");
+ kfree(adapterData);
+ adapterData=NULL;
+ }
+
+ SMSC_TRACE(DBG_CLOSE,"<-------out of smsc9500_unbind\n");
+}
+
+static int smsc9500_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+ u8 *head;
+ u16 size;
+ u32 header,AlignCount=0;
+ char *packet;
+ struct sk_buff *ax_skb;
+ int ret = RX_FIXUP_VALID_SKB;
+ u16 *vlan_tag;
+
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+ SMSC_TRACE(DBG_RX,"------->in smsc9500_rx_fixup\n");
+
+ head = (u8 *) skb->data;
+ memcpy(&header, head, sizeof(header));
+ le32_to_cpus(&header);
+#ifdef RX_OFFSET
+ skb_pull(skb, 4 + NET_IP_ALIGN); //two extra for ip header alignment
+ packet = skb->data;
+#else
+ packet = head + sizeof(header);
+ skb_pull(skb, 4);
+#endif //RX_OFFSET
+
+ while (skb->len > 0) {
+ /* get the packet length */
+ size = (u16) ((header & RX_STS_FL_)>>16);
+
+#ifdef RX_OFFSET
+ AlignCount = (STATUS_WORD_LEN - ((size + NET_IP_ALIGN) % STATUS_WORD_LEN)) % STATUS_WORD_LEN;
+#else
+ AlignCount = (STATUS_WORD_LEN - (size % STATUS_WORD_LEN)) % STATUS_WORD_LEN;
+#endif
+
+ if(header & RX_STS_ES_){
+ dev->stats.rx_errors++;
+ dev->stats.rx_dropped++;
+ if(header & RX_STS_CRC_){
+ dev->stats.rx_crc_errors++;
+ }else{
+ if(header & (RX_STS_TL_ | RX_STS_RF_)){
+ dev->stats.rx_frame_errors++;
+ }
+ if(((header & RX_STS_LE_) != 0L) && ((header & RX_STS_FT_)==0L)){
+ dev->stats.rx_length_errors++;
+ }
+ }
+
+ if(size == skb->len){//last packet
+ return RX_FIXUP_INVALID_SKB;
+ }else{
+
+ skb_pull(skb, size+AlignCount);
+
+ if (skb->len == 0) {
+ ret = RX_FIXUP_INVALID_SKB;
+
+ return ret;
+ }
+
+ goto NEXT_PACKET;
+ }
+
+ }
+
+ if ((size == skb->len)){
+
+ if (adapterData->UseRxCsum) {
+
+ u16 wHwCsum;
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+ wHwCsum = *(u16*)(skb_tail_pointer(skb) - 2);
+#else
+ wHwCsum = *(u16*)(skb->tail - 2);
+#endif
+ skb->csum = wHwCsum;
+
+ }
+
+
+ if (adapterData->UseRxCsum)
+
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
+ skb->ip_summed = CHECKSUM_HW;
+ #else
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ #endif
+
+ else
+ skb->ip_summed = CHECKSUM_NONE;
+
+ if (adapterData->UseRxCsum)
+ {
+ skb_trim(skb,size-2-4);
+
+ }
+ else
+ {
+ skb_trim(skb,size-4);
+
+ }
+
+#ifdef RX_SKB_COPY
+//FIXME: Kernel calculate received size based on skb->truesize, which holds total buffer size.
+//If we allocate a big skb buffer, but only part of buffer hold valid data like turbo mode did,
+//Kernel accumulate received data with skb->truesize, so total received data might be over limit. But
+//actual data size isn't, then kernel may drop the subsequent packets.
+ if(TurboMode){
+ ax_skb = alloc_skb (skb->len + NET_IP_ALIGN, GFP_ATOMIC);
+ skb_reserve (ax_skb, NET_IP_ALIGN);
+ skb_put(ax_skb, skb->len);
+ memcpy(ax_skb->data, skb->data, skb->len);
+
+ vlan_tag = (u16*)&ax_skb->cb[0];
+ *vlan_tag = VLAN_DUMMY; //Reserved value
+ smscusbnet_skb_return(dev, ax_skb);
+ ret = RX_FIXUP_INVALID_SKB;
+ }else{
+ ret = RX_FIXUP_VALID_SKB;
+ vlan_tag = (u16*)&skb->cb[0];
+ *vlan_tag = VLAN_DUMMY; //Reserved value
+ }
+#else
+//FIXME: We are not supposed to change truesize, but this is the easy way to cheat kernel without memory copy
+ skb->truesize = skb->len + sizeof(struct sk_buff);
+ vlan_tag = (u16*)&skb->cb[0];
+ *vlan_tag = VLAN_DUMMY; //Reserved value
+#endif
+ return ret;
+ }
+
+
+ if (size > (ETH_FRAME_LEN+12)) { // ETH_FRAME_LEN+4(CRC)+2(COE)+4(Vlan)
+ SMSC_TRACE(DBG_RX,"size > (ETH_FRAME_LEN+12), hearder= 0x%08x\n", header);
+ return RX_FIXUP_ERROR;
+ }
+
+#ifndef RX_SKB_COPY
+ ax_skb = skb_clone(skb, GFP_ATOMIC);
+#else
+ ax_skb = alloc_skb (size + NET_IP_ALIGN, GFP_ATOMIC);
+ skb_reserve (ax_skb, NET_IP_ALIGN);
+#endif
+ if (ax_skb) {
+#ifndef RX_SKB_COPY
+ ax_skb->len = size;
+ ax_skb->data = packet;
+
+ skb_trim(ax_skb, 0);
+ skb_put(ax_skb, size);
+
+#else
+ skb_put(ax_skb, size);
+ memcpy(ax_skb->data, packet, size);
+#endif
+
+ if (adapterData->UseRxCsum) {
+
+ u16 wHwCsum;
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+ wHwCsum = *(u16*)(skb_tail_pointer(ax_skb) - 2);
+#else
+ wHwCsum = *(u16*)(ax_skb->tail - 2);
+#endif
+ ax_skb->csum = wHwCsum;
+
+ }
+
+
+ if (adapterData->UseRxCsum)
+
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
+ ax_skb->ip_summed = CHECKSUM_HW;
+ #else
+ ax_skb->ip_summed = CHECKSUM_COMPLETE;
+ #endif
+
+ else
+ ax_skb->ip_summed = CHECKSUM_NONE;
+
+ if (adapterData->UseRxCsum)
+ {
+ skb_trim(ax_skb,size-2-4);
+
+ }
+ else
+ {
+ skb_trim(ax_skb,size-4);
+
+ }
+
+#ifndef RX_SKB_COPY
+//FIXME: We are not supposed to change truesize, but this is the easy way to cheat kernel without memory copy
+ ax_skb->truesize = ax_skb->len + sizeof(struct sk_buff);
+#endif
+
+ vlan_tag = (u16*)&ax_skb->cb[0];
+ *vlan_tag = VLAN_DUMMY; //Reserved value
+ smscusbnet_skb_return(dev, ax_skb);
+ } else {
+ SMSC_TRACE(DBG_RX,"no ax_skb\n");
+ return RX_FIXUP_ERROR;
+ }
+
+ skb_pull(skb, size+AlignCount);
+
+ if (skb->len == 0) {
+ SMSC_TRACE(DBG_RX,"skb->len==0 left\n");
+ break;
+ }
+NEXT_PACKET:
+ head = (u8 *) skb->data;
+ memcpy(&header, head, sizeof(header));
+ le32_to_cpus(&header);
+#ifdef RX_OFFSET
+ skb_pull(skb, 4 + NET_IP_ALIGN); //two extra for ip header alignment
+ packet = skb->data;
+#else
+ packet = head + sizeof(header);
+ skb_pull(skb, 4);
+#endif //RX_OFFSET
+ }
+
+ if (skb->len < 0) {
+ SMSC_WARNING("invalid rx length<0 %d", skb->len);
+ return RX_FIXUP_ERROR;
+ }
+
+
+ SMSC_TRACE(DBG_RX,"<-------out of smsc9500_rx_fixup\n");
+ return ret;
+}
+
+static struct sk_buff *smsc9500_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
+ int flags)
+{
+
+#ifndef TX_SKB_FORCE_COPY
+ int headroom = skb_headroom(skb);
+ int tailroom = skb_tailroom(skb);
+#endif /* TX_SKB_FORCE_COPY */
+ int SkbSize,CopySize,AlignmentSize;
+ u8 * prt;
+ int i;
+
+ unsigned skbFragCnt = skb_shinfo(skb)->nr_frags + 1;
+ u32 TxCommandA,TxCommandB;
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+ SMSC_TRACE(DBG_TX,"in smsc9500_tx_fixup\n");
+
+ if (adapterData->UseTxCsum) {
+
+
+ u32 dwTxCsumPreamble=0;
+
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
+
+ if (skb->ip_summed == CHECKSUM_HW)
+ {
+ int Chsum_start_offset=0;
+ CalculateTxChecksumOffset(
+ skb,
+ &Chsum_start_offset);
+
+ /* tx checksum problem workaround */
+ if (skb->len <= 45) {
+ u32 csum;
+ csum = csum_partial(skb->data + Chsum_start_offset, skb->len - (skb->data + Chsum_start_offset - skb->data), 0);
+ *((u16 *)(skb->data + Chsum_start_offset + skb->csum)) = csum_fold(csum);
+ goto Non_CheckSumOffLoad;
+ }
+
+ dwTxCsumPreamble=(((u16) (Chsum_start_offset + skb->csum)) << 16) | ((u16) Chsum_start_offset);
+
+ }
+ #else
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ {
+
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22))
+ int Chsum_start_offset=0;
+ CalculateTxChecksumOffset(
+ skb,
+ &Chsum_start_offset);
+
+ /* tx checksum problem workaround */
+ if (skb->len <= 45) {
+ u32 csum;
+ csum = csum_partial(skb->data + Chsum_start_offset, skb->len - (skb->data + Chsum_start_offset - skb->data), 0);
+ *((u16 *)(skb->data + Chsum_start_offset + skb->csum)) = csum_fold(csum);
+ goto Non_CheckSumOffLoad;
+ }
+
+ dwTxCsumPreamble=(((u16) (Chsum_start_offset + skb->csum)) << 16) | ((u16) Chsum_start_offset);
+
+ #else
+
+ /* tx checksum problem workaround */
+ if (skb->len <= 45) {
+ u32 csum;
+ csum = csum_partial(skb->head + skb->csum_start, skb->len - (skb->head + skb->csum_start - skb->data), 0);
+ *((u16 *)(skb->head + (skb->csum_start + skb->csum_offset))) = csum_fold(csum);
+ goto Non_CheckSumOffLoad;
+ }
+
+ dwTxCsumPreamble=(((u16) (skb->csum_offset+skb->csum_start-(skb->data - skb->head))) << 16)
+ | ((u16) (skb->csum_start-(skb->data - skb->head)));
+
+ #endif
+
+
+ }
+ #endif
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
+ if(skb->ip_summed == CHECKSUM_HW)
+ #else
+ if(skb->ip_summed == CHECKSUM_PARTIAL)
+ #endif
+ {
+ if (skbFragCnt ==1) {
+// ip_summed, one Fragament
+ SMSC_TRACE(DBG_TX,"ip_summed, Onefrag\n");
+
+ AlignmentSize = skb->len % STATUS_WORD_LEN;
+ if(AlignmentSize)AlignmentSize = STATUS_WORD_LEN - AlignmentSize;
+
+#ifndef TX_SKB_FORCE_COPY
+ if ((!skb_cloned(skb))
+ && ((headroom + tailroom) >= (12 + AlignmentSize))) {
+ if( (headroom < 12 ) || (tailroom < AlignmentSize) ){
+ skb->data = memmove(skb->head +12, skb->data, skb->len);
+ SkbSize = skb->len;
+ skb_trim(skb, 0);
+ skb_put(skb, SkbSize);
+
+
+ }
+ } else
+#endif /* TX_SKB_FORCE_COPY */
+ {
+ struct sk_buff *skb2;
+ skb2 = skb_copy_expand(skb, 12, AlignmentSize, flags);
+ dev_kfree_skb_any(skb);
+ skb = skb2;
+ if (!skb)
+ return NULL;
+ }
+
+ skb_push(skb, STATUS_WORD_LEN);
+ memcpy(skb->data, &dwTxCsumPreamble, STATUS_WORD_LEN);
+
+ skb_push(skb, STATUS_WORD_LEN);
+ TxCommandB=(u32)(skb->len-STATUS_WORD_LEN)|TX_CMD_B_CSUM_ENABLE;
+ cpu_to_le32s((u32*)&TxCommandB);
+ memcpy(skb->data, &TxCommandB, STATUS_WORD_LEN);
+ skb_push(skb, STATUS_WORD_LEN);
+ TxCommandA= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_ | (u32)(skb->len-8);
+ cpu_to_le32s((u32*)&TxCommandA);
+ memcpy(skb->data, &TxCommandA, STATUS_WORD_LEN);
+ skb_put(skb, AlignmentSize);
+ return skb;
+ }
+ else {
+// ip_summed, Multi Fragament
+
+ struct sk_buff *skb2;
+ SkbSize=skb->len+4*4*(skbFragCnt+1);
+ skb2 = dev_alloc_skb(SkbSize);
+
+ SMSC_TRACE(DBG_TX,"ip_summed, Multifrags\n");
+ if (!skb2)
+ return NULL;
+
+ skb_put(skb2, SkbSize);
+
+
+ {
+
+ TxCommandA=TX_CMD_A_FIRST_SEG_ |((u32)sizeof(u32));
+
+ TxCommandB=TX_CMD_B_CSUM_ENABLE |((u32)(skb->len+STATUS_WORD_LEN)) ;
+ cpu_to_le32s((u32*)&TxCommandA);
+ cpu_to_le32s((u32*)&TxCommandB);
+
+ memcpy(skb2->data, &TxCommandA, STATUS_WORD_LEN);
+ memcpy(skb2->data+STATUS_WORD_LEN, &TxCommandB, STATUS_WORD_LEN);
+ memcpy(skb2->data+8, &dwTxCsumPreamble, STATUS_WORD_LEN);
+ }
+
+ {
+
+
+ TxCommandA =
+ ((((unsigned long)(skb->data))&0x03UL)<<16) | //u32 alignment adjustment
+ ((u32)((skb->len)-(skb->data_len)));
+ TxCommandB=
+ ((u32)(skb->len+STATUS_WORD_LEN));
+ cpu_to_le32s((u32*)&TxCommandA);
+ cpu_to_le32s((u32*)&TxCommandB);
+ CopySize=((((u32)(skb->len - skb->data_len))+3+(((unsigned long)(skb->data))&0x03UL))>>2)*4;
+ memcpy(skb2->data + 12, &TxCommandA, STATUS_WORD_LEN);
+ memcpy(skb2->data + 12 + STATUS_WORD_LEN, &TxCommandB, STATUS_WORD_LEN);
+ memcpy(skb2->data + 12 + STATUS_WORD_LEN * 2, (u32 *)(((unsigned long)(skb->data))&0xFFFFFFFCUL),CopySize);
+ }
+
+ prt=(u8 *)skb2->data+20+CopySize;
+
+ for(i=1;i<skbFragCnt;i++)
+ {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1];
+ void *frag_addr = page_address(frag->page) + frag->page_offset;
+
+
+ TxCommandA=
+ ((((unsigned long)(frag_addr))&0x03UL)<<16) | //alignment adjustment
+ ((u32)(frag->size));
+
+
+ if (i==(skbFragCnt-1)){
+ TxCommandA |= TX_CMD_A_LAST_SEG_ ;
+ }
+
+
+ TxCommandB= ((u32)(skb->len+STATUS_WORD_LEN));
+ cpu_to_le32s((u32*)&TxCommandA);
+ cpu_to_le32s((u32*)&TxCommandB);
+ memcpy(prt, &TxCommandA, STATUS_WORD_LEN);
+ prt=prt+STATUS_WORD_LEN;
+ memcpy(prt, &TxCommandB, STATUS_WORD_LEN);
+ prt=prt+STATUS_WORD_LEN;
+ CopySize=((((unsigned long)(frag->size))+3+(((unsigned long)(frag_addr))&0x03UL))>>2)*4;
+ memcpy(prt, (u32 *)(((unsigned long)(frag_addr))&0xFFFFFFFCUL),CopySize);
+ prt=prt+CopySize;
+
+ }
+
+ skb_trim(skb2,prt-skb2->data);
+ dev_kfree_skb_any(skb);
+ return skb2;
+ }
+
+ }
+ else {
+ if (skbFragCnt >1) {
+//Non ip_summed, Multifrags
+
+ struct sk_buff *skb2;
+ SkbSize=skb->len+4*4*skbFragCnt;
+ skb2 = dev_alloc_skb(SkbSize);
+ SMSC_TRACE(DBG_TX,"Non ip_summed, Multifrags\n");
+
+ if (!skb2)
+ return NULL;
+
+ skb_put(skb2, SkbSize);
+
+ {
+
+ TxCommandA =((((unsigned long)(skb->data))&0x03UL)<<16) | //u32 alignment adjustment
+ TX_CMD_A_FIRST_SEG_ |
+ (u32)((skb->len)-(skb->data_len));
+
+ TxCommandB=TX_CMD_B_CSUM_ENABLE |((u32)(skb->len));
+ cpu_to_le32s((u32*)&TxCommandA);
+ cpu_to_le32s((u32*)&TxCommandB);
+ SMSC_TRACE(DBG_TX,"first frag. \n");
+ CopySize=((((unsigned long)((skb->len)-(skb->data_len)))+3+(((unsigned long)(skb->data))&0x03UL))>>2)*4;
+ memcpy(skb2->data, &TxCommandA, STATUS_WORD_LEN);
+ memcpy(skb2->data+STATUS_WORD_LEN, &TxCommandB, STATUS_WORD_LEN);
+ memcpy(skb2->data+STATUS_WORD_LEN*2, (void *)(((unsigned long)(skb->data))&0xFFFFFFFCUL),CopySize);
+ }
+
+ prt=(u8 *)skb2->data+8+CopySize;
+
+ for(i=1;i<skbFragCnt;i++)
+ {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1];
+ void *frag_addr = page_address(frag->page) + frag->page_offset;
+
+
+ TxCommandA=
+ ((((unsigned long)(frag_addr))&0x03UL)<<16) | //u32 alignment adjustment
+ ((u32)(frag->size));
+
+
+ if (i==(skbFragCnt-1)){
+ TxCommandA |= TX_CMD_A_LAST_SEG_ ;
+ }
+
+
+ TxCommandB=((u32)(skb->len));
+ cpu_to_le32s((u32*)&TxCommandA);
+ cpu_to_le32s((u32*)&TxCommandB);
+ memcpy(prt, &TxCommandA, STATUS_WORD_LEN);
+ prt=prt+STATUS_WORD_LEN;
+ memcpy(prt, &TxCommandB, STATUS_WORD_LEN);
+ prt=prt+STATUS_WORD_LEN;
+
+ CopySize=((((unsigned long)(frag->size))+3+(((unsigned long)(frag_addr))&0x03UL))>>2)*4;
+ memcpy(prt, (void *)(((unsigned long)(frag_addr))&0xFFFFFFFCUL),CopySize);
+ prt=prt+CopySize;
+
+ }
+
+ skb_trim(skb2,prt-skb2->data);
+ dev_kfree_skb_any(skb);
+ SMSC_TRACE(DBG_TX,"return from Nonip_summed, Multifrags\n");
+ return skb2;
+ } else {
+ goto Non_CheckSumOffLoad;
+ }
+ }
+
+ }
+ else {
+Non_CheckSumOffLoad:
+//Non ip_summed, onefrag
+ SMSC_TRACE(DBG_TX,"Non ip_summed, onefrag\n");
+ AlignmentSize = skb->len % STATUS_WORD_LEN;
+ if(AlignmentSize)AlignmentSize = STATUS_WORD_LEN - AlignmentSize;
+
+#ifndef TX_SKB_FORCE_COPY
+ if ((!skb_cloned(skb))
+ && ((headroom + tailroom) >= (2*STATUS_WORD_LEN + AlignmentSize))) {
+ if ((headroom < (2*STATUS_WORD_LEN)) || (tailroom < AlignmentSize)){
+ skb->data = memmove(skb->head + 2*STATUS_WORD_LEN, skb->data, skb->len);
+ SkbSize = skb->len;
+ skb_trim(skb, 0);
+ skb_put(skb, SkbSize);
+ }
+ } else
+#endif /* TX_SKB_FORCE_COPY */
+ {
+ struct sk_buff *skb2;
+ skb2 = skb_copy_expand(skb, 2*STATUS_WORD_LEN, AlignmentSize, flags);
+ dev_kfree_skb_any(skb);
+ skb = skb2;
+ if (!skb)
+ return NULL;
+ }
+
+ skb_push(skb, STATUS_WORD_LEN);
+ TxCommandB=(u32)(skb->len - STATUS_WORD_LEN);
+ cpu_to_le32s((u32*)&TxCommandB);
+
+ memcpy(skb->data, &TxCommandB, STATUS_WORD_LEN);
+ skb_push(skb, STATUS_WORD_LEN);
+ TxCommandA= TX_CMD_A_FIRST_SEG_ | TX_CMD_A_LAST_SEG_ | (u32)(skb->len - 2*STATUS_WORD_LEN);
+ cpu_to_le32s((u32*)&TxCommandA);
+ memcpy(skb->data, &TxCommandA, STATUS_WORD_LEN);
+
+ skb_put(skb, AlignmentSize);
+ return skb;
+ }
+
+}
+
+static int smsc9500_reset(struct usbnet *dev)
+{
+ int ret=0,Timeout;
+ u32 dwReadBuf, dwAddrH, dwAddrL, dwWriteBuf,dwMacCr,DwTemp, dwBurstCap;
+ SMSC9500_RX_STATS rx_stats;
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+ SMSC_TRACE(DBG_INIT,"---------->smsc9500_reset\n");
+
+ if ((ret = smsc9500_read_reg(dev,HW_CFG,&dwReadBuf)< 0)) {
+ SMSC_WARNING("Failed to read HW_CFG: %d", ret);
+ return ret;
+ }
+ dwReadBuf |= HW_CFG_LRST_;
+ if ((ret = smsc9500_write_reg(dev, HW_CFG, dwReadBuf))<0)
+ {
+ SMSC_WARNING("Failed to write HW_CFG_LRST_ bit in HW_CFG register, ret = %d \n",ret);
+ return ret;
+ }
+
+ Timeout = 0;
+ do {
+ if ((ret = smsc9500_read_reg(dev,HW_CFG,&dwReadBuf)< 0)) {
+ SMSC_WARNING("Failed to read HW_CFG: %d", ret);
+ return ret;
+ }
+ msleep(100); /* wait for 100 us before trying again */
+ Timeout++;
+ } while ( (dwReadBuf & HW_CFG_LRST_) && (Timeout < 100));
+
+ if(Timeout >= 100)
+ {
+ SMSC_WARNING("Timeout waiting for completion of Lite Reset\n");
+ return ret;
+ }
+
+ if((ret = smsc9500_read_reg(dev, ID_REV, &dev->chipID)) < 0){
+ SMSC_WARNING("Failed to read GPIO_CFG: %d", ret);
+ return ret;
+ }
+ dev->chipID = dev->chipID >> 16;
+
+ /*******Enable new features**************/
+ if(dev->chipID == ID_REV_9500A_CHIPID){
+ int i;
+ for(i=0; i<FEATURE_MAX_NO; i++){
+ dev->chipDependFeatures[i] = TRUE;
+ }
+ }
+ if(dev->chipID == ID_REV_9512_CHIPID)dev->chipDependFeatures[FEATURE_WUFF_8] = TRUE;
+ /*******Enable new features**************/
+
+ if ((ret = smsc9500_read_reg(dev,PM_CTRL,&DwTemp)< 0)) {
+ SMSC_WARNING("Failed to read PM_CTRL: %d", ret);
+ return ret;
+ }
+
+ if ((ret = smsc9500_write_reg(dev, PM_CTRL, (DwTemp | PM_CTL_PHY_RST_))< 0)) {
+ SMSC_WARNING("Failed to write PM_CTRL: %d", ret);
+ return ret;
+ }
+
+ Timeout = 0;
+ do {
+ if ((ret = smsc9500_read_reg(dev,PM_CTRL,&dwReadBuf)< 0)) {
+ SMSC_WARNING("Failed to read PM_CTRL: %d", ret);
+ return ret;
+ }
+ msleep(100);
+ Timeout++;
+ } while ( (dwReadBuf & PM_CTL_PHY_RST_) && (Timeout < 100));
+
+ if(Timeout >= 100)
+ {
+ SMSC_WARNING("Timeout waiting for PHY Reset\n");
+ return ret;
+ }
+ dwAddrH = 0x0000FFFFUL;
+ dwAddrL = 0xFFFFFFFF;
+
+ if(adapterData->macAddrHi16 != 0xFFFFFFFF || adapterData->MmacAddrLo32 != 0xFFFFFFFF){
+ dwAddrH = adapterData->macAddrHi16 & 0xFFFF;
+ dwAddrL = adapterData->MmacAddrLo32;
+ }else{
+ if(adapterData->eepromSize && smsc9500_read_eeprom(dev, EEPROM_MAC_OFFSET, 6, dev->net->dev_addr) == 0){
+ dwAddrL = dev->net->dev_addr[0] | dev->net->dev_addr[1] << 8 | dev->net->dev_addr[2] << 16 | dev->net->dev_addr[3] << 24;
+ dwAddrH = dev->net->dev_addr[4] | dev->net->dev_addr[5] << 8;
+ }else{//LAN9500's descriptor RAM may provide Mac address
+ MAC_ADDR_IN_RAM macRam;
+ if(ReadDataPort(dev, RAMSEL_EEPROM, 0, sizeof(MAC_ADDR_IN_RAM)/4, (u32*)&macRam, NULL) == SMSC9500_SUCCESS){
+ cpu_to_le32s(&macRam.signature);
+ cpu_to_le32s(&macRam.MacAddrL);
+ cpu_to_le32s(&macRam.MacAddrH);
+ cpu_to_le16s(&macRam.crc);
+ cpu_to_le16s(&macRam.crcComplement);
+ if(macRam.signature == 0x736D7363){//Signature "smsc"
+ u16 crc = CalculateCrc16((char*)&macRam, 12, FALSE);
+ if((crc == macRam.crc) && (crc == (u16)~macRam.crcComplement)){
+ dwAddrL = macRam.MacAddrL;
+ dwAddrH = macRam.MacAddrH;
+ }
+ }
+ }
+ }
+ }
+
+ //Mac address could be initialized by system firmware. Lan9500A will implement this way.
+ if((dwAddrH==0x0000FFFFUL)&&(dwAddrL==0xFFFFFFFF)){
+ if ((ret = smsc9500_read_reg(dev,ADDRL, &dwAddrL)< 0)) {
+ SMSC_WARNING("Failed to read ADDRL: %d", ret);
+ return ret;
+ }
+ if ((ret = smsc9500_read_reg(dev,ADDRH, &dwAddrH)< 0)) {
+ SMSC_WARNING("Failed to read ADDRH: %d", ret);
+ return ret;
+ }
+ }
+
+ if(((dwAddrH & 0xFFFF) == 0x0000FFFFUL) && (dwAddrL == 0xFFFFFFFF))
+ {
+ dwAddrH=0x00000070UL;
+ dwAddrL=0x110F8000UL;
+
+ SMSC_TRACE(DBG_INIT,"Mac Address is set by default to 0x%04X%08X\n",
+ dwAddrH,dwAddrL);
+ }
+ adapterData->macAddrHi16 = dwAddrH;
+ adapterData->MmacAddrLo32 = dwAddrL;
+
+ if ((ret = smsc9500_write_reg(dev,ADDRL, dwAddrL)< 0)) {
+ SMSC_WARNING("Failed to write ADDRL: %d", ret);
+ return ret;
+ }
+ if ((ret = smsc9500_write_reg(dev,ADDRH, dwAddrH)< 0)) {
+ SMSC_WARNING("Failed to write ADDRH: %d", ret);
+ return ret;
+ }
+
+ dev->net->dev_addr[0]=LOBYTE(LOWORD(dwAddrL));
+ dev->net->dev_addr[1]=HIBYTE(LOWORD(dwAddrL));
+ dev->net->dev_addr[2]=LOBYTE(HIWORD(dwAddrL));
+ dev->net->dev_addr[3]=HIBYTE(HIWORD(dwAddrL));
+ dev->net->dev_addr[4]=LOBYTE(LOWORD(dwAddrH));
+ dev->net->dev_addr[5]=HIBYTE(LOWORD(dwAddrH));
+
+ SMSC_TRACE(DBG_INIT,"dev->net->dev_addr %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dev->net->dev_addr [0], dev->net->dev_addr [1],
+ dev->net->dev_addr [2], dev->net->dev_addr [3],
+ dev->net->dev_addr [4], dev->net->dev_addr [5]);
+
+ if (!(smscusbnet_IsOperationalMode(dev))) {
+
+ if ((ret = smsc9500_read_reg(dev,HW_CFG,&dwReadBuf)< 0)) {
+ SMSC_WARNING("Failed to read HW_CFG: %d", ret);
+ return ret;
+ }
+ SMSC_TRACE(DBG_INIT,"Read Value from HW_CFG : 0x%08x\n",dwReadBuf);
+
+ dwReadBuf |=HW_CFG_BIR_;
+ if ((ret = smsc9500_write_reg(dev, HW_CFG, dwReadBuf))<0)
+ {
+ SMSC_WARNING("Failed to write HW_CFG_BIR_ bit in HW_CFG register, ret = %d ",ret);
+ return ret;
+ }
+
+ if ((ret = smsc9500_read_reg(dev,HW_CFG,&dwReadBuf)< 0)) {
+ SMSC_WARNING("Failed to read HW_CFG: %d", ret);
+ return ret;
+ }
+ SMSC_TRACE(DBG_INIT,"Read Value from HW_CFG after writing HW_CFG_BIR_: 0x%08x\n",dwReadBuf);
+ }
+
+ if (TurboMode) {
+ if(dev->udev->speed == USB_SPEED_HIGH){
+ dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE;
+ dwBurstCap = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE;
+ }else{
+ dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE;
+ dwBurstCap = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE;
+ }
+ }else{
+ dwBurstCap = 0;
+ dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE;
+ }
+ SMSC_TRACE(DBG_INIT,"rx_urb_size= %d\n", (int)dev->rx_urb_size);
+
+ if ((ret = smsc9500_write_reg(dev, BURST_CAP, dwBurstCap))<0)
+ {
+ SMSC_WARNING("Failed to write BURST_CAP");
+ return ret;
+ }
+ if ((ret = smsc9500_read_reg(dev,BURST_CAP,&dwReadBuf)< 0)) {
+ SMSC_WARNING("Failed to read BURST_CAP: %d", ret);
+ return ret;
+ }
+ SMSC_TRACE(DBG_INIT,"Read Value from BURST_CAP after writing: 0x%08x\n",dwReadBuf);
+
+ if ((ret = smsc9500_write_reg(dev, BULK_IN_DLY, bulkin_delay))<0)
+ {
+ SMSC_WARNING("Failed to write BULK_IN_DLY");
+ return ret;
+ }
+ if ((ret = smsc9500_read_reg(dev,BULK_IN_DLY,&dwReadBuf)< 0)) {
+ SMSC_WARNING("Failed to read BULK_IN_DLY: %d", ret);
+ return ret;
+ }
+ SMSC_TRACE(DBG_INIT,"Read Value from BULK_IN_DLY after writing: 0x%08x\n",dwReadBuf);
+
+ if ((ret = smsc9500_read_reg(dev,HW_CFG,&dwReadBuf)< 0)) {
+ SMSC_WARNING("Failed to read HW_CFG: %d", ret);
+ return ret;
+ }
+ SMSC_TRACE(DBG_INIT,"Read Value from HW_CFG: 0x%08x\n",dwReadBuf);
+
+ if (TurboMode) {
+ dwReadBuf |= (HW_CFG_MEF_|HW_CFG_BCE_);
+ }
+#ifdef RX_OFFSET
+ dwReadBuf &= ~HW_CFG_RXDOFF_;
+ dwReadBuf |= NET_IP_ALIGN << 9; //set Rx data offset=2, Make IP header aligns on word boundary.
+#endif
+ if ((ret = smsc9500_write_reg(dev, HW_CFG, dwReadBuf))<0)
+ {
+ SMSC_WARNING("Failed to write HW_CFG_BIR_ bit in HW_CFG register, ret = %d \n",ret);
+ return ret;
+ }
+
+ if ((ret = smsc9500_read_reg(dev,HW_CFG,&dwReadBuf)< 0)) {
+ SMSC_WARNING("Failed to read HW_CFG: %d", ret);
+ return ret;
+ }
+ SMSC_TRACE(DBG_INIT,"Read Value from HW_CFG after writing: 0x%08x\n",dwReadBuf);
+
+ if ((ret = smsc9500_write_reg(dev, INT_STS, 0xFFFFFFFFUL))<0)
+ {
+ SMSC_WARNING("Failed to write INT_STS register, ret = %d \n",ret);
+ return ret;
+ }
+
+
+ if ((ret = smsc9500_read_reg(dev,ID_REV,&dwReadBuf)< 0)) {
+ SMSC_WARNING("Failed to read ID_REV: %d", ret);
+ return ret;
+ }
+ adapterData->dwIdRev = dwReadBuf;
+ SMSC_TRACE(DBG_INIT,"ID_REV = 0x%08x\n",dwReadBuf);
+
+
+ if ((ret = smsc9500_read_reg(dev,FPGA_REV,&dwReadBuf)< 0)) {
+ SMSC_WARNING("Failed to read FPGA_REV: %d", ret);
+ return ret;
+ }
+ adapterData->dwFpgaRev = dwReadBuf;
+ SMSC_TRACE(DBG_INIT,"FPGA_REV = 0x%08x\n",dwReadBuf);
+
+ if (smscusbnet_IsOperationalMode(dev)) {
+
+ if ((ret = smsc9500_read_reg(dev,INT_EP_CTL,&dwReadBuf)< 0)) {
+ SMSC_WARNING("Failed to read INT_EP_CTL: %d", ret);
+ return ret;
+ }
+ SMSC_TRACE(DBG_INIT,"Read Value from INT_EP_CTL: 0x%08x\n",dwReadBuf);
+
+ dwReadBuf|=INT_EP_CTL_RX_FIFO_EN_;
+ if ((ret = smsc9500_write_reg(dev, INT_EP_CTL, dwReadBuf))<0)
+ {
+ SMSC_WARNING("Failed to write INT_EP_CTL register,ret = %d \n",ret);
+ return ret;
+ }
+
+ if ((ret = smsc9500_read_reg(dev,INT_EP_CTL,&dwReadBuf)< 0)) {
+ SMSC_WARNING("Failed to read INT_EP_CTL: %d", ret);
+ return ret;
+ }
+ SMSC_TRACE(DBG_INIT,"Read Value from INT_EP_CTLafter writing INT_EP_CTL_RX_FIFO_EN_: 0x%08x\n",dwReadBuf);
+ }
+
+//Init Tx
+
+ if(adapterData->UseTxCsum)
+
+ //Set TX COE
+ {
+ if ((ret = smsc9500_read_reg(dev, COE_CR,&dwReadBuf)< 0)) {
+ SMSC_WARNING("Failed to read COE_CR: %d", ret);
+ return ret;
+ }
+ dwReadBuf |= Tx_COE_EN_;
+
+ if ((ret = smsc9500_write_reg(dev, COE_CR, dwReadBuf)< 0)) {
+ SMSC_WARNING("Failed to write COE_CR: %d", ret);
+ return ret;
+ }
+
+ if ((ret = smsc9500_read_reg(dev, COE_CR,&DwTemp)< 0)) {
+ SMSC_WARNING("Failed to read COE_CR: %d", ret);
+ return ret;
+ }
+ SMSC_TRACE(DBG_INIT,"COE_CR = 0x%08x\n", DwTemp);
+ }
+
+ if ((ret = smsc9500_write_reg(dev, FLOW, 0x0UL)< 0)) {
+ SMSC_WARNING("Failed to write FLOW: %d", ret);
+ return ret;
+ }
+
+ if ((ret = smsc9500_write_reg(dev, AFC_CFG, AFC_CFG_DEFAULT)< 0)) {
+ SMSC_WARNING("Failed to write AFC_CFG: %d", ret);
+ return ret;
+ }
+
+ if ((ret = smsc9500_read_reg(dev, MAC_CR,&dwMacCr)< 0)) {
+ SMSC_WARNING("Failed to read MAC_CR: %d", ret);
+ return ret;
+ }
+ dwMacCr|=(MAC_CR_TXEN_);
+ if ((ret = smsc9500_write_reg(dev, MAC_CR, dwMacCr)< 0)) {
+ SMSC_WARNING("Failed to read MAC_CR: %d", ret);
+ return ret;
+ }
+ dwWriteBuf=TX_CFG_ON_;
+ if ((ret = smsc9500_write_reg(dev,TX_CFG, dwWriteBuf)< 0)) {
+ SMSC_WARNING("Failed to write TX_CFG: %d", ret);
+ return ret;
+ }
+
+//Init Rx
+
+ //Set Vlan
+ {
+ dwWriteBuf=(u32)ETH_P_8021Q;
+ if ((ret = smsc9500_write_reg(dev,VLAN1, dwWriteBuf)< 0)) {
+ SMSC_WARNING("Failed to write VAN1: %d", ret);
+ return ret;
+ }
+
+ }
+
+ //Set Rx COE
+ if (adapterData->UseRxCsum) {
+
+ if ((ret = smsc9500_read_reg(dev, COE_CR,&dwReadBuf)< 0)) {
+ SMSC_WARNING("Failed to read COE_CR: %d", ret);
+ return ret;
+ }
+
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15))
+ dwReadBuf|=(Rx_COE_EN_ | Rx_COE_MODE_);
+ #else
+ dwReadBuf|=Rx_COE_EN_;
+ #endif
+
+ if ((ret = smsc9500_write_reg(dev, COE_CR, dwReadBuf)< 0)) {
+ SMSC_WARNING("Failed to write COE_CR: %d", ret);
+ return ret;
+ }
+
+ if ((ret = smsc9500_read_reg(dev, COE_CR,&DwTemp)< 0)) {
+ SMSC_WARNING("Failed to read COE_CR: %d", ret);
+ return ret;
+ }
+ SMSC_TRACE(DBG_INIT,"COE_CR = 0x%08x\n", DwTemp);
+
+ }
+
+ if ((ret = smsc9500_read_reg(dev, MAC_CR,&dwMacCr)< 0)) {
+ SMSC_WARNING("Failed to read MAC_CR: %d", ret);
+ return ret;
+ }
+
+ dwMacCr|=MAC_CR_RXEN_;
+
+ if ((ret = smsc9500_write_reg(dev, MAC_CR, dwMacCr)< 0)) {
+ SMSC_WARNING("Failed to read MAC_CR: %d", ret);
+ return ret;
+ }
+
+ // Enable the LEDs by default
+ if ((ret = smsc9500_read_reg(dev, LED_GPIO_CFG,&DwTemp)< 0)) {
+ SMSC_WARNING("Failed to read LED_GPIO_CFG: %d", ret);
+ return ret;
+ }
+ DwTemp &= ~LED_GPIO_CFG_GPCTL_10_ | ~LED_GPIO_CFG_GPCTL_09_ | ~LED_GPIO_CFG_GPCTL_08_;
+
+ if((ret = smsc9500_write_reg( dev, LED_GPIO_CFG,
+ (LED_GPIO_CFG_GPCTL_LED_ << LED_GPIO_CFG_GPCTL_10_SH) |
+ (LED_GPIO_CFG_GPCTL_LED_ << LED_GPIO_CFG_GPCTL_09_SH) |
+ (LED_GPIO_CFG_GPCTL_LED_ << LED_GPIO_CFG_GPCTL_08_SH))) < 0)
+ {
+ SMSC_WARNING("Failed to write LED_GPIO_CFG: %d", ret);
+ return ret;
+ }
+
+ if (adapterData->LinkActLedCfg){ // Driver parameter enables separate Link and Activity LEDs in Thera 130
+ // Make sure that the device is Thera 130
+ if ((ret = smsc9500_read_reg(dev, ID_REV,&DwTemp)< 0)) {
+ SMSC_WARNING("Failed to read ID_REV: %d", ret);
+ return ret;
+ }
+ if(dev->chipDependFeatures[FEATURE_SEP_LEDS]){
+ u32 dwLedGpioCfg = (LED_GPIO_CFG_GPCTL_LED_ << LED_GPIO_CFG_GPCTL_10_SH) |
+ (LED_GPIO_CFG_GPCTL_LED_ << LED_GPIO_CFG_GPCTL_09_SH) |
+ (LED_GPIO_CFG_GPCTL_LED_ << LED_GPIO_CFG_GPCTL_08_SH);
+
+ // Enable separate Link/Act LEDs
+ dwLedGpioCfg |= LED_GPIO_CFG_LED_SEL_;
+
+ // Set LED GPIO Config
+ if((ret = smsc9500_write_reg(dev, LED_GPIO_CFG, dwLedGpioCfg)) < 0){
+ SMSC_WARNING("Failed to write LED_GPIO_CFG: %d", ret);
+ return ret;
+ }
+ }else{
+ // Reset to default value
+ adapterData->LinkActLedCfg = 0;
+ // Disable Software control LinkLed GPIO in case it is set
+ adapterData->LinkLedOnGpio = 11;
+ }
+ }else if (adapterData->LinkLedOnGpio <= 10){ // Software control LinkLed GPIO is enable
+ // If selected GPIO is GPIO0-GPIO7, make sure external PHY is not enable.
+ // GPIO0-GPIO7 pins are multiplexed with MII signals.
+ if (adapterData->LinkLedOnGpio < 8)
+ {
+ // Check PHY Strap
+ if((ret = smsc9500_read_reg(dev, HW_CFG, &DwTemp)) < 0){
+ SMSC_WARNING("Failed to read HW_CFG: %d", ret);
+ return ret;
+ }
+
+ if (DwTemp & HW_CFG_PSEL_) // External PHY is enable
+ {
+ SMSC_WARNING("External PHY Enable::GPIO%d can not be set as Link Up/Down Led\n", adapterData->LinkLedOnGpio);
+ // Disable Software control LinkLed GPIO
+ adapterData->LinkLedOnGpio = 11;
+ }
+ else
+ {
+ u32 dwGpioCfg;// = ~GPIO_CFG_GPO0_EN_ | GPIO_CFG_GPO0_DIR_;
+
+ if((ret = smsc9500_read_reg(dev, GPIO_CFG, &dwGpioCfg)) < 0){
+ SMSC_WARNING("Failed to read GPIO_CFG: %d", ret);
+ return ret;
+ }
+
+ dwGpioCfg &= ~(GPIO_CFG_GPO0_EN_ << adapterData->LinkLedOnGpio);
+ dwGpioCfg |= GPIO_CFG_GPO0_DIR_ << adapterData->LinkLedOnGpio;
+
+ // Check GPIO buffer type
+ if (!adapterData->LinkLedOnGpioBufType) // Push-Pull output
+ {
+ dwGpioCfg |= GPIO_CFG_GPO0_TYPE << adapterData->LinkLedOnGpio;
+ }
+
+ // Check GPIO Polarity
+ if (adapterData->LinkLedOnGpioPolarity) // Active low
+ {
+ dwGpioCfg |= GPIO_CFG_GPO0_DATA_ << adapterData->LinkLedOnGpio;
+ }
+
+ // Set GPIO Config register
+ if((ret = smsc9500_write_reg(dev, GPIO_CFG, dwGpioCfg)) < 0){
+ SMSC_WARNING("Failed to write GPIO_CFG: %d", ret);
+ return ret;
+ }
+ }
+ }
+ else
+ {
+ u32 dwLedGpioCfg = (LED_GPIO_CFG_GPCTL_LED_ << LED_GPIO_CFG_GPCTL_10_SH) |
+ (LED_GPIO_CFG_GPCTL_LED_ << LED_GPIO_CFG_GPCTL_09_SH) |
+ (LED_GPIO_CFG_GPCTL_LED_ << LED_GPIO_CFG_GPCTL_08_SH);
+
+ switch (adapterData->LinkLedOnGpio)
+ {
+ case 8:
+ // Enable GPIO 8
+ dwLedGpioCfg &= (~(LED_GPIO_CFG_GPCTL_LED_ << LED_GPIO_CFG_GPCTL_08_SH));
+ // Set Output Direction
+ dwLedGpioCfg |= LED_GPIO_CFG_GPDIR_08_;
+ // Set Buffer Type
+ if (!adapterData->LinkLedOnGpioBufType) // Push-Pull Output
+ {
+ dwLedGpioCfg |= LED_GPIO_CFG_GPBUF_08_;
+ }
+
+ // Check GPIO Polarity
+ if (adapterData->LinkLedOnGpioPolarity) // Active low
+ {
+ dwLedGpioCfg |= LED_GPIO_CFG_GPDATA_08_;
+ }
+ break;
+
+ case 9:
+ // Enable GPIO 9
+ dwLedGpioCfg &= (~(LED_GPIO_CFG_GPCTL_LED_ << LED_GPIO_CFG_GPCTL_09_SH));
+ // Set Output Direction
+ dwLedGpioCfg |= LED_GPIO_CFG_GPDIR_09_;
+ // Set Buffer Type
+ if (!adapterData->LinkLedOnGpioBufType) // Push-Pull Output
+ {
+ dwLedGpioCfg |= LED_GPIO_CFG_GPBUF_09_;
+ }
+
+ // Check GPIO Polarity
+ if (adapterData->LinkLedOnGpioPolarity) // Active low
+ {
+ dwLedGpioCfg |= LED_GPIO_CFG_GPDATA_09_;
+ }
+ break;
+
+ case 10:
+ // Enable GPIO 10
+ dwLedGpioCfg &= (~(LED_GPIO_CFG_GPCTL_LED_ << LED_GPIO_CFG_GPCTL_10_SH));
+ // Set Output Direction
+ dwLedGpioCfg |= LED_GPIO_CFG_GPDIR_10_;
+
+ // Set Buffer Type
+ if (!adapterData->LinkLedOnGpioBufType) // Push-Pull Output
+ {
+ dwLedGpioCfg |= LED_GPIO_CFG_GPBUF_10_;
+ }
+
+ // Check GPIO Polarity
+ if (adapterData->LinkLedOnGpioPolarity) // Active low
+ {
+ dwLedGpioCfg |= LED_GPIO_CFG_GPDATA_10_;
+ }
+ break;
+ }
+
+ // Set LED GPIO Config
+ if((ret = smsc9500_write_reg( dev, LED_GPIO_CFG, dwLedGpioCfg)) < 0){
+ SMSC_WARNING("Failed to write LED_GPIO_CFG: %d", ret);
+ return ret;
+ }
+ }
+ }
+
+ if(dev->chipDependFeatures[FEATURE_NEWSTATIS_CNT]){//Statistics counters are rollover ones in LAN9500A
+ if(smsc9500_get_stats(dev, (void*)&rx_stats) > 0){//Save initial value
+ le32_to_cpus((u32*)&rx_stats.RxFifoDroppedFrames);
+ rx_stats.RxFifoDroppedFrames &= 0xFFFFF; //This counter has 20 bits.
+ dev->preRxFifoDroppedFrame = rx_stats.RxFifoDroppedFrames;
+ }
+ }
+
+ smsc9500_rx_setmulticastlist(dev);
+
+ if (!Phy_Initialize(dev, phy_addr, link_mode))
+ return SMSC9500_FAIL;
+
+ SMSC_TRACE(DBG_INIT,"<--------out of smsc9500_reset, return 0\n");
+
+ return 0;
+}
+
+static int smsc9500_link_reset(struct usbnet *dev)
+{
+ int ret = 0;
+ SMSC_TRACE(DBG_LINK,"---->in smsc9500_link_reset\n");
+ ret = Phy_CheckLink(dev);
+
+ if(dev->StopLinkPolling){
+ clear_bit (EVENT_DEV_RECOVERY, &dev->flags);
+ }
+
+ if (test_bit (EVENT_DEV_RECOVERY, &dev->flags)) {
+ ret = smsc9500_device_recovery(dev);
+ clear_bit (EVENT_DEV_RECOVERY, &dev->flags);
+ }
+
+ SMSC_TRACE(DBG_LINK,"<----out of smsc9500_link_reset\n");
+ return ret;
+}
+
+
+static int smsc9500_stopTxPath(struct usbnet * dev)
+/*++
+
+Routine Description:
+
+ This routine Stops the Tx Path at both Mac and USB
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ u32 Value32;
+ int ret = SMSC9500_FAIL;
+ int Count = 0;
+
+ SMSC_TRACE(DBG_TX,"--> smsc9500_stopTxPath\n");
+
+ // Stop the Transmit path at SCSRs
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, TX_CFG, TX_CFG_STOP_));
+ // The bit should self clear as soon as the packet makes it out of the Mac,
+ // worst case is 10 Mbps HD and frame deferred to the maximum. This will
+ // be ~100ms tops. Assuming one register read per (micro)frame the case of
+ // high speed USB - 125us register read cycle time - is the worse and
+ // would need up to 800 reads. Let's just round up to 1000.
+ do
+ {
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, TX_CFG, &Value32));
+ // Let it try to do the 1000 reads even if the reg reads are failing
+ // If the previous write did go thru at least this way we have a better
+ // chance of making sure the transmit path did stop.
+ }
+ while ( (++Count<1000) && ((Value32 & (TX_CFG_STOP_ | TX_CFG_ON_)) != 0) );
+
+ // Disable Mac TX
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, MAC_CR, &Value32));
+ Value32 &= ~MAC_CR_TXEN_;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, MAC_CR, Value32 ));
+
+ SMSC_TRACE(DBG_TX,"<-- smsc9500_stopTxPath\n");
+ ret = SMSC9500_SUCCESS;
+DONE:
+ return ret;
+}
+
+static int smsc9500_stopAndFlushTxPath(struct usbnet *dev)
+/*++
+
+Routine Description:
+
+ This routine Stops the Tx Path at both Mac and USB and then flushes
+ the Tx Fifo pointers.
+
+Arguments:
+
+
+Return Value:
+
+--*/
+{
+
+ u32 Value32;
+ int ret = SMSC9500_FAIL;
+ SMSC_TRACE(DBG_TX,"--> smsc9500_stopAndFlushTxPath\n");
+
+
+ if(smsc9500_stopTxPath(dev) < 0)return ret;
+
+ // Flush the transmit path
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, TX_CFG, TX_CFG_FIFO_FLUSH_));
+
+ // Should self clear way before the read.
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, TX_CFG, &Value32));
+
+ if (Value32 & TX_CFG_FIFO_FLUSH_)
+ {
+ // Flush did not self clear!
+ goto DONE;
+ }
+ SMSC_TRACE(DBG_TX,"<-- smsc9500_stopAndFlushTxPath\n");
+ ret = SMSC9500_SUCCESS;
+DONE:
+ return ret;
+}
+
+
+static int smsc9500_stopRxPath(struct usbnet * dev)
+/*++
+
+Routine Description:
+
+ This routine Stops the Rx Path at both Mac and USB
+
+Arguments:
+
+Return Value:
+
+--*/
+{
+ u32 Value32;
+ int ret = SMSC9500_FAIL;
+ int Count = 0;
+
+ SMSC_TRACE(DBG_RX,"--> smsc9500_stopRxPath\n");
+
+ // Clr the Rx Stop bit if not already
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, INT_STS, INT_STS_RX_STOP_));
+
+ // Disable the receiver at the Mac
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, MAC_CR, &Value32));
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, MAC_CR, Value32 & (~MAC_CR_RXEN_)));
+
+ // The Rx Stop bit should assert as soon as the packet "in flight" makes
+ // it into the Mac, worst case is 10 Mbps HD. This will be ~2ms tops
+ // Assuming one register read per (micro)frame the case of high speed USB
+ // - 125us register read cycle time - is the worse and would need up to
+ // 16 reads. Let's just round up to 20.
+ do
+ {
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, INT_STS, &Value32));
+ // Let it try to do the 20 reads even if the reg reads are failing
+ // If the previous write did go thru at least this way we have a better
+ // chance of making sure the receiver did stop.
+ }
+ while ( (++Count<20) && ((Value32 & INT_STS_RX_STOP_) == 0) );
+
+
+
+ // Disable Mac RX
+ //CHECK_RETURN_STATUS(smsc9500_read_reg(dev, MAC_CR, &Value32));
+ //Value32 &= ~MAC_CR_RXEN_;
+ //CHECK_RETURN_STATUS(smsc9500_write_reg(dev, MAC_CR, Value32));
+
+ SMSC_TRACE(DBG_RX,"<-- smsc9500_stopRxPath\n");
+ ret = SMSC9500_SUCCESS;
+DONE:
+ return ret;
+}
+
+static int smsc9500_stopAndFlushRxPath(struct usbnet *dev)
+/*++
+
+Routine Description:
+
+ This routine Stops the Rx Path at both Mac and USB and then flushes
+ the Rx Fifo pointers.
+
+Arguments:
+
+
+Return Value:
+
+--*/
+{
+ u32 Value32;
+ int ret = SMSC9500_FAIL;
+ SMSC_TRACE(DBG_RX,"--> smsc9500_stopAndFlushRxPath\n");
+
+
+ if(smsc9500_stopRxPath(dev) < 0)goto DONE;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, RX_CFG, RX_FIFO_FLUSH_));
+
+ // Should self clear way before the read.
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, RX_CFG, &Value32));
+
+ if (Value32 & RX_FIFO_FLUSH_)
+ {
+ // Flush did not self clear!
+ goto DONE;
+ }
+
+ SMSC_TRACE(DBG_RX,"<-- smsc9500_stopAndFlushRxPath\n");
+ ret = SMSC9500_SUCCESS;
+DONE:
+ return ret;
+}
+
+static u16 CalculateCrc16(const BYTE * bpData,const u32 dwLen, const BOOLEAN fBitReverse)
+{
+ const u16 wCrc16Poly = 0x8005U; // s/b international standard for CRC-16
+ // x^16 + x^15 + x^2 + 1
+ //u16 wCrc16Poly = 0xA001; // reverse
+ u16 i, j, bit;
+ u16 wCrc = 0xFFFFU;
+ u16 wMsb;
+ BYTE bCurrentByte;
+ u16 wNumOfBits = 16U;
+ u16 wCrcOut=0;
+
+ wNumOfBits = wNumOfBits; // to avoid lint warning
+
+ for (i=0; i<(u16)dwLen; i++)
+ {
+ bCurrentByte = *bpData++;
+
+ for (bit=(u16)0U; bit<(u16)8U; bit++)
+ {
+ wMsb = wCrc >> 15;
+ wCrc <<= 1;
+
+ if (wMsb ^ (u16)(bCurrentByte & 1))
+ {
+ wCrc ^= wCrc16Poly;
+ wCrc |= (u16)0x0001U;
+ }
+ bCurrentByte >>= 1;
+ }
+ }
+ //bit reverse if needed
+ // so far we do not need this for 117
+ // but the standard CRC-16 seems to require this.
+ if (fBitReverse)
+ {
+ j = 1;
+ for (i=(u16)(1<<(wNumOfBits-(u16)1U)); i; i = i>>1) {
+ if (wCrc & i)
+ {
+ wCrcOut |= j;
+ }
+ j <<= 1;
+ }
+ wCrc = wCrcOut;
+ }
+
+ return wCrc;
+}
+
+/*++
+
+Routine Description:
+
+ This routine set/reset General Purpose Output.
+
+Arguments:
+
+ Adapter - pointer to our Adapter
+ Gpo - Gpo [0:10]
+ State - 1 = ON or 0 = OFF
+
+Return Value:
+
+--*/
+
+static int SetGpo(struct usbnet * dev, u32 Gpo, u32 State)
+{
+ int ret = SMSC9500_FAIL;
+ u32 Value32, Status;
+
+ if ((Gpo > 10) || (State > 1))
+ {
+ if (State > 1)
+ {
+ SMSC_WARNING("Gpo%d state (%d) is out of range [0:1] in NICSetGpo\n", Gpo, State);
+ }
+ goto Exit_NICSetGpo;
+ }
+
+ if (Gpo < 8)
+ {
+ if(smsc9500_read_reg( dev, GPIO_CFG, &Value32 ) < 0){
+ SMSC_WARNING("Failed to read GPIO_CFG\n");
+ goto Exit_NICSetGpo;
+ }
+
+ Value32 &= (~(GPIO_CFG_GPO0_DATA_ << Gpo));
+ Value32 |= (State << Gpo);
+
+ Status = smsc9500_write_reg(dev, GPIO_CFG, Value32);
+ if (Status < 0)
+ goto Exit_NICSetGpo;
+ }
+ else
+ {
+ Status = smsc9500_read_reg(dev, LED_GPIO_CFG, &Value32 );
+ if (Status < 0)
+ goto Exit_NICSetGpo;
+
+ Value32 &= (~(LED_GPIO_CFG_GPDATA_08_ << (Gpo-8)));
+ Value32 |= (State << (Gpo-8));
+
+ Status = smsc9500_write_reg(dev, LED_GPIO_CFG, Value32 );
+ if (Status < 0)
+ goto Exit_NICSetGpo;
+ }
+
+ ret = SMSC9500_SUCCESS;
+Exit_NICSetGpo:
+ return ret;
+}
+
+/*++
+
+Routine Description:
+
+ This routine set device at suspend3 state
+
+Arguments:
+
+ netdev - pointer to our device
+
+Return Value:
+
+--*/
+static int SetWakeupOnSuspend3(struct net_device *netdev)
+{
+ struct usbnet * dev=netdev_priv(netdev);
+ int ret = SMSC9500_FAIL;
+ u32 Value32;
+ BUG_ON(!dev);
+
+ SMSC_TRACE(DBG_PWR,"Setting Suspend3 mode\n");
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, RX_FIFO_INF, &Value32));
+
+ if((Value32 & 0xFFFF) != 0){
+ SMSC_TRACE(DBG_PWR,"Rx FIFO is not empty, abort suspend\n");
+ goto DONE;
+ }else{
+ SMSC_TRACE(DBG_PWR,"Rx FIFO is empty, continue suspend\n");
+ }
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, PM_CTRL, &Value32));
+ Value32 &= (~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_));
+ Value32 |= PM_CTL_SUS_MODE_3 | PM_CTL_RES_CLR_WKP_STS;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, PM_CTRL, Value32));
+ Value32 &= ~PM_CTL_WUPS_;
+ Value32 |= PM_CTL_WUPS_WOL_; //Clear wol status
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, PM_CTRL, Value32));
+
+ CHECK_RETURN_STATUS(smsc9500_set_feature(dev,USB_DEVICE_REMOTE_WAKEUP));
+
+ ret = SMSC9500_SUCCESS;
+
+DONE:
+ return ret;
+}
+
+static int SetWakeupEvents(struct net_device *netdev)
+{
+ WAKEUP_FILTER sFilter;
+ u32 dwValue,dwTemp,Value32;
+ u16 wValue;
+ int i=0, ret = SMSC9500_FAIL;
+ int filterMaskCnt=0, filterCmdCnt=0, filterOffsetCnt=0, filterCrcCnt=0;
+
+ struct usbnet * dev=netdev_priv(netdev);
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+ u32 opts = adapterData->WolWakeupOpts;
+
+ BYTE bBcast[BCAST_LEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+ BYTE bMcast[MCAST_LEN] = {0x01, 0x00, 0x5E};
+ BYTE bArp[ARP_LEN] = {0x08, 0x06};
+
+ BUG_ON(!netdev);
+
+ SMSC_TRACE(DBG_PWR,"In SetWakeupEvents. \n");
+
+ if(opts & (WAKE_PHY | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_MAGIC))
+ {
+ if(opts & WAKE_PHY) {
+
+ // Clear any pending Phy interrupt and enable the mask for it
+ adapterData->systemSuspendPHYEvent = PHY_INT_MASK_LINK_DOWN_;
+ if(adapterData->dwLinkSettings&LINK_AUTO_NEGOTIATE)
+ adapterData->systemSuspendPHYEvent |= PHY_INT_MASK_ANEG_COMP_;
+
+ EnablePHYWakeupInterrupt(dev, adapterData->systemSuspendPHYEvent);
+
+ // If there's currently no link we can use Suspend1
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev, PHY_BSR, &Value32));
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev, PHY_BSR, &Value32));
+ wValue=(u16)Value32;
+ if (!(wValue & PHY_BSR_LINK_STATUS_))
+ {
+ SMSC_TRACE(DBG_PWR,"Setting PHY in PD/ED Mode.\n");
+
+ //Enable the energy detect power-down mode
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_MODE_CTRL_STS,&dwValue));
+ wValue=(u16)dwValue;
+ wValue |= MODE_CTRL_STS_EDPWRDOWN_;
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev,PHY_MODE_CTRL_STS, wValue));
+
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_INT_SRC,&dwTemp)); //Read to clear
+ //Enable ENERGYON interrupt source
+
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_INT_MASK,&dwValue));
+ wValue=(u16)dwValue;
+ wValue |= PHY_INT_MASK_ENERGY_ON_;
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev,PHY_INT_MASK,wValue));
+
+ // Put Lan9500 in Suspend1
+ SMSC_TRACE(DBG_PWR,"Setting Suspend1 mode\n");
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, PM_CTRL, &Value32));
+ Value32 &= (~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_));
+ Value32 |= PM_CTL_SUS_MODE_1;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, PM_CTRL, Value32));
+ Value32 &= ~PM_CTL_WUPS_;
+ Value32 |= (PM_CTL_WUPS_ED_ | PM_CTL_ED_EN_); //Clear wol status, enable energy detection
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, PM_CTRL, Value32));
+
+ CHECK_RETURN_STATUS(smsc9500_set_feature(dev,USB_DEVICE_REMOTE_WAKEUP));
+ }
+ else
+ {
+ // Put Lan9500 in Suspend0
+ SMSC_TRACE(DBG_PWR,"Setting Suspend0 mode\n");
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, PM_CTRL, &Value32));
+ Value32 &= (~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_));
+ Value32 |=PM_CTL_SUS_MODE_0;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, PM_CTRL, Value32));
+ Value32 &= ~PM_CTL_WUPS_;
+ Value32 |= PM_CTL_WUPS_ED_; //Clear wol status
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, PM_CTRL, Value32));
+ CHECK_RETURN_STATUS(smsc9500_set_feature(dev,USB_DEVICE_REMOTE_WAKEUP));
+ }
+ }
+ else
+ {
+ // Clear any pending Phy interrupt and disable the mask for it
+ Value32 = PHY_INT_MASK_LINK_DOWN_;
+ if(adapterData->dwLinkSettings&LINK_AUTO_NEGOTIATE)
+ Value32 |= PHY_INT_MASK_ANEG_COMP_;
+ DisablePHYWakeupInterrupt(dev, Value32);
+ }
+
+
+ if(opts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST|WAKE_MAGIC))
+ {
+ if(opts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)){
+
+ int filter = 0;
+
+ memset(&sFilter,0,sizeof(sFilter));
+
+ if(opts & WAKE_BCAST) {
+ SMSC_TRACE(DBG_PWR,"Set broadicast detection\n");
+ sFilter.dwFilterMask[filter * 4] = 0x003F;
+ sFilter.dwFilterMask[filter * 4 + 1] = 0x00;
+ sFilter.dwFilterMask[filter * 4 + 2] = 0x00;
+ sFilter.dwFilterMask[filter * 4 + 3] = 0x00;
+ sFilter.dwCommad[filter/4] |= 0x05UL << ((filter % 4) * 8); //set command
+ sFilter.dwOffset[filter/4] |= 0x00 << ((filter % 4) * 8);
+ sFilter.dwCRC[filter/2] |= CalculateCrc16(bBcast, BCAST_LEN, FALSE) << ((filter % 2) * 16);
+ filter++;
+ }
+ if(opts & WAKE_MCAST) {
+ SMSC_TRACE(DBG_PWR,"Set multicast detection\n");
+ sFilter.dwFilterMask[filter * 4] = 0x0007;
+ sFilter.dwFilterMask[filter * 4 + 1] = 0x00;
+ sFilter.dwFilterMask[filter * 4 + 2] = 0x00;
+ sFilter.dwFilterMask[filter * 4 + 3] = 0x00;
+ sFilter.dwCommad[filter/4] |= 0x09UL << ((filter % 4) * 8); //set command
+ sFilter.dwOffset[filter/4] |= 0x00 << ((filter % 4) * 8);
+ sFilter.dwCRC[filter/2] |= (CalculateCrc16(bMcast, MCAST_LEN, FALSE) << ((filter % 2) * 16));
+ filter++;
+ }
+ if(opts & WAKE_ARP) {
+ SMSC_TRACE(DBG_PWR,"Set ARP detection\n");
+ sFilter.dwFilterMask[filter * 4] = 0x0003; //Check two bytes for ARP
+ sFilter.dwFilterMask[filter * 4 + 1] = 0x00;
+ sFilter.dwFilterMask[filter * 4 + 2] = 0x00;
+ sFilter.dwFilterMask[filter * 4 + 3] = 0x00;
+ sFilter.dwCommad[filter/4] |= 0x05UL << ((filter % 4) * 8); //set command
+ sFilter.dwOffset[filter/4] |= 0x0C << ((filter % 4) * 8);
+ sFilter.dwCRC[filter/2]= CalculateCrc16(bArp, ARP_LEN, FALSE) << ((filter % 2) * 16); //This is ARP type
+ filter++;
+ }
+ if(opts & WAKE_UCAST){
+ SMSC_TRACE(DBG_PWR,"Set UCAST detection\n");
+ sFilter.dwFilterMask[filter * 4] = 0x003F; //Check nothing
+ sFilter.dwFilterMask[filter * 4 + 1] = 0x00;
+ sFilter.dwFilterMask[filter * 4 + 2] = 0x00;
+ sFilter.dwFilterMask[filter * 4 + 3] = 0x00;
+ sFilter.dwCommad[filter/4] |= 0x01UL << ((filter % 4) * 8); //set command
+ sFilter.dwOffset[filter/4] |= 0x00 << ((filter % 4) * 8);
+ sFilter.dwCRC[filter/2] |= CalculateCrc16(&netdev->dev_addr[0], 6, FALSE) << ((filter % 2) * 16);
+ filter++;
+ }
+
+ if(!dev->chipDependFeatures[FEATURE_WUFF_8]){
+ filterMaskCnt = LAN9500_WUFF_NUM * 4;
+ filterCmdCnt = LAN9500_WUFF_NUM / 4;
+ filterOffsetCnt = LAN9500_WUFF_NUM / 4;
+ filterCrcCnt = LAN9500_WUFF_NUM / 2;
+ }else{
+ filterMaskCnt = LAN9500A_WUFF_NUM * 4;
+ filterCmdCnt = LAN9500A_WUFF_NUM / 4;
+ filterOffsetCnt = LAN9500A_WUFF_NUM / 4;
+ filterCrcCnt = LAN9500A_WUFF_NUM / 2;
+ }
+
+ for(i=0; i<filterMaskCnt; i++){
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev,WUFF, sFilter.dwFilterMask[i]));
+ }
+ for(i=0; i<filterCmdCnt; i++){
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev,WUFF, sFilter.dwCommad[i]));
+ }
+ for(i=0; i<filterOffsetCnt; i++){
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev,WUFF, sFilter.dwOffset[i]));
+ }
+ for(i=0; i<filterCrcCnt; i++){
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev,WUFF, sFilter.dwCRC[i]));
+ }
+
+ // Clear any pending pattern match packet status
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, WUCSR, &Value32));
+ Value32 |= WUCSR_WUFR_;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, WUCSR, Value32));
+
+ // Enable pattern match packet wake
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, WUCSR, &Value32));
+ Value32 |= WUCSR_WAKE_EN_;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, WUCSR, Value32));
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, PM_CTRL, &Value32));
+ Value32 |= PM_CTL_WOL_EN_;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, PM_CTRL, Value32));
+
+ }
+ else
+ {
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, WUCSR, &Value32));
+ Value32 &= (~WUCSR_WAKE_EN_);
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, WUCSR, Value32));
+ }
+
+ if (opts & WAKE_MAGIC)
+ {
+ SMSC_TRACE(DBG_PWR,"Setting magic packet detection\n");
+ // Clear any pending magic packet status
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, WUCSR, &Value32));
+ Value32 |= WUCSR_MPR_;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, WUCSR, Value32));
+
+ // Enable MP wake
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, WUCSR, &Value32));
+ Value32 |= WUCSR_MPEN_;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, WUCSR, Value32));
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, PM_CTRL, &Value32));
+ Value32 |= PM_CTL_WOL_EN_;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, PM_CTRL, Value32 ));
+ }
+ else
+ {
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, WUCSR, &Value32));
+ Value32 &= (~WUCSR_MPEN_);
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, WUCSR, Value32));
+ }
+
+
+ //enable recevier
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev,MAC_CR,&dwValue));
+ dwValue |= MAC_CR_RXEN_;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev,MAC_CR, dwValue));
+
+ SMSC_TRACE(DBG_PWR,"Setting Suspend0 mode\n");
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, PM_CTRL, &Value32));
+ Value32 &= (~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_));
+ Value32 |= PM_CTL_SUS_MODE_0;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, PM_CTRL, Value32));
+ Value32 &= ~PM_CTL_WUPS_;
+ Value32 |= PM_CTL_WUPS_WOL_; //Clear wol status, should not change suspned_mode while clearing WUPS_sts[1]
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, PM_CTRL, Value32));
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, PM_CTRL, &Value32));
+
+ CHECK_RETURN_STATUS(smsc9500_set_feature(dev,USB_DEVICE_REMOTE_WAKEUP));
+
+
+ }
+
+ }else {
+
+ SMSC_TRACE(DBG_PWR,"Disabling Wake events in ESS Regs. ");
+
+ // Disable Energy detect (Link up) & Wake up events to do USB wake
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, WUCSR, &Value32));
+ Value32 |=~(WUCSR_MPEN_ | WUCSR_WAKE_EN_ );
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, WUCSR, Value32));
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, PM_CTRL, &Value32));
+ Value32 &= ~(PM_CTL_ED_EN_ | PM_CTL_WOL_EN_);
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, PM_CTRL, Value32));
+
+ // Put Lan9500 in Suspend2
+ SMSC_TRACE(DBG_PWR,"Setting Suspend2 mode\n");
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, PM_CTRL, &Value32));
+ Value32 &= (~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_));
+ Value32 |= PM_CTL_SUS_MODE_2;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, PM_CTRL, Value32));
+ }
+
+ ret = 0;
+DONE:
+ return ret;
+}
+
+static int ResetWakeupEvents(struct net_device *netdev)
+{
+ u32 dwValue;
+ u16 wValue;
+ int ret = SMSC9500_FAIL;
+
+ struct usbnet * dev=netdev_priv(netdev);
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+
+ BUG_ON(!adapterData);
+ SMSC_TRACE(DBG_PWR,"In ResetWakeupEvents. \n");
+
+ if(!(adapterData->WolWakeupOpts & (WAKE_PHY | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_MAGIC)))
+ return ret;
+
+ smsc9500_clear_feature(dev,USB_DEVICE_REMOTE_WAKEUP);
+ if(adapterData->WolWakeupOpts & WAKE_PHY) {
+ //Disable the energy detect power-down mode
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_MODE_CTRL_STS,&dwValue));
+ wValue=(u16)dwValue;
+ wValue &= ~MODE_CTRL_STS_EDPWRDOWN_;
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev,PHY_MODE_CTRL_STS,wValue));
+
+ //Disable energy-detect wake-up
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev,PM_CTRL,&dwValue));
+ dwValue &= ~PM_CTL_PHY_RST_;
+ dwValue |= PM_CTL_WUPS_; //Clear wake-up status
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev,PM_CTRL, dwValue));
+
+ DisablePHYWakeupInterrupt(dev, adapterData->systemSuspendPHYEvent);
+ }else{
+
+ if(adapterData->WolWakeupOpts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)){
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev,WUCSR,&dwValue));
+ dwValue &= ~WUCSR_WAKE_EN_; //Disable Wake-up frame detection
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev,WUCSR, dwValue));
+
+ }
+ if(adapterData->WolWakeupOpts & WAKE_MAGIC){//Set Magic packet detection
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev,WUCSR,&dwValue));
+ dwValue &= ~WUCSR_MPEN_; //Disable magic frame detection
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev,WUCSR, dwValue));
+ }
+ //Disable wake-up frame interrupt
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev,PM_CTRL,&dwValue));
+ dwValue &= ~PM_CTL_WOL_EN_;
+ dwValue |= PM_CTL_WUPS_; //Clear wake-up status
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev,PM_CTRL, dwValue));
+ }
+
+ ret = 0;
+DONE:
+ return ret;
+}
+
+static int SetLinkDownWakeupEvents(struct usbnet *dev, int wakeUpMode)
+{
+ u32 dwValue;
+ u16 wValue;
+ int ret = SMSC9500_FAIL;
+
+ BUG_ON(!dev);
+
+ SMSC_TRACE(DBG_PWR,"In SetLinkDownWakeupEvents. \n");
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, PM_CTRL, &dwValue));
+ dwValue |= PM_CTL_ED_EN_;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, PM_CTRL, dwValue));
+
+ SMSC_TRACE(DBG_PWR,"Setting PHY in PD/ED Mode\n");
+
+ if(wakeUpMode == WAKEPHY_ENERGY){
+ //Enable the energy detect power-down mode
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_MODE_CTRL_STS,&dwValue));
+ wValue=(u16)dwValue;
+ wValue |= MODE_CTRL_STS_EDPWRDOWN_;
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev,PHY_MODE_CTRL_STS, wValue));
+
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_INT_SRC,&dwValue)); //Read to clear
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_INT_SRC,&dwValue)); //Read two times
+
+ //Enable interrupt source
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_INT_MASK,&dwValue));
+ wValue |= (PHY_INT_MASK_ENERGY_ON_ | PHY_INT_MASK_ANEG_COMP_);
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev,PHY_INT_MASK,wValue));
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, PM_CTRL, &dwValue));
+ dwValue &= (~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_));
+ dwValue |= PM_CTL_SUS_MODE_1;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, PM_CTRL, dwValue));
+
+ CHECK_RETURN_STATUS(smsc9500_set_feature(dev,USB_DEVICE_REMOTE_WAKEUP));
+
+ SMSC_TRACE(DBG_PWR,"Setting Suspend1 mode\n");
+
+ }else if (wakeUpMode == WAKEPHY_NEGO_COMPLETE){
+ //Disable the energy detect power-down mode
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_MODE_CTRL_STS,&dwValue));
+ dwValue &= ~MODE_CTRL_STS_EDPWRDOWN_;
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev,PHY_MODE_CTRL_STS, dwValue));
+
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_INT_SRC,&dwValue)); //Read to clear
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_INT_SRC,&dwValue)); //Read two times
+ //Enable interrupt source
+
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_INT_MASK,&dwValue));
+ dwValue |= PHY_INT_MASK_ANEG_COMP_;
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev,PHY_INT_MASK,dwValue));
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, PM_CTRL, &dwValue));
+ dwValue &= (~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_));
+ dwValue |= PM_CTL_SUS_MODE_0;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, PM_CTRL, dwValue));
+
+ CHECK_RETURN_STATUS(smsc9500_set_feature(dev,USB_DEVICE_REMOTE_WAKEUP));
+
+ SMSC_TRACE(DBG_PWR,"Setting Suspend0 mode\n");
+
+ }
+
+ ret = 0;
+DONE:
+ return ret;
+}
+
+static int ResetLinkDownWakeupEvents(struct usbnet *dev)
+{
+ u32 dwValue;
+ u16 wValue;
+ int ret = SMSC9500_FAIL;
+
+ SMSC_TRACE(DBG_PWR,"In ResetLinkDownWakeupEvents. \n");
+
+ smsc9500_clear_feature(dev,USB_DEVICE_REMOTE_WAKEUP);
+
+ //Disable the energy detect power-down mode
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_MODE_CTRL_STS,&dwValue));
+ wValue=(u16)dwValue;
+ wValue &= ~MODE_CTRL_STS_EDPWRDOWN_;
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev,PHY_MODE_CTRL_STS,wValue));
+
+ //Disable ENERGYON interrupt source
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_INT_MASK,&dwValue));
+ wValue=(u16)dwValue;
+ wValue &= ~PHY_INT_MASK_ENERGY_ON_;
+ if(dev->linkDownSuspend == WAKEPHY_ENERGY){
+ wValue &= ~(PHY_INT_MASK_ENERGY_ON_ | PHY_INT_MASK_ANEG_COMP_);
+ }else if (dev->linkDownSuspend == WAKEPHY_NEGO_COMPLETE){
+ wValue &= ~PHY_INT_MASK_ANEG_COMP_;
+ }
+
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev,PHY_INT_MASK, wValue));
+
+ //Disable energy-detect wake-up
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev,PM_CTRL,&dwValue));
+ dwValue &= ~PM_CTL_ED_EN_;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev,PM_CTRL, dwValue));
+
+ ret = 0;
+DONE:
+ return ret;
+}
+
+/*++
+
+Routine Description:
+
+ This routine enables PHY interrupt
+
+Arguments:
+ interrupt: Bit mask for PHY interrupt
+Return Value:
+
+--*/
+
+static int EnablePHYWakeupInterrupt(struct usbnet *dev, u32 interrupt)
+{
+ u32 dwValue;
+ int ret = SMSC9500_FAIL;
+
+ BUG_ON(!dev);
+
+ SMSC_TRACE(DBG_PWR,"In EnablePHYWakeupInterrupt. \n");
+
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_INT_SRC,&dwValue)); //Read to clear
+
+ //Enable interrupt source
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_INT_MASK,&dwValue));
+ dwValue |= interrupt;
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev,PHY_INT_MASK,dwValue));
+
+ if(dwValue){
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, PM_CTRL, &dwValue));
+ dwValue &= ~PM_CTL_PHY_RST_;
+ dwValue |= PM_CTL_ED_EN_;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, PM_CTRL, dwValue));
+ }
+
+ ret = SMSC9500_SUCCESS;
+DONE:
+ return ret;
+
+}
+
+/*++
+
+Routine Description:
+
+ This routine Disables linkdown interrupt in the PHY
+
+Arguments:
+
+Return Value:
+
+--*/
+
+static int DisablePHYWakeupInterrupt(struct usbnet *dev, u32 interrupt)
+{
+ u32 dwValue;
+ int ret = SMSC9500_FAIL;
+
+ BUG_ON(!dev);
+
+ SMSC_TRACE(DBG_PWR,"In DisablePHYWakeupInterrupt. \n");
+
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_INT_SRC,&dwValue)); //Read to clear
+
+ //Disable interrupt source
+ CHECK_RETURN_STATUS(smsc9500_read_phy(dev,PHY_INT_MASK,&dwValue));
+ dwValue &= PHY_INT_MASK_ALL;
+ dwValue &= ~interrupt;
+ CHECK_RETURN_STATUS(smsc9500_write_phy(dev,PHY_INT_MASK,dwValue));
+
+ if(dwValue == 0){ //All interrupt sources are disabled
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, PM_CTRL, &dwValue));
+ dwValue &= ~(PM_CTL_PHY_RST_ | PM_CTL_ED_EN_);
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, PM_CTRL, dwValue));
+ }
+
+ ret = SMSC9500_SUCCESS;
+
+DONE:
+ return ret;
+
+}
+
+/*++
+
+Routine Description:
+
+ This routine Starts the Tx path at the MAC and sets the flag to accept
+ bulk out requests
+
+--*/
+static int StartTxPath(struct usbnet * dev )
+{
+ u32 Value32;
+ int ret = SMSC9500_FAIL;
+
+ // Enable Tx at MAC
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, MAC_CR, &Value32));
+ Value32 |= MAC_CR_TXEN_;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, MAC_CR, Value32));
+
+ // Enable Tx at SCSRs
+ Value32 = TX_CFG_ON_;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, TX_CFG, Value32));
+
+ ret = 0;
+DONE:
+ return ret;
+}
+
+/*++
+
+Routine Description:
+
+ Starts the Receive path.
+
+ Note that if we are operating in USB bandwidth friendly mode we defer
+ starting the bulk in requests for now (will start when operational mode pipe signals
+ receive data available).
+
+--*/
+static int StartRxPath(struct usbnet *dev )
+{
+ u32 Value32;
+ int ret = SMSC9500_FAIL;
+
+ CHECK_RETURN_STATUS(smsc9500_read_reg(dev, MAC_CR, &Value32));
+ Value32 |= MAC_CR_RXEN_;
+ CHECK_RETURN_STATUS(smsc9500_write_reg(dev, MAC_CR, Value32));
+
+ ret = 0;
+DONE:
+ return ret;
+}
+
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,11))
+static int Smsc9500_suspend (struct usb_interface *intf, u32 state)
+#else
+static int Smsc9500_suspend (struct usb_interface *intf, pm_message_t state)
+#endif
+{
+
+ struct usbnet *dev = usb_get_intfdata(intf);
+ int ret = SMSC9500_SUCCESS;
+ u32 dwValue;
+
+ SMSC_TRACE(DBG_PWR,"---->Smsc9500_suspend\n");
+ BUG_ON(!dev);
+ BUG_ON(!dev->udev);
+
+ smsc9500_read_reg(dev,WUCSR,&dwValue);
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
+#ifdef CONFIG_PM
+ if(dev->udev->auto_pm) //Internal pm event, autosuspend
+#else
+ if(0)
+#endif //CONFIG_PM
+#else
+ if(0)
+#endif
+ {
+ ret = Smsc9500AutoSuspend(intf, state);
+ }else
+ {//It is system suspend
+ ret = Smsc9500SystemSuspend(intf, state);
+ }
+
+ SMSC_TRACE(DBG_PWR,"<----Smsc9500_suspend\n");
+
+ return ret;
+}
+
+
+static int Smsc9500AutoSuspend (struct usb_interface *intf, pm_message_t state)
+{
+
+ struct usbnet *dev = usb_get_intfdata(intf);
+ u32 Value32;
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+ int suspendFlag = dev->suspendFlag;
+ int ret = SMSC9500_SUCCESS;
+
+ SMSC_TRACE(DBG_PWR,"---->Smsc9500AutoSuspend, suspendFlag = 0x%x\n", suspendFlag);
+
+ dev->suspendFlag = 0;
+
+ if (netif_running (dev->net))
+ {
+ adapterData->wakeupOptsBackup = adapterData->WolWakeupOpts;
+
+ smsc9500_read_phy(dev, PHY_BSR, &Value32);
+ if(smsc9500_read_phy(dev, PHY_BSR, &Value32) < 0){
+ return SMSC9500_FAIL;
+ }
+
+ if (!(Value32 & PHY_BSR_LINK_STATUS_))//last minute link check
+ {//Link is down
+ if(dev->smartDetach){//Highest priority
+ if(!dev->pmLock){//Resume immediately, then detach device from USB bus.
+ smscusbnet_defer_myevent(dev, EVENT_IDLE_RESUME);
+ }
+ ret = SMSC9500_FAIL;
+ goto _SUSPEND_EXIT;
+ }else if(dev->linkDownSuspend || (suspendFlag & AUTOSUSPEND_LINKDOWN)){//Always check it to save more power
+ dev->suspendFlag |= AUTOSUSPEND_LINKDOWN;
+ ret = SetLinkDownWakeupEvents(dev, dev->linkDownSuspend);
+ }else if(suspendFlag & AUTOSUSPEND_DYNAMIC){//suspend on s0, work for lan9500 and lan9500a
+ dev->suspendFlag |= AUTOSUSPEND_DYNAMIC;
+ adapterData->WolWakeupOpts = (WAKE_UCAST | WAKE_BCAST | WAKE_MCAST | WAKE_ARP);
+ ret = SetWakeupEvents(dev->net);
+
+ //Save PHY interrupt event, so we can clear it after waking up.
+ adapterData->dynamicSuspendPHYEvent = PHY_INT_MASK_ENERGY_ON_ | PHY_INT_MASK_ANEG_COMP_;
+ ret = EnablePHYWakeupInterrupt(dev, adapterData->dynamicSuspendPHYEvent);
+
+ }else if(suspendFlag & AUTOSUSPEND_DYNAMIC_S3){//suspend on s3, only for lan9500a
+ dev->suspendFlag |= AUTOSUSPEND_DYNAMIC_S3;
+ ret = SetWakeupOnSuspend3(dev->net);
+ if(ret != SMSC9500_FAIL){
+ //Save PHY interrupt event, so we can clear it after waking up.
+ adapterData->dynamicSuspendPHYEvent = PHY_INT_MASK_ENERGY_ON_ | PHY_INT_MASK_ANEG_COMP_;
+ ret = EnablePHYWakeupInterrupt(dev, adapterData->dynamicSuspendPHYEvent);
+ }
+
+ }else{
+ SMSC_WARNING("auto suspend event is null\n");
+ ret = SMSC9500_FAIL;
+ }
+
+ }else{//link is up
+
+ if(suspendFlag & AUTOSUSPEND_DYNAMIC){//suspend on s0, work for lan9500 and lan9500a
+ dev->suspendFlag |= AUTOSUSPEND_DYNAMIC;
+ adapterData->WolWakeupOpts = (WAKE_UCAST | WAKE_BCAST | WAKE_MCAST | WAKE_ARP);
+ ret = SetWakeupEvents(dev->net);
+
+ //Save PHY interrupt event, so we can clear it after waking up.
+ adapterData->dynamicSuspendPHYEvent = PHY_INT_MASK_LINK_DOWN_;
+ ret = EnablePHYWakeupInterrupt(dev, adapterData->dynamicSuspendPHYEvent);
+ }else if(suspendFlag & AUTOSUSPEND_DYNAMIC_S3){//suspend on s3, only for lan9500a
+ dev->suspendFlag |= AUTOSUSPEND_DYNAMIC_S3;
+ ret = SetWakeupOnSuspend3(dev->net);
+ if(ret != SMSC9500_FAIL){
+ //Save PHY interrupt event, so we can clear it after waking up.
+ adapterData->dynamicSuspendPHYEvent = PHY_INT_MASK_LINK_DOWN_;
+ ret = EnablePHYWakeupInterrupt(dev, adapterData->dynamicSuspendPHYEvent);
+ }
+ }else{
+ ret = SMSC9500_FAIL;
+ }
+ }
+
+ if(ret != SMSC9500_SUCCESS){
+ //SMSC_WARNING("Failed to suspend device\n");
+ dev->suspendFlag = 0;
+ if(!dev->pmLock){//Resume immediately
+ smscusbnet_defer_myevent(dev, EVENT_IDLE_RESUME);
+ Tx_WakeQueue(dev,0x04UL);
+ tasklet_schedule (&dev->bh);
+ }
+ goto _SUSPEND_EXIT;
+ }else{
+ smscusbnet_FreeQueue(dev);
+ del_timer_sync(&dev->LinkPollingTimer);
+ }
+
+ }else{//Interface down
+ u32 Value32;
+ SMSC_TRACE(DBG_PWR,"Interface is down, set suspend2 mode\n");
+ smsc9500_read_reg(dev, PM_CTRL, &Value32);
+ Value32 &= (~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_));
+ Value32 |= PM_CTL_SUS_MODE_2;
+ smsc9500_write_reg(dev, PM_CTRL, Value32);
+
+ dev->suspendFlag |= AUTOSUSPEND_INTFDOWN;
+ }
+
+_SUSPEND_EXIT:
+ SMSC_TRACE(DBG_PWR,"<----Smsc9500AutoSuspend\n");
+ return ret;
+}
+
+static int Smsc9500SystemSuspend (struct usb_interface *intf, pm_message_t state)
+{
+
+ struct usbnet *dev = usb_get_intfdata(intf);
+
+ SMSC_TRACE(DBG_PWR,"---->Smsc9500SystemSuspend\n");
+
+ dev->idleCount = 0;
+ dev->suspendFlag = 0;
+
+ if (dev->net && netif_running (dev->net) && netif_device_present (dev->net))
+ {
+ netif_device_detach (dev->net);
+ smscusbnet_FreeQueue(dev);
+
+ dev->StopLinkPolling=TRUE;
+ del_timer_sync(&dev->LinkPollingTimer);
+
+ Tx_StopQueue(dev,0x04UL);
+ smsc9500_stopAndFlushTxPath(dev);
+ smsc9500_stopAndFlushRxPath(dev);
+
+ SetWakeupEvents(dev->net);
+
+ }else{
+ // Put Lan9500 in Suspend2
+ u32 Value32;
+ SMSC_TRACE(DBG_PWR,"Setting Suspend2 mode\n");
+ smsc9500_read_reg(dev, PM_CTRL, &Value32);
+ Value32 &= (~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_));
+ Value32 |= PM_CTL_SUS_MODE_2;
+ smsc9500_write_reg(dev, PM_CTRL, Value32);
+
+ dev->suspendFlag |= AUTOSUSPEND_INTFDOWN;
+ }
+
+ SMSC_TRACE(DBG_PWR,"<----Smsc9500SystemSuspend\n");
+
+ return SMSC9500_SUCCESS;
+}
+
+static int Smsc9500_resume(struct usb_interface *intf)
+{
+ int ret = SMSC9500_SUCCESS;
+
+ struct usbnet *dev = usb_get_intfdata(intf);
+
+ SMSC_TRACE(DBG_PWR,"--->in Smsc9500_resume\n");
+ BUG_ON(!dev);
+ BUG_ON(!dev->udev);
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
+ //autoresume
+ if(dev->suspendFlag)
+#else
+ if(0)
+#endif
+ {
+ ret = Smsc9500AutoResume(intf);
+ }else
+ {
+ ret = Smsc9500SystemResume(intf);
+ }
+ SMSC_TRACE(DBG_PWR,"------->out of in Smsc9500_resume\n");
+ return ret;
+}
+
+static int Smsc9500AutoResume(struct usb_interface *intf)
+{
+ struct usbnet *dev = usb_get_intfdata(intf);
+ PADAPTER_DATA adapterData=(PADAPTER_DATA)(dev->data[0]);
+ u32 dwValue;
+
+ SMSC_TRACE(DBG_PWR,"--->in Smsc9500AutoResume\n");
+
+ clear_bit(EVENT_DEV_RECOVERY, &dev->flags);
+
+ if (dev->suspendFlag & AUTOSUSPEND_INTFDOWN){
+ SMSC_TRACE(DBG_PWR,"Resume from interface down\n");
+ goto _EXIT_AUTORESUME;
+ }
+
+ if(!dev->pmLock){
+ smscusbnet_defer_myevent(dev, EVENT_IDLE_RESUME);
+ }
+
+ if(dev->suspendFlag & AUTOSUSPEND_LINKDOWN){
+ ResetLinkDownWakeupEvents(dev);
+ }else if(dev->suspendFlag & AUTOSUSPEND_DYNAMIC){
+ DisablePHYWakeupInterrupt(dev, adapterData->dynamicSuspendPHYEvent);
+ ResetWakeupEvents(dev->net);
+ }else if(dev->suspendFlag & AUTOSUSPEND_DYNAMIC_S3){//Resume from suspend3
+ smsc9500_clear_feature(dev,USB_DEVICE_REMOTE_WAKEUP);
+
+ DisablePHYWakeupInterrupt(dev, adapterData->dynamicSuspendPHYEvent);
+
+ if(smsc9500_read_reg(dev, PM_CTRL, &dwValue) < 0){
+ SMSC_WARNING("Failed to read PM_CTRL");
+ }
+ dwValue &= (~(PM_CTL_SUS_MODE_ | PM_CTL_PHY_RST_));
+ dwValue |= PM_CTL_SUS_MODE_2;
+
+ if(smsc9500_write_reg(dev, PM_CTRL, dwValue) < 0){
+ SMSC_WARNING("Failed to write PM_CTRL");
+ }
+ dwValue &= ~PM_CTL_WUPS_;
+ dwValue |= PM_CTL_WUPS_WOL_;
+
+ if(smsc9500_write_reg(dev, PM_CTRL, dwValue) < 0){ //Should not change suspend_mode while clearing WUPS_sts[1]
+ SMSC_WARNING("Failed to write PM_CTRL");
+ }
+
+ }
+ //if(dev->delay.function)(dev->delay.function)((unsigned long)dev);
+ tasklet_schedule (&dev->bh);
+
+ adapterData->WolWakeupOpts = adapterData->wakeupOptsBackup;
+ adapterData->wakeupOptsBackup = 0;
+
+ Tx_WakeQueue(dev,0x04UL);
+
+_EXIT_AUTORESUME:
+ dev->idleCount = 0;
+ dev->suspendFlag = 0;
+
+ SMSC_TRACE(DBG_PWR,"------->out of in Smsc9500AutoResume\n");
+ return SMSC9500_SUCCESS;
+}
+
+static int Smsc9500SystemResume(struct usb_interface *intf)
+{
+ int ret=0;
+#if 0 /* commenting as it was causing panic on harmony platform */
+ u32 dwValue;
+#endif
+
+ struct usbnet *dev = usb_get_intfdata(intf);
+
+ SMSC_TRACE(DBG_PWR,"--->in Smsc9500SystemResume\n");
+
+ if(netif_running (dev->net) && !netif_device_present (dev->net)){
+ netif_device_attach (dev->net);
+ }
+
+#if 0 /* commenting as it was causing panic on harmony platform */
+ //test if hcd is still alive
+
+ if(smsc9500_read_reg_async(dev, MAC_CR, &dwValue, TRUE) == ASYNC_RW_SUCCESS){
+ SMSC_TRACE(DBG_PWR,"hcd is alive\n");
+#endif
+ ret=smsc9500_reset(dev);
+ StartTxPath(dev);
+ StartRxPath(dev);
+
+ ResetWakeupEvents(dev->net);
+
+#if 0 /* commenting as it was causing panic on harmony platform */
+ }else{//This will happen on suspend-to-disk, if we access usb bus, will hang on usb_kill_urb
+ SMSC_TRACE(DBG_PWR,"no hcd\n");
+ }
+#endif
+
+ Tx_WakeQueue(dev,0x04UL);
+
+ init_timer(&(dev->LinkPollingTimer));
+ dev->StopLinkPolling=FALSE;
+ dev->LinkPollingTimer.function=smscusbnet_linkpolling;
+ dev->LinkPollingTimer.data=(unsigned long) dev;
+ dev->LinkPollingTimer.expires=jiffies+HZ;
+ add_timer(&(dev->LinkPollingTimer));
+ tasklet_schedule (&dev->bh);
+
+ dev->idleCount = 0;
+
+ SMSC_TRACE(DBG_PWR,"------->out of in Smsc9500SystemResume\n");
+ return SMSC9500_SUCCESS;
+}
+
+static int smsc9500_device_recovery(struct usbnet *dev)
+{
+ u32 dwReadBuf;
+
+ BUG_ON(!dev);
+
+ if (dev->net && netif_device_present (dev->net))
+ {
+
+ SMSC_WARNING("Device recovery is in progress\n");
+
+ if (smsc9500_read_reg(dev,INT_STS,&dwReadBuf)< 0)return SMSC9500_FAIL;
+
+ smscusbnet_FreeQueue(dev);
+
+ dev->StopLinkPolling=TRUE;
+ del_timer_sync(&dev->LinkPollingTimer);
+
+ Tx_StopQueue(dev,0x04UL);
+
+ if(smsc9500_stopAndFlushTxPath(dev) < 0)return SMSC9500_FAIL;
+ if(smsc9500_stopAndFlushRxPath(dev) < 0)return SMSC9500_FAIL;
+
+ if(dwReadBuf & INT_STS_TXE_){// reset only when TXE occurs
+ if(smsc9500_reset(dev) < 0)return SMSC9500_FAIL;
+ }
+
+ if(StartTxPath(dev) < 0)return SMSC9500_FAIL;
+ if(StartRxPath(dev) < 0)return SMSC9500_FAIL;
+
+ Tx_WakeQueue(dev,0x04UL);
+
+ init_timer(&(dev->LinkPollingTimer));
+ dev->StopLinkPolling=FALSE;
+ dev->LinkPollingTimer.function=smscusbnet_linkpolling;
+ dev->LinkPollingTimer.data=(unsigned long) dev;
+ dev->LinkPollingTimer.expires=jiffies+HZ;
+ add_timer(&(dev->LinkPollingTimer));
+
+ tasklet_schedule (&dev->bh);
+
+ SMSC_WARNING("Device recovery is done\n");
+ }
+
+ return SMSC9500_SUCCESS;
+}
+
+static const struct driver_info smsc9500_info = {
+ .description = "smsc9500 USB 2.0 Ethernet",
+ .bind = smsc9500_bind,
+ .unbind=smsc9500_unbind,
+ .status = smsc9500_status,
+ .link_reset = smsc9500_link_reset,
+ .reset = smsc9500_reset,
+ .flags = FLAG_ETHER,
+ .rx_fixup = smsc9500_rx_fixup,
+ .tx_fixup = smsc9500_tx_fixup,
+ .rx_setmulticastlist=smsc9500_rx_setmulticastlist,
+};
+
+static const struct usb_device_id products [] = {
+ {
+ // SMSC9500 USB Ethernet Device
+ USB_DEVICE (0x0424, PID_LAN9500),
+ .driver_info = (unsigned long) &smsc9500_info,
+ },
+ {
+ // SMSC9512/9514 USB hub with Ethernet Device
+ USB_DEVICE (0x0424, PID_LAN9512),
+ .driver_info = (unsigned long) &smsc9500_info,
+ },
+ {
+ // SMSC9500A USB Ethernet Device
+ USB_DEVICE (0x0424, PID_LAN9500A),
+ .driver_info = (unsigned long) &smsc9500_info,
+ },
+ { }, // END
+};
+
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver smsc9500_driver = {
+ .name = "smsc9500",
+ .id_table = products,
+ .probe = smscusbnet_probe,
+ .suspend = Smsc9500_suspend,
+ .resume = Smsc9500_resume,
+ .disconnect = smscusbnet_disconnect,
+ .reset_resume = Smsc9500SystemResume,
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
+ .supports_autosuspend = 1,
+#endif
+};
+
+static int __init smsc9500_init(void)
+{
+
+ return usb_register(&smsc9500_driver);
+}
+module_init(smsc9500_init);
+
+static void __exit smsc9500_exit(void)
+{
+ usb_deregister(&smsc9500_driver);
+}
+module_exit(smsc9500_exit);
+
+MODULE_AUTHOR("Nancy Lin and Sean(Xiang) Chen");
+MODULE_DESCRIPTION("SMSC9500 USB 2.0 Ethernet Devices");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/net/usb/smsc9500.h b/drivers/net/usb/smsc9500.h
new file mode 100644
index 000000000000..556744ab1f47
--- /dev/null
+++ b/drivers/net/usb/smsc9500.h
@@ -0,0 +1,775 @@
+/***************************************************************************
+ *
+ * Copyright (C) 2007-2010 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ ***************************************************************************
+ * File: smsc9500.h
+ ***************************************************************************/
+
+
+#ifndef _LAN9500_H
+#define _LAN9500_H
+
+typedef unsigned char BYTE;
+
+#define ON TRUE
+#define OFF FALSE
+#define STATUS_WORD_LEN 4
+
+#define RX_OFFSET
+#define LAN_REGISTER_RANGE 0xFF
+#define MAC_REGISTER_RANGE_MIN 0x100
+#define MAC_REGISTER_RANGE_MAX 0x130
+#define MAX_EEPROM_SIZE 512
+
+//product id definition
+#define PID_LAN9500 0x9500
+#define PID_LAN9500A 0x9E00
+#define PID_LAN9512 0xEC00
+
+
+// Tx MAT Cmds
+#define TX_CMD_A_DATA_OFFSET_ (0x001F0000UL)
+#define TX_CMD_A_FIRST_SEG_ (0x00002000UL)
+#define TX_CMD_A_LAST_SEG_ (0x00001000UL)
+#define TX_CMD_A_BUF_SIZE_ (0x000007FFUL)
+
+#define TX_CMD_B_CSUM_ENABLE (0x00004000UL)
+#define TX_CMD_B_ADD_CRC_DISABLE_ (0x00002000UL)
+#define TX_CMD_B_DISABLE_PADDING_ (0x00001000UL)
+#define TX_CMD_B_PKT_BYTE_LENGTH_ (0x000007FFUL)
+
+// Rx sts Wd
+#define RX_STS_FF_ 0x40000000UL // Filter Fail
+#define RX_STS_FL_ 0x3FFF0000UL // Frame Length
+#define RX_STS_DL_SHIFT_ 16UL
+#define RX_STS_ES_ 0x00008000UL // Error Summary
+#define RX_STS_BF_ 0x00002000UL // Broadcast Frame
+#define RX_STS_LE_ 0x00001000UL // Length Error
+#define RX_STS_RF_ 0x00000800UL // Runt Frame
+#define RX_STS_MF_ 0x00000400UL // Multicast Frame
+#define RX_STS_TL_ 0x00000080UL // Frame too long
+#define RX_STS_CS_ 0x00000040UL // Collision Seen
+#define RX_STS_FT_ 0x00000020UL // Frame Type
+#define RX_STS_RW_ 0x00000010UL // Receive Watchdog
+#define RX_STS_ME_ 0x00000008UL // Mii Error
+#define RX_STS_DB_ 0x00000004UL // Dribbling
+#define RX_STS_CRC_ 0x00000002UL // CRC Error
+
+#define RX_STS_ERR_MASK_ 0x000088DEUL
+
+
+// SCSRs
+#define ID_REV (0x00UL)
+ #define ID_REV_CHIP_ID_MASK_ (0xffff0000UL)
+ #define ID_REV_CHIP_REV_MASK_ (0x0000ffffUL)
+ #define ID_REV_CHIP_ID_9500_ (0x9500UL)
+ #define GetChipIdFromID_REV(dwReg) ((dwReg & ID_REV_CHIP_ID_MASK_) >> 16)
+ #define GetChiprevFromID_REV(dwReg) (dwReg & ID_REV_CHIP_REV_MASK_)
+
+#define FPGA_REV (0x04UL)
+
+#define INT_STS (0x08UL)
+ #define INT_STS_TX_STOP_ (0x00020000UL)
+ #define INT_STS_RX_STOP_ (0x00010000UL)
+ #define INT_STS_PHY_INT_ (0x00008000UL)
+ #define INT_STS_TXE_ (0x00004000UL)
+ #define INT_STS_TDFU_ (0x00002000UL)
+ #define INT_STS_TDFO_ (0x00001000UL)
+ #define INT_STS_RXDF_ (0x00000800UL)
+ #define INT_STS_GPIOS_ (0x000007FFUL)
+
+#define RX_CFG (0x0CUL)
+ #define RX_FIFO_FLUSH_ (0x00000001UL)
+
+#define TX_CFG (0x10UL)
+ #define TX_CFG_ON_ (0x00000004UL)
+ #define TX_CFG_STOP_ (0x00000002UL)
+ #define TX_CFG_FIFO_FLUSH_ (0x00000001UL)
+
+#define HW_CFG (0x14UL)
+ #define HW_CFG_SMDET_STS (0x00040000UL)
+ #define HW_CFG_SMDET_EN (0x00020000UL)
+ #define HW_CFG_EEM (0x00010000UL)
+ #define HW_CFG_RST_PROTECT (0x00008000UL)
+ #define HW_CFG_PHY_BOOST (0x00006000UL)
+ #define HW_CFG_PHY_BOOST_NORMAL (0x00000000UL)
+ #define HW_CFG_PHY_BOOST_4 (0x00002000UL)
+ #define HW_CFG_PHY_BOOST_8 (0x00004000UL)
+ #define HW_CFG_PHY_BOOST_12 (0x00006000UL)
+ #define HW_CFG_BIR_ (0x00001000UL)
+ #define HW_CFG_LEDB_ (0x00000800UL)
+ #define HW_CFG_RXDOFF_ (0x00000600UL)
+ #define HW_CFG_RXDOFF_2_ (0x00000400UL)
+ #define HW_CFG_SBP_ (0x00000100UL)
+ #define HW_CFG_IME_ (0x00000080UL)
+ #define HW_CFG_DRP_ (0x00000040UL)
+ #define HW_CFG_MEF_ (0x00000020UL)
+ #define HW_CFG_ETC_ (0x00000010UL)
+ #define HW_CFG_LRST_ (0x00000008UL)
+ #define HW_CFG_PSEL_ (0x00000004UL)
+ #define HW_CFG_BCE_ (0x00000002UL)
+ #define HW_CFG_SRST_ (0x00000001UL)
+
+#define RX_FIFO_INF (0x18UL)
+
+#define TX_FIFO_INF (0x1CUL)
+
+#define PM_CTRL (0x20UL)
+ #define PM_CTL_RES_CLR_WKP_STS (0x00000200UL)
+ #define PM_CTL_RES_CLR_WKP_EN (0x00000100UL)
+ #define PM_CTL_DEV_RDY_ (0x00000080UL)
+ #define PM_CTL_SUS_MODE_ (0x00000060UL)
+ #define PM_CTL_SUS_MODE_0 (0x00000000UL)
+ #define PM_CTL_SUS_MODE_1 (0x00000020UL)
+ #define PM_CTL_SUS_MODE_2 (0x00000040UL)
+ #define PM_CTL_SUS_MODE_3 (0x00000060UL)
+ #define PM_CTL_PHY_RST_ (0x00000010UL)
+ #define PM_CTL_WOL_EN_ (0x00000008UL)
+ #define PM_CTL_ED_EN_ (0x00000004UL)
+ #define PM_CTL_WUPS_ (0x00000003UL)
+ #define PM_CTL_WUPS_NO_ (0x00000000UL)
+ #define PM_CTL_WUPS_ED_ (0x00000001UL)
+ #define PM_CTL_WUPS_WOL_ (0x00000002UL)
+ #define PM_CTL_WUPS_MULTI_ (0x00000003UL)
+
+#define LED_GPIO_CFG (0x24UL)
+ #define LED_GPIO_CFG_LED_SEL_ (0x80000000UL)
+ #define LED_GPIO_CFG_GPCTL_10_ (0x03000000UL)
+ #define LED_GPIO_CFG_GPCTL_10_SH 24
+ #define LED_GPIO_CFG_GPCTL_09_ (0x00300000UL)
+ #define LED_GPIO_CFG_GPCTL_09_SH 20
+ #define LED_GPIO_CFG_GPCTL_08_ (0x00030000UL)
+ #define LED_GPIO_CFG_GPCTL_08_SH 16
+
+ #define LED_GPIO_CFG_GPCTL_DIAG2_ (0x3UL)
+ #define LED_GPIO_CFG_GPCTL_DIAG1_ (0x2UL)
+ #define LED_GPIO_CFG_GPCTL_LED_ (0x1UL)
+ #define LED_GPIO_CFG_GPCTL_GPIO_ (0x0UL)
+
+ #define LED_GPIO_CFG_GPBUF_ (0x00000700UL)
+ #define LED_GPIO_CFG_GPBUF_10_ (0x00000400UL)
+ #define LED_GPIO_CFG_GPBUF_09_ (0x00000200UL)
+ #define LED_GPIO_CFG_GPBUF_08_ (0x00000100UL)
+ #define LED_GPIO_CFG_GPDIR_ (0x00000070UL)
+ #define LED_GPIO_CFG_GPDIR_10_ (0x00000040UL)
+ #define LED_GPIO_CFG_GPDIR_09_ (0x00000020UL)
+ #define LED_GPIO_CFG_GPDIR_08_ (0x00000010UL)
+ #define LED_GPIO_CFG_GPDATA_ (0x00000007UL)
+ #define LED_GPIO_CFG_GPDATA_10_ (0x00000004UL)
+ #define LED_GPIO_CFG_GPDATA_09_ (0x00000002UL)
+ #define LED_GPIO_CFG_GPDATA_08_ (0x00000001UL)
+
+#define GPIO_CFG (0x28UL)
+#define GPIO_CFG_GPEN_ (0xFF000000UL)
+ #define GPIO_CFG_GPO0_EN_ (0x01000000UL)
+#define GPIO_CFG_GPTYPE (0x00FF0000UL)
+ #define GPIO_CFG_GPO0_TYPE (0x00010000UL)
+#define GPIO_CFG_GPDIR_ (0x0000FF00UL)
+ #define GPIO_CFG_GPO0_DIR_ (0x00000100UL)
+#define GPIO_CFG_GPDATA_ (0x000000FFUL)
+ #define GPIO_CFG_GPO0_DATA_ (0x00000001UL)
+
+#define AFC_CFG (0x2CUL)
+ #define AFC_CFG_DEFAULT (0x00f830A1UL) // Hi watermark = 15.5Kb (~10 mtu pkts)
+ // low watermark = 3k (~2 mtu pkts)
+ // backpressure duration = ~ 350us
+ // Apply FC on any frame.
+
+#define E2P_CMD (0x30UL)
+ #define E2P_CMD_BUSY_ 0x80000000UL
+ #define E2P_CMD_MASK_ 0x70000000UL
+ #define E2P_CMD_READ_ 0x00000000UL
+ #define E2P_CMD_EWDS_ 0x10000000UL
+ #define E2P_CMD_EWEN_ 0x20000000UL
+ #define E2P_CMD_WRITE_ 0x30000000UL
+ #define E2P_CMD_WRAL_ 0x40000000UL
+ #define E2P_CMD_ERASE_ 0x50000000UL
+ #define E2P_CMD_ERAL_ 0x60000000UL
+ #define E2P_CMD_RELOAD_ 0x70000000UL
+ #define E2P_CMD_TIMEOUT_ 0x00000400UL
+ #define E2P_CMD_LOADED_ 0x00000200UL
+ #define E2P_CMD_ADDR_ 0x000001FFUL
+ #define MAX_EEPROM_SIZE 512
+#define E2P_DATA (0x34UL)
+ #define E2P_DATA_MASK_ 0x000000FFUL
+
+#define BURST_CAP (0x38UL)
+#define STRAP_DBG (0x3CUL)
+#define DP_SEL (0x40UL)
+ #define DP_SEL_DPRDY (0x80000000UL)
+ #define DP_SEL_RSEL (0x00000006UL)
+ #define DP_SEL_RSEL_FCT (0x00000000UL)
+ #define DP_SEL_RSEL_EEPROM (0x00000002UL)
+ #define DP_SEL_RSEL_TXTLI (0x00000004UL)
+ #define DP_SEL_RSEL_RXTLI (0x00000006UL)
+ #define DP_SEL_TESTEN (0x00000001UL)
+#define DP_CMD (0x44UL)
+ #define DP_CMD_READ (0x0)
+ #define DP_CMD_WRITE (0x1)
+#define DP_ADDR (0x48UL)
+#define DP_DATA0 (0x4CUL)
+#define DP_DATA1 (0x50UL)
+#define RAM_BIST0 (0x54UL)
+#define RAM_BIST1 (0x58UL)
+#define RAM_BIST2 (0x5CUL)
+#define RAM_BIST3 (0x60UL)
+#define GPIO_WAKE (0x64UL)
+#define INT_EP_CTL (0x68UL)
+#define BULK_IN_DLY (0x6CUL)
+
+#define INT_EP_CTL_INTEP_ (0x80000000UL)
+#define INT_EP_CTL_MACRTO_ (0x00080000UL)
+#define INT_EP_CTL_RX_FIFO_EN_ (0x00040000UL)
+#define INT_EP_CTL_TX_STOP_ (0x00020000UL)
+#define INT_EP_CTL_RX_STOP_ (0x00010000UL)
+#define INT_EP_CTL_PHY_INT_ (0x00008000UL)
+#define INT_EP_CTL_TXE_ (0x00004000UL)
+#define INT_EP_CTL_TDFU_ (0x00002000UL)
+#define INT_EP_CTL_TDFO_ (0x00001000UL)
+#define INT_EP_CTL_RXDF_ (0x00000800UL)
+#define INT_EP_CTL_GPIOS_ (0x000007FFUL)
+
+// MAC CSRs
+#define MAC_CR (0x100UL)
+ #define MAC_CR_RXALL_ 0x80000000UL
+ #define MAC_CR_RCVOWN_ 0x00800000UL
+ #define MAC_CR_LOOPBK_ 0x00200000UL
+ #define MAC_CR_FDPX_ 0x00100000UL
+ #define MAC_CR_MCPAS_ 0x00080000UL
+ #define MAC_CR_PRMS_ 0x00040000UL
+ #define MAC_CR_INVFILT_ 0x00020000UL
+ #define MAC_CR_PASSBAD_ 0x00010000UL
+ #define MAC_CR_HFILT_ 0x00008000UL
+ #define MAC_CR_HPFILT_ 0x00002000UL
+ #define MAC_CR_LCOLL_ 0x00001000UL
+ #define MAC_CR_BCAST_ 0x00000800UL
+ #define MAC_CR_DISRTY_ 0x00000400UL
+ #define MAC_CR_PADSTR_ 0x00000100UL
+ #define MAC_CR_BOLMT_MASK 0x000000C0UL
+ #define MAC_CR_DFCHK_ 0x00000020UL
+ #define MAC_CR_TXEN_ 0x00000008UL
+ #define MAC_CR_RXEN_ 0x00000004UL
+#define ADDRH (0x104UL)
+#define ADDRL (0x108UL)
+#define HASHH (0x10CUL)
+#define HASHL (0x110UL)
+
+#define MII_ADDR (0x114UL)
+ #define MII_WRITE_ (0x02UL)
+ #define MII_BUSY_ (0x01UL)
+ #define MII_READ_ (0x00UL) // ~of MII Write bit
+
+ #define MII_DATA (0x118UL)
+ #define FLOW (0x11CUL)
+ #define FLOW_FCPT_ (0xFFFF0000UL)
+ #define FLOW_FCPASS_ (0x00000004UL)
+ #define FLOW_FCEN_ (0x00000002UL)
+ #define FLOW_FCBSY_ (0x00000001UL)
+
+#define VLAN1 (0x120UL)
+#define VLAN2 (0x124UL)
+#define WUFF (0x128UL)
+#define WUCSR (0x12CUL)
+ #define WUCSR_GUE_ (0x00000200UL)
+ #define WUCSR_WUFR_ (0x00000040UL)
+ #define WUCSR_MPR_ (0x00000020UL)
+ #define WUCSR_WAKE_EN_ (0x00000004UL)
+ #define WUCSR_MPEN_ (0x00000002UL)
+
+#define COE_CR (0x130UL)
+ #define Tx_COE_EN_ (0x00010000UL)
+ #define Rx_COE_MODE_ (0x00000002UL)
+ #define Rx_COE_EN_ (0x00000001UL)
+
+////////////////////////////////////
+// PHY Definitions
+////////////////////////////////////
+#define LAN_PHY_ADDR (1UL)
+
+#define PHY_BCR ((u32)0U)
+#define PHY_BCR_RESET_ ((u16)0x8000U)
+#define PHY_BCR_LOOPBACK_ ((u16)0x4000U)
+#define PHY_BCR_SPEED_SELECT_ ((u16)0x2000U)
+#define PHY_BCR_AUTO_NEG_ENABLE_ ((u16)0x1000U)
+#define PHY_BCR_POWER_DOWN ((u16)0x0800U)
+#define PHY_BCR_ISOLATE ((u16)0x0400U)
+#define PHY_BCR_RESTART_AUTO_NEG_ ((u16)0x0200U)
+#define PHY_BCR_DUPLEX_MODE_ ((u16)0x0100U)
+
+#define PHY_BSR ((u32)1U)
+ #define PHY_BSR_AUTO_NEG_COMP_ ((u16)0x0020U)
+ #define PHY_BSR_REMOTE_FAULT_ ((u16)0x0010U)
+ #define PHY_BSR_LINK_STATUS_ ((u16)0x0004U)
+
+#define PHY_ID_1 ((u32)2U)
+#define PHY_ID_2 ((u32)3U)
+
+#define PHY_ANEG_ADV ((u32)4U)
+
+#define PHY_ANEG_ADV_PAUSE_ ((u16)0x0C00)
+#define PHY_ANEG_ADV_ASYMP_ ((u16)0x0800)
+#define PHY_ANEG_ADV_SYMP_ ((u16)0x0400)
+#define PHY_ANEG_ADV_SPEED_ ((u16)0x1E0)
+#define PHY_ANEG_ADV_100F_ ((u16)0x100)
+#define PHY_ANEG_ADV_100H_ ((u16)0x80)
+#define PHY_ANEG_ADV_10F_ ((u16)0x40)
+#define PHY_ANEG_ADV_10H_ ((u16)0x20)
+
+
+#define PHY_ANEG_LPA ((u32)5U)
+#define PHY_ANEG_LPA_ASYMP_ ((u16)0x0800)
+#define PHY_ANEG_LPA_SYMP_ ((u16)0x0400)
+#define PHY_ANEG_LPA_T4_ ((u16)0x0200)
+#define PHY_ANEG_LPA_100FDX_ ((u16)0x0100)
+#define PHY_ANEG_LPA_100HDX_ ((u16)0x0080)
+#define PHY_ANEG_LPA_10FDX_ ((u16)0x0040)
+#define PHY_ANEG_LPA_10HDX_ ((u16)0x0020)
+
+#define PHY_ANEG_REG ((u32)6U) /* Auto-Negotiation Expansion Reg */
+#define PHY_LP_AN_ABLE ((u16)0x0001)
+
+#define PHY_SILICON_REV ((u32)16U)
+
+
+#define PHY_MODE_CTRL_STS ((u32)17) // Mode Control/Status Register
+ #define MODE_CTRL_STS_FASTRIP_ ((u16)0x4000U)
+ #define MODE_CTRL_STS_EDPWRDOWN_ ((u16)0x2000U)
+ #define MODE_CTRL_STS_LOWSQEN_ ((u16)0x0800U)
+ #define MODE_CTRL_STS_MDPREBP_ ((u16)0x0400U)
+ #define MODE_CTRL_STS_FARLOOPBACK_ ((u16)0x0200U)
+ #define MODE_CTRL_STS_FASTEST_ ((u16)0x0100U)
+ #define MODE_CTRL_STS_REFCLKEN_ ((u16)0x0010U)
+ #define MODE_CTRL_STS_PHYADBP_ ((u16)0x0008U)
+ #define MODE_CTRL_STS_FORCE_G_LINK_ ((u16)0x0004U)
+ #define MODE_CTRL_STS_ENERGYON_ ((u16)0x0002U)
+
+#define PHY_SPECIAL_MODES ((u32)18) // Special Modes
+
+#define PHY_TSTCNTL ((u32)20) // Testability/Configuration Control
+#define PHY_TSTREAD1 ((u32)21) // Testability data Read for LSB
+#define PHY_TSTREAD2 ((u32)22) // Testability data Read for MSB
+#define PHY_TSTWRITE ((u32)23) // Testability/Configuration data Write
+
+#define PHY_SPECIAL_CTRL_STS ((u32)27)
+ #define SPECIAL_CTRL_STS_OVRRD_AMDIX_ ((u16)0x8000U)
+ #define SPECIAL_CTRL_STS_AMDIX_ENABLE_ ((u16)0x4000U)
+ #define SPECIAL_CTRL_STS_AMDIX_STATE_ ((u16)0x2000U)
+
+#define PHY_SITC ((u32)28) //Special internal testability controls
+
+#define PHY_INT_SRC ((u32)29)
+#define PHY_INT_SRC_ENERGY_ON_ ((u16)0x0080U)
+#define PHY_INT_SRC_ANEG_COMP_ ((u16)0x0040U)
+#define PHY_INT_SRC_REMOTE_FAULT_ ((u16)0x0020U)
+#define PHY_INT_SRC_LINK_DOWN_ ((u16)0x0010U)
+
+#define PHY_INT_MASK ((u32)30)
+#define PHY_INT_MASK_ALL ((u16)0x00FFU)
+#define PHY_INT_MASK_ENERGY_ON_ ((u16)0x0080U)
+#define PHY_INT_MASK_ANEG_COMP_ ((u16)0x0040U)
+#define PHY_INT_MASK_REMOTE_FAULT_ ((u16)0x0020U)
+#define PHY_INT_MASK_LINK_DOWN_ ((u16)0x0010U)
+
+#define PHY_SPECIAL ((u32)31)
+#define PHY_SPECIAL_SPD_ ((u16)0x001CU)
+#define PHY_SPECIAL_SPD_10HALF_ ((u16)0x0004U)
+#define PHY_SPECIAL_SPD_10FULL_ ((u16)0x0014U)
+#define PHY_SPECIAL_SPD_100HALF_ ((u16)0x0008U)
+#define PHY_SPECIAL_SPD_100FULL_ ((u16)0x0018U)
+
+#define AMDIX_DISABLE_STRAIGHT ((u16)0x0U)
+#define AMDIX_DISABLE_CROSSOVER ((u16)0x01U)
+#define AMDIX_ENABLE ((u16)0x02U)
+
+
+
+#define PHY_LAN83C180_INT_ACK_REG 0x15
+#define PHY_LAN83C180_OPTIONS 0x18
+
+#define PHY_LAN83C183_STATUS_OUTPUT 0x12
+#define PHY_LAN83C183_INT_MASK 0x13
+#define PHY_LAN83C183_MASK_BASE 0xFFC0
+#define PHY_LAN83C183_MASK_INT ~0x8000
+#define PHY_LAN83C183_MASK_LINK ~0x4000
+
+#define PHY_FOX_INT_SOURCE 0x1D
+#define PHY_FOX_INT_ENERGY_ON BIT_7
+#define PHY_FOX_INT_NWAY_DONE BIT_6
+#define PHY_FOX_INT_REMOTE_FAULT BIT_5
+#define PHY_FOX_INT_LINK_DOWN BIT_4
+#define PHY_FOX_INT_NWAY_LP_ACK BIT_3
+#define PHY_FOX_INT_PARALLEL_DET BIT_2
+#define PHY_FOX_INT_NWAY_PAGE_RX BIT_1
+#define PHY_FOX_INT_RESERVED BIT_0
+
+#define PHY_FOX_INT_MASK 0x1E
+
+#define PHY_QSI_INT_SOURCE_REG 0x1D /* Interrupt Source Register for QSI */
+#define PHY_QSI_AN_COMPLETE_INT BIT_6
+#define PHY_QSI_REM_FAULT_INT BIT_5
+#define PHY_QSI_LINK_DOWN_INT BIT_4
+#define PHY_QSI_AN_LP_ACK_INT BIT_3
+#define PHY_QSI_PAR_DET_INT BIT_2
+#define PHY_QSI_AN_PAGE_RCVD_INT BIT_1
+#define PHY_QSI_RCV_ERR_CNT_INT BIT_0
+
+#define PHY_QSI_INT_MASK_REG 0x1E /* Interrupt Mask Register for QSI */
+#define PHY_QSI_INT_MODE BIT_15
+
+/******************************************************************************/
+/* Bit Mask definitions */
+/******************************************************************************/
+#define BIT_NONE 0x00000000
+#define BIT_0 0x0001
+#define BIT_1 0x0002
+#define BIT_2 0x0004
+#define BIT_3 0x0008
+#define BIT_4 0x0010
+#define BIT_5 0x0020
+#define BIT_6 0x0040
+#define BIT_7 0x0080
+#define BIT_8 0x0100
+#define BIT_9 0x0200
+#define BIT_10 0x0400
+#define BIT_11 0x0800
+#define BIT_12 0x1000
+#define BIT_13 0x2000
+#define BIT_14 0x4000
+#define BIT_15 0x8000
+#define BIT_16 0x10000
+#define BIT_17 0x20000
+#define BIT_18 0x40000
+#define BIT_19 0x80000
+#define BIT_20 0x100000
+#define BIT_21 0x200000
+#define BIT_22 0x400000
+#define BIT_23 0x800000
+#define BIT_24 0x1000000
+#define BIT_25 0x2000000
+#define BIT_26 0x4000000
+#define BIT_27 0x8000000
+#define BIT_28 0x10000000
+#define BIT_29 0x20000000
+#define BIT_30 0x40000000
+#define BIT_31 0x80000000
+
+// Vendor Requests
+#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0
+#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1
+#define USB_VENDOR_REQUEST_GET_STATS 0xA2
+
+// Stats block structures
+typedef struct _SMSC9500_RX_STATS {
+ u32 RxGoodFrames;
+ u32 RxCrcErrors;
+ u32 RxRuntFrameErrors;
+ u32 RxAlignmentErrors;
+ u32 RxFrameTooLongError;
+ u32 RxLaterCollisionError;
+ u32 RxBadFrames;
+ u32 RxFifoDroppedFrames;
+}SMSC9500_RX_STATS, *PSMSC9500_RX_STATS;
+
+typedef struct _SMSC9500_TX_STATS {
+ u32 TxGoodFrames;
+ u32 TxPauseFrames;
+ u32 TxSingleCollisions;
+ u32 TxMultipleCollisions;
+ u32 TxExcessiveCollisionErrors;
+ u32 TxLateCollisionErrors;
+ u32 TxBufferUnderrunErrors;
+ u32 TxExcessiveDeferralErrors;
+ u32 TxCarrierErrors;
+ u32 TxBadFrames;
+} SMSC9500_TX_STATS, *PSMSC9500_TX_STATS;
+
+// Interrupt Endpoint status word bitfields
+#define INT_ENP_RFHF_ (0x00040000UL)
+#define INT_ENP_TX_STOP_ ETH_INT_STS_TX_STOP_
+#define INT_ENP_RX_STOP_ ETH_INT_STS_RX_STOP_
+#define INT_ENP_PHY_INT_ ETH_INT_STS_PHY_INT_
+#define INT_ENP_TXE_ ETH_INT_STS_TXE_
+#define INT_ENP_TDFU_ ETH_INT_STS_TDFU_
+#define INT_ENP_TDFO_ ETH_INT_STS_TDFO_
+#define INT_ENP_RXDF_ ETH_INT_STS_RXDF_
+#define INT_ENP_GPIOS_ ETH_INT_STS_GPIOS_
+
+struct USB_CONTEXT{
+ struct usb_ctrlrequest req;
+ struct completion notify;
+};
+
+enum{
+ ASYNC_RW_SUCCESS,
+ ASYNC_RW_FAIL,
+ ASYNC_RW_TIMEOUT,
+};
+
+#define USE_DEBUG
+#ifdef USE_DEBUG
+#define USE_WARNING
+#define USE_TRACE
+#define USE_ASSERT
+#endif //USE_DEBUG
+
+
+#define HIBYTE(word) ((BYTE)(((u16)(word))>>8))
+#define LOBYTE(word) ((BYTE)(((u16)(word))&0x00FFU))
+#define HIWORD(dWord) ((u16)(((u32)(dWord))>>16))
+#define LOWORD(dWord) ((u16)(((u32)(dWord))&0x0000FFFFUL))
+
+/*******************************************************
+* Macro: SMSC_TRACE
+* Description:
+* This macro is used like printf.
+* It can be used anywhere you want to display information
+* For any release version it should not be left in
+* performance sensitive Tx and Rx code paths.
+* To use this macro define USE_TRACE and set bit 0 of debug_mode
+*******************************************************/
+#ifdef USING_LINT
+extern void SMSC_TRACE(unsigned long dbgBit, const char * a, ...);
+#else //not USING_LINT
+#ifdef USE_TRACE
+extern u32 debug_mode;
+#ifndef USE_WARNING
+#define USE_WARNING
+#endif
+# define SMSC_TRACE(dbgBit,msg,args...) \
+ if(debug_mode&dbgBit) { \
+ printk("SMSC_9500: " msg "\n", ## args); \
+ }
+#else
+# define SMSC_TRACE(dbgBit,msg,args...)
+#endif
+#endif //end of not USING_LINT
+
+/*******************************************************
+* Macro: SMSC_WARNING
+* Description:
+* This macro is used like printf.
+* It can be used anywhere you want to display warning information
+* For any release version it should not be left in
+* performance sensitive Tx and Rx code paths.
+* To use this macro define USE_TRACE or
+* USE_WARNING and set bit 1 of debug_mode
+*******************************************************/
+
+
+#ifdef USING_LINT
+extern void SMSC_WARNING(const char * a, ...);
+#else //not USING_LINT
+#ifdef USE_WARNING
+extern u32 debug_mode;
+#ifndef USE_ASSERT
+#define USE_ASSERT
+#endif
+# define SMSC_WARNING(msg, args...) \
+ if(debug_mode&DBG_WARNING) { \
+ printk("SMSC_9500_WARNING: ");\
+ printk(__FUNCTION__);\
+ printk(": " msg "\n",## args);\
+ }
+#else
+# define SMSC_WARNING(msg, args...)
+#endif
+#endif //end of not USING_LINT
+
+
+/*******************************************************
+* Macro: SMSC_ASSERT
+* Description:
+* This macro is used to test assumptions made when coding.
+* It can be used anywhere, but is intended only for situations
+* where a failure is fatal.
+* If code execution where allowed to continue it is assumed that
+* only further unrecoverable errors would occur and so this macro
+* includes an infinite loop to prevent further corruption.
+* Assertions are only intended for use during developement to
+* insure consistency of logic through out the driver.
+* A driver should not be released if assertion failures are
+* still occuring.
+* To use this macro define USE_TRACE or USE_WARNING or
+* USE_ASSERT
+*******************************************************/
+#ifdef USING_LINT
+extern void SMSC_ASSERT(BOOLEAN condition);
+#else //not USING_LINT
+#ifdef USE_ASSERT
+ #define SMSC_ASSERT(condition) \
+ if(!(condition)) { \
+ printk("SMSC_9500_ASSERTION_FAILURE: \n");\
+ printk(" Condition = " #condition "\n");\
+ printk(" Function = ");printk(__FUNCTION__);printk("\n");\
+ printk(" File = " __FILE__ "\n");\
+ printk(" Line = %d\n",__LINE__); \
+ while(1);\
+ }
+#else
+#define SMSC_ASSERT(condition)
+#endif
+#endif //end of not USING_LINT
+
+
+#define LINK_INIT (0xFFFF)
+#define LINK_OFF (0x00UL)
+#define LINK_SPEED_10HD (0x01UL)
+#define LINK_SPEED_10FD (0x02UL)
+#define LINK_SPEED_100HD (0x04UL)
+#define LINK_SPEED_100FD (0x08UL)
+#define LINK_SYMMETRIC_PAUSE (0x10UL)
+#define LINK_ASYMMETRIC_PAUSE (0x20UL)
+#define LINK_AUTO_NEGOTIATE (0x40UL)
+
+
+#define INT_END_MACRTO_INT_ (0x00080000UL)
+#define INT_END_RXFIFO_HAS_FRAME_ (0x00040000UL)
+#define INT_END_TXSTOP_INT_ (0x00020000UL)
+#define INT_END_RXSTOP_INT_ (0x00010000UL)
+#define INT_END_PHY_INT_ (0x00008000UL)
+#define INT_END_TXE_ (0x00004000UL)
+#define INT_END_TDFU_ (0x00002000UL)
+#define INT_END_TDFO_ (0x00001000UL)
+#define INT_END_RXDF_INT_ (0x00000800UL)
+#define INT_END_GPIO_INT_ (0x000007FFUL)
+
+#define DBG_TRACE (0x01UL)
+#define DBG_WARNING (0x02UL)
+#define DBG_INIT (0x80000000UL)
+#define DBG_CLOSE (0x40000000UL)
+#define DBG_INTR (0x20000000UL)
+#define DBG_PWR (0x10000000UL)
+#define DBG_IOCTL (0x08000000UL)
+#define DBG_LINK (0x04000000UL)
+#define DBG_RX (0x02000000UL)
+#define DBG_TX (0x01000000UL)
+#define DBG_MCAST (0x00800000UL)
+#define DBG_HOST (0x00400000UL)
+#define DBG_LINK_CHANGE (0x00200000UL)
+
+#define HS_USB_PKT_SIZE 512
+#define FS_USB_PKT_SIZE 64
+
+#define DEFAULT_HS_BURST_CAP_SIZE (16 *1024 + 5 * HS_USB_PKT_SIZE)
+#define DEFAULT_FS_BURST_CAP_SIZE (6 *1024 + 33 * FS_USB_PKT_SIZE)
+#define DEFAULT_BULK_IN_DELAY (0x00002000UL)
+#define ETH_HEADER_SIZE 14
+#define ETH_MAX_DATA_SIZE 1500
+#define ETH_MAX_PACKET_SIZE (ETH_MAX_DATA_SIZE + ETH_HEADER_SIZE)
+#define MAX_SINGLE_PACKET_SIZE 2048
+#define LAN9500_EEPROM_MAGIC 0x9500UL
+#define EEPROM_MAC_OFFSET 0x01
+
+struct smsc9500_int_data {
+ u32 IntEndPoint;
+} __attribute__ ((packed));
+
+#define MAX_WUFF_NUM 8
+#define LAN9500_WUFF_NUM 4
+#define LAN9500A_WUFF_NUM 8
+
+typedef struct _WAKEUP_FILTER {
+ u32 dwFilterMask[MAX_WUFF_NUM*4];
+ u32 dwCommad[MAX_WUFF_NUM/4];
+ u32 dwOffset[MAX_WUFF_NUM/4];
+ u32 dwCRC[MAX_WUFF_NUM/2];
+} WAKEUP_FILTER, *PWAKEUP_FILTER;
+
+typedef struct _ADAPTER_DATA {
+ u32 dwIdRev;
+ u32 dwPhyAddress;
+ u32 dwPhyId;
+ u32 dwFpgaRev;
+ u32 macAddrHi16; //Mac address high 16 bits
+ u32 MmacAddrLo32; //Mac address Low 32 bits
+
+ BOOLEAN UseScatterGather;
+ BOOLEAN UseTxCsum;
+ BOOLEAN UseRxCsum;
+
+ BOOLEAN LanInitialized;
+ BYTE bPhyModel;
+ BYTE bPhyRev;
+ u32 dwLinkSpeed;
+ u32 dwLinkSettings;
+ struct semaphore phy_mutex; // Mutex for PHY access
+ struct semaphore eeprom_mutex; //Mutex for eeprom access
+ struct semaphore internal_ram_mutex; //Mutex for internal ram operation
+ struct semaphore RxFilterLock;
+
+ u16 wLastADV;
+ u16 wLastADVatRestart;
+
+ spinlock_t TxQueueLock;
+ BOOLEAN TxInitialized;
+ u32 dwTxQueueDisableMask;
+ BOOLEAN TxQueueDisabled;
+
+ u32 WolWakeupOpts;
+ u32 wakeupOptsBackup;
+ u32 dwWUFF[20];
+
+ /* for Rx Multicast work around */
+ volatile u32 HashLo;
+ volatile u32 HashHi;
+ volatile BOOLEAN MulticastUpdatePending;
+ volatile u32 set_bits_mask;
+ volatile u32 clear_bits_mask;
+
+ u32 LinkLedOnGpio; //Gpio port number for link led
+ u32 LinkLedOnGpioPolarity; //Gpio Output polarity
+ u32 LinkActLedCfg; //
+ u32 LinkLedOnGpioBufType;
+
+ u32 dynamicSuspendPHYEvent; //Store PHY interrupt source for dynamic suspend
+ u32 systemSuspendPHYEvent; //Store PHY interrupt source for system suspend
+
+ u16 eepromSize;
+} ADAPTER_DATA, *PADAPTER_DATA;
+
+typedef struct _MAC_ADDR_IN_RAM{
+ u32 signature;
+ u32 MacAddrL;
+ u32 MacAddrH;
+ u16 crc;
+ u16 crcComplement;
+}MAC_ADDR_IN_RAM;
+
+enum{
+ RAMSEL_FCT,
+ RAMSEL_EEPROM,
+ RAMSEL_TXTLI,
+ RAMSEL_RXTLI
+};
+
+#ifndef min
+#define min(_a, _b) (((_a) < (_b)) ? (_a) : (_b))
+#endif
+
+#ifndef max
+#define max(_a, _b) (((_a) > (_b)) ? (_a) : (_b))
+#endif
+
+#define BCAST_LEN 6
+#define MCAST_LEN 3
+#define ARP_LEN 2
+
+#endif // _LAN9500_H
+
+
+
diff --git a/drivers/net/usb/smscusbnet.c b/drivers/net/usb/smscusbnet.c
new file mode 100644
index 000000000000..ab685fc33d83
--- /dev/null
+++ b/drivers/net/usb/smscusbnet.c
@@ -0,0 +1,2075 @@
+/*
+ * USB Network driver infrastructure
+ * Copyright (C) 2000-2005 by David Brownell
+ * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ *
+ * 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
+ */
+
+/*
+ * This is a generic "USB networking" framework that works with several
+ * kinds of full and high speed networking devices: host-to-host cables,
+ * smart usb peripherals, and actual Ethernet adapters.
+ *
+ * These devices usually differ in terms of control protocols (if they
+ * even have one!) and sometimes they define new framing to wrap or batch
+ * Ethernet packets. Otherwise, they talk to USB pretty much the same,
+ * so interface (un)binding, endpoint I/O queues, fault handling, and other
+ * issues can usefully be addressed by this framework.
+ */
+
+// #define DEBUG // error path messages, extra info
+// #define VERBOSE // more; success messages
+
+#include <linux/version.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13))
+#include <linux/config.h>
+#endif
+
+#include <linux/module.h>
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+#include <linux/moduleparam.h>
+#endif
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/if_vlan.h>
+#include "smscusbnet.h"
+#include "version.h"
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Nineteen USB 1.1 max size bulk transactions per frame (ms), max.
+ * Several dozen bytes of IPv4 data can fit in two such transactions.
+ * One maximum size Ethernet packet takes twenty four of them.
+ * For high speed, each frame comfortably fits almost 36 max size
+ * Ethernet packets (so queues should be bigger).
+ *
+ * REVISIT qlens should be members of 'struct usbnet'; the goal is to
+ * let the USB host controller be busy for 5msec or more before an irq
+ * is required, under load. Jumbograms change the equation.
+ */
+#define RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? rx_queue_size : 4)
+#define TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? tx_queue_size : 4)
+
+// reawaken network queue this soon after stopping; else watchdog barks
+#define TX_TIMEOUT_JIFFIES (5*HZ)
+
+#define Link_Check_Delay (1*HZ)
+
+// throttle rx/tx briefly after some faults, so khubd might disconnect()
+// us (it polls at HZ/4 usually) before we report too many false errors.
+#define THROTTLE_JIFFIES (HZ/8)
+
+// between wakeups
+#define UNLINK_TIMEOUT_MS 3
+
+#define TX_PENDING_LEN 30
+/*-------------------------------------------------------------------------*/
+
+// randomly generated ethernet address
+static u8 node_id [ETH_ALEN];
+
+static const char driver_name [] = "smscusbnet";
+
+/* use ethtool to change the level for any given device */
+static int msg_level = -1;
+module_param (msg_level, int, 0);
+MODULE_PARM_DESC (msg_level, "Override default message level");
+
+//operational_mode = 0----> low latency
+//operational_mode = 1----> low power
+static int operational_mode = 0;
+module_param(operational_mode,int, 0);
+MODULE_PARM_DESC(operational_mode,"Enable operational mode");
+
+static unsigned long rx_queue_size = 60UL;
+module_param(rx_queue_size, ulong, 0);
+MODULE_PARM_DESC(rx_queue_size,"Specifies the size of the rx queue lenght");
+
+static unsigned long tx_queue_size = 60UL;
+module_param(tx_queue_size, ulong, 0);
+MODULE_PARM_DESC(tx_queue_size,"Specifies the size of the tx queue lenght");
+
+int tx_hold_on_completion = TRUE;
+module_param(tx_hold_on_completion, bool, 0);
+MODULE_PARM_DESC(tx_hold_on_completion,"Hold tx until USB completion if Enable");
+
+
+static int smscusbnet_xmit (struct sk_buff *skb, struct net_device *net);
+static int smscusbnet_bundle_skb_ximt(struct usbnet *dev, struct sk_buff_head *q);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29))
+static struct net_device_stats *smscusbnet_get_stats (struct net_device *net);
+static int smscusbnet_stop (struct net_device *net);
+static int smscusbnet_open (struct net_device *net);
+static int smscusbnet_start_xmit (struct sk_buff *skb, struct net_device *net);
+static void smscusbnet_tx_timeout (struct net_device *net);
+static int smscusbnet_change_mtu (struct net_device *net, int new_mtu);
+#endif
+
+static inline struct urb *smscusbnet_get_rx_urb(struct usbnet *dev)
+{
+ unsigned long lockflags;
+ struct urb * urb = NULL;
+
+ spin_lock_irqsave (&dev->rx_urblist_lock, lockflags);
+ if(dev->rx_urb_pool_head != dev->rx_urb_pool_tail){
+ urb = dev->rx_urb_pool[dev->rx_urb_pool_head];
+ dev->rx_urb_pool[dev->rx_urb_pool_head] = NULL;
+ dev->rx_urb_pool_head = (dev->rx_urb_pool_head + 1) % dev->rx_urb_pool_size;
+ }
+ spin_unlock_irqrestore (&dev->rx_urblist_lock, lockflags);
+
+ return urb;
+}
+
+static inline int smscusbnet_return_rx_urb(struct usbnet *dev, struct urb *urb)
+{
+ unsigned long lockflags;
+
+ //return this urb to rx urb pool
+ if(urb == NULL){
+ return 0;
+ }
+
+ spin_lock_irqsave(&dev->rx_urblist_lock, lockflags);
+ dev->rx_urb_pool[dev->rx_urb_pool_tail] = urb;
+ dev->rx_urb_pool_tail = (dev->rx_urb_pool_tail + 1) % dev->rx_urb_pool_size;
+
+ spin_unlock_irqrestore (&dev->rx_urblist_lock, lockflags);
+
+ return 0;
+}
+
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29))
+static const struct net_device_ops smscusbnet_netdev_ops =
+{
+ .ndo_open = smscusbnet_open,
+ .ndo_stop = smscusbnet_stop,
+ .ndo_start_xmit = smscusbnet_start_xmit,
+ .ndo_tx_timeout = smscusbnet_tx_timeout,
+ .ndo_change_mtu = smscusbnet_change_mtu,
+ .ndo_get_stats = smscusbnet_get_stats,
+ .ndo_validate_addr = eth_validate_addr,
+};
+#endif //linux 2.6.29
+
+/*-------------------------------------------------------------------------*/
+
+int smscusbnet_IsOperationalMode(struct usbnet *dev)
+{
+
+ return operational_mode;
+}
+/*-------------------------------------------------------------------------*/
+
+
+/*-------------------------------------------------------------------------*/
+
+/* handles CDC Ethernet and many other network "bulk data" interfaces */
+int smscusbnet_get_endpoints(struct usbnet *dev, struct usb_interface *intf)
+{
+ int tmp;
+ struct usb_host_interface *alt = NULL;
+ struct usb_host_endpoint *in = NULL, *out = NULL;
+ struct usb_host_endpoint *status = NULL;
+
+ for (tmp = 0; tmp < intf->num_altsetting; tmp++) {
+ unsigned ep;
+
+ in = out = status = NULL;
+ alt = intf->altsetting + tmp;
+
+ /* take the first altsetting with in-bulk + out-bulk;
+ * remember any status endpoint, just in case;
+ * ignore other endpoints and altsetttings.
+ */
+ for (ep = 0; ep < alt->desc.bNumEndpoints; ep++) {
+ struct usb_host_endpoint *e;
+ int intr = 0;
+
+ e = alt->endpoint + ep;
+ switch (e->desc.bmAttributes) {
+ case USB_ENDPOINT_XFER_INT:
+ if (!(e->desc.bEndpointAddress & USB_DIR_IN))
+ continue;
+ intr = 1;
+ /* FALLTHROUGH */
+ case USB_ENDPOINT_XFER_BULK:
+ break;
+ default:
+ continue;
+ }
+ if (e->desc.bEndpointAddress & USB_DIR_IN) {
+ if (!intr && !in)
+ in = e;
+ else if (intr && !status)
+ status = e;
+ } else {
+ if (!out)
+ out = e;
+ }
+ }
+ if (in && out)
+ break;
+ }
+ if (!alt || !in || !out)
+ {
+ devdbg (dev, "return EINVAL = %d", EINVAL);
+ return -EINVAL;
+ }
+ if (alt->desc.bAlternateSetting != 0
+ || !(dev->driver_info->flags & FLAG_NO_SETINT)) {
+ tmp = usb_set_interface (dev->udev, alt->desc.bInterfaceNumber,
+ alt->desc.bAlternateSetting);
+ if (tmp < 0)
+ {
+ devdbg (dev, "return tmp = %d", tmp);
+ return tmp;
+
+ }
+ }
+
+ dev->in = usb_rcvbulkpipe (dev->udev,
+ in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ dev->out = usb_sndbulkpipe (dev->udev,
+ out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ dev->status = status;
+
+ return 0;
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
+static void intr_complete (struct urb *urb, struct pt_regs *regs);
+#else
+static void intr_complete (struct urb *urb);
+#endif
+
+static int init_status (struct usbnet *dev, struct usb_interface *intf)
+{
+ unsigned pipe = 0;
+ unsigned maxp;
+ unsigned period;
+ int i;
+
+ if (!dev->driver_info->status)
+ return 0;
+
+ if(dev->chipID == ID_REV_7500_CHIPID){
+ dev->device_id = DEV_SMSC7500;
+ dev->tx_hold_on_completion = 0; //LAN7500 doesn't need this feature
+ }else{
+ dev->device_id = DEV_SMSC9500;
+ dev->tx_hold_on_completion = tx_hold_on_completion;
+ }
+
+ pipe = usb_rcvintpipe (dev->udev,
+ dev->status->desc.bEndpointAddress
+ & USB_ENDPOINT_NUMBER_MASK);
+ maxp = usb_maxpacket (dev->udev, pipe, 0);
+
+
+ period = dev->status->desc.bInterval;
+ dev->interrupt_urb_buffer = NULL;
+ if(operational_mode){
+
+ dev->interrupt_urb_buffer = kmalloc (maxp, GFP_KERNEL);
+ if (dev->interrupt_urb_buffer) {
+ dev->interrupt = usb_alloc_urb (0, GFP_KERNEL);
+ if (!dev->interrupt) {
+ devdbg (dev, "failed to alloc interrupt urb\n!!!");
+ kfree (dev->interrupt_urb_buffer);
+ dev->interrupt_urb_buffer = NULL;
+ return -ENOMEM;
+ } else {
+ usb_fill_int_urb(dev->interrupt, dev->udev, pipe,
+ dev->interrupt_urb_buffer, maxp, intr_complete, dev, period);
+ devdbg (dev,"status ep%din, %d bytes period %d\n",
+ usb_pipeendpoint(pipe), maxp, period);
+ }
+ }
+ }
+
+ dev->rx_urb_pool_head = 0;
+ dev->rx_urb_pool_tail = 0;
+ dev->rx_urb_pool_size = RX_QLEN (dev);
+ //Allocate extra one to distinguish between empty and full list
+ dev->rx_urb_pool_size += 1;
+ dev->rx_urb_pool = kmalloc(sizeof(struct urb*) * dev->rx_urb_pool_size, GFP_KERNEL);
+ memset(dev->rx_urb_pool, 0, sizeof(struct urb*) * dev->rx_urb_pool_size);
+
+ if(!dev->rx_urb_pool){
+ devwarn(dev, "unable to allocate memory for rx_urb_pool");
+ return -ENOMEM;
+ }
+
+ for(i=0; i<(dev->rx_urb_pool_size-1); i++){
+ dev->rx_urb_pool[i] = usb_alloc_urb (0, GFP_KERNEL);
+ if(!dev->rx_urb_pool[i]){
+ devwarn(dev, "failed to alloc rx urb");
+ return -ENOMEM;
+ }
+ //URB_ASYNC_UNLINK: usb_unlink_urb will return immediately without waiting for complete handler
+ // which might result in deadlock problem on muitl-core cpu
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14))
+ dev->rx_urb_pool[i]->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+
+ }
+ dev->rx_urb_pool_tail = dev->rx_urb_pool_size - 1;
+ spin_lock_init(&(dev->rx_urblist_lock));
+ devdbg (dev, "init_status, rx_urb_pool_head = %d, rx_urb_pool_tail = %d", dev->rx_urb_pool_head, dev->rx_urb_pool_tail);
+
+ return 0;
+}
+
+/* Passes this packet up the stack, updating its accounting.
+ * Some link protocols batch packets, so their rx_fixup paths
+ * can return clones as well as just modify the original skb.
+ */
+void smscusbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
+{
+ int status;
+ u16 vlan_tag = *((u16 *)&skb->cb[0]);
+
+ skb->dev = dev->net;
+ skb->protocol = eth_type_trans (skb, dev->net);
+
+ if (netif_msg_rx_status (dev))
+ devdbg (dev, "< rx, len %zu, type 0x%x",
+ skb->len + sizeof (struct ethhdr), skb->protocol);
+ memset (skb->cb, 0, sizeof (struct skb_data));
+
+ if(vlan_tag == VLAN_DUMMY){//No vlan tag acceleration
+ status = netif_rx (skb);
+ }else{//Vlan tag acceleration
+ status = vlan_hwaccel_rx(skb, dev->vlgrp, vlan_tag);
+ }
+
+ if(status == NET_RX_DROP){
+ dev->stats.rx_dropped++;
+ }else{
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
+ }
+
+ if (status != NET_RX_SUCCESS && netif_msg_rx_err (dev))
+ devdbg (dev, "netif_rx status %d", status);
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * Network Device Driver (peer link to "Host Device", from USB host)
+ *
+ *-------------------------------------------------------------------------*/
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29))
+static
+#endif
+int smscusbnet_change_mtu (struct net_device *net, int new_mtu)
+{
+ struct usbnet *dev = netdev_priv(net);
+ int ll_mtu = new_mtu + net->hard_header_len;
+
+#ifdef JUMBO_FRAME
+ if (new_mtu <= 0)
+ return -EINVAL;
+ // no second zero-length packet read wanted after mtu-sized packets
+ if ((ll_mtu % dev->maxpacket) == 0)
+ return -EDOM;
+ if(dev->driver_info->set_max_frame_size(dev, net->hard_header_len + new_mtu - EXTRA_HEADER_LEN) < 0){
+ return -EINVAL;
+ }
+ net->mtu = new_mtu;
+ dev->hard_mtu = net->mtu + net->hard_header_len;
+#else
+ if (new_mtu <= 0 || ll_mtu > dev->hard_mtu)
+ return -EINVAL;
+ // no second zero-length packet read wanted after mtu-sized packets
+ if ((ll_mtu % dev->maxpacket) == 0)
+ return -EDOM;
+ net->mtu = new_mtu;
+#endif
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29))
+static
+#endif
+struct net_device_stats *smscusbnet_get_stats (struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+ return &dev->stats;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* some LK 2.4 HCDs oopsed if we freed or resubmitted urbs from
+ * completion callbacks. 2.5 should have fixed those bugs...
+ */
+
+static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_head *list)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&list->lock, flags);
+ __skb_unlink(skb, list);
+ spin_unlock(&list->lock);
+ spin_lock(&dev->done.lock);
+ __skb_queue_tail(&dev->done, skb);
+ if (dev->done.qlen == 1)
+ tasklet_schedule(&dev->bh);
+ spin_unlock_irqrestore(&dev->done.lock, flags);
+}
+
+/* some work can't be done in tasklets, so we use keventd
+ *
+ * NOTE: annoying asymmetry: if it's active, schedule_work() fails,
+ * but tasklet_schedule() doesn't. hope the failure is rare.
+ */
+void smscusbnet_defer_kevent (struct usbnet *dev, int work)
+{
+ set_bit (work, &dev->flags);
+ if (!schedule_work (&dev->kevent)){
+ devdbg (dev, "kevent %d may have been dropped", work);
+ }
+// else
+// devdbg (dev, "kevent %d scheduled", work);
+}
+
+/* some work can't be done in tasklets, so we use keventd
+ *
+ * We use myevent to schedule Rx Bulk in
+ *
+ */
+
+void smscusbnet_defer_myevent (struct usbnet *dev, int work)
+{
+ set_bit (work, &dev->flags);
+ if (!queue_work(dev->MyWorkQueue,&dev->myevent)){
+ //deverr (dev, "myevent %d may have been dropped", work);
+ }
+ //else
+ // devdbg (dev,"myevent %d scheduled", work);
+}
+
+void smscusbnet_linkpolling(unsigned long ptr)
+{
+
+ struct usbnet * dev= (struct usbnet *) ptr;
+ smscusbnet_defer_myevent(dev, EVENT_LINK_RESET);
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) && defined(CONFIG_PM)
+ if(dev->dynamicSuspend){
+ smscusbnet_defer_myevent(dev, EVENT_IDLE_CHECK);
+ }
+#endif //(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) && defined(CONFIG_PM)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) && defined(CONFIG_PM)
+ if(dev->linkDownSuspend || dev->smartDetach){
+#else
+ if(dev->smartDetach){
+#endif //(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) && defined(CONFIG_PM)
+ if(!netif_carrier_ok(dev->net) && dev->linkcheck++ > 2){//Check 2 times in case of conflict with resume
+ smscusbnet_defer_myevent(dev, EVENT_LINK_DOWN);
+ dev->linkcheck = 0;
+ }
+ }
+}
+
+
+
+/*-------------------------------------------------------------------------*/
+
+//static void rx_complete (struct urb *urb, struct pt_regs *regs);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
+static void rx_complete (struct urb *urb, struct pt_regs *regs );
+#else
+static void rx_complete (struct urb *urb);
+#endif
+static void rx_submit (struct usbnet *dev, struct urb *urb, int flags)
+{
+ struct sk_buff *skb;
+ struct skb_data *entry;
+ int retval = 0;
+ unsigned long lockflags;
+ size_t size = dev->rx_urb_size;
+
+#ifndef RX_OFFSET
+ if ((skb = alloc_skb (size + NET_IP_ALIGN , flags)) == NULL) {
+#else
+ if ((skb = alloc_skb (size, flags)) == NULL) {
+#endif //RX_OFFSET
+ if (netif_msg_rx_err (dev))
+ devdbg (dev,"no rx skb\n");
+ smscusbnet_defer_kevent (dev, EVENT_RX_MEMORY);
+ smscusbnet_return_rx_urb(dev, urb);
+
+ return;
+ }
+
+#ifndef RX_OFFSET
+ skb_reserve (skb, NET_IP_ALIGN);
+#endif
+
+ entry = (struct skb_data *) skb->cb;
+ entry->urb = urb;
+ entry->dev = dev;
+ entry->state = rx_start;
+ entry->length = 0;
+
+ usb_fill_bulk_urb (urb, dev->udev, dev->in,
+ skb->data, size, rx_complete, skb);
+
+ spin_lock_irqsave (&dev->rxq.lock, lockflags);
+
+ if (netif_running (dev->net)
+ && netif_device_present (dev->net)
+ && !test_bit (EVENT_RX_HALT, &dev->flags)) {
+ switch (retval = usb_submit_urb (urb, GFP_ATOMIC)){
+ case -EPIPE:
+ smscusbnet_defer_kevent (dev, EVENT_RX_HALT);
+ break;
+ case -ENOMEM:
+ smscusbnet_defer_kevent (dev, EVENT_RX_MEMORY);
+ break;
+ case -ENODEV:
+ if (netif_msg_ifdown (dev))
+ devdbg (dev, "device gone");
+ netif_device_detach (dev->net);
+ break;
+ default:
+ if (netif_msg_rx_err (dev))
+ devdbg (dev, "rx submit, %d", retval);
+ tasklet_schedule (&dev->bh);
+ break;
+ case 0:
+ __skb_queue_tail (&dev->rxq, skb);
+ }
+ } else {
+ if (netif_msg_ifdown (dev))
+ devdbg (dev, "rx: stopped");
+ retval = -ENOLINK;
+ }
+ spin_unlock_irqrestore (&dev->rxq.lock, lockflags);
+ if (retval) {
+ dev_kfree_skb_any (skb);
+ smscusbnet_return_rx_urb(dev, urb);
+ }
+}
+
+//-------------------------------------------------------------------------
+
+static inline void rx_process (struct usbnet *dev, struct sk_buff *skb)
+{
+ int ret;
+
+ if (!dev->driver_info->rx_fixup)return;
+
+ ret = dev->driver_info->rx_fixup (dev, skb);
+
+ if(ret == RX_FIXUP_INVALID_SKB)skb_queue_tail(&dev->done, skb);
+ else if(ret == RX_FIXUP_ERROR){
+ dev->stats.rx_errors++;
+ skb_queue_tail (&dev->done, skb);
+ }
+ else{
+ if (skb->len) {
+ smscusbnet_skb_return (dev, skb);
+
+ } else {
+ if (netif_msg_rx_err (dev))
+ devdbg (dev, "drop");
+ dev->stats.rx_errors++;
+ skb_queue_tail (&dev->done, skb);
+ }
+ }
+
+}
+
+/*-------------------------------------------------------------------------*/
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
+static void rx_complete (struct urb *urb, struct pt_regs *regs)
+#else
+static void rx_complete (struct urb *urb)
+#endif
+
+{
+ struct sk_buff *skb = (struct sk_buff *) urb->context;
+ struct skb_data *entry = (struct skb_data *) skb->cb;
+ struct usbnet *dev = entry->dev;
+ int urb_status = urb->status;
+
+ dev->idleCount = 0;
+
+ skb_put (skb, urb->actual_length);
+ entry->state = rx_done;
+ entry->urb = NULL;
+
+ switch (urb_status) {
+ // success
+ case 0:
+ if (operational_mode) {
+ if ((skb->len < dev->net->hard_header_len) && (skb->len != 0)) {
+ entry->state = rx_cleanup;
+ dev->stats.rx_errors++;
+ dev->stats.rx_length_errors++;
+ if (netif_msg_rx_err (dev))
+ devdbg (dev, "rx length %d", skb->len);
+ }
+
+ if (skb->len == 0) {
+ entry->state = rx_cleanup;
+ dev->StopSummitUrb=1;
+ }
+ else {
+ dev->StopSummitUrb=0;
+ }
+
+ } else {
+ if (skb->len < dev->net->hard_header_len) {
+ entry->state = rx_cleanup;
+ dev->stats.rx_errors++;
+ dev->stats.rx_length_errors++;
+ if (netif_msg_rx_err (dev))
+ devdbg (dev, "rx length %d", skb->len);
+ }
+
+ }
+ break;
+
+ // stalls need manual reset. this is rare ... except that
+ // when going through USB 2.0 TTs, unplug appears this way.
+ // we avoid the highspeed version of the ETIMEOUT/EILSEQ
+ // storm, recovering as needed.
+ case -EPIPE:
+
+ dev->extra_error_cnts.rx_epipe++;
+ dev->stats.rx_errors++;
+ devdbg (dev,"in rx_complete,case -EPIPE, dev->stats.rx_errors=0x%08lx\n", dev->stats.rx_errors);
+ smscusbnet_defer_kevent (dev, EVENT_RX_HALT);
+ // FALLTHROUGH
+
+ // software-driven interface shutdown
+ case -ECONNRESET: // async unlink
+ case -ESHUTDOWN: // hardware gone
+ if (netif_msg_ifdown (dev))
+ devdbg (dev, "rx shutdown, code %d", urb_status);
+ goto block;
+
+ // we get controller i/o faults during khubd disconnect() delays.
+ // throttle down resubmits, to avoid log floods; just temporarily,
+ // so we still recover when the fault isn't a khubd delay.
+ case -EPROTO: // ehci
+ dev->extra_error_cnts.rx_eproto++;
+ goto _HANDLE;
+ case -ETIMEDOUT: // ohci
+ dev->extra_error_cnts.rx_etimeout++;
+ goto _HANDLE;
+ case -EILSEQ: // uhci
+ dev->extra_error_cnts.rx_eilseq++;
+_HANDLE:
+
+ dev->stats.rx_errors++;
+ devdbg (dev,"in rx_complete,case -EPROTO, dev->stats.rx_errors=0x%08lx\n", dev->stats.rx_errors);
+ if (!timer_pending (&dev->delay)) {
+ mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES);
+ if (netif_msg_link (dev))
+ devdbg (dev, "rx throttle %d", urb_status);
+ }
+ //Set recovery flag
+ set_bit (EVENT_DEV_RECOVERY, &dev->flags);
+
+block:
+ entry->state = rx_cleanup;
+ entry->urb = urb;
+ urb = NULL;
+ break;
+
+ // data overrun ... flush fifo?
+ case -EOVERFLOW:
+ dev->extra_error_cnts.rx_eoverflow++;
+ dev->stats.rx_over_errors++;
+ // FALLTHROUGH
+ //Set recovery flag
+ set_bit (EVENT_DEV_RECOVERY, &dev->flags);
+
+ default:
+ entry->state = rx_cleanup;
+ dev->stats.rx_errors++;
+
+ devdbg (dev,"in rx_complete,case default dev->stats.rx_errors=0x%08lx\n", dev->stats.rx_errors);
+ if (netif_msg_rx_err (dev))
+ devdbg (dev, "rx status %d", urb_status);
+ break;
+ }
+
+ defer_bh(dev, skb, &dev->rxq);
+
+ if (urb) {
+ if (operational_mode) {
+ if (netif_running (dev->net)
+ && !test_bit (EVENT_RX_HALT, &dev->flags)
+ && !dev->StopSummitUrb) {
+ rx_submit (dev, urb, GFP_ATOMIC);
+ return;
+ }
+
+ } else {
+
+ if (netif_running (dev->net)
+ && !test_bit (EVENT_RX_HALT, &dev->flags)) {
+ rx_submit (dev, urb, GFP_ATOMIC);
+ return;
+ }
+ }
+ //return this urb to rx urb pool
+ smscusbnet_return_rx_urb(dev, urb);
+ }
+ if (netif_msg_rx_err (dev))
+ devdbg (dev, "no read resubmitted");
+}
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
+static void intr_complete (struct urb *urb, struct pt_regs *regs)
+#else
+static void intr_complete (struct urb *urb)
+#endif
+
+{
+ struct usbnet *dev = urb->context;
+ int status = urb->status;
+
+ switch (status) {
+ /* success */
+ case 0:
+ dev->driver_info->status(dev, urb);
+ break;
+
+ /* software-driven interface shutdown */
+ case -ENOENT: // urb killed
+ case -ESHUTDOWN: // hardware gone
+ if (netif_msg_ifdown (dev))
+ devdbg (dev,"intr shutdown, code %d", status);
+ return;
+
+ // we get controller i/o faults during khubd disconnect() delays.
+ // throttle down resubmits, to avoid log floods; just temporarily,
+ // so we still recover when the fault isn't a khubd delay.
+ /* We need throttling here like RX/TX, because flood events occur on slow ppc platform
+ * when the device is being disconnected.
+ */
+ case -EPROTO: // ehci
+ case -ETIMEDOUT: // ohci
+ case -EILSEQ: // uhci
+
+ devdbg (dev,"in intr_complete,case -EPROTO\n");
+ if (!timer_pending (&dev->delay)) {
+ mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES);
+ if (netif_msg_link (dev))
+ devdbg (dev, "intr throttle %d", status);
+ }
+ dev->intr_urb_delay_submit = TRUE;
+ return;
+
+ default:
+ devdbg (dev,"intr status %d", status);
+ break;
+ }
+
+ if (!netif_running (dev->net)){
+ return;
+ }
+
+ memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
+ status = usb_submit_urb (urb, GFP_ATOMIC);
+ if (status != 0 && netif_msg_timer (dev))
+ deverr(dev, "intr resubmit --> %d", status);
+}
+
+/*-------------------------------------------------------------------------*/
+
+// unlink pending rx/tx; completion handlers do all other cleanup
+
+static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
+{
+ unsigned long flags;
+ struct sk_buff *skb, *skbnext;
+ int count = 0;
+
+ spin_lock_irqsave (&q->lock, flags);
+ for (skb = q->next; skb != (struct sk_buff *) q; skb = skbnext) {
+ struct skb_data *entry;
+ struct urb *urb;
+ int retval;
+
+ entry = (struct skb_data *) skb->cb;
+ urb = entry->urb;
+ skbnext = skb->next;
+
+ // during some PM-driven resume scenarios,
+ // these (async) unlinks complete immediately
+ retval = usb_unlink_urb (urb);
+ if (retval != -EINPROGRESS && retval != 0)
+ devdbg (dev, "unlink urb err, %d", retval);
+ else
+ count++;
+ }
+ spin_unlock_irqrestore (&q->lock, flags);
+ return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// tasklet (work deferred from completions, in_irq) or timer
+
+static void smscusbnet_bh (unsigned long param)
+{
+ struct usbnet *dev = (struct usbnet *) param;
+ struct sk_buff *skb = NULL;
+ struct skb_data *entry = NULL;
+
+ while ((skb = skb_dequeue (&dev->done))) {
+ entry = (struct skb_data *) skb->cb;
+ switch (entry->state) {
+ case rx_done:
+ entry->state = rx_cleanup;
+ rx_process (dev, skb);
+ continue;
+ case tx_done:
+ usb_free_urb (entry->urb);
+ dev_kfree_skb (skb);
+
+ if(dev->device_id == DEV_SMSC7500){
+ if(!dev->txq.qlen){
+ skb = skb_dequeue(&dev->tx_pending_q);
+ if(skb){
+ smscusbnet_xmit(skb, dev->net);
+ }
+ }
+ }else{//DEV_SMSC9500
+ unsigned long flags;
+ spin_lock_irqsave (&dev->txq.lock, flags);
+ if(!dev->txq.qlen)
+ smscusbnet_bundle_skb_ximt(dev, &dev->tx_pending_q);
+ spin_unlock_irqrestore (&dev->txq.lock, flags);
+ }
+
+ continue;
+ case rx_cleanup:
+ if(entry->urb)smscusbnet_return_rx_urb(dev, entry->urb);
+ dev_kfree_skb (skb);
+ continue;
+ default:
+ devdbg (dev, "bogus skb state %d", entry->state);
+ }
+ }
+
+ if (netif_running (dev->net) && netif_device_present (dev->net) && !timer_pending (&dev->delay)) {
+ //submit delayed interrupt urb
+ if(operational_mode && dev->interrupt && dev->intr_urb_delay_submit){
+ int status = usb_submit_urb (dev->interrupt, GFP_ATOMIC);
+ dev->intr_urb_delay_submit = FALSE;
+ if (status != 0 && netif_msg_timer (dev))
+ deverr(dev, "intr resubmit --> %d", status);
+
+ }
+ }
+
+ // waiting for all pending urbs to complete?
+ if (dev->wait) {
+ if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) {
+ wake_up (dev->wait);
+ }
+
+ // or are we maybe short a few urbs?
+ } else if (netif_running (dev->net)
+ && netif_device_present (dev->net)
+ && !timer_pending (&dev->delay)
+ && !test_bit (EVENT_RX_HALT, &dev->flags)) {
+
+ if (((operational_mode)&&(!dev->StopSummitUrb)) ||(!(operational_mode) )) {
+ int temp = dev->rxq.qlen;
+ int qlen = RX_QLEN (dev);
+
+ if (temp < qlen) {
+ struct urb *urb;
+ int i;
+
+ // don't refill the queue all at once
+ for (i = 0; i < 10 && dev->rxq.qlen < qlen; i++) {
+ urb = smscusbnet_get_rx_urb(dev);
+ if (urb != NULL) {
+ rx_submit (dev, urb, GFP_ATOMIC);
+ }
+ }
+
+ //if (temp != dev->rxq.qlen && netif_msg_link (dev))
+ //devdbg (dev, "in dev->bh, rxqlen %d --> %d",
+ // temp, dev->rxq.qlen);
+ if (dev->rxq.qlen < qlen)
+ tasklet_schedule (&dev->bh);
+ }
+
+ }
+ if(!dev->tx_hold_on_completion){
+ if (dev->txq.qlen < TX_QLEN (dev))
+ netif_wake_queue (dev->net);
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+// precondition: never called in_interrupt
+
+int smscusbnet_stop (struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+ int temp;
+
+ DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup);
+ DECLARE_WAITQUEUE (wait, current);
+
+ netif_stop_queue (net);
+ if (netif_msg_ifdown (dev))
+ devinfo (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld",
+ dev->stats.rx_packets, dev->stats.tx_packets,
+ dev->stats.rx_errors, dev->stats.tx_errors
+ );
+
+ if(!skb_queue_empty(&dev->tx_pending_q)){
+ skb_queue_purge(&dev->tx_pending_q);
+ }
+ // ensure there are no more active urbs
+ add_wait_queue (&unlink_wakeup, &wait);
+ dev->wait = &unlink_wakeup;
+ temp = unlink_urbs (dev, &dev->txq) + unlink_urbs (dev, &dev->rxq);
+ // maybe wait for deletions to finish.
+ while (!skb_queue_empty(&dev->rxq) &&
+ !skb_queue_empty(&dev->txq) &&
+ !skb_queue_empty(&dev->done)) {
+ msleep(UNLINK_TIMEOUT_MS);
+ if (netif_msg_ifdown (dev))
+ devdbg (dev, "waited for %d urb completions", temp);
+ }
+ dev->wait = NULL;
+ remove_wait_queue (&unlink_wakeup, &wait);
+ if(operational_mode){
+ usb_kill_urb(dev->interrupt);
+ }
+ devdbg (dev, "smscusbnet_stop, rx_urb_pool_head = %d, rx_urb_pool_tail = %d", dev->rx_urb_pool_head, dev->rx_urb_pool_tail);
+
+ /* deferred work (task, timer, softirq) must also stop.
+ * can't flush_scheduled_work() until we drop rtnl (later),
+ * else workers could deadlock; so make workers a NOP.
+ */
+ dev->flags = 0;
+ //Clean up the counters
+ memset(&dev->stats, 0, sizeof(dev->stats));
+ memset(&dev->extra_error_cnts, 0, sizeof(dev->extra_error_cnts));
+
+ dev->StopLinkPolling=TRUE;
+ del_timer_sync (&dev->LinkPollingTimer);
+ del_timer_sync (&dev->delay);
+ tasklet_kill (&dev->bh);
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
+ //if thera is in suspend0 status already, resume it first. So we can set suspend2 in Smsc9500_suspend();
+ down(&dev->pm_mutex);
+ if(!dev->pmLock){
+ dev->pmLock = TRUE;
+ usb_autopm_get_interface(dev->uintf);
+ }
+ up(&dev->pm_mutex);
+
+ msleep(100);
+
+ down(&dev->pm_mutex);
+ if(dev->pmLock){
+ usb_autopm_put_interface(dev->uintf);
+ }
+ dev->pmLock = FALSE;
+ up(&dev->pm_mutex);
+#endif
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+// posts reads, and enables write queuing
+
+// precondition: never called in_interrupt
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29))
+static
+#endif
+int smscusbnet_open (struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+ int retval = 0;
+ struct driver_info *info = dev->driver_info;
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
+ down(&dev->pm_mutex);
+ dev->pmLock = TRUE;
+ usb_autopm_get_interface(dev->uintf);
+ up(&dev->pm_mutex);
+#endif
+
+ dev->delay.function = smscusbnet_bh;
+ dev->delay.data = (unsigned long) dev;
+ init_timer (&dev->delay);
+
+ dev->suspendFlag = 0;
+
+ // put into "known safe" state
+ if (info->reset && (retval = info->reset (dev)) < 0) {
+ devdbg (dev,"info->reset && (retval = info->reset (dev)) \n");
+ if (netif_msg_ifup (dev))
+ devinfo (dev,
+ "open reset fail (%d) usbnet usb-%s-%s, %s",
+ retval,
+ dev->udev->bus->bus_name, dev->udev->devpath,
+ info->description);
+ goto done;
+ }
+
+ dev->intr_urb_delay_submit = FALSE;
+ init_timer(&(dev->LinkPollingTimer));
+ dev->StopLinkPolling=FALSE;
+ dev->LinkPollingTimer.function=smscusbnet_linkpolling;
+ dev->LinkPollingTimer.data=(unsigned long) dev;
+ dev->LinkPollingTimer.expires=jiffies+HZ;
+ add_timer(&(dev->LinkPollingTimer));
+
+ // insist peer be connected
+ if (info->check_connect && (retval = info->check_connect (dev)) < 0) {
+ if (netif_msg_ifup (dev))
+ devdbg (dev, "can't open; %d", retval);
+ goto done;
+ }
+
+ /* start any status interrupt transfer */
+ if(operational_mode){
+ if (dev->interrupt) {
+ devdbg (dev,"dev->interrupt \n");
+ retval = usb_submit_urb (dev->interrupt, GFP_KERNEL);
+ if (retval < 0) {
+ if (netif_msg_ifup (dev))
+ deverr (dev, "intr submit %d", retval);
+ goto done;
+ }
+ }
+ }
+
+ netif_start_queue (net);
+ if (netif_msg_ifup (dev)) {
+ char *framing;
+ devdbg (dev,"netif_msg_ifup \n");
+ if (dev->driver_info->flags & FLAG_FRAMING_NC)
+ framing = "NetChip";
+ else if (dev->driver_info->flags & FLAG_FRAMING_GL)
+ framing = "GeneSys";
+ else if (dev->driver_info->flags & FLAG_FRAMING_Z)
+ framing = "Zaurus";
+ else if (dev->driver_info->flags & FLAG_FRAMING_RN)
+ framing = "RNDIS";
+ else if (dev->driver_info->flags & FLAG_FRAMING_AX)
+ framing = "ASIX";
+ else
+ framing = "simple";
+
+ devinfo (dev, "open: enable queueing "
+ "(rx %lx, tx %lx) mtu %d %s framing",
+ RX_QLEN (dev), TX_QLEN (dev), dev->net->mtu,
+ framing);
+ }
+
+ // delay posting reads until we're fully open
+ tasklet_schedule (&dev->bh);
+done:
+ return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* work that cannot be done in interrupt context uses keventd.
+ *
+ * NOTE: with 2.5 we could do more of this using completion callbacks,
+ * especially now that control transfers can be queued.
+ */
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20))
+static void kevent (void *data)
+{
+ struct usbnet *dev = data;
+#else
+static void kevent(struct work_struct *work)
+{
+ struct usbnet *dev = container_of(work,struct usbnet,kevent);
+#endif
+
+ int status;
+
+ /* usb_clear_halt() needs a thread context */
+ if (test_bit (EVENT_TX_HALT, &dev->flags)) {
+ unlink_urbs (dev, &dev->txq);
+ status = usb_clear_halt (dev->udev, dev->out);
+
+ //Set recovery flag, the device will be reset in the smsc9500.c
+ set_bit (EVENT_DEV_RECOVERY, &dev->flags);
+
+ if (status < 0
+ && status != -EPIPE
+ && status != -ESHUTDOWN) {
+ if (netif_msg_tx_err (dev))
+ deverr (dev, "can't clear tx halt, status %d",
+ status);
+ } else {
+ clear_bit (EVENT_TX_HALT, &dev->flags);
+ if (status != -ESHUTDOWN){
+ netif_wake_queue (dev->net);
+ }
+ }
+ }
+ if (test_bit (EVENT_RX_HALT, &dev->flags)) {
+ unlink_urbs (dev, &dev->rxq);
+ status = usb_clear_halt (dev->udev, dev->in);
+
+ //Set recovery flag.
+ set_bit (EVENT_DEV_RECOVERY, &dev->flags);
+
+ if (status < 0
+ && status != -EPIPE
+ && status != -ESHUTDOWN) {
+ if (netif_msg_rx_err (dev))
+ deverr (dev, "can't clear rx halt, status %d",
+ status);
+ } else {
+ clear_bit (EVENT_RX_HALT, &dev->flags);
+ tasklet_schedule (&dev->bh);
+ }
+ }
+
+ /* tasklet could resubmit itself forever if memory is tight */
+ if (test_bit (EVENT_RX_MEMORY, &dev->flags)) {
+ struct urb *urb = NULL;
+
+ if (netif_running (dev->net))
+ urb = smscusbnet_get_rx_urb(dev);
+ else
+ clear_bit (EVENT_RX_MEMORY, &dev->flags);
+ if (urb != NULL) {
+ clear_bit (EVENT_RX_MEMORY, &dev->flags);
+ rx_submit (dev, urb, GFP_KERNEL);
+ tasklet_schedule (&dev->bh);
+ }
+ }
+
+ if (dev->flags)
+ devdbg (dev, "kevent done, flags = 0x%lx \n",
+ dev->flags);
+}
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
+/*-------------------------------------------------------------------------*/
+
+/* Stop all traffic so kernel will suspend our device.
+ *
+ */
+#ifdef CONFIG_PM
+static int suspendDevice(struct usbnet *dev)
+{
+ if(operational_mode){
+ usb_kill_urb(dev->interrupt); //stop interrupt urb
+ }else{
+ DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup);
+ DECLARE_WAITQUEUE (wait, current);
+
+ add_wait_queue (&unlink_wakeup, &wait);
+ dev->wait = &unlink_wakeup;
+ unlink_urbs (dev, &dev->txq);
+ unlink_urbs (dev, &dev->rxq);
+ // maybe wait for deletions to finish.
+ while (!skb_queue_empty(&dev->rxq) &&
+ !skb_queue_empty(&dev->txq) &&
+ !skb_queue_empty(&dev->done)) {
+ msleep(UNLINK_TIMEOUT_MS);
+ }
+ dev->wait = NULL;
+ remove_wait_queue (&unlink_wakeup, &wait);
+ }
+
+ dev->StopLinkPolling = TRUE; //stop accessing registers
+ down(&dev->pm_mutex);
+ if(dev->pmLock){
+ dev->pmLock = FALSE;
+ usb_autopm_put_interface(dev->uintf);
+ }
+ up(&dev->pm_mutex);
+
+ return 0;
+}
+#endif //CONFIG_PM
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* work that cannot be done in interrupt context uses keventd.
+ *
+ * NOTE: with 2.5 we could do more of this using completion callbacks,
+ * especially now that control transfers can be queued.
+ */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20))
+static void myevent(void *data)
+{
+ struct usbnet *dev = data;
+
+#else
+
+static void myevent(struct work_struct *work)
+{
+
+ struct usbnet *dev = container_of(work,struct usbnet,myevent);
+#endif
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) && defined(CONFIG_PM)
+ if (test_bit (EVENT_IDLE_CHECK, &dev->flags)) {
+ clear_bit (EVENT_IDLE_CHECK, &dev->flags);
+ //autosuspend_disabled should be enabled by shell cmd "echo auto > /sys/bus/usb/devices/X-XX/power/level"
+ if(dev->dynamicSuspend && !dev->udev->autosuspend_disabled){
+ if((dev->idleCount >= PM_IDLE_DELAY)/* && (dev->uintf->pm_usage_cnt > 0)*/){
+
+ if(dev->device_id == DEV_SMSC7500){
+ dev->suspendFlag |= AUTOSUSPEND_DYNAMIC_S3;
+ }else{
+ if(dev->chipDependFeatures[FEATURE_SUSPEND3]){
+ dev->suspendFlag |= AUTOSUSPEND_DYNAMIC_S3;
+ }else{
+ dev->suspendFlag |= AUTOSUSPEND_DYNAMIC;
+ }
+ }
+ suspendDevice(dev);
+ }
+ if(dev->idleCount < PM_IDLE_DELAY)dev->idleCount++;
+ }
+ }
+#endif //(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) && defined(CONFIG_PM)
+ if (test_bit (EVENT_LINK_DOWN, &dev->flags)) {
+ clear_bit (EVENT_LINK_DOWN, &dev->flags);
+ if(dev->smartDetach){
+ dev->suspendFlag |= AUTOSUSPEND_DETACH;
+ }
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) && defined(CONFIG_PM)
+ else if(dev->linkDownSuspend && !dev->udev->autosuspend_disabled){
+ dev->suspendFlag |= AUTOSUSPEND_LINKDOWN;
+ suspendDevice(dev);
+ }
+#endif //(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) && defined(CONFIG_PM)
+ }
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) && defined(CONFIG_PM)
+ if (test_bit (EVENT_IDLE_RESUME, &dev->flags)) {
+ clear_bit (EVENT_IDLE_RESUME, &dev->flags);
+
+ if(dev->dynamicSuspend || dev->linkDownSuspend){
+ down(&dev->pm_mutex);
+ if(!dev->pmLock){
+ dev->pmLock = TRUE;
+ usb_autopm_get_interface(dev->uintf);
+ }
+ up(&dev->pm_mutex);
+
+ if(operational_mode){
+ if (dev->interrupt) {
+ if(usb_submit_urb (dev->interrupt, GFP_KERNEL) < 0){
+ deverr(dev, "intr submit ");
+ }
+ }
+ }
+ if( (!timer_pending(&dev->LinkPollingTimer))){
+ dev->LinkPollingTimer.expires=jiffies+HZ;
+ add_timer(&(dev->LinkPollingTimer));
+ }
+ dev->StopLinkPolling = FALSE;
+ netif_wake_queue (dev->net);
+ }
+ }
+
+#endif //(LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)) && defined(CONFIG_PM)
+ if (test_bit (EVENT_LINK_RESET, &dev->flags)) {
+ struct driver_info *info = dev->driver_info;
+ int retval = 0;
+
+ clear_bit (EVENT_LINK_RESET, &dev->flags);
+
+ if(info->link_reset && (retval = info->link_reset(dev)) < 0) {
+ devinfo(dev, "link reset failed (%d) usbnet usb-%s-%s, %s",
+ retval,
+ dev->udev->bus->bus_name, dev->udev->devpath,
+ info->description);
+ }
+
+ }
+
+ if (test_bit (EVENT_SET_MULTICAST, &dev->flags)) {
+ struct driver_info *info = dev->driver_info;
+ int retval = 0;
+
+ clear_bit (EVENT_SET_MULTICAST, &dev->flags);
+ if(info->rx_setmulticastlist&& (retval = info->rx_setmulticastlist(dev)) < 0) {
+ devinfo(dev, "Set Multicast failed (%d) usbnet usb-%s-%s, %s",
+ retval,
+ dev->udev->bus->bus_name, dev->udev->devpath,
+ info->description);
+ }
+ }
+
+ if (dev->flags)
+ devdbg (dev, "myevent done, flags = 0x%lx", dev->flags);
+}
+
+/*-------------------------------------------------------------------------*/
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
+static void tx_complete (struct urb *urb, struct pt_regs *regs)
+#else
+static void tx_complete (struct urb *urb)
+#endif
+
+{
+ struct sk_buff *skb = (struct sk_buff *) urb->context;
+ struct skb_data *entry = (struct skb_data *) skb->cb;
+ struct usbnet *dev = entry->dev;
+
+ dev->idleCount = 0;
+
+ if (urb->status == 0) {
+ if(dev->tx_hold_on_completion){
+ if (dev->tx_pending_q.qlen < TX_PENDING_LEN)
+ netif_wake_queue (dev->net);
+
+ dev->stats.tx_packets += entry->pkt_cnt;
+ }else{
+ dev->stats.tx_packets++;
+ }
+ dev->stats.tx_bytes += entry->length;
+ } else {
+
+ dev->stats.tx_errors++;
+
+ switch (urb->status) {
+ case -EPIPE:
+ smscusbnet_defer_kevent (dev, EVENT_TX_HALT);
+ dev->extra_error_cnts.tx_epipe++;
+ break;
+
+ /* software-driven interface shutdown */
+ case -ECONNRESET: // async unlink
+ case -ESHUTDOWN: // hardware gone
+ break;
+
+ // like rx, tx gets controller i/o faults during khubd delays
+ // and so it uses the same throttling mechanism.
+ case -EPROTO: // ehci
+ dev->extra_error_cnts.tx_eproto++;
+ goto _HANDLE;
+ case -ETIMEDOUT: // ohci
+ dev->extra_error_cnts.tx_etimeout++;
+ goto _HANDLE;
+ case -EILSEQ: // uhci
+ dev->extra_error_cnts.tx_eilseq++;
+_HANDLE:
+ if (!timer_pending (&dev->delay)) {
+ mod_timer (&dev->delay,
+ jiffies + THROTTLE_JIFFIES);
+ if (netif_msg_link (dev))
+ devdbg (dev, "tx throttle %d",
+ urb->status);
+ }
+ netif_stop_queue (dev->net);
+ //Set recovery flag
+ set_bit (EVENT_DEV_RECOVERY, &dev->flags);
+
+ break;
+ default:
+ if (netif_msg_tx_err (dev))
+ devdbg (dev, "tx err %d", entry->urb->status);
+ break;
+ }
+ }
+
+ urb->dev = NULL;
+ entry->state = tx_done;
+ defer_bh(dev, skb, &dev->txq);
+}
+
+/*-------------------------------------------------------------------------*/
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29))
+static
+#endif
+void smscusbnet_tx_timeout (struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ unlink_urbs (dev, &dev->txq);
+ tasklet_schedule (&dev->bh);
+
+ // FIXME: device recovery -- reset?
+}
+
+/*-------------------------------------------------------------------------*/
+/*txq.lock will be acquired by caller*/
+static int smscusbnet_bundle_skb_ximt(struct usbnet *dev, struct sk_buff_head *q)
+{
+ unsigned long flags;
+ struct sk_buff *skb = NULL, *skbnext = NULL;
+ int count = 0, pkt_cnt = 0;
+ int SkbSize = 0;
+ struct skb_data *entry;
+ int retval = NET_XMIT_SUCCESS;
+ struct urb *urb = NULL;
+
+ if((struct sk_buff *)q == q->next)return retval;
+
+ spin_lock_irqsave(&q->lock, flags);
+
+ for(skbnext = q->next; skbnext != (struct sk_buff *) q; skbnext = skbnext->next){
+ SkbSize += skbnext->len;
+ pkt_cnt++;
+ }
+ if(SkbSize == 0){
+ spin_unlock_irqrestore(&q->lock, flags);
+ return retval;
+ }
+
+ if(pkt_cnt > 1){
+ skb = dev_alloc_skb(SkbSize);
+
+ if (!skb){
+ devdbg (dev, "smscusbnet_merge_skb, skb is NULL, CX_SkbSize = %d", SkbSize);
+ spin_unlock_irqrestore(&q->lock, flags);
+ return retval;
+ }
+ skb_put(skb, SkbSize);
+ count = 0;
+
+ for(skbnext = q->next; skbnext != (struct sk_buff *) q; skbnext = skbnext->next){
+ memcpy(skb->data + count, skbnext->data, skbnext->len);
+ count += skbnext->len;
+ }
+ while((skbnext = __skb_dequeue(q)) != NULL){
+ kfree_skb(skbnext);
+ }
+
+ }else{ //one packet
+ skb = __skb_dequeue(q);
+ }
+ spin_unlock_irqrestore(&q->lock, flags);
+
+ netif_wake_queue (dev->net);
+
+ if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) {
+ if (netif_msg_tx_err (dev))
+ devdbg (dev, "no urb");
+ retval = NET_XMIT_SUCCESS;
+ dev->stats.tx_dropped += pkt_cnt;
+ if (skb){
+ dev_kfree_skb_any (skb);
+ skb = NULL;
+ }
+
+ goto drop;
+ }else{
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14))
+ urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+
+ entry = (struct skb_data *) skb->cb;
+ entry->urb = urb;
+ entry->dev = dev;
+ entry->state = tx_start;
+ entry->length = SkbSize;
+ entry->pkt_cnt = pkt_cnt;
+
+ usb_fill_bulk_urb (urb, dev->udev, dev->out,
+ skb->data, skb->len, tx_complete, skb);
+ }
+
+ switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) {
+ case -EPIPE:
+ devdbg (dev," in smscusbnet_bundle_skb_ximt,-EPIPE\n");
+ netif_stop_queue (dev->net);
+ smscusbnet_defer_kevent (dev, EVENT_TX_HALT);
+ break;
+ default:
+ if (netif_msg_tx_err (dev))
+ devdbg (dev, "tx: submit urb err %d", retval);
+ break;
+ case 0:
+ dev->net->trans_start = jiffies;
+ __skb_queue_tail (&dev->txq, skb);
+ if (dev->txq.qlen >= TX_QLEN (dev))
+ netif_stop_queue (dev->net);
+ }
+
+ if (retval) {
+ if (netif_msg_tx_err (dev))
+ devdbg (dev, "drop, code %d", retval);
+drop:
+ retval = NET_XMIT_SUCCESS;
+
+ dev->stats.tx_dropped += pkt_cnt;
+ if (skb)
+ dev_kfree_skb_any (skb);
+ usb_free_urb (urb);
+ } else if (netif_msg_tx_queued (dev)) {
+ devdbg (dev, "> tx, len %d, type 0x%x",
+ SkbSize, skb->protocol);
+ }
+
+ return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29))
+static
+#endif
+int smscusbnet_start_xmit (struct sk_buff *skb, struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+ int retval = NET_XMIT_SUCCESS;
+ struct driver_info *info = dev->driver_info;
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18))
+#if defined(CONFIG_PM) && defined(CONFIG_USB_SUSPEND)
+ if(dev->uintf->pm_usage_cnt <= 0){
+ netif_stop_queue (net);
+ smscusbnet_defer_myevent(dev, EVENT_IDLE_RESUME);
+ return NET_XMIT_DROP;
+ }
+#endif //CONFIG_PM && CONFIG_USB_SUSPEND
+#endif
+
+ // some devices want funky USB-level framing, for
+ // win32 driver (usually) and/or hardware quirks
+ if (info->tx_fixup) {
+ skb = info->tx_fixup (dev, skb, GFP_ATOMIC);
+ if (!skb) {
+ if (netif_msg_tx_err (dev))
+ devdbg (dev, "can't tx_fixup skb");
+ retval = NET_XMIT_SUCCESS;
+ dev->stats.tx_dropped++;
+ return NET_XMIT_DROP;
+ }
+ }
+
+ if(dev->tx_hold_on_completion){
+
+ if(dev->device_id == DEV_SMSC7500){
+ if(dev->txq.qlen != 0){
+
+ skb_queue_tail (&dev->tx_pending_q, skb);
+ if (dev->tx_pending_q.qlen >= TX_PENDING_LEN){
+ netif_stop_queue (net);
+ }
+ return retval;
+ }else{
+
+ if(dev->tx_pending_q.qlen != 0){
+ skb_queue_tail (&dev->tx_pending_q, skb);
+ skb = skb_dequeue(&dev->tx_pending_q);
+ if(!skb)return retval;
+ }
+ }
+ retval = smscusbnet_xmit(skb, net);
+ }else{
+ unsigned long flags;
+ skb_queue_tail (&dev->tx_pending_q, skb);
+
+ spin_lock_irqsave (&dev->txq.lock, flags);
+ if(dev->txq.qlen != 0){
+ if (dev->tx_pending_q.qlen >= TX_PENDING_LEN){
+ netif_stop_queue (net);
+ }
+ }else{
+ retval = smscusbnet_bundle_skb_ximt(dev, &dev->tx_pending_q);
+ }
+ spin_unlock_irqrestore (&dev->txq.lock, flags);
+ }
+ }else{
+
+ retval = smscusbnet_xmit(skb, net);
+ }
+
+ return retval;
+}
+
+static int smscusbnet_xmit (struct sk_buff *skb, struct net_device *net)
+{
+ int retval = NET_XMIT_SUCCESS;
+ struct usbnet *dev = netdev_priv(net);
+ struct urb *urb = NULL;
+ struct skb_data *entry;
+ unsigned long flags;
+
+ if (!(urb = usb_alloc_urb (0, GFP_ATOMIC))) {
+ if (netif_msg_tx_err (dev))
+ devdbg (dev, "no urb");
+ goto drop;
+ }
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14))
+ urb->transfer_flags |= URB_ASYNC_UNLINK;
+#endif
+
+ entry = (struct skb_data *) skb->cb;
+ entry->urb = urb;
+ entry->dev = dev;
+ entry->state = tx_start;
+ entry->length = skb->len;
+
+ usb_fill_bulk_urb (urb, dev->udev, dev->out,
+ skb->data, skb->len, tx_complete, skb);
+
+ /* don't assume the hardware handles USB_ZERO_PACKET
+ * NOTE: strictly conforming cdc-ether devices should expect
+ * the ZLP here, but ignore the one-byte packet.
+ *
+ * FIXME zero that byte, if it doesn't require a new skb.
+ */
+ if ((skb->len % dev->maxpacket) == 0) {
+ urb->transfer_buffer_length++;
+ if (skb_tailroom(skb)) {
+ skb->data[skb->len] = 0;
+ __skb_put(skb, 1);
+ }
+ }
+ spin_lock_irqsave (&dev->txq.lock, flags);
+
+ switch ((retval = usb_submit_urb (urb, GFP_ATOMIC))) {
+ case -EPIPE:
+ devdbg (dev," in tx_fixup,-EPIPE\n");
+ netif_stop_queue (net);
+ smscusbnet_defer_kevent (dev, EVENT_TX_HALT);
+ break;
+ default:
+ if (netif_msg_tx_err (dev))
+ devdbg (dev, "tx: submit urb err %d", retval);
+ break;
+ case 0:
+ net->trans_start = jiffies;
+ __skb_queue_tail (&dev->txq, skb);
+
+ if (dev->txq.qlen >= TX_QLEN (dev))
+ netif_stop_queue (net);
+ }
+ spin_unlock_irqrestore (&dev->txq.lock, flags);
+
+ if (retval) {
+ devdbg (dev," in tx_fixup,drop\n");
+ if (netif_msg_tx_err (dev))
+ devdbg (dev, "drop, code %d", retval);
+drop:
+ retval = NET_XMIT_SUCCESS;
+ dev->stats.tx_dropped++;
+ if (skb)
+ dev_kfree_skb_any (skb);
+ usb_free_urb (urb);
+ } else if (netif_msg_tx_queued (dev)) {
+ devdbg (dev, "> tx, len %d, type 0x%x",
+ skb->len, skb->protocol);
+ }
+
+ return retval;
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * USB Device Driver support
+ *
+ *-------------------------------------------------------------------------*/
+
+// precondition: never called in_interrupt
+
+void smscusbnet_disconnect (struct usb_interface *intf)
+{
+ struct usbnet *dev = NULL;
+ struct usb_device *xdev = NULL;
+ struct net_device *net = NULL;
+
+ dev = usb_get_intfdata(intf);
+ usb_set_intfdata(intf, NULL);
+ if (!dev)
+ return;
+
+ xdev = interface_to_usbdev (intf);
+
+ if (netif_msg_probe (dev))
+ devinfo (dev, "unregister '%s' usb-%s-%s, %s",
+ intf->dev.driver->name,
+ xdev->bus->bus_name, xdev->devpath,
+ dev->driver_info->description);
+
+ net = dev->net;
+ unregister_netdev (net);
+
+ devdbg (dev, "smscusbnet_disconnect, rx_urb_pool_head = %d, rx_urb_pool_tail = %d", dev->rx_urb_pool_head, dev->rx_urb_pool_tail);
+
+ if(dev->rx_urb_pool){
+ while(dev->rx_urb_pool_head != dev->rx_urb_pool_tail){
+ if(dev->rx_urb_pool[dev->rx_urb_pool_head])usb_free_urb(dev->rx_urb_pool[dev->rx_urb_pool_head]);
+ dev->rx_urb_pool[dev->rx_urb_pool_head] = NULL;
+ dev->rx_urb_pool_head = (dev->rx_urb_pool_head + 1) % dev->rx_urb_pool_size;
+ }
+ if(dev->rx_urb_pool[dev->rx_urb_pool_head]){
+ usb_free_urb(dev->rx_urb_pool[dev->rx_urb_pool_head]);
+ dev->rx_urb_pool[dev->rx_urb_pool_head] = NULL;
+ }
+ kfree(dev->rx_urb_pool);
+ }
+
+ if(operational_mode && dev->interrupt){
+ usb_free_urb(dev->interrupt);
+ }
+ if(dev->interrupt_urb_buffer){
+ kfree(dev->interrupt_urb_buffer);
+ dev->interrupt_urb_buffer = NULL;
+ }
+
+ if (dev->driver_info->unbind) {
+ devinfo (dev,"unbind usb\n");
+ dev->driver_info->unbind (dev, intf);
+ }
+
+ /* we don't hold rtnl here ... */
+ flush_scheduled_work ();
+ flush_workqueue(dev->MyWorkQueue);
+ destroy_workqueue(dev->MyWorkQueue);
+
+ free_netdev(net);
+ usb_put_dev (xdev);
+
+}
+
+/*-------------------------------------------------------------------------*/
+
+// precondition: never called in_interrupt
+
+int
+smscusbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
+{
+ struct usbnet *dev = NULL;
+ struct net_device *net = NULL;
+ struct usb_host_interface *interface = NULL;
+ struct driver_info *info = NULL;
+ struct usb_device *xdev = NULL;
+ int status = 0;
+ char version[15];
+
+ info = (struct driver_info *) prod->driver_info;
+ if (!info) {
+ return -ENODEV;
+ }
+ xdev = interface_to_usbdev (udev);
+ interface = udev->cur_altsetting;
+
+ usb_get_dev (xdev);
+
+ status = -ENOMEM;
+
+ sprintf(version,"%lX.%02lX.%02lX", (DRIVER_VERSION>>16),(DRIVER_VERSION>>8)&0xFF,(DRIVER_VERSION&0xFFUL));
+ printk("Driver smscusbnet.ko verison %s, built on %s, %s\n",version, __TIME__, __DATE__);
+
+ // set up our own records
+ net = alloc_etherdev(sizeof(*dev));
+ if (!net) {
+ devdbg (dev,"can't kmalloc dev");
+ goto out;
+ }
+ dev = netdev_priv(net);
+
+ init_MUTEX(&dev->pm_mutex);
+
+ dev->udev = xdev;
+ dev->uintf = udev;
+ dev->driver_info = info;
+ dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV
+ | NETIF_MSG_PROBE | NETIF_MSG_LINK);
+ skb_queue_head_init (&dev->rxq);
+ skb_queue_head_init (&dev->txq);
+ skb_queue_head_init (&dev->tx_pending_q);
+ skb_queue_head_init (&dev->done);
+ dev->bh.func = smscusbnet_bh;
+ dev->bh.data = (unsigned long) dev;
+
+ dev->idVendor = prod->idVendor;
+ dev->idProduct = prod->idProduct;
+
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20))
+ INIT_WORK (&dev->kevent, kevent,dev);
+ #else
+ INIT_WORK (&dev->kevent, kevent);
+ #endif
+
+ if(operational_mode)devdbg (dev,"Operational mode enabled\n");
+
+ dev->MyWorkQueue=create_singlethread_workqueue("intr_work");
+ if (!dev->MyWorkQueue) {
+ devdbg (dev,"can't create MyWorkQueue!\n");
+ goto out1;
+ }
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20))
+ INIT_WORK (&dev->myevent, myevent,dev);
+ #else
+ INIT_WORK (&dev->myevent, myevent);
+ #endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24))
+ SET_MODULE_OWNER (net);
+#endif
+ dev->net = net;
+ strcpy (net->name, "usb%d");
+ memcpy (net->dev_addr, node_id, sizeof node_id);
+
+ /* rx and tx sides can use different message sizes;
+ * bind() should set rx_urb_size in that case.
+ */
+ net->hard_header_len += EXTRA_HEADER_LEN; //Reserve extra 8 bytes for control word to eliminate memcpy in tx_fixup()
+ devdbg (dev, "hard_header_len = %d\n", (int)net->hard_header_len);
+ dev->hard_mtu = net->mtu + net->hard_header_len;
+#if 0
+// dma_supported() is deeply broken on almost all architectures
+ // possible with some EHCI controllers
+ if (dma_supported (&udev->dev, DMA_64BIT_MASK))
+ net->features |= NETIF_F_HIGHDMA;
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29))
+ net->change_mtu = smscusbnet_change_mtu;
+ net->get_stats = smscusbnet_get_stats;
+ net->hard_start_xmit = smscusbnet_start_xmit;
+ net->open = smscusbnet_open;
+ net->stop = smscusbnet_stop;
+ net->tx_timeout = smscusbnet_tx_timeout;
+#else
+ net->netdev_ops = &smscusbnet_netdev_ops;
+#endif //linux 2.6.29
+ net->watchdog_timeo = TX_TIMEOUT_JIFFIES;
+
+ // allow device-specific bind/init procedures
+ // NOTE net->name still not usable ...
+ if (info->bind) {
+ status = info->bind (dev, udev);
+
+ if (status < 0)
+ {
+ devdbg (dev,"info->bind,status<0 \n");
+ goto out2;
+ }
+ // heuristic: "usb%d" for links we know are two-host,
+ // else "eth%d" when there's reasonable doubt. userspace
+ // can rename the link if it knows better.
+ if ((dev->driver_info->flags & FLAG_ETHER) != 0
+ && (net->dev_addr [0] & 0x02) == 0)
+ {
+ strcpy (net->name, "eth%d");
+ }
+
+ /* maybe the remote can't receive an Ethernet MTU */
+ if (net->mtu > (dev->hard_mtu - net->hard_header_len))
+ net->mtu = dev->hard_mtu - net->hard_header_len;
+ } else if (!info->in || !info->out)
+ {
+ status = smscusbnet_get_endpoints (dev, udev);
+ }
+ else {
+ dev->in = usb_rcvbulkpipe (xdev, info->in);
+ dev->out = usb_sndbulkpipe (xdev, info->out);
+ if (!(info->flags & FLAG_NO_SETINT))
+ status = usb_set_interface (xdev,
+ interface->desc.bInterfaceNumber,
+ interface->desc.bAlternateSetting);
+ else
+ status = 0;
+
+ }
+ if (status >= 0 && dev->status)
+ {
+ devdbg (dev,"status==0 \n");
+ status = init_status (dev, udev);
+ }
+ if (status < 0)
+ {
+ devdbg (dev,"status<0 \n");
+ goto out3;
+ }
+
+ if (!dev->rx_urb_size)
+ dev->rx_urb_size = dev->hard_mtu;
+
+ devdbg (dev, "rx_urb_size = %d\n", (int)dev->rx_urb_size);
+ dev->StopSummitUrb=1;
+
+ dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1);
+ SET_NETDEV_DEV(net, &udev->dev);
+ status = register_netdev (net);
+
+ if (status)
+ {
+ goto out3;
+ }
+ if (netif_msg_probe (dev))
+ devinfo (dev, "register '%s' at usb-%s-%s, %s, "
+ "%02x:%02x:%02x:%02x:%02x:%02x",
+ udev->dev.driver->name,
+ xdev->bus->bus_name, xdev->devpath,
+ dev->driver_info->description,
+ net->dev_addr [0], net->dev_addr [1],
+ net->dev_addr [2], net->dev_addr [3],
+ net->dev_addr [4], net->dev_addr [5]);
+
+ // ok, it's ready to go.
+ usb_set_intfdata (udev, dev);
+
+ // start as if the link is up
+ netif_device_attach (net);
+
+ return 0;
+
+out3:
+ if (info->unbind)
+ info->unbind (dev, udev);
+out2:
+ if (dev->MyWorkQueue) {
+ destroy_workqueue(dev->MyWorkQueue);
+ }
+out1:
+ free_netdev(net);
+out:
+ usb_put_dev(xdev);
+
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* FIXME these suspend/resume methods assume non-CDC style
+ * devices, with only one interface.
+ */
+
+int smscusbnet_FreeQueue (struct usbnet *dev)
+{
+ DECLARE_WAIT_QUEUE_HEAD (unlink_wakeup);
+ DECLARE_WAITQUEUE (wait, current);
+
+ /* accelerate emptying of the rx and queues, to avoid
+ * having everything error out.
+ */
+ // ensure there are no more active urbs
+ add_wait_queue (&unlink_wakeup, &wait);
+ dev->wait = &unlink_wakeup;
+ (void) unlink_urbs (dev, &dev->rxq);
+ (void) unlink_urbs (dev, &dev->txq);
+
+ // maybe wait for deletions to finish.
+ while (!skb_queue_empty(&dev->rxq) &&
+ !skb_queue_empty(&dev->txq) &&
+ !skb_queue_empty(&dev->done)) {
+ msleep(UNLINK_TIMEOUT_MS);
+ }
+ dev->wait = NULL;
+ remove_wait_queue (&unlink_wakeup, &wait);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* FIXME these suspend/resume methods assume non-CDC style
+ * devices, with only one interface.
+ */
+ #ifndef pm_message_t
+#define pm_message_t u32
+#endif
+int smscusbnet_suspend (struct usb_interface *intf, pm_message_t state)
+{
+ struct usbnet *dev = usb_get_intfdata(intf);
+
+ /* accelerate emptying of the rx and queues, to avoid
+ * having everything error out.
+ */
+ netif_device_detach (dev->net);
+
+ (void) unlink_urbs (dev, &dev->txq);
+ (void) unlink_urbs (dev, &dev->rxq);
+
+ return 0;
+}
+
+int smscusbnet_resume (struct usb_interface *intf)
+{
+ struct usbnet *dev = usb_get_intfdata(intf);
+ netif_device_attach (dev->net);
+ tasklet_schedule (&dev->bh);
+ return 0;
+}
+
+EXPORT_SYMBOL_GPL(smscusbnet_IsOperationalMode);
+EXPORT_SYMBOL_GPL(smscusbnet_get_endpoints);
+EXPORT_SYMBOL_GPL(smscusbnet_skb_return);
+EXPORT_SYMBOL_GPL(smscusbnet_defer_kevent);
+EXPORT_SYMBOL_GPL(smscusbnet_defer_myevent);
+EXPORT_SYMBOL_GPL(smscusbnet_disconnect);
+EXPORT_SYMBOL_GPL(smscusbnet_probe);
+EXPORT_SYMBOL_GPL(smscusbnet_FreeQueue);
+EXPORT_SYMBOL_GPL(smscusbnet_linkpolling);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29))
+EXPORT_SYMBOL(smscusbnet_stop);
+EXPORT_SYMBOL(smscusbnet_get_stats);
+EXPORT_SYMBOL(smscusbnet_open);
+EXPORT_SYMBOL(smscusbnet_start_xmit);
+EXPORT_SYMBOL(smscusbnet_tx_timeout);
+EXPORT_SYMBOL(smscusbnet_change_mtu);
+#endif
+/*-------------------------------------------------------------------------*/
+
+static int __init smscusbnet_init(void)
+{
+ /* compiler should optimize this out */
+ BUG_ON (sizeof (((struct sk_buff *)0)->cb)
+ < sizeof (struct skb_data));
+
+ random_ether_addr(node_id);
+ return 0;
+}
+module_init(smscusbnet_init);
+
+static void __exit smscusbnet_exit(void)
+{
+}
+module_exit(smscusbnet_exit);
+
+MODULE_AUTHOR("David Brownell");
+MODULE_DESCRIPTION("USB network driver framework");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/smscusbnet.h b/drivers/net/usb/smscusbnet.h
new file mode 100644
index 000000000000..bef2fb96886d
--- /dev/null
+++ b/drivers/net/usb/smscusbnet.h
@@ -0,0 +1,318 @@
+/*
+ * USB Networking Link Interface
+ *
+ * Copyright (C) 2000-2005 by David Brownell <dbrownell@users.sourceforge.net>
+ * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ *
+ * 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 __SMSCUSBNET_H
+#define __SMSCUSBNET_H
+
+#ifndef DEBUG
+#define DEBUG
+#endif
+
+typedef unsigned char BOOLEAN;
+#define TRUE ((BOOLEAN)1)
+#define FALSE ((BOOLEAN)0)
+
+#define ID_REV_9500_CHIPID 0x9500
+#define ID_REV_9500A_CHIPID 0x9E00
+#define ID_REV_9512_CHIPID 0xEC00
+#define ID_REV_7500_CHIPID 0x7500
+
+#define EXTRA_HEADER_LEN 8
+#define VLAN_DUMMY 0xFFFF
+
+enum{
+ DEV_SMSC9500,
+ DEV_SMSC7500
+};
+
+//#define RX_SKB_COPY
+enum{
+ RX_FIXUP_VALID_SKB,
+ RX_FIXUP_INVALID_SKB,
+ RX_FIXUP_ERROR,
+};
+
+struct ExtraErrorCounter
+{
+ u32 tx_epipe;
+ u32 tx_eproto;
+ u32 tx_etimeout;
+ u32 tx_eilseq;
+ u32 tx_eoverflow;
+
+ u32 rx_epipe;
+ u32 rx_eproto;
+ u32 rx_etimeout;
+ u32 rx_eilseq;
+ u32 rx_eoverflow;
+
+};
+
+#define AUTOSUSPEND_DYNAMIC 0x01 //Suspend on s0, work for lan9500 and lan9500a
+#define AUTOSUSPEND_DYNAMIC_S3 0x02 //Suspend on s3, only for lan9500a
+#define AUTOSUSPEND_LINKDOWN 0x04 //Suspend when link is down
+#define AUTOSUSPEND_INTFDOWN 0x08 //Suspend when interface is down
+#define AUTOSUSPEND_DETACH 0x10 //Enable smart detach
+
+enum{
+ FEATURE_SUSPEND3,
+ FEATURE_SMARTDETACH,
+ FEATURE_NEWSTATIS_CNT,
+ FEATURE_SEP_LEDS,
+ FEATURE_WUFF_8,
+ FEATURE_MAX_NO
+};
+
+enum{
+ EVENT_TX_HALT,
+ EVENT_RX_HALT,
+ EVENT_RX_MEMORY,
+ EVENT_STS_SPLIT,
+ EVENT_LINK_RESET,
+ EVENT_SET_MULTICAST,
+ EVENT_HAS_FRAME,
+ EVENT_DEV_RECOVERY,
+ EVENT_IDLE_CHECK,
+ EVENT_IDLE_RESUME,
+ EVENT_LINK_DOWN,
+};
+
+/* interface from usbnet core to each USB networking link we handle */
+struct usbnet {
+ /* housekeeping */
+ struct usb_device *udev;
+ struct usb_interface *uintf;
+ struct driver_info *driver_info;
+ wait_queue_head_t *wait;
+ int StopSummitUrb;
+
+ /* i/o info: pipes etc */
+ unsigned in, out;
+ struct usb_host_endpoint *status;
+ unsigned maxpacket;
+ struct timer_list delay;
+ struct timer_list LinkPollingTimer;
+ BOOLEAN StopLinkPolling;
+ u16 idProduct; //Product id from device descriptor
+ u16 idVendor; //Vendor id from device descriptor
+ u32 linkDownSuspend; //1. auto-negotiation complete; 2. energy detection;
+ u32 dynamicSuspend; //Enable dynamic suspend when there are no traffic
+ u32 smartDetach; //Enable smart detach, only for LAN9500A
+
+ /* protocol/interface state */
+ struct net_device *net;
+ struct net_device_stats stats;
+ struct ExtraErrorCounter extra_error_cnts;
+ int msg_enable;
+ unsigned long data [5];
+ u32 xid;
+ u32 hard_mtu; /* count any extra framing */
+ size_t rx_urb_size; /* size for rx urbs */
+ struct mii_if_info mii;
+
+ /* various kinds of pending driver work */
+ struct sk_buff_head rxq;
+ struct sk_buff_head txq;
+ struct sk_buff_head tx_pending_q;
+ struct sk_buff_head done;
+
+ struct urb **rx_urb_pool; //idle urb pool
+ unsigned int rx_urb_pool_head;
+ unsigned int rx_urb_pool_tail;
+ unsigned int rx_urb_pool_size;
+ spinlock_t rx_urblist_lock;
+
+ struct urb *tx_urb;
+
+ struct urb *interrupt;
+ void *interrupt_urb_buffer;
+ struct tasklet_struct bh;
+
+ struct work_struct kevent;
+ struct work_struct myevent;
+ struct workqueue_struct * MyWorkQueue;
+ unsigned long flags;
+ BOOLEAN intr_urb_delay_submit;
+
+ int idleCount;
+ int linkcheck;
+ BOOLEAN pmLock;
+ struct semaphore pm_mutex;
+ int suspendFlag; //Flag indicates link down suspend and select suspend
+ u32 chipID;
+ u32 preRxFifoDroppedFrame; //Previous counter value
+
+ int chipDependFeatures[FEATURE_MAX_NO]; //Flag for chip-depend feratures
+ struct vlan_group *vlgrp; //vlan support
+
+ int device_id; //DEV_SMSC9500 or DEV_SMSC7500
+ int tx_hold_on_completion;
+};
+#define PM_IDLE_DELAY 3 //Time before auto-suspend
+enum{
+ WAKEPHY_OFF,
+ WAKEPHY_NEGO_COMPLETE,
+ WAKEPHY_ENERGY
+};
+
+static inline struct usb_driver *driver_of(struct usb_interface *intf)
+{
+ return to_usb_driver(intf->dev.driver);
+}
+/* interface from the device/framing level "minidriver" to core */
+struct driver_info {
+ char *description;
+
+ int flags;
+/* framing is CDC Ethernet, not writing ZLPs (hw issues), or optionally: */
+#define FLAG_FRAMING_NC 0x0001 /* guard against device dropouts */
+#define FLAG_FRAMING_GL 0x0002 /* genelink batches packets */
+#define FLAG_FRAMING_Z 0x0004 /* zaurus adds a trailer */
+#define FLAG_FRAMING_RN 0x0008 /* RNDIS batches, plus huge header */
+
+#define FLAG_NO_SETINT 0x0010 /* device can't set_interface() */
+#define FLAG_ETHER 0x0020 /* maybe use "eth%d" names */
+
+#define FLAG_FRAMING_AX 0x0040 /* AX88772/178 packets */
+
+ /* init device ... can sleep, or cause probe() failure */
+ int (*bind)(struct usbnet *, struct usb_interface *);
+
+ /* cleanup device ... can sleep, but can't fail */
+ void (*unbind)(struct usbnet *, struct usb_interface *);
+
+ /* reset device ... can sleep */
+ int (*reset)(struct usbnet *);
+
+ /* see if peer is connected ... can sleep */
+ int (*check_connect)(struct usbnet *);
+
+ /* for status polling */
+ void (*status)(struct usbnet *, struct urb *);
+ /* Set max frame size*/
+ int (*set_max_frame_size)(struct usbnet *, int size);
+ /* link reset handling, called from defer_kevent */
+ int (*link_reset)(struct usbnet *);
+
+ /* fixup rx packet (strip framing) */
+ int (*rx_fixup)(struct usbnet *dev, struct sk_buff *skb);
+
+ /*set multicast list */
+ int (*rx_setmulticastlist) (struct usbnet *dev);
+
+ /* fixup tx packet (add framing) */
+ struct sk_buff *(*tx_fixup)(struct usbnet *dev,
+ struct sk_buff *skb, int flags);
+
+ /* for new devices, use the descriptor-reading code instead */
+ int in; /* rx endpoint */
+ int out; /* tx endpoint */
+
+ unsigned long data; /* Misc driver specific data */
+
+};
+
+/* Minidrivers are just drivers using the "usbnet" core as a powerful
+ * network-specific subroutine library ... that happens to do pretty
+ * much everything except custom framing and chip-specific stuff.
+ */
+extern int smscusbnet_probe(struct usb_interface *, const struct usb_device_id *);
+extern void smscusbnet_disconnect(struct usb_interface *);
+extern void smscusbnet_linkpolling(unsigned long ptr);
+
+/* Drivers that reuse some of the standard USB CDC infrastructure
+ * (notably, using multiple interfaces according to the the CDC
+ * union descriptor) get some helper code.
+ */
+struct cdc_state {
+ struct usb_cdc_header_desc *header;
+ struct usb_cdc_union_desc *u;
+ struct usb_cdc_ether_desc *ether;
+ struct usb_interface *control;
+ struct usb_interface *data;
+};
+
+extern int usbnet_generic_cdc_bind (struct usbnet *, struct usb_interface *);
+extern void usbnet_cdc_unbind (struct usbnet *, struct usb_interface *);
+
+/* CDC and RNDIS support the same host-chosen packet filters for IN transfers */
+#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \
+ |USB_CDC_PACKET_TYPE_ALL_MULTICAST \
+ |USB_CDC_PACKET_TYPE_PROMISCUOUS \
+ |USB_CDC_PACKET_TYPE_DIRECTED)
+
+
+/* we record the state for each of our queued skbs */
+enum skb_state {
+ illegal = 0,
+ tx_start, tx_done,
+ rx_start, rx_done, rx_cleanup
+};
+
+struct skb_data { /* skb->cb is one of these */
+ struct urb *urb;
+ struct usbnet *dev;
+ enum skb_state state;
+ size_t length;
+ size_t pkt_cnt;
+};
+
+extern int smscusbnet_IsOperationalMode(struct usbnet *);
+extern int smscusbnet_get_endpoints(struct usbnet *, struct usb_interface *);
+extern void smscusbnet_defer_kevent (struct usbnet *, int);
+extern void smscusbnet_defer_myevent (struct usbnet *, int);
+extern void smscusbnet_skb_return (struct usbnet *, struct sk_buff *);
+extern int summit_IntrUrb (struct usbnet *dev);
+
+extern u32 smscusbnet_get_msglevel (struct net_device *);
+extern void smscusbnet_set_msglevel (struct net_device *, u32);
+extern void smscusbnet_get_drvinfo (struct net_device *, struct ethtool_drvinfo *);
+extern int smscusbnet_FreeQueue (struct usbnet *dev);
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29))
+extern int smscusbnet_stop (struct net_device *net);
+extern struct net_device_stats *smscusbnet_get_stats (struct net_device *net);
+extern int smscusbnet_open (struct net_device *net);
+extern int smscusbnet_start_xmit (struct sk_buff *skb, struct net_device *net);
+extern int smscusbnet_change_mtu (struct net_device *net, int new_mtu);
+extern void smscusbnet_tx_timeout (struct net_device *net);
+#endif
+/* messaging support includes the interface name, so it must not be
+ * used before it has one ... notably, in minidriver bind() calls.
+ */
+#ifdef DEBUG
+#define devdbg(usbnet, fmt, arg...) \
+ printk(KERN_DEBUG "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
+#else
+#define devdbg(usbnet, fmt, arg...) do {} while(0)
+#endif
+
+#define deverr(usbnet, fmt, arg...) \
+ printk(KERN_ERR "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
+#define devwarn(usbnet, fmt, arg...) \
+ printk(KERN_WARNING "%s: " fmt "\n" , (usbnet)->net->name , ## arg)
+
+#define devinfo(usbnet, fmt, arg...) \
+ printk(KERN_INFO "%s: " fmt "\n" , (usbnet)->net->name , ## arg); \
+
+
+#endif /* __smscusbnet_H */
diff --git a/drivers/net/usb/version.h b/drivers/net/usb/version.h
new file mode 100644
index 000000000000..f085e4759ce1
--- /dev/null
+++ b/drivers/net/usb/version.h
@@ -0,0 +1,6 @@
+#ifndef SMSC9500_VERSION_H_
+#define SMSC9500_VERSION_H_
+
+#define DRIVER_VERSION (0x01010604UL)
+
+#endif /*SMSC9500_VERSION_H_*/