Merge remote-tracking branch 'Drivers/master'

This commit is contained in:
EnderIce2
2024-11-20 05:01:24 +02:00
71 changed files with 13688 additions and 1 deletions

View File

@ -0,0 +1,22 @@
# Config files
include ../../../Makefile.conf
include ../../config.mk
S_SOURCES = $(shell find ./ -type f -name '*.S')
C_SOURCES = $(shell find ./ -type f -name '*.c')
CPP_SOURCES = $(shell find ./ -type f -name '*.cpp')
HEADERS = $(sort $(dir $(wildcard ../../include/*)))
OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(S_SOURCES:.S=.o)
STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CPP_SOURCES:.cpp=.su)
FILENAME = e1000.drv
build: $(FILENAME)
mv $(FILENAME) ../../out/$(FILENAME)
$(FILENAME): $(OBJ)
$(info Linking $@)
$(CC) $(DRIVER_LDFLAGS) $(OBJ) ../../out/dcrt0.o -L../../out -lkernel -o $@
clean:
rm -f file.map $(OBJ) $(STACK_USAGE_OBJ)

View File

@ -0,0 +1,512 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <netools.h>
#include <errno.h>
#include <regs.h>
#include <base.h>
#include <pci.h>
#include <network.h>
#include <io.h>
#include "e1000.hpp"
class E1000Device
{
private:
PCIHeader0 *Header;
uint16_t DeviceID;
bool Initialized = false;
bool EEPROMAvailable;
struct BARData
{
uint8_t Type;
uint16_t IOBase;
uint64_t MemoryBase;
} BAR;
#define E1000_NUM_RX_DESC 32
#define E1000_NUM_TX_DESC 8
RXDescriptor *RX[E1000_NUM_RX_DESC];
TXDescriptor *TX[E1000_NUM_TX_DESC];
uint16_t RXCurrent;
uint16_t TXCurrent;
const int BaseBufferSize = 8192;
const int AdditionalBytes = 16;
uint32_t CurrentPacket;
void WriteCMD(uint16_t Address, uint32_t Value)
{
if (BAR.Type == 0)
mmoutl((void *)(BAR.MemoryBase + Address), Value);
else
{
outl(BAR.IOBase, Address);
outl(BAR.IOBase + 4, Value);
}
}
uint32_t ReadCMD(uint16_t Address)
{
if (BAR.Type == 0)
return mminl((void *)(BAR.MemoryBase + Address));
else
{
outl(BAR.IOBase, Address);
return inl(BAR.IOBase + 0x4);
}
}
uint32_t ReadEEPROM(uint8_t Address)
{
uint16_t Data = 0;
uint32_t temp = 0;
if (EEPROMAvailable)
{
WriteCMD(REG::EEPROM, (1) | ((uint32_t)(Address) << 8));
while (!((temp = ReadCMD(REG::EEPROM)) & (1 << 4)))
;
}
else
{
WriteCMD(REG::EEPROM, (1) | ((uint32_t)(Address) << 2));
while (!((temp = ReadCMD(REG::EEPROM)) & (1 << 1)))
;
}
Data = (uint16_t)((temp >> 16) & 0xFFFF);
return Data;
}
void InitializeRX()
{
DebugLog("Initializing RX...");
uintptr_t Ptr = (uintptr_t)AllocateMemory(TO_PAGES(sizeof(RXDescriptor) *
E1000_NUM_RX_DESC +
AdditionalBytes));
for (int i = 0; i < E1000_NUM_RX_DESC; i++)
{
RX[i] = (RXDescriptor *)(Ptr + i * 16);
RX[i]->Address = (uint64_t)AllocateMemory(TO_PAGES(BaseBufferSize + AdditionalBytes));
RX[i]->Status = 0;
}
#pragma GCC diagnostic ignored "-Wshift-count-overflow"
WriteCMD(REG::TXDESCLO, (uint32_t)(Ptr >> 32));
WriteCMD(REG::TXDESCHI, (uint32_t)(Ptr & 0xFFFFFFFF));
WriteCMD(REG::RXDESCLO, (uint32_t)Ptr);
WriteCMD(REG::RXDESCHI, 0);
WriteCMD(REG::RXDESCLEN, E1000_NUM_RX_DESC * 16);
WriteCMD(REG::RXDESCHEAD, 0);
WriteCMD(REG::RXDESCTAIL, E1000_NUM_RX_DESC - 1);
RXCurrent = 0;
WriteCMD(REG::RCTRL, RCTL::EN | RCTL::SBP | RCTL::UPE |
RCTL::MPE | RCTL::LBM_NONE |
RTCL::RDMTS_HALF | RCTL::BAM |
RCTL::SECRC | RCTL::BSIZE_8192);
}
void InitializeTX()
{
DebugLog("Initializing TX...");
uintptr_t Ptr = (uintptr_t)AllocateMemory(TO_PAGES(sizeof(TXDescriptor) *
E1000_NUM_RX_DESC +
AdditionalBytes));
for (short i = 0; i < E1000_NUM_TX_DESC; i++)
{
TX[i] = (TXDescriptor *)((uintptr_t)Ptr + i * 16);
TX[i]->Address = 0;
TX[i]->Command = 0;
TX[i]->Status = TSTA::DD;
}
WriteCMD(REG::TXDESCHI, (uint32_t)((uint64_t)Ptr >> 32));
WriteCMD(REG::TXDESCLO, (uint32_t)((uint64_t)Ptr & 0xFFFFFFFF));
WriteCMD(REG::TXDESCLEN, E1000_NUM_TX_DESC * 16);
WriteCMD(REG::TXDESCHEAD, 0);
WriteCMD(REG::TXDESCTAIL, 0);
TXCurrent = 0;
WriteCMD(REG::TCTRL, TCTL::EN_ | TCTL::PSP |
(15 << TCTL::CT_SHIFT) |
(64 << TCTL::COLD_SHIFT) |
TCTL::RTLC);
WriteCMD(REG::TCTRL, 0b0110000000000111111000011111010);
WriteCMD(REG::TIPG, 0x0060200A);
}
public:
dev_t ID;
bool IsInitialized() { return Initialized; }
size_t write(uint8_t *Buffer, size_t Size)
{
TX[TXCurrent]->Address = (uint64_t)Buffer;
TX[TXCurrent]->Length = (uint16_t)Size;
TX[TXCurrent]->Command = CMD::EOP | CMD::IFCS | CMD::RS;
TX[TXCurrent]->Status = 0;
uint16_t OldTXCurrent = TXCurrent;
TXCurrent = (uint16_t)((TXCurrent + 1) % E1000_NUM_TX_DESC);
WriteCMD(REG::TXDESCTAIL, TXCurrent);
while (!(TX[OldTXCurrent]->Status & 0xFF))
Yield();
return Size;
}
MediaAccessControl GetMAC()
{
MediaAccessControl mac;
if (EEPROMAvailable)
{
uint32_t temp;
temp = ReadEEPROM(0);
mac.Address[0] = temp & 0xff;
mac.Address[1] = (uint8_t)(temp >> 8);
temp = ReadEEPROM(1);
mac.Address[2] = temp & 0xff;
mac.Address[3] = (uint8_t)(temp >> 8);
temp = ReadEEPROM(2);
mac.Address[4] = temp & 0xff;
mac.Address[5] = (uint8_t)(temp >> 8);
}
else
{
uint8_t *BaseMac8 = (uint8_t *)(BAR.MemoryBase + 0x5400);
uint32_t *BaseMac32 = (uint32_t *)(BAR.MemoryBase + 0x5400);
if (BaseMac32[0] != 0)
for (int i = 0; i < 6; i++)
mac.Address[i] = BaseMac8[i];
else
{
KernelLog("No MAC address found.");
return MediaAccessControl();
}
}
return mac;
}
int ioctl(NetIoctl req, void *arg)
{
switch (req)
{
case IOCTL_NET_GET_MAC:
{
MediaAccessControl mac = GetMAC();
*((uint48_t *)arg) = mac.ToHex(); /* UNTESTED */
return 0;
}
default:
return -EINVAL;
}
return 0;
}
void OnInterruptReceived(TrapFrame *)
{
WriteCMD(REG::IMASK, 0x1);
uint32_t status = ReadCMD(0xC0);
UNUSED(status);
while ((RX[RXCurrent]->Status & 0x1))
{
uint8_t *data = (uint8_t *)RX[RXCurrent]->Address;
uint16_t dataSz = RX[RXCurrent]->Length;
// ReportNetworkPacket(ID, data, dataSz);
/* FIXME: Implement */
KernelLog("FIXME: Received packet");
(void)data;
(void)dataSz;
RX[RXCurrent]->Status = 0;
uint16_t OldRXCurrent = RXCurrent;
RXCurrent = (uint16_t)((RXCurrent + 1) % E1000_NUM_RX_DESC);
WriteCMD(REG::RXDESCTAIL, OldRXCurrent);
}
}
void Panic()
{
WriteCMD(REG::IMASK, 0x00000000);
WriteCMD(REG::ITR, 0x00000000);
WriteCMD(REG::IAM, 0x00000000);
}
E1000Device(PCIHeader0 *_Header, uint16_t _DeviceID)
: Header(_Header),
DeviceID(_DeviceID)
{
uint32_t PCIBAR0 = Header->BAR0;
uint32_t PCIBAR1 = Header->BAR1;
BAR.Type = PCIBAR0 & 1;
BAR.IOBase = (uint16_t)(PCIBAR0 & (~3));
BAR.MemoryBase = PCIBAR1 & (~15);
switch (DeviceID)
{
case 0x100E:
{
KernelLog("Found Intel 82540EM Gigabit Ethernet Controller.");
/* Detect EEPROM */
WriteCMD(REG::EEPROM, 0x1);
for (int i = 0; i < 1000 && !EEPROMAvailable; i++)
if (ReadCMD(REG::EEPROM) & 0x10)
EEPROMAvailable = true;
else
EEPROMAvailable = false;
if (!GetMAC().Valid())
{
KernelLog("Failed to get MAC");
return;
}
/* Start link */
uint32_t cmdret = ReadCMD(REG::CTRL);
WriteCMD(REG::CTRL, cmdret | ECTRL::SLU);
for (int i = 0; i < 0x80; i++)
WriteCMD((uint16_t)(0x5200 + i * 4), 0);
WriteCMD(REG::IMASK, 0x1F6DC);
WriteCMD(REG::IMASK, 0xFF & ~4);
ReadCMD(0xC0);
InitializeRX();
InitializeTX();
break;
}
default:
{
KernelLog("Unimplemented E1000 device.");
return;
}
}
Initialized = true;
}
~E1000Device()
{
if (!Initialized)
return;
switch (DeviceID)
{
case 0x100E:
{
// Clearing Enable bit in Receive Control Register
uint32_t cmdret = ReadCMD(REG::RCTRL);
WriteCMD(REG::RCTRL, cmdret & ~RCTL::EN);
// Masking Interrupt Mask, Interrupt Throttling Rate & Interrupt Auto-Mask
WriteCMD(REG::IMASK, 0x00000000);
WriteCMD(REG::ITR, 0x00000000);
WriteCMD(REG::IAM, 0x00000000);
// Clearing SLU bit in Device Control Register
cmdret = ReadCMD(REG::CTRL);
WriteCMD(REG::CTRL, cmdret & ~ECTRL::SLU);
// Clear the Interrupt Cause Read register by reading it
ReadCMD(REG::ICR);
// Powering down the device (?)
WriteCMD(REG::CTRL, PCTRL::POWER_DOWN);
/* TODO: Stop link; further testing required */
break;
}
default:
{
KernelLog("Unimplemented E1000 device.");
return;
}
}
}
};
E1000Device *Drivers[4] = {nullptr};
dev_t NetID[4] = {(dev_t)-1};
#define OIR(x) OIR_##x
#define CREATE_OIR(x) \
void OIR_##x(TrapFrame *f) { Drivers[x]->OnInterruptReceived(f); }
CREATE_OIR(0);
CREATE_OIR(1);
CREATE_OIR(2);
CREATE_OIR(3);
int __fs_Open(struct Inode *, int, mode_t) { return 0; }
int __fs_Close(struct Inode *) { return 0; }
ssize_t __fs_Read(struct Inode *, void *, size_t, off_t) { return 0; }
ssize_t __fs_Write(struct Inode *Node, const void *Buffer, size_t Size, off_t)
{
return Drivers[NetID[Node->GetMinor()]]->write((uint8_t *)Buffer, Size);
}
int __fs_Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
{
return Drivers[NetID[Node->GetMinor()]]->ioctl((NetIoctl)Request, Argp);
}
const struct InodeOperations NetOps = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = __fs_Read,
.Write = __fs_Write,
.Truncate = nullptr,
.Open = __fs_Open,
.Close = __fs_Close,
.Ioctl = __fs_Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = nullptr,
.Stat = nullptr,
};
PCIArray *Devices;
EXTERNC int cxx_Panic()
{
PCIArray *ctx = Devices;
short Count = 0;
while (ctx != nullptr)
{
if (Drivers[Count] != nullptr)
Drivers[Count]->Panic();
Count++;
ctx = (PCIArray *)ctx->Next;
}
return 0;
}
EXTERNC int cxx_Probe()
{
uint16_t VendorIDs[] = {0x8086, /* Intel */
PCI_END};
uint16_t DeviceIDs[] = {0x100E, /* 82540EM */
0x100F, /* 82545EM */
0x10D3, /* 82574L */
0x10EA, /* I217-LM */
0x153A, /* 82577LM */
PCI_END};
Devices = GetPCIDevices(VendorIDs, DeviceIDs);
if (Devices == nullptr)
{
KernelLog("No E1000 device found.");
return -ENODEV;
}
return 0;
}
EXTERNC int cxx_Initialize()
{
PCIArray *ctx = Devices;
size_t Count = 0;
while (ctx != nullptr)
{
if (Count > sizeof(Drivers) / sizeof(E1000Device *))
break;
InitializePCI(ctx->Device);
Drivers[Count] = new E1000Device((PCIHeader0 *)ctx->Device->Header,
ctx->Device->Header->DeviceID);
if (Drivers[Count]->IsInitialized())
{
dev_t ret = RegisterDevice(NETWORK_TYPE_ETHERNET, &NetOps);
NetID[Count] = ret;
Drivers[Count]->ID = ret;
/* FIXME: bad code */
switch (Count)
{
case 0:
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(0));
break;
case 1:
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(1));
break;
case 2:
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(2));
break;
case 3:
RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(3));
break;
default:
break;
}
Count++;
}
ctx = (PCIArray *)ctx->Next;
}
if (Count == 0)
{
KernelLog("No valid E1000 device found.");
return -EINVAL;
}
return 0;
}
EXTERNC int cxx_Finalize()
{
PCIArray *ctx = Devices;
size_t Count = 0;
while (ctx != nullptr)
{
if (Count++ > sizeof(Drivers) / sizeof(E1000Device *))
break;
delete Drivers[Count++];
ctx->Device->Header->Command |= PCI_COMMAND_INTX_DISABLE;
ctx = (PCIArray *)ctx->Next;
}
for (size_t i = 0; i < sizeof(NetID) / sizeof(dev_t); i++)
{
if (NetID[i] != (dev_t)-1)
UnregisterDevice(NetID[i]);
}
return 0;
}

View File

@ -0,0 +1,163 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <types.h>
#ifdef __cplusplus
enum REG
{
CTRL = 0x0000,
STATUS = 0x0008,
ICR = 0x000C,
EEPROM = 0x0014,
CTRL_EXT = 0x0018,
ITR = 0x00C4,
IMASK = 0x00D0,
IAM = 0x00D8,
RCTRL = 0x0100,
RXDESCLO = 0x2800,
RXDESCHI = 0x2804,
RXDESCLEN = 0x2808,
RXDESCHEAD = 0x2810,
RXDESCTAIL = 0x2818,
TCTRL = 0x0400,
TXDESCLO = 0x3800,
TXDESCHI = 0x3804,
TXDESCLEN = 0x3808,
TXDESCHEAD = 0x3810,
TXDESCTAIL = 0x3818,
RDTR = 0x2820,
RXDCTL = 0x3828,
RADV = 0x282C,
RSRPD = 0x2C00,
TIPG = 0x0410
};
enum PCTRL
{
RESERVED = 0b000000,
SPEED_SELECTION_MSB = 0b010000,
UPDATE_COLLISION_TEST = 0b001000,
DUPLEX_MODE = 0b000100,
RESTART_AUTO_NEGOTIATION = 0b000010,
ISOLATE = 0b000001,
POWER_DOWN = 0b100000,
SPEED_SELECTION_LSB = 0b100000,
};
enum ECTRL
{
SLU = 0x40
};
enum RTCL
{
RDMTS_HALF = (0 << 8),
RDMTS_QUARTER = (1 << 8),
RDMTS_EIGHTH = (2 << 8)
};
enum RCTL
{
EN = (1 << 1),
SBP = (1 << 2),
UPE = (1 << 3),
MPE = (1 << 4),
LPE = (1 << 5),
LBM_NONE = (0 << 6),
LBM_PHY = (3 << 6),
MO_36 = (0 << 12),
MO_35 = (1 << 12),
MO_34 = (2 << 12),
MO_32 = (3 << 12),
BAM = (1 << 15),
VFE = (1 << 18),
CFIEN = (1 << 19),
CFI = (1 << 20),
DPF = (1 << 22),
PMCF = (1 << 23),
SECRC = (1 << 26),
BSIZE_256 = (3 << 16),
BSIZE_512 = (2 << 16),
BSIZE_1024 = (1 << 16),
BSIZE_2048 = (0 << 16),
BSIZE_4096 = ((3 << 16) | (1 << 25)),
BSIZE_8192 = ((2 << 16) | (1 << 25)),
BSIZE_16384 = ((1 << 16) | (1 << 25))
};
enum CMD
{
EOP = (1 << 0),
IFCS = (1 << 1),
IC = (1 << 2),
RS = (1 << 3),
RPS = (1 << 4),
VLE = (1 << 6),
IDE = (1 << 7)
};
enum TCTL
{
EN_ = (1 << 1),
PSP = (1 << 3),
CT_SHIFT = 4,
COLD_SHIFT = 12,
SWXOFF = (1 << 22),
RTLC = (1 << 24)
};
enum TSTA
{
DD = (1 << 0),
EC = (1 << 1),
LC = (1 << 2)
};
enum LSTA
{
LSTA_TU = (1 << 3)
};
struct RXDescriptor
{
volatile uint64_t Address;
volatile uint16_t Length;
volatile uint16_t Checksum;
volatile uint8_t Status;
volatile uint8_t Errors;
volatile uint16_t Special;
} __attribute__((packed));
struct TXDescriptor
{
volatile uint64_t Address;
volatile uint16_t Length;
volatile uint8_t cso;
volatile uint8_t Command;
volatile uint8_t Status;
volatile uint8_t css;
volatile uint16_t Special;
} __attribute__((packed));
#endif
EXTERNC int cxx_Panic();
EXTERNC int cxx_Probe();
EXTERNC int cxx_Initialize();
EXTERNC int cxx_Finalize();

View File

@ -0,0 +1,31 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include <base.h>
#include "e1000.hpp"
int DriverEntry() { return cxx_Initialize(); }
int DriverFinal() { return cxx_Finalize(); }
int DriverPanic() { return cxx_Panic(); }
int DriverProbe() { return cxx_Probe(); }
DriverInfo("e1000",
"Intel(R) PRO/1000 Network Driver",
"EnderIce2",
0, 0, 1,
"GPLv3");