mirror of
https://github.com/Fennix-Project/Drivers.git
synced 2025-05-25 22:14:31 +00:00
486 lines
11 KiB
C++
486 lines
11 KiB
C++
/*
|
|
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 <net.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
|
|
{
|
|
Log("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);
|
|
|
|
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:
|
|
{
|
|
Log("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())
|
|
{
|
|
Log("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:
|
|
{
|
|
Log("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:
|
|
{
|
|
Log("Unimplemented E1000 device.");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
E1000Device *Drivers[4] = {nullptr};
|
|
dev_t AudioID[4] = {0};
|
|
|
|
#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 drvOpen(dev_t, dev_t, int, mode_t) { return 0; }
|
|
int drvClose(dev_t, dev_t) { return 0; }
|
|
size_t drvRead(dev_t, dev_t, uint8_t *, size_t, off_t) { return 0; }
|
|
|
|
size_t drvWrite(dev_t, dev_t min, uint8_t *Buffer, size_t Size, off_t)
|
|
{
|
|
return Drivers[AudioID[min]]->write(Buffer, Size);
|
|
}
|
|
|
|
int drvIoctl(dev_t, dev_t min, unsigned long Request, void *Argp)
|
|
{
|
|
return Drivers[AudioID[min]]->ioctl((NetIoctl)Request, Argp);
|
|
}
|
|
|
|
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 = FindPCIDevices(VendorIDs, DeviceIDs);
|
|
if (Devices == nullptr)
|
|
{
|
|
Log("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 = RegisterNetDevice(ddt_Network,
|
|
drvOpen, drvClose,
|
|
drvRead, drvWrite,
|
|
drvIoctl);
|
|
AudioID[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)
|
|
{
|
|
Log("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;
|
|
}
|
|
|
|
return 0;
|
|
}
|