* - By John Hodge (thePowersGang)
*
* e1000.c
- * - Intel E1000 Network Card Driver (core)
+ * - Intel 8254x Network Card Driver (core)
*/
#define DEBUG 1
#define VERSION VER2(0,1)
#include <modules.h>
#include <drv_pci.h>
#include <IPStack/include/adapters_api.h>
+#include <timers.h> // Time_Delay
+
+const struct sSupportedCard {
+ Uint16 Vendor, Device;
+} caSupportedCards[] = {
+ {0x8086, 0x100E}, // 82540EM-A Desktop
+ {0x8086, 0x1010}, // 82546EB-A1 Copper Dual Port
+ {0x8086, 0x1012}, // 82546EB-A1 Fiber
+ {0x8086, 0x1019}, // 82547[EG]I Copper
+ {0x8086, 0x101A}, // 82547EI Mobile
+ {0x8086, 0x101D}, // 82546EB-A1 Copper Quad Port
+};
+const int ciNumSupportedCards = sizeof(caSupportedCards)/sizeof(caSupportedCards[0]);
// === PROTOTYPES ===
int E1000_Install(char **Arguments);
tIPStackBuffer *E1000_WaitForPacket(void *Ptr);
int E1000_SendPacket(void *Ptr, tIPStackBuffer *Buffer);
void E1000_IRQHandler(int Num, void *Ptr);
+ int E1000_int_InitialiseCard(tCard *Card);
+Uint16 E1000_int_ReadEEPROM(tCard *Card, Uint8 WordIdx);
// === GLOBALS ===
MODULE_DEFINE(0, VERSION, E1000, E1000_Install, E1000_Cleanup, NULL);
tIPStack_AdapterType gE1000_AdapterType = {
.Name = "E1000",
- .Type = 0, // TODO: Differentiate differnet wire protos and speeds
- .Flags = 0, // TODO: IP checksum offloading, MAC checksum offloading etc
+ .Type = ADAPTERTYPE_ETHERNET_1G, // TODO: Differentiate differnet wire protos and speeds
+ .Flags = ADAPTERFLAG_OFFLOAD_MAC, // TODO: IP/TCP/UDP checksum offloading
.SendPacket = E1000_SendPacket,
.WaitForPacket = E1000_WaitForPacket
};
+tCard *gaE1000_Cards;
// === CODE ===
int E1000_Install(char **Arguments)
{
- for( int id = -1; (id = PCI_GetDevice(0x8086, 0x100E, id)) != -1; )
+ int card_count = 0;
+ for( int modelidx = 0; modelidx < ciNumSupportedCards; modelidx ++ )
{
-
+ const struct sSupportedCard *cardtype = &caSupportedCards[modelidx];
+ card_count += PCI_CountDevices(cardtype->Vendor, cardtype->Device);
+ }
+ LOG("card_count = %i", card_count);
+ if( card_count == 0 ) {
+ LOG("Zero cards located");
+ return MODULE_ERR_NOTNEEDED;
+ }
+
+ // Allocate card array
+ gaE1000_Cards = calloc(sizeof(tCard), card_count);
+ if( !gaE1000_Cards ) {
+ return MODULE_ERR_MALLOC;
+ }
+
+ // Initialise cards
+ int card_idx = 0;
+ for( int modelidx = 0; modelidx < ciNumSupportedCards; modelidx ++ )
+ {
+ const struct sSupportedCard *cardtype = &caSupportedCards[modelidx];
+ for( int id = -1, i = 0; (id = PCI_GetDevice(cardtype->Vendor, cardtype->Device, i)) != -1; i ++ )
+ {
+ tCard *card = &gaE1000_Cards[card_idx++];
+ Uint32 mmiobase = PCI_GetBAR(id, 0);
+ if( mmiobase & (1|8) ) {
+ Log_Warning("E1000", "Dev %i: BAR0 should be non-prefetchable memory", id);
+ continue;
+ }
+ const int addrsize = (mmiobase>>1) & 3;
+ if( addrsize == 0 ) {
+ // Standard 32-bit
+ card->MMIOBasePhys = mmiobase & ~0xF;
+ }
+ else if( addrsize == 2 ) {
+ // 64-bit
+ card->MMIOBasePhys = (mmiobase & ~0xF) | ((Uint64)PCI_GetBAR(id, 1)<<32);
+ }
+ else {
+ Log_Warning("E1000", "Dev %i: Unknown memory address size %i", id, (mmiobase>>1)&3);
+ continue;
+ }
+
+ card->IRQ = PCI_GetIRQ(id);
+ IRQ_AddHandler(card->IRQ, E1000_IRQHandler, card);
+
+ Log_Debug("E1000", "Card %i: %P IRQ %i", card_idx, card->MMIOBasePhys, card->IRQ);
+
+ if( E1000_int_InitialiseCard(card) ) {
+ return MODULE_ERR_MALLOC;
+ }
+
+ card->IPStackHandle = IPStack_Adapter_Add(&gE1000_AdapterType, card, card->MacAddr);
+ }
}
- return MODULE_ERR_NOTNEEDED;
+ return MODULE_ERR_OK;
}
int E1000_Cleanup(void)
return 0;
}
+void E1000_int_ReleaseRXD(void *Arg, size_t HeadLen, size_t FootLen, const void *Data)
+{
+ tCard **cardptr = Arg;
+ tCard *Card = *cardptr;
+ int rxd = (Arg - (void*)Card->RXDescs) / sizeof(tRXDesc);
+
+ Card->RXDescs[rxd].Status = 0;
+ Mutex_Acquire(&Card->lRXDescs);
+ if( rxd == REG32(Card, REG_RDT) ) {
+ while( rxd != Card->FirstUnseenRXD && !(Card->RXDescs[rxd].Status & RXD_STS_DD) ) {
+ rxd ++;
+ if( rxd == NUM_RX_DESC )
+ rxd = 0;
+ }
+ REG32(Card, REG_RDT) = rxd;
+ LOG("Updated RDT=%i", rxd);
+ }
+ Mutex_Release(&Card->lRXDescs);
+}
+
tIPStackBuffer *E1000_WaitForPacket(void *Ptr)
{
- return NULL;
+ tCard *Card = Ptr;
+
+ if( Semaphore_Wait(&Card->AvailPackets, 1) != 1 )
+ return NULL;
+
+ ENTER("pPtr", Ptr);
+
+ Mutex_Acquire(&Card->lRXDescs);
+ int first_rxd = Card->FirstUnseenRXD;
+ int last_rxd = first_rxd;
+ int nDesc = 1;
+ while( last_rxd != Card->LastUnseenRXD ) {
+ if( !(Card->RXDescs[last_rxd].Status & RXD_STS_DD) )
+ break; // Oops, should ahve found an EOP first
+ if( Card->RXDescs[last_rxd].Status & RXD_STS_EOP )
+ break;
+ nDesc ++;
+ last_rxd = (last_rxd + 1) % NUM_RX_DESC;
+ }
+ Card->FirstUnseenRXD = (last_rxd + 1) % NUM_RX_DESC;
+ Mutex_Release(&Card->lRXDescs);
+
+ LOG("nDesc = %i, first_rxd = %i", nDesc, first_rxd);
+ tIPStackBuffer *ret = IPStack_Buffer_CreateBuffer(nDesc);
+ int rxd = first_rxd;
+ for( int i = 0; i < nDesc; i ++ )
+ {
+ IPStack_Buffer_AppendSubBuffer(ret, 0, Card->RXDescs[rxd].Length, Card->RXBuffers[rxd],
+ E1000_int_ReleaseRXD, &Card->RXBackHandles[rxd]);
+ }
+
+ LEAVE('p', ret);
+ return ret;
}
int E1000_SendPacket(void *Ptr, tIPStackBuffer *Buffer)
{
- return -1;
+ tCard *Card = Ptr;
+
+ ENTER("pPtr pBuffer", Ptr, Buffer);
+
+ int nDesc = 0;
+ size_t len;
+ const void *ptr;
+ // Count sub-buffers (including splitting cross-page entries)
+ int idx = -1;
+ while( (idx = IPStack_Buffer_GetBuffer(Buffer, idx, &len, &ptr)) != -1 )
+ {
+ if( len > PAGE_SIZE ) {
+ LOG("len=%i > PAGE_SIZE", len);
+ LEAVE('i', EINVAL);
+ return EINVAL;
+ }
+ if( MM_GetPhysAddr(ptr) + len-1 != MM_GetPhysAddr((char*)ptr + len-1) ) {
+ LOG("Buffer %p+%i spans non-contig physical pages", ptr, len);
+ nDesc ++;
+ }
+ nDesc ++;
+ }
+
+ // Request set of TX descriptors
+ int rv = Semaphore_Wait(&Card->FreeTxDescs, nDesc);
+ if(rv != nDesc) {
+ LEAVE('i', EINTR);
+ return EINTR;
+ }
+ Mutex_Acquire(&Card->lTXDescs);
+ int first_txd = Card->FirstFreeTXD;
+ Card->FirstFreeTXD = (first_txd + nDesc) % NUM_TX_DESC;
+ int last_txd = (first_txd + nDesc-1) % NUM_TX_DESC;
+
+ LOG("first_txd = %i, last_txd = %i", first_txd, last_txd);
+
+ // Populate buffers
+ idx = -1;
+ int txd = first_txd;
+ while( (idx = IPStack_Buffer_GetBuffer(Buffer, idx, &len, &ptr)) != -1 )
+ {
+ if( MM_GetPhysAddr(ptr) + len-1 != MM_GetPhysAddr((char*)ptr + len-1) )
+ {
+ size_t remlen = PAGE_SIZE - ((tVAddr)ptr & (PAGE_SIZE-1));
+ // Split in two
+ // - First Page
+ Card->TXDescs[txd].Buffer = MM_GetPhysAddr(ptr);
+ Card->TXDescs[txd].Length = remlen;
+ Card->TXDescs[txd].CMD = TXD_CMD_RS;
+ txd = (txd + 1) % NUM_TX_DESC;
+ // - Second page
+ Card->TXDescs[txd].Buffer = MM_GetPhysAddr((char*)ptr + remlen);
+ Card->TXDescs[txd].Length = len - remlen;
+ Card->TXDescs[txd].CMD = TXD_CMD_RS;
+ }
+ else
+ {
+ // Single
+ Card->TXDescs[txd].Buffer = MM_GetPhysAddr(ptr);
+ Card->TXDescs[txd].Length = len;
+ Card->TXDescs[txd].CMD = TXD_CMD_RS;
+ }
+ txd = (txd + 1) % NUM_TX_DESC;
+ }
+ Card->TXDescs[last_txd].CMD |= TXD_CMD_EOP|TXD_CMD_IDE|TXD_CMD_IFCS;
+ Card->TXSrcBuffers[last_txd] = Buffer;
+
+ // Trigger TX
+ IPStack_Buffer_LockBuffer(Buffer);
+ REG32(Card, REG_TDT) = Card->FirstFreeTXD;
+ Mutex_Release(&Card->lTXDescs);
+ LOG("Waiting for TX");
+
+ // Wait for completion (lock will block, then release straight away)
+ IPStack_Buffer_LockBuffer(Buffer);
+ IPStack_Buffer_UnlockBuffer(Buffer);
+
+ // TODO: Check status bits
+
+ LEAVE('i', 0);
+ return 0;
}
void E1000_IRQHandler(int Num, void *Ptr)
{
+ tCard *Card = Ptr;
+
+ Uint32 icr = REG32(Card, REG_ICR);
+ if( icr == 0 )
+ return ;
+ LOG("icr = %x", icr);
+
+ // Transmit descriptor written
+ if( (icr & ICR_TXDW) || (icr & ICR_TXQE) )
+ {
+ int nReleased = 0;
+ // Walk descriptors looking for the first non-complete descriptor
+ LOG("TX %i:%i", Card->LastFreeTXD, Card->FirstFreeTXD);
+ while( Card->LastFreeTXD != Card->FirstFreeTXD && (Card->TXDescs[Card->LastFreeTXD].Status & TXD_STS_DD) )
+ {
+ nReleased ++;
+ if( Card->TXSrcBuffers[Card->LastFreeTXD] ) {
+ IPStack_Buffer_UnlockBuffer( Card->TXSrcBuffers[Card->LastFreeTXD] );
+ Card->TXSrcBuffers[Card->LastFreeTXD] = NULL;
+ }
+ Card->LastFreeTXD ++;
+ if(Card->LastFreeTXD == NUM_TX_DESC)
+ Card->LastFreeTXD = 0;
+ }
+ Semaphore_Signal(&Card->FreeTxDescs, nReleased);
+ LOG("nReleased = %i", nReleased);
+ }
+
+ if( icr & ICR_LSC )
+ {
+ // Link status change
+ LOG("LSC");
+ // TODO: Detect link drop/raise and poke IPStack
+ }
+
+ // Pending packet (s)
+ if( icr & ICR_RXT0 )
+ {
+ int nPackets = 0;
+ LOG("RX %i:%i", Card->LastUnseenRXD, Card->FirstUnseenRXD);
+ while( (Card->RXDescs[Card->LastUnseenRXD].Status & RXD_STS_DD) )
+ {
+ if( Card->RXDescs[Card->LastUnseenRXD].Status & RXD_STS_EOP )
+ nPackets ++;
+ Card->LastUnseenRXD ++;
+ if( Card->LastUnseenRXD == NUM_RX_DESC )
+ Card->LastUnseenRXD = 0;
+
+ if( Card->LastUnseenRXD == Card->FirstUnseenRXD )
+ break;
+ }
+ Semaphore_Signal(&Card->AvailPackets, nPackets);
+ LOG("nPackets = %i", nPackets);
+ }
+
+ icr &= ~(ICR_RXT0|ICR_LSC|ICR_TXQE|ICR_TXDW);
+ if( icr )
+ Log_Warning("E1000", "Unhandled ICR bits 0x%x", icr);
+}
+
+// TODO: Move this function into Kernel/drvutil.c
+/**
+ * \brief Allocate a set of buffers in hardware mapped space
+ *
+ * Allocates \a NumBufs buffers using shared pages (if \a BufSize is less than a page) or
+ * as a set of contiugious allocations.
+ */
+int DrvUtil_AllocBuffers(void **Buffers, int NumBufs, int PhysBits, size_t BufSize)
+{
+ if( BufSize >= PAGE_SIZE )
+ {
+ const int pages_per_buf = BufSize / PAGE_SIZE;
+ ASSERT(pages_per_buf * PAGE_SIZE == BufSize);
+ for( int i = 0; i < NumBufs; i ++ ) {
+ Buffers[i] = (void*)MM_AllocDMA(pages_per_buf, PhysBits, NULL);
+ if( !Buffers[i] ) return 1;
+ }
+ }
+ else
+ {
+ size_t ofs = 0;
+ const int bufs_per_page = PAGE_SIZE / BufSize;
+ ASSERT(bufs_per_page * BufSize == PAGE_SIZE);
+ void *page;
+ for( int i = 0; i < NumBufs; i ++ )
+ {
+ if( ofs == 0 ) {
+ page = (void*)MM_AllocDMA(1, PhysBits, NULL);
+ if( !page ) return 1;
+ }
+ Buffers[i] = (char*)page + ofs;
+ ofs += BufSize;
+ if( ofs >= PAGE_SIZE )
+ ofs = 0;
+ }
+ }
+ return 0;
+}
+
+int E1000_int_InitialiseCard(tCard *Card)
+{
+ ENTER("pCard", Card);
+
+ // Map required structures
+ Card->MMIOBase = (void*)MM_MapHWPages( Card->MMIOBasePhys, 7 );
+ if( !Card->MMIOBase ) {
+ Log_Error("E1000", "%p: Failed to map MMIO Space (7 pages)", Card);
+ LEAVE('i', 1);
+ return 1;
+ }
+
+ // --- Read MAC address from EEPROM ---
+ {
+ Uint16 macword;
+ macword = E1000_int_ReadEEPROM(Card, 0);
+ Card->MacAddr[0] = macword & 0xFF;
+ Card->MacAddr[1] = macword >> 8;
+ macword = E1000_int_ReadEEPROM(Card, 1);
+ Card->MacAddr[2] = macword & 0xFF;
+ Card->MacAddr[3] = macword >> 8;
+ macword = E1000_int_ReadEEPROM(Card, 2);
+ Card->MacAddr[4] = macword & 0xFF;
+ Card->MacAddr[5] = macword >> 8;
+ }
+ Log_Log("E1000", "%p: MAC Address %02x:%02x:%02x:%02x:%02x:%02x",
+ Card,
+ Card->MacAddr[0], Card->MacAddr[1],
+ Card->MacAddr[2], Card->MacAddr[3],
+ Card->MacAddr[4], Card->MacAddr[5]);
+
+ // --- Prepare for RX ---
+ LOG("RX Preparation");
+ Card->RXDescs = (void*)MM_AllocDMA(1, 64, NULL);
+ if( !Card->RXDescs ) {
+ LEAVE('i', 2);
+ return 2;
+ }
+ if( DrvUtil_AllocBuffers(Card->RXBuffers, NUM_RX_DESC, 64, RX_DESC_BSIZE) ) {
+ LEAVE('i', 3);
+ return 3;
+ }
+ for( int i = 0; i < NUM_RX_DESC; i ++ )
+ {
+ Card->RXDescs[i].Buffer = MM_GetPhysAddr(Card->RXBuffers[i]);
+ Card->RXDescs[i].Status = 0; // Clear RXD_STS_DD, gives it to the card
+ Card->RXBackHandles[i] = Card;
+ }
+
+ REG64(Card, REG_RDBAL) = MM_GetPhysAddr((void*)Card->RXDescs);
+ REG32(Card, REG_RDLEN) = NUM_RX_DESC * 16;
+ REG32(Card, REG_RDH) = 0;
+ REG32(Card, REG_RDT) = NUM_RX_DESC;
+ // Hardware size, Multicast promisc, Accept broadcast, Interrupt at 1/4 Rx descs free
+ REG32(Card, REG_RCTL) = RX_DESC_BSIZEHW | RCTL_MPE | RCTL_BAM | RCTL_RDMTS_1_4;
+ Card->FirstUnseenRXD = 0;
+ Card->LastUnseenRXD = 0;
+
+ // --- Prepare for TX ---
+ LOG("TX Preparation");
+ Card->TXDescs = (void*)MM_AllocDMA(1, 64, NULL);
+ if( !Card->RXDescs ) {
+ LEAVE('i', 4);
+ return 4;
+ }
+ for( int i = 0; i < NUM_TX_DESC; i ++ )
+ {
+ Card->TXDescs[i].Buffer = 0;
+ Card->TXDescs[i].CMD = 0;
+ }
+ REG64(Card, REG_TDBAL) = MM_GetPhysAddr((void*)Card->TXDescs);
+ REG32(Card, REG_TDLEN) = NUM_TX_DESC * 16;
+ REG32(Card, REG_TDH) = 0;
+ REG32(Card, REG_TDT) = 0;
+ // Enable, pad short packets
+ REG32(Card, REG_TCTL) = TCTL_EN | TCTL_PSP | (0x0F << TCTL_CT_ofs) | (0x40 << TCTL_COLD_ofs);
+ Card->FirstFreeTXD = 0;
+
+ // -- Prepare Semaphores
+ Semaphore_Init(&Card->FreeTxDescs, NUM_TX_DESC, NUM_TX_DESC, "E1000", "TXDescs");
+ Semaphore_Init(&Card->AvailPackets, 0, NUM_RX_DESC, "E1000", "RXDescs");
+
+ // --- Prepare for full operation ---
+ LOG("Starting card");
+ REG32(Card, REG_CTRL) = CTRL_SLU|CTRL_ASDE; // Link up, auto speed detection
+ REG32(Card, REG_IMS) = 0x1F6DC; // Interrupt mask
+ (void)REG32(Card, REG_ICR); // Discard pending interrupts
+ REG32(Card, REG_RCTL) |= RCTL_EN;
+ LEAVE('i', 0);
+ return 0;
+}
+
+Uint16 E1000_int_ReadEEPROM(tCard *Card, Uint8 WordIdx)
+{
+ REG32(Card, REG_EERD) = ((Uint32)WordIdx << 8) | 1;
+ Uint32 tmp;
+ while( !((tmp = REG32(Card, REG_EERD)) & (1 << 4)) ) {
+ // TODO: use something like Time_MicroDelay instead
+ Time_Delay(1);
+ }
+
+ return tmp >> 16;
}
--- /dev/null
+/*
+ * Acess2 E1000 Network Driver
+ * - By John Hodge (thePowersGang)
+ *
+ * e1000_hw.h
+ * - Hardware Definitions
+ */
+#ifndef _E1000_HW_H_
+#define _E1000_HW_H_
+
+typedef struct sRXDesc tRXDesc;
+typedef struct sTXDesc tTXDesc;
+
+#define RXD_STS_PIF (1<<7) // Passed inexact filter (multicast, probably)
+#define RXD_STS_IPCS (1<<6) // IP Checksum was calculated
+#define RXD_STS_TCPCS (1<<5) // TCP Checksum was calculated
+#define RXD_STS_RSV (1<<4) // reserved
+#define RXD_STS_VP (1<<3) // Packet was 802.1q tagged
+#define RXD_STS_IXSM (1<<2) // Ignore IPCS/TCPS
+#define RXD_STS_EOP (1<<1) // Last descriptor in apcket
+#define RXD_STS_DD (1<<0) // Descriptor Done, buffer data is now valid
+
+#define RXD_ERR_RXE (1<<7) // RX Error
+#define RXD_ERR_IPE (1<<6) // IP Checksum Error
+#define RXD_ERR_TCPE (1<<5) // TCP/UDP Checksum Error
+#define RXD_ERR_CXE (1<<4) // Carrier Extension Error (GMII cards [82544GC/EI] only)
+#define RXD_ERR_SEQ (1<<2) // Sequence Error (aka Framing Error)
+#define RXD_ERR_SE (1<<1) // Symbol error (TBI mode)
+#define RXD_ERR_CE (1<<0) // CRC Error / Alignmnet Error
+
+struct sRXDesc
+{
+ Uint64 Buffer;
+ Uint16 Length;
+ Uint16 Checksum;
+ Uint8 Status;
+ Uint8 Errors;
+ Uint16 Special;
+} PACKED;
+
+#define TXD_CMD_IDE (1<<7) // Interrupt delay enable
+#define TXD_CMD_VLE (1<<6) // VLAN Packet enable
+#define TXD_CMD_DEXT (1<<5) // Use extended descriptor format (TODO)
+#define TXD_CMD_RPS (1<<4) // GC/EI Report Packet Set
+#define TXD_CMD_RS (1<<3) // Report Status
+#define TXD_CMD_IC (1<<2) // Insert checksum at indicated offset
+#define TXD_CMD_IFCS (1<<1) // Insert frame check sum
+#define TXD_CMD_EOP (1<<0) // End of packet
+
+#define TXD_STS_TU (1<<3) // [GC/EI] Transmit Underrun
+#define TXD_STS_LC (1<<2) // Late collision
+#define TXD_STS_EC (1<<1) // Excess collisions
+#define TXD_STS_DD (1<<0) // Descriptor Done
+
+struct sTXDesc
+{
+ Uint64 Buffer;
+ Uint16 Length;
+ Uint8 CSO; // TCP Checksum offset
+ Uint8 CMD;
+ Uint8 Status;
+ Uint8 CSS; // TCP Checksum start
+ Uint16 Special;
+} PACKED;
+
+#define TXCD_CMD_IDE (1<<(24+7)) // Interrupt Delay
+#define TXCD_CMD_DEXT (1<<(24+5)) // Descriptor Extension (Must be one)
+#define TXCD_CMD_RS (1<<(24+3)) // Report Status
+#define TXCD_CMD_TSE (1<<(24+2)) // TCP Segmentation Enable
+#define TXCD_CMD_IP (1<<(24+1)) // IP version (1=IPv4, 0=IPv6)
+#define TXCD_CMD_TCP (1<<(24+0)) // Packet Type (1=TCP, 0=Other)
+
+#define TXCD_STS_DD (1<<0) // Descriptor Done
+
+struct sTXCtxDesc
+{
+ Uint8 IPCSS; // IP Checksum Start
+ Uint8 IPCSO; // IP Checksum Offset
+ Uint16 IPCSE; // IP Checksum Ending (last byte)
+ Uint8 TUCSS; // TCP/UDP Checksum Start
+ Uint8 TUCSO; // TCP/UDP Checksum Offset
+ Uint16 TUCSE; // TCP/UDP Checksum Ending
+ Uint32 CmdLen; // [0:19] Length, [20:23] DTYP (0), [24:31] TUCMD
+ Uint8 Status;
+ Uint8 HdrLen; // Header length
+ Uint16 MSS; // Maximum segment size
+} PACKED;
+
+#define TXDD_CMD_IDE (1<<(24+7)) // Interrupt Delay
+#define TXDD_CMD_VLE (1<<(24+6)) // VLAN Enable
+#define TXDD_CMD_DEXT (1<<(24+5)) // Descriptor Extension
+#define TXDD_CMD_RPS (1<<(24+4)) // [GC/EI] Report Packet Sent
+#define TXDD_CMD_RS (1<<(24+3)) // Report Status
+#define TXDD_CMD_TSE (1<<(24+2)) // TCP Segmentation Enable
+#define TXDD_CMD_IFCS (1<<(24+1)) // Insert FCS
+#define TXDD_CMD_EOP (1<<(24+0)) // End of packet
+
+#define TXDD_STS_TU (1<<3) // [GC/EI] Transmit Underrun
+#define TXDD_STS_LC (1<<2) // Late collision
+#define TXDD_STS_EC (1<<1) // Excess collisions
+#define TXDD_STS_DD (1<<0) // Descriptor Done
+
+#define TXDD_POPTS_TXSM (1<<1) // Insert TCP/UDP Checksum
+#define TXDD_POPTS_IXSM (1<<0) // Insert IP Checksum
+
+struct sTXDataDesc
+{
+ Uint64 Buffer;
+ Uint32 CmdLen; // [0:19] Length, [20:23] DTYP (1), [24:31] DCMD
+ Uint8 Status;
+ Uint8 POpts; // Packet option field
+ Uint16 Special;
+};
+
+#define REG32(card,ofs) (((volatile Uint32*)card->MMIOBase)[ofs/4])
+#define REG64(card,ofs) (((volatile Uint64*)card->MMIOBase)[ofs/8])
+
+enum eRegs
+{
+ REG_CTRL = 0x000,
+ REG_STATUS = 0x008,
+ REG_EECD = 0x010,
+ REG_EERD = 0x014,
+ REG_CTRL_EXT = 0x018,
+ REG_FLA = 0x01C,
+ REG_MDIC = 0x020,
+ REG_FCAL = 0x028, // 64-bit value
+ REG_FCAH = 0x02C,
+ REG_FCT = 0x030,
+
+ REG_ICR = 0x0C0, // Interrupt cause read
+ REG_ITR = 0x0C4, // Interrupt throttling
+ REG_ICS = 0x0C8, // Interrupt cause set
+ REG_IMS = 0x0D0, // Interrupt mask set/read
+ REG_IMC = 0x0D8, // Interrupt mask clear
+
+ REG_TXCW = 0x178, // N/A for 82540EP/EM, 82541xx and 82547GI/EI
+ REG_RXCW = 0x180, // ^^
+ REG_LEDCTL = 0xE00,
+
+ REG_RCTL = 0x100,
+ REG_RDBAL = 0x2800,
+ REG_RDLEN = 0x2808,
+ REG_RDH = 0x2810,
+ REG_RDT = 0x2818,
+ REG_RDTR = 0x2820,
+ REG_RXDCTL = 0x2820, // Receive Descriptor Control
+
+ REG_TCTL = 0x400,
+ REG_TDBAL = 0x3800,
+ REG_TDLEN = 0x3808,
+ REG_TDH = 0x3810,
+ REG_TDT = 0x3818,
+ REG_TDIV = 0x3820, // Transmit Interrupt Delay Value
+ REG_TXDCTL = 0x3828, // Transmit Descriptor Control
+
+ REG_MTA0 = 0x5200, // 128 entries
+ REG_RA0 = 0x5400, // 16 entries of ?
+ REG_VFTA0 = 0x6500, // 128 entries
+};
+
+#define CTRL_FD (1 << 0) // Full-Duplex
+#define CTRL_LRST (1 << 3) // Link Reset
+#define CTRL_ASDE (1 << 5) // Auto-Speed Detection Enable
+#define CTRL_SLU (1 << 6) // Set link up
+// TODO: CTRL_*
+#define CTRL_SDP0_DATA (1 << 18) // Software Programmable IO #0 (Data)
+#define CTRL_SDP1_DATA (1 << 19) // Software Programmable IO #1 (Data)
+// TODO: CTRL_*
+#define CTRL_RST (1 << 26) // Software reset (cleared by hw once complete)
+#define CTRL_RFCE (1 << 27) // Receive Flow Control Enable
+#define CTRL_TFCE (1 << 28) // Transmit Flow Control Enable
+#define CTRL_VME (1 << 30) // VLAN Mode Enable
+#define CTRL_PHY_RST (1 << 31) // PHY Reset (3uS)
+
+#define ICR_TXDW (1 << 0) // Transmit Descriptor Written Back
+#define ICR_TXQE (1 << 1) // Transmit Queue Empty
+#define ICR_LSC (1 << 2) // Link Status Change
+#define ICR_RXSEQ (1 << 3) // Receive Sequence Error (Framing Error)
+#define ICR_RXDMT0 (1 << 4) // Receive Descriptor Minimum Threshold Reached (need more RX descs)
+#define ICR_XRO (1 << 6) // Receiver overrun
+#define ICR_RXT0 (1 << 7) // Receiver Timer Interrupt
+#define ICR_MDAC (1 << 9) // MDI/O Access Complete
+#define ICR_RXCFG (1 << 10) // Receiving /C/ ordered sets
+#define ICR_PHYINT (1 << 12) // PHY Interrupt
+#define ICR_GPI_SDP6 (1 << 13) // GP Interrupt SDP6/SDP2
+#define ICR_GPI_SDP7 (1 << 14) // GP Interrupt SDP7/SDP3
+#define ICR_TXD_LOW (1 << 15) // Transmit Descriptor Low Threshold hit
+#define ICR_SRPD (1 << 16) // Small Receive Packet Detected
+
+#define RCTL_EN (1 << 1) // Receiver Enable
+#define RCTL_SBP (1 << 2) // Store Bad Packets
+#define RCTL_UPE (1 << 3) // Unicast Promiscuous Enabled
+#define RCTL_MPE (1 << 4) // Multicast Promiscuous Enabled
+#define RCTL_LPE (1 << 5) // Long Packet Reception Enable
+#define RCTL_LBM (3 << 6) // Loopback mode
+enum {
+ RCTL_LBM_NONE = 0 << 6, // - No Loopback
+ RCTL_LBM_UD1 = 1 << 6, // - Undefined
+ RCTL_LBM_UD2 = 2 << 6, // - Undefined
+ RCTL_LBM_PHY = 3 << 6, // - PHY or external SerDes loopback
+};
+#define RCTL_RDMTS (3 << 8) // Receive Descriptor Minimum Threshold Size
+enum {
+ RCTL_RDMTS_1_2 = 0 << 8, // - 1/2 RDLEN free
+ RCTL_RDMTS_1_4 = 1 << 8, // - 1/4 RDLEN free
+ RCTL_RDMTS_1_8 = 2 << 8, // - 1/8 RDLEN free
+ RCTL_RDMTS_RSVD = 3 << 8, // - Reserved
+};
+#define RCTL_MO (3 << 12) // Multicast Offset
+enum {
+ RCTL_MO_36 = 0 << 12, // bits [47:36] of multicast address
+ RCTL_MO_35 = 1 << 12, // bits [46:35] of multicast address
+ RCTL_MO_34 = 2 << 12, // bits [45:34] of multicast address
+ RCTL_MO_32 = 3 << 12, // bits [43:32] of multicast address
+};
+#define RCTL_BAM (1 << 15) // Broadcast Accept Mode
+#define RCTL_BSIZE (1 << 16) // Receive Buffer Size
+enum {
+ RCTL_BSIZE_2048 = (0 << 16), // - 2048 bytes
+ RCTL_BSIZE_1024 = (1 << 16), // - 1024 bytes
+ RCTL_BSIZE_512 = (2 << 16), // - 512 bytes
+ RCTL_BSIZE_256 = (3 << 16), // - 256 bytes
+
+ RCTL_BSIZE_RSVD = (0 << 16)|(1 << 25), // - Reserved
+ RCTL_BSIZE_16K = (1 << 16)|(1 << 25), // - 16384 bytes
+ RCTL_BSIZE_8192 = (2 << 16)|(1 << 25), // - 8192 bytes
+ RCTL_BSIZE_4096 = (3 << 16)|(1 << 25), // - 4096 bytes
+};
+#define RCTL_VFE (1 << 18) // VLAN Filter Enable
+#define RCTL_CFIEN (1 << 19) // Canoical Form Indicator Enable
+#define RCTL_CFI (1 << 20) // Value to check the CFI for [Valid if CFIEN]
+#define RCTL_DPF (1 << 22) // Discard Pause Frames
+#define RCTL_PMCF (1 << 23) // Pass MAC Control Frames
+#define RCTL_BSEX (1 << 25) // Buffer Size Extension (multply BSIZE by 16, used in BSIZE enum)
+#define RCTL_SECRC (1 << 26) // Strip Ethernet CRC
+
+#define TCTL_EN (1 << 1) // Transmit Enable
+#define TCTL_PSP (1 << 3) // Pad Short Packets
+#define TCTL_CT (255 << 4) // Collision Threshold
+#define TCTL_CT_ofs 4
+#define TCTL_COLD (1023 << 12) // Collision Distance
+#define TCTL_COLD_ofs 12
+#define TCTL_SWXOFF (1 << 22) // Software XOFF Transmission
+#define TCTL_RTLC (1 << 24) // Retransmit on Late Collision
+#define TCTL_NRTU (1 << 25) // No retransmit on underrun [82544GC/EI]
+
+#endif
+