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