// YOU SHOULD CAREFULLY READ THE FOLLOWING TERMS AND CONDITIONS BEFORE // INSTALLING AND USING THIS PRODUCT, // THE USE OF WHICH IS LICENSED BY 3COM CORPORATION ("3COM") // and others as set forth below for your USE ONLY AS SET FORTH BELOW. // DOWNLOADING, INSTALLING OR OTHERWISE USING ANY PART OF THE SOFTWARE OR // DOCUMENTATION INDICATES THAT YOU ACCEPT THESE TERMS AND CONDITIONS. // IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT, // DO NOT DOWNLOAD, INSTALL OR OTHERWISE USE THE SOFTWARE OR DOCUMENTATION. // AND IF YOU HAVE RECEIVED THE SOFTWARE AND DOCUMENTATION ON PHYSICAL MEDIA, // RETURN THE ENTIRE PRODUCT WITH THE SOFTWARE AND DOCUMENTATION UNUSED TO THE // SUPPLIER WHERE YOU OBTAINED IT. // This driver (3c990.c) has been written to work with the 3cr990 product line // of network cards, manufactured by 3Com Corp. // This driver is not intended for any other product line, including the 3c59x // or 3C90x product lines (although drivers with both of these names, // and for both of these product lines, are available ). // This is a Beta version of the driver (note the version number 1.0.0a). // It does not work with the 2.3 kernel; only the 2.0 and 2.2 at this point. // There has also been very little work (~none) on performance as of yet. // The driver contains no support now for architectures other than IA-32 bit. // If you want a better version, try back later. // To force the media selection, use the command line argument force=X, // where X denotes the selection as follows: // 0=10(megabit)Half(Duplex); 1=10Full; 2=100Half; 3=100Full; 4=auto (default) // e.g. insmod 3c990.o force=1 // ... should give you 10 megabit full duplex forced action. // You may contact 3Com for updates and information (regarding this driver) at: // http://support.3com.com/infodeli/tools/nic/3c990.htm // You may request or submit driver modifications at: // http://support.3com.com/infodeli/tools/nic/linuxrequest.htm // If you would like more information about compiling the driver than is // available at the bottom of this file, you may choose to reference: // http://cesdis.gsfc.nasa.gov/linux/misc/modules.html // and/or you may reference the readme file for the 3c90x driver. // License to "tc990image" // The binary image "tc990image" is licensed to users by 3Com Corporation, // with intended use for 3Com's Network Interface Card models 3CR990-x. // (Where x indicates any extension model number.) // LICENSE: 3Com grants you a nonexclusive, nontransferable // (except as specified herein) license to use the tc990image in conjunction // with your use of 3Com's Network Interface Card models 3CR990-x as specified // above. You are not permitted to lease, rent, distribute or sublicense // (except as specified herein) the tc990image or to use the tc990image or in // any other unauthorized manner. Further, no license is granted to you in the // human readable code of the Software (source code). Except as provided // below, this Agreement does not grant you any rights to patents, copyrights, // trade secrets, trademarks, or any other rights with respect to the Software // or Documentation. // Subject to the restrictions set forth herein, the tc990image is licensed to // be used on any workstation or any network server owned by or leased to you, // for your internal use, provided that the tc990image is used only in // connection with this 3Com product. You may reproduce and provide one (1) // copy of the tc990image for each such workstation or network server on which // the Software is used as permitted hereunder. Otherwise, the Software and // Documentation may be copied only as essential for backup or archive purposes // in support of your use of the Software as permitted hereunder. Each copy // of the tc990image must contain 3Com's and its licensors' proprietary rights // and copyright notices in the same form as on the original. You agree not to // remove or deface any portion of any legend provided on any licensed program // or documentation delivered to you under this Agreement. // ASSIGNMENT; You may transfer the tc990image and the licenses granted // herein to another party in the same country in which you obtained it if the // other party agrees in writing to accept and be bound by the terms and // conditions of this Agreement. If you transfer the tc990image, you must at // the same time either transfer all copies of the Software and Documentation // to the party or you must destroy any copies not transferred. Except as set // forth above, you may not assign or transfer your rights. // NO REVERSE ENGINEERING: Modification, reverse engineering, reverse // compiling, or disassembly of the tc990image is expressly prohibited. // However, if you are a European Union ("EU") resident, information necessary // to achieve interoperability of the tc990image with other programs within the // meaning of the EU Directive on the Legal Protection of Computer Programs is // available to you from 3Com upon written request. // TRADE SECRETS; TITLE: You acknowledge and agree that the structure, // sequence and organization of the tc990image are the valuable trade secrets // of 3Com and its suppliers. You agree to hold such trade secrets in // confidence. You further acknowledge and agree that ownership of, and title // to, the tc990image and all subsequent copies thereof regardless of the form // or media are held by 3Com. // License to this driver // Some material in this driver has been adopted from pci-skeleton.c, // a Linux PCI network adapter skeleton device driver. // pci-skeleton.c written in 1998-1999 by Donald Becker. // The author of pci-skeleton.c may be reached as becker@scyld.com. // More information about Becker is available at http://www.scyld.com/ // Except as otherwise provided, This software and pci-skeleton.c, may be used // and distributed according to the terms of the GNU Public License (GPL), // incorporated herein by reference. // UNITED STATES GOVERNMENT LEGENDS: The portions of this Driver developed by // 3Com, and the tc990image ("Software") is commercial in nature and developed // solely at private expense. The Software is delivered as // "Commercial Computer Software" as defined in DFARS 252.227-7014 (June 1995) // or as a commercial item as defined in FAR 2.101(a) and as such is provided // with only such rights as are provided herein. Technical data is provided // with limited rights only as provided in DFAR 252.227-7015 (Nov. 1995) or // FAR 52.227-14 (June 1987), whichever is applicable. // TERM AND TERMINATION: The licenses to the Software granted hereunder are // perpetual unless terminated earlier as specified below or otherwise // provided. You may terminate the licenses and this Agreement at any time by // destroying the Software and Documentation together with all copies and // merged portions in any form. The licenses and this Agreement will also // terminate immediately if you fail to comply with any term or condition of // this Agreement. Upon such termination you agree to destroy the Software and // Documentation, together with all copies and merged portions in any form. // LIMITED WARRANTIES AND LIMITATION OF LIABILITY: This driver and tc990image // are provided with no warranty, including warranty of fitness for any // particular purpose. 3Com assumes no liability for any damages or injuries // resulting from use of the driver or tc990image file. // GOVERNING LAW: This License shall be governed by the laws of the State of // California, U.S.A. excluding its conflicts of laws principles and excluding // the United Nations Convention on Contracts for the // International Sale of Goods. // SEVERABILITY: In the event any provision of this License is found to be // invalid, illegal or unenforceable, the validity, legality and enforceability // of any of the remaining provisions shall not in any way be affected or // impaired and a valid, legal and enforceable provision of similar intent and // economic impact shall be substituted therefor. // ENTIRE AGREEMENT: This Agreement sets forth the entire understanding and // agreement between you and 3Com and supersedes all prior agreements, whether // written or oral, with respect to the Software and Documentation, and may be // amended only in a writing signed by both parties. // Should you have any questions concerning this Agreement or if you desire to // contact 3Com for any reason, please contact the 3Com subsidiary serving your // country, or write: // 3Com Corporation, Customer Support Information, // 5400 Bayfront Plaza, Santa Clara, CA 95052 static const char *version = "3Com 3c990.c v1.0.0a 8/2000 \n"; static int min_pci_latency = 64; static int force = 4; #if !defined(__OPTIMIZE__) || !defined(__KERNEL__) #warning You must compile this file with the correct options! #warning See the last lines of the source file. #error You must compile this driver with "-O". #endif #define UPDATE_INDEX( index, size, entries ) { index += size; if ( index == (entries * size ) ) index = 0; } // Include files to support kernel versions 2.0.x and 2.2.x. #include #include #ifdef MODULE #ifdef MODVERSIONS #include #endif #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 // * Processor type for cache alignment. * #include #include #include #include #include "3c990img.h" #if (LINUX_VERSION_CODE >= 0x20100) char kernel_version[] = UTS_RELEASE; #else #ifndef __alpha__ #define ioremap vremap #define iounmap vfree #endif #endif #if defined(MODULE) && LINUX_VERSION_CODE > 0x20115 MODULE_AUTHOR("David P. McLean "); MODULE_DESCRIPTION("Driver for 3Com EtherLink 10/100 PCI NIC with 3XP Processor"); MODULE_PARM(min_pci_latency, "i"); MODULE_PARM(force, "i"); #endif #if LINUX_VERSION_CODE < 0x20123 #define test_and_set_bit(val, addr) set_bit(val, addr) #endif #if LINUX_VERSION_CODE <= 0x20139 #define net_device_stats enet_statistics #else #define NETSTATS_VER2 #endif #if LINUX_VERSION_CODE < 0x20155 #include #define PCI_SUPPORT_VER1 #else #define PCI_SUPPORT_VER2 #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 #if ! defined(CAP_NET_ADMIN) #define capable(CAP_XXX) (suser()) #endif #define tc990_IMAGE_SIZE 0x10000 #define tc990_VENDOR_ID 0x10B7 #define TX_ENTRIES 128 #define RX_ENTRIES 128 #define CMD_ENTRIES 64 #define RESPONSE_ENTRIES 64 // 3cr990 Commands. #define tc990_CMD_TX_ENABLE 0x1 #define tc990_CMD_TX_DISABLE 0x2 #define tc990_CMD_RX_ENABLE 0x3 #define tc990_CMD_RX_DISABLE 0x4 #define tc990_CMD_RX_FILT_WRITE 0x5 #define tc990_CMD_READ_STATS 0x7 #define tc990_CMD_XCVR_SELECT 0x13 #define tc990_CMD_MAX_PKT_SIZE_WRITE 0x1A #define tc990_CMD_MCAST_HASH_MASK_WRITE 0x25 #define tc990_CMD_STATION_ADR_READ 0x27 // Descriptor Type #define CMD_DSC_TYPE_CMD_FRAME 0x2 #define CMD_DSC_FRAME_VALID ( 1 << 7 ) #define CMD_DSC_RESPONSE_NEEDED ( 1 << 6 ) #define CMD_DSC_RESPONSE_NOT_NEEDED 0 #define RESPONSE_DSC_ERROR_SET ( 1 << 6 ) #define tc990_STATUS_CMD_FOUND 2 // 3cr990 Registers #define tc990_SOFT_RESET_REG 0x0 #define tc990_INT_STATUS_REG 0x4 #define tc990_INT_ENABLE_REG 0x8 #define tc990_INT_MASK_REG 0xC #define tc990_HostTo3XP_COMM_5_REG 0x1C #define tc990_HostTo3XP_COMM_4_REG 0x20 #define tc990_HostTo3XP_COMM_3_REG 0x24 #define tc990_HostTo3XP_COMM_2_REG 0x28 #define tc990_HostTo3XP_COMM_1_REG 0x2C #define tc990_HostTo3XP_COMM_0_REG 0x30 #define tc990_3XP2HOST_COMM_0_REG 0x40 #define tc990_MASK_ALL_INT 0xFFFFFFFF #define tc990_UNMASK_ALL_INT 0x0 #define tc990_ENABLE_ALL_INT 0xFFFFFFEF #define tc990_RESET_ALL 0x7F // 3cr990 commands for handshake with firmware. #define tc990_NULL_CMD 0x00 #define tc990_BOOTCMD_REG_BOOT_RECORD 0x0FF #define tc990_BOOTCMD_RUNTIME_IMAGE 0xFD #define tc990_BOOTCMD_DOWNLOAD_COMPLETE 0xFB #define tc990_BOOTCMD_SEGMENT_AVAILABLE 0xFC // Waiting for boot signature #define tc990_WAITING_FOR_BOOT 0x7 #define tc990_WAITING_FOR_HOST_REQUEST 0xD #define tc990_WAITING_FOR_SEGMENT 0x10 #define tc990_3XP_COMM_INT0 0x2 // Delay in microseconds to wait for the reset to be over #define tc990_WAIT_COUNTER 100000 #define ETHERNET_MAXIMUM_FRAME_SIZE 1514 //#define ETHERNET_ADDRESS_SIZE 6 //#define ETHERNET_HEADER_SIZE 14 #define tc990_RX_FILT_DIRECTED 0x1 #define tc990_RX_FILT_ALL_MULTICAST 0x2 #define tc990_RX_FILT_BROADCAST 0x4 #define tc990_RX_FILT_PROMISCUOUS 0x8 #define tc990_RX_FILT_HASH_MULTICAST 0x10 #define tc990_MULTICAST_BITS 0x3f // Slot information. struct SLOT { u32 PhysicalAddressLo; u32 PhysicalAddressHi; u32 VirtualAddressLo; u32 VirtualAddressHi; u32 BufferLength; u32 skb; }; typedef struct _tc990_RING { u32 RingBase; // ring address (sharedmemory) u32 NoEntries; // number of entries in ring u32 LastWriteUL; // send: last insert } tc990_RING; typedef struct _TX_RING { u32 RingBase; // ring address (sharedmemory) u32 NoEntries; // number of entries in ring u32 LastWriteUL; // send: last insert u32 LastReadUL; // cleanup: last read - current read u32 WriteRegister; // register used to single NIC u32 PacketPendingNo; // Number of packets sent - not completed } TX_RING; // This structure is updated by the driver and read by the firmware. typedef struct _HOST_WRITE_INDEXES { volatile u32 regRxHiReadUL; volatile u32 regRxLoReadUL; volatile u32 regRxBuffWriteUL; volatile u32 regRespReadUL; } HOST_WRITE_INDEXES; // This structure is updated by firmware and read by the driver. typedef struct _HOST_READ_INDEXES { volatile u32 regTxLoReadUL; volatile u32 regTxHiReadUL; volatile u32 regRxLoWriteUL; volatile u32 regRxBuffReadUL; volatile u32 regCmdReadUL; volatile u32 regRespWriteUL; volatile u32 regRxHiWriteUL; } HOST_READ_INDEXES; // main variable structure typedef struct _HOST_VAR_S { HOST_WRITE_INDEXES hvWriteS; HOST_READ_INDEXES hvReadS; } HOST_VAR_S; typedef struct _HOST_INIT_S { HOST_VAR_S *hostVarsPS; // points to host variable data struct u32 hostVarsHiUL; u32 hostTxLoStartUL; // Tx Lo priority ring u32 hostTxLoStartHiUL; u32 hostTxLoSizeUL; u32 hostTxHiStartUL; // Tx Hi priority ring u32 hostTxHiStartHiUL; u32 hostTxHiSizeUL; u32 hostRxLoStartUL; // Rx Lo priority ring u32 hostRxLoStartHiUL; u32 hostRxLoSizeUL; u32 hostRxFreeStartUL; // Rx free buffer ring u32 hostRxFreeStartHiUL; u32 hostRxFreeSizeUL; u32 hostCtrlStartUL; // Comand ring u32 hostCtrlStartHiUL; u32 hostCtrlSizeUL; u32 hostRespStartUL; // Command Response ring u32 hostRespStartHiUL; u32 hostRespSizeUL; u32 hostZeroWordUL; // (lo) physical address of zero word u32 hostZeroWordHiUL; // (Hi) used for dma u32 hostRxHiStartUL; // Rx Hi priority ring u32 hostRxHiStartHiUL; u32 hostRxHiSizeUL; } HOST_INIT_S; // Receive Descriptor. This structure is updated by the firmware. typedef struct _RX_DSC { u32 Flags:8; u32 NumDescriptor:8; u32 FrameLength:16; u32 VirtualAddressLo; u32 VirtualAddressHi; u32 RxStatus; u16 FilterResults; u16 res1; u32 res2; } RX_DSC; // Receive Free Descriptor. This structure is filled by the driver to give a // buffer to the firmware. typedef struct _RX_FREE_DSC { u32 PhysicalAddressLo; u32 PhysicalAddressHi; u32 VirtualAddressLo; u32 VirtualAddressHi; } RX_FREE_DSC; // used for both commands and responses typedef struct { u32 Flags:8; u32 NumDescriptor:8; u32 Command:16; u32 SequenceNo:16; u32 Parameter1:16; u32 Parameter2; u32 Parameter3; } CMD_DSC; // stats response labels follow (until RESPONSE_DSC union) typedef struct { u32 reserved; u32 reservedB; u32 pkt; u32 byte; }rtnTx; typedef struct { u32 reserved; u32 deferred; u32 lateCollision; u32 collisions; }rtnTxMore; typedef struct { u32 carrierLost; u32 multCollision; u32 reserved; u32 reservedB; }rtnTxCrit; typedef struct { u32 reserved; u32 txFiltered; u32 rxPkt; u32 rxByte; }rtnTxRx; typedef struct { u32 reserved; u32 fifoOverrun; u32 badSSD; u32 crcError; }rtnRx; typedef struct { u32 oversize; u32 reserved; u32 reservedB; u32 reservedC; }rtnRxGeneral; typedef struct { u32 filtered; u32 reserved; u32 reservedB; u32 reservedC; }rtnRxEtc; typedef union { CMD_DSC nrml; //"normal" response values rtnTx tx; rtnTxMore txMore; rtnTxCrit txCrit; rtnTxRx txRx; rtnRx rx; rtnRxGeneral rxGeneral; rtnRxEtc rxEtc; } RESPONSE_DSC; // individual descriptors for each packet typedef struct _TX_DSC { u8 pktFlagsUC; u8 pktReservedUC; u16 pktLenUW; // # of bytes in this packet u32 pktHostAddrLoUL; // PCI address of packet data u32 pktHostAddrHiUL; // PCI address of packet data u32 pktSpareUL; // not used } TX_DSC; typedef struct _tc990_FILE_HEADER { u8 tagID[8]; u32 Version; u32 NumSections; u32 ExecuteAddress; } tc990_FILE_HEADER; typedef struct _tc990_FILE_SECTION { u32 NumBytes; u16 Checksum; u16 Reserved; u32 tc3XPStartAddress; } tc990_FILE_SECTION; struct pci_id_info { const char *name; u16 device_id; }; static struct pci_id_info pci_tbl[] = { {"3Com EtherLink 10/100 PCI NIC with 3XP Processor (3CR990-TX-95)",0x9902}, {"3Com EtherLink 10/100 PCI NIC with 3XP Processor (3CR990-TX-97)",0x9903}, {"3Com EtherLink Server 10/100 PCI NIC with 3XP Processor (3CR990SVR95)", 0x9908}, {"3Com EtherLink Server 10/100 PCI NIC with 3XP Processor (3CR990SVR97)", 0x9909}, {0,}, // 0 terminated list. }; struct tc990phys { HOST_INIT_S init; u32 slack; HOST_VAR_S var; TX_DSC txLo[TX_ENTRIES], txHi[TX_ENTRIES]; RX_FREE_DSC rxFree[RX_ENTRIES]; RX_DSC rxLo[RX_ENTRIES], rxHi[RX_ENTRIES]; CMD_DSC cmd[CMD_ENTRIES]; RESPONSE_DSC rsp[RESPONSE_ENTRIES]; }; struct tc990virt { tc990_RING RxHiRing, RxLoRing, RxBuffRing; // receive information TX_RING TxHiRing, TxLoRing; // send information tc990_RING CmdRing, RspRing; // command ring information }; struct tc990_private { struct tc990phys * pys; struct tc990virt * vrt; void * DownloadPageVirtual; u32 DownloadPagePhysical; u32 ringPhysical; u32 BufferPhysical; struct SLOT* Slot; struct device *next_module; // Link for devices of this type. const char *product_name; struct net_device_stats stats; struct wait_queue *statsWait; u16 chip_id; unsigned char pci_bus, pci_devfn; u8 interruptUp; u32 pad[4]; // Used for 32-byte alignment }; static struct device *tc990probe(u16 pci_bus, u16 pci_devfn, struct device *dev, long ioaddr, u16 irq, u16 chp_idx, u16 fnd_cnt); static int tc990_open(struct device *dev); static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs); static void stats_handler(struct tc990_private *np); static int tc990_close(struct device *dev); static int start_tx(struct sk_buff *skb, struct device *dev); static struct net_device_stats *get_stats(struct device *dev); static void set_rx_mode(struct device *dev); static void ReleaseBufferToFirmware( struct device *dev, u32 SlotIndex ); static void ProcessReceive(struct device *dev, tc990_RING *pRxRing, volatile u32 *regRxReadUL, volatile u32 *regRxWriteUL); static u16 CalculateBufferChecksum(u16 *Buffer, u32 Count); static u16 Downloadbootrecord(struct device *dev, u16 process); static u16 DownloadRunTimeImage(struct device *dev); static u16 issueCommand(struct device *dev, u16 Command, u16 Parameter1, u32 Parameter2, u32 Parameter3, u16 *Return1, u32 *Return2, u32 *Return3, u8 WaitForResponse ); static u8 ProcessResponse( struct tc990_private *np, u32 responseReadIndex, u16 Command ); static void bailOutNow ( struct device *dev, u16 state ); static u16 issueCommand( struct device *dev, u16 Command, u16 Parameter1, u32 Parameter2, u32 Parameter3, u16 *Return1, u32 *Return2, u32 *Return3, u8 WaitForResponse ) { struct tc990_private *np = (struct tc990_private *)dev->priv; CMD_DSC* commandDescriptor; RESPONSE_DSC* responseDescriptor; u32 count, responseStatus; long ioaddr = dev->base_addr; commandDescriptor = (CMD_DSC*) (np->vrt->CmdRing.RingBase + np->vrt->CmdRing.LastWriteUL ); memset(commandDescriptor, 0, sizeof( CMD_DSC )); if ( WaitForResponse ) // Check if wait for response is needed. // Set the flag indicating the need for a response to the command. responseStatus = CMD_DSC_RESPONSE_NEEDED; else responseStatus = CMD_DSC_RESPONSE_NOT_NEEDED; commandDescriptor->Command = Command; commandDescriptor->NumDescriptor = 0; commandDescriptor->Parameter1 = Parameter1; commandDescriptor->Parameter2 = Parameter2; commandDescriptor->Parameter3 = Parameter3; commandDescriptor->Flags = CMD_DSC_FRAME_VALID | responseStatus | CMD_DSC_TYPE_CMD_FRAME ; commandDescriptor->SequenceNo = 0x11; UPDATE_INDEX( np->vrt->CmdRing.LastWriteUL, sizeof( CMD_DSC ), CMD_ENTRIES) ; writel(np->vrt->CmdRing.LastWriteUL, ioaddr + tc990_HostTo3XP_COMM_2_REG); if ( WaitForResponse ) { // Check for response for ( count = 0; count < tc990_WAIT_COUNTER; count++ ){ // If firmware has posted the response then break from loop if ( np->pys->var.hvWriteS.regRespReadUL != np->pys->var.hvReadS.regRespWriteUL ){ if(tc990_CMD_READ_STATS==commandDescriptor->Command){ ProcessResponse (np, np->pys->var.hvWriteS.regRespReadUL, 0); return 0; } if (tc990_STATUS_CMD_FOUND == ProcessResponse (np, np->pys->var.hvWriteS.regRespReadUL, (u16)commandDescriptor->Command)) break; } udelay(50); } // If no response for tc990_WAIT_COUNTER, return failure. if ( count >= tc990_WAIT_COUNTER ) return 1; responseDescriptor = (RESPONSE_DSC*) (np->vrt->RspRing.RingBase + np->pys->var.hvWriteS.regRespReadUL); if ( responseDescriptor->nrml.Flags & RESPONSE_DSC_ERROR_SET ) return 1; // Command is successful, pass the response results back. if ( Return1 ) *Return1 = (u16)responseDescriptor->nrml.Parameter1; if ( Return2 ) *Return2 = responseDescriptor->nrml.Parameter2; if ( Return3 ) *Return3 = responseDescriptor->nrml.Parameter3; UPDATE_INDEX(np->pys->var.hvWriteS.regRespReadUL, sizeof( RESPONSE_DSC ), RESPONSE_ENTRIES); } return 0; } //Description: // This routine calculates the checksum of the count bytes in the buffer. //Arguments: // Buffer - Pointer to the buffer. // Count - Count of bytes for which to compute the checksum. //Return Value: // Checksum of the block. static u16 CalculateBufferChecksum(u16 *Buffer, u32 Count) { u32 checksum = 0; while ( Count > 1 ){ // This is the inner loop. checksum += *Buffer++; Count -= 2; // Fold 32-bit checksum to 16 bits. while ( checksum >> 16 ) checksum = ( checksum & 0xffff ) + ( checksum >> 16 ); } if ( Count > 0 ) checksum += *Buffer; while ( checksum >> 16 ) checksum = ( checksum & 0xffff ) + ( checksum >> 16 ); return (u16)(~checksum ); } static u16 ResetAdapter(struct device *dev) { u32 count; long ioaddr = dev->base_addr; writel(tc990_RESET_ALL, ioaddr + tc990_SOFT_RESET_REG); udelay(10); // Give the board some time to adjust writel(0, ioaddr + tc990_SOFT_RESET_REG); for ( count = 0; count < tc990_WAIT_COUNTER; count++ ) { if ( readl(ioaddr + tc990_3XP2HOST_COMM_0_REG) == tc990_WAITING_FOR_HOST_REQUEST ) break; udelay(50); } if ( count >= tc990_WAIT_COUNTER ) return 1; return 0; } // This routine verifies that the adpater is present and fills in the // adapter specific data static u16 Downloadbootrecord(struct device *dev, u16 process) { struct tc990_private *np = (struct tc990_private *)dev->priv; u32 val, counter; long ioaddr = dev->base_addr; // Check if the adapter status is waiting for boot. for ( counter = 0; counter < tc990_WAIT_COUNTER; counter++ ) { val = readl(ioaddr + tc990_3XP2HOST_COMM_0_REG); if ( val == process ) break; udelay(50); } if ( tc990_WAIT_COUNTER == counter ) return 21; // register the ring with the firmware writel(0, ioaddr + tc990_HostTo3XP_COMM_2_REG); writel( np->ringPhysical, ioaddr + tc990_HostTo3XP_COMM_1_REG ); writel(tc990_BOOTCMD_REG_BOOT_RECORD, ioaddr + tc990_HostTo3XP_COMM_0_REG); // wait for the firmware to pick up the command. for ( counter = 0; counter < tc990_WAIT_COUNTER; counter++ ) { val = readl(ioaddr + tc990_3XP2HOST_COMM_0_REG); if ( val != process ) break; udelay(50); } if ( tc990_WAIT_COUNTER == counter ) return 22; // clear the tx and cmd ring write registers writel(tc990_NULL_CMD, ioaddr + tc990_HostTo3XP_COMM_1_REG); writel(tc990_NULL_CMD, ioaddr + tc990_HostTo3XP_COMM_2_REG); writel(tc990_NULL_CMD, ioaddr + tc990_HostTo3XP_COMM_3_REG); writel(tc990_NULL_CMD, ioaddr + tc990_HostTo3XP_COMM_0_REG); return 0; } //Returns 0 if the image could be downloaded (else other than 0). static u16 DownloadRunTimeImage(struct device *dev) { struct tc990_private *np = (struct tc990_private *)dev->priv; tc990_FILE_HEADER *fileHeader; u32 index; tc990_FILE_SECTION *fileSection; u32 count, value, oldIntMask, oldIntEnable; u32 tempLength, thisSectionHeaderLocation, thisSectionDataLocation; void *sectionDataBuffer; u32 tc3XPStartAddress, numBytesInThisSection, checksum; u32 sections; long ioaddr = dev->base_addr; (void *)np->DownloadPageVirtual = kmalloc(0xfc00, GFP_KERNEL); if (np->DownloadPageVirtual == NULL) return 40; fileHeader = (tc990_FILE_HEADER*)tc990image; if((fileHeader->tagID[0] != 'T') || (fileHeader->tagID[1] != 'Y') || (fileHeader->tagID[2] != 'P') || (fileHeader->tagID[3] != 'H') || (fileHeader->tagID[4] != 'O') || (fileHeader->tagID[5] != 'O') || (fileHeader->tagID[6] != 'N')) { kfree(np->DownloadPageVirtual); return 42; } if ( ResetAdapter(dev) ) { kfree(np->DownloadPageVirtual); return 43; } udelay(5000); oldIntEnable = readl(ioaddr + tc990_3XP2HOST_COMM_0_REG); // Mask the interrupts and enable the interrupts. oldIntEnable = readl(ioaddr + tc990_INT_ENABLE_REG); // zeros writel(oldIntEnable | tc990_3XP_COMM_INT0, ioaddr + tc990_INT_ENABLE_REG); oldIntMask = readl(ioaddr + tc990_INT_MASK_REG); writel(oldIntMask | tc990_3XP_COMM_INT0, ioaddr + tc990_INT_MASK_REG ); for ( count = 0; count < tc990_WAIT_COUNTER ; count++ ) { value = readl(ioaddr + tc990_3XP2HOST_COMM_0_REG); if ( value == tc990_WAITING_FOR_HOST_REQUEST ) break; udelay(50); } if ( count == tc990_WAIT_COUNTER ) { kfree(np->DownloadPageVirtual); return 44; } udelay(5000); // Acknowledge the status. writel(tc990_3XP_COMM_INT0, ioaddr + tc990_INT_STATUS_REG); writel(fileHeader->ExecuteAddress, ioaddr + tc990_HostTo3XP_COMM_1_REG); writel(tc990_BOOTCMD_RUNTIME_IMAGE, ioaddr + tc990_HostTo3XP_COMM_0_REG); thisSectionHeaderLocation = sizeof(struct _tc990_FILE_HEADER )/4; sections = fileHeader->NumSections; for ( index = 0; index < sections; index++ ) { fileSection = (tc990_FILE_SECTION*)(tc990image + thisSectionHeaderLocation ); numBytesInThisSection = fileSection->NumBytes; tc3XPStartAddress = fileSection->tc3XPStartAddress; thisSectionDataLocation = sizeof( tc990_FILE_SECTION )/4; while( numBytesInThisSection ) { // Wait for 3cr990 to be ready to accept this section. for ( count = 0; count < tc990_WAIT_COUNTER; count++ ) { value = readl(ioaddr + tc990_INT_STATUS_REG ); if ( value & tc990_3XP_COMM_INT0 ) break; udelay(50); } if ( count == tc990_WAIT_COUNTER ) { kfree(np->DownloadPageVirtual); return 46; } udelay(5000); writel(tc990_3XP_COMM_INT0, ioaddr + tc990_INT_STATUS_REG); value = readl(ioaddr + tc990_3XP2HOST_COMM_0_REG); if ( value != tc990_WAITING_FOR_SEGMENT ){ udelay(100); kfree(np->DownloadPageVirtual); return 47; } // Calculate the location of the data. sectionDataBuffer = (u8*)( tc990image + thisSectionHeaderLocation + thisSectionDataLocation ); // 3cr990 is ready to accept section. Prepare the section. // Download entire segment if it fits tempLength = numBytesInThisSection; np->DownloadPagePhysical = virt_to_bus(np->DownloadPageVirtual); memcpy(np->DownloadPageVirtual, sectionDataBuffer, tempLength); checksum = CalculateBufferChecksum(np->DownloadPageVirtual, tempLength); writel(tempLength, ioaddr + tc990_HostTo3XP_COMM_1_REG); writel(checksum, ioaddr + tc990_HostTo3XP_COMM_2_REG); writel(tc3XPStartAddress, ioaddr + tc990_HostTo3XP_COMM_3_REG); writel(0, ioaddr + tc990_HostTo3XP_COMM_4_REG); writel(np->DownloadPagePhysical, ioaddr + tc990_HostTo3XP_COMM_5_REG); // Tell 3cr990 it is ready. writel(tc990_BOOTCMD_SEGMENT_AVAILABLE, ioaddr + tc990_HostTo3XP_COMM_0_REG); // Update length and load address for next pass. numBytesInThisSection -= tempLength; tc3XPStartAddress += tempLength; // Update the section data location. thisSectionDataLocation += tempLength/4; } // Update the section header location. numBytesInThisSection = fileSection->NumBytes; thisSectionHeaderLocation += ( numBytesInThisSection + sizeof( tc990_FILE_SECTION ) )/4; } udelay(5000); // We have been able to download all the sections successfully. // Tell 3cr990 we are done and wait for a boot message. writel(tc990_BOOTCMD_DOWNLOAD_COMPLETE, ioaddr+tc990_HostTo3XP_COMM_0_REG); for ( count = 0; count < tc990_WAIT_COUNTER; count++ ) { value = readl(ioaddr + tc990_3XP2HOST_COMM_0_REG); if ( value == tc990_WAITING_FOR_BOOT ) break; udelay(50); } if ( count == tc990_WAIT_COUNTER ) { kfree(np->DownloadPageVirtual); return 49; } udelay(100); // Program the original values back on IntEnable and IntMask writel(oldIntMask, ioaddr + tc990_INT_MASK_REG); writel(oldIntEnable, ioaddr + tc990_INT_ENABLE_REG); kfree(np->DownloadPageVirtual); return 0; } // * A list of our installed devices, for removing the driver module. * static struct device *root_net_dev = NULL; // * we detect the cards we know about in slot order. * static u16 pci_etherdev_probe(struct device *dev, struct pci_id_info pci_tbl[]) { struct pci_dev *pdev; u16 cards_found = 0, pci_index = 0; unsigned char pci_bus, pci_device_fn, cacheSz; u8 pci_latency; if ( ! pcibios_present()) return -ENODEV; for (;pci_index < 0xff; pci_index++) { u16 vendor, device = 0, pci_command; u16 chip_idx, irq = 0; u8 rev; long pciaddr = 0; long ioaddr; if (pcibios_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pci_index, &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) break; pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &vendor); if (vendor != tc990_VENDOR_ID) continue; pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &device); for (chip_idx = 0; pci_tbl[chip_idx].device_id; chip_idx++) if (device == pci_tbl[chip_idx].device_id) break; if (pci_tbl[chip_idx].device_id == 0) continue; // * Compiled out! * pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_CLASS_REVISION, &rev); pdev = pci_find_slot(pci_bus, pci_device_fn); pciaddr = pdev->base_address[1]; irq = pdev->irq; if ((ioaddr = (long)ioremap(pciaddr & PCI_BASE_ADDRESS_MEM_MASK, 0x200)) == 0) { printk(KERN_INFO "Failed to map PCI address %#lx.\n", pciaddr); continue; } // Read the cache line size from the PCI space. pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_CACHE_LINE_SIZE, &cacheSz); // Cache line size is in DWORDS, calculate bytes. cacheSz *= 4; // Check the cache line size. if ((cacheSz % 0x10) || (!cacheSz)) { printk(KERN_INFO "Cacheline size not proper.\n"); cacheSz = 0x20; //default:Use the paragraph boundary for alignment. } // Here the Command reg is read and modified if nec. pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND, &pci_command); if ( (pci_command & (PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE) ) != (PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE) ) { printk(KERN_INFO "The PCI BIOS has not enabled the" " device at %d/%d. Updating PCI command %4.4x->" " | (PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE).\n", pci_bus, pci_device_fn, pci_command); pci_command |= (PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE); pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, pci_command); } dev = tc990probe(pci_bus, pci_device_fn, dev, ioaddr, irq, chip_idx, cards_found); if (!dev) break; //PCI latency check (set) pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, &pci_latency); if (pci_latency < min_pci_latency) { printk(KERN_INFO " PCI latency timer (CFLT) is " "unreasonably low at %d. Setting to %d clocks.\n", pci_latency, min_pci_latency); pcibios_write_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, min_pci_latency); } dev = 0; cards_found++; } return cards_found ? 0 : -ENODEV; } #ifndef MODULE u16 skel_tc990_probe(struct device *dev) { printk(KERN_INFO "%s", version); return pci_etherdev_probe(dev, pci_tbl); } #endif //Initialize boot record ring area static u16 initRingZone( struct device *dev ) { struct tc990_private *np = (struct tc990_private *)dev->priv; u32 physicalLow = virt_to_bus(&np->pys->init); u16 index; RX_FREE_DSC *rxFreeDescriptor; u16 rxBufferSize = ETHERNET_MAXIMUM_FRAME_SIZE + 4 + 2; struct sk_buff *skb; np->ringPhysical = physicalLow; // initialize the BootRecord and HostRingReadWrite registers np->pys->init.hostZeroWordUL = physicalLow + sizeof(HOST_INIT_S); np->pys->init.hostZeroWordHiUL = 0; physicalLow += sizeof(HOST_INIT_S) + sizeof(u32); np->pys->init.hostVarsPS = (HOST_VAR_S*)physicalLow; np->pys->init.hostVarsHiUL = 0; physicalLow += sizeof(HOST_VAR_S); // Save the address of Tx descriptor rings. np->vrt->TxLoRing.RingBase = (u32)bus_to_virt(physicalLow); np->pys->init.hostTxLoStartUL = physicalLow; np->pys->init.hostTxLoStartHiUL = 0; np->pys->init.hostTxLoSizeUL = TX_ENTRIES * sizeof(TX_DSC); physicalLow += np->pys->init.hostTxLoSizeUL; np->vrt->TxHiRing.RingBase = (u32)bus_to_virt(physicalLow); np->pys->init.hostTxHiStartUL = physicalLow; np->pys->init.hostTxHiStartHiUL = 0; np->pys->init.hostTxHiSizeUL = TX_ENTRIES * sizeof(TX_DSC); physicalLow += np->pys->init.hostTxHiSizeUL; // Save the address of RxLo descriptor ring. This ring is filled by f/w. np->vrt->RxLoRing.RingBase = (u32)bus_to_virt(physicalLow); np->pys->init.hostRxLoStartUL = physicalLow; np->pys->init.hostRxLoStartHiUL = 0; np->pys->init.hostRxLoSizeUL = RX_ENTRIES * sizeof(RX_DSC); physicalLow += np->pys->init.hostRxLoSizeUL; // Save the address of RxHi descriptor ring. This ring is filled by f/w. np->vrt->RxHiRing.RingBase = (u32)bus_to_virt(physicalLow); np->pys->init.hostRxHiStartUL = physicalLow; np->pys->init.hostRxHiStartHiUL = 0; np->pys->init.hostRxHiSizeUL = RX_ENTRIES * sizeof(RX_DSC); physicalLow += np->pys->init.hostRxHiSizeUL; // Save the address of Rx buffer ring. This ring is filled by driver. np->vrt->RxBuffRing.RingBase = (u32)bus_to_virt(physicalLow); np->pys->init.hostRxFreeStartUL = physicalLow; np->pys->init.hostRxFreeStartHiUL = 0; np->pys->init.hostRxFreeSizeUL = RX_ENTRIES * sizeof(RX_FREE_DSC); physicalLow += np->pys->init.hostRxFreeSizeUL; // Save the command and response buffers np->vrt->CmdRing.RingBase = (u32)bus_to_virt(physicalLow); np->pys->init.hostCtrlStartUL = physicalLow; np->pys->init.hostCtrlStartHiUL = 0; np->pys->init.hostCtrlSizeUL = CMD_ENTRIES * sizeof(CMD_DSC); physicalLow += np->pys->init.hostCtrlSizeUL; np->vrt->RspRing.RingBase = (u32)bus_to_virt(physicalLow); np->pys->init.hostRespStartUL = physicalLow; np->pys->init.hostRespStartHiUL = 0; np->pys->init.hostRespSizeUL = RESPONSE_ENTRIES * sizeof(RESPONSE_DSC); np->pys->var.hvWriteS.regRxBuffWriteUL = ( RX_ENTRIES - 1 ) * sizeof(RX_FREE_DSC); // Initialize counters (from ResetRingStructures) np->vrt->RxLoRing.LastWriteUL = 0; np->vrt->RxHiRing.LastWriteUL = 0; np->vrt->RxBuffRing.LastWriteUL = 0; np->vrt->TxLoRing.LastWriteUL = 0; np->vrt->TxHiRing.LastWriteUL = 0; np->vrt->CmdRing.LastWriteUL = 0; np->vrt->TxLoRing.LastReadUL = 0; np->vrt->TxHiRing.LastReadUL = 0; np->vrt->TxLoRing.WriteRegister = tc990_HostTo3XP_COMM_3_REG; np->vrt->TxHiRing.WriteRegister = tc990_HostTo3XP_COMM_1_REG; np->vrt->TxLoRing.PacketPendingNo = 0; np->vrt->TxHiRing.PacketPendingNo = 0; np->vrt->TxLoRing.NoEntries = TX_ENTRIES; np->vrt->TxHiRing.NoEntries = TX_ENTRIES; np->vrt->CmdRing.NoEntries = CMD_ENTRIES; np->vrt->RspRing.NoEntries = RESPONSE_ENTRIES; np->Slot = kmalloc (((RX_ENTRIES-1)*(sizeof(struct SLOT))), GFP_KERNEL); if (!np->Slot) return 50; memset( np->Slot, 0, ((RX_ENTRIES -1)*(sizeof(struct SLOT)) ) ); /// Alloc slots // Put the addresses of all the receive buffers in the receive buffer // descriptor ring. for ( index = 0 ; index < RX_ENTRIES - 1; index++ ) { if ((skb = dev_alloc_skb(rxBufferSize)) == NULL) { printk(KERN_INFO "3c990: Allocate rx skb failed. Not enough memory. Try reducing RX_ENRIES value and recompiling this module. \n"); return 52; } skb->dev = dev; skb_reserve(skb, 4); // 32 bit align the IP header // Save the information in the slot. np->Slot[index].VirtualAddressLo = (u32)skb->tail; np->Slot[index].VirtualAddressHi = 0; np->Slot[index].skb = (u32)skb; np->Slot[index].PhysicalAddressLo = (u32)virt_to_bus((void*)np->Slot[index].VirtualAddressLo); np->Slot[index].PhysicalAddressHi = 0; np->Slot[index].BufferLength = rxBufferSize; } // Indicate all the buffers are free. rxFreeDescriptor = (RX_FREE_DSC*)np->vrt->RxBuffRing.RingBase; for(index = 0; index < RX_ENTRIES - 1; index++ ) { rxFreeDescriptor->PhysicalAddressLo = np->Slot[index].PhysicalAddressLo; rxFreeDescriptor->PhysicalAddressHi = np->Slot[index].PhysicalAddressHi; rxFreeDescriptor->VirtualAddressLo = index; rxFreeDescriptor->VirtualAddressHi = 0; rxFreeDescriptor++; } return 0; } static struct device *tc990probe ( u16 pci_bus, u16 pci_devfn, struct device *dev, long ioaddr, u16 irq, u16 chip_id, u16 card_idx ) { struct tc990_private *np; u16 errVal; u32 stationAddressLo = 0; u16 stationAddressHi = 0; dev = init_etherdev(dev, sizeof(struct tc990_private)); dev->base_addr = ioaddr; dev->irq = irq; // * Make certain the descriptor lists are aligned. * np = (void *)(((long)kmalloc (sizeof(*np), GFP_KERNEL) + 15) & ~15); if (!np) { bailOutNow(dev, 5); return NULL; } memset( np, 0, sizeof(*np) ); dev->priv = np; np->pys = (struct tc990phys *) kmalloc (sizeof(struct tc990phys), GFP_KERNEL); np->vrt = (struct tc990virt *) kmalloc (sizeof(struct tc990virt), GFP_KERNEL); if ( !np->pys || !np->vrt ) { bailOutNow(dev, 15); return NULL; } memset( np->pys, 0, sizeof(struct tc990phys) ); memset( np->vrt, 0, sizeof(struct tc990virt) ); memset( &np->stats, 0, sizeof(struct net_device_stats) ); errVal = initRingZone(dev); if (errVal) { bailOutNow(dev, 25); return NULL; } np->next_module = root_net_dev; root_net_dev = dev; np->pci_bus = pci_bus; np->pci_devfn = pci_devfn; np->chip_id = chip_id; // * The chip-specific entries in the device structure. * dev->open = &tc990_open; dev->hard_start_xmit = &start_tx; dev->stop = &tc990_close; dev->get_stats = &get_stats; dev->set_multicast_list = &set_rx_mode; errVal = DownloadRunTimeImage(dev); if (errVal) { bailOutNow(dev, 50); return NULL; } errVal = Downloadbootrecord(dev, tc990_WAITING_FOR_BOOT); if (errVal) { bailOutNow(dev, 70); return NULL; } // Set the maximum packet size for the firmware errVal = issueCommand( dev, tc990_CMD_MAX_PKT_SIZE_WRITE, 0x800, 0, 0, NULL, NULL, NULL, 1); if (errVal) { bailOutNow(dev, 77); return NULL; } // Read the station address from the adapter. errVal = issueCommand( dev, tc990_CMD_STATION_ADR_READ, 0, 0, 0, &stationAddressHi, &stationAddressLo, NULL, 1); if (errVal) { bailOutNow(dev, 78); return NULL; } dev->dev_addr[0] = ((u8*)(&stationAddressHi))[1]; dev->dev_addr[1] = ((u8*)(&stationAddressHi))[0]; dev->dev_addr[2] = ((u8*)(&stationAddressLo))[3]; dev->dev_addr[3] = ((u8*)(&stationAddressLo))[2]; dev->dev_addr[4] = ((u8*)(&stationAddressLo))[1]; dev->dev_addr[5] = ((u8*)(&stationAddressLo))[0]; return dev; } static int tc990_open(struct device *dev) { struct tc990_private *np = (struct tc990_private *)dev->priv; long ioaddr = dev->base_addr; u16 retVal = 0; if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev)) return -EAGAIN; MOD_INC_USE_COUNT; dev->tbusy = 0; dev->interrupt = 0; np->interruptUp = 1; dev->start = 1; if ( (force < 0) || (force > 4) ) printk (KERN_WARNING "3c990: Bad value -- ""force=%x"". Autonegotiation default used instead. \n", force); else retVal = issueCommand(dev, tc990_CMD_XCVR_SELECT, force, 0, 0, NULL, NULL, NULL, 0); retVal |= issueCommand(dev, tc990_CMD_TX_ENABLE, //enableTransmitOnAdapter 0, 0, 0, NULL, NULL, NULL, 1); retVal |= issueCommand(dev, tc990_CMD_RX_ENABLE, //same for receive 0, 0, 0, NULL, NULL, NULL, 1); if (retVal) return -EAGAIN; //If something didn't work, bail out. writel(tc990_ENABLE_ALL_INT, ioaddr + tc990_INT_ENABLE_REG); //go writel(tc990_UNMASK_ALL_INT, ioaddr + tc990_INT_MASK_REG ); return 0; } static int start_tx(struct sk_buff *skb, struct device *dev) { struct tc990_private *np = (struct tc990_private *)dev->priv; unsigned entry, processed, available; unsigned long flags; TX_RING *pTxRing = &np->vrt->TxLoRing; // * Block a timer-based transmit from overlapping. This could better //be done with atomic_swap(1, dev->tbusy), but set_bit() works as well.* if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) { return 1; } save_flags(flags); cli(); // * Calculate the next Tx descriptor entry. * entry = (np->vrt->TxLoRing.LastWriteUL / (sizeof (TX_DSC))); processed = (np->vrt->TxLoRing.LastReadUL / (sizeof (TX_DSC))); if (processed > entry) available = processed - entry; else available = TX_ENTRIES - (entry - processed); //Fill Frame fields np->pys->txLo[entry].pktFlagsUC = 0x1; np->pys->txLo[entry].pktReservedUC = 0x1; np->pys->txLo[entry].pktLenUW = 0; np->pys->txLo[entry].pktHostAddrLoUL = (u32)skb; np->pys->txLo[entry].pktHostAddrHiUL = 0; np->pys->txLo[entry].pktSpareUL = 0; np->pys->txLo[entry+1].pktHostAddrLoUL = virt_to_bus(skb->data); np->pys->txLo[entry+1].pktLenUW = skb->len; //skb->len in bytes np->vrt->TxLoRing.LastWriteUL += sizeof(TX_DSC); if (np->vrt->TxLoRing.LastWriteUL >= (TX_ENTRIES * sizeof(TX_DSC))) np->vrt->TxLoRing.LastWriteUL = 0; np->vrt->TxLoRing.LastWriteUL += sizeof(TX_DSC); if (np->vrt->TxLoRing.LastWriteUL >= (TX_ENTRIES * sizeof(TX_DSC))) np->vrt->TxLoRing.LastWriteUL = 0; restore_flags(flags); // Tell 3cr990: do some work now writel(np->vrt->TxLoRing.LastWriteUL, dev->base_addr + pTxRing->WriteRegister); if (available < 4) return 0; clear_bit(0, (void*)&dev->tbusy); // * Typical path * return 0; } // * The interrupt handler does all of the Rx thread work and cleans up // after the Tx thread. * // static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs) { struct device *dev = (struct device *)dev_instance; struct tc990_private *np; volatile u32 intr_status; volatile u32 *rxReadIndex; volatile u32 *rxWriteIndex; long ioaddr; u16 count; u16 entry = 0, processed, available; ioaddr = dev->base_addr; np = (struct tc990_private *)dev->priv; #if defined(__i386__) // * A lock to prevent simultaneous entry bug on Intel SMP machines. * if (test_and_set_bit(0, (void*)&dev->interrupt)) { printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n", dev->name); dev->interrupt = 0; // * Avoid halting machine. * return; } #else if (dev->interrupt) { printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name); return; } dev->interrupt = 1; #endif // Mask all the interrupts, so that we don't get an interrupt writel(tc990_MASK_ALL_INT, ioaddr + tc990_INT_MASK_REG); intr_status = readl(ioaddr + tc990_INT_STATUS_REG);//wasIntrStatus do { // * Acknowledge all of the current interrupt sources ASAP. * writel(intr_status, ioaddr + tc990_INT_STATUS_REG); /// Check for new Receive Hi data rxReadIndex = &np->pys->var.hvWriteS.regRxHiReadUL; rxWriteIndex = &np->pys->var.hvReadS.regRxHiWriteUL; if(*rxReadIndex != *rxWriteIndex) ProcessReceive( dev, &np->vrt->RxHiRing, rxReadIndex, rxWriteIndex ); /// Check for new Receive Lo data rxReadIndex = &np->pys->var.hvWriteS.regRxLoReadUL; rxWriteIndex = &np->pys->var.hvReadS.regRxLoWriteUL; if (*rxReadIndex != *rxWriteIndex) ProcessReceive( dev, &np->vrt->RxLoRing, rxReadIndex, rxWriteIndex ); /// Check send complete Lo queue entry = (np->vrt->TxLoRing.LastWriteUL / (sizeof(TX_DSC))); processed = (np->vrt->TxLoRing.LastReadUL / (sizeof (TX_DSC))); if (processed > entry) available = processed - entry; else available = TX_ENTRIES - (entry - processed); count = TX_ENTRIES - available - 4; //Buffer by a couple for elbow room if (count > 32) count = 32; available = available + count; while ( (count>0) ){ if (np->pys->txLo[processed].pktFlagsUC){ // * Free the original skb. * dev_free_skb((void*)np->pys->txLo[processed].pktHostAddrLoUL); np->pys->txLo[processed].pktHostAddrLoUL = 0; } //updateTxRingIndex np->vrt->TxLoRing.LastReadUL += sizeof(TX_DSC); if (np->vrt->TxLoRing.LastReadUL >= (TX_ENTRIES * sizeof(TX_DSC))) np->vrt->TxLoRing.LastReadUL = 0; count--; processed = (np->vrt->TxLoRing.LastReadUL / (sizeof (TX_DSC))); } // * Note the hysteresis in marking the queue non-full. * if (dev->tbusy){ if (available > 30){ // * The ring is no longer full, clear tbusy.* clear_bit(0, (void*)&dev->tbusy); mark_bh(NET_BH); } } /// Check if the response write index has changed. if ( np->pys->var.hvWriteS.regRespReadUL != np->pys->var.hvReadS.regRespWriteUL ) ProcessResponse( np, np->pys->var.hvWriteS.regRespReadUL, 0 ); intr_status = readl(ioaddr + tc990_INT_STATUS_REG); } while (intr_status); writel(tc990_UNMASK_ALL_INT, ioaddr + tc990_INT_MASK_REG ); #if defined(__i386__) clear_bit(0, (void*)&dev->interrupt); #else dev->interrupt = 0; #endif return; } static void stats_handler( struct tc990_private *np ) { RESPONSE_DSC* responseDescriptor; responseDescriptor = (RESPONSE_DSC*) //tx (np->vrt->RspRing.RingBase + np->pys->var.hvWriteS.regRespReadUL); if ( responseDescriptor->nrml.Flags & RESPONSE_DSC_ERROR_SET ) { printk(KERN_WARNING "3c990: Stats error flag set.\n" ); return; } np->stats.tx_packets = responseDescriptor->tx.pkt; #if LINUX_VERSION_CODE > 0x20024 np->stats.tx_bytes = responseDescriptor->tx.byte; #endif UPDATE_INDEX( np->pys->var.hvWriteS.regRespReadUL, sizeof( RESPONSE_DSC ), RESPONSE_ENTRIES ); responseDescriptor = (RESPONSE_DSC*) //txMore (np->vrt->RspRing.RingBase + np->pys->var.hvWriteS.regRespReadUL ); UPDATE_INDEX( np->pys->var.hvWriteS.regRespReadUL, sizeof( RESPONSE_DSC ), RESPONSE_ENTRIES ); responseDescriptor = (RESPONSE_DSC*) //txCrit (np->vrt->RspRing.RingBase + np->pys->var.hvWriteS.regRespReadUL ); np->stats.tx_carrier_errors = responseDescriptor->txCrit.carrierLost; np->stats.collisions = responseDescriptor->txCrit.multCollision; np->stats.tx_errors = responseDescriptor->txCrit.carrierLost; UPDATE_INDEX( np->pys->var.hvWriteS.regRespReadUL, sizeof( RESPONSE_DSC ), RESPONSE_ENTRIES ); responseDescriptor = (RESPONSE_DSC*) //txRx (np->vrt->RspRing.RingBase + np->pys->var.hvWriteS.regRespReadUL ); np->stats.rx_packets = responseDescriptor->txRx.rxPkt; #if LINUX_VERSION_CODE > 0x20024 np->stats.rx_bytes = responseDescriptor->txRx.rxByte; #endif UPDATE_INDEX( np->pys->var.hvWriteS.regRespReadUL, sizeof( RESPONSE_DSC ), RESPONSE_ENTRIES ); responseDescriptor = (RESPONSE_DSC*) //rx (np->vrt->RspRing.RingBase + np->pys->var.hvWriteS.regRespReadUL ); np->stats.rx_fifo_errors = responseDescriptor->rx.fifoOverrun; np->stats.rx_errors = responseDescriptor->rx.fifoOverrun; np->stats.rx_errors += responseDescriptor->rx.badSSD; np->stats.rx_errors += responseDescriptor->rx.crcError; np->stats.rx_crc_errors = responseDescriptor->rx.crcError; UPDATE_INDEX( np->pys->var.hvWriteS.regRespReadUL, sizeof( RESPONSE_DSC ), RESPONSE_ENTRIES ); responseDescriptor = (RESPONSE_DSC*) //rxGeneral (np->vrt->RspRing.RingBase + np->pys->var.hvWriteS.regRespReadUL ); np->stats.rx_length_errors = responseDescriptor->rxGeneral.oversize; UPDATE_INDEX( np->pys->var.hvWriteS.regRespReadUL, sizeof( RESPONSE_DSC ), RESPONSE_ENTRIES ); responseDescriptor = (RESPONSE_DSC*) //rxEtc (np->vrt->RspRing.RingBase + np->pys->var.hvWriteS.regRespReadUL ); UPDATE_INDEX( np->pys->var.hvWriteS.regRespReadUL, sizeof( RESPONSE_DSC ), RESPONSE_ENTRIES ); responseDescriptor = (RESPONSE_DSC*) //dpm7 (np->vrt->RspRing.RingBase + np->pys->var.hvWriteS.regRespReadUL ); if (np->interruptUp == 1) wake_up_interruptible( &np->statsWait ); } static struct enet_statistics *get_stats(struct device *dev) { u16 errVal = 0; //general use variable struct tc990_private *np = (struct tc990_private *)dev->priv; if (np->interruptUp == 1) { errVal = issueCommand( dev, (u16)tc990_CMD_READ_STATS, 0, 0, 0, NULL, NULL, NULL, 0 ); if (errVal) printk(KERN_CRIT "3c990: Stats command gone BAD." ); interruptible_sleep_on( &np->statsWait ); } else { errVal = issueCommand( dev, (u16)tc990_CMD_READ_STATS, 0, 0, 0, NULL, NULL, NULL, 1 ); if (errVal) printk(KERN_CRIT "3c990:Stats command gone BAD." ); } return &np->stats; } #define POLYNOMIAL 0x04c11db7 static void set_rx_mode(struct device *dev) { u32 filter = 0; u32 Crc, Carry; u16 i, j; u8 ThisByte; u8 Address[6]; u32 hashFilterArray[(tc990_MULTICAST_BITS + 1) / 32]; u32 hashBit, count; if (dev->flags & IFF_PROMISC) { // * Set promiscuous. * log net taps. * printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name); filter |= tc990_RX_FILT_PROMISCUOUS; } else if (dev->flags & IFF_ALLMULTI) { // || (dev->flags & IFF_ALLMULTI)) { filter |= tc990_RX_FILT_DIRECTED; filter |= tc990_RX_FILT_ALL_MULTICAST; filter |= tc990_RX_FILT_BROADCAST; } else if (dev->flags & IFF_MULTICAST) { filter |= tc990_RX_FILT_DIRECTED; filter |= tc990_RX_FILT_HASH_MULTICAST; filter |= tc990_RX_FILT_BROADCAST; ///SetMulticastAddressList on the adapter. // Clear all bits memset( hashFilterArray, 0, sizeof( hashFilterArray ) ); // Calculate hash bit for eash address and stuff in bit array for ( count = 0; count < dev->mc_count; count++ ) { //CalculateHashBits // Compute CRC for the address value. Crc = 0xffffffff; // initial value // For each byte of the address for ( i = 0; i < 6; i++ ) { ThisByte = Address[i]; // For each bit in the byte for ( j = 0; j < 8; j++ ) { Carry = ((Crc & 0x80000000) ? 1 : 0) ^ (ThisByte & 0x01); Crc <<= 1; ThisByte >>= 1; if ( Carry ) Crc = (Crc ^ POLYNOMIAL) | Carry; } } // filter bit position. hashBit = (u16)(Crc & tc990_MULTICAST_BITS) ; hashFilterArray[ hashBit / 32 ] |= ( 1 << hashBit % 32 ) ; } i = issueCommand( dev, (u16)tc990_CMD_MCAST_HASH_MASK_WRITE, (u16)2, hashFilterArray[0], hashFilterArray[1], NULL, NULL, NULL, 0 ); } else { filter |= tc990_RX_FILT_DIRECTED; filter |= tc990_RX_FILT_BROADCAST; } i = issueCommand(dev, (u16)tc990_CMD_RX_FILT_WRITE, (u16)filter, 0, 0, NULL, NULL, NULL, 0); } static int tc990_close(struct device *dev) { u16 errVal; long ioaddr = dev->base_addr; struct tc990_private *np = (struct tc990_private *)dev->priv; dev->start = 0; dev->tbusy = 1; np->interruptUp = 0; // * Disable interrupts by clearing the interrupt mask. * writel(tc990_MASK_ALL_INT, ioaddr + tc990_INT_MASK_REG); // * Stop the chip's Tx and Rx processes. * errVal = issueCommand(dev, tc990_CMD_TX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 1); errVal |= issueCommand(dev, tc990_CMD_RX_DISABLE, 0, 0, 0, NULL, NULL, NULL, 1); if (errVal) printk(KERN_CRIT "3c990: Unaccounted for failure of command to network adapter. DANGER!"); free_irq(dev->irq, dev); MOD_DEC_USE_COUNT; return 0; } #ifdef MODULE u16 init_module(void) { printk(KERN_INFO "%s", version); return pci_etherdev_probe(NULL, pci_tbl); } void cleanup_module(void) { struct device *next_dev; u16 index; u16 processed; // * No need to check MOD_IN_USE, as sys_delete_module() checks. * while (root_net_dev) { struct tc990_private *np=(struct tc990_private *)root_net_dev->priv; while (np->vrt->TxLoRing.LastReadUL != np->pys->var.hvReadS.regTxLoReadUL) { processed = (np->vrt->TxLoRing.LastReadUL / (sizeof (TX_DSC))); if (np->pys->txLo[processed].pktFlagsUC){ dev_free_skb((void*)np->pys->txLo[processed].pktHostAddrLoUL); np->pys->txLo[processed].pktHostAddrLoUL = 0; } np->vrt->TxLoRing.LastReadUL += sizeof(TX_DSC); if (np->vrt->TxLoRing.LastReadUL >= (TX_ENTRIES * sizeof(TX_DSC))) np->vrt->TxLoRing.LastReadUL = 0; } next_dev = np->next_module; unregister_netdev(root_net_dev); iounmap((char *)root_net_dev->base_addr); // Free all the receive buffers in the receive buffer descriptor ring. for ( index = 0 ; index < RX_ENTRIES - 1; index++ ) { if (np->Slot[index].skb != (u32)NULL) { dev_free_skb((void*)np->Slot[index].skb); } np->Slot[index].VirtualAddressLo = 0; np->Slot[index].PhysicalAddressLo = 0; } kfree(np->Slot); kfree(np->pys); kfree(np->vrt); kfree(np); kfree(root_net_dev); root_net_dev = next_dev; } } #endif // * MODULE * static void bailOutNow (struct device *dev, u16 state) { u16 index; struct tc990_private *np=(struct tc990_private *)dev->priv; printk(KERN_INFO"3c990: There appear to be inadequate resources at this time and on this machine for this driver. Killing further initialization. \n" ); if (dev->flags & IFF_UP) { // * Some kernel versions close automatically. * tc990_close(dev); dev->flags &= ~(IFF_UP|IFF_RUNNING); } if (state > 25) root_net_dev = np->next_module; unregister_netdev(dev); iounmap((char *)dev->base_addr); if (state > 10){ // Free all the receive buffers. if (np->Slot){ for( index = 0 ; index < RX_ENTRIES - 1; index++ ) { if (np->Slot[index].skb != (u32)NULL) { dev_free_skb((void*)np->Slot[index].skb); } np->Slot[index].VirtualAddressLo = 0; np->Slot[index].PhysicalAddressLo = 0; } } kfree(np->Slot); kfree(np->pys); kfree(np->vrt); kfree(np); } kfree(dev); return; } static void ProcessReceive(struct device *dev, tc990_RING *pRxRing, volatile u32 *regRxReadUL, volatile u32 *regRxWriteUL) { struct sk_buff *skb; RX_DSC *rxDescriptor ; u32 slotIndex, rxReadIndex = *regRxReadUL, writeVal = *regRxWriteUL; u16 rxBufferSize = ETHERNET_MAXIMUM_FRAME_SIZE + 4 + 2; struct tc990_private *np = (struct tc990_private *)dev->priv; while ( rxReadIndex != writeVal ){ rxDescriptor = (RX_DSC*)(pRxRing->RingBase + rxReadIndex); slotIndex = rxDescriptor->VirtualAddressLo; // Handle rx if ( rxDescriptor->RxStatus ) { printk (KERN_INFO "3c990: rx_error \n"); continue; } else { skb = (struct sk_buff*)np->Slot[slotIndex].skb; if (skb){ skb_put(skb, rxDescriptor->FrameLength); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; } } UPDATE_INDEX( rxReadIndex, sizeof( RX_DSC ), RX_ENTRIES ); writeVal = *regRxWriteUL; } // Refill buffer slots. rxReadIndex = *regRxReadUL; while ( rxReadIndex != writeVal ) { /// Give this receive descriptor back to firmware. rxDescriptor = (RX_DSC*)(pRxRing->RingBase + rxReadIndex); slotIndex = rxDescriptor->VirtualAddressLo; UPDATE_INDEX( rxReadIndex, sizeof( RX_DSC ), RX_ENTRIES ); *regRxReadUL = rxReadIndex ; if ((skb = dev_alloc_skb(rxBufferSize)) == NULL) { printk(KERN_CRIT "3c990: Memory squeeze (alloc rx buff failed) Danger!\n"); return; // That hurts. } skb->dev = dev; skb_reserve(skb, 4); // Save the information in the slot. np->Slot[slotIndex].VirtualAddressLo = (u32)skb->tail; np->Slot[slotIndex].VirtualAddressHi = 0; np->Slot[slotIndex].skb = (u32)skb; np->Slot[slotIndex].PhysicalAddressLo = virt_to_bus((void*)np->Slot[slotIndex].VirtualAddressLo); np->Slot[slotIndex].PhysicalAddressHi = 0; np->Slot[slotIndex].BufferLength = rxBufferSize; ReleaseBufferToFirmware(dev, slotIndex ); } } //Routine Description: Returns the buffer at SlotIndex to the firmware. static void ReleaseBufferToFirmware( struct device *dev, u32 SlotIndex ) { RX_FREE_DSC *rxFreeDescriptor; HOST_VAR_S *pHostVar; struct tc990_private *np = (struct tc990_private *)dev->priv; pHostVar = (HOST_VAR_S*)bus_to_virt((u32)np->pys->init.hostVarsPS); rxFreeDescriptor = (RX_FREE_DSC*) (np->vrt->RxBuffRing.RingBase + pHostVar->hvWriteS.regRxBuffWriteUL); rxFreeDescriptor->PhysicalAddressLo =np->Slot[SlotIndex].PhysicalAddressLo; rxFreeDescriptor->PhysicalAddressHi =np->Slot[SlotIndex].PhysicalAddressHi; rxFreeDescriptor->VirtualAddressLo = SlotIndex; rxFreeDescriptor->VirtualAddressHi = 0; UPDATE_INDEX( pHostVar->hvWriteS.regRxBuffWriteUL, sizeof( RX_FREE_DSC ), RX_ENTRIES ); } static u8 ProcessResponse( struct tc990_private *np, u32 responseReadIndex, u16 Command ) { RESPONSE_DSC* responseDesc; while ( responseReadIndex != np->pys->var.hvReadS.regRespWriteUL ){ responseDesc = (RESPONSE_DSC*) (np->vrt->RspRing.RingBase + responseReadIndex); if ( Command == responseDesc->nrml.Command ) return tc990_STATUS_CMD_FOUND; if (responseDesc->nrml.Command == tc990_CMD_READ_STATS){ stats_handler( np ); responseReadIndex = np->pys->var.hvWriteS.regRespReadUL; return 0; } else { if ( responseDesc->nrml.Flags & RESPONSE_DSC_ERROR_SET ) printk(KERN_WARNING "tc990: Response error bit set \n"); UPDATE_INDEX( np->pys->var.hvWriteS.regRespReadUL, sizeof(RESPONSE_DSC), RESPONSE_ENTRIES ); responseReadIndex = np->pys->var.hvWriteS.regRespReadUL; } } return 0; } /* * Local variables: * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -I/usr/src/linux/include -O6 -c 3c990.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" * SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -I/usr/src/linux/include -O6 -c 3c990.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`" * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */