Here is the code.
Code: Select all
#ifndef USB_h
#define USB_h
#include "StandardTypedef.h"
#include "VirtualMemoryPrivate.h"
struct QTDToken
{
byte status;
byte pid: 2;
byte errorCounter: 2;
byte currPage: 3;
byte interrupt: 1;
word bytes: 15;
word dataToggle: 1;
};
struct QTD
{
dword next;
dword nextAlt;
struct QTDToken token;
dword buffer0;
dword buffer1;
dword buffer2;
dword buffer3;
dword buffer4;
};
struct QHD
{
dword HorizontalPointer;
dword deviceAddress: 7;
dword inactive: 1;
dword endpoint: 4;
dword endpointSpeed: 2;
dword dataToggleControl: 1;
dword H: 1;
dword maxPacketLength: 11;
dword controlEndpointFlag: 1;
dword nakCountReload: 4;
byte interruptScheduleMask;
byte splitCompletionMask;
word hubaddr: 7;
word portNumber: 7;
word mult: 2;
dword current;
struct QTD qtd;
};
struct EHCIRequest
{
byte type;
byte request;
byte valueLo;
byte valueHi;
word index;
word length;
};
void CreateQH(void* address, dword horizontalPointer, void* firstQTDPhys, byte H, dword device, dword endpoint, dword packetSize)
{
QHD* head = (QHD*)address;
memset(address, 0, sizeof(QHD));
head->HorizontalPointer = horizontalPointer | 2;
head->deviceAddress = device;
head->inactive = 0;
head->endpoint = endpoint;
head->endpointSpeed = 2;
head->dataToggleControl = 1;
head->H = H;
head->maxPacketLength = packetSize;
head->controlEndpointFlag = 0;
head->nakCountReload = 15;
head->interruptScheduleMask = 0;
head->splitCompletionMask = 0;
head->hubaddr = 0;
head->portNumber = 0;
head->mult = 1;
if (firstQTDPhys == 0)
head->qtd.next = 0x1;
else
{
dword physNext = VirtualMemory::PhysicalAddress((dword)firstQTDPhys);;
head->qtd.next = physNext;
}
}
QTD* AllocQTD(dword next)
{
QTD* td = 0;
VirtualMemory::AllocateRandomPage(true, (dword*)&td);
memset(td, 0, sizeof(QTD));
if (next != 0x1)
td->next = VirtualMemory::PhysicalAddress(next);
else
td->next = 1;
return td;
}
dword AllocQTDBuffer(QTD* td)
{
void* data = 0;
VirtualMemory::AllocateRandomPage(true, (dword*)&data);
memset(data, 0, 4096);
td->buffer0 = VirtualMemory::PhysicalAddress((dword)data);
td->buffer1 = td->buffer2 = td->buffer3 = td->buffer4 = 0;
return (dword)data;
}
void* CreateQTD_SETUP(dword next, bool toggle, dword tokenBytes, dword type, dword req, dword hiVal, dword loVal, dword index, dword length)
{
QTD* td = AllocQTD(next);
td->nextAlt = 0x1;
td->token.status = 0x80;
td->token.pid = 2;
td->token.errorCounter = 3;
td->token.currPage = 0;
td->token.interrupt = 1;
td->token.bytes = tokenBytes;
td->token.dataToggle = toggle;
dword Buffer = AllocQTDBuffer(td);
EHCIRequest* request = (EHCIRequest*)Buffer;
request->type = type;
request->request = req;
request->valueHi = hiVal;
request->valueLo = loVal;
request->index = index;
request->length = length;
return (void*)td;
}
void* CreateQTD_IO(dword next, byte direction, bool toggle, dword tokenBytes, dword* buff)
{
QTD* td = AllocQTD(next);
td->nextAlt = 0x1;
td->token.status = 0x80;
td->token.pid = direction;
td->token.errorCounter = 3;
td->token.currPage = 0;
td->token.interrupt = 1;
td->token.bytes = tokenBytes;
td->token.dataToggle = toggle;
dword Buffer = AllocQTDBuffer(td);
*buff = Buffer;
return (void*)td;
}
#endif USB_h
Code: Select all
#include "LoadKernel.h"
#include "Bootinfo.h"
#define GetPhysicalAddress(x) (*x & ~0xFFF)
void EHCI();
void Main(Bootinfo::MultibootInfo* bootinfo, dword kernelsize)
{
LoadKernel::InitializeSystems(bootinfo, kernelsize);
EHCI();
Shell::RunShell();
LoadKernel::DeInitializeSystems();
}
void Beep(byte on)
{
OutPort(0x61, on * 163);
}
// /USB Testing -
// /-------------
// /-------------
// /-------------
#include "USB.h"
dword BaseRegister = 0;
dword OperationalRegisterBase = 0;
byte NumberOfPorts = 0;
dword PortBase = 0;
bool USBINTflag = false;
struct CapibilityRegisters
{
byte CAPLENGTH;
byte RESERVED;
word HCIVERSION;
dword HCSPARAMS;
dword HCCPARAMS;
byte HCSPPORTROUTE[8];
};
struct OperationRegisters
{
dword USBCMD;
dword USBSTS;
dword USBINTR;
dword FRINDEX;
dword CTRLDSSEGMENT;
dword PERIODICLISTBASE;
dword ASYNCLISTADDR;
byte Reserved[36];
dword CONFIGFLAG;
};
OperationRegisters* op;
dword eecp = 0;
void EHCIHandler()
{
_asm add esp, 12;
_asm pushad;
Video::Print("\nInterrupt Recieved");
if (op->USBSTS & 1)
{
USBINTflag = true;
Video::Print("\nUSB Interrupt\n");
op->USBSTS |= 1;
}
if (op->USBSTS & 2)
{
Video::Print("\nUSB Error Interrupt\n");
op->USBSTS |= 2;
}
if (op->USBSTS & 4)
{
Video::Print("\nPort Change Interrupt\n");
op->USBSTS |= 4;
}
if (op->USBSTS & 0x10)
{
Video::Print("\nHost System Error Interrupt\n");
op->USBSTS |= 0x10;
Video::Print("Halting System...");
for (;;);
}
if (op->USBSTS & 0x20)
{
Video::Print("\nInterrupt On Async Advance\n");
op->USBSTS |= 0x20;
}
OutPort(0xA0, 0x20);
OutPort(0x20, 0x20);
_asm popad;
_asm iretd;
}
dword ReadPci(byte bus, byte device, byte funct, byte offset)
{
dword address = (bus << 16) | (device << 11) | (funct << 8) | (offset & 0xFC) | (0x80000000);
OutPortDWord(0xCF8, address);
return InPortDWord(0xCFC);
}
dword ReadPciBytes(byte bus, byte device, byte funct, byte offset)
{
byte length = offset >> 8;
byte regoff = offset & 0xFF;
byte reg = regoff & 0xFC;
byte offset2 = regoff % 0x4;
OutPortDWord(0xCF8, 0x80000000 | (bus << 16) | (device << 11) | (funct << 8) | reg);
dword readVal = InPortDWord(0xCFC) >> (8 * offset2);
switch (length)
{
case 1:
readVal &= 0x000000FF;
break;
case 2:
readVal &= 0x0000FFFF;
break;
case 4:
readVal &= 0xFFFFFFFF;
break;
}
return readVal;
}
void WritePci(byte bus, byte device, byte funct, byte offset, byte data)
{
OutPortDWord(0xCF8, (bus << 16) | (device << 11) | (funct << 8) | (offset & 0xFC) | (0x80000000));
OutPortDWord(0xCFC, data);
}
void WritePciBytes(byte bus, byte device, byte func, byte reg, byte val)
{
OutPortDWord(0xCF8,
0x80000000
| (bus << 16)
| (device << 11)
| (func << 8)
| (reg & 0xFC));
OutPort(0xCFC + (reg & 0x3), val);
}
void ShowUSBSTS()
{
if (op->USBSTS & 1) { Video::Print("\n\nUSB Interrupt");}
if (op->USBSTS & 2) { Video::Print("\nUSB Error Interrupt");}
if (op->USBSTS & 4) { Video::Print("\nPort Change Detect");}
if (op->USBSTS & 8) { Video::Print("\nFrame List Rollover");}
if (op->USBSTS & 0x10) { Video::Print("\nHost System Error");}
if (op->USBSTS & 0x20) { Video::Print("\nInterrupt on Async Advance");}
if (op->USBSTS & 0x1000) { Video::Print("\nHCHalted");}
if (op->USBSTS & 0x2000) { Video::Print("\nReclamation");}
if (op->USBSTS & 0x4000) { Video::Print("\nPeriodic Schedule Status");}
if (op->USBSTS & 0x8000) { Video::Print("\nAsynchronous Schedule Status\n\n");}
}
void DisplayEHCIPCI()
{
dword classcode = ReadPci(0, 3, 3, 8);
Video::Print("Class code: ");
Video::PrintNumber((classcode >> 24) & 0xFF, 16);
Video::Print(" Sub class code: ");
Video::PrintNumber((classcode >> 16) & 0xFF, 16);
Video::Print(" Programming Interface: ");
Video::PrintNumber((classcode >> 8) & 0xFF, 16);
dword commandRegister = ReadPci(0, 3, 3, 4);
Video::Print(" Command Register: ");
Video::PrintNumber(commandRegister, 10);
dword irqline = ReadPci(0, 3, 3, 0x3C);
irqline &= 0xFF;
Video::Print(" IRQ: ");
Video::PrintNumber(irqline, 10);
IDT::InstallIRQ((dword)&EHCIHandler, 0x8, PModeGate | PresentGate, 41);
Video::Print("\n\n");
}
void ReadBaseRegister()
{
dword base = ReadPci(0, 3, 3, 0x10);
Video::Print("Base register address: 0x");
Video::PrintNumber(base, 16);
Video::Print(" - ");
Video::PrintNumber(base, 10);
VirtualMemory::AllocateRandomPage(true, &BaseRegister);
VirtualMemory::FreePage(BaseRegister);
VirtualMemory::MapPage((void*)base, (void*)BaseRegister);
Video::Print("\n\n");
}
void ReadPCIReleaseNumber()
{
dword num = ReadPci(0, 3, 3, 0x60);
Video::Print("Serial Bus Release Number: ");
Video::PrintNumber((byte)num, 16);
Video::Print("\n\n");
}
void CapibilityPortsHCS(dword HCSPARAMS)
{
byte NOPs = (HCSPARAMS & 0xF);
Video::Print("\nNOPs: ");
Video::PrintNumber(NOPs, 10);
NumberOfPorts = NOPs;
byte DebugPortNumber = (HCSPARAMS & 0xFF000000);
Video::Print(" Debug Port: ");
Video::PrintNumber(DebugPortNumber, 10);
byte PortPower = ((HCSPARAMS >> 4) & 1);
Video::Print(" Port Power: ");
Video::PrintNumber(PortPower, 10);
}
void ParseCapibilityRegisters()
{
CapibilityRegisters cap = *((CapibilityRegisters*)BaseRegister);
Video::Print("CAPLENGTH: ");
Video::PrintNumber(cap.CAPLENGTH, 10);
OperationalRegisterBase = (BaseRegister + cap.CAPLENGTH);
Video::Print("\nHCIVERSION: ");
Video::PrintNumber(cap.HCIVERSION, 16);
CapibilityPortsHCS(cap.HCSPARAMS);
eecp = (((cap.HCCPARAMS) >> 8) & 0xFF);
Video::Print("\n\n");
}
void OperateRegisters()
{
op = ((OperationRegisters*)OperationalRegisterBase);
op->USBCMD &= ~1;
while (!(op->USBSTS & 0x1000))
{
PIT::WaitMilliseconds(30);
}
op->USBCMD |= 2;
while ((op->USBCMD & 2) != 0)
{
PIT::WaitMilliseconds(20);
}
Video::Print("Host Controller Reset\n\n");
op->CTRLDSSEGMENT = 0;
op->USBINTR = 0x20 | 0x10 | 4 | 2 | 1;
if (op->USBSTS & 0x1000)
{
op->USBCMD |= 1;
while (op->USBSTS & 0x1000)
{
}
Video::Print("Host Controller started.");
}
op->CONFIGFLAG = 1;
PIT::WaitMilliseconds(100);
PortBase = OperationalRegisterBase + 0x44;
}
dword* SelectPort(byte portnumber)
{
return (dword*)(PortBase + (portnumber * 4));
}
byte Ports()
{
while (true)
{
for (int i = 0; i < NumberOfPorts; i++)
{
dword* base = SelectPort(i);
dword enable = (((*base) >> 0) & 1);
if (enable)
{
Video::Print("Device has been inserted into port: ");
Video::PrintNumber(i, 10);
Beep(1);
op->USBINTR = 0;
*base |= 256;
PIT::WaitMilliseconds(100);
*base &= ~256;
PIT::WaitMilliseconds(50);
op->USBINTR = 0x20 | 0x10 | 4 | 2 | 1;
Beep(0);
Video::Print(" With data code: ");
Video::PrintNumber(*base, 10);
Video::NewLine();
return i;
}
}
}
}
void DoAsync()
{
op->USBSTS |= 1;
USBINTflag = false;
op->USBCMD |= (0x20);
while (!(op->USBSTS & 0x8000))
{
PIT::WaitMilliseconds(20);
}
op->USBCMD |= 0x40;
PIT::WaitMilliseconds(100);
while (!USBINTflag)
{
PIT::WaitMilliseconds(20);
}
op->USBSTS |= 1;
USBINTflag = false;
op->USBCMD &= ~0x20;
Video::Print("\nTransaction Completed\n");
}
struct DeviceDesc
{
byte Length;
byte DescType;
word bcdUSB;
byte DeviceClass;
byte SubClass;
byte Protocal;
byte MaxPacketSize;
word Vendor;
word Product;
word bcdDevice;
byte Manufacturer;
byte ProductIndex;
byte Serial;
byte NumConfigs;
};
void NextTransAction()
{
void* virtAList = 0;
VirtualMemory::AllocateRandomPage(true, (dword*)&virtAList);
op->USBCMD &= ~0x20;
op->ASYNCLISTADDR = VirtualMemory::PhysicalAddress((dword)virtAList);
dword buff = 0;
void* next = CreateQTD_IO(1, 0, 1, 0, &buff);
next = CreateQTD_IO((dword)next, 1, 1, 18, &buff);
void* setup = CreateQTD_SETUP((dword)next, 0, 8, 0x80, 6, 1, 0, 0, 18);
CreateQH(virtAList, VirtualMemory::PhysicalAddress((dword)virtAList), setup, 1, 5, 0, 64);
DoAsync();
DeviceDesc* dev = (DeviceDesc*)((dword*)buff);
Video::Print("\n\n\nDevice Descriptor\n\n");
Video::PrintNumber(dev->Length, 10);
Video::NewLine();
Video::PrintNumber(dev->DescType, 10);
Video::NewLine();
Video::PrintNumber(dev->bcdUSB, 10);
Video::NewLine();
Video::PrintNumber(dev->DeviceClass, 10);
Video::NewLine();
Video::PrintNumber(dev->SubClass, 10);
Video::NewLine();
Video::PrintNumber(dev->Protocal, 10);
Video::NewLine();
Video::PrintNumber(dev->MaxPacketSize, 10);
Video::NewLine();
Video::PrintNumber(dev->Vendor, 10);
Video::NewLine();
Video::PrintNumber(dev->Product, 10);
Video::NewLine();
Video::PrintNumber(dev->bcdDevice, 10);
Video::NewLine();
Video::PrintNumber(dev->Manufacturer, 10);
Video::NewLine();
Video::PrintNumber(dev->ProductIndex, 10);
Video::NewLine();
Video::PrintNumber(dev->Serial, 10);
Video::NewLine();
Video::PrintNumber(dev->NumConfigs, 10);
for (;;);
}
void DoTransactions(byte port)
{
Video::Print("Press any key to continue...\n");
Keyboard::WaitForKeyDown();
void* virtAList = 0;
VirtualMemory::AllocateRandomPage(true, (dword*)&virtAList);
op->USBCMD &= ~0x20;
op->ASYNCLISTADDR = VirtualMemory::PhysicalAddress((dword)virtAList);
dword buff = 0;
void* next = CreateQTD_IO(1, 1, 1, 0, &buff);
void* setupQtd = CreateQTD_SETUP((dword)next, 0, 8, 0, 5, 0, 5, 0, 0);
CreateQH(virtAList, VirtualMemory::PhysicalAddress((dword)virtAList), setupQtd, 1, 0, 0, 64);
DoAsync();
NextTransAction();
}
void EHCI()
{
// PCI
DisplayEHCIPCI();
ReadBaseRegister();
ReadPCIReleaseNumber();
//EHCI
ParseCapibilityRegisters();
OperateRegisters();
byte port = Ports();
DoTransactions(port);
Video::Print("\n\n\nPress any key to shut down...\n\n");
Keyboard::WaitForKeyDown();
LoadKernel::DeInitializeSystems();
}
The code enters in at Main().
Remember this is just a test driver so it is horribly unorganised and has magic numbers everywhere so if you have any questions, I can answer them for you.