BarryServer : Git

All the code for all my projects
// BarryServer : Git / Orion / blob / 1628fcfdfdf2978ed9ccac96ee7d13bb3dc43a01 / drivers / net / rtl8139.c

// Related

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);
}