mirror of https://gitlab.com/nakst/essence
508 lines
17 KiB
C++
508 lines
17 KiB
C++
// This file is part of the Essence operating system.
|
|
// It is released under the terms of the MIT license -- see LICENSE.md.
|
|
// Written by: nakst.
|
|
|
|
// TODO Initialise on a separate thread.
|
|
// TODO Checksum off-loading.
|
|
|
|
#include <module.h>
|
|
|
|
#define RD_REGISTER_CTRL() Read(0x00) // Device control.
|
|
#define WR_REGISTER_CTRL(x) Write(0x00, (x) & ~0x20000416)
|
|
#define RD_REGISTER_STATUS() Read(0x08) // Device status.
|
|
#define RD_REGISTER_EECD() Read(0x10) // EEPROM data.
|
|
#define RD_REGISTER_EERD() Read(0x14) // EEPROM read.
|
|
#define WR_REGISTER_EERD(x) Write(0x14, x)
|
|
#define WR_REGISTER_FCAL(x) Write(0x28, x) // Flow control low.
|
|
#define WR_REGISTER_FCAH(x) Write(0x2C, x) // Flow control high.
|
|
#define WR_REGISTER_FCT(x) Write(0x30, x) // Flow control type.
|
|
#define RD_REGISTER_ICR() Read(0xC0) // Interrupt cause read.
|
|
#define RD_REGISTER_IMS() Read(0xD0) // Interrupt mask set/read.
|
|
#define WR_REGISTER_IMS(x) Write(0xD0, x)
|
|
#define WR_REGISTER_IMC(x) Write(0xD8, x) // Interrupt mask clear.
|
|
#define RD_REGISTER_RCTL() Read(0x100) // Receive control.
|
|
#define WR_REGISTER_RCTL(x) Write(0x100, (x) & ~0xF9204C01)
|
|
#define RD_REGISTER_RDBAL() Read(0x2800) // Receive descriptor base address low.
|
|
#define WR_REGISTER_RDBAL(x) Write(0x2800, x)
|
|
#define RD_REGISTER_RDBAH() Read(0x2804) // Receive descriptor base address high.
|
|
#define WR_REGISTER_RDBAH(x) Write(0x2804, x)
|
|
#define RD_REGISTER_RDLEN() Read(0x2808) // Receive descriptor length.
|
|
#define WR_REGISTER_RDLEN(x) Write(0x2808, x)
|
|
#define RD_REGISTER_RDH() Read(0x2810) // Receive descriptor head.
|
|
#define WR_REGISTER_RDH(x) Write(0x2810, x)
|
|
#define RD_REGISTER_RDT() Read(0x2818) // Receive descriptor tail.
|
|
#define WR_REGISTER_RDT(x) Write(0x2818, x)
|
|
#define WR_REGISTER_MTA(x, y) Write(0x5200 + x * 4, y) // Multicast table array.
|
|
#define RD_REGISTER_RAL() Read(0x5400) // Receive address low.
|
|
#define WR_REGISTER_RAL(x) Write(0x5400, x)
|
|
#define RD_REGISTER_RAH() Read(0x5404) // Receive address high.
|
|
#define WR_REGISTER_RAH(x) Write(0x5404, x)
|
|
#define RD_REGISTER_TCTL() Read(0x400) // Transmit control.
|
|
#define WR_REGISTER_TCTL(x) Write(0x400, (x) & ~0xFC800005)
|
|
#define RD_REGISTER_TDBAL() Read(0x3800) // Transmit descriptor base address low.
|
|
#define WR_REGISTER_TDBAL(x) Write(0x3800, x)
|
|
#define RD_REGISTER_TDBAH() Read(0x3804) // Transmit descriptor base address high.
|
|
#define WR_REGISTER_TDBAH(x) Write(0x3804, x)
|
|
#define RD_REGISTER_TDLEN() Read(0x3808) // Transmit descriptor length.
|
|
#define WR_REGISTER_TDLEN(x) Write(0x3808, x)
|
|
#define RD_REGISTER_TDH() Read(0x3810) // Transmit descriptor head.
|
|
#define WR_REGISTER_TDH(x) Write(0x3810, x)
|
|
#define RD_REGISTER_TDT() Read(0x3818) // Transmit descriptor tail.
|
|
#define WR_REGISTER_TDT(x) Write(0x3818, x)
|
|
|
|
#define RECEIVE_DESCRIPTOR_COUNT (64)
|
|
#define TRANSMIT_DESCRIPTOR_COUNT (64)
|
|
|
|
#define RECEIVE_BUFFER_SIZE (8192)
|
|
#define TRANSMIT_BUFFER_SIZE (8192)
|
|
#define TRANSFER_BUFFER_EXTRA (16) // TODO What are these for?
|
|
|
|
struct ReceiveDescriptor {
|
|
uint64_t address;
|
|
uint16_t length;
|
|
uint16_t checksum;
|
|
uint8_t status, errors;
|
|
uint16_t special;
|
|
};
|
|
|
|
struct TransmitDescriptor {
|
|
uint64_t address;
|
|
uint16_t length;
|
|
uint8_t checksumOffset;
|
|
uint8_t command;
|
|
uint8_t status;
|
|
uint8_t checksumStartField;
|
|
uint16_t special;
|
|
};
|
|
|
|
struct Controller : NetInterface {
|
|
KPCIDevice *pci;
|
|
|
|
bool hasEEPROM;
|
|
|
|
ReceiveDescriptor *receiveDescriptors;
|
|
TransmitDescriptor *transmitDescriptors;
|
|
uint8_t *receiveBuffers[RECEIVE_DESCRIPTOR_COUNT];
|
|
void *transmitBuffers[TRANSMIT_DESCRIPTOR_COUNT];
|
|
uintptr_t receiveTail;
|
|
uintptr_t transmitTail;
|
|
|
|
// Used by the dispatch thread.
|
|
uint8_t *dispatchBuffers[RECEIVE_DESCRIPTOR_COUNT];
|
|
size_t dispatchByteCounts[RECEIVE_DESCRIPTOR_COUNT];
|
|
uintptr_t dispatchPhysicalAddresses[RECEIVE_DESCRIPTOR_COUNT];
|
|
|
|
KMutex transmitMutex;
|
|
KEvent receiveEvent;
|
|
|
|
uint32_t Read(uintptr_t offset);
|
|
void Write(uintptr_t offset, uint32_t value);
|
|
bool ReadEEPROM(uint8_t address, uint16_t *data);
|
|
|
|
void Initialise();
|
|
bool Transmit(void *dataVirtual, uintptr_t dataPhysical, size_t dataBytes);
|
|
bool HandleIRQ();
|
|
void DispatchThread();
|
|
void DumpState();
|
|
};
|
|
|
|
void Controller::DumpState() {
|
|
EsPrint("I8254x controller state:\n");
|
|
|
|
EsPrint("\t--- Internal ---\n");
|
|
EsPrint("\t\tHas EEPROM: %z.\n", hasEEPROM ? "yes" : "no");
|
|
EsPrint("\t\tMAC address: %X:%X:%X:%X:%X:%X.\n", macAddress.d[0], macAddress.d[1], macAddress.d[2],
|
|
macAddress.d[3], macAddress.d[4], macAddress.d[5]);
|
|
|
|
EsPrint("\t--- Registers ---\n");
|
|
EsPrint("\t\tDevice control: %x.\n", RD_REGISTER_CTRL());
|
|
EsPrint("\t\tDevice status: %x.\n", RD_REGISTER_STATUS());
|
|
EsPrint("\t\tEEPROM data: %x.\n", RD_REGISTER_EECD());
|
|
EsPrint("\t\tEEPROM read: %x.\n", RD_REGISTER_EERD());
|
|
EsPrint("\t\tReceive descriptor base address: %x.\n", (uint64_t) RD_REGISTER_RDBAL() | ((uint64_t) RD_REGISTER_RDBAH() << 32));
|
|
EsPrint("\t\tReceive descriptor length: %x.\n", RD_REGISTER_RDLEN());
|
|
EsPrint("\t\tReceive descriptor head: %x.\n", RD_REGISTER_RDH());
|
|
EsPrint("\t\tReceive descriptor tail: %x.\n", RD_REGISTER_RDT());
|
|
EsPrint("\t\tReceive control: %x.\n", RD_REGISTER_RCTL());
|
|
EsPrint("\t\tReceive MAC address: %x.\n", (uint64_t) RD_REGISTER_RAL() | ((uint64_t) RD_REGISTER_RAH() << 32));
|
|
EsPrint("\t\tTransmit descriptor base address: %x.\n", (uint64_t) RD_REGISTER_TDBAL() | ((uint64_t) RD_REGISTER_TDBAH() << 32));
|
|
EsPrint("\t\tTransmit descriptor length: %x.\n", RD_REGISTER_TDLEN());
|
|
EsPrint("\t\tTransmit descriptor head: %x.\n", RD_REGISTER_TDH());
|
|
EsPrint("\t\tTransmit descriptor tail: %x.\n", RD_REGISTER_TDT());
|
|
EsPrint("\t\tTransmit control: %x.\n", RD_REGISTER_TCTL());
|
|
}
|
|
|
|
bool Controller::Transmit(void *dataVirtual, uintptr_t dataPhysical, size_t dataBytes) {
|
|
if (!dataBytes) {
|
|
KernelPanic("Controller::Transmit - dataBytes is zero.\n");
|
|
}
|
|
|
|
KMutexAcquire(&transmitMutex);
|
|
EsDefer(KMutexRelease(&transmitMutex));
|
|
|
|
// Get a next index to use for the transmit descriptor.
|
|
|
|
uint32_t head = RD_REGISTER_TDH();
|
|
uint32_t index = transmitTail;
|
|
uint32_t tail = (index + 1) % TRANSMIT_DESCRIPTOR_COUNT;
|
|
|
|
if (head == tail) {
|
|
// Wait upto 20ms for the head to move.
|
|
KTimeout timeout(20);
|
|
while (!timeout.Hit() && (RD_REGISTER_TDH() == tail));
|
|
head = RD_REGISTER_TDH();
|
|
}
|
|
|
|
if (head == tail) {
|
|
KernelLog(LOG_ERROR, "I8254x", "transmit overrun", "Attempting to transmit a packet with the head at the same position as the tail.\n");
|
|
return false;
|
|
}
|
|
|
|
// Free any unused transmit buffers.
|
|
|
|
if (transmitBuffers[index]) {
|
|
NetTransmitBufferReturn(transmitBuffers[index]);
|
|
}
|
|
|
|
for (uintptr_t i = tail; i != head; i = (i + 1) % TRANSMIT_DESCRIPTOR_COUNT) {
|
|
if (transmitBuffers[i]) {
|
|
NetTransmitBufferReturn(transmitBuffers[i]);
|
|
transmitBuffers[i] = nullptr;
|
|
}
|
|
}
|
|
|
|
// Set up transmit descriptor.
|
|
|
|
transmitDescriptors[index].length = dataBytes;
|
|
transmitDescriptors[index].status = 0;
|
|
transmitDescriptors[index].address = dataPhysical;
|
|
transmitDescriptors[index].command = (1 << 0 /* end of packet */) | (1 << 1 /* insert CRC in Ethernet packet */) | (1 << 3 /* report status */);
|
|
|
|
transmitBuffers[index] = dataVirtual;
|
|
|
|
if ((dataPhysical >> K_PAGE_BITS) != ((dataPhysical + dataBytes - 1) >> K_PAGE_BITS)) {
|
|
KernelPanic("Controller::Transmit - Data spanned over page boundary.\n");
|
|
}
|
|
|
|
// Submit the transmit descriptor to the controller.
|
|
|
|
transmitTail = tail;
|
|
__sync_synchronize();
|
|
WR_REGISTER_TDT(tail);
|
|
|
|
return true;
|
|
}
|
|
|
|
void Controller::DispatchThread() {
|
|
while (true) {
|
|
KEvent *events[] = { &receiveEvent };
|
|
KEventWaitMultiple(events, 1);
|
|
|
|
NetInterfaceSetConnected(this, RD_REGISTER_STATUS() & (1 << 1));
|
|
|
|
uint32_t tail = receiveTail, head = RD_REGISTER_RDH();
|
|
uintptr_t dispatchCount = 0;
|
|
|
|
while (true) {
|
|
uint32_t nextTail = (tail + 1) % RECEIVE_DESCRIPTOR_COUNT;
|
|
|
|
if (nextTail == head) {
|
|
// Keep the tail one behind the head, otherwise controller assumes queue is empty of usable slots.
|
|
break;
|
|
}
|
|
|
|
if (~receiveDescriptors[nextTail].status & (1 << 0 /* descriptor done */)) {
|
|
break;
|
|
}
|
|
|
|
tail = nextTail;
|
|
|
|
uint16_t status = receiveDescriptors[tail].status;
|
|
receiveDescriptors[tail].status = 0;
|
|
|
|
if (~status & (1 << 1 /* end of packet */)) {
|
|
KernelLog(LOG_ERROR, "I8254x", "clear EOP bit", "Received descriptor with clear end of packet bit; this is unsupported.\n");
|
|
goto next;
|
|
}
|
|
|
|
if (receiveDescriptors[tail].errors) {
|
|
KernelLog(LOG_ERROR, "I8254x", "received error", "Received descriptor with error bits %X set.\n", receiveDescriptors[tail].errors);
|
|
goto next;
|
|
}
|
|
|
|
if (receiveDescriptors[tail].length < 60) {
|
|
KernelLog(LOG_ERROR, "I8254x", "short packet", "Received descriptor with packet less than 60 bytes; this is unsupported.\n");
|
|
goto next;
|
|
}
|
|
|
|
KernelLog(LOG_VERBOSE, "I8254x", "received packet", "Received packet at index %d with length %D.\n", tail, receiveDescriptors[tail].length);
|
|
|
|
uint8_t *newVirtualAddress;
|
|
uintptr_t newPhysicalAddress;
|
|
|
|
if (!MMPhysicalAllocateAndMap(RECEIVE_BUFFER_SIZE + TRANSFER_BUFFER_EXTRA,
|
|
16, 64, false, 0, &newVirtualAddress, &newPhysicalAddress)) {
|
|
// If we couldn't allocate memory, dispatch the buffer immediately.
|
|
|
|
NetInterfaceReceive(this, receiveBuffers[tail], receiveDescriptors[tail].length, NET_PACKET_ETHERNET);
|
|
} else {
|
|
// Queue the buffer to be dispatched.
|
|
|
|
dispatchBuffers[dispatchCount] = receiveBuffers[tail];
|
|
dispatchByteCounts[dispatchCount] = receiveDescriptors[tail].length;
|
|
dispatchPhysicalAddresses[dispatchCount] = receiveDescriptors[tail].address;
|
|
dispatchCount++;
|
|
|
|
receiveBuffers[tail] = newVirtualAddress;
|
|
receiveDescriptors[tail].address = newPhysicalAddress;
|
|
}
|
|
|
|
next:;
|
|
}
|
|
|
|
__sync_synchronize();
|
|
receiveTail = tail;
|
|
WR_REGISTER_RDT(tail);
|
|
|
|
for (uintptr_t i = 0; i < dispatchCount; i++) {
|
|
NetInterfaceReceive(this, dispatchBuffers[i], dispatchByteCounts[i], NET_PACKET_ETHERNET);
|
|
MMFree(MMGetKernelSpace(), dispatchBuffers[i], RECEIVE_BUFFER_SIZE + TRANSFER_BUFFER_EXTRA);
|
|
MMPhysicalFree(dispatchPhysicalAddresses[i], false, (RECEIVE_BUFFER_SIZE + TRANSFER_BUFFER_EXTRA + K_PAGE_SIZE - 1) / K_PAGE_SIZE);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Controller::HandleIRQ() {
|
|
uint32_t cause = RD_REGISTER_ICR();
|
|
|
|
if (!cause) {
|
|
return false;
|
|
}
|
|
|
|
KernelLog(LOG_VERBOSE, "I8254x", "received IRQ", "Received IRQ with cause %x.\n", cause);
|
|
|
|
if (cause & (1 << 2)) {
|
|
KernelLog(LOG_INFO, "I8254x", "link status change", "Link is now %z.\n",
|
|
(RD_REGISTER_STATUS() & (1 << 1)) ? "up" : "down");
|
|
KEventSet(&receiveEvent, true);
|
|
}
|
|
|
|
if (cause & (1 << 6)) {
|
|
KernelLog(LOG_ERROR, "I8254x", "receive underrun", "Controller reported receive underrun; packets have been lost.\n");
|
|
}
|
|
|
|
if (cause & ((1 << 6) | (1 << 7) | (1 << 4))) {
|
|
KEventSet(&receiveEvent, true);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
uint32_t Controller::Read(uintptr_t offset) {
|
|
if (pci->baseAddresses[0] & 1) {
|
|
pci->WriteBAR32(0, 0, offset);
|
|
return pci->ReadBAR32(0, 4);
|
|
} else {
|
|
return pci->ReadBAR32(0, offset);
|
|
}
|
|
}
|
|
|
|
void Controller::Write(uintptr_t offset, uint32_t value) {
|
|
if (pci->baseAddresses[0] & 1) {
|
|
pci->WriteBAR32(0, 0, offset);
|
|
pci->WriteBAR32(0, 4, value);
|
|
} else {
|
|
pci->WriteBAR32(0, offset, value);
|
|
}
|
|
}
|
|
|
|
bool Controller::ReadEEPROM(uint8_t address, uint16_t *data) {
|
|
if (!hasEEPROM) {
|
|
KernelPanic("Controller::ReadEEPROM - EEPROM not present.\n");
|
|
}
|
|
|
|
WR_REGISTER_EERD(1 | ((uint32_t) address << 8));
|
|
KTimeout timeout(20);
|
|
while (!timeout.Hit() && (RD_REGISTER_EERD() & (1 << 4)));
|
|
if (data) *data = RD_REGISTER_EERD() >> 16;
|
|
return RD_REGISTER_EERD() & (1 << 4);
|
|
}
|
|
|
|
void Controller::Initialise() {
|
|
KEvent wait = {};
|
|
|
|
// Create a thread for dispatching received packets.
|
|
|
|
receiveEvent.autoReset = true;
|
|
|
|
if (!KThreadCreate("I8254xDispatch", [] (uintptr_t self) { ((Controller *) self)->DispatchThread(); }, (uintptr_t) this)) {
|
|
KernelLog(LOG_ERROR, "I8254x", "thread error", "Could not create the dispatch thread.\n");
|
|
return;
|
|
}
|
|
|
|
// Detect the EEPROM.
|
|
|
|
hasEEPROM = true;
|
|
hasEEPROM = ReadEEPROM(0, nullptr);
|
|
|
|
// Reset the controller.
|
|
|
|
uint32_t controlReset = RD_REGISTER_CTRL();
|
|
|
|
WR_REGISTER_CTRL(controlReset | (1 << 31 /* PHY_RST */));
|
|
RD_REGISTER_STATUS();
|
|
KEventWait(&wait, 20 /* specification says minimum time is 10ms, double for safety */);
|
|
|
|
WR_REGISTER_CTRL(controlReset | (1 << 26 /* RST */));
|
|
RD_REGISTER_STATUS();
|
|
KEventWait(&wait, 20);
|
|
|
|
if (RD_REGISTER_CTRL() & (1 << 26)) {
|
|
KernelLog(LOG_ERROR, "I8254x", "reset timeout", "Reset bit in control register did not clear after 20ms.\n");
|
|
return;
|
|
}
|
|
|
|
// Configure the control register.
|
|
|
|
uint32_t controlClearBits = (1 << 3 /* LRST */) | (1 << 31 /* PHY_RST */) | (1 << 7 /* ILOS */) | (1 << 30 /* VME */);
|
|
uint32_t controlSetBits = (1 << 5 /* ASDE */) | (1 << 6 /* SLU */);
|
|
WR_REGISTER_CTRL((RD_REGISTER_CTRL() & ~controlClearBits) | controlSetBits);
|
|
|
|
// Allocate receive and transmit descriptors and their buffers.
|
|
|
|
uintptr_t receiveDescriptorsPhysical, transmitDescriptorsPhysical;
|
|
|
|
if (!MMPhysicalAllocateAndMap(sizeof(ReceiveDescriptor) * RECEIVE_DESCRIPTOR_COUNT, 16, 64, true,
|
|
0, (uint8_t **) &receiveDescriptors, &receiveDescriptorsPhysical)) {
|
|
KernelLog(LOG_ERROR, "I8254x", "allocation failure", "Could not allocate receive descriptors.\n");
|
|
return;
|
|
}
|
|
|
|
if (!MMPhysicalAllocateAndMap(sizeof(TransmitDescriptor) * TRANSMIT_DESCRIPTOR_COUNT, 16, 64, true,
|
|
0, (uint8_t **) &transmitDescriptors, &transmitDescriptorsPhysical)) {
|
|
KernelLog(LOG_ERROR, "I8254x", "allocation failure", "Could not allocate transmit descriptors.\n");
|
|
return;
|
|
}
|
|
|
|
for (uintptr_t i = 0; i < RECEIVE_DESCRIPTOR_COUNT; i++) {
|
|
uintptr_t address;
|
|
|
|
if (!MMPhysicalAllocateAndMap(RECEIVE_BUFFER_SIZE + TRANSFER_BUFFER_EXTRA,
|
|
16, 64, false, 0, receiveBuffers + i, &address)) {
|
|
KernelLog(LOG_ERROR, "I8254x", "allocation failure", "Could not allocate receive buffers.\n");
|
|
return;
|
|
}
|
|
|
|
receiveDescriptors[i].address = address;
|
|
}
|
|
|
|
// Disable flow control.
|
|
|
|
WR_REGISTER_FCAL(0);
|
|
WR_REGISTER_FCAH(0);
|
|
WR_REGISTER_FCT(0);
|
|
|
|
// Get the MAC address.
|
|
|
|
if (hasEEPROM) {
|
|
if (!ReadEEPROM(0, (uint16_t *) &macAddress.d[0])
|
|
|| !ReadEEPROM(1, (uint16_t *) &macAddress.d[2])
|
|
|| !ReadEEPROM(2, (uint16_t *) &macAddress.d[4])) {
|
|
KernelLog(LOG_ERROR, "I8254x", "EEPROM error", "Could not read the MAC address from the EEPROM.\n");
|
|
return;
|
|
}
|
|
} else {
|
|
macAddress64 = ((uint64_t) RD_REGISTER_RAL() | ((uint64_t) RD_REGISTER_RAH() << 32)) & 0xFFFFFFFFFFFF;
|
|
}
|
|
|
|
// Enable interrupts and the register the handler.
|
|
|
|
WR_REGISTER_IMC((1 << 17) - 1);
|
|
WR_REGISTER_IMS((1 << 6 /* RXO */) | (1 << 7 /* RXT */) | (1 << 4 /* RXDMT */) | (1 << 2 /* LSC */));
|
|
RD_REGISTER_ICR();
|
|
|
|
if (!pci->EnableSingleInterrupt([] (uintptr_t, void *context) { return ((Controller *) context)->HandleIRQ(); }, this, "I8254x")) {
|
|
KernelLog(LOG_ERROR, "I8254x", "IRQ registration failure", "Could not register IRQ %d.\n", pci->interruptLine);
|
|
return;
|
|
}
|
|
|
|
// Setup receive registers.
|
|
|
|
WR_REGISTER_RAL(macAddress64 & 0xFFFFFFFF);
|
|
WR_REGISTER_RAH(((macAddress64 >> 32) & 0xFFFF) | (1 << 31 /* AV */));
|
|
|
|
WR_REGISTER_RDBAL(receiveDescriptorsPhysical & 0xFFFFFFFF);
|
|
WR_REGISTER_RDBAH(receiveDescriptorsPhysical >> 32);
|
|
WR_REGISTER_RDLEN(sizeof(ReceiveDescriptor) * RECEIVE_DESCRIPTOR_COUNT);
|
|
|
|
WR_REGISTER_RDH(0);
|
|
WR_REGISTER_RDT(RECEIVE_DESCRIPTOR_COUNT - 1);
|
|
receiveTail = RECEIVE_DESCRIPTOR_COUNT - 1;
|
|
|
|
for (uintptr_t i = 0; i < 128; i++) {
|
|
// Clear the multicast table array.
|
|
WR_REGISTER_MTA(i, 0);
|
|
}
|
|
|
|
uint32_t receiveControlClearBits = (1 << 2) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 16) | (1 << 17) | (1 << 25) | (1 << 26);
|
|
uint32_t receiveControlSetBits = (1 << 1) | (1 << 4) | (1 << 15);
|
|
WR_REGISTER_RCTL((RD_REGISTER_RCTL() & ~receiveControlClearBits) | receiveControlSetBits);
|
|
|
|
// Setup transmit registers.
|
|
|
|
WR_REGISTER_TDBAL(transmitDescriptorsPhysical & 0xFFFFFFFF);
|
|
WR_REGISTER_TDBAH(transmitDescriptorsPhysical >> 32);
|
|
WR_REGISTER_TDLEN(sizeof(TransmitDescriptor) * TRANSMIT_DESCRIPTOR_COUNT);
|
|
|
|
WR_REGISTER_TDH(0);
|
|
WR_REGISTER_TDT(0);
|
|
transmitTail = 0;
|
|
|
|
// TODO Is the TCTL.COLD value correct?
|
|
WR_REGISTER_TCTL((1 << 1) | (1 << 3) | (0x0F << 4) | (0x40 << 12));
|
|
|
|
// Register the device.
|
|
|
|
transmit = [] (NetInterface *self, void *dataVirtual, uintptr_t dataPhysical, size_t dataBytes) {
|
|
return ((Controller *) self)->Transmit(dataVirtual, dataPhysical, dataBytes);
|
|
};
|
|
|
|
KRegisterNetInterface(this);
|
|
|
|
if (RD_REGISTER_STATUS() & (1 << 1 /* link up */)) {
|
|
NetInterfaceSetConnected(this, true);
|
|
}
|
|
}
|
|
|
|
static void DeviceAttach(KDevice *_parent) {
|
|
KPCIDevice *parent = (KPCIDevice *) _parent;
|
|
|
|
Controller *device = (Controller *) KDeviceCreate("I8254x", parent, sizeof(Controller));
|
|
if (!device) return;
|
|
|
|
device->shutdown = [] (KDevice *device) {
|
|
NetInterfaceShutdown((Controller *) device);
|
|
|
|
// Wait a little bit for the transmit descriptors to be processed.
|
|
// TODO Can this be done asynchronously?
|
|
KEvent wait = {};
|
|
KEventWait(&wait, 50);
|
|
};
|
|
|
|
parent->EnableFeatures(K_PCI_FEATURE_MEMORY_SPACE_ACCESS
|
|
| K_PCI_FEATURE_BUSMASTERING_DMA
|
|
| K_PCI_FEATURE_INTERRUPTS
|
|
| K_PCI_FEATURE_IO_PORT_ACCESS
|
|
| K_PCI_FEATURE_BAR_0);
|
|
|
|
KernelLog(LOG_INFO, "I8254x", "found controller", "Found I8254x controller with ID %x.\n", parent->deviceID);
|
|
|
|
device->pci = parent;
|
|
device->Initialise();
|
|
// device->DumpState();
|
|
}
|
|
|
|
KDriver driverI8254x = {
|
|
.attach = DeviceAttach,
|
|
};
|