+ 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;
+ int txd = Card->LastFreeTXD;
+ int nReleasedAtLastDD = 0;
+ int idxOfLastDD = txd;
+ // Walk descriptors looking for the first non-complete descriptor
+ LOG("TX %i:%i", Card->LastFreeTXD, Card->FirstFreeTXD);
+ while( txd != Card->FirstFreeTXD )
+ {
+ nReleased ++;
+ if(Card->TXDescs[txd].Status & TXD_STS_DD) {
+ nReleasedAtLastDD = nReleased;
+ idxOfLastDD = txd;
+ }
+ txd ++;
+ if(txd == NUM_TX_DESC)
+ txd = 0;
+ }
+ if( nReleasedAtLastDD )
+ {
+ // Unlock buffers
+ txd = Card->LastFreeTXD;
+ LOG("TX unlocking range %i-%i", txd, idxOfLastDD);
+ while( txd != (idxOfLastDD+1)%NUM_TX_DESC )
+ {
+ if( Card->TXSrcBuffers[txd] ) {
+ LOG("- Unlocking %i:%p", txd, Card->TXSrcBuffers[txd]);
+ IPStack_Buffer_UnlockBuffer( Card->TXSrcBuffers[txd] );
+ Card->TXSrcBuffers[txd] = NULL;
+ }
+ txd ++;
+ if(txd == NUM_TX_DESC)
+ txd = 0;
+ }
+ // Update last free
+ Card->LastFreeTXD = txd;
+ Semaphore_Signal(&Card->FreeTxDescs, nReleasedAtLastDD);
+ LOG("nReleased = %i", nReleasedAtLastDD);
+ }
+ else
+ {
+ LOG("No completed TXDs");
+ }
+ }
+
+ if( icr & ICR_LSC )
+ {
+ // Link status change
+ LOG("LSC");
+ // TODO: Detect link drop/raise and poke IPStack
+ }
+
+ if( icr & ICR_RXO )
+ {
+ LOG("RX Overrun");
+ }
+
+ // 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 = NULL;
+ 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;
+ }
+ LOG("Card->RXDescs = %p [%P]", Card->TXDescs, MM_GetPhysAddr((void*)Card->TXDescs));
+ 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;