From: John Hodge Date: Fri, 6 Nov 2009 10:29:56 +0000 (+0800) Subject: Added IO Cache Library, Moved FDD Driver to Modules Tree, Fixed Doxygen commenting X-Git-Tag: rel0.06~374 X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=75e87cf46a3899f76bae5c64e130cfc033562e9a;p=tpg%2Facess2.git Added IO Cache Library, Moved FDD Driver to Modules Tree, Fixed Doxygen commenting --- diff --git a/Kernel/Makefile b/Kernel/Makefile index 5f155978..9bec8449 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -21,7 +21,7 @@ OBJ += binary.o bin/elf.o OBJ += vfs/main.o vfs/open.o vfs/acls.o vfs/dir.o vfs/io.o vfs/mount.o vfs/memfile.o vfs/nodecache.o OBJ += vfs/fs/root.o vfs/fs/devfs.o OBJ += $(addprefix vfs/fs/, $(addsuffix .o,$(FILESYSTEMS))) -OBJ += drv/fifo.o drv/dma.o drv/pci.o drv/kb.o drv/vga.o drv/vterm.o +OBJ += drv/fifo.o drv/dma.o drv/iocache.o drv/pci.o drv/kb.o drv/vga.o drv/vterm.o OBJ += $(addprefix drv/, $(addsuffix .o,$(DRIVERS))) OBJ := $(addsuffix .$(ARCH), $(OBJ)) MODS += $(addprefix ../Modules/, $(addsuffix .xo.$(ARCH),$(MODULES))) @@ -33,13 +33,16 @@ DEPFILES := $(DEPFILES:%.o.$(ARCH)=%.d.$(ARCH)) SRCFILES = $(OBJ:%.o.$(ARCH)=%.c) SRCFILES := $(SRCFILES:%.ao.$(ARCH)=%.asm) -.PHONY: all clean +.PHONY: all clean apidoc all: $(BIN) clean: @$(RM) $(BIN) $(OBJ) $(DEPFILES) +apidoc: + doxygen Doxyfile.api + $(BIN): $(OBJ) $(MODS) arch/$(ARCHDIR)/link.ld Makefile @echo --- LD -o $(BIN) @$(LD) $(LDFLAGS) -o $(BIN) $(OBJ) $(MODS) -Map ../Map.$(ARCH).txt diff --git a/Kernel/drv/fdd.c b/Kernel/drv/fdd.c deleted file mode 100644 index 3c86b08b..00000000 --- a/Kernel/drv/fdd.c +++ /dev/null @@ -1,770 +0,0 @@ -/* - * AcessOS 0.1 - * Floppy Disk Access Code - */ -#define DEBUG 1 -#include -#include -#include -#include -#include - -#define WARN 0 - -// === CONSTANTS === -// --- Current Version -#define FDD_VERSION ((0<<8)|(75)) - -// --- Options -#define USE_CACHE 1 // Use Sector Cache -#define CACHE_SIZE 32 // Number of cachable sectors -#define FDD_SEEK_TIMEOUT 10 // Timeout for a seek operation -#define MOTOR_ON_DELAY 500 // Miliseconds -#define MOTOR_OFF_DELAY 2000 // Miliseconds - -// === TYPEDEFS === -/** - * \brief Representation of a floppy drive - */ -typedef struct { - int type; - volatile int motorState; //2 - On, 1 - Spinup, 0 - Off - int track[2]; - int timer; - tVFS_Node Node; -} t_floppyDevice; - -/** - * \brief Cached Sector - */ -typedef struct { - Uint64 timestamp; - Uint16 disk; - Uint16 sector; // Allows 32Mb of addressable space (Plenty for FDD) - Uint8 data[512]; -} t_floppySector; - -// === CONSTANTS === -static const char *cFDD_TYPES[] = {"None", "360kB 5.25\"", "1.2MB 5.25\"", "720kB 3.5\"", "1.44MB 3.5\"", "2.88MB 3.5\"" }; -static const int cFDD_SIZES[] = { 0, 360*1024, 1200*1024, 720*1024, 1440*1024, 2880*1024 }; -static const short cPORTBASE[] = { 0x3F0, 0x370 }; - -enum FloppyPorts { - PORT_STATUSA = 0x0, - PORT_STATUSB = 0x1, - PORT_DIGOUTPUT = 0x2, - PORT_MAINSTATUS = 0x4, - PORT_DATARATE = 0x4, - PORT_DATA = 0x5, - PORT_DIGINPUT = 0x7, - PORT_CONFIGCTRL = 0x7 -}; - -enum FloppyCommands { - FIX_DRIVE_DATA = 0x03, - HECK_DRIVE_STATUS = 0x04, - CALIBRATE_DRIVE = 0x07, - CHECK_INTERRUPT_STATUS = 0x08, - SEEK_TRACK = 0x0F, - READ_SECTOR_ID = 0x4A, - FORMAT_TRACK = 0x4D, - READ_TRACK = 0x42, - READ_SECTOR = 0x66, - WRITE_SECTOR = 0xC5, - WRITE_DELETE_SECTOR = 0xC9, - READ_DELETE_SECTOR = 0xCC, -}; - -// === PROTOTYPES === -// --- Filesystem - int FDD_Install(char **Arguments); -char *FDD_ReadDir(tVFS_Node *Node, int pos); -tVFS_Node *FDD_FindDir(tVFS_Node *dirNode, char *Name); - int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data); -Uint64 FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer); -// --- Raw Disk Access - int FDD_ReadSector(int disk, int lba, void *buf); -// --- Helpers -void FDD_IRQHandler(int Num); -void FDD_WaitIRQ(); -void FDD_SensInt(int base, Uint8 *sr0, Uint8 *cyl); -inline void FDD_AquireSpinlock(); -inline void FDD_FreeSpinlock(); -#if USE_CACHE -inline void FDD_AquireCacheSpinlock(); -inline void FDD_FreeCacheSpinlock(); -#endif -void FDD_int_SendByte(int base, char byte); - int FDD_int_GetByte(int base); -void FDD_Reset(int id); -void FDD_Recalibrate(int disk); - int FDD_int_SeekTrack(int disk, int head, int track); -void FDD_int_TimerCallback(int arg); -void FDD_int_StopMotor(int disk); -void FDD_int_StartMotor(int disk); - int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt); - -// === GLOBALS === -MODULE_DEFINE(0, FDD_VERSION, FDD, FDD_Install, NULL, NULL); -t_floppyDevice gFDD_Devices[2]; -volatile int fdd_inUse = 0; -volatile int fdd_irq6 = 0; -tDevFS_Driver gFDD_DriverInfo = { - NULL, "fdd", - { - .Size = -1, - .NumACLs = 1, - .ACLs = &gVFS_ACL_EveryoneRX, - .Flags = VFS_FFLAG_DIRECTORY, - .ReadDir = FDD_ReadDir, - .FindDir = FDD_FindDir, - .IOCtl = FDD_IOCtl - } -}; -#if USE_CACHE -int siFDD_CacheInUse = 0; -int siFDD_SectorCacheSize = CACHE_SIZE; -t_floppySector sFDD_SectorCache[CACHE_SIZE]; -#endif - -// === CODE === -/** - * \fn int FDD_Install(char **Arguments) - * \brief Installs floppy driver - */ -int FDD_Install(char **Arguments) -{ - Uint8 data; - - // Determine Floppy Types (From CMOS) - outb(0x70, 0x10); - data = inb(0x71); - gFDD_Devices[0].type = data >> 4; - gFDD_Devices[1].type = data & 0xF; - gFDD_Devices[0].track[0] = -1; - gFDD_Devices[1].track[1] = -1; - - Log("[FDD ] Detected Disk 0: %s and Disk 1: %s\n", cFDD_TYPES[data>>4], cFDD_TYPES[data&0xF]); - - // Clear FDD IRQ Flag - FDD_SensInt(0x3F0, NULL, NULL); - // Install IRQ6 Handler - IRQ_AddHandler(6, FDD_IRQHandler); - // Reset Primary FDD Controller - FDD_Reset(0); - - // Initialise Root Node - gFDD_DriverInfo.RootNode.CTime = gFDD_DriverInfo.RootNode.MTime - = gFDD_DriverInfo.RootNode.ATime = now(); - - // Initialise Child Nodes - gFDD_Devices[0].Node.Inode = 0; - gFDD_Devices[0].Node.Flags = 0; - gFDD_Devices[0].Node.NumACLs = 0; - gFDD_Devices[0].Node.Read = FDD_ReadFS; - gFDD_Devices[0].Node.Write = NULL;//fdd_writeFS; - memcpy(&gFDD_Devices[1].Node, &gFDD_Devices[0].Node, sizeof(tVFS_Node)); - - gFDD_Devices[1].Node.Inode = 1; - - // Set Lengths - gFDD_Devices[0].Node.Size = cFDD_SIZES[data >> 4]; - gFDD_Devices[1].Node.Size = cFDD_SIZES[data & 0xF]; - - // Create Sector Cache - #if USE_CACHE - //sFDD_SectorCache = malloc(sizeof(*sFDD_SectorCache)*CACHE_SIZE); - //siFDD_SectorCacheSize = CACHE_SIZE; - #endif - - // Register with devfs - DevFS_AddDevice(&gFDD_DriverInfo); - - return 0; -} - -/** - * \fn char *FDD_ReadDir(tVFS_Node *Node, int pos) - * \brief Read Directory - */ -char *FDD_ReadDir(tVFS_Node *Node, int pos) -{ - char name[2] = "0\0"; - //Update Accessed Time - //gFDD_DrvInfo.rootNode.atime = now(); - - //Check for bounds - if(pos >= 2 || pos < 0) - return NULL; - - if(gFDD_Devices[pos].type == 0) - return VFS_SKIP; - - name[0] += pos; - - //Return - return strdup(name); -} - -/** - * \fn tVFS_Node *FDD_FindDir(tVFS_Node *Node, char *filename); - * \brief Find File Routine (for vfs_node) - */ -tVFS_Node *FDD_FindDir(tVFS_Node *Node, char *filename) -{ - int i; - - ENTER("sfilename", filename); - - // Sanity check string - if(filename == NULL) { - LEAVE('n'); - return NULL; - } - - // Check string length (should be 1) - if(filename[0] == '\0' || filename[1] != '\0') { - LEAVE('n'); - return NULL; - } - - // Get First character - i = filename[0] - '0'; - - // Check for 1st disk and if it is present return - if(i == 0 && gFDD_Devices[0].type != 0) { - LEAVE('p', &gFDD_Devices[0].Node); - return &gFDD_Devices[0].Node; - } - - // Check for 2nd disk and if it is present return - if(i == 1 && gFDD_Devices[1].type != 0) { - LEAVE('p', &gFDD_Devices[1].Node); - return &gFDD_Devices[1].Node; - } - - // Else return null - LEAVE('n'); - return NULL; -} - -/** - * \fn int FDD_IOCtl(tVFS_Node *node, int id, void *data) - * \brief Stub ioctl function - */ -int FDD_IOCtl(tVFS_Node *node, int id, void *data) -{ - switch(id) - { - case DRV_IOCTL_TYPE: return DRV_TYPE_DISK; - case DRV_IOCTL_IDENT: memcpy(data, "FDD\0", 4); return 1; - case DRV_IOCTL_VERSION: return FDD_VERSION; - default: return 0; - } -} - -/** - * \fn Uint64 fdd_readFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer) - * \brief Read Data from a disk -*/ -Uint64 FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer) -{ - int i = 0; - int disk; - Uint32 buf[128]; - - ENTER("xoff xlen pbuffer", off, len, buffer); - - if(node == NULL) { - LEAVE('i', -1); - return -1; - } - - if(node->Inode != 0 && node->Inode != 1) { - LEAVE('i', -1); - return -1; - } - - disk = node->Inode; - - // Update Accessed Time - node->ATime = now(); - - if((off & 0x1FF) || (len & 0x1FF)) - { - // Un-Aligned Offset/Length - int startOff = off>>9; - int sectOff = off&0x1FF; - int sectors = (len+0x1FF)>>9; - - LOG("Non-aligned Read"); - - //Read Starting Sector - if(!FDD_ReadSector(disk, startOff, buf)) - return 0; - memcpy(buffer, (char*)(buf+sectOff), len>512-sectOff?512-sectOff:len); - - //If the data size is one sector or less - if(len <= 512-sectOff) { - LEAVE('X', len); - return len; //Return - } - buffer += 512-sectOff; - - //Read Middle Sectors - for(i=1;i> 9; - int sector = off >> 9; - LOG("Aligned Read"); - //Aligned Offset and Length - Simple Code - for(i=0;i>1]; - - LOG("Calculating Disk Dimensions"); - //Get CHS position - if(FDD_int_GetDims(gFDD_Devices[disk].type, lba, &cyl, &head, &sec, &spt) != 1) { - LEAVE('i', -1); - return -1; - } - - // Remove Old Timer - Time_RemoveTimer(gFDD_Devices[disk].timer); - // Check if Motor is on - if(gFDD_Devices[disk].motorState == 0) { - FDD_int_StartMotor(disk); - } - - LOG("Wait for Motor Spinup"); - - // Wait for spinup - while(gFDD_Devices[disk].motorState == 1) Threads_Yield(); - - LOG("C:%i,H:%i,S:%i", cyl, head, sec); - LOG("Acquire Spinlock"); - - FDD_AquireSpinlock(); - - // Seek to track - outb(base+CALIBRATE_DRIVE, 0); - i = 0; - while(FDD_int_SeekTrack(disk, head, (Uint8)cyl) == 0 && i++ < FDD_SEEK_TIMEOUT ) Threads_Yield(); - //FDD_SensInt(base, NULL, NULL); // Wait for IRQ - - LOG("Setting DMA for read"); - - //Read Data from DMA - DMA_SetChannel(2, 512, 1); // Read 512 Bytes - - LOG("Sending read command"); - - //Threads_Wait(100); // Wait for Head to settle - Time_Delay(100); - FDD_int_SendByte(base, READ_SECTOR); // Was 0xE6 - FDD_int_SendByte(base, (head << 2) | (disk&1)); - FDD_int_SendByte(base, (Uint8)cyl); - FDD_int_SendByte(base, (Uint8)head); - FDD_int_SendByte(base, (Uint8)sec); - FDD_int_SendByte(base, 0x02); // Bytes Per Sector (Real BPS=128*2^{val}) - FDD_int_SendByte(base, spt); // SPT - FDD_int_SendByte(base, 0x1B); // Gap Length (27 is default) - FDD_int_SendByte(base, 0xFF); // Data Length - - // Wait for IRQ - LOG("Waiting for Data to be read"); - FDD_WaitIRQ(); - - // Read Data from DMA - LOG(" FDD_ReadSector: Reading Data"); - DMA_ReadData(2, 512, buf); - - // Clear Input Buffer - LOG("Clearing Input Buffer"); - FDD_int_GetByte(base); FDD_int_GetByte(base); FDD_int_GetByte(base); - FDD_int_GetByte(base); FDD_int_GetByte(base); FDD_int_GetByte(base); FDD_int_GetByte(base); - - LOG("Realeasing Spinlock and Setting motor to stop"); - // Release Spinlock - FDD_FreeSpinlock(); - - //Set timer to turn off motor affter a gap - gFDD_Devices[disk].timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotor, (void*)disk); //One Shot Timer - - #if USE_CACHE - { - FDD_AquireCacheSpinlock(); - int oldest = 0; - for(i=0;i>1]; - - // Check if seeking is needed - if(gFDD_Devices[disk].track[head] == track) - return 1; - - // - Seek Head 0 - FDD_int_SendByte(base, SEEK_TRACK); - FDD_int_SendByte(base, (head<<2)|(disk&1)); - FDD_int_SendByte(base, track); // Send Seek command - FDD_WaitIRQ(); - FDD_SensInt(base, &sr0, &cyl); // Wait for IRQ - if((sr0 & 0xF0) != 0x20) { - LOG("sr0 = 0x%x", sr0); - return 0; //Check Status - } - if(cyl != track) return 0; - - // Set Track in structure - gFDD_Devices[disk].track[head] = track; - return 1; -} - -/** - * \fn int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt) - * \brief Get Dimensions of a disk - */ -int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt) -{ - switch(type) { - case 0: - return 0; - - // 360Kb 5.25" - case 1: - *spt = 9; - *s = (lba % 9) + 1; - *c = lba / 18; - *h = (lba / 9) & 1; - break; - - // 1220Kb 5.25" - case 2: - *spt = 15; - *s = (lba % 15) + 1; - *c = lba / 30; - *h = (lba / 15) & 1; - break; - - // 720Kb 3.5" - case 3: - *spt = 9; - *s = (lba % 9) + 1; - *c = lba / 18; - *h = (lba / 9) & 1; - break; - - // 1440Kb 3.5" - case 4: - *spt = 18; - *s = (lba % 18) + 1; - *c = lba / 36; - *h = (lba / 18) & 1; - break; - - // 2880Kb 3.5" - case 5: - *spt = 36; - *s = (lba % 36) + 1; - *c = lba / 72; - *h = (lba / 32) & 1; - break; - - default: - return -2; - } - return 1; -} - -/** - * \fn void FDD_IRQHandler(int Num) - * \brief Handles IRQ6 - */ -void FDD_IRQHandler(int Num) -{ - fdd_irq6 = 1; -} - -/** - * \fn FDD_WaitIRQ() - * \brief Wait for an IRQ6 - */ -void FDD_WaitIRQ() -{ - // Wait for IRQ - while(!fdd_irq6) Threads_Yield(); - fdd_irq6 = 0; -} - -void FDD_SensInt(int base, Uint8 *sr0, Uint8 *cyl) -{ - FDD_int_SendByte(base, CHECK_INTERRUPT_STATUS); - if(sr0) *sr0 = FDD_int_GetByte(base); - else FDD_int_GetByte(base); - if(cyl) *cyl = FDD_int_GetByte(base); - else FDD_int_GetByte(base); -} - -void FDD_AquireSpinlock() -{ - while(fdd_inUse) - Threads_Yield(); - fdd_inUse = 1; -} - -inline void FDD_FreeSpinlock() -{ - fdd_inUse = 0; -} - -#if USE_CACHE -inline void FDD_AquireCacheSpinlock() -{ - while(siFDD_CacheInUse) Threads_Yield(); - siFDD_CacheInUse = 1; -} -inline void FDD_FreeCacheSpinlock() -{ - siFDD_CacheInUse = 0; -} -#endif - -/** - * void FDD_int_SendByte(int base, char byte) - * \brief Sends a command to the controller - */ -void FDD_int_SendByte(int base, char byte) -{ - volatile int state; - int timeout = 128; - for( ; timeout--; ) - { - state = inb(base + PORT_MAINSTATUS); - if ((state & 0xC0) == 0x80) - { - outb(base + PORT_DATA, byte); - return; - } - inb(0x80); //Delay - } - #if WARN - Warning("FDD_int_SendByte - Timeout sending byte 0x%x to base 0x%x\n", byte, base); - #endif -} - -/** - * int FDD_int_GetByte(int base, char byte) - * \brief Receive data from fdd controller - */ -int FDD_int_GetByte(int base) -{ - volatile int state; - int timeout; - for( timeout = 128; timeout--; ) - { - state = inb((base + PORT_MAINSTATUS)); - if ((state & 0xd0) == 0xd0) - return inb(base + PORT_DATA); - inb(0x80); - } - return -1; -} - -/** - * \brief Recalibrate the specified disk - */ -void FDD_Recalibrate(int disk) -{ - ENTER("idisk", disk); - - LOG("Starting Motor"); - FDD_int_StartMotor(disk); - // Wait for Spinup - while(gFDD_Devices[disk].motorState == 1) Threads_Yield(); - - LOG("Sending Calibrate Command"); - FDD_int_SendByte(cPORTBASE[disk>>1], CALIBRATE_DRIVE); - FDD_int_SendByte(cPORTBASE[disk>>1], disk&1); - - LOG("Waiting for IRQ"); - FDD_WaitIRQ(); - FDD_SensInt(cPORTBASE[disk>>1], NULL, NULL); - - LOG("Stopping Motor"); - FDD_int_StopMotor(disk); - LEAVE('-'); -} - -/** - * \brief Reset the specified FDD controller - */ -void FDD_Reset(int id) -{ - int base = cPORTBASE[id]; - - ENTER("iID", id); - - outb(base + PORT_DIGOUTPUT, 0); // Stop Motors & Disable FDC - outb(base + PORT_DIGOUTPUT, 0x0C); // Re-enable FDC (DMA and Enable) - - LOG("Awaiting IRQ"); - - FDD_WaitIRQ(); - FDD_SensInt(base, NULL, NULL); - - LOG("Setting Driver Info"); - outb(base + PORT_DATARATE, 0); // Set data rate to 500K/s - FDD_int_SendByte(base, FIX_DRIVE_DATA); // Step and Head Load Times - FDD_int_SendByte(base, 0xDF); // Step Rate Time, Head Unload Time (Nibble each) - FDD_int_SendByte(base, 0x02); // Head Load Time >> 1 - while(FDD_int_SeekTrack(0, 0, 1) == 0); // set track - while(FDD_int_SeekTrack(0, 1, 1) == 0); // set track - - LOG("Recalibrating Disk"); - FDD_Recalibrate((id<<1)|0); - FDD_Recalibrate((id<<1)|1); - - LEAVE('-'); -} - -/** - * \fn void FDD_int_TimerCallback() - * \brief Called by timer - */ -void FDD_int_TimerCallback(int arg) -{ - ENTER("iarg", arg); - if(gFDD_Devices[arg].motorState == 1) - gFDD_Devices[arg].motorState = 2; - Time_RemoveTimer(gFDD_Devices[arg].timer); - gFDD_Devices[arg].timer = -1; - LEAVE('-'); -} - -/** - * \fn void FDD_int_StartMotor(char disk) - * \brief Starts FDD Motor - */ -void FDD_int_StartMotor(int disk) -{ - Uint8 state; - state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT ); - state |= 1 << (4+disk); - outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state ); - gFDD_Devices[disk].motorState = 1; - gFDD_Devices[disk].timer = Time_CreateTimer(MOTOR_ON_DELAY, FDD_int_TimerCallback, (void*)disk); //One Shot Timer -} - -/** - * \fn void FDD_int_StopMotor(int disk) - * \brief Stops FDD Motor - */ -void FDD_int_StopMotor(int disk) -{ - Uint8 state; - state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT ); - state &= ~( 1 << (4+disk) ); - outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state ); - gFDD_Devices[disk].motorState = 0; -} - -/** - * \fn void ModuleUnload() - * \brief Prepare the module for removal - */ -void ModuleUnload() -{ - int i; - FDD_AquireSpinlock(); - for(i=0;i<4;i++) { - Time_RemoveTimer(gFDD_Devices[i].timer); - FDD_int_StopMotor(i); - } - //IRQ_Clear(6); -} diff --git a/Kernel/drv/iocache.c b/Kernel/drv/iocache.c new file mode 100644 index 00000000..8ea6929a --- /dev/null +++ b/Kernel/drv/iocache.c @@ -0,0 +1,317 @@ +/* + * Acess2 Kernel + * - IO Cache + * + * By thePowersGang (John Hodge) + */ +#include +#include + +// === TYPES === +typedef struct sIOCache_Ent tIOCache_Ent; + +// === STRUCTURES === +struct sIOCache_Ent +{ + tIOCache_Ent *Next; + Uint64 Num; + Sint64 LastAccess; + Sint64 LastWrite; + Uint8 Data[]; +}; + +struct sIOCache +{ + tIOCache *Next; + int SectorSize; + int Lock; + int Mode; + Uint32 ID; + tIOCache_WriteCallback Write; + int CacheSize; + int CacheUsed; + tIOCache_Ent *Entries; +}; + +// === GLOBALS === + int glIOCache_Caches; +tIOCache *gIOCache_Caches = NULL; + int giIOCache_NumCaches = 0; + +// === CODE === +/** + * \fn tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize ) + * \brief Creates a new IO Cache + */ +tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize ) +{ + tIOCache *ret = malloc( sizeof(tIOCache) ); + + // Sanity Check + if(!ret) return NULL; + + // Fill Structure + ret->SectorSize = SectorSize; + ret->Mode = IOCACHE_WRITEBACK; + ret->ID = ID; + ret->Write = Write; + ret->CacheSize = CacheSize; + ret->CacheUsed = 0; + ret->Entries = 0; + + // Append to list + LOCK( &glIOCache_Caches ); + ret->Next = gIOCache_Caches; + gIOCache_Caches = ret; + RELEASE( &glIOCache_Caches ); + + // Return + return ret; +} + +/** + * \fn int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer ) + * \brief Read from a cached sector + */ +int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer ) +{ + tIOCache_Ent *ent; + + // Sanity Check! + if(!Cache || !Buffer) + return -1; + + // Lock + LOCK( &Cache->Lock ); + if(Cache->CacheSize == 0) { + RELEASE( &Cache->Lock ); + return -1; + } + + // Search the list + for( ent = Cache->Entries; ent; ent = ent->Next ) + { + // Have we found what we are looking for? + if( ent->Num == Sector ) { + memcpy(Buffer, ent->Data, Cache->SectorSize); + ent->LastAccess = now(); + RELEASE( &Cache->Lock ); + return 1; + } + // It's a sorted list, so as soon as we go past `Sector` we know + // it's not there + if(ent->Num > Sector) break; + } + + RELEASE( &Cache->Lock ); + return 0; +} + +/** + * \fn int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer ) + * \brief Cache a sector + */ +int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer ) +{ + tIOCache_Ent *ent, *prev; + tIOCache_Ent *new; + tIOCache_Ent *oldest = NULL, *oldestPrev; + + // Sanity Check! + if(!Cache || !Buffer) + return -1; + + // Lock + LOCK( &Cache->Lock ); + if(Cache->CacheSize == 0) { + RELEASE( &Cache->Lock ); + return -1; + } + + // Search the list + prev = (tIOCache_Ent*)&Cache->Entries; + for( ent = Cache->Entries; ent; prev = ent, ent = ent->Next ) + { + // Is it already here? + if( ent->Num == Sector ) { + RELEASE( &Cache->Lock ); + return 0; + } + + // Check if we have found the oldest entry + if( !oldest || oldest->LastAccess > ent->LastAccess ) { + oldest = ent; + oldestPrev = prev; + } + + // Here we go! + if(ent->Num > Sector) + break; + } + + // Create the new entry + new = malloc( sizeof(tIOCache_Ent) + Cache->SectorSize ); + new->Next = ent; + new->Num = Sector; + new->LastAccess = now(); + new->LastWrite = 0; // Zero is special, it means unmodified + memcpy(new->Data, Buffer, Cache->SectorSize); + + // Have we reached the maximum cached entries? + if( Cache->CacheUsed == Cache->CacheSize ) + { + tIOCache_Ent *savedPrev = prev; + oldestPrev = (tIOCache_Ent*)&Cache->Entries; + // If so, search for the least recently accessed entry + for( ; ent; prev = ent, ent = ent->Next ) + { + // Check if we have found the oldest entry + if( !oldest || oldest->LastAccess > ent->LastAccess ) { + oldest = ent; + oldestPrev = prev; + } + } + // Remove from list, write back and free + oldestPrev->Next = oldest->Next; + if(oldest->LastWrite && Cache->Mode != IOCACHE_VIRTUAL) + Cache->Write(Cache->ID, oldest->Num, oldest->Data); + free(oldest); + + // Decrement the used count + Cache->CacheUsed --; + + // Restore `prev` + prev = savedPrev; + } + + // Append to list + prev->Next = new; + Cache->CacheUsed ++; + + // Release Spinlock + RELEASE( &Cache->Lock ); + + // Return success + return 1; +} + +/** + * \fn int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer ) + * \brief Read from a cached sector + */ +int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer ) +{ + tIOCache_Ent *ent; + + // Sanity Check! + if(!Cache || !Buffer) + return -1; + // Lock + LOCK( &Cache->Lock ); + if(Cache->CacheSize == 0) { + RELEASE( &Cache->Lock ); + return -1; + } + + // Search the list + for( ent = Cache->Entries; ent; ent = ent->Next ) + { + // Have we found what we are looking for? + if( ent->Num == Sector ) { + memcpy(ent->Data, Buffer, Cache->SectorSize); + ent->LastAccess = ent->LastWrite = now(); + + if(Cache->Mode == IOCACHE_WRITEBACK) { + Cache->Write(Cache->ID, Sector, Buffer); + ent->LastWrite = 0; + } + + RELEASE( &Cache->Lock ); + return 1; + } + // It's a sorted list, so as soon as we go past `Sector` we know + // it's not there + if(ent->Num > Sector) break; + } + + RELEASE( &Cache->Lock ); + return 0; +} + +/** + * \fn void IOCache_Flush( tIOCache *Cache ) + * \brief Flush a cache + */ +void IOCache_Flush( tIOCache *Cache ) +{ + tIOCache_Ent *ent; + + if( Cache->Mode == IOCACHE_VIRTUAL ) return; + + // Lock + LOCK( &Cache->Lock ); + if(Cache->CacheSize == 0) { + RELEASE( &Cache->Lock ); + return; + } + + // Write All + for( ent = Cache->Entries; ent; ent = ent->Next ) + { + Cache->Write(Cache->ID, ent->Num, ent->Data); + ent->LastWrite = 0; + } + + RELEASE( &Cache->Lock ); +} + +/** + * \fn void IOCache_Destroy( tIOCache *Cache ) + * \brief Destroy a cache + */ +void IOCache_Destroy( tIOCache *Cache ) +{ + tIOCache_Ent *ent, *prev = NULL; + + // Lock + LOCK( &Cache->Lock ); + if(Cache->CacheSize == 0) { + RELEASE( &Cache->Lock ); + return; + } + + // Free All + for(ent = Cache->Entries; + ent; + prev = ent, ent = ent->Next, free(prev) ) + { + if( Cache->Mode != IOCACHE_VIRTUAL ) + { + Cache->Write(Cache->ID, ent->Num, ent->Data); + ent->LastWrite = 0; + } + } + + Cache->CacheSize = 0; + + RELEASE( &Cache->Lock ); + + // Remove from list + LOCK( &glIOCache_Caches ); + { + tIOCache *ent; + tIOCache *prev = (tIOCache*)&gIOCache_Caches; + for(ent = gIOCache_Caches; + ent; + prev = ent, ent = ent->Next ) + { + if(ent == Cache) { + prev->Next = ent->Next; + break; + } + } + } + RELEASE( &glIOCache_Caches ); + + free(Cache); +} diff --git a/Kernel/drv/vterm.c b/Kernel/drv/vterm.c index afd53ffc..1dde000a 100644 --- a/Kernel/drv/vterm.c +++ b/Kernel/drv/vterm.c @@ -24,6 +24,7 @@ #define DEFAULT_COLOUR (VT_COL_BLACK|(0xAAA<<16)) #define VT_FLAG_HIDECSR 0x01 +#define VT_FLAG_HASFB 0x10 //!< Set if the VTerm has requested the Framebuffer enum eVT_InModes { VT_INMODE_TEXT8, // UTF-8 Text Mode (VT100 Emulation) @@ -184,7 +185,7 @@ int VT_Install(char **Arguments) DevFS_AddDevice( &gVT_DrvInfo ); // Set kernel output to VT0 - //Debug_SetKTerminal("/Devices/VTerm/0"); + Debug_SetKTerminal("/Devices/VTerm/0"); return 0; } diff --git a/Kernel/include/fs_devfs.h b/Kernel/include/fs_devfs.h index 4b29cf1b..add3eeb7 100644 --- a/Kernel/include/fs_devfs.h +++ b/Kernel/include/fs_devfs.h @@ -3,18 +3,30 @@ * Device Filesystem (DevFS) * - vfs/fs/devfs.c */ +/** + * \file fs_devfs.h + * \brief Acess Device Filesystem interface + */ #ifndef _FS_DEVFS_H #define _FS_DEVFS_H #include // === TYPES === +/** + * \brief DevFS Driver + */ typedef struct sDevFS_Driver { - struct sDevFS_Driver *Next; - char *Name; - tVFS_Node RootNode; + struct sDevFS_Driver *Next; //!< Set to NULL by drivers (used internally) + char *Name; //!< Name of the driver file/folder + tVFS_Node RootNode; //!< Root node of driver } tDevFS_Driver; // === FUNCTIONS === +/** + * \fn int DevFS_AddDevice(tDevFS_Driver *Dev) + * \brief Registers a device in the Device filesystem + * \param Dev Pointer to a persistant structure that represents the driver + */ extern int DevFS_AddDevice(tDevFS_Driver *Dev); #endif diff --git a/Kernel/include/iocache.h b/Kernel/include/iocache.h new file mode 100644 index 00000000..f008a2d0 --- /dev/null +++ b/Kernel/include/iocache.h @@ -0,0 +1,120 @@ +/* + * Acess2 Kernel + * - IO Cache + * + * By thePowersGang (John Hodge) + */ +/** + * \file iocache.h + * \brief I/O Caching Helper Subsystem + * + * The IO Cache abstracts caching of disk sectors away from the device + * driver to reduce code duplication and allow a central location for + * disk cache management that can be flushed simply by the kernel, without + * having to ask each driver to do it indivitually. + */ +#ifndef _IOCHACHE_H_ +#define _IOCHACHE_H_ + +// === TYPES === +/** + * \brief IO Cache Handle + */ +typedef struct sIOCache tIOCache; +/** + * \brief Write Callback + * + * Called to write a sector back to the device + */ +typedef int (*tIOCache_WriteCallback)(Uint32 ID, Uint64 Sector, void *Buffer); + +// === CONSTANTS === +/** + * \brief I/O Cache handling modes + */ +enum eIOCache_Modess { + /** + * \brief Writeback + * + * Transparently writes data straight to the device when the cache + * is written to. + */ + IOCACHE_WRITEBACK, + /** + * \brief Delay Write + * + * Only writes when instructed to (by ::IOCache_Flush) or when a + * cached sector is being reallocated. + */ + IOCACHE_DELAYWRITE, + /** + * \brief Virtual - No Writes + * + * Changes to the cache contents are only reflected in memory, + * any calls to ::IOCache_Flush will silently return without doing + * anything and if a sector is reallocated, all changes will be lost + */ + IOCACHE_VIRTUAL +}; + +// === FUNCTIONS === +/** + * \brief Creates a new IO Cache + * \param Write Function to call to write a sector to the device + * \param ID ID to pass to \a Write + * \param SectorSize Size of a cached sector + * \param CacheSize Maximum number of objects that can be in the cache at one time + */ +tIOCache *IOCache_Create( tIOCache_WriteCallback Write, Uint32 ID, int SectorSize, int CacheSize ); + +/** + * \brief Reads from a cached sector + * \param Cache Cache handle returned by ::IOCache_Create + * \param Sector Sector's ID number + * \param Buffer Destination for the data read + * \return 1 if the data was read, 0 if the sector is not cached, -1 on error + */ + int IOCache_Read( tIOCache *Cache, Uint64 Sector, void *Buffer ); + +/** + * \brief Adds a sector to the cache + * \param Cache Cache handle returned by ::IOCache_Create + * \param Sector Sector's ID number + * \param Buffer Data to cache + * \return 1 on success, 0 if the sector is already cached, -1 on error + */ + int IOCache_Add( tIOCache *Cache, Uint64 Sector, void *Buffer ); + +/** + * \brief Writes to a cached sector + * \param Cache Cache handle returned by ::IOCache_Create + * \param Sector Sector's ID number + * \param Buffer Data to write to the cache + * \return 1 if the data was read, 0 if the sector is not cached, -1 on error + * + * If the sector is in the cache, it is updated. + * Wether the Write callback is called depends on the selected caching + * behaviour. + */ + int IOCache_Write( tIOCache *Cache, Uint64 Sector, void *Buffer ); + +/** + * \brief Flush altered sectors out to the device + * \param Cache Cache handle returned by ::IOCache_Create + * + * This will call the cache's write callback on each altered sector + * to flush the write cache. + */ +void IOCache_Flush( tIOCache *Cache ); + +/** + * \brief Flushes the cache and then removes it + * \param Cache Cache handle returned by ::IOCache_Create + * \note After this is called \a Cache is no longer valid + * + * IOCache_Destroy writes all changed sectors to the device and then + * deallocates the cache for other systems to use. + */ +void IOCache_Destroy( tIOCache *Cache ); + +#endif diff --git a/Kernel/include/tpl_drv_common.h b/Kernel/include/tpl_drv_common.h index 31ad97a0..7608bc8a 100644 --- a/Kernel/include/tpl_drv_common.h +++ b/Kernel/include/tpl_drv_common.h @@ -1,8 +1,11 @@ +/* + * Acess2 + * - Common Driver Interface + */ /** - AcessOS Version 1 - \file tpl_drv_common.h - \brief Common Driver Interface Definitions -*/ + * \file tpl_drv_common.h + * \brief Common Driver Interface Definitions + */ #ifndef _TPL_COMMON_H #define _TPL_COMMON_H @@ -18,7 +21,7 @@ enum eTplDrv_IOCtl { /// \brief Get driver version - (int *ver) DRV_IOCTL_VERSION, /// \brief Get a IOCtl from a symbolic name - DRV_IOCTL_LOOKUP, + DRV_IOCTL_LOOKUP }; /** @@ -39,6 +42,17 @@ enum eTplDrv_Type { }; // === FUNCTIONS === +/** + * \fn int GetIOCtlId(int Class, char *Name) + * \brief Transforms a symbolic name into an ID + * \param Class ::eTplDrv_Type type to use + * \param Name Symbolic name to resolve + * + * This function is designed to be used by device drivers to implement + * ::eTplDrv_IOCtl.DRV_IOCTL_LOOKUP easily given that they conform to + * the standard interfaces (::eTplDrv_Type except DRV_TYPE_MISC) and do + * not add their own call numbers. + */ extern int GetIOCtlId(int Class, char *Name); #endif diff --git a/Kernel/include/tpl_drv_keyboard.h b/Kernel/include/tpl_drv_keyboard.h index 77f8295a..1f330039 100644 --- a/Kernel/include/tpl_drv_keyboard.h +++ b/Kernel/include/tpl_drv_keyboard.h @@ -21,9 +21,15 @@ enum eTplKeyboard_IOCtl { KB_IOCTL_SETCALLBACK }; -typedef void (*tKeybardCallback)(Uint32); +/** + * \brief Callback type for KB_IOCTL_SETCALLBACK + */ +typedef void (*tKeybardCallback)(Uint32 Key); -enum { +/** + * \brief Symbolic key codes + */ +enum eTplKeyboard_KeyCodes { KEY_ESC = 0x1B, KEY_NP_MASK = 0x80, //End of ASCII Range diff --git a/Kernel/include/tpl_drv_video.h b/Kernel/include/tpl_drv_video.h index b84a97e2..0272078e 100644 --- a/Kernel/include/tpl_drv_video.h +++ b/Kernel/include/tpl_drv_video.h @@ -44,9 +44,8 @@ enum eTplVideo_IOCtl { }; /** - \struct sVideo_IOCtl_Mode - \brief Mode Structure used in IOCtl Calls -*/ + * \brief Mode Structure used in IOCtl Calls + */ struct sVideo_IOCtl_Mode { short id; //!< Mide ID Uint16 width; //!< Width @@ -55,45 +54,76 @@ struct sVideo_IOCtl_Mode { Uint8 flags; //!< Mode Flags }; typedef struct sVideo_IOCtl_Mode tVideo_IOCtl_Mode; //!< Mode Type + +//! \name Video Mode flags +//! \{ /** * \brief Text Mode Flag * \note A text mode should have the ::sVideo_IOCtl_Mode.bpp set to 12 */ #define VIDEO_FLAG_TEXT 0x1 -#define VIDEO_FLAG_SLOW 0x2 //!< Non-accelerated mode +/** + * \brief Slow (non-accellerated mode) + */ +#define VIDEO_FLAG_SLOW 0x2 +//! \} -typedef struct sVideo_IOCtl_Pos tVideo_IOCtl_Pos; //!< Position Type /** + * \brief Describes a position in the video framebuffer */ +typedef struct sVideo_IOCtl_Pos tVideo_IOCtl_Pos; struct sVideo_IOCtl_Pos { Sint16 x; //!< X Coordinate Sint16 y; //!< Y Coordinate }; /** - * \struct sVT_Char * \brief Virtual Terminal Representation of a character */ +typedef struct sVT_Char tVT_Char; struct sVT_Char { - Uint32 Ch; + Uint32 Ch; //!< UTF-32 Character union { struct { - Uint16 BGCol; - Uint16 FGCol; + Uint16 BGCol; //!< 12-bit Foreground Colour + Uint16 FGCol; //!< 12-bit Background Colour }; - Uint32 Colour; + Uint32 Colour; //!< Compound colour for ease of access }; }; -typedef struct sVT_Char tVT_Char; +/** + * \name Basic builtin colour definitions + * \{ + */ #define VT_COL_BLACK 0x0000 #define VT_COL_GREY 0x0888 #define VT_COL_LTGREY 0x0CCC #define VT_COL_WHITE 0x0FFF +/** + * \} + */ +//! \brief Defines the width of a rendered character extern int giVT_CharWidth; +//! \brief Defines the height of a rendered character extern int giVT_CharHeight; +/** + * \fn void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Pitch, Uint32 BGC, Uint32 FGC) + * \brief Renders a character to a buffer + * \param Codepoint Unicode character to render + * \param Buffer Buffer to render to (32-bpp) + * \param Pitch Number of DWords per line + * \param BGC 32-bit Background Colour + * \param FGC 32-bit Foreground Colour + */ extern void VT_Font_Render(Uint32 Codepoint, void *Buffer, int Pitch, Uint32 BGC, Uint32 FGC); +/** + * \fn Uint32 VT_Colour12to24(Uint16 Col12) + * \brief Converts a colour from 12bpp to 32bpp + * \param Col12 12-bpp input colour + * \return Expanded 32-bpp (24-bit colour) version of \a Col12 + */ extern Uint32 VT_Colour12to24(Uint16 Col12); #endif diff --git a/Kernel/include/vfs.h b/Kernel/include/vfs.h index 025a5262..6fc2491d 100644 --- a/Kernel/include/vfs.h +++ b/Kernel/include/vfs.h @@ -1,36 +1,75 @@ /* - * Acess Micro - VFS Server Ver 1 + * Acess2 + * VFS Common Header + */ +/** + * \file vfs.h + * \brief Acess VFS Layer */ #ifndef _VFS_H #define _VFS_H #include +//! \name ACL Permissions +//! \{ +/** + * \brief Readable + */ #define VFS_PERM_READ 0x00000001 +/** + * \brief Writeable + */ #define VFS_PERM_WRITE 0x00000002 +/** + * \brief Append allowed + */ #define VFS_PERM_APPEND 0x00000004 +/** + * \brief Executable + */ #define VFS_PERM_EXECUTE 0x00000008 +/** + * \brief All permissions granted + */ #define VFS_PERM_ALL 0x7FFFFFFF // Mask for permissions +/** + * \brief Denies instead of granting permissions + * \note Denials take precedence + */ #define VFS_PERM_DENY 0x80000000 // Inverts permissions +//! \} -typedef struct sVFS_ACL { +/** + * \brief ACL Defintion Structure + */ +typedef struct sVFS_ACL +{ struct { - unsigned Group: 1; // Group (as opposed to user) flag - unsigned ID: 31; // ID of Group/User (-1 for nobody/world) + unsigned Group: 1; //!< Group (as opposed to user) flag + unsigned ID: 31; //!< ID of Group/User (-1 for nobody/world) }; struct { - unsigned Inv: 1; // Invert Permissions - unsigned Perms: 31; // Permission Flags + unsigned Inv: 1; //!< Invert Permissions + unsigned Perms: 31; //!< Permission Flags }; } tVFS_ACL; -#define VFS_FFLAG_READONLY 0x01 -#define VFS_FFLAG_DIRECTORY 0x02 -#define VFS_FFLAG_SYMLINK 0x04 +/** + * \name VFS Node Flags + * \{ + */ +#define VFS_FFLAG_READONLY 0x01 //!< Read-only file +#define VFS_FFLAG_DIRECTORY 0x02 //!< Directory +#define VFS_FFLAG_SYMLINK 0x04 //!< Symbolic Link +/** + * \} + */ -typedef struct sVFS_Node { - //char *Name; //!< Node's Name (UTF-8) - +/** + * \brief VFS Node + */ +typedef struct sVFS_Node { Uint64 Inode; //!< Inode ID Uint ImplInt; //!< Implementation Usable Integer void *ImplPtr; //!< Implementation Usable Pointer @@ -58,16 +97,23 @@ typedef struct sVFS_Node { //! Send an IO Control int (*IOCtl)(struct sVFS_Node *Node, int Id, void *Data); + //! \brief Read from the file Uint64 (*Read)(struct sVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); + //! \brief Write to the file Uint64 (*Write)(struct sVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); - //! Find an directory entry by name + //! \brief Find an directory entry by name + //! \note The node returned must be accessable until ::tVFS_Node.Close is called struct sVFS_Node *(*FindDir)(struct sVFS_Node *Node, char *Name); - //! Read from a directory - MUST return a heap address + + //! \brief Read from a directory + //! \note MUST return a heap address char *(*ReadDir)(struct sVFS_Node *Node, int Pos); - //! Create a node in a directory + + //! \brief Create a node in a directory int (*MkNod)(struct sVFS_Node *Node, char *Name, Uint Flags); - //! Relink (Rename/Remove) a file/directory + + //! \brief Relink (Rename/Remove) a file/directory int (*Relink)(struct sVFS_Node *Node, char *OldName, char *NewName); //!< \todo Complete @@ -76,34 +122,104 @@ typedef struct sVFS_Node { /** * \brief VFS Driver (Filesystem) Definition */ -typedef struct sVFS_Driver { +typedef struct sVFS_Driver +{ + //! \brief Unique Identifier for this filesystem type char *Name; + //! \brief Flags applying to this driver Uint Flags; + + //! \brief Callback to mount a device tVFS_Node *(*InitDevice)(char *Device, char **Options); + //! \brief Callback to unmount a device void (*Unmount)(tVFS_Node *Node); + //! \brief Used internally (next driver in the chain) struct sVFS_Driver *Next; } tVFS_Driver; // === GLOBALS === +//! \brief Maximum number of elements that can be skipped in one return #define VFS_MAXSKIP ((void*)1024) +//! \brief Skip a single entry in readdir #define VFS_SKIP ((void*)1) +//! \brief Skip \a n entries in readdir #define VFS_SKIPN(n) ((void*)(n)) -extern tVFS_Node NULLNode; -extern tVFS_ACL gVFS_ACL_EveryoneRWX; -extern tVFS_ACL gVFS_ACL_EveryoneRW; -extern tVFS_ACL gVFS_ACL_EveryoneRX; -extern tVFS_ACL gVFS_ACL_EveryoneRO; + +extern tVFS_Node NULLNode; //!< NULL VFS Node (Ignored/Skipped) +/** + * \name Simple ACLs to aid writing drivers + * \{ + */ +extern tVFS_ACL gVFS_ACL_EveryoneRWX; //!< Everyone Read/Write/Execute +extern tVFS_ACL gVFS_ACL_EveryoneRW; //!< Everyone Read/Write +extern tVFS_ACL gVFS_ACL_EveryoneRX; //!< Everyone Read/Execute +extern tVFS_ACL gVFS_ACL_EveryoneRO; //!< Everyone Read only +/** + * \} + */ // === FUNCTIONS === +/** + * \fn int VFS_AddDriver(tVFS_Driver *Info) + * \brief Registers the driver with the DevFS layer + * \param Info Driver information structure + */ extern int VFS_AddDriver(tVFS_Driver *Info); +/** + * \fn tVFS_Driver *VFS_GetFSByName(char *Name) + * \brief Get the information structure of a driver given its name + * \param Name Name of filesystem driver to find + */ extern tVFS_Driver *VFS_GetFSByName(char *Name); +/** + * \fn tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group) + * \brief Transforms Unix Permssions into Acess ACLs + * \param Mode Unix RWXrwxRWX mask + * \param Owner UID of the file's owner + * \param Group GID of the file's owning group + * \return An array of 3 Acess ACLs + */ extern tVFS_ACL *VFS_UnixToAcessACL(Uint Mode, Uint Owner, Uint Group); // --- Node Cache -- +//! \name Node Cache +//! \{ +/** + * \fn int Inode_GetHandle() + * \brief Gets a unique handle to the Node Cache + * \return A unique handle for use for the rest of the Inode_* functions + */ extern int Inode_GetHandle(); +/** + * \fn tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode) + * \brief Gets an inode from the node cache + * \param Handle A handle returned by Inode_GetHandle() + * \param Inode Value of the Inode field of the ::tVFS_Node you want + * \return A pointer to the cached node + */ extern tVFS_Node *Inode_GetCache(int Handle, Uint64 Inode); +/** + * \fn tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node) + * \brief Caches a node in the Node Cache + * \param Handle A handle returned by Inode_GetHandle() + * \param Node A pointer to the node to be cached (a copy is taken) + * \return A pointer to the node in the node cache + */ extern tVFS_Node *Inode_CacheNode(int Handle, tVFS_Node *Node); +/** + * \fn void Inode_UncacheNode(int Handle, Uint64 Inode) + * \brief Dereferences (and removes if needed) a node from the cache + * \param Handle A handle returned by Inode_GetHandle() + * \param Inode Value of the Inode field of the ::tVFS_Node you want to remove + */ extern void Inode_UncacheNode(int Handle, Uint64 Inode); +/** + * \fn void Inode_ClearCache(int Handle) + * \brief Clears the cache for a handle + * \param Handle A handle returned by Inode_GetHandle() + */ extern void Inode_ClearCache(int Handle); +//! \} + #endif diff --git a/Makefile.cfg b/Makefile.cfg index 17524975..a8d3b200 100644 --- a/Makefile.cfg +++ b/Makefile.cfg @@ -15,8 +15,8 @@ ARCH = i386 ARCHDIR = x86 FILESYSTEMS = fat ext2 -DRIVERS = ata_x86 fdd -MODULES = NE2000 BochsVBE +DRIVERS = ata_x86 +MODULES = FDD NE2000 BochsVBE DISTROOT = /mnt/AcessHDD/Acess2 ACESSDIR = /home/hodgeja/Projects/Acess2 diff --git a/Modules/FDD/Makefile b/Modules/FDD/Makefile new file mode 100644 index 00000000..15965531 --- /dev/null +++ b/Modules/FDD/Makefile @@ -0,0 +1,7 @@ +# +# + +OBJ = fdd.o +NAME = FDD + +-include ../Makefile.tpl diff --git a/Modules/FDD/fdd.c b/Modules/FDD/fdd.c new file mode 100644 index 00000000..c33b2818 --- /dev/null +++ b/Modules/FDD/fdd.c @@ -0,0 +1,793 @@ +/* + * AcessOS 0.1 + * Floppy Disk Access Code + */ +#define DEBUG 0 +#include +#include +#include +#include +#include +#include + +#define WARN 0 + +// === CONSTANTS === +// --- Current Version +#define FDD_VERSION ((0<<8)|(75)) + +// --- Options +#define USE_CACHE 1 // Use Sector Cache +#define CACHE_SIZE 32 // Number of cachable sectors +#define FDD_SEEK_TIMEOUT 10 // Timeout for a seek operation +#define MOTOR_ON_DELAY 500 // Miliseconds +#define MOTOR_OFF_DELAY 2000 // Miliseconds + +// === TYPEDEFS === +/** + * \brief Representation of a floppy drive + */ +typedef struct { + int type; + volatile int motorState; //2 - On, 1 - Spinup, 0 - Off + int track[2]; + int timer; + tVFS_Node Node; +} t_floppyDevice; + +/** + * \brief Cached Sector + */ +typedef struct { + Uint64 timestamp; + Uint16 disk; + Uint16 sector; // Allows 32Mb of addressable space (Plenty for FDD) + Uint8 data[512]; +} t_floppySector; + +// === CONSTANTS === +static const char *cFDD_TYPES[] = {"None", "360kB 5.25\"", "1.2MB 5.25\"", "720kB 3.5\"", "1.44MB 3.5\"", "2.88MB 3.5\"" }; +static const int cFDD_SIZES[] = { 0, 360*1024, 1200*1024, 720*1024, 1440*1024, 2880*1024 }; +static const short cPORTBASE[] = { 0x3F0, 0x370 }; + +enum FloppyPorts { + PORT_STATUSA = 0x0, + PORT_STATUSB = 0x1, + PORT_DIGOUTPUT = 0x2, + PORT_MAINSTATUS = 0x4, + PORT_DATARATE = 0x4, + PORT_DATA = 0x5, + PORT_DIGINPUT = 0x7, + PORT_CONFIGCTRL = 0x7 +}; + +enum FloppyCommands { + FIX_DRIVE_DATA = 0x03, + HECK_DRIVE_STATUS = 0x04, + CALIBRATE_DRIVE = 0x07, + CHECK_INTERRUPT_STATUS = 0x08, + SEEK_TRACK = 0x0F, + READ_SECTOR_ID = 0x4A, + FORMAT_TRACK = 0x4D, + READ_TRACK = 0x42, + READ_SECTOR = 0x66, + WRITE_SECTOR = 0xC5, + WRITE_DELETE_SECTOR = 0xC9, + READ_DELETE_SECTOR = 0xCC, +}; + +// === PROTOTYPES === +// --- Filesystem + int FDD_Install(char **Arguments); +char *FDD_ReadDir(tVFS_Node *Node, int pos); +tVFS_Node *FDD_FindDir(tVFS_Node *dirNode, char *Name); + int FDD_IOCtl(tVFS_Node *Node, int ID, void *Data); +Uint64 FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer); +// --- Raw Disk Access + int FDD_ReadSector(Uint32 disk, Uint64 lba, void *Buffer); + int FDD_WriteSector(Uint32 Disk, Uint64 LBA, void *Buffer); +// --- Helpers +void FDD_IRQHandler(int Num); +void FDD_WaitIRQ(); +void FDD_SensInt(int base, Uint8 *sr0, Uint8 *cyl); +inline void FDD_AquireSpinlock(); +inline void FDD_FreeSpinlock(); +#if USE_CACHE +inline void FDD_AquireCacheSpinlock(); +inline void FDD_FreeCacheSpinlock(); +#endif +void FDD_int_SendByte(int base, char byte); + int FDD_int_GetByte(int base); +void FDD_Reset(int id); +void FDD_Recalibrate(int disk); + int FDD_int_SeekTrack(int disk, int head, int track); +void FDD_int_TimerCallback(int arg); +void FDD_int_StopMotor(int disk); +void FDD_int_StartMotor(int disk); + int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt); + +// === GLOBALS === +MODULE_DEFINE(0, FDD_VERSION, FDD, FDD_Install, NULL, NULL); +t_floppyDevice gFDD_Devices[2]; +volatile int fdd_inUse = 0; +volatile int fdd_irq6 = 0; +tDevFS_Driver gFDD_DriverInfo = { + NULL, "fdd", + { + .Size = -1, + .NumACLs = 1, + .ACLs = &gVFS_ACL_EveryoneRX, + .Flags = VFS_FFLAG_DIRECTORY, + .ReadDir = FDD_ReadDir, + .FindDir = FDD_FindDir, + .IOCtl = FDD_IOCtl + } +}; +#if USE_CACHE +int siFDD_CacheInUse = 0; +int siFDD_SectorCacheSize = CACHE_SIZE; +t_floppySector sFDD_SectorCache[CACHE_SIZE]; +#endif + +// === CODE === +/** + * \fn int FDD_Install(char **Arguments) + * \brief Installs floppy driver + */ +int FDD_Install(char **Arguments) +{ + Uint8 data; + + // Determine Floppy Types (From CMOS) + outb(0x70, 0x10); + data = inb(0x71); + gFDD_Devices[0].type = data >> 4; + gFDD_Devices[1].type = data & 0xF; + gFDD_Devices[0].track[0] = -1; + gFDD_Devices[1].track[1] = -1; + + Log("[FDD ] Detected Disk 0: %s and Disk 1: %s\n", cFDD_TYPES[data>>4], cFDD_TYPES[data&0xF]); + + // Clear FDD IRQ Flag + FDD_SensInt(0x3F0, NULL, NULL); + // Install IRQ6 Handler + IRQ_AddHandler(6, FDD_IRQHandler); + // Reset Primary FDD Controller + FDD_Reset(0); + + // Initialise Root Node + gFDD_DriverInfo.RootNode.CTime = gFDD_DriverInfo.RootNode.MTime + = gFDD_DriverInfo.RootNode.ATime = now(); + + // Initialise Child Nodes + gFDD_Devices[0].Node.Inode = 0; + gFDD_Devices[0].Node.Flags = 0; + gFDD_Devices[0].Node.NumACLs = 0; + gFDD_Devices[0].Node.Read = FDD_ReadFS; + gFDD_Devices[0].Node.Write = NULL;//fdd_writeFS; + memcpy(&gFDD_Devices[1].Node, &gFDD_Devices[0].Node, sizeof(tVFS_Node)); + + gFDD_Devices[1].Node.Inode = 1; + + // Set Lengths + gFDD_Devices[0].Node.Size = cFDD_SIZES[data >> 4]; + gFDD_Devices[1].Node.Size = cFDD_SIZES[data & 0xF]; + + // Create Sector Cache + #if USE_CACHE + //sFDD_SectorCache = malloc(sizeof(*sFDD_SectorCache)*CACHE_SIZE); + //siFDD_SectorCacheSize = CACHE_SIZE; + #else + if( cFDD_SIZES[data >> 4] ) { + gFDD_Devices[0].CacheHandle = IOCache_Create( + FDD_WriteSector, 0, 512, + gFDD_Devices[0].Node.Size / (512*4) + ); // Cache is 1/4 the size of the disk + } + if( cFDD_SIZES[data & 15] ) { + gFDD_Devices[1].CacheHandle = IOCache_Create( + FDD_WriteSector, 0, 512, + gFDD_Devices[1].Node.Size / (512*4) + ); // Cache is 1/4 the size of the disk + } + #endif + + // Register with devfs + DevFS_AddDevice(&gFDD_DriverInfo); + + return 0; +} + +/** + * \fn char *FDD_ReadDir(tVFS_Node *Node, int pos) + * \brief Read Directory + */ +char *FDD_ReadDir(tVFS_Node *Node, int pos) +{ + char name[2] = "0\0"; + //Update Accessed Time + //gFDD_DrvInfo.rootNode.atime = now(); + + //Check for bounds + if(pos >= 2 || pos < 0) + return NULL; + + if(gFDD_Devices[pos].type == 0) + return VFS_SKIP; + + name[0] += pos; + + //Return + return strdup(name); +} + +/** + * \fn tVFS_Node *FDD_FindDir(tVFS_Node *Node, char *filename); + * \brief Find File Routine (for vfs_node) + */ +tVFS_Node *FDD_FindDir(tVFS_Node *Node, char *filename) +{ + int i; + + ENTER("sfilename", filename); + + // Sanity check string + if(filename == NULL) { + LEAVE('n'); + return NULL; + } + + // Check string length (should be 1) + if(filename[0] == '\0' || filename[1] != '\0') { + LEAVE('n'); + return NULL; + } + + // Get First character + i = filename[0] - '0'; + + // Check for 1st disk and if it is present return + if(i == 0 && gFDD_Devices[0].type != 0) { + LEAVE('p', &gFDD_Devices[0].Node); + return &gFDD_Devices[0].Node; + } + + // Check for 2nd disk and if it is present return + if(i == 1 && gFDD_Devices[1].type != 0) { + LEAVE('p', &gFDD_Devices[1].Node); + return &gFDD_Devices[1].Node; + } + + // Else return null + LEAVE('n'); + return NULL; +} + +/** + * \fn int FDD_IOCtl(tVFS_Node *node, int id, void *data) + * \brief Stub ioctl function + */ +int FDD_IOCtl(tVFS_Node *node, int id, void *data) +{ + switch(id) + { + case DRV_IOCTL_TYPE: return DRV_TYPE_DISK; + case DRV_IOCTL_IDENT: memcpy(data, "FDD\0", 4); return 1; + case DRV_IOCTL_VERSION: return FDD_VERSION; + default: return 0; + } +} + +/** + * \fn Uint64 fdd_readFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer) + * \brief Read Data from a disk +*/ +Uint64 FDD_ReadFS(tVFS_Node *node, Uint64 off, Uint64 len, void *buffer) +{ + int i = 0; + int disk; + Uint32 buf[128]; + + ENTER("xoff xlen pbuffer", off, len, buffer); + + if(node == NULL) { + LEAVE('i', -1); + return -1; + } + + if(node->Inode != 0 && node->Inode != 1) { + LEAVE('i', -1); + return -1; + } + + disk = node->Inode; + + // Update Accessed Time + node->ATime = now(); + + if((off & 0x1FF) || (len & 0x1FF)) + { + // Un-Aligned Offset/Length + int startOff = off>>9; + int sectOff = off&0x1FF; + int sectors = (len+0x1FF)>>9; + + LOG("Non-aligned Read"); + + //Read Starting Sector + if(!FDD_ReadSector(disk, startOff, buf)) + return 0; + memcpy(buffer, (char*)(buf+sectOff), len>512-sectOff?512-sectOff:len); + + //If the data size is one sector or less + if(len <= 512-sectOff) { + LEAVE('X', len); + return len; //Return + } + buffer += 512-sectOff; + + //Read Middle Sectors + for(i=1;i> 9; + int sector = off >> 9; + LOG("Aligned Read"); + //Aligned Offset and Length - Simple Code + for(i=0;i>1]; + + LOG("Calculating Disk Dimensions"); + //Get CHS position + if(FDD_int_GetDims(gFDD_Devices[Disk].type, lba, &cyl, &head, &sec, &spt) != 1) { + LEAVE('i', -1); + return -1; + } + + // Remove Old Timer + Time_RemoveTimer(gFDD_Devices[Disk].timer); + // Check if Motor is on + if(gFDD_Devices[Disk].motorState == 0) { + FDD_int_StartMotor(Disk); + } + + LOG("Wait for Motor Spinup"); + + // Wait for spinup + while(gFDD_Devices[Disk].motorState == 1) Threads_Yield(); + + LOG("C:%i,H:%i,S:%i", cyl, head, sec); + LOG("Acquire Spinlock"); + + FDD_AquireSpinlock(); + + // Seek to track + outb(base+CALIBRATE_DRIVE, 0); + i = 0; + while(FDD_int_SeekTrack(Disk, head, (Uint8)cyl) == 0 && i++ < FDD_SEEK_TIMEOUT ) Threads_Yield(); + //FDD_SensInt(base, NULL, NULL); // Wait for IRQ + + LOG("Setting DMA for read"); + + //Read Data from DMA + DMA_SetChannel(2, 512, 1); // Read 512 Bytes + + LOG("Sending read command"); + + //Threads_Wait(100); // Wait for Head to settle + Time_Delay(100); + FDD_int_SendByte(base, READ_SECTOR); // Was 0xE6 + FDD_int_SendByte(base, (head << 2) | (Disk&1)); + FDD_int_SendByte(base, (Uint8)cyl); + FDD_int_SendByte(base, (Uint8)head); + FDD_int_SendByte(base, (Uint8)sec); + FDD_int_SendByte(base, 0x02); // Bytes Per Sector (Real BPS=128*2^{val}) + FDD_int_SendByte(base, spt); // SPT + FDD_int_SendByte(base, 0x1B); // Gap Length (27 is default) + FDD_int_SendByte(base, 0xFF); // Data Length + + // Wait for IRQ + LOG("Waiting for Data to be read"); + FDD_WaitIRQ(); + + // Read Data from DMA + LOG(" FDD_ReadSector: Reading Data"); + DMA_ReadData(2, 512, Buffer); + + // Clear Input Buffer + LOG("Clearing Input Buffer"); + FDD_int_GetByte(base); FDD_int_GetByte(base); FDD_int_GetByte(base); + FDD_int_GetByte(base); FDD_int_GetByte(base); FDD_int_GetByte(base); FDD_int_GetByte(base); + + LOG("Realeasing Spinlock and Setting motor to stop"); + // Release Spinlock + FDD_FreeSpinlock(); + + //Set timer to turn off motor affter a gap + gFDD_Devices[Disk].timer = Time_CreateTimer(MOTOR_OFF_DELAY, FDD_int_StopMotor, (void*)Disk); //One Shot Timer + + #if USE_CACHE + { + FDD_AquireCacheSpinlock(); + int oldest = 0; + for(i=0;i>1]; + + // Check if seeking is needed + if(gFDD_Devices[disk].track[head] == track) + return 1; + + // - Seek Head 0 + FDD_int_SendByte(base, SEEK_TRACK); + FDD_int_SendByte(base, (head<<2)|(disk&1)); + FDD_int_SendByte(base, track); // Send Seek command + FDD_WaitIRQ(); + FDD_SensInt(base, &sr0, &cyl); // Wait for IRQ + if((sr0 & 0xF0) != 0x20) { + LOG("sr0 = 0x%x", sr0); + return 0; //Check Status + } + if(cyl != track) return 0; + + // Set Track in structure + gFDD_Devices[disk].track[head] = track; + return 1; +} + +/** + * \fn int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt) + * \brief Get Dimensions of a disk + */ +int FDD_int_GetDims(int type, int lba, int *c, int *h, int *s, int *spt) +{ + switch(type) { + case 0: + return 0; + + // 360Kb 5.25" + case 1: + *spt = 9; + *s = (lba % 9) + 1; + *c = lba / 18; + *h = (lba / 9) & 1; + break; + + // 1220Kb 5.25" + case 2: + *spt = 15; + *s = (lba % 15) + 1; + *c = lba / 30; + *h = (lba / 15) & 1; + break; + + // 720Kb 3.5" + case 3: + *spt = 9; + *s = (lba % 9) + 1; + *c = lba / 18; + *h = (lba / 9) & 1; + break; + + // 1440Kb 3.5" + case 4: + *spt = 18; + *s = (lba % 18) + 1; + *c = lba / 36; + *h = (lba / 18) & 1; + break; + + // 2880Kb 3.5" + case 5: + *spt = 36; + *s = (lba % 36) + 1; + *c = lba / 72; + *h = (lba / 32) & 1; + break; + + default: + return -2; + } + return 1; +} + +/** + * \fn void FDD_IRQHandler(int Num) + * \brief Handles IRQ6 + */ +void FDD_IRQHandler(int Num) +{ + fdd_irq6 = 1; +} + +/** + * \fn FDD_WaitIRQ() + * \brief Wait for an IRQ6 + */ +void FDD_WaitIRQ() +{ + // Wait for IRQ + while(!fdd_irq6) Threads_Yield(); + fdd_irq6 = 0; +} + +void FDD_SensInt(int base, Uint8 *sr0, Uint8 *cyl) +{ + FDD_int_SendByte(base, CHECK_INTERRUPT_STATUS); + if(sr0) *sr0 = FDD_int_GetByte(base); + else FDD_int_GetByte(base); + if(cyl) *cyl = FDD_int_GetByte(base); + else FDD_int_GetByte(base); +} + +void FDD_AquireSpinlock() +{ + while(fdd_inUse) + Threads_Yield(); + fdd_inUse = 1; +} + +inline void FDD_FreeSpinlock() +{ + fdd_inUse = 0; +} + +#if USE_CACHE +inline void FDD_AquireCacheSpinlock() +{ + while(siFDD_CacheInUse) Threads_Yield(); + siFDD_CacheInUse = 1; +} +inline void FDD_FreeCacheSpinlock() +{ + siFDD_CacheInUse = 0; +} +#endif + +/** + * void FDD_int_SendByte(int base, char byte) + * \brief Sends a command to the controller + */ +void FDD_int_SendByte(int base, char byte) +{ + volatile int state; + int timeout = 128; + for( ; timeout--; ) + { + state = inb(base + PORT_MAINSTATUS); + if ((state & 0xC0) == 0x80) + { + outb(base + PORT_DATA, byte); + return; + } + inb(0x80); //Delay + } + #if WARN + Warning("FDD_int_SendByte - Timeout sending byte 0x%x to base 0x%x\n", byte, base); + #endif +} + +/** + * int FDD_int_GetByte(int base, char byte) + * \brief Receive data from fdd controller + */ +int FDD_int_GetByte(int base) +{ + volatile int state; + int timeout; + for( timeout = 128; timeout--; ) + { + state = inb((base + PORT_MAINSTATUS)); + if ((state & 0xd0) == 0xd0) + return inb(base + PORT_DATA); + inb(0x80); + } + return -1; +} + +/** + * \brief Recalibrate the specified disk + */ +void FDD_Recalibrate(int disk) +{ + ENTER("idisk", disk); + + LOG("Starting Motor"); + FDD_int_StartMotor(disk); + // Wait for Spinup + while(gFDD_Devices[disk].motorState == 1) Threads_Yield(); + + LOG("Sending Calibrate Command"); + FDD_int_SendByte(cPORTBASE[disk>>1], CALIBRATE_DRIVE); + FDD_int_SendByte(cPORTBASE[disk>>1], disk&1); + + LOG("Waiting for IRQ"); + FDD_WaitIRQ(); + FDD_SensInt(cPORTBASE[disk>>1], NULL, NULL); + + LOG("Stopping Motor"); + FDD_int_StopMotor(disk); + LEAVE('-'); +} + +/** + * \brief Reset the specified FDD controller + */ +void FDD_Reset(int id) +{ + int base = cPORTBASE[id]; + + ENTER("iID", id); + + outb(base + PORT_DIGOUTPUT, 0); // Stop Motors & Disable FDC + outb(base + PORT_DIGOUTPUT, 0x0C); // Re-enable FDC (DMA and Enable) + + LOG("Awaiting IRQ"); + + FDD_WaitIRQ(); + FDD_SensInt(base, NULL, NULL); + + LOG("Setting Driver Info"); + outb(base + PORT_DATARATE, 0); // Set data rate to 500K/s + FDD_int_SendByte(base, FIX_DRIVE_DATA); // Step and Head Load Times + FDD_int_SendByte(base, 0xDF); // Step Rate Time, Head Unload Time (Nibble each) + FDD_int_SendByte(base, 0x02); // Head Load Time >> 1 + while(FDD_int_SeekTrack(0, 0, 1) == 0); // set track + while(FDD_int_SeekTrack(0, 1, 1) == 0); // set track + + LOG("Recalibrating Disk"); + FDD_Recalibrate((id<<1)|0); + FDD_Recalibrate((id<<1)|1); + + LEAVE('-'); +} + +/** + * \fn void FDD_int_TimerCallback() + * \brief Called by timer + */ +void FDD_int_TimerCallback(int arg) +{ + ENTER("iarg", arg); + if(gFDD_Devices[arg].motorState == 1) + gFDD_Devices[arg].motorState = 2; + Time_RemoveTimer(gFDD_Devices[arg].timer); + gFDD_Devices[arg].timer = -1; + LEAVE('-'); +} + +/** + * \fn void FDD_int_StartMotor(char disk) + * \brief Starts FDD Motor + */ +void FDD_int_StartMotor(int disk) +{ + Uint8 state; + state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT ); + state |= 1 << (4+disk); + outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state ); + gFDD_Devices[disk].motorState = 1; + gFDD_Devices[disk].timer = Time_CreateTimer(MOTOR_ON_DELAY, FDD_int_TimerCallback, (void*)disk); +} + +/** + * \fn void FDD_int_StopMotor(int disk) + * \brief Stops FDD Motor + */ +void FDD_int_StopMotor(int disk) +{ + Uint8 state; + state = inb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT ); + state &= ~( 1 << (4+disk) ); + outb( cPORTBASE[ disk>>1 ] + PORT_DIGOUTPUT, state ); + gFDD_Devices[disk].motorState = 0; +} + +/** + * \fn void ModuleUnload() + * \brief Prepare the module for removal + */ +void ModuleUnload() +{ + int i; + FDD_AquireSpinlock(); + for(i=0;i<4;i++) { + Time_RemoveTimer(gFDD_Devices[i].timer); + FDD_int_StopMotor(i); + } + //IRQ_Clear(6); +} diff --git a/Modules/Makefile.tpl b/Modules/Makefile.tpl index 41743443..64cf4b55 100644 --- a/Modules/Makefile.tpl +++ b/Modules/Makefile.tpl @@ -24,7 +24,7 @@ clean: $(BIN): $(OBJ) @echo --- $(LD) -o $@ @$(LD) -T ../link.ld -shared -o $@ $(OBJ) - @echo --- $(LD) -o ../$(NAME).o.$(ARCH) + @echo --- $(LD) -o $(KOBJ) @$(CC) -Wl,-r -nostdlib -o $(KOBJ) $(OBJ) %.o.$(ARCH): %.c Makefile ../Makefile.tpl ../../Makefile.cfg