--- linux/drivers/net/Config.in Fri Aug 6 16:43:26 1999 +++ linux/drivers/net/Config.in.patched Fri Aug 6 14:07:48 1999 @@ -58,6 +58,7 @@ fi tristate '3c509/3c579 support' CONFIG_EL3 tristate '3c515 ISA Fast EtherLink' CONFIG_3C515 + tristate '3c90x/3c980 B/C series "Cyclone/Hurricane/Tornado" support' CONFIG_BC90X tristate '3c590/3c900 series (592/595/597) "Vortex/Boomerang" support' CONFIG_VORTEX fi tristate 'AMD LANCE and PCnet (AT1500 and NE2100) support' CONFIG_LANCE --- linux/drivers/net/Makefile Fri Aug 6 16:43:26 1999 +++ linux/drivers/net/Makefile.patched Fri Aug 6 14:07:48 1999 @@ -510,6 +510,14 @@ endif endif +ifeq ($(CONFIG_BC90X),y) +L_OBJS += 3c90x.o +else + ifeq ($(CONFIG_BC90X),m) + M_OBJS += 3c90x.o + endif +endif + ifeq ($(CONFIG_VORTEX),y) L_OBJS += 3c59x.o else --- linux/drivers/net/Space.c Fri Aug 6 16:43:26 1999 +++ linux/drivers/net/Space.c.patched Fri Aug 6 14:07:49 1999 @@ -81,6 +81,7 @@ extern int sonic_probe(struct device *); extern int SK_init(struct device *); extern int seeq8005_probe(struct device *); +extern int tc90xbc_probe(struct device *); extern int tc59x_probe(struct device *); extern int dgrs_probe(struct device *); extern int smc_init( struct device * ); @@ -173,6 +174,9 @@ #endif #ifdef CONFIG_RCPCI {rcpci_probe, 0}, +#endif +#ifdef CONFIG_BC90X + {tc90xbc_probe, 0}, #endif #ifdef CONFIG_VORTEX {tc59x_probe, 0}, --- /dev/null Tue May 5 13:32:27 1998 +++ linux/drivers/net/3c90x.c Thu Oct 7 05:26:52 1999 @@ -0,0 +1,7230 @@ +#include "3c90x.h" + +#ifndef NOSTATIC +static +#endif +char *version = + "3Com 3c90x Version 1.0.0i 1999 \n"; + +/* + 3Com EtherLink 10/100 PCI (3C90x) Linux Network Driver, Copyright (c) 1999 + 3Com Corporation. All rights reserved. + 3Com Linux Network Driver software is distributed as is, without any warranty + of any kind, either express or implied as further specified in the GNU Public + License. This software may be used and distributed according to the terms of + the GNU Public License, located in the file LICENSE. + + 3Com and EtherLink are registered trademarks of 3Com Corporation. Linux is a + registered trademarks of Linus Torvalds. + + + Credit + ------ + Special thanks goes to Donald Becker for providing the skeleton driver outline + used in this driver, and for the hard work he has put into the 3c59x EtherLink + The 3Com 3c90x driver works in cooperatioon with his 3c59x driver. + + skeleton.c: A network driver out line for Linux. + Copyright 1993 Uninted States Government as represented by the Director, National + Security Agency. + + Donald Becker may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O Center of + Excellence in Space Data and Information Sciences Code 930.5, Goddard Space Flight + Center, Greenbelt MD 20771 + http://cesdis.gsfc.nasa.gov/linux/ + + + This is 3Com's EtherLink PCI driver for Linux. It provides support for the + 3c90x and 3c980 network adapters listed here: + + EtherLink 10/100 PCI NICs + 3C905C Family and 3C920 ASICs EtherLink 10/100 PCI including + the -TX and -TX-M + 3C905B Family and 3C918 ASICs EtherLink 10/100 PCI including + the -TX -TX-M and -TX-NM + 3C905B-COMBO EtherLink 10/100 PCI COMBO + 3C905B-T4 EtherLink 10/100 PCI T4 + + EtherLink Server 10/100 PCI NICs + 3C980C-TX EtherLink Server 10/100 PCI + 3C980B-TX EtherLink Server 10/100 PCI + 3C980-TX EtherLink Server 10/100 PCI + + EtherLink 100 PCI NIC + 3C905B-FX EtherLink 100 PCI Fiber + + EtherLink 10 PCI NICs + 3C900B-TPO EtherLink 10 PCI TPO + 3C900B-TPC EtherLink 10 PCI TPC + 3C900B-COMBO EtherLink 10 PCI COMBO + 3C900B-FL EtherLink 10 PCI Fiber + + E-mail Support: + + - USA or Canada: 3COM_US_NIC_FAMILY@3COM.COM + - Mexico and Latin America: AMI_HD@3com.com + - Brazil: br-nicsupport@3com.com + - Europe, Middle East and Africa: European_Technical_Support@3com.com + - Asia Pacific Rim: apr_technical_support@3com.com + + URL: http://support.3com.com/infodeli/tools/nic/linux.htm + + + Compile command : + + gcc -c 3c90x.c -O2 -Wall -Wstrict-prototypes -fomit-frame-pointer \ + -fno-strength-reduce -pipe -m486 -malign-loops=2 \ + -malign-jumps=2 -malign-functions=2 -DCPU=486 -DMODULE -D__KERNEL__ + + + Add -D__SMP__ to the command line for SMP support + + Add -DMODVERSIONS to the command line if your kernel was built with + symbol versioning (RedHat, etc.) + + + + c-indent-level: 4 + c-basic-offset: 4 + tab-width: 8 + */ + + + +#include + +#if defined(MODULE) && (LINUX_VERSION_CODE >= 0x20115) + +MODULE_AUTHOR("3Com Corporation "); +MODULE_DESCRIPTION("EtherLink PCI Driver"); + +#ifdef DEBUG +MODULE_PARM(debug, "i"); +#endif + +MODULE_PARM(switchdelay, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(downpoll, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(flowcontrol, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(media_select, "1-" __MODULE_STRING(MAX_UNITS) "i"); +MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i"); + +#endif // MODULE && LINUX_VERSION_CODE + +char kernel_version [] = UTS_RELEASE; + +static const INT MTU = 1500; +PCHAR ProductName = "3Com EtherLink PCI NIC\n"; + +static ULONG tc90x_Index = 0; + +#ifndef NOSTATIC +static +#endif +UCHAR BroadcastAddr[] = {0xff,0xff,0xff,0xff,0xff,0xff}; + +// Global variable for waittimer +TIMER WaitTimer; +BOOLEAN InWaitTimer; +BOOLEAN DCConverterEnabledState_g; +ULONG TimeOutCount = 0; +USHORT MediaStatus_g = 0; +BOOLEAN PhyResponding_g; +USHORT PhyStatus_g; +ULONG DownListPointer_g; +ULONG UpListPointer_g; +ULONG portValue_g; +ULONG dmaControl_g; + + +PDEVICE RootNICDevice = NULL; + +#ifdef MODULE + +/*++ + +Routine Name: + + init_module + +Routine Description: + + This routine finds the adapter using the Linux calls + +Arguments: + + Device - Pointer to the device structure. + +Return Value: + + -ENODEV if no adapter found + noAdapterFound if adapter(s) found + +--*/ + +INT +init_module(VOID) +{ + NIC_STATUS nicStatus; + DBGPRINT_INIT((KERN_CRIT "init_module: IN\n")); + + nicStatus = tc90xbc_ScanDevices(0); + + if (NIC_STATUS_SUCCESS == nicStatus) { + + DBGPRINT_FUNCTION((KERN_CRIT "init_module: OUT\n")); + printk(version); + return 0; + + } + DBGPRINT_ERROR((KERN_CRIT "Scan Devices failed\n")); + DBGPRINT_FUNCTION((KERN_CRIT "init_module: OUT\n")); + return -ENODEV; + +} + +#else + +/*++ + +Routine Name: + + tc90xbc_probe + +Routine Description: + + This routine finds the adapter using the Linux calls + +Arguments: + + Device - Pointer to the device structure. + +Return Value: + + -ENODEV if no adapter found + noAdapterFound if adapter(s) found + +--*/ + +INT +tc90xbc_probe( + IN PDEVICE Device + ) +{ + static int scanned = 0; + NIC_STATUS nicStatus; + + DBGPRINT_INIT((KERN_CRIT "tc90xbc_probe: IN\n")); + + if(scanned++) + return -ENODEV; + printk(KERN_INFO "%s", version); + + nicStatus = tc90xbc_ScanDevices(Device); + + if (NIC_STATUS_SUCCESS == nicStatus) { + + DBGPRINT_FUNCTION(( + KERN_CRIT"NICScanDevices: OUT-success\n")); + return 0; + } + else { + + DBGPRINT_ERROR(( + KERN_CRIT "NICScanDevices returned error\n")); + return -ENODEV; + } +} + +#endif + + +NIC_STATUS +tc90xbc_ScanDevices( + IN PDEVICE Device + ) +{ + PNIC_INFORMATION pAdapter = NULL; + INT ioBaseAddress; + USHORT pciCommand, vendorId, deviceId; + INT interruptVector, noAdapterFound = 0; + UCHAR pciBus, pciDeviceFunction; + static INT pciIndex = 0; + UCHAR cacheLineSize, revisionId; + USHORT powerManagementControl; + + DBGPRINT_INIT((KERN_CRIT "tc90xbc_ScanDevices: IN\n")); + + for (; pciIndex < 0xff; pciIndex++) { + + if (pcibios_find_class( + PCI_CLASS_NETWORK_ETHERNET << 8, + pciIndex, + &pciBus, + &pciDeviceFunction + ) != PCIBIOS_SUCCESSFUL) + break; + + pcibios_read_config_word( + pciBus, + pciDeviceFunction, + PCI_VENDOR_ID, + &vendorId); + + if (vendorId != NIC_VENDOR_ID) + continue; + + pcibios_read_config_word( + pciBus, + pciDeviceFunction, + PCI_DEVICE_ID, + &deviceId); + + switch (deviceId) { + + case NIC_PCI_DEVICE_ID_9055: + DBGPRINT_INIT(("10/100 Base-TX NIC found\n")); + break; + + case NIC_PCI_DEVICE_ID_9058: + DBGPRINT_INIT(("10/100 COMBO Deluxe board found\n")); + break; + + case NIC_PCI_DEVICE_ID_9004: + DBGPRINT_INIT(("10Base-T TPO NIC found\n")); + break; + + case NIC_PCI_DEVICE_ID_9005: + DBGPRINT_INIT(("10Base-T/10Base-2/AUI Combo found\n")); + + case NIC_PCI_DEVICE_ID_9006: + DBGPRINT_INIT(("10Base-T/10Base-2/TPC found\n")); + break; + + case NIC_PCI_DEVICE_ID_900A: + DBGPRINT_INIT(("10Base-FL NIC found\n")); + break; + + case NIC_PCI_DEVICE_ID_905A: + DBGPRINT_INIT(("100Base-Fx NIC found\n")); + break; + + case NIC_PCI_DEVICE_ID_9200: + DBGPRINT_INIT(("Tornado NIC found\n")); + break; + + case NIC_PCI_DEVICE_ID_9800: + DBGPRINT_INIT(("10/100 Base-TX NIC(Python-H) found\n")); + break; + + + case NIC_PCI_DEVICE_ID_9805: + DBGPRINT_INIT(("10/100 Base-TX NIC(Python-T) found\n")); + break; + + case NIC_PCI_DEVICE_ID_4500: + DBGPRINT_INIT(("10/100 Base-TX NIC(Home Network) found\n")); + break; + + case NIC_PCI_DEVICE_ID_7646: + DBGPRINT_INIT(("10/100 Base-TX NIC(SOHO) found\n")); + break; + + default: + DBGPRINT_INIT(("UnSupported NIC found\n")); + continue; + } + // + // Initialize the ether device. + // + Device = init_etherdev(Device, 0); + DBGPRINT_INIT(( + KERN_CRIT "3Com device %s\n",Device->name)); + Device->priv = kmalloc(sizeof(NIC_INFORMATION), GFP_KERNEL); + memset(Device->priv, 0, sizeof(NIC_INFORMATION)); + pAdapter = (PNIC_INFORMATION)Device->priv; + pAdapter->Device = Device; + // + // Save the NIC index. + // + pAdapter->Index = tc90x_Index++; + + pcibios_read_config_word( + pciBus, + pciDeviceFunction, + PCI_COMMAND, + &pciCommand); + + if (!(pciCommand & PCI_COMMAND_MASTER)) { + + DBGPRINT_INIT(("Enabling Bus Matering\n")); + pciCommand |= PCI_COMMAND_MASTER; + } + else { + DBGPRINT_INIT(("Bus Mastering enabled by BIOS\n")); + } + { + +#if LINUX_VERSION_CODE >= 0x20155 + struct pci_dev *pdev = pci_find_slot( + pciBus, + pciDeviceFunction + ); + ioBaseAddress = pdev->base_address[0]; + interruptVector = pdev->irq; +#else + u32 pciIoAddress; + u8 pciIrqLine; + + pcibios_read_config_byte( + pciBus, + pciDeviceFunction, + PCI_INTERRUPT_LINE, + &pciIrqLine); + + pcibios_read_config_dword( + pciBus, + pciDeviceFunction, + PCI_BASE_ADDRESS_0, + &pciIoAddress); + + ioBaseAddress = pciIoAddress; + interruptVector = pciIrqLine; +#endif + } + ioBaseAddress &= ~1; + // + //Check that IO range is free. + // + if (check_region(ioBaseAddress, 0x40)) { + + DBGPRINT_ERROR(("NICScan: check_region failed\n")); + continue; + } + DBGPRINT_INIT(("Irq = %x , IoAddress = %x\n", + interruptVector, + ioBaseAddress)); + + pcibios_read_config_byte( + pciBus, + pciDeviceFunction, + PCI_REVISION_ID, + &revisionId); + + pcibios_read_config_byte( + pciBus, + pciDeviceFunction, + PCI_CACHE_LINE_SIZE, + &cacheLineSize); + + powerManagementControl = PCI_PME_STATUS | + PCI_POWER_STATE_D0; + + + pcibios_write_config_word( + pciBus, + pciDeviceFunction, + PCI_POWER_CONTROL, + powerManagementControl); + + pcibios_write_config_dword( + pciBus, + pciDeviceFunction, + PCI_BASE_ADDRESS_0, + ioBaseAddress); + + pcibios_write_config_byte( + pciBus, + pciDeviceFunction, + PCI_INTERRUPT_LINE, + interruptVector); + + pcibios_write_config_byte( + pciBus, + pciDeviceFunction, + PCI_CACHE_LINE_SIZE, + cacheLineSize); + + pcibios_write_config_word( + pciBus, + pciDeviceFunction, + PCI_COMMAND, + pciCommand); + + pAdapter->Hardware.CacheLineSize = cacheLineSize * 4; + + if ((pAdapter->Hardware.CacheLineSize % 0x10) || + (!pAdapter->Hardware.CacheLineSize)) { + + DBGPRINT_ERROR(( + "tc90xbc_Scan: Cacheline size wrong\n")); + + pAdapter->Hardware.CacheLineSize = 0x20; + } + // + // Save the variables in adapter structure + // + pAdapter->IoBaseAddress = ioBaseAddress; + pAdapter->PCI.IoBaseAddress = ioBaseAddress; + pAdapter->PCI.InterruptVector = interruptVector; + + pAdapter->Hardware.RevisionId = revisionId; + pAdapter->Hardware.DeviceId = deviceId; + +#ifdef __SMP__ + spin_lock_init(&pAdapter->SpinLock_m); // set multicast + spin_lock_init(&pAdapter->SpinLock_send); + spin_lock_init(&pAdapter->SpinLock_int); + spin_lock_init(&pAdapter->SpinLock_misc); //recv mode/close/timer +#endif + // + // Fill the device structure + // + if (tc90x_FillDeviceStructure(pAdapter) != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(( + KERN_CRIT "FillDeviceStructure failed\n")); + tc90x_FreeAdapterResources(pAdapter); + continue; + } + + if (tc90x_ReadCommandLineChanges(pAdapter) != + NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(( + KERN_CRIT "ReadCommandLineChanges failed\n" + )); + tc90x_FreeAdapterResources(pAdapter); + continue; + } + // + // Allocate Shared memory + // + if (tc90x_AllocateSharedMemory(pAdapter) != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(( + "tc90x_AllocateSharedMemory failed\n" + )); + tc90x_FreeAdapterResources(pAdapter); + continue; + } + // + // Reserve the io range. + // + request_region( + Device->base_addr, + 0x80, + ProductName); + + pAdapter->ResourcesReserved |= NIC_IO_PORT_REGISTERED; + // + // Set the root device and the next device. + // + ((PNIC_INFORMATION)(Device->priv))->NextDevice = RootNICDevice; + ((PNIC_INFORMATION)(Device->priv))->Device = Device; + RootNICDevice = Device; + + Device = 0; + noAdapterFound++; + } + + + DBGPRINT_FUNCTION((KERN_CRIT "tc90xbc_ScanDevices: OUT\n")); + + if (noAdapterFound) { + + DBGPRINT_INITIALIZE(("NoAdapter = %x\n", noAdapterFound)); + return NIC_STATUS_SUCCESS; + } + else + return NIC_STATUS_FAILURE; +} + + + +/*++ + +Routine Name: + + tc90x_FillDeviceStructure + +Routine Description: + + This routine fills the device structure + +Arguments: + + Device - Pointer to the device structure. + IoBaseAddress - IoBase address for the adapter. + Irq - Irq of the adapter. + + +Return Value: + + -ENODEV if no adapter found + noAdapterFound if adapter(s) found + +--*/ + +NIC_STATUS +tc90x_FillDeviceStructure( + IN PNIC_INFORMATION Adapter + ) +{ + + PNIC_INFORMATION pAdapter = Adapter; + PDEVICE device = pAdapter->Device; + + DBGPRINT_INIT(("FillDeviceStructure: IN\n")); + device->base_addr = pAdapter->PCI.IoBaseAddress; + device->irq = pAdapter->PCI.InterruptVector; + device->mtu = MTU; + // + // Set the routine addresses + // + device->open = &NICOpen; + device->hard_start_xmit = &NICSendPacket; + device->stop = &NICClose; + device->get_stats = &NICGetStatistics; + device->do_ioctl = &NICIoctl; +#ifdef NEW_MULTICAST + device->set_multicast_list = &NICSetReceiveMode; +#else + device->set_multicast_list = &NICSetMulticastList; +#endif + DBGPRINT_INIT(("FillDeviceStructure: OUT\n")); + return NIC_STATUS_SUCCESS; +} + + +/*++ + +Routine Name: + + NICOpen + +Routine Description: + + This routine opens the interface. + +Arguments: + + Device - Pointer to the device structure. + +Return Value: + + 0 if SUCCESS + -ENODEV if FAILURE + +--*/ + + +INT +NICOpen( + IN PDEVICE Device + ) + +{ + PNIC_INFORMATION pAdapter = (PNIC_INFORMATION)Device->priv; + + DBGPRINT_FUNCTION(("New NICOpen: IN\n")); + // + // Initialize general wait timer used during driver init + // + init_timer(&WaitTimer); + WaitTimer.data = (ULONG)Device; + WaitTimer.function = &WaitTimerHandler; + pAdapter->WaitCases = NONE; + add_timer(&WaitTimer); + InWaitTimer = TRUE; + pAdapter->ResourcesReserved |= WAIT_TIMER_REGISTERED; + +#if LINUX_VERSION_CODE >= 0x20200 + pAdapter->SpinLock_int = (spinlock_t) SPIN_LOCK_UNLOCKED; + pAdapter->SpinLock_send = (spinlock_t) SPIN_LOCK_UNLOCKED; + pAdapter->SpinLock_misc = (spinlock_t) SPIN_LOCK_UNLOCKED; + pAdapter->SpinLock_m = (spinlock_t) SPIN_LOCK_UNLOCKED; +#endif + // + // Register IOBase and Irq with the OS + // + if (tc90x_RegisterAdapter(pAdapter) != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(("NICOpen: RegisterAdapter failed\n")); + DBGPRINT_FUNCTION(("NICOpen: Out with ERROR\n")); + tc90x_FreeAdapterResources(pAdapter); + return -ENODEV; + } + + if (tc90x_GetAdapterProperties(pAdapter) != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(("NICOpen: GetAdapterProperties failed\n")); + DBGPRINT_FUNCTION(("NICOpen: Out with ERROR\n")); + tc90x_FreeAdapterResources(pAdapter); + return -ENODEV; + } + + if (tc90x_BasicInitializeAdapter(pAdapter) != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(("NICOpen: BasicInitializeAdapter failed\n")); + DBGPRINT_FUNCTION(("NICOpen: Out with error\n")); + tc90x_FreeAdapterResources(pAdapter); + return -ENODEV; + } + + if (tc90x_TestAdapter(pAdapter) != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(("NICOpen: TestAdapter failed\n")); + DBGPRINT_FUNCTION(("NICOpen: Out with error\n")); + tc90x_FreeAdapterResources(pAdapter); + return -ENODEV; + } + + if (tc90x_SetupMedia(Device) != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(("NICOpen: SetupMedia failed\n")); + DBGPRINT_FUNCTION(("NICOpen: Out with error\n")); + tc90x_FreeAdapterResources(pAdapter); + return -ENODEV; + } + + if (tc90x_SoftwareWork(pAdapter) != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(( + "NICOpen: EnableSoftwareWork failed\n")); + DBGPRINT_FUNCTION(("NICOpen: Out with error\n")); + tc90x_FreeAdapterResources(pAdapter); + return -ENODEV; + } + + if (tc90x_StartAdapter(pAdapter) != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(("NICOpen: StartAdapter failed\n")); + DBGPRINT_FUNCTION(("NICOpen: Out with error\n")); + tc90x_FreeAdapterResources(pAdapter); + return -ENODEV; + } + + Device->tbusy = 0; + Device->interrupt = 0; + Device->start = 1; + // + // Initialize the timer. + // + pAdapter->Resources.TimerInterval = 100; + init_timer(&pAdapter->Resources.Timer); + pAdapter->Resources.Timer.expires = RUN_AT(HZ/10); + pAdapter->Resources.Timer.data = (ULONG)Device; + pAdapter->Resources.Timer.function = &NICTimer; + add_timer(&pAdapter->Resources.Timer); + // + // Initialize task queue for hosterr task, just in case + // + pAdapter->Resources.hostErr_task.routine = ReStartAdapter; + pAdapter->Resources.hostErr_task.data = (void *)&pAdapter; + pAdapter->Resources.hostErr_task.sync = 0; + // + // Timer has been registered. + // + pAdapter->ResourcesReserved |= NIC_TIMER_REGISTERED; + + MOD_INC_USE_COUNT; + + DBGPRINT_FUNCTION(("NICOpen: OUT with SUCCESS\n")); + return 0; + +} + +/*++ + +Routine Name: + + NICClose + +Routine Description: + + This routine closes the interface. + +Arguments: + + Device - Pointer to the device structure. + +Return Value: + + -ENODEV if no adapter found + noAdapterFound if adapter(s) found + +--*/ +INT +NICClose( + IN PDEVICE Device + ) +{ + + PNIC_INFORMATION pAdapter = (PNIC_INFORMATION)Device->priv; + PDEVICE device = pAdapter->Device; + + DBGPRINT_FUNCTION(("NICClose: IN\n")); + +#if LINUX_VERSION_CODE >= 0x20200 + spin_lock(&pAdapter->SpinLock_misc); +#endif + + Device->start = 0; + Device->tbusy = 1; + // + // Disable transmit and receive. + // + NIC_COMMAND(pAdapter, COMMAND_TX_DISABLE); + NIC_COMMAND(pAdapter, COMMAND_RX_DISABLE); + // + // Wait for the transmit in progress to go off. + // + NIC_COMMAND(pAdapter,COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + MediaStatus_g = NIC_READ_PORT_USHORT(pAdapter,MEDIA_STATUS_REGISTER); + NIC_DELAY(10); + + if(MediaStatus_g & MEDIA_STATUS_TX_IN_PROGRESS) + { pAdapter->WaitCases = CHECK_TRANSMIT_IN_PROGRESS; + TimeOutCount = jiffies + HZ; //max = 1s + if(!InWaitTimer) + { WaitTimer.expires = RUN_AT(HZ/100); + add_timer(&WaitTimer); + InWaitTimer = TRUE; + } + while(TimeOutCount > jiffies) ; + if(!(MediaStatus_g & MEDIA_STATUS_TX_IN_PROGRESS)) + DBGPRINT_ERROR(("NICClose: Adapter is not responding\n")); + } + // Issue Global reset. + // + NIC_COMMAND_WAIT(pAdapter, + COMMAND_GLOBAL_RESET | + GLOBAL_RESET_MASK_TP_AUI_RESET | + GLOBAL_RESET_MASK_ENDEC_RESET | + GLOBAL_RESET_MASK_AISM_RESET | + GLOBAL_RESET_MASK_SMB_RESET | + GLOBAL_RESET_MASK_VCO_RESET); + + NIC_COMMAND_WAIT(pAdapter, COMMAND_TX_RESET); + NIC_COMMAND_WAIT(pAdapter, COMMAND_RX_RESET); + // + // Mask and acknowledge all interrupts. + // + NIC_MASK_ALL_INTERRUPT(pAdapter); + NIC_ACKNOWLEDGE_ALL_INTERRUPT(pAdapter); + tc90x_CleanupSendLogic(Device); + // + // Unregister the interrupt handler. + // + if (pAdapter->ResourcesReserved & NIC_INTERRUPT_REGISTERED) { + + DBGPRINT_INITIALIZE(("Releasing interrupt\n")); + free_irq(device->irq, device); + pAdapter->ResourcesReserved &= ~NIC_INTERRUPT_REGISTERED; + } + // + // Unregister the timer handler. + // + if (pAdapter->ResourcesReserved & NIC_TIMER_REGISTERED) { + DBGPRINT_INITIALIZE((KERN_CRIT "Releasing Timer\n")); + if (del_timer(&pAdapter->Resources.Timer)) { + DBGPRINT_INITIALIZE(("Timer already queued\n")); + } + pAdapter->ResourcesReserved &= ~NIC_TIMER_REGISTERED; + } + if (pAdapter->ResourcesReserved & WAIT_TIMER_REGISTERED) { + DBGPRINT_INITIALIZE((KERN_CRIT "Releasing WaitTimer\n")); + if (del_timer(&WaitTimer)) + DBGPRINT_INITIALIZE(("WaitTimer already queued\n")); + pAdapter->ResourcesReserved &= ~WAIT_TIMER_REGISTERED; + } + MOD_DEC_USE_COUNT; + +#if LINUX_VERSION_CODE >= 0x20200 + spin_unlock(&pAdapter->SpinLock_misc); +#endif + + DBGPRINT_FUNCTION(("NICClose: OUT\n")); + return 0; +} + + +/*++ + +Routine Name: + + cleanup_module + +Routine Description: + + This routine releases the resources. + +Arguments: + + None + +Return Value: + + None + +--*/ + +#ifdef MODULE + +VOID +cleanup_module( + VOID + ) +{ + PDEVICE nextDevice; + PNIC_INFORMATION pAdapter; + + DBGPRINT_FUNCTION(("cleanup_module: IN\n")); + + while (RootNICDevice) { + + nextDevice = ((PNIC_INFORMATION) + (RootNICDevice->priv))->NextDevice; + + unregister_netdev(RootNICDevice); + pAdapter = (PNIC_INFORMATION)RootNICDevice->priv; + tc90x_FreeAdapterResources(pAdapter); + kfree(RootNICDevice); + kfree(pAdapter); + RootNICDevice = nextDevice; + } + + DBGPRINT_FUNCTION(("cleanup_module: OUT\n")); +} +#endif + + + + + + +/*++ + +Routine Name: + + MainAutoSelectionRoutine + +Routine Description: + + If autoselection is set, determine the connector and link speed + by trying the various transceiver types. + +Arguments: + + IN PNIC_INFORMATION Adapter + IN USHORT Options + +Return Value: + + VOID + +--*/ + +VOID +tc90x_MainAutoSelectionRoutine( + IN PNIC_INFORMATION Adapter, + IN USHORT Options + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + CONNECTOR_TYPE NotUsed; + USHORT index; + + DBGPRINT_FUNCTION(("MainAutoSelectionRoutine: IN \n")); + + pAdapter->Hardware.Connector = CONNECTOR_UNKNOWN; + // + // Try 100MB Connectors + // + if ((Options & MEDIA_OPTIONS_100BASETX_AVAILABLE) || + (Options & MEDIA_OPTIONS_10BASET_AVAILABLE) || + (Options & MEDIA_OPTIONS_MII_AVAILABLE)) { + // + // For 10Base-T and 100Base-TX, select autonegotiation + // instead of autoselect before calling trymii + // + if ((Options & MEDIA_OPTIONS_100BASETX_AVAILABLE) || + (Options & MEDIA_OPTIONS_10BASET_AVAILABLE)) { + pAdapter->Hardware.Connector = + CONNECTOR_AUTONEGOTIATION; + } + else + pAdapter->Hardware.Connector = CONNECTOR_MII; + + DBGPRINT_INITIALIZE(("Trying MII\n")); + + if (!tc90x_TryMII(pAdapter, Options)) + pAdapter->Hardware.Connector = CONNECTOR_UNKNOWN; + } + // + // Transceiver available is 100Base-FX + // + if ((Options & MEDIA_OPTIONS_100BASEFX_AVAILABLE) && + (pAdapter->Hardware.Connector == CONNECTOR_UNKNOWN)) { + DBGPRINT_INITIALIZE(("Trying 100BFX\n")); + if (tc90x_TryLinkBeat(pAdapter, CONNECTOR_100BASEFX)) { + pAdapter->Hardware.Connector = CONNECTOR_100BASEFX; + pAdapter->Hardware.LinkSpeed = LINK_SPEED_100; + } + } + // + // Transceiver available is 10AUI + // + if ((Options & MEDIA_OPTIONS_10AUI_AVAILABLE) && + (pAdapter->Hardware.Connector == CONNECTOR_UNKNOWN)) { + DBGPRINT_INITIALIZE(("Trying 10AUI\n")); + tc90x_SetupConnector(pAdapter, CONNECTOR_10AUI, &NotUsed); + // + // Try to loopback packet + // + for (index = 0; index < 3; index++) { + if (TestPacket(pAdapter)) { + pAdapter->Hardware.Connector = CONNECTOR_10AUI; + pAdapter->Hardware.LinkSpeed = LINK_SPEED_10; + DBGPRINT_INITIALIZE(("Found AUI\n")); + break; + } + } + if (index == 3) + DBGPRINT_INITIALIZE(("Unable to find AUI\n")); + } + // + // Transceiver available is 10Base-2 + // + if ((Options & MEDIA_OPTIONS_10BASE2_AVAILABLE) && + (pAdapter->Hardware.Connector == CONNECTOR_UNKNOWN)) { + DBGPRINT_INITIALIZE(("Trying 10BASEB2\n")); + // + // Set up the connector + // + tc90x_SetupConnector(pAdapter, CONNECTOR_10BASE2, &NotUsed); + // + // Try to loopback packet + // + for (index = 0; index < 3; index++) { + if (TestPacket(pAdapter)) { + pAdapter->Hardware.Connector = CONNECTOR_10BASE2; + pAdapter->Hardware.LinkSpeed = LINK_SPEED_10; + + DBGPRINT_INITIALIZE(("Found 10Base2 \n")); + break; + } + } + if (index == 3) + DBGPRINT_INITIALIZE(("Unable to find 10Base2\n")); + // + // Disable DC converter + // + NIC_COMMAND(pAdapter, COMMAND_DISABLE_DC_CONVERTER); + // + // Check if DC convertor has been disabled + // + tc90x_CheckDCConverter(pAdapter, FALSE); + } + // + // Nothing left to try! + // + if (pAdapter->Hardware.Connector == CONNECTOR_UNKNOWN) { + + pAdapter->Hardware.Connector = pAdapter->Hardware.ConfigConnector; + pAdapter->Hardware.LinkSpeed = LINK_SPEED_10; + DBGPRINT_INITIALIZE(("AutoSelection failed. Using default.\n")); + DBGPRINT_INITIALIZE(("Connector: %x\n",pAdapter->Hardware.Connector)); + pAdapter->Hardware.LinkState = LINK_DOWN_AT_INIT; + } + + tc90x_SetupConnector( + pAdapter, + pAdapter->Hardware.Connector, + &NotUsed); + + DBGPRINT_FUNCTION(("MainAutoSelectionRoutine: OUT \n")); +} + + + +/*++ + +Routine Name: + + tc90x_CheckDCConverter + +Routine Description: + + +Arguments: + + IN PNIC_INFORMATION Adapter + IN BOOLEAN EnabledState + +Return Value: + + BOOLEAN + +--*/ + +BOOLEAN +tc90x_CheckDCConverter ( + IN PNIC_INFORMATION Adapter, + IN BOOLEAN EnabledState + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + NIC_COMMAND(pAdapter,COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + MediaStatus_g = NIC_READ_PORT_USHORT(pAdapter,MEDIA_STATUS_REGISTER); + NIC_DELAY(1000); + + if( ((EnabledState) && !(MediaStatus_g & MEDIA_STATUS_DC_CONVERTER_ENABLED) ) || + ((!EnabledState) && (MediaStatus_g & MEDIA_STATUS_DC_CONVERTER_ENABLED)) ) + { + DCConverterEnabledState_g = EnabledState; /* for waittimer */ + pAdapter->WaitCases = CHECK_DC_CONVERTER; + TimeOutCount = jiffies + 3; // 30ms + if(!InWaitTimer) + { WaitTimer.expires = RUN_AT(HZ/100); + add_timer(&WaitTimer); + InWaitTimer = TRUE; + } + while(TimeOutCount > jiffies) ; + if( (EnabledState && !(MediaStatus_g & MEDIA_STATUS_DC_CONVERTER_ENABLED)) || + (!EnabledState && (MediaStatus_g & MEDIA_STATUS_DC_CONVERTER_ENABLED)) + ) + { pAdapter->Hardware.Status = HARDWARE_STATUS_FAILURE; + DBGPRINT_ERROR(("ConfigureDCConverter: Timeout setting DC Converter \n")); + return FALSE; + } + } + return TRUE; +} + + +/*++ + +Routine Name: + + tc90x_SetupConnector + +Routine Description: + + Setup new transceiver type in InternalConfig. Determine whether to + set JabberGuardEnable, enableSQEStats and linkBeatEnable in MediaStatus. + Determine if the coax transceiver also needs to be enabled/disabled. + +Arguments: + + IN PNIC_INFORMATION Adapter + IN CONNECTOR_TYPE NewConnector + OUT PCONNECTOR_TYPE OldConnector + +Return Value: + + VOID + +--*/ + +VOID +tc90x_SetupConnector( + IN PNIC_INFORMATION Adapter, + IN CONNECTOR_TYPE NewConnector, + OUT PCONNECTOR_TYPE OldConnector + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + ULONG InternalConfig = 0; + ULONG OldInternalConfig = 0; + USHORT MediaStatus = 0; + + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_3); + + InternalConfig = NIC_READ_PORT_ULONG( + pAdapter, + INTERNAL_CONFIG_REGISTER); + OldInternalConfig = InternalConfig; + // + // Save old choice + // + *OldConnector = + (InternalConfig & INTERNAL_CONFIG_TRANSCEIVER_MASK) >> 20; + // + // Program the MII registers if forcing the configuration to 10/100BaseT. + // + if ((NewConnector == CONNECTOR_10BASET) || + (NewConnector == CONNECTOR_100BASETX)){ + // + // Clear transceiver type and change to new transceiver type. + // + InternalConfig &= ~(INTERNAL_CONFIG_TRANSCEIVER_MASK); + InternalConfig |= (CONNECTOR_AUTONEGOTIATION << 20); + // + // Update the internal config register. Only do this if the value has + //changed to avoid dropping link. + // + if (OldInternalConfig != InternalConfig) { + NIC_WRITE_PORT_ULONG( + pAdapter, + INTERNAL_CONFIG_REGISTER, + InternalConfig); + } + // + // Force the MII registers to the correct settings. + // + if ( !CheckMIIConfiguration( + pAdapter, + (USHORT)((NewConnector == CONNECTOR_100BASETX) + ? MEDIA_OPTIONS_100BASETX_AVAILABLE + : MEDIA_OPTIONS_10BASET_AVAILABLE)) ) { + // + // If the forced configuration didn't work, check the results and see why. + // + CheckMIIAutoNegotiationStatus(pAdapter); + return; + } + } + else { + // + // Clear transceiver type and change to new transceiver type + // + InternalConfig = InternalConfig & (~INTERNAL_CONFIG_TRANSCEIVER_MASK); + InternalConfig |= (NewConnector << 20); + // + // Update the internal config register. Only do this if the value has + // changed to avoid dropping link. + // + if (OldInternalConfig != InternalConfig) { + NIC_WRITE_PORT_ULONG( + pAdapter, + INTERNAL_CONFIG_REGISTER, + InternalConfig); + } + } + // + // Determine whether to set enableSQEStats and linkBeatEnable + // Automatically set JabberGuardEnable in MediaStatus register. + // + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + + MediaStatus = NIC_READ_PORT_USHORT( + pAdapter, + MEDIA_STATUS_REGISTER); + + MediaStatus &= ~( + MEDIA_STATUS_SQE_STATISTICS_ENABLE | + MEDIA_STATUS_LINK_BEAT_ENABLE | + MEDIA_STATUS_JABBER_GUARD_ENABLE); + + MediaStatus |= MEDIA_STATUS_JABBER_GUARD_ENABLE; + + if (NewConnector == CONNECTOR_10AUI) + MediaStatus |= MEDIA_STATUS_SQE_STATISTICS_ENABLE; + + if (NewConnector == CONNECTOR_AUTONEGOTIATION) + MediaStatus |= MEDIA_STATUS_LINK_BEAT_ENABLE; + else { + if ((NewConnector == CONNECTOR_10BASET) || + (NewConnector == CONNECTOR_100BASETX) || + (NewConnector == CONNECTOR_100BASEFX)) { + if (!pAdapter->Hardware.LinkBeatDisable) + MediaStatus |= MEDIA_STATUS_LINK_BEAT_ENABLE; + } + } + NIC_WRITE_PORT_USHORT(pAdapter, MEDIA_STATUS_REGISTER, MediaStatus); + + DBGPRINT_INITIALIZE(( + "tc90x_SetupConnector: MediaStatus = %x \n",MediaStatus)); + // + // If configured for coax we must start the internal transceiver. + // If not, we stop it (in case the configuration changed across a + // warm boot). + // + if (NewConnector == CONNECTOR_10BASE2) { + NIC_COMMAND(pAdapter, COMMAND_ENABLE_DC_CONVERTER); + // + // Check if DC convertor has been enabled + // + tc90x_CheckDCConverter(pAdapter, TRUE); + } + else { + NIC_COMMAND(pAdapter, COMMAND_DISABLE_DC_CONVERTER); + // + // Check if DC convertor has been disabled + // + tc90x_CheckDCConverter(pAdapter, FALSE); + } +} + + +/*++ + +Routine Name: + + tc90x_TryMII + +Routine Description: + + Used to detect if 10Base-T, 100Base-TX, or external MII is available. + +Arguments: + + IN pAdapter pAdapter - Pointer to the adapter structure + IN CONNECTOR_TYPE newconnector + +Return Value: + BOOLEAN + +--*/ + +BOOLEAN +tc90x_TryMII( + IN PNIC_INFORMATION Adapter, + IN USHORT MediaOptions + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + BOOLEAN Handles100Mbit = FALSE; + //CONNECTOR_TYPE NotUsed; + //USHORT PhyControl; + //BOOLEAN PhyResponding; + //USHORT PhyStatus; + + DBGPRINT_FUNCTION(("tc90x_TryMII: IN \n")); + // + // First see if there's anything connected to the MII + // + if (!FindMIIPhy(pAdapter)) { + DBGPRINT_ERROR(("tc90x_TryMII: FindMIIPhy failed \n")); + pAdapter->Hardware.Status = HARDWARE_STATUS_FAILURE; + return FALSE; + } + // + // Nowhere is it written that the register must be latched, and since + // reset is the last bit out, the contents might not be valid. read + // it one more time. + // + /*PhyResponding_g = ReadMIIPhy(pAdapter, MII_PHY_CONTROL, &PhyControl); + if (!PhyResponding_g) { + + DBGPRINT_ERROR(("tc90x_TryMII: Phy not responding (1) \n")); + pAdapter->Hardware.Status = HARDWARE_STATUS_FAILURE; + return FALSE; + }*/ + // + // Now we can read the status and try to figure out what's out there. + // + PhyResponding_g = ReadMIIPhy(pAdapter, MII_PHY_STATUS, &PhyStatus_g); + if (!PhyResponding_g) { + DBGPRINT_ERROR(("tc90x_TryMII: Phy not responding (2) \n")); + pAdapter->Hardware.Status = HARDWARE_STATUS_FAILURE; + return FALSE; + } + + if ((PhyStatus_g & MII_STATUS_AUTO) && + (PhyStatus_g & MII_STATUS_EXTENDED)) { + // + // If it is capable of auto negotiation, see if it has + // been done already. + // + DBGPRINT_INITIALIZE(("MII Capable of Autonegotiation\n")); + // + // Check the current MII auto-negotiation state and see if we need to + // start auto-neg over. + // + if (!CheckMIIConfiguration(pAdapter, MediaOptions)) + return FALSE; + // + // See if link is up... + // + PhyResponding_g = ReadMIIPhy(pAdapter, MII_PHY_STATUS, &PhyStatus_g); + if (!PhyResponding_g) { + DBGPRINT_ERROR(("tc90x_TryMII: Phy not responding (3) \n")); + pAdapter->Hardware.Status = HARDWARE_STATUS_FAILURE; + return FALSE; + } + if (PhyStatus_g & MII_STATUS_LINK_UP) { + if (!GetLinkSpeed(pAdapter, &Handles100Mbit)) { + DBGPRINT_ERROR(("TryMII: Unknown link speed \n")); + pAdapter->Hardware.Status = HARDWARE_STATUS_FAILURE; + return FALSE; + } + if (Handles100Mbit) { + DBGPRINT_INITIALIZE(("TryMII: Link speed set to 100\n")); + pAdapter->Hardware.LinkSpeed = LINK_SPEED_100; + } + else { + DBGPRINT_INITIALIZE(("TryMII: Link speed set to 10\n")); + pAdapter->Hardware.LinkSpeed = LINK_SPEED_10; + } + return TRUE; + + } else { + // + // Assume 10Mbit if no link + // + if (PhyStatus_g & MII_STATUS_100MB_MASK) { + DBGPRINT_INITIALIZE(( + "TryMII: Link speed defaulted to 100 \n")); + pAdapter->Hardware.LinkSpeed = LINK_SPEED_100; + } + else { + DBGPRINT_INITIALIZE(( + "TryMII: Link speed defaulted to 10 \n")); + pAdapter->Hardware.LinkSpeed = LINK_SPEED_10; + } + return TRUE; + } + + } + return FALSE; +} + + +/*++ + +Routine Name: + + tc90x_TryLinkBeat + +Routine Description: + + Download a self-directed packet. If successful, 100BASE-FX is available. + +Arguments: + + IN PNIC_INFORMATION Adapter + IN CONNECTOR_TYPE NewConnector + +Return Value: + + BOOLEAN + +--*/ + +BOOLEAN +tc90x_TryLinkBeat( + IN PNIC_INFORMATION Adapter, + IN CONNECTOR_TYPE NewConnector + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + USHORT MediaStatus = 0; + BOOLEAN retval = FALSE; + CONNECTOR_TYPE NotUsed; + + DBGPRINT_FUNCTION(("tc90x_TryLinkBeat: IN \n")); + // + // Go quiet for 1.5 seconds to get any N-Way hub into a receptive + // state to sense the new link speed. We go quiet by switching over + // to 10BT and disabling Linkbeat. + // + pAdapter->Hardware.LinkBeatDisable = TRUE; + tc90x_SetupConnector(pAdapter, CONNECTOR_10BASET, &NotUsed); + // + // Delay 1.5 seconds + // + TimeOutCount = jiffies + 15*HZ/10; + while(TimeOutCount > jiffies) ; + + NIC_COMMAND_WAIT(pAdapter, COMMAND_TX_RESET | TX_RESET_MASK_NETWORK_RESET); + NIC_COMMAND_WAIT(pAdapter, COMMAND_RX_RESET | RX_RESET_MASK_NETWORK_RESET); + // + // Set up for TP transceiver + // + tc90x_SetupConnector(pAdapter, NewConnector, &NotUsed); + + // delay 5 milliseconds + TimeOutCount = jiffies + 1; //make it 10ms + while(TimeOutCount > jiffies) ; + // + // We need to send a test packet to clear the + // the Partition if for some reason it's partitioned + // (i.e., we were testing 100mb before.) + // Download a 20 byte packet into the TxFIFO, from us, to us + // + if (!DownloadSelfDirected(pAdapter)) { + return FALSE; + } + // + // Acknowledge the down complete + // + NIC_COMMAND( + pAdapter, + COMMAND_ACKNOWLEDGE_INTERRUPT + INTSTATUS_DOWN_COMPLETE); + // + // Check MediaStatus for linkbeat indication + // + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + + MediaStatus = NIC_READ_PORT_USHORT(pAdapter, MEDIA_STATUS_REGISTER); + + if (MediaStatus & MEDIA_STATUS_LINK_DETECT) { + retval = TRUE; + } + + NIC_COMMAND_WAIT(pAdapter, COMMAND_TX_RESET | TX_RESET_MASK_NETWORK_RESET); + NIC_COMMAND_WAIT(pAdapter, COMMAND_RX_RESET | RX_RESET_MASK_NETWORK_RESET); + + DBGPRINT_INITIALIZE(("tc90x_TryLinkBeat: OUT with success\n")); + return retval; +} + +/*++ +Routine Name: + + DownloadSelfDirected + +Routine Description: + + Download a 20 byte packet into the TxFIFO, from us, to us. + +Arguments: + + IN PNIC_INFORMATION Adapter + +Return Value: + + BOOLEAN + +--*/ + +BOOLEAN +DownloadSelfDirected( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + PDPD_LIST_ENTRY dpdVirtual; + ULONG PacketBufPhysAddr; + UCHAR *PacketBuffer; + PUCHAR pbuffer; + // + // Set allocated buffer for transmit + // + PacketBuffer = (PUCHAR)pAdapter->TestBufferVirtual[0]; + PacketBufPhysAddr = pAdapter->TestBufferPhysical[0]; + // + // Fill data in the buffer + // + pbuffer = pAdapter->StationAddress; + memcpy(PacketBuffer, pbuffer, 6); // destination is me + memcpy(PacketBuffer + 6, pbuffer, 6); // source is me + *(ULONG *)(PacketBuffer + 12) = 0; + *(ULONG *)(PacketBuffer + 16) = 0; + // + // Create a single DPD + // + dpdVirtual = (PDPD_LIST_ENTRY)pAdapter->TestDPDVirtual[0]; + + dpdVirtual->DownNextPointer = 0; + dpdVirtual->FrameStartHeader = 20 | FSH_ROUND_UP_DEFEAT; + dpdVirtual->SGList[0].Address = PacketBufPhysAddr; + dpdVirtual->SGList[0].Count = 20 | 0x80000000; + // + // Download DPD + // + NIC_COMMAND_WAIT(pAdapter, COMMAND_DOWN_STALL); + + NIC_WRITE_PORT_ULONG( + pAdapter, + DOWN_LIST_POINTER_REGISTER, + dpdVirtual->DPDPhysicalAddress); + + NIC_COMMAND(pAdapter, COMMAND_DOWN_UNSTALL); + // + // Check if the DPD is done with + // + DownListPointer_g = NIC_READ_PORT_ULONG(pAdapter,DOWN_LIST_POINTER_REGISTER); + + NIC_DELAY(100); + + if(DownListPointer_g == dpdVirtual->DPDPhysicalAddress) + { + pAdapter->WaitCases = CHECK_DOWNLOAD_SELFDIRECTED; + TimeOutCount = jiffies + 3*HZ; //max = 3s + //WaitTimer.expires = RUN_AT(HZ/100); + //add_timer(&WaitTimer); + //InWaitTimer = TRUE; + while(TimeOutCount > jiffies) ; + if(DownListPointer_g != dpdVirtual->DPDPhysicalAddress) + { pAdapter->Hardware.Status = HARDWARE_STATUS_FAILURE; + DBGPRINT_ERROR(("DownloadSelfDirected: DPD not finished\n")); + return FALSE; + } + } + return TRUE; +} + +BOOLEAN +CheckTransmitInProgress( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + MediaStatus_g = 0; + + NIC_COMMAND(pAdapter,COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + + MediaStatus_g = NIC_READ_PORT_USHORT(pAdapter,MEDIA_STATUS_REGISTER); + + NIC_DELAY(10); + + if(MediaStatus_g & MEDIA_STATUS_TX_IN_PROGRESS) + { pAdapter->WaitCases = CHECK_TRANSMIT_IN_PROGRESS; + TimeOutCount = jiffies + HZ; //max = 1s + if(!InWaitTimer) + { WaitTimer.expires = RUN_AT(HZ/100); + add_timer(&WaitTimer); + InWaitTimer = TRUE; + } + while(TimeOutCount > jiffies) ; + if(!(MediaStatus_g & MEDIA_STATUS_TX_IN_PROGRESS)) + { pAdapter->Hardware.Status = HARDWARE_STATUS_FAILURE; + DBGPRINT_ERROR(("CheckTransmitInProgress: Transmit still in progress\n")); + return FALSE; + } + } + return TRUE; +} + + +/*++ + +Routine Name: + + TestPacket + +Routine Description: + + This function is called by TryLoopback to determine if a packet can + successfully be loopbacked for 10Base-2 and AUI. + +Arguments: + + IN PNIC_INFORMATION Adapter + +Return Value: + + BOOLEAN + +--*/ + +BOOLEAN +TestPacket( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + BOOLEAN ReturnValue = FALSE; + USHORT MacControl = 0; + ULONG PacketStatus = 0; + + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_3); + + MacControl = NIC_READ_PORT_USHORT( + pAdapter, + MAC_CONTROL_REGISTER); + // + // Enable full duplex + // + NIC_WRITE_PORT_USHORT( + pAdapter, + MAC_CONTROL_REGISTER, + (USHORT)(MacControl | MAC_CONTROL_FULL_DUPLEX_ENABLE)); + // + // Write UpListPointer to UpListPointer register and unstall + // + NIC_COMMAND_WAIT(pAdapter, COMMAND_UP_STALL); + + NIC_WRITE_PORT_ULONG( + pAdapter, + UP_LIST_POINTER_REGISTER, + pAdapter->HeadUPDVirtual->UPDPhysicalAddress); + + NIC_COMMAND(pAdapter, COMMAND_UP_UNSTALL); + + // + // Enable receive and transmit and setup our packet filter + // + NIC_COMMAND_WAIT(pAdapter, COMMAND_TX_ENABLE); + NIC_COMMAND_WAIT(pAdapter, COMMAND_RX_ENABLE); + NIC_COMMAND_WAIT(pAdapter, COMMAND_SET_RX_FILTER + + RX_FILTER_INDIVIDUAL); + + // + // Create single DPD and download + // + if (!DownloadSelfDirected(pAdapter)) { + + return FALSE; + } + // + // Check if transmit is still in progress + // + if (!CheckTransmitInProgress(pAdapter)) + return FALSE; + // + // Reset the transmitter to get rid of any TxStatus we haven't seen yet + // + NIC_COMMAND_WAIT(pAdapter, COMMAND_TX_RESET | TX_RESET_MASK_NETWORK_RESET); + // + // Check UpListPtr to see if it has changed to see if upload complete + // + UpListPointer_g = NIC_READ_PORT_ULONG(pAdapter,UP_LIST_POINTER_REGISTER); + + NIC_DELAY(100); + + if(UpListPointer_g == pAdapter->HeadUPDVirtual->UPDPhysicalAddress) + { pAdapter->WaitCases = AUTONEG_TEST_PACKET; + TimeOutCount = jiffies + HZ; //max =1s + while(TimeOutCount > jiffies) ; + if(UpListPointer_g != pAdapter->HeadUPDVirtual->UPDPhysicalAddress) + { DBGPRINT_ERROR(("TestPacket: UPD not finished\n")); + return FALSE; + } + } + // + // Check RxStatus. If we've got a packet without any errors, this + // connector is okay. + // + PacketStatus = pAdapter->HeadUPDVirtual->UpPacketStatus; + + if (!(PacketStatus & UP_PACKET_STATUS_ERROR) && + (PacketStatus & UP_PACKET_STATUS_COMPLETE)) { + + ReturnValue = TRUE; // Received a good packet + } + // + // The following cleans up after the test we just ran + // + NIC_WRITE_PORT_ULONG(pAdapter, UP_LIST_POINTER_REGISTER, 0); + NIC_WRITE_PORT_ULONG(pAdapter, DOWN_LIST_POINTER_REGISTER, 0); + pAdapter->HeadUPDVirtual->UpPacketStatus = 0; + // + // Reset the receiver to wipe anything we haven't seen yet + // + NIC_COMMAND_WAIT(pAdapter, COMMAND_RX_RESET | RX_RESET_MASK_NETWORK_RESET); + NIC_COMMAND( + pAdapter, + COMMAND_ACKNOWLEDGE_INTERRUPT + + INTSTATUS_ACKNOWLEDGE_ALL); + + // + // Get out of loopback mode + // + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_3); + + MacControl = NIC_READ_PORT_USHORT(pAdapter, MAC_CONTROL_REGISTER); + + NIC_WRITE_PORT_USHORT( + pAdapter, + MAC_CONTROL_REGISTER, + (USHORT)(MacControl & ~MAC_CONTROL_FULL_DUPLEX_ENABLE)); + + return ReturnValue; +} + + +/*++ + +Routine Name: + + GetLinkSpeed + +Routine Description: + + Determine from the MII AutoNegotiationAdvertisement and + AutoNegotiationPartnerAbility registers whether the + current linkspeed is 10Mbits or 100Mbits. + +Arguments: + + IN PNIC_INFORMATION Adapter, + OUT PBOOLEAN handles100Mbitptr + +Return Value: + + BOOLEAN + +--*/ + +BOOLEAN +GetLinkSpeed( + IN PNIC_INFORMATION Adapter, + OUT PBOOLEAN handles100Mbitptr + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + BOOLEAN PhyResponding; + USHORT PhyAnlpar; + USHORT PhyAner; + USHORT PhyAnar; + USHORT PhyStatus; + + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_ANER, &PhyAner); + if (!PhyResponding) + return FALSE; + + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_ANLPAR, &PhyAnlpar); + if (!PhyResponding) + return FALSE; + + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_ANAR, &PhyAnar); + if (!PhyResponding) + return FALSE; + + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_STATUS, &PhyStatus); + if (!PhyResponding) + return FALSE; + // + // Check to see if we've completed auto-negotiation. + // + if (!(PhyStatus & MII_STATUS_AUTO_DONE)) + return FALSE; + + if ((PhyAnar & MII_ANAR_100TXFD) && + (PhyAnlpar & MII_ANLPAR_100TXFD)) { + pAdapter->Hardware.MIIPhyUsed = MII_100TXFD; + *handles100Mbitptr = TRUE; + pAdapter->Hardware.FullDuplexEnable = TRUE; + + } + else if ((PhyAnar & MII_ANAR_100TX) && (PhyAnlpar & MII_ANLPAR_100TX)) { + pAdapter->Hardware.MIIPhyUsed = MII_100TX ; + *handles100Mbitptr = TRUE; + pAdapter->Hardware.FullDuplexEnable = FALSE; + } + else if ((PhyAnar & MII_ANAR_10TFD) && (PhyAnlpar & MII_ANLPAR_10TFD)) { + pAdapter->Hardware.MIIPhyUsed = MII_10TFD ; + pAdapter->Hardware.FullDuplexEnable = TRUE; + *handles100Mbitptr = FALSE; + } + else if ((PhyAnar & MII_ANAR_10T) && (PhyAnlpar & MII_ANLPAR_10T)) { + pAdapter->Hardware.MIIPhyUsed = MII_10T ; + pAdapter->Hardware.FullDuplexEnable = FALSE; + *handles100Mbitptr = FALSE; + } + else if (!(PhyAner & MII_ANER_LPANABLE)) { + // + // Link partner is not capable of auto-negotiation. Fall back to 10HD. + // + pAdapter->Hardware.MIIPhyUsed = MII_10T ; + pAdapter->Hardware.FullDuplexEnable = FALSE; + *handles100Mbitptr = FALSE; + } + else + return FALSE; + + return TRUE; +} + + + + + + +/*++ + +Routine Name: + + tc90x_FreeAdapterResources. + +Routine Description: + + This routine frees up the adapter resources. + +Arguments: + + Device - Pointer to the device structure. + +Return Value: + + None + +--*/ + +VOID +tc90x_FreeAdapterResources( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + PDEVICE device = pAdapter->Device; + PUPD_LIST_ENTRY currentUPDVirtual = pAdapter->HeadUPDVirtual; + + + DBGPRINT_FUNCTION(("FreeAdapterResources: IN\n")); + + if (pAdapter->ResourcesReserved & NIC_INTERRUPT_REGISTERED) { + + DBGPRINT_INITIALIZE(("Releasing interrupt\n")); + free_irq(device->irq, device); + pAdapter->ResourcesReserved &= ~NIC_INTERRUPT_REGISTERED; + + } + + if (pAdapter->ResourcesReserved & WAIT_TIMER_REGISTERED) { + + DBGPRINT_INITIALIZE((KERN_CRIT "Releasing WaitTimer\n")); + if (del_timer(&WaitTimer)) + /* Timer has already been queued. */ + DBGPRINT_ERROR(("WaitTimer already queued\n")); + pAdapter->ResourcesReserved &= ~WAIT_TIMER_REGISTERED; + } + + if (pAdapter->ResourcesReserved & NIC_TIMER_REGISTERED) { + + DBGPRINT_INITIALIZE((KERN_CRIT "Releasing Timers\n")); + if (del_timer(&pAdapter->Resources.Timer)) { + // + // Timer has already been queued. + // + DBGPRINT_ERROR(("Timer already queued\n")); + } + pAdapter->ResourcesReserved &= ~NIC_TIMER_REGISTERED; + if (del_timer(&WaitTimer)) + DBGPRINT_ERROR(("WaitTimer already queued\n")); + } + + if (pAdapter->ResourcesReserved & NIC_SHARED_MEMORY_ALLOCATED) { + + DBGPRINT_INITIALIZE((KERN_CRIT "Releasing memory\n")); + + currentUPDVirtual = pAdapter->HeadUPDVirtual; + // + // Release the SKBs allocated + // + while (1) { + + if (currentUPDVirtual->SocketBuffer) +#if LINUX_VERSION_CODE >= 0x20200 + dev_kfree_skb( + currentUPDVirtual->SocketBuffer + ); +#else + dev_kfree_skb( + currentUPDVirtual->SocketBuffer, + GFP_ATOMIC + ); + +#endif + currentUPDVirtual = currentUPDVirtual->Next; + if (currentUPDVirtual == pAdapter->HeadUPDVirtual) + break; + } + + kfree(pAdapter->Resources.SharedMemoryVirtual); + pAdapter->ResourcesReserved &= ~NIC_SHARED_MEMORY_ALLOCATED; + } + + + if (pAdapter->ResourcesReserved & NIC_IO_PORT_REGISTERED) { + + DBGPRINT_INITIALIZE((KERN_CRIT "Releasing IO port region\n")); + release_region(device->base_addr, 0x80); + pAdapter->ResourcesReserved &= ~NIC_IO_PORT_REGISTERED; + } + DBGPRINT_FUNCTION(("FreeAdapterResources: OUT\n")); +} + + +/*++ + +RoutineName: + + tc90x_RegisterAdapter. + +Routine Description: + + This routine registers the adapter resources with Linux. + +Arguments: + + Device - Pointer to the device structure. + +Return Value: + + NIC_STATUS_SUCCESS if the adapter resources could be registered. + NIC_STATUS_FAILURE if the adapter resources could not be registered. + +--*/ + +NIC_STATUS +tc90x_RegisterAdapter( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + PDEVICE device = pAdapter->Device; + + DBGPRINT_FUNCTION(("RegisterAdapter: IN\n")); + // + // Use the now-standard shared IRQ implementation. + // + DBGPRINT_INITIALIZE(("SA_SHIRQ registering IRQ %x\n", device->irq)); + if (request_irq( + device->irq, + &NICInterrupt, + SA_SHIRQ, + device->name, + device)) { + + DBGPRINT_ERROR(("RegisterAdapter: IRQ registration failed\n")); + return NIC_STATUS_FAILURE; + + } + + pAdapter->ResourcesReserved|= NIC_INTERRUPT_REGISTERED; + + DBGPRINT_FUNCTION(("RegisterAdapter: OUT\n")); + return NIC_STATUS_SUCCESS; +} + +/*++ + +Routine Name: + + tc90x_GetAdapterProperties. + +Routine Description: + + This routine sets up the adapter. + +Arguments: + + Device - Pointer to the device structure. + +Return Value: + + NIC_STATUS_SUCCESS if the adapter could be set up successfully. + NIC_STATUS_FAILURE if the adpater could not be set up successfully. + +--*/ + +NIC_STATUS +tc90x_GetAdapterProperties( + IN PNIC_INFORMATION Adapter + ) + +{ + PNIC_INFORMATION pAdapter = Adapter; + PDEVICE device = pAdapter->Device; + + NIC_STATUS nicStatus; + USHORT eepromValue; + USHORT intStatus; + COMPATABILITY_WORD compatability; + SOFTWARE_INFORMATION_1 information1; + SOFTWARE_INFORMATION_2 information2; + CAPABILITIES_WORD capabilities; + UCHAR value, index; + + DBGPRINT_FUNCTION(("GetAdapterProperties: IN \n")); + // + // Check the ASIC type. Udp checksum should not be used in + // pre-Tornado cards- JRR + // + + if ((pAdapter->Hardware.DeviceId == NIC_PCI_DEVICE_ID_9055) || + (pAdapter->Hardware.DeviceId == NIC_PCI_DEVICE_ID_9004) || + (pAdapter->Hardware.DeviceId == NIC_PCI_DEVICE_ID_9005) || + (pAdapter->Hardware.DeviceId == NIC_PCI_DEVICE_ID_9006) || + (pAdapter->Hardware.DeviceId == NIC_PCI_DEVICE_ID_9058) || + (pAdapter->Hardware.DeviceId == NIC_PCI_DEVICE_ID_900A) || + (pAdapter->Hardware.DeviceId == NIC_PCI_DEVICE_ID_905A) || + (pAdapter->Hardware.DeviceId == NIC_PCI_DEVICE_ID_4500) || + (pAdapter->Hardware.DeviceId == NIC_PCI_DEVICE_ID_7646) || + (pAdapter->Hardware.DeviceId == NIC_PCI_DEVICE_ID_9800)) { + + pAdapter->Hardware.BitsInHashFilter = 0x40; + } + else { + + pAdapter->Hardware.BitsInHashFilter = 0x100; + pAdapter->Hardware.UDPChecksumErrDone = TRUE; + } + + pAdapter->Hardware.Status = HARDWARE_STATUS_WORKING; + pAdapter->Hardware.LinkSpeed = LINK_SPEED_100; + + // + // Make sure we can see the adapter. Set up for window 7, and make + // sure the window number gets reflected in status. + // + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_7); + + intStatus = NIC_READ_PORT_USHORT( + pAdapter, + INTSTATUS_COMMAND_REGISTER); + + DBGPRINT_INITIALIZE(("intStatus =%x\n", intStatus)); + DBGPRINT_INITIALIZE(("ioBase =%x\n", (INT)pAdapter->IoBaseAddress)); + + if ((intStatus & REGISTER_WINDOW_MASK) != + (REGISTER_WINDOW_7 << 13)) { + + DBGPRINT_ERROR(("SetupAdapter: Window selection failure\n")); + DBGPRINT_INITIALIZE(("SetupAdapter: Out with error\n")); + return NIC_STATUS_FAILURE; + } + // + // ----------------- Read the compatability level ---------- + // + + nicStatus = tc90x_ReadEEPROM( + pAdapter, + EEPROM_COMPATABILITY_WORD, + (PUSHORT)&compatability); + + if (nicStatus != NIC_STATUS_SUCCESS) { + + DBGPRINT_INITIALIZE(( + "GetAdapterProperties: compatability read failed\n")); + return NIC_STATUS_FAILURE; + } + // + // Check the Failure level. + // + + if (compatability.FailureLevel > EEPROM_COMPATABILITY_LEVEL) { + + DBGPRINT_ERROR(( + "GetAdapterProperties: Incompatible level\n")); + DBGPRINT_INITIALIZE(( + "GetAdapterProperties: Out with error\n")); + return NIC_STATUS_FAILURE; + } + // + // Check the warning level. + // + + if (compatability.WarningLevel > EEPROM_COMPATABILITY_LEVEL) { + + DBGPRINT_ERROR(( + "GetAdapterProperties: Wrong down compatability level\n" + )); + } + // + // ----------------- Read the software information 1 ------- + // + nicStatus = tc90x_ReadEEPROM( + pAdapter, + EEPROM_SOFTWARE_INFORMATION_1, + (PUSHORT)&information1); + + if (nicStatus != NIC_STATUS_SUCCESS) { + + DBGPRINT_INITIALIZE(( + "GetAdapterProperties: EEPROM s/w info1 read failed\n")); + return NIC_STATUS_FAILURE; + + } + if (information1.LinkBeatDisable) { + + DBGPRINT_INITIALIZE(("s/w information1 - Link beat disable\n")); + pAdapter->Hardware.LinkBeatDisable = TRUE; + + } + if (pAdapter->Hardware.DuplexCommandOverride == FALSE) { + if (information1.FullDuplexMode) { + + DBGPRINT_INITIALIZE(("s/w information1 - Full duplex enable\n")); + pAdapter->Hardware.FullDuplexEnable = TRUE; + } + else { + DBGPRINT_INITIALIZE(("s/w information 1 - Full duplex disabled\n")); + pAdapter->Hardware.FullDuplexEnable = FALSE; + } + } + + switch (information1.OptimizeFor) { + + case EEPROM_OPTIMIZE_FOR_THROUGHPUT: + + DBGPRINT_INITIALIZE(("sw info1 - optimize throughput\n")); + pAdapter->Hardware.OptimizeForThroughput = TRUE; + break; + + case EEPROM_OPTIMIZE_FOR_CPU: + + DBGPRINT_INITIALIZE(("s/w info1 - optimize CPU\n")); + pAdapter->Hardware.OptimizeForCPU = TRUE; + break; + + case EEPROM_OPTIMIZE_NORMAL: + + DBGPRINT_INITIALIZE(("s/w info1 - optimize Normal\n")); + pAdapter->Hardware.OptimizeNormal = TRUE; + break; + + default: + DBGPRINT_ERROR(( + "GetAdapterProperties: Wrong optimization level\n")); + return NIC_STATUS_FAILURE; + break; + + } + + // ----------------- Read the capabilities information ----- + // + nicStatus = tc90x_ReadEEPROM( + pAdapter, + EEPROM_CAPABILITIES_WORD, + (PUSHORT)&capabilities + ); + + if (nicStatus != NIC_STATUS_SUCCESS) { + + DBGPRINT_INITIALIZE(( + "GetAdapterProprties: EEPROM s/w capabilities read failed\n")); + + return NIC_STATUS_FAILURE; + } + + if (capabilities.SupportsPowerManagement) { + + DBGPRINT_INITIALIZE(("Adapter supports power management\n")); + pAdapter->Hardware.SupportsPowerManagement = TRUE; + } + // + // ----------------- Read the software information 2 ------- + // + + nicStatus = tc90x_ReadEEPROM( + pAdapter, + EEPROM_SOFTWARE_INFORMATION_2, + (PUSHORT)&information2); + + if (nicStatus != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(( + "GetAdapterProperties: ReadEEPROM , SWINFO2 failed\n")); + DBGPRINT_INITIALIZE(( + "GetAdapterProperties: Out with error\n")); + return NIC_STATUS_FAILURE; + + } + + if (information2.BroadcastRxErrDone){ + + DBGPRINT_INITIALIZE(("Adapter has BroadcastRxErrDone\n")); + pAdapter->Hardware.BroadcastErrDone = TRUE; + } + + if (information2.MWIErrDone) { + + DBGPRINT_INITIALIZE(("Adapter has MWIErrDone\n")); + pAdapter->Hardware.MWIErrDone = TRUE; + } + + if (information2.WOLConnectorPresent){ + + DBGPRINT_INITIALIZE(("WOL is connected\n")); + pAdapter->Hardware.WOLConnectorPresent = TRUE; + } + + if (information2.AutoResetToD0) { + + DBGPRINT_INITIALIZE(("Auto reset to D0 bit on\n")); + pAdapter->Hardware.AutoResetToD0 = TRUE; + } + // + // ----------------- Read the OEM station address ---------- + // + + nicStatus = tc90x_ReadEEPROM( + pAdapter, + EEPROM_OEM_NODE_ADDRESS_WORD_0, + &eepromValue); + + if (nicStatus != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(( + "GetAdapterProperties: EEPROM read word 0 failed\n")); + DBGPRINT_INITIALIZE(( + "GetAdapterProperties: Out with error\n")); + return NIC_STATUS_FAILURE; + + } + + pAdapter->PermanentAddress[0] = HIBYTE(eepromValue); + pAdapter->PermanentAddress[1] = LOBYTE(eepromValue); + + nicStatus = tc90x_ReadEEPROM( + pAdapter, + EEPROM_OEM_NODE_ADDRESS_WORD_1, + &eepromValue); + + if (nicStatus != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(( + "GetAdapterProperties: EEPROM read word 1 failed\n")); + DBGPRINT_INITIALIZE(( + "GetAdapterProperties: Out with error\n")); + return NIC_STATUS_FAILURE; + + } + + pAdapter->PermanentAddress[2] = HIBYTE(eepromValue); + pAdapter->PermanentAddress[3] = LOBYTE(eepromValue); + + nicStatus = tc90x_ReadEEPROM( + pAdapter, + EEPROM_OEM_NODE_ADDRESS_WORD_2, + &eepromValue); + + if (nicStatus != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(( + "GetAdapterProperties: EEPROM read word 2 failed\n")); + DBGPRINT_INITIALIZE(( + "GetAdapterProperties: Out with error\n")); + return NIC_STATUS_FAILURE; + + } + + pAdapter->PermanentAddress[4] = HIBYTE(eepromValue); + pAdapter->PermanentAddress[5] = LOBYTE(eepromValue); + + // + // If the station address has not been overriden, fill the permanent + // address into it. + // + + value = pAdapter->StationAddress[0] | + pAdapter->StationAddress[1] | + pAdapter->StationAddress[2] | + pAdapter->StationAddress[3] | + pAdapter->StationAddress[4] | + pAdapter->StationAddress[5]; + + // + // If the station address has not been overriden, set this value + // in the station address. + // + + if (!value) { + + for (index=0; index < 6; index++) + pAdapter->StationAddress[index] = + pAdapter->PermanentAddress[index]; + + + } + + for (index=0; index < 6; index++) + device->dev_addr[index] = pAdapter->StationAddress[index]; + + DBGPRINT_FUNCTION(("GetAdapterProperties: OUT \n")); + return NIC_STATUS_SUCCESS; + +} + + +/*++ + +Routine Name: + + tc90x_BasicInitializeAdapter. + +Routine Description: + + This routine does the basic initialize of the adapter. It does + not set the media specific stuff. + +Arguments: + + MiniportAdapterContext - Pointer to the adapter structure. + +Return Value: + + NIC_STATUS_SUCCESS if the initialization succeeds. + NIC_STATUS_FAILUTE if the initialization fails. + +--*/ + +NIC_STATUS +tc90x_BasicInitializeAdapter( + IN PNIC_INFORMATION Adapter + ) + +{ + + PNIC_INFORMATION pAdapter = Adapter; + USHORT stationTemp, macControl; + NIC_STATUS nicStatus; + + DBGPRINT_FUNCTION(("BasicInitializeAdapter: In\n")); + // + // ----------------- Tx Engine handling -------------------- + // + + nicStatus = NIC_COMMAND_WAIT(pAdapter, COMMAND_TX_DISABLE); + + if (nicStatus != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(( + "BasicInitializeAdapter: COMMAND_TX_DISABLE failed\n")); + DBGPRINT_FUNCTION(( + "BasicInitializeAdapter: Out with error\n")); + return NIC_STATUS_FAILURE; + + } + +#ifdef SERR_NEEDED + // Down stall the adapter and wait for 100 milliseconds for + // any pending PCI retry to be over. + NIC_COMMAND_WAIT(pAdapter, COMMAND_DOWN_STALL); +#endif + + nicStatus = NIC_COMMAND_WAIT(pAdapter, COMMAND_TX_RESET | TX_RESET_MASK_NETWORK_RESET); + + if (nicStatus != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(( + "BasicInitializeAdapter: COMMAND_TX_RESET failed\n")); + DBGPRINT_FUNCTION(( + "GetAdapterProperties: Out with error\n")); + return NIC_STATUS_FAILURE; + } + // + // ----------------- Rx engine handling -------------------- + // + + nicStatus = NIC_COMMAND_WAIT(pAdapter, COMMAND_RX_DISABLE); + + if (nicStatus != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(( + "BasicInitializeAdapter: Rx disable failed\n")); + DBGPRINT_FUNCTION(( + "BasicInitializeAdapter: Out with error\n")); + return NIC_STATUS_FAILURE; + } + +#ifdef SERR_NEEDED + // Up stall the adapter and wait for 100 milliseconds for + // any pending PCI retry to be over. + NIC_COMMAND_WAIT(pAdapter, COMMAND_UP_STALL); +#endif + + nicStatus = NIC_COMMAND_WAIT(pAdapter, COMMAND_RX_RESET | RX_RESET_MASK_NETWORK_RESET); + + if (nicStatus != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(( + "BasicInitializeAdapter: Rx reset failed\n" + )); + DBGPRINT_FUNCTION(( + "BasicInitializeAdapter: Out with error\n" + )); + return NIC_STATUS_FAILURE; + } + // + // Take care of the interrupts. + // + NIC_ACKNOWLEDGE_ALL_INTERRUPT(pAdapter); + NIC_COMMAND(pAdapter, COMMAND_STATISTICS_DISABLE); + + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_6); + // + // Clear the statistics from the hardware. + // + NIC_READ_PORT_UCHAR(pAdapter, CARRIER_LOST_REGISTER); + NIC_READ_PORT_UCHAR(pAdapter, SQE_ERRORS_REGISTER); + NIC_READ_PORT_UCHAR(pAdapter, MULTIPLE_COLLISIONS_REGISTER); + NIC_READ_PORT_UCHAR(pAdapter, SINGLE_COLLISIONS_REGISTER); + NIC_READ_PORT_UCHAR(pAdapter, LATE_COLLISIONS_REGISTER); + NIC_READ_PORT_UCHAR(pAdapter, RX_OVERRUNS_REGISTER); + NIC_READ_PORT_UCHAR(pAdapter, FRAMES_TRANSMITTED_OK_REGISTER); + NIC_READ_PORT_UCHAR(pAdapter, FRAMES_RECEIVED_OK_REGISTER); + NIC_READ_PORT_UCHAR(pAdapter, FRAMES_DEFERRED_REGISTER); + NIC_READ_PORT_UCHAR(pAdapter, UPPER_FRAMES_OK_REGISTER); + NIC_READ_PORT_USHORT(pAdapter, BYTES_RECEIVED_OK_REGISTER); + NIC_READ_PORT_USHORT(pAdapter, BYTES_TRANSMITTED_OK_REGISTER); + + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + + NIC_READ_PORT_UCHAR(pAdapter, BAD_SSD_REGISTER); + NIC_READ_PORT_UCHAR(pAdapter, UPPER_BYTES_OK_REGISTER); + // + // Program the station address. + // + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_2); + + stationTemp = pAdapter->StationAddress[1] << 8; + stationTemp |= pAdapter->StationAddress[0]; + + NIC_WRITE_PORT_USHORT( + pAdapter, + STATION_ADDRESS_LOW_REGISTER, + stationTemp); + + stationTemp = pAdapter->StationAddress[3] << 8; + stationTemp |= pAdapter->StationAddress[2]; + + NIC_WRITE_PORT_USHORT( + pAdapter, + STATION_ADDRESS_MID_REGISTER, + stationTemp); + + stationTemp = pAdapter->StationAddress[5] << 8; + stationTemp |= pAdapter->StationAddress[4]; + + NIC_WRITE_PORT_USHORT( + pAdapter, + STATION_ADDRESS_HIGH_REGISTER, + stationTemp); + + NIC_WRITE_PORT_USHORT(pAdapter, 0x6, 0); + NIC_WRITE_PORT_USHORT(pAdapter, 0x8, 0); + NIC_WRITE_PORT_USHORT(pAdapter, 0xA, 0); + NIC_COMMAND(pAdapter, COMMAND_STATISTICS_ENABLE); + // + // Clear the mac control register. + // + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_3); + + macControl = NIC_READ_PORT_USHORT(pAdapter, MAC_CONTROL_REGISTER); + macControl &= 0x1; + NIC_WRITE_PORT_USHORT(pAdapter, MAC_CONTROL_REGISTER, macControl); + + DBGPRINT_FUNCTION(( + "BasicInitializeAdapter: Out with success\n")); + + return NIC_STATUS_SUCCESS; + +} + +/*++ + +Routine Name: + + tc90x_AllocateSharedMemory. + +Routine Description: + + This routine allocates the shared memory + +Arguments: + + Device - Pointer to the device structure + +Return Value: + + NIC_STATUS_SUCCESS if memory allocations succeeds + NIC_STATUS_FAILURE if memory allocation fails + +--*/ + +NIC_STATUS +tc90x_AllocateSharedMemory( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + PDEVICE device = pAdapter->Device; + + ULONG updMemoryForOne, totalUPDMemory; + ULONG dpdMemoryForOne, totalDPDMemory; + + ULONG rxMemoryForOne; + + ULONG cacheLineSize, count, alignment; + + ULONG memoryBaseVirtual, memoryBasePhysical; + ULONG updMemoryVirtualStart, updMemoryPhysicalStart; + ULONG dpdMemoryVirtualStart, dpdMemoryPhysicalStart; + + ULONG currentUPDPhysical, previousUPDPhysical; + ULONG firstUPDPhysical = 0; + + PUPD_LIST_ENTRY currentUPDVirtual = NULL; + PUPD_LIST_ENTRY firstUPDVirtual = NULL; + PUPD_LIST_ENTRY previousUPDVirtual = NULL; + + PUCHAR dataPointer; + + PDPD_LIST_ENTRY currentDPDVirtual = NULL; + PDPD_LIST_ENTRY headDPDVirtual = NULL; + PDPD_LIST_ENTRY previousDPDVirtual = NULL; + ULONG currentDPDPhysical = 0; + + ULONG testMemoryVirtualStart, testMemoryPhysicalStart; + ULONG totalTestMemory; + + DBGPRINT_FUNCTION(("tc90x_AllocateSharedMemory: In\n")); + + cacheLineSize = pAdapter->Hardware.CacheLineSize; + // + // UPD structure memory requirement. + // + updMemoryForOne = sizeof(UPD_LIST_ENTRY) + cacheLineSize; + totalUPDMemory = pAdapter->Resources.ReceiveCount * updMemoryForOne; + // + // Receive buffer requirement. + // + rxMemoryForOne = ETHERNET_MAXIMUM_FRAME_SIZE + cacheLineSize; + // + // DPD structure memory requirement. + // + dpdMemoryForOne = sizeof(DPD_LIST_ENTRY) + cacheLineSize; + totalDPDMemory = pAdapter->Resources.SendCount * dpdMemoryForOne; + // + // Calculate the test memory required. + // + totalTestMemory = MAXIMUM_TEST_BUFFERS * + (dpdMemoryForOne + rxMemoryForOne); + + pAdapter->Resources.SharedMemorySize = totalUPDMemory + + totalDPDMemory + + totalTestMemory; + // + // Allocate the memory + // + pAdapter->Resources.SharedMemoryVirtual = + (PUCHAR) kmalloc( + pAdapter->Resources.SharedMemorySize, + GFP_KERNEL); + + pAdapter->ResourcesReserved |= NIC_SHARED_MEMORY_ALLOCATED; + // + // Zero out the memory. + // + memset( + pAdapter->Resources.SharedMemoryVirtual, + 0, + pAdapter->Resources.SharedMemorySize); + // + // -------------- Carve out the regions --------------- + // + memoryBaseVirtual = (ULONG)pAdapter->Resources.SharedMemoryVirtual; + memoryBasePhysical = virt_to_bus( + pAdapter->Resources.SharedMemoryVirtual); + + DBGPRINT_INITIALIZE(("memoryBaseVirtual = %x, memoryBasePhysical =%x\n", + (INT)memoryBaseVirtual, + (INT)memoryBasePhysical)); + // + // Virtual addresses of the regions. + // + updMemoryVirtualStart = memoryBaseVirtual; + dpdMemoryVirtualStart = updMemoryVirtualStart + totalUPDMemory; + testMemoryVirtualStart = dpdMemoryVirtualStart + totalDPDMemory; + // + // Physical addresses of the regions. + // + updMemoryPhysicalStart = memoryBasePhysical; + dpdMemoryPhysicalStart = updMemoryPhysicalStart + totalUPDMemory; + testMemoryPhysicalStart = dpdMemoryPhysicalStart + totalDPDMemory; + // + // -------------- Make the receive structures -------------- + // + for (count = 0; count < pAdapter->Resources.ReceiveCount; count++) { + + currentUPDPhysical = updMemoryPhysicalStart + + count * updMemoryForOne; + + alignment = cacheLineSize - + (currentUPDPhysical % cacheLineSize ); + currentUPDPhysical += alignment; + + currentUPDVirtual = (PUPD_LIST_ENTRY)( + updMemoryVirtualStart + + alignment + + count * updMemoryForOne ); + // + // Store the physical address of this UPD in the UPD itself. + // + currentUPDVirtual->UPDPhysicalAddress = currentUPDPhysical; + + if (0 == count) { + // + // Store the virtual and physical address of the + // first UPD. + firstUPDVirtual = currentUPDVirtual; + firstUPDPhysical = currentUPDPhysical; + + } + else { + // + // Put the links in the UPDs. + // + previousUPDVirtual->Next = currentUPDVirtual; + previousUPDVirtual->UpNextPointer = currentUPDPhysical; + currentUPDVirtual->Previous = previousUPDVirtual; + + } + // + // Allocate the skb per UPD + // + currentUPDVirtual->SocketBuffer = + DEV_ALLOC_SKB( + ETHERNET_MAXIMUM_FRAME_SIZE + + 2); + // + // Make the current UPD as the previous one. + // + if (currentUPDVirtual->SocketBuffer == 0) { + + DBGPRINT_ERROR(( + "Socket buffer allocation failed\n")); + return NIC_STATUS_FAILURE; + } + else { + + currentUPDVirtual->SocketBuffer->dev = device; + // + // Align IP on 16 byte boundaries + // + skb_reserve( + currentUPDVirtual->SocketBuffer, + 2); + + dataPointer = skb_put( + currentUPDVirtual->SocketBuffer, + ETHERNET_MAXIMUM_FRAME_SIZE ); + } + + currentUPDVirtual->RxBufferVirtual = dataPointer; + currentUPDVirtual->SGList[0].Address = virt_to_bus(dataPointer); + currentUPDVirtual->SGList[0].Count = + ETHERNET_MAXIMUM_FRAME_SIZE | 0x80000000; + + previousUPDVirtual = currentUPDVirtual; + previousUPDPhysical = currentUPDPhysical; + + } + // + // Link the first and last UPDs. + // + + currentUPDVirtual->Next = firstUPDVirtual; + currentUPDVirtual->UpNextPointer = firstUPDPhysical; + firstUPDVirtual->Previous = currentUPDVirtual; + // + // Save the address of the first UPD in the adapter structure. + // + pAdapter->HeadUPDVirtual = firstUPDVirtual; + + // + // --------------- Carve out DPD structures ----------------- + // + + for (count=0; count < pAdapter->Resources.SendCount; count++) { + + currentDPDPhysical = dpdMemoryPhysicalStart + + count * dpdMemoryForOne; + + alignment = cacheLineSize - + (currentDPDPhysical % cacheLineSize); + currentDPDPhysical += alignment; + + currentDPDVirtual = (PDPD_LIST_ENTRY)( + dpdMemoryVirtualStart + + count * dpdMemoryForOne + + alignment); + // + // Save the physical address of this DPD in the DPD itself. + // + currentDPDVirtual->DPDPhysicalAddress = currentDPDPhysical; + + if (0 == count) { + + headDPDVirtual = currentDPDVirtual; + + } + else { + + previousDPDVirtual->Next = currentDPDVirtual; + currentDPDVirtual->Previous = previousDPDVirtual; + } + previousDPDVirtual = currentDPDVirtual; + } + + // + // Link head and tail. + // + headDPDVirtual->Previous = currentDPDVirtual; + currentDPDVirtual->Next = headDPDVirtual; + // + // Point head and tail to this DPD. + // + pAdapter->HeadDPDVirtual = headDPDVirtual; + pAdapter->TailDPDVirtual = headDPDVirtual; + + // + // -------------- Test DPD and test buffer ----------------- + // + + for ( count = 0; count < MAXIMUM_TEST_BUFFERS; count++ ) { + + alignment = cacheLineSize - + (testMemoryPhysicalStart % cacheLineSize); + pAdapter->TestDPDVirtual[count] = testMemoryVirtualStart + + alignment; + pAdapter->TestDPDPhysical[count] = testMemoryPhysicalStart + + alignment; + + testMemoryVirtualStart += dpdMemoryForOne; + testMemoryPhysicalStart += dpdMemoryForOne; + + alignment = cacheLineSize - + (testMemoryPhysicalStart % cacheLineSize); + + pAdapter->TestBufferVirtual[count] = + testMemoryVirtualStart + alignment; + pAdapter->TestBufferPhysical[count] = + testMemoryPhysicalStart + alignment; + // + // Save the physical address of the DPD, in the DPD. + // + ((PDPD_LIST_ENTRY) + (pAdapter->TestDPDVirtual[count]))->DPDPhysicalAddress = + pAdapter->TestDPDPhysical[count]; + + testMemoryVirtualStart += rxMemoryForOne; + testMemoryPhysicalStart += rxMemoryForOne; + + } + + DBGPRINT_FUNCTION(("tc90x_AllocateSharedMemory: Out\n")); + return NIC_STATUS_SUCCESS; +} + +/*++ + +Routine Name: + + TestAdapter. + +Routine Description: + + This routine tests the functionality of the adapter. + +Arguments: + + MiniportAdapterContext - Pointer to the adapter structure. + +Return Value: + + NIC_STATUS_SUCCESS if the adapter is functioning properly. + NIC_STATUS_FAILURE if the adapter is not functioning properly. + +--*/ + +NIC_STATUS +tc90x_TestAdapter( + IN PNIC_INFORMATION Adapter + ) + +{ + PNIC_INFORMATION pAdapter = Adapter; + NIC_STATUS nicStatus; + PDPD_LIST_ENTRY dpdVirtual; + PUPD_LIST_ENTRY updVirtual; + PUCHAR sourceBufferVirtual, destinationBufferVirtual; + ULONG sourceBufferPhysical; + ULONG count; + USHORT networkDiagnosticsValue; + + DBGPRINT_FUNCTION(("TestAdapter: IN \n")); + // + // Select the network diagnostics window. + // + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + + // + // Read the network diagnostics register. + // + networkDiagnosticsValue = NIC_READ_PORT_USHORT( + pAdapter, + NETWORK_DIAGNOSTICS_REGISTER); + // + // Enable loop back on the adapter. + // + + networkDiagnosticsValue |= BIT_12; + NIC_WRITE_PORT_USHORT( + pAdapter, + NETWORK_DIAGNOSTICS_REGISTER, + networkDiagnosticsValue); + + NIC_COMMAND(pAdapter, COMMAND_TX_ENABLE); + NIC_COMMAND(pAdapter, COMMAND_RX_ENABLE); + // + // Write the address to the UpListPointer register. + // + + NIC_WRITE_PORT_ULONG( + pAdapter, + UP_LIST_POINTER_REGISTER, + pAdapter->HeadUPDVirtual->UPDPhysicalAddress); + + NIC_COMMAND(pAdapter, COMMAND_UP_UNSTALL); + // + // Use the buffer in the second UPD to make the packet. + // + sourceBufferVirtual = (PUCHAR)pAdapter->TestBufferVirtual[0]; + sourceBufferPhysical = pAdapter->TestBufferPhysical[0]; + + for (count=0; count < ETHERNET_MAXIMUM_FRAME_SIZE; count++) { + + *sourceBufferVirtual++ = (UCHAR)count; + } + + dpdVirtual = (PDPD_LIST_ENTRY)pAdapter->TestDPDVirtual[0]; + dpdVirtual->FrameStartHeader = ETHERNET_MAXIMUM_FRAME_SIZE; + dpdVirtual->DownNextPointer = 0; + dpdVirtual->SGList[0].Address = sourceBufferPhysical; + dpdVirtual->SGList[0].Count = ETHERNET_MAXIMUM_FRAME_SIZE | + 0x80000000; + + NIC_COMMAND_WAIT(pAdapter, COMMAND_DOWN_STALL); + // + // Write the down list pointer register. + // + NIC_WRITE_PORT_ULONG( + pAdapter, + DOWN_LIST_POINTER_REGISTER, + dpdVirtual->DPDPhysicalAddress); + + NIC_COMMAND(pAdapter, COMMAND_DOWN_UNSTALL); + // + // Check that packet has been picked up by the hardware. + // + portValue_g = NIC_READ_PORT_ULONG(pAdapter,DOWN_LIST_POINTER_REGISTER); + NIC_DELAY(10); + + if(portValue_g == dpdVirtual->DPDPhysicalAddress) + { pAdapter->WaitCases = CHECK_DOWNLOAD_STATUS; + TimeOutCount = jiffies + HZ; + if(!InWaitTimer) + { WaitTimer.expires = RUN_AT(HZ/100); //max=1s + add_timer(&WaitTimer); + InWaitTimer = TRUE; + } + while(TimeOutCount > jiffies) ; + if(portValue_g == dpdVirtual->DPDPhysicalAddress) + { DBGPRINT_ERROR(("Packet not picked up by the hardware\n")); + DBGPRINT_INITIALIZE(("TestAdapter: Out with error\n")); + return NIC_STATUS_FAILURE; + } + } + // + // Check the upload information. + // + portValue_g = NIC_READ_PORT_ULONG(pAdapter,UP_LIST_POINTER_REGISTER); + + NIC_DELAY(10); + + if(portValue_g == pAdapter->HeadUPDVirtual->UPDPhysicalAddress) + { pAdapter->WaitCases = CHECK_UPLOAD_STATUS; + TimeOutCount = jiffies + HZ; + if(!InWaitTimer) + { WaitTimer.expires = RUN_AT(HZ/100); // max=1s + add_timer(&WaitTimer); + InWaitTimer = TRUE; + } + while(TimeOutCount > jiffies) ; + if(portValue_g == pAdapter->HeadUPDVirtual->UPDPhysicalAddress) + { DBGPRINT_ERROR(("Packet not uploaded by adapter\n")); + DBGPRINT_INITIALIZE(("TestAdapter: Out with error\n")); + return NIC_STATUS_FAILURE; + } + } + // + // Check the contents of the packet. + // + updVirtual = pAdapter->HeadUPDVirtual; + destinationBufferVirtual = (PUCHAR)updVirtual->RxBufferVirtual; + + for (count=0; count < ETHERNET_MAXIMUM_FRAME_SIZE; count++) { + + if (*(destinationBufferVirtual + count) != (UCHAR)count) + break; + } + + if (ETHERNET_MAXIMUM_FRAME_SIZE != count) { + + DBGPRINT_ERROR(( + "TestAdapter: Receive buffer contents not ok\n")); + DBGPRINT_INITIALIZE(("TestAdapter: Out with error\n")); + return NIC_STATUS_FAILURE; + + } + + pAdapter->HeadUPDVirtual->UpPacketStatus = 0; + + NIC_WRITE_PORT_ULONG(pAdapter, UP_LIST_POINTER_REGISTER, 0); + NIC_ACKNOWLEDGE_ALL_INTERRUPT(pAdapter); + // + // Issue transmit and receive reset here. + // + nicStatus = NIC_COMMAND_WAIT(pAdapter, COMMAND_TX_RESET | TX_RESET_MASK_NETWORK_RESET); + + if (nicStatus != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(("TestAdapter: Transmit reset failed\n")); + DBGPRINT_INITIALIZE(("TestAdapter: Out with error\n")); + return NIC_STATUS_FAILURE; + + } + + nicStatus = NIC_COMMAND_WAIT(pAdapter, COMMAND_RX_RESET | RX_RESET_MASK_NETWORK_RESET); + + if (nicStatus != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(("TestAdapter: Receiver reset failed\n")); + DBGPRINT_INITIALIZE(("TestAdapter: Out with error\n")); + return NIC_STATUS_FAILURE; + + } + // + // Clear the loop back bit in network diagnostics. + // + networkDiagnosticsValue &= ~BIT_12; + + NIC_WRITE_PORT_USHORT( + pAdapter, + NETWORK_DIAGNOSTICS_REGISTER, + networkDiagnosticsValue); + + DBGPRINT_FUNCTION(("TestAdapter: OUT\n")); + return NIC_STATUS_SUCCESS; +} + + +/*++ + +Routine Name: + + StartAdapter. + +Routine Description: + + This routine starts the adapter. + +Arguments: + + MiniportAdapterContext - Pointer to the adapter structure. + +Return Value: + + None. + +--*/ + +NIC_STATUS +tc90x_StartAdapter( + IN PNIC_INFORMATION Adapter + ) +{ + + PNIC_INFORMATION pAdapter = Adapter; + + + NIC_STATUS nicStatus; + + PDPD_LIST_ENTRY headDPDVirtual; + + USHORT diagnostics; + ULONG dmaControl; + + DBGPRINT_FUNCTION(("StartAdapter: In\n")); + + + // + // Enable upper bytes counting in diagnostics register. + // + + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + + diagnostics = NIC_READ_PORT_USHORT( + pAdapter, + NETWORK_DIAGNOSTICS_REGISTER); + + diagnostics |= NETWORK_DIAGNOSTICS_UPPER_BYTES_ENABLE; + + NIC_WRITE_PORT_USHORT( + pAdapter, + NETWORK_DIAGNOSTICS_REGISTER, + diagnostics); + // + // Enable counter speed in DMA control. + // + dmaControl = NIC_READ_PORT_ULONG( + pAdapter, + DMA_CONTROL_REGISTER); + + if (100000000 == pAdapter->Hardware.LinkSpeed) + dmaControl |= DMA_CONTROL_COUNTER_SPEED; + + NIC_WRITE_PORT_ULONG( + pAdapter, + DMA_CONTROL_REGISTER, + dmaControl); + // + // ------------ Give download structures to the adapter ----- + // + // Stall the download engine. + // + nicStatus = NIC_COMMAND_WAIT(pAdapter, COMMAND_DOWN_STALL); + + if (nicStatus != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(("StartAdapter: down stall failed\n")); + DBGPRINT_INITIALIZE(("StartAdapter: Out with error\n")); + return NIC_STATUS_FAILURE; + + } + // + // Use the head DPD to mark it as dummy. + // + headDPDVirtual = pAdapter->HeadDPDVirtual; + headDPDVirtual->FrameStartHeader = FSH_DPD_EMPTY; + // + // Now move head and tail to next one. + // + pAdapter->HeadDPDVirtual = headDPDVirtual->Next; + pAdapter->TailDPDVirtual = headDPDVirtual->Next; + // + // Write the first DPD address to the hardware. + // + NIC_WRITE_PORT_ULONG( + pAdapter, + DOWN_LIST_POINTER_REGISTER, + headDPDVirtual->DPDPhysicalAddress); + // + // Enable down polling on the hardware. + // + NIC_WRITE_PORT_UCHAR( + pAdapter, + DOWN_POLL_REGISTER, + (UCHAR)pAdapter->Resources.DownPollRate); + // + // Unstall the download engine. + // + NIC_COMMAND(pAdapter, COMMAND_DOWN_UNSTALL); + // + // ------------ Give upload structures to the adapter ------- + // + // + // Stall the upload engine. + // + nicStatus = NIC_COMMAND_WAIT(pAdapter, COMMAND_UP_STALL); + + if (nicStatus != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(("StartAdapter: up stall failed\n")); + DBGPRINT_INITIALIZE(("StartAdapter: Out with error\n")); + return NIC_STATUS_FAILURE; + } + // + // Give the address of the first UPD to the adapter. + // + + NIC_WRITE_PORT_ULONG( + pAdapter, + UP_LIST_POINTER_REGISTER, + pAdapter->HeadUPDVirtual->UPDPhysicalAddress); + // + // Write the up poll register. + // + NIC_WRITE_PORT_UCHAR( + pAdapter, + UP_POLL_REGISTER, + 8); + // + // Unstall the download engine. + // + NIC_COMMAND(pAdapter, COMMAND_UP_UNSTALL); + // + // Enable the statistics back. + // + NIC_COMMAND(pAdapter, COMMAND_STATISTICS_ENABLE); + // + // Acknowledge any pending interrupts. + // + NIC_ACKNOWLEDGE_ALL_INTERRUPT(pAdapter); + // + // Enable indication for all interrupts. + // + NIC_ENABLE_ALL_INTERRUPT_INDICATION(pAdapter); + // + // Enable all interrupts to the host. + // + NIC_UNMASK_ALL_INTERRUPT(pAdapter); + // + // Enable the transmit and receive engines. + // + NIC_COMMAND(pAdapter, COMMAND_RX_ENABLE); + NIC_COMMAND(pAdapter, COMMAND_TX_ENABLE); + // + // Delay three seconds, only some switches need this, + // default is no delay, user can enable this delay in command line + // + if(pAdapter->DelayStart == TRUE) { + TimeOutCount = jiffies + 3*HZ; + while(TimeOutCount > jiffies) ; + } + DBGPRINT_FUNCTION(("StartAdapter: Out\n")); + return NIC_STATUS_SUCCESS; +} + +/*++ + +Routine Name: + + ReStartAdapter. + +Routine Description: + + This routine starts the adapter. + +Arguments: + + MiniportAdapterContext - Pointer to the adapter structure. + +Return Value: + + None. + +--*/ + +VOID +ReStartAdapter( + //IN PNIC_INFORMATION Adapter + PVOID Adapter + ) +{ + + PNIC_INFORMATION pAdapter = Adapter; + ULONG internalConfig = pAdapter->keepForGlobalReset; + + DBGPRINT_INTERRUPT((KERN_CRIT "ReStartAdapter after global reset: IN\n")); + // + // Delay for 1 second, might not be that long since some has been comsumed with task queue + TimeOutCount = jiffies + HZ; + while(TimeOutCount > jiffies) ; + // + // Mask all the interrupts. + // + NIC_MASK_ALL_INTERRUPT(pAdapter); + // + // Enable indication for all interrupts. + // + NIC_ENABLE_ALL_INTERRUPT_INDICATION(pAdapter); + // + // Write the internal config back. + // + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_3); + + NIC_WRITE_PORT_ULONG( + pAdapter, + INTERNAL_CONFIG_REGISTER, + internalConfig); + // + // Set the adapter for operation + // + if (tc90x_GetAdapterProperties(pAdapter) != NIC_STATUS_SUCCESS) + DBGPRINT_INTERRUPT((KERN_CRIT "GetAdapterProperties failed\n")); + + if (tc90x_BasicInitializeAdapter(pAdapter) != NIC_STATUS_SUCCESS) + DBGPRINT_INTERRUPT((KERN_CRIT "BasicInitialize failed\n")); + + if (tc90x_SetupMedia(pAdapter->Device) != NIC_STATUS_SUCCESS) + DBGPRINT_INTERRUPT((KERN_CRIT "SetupMedia failed\n")); + + if (tc90x_SoftwareWork(pAdapter) != NIC_STATUS_SUCCESS) + DBGPRINT_INTERRUPT((KERN_CRIT "SoftwareWork failed\n")); + // + // Countdown timer is cleared by reset. So write it back. + // + tc90x_SetCountDownTimer(pAdapter); + + pAdapter->DelayStart = FALSE; // no need to delay for switch here + + if (tc90x_StartAdapter(pAdapter) != NIC_STATUS_SUCCESS) + DBGPRINT_INTERRUPT((KERN_CRIT "ReStartAdapter: StartAdapter failed\n")); + + DBGPRINT_INTERRUPT((KERN_CRIT "ReStartAdapter after global reset: OUT\n")); + + return; +} + + +UCHAR firstTime = 0; + +/*++ + +Routine Name: + + NICSendPacket + +Routine Description: + + This routine sends the packet + +Arguments: + + SocketBuffer - Pointer to the socket buffer + Device - Pointer to the device structure + +Return Value: + + 0 if packet could be sent + 1 if packet could not be sent + +--*/ + +INT +NICSendPacket( + IN PSKB SocketBuffer, + IN PDEVICE Device + ) +{ + PNIC_INFORMATION pAdapter = (PNIC_INFORMATION)Device->priv; + PDPD_LIST_ENTRY dpdVirtual; + ULONG packetLength, flags; +#ifdef DEBUG + PDEVICE device = pAdapter->Device; +#endif + if (test_and_set_bit(0, (void*)&Device->tbusy) != 0) + { DBGPRINT_SEND(("SendPacket: %s Device busy\n",device->name)); + return 1; + } + //spin_lock_irqsave(&pAdapter->SpinLock_send, flags); + if (pAdapter->TailDPDVirtual->Next == pAdapter->HeadDPDVirtual) + { DBGPRINT_SEND(("SendPacket: %s DPDring full\n",device->name)); + pAdapter->DPDRingFull = TRUE; + //spin_unlock_irqrestore(&pAdapter->SpinLock_send, flags); + return 1; + } + /* Get the free DPD from the DPD ring */ + dpdVirtual = pAdapter->TailDPDVirtual; + dpdVirtual->FrameStartHeader = 0; + dpdVirtual->SGList[0].Address = virt_to_bus( + SocketBuffer->data); + packetLength = SocketBuffer->len; + dpdVirtual->SGList[0].Count = packetLength | 0x80000000; + dpdVirtual->FrameStartHeader |= (ULONG)(FSH_ROUND_UP_DEFEAT); + dpdVirtual->SocketBuffer = SocketBuffer; + dpdVirtual->PacketLength = SocketBuffer->len; + dpdVirtual->DownNextPointer = 0; + + save_flags(flags); + cli(); + NIC_COMMAND_WAIT(pAdapter, COMMAND_DOWN_STALL); + pAdapter->BytesInDPDQueue += packetLength; + pAdapter->TailDPDVirtual->Previous->DownNextPointer = + dpdVirtual->DPDPhysicalAddress; + pAdapter->TailDPDVirtual = dpdVirtual->Next; + NIC_COMMAND(pAdapter, COMMAND_DOWN_UNSTALL); + tc90x_SetCountDownTimer(pAdapter); + restore_flags(flags); + + //spin_unlock_irqrestore(&pAdapter->SpinLock_send, flags); + clear_bit(0, (void*)&Device->tbusy); + Device->trans_start = jiffies; + return 0; +} + + +/*++ + +Routine Name: + + tc90x_TxCompleteEvent. + +Routine Description: + + This routine handles the Tx complete event. + +Arguments: + + Device - Pointer to the device structure. + +Return Value: + + None + +--*/ + +VOID +tc90x_TxCompleteEvent( + IN PNIC_INFORMATION Adapter + ) + +{ + PNIC_INFORMATION pAdapter = Adapter; + + UCHAR txStatus; + + DBGPRINT_ERROR((KERN_CRIT "TxCompleteEvent: IN\n")); + + LOG_LABEL(pAdapter, "TCE>"); + + txStatus = NIC_READ_PORT_UCHAR(pAdapter, TX_STATUS_REGISTER); + NIC_WRITE_PORT_UCHAR(pAdapter, TX_STATUS_REGISTER, txStatus); + + if (txStatus & TX_STATUS_HWERROR) { + // + // Transmit HWError recovery. + // + DBGPRINT_SEND(("TxCompleteEvent: TxHWError\n")); + pAdapter->Statistics.TxHWErrors++; + if (tc90x_ResetAndEnableTransmitter(pAdapter) != + NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(("TxCompleteEvent: TxReset failed\n")); + pAdapter->Hardware.Status = HARDWARE_STATUS_HUNG; + return; + + } + + } + else if (txStatus & TX_STATUS_JABBER) { + + DBGPRINT_ERROR(("TxCompleteEvent: Jabber\n")); + pAdapter->Statistics.TxJabberError++; + + if (tc90x_ResetAndEnableTransmitter(pAdapter) != + NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(("TxCompleteEvent: TxReset failed\n")); + pAdapter->Hardware.Status = HARDWARE_STATUS_HUNG; + return; + } + } + else if (txStatus & TX_STATUS_MAXIMUM_COLLISION) { + + DBGPRINT_ERROR(("TxCompleteEvent: Maximum collision\n")); + pAdapter->Statistics.TxMaximumCollisions++; + NIC_COMMAND(pAdapter, COMMAND_TX_ENABLE); + } + else { + + if (txStatus != 0 ) { + + DBGPRINT_ERROR(( + KERN_CRIT "TxCompleteEvent: Unknown error\n")); + pAdapter->Statistics.TxUnknownError++; + NIC_COMMAND(pAdapter, COMMAND_TX_ENABLE); + } + } + DBGPRINT_ERROR((KERN_CRIT "TxCompleteEvent: OUT\n")); +} + + +/*++ + +Routine Name: + + tc90x_ResetAndEnableTransmitter + +Routine Description: + + This routine resets the transmitter. + +Arguments: + + Device - Pointer to the device structure. + +Return Value: + + NIC_STATUS_SUCCESS if reset is successful. + NIC_STATUS_FAILURE if reset failed. + +--*/ + + +NIC_STATUS +tc90x_ResetAndEnableTransmitter( + IN PNIC_INFORMATION Adapter + ) + +{ + PNIC_INFORMATION pAdapter = Adapter; + NIC_STATUS nicStatus; + + NIC_COMMAND(pAdapter, COMMAND_TX_DISABLE); + // + // Wait for the transmit to go quiet. + // + NIC_COMMAND(pAdapter,COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + + MediaStatus_g = NIC_READ_PORT_USHORT(pAdapter,MEDIA_STATUS_REGISTER); + + NIC_DELAY(10); + + if(MediaStatus_g & MEDIA_STATUS_TX_IN_PROGRESS) + { pAdapter->WaitCases = CHECK_TRANSMIT_IN_PROGRESS; + TimeOutCount = jiffies + HZ; + if(!InWaitTimer) + { WaitTimer.expires = RUN_AT(HZ/100); //max = 1s + add_timer(&WaitTimer); + InWaitTimer = TRUE; + } + while(TimeOutCount > jiffies); + if(!(MediaStatus_g & MEDIA_STATUS_TX_IN_PROGRESS)) + { DBGPRINT_ERROR(( + "tc90x_ResetAndEnableTransmitter: media status is hung\n")); + return NIC_STATUS_FAILURE; + } + } + +#ifdef SERR_NEEDED + // + // Issue down stall and delay 100 miliseconds for PCI retries to be over + // + NIC_COMMAND_WAIT(pAdapter, COMMAND_DOWN_STALL); + TimeOutCount = jiffies + HZ/10; + while(TimeOutCount > jiffies) ; +#endif + // + // Wait for download engine to stop + // + dmaControl_g = NIC_READ_PORT_ULONG(pAdapter,DMA_CONTROL_REGISTER); + NIC_DELAY(10); + + if(dmaControl_g & DMA_CONTROL_DOWN_IN_PROGRESS) + { pAdapter->WaitCases = CHECK_DMA_CONTROL; + TimeOutCount = jiffies + HZ; + if(!InWaitTimer) + { WaitTimer.expires = RUN_AT(HZ/100); //max = 1s + add_timer(&WaitTimer); + InWaitTimer = TRUE; + } + while(TimeOutCount > jiffies); + if(!(dmaControl_g & DMA_CONTROL_DOWN_IN_PROGRESS)) + { DBGPRINT_ERROR(( + "tc90x_ResetAndEnableTransmitter: DMAControl hung\n")); + return NIC_STATUS_FAILURE; + } + } + + nicStatus = NIC_COMMAND_WAIT( + pAdapter, + COMMAND_TX_RESET | + TX_RESET_MASK_DOWN_RESET ); + + if (nicStatus != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(( + "tc90x_ResetAndEnableTransmitter: Tx reset failed\n")); + return NIC_STATUS_FAILURE; + + } + + NIC_COMMAND(pAdapter, COMMAND_TX_ENABLE); + + return NIC_STATUS_SUCCESS; +} + + + +/*++ + +Routine Name: + + tc90x_CleanupSendLogic + +Routine Description: + + This routines cleans the send logic. + +Arguments: + + Device - Pointer to the device structure. + +Return Value: + + None + +--*/ + +VOID +tc90x_CleanupSendLogic( + IN PDEVICE Device + ) + +{ + PNIC_INFORMATION pAdapter = (PNIC_INFORMATION)Device->priv; + PDPD_LIST_ENTRY headDPDVirtual; + + DBGPRINT_SEND(("tc90x_CleanupSendLogic: IN\n")); + // + // Now clean up the DPD ring. + // + headDPDVirtual = pAdapter->HeadDPDVirtual; + // + // This is to take care of hardware raise condition. + // + pAdapter->TailDPDVirtual->FrameStartHeader = 0; + + while (1) { + + if (headDPDVirtual == pAdapter->TailDPDVirtual) + break; + // + // Complete all the packets. + // + pAdapter->BytesInDPDQueue -= headDPDVirtual->PacketLength; + + DEV_FREE_SKB(headDPDVirtual->SocketBuffer); + + headDPDVirtual->SocketBuffer = NULL; + headDPDVirtual->FrameStartHeader = 0; + + headDPDVirtual = headDPDVirtual->Next; + } + // + // Update the head to point to this DPD now. + // + pAdapter->HeadDPDVirtual = headDPDVirtual; + // + // Initialize all DPDs. + // + headDPDVirtual = pAdapter->HeadDPDVirtual; + + while (1) { + + headDPDVirtual->DownNextPointer = 0; + headDPDVirtual->SocketBuffer = NULL; + headDPDVirtual->FrameStartHeader = 0; + headDPDVirtual = headDPDVirtual->Next; + if (headDPDVirtual == pAdapter->HeadDPDVirtual) + break; + } + + DBGPRINT_SEND(("tc90x_CleanupSendLogic: OUT\n")); +} + + + + + +/*++ + +Routine Name: + + tc90x_UpCompleteEvent + +Routine Description: + + This routine handles the receive event. + +Arguments: + + Adapter - Pointer to the adapter structure + +Return Value: + + None + +--*/ + +VOID +tc90x_UpCompleteEvent( + IN PNIC_INFORMATION Adapter + ) + +{ + PNIC_INFORMATION pAdapter = Adapter; + PDEVICE device = pAdapter->Device; + PUPD_LIST_ENTRY currentUPDVirtual = pAdapter->HeadUPDVirtual; + + PSKB socketBuffer; + + ULONG upPacketStatus; + ULONG frameLength; + PUCHAR dataPointer; + PETH_ADDR EthAddr; + + DBGPRINT_RECEIVE(("UpCompleteEvent: IN \n")); + LOG_LABEL(pAdapter, "UCE>"); + + + while (1) { + // + // If done with all UPDs break. + // + upPacketStatus = currentUPDVirtual->UpPacketStatus; + + if (!(upPacketStatus & UP_PACKET_STATUS_COMPLETE)) { + break; + } + // + // Get the frame length from the UPD. + // + frameLength = currentUPDVirtual->UpPacketStatus & 0x1FFF; + // + // Check if there is any error bit set. + // + if (upPacketStatus & UP_PACKET_STATUS_ERROR) { + + if ((frameLength < ETHERNET_MINIMUM_FRAME_SIZE) || + (upPacketStatus & UP_PACKET_STATUS_OVERRUN) || + (upPacketStatus & UP_PACKET_STATUS_ALIGNMENT_ERROR) || + (upPacketStatus & UP_PACKET_STATUS_CRC_ERROR) || + (upPacketStatus & UP_PACKET_STATUS_OVERSIZE_FRAME)) { + + if (upPacketStatus & UP_PACKET_STATUS_RUNT_FRAME) { + DBGPRINT_ERROR(("UpCompleteEvent: Runt\n")); + LOG_LABEL(pAdapter, "RUNT"); + } + if (upPacketStatus & UP_PACKET_STATUS_ALIGNMENT_ERROR) { + LOG_LABEL(pAdapter, "ALGN"); + DBGPRINT_ERROR(("UpCompleteEvent: Alignment\n")); + pAdapter->Statistics.RxAlignmentError++; + } + if (upPacketStatus & UP_PACKET_STATUS_CRC_ERROR) { + LOG_LABEL(pAdapter, "CRC "); + DBGPRINT_ERROR(("UpCompleteEvent: Crc\n")); + pAdapter->Statistics.RxBadCRCError++; + + } + if (upPacketStatus & UP_PACKET_STATUS_OVERSIZE_FRAME){ + LOG_LABEL(pAdapter, "OVSZ"); + DBGPRINT_ERROR(("UpCompleteEvent: Oversize\n")); + pAdapter->Statistics.RxOversizeError++; + + } + // + // Discard this packet and move on. + // + currentUPDVirtual->UpPacketStatus = 0; + currentUPDVirtual = currentUPDVirtual->Next; + continue; + } + else { + pAdapter->Statistics.RxFramesOk++; + pAdapter->Statistics.RxBytesOk += frameLength; + } + } + // + // Try to allocate SKB + // + socketBuffer = DEV_ALLOC_SKB( + ETHERNET_MAXIMUM_FRAME_SIZE + 2 + + pAdapter->Hardware.CacheLineSize); + + if (socketBuffer != 0) { + socketBuffer->dev = device; + // + // Align IP on 16 byte boundaries + // + skb_reserve(socketBuffer, 2); + dataPointer = skb_put( + socketBuffer, + ETHERNET_MAXIMUM_FRAME_SIZE ); + currentUPDVirtual->SGList[0].Address = + virt_to_bus(dataPointer); + + currentUPDVirtual->SGList[0].Count = + ETHERNET_MAXIMUM_FRAME_SIZE | 0x80000000; + + currentUPDVirtual->RxBufferVirtual = dataPointer; + + currentUPDVirtual->SocketBuffer->protocol = + eth_type_trans( + currentUPDVirtual->SocketBuffer, + device); + + SetRxTcpIpChecksumOffloadFlagsInSocketBuffer( + pAdapter, + currentUPDVirtual->SocketBuffer, + currentUPDVirtual->UpPacketStatus); + + netif_rx(currentUPDVirtual->SocketBuffer); + device->last_rx = jiffies; + currentUPDVirtual->SocketBuffer = socketBuffer; + // + // Check for Multicast + // + EthAddr = (PETH_ADDR)(currentUPDVirtual->SocketBuffer->data); + if( (EthAddr->Addr[0] & ETH_MULTICAST_BIT) && + !(COMPARE_MACS(EthAddr, BroadcastAddr)) ) + pAdapter->Statistics.Rx_MulticastPkts++; + } + else { + + printk( KERN_CRIT "SKB allocation failed\n"); + DBGPRINT_ERROR(( + "UpCompleteEvent: SKB allocation failed\n")); + + } + currentUPDVirtual->UpPacketStatus = 0; + currentUPDVirtual = currentUPDVirtual->Next; + } + pAdapter->HeadUPDVirtual = currentUPDVirtual; + + DBGPRINT_RECEIVE(("UpCompleteEvent: OUT \n")); + LOG_LABEL(pAdapter, "UCE<"); +} + + +/*++ + +Routine Name: + + tc90x_ResetAndEnableReceiver. + +Routine Description: + + This routine resets the receiver. + +Arguments: + + Adapter - Pointer to the adapter structure + +Return Value: + + NIC_STATUS_SUCCESS if reset succeeds. + +--*/ + +NIC_STATUS +tc90x_ResetAndEnableReceiver( + IN PNIC_INFORMATION Adapter + ) + +{ + + PNIC_INFORMATION pAdapter = Adapter; + + NIC_COMMAND_WAIT(pAdapter, COMMAND_RX_DISABLE); + NIC_COMMAND_WAIT(pAdapter, COMMAND_RX_RESET | RX_RESET_MASK_NETWORK_RESET); + NIC_COMMAND_WAIT(pAdapter, COMMAND_RX_ENABLE); + + return NIC_STATUS_SUCCESS; + +} + + + + +/*++ + +Routine Name: + + NICIoctl + +Routine Description: + + This routine is IOCTL handler. + +Arguments: + + Device - Pointer to the device structure. + +Return Value: + + -ENODEV if no adapter found + noAdapterFound if adapter(s) found + +--*/ + +INT NICIoctl( IN PDEVICE Device, + IN PIFREQ Request, + IN INT command ) +{ + BOOLEAN PhyResponding; + long ioaddr; + u16 *data; + USHORT phy; + PNIC_INFORMATION pAdapter; + + DBGPRINT_IOCTL(("NICIoctl: IN\n")); + + pAdapter = (PNIC_INFORMATION)Device->priv; + ioaddr = Device->base_addr; + data = (u16 *)&Request->ifr_data; + phy = pAdapter->Hardware.phys; + + switch(command) { + + case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */ + data[0] = phy; + /* Fall Through */ + + case SIOCDEVPRIVATE+1: /* Read the specified MII register. */ + + PhyResponding = ReadMIIPhy(pAdapter, data[0], (PUSHORT)&data[3]); + if (!PhyResponding) + DBGPRINT_ERROR(("IOCTL-ReadPhy: Phy not responding")); + return 0; + + case SIOCDEVPRIVATE+2: /* Write the specified MII register */ + if (!suser()) + return -EPERM; + WriteMIIPhy(pAdapter, data[0], data[2]); + return 0; + + default: + return -EOPNOTSUPP; + } +} + + +/*++ + +Routine Name: + + NICSetReceiveMode + +Routine Description: + + This routine sets receive mode + +Arguments: + + Device - Pointer to the device structure. + +Return Value: + + -ENODEV if no adapter found + noAdapterFound if adapter(s) found + +--*/ + +VOID +NICSetReceiveMode( + IN PDEVICE Device + ) +{ + + PNIC_INFORMATION pAdapter = (PNIC_INFORMATION)Device->priv; + ULONG count, hardwareReceiveFilter = 0; + UCHAR flowControlAddress[ETHERNET_ADDRESS_SIZE]; + UCHAR broadcastAddress[ETHERNET_ADDRESS_SIZE]; + UCHAR address[ETHERNET_ADDRESS_SIZE]; + ULONG numberMulticast, bitsInHashFilter, index; + PDEV_MC_LIST multicastList; + + + DBGPRINT_FUNCTION(("NICSetReceiveMode: In\n")); + +#if LINUX_VERSION_CODE >= 0x20200 + spin_lock(&pAdapter->SpinLock_misc); +#endif + + bitsInHashFilter = pAdapter->Hardware.BitsInHashFilter; + // + // Check if Promiscuous mode to be enabled. + // + if (Device->flags & IFF_PROMISC) { + + DBGPRINT_INITIALIZE(("IFF_PROMISC mode \n")); + hardwareReceiveFilter |= RX_FILTER_PROMISCUOUS; + } + else if (Device->flags & IFF_ALLMULTI) { + // + // Check if ALL_MULTI mode to be enabled. + // + DBGPRINT_INITIALIZE(("IFF_ALLMULTI\n")); + hardwareReceiveFilter |= RX_FILTER_INDIVIDUAL; + hardwareReceiveFilter |= RX_FILTER_BROADCAST; + hardwareReceiveFilter |= RX_FILTER_ALL_MULTICAST; + } + else if (Device->flags & IFF_MULTICAST) { + // + // Check if hash multicast to be enabled. + // + DBGPRINT_INITIALIZE(("IFF_MULTICAST\n")); + hardwareReceiveFilter |= RX_FILTER_INDIVIDUAL; + hardwareReceiveFilter |= RX_FILTER_BROADCAST; + hardwareReceiveFilter |= RX_FILTER_MULTICAST_HASH; + } + else { + // + // OS does not want to enable multicast. + // + DBGPRINT_INITIALIZE(( + "Setting filter individual and broadcast\n")); + hardwareReceiveFilter |= RX_FILTER_INDIVIDUAL; + hardwareReceiveFilter |= RX_FILTER_BROADCAST; + } + // + // Write the Rx filter + // + NIC_COMMAND( + pAdapter, + (USHORT)(COMMAND_SET_RX_FILTER | hardwareReceiveFilter)); + // + // Clear the hash filter. + // + for (count=0; count < bitsInHashFilter; count++) { + + NIC_COMMAND( + pAdapter, + (USHORT)(COMMAND_SET_HASH_FILTER_BIT | count)); + } + // + // Set the hash filter. + // + numberMulticast = Device->mc_count; + multicastList = Device->mc_list; + + DBGPRINT_INITIALIZE(("mcCount = %x\n", (INT)numberMulticast)); + + for (count=0; count < numberMulticast; count++) { + + DBGPRINT_INITIALIZE(("Multicast = ")); + + for (index=0; index < 6; index++) { + address[index] = multicastList->dmi_addr[index]; + DBGPRINT_INITIALIZE(("%x", address[index])); + } + + DBGPRINT_INITIALIZE(("\n")); + + NIC_COMMAND( + pAdapter, + (USHORT) + (COMMAND_SET_HASH_FILTER_BIT | + 0x400 | + tc90x_HashAddress(address))); + + multicastList = multicastList->next; + } + // + // If receive filter is not promiscuos or multicast, enable + // hash multicast for receiving the flow control packets and + // for the broadcast. + // + if (!((hardwareReceiveFilter & RX_FILTER_PROMISCUOUS) || + (hardwareReceiveFilter & RX_FILTER_ALL_MULTICAST))) { + + hardwareReceiveFilter |= RX_FILTER_MULTICAST_HASH; + // + // Set the flow control enable + // + if (pAdapter->Hardware.FlowControlEnable && + pAdapter->Hardware.FlowControlSupported && + pAdapter->Hardware.FullDuplexEnable) { + + DBGPRINT_INITIALIZE(("Setting flow control bit\n")); + // + // Set the flow control address bit + // + flowControlAddress[0] = NIC_FLOW_CONTROL_ADDRESS_0; + flowControlAddress[1] = NIC_FLOW_CONTROL_ADDRESS_1; + flowControlAddress[2] = NIC_FLOW_CONTROL_ADDRESS_2; + flowControlAddress[3] = NIC_FLOW_CONTROL_ADDRESS_3; + flowControlAddress[4] = NIC_FLOW_CONTROL_ADDRESS_4; + flowControlAddress[5] = NIC_FLOW_CONTROL_ADDRESS_5; + + NIC_COMMAND( + pAdapter, + (USHORT)(COMMAND_SET_HASH_FILTER_BIT | + 0x0400 | + tc90x_HashAddress(flowControlAddress))); + } + // + // If there is a broadcast error, write value for broadcast. + // + if (FALSE == pAdapter->Hardware.BroadcastErrDone) { + + DBGPRINT_INITIALIZE(("Broadcast Err Done\n")); + for (count=0; count < ETHERNET_ADDRESS_SIZE; count++) + broadcastAddress[count] = 0xff; + + NIC_COMMAND( + pAdapter, + (USHORT) + (COMMAND_SET_HASH_FILTER_BIT | + 0x400 | + tc90x_HashAddress(broadcastAddress)) ); + } + } +#if LINUX_VERSION_CODE >= 0x20200 + spin_unlock(&pAdapter->SpinLock_misc); +#endif + + DBGPRINT_FUNCTION(("NICSetReceiveMode: Out\n")); +} + + +/*++ + +Routine Name: + + NICGetStatistics + +Routine Description: + + This routine gets the statistics. + +Arguments: + + Device - Pointer to the device structure. + +Return Value: + + -ENODEV if no adapter found + noAdapterFound if adapter(s) found + +--*/ + +PENET_STATISTICS +NICGetStatistics( + IN PDEVICE Device + ) +{ + PNIC_INFORMATION pAdapter = (PNIC_INFORMATION)Device->priv; + PNIC_STATISTICS statistics = &pAdapter->Statistics; + PENET_STATISTICS enetStatistics = &pAdapter->EnetStatistics; + ULONG flags; + + if(Device->start) { + save_flags(flags); + cli(); + + enetStatistics->rx_packets = statistics->RxFramesOk; + enetStatistics->tx_packets = statistics->TxFramesOk; + +#if LINUX_VERSION_CODE > 0x20024 + enetStatistics->rx_bytes = statistics->RxBytesOk; + enetStatistics->tx_bytes = statistics->TxBytesOk; +#endif + + enetStatistics->rx_errors = statistics->RxBadCRCError + + statistics->RxOverruns + + statistics->RxAlignmentError + statistics->RxOversizeError; + + enetStatistics->tx_errors = statistics->TxHWErrors + + statistics->TxMaximumCollisions + + statistics->TxJabberError + + statistics->TxUnknownError; + + enetStatistics->rx_dropped = statistics->RxOverruns; + enetStatistics->multicast = statistics->Rx_MulticastPkts; + enetStatistics->collisions = statistics->TxMaximumCollisions; + enetStatistics->rx_over_errors = statistics->RxOversizeError; + enetStatistics->rx_crc_errors = statistics->RxBadCRCError; + enetStatistics->rx_frame_errors = statistics->RxAlignmentError; + enetStatistics->rx_fifo_errors = statistics->RxOverruns; + enetStatistics->tx_aborted_errors = statistics->TxUnknownError; + enetStatistics->tx_carrier_errors = statistics->TxCarrierLost; + enetStatistics->tx_heartbeat_errors = statistics->TxSQEErrors; + + enetStatistics->rx_missed_errors = 0; + enetStatistics->tx_dropped = 0; + enetStatistics->rx_length_errors= 0; + enetStatistics->tx_fifo_errors = 0; + enetStatistics->tx_window_errors = 0; + + restore_flags(flags); + } + + return enetStatistics; +} + + +/*++ + +Routine Name: + + tc90x_HashAddress. + +Routine Description: + + This routine returns a bit corresponding to the hash address. + +Arguments: + + MulticastAddress - Mutlicast address to be hashed. + +Return Value: + + Hash Value + +--*/ + +USHORT +tc90x_HashAddress( + IN PUCHAR Address + ) +{ + + ULONG crc, carry, count, bit; + UCHAR thisByte; + // + // Intialize CRC. + // + crc = 0xffffffff; + + for (count = 0; count < 6; count++) { + thisByte = Address[count]; + for ( bit = 0; bit < 8; bit++) { + carry = ((crc & 0x80000000) ? 1 : 0) ^ + (thisByte & 0x01); + crc <<= 1; + thisByte >>= 1; + if (carry) + crc = (crc ^ 0x04c11db6) | carry; + } + + } + return (USHORT)(crc & 0x000003FF) ; +} + +/*++ + +Routine Name: + + tc90x_SetMulticastAddresses. + +Routine Description: + + This routine sets up the multicast list on the adapter. + +Arguments: + + Device - Pointer to the device structure + +Return Value: + + NIC_STATUS_SUCCESS if the addresses could be set. + NIC_STATUS_FAILURE if the addresses could not be set. + +--*/ + +NIC_STATUS +tc90x_SetMulticastAddresses( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + UCHAR FlowControlAddress[ETHERNET_ADDRESS_SIZE]; + + DBGPRINT_FUNCTION(("SetMulticastAddresses: IN\n")); +#if LINUX_VERSION_CODE >= 0x20200 + spin_lock(&pAdapter->SpinLock_m); +#endif + // + // Clear all bits in the hash filter, then write all multicast bits back + // + tc90x_InitializeHashFilter(pAdapter); + + if ((pAdapter->Hardware.FlowControlEnable && + pAdapter->Hardware.FlowControlSupported) && + (pAdapter->Hardware.FullDuplexEnable)) { + // + // Set the flow control address bit + // + FlowControlAddress[0] = NIC_FLOW_CONTROL_ADDRESS_0; + FlowControlAddress[1] = NIC_FLOW_CONTROL_ADDRESS_1; + FlowControlAddress[2] = NIC_FLOW_CONTROL_ADDRESS_2; + FlowControlAddress[3] = NIC_FLOW_CONTROL_ADDRESS_3; + FlowControlAddress[4] = NIC_FLOW_CONTROL_ADDRESS_4; + FlowControlAddress[5] = NIC_FLOW_CONTROL_ADDRESS_5; + + NIC_COMMAND( + pAdapter, + (USHORT)(COMMAND_SET_HASH_FILTER_BIT | + 0x0400 | + tc90x_HashAddress(FlowControlAddress)) ); + } + +#if LINUX_VERSION_CODE >= 0x20200 + spin_unlock(&pAdapter->SpinLock_m); +#endif + + DBGPRINT_FUNCTION(("SetMulticastAddresses: OUT\n")); + + return NIC_STATUS_SUCCESS; +} + + +/*++ + +Routine Name: + + InitializeHashFilter + +Routine Description: + + Clear all bits in hash filter and setup the multicast address bit. + +Arguments: + + IN PNIC_INFORMATION Adapter + +Return Value: + + VOID + +--*/ + +VOID +tc90x_InitializeHashFilter( + IN PNIC_INFORMATION Adapter + ) +{ + + PNIC_INFORMATION pAdapter= Adapter; + PDEVICE device = pAdapter->Device; + ULONG count; + ULONG bitsInHashFilter; + ULONG numberMulticast; + PDEV_MC_LIST multicastList; + UCHAR address[ETHERNET_ADDRESS_SIZE]; + ULONG index; + // + // Clear all bits in the hash filter, then write all multicast bits back + // + bitsInHashFilter = pAdapter->Hardware.BitsInHashFilter; + + for (count = 0; count < bitsInHashFilter; count++ ) { + + NIC_COMMAND( + pAdapter, + (USHORT)(COMMAND_SET_HASH_FILTER_BIT | count)); + } + + numberMulticast = device->mc_count; + multicastList = device->mc_list; + + DBGPRINT_INITIALIZE(("mcCount = %x\n", (INT)numberMulticast)); + + for (count=0; count < numberMulticast; count++) { + + DBGPRINT_INITIALIZE(("Multicast = ")); + + for (index=0; index < 6; index++) { + address[index] = multicastList->dmi_addr[index]; + DBGPRINT_INITIALIZE(("%x", address[index])); + + } + DBGPRINT_INITIALIZE(("\n")); + + NIC_COMMAND( + pAdapter, + (USHORT) + (COMMAND_SET_HASH_FILTER_BIT | + 0x400 | + tc90x_HashAddress(address)) ); + + multicastList = multicastList->next; + } +} + + + +/*++ + +Routine Name: + + tc90x_SetupMedia. + +Routine Description: + + This routine checks whether autoselection is specified. If it + does, it calls MainAutoSelectionRoutine else for non-autoselect + case, calls ProgramMII. + +Arguments: + + Device - Pointer to the device structure. + +Return Value: + + NIC_STATUS_SUCCESS if the media could be set up. + +--*/ + +NIC_STATUS +tc90x_SetupMedia( + IN PDEVICE Device + ) + +{ + PNIC_INFORMATION pAdapter = (PNIC_INFORMATION)Device->priv; + USHORT OptionAvailable = 0; + ULONG InternalConfig = 0; + USHORT InternalConfig0 = 0; + USHORT InternalConfig1 = 0; + NIC_STATUS nicStatus; + USHORT MacControl = 0; + CONNECTOR_TYPE NotUsed; + BOOLEAN Handles100Mbit = FALSE; + + DBGPRINT_FUNCTION(("SetupMedia: In\n")); + + pAdapter->Hardware.AutoSelect = FALSE; + pAdapter->Hardware.ConfigConnector = CONNECTOR_UNKNOWN; + pAdapter->Hardware.Connector = CONNECTOR_UNKNOWN; + // + // Assumption made here for Cyclone, Hurricane, and Tornado + // adapters have the same fixed PHY address. For other PHY + // address values, this needs to be changed. + // + pAdapter->Hardware.phys = MII_PHY_ADDRESS; + pAdapter->Hardware.MIIReadCommand = MII_PHY_ADDRESS | 0x6000; + pAdapter->Hardware.MIIWriteCommand = MII_PHY_ADDRESS | 0x5002; + + // If this is a 10mb Lightning card, assume that the 10FL bit is + // set in the media options register + // + if (pAdapter->Hardware.DeviceId == NIC_PCI_DEVICE_ID_900A) { + DBGPRINT_INITIALIZE(( + "SetupMedia: 10BaseFL force Media Option \n")); + OptionAvailable = MEDIA_OPTIONS_10BASEFL_AVAILABLE; + } + else { + // + // Read the MEDIA OPTIONS to see what connectors are available + // + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_3); + OptionAvailable = NIC_READ_PORT_USHORT( + pAdapter, + MEDIA_OPTIONS_REGISTER); + } + // + // Read the internal config through EEPROM since reset + // invalidates the normal register value. + // + nicStatus = tc90x_ReadEEPROM( + pAdapter, + EEPROM_INTERNAL_CONFIG_WORD_0, + (PUSHORT)&InternalConfig0); + + if (nicStatus != NIC_STATUS_SUCCESS) { + DBGPRINT_ERROR(( + "SetupMedia: InternalConfig 0 read failed\n")); + return NIC_STATUS_FAILURE; + } + + nicStatus = tc90x_ReadEEPROM( + pAdapter, + EEPROM_INTERNAL_CONFIG_WORD_1, + (PUSHORT)&InternalConfig1); + + if (nicStatus != NIC_STATUS_SUCCESS) { + DBGPRINT_ERROR(( + "SetupMedia: InternalConfig 1 read failed\n")); + return NIC_STATUS_FAILURE; + } + + InternalConfig = InternalConfig0 | (InternalConfig1 <<16); + + DBGPRINT_INITIALIZE(( + "SetupMedia: InternalConfig %x\n", (INT)InternalConfig)); + + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_3); + // + // Read the current value of the InternalConfig register. If it's different + // from the EEPROM values, than write it out using the EEPROM values. + // This is done since a global reset may invalidate the register value on + // some ASICs. Also, writing to InternalConfig may reset the PHY on some ASICs. + // + if (InternalConfig != NIC_READ_PORT_ULONG( + pAdapter, + INTERNAL_CONFIG_REGISTER)) { + NIC_WRITE_PORT_ULONG( + pAdapter, + INTERNAL_CONFIG_REGISTER, + InternalConfig); + } + // + // Get the connector to use. + // + if (pAdapter->Hardware.ConfigConnector == CONNECTOR_UNKNOWN) { + pAdapter->Hardware.ConfigConnector = + (InternalConfig & INTERNAL_CONFIG_TRANSCEIVER_MASK) >> 20; + pAdapter->Hardware.Connector = + pAdapter->Hardware.ConfigConnector; + if (InternalConfig & INTERNAL_CONFIG_AUTO_SELECT) + pAdapter->Hardware.AutoSelect = TRUE; + ProcessMediaOverrides(pAdapter, OptionAvailable); + } + // + // If auto selection of connector was specified, do it now... + // + if (pAdapter->Hardware.AutoSelect) { + DBGPRINT_INITIALIZE(("SetupMedia: Autoselect set\n")); + NIC_COMMAND(pAdapter, COMMAND_STATISTICS_DISABLE); + tc90x_MainAutoSelectionRoutine(pAdapter, OptionAvailable); + } + else { + // + // MII connector needs to be initialized and the data rates + // set up even in the non-autoselect case + // + DBGPRINT_INITIALIZE(("SetupMedia: Adapter in forced-mode\n")); + + if ((pAdapter->Hardware.Connector == CONNECTOR_MII) || + (pAdapter->Hardware.Connector == + CONNECTOR_AUTONEGOTIATION)) { + ProgramMII(pAdapter, CONNECTOR_MII); + } + else { + if ((pAdapter->Hardware.Connector == + CONNECTOR_100BASEFX) || + (pAdapter->Hardware.Connector == + CONNECTOR_100BASETX)) { + pAdapter->Hardware.LinkSpeed = LINK_SPEED_100; + } + else { + pAdapter->Hardware.LinkSpeed = LINK_SPEED_10; + } + } + tc90x_SetupConnector( + pAdapter, + pAdapter->Hardware.Connector, + &NotUsed); + } + // + // Check link speed and duplex settings before doing anything else. + // If the call succeeds, we know the link is up, so we'll update the + // link state. + // + if (GetLinkSpeed(pAdapter, &Handles100Mbit)) { + pAdapter->Hardware.LinkSpeed = + (Handles100Mbit) ? LINK_SPEED_100 : LINK_SPEED_10; + pAdapter->Hardware.LinkState = LINK_UP; + } + else + pAdapter->Hardware.LinkState = LINK_DOWN_AT_INIT; + // + // Set up duplex mode + // + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_3); + + MacControl = NIC_READ_PORT_USHORT(pAdapter, MAC_CONTROL_REGISTER); + + if (pAdapter->Hardware.FullDuplexEnable) { + // + // Set Full duplex in MacControl register + // + MacControl |= MAC_CONTROL_FULL_DUPLEX_ENABLE; + DBGPRINT_INITIALIZE(("Changed link to full duplex\n")); + // + // Since we're switching to full duplex, enable flow control. + // + if (pAdapter->Hardware.FlowControlSupported) { + DBGPRINT_INITIALIZE(("SetupMedia: flow Control support is on!\n")); + MacControl |= MAC_CONTROL_FLOW_CONTROL_ENABLE; + pAdapter->Hardware.FlowControlEnable = TRUE; + tc90x_SetMulticastAddresses(pAdapter); + } + } + else { + // + // Set Half duplex in MacControl register + // + MacControl &= ~MAC_CONTROL_FULL_DUPLEX_ENABLE; + DBGPRINT_INITIALIZE(("Changed link to half duplex\n")); + // + // Since we're switching to half duplex, disable flow control + // + if (pAdapter->Hardware.FlowControlSupported) { + MacControl &= ~ MAC_CONTROL_FLOW_CONTROL_ENABLE; + pAdapter->Hardware.FlowControlEnable = FALSE; + tc90x_SetMulticastAddresses(pAdapter); + } + } + + NIC_WRITE_PORT_USHORT(pAdapter,MAC_CONTROL_REGISTER,MacControl); + + if (tc90x_ResetAndEnableTransmitter(pAdapter) != NIC_STATUS_SUCCESS) { + DBGPRINT_INITIALIZE(( + "SetupMedia: Reset transmitter failed\n")); + DBGPRINT_FUNCTION(("SetupMedia: Out with error\n")); + return NIC_STATUS_FAILURE; + } + + if (tc90x_ResetAndEnableReceiver(pAdapter) != NIC_STATUS_SUCCESS) { + DBGPRINT_INITIALIZE(( + "SetupMedia: Reset receiver failed\n")); + DBGPRINT_FUNCTION(( + "SetupMedia: Out with error\n")); + return NIC_STATUS_FAILURE; + } + // + // This is for advertisement of flow control. We only need to + // call this if the adapter is using flow control, in Autoselect + // mode and not a Tornado board. + // + if ((pAdapter->Hardware.AutoSelect && + pAdapter->Hardware.FlowControlEnable) && + (pAdapter->Hardware.DeviceId != NIC_PCI_DEVICE_ID_9200) && + (pAdapter->Hardware.DeviceId != NIC_PCI_DEVICE_ID_9805)) { + tc90x_FlowControl(pAdapter); + } + DBGPRINT_FUNCTION(("SetupMedia: Out\n")); + return NIC_STATUS_SUCCESS; +} + +/*++ + +Routine Name: + + ProcessMediaOverrides + +Routine Description: + + Change the connector and duplex if values are present in command line. + +Arguments: + + IN PNIC_INFORMATION Adapter + IN USHORT OptionAvailable + +Return Value: + + VOID + +--*/ + +VOID +ProcessMediaOverrides( + IN PNIC_INFORMATION Adapter, + IN USHORT OptionAvailable + ) +{ + PNIC_INFORMATION pAdapter= Adapter; + ULONG InternalConfig = 0; + ULONG OldInternalConfig = 0; + + NIC_COMMAND(pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | + REGISTER_WINDOW_3); + + InternalConfig = NIC_READ_PORT_ULONG( + pAdapter, + INTERNAL_CONFIG_REGISTER); + + OldInternalConfig = InternalConfig; + + if (pAdapter->Hardware.MediaOverride == MEDIA_AUTO_SELECT) + InternalConfig |= INTERNAL_CONFIG_AUTO_SELECT; + else + InternalConfig &= ~INTERNAL_CONFIG_AUTO_SELECT; + // + // Write to the InternalConfig register only if it's changed. + // + if (OldInternalConfig != InternalConfig) { + NIC_WRITE_PORT_ULONG(pAdapter, + INTERNAL_CONFIG_REGISTER, + InternalConfig); + } + + switch (pAdapter->Hardware.MediaOverride) { + + case MEDIA_AUTO_SELECT: + pAdapter->Hardware.AutoSelect = TRUE; + break; + + case MEDIA_10BASE_T: + if (OptionAvailable & MEDIA_OPTIONS_10BASET_AVAILABLE) { + pAdapter->Hardware.Connector = CONNECTOR_10BASET; + pAdapter->Hardware.AutoSelect = FALSE; + } + break; + + case MEDIA_10AUI: + if (OptionAvailable & MEDIA_OPTIONS_10AUI_AVAILABLE) { + pAdapter->Hardware.Connector = CONNECTOR_10AUI; + pAdapter->Hardware.AutoSelect = FALSE; + } + break; + + case MEDIA_10BASE_2: + if (OptionAvailable & MEDIA_OPTIONS_10BASE2_AVAILABLE) { + pAdapter->Hardware.Connector = CONNECTOR_10BASE2; + pAdapter->Hardware.AutoSelect = FALSE; + } + break; + + case MEDIA_100BASE_TX: + if (OptionAvailable & + MEDIA_OPTIONS_100BASETX_AVAILABLE) { + pAdapter->Hardware.Connector = CONNECTOR_100BASETX; + pAdapter->Hardware.AutoSelect = FALSE; + } + break; + + case MEDIA_100BASE_FX: + if (OptionAvailable & + MEDIA_OPTIONS_100BASEFX_AVAILABLE) { + pAdapter->Hardware.Connector = CONNECTOR_100BASEFX; + pAdapter->Hardware.AutoSelect = FALSE; + } + break; + + case MEDIA_10BASE_FL: + if (OptionAvailable & + MEDIA_OPTIONS_10BASEFL_AVAILABLE) { + pAdapter->Hardware.Connector = CONNECTOR_10AUI; + pAdapter->Hardware.AutoSelect = FALSE; + + } + break; + + case MEDIA_NONE: + break; + } +} + +/*++ + +Routine Name: + + TickMediaHandler + +Routine Description: + + Adjust linkspeed and duplex in tick handler + +Arguments: + + IN PNIC_INFORMATION Adapter + +Return Value: + + VOID + +--*/ + +VOID +tc90x_TickMediaHandler( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + + if ((pAdapter->Hardware.Connector == CONNECTOR_AUTONEGOTIATION) || + (pAdapter->Hardware.Connector == CONNECTOR_10BASET) || + (pAdapter->Hardware.Connector == CONNECTOR_100BASETX)) { + // + // TP case + // + CheckTPLinkState(pAdapter); + } + else if (pAdapter->Hardware.Connector == CONNECTOR_100BASEFX) { + // + // FX case + // + CheckFXLinkState(pAdapter); + } +} + + +/*++ + +Routine Name: + + CheckTPLinkState + +Routine Description: + + Determine whether to notify the operating system if link is lost + or restored. For autonegotiation case, made adjustments to duplex + and speed if necessary. + +Arguments: + + IN PNIC_INFORMATION Adapter + +Return Value: + + VOID + +--*/ + +VOID +CheckTPLinkState( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + BOOLEAN handles100Mbit = FALSE; + USHORT PhyStatus = 0; + BOOLEAN OldFullDuplex; + ULONG OldLinkSpeed; + + if (ReadMIIPhy(pAdapter, MII_PHY_STATUS, &PhyStatus)) { + + if (!(PhyStatus & MII_STATUS_LINK_UP)) { + // + // If OS doesn't know link was lost, go + // ahead and notify + // + if (pAdapter->Hardware.LinkState == LINK_UP) + IndicateToOSLinkStateChange(pAdapter); + } + else { + // + // If OS doesn't know link was restored, go + // ahead and notify + // + if (pAdapter->Hardware.LinkState != LINK_UP) + IndicateToOSLinkStateChange(pAdapter); + + if (((PhyStatus & MII_STATUS_AUTO) && + (PhyStatus & MII_STATUS_EXTENDED)) && + (pAdapter->Hardware.Connector == CONNECTOR_AUTONEGOTIATION)) { + + OldLinkSpeed = pAdapter->Hardware.LinkSpeed; + OldFullDuplex = pAdapter->Hardware.FullDuplexEnable; + // + // Capable of autonegotiation + // + if (GetLinkSpeed(pAdapter, &handles100Mbit)) { + if (handles100Mbit) + pAdapter->Hardware.LinkSpeed = LINK_SPEED_100; + else + pAdapter->Hardware.LinkSpeed = LINK_SPEED_10; + // + // Set up for new speed if speed changed. Set + // counterSpeed bit in DmaCtrl register. + // + if (pAdapter->Hardware.LinkSpeed != OldLinkSpeed) + tc90x_SetupNewSpeed(pAdapter); + // + // Set up for new duplex mode if duplex changed. + // + if (pAdapter->Hardware.FullDuplexEnable != OldFullDuplex) + tc90x_SetupNewDuplex(pAdapter); + } + else + DBGPRINT_INITIALIZE(("Failed to get link speed\n")); + } + } + } +} + +/*++ + +Routine Name: + + CheckFXLinkState + +Routine Description: + +Arguments: + + IN PNIC_INFORMATION Adapter + +Return Value: + + VOID + +--*/ + +VOID +CheckFXLinkState( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + USHORT MediaStatus = 0; + + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + + MediaStatus = NIC_READ_PORT_USHORT(pAdapter,MEDIA_STATUS_REGISTER); + + if (!(MediaStatus & MEDIA_STATUS_LINK_DETECT)) { + if (pAdapter->Hardware.LinkState == LINK_UP) + IndicateToOSLinkStateChange(pAdapter); + } + else { + if (pAdapter->Hardware.LinkState != LINK_UP) + IndicateToOSLinkStateChange(pAdapter); + } +} + +/*++ + +Routine Name: + + tc90x_SetupNewDuplex + +Routine Description: + + Setup new duplex in MacControl register. + +Arguments: + + IN PNIC_INFORMATION Adapter + + +Return Value: + + NIC_STATUS + +--*/ +NIC_STATUS +tc90x_SetupNewDuplex( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + USHORT MacControl = 0; + + DBGPRINT_INITIALIZE(("SetupNewDuplex: IN\n")); + + NIC_COMMAND(pAdapter, COMMAND_RX_DISABLE); + NIC_COMMAND(pAdapter, COMMAND_TX_DISABLE); + + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + // + // Wait for transmit to go quiet + // + MediaStatus_g = 0; + MediaStatus_g = NIC_READ_PORT_USHORT(pAdapter,MEDIA_STATUS_REGISTER); + NIC_DELAY(10); + + if(MediaStatus_g & MEDIA_STATUS_TX_IN_PROGRESS) + { pAdapter->WaitCases = CHECK_TRANSMIT_IN_PROGRESS; + TimeOutCount = jiffies + HZ; + if(!InWaitTimer) + { WaitTimer.expires = RUN_AT(HZ/100); //max = 1s + add_timer(&WaitTimer); + InWaitTimer = TRUE; + } + while(TimeOutCount > jiffies); + if(!(MediaStatus_g & MEDIA_STATUS_TX_IN_PROGRESS)) + { DBGPRINT_ERROR(( + "tc90x_SetupNewDuplex: Packet not picked up by hardware")); + return NIC_STATUS_FAILURE; + } + } + // + // Wait for receive to go quiet + // + MediaStatus_g = NIC_READ_PORT_USHORT(pAdapter,MEDIA_STATUS_REGISTER); + + NIC_DELAY(10); + + if(MediaStatus_g & MEDIA_STATUS_CARRIER_SENSE) + { pAdapter->WaitCases = CHECK_CARRIER_SENSE; + TimeOutCount = jiffies + HZ; + if(!InWaitTimer) + { WaitTimer.expires = RUN_AT(HZ/100); //max = 1s + add_timer(&WaitTimer); + InWaitTimer = TRUE; + } + while(TimeOutCount > jiffies) ; + if(!(MediaStatus_g & MEDIA_STATUS_CARRIER_SENSE)) + { pAdapter->Hardware.Status = HARDWARE_STATUS_HUNG; + DBGPRINT_ERROR(("tc90x_SetupNewDuplex: Packet not uploaded by hardware")); + return NIC_STATUS_FAILURE; + } + } + + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_3); + + MacControl = NIC_READ_PORT_USHORT(pAdapter, MAC_CONTROL_REGISTER); + + if (pAdapter->Hardware.FullDuplexEnable) { + // + // Set Full duplex in MacControl register + // + MacControl |= MAC_CONTROL_FULL_DUPLEX_ENABLE; + DBGPRINT_INITIALIZE(("Changed link to full duplex\n")); + // + // Since we're switching to full duplex, enable flow control. + // + if (pAdapter->Hardware.FlowControlSupported) { + + MacControl |= MAC_CONTROL_FLOW_CONTROL_ENABLE; + pAdapter->Hardware.FlowControlEnable = TRUE; + + tc90x_SetMulticastAddresses(pAdapter); + } + } + else { + // + // Set Half duplex in MacControl register + // + MacControl &= ~MAC_CONTROL_FULL_DUPLEX_ENABLE; + DBGPRINT_INITIALIZE(("Changed link to half duplex\n")); + // + // Since we're switching to half duplex, disable flow control + // + if (pAdapter->Hardware.FlowControlEnable && + pAdapter->Hardware.FlowControlSupported) { + + MacControl &= ~ MAC_CONTROL_FLOW_CONTROL_ENABLE; + pAdapter->Hardware.FlowControlEnable = FALSE; + tc90x_SetMulticastAddresses(pAdapter); + } + } + NIC_WRITE_PORT_USHORT(pAdapter, MAC_CONTROL_REGISTER, MacControl); + NIC_DELAY(20); + + /* must do Tx/Rx reset and wait at least 3s */ +// NIC_COMMAND(pAdapter,COMMAND_TX_RESET); +// NIC_COMMAND(pAdapter, COMMAND_RX_RESET); + +// TimeOutCount = jiffies + 3*HZ; +// while(TimeOutCount > jiffies) ; + + NIC_COMMAND(pAdapter, COMMAND_RX_ENABLE); + NIC_COMMAND(pAdapter, COMMAND_TX_ENABLE); + + DBGPRINT_INITIALIZE(("SetupNewDuplex: OUT\n")); + + return NIC_STATUS_SUCCESS; +} + + +/*++ + +Routine Name: + + tc90x_SetupNewSpeed + +Routine Description: + + Sets the counter speed in the DMA control register. Clear bit + for 10Mbps or set the bit for 100Mbps. + +Arguments: + + IN PNIC_INFORMATION Adapter + +Return Value: + + VOID + +--*/ + +VOID +tc90x_SetupNewSpeed( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + ULONG DmaControl = 0; + + DBGPRINT_INITIALIZE(("SetupNewSpeed: IN\n")); + + DmaControl = NIC_READ_PORT_ULONG( + pAdapter, + DMA_CONTROL_REGISTER); + + if (pAdapter->Hardware.LinkSpeed == LINK_SPEED_100) { + + DmaControl |= DMA_CONTROL_COUNTER_SPEED; + } + else { + + DmaControl &= ~DMA_CONTROL_COUNTER_SPEED; + } + + NIC_WRITE_PORT_ULONG( + pAdapter, + DMA_CONTROL_REGISTER, + DmaControl); + + DBGPRINT_INITIALIZE(("Changed link speed to %ld bps\n", + pAdapter->Hardware.LinkSpeed)); + + DBGPRINT_INITIALIZE(("SetupNewSpeed: OUT\n")); +} + + +/*++ + +Routine Name: + + IndicateToOSLinkStateChange + +Routine Description: + + Notify to NDI wrapper the change in link state. + +Arguments: + + IN PNIC_INFORMATION Adapter + +Return Value: + + VOID + +--*/ + +VOID +IndicateToOSLinkStateChange( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + + if (pAdapter->Hardware.LinkState != LINK_DOWN) { + DBGPRINT_INITIALIZE(("Link Lost...\n")); + pAdapter->Hardware.LinkState = LINK_DOWN; + //(pAdapter->Device)->flags &= ~(IFF_UP|IFF_RUNNING); + } + else { + DBGPRINT_INITIALIZE(("Link Regained...\n")); + pAdapter->Hardware.LinkState = LINK_UP; + //(pAdapter->Device)->flags |= (IFF_UP|IFF_RUNNING); + } +} + + + + + + +/*++ + +Routine Name: + + NICInterrupt + +Routine Description: + + This routine handles the interrupt. + +Arguments: + + Irq + DeviceId + Registers + +Return Value: + + None + +--*/ + +VOID +NICInterrupt IRQ( + INT Irq, + PVOID DeviceId, + PTREGS Registers + ) + +{ + // + // Use the now-standard shared IRQ implementation. + // + PDEVICE Device = DeviceId; + PNIC_INFORMATION pAdapter = (PNIC_INFORMATION)Device->priv; + USHORT intStatus = 0; + UCHAR loopCount = 2; + BOOLEAN countDownTimerEventCalled = FALSE; + + +#if defined(__i386__) /* SMP - Becker's fix */ + + /* A lock to prevent simultaneous entry bug on Intel SMP machines. */ + if (test_and_set_bit(0, (void*)&Device->interrupt)) { + DBGPRINT_ERROR(("NICInterrupt: %s simultaneous entry of an interrupt handler.\n", + Device->name)); + /* Avoid halting machine. */ + Device->interrupt = 0; + return ; + } +#else + if (Device->interrupt) { + DBGPRINT_ERROR(("NICInterrupt: %s Re-enter the interrupt handler.\n", Device->name)); + return; + } +#endif /* end fix */ + + Device->interrupt = 1; + +#if LINUX_VERSION_CODE >= 0x20200 + spin_lock(&pAdapter->SpinLock_int); +#endif + + intStatus = NIC_READ_PORT_UCHAR( + pAdapter, + INTSTATUS_COMMAND_REGISTER); + + if (!(intStatus & INTSTATUS_INTERRUPT_LATCH)) + { + + #if LINUX_VERSION_CODE >= 0x20200 + spin_unlock(&pAdapter->SpinLock_int); + #endif + Device->interrupt = 0; + return; + } + else + { + /* Mask all the interrupts */ + NIC_MASK_ALL_INTERRUPT(pAdapter); + NIC_COMMAND( + pAdapter, + COMMAND_ACKNOWLEDGE_INTERRUPT | + ACKNOWLEDGE_INTERRUPT_LATCH); + } + while (loopCount--) + { + /* Read the interrupt status register. */ + intStatus = NIC_READ_PORT_USHORT(pAdapter, + INTSTATUS_COMMAND_REGISTER); + + intStatus &= INTSTATUS_INTERRUPT_MASK; + + if (!intStatus) break; + + if (intStatus & INTSTATUS_HOST_ERROR) + { printk( KERN_CRIT "HostError "); + DBGPRINT_ERROR(( + "NICInterrupt: HostError event happened.\n")); + tc90x_HostErrorEvent(pAdapter); + } + if (intStatus & INTSTATUS_UPDATE_STATISTICS) + { // interrupt is cleared by reading statistics. + tc90x_UpdateStatisticsEvent(pAdapter); + } + + if (intStatus & INTSTATUS_UP_COMPLETE) + { NIC_COMMAND( + pAdapter, + COMMAND_ACKNOWLEDGE_INTERRUPT | + ACKNOWLEDGE_UP_COMPLETE); + + tc90x_UpCompleteEvent(pAdapter); + } + if (intStatus & INTSTATUS_INTERRUPT_REQUESTED) + { NIC_COMMAND( + pAdapter, + COMMAND_ACKNOWLEDGE_INTERRUPT | + ACKNOWLEDGE_INTERRUPT_REQUESTED); + + tc90x_CountDownTimerEvent(pAdapter); + countDownTimerEventCalled = TRUE; + } + + if (intStatus & INTSTATUS_TX_COMPLETE) + { tc90x_TxCompleteEvent(pAdapter); + } + + if ((intStatus & INTSTATUS_RX_COMPLETE) || + (intStatus & INTSTATUS_LINK_EVENT) || + (intStatus & INTSTATUS_DOWN_COMPLETE)) + { DBGPRINT_ERROR(( + "NICInterrupt: Unknown interrupt\n")); + DBGPRINT_ERROR(( + "NICInterrupt: IntStatus =%x\n", intStatus)); + } + + if (FALSE == countDownTimerEventCalled) + { tc90x_CountDownTimerEvent(pAdapter); + countDownTimerEventCalled = TRUE; + } + + } + NIC_UNMASK_ALL_INTERRUPT(pAdapter); + +#if LINUX_VERSION_CODE >= 0x20200 + spin_unlock(&pAdapter->SpinLock_int); +#endif + +// Becker's fix? +#if defined(__i386__) + clear_bit(0, (void*)&Device->interrupt); +#else + Device->interrupt = 0; +#endif + + return; +} + + +/*++ + +Routine Name: + + tc90x_HostErrorEvent. + +Routine Description: + + This routine handles the host error. + +Arguments: + + Device - Pointer to the device structure. + +Return Value: + + None + +--*/ + +VOID +tc90x_HostErrorEvent(IN PNIC_INFORMATION Adapter) +{ + PNIC_INFORMATION pAdapter = Adapter; + TASKQ HostErrTask; + + HostErrTask = pAdapter->Resources.hostErr_task; + + DBGPRINT_INTERRUPT((KERN_CRIT "tc90x_HostErrorEvent: IN \n")); + // + // Read the internal config. + // + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_3); + + pAdapter->keepForGlobalReset = NIC_READ_PORT_ULONG( + pAdapter, + INTERNAL_CONFIG_REGISTER); + + DBGPRINT_INTERRUPT((KERN_CRIT "Adapter does global reset and restart \n")); + // + // Issue Global reset. I will mask the updown reset so that + // I don't have to set the UpPoll, DownPoll, UpListPointer + // and DownListPointer. + // + NIC_COMMAND_WAIT( + pAdapter, + COMMAND_GLOBAL_RESET | + GLOBAL_RESET_MASK_NETWORK_RESET | + GLOBAL_RESET_MASK_TP_AUI_RESET | + GLOBAL_RESET_MASK_ENDEC_RESET | + GLOBAL_RESET_MASK_AISM_RESET | + GLOBAL_RESET_MASK_SMB_RESET | + GLOBAL_RESET_MASK_VCO_RESET | + GLOBAL_RESET_MASK_UP_DOWN_RESET + ); + + // run task in contex of a process so that we can sleep at start + + queue_task(&HostErrTask, &tq_scheduler); + + DBGPRINT_INTERRUPT((KERN_CRIT "tc90x_HostErrorEvent: OUT\n")); + + return; +} + + +/*++ + +Routine Name: + + tc90x_UpdateStatisticsEvent. + +Routine Description: + + This routine handles the update statistics interrupt. + +Arguments: + + Device - Pointer to the device structure. + +Return Value: + + None + +--*/ + +VOID +tc90x_UpdateStatisticsEvent( + IN PNIC_INFORMATION Adapter + ) + +{ + + PNIC_INFORMATION pAdapter = Adapter; + PNIC_STATISTICS statistics = &pAdapter->Statistics; + USHORT rxPackets, txPackets, highPackets; + USHORT rxBytes, txBytes, highBytes; + + DBGPRINT_INTERRUPT((KERN_CRIT "tc90x_UpdateStatisticsEvent: IN\n")); + // + // Change the window. + // + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_6); + + statistics->TxSQEErrors += NIC_READ_PORT_UCHAR( + pAdapter, + SQE_ERRORS_REGISTER); + + statistics->TxMultipleCollisions += NIC_READ_PORT_UCHAR( + pAdapter, + MULTIPLE_COLLISIONS_REGISTER); + + statistics->TxSingleCollisions += NIC_READ_PORT_UCHAR( + pAdapter, + SINGLE_COLLISIONS_REGISTER); + + statistics->RxOverruns += NIC_READ_PORT_UCHAR( + pAdapter, + RX_OVERRUNS_REGISTER); + + statistics->TxCarrierLost += NIC_READ_PORT_UCHAR( + pAdapter, + CARRIER_LOST_REGISTER); + + statistics->TxLateCollisions += NIC_READ_PORT_UCHAR( + pAdapter, + LATE_COLLISIONS_REGISTER); + + statistics->TxFramesDeferred += NIC_READ_PORT_UCHAR( + pAdapter, + FRAMES_DEFERRED_REGISTER); + rxPackets = NIC_READ_PORT_UCHAR( + pAdapter, + FRAMES_RECEIVED_OK_REGISTER); + + txPackets = NIC_READ_PORT_UCHAR( + pAdapter, + FRAMES_TRANSMITTED_OK_REGISTER); + + highPackets = NIC_READ_PORT_UCHAR( + pAdapter, + UPPER_FRAMES_OK_REGISTER); + + rxPackets += ((highPackets & 0x03) << 8); + txPackets += ((highPackets & 0x30) << 4); + + if (pAdapter->Hardware.SQEDisable) + statistics->TxSQEErrors += txPackets; + + statistics->RxFramesOk += rxPackets; + statistics->TxFramesOk += txPackets; + + rxBytes = NIC_READ_PORT_USHORT( + pAdapter, + BYTES_RECEIVED_OK_REGISTER); + + txBytes = NIC_READ_PORT_USHORT( + pAdapter, + BYTES_TRANSMITTED_OK_REGISTER); + + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + + highBytes = NIC_READ_PORT_UCHAR( + pAdapter, + UPPER_BYTES_OK_REGISTER); + + rxBytes += ((highBytes & 0x0F) << 8); + txBytes += ((highBytes & 0xF0) << 4); + + statistics->RxBytesOk += rxBytes; + statistics->TxBytesOk += txBytes; + + statistics->RxBadSSD += NIC_READ_PORT_UCHAR( + pAdapter, + BAD_SSD_REGISTER); + + DBGPRINT_INTERRUPT((KERN_CRIT "tc90x_UpdateStatisticsEvent: OUT\n")); +} + +/*++ + +Routine Name: + + tc90x_CountDownTimerEvent. + +Routine Description: + + This routine handles the interrupt requested event. + +Arguments: + + Device - Pointer to the device structure + +Return Value: + + None + +--*/ + +VOID +tc90x_CountDownTimerEvent( + IN PNIC_INFORMATION Adapter + ) + +{ + PNIC_INFORMATION pAdapter = Adapter; + PDPD_LIST_ENTRY headDPDVirtual; + PDEVICE device = pAdapter->Device; + + DBGPRINT_INTERRUPT((KERN_CRIT "tc90x_CountDownTimerEvent: IN\n")); + headDPDVirtual = pAdapter->HeadDPDVirtual; + // + // This clears the FSH_DPD_EMPTY and a raise condition of + // hardware. + // + pAdapter->TailDPDVirtual->FrameStartHeader = 0; + while (1) + { + if (!(headDPDVirtual->FrameStartHeader & FSH_DOWN_COMPLETE)) + break; + ASSERT(headDPDVirtual->Packet != NULL); + DEV_FREE_SKB(headDPDVirtual->SocketBuffer); + headDPDVirtual->SocketBuffer = NULL; + + /* Clear the down complete bit in the frame start header. */ + headDPDVirtual->FrameStartHeader = 0; + pAdapter->BytesInDPDQueue -= headDPDVirtual->PacketLength; + headDPDVirtual = headDPDVirtual->Next; + ASSERT(pAdapter->HeadDPDVirtual != NULL); + } + pAdapter->HeadDPDVirtual = headDPDVirtual; + if (pAdapter->BytesInDPDQueue) + tc90x_SetCountDownTimer(pAdapter); + + /* If DPD ring is full, run the bottom half */ + if ((device->tbusy) && (pAdapter->DPDRingFull==TRUE)) + { DBGPRINT_SEND(("CountdownTimer: set RingFull false,mark bh\n")); + pAdapter->DPDRingFull = FALSE; + clear_bit(0, (void*)&device->tbusy); + mark_bh(NET_BH); + } + DBGPRINT_INTERRUPT((KERN_CRIT "tc90x_CountDownTimerEvent: OUT\n")); +} + + + + +/*++ + +Routine Name: + + NICTimer + +Routine Description: + + This is the tick handler. + +Arguments: + +Return Value: + + None. + +--*/ + + + + +VOID +NICTimer( + IN ULONG Data + ) +{ + PDEVICE device = (PDEVICE)Data; + PNIC_INFORMATION pAdapter = (PNIC_INFORMATION)device->priv; + pAdapter->InTimer = TRUE; + + if ((pAdapter->Statistics.UpdateInterval += + pAdapter->Resources.TimerInterval) > 1000) { + + pAdapter->Statistics.UpdateInterval = 0; + tc90x_UpdateStatisticsEvent(pAdapter); + } + // + // Check every five seconds for media changed speed or duplex + // + if ((pAdapter->Hardware.UpdateInterval += + pAdapter->Resources.TimerInterval) > 5000) { + + pAdapter->Hardware.UpdateInterval = 0; + tc90x_TickMediaHandler(pAdapter); + } + + pAdapter->Resources.Timer.expires = RUN_AT(HZ/10); + add_timer(&pAdapter->Resources.Timer); + pAdapter->InTimer = FALSE; +} + + +VOID +WaitTimerHandler( + IN ULONG Data + ) +{ + PDEVICE device = (PDEVICE)Data; + PNIC_INFORMATION pAdapter = (PNIC_INFORMATION)device->priv; + INT NextTick = 0; + PDPD_LIST_ENTRY dpdVirtual; + + DBGPRINT_FUNCTION(("In WaitTimer-Time=%x\n",(int)jiffies)); +#if LINUX_VERSION_CODE >= 0x20200 + spin_lock(&pAdapter->SpinLock_misc); +#endif + switch (pAdapter->WaitCases) + { + case CHECK_DOWNLOAD_STATUS: + DBGPRINT_INITIALIZE(("CHECK_DOWNLOAD_STATUS\n")); + dpdVirtual = (PDPD_LIST_ENTRY)pAdapter->TestDPDVirtual[0]; + if(portValue_g == dpdVirtual->DPDPhysicalAddress) + { portValue_g = NIC_READ_PORT_ULONG(pAdapter,DOWN_LIST_POINTER_REGISTER); + NextTick = HZ/10; + } + break; + + case CHECK_UPLOAD_STATUS: + DBGPRINT_INITIALIZE(("CHECK_UPLOAD_STATUS\n")); + if(portValue_g == pAdapter->HeadUPDVirtual->UPDPhysicalAddress) + { portValue_g = NIC_READ_PORT_ULONG(pAdapter,UP_LIST_POINTER_REGISTER); + NextTick = HZ/10; + } + break; + + case CHECK_DC_CONVERTER: + DBGPRINT_INITIALIZE(("CHECK_DC_CONVERTER\n")); + if ( ((DCConverterEnabledState_g) && !(MediaStatus_g & MEDIA_STATUS_DC_CONVERTER_ENABLED) ) || + ((!DCConverterEnabledState_g) && (MediaStatus_g & MEDIA_STATUS_DC_CONVERTER_ENABLED)) ) + { MediaStatus_g = NIC_READ_PORT_USHORT(pAdapter,MEDIA_STATUS_REGISTER); + NextTick = HZ/10; + } + break; + + case CHECK_PHY_STATUS: + DBGPRINT_INITIALIZE(("CHECK_PHY_STATUS\n")); + if(!(PhyStatus_g & MII_STATUS_AUTO_DONE) ) + { PhyResponding_g = ReadMIIPhy(pAdapter, MII_PHY_STATUS, &PhyStatus_g); + NextTick = HZ/10; + } + break; + + case CHECK_TRANSMIT_IN_PROGRESS: + DBGPRINT_INITIALIZE(("CHECK_TRANSMIT_IN_PROGRESS\n")); + if(MediaStatus_g & MEDIA_STATUS_TX_IN_PROGRESS) + { MediaStatus_g = NIC_READ_PORT_USHORT(pAdapter,MEDIA_STATUS_REGISTER); + NextTick = HZ/10; + } + break; + + case CHECK_DOWNLOAD_SELFDIRECTED: + DBGPRINT_INITIALIZE(("CHECK_DOWNLOAD_SELFDIRECTED\n")); + dpdVirtual = (PDPD_LIST_ENTRY)pAdapter->TestDPDVirtual[0]; + if(DownListPointer_g == dpdVirtual->DPDPhysicalAddress) + { DownListPointer_g = NIC_READ_PORT_ULONG(pAdapter,DOWN_LIST_POINTER_REGISTER); + NextTick = HZ/10; + } + break; + + case AUTONEG_TEST_PACKET: + DBGPRINT_INITIALIZE(("AUTONEG_TEST_PACKET\n")); + if(UpListPointer_g == pAdapter->HeadUPDVirtual->UPDPhysicalAddress) + { UpListPointer_g = NIC_READ_PORT_ULONG(pAdapter,UP_LIST_POINTER_REGISTER); + NextTick = HZ/10; + } + break; + + case CHECK_DMA_CONTROL: + DBGPRINT_INITIALIZE(("CHECK_DMA_CONTROL\n")); + if(dmaControl_g & DMA_CONTROL_DOWN_IN_PROGRESS) + { dmaControl_g = NIC_READ_PORT_ULONG(pAdapter,DMA_CONTROL_REGISTER); + NextTick = HZ/10; + } + break; + + case CHECK_CARRIER_SENSE: + DBGPRINT_INITIALIZE(("CHECK_CARRIER_SENSE\n")); + if(MediaStatus_g & MEDIA_STATUS_CARRIER_SENSE) + { MediaStatus_g = NIC_READ_PORT_USHORT(pAdapter,MEDIA_STATUS_REGISTER); + NextTick = HZ/10; + } + break; + + default: + break; + } + if(NextTick) + { WaitTimer.expires = RUN_AT(NextTick); + add_timer(&WaitTimer); + } + else + InWaitTimer = FALSE; +#if LINUX_VERSION_CODE >= 0x20200 + spin_unlock(&pAdapter->SpinLock_misc); +#endif + DBGPRINT_FUNCTION(("Out WaitTimer\n")); +} + + + + + +/*++ + +Routine Name: + + FindMIIPhy + +Routine Description: + + Search for any PHY that is not known. + +Arguments: + + Adapter - Pointer to the adapter structure. + +Return Value: + + BOOLEAN + +--*/ + +BOOLEAN +FindMIIPhy( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = (PNIC_INFORMATION)Adapter; + USHORT MediaOptions = 0; + USHORT PhyManagement = 0; + UCHAR index; + // + // Read the MEDIA OPTIONS to see what connectors are available + // + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_3); + + MediaOptions = NIC_READ_PORT_USHORT(pAdapter,MEDIA_OPTIONS_REGISTER); + + if ((MediaOptions & MEDIA_OPTIONS_MII_AVAILABLE) || + (MediaOptions & MEDIA_OPTIONS_100BASET4_AVAILABLE) ) { + // + // Drop everything, so we are not driving the data, and run the + // clock through 32 cycles in case the PHY is trying to tell us + // something. Then read the data line, since the PHY's pull-up + // will read as a 1 if it's present. + // + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + + NIC_WRITE_PORT_USHORT(pAdapter,PHYSICAL_MANAGEMENT_REGISTER,0); + + for (index = 0; index < 32; index++) { + + NIC_DELAY(1); + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_CLOCK); + + NIC_DELAY(1); + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + 0); + } + + PhyManagement = NIC_READ_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER); + + if ( PhyManagement & PHY_DATA1) { + + return TRUE; + } + else { + return FALSE; + } + } + return TRUE; +} + + + +/*++ + +Routine Name: + + SendMIIPhyPreamble + +Routine Description: + + Establishes the synchronization for each MII transaction. This + is done by sending thirty-two "1" bits. + +Arguments: + + Adapter - Pointer to the adapter structure. + +Return Value: + + VOID + +--*/ + +VOID +SendMIIPhyPreamble( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = (PNIC_INFORMATION)Adapter; + UCHAR index; + // + // Set up and send the preamble, a sequence of 32 "1" bits + // + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_WRITE); + + for (index = 0; index < 32; index++) { + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_WRITE | PHY_DATA1 + ); + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_WRITE | PHY_DATA1 | PHY_CLOCK); + + NIC_DELAY(1); + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_WRITE); + + NIC_DELAY(1); + } +} + + +/*++ + +Routine Name: + + WriteMIIPhy + +Routine Description: + + Writes to a particular MII PHY register given the proper offset. + +Arguments: + + IN PNIC_INFORMATION Adapter + IN USHORT RegAddr + IN USHORT Output + +Return Value: + + VOID + +--*/ + +VOID +WriteMIIPhy( + IN PNIC_INFORMATION Adapter, + IN USHORT RegAddr, + IN USHORT Output + ) +{ + PNIC_INFORMATION pAdapter = (PNIC_INFORMATION)Adapter; + ULONG index,index2; + USHORT writecmd[2]; + + writecmd[0] = pAdapter->Hardware.MIIWriteCommand; + writecmd[1] = 0; + + SendMIIPhyPreamble(pAdapter); + // + // Bits 2..6 of the command word specify the register. + // + writecmd[0] |= (RegAddr & 0x1F) << 2; + writecmd[1] = Output; + + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + + for (index2 = 0; index2 < 2; index2++) { + + for (index = 0x8000; index; index >>= 1) { + + if (writecmd[index2] & index) { + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_WRITE | PHY_DATA1); + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_WRITE | PHY_DATA1 | PHY_CLOCK); + } + else { + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_WRITE); + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_WRITE | PHY_CLOCK); + } + NIC_DELAY(1); + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_WRITE); + + NIC_DELAY(1); + } + } + // + // OK now give it a couple of clocks with nobody driving. + // + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + 0); + + for (index = 0; index < 2; index++) { + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_CLOCK); + + NIC_DELAY(1); + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + 0); + + NIC_DELAY(1); + } +} + + +/*++ + +Routine Name: + + ReadMIIPhy + +Routine Description: + + Reads a particular MII PHY register given the proper offset. + +Arguments: + + IN PNIC_INFORMATION Adapter + IN USHORT RegisterAddress + OUT PUSHORT pInput + +Return Value: + BOOLEAN + +--*/ + +BOOLEAN +ReadMIIPhy( + IN PNIC_INFORMATION Adapter, + IN USHORT RegisterAddress, + OUT PUSHORT pInput + ) +{ + PNIC_INFORMATION pAdapter = (PNIC_INFORMATION)Adapter; + USHORT PhyManagement = 0; + USHORT ReadCommand; + ULONG index; + + ReadCommand = pAdapter->Hardware.MIIReadCommand; + + SendMIIPhyPreamble(pAdapter); + // + // Bits 2..6 of the command word specify the register. + // + ReadCommand |= (RegisterAddress & 0x1F) << 2; + + for (index = 0x8000; index > 2; index >>= 1) { + + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + + if (ReadCommand & index) { + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_WRITE | PHY_DATA1); + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_WRITE | PHY_DATA1 | PHY_CLOCK); + } + else { + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_WRITE); + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_WRITE | PHY_CLOCK); + } + NIC_DELAY(1); + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_WRITE); + + NIC_DELAY(1); + } + // + // Now run one clock with nobody driving. + // + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + 0); + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_CLOCK); + + NIC_DELAY(1); + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + 0); + + NIC_DELAY(1); + // + // Now run one clock, expecting the PHY to be driving a 0 on the data + // line. If we read a 1, it has to be just his pull-up, and he's not + // responding. + // + PhyManagement = NIC_READ_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER); + + if (PhyManagement & PHY_DATA1) { + return FALSE; + } + // + // We think we are in sync. Now we read 16 bits of data from the PHY. + // + for (index = 0x8000; index; index >>= 1) { + // + // Shift input up one to make room + // + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_CLOCK); + + NIC_DELAY(1); + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + 0); + + NIC_DELAY(1); + + PhyManagement = NIC_READ_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER); + + if (PhyManagement & PHY_DATA1) + *pInput |= index; + else + *pInput &= ~index; + } + // + // OK now give it a couple of clocks with nobody driving. + // + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + 0); + + for (index = 0; index < 2; index++) { + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + PHY_CLOCK); + + NIC_DELAY(1); + + NIC_WRITE_PORT_USHORT( + pAdapter, + PHYSICAL_MANAGEMENT_REGISTER, + 0); + + NIC_DELAY(1); + } + return TRUE; +} + + +/*++ + +Routine Name: + + MIIMediaOverride + +Routine Description: + + MII values need to be updated based on what was set in the + registry. + +Arguments: + + IN PNIC_INFORMATION Adapter + IN USHORT PhyModes + OUT PUSHORT MiiType + +Return Value: + BOOLEAN + +--*/ + +BOOLEAN +MIIMediaOverride( + IN PNIC_INFORMATION Adapter, + IN USHORT PhyModes, + OUT PUSHORT MiiType + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + + switch(pAdapter->Hardware.MediaOverride) { + + case MEDIA_10BASE_T: + + if (( PhyModes & MII_STATUS_10TFD ) && + ( pAdapter->Hardware.FullDuplexEnable == TRUE )) + *MiiType = MIISELECT_10BT; + + else if ( PhyModes & MII_STATUS_10T ) + *MiiType = MIISELECT_10BT; + + else + return FALSE; + + break; + + case MEDIA_100BASE_TX: + + if (( PhyModes & MII_STATUS_100TXFD ) && + ( pAdapter->Hardware.FullDuplexEnable == TRUE )) + *MiiType = MIISELECT_100BTX; + + else if ( PhyModes & MII_STATUS_100TX ) + *MiiType = MIISELECT_100BTX; + + else + return FALSE; + + break; + } + + return TRUE; +} + + +/*++ + +Routine Name: + + ProgramMII + +Routine Description: + + Setup the necessary MII registers with values either + read from the EEPROM or from command line + +Arguments: + + IN PNIC_INFORMATION Adapter + IN CONNECTOR_TYPE NewConnector + +Return Value: + BOOLEAN + +--*/ + +BOOLEAN +ProgramMII( + IN PNIC_INFORMATION Adapter, + IN CONNECTOR_TYPE NewConnector + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + BOOLEAN PhyResponding; + USHORT PhyControl; + USHORT PhyStatus; + NIC_STATUS status; + USHORT MiiType=0, PhyModes; + // + // First see if there's anything connected to the MII + // + if (!FindMIIPhy(pAdapter)) + return FALSE; + // Nowhere is it written that the register must be latched, and since + // reset is the last bit out, the contents might not be valid. read + // it one more time. + // + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_CONTROL, &PhyControl); + // + // Now we can read the status and try to figure out what's out there. + // + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_STATUS, &PhyStatus); + if (!PhyResponding) + return FALSE; + // + // Reads the miiSelect field in EEPROM. Program MII as the default. + // + status = tc90x_ReadEEPROM( + pAdapter, + EEPROM_SOFTWARE_INFORMATION_3, + &MiiType); + // + // If an override is present AND the transceiver type is available + // on the card, that type will be used. + // + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_STATUS, &PhyModes); + if (!PhyResponding) + return FALSE; + + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_CONTROL, &PhyControl); + if (!PhyResponding) + return FALSE; + + if (!MIIMediaOverride(pAdapter, PhyModes, &MiiType)) + return FALSE; + // + // If full duplex selected, set it in PhyControl. + // + if (pAdapter->Hardware.FullDuplexEnable) + PhyControl |= MII_CONTROL_FULL_DUPLEX; + else + PhyControl &= ~MII_CONTROL_FULL_DUPLEX; + + PhyControl &= ~MII_CONTROL_ENABLE_AUTO; + + if (((MiiType & MIITXTYPE_MASK) == MIISELECT_100BTX) || + ((MiiType & MIITXTYPE_MASK) == MIISELECT_100BTX_ANE)) { + PhyControl |= MII_CONTROL_100MB; + WriteMIIPhy(pAdapter, MII_PHY_CONTROL, PhyControl); + //delay 600 milliseconds + TimeOutCount = jiffies + 6*HZ/10; + while(TimeOutCount > jiffies) ; + pAdapter->Hardware.LinkSpeed = 100000000L; + DBGPRINT_INITIALIZE(("ProgramMII() Set to 100M\n")); + return TRUE; + } + else if (((MiiType & MIITXTYPE_MASK ) == MIISELECT_10BT) || + ((MiiType & MIITXTYPE_MASK ) == MIISELECT_10BT_ANE)) { + PhyControl &= ~MII_CONTROL_100MB; + WriteMIIPhy(pAdapter, MII_PHY_CONTROL, PhyControl); + //delay 600 milliseconds + TimeOutCount = jiffies + 6*HZ/10; + while(TimeOutCount > jiffies) ; + pAdapter->Hardware.LinkSpeed = 10000000L; + DBGPRINT_INITIALIZE(("ProgramMII() Set to 10M\n")); + return TRUE; + } + + PhyControl &= ~MII_CONTROL_100MB; + WriteMIIPhy(pAdapter, MII_PHY_CONTROL, PhyControl); + //delay 600 milliseconds + TimeOutCount = jiffies + 6*HZ/10; + while(TimeOutCount > jiffies) ; + + pAdapter->Hardware.LinkSpeed = 10000000L; + DBGPRINT_INITIALIZE(("ProgramMII() Defaults to 10M\n")); + return FALSE; +} + +/*++ + +Routine Name: + + CheckMIIConfiguration + +Routine Description: + + Sets up the tranceiver through MII registers. This will first check on the current connection + state as shown by the MII registers. If the current state matches what the media options support, + then the link is kept. If not, the registers will be configured in the proper manner and auto-negotiation + will be restarted. + + Since this function is called for forced xcvr configurations, it assumes that the xcvr type has + been verified as supported by the NIC. + +Arguments: + + IN pAdapter pAdapter - Pointer to the adapter structure + IN USHORT MediaOptions - Value of MediaOptions register passed by caller. + +Return Value: + BOOLEAN + +--*/ + +BOOLEAN +CheckMIIConfiguration( + IN PNIC_INFORMATION Adapter, + IN USHORT MediaOptions + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + BOOLEAN PhyResponding; + USHORT PhyControl; + USHORT PhyStatus; + USHORT PhyAnar; + USHORT tempAnar; + // + // Check to see if auto-negotiation has completed. Check the results + // in the control and status registers. + // + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_CONTROL, &PhyControl); + if (!PhyResponding) { + DBGPRINT_ERROR(("CheckMIIConfiguration: Phy not responding (1) \n")); + pAdapter->Hardware.Status = HARDWARE_STATUS_FAILURE; + return FALSE; + } + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_STATUS, &PhyStatus); + if (!PhyResponding) { + DBGPRINT_ERROR(("CheckMIIConfiguration: Phy not responding (2) \n")); + pAdapter->Hardware.Status = HARDWARE_STATUS_FAILURE; + return FALSE; + } + if (!((PhyControl & MII_CONTROL_ENABLE_AUTO) && (PhyStatus & MII_STATUS_AUTO_DONE))) { + // + // Auto-negotiation did not complete, so start it over using the new settings. + // + if (!ConfigureMII(pAdapter,MediaOptions)) + return FALSE; + } + // + // Auto-negotiation has completed. Check the results against the ANAR and ANLPAR + // registers to see if we need to restart auto-neg. + // + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_ANAR, &PhyAnar); + if (!PhyResponding) { + DBGPRINT_ERROR(("CheckMIIConfiguration: Phy not responding (3) \n")); + pAdapter->Hardware.Status = HARDWARE_STATUS_FAILURE; + return FALSE; + } + // + // Check to see what we negotiated with the link partner. First, let's make + // sure that the ANAR is set properly based on the media options defined. + // + tempAnar = 0; + if (MediaOptions & MEDIA_OPTIONS_100BASETX_AVAILABLE) { + if (pAdapter->Hardware.AutoSelect) { + tempAnar |= MII_ANAR_100TXFD | MII_ANAR_100TX; + } + else { + if (pAdapter->Hardware.FullDuplexEnable) + tempAnar |= MII_ANAR_100TXFD; + else + tempAnar |= MII_ANAR_100TX; + } + } + if (MediaOptions & MEDIA_OPTIONS_10BASET_AVAILABLE) { + if (pAdapter->Hardware.AutoSelect) + tempAnar |= MII_ANAR_10TFD | MII_ANAR_10T; + else { + if (pAdapter->Hardware.FullDuplexEnable) + tempAnar |= MII_ANAR_10TFD; + else + tempAnar |= MII_ANAR_10T; + } + } + if ( pAdapter->Hardware.FullDuplexEnable && + pAdapter->Hardware.FlowControlSupported ) + tempAnar |= MII_ANAR_FLOWCONTROL; + + if ((PhyAnar & MII_ANAR_MEDIA_MASK) == tempAnar) { + // + // The negotiated configuration hasn't changed. + // So, return and don't restart auto-negotiation. + // + return TRUE; + } + // + // Check the media settings. + // + if (MediaOptions & MEDIA_OPTIONS_100BASETX_AVAILABLE) { + // + // Check 100BaseTX settings. + // + if ((PhyAnar & MII_ANAR_MEDIA_100_MASK) != (tempAnar & MII_ANAR_MEDIA_100_MASK)) { + DBGPRINT_INITIALIZE(("CheckMIIConfiguration: Re-Initiating autonegotiation...\n")); + return ConfigureMII(pAdapter,MediaOptions); + } + } + if (MediaOptions & MEDIA_OPTIONS_10BASET_AVAILABLE) { + // + // Check 10BaseT settings. + // + if ((PhyAnar & MII_ANAR_MEDIA_10_MASK) != (tempAnar & MII_ANAR_MEDIA_10_MASK)) { + DBGPRINT_INITIALIZE(("CheckMIIConfiguration: Re-Initiating autonegotiation...\n")); + return ConfigureMII(pAdapter,MediaOptions); + } + } + return TRUE; +} + +/*++ + +Routine Name: ConfigureMII + +Routine Description: + + Used to setup the configuration for 10Base-T and 100Base-TX. + +Arguments: + + IN pAdapter - Pointer to the adapter structure + IN MediaOptions - Media options that will define how to set up MII registers. + +Return Value: + BOOLEAN + +--*/ + +BOOLEAN +ConfigureMII( + IN PNIC_INFORMATION Adapter, + IN USHORT MediaOptions + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + BOOLEAN PhyResponding; + USHORT PhyControl; + USHORT PhyAnar; + ULONG TimeOutCount; + + DBGPRINT_INITIALIZE(("ConfigureMII: IN \n")); + // + // Nowhere is it written that the register must be latched, and since + // reset is the last bit out, the contents might not be valid. read + // it one more time. + // + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_CONTROL, &PhyControl); + if (!PhyResponding) { + DBGPRINT_ERROR(("ConfigureMII: Phy not responding (1) \n")); + pAdapter->Hardware.Status = HARDWARE_STATUS_FAILURE; + return FALSE; + } + // + // Also, read the ANAR register and clear out it's current settings. + // + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_ANAR, &PhyAnar); + if (!PhyResponding) { + DBGPRINT_ERROR(("ConfigureMII: Phy not responding (2) \n")); + pAdapter->Hardware.Status = HARDWARE_STATUS_FAILURE; + return FALSE; + } + // + // Set up speed and duplex settings in MII Control and ANAR register. + // + PhyAnar &= ~( + MII_ANAR_100TXFD| + MII_ANAR_100TX | + MII_ANAR_10TFD | + MII_ANAR_10T); + // + // Set up duplex. + // + if (pAdapter->Hardware.FullDuplexEnable) + PhyControl |= MII_CONTROL_FULL_DUPLEX; + else + PhyControl &= ~(MII_CONTROL_FULL_DUPLEX); + // + // Set up flow control. + // NOTE: On some NICs, such as Tornado, this will be hardwired to be set. + // Clearing it will have no effect. + // + if (pAdapter->Hardware.FlowControlSupported) + PhyAnar |= MII_ANAR_FLOWCONTROL; + else + PhyAnar &= ~(MII_ANAR_FLOWCONTROL); + // + // Set up the media options. For duplex settings, if we're set to auto-select + // then enable both half and full-duplex settings. Otherwise, go by what's + // been enabled for duplex mode. + // + if (MediaOptions & MEDIA_OPTIONS_100BASETX_AVAILABLE){ + if (pAdapter->Hardware.AutoSelect) + PhyAnar |= (MII_ANAR_100TXFD | MII_ANAR_100TX); + else { + if (pAdapter->Hardware.FullDuplexEnable) + PhyAnar |= MII_ANAR_100TXFD; + else + PhyAnar |= MII_ANAR_100TX; + } + } + if (MediaOptions & MEDIA_OPTIONS_10BASET_AVAILABLE){ + if (pAdapter->Hardware.AutoSelect) + PhyAnar |= (MII_ANAR_10TFD | MII_ANAR_10T); + else { + if (pAdapter->Hardware.FullDuplexEnable) + PhyAnar |= MII_ANAR_10TFD; + else + PhyAnar |= MII_ANAR_10T; + } + } + // + // Enable and start auto-negotiation + // + PhyControl |= (MII_CONTROL_ENABLE_AUTO | MII_CONTROL_START_AUTO); + // + // Write the MII registers back. + // + WriteMIIPhy(pAdapter, MII_PHY_ANAR, PhyAnar); + WriteMIIPhy(pAdapter, MII_PHY_CONTROL, PhyControl); + // + // Wait for auto-negotiation to finish. + // + PhyStatus_g = 0; + PhyResponding_g = ReadMIIPhy(pAdapter, + MII_PHY_STATUS, + &PhyStatus_g); + NIC_DELAY(1000); + if (!PhyResponding_g) { + DBGPRINT_ERROR(("ConfigureMII: Phy not responding (3) \n")); + pAdapter->Hardware.Status = HARDWARE_STATUS_FAILURE; + return FALSE; + } + if (!(PhyStatus_g & MII_STATUS_AUTO_DONE) ) + { TimeOutCount = jiffies + (3*HZ); // run max = 3s + pAdapter->WaitCases = CHECK_PHY_STATUS; + if(!InWaitTimer) + { WaitTimer.expires = RUN_AT(HZ/10); + add_timer(&WaitTimer); + InWaitTimer = TRUE; + } + while(TimeOutCount > jiffies) ; + pAdapter->WaitCases = NONE; + if(!(PhyStatus_g & MII_STATUS_AUTO_DONE)) + { DBGPRINT_ERROR(("ConfigureMII: Autonegotiation not done \n")); + pAdapter->Hardware.LinkState = LINK_DOWN_AT_INIT; + return FALSE; + } + } + pAdapter->Hardware.LinkState = LINK_UP; + return TRUE; +} + +/*++ + +Routine Name: + + CheckMIIAutoNegotiationStatus + +Routine Description: + + Since this function is called for forced xcvr configurations, it assumes that the xcvr type has + been verified as supported by the NIC. + +Arguments: + + IN pAdapter pAdapter - Pointer to the adapter structure + +Return Value: + None + +--*/ + +VOID +CheckMIIAutoNegotiationStatus( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + BOOLEAN PhyResponding; + USHORT PhyStatus; + USHORT PhyAnar; + USHORT PhyAnlpar; + // + // Check to see if auto-negotiation has completed. Check the results in the status + // registers. + // + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_STATUS, &PhyStatus); + if (!PhyResponding) { + DBGPRINT_ERROR(("CheckMIIAutoNegotiationStatus: Phy not responding (1) \n")); + pAdapter->Hardware.Status = HARDWARE_STATUS_FAILURE; + return; + } + if (PhyStatus & MII_STATUS_LINK_UP) + return; // We have a valid link, so get out! + // + // Check to see why auto-negotiation or parallel detection has failed. We'll do this + // by comparing the advertisement registers between the NIC and the link partner. + // + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_ANAR, &PhyAnar); + if (!PhyResponding) { + DBGPRINT_ERROR(("CheckMIIAutoNegotiationStatus: Phy not responding (2) \n")); + pAdapter->Hardware.Status = HARDWARE_STATUS_FAILURE; + return; + } + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_ANLPAR, &PhyAnlpar); + if (!PhyResponding) { + DBGPRINT_ERROR(("CheckMIIAutoNegotiationStatus: Phy not responding (3) \n")); + pAdapter->Hardware.Status = HARDWARE_STATUS_FAILURE; + return; + } + // + // Now, compare what was advertised between the NIC and it's link partner. + // If the media bits don't match, then write an error log entry. + // + if ((PhyAnar & MII_ANAR_MEDIA_MASK) != (PhyAnlpar & MII_ANAR_MEDIA_MASK)) + DBGPRINT_ERROR(("CheckMIIAutoNegotiationStatus: Incompatible configuration \n")); +} + + + +/*++ + +Routine Name : + + tc90x_CheckIfEEPROMBusy + +Routine Description: + + This routine checks if the EEPROM is busy + +Arguments: + + PDEVICE - Pointer to the device structure + +Return Value: + + NIC_STATUS_SUCCESS + NIC_STATUS_FAILURE + +--*/ + +NIC_STATUS +tc90x_CheckIfEEPROMBusy( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + USHORT command = 0; + ULONG count; + + count = jiffies + HZ; + do { + command = NIC_READ_PORT_USHORT( + pAdapter, + EEPROM_COMMAND_REGISTER); + NIC_DELAY(10); + } while ( (command & EEPROM_BUSY_BIT) && (count > jiffies) ); + + if (count < jiffies) { + DBGPRINT_ERROR(("tc90x_CheckIfEEPROMBusy: command timeout")); + return NIC_STATUS_FAILURE; + } + return NIC_STATUS_SUCCESS; +} + +/*++ + +Routine Name: + + tc90x_ReadEEPROM + +Routine Description: + + This routine reads from the EEPROM + +Arguments: + + PDEVICE - Pointer to the device structure + EEPROMAddress - EEPROM register to read + Contents - Buffer where contents of the location are returned + +Return Value: + + NIC_STATUS_SUCCESS + NIC_STATUS_FAILURE + +--*/ + +NIC_STATUS +tc90x_ReadEEPROM( + IN PNIC_INFORMATION Adapter, + IN USHORT EEPROMAddress, + OUT PUSHORT Contents + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + USHORT lowerOffset = 0; + USHORT upperOffset = 0; + + if (EEPROMAddress > 0x003F) { + + lowerOffset = EEPROMAddress & 0x003F; + upperOffset = (EEPROMAddress & 0x03C0) << 2; + EEPROMAddress = upperOffset | lowerOffset; + } + + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_0); + // + // Check if EEPROM is busy + // + if (tc90x_CheckIfEEPROMBusy(pAdapter) != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(("tc90x_ReadEEPROM: EEPROM is busy\n")); + return NIC_STATUS_FAILURE; + + } + // + // Issue the read eeprom data command + // + NIC_WRITE_PORT_USHORT( + pAdapter, + EEPROM_COMMAND_REGISTER, + (USHORT)(EEPROM_COMMAND_READ + (USHORT)EEPROMAddress)); + // + // Check if EEPROM is busy + // + if (tc90x_CheckIfEEPROMBusy(pAdapter) != NIC_STATUS_SUCCESS) { + + DBGPRINT_ERROR(( + "tc90x_ReadEEPROM: EEPROM is busy after command.\n")); + + return NIC_STATUS_FAILURE; + } + // + // Save value read from eeprom + // + *Contents = NIC_READ_PORT_USHORT( + pAdapter, + EEPROM_DATA_REGISTER); + + return NIC_STATUS_SUCCESS; +} + + +/*++ + +Routine Name : + + WriteEEPROM + +Routine Description: + + This routine writes to the EEPROM + +Arguments: + + PDEVICE - Pointer to the device structure + EEPROMAddress - EEPROM register to write + Data - Data to write + +Return Value: + + NIC_STATUS_SUCCESS + NIC_STATUS_FAILURE + +--*/ + +NIC_STATUS +tc90x_WriteEEPROM( + IN PNIC_INFORMATION Adapter, + IN USHORT EEPROMAddress, + IN USHORT Data + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + USHORT lowerOffset = 0; + USHORT upperOffset = 0; + USHORT saveAddress; + + saveAddress = EEPROMAddress; + + if (EEPROMAddress > 0x003F) { + + lowerOffset = EEPROMAddress & 0x003F; + upperOffset = (EEPROMAddress & 0x03C0) << 2; + EEPROMAddress = upperOffset | lowerOffset; + } + + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_0); + + // + // Issue erase register command prior to writing + // + NIC_WRITE_PORT_USHORT( + pAdapter, + EEPROM_COMMAND_REGISTER, + (USHORT)EEPROM_WRITE_ENABLE); + + if (tc90x_CheckIfEEPROMBusy(pAdapter) != NIC_STATUS_SUCCESS){ + + DBGPRINT_ERROR(( + "WriteEEPROM: Write enable, EEPROM is busy\n")); + + return NIC_STATUS_FAILURE; + } + + NIC_WRITE_PORT_USHORT( + pAdapter, + EEPROM_COMMAND_REGISTER, + (USHORT)(EEPROM_ERASE_REGISTER + (USHORT)EEPROMAddress)); + + if (tc90x_CheckIfEEPROMBusy(pAdapter) != NIC_STATUS_SUCCESS){ + + DBGPRINT_ERROR(( + "WriteEEPROM: Erase Register, EEPROM is busy\n")); + + return NIC_STATUS_FAILURE; + } + // + // Load data to be written to the eeprom + // + NIC_WRITE_PORT_USHORT(pAdapter, EEPROM_DATA_REGISTER, Data); + +// DBGPRINT_INITIALIZE(( +// "WriteEeprom: Writing value %x at %x \n" ,Data, saveAddress)); + + if (tc90x_CheckIfEEPROMBusy(pAdapter) != NIC_STATUS_SUCCESS){ + + DBGPRINT_ERROR(( + "WriteEEPROM: Write data, EEPROM is busy\n" + )); + return NIC_STATUS_FAILURE; + } + + + // + // Issue the write eeprom data command + // + NIC_WRITE_PORT_USHORT( + pAdapter, + EEPROM_COMMAND_REGISTER, + (USHORT)EEPROM_WRITE_ENABLE); + + if (tc90x_CheckIfEEPROMBusy(pAdapter) != NIC_STATUS_SUCCESS){ + + DBGPRINT_ERROR(("WriteEEPROM: EEPROM is busy\n")); + return NIC_STATUS_FAILURE; + } + + NIC_WRITE_PORT_USHORT( + pAdapter, + EEPROM_COMMAND_REGISTER, + (USHORT)(EEPROM_WRITE_REGISTER + (USHORT)EEPROMAddress)); + + if (tc90x_CheckIfEEPROMBusy(pAdapter) != NIC_STATUS_SUCCESS){ + + DBGPRINT_ERROR(( + "WriteEEPROM: Write register, EEPROM is busy\n")); + return NIC_STATUS_FAILURE; + } + return NIC_STATUS_SUCCESS; +} + + + +/*++ + +Routine Name : + + CalculateEEPROMChecksum1 + +Routine Description: + + Calculates the EEPROM checksum #1 from offset 0 to 0x1F. + +Arguments: + + PDEVICE - Pointer to the device structure + +Return Value: + + Checksum #1 value + +--*/ + +USHORT +tc90x_CalculateEEPROMChecksum1( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + NIC_STATUS status; + USHORT checksum=0; + USHORT value=0; + UCHAR index; + + DBGPRINT_FUNCTION(("CalculateChecksum1: IN \n")); + + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_0); + + for (index = EEPROM_NODE_ADDRESS_WORD_0; + index < EEPROM_CHECKSUM_1; + index++) { + + status = tc90x_ReadEEPROM(pAdapter, index, &value); + + if (status == NIC_STATUS_FAILURE) { + + DBGPRINT_ERROR(( + "CalculateEEPROMChecksum1: Read failure\n")); + + return NIC_STATUS_FAILURE; + } + + checksum ^= (USHORT)LOBYTE(value); + checksum ^= (USHORT)HIBYTE(value); + } + + DBGPRINT_FUNCTION(("CalculateChecksum1: OUT \n")); + + return((USHORT)checksum); +} + + + + + +NIC_STATUS +tc90x_SoftwareWork( + IN PNIC_INFORMATION Adapter + ) + +{ + PNIC_INFORMATION pAdapter = Adapter; + USHORT SoftwareInformation2 = 0; + ULONG DmaControl = 0; + USHORT NetDiag = 0; + USHORT Contents = 0; + + DBGPRINT_FUNCTION(("SoftwareWork: IN\n")); + // Additional work#1 + + tc90x_ReadEEPROM( + pAdapter, + EEPROM_SOFTWARE_INFORMATION_2, + &Contents); + + if (!(Contents & ENABLE_MWI_WORK)) { + + DmaControl = NIC_READ_PORT_ULONG( + pAdapter, + DMA_CONTROL_REGISTER); + + NIC_WRITE_PORT_ULONG( + pAdapter, + DMA_CONTROL_REGISTER, + (ULONG)(DMA_CONTROL_DEFEAT_MWI | DmaControl)); + } + + // Additional work#2 + + NIC_COMMAND( + pAdapter, + COMMAND_SELECT_REGISTER_WINDOW | REGISTER_WINDOW_4); + + NetDiag = NIC_READ_PORT_USHORT(pAdapter, NETWORK_DIAGNOSTICS_REGISTER); + + if ((((NetDiag & NETWORK_DIAGNOSTICS_ASIC_REVISION) >> 4) == 1) && + (((NetDiag & NETWORK_DIAGNOSTICS_ASIC_REVISION_LOW) >> 1) < 4)) { + pAdapter->Hardware.HurricaneEarlyRevision = TRUE; + DBGPRINT_INITIALIZE(("Hurricane Early board\n")); + tc90x_HurricaneEarlyRevision(pAdapter); + } + + SoftwareInformation2 = 0; + ((PSOFTWARE_INFORMATION_2)&SoftwareInformation2)->D3Work = 1; + + if (Contents & SoftwareInformation2) { + DBGPRINT_INITIALIZE(("Enable D3 work \n")); + pAdapter->Hardware.D3Work = TRUE; + } + + + // Additional work#3 + + pAdapter->Hardware.DontSleep = FALSE; + + SoftwareInformation2 = 0; + ((PSOFTWARE_INFORMATION_2) + &SoftwareInformation2)->WOLConnectorPresent = 1; + + ((PSOFTWARE_INFORMATION_2)&SoftwareInformation2)->AutoResetToD0 = 1; + + DBGPRINT_INITIALIZE(("SoftwareInfo2 : %x\n", SoftwareInformation2)); + + if (!(Contents & SoftwareInformation2)) { + DBGPRINT_INITIALIZE(("Don't sleep is TRUE \n")); + pAdapter->Hardware.DontSleep = TRUE; + } + + DBGPRINT_FUNCTION(("SoftwareWork: OUT\n")); + return NIC_STATUS_SUCCESS; +} + +VOID +tc90x_FlowControl( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = (PNIC_INFORMATION)Adapter; + BOOLEAN PhyResponding; + USHORT PhyAnar; + USHORT PhyControl; + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_ANAR, &PhyAnar); + PhyAnar |= MII_ANAR_FLOWCONTROL; + WriteMIIPhy(pAdapter, MII_PHY_ANAR, PhyAnar); + + PhyResponding = ReadMIIPhy(pAdapter, MII_PHY_CONTROL, &PhyControl); + PhyControl |= MII_CONTROL_START_AUTO; + WriteMIIPhy(pAdapter, MII_PHY_CONTROL, PhyControl); +} + +VOID +tc90x_HurricaneEarlyRevision( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + BOOLEAN PhyResponding; + USHORT PhyRegisterValue; + + PhyResponding = ReadMIIPhy( + pAdapter, + MII_PHY_REGISTER_24, + &PhyRegisterValue); + + if (!PhyResponding) + DBGPRINT_ERROR(("CaneRev-ReadMIIPhy: Phy not responding\n")); + + PhyRegisterValue |= MII_PHY_REGISTER_24_PVCIRC; + + WriteMIIPhy( + pAdapter, + MII_PHY_REGISTER_24, + PhyRegisterValue); +} + +/* NIC_STATUS +RxResetAndWork(IN PNIC_INFORMATION Adapter) +{ + PNIC_INFORMATION pAdapter = Adapter; + NIC_STATUS nicStatus; + + nicStatus = NIC_COMMAND_WAIT(pAdapter, COMMAND_RX_RESET); + + if (nicStatus != NIC_STATUS_SUCCESS) { + DBGPRINT_ERROR(("RxResetAndWork: Rx reset failed\n")); + return NIC_STATUS_FAILURE; + } + // not all work needed after Rx reset. Refine later + if (tc90x_SoftwareWork(pAdapter) != NIC_STATUS_SUCCESS) { + DBGPRINT_ERROR(("RxResetAndWork: SoftwareWork failed\n")); + return NIC_STATUS_FAILURE; + } + return NIC_STATUS_SUCCESS; +} */ + + + + +#define MAX_UNITS 8 + +#ifdef DEBUG +static ULONG debug = DEBUG_ERROR; +#endif + +static INT switchdelay[] = {0, 0, 0, 0, 0, 0, 0, 0}; +static INT media_select[MAX_UNITS] = {MEDIA_NONE, }; +static INT flowcontrol[MAX_UNITS] = {0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1}; +static INT downpoll[MAX_UNITS] = {0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8}; +static INT full_duplex[MAX_UNITS] = {-1, -1, -1, -1, -1, -1, -1, -1}; + +static ULONG tc90x_SendCount[] = { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 }; +static ULONG tc90x_ReceiveCount[] = { 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 }; + +NIC_STATUS +tc90x_ReadCommandLineChanges( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + INT index = pAdapter->Index; + USHORT mediatype; + + DBGPRINT_INIT(("ReadCommandLineChanges: IN index=%x\n",index)); + + if (tc90x_SendCount[index] < NIC_MINIMUM_SEND_COUNT || + tc90x_SendCount[index] > NIC_MAXIMUM_SEND_COUNT) { + + DBGPRINT_ERROR(("ProcessOverride: Using default value\n")); + pAdapter->Resources.SendCount = NIC_DEFAULT_SEND_COUNT; + } + else { + DBGPRINT_INITIALIZE(("SendCount = %x\n",(INT)tc90x_SendCount[index])); + pAdapter->Resources.SendCount = tc90x_SendCount[index]; + } + + if (tc90x_ReceiveCount[index] < NIC_MINIMUM_RECEIVE_COUNT || + tc90x_ReceiveCount[index] > NIC_MAXIMUM_RECEIVE_COUNT) { + + DBGPRINT_ERROR(("ReadCommandLineChanges: Using default receive\n")); + pAdapter->Resources.ReceiveCount = NIC_DEFAULT_RECEIVE_COUNT; + + } + else { + pAdapter->Resources.ReceiveCount = tc90x_ReceiveCount[index]; + } + + DBGPRINT_INITIALIZE(( + "ReceiveCount=%x\n", (INT)pAdapter->Resources.ReceiveCount)); + + if ( (index < MAX_UNITS) && (flowcontrol[index] <= 0) ){ + + DBGPRINT_INITIALIZE(("User disables FlowControl\n")); + pAdapter->Hardware.FlowControlSupported = FALSE; + pAdapter->Hardware.FlowControlEnable = FALSE; + } + else { + + DBGPRINT_INITIALIZE(("FlowControl is enabled by default\n")); + pAdapter->Hardware.FlowControlSupported = TRUE; + pAdapter->Hardware.FlowControlEnable = TRUE ; + } + + if ( (index < MAX_UNITS) && (downpoll[index] == 0x40) ){ + + DBGPRINT_INITIALIZE(("DownPollRate is 64\n")); + pAdapter->Resources.DownPollRate = 0x40; + } + else { + + DBGPRINT_INITIALIZE(("DownPollRate is 8 by default\n")); + pAdapter->Resources.DownPollRate = 0x8; + } + + if ( (index < MAX_UNITS) && (switchdelay[index] > 0) ){ + + DBGPRINT_INITIALIZE(("User enables delay for switch\n")); + pAdapter->DelayStart = TRUE; + } + else + pAdapter->DelayStart = FALSE; + + /* Possible media selections: + MEDIA_NONE 0 + MEDIA_10BASE_T 1 + MEDIA_10AUI 2 + MEDIA_10BASE_2 3 + MEDIA_100BASE_TX 4 + MEDIA_100BASE_FX 5 + MEDIA_10BASE_FL 6 + MEDIA_AUTO_SELECT 7 + */ + if (index < MAX_UNITS) + mediatype = media_select[index]; + else + mediatype = MEDIA_NONE; + + if ((mediatype > 0) && (mediatype <= 7)) + { pAdapter->Hardware.MediaOverride = mediatype; + DBGPRINT_INITIALIZE(("User selects Media = %i\n", mediatype)); + } + else + pAdapter->Hardware.MediaOverride = MEDIA_NONE; + + if (pAdapter->Hardware.MediaOverride != MEDIA_AUTO_SELECT) + { if ( (index < MAX_UNITS) && (full_duplex[index] > 0) ) + { DBGPRINT_INITIALIZE(("NotAutoSelect: User enables full duplex\n")); + if ( (pAdapter->Hardware.MediaOverride != MEDIA_10AUI) && + (pAdapter->Hardware.MediaOverride != MEDIA_10BASE_2) ) + pAdapter->Hardware.FullDuplexEnable = TRUE; + pAdapter->Hardware.DuplexCommandOverride = TRUE; + } + else if ( (index < MAX_UNITS) && (full_duplex[index] == 0) ) + { DBGPRINT_INITIALIZE(("NotAutoSelect: User disables full duplex\n")); + pAdapter->Hardware.FullDuplexEnable = FALSE; + pAdapter->Hardware.DuplexCommandOverride = TRUE; + } + else + pAdapter->Hardware.DuplexCommandOverride = FALSE; + } + + DBGPRINT_INITIALIZE(("ReadCommandLineChanges: OUT\n")); + return NIC_STATUS_SUCCESS; +} + --- /dev/null Tue May 5 13:32:27 1998 +++ linux/drivers/net/3c90x.h Thu Oct 7 05:26:52 1999 @@ -0,0 +1,2204 @@ +/* + 3Com EtherLink 10/100 PCI (3C90x) Linux Network Driver, Copyright (c) 1999 + 3Com Corporation. All rights reserved. + 3Com Linux Network Driver software is distributed as is, without any warranty + of any kind, either express or implied as further specified in the GNU Public + License. This software may be used and distributed according to the terms of + the GNU Public License, located in the file LICENSE. + + 3Com and EtherLink are registered trademarks of 3Com Corporation. Linux is a + registered trademarks of Linus Torvalds. +*/ + +//#define DEBUG 1 +#define SERR_NEEDED 1 + + + + +#include + +#ifdef MODULE +#ifdef MODVERSIONS +#include +#endif +/* don't define kernel_verion in module.h */ +#define __NO_VERSION__ + +#include +#include +#else +#define MOD_INC_USE_COUNT +#define MOD_DEC_USE_COUNT +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + + +#ifndef LINUX_VERSION_CODE +#include +#endif + +#if LINUX_VERSION_CODE <= 0x20200 +#include +#endif + +#if LINUX_VERSION_CODE < 0x10300 +#define RUN_AT(x) (x) +#define DEV_ALLOC_SKB(length) alloc_skb(length, GFP_ATOMIC) + +//#if defined(__alpha) +//#error "The Alpha architecture is only supported with kernel version 2.0." +//#endif + +#define virt_to_bus(address) ((ULONG)address) +#define bus_to_virt(address) ((PVOID)address) +#define NR_IRQS 16 + +#else + +#define RUN_AT(x) (jiffies + (x)) +#define DEV_ALLOC_SKB(length) dev_alloc_skb(length) + +#endif + + +#if LINUX_VERSION_CODE < 0x20159 +#define DEV_FREE_SKB(skb) dev_kfree_skb(skb, FREE_WRITE); +#else +#define DEV_FREE_SKB(skb) dev_kfree_skb(skb); +#endif + + +#ifdef SA_SHIRQ +#define FREE_IRQ(irqnum, dev) free_irq(irqnum, dev) +#define REQUEST_IRQ(i,h,f,n,instance) request_irq(i,h,f,n,instance) +#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs) +#else +#define FREE_IRQ(irqnum, dev) free_irq(irqnum) +#define REQUEST_IRQ(i,h,f,n,instance) request_irq(i,h,f,n) +#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs) +#endif + + +#if (LINUX_VERSION_CODE >= 0x10344) +#define NEW_MULTICAST +#include +#else +#define udelay(microsec) \ + do { int _i = 4*microsec; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0) +#endif + +#if LINUX_VERSION_CODE >= 0x20200 +#include +#endif + +#if LINUX_VERSION_CODE < 0x20138 +#define test_and_set_bit(value, address) \ + set_bit(value, address) +#endif + + + + +#define BIT_0 (1 << 0) +#define BIT_1 (1 << 1) +#define BIT_2 (1 << 2) +#define BIT_3 (1 << 3) +#define BIT_4 (1 << 4) +#define BIT_5 (1 << 5) +#define BIT_6 (1 << 6) +#define BIT_7 (1 << 7) +#define BIT_8 (1 << 8) +#define BIT_9 (1 << 9) +#define BIT_10 (1 << 10) +#define BIT_11 (1 << 11) +#define BIT_12 (1 << 12) +#define BIT_13 (1 << 13) +#define BIT_14 (1 << 14) +#define BIT_15 (1 << 15) +#define BIT_16 (1 << 16) +#define BIT_17 (1 << 17) +#define BIT_18 (1 << 18) +#define BIT_19 (1 << 19) +#define BIT_20 (1 << 20) +#define BIT_21 (1 << 21) +#define BIT_22 (1 << 22) +#define BIT_23 (1 << 23) +#define BIT_24 (1 << 24) +#define BIT_25 (1 << 25) +#define BIT_26 (1 << 26) +#define BIT_27 (1 << 27) +#define BIT_28 (1 << 28) +#define BIT_29 (1 << 29) +#define BIT_30 (1 << 30) +#define BIT_31 (1 << 31) + + +#ifndef HIBYTE + +#define HIBYTE(_w) (((USHORT)(_w)) >> 8) + +#endif + +#ifndef LOBYTE + +#define LOBYTE(_w) ((UCHAR)((_w) & 0xFF)) + +#endif + + +typedef char *PCHAR; +typedef long LONG; +typedef unsigned long ULONG; +typedef unsigned short USHORT; +typedef unsigned char UCHAR; +typedef void VOID; +typedef char CHAR; +typedef int INT; +typedef unsigned long *PULONG; +typedef unsigned short *PUSHORT; +typedef unsigned char *PUCHAR; +typedef void *PVOID; +typedef UCHAR BOOLEAN; +typedef BOOLEAN *PBOOLEAN; + +typedef struct enet_statistics ENET_STATISTICS; +typedef struct dev_mc_list DEV_MC_LIST, *PDEV_MC_LIST; +typedef struct timer_list TIMER; +typedef struct tq_struct TASKQ; + +#define FALSE 0 +#define TRUE 1 + +// +// Linux Error code. +// +typedef ULONG LINUX_ERROR_CODE; +#define LINUX_ERROR_CODE_OUT_OF_RESOURCES 0x1 + +#define IN +#define OUT + + +/* + * Typedefs for Linux structures + */ + +typedef struct device *PDEVICE; +typedef struct device DEVICE; +typedef struct ifreq *PIFREQ; +typedef struct sk_buff *PSKB; +typedef struct pt_regs *PTREGS; +typedef struct enet_statistics *PENET_STATISTICS; + + + + + +/* + * 3Com Node Address + */ + +#define EEPROM_NODE_ADDRESS_WORD_0 0x00 +#define EEPROM_NODE_ADDRESS_WORD_1 0x01 +#define EEPROM_NODE_ADDRESS_WORD_2 0x02 + +#define EEPROM_DEVICE_ID 0x03 +/*++ + + Possible values: + + 0x9055 - PCI 10/100 Mbps; shared 10BASE-T/100BASE-TX connector. + 0x9056 - PCI 10/100 Mbps; shared 10BASE-T/100BASE-T4 connector. + 0x9004 - PCI 10BASE-T (TPO) + 0x9005 - PCI 10BASE-T/10BASE-2/AUI(COMBO) + 0x9006 - PCI 10BASE-T/10BASE-2/(TPC) + +--*/ +#define EEPROM_MANUFACTURING_DATE 0x04 + +typedef struct _MANUFACTURING_DATE { + + USHORT Day:5; + USHORT Month:4; + USHORT Year:7; + +} MANUFACTURING_DATE; + +#define EEPROM_MANUFACTURING_DIVISION 0x05 +#define EEPROM_MANUFACTURING_PRODUCT_CODE 0x06 +#define EEPROM_MANUFACTURING_ID 0x07 +#define EEPROM_PCI_PARAMETERS_1 0x08 + +typedef struct _PCI_PARAMETERS_1 { + + USHORT Bit0:1; + USHORT Lower1Meg:1; + USHORT DisableMemoryBase:1; + USHORT D3SupportCold:1; + USHORT D1Support:1; + USHORT D2Support:1; + USHORT MinimumGrant:4; + USHORT MaximumLatency:6; + +} PCI_PARAMETERS_1, *PPCI_PARAMETERS_1; + +#define EEPROM_ROM_INFORMATION 0x09 + +typedef struct _ROM_INFORMATION { + + USHORT Reserved:11; + USHORT ROMPresent:1; + // + // 0x00 - 64k * 8. + // 0x01 - 128k * 8. + // 0x1x - Reserved. + // + USHORT RomSize:2; + +} ROM_INFORMATION, *PROM_INFORMATION; + +/* + * OEM Node address + */ +#define EEPROM_OEM_NODE_ADDRESS_WORD_0 0x0A +#define EEPROM_OEM_NODE_ADDRESS_WORD_1 0x0B +#define EEPROM_OEM_NODE_ADDRESS_WORD_2 0x0C + +#define EEPROM_SOFTWARE_INFORMATION_1 0x0D + +typedef struct _SOFTWARE_INFORMATION_1 { + + USHORT Reserved1:4; + + #define EEPROM_OPTIMIZE_NORMAL 0x1 + #define EEPROM_OPTIMIZE_FOR_THROUGHPUT 0x2 + #define EEPROM_OPTIMIZE_FOR_CPU 0x3 + USHORT OptimizeFor:2; + + USHORT Reserved2:8; + USHORT LinkBeatDisable:1; + + #define EEPROM_DISABLE_FULL_DUPLEX 0x00 + #define EEPROM_ENABLE_FULL_DUPLEX 0x01 + USHORT FullDuplexMode:1; + +} SOFTWARE_INFORMATION_1, *PSOFTWARE_INFORMATION_1; + +#define EEPROM_COMPATABILITY_WORD 0x0E +#define EEPROM_COMPATABILITY_LEVEL 0x00 +typedef struct _COMPATABILITY_WORD { + + USHORT WarningLevel:8; + USHORT FailureLevel:8; + +} COMPATABILITY_WORD, *PCOMPATABILITY_WORD; + +#define EEPROM_SOFTWARE_INFORMATION_2 0x0F +#define ENABLE_MWI_WORK 0x0020 + +typedef struct _SOFTWARE_INFORMATION_2 { + + USHORT Reserved1:1; + USHORT BroadcastRxErrDone:1; + USHORT EncoderDecoderLoopBackErrDone:1; + USHORT WOLConnectorPresent:1; + USHORT PMEPulsed:1; + USHORT MWIErrDone:1; + USHORT AutoResetToD0:1; + USHORT D3Work:1; + +} SOFTWARE_INFORMATION_2, *PSOFTWARE_INFORMATION_2; + +#define EEPROM_CAPABILITIES_WORD 0x10 +typedef struct _CAPABILITIES_WORD { + + USHORT SupportsPlugNPlay:1; + USHORT SupportsFullDuplex:1; + USHORT SupportsLargePackets:1; + USHORT SupportsSlaveDMA:1; + USHORT SupportsSecondDMA:1; + USHORT SupportsFullBusMaster:1; + USHORT SupportsFragBusMaster:1; + USHORT SupportsCRCPassThrough:1; + USHORT SupportsTxDone:1; + USHORT SupportsNoTxLength:1; + USHORT SupportsRxRepeat:1; + USHORT Supports100Mbps:1; + USHORT SupportsPowerManagement:1; + +} CAPABILITIES_WORD, *PCAPABILITIES_WORD; + +#define EEPROM_RESERVED_LOCATION 0x11 +#define EEPROM_INTERNAL_CONFIG_WORD_0 0x12 +#define EEPROM_INTERNAL_CONFIG_WORD_1 0x13 +#define EEPROM_ANALOG_DIAGNOSTICS 0x14 +#define EEPROM_SOFTWARE_INFORMATION_3 0x15 + +typedef struct _SOFTWARE_INFORMATION_3 { + + #define EEPROM_GENERIC_MII 0x00 + #define EEPROM_100BASE_T4_MII 0x01 + #define EEPROM_10BASE_T_MII 0x02 + #define EEPROM_100BASE_TX_MII 0x03 + #define EEPROM_10_BASE_T_AUTONEGOTIATION 0x04 + #define EEPROM_100_BASE_TX_AUTONEGOTIATION 0x04 + USHORT ForceXcvr:4; + + USHORT Reserved:12; + +} SOFTWARE_INFORMATION_3, *PSOFTWARE_INFORMATION_3; +/* + * Locations 0x1E - 0x1F are reserved. + */ +#define EEPROM_CHECKSUM_1 0x20 +/* + * Locations 0x21 - 0x2F are reserved. + */ +#define EEPROM_SOS_PINS_1_TO_4 0x21 +#define EEPROM_SOS_PINS_5_TO_7 0x22 +/* + * Locations 0x00 - 0xFD are flexible format locations (4kb EEPROMs) + */ +#define EEPROM_CHECKSUM_2_UPPER 0xFE +#define EEPROM_CHECKSUM_2_LOWER 0xFF +/* + * Locations 0x00 - 0x3FD are flexible format locations (16Kb EEPROMs) + */ +#define EEPROM_CHECKSUM_3_UPPER 0x3FE +#define EEPROM_CHECKSUM_3_LOWER 0x3FF +#define EEPROM_COMMAND_MASK 0xE000 +#define EEPROM_COMMAND_AUTOINIT_DONE 0xE000 +#define EEPROM_COMMAND_PCI_CONFIG_WRITE 0xA000 +#define EEPROM_COMMAND_REGISTER_WRITE 0x6000 +#define EEPROM_COMMAND_TX_FIFO_WRITE 0x2000 +#define EEPROM_CURRENT_WINDOW_MASK 0x7000 +#define EEPROM_ADDRESS_MASK 0x00FF +#define EEPROM_TX_BYTE_COUNT 0x03FF +#define EEPROM_FLEXIBLE_FORMAT_START 0x40 +#define EEPROM_WORD_ACCESS 0x1000 +#define MAX_FLEX_EEPROM_SIZE 2048 + +#define EEPROM_WINDOW_0 (0x0 << 0x8) +#define EEPROM_WINDOW_1 (0x1 << 0x8) +#define EEPROM_WINDOW_2 (0x2 << 0x8) +#define EEPROM_WINDOW_3 (0x3 << 0x8) +#define EEPROM_WINDOW_4 (0x4 << 0x8) +#define EEPROM_WINDOW_5 (0x5 << 0x8) +#define EEPROM_WINDOW_6 (0x6 << 0x8) +#define EEPROM_WINDOW_7 (0x7 << 0x8) + + + +typedef struct _PCI_CONFIG_CONFIGURATION_REGISTERS { + + USHORT VendorId; + USHORT DeviceId; + USHORT Command; + USHORT Status; + UCHAR RevisionId; + UCHAR ClassCode[3]; + UCHAR CacheLineSize; + UCHAR LatencyTimer; + UCHAR HeaderType; + UCHAR Reserved1; + ULONG IoBaseAddress; + ULONG MemoryBaseAddress; + ULONG Reserved2; + ULONG Reserved3; + ULONG Reserved4; + ULONG Reserved5; + ULONG Reserved6; + USHORT SubSystemVendorId; + USHORT SubSystemId; + ULONG BIOSRomControl; + UCHAR CapabilityPointer; + UCHAR Reserved7[3]; + ULONG Reserved8; + UCHAR InterruptLine; + UCHAR InterruptPin; + UCHAR MinimumGrant; + UCHAR MaximumLatency; + ULONG Reserved9; + ULONG Reserved10; + ULONG Reserved11; + ULONG Reserved12; + UCHAR MediaTestModeLow; + UCHAR Reserved13[3]; + UCHAR MediaTestModeHigh; + UCHAR Reserved14[3]; + UCHAR MediaTestOutput; + UCHAR Reserved15[3]; + UCHAR MediaTestPattern; + UCHAR Reserved16[3]; + ULONG Reserved17[31]; + UCHAR CapabilityId; + UCHAR NextPointer; + USHORT PowerManagementCapability; + USHORT PowerManagementControl; + UCHAR Reserved18; + UCHAR Data; + +} PCI_CONFIG_CONFIGURATION_REGISTERS, *PPCI_CONFIG__CONFIGURATION_REGISTERS; + +typedef struct _PCI_CONFIG_COMMAND { + + USHORT IoSpace:1; + USHORT MemorySpace:1; + USHORT BusMasterEnable:1; + USHORT Reserved1:1; + USHORT MWIEnable:1; + USHORT Reserved2:1; + USHORT ParityErrorResponse:1; + USHORT Reserved3:1; + USHORT SERREnable:1; + +} PCI_CONFIG_COMMAND, *PPCI_CONFIG_COMMAND; + + +typedef struct _PCI_CONFIG_STATUS { + + USHORT Reserved1:4; + USHORT CapabilitiesList:1; + USHORT Reserved2:2; + USHORT FastBackToBack:1; + USHORT DmaParityDetected:1; + USHORT DevselTiming:2; + USHORT SignalTargetAbort:1; + USHORT ReceivedTargetAbort:1; + USHORT ReceivedMasterAbort:1; + USHORT SignaledSystemError:1; + USHORT DetectedParityError:1; + +} PCI_CONFIG__STATUS, *PPCI_CONFIG_STATUS; + +typedef struct _PCI_CONFIG_REVISION_ID { + + UCHAR Revision:5; + UCHAR ChipVersion:3; + +} PCI_CONFIG_REVISION_ID, *PCI_CONFIG_PREVISION_ID; + + +#define PCI_POWER_CONTROL 0xE0 +#define PCI_PME_ENABLE 0x0100 +#define PCI_PME_STATUS 0x8000 + + +typedef struct _PCI_CONFIG_POWER_MANAGEMENT_CONTROL { + + #define PCI_POWER_STATE_D0 0x00 + #define PCI_POWER_STATE_D1 0x01 + #define PCI_POWER_STATE_D2 0x02 + #define PCI_POWER_STATE_D3 0x03 + USHORT PowerState:2; + + USHORT Reserved1:6; + USHORT PMEEnable:1; + USHORT DataSelect:4; + USHORT DataScale:2; + USHORT PMEStatus:1; + +} PCI_CONFIG_POWER_MANAGEMENT_CONTROL, + *PPCI_CONFIG_POWER_MANAGEMENT_CONTROL; + + +// +// Supported PCI device id's +// +#define NIC_VENDOR_ID 0x10B7 +#define NIC_PCI_DEVICE_ID_9055 0x9055 +#define NIC_PCI_DEVICE_ID_9056 0x9056 +#define NIC_PCI_DEVICE_ID_9058 0x9058 +#define NIC_PCI_DEVICE_ID_9004 0x9004 +#define NIC_PCI_DEVICE_ID_9005 0x9005 +#define NIC_PCI_DEVICE_ID_9006 0x9006 +#define NIC_PCI_DEVICE_ID_900A 0x900A +#define NIC_PCI_DEVICE_ID_905A 0x905A +#define NIC_PCI_DEVICE_ID_9200 0x9200 +#define NIC_PCI_DEVICE_ID_9800 0x9800 +#define NIC_PCI_DEVICE_ID_9805 0x9805 +#define NIC_PCI_DEVICE_ID_4500 0x4500 +#define NIC_PCI_DEVICE_ID_7646 0x7646 +// +// ASIC versions. +// +#define NIC_ASIC_CYCLONE_KRAKATOA_LUCENT 0x0 +#define NIC_ASIC_HURRICANE_TORNADO_LUCENT 0x1 +#define NIC_ASIC_HURRICANE_NATIONAL 0x2 +#define NIC_ASIC_HURRICANE_TORNADO_BROADCOM 0x3 +// +// Window definitions. +// +#define REGISTER_WINDOW_0 0x0 // setup/configuration +#define REGISTER_WINDOW_1 0x1 // operating set +#define REGISTER_WINDOW_2 0x2 // station address setup/read +#define REGISTER_WINDOW_3 0x3 // FIFO management +#define REGISTER_WINDOW_4 0x4 // diagnostics +#define REGISTER_WINDOW_5 0x5 // registers set by commands +#define REGISTER_WINDOW_6 0x6 // statistics +#define REGISTER_WINDOW_7 0x7 // bus master control +#define REGISTER_WINDOW_MASK 0xE000 + +// +// ------------------ Register definitions --------------------- +// +#define INTSTATUS_INTERRUPT_MASK 0x6EE +// +// Window 0 registers. +// + +#define BIOS_ROM_ADDRESS_REGISTER 0x4 +#define BIOS_ROM_DATA_REGISTER 0x8 + +#define EEPROM_COMMAND_REGISTER 0xA +#define EEPROM_BUSY_BIT BIT_15 +#define EEPROM_COMMAND_READ 0x0080 +#define EEPROM_WRITE_ENABLE 0x0030 +#define EEPROM_ERASE_REGISTER 0x00C0 +#define EEPROM_WRITE_REGISTER 0x0040 + +#define EEPROM_DATA_REGISTER 0xC + +#define INTSTATUS_COMMAND_REGISTER 0xE +#define INTSTATUS_INTERRUPT_LATCH BIT_0 +#define INTSTATUS_HOST_ERROR BIT_1 +#define INTSTATUS_TX_COMPLETE BIT_2 +#define INTSTATUS_RX_COMPLETE BIT_4 +#define INTSTATUS_INTERRUPT_REQUESTED BIT_6 +#define INTSTATUS_UPDATE_STATISTICS BIT_7 +#define INTSTATUS_LINK_EVENT BIT_8 +#define INTSTATUS_DOWN_COMPLETE BIT_9 +#define INTSTATUS_UP_COMPLETE BIT_10 +#define INTSTATUS_COMMAND_IN_PROGRESS BIT_12 +#define INTSTATUS_INTERRUPT_NONE 0 +#define INTSTATUS_INTERRUPT_ALL 0x6EE +#define INTSTATUS_ACKNOWLEDGE_ALL 0x7FF + + +// +// Window 2 registers. +// +#define STATION_ADDRESS_LOW_REGISTER 0x0 +#define STATION_ADDRESS_MID_REGISTER 0x2 +#define STATION_ADDRESS_HIGH_REGISTER 0x4 + +// +// Window 3 registers. +// + +#define INTERNAL_CONFIG_REGISTER 0x0 +#define INTERNAL_CONFIG_DISABLE_BAD_SSD BIT_8 +#define INTERNAL_CONFIG_ENABLE_TX_LARGE BIT_14 +#define INTERNAL_CONFIG_ENABLE_RX_LARGE BIT_15 +#define INTERNAL_CONFIG_AUTO_SELECT BIT_24 +#define INTERNAL_CONFIG_DISABLE_ROM BIT_25 +#define INTERNAL_CONFIG_TRANSCEIVER_MASK 0x00F00000L + +#define MAXIMUM_PACKET_SIZE_REGISTER 0x4 + +#define MAC_CONTROL_REGISTER 0x6 +#define MAC_CONTROL_FULL_DUPLEX_ENABLE BIT_5 +#define MAC_CONTROL_ALLOW_LARGE_PACKETS BIT_6 +#define MAC_CONTROL_FLOW_CONTROL_ENABLE BIT_8 +#define MEDIA_OPTIONS_REGISTER 0x8 +#define MEDIA_OPTIONS_100BASET4_AVAILABLE BIT_0 +#define MEDIA_OPTIONS_100BASETX_AVAILABLE BIT_1 +#define MEDIA_OPTIONS_100BASEFX_AVAILABLE BIT_2 +#define MEDIA_OPTIONS_10BASET_AVAILABLE BIT_3 +#define MEDIA_OPTIONS_10BASE2_AVAILABLE BIT_4 +#define MEDIA_OPTIONS_10AUI_AVAILABLE BIT_5 +#define MEDIA_OPTIONS_MII_AVAILABLE BIT_6 +#define MEDIA_OPTIONS_10BASEFL_AVAILABLE BIT_8 + +#define RX_FREE_REGISTER 0xA +#define TX_FREE_REGISTER 0xC + +// +// Window 4 registers. +// +#define PHYSICAL_MANAGEMENT_REGISTER 0x8 +#define NETWORK_DIAGNOSTICS_REGISTER 0x6 +#define NETWORK_DIAGNOSTICS_ASIC_REVISION 0x003E +#define NETWORK_DIAGNOSTICS_ASIC_REVISION_LOW 0x000E +#define NETWORK_DIAGNOSTICS_UPPER_BYTES_ENABLE BIT_6 +#define MEDIA_STATUS_REGISTER 0xA +#define MEDIA_STATUS_SQE_STATISTICS_ENABLE BIT_3 +#define MEDIA_STATUS_CARRIER_SENSE BIT_5 +#define MEDIA_STATUS_JABBER_GUARD_ENABLE BIT_6 +#define MEDIA_STATUS_LINK_BEAT_ENABLE BIT_7 +#define MEDIA_STATUS_LINK_DETECT BIT_11 +#define MEDIA_STATUS_TX_IN_PROGRESS BIT_12 +#define MEDIA_STATUS_DC_CONVERTER_ENABLED BIT_14 +#define BAD_SSD_REGISTER 0xC +#define UPPER_BYTES_OK_REGISTER 0xD +// +// Window 5 registers. +// +#define RX_FILTER_REGISTER 0x8 +#define INTERRUPT_ENABLE_REGISTER 0xA +#define INDICATION_ENABLE_REGISTER 0xC +// +// Window 6 registers. +// +#define CARRIER_LOST_REGISTER 0x0 +#define SQE_ERRORS_REGISTER 0x1 +#define MULTIPLE_COLLISIONS_REGISTER 0x2 +#define SINGLE_COLLISIONS_REGISTER 0x3 +#define LATE_COLLISIONS_REGISTER 0x4 +#define RX_OVERRUNS_REGISTER 0x5 +#define FRAMES_TRANSMITTED_OK_REGISTER 0x6 +#define FRAMES_RECEIVED_OK_REGISTER 0x7 +#define FRAMES_DEFERRED_REGISTER 0x8 +#define UPPER_FRAMES_OK_REGISTER 0x9 +#define BYTES_RECEIVED_OK_REGISTER 0xA +#define BYTES_TRANSMITTED_OK_REGISTER 0xC +// +// Window 7 registers. +// +#define TIMER_REGISTER 0x1A +#define TX_STATUS_REGISTER 0x1B +#define TX_STATUS_MAXIMUM_COLLISION BIT_3 +#define TX_STATUS_HWERROR BIT_4 +#define TX_STATUS_JABBER BIT_5 +#define TX_STATUS_INTERRUPT_REQUESTED BIT_6 +#define TX_STATUS_COMPLETE BIT_7 +#define INT_STATUS_AUTO_REGISTER 0x1E +#define DMA_CONTROL_REGISTER 0x20 +#define DMA_CONTROL_DOWN_STALLED BIT_2 +#define DMA_CONTROL_UP_COMPLETE BIT_3 +#define DMA_CONTROL_DOWN_COMPLETE BIT_4 + +#define DMA_CONTROL_ARM_COUNTDOWN BIT_6 +#define DMA_CONTROL_DOWN_IN_PROGRESS BIT_7 +#define DMA_CONTROL_COUNTER_SPEED BIT_8 +#define DMA_CONTROL_COUNTDOWN_MODE BIT_9 +#define DMA_CONTROL_DOWN_SEQ_DISABLE BIT_17 +#define DMA_CONTROL_DEFEAT_MWI BIT_20 +#define DMA_CONTROL_DEFEAT_MRL BIT_21 +#define DMA_CONTROL_UPOVERDISC_DISABLE BIT_22 +#define DMA_CONTROL_TARGET_ABORT BIT_30 +#define DMA_CONTROL_MASTER_ABORT BIT_31 + +#define DOWN_LIST_POINTER_REGISTER 0x24 +#define DOWN_POLL_REGISTER 0x2D +#define UP_PACKET_STATUS_REGISTER 0x30 +#define UP_PACKET_STATUS_ERROR BIT_14 +#define UP_PACKET_STATUS_COMPLETE BIT_15 +#define UP_PACKET_STATUS_OVERRUN BIT_16 +#define UP_PACKET_STATUS_RUNT_FRAME BIT_17 +#define UP_PACKET_STATUS_ALIGNMENT_ERROR BIT_18 +#define UP_PACKET_STATUS_CRC_ERROR BIT_19 +#define UP_PACKET_STATUS_OVERSIZE_FRAME BIT_20 +#define UP_PACKET_STATUS_DRIBBLE_BITS BIT_23 +#define UP_PACKET_STATUS_OVERFLOW BIT_24 +#define UP_PACKET_STATUS_IP_CHECKSUM_ERROR BIT_25 +#define UP_PACKET_STATUS_TCP_CHECKSUM_ERROR BIT_26 +#define UP_PACKET_STATUS_UDP_CHECKSUM_ERROR BIT_27 +#define UP_PACKET_STATUS_IMPLIED_BUFFER_ENABLE BIT_28 +#define UP_PACKET_STATUS_IP_CHECKSUM_CHECKED BIT_29 +#define UP_PACKET_STATUS_TCP_CHECKSUM_CHECKED BIT_30 +#define UP_PACKET_STATUS_UDP_CHECKSUM_CHECKED BIT_31 +#define UP_PACKET_STATUS_ERROR_MASK 0x1F0000 +#define FREE_TIMER_REGISTER 0x34 +#define COUNTDOWN_REGISTER 0x36 +#define UP_LIST_POINTER_REGISTER 0x38 +#define UP_POLL_REGISTER 0x3D +#define REAL_TIME_COUNTER_REGISTER 0x40 +#define CONFIG_ADDRESS_REGISTER 0x44 +#define CONFIG_DATA_REGISTER 0x48 +#define DEBUG_DATA_REGISTER 0x70 +#define DEBUG_CONTROL_REGISTER 0x74 +// ------------------ Commands --------------------------------- +// +// Global reset command. +// + +#define COMMAND_GLOBAL_RESET (0x0 << 0xB) +#define GLOBAL_RESET_MASK_TP_AUI_RESET BIT_0 +#define GLOBAL_RESET_MASK_ENDEC_RESET BIT_1 +#define GLOBAL_RESET_MASK_NETWORK_RESET BIT_2 +#define GLOBAL_RESET_MASK_FIFO_RESET BIT_3 +#define GLOBAL_RESET_MASK_AISM_RESET BIT_4 +#define GLOBAL_RESET_MASK_HOST_RESET BIT_5 +#define GLOBAL_RESET_MASK_SMB_RESET BIT_6 +#define GLOBAL_RESET_MASK_VCO_RESET BIT_7 +#define GLOBAL_RESET_MASK_UP_DOWN_RESET BIT_8 + +#define COMMAND_SELECT_REGISTER_WINDOW (0x1 << 0xB) +#define COMMAND_ENABLE_DC_CONVERTER (0x2 << 0xB) +#define COMMAND_RX_DISABLE (0x3 << 0xB) +#define COMMAND_RX_ENABLE (0x4 << 0xB) +// +// Receiver reset command. +// +#define COMMAND_RX_RESET (0x5 << 0xB) +#define RX_RESET_MASK_TP_AUI_RESET BIT_0 +#define RX_RESET_MASK_ENDEC_RESET BIT_1 +#define RX_RESET_MASK_NETWORK_RESET BIT_2 +#define RX_RESET_MASK_FIFO_RESET BIT_3 +#define RX_RESET_MASK_UP_RESET BIT_8 + +#define COMMAND_UP_STALL ((0x6 << 0xB) | 0x0) +#define COMMAND_UP_UNSTALL ((0x6 << 0xB) | 0x1) +#define COMMAND_DOWN_STALL ((0x6 << 0xB) | 0x2) +#define COMMAND_DOWN_UNSTALL ((0x6 << 0xB) | 0x3) +#define COMMAND_TX_DONE (0x7 << 0xB) +#define COMMAND_RX_DISCARD (0x8 << 0xB) +#define COMMAND_TX_ENABLE (0x9 << 0xB) +#define COMMAND_TX_DISABLE (0xA << 0xB) +// +// Transmitter reset command. +// +#define COMMAND_TX_RESET (0xB << 0xB) +#define TX_RESET_MASK_TP_AUI_RESET BIT_0 +#define TX_RESET_MASK_ENDEC_RESET BIT_1 +#define TX_RESET_MASK_NETWORK_RESET BIT_2 +#define TX_RESET_MASK_FIFO_RESET BIT_3 +#define TX_RESET_MASK_DOWN_RESET BIT_8 +#define COMMAND_REQUEST_INTERRUPT (0xC << 0xB) +// +// Interrupt acknowledge command. +// + +#define COMMAND_ACKNOWLEDGE_INTERRUPT (0xD << 0xB) +#define ACKNOWLEDGE_INTERRUPT_LATCH BIT_0 +#define ACKNOWLEDGE_LINK_EVENT BIT_1 +#define ACKNOWLEDGE_INTERRUPT_REQUESTED BIT_6 +#define ACKNOWLEDGE_DOWN_COMPLETE BIT_9 +#define ACKNOWLEDGE_UP_COMPLETE BIT_10 +#define ACKNOWLEDGE_ALL_INTERRUPT 0x7FF +#define COMMAND_SET_INTERRUPT_ENABLE (0xE << 0xB) +#define DISABLE_ALL_INTERRUPT 0x0 +#define ENABLE_ALL_INTERRUPT 0x6EE +#define COMMAND_SET_INDICATION_ENABLE (0xF << 0xB) +// +// Receive filter command. +// +#define COMMAND_SET_RX_FILTER (0x10 << 0xB) +#define RX_FILTER_INDIVIDUAL BIT_0 +#define RX_FILTER_ALL_MULTICAST BIT_1 +#define RX_FILTER_BROADCAST BIT_2 +#define RX_FILTER_PROMISCUOUS BIT_3 +#define RX_FILTER_MULTICAST_HASH BIT_4 +#define COMMAND_TX_AGAIN (0x13 << 0xB) +#define COMMAND_STATISTICS_ENABLE (0x15 << 0xB) +#define COMMAND_STATISTICS_DISABLE (0x16 << 0xB) +#define COMMAND_DISABLE_DC_CONVERTER (0x17 << 0xB) +#define COMMAND_SET_HASH_FILTER_BIT (0x19 << 0xB) +#define COMMAND_TX_FIFO_BISECT (0x1B << 0xB) +// +// ------------------ Adapter limits --------------------------- +// +#define TRANSMIT_FIFO_SIZE 0x800 +#define RECEIVE_FIFO_SIZE 0x800 +// +// Ethernet limits. +// +#define ETHERNET_MAXIMUM_FRAME_SIZE 1514 +#define ETHERNET_MINIMUM_FRAME_SIZE 60 +#define ETHERNET_ADDRESS_SIZE 6 +#define ETHERNET_HEADER_SIZE 14 +// +// Flow Control Address that gets put into the hash filter for +// flow control enable +// +#define NIC_FLOW_CONTROL_ADDRESS_0 0x01 +#define NIC_FLOW_CONTROL_ADDRESS_1 0x80 +#define NIC_FLOW_CONTROL_ADDRESS_2 0xC2 +#define NIC_FLOW_CONTROL_ADDRESS_3 0x00 +#define NIC_FLOW_CONTROL_ADDRESS_4 0x00 +#define NIC_FLOW_CONTROL_ADDRESS_5 0x01 +// +// DPD Frame Start header bit definitions. +// +#define FSH_CRC_APPEND_DISABLE BIT_13 +#define FSH_TX_INDICATE BIT_15 +#define FSH_DOWN_COMPLETE BIT_16 +#define FSH_LAST_KEEP_ALIVE_PACKET BIT_24 +#define FSH_ADD_IP_CHECKSUM BIT_25 +#define FSH_ADD_TCP_CHECKSUM BIT_26 +#define FSH_ADD_UDP_CHECKSUM BIT_27 +#define FSH_ROUND_UP_DEFEAT BIT_28 +#define FSH_DPD_EMPTY BIT_29 +#define FSH_DOWN_INDICATE BIT_31 +#define MAXIMUM_SCATTER_GATHER_LIST 0x10 +// +// Scatter Gather entry defintion. +// +typedef struct _SCATTER_GATHER_ENTRY { + + ULONG Address; + ULONG Count; + +} SCATTER_GATHER_ENTRY, *PSCATTER_GATHER_ENTRY; + + +typedef struct _DPD_LIST_ENTRY { + ULONG DownNextPointer; + ULONG FrameStartHeader; + SCATTER_GATHER_ENTRY SGList[MAXIMUM_SCATTER_GATHER_LIST]; + struct _DPD_LIST_ENTRY *Next; + struct _DPD_LIST_ENTRY *Previous; + ULONG DPDPhysicalAddress; + PSKB SocketBuffer; + ULONG PacketLength; + +} DPD_LIST_ENTRY, *PDPD_LIST_ENTRY; + + +typedef struct _UPD_LIST_ENTRY { + ULONG UpNextPointer; + ULONG UpPacketStatus; + SCATTER_GATHER_ENTRY SGList[1]; + struct _UPD_LIST_ENTRY *Next; + struct _UPD_LIST_ENTRY *Previous; + ULONG UPDPhysicalAddress; + PUCHAR RxBufferVirtual; + PSKB SocketBuffer; +} UPD_LIST_ENTRY, *PUPD_LIST_ENTRY; +// +// Connector Type +// +typedef enum _CONNECTOR_TYPE { + + CONNECTOR_10BASET = 0, + CONNECTOR_10AUI = 1, + CONNECTOR_10BASE2 = 3, + CONNECTOR_100BASETX = 4, + CONNECTOR_100BASEFX = 5, + CONNECTOR_MII = 6, + CONNECTOR_AUTONEGOTIATION = 8, + CONNECTOR_EXTERNAL_MII = 9, + CONNECTOR_UNKNOWN = 0xFF + +} CONNECTOR_TYPE, *PCONNECTOR_TYPE; + +typedef enum _LINK_STATE { + + LINK_UP = 0, // Link established + LINK_DOWN = 1, // Link lost + LINK_DOWN_AT_INIT = 2 // Link lost and needs notification to NDIS + +} LINK_STATE; + +typedef struct _NIC_PCI_INFORMATION { + INT InterruptVector; + ULONG IoBaseAddress; + +} NIC_PCI_INFORMATION; + + +typedef struct _NIC_HARDWARE_INFORMATION { + + UCHAR CacheLineSize; + UCHAR RevisionId; + UCHAR Status; + #define HARDWARE_STATUS_WORKING 0x0 + #define HARDWARE_STATUS_HUNG 0x1 + #define HARDWARE_STATUS_FAILURE 0x2 + + USHORT XcvrType; + USHORT DeviceId; + ULONG BitsInHashFilter; + ULONG LinkSpeed; + ULONG UpdateInterval; + + CONNECTOR_TYPE Connector; + CONNECTOR_TYPE ConfigConnector; + + BOOLEAN HurricaneEarlyRevision; + UCHAR FeatureSet; + #define MOTHERBOARD_FEATURE_SET 0x0 + #define LOW_COST_ADAPTER_FEATURE_SET 0x1 + #define STANDARD_ADAPTER_FEATURE_SET 0x2 + #define SERVER_ADAPTER_FEATURE_SET 0x4 + + BOOLEAN OptimizeForThroughput; + BOOLEAN OptimizeForCPU; + BOOLEAN OptimizeNormal; + + BOOLEAN BroadcastErrDone; + BOOLEAN UDPChecksumErrDone; + BOOLEAN FullDuplexEnable; + BOOLEAN DuplexCommandOverride; + + BOOLEAN MWIErrDone; + BOOLEAN FlowControlEnable; + BOOLEAN FlowControlSupported; + BOOLEAN LinkBeatDisable; + + BOOLEAN SupportsPowerManagement; + BOOLEAN WOLConnectorPresent; + BOOLEAN AutoResetToD0; + BOOLEAN DontSleep; + BOOLEAN D3Work; + + BOOLEAN WakeOnMagicPacket; + BOOLEAN WakeOnLinkChange; + + BOOLEAN SQEDisable; + BOOLEAN AutoSelect; + BOOLEAN LightTen; + LINK_STATE LinkState; + // + // TryMII sets these parameters. + // + USHORT MIIReadCommand; + USHORT MIIWriteCommand; + USHORT MIIPhyOui; + USHORT MIIPhyModel; + USHORT MIIPhyUsed; + USHORT MediaOverride; + + USHORT phys; /* MII device addr. - for Becker's diag */ + +} NIC_HARDWARE_INFORMATION , *PNIC_HARDWARE_INFORMATION; +// +// Command line media override values +// +#define MEDIA_NONE 0 +#define MEDIA_10BASE_T 1 +#define MEDIA_10AUI 2 +#define MEDIA_10BASE_2 3 +#define MEDIA_100BASE_TX 4 +#define MEDIA_100BASE_FX 5 +#define MEDIA_10BASE_FL 6 +#define MEDIA_AUTO_SELECT 7 + + +#define MII_PHY_ADDRESS 0x0C00 + +// +//--------------------- MII register definitions -------------------- +// +#define MII_PHY_CONTROL 0 // control reg address +#define MII_PHY_STATUS 1 // status reg address +#define MII_PHY_OUI 2 // most of the OUI bits +#define MII_PHY_MODEL 3 // model/rev bits, and rest of OUI +#define MII_PHY_ANAR 4 // Auto negotiate advertisement reg +#define MII_PHY_ANLPAR 5 // auto negotiate Link Partner +#define MII_PHY_ANER 0x6 +#define MII_PAR 0x19 +#define MII_PCR 0x17 // PCS Config register + +#define MII_PHY_REGISTER_24 24 // Register 24 of the MII +#define MII_PHY_REGISTER_24_PVCIRC 0x01 // Process Variation Circuit bit + +// +//--------------------- Bit definitions: Physical Management -------------------- +// +#define PHY_WRITE 0x0004 // Write to PHY (drive MDIO) +#define PHY_DATA1 0x0002 // MDIO data bit +#define PHY_CLOCK 0x0001 // MII clock signal + +// +//--------------------- Bit definitions: MII Control -------------------- +// +#define MII_CONTROL_RESET 0x8000 // reset bit in control reg +#define MII_CONTROL_100MB 0x2000 // 100Mbit or 10 Mbit flag +#define MII_CONTROL_ENABLE_AUTO 0x1000 // autonegotiate enable +#define MII_CONTROL_ISOLATE 0x0400 // islolate bit +#define MII_CONTROL_START_AUTO 0x0200 // restart autonegotiate +#define MII_CONTROL_FULL_DUPLEX 0x0100 + + +// +//--------------------- Bit definitions: MII Status -------------------- +// +#define MII_STATUS_100MB_MASK 0xE000 // any of these indicate 100 Mbit +#define MII_STATUS_10MB_MASK 0x1800 // either of these indicate 10 Mbit +#define MII_STATUS_AUTO_DONE 0x0020 // auto negotiation complete +#define MII_STATUS_AUTO 0x0008 // auto negotiation is available +#define MII_STATUS_LINK_UP 0x0004 // link status bit +#define MII_STATUS_EXTENDED 0x0001 // extended regs exist +#define MII_STATUS_100T4 0x8000 // capable of 100BT4 +#define MII_STATUS_100TXFD 0x4000 // capable of 100BTX full duplex +#define MII_STATUS_100TX 0x2000 // capable of 100BTX +#define MII_STATUS_10TFD 0x1000 // capable of 10BT full duplex +#define MII_STATUS_10T 0x0800 // capable of 10BT + + +// +//----------- Bit definitions: Auto-Negotiation Link Partner Ability ---------- +// +#define MII_ANLPAR_100T4 0x0200 // support 100BT4 +#define MII_ANLPAR_100TXFD 0x0100 // support 100BTX full duplex +#define MII_ANLPAR_100TX 0x0080 // support 100BTX half duplex +#define MII_ANLPAR_10TFD 0x0040 // support 10BT full duplex +#define MII_ANLPAR_10T 0x0020 // support 10BT half duplex + +// +//----------- Bit definitions: Auto-Negotiation Advertisement ---------- +// +#define MII_ANAR_100T4 0x0200 // support 100BT4 +#define MII_ANAR_100TXFD 0x0100 // support 100BTX full duplex +#define MII_ANAR_100TX 0x0080 // support 100BTX half duplex +#define MII_ANAR_10TFD 0x0040 // support 10BT full duplex +#define MII_ANAR_10T 0x0020 // support 10BT half duplex +#define MII_ANAR_FLOWCONTROL 0x0400 // support Flow Control + +#define MII_ANAR_MEDIA_MASK 0x07E0 // Mask the media selection bits +#define MII_ANAR_MEDIA_100_MASK (MII_ANAR_100TXFD | MII_ANAR_100TX) +#define MII_ANAR_MEDIA_10_MASK (MII_ANAR_10TFD | MII_ANAR_10T) + +#define MII_100TXFD 0x01 +#define MII_100T4 0x02 +#define MII_100TX 0x03 +#define MII_10TFD 0x04 +#define MII_10T 0x05 + +// +//----------- Bit definitions: Auto-Negotiation Expansion ---------- +// +#define MII_ANER_LPANABLE 0x0001 // Link partner autonegotiatable ? +#define MII_ANER_MLF 0x0010 // Multiple Link Fault bit + +// +// MII Transceiver Type store in miiSelect +// +#define MIISELECT_GENERIC 0x0000 +#define MIISELECT_100BT4 0x0001 +#define MIISELECT_10BT 0x0002 +#define MIISELECT_100BTX 0x0003 +#define MIISELECT_10BT_ANE 0x0004 +#define MIISELECT_100BTX_ANE 0x0005 +#define MIITXTYPE_MASK 0x000F + + + + +#define NIC_STATUS ULONG + +#define NIC_STATUS_SUCCESS 0x1 +#define NIC_STATUS_FAILURE 0x2 + +// +// Software limits defined here. +// +#define NIC_DEFAULT_SEND_COUNT 0x40 +#define NIC_DEFAULT_RECEIVE_COUNT 0x40 +#define NIC_MINIMUM_SEND_COUNT 0x2 +#define NIC_MAXIMUM_SEND_COUNT 0x80 +#define NIC_MINIMUM_RECEIVE_COUNT 0x2 +#define NIC_MAXIMUM_RECEIVE_COUNT 0x80 + +#define LINK_SPEED_100 100000000L +#define LINK_SPEED_10 10000000L + +#define ETH_ADDR_SIZE 6 +#define ETH_MULTICAST_BIT 1 + +typedef struct _ETH_ADDR { + UCHAR Addr[ETH_ADDR_SIZE]; +} ETH_ADDR, *PETH_ADDR; + +typedef struct _NIC_RESOURCES { + + ULONG ReceiveCount; + ULONG SendCount; + ULONG SharedMemorySize; + PUCHAR SharedMemoryVirtual; + TIMER Timer; + TASKQ hostErr_task; + ULONG TimerInterval; + ULONG DownPollRate; + +} NIC_RESOURCES, *PNIC_RESOURCES; + +// +// Statistics maintained by the driver. +// + + +typedef struct _NIC_STATISTICS { + + // + // Transmit statistics. + // + ULONG TxFramesOk; + ULONG TxBytesOk; + ULONG TxFramesDeferred; + ULONG TxSingleCollisions; + ULONG TxMultipleCollisions; + ULONG TxLateCollisions; + ULONG TxCarrierLost; + + ULONG TxMaximumCollisions; + ULONG TxSQEErrors; + ULONG TxHWErrors; + ULONG TxJabberError; + ULONG TxUnknownError; + + ULONG TxLastPackets; + ULONG TxLastCollisions; + ULONG TxLastDeferred; + + // + // Receive statistics. + // + ULONG RxFramesOk; + ULONG RxBytesOk; + + ULONG RxOverruns; + ULONG RxBadSSD; + ULONG RxAlignmentError; + ULONG RxBadCRCError; + ULONG RxOversizeError; + + ULONG RxNoBuffer; + + ULONG RxLastPackets; + ULONG UpdateInterval; + + // + // Multicasts statistics + // + ULONG Rx_MulticastPkts; + +} NIC_STATISTICS, *PNIC_STATISTICS; +// +// Memory allocation +// +#define NIC_IO_PORT_REGISTERED 0x00000001 +#define NIC_INTERRUPT_REGISTERED 0x00000002 +#define NIC_SHARED_MEMORY_ALLOCATED 0x00000004 +#define WAIT_TIMER_REGISTERED 0x00000008 +#define NIC_TIMER_REGISTERED 0x00000010 + +#define MAXIMUM_TEST_BUFFERS 1 +// +// This queue maintains the pending packets. +// +typedef struct _PACKET_PENDING_QUEUE { + + PSKB Head; + PSKB Tail; + +} PACKET_PENDING_QUEUE; + +typedef enum _NIC_WAIT_CASES { + + CHECK_UPLOAD_STATUS, + CHECK_DOWNLOAD_STATUS, + CHECK_DC_CONVERTER, + CHECK_PHY_STATUS, + CHECK_TRANSMIT_IN_PROGRESS, + CHECK_DOWNLOAD_SELFDIRECTED, + AUTONEG_TEST_PACKET, + CHECK_DMA_CONTROL, + CHECK_CARRIER_SENSE, + NONE +} NIC_WAIT_CASES; + + +typedef struct _NIC_INFORMATION { + + ULONG IoBaseAddress; + UCHAR DeviceName[8]; + UCHAR PermanentAddress[6]; + UCHAR StationAddress[6]; + ULONG ResourcesReserved; + NIC_PCI_INFORMATION PCI; + + PUPD_LIST_ENTRY HeadUPDVirtual; + PDPD_LIST_ENTRY HeadDPDVirtual; + PDPD_LIST_ENTRY TailDPDVirtual; + + ULONG TestDPDVirtual[MAXIMUM_TEST_BUFFERS]; + ULONG TestDPDPhysical[MAXIMUM_TEST_BUFFERS]; + ULONG TestBufferVirtual[MAXIMUM_TEST_BUFFERS]; + ULONG TestBufferPhysical[MAXIMUM_TEST_BUFFERS]; + + NIC_RESOURCES Resources; + NIC_STATISTICS Statistics; + ENET_STATISTICS EnetStatistics; + NIC_HARDWARE_INFORMATION Hardware; + ULONG BytesInDPDQueue; + PDEVICE NextDevice; + PDEVICE Device; + BOOLEAN InTimer; + BOOLEAN DelayStart; + INT Index; + PACKET_PENDING_QUEUE PendingQueue; + ULONG TxPendingQueueCount; +#if LINUX_VERSION_CODE >= 0x20200 + spinlock_t SpinLock_m; + spinlock_t SpinLock_misc; + spinlock_t SpinLock_send; + spinlock_t SpinLock_int; +#endif + BOOLEAN DPDRingFull; + BOOLEAN DeviceGivenByOS; + + ULONG keepForGlobalReset; + NIC_WAIT_CASES WaitCases; + +} NIC_INFORMATION, *PNIC_INFORMATION; + + + + + + + +#ifndef LOBYTE +#define LOBYTE(_w) ((UCHAR)((_w) & 0xFF)) +#endif + +#ifndef HIBYTE +#define HIBYTE(_w) (((USHORT)(_w)) >> 8) +#endif + + +#ifndef LOWORD +#define LOWORD(_d) ((USHORT)((_d) & 0xFFFF)) +#endif + +#ifndef HIWORD +#define HIWORD(_d) (((ULONG)(_d)) >> 16) +#endif + + +#define NIC_READ_PORT_UCHAR(pAdapter, Register) \ + inb(pAdapter->IoBaseAddress + Register) + +#define NIC_READ_PORT_USHORT(pAdapter, Register) \ + inw(pAdapter->IoBaseAddress + Register) + +#define NIC_READ_PORT_ULONG(pAdapter, Register) \ + inl(pAdapter->IoBaseAddress + Register) + +#define NIC_WRITE_PORT_UCHAR(pAdapter, Register, Value) \ + outb(Value, pAdapter->IoBaseAddress + Register) + +#define NIC_WRITE_PORT_USHORT(pAdapter, Register, Value) \ + outw(Value, pAdapter->IoBaseAddress + Register) + +#define NIC_WRITE_PORT_ULONG(pAdapter, Register, Value) \ + outl(Value, pAdapter->IoBaseAddress + Register) + +#define NIC_COMMAND(pAdapter, Command) \ + NIC_WRITE_PORT_USHORT(pAdapter, INTSTATUS_COMMAND_REGISTER, Command) + +#define NIC_MASK_ALL_INTERRUPT(pAdapter) { \ + NIC_COMMAND( \ + pAdapter, \ + COMMAND_SET_INTERRUPT_ENABLE | DISABLE_ALL_INTERRUPT \ + ); \ + NIC_READ_PORT_USHORT(pAdapter, INTSTATUS_COMMAND_REGISTER); \ +} + +#define NIC_UNMASK_ALL_INTERRUPT(pAdapter){ \ + NIC_COMMAND( \ + pAdapter, \ + COMMAND_SET_INTERRUPT_ENABLE | ENABLE_ALL_INTERRUPT \ + ); \ + NIC_READ_PORT_USHORT(pAdapter, INTSTATUS_COMMAND_REGISTER); \ +} + + +#define NIC_ACKNOWLEDGE_ALL_INTERRUPT(pAdapter) \ + NIC_COMMAND( \ + pAdapter, \ + COMMAND_ACKNOWLEDGE_INTERRUPT | ACKNOWLEDGE_ALL_INTERRUPT \ + ) + +#define NIC_ENABLE_ALL_INTERRUPT_INDICATION(pAdapter) \ + NIC_COMMAND( \ + pAdapter, \ + COMMAND_SET_INDICATION_ENABLE | ENABLE_ALL_INTERRUPT \ + ) + +#define NIC_DISABLE_ALL_INTERRUPT_INDICATION(pAdapter) \ + NIC_COMMAND( \ + pAdapter, \ + COMMAND_SET_INDICATION_ENABLE | DISABLE_ALL_INTERRUPT \ + ) + +#define COMPARE_MACS(pAddr1, pAddr2) \ + ( *((PULONG)((PUCHAR)(pAddr1)+2)) == *((PULONG)((PUCHAR)(pAddr2)+2)) && \ + *((PULONG)(pAddr1)) == *((PULONG)(pAddr2))) + +#define NIC_DELAY(A) udelay(A) + +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define MAX(A, B) ((A) > (B) ? (A) : (B)) + + +#ifdef DEBUG +extern ULONG debug; +#endif + +#define DEBUG_INITIALIZE 0x00000001 +#define DEBUG_FUNCTION 0x00000002 +#define DEBUG_IOCTL 0x00000004 +#define DEBUG_GET_STATISTICS 0x00000008 +#define DEBUG_SEND 0x00000010 +#define DEBUG_RECEIVE 0x00000020 +#define DEBUG_INTERRUPT 0x00000040 +#define DEBUG_ERROR 0x80000000 + +#ifdef DEBUG + +#define DBGPRINT_INITIALIZE(A) if (debug & DEBUG_INITIALIZE) printk A +#define DBGPRINT_FUNCTION(A) if (debug & DEBUG_FUNCTION) printk A +#define DBGPRINT_SEND(A) if (debug & DEBUG_SEND) printk A +#define DBGPRINT_RECEIVE(A) if (debug & DEBUG_RECEIVE) printk A +#define DBGPRINT_INTERRUPT(A) if (debug & DEBUG_INTERRUPT) printk A +#define DBGPRINT_GET_STATISTICS(A) \ + if (debug & DEBUG_GET_STATISTICS) printk A +#define DBGPRINT_IOCTL(A) if (debug & DEBUG_IOCTL) printk A +#define DBGPRINT_ERROR(A) printk A +#define DBGPRINT_INIT(A) printk A + +#else + +#define DBGPRINT_INITIALIZE(A) +#define DBGPRINT_FUNCTION(A) +#define DBGPRINT_SEND(A) +#define DBGPRINT_RECEIVE(A) +#define DBGPRINT_INTERRUPT(A) +#define DBGPRINT_GET_STATISTICS(A) +#define DBGPRINT_QUERY(A) +#define DBGPRINT_SET(A) +#define DBGPRINT_IOCTL(A) +#define DBGPRINT_ERROR(A) +#define DBGPRINT_INIT(A) + +#endif + +#define LOG_LABEL(A, B) +#define ASSERT(A) + + +extern TIMER WaitTimer; +extern BOOLEAN InWaitTimer; +extern BOOLEAN DCConverterEnabledState_g; +extern ULONG TimeOutCount; +extern USHORT MediaStatus_g; +extern BOOLEAN PhyResponding_g; +extern USHORT PhyStatus_g; +extern ULONG DownListPointer_g; +extern ULONG UpListPointer_g; +extern ULONG portValue_g; +extern ULONG dmaControl_g; + +extern PCHAR ProductName; +extern ULONG tc90x_SendCount[]; +extern ULONG tc90x_ReceiveCount[]; +extern ULONG tc90x_Index; +extern UCHAR BroadcastAddr[]; + +#ifdef DEBUG +extern ULONG debug; +#endif + +extern INT switchdelay[]; +extern INT media_select[]; +extern INT full_duplex[]; +extern INT downpoll[]; +extern INT flowcontrol[]; +// +//-------------------------- NIC.C Definitions -------------------------- +// + +NIC_STATUS +tc90xbc_ScanDevices( + IN PDEVICE Device + ); + +NIC_STATUS +tc90x_FillDeviceStructure( + IN PNIC_INFORMATION Adapter + ); + +INT +NICOpen( + IN PDEVICE Device + ); + +INT +NICClose( + IN PDEVICE Device + ); + +// +//-------------------------- INIT.C Definitions -------------------------- +// + +VOID +tc90x_FreeAdapterResources( + IN PNIC_INFORMATION Adapter + ); + +NIC_STATUS +tc90x_RegisterAdapter( + IN PNIC_INFORMATION Adapter + ); + +NIC_STATUS +tc90x_GetAdapterProperties( + IN PNIC_INFORMATION Adapter + ); + +NIC_STATUS +tc90x_BasicInitializeAdapter( + IN PNIC_INFORMATION Adapter + ); + +NIC_STATUS +tc90x_AllocateSharedMemory( + IN PNIC_INFORMATION Adapter + ); + +NIC_STATUS +tc90x_TestAdapter( + IN PNIC_INFORMATION Adapter + ); + +NIC_STATUS +tc90x_StartAdapter( + IN PNIC_INFORMATION Adapter + ); + +VOID +ReStartAdapter( + //IN PNIC_INFORMATION Adapter + PVOID Adapter + ); + +//-------------------------- SEND.C Definitions -------------------------- +// + +INT +NICSendPacket( + IN PSKB SocketBuffer, + IN PDEVICE Device + ); + +VOID +tc90x_TxCompleteEvent( + IN PNIC_INFORMATION Adapter + ); + +NIC_STATUS +tc90x_ResetAndEnableTransmitter( + IN PNIC_INFORMATION Adapter + ); + + +VOID +tc90x_CleanupSendLogic( + IN PDEVICE Device + ); + + + +// +//-------------------------- REQUEST.C Definitions -------------------------- +// + + +INT +NICIoctl( + IN PDEVICE Device, + IN PIFREQ Request, + IN INT command + ); + +VOID +NICSetReceiveMode( + IN PDEVICE Device + ); + +PENET_STATISTICS +NICGetStatistics( + IN PDEVICE Device + ); +VOID +NICSetMulticastList( + IN PDEVICE Device, + IN INT NumberOfAddresses, + PVOID AddressList + ); + + +USHORT +tc90x_HashAddress( + IN PUCHAR Address + ); + + +NIC_STATUS +tc90x_SetMulticastAddresses( + IN PNIC_INFORMATION Adapter + ); + + +VOID +tc90x_InitializeHashFilter( + IN PNIC_INFORMATION Adapter + ); + + +// +//-------------------------- WORK.C Definitions ------------------- +// + +VOID +tc90x_FlowControl( + IN PNIC_INFORMATION Adapter + ); + + +VOID +tc90x_HurricaneEarlyRevision( + IN PNIC_INFORMATION Adapter + ); + + +NIC_STATUS +tc90x_SoftwareWork( + IN PNIC_INFORMATION Adapter + ); + + +/*NIC_STATUS +RxResetAndWork( + IN PNIC_INFORMATION Adapter + );*/ +// +//-------------------------- AUTOSELECT.C Definitions ------------------- +// + +VOID +tc90x_MainAutoSelectionRoutine( + IN PNIC_INFORMATION Adapter, + IN USHORT Options + ); + +BOOLEAN +tc90x_CheckDCConverter ( + IN PNIC_INFORMATION Adapter, + IN BOOLEAN EnabledState + ); + + +VOID +tc90x_SetupConnector( + IN PNIC_INFORMATION Adapter, + IN CONNECTOR_TYPE NewConnector, + OUT PCONNECTOR_TYPE OldConnector + ) ; + + +BOOLEAN +tc90x_TryMII( + IN PNIC_INFORMATION Adapter, + IN USHORT MediaOptions + ); + +BOOLEAN +tc90x_TryLinkBeat( + IN PNIC_INFORMATION Adapter, + IN CONNECTOR_TYPE NewConnector + ); + + +BOOLEAN +DownloadSelfDirected( + IN PNIC_INFORMATION Adapter + ) ; + +BOOLEAN +CheckTransmitInProgress( + IN PNIC_INFORMATION Adapter + ); + + +BOOLEAN +TestPacket( + IN PNIC_INFORMATION Adapter + ); + +BOOLEAN +GetLinkSpeed( + IN PNIC_INFORMATION Adapter, + OUT PBOOLEAN handles100Mbitptr + ) ; + +// +// --------------------- EEPROM.C definitions ------------------- +// + +NIC_STATUS +tc90x_CheckIfEEPROMBusy( + IN PNIC_INFORMATION Adapter + ); + +NIC_STATUS +tc90x_ReadEEPROM( + IN PNIC_INFORMATION Adapter, + IN USHORT EEPROMAddress, + OUT PUSHORT Contents + ); + +NIC_STATUS +tc90x_WriteEEPROM( + IN PNIC_INFORMATION Adapter, + IN USHORT EEPROMAddress, + IN USHORT Data + ); + +USHORT +tc90x_CalculateEEPROMChecksum1( + IN PNIC_INFORMATION Adapter + ) ; + + +// +// --------------------- ISR.C definitions ------------------- +// + +VOID +NICInterrupt IRQ( + INT Irq, + PVOID DeviceId, + PTREGS Registers + ); + +VOID +tc90x_HostErrorEvent( + IN PNIC_INFORMATION Adapter + ); + +VOID +tc90x_CountDownTimerEvent( + IN PNIC_INFORMATION Adapter + ); + + +VOID +tc90x_UpdateStatisticsEvent( + IN PNIC_INFORMATION Adapter + ); + + + + +// +// --------------------- RECEIVE.C definitions ------------------- +// + +VOID +tc90x_UpCompleteEvent( + IN PNIC_INFORMATION Adapter + ); + +NIC_STATUS +tc90x_ResetAndEnableReceiver( + IN PNIC_INFORMATION Adapter + ); + + +// +// --------------------- TIMER.C definitions ------------------- +// + +VOID +NICTimer( + IN ULONG Data + ); + +VOID +WaitTimerHandler( + IN ULONG Data + ); +// +// --------------------- CMDLINE.C definitions ------------------- +// + +NIC_STATUS +tc90x_ReadCommandLineChanges( + IN PNIC_INFORMATION Adapter + ); + +// +// --------------------- XCVR.C definitions ------------------- +// + +NIC_STATUS +tc90x_SetupMedia( + IN PDEVICE Device + ); + +VOID +ProcessMediaOverrides( + IN PNIC_INFORMATION Adapter, + IN USHORT OptionAvailable + ); + + +VOID +tc90x_TickMediaHandler( + IN PNIC_INFORMATION Adapter + ); + +VOID +CheckTPLinkState( + IN PNIC_INFORMATION Adapter + ); + +VOID +CheckFXLinkState( + IN PNIC_INFORMATION Adapter + ); + +NIC_STATUS +tc90x_SetupNewDuplex( + IN PNIC_INFORMATION Adapter + ); + +VOID +tc90x_SetupNewSpeed( + IN PNIC_INFORMATION Adapter + ); + + +VOID +IndicateToOSLinkStateChange( + IN PNIC_INFORMATION Adapter + ); + +/**************************** MIIPHY.C *********************************/ + +BOOLEAN +FindMIIPhy( + IN PNIC_INFORMATION Adapter + ) ; + +VOID +SendMIIPhyPreamble( + IN PNIC_INFORMATION Adapter + ) ; + +VOID +WriteMIIPhy( + IN PNIC_INFORMATION Adapter, + IN USHORT RegAddr, + IN USHORT Output + ); + +BOOLEAN +ReadMIIPhy( + IN PNIC_INFORMATION Adapter, + IN USHORT RegisterAddress, + OUT PUSHORT pInput + ) ; + + +BOOLEAN +MIIMediaOverride( + IN PNIC_INFORMATION Adapter, + IN USHORT PhyModes, + OUT PUSHORT MiiType + ); + +BOOLEAN +ProgramMII( + IN PNIC_INFORMATION Adapter, + IN CONNECTOR_TYPE NewConnector + ) ; + +BOOLEAN +ConfigureMII( + IN PNIC_INFORMATION Adapter, + IN USHORT MediaOptions + ); + +BOOLEAN +CheckMIIConfiguration( + IN PNIC_INFORMATION Adapter, + IN USHORT MediaOptions + ); + +VOID +CheckMIIAutoNegotiationStatus( + IN PNIC_INFORMATION Adapter + ); + + +/*++ + +Routine: + + NIC_COMMAND_WAIT. + +Description: + + This routine issues a command and spins for the completion. + +Arguments: + + MiniportAdapterContext - Pointer to the adapter structure. + Command - Command to be issued. + +Return Value: + + NIC_STATUS_SUCCESS if hardware executes the command. + NIC_STATUS_FAILURE if hardware does not respond. + +--*/ + +__inline +static +NIC_STATUS +NIC_COMMAND_WAIT( + IN PNIC_INFORMATION Adapter, + IN USHORT Command + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + ULONG count; + USHORT value; + + NIC_WRITE_PORT_USHORT( + pAdapter, + INTSTATUS_COMMAND_REGISTER, + Command); + + count = jiffies + HZ; + do { + value = NIC_READ_PORT_USHORT( + pAdapter, + INTSTATUS_COMMAND_REGISTER); + NIC_DELAY(10); + } while ( (value & INTSTATUS_COMMAND_IN_PROGRESS) && + (count > jiffies) ); + + if (count < jiffies) { + DBGPRINT_ERROR(("NIC_COMMAND_WAIT: timeout\n")); + return NIC_STATUS_FAILURE; + } + return NIC_STATUS_SUCCESS; +} + +/*++ + +Routine Name: + + tc90x_SetCountDownTimer. + +Routine Description: + + This routine sets the countdown timer on the hardware. + +Arguments: + + MiniportAdapterContext - Pointer to the adapter structure. + +Return Value: + + None. + +--*/ + +__inline +static +VOID +tc90x_SetCountDownTimer( + IN PNIC_INFORMATION Adapter + ) +{ + PNIC_INFORMATION pAdapter = Adapter; + ULONG countDownValue; + + countDownValue = pAdapter->BytesInDPDQueue /4; + + if (countDownValue < 10) + countDownValue = 10; + + NIC_WRITE_PORT_USHORT( + pAdapter, + COUNTDOWN_REGISTER, + (USHORT)countDownValue + ); + +} + + +/*++ + +Routine Name: + + GetPacketFromPendingQueueAtHead + +Routine Description: + + This routine gets a packet from the pending queue. + +Arguments: + + pAdapter - Pointer to the adapter structure. + +Return Value: + + + SocketBuffer - if there is a socket buffer in the queue. + NULL - if there is no socket buffer in the queue. + +--*/ + +__inline +static +PSKB +GetPacketFromPendingQueueAtHead( + IN PNIC_INFORMATION Adapter + ) +{ + PSKB socketBuffer; + + // + // Get the socket buffer from the queue head. + // + socketBuffer = Adapter->PendingQueue.Head; + + if (socketBuffer) { + + // + // Move the head. + // + Adapter->PendingQueue.Head = socketBuffer->next; + Adapter->TxPendingQueueCount--; + + } + + // + // return the socket buffer pointer. + // + socketBuffer->next = NULL; + + return socketBuffer; + +} + +/*++ + +Routine Name: + + PutPacketInPendingQueueAtHead + +Routine Description: + + This routine puts the packet in the pending queue. + +Arguments: + + Adapter - Pointer to the adapter structure. + SocketBuffer - Socket buffer + +Return Value: + + None. + +--*/ + +__inline +static +VOID +PutPacketInPendingQueueAtHead( + IN PNIC_INFORMATION Adapter, + IN PSKB SocketBuffer + ) +{ + + // + // This packet points to head. + // + SocketBuffer->next = Adapter->PendingQueue.Head; + + if (NULL == Adapter->PendingQueue.Head) { + + // + // Nothing in queue, tail points to this packet. + // + + Adapter->PendingQueue.Tail = SocketBuffer; + + } + + // + // Point head to this packet. + // + + Adapter->PendingQueue.Head = SocketBuffer; + Adapter->TxPendingQueueCount++; + +} + + + +/*++ + +Routine Name: + + PutPacketPendingQueueAtTail. + +Routine Description: + + This routine puts the packet in the pending queue. + +Arguments: + + Adapter - Pointer to the adapter structure. + Packet - Packet pointer. + +Return Value: + + None. + +--*/ + +__inline +static +VOID +PutPacketInPendingQueueAtTail( + IN PNIC_INFORMATION Adapter, + IN PSKB SocketBuffer + ) +{ + + SocketBuffer->next = NULL; + + if (NULL == Adapter->PendingQueue.Head) { + + // + // Head is NULL, point head to this one. + // + + Adapter->PendingQueue.Head = SocketBuffer ; + + } + else { + + // + // Head is valid, add this packet to the tail. + // + Adapter->PendingQueue.Tail->next = SocketBuffer; + + } + + // + // Point tail to this one. + // + + Adapter->PendingQueue.Tail = SocketBuffer; + Adapter->TxPendingQueueCount++; + +} + + +/*++ + +Routine Name: + + SetRxTcpIpChecksumFlagsInPacket. + +Routine Description: + + This routine sets the checksum information. + +Arguments: + + Adapter - Pointer to the adapter structure. + SocketBuffer - Pointer to the socket buffer. + UpPacketStatus - UpPacketStatus given by hardware. + +Return Value: + + None. + +--*/ + +__inline +static +VOID +SetRxTcpIpChecksumOffloadFlagsInSocketBuffer( + IN PNIC_INFORMATION Adapter, + IN PSKB SocketBuffer, + IN ULONG UpPacketStatus + ) + +{ + PNIC_INFORMATION pAdapter = Adapter; + // + // Check if this is IP packet. + // + if (UpPacketStatus & UP_PACKET_STATUS_IP_CHECKSUM_CHECKED) { + + if (UpPacketStatus & UP_PACKET_STATUS_IP_CHECKSUM_ERROR) { + DBGPRINT_ERROR(( + KERN_CRIT "IP checksum error\n")); + SocketBuffer->ip_summed = CHECKSUM_NONE; + return; + } + // + // Check if packet is TCP packet. + // + if (UpPacketStatus & UP_PACKET_STATUS_TCP_CHECKSUM_CHECKED) { + // + // Check if TCP checksum has been offloaded to us. + // + if (UpPacketStatus & + UP_PACKET_STATUS_TCP_CHECKSUM_ERROR) { + DBGPRINT_ERROR(( + KERN_CRIT "TCP Checksum error\n")); + SocketBuffer->ip_summed = CHECKSUM_NONE; + return; + } + } + // + // Check if this is UDP packet. + // + if (UpPacketStatus & UP_PACKET_STATUS_UDP_CHECKSUM_CHECKED) { + if (TRUE == pAdapter->Hardware.UDPChecksumErrDone) { + if (UpPacketStatus & + UP_PACKET_STATUS_UDP_CHECKSUM_ERROR) { + DBGPRINT_ERROR(( + KERN_CRIT "UDP Error")); + SocketBuffer->ip_summed = CHECKSUM_NONE; + return; + } + else { + SocketBuffer->ip_summed = CHECKSUM_NONE; + return; + } + } + } + } + SocketBuffer->ip_summed = CHECKSUM_UNNECESSARY; + return; +} + --- /dev/null Tue May 5 13:32:27 1998 +++ linux/Documentation/networking/3c90x.txt Mon Sep 27 04:54:31 1999 @@ -0,0 +1,345 @@ +File: README + + 3Com (R) Corporation + EtherLink 10/100 PCI (3C90x) Network Interface Card + Driver for Linux + + +This file describes the 3C90x network interface card (NIC) driver for Linux. +It includes the following topics: + + * Supported NICs + * Supported Platforms and Distributions + * Loading the Driver Module + * Building the Driver + * Configuring the Driver + * Support Information + * Additional Information + + CAUTION: 3Com has not yet verified functionality of the 3C90x + driver on platforms, architectures, or configurations other than + those specified in this file. Although it may operate correctly + on other configurations, users are encouraged to exercise caution + when using the driver with other configurations. + + DISCLAIMER: 3Com makes no warranties or guarantees, expressly, + implied, or otherwise. Usage of this driver is solely at the + risk of the user. + + +* Supported NICs +---------------- + +The 3C90x NIC driver for Linux supports the following EtherLink NICs: + + EtherLink 10/100 PCI NICs + 3C905C Family and 3C920 ASICs EtherLink 10/100 PCI including + the -TX and -TX-M + 3C905B Family and 3C918 ASICs EtherLink 10/100 PCI including + the -TX -TX-M and -TX-NM + 3C905B-COMBO EtherLink 10/100 PCI COMBO + 3C905B-T4 EtherLink 10/100 PCI T4 + + EtherLink Server 10/100 PCI NICs + 3C980C-TX EtherLink Server 10/100 PCI + 3C980B-TX EtherLink Server 10/100 PCI + 3C980-TX EtherLink Server 10/100 PCI + + EtherLink 100 PCI NIC + 3C905B-FX EtherLink 100 PCI Fiber + + EtherLink 10 PCI NICs + 3C900B-TPO EtherLink 10 PCI TPO + 3C900B-TPC EtherLink 10 PCI TPC + 3C900B-COMBO EtherLink 10 PCI COMBO + 3C900B-FL EtherLink 10 PCI Fiber + + The 3c590/3c592/3c595/3c597 and 3C905 Families are supported + with the 3c59x driver from Donald Becker. + +* Supported Platforms and Distributions +--------------------------------------- + + The 3C90x driver for Linux has been tested with the following + commercial Linux distributions, using the i386 architecture: + + RedHat 5.2 + Kernel version 2.0.36 (i386) + + RedHat 6.0 + Kernel version 2.2.5-15 (i386) + Kernel version 2.2.5-22 (i386) + + NOTE: The driver has been tested on both SMP and UP machines. + + +* Loading the Driver Module +--------------------------- + + To load the 3C90x driver module on a RedHat 5.2 or 6.0 system, + place an "alias" in the file named /etc/conf.modules. + + The alias instructs the kernel module loader to use the + 3C90x driver module for a specific Linux Ethernet interface. + When the system attempts to use this Ethernet interface, it + causes the kernel module loader to load the 3c90x driver + into the system automatically, and initialize it for use. + + The alias listed below associates the Ethernet device "eth0" with + the 3c90x driver module. If you have one of the EtherLink NICs + that is supported by the driver, place the following line in the + /etc/conf.modules file: + + alias eth0 3c90x + + To enable additional supported EtherLink NICs, create additional + aliases, such as: + + alias eth1 3c90x + alias eth2 3c90x + + + Using 3c90x and 3c59x drivers + ------------------------------ + The 3C90x driver for Linux supports the "B" and "C" models of the + EtherLink 3C90x NIC family (see the supported NICs listed above). + + Support for older 3C900 and 3C59x NICs, as well as many current + 3Com NICs, is provided by the 3C59x driver in Linux. + + Because both of these drivers support many of the same models of NICs, + 3Com recommends using the 3C90x driver module for the most recent NICs + (those listed above), and the 3c59x driver for those older NICs + not supported by the 3c90x driver. + + Both the 3C90x and 3C59x driver modules can be used at the same time. + However, to make sure that the 3C90x driver is used with the + appropriate NIC, configure your system to load the 3C90x driver first. + This allows the 3C90x driver to attach itself to all of the NICs that + it supports. Once this is complete, the 3C59x driver can be loaded + and used by your system for any remaining 3Com NICs that are not + supported by the 3C90x driver. + + To accomplish this driver module loading order, it is important to + understand how both the 3C90x and 3C59x drivers scan the PCI slots + for the presence of a suitable NIC, how the kernel module loading + system works, and how the "eth0," "eth1," "eth2," etc. device names + are assigned. + + Both of the 3C90x and 3C59x drivers attempt to locate all of the + PCI network devices they support. If the 3C90x driver first locates + three supported network devices, they will be named eth0, eth1, and + eth2. If, subsequently, the 3C59x driver finds two devices, they + will be named eth3 and eth4. (Note that the ethN devices names are + assigned in a first-found order; this may not be what is requested + in the /etc/conf.modules file.) + + Both drivers scan for devices in PCI slot number order, beginning at + slot 0, and scanning to slot 255. Unfortunately, there is no + standard for how PCI slots are numbered relative to their location + in a PC; each PC vendor determines the numbering order of PCI slots. + This means that the slots in which you have placed your NICs affects + the order in which the NICs are "found" by the driver. The + device named eth0 will be the first device found and successfully + scanned by the driver; eth1 will be the second Ethernet device; + eth2 the third, and so on. + + To ensure that the 3C90x driver module is the first to detect all + supported NICs, place an "alias eth0 3c90x" alias in + /etc/conf.modules. + + Remember: ALL supported devices found by either the 3C90x or 3C59x + driver will be used by that driver. There is currently no way to + control specific NIC-to-driver assignments. + + +* Building the Driver +--------------------- + The 3C90x driver can be built either as a kernel loadable module, or + it can be compiled directly into the kernel. + + Building a loadable module: + --------------------------- + To build the 3c90x driver as a loadable module, compile the 3c90x.c + source file with the following command: + + gcc -c 3c90x.c -O2 -Wall -Wstrict-prototypes -fomit-frame-pointer \ + -fno-strength-reduce -pipe -m486 -malign-loops=2 \ + -malign-jumps=2 -malign-functions=2 -DCPU=486 \ + -DMODULE -D__KERNEL__ + + Additionally, you can modify this compile line by: + + Adding -DMODVERSIONS to the command line if your kernel was built + with symbol versioning (RedHat, etc.) + + Adding -D__SMP__ to the command line for SMP support + + + To install a loadable module, enter the following command: + + insmod 3c90x.o + + + Building the driver into the base kernel: + ---------------------------------------- + To build the 3C90x driver into the base kernel, run the patch utility + to alter the original kernel source to include the 3C90x driver + source, and then re-run the kernel configuration process. + + Note: It is assumed that the user understands how to configure and + build the Linux kernel, a process which is beyond the scope of this + document and not described here. + + Included with the 3C90x distribution tar file are two patch input files + named "patch-2.2.5" and "patch-2.0.36". These are used to patch your + original kernel source. Use the patch file that matches the version + of your kernel source. + + CAUTION: The following process MODIFIES your original kernel source + code. It is highly recommended that you backup your kernel source + code if you are concerned about these modifications. + + To build the 3C90x driver into the base kernel: + + 1. Run the following command to patch your kernel source code + and replace the with the root of your + kernel source code: + + % patch -d -p0 < + + NOTE: is the name of the patch file (patch-2.2.5 or + patch-2.0.36) that is appropriate for your kernel. Both + patch files are included with the driver: + + Use patch-2.2.5 for kernel versions 2.2.5-15 and 2.2.5-22. + Use patch-2.0.36 for kernel version 2.0.36-0.7. + + The root of your kernel source code may be /usr/src/linux. + + 2. After the patch has been applied successfully, reconfigure, + rebuild and reinstall your kernel. + + The patch applied to your kernel source updates the appropriate + files such that the "make config" process understands the new + 3c90x driver. Be sure to configure 3Com networking, and specifically + support for the 3c90x driver. + + + +* Configuring the Driver +------------------------ + + The 3C90x driver supports the following options, which can be + supplied as command line arguments to the insmod command or in the + /etc/conf.modules file. Unless otherwise stated, all settings take + the form of: + +