I am currently working on a driver for the i219-V network card. While there unfortunately is little documentation on its software interface, I was able to figure out most of its functionality using old manuals and Intel's official e1000e driver; after understanding the relation between the PCH and PHY things are quite similar to older e1000 cards. At this point my driver is able to transmit packets on real hardware, as I successfully tested with a hand-crafted ARP packet and Wireshark.
Unfortunately, receiving does not work. I already tried debugging that problem by printing the configuration and status registers; but from my point of view, the respective flags seem to be correct. The various statistics registers did not change, even when I was sure that the card *must* have received a packet (e.g. when getting the ARP response). Manually examing the receive descriptor ring did not show anything, too. Right now it looks like the PHY never receives packets, either because there is some misconfiguration on a very low level or the packet filtering is not properly initialized.
I've already checked with the e1000e driver, but it implements very many features and thus makes it easy to miss something important. Intel's driver seems to be doing a full hardware reset on startup; but since transmitting works already, I hope to avoid this.
I put the working parts of my code into a Wiki article draft; I will be happy to turn it into a full Wiki article, since digging through the documentation took me a lot of time.
The receiving code looks like this:
Code: Select all
// Allocate receive data buffer
uint64_t rxBufferMemPhy;
rxBufferMem = heap_alloc_contiguous(RX_DESC_COUNT * RX_BUFFER_SIZE, VM_R | VM_W, &rxBufferMemPhy);
if(!rxBufferMem)
panic("Could not allocate i219 receive data buffer.");
// Allocate and initialize receive descriptor buffer
uint64_t rxDescriptorsPhy;
rxDescriptors = heap_alloc_contiguous(RX_DESC_COUNT * sizeof(rx_desc_t), VM_R | VM_W, &rxDescriptorsPhy);
if(!rxDescriptors)
panic("Could not allocate i219 receive descriptor buffer.");
for(int i = 0; i < RX_DESC_COUNT; ++i)
{
// Initialize descriptor
rx_desc_t *currDesc = &rxDescriptors[i];
currDesc->address = rxBufferMemPhy + i * RX_BUFFER_SIZE;
currDesc->status = 0;
}
// Pass receive descriptor buffer
i219_write(E1000_REG_RDBAH, rxDescriptorsPhy >> 32);
i219_write(E1000_REG_RDBAL, rxDescriptorsPhy & 0xFFFFFFFF);
i219_write(E1000_REG_RDLEN, RX_DESC_COUNT * sizeof(rx_desc_t));
i219_write(E1000_REG_RDH, 0);
i219_write(E1000_REG_RDT, RX_DESC_COUNT - 1);
rxTail = RX_DESC_COUNT - 1;
// Enable receiver
uint32_t rctl = i219_read(E1000_REG_RCTL);
rctl |= E1000_RCTL_EN; // EN (Receiver Enable)
rctl &= ~E1000_RCTL_SBP; // SBP (Store Pad Packets)
rctl |= E1000_RCTL_BAM; // BAM (Broadcast Accept Mode)
rctl &= ~E1000_RCTL_SZ_4096;
rctl |= E1000_RCTL_SZ_2048; // BSIZE = 2048 (Receive Buffer Size)
rctl &= ~E1000_RCTL_BSEX;
rctl |= E1000_RCTL_SECRC; // SECRC (Strip Ethernet CRC)
i219_write(E1000_REG_RCTL, rctl);