Intel Ethernet i217: Difference between revisions

Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content deleted Content added
No edit summary
No edit summary
Line 2: Line 2:


I am writing this Wiki as a demonstration of my own experience of getting a working driver for the Intel I217 and 82577LM network cards to work, on a real native bare metal hardware, namely Thinkpads W540 and W510. Linux uses the e1000e network driver for those cards. I have started from a working e1000 driver that I have developed for my OS and which is operational on Qemu, Bochs, and VirtualBox. The main objective of this Wiki is to try to highlight the differences and the addition needed on an operations e1000 driver to work handle network cards that work with the e1000e. So the provided knowledge in this wiki might be applicable on other Intel interfaces. For completion, I will present in this wiki my e1000 driver with the additions that made it work on those native NICs, I217 and 82577LM. I built my original e1000 driver based on information from OSDev and some hobby operating systems on github.
I am writing this Wiki as a demonstration of my own experience of getting a working driver for the Intel I217 and 82577LM network cards to work, on a real native bare metal hardware, namely Thinkpads W540 and W510. Linux uses the e1000e network driver for those cards. I have started from a working e1000 driver that I have developed for my OS and which is operational on Qemu, Bochs, and VirtualBox. The main objective of this Wiki is to try to highlight the differences and the addition needed on an operations e1000 driver to work handle network cards that work with the e1000e. So the provided knowledge in this wiki might be applicable on other Intel interfaces. For completion, I will present in this wiki my e1000 driver with the additions that made it work on those native NICs, I217 and 82577LM. I built my original e1000 driver based on information from OSDev and some hobby operating systems on github.

It is very important to highlight that this wiki does not utilize all the features in the above NICs, but it show how to configure the NICs for basic functionality such as initialization, read packets, and write packets.


== Card Addresses and Data Structures==
== Card Addresses and Data Structures==
Line 261: Line 263:




The first thing you will need to do after detecting the BAR0 type and the existence of the EEPROM is to read the hardware MAC address of the NIC. The following method reads the hardware mac address based. If an EEPROM exists it will read it from the EEPROM else it will read it from address 0x5400 where it should be located in that case.
The first thing you will need to do after detecting the BAR0 type and the existence of the EEPROM is to read the hardware MAC address of the NIC. The following method reads the hardware mac address based. If an EEPROM exists it will read it from the EEPROM else it will read it from address 0x5400 where it should be located in that case. It is very important to detect if an EEPROM exists or not prior to reading the MAC address.


<source lang="c">
<source lang="c">
Line 295: Line 297:
return true;
return true;
}
}
</source>


Now, we need to configure the transmit and receive descriptor buffers, here are the implementation of the corresponding methods. The rxinit method is identical to the one I use for my e1000 driver. The difference is in txinit

<source lang="c">

void E1000::rxinit()
{
uint8_t * ptr;
struct e1000_rx_desc *descs;

// Allocate buffer for receive descriptors. For simplicity, in my case khmalloc returns a virtual address that is identical to it physical mapped address.
// In your case you should handle virtual and physical addresses as the addresses passed to the NIC should be physical ones
ptr = (uint8_t *)(kmalloc_ptr->khmalloc(sizeof(struct e1000_rx_desc)*E1000_NUM_RX_DESC + 16));

descs = (struct e1000_rx_desc *)ptr;
for(int i = 0; i < E1000_NUM_RX_DESC; i++)
{
rx_descs[i] = (struct e1000_rx_desc *)((uint8_t *)descs + i*16);
rx_descs[i]->addr = (uint64_t)(uint8_t *)(kmalloc_ptr->khmalloc(8192 + 16));
rx_descs[i]->status = 0;
}

writeCommand(REG_TXDESCLO, (uint32_t)((uint64_t)ptr >> 32) );
writeCommand(REG_TXDESCHI, (uint32_t)((uint64_t)ptr & 0xFFFFFFFF));

writeCommand(REG_RXDESCLO, (uint64_t)ptr);
writeCommand(REG_RXDESCHI, 0);

writeCommand(REG_RXDESCLEN, E1000_NUM_RX_DESC * 16);

writeCommand(REG_RXDESCHEAD, 0);
writeCommand(REG_RXDESCTAIL, E1000_NUM_RX_DESC-1);
rx_cur = 0;
writeCommand(REG_RCTRL, RCTL_EN| RCTL_SBP| RCTL_UPE | RCTL_MPE | RCTL_LBM_NONE | RTCL_RDMTS_HALF | RCTL_BAM | RCTL_SECRC | RCTL_BSIZE_2048);
}


void E1000::txinit()
{
uint8_t * ptr;
struct e1000_tx_desc *descs;
// Allocate buffer for receive descriptors. For simplicity, in my case khmalloc returns a virtual address that is identical to it physical mapped address.
// In your case you should handle virtual and physical addresses as the addresses passed to the NIC should be physical ones
ptr = (uint8_t *)(kmalloc_ptr->khmalloc(sizeof(struct e1000_tx_desc)*E1000_NUM_TX_DESC + 16));

descs = (struct e1000_tx_desc *)ptr;
for(int i = 0; i < E1000_NUM_TX_DESC; i++)
{
tx_descs[i] = (struct e1000_tx_desc *)((uint8_t*)descs + i*16);
tx_descs[i]->addr = 0;
tx_descs[i]->cmd = 0;
tx_descs[i]->status = TSTA_DD;
}

writeCommand(REG_TXDESCHI, (uint32_t)((uint64_t)ptr >> 32) );
writeCommand(REG_TXDESCLO, (uint32_t)((uint64_t)ptr & 0xFFFFFFFF));


//now setup total length of descriptors
writeCommand(REG_TXDESCLEN, E1000_NUM_TX_DESC * 16);


//setup numbers
writeCommand( REG_TXDESCHEAD, 0);
writeCommand( REG_TXDESCTAIL, 0);
tx_cur = 0;
writeCommand(REG_TCTRL, TCTL_EN
| TCTL_PSP
| (15 << TCTL_CT_SHIFT)
| (64 << TCTL_COLD_SHIFT)
| TCTL_RTLC);

// This line of code overrides the one before it but I left both to highlight that the previous one works with e1000 cards, but for the e1000e cards
// you should set the TCTRL register as follows. For detailed description of each bit, please refer to the Intel Manual.
// In the case of I217 and 82577LM packets will not be sent if the TCTRL is not configured using the following bits.
writeCommand(REG_TCTRL, 0b0110000000000111111000011111010);
writeCommand(REG_TIPG, 0x0060200A);

}


</source>
</source>