summaryrefslogtreecommitdiff
path: root/driver/e1000.c
diff options
context:
space:
mode:
Diffstat (limited to 'driver/e1000.c')
-rw-r--r--driver/e1000.c262
1 files changed, 179 insertions, 83 deletions
diff --git a/driver/e1000.c b/driver/e1000.c
index 8233993..35e106e 100644
--- a/driver/e1000.c
+++ b/driver/e1000.c
@@ -1,10 +1,49 @@
//https://github.com/blanham/ChickenOS/blob/master/src/device/net/e1000.c
//https://wiki.osdev.org/Intel_Ethernet_i217
+//https://github.com/torvalds/linux/blob/master/drivers/net/ethernet/intel/e1000/e1000_hw.c
+//https://github.com/qemu/qemu/blob/master/hw/net/e1000.c
+//registers etc. verified from pdf at https://pdos.csail.mit.edu/6.828/2006/readings/hardware/8254x_GBe_SDM.pdf
+
#include <stdint.h>
#include "log.h"
#include "e1000.h"
#include "kmalloc.h"
+#define REG_CTRL 0x0000 // device control R/W
+#define REG_CTRL_LRST 1<<3 // Link Reset / transition to 0 to initiates auto-negotiation
+#define REG_CTRL_SLU 1<<6 // Set Link Up / signal forced
+
+#define REG_STATUS 0x0008 // device status R
+
+#define REG_EERD 0x0014 // EEPROM / R
+
+#define REG_ICR 0x00C0 // interrupt cause / (reading clears) / R
+#define REG_IMS 0x00D0 // interrupt mask / R/W
+
+// RECEIVE
+
+#define REG_RCTL 0x0100 // Receive Control R/W
+#define REG_RDBAL 0x2800
+#define REG_RDBAH 0x2804
+#define REG_RDLEN 0x2808
+#define REG_RDH 0x2810
+#define REG_RDT 0x2818
+
+#define REG_MTA 0x5200 // multicast table array
+
+/// TRANSMIT
+
+#define REG_TCTL 0x0400 // Transmit Control R/W
+#define REG_TDBAL 0x3800
+#define REG_TDBAH 0x3804
+#define REG_TDLEN 0x3808
+#define REG_TDH 0x3810
+#define REG_TDT 0x3818
+
+// check?
+#define TSTA_DD (1 << 0) // Descriptor Done
+/*
+///
#define INTEL_VEND 0x8086 // Vendor ID for Intel
#define E1000_DEV 0x100E // Device ID for the e1000 Qemu, Bochs, and VirtualBox emmulated NICs
#define E1000_I217 0x153A // Device ID for Intel I217
@@ -13,36 +52,20 @@
// I have gathered those from different Hobby online operating systems instead of getting them one by one from the manual
-#define REG_CTRL 0x0000
-#define REG_ICR 0x00C0 // interrupt cause register
-#define REG_STATUS 0x0008
-#define REG_EEPROM 0x0014
#define REG_CTRL_EXT 0x0018
-#define REG_IMASK 0x00D0
-#define REG_RCTRL 0x0100
-#define REG_RXDESCLO 0x2800
-#define REG_RXDESCHI 0x2804
-#define REG_RXDESCLEN 0x2808
-#define REG_RXDESCHEAD 0x2810
-#define REG_RXDESCTAIL 0x2818
-
-#define REG_TCTRL 0x0400
-#define REG_TXDESCLO 0x3800
-#define REG_TXDESCHI 0x3804
-#define REG_TXDESCLEN 0x3808
-#define REG_TXDESCHEAD 0x3810
-#define REG_TXDESCTAIL 0x3818
-#define REG_RDTR 0x2820 // RX Delay Timer Register
+
#define REG_RXDCTL 0x3828 // RX Descriptor Control
+
+#define REG_RDTR 0x2820 // RX Delay Timer Register
#define REG_RADV 0x282C // RX Int. Absolute Delay Timer
#define REG_RSRPD 0x2C00 // RX Small Packet Detect Interrupt
-#define REG_TIPG 0x0410 // Transmit Inter Packet Gap
-#define ECTRL_SLU 0x40 //set link up
+#define REG_TIPG 0x0410 // Transmit Inter Packet Gap
+#define CTRL_SLU 0x40 //set link up
#define RCTL_EN (1 << 1) // Receiver Enable
@@ -101,22 +124,26 @@
#define TSTA_EC (1 << 1) // Excess Collisions
#define TSTA_LC (1 << 2) // Late Collision
#define LSTA_TU (1 << 3) // Transmit Underrun
+*/
// buffers
#define E1000_NUM_RX_DESC 32
#define E1000_NUM_TX_DESC 8
struct e1000_rx_desc {
- volatile uint64_t addr;
- volatile uint16_t length;
- volatile uint16_t checksum;
- volatile uint8_t status;
- volatile uint8_t errors;
- volatile uint16_t special;
+ volatile uint32_t addr_lo; // 4
+ volatile uint32_t addr_hi; // 4
+ volatile uint16_t length; // 2
+ volatile uint16_t checksum; // 2
+ volatile uint8_t status; // 1
+ volatile uint8_t errors; // 1
+ volatile uint16_t special; // 2
+ // sum: 16 byte
} __attribute__((packed));
struct e1000_tx_desc {
- volatile uint64_t addr;
+ volatile uint32_t addr_lo; // 4
+ volatile uint32_t addr_hi; // 4
volatile uint16_t length;
volatile uint8_t cso;
volatile uint8_t cmd;
@@ -151,6 +178,18 @@ void writeCommand( uint16_t p_address, uint32_t p_value)
// }
}
+void writeCommand16( uint16_t p_address, uint16_t p_value)
+{
+// if ( bar_type == 0 )
+// {
+ (*((volatile uint16_t*)(mem_base+p_address)))=(p_value);
+// }
+// else
+// {
+ //outportl(io_base, p_address);
+ //outportl(io_base + 4, p_value);
+// }
+}
uint32_t readCommand( uint16_t p_address)
{
// if ( bar_type == 0 )
@@ -167,11 +206,11 @@ uint32_t readCommand( uint16_t p_address)
bool detectEEProm()
{
uint32_t val = 0;
- writeCommand(REG_EEPROM, 0x1);
+ writeCommand(REG_EERD, 0x1);
for(int i = 0; i < 1000 && ! eerprom_exists; i++)
{
- val = readCommand( REG_EEPROM);
+ val = readCommand( REG_EERD);
if(val & 0x10)
eerprom_exists = true;
else
@@ -188,13 +227,13 @@ uint32_t eepromRead( uint8_t addr)
uint32_t tmp = 0;
if ( eerprom_exists)
{
- writeCommand( REG_EEPROM, (1) | ((uint32_t)(addr) << 8) );
- while( !((tmp = readCommand(REG_EEPROM)) & (1 << 4)) );
+ writeCommand( REG_EERD, (1) | ((uint32_t)(addr) << 8) );
+ while( !((tmp = readCommand(REG_EERD)) & (1 << 4)) );
}
else
{
- writeCommand( REG_EEPROM, (1) | ((uint32_t)(addr) << 2) );
- while( !((tmp = readCommand(REG_EEPROM)) & (1 << 1)) );
+ writeCommand( REG_EERD, (1) | ((uint32_t)(addr) << 2) );
+ while( !((tmp = readCommand(REG_EERD)) & (1 << 1)) );
}
data = (uint16_t)((tmp >> 16) & 0xFFFF);
return data;
@@ -240,16 +279,16 @@ void rxinit()
// 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 onesk
- uint32_t alloc_pages=1+(sizeof(struct e1000_rx_desc)*E1000_NUM_RX_DESC + 16)/4096;
-
- ptr = kballoc(alloc_pages);
+ ptr = kballoc(1); // one page can hold up to 256 descriptors.
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 = kballoc(3); // what a waste :( TODO!
- rx_descs[i]->status = 0;
+ rx_descs[i] = descs;
+ rx_descs[i]->addr_lo = kballoc(1); //TODO: do not waste!
+ rx_descs[i]->addr_hi = 0;
+ rx_descs[i]->status = 0;
+ descs++;
}
/*
@@ -260,66 +299,93 @@ void rxinit()
writeCommand(REG_TXDESCHI, (uint32_t)(ptr));
*/
- writeCommand(REG_RXDESCLO, 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);
+
+ writeCommand(REG_RDBAL, ptr);
+ writeCommand(REG_RDBAH, 0);
+
+ writeCommand(REG_RDLEN, E1000_NUM_RX_DESC * 16); // size of ALL descirptors in bytes
+
+ writeCommand(REG_RDT, E1000_NUM_RX_DESC); // index of one after last descirptor
+ writeCommand(REG_RDH, 0); // first valid descirptor index
+
rx_cur = 0;
//enable receiving
- //uint32_t flags = (2 << 16) | (1 << 25) | (1 << 26) | (1 << 15) | (1 << 5) | (0 << 8) | (0 << 4) | (0 << 3) | ( 1 << 2);
- //writeCommand(REG_RCTRL, flags);
- writeCommand(REG_RCTRL, RCTL_EN| RCTL_SBP| RCTL_UPE | RCTL_MPE | RCTL_LBM_NONE | RTCL_RDMTS_HALF | RCTL_BAM | RCTL_SECRC | RCTL_BSIZE_2048);
-
+ /*
+ uint32_t flags = (2 << 16) | (1 << 25) | (1 << 26)
+ | (1 << 15) | (1 << 5) | (0 << 8)
+ | (0 << 4) | (0 << 3) | ( 1 << 2) | 1;
+ */
+// writeCommand(REG_RCTRL, flags);
+
+// writeCommand(REG_RCTRL, RCTL_EN| RCTL_SBP| RCTL_UPE | RCTL_MPE | RCTL_LBM_NONE | RTCL_RDMTS_HALF | RCTL_BAM | RCTL_SECRC | RCTL_BSIZE_2048);
+
+
+ writeCommand(REG_RCTL,
+ 1<<1 // receive enable // RCTL.EN
+ |1<<3 // take all unicast
+ |1<<4 // take all multicast
+ |1<<15 // take broadcast // RCTL.BAM
+ |2<<16 // 512 bytes rcv buffers // TCTL.BSIZE
+ // RDTMS
+ // MO
+ //
+ );
+
+
+ /*
+ for(int i = 0; i < E1000_NUM_RX_DESC; i++)
+ {
+ klog("rx_desc %d (0x%08X) at addr %08X %08X, status= %d",i,rx_descs[i],rx_descs[i]->addr_hi,rx_descs[i]->addr_lo, rx_descs[i]->status);
+ }
+ */
}
void txinit()
-{
+{
uint8_t * ptr;
// 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
-
- uint32_t alloc_pages=1+(sizeof(struct e1000_tx_desc)*E1000_NUM_TX_DESC + 16)/4096;
- ptr = kballoc(alloc_pages);
+ ptr = kballoc(1);
struct e1000_tx_desc *descs=ptr;
for(int i = 0; i < E1000_NUM_TX_DESC; i++)
{
tx_descs[i] = descs++;
- tx_descs[i]->addr = 0;
+ tx_descs[i]->addr_lo = 0;
+ tx_descs[i]->addr_hi = 0;
tx_descs[i]->cmd = 0;
tx_descs[i]->status = TSTA_DD;
}
- writeCommand(REG_TXDESCLO, ptr); //physical here
- writeCommand(REG_TXDESCHI, 0);
+ writeCommand(REG_TDBAL, ptr);
+ writeCommand(REG_TDBAH, 0);
//now setup total length of descriptors
- writeCommand(REG_TXDESCLEN, E1000_NUM_TX_DESC * 16); //
+ writeCommand(REG_TDLEN, E1000_NUM_TX_DESC * 16); //
- //setup numbers
- writeCommand( REG_TXDESCHEAD, 0);
- writeCommand( REG_TXDESCTAIL, 0);
- tx_cur = 0;
+ //setup head and tail
+ writeCommand( REG_TDH, 0);
+ writeCommand( REG_TDT, 0);
-
+ tx_cur = 0;
+
+ /*
writeCommand(REG_TCTRL, TCTL_EN
| TCTL_PSP
| (15 << TCTL_CT_SHIFT)
| (64 << TCTL_COLD_SHIFT)
| TCTL_RTLC);
-/*
- writeCommand(REG_TCTRL,
- 1<<0 // enable transmitter
+ */
+
+ writeCommand(REG_TCTL,
+ 1<<1 // enable transmitter
|1<<3 // pad short packets
// ,64<<12 // collision distance
);
- */
+
// 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
@@ -327,6 +393,8 @@ void txinit()
// of I217 and 82577LM packets will not be sent if the TCTRL is not
// configured using the following bits.
+ // TODO: programm tipg
+ //
//writeCommand(REG_TCTRL, 0b0110000000000111111000011111010);
//writeCommand(REG_TIPG, 0x0060200A);
@@ -337,11 +405,11 @@ void e1000_handleReceive()
uint16_t old_cur;
bool got_packet = false;
- while((rx_descs[rx_cur]->status & 0x1))
+ while((rx_descs[rx_cur]->status !=0))
{
got_packet = true;
klog("GOT FIRST PACKET WOOOOW");
- uint8_t *buf = (uint8_t *)(uint32_t)rx_descs[rx_cur]->addr;
+ uint8_t *buf = (uint8_t *)(uint32_t)rx_descs[rx_cur]->addr_lo;
uint16_t len = rx_descs[rx_cur]->length;
// Here you should inject the received packet into your network stack
@@ -349,13 +417,13 @@ void e1000_handleReceive()
rx_descs[rx_cur]->status = 0;
old_cur = rx_cur;
rx_cur = (rx_cur + 1) % E1000_NUM_RX_DESC;
- writeCommand(REG_RXDESCTAIL, old_cur );
+ writeCommand(REG_RDT, old_cur );
}
}
int e1000_sendPacket(const void * p_data, uint16_t p_len)
{
- tx_descs[tx_cur]->addr = p_data; // physical addy of data
+ tx_descs[tx_cur]->addr_lo = p_data; // physical addy of data
tx_descs[tx_cur]->length = p_len; // length in bytes
//tx_descs[tx_cur]->cmd = CMD_EOP | CMD_IFCS | CMD_RS | CMD_RPS;
tx_descs[tx_cur]->cmd = ((1<<3)|3);
@@ -363,7 +431,7 @@ int e1000_sendPacket(const void * p_data, uint16_t p_len)
uint8_t old_cur = tx_cur;
tx_cur = (tx_cur + 1) % E1000_NUM_TX_DESC;
- writeCommand(REG_TXDESCTAIL, tx_cur);
+ writeCommand(REG_TDT, tx_cur);
while(!(tx_descs[old_cur]->status & 0xff));
klog("SENT FIRST PACKET WOOOOW");
return 0;
@@ -377,44 +445,60 @@ void enableInterrupt()
// writeCommand(REG_IMASK ,0x1F6DC);
// writeCommand(REG_IMASK ,0xff & ~4);
- writeCommand(REG_IMASK,0xffffffff);
+// RXT, RXO, RXDMT,RXSEQ, and LSC suugestef for rec.
+ writeCommand(REG_IMS,
+ 1<<6 // RXO // fifo overrun
+ |1<<7 // RXT0 // receiver timer
+ |1<<4 // RXDMT0 // min. threshold
+ |1<<3 // RXSEQ // sequence error
+ |1<<2 // LSC // link status change
+ );
+// readCommand(REG_ICR); // clear cause reg?
}
+// force link up
void e1000_linkup()
{
uint32_t val;
val = readCommand(REG_CTRL);
- writeCommand(REG_CTRL, val | ECTRL_SLU);
+ writeCommand(REG_CTRL, val | REG_CTRL_SLU);
}
void e1000_linkdown()
{
uint32_t val;
val = readCommand(REG_CTRL);
- writeCommand(REG_CTRL, val | ~ECTRL_SLU);
+ writeCommand(REG_CTRL, val | REG_CTRL_SLU);
}
bool e1000_init(uint32_t base)
{
klog("init E1000");
- if(base!=0){ mem_base=base; return true;}
+ klog("interrupt mask: 0x%08X",readCommand(REG_IMS));
+ klog("device status: 0x%08X",readCommand(REG_STATUS));
+ klog("control: 0x%08X",readCommand(REG_CTRL));
+// uint32_t s=readCommand(REG_CTRL)&~REG_CTRL_LRST;
+ // klog("set control: 0x%08X",s);
+ //writeCommand(REG_CTRL,s);
+
+ if(base!=0){ mem_base=base;}
detectEEProm();
if (! readMACAddress()) return false;
klog("mac : %02x:%02x:%02x:%02x:%02x:%02x",mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],mac[6]);
-// e1000_linkup();
+ // clear multicast table
+ for(int i = 0; i < 0x80; i++)writeCommand(REG_MTA + i*4, 0);
- for(int i = 0; i < 0x80; i++)writeCommand(0x5200 + i*4, 0);
+ //e1000_linkup();
enableInterrupt();
txinit();
rxinit();
-
klog("E1000 initialized");
return true;
@@ -427,16 +511,28 @@ void e1000_irq (int irq)
* from each device and must be done before EOI if using the PIC.
Without this, the card will spam interrupts as the int-line will
stay high. */
-
//writeCommand(REG_IMASK, 0x1);
//enableInterrupt();
- int status =0;
-// uint32_t status = readCommand(REG_ICR); // check interrupt cause register.
+
+ // check interrupt cause register.
+ // this acknowledges the interrupt at the same time.
+ uint32_t status = readCommand(REG_ICR);
+ /*
klog("e1000_irq status=%d",status);
+ klog("rx descriptor head is at: %d", readCommand(REG_RDH));
+ klog("rx descriptor tail is at: %d", readCommand(REG_RDT));
+
+ klog("rx descriptor addr lo: 0x%08X", readCommand(REG_RDBAL));
+ klog("rx descriptor addr hi: 0x%08X", readCommand(REG_RDBAH));
+ klog("rx descriptor len: 0x%08X", readCommand(REG_RDLEN));
+
if(status & 0x1) klog ("transmit descriptor written back");
if(status & 0x2) klog ("transmit queue empty");
if(status & 0x4) klog ("link status change");
if(status & 0x8) klog ("receive sequence error");
+ if(status & 1<<6) klog ("fifo overrun");
+ */
+ if(status & 0x80)e1000_handleReceive();
//...
}