Makefile.user.cfg
QemuLog.txt
Screenshots/
+
+Tools/*/src/obj/
+Tools/*/src/Makefile.BuildNum
+Tools/DiskTool/DiskTool
* nativefs.c\r
* - Host filesystem access\r
*/\r
-#define DEBUG 1\r
+#define DEBUG 0\r
#define off_t _acess_off_t\r
#include <acess.h> // Acess\r
#include <vfs.h> // Acess\r
void NativeFS_Unmount(tVFS_Node *Node);\r
tVFS_Node *NativeFS_FindDir(tVFS_Node *Node, const char *Name);\r
char *NativeFS_ReadDir(tVFS_Node *Node, int Position);\r
-Uint64 NativeFS_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer);\r
+size_t NativeFS_Read(tVFS_Node *Node, _acess_off_t Offset, size_t Length, void *Buffer);\r
+size_t NativeFS_Write(tVFS_Node *Node, _acess_off_t Offset, size_t Length, const void *Buffer);\r
+void NativeFS_Close(tVFS_Node *Node);\r
\r
// === GLOBALS ===\r
tVFS_NodeType gNativeFS_FileNodeType = {\r
- .Read = NativeFS_Read\r
+ .Read = NativeFS_Read,\r
+ .Write = NativeFS_Write,\r
+ .Close = NativeFS_Close\r
};\r
tVFS_NodeType gNativeFS_DirNodeType = {\r
.FindDir = NativeFS_FindDir,\r
.ReadDir = NativeFS_ReadDir,\r
+ .Close = NativeFS_Close\r
};\r
tVFS_Driver gNativeFS_Driver = {\r
"nativefs", 0,\r
\r
dp = opendir(Device);\r
if(!dp) {\r
- Log_Warning("NativeFS", "ERRO: Unable to open device root '%s'", Device);\r
+ Log_Warning("NativeFS", "ERROR: Unable to open device root '%s'", Device);\r
return NULL;\r
}\r
\r
ret->Data = strdup(Device);\r
ret->ImplInt = strlen(ret->Data);\r
ret->ImplPtr = info;\r
- ret->Inode = (Uint64)dp;\r
+ ret->Inode = (Uint64)(tVAddr)dp;\r
ret->Flags = VFS_FFLAG_DIRECTORY;\r
\r
ret->Type = &gNativeFS_DirNodeType; \r
- \r
+\r
return ret;\r
}\r
\r
{\r
tNativeFS *info = Node->ImplPtr;\r
Inode_ClearCache( info->InodeHandle );\r
- closedir( (void *)Node->Inode );\r
+ closedir( (void *)(tVAddr)Node->Inode );\r
free(Node->Data);\r
free(Node);\r
free(info);\r
void NativeFS_Close(tVFS_Node *Node)\r
{\r
tNativeFS *info = Node->ImplPtr;\r
- Inode_UncacheNode( info->InodeHandle, Node->Inode );\r
+ DIR *dp = (Node->Flags & VFS_FFLAG_DIRECTORY) ? (DIR*)(tVAddr)Node->Inode : 0;\r
+ FILE *fp = (Node->Flags & VFS_FFLAG_DIRECTORY) ? 0 : (FILE*)(tVAddr)Node->Inode;\r
+ \r
+ if( Inode_UncacheNode( info->InodeHandle, Node->Inode ) == 1 ) {\r
+ if(dp) closedir(dp);\r
+ if(fp) fclose(fp);\r
+ }\r
}\r
\r
tVFS_Node *NativeFS_FindDir(tVFS_Node *Node, const char *Name)\r
{\r
- char *path = malloc(Node->ImplInt + 1 + strlen(Name) + 1);\r
+ char *path;\r
tNativeFS *info = Node->ImplPtr;\r
tVFS_Node baseRet;\r
struct stat statbuf;\r
ENTER("pNode sName", Node, Name);\r
\r
// Create path\r
+ path = malloc(Node->ImplInt + 1 + strlen(Name) + 1);\r
strcpy(path, Node->Data);\r
path[Node->ImplInt] = '/';\r
strcpy(path + Node->ImplInt + 1, Name);\r
if( S_ISDIR(statbuf.st_mode) )\r
{\r
LOG("Directory");\r
- baseRet.Inode = (Uint64) opendir(path);\r
+ baseRet.Inode = (Uint64)(tVAddr) opendir(path);\r
baseRet.Type = &gNativeFS_DirNodeType;\r
baseRet.Flags |= VFS_FFLAG_DIRECTORY;\r
baseRet.Size = -1;\r
else\r
{\r
LOG("File");\r
- baseRet.Inode = (Uint64) fopen(path, "r+");\r
+ baseRet.Inode = (Uint64)(tVAddr) fopen(path, "r+");\r
baseRet.Type = &gNativeFS_FileNodeType;\r
\r
fseek( (FILE*)(tVAddr)baseRet.Inode, 0, SEEK_END );\r
return ret;\r
}\r
\r
-Uint64 NativeFS_Read(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer)\r
+size_t NativeFS_Read(tVFS_Node *Node, _acess_off_t Offset, size_t Length, void *Buffer)\r
{\r
- ENTER("pNode XOffset XLength pBuffer", Node, Offset, Length, Buffer);\r
- if( fseek( (void *)Node->Inode, Offset, SEEK_SET ) != 0 )\r
+ ENTER("pNode XOffset xLength pBuffer", Node, Offset, Length, Buffer);\r
+ if( fseek( (FILE *)(tVAddr)Node->Inode, Offset, SEEK_SET ) != 0 )\r
{\r
LEAVE('i', 0);\r
return 0;\r
}\r
LEAVE('-');\r
- return fread( Buffer, 1, Length, (void *)Node->Inode );\r
+ return fread( Buffer, 1, Length, (FILE *)(tVAddr)Node->Inode );\r
+}\r
+\r
+size_t NativeFS_Write(tVFS_Node *Node, _acess_off_t Offset, size_t Length, const void *Buffer)\r
+{\r
+ FILE *fp = (FILE *)(tVAddr)Node->Inode;\r
+ ENTER("pNode XOffset xLength pBuffer", Node, Offset, Length, Buffer);\r
+ if( fseek( fp, Offset, SEEK_SET ) != 0 )\r
+ {\r
+ LEAVE('i', 0);\r
+ return 0;\r
+ }\r
+ size_t ret = fwrite( Buffer, 1, Length, fp );\r
+ fflush( fp );\r
+ LEAVE('i', ret);\r
+ return ret;\r
+\r
}\r
-CC = arm-elf-gcc
+ARM_CPUNAME = gerneric-armv7
+CC = arm-elf-gcc -mcpu=$(ARM_CPUNAME)
AS = arm-elf-gcc -c
LD = arm-elf-ld
OBJDUMP = arm-elf-objdump
include $(ACESSDIR)/BuildConf/armv7/default.mk
+ARM_CPUNAME = cortex-a8
MODULES += Input/PS2KbMouse
MODULES += Display/PL110
include $(ACESSDIR)/BuildConf/armv7/default.mk
+ARM_CPUNAME = cortex-a9
MODULES += Display/Tegra2Vid
BUILDINFO_SRC := $(OBJDIR)buildinfo.c$(OBJSUFFIX)
OBJ := $(addprefix arch/$(ARCHDIR)/,$(A_OBJ))
-OBJ += heap.o drvutil.o logging.o debug.o lib.o adt.o time.o
+OBJ += heap.o logging.o debug.o lib.o libc.o adt.o time.o
+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
+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/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
{
{
const char *fmt = "Acess2 "EXPAND_STR(KERNEL_VERSION)" "EXPAND_STR(ARCHDIR)" build %i, hash %s";
- gSysFS_Version_Kernel.Node.Size = sprintf(NULL, fmt, BUILD_NUM, gsGitHash);
+ gSysFS_Version_Kernel.Node.Size = snprintf(NULL, 0, fmt, BUILD_NUM, gsGitHash);
gSysFS_Version_Kernel.Node.ImplPtr = malloc( gSysFS_Version_Kernel.Node.Size + 1 );
sprintf(gSysFS_Version_Kernel.Node.ImplPtr, fmt, BUILD_NUM, gsGitHash);
}
+++ /dev/null
-/*
- * Acess2 Kernel
- * - By John Hodge
- *
- * drvutil.c
- * - Common Driver/Filesystem Helper Functions
- */
-#define DEBUG 0
-#include <acess.h>
-#include <api_drv_disk.h>
-#include <api_drv_video.h>
-
-// === TYPES ===
-
-// === PROTOTYPES ===
-//int DrvUtil_Video_2DStream(void *Ent, void *Buffer, int Length, tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers);
-//size_t DrvUtil_Video_WriteLFB(int Mode, tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, void *Src);
-//void DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap);
-//void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y);
-void DrvUtil_Video_RenderCursor(tDrvUtil_Video_BufInfo *Buf);
-//void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf);
-void DrvUtil_Video_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour);
-void DrvUtil_Video_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H);
-
-// === GLOBALS ===
-tDrvUtil_Video_2DHandlers gDrvUtil_Stub_2DFunctions = {
- NULL,
- DrvUtil_Video_2D_Fill,
- DrvUtil_Video_2D_Blit
-};
-tVideo_IOCtl_Bitmap gDrvUtil_TextModeCursor = {
- 8, 16,
- 0, 0,
- {
- 0, 0, 0 , 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
- 0, 0, 0 , 0, 0, 0, 0, 0,
- 0, 0, 0 , 0, 0, 0, 0, 0
- }
-};
-
-// === CODE ===
-// --- Video Driver Helpers ---
-int DrvUtil_Video_2DStream(void *Ent, const void *Buffer, int Length,
- tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers)
-{
- const Uint8 *stream = Buffer;
- int rem = Length;
- int op;
-
- Uint16 tmp[6];
-
- while( rem )
- {
- rem --;
- op = *stream;
- stream ++;
-
- if(op > NUM_VIDEO_2DOPS) {
- Log_Warning("DrvUtil",
- "DrvUtil_Video_2DStream: Unknown operation %i",
- op);
- return Length-rem;
- }
-
- if(op*sizeof(void*) > SizeofHandlers) {
- Log_Warning("DrvUtil",
- "DrvUtil_Video_2DStream: Driver does not support op %i",
- op);
- return Length-rem;
- }
-
- switch(op)
- {
- case VIDEO_2DOP_NOP: break;
-
- case VIDEO_2DOP_FILL:
- if(rem < 12) return Length-rem;
- memcpy(tmp, stream, 6*2);
-
- if(!Handlers->Fill) {
- Log_Warning("DrvUtil", "DrvUtil_Video_2DStream: Driver"
- " does not support VIDEO_2DOP_FILL");
- return Length-rem;
- }
-
- Handlers->Fill(
- Ent,
- tmp[0], tmp[1], tmp[2], tmp[3],
- tmp[4] | ((Uint32)tmp[5] << 16)
- );
-
- rem -= 12;
- stream += 12;
- break;
-
- case VIDEO_2DOP_BLIT:
- if(rem < 12) return Length-rem;
- memcpy(tmp, stream, 6*2);
-
- if(!Handlers->Blit) {
- Log_Warning("DrvUtil", "DrvUtil_Video_2DStream: Driver"
- " does not support VIDEO_2DOP_BLIT");
- return Length-rem;
- }
-
- Handlers->Blit(
- Ent,
- tmp[0], tmp[1], tmp[2], tmp[3],
- tmp[4], tmp[5]
- );
-
- rem -= 12;
- stream += 12;
- break;
-
- }
- }
- return 0;
-}
-
-int DrvUtil_Video_WriteLFB(tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, const void *Buffer)
-{
- Uint8 *dest;
- const Uint32 *src = Buffer;
- int csr_x, csr_y;
- int x, y;
- int bytes_per_px = (FBInfo->Depth + 7) / 8;
- ENTER("pFBInfo xOffset xLength pBuffer",
- FBInfo, Offset, Length, Buffer);
-
- csr_x = FBInfo->CursorX;
- csr_y = FBInfo->CursorY;
-
- DrvUtil_Video_RemoveCursor(FBInfo);
-
- switch( FBInfo->BufferFormat )
- {
- case VIDEO_BUFFMT_TEXT:
- {
- const tVT_Char *chars = Buffer;
- int widthInChars = FBInfo->Width/giVT_CharWidth;
- int heightInChars = FBInfo->Height/giVT_CharHeight;
- int i;
-
- LOG("bytes_per_px = %i", bytes_per_px);
- LOG("widthInChars = %i, heightInChars = %i", widthInChars, heightInChars);
-
- Length /= sizeof(tVT_Char); Offset /= sizeof(tVT_Char);
-
- x = Offset % widthInChars; y = Offset / widthInChars;
- LOG("x = %i, y = %i", x, y);
-
- // Sanity Check
- if(Offset > heightInChars * widthInChars) LEAVE_RET('i', 0);
- if(y >= heightInChars) LEAVE_RET('i', 0);
-
- if( Offset + Length > heightInChars*widthInChars )
- {
- Length = heightInChars*widthInChars - Offset;
- }
-
- dest = FBInfo->Framebuffer;
- LOG("dest = %p", dest);
- dest += y * giVT_CharHeight * FBInfo->Pitch;
- LOG("dest = %p", dest);
-
- for( i = 0; i < Length; i++ )
- {
- if( y >= heightInChars )
- {
- Log_Notice("DrvUtil", "Stopped at %i", i);
- break;
- }
-
- VT_Font_Render(
- chars->Ch,
- dest + x*giVT_CharWidth*bytes_per_px, FBInfo->Depth, FBInfo->Pitch,
- VT_Colour12toN(chars->BGCol, FBInfo->Depth),
- VT_Colour12toN(chars->FGCol, FBInfo->Depth)
- );
-
- chars ++;
- x ++;
- if( x >= widthInChars )
- {
- x = 0;
- y ++;
- dest += FBInfo->Pitch*giVT_CharHeight;
- LOG("dest = %p", dest);
- }
- }
- Length = i * sizeof(tVT_Char);
- }
- break;
-
- case VIDEO_BUFFMT_FRAMEBUFFER:
- if(FBInfo->Width*FBInfo->Height*4 < Offset+Length)
- {
- Log_Warning("DrvUtil", "DrvUtil_Video_WriteLFB - Framebuffer Overflow");
- return 0;
- }
-
- switch(FBInfo->Depth)
- {
- case 15:
- case 16:
- Log_Warning("DrvUtil", "TODO: Support 15/16 bpp modes in LFB write");
- break;
- case 24:
- x = Offset % FBInfo->Width;
- y = Offset / FBInfo->Width;
- dest = (Uint8*)FBInfo->Framebuffer + y*FBInfo->Pitch;
- for( ; Length >= 4; Length -= 4 )
- {
- dest[x*3+0] = *src & 0xFF;
- dest[x*3+1] = (*src >> 8) & 0xFF;
- dest[x*3+2] = (*src >> 16) & 0xFF;
- x ++;
- if(x == FBInfo->Width) {
- dest += FBInfo->Pitch;
- x = 0;
- }
- }
- break;
- case 32:
- // Copy to Frambuffer
- if( FBInfo->Pitch != FBInfo->Width*4 )
- {
- Uint32 *px;
- // Pitch isn't 4*Width
- x = Offset % FBInfo->Width;
- y = Offset / FBInfo->Height;
-
- px = (Uint32*)FBInfo->Framebuffer + y*FBInfo->Pitch/4;
-
- for( ; Length >= 4; Length -= 4, x )
- {
- px[x++] = *src ++;
- if( x == FBInfo->Width ) {
- x = 0;
- px += FBInfo->Pitch;
- }
- }
- }
- else
- {
- dest = (Uint8 *)FBInfo->Framebuffer + Offset;
- memcpy(dest, Buffer, Length);
- }
- break;
- default:
- Log_Warning("DrvUtil", "DrvUtil_Video_WriteLFB - Unknown bit depthn %i", FBInfo->Depth);
- break;
- }
- break;
-
- case VIDEO_BUFFMT_2DSTREAM:
- Length = DrvUtil_Video_2DStream(
- FBInfo, Buffer, Length,
- &gDrvUtil_Stub_2DFunctions, sizeof(gDrvUtil_Stub_2DFunctions)
- );
- break;
-
- default:
- LEAVE('i', -1);
- return -1;
- }
-
- DrvUtil_Video_DrawCursor(FBInfo, csr_x, csr_y);
-
- LEAVE('x', Length);
- return Length;
-}
-
-int DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap)
-{
- int csrX = Buf->CursorX, csrY = Buf->CursorY;
- size_t size;
-
- ENTER("pBuf pBitmap", Buf, Bitmap);
-
- // Clear old bitmap
- if( Buf->CursorBitmap )
- {
- LOG("Clearing old cursor");
- DrvUtil_Video_RemoveCursor(Buf);
- if( !Bitmap || Bitmap->W != Buf->CursorBitmap->W || Bitmap->H != Buf->CursorBitmap->H )
- {
- free( Buf->CursorSaveBuf );
- Buf->CursorSaveBuf = NULL;
- }
- if( Buf->CursorBitmap != &gDrvUtil_TextModeCursor)
- free(Buf->CursorBitmap);
- Buf->CursorBitmap = NULL;
- }
-
- // If the new bitmap is null, disable drawing
- if( !Bitmap )
- {
- Buf->CursorX = -1;
- Buf->CursorY = -1;
- LEAVE('i', 0);
- return 0;
- }
-
- // Sanity check the bitmap
- LOG("Sanity checking plox");
- if( !CheckMem(Bitmap, sizeof(*Bitmap)) || !CheckMem(Bitmap->Data, Bitmap->W*Bitmap->H*sizeof(Uint32)) )
- {
- Log_Warning("DrvUtil", "DrvUtil_Video_SetCursor: Bitmap (%p) is in invalid memory", Bitmap);
- errno = -EINVAL;
- LEAVE('i', -1);
- return -1;
- }
-
- // Don't take a copy of the DrvUtil provided cursor
- if( Bitmap == &gDrvUtil_TextModeCursor )
- {
- LOG("No copy (provided cursor)");
- Buf->CursorBitmap = Bitmap;
- }
- else
- {
- LOG("Make copy");
- size = sizeof(tVideo_IOCtl_Bitmap) + Bitmap->W*Bitmap->H*4;
-
- // Take a copy
- Buf->CursorBitmap = malloc( size );
- memcpy(Buf->CursorBitmap, Bitmap, size);
- }
-
- // Restore cursor position
- LOG("Drawing");
- DrvUtil_Video_DrawCursor(Buf, csrX, csrY);
- LEAVE('i', 0);
- return 0;
-}
-
-void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y)
-{
- int render_ox=0, render_oy=0, render_w, render_h;
-
- ENTER("pBuf iX iY", Buf, X, Y);
- DrvUtil_Video_RemoveCursor(Buf);
-
- // X < 0 disables the cursor
- if( X < 0 ) {
- Buf->CursorX = -1;
- LEAVE('-');
- return ;
- }
-
- // Sanity checking
- if( X < 0 || Y < 0 || X >= Buf->Width || Y >= Buf->Height ) {
- LEAVE('-');
- return ;
- }
-
- // Ensure the cursor is enabled
- if( !Buf->CursorBitmap ) {
- LEAVE('-');
- return ;
- }
-
- // Save cursor position (for changing the bitmap)
- Buf->CursorX = X; Buf->CursorY = Y;
- // Apply cursor's center offset
- X -= Buf->CursorBitmap->XOfs;
- Y -= Buf->CursorBitmap->YOfs;
-
- // Get the width of the cursor on screen (clipping to right/bottom edges)
- render_w = X > Buf->Width - Buf->CursorBitmap->W ? Buf->Width - X : Buf->CursorBitmap->W;
- render_h = Y > Buf->Height - Buf->CursorBitmap->H ? Buf->Height - Y : Buf->CursorBitmap->H;
-
- // Clipp to left/top edges
- if(X < 0) { render_ox = -X; X = 0; }
- if(Y < 0) { render_oy = -Y; Y = 0; }
-
- // Save values
- Buf->CursorRenderW = render_w; Buf->CursorRenderH = render_h;
- Buf->CursorDestX = X; Buf->CursorDestY = Y;
- Buf->CursorReadX = render_ox; Buf->CursorReadY = render_oy;
-
- LOG("%ix%i at %i,%i offset %i,%i",
- render_w, render_h, X, Y, render_ox, render_oy);
-
- // Call render routine
- DrvUtil_Video_RenderCursor(Buf);
- LEAVE('-');
-}
-
-void DrvUtil_Video_RenderCursor(tDrvUtil_Video_BufInfo *Buf)
-{
- int src_x = Buf->CursorReadX, src_y = Buf->CursorReadY;
- int render_w = Buf->CursorRenderW, render_h = Buf->CursorRenderH;
- int dest_x = Buf->CursorDestX, dest_y = Buf->CursorDestY;
- int bytes_per_px = (Buf->Depth + 7) / 8;
- int save_pitch = Buf->CursorBitmap->W * bytes_per_px;
- void *dest;
- Uint32 *src;
- int x, y;
-
- dest = (Uint8*)Buf->Framebuffer + dest_y*Buf->Pitch + dest_x*bytes_per_px;
- src = Buf->CursorBitmap->Data + src_y * Buf->CursorBitmap->W + src_x;
-
- LOG("dest = %p, src = %p", dest, src);
-
- // Allocate save buffer if not already
- if( !Buf->CursorSaveBuf )
- Buf->CursorSaveBuf = malloc( Buf->CursorBitmap->W*Buf->CursorBitmap->H*bytes_per_px );
-
- LOG("Saving back");
- // Save behind the cursor
- for( y = 0; y < render_h; y ++ )
- memcpy(
- (Uint8*)Buf->CursorSaveBuf + y*save_pitch,
- (Uint8*)dest + y*Buf->Pitch,
- render_w*bytes_per_px
- );
-
- // Draw the cursor
- switch(Buf->Depth)
- {
- case 15:
- case 16:
- Log_Warning("DrvUtil", "TODO: Support 15/16 bpp modes in cursor draw");
- break;
- case 24:
- LOG("24-bit render");
- for( y = 0; y < render_h; y ++ )
- {
- Uint8 *px;
- px = dest;
- for(x = 0; x < render_w; x ++, px += 3)
- {
- Uint32 value = src[x];
- // TODO: Should I implement alpha blending?
- if(value & 0xFF000000)
- {
- px[0] = value & 0xFF;
- px[1] = (value >> 8) & 0xFF;
- px[2] = (value >> 16) & 0xFF;
- }
- else
- ;
- }
- src += Buf->CursorBitmap->W;
- dest = (Uint8*)dest + Buf->Pitch;
- }
- break;
- case 32:
- LOG("32-bit render");
- for( y = 0; y < render_h; y ++ )
- {
- Uint32 *px;
- px = dest;
- for(x = 0; x < render_w; x ++, px ++)
- {
- Uint32 value = src[x];
- // TODO: Should I implement alpha blending?
- if(value & 0xFF000000)
- *px = value;
- else
- ; // NOP, completely transparent
- }
- LOG("row %i/%i (%p-%P) done", y+1, render_h, dest, MM_GetPhysAddr(dest));
- src += Buf->CursorBitmap->W;
- dest = (Uint8*)dest + Buf->Pitch;
- }
- break;
- default:
- Log_Error("DrvUtil", "RenderCursor - Unknown bit depth %i", Buf->Depth);
- Buf->CursorX = -1;
- break;
- }
-}
-
-void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf)
-{
- int bytes_per_px = (Buf->Depth + 7) / 8;
- int y, save_pitch;
- Uint8 *dest, *src;
-
- // Just a little sanity
- if( !Buf->CursorBitmap || Buf->CursorX == -1 ) return ;
- if( !Buf->CursorSaveBuf ) return ;
-
-// Debug("DrvUtil_Video_RemoveCursor: (Buf=%p) dest_x=%i, dest_y=%i", Buf, Buf->CursorDestX, Buf->CursorDestY);
-
- // Set up
- save_pitch = Buf->CursorBitmap->W * bytes_per_px;
- dest = (Uint8*)Buf->Framebuffer + Buf->CursorDestY * Buf->Pitch + Buf->CursorDestX*bytes_per_px;
- src = Buf->CursorSaveBuf;
-
- // Copy each line back
- for( y = 0; y < Buf->CursorRenderH; y ++ )
- {
- memcpy( dest, src, Buf->CursorRenderW * bytes_per_px );
- src += save_pitch;
- dest += Buf->Pitch;
- }
-
- // Set the cursor as removed
- Buf->CursorX = -1;
-}
-
-void DrvUtil_Video_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour)
-{
- tDrvUtil_Video_BufInfo *FBInfo = Ent;
-
- // TODO: Handle non-32bit modes
- if( FBInfo->Depth != 32 ) return;
-
- // TODO: Be less hacky
- int pitch = FBInfo->Pitch/4;
- Uint32 *buf = (Uint32*)FBInfo->Framebuffer + Y*pitch + X;
- while( H -- ) {
- Uint32 *tmp;
- int i;
- tmp = buf;
- for(i=W;i--;tmp++) *tmp = Colour;
- buf += pitch;
- }
-}
-
-void DrvUtil_Video_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H)
-{
- tDrvUtil_Video_BufInfo *FBInfo = Ent;
- int scrnpitch = FBInfo->Pitch;
- int bytes_per_px = (FBInfo->Depth + 7) / 8;
- int dst = DstY*scrnpitch + DstX;
- int src = SrcY*scrnpitch + SrcX;
- int tmp;
-
- //Log("Vesa_2D_Blit: (Ent=%p, DstX=%i, DstY=%i, SrcX=%i, SrcY=%i, W=%i, H=%i)",
- // Ent, DstX, DstY, SrcX, SrcY, W, H);
-
- if(SrcX + W > FBInfo->Width) W = FBInfo->Width - SrcX;
- if(DstX + W > FBInfo->Width) W = FBInfo->Width - DstX;
- if(SrcY + H > FBInfo->Height) H = FBInfo->Height - SrcY;
- if(DstY + H > FBInfo->Height) H = FBInfo->Height - DstY;
-
- //Debug("W = %i, H = %i", W, H);
-
- if( dst > src ) {
- // Reverse copy
- dst += H*scrnpitch;
- src += H*scrnpitch;
- while( H -- ) {
- dst -= scrnpitch;
- src -= scrnpitch;
- tmp = W*bytes_per_px;
- for( tmp = W; tmp --; ) {
- *((Uint8*)FBInfo->Framebuffer + dst + tmp) = *((Uint8*)FBInfo->Framebuffer + src + tmp);
- }
- }
- }
- else if(W == FBInfo->Width && FBInfo->Pitch == FBInfo->Width*bytes_per_px) {
- memmove((Uint8*)FBInfo->Framebuffer + dst, (Uint8*)FBInfo->Framebuffer + src, H*FBInfo->Pitch);
- }
- else {
- // Normal copy is OK
- while( H -- ) {
- memcpy((Uint8*)FBInfo->Framebuffer + dst, (Uint8*)FBInfo->Framebuffer + src, W*bytes_per_px);
- dst += scrnpitch;
- src += scrnpitch;
- }
- }
- //Log("Vesa_2D_Blit: RETURN");
-}
-
-
-// --- Disk Driver Helpers ---
-Uint64 DrvUtil_ReadBlock(Uint64 Start, Uint64 Length, void *Buffer,
- tDrvUtil_Read_Callback ReadBlocks, Uint64 BlockSize, void *Argument)
-{
- Uint8 tmp[BlockSize]; // C99
- Uint64 block = Start / BlockSize;
- int offset = Start - block * BlockSize;
- int leading = BlockSize - offset;
- Uint64 num;
- int tailings;
- Uint64 ret;
-
- ENTER("XStart XLength pBuffer pReadBlocks XBlockSize pArgument",
- Start, Length, Buffer, ReadBlocks, BlockSize, Argument);
-
- // Non aligned start, let's fix that!
- if(offset != 0)
- {
- if(leading > Length) leading = Length;
- LOG("Reading %i bytes from Block1+%i", leading, offset);
- ret = ReadBlocks(block, 1, tmp, Argument);
- if(ret != 1) {
- LEAVE('i', 0);
- return 0;
- }
- memcpy( Buffer, &tmp[offset], leading );
-
- if(leading == Length) {
- LEAVE('i', leading);
- return leading;
- }
-
- Buffer = (Uint8*)Buffer + leading;
- block ++;
- num = ( Length - leading ) / BlockSize;
- tailings = Length - num * BlockSize - leading;
- }
- else {
- num = Length / BlockSize;
- tailings = Length % BlockSize;
- }
-
- // Read central blocks
- if(num)
- {
- LOG("Reading %i blocks", num);
- ret = ReadBlocks(block, num, Buffer, Argument);
- if(ret != num ) {
- LEAVE('X', leading + ret * BlockSize);
- return leading + ret * BlockSize;
- }
- }
-
- // Read last tailing block
- if(tailings != 0)
- {
- LOG("Reading %i bytes from last block", tailings);
- block += num;
- Buffer = (Uint8*)Buffer + num * BlockSize;
- ret = ReadBlocks(block, 1, tmp, Argument);
- if(ret != 1) {
- LEAVE('X', leading + num * BlockSize);
- return leading + num * BlockSize;
- }
- memcpy( Buffer, tmp, tailings );
- }
-
- LEAVE('X', Length);
- return Length;
-}
-
-Uint64 DrvUtil_WriteBlock(Uint64 Start, Uint64 Length, const void *Buffer,
- tDrvUtil_Read_Callback ReadBlocks, tDrvUtil_Write_Callback WriteBlocks,
- Uint64 BlockSize, void *Argument)
-{
- Uint8 tmp[BlockSize]; // C99
- Uint64 block = Start / BlockSize;
- int offset = Start - block * BlockSize;
- int leading = BlockSize - offset;
- Uint64 num;
- int tailings;
- Uint64 ret;
-
- ENTER("XStart XLength pBuffer pReadBlocks pWriteBlocks XBlockSize pArgument",
- Start, Length, Buffer, ReadBlocks, WriteBlocks, BlockSize, Argument);
-
- // Non aligned start, let's fix that!
- if(offset != 0)
- {
- if(leading > Length) leading = Length;
- LOG("Writing %i bytes to Block1+%i", leading, offset);
- // Read a copy of the block
- ret = ReadBlocks(block, 1, tmp, Argument);
- if(ret != 1) {
- LEAVE('i', 0);
- return 0;
- }
- // Modify
- memcpy( &tmp[offset], Buffer, leading );
- // Write Back
- ret = WriteBlocks(block, 1, tmp, Argument);
- if(ret != 1) {
- LEAVE('i', 0);
- return 0;
- }
-
- if(leading == Length) {
- LEAVE('i', leading);
- return leading;
- }
-
- Buffer = (Uint8*)Buffer + leading;
- block ++;
- num = ( Length - leading ) / BlockSize;
- tailings = Length - num * BlockSize - leading;
- }
- else {
- num = Length / BlockSize;
- tailings = Length % BlockSize;
- }
-
- // Read central blocks
- if(num)
- {
- LOG("Writing %i blocks", num);
- ret = WriteBlocks(block, num, Buffer, Argument);
- if(ret != num ) {
- LEAVE('X', leading + ret * BlockSize);
- return leading + ret * BlockSize;
- }
- }
-
- // Read last tailing block
- if(tailings != 0)
- {
- LOG("Writing %i bytes to last block", tailings);
- block += num;
- Buffer = (Uint8*)Buffer + num * BlockSize;
- // Read
- ret = ReadBlocks(block, 1, tmp, Argument);
- if(ret != 1) {
- LEAVE('X', leading + num * BlockSize);
- return leading + num * BlockSize;
- }
- // Modify
- memcpy( tmp, Buffer, tailings );
- // Write
- ret = WriteBlocks(block, 1, tmp, Argument);
- if(ret != 1) {
- LEAVE('X', leading + num * BlockSize);
- return leading + num * BlockSize;
- }
-
- }
-
- LEAVE('X', Length);
- return Length;
-}
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge
+ *
+ * drvutil_disk.c
+ * - Storage Driver Helper Functions
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <api_drv_disk.h>
+
+// --- Disk Driver Helpers ---
+Uint64 DrvUtil_ReadBlock(Uint64 Start, Uint64 Length, void *Buffer,
+ tDrvUtil_Read_Callback ReadBlocks, Uint64 BlockSize, void *Argument)
+{
+ Uint8 tmp[BlockSize]; // C99
+ Uint64 block = Start / BlockSize;
+ int offset = Start - block * BlockSize;
+ int leading = BlockSize - offset;
+ Uint64 num;
+ int tailings;
+ Uint64 ret;
+
+ ENTER("XStart XLength pBuffer pReadBlocks XBlockSize pArgument",
+ Start, Length, Buffer, ReadBlocks, BlockSize, Argument);
+
+ // Non aligned start, let's fix that!
+ if(offset != 0)
+ {
+ if(leading > Length) leading = Length;
+ LOG("Reading %i bytes from Block1+%i", leading, offset);
+ ret = ReadBlocks(block, 1, tmp, Argument);
+ if(ret != 1) {
+ LEAVE('i', 0);
+ return 0;
+ }
+ memcpy( Buffer, &tmp[offset], leading );
+
+ if(leading == Length) {
+ LEAVE('i', leading);
+ return leading;
+ }
+
+ Buffer = (Uint8*)Buffer + leading;
+ block ++;
+ num = ( Length - leading ) / BlockSize;
+ tailings = Length - num * BlockSize - leading;
+ }
+ else {
+ num = Length / BlockSize;
+ tailings = Length % BlockSize;
+ }
+
+ // Read central blocks
+ if(num)
+ {
+ LOG("Reading %i blocks", num);
+ ret = ReadBlocks(block, num, Buffer, Argument);
+ if(ret != num ) {
+ LEAVE('X', leading + ret * BlockSize);
+ return leading + ret * BlockSize;
+ }
+ }
+
+ // Read last tailing block
+ if(tailings != 0)
+ {
+ LOG("Reading %i bytes from last block", tailings);
+ block += num;
+ Buffer = (Uint8*)Buffer + num * BlockSize;
+ ret = ReadBlocks(block, 1, tmp, Argument);
+ if(ret != 1) {
+ LEAVE('X', leading + num * BlockSize);
+ return leading + num * BlockSize;
+ }
+ memcpy( Buffer, tmp, tailings );
+ }
+
+ LEAVE('X', Length);
+ return Length;
+}
+
+Uint64 DrvUtil_WriteBlock(Uint64 Start, Uint64 Length, const void *Buffer,
+ tDrvUtil_Read_Callback ReadBlocks, tDrvUtil_Write_Callback WriteBlocks,
+ Uint64 BlockSize, void *Argument)
+{
+ Uint8 tmp[BlockSize]; // C99
+ Uint64 block = Start / BlockSize;
+ int offset = Start - block * BlockSize;
+ int leading = BlockSize - offset;
+ Uint64 num;
+ int tailings;
+ Uint64 ret;
+
+ ENTER("XStart XLength pBuffer pReadBlocks pWriteBlocks XBlockSize pArgument",
+ Start, Length, Buffer, ReadBlocks, WriteBlocks, BlockSize, Argument);
+
+ // Non aligned start, let's fix that!
+ if(offset != 0)
+ {
+ if(leading > Length) leading = Length;
+ LOG("Writing %i bytes to Block1+%i", leading, offset);
+ // Read a copy of the block
+ ret = ReadBlocks(block, 1, tmp, Argument);
+ if(ret != 1) {
+ LEAVE('i', 0);
+ return 0;
+ }
+ // Modify
+ memcpy( &tmp[offset], Buffer, leading );
+ // Write Back
+ ret = WriteBlocks(block, 1, tmp, Argument);
+ if(ret != 1) {
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ if(leading == Length) {
+ LEAVE('i', leading);
+ return leading;
+ }
+
+ Buffer = (Uint8*)Buffer + leading;
+ block ++;
+ num = ( Length - leading ) / BlockSize;
+ tailings = Length - num * BlockSize - leading;
+ }
+ else {
+ num = Length / BlockSize;
+ tailings = Length % BlockSize;
+ }
+
+ // Read central blocks
+ if(num)
+ {
+ LOG("Writing %i blocks", num);
+ ret = WriteBlocks(block, num, Buffer, Argument);
+ if(ret != num ) {
+ LEAVE('X', leading + ret * BlockSize);
+ return leading + ret * BlockSize;
+ }
+ }
+
+ // Read last tailing block
+ if(tailings != 0)
+ {
+ LOG("Writing %i bytes to last block", tailings);
+ block += num;
+ Buffer = (Uint8*)Buffer + num * BlockSize;
+ // Read
+ ret = ReadBlocks(block, 1, tmp, Argument);
+ if(ret != 1) {
+ LEAVE('X', leading + num * BlockSize);
+ return leading + num * BlockSize;
+ }
+ // Modify
+ memcpy( tmp, Buffer, tailings );
+ // Write
+ ret = WriteBlocks(block, 1, tmp, Argument);
+ if(ret != 1) {
+ LEAVE('X', leading + num * BlockSize);
+ return leading + num * BlockSize;
+ }
+
+ }
+
+ LEAVE('X', Length);
+ return Length;
+}
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge
+ *
+ * drvutil.c
+ * - Video Driver Helper Functions
+ */
+#define DEBUG 0
+#include <acess.h>
+#include <api_drv_video.h>
+
+// === TYPES ===
+
+// === PROTOTYPES ===
+//int DrvUtil_Video_2DStream(void *Ent, void *Buffer, int Length, tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers);
+//size_t DrvUtil_Video_WriteLFB(int Mode, tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, void *Src);
+//void DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap);
+//void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y);
+void DrvUtil_Video_RenderCursor(tDrvUtil_Video_BufInfo *Buf);
+//void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf);
+void DrvUtil_Video_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour);
+void DrvUtil_Video_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H);
+
+// === GLOBALS ===
+tDrvUtil_Video_2DHandlers gDrvUtil_Stub_2DFunctions = {
+ NULL,
+ DrvUtil_Video_2D_Fill,
+ DrvUtil_Video_2D_Blit
+};
+tVideo_IOCtl_Bitmap gDrvUtil_TextModeCursor = {
+ 8, 16,
+ 0, 0,
+ {
+ 0, 0, 0 , 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0,-1, 0xFF000000, 0, 0, 0, 0, 0,
+ 0, 0, 0 , 0, 0, 0, 0, 0,
+ 0, 0, 0 , 0, 0, 0, 0, 0
+ }
+};
+
+// === CODE ===
+// --- Video Driver Helpers ---
+int DrvUtil_Video_2DStream(void *Ent, const void *Buffer, int Length,
+ tDrvUtil_Video_2DHandlers *Handlers, int SizeofHandlers)
+{
+ const Uint8 *stream = Buffer;
+ int rem = Length;
+ int op;
+
+ Uint16 tmp[6];
+
+ while( rem )
+ {
+ rem --;
+ op = *stream;
+ stream ++;
+
+ if(op > NUM_VIDEO_2DOPS) {
+ Log_Warning("DrvUtil",
+ "DrvUtil_Video_2DStream: Unknown operation %i",
+ op);
+ return Length-rem;
+ }
+
+ if(op*sizeof(void*) > SizeofHandlers) {
+ Log_Warning("DrvUtil",
+ "DrvUtil_Video_2DStream: Driver does not support op %i",
+ op);
+ return Length-rem;
+ }
+
+ switch(op)
+ {
+ case VIDEO_2DOP_NOP: break;
+
+ case VIDEO_2DOP_FILL:
+ if(rem < 12) return Length-rem;
+ memcpy(tmp, stream, 6*2);
+
+ if(!Handlers->Fill) {
+ Log_Warning("DrvUtil", "DrvUtil_Video_2DStream: Driver"
+ " does not support VIDEO_2DOP_FILL");
+ return Length-rem;
+ }
+
+ Handlers->Fill(
+ Ent,
+ tmp[0], tmp[1], tmp[2], tmp[3],
+ tmp[4] | ((Uint32)tmp[5] << 16)
+ );
+
+ rem -= 12;
+ stream += 12;
+ break;
+
+ case VIDEO_2DOP_BLIT:
+ if(rem < 12) return Length-rem;
+ memcpy(tmp, stream, 6*2);
+
+ if(!Handlers->Blit) {
+ Log_Warning("DrvUtil", "DrvUtil_Video_2DStream: Driver"
+ " does not support VIDEO_2DOP_BLIT");
+ return Length-rem;
+ }
+
+ Handlers->Blit(
+ Ent,
+ tmp[0], tmp[1], tmp[2], tmp[3],
+ tmp[4], tmp[5]
+ );
+
+ rem -= 12;
+ stream += 12;
+ break;
+
+ }
+ }
+ return 0;
+}
+
+int DrvUtil_Video_WriteLFB(tDrvUtil_Video_BufInfo *FBInfo, size_t Offset, size_t Length, const void *Buffer)
+{
+ Uint8 *dest;
+ const Uint32 *src = Buffer;
+ int csr_x, csr_y;
+ int x, y;
+ int bytes_per_px = (FBInfo->Depth + 7) / 8;
+ ENTER("pFBInfo xOffset xLength pBuffer",
+ FBInfo, Offset, Length, Buffer);
+
+ csr_x = FBInfo->CursorX;
+ csr_y = FBInfo->CursorY;
+
+ DrvUtil_Video_RemoveCursor(FBInfo);
+
+ switch( FBInfo->BufferFormat )
+ {
+ case VIDEO_BUFFMT_TEXT:
+ {
+ const tVT_Char *chars = Buffer;
+ int widthInChars = FBInfo->Width/giVT_CharWidth;
+ int heightInChars = FBInfo->Height/giVT_CharHeight;
+ int i;
+
+ LOG("bytes_per_px = %i", bytes_per_px);
+ LOG("widthInChars = %i, heightInChars = %i", widthInChars, heightInChars);
+
+ Length /= sizeof(tVT_Char); Offset /= sizeof(tVT_Char);
+
+ x = Offset % widthInChars; y = Offset / widthInChars;
+ LOG("x = %i, y = %i", x, y);
+
+ // Sanity Check
+ if(Offset > heightInChars * widthInChars) LEAVE_RET('i', 0);
+ if(y >= heightInChars) LEAVE_RET('i', 0);
+
+ if( Offset + Length > heightInChars*widthInChars )
+ {
+ Length = heightInChars*widthInChars - Offset;
+ }
+
+ dest = FBInfo->Framebuffer;
+ LOG("dest = %p", dest);
+ dest += y * giVT_CharHeight * FBInfo->Pitch;
+ LOG("dest = %p", dest);
+
+ for( i = 0; i < Length; i++ )
+ {
+ if( y >= heightInChars )
+ {
+ Log_Notice("DrvUtil", "Stopped at %i", i);
+ break;
+ }
+
+ VT_Font_Render(
+ chars->Ch,
+ dest + x*giVT_CharWidth*bytes_per_px, FBInfo->Depth, FBInfo->Pitch,
+ VT_Colour12toN(chars->BGCol, FBInfo->Depth),
+ VT_Colour12toN(chars->FGCol, FBInfo->Depth)
+ );
+
+ chars ++;
+ x ++;
+ if( x >= widthInChars )
+ {
+ x = 0;
+ y ++;
+ dest += FBInfo->Pitch*giVT_CharHeight;
+ LOG("dest = %p", dest);
+ }
+ }
+ Length = i * sizeof(tVT_Char);
+ }
+ break;
+
+ case VIDEO_BUFFMT_FRAMEBUFFER:
+ if(FBInfo->Width*FBInfo->Height*4 < Offset+Length)
+ {
+ Log_Warning("DrvUtil", "DrvUtil_Video_WriteLFB - Framebuffer Overflow");
+ return 0;
+ }
+
+ switch(FBInfo->Depth)
+ {
+ case 15:
+ case 16:
+ Log_Warning("DrvUtil", "TODO: Support 15/16 bpp modes in LFB write");
+ break;
+ case 24:
+ x = Offset % FBInfo->Width;
+ y = Offset / FBInfo->Width;
+ dest = (Uint8*)FBInfo->Framebuffer + y*FBInfo->Pitch;
+ for( ; Length >= 4; Length -= 4 )
+ {
+ dest[x*3+0] = *src & 0xFF;
+ dest[x*3+1] = (*src >> 8) & 0xFF;
+ dest[x*3+2] = (*src >> 16) & 0xFF;
+ x ++;
+ if(x == FBInfo->Width) {
+ dest += FBInfo->Pitch;
+ x = 0;
+ }
+ }
+ break;
+ case 32:
+ // Copy to Frambuffer
+ if( FBInfo->Pitch != FBInfo->Width*4 )
+ {
+ Uint32 *px;
+ // Pitch isn't 4*Width
+ x = Offset % FBInfo->Width;
+ y = Offset / FBInfo->Height;
+
+ px = (Uint32*)FBInfo->Framebuffer + y*FBInfo->Pitch/4;
+
+ for( ; Length >= 4; Length -= 4, x )
+ {
+ px[x++] = *src ++;
+ if( x == FBInfo->Width ) {
+ x = 0;
+ px += FBInfo->Pitch;
+ }
+ }
+ }
+ else
+ {
+ dest = (Uint8 *)FBInfo->Framebuffer + Offset;
+ memcpy(dest, Buffer, Length);
+ }
+ break;
+ default:
+ Log_Warning("DrvUtil", "DrvUtil_Video_WriteLFB - Unknown bit depthn %i", FBInfo->Depth);
+ break;
+ }
+ break;
+
+ case VIDEO_BUFFMT_2DSTREAM:
+ Length = DrvUtil_Video_2DStream(
+ FBInfo, Buffer, Length,
+ &gDrvUtil_Stub_2DFunctions, sizeof(gDrvUtil_Stub_2DFunctions)
+ );
+ break;
+
+ default:
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ DrvUtil_Video_DrawCursor(FBInfo, csr_x, csr_y);
+
+ LEAVE('x', Length);
+ return Length;
+}
+
+int DrvUtil_Video_SetCursor(tDrvUtil_Video_BufInfo *Buf, tVideo_IOCtl_Bitmap *Bitmap)
+{
+ int csrX = Buf->CursorX, csrY = Buf->CursorY;
+ size_t size;
+
+ ENTER("pBuf pBitmap", Buf, Bitmap);
+
+ // Clear old bitmap
+ if( Buf->CursorBitmap )
+ {
+ LOG("Clearing old cursor");
+ DrvUtil_Video_RemoveCursor(Buf);
+ if( !Bitmap || Bitmap->W != Buf->CursorBitmap->W || Bitmap->H != Buf->CursorBitmap->H )
+ {
+ free( Buf->CursorSaveBuf );
+ Buf->CursorSaveBuf = NULL;
+ }
+ if( Buf->CursorBitmap != &gDrvUtil_TextModeCursor)
+ free(Buf->CursorBitmap);
+ Buf->CursorBitmap = NULL;
+ }
+
+ // If the new bitmap is null, disable drawing
+ if( !Bitmap )
+ {
+ Buf->CursorX = -1;
+ Buf->CursorY = -1;
+ LEAVE('i', 0);
+ return 0;
+ }
+
+ // Sanity check the bitmap
+ LOG("Sanity checking plox");
+ if( !CheckMem(Bitmap, sizeof(*Bitmap)) || !CheckMem(Bitmap->Data, Bitmap->W*Bitmap->H*sizeof(Uint32)) )
+ {
+ Log_Warning("DrvUtil", "DrvUtil_Video_SetCursor: Bitmap (%p) is in invalid memory", Bitmap);
+ errno = -EINVAL;
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ // Don't take a copy of the DrvUtil provided cursor
+ if( Bitmap == &gDrvUtil_TextModeCursor )
+ {
+ LOG("No copy (provided cursor)");
+ Buf->CursorBitmap = Bitmap;
+ }
+ else
+ {
+ LOG("Make copy");
+ size = sizeof(tVideo_IOCtl_Bitmap) + Bitmap->W*Bitmap->H*4;
+
+ // Take a copy
+ Buf->CursorBitmap = malloc( size );
+ memcpy(Buf->CursorBitmap, Bitmap, size);
+ }
+
+ // Restore cursor position
+ LOG("Drawing");
+ DrvUtil_Video_DrawCursor(Buf, csrX, csrY);
+ LEAVE('i', 0);
+ return 0;
+}
+
+void DrvUtil_Video_DrawCursor(tDrvUtil_Video_BufInfo *Buf, int X, int Y)
+{
+ int render_ox=0, render_oy=0, render_w, render_h;
+
+ ENTER("pBuf iX iY", Buf, X, Y);
+ DrvUtil_Video_RemoveCursor(Buf);
+
+ // X < 0 disables the cursor
+ if( X < 0 ) {
+ Buf->CursorX = -1;
+ LEAVE('-');
+ return ;
+ }
+
+ // Sanity checking
+ if( X < 0 || Y < 0 || X >= Buf->Width || Y >= Buf->Height ) {
+ LEAVE('-');
+ return ;
+ }
+
+ // Ensure the cursor is enabled
+ if( !Buf->CursorBitmap ) {
+ LEAVE('-');
+ return ;
+ }
+
+ // Save cursor position (for changing the bitmap)
+ Buf->CursorX = X; Buf->CursorY = Y;
+ // Apply cursor's center offset
+ X -= Buf->CursorBitmap->XOfs;
+ Y -= Buf->CursorBitmap->YOfs;
+
+ // Get the width of the cursor on screen (clipping to right/bottom edges)
+ render_w = X > Buf->Width - Buf->CursorBitmap->W ? Buf->Width - X : Buf->CursorBitmap->W;
+ render_h = Y > Buf->Height - Buf->CursorBitmap->H ? Buf->Height - Y : Buf->CursorBitmap->H;
+
+ // Clipp to left/top edges
+ if(X < 0) { render_ox = -X; X = 0; }
+ if(Y < 0) { render_oy = -Y; Y = 0; }
+
+ // Save values
+ Buf->CursorRenderW = render_w; Buf->CursorRenderH = render_h;
+ Buf->CursorDestX = X; Buf->CursorDestY = Y;
+ Buf->CursorReadX = render_ox; Buf->CursorReadY = render_oy;
+
+ LOG("%ix%i at %i,%i offset %i,%i",
+ render_w, render_h, X, Y, render_ox, render_oy);
+
+ // Call render routine
+ DrvUtil_Video_RenderCursor(Buf);
+ LEAVE('-');
+}
+
+void DrvUtil_Video_RenderCursor(tDrvUtil_Video_BufInfo *Buf)
+{
+ int src_x = Buf->CursorReadX, src_y = Buf->CursorReadY;
+ int render_w = Buf->CursorRenderW, render_h = Buf->CursorRenderH;
+ int dest_x = Buf->CursorDestX, dest_y = Buf->CursorDestY;
+ int bytes_per_px = (Buf->Depth + 7) / 8;
+ int save_pitch = Buf->CursorBitmap->W * bytes_per_px;
+ void *dest;
+ Uint32 *src;
+ int x, y;
+
+ dest = (Uint8*)Buf->Framebuffer + dest_y*Buf->Pitch + dest_x*bytes_per_px;
+ src = Buf->CursorBitmap->Data + src_y * Buf->CursorBitmap->W + src_x;
+
+ LOG("dest = %p, src = %p", dest, src);
+
+ // Allocate save buffer if not already
+ if( !Buf->CursorSaveBuf )
+ Buf->CursorSaveBuf = malloc( Buf->CursorBitmap->W*Buf->CursorBitmap->H*bytes_per_px );
+
+ LOG("Saving back");
+ // Save behind the cursor
+ for( y = 0; y < render_h; y ++ )
+ memcpy(
+ (Uint8*)Buf->CursorSaveBuf + y*save_pitch,
+ (Uint8*)dest + y*Buf->Pitch,
+ render_w*bytes_per_px
+ );
+
+ // Draw the cursor
+ switch(Buf->Depth)
+ {
+ case 15:
+ case 16:
+ Log_Warning("DrvUtil", "TODO: Support 15/16 bpp modes in cursor draw");
+ break;
+ case 24:
+ LOG("24-bit render");
+ for( y = 0; y < render_h; y ++ )
+ {
+ Uint8 *px;
+ px = dest;
+ for(x = 0; x < render_w; x ++, px += 3)
+ {
+ Uint32 value = src[x];
+ // TODO: Should I implement alpha blending?
+ if(value & 0xFF000000)
+ {
+ px[0] = value & 0xFF;
+ px[1] = (value >> 8) & 0xFF;
+ px[2] = (value >> 16) & 0xFF;
+ }
+ else
+ ;
+ }
+ src += Buf->CursorBitmap->W;
+ dest = (Uint8*)dest + Buf->Pitch;
+ }
+ break;
+ case 32:
+ LOG("32-bit render");
+ for( y = 0; y < render_h; y ++ )
+ {
+ Uint32 *px;
+ px = dest;
+ for(x = 0; x < render_w; x ++, px ++)
+ {
+ Uint32 value = src[x];
+ // TODO: Should I implement alpha blending?
+ if(value & 0xFF000000)
+ *px = value;
+ else
+ ; // NOP, completely transparent
+ }
+ LOG("row %i/%i (%p-%P) done", y+1, render_h, dest, MM_GetPhysAddr(dest));
+ src += Buf->CursorBitmap->W;
+ dest = (Uint8*)dest + Buf->Pitch;
+ }
+ break;
+ default:
+ Log_Error("DrvUtil", "RenderCursor - Unknown bit depth %i", Buf->Depth);
+ Buf->CursorX = -1;
+ break;
+ }
+}
+
+void DrvUtil_Video_RemoveCursor(tDrvUtil_Video_BufInfo *Buf)
+{
+ int bytes_per_px = (Buf->Depth + 7) / 8;
+ int y, save_pitch;
+ Uint8 *dest, *src;
+
+ // Just a little sanity
+ if( !Buf->CursorBitmap || Buf->CursorX == -1 ) return ;
+ if( !Buf->CursorSaveBuf ) return ;
+
+// Debug("DrvUtil_Video_RemoveCursor: (Buf=%p) dest_x=%i, dest_y=%i", Buf, Buf->CursorDestX, Buf->CursorDestY);
+
+ // Set up
+ save_pitch = Buf->CursorBitmap->W * bytes_per_px;
+ dest = (Uint8*)Buf->Framebuffer + Buf->CursorDestY * Buf->Pitch + Buf->CursorDestX*bytes_per_px;
+ src = Buf->CursorSaveBuf;
+
+ // Copy each line back
+ for( y = 0; y < Buf->CursorRenderH; y ++ )
+ {
+ memcpy( dest, src, Buf->CursorRenderW * bytes_per_px );
+ src += save_pitch;
+ dest += Buf->Pitch;
+ }
+
+ // Set the cursor as removed
+ Buf->CursorX = -1;
+}
+
+void DrvUtil_Video_2D_Fill(void *Ent, Uint16 X, Uint16 Y, Uint16 W, Uint16 H, Uint32 Colour)
+{
+ tDrvUtil_Video_BufInfo *FBInfo = Ent;
+
+ // TODO: Handle non-32bit modes
+ if( FBInfo->Depth != 32 ) return;
+
+ // TODO: Be less hacky
+ int pitch = FBInfo->Pitch/4;
+ Uint32 *buf = (Uint32*)FBInfo->Framebuffer + Y*pitch + X;
+ while( H -- ) {
+ Uint32 *tmp;
+ int i;
+ tmp = buf;
+ for(i=W;i--;tmp++) *tmp = Colour;
+ buf += pitch;
+ }
+}
+
+void DrvUtil_Video_2D_Blit(void *Ent, Uint16 DstX, Uint16 DstY, Uint16 SrcX, Uint16 SrcY, Uint16 W, Uint16 H)
+{
+ tDrvUtil_Video_BufInfo *FBInfo = Ent;
+ int scrnpitch = FBInfo->Pitch;
+ int bytes_per_px = (FBInfo->Depth + 7) / 8;
+ int dst = DstY*scrnpitch + DstX;
+ int src = SrcY*scrnpitch + SrcX;
+ int tmp;
+
+ //Log("Vesa_2D_Blit: (Ent=%p, DstX=%i, DstY=%i, SrcX=%i, SrcY=%i, W=%i, H=%i)",
+ // Ent, DstX, DstY, SrcX, SrcY, W, H);
+
+ if(SrcX + W > FBInfo->Width) W = FBInfo->Width - SrcX;
+ if(DstX + W > FBInfo->Width) W = FBInfo->Width - DstX;
+ if(SrcY + H > FBInfo->Height) H = FBInfo->Height - SrcY;
+ if(DstY + H > FBInfo->Height) H = FBInfo->Height - DstY;
+
+ //Debug("W = %i, H = %i", W, H);
+
+ if( dst > src ) {
+ // Reverse copy
+ dst += H*scrnpitch;
+ src += H*scrnpitch;
+ while( H -- ) {
+ dst -= scrnpitch;
+ src -= scrnpitch;
+ tmp = W*bytes_per_px;
+ for( tmp = W; tmp --; ) {
+ *((Uint8*)FBInfo->Framebuffer + dst + tmp) = *((Uint8*)FBInfo->Framebuffer + src + tmp);
+ }
+ }
+ }
+ else if(W == FBInfo->Width && FBInfo->Pitch == FBInfo->Width*bytes_per_px) {
+ memmove((Uint8*)FBInfo->Framebuffer + dst, (Uint8*)FBInfo->Framebuffer + src, H*FBInfo->Pitch);
+ }
+ else {
+ // Normal copy is OK
+ while( H -- ) {
+ memcpy((Uint8*)FBInfo->Framebuffer + dst, (Uint8*)FBInfo->Framebuffer + src, W*bytes_per_px);
+ dst += scrnpitch;
+ src += scrnpitch;
+ }
+ }
+ //Log("Vesa_2D_Blit: RETURN");
+}
+
+
* \}
*/
+#include <ctype.h>
+
/**
* \brief Get a random number
* \return Random number
*/
/**
* \brief Create a timestamp from a time
+ * \note Days/Months are zero-based (e.g. December is 11, and has a day range of 0-30)
*/
extern tTime timestamp(int sec, int mins, int hrs, int day, int month, int year);
/**
* \brief Extract the date/time from a timestamp
+ * \note Days/Months are zero-based (e.g. December is 11, and has a day range of 0-30)
*/
extern void format_date(tTime TS, int *year, int *month, int *day, int *hrs, int *mins, int *sec, int *ms);
/**
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * ctype.h
+ * -
+ */
+#ifndef _ACESS__CTYPE_H_
+#define _ACESS__CTYPE_H_
+
+extern int isalnum(int c);
+extern int isalpha(int c);
+extern int isascii(int c);
+extern int isblank(int c);
+extern int iscntrl(int c);
+extern int isdigit(int c);
+extern int isgraph(int c);
+extern int islower(int c);
+extern int isprint(int c);
+extern int ispunct(int c);
+extern int isspace(int c);
+extern int isupper(int c);
+extern int isxdigit(int c);
+
+extern int toupper(int c);
+extern int tolower(int c);
+
+#endif
+
EINVAL, // Invalid Paramater
ENOMEM, // No free memory
EACCES, // Not permitted
+ EBUSY, // Resource is busy
ENOTFOUND, // Item not found
EREADONLY, // Read only
ENOTIMPL, // Not implemented
struct sModule *Next; //!< Next module in list (not to be touched by the driver)
const char *Name; //!< Module Name/Identifier
int (*Init)(char **Arguments); //!< Module initialiser / entrypoint
- void (*Deinit)(void); //!< Cleanup Function
+ int (*Deinit)(void); //!< Cleanup Function
const char **Dependencies; //!< NULL terminated list of dependencies
} PACKED tModule;
--- /dev/null
+/*
+ * Acess2 Kernel
+ *
+ * rwmutex.c
+ * - Reader-Writer Mutex (Multiple Readers, One Writer)
+ */
+#ifndef _RWLOCK_H
+#define _RWLOCK_H
+
+#include <acess.h>
+
+typedef struct sRWLock tRWLock;
+
+struct sRWLock
+{
+ tShortSpinlock Protector; //!< Protector for the lock strucure
+ const char *Name; //!< Human-readable name
+ int Level; // Number of readers active
+ struct sThread *volatile Owner; //!< Owner of the lock (set upon getting the lock)
+ struct sThread *ReaderWaiting; //!< Waiting threads (readers)
+ struct sThread *ReaderWaitingLast; //!< Waiting threads
+
+ struct sThread *WriterWaiting; //!< Waiting threads (writers)
+ struct sThread *WriterWaitingLast; //!< Waiting threads
+};
+
+/**
+ * \brief Acquire a heavy mutex
+ * \param Mutex Mutex to acquire
+ * \return zero on success, -1 if terminated
+ *
+ * This type of mutex checks if the mutex is avaliable, and acquires it
+ * if it is. Otherwise, the current thread is added to the mutex's wait
+ * queue and the thread suspends. When the holder of the mutex completes,
+ * the oldest thread (top thread) on the queue is given the lock and
+ * restarted.
+ */
+extern int RWLock_AcquireRead(tRWLock *Lock);
+
+extern int RWLock_AcquireWrite(tRWLock *LOck);
+
+/**
+ * \brief Release a held mutex
+ * \param Mutex Mutex to release
+ * \note Releasing a non-held mutex has no effect
+ */
+extern void RWLock_Release(tRWLock *Lock);
+
+#endif
THREAD_STAT_ACTIVE, // Running and schedulable process
THREAD_STAT_SLEEPING, // Message Sleep
THREAD_STAT_MUTEXSLEEP, // Mutex Sleep
+ THREAD_STAT_RWLOCKSLEEP, // Read-Writer lock Sleep
THREAD_STAT_SEMAPHORESLEEP, // Semaphore Sleep
THREAD_STAT_QUEUESLEEP, // Queue
THREAD_STAT_EVENTSLEEP, // Event sleep
* \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
+ * \return -1: Error (not present), 0: Not freed, 1: Freed
*/
-extern void Inode_UncacheNode(int Handle, Uint64 Inode);
+extern int Inode_UncacheNode(int Handle, Uint64 Inode);
/**
* \fn void Inode_ClearCache(int Handle)
* \brief Clears the cache for a handle
#define VFS_OPENFLAG_NOLINK 0x40
//! Create the file if it doesn't exist
#define VFS_OPENFLAG_CREATE 0x80
+//! Treat as a directory
+#define VFS_OPENFLAG_DIRECTORY 0x100
//! Open as a user
#define VFS_OPENFLAG_USER 0x8000
/**
* \return 1 on succes, -1 on error
*/
extern int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options);
+/**
+ * \brief Unmount a mounted filesystem
+ * \param Mountpoint Location of the mount
+ * \return 0 on success, errno on error
+ */
+extern int VFS_Unmount(const char *Mountpoint);
+/**
+ * \brief Attemt to unmount all fileystems
+ * \return Number of unmounted filesytems, -1 if none left to unmount
+ * \note Can return 0 when there are stil volumes mounted if there are open handles
+ */
+extern int VFS_UnmountAll(void);
+
/**
* \brief Create a new directory
* \param Path Path to new directory (absolute or relative)
#define _VFS_INT_H
#include "vfs.h"
+#include <rwlock.h>
// === TYPES ===
typedef struct sVFS_Mount {
char *Options;
tVFS_Driver *Filesystem;
tVFS_Node *RootNode;
+
+ int OpenHandleCount;
+
char StrData[];
} tVFS_Mount;
} tVFS_MMapPage;
// === GLOBALS ===
+extern tRWLock glVFS_MountList;
extern tVFS_Mount *gVFS_Mounts;
// === PROTOTYPES ===
* Common Library Functions
*/
#include <acess.h>
-#include <hal_proc.h>
// === CONSTANTS ===
-#define RANDOM_SEED 0xACE55052
-#define RANDOM_A 0x00731ADE
-#define RANDOM_C 12345
-#define RANDOM_SPRUCE 0xf12b039
// Jan Feb Mar Apr May Jun Jul Aug Sept Oct Nov Dec
const short DAYS_BEFORE[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
#define UNIX_TO_2K ((30*365*3600*24) + (7*3600*24)) //Normal years + leap years
// === PROTOTYPES ===
#if 0
- int atoi(const char *string);
- int ParseInt(const char *string, int *Val);
-void itoa(char *buf, Uint64 num, int base, int minLength, char pad);
- int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args);
- int snprintf(char *__s, size_t __n, const char *__format, ...);
- int sprintf(char *__s, const char *__format, ...);
-#endif
- int tolower(int c);
-#if 0
- int strucmp(const char *Str1, const char *Str2);
-char *strchr(const char *__s, int __c);
- int strpos(const char *Str, char Ch);
Uint8 ByteSum(void *Ptr, int Size);
-size_t strlen(const char *__s);
-char *strcpy(char *__str1, const char *__str2);
-char *strncpy(char *__str1, const char *__str2, size_t max);
- int strcmp(const char *str1, const char *str2);
- int strncmp(const char *str1, const char *str2, size_t num);
-char *_strdup(const char *File, int Line, const char *Str);
char **str_split(const char *__str, char __ch);
int strpos8(const char *str, Uint32 Search);
int ReadUTF8(Uint8 *str, Uint32 *Val);
int DivUp(int num, int dem);
Sint64 timestamp(int sec, int mins, int hrs, int day, int month, int year);
void format_date(tTime TS, int *year, int *month, int *day, int *hrs, int *mins, int *sec, int *ms);
- int rand(void);
-
- int CheckString(char *String);
- int CheckMem(void *Mem, int NumBytes);
int ModUtil_LookupString(char **Array, char *Needle);
int ModUtil_SetIdent(char *Dest, char *Value);
int Hex(char *Dest, size_t Size, const Uint8 *SourceData);
int UnHex(Uint8 *Dest, size_t DestSize, const char *SourceString);
+
+Uint16 SwapEndian16(Uint16 Val);
+Uint32 SwapEndian32(Uint16 Val);
+Uint64 SwapEndian64(Uint16 Val);
#endif
// === EXPORTS ===
-EXPORT(atoi);
-EXPORT(itoa);
-EXPORT(vsnprintf);
-EXPORT(snprintf);
-EXPORT(sprintf);
-EXPORT(tolower);
-EXPORT(strucmp);
-EXPORT(strchr);
-EXPORT(strpos);
EXPORT(ByteSum);
-EXPORT(strlen);
-EXPORT(strcpy);
-EXPORT(strncpy);
-EXPORT(strcat);
-EXPORT(strncat);
-EXPORT(strcmp);
-EXPORT(strncmp);
-//EXPORT(strdup);
-EXPORT(_strdup); // Takes File/Line too
EXPORT(str_split);
EXPORT(strpos8);
EXPORT(DivUp);
EXPORT(ReadUTF8);
EXPORT(WriteUTF8);
EXPORT(timestamp);
-EXPORT(CheckString);
-EXPORT(CheckMem);
EXPORT(ModUtil_LookupString);
EXPORT(ModUtil_SetIdent);
+EXPORT(Hex);
EXPORT(UnHex);
EXPORT(SwapEndian16);
EXPORT(SwapEndian32);
EXPORT(SwapEndian64);
-EXPORT(memmove);
// === CODE ===
-/**
- * \brief Convert a string into an integer
- */
-int atoi(const char *string)
-{
- int ret = 0;
- ParseInt(string, &ret);
- return ret;
-}
-int ParseInt(const char *string, int *Val)
-{
- int ret = 0;
- int bNeg = 0;
- const char *orig_string = string;
-
- //Log("atoi: (string='%s')", string);
-
- // Clear non-numeric characters
- while( !('0' <= *string && *string <= '9') && *string != '-' ) string++;
- if( *string == '-' ) {
- bNeg = 1;
- while( !('0' <= *string && *string <= '9') ) string++;
- }
-
- if(*string == '0')
- {
- string ++;
- if(*string == 'x')
- {
- // Hex
- string ++;
- for( ;; string ++ )
- {
- if('0' <= *string && *string <= '9') {
- ret *= 16;
- ret += *string - '0';
- }
- else if('A' <= *string && *string <= 'F') {
- ret *= 16;
- ret += *string - 'A' + 10;
- }
- else if('a' <= *string && *string <= 'f') {
- ret *= 16;
- ret += *string - 'a' + 10;
- }
- else
- break;
- }
- }
- else // Octal
- {
- for( ; '0' <= *string && *string <= '7'; string ++ )
- {
- ret *= 8;
- ret += *string - '0';
- }
- }
- }
- else // Decimal
- {
- for( ; '0' <= *string && *string <= '9'; string++)
- {
- ret *= 10;
- ret += *string - '0';
- }
- // Error check
- if( ret == 0 ) return 0;
- }
-
- if(bNeg) ret = -ret;
-
- //Log("atoi: RETURN %i", ret);
-
- if(Val) *Val = ret;
-
- return string - orig_string;
-}
-
-static const char cUCDIGITS[] = "0123456789ABCDEF";
-/**
- * \fn void itoa(char *buf, Uint64 num, int base, int minLength, char pad)
- * \brief Convert an integer into a character string
- */
-void itoa(char *buf, Uint64 num, int base, int minLength, char pad)
-{
- char tmpBuf[64+1];
- int pos=0, i;
- Uint64 rem;
-
- // Sanity check
- if(!buf) return;
-
- // Sanity Check
- if(base > 16 || base < 2) {
- buf[0] = 0;
- return;
- }
-
- // Convert
- while(num > base-1) {
- num = DivMod64U(num, base, &rem); // Shift `num` and get remainder
- tmpBuf[pos] = cUCDIGITS[ rem ];
- pos++;
- }
- tmpBuf[pos++] = cUCDIGITS[ num ]; // Last digit of `num`
-
- // Put in reverse
- i = 0;
- minLength -= pos;
- while(minLength-- > 0) buf[i++] = pad;
- while(pos-- > 0) buf[i++] = tmpBuf[pos]; // Reverse the order of characters
- buf[i] = 0;
-}
-
-/**
- * \brief Append a character the the vsnprintf output
- */
-#define PUTCH(c) _putch(c)
-#define GETVAL() do {\
- if(isLongLong) val = va_arg(args, Uint64);\
- else val = va_arg(args, unsigned int);\
- }while(0)
-/**
- * \brief VArg String Number Print Formatted
- */
-int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args)
-{
- char c, pad = ' ';
- int minSize = 0, precision = -1, len;
- char tmpBuf[34]; // For Integers
- const char *p = NULL;
- int isLongLong = 0;
- Uint64 val;
- size_t pos = 0;
- // Flags
- int bPadLeft = 0;
-
- auto void _putch(char ch);
-
- void _putch(char ch)
- {
- if(pos < __maxlen)
- {
- if(__s) __s[pos] = ch;
- pos ++;
- }
- }
-
- while((c = *__format++) != 0)
- {
- // Non control character
- if(c != '%') { PUTCH(c); continue; }
-
- c = *__format++;
- if(c == '\0') break;
-
- // Literal %
- if(c == '%') { PUTCH('%'); continue; }
-
- // Pointer - Done first for debugging
- if(c == 'p') {
- Uint ptr = va_arg(args, Uint);
- PUTCH('*'); PUTCH('0'); PUTCH('x');
- for( len = BITS/4; len --; )
- PUTCH( cUCDIGITS[ (ptr>>(len*4))&15 ] );
- continue ;
- }
-
- // - Padding Side Flag
- if(c == '-') {
- bPadLeft = 1;
- c = *__format++;
- }
-
- // - Padding
- if(c == '0') {
- pad = '0';
- c = *__format++;
- }
- else
- pad = ' ';
-
- // - Minimum length
- if(c == '*') { // Dynamic length
- minSize = va_arg(args, unsigned int);
- c = *__format++;
- }
- else if('1' <= c && c <= '9')
- {
- minSize = 0;
- while('0' <= c && c <= '9')
- {
- minSize *= 10;
- minSize += c - '0';
- c = *__format++;
- }
- }
- else
- minSize = 0;
-
- // - Precision
- precision = -1;
- if( c == '.' ) {
- c = *__format++;
-
- if(c == '*') { // Dynamic length
- precision = va_arg(args, unsigned int);
- c = *__format++;
- }
- else if('1' <= c && c <= '9')
- {
- precision = 0;
- while('0' <= c && c <= '9')
- {
- precision *= 10;
- precision += c - '0';
- c = *__format++;
- }
- }
- }
-
- // - Default, Long or LongLong?
- isLongLong = 0;
- if(c == 'l') // Long is actually the default on x86
- {
- c = *__format++;
- if(c == 'l') {
- c = *__format++;
- isLongLong = 1;
- }
- }
-
- // - Now get the format code
- p = tmpBuf;
- switch(c)
- {
- case 'd':
- case 'i':
- GETVAL();
- if( isLongLong && val >> 63 ) {
- PUTCH('-');
- val = -val;
- }
- else if( !isLongLong && val >> 31 ) {
- PUTCH('-');
- val = -(Sint32)val;
- }
- itoa(tmpBuf, val, 10, minSize, pad);
- goto printString;
- case 'u': // Unsigned
- GETVAL();
- itoa(tmpBuf, val, 10, minSize, pad);
- goto printString;
- case 'P': // Physical Address
- PUTCH('0');
- PUTCH('x');
- if(sizeof(tPAddr) > 4) isLongLong = 1;
- GETVAL();
- itoa(tmpBuf, val, 16, minSize, pad);
- goto printString;
- case 'X': // Hex
- if(BITS == 64)
- isLongLong = 1; // TODO: Handle non-x86 64-bit archs
- GETVAL();
- itoa(tmpBuf, val, 16, minSize, pad);
- goto printString;
-
- case 'x': // Lower case hex
- GETVAL();
- itoa(tmpBuf, val, 16, minSize, pad);
- goto printString;
- case 'o': // Octal
- GETVAL();
- itoa(tmpBuf, val, 8, minSize, pad);
- goto printString;
- case 'b':
- GETVAL();
- itoa(tmpBuf, val, 2, minSize, pad);
- goto printString;
-
- case 'B': //Boolean
- val = va_arg(args, unsigned int);
- if(val) p = "True";
- else p = "False";
- goto printString;
-
- // String - Null Terminated Array
- case 's':
- p = va_arg(args, char*); // Get Argument
- if( !p || !CheckString(p) ) p = "(inval)"; // Avoid #PFs
- printString:
- if(!p) p = "(null)";
- len = strlen(p);
- if( !bPadLeft ) while(len++ < minSize) PUTCH(pad);
- while(*p && precision--) PUTCH(*p++);
- if( bPadLeft ) while(len++ < minSize) PUTCH(pad);
- break;
-
- case 'C': // Non-Null Terminated Character Array
- p = va_arg(args, char*);
- if( !CheckMem(p, minSize) ) continue; // No #PFs please
- if(!p) goto printString;
- while(minSize--) PUTCH(*p++);
- break;
-
- // Single Character
- case 'c':
- default:
- GETVAL();
- PUTCH( (Uint8)val );
- break;
- }
- }
-
- if(__s && pos != __maxlen)
- __s[pos] = '\0';
-
- return pos;
-}
-#undef PUTCH
-
-/**
- */
-int snprintf(char *__s, size_t __n, const char *__format, ...)
-{
- va_list args;
- int ret;
-
- va_start(args, __format);
- ret = vsnprintf(__s, __n, __format, args);
- va_end(args);
-
- return ret;
-}
-
-/**
- */
-int sprintf(char *__s, const char *__format, ...)
-{
- va_list args;
- int ret;
-
- va_start(args, __format);
- ret = vsnprintf(__s, -1, __format, args);
- va_end(args);
-
- return ret;
-}
-
-/**
- * \fn int tolower(int c)
- * \brief Converts a character to lower case
- */
-int tolower(int c)
-{
- if('A' <= c && c <= 'Z')
- return c - 'A' + 'a';
- return c;
-}
-
-/**
- * \fn int strucmp(const char *Str1, const char *Str2)
- * \brief Compare \a Str1 and \a Str2 case-insensitively
- */
-int strucmp(const char *Str1, const char *Str2)
-{
- while(*Str1 && tolower(*Str1) == tolower(*Str2))
- Str1++, Str2++;
- return tolower(*Str1) - tolower(*Str2);
-}
-
-/**
- * \brief Locate a byte in a string
- */
-char *strchr(const char *__s, int __c)
-{
- for( ; *__s; __s ++ )
- {
- if( *__s == __c ) return (char*)__s;
- }
- return NULL;
-}
-
-/**
- * \fn int strpos(const char *Str, char Ch)
- * \brief Search a string for an ascii character
- */
-int strpos(const char *Str, char Ch)
-{
- int pos;
- for(pos=0;Str[pos];pos++)
- {
- if(Str[pos] == Ch) return pos;
- }
- return -1;
-}
-
/**
* \fn Uint8 ByteSum(void *Ptr, int Size)
* \brief Adds the bytes in a memory region and returns the sum
return sum;
}
-/**
- * \fn size_t strlen(const char *__str)
- * \brief Get the length of string
- */
-size_t strlen(const char *__str)
-{
- size_t ret = 0;
- while(*__str++) ret++;
- return ret;
-}
-
-/**
- * \brief Copy a string to a new location
- */
-char *strcpy(char *__str1, const char *__str2)
-{
- while(*__str2)
- *__str1++ = *__str2++;
- *__str1 = '\0'; // Terminate String
- return __str1;
-}
-
-/**
- * \brief Copy a string to a new location
- * \note Copies at most `max` chars
- */
-char *strncpy(char *__str1, const char *__str2, size_t __max)
-{
- while(*__str2 && __max-- >= 1)
- *__str1++ = *__str2++;
- if(__max)
- *__str1 = '\0'; // Terminate String
- return __str1;
-}
-
-/**
- * \brief Append a string to another
- */
-char *strcat(char *__dest, const char *__src)
-{
- while(*__dest++);
- __dest--;
- while(*__src)
- *__dest++ = *__src++;
- *__dest = '\0';
- return __dest;
-}
-
-/**
- * \brief Append at most \a n chars to a string from another
- * \note At most n+1 chars are written (the dest is always zero terminated)
- */
-char *strncat(char *__dest, const char *__src, size_t n)
-{
- while(*__dest++);
- while(*__src && n-- >= 1)
- *__dest++ = *__src++;
- *__dest = '\0';
- return __dest;
-}
-
-/**
- * \fn int strcmp(const char *str1, const char *str2)
- * \brief Compare two strings return the difference between
- * the first non-matching characters.
- */
-int strcmp(const char *str1, const char *str2)
-{
- while(*str1 && *str1 == *str2)
- str1++, str2++;
- return *str1 - *str2;
-}
-
-/**
- * \fn int strncmp(const char *Str1, const char *Str2, size_t num)
- * \brief Compare strings \a Str1 and \a Str2 to a maximum of \a num characters
- */
-int strncmp(const char *Str1, const char *Str2, size_t num)
-{
- if(num == 0) return 0; // TODO: Check what should officially happen here
- while(--num && *Str1 && *Str1 == *Str2)
- Str1++, Str2++;
- return *Str1-*Str2;
-}
-
-#if 0
-/**
- * \fn char *strdup(const char *Str)
- * \brief Duplicates a string
- */
-char *strdup(const char *Str)
-{
- char *ret;
- ret = malloc(strlen(Str)+1);
- if( !ret ) return NULL;
- strcpy(ret, Str);
- return ret;
-}
-#else
-
-/**
- * \fn char *_strdup(const char *File, int Line, const char *Str)
- * \brief Duplicates a string
- */
-char *_strdup(const char *File, int Line, const char *Str)
-{
- char *ret;
- ret = Heap_Allocate(File, Line, strlen(Str)+1);
- if( !ret ) return NULL;
- strcpy(ret, Str);
- return ret;
-}
-#endif
-
/**
* \brief Split a string using the passed character
* \return NULL terminated array of strings on the heap
*/
int ReadUTF8(const Uint8 *str, Uint32 *Val)
{
+ Uint32 outval;
+
*Val = 0xFFFD; // Assume invalid character
// ASCII
// Two Byte
if( (*str & 0xE0) == 0xC0 ) {
- *Val = (*str & 0x1F) << 6; // Upper 6 Bits
+ outval = (*str & 0x1F) << 6; // Upper 6 Bits
str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F); // Lower 6 Bits
+ if( (*str & 0xC0) != 0x80) return 2; // Validity check
+ outval |= (*str & 0x3F); // Lower 6 Bits
+ *Val = outval;
return 2;
}
// Three Byte
if( (*str & 0xF0) == 0xE0 ) {
- *Val = (*str & 0x0F) << 12; // Upper 4 Bits
+ outval = (*str & 0x0F) << 12; // Upper 4 Bits
str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F) << 6; // Middle 6 Bits
+ if( (*str & 0xC0) != 0x80) return 2; // Validity check
+ outval |= (*str & 0x3F) << 6; // Middle 6 Bits
str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F); // Lower 6 Bits
+ if( (*str & 0xC0) != 0x80) return 3; // Validity check
+ outval |= (*str & 0x3F); // Lower 6 Bits
+ *Val = outval;
return 3;
}
// Four Byte
if( (*str & 0xF1) == 0xF0 ) {
- *Val = (*str & 0x07) << 18; // Upper 3 Bits
+ outval = (*str & 0x07) << 18; // Upper 3 Bits
str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F) << 12; // Middle-upper 6 Bits
+ if( (*str & 0xC0) != 0x80) return 2; // Validity check
+ outval |= (*str & 0x3F) << 12; // Middle-upper 6 Bits
str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F) << 6; // Middle-lower 6 Bits
+ if( (*str & 0xC0) != 0x80) return 3; // Validity check
+ outval |= (*str & 0x3F) << 6; // Middle-lower 6 Bits
str ++;
- if( (*str & 0xC0) != 0x80) return -1; // Validity check
- *Val |= (*str & 0x3F); // Lower 6 Bits
+ if( (*str & 0xC0) != 0x80) return 4; // Validity check
+ outval |= (*str & 0x3F); // Lower 6 Bits
+ *Val = outval;
return 4;
}
*day += TS; // Plus offset from leap handling above
}
-/**
- * \fn int rand()
- * \brief Pseudo random number generator
- */
-int rand(void)
-{
- #if 0
- static Uint state = RANDOM_SEED;
- Uint old = state;
- // Get the next state value
- giRandomState = (RANDOM_A*state + RANDOM_C);
- // Check if it has changed, and if it hasn't, change it
- if(state == old) state += RANDOM_SPRUCE;
- return state;
- #else
- // http://en.wikipedia.org/wiki/Xorshift
- // 2010-10-03
- static Uint32 x = 123456789;
- static Uint32 y = 362436069;
- static Uint32 z = 521288629;
- static Uint32 w = 88675123;
- Uint32 t;
-
- t = x ^ (x << 11);
- x = y; y = z; z = w;
- return w = w ^ (w >> 19) ^ t ^ (t >> 8);
- #endif
-}
-
-/* *
- * \name Memory Validation
- * \{
- */
-/**
- * \brief Checks if a string resides fully in valid memory
- */
-int CheckString(const char *String)
-{
- tVAddr addr;
- int bUser;
-
- addr = (tVAddr)String;
-
- if( !MM_GetPhysAddr( addr ) )
- return 0;
-
- // Check 1st page
- bUser = MM_IsUser( addr );
-
- while( *(char*)addr )
- {
- if( (addr & (PAGE_SIZE-1)) == 0 )
- {
- if(bUser && !MM_IsUser(addr) )
- return 0;
- if(!bUser && !MM_GetPhysAddr(addr) )
- return 0;
- }
- addr ++;
- }
- return 1;
-}
-
-/**
- * \brief Check if a sized memory region is valid memory
- * \return Boolean success
- */
-int CheckMem(const void *Mem, int NumBytes)
-{
- return MM_IsValidBuffer( (tVAddr)Mem, NumBytes );
-}
-/* *
- * \}
- */
-
/**
* \brief Search a string array for \a Needle
* \note Helper function for eTplDrv_IOCtl::DRV_IOCTL_LOOKUP
{
return SwapEndian32(Val >> 32) | ((Uint64)SwapEndian32(Val) << 32);
}
-
-void *memmove(void *__dest, const void *__src, size_t len)
-{
- size_t block_size;
- char *dest = __dest;
- const char *src = __src;
- void *ret = __dest;
-
- if( len == 0 || dest == src )
- return dest;
-
- if( (tVAddr)dest > (tVAddr)src + len )
- return memcpy(dest, src, len);
- if( (tVAddr)dest + len < (tVAddr)src )
- return memcpy(dest, src, len);
-
- // NOTE: Assumes memcpy works forward
- if( (tVAddr)dest < (tVAddr)src )
- return memcpy(dest, src, len);
-
- if( (tVAddr)dest < (tVAddr)src )
- block_size = (tVAddr)src - (tVAddr)dest;
- else
- block_size = (tVAddr)dest - (tVAddr)src;
-
- block_size &= ~0xF;
-
- while(len >= block_size)
- {
- memcpy(dest, src, block_size);
- len -= block_size;
- dest += block_size;
- src += block_size;
- }
- memcpy(dest, src, len);
- return ret;
-
-}
-
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * libc.c
+ * - Kernel-land C Library
+ */
+#include <acess.h>
+#include <hal_proc.h> // For MM_*
+
+// === CONSTANTS ===
+#define RANDOM_SEED 0xACE55052
+#define RANDOM_A 0x00731ADE
+#define RANDOM_C 12345
+#define RANDOM_SPRUCE 0xf12b039
+
+// === PROTOTYPES ===
+#if 0
+ int atoi(const char *string);
+ int ParseInt(const char *string, int *Val);
+void itoa(char *buf, Uint64 num, int base, int minLength, char pad);
+ int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args);
+ int snprintf(char *__s, size_t __n, const char *__format, ...);
+ int sprintf(char *__s, const char *__format, ...);
+ int strucmp(const char *Str1, const char *Str2);
+char *strchr(const char *__s, int __c);
+ int strpos(const char *Str, char Ch);
+size_t strlen(const char *__s);
+char *strcpy(char *__str1, const char *__str2);
+char *strncpy(char *__str1, const char *__str2, size_t max);
+char *strcat(char *dest, const char *source);
+ int strcmp(const char *str1, const char *str2);
+ int strncmp(const char *str1, const char *str2, size_t num);
+char *_strdup(const char *File, int Line, const char *Str);
+ int rand(void);
+void *memmove(void *__dest, const void *__src, size_t len);
+
+ int CheckString(char *String);
+ int CheckMem(void *Mem, int NumBytes);
+#endif
+
+// === EXPORTS ===
+EXPORT(atoi);
+EXPORT(itoa);
+EXPORT(vsnprintf);
+EXPORT(snprintf);
+EXPORT(sprintf);
+EXPORT(tolower);
+
+EXPORT(strucmp);
+EXPORT(strchr);
+EXPORT(strpos);
+EXPORT(strlen);
+EXPORT(strcpy);
+EXPORT(strncpy);
+EXPORT(strcat);
+EXPORT(strncat);
+EXPORT(strcmp);
+EXPORT(strncmp);
+//EXPORT(strdup);
+EXPORT(_strdup); // Takes File/Line too
+EXPORT(rand);
+EXPORT(memmove);
+
+EXPORT(CheckString);
+EXPORT(CheckMem);
+
+// === CODE ===
+/**
+ * \brief Convert a string into an integer
+ */
+int atoi(const char *string)
+{
+ int ret = 0;
+ ParseInt(string, &ret);
+ return ret;
+}
+int ParseInt(const char *string, int *Val)
+{
+ int ret = 0;
+ int bNeg = 0;
+ const char *orig_string = string;
+
+ //Log("atoi: (string='%s')", string);
+
+ // Clear non-numeric characters
+ while( !('0' <= *string && *string <= '9') && *string != '-' ) string++;
+ if( *string == '-' ) {
+ bNeg = 1;
+ while( !('0' <= *string && *string <= '9') ) string++;
+ }
+
+ if(*string == '0')
+ {
+ string ++;
+ if(*string == 'x')
+ {
+ // Hex
+ string ++;
+ for( ;; string ++ )
+ {
+ if('0' <= *string && *string <= '9') {
+ ret *= 16;
+ ret += *string - '0';
+ }
+ else if('A' <= *string && *string <= 'F') {
+ ret *= 16;
+ ret += *string - 'A' + 10;
+ }
+ else if('a' <= *string && *string <= 'f') {
+ ret *= 16;
+ ret += *string - 'a' + 10;
+ }
+ else
+ break;
+ }
+ }
+ else // Octal
+ {
+ for( ; '0' <= *string && *string <= '7'; string ++ )
+ {
+ ret *= 8;
+ ret += *string - '0';
+ }
+ }
+ }
+ else // Decimal
+ {
+ for( ; '0' <= *string && *string <= '9'; string++)
+ {
+ ret *= 10;
+ ret += *string - '0';
+ }
+ // Error check
+ if( ret == 0 ) return 0;
+ }
+
+ if(bNeg) ret = -ret;
+
+ //Log("atoi: RETURN %i", ret);
+
+ if(Val) *Val = ret;
+
+ return string - orig_string;
+}
+
+static const char cUCDIGITS[] = "0123456789ABCDEF";
+/**
+ * \fn void itoa(char *buf, Uint64 num, int base, int minLength, char pad)
+ * \brief Convert an integer into a character string
+ */
+void itoa(char *buf, Uint64 num, int base, int minLength, char pad)
+{
+ char tmpBuf[64+1];
+ int pos=0, i;
+ Uint64 rem;
+
+ // Sanity check
+ if(!buf) return;
+
+ // Sanity Check
+ if(base > 16 || base < 2) {
+ buf[0] = 0;
+ return;
+ }
+
+ // Convert
+ while(num > base-1) {
+ num = DivMod64U(num, base, &rem); // Shift `num` and get remainder
+ tmpBuf[pos] = cUCDIGITS[ rem ];
+ pos++;
+ }
+ tmpBuf[pos++] = cUCDIGITS[ num ]; // Last digit of `num`
+
+ // Put in reverse
+ i = 0;
+ minLength -= pos;
+ while(minLength-- > 0) buf[i++] = pad;
+ while(pos-- > 0) buf[i++] = tmpBuf[pos]; // Reverse the order of characters
+ buf[i] = 0;
+}
+
+/**
+ * \brief Append a character the the vsnprintf output
+ */
+#define PUTCH(c) _putch(c)
+#define GETVAL() do {\
+ if(isLongLong) val = va_arg(args, Uint64);\
+ else val = va_arg(args, unsigned int);\
+ }while(0)
+/**
+ * \brief VArg String Number Print Formatted
+ */
+int vsnprintf(char *__s, size_t __maxlen, const char *__format, va_list args)
+{
+ char c, pad = ' ';
+ int minSize = 0, precision = -1, len;
+ char tmpBuf[34]; // For Integers
+ const char *p = NULL;
+ int isLongLong = 0;
+ Uint64 val;
+ size_t pos = 0;
+ // Flags
+ int bPadLeft = 0;
+
+ auto void _putch(char ch);
+
+ void _putch(char ch)
+ {
+ if(pos < __maxlen)
+ {
+ if(__s) __s[pos] = ch;
+ pos ++;
+ }
+ }
+
+ while((c = *__format++) != 0)
+ {
+ // Non control character
+ if(c != '%') { PUTCH(c); continue; }
+
+ c = *__format++;
+ if(c == '\0') break;
+
+ // Literal %
+ if(c == '%') { PUTCH('%'); continue; }
+
+ // Pointer - Done first for debugging
+ if(c == 'p') {
+ Uint ptr = va_arg(args, Uint);
+ PUTCH('*'); PUTCH('0'); PUTCH('x');
+ for( len = BITS/4; len --; )
+ PUTCH( cUCDIGITS[ (ptr>>(len*4))&15 ] );
+ continue ;
+ }
+
+ // - Padding Side Flag
+ if(c == '-') {
+ bPadLeft = 1;
+ c = *__format++;
+ }
+
+ // - Padding
+ if(c == '0') {
+ pad = '0';
+ c = *__format++;
+ }
+ else
+ pad = ' ';
+
+ // - Minimum length
+ if(c == '*') { // Dynamic length
+ minSize = va_arg(args, unsigned int);
+ c = *__format++;
+ }
+ else if('1' <= c && c <= '9')
+ {
+ minSize = 0;
+ while('0' <= c && c <= '9')
+ {
+ minSize *= 10;
+ minSize += c - '0';
+ c = *__format++;
+ }
+ }
+ else
+ minSize = 0;
+
+ // - Precision
+ precision = -1;
+ if( c == '.' ) {
+ c = *__format++;
+
+ if(c == '*') { // Dynamic length
+ precision = va_arg(args, unsigned int);
+ c = *__format++;
+ }
+ else if('1' <= c && c <= '9')
+ {
+ precision = 0;
+ while('0' <= c && c <= '9')
+ {
+ precision *= 10;
+ precision += c - '0';
+ c = *__format++;
+ }
+ }
+ }
+
+ // - Default, Long or LongLong?
+ isLongLong = 0;
+ if(c == 'l') // Long is actually the default on x86
+ {
+ c = *__format++;
+ if(c == 'l') {
+ c = *__format++;
+ isLongLong = 1;
+ }
+ }
+
+ // - Now get the format code
+ p = tmpBuf;
+ switch(c)
+ {
+ case 'd':
+ case 'i':
+ GETVAL();
+ if( isLongLong && val >> 63 ) {
+ PUTCH('-');
+ val = -val;
+ }
+ else if( !isLongLong && val >> 31 ) {
+ PUTCH('-');
+ val = -(Sint32)val;
+ }
+ itoa(tmpBuf, val, 10, minSize, pad);
+ goto printString;
+ case 'u': // Unsigned
+ GETVAL();
+ itoa(tmpBuf, val, 10, minSize, pad);
+ goto printString;
+ case 'P': // Physical Address
+ PUTCH('0');
+ PUTCH('x');
+ if(sizeof(tPAddr) > 4) isLongLong = 1;
+ GETVAL();
+ itoa(tmpBuf, val, 16, minSize, pad);
+ goto printString;
+ case 'X': // Hex
+ if(BITS == 64)
+ isLongLong = 1; // TODO: Handle non-x86 64-bit archs
+ GETVAL();
+ itoa(tmpBuf, val, 16, minSize, pad);
+ goto printString;
+
+ case 'x': // Lower case hex
+ GETVAL();
+ itoa(tmpBuf, val, 16, minSize, pad);
+ goto printString;
+ case 'o': // Octal
+ GETVAL();
+ itoa(tmpBuf, val, 8, minSize, pad);
+ goto printString;
+ case 'b':
+ GETVAL();
+ itoa(tmpBuf, val, 2, minSize, pad);
+ goto printString;
+
+ case 'B': //Boolean
+ val = va_arg(args, unsigned int);
+ if(val) p = "True";
+ else p = "False";
+ goto printString;
+
+ // String - Null Terminated Array
+ case 's':
+ p = va_arg(args, char*); // Get Argument
+ if( !p || !CheckString(p) ) p = "(inval)"; // Avoid #PFs
+ printString:
+ if(!p) p = "(null)";
+ len = strlen(p);
+ if( !bPadLeft ) while(len++ < minSize) PUTCH(pad);
+ while(*p && precision--) PUTCH(*p++);
+ if( bPadLeft ) while(len++ < minSize) PUTCH(pad);
+ break;
+
+ case 'C': // Non-Null Terminated Character Array
+ p = va_arg(args, char*);
+ if( !CheckMem(p, minSize) ) continue; // No #PFs please
+ if(!p) goto printString;
+ while(minSize--) PUTCH(*p++);
+ break;
+
+ // Single Character
+ case 'c':
+ default:
+ GETVAL();
+ PUTCH( (Uint8)val );
+ break;
+ }
+ }
+
+ if(__s && pos != __maxlen)
+ __s[pos] = '\0';
+
+ return pos;
+}
+#undef PUTCH
+
+/**
+ */
+int snprintf(char *__s, size_t __n, const char *__format, ...)
+{
+ va_list args;
+ int ret;
+
+ va_start(args, __format);
+ ret = vsnprintf(__s, __n, __format, args);
+ va_end(args);
+
+ return ret;
+}
+
+/**
+ */
+int sprintf(char *__s, const char *__format, ...)
+{
+ va_list args;
+ int ret;
+
+ va_start(args, __format);
+ ret = vsnprintf(__s, -1, __format, args);
+ va_end(args);
+
+ return ret;
+}
+
+/*
+ * ==================
+ * ctype.h
+ * ==================
+ */
+int isalnum(int c)
+{
+ return isalpha(c) || isdigit(c);
+}
+int isalpha(int c)
+{
+ return isupper(c) || islower(c);
+}
+int isascii(int c)
+{
+ return (0 <= c && c < 128);
+}
+int isblank(int c)
+{
+ if(c == '\t') return 1;
+ if(c == ' ') return 1;
+ return 0;
+}
+int iscntrl(int c)
+{
+ // TODO: Check iscntrl
+ if(c < ' ') return 1;
+ return 0;
+}
+int isdigit(int c)
+{
+ return ('0' <= c && c <= '9');
+}
+int isgraph(int c)
+{
+ // TODO: Check isgraph
+ return 0;
+}
+int islower(int c)
+{
+ return ('a' <= c && c <= 'z');
+}
+int isprint(int c)
+{
+ if( ' ' <= c && c <= 0x7F ) return 1;
+ return 0;
+}
+int ispunct(int c)
+{
+ switch(c)
+ {
+ case '.': case ',':
+ case '?': case '!':
+ return 1;
+ default:
+ return 0;
+ }
+}
+int isspace(int c)
+{
+ if(c == ' ') return 1;
+ if(c == '\t') return 1;
+ if(c == '\v') return 1;
+ if(c == '\n') return 1;
+ if(c == '\r') return 1;
+ return 0;
+}
+int isupper(int c)
+{
+ return ('a' <= c && c <= 'z');
+}
+int isxdigit(int c)
+{
+ return isdigit(c) || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F');
+}
+
+int toupper(int c)
+{
+ if( islower(c) )
+ return c - 0x20;
+ else
+ return c;
+}
+int tolower(int c)
+{
+ if( isupper(c) )
+ return c + 0x20;
+ else
+ return c;
+}
+
+/**
+ * \fn int strucmp(const char *Str1, const char *Str2)
+ * \brief Compare \a Str1 and \a Str2 case-insensitively
+ */
+int strucmp(const char *Str1, const char *Str2)
+{
+ while(*Str1 && tolower(*Str1) == tolower(*Str2))
+ Str1++, Str2++;
+ return tolower(*Str1) - tolower(*Str2);
+}
+
+/**
+ * \brief Locate a byte in a string
+ */
+char *strchr(const char *__s, int __c)
+{
+ for( ; *__s; __s ++ )
+ {
+ if( *__s == __c ) return (char*)__s;
+ }
+ return NULL;
+}
+
+/**
+ * \fn int strpos(const char *Str, char Ch)
+ * \brief Search a string for an ascii character
+ */
+int strpos(const char *Str, char Ch)
+{
+ int pos;
+ for(pos=0;Str[pos];pos++)
+ {
+ if(Str[pos] == Ch) return pos;
+ }
+ return -1;
+}
+
+/**
+ * \fn size_t strlen(const char *__str)
+ * \brief Get the length of string
+ */
+size_t strlen(const char *__str)
+{
+ size_t ret = 0;
+ while(*__str++) ret++;
+ return ret;
+}
+
+/**
+ * \brief Copy a string to a new location
+ */
+char *strcpy(char *__str1, const char *__str2)
+{
+ while(*__str2)
+ *__str1++ = *__str2++;
+ *__str1 = '\0'; // Terminate String
+ return __str1;
+}
+
+/**
+ * \brief Copy a string to a new location
+ * \note Copies at most `max` chars
+ */
+char *strncpy(char *__str1, const char *__str2, size_t __max)
+{
+ while(*__str2 && __max-- >= 1)
+ *__str1++ = *__str2++;
+ if(__max)
+ *__str1 = '\0'; // Terminate String
+ return __str1;
+}
+
+/**
+ * \brief Append a string to another
+ */
+char *strcat(char *__dest, const char *__src)
+{
+ while(*__dest++);
+ __dest--;
+ while(*__src)
+ *__dest++ = *__src++;
+ *__dest = '\0';
+ return __dest;
+}
+
+/**
+ * \brief Append at most \a n chars to a string from another
+ * \note At most n+1 chars are written (the dest is always zero terminated)
+ */
+char *strncat(char *__dest, const char *__src, size_t n)
+{
+ while(*__dest++);
+ while(*__src && n-- >= 1)
+ *__dest++ = *__src++;
+ *__dest = '\0';
+ return __dest;
+}
+
+/**
+ * \fn int strcmp(const char *str1, const char *str2)
+ * \brief Compare two strings return the difference between
+ * the first non-matching characters.
+ */
+int strcmp(const char *str1, const char *str2)
+{
+ while(*str1 && *str1 == *str2)
+ str1++, str2++;
+ return *str1 - *str2;
+}
+
+/**
+ * \fn int strncmp(const char *Str1, const char *Str2, size_t num)
+ * \brief Compare strings \a Str1 and \a Str2 to a maximum of \a num characters
+ */
+int strncmp(const char *Str1, const char *Str2, size_t num)
+{
+ if(num == 0) return 0; // TODO: Check what should officially happen here
+ while(--num && *Str1 && *Str1 == *Str2)
+ Str1++, Str2++;
+ return *Str1-*Str2;
+}
+
+#if 0
+/**
+ * \fn char *strdup(const char *Str)
+ * \brief Duplicates a string
+ */
+char *strdup(const char *Str)
+{
+ char *ret;
+ ret = malloc(strlen(Str)+1);
+ if( !ret ) return NULL;
+ strcpy(ret, Str);
+ return ret;
+}
+#else
+
+/**
+ * \fn char *_strdup(const char *File, int Line, const char *Str)
+ * \brief Duplicates a string
+ */
+char *_strdup(const char *File, int Line, const char *Str)
+{
+ char *ret;
+ ret = Heap_Allocate(File, Line, strlen(Str)+1);
+ if( !ret ) return NULL;
+ strcpy(ret, Str);
+ return ret;
+}
+#endif
+
+/**
+ * \fn int rand()
+ * \brief Pseudo random number generator
+ */
+int rand(void)
+{
+ #if 0
+ static Uint state = RANDOM_SEED;
+ Uint old = state;
+ // Get the next state value
+ giRandomState = (RANDOM_A*state + RANDOM_C);
+ // Check if it has changed, and if it hasn't, change it
+ if(state == old) state += RANDOM_SPRUCE;
+ return state;
+ #else
+ // http://en.wikipedia.org/wiki/Xorshift
+ // 2010-10-03
+ static Uint32 x = 123456789;
+ static Uint32 y = 362436069;
+ static Uint32 z = 521288629;
+ static Uint32 w = 88675123;
+ Uint32 t;
+
+ t = x ^ (x << 11);
+ x = y; y = z; z = w;
+ return w = w ^ (w >> 19) ^ t ^ (t >> 8);
+ #endif
+}
+
+void *memmove(void *__dest, const void *__src, size_t len)
+{
+ size_t block_size;
+ char *dest = __dest;
+ const char *src = __src;
+ void *ret = __dest;
+
+ if( len == 0 || dest == src )
+ return dest;
+
+ if( (tVAddr)dest > (tVAddr)src + len )
+ return memcpy(dest, src, len);
+ if( (tVAddr)dest + len < (tVAddr)src )
+ return memcpy(dest, src, len);
+
+ // NOTE: Assumes memcpy works forward
+ if( (tVAddr)dest < (tVAddr)src )
+ return memcpy(dest, src, len);
+
+ if( (tVAddr)dest < (tVAddr)src )
+ block_size = (tVAddr)src - (tVAddr)dest;
+ else
+ block_size = (tVAddr)dest - (tVAddr)src;
+
+ block_size &= ~0xF;
+
+ while(len >= block_size)
+ {
+ memcpy(dest, src, block_size);
+ len -= block_size;
+ dest += block_size;
+ src += block_size;
+ }
+ memcpy(dest, src, len);
+ return ret;
+
+}
+
+// NOTE: Strictly not libc, but lib.c is used by userland code too
+/**
+ * \name Memory Validation
+ * \{
+ */
+/**
+ * \brief Checks if a string resides fully in valid memory
+ */
+int CheckString(const char *String)
+{
+ tVAddr addr;
+ int bUser;
+
+ addr = (tVAddr)String;
+
+ if( !MM_GetPhysAddr( addr ) )
+ return 0;
+
+ // Check 1st page
+ bUser = MM_IsUser( addr );
+
+ while( *(char*)addr )
+ {
+ if( (addr & (PAGE_SIZE-1)) == 0 )
+ {
+ if(bUser && !MM_IsUser(addr) )
+ return 0;
+ if(!bUser && !MM_GetPhysAddr(addr) )
+ return 0;
+ }
+ addr ++;
+ }
+ return 1;
+}
+
+/**
+ * \brief Check if a sized memory region is valid memory
+ * \return Boolean success
+ */
+int CheckMem(const void *Mem, int NumBytes)
+{
+ return MM_IsValidBuffer( (tVAddr)Mem, NumBytes );
+}
+/* *
+ * \}
+ */
+
--- /dev/null
+/*
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * rwlock.c
+ * - Reader-Writer Lockes
+ */
+#include <acess.h>
+#include <threads_int.h>
+#include <rwlock.h>
+
+// === PROTOTYPES ===
+//
+// Acquire as a reader (see rwlock.h for documentation)
+//
+int RWLock_AcquireRead(tRWLock *Lock)
+{
+ tThread *us;
+ // Get protector
+ SHORTLOCK( &Lock->Protector );
+
+ // Check if the lock is already held by a writer
+ if( Lock->Owner )
+ {
+ SHORTLOCK( &glThreadListLock );
+
+ // - Remove from active list
+ us = Threads_RemActive();
+ us->Next = NULL;
+ // - Mark as sleeping
+ us->Status = THREAD_STAT_RWLOCKSLEEP;
+ us->WaitPointer = Lock;
+
+ // - Add to waiting
+ if(Lock->ReaderWaiting)
+ Lock->ReaderWaitingLast->Next = us;
+ else
+ Lock->ReaderWaiting = us;
+ Lock->ReaderWaitingLast = us;
+
+ #if DEBUG_TRACE_STATE
+ Log("%p (%i %s) waiting on rwlock %p",
+ us, us->TID, us->ThreadName, Lock);
+ #endif
+
+ SHORTREL( &glThreadListLock );
+ SHORTREL( &Lock->Protector );
+ while(us->Status == THREAD_STAT_RWLOCKSLEEP) Threads_Yield();
+ // We're only woken when we get the lock
+ // TODO: Handle when this isn't the case
+ us->WaitPointer = NULL;
+ }
+ // Ooh, no problems then!
+ else
+ {
+ Lock->Level++;
+ SHORTREL( & Lock->Protector );
+ }
+
+ return 0;
+}
+
+int RWLock_AcquireWrite(tRWLock *Lock)
+{
+ tThread *us;
+
+ SHORTLOCK(&Lock->Protector);
+ if( Lock->Owner || Lock->Level != 0 )
+ {
+ SHORTLOCK(&glThreadListLock);
+
+ us = Threads_RemActive();
+ us->Next = NULL;
+ us->Status = THREAD_STAT_RWLOCKSLEEP;
+ us->WaitPointer = Lock;
+
+ if( Lock->WriterWaiting )
+ Lock->WriterWaitingLast->Next = us;
+ else
+ Lock->WriterWaiting = us;
+ Lock->WriterWaitingLast = us;
+
+ SHORTREL( &glThreadListLock );
+ SHORTREL( &Lock->Protector );
+
+ while(us->Status == THREAD_STAT_RWLOCKSLEEP) Threads_Yield();
+ us->WaitPointer = NULL;
+ }
+ else
+ {
+ // Nothing else is using the lock, nice :)
+ Lock->Owner = Proc_GetCurThread();
+ SHORTREL(&Lock->Protector);
+ }
+ return 0;
+}
+
+// Release a mutex
+void RWLock_Release(tRWLock *Lock)
+{
+ SHORTLOCK( &Lock->Protector );
+
+ if( Lock->Owner == Proc_GetCurThread() )
+ Lock->Level --;
+
+ // Writers first
+ if( Lock->WriterWaiting )
+ {
+ Lock->Owner = Lock->WriterWaiting; // Set owner
+ Lock->WriterWaiting = Lock->WriterWaiting->Next; // Next!
+
+ // Wake new owner
+ if( Lock->Owner->Status != THREAD_STAT_ACTIVE )
+ Threads_AddActive(Lock->Owner);
+ }
+ else
+ {
+ Lock->Owner = NULL;
+
+ while( Lock->ReaderWaiting ) {
+ Lock->Level ++;
+ Threads_AddActive(Lock->ReaderWaiting);
+ Lock->ReaderWaiting = Lock->ReaderWaiting->Next;
+ }
+ }
+ SHORTREL( &Lock->Protector );
+}
+
+// === EXPORTS ===
+EXPORT(RWLock_AcquireRead);
+EXPORT(RWLock_AcquireWrite);
+EXPORT(RWLock_Release);
*/
int VFS_MkNod(const char *Path, Uint Flags)
{
+ tVFS_Mount *mountpt;
char *absPath, *name;
int pos = 0, oldpos = 0;
int next = 0;
// Check for root
if(absPath[0] == '\0')
- parent = VFS_ParsePath("/", NULL, NULL);
+ parent = VFS_ParsePath("/", NULL, &mountpt);
else
- parent = VFS_ParsePath(absPath, NULL, NULL);
+ parent = VFS_ParsePath(absPath, NULL, &mountpt);
LOG("parent = %p", parent);
LEAVE('i', -1);
return -1; // Error Check
}
-
+
// Permissions Check
if( !VFS_CheckACL(parent, VFS_PERM_EXECUTE|VFS_PERM_WRITE) ) {
_CloseNode(parent);
+ mountpt->OpenHandleCount --;
free(absPath);
LEAVE('i', -1);
return -1;
LOG("parent = %p", parent);
if(!parent->Type || !parent->Type->MkNod) {
- Warning("VFS_MkNod - Directory has no MkNod method");
+ Log_Warning("VFS", "VFS_MkNod - Directory has no MkNod method");
+ mountpt->OpenHandleCount --;
LEAVE('i', -1);
return -1;
}
free(absPath);
// Free Parent
+ mountpt->OpenHandleCount --;
_CloseNode(parent);
// Error Check
- if(ret == 0) {
+ if(ret != 0) {
LEAVE('i', -1);
return -1;
}
void DevFS_DelDevice(tDevFS_Driver *Device);
#endif
tVFS_Node *DevFS_InitDevice(const char *Device, const char **Options);
+void DevFS_Unmount(tVFS_Node *RootNode);
char *DevFS_ReadDir(tVFS_Node *Node, int Pos);
tVFS_Node *DevFS_FindDir(tVFS_Node *Node, const char *Name);
// === GLOBALS ===
tVFS_Driver gDevFS_Info = {
- "devfs", 0, DevFS_InitDevice, NULL, NULL
+ "devfs", 0, DevFS_InitDevice, DevFS_Unmount, NULL
};
tVFS_NodeType gDevFS_DirType = {
.TypeName = "DevFS-Dir",
return &gDevFS_RootNode;
}
+void DevFS_Unmount(tVFS_Node *RootNode)
+{
+
+}
+
/**
* \fn char *DevFS_ReadDir(tVFS_Node *Node, int Pos)
*/
// Create Root Node
root = &RootFS_Files[0];
-
+
+ root->Name[0] = '/';
+ root->Name[1] = '\0';
root->Node.ImplPtr = root;
root->Node.CTime
{
tRamFS_File *parent = Node->ImplPtr;
tRamFS_File *child;
- tRamFS_File *prev = (tRamFS_File *) &parent->Data.FirstChild;
+ tRamFS_File *prev = NULL;
ENTER("pNode sName xFlags", Node, Name, Flags);
- LOG("%i > %i", strlen(Name)+1, sizeof(child->Name));
+ LOG("Sanity check name length - %i > %i", strlen(Name)+1, sizeof(child->Name));
if(strlen(Name) + 1 > sizeof(child->Name))
LEAVE_RET('i', 0);
for( child = parent->Data.FirstChild; child; prev = child, child = child->Next )
{
if(strcmp(child->Name, Name) == 0) {
+ LOG("Duplicate");
LEAVE('i', 0);
return 0;
}
memset(child, 0, sizeof(tRamFS_File));
strcpy(child->Name, Name);
+ LOG("Name = '%s'", child->Name);
child->Parent = parent;
child->Next = NULL;
child->Node.Type = &gRootFS_FileType;
}
- prev->Next = child;
+ // Append!
+ if( prev )
+ prev->Next = child;
+ else
+ parent->Data.FirstChild = child;
parent->Node.Size ++;
tRamFS_File *child = parent->Data.FirstChild;
ENTER("pNode sName", Node, Name);
- //Log("Root_FindDir: (Node=%p, Name='%s')", Node, Name);
- for(;child;child = child->Next)
+ for( child = parent->Data.FirstChild; child; child = child->Next )
{
- //Log(" Root_FindDir: strcmp('%s', '%s')", child->Node.Name, Name);
LOG("child->Name = '%s'", child->Name);
- if(strcmp(child->Name, Name) == 0) {
+ if(strcmp(child->Name, Name) == 0)
+ {
LEAVE('p', &child->Node);
return &child->Node;
}
continue ;
if( h->Node->Type && h->Node->Type->Reference )
h->Node->Type->Reference( h->Node );
+ h->Mount->OpenHandleCount ++;
}
return ret;
continue ;
if( h->Node->Type && h->Node->Type->Reference )
h->Node->Type->Reference( h->Node );
+ h->Mount->OpenHandleCount ++;
}
}
continue ;
if( h->Node->Type && h->Node->Type->Close )
h->Node->Type->Close( h->Node );
+ h->Mount->OpenHandleCount --;
}
free( Handles );
}
/*
* Acess Micro - VFS Server version 1
*/
+#define DEBUG 1
#include <acess.h>
#include <vfs.h>
#include <vfs_int.h>
void VFS_UpdateMountFile(void);
// === GLOBALS ===
-tMutex glVFS_MountList;
+tRWLock glVFS_MountList;
tVFS_Mount *gVFS_Mounts;
tVFS_Mount *gVFS_RootMount = NULL;
Uint32 giVFS_NextMountIdent = 1;
*/
int VFS_Mount(const char *Device, const char *MountPoint, const char *Filesystem, const char *Options)
{
- tVFS_Mount *mnt;
+ tVFS_Mount *mnt, *parent_mnt;
tVFS_Driver *fs;
int deviceLen = strlen(Device);
int mountLen = strlen(MountPoint);
if(!mnt) {
return -2;
}
+
+ // Validate the mountpoint target
+ // - Only if / is mounted
+ if( gVFS_Mounts )
+ {
+ tVFS_Node *mpnode = VFS_ParsePath(MountPoint, NULL, &parent_mnt);
+ if( !mpnode ) {
+ Log_Warning("VFS", "VFS_Mount - Mountpoint '%s' does not exist", MountPoint);
+ return -1;
+ }
+ if( mpnode->Type->Close )
+ mpnode->Type->Close(mpnode);
+ if( parent_mnt->RootNode == mpnode ) {
+ Log_Warning("VFS", "VFS_Mount - Attempt to mount over '%s' (%s)",
+ MountPoint, parent_mnt->MountPoint);
+ return -1;
+ }
+ }
// HACK: Forces VFS_ParsePath to fall back on root
if(mountLen == 1 && MountPoint[0] == '/')
// Fill Structure
mnt->Filesystem = fs;
+ mnt->OpenHandleCount = 0;
mnt->Device = &mnt->StrData[0];
memcpy( mnt->Device, Device, deviceLen+1 );
if(str) *str = '\0';
} while( str );
args[nArg] = 0; // NULL terminal
-
+
// Initialise Volume
mnt->RootNode = fs->InitDevice(Device, (const char **)args);
if(!mnt->RootNode) {
free(mnt);
+ parent_mnt->OpenHandleCount --;
return -2;
}
if(!gVFS_RootMount) gVFS_RootMount = mnt;
// Add to mount list
- Mutex_Acquire( &glVFS_MountList );
+ RWLock_AcquireWrite( &glVFS_MountList );
{
- tVFS_Mount *tmp;
mnt->Next = NULL;
if(gVFS_Mounts) {
+ tVFS_Mount *tmp;
for( tmp = gVFS_Mounts; tmp->Next; tmp = tmp->Next );
tmp->Next = mnt;
}
gVFS_Mounts = mnt;
}
}
- Mutex_Release( &glVFS_MountList );
+ RWLock_Release( &glVFS_MountList );
Log_Log("VFS", "Mounted '%s' to '%s' ('%s')", Device, MountPoint, Filesystem);
return 0;
}
+int VFS_Unmount(const char *Mountpoint)
+{
+ tVFS_Mount *mount, *prev = NULL;
+ RWLock_AcquireWrite( &glVFS_MountList );
+ for( mount = gVFS_Mounts; mount; prev = mount, mount = mount->Next )
+ {
+ if( strcmp(Mountpoint, mount->MountPoint) == 0 ) {
+ if( mount->OpenHandleCount ) {
+ LOG("Mountpoint busy");
+ RWLock_Release(&glVFS_MountList);
+ return EBUSY;
+ }
+ if(prev)
+ prev->Next = mount->Next;
+ else
+ gVFS_Mounts = mount->Next;
+ break;
+ }
+ }
+ RWLock_Release( &glVFS_MountList );
+ if( !mount ) {
+ LOG("Mountpoint not found");
+ return ENOENT;
+ }
+
+ Log_Warning("VFS", "TODO: Impliment unmount");
+
+ // Decrease the open handle count for the mountpoint filesystem.
+ tVFS_Mount *mpmnt;
+ tVFS_Node *mpnode = VFS_ParsePath(mount->MountPoint, NULL, &mpmnt);
+ if(mpnode)
+ {
+ mpmnt->OpenHandleCount -= 2; // -1 for _ParsePath here, -1 for in _Mount
+ }
+
+ mount->Filesystem->Unmount( mount->RootNode );
+ free(mount);
+
+ return EOK;
+}
+
+int VFS_UnmountAll(void)
+{
+ int nUnmounted = 0;
+ tVFS_Mount *mount, *prev = NULL, *next;
+
+ RWLock_AcquireWrite( &glVFS_MountList );
+ // If we've unmounted the final filesystem, all good
+ if( gVFS_Mounts == NULL) {
+ RWLock_Release( &glVFS_MountList );
+ return -1;
+ }
+
+ for( mount = gVFS_Mounts; mount; prev = mount, mount = next )
+ {
+ next = mount->Next;
+ // Can't unmount stuff with open handles
+ if( mount->OpenHandleCount > 0 ) {
+ LOG("%p (%s) has open handles (%i of them)",
+ mount, mount->MountPoint, mount->OpenHandleCount);
+ continue;
+ }
+
+ if(prev)
+ prev->Next = mount->Next;
+ else
+ gVFS_Mounts = mount->Next;
+
+ if( mount->Filesystem->Unmount ) {
+ mount->Filesystem->Unmount( mount->RootNode );
+ }
+ else {
+ Log_Error("VFS", "%s (%s) does not have an unmount method, not calling it",
+ mount->MountPoint, mount->Filesystem->Name);
+ }
+ free(mount);
+ mount = prev;
+ nUnmounted ++;
+ }
+ RWLock_Release( &glVFS_MountList );
+
+ return nUnmounted;
+}
+
/**
* \brief Gets a mount point given the identifier
*/
tVFS_Mount *VFS_GetMountByIdent(Uint32 MountID)
{
tVFS_Mount *mnt;
+
+ RWLock_AcquireRead(&glVFS_MountList);
for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
{
if(mnt->Identifier == MountID)
- return mnt;
+ break;
}
- return NULL;
+ if(mnt)
+ mnt->OpenHandleCount ++;
+ RWLock_Release(&glVFS_MountList);
+ return mnt;
}
/**
// Format:
// <device>\t<location>\t<type>\t<options>\n
+ RWLock_AcquireRead( &glVFS_MountList );
for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
{
len += 4 + strlen(mnt->Device) + strlen(mnt->MountPoint)
+ strlen(mnt->Filesystem->Name) + strlen(mnt->Options);
}
+ RWLock_Release( &glVFS_MountList );
buf = malloc( len + 1 );
len = 0;
+ RWLock_AcquireRead( &glVFS_MountList );
for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
{
strcpy( &buf[len], mnt->Device );
len += strlen(mnt->Options);
buf[len++] = '\n';
}
+ RWLock_Release( &glVFS_MountList );
buf[len] = 0;
SysFS_UpdateFile( giVFS_MountFileID, buf, len );
/*
- * AcessMicro VFS
- * - File IO Passthru's
+ * Acess2 Kernel
+ * - By John Hodge (thePowersGang)
+ *
+ * vfs/nodecache.c
+ * - VFS Node Caching facility
*/
+#define DEBUG 0
#include <acess.h>
#include "vfs.h"
#include "vfs_int.h"
gVFS_InodeCache = ent;
SHORTREL( &glVFS_InodeCache );
- return gVFS_NextInodeHandle-1;
+ return ent->Handle;
}
/**
newEnt->Next = ent;
memcpy(&newEnt->Node, Node, sizeof(tVFS_Node));
prev->Next = newEnt;
-
+ newEnt->Node.ReferenceCount = 1;
+
+ LOG("Cached %llx as %p", Node->Inode, &newEnt->Node);
+
return &newEnt->Node;
}
* \fn void Inode_UncacheNode(int Handle, Uint64 Inode)
* \brief Dereferences/Removes a cached node
*/
-void Inode_UncacheNode(int Handle, Uint64 Inode)
+int Inode_UncacheNode(int Handle, Uint64 Inode)
{
tInodeCache *cache;
tCachedInode *ent, *prev;
cache = Inode_int_GetFSCache(Handle);
- if(!cache) return ;
-
- if(Inode > cache->MaxCached) return ;
+ if(!cache) {
+ Log_Notice("Inode", "Invalid cache handle %i used", Handle);
+ return -1;
+ }
+
+ ENTER("iHandle XInode", Handle, Inode);
+
+ if(Inode > cache->MaxCached) {
+ LEAVE('i', -1);
+ return -1;
+ }
// Search Cache
ent = cache->FirstNode;
for( ; ent; prev = ent, ent = ent->Next )
{
if(ent->Node.Inode < Inode) continue;
- if(ent->Node.Inode > Inode) return;
- ent->Node.ReferenceCount --;
- // Check if node needs to be freed
- if(ent->Node.ReferenceCount == 0)
+ if(ent->Node.Inode > Inode) {
+ LEAVE('i', -1);
+ return -1;
+ }
+ break;
+ }
+
+ LOG("ent = %p", ent);
+
+ if( !ent ) {
+ LEAVE('i', -1);
+ return -1;
+ }
+
+ ent->Node.ReferenceCount --;
+ // Check if node needs to be freed
+ if(ent->Node.ReferenceCount == 0)
+ {
+ prev->Next = ent->Next;
+ if(ent->Node.Inode == cache->MaxCached)
{
- prev->Next = ent->Next;
- if(ent->Node.Inode == cache->MaxCached)
- {
- if(ent != cache->FirstNode)
- cache->MaxCached = prev->Node.Inode;
- else
- cache->MaxCached = 0;
- }
-
- free(ent);
+ if(ent != cache->FirstNode)
+ cache->MaxCached = prev->Node.Inode;
+ else
+ cache->MaxCached = 0;
}
- return ;
+
+ if(ent->Node.Data)
+ free(ent->Node.Data);
+ free(ent);
+ LOG("Freed");
+ LEAVE('i', 1);
+ return 1;
+ }
+ else
+ {
+ LEAVE('i', 0);
+ return 0;
}
-
- return ;
}
/**
// Find Mountpoint
longestMount = gVFS_RootMount;
+ RWLock_AcquireRead( &glVFS_MountList );
for(mnt = gVFS_Mounts; mnt; mnt = mnt->Next)
{
// Quick Check
// Length Check - If the length is smaller than the longest match sofar
if(mnt->MountPointLen < longestMount->MountPointLen) continue;
// String Compare
- cmp = strcmp(Path, mnt->MountPoint);
+ cmp = strncmp(Path, mnt->MountPoint, mnt->MountPointLen);
+ // Not a match, continue
+ if(cmp != 0) continue;
#if OPEN_MOUNT_ROOT
// Fast Break - Request Mount Root
- if(cmp == 0) {
+ if(Path[mnt->MountPointLen] == '\0') {
if(TruePath) {
*TruePath = malloc( mnt->MountPointLen+1 );
strcpy(*TruePath, mnt->MountPoint);
}
if(MountPoint)
*MountPoint = mnt;
+ RWLock_Release( &glVFS_MountList );
+ LOG("Mount %p root", mnt);
LEAVE('p', mnt->RootNode);
return mnt->RootNode;
}
#endif
- // Not a match, continue
- if(cmp != '/') continue;
longestMount = mnt;
}
+ longestMount->OpenHandleCount ++; // Increment assuimg it worked
+ RWLock_Release( &glVFS_MountList );
// Save to shorter variable
mnt = longestMount;
// EVIL: Goto :)
LOG("Symlink -> '%s', restart", Path);
+ mnt->OpenHandleCount --; // Not in this mountpoint
goto restart_parse;
}
*MountPoint = mnt;
}
+ // Leave the mointpoint's count increased
+
LEAVE('p', tmpNode);
return tmpNode;
free(*TruePath);
*TruePath = NULL;
}
+ // Open failed, so decrement the open handle count
+ mnt->OpenHandleCount --;
+
LEAVE('n');
return NULL;
}
{
// TODO: Translate `Mode` into ACL and node flags
// Get parent, create node
- VFS_MkNod(absPath, 0);
+ if( VFS_MkNod(absPath, 0) ) {
+ free(absPath);
+ return -1;
+ }
node = VFS_ParsePath(absPath, NULL, &mnt);
}
LEAVE_RET('i', -1);
}
+ // Increment open handle count, no problems with the mount going away as `h` is already open on it
+ h->Mount->OpenHandleCount ++;
+
LEAVE_RET('x', VFS_int_CreateHandle(node, h->Mount, Mode));
}
#endif
_CloseNode(h->Node);
-
+
+ h->Mount->OpenHandleCount --;
+
h->Node = NULL;
}
\r
// === PROTOTYPES ===\r
int Ext2_Install(char **Arguments);\r
-void Ext2_Cleanup(void);\r
+ int Ext2_Cleanup(void);\r
// - Interface Functions\r
tVFS_Node *Ext2_InitDevice(const char *Device, const char **Options);\r
void Ext2_Unmount(tVFS_Node *Node);\r
/**\r
* \brief Clean up driver state before unload\r
*/\r
-void Ext2_Cleanup(void)\r
+int Ext2_Cleanup(void)\r
{\r
- \r
+ return 0;\r
}\r
\r
/**\r
#
#
-OBJ = fat.o
+OBJ = fat.o dir.o fatio.o nodecache.o
NAME = FAT
-include ../Makefile.tpl
--- /dev/null
+/*
+ * Acess2 FAT Filesystem Driver
+ * - By John Hodge (thePowersGang)
+ *
+ * common.h
+ * - FAT internal common header
+ */
+#ifndef _FS__FAT__COMMON_H_
+#define _FS__FAT__COMMON_H_
+
+#include "fs_fat.h"
+#include <vfs.h>
+
+#define CACHE_FAT 0 //!< Caches the FAT in memory
+#define USE_LFN 1 //!< Enables the use of Long File Names
+#define SUPPORT_WRITE 1 //!< Enables write support
+
+#define FAT_FLAG_DIRTY 0x10000
+#define FAT_FLAG_DELETE 0x20000
+
+typedef struct sFAT_VolInfo tFAT_VolInfo;
+#if USE_LFN
+typedef struct sFAT_LFNCacheEnt tFAT_LFNCacheEnt;
+typedef struct sFAT_LFNCache tFAT_LFNCache;
+#endif
+typedef struct sFAT_CachedNode tFAT_CachedNode;
+
+/**
+ * \brief Internal IDs for FAT types
+ */
+enum eFatType
+{
+ FAT12, //!< FAT12 Volume
+ FAT16, //!< FAT16 Volume
+ FAT32, //!< FAT32 Volume
+};
+
+// === TYPES ===
+struct sFAT_VolInfo
+{
+ int fileHandle; //!< File Handle
+ enum eFatType type; //!< FAT Variant
+ char name[12]; //!< Volume Name (With NULL Terminator)
+ Uint32 firstDataSect; //!< First data sector
+ Uint32 rootOffset; //!< Root Offset (clusters)
+ Uint32 ClusterCount; //!< Total Cluster Count
+ fat_bootsect bootsect; //!< Boot Sector
+ tVFS_Node rootNode; //!< Root Node
+ int BytesPerCluster;
+
+ tMutex lNodeCache;
+ tFAT_CachedNode *NodeCache;
+
+ tMutex lFAT; //!< Lock to prevent double-writing to the FAT
+ #if CACHE_FAT
+ Uint32 *FATCache; //!< FAT Cache
+ #endif
+};
+
+#if USE_LFN
+/**
+ * \brief Long-Filename cache entry
+ */
+struct sFAT_LFNCacheEnt
+{
+ int ID;
+ Uint16 Data[256];
+};
+/**
+ * \brief Long-Filename cache
+ */
+struct sFAT_LFNCache
+{
+ int NumEntries;
+ tFAT_LFNCacheEnt Entries[];
+};
+#endif
+
+struct sFAT_CachedNode
+{
+ struct sFAT_CachedNode *Next;
+ tVFS_Node Node;
+};
+
+// --- General Helpers ---
+extern int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster);
+extern tTime FAT_int_GetAcessTimestamp(Uint16 Date, Uint16 Time, Uint8 MS);
+extern void FAT_int_GetFATTimestamp(tTime AcessTimestamp, Uint16 *Date, Uint16 *Time, Uint8 *MS);
+
+// --- Node Caching ---
+// NOTE: FAT uses its own node cache that references by cluster (not the inode value that the Inode_* cache uses)
+// because tVFS_Node.Inode contains the parent directory inode
+extern tVFS_Node *FAT_int_CreateNode(tVFS_Node *Parent, fat_filetable *Entry);
+extern tVFS_Node *FAT_int_CreateIncompleteDirNode(tFAT_VolInfo *Disk, Uint32 Cluster);
+extern tVFS_Node *FAT_int_GetNode(tFAT_VolInfo *Disk, Uint32 Cluster);
+extern int FAT_int_DerefNode(tVFS_Node *Node);
+extern void FAT_int_ClearNodeCache(tFAT_VolInfo *Disk);
+
+// --- FAT Access ---
+extern Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 Cluster);
+#if SUPPORT_WRITE
+extern Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous);
+extern Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster);
+#endif
+extern void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer);
+extern void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, const void *Buffer);
+
+// --- Directory Access ---
+extern char *FAT_ReadDir(tVFS_Node *Node, int ID);
+extern tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name);
+extern tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode);
+extern int FAT_int_GetEntryByCluster(tVFS_Node *DirNode, Uint32 Cluster, fat_filetable *Entry);
+#if SUPPORT_WRITE
+extern int FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry);
+extern int FAT_Mknod(tVFS_Node *Node, const char *Name, Uint Flags);
+extern int FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *Node);
+extern int FAT_Unlink(tVFS_Node *DirNode, const char *OldName);
+#endif
+extern void FAT_CloseFile(tVFS_Node *node);
+
+// === GLOBALS ===
+extern tVFS_NodeType gFAT_DirType;
+extern tVFS_NodeType gFAT_FileType;
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 FAT12/16/32 Driver
+ * - By John Hodge (thePowersGang)
+ *
+ * dir.c
+ * - Directory access/manipulation code
+ */
+#define DEBUG 1
+#include <acess.h>
+#include <vfs.h>
+#include "common.h"
+
+// === PROTOTYPES ===
+void FAT_int_ProperFilename(char *dest, const char *src);
+char *FAT_int_CreateName(fat_filetable *ft, const Uint16 *LongFileName);
+ int FAT_int_ConvertUTF16_to_UTF8(Uint8 *Dest, const Uint16 *Source);
+ int FAT_int_ConvertUTF8_to_UTF16(Uint16 *Dest, const Uint8 *Source);
+
+ int FAT_int_GetEntryByName(tVFS_Node *DirNode, const char *Name, fat_filetable *Entry);
+ int FAT_int_GetEntryByCluster(tVFS_Node *DirNode, Uint32 Cluster, fat_filetable *Entry);
+ int FAT_int_ReadDirSector(tVFS_Node *Node, int Sector, fat_filetable *Buffer);
+#if SUPPORT_WRITE
+ int FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry);
+#endif
+#if USE_LFN
+Uint16 *FAT_int_GetLFN(tVFS_Node *Node, int ID);
+void FAT_int_DelLFN(tVFS_Node *Node, int ID);
+#endif
+char *FAT_ReadDir(tVFS_Node *Node, int ID);
+tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name);
+tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode);
+#if SUPPORT_WRITE
+ int FAT_Mknod(tVFS_Node *Node, const char *Name, Uint Flags);
+ int FAT_int_IsValid83Filename(const char *Name);
+ int FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *Node);
+ int FAT_Relink(tVFS_Node *node, const char *OldName, const char *NewName);
+#endif
+
+// === CODE ===
+
+/**
+ * \brief Converts a FAT directory entry name into a proper filename
+ * \param dest Destination array (must be at least 13 bytes in size)
+ * \param src 8.3 filename (concatenated, e.g 'FILE1 TXT')
+ */
+void FAT_int_ProperFilename(char *dest, const char *src)
+{
+ int inpos, outpos;
+
+ // Name
+ outpos = 0;
+ for( inpos = 0; inpos < 8; inpos++ ) {
+ if(src[inpos] == ' ') break;
+ dest[outpos++] = src[inpos];
+ }
+ inpos = 8;
+ // Check for empty extensions
+ if(src[8] != ' ')
+ {
+ dest[outpos++] = '.';
+ for( ; inpos < 11; inpos++) {
+ if(src[inpos] == ' ') break;
+ dest[outpos++] = src[inpos];
+ }
+ }
+ dest[outpos++] = '\0';
+
+ //LOG("dest='%s'", dest);
+}
+
+/**
+ * \fn char *FAT_int_CreateName(fat_filetable *ft, Uint8 *LongFileName)
+ * \brief Converts either a LFN or a 8.3 Name into a proper name
+ * \param ft Pointer to the file's entry in the parent directory
+ * \param LongFileName Long file name pointer
+ * \return Filename as a heap string
+ */
+char *FAT_int_CreateName(fat_filetable *ft, const Uint16 *LongFileName)
+{
+ char *ret;
+ ENTER("pft sLongFileName", ft, LongFileName);
+ //Log_Debug("FAT", "FAT_int_CreateName(ft=%p, LongFileName=%p'%s')", ft, LongFileName);
+ #if USE_LFN
+ if(LongFileName && LongFileName[0] != 0)
+ {
+ int len = FAT_int_ConvertUTF16_to_UTF8(NULL, LongFileName);
+ ret = malloc( len + 1 );
+ FAT_int_ConvertUTF16_to_UTF8((Uint8*)ret, LongFileName);
+ }
+ else
+ {
+ #endif
+ ret = (char*) malloc(13);
+ if( !ret ) {
+ Log_Warning("FAT", "FAT_int_CreateName: malloc(13) failed");
+ return NULL;
+ }
+ FAT_int_ProperFilename(ret, ft->name);
+ #if USE_LFN
+ }
+ #endif
+ LEAVE('s', ret);
+ return ret;
+}
+
+#if USE_LFN
+int FAT_int_CompareUTF16_UTF8(const Uint16 *Str16, const char *Str8)
+{
+ int pos16 = 0, pos8 = 0;
+ const Uint8 *str8 = (const Uint8 *)Str8;
+
+ while( Str16[pos16] && str8[pos8] )
+ {
+ Uint32 cp8, cp16;
+ if( Str16[pos16] & 0x8000 ) {
+ // Do something!
+ cp16 = 0;
+ }
+ else {
+ cp16 = Str16[pos16];
+ pos16 ++;
+ }
+ pos8 += ReadUTF8(str8 + pos8, &cp8);
+
+ if(cp16 == cp8) continue ;
+
+ if(cp16 < cp8)
+ return -1;
+ else
+ return 1;
+ }
+ if(Str16[pos16] == str8[pos8])
+ return 0;
+ if(Str16[pos16] < str8[pos8])
+ return -1;
+ else
+ return 1;
+}
+
+int FAT_int_ConvertUTF16_to_UTF8(Uint8 *Dest, const Uint16 *Source)
+{
+ int len = 0;
+ for( ; *Source; Source ++ )
+ {
+ // TODO: Decode/Reencode
+ if( Dest )
+ Dest[len] = *Source;
+ len += 1;
+ }
+ if( Dest )
+ Dest[len] = 0;
+ return len;
+}
+
+int FAT_int_ConvertUTF8_to_UTF16(Uint16 *Dest, const Uint8 *Source)
+{
+ int len = 0;
+ while( *Source )
+ {
+ Uint32 cp;
+ int cpl;
+
+ cpl = ReadUTF8(Source, &cp);
+ if(cp < 0x8000) {
+ if( Dest )
+ Dest[len] = cp;
+ len ++;
+ }
+ else {
+ // TODO!
+ }
+ Source += cpl;
+ }
+ Dest[len] = 0;
+ return len;
+}
+
+int FAT_int_ParseLFN(const fat_filetable *Entry, Uint16 *Buffer)
+{
+ const fat_longfilename *lfnInfo;
+ int ofs;
+
+ lfnInfo = (const void*)Entry;
+
+ if(lfnInfo->id & 0x40) {
+ memset(Buffer, 0, 256*2);
+ }
+ ofs = (lfnInfo->id & 0x3F) * 13 - 1;
+ if( ofs >= 255 )
+ return -1;
+
+ Buffer[ofs--] = lfnInfo->name3[1]; Buffer[ofs--] = lfnInfo->name3[0];
+ Buffer[ofs--] = lfnInfo->name2[5]; Buffer[ofs--] = lfnInfo->name2[4];
+ Buffer[ofs--] = lfnInfo->name2[3]; Buffer[ofs--] = lfnInfo->name2[2];
+ Buffer[ofs--] = lfnInfo->name2[1]; Buffer[ofs--] = lfnInfo->name2[0];
+ Buffer[ofs--] = lfnInfo->name1[4]; Buffer[ofs--] = lfnInfo->name1[3];
+ Buffer[ofs--] = lfnInfo->name1[2]; Buffer[ofs--] = lfnInfo->name1[1];
+ Buffer[ofs--] = lfnInfo->name1[0];
+
+ if((lfnInfo->id&0x3F) == 1)
+ return 1;
+ return 0;
+}
+#endif
+
+int FAT_int_GetEntryByName(tVFS_Node *DirNode, const char *Name, fat_filetable *Entry)
+{
+ fat_filetable fileinfo[16];
+ char tmpName[13];
+ #if USE_LFN
+ Uint16 lfn[256];
+ int lfnId = -1;
+ #endif
+
+ ENTER("pDirNode sName pEntry", DirNode, Name, Entry);
+
+ for( int i = 0; ; i++ )
+ {
+ if((i & 0xF) == 0) {
+ if(FAT_int_ReadDirSector(DirNode, i/16, fileinfo))
+ {
+ LEAVE('i', -1);
+ return -1;
+ }
+ }
+
+ //Check if the files are free
+ if(fileinfo[i&0xF].name[0] == '\0') break; // End of List marker
+ if(fileinfo[i&0xF].name[0] == '\xE5') continue; // Free entry
+
+
+ #if USE_LFN
+ // Long File Name Entry
+ if(fileinfo[i & 0xF].attrib == ATTR_LFN)
+ {
+ if( FAT_int_ParseLFN(&fileinfo[i&0xF], lfn) )
+ lfnId = i+1;
+ continue ;
+ }
+ // Remove LFN if it does not apply
+ if(lfnId != i) lfn[0] = 0;
+ #else
+ if(fileinfo[i&0xF].attrib == ATTR_LFN) continue;
+ #endif
+
+ // Get Real Filename
+ FAT_int_ProperFilename(tmpName, fileinfo[i&0xF].name);
+// LOG("tmpName = '%s'", tmpName);
+// #if DEBUG
+// Debug_HexDump("FAT tmpName", tmpName, strlen(tmpName));
+// #endif
+/*
+ #if DEBUG && USE_LFN
+ if(lfnId == i)
+ {
+ Uint8 lfntmp[256*3+1];
+ FAT_int_ConvertUTF16_to_UTF8(lfntmp, lfn);
+ LOG("lfntmp = '%s'", lfntmp);
+ }
+ #endif
+*/
+
+ // Only the long name is case sensitive, 8.3 is not
+ #if USE_LFN
+ if(strucmp(tmpName, Name) == 0 || FAT_int_CompareUTF16_UTF8(lfn, Name) == 0)
+ #else
+ if(strucmp(tmpName, Name) == 0)
+ #endif
+ {
+ memcpy(Entry, fileinfo + (i&0xF), sizeof(*Entry));
+ LOG("Found %s at %i", Name, i);
+ LEAVE('i', i);
+ return i;
+ }
+ }
+
+ LEAVE('i', -1);
+ return -1;
+}
+
+int FAT_int_GetEntryByCluster(tVFS_Node *DirNode, Uint32 Cluster, fat_filetable *Entry)
+{
+ int ents_per_sector = 512 / sizeof(fat_filetable);
+ fat_filetable fileinfo[ents_per_sector];
+ int i, sector;
+
+ Mutex_Acquire(&DirNode->Lock);
+ sector = 0;
+ for( i = 0; ; i ++ )
+ {
+ if( i == 0 || i == ents_per_sector )
+ {
+ if(FAT_int_ReadDirSector(DirNode, sector, fileinfo))
+ {
+ LOG("ReadDirSector failed");
+ break ;
+ }
+ i = 0;
+ sector ++;
+ }
+
+ // Check for free/end of list
+ if(fileinfo[i].name[0] == '\0') break; // End of List marker
+ if(fileinfo[i].name[0] == '\xE5') continue; // Free entry
+
+ if(fileinfo[i].attrib == ATTR_LFN) continue;
+
+ LOG("fileinfo[i].cluster = %x:%04x", fileinfo[i].clusterHi, fileinfo[i].cluster);
+ #if DEBUG
+ {
+ char tmpName[13];
+ FAT_int_ProperFilename(tmpName, fileinfo[i].name);
+ LOG("tmpName = '%s'", tmpName);
+ }
+ #endif
+
+
+ if(fileinfo[i].cluster != (Cluster & 0xFFFF)) continue;
+ if(fileinfo[i].clusterHi != ((Cluster >> 16) & 0xFFFF)) continue;
+
+ memcpy(Entry, &fileinfo[i], sizeof(*Entry));
+ Mutex_Release(&DirNode->Lock);
+ return i;
+ }
+
+ Mutex_Release(&DirNode->Lock);
+ return -1;
+}
+
+/*
+ * ====================
+ * Directory IO
+ * ====================
+ */
+
+/**
+ * \brief Reads a sector from the disk
+ * \param Node Directory node to read
+ * \param Sector Sector number in the directory to read
+ * \param Buffer Destination buffer for the read data
+ */
+int FAT_int_ReadDirSector(tVFS_Node *Node, int Sector, fat_filetable *Buffer)
+{
+ Uint64 addr;
+ tFAT_VolInfo *disk = Node->ImplPtr;
+
+ ENTER("pNode iSector pEntry", Node, Sector, Buffer);
+
+ // Parse address
+ if(FAT_int_GetAddress(Node, Sector * 512, &addr, NULL))
+ {
+ LEAVE('i', 1);
+ return 1;
+ }
+
+ LOG("addr = 0x%llx", addr);
+ // Read Sector
+ if(VFS_ReadAt(disk->fileHandle, addr, 512, Buffer) != 512)
+ {
+ LEAVE('i', 1);
+ return 1;
+ }
+
+ LEAVE('i', 0);
+ return 0;
+}
+
+#if SUPPORT_WRITE
+/**
+ * \brief Write a sector to the disk
+ * \param Node Directory node to write
+ * \param Sector Sector number in the directory to write
+ * \param Buffer Source data
+ */
+int FAT_int_WriteDirSector(tVFS_Node *Node, int Sector, const fat_filetable *Buffer)
+{
+ Uint64 addr;
+ tFAT_VolInfo *disk = Node->ImplPtr;
+
+ ENTER("pNode iSector pEntry", Node, Sector, Buffer);
+
+ // Parse address
+ if(FAT_int_GetAddress(Node, Sector * 512, &addr, NULL))
+ {
+ LEAVE('i', 1);
+ return 1;
+ }
+
+ // Read Sector
+ if(VFS_WriteAt(disk->fileHandle, addr, 512, Buffer) != 512)
+ {
+ LEAVE('i', 1);
+ return 1;
+ }
+
+ LEAVE('i', 0);
+ return 0;
+}
+
+/**
+ * \brief Writes an entry to the disk
+ * \todo Support expanding a directory
+ * \param Node Directory node
+ * \param ID ID of entry to update
+ * \param Entry Entry data
+ * \return Zero on success, non-zero on error
+ */
+int FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry)
+{
+ Uint64 addr = 0;
+ int tmp;
+ Uint32 cluster = 0;
+ tFAT_VolInfo *disk = Node->ImplPtr;
+
+ ENTER("pNode iID pEntry", Node, ID, Entry);
+
+ tmp = FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);
+ if( tmp )
+ {
+ //TODO: Allocate a cluster
+ cluster = FAT_int_AllocateCluster(Node->ImplPtr, cluster);
+ if(cluster == -1) {
+ Log_Warning("FAT", "Unable to allocate an other cluster for %p", Node);
+ LEAVE('i', 1);
+ return 1;
+ }
+ FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);
+ }
+
+
+ LOG("addr = 0x%llx", addr);
+
+ // Wriet data to disk
+ VFS_WriteAt(disk->fileHandle, addr, sizeof(fat_filetable), Entry);
+
+ LEAVE('i', 0);
+ return 0;
+}
+#endif
+
+#if USE_LFN
+/**
+ * \fn Uint16 *FAT_int_GetLFN(tVFS_Node *node)
+ * \brief Return pointer to LFN cache entry
+ * \param Node Directory node
+ * \param ID ID of the short name
+ * \return Pointer to the LFN cache entry
+ */
+Uint16 *FAT_int_GetLFN(tVFS_Node *Node, int ID)
+{
+ tFAT_LFNCache *cache;
+ int i, firstFree;
+
+ Mutex_Acquire( &Node->Lock );
+
+ // TODO: Thread Safety (Lock things)
+ cache = Node->Data;
+
+ // Create a cache if it isn't there
+ if(!cache) {
+ cache = Node->Data = malloc( sizeof(tFAT_LFNCache) + sizeof(tFAT_LFNCacheEnt) );
+ cache->NumEntries = 1;
+ cache->Entries[0].ID = ID;
+ cache->Entries[0].Data[0] = 0;
+ Mutex_Release( &Node->Lock );
+ //Log_Debug("FAT", "Return = %p (new)", cache->Entries[0].Data);
+ return cache->Entries[0].Data;
+ }
+
+ // Scan for this entry
+ firstFree = -1;
+ for( i = 0; i < cache->NumEntries; i++ )
+ {
+ if( cache->Entries[i].ID == ID ) {
+ Mutex_Release( &Node->Lock );
+ //Log_Debug("FAT", "Return = %p (match)", cache->Entries[i].Data);
+ return cache->Entries[i].Data;
+ }
+ if( cache->Entries[i].ID == -1 && firstFree == -1 )
+ firstFree = i;
+ }
+
+ if(firstFree == -1) {
+ // Use `i` for temp length
+ i = sizeof(tFAT_LFNCache) + (cache->NumEntries+1)*sizeof(tFAT_LFNCacheEnt);
+ Node->Data = realloc( Node->Data, i );
+ if( !Node->Data ) {
+ Log_Error("FAT", "realloc() fail, unable to allocate %i for LFN cache", i);
+ Mutex_Release( &Node->Lock );
+ return NULL;
+ }
+ //Log_Debug("FAT", "Realloc (%i)\n", i);
+ cache = Node->Data;
+ i = cache->NumEntries;
+ cache->NumEntries ++;
+ }
+ else {
+ i = firstFree;
+ }
+
+ // Create new entry
+ cache->Entries[ i ].ID = ID;
+ cache->Entries[ i ].Data[0] = '\0';
+
+ Mutex_Release( &Node->Lock );
+ //Log_Debug("FAT", "Return = %p (firstFree, i = %i)", cache->Entries[i].Data, i);
+ return cache->Entries[ i ].Data;
+}
+
+/**
+ * \fn void FAT_int_DelLFN(tVFS_Node *node)
+ * \brief Delete a LFN cache entry
+ * \param Node Directory node
+ * \param ID File Entry ID
+ */
+void FAT_int_DelLFN(tVFS_Node *Node, int ID)
+{
+ tFAT_LFNCache *cache = Node->Data;
+ int i;
+
+ // Fast return
+ if(!cache) return;
+
+ // Scan for a current entry
+ for( i = 0; i < cache->NumEntries; i++ )
+ {
+ if( cache->Entries[i].ID == ID )
+ cache->Entries[i].ID = -1;
+ }
+ return ;
+}
+#endif
+
+/**
+ * \fn char *FAT_ReadDir(tVFS_Node *Node, int ID)
+ * \param Node Node structure of directory
+ * \param ID Directory position
+ * \return Filename as a heap string, NULL or VFS_SKIP
+ */
+char *FAT_ReadDir(tVFS_Node *Node, int ID)
+{
+ fat_filetable fileinfo[16]; // sizeof(fat_filetable)=32, so 16 per sector
+ int a;
+ char *ret;
+ #if USE_LFN
+ Uint16 *lfn = NULL;
+ #endif
+
+ ENTER("pNode iID", Node, ID);
+
+ if(FAT_int_ReadDirSector(Node, ID/16, fileinfo))
+ {
+ LOG("End of chain, end of dir");
+ LEAVE('n');
+ return NULL;
+ }
+
+ // Offset in sector
+ a = ID % 16;
+
+ LOG("fileinfo[%i].name[0] = 0x%x", a, (Uint8)fileinfo[a].name[0]);
+
+ // Check if this is the last entry
+ if( fileinfo[a].name[0] == '\0' ) {
+ Node->Size = ID;
+ LOG("End of list");
+ LEAVE('n');
+ return NULL; // break
+ }
+
+ // Check for empty entry
+ if( (Uint8)fileinfo[a].name[0] == 0xE5 ) {
+ LOG("Empty Entry");
+ #if 0 // Stop on empty entry?
+ LEAVE('n');
+ return NULL; // Stop
+ #else
+ LEAVE('p', VFS_SKIP);
+ return VFS_SKIP; // Skip
+ #endif
+ }
+
+ #if USE_LFN
+ // Get Long File Name Cache
+ if(fileinfo[a].attrib == ATTR_LFN)
+ {
+ fat_longfilename *lfnInfo;
+
+ lfnInfo = (fat_longfilename *) &fileinfo[a];
+
+ // Get cache for corresponding file
+ // > ID + Index gets the corresponding short node
+ lfn = FAT_int_GetLFN( Node, ID + (lfnInfo->id & 0x3F) );
+
+ a = FAT_int_ParseLFN(&fileinfo[a], lfn);
+ if( a < 0 ) {
+ LOG("Invalid LFN, error");
+ LEAVE('n');
+ return NULL;
+ }
+
+// LOG("lfn = '%s'", lfn);
+ //Log_Debug("FAT", "lfn = '%s'", lfn);
+ LEAVE('p', VFS_SKIP);
+ return VFS_SKIP;
+ }
+ #endif
+
+ // Check if it is a volume entry
+ if(fileinfo[a].attrib & 0x08) {
+ LEAVE('p', VFS_SKIP);
+ return VFS_SKIP;
+ }
+ // Ignore .
+ if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == ' ') {
+ LEAVE('p', VFS_SKIP);
+ return VFS_SKIP;
+ }
+ // and ..
+ if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == '.' && fileinfo[a].name[2] == ' ') {
+ LEAVE('p', VFS_SKIP);
+ return VFS_SKIP;
+ }
+
+ LOG("name='%c%c%c%c%c%c%c%c.%c%c%c'",
+ fileinfo[a].name[0], fileinfo[a].name[1], fileinfo[a].name[2], fileinfo[a].name[3],
+ fileinfo[a].name[4], fileinfo[a].name[5], fileinfo[a].name[6], fileinfo[a].name[7],
+ fileinfo[a].name[8], fileinfo[a].name[9], fileinfo[a].name[10] );
+
+ #if USE_LFN
+ lfn = FAT_int_GetLFN(Node, ID);
+ //Log_Debug("FAT", "lfn = %p'%s'", lfn, lfn);
+ ret = FAT_int_CreateName(&fileinfo[a], lfn);
+ #else
+ ret = FAT_int_CreateName(&fileinfo[a], NULL);
+ #endif
+
+ LEAVE('s', ret);
+ return ret;
+}
+
+/**
+ * \fn tVFS_Node *FAT_FindDir(tVFS_Node *node, char *name)
+ * \brief Finds an entry in the current directory
+ */
+tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name)
+{
+ fat_filetable fileent;
+
+ ENTER("pNode sname", Node, Name);
+
+ // Fast Returns
+ if(!Name || Name[0] == '\0') {
+ LEAVE('n');
+ return NULL;
+ }
+
+ if( FAT_int_GetEntryByName(Node, Name, &fileent) == -1 ) {
+ LEAVE('n');
+ return NULL;
+ }
+
+
+ tVFS_Node *ret = FAT_int_CreateNode(Node, &fileent);
+ LOG("Found %s as %p", Name, ret);
+ LEAVE('p', ret);
+ return ret;
+}
+
+tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode)
+{
+ tFAT_VolInfo *disk = Root->ImplPtr;
+ tVFS_Node *dirnode, *ret;
+ fat_filetable ft;
+
+ ENTER("pRoot XInode", Root, Inode);
+
+ ret = FAT_int_GetNode(disk, Inode & 0xFFFFFFFF);
+ if( ret ) {
+ if( (ret->Inode >> 32) != 0 ) {
+ LOG("Node in cache, quick return");
+ return ret;
+ }
+ else {
+ LOG("Node cached, but incomplete");
+ // Fall on through
+ }
+ ret = NULL;
+ }
+
+ dirnode = FAT_int_CreateIncompleteDirNode(disk, Inode >> 32);
+
+ int id = FAT_int_GetEntryByCluster(dirnode, Inode & 0xFFFFFFFF, &ft);
+ if( id != -1 ) {
+ ret = FAT_int_CreateNode(dirnode, &ft);
+ }
+
+ dirnode->Type->Close(dirnode);
+
+ LEAVE('p', ret);
+ return ret;
+}
+
+#if SUPPORT_WRITE
+/**
+ * \brief Create a new node
+ */
+int FAT_Mknod(tVFS_Node *DirNode, const char *Name, Uint Flags)
+{
+ tFAT_VolInfo *disk = DirNode->ImplPtr;
+ int rv;
+ fat_filetable ft;
+ memset(&ft, 0, sizeof(ft));
+
+ ENTER("pDirNode sName xFlags", DirNode, Name, Flags);
+
+ // Allocate a cluster
+ Uint32 cluster = FAT_int_AllocateCluster(disk, -1);
+ LOG("Cluster 0x%07x allocated", cluster);
+
+ // Create a temporary file table entry for an empty node
+ ft.cluster = cluster & 0xFFFF;
+ ft.clusterHi = cluster >> 16;
+ ft.size = 0;
+ if( Flags & VFS_FFLAG_DIRECTORY )
+ ft.attrib = ATTR_DIRECTORY;
+ else
+ ft.attrib = 0;
+
+ tVFS_Node *newnode = FAT_int_CreateNode(DirNode, &ft);
+ if( !newnode ) {
+ return -1;
+ }
+ LOG("newnode = %p", newnode);
+
+ // Call link
+ if( (rv = FAT_Link(DirNode, Name, newnode)) ) {
+ newnode->ImplInt |= FAT_FLAG_DELETE;
+ }
+ FAT_CloseFile(newnode);
+ LEAVE('i', rv);
+ return rv;
+}
+
+/**
+ * \brief Internal - Checks if a character is valid in an 8.3 filename
+ */
+static inline int is_valid_83_char(char ch)
+{
+ if( '0' <= ch && ch <= '9' )
+ return 1;
+ if( 'A' <= ch && ch <= 'Z' )
+ return 1;
+ if( 'a' <= ch && ch <= 'z' )
+ return 1;
+ if( strchr("$%'-_@~`#!(){}^#&", ch) )
+ return 1;
+ if( ch > 128 )
+ return 1;
+ return 0;
+}
+
+Uint8 FAT_int_UnicodeTo83(Uint32 Input)
+{
+ Input = toupper(Input);
+ // Input = unicode_to_oem(Input);
+ if( Input > 256 )
+ Input = '_';
+ if(!is_valid_83_char(Input))
+ Input = '_';
+ return Input;
+}
+
+/**
+ * \brief Internal - Determines if a filename is a valid 8.3 filename
+ */
+int FAT_int_IsValid83Filename(const char *Name)
+{
+ int i, j;
+
+ if( !Name[0] || Name[0] == '.' )
+ return 0;
+
+ // Check filename portion
+ for( i = 0; Name[i] && i < 8; i ++ )
+ {
+ if( Name[i] == '.' )
+ break;
+ if( !is_valid_83_char(Name[i]) )
+ return 0;
+ }
+ // If the next char is not \0 or '.', it's not valid
+ if( Name[i] && Name[i++] != '.' )
+ return 0;
+
+ // Check the extension portion
+ for( j = 0; Name[i+j] && j < 3; j ++ )
+ {
+ if( !is_valid_83_char(Name[i+j]) )
+ return 0;
+ }
+
+ // After the extension must be the end
+ if( Name[i+j] )
+ return 0;
+
+ return 1;
+}
+
+Uint8 FAT_int_MakeLFNChecksum(const char *ShortName)
+{
+ Uint8 ret = 0;
+ for( int i = 0; i < 11; i++ )
+ {
+ // ret = (ret >>> 1) + ShortName[i]
+ // where >>> is rotate right
+ ret = ((ret & 1) ? 0x80 : 0x00) + (ret >> 1) + ShortName[i];
+ }
+ return ret;
+}
+
+/**
+ * \brief Create a new name for a file
+ * \note Since FAT doesn't support reference counting, this will cause double-references if
+ * a file is hardlinked and not unlinked
+ */
+int FAT_Link(tVFS_Node *DirNode, const char *NewName, tVFS_Node *NewNode)
+{
+ Uint16 lfn[256];
+ fat_filetable ft;
+ int nLFNEnt = 0;
+ const int eps = 512 / sizeof(fat_filetable);
+ fat_filetable fileinfo[eps];
+
+ Mutex_Acquire( &DirNode->Lock );
+
+ // -- Ensure duplicates aren't created --
+ if( FAT_int_GetEntryByName(DirNode, NewName, &ft) >= 0 ) {
+ Mutex_Release( &DirNode->Lock );
+ return EEXIST;
+ }
+
+ // -- Create filetable entry --
+ #if 0
+ {
+ int bDirty = 0;
+ int inofs = 0;
+ while( NewName[inofs] && NewName[inofs] == '.' )
+ inofs ++, bDirty = 1;
+ for( int i = 0; i < 8 && NewName[inofs] && NewName[inofs] != '.'; i ++ )
+ {
+ Uint32 cp;
+ inofs += ReadUTF8(NewName + inofs, &cp);
+ // Spaces are silently skipped
+ if(isspace(cp)) {
+ i --, bDirty = 1;
+ continue ;
+ }
+ ft.name[i] = FAT_int_UnicodeTo83(cp);
+ if(ft.name[i] != cp)
+ bDirty = 1;
+ }
+ while( NewName[inofs] && NewName[inofs] != '.' )
+ inofs ++, bDirty = 1;
+ for( ; i < 8+3 && NewName[inofs]; i ++ )
+ {
+ Uint32 cp;
+ inofs += ReadUTF8(NewName + inofs, &cp);
+ // Spaces are silently skipped
+ if(isspace(cp)) {
+ i --, bDirty = 1;
+ continue ;
+ }
+ ft.name[i] = FAT_int_UnicodeTo83(cp);
+ if(ft.name[i] != cp)
+ bDirty = 1;
+ }
+ if( !NewName[inofs] ) bDirty = 1;
+
+ if( bDirty )
+ {
+ int lfnlen = FAT_int_ConvertUTF8_to_UTF16(lfn, (const Uint8*)NewName);
+ lfn[lfnlen] = 0;
+ nLFNEnt = DivUp(lfnlen, 13);
+ }
+ }
+ #endif
+ int bNeedsLFN = !FAT_int_IsValid83Filename(NewName);
+ if( bNeedsLFN )
+ {
+ int lfnlen = FAT_int_ConvertUTF8_to_UTF16(lfn, (const Uint8*)NewName);
+ lfn[lfnlen] = 0;
+ nLFNEnt = DivUp(lfnlen, 13);
+
+ // Create a base mangled filetable entry
+ int i, j = 0;
+ while(NewName[j] == '.') j ++; // Eat leading dots
+ for( i = 0; i < 6 && NewName[j] && NewName[j] != '.'; i ++, j ++ )
+ {
+ if( !isalpha(NewName[j]) && !is_valid_83_char(NewName[j]) )
+ ft.name[i] = '_';
+ else
+ ft.name[i] = toupper(NewName[j]);
+ }
+ ft.name[i++] = '~';
+ ft.name[i++] = '1';
+ while(i < 8) ft.name[i++] = ' ';
+ while(NewName[j] && NewName[j] != '.') j ++;
+ for( ; i < 8+3 && NewName[j]; i ++, j ++ )
+ {
+ if( NewName[j] == '.' )
+ i --;
+ else if( !is_valid_83_char(NewName[j]) )
+ ft.name[i] = '_';
+ else
+ ft.name[i] = toupper(NewName[j]);
+ }
+ while(i < 8+3) ft.name[i++] = ' ';
+
+ // - Ensure there isn't a duplicate short-name
+ int bIsDuplicate = 1;
+ while( bIsDuplicate )
+ {
+ bIsDuplicate = 0; // Assume none
+
+ // Scan directory
+ for( int id = 0; ; id ++ )
+ {
+ if( id % eps == 0 )
+ {
+ if(FAT_int_ReadDirSector(DirNode, id/eps, fileinfo))
+ break; // end of cluster chain
+ }
+
+ // End of file list
+ if( fileinfo[id%eps].name[0] == '\0' ) break;
+ // Empty entry
+ if( fileinfo[id%eps].name[0] == '\xE5' ) continue;
+ // LFN entry
+ if( fileinfo[id%eps].attrib == ATTR_LFN ) continue;
+
+ // Is this a duplicate?
+ if( memcmp(ft.name, fileinfo[id%eps].name, 8+3) == 0 ) {
+ bIsDuplicate = 1;
+ break;
+ }
+
+ // No - move a long
+ }
+
+ // If a duplicate was found, increment the suffix
+ if( bIsDuplicate )
+ {
+ if( ft.name[7] == '9' ) {
+ // TODO: Expand into ~00
+ Log_Error("FAT", "TODO: Use two digit LFN suffixes");
+ Mutex_Release(&DirNode->Lock);
+ return ENOTIMPL;
+ }
+
+ ft.name[7] += 1;
+ }
+ }
+ }
+ else
+ {
+ // Create pure filetable entry
+ int i;
+ // - Copy filename
+ for( i = 0; i < 8 && *NewName && *NewName != '.'; i ++, NewName++ )
+ ft.name[i] = *NewName;
+ // - Pad with spaces
+ for( ; i < 8; i ++ )
+ ft.name[i] = ' ';
+ // - Eat '.'
+ if(*NewName)
+ NewName ++;
+ // - Copy extension
+ for( ; i < 8+3 && *NewName; i ++, NewName++ )
+ ft.name[i] = *NewName;
+ // - Pad with spaces
+ for( ; i < 8+3; i ++ )
+ ft.name[i] = ' ';
+ }
+
+ ft.attrib = 0;
+ if(NewNode->Flags & VFS_FFLAG_DIRECTORY )
+ ft.attrib |= ATTR_DIRECTORY;
+ ft.ntres = 0;
+ FAT_int_GetFATTimestamp(NewNode->CTime, &ft.cdate, &ft.ctime, &ft.ctimems);
+// ft.ctimems = ft.ctimems;
+ ft.ctime = LittleEndian16(ft.ctime);
+ ft.cdate = LittleEndian16(ft.cdate);
+ FAT_int_GetFATTimestamp(NewNode->MTime, &ft.mdate, &ft.mtime, NULL);
+ ft.mtime = LittleEndian16(ft.mtime);
+ ft.mdate = LittleEndian16(ft.mdate);
+ FAT_int_GetFATTimestamp(NewNode->ATime, &ft.adate, NULL, NULL);
+ ft.adate = LittleEndian16(ft.adate);
+ ft.clusterHi = LittleEndian16((NewNode->Inode >> 16) & 0xFFFF);
+ ft.cluster = LittleEndian16(NewNode->Inode & 0xFFFF);
+ ft.size = LittleEndian32(NewNode->Size);
+
+ LOG("ft.name = '%.11s'", ft.name);
+
+ // -- Add entry to the directory --
+ // Locate a range of nLFNEnt + 1 free entries
+ int end_id = -1;
+ int range_first = 0, range_last = -1;
+ for( int id = 0; ; id ++ )
+ {
+ if( id % eps == 0 )
+ {
+ if(FAT_int_ReadDirSector(DirNode, id/eps, fileinfo))
+ break; // end of cluster chain
+ }
+
+ // End of file list, break out
+ if( fileinfo[id%eps].name[0] == '\0' ) {
+ if( id - range_first == nLFNEnt )
+ range_last = id;
+ end_id = id;
+ break;
+ }
+
+ // If an entry is occupied, clear the range
+ if( fileinfo[id%eps].name[0] != '\xE5' ) {
+ range_first = id + 1;
+ continue ;
+ }
+
+ // Free entry, check if we have enough
+ if( id - range_first == nLFNEnt ) {
+ range_last = id;
+ break;
+ }
+ // Check the next one
+ }
+ if( range_last == -1 )
+ {
+ // - If there are none, defragment the directory?
+
+ // - Else, expand the directory
+ if( end_id == -1 ) {
+ // End of cluster chain
+ }
+ else {
+ // Just end of block
+ }
+ // - and if that fails, return an error
+ Log_Warning("FAT", "TODO: Impliment directory expansion / defragmenting");
+ Mutex_Release(&DirNode->Lock);
+ return ENOTIMPL;
+ }
+
+ // Calculate the checksum used for LFN
+ Uint8 lfn_checksum = 0;
+ if( nLFNEnt )
+ {
+ lfn_checksum = FAT_int_MakeLFNChecksum(ft.name);
+ }
+
+ // Insert entries
+ if( range_first % eps != 0 )
+ FAT_int_ReadDirSector(DirNode, range_first/eps, fileinfo);
+ for( int id = range_first; id <= range_last; id ++ )
+ {
+ if( id % eps == 0 ) {
+ if( id != range_first )
+ FAT_int_WriteDirSector(DirNode, (id-1)/eps, fileinfo);
+ FAT_int_ReadDirSector(DirNode, id/eps, fileinfo);
+ }
+
+ if( id == range_last ) {
+ // Actual entry
+ memcpy(fileinfo + id % eps, &ft, sizeof(fat_filetable));
+ }
+ else {
+ // Long filename
+ int lfnid = (nLFNEnt - (id - range_first));
+ int ofs = (lfnid-1) * 13;
+ int i=0, j=0;
+ fat_longfilename *lfnent = (void*)( fileinfo + id%eps );
+
+ lfnent->id = 0x40 | lfnid;
+ lfnent->attrib = ATTR_LFN;
+ lfnent->type = 0;
+ lfnent->firstCluster = 0;
+ lfnent->checksum = lfn_checksum; // ???
+
+ for( i = 0; i < 13; i ++ )
+ {
+ Uint16 wd;
+ if( (wd = lfn[ofs+j]) ) j ++;
+ wd = LittleEndian16(wd);
+ if(i < 5)
+ lfnent->name1[i ] = wd;
+ else if( i < 5+6 )
+ lfnent->name2[i-5 ] = wd;
+ else
+ lfnent->name3[i-5-6] = wd;
+ }
+ }
+ }
+ FAT_int_WriteDirSector(DirNode, range_last/eps, fileinfo);
+
+ Mutex_Release( &DirNode->Lock );
+ return 0;
+}
+
+/**
+ * \fn int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)
+ * \brief Rename / Delete a file
+ */
+int FAT_Unlink(tVFS_Node *Node, const char *OldName)
+{
+ tVFS_Node *child;
+ fat_filetable ft;
+
+ Mutex_Acquire(&Node->Lock);
+
+ int id = FAT_int_GetEntryByName(Node, OldName, &ft);
+ if(id == -1) {
+ Mutex_Release(&Node->Lock);
+ return ENOTFOUND;
+ }
+
+ child = FAT_int_CreateNode(Node->ImplPtr, &ft);
+ if( !child ) {
+ Mutex_Release(&Node->Lock);
+ return EINVAL;
+ }
+ child->ImplInt |= FAT_FLAG_DELETE; // Mark for deletion on close
+
+ // TODO: If it has a LFN, remove that too
+
+ // Delete from the directory
+ ft.name[0] = '\xE5';
+ FAT_int_WriteDirEntry(Node, id, &ft);
+
+ // Close child
+ child->Type->Close( child );
+ Mutex_Release( &Node->Lock );
+ return EOK;
+}
+#endif
* \todo Implement changing of the parent directory when a file is written to\r
* \todo Implement file creation / deletion\r
*/\r
-#define DEBUG 0\r
+#define DEBUG 1\r
#define VERBOSE 1\r
\r
-#define CACHE_FAT 0 //!< Caches the FAT in memory\r
-#define USE_LFN 1 //!< Enables the use of Long File Names\r
-#define SUPPORT_WRITE 0 //!< Enables write support\r
-\r
#include <acess.h>\r
#include <modules.h>\r
-#include <vfs.h>\r
-#include "fs_fat.h"\r
-\r
-#define FAT_FLAG_DIRTY 0x10000\r
-#define FAT_FLAG_DELETE 0x20000\r
-\r
-// === TYPES ===\r
-#if USE_LFN\r
-/**\r
- * \brief Long-Filename cache entry\r
- */\r
-typedef struct sFAT_LFNCacheEnt\r
-{\r
- int ID;\r
- // TODO: Handle UTF16 names correctly\r
- char Data[256];\r
-} tFAT_LFNCacheEnt;\r
-/**\r
- * \brief Long-Filename cache\r
- */\r
-typedef struct sFAT_LFNCache\r
-{\r
- int NumEntries;\r
- tFAT_LFNCacheEnt Entries[];\r
-} tFAT_LFNCache;\r
-#endif\r
+#include "common.h"\r
\r
// === PROTOTYPES ===\r
// --- Driver Core\r
void FAT_Unmount(tVFS_Node *Node);\r
// --- Helpers\r
int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster);\r
-Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 Cluster);\r
-#if SUPPORT_WRITE\r
-Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous);\r
-Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster);\r
-#endif\r
-void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer);\r
// --- File IO\r
size_t FAT_Read(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer);\r
#if SUPPORT_WRITE\r
-void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer);\r
-size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer);\r
-#endif\r
-// --- Directory IO\r
-char *FAT_ReadDir(tVFS_Node *Node, int ID);\r
-tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name);\r
-tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode);\r
-#if SUPPORT_WRITE\r
- int FAT_Mknod(tVFS_Node *Node, const char *Name, Uint Flags);\r
- int FAT_Relink(tVFS_Node *node, const char *OldName, const char *NewName);\r
+size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer);\r
#endif\r
void FAT_CloseFile(tVFS_Node *node);\r
\r
+\r
// === Options ===\r
int giFAT_MaxCachedClusters = 1024*512/4;\r
\r
// === SEMI-GLOBALS ===\r
-MODULE_DEFINE(0, (0<<8)|50 /*v0.50*/, VFAT, FAT_Install, NULL, NULL);\r
+MODULE_DEFINE(0, VER2(0,80) /*v0.80*/, VFAT, FAT_Install, NULL, NULL);\r
tFAT_VolInfo gFAT_Disks[8];\r
int giFAT_PartCount = 0;\r
tVFS_Driver gFAT_FSInfo = {"fat", 0, FAT_InitDevice, FAT_Unmount, FAT_GetNodeFromINode, NULL};\r
.FindDir = FAT_FindDir,\r
#if SUPPORT_WRITE\r
.MkNod = FAT_Mknod,\r
- .Relink = FAT_Relink,\r
+ .Link = FAT_Link,\r
+ .Unlink = FAT_Unlink,\r
#endif\r
.Close = FAT_CloseFile\r
};\r
Uint32 FATSz, RootDirSectors, TotSec;\r
tVFS_Node *node = NULL;\r
tFAT_VolInfo *diskInfo = &gFAT_Disks[giFAT_PartCount];\r
+\r
+ memset(diskInfo, 0, sizeof(*diskInfo));\r
\r
// Temporary Pointer\r
bs = &diskInfo->bootsect;\r
VFS_ReadAt(diskInfo->fileHandle, 0, 512, bs);\r
\r
if(bs->bps == 0 || bs->spc == 0) {\r
- Log_Notice("FAT", "Error in FAT Boot Sector");\r
+ Log_Notice("FAT", "Error in FAT Boot Sector (zero BPS/SPC)");\r
+ VFS_Close(diskInfo->fileHandle);\r
return NULL;\r
}\r
\r
\r
diskInfo->ClusterCount = (TotSec - (bs->resvSectCount + (bs->fatCount * FATSz) + RootDirSectors)) / bs->spc;\r
\r
- if(diskInfo->ClusterCount < 4085)\r
+ if(diskInfo->ClusterCount < FAT16_MIN_SECTORS)\r
diskInfo->type = FAT12;\r
- else if(diskInfo->ClusterCount < 65525)\r
+ else if(diskInfo->ClusterCount < FAT32_MIN_CLUSTERS)\r
diskInfo->type = FAT16;\r
else\r
diskInfo->type = FAT32;\r
diskInfo->FATCache = (Uint32*)malloc(sizeof(Uint32)*diskInfo->ClusterCount);\r
if(diskInfo->FATCache == NULL) {\r
Log_Warning("FAT", "Heap Exhausted");\r
+ VFS_Cose(diskInfo->fileHandle);\r
return NULL;\r
}\r
Ofs = bs->resvSectCount*512;\r
\r
diskInfo->BytesPerCluster = bs->spc * bs->bps;\r
\r
- // Initalise inode cache for filesystem\r
- diskInfo->inodeHandle = Inode_GetHandle();\r
- LOG("Inode Cache handle is %i", diskInfo->inodeHandle);\r
- \r
// == VFS Interface\r
node = &diskInfo->rootNode;\r
//node->Size = bs->files_in_root;\r
// Close Disk Handle\r
VFS_Close( disk->fileHandle );\r
// Clear Node Cache\r
- Inode_ClearCache(disk->inodeHandle);\r
+ FAT_int_ClearNodeCache(disk);\r
// Mark as unused\r
disk->fileHandle = -2;\r
return;\r
*/\r
int FAT_int_GetAddress(tVFS_Node *Node, Uint64 Offset, Uint64 *Addr, Uint32 *Cluster)\r
{\r
- Uint32 cluster;\r
+ Uint32 cluster, base_cluster;\r
Uint64 addr;\r
int skip;\r
tFAT_VolInfo *disk = Node->ImplPtr;\r
\r
ENTER("pNode XOffset", Node, Offset);\r
\r
- cluster = Node->Inode & 0xFFFFFFF; // Cluster ID\r
- LOG("cluster = 0x%07x", cluster);\r
+ cluster = base_cluster = Node->Inode & 0xFFFFFFF; // Cluster ID\r
+// LOG("base cluster = 0x%07x", cluster);\r
\r
// Do Cluster Skip\r
// - Pre FAT32 had a reserved area for the root.\r
if(Cluster) *Cluster = cluster;\r
}\r
else {\r
+ // TODO: Bounds checking on root\r
+// LOG("Root cluster count %i", disk->bootsect.files_in_root*32/disk->BytesPerCluster);\r
// Increment by clusters in offset\r
cluster += Offset / disk->BytesPerCluster;\r
}\r
\r
- LOG("cluster = %08x", cluster);\r
+// LOG("cluster = 0x%07x", cluster);\r
\r
// Bounds Checking (Used to spot corruption)\r
if(cluster > disk->ClusterCount + 2)\r
\r
// Compute Offsets\r
// - Pre FAT32 cluster base (in sectors)\r
- if( cluster == disk->rootOffset && disk->type != FAT32 ) {\r
+ if( base_cluster == disk->rootOffset && disk->type != FAT32 ) {\r
addr = disk->bootsect.resvSectCount * disk->bootsect.bps;\r
addr += cluster * disk->BytesPerCluster;\r
}\r
return 0;\r
}\r
\r
-/*\r
- * ====================\r
- * FAT Manipulation\r
- * ====================\r
- */\r
-/**\r
- * \fn Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)\r
- * \brief Fetches a value from the FAT\r
- */\r
-Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)\r
-{\r
- Uint32 val = 0;\r
- Uint32 ofs;\r
- ENTER("pDisk xCluster", Disk, cluster);\r
- Mutex_Acquire( &Disk->lFAT );\r
- #if CACHE_FAT\r
- if( Disk->ClusterCount <= giFAT_MaxCachedClusters )\r
- {\r
- val = Disk->FATCache[cluster];\r
- if(Disk->type == FAT12 && val == EOC_FAT12) val = -1;\r
- if(Disk->type == FAT16 && val == EOC_FAT16) val = -1;\r
- if(Disk->type == FAT32 && val == EOC_FAT32) val = -1;\r
- }\r
- else\r
- {\r
- #endif\r
- ofs = Disk->bootsect.resvSectCount*512;\r
- if(Disk->type == FAT12) {\r
- VFS_ReadAt(Disk->fileHandle, ofs+(cluster/2)*3, 3, &val);\r
- val = (cluster & 1 ? val>>12 : val & 0xFFF);\r
- if(val == EOC_FAT12) val = -1;\r
- } else if(Disk->type == FAT16) {\r
- VFS_ReadAt(Disk->fileHandle, ofs+cluster*2, 2, &val);\r
- if(val == EOC_FAT16) val = -1;\r
- } else {\r
- VFS_ReadAt(Disk->fileHandle, ofs+cluster*4, 4, &val);\r
- if(val == EOC_FAT32) val = -1;\r
- }\r
- #if CACHE_FAT\r
- }\r
- #endif /*CACHE_FAT*/\r
- Mutex_Release( &Disk->lFAT );\r
- LEAVE('x', val);\r
- return val;\r
-}\r
-\r
-#if SUPPORT_WRITE\r
-/**\r
- * \brief Allocate a new cluster\r
- */\r
-Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous)\r
-{\r
- Uint32 ret = Previous;\r
- #if CACHE_FAT\r
- if( Disk->ClusterCount <= giFAT_MaxCachedClusters )\r
- {\r
- Uint32 eoc;\r
- \r
- LOCK(Disk->lFAT);\r
- for(ret = Previous; ret < Disk->ClusterCount; ret++)\r
- {\r
- if(Disk->FATCache[ret] == 0)\r
- goto append;\r
- }\r
- for(ret = 0; ret < Previous; ret++)\r
- {\r
- if(Disk->FATCache[ret] == 0)\r
- goto append;\r
- }\r
- \r
- RELEASE(Disk->lFAT);\r
- return 0;\r
- \r
- append:\r
- switch(Disk->type)\r
- {\r
- case FAT12: eoc = EOC_FAT12; break;\r
- case FAT16: eoc = EOC_FAT16; break;\r
- case FAT32: eoc = EOC_FAT32; break;\r
- default: return 0;\r
- }\r
- \r
- Disk->FATCache[ret] = eoc;\r
- Disk->FATCache[Previous] = ret;\r
- \r
- RELEASE(Disk->lFAT);\r
- return ret;\r
- }\r
- else\r
- {\r
- #endif\r
- Uint32 val;\r
- Uint32 ofs = Disk->bootsect.resvSectCount*512;\r
- Log_Warning("FAT", "TODO: Implement cluster allocation with non cached FAT");\r
- return 0;\r
- \r
- switch(Disk->type)\r
- {\r
- case FAT12:\r
- VFS_ReadAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);\r
- if( Previous & 1 ) {\r
- val &= 0xFFF000;\r
- val |= ret;\r
- }\r
- else {\r
- val &= 0xFFF;\r
- val |= ret<<12;\r
- }\r
- VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);\r
- \r
- VFS_ReadAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val);\r
- if( Cluster & 1 ) {\r
- val &= 0xFFF000;\r
- val |= eoc;\r
- }\r
- else {\r
- val &= 0x000FFF;\r
- val |= eoc<<12;\r
- }\r
- VFS_WriteAt(Disk->fileHandle, ofs+(ret>>1)*3, 3, &val);\r
- break;\r
- case FAT16:\r
- VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret);\r
- VFS_WriteAt(Disk->fileHandle, ofs+ret*2, 2, &eoc);\r
- break;\r
- case FAT32:\r
- VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret);\r
- VFS_WriteAt(Disk->fileHandle, ofs+ret*4, 4, &eoc);\r
- break;\r
- }\r
- return ret;\r
- #if CACHE_FAT\r
- }\r
- #endif\r
-}\r
-\r
-/**\r
- * \brief Free's a cluster\r
- * \return The original contents of the cluster\r
- */\r
-Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster)\r
-{\r
- Uint32 ret;\r
- #if CACHE_FAT\r
- if( Disk->ClusterCount <= giFAT_MaxCachedClusters )\r
- {\r
- LOCK(Disk->lFAT);\r
- \r
- ret = Disk->FATCache[Cluster];\r
- Disk->FATCache[Cluster] = 0;\r
- \r
- RELEASE(Disk->lFAT);\r
- }\r
- else\r
- {\r
- #endif\r
- Uint32 val;\r
- Uint32 ofs = Disk->bootsect.resvSectCount*512;\r
- LOCK(Disk->lFAT);\r
- switch(Disk->type)\r
- {\r
- case FAT12:\r
- VFS_ReadAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val);\r
- if( Cluster & 1 ) {\r
- ret = val & 0xFFF0000;\r
- val &= 0xFFF;\r
- }\r
- else {\r
- ret = val & 0xFFF;\r
- val &= 0xFFF000;\r
- }\r
- VFS_WriteAt(Disk->fileHandle, ofs+(Previous>>1)*3, 3, &val);\r
- break;\r
- case FAT16:\r
- VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret);\r
- val = 0;\r
- VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val);\r
- break;\r
- case FAT32:\r
- VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret);\r
- val = 0;\r
- VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val);\r
- break;\r
- }\r
- RELEASE(Disk->lFAT);\r
- #if CACHE_FAT\r
- }\r
- #endif\r
- if(Disk->type == FAT12 && ret == EOC_FAT12) ret = -1;\r
- if(Disk->type == FAT16 && ret == EOC_FAT16) ret = -1;\r
- if(Disk->type == FAT32 && ret == EOC_FAT32) ret = -1;\r
- return ret;\r
-}\r
-#endif\r
-\r
-/*\r
- * ====================\r
- * Cluster IO\r
- * ====================\r
- */\r
-/**\r
- * \brief Read a cluster\r
- * \param Disk Disk (Volume) to read from\r
- * \param Length Length to read\r
- * \param Buffer Destination for read data\r
- */\r
-void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer)\r
-{\r
- ENTER("pDisk xCluster iLength pBuffer", Disk, Cluster, Length, Buffer);\r
- VFS_ReadAt(\r
- Disk->fileHandle,\r
- (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )\r
- * Disk->bootsect.bps,\r
- Length,\r
- Buffer\r
- );\r
- LEAVE('-');\r
-}\r
-\r
/* ====================\r
* File IO\r
* ====================\r
}\r
\r
#if SUPPORT_WRITE\r
-/**\r
- * \brief Write a cluster to disk\r
- */\r
-void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, void *Buffer)\r
-{\r
- ENTER("pDisk xCluster pBuffer", Disk, Cluster, Buffer);\r
- VFS_ReadAt(\r
- Disk->fileHandle,\r
- (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )\r
- * Disk->bootsect.bps,\r
- Disk->BytesPerCluster,\r
- Buffer\r
- );\r
- LEAVE('-');\r
-}\r
-\r
/**\r
* \brief Write to a file\r
* \param Node File Node\r
* \param Length Size of data to write\r
* \param Buffer Data source\r
*/\r
-size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, void *Buffer)\r
+size_t FAT_Write(tVFS_Node *Node, off_t Offset, size_t Length, const void *Buffer)\r
{\r
tFAT_VolInfo *disk = Node->ImplPtr;\r
char tmpBuf[disk->BytesPerCluster];\r
int remLength = Length;\r
Uint32 cluster, tmpCluster;\r
int bNewCluster = 0;\r
+ off_t original_offset = Offset;\r
\r
if(Offset > Node->Size) return 0;\r
\r
+ ENTER("pNode Xoffset xlength pbuffer", Node, Offset, Length, Buffer);\r
+ \r
// Seek Clusters\r
cluster = Node->Inode & 0xFFFFFFFF;\r
while( Offset > disk->BytesPerCluster )\r
cluster = FAT_int_GetFatValue( disk, cluster );\r
if(cluster == -1) {\r
Log_Warning("FAT", "EOC Unexpectedly Reached");\r
+ LEAVE('i', 0);\r
return 0;\r
}\r
Offset -= disk->BytesPerCluster;\r
if( Offset == disk->BytesPerCluster )\r
{\r
Uint32 tmp = FAT_int_AllocateCluster(disk, cluster);\r
- if(!tmp) return 0;\r
+ if(!tmp) {\r
+ LEAVE('i', 0);\r
+ return 0;\r
+ }\r
cluster = tmp;\r
Offset -= disk->BytesPerCluster;\r
}\r
{\r
char tmpBuf[disk->BytesPerCluster];\r
\r
+ LOG("Read-Modify-Write single");\r
+ \r
// Read-Modify-Write\r
FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );\r
memcpy( tmpBuf + Offset, Buffer, Length );\r
FAT_int_WriteCluster( disk, cluster, tmpBuf );\r
- \r
- return Length;\r
+ goto return_full;\r
}\r
\r
// Clean up changes within a cluster\r
if( Offset )\r
{ \r
+ LOG("Read-Modify-Write first");\r
+\r
// Read-Modify-Write\r
FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );\r
memcpy( tmpBuf + Offset, Buffer, disk->BytesPerCluster - Offset );\r
tmpCluster = FAT_int_GetFatValue(disk, cluster);\r
if(tmpCluster == -1) {\r
tmpCluster = FAT_int_AllocateCluster(disk, cluster);\r
- if( tmpCluster == 0 ) {\r
- return Length - remLength;\r
- }\r
+ if( tmpCluster == 0 )\r
+ goto ret_incomplete;\r
}\r
cluster = tmpCluster;\r
}\r
{\r
FAT_int_WriteCluster( disk, cluster, Buffer );\r
Buffer += disk->BytesPerCluster;\r
+ remLength -= disk->BytesPerCluster;\r
\r
// Get next cluster (allocating if needed)\r
tmpCluster = FAT_int_GetFatValue(disk, cluster);\r
if(tmpCluster == -1) {\r
bNewCluster = 1;\r
tmpCluster = FAT_int_AllocateCluster(disk, cluster);\r
- if( tmpCluster == 0 ) {\r
- return Length - remLength;\r
- }\r
+ if( tmpCluster == 0 )\r
+ goto ret_incomplete;\r
}\r
cluster = tmpCluster;\r
}\r
\r
// Finish off\r
- tmpBuf = malloc( disk->BytesPerCluster );\r
- if( bNewCluster )\r
- memset(tmpBuf, 0, disk->BytesPerCluster);\r
- else\r
- FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );\r
- memcpy( tmpBuf, Buffer, remLength );\r
- FAT_int_WriteCluster( disk, cluster, tmpBuf );\r
- free( tmpBuf );\r
- \r
- return Length;\r
-}\r
-#endif\r
-\r
-/* ====================\r
- * File Names & Nodes\r
- * ====================\r
- */\r
-/**\r
- * \brief Converts a FAT directory entry name into a proper filename\r
- * \param dest Destination array (must be at least 13 bytes in size)\r
- * \param src 8.3 filename (concatenated, e.g 'FILE1 TXT')\r
- */\r
-void FAT_int_ProperFilename(char *dest, const char *src)\r
-{\r
- int inpos, outpos;\r
- \r
- // Name\r
- outpos = 0;\r
- for( inpos = 0; inpos < 8; inpos++ ) {\r
- if(src[inpos] == ' ') break;\r
- dest[outpos++] = src[inpos];\r
- }\r
- inpos = 8;\r
- // Check for empty extensions\r
- if(src[8] != ' ')\r
- {\r
- dest[outpos++] = '.';\r
- for( ; inpos < 11; inpos++) {\r
- if(src[inpos] == ' ') break;\r
- dest[outpos++] = src[inpos];\r
- }\r
- }\r
- dest[outpos++] = '\0';\r
- \r
- //LOG("dest='%s'", dest);\r
-}\r
-\r
-/**\r
- * \fn char *FAT_int_CreateName(fat_filetable *ft, char *LongFileName)\r
- * \brief Converts either a LFN or a 8.3 Name into a proper name\r
- * \param ft Pointer to the file's entry in the parent directory\r
- * \param LongFileName Long file name pointer\r
- * \return Filename as a heap string\r
- */\r
-char *FAT_int_CreateName(fat_filetable *ft, char *LongFileName)\r
-{\r
- char *ret;\r
- ENTER("pft sLongFileName", ft, LongFileName);\r
- //Log_Debug("FAT", "FAT_int_CreateName(ft=%p, LongFileName=%p'%s')", ft, LongFileName);\r
- #if USE_LFN\r
- if(LongFileName && LongFileName[0] != '\0')\r
- { \r
- ret = strdup(LongFileName);\r
- }\r
- else\r
- {\r
- #endif\r
- ret = (char*) malloc(13);\r
- if( !ret ) {\r
- Log_Warning("FAT", "FAT_int_CreateName: malloc(13) failed");\r
- return NULL;\r
- }\r
- FAT_int_ProperFilename(ret, ft->name);\r
- #if USE_LFN\r
- }\r
- #endif\r
- LEAVE('s', ret);\r
- return ret;\r
-}\r
-\r
-/**\r
- * \brief Creates a tVFS_Node structure for a given file entry\r
- * \param Parent Parent directory VFS node\r
- * \param Entry File table entry for the new node\r
- * \param Pos Position in the parent of the new node\r
- */\r
-tVFS_Node *FAT_int_CreateNode(tVFS_Node *Parent, fat_filetable *Entry, int Pos)\r
-{\r
- tVFS_Node node;\r
- tVFS_Node *ret;\r
- tFAT_VolInfo *disk = Parent->ImplPtr;\r
- \r
- ENTER("pParent pFT", Parent, Entry);\r
- LOG("disk = %p", disk);\r
- \r
- memset(&node, 0, sizeof(tVFS_Node));\r
- \r
- // Set Other Data\r
- // 0-27: Cluster, 32-59: Parent Cluster\r
- node.Inode = Entry->cluster | (Entry->clusterHi<<16) | (Parent->Inode << 32);\r
- LOG("node.Inode = %llx", node.Inode);\r
- // Position in parent directory\r
- node.ImplInt = Pos & 0xFFFF;\r
- // Disk Pointer\r
- node.ImplPtr = disk;\r
- node.Size = Entry->size;\r
- LOG("Entry->size = %i", Entry->size);\r
- // root:root\r
- node.UID = 0; node.GID = 0;\r
- node.NumACLs = 1;\r
- \r
- node.Flags = 0;\r
- if(Entry->attrib & ATTR_DIRECTORY) node.Flags |= VFS_FFLAG_DIRECTORY;\r
- if(Entry->attrib & ATTR_READONLY) {\r
- node.Flags |= VFS_FFLAG_READONLY;\r
- node.ACLs = &gVFS_ACL_EveryoneRX; // R-XR-XR-X\r
- }\r
- else {\r
- node.ACLs = &gVFS_ACL_EveryoneRWX; // RWXRWXRWX\r
- }\r
- \r
- // Create timestamps\r
- node.ATime = timestamp(0,0,0,\r
- ((Entry->adate&0x1F) - 1), // Days\r
- ((Entry->adate&0x1E0) - 1), // Months\r
- 1980+((Entry->adate&0xFF00)>>8) // Years\r
- );\r
- \r
- node.CTime = Entry->ctimems * 10; // Miliseconds\r
- node.CTime += timestamp(\r
- ((Entry->ctime&0x1F)<<1), // Seconds\r
- ((Entry->ctime&0x3F0)>>5), // Minutes\r
- ((Entry->ctime&0xF800)>>11), // Hours\r
- ((Entry->cdate&0x1F)-1), // Days\r
- ((Entry->cdate&0x1E0)-1), // Months\r
- 1980+((Entry->cdate&0xFF00)>>8) // Years\r
- );\r
- \r
- node.MTime = timestamp(\r
- ((Entry->mtime&0x1F)<<1), // Seconds\r
- ((Entry->mtime&0x3F0)>>5), // Minutes\r
- ((Entry->mtime&0xF800)>>11), // Hours\r
- ((Entry->mdate&0x1F)-1), // Days\r
- ((Entry->mdate&0x1E0)-1), // Months\r
- 1980+((Entry->mdate&0xFF00)>>8) // Years\r
- );\r
- \r
- // Set pointers\r
- if(node.Flags & VFS_FFLAG_DIRECTORY) {\r
- //Log_Debug("FAT", "Directory %08x has size 0x%x", node.Inode, node.Size);\r
- node.Type = &gFAT_DirType; \r
- node.Size = -1;\r
- }\r
- else {\r
- node.Type = &gFAT_FileType;\r
- }\r
- \r
- ret = Inode_CacheNode(disk->inodeHandle, &node);\r
- LEAVE('p', ret);\r
- return ret;\r
-}\r
-\r
-/* \r
- * ====================\r
- * Directory IO\r
- * ====================\r
- */\r
-\r
-/**\r
- * \brief Reads a sector from the disk\r
- * \param Node Directory node to read\r
- * \param Sector Sector number in the directory to read\r
- * \param Buffer Destination buffer for the read data\r
- */\r
-int FAT_int_ReadDirSector(tVFS_Node *Node, int Sector, fat_filetable *Buffer)\r
-{\r
- Uint64 addr;\r
- tFAT_VolInfo *disk = Node->ImplPtr;\r
- \r
- ENTER("pNode iSector pEntry", Node, Sector, Buffer);\r
- \r
- // Parse address\r
- if(FAT_int_GetAddress(Node, Sector * 512, &addr, NULL))\r
- {\r
- LEAVE('i', 1);\r
- return 1;\r
- }\r
- \r
- LOG("addr = 0x%llx", addr);\r
- // Read Sector\r
- if(VFS_ReadAt(disk->fileHandle, addr, 512, Buffer) != 512)\r
- {\r
- LEAVE('i', 1);\r
- return 1;\r
- }\r
- \r
- LEAVE('i', 0);\r
- return 0;\r
-}\r
-\r
-#if SUPPORT_WRITE\r
-/**\r
- * \brief Writes an entry to the disk\r
- * \todo Support expanding a directory\r
- * \param Node Directory node\r
- * \param ID ID of entry to update\r
- * \param Entry Entry data\r
- * \return Zero on success, non-zero on error\r
- */\r
-int FAT_int_WriteDirEntry(tVFS_Node *Node, int ID, fat_filetable *Entry)\r
-{\r
- Uint64 addr = 0;\r
- int tmp;\r
- Uint32 cluster = 0;\r
- tFAT_VolInfo *disk = Node->ImplPtr;\r
- \r
- ENTER("pNode iID pEntry", Node, ID, Entry);\r
- \r
- tmp = FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);\r
- if( tmp )\r
- {\r
- //TODO: Allocate a cluster\r
- cluster = FAT_int_AllocateCluster(Node->ImplPtr, cluster);\r
- if(cluster == -1) {\r
- Log_Warning("FAT", "Unable to allocate an other cluster for %p", Node);\r
- LEAVE('i', 1);\r
- return 1;\r
- }\r
- FAT_int_GetAddress(Node, ID * sizeof(fat_filetable), &addr, &cluster);\r
- }\r
- \r
-\r
- LOG("addr = 0x%llx", addr);\r
- \r
- // Read Sector\r
- VFS_WriteAt(disk->fileHandle, addr, sizeof(fat_filetable), Entry); // Read Dir Data\r
- \r
- LEAVE('i', 0);\r
- return 0;\r
-}\r
-#endif\r
-\r
-#if USE_LFN \r
-/**\r
- * \fn char *FAT_int_GetLFN(tVFS_Node *node)\r
- * \brief Return pointer to LFN cache entry\r
- * \param Node Directory node\r
- * \param ID ID of the short name\r
- * \return Pointer to the LFN cache entry\r
- */\r
-char *FAT_int_GetLFN(tVFS_Node *Node, int ID)\r
-{\r
- tFAT_LFNCache *cache;\r
- int i, firstFree;\r
- \r
- Mutex_Acquire( &Node->Lock );\r
- \r
- // TODO: Thread Safety (Lock things)\r
- cache = Node->Data;\r
- \r
- // Create a cache if it isn't there\r
- if(!cache) {\r
- cache = Node->Data = malloc( sizeof(tFAT_LFNCache) + sizeof(tFAT_LFNCacheEnt) );\r
- cache->NumEntries = 1;\r
- cache->Entries[0].ID = ID;\r
- cache->Entries[0].Data[0] = '\0';\r
- Mutex_Release( &Node->Lock );\r
- //Log_Debug("FAT", "Return = %p (new)", cache->Entries[0].Data);\r
- return cache->Entries[0].Data;\r
- }\r
- \r
- // Scan for this entry\r
- firstFree = -1;\r
- for( i = 0; i < cache->NumEntries; i++ )\r
- {\r
- if( cache->Entries[i].ID == ID ) {\r
- Mutex_Release( &Node->Lock );\r
- //Log_Debug("FAT", "Return = %p (match)", cache->Entries[i].Data);\r
- return cache->Entries[i].Data;\r
- }\r
- if( cache->Entries[i].ID == -1 && firstFree == -1 )\r
- firstFree = i;\r
- }\r
- \r
- if(firstFree == -1) {\r
- // Use `i` for temp length\r
- i = sizeof(tFAT_LFNCache) + (cache->NumEntries+1)*sizeof(tFAT_LFNCacheEnt);\r
- Node->Data = realloc( Node->Data, i );\r
- if( !Node->Data ) {\r
- Log_Error("FAT", "realloc() fail, unable to allocate %i for LFN cache", i);\r
- Mutex_Release( &Node->Lock );\r
- return NULL;\r
- }\r
- //Log_Debug("FAT", "Realloc (%i)\n", i);\r
- cache = Node->Data;\r
- i = cache->NumEntries;\r
- cache->NumEntries ++;\r
- }\r
- else {\r
- i = firstFree;\r
- }\r
- \r
- // Create new entry\r
- cache->Entries[ i ].ID = ID;\r
- cache->Entries[ i ].Data[0] = '\0';\r
- \r
- Mutex_Release( &Node->Lock );\r
- //Log_Debug("FAT", "Return = %p (firstFree, i = %i)", cache->Entries[i].Data, i);\r
- return cache->Entries[ i ].Data;\r
-}\r
-\r
-/**\r
- * \fn void FAT_int_DelLFN(tVFS_Node *node)\r
- * \brief Delete a LFN cache entry\r
- * \param Node Directory node\r
- * \param ID File Entry ID\r
- */\r
-void FAT_int_DelLFN(tVFS_Node *Node, int ID)\r
-{\r
- tFAT_LFNCache *cache = Node->Data;\r
- int i;\r
- \r
- // Fast return\r
- if(!cache) return;\r
- \r
- // Scan for a current entry\r
- for( i = 0; i < cache->NumEntries; i++ )\r
- {\r
- if( cache->Entries[i].ID == ID )\r
- cache->Entries[i].ID = -1;\r
- }\r
- return ;\r
-}\r
-#endif\r
-\r
-/**\r
- * \fn char *FAT_ReadDir(tVFS_Node *Node, int ID)\r
- * \param Node Node structure of directory\r
- * \param ID Directory position\r
- * \return Filename as a heap string, NULL or VFS_SKIP\r
- */\r
-char *FAT_ReadDir(tVFS_Node *Node, int ID)\r
-{\r
- fat_filetable fileinfo[16]; // sizeof(fat_filetable)=32, so 16 per sector\r
- int a = 0;\r
- char *ret;\r
- #if USE_LFN\r
- char *lfn = NULL;\r
- #endif\r
- \r
- ENTER("pNode iID", Node, ID);\r
- \r
- if(FAT_int_ReadDirSector(Node, ID/16, fileinfo))\r
- {\r
- LOG("End of chain, end of dir");\r
- LEAVE('n');\r
- return NULL;\r
- }\r
- \r
- // Offset in sector\r
- a = ID % 16;\r
-\r
- LOG("fileinfo[%i].name[0] = 0x%x", a, (Uint8)fileinfo[a].name[0]);\r
- \r
- // Check if this is the last entry\r
- if( fileinfo[a].name[0] == '\0' ) {\r
- Node->Size = ID;\r
- LOG("End of list");\r
- LEAVE('n');\r
- return NULL; // break\r
- }\r
- \r
- // Check for empty entry\r
- if( (Uint8)fileinfo[a].name[0] == 0xE5 ) {\r
- LOG("Empty Entry");\r
- #if 0 // Stop on empty entry?\r
- LEAVE('n');\r
- return NULL; // Stop\r
- #else\r
- LEAVE('p', VFS_SKIP);\r
- return VFS_SKIP; // Skip\r
- #endif\r
- }\r
- \r
- #if USE_LFN\r
- // Get Long File Name Cache\r
- if(fileinfo[a].attrib == ATTR_LFN)\r
- {\r
- fat_longfilename *lfnInfo;\r
- \r
- lfnInfo = (fat_longfilename *) &fileinfo[a];\r
- \r
- // Get cache for corresponding file\r
- // > ID + Index gets the corresponding short node\r
- lfn = FAT_int_GetLFN( Node, ID + (lfnInfo->id & 0x3F) );\r
- \r
- // Bit 6 indicates the start of an entry\r
- if(lfnInfo->id & 0x40) memset(lfn, 0, 256);\r
- \r
- a = ((lfnInfo->id & 0x3F) - 1) * 13;\r
- //Log_Debug("FAT", "ID = 0x%02x, a = %i", lfnInfo->id, a);\r
- \r
- // Sanity Check (FAT implementations should not allow >255 character names)\r
- if(a > 255) return VFS_SKIP;\r
- \r
- // Append new bytes\r
- lfn[a+ 0] = lfnInfo->name1[0]; lfn[a+ 1] = lfnInfo->name1[1];\r
- lfn[a+ 2] = lfnInfo->name1[2]; lfn[a+ 3] = lfnInfo->name1[3];\r
- lfn[a+ 4] = lfnInfo->name1[4]; \r
- lfn[a+ 5] = lfnInfo->name2[0]; lfn[a+ 6] = lfnInfo->name2[1];\r
- lfn[a+ 7] = lfnInfo->name2[2]; lfn[a+ 8] = lfnInfo->name2[3];\r
- lfn[a+ 9] = lfnInfo->name2[4]; lfn[a+10] = lfnInfo->name2[5];\r
- lfn[a+11] = lfnInfo->name3[0]; lfn[a+12] = lfnInfo->name3[1];\r
- LOG("lfn = '%s'", lfn);\r
- //Log_Debug("FAT", "lfn = '%s'", lfn);\r
- LEAVE('p', VFS_SKIP);\r
- return VFS_SKIP;\r
- }\r
- #endif\r
- \r
- // Check if it is a volume entry\r
- if(fileinfo[a].attrib & 0x08) {\r
- LEAVE('p', VFS_SKIP);\r
- return VFS_SKIP;\r
- }\r
- // Ignore .\r
- if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == ' ') {\r
- LEAVE('p', VFS_SKIP);\r
- return VFS_SKIP;\r
- }\r
- // and ..\r
- if(fileinfo[a].name[0] == '.' && fileinfo[a].name[1] == '.' && fileinfo[a].name[2] == ' ') {\r
- LEAVE('p', VFS_SKIP);\r
- return VFS_SKIP;\r
- }\r
- \r
- LOG("name='%c%c%c%c%c%c%c%c.%c%c%c'",\r
- fileinfo[a].name[0], fileinfo[a].name[1], fileinfo[a].name[2], fileinfo[a].name[3],\r
- fileinfo[a].name[4], fileinfo[a].name[5], fileinfo[a].name[6], fileinfo[a].name[7],\r
- fileinfo[a].name[8], fileinfo[a].name[9], fileinfo[a].name[10] );\r
- \r
- #if USE_LFN\r
- lfn = FAT_int_GetLFN(Node, ID);\r
- //Log_Debug("FAT", "lfn = %p'%s'", lfn, lfn);\r
- ret = FAT_int_CreateName(&fileinfo[a], lfn);\r
- #else\r
- ret = FAT_int_CreateName(&fileinfo[a], NULL);\r
- #endif\r
- \r
- LEAVE('s', ret);\r
- return ret;\r
-}\r
-\r
-/**\r
- * \fn tVFS_Node *FAT_FindDir(tVFS_Node *node, char *name)\r
- * \brief Finds an entry in the current directory\r
- */\r
-tVFS_Node *FAT_FindDir(tVFS_Node *Node, const char *Name)\r
-{\r
- fat_filetable fileinfo[16];\r
- char tmpName[13];\r
- #if USE_LFN\r
- fat_longfilename *lfnInfo;\r
- char lfn[256];\r
- int lfnPos=255, lfnId = -1;\r
- #endif\r
- int i;\r
- tVFS_Node *tmpNode;\r
- tFAT_VolInfo *disk = Node->ImplPtr;\r
- Uint32 cluster;\r
- \r
- ENTER("pNode sname", Node, Name); \r
-\r
- // Fast Returns\r
- if(!Name || Name[0] == '\0') {\r
- LEAVE('n');\r
- return NULL;\r
- }\r
- \r
- for( i = 0; ; i++ )\r
+ if( remLength )\r
{\r
- if((i & 0xF) == 0) {\r
- if(FAT_int_ReadDirSector(Node, i/16, fileinfo))\r
- {\r
- LEAVE('n');\r
- return NULL;\r
- }\r
- }\r
- \r
- //Check if the files are free\r
- if(fileinfo[i&0xF].name[0] == '\0') break; // End of List marker\r
- if(fileinfo[i&0xF].name[0] == '\xE5') continue; // Free entry\r
- \r
- \r
- #if USE_LFN\r
- // Long File Name Entry\r
- if(fileinfo[i & 0xF].attrib == ATTR_LFN)\r
- {\r
- lfnInfo = (fat_longfilename *) &fileinfo[i&0xF];\r
- if(lfnInfo->id & 0x40) {\r
- memset(lfn, 0, 256);\r
- lfnPos = (lfnInfo->id & 0x3F) * 13 - 1;\r
- }\r
- // Sanity check the position so we don't overflow\r
- if( lfnPos < 12 )\r
- continue ;\r
- lfn[lfnPos--] = lfnInfo->name3[1]; lfn[lfnPos--] = lfnInfo->name3[0];\r
- lfn[lfnPos--] = lfnInfo->name2[5]; lfn[lfnPos--] = lfnInfo->name2[4];\r
- lfn[lfnPos--] = lfnInfo->name2[3]; lfn[lfnPos--] = lfnInfo->name2[2];\r
- lfn[lfnPos--] = lfnInfo->name2[1]; lfn[lfnPos--] = lfnInfo->name2[0];\r
- lfn[lfnPos--] = lfnInfo->name1[4]; lfn[lfnPos--] = lfnInfo->name1[3];\r
- lfn[lfnPos--] = lfnInfo->name1[2]; lfn[lfnPos--] = lfnInfo->name1[1];\r
- lfn[lfnPos--] = lfnInfo->name1[0];\r
- if((lfnInfo->id&0x3F) == 1)\r
- {\r
- lfnId = i+1;\r
- }\r
- }\r
+ if( bNewCluster )\r
+ memset(tmpBuf, 0, disk->BytesPerCluster);\r
else\r
- {\r
- // Remove LFN if it does not apply\r
- if(lfnId != i) lfn[0] = '\0';\r
- #else\r
- if(fileinfo[i&0xF].attrib == ATTR_LFN) continue;\r
- #endif\r
- // Get Real Filename\r
- FAT_int_ProperFilename(tmpName, fileinfo[i&0xF].name);\r
- LOG("tmpName = '%s'", tmpName);\r
- \r
- // Only the long name is case sensitive, 8.3 is not\r
- #if USE_LFN\r
- if(strucmp(tmpName, Name) == 0 || strcmp(lfn, Name) == 0)\r
- #else\r
- if(strucmp(tmpName, Name) == 0)\r
- #endif\r
- {\r
- cluster = fileinfo[i&0xF].cluster | (fileinfo[i&0xF].clusterHi << 16);\r
- tmpNode = Inode_GetCache(disk->inodeHandle, cluster);\r
- if(tmpNode == NULL) // Node is not cached\r
- {\r
- tmpNode = FAT_int_CreateNode(Node, &fileinfo[i&0xF], i);\r
- }\r
- LEAVE('p', tmpNode);\r
- return tmpNode;\r
- }\r
- #if USE_LFN\r
- }\r
- #endif\r
+ FAT_int_ReadCluster( disk, cluster, disk->BytesPerCluster, tmpBuf );\r
+ memcpy( tmpBuf, Buffer, remLength );\r
+ FAT_int_WriteCluster( disk, cluster, tmpBuf );\r
}\r
- \r
- LEAVE('n');\r
- return NULL;\r
-}\r
-\r
-tVFS_Node *FAT_GetNodeFromINode(tVFS_Node *Root, Uint64 Inode)\r
-{\r
- tFAT_VolInfo *disk = Root->ImplPtr;\r
- int ents_per_sector = 512 / sizeof(fat_filetable); \r
- fat_filetable fileinfo[ents_per_sector];\r
- int sector = 0, i;\r
- tVFS_Node stub_node;\r
-\r
- ENTER("pRoot XInode", Root, Inode);\r
-\r
- stub_node.ImplPtr = disk;\r
- stub_node.Size = -1;\r
- stub_node.Inode = Inode >> 32;\r
-\r
- for( i = 0; ; i ++ )\r
- {\r
- if( i == 0 || i == ents_per_sector )\r
- {\r
- if(FAT_int_ReadDirSector(&stub_node, sector, fileinfo))\r
- {\r
- LOG("ReadDirSector failed");\r
- LEAVE('n');\r
- return NULL;\r
- }\r
- i = 0;\r
- sector ++;\r
- }\r
- \r
- // Check for free/end of list\r
- if(fileinfo[i].name[0] == '\0') break; // End of List marker\r
- if(fileinfo[i].name[0] == '\xE5') continue; // Free entry\r
- \r
- if(fileinfo[i].attrib == ATTR_LFN) continue;\r
-\r
- LOG("fileinfo[i].cluster = %x %04x", fileinfo[i].clusterHi, fileinfo[i].cluster);\r
- #if DEBUG\r
- {\r
- char tmpName[13];\r
- FAT_int_ProperFilename(tmpName, fileinfo[i].name);\r
- LOG("tmpName = '%s'", tmpName);\r
- }\r
- #endif\r
- \r
- \r
- if(fileinfo[i].cluster != (Inode & 0xFFFF)) continue;\r
- if(fileinfo[i].clusterHi != ((Inode >> 16) & 0xFFFF)) continue;\r
\r
- LEAVE_RET('p', FAT_int_CreateNode(&stub_node, &fileinfo[i], sector*ents_per_sector+i));\r
+return_full:\r
+ if( original_offset + Length > Node->Size ) {\r
+ Node->Size = original_offset + Length;\r
+ LOG("Updated size to %x", Node->Size);\r
+ Node->ImplInt |= FAT_FLAG_DIRTY;\r
}\r
- LOG("sector = %i, i = %i", sector, i);\r
- LEAVE('n');\r
- return NULL;\r
-}\r
\r
-#if SUPPORT_WRITE\r
-/**\r
- * \fn int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags)\r
- * \brief Create a new node\r
- */\r
-int FAT_Mknod(tVFS_Node *Node, char *Name, Uint Flags)\r
-{\r
- return 0;\r
-}\r
-\r
-/**\r
- * \fn int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)\r
- * \brief Rename / Delete a file\r
- */\r
-int FAT_Relink(tVFS_Node *Node, char *OldName, char *NewName)\r
-{\r
- tVFS_Node *child;\r
- fat_filetable ft = {0};\r
- int ret;\r
- \r
- child = FAT_FindDir(Node, OldName);\r
- if(!child) return ENOTFOUND;\r
- \r
- // Delete?\r
- if( NewName == NULL )\r
- {\r
- child->ImplInt |= FAT_FLAG_DELETE; // Mark for deletion on close\r
- \r
- // Delete from the directory\r
- ft.name[0] = '\xE9';\r
- FAT_int_WriteDirEntry(Node, child->ImplInt & 0xFFFF, &ft);\r
- \r
- // Return success\r
- ret = EOK;\r
- }\r
- // Rename\r
- else\r
- {\r
- Log_Warning("FAT", "Renaming no yet supported %p ('%s' => '%s')",\r
- Node, OldName, NewName);\r
- ret = ENOTIMPL;\r
- }\r
- \r
- // Close child\r
- child->Close( child );\r
- return ret;\r
+ LEAVE('i', Length);\r
+ return Length;\r
+ret_incomplete:\r
+ LOG("Write incomplete");\r
+ Length -= remLength;\r
+ if( original_offset + Length > Node->Size ) {\r
+ Node->Size = original_offset + Length; \r
+ Node->ImplInt |= FAT_FLAG_DIRTY;\r
+ }\r
+ LEAVE('i', Length);\r
+ return Length;\r
}\r
#endif\r
\r
{\r
tFAT_VolInfo *disk = Node->ImplPtr;\r
if(Node == NULL) return ;\r
- \r
+\r
+ ENTER("pNode", Node); \r
+\r
#if SUPPORT_WRITE\r
// Update the node if it's dirty (don't bother if it's marked for\r
// deletion)\r
if( (Node->ImplInt & FAT_FLAG_DIRTY) && !(Node->ImplInt & FAT_FLAG_DELETE) )\r
{\r
- tFAT_VolInfo buf[16];\r
- tFAT_VolInfo *ft = &buf[ (Node->ImplInt & 0xFFFF) % 16 ];\r
- \r
- FAT_int_ReadDirSector(Node, (Node->ImplInt & 0xFFFF)/16, buf);\r
- ft->size = Node->Size;\r
+ fat_filetable ft;\r
+ tVFS_Node *dirnode;\r
+\r
+ dirnode = FAT_int_CreateIncompleteDirNode(disk, Node->Inode >> 32);\r
+ if( !dirnode ) {\r
+ Log_Error("FAT", "Can't get node for directory cluster #0x%x", Node->Inode>>32);\r
+ LEAVE('-');\r
+ return ;\r
+ }\r
+\r
+ int id = FAT_int_GetEntryByCluster(dirnode, Node->Inode & 0xFFFFFFFF, &ft);\r
+ ft.size = Node->Size;\r
// TODO: update adate, mtime, mdate\r
- FAT_int_WriteDirEntry(Node, Node->ImplInt & 0xFFFF, ft);\r
+ FAT_int_WriteDirEntry(dirnode, id, &ft);\r
+ \r
+ dirnode->Type->Close(dirnode);\r
\r
Node->ImplInt &= ~FAT_FLAG_DIRTY;\r
}\r
#endif\r
+\r
+ Uint32 cluster = Node->Inode;\r
+ Uint32 implint = Node->ImplInt;\r
\r
- // TODO: Make this more thread safe somehow, probably by moving the\r
- // Inode_UncacheNode higher up and saving the cluster value somewhere\r
- if( Node->ReferenceCount == 1 )\r
- { \r
- #if SUPPORT_WRITE\r
+ #if SUPPORT_WRITE\r
+ if( FAT_int_DerefNode(Node) == 1 )\r
+ {\r
+ LOG("implint = %x", implint);\r
// Delete File\r
- if( Node->ImplInt & FAT_FLAG_DELETE ) {\r
+ if( implint & FAT_FLAG_DELETE ) {\r
+ Log_Debug("FAT", "Deallocating chain stating at 0x%07x", cluster);\r
// Since the node is marked, we only need to remove it's data\r
- Uint32 cluster = Node->Inode & 0xFFFFFFFF;\r
while( cluster != -1 )\r
- cluster = FAT_int_FreeCluster(Node->ImplPtr, cluster);\r
+ cluster = FAT_int_FreeCluster(disk, cluster);\r
}\r
- #endif\r
}\r
- \r
- Inode_UncacheNode(disk->inodeHandle, Node->Inode);\r
- return ;\r
+ #endif\r
+ LEAVE('-');\r
}\r
--- /dev/null
+/*
+ * Acess2 FAT12/16/32 Driver
+ * - By John Hodge (thePowersGang)
+ *
+ * fatio.c
+ * - FAT Manipulation and Cluster IO
+ */
+#define DEBUG 1
+#include <acess.h>
+#include <vfs.h>
+#include "common.h"
+
+// === CODE ===
+/**
+ * \fn Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)
+ * \brief Fetches a value from the FAT
+ */
+Uint32 FAT_int_GetFatValue(tFAT_VolInfo *Disk, Uint32 cluster)
+{
+ Uint32 val = 0;
+ Uint32 ofs;
+ ENTER("pDisk xCluster", Disk, cluster);
+ Mutex_Acquire( &Disk->lFAT );
+ #if CACHE_FAT
+ if( Disk->ClusterCount <= giFAT_MaxCachedClusters )
+ {
+ val = Disk->FATCache[cluster];
+ if(Disk->type == FAT12 && val == EOC_FAT12) val = -1;
+ if(Disk->type == FAT16 && val == EOC_FAT16) val = -1;
+ if(Disk->type == FAT32 && val == EOC_FAT32) val = -1;
+ }
+ else
+ {
+ #endif
+ ofs = Disk->bootsect.resvSectCount*512;
+ if(Disk->type == FAT12) {
+ VFS_ReadAt(Disk->fileHandle, ofs+(cluster/2)*3, 3, &val);
+ LOG("3 bytes at 0x%x are (Uint32)0x%x", ofs+(cluster/2)*3, val);
+ val = (cluster & 1) ? (val>>12) : (val & 0xFFF);
+ if(val == EOC_FAT12) val = -1;
+ } else if(Disk->type == FAT16) {
+ VFS_ReadAt(Disk->fileHandle, ofs+cluster*2, 2, &val);
+ if(val == EOC_FAT16) val = -1;
+ } else {
+ VFS_ReadAt(Disk->fileHandle, ofs+cluster*4, 4, &val);
+ if(val == EOC_FAT32) val = -1;
+ }
+ #if CACHE_FAT
+ }
+ #endif /*CACHE_FAT*/
+ Mutex_Release( &Disk->lFAT );
+ LEAVE('x', val);
+ return val;
+}
+
+#if SUPPORT_WRITE
+/**
+ * \brief Allocate a new cluster
+ */
+Uint32 FAT_int_AllocateCluster(tFAT_VolInfo *Disk, Uint32 Previous)
+{
+ Uint32 ret = -1;
+
+ #if CACHE_FAT
+ if( Disk->ClusterCount <= giFAT_MaxCachedClusters )
+ {
+ int bFoundCluster = 0;
+ Uint32 eoc;
+
+ switch(Disk->type)
+ {
+ case FAT12: eoc = EOC_FAT12; break;
+ case FAT16: eoc = EOC_FAT16; break;
+ case FAT32: eoc = EOC_FAT32; break;
+ default: return 0;
+ }
+
+ Mutex_Acquire(&Disk->lFAT);
+ if( Previous != -1 )
+ {
+ for(ret = Previous; ret < Disk->ClusterCount; ret++)
+ {
+ if(Disk->FATCache[ret] != 0) {
+ bFoundCluster = 1;
+ break;
+ }
+ }
+ }
+ if( !bFoundCluster )
+ {
+ for(ret = 0; ret < Previous; ret++)
+ {
+ if(Disk->FATCache[ret] == 0) {
+ bFoundCluster = 1;
+ break;
+ }
+ }
+ }
+
+ if(bFoundCluster)
+ {
+ Disk->FATCache[ret] = eoc;
+ if( Previous != -1 )
+ Disk->FATCache[Previous] = ret;
+ }
+ else
+ {
+ ret = 0;
+ }
+
+ Mutex_Release(&Disk->lFAT);
+ LOG("Allocated cluster %x", ret);
+ return ret;
+ }
+ else
+ {
+ #endif
+ Uint32 val = 0;
+ Uint32 base = Disk->bootsect.resvSectCount*512;
+ int block = 0, block_ofs = 0;
+ int first_block;
+ const int block_size = 512*3;
+ const int ents_per_block_12 = block_size * 2 / 3; // 1.5 bytes per entry
+// const int ents_per_block_16 = block_size / 2; // 2 bytes per entry
+// const int ents_per_block_32 = block_size / 4; // 4 bytes per entry
+ int block_count_12 = DivUp(Disk->ClusterCount, ents_per_block_12);
+ Uint8 sector_data[block_size+1];
+ sector_data[block_size] = 0;
+
+ Mutex_Acquire(&Disk->lFAT);
+ switch(Disk->type)
+ {
+ case FAT12:
+ if( Previous != -1 )
+ block = Previous / ents_per_block_12;
+ else
+ block = 0;
+ first_block = block;
+
+ // Search within the same block as the previous cluster first
+ do {
+ VFS_ReadAt(Disk->fileHandle, base + block*block_size, block_size, sector_data);
+ for( block_ofs = 0; block_ofs < ents_per_block_12; block_ofs ++ )
+ {
+ Uint32 *valptr = (void*)( sector_data + block_ofs / 2 * 3 );
+ int bitofs = 12 * (block_ofs % 2);
+// LOG("%i:%i - FAT Ent 0x%03x", block, block_ofs, (*valptr>>bitofs) & 0xFFF);
+ if( ((*valptr >> bitofs) & 0xFFF) == 0 ) {
+ // Found a free cluster
+ *valptr |= EOC_FAT12 << bitofs;
+ ret = block * ents_per_block_12 + block_ofs;
+ break;
+ }
+ }
+ // Check for early break from the above loop
+ if( block_ofs != ents_per_block_12 )
+ break;
+
+ // Next block please
+ block ++;
+ if( block == block_count_12 )
+ block = 0;
+ } while( block != first_block );
+
+ if( ret != 0 ) // TODO: Could cluster 0 be valid?
+ {
+ // Write back changes to this part of the FAT
+ VFS_WriteAt(Disk->fileHandle, base + block, block_size, sector_data);
+
+ // Note the new cluster in the chain
+ if( Previous != -1 )
+ {
+ LOG("Updating cluster %x to point to %x (offset %x)", Previous, ret,
+ base + (Previous>>1)*3);
+ VFS_ReadAt(Disk->fileHandle, base + (Previous>>1)*3, 3, &val);
+ if( Previous & 1 ) {
+ val &= 0x000FFF;
+ val |= ret << 12;
+ }
+ else {
+ val &= 0xFFF000;
+ val |= ret << 0;
+ }
+ VFS_WriteAt(Disk->fileHandle, base + (Previous>>1)*3, 3, &val);
+ }
+ }
+ break;
+ case FAT16:
+ Log_Warning("FAT", "TODO: Implement cluster allocation with FAT16");
+// VFS_ReadAt(Disk->fileHandle, ofs+Previous*2, 2, &ret);
+// VFS_WriteAt(Disk->fileHandle, ofs+ret*2, 2, &eoc);
+ break;
+ case FAT32:
+ Log_Warning("FAT", "TODO: Implement cluster allocation with FAT32");
+// VFS_ReadAt(Disk->fileHandle, ofs+Previous*4, 4, &ret);
+// VFS_WriteAt(Disk->fileHandle, ofs+ret*4, 4, &eoc);
+ break;
+ }
+ Mutex_Release(&Disk->lFAT);
+ LOG("Allocated cluster %x", ret);
+ return ret;
+ #if CACHE_FAT
+ }
+ #endif
+}
+
+/**
+ * \brief Free's a cluster
+ * \return The original contents of the cluster
+ */
+Uint32 FAT_int_FreeCluster(tFAT_VolInfo *Disk, Uint32 Cluster)
+{
+ Uint32 ret;
+
+ if( Cluster < 2 || Cluster > Disk->ClusterCount ) // oops?
+ {
+ Log_Notice("FAT", "Cluster 0x%x is out of range (2 ... 0x%x)",
+ Cluster, Disk->ClusterCount-1);
+ return -1;
+ }
+
+ Mutex_Acquire(&Disk->lFAT);
+ #if CACHE_FAT
+ if( Disk->ClusterCount <= giFAT_MaxCachedClusters )
+ {
+
+ ret = Disk->FATCache[Cluster];
+ Disk->FATCache[Cluster] = 0;
+ }
+ else
+ {
+ #endif
+ Uint32 val = 0;
+ Uint32 ofs = Disk->bootsect.resvSectCount*512;
+ switch(Disk->type)
+ {
+ case FAT12:
+ VFS_ReadAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val);
+ val = LittleEndian32(val);
+ if( Cluster & 1 ) {
+ ret = (val >> 12) & 0xFFF;
+ val &= 0xFFF;
+ }
+ else {
+ ret = val & 0xFFF;
+ val &= 0xFFF000;
+ }
+ val = LittleEndian32(val);
+ VFS_WriteAt(Disk->fileHandle, ofs+(Cluster>>1)*3, 3, &val);
+ break;
+ case FAT16:
+ VFS_ReadAt(Disk->fileHandle, ofs+Cluster*2, 2, &ret);
+ ret = LittleEndian16(ret);
+ val = 0;
+ VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 2, &val);
+ break;
+ case FAT32:
+ VFS_ReadAt(Disk->fileHandle, ofs+Cluster*4, 4, &ret);
+ ret = LittleEndian32(ret);
+ val = 0;
+ VFS_WriteAt(Disk->fileHandle, ofs+Cluster*2, 4, &val);
+ break;
+ }
+ #if CACHE_FAT
+ }
+ #endif
+ Mutex_Release(&Disk->lFAT);
+ LOG("ret = %07x, eoc = %07x", ret, EOC_FAT12);
+ if(ret == 0) {
+ Log_Notice("FAT", "Cluster 0x%x was already free", Cluster);
+ return -1;
+ }
+ if(Disk->type == FAT12 && ret == EOC_FAT12) ret = -1;
+ if(Disk->type == FAT16 && ret == EOC_FAT16) ret = -1;
+ if(Disk->type == FAT32 && ret == EOC_FAT32) ret = -1;
+ LOG("ret = %07x", ret);
+ return ret;
+}
+#endif
+
+/*
+ * ====================
+ * Cluster IO
+ * ====================
+ */
+/**
+ * \brief Read a cluster
+ * \param Disk Disk (Volume) to read from
+ * \param Length Length to read
+ * \param Buffer Destination for read data
+ */
+void FAT_int_ReadCluster(tFAT_VolInfo *Disk, Uint32 Cluster, int Length, void *Buffer)
+{
+ ENTER("pDisk xCluster iLength pBuffer", Disk, Cluster, Length, Buffer);
+ VFS_ReadAt(
+ Disk->fileHandle,
+ (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )
+ * Disk->bootsect.bps,
+ Length,
+ Buffer
+ );
+ LEAVE('-');
+}
+
+#if SUPPORT_WRITE
+/**
+ * \brief Write a cluster to disk
+ */
+void FAT_int_WriteCluster(tFAT_VolInfo *Disk, Uint32 Cluster, const void *Buffer)
+{
+ ENTER("pDisk xCluster pBuffer", Disk, Cluster, Buffer);
+ VFS_WriteAt(
+ Disk->fileHandle,
+ (Disk->firstDataSect + (Cluster-2)*Disk->bootsect.spc )
+ * Disk->bootsect.bps,
+ Disk->BytesPerCluster,
+ Buffer
+ );
+ LEAVE('-');
+}
+#endif
#ifndef _FS_FAT_H_\r
#define _FS_FAT_H_\r
\r
+#define FAT16_MIN_SECTORS 4085\r
+#define FAT32_MIN_CLUSTERS 65525\r
+\r
// === On Disk Structures ===\r
/**\r
* \struct fat_bootsect_s\r
* \}\r
*/\r
\r
-/**\r
- * \brief Internal IDs for FAT types\r
- */\r
-enum eFatType\r
-{\r
- FAT12, //!< FAT12 Volume\r
- FAT16, //!< FAT16 Volume\r
- FAT32, //!< FAT32 Volume\r
-};\r
-\r
/**\r
* \name End of Cluster marks\r
* \brief FAT values that indicate the end of a cluster chain in\r
typedef struct fat_filetable_s fat_filetable;\r
typedef struct fat_longfilename_s fat_longfilename;\r
\r
-// === Memory Structures ===\r
-/**\r
- * \struct drv_fat_volinfo_s\r
- * \brief Representation of a volume in memory\r
- */\r
-struct drv_fat_volinfo_s\r
-{\r
- int fileHandle; //!< File Handle\r
- int type; //!< FAT Type. See eFatType\r
- char name[12]; //!< Volume Name (With NULL Terminator)\r
- tMutex lFAT; //!< Lock to prevent double-writing to the FAT\r
- Uint32 firstDataSect; //!< First data sector\r
- Uint32 rootOffset; //!< Root Offset (clusters)\r
- Uint32 ClusterCount; //!< Total Cluster Count\r
- fat_bootsect bootsect; //!< Boot Sector\r
- tVFS_Node rootNode; //!< Root Node\r
- int BytesPerCluster;\r
- int inodeHandle; //!< Inode Cache Handle\r
- #if CACHE_FAT\r
- Uint32 *FATCache; //!< FAT Cache\r
- #endif\r
-};\r
-\r
-typedef struct drv_fat_volinfo_s tFAT_VolInfo;\r
-\r
#endif\r
--- /dev/null
+/*
+ * Acess2 FAT12/16/32 Driver
+ * - By John Hodge (thePowersGang)
+ *
+ * nodecache.c
+ * - FAT-Specific node caching
+ */
+#include <acess.h>
+#include <vfs.h>
+#include "common.h"
+
+// === PROTOTYPES ===
+extern tVFS_Node *FAT_int_CacheNode(tFAT_VolInfo *Disk, const tVFS_Node *Node);
+
+// === CODE ===
+tTime FAT_int_GetAcessTimestamp(Uint16 Date, Uint16 Time, Uint8 MS)
+{
+ return MS * 10 + timestamp(
+ // Seconds Minutes Hours
+ (Time & 0x1F) * 2, (Time >> 5) & 0x3F, (Time >> 11) & 0x1F,
+ // Day Month Year
+ (Date & 0x1F) - 1, ((Date >> 5) & 0xF) - 1, 1980 + ((Date >> 9) & 0xFF)
+ );
+}
+
+void FAT_int_GetFATTimestamp(tTime AcessTimestamp, Uint16 *Date, Uint16 *Time, Uint8 *MS)
+{
+ int y, m, d;
+ int h, min, s, ms;
+ format_date(AcessTimestamp, &y, &m, &d, &h, &min, &s, &ms);
+ if(Date)
+ *Date = (d + 1) | ((m + 1) << 5) | ((y - 1980) << 9);
+ if(Time)
+ *Time = (s / 2) | (min << 5) | (h << 11);
+ if(MS)
+ *MS = (ms / 10) + (s & 1) * 100;
+}
+
+/**
+ * \brief Creates a tVFS_Node structure for a given file entry
+ * \param Parent Parent directory VFS node
+ * \param Entry File table entry for the new node
+ */
+tVFS_Node *FAT_int_CreateNode(tVFS_Node *Parent, fat_filetable *Entry)
+{
+ tVFS_Node node;
+ tVFS_Node *ret;
+ tFAT_VolInfo *disk = Parent->ImplPtr;
+
+ ENTER("pParent pEntry", Parent, Entry);
+ LOG("disk = %p", disk);
+
+ if( (ret = FAT_int_GetNode(disk, Entry->cluster | (Entry->clusterHi<<16))) ) {
+ LEAVE('p', ret);
+ return ret;
+ }
+
+ memset(&node, 0, sizeof(tVFS_Node));
+
+ // Set Other Data
+ // 0-27: Cluster, 32-59: Parent Cluster
+ node.Inode = Entry->cluster | (Entry->clusterHi<<16) | (Parent->Inode << 32);
+ LOG("node.Inode = %llx", node.Inode);
+ node.ImplInt = 0;
+ // Disk Pointer
+ node.ImplPtr = disk;
+ node.Size = Entry->size;
+ LOG("Entry->size = %i", Entry->size);
+ // root:root
+ node.UID = 0; node.GID = 0;
+ node.NumACLs = 1;
+
+ node.Flags = 0;
+ if(Entry->attrib & ATTR_DIRECTORY) node.Flags |= VFS_FFLAG_DIRECTORY;
+ if(Entry->attrib & ATTR_READONLY) {
+ node.Flags |= VFS_FFLAG_READONLY;
+ node.ACLs = &gVFS_ACL_EveryoneRX; // R-XR-XR-X
+ }
+ else {
+ node.ACLs = &gVFS_ACL_EveryoneRWX; // RWXRWXRWX
+ }
+
+ // Create timestamps
+ node.CTime = FAT_int_GetAcessTimestamp(Entry->cdate, Entry->ctime, Entry->ctimems);
+ node.MTime = FAT_int_GetAcessTimestamp(Entry->mdate, Entry->mtime, 0);
+ node.ATime = FAT_int_GetAcessTimestamp(Entry->adate, 0, 0);
+
+ // Set pointers
+ if(node.Flags & VFS_FFLAG_DIRECTORY) {
+ //Log_Debug("FAT", "Directory %08x has size 0x%x", node.Inode, node.Size);
+ node.Type = &gFAT_DirType;
+ node.Size = -1;
+ }
+ else {
+ node.Type = &gFAT_FileType;
+ }
+
+ // TODO: Cache node
+ ret = FAT_int_CacheNode(disk, &node);
+ LEAVE('p', ret);
+ return ret;
+}
+
+tVFS_Node *FAT_int_CreateIncompleteDirNode(tFAT_VolInfo *Disk, Uint32 Cluster)
+{
+ if( Cluster == Disk->rootOffset )
+ return &Disk->rootNode;
+
+ // If the directory isn't in the cache, what do?
+ // - we want to lock it such that we don't collide, but don't want to put crap data in the cache
+ // - Put a temp node in with a flag that indicates it's incomplete?
+
+ Mutex_Acquire(&Disk->lNodeCache);
+ tFAT_CachedNode *cnode;
+
+ for(cnode = Disk->NodeCache; cnode; cnode = cnode->Next)
+ {
+ if( (cnode->Node.Inode & 0xFFFFFFFF) == Cluster ) {
+ cnode->Node.ReferenceCount ++;
+ Mutex_Release(&Disk->lNodeCache);
+ return &cnode->Node;
+ }
+ }
+
+ // Create a temporary node?
+ Log_Warning("FAT", "TODO: Impliment FAT_int_CreateIncompleteDirNode()");
+
+ Mutex_Release(&Disk->lNodeCache);
+ return NULL;
+}
+
+tVFS_Node *FAT_int_GetNode(tFAT_VolInfo *Disk, Uint32 Cluster)
+{
+ if( Cluster == Disk->rootOffset )
+ return &Disk->rootNode;
+ Mutex_Acquire(&Disk->lNodeCache);
+ tFAT_CachedNode *cnode;
+
+ for(cnode = Disk->NodeCache; cnode; cnode = cnode->Next)
+ {
+ if( (cnode->Node.Inode & 0xFFFFFFFF) == Cluster ) {
+ cnode->Node.ReferenceCount ++;
+ Mutex_Release(&Disk->lNodeCache);
+ return &cnode->Node;
+ }
+ }
+
+ Mutex_Release(&Disk->lNodeCache);
+ return NULL;
+}
+
+tVFS_Node *FAT_int_CacheNode(tFAT_VolInfo *Disk, const tVFS_Node *Node)
+{
+ tFAT_CachedNode *cnode, *prev = NULL;
+ Mutex_Acquire(&Disk->lNodeCache);
+
+ for(cnode = Disk->NodeCache; cnode; prev = cnode, cnode = cnode->Next )
+ {
+ if( cnode->Node.Inode == Node->Inode ) {
+ cnode->Node.ReferenceCount ++;
+ Mutex_Release(&Disk->lNodeCache);
+ return &cnode->Node;
+ }
+ }
+
+ cnode = malloc(sizeof(tFAT_CachedNode));
+ cnode->Next = NULL;
+ memcpy(&cnode->Node, Node, sizeof(tVFS_Node));
+ cnode->Node.ReferenceCount = 1;
+
+ if( prev )
+ prev->Next = cnode;
+ else
+ Disk->NodeCache = cnode;
+
+ Mutex_Release(&Disk->lNodeCache);
+ return &cnode->Node;
+}
+
+int FAT_int_DerefNode(tVFS_Node *Node)
+{
+ tFAT_VolInfo *Disk = Node->ImplPtr;
+ tFAT_CachedNode *cnode, *prev = NULL;
+ int bFreed = 0;
+
+ if( Node == &Disk->rootNode )
+ return 0;
+
+ Mutex_Acquire(&Disk->lNodeCache);
+ Node->ReferenceCount --;
+ for(cnode = Disk->NodeCache; cnode; prev = cnode, cnode = cnode->Next )
+ {
+ if(Node == &cnode->Node) {
+ if(prev)
+ prev->Next = cnode->Next;
+ else
+ Disk->NodeCache = cnode->Next;
+ break;
+ }
+ }
+ if(Node->ReferenceCount == 0 && cnode) {
+ // Already out of the list :)
+ free(cnode->Node.Data);
+ free(cnode);
+ bFreed = 1;
+ }
+ Mutex_Release(&Disk->lNodeCache);
+ if( !cnode ) {
+ // Not here?
+ return -1;
+ }
+
+ return bFreed;
+}
+
+void FAT_int_ClearNodeCache(tFAT_VolInfo *Disk)
+{
+ // TODO: In theory when this is called, all handles will be closed
+}
}
}
}
+#$Dir "Keen5" {
+# File "keen5e" "/home/tpg/Projects/AcessPorts/omnispeak/bin/keen5e"
+# File "EGADICT.CK5" "/home/tpg/Projects/AcessPorts/omnispeak/bin/original/EGADICT.CK5"
+# File "EGAGRAPH.CK5" "/home/tpg/Projects/AcessPorts/omnispeak/bin/original/EGAGRAPH.CK5"
+# File "EGAHEAD.CK5" "/home/tpg/Projects/AcessPorts/omnispeak/bin/original/EGAHEAD.CK5"
+# File "GAMEMAPS.CK5" "/home/tpg/Projects/AcessPorts/omnispeak/bin/original/GAMEMAPS.CK5"
+# File "GFXINFOE.CK5" "/home/tpg/Projects/AcessPorts/omnispeak/bin/original/GFXINFOE.CK5"
+# File "MAPHEAD.CK5" "/home/tpg/Projects/AcessPorts/omnispeak/bin/original/MAPHEAD.CK5"
+# File "TILEINFO.CK5" "/home/tpg/Projects/AcessPorts/omnispeak/bin/original/TILEINFO.CK5"
+#}
{
tInterface *iface;
tIPv6Header *hdr = Buffer;
- int ret, dataLength;
+ int ret;
char *dataPtr;
Uint8 nextHeader;
// Process Options
nextHeader = hdr->NextHeader;
dataPtr = hdr->Data;
- dataLength = hdr->PayloadLength;
for( ;; )
{
struct {
void *data = IPStack_Buffer_CompactBuffer(Buffer, &len);
tEthernetHeader *hdr = (void*)data;
- int i;
- Uint32 checksum;
if(len < sizeof(tEthernetHeader)) {
Log_Log("Net Link", "Recieved an undersized packet (%i < %i)",
hdr->Dest.B[3], hdr->Dest.B[4], hdr->Dest.B[5],
ntohs(hdr->Type)
);
- checksum = *(Uint32*)&hdr->Data[len-sizeof(tEthernetHeader)-4];
+// Uint32 checksum = *(Uint32*)(data + len + 4);
//Log_Log("NET", "Checksum 0x%08x", checksum);
// TODO: Check checksum
// Check if there is a registered callback for this packet type
+ int i;
for( i = giRegisteredTypes; i--; )
{
if(gaRegisteredTypes[i].Type == ntohs(hdr->Type)) break;
// === PROTOTYPES ===
int Keyboard_Install(char **Arguments);
-void Keyboard_Cleanup(void);
+ int Keyboard_Cleanup(void);
// - Internal
tKeymap *Keyboard_LoadMap(const char *Name);
void Keyboard_FreeMap(tKeymap *Keymap);
/**
* \brief Pre-unload cleanup function
*/
-void Keyboard_Cleanup(void)
+int Keyboard_Cleanup(void)
{
// TODO: Do I need this?
+ return 0;
}
// --- Map Management ---
// === PROTOTYPES ===
int Mouse_Install(char **Arguments);
-void Mouse_Cleanup(void);
+ int Mouse_Cleanup(void);
// - "User" side
char *Mouse_Root_ReadDir(tVFS_Node *Node, int Pos);
tVFS_Node *Mouse_Root_FindDir(tVFS_Node *Node, const char *Name);
/**
* \brief Pre-unload cleanup function
*/
-void Mouse_Cleanup(void)
+int Mouse_Cleanup(void)
{
+ return 0;
}
// --- VFS Interface ---
{
const char *Name;
- int (*Read)(void *, Uint64, size_t, void *);
- int (*Write)(void *, Uint64, size_t, const void *);
+ int (*Read)(void *, Uint64, size_t, void *);
+ int (*Write)(void *, Uint64, size_t, const void *);
+ void (*Cleanup)(void *);
};
* lvm.h
* - LVM Core definitions
*/
-#define DEBUG 0
+#define DEBUG 1
#define VERSION VER2(0,1)
#include "lvm_int.h"
#include <fs_devfs.h>
// === PROTOTYPES ===
// ---
int LVM_Initialise(char **Arguments);
-void LVM_Cleanup(void);
+ int LVM_Cleanup(void);
// ---
char *LVM_Root_ReadDir(tVFS_Node *Node, int ID);
tVFS_Node *LVM_Root_FindDir(tVFS_Node *Node, const char *Name);
return 0;
}
-void LVM_Cleanup(void)
+int LVM_Cleanup(void)
{
+ // Attempt to destroy all volumes
+ tLVM_Vol *vol, *prev = NULL, *next;
+ // TODO: Locks?
+ for( vol = gpLVM_FirstVolume; vol; prev = vol, vol = next )
+ {
+ next = vol->Next;
+ int nFree = 0;
+
+ for( int i = 0; i < vol->nSubVolumes; i ++ )
+ {
+ tLVM_SubVolume *sv;
+ sv = vol->SubVolumes[i];
+ if( sv == NULL ) {
+ nFree ++;
+ continue;
+ }
+
+ Mutex_Acquire(&sv->Node.Lock);
+ if(sv->Node.ReferenceCount == 0) {
+ nFree ++;
+ vol->SubVolumes[i] = NULL;
+ }
+ Mutex_Release(&sv->Node.Lock);
+
+ Mutex_Acquire(&sv->Node.Lock);
+ LOG("Removed subvolume %s:%s", vol->Name, sv->Name);
+ free(sv);
+ }
+
+ if( nFree != vol->nSubVolumes )
+ continue ;
+
+ if(prev)
+ prev->Next = next;
+ else
+ gpLVM_FirstVolume = next;
+
+ Mutex_Acquire(&vol->DirNode.Lock);
+ Mutex_Acquire(&vol->VolNode.Lock);
+ if( vol->Type->Cleanup )
+ vol->Type->Cleanup( vol->Ptr );
+ LOG("Removed volume %s", vol->Name);
+ free(vol);
+ }
+
+ if( gpLVM_FirstVolume )
+ return EBUSY;
+
+ return EOK;
}
// --------------------------------------------------------------------
real_vol->Next = NULL;
real_vol->Type = Type;
real_vol->Ptr = Ptr;
+ real_vol->BlockSize = BlockSize;
real_vol->BlockCount = BlockCount;
real_vol->nSubVolumes = dummy_vol.nSubVolumes;
real_vol->SubVolumes = (void*)( real_vol->Name + strlen(Name) + 1 );
const int ciMaxPacketSize = 0x400;
struct sDeviceRequest req;
int bToggle = 0;
- void *final;
int dest = Dev->Address*16 + Endpoint;
ENTER("pDev xdest iType iIndex iLength pDest",
Threads_ClearEvent(THREAD_EVENT_SHORTWAIT);
LOG("OUT (Status)");
- final = Dev->Host->HostDef->ControlOUT(
+ Dev->Host->HostDef->ControlOUT(
Dev->Host->Ptr, dest, 1,
USB_int_WakeThread, Proc_GetCurThread(),
NULL, 0
// === PROTOTYPES ===
int MSC_Initialise(char **Arguments);
-void MSC_Cleanup(void);
+ int MSC_Cleanup(void);
void MSC_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen);
void MSC_DataIn(tUSBInterface *Dev, int EndPt, int Length, void *Data);
// --- Internal Helpers
return 0;
}
-void MSC_Cleanup(void)
+int MSC_Cleanup(void)
{
+ return 0;
}
void MSC_DeviceConnected(tUSBInterface *Dev, void *Descriptors, size_t DescriptorsLen)
ch.BGCol = (Colour & 0x0F0000) >> (16-8);
ch.BGCol |= (Colour & 0x000F00) >> (8-4);
ch.BGCol |= (Colour & 0x00000F);
+ ch.FGCol = 0;
word = VGA_int_GetWord(&ch);
Log("Fill (%i,%i) %ix%i with 0x%x", X, Y, W, H, word);
while [ $# -ne 0 ]; do
case $1 in
+ -raspberrypi)
+ _SYSTEM="versatilepb"
+ QEMU_PARAMS=$QEMU_PARAMS" -cpu arm1176 -m 192 -localtime"
+ _KERNEL=Acess2.armv6-raspberrypi.bin
+ ;;
-gdb)
QEMU_PARAMS=$QEMU_PARAMS" -s -S"
;;
esac
shift
done
-QEMU_PARAMS="-M $_SYSTEM -kernel $_KERNEL -net nic -net $_NETTYPE"$QEMU_PARAMS
+QEMU_PARAMS="-M $_SYSTEM -kernel KernelLand/$_KERNEL -net nic -net $_NETTYPE"$QEMU_PARAMS
# /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
--- /dev/null
+
+TARGET := $(shell gcc -v 2>&1 | grep Targ | awk '{print $$2}')
+
+include ../../../Makefile.Version.cfg
+-include Makefile.BuildNum
+ifeq ($(BUILD_NUM),)
+BUILD_NUM = 1
+endif
+
+
+KERNEL_SRC = ../../../KernelLand/Kernel/
+MODULE_SRC = ../../../KernelLand/Modules/
+
+BIN = ../DiskTool
+# Kernel Sources (compiled with -ffreestanding)
+K_OBJ := lib.o
+K_OBJ += vfs/main.o vfs/open.o vfs/acls.o vfs/io.o vfs/dir.o
+K_OBJ += vfs/nodecache.o vfs/mount.o vfs/memfile.o # vfs/select.o
+K_OBJ += vfs/fs/root.o vfs/fs/devfs.o
+K_OBJ += drvutil_disk.o drv/proc.o
+# Modules
+MODULES := Storage/LVM Filesystems/FAT Filesystems/Ext2
+# Local kernel soruces (same as above, but located in same directory as Makefile)
+L_OBJ = vfs_handles.o threads.o nativefs.o time.o actions.o
+# Native Sources (compiled as usual)
+N_OBJ = main.o script.o logging.o helpers.o
+
+# Compilation Options
+CFLAGS := -Wall -std=gnu99 -g -Werror
+CPPFLAGS := -I include/
+K_CPPFLAGS := -I $(KERNEL_SRC)include -I $(MODULE_SRC)
+LDFLAGS += -Wl,--defsym,__buildnum=$(BUILD_NUM) -g
+
+BUILDINFO_OBJ := obj/$(TARGET)/buildinfo.o
+BUILDINFO_SRC := $(BUILDINFO_OBJ:%.o=%.c)
+
+# ====================
+# == Start of Magic ==
+# ====================
+# -- Load modules ---
+$(foreach module,$(MODULES), $(eval include $(MODULE_SRC)$(module)/Makefile) $(eval M_OBJ += $(addprefix $(module)/,$(OBJ))) )
+
+# -- Apply Prefixes to object paths
+OBJ_PREFIX = obj/$(TARGET)/
+K_OBJ_PREFIX = $(OBJ_PREFIX)_Kernel/
+M_OBJ_PREFIX = $(OBJ_PREFIX)_Module/
+K_OBJ := $(addprefix $(K_OBJ_PREFIX),$(K_OBJ))
+M_OBJ := $(addprefix $(M_OBJ_PREFIX),$(M_OBJ))
+L_OBJ := $(addprefix $(OBJ_PREFIX),$(L_OBJ))
+N_OBJ := $(addprefix $(OBJ_PREFIX),$(N_OBJ))
+
+OBJ := $(N_OBJ) $(L_OBJ) $(K_OBJ) $(M_OBJ) $(BUILDINFO_OBJ)
+
+DEPFILES = $(filter %.o,$(OBJ))
+DEPFILES := $(DEPFILES:%=%.dep)
+
+
+.PHONY: all clean
+
+all: $(BIN)
+
+clean:
+ $(RM) -f $(OBJ) $(DEPFILES) $(BIN)
+
+$(BIN): $(OBJ)
+ @echo [CC Link] -o $(BIN)
+ @$(CC) -o $(BIN) $(OBJ) $(LDFLAGS)
+ @echo BUILD_NUM = $$(( $(BUILD_NUM) + 1 )) > Makefile.BuildNum
+
+$(M_OBJ): $(M_OBJ_PREFIX)%.o: $(MODULE_SRC)%.c
+ @mkdir -p $(dir $@)
+ @echo [CC Module] -o $@
+ @$(CC) -c $< -o $@ -ffreestanding $(CFLAGS) $(CPPFLAGS) $(K_CPPFLAGS) -MMD -MP -MF
[email protected]
+
+$(K_OBJ): $(K_OBJ_PREFIX)%.o: $(KERNEL_SRC)%.c
+ @mkdir -p $(dir $@)
+ @echo [CC Kernel] -o $@
+ @$(CC) -c $< -o $@ -ffreestanding $(CFLAGS) $(CPPFLAGS) $(K_CPPFLAGS) -MMD -MP -MF
[email protected]
+
+$(L_OBJ): $(OBJ_PREFIX)%.o: %.c
+ @mkdir -p $(dir $@)
+ @echo [CC Local] -o $@
+ @$(CC) -c $< -o $@ -ffreestanding $(CFLAGS) $(CPPFLAGS) $(K_CPPFLAGS) -MMD -MP -MF
[email protected]
+
+$(N_OBJ): $(OBJ_PREFIX)%.o: %.c
+ @mkdir -p $(dir $@)
+ @echo [CC Native] -o $@
+
+# Hacky buildinfo.c file
+$(BUILDINFO_SRC): $(filter-out $(BUILDINFO_OBJ), $(OBJ)) Makefile
+ @echo "" > $@
+ @echo "const char gsKernelVersion[] = \"$(ACESS_VERSION)\";" >> $@
+ @echo "const char gsGitHash[] = \""`git log -n 1 | head -n 1 | awk '{print $$2}'`"\";" >> $@
+ @echo "const int giBuildNumber = $(BUILD_NUM);" >> $@
+$(BUILDINFO_OBJ): $(BUILDINFO_SRC)
+ @echo [CC] -o $@
+ @$(CC) -o $@ -c $< $(CFLAGS) $(CPPFLAGS)
+
+$(OBJ): Makefile
+
+-include $(DEPFILES)
--- /dev/null
+/*
+ * Acess2 DiskTool
+ * - By John Hodge (thePowersGang)
+ *
+ * actions.c
+ * - High level actions that call the VFS
+ * # Kernel-space compiled
+ */
+#include <acess.h>
+#include <disktool_common.h>
+#include <Storage/LVM/include/lvm.h>
+
+// === IMPORTS ===
+extern int NativeFS_Install(char **Arguments);
+extern int LVM_Cleanup(void);
+
+// === PROTOTYPES ===
+void DiskTool_Initialise(void) __attribute__((constructor(101)));
+void DiskTool_Cleanup(void);
+ int DiskTool_int_TranslateOpen(const char *File, int Mode);
+ int DiskTool_LVM_Read(void *Handle, Uint64 Block, size_t BlockCount, void *Dest);
+ int DiskTool_LVM_Write(void *Handle, Uint64 Block, size_t BlockCount, const void *Dest);
+void DiskTool_LVM_Cleanup(void *Handle);
+
+// === GLOBALS ===
+tLVM_VolType gDiskTool_VolumeType = {
+ .Name = "DiskTool",
+ .Read = DiskTool_LVM_Read,
+ .Write = DiskTool_LVM_Write,
+ .Cleanup = DiskTool_LVM_Cleanup
+};
+
+// === CODE ===
+void DiskTool_Initialise(void)
+{
+ VFS_Init();
+ NativeFS_Install(NULL);
+ VFS_MkDir("/Native");
+ VFS_Mount("/", "/Native", "nativefs", "");
+}
+
+void DiskTool_Cleanup(void)
+{
+ int vfs_rv, lvm_rv;
+ int nNochangeLoop = 0;
+ // Unmount all
+ do {
+ lvm_rv = LVM_Cleanup();
+ vfs_rv = VFS_UnmountAll();
+ Log_Debug("DiskTool", "Unmounted %i volumes", vfs_rv);
+ if( vfs_rv == 0 && lvm_rv == 0 ) {
+ nNochangeLoop ++;
+ if(nNochangeLoop == 2) {
+ Log_Error("DiskTool", "Possible handle leak");
+ break;
+ }
+ }
+ else {
+ nNochangeLoop = 0;
+ }
+ }
+ while( vfs_rv >= 0 || lvm_rv != 0 );
+}
+
+int DiskTool_RegisterLVM(const char *Identifier, const char *Path)
+{
+ int fd = DiskTool_int_TranslateOpen(Path, VFS_OPENFLAG_READ|VFS_OPENFLAG_WRITE);
+ if(fd == -1)
+ return -1;
+ VFS_Seek(fd, 0, SEEK_END);
+ LVM_AddVolume( &gDiskTool_VolumeType, Identifier, (void*)(tVAddr)fd, 512, VFS_Tell(fd)/512);
+ return 0;
+}
+
+int DiskTool_MountImage(const char *Identifier, const char *Path)
+{
+ // Validate Identifier and make mountpoint string
+ char mountpoint[sizeof("/Mount/") + strlen(Identifier) + 1];
+ strcpy(mountpoint, "/Mount/");
+ strcat(mountpoint, Identifier);
+
+ // Translate path
+ size_t tpath_len = DiskTool_int_TranslatePath(NULL, Path);
+ if(tpath_len == -1)
+ return -1;
+ char tpath[tpath_len-1];
+ DiskTool_int_TranslatePath(tpath, Path);
+
+ // Call mount
+ VFS_MkDir(mountpoint);
+ // TODO: Detect filesystem?
+ return VFS_Mount(tpath, mountpoint, "fat", "");
+}
+
+int DiskTool_Copy(const char *Source, const char *Destination)
+{
+ int src = DiskTool_int_TranslateOpen(Source, VFS_OPENFLAG_READ);
+ if( src == -1 ) {
+ Log_Error("DiskTool", "Unable to open %s for reading", Source);
+ return -1;
+ }
+ int dst = DiskTool_int_TranslateOpen(Destination, VFS_OPENFLAG_WRITE|VFS_OPENFLAG_CREATE);
+ if( dst == -1 ) {
+ Log_Error("DiskTool", "Unable to open %s for writing", Destination);
+ VFS_Close(src);
+ return -1;
+ }
+
+ char buf[1024];
+ size_t len, total = 0;
+ while( (len = VFS_Read(src, sizeof(buf), buf)) == sizeof(buf) ) {
+ VFS_Write(dst, len, buf);
+ total += len;
+ }
+ VFS_Write(dst, len, buf), total += len;
+
+ Log_Notice("DiskTool", "Copied %i from %s to %s", total, Source, Destination);
+
+ VFS_Close(dst);
+ VFS_Close(src);
+
+ return 0;
+}
+
+int DiskTool_ListDirectory(const char *Directory)
+{
+ int fd = DiskTool_int_TranslateOpen(Directory, VFS_OPENFLAG_READ|VFS_OPENFLAG_DIRECTORY);
+ if(fd == -1) {
+// fprintf(stderr, "Can't open '%s'\n", Directory);
+ return -1;
+ }
+
+ Log("Directory listing of '%s'", Directory);
+
+ char name[256];
+ while( VFS_ReadDir(fd, name) )
+ {
+ Log("- %s", name);
+ }
+
+ VFS_Close(fd);
+
+ return 0;
+}
+
+int DiskTool_LVM_Read(void *Handle, Uint64 Block, size_t BlockCount, void *Dest)
+{
+ VFS_ReadAt( (int)(tVAddr)Handle, Block*512, BlockCount*512, Dest);
+ return 0;
+}
+int DiskTool_LVM_Write(void *Handle, Uint64 Block, size_t BlockCount, const void *Dest)
+{
+ VFS_WriteAt( (int)(tVAddr)Handle, Block*512, BlockCount*512, Dest);
+ return 0;
+}
+void DiskTool_LVM_Cleanup(void *Handle)
+{
+ VFS_Close( (int)(tVAddr)Handle );
+}
+
+// --- Internal helpers ---
+int DiskTool_int_TranslateOpen(const char *File, int Flags)
+{
+ size_t tpath_len = DiskTool_int_TranslatePath(NULL, File);
+ if(tpath_len == -1)
+ return -1;
+ char tpath[tpath_len-1];
+ DiskTool_int_TranslatePath(tpath, File);
+
+ return VFS_Open(tpath, Flags);
+}
+
--- /dev/null
+/*
+ * Acess2 DiskTool
+ * - By John Hodge (thePowersGang)
+ *
+ * helpers.c
+ */
+#include <stdlib.h>
+#include <acess_logging.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+
+// === GLOBALS ===
+char gsWorkingDirectory[1024];
+
+
+// === CODE ===
+size_t DiskTool_int_TranslatePath(char *Buffer, const char *Path)
+{
+ int len;
+ const char *colon = strchr(Path, ':');
+
+ if( Path[0] == '#' )
+ {
+ if(Buffer)
+ strcpy(Buffer, Path+1);
+ return strlen(Path) - 1;
+ }
+
+ if( colon )
+ {
+ const char *pos;
+ for(pos = Path; pos < colon; pos ++)
+ {
+ if( !isalpha(*pos) )
+ goto native_path;
+ }
+
+ len = strlen("/Mount/");
+ len += strlen(Path);
+ if( Buffer ) {
+ strcpy(Buffer, "/Mount/");
+ strncat(Buffer+strlen("/Mount/"), Path, colon - Path);
+ strcat(Buffer, colon + 1);
+ }
+ return len;
+ }
+
+native_path:
+
+ if( !gsWorkingDirectory[0] ) {
+ getcwd(gsWorkingDirectory, 1024);
+ }
+
+ len = strlen("/Native");
+ len += strlen( gsWorkingDirectory ) + 1;
+ len += strlen(Path);
+ if( Buffer ) {
+ strcpy(Buffer, "/Native");
+ strcat(Buffer, gsWorkingDirectory);
+ strcat(Buffer, "/");
+ strcat(Buffer, Path);
+ }
+ return len;
+}
--- /dev/null
+/*
+ * Acess2 DiskTool utility
+ * - By John Hodge (thePowersGang)
+ *
+ * include/acess.h
+ * - Mock kernel core header
+ */
+#ifndef _DISKTOOL__ACESS_H_
+#define _DISKTOOL__ACESS_H_
+
+#define CONCAT(x,y) x ## y
+#define EXPAND_CONCAT(x,y) CONCAT(x,y)
+#define STR(x) #x
+#define EXPAND_STR(x) STR(x)
+
+#define ASSERT(x) do{}while(0)
+
+extern char __buildnum[];
+#define BUILD_NUM ((int)(Uint)&__buildnum)
+extern const char gsGitHash[];
+
+#define BITS 32
+#define NULL ((void*)0)
+#include <stdint.h>
+
+typedef uintptr_t Uint;
+//typedef unsigned int size_t;
+#include <stddef.h>
+typedef uint64_t off_t;
+typedef char BOOL;
+
+
+typedef uint8_t Uint8;
+typedef uint16_t Uint16;
+typedef uint32_t Uint32;
+typedef uint64_t Uint64;
+
+typedef int8_t Sint8;
+typedef int16_t Sint16;
+typedef int32_t Sint32;
+typedef int64_t Sint64;
+
+typedef uintptr_t tVAddr;
+typedef uint32_t tPAddr;
+
+typedef uint32_t tUID;
+typedef uint32_t tGID;
+typedef uint32_t tTID;
+
+// NOTE: Since this is single-threaded (for now) mutexes can be implimented as simple locks
+typedef char tMutex;
+typedef char tShortSpinlock;
+
+typedef int64_t tTime;
+extern tTime now(void);
+extern int64_t timestamp(int sec, int min, int hr, int day, int month, int year);
+extern void format_date(tTime TS, int *year, int *month, int *day, int *hrs, int *mins, int *sec, int *ms);
+
+#define PACKED __attribute__((packed))
+#define DEPRECATED
+#define EXPORT(s)
+#define EXPORTV(s)
+
+#include <vfs_ext.h>
+
+// These are actually library functions, but they can't be included, so they're defined manually
+extern void *malloc(size_t bytes);
+extern void *calloc(size_t nmemb, size_t size);
+extern void *realloc(void *oldptr, size_t bytes);
+extern void free(void *buffer);
+
+#include <errno.h>
+#include <acess_logging.h>
+
+// Threads
+extern int *Threads_GetErrno(void);
+//extern tPGID Threads_GetPGID(void);
+//extern tPID Threads_GetPID(void);
+extern tTID Threads_GetTID(void);
+extern tUID Threads_GetUID(void);
+extern tGID Threads_GetGID(void);
+
+// Kinda hacky way of not colliding with native errno
+#define errno (*(Threads_GetErrno()))
+
+/**
+ * \name Endianness Swapping
+ * \{
+ */
+#ifdef __BIG_ENDIAN__
+#define LittleEndian16(_val) SwapEndian16(_val)
+#define LittleEndian32(_val) SwapEndian32(_val)
+#define LittleEndian64(_val) SwapEndian32(_val)
+#define BigEndian16(_val) (_val)
+#define BigEndian32(_val) (_val)
+#define BigEndian64(_val) (_val)
+#else
+#define LittleEndian16(_val) (_val)
+#define LittleEndian32(_val) (_val)
+#define LittleEndian64(_val) (_val)
+#define BigEndian16(_val) SwapEndian16(_val)
+#define BigEndian32(_val) SwapEndian32(_val)
+#define BigEndian64(_val) SwapEndian64(_val)
+#endif
+extern Uint16 SwapEndian16(Uint16 Val);
+extern Uint32 SwapEndian32(Uint32 Val);
+extern Uint64 SwapEndian64(Uint64 Val);
+/**
+ * \}
+ */
+
+
+#include <string.h>
+extern int strucmp(const char *s1, const char *s2);
+extern int strpos(const char *Str, char Ch);
+extern void itoa(char *buf, uint64_t num, int base, int minLength, char pad);
+extern int snprintf(char *buf, size_t len, const char *fmt, ...);
+extern int sprintf(char *buf, const char *fmt, ...);
+extern int ReadUTF8(const Uint8 *str, Uint32 *Val);
+extern int WriteUTF8(Uint8 *str, Uint32 Val);
+#define CheckString(str) (1)
+#define CheckMem(mem,sz) (1)
+#include <ctype.h>
+
+// TODO: Move out?
+extern int DivUp(int value, int divisor);
+extern uint64_t DivMod64U(uint64_t Num, uint64_t Den, uint64_t *Rem);
+
+static inline int Mutex_Acquire(tMutex *m) {
+ if(*m) Log_KernelPanic("---", "Double mutex lock");
+ *m = 1;
+ return 0;
+}
+static inline void Mutex_Release(tMutex *m) { *m = 0; }
+
+static inline void SHORTLOCK(tShortSpinlock *Lock) {
+ if(*Lock) Log_KernelPanic("---", "Double short lock");
+ *Lock = 1;
+}
+static inline void SHORTREL(tShortSpinlock *m) { *m = 0; }
+
+#endif
+
--- /dev/null
+
+#ifndef _DISKTOOL__ACESS_LOGGING_H_
+#define _DISKTOOL__ACESS_LOGGING_H_
+
+#if DEBUG
+# define ENTER(str, v...) Debug_TraceEnter(__func__, str, ##v)
+# define LOG(fmt, v...) Debug_TraceLog(__func__, fmt, ##v)
+# define LEAVE(t, v...) Debug_TraceLeave(__func__, t, ##v)
+# define LEAVE_RET(t,v) do{LEAVE('-');return v;}while(0)
+#else
+# define ENTER(...) do{}while(0)
+# define LOG(...) do{}while(0)
+# define LEAVE(...) do{}while(0)
+# define LEAVE_RET(t,v) return v;
+#endif
+
+extern void Log_KernelPanic(const char *Ident, const char *Message, ...) __attribute__((noreturn));
+extern void Log_Panic(const char *Ident, const char *Message, ...);
+extern void Log_Error(const char *Ident, const char *Message, ...);
+extern void Log_Warning(const char *Ident, const char *Message, ...);
+extern void Log_Notice(const char *Ident, const char *Message, ...);
+extern void Log_Log(const char *Ident, const char *Message, ...);
+extern void Log_Debug(const char *Ident, const char *Message, ...);
+
+extern void Warning(const char *Message, ...);
+extern void Log(const char *Message, ...);
+extern void Debug_HexDump(const char *Prefix, const void *Data, size_t Length);
+
+extern void Debug_TraceEnter(const char *Function, const char *Format, ...);
+extern void Debug_TraceLog(const char *Function, const char *Format, ...);
+extern void Debug_TraceLeave(const char *Function, char Type, ...);
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 DiskTool
+ * - By John Hodge (thePowersGang)
+ *
+ * include/disktool_common.h
+ * - DiskTool internal API between native and kernel code
+ */
+#ifndef _INCLUDE__DISKTOOL_COMMON_H_
+#define _INCLUDE__DISKTOOL_COMMON_H_
+
+extern void DiskTool_Cleanup(void);
+
+extern int DiskTool_RegisterLVM(const char *Identifier, const char *Path);
+extern int DiskTool_MountImage(const char *Identifier, const char *Path);
+extern int DiskTool_Copy(const char *Source, const char *Destination);
+extern int DiskTool_ListDirectory(const char *Directory);
+
+extern size_t DiskTool_int_TranslatePath(char *Buffer, const char *Path);
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 DiskTool
+ * - By John Hodge (thePowersGang)
+ *
+ * include/modules.h
+ * - Reimplimentation of kernel module interface for POSIX userland
+ */
+#ifndef _INCLUDE__MODULES_H_
+#define _INCLUDE__MODULES_H_
+
+enum
+{
+ MODULE_ERR_OK,
+};
+
+#define MODULE_DEFINE(flags, version, name, init, deinit, deps...) \
+void __init_##init(void) __attribute__((constructor(200))); void __init_##init(void){init(NULL);}
+
+#endif
+
--- /dev/null
+/*
+ * Acess2 Disk Tool
+ */
+#ifndef _RWLOCK_H
+#define _RWLOCK_H
+
+typedef char tRWLock;
+
+static inline int RWLock_AcquireRead(tRWLock *m) {
+ if(*m) Log_KernelPanic("---", "Double mutex lock");
+ *m = 1;
+ return 0;
+}
+static inline int RWLock_AcquireWrite(tRWLock *m) {
+ if(*m) Log_KernelPanic("---", "Double mutex lock");
+ *m = 1;
+ return 0;
+}
+static inline void RWLock_Release(tRWLock *m) { *m = 0; }
+
+#endif
+
--- /dev/null
+/*
+ *
+ */
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <acess_logging.h>
+#include <ctype.h>
+
+#define LOGHDR(col,type) fprintf(stderr, "\e["col"m[%-8.8s]"type" ", Ident)
+#define LOGTAIL() fprintf(stderr, "\e[0m\n")
+
+#define PUTERR(col,type) {\
+ LOGHDR(col,type);\
+ va_list args; va_start(args, Message);\
+ vfprintf(stderr, Message, args);\
+ va_end(args);\
+ LOGTAIL();\
+}
+
+// === CODE ===
+void Log_KernelPanic(const char *Ident, const char *Message, ...) {
+ PUTERR("35", "k")
+ exit(-1);
+}
+void Log_Panic(const char *Ident, const char *Message, ...)
+ PUTERR("34", "p")
+void Log_Error(const char *Ident, const char *Message, ...)
+ PUTERR("31", "e")
+void Log_Warning(const char *Ident, const char *Message, ...)
+ PUTERR("33", "w")
+void Log_Notice(const char *Ident, const char *Message, ...)
+ PUTERR("32", "n")
+void Log_Log(const char *Ident, const char *Message, ...)
+ PUTERR("37", "l")
+void Log_Debug(const char *Ident, const char *Message, ...)
+ PUTERR("37", "d")
+
+void Warning(const char *Message, ...) {
+ const char *Ident = "";
+ PUTERR("33", "W")
+}
+void Log(const char *Message, ...) {
+ const char *Ident = "";
+ PUTERR("37", "L")
+}
+
+void Debug_HexDump(const char *Prefix, const void *Data, size_t Length)
+{
+ const uint8_t *data = Data;
+ size_t ofs;
+ fprintf(stderr, "[HexDump ]d %s: %i bytes\n", Prefix, (int)Length);
+ for( ofs = 0; ofs + 16 <= Length; ofs += 16 )
+ {
+ fprintf(stderr, "[HexDump ]d %s:", Prefix);
+ fprintf(stderr, " %02x %02x %02x %02x %02x %02x %02x %02x",
+ data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
+ data += 8;
+ fprintf(stderr, " %02x %02x %02x %02x %02x %02x %02x %02x",
+ data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
+ data += 8;
+ fprintf(stderr, "\n");
+ }
+
+ fprintf(stderr, "[HexDump ]d %s:", Prefix);
+ for( ; ofs < Length; ofs ++ )
+ {
+ if( ofs % 16 == 8 ) fprintf(stderr, " ");
+ fprintf(stderr, " %02x", data[ofs%16]);
+ }
+ fprintf(stderr, "\n");
+}
+
+ int giDebug_TraceLevel = 0;
+
+void Debug_TraceEnter(const char *Function, const char *Format, ...)
+{
+ const char *Ident = "Trace";
+ LOGHDR("37","T");
+ for( int i = 0; i < giDebug_TraceLevel; i ++ )
+ fprintf(stderr, " ");
+ fprintf(stderr, "%s: (", Function);
+
+ va_list args;
+ va_start(args, Format);
+
+ int hasBeenPrev = 0;
+ while(*Format)
+ {
+ while( *Format && isblank(*Format) )
+ Format ++;
+ if( !*Format ) break;
+
+ char type = *Format++;
+ const char *start = Format;
+ while( *Format && !isblank(*Format) )
+ Format ++;
+
+ if(hasBeenPrev)
+ fprintf(stderr, ",");
+ hasBeenPrev = 1;
+
+ fprintf(stderr, "%.*s=", (int)(Format-start), start);
+ switch(type)
+ {
+ case 'p':
+ fprintf(stderr, "%p", va_arg(args,const void *));
+ break;
+ case 's':
+ fprintf(stderr, "\"%s\"", va_arg(args,const char *));
+ break;
+ case 'i':
+ fprintf(stderr, "%i", va_arg(args,int));
+ break;
+ case 'x':
+ fprintf(stderr, "0x%x", va_arg(args,unsigned int));
+ break;
+ default:
+ va_arg(args,uintptr_t);
+ fprintf(stderr, "?");
+ break;
+ }
+ }
+
+ va_end(args);
+
+ fprintf(stderr, ")");
+ LOGTAIL();
+ giDebug_TraceLevel ++;
+}
+
+void Debug_TraceLog(const char *Function, const char *Format, ...)
+{
+ const char *Ident = "Trace";
+ LOGHDR("37","T");
+
+ for( int i = 0; i < giDebug_TraceLevel; i ++ )
+ fprintf(stderr, " ");
+ fprintf(stderr, "%s: ", Function);
+
+ va_list args;
+ va_start(args, Format);
+
+ vfprintf(stderr, Format, args);
+
+ va_end(args);
+ LOGTAIL();
+}
+
+void Debug_TraceLeave(const char *Function, char Type, ...)
+{
+ if( giDebug_TraceLevel == 0 ) {
+ Log_Error("Debug", "Function %s called LEAVE without ENTER", Function);
+ }
+
+ const char *Ident = "Trace";
+ LOGHDR("37","T");
+
+ va_list args;
+ va_start(args, Type);
+
+ if( giDebug_TraceLevel > 0 )
+ {
+ giDebug_TraceLevel --;
+ for( int i = 0; i < giDebug_TraceLevel; i ++ )
+ fprintf(stderr, " ");
+ }
+ fprintf(stderr, "%s: RETURN", Function);
+ switch(Type)
+ {
+ case '-':
+ break;
+ case 'i':
+ fprintf(stderr, " %i", va_arg(args, int));
+ break;
+ case 'x':
+ fprintf(stderr, " 0x%x", va_arg(args, unsigned int));
+ break;
+ case 's':
+ fprintf(stderr, " \"%s\"", va_arg(args, const char *));
+ break;
+ case 'p':
+ fprintf(stderr, " %p", va_arg(args, const void *));
+ break;
+ default:
+ fprintf(stderr, " ?");
+ break;
+ }
+
+ va_end(args);
+ LOGTAIL();
+}
+
--- /dev/null
+/*
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <disktool_common.h>
+
+// === CODE ===
+int main(int argc, char *argv[])
+{
+ // Parse arguments
+ for( int i = 1; i < argc; i ++ )
+ {
+ if( strcmp("mount", argv[i]) == 0 || strcmp("-i", argv[i]) == 0 ) {
+ // Mount an image
+ if( argc - i < 3 ) {
+ fprintf(stderr, "--image/-i takes 2 arguments (ident and path)\n");
+ exit(-1);
+ }
+
+ if( DiskTool_MountImage(argv[i+1], argv[i+2]) ) {
+ fprintf(stderr, "Unable to mount '%s' as '%s'\n", argv[i+2], argv[i+1]);
+ break;
+ }
+
+ i += 2;
+ continue ;
+ }
+
+ if( strcmp("mountlvm", argv[i]) == 0 ) {
+
+ if( argc - i < 3 ) {
+ fprintf(stderr, "mountlvm takes 2 arguments (ident and path)\n");
+ exit(-1);
+ }
+
+ if( DiskTool_RegisterLVM(argv[i+1], argv[i+2]) ) {
+ fprintf(stderr, "Unable to register '%s' as LVM '%s'\n", argv[i+2], argv[i+1]);
+ break;
+ }
+
+ i += 2;
+ continue ;
+ }
+
+ if( strcmp("ls", argv[i]) == 0 ) {
+ if( argc - i < 2 ) {
+ fprintf(stderr, "ls 1 argument (path)\n");
+ break;
+ }
+
+ DiskTool_ListDirectory(argv[i+1]);
+ i += 1;
+ continue ;
+ }
+
+ if( strcmp("cp", argv[i]) == 0 ) {
+
+ if( argc - i < 3 ) {
+ fprintf(stderr, "cp takes 2 arguments (source and destination)\n");
+ break;
+ }
+
+ DiskTool_Copy(argv[i+1], argv[i+2]);
+
+ i += 2;
+ continue ;
+ }
+ }
+
+ DiskTool_Cleanup();
+
+ return 0;
+}
+
+// NOTE: This is in a native compiled file because it needs access to the real errno macro
+int *Threads_GetErrno(void)
+{
+ return &errno;
+}
+
+// TODO: Move into a helper lib?
+void itoa(char *buf, uint64_t num, int base, int minLength, char pad)
+{
+ char fmt[] = "%0ll*x";
+ switch(base)
+ {
+ case 8: fmt[5] = 'o'; break;
+ case 10: fmt[5] = 'd'; break;
+ case 16: fmt[5] = 'x'; break;
+ }
+ if(pad != '0') {
+ fmt[1] = '%';
+ sprintf(buf, fmt+1, minLength, num);
+ }
+ else {
+ sprintf(buf, fmt, minLength, num);
+ }
+}
+
+int strpos(const char *Str, char Ch)
+{
+ const char *r = strchr(Str, Ch);
+ if(!r) return -1;
+ return r - Str;
+}
+
+int strucmp(const char *s1, const char *s2)
+{
+ return strcasecmp(s1, s2);
+}
+
+uint64_t DivMod64U(uint64_t value, uint64_t divisor, uint64_t *remainder)
+{
+ if(remainder)
+ *remainder = value % divisor;
+ return value / divisor;
+}
+
--- /dev/null
+../../../AcessNative/acesskernel_src/nativefs.c
\ No newline at end of file
--- /dev/null
+/*
+ *
+ */
+#include <acess.h>
+#include <threads.h>
+
+// === CODE ===
+tThread *Proc_GetCurThread(void)
+{
+ return NULL;
+}
+
+void Threads_PostEvent(tThread *Thread, Uint32 Events)
+{
+
+}
+
+Uint32 Threads_WaitEvents(Uint32 Events)
+{
+ Log_KernelPanic("Threads", "Can't use _WaitEvents in DiskTool");
+ return 0;
+}
+
+void Threads_ClearEvent(Uint32 Mask)
+{
+
+}
+
+tUID Threads_GetUID(void) { return 0; }
+tGID Threads_GetGID(void) { return 0; }
+
+int *Threads_GetMaxFD(void) { static int max_fd=32; return &max_fd; }
+char **Threads_GetCWD(void) { static char *cwd; return &cwd; }
+char **Threads_GetChroot(void) { static char *chroot; return &chroot; }
+
--- /dev/null
+/*
+ * Acess2 DiskTool
+ * - By John Hodge (thePowersGang)
+ *
+ * time.c
+ * - Timing functions (emulated)
+ */
+#include <acess.h>
+#include <timers.h>
+
+// === CODE ===
+tTimer *Time_AllocateTimer(tTimerCallback *Callback, void *Argument)
+{
+ return NULL;
+}
+
+void Time_ScheduleTimer(tTimer *Timer, int Delta)
+{
+
+}
+
+void Time_FreeTimer(tTimer *Timer)
+{
+
+}
+
+Sint64 now(void)
+{
+ // TODO: Translate UNIX time into Acess time
+ return 0;
+}
+
--- /dev/null
+/*
+ *
+ */
+#include <acess.h>
+#include <vfs.h>
+#include <vfs_int.h>
+
+#define MAX_KERNEL_FILES 32
+
+// === GLOBALS ===
+tVFS_Handle gaKernelHandles[MAX_KERNEL_FILES];
+
+// === CODE ===
+int VFS_AllocHandle(int bIsUser, tVFS_Node *Node, int Mode)
+{
+ for( int i = 0; i < MAX_KERNEL_FILES; i ++ )
+ {
+ if(gaKernelHandles[i].Node) continue;
+ gaKernelHandles[i].Node = Node;
+ gaKernelHandles[i].Position = 0;
+ gaKernelHandles[i].Mode = Mode;
+ return i;
+ }
+
+ return -1;
+}
+
+tVFS_Handle *VFS_GetHandle(int ID)
+{
+ if( ID < 0 || ID >= MAX_KERNEL_FILES )
+ return NULL;
+ return &gaKernelHandles[ID];
+}
int tmp;
// TODO: Check this
giNetworkFileHandle = open("/Devices/ip/loop/udp", OPENFLAG_READ);
- tmp = AXWIN_PORT; ioctl(giNetworkFileHandle, 4, &tmp); // TODO: Don't hard-code IOCtl number
+ if( giNetworkFileHandle != -1 )
+ {
+ tmp = AXWIN_PORT;
+ ioctl(giNetworkFileHandle, 4, &tmp); // TODO: Don't hard-code IOCtl number
+ }
}
void IPC_FillSelect(int *nfds, fd_set *set)
{
- if( giNetworkFileHandle > *nfds ) *nfds = giNetworkFileHandle;
- FD_SET(giNetworkFileHandle, set);
+ if( giNetworkFileHandle != -1 )
+ {
+ if( giNetworkFileHandle > *nfds ) *nfds = giNetworkFileHandle;
+ FD_SET(giNetworkFileHandle, set);
+ }
}
void IPC_HandleSelect(fd_set *set)
{
- if( FD_ISSET(giNetworkFileHandle, set) )
+ if( giNetworkFileHandle != -1 )
{
- char staticBuf[STATICBUF_SIZE];
- int readlen, identlen;
- char *msg;
-
- readlen = read(giNetworkFileHandle, staticBuf, sizeof(staticBuf));
-
- identlen = 4 + Net_GetAddressSize( ((uint16_t*)staticBuf)[1] );
- msg = staticBuf + identlen;
-
- IPC_Handle(&gIPC_Type_Datagram, staticBuf, readlen - identlen, (void*)msg);
-// _SysDebug("IPC_HandleSelect: UDP handled");
+ if( FD_ISSET(giNetworkFileHandle, set) )
+ {
+ char staticBuf[STATICBUF_SIZE];
+ int readlen, identlen;
+ char *msg;
+
+ readlen = read(giNetworkFileHandle, staticBuf, sizeof(staticBuf));
+
+ identlen = 4 + Net_GetAddressSize( ((uint16_t*)staticBuf)[1] );
+ msg = staticBuf + identlen;
+
+ IPC_Handle(&gIPC_Type_Datagram, staticBuf, readlen - identlen, (void*)msg);
+// _SysDebug("IPC_HandleSelect: UDP handled");
+ }
}
while(SysGetMessage(NULL, NULL))
int tmpInt;
// Open terminal
+ #if 0
giTerminalFD = open(gsTerminalDevice, OPENFLAG_READ|OPENFLAG_WRITE);
if( giTerminalFD == -1 )
{
fprintf(stderr, "ERROR: Unable to open '%s' (%i)\n", gsTerminalDevice, _errno);
exit(-1);
}
-
+ #else
+ giTerminalFD = 1;
+ // Check that the console is a VT
+ // - ioctl(..., 0, NULL) returns the type, which should be 2
+ if( ioctl(1, 0, NULL) != 2 )
+ {
+ fprintf(stderr, "stdout is not an Acess VT, can't start");
+ _SysDebug("stdout is not an Acess VT, can't start");
+ exit(-1);
+ }
+ #endif
// Set mode to video
tmpInt = TERM_MODE_FB;
if( giVideo_LastDirtyLine == 0 ) return;
-// _SysDebug("Video_Update - Updating lines %i to %i (0x%x+0x%x px)",
-// giVideo_FirstDirtyLine, giVideo_LastDirtyLine, ofs, size);
+ _SysDebug("Video_Update - Updating lines %i to %i (0x%x+0x%x px)",
+ giVideo_FirstDirtyLine, giVideo_LastDirtyLine, ofs, size);
seek(giTerminalFD, ofs*4, 1);
+ _SysDebug("Video_Update - Sending");
write(giTerminalFD, gpScreenBuffer+ofs, size*4);
-// _SysDebug("Video_Update - Done");
+ _SysDebug("Video_Update - Done");
giVideo_FirstDirtyLine = 0;
giVideo_LastDirtyLine = 0;
}