Orion
Barry Importing existing Orion kernel d41a53c (2 years, 4 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); +}