Modules/E1000 - Partially functional driver
authorJohn Hodge <[email protected]>
Fri, 26 Apr 2013 05:37:54 +0000 (13:37 +0800)
committerJohn Hodge <[email protected]>
Fri, 26 Apr 2013 05:38:32 +0000 (13:38 +0800)
- Has caused qemu to segfault
- Causes system lockups in some cases

KernelLand/Modules/Network/E1000/e1000.c
KernelLand/Modules/Network/E1000/e1000.h
KernelLand/Modules/Network/E1000/e1000_hw.h [new file with mode: 0644]

index b225d3d..644d694 100644 (file)
@@ -3,7 +3,7 @@
  * - 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)
@@ -45,16 +114,349 @@ 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;
 }
index d8b864f..2a0eb8e 100644 (file)
@@ -8,6 +8,45 @@
 #ifndef _E1000_H_
 #define _E1000_H_
 
+#include "e1000_hw.h"
+
+#include <semaphore.h>
+#include <mutex.h>
+#include <IPStack/include/buffer.h>
+
+#define NUM_TX_DESC    (PAGE_SIZE/sizeof(struct sTXDesc))
+#define NUM_RX_DESC    (PAGE_SIZE/sizeof(struct sRXDesc))
+
+#define RX_DESC_BSIZE  4096
+#define RX_DESC_BSIZEHW        RCTL_BSIZE_4096
+
+typedef struct sCard
+{
+       tPAddr  MMIOBasePhys;
+        int    IRQ;
+       Uint16  IOBase;
+       volatile void   *MMIOBase;
+
+       tMutex  lRXDescs;
+        int    FirstUnseenRXD;
+        int    LastUnseenRXD;
+       void    *RXBuffers[NUM_RX_DESC];
+       volatile tRXDesc        *RXDescs;
+       tSemaphore      AvailPackets;
+       struct sCard    *RXBackHandles[NUM_RX_DESC];    // Pointers to this struct, offset used to select desc
+       
+       tMutex  lTXDescs;
+        int    FirstFreeTXD;
+        int    LastFreeTXD;
+       volatile tTXDesc        *TXDescs;
+       tSemaphore      FreeTxDescs;
+
+       tIPStackBuffer  *TXSrcBuffers[NUM_TX_DESC];
+
+       Uint8   MacAddr[6];
+
+       void    *IPStackHandle;
+} tCard;
 
 
 #endif
diff --git a/KernelLand/Modules/Network/E1000/e1000_hw.h b/KernelLand/Modules/Network/E1000/e1000_hw.h
new file mode 100644 (file)
index 0000000..cd182eb
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * 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
+

UCC git Repository :: git.ucc.asn.au