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