mirror of
https://github.com/EnderIce2/Fennix.git
synced 2025-07-02 02:49:15 +00:00
Merge remote-tracking branch 'Kernel/master'
This commit is contained in:
924
Kernel/core/driver/api.cpp
Normal file
924
Kernel/core/driver/api.cpp
Normal file
@ -0,0 +1,924 @@
|
||||
/*
|
||||
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 <driver.hpp>
|
||||
#include <interface/driver.h>
|
||||
#include <interface/fs.h>
|
||||
#include <type_traits>
|
||||
#include <interface/aip.h>
|
||||
#include <interface/input.h>
|
||||
#include <interface/pci.h>
|
||||
|
||||
#include "../../kernel.h"
|
||||
|
||||
#define DEBUG_API
|
||||
|
||||
#ifdef DEBUG_API
|
||||
#define dbg_api(Format, ...) func(Format, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_api(Format, ...)
|
||||
#endif
|
||||
|
||||
namespace v0
|
||||
{
|
||||
typedef int CriticalState;
|
||||
|
||||
void KernelPrint(dev_t DriverID, const char *Format, va_list args)
|
||||
{
|
||||
dbg_api("%d, %s, %#lx", DriverID, Format, args);
|
||||
|
||||
_KPrint(Format, args);
|
||||
}
|
||||
|
||||
void KernelLog(dev_t DriverID, const char *Format, va_list args)
|
||||
{
|
||||
dbg_api("%d, %s, %#lx", DriverID, Format, args);
|
||||
|
||||
fctprintf(uart_wrapper, nullptr, "DRVER| %ld: ", DriverID);
|
||||
vfctprintf(uart_wrapper, nullptr, Format, args);
|
||||
uart_wrapper('\n', nullptr);
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
CriticalState EnterCriticalSection(dev_t DriverID)
|
||||
{
|
||||
dbg_api("%d", DriverID);
|
||||
|
||||
CriticalState cs;
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
|
||||
uintptr_t Flags;
|
||||
#if defined(__x86_64__)
|
||||
asmv("pushfq");
|
||||
asmv("popq %0"
|
||||
: "=r"(Flags));
|
||||
#else
|
||||
asmv("pushfl");
|
||||
asmv("popl %0"
|
||||
: "=r"(Flags));
|
||||
#endif
|
||||
cs = Flags & (1 << 9);
|
||||
asmv("cli");
|
||||
|
||||
#elif defined(__arm__) || defined(__aarch64__)
|
||||
|
||||
uintptr_t Flags;
|
||||
asmv("mrs %0, cpsr"
|
||||
: "=r"(Flags));
|
||||
cs = Flags & (1 << 7);
|
||||
asmv("cpsid i");
|
||||
|
||||
#endif
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
void LeaveCriticalSection(dev_t DriverID, CriticalState PreviousState)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, PreviousState);
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
|
||||
if (PreviousState)
|
||||
asmv("sti");
|
||||
|
||||
#elif defined(__arm__) || defined(__aarch64__)
|
||||
|
||||
if (PreviousState)
|
||||
asmv("cpsie i");
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
int RegisterInterruptHandler(dev_t DriverID, uint8_t IRQ, void *Handler)
|
||||
{
|
||||
dbg_api("%d, %d, %#lx", DriverID, IRQ, Handler);
|
||||
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(DriverID);
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
if (drv->InterruptHandlers->contains(IRQ))
|
||||
return -EEXIST;
|
||||
|
||||
Interrupts::AddHandler((void (*)(CPU::TrapFrame *))Handler, IRQ);
|
||||
auto ih = drv->InterruptHandlers;
|
||||
ih->insert(std::pair<uint8_t, void *>(IRQ, Handler));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int OverrideInterruptHandler(dev_t DriverID, uint8_t IRQ, void *Handler)
|
||||
{
|
||||
dbg_api("%d, %d, %#lx", DriverID, IRQ, Handler);
|
||||
|
||||
debug("Overriding IRQ %d with %#lx", IRQ, Handler);
|
||||
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
|
||||
for (auto &var : drivers)
|
||||
{
|
||||
Driver::DriverObject *drv = &var.second;
|
||||
for (const auto &ih : *drv->InterruptHandlers)
|
||||
{
|
||||
if (ih.first != IRQ)
|
||||
continue;
|
||||
|
||||
debug("Removing IRQ %d: %#lx for %s", IRQ, (uintptr_t)ih.second, drv->Path.c_str());
|
||||
Interrupts::RemoveHandler((void (*)(CPU::TrapFrame *))ih.second, IRQ);
|
||||
drv->InterruptHandlers->erase(IRQ);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return RegisterInterruptHandler(DriverID, IRQ, Handler);
|
||||
}
|
||||
|
||||
int UnregisterInterruptHandler(dev_t DriverID, uint8_t IRQ, void *Handler)
|
||||
{
|
||||
dbg_api("%d, %d, %#lx", DriverID, IRQ, Handler);
|
||||
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(DriverID);
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
Interrupts::RemoveHandler((void (*)(CPU::TrapFrame *))Handler, IRQ);
|
||||
auto ih = drv->InterruptHandlers;
|
||||
ih->erase(IRQ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int UnregisterAllInterruptHandlers(dev_t DriverID, void *Handler)
|
||||
{
|
||||
dbg_api("%d, %#lx", DriverID, Handler);
|
||||
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(DriverID);
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
for (auto &i : *drv->InterruptHandlers)
|
||||
{
|
||||
Interrupts::RemoveHandler((void (*)(CPU::TrapFrame *))Handler, i.first);
|
||||
debug("Removed IRQ %d: %#lx for %s", i.first, (uintptr_t)Handler, drv->Path.c_str());
|
||||
}
|
||||
auto ih = drv->InterruptHandlers;
|
||||
ih->clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
dev_t RegisterFileSystem(dev_t DriverID, FileSystemInfo *Info, struct Inode *Root)
|
||||
{
|
||||
dbg_api("%d, %#lx, %#lx", DriverID, Info, Root);
|
||||
|
||||
return fs->RegisterFileSystem(Info, Root);
|
||||
}
|
||||
|
||||
int UnregisterFileSystem(dev_t DriverID, dev_t Device)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, Device);
|
||||
|
||||
return fs->UnregisterFileSystem(Device);
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
pid_t CreateKernelProcess(dev_t DriverID, const char *Name)
|
||||
{
|
||||
dbg_api("%d, %s", DriverID, Name);
|
||||
|
||||
Tasking::PCB *pcb = TaskManager->CreateProcess(nullptr, Name, Tasking::System,
|
||||
true, 0, 0);
|
||||
|
||||
return pcb->ID;
|
||||
}
|
||||
|
||||
pid_t CreateKernelThread(dev_t DriverID, pid_t pId, const char *Name, void *EntryPoint, void *Argument)
|
||||
{
|
||||
dbg_api("%d, %d, %s, %#lx, %#lx", DriverID, pId, Name, EntryPoint, Argument);
|
||||
|
||||
Tasking::PCB *parent = TaskManager->GetProcessByID(pId);
|
||||
if (!parent)
|
||||
return -EINVAL;
|
||||
|
||||
CriticalSection cs;
|
||||
Tasking::TCB *tcb = TaskManager->CreateThread(parent, (Tasking::IP)EntryPoint);
|
||||
if (Argument)
|
||||
tcb->SYSV_ABI_Call((uintptr_t)Argument);
|
||||
tcb->Rename(Name);
|
||||
return tcb->ID;
|
||||
}
|
||||
|
||||
pid_t GetCurrentProcess(dev_t DriverID)
|
||||
{
|
||||
dbg_api("%d", DriverID);
|
||||
|
||||
return TaskManager->GetCurrentProcess()->ID;
|
||||
}
|
||||
|
||||
int KillProcess(dev_t DriverID, pid_t pId, int ExitCode)
|
||||
{
|
||||
dbg_api("%d, %d, %d", DriverID, pId, ExitCode);
|
||||
|
||||
Tasking::PCB *pcb = TaskManager->GetProcessByID(pId);
|
||||
if (!pcb)
|
||||
return -EINVAL;
|
||||
TaskManager->KillProcess(pcb, (Tasking::KillCode)ExitCode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int KillThread(dev_t DriverID, pid_t tId, pid_t pId, int ExitCode)
|
||||
{
|
||||
dbg_api("%d, %d, %d", DriverID, tId, ExitCode);
|
||||
|
||||
Tasking::TCB *tcb = TaskManager->GetThreadByID(tId, TaskManager->GetProcessByID(pId));
|
||||
if (!tcb)
|
||||
return -EINVAL;
|
||||
TaskManager->KillThread(tcb, (Tasking::KillCode)ExitCode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Yield(dev_t DriverID)
|
||||
{
|
||||
dbg_api("%d", DriverID);
|
||||
|
||||
TaskManager->Yield();
|
||||
}
|
||||
|
||||
void Sleep(dev_t DriverID, uint64_t Milliseconds)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, Milliseconds);
|
||||
|
||||
TaskManager->Sleep(Milliseconds);
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
void PIC_EOI(dev_t DriverID, uint8_t IRQ)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, IRQ);
|
||||
|
||||
if (IRQ >= 8)
|
||||
outb(PIC2_CMD, _PIC_EOI);
|
||||
outb(PIC1_CMD, _PIC_EOI);
|
||||
}
|
||||
|
||||
void IRQ_MASK(dev_t DriverID, uint8_t IRQ)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, IRQ);
|
||||
|
||||
uint16_t port;
|
||||
uint8_t value;
|
||||
|
||||
if (IRQ < 8)
|
||||
port = PIC1_DATA;
|
||||
else
|
||||
{
|
||||
port = PIC2_DATA;
|
||||
IRQ -= 8;
|
||||
}
|
||||
|
||||
value = inb(port) | (1 << IRQ);
|
||||
outb(port, value);
|
||||
}
|
||||
|
||||
void IRQ_UNMASK(dev_t DriverID, uint8_t IRQ)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, IRQ);
|
||||
|
||||
uint16_t port;
|
||||
uint8_t value;
|
||||
|
||||
if (IRQ < 8)
|
||||
port = PIC1_DATA;
|
||||
else
|
||||
{
|
||||
port = PIC2_DATA;
|
||||
IRQ -= 8;
|
||||
}
|
||||
|
||||
value = inb(port) & ~(1 << IRQ);
|
||||
outb(port, value);
|
||||
}
|
||||
|
||||
void PS2Wait(dev_t DriverID, const bool Output)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, Output);
|
||||
|
||||
int Timeout = 100000;
|
||||
PS2_STATUSES Status = {.Raw = inb(PS2_STATUS)};
|
||||
while (Timeout--)
|
||||
{
|
||||
if (!Output) /* FIXME: Reverse? */
|
||||
{
|
||||
if (Status.OutputBufferFull == 0)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Status.InputBufferFull == 0)
|
||||
return;
|
||||
}
|
||||
Status.Raw = inb(PS2_STATUS);
|
||||
}
|
||||
|
||||
warn("PS/2 controller timeout! (Status: %#x, %d)", Status, Output);
|
||||
}
|
||||
|
||||
void PS2WriteCommand(dev_t DriverID, uint8_t Command)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, Command);
|
||||
|
||||
WaitInput;
|
||||
outb(PS2_CMD, Command);
|
||||
}
|
||||
|
||||
void PS2WriteData(dev_t DriverID, uint8_t Data)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, Data);
|
||||
|
||||
WaitInput;
|
||||
outb(PS2_DATA, Data);
|
||||
}
|
||||
|
||||
uint8_t PS2ReadData(dev_t DriverID)
|
||||
{
|
||||
dbg_api("%d", DriverID);
|
||||
|
||||
WaitOutput;
|
||||
return inb(PS2_DATA);
|
||||
}
|
||||
|
||||
uint8_t PS2ReadStatus(dev_t DriverID)
|
||||
{
|
||||
dbg_api("%d", DriverID);
|
||||
|
||||
WaitOutput;
|
||||
return inb(PS2_STATUS);
|
||||
}
|
||||
|
||||
uint8_t PS2ReadAfterACK(dev_t DriverID)
|
||||
{
|
||||
dbg_api("%d", DriverID);
|
||||
|
||||
uint8_t ret = PS2ReadData(DriverID);
|
||||
while (ret == PS2_ACK)
|
||||
{
|
||||
WaitOutput;
|
||||
ret = inb(PS2_DATA);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PS2ClearOutputBuffer(dev_t DriverID)
|
||||
{
|
||||
dbg_api("%d", DriverID);
|
||||
|
||||
PS2_STATUSES Status;
|
||||
int timeout = 0x500;
|
||||
while (timeout--)
|
||||
{
|
||||
Status.Raw = inb(PS2_STATUS);
|
||||
if (Status.OutputBufferFull == 0)
|
||||
return;
|
||||
inb(PS2_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
int PS2ACKTimeout(dev_t DriverID)
|
||||
{
|
||||
dbg_api("%d", DriverID);
|
||||
|
||||
int timeout = 0x500;
|
||||
while (timeout > 0)
|
||||
{
|
||||
if (PS2ReadData(DriverID) == PS2_ACK)
|
||||
return 0;
|
||||
timeout--;
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
void *AllocateMemory(dev_t DriverID, size_t Pages)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, Pages);
|
||||
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &Drivers =
|
||||
DriverManager->GetDrivers();
|
||||
|
||||
auto itr = Drivers.find(DriverID);
|
||||
assert(itr != Drivers.end());
|
||||
|
||||
void *ptr = itr->second.vma->RequestPages(Pages);
|
||||
memset(ptr, 0, FROM_PAGES(Pages));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void FreeMemory(dev_t DriverID, void *Pointer, size_t Pages)
|
||||
{
|
||||
dbg_api("%d, %#lx, %d", DriverID, Pointer, Pages);
|
||||
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &Drivers =
|
||||
DriverManager->GetDrivers();
|
||||
|
||||
auto itr = Drivers.find(DriverID);
|
||||
assert(itr != Drivers.end());
|
||||
|
||||
itr->second.vma->FreePages(Pointer, Pages);
|
||||
}
|
||||
|
||||
void *MemoryCopy(dev_t DriverID, void *Destination, const void *Source, size_t Length)
|
||||
{
|
||||
dbg_api("%d, %#lx, %#lx, %d", DriverID, Destination, Source, Length);
|
||||
|
||||
return memcpy(Destination, Source, Length);
|
||||
}
|
||||
|
||||
void *MemorySet(dev_t DriverID, void *Destination, int Value, size_t Length)
|
||||
{
|
||||
dbg_api("%d, %#lx, %d, %d", DriverID, Destination, Value, Length);
|
||||
|
||||
return memset(Destination, Value, Length);
|
||||
}
|
||||
|
||||
void *MemoryMove(dev_t DriverID, void *Destination, const void *Source, size_t Length)
|
||||
{
|
||||
dbg_api("%d, %#lx, %#lx, %d", DriverID, Destination, Source, Length);
|
||||
|
||||
return memmove(Destination, Source, Length);
|
||||
}
|
||||
|
||||
size_t StringLength(dev_t DriverID, const char String[])
|
||||
{
|
||||
dbg_api("%d, %s", DriverID, String);
|
||||
|
||||
return strlen(String);
|
||||
}
|
||||
|
||||
char *_strstr(dev_t DriverID, const char *Haystack, const char *Needle)
|
||||
{
|
||||
dbg_api("%d, %s, %s", DriverID, Haystack, Needle);
|
||||
|
||||
return (char *)strstr(Haystack, Needle);
|
||||
}
|
||||
|
||||
void MapPages(dev_t MajorID, void *PhysicalAddress, void *VirtualAddress, size_t Pages, uint32_t Flags)
|
||||
{
|
||||
dbg_api("%d, %#lx, %#lx, %d, %d", MajorID, PhysicalAddress, VirtualAddress, Pages, Flags);
|
||||
|
||||
Memory::Virtual vmm(KernelPageTable);
|
||||
vmm.Map(VirtualAddress, PhysicalAddress, Pages, Flags);
|
||||
}
|
||||
|
||||
void UnmapPages(dev_t MajorID, void *VirtualAddress, size_t Pages)
|
||||
{
|
||||
dbg_api("%d, %#lx, %d", MajorID, VirtualAddress, Pages);
|
||||
|
||||
Memory::Virtual vmm(KernelPageTable);
|
||||
vmm.Unmap(VirtualAddress, Pages);
|
||||
}
|
||||
|
||||
void AppendMapFlag(dev_t MajorID, void *Address, PageMapFlags Flag)
|
||||
{
|
||||
dbg_api("%d, %#lx, %d", MajorID, Address, Flag);
|
||||
|
||||
Memory::Virtual vmm(KernelPageTable);
|
||||
vmm.GetPTE(Address)->raw |= Flag;
|
||||
}
|
||||
|
||||
void RemoveMapFlag(dev_t MajorID, void *Address, PageMapFlags Flag)
|
||||
{
|
||||
dbg_api("%d, %#lx, %d", MajorID, Address, Flag);
|
||||
|
||||
Memory::Virtual vmm(KernelPageTable);
|
||||
vmm.GetPTE(Address)->raw &= ~Flag;
|
||||
}
|
||||
|
||||
void *Znwm(size_t Size)
|
||||
{
|
||||
dbg_api("%d", Size);
|
||||
|
||||
return malloc(Size);
|
||||
}
|
||||
|
||||
void ZdlPvm(void *Pointer, size_t Size)
|
||||
{
|
||||
dbg_api("%d, %#lx", Pointer, Size);
|
||||
|
||||
free(Pointer);
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
__PCIArray *GetPCIDevices(dev_t DriverID, uint16_t _Vendors[], uint16_t _Devices[])
|
||||
{
|
||||
dbg_api("%d, %#lx, %#lx", DriverID, _Vendors, _Devices);
|
||||
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &Drivers =
|
||||
DriverManager->GetDrivers();
|
||||
|
||||
auto itr = Drivers.find(DriverID);
|
||||
if (itr == Drivers.end())
|
||||
return nullptr;
|
||||
|
||||
std::list<uint16_t> VendorIDs;
|
||||
for (int i = 0; _Vendors[i] != 0x0; i++)
|
||||
VendorIDs.push_back(_Vendors[i]);
|
||||
|
||||
std::list<uint16_t> DeviceIDs;
|
||||
for (int i = 0; _Devices[i] != 0x0; i++)
|
||||
DeviceIDs.push_back(_Devices[i]);
|
||||
|
||||
std::list<PCI::PCIDevice> Devices = PCIManager->FindPCIDevice(VendorIDs, DeviceIDs);
|
||||
if (Devices.empty())
|
||||
return nullptr;
|
||||
|
||||
Memory::VirtualMemoryArea *vma = itr->second.vma;
|
||||
__PCIArray *head = nullptr;
|
||||
__PCIArray *array = nullptr;
|
||||
|
||||
foreach (auto &dev in Devices)
|
||||
{
|
||||
/* TODO: optimize memory allocation */
|
||||
PCI::PCIDevice *dptr = (PCI::PCIDevice *)vma->RequestPages(TO_PAGES(sizeof(PCI::PCIDevice)));
|
||||
memcpy(dptr, &dev, sizeof(PCI::PCIDevice));
|
||||
|
||||
__PCIArray *newArray = (__PCIArray *)vma->RequestPages(TO_PAGES(sizeof(__PCIArray)));
|
||||
|
||||
if (unlikely(head == nullptr))
|
||||
{
|
||||
head = newArray;
|
||||
array = head;
|
||||
}
|
||||
else
|
||||
{
|
||||
array->Next = newArray;
|
||||
array = newArray;
|
||||
}
|
||||
|
||||
array->Device = dptr;
|
||||
array->Next = nullptr;
|
||||
|
||||
debug("Found %02x.%02x.%02x: %04x:%04x",
|
||||
dev.Bus, dev.Device, dev.Function,
|
||||
dev.Header->VendorID, dev.Header->DeviceID);
|
||||
}
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
void InitializePCI(dev_t DriverID, void *_Header)
|
||||
{
|
||||
dbg_api("%d, %#lx", DriverID, _Header);
|
||||
|
||||
PCI::PCIDevice *__device = (PCI::PCIDevice *)_Header;
|
||||
PCI::PCIDeviceHeader *Header = (PCI::PCIDeviceHeader *)__device->Header;
|
||||
|
||||
debug("Header Type: %d", Header->HeaderType);
|
||||
switch (Header->HeaderType)
|
||||
{
|
||||
case 128:
|
||||
warn("Unknown header type %d! Guessing PCI Header 0",
|
||||
Header->HeaderType);
|
||||
[[fallthrough]];
|
||||
case 0: /* PCI Header 0 */
|
||||
{
|
||||
PCI::PCIHeader0 *hdr0 = (PCI::PCIHeader0 *)Header;
|
||||
|
||||
uint32_t BAR[6];
|
||||
size_t BARsSize[6];
|
||||
|
||||
BAR[0] = hdr0->BAR0;
|
||||
BAR[1] = hdr0->BAR1;
|
||||
BAR[2] = hdr0->BAR2;
|
||||
BAR[3] = hdr0->BAR3;
|
||||
BAR[4] = hdr0->BAR4;
|
||||
BAR[5] = hdr0->BAR5;
|
||||
|
||||
debug("Type: %d; IOBase: %#lx; MemoryBase: %#lx",
|
||||
BAR[0] & 1, BAR[1] & (~3), BAR[0] & (~15));
|
||||
|
||||
/* BARs Size */
|
||||
for (short i = 0; i < 6; i++)
|
||||
{
|
||||
if (BAR[i] == 0)
|
||||
continue;
|
||||
|
||||
size_t size;
|
||||
if ((BAR[i] & 1) == 0) /* Memory Base */
|
||||
{
|
||||
hdr0->BAR0 = 0xFFFFFFFF;
|
||||
size = hdr0->BAR0;
|
||||
hdr0->BAR0 = BAR[i];
|
||||
BARsSize[i] = size & (~15);
|
||||
BARsSize[i] = ~BARsSize[i] + 1;
|
||||
BARsSize[i] = BARsSize[i] & 0xFFFFFFFF;
|
||||
debug("BAR%d %#lx size: %d",
|
||||
i, BAR[i], BARsSize[i]);
|
||||
}
|
||||
else if ((BAR[i] & 1) == 1) /* I/O Base */
|
||||
{
|
||||
hdr0->BAR1 = 0xFFFFFFFF;
|
||||
size = hdr0->BAR1;
|
||||
hdr0->BAR1 = BAR[i];
|
||||
BARsSize[i] = size & (~3);
|
||||
BARsSize[i] = ~BARsSize[i] + 1;
|
||||
BARsSize[i] = BARsSize[i] & 0xFFFF;
|
||||
debug("BAR%d %#lx size: %d",
|
||||
i, BAR[i], BARsSize[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Memory::Virtual vmm(KernelPageTable);
|
||||
|
||||
/* Mapping the BARs */
|
||||
for (short i = 0; i < 6; i++)
|
||||
{
|
||||
if (BAR[i] == 0)
|
||||
continue;
|
||||
|
||||
if ((BAR[i] & 1) == 0) /* Memory Base */
|
||||
{
|
||||
uintptr_t BARBase = BAR[i] & (~15);
|
||||
size_t BARSize = BARsSize[i];
|
||||
|
||||
debug("Mapping BAR%d %#lx-%#lx",
|
||||
i, BARBase, BARBase + BARSize);
|
||||
|
||||
if (BARSize == 0)
|
||||
{
|
||||
warn("BAR%d size is zero!", i);
|
||||
BARSize++;
|
||||
}
|
||||
|
||||
vmm.Map((void *)BARBase, (void *)BARBase,
|
||||
BARSize, Memory::RW | Memory::PWT | Memory::PCD);
|
||||
}
|
||||
else if ((BAR[i] & 1) == 1) /* I/O Base */
|
||||
{
|
||||
uintptr_t BARBase = BAR[i] & (~3);
|
||||
size_t BARSize = BARsSize[i];
|
||||
|
||||
debug("Mapping BAR%d %#x-%#x",
|
||||
i, BARBase, BARBase + BARSize);
|
||||
|
||||
if (BARSize == 0)
|
||||
{
|
||||
warn("BAR%d size is zero!", i);
|
||||
BARSize++;
|
||||
}
|
||||
|
||||
vmm.Map((void *)BARBase, (void *)BARBase,
|
||||
BARSize, Memory::RW | Memory::PWT | Memory::PCD);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: /* PCI Header 1 (PCI-to-PCI Bridge) */
|
||||
{
|
||||
fixme("PCI Header 1 (PCI-to-PCI Bridge) not implemented yet");
|
||||
break;
|
||||
}
|
||||
case 2: /* PCI Header 2 (PCI-to-CardBus Bridge) */
|
||||
{
|
||||
fixme("PCI Header 2 (PCI-to-CardBus Bridge) not implemented yet");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
error("Unknown header type %d", Header->HeaderType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Header->Command |= PCI_COMMAND_MASTER |
|
||||
PCI_COMMAND_IO |
|
||||
PCI_COMMAND_MEMORY;
|
||||
Header->Command &= ~PCI_COMMAND_INTX_DISABLE;
|
||||
}
|
||||
|
||||
uint32_t GetBAR(dev_t DriverID, uint8_t i, void *_Header)
|
||||
{
|
||||
dbg_api("%d, %d, %#lx", DriverID, i, _Header);
|
||||
|
||||
PCI::PCIDevice *__device = (PCI::PCIDevice *)_Header;
|
||||
PCI::PCIDeviceHeader *Header = (PCI::PCIDeviceHeader *)__device->Header;
|
||||
|
||||
switch (Header->HeaderType)
|
||||
{
|
||||
case 128:
|
||||
warn("Unknown header type %d! Guessing PCI Header 0",
|
||||
Header->HeaderType);
|
||||
[[fallthrough]];
|
||||
case 0: /* PCI Header 0 */
|
||||
{
|
||||
PCI::PCIHeader0 *hdr0 =
|
||||
(PCI::PCIHeader0 *)Header;
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
return hdr0->BAR0;
|
||||
case 1:
|
||||
return hdr0->BAR1;
|
||||
case 2:
|
||||
return hdr0->BAR2;
|
||||
case 3:
|
||||
return hdr0->BAR3;
|
||||
case 4:
|
||||
return hdr0->BAR4;
|
||||
case 5:
|
||||
return hdr0->BAR5;
|
||||
default:
|
||||
assert(!"Invalid BAR index");
|
||||
}
|
||||
}
|
||||
case 1: /* PCI Header 1 (PCI-to-PCI Bridge) */
|
||||
{
|
||||
PCI::PCIHeader1 *hdr1 =
|
||||
(PCI::PCIHeader1 *)Header;
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
return hdr1->BAR0;
|
||||
case 1:
|
||||
return hdr1->BAR1;
|
||||
default:
|
||||
assert(!"Invalid BAR index");
|
||||
}
|
||||
}
|
||||
case 2: /* PCI Header 2 (PCI-to-CardBus Bridge) */
|
||||
{
|
||||
assert(!"PCI-to-CardBus Bridge not supported");
|
||||
}
|
||||
default:
|
||||
assert(!"Invalid PCI header type");
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t iLine(dev_t DriverID, PCIDevice *Device)
|
||||
{
|
||||
dbg_api("%d, %#lx", DriverID, Device);
|
||||
|
||||
PCIHeader0 *Header = (PCIHeader0 *)Device->Header;
|
||||
return Header->InterruptLine;
|
||||
}
|
||||
|
||||
uint8_t iPin(dev_t DriverID, PCIDevice *Device)
|
||||
{
|
||||
dbg_api("%d, %#lx", DriverID, Device);
|
||||
|
||||
PCIHeader0 *Header = (PCIHeader0 *)Device->Header;
|
||||
return Header->InterruptPin;
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
dev_t RegisterDevice(dev_t DriverID, DeviceType Type, const InodeOperations *Operations)
|
||||
{
|
||||
dbg_api("%d, %d, %#lx", DriverID, Type, Operations);
|
||||
|
||||
return DriverManager->RegisterDevice(DriverID, Type, Operations);
|
||||
}
|
||||
|
||||
int UnregisterDevice(dev_t DriverID, dev_t Device)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, Device);
|
||||
|
||||
return DriverManager->UnregisterDevice(DriverID, Device);
|
||||
}
|
||||
|
||||
int ReportInputEvent(dev_t DriverID, InputReport *Report)
|
||||
{
|
||||
dbg_api("%d, %#lx", DriverID, Report);
|
||||
|
||||
return DriverManager->ReportInputEvent(DriverID, Report);
|
||||
}
|
||||
}
|
||||
|
||||
struct APISymbols
|
||||
{
|
||||
const char *Name;
|
||||
void *Function;
|
||||
};
|
||||
|
||||
static struct APISymbols APISymbols_v0[] = {
|
||||
{"__KernelPrint", (void *)v0::KernelPrint},
|
||||
{"__KernelLog", (void *)v0::KernelLog},
|
||||
|
||||
{"__EnterCriticalSection", (void *)v0::EnterCriticalSection},
|
||||
{"__LeaveCriticalSection", (void *)v0::LeaveCriticalSection},
|
||||
|
||||
{"__RegisterInterruptHandler", (void *)v0::RegisterInterruptHandler},
|
||||
{"__OverrideInterruptHandler", (void *)v0::OverrideInterruptHandler},
|
||||
{"__UnregisterInterruptHandler", (void *)v0::UnregisterInterruptHandler},
|
||||
{"__UnregisterAllInterruptHandlers", (void *)v0::UnregisterAllInterruptHandlers},
|
||||
|
||||
{"__RegisterFileSystem", (void *)v0::RegisterFileSystem},
|
||||
{"__UnregisterFileSystem", (void *)v0::UnregisterFileSystem},
|
||||
|
||||
{"__CreateKernelProcess", (void *)v0::CreateKernelProcess},
|
||||
{"__CreateKernelThread", (void *)v0::CreateKernelThread},
|
||||
{"__GetCurrentProcess", (void *)v0::GetCurrentProcess},
|
||||
{"__KillProcess", (void *)v0::KillProcess},
|
||||
{"__KillThread", (void *)v0::KillThread},
|
||||
{"__Yield", (void *)v0::Yield},
|
||||
{"__Sleep", (void *)v0::Sleep},
|
||||
|
||||
{"__PIC_EOI", (void *)v0::PIC_EOI},
|
||||
{"__IRQ_MASK", (void *)v0::IRQ_MASK},
|
||||
{"__IRQ_UNMASK", (void *)v0::IRQ_UNMASK},
|
||||
{"__PS2Wait", (void *)v0::PS2Wait},
|
||||
{"__PS2WriteCommand", (void *)v0::PS2WriteCommand},
|
||||
{"__PS2WriteData", (void *)v0::PS2WriteData},
|
||||
{"__PS2ReadData", (void *)v0::PS2ReadData},
|
||||
{"__PS2ReadStatus", (void *)v0::PS2ReadStatus},
|
||||
{"__PS2ReadAfterACK", (void *)v0::PS2ReadAfterACK},
|
||||
{"__PS2ClearOutputBuffer", (void *)v0::PS2ClearOutputBuffer},
|
||||
{"__PS2ACKTimeout", (void *)v0::PS2ACKTimeout},
|
||||
|
||||
{"__AllocateMemory", (void *)v0::AllocateMemory},
|
||||
{"__FreeMemory", (void *)v0::FreeMemory},
|
||||
{"__MemoryCopy", (void *)v0::MemoryCopy},
|
||||
{"__MemorySet", (void *)v0::MemorySet},
|
||||
{"__MemoryMove", (void *)v0::MemoryMove},
|
||||
{"__StringLength", (void *)v0::StringLength},
|
||||
{"__strstr", (void *)v0::_strstr},
|
||||
{"__MapPages", (void *)v0::MapPages},
|
||||
{"__UnmapPages", (void *)v0::UnmapPages},
|
||||
{"__AppendMapFlag", (void *)v0::AppendMapFlag},
|
||||
{"__RemoveMapFlag", (void *)v0::RemoveMapFlag},
|
||||
{"_Znwm", (void *)v0::Znwm},
|
||||
{"_ZdlPvm", (void *)v0::ZdlPvm},
|
||||
|
||||
{"__GetPCIDevices", (void *)v0::GetPCIDevices},
|
||||
{"__InitializePCI", (void *)v0::InitializePCI},
|
||||
{"__GetBAR", (void *)v0::GetBAR},
|
||||
{"__iLine", (void *)v0::iLine},
|
||||
{"__iPin", (void *)v0::iPin},
|
||||
|
||||
{"__RegisterDevice", (void *)v0::RegisterDevice},
|
||||
{"__UnregisterDevice", (void *)v0::UnregisterDevice},
|
||||
{"__ReportInputEvent", (void *)v0::ReportInputEvent},
|
||||
};
|
||||
|
||||
long __KernelUndefinedFunction(long arg0, long arg1, long arg2, long arg3,
|
||||
long arg4, long arg5, long arg6, long arg7)
|
||||
{
|
||||
debug("%#lx, %#lx, %#lx, %#lx, %#lx, %#lx, %#lx, %#lx",
|
||||
arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
||||
assert(!"Undefined kernel driver API function called!");
|
||||
CPU::Stop();
|
||||
}
|
||||
|
||||
void *GetSymbolByName(const char *Name, int Version)
|
||||
{
|
||||
switch (Version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
for (auto sym : APISymbols_v0)
|
||||
{
|
||||
if (strcmp(Name, sym.Name) != 0)
|
||||
continue;
|
||||
|
||||
debug("Symbol %s found in API version %d", Name, Version);
|
||||
return sym.Function;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(!"Invalid API version");
|
||||
}
|
||||
|
||||
error("Symbol %s not found in API version %d", Name, Version);
|
||||
KPrint("Driver API symbol \"%s\" not found!", Name);
|
||||
return (void *)__KernelUndefinedFunction;
|
||||
}
|
890
Kernel/core/driver/daemon.cpp
Normal file
890
Kernel/core/driver/daemon.cpp
Normal file
@ -0,0 +1,890 @@
|
||||
/*
|
||||
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 <driver.hpp>
|
||||
|
||||
#include <interface/driver.h>
|
||||
#include <interface/input.h>
|
||||
#include <memory.hpp>
|
||||
#include <stropts.h>
|
||||
#include <ints.hpp>
|
||||
#include <task.hpp>
|
||||
#include <printf.h>
|
||||
#include <exec.hpp>
|
||||
#include <rand.hpp>
|
||||
#include <cwalk.h>
|
||||
#include <md5.h>
|
||||
|
||||
#include "../../kernel.h"
|
||||
|
||||
using namespace vfs;
|
||||
|
||||
namespace Driver
|
||||
{
|
||||
/**
|
||||
* maj = 0
|
||||
* min:
|
||||
* 0 - <ROOT>
|
||||
* 1 - /proc/self
|
||||
* 2 - /dev/null
|
||||
* 3 - /dev/zero
|
||||
* 4 - /dev/random
|
||||
* 5 - /dev/mem
|
||||
* 6 - /dev/kcon
|
||||
* 7 - /dev/tty
|
||||
* 8 - /dev/ptmx
|
||||
*
|
||||
* maj = 1
|
||||
* min:
|
||||
* 0 - /dev/input/keyboard
|
||||
* 1 - /dev/input/mouse
|
||||
* ..- /dev/input/eventX
|
||||
*/
|
||||
|
||||
TTY::PTMXDevice *ptmx = nullptr;
|
||||
|
||||
int __fs_Lookup(struct Inode *_Parent, const char *Name, struct Inode **Result)
|
||||
{
|
||||
func("%#lx %s %#lx", _Parent, Name, Result);
|
||||
|
||||
assert(_Parent != nullptr);
|
||||
|
||||
const char *basename;
|
||||
size_t length;
|
||||
cwk_path_get_basename(Name, &basename, &length);
|
||||
if (basename == NULL)
|
||||
{
|
||||
error("Invalid name %s", Name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
auto Parent = (Manager::DeviceInode *)_Parent;
|
||||
for (const auto &child : Parent->Children)
|
||||
{
|
||||
debug("Comparing %s with %s", basename, child->Name.c_str());
|
||||
if (strcmp(child->Name.c_str(), basename) != 0)
|
||||
continue;
|
||||
|
||||
*Result = &child->Node;
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug("Not found %s", Name);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int __fs_Create(struct Inode *_Parent, const char *Name, mode_t Mode, struct Inode **Result)
|
||||
{
|
||||
func("%#lx %s %d", _Parent, Name, Mode);
|
||||
|
||||
assert(_Parent != nullptr);
|
||||
|
||||
/* We expect to be /dev or children of it */
|
||||
auto Parent = (Manager::DeviceInode *)_Parent;
|
||||
auto _dev = new Manager::DeviceInode;
|
||||
_dev->Parent = nullptr;
|
||||
_dev->ParentInode = _Parent;
|
||||
_dev->Name = Name;
|
||||
_dev->Node.Mode = Mode;
|
||||
_dev->Node.Index = Parent->Node.Index + Parent->Children.size();
|
||||
Parent->Children.push_back(_dev);
|
||||
|
||||
*Result = &_dev->Node;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t __fs_Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
func("%#lx %d %d", Node, Size, Offset);
|
||||
switch (Node->GetMajor())
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 2: /* /dev/null */
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
case 3: /* /dev/zero */
|
||||
{
|
||||
if (Size <= 0)
|
||||
return 0;
|
||||
|
||||
memset(Buffer, 0, Size);
|
||||
return Size;
|
||||
}
|
||||
case 4: /* /dev/random */
|
||||
{
|
||||
if (Size <= 0)
|
||||
return 0;
|
||||
|
||||
if (Size < sizeof(uint64_t))
|
||||
{
|
||||
uint8_t *buf = (uint8_t *)Buffer;
|
||||
for (size_t i = 0; i < Size; i++)
|
||||
buf[i] = (uint8_t)(Random::rand16() & 0xFF);
|
||||
return Size;
|
||||
}
|
||||
|
||||
uint64_t *buf = (uint64_t *)Buffer;
|
||||
for (size_t i = 0; i < Size / sizeof(uint64_t); i++)
|
||||
buf[i] = Random::rand64();
|
||||
return Size;
|
||||
}
|
||||
case 5: /* /dev/mem */
|
||||
{
|
||||
stub;
|
||||
return 0;
|
||||
}
|
||||
case 6: /* /dev/kcon */
|
||||
return KernelConsole::CurrentTerminal.load()->Read(Buffer, Size, Offset);
|
||||
case 7: /* /dev/tty */
|
||||
{
|
||||
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
|
||||
if (tty == nullptr)
|
||||
return -ENOTTY;
|
||||
return tty->Read(Buffer, Size, Offset);
|
||||
}
|
||||
case 8: /* /dev/ptmx */
|
||||
return -ENOSYS;
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 0: /* /dev/input/keyboard */
|
||||
{
|
||||
if (Size < sizeof(KeyboardReport))
|
||||
return -EINVAL;
|
||||
|
||||
size_t nReads = Size / sizeof(KeyboardReport);
|
||||
|
||||
KeyboardReport *report = (KeyboardReport *)Buffer;
|
||||
|
||||
while (DriverManager->GlobalKeyboardInputReports.Count() == 0)
|
||||
TaskManager->Yield();
|
||||
|
||||
DriverManager->GlobalKeyboardInputReports.Read(report, nReads);
|
||||
return sizeof(KeyboardReport) * nReads;
|
||||
}
|
||||
case 1: /* /dev/input/mouse */
|
||||
{
|
||||
if (Size < sizeof(MouseReport))
|
||||
return -EINVAL;
|
||||
|
||||
size_t nReads = Size / sizeof(MouseReport);
|
||||
|
||||
MouseReport *report = (MouseReport *)Buffer;
|
||||
|
||||
while (DriverManager->GlobalMouseInputReports.Count() == 0)
|
||||
TaskManager->Yield();
|
||||
|
||||
DriverManager->GlobalMouseInputReports.Read(report, nReads);
|
||||
return sizeof(MouseReport) * nReads;
|
||||
}
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
default:
|
||||
{
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(Node->GetMajor());
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", Node->GetMajor());
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
auto dop = drv->DeviceOperations;
|
||||
auto dOps = dop->find(Node->GetMinor());
|
||||
if (dOps == dop->end())
|
||||
ReturnLogError(-EINVAL, "Device %d not found", Node->GetMinor());
|
||||
AssertReturnError(dOps->second.Ops, -ENOTSUP);
|
||||
AssertReturnError(dOps->second.Ops->Read, -ENOTSUP);
|
||||
return dOps->second.Ops->Read(Node, Buffer, Size, Offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t __fs_Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
func("%#lx %p %d %d", Node, Buffer, Size, Offset);
|
||||
|
||||
switch (Node->GetMajor())
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 2: /* /dev/null */
|
||||
{
|
||||
return Size;
|
||||
}
|
||||
case 3: /* /dev/zero */
|
||||
{
|
||||
return Size;
|
||||
}
|
||||
case 4: /* /dev/random */
|
||||
{
|
||||
return Size;
|
||||
}
|
||||
case 5: /* /dev/mem */
|
||||
{
|
||||
stub;
|
||||
return 0;
|
||||
}
|
||||
case 6: /* /dev/kcon */
|
||||
return KernelConsole::CurrentTerminal.load()->Write(Buffer, Size, Offset);
|
||||
case 7: /* /dev/tty */
|
||||
{
|
||||
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
|
||||
if (tty == nullptr)
|
||||
return -ENOTTY;
|
||||
return tty->Write(Buffer, Size, Offset);
|
||||
}
|
||||
case 8: /* /dev/ptmx */
|
||||
return -ENOSYS;
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 0: /* /dev/input/keyboard */
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
case 1: /* /dev/input/mouse */
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
default:
|
||||
{
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(Node->GetMajor());
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", Node->GetMajor());
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
auto dop = drv->DeviceOperations;
|
||||
auto dOps = dop->find(Node->GetMinor());
|
||||
if (dOps == dop->end())
|
||||
ReturnLogError(-EINVAL, "Device %d not found", Node->GetMinor());
|
||||
AssertReturnError(dOps->second.Ops, -ENOTSUP);
|
||||
AssertReturnError(dOps->second.Ops->Write, -ENOTSUP);
|
||||
return dOps->second.Ops->Write(Node, Buffer, Size, Offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int __fs_Open(struct Inode *Node, int Flags, mode_t Mode)
|
||||
{
|
||||
func("%#lx %d %d", Node, Flags, Mode);
|
||||
|
||||
switch (Node->GetMajor())
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 2: /* /dev/null */
|
||||
case 3: /* /dev/zero */
|
||||
case 4: /* /dev/random */
|
||||
case 5: /* /dev/mem */
|
||||
return -ENOSYS;
|
||||
case 6: /* /dev/kcon */
|
||||
return KernelConsole::CurrentTerminal.load()->Open(Flags, Mode);
|
||||
case 7: /* /dev/tty */
|
||||
{
|
||||
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
|
||||
if (tty == nullptr)
|
||||
return -ENOTTY;
|
||||
return tty->Open(Flags, Mode);
|
||||
}
|
||||
case 8: /* /dev/ptmx */
|
||||
return ptmx->Open();
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 0: /* /dev/input/keyboard */
|
||||
case 1: /* /dev/input/mouse */
|
||||
return -ENOTSUP;
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
default:
|
||||
{
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(Node->GetMajor());
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", Node->GetMajor());
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
auto dop = drv->DeviceOperations;
|
||||
auto dOps = dop->find(Node->GetMinor());
|
||||
if (dOps == dop->end())
|
||||
ReturnLogError(-EINVAL, "Device %d not found", Node->GetMinor());
|
||||
AssertReturnError(dOps->second.Ops, -ENOTSUP);
|
||||
AssertReturnError(dOps->second.Ops->Open, -ENOTSUP);
|
||||
return dOps->second.Ops->Open(Node, Flags, Mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int __fs_Close(struct Inode *Node)
|
||||
{
|
||||
func("%#lx", Node);
|
||||
|
||||
switch (Node->GetMajor())
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 2: /* /dev/null */
|
||||
case 3: /* /dev/zero */
|
||||
case 4: /* /dev/random */
|
||||
case 5: /* /dev/mem */
|
||||
return -ENOSYS;
|
||||
case 6: /* /dev/kcon */
|
||||
return KernelConsole::CurrentTerminal.load()->Close();
|
||||
case 7: /* /dev/tty */
|
||||
{
|
||||
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
|
||||
if (tty == nullptr)
|
||||
return -ENOTTY;
|
||||
return tty->Close();
|
||||
}
|
||||
case 8: /* /dev/ptmx */
|
||||
return ptmx->Close();
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 0: /* /dev/input/keyboard */
|
||||
case 1: /* /dev/input/mouse */
|
||||
return -ENOTSUP;
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
default:
|
||||
{
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(Node->GetMajor());
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", Node->GetMajor());
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
auto dop = drv->DeviceOperations;
|
||||
auto dOps = dop->find(Node->GetMinor());
|
||||
if (dOps == dop->end())
|
||||
ReturnLogError(-EINVAL, "Device %d not found", Node->GetMinor());
|
||||
AssertReturnError(dOps->second.Ops, -ENOTSUP);
|
||||
AssertReturnError(dOps->second.Ops->Close, -ENOTSUP);
|
||||
return dOps->second.Ops->Close(Node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int __fs_Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
|
||||
{
|
||||
func("%#lx %lu %#lx", Node, Request, Argp);
|
||||
|
||||
switch (Node->GetMajor())
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 2: /* /dev/null */
|
||||
case 3: /* /dev/zero */
|
||||
case 4: /* /dev/random */
|
||||
case 5: /* /dev/mem */
|
||||
return -ENOSYS;
|
||||
case 6: /* /dev/kcon */
|
||||
return KernelConsole::CurrentTerminal.load()->Ioctl(Request, Argp);
|
||||
case 7: /* /dev/tty */
|
||||
{
|
||||
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
|
||||
if (tty == nullptr)
|
||||
return -ENOTTY;
|
||||
return tty->Ioctl(Request, Argp);
|
||||
}
|
||||
case 8: /* /dev/ptmx */
|
||||
return -ENOSYS;
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 0: /* /dev/input/keyboard */
|
||||
case 1: /* /dev/input/mouse */
|
||||
return -ENOSYS;
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
default:
|
||||
{
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(Node->GetMajor());
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", Node->GetMajor());
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
auto dop = drv->DeviceOperations;
|
||||
auto dOps = dop->find(Node->GetMinor());
|
||||
if (dOps == dop->end())
|
||||
ReturnLogError(-EINVAL, "Device %d not found", Node->GetMinor());
|
||||
AssertReturnError(dOps->second.Ops, -ENOTSUP);
|
||||
AssertReturnError(dOps->second.Ops->Ioctl, -ENOTSUP);
|
||||
return dOps->second.Ops->Ioctl(Node, Request, Argp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__no_sanitize("alignment")
|
||||
ssize_t __fs_Readdir(struct Inode *_Node, struct kdirent *Buffer, size_t Size, off_t Offset, off_t Entries)
|
||||
{
|
||||
func("%#lx %#lx %d %d %d", _Node, Buffer, Size, Offset, Entries);
|
||||
|
||||
auto Node = (Manager::DeviceInode *)_Node;
|
||||
|
||||
off_t realOffset = Offset;
|
||||
|
||||
size_t totalSize = 0;
|
||||
uint16_t reclen = 0;
|
||||
struct kdirent *ent = nullptr;
|
||||
|
||||
if (Offset == 0)
|
||||
{
|
||||
reclen = (uint16_t)(offsetof(struct kdirent, d_name) + strlen(".") + 1);
|
||||
if (totalSize + reclen >= Size)
|
||||
return -EINVAL;
|
||||
|
||||
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
|
||||
ent->d_ino = Node->Node.Index;
|
||||
ent->d_off = Offset++;
|
||||
ent->d_reclen = reclen;
|
||||
ent->d_type = DT_DIR;
|
||||
strcpy(ent->d_name, ".");
|
||||
totalSize += reclen;
|
||||
}
|
||||
|
||||
if (Offset <= 1)
|
||||
{
|
||||
reclen = (uint16_t)(offsetof(struct kdirent, d_name) + strlen("..") + 1);
|
||||
if (totalSize + reclen >= Size)
|
||||
{
|
||||
if (realOffset == 1)
|
||||
return -EINVAL;
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
|
||||
|
||||
if (Node->Parent)
|
||||
ent->d_ino = Node->Parent->Node->Index;
|
||||
else if (Node->ParentInode)
|
||||
ent->d_ino = Node->ParentInode->Index;
|
||||
else
|
||||
{
|
||||
warn("Parent is null for %s", Node->Name.c_str());
|
||||
ent->d_ino = Node->Node.Index;
|
||||
}
|
||||
ent->d_off = Offset++;
|
||||
ent->d_reclen = reclen;
|
||||
ent->d_type = DT_DIR;
|
||||
strcpy(ent->d_name, "..");
|
||||
totalSize += reclen;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(Node->Node.Mode))
|
||||
return -ENOTDIR;
|
||||
|
||||
if ((Offset >= 2 ? (Offset - 2) : Offset) > (off_t)Node->Children.size())
|
||||
return -EINVAL;
|
||||
|
||||
off_t entries = 0;
|
||||
for (const auto &var : Node->Children)
|
||||
{
|
||||
debug("iterating \"%s\" inside \"%s\"", var->Name.c_str(), Node->Name.c_str());
|
||||
if (var->Node.Offset < realOffset)
|
||||
{
|
||||
debug("skipping \"%s\" (%d < %d)", var->Name.c_str(), var->Node.Offset, Offset);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entries >= Entries)
|
||||
break;
|
||||
|
||||
reclen = (uint16_t)(offsetof(struct kdirent, d_name) + strlen(var->Name.c_str()) + 1);
|
||||
|
||||
if (totalSize + reclen >= Size)
|
||||
break;
|
||||
|
||||
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
|
||||
ent->d_ino = var->Node.Index;
|
||||
ent->d_off = var->Node.Offset;
|
||||
ent->d_reclen = reclen;
|
||||
ent->d_type = IFTODT(var->Node.Mode);
|
||||
strncpy(ent->d_name, var->Name.c_str(), strlen(var->Name.c_str()));
|
||||
|
||||
totalSize += reclen;
|
||||
entries++;
|
||||
}
|
||||
|
||||
if (totalSize + sizeof(struct kdirent) >= Size)
|
||||
return totalSize;
|
||||
|
||||
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
|
||||
ent->d_ino = 0;
|
||||
ent->d_off = 0;
|
||||
ent->d_reclen = 0;
|
||||
ent->d_type = DT_UNKNOWN;
|
||||
ent->d_name[0] = '\0';
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
void ManagerDaemonWrapper() { DriverManager->Daemon(); }
|
||||
|
||||
void Manager::Daemon()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
TaskManager->Sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
dev_t Manager::RegisterInputDevice(std::unordered_map<dev_t, DriverHandlers> *dop,
|
||||
dev_t DriverID, size_t i, const InodeOperations *Operations)
|
||||
{
|
||||
std::string prefix = "event";
|
||||
for (size_t j = 0; j < 128; j++)
|
||||
{
|
||||
std::string deviceName = prefix + std::to_string(j);
|
||||
FileNode *node = fs->GetByPath(deviceName.c_str(), devInputNode);
|
||||
if (node)
|
||||
continue;
|
||||
|
||||
/* c rwx r-- r-- */
|
||||
mode_t mode = S_IRWXU |
|
||||
S_IRGRP |
|
||||
S_IROTH |
|
||||
S_IFCHR;
|
||||
|
||||
node = fs->ForceCreate(devInputNode, deviceName.c_str(), mode);
|
||||
node->Node->SetDevice(DriverID, i);
|
||||
|
||||
DriverHandlers dh{};
|
||||
dh.Ops = Operations;
|
||||
dh.Node = node->Node;
|
||||
dh.InputReports = new RingBuffer<InputReport>(16);
|
||||
dop->insert({i, std::move(dh)});
|
||||
return i;
|
||||
}
|
||||
|
||||
ReturnLogError(-1, "No available slots for device %d", DriverID);
|
||||
return -1; /* -Werror=return-type */
|
||||
}
|
||||
|
||||
dev_t Manager::RegisterBlockDevice(std::unordered_map<dev_t, DriverHandlers> *dop,
|
||||
dev_t DriverID, size_t i, const InodeOperations *Operations)
|
||||
{
|
||||
std::string prefix = "event";
|
||||
for (size_t j = 0; j < 128; j++)
|
||||
{
|
||||
std::string deviceName = prefix + std::to_string(j);
|
||||
FileNode *node = fs->GetByPath(deviceName.c_str(), devInputNode);
|
||||
if (node)
|
||||
continue;
|
||||
|
||||
/* c rwx r-- r-- */
|
||||
mode_t mode = S_IRWXU |
|
||||
S_IRGRP |
|
||||
S_IROTH |
|
||||
S_IFBLK;
|
||||
|
||||
node = fs->ForceCreate(devInputNode, deviceName.c_str(), mode);
|
||||
node->Node->SetDevice(DriverID, i);
|
||||
|
||||
DriverHandlers dh{};
|
||||
dh.Ops = Operations;
|
||||
dh.Node = node->Node;
|
||||
dh.InputReports = new RingBuffer<InputReport>(16);
|
||||
dop->insert({i, std::move(dh)});
|
||||
return i;
|
||||
}
|
||||
|
||||
ReturnLogError(-1, "No available slots for device %d", DriverID);
|
||||
return -1; /* -Werror=return-type */
|
||||
}
|
||||
|
||||
dev_t Manager::RegisterDevice(dev_t DriverID, DeviceType Type, const InodeOperations *Operations)
|
||||
{
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(DriverID);
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
auto dop = drv->DeviceOperations;
|
||||
for (size_t i = 0; i < 128; i++)
|
||||
{
|
||||
const auto dOps = dop->find(i);
|
||||
const auto dOpsEnd = dop->end();
|
||||
if (dOps != dOpsEnd)
|
||||
continue;
|
||||
|
||||
DeviceType devType = (DeviceType)(Type & DEVICE_TYPE_MASK);
|
||||
switch (devType)
|
||||
{
|
||||
case DEVICE_TYPE_INPUT:
|
||||
return RegisterInputDevice(dop, DriverID, i, Operations);
|
||||
case DEVICE_TYPE_BLOCK:
|
||||
return RegisterBlockDevice(dop, DriverID, i, Operations);
|
||||
default:
|
||||
ReturnLogError(-1, "Invalid device type %d", Type);
|
||||
}
|
||||
}
|
||||
|
||||
ReturnLogError(-1, "No available slots for device %d", DriverID);
|
||||
}
|
||||
|
||||
int Manager::UnregisterDevice(dev_t DriverID, dev_t Device)
|
||||
{
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(DriverID);
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
auto dop = drv->DeviceOperations;
|
||||
const auto dOps = dop->find(Device);
|
||||
if (dOps == dop->end())
|
||||
ReturnLogError(-EINVAL, "Device %d not found", Device);
|
||||
dop->erase(dOps);
|
||||
fixme("remove eventX from /dev/input");
|
||||
fixme("delete InputReports");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Manager::ReportInputEvent(dev_t DriverID, InputReport *Report)
|
||||
{
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(DriverID);
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
auto dop = drv->DeviceOperations;
|
||||
auto dOps = dop->find(Report->Device);
|
||||
if (dOps == dop->end())
|
||||
ReturnLogError(-EINVAL, "Device %d not found", Report->Device);
|
||||
|
||||
dOps->second.InputReports->Write(Report, 1);
|
||||
|
||||
switch (Report->Type)
|
||||
{
|
||||
case INPUT_TYPE_KEYBOARD:
|
||||
{
|
||||
KeyboardReport *kReport = &Report->Keyboard;
|
||||
GlobalKeyboardInputReports.Write(kReport, 1);
|
||||
break;
|
||||
}
|
||||
case INPUT_TYPE_MOUSE:
|
||||
{
|
||||
MouseReport *mReport = &Report->Mouse;
|
||||
GlobalMouseInputReports.Write(mReport, 1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(!"Invalid input type");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Manager::InitializeDaemonFS()
|
||||
{
|
||||
ptmx = new TTY::PTMXDevice;
|
||||
|
||||
dev_t MinorID = 0;
|
||||
DeviceInode *_dev = new DeviceInode;
|
||||
_dev->Name = "dev";
|
||||
|
||||
/* d rwx r-- r-- */
|
||||
mode_t mode = S_IRWXU |
|
||||
S_IRGRP |
|
||||
S_IROTH |
|
||||
S_IFDIR;
|
||||
Inode *dev = (Inode *)_dev;
|
||||
dev->Mode = mode;
|
||||
dev->Flags = I_FLAG_MOUNTPOINT | I_FLAG_CACHE_KEEP;
|
||||
|
||||
FileSystemInfo *fsi = new FileSystemInfo;
|
||||
fsi->Name = "Device Virtual File System";
|
||||
fsi->RootName = "dev";
|
||||
fsi->Flags = I_FLAG_ROOT | I_FLAG_MOUNTPOINT | I_FLAG_CACHE_KEEP;
|
||||
fsi->SuperOps = {};
|
||||
fsi->Ops.Lookup = __fs_Lookup;
|
||||
fsi->Ops.Create = __fs_Create;
|
||||
fsi->Ops.Read = __fs_Read;
|
||||
fsi->Ops.Write = __fs_Write;
|
||||
fsi->Ops.Open = __fs_Open;
|
||||
fsi->Ops.Close = __fs_Close;
|
||||
fsi->Ops.Ioctl = __fs_Ioctl;
|
||||
fsi->Ops.ReadDir = __fs_Readdir;
|
||||
|
||||
dev->Device = fs->RegisterFileSystem(fsi, dev);
|
||||
dev->SetDevice(0, MinorID++);
|
||||
MinorID++; /* 1 = /proc/self */
|
||||
|
||||
devNode = fs->Mount(fs->GetRoot(0), dev, "/dev");
|
||||
_dev->Parent = devNode->Parent;
|
||||
_dev->ParentInode = devNode->Parent->Node;
|
||||
|
||||
/* d rwx r-- r-- */
|
||||
mode = S_IRWXU |
|
||||
S_IRGRP |
|
||||
S_IROTH |
|
||||
S_IFDIR;
|
||||
DeviceInode *input = new DeviceInode;
|
||||
input->Parent = devNode;
|
||||
input->ParentInode = devNode->Node;
|
||||
input->Name = "input";
|
||||
input->Node.Device = dev->Device;
|
||||
input->Node.Mode = mode;
|
||||
input->Node.Flags = I_FLAG_CACHE_KEEP;
|
||||
input->Node.Offset = _dev->Children.size();
|
||||
_dev->Children.push_back(input);
|
||||
devInputNode = fs->GetByPath("input", devNode);
|
||||
|
||||
auto createDevice = [](DeviceInode *p1, FileNode *p2, const std::string &name, dev_t maj, dev_t min, mode_t mode)
|
||||
{
|
||||
DeviceInode *device = new DeviceInode;
|
||||
device->Parent = p2;
|
||||
device->ParentInode = p2->Node;
|
||||
device->Name = name;
|
||||
device->Node.Device = p2->Node->Device;
|
||||
device->Node.Mode = mode;
|
||||
device->Node.SetDevice(maj, min);
|
||||
device->Node.Flags = I_FLAG_CACHE_KEEP;
|
||||
device->Node.Offset = p1->Children.size();
|
||||
p1->Children.push_back(device);
|
||||
};
|
||||
|
||||
/* c rw- rw- rw- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP | S_IWGRP |
|
||||
S_IROTH | S_IWOTH |
|
||||
S_IFCHR;
|
||||
createDevice(_dev, devNode, "null", 0, MinorID++, mode);
|
||||
|
||||
/* c rw- rw- rw- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP | S_IWGRP |
|
||||
S_IROTH | S_IWOTH |
|
||||
S_IFCHR;
|
||||
createDevice(_dev, devNode, "zero", 0, MinorID++, mode);
|
||||
|
||||
/* c rw- rw- rw- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP | S_IWGRP |
|
||||
S_IROTH | S_IWOTH |
|
||||
S_IFCHR;
|
||||
createDevice(_dev, devNode, "random", 0, MinorID++, mode);
|
||||
|
||||
/* c rw- r-- --- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP |
|
||||
|
||||
S_IFCHR;
|
||||
createDevice(_dev, devNode, "mem", 0, MinorID++, mode);
|
||||
|
||||
/* c rw- r-- --- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP |
|
||||
|
||||
S_IFCHR;
|
||||
createDevice(_dev, devNode, "kcon", 0, MinorID++, mode);
|
||||
|
||||
/* c rw- rw- rw- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP | S_IWGRP |
|
||||
S_IRUSR | S_IWUSR |
|
||||
S_IFCHR;
|
||||
createDevice(_dev, devNode, "tty", 0, MinorID++, mode);
|
||||
|
||||
/* c rw- rw- rw- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP | S_IWGRP |
|
||||
S_IRUSR | S_IWUSR |
|
||||
S_IFCHR;
|
||||
createDevice(_dev, devNode, "ptmx", 0, MinorID++, mode);
|
||||
|
||||
/* ------------------------------------------------------ */
|
||||
|
||||
MinorID = 0;
|
||||
|
||||
/* c rw- r-- --- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP |
|
||||
|
||||
S_IFCHR;
|
||||
createDevice(input, devInputNode, "keyboard", 1, MinorID++, mode);
|
||||
|
||||
/* c rw- r-- --- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP |
|
||||
|
||||
S_IFCHR;
|
||||
createDevice(input, devInputNode, "mouse", 1, MinorID++, mode);
|
||||
}
|
||||
}
|
524
Kernel/core/driver/driver.cpp
Normal file
524
Kernel/core/driver/driver.cpp
Normal file
@ -0,0 +1,524 @@
|
||||
/*
|
||||
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 <driver.hpp>
|
||||
|
||||
#include <interface/driver.h>
|
||||
#include <interface/input.h>
|
||||
#include <memory.hpp>
|
||||
#include <ints.hpp>
|
||||
#include <task.hpp>
|
||||
#include <printf.h>
|
||||
#include <exec.hpp>
|
||||
#include <rand.hpp>
|
||||
#include <cwalk.h>
|
||||
#include <md5.h>
|
||||
|
||||
#include "../../kernel.h"
|
||||
|
||||
using namespace vfs;
|
||||
|
||||
namespace Driver
|
||||
{
|
||||
void Manager::PreloadDrivers()
|
||||
{
|
||||
debug("Initializing driver manager");
|
||||
const char *DriverDirectory = Config.DriverDirectory;
|
||||
FileNode *drvDirNode = fs->GetByPath(DriverDirectory, nullptr);
|
||||
if (!drvDirNode)
|
||||
{
|
||||
error("Failed to open driver directory %s", DriverDirectory);
|
||||
KPrint("Failed to open driver directory %s", DriverDirectory);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (const auto &drvNode in drvDirNode->Children)
|
||||
{
|
||||
debug("Checking driver %s", drvNode->Path.c_str());
|
||||
if (!drvNode->IsRegularFile())
|
||||
continue;
|
||||
|
||||
if (Execute::GetBinaryType(drvNode->Path) != Execute::BinTypeELF)
|
||||
{
|
||||
error("Driver %s is not an ELF binary", drvNode->Path.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
DriverObject drvObj = {.BaseAddress = 0,
|
||||
.EntryPoint = 0,
|
||||
.vma = new Memory::VirtualMemoryArea(thisProcess->PageTable),
|
||||
.Path = drvNode->Path,
|
||||
.InterruptHandlers = new std::unordered_map<uint8_t, void *>(),
|
||||
.DeviceOperations = new std::unordered_map<dev_t, DriverHandlers>(),
|
||||
.ID = DriverIDCounter};
|
||||
|
||||
int err = this->LoadDriverFile(drvObj, drvNode);
|
||||
debug("err = %d (%s)", err, strerror(err));
|
||||
if (err != 0)
|
||||
{
|
||||
error("Failed to load driver %s: %s",
|
||||
drvNode->Path.c_str(), strerror(err));
|
||||
|
||||
delete drvObj.vma;
|
||||
delete drvObj.InterruptHandlers;
|
||||
delete drvObj.DeviceOperations;
|
||||
continue;
|
||||
}
|
||||
|
||||
debug("gdb: \"0x%lX\" %s", drvObj.BaseAddress, drvObj.Name);
|
||||
|
||||
Drivers.insert({DriverIDCounter++, drvObj});
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::LoadAllDrivers()
|
||||
{
|
||||
if (Drivers.empty())
|
||||
{
|
||||
KPrint("\x1b[1;31;41mNo drivers to load");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (auto &var in Drivers)
|
||||
{
|
||||
DriverObject &Drv = var.second;
|
||||
|
||||
debug("Calling driver %s at %#lx", Drv.Path.c_str(), Drv.EntryPoint);
|
||||
int (*DrvInit)(dev_t) = (int (*)(dev_t))Drv.EntryPoint;
|
||||
Drv.ErrorCode = DrvInit(Drv.ID);
|
||||
if (Drv.ErrorCode < 0)
|
||||
{
|
||||
KPrint("FATAL: _start() failed for %s: %s",
|
||||
Drv.Name, strerror(Drv.ErrorCode));
|
||||
error("Failed to load driver %s: %s",
|
||||
Drv.Path.c_str(), strerror(Drv.ErrorCode));
|
||||
|
||||
Drv.vma->FreeAllPages();
|
||||
continue;
|
||||
}
|
||||
|
||||
KPrint("Loading driver %s", Drv.Name);
|
||||
|
||||
debug("Calling Probe()=%#lx on driver %s",
|
||||
Drv.Probe, Drv.Path.c_str());
|
||||
Drv.ErrorCode = Drv.Probe();
|
||||
if (Drv.ErrorCode < 0)
|
||||
{
|
||||
KPrint("Probe() failed for %s: %s",
|
||||
Drv.Name, strerror(Drv.ErrorCode));
|
||||
error("Failed to probe driver %s: %s",
|
||||
Drv.Path.c_str(), strerror(Drv.ErrorCode));
|
||||
|
||||
Drv.vma->FreeAllPages();
|
||||
continue;
|
||||
}
|
||||
|
||||
debug("Calling driver Entry()=%#lx function on driver %s",
|
||||
Drv.Entry, Drv.Path.c_str());
|
||||
Drv.ErrorCode = Drv.Entry();
|
||||
if (Drv.ErrorCode < 0)
|
||||
{
|
||||
KPrint("Entry() failed for %s: %s",
|
||||
Drv.Name, strerror(Drv.ErrorCode));
|
||||
error("Failed to initialize driver %s: %s",
|
||||
Drv.Path.c_str(), strerror(Drv.ErrorCode));
|
||||
|
||||
Drv.vma->FreeAllPages();
|
||||
continue;
|
||||
}
|
||||
|
||||
debug("Loaded driver %s", Drv.Path.c_str());
|
||||
Drv.Initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::UnloadAllDrivers()
|
||||
{
|
||||
foreach (auto &var in Drivers)
|
||||
{
|
||||
DriverObject *Drv = &var.second;
|
||||
if (!Drv->Initialized)
|
||||
continue;
|
||||
|
||||
debug("Unloading driver %s", Drv->Name);
|
||||
int err = Drv->Final();
|
||||
if (err < 0)
|
||||
{
|
||||
warn("Failed to unload driver %s: %s",
|
||||
Drv->Name, strerror(err));
|
||||
}
|
||||
|
||||
if (!Drv->InterruptHandlers->empty())
|
||||
{
|
||||
foreach (auto &rInt in * Drv->InterruptHandlers)
|
||||
{
|
||||
Interrupts::RemoveHandler((void (*)(CPU::TrapFrame *))rInt.second);
|
||||
}
|
||||
Drv->InterruptHandlers->clear();
|
||||
}
|
||||
}
|
||||
Drivers.clear();
|
||||
}
|
||||
|
||||
void Manager::Panic()
|
||||
{
|
||||
Memory::Virtual vmm;
|
||||
if (Drivers.size() == 0)
|
||||
return;
|
||||
|
||||
foreach (auto Driver in Drivers)
|
||||
{
|
||||
if (!Driver.second.Initialized)
|
||||
continue;
|
||||
|
||||
trace("Panic on driver %s", Driver.second.Name);
|
||||
debug("%#lx", Driver.second.Panic);
|
||||
|
||||
/* Crash while probing? */
|
||||
if (Driver.second.Panic && vmm.Check((void *)Driver.second.Panic))
|
||||
Driver.second.Panic();
|
||||
else
|
||||
error("No panic function for driver %s",
|
||||
Driver.second.Name);
|
||||
}
|
||||
}
|
||||
|
||||
int Manager::LoadDriverFile(DriverObject &Drv, FileNode *File)
|
||||
{
|
||||
trace("Loading driver %s in memory", File->Name.c_str());
|
||||
|
||||
Elf_Ehdr ELFHeader{};
|
||||
File->Read(&ELFHeader, sizeof(Elf_Ehdr), 0);
|
||||
|
||||
AssertReturnError(ELFHeader.e_ident[EI_CLASS] == ELFCLASS64, -ENOEXEC);
|
||||
AssertReturnError(ELFHeader.e_ident[EI_DATA] == ELFDATA2LSB, -ENOEXEC);
|
||||
AssertReturnError(ELFHeader.e_ident[EI_OSABI] == ELFOSABI_SYSV, -ENOEXEC);
|
||||
AssertReturnError(ELFHeader.e_ident[EI_ABIVERSION] == 0, -ENOEXEC);
|
||||
AssertReturnError(ELFHeader.e_type == ET_DYN, -ENOEXEC);
|
||||
AssertReturnError(ELFHeader.e_machine == EM_X86_64, -ENOEXEC);
|
||||
AssertReturnError(ELFHeader.e_version == EV_CURRENT, -ENOEXEC);
|
||||
AssertReturnError(ELFHeader.e_entry != 0x0, -ENOEXEC);
|
||||
AssertReturnError(ELFHeader.e_shstrndx != SHN_UNDEF, -ENOEXEC);
|
||||
Drv.EntryPoint = ELFHeader.e_entry;
|
||||
|
||||
size_t segSize = 0;
|
||||
Elf_Phdr phdr{};
|
||||
for (Elf_Half i = 0; i < ELFHeader.e_phnum; i++)
|
||||
{
|
||||
File->Read(&phdr, sizeof(Elf_Phdr), ELFHeader.e_phoff + (i * sizeof(Elf_Phdr)));
|
||||
if (phdr.p_type == PT_LOAD || phdr.p_type == PT_DYNAMIC)
|
||||
{
|
||||
if (segSize < phdr.p_vaddr + phdr.p_memsz)
|
||||
segSize = phdr.p_vaddr + phdr.p_memsz;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (phdr.p_type == PT_INTERP)
|
||||
{
|
||||
char interp[17];
|
||||
File->Read(interp, sizeof(interp), phdr.p_offset);
|
||||
if (strncmp(interp, "/boot/fennix.elf", sizeof(interp)) != 0)
|
||||
{
|
||||
error("Interpreter is not /boot/fennix.elf");
|
||||
return -ENOEXEC;
|
||||
}
|
||||
}
|
||||
}
|
||||
debug("segSize: %ld", segSize);
|
||||
|
||||
Drv.BaseAddress = (uintptr_t)Drv.vma->RequestPages(TO_PAGES(segSize) + 1);
|
||||
Drv.EntryPoint += Drv.BaseAddress;
|
||||
debug("Driver %s has entry point %#lx and base %#lx",
|
||||
File->Name.c_str(), Drv.EntryPoint, Drv.BaseAddress);
|
||||
|
||||
Elf64_Shdr sht_strtab{};
|
||||
Elf64_Shdr sht_symtab{};
|
||||
Elf_Shdr shstrtab{};
|
||||
Elf_Shdr shdr{};
|
||||
__DriverInfo driverInfo{};
|
||||
File->Read(&shstrtab, sizeof(Elf_Shdr), ELFHeader.e_shoff + (ELFHeader.e_shstrndx * ELFHeader.e_shentsize));
|
||||
for (Elf_Half i = 0; i < ELFHeader.e_shnum; i++)
|
||||
{
|
||||
if (i == ELFHeader.e_shstrndx)
|
||||
continue;
|
||||
|
||||
File->Read(&shdr, ELFHeader.e_shentsize, ELFHeader.e_shoff + (i * ELFHeader.e_shentsize));
|
||||
|
||||
switch (shdr.sh_type)
|
||||
{
|
||||
case SHT_PROGBITS:
|
||||
break;
|
||||
case SHT_SYMTAB:
|
||||
sht_symtab = shdr;
|
||||
continue;
|
||||
case SHT_STRTAB:
|
||||
sht_strtab = shdr;
|
||||
continue;
|
||||
case SHT_NULL:
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
char symName[16];
|
||||
File->Read(symName, sizeof(symName), shstrtab.sh_offset + shdr.sh_name);
|
||||
if (strcmp(symName, ".driver.info") != 0)
|
||||
continue;
|
||||
|
||||
File->Read(&driverInfo, sizeof(__DriverInfo), shdr.sh_offset);
|
||||
|
||||
/* Perform relocations */
|
||||
driverInfo.Name = (const char *)(Drv.BaseAddress + (uintptr_t)driverInfo.Name);
|
||||
driverInfo.Description = (const char *)(Drv.BaseAddress + (uintptr_t)driverInfo.Description);
|
||||
driverInfo.Author = (const char *)(Drv.BaseAddress + (uintptr_t)driverInfo.Author);
|
||||
driverInfo.License = (const char *)(Drv.BaseAddress + (uintptr_t)driverInfo.License);
|
||||
}
|
||||
|
||||
for (size_t h = 0; h < (sht_symtab.sh_size / sizeof(Elf64_Sym)); h++)
|
||||
{
|
||||
Elf64_Sym symEntry{};
|
||||
uintptr_t symOffset = sht_symtab.sh_offset + (h * sizeof(Elf64_Sym));
|
||||
File->Read(&symEntry, sizeof(Elf64_Sym), symOffset);
|
||||
|
||||
if (symEntry.st_name == 0)
|
||||
continue;
|
||||
|
||||
char symName[16];
|
||||
File->Read(symName, sizeof(symName), sht_strtab.sh_offset + symEntry.st_name);
|
||||
|
||||
switch (symEntry.st_shndx)
|
||||
{
|
||||
case SHN_UNDEF:
|
||||
case SHN_ABS:
|
||||
case SHN_LOPROC /* , SHN_LORESERVE and SHN_BEFORE */:
|
||||
case SHN_AFTER:
|
||||
case SHN_HIPROC:
|
||||
case SHN_COMMON:
|
||||
case SHN_HIRESERVE:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
debug("shndx: %d", symEntry.st_shndx);
|
||||
if (strcmp(symName, "DriverEntry") == 0)
|
||||
Drv.Entry = (int (*)())(Drv.BaseAddress + symEntry.st_value);
|
||||
else if (strcmp(symName, "DriverFinal") == 0)
|
||||
Drv.Final = (int (*)())(Drv.BaseAddress + symEntry.st_value);
|
||||
else if (strcmp(symName, "DriverPanic") == 0)
|
||||
Drv.Panic = (int (*)())(Drv.BaseAddress + symEntry.st_value);
|
||||
else if (strcmp(symName, "DriverProbe") == 0)
|
||||
Drv.Probe = (int (*)())(Drv.BaseAddress + symEntry.st_value);
|
||||
|
||||
debug("Found %s at %#lx", symName, symEntry.st_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Elf_Half i = 0; i < ELFHeader.e_phnum; i++)
|
||||
{
|
||||
File->Read(&phdr, sizeof(Elf_Phdr), ELFHeader.e_phoff + (i * sizeof(Elf_Phdr)));
|
||||
|
||||
switch (phdr.p_type)
|
||||
{
|
||||
case PT_LOAD:
|
||||
case PT_DYNAMIC:
|
||||
{
|
||||
if (phdr.p_memsz == 0)
|
||||
continue;
|
||||
|
||||
uintptr_t dest = Drv.BaseAddress + phdr.p_vaddr;
|
||||
debug("Copying PHDR %#lx to %#lx-%#lx (%ld file bytes, %ld mem bytes)",
|
||||
phdr.p_type, dest, dest + phdr.p_memsz,
|
||||
phdr.p_filesz, phdr.p_memsz);
|
||||
|
||||
if (phdr.p_filesz > 0)
|
||||
File->Read(dest, phdr.p_filesz, phdr.p_offset);
|
||||
|
||||
if (phdr.p_memsz - phdr.p_filesz > 0)
|
||||
{
|
||||
void *zero = (void *)(dest + phdr.p_filesz);
|
||||
memset(zero, 0, phdr.p_memsz - phdr.p_filesz);
|
||||
}
|
||||
|
||||
if (phdr.p_type != PT_DYNAMIC)
|
||||
break;
|
||||
|
||||
Elf64_Dyn *dyn = (Elf64_Dyn *)(Drv.BaseAddress + phdr.p_vaddr);
|
||||
Elf64_Dyn *relaSize = nullptr;
|
||||
Elf64_Dyn *pltrelSize = nullptr;
|
||||
|
||||
while (dyn->d_tag != DT_NULL)
|
||||
{
|
||||
switch (dyn->d_tag)
|
||||
{
|
||||
case DT_PLTRELSZ:
|
||||
{
|
||||
pltrelSize = dyn;
|
||||
break;
|
||||
}
|
||||
case DT_PLTGOT:
|
||||
{
|
||||
Elf_Addr *got = (Elf_Addr *)(Drv.BaseAddress + dyn->d_un.d_ptr);
|
||||
got[1] = 0;
|
||||
got[2] = 0;
|
||||
break;
|
||||
}
|
||||
case DT_RELASZ:
|
||||
{
|
||||
relaSize = dyn;
|
||||
break;
|
||||
}
|
||||
case DT_PLTREL:
|
||||
{
|
||||
AssertReturnError(dyn->d_un.d_val == DT_RELA, -ENOEXEC);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dyn++;
|
||||
}
|
||||
|
||||
dyn = (Elf64_Dyn *)(Drv.BaseAddress + phdr.p_vaddr);
|
||||
while (dyn->d_tag != DT_NULL)
|
||||
{
|
||||
switch (dyn->d_tag)
|
||||
{
|
||||
case DT_RELA: /* .rela.dyn */
|
||||
{
|
||||
AssertReturnError(relaSize != nullptr, -ENOEXEC);
|
||||
|
||||
Elf64_Rela *rela = (Elf64_Rela *)(Drv.BaseAddress + dyn->d_un.d_ptr);
|
||||
for (size_t i = 0; i < (relaSize->d_un.d_val / sizeof(Elf64_Rela)); i++)
|
||||
{
|
||||
Elf64_Rela *r = &rela[i];
|
||||
uintptr_t *reloc = (uintptr_t *)(Drv.BaseAddress + r->r_offset);
|
||||
uintptr_t relocTarget = 0;
|
||||
|
||||
switch (ELF64_R_TYPE(r->r_info))
|
||||
{
|
||||
case R_X86_64_GLOB_DAT:
|
||||
case R_X86_64_JUMP_SLOT:
|
||||
{
|
||||
relocTarget = Drv.BaseAddress;
|
||||
break;
|
||||
}
|
||||
case R_X86_64_RELATIVE:
|
||||
case R_X86_64_64:
|
||||
{
|
||||
relocTarget = Drv.BaseAddress + r->r_addend;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
fixme("Unhandled relocation type: %#lx",
|
||||
ELF64_R_TYPE(r->r_info));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*reloc = relocTarget;
|
||||
|
||||
debug("Relocated %#lx to %#lx",
|
||||
r->r_offset, *reloc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DT_JMPREL: /* .rela.plt */
|
||||
{
|
||||
AssertReturnError(pltrelSize != nullptr, -ENOEXEC);
|
||||
|
||||
std::vector<Elf64_Dyn> symtab = Execute::ELFGetDynamicTag_x86_64(File, DT_SYMTAB);
|
||||
Elf64_Sym *symbols = (Elf64_Sym *)((uintptr_t)Drv.BaseAddress + symtab[0].d_un.d_ptr);
|
||||
|
||||
std::vector<Elf64_Dyn> StrTab = Execute::ELFGetDynamicTag_x86_64(File, DT_STRTAB);
|
||||
char *DynStr = (char *)((uintptr_t)Drv.BaseAddress + StrTab[0].d_un.d_ptr);
|
||||
|
||||
Elf64_Rela *rela = (Elf64_Rela *)(Drv.BaseAddress + dyn->d_un.d_ptr);
|
||||
for (size_t i = 0; i < (pltrelSize->d_un.d_val / sizeof(Elf64_Rela)); i++)
|
||||
{
|
||||
Elf64_Rela *r = &rela[i];
|
||||
uintptr_t *reloc = (uintptr_t *)(Drv.BaseAddress + r->r_offset);
|
||||
|
||||
switch (ELF64_R_TYPE(r->r_info))
|
||||
{
|
||||
case R_X86_64_JUMP_SLOT:
|
||||
{
|
||||
Elf64_Xword symIndex = ELF64_R_SYM(r->r_info);
|
||||
Elf64_Sym *sym = symbols + symIndex;
|
||||
|
||||
const char *symName = DynStr + sym->st_name;
|
||||
debug("Resolving symbol %s", symName);
|
||||
|
||||
*reloc = (uintptr_t)GetSymbolByName(symName, driverInfo.Version.APIVersion);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
fixme("Unhandled relocation type: %#lx",
|
||||
ELF64_R_TYPE(r->r_info));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DT_PLTGOT:
|
||||
case DT_PLTRELSZ:
|
||||
case DT_RELASZ:
|
||||
case DT_PLTREL:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
fixme("Unhandled dynamic tag: %#lx", dyn->d_tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
dyn++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PT_PHDR:
|
||||
case PT_INTERP:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
fixme("Unhandled program header type: %#lx", phdr.p_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AssertReturnError(driverInfo.Name != nullptr, -EFAULT);
|
||||
strncpy(Drv.Name, driverInfo.Name, sizeof(Drv.Name));
|
||||
strncpy(Drv.Description, driverInfo.Description, sizeof(Drv.Description));
|
||||
strncpy(Drv.Author, driverInfo.Author, sizeof(Drv.Author));
|
||||
Drv.Version.Major = driverInfo.Version.Major;
|
||||
Drv.Version.Minor = driverInfo.Version.Minor;
|
||||
Drv.Version.Patch = driverInfo.Version.Patch;
|
||||
strncpy(Drv.License, driverInfo.License, sizeof(Drv.License));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Manager::Manager() { this->InitializeDaemonFS(); }
|
||||
|
||||
Manager::~Manager()
|
||||
{
|
||||
debug("Unloading drivers");
|
||||
UnloadAllDrivers();
|
||||
}
|
||||
}
|
368
Kernel/core/driver/scancode.cpp
Normal file
368
Kernel/core/driver/scancode.cpp
Normal file
@ -0,0 +1,368 @@
|
||||
/*
|
||||
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 <interface/driver.h>
|
||||
#include <driver.hpp>
|
||||
|
||||
static char ScanCodeConversionTableLower[] = {
|
||||
[KEY_1] = '1',
|
||||
[KEY_2] = '2',
|
||||
[KEY_3] = '3',
|
||||
[KEY_4] = '4',
|
||||
[KEY_5] = '5',
|
||||
[KEY_6] = '6',
|
||||
[KEY_7] = '7',
|
||||
[KEY_8] = '8',
|
||||
[KEY_9] = '9',
|
||||
[KEY_0] = '0',
|
||||
|
||||
[KEY_Q] = 'q',
|
||||
[KEY_W] = 'w',
|
||||
[KEY_E] = 'e',
|
||||
[KEY_R] = 'r',
|
||||
[KEY_T] = 't',
|
||||
[KEY_Y] = 'y',
|
||||
[KEY_U] = 'u',
|
||||
[KEY_I] = 'i',
|
||||
[KEY_O] = 'o',
|
||||
[KEY_P] = 'p',
|
||||
[KEY_A] = 'a',
|
||||
[KEY_S] = 's',
|
||||
[KEY_D] = 'd',
|
||||
[KEY_F] = 'f',
|
||||
[KEY_G] = 'g',
|
||||
[KEY_H] = 'h',
|
||||
[KEY_J] = 'j',
|
||||
[KEY_K] = 'k',
|
||||
[KEY_L] = 'l',
|
||||
[KEY_Z] = 'z',
|
||||
[KEY_X] = 'x',
|
||||
[KEY_C] = 'c',
|
||||
[KEY_V] = 'v',
|
||||
[KEY_B] = 'b',
|
||||
[KEY_N] = 'n',
|
||||
[KEY_M] = 'm',
|
||||
|
||||
[KEY_F1] = 0x00,
|
||||
[KEY_F2] = 0x00,
|
||||
[KEY_F3] = 0x00,
|
||||
[KEY_F4] = 0x00,
|
||||
[KEY_F5] = 0x00,
|
||||
[KEY_F6] = 0x00,
|
||||
[KEY_F7] = 0x00,
|
||||
[KEY_F8] = 0x00,
|
||||
[KEY_F9] = 0x00,
|
||||
[KEY_F10] = 0x00,
|
||||
[KEY_F11] = 0x00,
|
||||
[KEY_F12] = 0x00,
|
||||
|
||||
[KEYPAD_7] = '7',
|
||||
[KEYPAD_8] = '8',
|
||||
[KEYPAD_9] = '9',
|
||||
[KEYPAD_MINUS] = '-',
|
||||
[KEYPAD_4] = '4',
|
||||
[KEYPAD_5] = '5',
|
||||
[KEYPAD_6] = '6',
|
||||
[KEYPAD_PLUS] = '+',
|
||||
[KEYPAD_1] = '1',
|
||||
[KEYPAD_2] = '2',
|
||||
[KEYPAD_3] = '3',
|
||||
[KEYPAD_0] = '0',
|
||||
[KEYPAD_PERIOD] = '.',
|
||||
[KEYPAD_RETURN] = '\n',
|
||||
[KEYPAD_ASTERISK] = '*',
|
||||
[KEYPAD_SLASH] = '/',
|
||||
|
||||
[KEY_LEFT_CTRL] = 0x00,
|
||||
[KEY_RIGHT_CTRL] = 0x00,
|
||||
[KEY_LEFT_SHIFT] = 0x00,
|
||||
[KEY_RIGHT_SHIFT] = 0x00,
|
||||
[KEY_LEFT_ALT] = 0x00,
|
||||
[KEY_RIGHT_ALT] = 0x00,
|
||||
[KEY_ESCAPE] = '\e',
|
||||
[KEY_MINUS] = '-',
|
||||
[KEY_EQUAL] = '=',
|
||||
[KEY_BACKSPACE] = '\b',
|
||||
[KEY_TAB] = '\t',
|
||||
[KEY_LEFT_BRACKET] = '[',
|
||||
[KEY_RIGHT_BRACKET] = ']',
|
||||
[KEY_RETURN] = '\n',
|
||||
[KEY_SEMICOLON] = ';',
|
||||
[KEY_APOSTROPHE] = '\'',
|
||||
[KEY_BACK_TICK] = '`',
|
||||
[KEY_BACKSLASH] = '\\',
|
||||
[KEY_COMMA] = ',',
|
||||
[KEY_PERIOD] = '.',
|
||||
[KEY_SLASH] = '/',
|
||||
[KEY_SPACE] = ' ',
|
||||
[KEY_CAPS_LOCK] = 0x00,
|
||||
[KEY_NUM_LOCK] = 0x00,
|
||||
[KEY_SCROLL_LOCK] = 0x00,
|
||||
[KEY_PRINT_SCREEN] = 0x00,
|
||||
|
||||
[KEY_HOME] = 0x00,
|
||||
[KEY_UP_ARROW] = 0x00,
|
||||
[KEY_LEFT_ARROW] = 0x00,
|
||||
[KEY_RIGHT_ARROW] = 0x00,
|
||||
[KEY_DOWN_ARROW] = 0x00,
|
||||
[KEY_PAGE_UP] = 0x00,
|
||||
[KEY_PAGE_DOWN] = 0x00,
|
||||
[KEY_END] = 0x00,
|
||||
[KEY_INSERT] = 0x00,
|
||||
[KEY_DELETE] = 0x00,
|
||||
[KEY_LEFT_GUI] = 0x00,
|
||||
[KEY_RIGHT_GUI] = 0x00,
|
||||
[KEY_APPS] = 0x00,
|
||||
|
||||
[KEY_MULTIMEDIA_PREV_TRACK] = 0x00,
|
||||
[KEY_MULTIMEDIA_NEXT_TRACK] = 0x00,
|
||||
[KEY_MULTIMEDIA_MUTE] = 0x00,
|
||||
[KEY_MULTIMEDIA_CALCULATOR] = 0x00,
|
||||
[KEY_MULTIMEDIA_PLAY] = 0x00,
|
||||
[KEY_MULTIMEDIA_STOP] = 0x00,
|
||||
[KEY_MULTIMEDIA_VOL_DOWN] = 0x00,
|
||||
[KEY_MULTIMEDIA_VOL_UP] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_HOME] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_SEARCH] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_FAVORITES] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_REFRESH] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_STOP] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_FORWARD] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_BACK] = 0x00,
|
||||
[KEY_MULTIMEDIA_MY_COMPUTER] = 0x00,
|
||||
[KEY_MULTIMEDIA_EMAIL] = 0x00,
|
||||
[KEY_MULTIMEDIA_MEDIA_SELECT] = 0x00,
|
||||
|
||||
[KEY_ACPI_POWER] = 0x00,
|
||||
[KEY_ACPI_SLEEP] = 0x00,
|
||||
[KEY_ACPI_WAKE] = 0x00};
|
||||
|
||||
static char ScanCodeConversionTableUpper[] = {
|
||||
[KEY_1] = '!',
|
||||
[KEY_2] = '@',
|
||||
[KEY_3] = '#',
|
||||
[KEY_4] = '$',
|
||||
[KEY_5] = '%',
|
||||
[KEY_6] = '^',
|
||||
[KEY_7] = '&',
|
||||
[KEY_8] = '*',
|
||||
[KEY_9] = '(',
|
||||
[KEY_0] = ')',
|
||||
|
||||
[KEY_Q] = 'Q',
|
||||
[KEY_W] = 'W',
|
||||
[KEY_E] = 'E',
|
||||
[KEY_R] = 'R',
|
||||
[KEY_T] = 'T',
|
||||
[KEY_Y] = 'Y',
|
||||
[KEY_U] = 'U',
|
||||
[KEY_I] = 'I',
|
||||
[KEY_O] = 'O',
|
||||
[KEY_P] = 'P',
|
||||
[KEY_A] = 'A',
|
||||
[KEY_S] = 'S',
|
||||
[KEY_D] = 'D',
|
||||
[KEY_F] = 'F',
|
||||
[KEY_G] = 'G',
|
||||
[KEY_H] = 'H',
|
||||
[KEY_J] = 'J',
|
||||
[KEY_K] = 'K',
|
||||
[KEY_L] = 'L',
|
||||
[KEY_Z] = 'Z',
|
||||
[KEY_X] = 'X',
|
||||
[KEY_C] = 'C',
|
||||
[KEY_V] = 'V',
|
||||
[KEY_B] = 'B',
|
||||
[KEY_N] = 'N',
|
||||
[KEY_M] = 'M',
|
||||
|
||||
[KEY_F1] = 0x00,
|
||||
[KEY_F2] = 0x00,
|
||||
[KEY_F3] = 0x00,
|
||||
[KEY_F4] = 0x00,
|
||||
[KEY_F5] = 0x00,
|
||||
[KEY_F6] = 0x00,
|
||||
[KEY_F7] = 0x00,
|
||||
[KEY_F8] = 0x00,
|
||||
[KEY_F9] = 0x00,
|
||||
[KEY_F10] = 0x00,
|
||||
[KEY_F11] = 0x00,
|
||||
[KEY_F12] = 0x00,
|
||||
|
||||
[KEYPAD_7] = '7',
|
||||
[KEYPAD_8] = '8',
|
||||
[KEYPAD_9] = '9',
|
||||
[KEYPAD_MINUS] = '-',
|
||||
[KEYPAD_4] = '4',
|
||||
[KEYPAD_5] = '5',
|
||||
[KEYPAD_6] = '6',
|
||||
[KEYPAD_PLUS] = '+',
|
||||
[KEYPAD_1] = '1',
|
||||
[KEYPAD_2] = '2',
|
||||
[KEYPAD_3] = '3',
|
||||
[KEYPAD_0] = '0',
|
||||
[KEYPAD_PERIOD] = '.',
|
||||
[KEYPAD_RETURN] = '\n',
|
||||
[KEYPAD_ASTERISK] = '*',
|
||||
[KEYPAD_SLASH] = '/',
|
||||
|
||||
[KEY_LEFT_CTRL] = 0x00,
|
||||
[KEY_RIGHT_CTRL] = 0x00,
|
||||
[KEY_LEFT_SHIFT] = 0x00,
|
||||
[KEY_RIGHT_SHIFT] = 0x00,
|
||||
[KEY_LEFT_ALT] = 0x00,
|
||||
[KEY_RIGHT_ALT] = 0x00,
|
||||
[KEY_ESCAPE] = '\e',
|
||||
[KEY_MINUS] = '_',
|
||||
[KEY_EQUAL] = '+',
|
||||
[KEY_BACKSPACE] = '\b',
|
||||
[KEY_TAB] = '\t',
|
||||
[KEY_LEFT_BRACKET] = '{',
|
||||
[KEY_RIGHT_BRACKET] = '}',
|
||||
[KEY_RETURN] = '\n',
|
||||
[KEY_SEMICOLON] = ':',
|
||||
[KEY_APOSTROPHE] = '\"',
|
||||
[KEY_BACK_TICK] = '~',
|
||||
[KEY_BACKSLASH] = '|',
|
||||
[KEY_COMMA] = '<',
|
||||
[KEY_PERIOD] = '>',
|
||||
[KEY_SLASH] = '?',
|
||||
[KEY_SPACE] = ' ',
|
||||
[KEY_CAPS_LOCK] = 0x00,
|
||||
[KEY_NUM_LOCK] = 0x00,
|
||||
[KEY_SCROLL_LOCK] = 0x00,
|
||||
[KEY_PRINT_SCREEN] = 0x00,
|
||||
|
||||
[KEY_HOME] = 0x00,
|
||||
[KEY_UP_ARROW] = 0x00,
|
||||
[KEY_LEFT_ARROW] = 0x00,
|
||||
[KEY_RIGHT_ARROW] = 0x00,
|
||||
[KEY_DOWN_ARROW] = 0x00,
|
||||
[KEY_PAGE_UP] = 0x00,
|
||||
[KEY_PAGE_DOWN] = 0x00,
|
||||
[KEY_END] = 0x00,
|
||||
[KEY_INSERT] = 0x00,
|
||||
[KEY_DELETE] = 0x00,
|
||||
[KEY_LEFT_GUI] = 0x00,
|
||||
[KEY_RIGHT_GUI] = 0x00,
|
||||
[KEY_APPS] = 0x00,
|
||||
|
||||
[KEY_MULTIMEDIA_PREV_TRACK] = 0x00,
|
||||
[KEY_MULTIMEDIA_NEXT_TRACK] = 0x00,
|
||||
[KEY_MULTIMEDIA_MUTE] = 0x00,
|
||||
[KEY_MULTIMEDIA_CALCULATOR] = 0x00,
|
||||
[KEY_MULTIMEDIA_PLAY] = 0x00,
|
||||
[KEY_MULTIMEDIA_STOP] = 0x00,
|
||||
[KEY_MULTIMEDIA_VOL_DOWN] = 0x00,
|
||||
[KEY_MULTIMEDIA_VOL_UP] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_HOME] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_SEARCH] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_FAVORITES] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_REFRESH] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_STOP] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_FORWARD] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_BACK] = 0x00,
|
||||
[KEY_MULTIMEDIA_MY_COMPUTER] = 0x00,
|
||||
[KEY_MULTIMEDIA_EMAIL] = 0x00,
|
||||
[KEY_MULTIMEDIA_MEDIA_SELECT] = 0x00,
|
||||
|
||||
[KEY_ACPI_POWER] = 0x00,
|
||||
[KEY_ACPI_SLEEP] = 0x00,
|
||||
[KEY_ACPI_WAKE] = 0x00};
|
||||
|
||||
#ifdef DEBUG
|
||||
static const char *ScanCodeDebugNames[] = {
|
||||
"KEY_1", "KEY_2", "KEY_3", "KEY_4", "KEY_5", "KEY_6", "KEY_7", "KEY_8",
|
||||
"KEY_9", "KEY_0", "KEY_Q", "KEY_W", "KEY_E", "KEY_R", "KEY_T", "KEY_Y",
|
||||
"KEY_U", "KEY_I", "KEY_O", "KEY_P", "KEY_A", "KEY_S", "KEY_D", "KEY_F",
|
||||
"KEY_G", "KEY_H", "KEY_J", "KEY_K", "KEY_L", "KEY_Z", "KEY_X", "KEY_C",
|
||||
"KEY_V", "KEY_B", "KEY_N", "KEY_M", "KEY_F1", "KEY_F2", "KEY_F3", "KEY_F4",
|
||||
"KEY_F5", "KEY_F6", "KEY_F7", "KEY_F8", "KEY_F9", "KEY_F10", "KEY_F11",
|
||||
"KEY_F12", "KEYPAD_7", "KEYPAD_8", "KEYPAD_9", "KEYPAD_MINUS", "KEYPAD_4",
|
||||
"KEYPAD_5", "KEYPAD_6", "KEYPAD_PLUS", "KEYPAD_1", "KEYPAD_2", "KEYPAD_3",
|
||||
"KEYPAD_0", "KEYPAD_PERIOD", "KEYPAD_RETURN", "KEYPAD_ASTERISK", "KEYPAD_SLASH",
|
||||
"KEY_LEFT_CTRL", "KEY_RIGHT_CTRL", "KEY_LEFT_SHIFT", "KEY_RIGHT_SHIFT",
|
||||
"KEY_LEFT_ALT", "KEY_RIGHT_ALT", "KEY_ESCAPE", "KEY_MINUS", "KEY_EQUAL",
|
||||
"KEY_BACKSPACE", "KEY_TAB", "KEY_LEFT_BRACKET", "KEY_RIGHT_BRACKET",
|
||||
"KEY_RETURN", "KEY_SEMICOLON", "KEY_APOSTROPHE", "KEY_BACK_TICK",
|
||||
"KEY_BACKSLASH", "KEY_COMMA", "KEY_PERIOD", "KEY_SLASH", "KEY_SPACE",
|
||||
"KEY_CAPS_LOCK", "KEY_NUM_LOCK", "KEY_SCROLL_LOCK", "KEY_PRINT_SCREEN",
|
||||
"KEY_HOME", "KEY_UP_ARROW", "KEY_LEFT_ARROW", "KEY_RIGHT_ARROW",
|
||||
"KEY_DOWN_ARROW", "KEY_PAGE_UP", "KEY_PAGE_DOWN", "KEY_END", "KEY_INSERT",
|
||||
"KEY_DELETE", "KEY_LEFT_GUI", "KEY_RIGHT_GUI", "KEY_APPS",
|
||||
"KEY_MULTIMEDIA_PREV_TRACK", "KEY_MULTIMEDIA_NEXT_TRACK", "KEY_MULTIMEDIA_MUTE",
|
||||
"KEY_MULTIMEDIA_CALCULATOR", "KEY_MULTIMEDIA_PLAY", "KEY_MULTIMEDIA_STOP",
|
||||
"KEY_MULTIMEDIA_VOL_DOWN", "KEY_MULTIMEDIA_VOL_UP", "KEY_MULTIMEDIA_WWW_HOME",
|
||||
"KEY_MULTIMEDIA_WWW_SEARCH", "KEY_MULTIMEDIA_WWW_FAVORITES",
|
||||
"KEY_MULTIMEDIA_WWW_REFRESH", "KEY_MULTIMEDIA_WWW_STOP",
|
||||
"KEY_MULTIMEDIA_WWW_FORWARD", "KEY_MULTIMEDIA_WWW_BACK",
|
||||
"KEY_MULTIMEDIA_MY_COMPUTER", "KEY_MULTIMEDIA_EMAIL",
|
||||
"KEY_MULTIMEDIA_MEDIA_SELECT", "KEY_ACPI_POWER", "KEY_ACPI_SLEEP", "KEY_ACPI_WAKE"};
|
||||
#endif
|
||||
|
||||
namespace Driver
|
||||
{
|
||||
char GetScanCode(uint8_t ScanCode, bool Upper)
|
||||
{
|
||||
ScanCode &= 0x7F; /* Remove KEY_PRESSED bit */
|
||||
if (ScanCode >= sizeof(ScanCodeConversionTableLower))
|
||||
{
|
||||
warn("Unknown scancode %x", ScanCode);
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
// debug("Scancode %x (%s)", ScanCode, ScanCodeDebugNames[ScanCode]);
|
||||
return Upper
|
||||
? ScanCodeConversionTableUpper[ScanCode]
|
||||
: ScanCodeConversionTableLower[ScanCode];
|
||||
}
|
||||
|
||||
bool IsValidChar(uint8_t ScanCode)
|
||||
{
|
||||
ScanCode &= 0x7F; /* Remove KEY_PRESSED bit */
|
||||
if (ScanCode >= sizeof(ScanCodeConversionTableLower))
|
||||
return false;
|
||||
|
||||
if (ScanCode > KEY_M)
|
||||
{
|
||||
if (ScanCode < KEYPAD_7)
|
||||
return false; /* F1 - F12 */
|
||||
|
||||
switch (ScanCode)
|
||||
{
|
||||
case KEY_MINUS:
|
||||
case KEY_EQUAL:
|
||||
case KEY_LEFT_BRACKET:
|
||||
case KEY_RIGHT_BRACKET:
|
||||
case KEY_RETURN:
|
||||
case KEY_SEMICOLON:
|
||||
case KEY_APOSTROPHE:
|
||||
case KEY_BACK_TICK:
|
||||
case KEY_BACKSLASH:
|
||||
case KEY_COMMA:
|
||||
case KEY_PERIOD:
|
||||
case KEY_SLASH:
|
||||
case KEY_SPACE:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user