Orion
Barry Importing existing Orion kernel d41a53c (3 years, 2 months ago)
diff --git a/drivers/net/rtl8139.c b/drivers/net/rtl8139.c
new file mode 100644
index 0000000..e801845
--- /dev/null
+++ b/drivers/net/rtl8139.c
@@ -0,0 +1,148 @@
+/*
+ * The is the RTL8139 driver. It is a simple driver for a simple network card.
+ * It will just abstract the hardware away, and allows the network system to
+ * utilise it
+ */
+
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include "../pci.h"
+#include "../../mem/heap.h"
+#include "../../proc/proc.h"
+#include "../../net/net.h"
+#include "../../io.h"
+#include "../../screen.h"
+
+#define RTL_PORT_MAC 0x00
+#define RTL_PORT_RX_PTR 0x38
+#define RTL_PORT_RBSTART 0x30
+#define RTL_PORT_IMR 0x3C
+#define RTL_PORT_ISR 0x3E
+#define RTL_PORT_CMD 0x37
+#define RTL_PORT_RXMISS 0x4C
+#define RTL_PORT_TCR 0x40
+#define RTL_PORT_RCR 0x44
+#define RTL_PORT_CONFIG 0x52
+#define RTL_RX_BUF_SIZE (8192+16+1500)
+#define RTL_TX_BUF_SIZE (1536)
+
+#define RTL_CFG_AAP (1 << 0) /* Accept all packets */
+#define RTL_CFG_APM (1 << 1) /* Accept packets to NIC's MAC address */
+#define RTL_CFG_AM (1 << 2) /* Accept multicast packets */
+#define RTL_CFG_AB (1 << 3) /* Accept broadcast packets */
+
+#define RTL_ISR_ROK (1 << 0) /* Receive Okay */
+#define RTL_ISR_TOK (1 << 2) /* Transmit Okay */
+
+static NetIF *netif;
+
+void *txBuf, *rxBuf;
+uint8_t txIndex = 0;
+uint32_t rxIndex = 0;
+uint16_t port;
+
+uint16_t txStartRegs[] = {0x20, 0x24, 0x28, 0x2C};
+uint16_t txControlRegs[] = {0x10, 0x14, 0x18, 0x1C};
+
+void rtl8139_transmit(void *data, size_t len);
+void rtl8139_receive(void);
+
+/* Handle the RTL8139 interrupt */
+void
+rtl8139_irq(InterruptFrame *frame)
+{
+ uint16_t status;
+ /* Loop until everything has been handled */
+ while (1) {
+ status = inw(port + RTL_PORT_ISR);
+ outw(port + RTL_PORT_ISR, status);
+ if (!status)
+ break;
+ if (status & RTL_ISR_ROK)
+ rtl8139_receive();
+ }
+}
+
+/* Transmit a frame */
+void
+rtl8139_transmit(void *data, size_t len)
+{
+ memcpy(txBuf, data, len);
+ outl(port + txStartRegs[txIndex], (uintptr_t) txBuf);
+ outl(port + txControlRegs[txIndex], len);
+ txIndex++;
+ if (txIndex > 3)
+ txIndex = 0;
+}
+
+/* Receive a frame */
+void
+rtl8139_receive(void)
+{
+ uint8_t *frame, *buf = rxBuf;
+ uint32_t index = rxIndex;
+ uint32_t offset, len;
+
+ /* While the buffer is not empty */
+ while ((inb(port + RTL_PORT_CMD) & 0x01) == 0) {
+ offset = index % RTL_RX_BUF_SIZE;
+ len = (buf[3 + index] << 8) + buf[2 + index];
+
+ frame = kmalloc(len);
+ memcpy(frame, &buf[offset + 4], len);
+ ethernet_receive_frame(netif, frame, len);
+ kfree(frame);
+
+ index = (index + len + 7) & ~3;
+ outw(port + RTL_PORT_RX_PTR, index - 16);
+ }
+
+ rxIndex = index;
+}
+
+/* Get the RTL8139 MAC Address */
+void
+rtl8139_get_mac(char *buf)
+{
+ uint8_t i;
+ for (i = 0; i < 6; i++)
+ buf[i] = inb(port + RTL_PORT_MAC + i);
+}
+
+/* Initialise the RTL8139 Network card */
+void
+rtl8139_init(uint8_t bus, uint8_t dev, uint8_t func)
+{
+ /* Enable bus mastering */
+ uint16_t command = pci_read_word(bus, dev, func, 4);
+ if (!(command & (1 << 2)))
+ pci_write_word(bus, dev, func, 4, command | (1 << 2));
+
+ /* Turn device on */
+ port = pci_read_word(bus, dev, func, 0x10) & ~3;
+ outb(port + RTL_PORT_CONFIG, 0x00);
+ /* Software reset */
+ outb(port + RTL_PORT_CMD, 0x10);
+ while ((inb(port + RTL_PORT_CMD) & 0x10) != 0);
+ /* Initialise buffers */
+ txBuf = kmalloc(RTL_RX_BUF_SIZE);
+ rxBuf = kmalloc(RTL_TX_BUF_SIZE);
+ /* TODO: use dedicated DMA area */
+ outl(port + RTL_PORT_RBSTART, (uintptr_t) rxBuf);
+ /* IMR & ISR */
+ outw(port + RTL_PORT_IMR, 0x05);
+
+ /* Configure receive buffer */
+ outl(port + RTL_PORT_RCR, (1 << 7) |
+ RTL_CFG_AB | RTL_CFG_AM | RTL_CFG_APM | RTL_CFG_AAP);
+ /* Enable RX and TX */
+ outb(port + RTL_PORT_CMD, 0x0C);
+
+ /* Register interrupt handler */
+ uint8_t irq = pci_read_byte(bus, dev, func, 0x3C);
+ register_interrupt(irq, rtl8139_irq);
+
+ /* Setup Network Interface */
+ netif = net_create_interface(1, rtl8139_transmit, rtl8139_get_mac);
+}