ARM_CPUNAME = gerneric-armv7
-CC = arm-elf-gcc -mcpu=$(ARM_CPUNAME)
-AS = arm-elf-gcc -mcpu=$(ARM_CPUNAME) -c
-LD = arm-elf-ld
+CC = arm-eabi-gcc -mcpu=$(ARM_CPUNAME)
+AS = arm-eabi-gcc -mcpu=$(ARM_CPUNAME) -c
+LD = arm-eabi-ld
OBJDUMP = arm-elf-objdump
DISASM = $(OBJDUMP) -d -S
ARCHDIR = armv7
OBJ += drvutil_video.o drvutil_disk.o
OBJ += messages.o modules.o syscalls.o system.o
OBJ += threads.o mutex.o semaphore.o workqueue.o events.o rwlock.o
-OBJ += drv/zero-one.o drv/proc.o drv/fifo.o drv/iocache.o drv/pci.o
+OBJ += drv/zero-one.o drv/proc.o drv/fifo.o drv/iocache.o drv/pci.o drv/vpci.o
OBJ += drv/vterm.o drv/vterm_font.o drv/vterm_vt100.o drv/vterm_output.o drv/vterm_input.o drv/vterm_termbuf.o
OBJ += binary.o bin/elf.o bin/pe.o
OBJ += vfs/main.o vfs/open.o vfs/acls.o vfs/dir.o vfs/io.o vfs/mount.o
@echo BUILD_NUM = $$(( $(BUILD_NUM) + 1 )) > Makefile.BuildNum.$(ARCH)
$(POSTBUILD)
@cp $(BIN) $(BIN)_
- @-$(STRIP) $(BIN)_
+ @$(STRIP) $(BIN)_ || true
@gzip -c $(BIN)_ > $(GZBIN)
@$(RM) $(BIN)_
A_OBJ = start.ao main.o lib.o lib.ao time.o pci.o debug.o
A_OBJ += mm_phys.o mm_virt.o proc.o proc.ao
+A_OBJ += vpci_$(PLATFORM).o
#main.c: Makefile.BuildNum.$(ARCH)
// === PROTOTYPES ===
Uint64 __divmod64(Uint64 Num, Uint64 Den, Uint64 *Rem);
Uint32 __divmod32(Uint32 Num, Uint32 Den, Uint32 *Rem);
+#if 0
Uint64 __udivdi3(Uint64 Num, Uint64 Den);
Uint64 __umoddi3(Uint64 Num, Uint64 Den);
Uint32 __udivsi3(Uint32 Num, Uint32 Den);
Uint32 __umodsi3(Uint32 Num, Uint32 Den);
Sint32 __divsi3(Sint32 Num, Sint32 Den);
Sint32 __modsi3(Sint32 Num, Sint32 Den);
+#endif
+void abort(void);
// === CODE ===
void *memcpy(void *_dest, const void *_src, size_t _length)
return ret;
}
+#if 0
// Unsigned Divide 64-bit Integer
Uint64 __udivdi3(Uint64 Num, Uint64 Den)
{
DivMod32S(Num, Den, &rem);
return rem;
}
+#endif
+
+void abort(void)
+{
+ for(;;);
+}
*(.usertext)
}
. += gUsertextPhysStart + _kernel_base - _usertext_vbase;
-
+
+ /DISCARD/ : { *(.ARM.extab.init .ARM.exidx.init) }
+ .ARM.extab : AT( ADDR(.ARM.extab) - _kernel_base)
+ {
+ *(.ARM.extab* .gnu.linkonce.armextab.*)
+ }
+ PROVIDE_HIDDEN ( __exidx_start = . );
+ .ARM.exidx : AT( ADDR(.ARM.exidx) - _kernel_base)
+ {
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ }
+ PROVIDE_HIDDEN ( __exidx_end = . );
+ .eh_frame_hdr : AT( ADDR(.eh_frame_hdr) - _kernel_base) {
+ *(.eh_frame_hdr)
+ }
+ .eh_frame : AT( ADDR(.eh_frame) - _kernel_base) ONLY_IF_RO
+ {
+ KEEP (*(.eh_frame))
+ }
+ .gcc_except_table : AT( ADDR(.gcc_except_table) - _kernel_base) ONLY_IF_RO
+ {
+ *(.gcc_except_table .gcc_except_table.*)
+ }
+
/* 0x4000 (4 pages) alignment needed for root table */
- .data ALIGN(0x4000) : AT( ADDR(.data) - _kernel_base )
+ . = ALIGN(0x4000);
+ .data : AT( ADDR(.data) - _kernel_base )
{
*(.padata)
*(.data*)
*(KMODULES)
gKernelModulesEnd = .;
}
- .bss : AT( ADDR(.bss) - _kernel_base )
+ .bss ALIGN(0x1000) : AT( ADDR(.bss) - _kernel_base )
{
bss_start = .;
*(.bss*)
}
// --- Exports ---
-tPAddr MM_GetPhysAddr(tVAddr VAddr)
+tPAddr MM_GetPhysAddr(const void *Ptr)
{
tMM_PageInfo pi;
- if( MM_int_GetPageInfo(VAddr, &pi) )
+ if( MM_int_GetPageInfo((tVAddr)Ptr, &pi) )
return 0;
- return pi.PhysAddr | (VAddr & ((1 << pi.Size)-1));
+ return pi.PhysAddr | ((tVAddr)Ptr & ((1 << pi.Size)-1));
}
Uint MM_GetFlags(tVAddr VAddr)
return new->TID;
}
-int Proc_SpawnWorker( void (*Fnc)(void*), void *Ptr )
+tThread *Proc_SpawnWorker( void (*Fnc)(void*), void *Ptr )
{
tThread *new;
Uint32 sp;
if(!new->KernelStack) {
// TODO: Delete thread
Log_Error("Proc", "Unable to allocate kernel stack");
- return -1;
+ return NULL;
}
sp = new->KernelStack;
Threads_AddActive(new);
- return new->TID;
+ return new;
}
tTID Proc_NewKThread( void (*Fnc)(void*), void *Ptr )
--- /dev/null
+/*
+ * Acess2 Kernel ARMv7 Port
+ * - By John Hodge (thePowersGang)
+ *
+ * vpci_tegra2.c
+ * - Tegra2 VPCI Definitions
+ */
+#include <virtual_pci.h>
+
+// === PROTOTYPES ===
+
+// === GLOBALS ===
+tVPCI_Device gaVPCI_Devices[] = {
+ {
+ .Vendor=0x0ACE,.Device=0x1100,
+ .Class = 0x0C032000, // Serial, USB, ECHI
+ .BARs = {0xC5000000,0,0,0,0,0},
+ .IRQ = 0*32+20,
+ },
+ {
+ .Vendor=0x0ACE,.Device=0x1100,
+ .Class = 0x0C032000, // Serial, USB, ECHI
+ .BARs = {0xC5004000,0,0,0,0,0},
+ .IRQ = 0*32+21,
+ },
+ {
+ .Vendor=0x0ACE,.Device=0x1100,
+ .Class = 0x0C032000, // Serial, USB, ECHI
+ .BARs = {0xC5008000,0,0,0,0,0},
+ .IRQ = 4*32+1,
+ }
+};
+int giVPCI_DeviceCount = sizeof(gaVPCI_Devices)/sizeof(gaVPCI_Devices[0]);
+
A_OBJ = start.ao main.o mboot.o lib.o desctab.ao errors.o irq.o
A_OBJ += mm_phys.o mm_virt.o
A_OBJ += proc.o proc.ao time.o vm8086.o
-A_OBJ += kpanic.o pci.o
+A_OBJ += kpanic.o pci.o vpci.o
gsBootCmdLine = (char*)(mbInfo->CommandLine + KERNEL_BASE);
// Adjust Multiboot structure address
- mbInfo = (void*)( (Uint)MbInfoPtr + KERNEL_BASE );
+ mbInfo = (void*)( (tVAddr)MbInfoPtr + KERNEL_BASE );
nPMemMapEnts = Multiboot_LoadMemoryMap(mbInfo, KERNEL_BASE, pmemmap, MAX_PMEMMAP_ENTS,
KERNEL_LOAD, (tVAddr)&gKernelEnd - KERNEL_BASE);
* mboot.c
* - Multiboot Support
*/
-#define DEBUG 0
+#define DEBUG 1
#include <acess.h>
#include <mboot.h>
ENTER("pMBInfo pMapOffset pMap iMapSize PKStart PKEnd",
MBInfo, MapOffset, Map, MapSize, KStart, KEnd);
- // Build up memory map
- nPMemMapEnts = 0;
- while( ent < last && nPMemMapEnts < MapSize )
+ // Check that the memory map is present
+ if( MBInfo->Flags & (1 << 6) )
{
- tPMemMapEnt *nent = &Map[nPMemMapEnts];
- nent->Start = ent->Base;
- nent->Length = ent->Length;
- switch(ent->Type)
+ // Build up memory map
+ nPMemMapEnts = 0;
+ while( ent < last && nPMemMapEnts < MapSize )
{
- case 1:
- nent->Type = PMEMTYPE_FREE;
- break;
- default:
- nent->Type = PMEMTYPE_RESERVED;
- break;
+ tPMemMapEnt *nent = &Map[nPMemMapEnts];
+ if( !MM_GetPhysAddr(ent) )
+ Log_KernelPanic("MBoot", "MBoot Map entry %i addres bad (%p)",
+ nPMemMapEnts, ent);
+
+ nent->Start = ent->Base;
+ nent->Length = ent->Length;
+ switch(ent->Type)
+ {
+ case 1:
+ nent->Type = PMEMTYPE_FREE;
+ break;
+ default:
+ nent->Type = PMEMTYPE_RESERVED;
+ break;
+ }
+ nent->NUMADomain = 0;
+
+ nPMemMapEnts ++;
+ ent = (void*)( (tVAddr)ent + ent->Size + 4 );
}
- nent->NUMADomain = 0;
-
- nPMemMapEnts ++;
- ent = (void*)( (tVAddr)ent + ent->Size + 4 );
+ }
+ else if( MBInfo->Flags & (1 << 0) )
+ {
+ Log_Warning("MBoot", "No memory map passed, using mem_lower and mem_upper");
+ nPMemMapEnts = 2;
+ Map[0].Start = 0;
+ Map[0].Length = MBInfo->LowMem * 1024;
+ Map[0].Type = PMEMTYPE_FREE;
+ Map[0].NUMADomain = 0;
+
+ Map[1].Start = 0x100000;
+ Map[1].Length = MBInfo->HighMem * 1024;
+ Map[1].Type = PMEMTYPE_FREE;
+ Map[1].NUMADomain = 0;
+ }
+ else
+ {
+ Log_KernelPanic("MBoot", "Multiboot didn't pass memory information");
}
// Ensure it's valid
KStart, KEnd - KStart
);
- // Replace modules with PMEMTYPE_USED
- nPMemMapEnts = PMemMap_MarkRangeUsed(Map, nPMemMapEnts, MapSize,
- MBInfo->Modules, MBInfo->ModuleCount*sizeof(tMBoot_Module)
- );
- tMBoot_Module *mods = (void*)( (tVAddr)MBInfo->Modules + MapOffset);
- for( int i = 0; i < MBInfo->ModuleCount; i ++ )
+ PMemMap_DumpBlocks(Map, nPMemMapEnts);
+
+ // Check if boot modules were passed
+ if( MBInfo->Flags & (1 << 3) )
{
- nPMemMapEnts = PMemMap_MarkRangeUsed(
- Map, nPMemMapEnts, MapSize,
- mods->Start, mods->End - mods->Start
+ // Replace modules with PMEMTYPE_USED
+ nPMemMapEnts = PMemMap_MarkRangeUsed(Map, nPMemMapEnts, MapSize,
+ MBInfo->Modules, MBInfo->ModuleCount*sizeof(tMBoot_Module)
);
+ LOG("MBInfo->Modules = %x", MBInfo->Modules);
+ tMBoot_Module *mods = (void*)( (tVAddr)MBInfo->Modules + MapOffset);
+ for( int i = 0; i < MBInfo->ModuleCount; i ++ )
+ {
+ LOG("&mods[%i] = %p", i, &mods[i]);
+ LOG("mods[i] = {0x%x -> 0x%x}", mods[i].Start, mods[i].End);
+ nPMemMapEnts = PMemMap_MarkRangeUsed(
+ Map, nPMemMapEnts, MapSize,
+ mods[i].Start, mods[i].End - mods[i].Start
+ );
+ }
}
-
+
// Debug - Output map
PMemMap_DumpBlocks(Map, nPMemMapEnts);
tBootModule *Multiboot_LoadModules(tMBoot_Info *MBInfo, tVAddr MapOffset, int *ModuleCount)
{
+ if( !(MBInfo->Flags & (1 << 3)) ) {
+ *ModuleCount = 0;
+ Log_Log("Arch", "No multiboot module information passed");
+ return NULL;
+ }
+
tMBoot_Module *mods = (void*)( MBInfo->Modules + MapOffset );
*ModuleCount = MBInfo->ModuleCount;
tBootModule *ret = malloc( MBInfo->ModuleCount * sizeof(*ret) );
* \fn int Proc_SpawnWorker(void)
* \brief Spawns a new worker thread
*/
-int Proc_SpawnWorker(void (*Fcn)(void*), void *Data)
+tThread *Proc_SpawnWorker(void (*Fcn)(void*), void *Data)
{
tThread *new;
Uint stack_contents[4];
new = Threads_CloneThreadZero();
if(!new) {
Warning("Proc_SpawnWorker - Out of heap space!\n");
- return -1;
+ return NULL;
}
// Create the stack contents
new->Status = THREAD_STAT_PREINIT;
Threads_AddActive( new );
- return new->TID;
+ return new;
}
/**
--- /dev/null
+/*
+ * Acess2 Kernel x86 Port
+ * - By John Hodge (thePowersGang)
+ *
+ * vpci.c
+ * - Virtual PCI Bus
+ */
+#include <virtual_pci.h>
+
+// === GLOBALS ===
+ int giVPCI_DeviceCount = 0;
+tVPCI_Device gaVPCI_Devices[0];
+
* \fn int Proc_SpawnWorker(void)
* \brief Spawns a new worker thread
*/
-int Proc_SpawnWorker(void (*Fcn)(void*), void *Data)
+tThread *Proc_SpawnWorker(void (*Fcn)(void*), void *Data)
{
tThread *new, *cur;
Uint stack_contents[3];
new = Threads_CloneThreadZero();
if(!new) {
Warning("Proc_SpawnWorker - Out of heap space!\n");
- return -1;
+ return NULL;
}
// Create the stack contents
new->Status = THREAD_STAT_PREINIT;
Threads_AddActive( new );
- return new->TID;
+ return new;
}
/**
/*\r
- * AcessOS/AcessBasic v0.1\r
- * PCI Bus Driver\r
+ * Acess2 Kernel\r
+ * - By John Hodge (thePowersGang)\r
+ * \r
+ * drv/pci.c\r
+ * - PCI Enumeration and Arbitration\r
*/\r
#define DEBUG 0\r
#include <acess.h>\r
#include <fs_devfs.h>\r
#include <drv_pci.h>\r
#include <drv_pci_int.h>\r
+#include <virtual_pci.h>\r
\r
#define USE_PORT_BITMAP 0\r
#define LIST_DEVICES 1\r
+#define PCI_MAX_BUSSES 8\r
\r
// === STRUCTURES ===\r
typedef struct sPCIDevice\r
// === GLOBALS ===\r
MODULE_DEFINE(0, 0x0100, PCI, PCI_Install, NULL, NULL);\r
int giPCI_BusCount = 1;\r
+Uint8 gaPCI_BusNumbers[PCI_MAX_BUSSES];\r
int giPCI_InodeHandle = -1;\r
int giPCI_DeviceCount = 0;\r
tPCIDevice *gPCI_Devices = NULL;\r
#endif \r
\r
// Scan Bus (Bus 0, Don't fill gPCI_Devices)\r
+ giPCI_DeviceCount = 0;\r
+ giPCI_BusCount = 1;\r
+ gaPCI_BusNumbers[0] = 0;\r
for( bus = 0; bus < giPCI_BusCount; bus ++ )\r
{\r
- ret = PCI_ScanBus(bus, 0);\r
+ ret = PCI_ScanBus(gaPCI_BusNumbers[bus], 0);\r
if(ret != MODULE_ERR_OK) return ret;\r
}\r
- \r
+ // TODO: PCIe\r
+ // - Add VPCI Devices\r
+ giPCI_DeviceCount += giVPCI_DeviceCount;\r
+ \r
if(giPCI_DeviceCount == 0) {\r
Log_Notice("PCI", "No devices were found");\r
return MODULE_ERR_NOTNEEDED;\r
// Rescan, filling the PCI device array\r
for( bus = 0; bus < giPCI_BusCount; bus ++ )\r
{\r
- PCI_ScanBus(bus, 1);\r
+ PCI_ScanBus(gaPCI_BusNumbers[bus], 1);\r
+ }\r
+ // Insert VPCI Devices\r
+ for( int i = 0; i < giVPCI_DeviceCount; i ++ )\r
+ {\r
+ tPCIDevice *devinfo = &gPCI_Devices[giPCI_DeviceCount];\r
+ \r
+ devinfo->bus = -1;\r
+ devinfo->slot = i;\r
+ devinfo->fcn = 0;\r
+ devinfo->vendor = gaVPCI_Devices[i].Vendor;\r
+ devinfo->device = gaVPCI_Devices[i].Device;\r
+ devinfo->revision = gaVPCI_Devices[i].Class & 0xFF;\r
+ devinfo->class = gaVPCI_Devices[i].Class >> 8;\r
+ snprintf(devinfo->Name, sizeof(devinfo->Name), "%02x.%02x:%x", 0xFF, i, 0);\r
+\r
+ for(int j = 0; j < 256/4; j ++ )\r
+ devinfo->ConfigCache[i] = VPCI_Read(&gaVPCI_Devices[i], j*4, 4);\r
+\r
+ memset(&devinfo->Node, 0, sizeof(devinfo->Node));\r
+ devinfo->Node.Inode = giPCI_DeviceCount;\r
+ devinfo->Node.Size = 256;\r
+ devinfo->Node.NumACLs = 1;\r
+ devinfo->Node.ACLs = &gVFS_ACL_EveryoneRO;\r
+ devinfo->Node.Type = &gPCI_DevNodeType;\r
+\r
+ giPCI_DeviceCount ++;\r
}\r
\r
// Complete Driver Structure\r
if(!PCI_int_EnumDevice(BusID, dev, fcn, &devInfo))\r
continue;\r
\r
- if(devInfo.class == PCI_OC_PCIBRIDGE)\r
- {\r
- #if LIST_DEVICES\r
- if( !bFill )\r
- Log_Log("PCI", "Bridge @ %i,%i:%i (0x%x:0x%x)",\r
- BusID, dev, fcn, devInfo.vendor, devInfo.device);\r
- #endif\r
- //TODO: Handle PCI-PCI Bridges\r
- //PCI_ScanBus(devInfo.???, bFill);\r
- giPCI_BusCount ++;\r
- }\r
- else\r
- {\r
- #if LIST_DEVICES\r
- if( !bFill )\r
- Log_Log("PCI", "Device %i,%i:%i %06x => 0x%04x:0x%04x Rev %i",\r
- BusID, dev, fcn, devInfo.class,\r
- devInfo.vendor, devInfo.device, devInfo.revision);\r
- #endif\r
- }\r
+ #if LIST_DEVICES\r
+ if( !bFill )\r
+ Log_Log("PCI", "Device %i,%i:%i %06x => 0x%04x:0x%04x Rev %i",\r
+ BusID, dev, fcn, devInfo.class,\r
+ devInfo.vendor, devInfo.device, devInfo.revision);\r
+ #endif\r
\r
if( bFill ) {\r
devInfo.Node.Inode = giPCI_DeviceCount;\r
}\r
giPCI_DeviceCount ++;\r
\r
- // If bit 23 of (soemthing) is set, there are sub-functions\r
+ switch(devInfo.ConfigCache[3] & 0x007F0000)\r
+ {\r
+ case 0x00: // Normal device\r
+ break;\r
+ case 0x01: // PCI-PCI Bridge\r
+ {\r
+ // TODO: Add to list of busses?\r
+ Uint8 sec = (devInfo.ConfigCache[6] & 0x0000FF00) >> 8;\r
+ #if LIST_DEVICES\r
+ if( !bFill ) {\r
+ Uint8 pri = (devInfo.ConfigCache[6] & 0x000000FF) >> 0;\r
+ Log_Log("PCI", "- PCI-PCI Bridge %02x=>%02x", pri, sec);\r
+ }\r
+ #endif\r
+ gaPCI_BusNumbers[giPCI_BusCount++] = sec;\r
+ }\r
+ break;\r
+ case 0x02: // PCI-CardBus Bridge\r
+ break;\r
+ }\r
+\r
+ // If bit 8 of the Header Type register is set, there are sub-functions\r
if(fcn == 0 && !(devInfo.ConfigCache[3] & 0x00800000) )\r
break;\r
}\r
*/\r
tVFS_Node *PCI_int_FindDirRoot(tVFS_Node *node, const char *filename)\r
{\r
- int bus,slot,fcn;\r
int i;\r
- // Validate Filename (Pointer and length)\r
- if(!filename || strlen(filename) != 7)\r
- return NULL;\r
- // Check for spacers\r
- if(filename[2] != '.' || filename[5] != ':')\r
- return NULL;\r
- \r
- // Get Information\r
- if(filename[0] < '0' || filename[0] > '9') return NULL;\r
- bus = (filename[0] - '0')*10;\r
- if(filename[1] < '0' || filename[1] > '9') return NULL;\r
- bus += filename[1] - '0';\r
- if(filename[3] < '0' || filename[3] > '9') return NULL;\r
- slot = (filename[3] - '0')*10;\r
- if(filename[4] < '0' || filename[4] > '9') return NULL;\r
- slot += filename[4] - '0';\r
- if(filename[6] < '0' || filename[6] > '9') return NULL;\r
- fcn = filename[6] - '0';\r
\r
// Find Match\r
for(i=0;i<giPCI_DeviceCount;i++)\r
{\r
- if(gPCI_Devices[i].bus != bus) continue;\r
- if(gPCI_Devices[i].slot != slot) continue;\r
- if(gPCI_Devices[i].fcn != fcn) continue;\r
- \r
- return &gPCI_Devices[i].Node;\r
+ int cmp = strcmp(gPCI_Devices[i].Name, filename);\r
+ if( cmp > 0 ) // Sorted list\r
+ break;\r
+ if( cmp == 0 )\r
+ return &gPCI_Devices[i].Node;\r
}\r
\r
// Error Return\r
if( Offset & (Size - 1) ) return 0;\r
\r
dev = &gPCI_Devices[ID];\r
+ // Detect VPCI devices\r
+ if( dev->bus == -1 ) {\r
+ return VPCI_Read(&gaVPCI_Devices[dev->slot], Offset, Size);\r
+ }\r
+\r
addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset);\r
\r
dword = PCI_CfgReadDWord(addr);\r
tPCIDevice *dev;\r
Uint32 dword, addr;\r
int shift;\r
+\r
if( ID < 0 || ID >= giPCI_DeviceCount ) return ;\r
if( Offset < 0 || Offset > 256 ) return ;\r
- \r
+\r
dev = &gPCI_Devices[ID];\r
+\r
+ // Detect VPCI devices\r
+ if( dev->bus == -1 ) {\r
+ VPCI_Write(&gaVPCI_Devices[dev->slot], Offset, Size, Value);\r
+ return ;\r
+ }\r
+\r
addr = PCI_int_GetBusAddr(dev->bus, dev->slot, dev->fcn, Offset);\r
\r
if(Size != 4)\r
vendor_dev = PCI_CfgReadDWord( addr );\r
if((vendor_dev & 0xFFFF) == 0xFFFF) // Invalid Device\r
return 0;\r
+ \r
+ info->bus = bus;\r
+ info->slot = slot;\r
+ info->fcn = fcn;\r
\r
+ // Read configuration\r
info->ConfigCache[0] = vendor_dev;\r
for( i = 1, addr += 4; i < 256/4; i ++, addr += 4 )\r
{\r
info->ConfigCache[i] = PCI_CfgReadDWord(addr);\r
} \r
\r
- info->bus = bus;\r
- info->slot = slot;\r
- info->fcn = fcn;\r
info->vendor = vendor_dev & 0xFFFF;\r
info->device = vendor_dev >> 16;\r
tmp = info->ConfigCache[2];\r
// #endif\r
\r
// Make node name\r
- info->Name[0] = '0' + bus/10;\r
- info->Name[1] = '0' + bus%10;\r
- info->Name[2] = '.';\r
- info->Name[3] = '0' + slot/10;\r
- info->Name[4] = '0' + slot%10;\r
- info->Name[5] = ':';\r
- info->Name[6] = '0' + fcn;\r
- info->Name[7] = '\0';\r
+ snprintf(info->Name, 8, "%02x.%02x:%x", bus, slot, fcn);\r
\r
// Create VFS Node\r
memset( &info->Node, 0, sizeof(tVFS_Node) );\r
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * drv/vpci.c
+ * - Virtual PCI Bus
+ */
+#include <virtual_pci.h>
+
+// === CODE ===
+Uint32 VPCI_Read(tVPCI_Device *Dev, Uint8 Offset, Uint8 Size)
+{
+ Uint32 tmp_dword = 0;
+
+ if( Size > 4 || Size == 3 || Size == 0 )
+ return 0;
+ if( Offset & (Size - 1) )
+ return 0;
+
+ switch( Offset >> 2 )
+ {
+ case 0: // Vendor[0:15], Device[16:31]
+ tmp_dword = (Dev->Vendor) | (Dev->Device << 16);
+ break;
+ case 2: // Class Code
+ tmp_dword = Dev->Class;
+ break;
+ // 1: Command[0:15], Status[16:31]
+ // 3: Cache Line Size, Latency Timer, Header Type, BIST
+ // 4-9: BARs
+ // 10: Unused (Cardbus CIS Pointer)
+ // 11: Subsystem Vendor ID, Subsystem ID
+ // 12: Expansion ROM Address
+ // 13: Capabilities[0:8], Reserved[9:31]
+ // 14: Reserved
+ // 15: Interrupt Line, Interrupt Pin, Min Grant, Max Latency
+ default:
+ tmp_dword = Dev->Read(Dev->Ptr, Offset >> 2);
+ break;
+ }
+
+ tmp_dword >>= 8*(Offset & 3);
+ switch(Size)
+ {
+ case 4: break;
+ case 2: tmp_dword &= 0xFFFF; break;
+ case 1: tmp_dword &= 0xFF;
+ }
+
+ return tmp_dword;
+}
+
+void VPCI_Write(tVPCI_Device *Dev, Uint8 Offset, Uint8 Size, Uint32 Data)
+{
+ Uint32 tmp_dword;
+ if( Size > 4 || Size == 3 || Size == 0 )
+ return ;
+ if( Offset & (Size - 1) )
+ return ;
+
+ switch(Offset >> 2)
+ {
+ case 0: // Vendor / Device IDs
+ case 2: // Class Code
+ // READ ONLY
+ return ;
+ }
+
+ tmp_dword = Dev->Read(Dev->Ptr, Offset>>2);
+ switch(Size)
+ {
+ case 4: tmp_dword = 0; break;
+ case 2:
+ tmp_dword &= ~(0xFFFF << ((Offset&2)*16));
+ Data |= 0xFFFF;
+ break;
+ case 1:
+ tmp_dword &= ~(0xFF << ((Offset&3)*8));
+ Data |= 0xFF;
+ break;
+ }
+ tmp_dword |= Data << ((Offset&3)*8);
+ Dev->Write(Dev->Ptr, Offset>>2, tmp_dword);
+}
{
tHeapHead *head, *badHead;
tHeapFoot *foot = NULL;
+ static int in_heap_dump;
+ if( in_heap_dump ) return;
+
+ in_heap_dump = 1;
+
head = gHeapStart;
while( (Uint)head < (Uint)gHeapEnd )
{
}
// If the heap is valid, ok!
- if( (tVAddr)head == (tVAddr)gHeapEnd )
+ if( (tVAddr)head == (tVAddr)gHeapEnd ) {
+ in_heap_dump = 0;
return ;
+ }
// Check for a bad return
- if( (tVAddr)head >= (tVAddr)gHeapEnd )
+ if( (tVAddr)head >= (tVAddr)gHeapEnd ) {
+ in_heap_dump = 0;
return ;
+ }
#if !VERBOSE_DUMP
Log_Log("Heap", "%p (%P): 0x%08lx %i %4C",
* \name Threads and Processes
* \{
*/
-extern int Proc_SpawnWorker(void (*Fcn)(void*), void *Data);
+extern struct sThread *Proc_SpawnWorker(void (*Fcn)(void*), void *Data);
extern int Proc_Spawn(const char *Path);
extern int Proc_SysSpawn(const char *Binary, const char **ArgV, const char **EnvP, int nFD, int *FDs);
extern int Proc_Execve(const char *File, const char **ArgV, const char **EnvP, int DataSize);
#include <threads.h>
+/**
+ * \name Event Values
+ * \{
+ */
+//! Fired when a VFS wait is ready [used in select(2)]
#define THREAD_EVENT_VFS 0x00000001
+//! Fired when an IPC Message arrives
#define THREAD_EVENT_IPCMSG 0x00000002
+//! Fired when a signal (e.g. SIGINT) is asserted
#define THREAD_EVENT_SIGNAL 0x00000004
+//! Timer event fire
#define THREAD_EVENT_TIMER 0x00000008
+//! General purpose event for short waits
+//! e.g. waiting for an IRQ in a Read() call
#define THREAD_EVENT_SHORTWAIT 0x00000010
+#define THREAD_EVENT_USER1 0x10000000
+#define THREAD_EVENT_USER2 0x20000000
+#define THREAD_EVENT_USER3 0x40000000
+#define THREAD_EVENT_USER4 0x80000000
+/**
+ * \}
+ */
+
// === FUNCTIONS ===
extern void Threads_PostEvent(tThread *Thread, Uint32 EventMask);
extern void Threads_ClearEvent(Uint32 EventMask);
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * virtual_pci.h
+ * - Virtual PCI bus definitions
+ */
+#ifndef _VIRTUAL_PCI_H_
+#define _VIRTUAL_PCI_H_
+#include <acess.h>
+
+typedef struct sVPCI_Device tVPCI_Device;
+
+struct sVPCI_Device
+{
+ void *Ptr;
+
+ // Vendor/Device is defined at runtime as 0x0ACE:0x[ARCH][ARCH][IDX][IDX]
+ Uint16 Vendor;
+ Uint16 Device;
+ // Class code (Class, Subclass, Programming Interface, Revision)
+ Uint32 Class;
+
+ Uint32 BARs[6];
+ Uint8 IRQ;
+
+ /**
+ * \param Ptr Value in sVPCI_Device.Ptr
+ * \param Data Data to write to the specified address
+ */
+ void (*Write)(void *Ptr, Uint8 DWord, Uint32 Data);
+ Uint32 (*Read)(void *Ptr, Uint8 DWord);
+};
+
+extern int giVPCI_DeviceCount;
+extern tVPCI_Device gaVPCI_Devices[];
+
+extern Uint32 VPCI_Read(tVPCI_Device *Dev, Uint8 Offset, Uint8 Size);
+extern void VPCI_Write(tVPCI_Device *Dev, Uint8 Offset, Uint8 Size, Uint32 Data);
+#endif
+
Mutex_Release( &glIP_Adapters );
// Watch the adapter for incoming packets
- tTID tid = Proc_SpawnWorker(Adapter_int_WatchThread, ret);
- if(tid < 0) {
+ void *worker = Proc_SpawnWorker(Adapter_int_WatchThread, ret);
+ if(!worker) {
Log_Warning("IPStack", "Unable to create watcher thread for %p", ret);
}
OBJ = main.o
OBJ += usb.o usb_lowlevel.o usb_devinit.o usb_io.o usb_poll.o usb_info.o
-OBJ += hub.o
+OBJ += hub.o portctl.o
CPPFLAGS = -Iinclude
NAME = Core
// resvd
#define SET_FEATURE 3
-#define PORT_CONNECTION 0
-#define PORT_ENABLE 1
-#define PORT_SUSPEND 2
-#define PORT_OVER_CURRENT 3
-#define PORT_RESET 4
-#define PORT_POWER 8
-#define PORT_LOW_SPEED 9
-#define C_PORT_CONNECTION 16
-#define C_PORT_ENABLE 17
-#define C_PORT_SUSPEND 18
-#define C_PORT_OVER_CURRENT 19
-#define C_PORT_RESET 20
-#define PORT_TEST 21
-#define PORT_INDICATOR 21
-
struct sHubDescriptor
{
Uint8 DescLength;
void Hub_Disconnected(tUSBInterface *Dev);
void Hub_PortStatusChange(tUSBInterface *Dev, int Endpoint, int Length, void *Data);
void Hub_int_HandleChange(tUSBInterface *Dev, int Port);
+void Hub_SetPortFeature(tUSBInterface *Dev, int Port, int Feat);
+void Hub_ClearPortFeature(tUSBInterface *HubDev, int Port, int Feat);
+int Hub_GetPortStatus(tUSBInterface *HubDev, int Port, int Flag);
// === GLOBALS ===
tUSBDriver gUSBHub_Driver = {
// - Power on port
USB_Request(Dev, 0, 0x23, SET_FEATURE, PORT_POWER, Port, 0, NULL);
Time_Delay(info->PowerOnDelay);
- // - Reset
- USB_Request(Dev, 0, 0x23, SET_FEATURE, PORT_RESET, Port, 0, NULL);
- Time_Delay(20); // Spec says 10ms after reset, but how long is reset?
- // - Enable
- USB_Request(Dev, 0, 0x23, SET_FEATURE, PORT_ENABLE, Port, 0, NULL);
- // - Poke USB Stack
- USB_DeviceConnected(info->HubPtr, Port);
+ // - Start reset process
+ USB_PortCtl_BeginReset(info->HubPtr, Port);
}
else {
// Disconnected
USB_Request(Dev, 0, 0x23, CLEAR_FEATURE, C_PORT_RESET, Port, 0, NULL);
}
}
+
+void Hub_SetPortFeature(tUSBInterface *Dev, int Port, int Feat)
+{
+ USB_Request(Dev, 0, 0x23, SET_FEATURE, Feat, Port, 0, NULL);
+}
+
+void Hub_ClearPortFeature(tUSBInterface *Dev, int Port, int Feat)
+{
+ USB_Request(Dev, 0, 0x23, CLEAR_FEATURE, Feat, Port, 0, NULL);
+}
+
+int Hub_GetPortStatus(tUSBInterface *Dev, int Port, int Flag)
+{
+ Uint16 status[2]; // Status, Change
+ USB_Request(Dev, 0, 0xA3, GET_STATUS, 0, Port, 4, status);
+ return !!(status[0] & (1 << Flag));
+}
tUSBBulkOp SendBulk;
void (*FreeOp)(void *Ptr, void *Handle);
+ // Root hub stuff
void (*CheckPorts)(void *Ptr);
+ void (*SetPortFeature)(void *Ptr, int PortNum, int Feat);
+ void (*ClearPortFeature)(void *Ptr, int PortNum, int Feat);
+ int (*GetPortStatus)(void *Ptr, int PortNum, int Flag);
};
extern tUSBHub *USB_RegisterHost(tUSBHostDef *HostDef, void *ControllerPtr, int nPorts);
extern void USB_DeviceConnected(tUSBHub *Hub, int Port);
extern void USB_DeviceDisconnected(tUSBHub *Hub, int Port);
+#define PORT_CONNECTION 0
+#define PORT_ENABLE 1
+#define PORT_SUSPEND 2
+#define PORT_OVER_CURRENT 3
+#define PORT_RESET 4
+#define PORT_POWER 8
+#define PORT_LOW_SPEED 9
+#define C_PORT_CONNECTION 16
+#define C_PORT_ENABLE 17
+#define C_PORT_SUSPEND 18
+#define C_PORT_OVER_CURRENT 19
+#define C_PORT_RESET 20
+#define PORT_TEST 21
+#define PORT_INDICATOR 21
+
+extern void Hub_SetPortFeature(tUSBInterface *HubDev, int Port, int Feat);
+extern void Hub_ClearPortFeature(tUSBInterface *HubDev, int Port, int Feat);
+extern int Hub_GetPortStatus(tUSBInterface *HubDev, int Port, int Flag);
+
+extern void USB_PortCtl_BeginReset(tUSBHub *Hub, int Port);
+
#endif
// === IMPORTS ===
extern void USB_PollThread(void *unused);
extern void USB_AsyncThread(void *Unused);
+extern void USB_PortCtl_Init(void);
// === PROTOTYPES ===
int USB_Install(char **Arguments);
*/
int USB_Install(char **Arguments)
{
+ USB_PortCtl_Init();
Proc_SpawnWorker(USB_PollThread, NULL);
Proc_SpawnWorker(USB_AsyncThread, NULL);
--- /dev/null
+/*
+ * Acess2 USB Stack
+ * - By John Hodge (thePowersGang)
+ *
+ * portctl.c
+ * - Port control code
+ */
+#define DEBUG 1
+#define SANITY 1
+#include <acess.h>
+#include "usb.h"
+#include <workqueue.h>
+#include <timers.h>
+#include <usb_hub.h>
+
+// === PROTOTYPES ===
+void USB_PortCtl_Init(void);
+void USB_PortCtl_Worker(void *Unused);
+void USB_PortCtl_SetPortFeature(tUSBHub *Hub, int Port, int Feat);
+void USB_PortCtl_ClearPortFeature(tUSBHub *Hub, int Port, int Feat);
+ int USB_PortCtl_GetPortStatus(tUSBHub *Hub, int Port, int Flag);
+
+// === GLOBALS ===
+tWorkqueue gUSB_PortCtl_WorkQueue;
+
+// === CODE ===
+void USB_PortCtl_Init(void)
+{
+ Workqueue_Init(&gUSB_PortCtl_WorkQueue, "USB Port Reset Work Queue", offsetof(tUSBHubPort, ListNext));
+ Proc_SpawnWorker(USB_PortCtl_Worker, NULL);
+}
+
+void USB_PortCtl_Worker(void *Unused)
+{
+ Threads_SetName("USB PortCtl Worker");
+ for(;;)
+ {
+ tUSBHubPort *port;
+ tUSBHub *hub;
+
+ port = Workqueue_GetWork(&gUSB_PortCtl_WorkQueue);
+ if( !port ) {
+ Log_Warning("USB", "PortCtl Workqueue returned NULL");
+ break;
+ }
+ hub = (tUSBHub*)(port - port->PortNum) - 1;
+
+ LOG("port = %p, hub = %p", port, hub);
+
+ switch(port->Status)
+ {
+ case 1:
+ // Assert reset
+ USB_PortCtl_SetPortFeature(hub, port->PortNum, PORT_RESET);
+ LOG("Port reset starting");
+ // Wait 50 ms
+ Time_Delay(50);
+ USB_PortCtl_ClearPortFeature(hub, port->PortNum, PORT_RESET);
+ Time_Delay(10); // May take up to 2ms for reset to clear
+ // Enable port
+ LOG("Port enabling");
+ USB_PortCtl_SetPortFeature(hub, port->PortNum, PORT_ENABLE);
+ // Begin connect processing
+ port->Status = 2;
+ USB_DeviceConnected(hub, port->PortNum);
+ break;
+ }
+ }
+}
+
+void USB_PortCtl_BeginReset(tUSBHub *Hub, int Port)
+{
+ LOG("Starting %p %i", Hub, Port);
+ // Set status field in hub structure
+ Hub->Ports[Port].Status = 1;
+ Hub->Ports[Port].PortNum = Port;
+ // Add to the work queue
+ Workqueue_AddWork(&gUSB_PortCtl_WorkQueue, &Hub->Ports[Port]);
+}
+
+void USB_PortCtl_SetPortFeature(tUSBHub *Hub, int Port, int Feat)
+{
+ if( Hub->Interface->Driver == NULL ) {
+ // - Host Port
+ tUSBHost *host = Hub->Interface->Dev->Host;
+ ASSERT(host->HostDef->SetPortFeature);
+ host->HostDef->SetPortFeature(host->Ptr, Port, Feat);
+ }
+ else {
+ // - Hub Port
+ Hub_SetPortFeature(Hub->Interface, Port, Feat);
+ }
+}
+
+void USB_PortCtl_ClearPortFeature(tUSBHub *Hub, int Port, int Feat)
+{
+ if( Hub->Interface->Driver == NULL ) {
+ // - Host Port
+ tUSBHost *host = Hub->Interface->Dev->Host;
+ ASSERT(host->HostDef->ClearPortFeature);
+ host->HostDef->ClearPortFeature(host->Ptr, Port, Feat);
+ }
+ else {
+ // - Hub Port
+ Hub_ClearPortFeature(Hub->Interface, Port, Feat);
+ }
+}
+
+int USB_PortCtl_GetPortStatus(tUSBHub *Hub, int Port, int Flag)
+{
+ if( Hub->Interface->Driver == NULL ) {
+ // - Host Port
+ tUSBHost *host = Hub->Interface->Dev->Host;
+ ASSERT(host->HostDef->GetPortStatus);
+ return host->HostDef->GetPortStatus(host->Ptr, Port, Flag);
+ }
+ else {
+ // - Hub Port
+ return Hub_GetPortStatus(Hub->Interface, Port, Flag);
+ }
+ return 0;
+}
+
{
tUSBHost *host;
- host = malloc(sizeof(tUSBHost) + nPorts*sizeof(void*));
+ host = malloc(sizeof(tUSBHost) + nPorts*sizeof(tUSBHubPort));
if(!host) {
// Oh, bugger.
return NULL;
host->RootHubDev.ParentHub = NULL;
host->RootHubDev.Host = host;
host->RootHubDev.Address = 0;
+ ASSERT(HostDef->InitControl);
host->RootHubDev.EndpointHandles[0] = HostDef->InitControl(ControllerPtr, 0, 64);
// host->RootHubIf.Next = NULL;
host->RootHub.Interface = &host->RootHubIf;
host->RootHub.nPorts = nPorts;
- memset(host->RootHub.Devices, 0, sizeof(void*)*nPorts);
+ memset(host->RootHub.Ports, 0, sizeof(tUSBHubPort)*nPorts);
// Append to list
Mutex_Acquire( &glUSB_Hosts );
{
tUSBHub *ret;
- ret = malloc(sizeof(tUSBHub) + sizeof(ret->Devices[0])*PortCount);
+ ret = malloc(sizeof(tUSBHub) + sizeof(ret->Ports[0])*PortCount);
ret->Interface = Device;
ret->nPorts = PortCount;
- memset(ret->Devices, 0, sizeof(ret->Devices[0])*PortCount);
+ memset(ret->Ports, 0, sizeof(ret->Ports[0])*PortCount);
return ret;
}
{
for( int i = 0; i < Hub->nPorts; i ++ )
{
- if( Hub->Devices[i] )
+ if( Hub->Ports[i].Dev )
{
USB_DeviceDisconnected( Hub, i );
}
#include <usb_host.h>
#include "usb_proto.h"
+typedef struct sUSBHubPort tUSBHubPort;
typedef struct sUSBHost tUSBHost;
typedef struct sUSBDevice tUSBDevice;
typedef struct sUSBEndpoint tUSBEndpoint;
// === STRUCTURES ===
+struct sUSBHubPort
+{
+ void *ListNext;
+ char Status;
+ char PortNum;
+ tUSBDevice *Dev;
+};
+
/**
* \brief USB Hub data
*/
tUSBInterface *Interface;
int nPorts;
- tUSBDevice *Devices[];
+ struct sUSBHubPort Ports[];
};
struct sUSBEndpoint
tUSBDevice tmpdev;
tUSBDevice *dev = &tmpdev;
if( Port >= Hub->nPorts ) return ;
- if( Hub->Devices[Port] ) return ;
+ if( Hub->Ports[Port].Dev ) return ;
ENTER("pHub iPort", Hub, Port);
free(full_buf);
}
- Hub->Devices[Port] = dev;
+ Hub->Ports[Port].Dev = dev;
// Done.
LEAVE('-');
void USB_DeviceDisconnected(tUSBHub *Hub, int Port)
{
tUSBDevice *dev;
- if( !Hub->Devices[Port] ) {
+ if( !Hub->Ports[Port].Dev ) {
Log_Error("USB", "Non-registered device disconnected");
return;
}
- dev = Hub->Devices[Port];
+ dev = Hub->Ports[Port].Dev;
// TODO: Free related resources
// - Endpoint registrations
// - Bus Address
USB_int_DeallocateAddress(dev->Host, dev->Address);
// - Inform handler
- // - Allocate memory
+ // - Release memory
free(dev);
+ Hub->Ports[Port].Dev = NULL;
}
void *USB_GetDeviceDataPtr(tUSBInterface *Dev) { return Dev->Data; }
// Check hosts
for( tUSBHost *host = gUSB_Hosts; host; host = host->Next )
{
- host->HostDef->CheckPorts(host->Ptr);
+ if( host->HostDef->CheckPorts )
+ host->HostDef->CheckPorts(host->Ptr);
}
Time_Delay(100);
#include "ehci.h"
#include <drv_pci.h>
#include <limits.h>
+#include <events.h>
+#include <timers.h>
// === CONSTANTS ===
#define EHCI_MAX_CONTROLLERS 4
+#define EHCI_THREADEVENT_IOC THREAD_EVENT_USER1
+#define EHCI_THREADEVENT_PORTSC THREAD_EVENT_USER2
// === PROTOTYPES ===
int EHCI_Initialise(char **Arguments);
);
void *EHCI_SendBulk(void *Ptr, void *Dest, tUSBHostCb Cb, void *CbData, int Dir, void *Data, size_t Length);
void EHCI_FreeOp(void *Ptr, void *Handle);
+Uint32 EHCI_int_RootHub_FeatToMask(int Feat);
+void EHCI_RootHub_SetPortFeature(void *Ptr, int Port, int Feat);
+void EHCI_RootHub_ClearPortFeature(void *Ptr, int Port, int Feat);
+ int EHCI_RootHub_GetPortStatus(void *Ptr, int Port, int Flag);
// --- Internals ---
tEHCI_qTD *EHCI_int_AllocateTD(tEHCI_Controller *Cont, int PID, void *Data, size_t Length, tUSBHostCb Cb, void *CbData);
void EHCI_int_DeallocateTD(tEHCI_Controller *Cont, tEHCI_qTD *TD);
-void EHCI_int_AppendTD(tEHCI_QH *QH, tEHCI_qTD *TD);
+void EHCI_int_AppendTD(tEHCI_Controller *Cont, tEHCI_QH *QH, tEHCI_qTD *TD);
tEHCI_QH *EHCI_int_AllocateQH(tEHCI_Controller *Cont, int Endpoint, size_t MaxPacketSize);
void EHCI_int_DeallocateQH(tEHCI_Controller *Cont, tEHCI_QH *QH);
+void EHCI_int_InterruptThread(void *ControllerPtr);
// === GLOBALS ===
MODULE_DEFINE(0, VERSION, USB_EHCI, EHCI_Initialise, NULL, "USB_Core", NULL);
tUSBHostDef gEHCI_HostDef = {
.InitInterrupt = EHCI_InitInterrupt,
.InitIsoch = EHCI_InitIsoch,
+ .InitControl = EHCI_InitControl,
.InitBulk = EHCI_InitBulk,
.RemEndpoint = EHCI_RemEndpoint,
.SendIsoch = NULL,
.SendControl = EHCI_SendControl,
.SendBulk = EHCI_SendBulk,
- .FreeOp = EHCI_FreeOp
+ .FreeOp = EHCI_FreeOp,
+
+ .CheckPorts = NULL, // No need
+ .SetPortFeature = EHCI_RootHub_SetPortFeature,
+ .ClearPortFeature = EHCI_RootHub_ClearPortFeature,
+ .GetPortStatus = EHCI_RootHub_GetPortStatus,
};
// === CODE ===
// TODO: The same
}
- if( EHCI_InitController(addr, irq) ) {
+ Log_Log("ECHI", "Controller at PCI %i 0x%x IRQ 0x%x",
+ id, addr, irq);
+
+ if( EHCI_InitController(addr, irq) )
+ {
// TODO: Detect other forms of failure than "out of slots"
break ;
}
+
+ // TODO: Register with the USB stack
}
return 0;
}
break;
}
}
-
if(!cont) {
+ Log_Notice("EHCI", "Too many controllers (EHCI_MAX_CONTROLLERS=%i)",
+ EHCI_MAX_CONTROLLERS);
return 1;
}
+ // - Nuke a couple of fields so error handling code doesn't derp
+ cont->CapRegs = NULL;
+ cont->PeriodicQueue = NULL;
+ cont->TDPool = NULL;
+
// -- Build up structure --
cont->CapRegs = (void*)MM_MapHWPages(BaseAddress, 1);
+ if( !cont->CapRegs ) {
+ Log_Warning("EHCI", "Can't map 1 page at %P into kernel space", BaseAddress);
+ goto _error;
+ }
// TODO: Error check
+ if( (cont->CapRegs->CapLength & 3) ) {
+ Log_Warning("EHCI", "Controller at %P non-aligned op regs", BaseAddress);
+ goto _error;
+ }
cont->OpRegs = (void*)( (Uint32*)cont->CapRegs + cont->CapRegs->CapLength / 4 );
// - Allocate periodic queue
- cont->PeriodicQueue = (void*)MM_AllocDMA(1, 32, NULL);
+ tPAddr unused;
+ cont->PeriodicQueue = (void*)MM_AllocDMA(1, 32, &unused);
+ if( !cont->PeriodicQueue ) {
+ Log_Warning("ECHI", "Can't allocate 1 32-bit page for periodic queue");
+ goto _error;
+ }
// TODO: Error check
// > Populate queue
+ // - Allocate TD pool
+ cont->TDPool = (void*)MM_AllocDMA(1, 32, &unused);
+ if( !cont->TDPool ) {
+ Log_Warning("ECHI", "Can't allocate 1 32-bit page for qTD pool");
+ goto _error;
+ }
+ for( int i = 0; i < TD_POOL_SIZE; i ++ ) {
+ cont->TDPool[i].Token = 3 << 8;
+ }
+
+ // Get port count
+ cont->nPorts = cont->CapRegs->HCSParams & 0xF;
+
// -- Bind IRQ --
IRQ_AddHandler(InterruptNum, EHCI_InterruptHandler, cont);
+ cont->InterruptThread = Proc_SpawnWorker(EHCI_int_InterruptThread, cont);
+ if( !cont->InterruptThread ) {
+ Log_Warning("EHCI", "Can't spawn interrupt worker thread");
+ goto _error;
+ }
+ LOG("cont->InterruptThread = %p", cont->InterruptThread);
// -- Initialisation procedure (from ehci-r10) --
// - Reset controller
cont->OpRegs->USBCmd = (0x40 << 16) | USBCMD_PeriodicEnable | USBCMD_Run;
// - Route all ports
cont->OpRegs->ConfigFlag = 1;
-
+
+ // -- Register with USB Core --
+ cont->RootHub = USB_RegisterHost(&gEHCI_HostDef, cont, cont->nPorts);
+
return 0;
+_error:
+ cont->PhysBase = 0;
+ if( cont->CapRegs )
+ MM_Deallocate( (tVAddr)cont->CapRegs );
+ if( cont->PeriodicQueue )
+ MM_Deallocate( (tVAddr)cont->PeriodicQueue );
+ if( cont->TDPool )
+ MM_Deallocate( (tVAddr)cont->TDPool );
+ return 2;
}
void EHCI_InterruptHandler(int IRQ, void *Ptr)
{
- tEHCI_Controller *cont = Ptr;
- Uint32 sts = cont->OpRegs->USBSts;
+ tEHCI_Controller *Cont = Ptr;
+ Uint32 sts = Cont->OpRegs->USBSts;
// Clear interrupts
- cont->OpRegs->USBSts = sts;
+ Cont->OpRegs->USBSts = sts;
+
+ if( sts & 0xFFFF0FC0 ) {
+ LOG("Oops, reserved bits set (%08x), funny hardware?", sts);
+ sts &= ~0xFFFF0FFC0;
+ }
+
+ // Unmask read-only bits
+ sts &= ~(0xF000);
if( sts & USBINTR_IOC ) {
// IOC
+ Threads_PostEvent(Cont->InterruptThread, EHCI_THREADEVENT_IOC);
sts &= ~USBINTR_IOC;
}
if( sts & USBINTR_PortChange ) {
// Port change, determine what port and poke helper thread
+ LOG("Port status change");
+ Threads_PostEvent(Cont->InterruptThread, EHCI_THREADEVENT_PORTSC);
sts &= ~USBINTR_PortChange;
}
if( sts & USBINTR_FrameRollover ) {
// Frame rollover, used to aid timing (trigger per-second operations)
+ LOG("Frame rollover");
sts &= ~USBINTR_FrameRollover;
}
if( sts ) {
// Unhandled interupt bits
// TODO: Warn
+ LOG("WARN - Bitmask %x unhandled", sts);
}
+
+
}
// --------------------------------------------------------------------
tEHCI_QH *qh = EHCI_int_AllocateQH(Cont, Endpoint, Length);
qh->Impl.IntPeriodPow = period_pow;
+ Mutex_Acquire(&Cont->PeriodicListLock);
+
// Choose an interrupt slot to use
int minslot = 0, minslot_load = INT_MAX;
for( int slot = 0; slot < Period; slot ++ )
}
}
// Increase loading on the selected slot
- for( int i = 0; i < PERIODIC_SIZE; i += Period )
- Cont->InterruptLoad[i+minslot] += Length;
+ for( int i = minslot; i < PERIODIC_SIZE; i += Period )
+ Cont->InterruptLoad[i] += Length;
qh->Impl.IntOfs = minslot;
// Allocate TD for the data
tEHCI_qTD *td = EHCI_int_AllocateTD(Cont, (bOutbound ? PID_OUT : PID_IN), Buf, Length, Cb, CbData);
- EHCI_int_AppendTD(qh, td);
+ EHCI_int_AppendTD(Cont, qh, td);
// Insert into the periodic list
+ for( int i = 0; i < PERIODIC_SIZE; i += Period )
+ {
+ // Walk list until
+ // - the end is reached
+ // - this QH is found
+ // - A QH with a lower period is encountered
+ tEHCI_QH *pqh = NULL;
+ tEHCI_QH *nqh;
+ for( nqh = Cont->PeriodicQueueV[i]; nqh; pqh = nqh, nqh = nqh->Impl.Next )
+ {
+ if( nqh == qh )
+ break;
+ if( nqh->Impl.IntPeriodPow < period_pow )
+ break;
+ }
+
+ // Somehow, we've already been added to this queue.
+ if( nqh && nqh == qh )
+ continue ;
+
+ if( qh->Impl.Next && qh->Impl.Next != nqh ) {
+ Log_Warning("EHCI", "Suspected bookkeeping error on %p - int list %i+%i overlap",
+ Cont, period_pow, minslot);
+ break;
+ }
+
+ if( nqh ) {
+ qh->Impl.Next = nqh;
+ qh->HLink = MM_GetPhysAddr(nqh) | 2;
+ }
+ else {
+ qh->Impl.Next = NULL;
+ qh->HLink = 2|1; // QH, Terminate
+ }
+
+ if( pqh ) {
+ pqh->Impl.Next = qh;
+ pqh->HLink = MM_GetPhysAddr(qh) | 2;
+ }
+ else {
+ Cont->PeriodicQueueV[i] = qh;
+ Cont->PeriodicQueue[i] = MM_GetPhysAddr(qh) | 2;
+ }
+ }
+ Mutex_Release(&Cont->PeriodicListLock);
return qh;
}
Cont->OpRegs->AsyncListAddr = MM_GetPhysAddr(qh)|2;
Cont->LastAsyncHead = qh;
+ LOG("Created %p for %p Ep 0x%x - %i bytes MPS", qh, Ptr, Endpoint, MaxPacketSize);
+
return qh;
}
void *EHCI_InitBulk(void *Ptr, int Endpoint, size_t MaxPacketSize)
else if( (tVAddr)Handle <= 256*16 )
return ; // Isoch
else {
+ tEHCI_QH *qh = Ptr;
+
// Remove QH from list
// - If it's a polling endpoint, need to remove from all periodic lists
+ if( qh->Impl.IntPeriodPow != 0xFF) {
+ // Poll
+ }
+ else {
+ // GP
+ }
// Deallocate QH
EHCI_int_DeallocateQH(Ptr, Handle);
td_setup = EHCI_int_AllocateTD(Cont, PID_SETUP, (void*)SetupData, SetupLength, NULL, NULL);
if( isOutbound )
{
- td_data = EHCI_int_AllocateTD(Cont, PID_OUT, (void*)OutData, OutLength, NULL, NULL);
+ td_data = OutData ? EHCI_int_AllocateTD(Cont, PID_OUT, (void*)OutData, OutLength, NULL, NULL) : NULL;
td_status = EHCI_int_AllocateTD(Cont, PID_IN, InData, InLength, Cb, CbData);
}
else
{
- td_data = EHCI_int_AllocateTD(Cont, PID_IN, InData, InLength, NULL, NULL);
+ td_data = InData ? EHCI_int_AllocateTD(Cont, PID_IN, InData, InLength, NULL, NULL) : NULL;
td_status = EHCI_int_AllocateTD(Cont, PID_OUT, (void*)OutData, OutLength, Cb, CbData);
+ td_status->Token |= (1 << 15);
}
// Append TDs
- EHCI_int_AppendTD(Dest, td_setup);
- EHCI_int_AppendTD(Dest, td_data);
- EHCI_int_AppendTD(Dest, td_status);
+ if( td_data ) {
+ td_setup->Link = MM_GetPhysAddr(td_data);
+ td_data->Link = MM_GetPhysAddr(td_status) | 1;
+ }
+ else {
+ td_setup->Link = MM_GetPhysAddr(td_status) | 1;
+ }
+ EHCI_int_AppendTD(Cont, Dest, td_setup);
return td_status;
}
// Allocate single TD
tEHCI_qTD *td = EHCI_int_AllocateTD(Cont, (Dir ? PID_OUT : PID_IN), Data, Length, Cb, CbData);
- EHCI_int_AppendTD(Dest, td);
+ EHCI_int_AppendTD(Cont, Dest, td);
return td;
}
EHCI_int_DeallocateTD(Cont, Handle);
}
+Uint32 EHCI_int_RootHub_FeatToMask(int Feat)
+{
+ switch(Feat)
+ {
+ case PORT_RESET: return PORTSC_PortReset;
+ case PORT_ENABLE: return PORTSC_PortEnabled;
+ default:
+ Log_Warning("EHCI", "Unknown root hub port feature %i", Feat);
+ return 0;
+ }
+}
+
+void EHCI_RootHub_SetPortFeature(void *Ptr, int Port, int Feat)
+{
+ tEHCI_Controller *Cont = Ptr;
+ if(Port >= Cont->nPorts) return;
+
+ Cont->OpRegs->PortSC[Port] |= EHCI_int_RootHub_FeatToMask(Feat);
+}
+
+void EHCI_RootHub_ClearPortFeature(void *Ptr, int Port, int Feat)
+{
+ tEHCI_Controller *Cont = Ptr;
+ if(Port >= Cont->nPorts) return;
+
+ Cont->OpRegs->PortSC[Port] &= ~EHCI_int_RootHub_FeatToMask(Feat);
+}
+
+int EHCI_RootHub_GetPortStatus(void *Ptr, int Port, int Flag)
+{
+ tEHCI_Controller *Cont = Ptr;
+ if(Port >= Cont->nPorts) return 0;
+
+ return !!(Cont->OpRegs->PortSC[Port] & EHCI_int_RootHub_FeatToMask(Flag));
+}
+
// --------------------------------------------------------------------
// Internals
// --------------------------------------------------------------------
+tEHCI_qTD *EHCI_int_GetTDFromPhys(tEHCI_Controller *Cont, Uint32 Addr)
+{
+ if( Addr == 0 ) return NULL;
+ LOG("%p + (%x - %x)", Cont->TDPool, Addr, MM_GetPhysAddr(Cont->TDPool));
+ return Cont->TDPool + (Addr - MM_GetPhysAddr(Cont->TDPool))/sizeof(tEHCI_qTD);
+}
+
tEHCI_qTD *EHCI_int_AllocateTD(tEHCI_Controller *Cont, int PID, void *Data, size_t Length, tUSBHostCb Cb, void *CbData)
{
+// Semaphore_Wait(&Cont->TDSemaphore, 1);
+ Mutex_Acquire(&Cont->TDPoolMutex);
+ for( int i = 0; i < TD_POOL_SIZE; i ++ )
+ {
+ if( ((Cont->TDPool[i].Token >> 8) & 3) != 3 )
+ continue ;
+ Cont->TDPool[i].Token = (PID << 8) | (Length << 16);
+ // NOTE: Assumes that `Length` is <= PAGE_SIZE
+ Cont->TDPool[i].Pages[0] = MM_GetPhysAddr(Data);
+ if( (Cont->TDPool[i].Pages[0] & (PAGE_SIZE-1)) + Length - 1 > PAGE_SIZE )
+ Cont->TDPool[i].Pages[1] = MM_GetPhysAddr((char*)Data + Length - 1) & ~(PAGE_SIZE-1);
+ Mutex_Release(&Cont->TDPoolMutex);
+ LOG("Allocated %p for PID %i on %p", &Cont->TDPool[i], PID, Cont);
+ return &Cont->TDPool[i];
+ }
+
+ Mutex_Release(&Cont->TDPoolMutex);
return NULL;
}
void EHCI_int_DeallocateTD(tEHCI_Controller *Cont, tEHCI_qTD *TD)
{
+ UNIMPLEMENTED();
}
-void EHCI_int_AppendTD(tEHCI_QH *QH, tEHCI_qTD *TD)
+void EHCI_int_AppendTD(tEHCI_Controller *Cont, tEHCI_QH *QH, tEHCI_qTD *TD)
{
+ tEHCI_qTD *ptd = NULL;
+ Uint32 link = QH->CurrentTD;
+
+ // TODO: Need locking and validation here
+ while( link && !(link & 1) )
+ {
+ ptd = EHCI_int_GetTDFromPhys(Cont, link);
+ link = ptd->Link;
+ }
+ // TODO: Figure out how to follow this properly
+ if( !ptd ) {
+ QH->CurrentTD = MM_GetPhysAddr(TD);
+ }
+ else
+ ptd->Link = MM_GetPhysAddr(TD);
}
tEHCI_QH *EHCI_int_AllocateQH(tEHCI_Controller *Cont, int Endpoint, size_t MaxPacketSize)
{
+ tEHCI_QH *ret;
+ Mutex_Acquire(&Cont->QHPoolMutex);
+ for( int i = 0; i < QH_POOL_SIZE; i ++ )
+ {
+ if( !MM_GetPhysAddr( Cont->QHPools[i/QH_POOL_NPERPAGE] ) ) {
+ tPAddr tmp;
+ Cont->QHPools[i/QH_POOL_NPERPAGE] = (void*)MM_AllocDMA(1, 32, &tmp);
+ memset(Cont->QHPools[i/QH_POOL_NPERPAGE], 0, PAGE_SIZE);
+ }
+
+ ret = &Cont->QHPools[i/QH_POOL_NPERPAGE][i%QH_POOL_NPERPAGE];
+ if( ret->HLink == 0 ) {
+ ret->HLink = 1;
+ ret->CurrentTD = 0;
+ ret->Overlay.Link = 1;
+ ret->Endpoint = (Endpoint >> 4) | ((Endpoint & 0xF) << 8)
+ | (MaxPacketSize << 16);
+ // TODO: Endpoint speed (13:12) 0:Full, 1:Low, 2:High
+ // TODO: Control Endpoint Flag (27) 0:*, 1:Full/Low Control
+ Mutex_Release(&Cont->QHPoolMutex);
+ return ret;
+ }
+ }
+ Mutex_Release(&Cont->QHPoolMutex);
return NULL;
}
void EHCI_int_DeallocateQH(tEHCI_Controller *Cont, tEHCI_QH *QH)
{
+ // TODO: Ensure it's unused (somehow)
+ QH->HLink = 0;
+}
+
+void EHCI_int_HandlePortConnectChange(tEHCI_Controller *Cont, int Port)
+{
+ // Connect Event
+ if( Cont->OpRegs->PortSC[Port] & PORTSC_CurrentConnectStatus )
+ {
+ // Is the device low-speed?
+ if( (Cont->OpRegs->PortSC[Port] & PORTSC_LineStatus_MASK) == PORTSC_LineStatus_Kstate )
+ {
+ LOG("Low speed device on %p Port %i, giving to companion", Cont, Port);
+ Cont->OpRegs->PortSC[Port] |= PORTSC_PortOwner;
+ }
+ // not a low-speed device, EHCI reset
+ else
+ {
+ LOG("Device connected on %p #%i", Cont, Port);
+ // Reset procedure.
+ USB_PortCtl_BeginReset(Cont->RootHub, Port);
+ }
+ }
+ // Disconnect Event
+ else
+ {
+ if( Cont->OpRegs->PortSC[Port] & PORTSC_PortOwner ) {
+ LOG("Companion port %i disconnected, taking it back", Port);
+ Cont->OpRegs->PortSC[Port] &= ~PORTSC_PortOwner;
+ }
+ else {
+ LOG("Port %i disconnected", Port);
+ USB_DeviceDisconnected(Cont->RootHub, Port);
+ }
+ }
}
+void EHCI_int_InterruptThread(void *ControllerPtr)
+{
+ tEHCI_Controller *Cont = ControllerPtr;
+ while(Cont->OpRegs)
+ {
+ Uint32 events;
+
+ LOG("sleeping for events");
+ events = Threads_WaitEvents(EHCI_THREADEVENT_IOC|EHCI_THREADEVENT_PORTSC);
+ if( !events ) {
+ // TODO: Should this cause a termination?
+ }
+ LOG("events = 0x%x", events);
+
+ if( events & EHCI_THREADEVENT_IOC )
+ {
+ // IOC, Do whatever it is you do
+ }
+
+ // Port status change interrupt
+ if( events & EHCI_THREADEVENT_PORTSC )
+ {
+ // Check for port status changes
+ for(int i = 0; i < Cont->nPorts; i ++ )
+ {
+ Uint32 sts = Cont->OpRegs->PortSC[i];
+ LOG("Port %i: sts = %x", i, sts);
+ Cont->OpRegs->PortSC[i] = sts;
+ if( sts & PORTSC_ConnectStatusChange )
+ EHCI_int_HandlePortConnectChange(Cont, i);
+
+ if( sts & PORTSC_PortEnableChange )
+ {
+ // Handle enable/disable
+ }
+
+ if( sts & PORTSC_OvercurrentChange )
+ {
+ // Handle over-current change
+ }
+ }
+ }
+
+ LOG("Going back to sleep");
+ }
+}
#ifndef _EHCI_H_
#define _EHCI_H_
+#include <threads.h>
+
#define PERIODIC_SIZE 1024
typedef struct sEHCI_CapRegs tEHCI_CapRegs;
* 15 = Asynchronous Schedule Status
* 16:31 = Reserved ?(Zero)
*/
- Uint32 USBSts;
+ volatile Uint32 USBSts;
/**
* USB Interrupt Enable Register
*
*
* Bits 14:3 are used as n index into PeridocListBase
*/
- Uint32 FrIndex;
+ volatile Uint32 FrIndex;
/**
* Control Data Structure Segment Register
*
* 22 = Wake on Over-current Enable
* 23:31 = Reserved (ZERO)
*/
- Uint32 PortSC[15];
+ volatile Uint32 PortSC[15];
};
#define USBCMD_Run 0x0001
#define USBINTR_HostSystemError 0x0010
#define USBINTR_AsyncAdvance 0x0020
+#define PORTSC_CurrentConnectStatus 0x0001
+#define PORTSC_ConnectStatusChange 0x0002
+#define PORTSC_PortEnabled 0x0004
+#define PORTSC_PortEnableChange 0x0008
+#define PORTSC_OvercurrentActive 0x0010
+#define PORTSC_OvercurrentChange 0x0020
+#define PORTSC_ForcePortResume 0x0040
+#define PORTSC_Suspend 0x0080
+#define PORTSC_PortReset 0x0100
+#define PORTSC_Reserved1 0x0200
+#define PORTSC_LineStatus_MASK 0x0C00
+#define PORTSC_LineStatus_SE0 0x0000
+#define PORTSC_LineStatus_Jstate 0x0400
+#define PORTSC_LineStatus_Kstate 0x0800
+#define PORTSC_LineStatus_Undef 0x0C00
+#define PORTSC_PortPower 0x1000
+#define PORTSC_PortOwner 0x2000
+#define PORTSC_PortIndicator_MASK 0xC000
+#define PORTSC_PortIndicator_Off 0x0000
+#define PORTSC_PortIndicator_Amber 0x4000
+#define PORTSC_PortIndicator_Green 0x800
+
// Isochronous (High-Speed) Transfer Descriptor
struct sEHCI_iTD
{
Uint32 Endpoint;
Uint32 EndpointExt;
Uint32 CurrentTD;
- tEHCI_qTD Overlay;
+ struct {
+ Uint32 Link;
+ Uint32 Link2;
+ Uint32 Token;
+ Uint32 Pages[5];
+ } Overlay;
struct {
Uint8 IntOfs;
Uint8 IntPeriodPow;
tEHCI_QH *Next;
} Impl;
} __attribute__((aligned(32)));
-// sizeof = 48 (64)
+// sizeof = 48 (round up to 64)
#define PID_OUT 0
#define PID_IN 1
#define PID_SETUP 2
+#define TD_POOL_SIZE (PAGE_SIZE/sizeof(tEHCI_qTD))
+// - 256 addresses * 16 endpoints
+#define QH_POOL_SIZE (256*16)
+#define QH_POOL_PAGES (QH_POOL_SIZE*sizeof(tEHCI_QH)/PAGE_SIZE)
+#define QH_POOL_NPERPAGE (PAGE_SIZE/sizeof(tEHCI_QH))
+
struct sEHCI_Controller
{
+ tUSBHub *RootHub;
+ tThread *InterruptThread;
+ int nPorts;
+
tPAddr PhysBase;
tEHCI_CapRegs *CapRegs;
tEHCI_OpRegs *OpRegs;
int InterruptLoad[PERIODIC_SIZE];
tEHCI_QH *LastAsyncHead;
- Uint32 *PeriodicQueue;
- tEHCI_QH PeriodicQueueV[PERIODIC_SIZE];
+ tMutex PeriodicListLock;
+ Uint32 *PeriodicQueue;
+ tEHCI_QH *PeriodicQueueV[PERIODIC_SIZE];
- tEHCI_QH *QHPools[(256*16)*sizeof(tEHCI_QH)/PAGE_SIZE]; // [PAGE_SIZE/64]
- tEHCI_qTD *TDPool[PAGE_SIZE/sizeof(tEHCI_qTD)];
+ tMutex QHPoolMutex;
+ tEHCI_QH *QHPools[QH_POOL_PAGES]; // [PAGE_SIZE/64]
+ tMutex TDPoolMutex;
+ tEHCI_qTD *TDPool; // [TD_POOL_SIZE]
};
#endif
--- /dev/null
+Rationale:
+- Provides a method for ARM SoCs to use existing device drivers (e.g. USB ECHI) with minimal modifcation.
+
+Method possibilities
+# Hook in Kernel/drv/pci.c that enumerates the virtual bus using a fixed format.
-dbgbin)
QEMU=/home/tpg/apps/bin/qemu-system-x86_64
;;
+ -bin)
+ shift
+ QEMU=$1
+ ;;
+ -dbgscript)
+ QEMU="echo $QEMU"
+ ;;
-extramem)
QEMU_PARAMS=$QEMU_PARAMS" -m 768"
;;
-notee)
_NOTEE="yes"
;;
+ -nographic)
+ _NOGRAPHIC="yes"
+ ;;
esac
shift
done
if [ "x$_NOUSB" != "xyes" ] ; then
QEMU_PARAMS=$QEMU_PARAMS" -usb"
+ QEMU_PARAMS=$QEMU_PARAMS" -device usb-ehci"
QEMU_PARAMS=$QEMU_PARAMS" -drive id=test_usb_image,file=USB_Test_Image.img,if=none"
QEMU_PARAMS=$QEMU_PARAMS" -device usb-storage,drive=test_usb_image"
QEMU_PARAMS=$QEMU_PARAMS" -usbdevice mouse"
# /home/tpg/apps/bin/qemu-system-x86_64 $QEMU_PARAMS -serial stdio -serial file:QemuLog.txt
# qemu-system-x86_64 $QEMU_PARAMS -serial stdio | tee QemuLog.txt
#echo $QEMU $BOOTOPT $QEMU_PARAMS
-if [ "x$_NOTEE" != "xyes" ] ; then
- $QEMU $BOOTOPT $QEMU_PARAMS -serial stdio | tee QemuLog.txt
-else
+if [ "x$_NOGRAPHIC" = "xyes" ] ; then
+ $QEMU $BOOTOPT $QEMU_PARAMS -nographic
+ exit
+fi
+
+if [ "x$_NOTEE" = "xyes" ] ; then
$QEMU $BOOTOPT $QEMU_PARAMS -serial stdio
+ exit
fi
+
+$QEMU $BOOTOPT $QEMU_PARAMS -serial stdio | tee QemuLog.txt
*(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
}
+ __exidx_start = .;
.text : AT(ADDR(.text)) {
code = .;
*(.text)
*(.rodata*)
}
+ __exidx_end = .;
.data ALIGN (0x1000) : AT(ADDR(.data)) {
data = .;
#define O_TRUNC OPENFLAG_TRUNCATE
#define O_APPEND OPENFLAG_APPEND
-typedef intptr_t ssize_t;
+//typedef intptr_t ssize_t;
#include "acess/sys.h"
#endif\r
for(;;);\r
}\r
+\r
+void abort(void)\r
+{\r
+ _exit(-4);\r
+}\r