diff --git a/Fennix Kernel.code-workspace b/Fennix Kernel.code-workspace
index 3298aaaa..db2b6dd4 100644
--- a/Fennix Kernel.code-workspace
+++ b/Fennix Kernel.code-workspace
@@ -33,7 +33,8 @@
"vscode",
"kernel",
"kernel/pci",
- "kernel/driver"
+ "kernel/driver",
+ "kernel/drivers"
]
}
}
diff --git a/Kernel/drivers/audio/ac97/ac97.cpp b/Kernel/drivers/audio/ac97/ac97.cpp
new file mode 100644
index 00000000..8ceec9cf
--- /dev/null
+++ b/Kernel/drivers/audio/ac97/ac97.cpp
@@ -0,0 +1,820 @@
+/*
+ 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 .
+*/
+
+#include
+#include
+#include
+#include
+
+extern Driver::Manager *DriverManager;
+extern PCI::Manager *PCIManager;
+namespace Driver::AC97
+{
+ dev_t DriverID;
+
+#define DescriptorListLength 0x20
+
+ enum AudioVolumeValues
+ {
+ AV_Maximum = 0x0,
+ AV_Minimum = 0x3F,
+ };
+
+ enum AudioEncodingValues
+ {
+ AE_PCMs8,
+ AE_PCMu8,
+
+ AE_PCMs16le,
+ AE_PCMs20le,
+ AE_PCMs24le,
+ AE_PCMs32le,
+
+ AE_PCMu16le,
+ AE_PCMu20le,
+ AE_PCMu24le,
+ AE_PCMu32le,
+
+ AE_PCMs16be,
+ AE_PCMs20be,
+ AE_PCMs24be,
+ AE_PCMs32be,
+
+ AE_PCMu16be,
+ AE_PCMu20be,
+ AE_PCMu24be,
+ AE_PCMu32be,
+ };
+
+ enum NativeAudioMixerRegisters
+ {
+ /**
+ * @brief Reset Register
+ * @note Length: word
+ */
+ NAM_Reset = 0x00,
+
+ /**
+ * @brief Master Volume Register
+ * @note Length: word
+ */
+ NAM_MasterVolume = 0x02,
+
+ /**
+ * @brief Microphone Volume Register
+ * @note Length: word
+ */
+ NAM_MicrophoneVolume = 0x0E,
+
+ /**
+ * @brief PCM Out Volume Register
+ * @note Length: word
+ */
+ NAM_PCMOutVolume = 0x18,
+
+ /**
+ * @brief Select Record Input Register
+ * @note Length: word
+ */
+ NAM_SelectRecordInput = 0x1A,
+
+ /**
+ * @brief Record Gain Register
+ * @note Length: word
+ */
+ NAM_RecordGain = 0x1C,
+
+ /**
+ * @brief Record Gain Microphone Register
+ * @note Length: word
+ */
+ NAM_RecordGainMicrophone = 0x1E,
+ };
+
+ enum NativeAudioBusMasterRegisters
+ {
+ /**
+ * @brief Register box for PCM IN
+ * @note Length: below
+ */
+ NABM_PCMInBox = 0x00,
+
+ /**
+ * @brief Register box for PCM OUT
+ * @note Length: below
+ */
+ NABM_PCMOutBox = 0x10,
+
+ /**
+ * @brief Register box for Microphone
+ * @note Length: below
+ */
+ NABM_MicrophoneBox = 0x20,
+
+ /**
+ * @brief Global Control Register
+ * @note Length: dword
+ */
+ NABM_GlobalControl = 0x2C, /* 0x30 */
+
+ /**
+ * @brief Global Status Register
+ * @note Length: dword
+ */
+ NABM_GlobalStatus = 0x30, /* 0x34 */
+ };
+
+ enum NativeAudioBusMasterBoxOffsets
+ {
+ /**
+ * @brief Physical Address of Buffer Descriptor List
+ * @note Length: dword
+ */
+ NABMBOFF_BufferDescriptorList = 0x00,
+
+ /**
+ * @brief Number of Actual Processed Buffer Descriptor Entry
+ * @note Length: byte
+ */
+ NABMBOFF_BufferDescriptorEntry = 0x04,
+
+ /**
+ * @brief Number of all Descriptor Entries
+ * @note Length: byte
+ */
+ NABMBOFF_DescriptorEntries = 0x05,
+
+ /**
+ * @brief Status of transferring Data
+ * @note Length: word
+ */
+ NABMBOFF_Status = 0x06,
+
+ /**
+ * @brief Number of transferred Samples in Actual Processed Entry
+ * @note Length: word
+ */
+ NABMBOFF_TransferredSamples = 0x08,
+
+ /**
+ * @brief Number of next processed Buffer Entry
+ * @note Length: byte
+ */
+ NABMBOFF_NextProcessedBufferEntry = 0x0A,
+
+ /**
+ * @brief Transfer Control
+ * @note Length: byte
+ */
+ NABMBOFF_TransferControl = 0x0B,
+ };
+
+ enum OutputPulseCodeModulationRegisters
+ {
+ /**
+ * @brief Physical Address of Buffer Descriptor List
+ * @note Length: dword
+ */
+ PCMOUT_BufferDescriptorList = (int)NABM_PCMOutBox + (int)NABMBOFF_BufferDescriptorList,
+
+ /**
+ * @brief Number of Actual Processed Buffer Descriptor Entry
+ * @note Length: byte
+ */
+ PCMOUT_BufferDescriptorEntry = (int)NABM_PCMOutBox + (int)NABMBOFF_BufferDescriptorEntry,
+
+ /**
+ * @brief Number of all Descriptor Entries
+ * @note Length: byte
+ */
+ PCMOUT_DescriptorEntries = (int)NABM_PCMOutBox + (int)NABMBOFF_DescriptorEntries,
+
+ /**
+ * @brief Status of transferring Data
+ * @note Length: word
+ */
+ PCMOUT_Status = (int)NABM_PCMOutBox + (int)NABMBOFF_Status,
+
+ /**
+ * @brief Number of transferred Samples in Actual Processed Entry
+ * @note Length: word
+ */
+ PCMOUT_TransferredSamples = (int)NABM_PCMOutBox + (int)NABMBOFF_TransferredSamples,
+
+ /**
+ * @brief Number of next processed Buffer Entry
+ * @note Length: byte
+ */
+ PCMOUT_NextProcessedBufferEntry = (int)NABM_PCMOutBox + (int)NABMBOFF_NextProcessedBufferEntry,
+
+ /**
+ * @brief Transfer Control
+ * @note Length: byte
+ */
+ PCMOUT_TransferControl = (int)NABM_PCMOutBox + (int)NABMBOFF_TransferControl,
+ };
+
+ enum TransferControlRegisters
+ {
+ /**
+ * @brief DMA controller control
+ *
+ * 0 = Pause transfer
+ * 1 = Transfer sound data
+ */
+ TC_DMAControllerControl = 0x01,
+
+ /**
+ * @brief Reset
+ *
+ * 0 = Remove reset condition
+ * 1 = Reset this NABM register box, this bit is cleared by card when is reset complete
+ */
+ TC_TransferReset = 0x02,
+
+ /**
+ * @brief Last Buffer Entry Interrupt enable
+ *
+ * 0 = Disable interrupt
+ * 1 = Enable interrupt
+ */
+ TC_LastBufferEntryInterruptEnable = 0x04,
+
+ /**
+ * @brief IOC Interrupt enable
+ *
+ * 0 = Disable interrupt
+ * 1 = Enable interrupt
+ */
+ TC_IOCInterruptEnable = 0x08,
+
+ /**
+ * @brief Fifo ERROR Interrupt enable
+ *
+ * 0 = Disable interrupt
+ * 1 = Enable interrupt
+ */
+ TC_FifoERRORInterruptEnable = 0x10,
+ };
+
+ enum GlobalControlRegisters
+ {
+ /**
+ * @brief Global Interrupt Enable
+ *
+ * 0 = Disable Interrupts
+ * 1 = Enable Interrupts
+ */
+ GC_GlobalInterruptEnable = 0x01,
+
+ /**
+ * @brief Cold reset
+ *
+ * 0 = Device is in reset and can not be used
+ * 1 = Resume to operational state
+ */
+ GC_ColdReset = 0x02,
+
+ /**
+ * @brief Warm reset
+ */
+ GC_WarmReset = 0x04,
+
+ /**
+ * @brief Shut down
+ *
+ * 0 = Device is powered
+ * 1 = Shut down
+ */
+ GC_ShutDown = 0x08,
+
+ /**
+ * @brief Channels for PCM Output
+ *
+ * 00 = 2 channels
+ * 01 = 4 channels
+ * 10 = 6 channels
+ * 11 = Reserved
+ */
+ GC_ChannelsForPCMOutput = 0x30,
+
+ /**
+ * @brief PCM Output mode
+ *
+ * 00 = 16 bit samples
+ * 01 = 20 bit samples
+ */
+ GC_PCMOutputMode = 0xC0,
+ };
+
+ struct BufferDescriptorList
+ {
+ /**
+ * @brief Physical Address to sound data in memory
+ * @note Length: dword
+ */
+ uint32_t Address;
+
+ /**
+ * @brief Number of samples in this buffer
+ * @note Length: word
+ */
+ uint16_t SampleCount;
+
+ /**
+ * @brief Flags
+ * @note Length: word
+ *
+ * Bit 15 = Interrupt fired when data from this entry is transferred
+ * Bit 14 = Last entry of buffer, stop playing
+ * Other bits = Reserved
+ */
+ uint16_t Flags;
+ } __attribute__((packed));
+
+ uint16_t MixerVolume(uint8_t Left, uint8_t Right, bool Mute)
+ {
+ return ((uint16_t)((Right & 0x3F) |
+ ((Left & 0x3F) << 0x8) |
+ (Mute & 1 << 0xF)));
+ }
+
+ class AC97Device
+ {
+ private:
+ PCI::PCIHeader0 *Header;
+ BufferDescriptorList *DescriptorList = nullptr;
+
+ uint16_t MixerAddress;
+ uint16_t BusMasterAddress;
+
+ AudioEncodingValues Encoding = AE_PCMs16le;
+ char Channels = 2;
+ uint8_t Volume = AV_Maximum;
+ bool Mute = false;
+ int SampleRate = 48000;
+ char SampleSize = 2;
+
+ public:
+ size_t write(uint8_t *Buffer, size_t Size)
+ {
+ if (Buffer == nullptr)
+ {
+ info("Invalid buffer.");
+ return -EINVAL;
+ }
+
+ if ((Size == 0) || (Size % (SampleSize * Channels)))
+ {
+ info("Invalid buffer length.");
+ return -EINVAL;
+ }
+
+ int TotalBDLToFill = (int)((Size + PAGE_SIZE - 1) >> 12);
+
+ while (Size > 0)
+ {
+ bool ActiveDMA = !(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl);
+
+ if (ActiveDMA)
+ {
+ int RemainingBDL = 0;
+
+ do
+ {
+ int CurrentBDL = inb(BusMasterAddress + PCMOUT_BufferDescriptorEntry);
+ int LastBDL = inb(BusMasterAddress + PCMOUT_DescriptorEntries);
+
+ RemainingBDL = LastBDL - CurrentBDL;
+ if (RemainingBDL < 0)
+ RemainingBDL += DescriptorListLength;
+
+ RemainingBDL += 1;
+
+ if (RemainingBDL >= DescriptorListLength - 1)
+ {
+ long SampleCount = DescriptorList[(CurrentBDL + 1) % DescriptorListLength].SampleCount / Channels;
+ if (SampleCount > 0)
+ v0::Sleep(DriverID, SampleCount * 1000 / SampleRate);
+ }
+
+ } while (RemainingBDL >= DescriptorListLength - 1 &&
+ !(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl));
+ }
+
+ uint8_t CurrentBDL = inb(BusMasterAddress + PCMOUT_BufferDescriptorEntry);
+ uint8_t LastBDL = inb(BusMasterAddress + PCMOUT_DescriptorEntries);
+ uint8_t NextBDL = LastBDL % DescriptorListLength;
+
+ ActiveDMA = !(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl);
+ if (ActiveDMA)
+ {
+ NextBDL = (uint8_t)((LastBDL + 1) % DescriptorListLength);
+ if (NextBDL == CurrentBDL)
+ continue;
+ }
+
+ do
+ {
+ size_t Wrote = (PAGE_SIZE > Size) ? size_t(Size)
+ : size_t(PAGE_SIZE);
+
+ if (Wrote == 0)
+ {
+ info("Wrote 0 bytes.");
+ break;
+ }
+
+ memcpy((void *)((uint64_t)DescriptorList[NextBDL].Address), Buffer, Wrote);
+ DescriptorList[NextBDL].Flags = 0;
+
+ Buffer += Wrote;
+ Size -= (unsigned int)Wrote;
+
+ DescriptorList[NextBDL].SampleCount = uint16_t(Wrote / SampleSize);
+ TotalBDLToFill--;
+ NextBDL = (uint8_t)((NextBDL + 1) % DescriptorListLength);
+ } while (TotalBDLToFill-- && NextBDL != CurrentBDL);
+
+ outb(BusMasterAddress + PCMOUT_DescriptorEntries, NextBDL - 1);
+
+ ActiveDMA = !(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl);
+ if (!ActiveDMA)
+ {
+ // Start DMA
+ outb(BusMasterAddress + PCMOUT_TransferControl,
+ inb(BusMasterAddress + PCMOUT_TransferControl) | TC_DMAControllerControl);
+ }
+ }
+ return Size;
+ }
+
+ int ioctl(AudioIoctl, void *)
+ {
+ // if (Data->AudioCallback.Adjust._Volume)
+ // {
+ // Volume = (uint8_t)(0x3F - (0x3F * Data->AudioCallback.Adjust.Volume / 100));
+ // outw(BAR.MixerAddress + NAM_MasterVolume, MixerVolume(Volume, Volume, Mute));
+ // // outw(BAR.MixerAddress + NAM_PCMOutVolume, MixerVolume(Volume, Volume, Mute));
+ // }
+ // else if (Data->AudioCallback.Adjust._Encoding)
+ // {
+ // fixme("Encoding changing not supported yet.");
+ // }
+ // else if (Data->AudioCallback.Adjust._SampleRate)
+ // {
+ // switch (Data->AudioCallback.Adjust.SampleRate)
+ // {
+ // case 0:
+ // {
+ // SampleRate = 8000;
+ // break;
+ // }
+ // case 1:
+ // {
+ // SampleRate = 11025;
+ // break;
+ // }
+ // case 2:
+ // {
+ // SampleRate = 16000;
+ // break;
+ // }
+ // case 3:
+ // {
+ // SampleRate = 22050;
+ // break;
+ // }
+ // case 4:
+ // {
+ // SampleRate = 32000;
+ // break;
+ // }
+ // case 5:
+ // {
+ // SampleRate = 44100;
+ // break;
+ // }
+ // case 6:
+ // {
+ // SampleRate = 48000;
+ // break;
+ // }
+ // case 7:
+ // {
+ // SampleRate = 88200;
+ // break;
+ // }
+ // case 8:
+ // {
+ // SampleRate = 96000;
+ // break;
+ // }
+ // default:
+ // {
+ // SampleRate = 16000;
+ // error("Invalid sample rate. Defaulting to 16000.");
+ // break;
+ // }
+ // }
+ // }
+ // else if (Data->AudioCallback.Adjust._Channels)
+ // {
+ // switch (Data->AudioCallback.Adjust.Channels)
+ // {
+ // case 0:
+ // {
+ // Channels = 1; // Mono
+ // break;
+ // }
+ // case 1:
+ // {
+ // Channels = 2; // Stereo
+ // break;
+ // }
+ // default:
+ // {
+ // Channels = 2;
+ // error("Invalid channel count. Defaulting to 2.");
+ // break;
+ // }
+ // }
+ // }
+ return 0;
+ }
+
+ void OnInterruptReceived(CPU::TrapFrame *)
+ {
+ uint16_t Status = inw(MixerAddress + PCMOUT_Status);
+ if (Status & TC_IOCInterruptEnable)
+ {
+ debug("IOC");
+ outw(MixerAddress + PCMOUT_Status, TC_IOCInterruptEnable);
+ uint16_t CurrentBDL = inb(BusMasterAddress + PCMOUT_BufferDescriptorEntry);
+ uint16_t LastBDL = (CurrentBDL + 2) & (DescriptorListLength - 1);
+ outb(BusMasterAddress + PCMOUT_DescriptorEntries, LastBDL);
+ info("FIXME: CurrentBDL: %d, LastBDL: %d", CurrentBDL, LastBDL);
+ }
+ else if (Status & TC_LastBufferEntryInterruptEnable)
+ {
+ debug("Last buffer entry");
+ // Stop DMA
+ uint8_t TransferControl = inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl));
+ TransferControl &= ~TC_DMAControllerControl;
+ outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl), TransferControl);
+
+ outw(MixerAddress + PCMOUT_Status, TC_LastBufferEntryInterruptEnable);
+ }
+ else if (Status & TC_FifoERRORInterruptEnable)
+ {
+ info("FIFO error");
+ outw(MixerAddress + PCMOUT_Status, TC_FifoERRORInterruptEnable);
+ }
+ else
+ {
+ debug("Unknown interrupt status %#x", Status);
+ outw(MixerAddress + PCMOUT_Status, 0xFFFF);
+ }
+ }
+
+ void Panic()
+ {
+ uint8_t TransferControl = inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl));
+ TransferControl &= ~(TC_LastBufferEntryInterruptEnable |
+ TC_IOCInterruptEnable |
+ TC_FifoERRORInterruptEnable);
+ outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl), TransferControl);
+
+ uint32_t GlobalControl = inl((uint16_t)(BusMasterAddress + NABM_GlobalControl));
+ GlobalControl &= ~GC_GlobalInterruptEnable;
+ GlobalControl |= GC_ShutDown;
+ outl((uint16_t)(BusMasterAddress + NABM_GlobalControl), GlobalControl);
+ }
+
+ AC97Device(PCI::PCIHeader0 *_Header)
+ : Header(_Header)
+ {
+ /* Native Audio Mixer Base Address */
+ uint32_t PCIBAR0 = Header->BAR0;
+
+ /* Native Audio Bus Master Base Address */
+ uint32_t PCIBAR1 = Header->BAR1;
+
+ // uint8_t Type = PCIBAR0 & 1;
+ MixerAddress = (uint16_t)(PCIBAR0 & (~3));
+ BusMasterAddress = PCIBAR1 & (~15);
+
+ uint16_t OutputPCMTransferControl = BusMasterAddress + PCMOUT_TransferControl;
+
+ /* DescriptorList address MUST be physical. */
+ DescriptorList = (BufferDescriptorList *)v0::AllocateMemory(DriverID, TO_PAGES(sizeof(BufferDescriptorList) * DescriptorListLength));
+ memset(DescriptorList, 0, sizeof(BufferDescriptorList) * DescriptorListLength);
+
+ uint16_t DLSampleCount = (uint16_t)(PAGE_SIZE / SampleSize);
+ for (int i = 0; i < DescriptorListLength; i++)
+ {
+ DescriptorList[i].Address = (uint32_t)(uintptr_t)v0::AllocateMemory(DriverID, TO_PAGES(sizeof(uint16_t *)));
+ DescriptorList[i].SampleCount = DLSampleCount;
+ DescriptorList[i].Flags = 0;
+ debug("DescriptorList[%d] = { Address: %#lx, SampleCount: %d, Flags: %#lx }",
+ i,
+ DescriptorList[i].Address,
+ DescriptorList[i].SampleCount,
+ DescriptorList[i].Flags);
+ }
+
+ outw(MixerAddress + NAM_MasterVolume, MixerVolume(Volume, Volume, Mute));
+ outw(MixerAddress + NAM_PCMOutVolume, MixerVolume(Volume, Volume, Mute));
+
+ Volume = 0x3F - (0x3F * /* VOL 50% */ 50 / 100);
+ outw(MixerAddress + NAM_MasterVolume, MixerVolume(Volume, Volume, Mute));
+
+ outb(OutputPCMTransferControl, inb(OutputPCMTransferControl) | TC_TransferReset);
+ while (inb(OutputPCMTransferControl) & TC_TransferReset)
+ ;
+
+ uint32_t GlobalControl = inl(BusMasterAddress + NABM_GlobalControl);
+ GlobalControl = (GlobalControl & ~((0x3U) << 0x16)); /* PCM 16-bit mode */
+ GlobalControl = (GlobalControl & ~((0x3U) << 20)); /* 2 channels */
+ GlobalControl |= GC_GlobalInterruptEnable;
+ GlobalControl &= ~GC_ShutDown;
+
+ outl(BusMasterAddress + PCMOUT_BufferDescriptorList,
+ (uint32_t)(uint64_t)DescriptorList);
+
+ outl(BusMasterAddress + NABM_GlobalControl, GlobalControl);
+
+ uint8_t TransferControl = inb(OutputPCMTransferControl);
+ TransferControl |= TC_IOCInterruptEnable |
+ TC_FifoERRORInterruptEnable;
+ outb(OutputPCMTransferControl, TransferControl);
+
+ // Stop DMA
+ outb(OutputPCMTransferControl, inb(OutputPCMTransferControl) & ~TC_DMAControllerControl);
+ }
+
+ ~AC97Device()
+ {
+ outw(MixerAddress + NAM_MasterVolume, MixerVolume(AV_Maximum, AV_Maximum, true));
+ outw(MixerAddress + NAM_PCMOutVolume, MixerVolume(AV_Maximum, AV_Maximum, true));
+
+ // Stop DMA
+ outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl),
+ inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl)) & ~TC_DMAControllerControl);
+
+ // Disable interrupts
+ uint8_t TransferControl = inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl));
+
+ TransferControl &= ~(TC_LastBufferEntryInterruptEnable |
+ TC_IOCInterruptEnable |
+ TC_FifoERRORInterruptEnable);
+ outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl), TransferControl);
+
+ // Disable global control
+ uint32_t GlobalControl = inl((uint16_t)(BusMasterAddress + NABM_GlobalControl));
+ GlobalControl &= ~GC_GlobalInterruptEnable;
+ GlobalControl |= GC_ShutDown;
+ outl((uint16_t)(BusMasterAddress + NABM_GlobalControl), GlobalControl);
+ }
+ };
+
+ std::unordered_map Drivers;
+
+ int Open(struct Inode *, int, mode_t)
+ {
+ return 0;
+ }
+
+ int Close(struct Inode *)
+ {
+ return 0;
+ }
+
+ ssize_t Read(struct Inode *, void *, size_t, off_t)
+ {
+ return 0;
+ }
+
+ ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t)
+ {
+ return Drivers[Node->GetMinor()]->write((uint8_t *)Buffer, Size);
+ }
+
+ int Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
+ {
+ return Drivers[Node->GetMinor()]->ioctl((AudioIoctl)Request, Argp);
+ }
+
+ const struct InodeOperations ops = {
+ .Lookup = nullptr,
+ .Create = nullptr,
+ .Remove = nullptr,
+ .Rename = nullptr,
+ .Read = Read,
+ .Write = Write,
+ .Truncate = nullptr,
+ .Open = Open,
+ .Close = Close,
+ .Ioctl = Ioctl,
+ .ReadDir = nullptr,
+ .MkDir = nullptr,
+ .RmDir = nullptr,
+ .SymLink = nullptr,
+ .ReadLink = nullptr,
+ .Seek = nullptr,
+ .Stat = nullptr,
+ };
+
+ std::list Devices;
+ int Entry()
+ {
+ for (auto &&dev : Devices)
+ {
+ PCIManager->InitializeDevice(dev, KernelPageTable);
+ AC97Device *ac97 = new AC97Device((PCI::PCIHeader0 *)dev.Header);
+ dev_t ret = v0::RegisterDevice(DriverID, NETWORK_TYPE_ETHERNET, &ops);
+ Drivers[ret] = ac97;
+ }
+
+ if (Drivers.empty())
+ {
+ info("No valid AC'97 device found.");
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ int Final()
+ {
+ for (auto &&dev : Drivers)
+ {
+ dev_t ret = dev.first;
+ v0::UnregisterDevice(DriverID, ret);
+ delete dev.second;
+ }
+ return 0;
+ }
+
+ int Panic()
+ {
+ for (auto &&i : Drivers)
+ i.second->Panic();
+ return 0;
+ }
+
+ int Probe()
+ {
+ Devices = PCIManager->FindPCIDevice(
+ {
+ 0x8086, /* Intel */
+ },
+ {
+ 0x2415, /* AC'97 */
+ });
+
+ for (auto &&i : Devices)
+ {
+ PCI::PCIHeader0 *hdr0 = (PCI::PCIHeader0 *)i.Header;
+ uint8_t type = hdr0->BAR0 & 1;
+ if (type != 1)
+ {
+ warn("Device %x:%x.%d BAR0 is not I/O.",
+ hdr0->Header.VendorID,
+ hdr0->Header.DeviceID,
+ hdr0->Header.ProgIF);
+ continue;
+ }
+ }
+
+ if (Devices.empty())
+ {
+ info("No AC'97 device found.");
+ return -ENODEV;
+ }
+ return 0;
+ }
+
+ REGISTER_BUILTIN_DRIVER(ac97,
+ "Audio Codec '97 Driver",
+ "enderice2",
+ 1, 0, 0,
+ Entry,
+ Final,
+ Panic,
+ Probe);
+}
diff --git a/Kernel/drivers/audio/hda/hda.cpp b/Kernel/drivers/audio/hda/hda.cpp
new file mode 100644
index 00000000..8756072f
--- /dev/null
+++ b/Kernel/drivers/audio/hda/hda.cpp
@@ -0,0 +1,210 @@
+/*
+ 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 .
+*/
+
+#include
+#include
+#include
+#include "hda.hpp"
+
+extern Driver::Manager *DriverManager;
+extern PCI::Manager *PCIManager;
+namespace Driver::HighDefinitionAudio
+{
+ dev_t DriverID;
+
+ class HDADevice
+ {
+ private:
+ PCI::PCIHeader0 *Header;
+ bool Initialized = false;
+
+ ControllerRegisters *CTL;
+
+ uint32_t *CORB;
+ uint64_t *RIRB;
+
+ public:
+ bool IsInitialized() { return Initialized; }
+
+ size_t write(uint8_t *, size_t Size)
+ {
+ return Size;
+ }
+
+ int ioctl(AudioIoctl, void *)
+ {
+ return 0;
+ }
+
+ void OnInterruptReceived(CPU::TrapFrame *)
+ {
+ }
+
+ void Panic()
+ {
+ }
+
+ HDADevice(PCI::PCIHeader0 *_Header)
+ : Header(_Header),
+ CORB((uint32_t *)(uintptr_t)DriverManager->AllocateMemory(DriverID, 1)),
+ RIRB((uint64_t *)DriverManager->AllocateMemory(DriverID, 1))
+ {
+ CTL = (ControllerRegisters *)(uintptr_t)Header->BAR0;
+ fixme("Unimplemented HDA driver");
+ return;
+ Initialized = true;
+ }
+
+ ~HDADevice()
+ {
+ if (!Initialized)
+ return;
+ }
+ };
+
+ std::unordered_map Drivers;
+
+ int Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
+ {
+ return Drivers[Node->GetMinor()]->ioctl((AudioIoctl)Request, Argp);
+ }
+
+ ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset)
+ {
+ return Drivers[Node->GetMinor()]->write((uint8_t *)Buffer, Size);
+ }
+
+ const struct InodeOperations ops = {
+ .Lookup = nullptr,
+ .Create = nullptr,
+ .Remove = nullptr,
+ .Rename = nullptr,
+ .Read = nullptr,
+ .Write = Write,
+ .Truncate = nullptr,
+ .Open = nullptr,
+ .Close = nullptr,
+ .Ioctl = Ioctl,
+ .ReadDir = nullptr,
+ .MkDir = nullptr,
+ .RmDir = nullptr,
+ .SymLink = nullptr,
+ .ReadLink = nullptr,
+ .Seek = nullptr,
+ .Stat = nullptr,
+ };
+
+ std::list Devices;
+ int Entry()
+ {
+ for (auto &&dev : Devices)
+ {
+ PCI::PCIHeader0 *hdr0 = (PCI::PCIHeader0 *)dev.Header;
+ uint8_t type = hdr0->BAR0 & 1;
+ if (type == 1)
+ {
+ debug("Device %x:%x.%d BAR0 is I/O.",
+ hdr0->Header.VendorID,
+ hdr0->Header.DeviceID,
+ hdr0->Header.ProgIF);
+ continue;
+ }
+
+ PCIManager->InitializeDevice(dev, KernelPageTable);
+
+ HDADevice *driver = new HDADevice((PCI::PCIHeader0 *)dev.Header);
+
+ if (driver->IsInitialized())
+ {
+ dev_t ret = v0::RegisterDevice(DriverID, AUDIO_TYPE_PCM, &ops);
+ Drivers[ret] = driver;
+ }
+ }
+
+ if (Drivers.empty())
+ {
+ debug("No valid HDA device found.");
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ int Final()
+ {
+ for (auto &&dev : Drivers)
+ {
+ dev_t ret = dev.first;
+ v0::UnregisterDevice(DriverID, ret);
+ delete dev.second;
+ }
+ return 0;
+ }
+
+ int Panic()
+ {
+ for (auto &&i : Drivers)
+ i.second->Panic();
+ return 0;
+ }
+
+ int Probe()
+ {
+ Devices = PCIManager->FindPCIDevice(
+ {
+ 0x8086, /* Intel */
+ 0x15AD, /* VMware */
+ },
+ {
+ 0x9D71, /* Sunrise Point-LP HD Audio */
+ 0x2668, /* ICH6 */
+ 0x293E, /* ICH9 */
+ });
+
+ if (Devices.empty())
+ {
+ trace("No HDA device found.");
+ return -ENODEV;
+ }
+
+ for (auto &&dev : Devices)
+ {
+ PCI::PCIHeader0 *PCIBaseAddress = (PCI::PCIHeader0 *)dev.Header;
+ uint32_t PCIBAR0 = PCIBaseAddress->BAR0;
+ uint8_t Type = PCIBAR0 & 1;
+ if (Type == 1)
+ {
+ debug("Device %x:%x.%d BAR0 is I/O.",
+ PCIBaseAddress->Header.VendorID,
+ PCIBaseAddress->Header.DeviceID,
+ PCIBaseAddress->Header.ProgIF);
+ continue;
+ }
+ }
+
+ return 0;
+ }
+
+ REGISTER_BUILTIN_DRIVER(hda,
+ "Intel High Definition Audio Driver",
+ "enderice2",
+ 1, 0, 0,
+ Entry,
+ Final,
+ Panic,
+ Probe);
+}
diff --git a/Kernel/drivers/audio/hda/hda.hpp b/Kernel/drivers/audio/hda/hda.hpp
new file mode 100644
index 00000000..1d653d0c
--- /dev/null
+++ b/Kernel/drivers/audio/hda/hda.hpp
@@ -0,0 +1,627 @@
+/*
+ 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 .
+*/
+
+#pragma once
+
+#include
+
+struct StreamDescriptor
+{
+ /** Control */
+ uint32_t CTL : 24;
+
+ /** Status */
+ uint8_t STS;
+
+ /** Link Position in Current Buffer */
+ uint32_t LPIB;
+
+ /** Cyclic Buffer Length */
+ uint32_t CBL;
+
+ /** Last Valid Index */
+ uint16_t LVI;
+
+ /** Reserved */
+ uint8_t Rsvd0[2];
+
+ /** FIFO Size */
+ uint16_t FIFOD;
+
+ /** Format */
+ uint16_t FMT;
+
+ /** Reserved */
+ uint8_t Rsvd1[4];
+
+ /** Buffer Descriptor List Pointer - Lower */
+ uint32_t BDPL;
+
+ /** Buffer Descriptor List Pointer - Upper */
+ uint32_t BDPU;
+} __attribute__((packed));
+
+struct ControllerRegisters
+{
+ uint16_t GCAP;
+ uint8_t VMIN;
+ uint8_t VMJ;
+ uint16_t OUTPAY;
+ uint16_t INPAY;
+ uint32_t GCTL;
+ uint16_t WAKEEN;
+ uint16_t WAKESTS;
+ uint16_t GSTS;
+ uint8_t Rsvd0[6];
+ uint16_t OUTSTRMPAY;
+ uint16_t INSTRMPAY;
+ uint8_t Rsvd1[4];
+ uint32_t INTCTL;
+ uint32_t INTSTS;
+ uint8_t Rsvd2[8];
+ uint32_t WALCLK;
+ uint8_t Rsvd3[4];
+ uint32_t SSYNC;
+ uint8_t Rsvd4[4];
+ uint32_t CORBLBASE;
+ uint32_t CORBUBASE;
+ uint16_t CORBWP;
+ uint16_t CORBRP;
+ uint8_t CORBCTL;
+ uint8_t CORBSTS;
+ uint8_t CORBSIZE;
+ uint8_t Rsvd5;
+ uint32_t RIRBLBASE;
+ uint32_t RIRBUBASE;
+ uint16_t RIRBWP;
+ uint16_t RINTCNT;
+ uint8_t RIRBCTL;
+ uint8_t RIRBSTS;
+ uint8_t RIRBSIZE;
+ uint8_t Rsvd6;
+ uint32_t ICOI;
+ uint32_t ICII;
+ uint16_t ICIS;
+ uint8_t Rsvd7[6];
+ uint32_t DPIBLBASE;
+ uint32_t DPIBUBASE;
+ uint8_t Rsvd8[8];
+ StreamDescriptor SD[];
+} __attribute__((packed));
+
+/* Not working as expected */
+struct __ControllerRegisters
+{
+ /** Global Capabilities */
+ union
+ {
+ struct
+ {
+ /** 64 Bit Address Supported
+ *
+ * 0 = 32-bit addressing
+ * 1 = 64-bit addressing
+ */
+ uint16_t _64OK : 1;
+
+ /** Number of Serial Data Out Signals
+ *
+ * 00 = 1 SDO
+ * 01 = 2 SDOs
+ * 10 = 4 SDOs
+ * 11 = Reserved
+ */
+ uint16_t NSDO : 2;
+
+ /** Number of Bidirectional Streams Supported
+ *
+ * 00000b = No bidirectional streams supported
+ * 00001b = 1 bidirectional stream supported
+ * ...
+ * 11110b = 30 bidirectional streams supported
+ */
+ uint16_t BSS : 5;
+
+ /** Number of Input Streams Supported
+ *
+ * 0000b = No input streams supported
+ * 0001b = 1 input stream supported
+ * ...
+ * 1111b = 15 input streams supported
+ */
+ uint16_t ISS : 4;
+
+ /** Number of Output Streams Supported
+ *
+ * 0000b = No output streams supported
+ * 0001b = 1 output stream supported
+ * ...
+ * 1111b = 15 output streams supported
+ */
+ uint16_t OSS : 4;
+ } __attribute__((packed));
+ uint16_t Raw;
+ } GCAP;
+
+ /** Minor Version */
+ uint8_t VMIN;
+
+ /** Major Version */
+ uint8_t VMJ;
+
+ /** Output Payload Capability
+ *
+ * 00h = 0 Words
+ * 01h = 1 Word payload
+ * ...
+ * FFh = 255h Word payload
+ */
+ uint16_t OUTPAY;
+
+ /** Input Payload Capability
+ *
+ * 00h = 0 Words
+ * 01h = 1 Word payload
+ * ...
+ * FFh = 255h Word payload
+ */
+ uint16_t INPAY;
+
+ /** Global Control */
+ union
+ {
+ struct
+ {
+ /** Controller Reset
+ *
+ * 0 = Reset
+ * 1 = Normal Operation
+ */
+ uint32_t CRST : 1;
+
+ /** Flush Control
+ *
+ * 0 = Idle
+ * 1 = Flush
+ */
+ uint32_t FCNTRL : 1;
+
+ /** Reserved */
+ uint32_t RsvdP0 : 6;
+
+ /** Accept Unsolicited Response Enable
+ *
+ * 0 = Disabled
+ * 1 = Enabled
+ */
+ uint32_t UNSOL : 1;
+
+ /** Reserved */
+ uint32_t RsvdP1 : 23;
+ } __attribute__((packed));
+ uint32_t Raw;
+ } GCTL;
+
+ /** Wake Enable */
+ union
+ {
+ struct
+ {
+ /** SDIN Wake Enable Flags */
+ uint16_t SDIWEN : 15;
+
+ /** Reserved */
+ uint16_t RsvdP0 : 1;
+ } __attribute__((packed));
+ uint16_t Raw;
+ } WAKEEN;
+
+ /** Wake Status */
+ union
+ {
+ struct
+ {
+ /** SDIN State Change Status Flags */
+ uint16_t SDIWAKE : 15;
+
+ /** Reserved */
+ uint16_t RsvdZ0 : 1;
+ } __attribute__((packed));
+ uint16_t Raw;
+ } WAKESTS;
+
+ /** Global Status */
+ union
+ {
+ struct
+ {
+ uint16_t RsvdZ0 : 1;
+ uint16_t FSTS : 1;
+ uint16_t RsvdZ1 : 14;
+ } __attribute__((packed));
+ uint16_t Raw;
+ } GSTS;
+
+ /** Reserved */
+ uint8_t Rsvd0[6];
+
+ /** Output Stream Payload Capability */
+ uint16_t OUTSTRMPAY;
+
+ /** Input Stream Payload Capability */
+ uint16_t INSTRMPAY;
+
+ /** Reserved */
+ uint8_t Rsvd1[4];
+
+ /** Interrupt Control */
+ union
+ {
+ struct
+ {
+ /** Stream Interrupt Enable
+ *
+ * Bit 0 = Input Stream 0
+ * Bit 1 = Input Stream 1
+ * Bit 2 = Output Stream 0
+ * Bit 3 = Output Stream 1
+ * Bit 4 = Output Stream 2
+ * Bit 5 = Bidirectional Stream 0
+ * Bits 6-28 = Reserved
+ */
+ uint32_t SIE : 30;
+
+ /** Controller Interrupt Enable */
+ uint32_t CIE : 1;
+
+ /** Global Interrupt Enable */
+ uint32_t GIE : 1;
+ } __attribute__((packed));
+ uint32_t Raw;
+ } INTCTL;
+
+ /** Interrupt Status */
+ union
+ {
+ struct
+ {
+ /** Stream Interrupt Status */
+ uint32_t SIS : 30;
+
+ /** Controller Interrupt Status */
+ uint32_t CIS : 1;
+
+ /** Global Interrupt Status */
+ uint32_t GIS : 1;
+ } __attribute__((packed));
+ uint32_t Raw;
+ } INTSTS;
+
+ /** Reserved */
+ uint8_t Rsvd2[8];
+
+ /** Wall Clock Counter */
+ uint32_t WALCLK;
+
+ /** Reserved */
+ uint8_t Rsvd3[4];
+
+ /** Stream Synchronization */
+ union
+ {
+ struct
+ {
+ /** Stream Synchronization Bits */
+ uint32_t SSYNC : 30;
+
+ /** Reserved */
+ uint32_t RsvdP0 : 2;
+ } __attribute__((packed));
+ uint32_t Raw;
+ } SSYNC;
+
+ /** Reserved */
+ uint8_t Rsvd4[4];
+
+ /** CORB Lower Base Address */
+ union
+ {
+ struct
+ {
+ /** CORB Lower Base Unimplemented Bits */
+ uint32_t Unimplemented : 7;
+
+ /** CORB Lower Base Address */
+ uint32_t CORBLBASE : 25;
+ } __attribute__((packed));
+ uint32_t Raw;
+ } CORBLBASE;
+
+ /** CORB Upper Base Address */
+ uint32_t CORBUBASE;
+
+ /** CORB Write Pointer */
+ union
+ {
+ struct
+ {
+ /** CORB Write Pointer */
+ uint16_t CORBWP : 8;
+
+ /** Reserved */
+ uint16_t RsvdP0 : 8;
+ } __attribute__((packed));
+ uint16_t Raw;
+ } CORBWP;
+
+ /** CORB Read Pointer */
+ union
+ {
+ struct
+ {
+ /** CORB Read Pointer */
+ uint16_t CORBRP : 8;
+
+ /** Reserved */
+ uint16_t RsvdP0 : 7;
+
+ /** CORB Read Pointer Reset */
+ uint16_t CORBRPRST : 1;
+ } __attribute__((packed));
+ uint16_t Raw;
+ } CORBRP;
+
+ /** CORB Control */
+ union
+ {
+ struct
+ {
+ /** CORB Memory Error Interrupt Enable */
+ uint8_t CMEIE : 1;
+
+ /** Enable CORB DMA Engine
+ *
+ * 0 = DMA Stop
+ * 1 = DMA Run
+ *
+ * @note Must read the value back.
+ */
+ uint8_t CORBRUN : 1;
+
+ /** Reserved */
+ uint8_t RsvdP0 : 6;
+ } __attribute__((packed));
+ uint8_t Raw;
+ } CORBCTL;
+
+ /** CORB Status */
+ union
+ {
+ struct
+ {
+ /** CORB Memory Error Indication */
+ uint8_t CMEI : 1;
+
+ /** Reserved */
+ uint8_t RsvdZ0 : 7;
+ } __attribute__((packed));
+ uint8_t Raw;
+ } CORBSTS;
+
+ /** CORB Size */
+ union
+ {
+ struct
+ {
+ /** CORB Size
+ *
+ * 00b = 2 entries
+ * 01b = 16 entries
+ * 10b = 256 entries
+ * 11b = Reserved
+ */
+ uint8_t CORBSIZE : 2;
+
+ /** Reserved */
+ uint8_t RsvdP0 : 2;
+
+ /** CORB Size Capability
+ *
+ * 0001b = 2 entries
+ * 0010b = 16 entries
+ * 0100b = 256 entries
+ * 1000b = Reserved
+ */
+ uint8_t CORBSZCAP : 4;
+ } __attribute__((packed));
+ uint8_t Raw;
+ } CORBSIZE;
+
+ /** Reserved */
+ uint8_t Rsvd5;
+
+ /** RIRB Lower Base Address */
+ union
+ {
+ struct
+ {
+ /** RIRB Lower Base Unimplemented Bits */
+ uint32_t Unimplemented : 7;
+
+ /** RIRB Lower Base Address */
+ uint32_t RIRBLBASE : 25;
+ } __attribute__((packed));
+ uint32_t Raw;
+ } RIRBLBASE;
+
+ /** RIRB Upper Base Address */
+ uint32_t RIRBUBASE;
+
+ /** RIRB Write Pointer */
+ union
+ {
+ struct
+ {
+ /** RIRB Write Pointer */
+ uint16_t RIRBWP : 8;
+
+ /** Reserved */
+ uint16_t RsvdP0 : 7;
+
+ /** RIRB Write Pointer Reset */
+ uint16_t RIRBWPRST : 1;
+ } __attribute__((packed));
+ uint16_t Raw;
+ } RIRBWP;
+
+ /** Response Interrupt Count */
+ union
+ {
+ struct
+ {
+ /** N Response Interrupt Count
+ *
+ * 00000001b = 1 Response sent to RIRB
+ * ...
+ * 11111111b = 255 Responses sent to RIRB
+ * 00000000b = 256 Response sent to RIRB
+ */
+ uint16_t RINTCNT : 8;
+
+ /** Reserved */
+ uint16_t RsvdP0 : 8;
+ } __attribute__((packed));
+ uint16_t Raw;
+ } RINTCNT;
+
+ /** RIRB Control */
+ union
+ {
+ struct
+ {
+ /** Response Interrupt Control
+ *
+ * 0 = Disable Interrupt
+ * 1 = Generate an interrupt after N responses are sent to the RIRB
+ */
+ uint8_t RINTCTL : 1;
+
+ /** RIRB DMA Enable
+ *
+ * 0 = DMA Stop
+ * 1 = DMA Run
+ */
+ uint8_t RIRBDMAEN : 1;
+
+ /** Response Overrun Interrupt Control */
+ uint8_t RIRBOIC : 1;
+
+ /** Reserved */
+ uint8_t RsvdP0 : 5;
+ } __attribute__((packed));
+ uint8_t Raw;
+ } RIRBCTL;
+
+ /** RIRB Status */
+ union
+ {
+ struct
+ {
+ /** Response Interrupt */
+ uint8_t RINTFL : 1;
+
+ /** Reserved */
+ uint8_t RsvdZ0 : 1;
+
+ /** Response Overrun Interrupt Status */
+ uint8_t RIRBOIS : 1;
+
+ /** Reserved */
+ uint8_t RsvdZ1 : 5;
+ } __attribute__((packed));
+ uint8_t Raw;
+ } RIRBSTS;
+
+ /** RIRB Size */
+ union
+ {
+ struct
+ {
+ /** RIRB Size
+ *
+ * 00b = 2 entries
+ * 01b = 16 entries
+ * 10b = 256 entries
+ * 11b = Reserved
+ */
+ uint8_t RIRBSIZE : 2;
+
+ /** Reserved */
+ uint8_t RsvdP0 : 2;
+
+ /** RIRB Size Capability
+ *
+ * 0001b = 2 entries
+ * 0010b = 16 entries
+ * 0100b = 256 entries
+ * 1000b = Reserved
+ */
+ uint8_t RIRBSZCAP : 4;
+ } __attribute__((packed));
+ uint8_t Raw;
+ } RIRBSIZE;
+
+ /** Reserved */
+ uint8_t Rsvd6;
+
+ /** Immediate Command Output Interface */
+ uint32_t ICOI;
+
+ /** Immediate Command Input Interface */
+ uint32_t ICII;
+
+ /** Immediate Command Status */
+ uint16_t ICIS;
+
+ /** Reserved */
+ uint8_t Rsvd7[6];
+
+ /** DMA Position Buffer Lower Base */
+ union
+ {
+ struct
+ {
+ /** DMA Position Buffer Enable */
+ uint32_t DPBEN : 1;
+
+ /** Reserved */
+ uint32_t RsvdZ0 : 6;
+
+ /** DMA Position Lower Base Address */
+ uint32_t DPLBASE : 25;
+ } __attribute__((packed));
+ uint32_t Raw;
+ } DPIBLBASE;
+
+ /** DMA Position Buffer Upper Base */
+ uint32_t DPIBUBASE;
+
+ /** Reserved */
+ uint8_t Rsvd8[8];
+
+ StreamDescriptor SD[];
+} __attribute__((packed));
diff --git a/Kernel/drivers/fs/fat/fat.cpp b/Kernel/drivers/fs/fat/fat.cpp
new file mode 100644
index 00000000..3feece7b
--- /dev/null
+++ b/Kernel/drivers/fs/fat/fat.cpp
@@ -0,0 +1,37 @@
+/*
+ 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 .
+*/
+
+#include
+
+namespace Driver::FileAllocationTable
+{
+ dev_t DriverID;
+
+ int Entry() { return 0; }
+ int Final() { return 0; }
+ int Panic() { return 0; }
+ int Probe() { return 0; }
+
+ REGISTER_BUILTIN_DRIVER(fat,
+ "File Allocation Table Driver",
+ "enderice2",
+ 1, 0, 0,
+ Entry,
+ Final,
+ Panic,
+ Probe);
+}
diff --git a/Kernel/drivers/fs/fat/fat.hpp b/Kernel/drivers/fs/fat/fat.hpp
new file mode 100644
index 00000000..3c416341
--- /dev/null
+++ b/Kernel/drivers/fs/fat/fat.hpp
@@ -0,0 +1,287 @@
+/*
+ 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 .
+*/
+
+#pragma once
+
+#include
+
+/* Source: https://wiki.osdev.org/FAT */
+
+struct BIOSParameterBlock
+{
+ /** The first three bytes EB 3C 90 disassemble to JMP SHORT 3C NOP.
+ * (The 3C value may be different.) The reason for this is to jump
+ * over the disk format information (the BPB and EBPB). Since the
+ * first sector of the disk is loaded into ram at location
+ * 0x0000:0x7c00 and executed, without this jump, the processor
+ * would attempt to execute data that isn't code. Even for
+ * non-bootable volumes, code matching this pattern (or using the
+ * E9 jump opcode) is required to be present by both Windows and
+ * OS X. To fulfil this requirement, an infinite loop can be placed
+ * here with the bytes EB FE 90. */
+ uint8_t JumpBoot[3];
+
+ /** OEM identifier. The first 8 Bytes (3 - 10) is the version of DOS
+ * being used. The next eight Bytes 29 3A 63 7E 2D 49 48 and 43 read
+ * out the name of the version. The official FAT Specification from
+ * Microsoft says that this field is really meaningless and is ignored
+ * by MS FAT Modules, however it does recommend the value "MSWIN4.1"
+ * as some 3rd party drivers supposedly check it and expect it to
+ * have that value. Older versions of dos also report MSDOS5.1,
+ * linux-formatted floppy will likely to carry "mkdosfs" here, and
+ * FreeDOS formatted disks have been observed to have "FRDOS5.1" here.
+ * If the string is less than 8 bytes, it is padded with spaces. */
+ uint8_t OEM[8];
+
+ /** The number of Bytes per sector (remember, all numbers are in the
+ * little-endian format). */
+ uint16_t BytesPerSector;
+
+ /** Number of sectors per cluster. */
+ uint8_t SectorsPerCluster;
+
+ /** Number of reserved sectors. The boot record sectors are included
+ * in this value. */
+ uint16_t ReservedSectors;
+
+ /** Number of File Allocation Tables (FAT's) on the storage media.
+ * Often this value is 2. */
+ uint8_t NumberOfFATs;
+
+ /** Number of root directory entries (must be set so that the root
+ * directory occupies entire sectors). */
+ uint16_t RootDirectoryEntries;
+
+ /** The total sectors in the logical volume. If this value is 0, it
+ * means there are more than 65535 sectors in the volume, and the
+ * actual count is stored in the Large Sector Count entry at 0x20. */
+ uint16_t Sectors16;
+
+ /** This Byte indicates the media descriptor type. */
+ uint8_t Media;
+
+ /** Number of sectors per FAT. FAT12/FAT16 only. */
+ uint16_t SectorsPerFAT;
+
+ /** Number of sectors per track. */
+ uint16_t SectorsPerTrack;
+
+ /** Number of heads or sides on the storage media. */
+ uint16_t NumberOfHeads;
+
+ /** Number of hidden sectors. (i.e. the LBA of the beginning of
+ * the partition). */
+ uint32_t HiddenSectors;
+
+ /** Large sector count. This field is set if there are more than
+ * 65535 sectors in the volume, resulting in a value which does not
+ * fit in the Number of Sectors entry at 0x13. */
+ uint32_t Sectors32;
+} __packed;
+
+struct ExtendedBootRecord_FAT12_16
+{
+ /** Drive number. The value here should be identical to the value
+ * returned by BIOS interrupt 0x13, or passed in the DL register;
+ * i.e. 0x00 for a floppy disk and 0x80 for hard disks. This number
+ * is useless because the media is likely to be moved to another
+ * machine and inserted in a drive with a different drive number. */
+ uint8_t DriveNumber;
+
+ /** Flags in Windows NT. Reserved otherwise. */
+ uint8_t Flags;
+
+ /** Signature (must be 0x28 or 0x29). */
+ uint8_t Signature;
+
+ /** VolumeID 'Serial' number. Used for tracking volumes between
+ * computers. You can ignore this if you want. */
+ uint32_t VolumeID;
+
+ /** Volume label string. This field is padded with spaces. */
+ uint8_t VolumeLabel[11];
+
+ /** System identifier string. This field is a string representation
+ * of the FAT file system type. It is padded with spaces. The spec
+ * says never to trust the contents of this string for any use. */
+ uint8_t SystemIdentifier[8];
+
+ /** Boot code. */
+ uint8_t BootCode[448];
+
+ /** Bootable partition signature 0xAA55. */
+ uint16_t BootSignature;
+} __packed;
+
+struct ExtendedBootRecord_FAT32
+{
+ /** Sectors per FAT. The size of the FAT in sectors. */
+ uint32_t SectorsPerFAT;
+
+ /** Flags. */
+ uint16_t Flags;
+
+ /** FAT version number. The high byte is the major version and the
+ * low byte is the minor version. FAT drivers should respect this
+ * field. */
+ uint16_t FATVersion;
+
+ /** The cluster number of the root directory. Often this field is
+ * set to 2. */
+ uint32_t RootDirectoryCluster;
+
+ /** The sector number of the FSInfo structure. */
+ uint16_t FSInfoSector;
+
+ /** The sector number of the backup boot sector. */
+ uint16_t BackupBootSector;
+
+ /** Reserved. When the volume is formated these bytes should be zero. */
+ uint8_t Reserved[12];
+
+ /** Drive number. The values here are identical to the values returned
+ * by the BIOS interrupt 0x13. 0x00 for a floppy disk and 0x80 for
+ * hard disks. */
+ uint8_t DriveNumber;
+
+ /** Flags in Windows NT. Reserved otherwise. */
+ uint8_t Flags2;
+
+ /** Signature (must be 0x28 or 0x29). */
+ uint8_t Signature;
+
+ /** Volume ID 'Serial' number. Used for tracking volumes between
+ * computers. You can ignore this if you want. */
+ uint32_t VolumeID;
+
+ /** Volume label string. This field is padded with spaces. */
+ uint8_t VolumeLabel[11];
+
+ /** System identifier string. Always "FAT32 ". The spec says never
+ * to trust the contents of this string for any use. */
+ uint8_t SystemIdentifier[8];
+
+ /** Boot code. */
+ uint8_t BootCode[420];
+
+ /** Bootable partition signature 0xAA55. */
+ uint16_t BootSignature;
+} __packed;
+
+struct FSInfo
+{
+ /** Lead signature (must be 0x41615252 to indicate a valid FSInfo
+ * structure). */
+ uint32_t LeadSignature;
+
+ /** Reserved, these bytes should never be used. */
+ uint8_t Reserved1[480];
+
+ /** Another signature (must be 0x61417272). */
+ uint32_t AnotherSignature;
+
+ /** Contains the last known free cluster count on the volume. If the
+ * value is 0xFFFFFFFF, then the free count is unknown and must be
+ * computed. However, this value might be incorrect and should at
+ * least be range checked (<= volume cluster count). */
+ uint32_t FreeClusterCount;
+
+ /** Indicates the cluster number at which the filesystem driver should
+ * start looking for available clusters. If the value is 0xFFFFFFFF,
+ * then there is no hint and the driver should start searching at 2.
+ * Typically this value is set to the last allocated cluster number.
+ * As the previous field, this value should be range checked. */
+ uint32_t NextFreeCluster;
+
+ /** Reserved. */
+ uint8_t Reserved2[12];
+
+ /** Trail signature (0xAA550000). */
+ uint32_t TrailSignature;
+} __packed;
+
+struct exFATBootRecord
+{
+ /** The first three bytes EB 3C 90 disassemble to JMP SHORT 3C NOP.
+ * (The 3C value may be different.) The reason for this is to jump
+ * over the disk format information (the BPB and EBPB). Since the
+ * first sector of the disk is loaded into ram at location
+ * 0x0000:0x7c00 and executed, without this jump, the processor
+ * would attempt to execute data that isn't code. Even for
+ * non-bootable volumes, code matching this pattern (or using the
+ * E9 jump opcode) is required to be present by both Windows and
+ * OS X. To fulfil this requirement, an infinite loop can be placed
+ * here with the bytes EB FE 90. */
+ uint8_t JumpBoot[3];
+
+ /** OEM identifier. This contains the string "EXFAT ". Not to be
+ * used for filesystem determination, but it's a nice hint. */
+ uint8_t OEM[8];
+
+ /** Set to zero. This makes sure any FAT driver will not be able to
+ * load it. */
+ uint8_t Reserved1[53];
+
+ /** Partition offset. No idea why the partition itself would have
+ * this, but it's here. Might be wrong. Probably best to just ignore. */
+ uint64_t PartitionOffset;
+
+ /** Volume length. */
+ uint64_t VolumeLength;
+
+ /** FAT offset (in sectors) from start of partition. */
+ uint32_t FATOffset;
+
+ /** FAT length (in sectors). */
+ uint32_t FATLength;
+
+ /** Cluster heap offset (in sectors). */
+ uint32_t ClusterHeapOffset;
+
+ /** Cluster count. */
+ uint32_t ClusterCount;
+
+ /** Root directory cluster. Typically 4 (but just read this value). */
+ uint32_t RootDirectoryCluster;
+
+ /** Serial number of partition. */
+ uint32_t SerialNumber;
+
+ /** Filesystem revision. */
+ uint16_t FilesystemRevision;
+
+ /** Flags. */
+ uint16_t Flags;
+
+ /** Sector shift. */
+ uint8_t SectorShift;
+
+ /** Cluster shift. */
+ uint8_t ClusterShift;
+
+ /** Number of FATs. */
+ uint8_t NumberOfFATs;
+
+ /** Drive select. */
+ uint8_t DriveSelect;
+
+ /** Percentage in use. */
+ uint8_t PercentageInUse;
+
+ /** Reserved (set to 0). */
+ uint8_t Reserved2[7];
+} __packed;
diff --git a/Kernel/drivers/misc/aip/aip.hpp b/Kernel/drivers/misc/aip/aip.hpp
new file mode 100644
index 00000000..6d611167
--- /dev/null
+++ b/Kernel/drivers/misc/aip/aip.hpp
@@ -0,0 +1,43 @@
+/*
+ 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 .
+*/
+
+#pragma once
+
+#include
+#include
+
+namespace Driver::AdvancedIntegratedPeripheral
+{
+ extern uint8_t Device1ID[];
+ extern uint8_t Device2ID[];
+
+ void PS2KbdInterruptHandler(CPU::TrapFrame *);
+ int InitializeKeyboard();
+ int FinalizeKeyboard();
+ int DetectPS2Keyboard();
+
+ void PS2MouseInterruptHandler(CPU::TrapFrame *);
+ int InitializeMouse();
+ int FinalizeMouse();
+ int DetectPS2Mouse();
+ int DetectUART();
+
+ void MasterInterruptHandler(CPU::TrapFrame *);
+ void SlaveInterruptHandler(CPU::TrapFrame *);
+ int InitializeATA();
+ int FinalizeATA();
+}
diff --git a/Kernel/drivers/misc/aip/ata.cpp b/Kernel/drivers/misc/aip/ata.cpp
new file mode 100644
index 00000000..adcdfbed
--- /dev/null
+++ b/Kernel/drivers/misc/aip/ata.cpp
@@ -0,0 +1,70 @@
+/*
+ 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 .
+*/
+
+#include
+#include
+#include
+#include
+
+extern Driver::Manager *DriverManager;
+extern PCI::Manager *PCIManager;
+namespace Driver::AdvancedIntegratedPeripheral
+{
+ extern dev_t DriverID;
+
+ const struct InodeOperations opsATA = {
+ .Lookup = nullptr,
+ .Create = nullptr,
+ .Remove = nullptr,
+ .Rename = nullptr,
+ .Read = nullptr,
+ .Write = nullptr,
+ .Truncate = nullptr,
+ .Open = nullptr,
+ .Close = nullptr,
+ .Ioctl = nullptr,
+ .ReadDir = nullptr,
+ .MkDir = nullptr,
+ .RmDir = nullptr,
+ .SymLink = nullptr,
+ .ReadLink = nullptr,
+ .Seek = nullptr,
+ .Stat = nullptr,
+ };
+
+ void MasterInterruptHandler(CPU::TrapFrame *)
+ {
+ }
+
+ void SlaveInterruptHandler(CPU::TrapFrame *)
+ {
+ }
+
+ int InitializeATA()
+ {
+ v0::RegisterInterruptHandler(DriverID, 14, (void *)MasterInterruptHandler);
+ v0::RegisterInterruptHandler(DriverID, 15, (void *)SlaveInterruptHandler);
+ return 0;
+ }
+
+ int FinalizeATA()
+ {
+ v0::UnregisterInterruptHandler(DriverID, 14, (void *)MasterInterruptHandler);
+ v0::UnregisterInterruptHandler(DriverID, 15, (void *)SlaveInterruptHandler);
+ return 0;
+ }
+}
diff --git a/Kernel/drivers/misc/aip/keyboard.cpp b/Kernel/drivers/misc/aip/keyboard.cpp
new file mode 100644
index 00000000..6d7898c1
--- /dev/null
+++ b/Kernel/drivers/misc/aip/keyboard.cpp
@@ -0,0 +1,224 @@
+/*
+ This file is part of Fennix Drivers.
+
+ Fennix Drivers is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ Fennix Drivers is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Fennix Drivers. If not, see .
+*/
+
+#include "aip.hpp"
+
+#include
+#include
+#include
+#include
+#include
+
+extern const unsigned short ScanCodeSet1[];
+extern const unsigned short ScanCodeSet1mm[];
+extern const unsigned short ScanCodeSet3[];
+
+namespace Driver::AdvancedIntegratedPeripheral
+{
+ extern dev_t DriverID;
+ uint8_t KeyboardScanCodeSet = 0;
+ dev_t KeyboardDevID = -1;
+
+ InputReport kir = {};
+ int ReportKeyboardEvent(dev_t Device, const unsigned short ScanCode, uint8_t Pressed)
+ {
+ kir.Type = INPUT_TYPE_KEYBOARD;
+ kir.Device = Device;
+ kir.Keyboard.Key = (KeyScanCodes)(ScanCode);
+ kir.Keyboard.Key = (KeyScanCodes)((int)kir.Keyboard.Key | (Pressed ? KEY_PRESSED : 0));
+ // kir.Keyboard.Key |= Pressed ? KEY_PRESSED : 0;
+ v0::ReportInputEvent(DriverID, &kir);
+ return 0;
+ }
+
+ bool IsE0 = false;
+ bool IsE1 = false;
+ void PS2KbdInterruptHandler(CPU::TrapFrame *)
+ {
+ uint8_t sc = inb(PS2_DATA);
+ if (sc == PS2_KBD_RESP_ACK ||
+ sc == PS2_KBD_RESP_ECHO ||
+ sc == PS2_KBD_RESP_RESEND)
+ return;
+
+ if (sc == 0xE0)
+ {
+ IsE0 = true;
+ return;
+ }
+
+ if (sc == 0xE1)
+ {
+ IsE1 = true;
+ return;
+ }
+
+ switch (KeyboardScanCodeSet)
+ {
+ case PS2_KBD_SC_SET_1:
+ case PS2_KBD_SC_SET_2:
+ {
+ if (IsE0)
+ {
+ IsE0 = false;
+ ReportKeyboardEvent(KeyboardDevID, ScanCodeSet1mm[sc], sc < 0x90);
+ return;
+ }
+ else
+ {
+ bool released = sc & 0x80;
+ uint8_t scFinal = released ? sc & 0x7F : sc;
+ ReportKeyboardEvent(KeyboardDevID, ScanCodeSet1[scFinal], !released);
+ return;
+ }
+ }
+ /* FIXME: https://wiki.osdev.org/PS/2_Keyboard */
+ // case PS2_KBD_SC_SET_2:
+ // {
+ // break;
+ // }
+ case PS2_KBD_SC_SET_3:
+ {
+ ReportKeyboardEvent(KeyboardDevID, ScanCodeSet3[sc], true);
+ ReportKeyboardEvent(KeyboardDevID, ScanCodeSet3[sc], false);
+ break;
+ }
+ default:
+ {
+ if (IsE0)
+ IsE0 = false;
+ trace("Unknown PS/2 Keyboard Scan Code Set: %#x", KeyboardScanCodeSet);
+ break;
+ }
+ }
+ }
+
+ int __fs_kb_Ioctl(struct Inode *, unsigned long, void *)
+ {
+ return 0;
+ }
+
+ const struct InodeOperations KbdOps = {
+ .Lookup = nullptr,
+ .Create = nullptr,
+ .Remove = nullptr,
+ .Rename = nullptr,
+ .Read = nullptr,
+ .Write = nullptr,
+ .Truncate = nullptr,
+ .Open = nullptr,
+ .Close = nullptr,
+ .Ioctl = __fs_kb_Ioctl,
+ .ReadDir = nullptr,
+ .MkDir = nullptr,
+ .RmDir = nullptr,
+ .SymLink = nullptr,
+ .ReadLink = nullptr,
+ .Seek = nullptr,
+ .Stat = nullptr,
+ };
+
+ int InitializeKeyboard()
+ {
+ // v0::PS2WriteData(DriverID, PS2_KBD_CMD_RESET);
+ // uint8_t test = v0::PS2ReadData(DriverID);
+ // if (test != PS2_KBD_RESP_TEST_PASSED &&
+ // test != PS2_KBD_RESP_ACK)
+ // {
+ // trace("PS/2 keyboard reset failed (%#x)", test);
+ // return -EFAULT;
+ // }
+
+ v0::PS2WriteData(DriverID, PS2_KBD_CMD_DEFAULTS);
+ if (v0::PS2ACKTimeout(DriverID) != 0)
+ trace("PS/2 keyboard failed to set defaults");
+
+ v0::PS2WriteData(DriverID, PS2_KBD_CMD_SCAN_CODE_SET);
+ if (v0::PS2ACKTimeout(DriverID) != 0)
+ trace("PS/2 keyboard failed to set scan code set");
+
+ /* We want Scan Code Set 1 */
+ v0::PS2WriteData(DriverID, PS2_KBD_SCAN_CODE_SET_2); /* It will set to 1 but with translation? */
+ if (v0::PS2ACKTimeout(DriverID) != 0)
+ trace("PS/2 keyboard failed to set scan code set 2");
+
+ v0::PS2WriteData(DriverID, PS2_KBD_CMD_SCAN_CODE_SET);
+ if (v0::PS2ACKTimeout(DriverID) != 0)
+ trace("PS/2 keyboard failed to set scan code set");
+
+ v0::PS2WriteData(DriverID, PS2_KBD_SCAN_CODE_GET_CURRENT);
+ if (v0::PS2ACKTimeout(DriverID) != 0)
+ trace("PS/2 keyboard failed to get current scan code set");
+
+ KeyboardScanCodeSet = v0::PS2ReadAfterACK(DriverID);
+ trace("PS/2 Keyboard Scan Code Set: 0x%X", KeyboardScanCodeSet);
+ v0::PS2ClearOutputBuffer(DriverID);
+
+ v0::PS2WriteData(DriverID, PS2_KBD_CMD_ENABLE_SCANNING);
+
+ v0::RegisterInterruptHandler(DriverID, 1, (void *)PS2KbdInterruptHandler);
+
+ KeyboardDevID = v0::RegisterDevice(DriverID, INPUT_TYPE_KEYBOARD, &KbdOps);
+ return 0;
+ }
+
+ int FinalizeKeyboard()
+ {
+ v0::PS2WriteData(DriverID, PS2_KBD_CMD_DISABLE_SCANNING);
+ if (v0::PS2ACKTimeout(DriverID) != 0)
+ trace("PS/2 keyboard failed to disable scanning");
+
+ v0::UnregisterDevice(DriverID, KeyboardDevID);
+ return 0;
+ }
+
+ int DetectPS2Keyboard()
+ {
+ v0::PS2WriteData(DriverID, PS2_KBD_CMD_DISABLE_SCANNING);
+ if (v0::PS2ACKTimeout(DriverID) != 0)
+ trace("PS/2 keyboard failed to disable scanning");
+
+ v0::PS2WriteData(DriverID, PS2_KBD_CMD_IDENTIFY);
+ if (v0::PS2ACKTimeout(DriverID) != 0)
+ trace("PS/2 keyboard failed to identify");
+
+ uint8_t recByte;
+ int timeout = 1000000;
+ while (timeout--)
+ {
+ recByte = v0::PS2ReadData(DriverID);
+ if (recByte != PS2_ACK)
+ break;
+ }
+ Device1ID[0] = recByte;
+
+ timeout = 1000000;
+ while (timeout--)
+ {
+ recByte = v0::PS2ReadData(DriverID);
+ if (recByte != PS2_ACK)
+ break;
+ }
+ if (timeout == 0)
+ trace("PS/2 keyboard second byte timed out");
+ else
+ Device1ID[1] = recByte;
+
+ trace("PS2 Keyboard Device: 0x%X 0x%X", Device1ID[0], Device1ID[1]);
+ return 0;
+ }
+}
diff --git a/Kernel/drivers/misc/aip/main.cpp b/Kernel/drivers/misc/aip/main.cpp
new file mode 100644
index 00000000..ee73c6cf
--- /dev/null
+++ b/Kernel/drivers/misc/aip/main.cpp
@@ -0,0 +1,260 @@
+/*
+ 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 .
+*/
+
+#include
+#include
+#include
+#include
+
+#include "aip.hpp"
+
+extern Driver::Manager *DriverManager;
+extern PCI::Manager *PCIManager;
+EXTERNC void KPrint(const char *Format, ...);
+namespace Driver::AdvancedIntegratedPeripheral
+{
+ dev_t DriverID;
+
+ bool IsATAPresent()
+ {
+ outb(0x1F0 + 2, 0);
+ outb(0x1F0 + 3, 0);
+ outb(0x1F0 + 4, 0);
+ outb(0x1F0 + 5, 0);
+ outb(0x1F0 + 7, 0xEC);
+ if (inb(0x1F0 + 7) == 0 || inb(0x1F0 + 1) != 0)
+ return false;
+ return true;
+ }
+
+ bool IsKeyboard(uint8_t ID)
+ {
+ /* Common keyboard IDs */
+ return ID == 0xAB || ID == 0xAC || ID == 0x5D ||
+ ID == 0x2B || ID == 0x47 || ID == 0x60;
+ }
+
+ bool IsMouse(uint8_t ID)
+ {
+ /* Common mouse IDs */
+ return ID == 0x00 || ID == 0x03 || ID == 0x04;
+ }
+
+ const char *GetPS2DeviceName(uint8_t ID, uint8_t SubID)
+ {
+ switch (ID)
+ {
+ case 0x00:
+ return "Standard PS/2 Mouse";
+ case 0x03:
+ return "Mouse with scroll wheel";
+ case 0x04:
+ return "Mouse 5 buttons";
+ case 0xAB:
+ {
+ switch (SubID)
+ {
+ case 0x83: /* Normal */
+ case 0x41: /* Translated */
+ case 0xC1: /* Normal + Translated */
+ return "Standard PS/2 Keyboard";
+ case 0x84:
+ case 0x54:
+ return "IBM Thinkpad/Spacesaver Keyboard";
+ case 0x85:
+ return "NCD N-97/122-Key Host Connect(ed) Keyboard";
+ case 0x86:
+ return "122-Key Keyboard";
+ case 0x90:
+ return "Japanese \"G\" Keyboard";
+ case 0x91:
+ return "Japanese \"P\" Keyboard";
+ case 0x92:
+ return "Japanese \"A\" Keyboard";
+ default:
+ return "Unknown PS/2 Keyboard";
+ }
+ }
+ case 0xAC:
+ {
+ switch (SubID)
+ {
+ case 0xA1:
+ return "NCD Sun Keyboard";
+ default:
+ return "Unknown NCD Sun Keyboard";
+ }
+ }
+ case 0x5D:
+ case 0x2B:
+ return "Trust Keyboard";
+ case 0x47:
+ case 0x60:
+ return "NMB SGI Keyboard";
+ default:
+ return "Unknown PS/2 Device";
+ }
+ }
+
+ uint8_t Device1ID[2] = {0x00, 0x00};
+ uint8_t Device2ID[2] = {0x00, 0x00};
+ bool DualChannel = false;
+ bool ATAPresent = false;
+
+ int Entry()
+ {
+ v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_1);
+ v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_2);
+ v0::PS2ClearOutputBuffer(DriverID);
+
+ v0::PS2WriteCommand(DriverID, PS2_CMD_READ_CONFIG);
+ PS2_CONFIGURATION cfg = {.Raw = v0::PS2ReadData(DriverID)};
+
+ DualChannel = cfg.Port2Clock;
+ if (DualChannel)
+ trace("Dual channel PS/2 controller detected");
+ cfg.Port1Interrupt = 1;
+ cfg.Port2Interrupt = 1;
+ cfg.Port1Translation = 1;
+
+ v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_CONFIG);
+ v0::PS2WriteData(DriverID, cfg.Raw);
+
+ v0::PS2WriteCommand(DriverID, PS2_CMD_TEST_CONTROLLER);
+ uint8_t test = v0::PS2ReadData(DriverID);
+ if (test != PS2_TEST_PASSED)
+ {
+ trace("PS/2 controller self test failed (%#x)", test);
+ return -EFAULT;
+ }
+
+ v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_CONFIG);
+ v0::PS2WriteData(DriverID, cfg.Raw);
+
+ // bool port2avail = false;
+ // if (DualChannel)
+ // {
+ // v0::PS2WriteCommand(DriverID, PS2_CMD_ENABLE_PORT_1);
+ // v0::PS2WriteCommand(DriverID, PS2_CMD_READ_CONFIG);
+ // cfg.Raw = v0::PS2ReadData(DriverID);
+ // port2avail = cfg.Port2Clock;
+ // v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_1);
+ // }
+
+ v0::PS2WriteCommand(DriverID, PS2_CMD_TEST_PORT_1);
+ test = v0::PS2ReadData(DriverID);
+ if (test != 0x00)
+ {
+ trace("PS/2 Port 1 self test failed (%#x)", test);
+ return -EFAULT;
+ }
+
+ if (DualChannel)
+ {
+ v0::PS2WriteCommand(DriverID, PS2_CMD_TEST_PORT_2);
+ test = v0::PS2ReadData(DriverID);
+ if (test != 0x00)
+ {
+ trace("PS/2 Port 2 self test failed (%#x)", test);
+ return -EFAULT;
+ }
+ }
+
+ v0::PS2WriteCommand(DriverID, PS2_CMD_ENABLE_PORT_1);
+ if (DualChannel)
+ v0::PS2WriteCommand(DriverID, PS2_CMD_ENABLE_PORT_2);
+
+ int errK = InitializeKeyboard();
+
+ int errM = 0;
+ if (DualChannel)
+ errM = InitializeMouse();
+
+ ATAPresent = IsATAPresent();
+
+ if (errK != 0 && errM != 0 && ATAPresent == false)
+ return -ENODEV;
+ return 0;
+ }
+
+ int Final()
+ {
+ FinalizeKeyboard();
+ FinalizeMouse();
+ v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_1);
+ v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_2);
+ return 0;
+ }
+
+ int Panic()
+ {
+ v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_1);
+ v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_2);
+ return 0;
+ }
+
+ void __intStub() {}
+ int Probe()
+ {
+ v0::RegisterInterruptHandler(DriverID, 1, (void *)__intStub);
+ v0::RegisterInterruptHandler(DriverID, 12, (void *)__intStub);
+
+ int kbd = DetectPS2Keyboard();
+ int mouse = DetectPS2Mouse();
+ int uart = DetectUART();
+
+ v0::UnregisterAllInterruptHandlers(DriverID, (void *)__intStub);
+
+ if (kbd != 0 && mouse != 0 && uart != 0)
+ return -ENODEV;
+
+ if (kbd == 0)
+ {
+ if (!IsKeyboard(Device1ID[0]))
+ {
+ trace("PS/2 Port 1 is not a keyboard");
+ // return -EINVAL;
+ }
+ }
+
+ if (mouse == 0)
+ {
+ if (!IsMouse(Device2ID[0]))
+ {
+ trace("PS/2 Port 2 is not a mouse");
+ // return -EINVAL;
+ }
+ }
+
+ KPrint("PS/2 Port 1: %s (0x%X 0x%X)",
+ GetPS2DeviceName(Device1ID[0], Device1ID[1]),
+ Device1ID[0], Device1ID[1]);
+ KPrint("PS/2 Port 2: %s (0x%X 0x%X)",
+ GetPS2DeviceName(Device2ID[0], Device2ID[1]),
+ Device2ID[0], Device2ID[1]);
+ return 0;
+ }
+
+ REGISTER_BUILTIN_DRIVER(aip,
+ "Advanced Integrated Peripheral Driver",
+ "enderice2",
+ 1, 0, 0,
+ Entry,
+ Final,
+ Panic,
+ Probe);
+}
diff --git a/Kernel/drivers/misc/aip/mouse.cpp b/Kernel/drivers/misc/aip/mouse.cpp
new file mode 100644
index 00000000..65849621
--- /dev/null
+++ b/Kernel/drivers/misc/aip/mouse.cpp
@@ -0,0 +1,263 @@
+/*
+ This file is part of Fennix Drivers.
+
+ Fennix Drivers is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ Fennix Drivers is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Fennix Drivers. If not, see .
+*/
+
+#include "aip.hpp"
+
+#include
+#include
+#include
+
+namespace Driver::AdvancedIntegratedPeripheral
+{
+ extern dev_t DriverID;
+ dev_t MouseDevID = -1;
+ bool PacketReady = false;
+ bool FourPackets = false;
+ bool MouseButton45 = false;
+ uint8_t Cycle = 0;
+ PS2_MOUSE_PACKET Packet = {};
+
+ InputReport mir = {};
+ void PS2MouseInterruptHandler(CPU::TrapFrame *)
+ {
+ uint8_t data = v0::PS2ReadData(DriverID);
+ if (data == PS2_MOUSE_RESP_ACK ||
+ data == PS2_MOUSE_RESP_RESEND)
+ return;
+
+ if (!PacketReady)
+ {
+ switch (Cycle)
+ {
+ case 0:
+ {
+ if ((data & 0b00001000 /* Always 1 */) == 0)
+ return;
+
+ Packet.Base.Raw = data;
+ Cycle++;
+ break;
+ }
+ case 1:
+ {
+ Packet.XMovement = data;
+ Cycle++;
+ break;
+ }
+ case 2:
+ {
+ Packet.YMovement = data;
+ if (FourPackets)
+ Cycle++;
+ else
+ {
+ Cycle = 0;
+ PacketReady = true;
+ }
+ break;
+ }
+ case 3:
+ {
+ Packet.ZMovement.Raw = data;
+ Cycle = 0;
+ PacketReady = true;
+ break;
+ }
+ default:
+ break;
+ }
+ return;
+ }
+
+/* https://stackoverflow.com/a/3208376/9352057 */
+#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
+#define BYTE_TO_BINARY(byte) \
+ ((byte) & 0x80 ? '1' : '0'), \
+ ((byte) & 0x40 ? '1' : '0'), \
+ ((byte) & 0x20 ? '1' : '0'), \
+ ((byte) & 0x10 ? '1' : '0'), \
+ ((byte) & 0x08 ? '1' : '0'), \
+ ((byte) & 0x04 ? '1' : '0'), \
+ ((byte) & 0x02 ? '1' : '0'), \
+ ((byte) & 0x01 ? '1' : '0')
+
+ debug("PS/2 Mouse Packet: [" BYTE_TO_BINARY_PATTERN ":" BYTE_TO_BINARY_PATTERN ":" BYTE_TO_BINARY_PATTERN ":" BYTE_TO_BINARY_PATTERN "] LB:%d RB:%d MB:%d A1:%d XS:%d YS:%d XO:%d YO:%d | X:%03d Y:%03d | Z:%d B4:%d B5:%d A0:%d A0:%d",
+ BYTE_TO_BINARY(Packet.Base.Raw),
+ BYTE_TO_BINARY(Packet.XMovement),
+ BYTE_TO_BINARY(Packet.YMovement),
+ BYTE_TO_BINARY(Packet.ZMovement.Raw),
+ Packet.Base.LeftButton, Packet.Base.RightButton, Packet.Base.MiddleButton,
+ Packet.Base.Always1,
+ Packet.Base.XSign, Packet.Base.YSign,
+ Packet.Base.XOverflow, Packet.Base.YOverflow,
+ Packet.XMovement, Packet.YMovement,
+ Packet.ZMovement.Z, Packet.ZMovement.Button4, Packet.ZMovement.Button5,
+ Packet.ZMovement.Always0, Packet.ZMovement.Always0_2);
+
+ int X, Y;
+ X = Packet.XMovement - (Packet.Base.XSign ? 256 : 0);
+ Y = Packet.YMovement - (Packet.Base.YSign ? 256 : 0);
+
+ if (Packet.Base.XOverflow)
+ X = 0;
+
+ if (Packet.Base.YOverflow)
+ Y = 0;
+
+ mir.Type = INPUT_TYPE_MOUSE;
+ mir.Device = MouseDevID;
+ mir.Mouse.LeftButton = Packet.Base.LeftButton;
+ mir.Mouse.RightButton = Packet.Base.RightButton;
+ mir.Mouse.MiddleButton = Packet.Base.MiddleButton;
+ mir.Mouse.Button4 = Packet.ZMovement.Button4;
+ mir.Mouse.Button5 = Packet.ZMovement.Button5;
+ mir.Mouse.X = X;
+ mir.Mouse.Y = -Y;
+ mir.Mouse.Z = Packet.ZMovement.Z;
+ v0::ReportInputEvent(DriverID, &mir);
+ PacketReady = false;
+ }
+
+ void MouseSampleRate(uint8_t SampleRate)
+ {
+ v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
+ v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_SET_SAMPLE_RATE);
+ v0::PS2ReadData(DriverID);
+
+ v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
+ v0::PS2WriteData(DriverID, SampleRate);
+ v0::PS2ReadData(DriverID);
+ }
+
+ int __fs_ms_Ioctl(struct Inode *, unsigned long, void *)
+ {
+ return 0;
+ }
+
+ const struct InodeOperations MouseOps = {
+ .Lookup = nullptr,
+ .Create = nullptr,
+ .Remove = nullptr,
+ .Rename = nullptr,
+ .Read = nullptr,
+ .Write = nullptr,
+ .Truncate = nullptr,
+ .Open = nullptr,
+ .Close = nullptr,
+ .Ioctl = __fs_ms_Ioctl,
+ .ReadDir = nullptr,
+ .MkDir = nullptr,
+ .RmDir = nullptr,
+ .SymLink = nullptr,
+ .ReadLink = nullptr,
+ .Seek = nullptr,
+ .Stat = nullptr,
+ };
+
+ int InitializeMouse()
+ {
+ v0::PS2WriteData(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
+ v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_RESET);
+ uint8_t test = v0::PS2ReadData(DriverID);
+ if (test != PS2_MOUSE_RESP_TEST_PASSED &&
+ test != PS2_MOUSE_RESP_ACK)
+ {
+ trace("PS/2 mouse reset failed! (%#x)", test);
+ return -EFAULT;
+ }
+
+ v0::RegisterInterruptHandler(DriverID, 12, (void *)PS2MouseInterruptHandler);
+
+ MouseDevID = v0::RegisterDevice(DriverID, INPUT_TYPE_MOUSE, &MouseOps);
+
+ v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
+ v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_SET_DEFAULTS);
+ v0::PS2ReadData(DriverID);
+
+ v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
+ v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_ENABLE_DATA_REPORTING);
+
+ MouseSampleRate(200);
+ MouseSampleRate(100);
+ MouseSampleRate(80);
+
+ v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
+ v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_READ_ID);
+ uint8_t Device2ID = v0::PS2ReadData(DriverID);
+ trace("PS/2 Mouse ID: %#x", Device2ID);
+
+ MouseSampleRate(200);
+ MouseSampleRate(200);
+ MouseSampleRate(80);
+
+ v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
+ v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_READ_ID);
+ Device2ID = v0::PS2ReadData(DriverID);
+ trace("PS/2 Mouse ID: %#x", Device2ID);
+
+ if (Device2ID >= 3 && Device2ID <= 4)
+ FourPackets = true;
+ if (Device2ID == 4)
+ MouseButton45 = true;
+
+ return 0;
+ }
+
+ int FinalizeMouse()
+ {
+ v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
+ v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_DISABLE_DATA_REPORTING);
+
+ v0::UnregisterDevice(DriverID, MouseDevID);
+ return 0;
+ }
+
+ int DetectPS2Mouse()
+ {
+ v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
+ v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_DISABLE_DATA_REPORTING);
+ if (v0::PS2ACKTimeout(DriverID) != 0)
+ trace("PS/2 mouse failed to disable data reporting!");
+
+ v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
+ v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_READ_ID);
+ if (v0::PS2ACKTimeout(DriverID) != 0)
+ trace("PS/2 mouse failed to read ID!");
+
+ uint8_t recByte;
+ int timeout = 1000000;
+ while (timeout--)
+ {
+ recByte = v0::PS2ReadData(DriverID);
+ if (recByte != PS2_ACK)
+ break;
+ }
+ Device2ID[0] = recByte;
+
+ timeout = 1000000;
+ while (timeout--)
+ {
+ recByte = v0::PS2ReadData(DriverID);
+ if (recByte != PS2_ACK)
+ break;
+ }
+ Device2ID[1] = recByte;
+
+ trace("PS2 Mouse Device: 0x%X 0x%X", Device2ID[0], Device2ID[1]);
+ return 0;
+ }
+}
diff --git a/Kernel/drivers/misc/aip/scancodes.c b/Kernel/drivers/misc/aip/scancodes.c
new file mode 100644
index 00000000..d4bf2c57
--- /dev/null
+++ b/Kernel/drivers/misc/aip/scancodes.c
@@ -0,0 +1,154 @@
+/*
+ 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 .
+*/
+
+#include
+#include
+
+const unsigned short ScanCodeSet1[] =
+ {KEY_NULL, KEY_ESCAPE,
+ KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0,
+ KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_TAB,
+ KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P,
+ KEY_LEFT_BRACKET, KEY_RIGHT_BRACKET, KEY_RETURN, KEY_LEFT_CTRL,
+ KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L,
+ KEY_SEMICOLON, KEY_APOSTROPHE, KEY_BACK_TICK, KEY_LEFT_SHIFT, KEY_BACKSLASH,
+ KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M,
+ KEY_COMMA, KEY_PERIOD, KEY_SLASH, KEY_RIGHT_SHIFT,
+ KEYPAD_ASTERISK, KEY_LEFT_ALT, KEY_SPACE, KEY_CAPS_LOCK,
+ KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,
+ KEY_NUM_LOCK, KEY_SCROLL_LOCK,
+ 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,
+ KEY_NULL, KEY_NULL, KEY_NULL,
+ KEY_F11, KEY_F12};
+
+const unsigned short ScanCodeSet1mm[] = {
+ [0x10] = KEY_MULTIMEDIA_PREV_TRACK,
+ [0x19] = KEY_MULTIMEDIA_NEXT_TRACK,
+ [0x1C] = KEYPAD_RETURN,
+ [0x1D] = KEY_RIGHT_CTRL,
+ [0x20] = KEY_MULTIMEDIA_MUTE,
+ [0x21] = KEY_MULTIMEDIA_CALCULATOR,
+ [0x22] = KEY_MULTIMEDIA_PLAY,
+ [0x24] = KEY_MULTIMEDIA_STOP,
+ [0x2A] = KEY_PRINT_SCREEN,
+ [0x2E] = KEY_MULTIMEDIA_VOL_DOWN,
+ [0x30] = KEY_MULTIMEDIA_VOL_UP,
+ [0x32] = KEY_MULTIMEDIA_WWW_HOME,
+ [0x35] = KEYPAD_SLASH,
+ [0x37] = KEY_PRINT_SCREEN,
+ [0x38] = KEY_RIGHT_ALT,
+ [0x47] = KEY_HOME,
+ [0x48] = KEY_UP_ARROW,
+ [0x49] = KEY_PAGE_UP,
+ [0x4B] = KEY_LEFT_ARROW,
+ [0x4D] = KEY_RIGHT_ARROW,
+ [0x4F] = KEY_END,
+ [0x50] = KEY_DOWN_ARROW,
+ [0x51] = KEY_PAGE_DOWN,
+ [0x52] = KEY_INSERT,
+ [0x53] = KEY_DELETE,
+ [0x5B] = KEY_LEFT_GUI,
+ [0x5C] = KEY_RIGHT_GUI,
+ [0x5D] = KEY_APPS,
+ [0x5E] = KEY_ACPI_POWER,
+ [0x5F] = KEY_ACPI_SLEEP,
+ [0x63] = KEY_ACPI_WAKE,
+ [0x65] = KEY_MULTIMEDIA_WWW_SEARCH,
+ [0x66] = KEY_MULTIMEDIA_WWW_FAVORITES,
+ [0x67] = KEY_MULTIMEDIA_WWW_REFRESH,
+ [0x68] = KEY_MULTIMEDIA_WWW_STOP,
+ [0x69] = KEY_MULTIMEDIA_WWW_FORWARD,
+ [0x6A] = KEY_MULTIMEDIA_WWW_BACK,
+ [0x6B] = KEY_MULTIMEDIA_MY_COMPUTER,
+ [0x6C] = KEY_MULTIMEDIA_EMAIL,
+ [0x6D] = KEY_MULTIMEDIA_MEDIA_SELECT,
+
+ /* RELEASED */
+
+ [0x90] = KEY_MULTIMEDIA_PREV_TRACK,
+ [0x99] = KEY_MULTIMEDIA_NEXT_TRACK,
+ [0x9C] = KEYPAD_RETURN,
+ [0x9D] = KEY_RIGHT_CTRL,
+ [0xA0] = KEY_MULTIMEDIA_MUTE,
+ [0xA1] = KEY_MULTIMEDIA_CALCULATOR,
+ [0xA2] = KEY_MULTIMEDIA_PLAY,
+ [0xA4] = KEY_MULTIMEDIA_STOP,
+ [0xAA] = KEY_PRINT_SCREEN,
+ [0xAE] = KEY_MULTIMEDIA_VOL_DOWN,
+ [0xB0] = KEY_MULTIMEDIA_VOL_UP,
+ [0xB2] = KEY_MULTIMEDIA_WWW_HOME,
+ [0xB5] = KEYPAD_SLASH,
+ [0xB7] = KEY_PRINT_SCREEN,
+ [0xB8] = KEY_RIGHT_ALT,
+ [0xC7] = KEY_HOME,
+ [0xC8] = KEY_UP_ARROW,
+ [0xC9] = KEY_PAGE_UP,
+ [0xCB] = KEY_LEFT_ARROW,
+ [0xCD] = KEY_RIGHT_ARROW,
+ [0xCF] = KEY_END,
+ [0xD0] = KEY_DOWN_ARROW,
+ [0xD1] = KEY_PAGE_DOWN,
+ [0xD2] = KEY_INSERT,
+ [0xD3] = KEY_DELETE,
+ [0xDB] = KEY_LEFT_GUI,
+ [0xDC] = KEY_RIGHT_GUI,
+ [0xDD] = KEY_APPS,
+ [0xDE] = KEY_ACPI_POWER,
+ [0xDF] = KEY_ACPI_SLEEP,
+ [0xE3] = KEY_ACPI_WAKE,
+ [0xE5] = KEY_MULTIMEDIA_WWW_SEARCH,
+ [0xE6] = KEY_MULTIMEDIA_WWW_FAVORITES,
+ [0xE7] = KEY_MULTIMEDIA_WWW_REFRESH,
+ [0xE8] = KEY_MULTIMEDIA_WWW_STOP,
+ [0xE9] = KEY_MULTIMEDIA_WWW_FORWARD,
+ [0xEA] = KEY_MULTIMEDIA_WWW_BACK,
+ [0xEB] = KEY_MULTIMEDIA_MY_COMPUTER,
+ [0xEC] = KEY_MULTIMEDIA_EMAIL,
+ [0xED] = KEY_MULTIMEDIA_MEDIA_SELECT};
+
+const unsigned short ScanCodeSet3[] = {
+ [0x15] = KEY_Q,
+ [0x1A] = KEY_Z,
+ [0x1B] = KEY_S,
+ [0x1C] = KEY_A,
+ [0x1D] = KEY_W,
+
+ [0x21] = KEY_C,
+ [0x22] = KEY_X,
+ [0x23] = KEY_D,
+ [0x24] = KEY_E,
+ [0x2A] = KEY_V,
+ [0x2B] = KEY_F,
+ [0x2C] = KEY_T,
+ [0x2D] = KEY_R,
+
+ [0x31] = KEY_N,
+ [0x32] = KEY_B,
+ [0x33] = KEY_H,
+ [0x34] = KEY_G,
+ [0x35] = KEY_Y,
+ [0x3A] = KEY_M,
+ [0x3B] = KEY_J,
+ [0x3C] = KEY_U,
+
+ [0x42] = KEY_K,
+ [0x43] = KEY_I,
+ [0x44] = KEY_O,
+ [0x4B] = KEY_L,
+ [0x4D] = KEY_P};
diff --git a/Kernel/drivers/misc/aip/uart.cpp b/Kernel/drivers/misc/aip/uart.cpp
new file mode 100644
index 00000000..868276ad
--- /dev/null
+++ b/Kernel/drivers/misc/aip/uart.cpp
@@ -0,0 +1,647 @@
+/*
+ This file is part of Fennix Drivers.
+
+ Fennix Drivers is free software: you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ Fennix Drivers is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Fennix Drivers. If not, see .
+*/
+
+#include
+#include
+
+#include "aip.hpp"
+
+namespace Driver::AdvancedIntegratedPeripheral
+{
+ extern dev_t DriverID;
+
+#define SERIAL_ENABLE_DLAB 0x80
+#define SERIAL_BUFFER_EMPTY 0x20
+
+ enum Ports
+ {
+ COM1 = 0x3F8,
+ COM2 = 0x2F8,
+ COM3 = 0x3E8,
+ COM4 = 0x2E8,
+ COM5 = 0x5F8,
+ COM6 = 0x4F8,
+ COM7 = 0x5E8,
+ COM8 = 0x4E8,
+
+ LPT1 = 0x378,
+ LPT2 = 0x278,
+ LPT3 = 0x3BC
+ };
+
+ enum SerialSpeed
+ {
+ RATE_50_HI = 0x09,
+ RATE_50_LO = 0x00,
+
+ RATE_300_HI = 0x01,
+ RATE_300_LO = 0x80,
+
+ RATE_600_HI = 0x00,
+ RATE_600_LO = 0xC0,
+
+ RATE_2400_HI = 0x00,
+ RATE_2400_LO = 0x30,
+
+ RATE_4800_HI = 0x00,
+ RATE_4800_LO = 0x18,
+
+ RATE_9600_HI = 0x00,
+ RATE_9600_LO = 0x0C,
+
+ RATE_19200_HI = 0x00,
+ RATE_19200_LO = 0x06,
+
+ RATE_38400_HI = 0x00,
+ RATE_38400_LO = 0x03,
+
+ RATE_57600_HI = 0x00,
+ RATE_57600_LO = 0x02,
+
+ RATE_115200_HI = 0x00,
+ RATE_115200_LO = 0x01
+ };
+
+ /*
+ . Table of Registers .
+ /---------------------------------------------------------------------\
+ | Base Address | DLAB | R/W | Abr | Register Name |
+ |---------------------------------------------------------------------|
+ | +0 | =0 | W | - | Transmitter Holding Buffer |
+ | | =0 | R | - | Receiver Buffer |
+ | | =1 | R/W | - | Divisor Latch Low Byte |
+ | +1 | =0 | R/W | IER | Interrupt Enable Register |
+ | | =1 | R/W | - | Divisor Latch High Byte |
+ | +2 | - | R | IIR | Interrupt Identification Register |
+ | | - | W | FCR | FIFO Control Register |
+ | +3 | - | R/W | LCR | Line Control Register |
+ | +4 | - | R/W | MCR | Modem Control Register |
+ | +5 | - | R | LSR | Line Status Register |
+ | +6 | - | R | MSR | Modem Status Register |
+ | +7 | - | R/W | - | Scratch Register |
+ \---------------------------------------------------------------------/
+
+ Source:
+ Interfacing the Serial / RS232 Port V5.0
+ Table 5 : Table of Registers
+ */
+
+ /** Interrupt Enable Register */
+ typedef union
+ {
+ struct
+ {
+ /* Enable Received Data Available Interrupt */
+ uint8_t InterruptOnReceive : 1;
+
+ /* Enable Transmitter Holding Register Empty Interrupt */
+ uint8_t InterruptOnTransmitter : 1;
+
+ /* Enable Receiver Line Status Interrupt */
+ uint8_t LineStatusInterrupt : 1;
+
+ /* Enable Modem Status Interrupt */
+ uint8_t ModemStatusInterrupt : 1;
+
+ /* Enables Sleep Mode (16750) */
+ uint8_t SleepMode : 1;
+
+ /* Enables Low Power Mode (16750) */
+ uint8_t LowPowerMode : 1;
+
+ /* Reserved */
+ uint8_t __reserved : 2;
+ };
+ uint8_t raw;
+ } IER;
+
+ /** Interrupt Identification Register */
+ typedef union
+ {
+ struct
+ {
+ /* Interrupt pending */
+ uint8_t InterruptPending : 1;
+
+ /**
+ * Interrupt Status
+ *
+ * 00b = Modem Status Interrupt
+ * 01b = Transmitter Holding Register Empty Interrupt
+ * 10b = Received Data Available Interrupt
+ * 11b = Receiver Line Status Interrupt
+ */
+ uint8_t InterruptStatus : 2;
+
+ /**
+ * 16550 Time-out Interrupt Pending
+ *
+ * @note Reserved on 8250, 16450
+ */
+ uint8_t TimeOutIP : 1;
+
+ /** Reserved */
+ uint8_t __reserved : 1;
+
+ /** 64 Byte Fifo Enabled (16750 only) */
+ uint8_t FIFO64 : 1;
+
+ /**
+ * Enable FIFO
+ *
+ * 00b = No FIFO
+ * 01b = FIFO Enabled but Unusable
+ * 11b = FIFO Enabled
+ */
+ uint8_t FIFO : 2;
+ };
+ uint8_t raw;
+ } IIR;
+
+ /** First In / First Out Control Register */
+ typedef union
+ {
+ struct
+ {
+ /** Enable FIFO's */
+ uint8_t FIFO : 1;
+
+ /** Clear Receive FIFO */
+ uint8_t ClearRX : 1;
+
+ /** Clear Transmit FIFO */
+ uint8_t ClearTX : 1;
+
+ /** DMA Mode Select.
+ *
+ * Change status of RXRDY & TXRDY pins from mode 1 to mode 2.
+ */
+ uint8_t DMAMode : 1;
+
+ /** Reserved */
+ uint8_t __reserved : 1;
+
+ /** Enable 64 Byte FIFO (16750 only) */
+ uint8_t FIFO64 : 1;
+
+ /** Interrupt Trigger Level
+ *
+ * 00b = 1 Byte
+ * 01b = 4 Bytes
+ * 10b = 8 Bytes
+ * 11b = 14 Bytes
+ */
+ uint8_t TriggerLevel : 2;
+ };
+ uint8_t raw;
+ } FCR;
+
+ /** Line Control Register */
+ typedef union
+ {
+ struct
+ {
+ /** Word Length
+ *
+ * 00b = 5 bits
+ * 01b = 6 bits
+ * 10b = 7 bits
+ * 11b = 8 bits
+ */
+ uint8_t WordLength : 2;
+
+ /** Length of Stop Bit
+ *
+ * 0b = One Stop Bit
+ * 1b = 2 Stop bits for words of length 6,7 or 8 bits or 1.5 Stop Bits for Word lengths of 5 bits.
+ */
+ uint8_t StopBit : 1;
+
+ /** Parity Select
+ *
+ * 0b = No Parity
+ * 001b = Odd Parity
+ * 011b = Even Parity
+ * 101b = High Parity (Sticky)
+ * 111b = Low Parity (Sticky)
+ */
+ uint8_t Parity : 3;
+
+ /** Set Break Enable */
+ uint8_t SetBreak : 1;
+
+ /**
+ * Divisor Latch Access
+ *
+ * 0b = Access to Receiver buffer, Transmitter buffer & Interrupt Enable Register
+ * 1b = Divisor Latch Access Bit
+ */
+ uint8_t DLAB : 1;
+ };
+ uint8_t raw;
+ } LCR;
+
+ /** Modem Control Register */
+ typedef union
+ {
+ struct
+ {
+ /** Force Data Terminal Ready */
+ uint8_t DataTerminalReady : 1;
+
+ /** Force Request to Send */
+ uint8_t RequestToSend : 1;
+
+ /** Auxiliary Output 1 */
+ uint8_t Out1 : 1;
+
+ /** Auxiliary Output 2 */
+ uint8_t Out2 : 1;
+
+ /** Loopback Mode */
+ uint8_t Loopback : 1;
+
+ /** Autoflow Control Enabled (16750 only) */
+ uint8_t Autoflow : 1;
+
+ /** Reserved */
+ uint8_t __reserved : 2;
+ };
+ uint8_t raw;
+ } MCR;
+
+ /** Line Status Register */
+ typedef union
+ {
+ struct
+ {
+ /** Data Ready */
+ uint8_t DataReady : 1;
+
+ /** Overrun Error */
+ uint8_t OverrunError : 1;
+
+ /** Parity Error */
+ uint8_t ParityError : 1;
+
+ /** Framing Error */
+ uint8_t FramingError : 1;
+
+ /** Break Interrupt */
+ uint8_t BreakInterrupt : 1;
+
+ /** Empty Transmitter Holding Register */
+ uint8_t EmptyTransmitterHolding : 1;
+
+ /** Empty Data Holding Registers */
+ uint8_t EmptyDataHolding : 1;
+
+ /** Error in Received FIFO */
+ uint8_t ErrorReceivedFIFO : 1;
+ };
+ uint8_t raw;
+ } LSR;
+
+ /** Modem Status Register */
+ typedef union
+ {
+ struct
+ {
+ /** Delta Clear to Send */
+ uint8_t DeltaClearToSend : 1;
+
+ /** Delta Data Set Ready */
+ uint8_t DeltaDataSetReady : 1;
+
+ /** Trailing Edge Ring Indicator */
+ uint8_t TrailingEdgeRingIndicator : 1;
+
+ /** Delta Data Carrier Detect */
+ uint8_t DeltaDataCarrierDetect : 1;
+
+ /** Clear To Send */
+ uint8_t ClearToSend : 1;
+
+ /** Data Set Ready */
+ uint8_t DataSetReady : 1;
+
+ /** Ring Indicator */
+ uint8_t RingIndicator : 1;
+
+ /** Carrier Detect */
+ uint8_t CarrierDetect : 1;
+ };
+ uint8_t raw;
+ } MSR;
+
+ union UARTs
+ {
+ struct
+ {
+ uint8_t com1 : 1;
+ uint8_t com2 : 1;
+ uint8_t com3 : 1;
+ uint8_t com4 : 1;
+ uint8_t com5 : 1;
+ uint8_t com6 : 1;
+ uint8_t com7 : 1;
+ uint8_t com8 : 1;
+
+ uint8_t lpt1 : 1;
+ uint8_t lpt2 : 1;
+ uint8_t lpt3 : 1;
+
+ uint8_t __reserved : 5;
+ };
+ uint16_t raw;
+ } uart;
+
+ bool IsDataReady(uint16_t Port)
+ {
+ LSR lsr;
+ lsr.raw = inb(Port + 5);
+ return lsr.DataReady;
+ }
+
+ bool IsTransmitEmpty(uint16_t Port)
+ {
+ LSR lsr;
+ lsr.raw = inb(Port + 5);
+ return lsr.EmptyTransmitterHolding;
+ }
+
+ char ReadSerial(uint16_t Port)
+ {
+ while (!IsDataReady(Port))
+ v0::Yield(DriverID);
+ return inb(Port);
+ }
+
+ void WriteSerial(uint16_t Port, char Character)
+ {
+ while (!IsTransmitEmpty(Port))
+ v0::Yield(DriverID);
+ outb(Port, Character);
+ }
+
+ void ReportSerialReceived(uint8_t Data)
+ {
+ debug("%c", Data);
+ }
+
+ void UartCOM24(CPU::TrapFrame *)
+ {
+ LSR lsr2, lsr4;
+ do
+ {
+ lsr2.raw = inb(COM2 + 5);
+ if (lsr2.DataReady)
+ ReportSerialReceived(inb(COM2));
+ lsr4.raw = inb(COM4 + 5);
+ if (lsr4.DataReady)
+ ReportSerialReceived(inb(COM4));
+ } while (lsr2.DataReady || lsr4.DataReady);
+ }
+
+ void UartCOM13(CPU::TrapFrame *)
+ {
+ LSR lsr1, lsr3;
+ do
+ {
+ lsr1.raw = inb(COM1 + 5);
+ if (lsr1.DataReady)
+ ReportSerialReceived(inb(COM1));
+ lsr3.raw = inb(COM3 + 5);
+ if (lsr3.DataReady)
+ ReportSerialReceived(inb(COM3));
+ } while (lsr1.DataReady || lsr3.DataReady);
+ }
+
+ bool InitializePort(uint16_t Port)
+ {
+ v0::CriticalState cs = v0::EnterCriticalSection(DriverID);
+ LCR lcr = {};
+ IER ier = {};
+ FCR fcr = {};
+ MCR mcr = {};
+
+ outb(Port + 3, lcr.raw);
+ outb(Port + 1, ier.raw);
+
+ lcr.DLAB = 1;
+ outb(Port + 3, lcr.raw);
+
+ outb(Port + 0, RATE_115200_LO);
+ outb(Port + 1, RATE_115200_HI);
+
+ lcr.DLAB = 0;
+ lcr.WordLength = 0b11;
+ outb(Port + 3, lcr.raw);
+
+ fcr.FIFO = 1;
+ fcr.ClearRX = 1;
+ fcr.ClearTX = 1;
+ fcr.TriggerLevel = 0b11;
+ outb(Port + 2, fcr.raw);
+
+ mcr.DataTerminalReady = 1;
+ mcr.RequestToSend = 1;
+ mcr.Out2 = 1;
+ mcr.Loopback = 1;
+ outb(Port + 4, mcr.raw);
+
+ /* Test the serial port */
+ outb(Port + 0, 0x48);
+ uint8_t result = inb(Port + 0);
+ if (result != 0x48)
+ {
+ /* FIXME: DETECT BAUD RATE
+ Do multiple test to check if the output is garbage.
+ If so, reduce the baud rate until it works. */
+
+ v0::LeaveCriticalSection(DriverID, cs);
+ trace("Port %#X test failed!", Port);
+ return false;
+ }
+
+ /* Set normal operation mode */
+ mcr.DataTerminalReady = 1;
+ mcr.RequestToSend = 1;
+ mcr.Out1 = 1;
+ mcr.Out2 = 1;
+ mcr.Loopback = 0;
+ outb(Port + 4, mcr.raw);
+
+ /* Enable interrupts on receive */
+ ier.InterruptOnReceive = 1;
+ outb(Port + 1, ier.raw);
+ v0::RegisterInterruptHandler(DriverID, 3, (void *)UartCOM24);
+ v0::RegisterInterruptHandler(DriverID, 4, (void *)UartCOM13);
+
+ v0::LeaveCriticalSection(DriverID, cs);
+ trace("Port %#X initialized", Port);
+ return true;
+ }
+
+ int DetectUART()
+ {
+ uart.com1 = inb(COM1) != 0xFF ? true : false;
+ uart.com2 = inb(COM2) != 0xFF ? true : false;
+ uart.com3 = inb(COM3) != 0xFF ? true : false;
+ uart.com4 = inb(COM4) != 0xFF ? true : false;
+ uart.com5 = inb(COM5) != 0xFF ? true : false;
+ uart.com6 = inb(COM6) != 0xFF ? true : false;
+ uart.com7 = inb(COM7) != 0xFF ? true : false;
+ uart.com8 = inb(COM8) != 0xFF ? true : false;
+
+ uart.lpt1 = inb(LPT1) != 0xFF ? true : false;
+ uart.lpt2 = inb(LPT2) != 0xFF ? true : false;
+ uart.lpt3 = inb(LPT3) != 0xFF ? true : false;
+
+ if (uart.com1 == true)
+ if (InitializePort(COM1) == false)
+ uart.com1 = false;
+
+ if (uart.com2 == true)
+ if (InitializePort(COM2) == false)
+ uart.com1 = false;
+
+ if (uart.com3 == true)
+ if (InitializePort(COM3) == false)
+ uart.com1 = false;
+
+ if (uart.com4 == true)
+ if (InitializePort(COM4) == false)
+ uart.com1 = false;
+
+ if (uart.com5 == true)
+ if (InitializePort(COM5) == false)
+ uart.com1 = false;
+
+ if (uart.com6 == true)
+ if (InitializePort(COM6) == false)
+ uart.com1 = false;
+
+ if (uart.com7 == true)
+ if (InitializePort(COM7) == false)
+ uart.com1 = false;
+
+ if (uart.com8 == true)
+ if (InitializePort(COM8) == false)
+ uart.com1 = false;
+
+ if (uart.lpt1 == true)
+ trace("LPT1 is present");
+
+ if (uart.lpt2 == true)
+ trace("LPT2 is present");
+
+ if (uart.lpt3 == true)
+ trace("LPT3 is present");
+ return 0;
+ }
+
+ // static int once = 0;
+ // static uint8_t com4 = 0xFF;
+ // if (!once++)
+ // com4 = inb(0x2E8);
+ // if (com4 == 0xFF)
+ // CPU::Halt(true);
+ // char UserInputBuffer[256]{'\0'};
+ // int BackSpaceLimit = 0;
+ // while (true)
+ // {
+ // while ((inb(0x2E8 + 5) & 1) == 0)
+ // CPU::Pause();
+ // char key = inb(0x2E8);
+ // // debug("key: %d", key);
+ // if (key == '\x7f') /* Backspace (DEL) */
+ // {
+ // if (BackSpaceLimit <= 0)
+ // continue;
+ // char keyBuf[5] = {'\b', '\x1b', '[', 'K', '\0'};
+ // ExPrint(keyBuf);
+ // backspace(UserInputBuffer);
+ // BackSpaceLimit--;
+ // continue;
+ // }
+ // else if (key == '\x0d') /* Enter (CR) */
+ // {
+ // UserInput(UserInputBuffer);
+ // BackSpaceLimit = 0;
+ // UserInputBuffer[0] = '\0';
+ // continue;
+ // }
+ // else if (key == '\x1b') /* Escape */
+ // {
+ // char tmp[16]{'\0'};
+ // append(tmp, key);
+ // while ((inb(0x2E8 + 5) & 1) == 0)
+ // CPU::Pause();
+ // char key = inb(0x2E8);
+ // append(tmp, key);
+ // if (key == '[')
+ // {
+ // // 27 91
+ // // < 68
+ // // > 67
+ // // down 66
+ // // up 65
+ // while ((inb(0x2E8 + 5) & 1) == 0)
+ // CPU::Pause();
+ // key = inb(0x2E8);
+ // append(tmp, key);
+ // switch (key)
+ // {
+ // case 'A':
+ // key = KEY_D_UP;
+ // break;
+ // case 'B':
+ // key = KEY_D_DOWN;
+ // break;
+ // case 'C':
+ // key = KEY_D_RIGHT;
+ // break;
+ // case 'D':
+ // key = KEY_D_LEFT;
+ // break;
+ // default:
+ // {
+ // for (size_t i = 0; i < strlen(tmp); i++)
+ // {
+ // if ((int)sizeof(UserInputBuffer) <= BackSpaceLimit)
+ // continue;
+ // append(UserInputBuffer, tmp[i]);
+ // BackSpaceLimit++;
+ // char keyBuf[2] = {(char)tmp[i], '\0'};
+ // ExPrint(keyBuf);
+ // }
+ // continue;
+ // }
+ // }
+ // ArrowInput(key);
+ // continue;
+ // }
+ // }
+ // if ((int)sizeof(UserInputBuffer) <= BackSpaceLimit)
+ // continue;
+ // append(UserInputBuffer, key);
+ // BackSpaceLimit++;
+ // char keyBuf[2] = {(char)key, '\0'};
+ // ExPrint(keyBuf);
+ // }
+}
diff --git a/Kernel/drivers/misc/mem/mem.cpp b/Kernel/drivers/misc/mem/mem.cpp
new file mode 100644
index 00000000..5c452b19
--- /dev/null
+++ b/Kernel/drivers/misc/mem/mem.cpp
@@ -0,0 +1,164 @@
+/*
+ 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 .
+*/
+
+#include
+#include
+
+extern Driver::Manager *DriverManager;
+namespace Driver::MemoryDevices
+{
+ dev_t DriverID;
+
+ struct
+ {
+ dev_t null;
+ dev_t zero;
+ dev_t random;
+ dev_t urandom;
+ dev_t mem;
+ } ids;
+
+ int Open(struct Inode *Node, int Flags, mode_t Mode) { return -ENOENT; }
+
+ int Close(struct Inode *Node) { return -ENOSYS; }
+
+ int Ioctl(struct Inode *Node, unsigned long Request, void *Argp) { return -ENOSYS; }
+
+ ssize_t Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset)
+ {
+ dev_t min = Node->GetMinor();
+ if (min == ids.null)
+ return 0;
+ else if (min == ids.zero)
+ {
+ if (Size <= 0)
+ return 0;
+
+ memset(Buffer, 0, Size);
+ return Size;
+ }
+ else if (min == ids.random || min == ids.urandom)
+ {
+ 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;
+ }
+ else if (min == ids.mem)
+ {
+ stub;
+ return 0;
+ }
+
+ return -ENODEV;
+ }
+
+ ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset)
+ {
+ dev_t min = Node->GetMinor();
+ if (min == ids.null)
+ return Size;
+ else if (min == ids.zero)
+ return Size;
+ else if (min == ids.random || min == ids.urandom)
+ return Size;
+ else if (min == ids.mem)
+ return Size;
+
+ return -ENODEV;
+ }
+
+ off_t Seek(struct Inode *Node, off_t Offset) { return -ENOSYS; }
+
+ int Stat(struct Inode *Node, struct kstat *Stat) { return -ENOSYS; }
+
+ const struct InodeOperations ops = {
+ .Lookup = nullptr,
+ .Create = nullptr,
+ .Remove = nullptr,
+ .Rename = nullptr,
+ .Read = Read,
+ .Write = Write,
+ .Truncate = nullptr,
+ .Open = Open,
+ .Close = Close,
+ .Ioctl = Ioctl,
+ .ReadDir = nullptr,
+ .MkDir = nullptr,
+ .RmDir = nullptr,
+ .SymLink = nullptr,
+ .ReadLink = nullptr,
+ .Seek = Seek,
+ .Stat = Stat,
+ };
+
+ int Entry()
+ {
+ mode_t mode = 0;
+
+ /* c rw- rw- rw- */
+ mode = S_IRUSR | S_IWUSR |
+ S_IRGRP | S_IWGRP |
+ S_IROTH | S_IWOTH |
+ S_IFCHR;
+ ids.null = DriverManager->CreateDeviceFile(DriverID, "null", mode, &ops);
+ ids.zero = DriverManager->CreateDeviceFile(DriverID, "zero", mode, &ops);
+ ids.random = DriverManager->CreateDeviceFile(DriverID, "random", mode, &ops);
+ ids.urandom = DriverManager->CreateDeviceFile(DriverID, "urandom", mode, &ops);
+
+ /* c rw- r-- --- */
+ mode = S_IRUSR | S_IWUSR |
+ S_IRGRP |
+
+ S_IFCHR;
+ ids.mem = DriverManager->CreateDeviceFile(DriverID, "mem", mode, &ops);
+ return 0;
+ }
+
+ int Final()
+ {
+ DriverManager->UnregisterDevice(DriverID, ids.null);
+ DriverManager->UnregisterDevice(DriverID, ids.zero);
+ DriverManager->UnregisterDevice(DriverID, ids.random);
+ DriverManager->UnregisterDevice(DriverID, ids.urandom);
+ DriverManager->UnregisterDevice(DriverID, ids.mem);
+ return 0;
+ }
+
+ int Panic() { return 0; }
+ int Probe() { return 0; }
+
+ REGISTER_BUILTIN_DRIVER(mem,
+ "Memory Devices Driver",
+ "enderice2",
+ 1, 0, 0,
+ Entry,
+ Final,
+ Panic,
+ Probe);
+}
diff --git a/Kernel/drivers/misc/pty/pty.cpp b/Kernel/drivers/misc/pty/pty.cpp
new file mode 100644
index 00000000..dcbe9552
--- /dev/null
+++ b/Kernel/drivers/misc/pty/pty.cpp
@@ -0,0 +1,192 @@
+/*
+ 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 .
+*/
+
+#include
+#include
+#include
+#include
+
+extern Driver::Manager *DriverManager;
+namespace Driver::TeleTypeDevices
+{
+ dev_t DriverID;
+ TTY::PTMXDevice *ptmx = nullptr;
+
+ struct
+ {
+ dev_t kcon;
+ dev_t tty;
+ dev_t ptmx;
+ } ids;
+
+ int Open(struct Inode *Node, int Flags, mode_t Mode)
+ {
+ dev_t min = Node->GetMinor();
+ if (min == ids.kcon)
+ return KernelConsole::CurrentTerminal.load()->Open(Flags, Mode);
+ else if (min == ids.tty)
+ {
+ TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
+ if (tty == nullptr)
+ return -ENOTTY;
+ return tty->Open(Flags, Mode);
+ }
+ else if (min == ids.ptmx)
+ return ptmx->Open();
+
+ return -ENODEV;
+ }
+
+ int Close(struct Inode *Node)
+ {
+ dev_t min = Node->GetMinor();
+ if (min == ids.kcon)
+ return KernelConsole::CurrentTerminal.load()->Close();
+ else if (min == ids.tty)
+ {
+ TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
+ if (tty == nullptr)
+ return -ENOTTY;
+ return tty->Close();
+ }
+ else if (min == ids.ptmx)
+ return ptmx->Close();
+
+ return -ENODEV;
+ }
+
+ int Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
+ {
+ dev_t min = Node->GetMinor();
+ if (min == ids.kcon)
+ return KernelConsole::CurrentTerminal.load()->Ioctl(Request, Argp);
+ else if (min == ids.tty)
+ {
+ TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
+ if (tty == nullptr)
+ return -ENOTTY;
+ return tty->Ioctl(Request, Argp);
+ }
+ else if (min == ids.ptmx)
+ return -ENOSYS;
+
+ return -ENODEV;
+ }
+
+ ssize_t Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset)
+ {
+ dev_t min = Node->GetMinor();
+ if (min == ids.kcon)
+ return KernelConsole::CurrentTerminal.load()->Read(Buffer, Size, Offset);
+ else if (min == ids.tty)
+ {
+ TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
+ if (tty == nullptr)
+ return -ENOTTY;
+ return tty->Read(Buffer, Size, Offset);
+ }
+ else if (min == ids.ptmx)
+ return -ENOSYS;
+
+ return -ENODEV;
+ }
+
+ ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset)
+ {
+ dev_t min = Node->GetMinor();
+ if (min == ids.kcon)
+ return KernelConsole::CurrentTerminal.load()->Write(Buffer, Size, Offset);
+ else if (min == ids.tty)
+ {
+ TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
+ if (tty == nullptr)
+ return -ENOTTY;
+ return tty->Write(Buffer, Size, Offset);
+ }
+ else if (min == ids.ptmx)
+ return -ENOSYS;
+
+ return -ENODEV;
+ }
+
+ off_t Seek(struct Inode *Node, off_t Offset) { return -ENOSYS; }
+ int Stat(struct Inode *Node, struct kstat *Stat) { return -ENOSYS; }
+
+ const struct InodeOperations ops = {
+ .Lookup = nullptr,
+ .Create = nullptr,
+ .Remove = nullptr,
+ .Rename = nullptr,
+ .Read = Read,
+ .Write = Write,
+ .Truncate = nullptr,
+ .Open = Open,
+ .Close = Close,
+ .Ioctl = Ioctl,
+ .ReadDir = nullptr,
+ .MkDir = nullptr,
+ .RmDir = nullptr,
+ .SymLink = nullptr,
+ .ReadLink = nullptr,
+ .Seek = Seek,
+ .Stat = Stat,
+ };
+
+ int Entry()
+ {
+ ptmx = new TTY::PTMXDevice;
+ mode_t mode = 0;
+
+ /* c rw- r-- --- */
+ mode = S_IRUSR | S_IWUSR |
+ S_IRGRP |
+
+ S_IFCHR;
+ ids.kcon = DriverManager->CreateDeviceFile(DriverID, "kcon", mode, &ops);
+
+ /* c rw- rw- rw- */
+ mode = S_IRUSR | S_IWUSR |
+ S_IRGRP | S_IWGRP |
+ S_IRUSR | S_IWUSR |
+ S_IFCHR;
+ ids.tty = DriverManager->CreateDeviceFile(DriverID, "tty", mode, &ops);
+ ids.ptmx = DriverManager->CreateDeviceFile(DriverID, "ptmx", mode, &ops);
+
+ return 0;
+ }
+
+ int Final()
+ {
+ DriverManager->UnregisterDevice(DriverID, ids.kcon);
+ DriverManager->UnregisterDevice(DriverID, ids.tty);
+ DriverManager->UnregisterDevice(DriverID, ids.ptmx);
+ delete ptmx;
+ return 0;
+ }
+
+ int Panic() { return 0; }
+ int Probe() { return 0; }
+
+ REGISTER_BUILTIN_DRIVER(pty,
+ "Pseudo Terminal Devices Driver",
+ "enderice2",
+ 1, 0, 0,
+ Entry,
+ Final,
+ Panic,
+ Probe);
+}
diff --git a/Kernel/drivers/misc/vmware/vmware.cpp b/Kernel/drivers/misc/vmware/vmware.cpp
new file mode 100644
index 00000000..487a8191
--- /dev/null
+++ b/Kernel/drivers/misc/vmware/vmware.cpp
@@ -0,0 +1,917 @@
+/*
+ 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 .
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+extern Driver::Manager *DriverManager;
+extern PCI::Manager *PCIManager;
+namespace Driver::VMwareToolBox
+{
+ dev_t DriverID;
+
+ enum RPCMessages
+ {
+ MSG_OPEN,
+ MSG_SENDSIZE,
+ MSG_SENDPAYLOAD,
+ MSG_RECVSIZE,
+ MSG_RECVPAYLOAD,
+ MSG_RECVSTATUS,
+ MSG_CLOSE,
+ };
+
+ enum RPCStatus
+ {
+ STATUS_SUCCESS = 0x1,
+ STATUS_DORECV = 0x2,
+ STATUS_CPT = 0x10,
+ STATUS_HB = 0x80,
+ };
+
+ typedef struct
+ {
+ union
+ {
+ uint32_t ax;
+ uint32_t magic;
+ };
+ union
+ {
+ uint32_t bx;
+ size_t size;
+ };
+ union
+ {
+ uint32_t cx;
+ uint16_t command;
+ };
+ union
+ {
+ uint32_t dx;
+ uint16_t port;
+ };
+ uint32_t si;
+ uint32_t di;
+ } VMwareCommand;
+
+#define VMWARE_MAGIC 0x564D5868
+
+#define VMWARE_PORT 0x5658
+#define VMWARE_PORTHB 0x5659
+
+#define VMWARE_HYPERVISOR_HB 0x00000000
+#define VMWARE_HYPERVISOR_OUT 0x00000001
+
+#define CMD_GETVERSION 0xA
+#define CMD_MESSAGE 0x1E
+#define CMD_ABSPOINTER_DATA 0x27
+#define CMD_ABSPOINTER_STATUS 0x28
+#define CMD_ABSPOINTER_COMMAND 0x29
+
+#define ABSPOINTER_ENABLE 0x45414552
+#define ABSPOINTER_RELATIVE 0xF5
+#define ABSPOINTER_ABSOLUTE 0x53424152
+
+#define MESSAGE_RPCI 0x49435052
+#define MESSAGE_TCLO 0x4f4c4354
+
+#define FLAG_COOKIE 0x80000000
+
+#define ToMsg(x) ((x) << 16 | CMD_MESSAGE)
+#define HighWord(x) ((x & 0xFFFF0000) >> 16)
+
+#define MESSAGE_HB_MSG 0
+
+#define MESSAGE_OPEN_CHANNEL ToMsg(MSG_OPEN)
+#define MESSAGE_CLOSE_CHANNEL ToMsg(MSG_CLOSE)
+
+#define MESSAGE_SEND_SIZE ToMsg(MSG_SENDSIZE)
+#define MESSAGE_SEND_PAYLOAD ToMsg(MSG_SENDPAYLOAD)
+
+#define MESSAGE_RECV_SIZE ToMsg(MSG_RECVSIZE)
+#define MESSAGE_RECV_PAYLOAD ToMsg(MSG_RECVPAYLOAD)
+#define MESSAGE_RECV_STATUS ToMsg(MSG_RECVSTATUS)
+
+#if defined(__amd64__)
+
+#define VM_PORT(cmd, in_ebx, isi, idi, \
+ flags, magic, \
+ ax, bx, cx, dx, si, di) \
+ __asm__ __volatile__("movw $0x5658, %%dx\n" \
+ "inl %%dx, %%eax\n" \
+ : "=a"(ax), \
+ "=b"(bx), \
+ "=c"(cx), \
+ "=d"(dx), \
+ "=S"(si), \
+ "=D"(di) \
+ : "a"(magic), \
+ "b"(in_ebx), \
+ "c"(cmd), \
+ "d"(flags), \
+ "S"(isi), \
+ "D"(idi) : "memory")
+
+#define VM_PORT_HB_OUT(cmd, in_ecx, isi, idi, \
+ flags, magic, bp, \
+ ax, bx, cx, dx, si, di) \
+ __asm__ __volatile__("push %%rbp\n" \
+ "mov %12, %%rbp\n" \
+ "movw $0x5659, %%dx\n" \
+ "rep outsb\n" \
+ "pop %%rbp\n" \
+ : "=a"(ax), \
+ "=b"(bx), \
+ "=c"(cx), \
+ "=d"(dx), \
+ "=S"(si), \
+ "=D"(di) \
+ : "a"(magic), \
+ "b"(cmd), \
+ "c"(in_ecx), \
+ "d"(flags), \
+ "S"(isi), \
+ "D"(idi), \
+ "r"(bp) : "memory", "cc")
+
+#define VM_PORT_HB_IN(cmd, in_ecx, isi, idi, \
+ flags, magic, bp, \
+ ax, bx, cx, dx, si, di) \
+ __asm__ __volatile__("push %%rbp\n" \
+ "mov %12, %%rbp\n" \
+ "movw $0x5659, %%dx\n" \
+ "rep insb\n" \
+ "pop %%rbp\n" \
+ : "=a"(ax), \
+ "=b"(bx), \
+ "=c"(cx), \
+ "=d"(dx), \
+ "=S"(si), \
+ "=D"(di) \
+ : "a"(magic), \
+ "b"(cmd), \
+ "c"(in_ecx), \
+ "d"(flags), \
+ "S"(isi), \
+ "D"(idi), \
+ "r"(bp) : "memory", "cc")
+
+#elif defined(__i386__)
+
+#define VM_PORT(cmd, in_ebx, isi, idi, \
+ flags, magic, \
+ ax, bx, cx, dx, si, di)
+
+#define VM_PORT_HB_OUT(cmd, in_ecx, isi, idi, \
+ flags, \
+ magic, bp, ax, \
+ bx, cx, dx, si, di)
+
+#define VM_PORT_HB_IN(cmd, in_ecx, isi, idi, \
+ flags, magic, bp, \
+ ax, bx, cx, dx, si, di)
+
+#endif
+
+ /* TODO:
+ - use vmcall or vmmcall instead of "out" and "in" if available
+ */
+
+ typedef struct
+ {
+ int TCLOChannel;
+ uint16_t ChannelID;
+ uint32_t CookieHigh;
+ uint32_t CookieLow;
+ } ToolboxContext;
+
+ dev_t MouseDevID = -1;
+
+ int __strcmp(const char *l, const char *r)
+ {
+ for (; *l == *r && *l; l++, r++)
+ ;
+
+ return *(unsigned char *)l - *(unsigned char *)r;
+ }
+
+ void __cpuid(uint32_t Function,
+ uint32_t *eax, uint32_t *ebx,
+ uint32_t *ecx, uint32_t *edx)
+ {
+ asmv("cpuid" : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) : "a"(Function));
+ }
+
+ bool __CheckHypervisorBit()
+ {
+ uint32_t eax, ebx, ecx, edx;
+ __cpuid(0x1, &eax, &ebx, &ecx, &edx);
+ if (!(ecx & (1 << 31)))
+ return false; /* Hypervisor not detected */
+ return true;
+ }
+
+ bool __VMwareBackdoorHypervisors()
+ {
+ const char hv[13] = {0};
+ uint32_t eax, ebx, ecx, edx;
+ __cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
+
+ *(uint32_t *)hv = ebx;
+ *(uint32_t *)(hv + 4) = ecx;
+ *(uint32_t *)(hv + 8) = edx;
+
+ if (__strcmp(hv, "VMwareVMware") != 0 &&
+ __strcmp(hv, "KVMKVMKVM") != 0 &&
+ __strcmp(hv, "TCGTCGTCGTCG") != 0)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ bool IsVMwareBackdoorAvailable()
+ {
+ if (!__CheckHypervisorBit())
+ return false;
+
+ if (!__VMwareBackdoorHypervisors())
+ return false;
+
+ struct
+ {
+ union
+ {
+ uint32_t ax;
+ uint32_t magic;
+ };
+ union
+ {
+ uint32_t bx;
+ size_t size;
+ };
+ union
+ {
+ uint32_t cx;
+ uint16_t command;
+ };
+ union
+ {
+ uint32_t dx;
+ uint16_t port;
+ };
+ uint32_t si;
+ uint32_t di;
+ } cmd;
+
+ cmd.si = cmd.di = 0;
+ cmd.bx = ~0x564D5868;
+ cmd.command = 0xA;
+ cmd.magic = 0x564D5868;
+ cmd.port = 0x5658;
+
+ asmv("in %%dx, %0" : "+a"(cmd.ax), "+b"(cmd.bx),
+ "+c"(cmd.cx), "+d"(cmd.dx),
+ "+S"(cmd.si), "+D"(cmd.di));
+
+ if (cmd.bx != 0x564D5868 ||
+ cmd.ax == 0xFFFFFFFF)
+ return false;
+ return true;
+ }
+
+ static int OpenMessageChannel(ToolboxContext *ctx, uint32_t Protocol)
+ {
+ uintptr_t ax, bx, cx, dx, si = 0, di = 0;
+
+ VM_PORT(MESSAGE_OPEN_CHANNEL,
+ (Protocol | FLAG_COOKIE), si, di,
+ 0, VMWARE_MAGIC,
+ ax, bx, cx, dx, si, di);
+
+ if ((HighWord(cx) & STATUS_SUCCESS) == 0)
+ {
+ trace("Failed to open message channel %#lx", Protocol);
+ return -EINVAL;
+ }
+
+ debug("Opened message channel %d (Protocol: %#lx)",
+ HighWord(dx), Protocol);
+ ctx->ChannelID = (uint16_t)HighWord(dx);
+ ctx->CookieHigh = si;
+ ctx->CookieLow = di;
+ return 0;
+ }
+
+ static void MessageClose(ToolboxContext *ctx)
+ {
+ uintptr_t ax, bx, cx, dx,
+ si = ctx->CookieHigh,
+ di = ctx->CookieLow;
+
+ VM_PORT(MESSAGE_CLOSE_CHANNEL,
+ 0, si, di,
+ ctx->ChannelID << 16,
+ VMWARE_MAGIC,
+ ax, bx, cx, dx, si, di);
+
+ debug("Closed message channel %d", ctx->ChannelID);
+ }
+
+ static uintptr_t MessageSendHB(ToolboxContext *ctx,
+ const char *Message)
+ {
+ uintptr_t ax, bx, cx, dx,
+ si = (uintptr_t)Message,
+ di = ctx->CookieLow,
+ bp = ctx->CookieHigh;
+
+ uint32_t ChannelID = ctx->ChannelID << 16;
+ size_t Size = strlen(Message);
+
+ VM_PORT_HB_OUT((STATUS_SUCCESS << 16) | MESSAGE_HB_MSG,
+ Size, si, di,
+ VMWARE_HYPERVISOR_HB | ChannelID | VMWARE_HYPERVISOR_OUT,
+ VMWARE_MAGIC, bp,
+ ax, bx, cx, dx, si, di);
+
+ return bx;
+ }
+
+ static uintptr_t MessageSendLB(ToolboxContext *ctx,
+ const char *Message)
+ {
+ uintptr_t ax, bx,
+ cx = STATUS_SUCCESS << 16,
+ dx, si, di;
+
+ size_t Size = strlen(Message);
+ while (Size &&
+ (HighWord(cx) & STATUS_SUCCESS))
+ {
+ uint32_t TotalBytes = MIN((uint32_t)Size, (uint32_t)4);
+ uint32_t Word = 0;
+ memcpy(&Word, Message, TotalBytes);
+ Message += TotalBytes;
+
+ si = ctx->CookieHigh;
+ di = ctx->CookieLow;
+
+ VM_PORT(MESSAGE_SEND_PAYLOAD,
+ Word, si, di,
+ ctx->ChannelID << 16,
+ VMWARE_MAGIC,
+ ax, bx, cx, dx, si, di);
+ }
+
+ return cx;
+ }
+
+ static uintptr_t MessageReceiveHB(ToolboxContext *ctx,
+ char *Buffer,
+ size_t BufferSize)
+ {
+ uintptr_t ax, bx, cx, dx,
+ si = ctx->CookieHigh,
+ di = (uintptr_t)Buffer,
+ bp = ctx->CookieLow;
+
+ uint32_t ChannelID = ctx->ChannelID << 16;
+
+ VM_PORT_HB_IN((STATUS_SUCCESS << 16) | MESSAGE_HB_MSG,
+ BufferSize, si, di,
+ VMWARE_HYPERVISOR_HB | ChannelID | VMWARE_HYPERVISOR_OUT,
+ VMWARE_MAGIC, bp,
+ ax, bx, cx, dx, si, di);
+
+ return bx;
+ }
+
+ static uintptr_t MessageReceiveLB(ToolboxContext *ctx,
+ char *Buffer,
+ size_t BufferSize)
+ {
+ uintptr_t ax, bx,
+ cx = STATUS_SUCCESS << 16,
+ dx, si, di;
+
+ while (BufferSize)
+ {
+ uint32_t TotalBytes = MIN((uint32_t)BufferSize, (uint32_t)4);
+
+ si = ctx->CookieHigh;
+ di = ctx->CookieLow;
+
+ VM_PORT(MESSAGE_RECV_PAYLOAD,
+ STATUS_SUCCESS, si, di,
+ ctx->ChannelID << 16,
+ VMWARE_MAGIC,
+ ax, bx, cx, dx, si, di);
+
+ if ((HighWord(cx) & STATUS_SUCCESS) == 0)
+ break;
+
+ memcpy(Buffer, &bx, TotalBytes);
+ Buffer += TotalBytes;
+ BufferSize -= TotalBytes;
+ }
+
+ return cx;
+ }
+
+ static int MessageSend(ToolboxContext *ctx,
+ const char *Message)
+ {
+ uintptr_t ax, bx, cx, dx, si, di;
+ size_t Size = strlen(Message);
+ int Retries = 0;
+
+ while (Retries < 2)
+ {
+ Retries++;
+ si = ctx->CookieHigh;
+ di = ctx->CookieLow;
+
+ VM_PORT(MESSAGE_SEND_SIZE,
+ Size, si, di,
+ ctx->ChannelID << 16,
+ VMWARE_MAGIC,
+ ax, bx, cx, dx, si, di);
+
+ if ((HighWord(cx) & STATUS_SUCCESS) == 0)
+ {
+ trace("Failed to send message size for \"%s\": %d",
+ Message, cx);
+ return -EINVAL;
+ }
+
+ bool HighBand = (HighWord(cx) & STATUS_HB) != 0;
+ if (HighBand)
+ bx = MessageSendHB(ctx, Message);
+ else
+ bx = MessageSendLB(ctx, Message);
+
+ int status = HighWord(bx);
+
+ if ((status & STATUS_SUCCESS) != 0)
+ {
+ debug("Message \"%s\" sent", Message);
+ return 0;
+ }
+ else if ((status & STATUS_CPT) == 0)
+ {
+ trace("Checkpoint occurred for message \"%s\"", Message);
+ continue;
+ }
+ else
+ break;
+ }
+
+ trace("Failed to send message \"%s\": %#lx", Message, bx);
+ return -EINVAL;
+ }
+
+ static int MessageReceive(ToolboxContext *ctx,
+ char **Buffer,
+ size_t *BufferSize)
+ {
+ uintptr_t ax, bx, cx, dx, si, di;
+ int Retries = 0;
+
+ *Buffer = NULL;
+ *BufferSize = 0;
+
+ char *ReplyBuf = NULL;
+ size_t ReplyBufPages = 0;
+ size_t ReplySize = 0;
+ while (Retries < 2)
+ {
+ Retries++;
+ si = ctx->CookieHigh;
+ di = ctx->CookieLow;
+
+ VM_PORT(MESSAGE_RECV_SIZE,
+ 0, si, di,
+ ctx->ChannelID << 16,
+ VMWARE_MAGIC,
+ ax, bx, cx, dx, si, di);
+
+ if ((HighWord(cx) & STATUS_SUCCESS) == 0)
+ {
+ trace("Failed to receive message size: %d", cx);
+ return -EINVAL;
+ }
+ else if ((HighWord(cx) & STATUS_DORECV) == 0)
+ {
+ debug("No message to receive");
+ return -EAGAIN;
+ }
+
+ ReplySize = bx;
+
+ if (ReplyBuf != NULL)
+ DriverManager->FreeMemory(DriverID, ReplyBuf, ReplyBufPages);
+ ReplyBufPages = ReplySize / 0x1000 + 1;
+ ReplyBuf = (char *)DriverManager->AllocateMemory(DriverID, ReplyBufPages);
+
+ bool HighBand = (HighWord(cx) & STATUS_HB) != 0;
+ if (HighBand)
+ bx = MessageReceiveHB(ctx, ReplyBuf, ReplySize);
+ else
+ bx = MessageReceiveLB(ctx, ReplyBuf, ReplySize);
+
+ if ((HighWord(bx) & STATUS_SUCCESS) == 0)
+ {
+ if ((HighWord(bx) & STATUS_CPT) == 0)
+ {
+ trace("Checkpoint occurred for message payload");
+ continue;
+ }
+
+ trace("Failed to receive message payload: %d", HighWord(bx));
+ DriverManager->FreeMemory(DriverID, ReplyBuf, ReplyBufPages);
+ return -EINVAL;
+ }
+
+ ReplyBuf[ReplySize] = '\0';
+
+ si = ctx->CookieHigh;
+ di = ctx->CookieLow;
+
+ VM_PORT(MESSAGE_RECV_STATUS,
+ STATUS_SUCCESS, si, di,
+ ctx->ChannelID << 16,
+ VMWARE_MAGIC,
+ ax, bx, cx, dx, si, di);
+
+ if ((HighWord(cx) & STATUS_SUCCESS) == 0)
+ {
+ if ((HighWord(cx) & STATUS_CPT) == 0)
+ {
+ trace("Retrying message receive");
+ continue;
+ }
+
+ trace("Failed to receive message status: %d", HighWord(cx));
+ DriverManager->FreeMemory(DriverID, ReplyBuf, ReplyBufPages);
+ return -EINVAL;
+ }
+
+ break;
+ }
+
+ if (ReplyBuf == NULL)
+ {
+ trace("Failed to receive message");
+ return -EINVAL;
+ }
+
+ *Buffer = ReplyBuf;
+ *BufferSize = ReplySize;
+ debug("Received message \"%s\"", ReplyBuf);
+ return 0;
+ }
+
+ static int SendRPCI(ToolboxContext *, const char *Request)
+ {
+ ToolboxContext rpci_ctx = {};
+ int status = OpenMessageChannel(&rpci_ctx, MESSAGE_RPCI);
+ if (status < 0)
+ {
+ trace("Failed to open RPCI channel: %d", status);
+ return status;
+ }
+
+ status = MessageSend(&rpci_ctx, Request);
+ if (status < 0)
+ {
+ trace("Failed to send RPCI request: %d", status);
+ return status;
+ }
+
+ MessageClose(&rpci_ctx);
+ return 0;
+ }
+
+ int MsgEqual(const char *haystack, const char *needle)
+ {
+ return strstr(haystack, needle) == haystack;
+ }
+
+ static int DisplayGetSize(ToolboxContext *ctx)
+ {
+ if (ctx->TCLOChannel != -1)
+ MessageClose(ctx);
+ OpenMessageChannel(ctx, MESSAGE_TCLO);
+
+ char EmptyBuffer[256] = {'\0'};
+ MessageSend(ctx, EmptyBuffer);
+
+ while (true)
+ {
+ /* FIXME: buf memory leak */
+ char *buf;
+ size_t len;
+
+ int status = MessageReceive(ctx, &buf, &len);
+ if (status == -EAGAIN)
+ {
+ v0::Sleep(DriverID, 1000);
+ continue;
+ }
+ else if (status < 0)
+ {
+ trace("Failed to receive message");
+ return 1;
+ }
+
+ buf[strlen(buf)] = '\0';
+ if (MsgEqual(buf, "reset"))
+ {
+ if (MessageSend(ctx, "OK ATR toolbox") < 0)
+ return 1;
+ }
+ else if (MsgEqual(buf, "ping"))
+ {
+ if (MessageSend(ctx, "OK ") < 0)
+ return 1;
+ }
+ else if (MsgEqual(buf, "Capabilities_Register"))
+ {
+ SendRPCI(ctx, "tools.capability.resolution_set 1");
+ SendRPCI(ctx, "tools.capability.resolution_server toolbox 1");
+ SendRPCI(ctx, "tools.capability.display_topology_set 1");
+ SendRPCI(ctx, "tools.capability.color_depth_set 1");
+ SendRPCI(ctx, "tools.capability.resolution_min 0 0");
+ SendRPCI(ctx, "tools.capability.unity 1");
+
+ if (MessageSend(ctx, "OK ") < 0)
+ return 1;
+ }
+ else if (MsgEqual(buf, "Resolution_Set"))
+ {
+ debug("%s", buf);
+ if (MessageSend(ctx, "OK ") < 0)
+ return 1;
+ MessageClose(ctx);
+ return 0;
+ }
+ else
+ {
+ if (MessageSend(ctx, "ERROR Unknown command") < 0)
+ return 1;
+ }
+ }
+ }
+
+ pid_t dst_id = -1;
+ pid_t dst_pid = -1;
+ ToolboxContext *tb_ctx = NULL;
+ void DisplayScaleThread()
+ {
+ /* sizeof ToolboxContext */
+ tb_ctx = (ToolboxContext *)DriverManager->AllocateMemory(DriverID, 1);
+ v0::Sleep(DriverID, 2000);
+
+ while (true)
+ {
+ if (DisplayGetSize(tb_ctx) != 0)
+ trace("Failed to scale display");
+ v0::Sleep(DriverID, 1000);
+ }
+ }
+
+ void CommandSend(VMwareCommand *cmd)
+ {
+ cmd->magic = VMWARE_MAGIC;
+ cmd->port = VMWARE_PORT;
+ asm volatile("in %%dx, %0"
+ : "+a"(cmd->ax), "+b"(cmd->bx),
+ "+c"(cmd->cx), "+d"(cmd->dx),
+ "+S"(cmd->si), "+D"(cmd->di));
+ }
+
+ void Absolute()
+ {
+ VMwareCommand cmd = {};
+
+ /* Enable */
+ cmd.bx = ABSPOINTER_ENABLE;
+ cmd.command = CMD_ABSPOINTER_COMMAND;
+ CommandSend(&cmd);
+
+ /* Status */
+ cmd.bx = 0;
+ cmd.command = CMD_ABSPOINTER_STATUS;
+ CommandSend(&cmd);
+
+ /* Read data (1) */
+ cmd.bx = 1;
+ cmd.command = CMD_ABSPOINTER_DATA;
+ CommandSend(&cmd);
+
+ /* Enable absolute */
+ cmd.bx = ABSPOINTER_ABSOLUTE;
+ cmd.command = CMD_ABSPOINTER_COMMAND;
+ CommandSend(&cmd);
+ }
+
+ void Relative()
+ {
+ VMwareCommand cmd = {};
+ cmd.bx = ABSPOINTER_RELATIVE;
+ cmd.command = CMD_ABSPOINTER_COMMAND;
+ CommandSend(&cmd);
+ }
+
+ InputReport ir = {};
+ void InterruptHandler(CPU::TrapFrame *)
+ {
+ uint8_t Data = inb(0x60);
+ (void)Data;
+
+ VMwareCommand cmd = {};
+ cmd.bx = 0;
+ cmd.command = CMD_ABSPOINTER_STATUS;
+ CommandSend(&cmd);
+
+ if (cmd.ax == 0xFFFF0000)
+ {
+ trace("VMware mouse is not connected?");
+ Relative();
+ Absolute();
+ return;
+ }
+
+ if ((cmd.ax & 0xFFFF) < 4)
+ return;
+
+ cmd.bx = 4;
+ cmd.command = CMD_ABSPOINTER_DATA;
+ CommandSend(&cmd);
+
+ int Buttons = (cmd.ax & 0xFFFF);
+
+ /**
+ * How should I handle this?
+ * (cmd.[bx,cx] * Width) / 0xFFFF
+ * Maybe TODO: Width and Height API?
+ */
+ uintptr_t AbsoluteX = cmd.bx;
+ uintptr_t AbsoluteY = cmd.cx;
+
+ ir.Type = INPUT_TYPE_MOUSE;
+ ir.Device = MouseDevID;
+ ir.Mouse.X = AbsoluteX;
+ ir.Mouse.Y = AbsoluteY;
+ ir.Mouse.Z = (int8_t)cmd.dx;
+ ir.Mouse.Absolute = 1;
+ ir.Mouse.LeftButton = Buttons & 0x20;
+ ir.Mouse.RightButton = Buttons & 0x10;
+ ir.Mouse.MiddleButton = Buttons & 0x08;
+ // ir.Mouse.Button4 = 0x0;
+ // ir.Mouse.Button5 = 0x0;
+ // ir.Mouse.Button6 = 0x0;
+ // ir.Mouse.Button7 = 0x0;
+ // ir.Mouse.Button8 = 0x0;
+ v0::ReportInputEvent(DriverID, &ir);
+ }
+
+ int Ioctl(struct Inode *, unsigned long Request, void *)
+ {
+ switch (Request)
+ {
+ case 0x1:
+ Relative();
+ break;
+ case 0x2:
+ Absolute();
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ const struct InodeOperations ops = {
+ .Lookup = nullptr,
+ .Create = nullptr,
+ .Remove = nullptr,
+ .Rename = nullptr,
+ .Read = nullptr,
+ .Write = nullptr,
+ .Truncate = nullptr,
+ .Open = nullptr,
+ .Close = nullptr,
+ .Ioctl = Ioctl,
+ .ReadDir = nullptr,
+ .MkDir = nullptr,
+ .RmDir = nullptr,
+ .SymLink = nullptr,
+ .ReadLink = nullptr,
+ .Seek = nullptr,
+ .Stat = nullptr,
+ };
+
+ bool ToolboxSupported = false;
+ int Entry()
+ {
+ ToolboxContext tb_ctx = {};
+ /* Test if it's supported */
+ int status = OpenMessageChannel(&tb_ctx, MESSAGE_TCLO);
+ if (status == 0)
+ {
+ ToolboxSupported = true;
+ MessageClose(&tb_ctx);
+ dst_id = v0::CreateKernelThread(DriverID, 0, "VMware Display Scale",
+ (void *)DisplayScaleThread, NULL);
+ dst_pid = v0::GetCurrentProcess(DriverID);
+ }
+
+ v0::PS2WriteCommand(DriverID, PS2_CMD_ENABLE_PORT_2);
+ v0::PS2WriteCommand(DriverID, PS2_CMD_READ_CONFIG);
+ PS2_CONFIGURATION config = {.Raw = v0::PS2ReadData(DriverID)};
+ config.Port2Interrupt = 1;
+ v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_CONFIG);
+ v0::PS2WriteData(DriverID, config.Raw);
+
+ v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
+ v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_SET_DEFAULTS);
+ v0::PS2ReadData(DriverID);
+
+ v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
+ v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_ENABLE_DATA_REPORTING);
+ v0::PS2ReadData(DriverID);
+ Absolute();
+
+ /**
+ * If we have another driver using the PS/2 mouse, we need to
+ * override its interrupt handler.
+ */
+ v0::OverrideInterruptHandler(DriverID, 12, (void *)InterruptHandler);
+
+ MouseDevID = v0::RegisterDevice(DriverID, INPUT_TYPE_MOUSE, &ops);
+ return 0;
+ }
+
+ int Final()
+ {
+ v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
+ v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_DISABLE_DATA_REPORTING);
+
+ Relative();
+
+ v0::UnregisterDevice(DriverID, MouseDevID);
+
+ if (ToolboxSupported)
+ {
+ v0::KillThread(DriverID, dst_id, dst_pid, 0);
+ if (tb_ctx->TCLOChannel != -1)
+ MessageClose(tb_ctx);
+ DriverManager->FreeMemory(DriverID, tb_ctx, 1);
+ }
+ return 0;
+ }
+
+ int Panic()
+ {
+ Relative();
+ v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
+ v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_DISABLE_DATA_REPORTING);
+ return 0;
+ }
+
+ int Probe()
+ {
+ if (!IsVMwareBackdoorAvailable())
+ return -ENODEV;
+ return 0;
+ }
+
+ REGISTER_BUILTIN_DRIVER(vmware,
+ "VMware Tools Driver",
+ "enderice2",
+ 1, 0, 0,
+ Entry,
+ Final,
+ Panic,
+ Probe);
+}
diff --git a/Kernel/drivers/net/e1000/e1000.cpp b/Kernel/drivers/net/e1000/e1000.cpp
new file mode 100644
index 00000000..063c7d3d
--- /dev/null
+++ b/Kernel/drivers/net/e1000/e1000.cpp
@@ -0,0 +1,485 @@
+/*
+ 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 .
+*/
+
+#include
+#include
+#include
+#include
+
+#include "e1000.hpp"
+
+extern Driver::Manager *DriverManager;
+extern PCI::Manager *PCIManager;
+namespace Driver::E1000
+{
+ dev_t DriverID;
+
+ class E1000Device
+ {
+ private:
+ PCI::PCIHeader0 *Header;
+ uint16_t DeviceID;
+ bool Initialized = false;
+
+ bool EEPROMAvailable = false;
+
+ 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()
+ {
+ debug("Initializing RX...");
+ uintptr_t Ptr = (uintptr_t)v0::AllocateMemory(DriverID,
+ 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)v0::AllocateMemory(DriverID,
+ 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()
+ {
+ debug("Initializing TX...");
+ uintptr_t Ptr = (uintptr_t)v0::AllocateMemory(DriverID,
+ 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))
+ v0::Yield(DriverID);
+ 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
+ {
+ trace("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(CPU::TrapFrame *)
+ {
+ WriteCMD(REG::IMASK, 0x1);
+ uint32_t status = ReadCMD(0xC0);
+ UNUSED(status);
+
+ while ((RX[RXCurrent]->Status & 0x1))
+ {
+ uint8_t *data = (uint8_t *)RX[RXCurrent]->Address;
+ uint16_t dataSz = RX[RXCurrent]->Length;
+
+ // ReportNetworkPacket(ID, data, dataSz);
+ /* FIXME: Implement */
+ trace("FIXME: Received packet");
+ (void)data;
+ (void)dataSz;
+
+ RX[RXCurrent]->Status = 0;
+ uint16_t OldRXCurrent = RXCurrent;
+ RXCurrent = (uint16_t)((RXCurrent + 1) % E1000_NUM_RX_DESC);
+ WriteCMD(REG::RXDESCTAIL, OldRXCurrent);
+ }
+ }
+
+ void Panic()
+ {
+ WriteCMD(REG::IMASK, 0x00000000);
+ WriteCMD(REG::ITR, 0x00000000);
+ WriteCMD(REG::IAM, 0x00000000);
+ }
+
+ E1000Device(PCI::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:
+ {
+ trace("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())
+ {
+ trace("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:
+ {
+ trace("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:
+ {
+ trace("Unimplemented E1000 device.");
+ return;
+ }
+ }
+
+ ((PCI::PCIDeviceHeader *)Header)->Command |= PCI::PCI_COMMAND_INTX_DISABLE;
+ }
+ };
+
+ std::unordered_map Drivers;
+
+ int Open(struct Inode *, int, mode_t)
+ {
+ return 0;
+ }
+
+ int Close(struct Inode *)
+ {
+ return 0;
+ }
+
+ ssize_t Read(struct Inode *, void *, size_t, off_t)
+ {
+ return 0;
+ }
+
+ ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t)
+ {
+ return Drivers[Node->GetMinor()]->write((uint8_t *)Buffer, Size);
+ }
+
+ int Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
+ {
+ return Drivers[Node->GetMinor()]->ioctl((NetIoctl)Request, Argp);
+ }
+
+ const struct InodeOperations ops = {
+ .Lookup = nullptr,
+ .Create = nullptr,
+ .Remove = nullptr,
+ .Rename = nullptr,
+ .Read = Read,
+ .Write = Write,
+ .Truncate = nullptr,
+ .Open = Open,
+ .Close = Close,
+ .Ioctl = Ioctl,
+ .ReadDir = nullptr,
+ .MkDir = nullptr,
+ .RmDir = nullptr,
+ .SymLink = nullptr,
+ .ReadLink = nullptr,
+ .Seek = nullptr,
+ .Stat = nullptr,
+ };
+
+ std::list Devices;
+ int Entry()
+ {
+ for (auto &&dev : Devices)
+ {
+ PCIManager->InitializeDevice(dev, KernelPageTable);
+ E1000Device *e1000 = new E1000Device((PCI::PCIHeader0 *)dev.Header,
+ dev.Header->DeviceID);
+
+ if (e1000->IsInitialized())
+ {
+ dev_t ret = v0::RegisterDevice(DriverID, NETWORK_TYPE_ETHERNET, &ops);
+ Drivers[ret] = e1000;
+ }
+ }
+
+ if (Drivers.empty())
+ {
+ trace("No valid E1000 device found.");
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ int Final()
+ {
+ for (auto &&dev : Drivers)
+ {
+ dev_t ret = dev.first;
+ v0::UnregisterDevice(DriverID, ret);
+ delete dev.second;
+ }
+ return 0;
+ }
+
+ int Panic()
+ {
+ for (auto &&i : Drivers)
+ i.second->Panic();
+ return 0;
+ }
+
+ int Probe()
+ {
+ Devices = PCIManager->FindPCIDevice(
+ {
+ 0x8086, /* Intel */
+ },
+ {
+ 0x100E, /* 82540EM */
+ 0x100F, /* 82545EM */
+ 0x10D3, /* 82574L */
+ 0x10EA, /* I217-LM */
+ 0x153A, /* 82577LM */
+ });
+
+ if (Devices.empty())
+ {
+ trace("No E1000 device found.");
+ return -ENODEV;
+ }
+ return 0;
+ }
+
+ REGISTER_BUILTIN_DRIVER(e1000,
+ "Intel(R) PRO/1000 Network Driver",
+ "enderice2",
+ 1, 0, 0,
+ Entry,
+ Final,
+ Panic,
+ Probe);
+}
diff --git a/Kernel/drivers/net/e1000/e1000.hpp b/Kernel/drivers/net/e1000/e1000.hpp
new file mode 100644
index 00000000..4c4ac0fd
--- /dev/null
+++ b/Kernel/drivers/net/e1000/e1000.hpp
@@ -0,0 +1,156 @@
+/*
+ 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 .
+*/
+
+#pragma once
+
+#include
+
+enum REG
+{
+ CTRL = 0x0000,
+ STATUS = 0x0008,
+ ICR = 0x000C,
+ EEPROM = 0x0014,
+ CTRL_EXT = 0x0018,
+ ITR = 0x00C4,
+ IMASK = 0x00D0,
+ IAM = 0x00D8,
+ RCTRL = 0x0100,
+ RXDESCLO = 0x2800,
+ RXDESCHI = 0x2804,
+ RXDESCLEN = 0x2808,
+ RXDESCHEAD = 0x2810,
+ RXDESCTAIL = 0x2818,
+ TCTRL = 0x0400,
+ TXDESCLO = 0x3800,
+ TXDESCHI = 0x3804,
+ TXDESCLEN = 0x3808,
+ TXDESCHEAD = 0x3810,
+ TXDESCTAIL = 0x3818,
+ RDTR = 0x2820,
+ RXDCTL = 0x3828,
+ RADV = 0x282C,
+ RSRPD = 0x2C00,
+ TIPG = 0x0410
+};
+
+enum PCTRL
+{
+ RESERVED = 0b000000,
+ SPEED_SELECTION_MSB = 0b010000,
+ UPDATE_COLLISION_TEST = 0b001000,
+ DUPLEX_MODE = 0b000100,
+ RESTART_AUTO_NEGOTIATION = 0b000010,
+ ISOLATE = 0b000001,
+ POWER_DOWN = 0b100000,
+ SPEED_SELECTION_LSB = 0b100000,
+};
+
+enum ECTRL
+{
+ SLU = 0x40
+};
+
+enum RTCL
+{
+ RDMTS_HALF = (0 << 8),
+ RDMTS_QUARTER = (1 << 8),
+ RDMTS_EIGHTH = (2 << 8)
+};
+
+enum RCTL
+{
+ EN = (1 << 1),
+ SBP = (1 << 2),
+ UPE = (1 << 3),
+ MPE = (1 << 4),
+ LPE = (1 << 5),
+ LBM_NONE = (0 << 6),
+ LBM_PHY = (3 << 6),
+ MO_36 = (0 << 12),
+ MO_35 = (1 << 12),
+ MO_34 = (2 << 12),
+ MO_32 = (3 << 12),
+ BAM = (1 << 15),
+ VFE = (1 << 18),
+ CFIEN = (1 << 19),
+ CFI = (1 << 20),
+ DPF = (1 << 22),
+ PMCF = (1 << 23),
+ SECRC = (1 << 26),
+ BSIZE_256 = (3 << 16),
+ BSIZE_512 = (2 << 16),
+ BSIZE_1024 = (1 << 16),
+ BSIZE_2048 = (0 << 16),
+ BSIZE_4096 = ((3 << 16) | (1 << 25)),
+ BSIZE_8192 = ((2 << 16) | (1 << 25)),
+ BSIZE_16384 = ((1 << 16) | (1 << 25))
+};
+
+enum CMD
+{
+ EOP = (1 << 0),
+ IFCS = (1 << 1),
+ IC = (1 << 2),
+ RS = (1 << 3),
+ RPS = (1 << 4),
+ VLE = (1 << 6),
+ IDE = (1 << 7)
+};
+
+enum TCTL
+{
+ EN_ = (1 << 1),
+ PSP = (1 << 3),
+ CT_SHIFT = 4,
+ COLD_SHIFT = 12,
+ SWXOFF = (1 << 22),
+ RTLC = (1 << 24)
+};
+
+enum TSTA
+{
+ DD = (1 << 0),
+ EC = (1 << 1),
+ LC = (1 << 2)
+};
+
+enum LSTA
+{
+ LSTA_TU = (1 << 3)
+};
+
+struct RXDescriptor
+{
+ volatile uint64_t Address;
+ volatile uint16_t Length;
+ volatile uint16_t Checksum;
+ volatile uint8_t Status;
+ volatile uint8_t Errors;
+ volatile uint16_t Special;
+} __attribute__((packed));
+
+struct TXDescriptor
+{
+ volatile uint64_t Address;
+ volatile uint16_t Length;
+ volatile uint8_t cso;
+ volatile uint8_t Command;
+ volatile uint8_t Status;
+ volatile uint8_t css;
+ volatile uint16_t Special;
+} __attribute__((packed));
diff --git a/Kernel/drivers/net/rtl8139/rtl8139.cpp b/Kernel/drivers/net/rtl8139/rtl8139.cpp
new file mode 100644
index 00000000..a785c70d
--- /dev/null
+++ b/Kernel/drivers/net/rtl8139/rtl8139.cpp
@@ -0,0 +1,294 @@
+/*
+ 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 .
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+#include "rtl8139.hpp"
+
+extern Driver::Manager *DriverManager;
+extern PCI::Manager *PCIManager;
+namespace Driver::RTL8139
+{
+ dev_t DriverID;
+
+ class RTL8139Device
+ {
+ private:
+ PCI::PCIHeader0 *Header;
+ bool Initialized = false;
+
+ struct BARData
+ {
+ uint8_t Type;
+ uint16_t IOBase;
+ uint64_t MemoryBase;
+ } BAR;
+
+ const int BaseBufferSize = 8192;
+ const int WRAPBytes = 1500;
+ const int AdditionalBytes = 16;
+ const int BufferSize = BaseBufferSize +
+ WRAPBytes +
+ AdditionalBytes;
+
+ uint8_t *RXBuffer = nullptr;
+ int TXCurrent = 0;
+ uint16_t CurrentPacket = 0;
+
+ uint8_t TSAD[4] = {0x20, 0x24, 0x28, 0x2C};
+ uint8_t TSD[4] = {0x10, 0x14, 0x18, 0x1C};
+
+ public:
+ dev_t ID;
+
+ bool IsInitialized() { return Initialized; }
+
+ size_t write(uint8_t *Buffer, size_t Size)
+ {
+ outl(TSAD[TXCurrent], (uint32_t)(reinterpret_cast(Buffer)));
+ outl(TSD[TXCurrent++], (uint32_t)Size);
+ if (TXCurrent > 3)
+ TXCurrent = 0;
+ return Size;
+ }
+
+ MediaAccessControl GetMAC()
+ {
+ return MediaAccessControl();
+ }
+
+ int ioctl(NetIoctl req, void *)
+ {
+ switch (req)
+ {
+ case IOCTL_NET_GET_MAC:
+ {
+ return -ENOSYS;
+ }
+ default:
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ void OnInterruptReceived(CPU::TrapFrame *)
+ {
+ /* Acknowledge interrupt */
+ uint16_t status = inw(RegISR);
+ debug("%#lx", status);
+
+ /* Read status */
+ if (status & RecOK)
+ {
+ /* Get the current packet */
+ uint16_t *data = (uint16_t *)(RXBuffer + CurrentPacket);
+ uint16_t dataSz = *(data + 1);
+ data += 2;
+
+ // ReportNetworkPacket(ID, data, dataSz);
+ /* FIXME: Implement */
+ fixme("Received packet");
+ (void)data;
+ (void)dataSz;
+
+ /* Update CAPR */
+#define RX_READ_PTR_MASK (~0x3)
+ CurrentPacket = (uint16_t)((CurrentPacket + dataSz + 4 + 3) & RX_READ_PTR_MASK);
+ if (CurrentPacket > BufferSize)
+ CurrentPacket -= uint16_t(BufferSize);
+ outw(RegCAPR, CurrentPacket - 0x10);
+ }
+
+ /* Clear interrupt */
+ outw(RegISR, (RecOK | RecBad | SendOK | SendBad));
+ }
+
+ void Panic()
+ {
+ }
+
+ RTL8139Device(PCI::PCIHeader0 *_Header)
+ : Header(_Header)
+ {
+ uint32_t PCIBAR0 = Header->BAR0;
+ uint32_t PCIBAR1 = Header->BAR1;
+ BAR.Type = PCIBAR0 & 1;
+ BAR.IOBase = (uint16_t)(PCIBAR0 & (~3));
+ BAR.MemoryBase = PCIBAR1 & (~15);
+
+ RXBuffer = (uint8_t *)v0::AllocateMemory(DriverID, TO_PAGES(BufferSize));
+
+ /* Power on */
+ outb(RegCONFIG1, 0x0);
+
+ /* Software Reset */
+ outb(RegCMD, 0x10);
+ while (inb(RegCMD) & 0x10)
+ v0::Yield(DriverID);
+
+ /* Initialize receive buffer */
+ outl(RegRBSTART, (uint32_t)(reinterpret_cast(RXBuffer)));
+
+ /* Configure interrupt mask register */
+ outw(RegIMR, (RecOK | RecBad | SendOK | SendBad));
+ outl(regRCR, (RcAB | RcAM | RcAPM | RcAAP) | RcWRAP);
+
+ /* Enable receive and transmit */
+ outb(RegCMD, 0xC); /* 0xC = RE and TE bit */
+
+ uint32_t MAC1 = inl(RegMAC);
+ uint16_t MAC2 = inw(RegMAR);
+ MediaAccessControl mac = {
+ mac.Address[0] = (uint8_t)MAC1,
+ mac.Address[1] = (uint8_t)(MAC1 >> 8),
+ mac.Address[2] = (uint8_t)(MAC1 >> 16),
+ mac.Address[3] = (uint8_t)(MAC1 >> 24),
+ mac.Address[4] = (uint8_t)MAC2,
+ mac.Address[5] = (uint8_t)(MAC2 >> 8)};
+
+ Initialized = true;
+ }
+
+ ~RTL8139Device()
+ {
+ if (!Initialized)
+ return;
+
+ ((PCI::PCIDeviceHeader *)Header)->Command |= PCI::PCI_COMMAND_INTX_DISABLE;
+ /* FIXME: Shutdown code */
+ }
+ };
+
+ std::unordered_map Drivers;
+
+ int Open(struct Inode *, int, mode_t)
+ {
+ return 0;
+ }
+
+ int Close(struct Inode *)
+ {
+ return 0;
+ }
+
+ ssize_t Read(struct Inode *, void *, size_t, off_t)
+ {
+ return 0;
+ }
+
+ ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t)
+ {
+ return Drivers[Node->GetMinor()]->write((uint8_t *)Buffer, Size);
+ }
+
+ int Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
+ {
+ return Drivers[Node->GetMinor()]->ioctl((NetIoctl)Request, Argp);
+ }
+
+ const struct InodeOperations ops = {
+ .Lookup = nullptr,
+ .Create = nullptr,
+ .Remove = nullptr,
+ .Rename = nullptr,
+ .Read = Read,
+ .Write = Write,
+ .Truncate = nullptr,
+ .Open = Open,
+ .Close = Close,
+ .Ioctl = Ioctl,
+ .ReadDir = nullptr,
+ .MkDir = nullptr,
+ .RmDir = nullptr,
+ .SymLink = nullptr,
+ .ReadLink = nullptr,
+ .Seek = nullptr,
+ .Stat = nullptr,
+ };
+
+ std::list Devices;
+ int Entry()
+ {
+ for (auto &&dev : Devices)
+ {
+ PCIManager->InitializeDevice(dev, KernelPageTable);
+ RTL8139Device *rtl8139 = new RTL8139Device((PCI::PCIHeader0 *)dev.Header);
+ if (rtl8139->IsInitialized())
+ {
+ dev_t ret = v0::RegisterDevice(DriverID, NETWORK_TYPE_ETHERNET, &ops);
+ Drivers[ret] = rtl8139;
+ }
+ }
+
+ if (Drivers.empty())
+ {
+ trace("No valid RTL8139 device found.");
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ int Final()
+ {
+ for (auto &&dev : Drivers)
+ {
+ dev_t ret = dev.first;
+ v0::UnregisterDevice(DriverID, ret);
+ delete dev.second;
+ }
+ return 0;
+ }
+
+ int Panic()
+ {
+ for (auto &&i : Drivers)
+ i.second->Panic();
+ return 0;
+ }
+
+ int Probe()
+ {
+ Devices = PCIManager->FindPCIDevice(
+ {
+ 0x10EC, /* Realtek */
+ },
+ {
+ 0x8139, /* RTL8139 */
+ });
+
+ if (Devices.empty())
+ {
+ trace("No RTL8139 device found.");
+ return -ENODEV;
+ }
+ return 0;
+ }
+
+ REGISTER_BUILTIN_DRIVER(rtl8139,
+ "Realtek RTL8139 Network Driver",
+ "enderice2",
+ 1, 0, 0,
+ Entry,
+ Final,
+ Panic,
+ Probe);
+}
diff --git a/Kernel/drivers/net/rtl8139/rtl8139.hpp b/Kernel/drivers/net/rtl8139/rtl8139.hpp
new file mode 100644
index 00000000..2ac45242
--- /dev/null
+++ b/Kernel/drivers/net/rtl8139/rtl8139.hpp
@@ -0,0 +1,88 @@
+/*
+ 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 .
+*/
+
+#pragma once
+
+#include
+
+struct RXQueueEntry
+{
+ uint8_t *Buffer;
+ uint16_t Size;
+} __attribute__((packed));
+
+enum InterruptStatus
+{
+ RecOK = 0x1,
+ RecBad = 0x2,
+ SendOK = 0x4,
+ SendBad = 0x8,
+};
+
+enum ReceiveConfig
+{
+ /**
+ * Accept broadcast packets
+ * sent to mac ff:ff:ff:ff:ff:ff
+ */
+ RcAB = 0x1,
+
+ /**
+ * Accept packets sent to the
+ * multicast address
+ */
+ RcAM = 0x2,
+
+ /**
+ * Accept packets sent to the
+ * NIC's MAC address
+ */
+ RcAPM = 0x4,
+
+ /**
+ * Accept all packets
+ * (promiscuous mode)
+ */
+ RcAAP = 0x8,
+
+ /**
+ * The WRAP bit is used to tell the
+ * NIC to wrap around the ring
+ * buffer when it reaches the end
+ * of the buffer.
+ *
+ * @note If this bit is set, the
+ * buffer must have an additional
+ * 1500 bytes of space at the end
+ * of the buffer to prevent the
+ * NIC from overflowing the buffer.
+ */
+ RcWRAP = 0x80,
+};
+
+enum Registers
+{
+ RegMAC = 0x0,
+ RegMAR = 0x8,
+ RegRBSTART = 0x30,
+ RegCMD = 0x37,
+ RegCAPR = 0x38,
+ regRCR = 0x44,
+ RegCONFIG1 = 0x52,
+ RegIMR = 0x3C,
+ RegISR = 0x3E,
+};
diff --git a/Kernel/drivers/storage/ahci/ahci.cpp b/Kernel/drivers/storage/ahci/ahci.cpp
new file mode 100644
index 00000000..a7f097ed
--- /dev/null
+++ b/Kernel/drivers/storage/ahci/ahci.cpp
@@ -0,0 +1,1010 @@
+/*
+ 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 .
+*/
+
+#include
+#include
+#include
+
+extern Driver::Manager *DriverManager;
+extern PCI::Manager *PCIManager;
+EXTERNC void KPrint(const char *Format, ...);
+namespace Driver::AHCI
+{
+ dev_t DriverID;
+
+#define ATA_DEV_DRQ 0x08
+#define ATA_DEV_BUSY 0x80
+#define ATA_CMD_READ_DMA_EX 0x25
+#define ATA_CMD_WRITE_DMA_EX 0x35
+#define ATA_CMD_IDENTIFY 0xEC
+
+#define ATAPI_CMD_IDENTIFY_PACKET 0xA1
+
+#define HBA_PORT_IPM_ACTIVE 0x1
+#define HBA_PORT_DEV_PRESENT 0x3
+#define HBA_PxIS_TFES (1 << 30)
+
+#define SATA_SIG_ATA 0x00000101
+#define SATA_SIG_PM 0x96690101
+#define SATA_SIG_SEMB 0xC33C0101
+#define SATA_SIG_ATAPI 0xEB140101
+
+#define HBA_PxCMD_ST 0x0001
+#define HBA_PxCMD_FRE 0x0010
+#define HBA_PxCMD_FR 0x4000
+#define HBA_PxCMD_CR 0x8000
+
+ enum PortType
+ {
+ None = 0,
+ SATA = 1,
+ SEMB = 2,
+ PM = 3,
+ SATAPI = 4,
+ };
+
+ enum FIS_TYPE
+ {
+ FIS_TYPE_REG_H2D = 0x27,
+ FIS_TYPE_REG_D2H = 0x34,
+ FIS_TYPE_DMA_ACT = 0x39,
+ FIS_TYPE_DMA_SETUP = 0x41,
+ FIS_TYPE_DATA = 0x46,
+ FIS_TYPE_BIST = 0x58,
+ FIS_TYPE_PIO_SETUP = 0x5F,
+ FIS_TYPE_DEV_BITS = 0xA1,
+ };
+
+ struct HBAPort
+ {
+ uint32_t CommandListBase;
+ uint32_t CommandListBaseUpper;
+ uint32_t FISBaseAddress;
+ uint32_t FISBaseAddressUpper;
+ uint32_t InterruptStatus;
+ uint32_t InterruptEnable;
+ uint32_t CommandStatus;
+ uint32_t Reserved0;
+ uint32_t TaskFileData;
+ uint32_t Signature;
+ uint32_t SataStatus;
+ uint32_t SataControl;
+ uint32_t SataError;
+ uint32_t SataActive;
+ uint32_t CommandIssue;
+ uint32_t SataNotification;
+ uint32_t FISSwitchControl;
+ uint32_t Reserved1[11];
+ uint32_t Vendor[4];
+ };
+
+ struct HBAMemory
+ {
+ uint32_t HostCapability;
+ uint32_t GlobalHostControl;
+ uint32_t InterruptStatus;
+ uint32_t PortsImplemented;
+ uint32_t Version;
+ uint32_t CCCControl;
+ uint32_t CCCPorts;
+ uint32_t EnclosureManagementLocation;
+ uint32_t EnclosureManagementControl;
+ uint32_t HostCapabilitiesExtended;
+ uint32_t BIOSHandoffControlStatus;
+ uint8_t Reserved0[0x74];
+ uint8_t Vendor[0x60];
+ HBAPort Ports[1];
+ };
+
+ struct HBACommandHeader
+ {
+ uint8_t CommandFISLength : 5;
+ uint8_t ATAPI : 1;
+ uint8_t Write : 1;
+ uint8_t Preferable : 1;
+ uint8_t Reset : 1;
+ uint8_t BIST : 1;
+ uint8_t ClearBusy : 1;
+ uint8_t Reserved0 : 1;
+ uint8_t PortMultiplier : 4;
+ uint16_t PRDTLength;
+ uint32_t PRDBCount;
+ uint32_t CommandTableBaseAddress;
+ uint32_t CommandTableBaseAddressUpper;
+ uint32_t Reserved1[4];
+ };
+
+ struct HBAPRDTEntry
+ {
+ uint32_t DataBaseAddress;
+ uint32_t DataBaseAddressUpper;
+ uint32_t Reserved0;
+ uint32_t ByteCount : 22;
+ uint32_t Reserved1 : 9;
+ uint32_t InterruptOnCompletion : 1;
+ };
+
+ struct HBACommandTable
+ {
+ uint8_t CommandFIS[64];
+ uint8_t ATAPICommand[16];
+ uint8_t Reserved[48];
+ HBAPRDTEntry PRDTEntry[];
+ };
+
+ struct FIS_REG_H2D
+ {
+ uint8_t FISType;
+ uint8_t PortMultiplier : 4;
+ uint8_t Reserved0 : 3;
+ uint8_t CommandControl : 1;
+ uint8_t Command;
+ uint8_t FeatureLow;
+ uint8_t LBA0;
+ uint8_t LBA1;
+ uint8_t LBA2;
+ uint8_t DeviceRegister;
+ uint8_t LBA3;
+ uint8_t LBA4;
+ uint8_t LBA5;
+ uint8_t FeatureHigh;
+ uint8_t CountLow;
+ uint8_t CountHigh;
+ uint8_t ISOCommandCompletion;
+ uint8_t Control;
+ uint8_t Reserved1[4];
+ };
+
+ /* https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ata/ns-ata-_identify_device_data */
+ struct __packed ATA_IDENTIFY
+ {
+ struct __packed
+ {
+ uint16_t Reserved1 : 1;
+ uint16_t Retired3 : 1;
+ uint16_t ResponseIncomplete : 1;
+ uint16_t Retired2 : 3;
+ uint16_t FixedDevice : 1;
+ uint16_t RemovableMedia : 1;
+ uint16_t Retired1 : 7;
+ uint16_t DeviceType : 1;
+ } GeneralConfiguration;
+ uint16_t NumCylinders;
+ uint16_t SpecificConfiguration;
+ uint16_t NumHeads;
+ uint16_t Retired1[2];
+ uint16_t NumSectorsPerTrack;
+ uint16_t VendorUnique1[3];
+ uint8_t SerialNumber[20];
+ uint16_t Retired2[2];
+ uint16_t Obsolete1;
+ uint8_t FirmwareRevision[8];
+ uint8_t ModelNumber[40];
+ uint8_t MaximumBlockTransfer;
+ uint8_t VendorUnique2;
+ struct __packed
+ {
+ uint16_t FeatureSupported : 1;
+ uint16_t Reserved : 15;
+ } TrustedComputing;
+ struct __packed
+ {
+ uint8_t CurrentLongPhysicalSectorAlignment : 2;
+ uint8_t ReservedByte49 : 6;
+ uint8_t DmaSupported : 1;
+ uint8_t LbaSupported : 1;
+ uint8_t IordyDisable : 1;
+ uint8_t IordySupported : 1;
+ uint8_t Reserved1 : 1;
+ uint8_t StandybyTimerSupport : 1;
+ uint8_t Reserved2 : 2;
+ uint16_t ReservedWord50;
+ } Capabilities;
+ uint16_t ObsoleteWords51[2];
+ uint16_t TranslationFieldsValid : 3;
+ uint16_t Reserved3 : 5;
+ uint16_t FreeFallControlSensitivity : 8;
+ uint16_t NumberOfCurrentCylinders;
+ uint16_t NumberOfCurrentHeads;
+ uint16_t CurrentSectorsPerTrack;
+ uint32_t CurrentSectorCapacity;
+ uint8_t CurrentMultiSectorSetting;
+ uint8_t MultiSectorSettingValid : 1;
+ uint8_t ReservedByte59 : 3;
+ uint8_t SanitizeFeatureSupported : 1;
+ uint8_t CryptoScrambleExtCommandSupported : 1;
+ uint8_t OverwriteExtCommandSupported : 1;
+ uint8_t BlockEraseExtCommandSupported : 1;
+ uint32_t UserAddressableSectors;
+ uint16_t ObsoleteWord62;
+ uint16_t MultiWordDMASupport : 8;
+ uint16_t MultiWordDMAActive : 8;
+ uint16_t AdvancedPIOModes : 8;
+ uint16_t ReservedByte64 : 8;
+ uint16_t MinimumMWXferCycleTime;
+ uint16_t RecommendedMWXferCycleTime;
+ uint16_t MinimumPIOCycleTime;
+ uint16_t MinimumPIOCycleTimeIORDY;
+ struct __packed
+ {
+ uint16_t ZonedCapabilities : 2;
+ uint16_t NonVolatileWriteCache : 1;
+ uint16_t ExtendedUserAddressableSectorsSupported : 1;
+ uint16_t DeviceEncryptsAllUserData : 1;
+ uint16_t ReadZeroAfterTrimSupported : 1;
+ uint16_t Optional28BitCommandsSupported : 1;
+ uint16_t IEEE1667 : 1;
+ uint16_t DownloadMicrocodeDmaSupported : 1;
+ uint16_t SetMaxSetPasswordUnlockDmaSupported : 1;
+ uint16_t WriteBufferDmaSupported : 1;
+ uint16_t ReadBufferDmaSupported : 1;
+ uint16_t DeviceConfigIdentifySetDmaSupported : 1;
+ uint16_t LPSAERCSupported : 1;
+ uint16_t DeterministicReadAfterTrimSupported : 1;
+ uint16_t CFastSpecSupported : 1;
+ } AdditionalSupported;
+ uint16_t ReservedWords70[5];
+ uint16_t QueueDepth : 5;
+ uint16_t ReservedWord75 : 11;
+ struct __packed
+ {
+ uint16_t Reserved0 : 1;
+ uint16_t SataGen1 : 1;
+ uint16_t SataGen2 : 1;
+ uint16_t SataGen3 : 1;
+ uint16_t Reserved1 : 4;
+ uint16_t NCQ : 1;
+ uint16_t HIPM : 1;
+ uint16_t PhyEvents : 1;
+ uint16_t NcqUnload : 1;
+ uint16_t NcqPriority : 1;
+ uint16_t HostAutoPS : 1;
+ uint16_t DeviceAutoPS : 1;
+ uint16_t ReadLogDMA : 1;
+ uint16_t Reserved2 : 1;
+ uint16_t CurrentSpeed : 3;
+ uint16_t NcqStreaming : 1;
+ uint16_t NcqQueueMgmt : 1;
+ uint16_t NcqReceiveSend : 1;
+ uint16_t DEVSLPtoReducedPwrState : 1;
+ uint16_t Reserved3 : 8;
+ } SerialAtaCapabilities;
+ struct __packed
+ {
+ uint16_t Reserved0 : 1;
+ uint16_t NonZeroOffsets : 1;
+ uint16_t DmaSetupAutoActivate : 1;
+ uint16_t DIPM : 1;
+ uint16_t InOrderData : 1;
+ uint16_t HardwareFeatureControl : 1;
+ uint16_t SoftwareSettingsPreservation : 1;
+ uint16_t NCQAutosense : 1;
+ uint16_t DEVSLP : 1;
+ uint16_t HybridInformation : 1;
+ uint16_t Reserved1 : 6;
+ } SerialAtaFeaturesSupported;
+ struct __packed
+ {
+ uint16_t Reserved0 : 1;
+ uint16_t NonZeroOffsets : 1;
+ uint16_t DmaSetupAutoActivate : 1;
+ uint16_t DIPM : 1;
+ uint16_t InOrderData : 1;
+ uint16_t HardwareFeatureControl : 1;
+ uint16_t SoftwareSettingsPreservation : 1;
+ uint16_t DeviceAutoPS : 1;
+ uint16_t DEVSLP : 1;
+ uint16_t HybridInformation : 1;
+ uint16_t Reserved1 : 6;
+ } SerialAtaFeaturesEnabled;
+ uint16_t MajorRevision;
+ uint16_t MinorRevision;
+ struct __packed
+ {
+ uint16_t SmartCommands : 1;
+ uint16_t SecurityMode : 1;
+ uint16_t RemovableMediaFeature : 1;
+ uint16_t PowerManagement : 1;
+ uint16_t Reserved1 : 1;
+ uint16_t WriteCache : 1;
+ uint16_t LookAhead : 1;
+ uint16_t ReleaseInterrupt : 1;
+ uint16_t ServiceInterrupt : 1;
+ uint16_t DeviceReset : 1;
+ uint16_t HostProtectedArea : 1;
+ uint16_t Obsolete1 : 1;
+ uint16_t WriteBuffer : 1;
+ uint16_t ReadBuffer : 1;
+ uint16_t Nop : 1;
+ uint16_t Obsolete2 : 1;
+ uint16_t DownloadMicrocode : 1;
+ uint16_t DmaQueued : 1;
+ uint16_t Cfa : 1;
+ uint16_t AdvancedPm : 1;
+ uint16_t Msn : 1;
+ uint16_t PowerUpInStandby : 1;
+ uint16_t ManualPowerUp : 1;
+ uint16_t Reserved2 : 1;
+ uint16_t SetMax : 1;
+ uint16_t Acoustics : 1;
+ uint16_t BigLba : 1;
+ uint16_t DeviceConfigOverlay : 1;
+ uint16_t FlushCache : 1;
+ uint16_t FlushCacheExt : 1;
+ uint16_t WordValid83 : 2;
+ uint16_t SmartErrorLog : 1;
+ uint16_t SmartSelfTest : 1;
+ uint16_t MediaSerialNumber : 1;
+ uint16_t MediaCardPassThrough : 1;
+ uint16_t StreamingFeature : 1;
+ uint16_t GpLogging : 1;
+ uint16_t WriteFua : 1;
+ uint16_t WriteQueuedFua : 1;
+ uint16_t WWN64Bit : 1;
+ uint16_t URGReadStream : 1;
+ uint16_t URGWriteStream : 1;
+ uint16_t ReservedForTechReport : 2;
+ uint16_t IdleWithUnloadFeature : 1;
+ uint16_t WordValid : 2;
+ } CommandSetSupport;
+ struct __packed
+ {
+ uint16_t SmartCommands : 1;
+ uint16_t SecurityMode : 1;
+ uint16_t RemovableMediaFeature : 1;
+ uint16_t PowerManagement : 1;
+ uint16_t Reserved1 : 1;
+ uint16_t WriteCache : 1;
+ uint16_t LookAhead : 1;
+ uint16_t ReleaseInterrupt : 1;
+ uint16_t ServiceInterrupt : 1;
+ uint16_t DeviceReset : 1;
+ uint16_t HostProtectedArea : 1;
+ uint16_t Obsolete1 : 1;
+ uint16_t WriteBuffer : 1;
+ uint16_t ReadBuffer : 1;
+ uint16_t Nop : 1;
+ uint16_t Obsolete2 : 1;
+ uint16_t DownloadMicrocode : 1;
+ uint16_t DmaQueued : 1;
+ uint16_t Cfa : 1;
+ uint16_t AdvancedPm : 1;
+ uint16_t Msn : 1;
+ uint16_t PowerUpInStandby : 1;
+ uint16_t ManualPowerUp : 1;
+ uint16_t Reserved2 : 1;
+ uint16_t SetMax : 1;
+ uint16_t Acoustics : 1;
+ uint16_t BigLba : 1;
+ uint16_t DeviceConfigOverlay : 1;
+ uint16_t FlushCache : 1;
+ uint16_t FlushCacheExt : 1;
+ uint16_t Resrved3 : 1;
+ uint16_t Words119_120Valid : 1;
+ uint16_t SmartErrorLog : 1;
+ uint16_t SmartSelfTest : 1;
+ uint16_t MediaSerialNumber : 1;
+ uint16_t MediaCardPassThrough : 1;
+ uint16_t StreamingFeature : 1;
+ uint16_t GpLogging : 1;
+ uint16_t WriteFua : 1;
+ uint16_t WriteQueuedFua : 1;
+ uint16_t WWN64Bit : 1;
+ uint16_t URGReadStream : 1;
+ uint16_t URGWriteStream : 1;
+ uint16_t ReservedForTechReport : 2;
+ uint16_t IdleWithUnloadFeature : 1;
+ uint16_t Reserved4 : 2;
+ } CommandSetActive;
+ uint16_t UltraDMASupport : 8;
+ uint16_t UltraDMAActive : 8;
+ struct __packed
+ {
+ uint16_t TimeRequired : 15;
+ uint16_t ExtendedTimeReported : 1;
+ } NormalSecurityEraseUnit;
+ struct __packed
+ {
+ uint16_t TimeRequired : 15;
+ uint16_t ExtendedTimeReported : 1;
+ } EnhancedSecurityEraseUnit;
+ uint16_t CurrentAPMLevel : 8;
+ uint16_t ReservedWord91 : 8;
+ uint16_t MasterPasswordID;
+ uint16_t HardwareResetResult;
+ uint16_t CurrentAcousticValue : 8;
+ uint16_t RecommendedAcousticValue : 8;
+ uint16_t StreamMinRequestSize;
+ uint16_t StreamingTransferTimeDMA;
+ uint16_t StreamingAccessLatencyDMAPIO;
+ uint32_t StreamingPerfGranularity;
+ uint32_t Max48BitLBA[2];
+ uint16_t StreamingTransferTime;
+ uint16_t DsmCap;
+ struct __packed
+ {
+ uint16_t LogicalSectorsPerPhysicalSector : 4;
+ uint16_t Reserved0 : 8;
+ uint16_t LogicalSectorLongerThan256Words : 1;
+ uint16_t MultipleLogicalSectorsPerPhysicalSector : 1;
+ uint16_t Reserved1 : 2;
+ } PhysicalLogicalSectorSize;
+ uint16_t InterSeekDelay;
+ uint16_t WorldWideName[4];
+ uint16_t ReservedForWorldWideName128[4];
+ uint16_t ReservedForTlcTechnicalReport;
+ uint16_t WordsPerLogicalSector[2];
+ struct __packed
+ {
+ uint16_t ReservedForDrqTechnicalReport : 1;
+ uint16_t WriteReadVerify : 1;
+ uint16_t WriteUncorrectableExt : 1;
+ uint16_t ReadWriteLogDmaExt : 1;
+ uint16_t DownloadMicrocodeMode3 : 1;
+ uint16_t FreefallControl : 1;
+ uint16_t SenseDataReporting : 1;
+ uint16_t ExtendedPowerConditions : 1;
+ uint16_t Reserved0 : 6;
+ uint16_t WordValid : 2;
+ } CommandSetSupportExt;
+ struct __packed
+ {
+ uint16_t ReservedForDrqTechnicalReport : 1;
+ uint16_t WriteReadVerify : 1;
+ uint16_t WriteUncorrectableExt : 1;
+ uint16_t ReadWriteLogDmaExt : 1;
+ uint16_t DownloadMicrocodeMode3 : 1;
+ uint16_t FreefallControl : 1;
+ uint16_t SenseDataReporting : 1;
+ uint16_t ExtendedPowerConditions : 1;
+ uint16_t Reserved0 : 6;
+ uint16_t Reserved1 : 2;
+ } CommandSetActiveExt;
+ uint16_t ReservedForExpandedSupportandActive[6];
+ uint16_t MsnSupport : 2;
+ uint16_t ReservedWord127 : 14;
+ struct __packed
+ {
+ uint16_t SecuritySupported : 1;
+ uint16_t SecurityEnabled : 1;
+ uint16_t SecurityLocked : 1;
+ uint16_t SecurityFrozen : 1;
+ uint16_t SecurityCountExpired : 1;
+ uint16_t EnhancedSecurityEraseSupported : 1;
+ uint16_t Reserved0 : 2;
+ uint16_t SecurityLevel : 1;
+ uint16_t Reserved1 : 7;
+ } SecurityStatus;
+ uint16_t ReservedWord129[31];
+ struct __packed
+ {
+ uint16_t MaximumCurrentInMA : 12;
+ uint16_t CfaPowerMode1Disabled : 1;
+ uint16_t CfaPowerMode1Required : 1;
+ uint16_t Reserved0 : 1;
+ uint16_t Word160Supported : 1;
+ } CfaPowerMode1;
+ uint16_t ReservedForCfaWord161[7];
+ uint16_t NominalFormFactor : 4;
+ uint16_t ReservedWord168 : 12;
+ struct __packed
+ {
+ uint16_t SupportsTrim : 1;
+ uint16_t Reserved0 : 15;
+ } DataSetManagementFeature;
+ uint16_t AdditionalProductID[4];
+ uint16_t ReservedForCfaWord174[2];
+ uint16_t CurrentMediaSerialNumber[30];
+ struct __packed
+ {
+ uint16_t Supported : 1;
+ uint16_t Reserved0 : 1;
+ uint16_t WriteSameSuported : 1;
+ uint16_t ErrorRecoveryControlSupported : 1;
+ uint16_t FeatureControlSuported : 1;
+ uint16_t DataTablesSuported : 1;
+ uint16_t Reserved1 : 6;
+ uint16_t VendorSpecific : 4;
+ } SCTCommandTransport;
+ uint16_t ReservedWord207[2];
+ struct __packed
+ {
+ uint16_t AlignmentOfLogicalWithinPhysical : 14;
+ uint16_t Word209Supported : 1;
+ uint16_t Reserved0 : 1;
+ } BlockAlignment;
+ uint16_t WriteReadVerifySectorCountMode3Only[2];
+ uint16_t WriteReadVerifySectorCountMode2Only[2];
+ struct __packed
+ {
+ uint16_t NVCachePowerModeEnabled : 1;
+ uint16_t Reserved0 : 3;
+ uint16_t NVCacheFeatureSetEnabled : 1;
+ uint16_t Reserved1 : 3;
+ uint16_t NVCachePowerModeVersion : 4;
+ uint16_t NVCacheFeatureSetVersion : 4;
+ } NVCacheCapabilities;
+ uint16_t NVCacheSizeLSW;
+ uint16_t NVCacheSizeMSW;
+ uint16_t NominalMediaRotationRate;
+ uint16_t ReservedWord218;
+ struct __packed
+ {
+ uint8_t NVCacheEstimatedTimeToSpinUpInSeconds;
+ uint8_t Reserved;
+ } NVCacheOptions;
+ uint16_t WriteReadVerifySectorCountMode : 8;
+ uint16_t ReservedWord220 : 8;
+ uint16_t ReservedWord221;
+ struct __packed
+ {
+ uint16_t MajorVersion : 12;
+ uint16_t TransportType : 4;
+ } TransportMajorVersion;
+ uint16_t TransportMinorVersion;
+ uint16_t ReservedWord224[6];
+ uint32_t ExtendedNumberOfUserAddressableSectors[2];
+ uint16_t MinBlocksPerDownloadMicrocodeMode03;
+ uint16_t MaxBlocksPerDownloadMicrocodeMode03;
+ uint16_t ReservedWord236[19];
+ uint16_t Signature : 8;
+ uint16_t CheckSum : 8;
+ };
+
+ class Port
+ {
+ public:
+ PortType AHCIPortType;
+ HBAPort *HBAPortPtr;
+ uint8_t *Buffer;
+ uint8_t PortNumber;
+ ATA_IDENTIFY *IdentifyData;
+
+ Port(PortType Type, HBAPort *PortPtr, uint8_t PortNumber)
+ {
+ this->AHCIPortType = Type;
+ this->HBAPortPtr = PortPtr;
+ this->Buffer = static_cast(v0::AllocateMemory(DriverID, 1));
+ memset(this->Buffer, 0, PAGE_SIZE);
+ this->IdentifyData = static_cast(v0::AllocateMemory(DriverID, 1));
+ memset(this->IdentifyData, 0, PAGE_SIZE);
+ this->PortNumber = PortNumber;
+ }
+
+ ~Port()
+ {
+ v0::FreeMemory(DriverID, this->Buffer, 1);
+ }
+
+ void StartCMD()
+ {
+ while (HBAPortPtr->CommandStatus & HBA_PxCMD_CR)
+ v0::Yield(DriverID);
+ HBAPortPtr->CommandStatus |= HBA_PxCMD_FRE;
+ HBAPortPtr->CommandStatus |= HBA_PxCMD_ST;
+ }
+
+ void StopCMD()
+ {
+ HBAPortPtr->CommandStatus &= ~HBA_PxCMD_ST;
+ HBAPortPtr->CommandStatus &= ~HBA_PxCMD_FRE;
+ while (true)
+ {
+ if (HBAPortPtr->CommandStatus & HBA_PxCMD_FR)
+ continue;
+ if (HBAPortPtr->CommandStatus & HBA_PxCMD_CR)
+ continue;
+ break;
+ }
+ }
+
+ void Configure()
+ {
+ this->StopCMD();
+ void *CmdBase = v0::AllocateMemory(DriverID, 1);
+ HBAPortPtr->CommandListBase = (uint32_t)(uint64_t)CmdBase;
+ HBAPortPtr->CommandListBaseUpper = (uint32_t)((uint64_t)CmdBase >> 32);
+ memset(reinterpret_cast(HBAPortPtr->CommandListBase), 0, 1024);
+
+ void *FISBase = v0::AllocateMemory(DriverID, 1);
+ HBAPortPtr->FISBaseAddress = (uint32_t)(uint64_t)FISBase;
+ HBAPortPtr->FISBaseAddressUpper = (uint32_t)((uint64_t)FISBase >> 32);
+ memset(FISBase, 0, 256);
+
+ HBACommandHeader *CommandHeader = (HBACommandHeader *)((uint64_t)HBAPortPtr->CommandListBase + ((uint64_t)HBAPortPtr->CommandListBaseUpper << 32));
+ for (int i = 0; i < 32; i++)
+ {
+ CommandHeader[i].PRDTLength = 8;
+ void *CommandTableAddress = v0::AllocateMemory(DriverID, 1);
+ uint64_t Address = (uint64_t)CommandTableAddress + (i << 8);
+ CommandHeader[i].CommandTableBaseAddress = (uint32_t)(uint64_t)Address;
+ CommandHeader[i].CommandTableBaseAddressUpper = (uint32_t)((uint64_t)Address >> 32);
+ memset(CommandTableAddress, 0, 256);
+ }
+ this->StartCMD();
+
+ Identify();
+
+ trace("Port %d \"%x %x %x %x\" configured", PortNumber,
+ HBAPortPtr->Vendor[0], HBAPortPtr->Vendor[1],
+ HBAPortPtr->Vendor[2], HBAPortPtr->Vendor[3]);
+ }
+
+ bool ReadWrite(uint64_t Sector, uint32_t SectorCount, void *Buffer, bool Write)
+ {
+ if (this->AHCIPortType == PortType::SATAPI && Write == true)
+ {
+ trace("SATAPI port does not support write.");
+ return false;
+ }
+
+ uint32_t SectorL = (uint32_t)Sector;
+ uint32_t SectorH = (uint32_t)(Sector >> 32);
+
+ HBAPortPtr->InterruptStatus = 0xFFFFFFFF; /* Clear pending interrupt bits */
+
+ HBACommandHeader *CommandHeader = reinterpret_cast(HBAPortPtr->CommandListBase);
+ CommandHeader->CommandFISLength = sizeof(FIS_REG_H2D) / sizeof(uint32_t);
+ if (Write)
+ CommandHeader->Write = 1;
+ else
+ CommandHeader->Write = 0;
+ CommandHeader->PRDTLength = 1;
+
+ HBACommandTable *CommandTable = reinterpret_cast(CommandHeader->CommandTableBaseAddress);
+ memset(CommandTable, 0, sizeof(HBACommandTable) + (CommandHeader->PRDTLength - 1) * sizeof(HBAPRDTEntry));
+
+ CommandTable->PRDTEntry[0].DataBaseAddress = (uint32_t)(uint64_t)Buffer;
+ CommandTable->PRDTEntry[0].DataBaseAddressUpper = (uint32_t)((uint64_t)Buffer >> 32);
+
+#pragma GCC diagnostic push
+/* conversion from 'uint32_t' {aka 'unsigned int'} to 'unsigned int:22' may change value */
+#pragma GCC diagnostic ignored "-Wconversion"
+ CommandTable->PRDTEntry[0].ByteCount = (SectorCount << 9) - 1; /* 512 bytes per sector */
+#pragma GCC diagnostic pop
+
+ CommandTable->PRDTEntry[0].InterruptOnCompletion = 1;
+
+ FIS_REG_H2D *CommandFIS = (FIS_REG_H2D *)(&CommandTable->CommandFIS);
+
+ CommandFIS->FISType = FIS_TYPE_REG_H2D;
+ CommandFIS->CommandControl = 1;
+ if (Write)
+ CommandFIS->Command = ATA_CMD_WRITE_DMA_EX;
+ else
+ CommandFIS->Command = ATA_CMD_READ_DMA_EX;
+
+ CommandFIS->LBA0 = (uint8_t)SectorL;
+ CommandFIS->LBA1 = (uint8_t)(SectorL >> 8);
+ CommandFIS->LBA2 = (uint8_t)(SectorL >> 16);
+ CommandFIS->LBA3 = (uint8_t)SectorH;
+ CommandFIS->LBA4 = (uint8_t)(SectorH >> 8);
+ CommandFIS->LBA5 = (uint8_t)(SectorH >> 16);
+
+ CommandFIS->DeviceRegister = 1 << 6; /* LBA mode */
+ CommandFIS->CountLow = SectorCount & 0xFF;
+ CommandFIS->CountHigh = (SectorCount >> 8) & 0xFF;
+
+ uint64_t spinLock = 0;
+ while ((HBAPortPtr->TaskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && spinLock < 1000000)
+ spinLock++;
+
+ if (spinLock == 1000000)
+ {
+ trace("Port not responding.");
+ return false;
+ }
+
+ HBAPortPtr->CommandIssue = 1;
+
+ spinLock = 0;
+ int retries = 0;
+ while (true)
+ {
+ if (spinLock > 100000000)
+ {
+ trace("Port %d not responding. (%d)",
+ this->PortNumber, retries);
+
+ spinLock = 0;
+ retries++;
+ if (retries > 10)
+ return false;
+ }
+
+ if (HBAPortPtr->CommandIssue == 0)
+ break;
+ spinLock++;
+
+ if (HBAPortPtr->InterruptStatus & HBA_PxIS_TFES)
+ {
+ trace("Error reading/writing (%d).", Write);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ void Identify()
+ {
+ memset(this->IdentifyData, 0, sizeof(ATA_IDENTIFY));
+ HBACommandHeader *CommandHeader = reinterpret_cast(HBAPortPtr->CommandListBase);
+ CommandHeader->CommandFISLength = sizeof(FIS_REG_H2D) / sizeof(uint32_t);
+ CommandHeader->Write = 0;
+ CommandHeader->PRDTLength = 1;
+
+ HBACommandTable *CommandTable = reinterpret_cast(CommandHeader->CommandTableBaseAddress);
+ memset(CommandTable, 0, sizeof(HBACommandTable) + (CommandHeader->PRDTLength - 1) * sizeof(HBAPRDTEntry));
+
+ CommandTable->PRDTEntry[0].DataBaseAddress = (uint32_t)(uint64_t)this->IdentifyData;
+ CommandTable->PRDTEntry[0].DataBaseAddressUpper = (uint32_t)((uint64_t)this->IdentifyData >> 32);
+ CommandTable->PRDTEntry[0].ByteCount = 511;
+ CommandTable->PRDTEntry[0].InterruptOnCompletion = 1;
+
+ FIS_REG_H2D *CommandFIS = (FIS_REG_H2D *)(&CommandTable->CommandFIS);
+ CommandFIS->FISType = FIS_TYPE_REG_H2D;
+ CommandFIS->CommandControl = 1;
+ CommandFIS->Command = this->AHCIPortType == PortType::SATAPI ? ATAPI_CMD_IDENTIFY_PACKET : ATA_CMD_IDENTIFY;
+
+ HBAPortPtr->CommandIssue = 1;
+
+ while (HBAPortPtr->CommandIssue)
+ v0::Yield(DriverID);
+
+ if (HBAPortPtr->InterruptStatus & HBA_PxIS_TFES)
+ {
+ trace("Error reading IDENTIFY command.");
+ return;
+ }
+
+ if (IdentifyData->Signature != 0xA5)
+ trace("Port %d has no validity signature.", PortNumber);
+ else
+ {
+ uint8_t *ptr = (uint8_t *)IdentifyData;
+ uint8_t sum = 0;
+ for (size_t i = 0; i < sizeof(ATA_IDENTIFY); i++)
+ sum += ptr[i];
+ if (sum != 0)
+ {
+ trace("Port %d has invalid checksum.", PortNumber);
+ return;
+ }
+ else
+ trace("Port %d has valid checksum.", PortNumber);
+ }
+
+ char *Model = (char *)this->IdentifyData->ModelNumber;
+ char ModelSwap[41];
+ for (size_t i = 0; i < 40; i += 2)
+ {
+ ModelSwap[i] = Model[i + 1];
+ ModelSwap[i + 1] = Model[i];
+ }
+ ModelSwap[40] = 0;
+
+ trace("Port %d \"%s\" identified", PortNumber,
+ ModelSwap);
+ trace("Port %d is %s (%d rotation rate)", PortNumber,
+ IdentifyData->NominalMediaRotationRate == 1 ? "SSD" : "HDD",
+ IdentifyData->NominalMediaRotationRate);
+ }
+ };
+
+ std::unordered_map PortDevices;
+
+ const char *PortTypeName[] = {"None",
+ "SATA",
+ "SEMB",
+ "PM",
+ "SATAPI"};
+
+ PortType CheckPortType(HBAPort *Port)
+ {
+ uint32_t SataStatus = Port->SataStatus;
+ uint8_t InterfacePowerManagement = (SataStatus >> 8) & 0b111;
+ uint8_t DeviceDetection = SataStatus & 0b111;
+
+ if (DeviceDetection != HBA_PORT_DEV_PRESENT)
+ return PortType::None;
+ if (InterfacePowerManagement != HBA_PORT_IPM_ACTIVE)
+ return PortType::None;
+
+ switch (Port->Signature)
+ {
+ case SATA_SIG_ATAPI:
+ return PortType::SATAPI;
+ case SATA_SIG_ATA:
+ return PortType::SATA;
+ case SATA_SIG_PM:
+ return PortType::PM;
+ case SATA_SIG_SEMB:
+ return PortType::SEMB;
+ default:
+ return PortType::None;
+ }
+ }
+
+ int Open(struct Inode *, int, mode_t)
+ {
+ return 0;
+ }
+
+ int Close(struct Inode *)
+ {
+ return 0;
+ }
+
+ ssize_t Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset)
+ {
+ uint64_t sector = Offset / 512;
+ uint32_t sectorCount = uint32_t(Size / 512);
+ int num = Node->GetMinor();
+ bool ok = PortDevices[num]->ReadWrite(sector, sectorCount, Buffer, false);
+ return ok ? Size : 0;
+ }
+
+ ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset)
+ {
+ uint64_t sector = Offset / 512;
+ uint32_t sectorCount = uint32_t(Size / 512);
+ int num = Node->GetMinor();
+ bool ok = PortDevices[num]->ReadWrite(sector, sectorCount, (void *)Buffer, true);
+ return ok ? Size : 0;
+ }
+
+ const struct InodeOperations ops = {
+ .Lookup = nullptr,
+ .Create = nullptr,
+ .Remove = nullptr,
+ .Rename = nullptr,
+ .Read = Read,
+ .Write = Write,
+ .Truncate = nullptr,
+ .Open = Open,
+ .Close = Close,
+ .Ioctl = nullptr,
+ .ReadDir = nullptr,
+ .MkDir = nullptr,
+ .RmDir = nullptr,
+ .SymLink = nullptr,
+ .ReadLink = nullptr,
+ .Seek = nullptr,
+ .Stat = nullptr,
+ };
+
+ std::list Devices;
+ int Entry()
+ {
+ /* We loop through all the devices and initialize them */
+ for (auto &&dev : Devices)
+ {
+ PCIManager->InitializeDevice(dev, KernelPageTable);
+ PCI::PCIHeader0 *hdr0 = (PCI::PCIHeader0 *)dev.Header;
+ HBAMemory *hba = (HBAMemory *)(uintptr_t)hdr0->BAR5;
+ uint32_t portsImplemented = hba->PortsImplemented;
+ trace("AHCI ports implemented: %x", portsImplemented);
+
+ for (int i = 0; i < 32; i++)
+ {
+ if (!(portsImplemented & (1 << i)))
+ continue;
+
+ trace("Port %d implemented", i);
+
+ PortType portType = CheckPortType(&hba->Ports[i]);
+ switch (portType)
+ {
+ case PortType::SATA:
+ case PortType::SATAPI:
+ {
+ KPrint("%s drive found at port %d", PortTypeName[portType], i);
+ Port *port = new Port(portType, &hba->Ports[i], i);
+ dev_t ret = v0::RegisterDevice(DriverID, BLOCK_TYPE_HDD, &ops);
+ PortDevices[ret] = port;
+ break;
+ }
+ case PortType::SEMB:
+ case PortType::PM:
+ {
+ KPrint("Unimplemented drive type %s found at port %d",
+ PortTypeName[portType], i);
+ break;
+ }
+ default:
+ {
+ trace("Unsupported drive type %s found at port %d",
+ PortTypeName[portType], i);
+ break;
+ }
+ }
+ }
+ }
+
+ if (PortDevices.empty())
+ {
+ info("No valid AHCI device found.");
+ return -ENODEV;
+ }
+
+ trace("Initializing AHCI ports");
+ for (auto &&p : PortDevices)
+ p.second->Configure();
+
+ /* We don't use the interrupt handler now... maybe we will in the future */
+ // RegisterInterruptHandler(iLine(ctx->Device), (void *)OnInterruptReceived);
+
+ return 0;
+ }
+
+ int Final()
+ {
+ for (auto &&p : PortDevices)
+ {
+ p.second->StopCMD();
+ v0::UnregisterDevice(DriverID, p.first);
+ delete p.second;
+ }
+
+ /* Making sure that PortDevices is empty */
+ PortDevices.clear();
+ // ctx->Device->Header->Command |= PCI::PCI_COMMAND_INTX_DISABLE;
+
+ // std::list Devices = PCIManager->FindPCIDevice(VendorIDs, DeviceIDs);
+ // foreach (auto dev in Devices)
+ // Interrupts::RemoveHandler(OnInterruptReceived, iLine(dev));
+ return 0;
+ }
+
+ int Panic()
+ {
+ for (auto &&p : PortDevices)
+ p.second->StopCMD();
+ return 0;
+ }
+
+ int Probe()
+ {
+ Devices = PCIManager->FindPCIDevice(
+ {
+ 0x8086, /* Intel */
+ 0x15AD, /* VMware */
+ },
+ {
+ 0x2922, /* ICH9 */
+ 0x2829, /* ICH8 */
+ 0x07E0, /* SATA AHCI (VMware) */
+ });
+
+ if (Devices.empty())
+ {
+ trace("No AHCI device found.");
+ return -ENODEV;
+ }
+ return 0;
+ }
+
+ REGISTER_BUILTIN_DRIVER(ahci,
+ "Advanced Host Controller Interface Driver",
+ "enderice2",
+ 1, 0, 0,
+ Entry,
+ Final,
+ Panic,
+ Probe);
+}
diff --git a/Kernel/drivers/video/kdm/kdm.cpp b/Kernel/drivers/video/kdm/kdm.cpp
new file mode 100644
index 00000000..921ece1a
--- /dev/null
+++ b/Kernel/drivers/video/kdm/kdm.cpp
@@ -0,0 +1,128 @@
+/*
+ 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 .
+*/
+
+#include
+
+extern Driver::Manager *DriverManager;
+namespace Driver::KernelDisplayManager
+{
+ dev_t DriverID;
+
+ struct
+ {
+ dev_t kdm;
+ } ids;
+
+ int Open(struct Inode *Node, int Flags, mode_t Mode)
+ {
+ dev_t min = Node->GetMinor();
+ if (min == ids.kdm)
+ return -ENOSYS;
+
+ return -ENODEV;
+ }
+
+ int Close(struct Inode *Node)
+ {
+ dev_t min = Node->GetMinor();
+ if (min == ids.kdm)
+ return -ENOSYS;
+
+ return -ENODEV;
+ }
+
+ int Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
+ {
+ dev_t min = Node->GetMinor();
+ if (min == ids.kdm)
+ return -ENOSYS;
+
+ return -ENODEV;
+ }
+
+ ssize_t Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset)
+ {
+ dev_t min = Node->GetMinor();
+ if (min == ids.kdm)
+ return -ENOSYS;
+
+ return -ENODEV;
+ }
+
+ ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset)
+ {
+ dev_t min = Node->GetMinor();
+ if (min == ids.kdm)
+ return -ENOSYS;
+
+ return -ENODEV;
+ }
+
+ off_t Seek(struct Inode *Node, off_t Offset) { return -ENOSYS; }
+ int Stat(struct Inode *Node, struct kstat *Stat) { return -ENOSYS; }
+
+ const struct InodeOperations ops = {
+ .Lookup = nullptr,
+ .Create = nullptr,
+ .Remove = nullptr,
+ .Rename = nullptr,
+ .Read = Read,
+ .Write = Write,
+ .Truncate = nullptr,
+ .Open = Open,
+ .Close = Close,
+ .Ioctl = Ioctl,
+ .ReadDir = nullptr,
+ .MkDir = nullptr,
+ .RmDir = nullptr,
+ .SymLink = nullptr,
+ .ReadLink = nullptr,
+ .Seek = Seek,
+ .Stat = Stat,
+ };
+
+ int Entry()
+ {
+ mode_t mode = 0;
+
+ /* c rw- rw- rw- */
+ mode = S_IRUSR | S_IWUSR |
+ S_IRGRP | S_IWGRP |
+ S_IROTH | S_IWOTH |
+ S_IFCHR;
+ ids.kdm = DriverManager->CreateDeviceFile(DriverID, "kdm", mode, &ops);
+ return 0;
+ }
+
+ int Final()
+ {
+ DriverManager->UnregisterDevice(DriverID, ids.kdm);
+ return 0;
+ }
+
+ int Panic() { return 0; }
+ int Probe() { return 0; }
+
+ REGISTER_BUILTIN_DRIVER(kdm,
+ "Kernel Display Manager Driver",
+ "enderice2",
+ 1, 0, 0,
+ Entry,
+ Final,
+ Panic,
+ Probe);
+}