From 85ab89fd27347032bc1930e31615f71a5078b047 Mon Sep 17 00:00:00 2001
From: Marvin Borner
Date: Mon, 2 Nov 2020 17:59:19 +0100
Subject: Improved error handling in rtl driver

---
 kernel/drivers/rtl8139.c | 108 +++++++++++++++++++++++++++++------------------
 1 file changed, 67 insertions(+), 41 deletions(-)

(limited to 'kernel/drivers/rtl8139.c')

diff --git a/kernel/drivers/rtl8139.c b/kernel/drivers/rtl8139.c
index 2dd0453..7550c75 100644
--- a/kernel/drivers/rtl8139.c
+++ b/kernel/drivers/rtl8139.c
@@ -11,12 +11,13 @@
 #include <print.h>
 #include <rtl8139.h>
 
+static int rtl_irq = 0;
 static u8 mac[6];
-static u8 *rx_buffer;
-static u32 current_packet_ptr;
-
+static u8 *last_packet = NULL;
+static u8 *rtl_rx_buffer;
 static u32 rtl_device_pci = 0;
 static u32 rtl_iobase = 0;
+static u32 cur_rx = 0;
 
 u8 *rtl8139_get_mac()
 {
@@ -28,23 +29,38 @@ u8 *rtl8139_get_mac()
 
 void rtl8139_receive_packet()
 {
-	if (!rtl_device_pci)
-		return;
+	while ((inb(rtl_iobase + RTL_PORT_CMD) & 0x01) == 0) {
+		print("RECEIVE\n");
+
+		int offset = cur_rx % 0x2000;
+
+		u32 *buf_start = (u32 *)((u32)rtl_rx_buffer + offset);
+		u32 rx_status = buf_start[0];
+		u32 rx_size = rx_status >> 16;
 
-	u16 *t = (u16 *)(rx_buffer + current_packet_ptr);
-	u16 length = *(t + 1);
-	t += 2;
+		if (rx_status & (0x0020 | 0x0010 | 0x0004 | 0x0002)) {
+			print("RX Error\n");
+		} else {
+			u8 *buf_8 = (u8 *)&(buf_start[1]);
 
-	void *packet = malloc(length);
-	memcpy(packet, t, length);
-	ethernet_handle_packet(packet, length);
+			last_packet = malloc(rx_size);
 
-	current_packet_ptr = (current_packet_ptr + length + 4 + 3) & (~3);
+			u32 packet_end = (u32)buf_8 + rx_size;
+			if (packet_end > (u32)rtl_rx_buffer + 0x2000) {
+				u32 s = ((u32)rtl_rx_buffer + 0x2000) - (u32)buf_8;
+				memcpy(last_packet, buf_8, s);
+				memcpy((void *)((u32)last_packet + s), rtl_rx_buffer, rx_size - s);
+			} else {
+				memcpy(last_packet, buf_8, rx_size);
+			}
 
-	if (current_packet_ptr >= RX_BUF_SIZE)
-		current_packet_ptr -= RX_BUF_SIZE;
+			u16 *t = (u16 *)(rtl_rx_buffer + cur_rx);
+			ethernet_handle_packet((struct ethernet_packet *)last_packet, *(t + 1));
+		}
 
-	outw(rtl_iobase + RTL_PORT_RXPTR, current_packet_ptr - 0x10);
+		cur_rx = (cur_rx + rx_size + 4 + 3) & ~3;
+		outw(rtl_iobase + RTL_PORT_RXPTR, cur_rx - 16);
+	}
 }
 
 static u8 tsad_array[4] = { 0x20, 0x24, 0x28, 0x2C };
@@ -55,7 +71,7 @@ void rtl8139_send_packet(void *data, u32 len)
 	if (!rtl_device_pci)
 		return;
 
-	/* printf("Sending packet %d\n\n", len); */
+	printf("Sending packet %d\n\n", len);
 	outl(rtl_iobase + tsad_array[tx_current], (u32)data);
 	outl(rtl_iobase + tsd_array[tx_current++], len);
 	if (tx_current > 3)
@@ -69,23 +85,16 @@ void rtl8139_find(u32 device, u16 vendor_id, u16 device_id, void *extra)
 	}
 }
 
-// TODO: Fix late packet arrival
 void rtl8139_irq_handler()
 {
-	if (!rtl_device_pci)
-		return;
-
-	/* print("\nRTL INT!\n"); */
+	print("RTL INT!\n");
 	u16 status = inw(rtl_iobase + RTL_PORT_ISR);
+	if (!status)
+		return;
+	outw(rtl_iobase + RTL_PORT_ISR, status);
 
-	if (status & RTL_TOK) {
-		print("Sent packet\n");
-		rtl8139_receive_packet();
-	} else if (status & RTL_ROK) {
+	if (status & 0x01 || status & 0x02)
 		rtl8139_receive_packet();
-	}
-
-	outw(rtl_iobase + RTL_PORT_ISR, 0x5);
 }
 
 void rtl8139_init()
@@ -99,12 +108,15 @@ void rtl8139_init()
 		pci_write_field(rtl_device_pci, PCI_COMMAND, command_reg);
 	}
 
+	rtl_irq = pci_get_interrupt(rtl_device_pci);
+	irq_install_handler(rtl_irq, rtl8139_irq_handler);
+
 	u32 rtl_bar0 = pci_read_field(rtl_device_pci, PCI_BAR0, 4);
 	/* u32 rtl_bar1 = pci_read_field(rtl_device_pci, PCI_BAR1, 4); */
 	rtl_iobase = 0;
 
 	if (rtl_bar0 & 0x00000001)
-		rtl_iobase = rtl_bar0 & (~0x3);
+		rtl_iobase = rtl_bar0 & 0xFFFFFFFC;
 
 	// Get mac address
 	for (int i = 0; i < 6; ++i)
@@ -120,22 +132,36 @@ void rtl8139_init()
 		;
 
 	// Set receive buffer
-	rx_buffer = (u8 *)malloc(RX_BUF_SIZE + 1516);
-	memset(rx_buffer, 0, RX_BUF_SIZE + 1516);
-	outl(rtl_iobase + RTL_PORT_RBSTART, (u32)rx_buffer);
-
-	// Set TOK and ROK
-	outw(rtl_iobase + RTL_PORT_IMR, 0x0005);
-
-	// AB+AM+APM+AAP except WRAP
-	outl(rtl_iobase + RTL_PORT_RCR, 0xf | (1 << 7));
+	rtl_rx_buffer = (u8 *)malloc(0x3000);
+	memset(rtl_rx_buffer, 0, 0x3000);
+	outl(rtl_iobase + RTL_PORT_RBSTART, (u32)rtl_rx_buffer);
+
+	// Enable IRQs
+	outw(rtl_iobase + RTL_PORT_IMR, 0x8000 | /* PCI error */
+						0x4000 | /* PCS timeout */
+						0x40 | /* Rx FIFO over */
+						0x20 | /* Rx underrun */
+						0x10 | /* Rx overflow */
+						0x08 | /* Tx error */
+						0x04 | /* Tx okay */
+						0x02 | /* Rx error */
+						0x01 /* Rx okay */
+	);
+
+	// Configure transmit
+	outl(rtl_iobase + RTL_PORT_TCR, 0);
+
+	// Configure receive
+	outl(rtl_iobase + RTL_PORT_RCR, (0) | /* 8K receive */
+						0x08 | /* broadcast */
+						0x01 /* all physical */
+	);
 
 	// Enable receive and transmit
 	outb(rtl_iobase + RTL_PORT_CMD, 0x08 | 0x04);
 
-	// Install interrupt handler
-	u32 rtl_irq = pci_get_interrupt(rtl_device_pci);
-	irq_install_handler(rtl_irq, rtl8139_irq_handler);
+	// Reset rx statistics
+	outl(rtl_iobase + RTL_PORT_RXMISS, 0);
 }
 
 int rtl8139_install()
-- 
cgit v1.2.3