From 91836dca855dc2f1717f0d1a9b62ce565f68bab6 Mon Sep 17 00:00:00 2001 From: marcan Date: Sun, 28 Dec 2008 14:35:37 +0100 Subject: [PATCH] Add miniios --- .gitignore | 3 + Makefile | 45 ++ diskio.c | 125 +++ diskio.h | 71 ++ elf.h | 44 ++ ff.c | 2036 +++++++++++++++++++++++++++++++++++++++++++++++++ ff.h | 339 ++++++++ gecko.c | 201 +++++ gecko.h | 18 + hollywood.h | 258 +++++++ integer.h | 30 + lcd.c | 208 +++++ lcd.h | 11 + main.c | 168 ++++ memory.c | 218 ++++++ memory.h | 27 + memory_asm.S | 39 + panic.c | 39 + panic.h | 8 + powerpc.c | 58 ++ powerpc.h | 9 + powerpc_elf.c | 89 +++ powerpc_elf.h | 6 + sdhc.c | 1116 +++++++++++++++++++++++++++ sdhc.h | 40 + start.S | 177 +++++ start.h | 13 + string.c | 94 +++ string.h | 15 + stub.c | 327 ++++++++ stub.ld | 104 +++ types.h | 28 + utils.c | 40 + utils.h | 187 +++++ utils_asm.S | 62 ++ vsprintf.c | 291 +++++++ vsprintf.h | 3 + 37 files changed, 6547 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 diskio.c create mode 100644 diskio.h create mode 100644 elf.h create mode 100644 ff.c create mode 100644 ff.h create mode 100644 gecko.c create mode 100644 gecko.h create mode 100644 hollywood.h create mode 100644 integer.h create mode 100644 lcd.c create mode 100644 lcd.h create mode 100644 main.c create mode 100644 memory.c create mode 100644 memory.h create mode 100644 memory_asm.S create mode 100644 panic.c create mode 100644 panic.h create mode 100644 powerpc.c create mode 100644 powerpc.h create mode 100644 powerpc_elf.c create mode 100644 powerpc_elf.h create mode 100644 sdhc.c create mode 100644 sdhc.h create mode 100644 start.S create mode 100644 start.h create mode 100644 string.c create mode 100644 string.h create mode 100644 stub.c create mode 100644 stub.ld create mode 100644 types.h create mode 100644 utils.c create mode 100644 utils.h create mode 100644 utils_asm.S create mode 100644 vsprintf.c create mode 100644 vsprintf.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6c4b919 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.d +*.bin + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f6a7f47 --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +CC = arm-eabi-gcc +AS = arm-eabi-as +LD = arm-eabi-gcc +OBJCOPY = arm-eabi-objcopy +CFLAGS = -mbig-endian -fomit-frame-pointer -Os -fpic -Wall -I. +ASFLAGS = -mbig-endian +LDFLAGS = -nostartfiles -mbig-endian -Wl,-T,stub.ld + +TARGET = iosboot.bin +ELF = iosboot.elf +OBJECTS = start.o main.o vsprintf.o string.o gecko.o memory.o memory_asm.o \ + utils_asm.o utils.o ff.o diskio.o sdhc.o powerpc_elf.o powerpc.o panic.o + + +$(TARGET) : $(ELF) + @echo "OBJCPY $@" + @$(OBJCOPY) -O binary $< $@ + +$(ELF) : stub.ld $(OBJECTS) + @echo "LD $@" + @$(LD) $(LDFLAGS) $(OBJECTS) -o $@ + +%.o : %.S + @echo "AS $@" + @$(AS) $(ASFLAGS) -o $@ $< + +%.o : %.c + @echo "CC $@" + @$(CC) $(CFLAGS) -c -o $@ $< + +%.d: %.c + @echo "DEP $@" + @set -e; $(CC) -M $(CFLAGS) $< \ + | sed 's?\($*\)\.o[ :]*?\1.o $@ : ?g' > $@; \ + [ -s $@ ] || rm -f $@ + +%.d: %.S + @echo "DEP $@" + @touch $@ + +-include $(OBJECTS:.o=.d) + +clean: + -rm -f *.elf *.o *.bin *.d + diff --git a/diskio.c b/diskio.c new file mode 100644 index 0000000..af33687 --- /dev/null +++ b/diskio.c @@ -0,0 +1,125 @@ +/*-----------------------------------------------------------------------*/ +/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2007 */ +/*-----------------------------------------------------------------------*/ +/* This is a stub disk I/O module that acts as front end of the existing */ +/* disk I/O modules and attach it to FatFs module with common interface. */ +/*-----------------------------------------------------------------------*/ + +#include "diskio.h" +#include "sdhc.h" +#include + +static sdhci_t sdhci; +static u8 *buffer[512] __attribute__((aligned(32))); + +/*-----------------------------------------------------------------------*/ +/* Inidialize a Drive */ + +DSTATUS disk_initialize ( + BYTE drv /* Physical drive nmuber (0..) */ +) +{ + s32 ret; + + sd_init(&sdhci, 0); + ret = sd_mount(&sdhci); + if(ret < 0) + return STA_NOINIT; + else + return disk_status(drv); +} + + + +/*-----------------------------------------------------------------------*/ +/* Return Disk Status */ + +DSTATUS disk_status ( + BYTE drv /* Physical drive nmuber (0..) */ +) +{ + if(sd_inserted(&sdhci) == 0) + return STA_NODISK; + return 0; +} + + + +/*-----------------------------------------------------------------------*/ +/* Read Sector(s) */ + +DRESULT disk_read ( + BYTE drv, /* Physical drive nmuber (0..) */ + BYTE *buff, /* Data buffer to store read data */ + DWORD sector, /* Sector address (LBA) */ + BYTE count /* Number of sectors to read (1..255) */ +) +{ + int i; + DRESULT res; + + res = RES_OK; + for(i = 0; i < count; i++) + { + if(sd_read(&sdhci, sector + i, 1, buffer) != 0) + { + res = RES_ERROR; + break; + } + memcpy(buff + i * 512, buffer, 512); + } + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Write Sector(s) */ + +#if _READONLY == 0 + +DRESULT disk_write ( + BYTE drv, /* Physical drive nmuber (0..) */ + const BYTE *buff, /* Data to be written */ + DWORD sector, /* Sector address (LBA) */ + BYTE count /* Number of sectors to write (1..255) */ +) +{ + int i; + DRESULT res; + + res = RES_OK; + for(i = 0; i < count; i++) + { + memcpy(buffer, buff + i * 512, 512); + if(sd_write(&sdhci, sector + i, 1, buffer) != 0) + { + res = RES_ERROR; + break; + } + } + return res; +} +#endif /* _READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* Miscellaneous Functions */ + +DRESULT disk_ioctl ( + BYTE drv, /* Physical drive nmuber (0..) */ + BYTE ctrl, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + if(ctrl == CTRL_SYNC) + return RES_OK; + return RES_PARERR; +} + +DWORD get_fattime (void) +{ + return 0; // TODO +} + diff --git a/diskio.h b/diskio.h new file mode 100644 index 0000000..1d5f818 --- /dev/null +++ b/diskio.h @@ -0,0 +1,71 @@ +/*----------------------------------------------------------------------- +/ Low level disk interface modlue include file R0.06 (C)ChaN, 2007 +/-----------------------------------------------------------------------*/ + +#ifndef _DISKIO + +#define _READONLY 1 /* 1: Read-only mode */ +#define _USE_IOCTL 1 + +#include "integer.h" + + +/* Status of Disk Functions */ +typedef BYTE DSTATUS; + +/* Results of Disk Functions */ +typedef enum { + RES_OK = 0, /* 0: Successful */ + RES_ERROR, /* 1: R/W Error */ + RES_WRPRT, /* 2: Write Protected */ + RES_NOTRDY, /* 3: Not Ready */ + RES_PARERR /* 4: Invalid Parameter */ +} DRESULT; + + +/*---------------------------------------*/ +/* Prototypes for disk control functions */ + +DSTATUS disk_initialize (BYTE); +DSTATUS disk_status (BYTE); +DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE); +#if _READONLY == 0 +DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE); +#endif +DRESULT disk_ioctl (BYTE, BYTE, void*); +void disk_timerproc (void); + + + + +/* Disk Status Bits (DSTATUS) */ + +#define STA_NOINIT 0x01 /* Drive not initialized */ +#define STA_NODISK 0x02 /* No medium in the drive */ +#define STA_PROTECT 0x04 /* Write protected */ + + +/* Command code for disk_ioctrl() */ + +/* Generic command */ +#define CTRL_SYNC 0 /* Mandatory for read/write configuration */ +#define GET_SECTOR_COUNT 1 /* Mandatory for only f_mkfs() */ +#define GET_SECTOR_SIZE 2 +#define GET_BLOCK_SIZE 3 /* Mandatory for only f_mkfs() */ +#define CTRL_POWER 4 +#define CTRL_LOCK 5 +#define CTRL_EJECT 6 +/* MMC/SDC command */ +#define MMC_GET_TYPE 10 +#define MMC_GET_CSD 11 +#define MMC_GET_CID 12 +#define MMC_GET_OCR 13 +#define MMC_GET_SDSTAT 14 +/* ATA/CF command */ +#define ATA_GET_REV 20 +#define ATA_GET_MODEL 21 +#define ATA_GET_SN 22 + + +#define _DISKIO +#endif diff --git a/elf.h b/elf.h new file mode 100644 index 0000000..f8b98dd --- /dev/null +++ b/elf.h @@ -0,0 +1,44 @@ +#ifndef __ELF_H__ +#define __ELF_H__ + +#include "types.h" + +#define EI_NIDENT 16 + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + u16 e_type; + u16 e_machine; + u32 e_version; + void *e_entry; + u32 e_phoff; + u32 e_shoff; + u32 e_flags; + u16 e_ehsize; + u16 e_phentsize; + u16 e_phnum; + u16 e_shentsize; + u16 e_shnum; + u16 e_shtrndx; +} Elf32_Ehdr; + +typedef struct { + u32 p_type; + u32 p_offset; + void *p_vaddr; + void *p_paddr; + u32 p_filesz; + u32 p_memsz; + u32 p_flags; + u32 p_align; +} Elf32_Phdr; + +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 + +#endif diff --git a/ff.c b/ff.c new file mode 100644 index 0000000..ab28a3d --- /dev/null +++ b/ff.c @@ -0,0 +1,2036 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - FAT file system module R0.06 (C)ChaN, 2008 +/-----------------------------------------------------------------------------/ +/ The FatFs module is an experimenal project to implement FAT file system to +/ cheap microcontrollers. This is a free software and is opened for education, +/ research and development under license policy of following trems. +/ +/ Copyright (C) 2008, ChaN, all right reserved. +/ +/ * The FatFs module is a free software and there is no warranty. +/ * You can use, modify and/or redistribute it for personal, non-profit or +/ commercial use without restriction under your responsibility. +/ * Redistributions of source code must retain the above copyright notice. +/ +/-----------------------------------------------------------------------------/ +/ Feb 26,'06 R0.00 Prototype. +/ +/ Apr 29,'06 R0.01 First stable version. +/ +/ Jun 01,'06 R0.02 Added FAT12 support. +/ Removed unbuffered mode. +/ Fixed a problem on small (<32M) patition. +/ Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM). +/ +/ Sep 22,'06 R0.03 Added f_rename(). +/ Changed option _FS_MINIMUM to _FS_MINIMIZE. +/ Dec 11,'06 R0.03a Improved cluster scan algolithm to write files fast. +/ Fixed f_mkdir() creates incorrect directory on FAT32. +/ +/ Feb 04,'07 R0.04 Supported multiple drive system. +/ Changed some interfaces for multiple drive system. +/ Changed f_mountdrv() to f_mount(). +/ Added f_mkfs(). +/ Apr 01,'07 R0.04a Supported multiple partitions on a plysical drive. +/ Added a capability of extending file size to f_lseek(). +/ Added minimization level 3. +/ Fixed an endian sensitive code in f_mkfs(). +/ May 05,'07 R0.04b Added a configuration option _USE_NTFLAG. +/ Added FSInfo support. +/ Fixed DBCS name can result FR_INVALID_NAME. +/ Fixed short seek (<= csize) collapses the file object. +/ +/ Aug 25,'07 R0.05 Changed arguments of f_read(), f_write() and f_mkfs(). +/ Fixed f_mkfs() on FAT32 creates incorrect FSInfo. +/ Fixed f_mkdir() on FAT32 creates incorrect directory. +/ Feb 03,'08 R0.05a Added f_truncate() and f_utime(). +/ Fixed off by one error at FAT sub-type determination. +/ Fixed btr in f_read() can be mistruncated. +/ Fixed cached sector is not flushed when create and close +/ without write. +/ +/ Apr 01,'08 R0.06 Added fputc(), fputs(), fprintf() and fgets(). +/ Improved performance of f_lseek() on moving to the same +/ or following cluster. +/---------------------------------------------------------------------------*/ + +#include +#include "ff.h" /* FatFs declarations */ +#include "diskio.h" /* Include file for user provided disk functions */ + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + +static +FATFS *FatFs[_DRIVES]; /* Pointer to the file system objects (logical drives) */ +static +WORD fsid; /* File system mount ID */ + + + +/*-----------------------------------------------------------------------*/ +/* Change window offset */ +/*-----------------------------------------------------------------------*/ + +static +BOOL move_window ( /* TRUE: successful, FALSE: failed */ + FATFS *fs, /* File system object */ + DWORD sector /* Sector number to make apperance in the fs->win[] */ +) /* Move to zero only writes back dirty window */ +{ + DWORD wsect; + + + wsect = fs->winsect; + if (wsect != sector) { /* Changed current window */ +#if !_FS_READONLY + BYTE n; + if (fs->winflag) { /* Write back dirty window if needed */ + if (disk_write(fs->drive, fs->win, wsect, 1) != RES_OK) + return FALSE; + fs->winflag = 0; + if (wsect < (fs->fatbase + fs->sects_fat)) { /* In FAT area */ + for (n = fs->n_fats; n >= 2; n--) { /* Refrect the change to FAT copy */ + wsect += fs->sects_fat; + disk_write(fs->drive, fs->win, wsect, 1); + } + } + } +#endif + if (sector) { + if (disk_read(fs->drive, fs->win, sector, 1) != RES_OK) + return FALSE; + fs->winsect = sector; + } + } + return TRUE; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Clean-up cached data */ +/*-----------------------------------------------------------------------*/ + +#if !_FS_READONLY +static +FRESULT sync ( /* FR_OK: successful, FR_RW_ERROR: failed */ + FATFS *fs /* File system object */ +) +{ + fs->winflag = 1; + if (!move_window(fs, 0)) return FR_RW_ERROR; +#if _USE_FSINFO + /* Update FSInfo sector if needed */ + if (fs->fs_type == FS_FAT32 && fs->fsi_flag) { + fs->winsect = 0; + memset(fs->win, 0, 512); + ST_WORD(&fs->win[BS_55AA], 0xAA55); + ST_DWORD(&fs->win[FSI_LeadSig], 0x41615252); + ST_DWORD(&fs->win[FSI_StrucSig], 0x61417272); + ST_DWORD(&fs->win[FSI_Free_Count], fs->free_clust); + ST_DWORD(&fs->win[FSI_Nxt_Free], fs->last_clust); + disk_write(fs->drive, fs->win, fs->fsi_sector, 1); + fs->fsi_flag = 0; + } +#endif + /* Make sure that no pending write process in the physical drive */ + if (disk_ioctl(fs->drive, CTRL_SYNC, NULL) != RES_OK) + return FR_RW_ERROR; + return FR_OK; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Get a cluster status */ +/*-----------------------------------------------------------------------*/ + +static +DWORD get_cluster ( /* 0,>=2: successful, 1: failed */ + FATFS *fs, /* File system object */ + DWORD clust /* Cluster# to get the link information */ +) +{ + WORD wc, bc; + DWORD fatsect; + + + if (clust >= 2 && clust < fs->max_clust) { /* Is it a valid cluster#? */ + fatsect = fs->fatbase; + switch (fs->fs_type) { + case FS_FAT12 : + bc = (WORD)clust * 3 / 2; + if (!move_window(fs, fatsect + (bc / SS(fs)))) break; + wc = fs->win[bc & (SS(fs) - 1)]; bc++; + if (!move_window(fs, fatsect + (bc / SS(fs)))) break; + wc |= (WORD)fs->win[bc & (SS(fs) - 1)] << 8; + return (clust & 1) ? (wc >> 4) : (wc & 0xFFF); + + case FS_FAT16 : + if (!move_window(fs, fatsect + (clust / (SS(fs) / 2)))) break; + return LD_WORD(&fs->win[((WORD)clust * 2) & (SS(fs) - 1)]); + + case FS_FAT32 : + if (!move_window(fs, fatsect + (clust / (SS(fs) / 4)))) break; + return LD_DWORD(&fs->win[((WORD)clust * 4) & (SS(fs) - 1)]) & 0x0FFFFFFF; + } + } + + return 1; /* Out of cluster range, or an error occured */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change a cluster status */ +/*-----------------------------------------------------------------------*/ + +#if !_FS_READONLY +static +BOOL put_cluster ( /* TRUE: successful, FALSE: failed */ + FATFS *fs, /* File system object */ + DWORD clust, /* Cluster# to change (must be 2 to fs->max_clust-1) */ + DWORD val /* New value to mark the cluster */ +) +{ + WORD bc; + BYTE *p; + DWORD fatsect; + + + fatsect = fs->fatbase; + switch (fs->fs_type) { + case FS_FAT12 : + bc = (WORD)clust * 3 / 2; + if (!move_window(fs, fatsect + (bc / SS(fs)))) return FALSE; + p = &fs->win[bc & (SS(fs) - 1)]; + *p = (clust & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; + bc++; + fs->winflag = 1; + if (!move_window(fs, fatsect + (bc / SS(fs)))) return FALSE; + p = &fs->win[bc & (SS(fs) - 1)]; + *p = (clust & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); + break; + + case FS_FAT16 : + if (!move_window(fs, fatsect + (clust / (SS(fs) / 2)))) return FALSE; + ST_WORD(&fs->win[((WORD)clust * 2) & (SS(fs) - 1)], (WORD)val); + break; + + case FS_FAT32 : + if (!move_window(fs, fatsect + (clust / (SS(fs) / 4)))) return FALSE; + ST_DWORD(&fs->win[((WORD)clust * 4) & (SS(fs) - 1)], val); + break; + + default : + return FALSE; + } + fs->winflag = 1; + return TRUE; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ + +#if !_FS_READONLY +static +BOOL remove_chain ( /* TRUE: successful, FALSE: failed */ + FATFS *fs, /* File system object */ + DWORD clust /* Cluster# to remove chain from */ +) +{ + DWORD nxt; + + + while (clust >= 2 && clust < fs->max_clust) { + nxt = get_cluster(fs, clust); + if (nxt == 1) return FALSE; + if (!put_cluster(fs, clust, 0)) return FALSE; + if (fs->free_clust != 0xFFFFFFFF) { + fs->free_clust++; +#if _USE_FSINFO + fs->fsi_flag = 1; +#endif + } + clust = nxt; + } + return TRUE; +} +#endif + + + + +/*-----------------------------------------------------------------------*/ +/* Stretch or create a cluster chain */ +/*-----------------------------------------------------------------------*/ + +#if !_FS_READONLY +static +DWORD create_chain ( /* 0: No free cluster, 1: Error, >=2: New cluster number */ + FATFS *fs, /* File system object */ + DWORD clust /* Cluster# to stretch, 0 means create new */ +) +{ + DWORD cstat, ncl, scl, mcl = fs->max_clust; + + + if (clust == 0) { /* Create new chain */ + scl = fs->last_clust; /* Get suggested start point */ + if (scl == 0 || scl >= mcl) scl = 1; + } + else { /* Stretch existing chain */ + cstat = get_cluster(fs, clust); /* Check the cluster status */ + if (cstat < 2) return 1; /* It is an invalid cluster */ + if (cstat < mcl) return cstat; /* It is already followed by next cluster */ + scl = clust; + } + + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= mcl) { /* Wrap around */ + ncl = 2; + if (ncl > scl) return 0; /* No free custer */ + } + cstat = get_cluster(fs, ncl); /* Get the cluster status */ + if (cstat == 0) break; /* Found a free cluster */ + if (cstat == 1) return 1; /* Any error occured */ + if (ncl == scl) return 0; /* No free custer */ + } + + if (!put_cluster(fs, ncl, 0x0FFFFFFF)) return 1; /* Mark the new cluster "in use" */ + if (clust != 0 && !put_cluster(fs, clust, ncl)) return 1; /* Link it to previous one if needed */ + + fs->last_clust = ncl; /* Update fsinfo */ + if (fs->free_clust != 0xFFFFFFFF) { + fs->free_clust--; +#if _USE_FSINFO + fs->fsi_flag = 1; +#endif + } + + return ncl; /* Return new cluster number */ +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Get sector# from cluster# */ +/*-----------------------------------------------------------------------*/ + +static +DWORD clust2sect ( /* !=0: sector number, 0: failed - invalid cluster# */ + FATFS *fs, /* File system object */ + DWORD clust /* Cluster# to be converted */ +) +{ + clust -= 2; + if (clust >= (fs->max_clust - 2)) return 0; /* Invalid cluster# */ + return clust * fs->csize + fs->database; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Move directory pointer to next */ +/*-----------------------------------------------------------------------*/ + +static +BOOL next_dir_entry ( /* TRUE: successful, FALSE: could not move next */ + DIR *dj /* Pointer to directory object */ +) +{ + DWORD clust; + WORD idx; + + + idx = dj->index + 1; + if ((idx & ((SS(dj->fs) - 1) / 32)) == 0) { /* Table sector changed? */ + dj->sect++; /* Next sector */ + if (dj->clust == 0) { /* In static table */ + if (idx >= dj->fs->n_rootdir) return FALSE; /* Reached to end of table */ + } else { /* In dynamic table */ + if (((idx / (SS(dj->fs) / 32)) & (dj->fs->csize - 1)) == 0) { /* Cluster changed? */ + clust = get_cluster(dj->fs, dj->clust); /* Get next cluster */ + if (clust < 2 || clust >= dj->fs->max_clust) /* Reached to end of table */ + return FALSE; + dj->clust = clust; /* Initialize for new cluster */ + dj->sect = clust2sect(dj->fs, clust); + } + } + } + dj->index = idx; /* Lower several bits of dj->index indicates offset in dj->sect */ + return TRUE; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get file status from directory entry */ +/*-----------------------------------------------------------------------*/ + +#if _FS_MINIMIZE <= 1 +static +void get_fileinfo ( /* No return code */ + FILINFO *finfo, /* Ptr to store the file information */ + const BYTE *dir /* Ptr to the directory entry */ +) +{ + BYTE n, c, a; + char *p; + + + p = &finfo->fname[0]; + a = _USE_NTFLAG ? dir[DIR_NTres] : 0; /* NT flag */ + for (n = 0; n < 8; n++) { /* Convert file name (body) */ + c = dir[n]; + if (c == ' ') break; + if (c == 0x05) c = 0xE5; + if (a & 0x08 && c >= 'A' && c <= 'Z') c += 0x20; + *p++ = c; + } + if (dir[8] != ' ') { /* Convert file name (extension) */ + *p++ = '.'; + for (n = 8; n < 11; n++) { + c = dir[n]; + if (c == ' ') break; + if (a & 0x10 && c >= 'A' && c <= 'Z') c += 0x20; + *p++ = c; + } + } + *p = '\0'; + + finfo->fattrib = dir[DIR_Attr]; /* Attribute */ + finfo->fsize = LD_DWORD(&dir[DIR_FileSize]); /* Size */ + finfo->fdate = LD_WORD(&dir[DIR_WrtDate]); /* Date */ + finfo->ftime = LD_WORD(&dir[DIR_WrtTime]); /* Time */ +} +#endif /* _FS_MINIMIZE <= 1 */ + + + + +/*-----------------------------------------------------------------------*/ +/* Pick a paragraph and create the name in format of directory entry */ +/*-----------------------------------------------------------------------*/ + +static +char make_dirfile ( /* 1: error - detected an invalid format, '\0'or'/': next character */ + const char **path, /* Pointer to the file path pointer */ + char *dirname /* Pointer to directory name buffer {Name(8), Ext(3), NT flag(1)} */ +) +{ + BYTE n, t, c, a, b; + + + memset(dirname, ' ', 8+3); /* Fill buffer with spaces */ + a = 0; b = 0x18; /* NT flag */ + n = 0; t = 8; + for (;;) { + c = *(*path)++; + if (c == '\0' || c == '/') { /* Reached to end of str or directory separator */ + if (n == 0) break; + dirname[11] = _USE_NTFLAG ? (a & b) : 0; + return c; + } + if (c <= ' ' || c == 0x7F) break; /* Reject invisible chars */ + if (c == '.') { + if (!(a & 1) && n >= 1 && n <= 8) { /* Enter extension part */ + n = 8; t = 11; continue; + } + break; + } + if (_USE_SJIS && + ((c >= 0x81 && c <= 0x9F) || /* Accept S-JIS code */ + (c >= 0xE0 && c <= 0xFC))) { + if (n == 0 && c == 0xE5) /* Change heading \xE5 to \x05 */ + c = 0x05; + a ^= 0x01; goto md_l2; + } + if (c == '"') break; /* Reject " */ + if (c <= ')') goto md_l1; /* Accept ! # $ % & ' ( ) */ + if (c <= ',') break; /* Reject * + , */ + if (c <= '9') goto md_l1; /* Accept - 0-9 */ + if (c <= '?') break; /* Reject : ; < = > ? */ + if (!(a & 1)) { /* These checks are not applied to S-JIS 2nd byte */ + if (c == '|') break; /* Reject | */ + if (c >= '[' && c <= ']') break;/* Reject [ \ ] */ + if (_USE_NTFLAG && c >= 'A' && c <= 'Z') + (t == 8) ? (b &= 0xF7) : (b &= 0xEF); + if (c >= 'a' && c <= 'z') { /* Convert to upper case */ + c -= 0x20; + if (_USE_NTFLAG) (t == 8) ? (a |= 0x08) : (a |= 0x10); + } + } + md_l1: + a &= 0xFE; + md_l2: + if (n >= t) break; + dirname[n++] = c; + } + return 1; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Trace a file path */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT trace_path ( /* FR_OK(0): successful, !=0: error code */ + DIR *dj, /* Pointer to directory object to return last directory */ + char *fn, /* Pointer to last segment name to return {file(8),ext(3),attr(1)} */ + const char *path, /* Full-path string to trace a file or directory */ + BYTE **dir /* Pointer to pointer to found entry to retutn */ +) +{ + DWORD clust; + char ds; + BYTE *dptr = NULL; + FATFS *fs = dj->fs; + + + /* Initialize directory object */ + clust = fs->dirbase; + if (fs->fs_type == FS_FAT32) { + dj->clust = dj->sclust = clust; + dj->sect = clust2sect(fs, clust); + } else { + dj->clust = dj->sclust = 0; + dj->sect = clust; + } + dj->index = 0; + + if (*path == '\0') { /* Null path means the root directory */ + *dir = NULL; return FR_OK; + } + + for (;;) { + ds = make_dirfile(&path, fn); /* Get a paragraph into fn[] */ + if (ds == 1) return FR_INVALID_NAME; + for (;;) { + if (!move_window(fs, dj->sect)) return FR_RW_ERROR; + dptr = &fs->win[(dj->index & ((SS(fs) - 1) / 32)) * 32]; /* Pointer to the directory entry */ + if (dptr[DIR_Name] == 0) /* Has it reached to end of dir? */ + return !ds ? FR_NO_FILE : FR_NO_PATH; + if (dptr[DIR_Name] != 0xE5 /* Matched? */ + && !(dptr[DIR_Attr] & AM_VOL) + && !memcmp(&dptr[DIR_Name], fn, 8+3) ) break; + if (!next_dir_entry(dj)) /* Next directory pointer */ + return !ds ? FR_NO_FILE : FR_NO_PATH; + } + if (!ds) { *dir = dptr; return FR_OK; } /* Matched with end of path */ + if (!(dptr[DIR_Attr] & AM_DIR)) return FR_NO_PATH; /* Cannot trace because it is a file */ + clust = ((DWORD)LD_WORD(&dptr[DIR_FstClusHI]) << 16) | LD_WORD(&dptr[DIR_FstClusLO]); /* Get cluster# of the directory */ + dj->clust = dj->sclust = clust; /* Restart scanning at the new directory */ + dj->sect = clust2sect(fs, clust); + dj->index = 2; + } +} + + + + +/*-----------------------------------------------------------------------*/ +/* Reserve a directory entry */ +/*-----------------------------------------------------------------------*/ + +#if !_FS_READONLY +static +FRESULT reserve_direntry ( /* FR_OK: successful, FR_DENIED: no free entry, FR_RW_ERROR: a disk error occured */ + DIR *dj, /* Target directory to create new entry */ + BYTE **dir /* Pointer to pointer to created entry to retutn */ +) +{ + DWORD clust, sector; + BYTE c, n, *dptr; + FATFS *fs = dj->fs; + + + /* Re-initialize directory object */ + clust = dj->sclust; + if (clust != 0) { /* Dyanmic directory table */ + dj->clust = clust; + dj->sect = clust2sect(fs, clust); + } else { /* Static directory table */ + dj->sect = fs->dirbase; + } + dj->index = 0; + + do { + if (!move_window(fs, dj->sect)) return FR_RW_ERROR; + dptr = &fs->win[(dj->index & ((SS(dj->fs) - 1) / 32)) * 32]; /* Pointer to the directory entry */ + c = dptr[DIR_Name]; + if (c == 0 || c == 0xE5) { /* Found an empty entry */ + *dir = dptr; return FR_OK; + } + } while (next_dir_entry(dj)); /* Next directory pointer */ + /* Reached to end of the directory table */ + + /* Abort when it is a static table or could not stretch dynamic table */ + if (clust == 0 || !(clust = create_chain(fs, dj->clust))) return FR_DENIED; + if (clust == 1 || !move_window(fs, 0)) return FR_RW_ERROR; + + /* Cleanup the expanded table */ + fs->winsect = sector = clust2sect(fs, clust); + memset(fs->win, 0, SS(fs)); + for (n = fs->csize; n; n--) { + if (disk_write(fs->drive, fs->win, sector, 1) != RES_OK) + return FR_RW_ERROR; + sector++; + } + fs->winflag = 1; + *dir = fs->win; + + return FR_OK; +} +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Load boot record and check if it is an FAT boot record */ +/*-----------------------------------------------------------------------*/ + +static +BYTE check_fs ( /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record or error */ + FATFS *fs, /* File system object */ + DWORD sect /* Sector# (lba) to check if it is an FAT boot record or not */ +) +{ + if (disk_read(fs->drive, fs->win, sect, 1) != RES_OK) /* Load boot record */ + return 2; + if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55) /* Check record signature (always placed at offset 510 even if the sector size is >512) */ + return 2; + + if (!memcmp(&fs->win[BS_FilSysType], "FAT", 3)) /* Check FAT signature */ + return 0; + if (!memcmp(&fs->win[BS_FilSysType32], "FAT32", 5) && !(fs->win[BPB_ExtFlags] & 0x80)) + return 0; + + return 1; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Make sure that the file system is valid */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT auto_mount ( /* FR_OK(0): successful, !=0: any error occured */ + const char **path, /* Pointer to pointer to the path name (drive number) */ + FATFS **rfs, /* Pointer to pointer to the found file system object */ + BYTE chk_wp /* !=0: Check media write protection for write access */ +) +{ + BYTE drv, fmt, *tbl; + DSTATUS stat; + DWORD bootsect, fatsize, totalsect, maxclust; + const char *p = *path; + FATFS *fs; + + + /* Get drive number from the path name */ + while (*p == ' ') p++; /* Strip leading spaces */ + drv = p[0] - '0'; /* Is there a drive number? */ + if (drv <= 9 && p[1] == ':') + p += 2; /* Found a drive number, get and strip it */ + else + drv = 0; /* No drive number is given, use drive number 0 as default */ + if (*p == '/') p++; /* Strip heading slash */ + *path = p; /* Return pointer to the path name */ + + /* Check if the drive number is valid or not */ + if (drv >= _DRIVES) return FR_INVALID_DRIVE; /* Is the drive number valid? */ + *rfs = fs = FatFs[drv]; /* Returen pointer to the corresponding file system object */ + if (!fs) return FR_NOT_ENABLED; /* Is the file system object registered? */ + + if (fs->fs_type) { /* If the logical drive has been mounted */ + stat = disk_status(fs->drive); + if (!(stat & STA_NOINIT)) { /* and physical drive is kept initialized (has not been changed), */ +#if !_FS_READONLY + if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */ + return FR_WRITE_PROTECTED; +#endif + return FR_OK; /* The file system object is valid */ + } + } + + /* The logical drive must be re-mounted. Following code attempts to mount the logical drive */ + + memset(fs, 0, sizeof(FATFS)); /* Clean-up the file system object */ + fs->drive = LD2PD(drv); /* Bind the logical drive and a physical drive */ + stat = disk_initialize(fs->drive); /* Initialize low level disk I/O layer */ + if (stat & STA_NOINIT) /* Check if the drive is ready */ + return FR_NOT_READY; +#if S_MAX_SIZ > 512 /* Get disk sector size if needed */ + if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > S_MAX_SIZ) + return FR_NO_FILESYSTEM; +#endif +#if !_FS_READONLY + if (chk_wp && (stat & STA_PROTECT)) /* Check write protection if needed */ + return FR_WRITE_PROTECTED; +#endif + /* Search FAT partition on the drive */ + fmt = check_fs(fs, bootsect = 0); /* Check sector 0 as an SFD format */ + if (fmt == 1) { /* Not an FAT boot record, it may be patitioned */ + /* Check a partition listed in top of the partition table */ + tbl = &fs->win[MBR_Table + LD2PT(drv) * 16]; /* Partition table */ + if (tbl[4]) { /* Is the partition existing? */ + bootsect = LD_DWORD(&tbl[8]); /* Partition offset in LBA */ + fmt = check_fs(fs, bootsect); /* Check the partition */ + } + } + if (fmt || LD_WORD(&fs->win[BPB_BytsPerSec]) != SS(fs)) /* No valid FAT patition is found */ + return FR_NO_FILESYSTEM; + + /* Initialize the file system object */ + fatsize = LD_WORD(&fs->win[BPB_FATSz16]); /* Number of sectors per FAT */ + if (!fatsize) fatsize = LD_DWORD(&fs->win[BPB_FATSz32]); + fs->sects_fat = fatsize; + fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FAT copies */ + fatsize *= fs->n_fats; /* (Number of sectors in FAT area) */ + fs->fatbase = bootsect + LD_WORD(&fs->win[BPB_RsvdSecCnt]); /* FAT start sector (lba) */ + fs->csize = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */ + fs->n_rootdir = LD_WORD(&fs->win[BPB_RootEntCnt]); /* Nmuber of root directory entries */ + totalsect = LD_WORD(&fs->win[BPB_TotSec16]); /* Number of sectors on the file system */ + if (!totalsect) totalsect = LD_DWORD(&fs->win[BPB_TotSec32]); + fs->max_clust = maxclust = (totalsect /* max_clust = Last cluster# + 1 */ + - LD_WORD(&fs->win[BPB_RsvdSecCnt]) - fatsize - fs->n_rootdir / (SS(fs)/32) + ) / fs->csize + 2; + + fmt = FS_FAT12; /* Determine the FAT sub type */ + if (maxclust >= 0xFF7) fmt = FS_FAT16; + if (maxclust >= 0xFFF7) fmt = FS_FAT32; + + if (fmt == FS_FAT32) + fs->dirbase = LD_DWORD(&fs->win[BPB_RootClus]); /* Root directory start cluster */ + else + fs->dirbase = fs->fatbase + fatsize; /* Root directory start sector (lba) */ + fs->database = fs->fatbase + fatsize + fs->n_rootdir / (SS(fs)/32); /* Data start sector (lba) */ + +#if !_FS_READONLY + /* Initialize allocation information */ + fs->free_clust = 0xFFFFFFFF; +#if _USE_FSINFO + /* Get fsinfo if needed */ + if (fmt == FS_FAT32) { + fs->fsi_sector = bootsect + LD_WORD(&fs->win[BPB_FSInfo]); + if (disk_read(fs->drive, fs->win, fs->fsi_sector, 1) == RES_OK && + LD_WORD(&fs->win[BS_55AA]) == 0xAA55 && + LD_DWORD(&fs->win[FSI_LeadSig]) == 0x41615252 && + LD_DWORD(&fs->win[FSI_StrucSig]) == 0x61417272) { + fs->last_clust = LD_DWORD(&fs->win[FSI_Nxt_Free]); + fs->free_clust = LD_DWORD(&fs->win[FSI_Free_Count]); + } + } +#endif +#endif + + fs->fs_type = fmt; /* FAT syb-type */ + fs->id = ++fsid; /* File system mount ID */ + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file/dir object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT validate ( /* FR_OK(0): The object is valid, !=0: Invalid */ + const FATFS *fs, /* Pointer to the file system object */ + WORD id /* Member id of the target object to be checked */ +) +{ + if (!fs || !fs->fs_type || fs->id != id) + return FR_INVALID_OBJECT; + if (disk_status(fs->drive) & STA_NOINIT) + return FR_NOT_READY; + + return FR_OK; +} + + + + +/*-------------------------------------------------------------------------- + + Public Functions + +--------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Locical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mount ( + BYTE drv, /* Logical drive number to be mounted/unmounted */ + FATFS *fs /* Pointer to new file system object (NULL for unmount)*/ +) +{ + if (drv >= _DRIVES) return FR_INVALID_DRIVE; + + if (FatFs[drv]) FatFs[drv]->fs_type = 0; /* Clear old object */ + + FatFs[drv] = fs; /* Register and clear new object */ + if (fs) fs->fs_type = 0; + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_open ( + FIL *fp, /* Pointer to the blank file object */ + const char *path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + char fn[8+3+1]; + + + fp->fs = NULL; /* Clear file object */ +#if !_FS_READONLY + mode &= (FA_READ|FA_WRITE|FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW); + res = auto_mount(&path, &dj.fs, (BYTE)(mode & (FA_WRITE|FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW))); +#else + mode &= FA_READ; + res = auto_mount(&path, &dj.fs, 0); +#endif + if (res != FR_OK) return res; + res = trace_path(&dj, fn, path, &dir); /* Trace the file path */ + +#if !_FS_READONLY + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS|FA_OPEN_ALWAYS|FA_CREATE_NEW)) { + DWORD ps, rs; + if (res != FR_OK) { /* No file, create new */ + if (res != FR_NO_FILE) return res; + res = reserve_direntry(&dj, &dir); + if (res != FR_OK) return res; + memset(dir, 0, 32); /* Initialize the new entry with open name */ + memcpy(&dir[DIR_Name], fn, 8+3); + dir[DIR_NTres] = fn[11]; + mode |= FA_CREATE_ALWAYS; + } + else { /* Any object is already existing */ + if (mode & FA_CREATE_NEW) /* Cannot create new */ + return FR_EXIST; + if (!dir || (dir[DIR_Attr] & (AM_RDO|AM_DIR))) /* Cannot overwrite it (R/O or DIR) */ + return FR_DENIED; + if (mode & FA_CREATE_ALWAYS) { /* Resize it to zero if needed */ + rs = ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) | LD_WORD(&dir[DIR_FstClusLO]); /* Get start cluster */ + ST_WORD(&dir[DIR_FstClusHI], 0); /* cluster = 0 */ + ST_WORD(&dir[DIR_FstClusLO], 0); + ST_DWORD(&dir[DIR_FileSize], 0); /* size = 0 */ + dj.fs->winflag = 1; + ps = dj.fs->winsect; /* Remove the cluster chain */ + if (!remove_chain(dj.fs, rs) || !move_window(dj.fs, ps)) + return FR_RW_ERROR; + dj.fs->last_clust = rs - 1; /* Reuse the cluster hole */ + } + } + if (mode & FA_CREATE_ALWAYS) { + dir[DIR_Attr] = 0; /* Reset attribute */ + ps = get_fattime(); + ST_DWORD(&dir[DIR_CrtTime], ps); /* Created time */ + dj.fs->winflag = 1; + mode |= FA__WRITTEN; /* Set file changed flag */ + } + } + /* Open an existing file */ + else { +#endif /* !_FS_READONLY */ + if (res != FR_OK) return res; /* Trace failed */ + if (!dir || (dir[DIR_Attr] & AM_DIR)) /* It is a directory */ + return FR_NO_FILE; +#if !_FS_READONLY + if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */ + return FR_DENIED; + } + fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dir; +#endif + fp->flag = mode; /* File access mode */ + fp->org_clust = /* File start cluster */ + ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) | LD_WORD(&dir[DIR_FstClusLO]); + fp->fsize = LD_DWORD(&dir[DIR_FileSize]); /* File size */ + fp->fptr = 0; fp->csect = 255; /* File pointer */ + fp->curr_sect = 0; + fp->fs = dj.fs; fp->id = dj.fs->id; /* Owner file system object of the file */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_read ( + FIL *fp, /* Pointer to the file object */ + void *buff, /* Pointer to data buffer */ + UINT btr, /* Number of bytes to read */ + UINT *br /* Pointer to number of bytes read */ +) +{ + FRESULT res; + DWORD clust, sect, remain; + UINT rcnt, cc; + BYTE *rbuff = buff; + + + *br = 0; + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) return res; + if (fp->flag & FA__ERROR) return FR_RW_ERROR; /* Check error flag */ + if (!(fp->flag & FA_READ)) return FR_DENIED; /* Check access mode */ + remain = fp->fsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr; /* Repeat until all data transferred */ + rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */ + clust = (fp->fptr == 0) ? /* On the top of the file? */ + fp->org_clust : get_cluster(fp->fs, fp->curr_clust); + if (clust < 2 || clust >= fp->fs->max_clust) goto fr_error; + fp->curr_clust = clust; /* Update current cluster */ + fp->csect = 0; /* Reset sector address in the cluster */ + } + sect = clust2sect(fp->fs, fp->curr_clust) + fp->csect; /* Get current sector */ + cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Read maximum contiguous sectors directly */ + if (fp->csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - fp->csect; + if (disk_read(fp->fs->drive, rbuff, sect, (BYTE)cc) != RES_OK) + goto fr_error; + fp->csect += (BYTE)cc; /* Next sector address in the cluster */ + rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } + if (sect != fp->curr_sect) { /* Is window offset changed? */ +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write back file I/O buffer if needed */ + if (disk_write(fp->fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK) + goto fr_error; + fp->flag &= (BYTE)~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drive, fp->buffer, sect, 1) != RES_OK) /* Fill file I/O buffer with file data */ + goto fr_error; + fp->curr_sect = sect; + } + fp->csect++; /* Next sector address in the cluster */ + } + rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Get partial sector from file I/O buffer */ + if (rcnt > btr) rcnt = btr; + memcpy(rbuff, &fp->buffer[fp->fptr % SS(fp->fs)], rcnt); + } + + return FR_OK; + +fr_error: /* Abort this file due to an unrecoverable error */ + fp->flag |= FA__ERROR; + return FR_RW_ERROR; +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Write File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_write ( + FIL *fp, /* Pointer to the file object */ + const void *buff, /* Pointer to the data to be written */ + UINT btw, /* Number of bytes to write */ + UINT *bw /* Pointer to number of bytes written */ +) +{ + FRESULT res; + DWORD clust, sect; + UINT wcnt, cc; + const BYTE *wbuff = buff; + + + *bw = 0; + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) return res; + if (fp->flag & FA__ERROR) return FR_RW_ERROR; /* Check error flag */ + if (!(fp->flag & FA_WRITE)) return FR_DENIED; /* Check access mode */ + if (fp->fsize + btw < fp->fsize) return FR_OK; /* File size cannot reach 4GB */ + + for ( ; btw; /* Repeat until all data transferred */ + wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { + if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */ + if (fp->csect >= fp->fs->csize) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clust = fp->org_clust; /* Follow from the origin */ + if (clust == 0) /* When there is no cluster chain, */ + fp->org_clust = clust = create_chain(fp->fs, 0); /* Create a new cluster chain */ + } else { /* Middle or end of the file */ + clust = create_chain(fp->fs, fp->curr_clust); /* Trace or streach cluster chain */ + } + if (clust == 0) break; /* Could not allocate a new cluster (disk full) */ + if (clust == 1 || clust >= fp->fs->max_clust) goto fw_error; + fp->curr_clust = clust; /* Update current cluster */ + fp->csect = 0; /* Reset sector address in the cluster */ + } + sect = clust2sect(fp->fs, fp->curr_clust) + fp->csect; /* Get current sector */ + cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Write maximum contiguous sectors directly */ + if (fp->csect + cc > fp->fs->csize) /* Clip at cluster boundary */ + cc = fp->fs->csize - fp->csect; + if (disk_write(fp->fs->drive, wbuff, sect, (BYTE)cc) != RES_OK) + goto fw_error; + fp->csect += (BYTE)cc; /* Next sector address in the cluster */ + wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */ + continue; + } + if (sect != fp->curr_sect) { /* Is window offset changed? */ + if (fp->flag & FA__DIRTY) { /* Write back file I/O buffer if needed */ + if (disk_write(fp->fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK) + goto fw_error; + fp->flag &= (BYTE)~FA__DIRTY; + } + if (fp->fptr < fp->fsize && /* Fill file I/O buffer with file data */ + disk_read(fp->fs->drive, fp->buffer, sect, 1) != RES_OK) + goto fw_error; + fp->curr_sect = sect; + } + fp->csect++; /* Next sector address in the cluster */ + } + wcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Put partial sector into file I/O buffer */ + if (wcnt > btw) wcnt = btw; + memcpy(&fp->buffer[fp->fptr % SS(fp->fs)], wbuff, wcnt); + fp->flag |= FA__DIRTY; + } + + if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */ + fp->flag |= FA__WRITTEN; /* Set file changed flag */ + return FR_OK; + +fw_error: /* Abort this file due to an unrecoverable error */ + fp->flag |= FA__ERROR; + return FR_RW_ERROR; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize the file object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_sync ( + FIL *fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD tim; + BYTE *dir; + + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res == FR_OK) { + if (fp->flag & FA__WRITTEN) { /* Has the file been written? */ + /* Write back data buffer if needed */ + if (fp->flag & FA__DIRTY) { + if (disk_write(fp->fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK) + return FR_RW_ERROR; + fp->flag &= (BYTE)~FA__DIRTY; + } + /* Update the directory entry */ + if (!move_window(fp->fs, fp->dir_sect)) + return FR_RW_ERROR; + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ + ST_DWORD(&dir[DIR_FileSize], fp->fsize); /* Update file size */ + ST_WORD(&dir[DIR_FstClusLO], fp->org_clust); /* Update start cluster */ + ST_WORD(&dir[DIR_FstClusHI], fp->org_clust >> 16); + tim = get_fattime(); /* Updated time */ + ST_DWORD(&dir[DIR_WrtTime], tim); + fp->flag &= (BYTE)~FA__WRITTEN; + res = sync(fp->fs); + } + } + return res; +} + +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Close File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_close ( + FIL *fp /* Pointer to the file object to be closed */ +) +{ + FRESULT res; + + +#if !_FS_READONLY + res = f_sync(fp); +#else + res = validate(fp->fs, fp->id); +#endif + if (res == FR_OK) fp->fs = NULL; + return res; +} + + + + +#if _FS_MINIMIZE <= 2 +/*-----------------------------------------------------------------------*/ +/* Seek File R/W Pointer */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_lseek ( + FIL *fp, /* Pointer to the file object */ + DWORD ofs /* File pointer from top of file */ +) +{ + FRESULT res; + DWORD clust, csize, nsect, ifptr; + + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) return res; + if (fp->flag & FA__ERROR) return FR_RW_ERROR; + if (ofs > fp->fsize /* In read-only mode, clip offset with the file size */ +#if !_FS_READONLY + && !(fp->flag & FA_WRITE) +#endif + ) ofs = fp->fsize; + + ifptr = fp->fptr; + fp->fptr = 0; fp->csect = 255; + nsect = 0; + if (ofs > 0) { + csize = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */ + if (ifptr > 0 && + (ofs - 1) / csize >= (ifptr - 1) / csize) {/* When seek to same or following cluster, */ + fp->fptr = (ifptr - 1) & ~(csize - 1); /* start from the current cluster */ + ofs -= fp->fptr; + clust = fp->curr_clust; + } else { /* When seek to back cluster, */ + clust = fp->org_clust; /* start from the first cluster */ +#if !_FS_READONLY + if (clust == 0) { /* If no cluster chain, create a new chain */ + clust = create_chain(fp->fs, 0); + if (clust == 1) goto fk_error; + fp->org_clust = clust; + } +#endif + fp->curr_clust = clust; + } + if (clust != 0) { + while (ofs > csize) { /* Cluster following loop */ +#if !_FS_READONLY + if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ + clust = create_chain(fp->fs, clust); /* Force streached if in write mode */ + if (clust == 0) { /* When disk gets full, clip file size */ + ofs = csize; break; + } + } else +#endif + clust = get_cluster(fp->fs, clust); /* Follow cluster chain if not in write mode */ + if (clust < 2 || clust >= fp->fs->max_clust) goto fk_error; + fp->curr_clust = clust; + fp->fptr += csize; + ofs -= csize; + } + fp->fptr += ofs; + fp->csect = (BYTE)(ofs / SS(fp->fs)); /* Sector offset in the cluster */ + if (ofs & (SS(fp->fs) - 1)) { + nsect = clust2sect(fp->fs, clust) + fp->csect; /* Current sector */ + fp->csect++; + } + } + } + if (nsect && nsect != fp->curr_sect) { +#if !_FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty buffer if needed */ + if (disk_write(fp->fs->drive, fp->buffer, fp->curr_sect, 1) != RES_OK) + goto fk_error; + fp->flag &= (BYTE)~FA__DIRTY; + } +#endif + if (disk_read(fp->fs->drive, fp->buffer, nsect, 1) != RES_OK) + goto fk_error; + fp->curr_sect = nsect; + } + +#if !_FS_READONLY + if (fp->fptr > fp->fsize) { /* Set changed flag if the file was extended */ + fp->fsize = fp->fptr; + fp->flag |= FA__WRITTEN; + } +#endif + + return FR_OK; + +fk_error: /* Abort this file due to an unrecoverable error */ + fp->flag |= FA__ERROR; + return FR_RW_ERROR; +} + + + + +#if _FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Create a directroy object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_opendir ( + DIR *dj, /* Pointer to directory object to create */ + const char *path /* Pointer to the directory path */ +) +{ + FRESULT res; + BYTE *dir; + char fn[8+3+1]; + + + res = auto_mount(&path, &dj->fs, 0); + if (res == FR_OK) { + res = trace_path(dj, fn, path, &dir); /* Trace the directory path */ + if (res == FR_OK) { /* Trace completed */ + if (dir) { /* It is not the root dir */ + if (dir[DIR_Attr] & AM_DIR) { /* The entry is a directory */ + dj->clust = ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) | LD_WORD(&dir[DIR_FstClusLO]); + dj->sect = clust2sect(dj->fs, dj->clust); + dj->index = 2; + } else { /* The entry is not a directory */ + res = FR_NO_FILE; + } + } + dj->id = dj->fs->id; + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entry in Sequense */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_readdir ( + DIR *dj, /* Pointer to the directory object */ + FILINFO *finfo /* Pointer to file information to return */ +) +{ + BYTE *dir, c, res; + + + res = validate(dj->fs, dj->id); /* Check validity of the object */ + if (res != FR_OK) return res; + + finfo->fname[0] = 0; + while (dj->sect) { + if (!move_window(dj->fs, dj->sect)) + return FR_RW_ERROR; + dir = &dj->fs->win[(dj->index & ((SS(dj->fs) - 1) >> 5)) * 32]; /* pointer to the directory entry */ + c = dir[DIR_Name]; + if (c == 0) break; /* Has it reached to end of dir? */ + if (c != 0xE5 && !(dir[DIR_Attr] & AM_VOL)) /* Is it a valid entry? */ + get_fileinfo(finfo, dir); + if (!next_dir_entry(dj)) dj->sect = 0; /* Next entry */ + if (finfo->fname[0]) break; /* Found valid entry */ + } + + return FR_OK; +} + + + + +#if _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const char *path, /* Pointer to the file path */ + FILINFO *finfo /* Pointer to file information to return */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + char fn[8+3+1]; + + + res = auto_mount(&path, &dj.fs, 0); + if (res == FR_OK) { + res = trace_path(&dj, fn, path, &dir); /* Trace the file path */ + if (res == FR_OK) { /* Trace completed */ + if (dir) /* Found an object */ + get_fileinfo(finfo, dir); + else /* It is root dir */ + res = FR_INVALID_NAME; + } + } + + return res; +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Truncate File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_truncate ( + FIL *fp /* Pointer to the file object */ +) +{ + FRESULT res; + DWORD ncl; + + + res = validate(fp->fs, fp->id); /* Check validity of the object */ + if (res != FR_OK) return res; + if (fp->flag & FA__ERROR) return FR_RW_ERROR; /* Check error flag */ + if (!(fp->flag & FA_WRITE)) return FR_DENIED; /* Check access mode */ + + if (fp->fsize > fp->fptr) { + fp->fsize = fp->fptr; /* Set file size to current R/W point */ + fp->flag |= FA__WRITTEN; + if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ + if (!remove_chain(fp->fs, fp->org_clust)) goto ft_error; + fp->org_clust = 0; + } else { /* When truncate a part of the file, remove remaining clusters */ + ncl = get_cluster(fp->fs, fp->curr_clust); + if (ncl < 2) goto ft_error; + if (ncl < fp->fs->max_clust) { + if (!put_cluster(fp->fs, fp->curr_clust, 0x0FFFFFFF)) goto ft_error; + if (!remove_chain(fp->fs, ncl)) goto ft_error; + } + } + } + + return FR_OK; + +ft_error: /* Abort this file due to an unrecoverable error */ + fp->flag |= FA__ERROR; + return FR_RW_ERROR; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const char *drv, /* Pointer to the logical drive number (root dir) */ + DWORD *nclust, /* Pointer to the variable to return number of free clusters */ + FATFS **fatfs /* Pointer to pointer to corresponding file system object to return */ +) +{ + FRESULT res; + DWORD n, clust, sect; + BYTE fat, f, *p; + + + /* Get drive number */ + res = auto_mount(&drv, fatfs, 0); + if (res != FR_OK) return res; + + /* If number of free cluster is valid, return it without cluster scan. */ + if ((*fatfs)->free_clust <= (*fatfs)->max_clust - 2) { + *nclust = (*fatfs)->free_clust; + return FR_OK; + } + + /* Get number of free clusters */ + fat = (*fatfs)->fs_type; + n = 0; + if (fat == FS_FAT12) { + clust = 2; + do { + if ((WORD)get_cluster(*fatfs, clust) == 0) n++; + } while (++clust < (*fatfs)->max_clust); + } else { + clust = (*fatfs)->max_clust; + sect = (*fatfs)->fatbase; + f = 0; p = 0; + do { + if (!f) { + if (!move_window(*fatfs, sect++)) return FR_RW_ERROR; + p = (*fatfs)->win; + } + if (fat == FS_FAT16) { + if (LD_WORD(p) == 0) n++; + p += 2; f += 1; + } else { + if (LD_DWORD(p) == 0) n++; + p += 4; f += 2; + } + } while (--clust); + } + (*fatfs)->free_clust = n; +#if _USE_FSINFO + if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1; +#endif + + *nclust = n; + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File or Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( + const char *path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir, *sdir; + DWORD dclust, dsect; + char fn[8+3+1]; + + + res = auto_mount(&path, &dj.fs, 1); + if (res != FR_OK) return res; + res = trace_path(&dj, fn, path, &dir); /* Trace the file path */ + if (res != FR_OK) return res; /* Trace failed */ + if (!dir) return FR_INVALID_NAME; /* It is the root directory */ + if (dir[DIR_Attr] & AM_RDO) return FR_DENIED; /* It is a R/O object */ + dsect = dj.fs->winsect; + dclust = ((DWORD)LD_WORD(&dir[DIR_FstClusHI]) << 16) | LD_WORD(&dir[DIR_FstClusLO]); + + if (dir[DIR_Attr] & AM_DIR) { /* It is a sub-directory */ + dj.clust = dclust; /* Check if the sub-dir is empty or not */ + dj.sect = clust2sect(dj.fs, dclust); + dj.index = 2; + do { + if (!move_window(dj.fs, dj.sect)) return FR_RW_ERROR; + sdir = &dj.fs->win[(dj.index & ((SS(dj.fs) - 1) >> 5)) * 32]; + if (sdir[DIR_Name] == 0) break; + if (sdir[DIR_Name] != 0xE5 && !(sdir[DIR_Attr] & AM_VOL)) + return FR_DENIED; /* The directory is not empty */ + } while (next_dir_entry(&dj)); + } + + if (!move_window(dj.fs, dsect)) return FR_RW_ERROR; /* Mark the directory entry 'deleted' */ + dir[DIR_Name] = 0xE5; + dj.fs->winflag = 1; + if (!remove_chain(dj.fs, dclust)) return FR_RW_ERROR; /* Remove the cluster chain */ + + return sync(dj.fs); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const char *path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir, *fw, n; + char fn[8+3+1]; + DWORD sect, dsect, dclust, pclust, tim; + + + res = auto_mount(&path, &dj.fs, 1); + if (res != FR_OK) return res; + res = trace_path(&dj, fn, path, &dir); /* Trace the file path */ + if (res == FR_OK) return FR_EXIST; /* Any file or directory is already existing */ + if (res != FR_NO_FILE) return res; + + res = reserve_direntry(&dj, &dir); /* Reserve a directory entry */ + if (res != FR_OK) return res; + sect = dj.fs->winsect; + dclust = create_chain(dj.fs, 0); /* Allocate a cluster for new directory table */ + if (dclust == 1) return FR_RW_ERROR; + dsect = clust2sect(dj.fs, dclust); + if (!dsect) return FR_DENIED; + if (!move_window(dj.fs, dsect)) return FR_RW_ERROR; + + fw = dj.fs->win; + memset(fw, 0, SS(dj.fs)); /* Clear the new directory table */ + for (n = 1; n < dj.fs->csize; n++) { + if (disk_write(dj.fs->drive, fw, ++dsect, 1) != RES_OK) + return FR_RW_ERROR; + } + memset(&fw[DIR_Name], ' ', 8+3); /* Create "." entry */ + fw[DIR_Name] = '.'; + fw[DIR_Attr] = AM_DIR; + tim = get_fattime(); + ST_DWORD(&fw[DIR_WrtTime], tim); + memcpy(&fw[32], &fw[0], 32); fw[33] = '.'; /* Create ".." entry */ + ST_WORD(&fw[ DIR_FstClusLO], dclust); + ST_WORD(&fw[ DIR_FstClusHI], dclust >> 16); + pclust = dj.sclust; + if (dj.fs->fs_type == FS_FAT32 && pclust == dj.fs->dirbase) pclust = 0; + ST_WORD(&fw[32+DIR_FstClusLO], pclust); + ST_WORD(&fw[32+DIR_FstClusHI], pclust >> 16); + dj.fs->winflag = 1; + + if (!move_window(dj.fs, sect)) return FR_RW_ERROR; + memset(&dir[0], 0, 32); /* Initialize the new entry */ + memcpy(&dir[DIR_Name], fn, 8+3); /* Name */ + dir[DIR_NTres] = fn[11]; + dir[DIR_Attr] = AM_DIR; /* Attribute */ + ST_DWORD(&dir[DIR_WrtTime], tim); /* Crated time */ + ST_WORD(&dir[DIR_FstClusLO], dclust); /* Table start cluster */ + ST_WORD(&dir[DIR_FstClusHI], dclust >> 16); + + return sync(dj.fs); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change File Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const char *path, /* Pointer to the file path */ + BYTE value, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + char fn[8+3+1]; + + + res = auto_mount(&path, &dj.fs, 1); + if (res == FR_OK) { + res = trace_path(&dj, fn, path, &dir); /* Trace the file path */ + if (res == FR_OK) { /* Trace completed */ + if (!dir) { + res = FR_INVALID_NAME; /* Root directory */ + } else { + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ + dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + res = sync(dj.fs); + } + } + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Timestamp */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_utime ( + const char *path, /* Pointer to the file/directory name */ + const FILINFO *finfo /* Pointer to the timestamp to be set */ +) +{ + FRESULT res; + DIR dj; + BYTE *dir; + char fn[8+3+1]; + + + res = auto_mount(&path, &dj.fs, 1); + if (res == FR_OK) { + res = trace_path(&dj, fn, path, &dir); /* Trace the file path */ + if (res == FR_OK) { /* Trace completed */ + if (!dir) { + res = FR_INVALID_NAME; /* Root directory */ + } else { + ST_WORD(&dir[DIR_WrtTime], finfo->ftime); + ST_WORD(&dir[DIR_WrtDate], finfo->fdate); + res = sync(dj.fs); + } + } + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Rename File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( + const char *path_old, /* Pointer to the old name */ + const char *path_new /* Pointer to the new name */ +) +{ + FRESULT res; + DIR dj; + DWORD sect_old; + BYTE *dir_old, *dir_new, direntry[32-11]; + char fn[8+3+1]; + + + res = auto_mount(&path_old, &dj.fs, 1); + if (res != FR_OK) return res; + + res = trace_path(&dj, fn, path_old, &dir_old); /* Check old object */ + if (res != FR_OK) return res; /* The old object is not found */ + if (!dir_old) return FR_NO_FILE; + sect_old = dj.fs->winsect; /* Save the object information */ + memcpy(direntry, &dir_old[DIR_Attr], 32-11); + + res = trace_path(&dj, fn, path_new, &dir_new); /* Check new object */ + if (res == FR_OK) return FR_EXIST; /* The new object name is already existing */ + if (res != FR_NO_FILE) return res; /* Is there no old name? */ + res = reserve_direntry(&dj, &dir_new); /* Reserve a directory entry */ + if (res != FR_OK) return res; + + memcpy(&dir_new[DIR_Attr], direntry, 32-11); /* Create new entry */ + memcpy(&dir_new[DIR_Name], fn, 8+3); + dir_new[DIR_NTres] = fn[11]; + dj.fs->winflag = 1; + + if (!move_window(dj.fs, sect_old)) return FR_RW_ERROR; /* Delete old entry */ + dir_old[DIR_Name] = 0xE5; + + return sync(dj.fs); +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_MINIMIZE == 0 */ +#endif /* _FS_MINIMIZE <= 1 */ +#endif /* _FS_MINIMIZE <= 2 */ + + + +#if _USE_MKFS && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Create File System on the Drive */ +/*-----------------------------------------------------------------------*/ +#define N_ROOTDIR 512 /* Multiple of 32 and <= 2048 */ +#define N_FATS 1 /* 1 or 2 */ +#define MAX_SECTOR 64000000UL /* Maximum partition size */ +#define MIN_SECTOR 2000UL /* Minimum partition size */ + + + +FRESULT f_mkfs ( + BYTE drv, /* Logical drive number */ + BYTE partition, /* Partitioning rule 0:FDISK, 1:SFD */ + WORD allocsize /* Allocation unit size [bytes] */ +) +{ + BYTE fmt, m, *tbl; + DWORD b_part, b_fat, b_dir, b_data; /* Area offset (LBA) */ + DWORD n_part, n_rsv, n_fat, n_dir; /* Area size */ + DWORD n_clust, n; + FATFS *fs; + DSTATUS stat; + + + /* Check validity of the parameters */ + if (drv >= _DRIVES) return FR_INVALID_DRIVE; + if (partition >= 2) return FR_MKFS_ABORTED; + for (n = 512; n <= 32768U && n != allocsize; n <<= 1); + if (n != allocsize) return FR_MKFS_ABORTED; + + /* Check mounted drive and clear work area */ + fs = FatFs[drv]; + if (!fs) return FR_NOT_ENABLED; + fs->fs_type = 0; + drv = LD2PD(drv); + + /* Get disk statics */ + stat = disk_initialize(drv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; + if (disk_ioctl(drv, GET_SECTOR_COUNT, &n_part) != RES_OK || n_part < MIN_SECTOR) + return FR_MKFS_ABORTED; + if (n_part > MAX_SECTOR) n_part = MAX_SECTOR; + b_part = (!partition) ? 63 : 0; /* Boot sector */ + n_part -= b_part; +#if S_MAX_SIZ > 512 /* Check disk sector size */ + if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK + || SS(fs) > S_MAX_SIZ + || SS(fs) > allocsize) + return FR_MKFS_ABORTED; +#endif + allocsize /= SS(fs); /* Number of sectors per cluster */ + + /* Pre-compute number of clusters and FAT type */ + n_clust = n_part / allocsize; + fmt = FS_FAT12; + if (n_clust >= 0xFF5) fmt = FS_FAT16; + if (n_clust >= 0xFFF5) fmt = FS_FAT32; + + /* Determine offset and size of FAT structure */ + switch (fmt) { + case FS_FAT12: + n_fat = ((n_clust * 3 + 1) / 2 + 3 + SS(fs) - 1) / SS(fs); + n_rsv = 1 + partition; + n_dir = N_ROOTDIR * 32 / SS(fs); + break; + case FS_FAT16: + n_fat = ((n_clust * 2) + 4 + SS(fs) - 1) / SS(fs); + n_rsv = 1 + partition; + n_dir = N_ROOTDIR * 32 / SS(fs); + break; + default: + n_fat = ((n_clust * 4) + 8 + SS(fs) - 1) / SS(fs); + n_rsv = 33 - partition; + n_dir = 0; + } + b_fat = b_part + n_rsv; /* FATs start sector */ + b_dir = b_fat + n_fat * N_FATS; /* Directory start sector */ + b_data = b_dir + n_dir; /* Data start sector */ + + /* Align data start sector to erase block boundary (for flash memory media) */ + if (disk_ioctl(drv, GET_BLOCK_SIZE, &n) != RES_OK) return FR_MKFS_ABORTED; + n = (b_data + n - 1) & ~(n - 1); + n_fat += (n - b_data) / N_FATS; + /* b_dir and b_data are no longer used below */ + + /* Determine number of cluster and final check of validity of the FAT type */ + n_clust = (n_part - n_rsv - n_fat * N_FATS - n_dir) / allocsize; + if ( (fmt == FS_FAT16 && n_clust < 0xFF5) + || (fmt == FS_FAT32 && n_clust < 0xFFF5)) + return FR_MKFS_ABORTED; + + /* Create partition table if needed */ + if (!partition) { + DWORD n_disk = b_part + n_part; + + tbl = &fs->win[MBR_Table]; + ST_DWORD(&tbl[0], 0x00010180); /* Partition start in CHS */ + if (n_disk < 63UL * 255 * 1024) { /* Partition end in CHS */ + n_disk = n_disk / 63 / 255; + tbl[7] = (BYTE)n_disk; + tbl[6] = (BYTE)((n_disk >> 2) | 63); + } else { + ST_WORD(&tbl[6], 0xFFFF); + } + tbl[5] = 254; + if (fmt != FS_FAT32) /* System ID */ + tbl[4] = (n_part < 0x10000) ? 0x04 : 0x06; + else + tbl[4] = 0x0c; + ST_DWORD(&tbl[8], 63); /* Partition start in LBA */ + ST_DWORD(&tbl[12], n_part); /* Partition size in LBA */ + ST_WORD(&tbl[64], 0xAA55); /* Signature */ + if (disk_write(drv, fs->win, 0, 1) != RES_OK) + return FR_RW_ERROR; + } + + /* Create boot record */ + tbl = fs->win; /* Clear buffer */ + memset(tbl, 0, SS(fs)); + ST_DWORD(&tbl[BS_jmpBoot], 0x90FEEB); /* Boot code (jmp $, nop) */ + ST_WORD(&tbl[BPB_BytsPerSec], SS(fs)); /* Sector size */ + tbl[BPB_SecPerClus] = (BYTE)allocsize; /* Sectors per cluster */ + ST_WORD(&tbl[BPB_RsvdSecCnt], n_rsv); /* Reserved sectors */ + tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */ + ST_WORD(&tbl[BPB_RootEntCnt], SS(fs) / 32 * n_dir); /* Number of rootdir entries */ + if (n_part < 0x10000) { /* Number of total sectors */ + ST_WORD(&tbl[BPB_TotSec16], n_part); + } else { + ST_DWORD(&tbl[BPB_TotSec32], n_part); + } + tbl[BPB_Media] = 0xF8; /* Media descripter */ + ST_WORD(&tbl[BPB_SecPerTrk], 63); /* Number of sectors per track */ + ST_WORD(&tbl[BPB_NumHeads], 255); /* Number of heads */ + ST_DWORD(&tbl[BPB_HiddSec], b_part); /* Hidden sectors */ + n = get_fattime(); /* Use current time as a VSN */ + if (fmt != FS_FAT32) { + ST_DWORD(&tbl[BS_VolID], n); /* Volume serial number */ + ST_WORD(&tbl[BPB_FATSz16], n_fat); /* Number of secters per FAT */ + tbl[BS_DrvNum] = 0x80; /* Drive number */ + tbl[BS_BootSig] = 0x29; /* Extended boot signature */ + memcpy(&tbl[BS_VolLab], "NO NAME FAT ", 19); /* Volume lavel, FAT signature */ + } else { + ST_DWORD(&tbl[BS_VolID32], n); /* Volume serial number */ + ST_DWORD(&tbl[BPB_FATSz32], n_fat); /* Number of secters per FAT */ + ST_DWORD(&tbl[BPB_RootClus], 2); /* Root directory cluster (2) */ + ST_WORD(&tbl[BPB_FSInfo], 1); /* FSInfo record (bs+1) */ + ST_WORD(&tbl[BPB_BkBootSec], 6); /* Backup boot record (bs+6) */ + tbl[BS_DrvNum32] = 0x80; /* Drive number */ + tbl[BS_BootSig32] = 0x29; /* Extended boot signature */ + memcpy(&tbl[BS_VolLab32], "NO NAME FAT32 ", 19); /* Volume lavel, FAT signature */ + } + ST_WORD(&tbl[BS_55AA], 0xAA55); /* Signature */ + if (disk_write(drv, tbl, b_part+0, 1) != RES_OK) + return FR_RW_ERROR; + if (fmt == FS_FAT32) + disk_write(drv, tbl, b_part+6, 1); + + /* Initialize FAT area */ + for (m = 0; m < N_FATS; m++) { + memset(tbl, 0, SS(fs)); /* 1st sector of the FAT */ + if (fmt != FS_FAT32) { + n = (fmt == FS_FAT12) ? 0x00FFFFF8 : 0xFFFFFFF8; + ST_DWORD(&tbl[0], n); /* Reserve cluster #0-1 (FAT12/16) */ + } else { + ST_DWORD(&tbl[0], 0xFFFFFFF8); /* Reserve cluster #0-1 (FAT32) */ + ST_DWORD(&tbl[4], 0xFFFFFFFF); + ST_DWORD(&tbl[8], 0x0FFFFFFF); /* Reserve cluster #2 for root dir */ + } + if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) + return FR_RW_ERROR; + memset(tbl, 0, SS(fs)); /* Following FAT entries are filled by zero */ + for (n = 1; n < n_fat; n++) { + if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) + return FR_RW_ERROR; + } + } + + /* Initialize Root directory */ + m = (BYTE)((fmt == FS_FAT32) ? allocsize : n_dir); + do { + if (disk_write(drv, tbl, b_fat++, 1) != RES_OK) + return FR_RW_ERROR; + } while (--m); + + /* Create FSInfo record if needed */ + if (fmt == FS_FAT32) { + ST_WORD(&tbl[BS_55AA], 0xAA55); + ST_DWORD(&tbl[FSI_LeadSig], 0x41615252); + ST_DWORD(&tbl[FSI_StrucSig], 0x61417272); + ST_DWORD(&tbl[FSI_Free_Count], n_clust - 1); + ST_DWORD(&tbl[FSI_Nxt_Free], 0xFFFFFFFF); + disk_write(drv, tbl, b_part+1, 1); + disk_write(drv, tbl, b_part+7, 1); + } + + return (disk_ioctl(drv, CTRL_SYNC, NULL) == RES_OK) ? FR_OK : FR_RW_ERROR; +} + +#endif /* _USE_MKFS && !_FS_READONLY */ + + + + +#if _USE_STRFUNC >= 1 +/*-----------------------------------------------------------------------*/ +/* Get a string from the file */ +/*-----------------------------------------------------------------------*/ +char* fgets ( + char* buff, /* Pointer to the string buffer to read */ + int len, /* Size of string buffer */ + FIL* fil /* Pointer to the file object */ +) +{ + int i = 0; + char *p = buff; + UINT rc; + + + while (i < len - 1) { /* Read bytes until buffer gets filled */ + f_read(fil, p, 1, &rc); + if (rc != 1) break; /* Break when no data to read */ +#if _USE_STRFUNC >= 2 + if (*p == '\r') continue; /* Strip '\r' */ +#endif + i++; + if (*p++ == '\n') break; /* Break when reached end of line */ + } + *p = 0; + return i ? buff : 0; /* When no data read (eof or error), return with error. */ +} + + + +#if !_FS_READONLY +#include +/*-----------------------------------------------------------------------*/ +/* Put a character to the file */ +/*-----------------------------------------------------------------------*/ +int fputc ( + int chr, /* A character to be output */ + FIL* fil /* Ponter to the file object */ +) +{ + UINT bw; + char c; + + +#if _USE_STRFUNC >= 2 + if (chr == '\n') fputc ('\r', fil); /* LF -> CRLF conversion */ +#endif + if (!fil) { /* Special value may be used to switch the destination to any other device */ + /* put_console(chr); */ + return chr; + } + c = (char)chr; + f_write(fil, &c, 1, &bw); /* Write a byte to the file */ + return bw ? chr : EOF; /* Return the resulut */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a string to the file */ +/*-----------------------------------------------------------------------*/ +int fputs ( + const char* str, /* Pointer to the string to be output */ + FIL* fil /* Pointer to the file object */ +) +{ + int n; + + + for (n = 0; *str; str++, n++) { + if (fputc(*str, fil) == EOF) return EOF; + } + return n; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a formatted string to the file */ +/*-----------------------------------------------------------------------*/ +int fprintf ( + FIL* fil, /* Pointer to the file object */ + const char* str, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + UCHAR c, f, r; + ULONG val; + char s[16]; + int i, w, res, cc; + + + va_start(arp, str); + + for (cc = res = 0; cc != EOF; res += cc) { + c = *str++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape cahracter */ + cc = fputc(c, fil); + if (cc != EOF) cc = 1; + continue; + } + w = f = 0; + c = *str++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *str++; + } + while (c >= '0' && c <= '9') { /* Precision */ + w = w * 10 + (c - '0'); + c = *str++; + } + if (c == 'l') { /* Prefix: Size is long int */ + f |= 2; c = *str++; + } + if (c == 's') { /* Type is string */ + cc = fputs(va_arg(arp, char*), fil); + continue; + } + if (c == 'c') { /* Type is character */ + cc = fputc(va_arg(arp, char), fil); + if (cc != EOF) cc = 1; + continue; + } + r = 0; + if (c == 'd') r = 10; /* Type is signed decimal */ + if (c == 'u') r = 10; /* Type is unsigned decimal */ + if (c == 'X') r = 16; /* Type is unsigned hexdecimal */ + if (r == 0) break; /* Unknown type */ + if (f & 2) { /* Get the value */ + val = (ULONG)va_arg(arp, long); + } else { + val = (c == 'd') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int); + } + /* Put numeral string */ + if (c == 'd') { + if (val >= 0x80000000) { + val = 0 - val; + f |= 4; + } + } + i = sizeof(s) - 1; s[i] = 0; + do { + c = (UCHAR)(val % r + '0'); + if (c > '9') c += 7; + s[--i] = c; + val /= r; + } while (i && val); + if (i && (f & 4)) s[--i] = '-'; + w = sizeof(s) - 1 - w; + while (i && i > w) s[--i] = (f & 1) ? '0' : ' '; + cc = fputs(&s[i], fil); + } + + va_end(arp); + return (cc == EOF) ? cc : res; +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_STRFUNC >= 1*/ diff --git a/ff.h b/ff.h new file mode 100644 index 0000000..ad650e6 --- /dev/null +++ b/ff.h @@ -0,0 +1,339 @@ +/*--------------------------------------------------------------------------/ +/ FatFs - FAT file system module include file R0.06 (C)ChaN, 2008 +/---------------------------------------------------------------------------/ +/ FatFs module is an experimenal project to implement FAT file system to +/ cheap microcontrollers. This is a free software and is opened for education, +/ research and development under license policy of following trems. +/ +/ Copyright (C) 2008, ChaN, all right reserved. +/ +/ * The FatFs module is a free software and there is no warranty. +/ * You can use, modify and/or redistribute it for personal, non-profit or +/ commercial use without any restriction under your responsibility. +/ * Redistributions of source code must retain the above copyright notice. +/ +/---------------------------------------------------------------------------*/ + +#ifndef _FATFS + +#define _MCU_ENDIAN 2 +/* The _MCU_ENDIAN defines which access method is used to the FAT structure. +/ 1: Enable word access. +/ 2: Disable word access and use byte-by-byte access instead. +/ When the architectural byte order of the MCU is big-endian and/or address +/ miss-aligned access results incorrect behavior, the _MCU_ENDIAN must be set to 2. +/ If it is not the case, it can also be set to 1 for good code efficiency. */ + +#define _FS_READONLY 1 +/* Setting _FS_READONLY to 1 defines read only configuration. This removes +/ writing functions, f_write, f_sync, f_unlink, f_mkdir, f_chmod, f_rename, +/ f_truncate and useless f_getfree. */ + +#define _FS_MINIMIZE 2 +/* The _FS_MINIMIZE option defines minimization level to remove some functions. +/ 0: Full function. +/ 1: f_stat, f_getfree, f_unlink, f_mkdir, f_chmod, f_truncate and f_rename are removed. +/ 2: f_opendir and f_readdir are removed in addition to level 1. +/ 3: f_lseek is removed in addition to level 2. */ + +#define _USE_STRFUNC 0 +/* To enable string functions, set _USE_STRFUNC to 1 or 2. */ + +#define _USE_MKFS 0 +/* When _USE_MKFS is set to 1 and _FS_READONLY is set to 0, f_mkfs function is +/ enabled. */ + +#define _DRIVES 2 +/* Number of logical drives to be used. This affects the size of internal table. */ + +#define _MULTI_PARTITION 0 +/* When _MULTI_PARTITION is set to 0, each logical drive is bound to same +/ physical drive number and can mount only 1st primaly partition. When it is +/ set to 1, each logical drive can mount a partition listed in Drives[]. */ + +#define _USE_FSINFO 0 +/* To enable FSInfo support on FAT32 volume, set _USE_FSINFO to 1. */ + +#define _USE_SJIS 1 +/* When _USE_SJIS is set to 1, Shift-JIS code transparency is enabled, otherwise +/ only US-ASCII(7bit) code can be accepted as file/directory name. */ + +#define _USE_NTFLAG 1 +/* When _USE_NTFLAG is set to 1, upper/lower case of the file name is preserved. +/ Note that the files are always accessed in case insensitive. */ + + +#include "integer.h" + + + +/* Definitions corresponds to multiple sector size (not tested) */ +#define S_MAX_SIZ 512U /* Do not change */ +#if S_MAX_SIZ > 512U +#define SS(fs) ((fs)->s_size) +#else +#define SS(fs) 512U +#endif + + +/* File system object structure */ +typedef struct _FATFS { + WORD id; /* File system mount ID */ + WORD n_rootdir; /* Number of root directory entries */ + DWORD winsect; /* Current sector appearing in the win[] */ + DWORD sects_fat; /* Sectors per fat */ + DWORD max_clust; /* Maximum cluster# + 1 */ + DWORD fatbase; /* FAT start sector */ + DWORD dirbase; /* Root directory start sector (cluster# for FAT32) */ + DWORD database; /* Data start sector */ +#if !_FS_READONLY + DWORD last_clust; /* Last allocated cluster */ + DWORD free_clust; /* Number of free clusters */ +#if _USE_FSINFO + DWORD fsi_sector; /* fsinfo sector */ + BYTE fsi_flag; /* fsinfo dirty flag (1:must be written back) */ + BYTE pad2; +#endif +#endif + BYTE fs_type; /* FAT sub type */ + BYTE csize; /* Number of sectors per cluster */ +#if S_MAX_SIZ > 512U + WORD s_size; /* Sector size */ +#endif + BYTE n_fats; /* Number of FAT copies */ + BYTE drive; /* Physical drive number */ + BYTE winflag; /* win[] dirty flag (1:must be written back) */ + BYTE pad1; + BYTE win[S_MAX_SIZ]; /* Disk access window for Directory/FAT */ +} FATFS; + + +/* Directory object structure */ +typedef struct _DIR { + WORD id; /* Owner file system mount ID */ + WORD index; /* Current index */ + FATFS* fs; /* Pointer to the owner file system object */ + DWORD sclust; /* Start cluster */ + DWORD clust; /* Current cluster */ + DWORD sect; /* Current sector */ +} DIR; + + +/* File object structure */ +typedef struct _FIL { + WORD id; /* Owner file system mount ID */ + BYTE flag; /* File status flags */ + BYTE csect; /* Sector address in the cluster */ + FATFS* fs; /* Pointer to the owner file system object */ + DWORD fptr; /* File R/W pointer */ + DWORD fsize; /* File size */ + DWORD org_clust; /* File start cluster */ + DWORD curr_clust; /* Current cluster */ + DWORD curr_sect; /* Current sector */ +#if _FS_READONLY == 0 + DWORD dir_sect; /* Sector containing the directory entry */ + BYTE* dir_ptr; /* Ponter to the directory entry in the window */ +#endif + BYTE buffer[S_MAX_SIZ]; /* File R/W buffer */ +} FIL; + + +/* File status structure */ +typedef struct _FILINFO { + DWORD fsize; /* Size */ + WORD fdate; /* Date */ + WORD ftime; /* Time */ + BYTE fattrib; /* Attribute */ + char fname[8+1+3+1]; /* Name (8.3 format) */ +} FILINFO; + + + +/* Definitions corresponds to multi partition */ + +#if _MULTI_PARTITION != 0 /* Multiple partition cfg */ + +typedef struct _PARTITION { + BYTE pd; /* Physical drive # (0-255) */ + BYTE pt; /* Partition # (0-3) */ +} PARTITION; +extern +const PARTITION Drives[]; /* Logical drive# to physical location conversion table */ +#define LD2PD(drv) (Drives[drv].pd) /* Get physical drive# */ +#define LD2PT(drv) (Drives[drv].pt) /* Get partition# */ + +#else /* Single partition cfg */ + +#define LD2PD(drv) (drv) /* Physical drive# is equal to logical drive# */ +#define LD2PT(drv) 0 /* Always mounts the 1st partition */ + +#endif + + +/* File function return code (FRESULT) */ + +typedef enum { + FR_OK = 0, /* 0 */ + FR_NOT_READY, /* 1 */ + FR_NO_FILE, /* 2 */ + FR_NO_PATH, /* 3 */ + FR_INVALID_NAME, /* 4 */ + FR_INVALID_DRIVE, /* 5 */ + FR_DENIED, /* 6 */ + FR_EXIST, /* 7 */ + FR_RW_ERROR, /* 8 */ + FR_WRITE_PROTECTED, /* 9 */ + FR_NOT_ENABLED, /* 10 */ + FR_NO_FILESYSTEM, /* 11 */ + FR_INVALID_OBJECT, /* 12 */ + FR_MKFS_ABORTED /* 13 */ +} FRESULT; + + + +/*-----------------------------------------------------*/ +/* FatFs module application interface */ + +FRESULT f_mount (BYTE, FATFS*); /* Mount/Unmount a logical drive */ +FRESULT f_open (FIL*, const char*, BYTE); /* Open or create a file */ +FRESULT f_read (FIL*, void*, UINT, UINT*); /* Read data from a file */ +FRESULT f_write (FIL*, const void*, UINT, UINT*); /* Write data to a file */ +FRESULT f_lseek (FIL*, DWORD); /* Move file pointer of a file object */ +FRESULT f_close (FIL*); /* Close an open file object */ +FRESULT f_opendir (DIR*, const char*); /* Open an existing directory */ +FRESULT f_readdir (DIR*, FILINFO*); /* Read a directory item */ +FRESULT f_stat (const char*, FILINFO*); /* Get file status */ +FRESULT f_getfree (const char*, DWORD*, FATFS**); /* Get number of free clusters on the drive */ +FRESULT f_truncate (FIL*); /* Truncate file */ +FRESULT f_sync (FIL*); /* Flush cached data of a writing file */ +FRESULT f_unlink (const char*); /* Delete an existing file or directory */ +FRESULT f_mkdir (const char*); /* Create a new directory */ +FRESULT f_chmod (const char*, BYTE, BYTE); /* Change file/dir attriburte */ +FRESULT f_utime (const char*, const FILINFO*); /* Change file/dir timestamp */ +FRESULT f_rename (const char*, const char*); /* Rename/Move a file or directory */ +FRESULT f_mkfs (BYTE, BYTE, WORD); /* Create a file system on the drive */ +#if _USE_STRFUNC +#define feof(fp) ((fp)->fptr == (fp)->fsize) +#define EOF -1 +int fputc (int, FIL*); /* Put a character to the file */ +int fputs (const char*, FIL*); /* Put a string to the file */ +int fprintf (FIL*, const char*, ...); /* Put a formatted string to the file */ +char* fgets (char*, int, FIL*); /* Get a string from the file */ +#endif + +/* User defined function to give a current time to fatfs module */ + +DWORD get_fattime (void); /* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */ + /* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */ + + + +/* File access control and file status flags (FIL.flag) */ + +#define FA_READ 0x01 +#define FA_OPEN_EXISTING 0x00 +#if _FS_READONLY == 0 +#define FA_WRITE 0x02 +#define FA_CREATE_NEW 0x04 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA__WRITTEN 0x20 +#define FA__DIRTY 0x40 +#endif +#define FA__ERROR 0x80 + + +/* FAT sub type (FATFS.fs_type) */ + +#define FS_FAT12 1 +#define FS_FAT16 2 +#define FS_FAT32 3 + + +/* File attribute bits for directory entry */ + +#define AM_RDO 0x01 /* Read only */ +#define AM_HID 0x02 /* Hidden */ +#define AM_SYS 0x04 /* System */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_DIR 0x10 /* Directory */ +#define AM_ARC 0x20 /* Archive */ + + + +/* Offset of FAT structure members */ + +#define BS_jmpBoot 0 +#define BS_OEMName 3 +#define BPB_BytsPerSec 11 +#define BPB_SecPerClus 13 +#define BPB_RsvdSecCnt 14 +#define BPB_NumFATs 16 +#define BPB_RootEntCnt 17 +#define BPB_TotSec16 19 +#define BPB_Media 21 +#define BPB_FATSz16 22 +#define BPB_SecPerTrk 24 +#define BPB_NumHeads 26 +#define BPB_HiddSec 28 +#define BPB_TotSec32 32 +#define BS_55AA 510 + +#define BS_DrvNum 36 +#define BS_BootSig 38 +#define BS_VolID 39 +#define BS_VolLab 43 +#define BS_FilSysType 54 + +#define BPB_FATSz32 36 +#define BPB_ExtFlags 40 +#define BPB_FSVer 42 +#define BPB_RootClus 44 +#define BPB_FSInfo 48 +#define BPB_BkBootSec 50 +#define BS_DrvNum32 64 +#define BS_BootSig32 66 +#define BS_VolID32 67 +#define BS_VolLab32 71 +#define BS_FilSysType32 82 + +#define FSI_LeadSig 0 +#define FSI_StrucSig 484 +#define FSI_Free_Count 488 +#define FSI_Nxt_Free 492 + +#define MBR_Table 446 + +#define DIR_Name 0 +#define DIR_Attr 11 +#define DIR_NTres 12 +#define DIR_CrtTime 14 +#define DIR_CrtDate 16 +#define DIR_FstClusHI 20 +#define DIR_WrtTime 22 +#define DIR_WrtDate 24 +#define DIR_FstClusLO 26 +#define DIR_FileSize 28 + + + +/* Multi-byte word access macros */ + +#if _MCU_ENDIAN == 1 /* Use word access */ +#define LD_WORD(ptr) (WORD)(*(WORD*)(BYTE*)(ptr)) +#define LD_DWORD(ptr) (DWORD)(*(DWORD*)(BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(WORD)(val) +#define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(DWORD)(val) +#elif _MCU_ENDIAN == 2 /* Use byte-by-byte access */ +#define LD_WORD(ptr) (WORD)(((WORD)*(volatile BYTE*)((ptr)+1)<<8)|(WORD)*(volatile BYTE*)(ptr)) +#define LD_DWORD(ptr) (DWORD)(((DWORD)*(volatile BYTE*)((ptr)+3)<<24)|((DWORD)*(volatile BYTE*)((ptr)+2)<<16)|((WORD)*(volatile BYTE*)((ptr)+1)<<8)|*(volatile BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(volatile BYTE*)(ptr)=(BYTE)(val); *(volatile BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8) +#define ST_DWORD(ptr,val) *(volatile BYTE*)(ptr)=(BYTE)(val); *(volatile BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8); *(volatile BYTE*)((ptr)+2)=(BYTE)((DWORD)(val)>>16); *(volatile BYTE*)((ptr)+3)=(BYTE)((DWORD)(val)>>24) +#else +#error Do not forget to set _MCU_ENDIAN properly! +#endif + + +#define _FATFS +#endif /* _FATFS */ diff --git a/gecko.c b/gecko.c new file mode 100644 index 0000000..b24b361 --- /dev/null +++ b/gecko.c @@ -0,0 +1,201 @@ +#include "types.h" +#include "start.h" +#include "vsprintf.h" +#include "string.h" +#include "utils.h" +#include "hollywood.h" + +// These two don't really seem to be needed +// Maybe only for boot buffer or some PPC stuff +static inline void _gecko_get(void) +{ + //set32(HW_EXICTRL, 1); +} + +static inline void _gecko_release(void) +{ + //clear32(HW_EXICTRL, 1); +} + +static u32 _gecko_command(u32 command) +{ + u32 i; + // Memory Card Port B (Channel 1, Device 0, Frequency 3 (32Mhz Clock)) + write32(EXI1_CSR, 0xd0); + write32(EXI1_DATA, command); + write32(EXI1_CR, 0x19); + i = 1000; + while ((read32(EXI1_CR) & 1) && (i--)); + i = read32(EXI1_DATA); + write32(EXI1_CSR, 0); + return i; +} + +static u32 _gecko_sendbyte(char sendbyte) +{ + u32 i = 0; + i = _gecko_command(0xB0000000 | (sendbyte<<20)); + if (i&0x04000000) + return 1; // Return 1 if byte was sent + return 0; +} + +static u32 _gecko_recvbyte(char *recvbyte) +{ + u32 i = 0; + *recvbyte = 0; + i = _gecko_command(0xA0000000); + if (i&0x08000000) { + // Return 1 if byte was received + *recvbyte = (i>>16)&0xff; + return 1; + } + return 0; +} + +static u32 _gecko_checksend(void) +{ + u32 i = 0; + i = _gecko_command(0xC0000000); + if (i&0x04000000) + return 1; // Return 1 if safe to send + return 0; +} + +static u32 _gecko_checkrecv(void) +{ + u32 i = 0; + i = _gecko_command(0xD0000000); + if (i&0x04000000) + return 1; // Return 1 if safe to recv + return 0; +} + +void gecko_init(void) +{ + write32(EXI0_CSR, 0); + write32(EXI1_CSR, 0); + write32(EXI2_CSR, 0); + write32(EXI0_CSR, 0x2000); + write32(EXI0_CSR, 3<<10); + write32(EXI1_CSR, 3<<10); +} + +void gecko_flush(void) +{ + char tmp; + while(_gecko_recvbyte(&tmp)); +} + +int gecko_isalive(void) +{ + u32 i = 0; + i = _gecko_command(0x90000000); + if (i&0x04700000) + return 1; + return 0; +} + +int gecko_recvbuffer(void *buffer, u32 size) +{ + u32 left = size; + char *ptr = (char*)buffer; + + _gecko_get(); + while(left>0) { + if(!_gecko_recvbyte(ptr)) + break; + ptr++; + left--; + } + _gecko_release(); + return (size - left); +} + +int gecko_sendbuffer(const void *buffer, u32 size) +{ + u32 left = size; + char *ptr = (char*)buffer; + + _gecko_get(); + while(left>0) { + if(!_gecko_sendbyte(*ptr)) + break; + ptr++; + left--; + } + _gecko_release(); + return (size - left); +} + +int gecko_recvbuffer_safe(void *buffer, u32 size) +{ + u32 left = size; + char *ptr = (char*)buffer; + + _gecko_get(); + while(left>0) { + if(_gecko_checkrecv()) { + if(!_gecko_recvbyte(ptr)) + break; + ptr++; + left--; + } + } + _gecko_release(); + return (size - left); +} + +int gecko_sendbuffer_safe(const void *buffer, u32 size) +{ + u32 left = size; + char *ptr = (char*)buffer; + + if((read32(HW_EXICTRL) & EXICTRL_ENABLE_EXI) == 0) + return left; + + _gecko_get(); + while(left>0) { + if(_gecko_checksend()) { + if(!_gecko_sendbyte(*ptr)) + break; + ptr++; + left--; + } + } + _gecko_release(); + return (size - left); +} + +int gecko_putchar(int ic) +{ + char b = ic; + return gecko_sendbuffer(&b, 1); +} + +int gecko_getchar(void) +{ + char b; + if(gecko_recvbuffer_safe(&b, 1) != 1) + return -1; + return b; +} + +int gecko_puts(const char *s) +{ + udelay(10000); + return gecko_sendbuffer(s, strlen(s)); +} + +int gecko_printf( const char *fmt, ...) +{ + va_list args; + char buffer[1024]; + int i; + + va_start(args, fmt); + i = vsprintf(buffer, fmt, args); + va_end(args); + gecko_puts(buffer); + return i; +} diff --git a/gecko.h b/gecko.h new file mode 100644 index 0000000..a949525 --- /dev/null +++ b/gecko.h @@ -0,0 +1,18 @@ +#ifndef __GECKO_H__ +#define __GECKO_H__ + +#include "types.h" + +void gecko_flush(void); +int gecko_isalive(void); +int gecko_recvbuffer(void *buffer, u32 size); +int gecko_sendbuffer(const void *buffer, u32 size); +int gecko_recvbuffer_safe(void *buffer, u32 size); +int gecko_sendbuffer_safe(const void *buffer, u32 size); +int gecko_putchar(int c); +int gecko_getchar(void); +int gecko_puts(const char *s); +int gecko_printf( const char *fmt, ...); +void gecko_init(void); + +#endif diff --git a/hollywood.h b/hollywood.h new file mode 100644 index 0000000..0486e9f --- /dev/null +++ b/hollywood.h @@ -0,0 +1,258 @@ +#ifndef __HOLLYWOOD_H__ +#define __HOLLYWOOD_H__ + +/* Hollywood Registers */ + +#define HW_PPC_REG_BASE 0xd000000 +#define HW_REG_BASE 0xd800000 + +// The PPC can only see the first three IPC registers +#define HW_IPC_PPCMSG (HW_REG_BASE + 0x000) //PPC to ARM +#define HW_IPC_PPCCTRL (HW_REG_BASE + 0x004) +#define HW_IPC_ARMMSG (HW_REG_BASE + 0x008) //ARM to PPC +#define HW_IPC_ARMCTRL (HW_REG_BASE + 0x00c) + +// Write one to send a message. Cleared when peer writes one to IPC_CTRL_RECV. +#define IPC_CTRL_SEND 0x01 +// Set by peer to acknowledge a message. Write one to clear. +#define IPC_CTRL_SENT 0x02 +// Set by peer to send a message. Write one to clear. +#define IPC_CTRL_RECV 0x04 +// Write one acknowledge a message. Cleared when peer writes one to IPC_CTRL_SENT. +#define IPC_CTRL_RECVD 0x08 +// Enable interrupt when a message is received +#define IPC_CTRL_INT_RECV 0x10 +// Enable interrupt when a sent message is acknowledged +#define IPC_CTRL_INT_SENT 0x20 + +/* + The IPC registers are connected to each other. + Both registers are identical and this works for + both peers. Flag bits are cleared by writing a one + to them. + + When Peer A sets this Peer B sees this set + IPC_CTRL_SEND IPC_CTRL_RECV + IPC_CTRL_RECVD IPC_CTRL_SENT + + In fact, bit _SEND on Peer A and bit _RECV on peer B are the same bit, + and the same goes for _RECVD and _SENT, except writing one from A _sets_ + the bit, and writing one from B _clears_ the bit. The same, of course, + is true for the other pair of bits in the other direction. + + The flow, therefore, goes as follows, for a message + from A to B. Steps with the same number can be taken + in any order. + + 1. Peer A writes the message address to the register + 2. Peer A sets IPC_CTRL_SEND + 3. Peer B sees IPC_CTRL_RECV + 4. Peer B writes one to IPC_CTRL_RECV to clear it (A's IPC_CTRL_SEND is cleared at this point) + 4. Peer B reads its message address register + 5. Peer B sets IPC_CTRL_RECVD + 6. Peer A sees IPC_CTRL_SENT + 7. Peer A writes one to IPC_CTRL_SENT to clear it (B's IPC_CTRL_RECVD is cleared at this point) + 7. Peer A may now write to the message address register again + + The same is true for a message from Peer B to Peer A. + + In the particular case of IOS IPC, the PPC is always the "master" + (it sends requests as messages) and the ARM is always the "slave" + (it replies to PPC's requests). IOS can handle up to 16(15?) + simultaneously pending transactions (the PPC can send up to 16(15?) + messages without getting any replies - for example, due to + asynchronous requests or multithreaded execution of blocking + requests) + +*/ + +#define HW_TIMER (HW_REG_BASE + 0x010) +#define HW_ALARM (HW_REG_BASE + 0x014) + +// maybe? +#define HW_FIQFLAG (HW_REG_BASE + 0x030) +#define HW_FIQENABLE (HW_REG_BASE + 0x034) + +#define HW_IRQFLAG (HW_REG_BASE + 0x038) +#define HW_IRQENABLE (HW_REG_BASE + 0x03c) + +#define HW_MEMMIRR (HW_REG_BASE + 0x060) + +// something to do with PPCBOOT +// and legacy DI it seems ?!? +#define HW_EXICTRL (HW_REG_BASE + 0x070) +#define EXICTRL_ENABLE_EXI 1 + +// PPC side of GPIO1 (Starlet can access this too) +// Output state +#define HW_GPIO1BOUT (HW_REG_BASE + 0x0c0) +// Direction (1=output) +#define HW_GPIO1BDIR (HW_REG_BASE + 0x0c4) +// Input state +#define HW_GPIO1BIN (HW_REG_BASE + 0x0c8) +// Interrupt level +#define HW_GPIO1BINTLVL (HW_REG_BASE + 0x0cc) +// Interrupt flags (write 1 to clear) +#define HW_GPIO1BINTFLAG (HW_REG_BASE + 0x0d0) +// Interrupt propagation enable +// Do these interrupts go anywhere??? +#define HW_GPIO1BINTENABLE (HW_REG_BASE + 0x0d4) +//??? seems to be a mirror of inputs at some point... power-up state? +#define HW_GPIO1BINMIR (HW_REG_BASE + 0x0d8) +// 0xFFFFFF by default, if cleared disables respective outputs. Top bits non-settable. +#define HW_GPIO1ENABLE (HW_REG_BASE + 0x0dc) + +#define HW_GPIO1_SLOT 0x000020 +#define HW_GPIO1_DEBUG 0xFF0000 +#define HW_GPIO1_DEBUG_SH 16 + +// Starlet side of GPIO1 +// Output state +#define HW_GPIO1OUT (HW_REG_BASE + 0x0e0) +// Direction (1=output) +#define HW_GPIO1DIR (HW_REG_BASE + 0x0e4) +// Input state +#define HW_GPIO1IN (HW_REG_BASE + 0x0e8) +// Interrupt level +#define HW_GPIO1INTLVL (HW_REG_BASE + 0x0ec) +// Interrupt flags (write 1 to clear) +#define HW_GPIO1INTFLAG (HW_REG_BASE + 0x0f0) +// Interrupt propagation enable (interrupts go to main interrupt 0x800) +#define HW_GPIO1INTENABLE (HW_REG_BASE + 0x0f4) +//??? seems to be a mirror of inputs at some point... power-up state? +#define HW_GPIO1INMIR (HW_REG_BASE + 0x0f8) +// Owner of each GPIO bit. If 1, GPIO1B registers assume control. If 0, GPIO1 registers assume control. +#define HW_GPIO1OWNER (HW_REG_BASE + 0x0fc) + +// ???? +#define HW_DIFLAGS (HW_REG_BASE + 0x180) +#define DIFLAGS_BOOT_CODE 0x100000 + +// maybe a GPIO??? +#define HW_RESETS (HW_REG_BASE + 0x194) + +#define HW_GPIO2OUT (HW_REG_BASE + 0x1c8) +#define HW_GPIO2DIR (HW_REG_BASE + 0x1cc) +#define HW_GPIO2IN (HW_REG_BASE + 0x1d0) + +#define HW_OTPCMD (HW_REG_BASE + 0x1ec) +#define HW_OTPDATA (HW_REG_BASE + 0x1f0) + + +/* NAND Registers */ + +#define NAND_REG_BASE 0xd010000 + +#define NAND_CMD (NAND_REG_BASE + 0x000) +#define NAND_STATUS NAND_CMD +#define NAND_CONF (NAND_REG_BASE + 0x004) +#define NAND_ADDR0 (NAND_REG_BASE + 0x008) +#define NAND_ADDR1 (NAND_REG_BASE + 0x00c) +#define NAND_DATA (NAND_REG_BASE + 0x010) +#define NAND_ECC (NAND_REG_BASE + 0x014) +#define NAND_UNK1 (NAND_REG_BASE + 0x018) +#define NAND_UNK2 (NAND_REG_BASE + 0x01c) + + + +/* AES Registers */ + +#define AES_REG_BASE 0xd020000 + +#define AES_CMD (AES_REG_BASE + 0x000) +#define AES_SRC (AES_REG_BASE + 0x004) +#define AES_DEST (AES_REG_BASE + 0x008) +#define AES_KEY (AES_REG_BASE + 0x00c) +#define AES_IV (AES_REG_BASE + 0x010) + + + +/* SHA-1 Registers */ + +#define SHA_REG_BASE 0xd030000 + +#define SHA_CMD (SHA_REG_BASE + 0x000) +#define SHA_SRC (SHA_REG_BASE + 0x004) +#define SHA_H0 (SHA_REG_BASE + 0x008) +#define SHA_H1 (SHA_REG_BASE + 0x00c) +#define SHA_H2 (SHA_REG_BASE + 0x010) +#define SHA_H3 (SHA_REG_BASE + 0x014) +#define SHA_H4 (SHA_REG_BASE + 0x018) + + + +/* SD Host Controller Registers */ + +#define SD_REG_BASE 0xd070000 + +#define SDHC_SDMA_ADDR (0x000) +#define SDHC_BLOCK_SIZE (0x004) +#define SDHC_BLOCK_COUNT (0x006) +#define SDHC_CMD_ARG (0x008) +#define SDHC_CMD_TRANSFER_MODE (0x00c) +#define SDHC_CMD (0x00e) +#define SDHC_RESPONSE (0x010) +#define SDHC_DATA (0x020) +#define SDHC_PRESENT_STATE (0x024) +#define SDHC_HOST_CONTROL (0x028) +#define SDHC_POWER_CONTROL (0x029) +#define SDHC_BLOCK_GAP_CONTROL (0x02a) +#define SDHC_WAKEUP_CONTROL (0x02b) +#define SDHC_CLOCK_CONTROL (0x02c) +#define SDHC_TIMEOUT_CONTROL (0x02e) +#define SDHC_SOFTWARE_RESET (0x02f) +#define SDHC_NORMAL_INTERRUPT_STATUS (0x030) +#define SDHC_ERROR_INTERRUPT_STATUS (0x032) +#define SDHC_NORMAL_INTERRUPT_ENABLE (0x034) +#define SDHC_ERROR_INTERRUPT_ENABLE (0x036) +#define SDHC_NORMAL_INTERRUPT_SIGNAL_ENABLE (0x038) +#define SDHC_ERROR_INTERRUPT_SIGNAL_ENABLE (0x03a) +#define SDHC_AMCD12_ERROR_STATUS (0x03c) +#define SDHC_CAPABILITIES (0x040) +#define SDHC_MAX_CAPABILITIES (0x048) +#define SDHC_FORCE_ERROR_EVENT_ACMD12 (0x050) +#define SDHC_FORCE_ERROR_EVENT_INTERRUPT (0x052) +#define SDHC_ADMA_ERROR_STATUS (0x054) +#define SDHC_ADMA_SYSTEM_ADDR (0x058) +#define SDHC_SLOT_INTERRUPT_STATUS (0x0fc) +#define SDHC_VERSION (0x0fe) + +/* EXI Registers */ + +#define EXI_REG_BASE 0xd806800 +#define EXI0_REG_BASE (EXI_REG_BASE+0x000) +#define EXI1_REG_BASE (EXI_REG_BASE+0x014) +#define EXI2_REG_BASE (EXI_REG_BASE+0x028) + +#define EXI0_CSR (EXI0_REG_BASE+0x000) +#define EXI0_MAR (EXI0_REG_BASE+0x004) +#define EXI0_LENGTH (EXI0_REG_BASE+0x008) +#define EXI0_CR (EXI0_REG_BASE+0x00c) +#define EXI0_DATA (EXI0_REG_BASE+0x010) + +#define EXI1_CSR (EXI1_REG_BASE+0x000) +#define EXI1_MAR (EXI1_REG_BASE+0x004) +#define EXI1_LENGTH (EXI1_REG_BASE+0x008) +#define EXI1_CR (EXI1_REG_BASE+0x00c) +#define EXI1_DATA (EXI1_REG_BASE+0x010) + +#define EXI2_CSR (EXI2_REG_BASE+0x000) +#define EXI2_MAR (EXI2_REG_BASE+0x004) +#define EXI2_LENGTH (EXI2_REG_BASE+0x008) +#define EXI2_CR (EXI2_REG_BASE+0x00c) +#define EXI2_DATA (EXI2_REG_BASE+0x010) + +#define EXI_BOOT_BASE (EXI_REG_BASE+0x040) + + + +/* MEMORY CONTROLLER Registers */ + +#define MEM_REG_BASE 0xd8b4000 +#define MEM_PROT (MEM_REG_BASE+0x20a) +#define MEM_PROT_START (MEM_REG_BASE+0x20c) +#define MEM_PROT_END (MEM_REG_BASE+0x20e) +#define MEM_FLUSHREQ (MEM_REG_BASE+0x228) +#define MEM_FLUSHACK (MEM_REG_BASE+0x22a) + +#endif diff --git a/integer.h b/integer.h new file mode 100644 index 0000000..8c70183 --- /dev/null +++ b/integer.h @@ -0,0 +1,30 @@ +/*-------------------------------------------*/ +/* Integer type definitions for FatFs module */ +/*-------------------------------------------*/ + +#ifndef _INTEGER + +/* These types must be 16-bit, 32-bit or larger integer */ +typedef int INT; +typedef unsigned int UINT; + +/* These types must be 8-bit integer */ +typedef signed char CHAR; +typedef unsigned char UCHAR; +typedef unsigned char BYTE; + +/* These types must be 16-bit integer */ +typedef short SHORT; +typedef unsigned short USHORT; +typedef unsigned short WORD; + +/* These types must be 32-bit integer */ +typedef long LONG; +typedef unsigned long ULONG; +typedef unsigned long DWORD; + +/* Boolean type */ +typedef enum { FALSE = 0, TRUE } BOOL; + +#define _INTEGER +#endif diff --git a/lcd.c b/lcd.c new file mode 100644 index 0000000..3acd79d --- /dev/null +++ b/lcd.c @@ -0,0 +1,208 @@ +#include "types.h" +#include "lcd.h" +#include "start.h" +#include "vsprintf.h" +#include "string.h" +#include "utils.h" +#include "hollywood.h" + +void lcdinit(void); + +char ddram[0x80]; +u8 ddaddr = 0; +u32 chardelay = 0; + +#define WIDTH 16 +#define HEIGHT 2 + +u8 linestarts[HEIGHT] = { 0x00, 0x40 }; + +u8 cx, cy; + +// for 4x20 +//u8 linestarts[HEIGHT] = { 0x00, 0x40, 0x14, 0x54 }; + +#define LCD_HOME_CLEAR 0x01 +#define LCD_HOME 0x02 +#define LCD_ENTRY_MODE 0x04 +# define EM_INC 0x02 +# define EM_DEC 0x00 +# define EM_SHIFT 0x01 +#define LCD_DISPLAY_ONOFF 0x08 +# define D_DISPLAY 0x04 +# define D_CURSOR 0x02 +# define D_BLINK 0x01 +#define LCD_SHIFT 0x10 +# define SH_SHIFT 0x08 +# define SH_CURSOR 0x00 +# define SH_RIGHT 0x04 +# define SH_LEFT 0x00 +#define LCD_FUNCTION_SET 0x20 +# define FS_8BIT 0x10 +# define FS_4BIT 0x00 +# define FS_2LINE 0x08 +# define FS_1LINE 0x00 +# define FS_5X10 0x04 +# define FS_5X8 0x00 +#define LCD_CGRAM 0x40 +#define LCD_DDRAM 0x80 + +#define LCD_PORT HW_GPIO1OUT +#define LCD_MASK 0x00FC0000 +#define LCD_E 0x00040000 +#define LCD_RS 0x00080000 +#define LCD_DSHIFT 20 + +static void _lcdnybble(char rs, char n) +{ + if(rs) + mask32(LCD_PORT, LCD_MASK, ((n&0xF) << LCD_DSHIFT) | LCD_RS); + else + mask32(LCD_PORT, LCD_MASK, ((n&0xF) << LCD_DSHIFT)); + + udelay(4); + set32(HW_GPIO1OUT, LCD_E); + udelay(4); + clear32(HW_GPIO1OUT, LCD_E); + udelay(4); +} + +static void _lcdcmd(char c) +{ + _lcdnybble(0, c>>4); + _lcdnybble(0, c&0x0F); + udelay(50); +} + +static void _lcdwrite(char c) +{ + _lcdnybble(1, c>>4); + _lcdnybble(1, c&0x0F); + ddram[ddaddr] = c; + ddaddr = (ddaddr+1)&0x7F; + udelay(50); +} + +static void _lcdgoto(u8 addr) +{ + _lcdcmd(LCD_DDRAM | addr); + ddaddr = addr; +} + +static void _lcdgotoxy(u8 x, u8 y) +{ + u8 addr = (x + linestarts[y]); + _lcdcmd(LCD_DDRAM | addr); + ddaddr = addr; +} + +static void _lcdclear(void) +{ + int i; + _lcdcmd(LCD_HOME_CLEAR); + udelay(2000); + for(i=0;i<0x80;i++) { + ddram[i] = ' '; + } +} + +static void _lcdscroll(u8 count) +{ + int x,y; + _lcdcmd(LCD_HOME_CLEAR); + udelay(2000); + for(y=0;y<(HEIGHT-count);y++) { + _lcdgotoxy(0,y); + for(x=0;x>4); + udelay(10000); + _lcdnybble(0, (LCD_FUNCTION_SET | FS_8BIT)>>4); + udelay(1000); + _lcdnybble(0, (LCD_FUNCTION_SET | FS_8BIT)>>4); + udelay(1000); + _lcdnybble(0, (LCD_FUNCTION_SET | FS_4BIT)>>4); + udelay(1000); + _lcdcmd(LCD_FUNCTION_SET | FS_4BIT | FS_2LINE | FS_5X8); + _lcdcmd(LCD_DISPLAY_ONOFF); + _lcdcmd(LCD_HOME_CLEAR); + udelay(2000); + _lcdcmd(LCD_ENTRY_MODE | EM_INC); + _lcdcmd(LCD_DISPLAY_ONOFF | D_DISPLAY | D_CURSOR); +} + +int lcd_putchar(int ic) +{ + char c = ic; + // defer newlines to keep last line of LCD full + if(c == '\n') { + cx = 0; + cy++; + if(cy < HEIGHT) + _lcdgotoxy(cx,cy); + } else { + if(cx >= 16) { + cx = 0; + cy++; + if(cy < HEIGHT) + _lcdgotoxy(cx,cy); + } + if(cy >= (2*HEIGHT-1)) { + _lcdclear(); + _lcdgoto(linestarts[HEIGHT-1]); + cy = HEIGHT - 1; + cx = 0; + } else if(cy >= HEIGHT) { + _lcdscroll(cy - HEIGHT + 1); + cy = HEIGHT - 1; + cx = 0; + } + _lcdwrite(c); + cx++; + } + return (u8)c; +} + +void lcd_setdelay(u32 delay) +{ + chardelay = delay; +} + +int lcd_puts(const char *s) +{ + while(*s) { + lcd_putchar(*s++); + udelay(chardelay); + } + return 0; +} + +int lcd_printf( const char *fmt, ...) +{ + va_list args; + char buffer[1024]; + int i; + + va_start(args, fmt); + i = vsprintf(buffer, fmt, args); + va_end(args); + lcd_puts(buffer); + return i; +} diff --git a/lcd.h b/lcd.h new file mode 100644 index 0000000..4303b76 --- /dev/null +++ b/lcd.h @@ -0,0 +1,11 @@ +#ifndef __LCD_H__ +#define __LCD_H__ + +#include "types.h" + +void lcd_init(void); +int lcd_putchar(int c); +int lcd_puts(const char *s); +void lcd_setdelay(u32 delay); +int lcd_printf( const char *fmt, ...); +#endif diff --git a/main.c b/main.c new file mode 100644 index 0000000..6206fae --- /dev/null +++ b/main.c @@ -0,0 +1,168 @@ +#include "types.h" +#include "utils.h" +#include "start.h" +#include "hollywood.h" +#include "sdhc.h" +#include "string.h" +#include "memory.h" +#include "elf.h" +#include "gecko.h" +#include "ff.h" +#include "panic.h" +#include "powerpc_elf.h" + +typedef struct { + u32 hdrsize; + u32 loadersize; + u32 elfsize; + u32 argument; +} ioshdr; + +int dogecko; + +void boot2_loadelf(u8 *elf) { + if(dogecko) + gecko_puts("Loading boot2 ELF...\n"); + + if(memcmp("\x7F" "ELF\x01\x02\x01\x61\x01",elf,9)) { + if(dogecko) + gecko_printf("Invalid ELF header! 0x%02x 0x%02x 0x%02x 0x%02x\n",elf[0], elf[1], elf[2], elf[3]); + panic(0xE3); + } + + Elf32_Ehdr *ehdr = (Elf32_Ehdr*)elf; + if(ehdr->e_phoff == 0) { + if(dogecko) + gecko_printf("ELF has no program headers!\n"); + panic(0xE4); + } + int count = ehdr->e_phnum; + Elf32_Phdr *phdr = (Elf32_Phdr*)(elf + ehdr->e_phoff); + if(dogecko) + gecko_printf("PHDRS at %p\n",phdr); + while(count--) + { + if(phdr->p_type != PT_LOAD) { + if(dogecko) + gecko_printf("Skipping PHDR of type %d\n",phdr->p_type); + } else { + void *src = elf + phdr->p_offset; + if(dogecko) + gecko_printf("LOAD %p -> %p [0x%x]\n",src, phdr->p_paddr, phdr->p_filesz); + memcpy(phdr->p_paddr, src, phdr->p_filesz); + } + phdr++; + } + if(dogecko) + gecko_puts("Done!\n"); +} + +#define PPC_BOOT_FILE "/system/ppcboot.elf" + +FATFS fatfs; + +void turn_stuff_on(void) +{ + clear32(HW_GPIO1OUT, 0x10); + udelay(100); + set32(HW_RESETS, 0x7FFFFCF); +} + +void reset_audio(u8 flag) +{ + + // GPIO2IN is probably mislabeled + if(flag) + clear32(HW_DIFLAGS, 0x180); + else + mask32(HW_DIFLAGS, 0x80, 0x100); + + clear32(HW_GPIO2IN, 0x80000000); + udelay(2); + clear32(HW_GPIO2IN, 0x40000000); + + if(flag) { + clear32(HW_GPIO2IN, 0x10000000); + mask32(HW_GPIO2DIR, 0x7FFFFFF, 0x4B0FFCE); + } else { + mask32(HW_GPIO2DIR, 0x7FFFFFF, 0x4640FC0); + } + udelay(10); + set32(HW_GPIO2IN, 0x40000000); + udelay(500); + set32(HW_GPIO2IN, 0x80000000); + udelay(2); +} + +void regs_setup(void) +{ + u8 hwood_ver, hwood_hi, hwood_lo; + hwood_ver = read32(0xd800214); + hwood_hi = hwood_ver >> 4; //R0 + hwood_lo = hwood_ver & 0xF; //R1 + + *(u32*)0xFFFF897C = *(u32*)0xFFFF86D0; + set32(HW_EXICTRL, EXICTRL_ENABLE_EXI); + mem_protect(1, (void*)0x13420000, (void*)0x1fffffff); + clear32(HW_EXICTRL, 0x10); + if(hwood_hi == 0) + write32(0xd8b0010, 0); + write32(0xd8b0010, 0); + if(hwood_hi == 1 && hwood_lo == 0) + mask32(0xd800140, 0x0000FFF0, 1); + set32(0xd80018C, 0x400); + set32(0xd80018C, 0x800); + + reset_audio(0); + //boot2_sub_FFFF5D08(0); + //boot2_sub_FFFF5C40(hwood_hi); + //boot2_sub_FFFF6AA8(); + + turn_stuff_on(); + // what do these two pokes do? no clue. Not needed but I'm leaving them in anyway. + write32(0xd8001e0, 0x65244A); //? + write32(0xd8001e4, 0x46A024); //? + + clear32(HW_GPIO1OWNER, 0x10); + set32(HW_GPIO1DIR, 0x10); + //write32(HW_ALARM,0); + //write32(HW_ALARM,0); +} + +void *_main(void *base) +{ + FRESULT fres; + int res; + + mem_setswap(1); + + write32(HW_IRQENABLE, 0); + + regs_setup(); + //debug_output(0x50); + //debug_output(0x51); + debug_output(0xF8); + + gecko_init(); + + debug_output(0xF9); + + gecko_puts("MiniIOS v0.1 loading\n"); + + fres = f_mount(0, &fatfs); + + if(fres != FR_OK) { + gecko_printf("Error %d while trying to mount SD\n", fres); + panic2(0, PANIC_MOUNT); + } + + gecko_puts("Trying to boot:" PPC_BOOT_FILE "\n"); + + res = powerpc_load_file(PPC_BOOT_FILE); + if(res < 0) { + gecko_printf("Failed to boot PPC: %d\n", res); + gecko_puts("Continuing anyway\n"); + } + + while(1); +} diff --git a/memory.c b/memory.c new file mode 100644 index 0000000..f76240b --- /dev/null +++ b/memory.c @@ -0,0 +1,218 @@ +#include "types.h" +#include "start.h" +#include "memory.h" +#include "utils.h" +#include "gecko.h" +#include "hollywood.h" + +void _dc_inval_entries(void *start, int count); +void _dc_flush_entries(void *start, int count); +void _dc_flush(void); +void _ic_inval(void); +void _drain_write_buffer(void); + +#define LINESIZE 0x20 +#define CACHESIZE 0x4000 + +// TODO: move to hollywood.h once we figure out WTF +#define HW_100 (HW_REG_BASE + 0x100) +#define HW_104 (HW_REG_BASE + 0x104) +#define HW_108 (HW_REG_BASE + 0x108) +#define HW_10c (HW_REG_BASE + 0x10c) +#define HW_110 (HW_REG_BASE + 0x110) +#define HW_114 (HW_REG_BASE + 0x114) +#define HW_118 (HW_REG_BASE + 0x118) +#define HW_11c (HW_REG_BASE + 0x11c) +#define HW_120 (HW_REG_BASE + 0x120) +#define HW_124 (HW_REG_BASE + 0x124) +#define HW_130 (HW_REG_BASE + 0x130) +#define HW_134 (HW_REG_BASE + 0x134) +#define HW_138 (HW_REG_BASE + 0x138) +#define HW_188 (HW_REG_BASE + 0x188) +#define HW_18C (HW_REG_BASE + 0x18c) +#define HW_214 (HW_REG_BASE + 0x214) + +// what is this thing doing anyway? +// and why only on reads? +u32 _mc_read32(u32 addr) +{ + u32 data; + u32 tmp130 = 0; + // this seems to be a bug workaround + if(!(read32(HW_214) & 0xF0)) + { + tmp130 = read32(HW_130); + write32(HW_130, tmp130 | 0x400); + // Dummy reads? + read32(HW_138); + read32(HW_138); + read32(HW_138); + read32(HW_138); + } + data = read32(addr); + read32(HW_214); //??? + + if(!(read32(HW_214) & 0xF0)) + write32(HW_130, tmp130); + + return data; +} + +void _magic_bullshit(int type) { + u32 mask = 10; + switch(type) { + case 0: mask = 0x8000; break; + case 1: mask = 0x4000; break; + case 2: mask = 0x0001; break; + case 3: mask = 0x0002; break; + case 4: mask = 0x0004; break; + case 5: mask = 0x0008; break; + case 6: mask = 0x0010; break; + case 7: mask = 0x0020; break; + case 8: mask = 0x0040; break; + case 9: mask = 0x0080; break; + case 10: mask = 0x0100; break; + case 11: mask = 0x1000; break; + case 12: mask = 0x0000; break; + } + //NOTE: 0xd8b000x, not 0xd8b400x! + u32 val = _mc_read32(0xd8b0008); + if(val & mask) { + if((type >= 2) && (type <= 10)) { + while((read32(HW_18C) & 0xF) == 9) + set32(HW_188, 0x10000); + clear32(HW_188, 0x10000); + set32(HW_188, 0x2000000); + mask32(HW_124, 0x7c0, 0x280); + set32(HW_134, 0x400); + while((read32(HW_18C) & 0xF) != 9); + set32(HW_100, 0x400); + set32(HW_104, 0x400); + set32(HW_108, 0x400); + set32(HW_10c, 0x400); + set32(HW_110, 0x400); + set32(HW_114, 0x400); + set32(HW_118, 0x400); + set32(HW_11c, 0x400); + set32(HW_120, 0x400); + write32(0xd8b0008, _mc_read32(0xd8b0008) & (~mask)); + write32(0xd8b0008, _mc_read32(0xd8b0008) | mask); + clear32(HW_134, 0x400); + clear32(HW_100, 0x400); + clear32(HW_104, 0x400); + clear32(HW_108, 0x400); + clear32(HW_10c, 0x400); + clear32(HW_110, 0x400); + clear32(HW_114, 0x400); + clear32(HW_118, 0x400); + clear32(HW_11c, 0x400); + clear32(HW_120, 0x400); + clear32(HW_188, 0x2000000); + mask32(HW_124, 0x7c0, 0xc0); + } else { + if((type == 11) || (type == 0) || (type == 1)) { + write32(0xd8b0008, val & (~mask)); + // wtfux + write32(0xd8b0008, val | mask); + write32(0xd8b0008, val | mask); + write32(0xd8b0008, val | mask); + } + } + } +} + +void magic_bullshit(int type) +{ + _magic_bullshit(type); + if(type != 0) + _magic_bullshit(0); +} + +void ahb_memflush(enum AHBDEV dev) +{ + u16 req = 0; + u16 ack; + int i; + + switch(dev) + { + case MEMORY: + req = 1; + break; + default: + if((dev >= RAW0) && (dev <= RAWF)) + { + req = dev - RAW0; + } else { + gecko_printf("ahb_memflush(0x%x): Invalid device\n", dev); + return; + } + break; + } + + write32(MEM_FLUSHREQ, req); + + for(i=0;i<1000000;i++) { + ack = read16(MEM_FLUSHACK); + _magic_bullshit(0); + if(ack == req) + break; + } + write32(MEM_FLUSHREQ, 0); + if(i>=1000000) { + gecko_printf("ahb_memflush(%d): Flush (0x%x) did not ack!\n", dev, req); + } +} + +void dc_flushrange(void *start, u32 size) +{ + if(size > 0x4000) { + _dc_flush(); + } else { + void *end = ALIGN_FORWARD(((u8*)start) + size, LINESIZE); + start = ALIGN_BACKWARD(start, LINESIZE); + _dc_flush_entries(start, (end - start) / LINESIZE); + } + _drain_write_buffer(); + //ahb_memflush(MEMORY); +} + +void dc_invalidaterange(void *start, u32 size) +{ + void *end = ALIGN_FORWARD(((u8*)start) + size, LINESIZE); + start = ALIGN_BACKWARD(start, LINESIZE); + _dc_inval_entries(start, (end - start) / LINESIZE); + //_magic_bullshit(0); +} + +void dc_flushall(void) +{ + _dc_flush(); + _drain_write_buffer(); + //ahb_memflush(MEMORY); +} + +void ic_invalidateall(void) +{ + _ic_inval(); + //_magic_bullshit(0); +} + +void mem_protect(int enable, void *start, void *end) +{ + write16(MEM_PROT, enable?1:0); + write16(MEM_PROT_START, (((u32)start) & 0xFFFFFFF) >> 12); + write16(MEM_PROT_END, (((u32)end) & 0xFFFFFFF) >> 12); + udelay(10); +} + +void mem_setswap(int enable) +{ + u32 d = read32(HW_MEMMIRR); + + if((d & 0x20) && !enable) + write32(HW_MEMMIRR, d & ~0x20); + if((!(d & 0x20)) && enable) + write32(HW_MEMMIRR, d | 0x20); + +} diff --git a/memory.h b/memory.h new file mode 100644 index 0000000..f4db029 --- /dev/null +++ b/memory.h @@ -0,0 +1,27 @@ +#ifndef __MEMORY_H__ +#define __MEMORY_H__ + +#include "types.h" + +#define ALIGN_FORWARD(x,align) \ + ((typeof(x))((((u32)(x)) + (align) - 1) & (~(align-1)))) + +#define ALIGN_BACKWARD(x,align) \ + ((typeof(x))(((u32)(x)) & (~(align-1)))) + +enum AHBDEV { + MEMORY = 0, + RAW0 = 0x100, + RAWF = 0x10F, +}; + +void dc_flushrange(void *start, u32 size); +void dc_invalidaterange(void *start, u32 size); +void dc_flushall(void); +void ic_invalidateall(void); +void magic_bullshit(int type); +void ahb_memflush(enum AHBDEV dev); +void mem_protect(int enable, void *start, void *end); +void mem_setswap(int enable); + +#endif diff --git a/memory_asm.S b/memory_asm.S new file mode 100644 index 0000000..f1da0d8 --- /dev/null +++ b/memory_asm.S @@ -0,0 +1,39 @@ +.arm + +.globl _dc_inval_entries +.globl _dc_flush_entries +.globl _dc_flush +.globl _ic_inval +.globl _drain_write_buffer + +.text + +_dc_inval_entries: + mcr p15, 0, r0, c7, c6, 1 + add r0, #0x20 + subs r1, #1 + bgt _dc_inval_entries + bx lr + +_dc_flush_entries: + mcr p15, 0, r0, c7, c10, 1 + add r0, #0x20 + subs r1, #1 + bgt _dc_flush_entries + bx lr + +_dc_flush: + mrc p15, 0, pc, c7, c10, 3 + bne _dc_flush + bx lr + +_ic_inval: + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 + bx lr + +_drain_write_buffer: + mov r0, #0 + mcr p15, 0, r0, c7, c10, 4 + bx lr + diff --git a/panic.c b/panic.c new file mode 100644 index 0000000..26e6918 --- /dev/null +++ b/panic.c @@ -0,0 +1,39 @@ +#include "types.h" +#include "utils.h" +#include "start.h" +#include "hollywood.h" +#include + +#define PANIC_ON 200000 +#define PANIC_OFF 300000 +#define PANIC_INTER 1000000 + +// figure out a use for mode... + +void panic2(int mode, ...) +{ + int arg; + va_list ap; + + clear32(HW_GPIO1OUT, HW_GPIO1_SLOT); + clear32(HW_GPIO1DIR, HW_GPIO1_SLOT); + clear32(HW_GPIO1OWNER, HW_GPIO1_SLOT); + + while(1) { + va_start(ap, mode); + + while(1) { + arg = va_arg(ap, int); + if(arg < 0) + break; + set32(HW_GPIO1OUT, HW_GPIO1_SLOT); + udelay(arg * PANIC_ON); + clear32(HW_GPIO1OUT, HW_GPIO1_SLOT); + udelay(PANIC_OFF); + } + + va_end(ap); + + udelay(PANIC_INTER); + } +} diff --git a/panic.h b/panic.h new file mode 100644 index 0000000..79d4bf7 --- /dev/null +++ b/panic.h @@ -0,0 +1,8 @@ +#ifndef __PANIC_H__ +#define __PANIC_H__ + +#define PANIC_MOUNT 1,1,-1 + +void panic2(int mode, ...) __attribute__ ((noreturn)); + +#endif diff --git a/powerpc.c b/powerpc.c new file mode 100644 index 0000000..36e95b2 --- /dev/null +++ b/powerpc.c @@ -0,0 +1,58 @@ +#include "types.h" +#include "powerpc.h" +#include "hollywood.h" +#include "utils.h" +#include "start.h" +#include "gecko.h" + + +const u32 stub_default[0x10] = { + 0x3c600000, + 0x60633400, + 0x7c7a03a6, + 0x38600000, + 0x7c7b03a6, + 0x4c000064, + 0, +}; + +void powerpc_upload_stub(const u32 *stub, u32 len) +{ + u32 i; + + set32(HW_EXICTRL, EXICTRL_ENABLE_EXI); + + if(stub == NULL || len == 0) + { + stub = stub_default; + len = sizeof(stub_default) / sizeof(u32); + } + + for(i = 0; i < len; i++) + write32(EXI_BOOT_BASE + 4*i, stub[i]); + + set32(HW_DIFLAGS, DIFLAGS_BOOT_CODE); + + gecko_printf("disabling EXI now...\n"); + clear32(HW_EXICTRL, EXICTRL_ENABLE_EXI); +} + +void powerpc_hang() +{ + clear32(HW_RESETS, 0x30); + udelay(100); + set32(HW_RESETS, 0x20); + udelay(100); +} + +void powerpc_reset() +{ + write32(0xD800034, 0x40000000); + clear32(HW_RESETS, 0x30); + udelay(100); + set32(HW_RESETS, 0x20); + udelay(100); + set32(HW_RESETS, 0x10); + udelay(100000); + set32(HW_EXICTRL, EXICTRL_ENABLE_EXI); +} diff --git a/powerpc.h b/powerpc.h new file mode 100644 index 0000000..a3369ab --- /dev/null +++ b/powerpc.h @@ -0,0 +1,9 @@ +#ifndef __POWERPC_H__ +#define __POWERPC_H__ 1 + +void ppc_boot_code(); +void powerpc_upload_stub(const u32 *stub, u32 len); +void powerpc_hang(); +void powerpc_reset(); + +#endif diff --git a/powerpc_elf.c b/powerpc_elf.c new file mode 100644 index 0000000..237a30b --- /dev/null +++ b/powerpc_elf.c @@ -0,0 +1,89 @@ +#include "types.h" +#include "powerpc.h" +#include "hollywood.h" +#include "utils.h" +#include "start.h" +#include "gecko.h" +#include "ff.h" +#include "powerpc_elf.h" +#include "elf.h" +#include "string.h" + +#define PHDR_MAX 10 + +Elf32_Ehdr elfhdr; +Elf32_Phdr phdrs[PHDR_MAX]; + +int powerpc_load_file(const char *path) +{ + FIL fd; + u32 read; + FRESULT fres; + + fres = f_open(&fd, path, FA_READ); + if(fres != FR_OK) + return -fres; + + fres = f_read(&fd, &elfhdr, sizeof(elfhdr), &read); + + if(fres != FR_OK) + return -fres; + if(read != sizeof(elfhdr)) + return -100; + + if(memcmp("\x7F" "ELF\x01\x02\x01\x00\x00",elfhdr.e_ident,9)) { + gecko_printf("Invalid ELF header! 0x%02x 0x%02x 0x%02x 0x%02x\n",elfhdr.e_ident[0], elfhdr.e_ident[1], elfhdr.e_ident[2], elfhdr.e_ident[3]); + return -101; + } + + if(elfhdr.e_phoff == 0 || elfhdr.e_phnum == 0) { + gecko_puts("ELF has no program headers!\n"); + return -102; + } + if(elfhdr.e_phnum > PHDR_MAX) { + gecko_printf("ELF has too many (%d) program headers!\n", elfhdr.e_phnum); + return -102; + } + + fres = f_lseek(&fd, elfhdr.e_phoff); + if(fres != FR_OK) + return -fres; + + fres = f_read(&fd, phdrs, sizeof(phdrs[0])*elfhdr.e_phnum, &read); + if(fres != FR_OK) + return -fres; + if(read != sizeof(phdrs[0])*elfhdr.e_phnum) + return -103; + + int count = elfhdr.e_phnum; + Elf32_Phdr *phdr = phdrs; + + powerpc_hang(); + + while(count--) + { + if(phdr->p_type != PT_LOAD) { + gecko_printf("Skipping PHDR of type %d\n",phdr->p_type); + } else { + void *dst = phdr->p_paddr; + dst = (void*)((u32)dst & ~0xC0000000); + + gecko_printf("LOAD 0x%x -> %p [0x%x]\n", phdr->p_offset, phdr->p_paddr, phdr->p_filesz); + fres = f_lseek(&fd, phdr->p_offset); + if(fres != FR_OK) + return -fres; + fres = f_read(&fd, dst, phdr->p_filesz, &read); + if(fres != FR_OK) + return -fres; + if(read != phdr->p_filesz) + return -104; + } + phdr++; + } + gecko_puts("ELF load done, booting PPC...\n"); + powerpc_upload_stub(NULL,0); + powerpc_reset(); + gecko_puts("PPC booted!\n"); + + return 0; +} diff --git a/powerpc_elf.h b/powerpc_elf.h new file mode 100644 index 0000000..656938f --- /dev/null +++ b/powerpc_elf.h @@ -0,0 +1,6 @@ +#ifndef __POWERPC_H__ +#define __POWERPC_H__ 1 + +int powerpc_load_file(const char *path); + +#endif diff --git a/sdhc.c b/sdhc.c new file mode 100644 index 0000000..09eb298 --- /dev/null +++ b/sdhc.c @@ -0,0 +1,1116 @@ +/* parts based on: + * * "SD Host Controller driver based on the SD Host Controller Standard" copyright (c) 2006 Uwe Stuehler + * * Simplified SD Host Controller Standard + */ + +#include "hollywood.h" +#include "sdhc.h" +#include "utils.h" +#include "string.h" +#include "start.h" +#include "memory.h" + +#define _READONLY 1 + +#define SDHC_PRINT_ERROR 1 +//#define SDHC_DEBUG 1 +//#define SDHC_DEBUG_V 1 + +#if defined(SDHC_DEBUG_V) && !defined(SDHC_DEBUG) +# define SDHC_DEBUG 1 +#endif + +#ifdef SDHC_DEBUG +# include "gecko.h" +# define sdhc_debug(reg, f, arg...) do { gecko_printf("sdhc%d: " f "\n", ((reg - SD_REG_BASE) / 0x100), ##arg); } while(0) +#else +# define sdhc_debug(reg, f, arg...) +#endif + +#ifdef SDHC_PRINT_ERROR +# include "gecko.h" +# define sdhc_error(reg, f, arg...) do { gecko_printf("sdhc%d: " f "\n", ((reg - SD_REG_BASE) / 0x100), ##arg); } while(0) +#else +# define sdhc_error(reg, f, arg...) +#endif + +#define SDHC_SOFTWARE_RESET_DAT (1 << 2) +#define SDHC_SOFTWARE_RESET_CMD (1 << 1) +#define SDHC_SOFTWARE_RESET_ALL (7) + +#define SDHC_TIMEOUT_MAX (0x0e) + +#define SDHC_BFREQ_KHZ(c) ((((c) >> 8) & 0x3f) * 1000) + +#define SDHC_CAP_VOLTAGE_33 (1 << 24) +#define SDHC_CAP_VOLTAGE_30 (1 << 25) +#define SDHC_CAP_VOLTAGE_18 (1 << 26) + +#define SDHC_CAP_SDMA (1 << 22) + +#define SDHC_PCTRL_VOLTAGE_SHIFT 1 +#define SDHC_PCTRL_VOLTAGE_33 (0x07 << SDHC_PCTRL_VOLTAGE_SHIFT) +#define SDHC_PCTRL_VOLTAGE_30 (0x06 << SDHC_PCTRL_VOLTAGE_SHIFT) +#define SDHC_PCTRL_VOLTAGE_18 (0x05 << SDHC_PCTRL_VOLTAGE_SHIFT) +#define SDHC_PCTRL_ENABLE 1 + +#define SDHC_CLOCK_INTERNAL_ENABLE (1 << 0) +#define SDHC_CLOCK_INTERNAL_STABLE (1 << 1) +#define SDHC_CLOCK_SD_ENABLE (1 << 2) + +#define SDHC_CARD_INSERTED (1 << 16) +#define SDHC_WRITE_PROTECT (1 << 19) + +#define SDHC_BLOCKS_MAX 65535 + +#define SDHC_CMDMODE_MULTIBLOCK (1 << 5) +#define SDHC_CMDMODE_READ (1 << 4) +#define SDHC_CMDMODE_WRITE (0 << 4) +#define SDHC_CMDMODE_ACMD12_ENABLE (1 << 2) +#define SDHC_CMDMODE_BLOCKCNT_ENABLE (1 << 1) +#define SDHC_CMDMODE_DMA_ENABLE (1 << 0) + +#define SDHC_CMD_DATA (1 << 5) +#define SDHC_CMD_IDXCHECK (1 << 4) +#define SDHC_CMD_CRC (1 << 3) + +#define SDHC_CMDTEST_READ 0x40 + +#define SDHC_CMD_MASK 0xff +#define SDHC_CMD_SHIFT 8 + +#define SDHC_CMD_NORESP 0 +#define SDHC_CMD_RESP_136 1 +#define SDHC_CMD_RESP_48 2 +#define SDHC_CMD_RESP_BUSY 3 + +#define SDHC_PRESENT_CMD_INHIBIT_CMD 1 +#define SDHC_PRESENT_CMD_INHIBIT_DAT 2 +#define SDHC_PRESENT_CMD_INHIBIT_BOTH 3 + +#define SDHC_BFR_READ_ENABLE (1 << 11) +#define SDHC_BFR_WRITE_ENABLE (1 << 10) + +#define SDHC_WAIT_TIMEOUT 10000 +#define SDHC_WAIT_TIMEOUT_MULTIPLY 50 +#define SDHC_WAIT_TIMEOUT_OUTER 50000 +#define SDHC_WAIT_TIMEOUT_OUTER_MULTIPLY 5 + + +#define SDHC_INTERRUPT_DMA (1 << 3) +#define SDHC_INTERRUPT_TRANSF_COMPLETE (1 << 1) + +#define SD_RSP_BUSY 0x0100 +#define SD_RSP_136 0x0200 +#define SD_RSP_CRC 0x0400 +#define SD_RSP_IDX 0x0800 +#define SD_RSP_PRESENT 0x1000 + +#define SD_R0 0 +#define SD_R1 (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX) +#define SD_R1B (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX | SD_RSP_BUSY) +#define SD_R2 (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_136) +#define SD_R3 SD_RSP_PRESENT +#define SD_R4 SD_RSP_PRESENT +#define SD_R5 (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX) +#define SD_R5B (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX | SD_RSP_BUSY) +#define SD_R6 (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX) +#define SD_R7 (SD_RSP_PRESENT | SD_RSP_CRC | SD_RSP_IDX) + +#define SD_READ 0x10000 + +#define SD_CMD_ACMD 0xC0 + +#define SD_CMD_RESET_CARD 0 +#define SD_CMD_ALL_SEND_CID 2 +#define SD_CMD_SEND_RELATIVE_ADDR 3 +#define SD_CMD_SELECT_CARD 7 +#define SD_CMD_SEND_IF_COND 8 +#define SD_CMD_SEND_STATUS 13 +#define SD_CMD_SET_BLOCKLEN 16 +#define SD_CMD_READ_MULTIPLE_BLOCK 18 +#define SD_CMD_WRITE_MULTIPLE_BLOCK 25 +#define SD_CMD_APP 55 +#define SD_CMD_READ_OCR 58 +#define SD_CMD_APP_SET_BUS_WIDTH (SD_CMD_ACMD + 6) +#define SD_CMD_APP_SEND_OP_COND (SD_CMD_ACMD + 41) + +#define SDHC_HCR_BUSWIDTH_4 2 + +#define BLOCK_SIZE 512 +#define BLOCK_SIZE_512K 7 /* SDMA block size */ + +#define SDMA_BLOCK_SIZE (512 * 1024) + +#define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */ +#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */ +#define MMC_VDD_29_30 0x00020000 /* VDD voltage 2.9 ~ 3.0 */ +#define MMC_VDD_30_31 0x00040000 /* VDD voltage 3.0 ~ 3.1 */ +#define MMC_VDD_165_195 0x00000080 + +#define OCR_POWERUP_STATUS (1 << 31) +#define OCR_HCS (1 << 30) +#define OCR_CCS OCR_HCS + +#define INTERRUPT_ERROR (1 << 15) +#define INTERRUPT_CARD (1 << 8) +#define INTERRUPT_CARD_REMOVAL (1 << 7) +#define INTERRUPT_CARD_INSERTION (1 << 6) +#define INTERRUPT_BUFFER_READ_READY (1 << 5) +#define INTERRUPT_BUFFER_WRITE_READY (1 << 4) +#define INTERRUPT_DMA (1 << 3) +#define INTERRUPT_BLOCK_GAP_EVENT (1 << 2) +#define INTERRUPT_TRANSFER_COMPLETE (1 << 1) +#define INTERRUPT_COMMAND_COMPLETE (1 << 0) + +#define INTERRUPT_ALL 0x81ff + +#define EINTERRUPT_ADMA (1 << 9) +#define EINTERRUPT_ACMD12 (1 << 8) +#define EINTERRUPT_CURRENT_LIMIT (1 << 7) +#define EINTERRUPT_DATA_END_BIT (1 << 6) +#define EINTERRUPT_DATA_CRC (1 << 5) +#define EINTERRUPT_DATA_TIMEOUT (1 << 4) +#define EINTERRUPT_CMD_INDEX (1 << 3) +#define EINTERRUPT_CMD_END_BIT (1 << 2) +#define EINTERRUPT_CMD_CRC (1 << 1) +#define EINTERRUPT_CMD_TIMEOUT (1 << 0) + +#define EINTERRUPT_ALL 0x3ff + +u8 __sd_read8(u32 addr) +{ + u32 mask; + u8 shift; + + shift = (addr & 3) * 8; + mask = (0xFF << shift); + + return (read32(addr & ~3) & mask) >> shift; +} + +u16 __sd_read16(u32 addr) +{ + if(addr & 3) + return (read32(addr & ~3) & 0xffff0000) >> 16; + else + return (read32(addr) & 0xffff); +} + +inline u32 __sd_read32(u32 addr) +{ + return read32(addr); +} + +void __sd_write8(u32 addr, u8 data) +{ + u32 mask; + u8 shift; + + shift = (addr & 3) * 8; + mask = (0xFF << shift); + + mask32(addr & ~3, mask, data << shift); +} + +void __sd_write16(u32 addr, u16 data) +{ + if(addr & 3) + mask32(addr & ~3, 0xffff0000, data << 16); + else + mask32(addr, 0xffff, ((u32)data)); +} + +inline void __sd_write32(u32 addr, u32 data) +{ + write32(addr, data); +} + + +#if 0 +static int __sd_wait8(u32 addr, u8 mask) +{ + u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY; + do + { + if(__sd_read8(addr) & mask) + return 0; + udelay(SDHC_WAIT_TIMEOUT); + } + while(timeout--); + return SDHC_ETIMEDOUT; +} +#endif + +static int __sd_wait16(u32 addr, u16 mask) +{ + u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY; + do + { + if(__sd_read16(addr) & mask) + return 0; + udelay(SDHC_WAIT_TIMEOUT); + } + while(timeout--); + return SDHC_ETIMEDOUT; +} + +#if 0 +static int __sd_wait32(u32 addr, u32 mask) +{ + u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY; + do + { + if(__sd_read32(addr) & mask) + return 0; + udelay(SDHC_WAIT_TIMEOUT); + } + while(timeout--); + return SDHC_ETIMEDOUT; +} +#endif + +static int __sd_wait8_r(u32 addr, u8 mask) +{ + u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY; + do + { + if(!(__sd_read8(addr) & mask)) + return 0; + udelay(SDHC_WAIT_TIMEOUT); + } + while(timeout--); + return SDHC_ETIMEDOUT; +} + +#if 0 +static int __sd_wait16_r(u32 addr, u16 mask) +{ + u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY; + do + { + if(!(__sd_read16(addr) & mask)) + return 0; + udelay(SDHC_WAIT_TIMEOUT); + } + while(timeout--); + return SDHC_ETIMEDOUT; +} +#endif + +static int __sd_wait32_r(u32 addr, u32 mask) +{ + u8 timeout = SDHC_WAIT_TIMEOUT_MULTIPLY; + do + { + if(!(__sd_read32(addr) & mask)) + return 0; + udelay(SDHC_WAIT_TIMEOUT); + } + while(timeout--); + return SDHC_ETIMEDOUT; +} + +#ifdef SDHC_DEBUG_V +static void __sd_dumpregs(sdhci_t *sdhci) +{ + sdhc_debug(sdhci->reg_base, " register dump:"); + sdhc_debug(sdhci->reg_base, " sys addr: 0x%08x", __sd_read32(sdhci->reg_base + SDHC_SDMA_ADDR)); + sdhc_debug(sdhci->reg_base, " version: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_VERSION)); + sdhc_debug(sdhci->reg_base, " bsize: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_BLOCK_SIZE)); + sdhc_debug(sdhci->reg_base, " bcount: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_BLOCK_COUNT)); + sdhc_debug(sdhci->reg_base, " argument: 0x%08x", __sd_read32(sdhci->reg_base + SDHC_CMD_ARG)); + sdhc_debug(sdhci->reg_base, " trans mode: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_CMD_TRANSFER_MODE)); + sdhc_debug(sdhci->reg_base, " pres state: 0x%08x", __sd_read32(sdhci->reg_base + SDHC_PRESENT_STATE)); + sdhc_debug(sdhci->reg_base, " hc: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_HOST_CONTROL)); + sdhc_debug(sdhci->reg_base, " pwr ctrl: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_POWER_CONTROL)); + sdhc_debug(sdhci->reg_base, " gap ctrl: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_BLOCK_GAP_CONTROL)); + sdhc_debug(sdhci->reg_base, " wup ctrl: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_WAKEUP_CONTROL)); + sdhc_debug(sdhci->reg_base, " clk ctrl: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_CLOCK_CONTROL)); + sdhc_debug(sdhci->reg_base, " to ctrl: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_TIMEOUT_CONTROL)); + sdhc_debug(sdhci->reg_base, " int status: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS)); + sdhc_debug(sdhci->reg_base, " int enable: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE)); + sdhc_debug(sdhci->reg_base, " eint status: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_STATUS)); + sdhc_debug(sdhci->reg_base, " eint enable: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_ENABLE)); + sdhc_debug(sdhci->reg_base, " caps: 0x%08x", __sd_read32(sdhci->reg_base + SDHC_CAPABILITIES)); + sdhc_debug(sdhci->reg_base, " max caps: 0x%08x", __sd_read32(sdhci->reg_base + SDHC_MAX_CAPABILITIES)); + sdhc_debug(sdhci->reg_base, " soft reset: 0x%08x", __sd_read8(sdhci->reg_base + SDHC_SOFTWARE_RESET)); + sdhc_debug(sdhci->reg_base, " command: 0x%08x", __sd_read16(sdhci->reg_base + SDHC_CMD)); +} +#else +#define __sd_dumpregs(s) while(0) { } +#endif + +static int __sd_reset(sdhci_t *sdhci, int all) +{ + u32 caps; + int retval; + u32 mask; + + sdhci->is_sdhc = 0; + sdhci->is_selected = 0; + sdhci->is_mounted = 0; + + __sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE, 0); + __sd_write16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_ENABLE, 0); + __sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_SIGNAL_ENABLE, 0); + __sd_write16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_SIGNAL_ENABLE, 0); + + if(all) + mask = SDHC_SOFTWARE_RESET_ALL; + else + mask = SDHC_SOFTWARE_RESET_CMD | SDHC_SOFTWARE_RESET_DAT; + + sdhc_error(sdhci->reg_base, "resetting card (mask = %x)", mask); + sdhc_debug(sdhci->reg_base, "software reset register: %02x", __sd_read8(sdhci->reg_base + SDHC_SOFTWARE_RESET)); + __sd_write8(sdhci->reg_base + SDHC_SOFTWARE_RESET, mask); + sdhc_debug(sdhci->reg_base, "software reset register: %02x", __sd_read8(sdhci->reg_base + SDHC_SOFTWARE_RESET)); + sdhc_debug(sdhci->reg_base, "waiting for reset bit to be unset"); + + retval = __sd_wait8_r(sdhci->reg_base + SDHC_SOFTWARE_RESET, mask); + if(retval < 0) + { + sdhc_debug(sdhci->reg_base, "reset failed, bits were never unset"); + sdhc_debug(sdhci->reg_base, "software reset register: %02x", __sd_read8(sdhci->reg_base + SDHC_SOFTWARE_RESET)); + return retval; + } + + sdhc_debug(sdhci->reg_base, "reset done"); + + __sd_dumpregs(sdhci); + __sd_write8(sdhci->reg_base + SDHC_TIMEOUT_CONTROL, SDHC_TIMEOUT_MAX); + + caps = __sd_read32(sdhci->reg_base + SDHC_CAPABILITIES); + sdhc_debug(sdhci->reg_base, "capabilites: %X", caps); + + return 0; +} + +static int __sd_clock_div(u32 base, u32 target) +{ + int d; + for(d = 1; d <= 256; d *= 2) + { + if((base / d) <= target) + return (d/2); + } + return 256 / 2; +} + +static int __sd_clock(sdhci_t *sdhci, u8 enable, u32 freq) +{ + u32 caps; + int d, retval; + + __sd_write16(sdhci->reg_base + SDHC_CLOCK_CONTROL, 0); + + if(!enable) + return 0; + + caps = read32(sdhci->reg_base + SDHC_CAPABILITIES); + sdhc_debug(sdhci->reg_base, "capabilites: %X", caps); + + if(SDHC_BFREQ_KHZ(caps) != 0) + d = __sd_clock_div(SDHC_BFREQ_KHZ(caps), freq); + else + d = 256 / 2; + + sdhc_debug(sdhci->reg_base, "using a clock divisor of %d to archieve a speed of %d kHz (base = %d)", 2*d, SDHC_BFREQ_KHZ(caps) / (2 * d), SDHC_BFREQ_KHZ(caps)); + sdhc_debug(sdhci->reg_base, " -> clock_control = %X", (d << 8) | SDHC_CLOCK_INTERNAL_ENABLE); + + __sd_write16(sdhci->reg_base + SDHC_CLOCK_CONTROL, (d << 8) | SDHC_CLOCK_INTERNAL_ENABLE); + + __sd_dumpregs(sdhci); + sdhc_debug(sdhci->reg_base, "waiting for internal clock to become stable"); + + retval = __sd_wait16(sdhci->reg_base + SDHC_CLOCK_CONTROL, SDHC_CLOCK_INTERNAL_STABLE); + if(retval < 0) + { + sdhc_debug(sdhci->reg_base, "clock didn't become stable :/"); + __sd_dumpregs(sdhci); + return retval; + } + sdhc_debug(sdhci->reg_base, "clock is stable; enabling sd clock"); + + __sd_write16(sdhci->reg_base + SDHC_CLOCK_CONTROL, (d << 8) | SDHC_CLOCK_SD_ENABLE | SDHC_CLOCK_INTERNAL_ENABLE); + return 0; +} + +static int __sd_power(sdhci_t *sdhci, int vdd) +{ + u32 caps; + u8 pwr; + + caps = __sd_read32(sdhci->reg_base + SDHC_CAPABILITIES); + + if(vdd == -1) + { + if(caps & SDHC_CAP_VOLTAGE_33) + vdd = SDHC_CAP_VOLTAGE_33; + else if(caps & SDHC_CAP_VOLTAGE_30) + vdd = SDHC_CAP_VOLTAGE_30; + else if(caps & SDHC_CAP_VOLTAGE_18) + vdd = SDHC_CAP_VOLTAGE_18; + else + { + sdhc_error(sdhci->reg_base, "no voltage supported by the host? this should never happen..."); + return SDHC_ESTRANGE; + } + } + + if(!(caps & vdd)) + { + sdhc_debug(sdhci->reg_base, "voltage %x not supported by the hc"); + return SDHC_EINVAL; + } + + + pwr = 0; + switch(vdd) + { + case SDHC_CAP_VOLTAGE_33: + pwr |= SDHC_PCTRL_VOLTAGE_33; + break; + case SDHC_CAP_VOLTAGE_30: + pwr |= SDHC_PCTRL_VOLTAGE_30; + break; + case SDHC_CAP_VOLTAGE_18: + pwr |= SDHC_PCTRL_VOLTAGE_18; + break; + default: + sdhc_debug(sdhci->reg_base, "invalid vdd: %x", vdd); + return SDHC_EINVAL; + } + + sdhc_debug(sdhci->reg_base, "writing %02x to power control", pwr); + __sd_write8(sdhci->reg_base + SDHC_POWER_CONTROL, pwr); + pwr |= SDHC_PCTRL_ENABLE; + sdhc_debug(sdhci->reg_base, "writing %02x to power control", pwr); + __sd_write8(sdhci->reg_base + SDHC_POWER_CONTROL, pwr); + + __sd_dumpregs(sdhci); + sdhc_debug(sdhci->reg_base, "card should get voltage now"); + + if(!(__sd_read8(sdhci->reg_base + SDHC_POWER_CONTROL) & SDHC_PCTRL_ENABLE)) + { + sdhc_error(sdhci->reg_base, "pctrl = 0 again"); + return SDHC_ESTRANGE; + } + + return 0; + +} + +static s32 __sd_cmd(sdhci_t *sdhci, u32 cmd, u32 type, u32 arg, u32 blk_cnt, void *buffer, u32 *response, u8 rlen) +{ + int mode; + int command; + int mask; + int use_dma; + int retval; + int i, imax; + u32 caps; +#if 0 /* only needed for PIO which is currently broken */ + u8 *ptr; + int len; +#endif + + if(cmd & SD_CMD_ACMD) + { + sdhc_debug(sdhci->reg_base, " cmd %X is ACMD%d, sending CMD55 first", cmd, cmd - SD_CMD_ACMD); + retval = __sd_cmd(sdhci, SD_CMD_APP, SD_R1, sdhci->rca << 16, 0, NULL, NULL, 0); + sdhc_debug(sdhci->reg_base, " CMD55: retval = %d", retval); + // TODO: also check the response here? + if(retval < 0) + return retval; + cmd -= SD_CMD_ACMD; + } + + __sd_write16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_STATUS, 0); + __sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, 0); + + sdhc_debug(sdhci->reg_base, "__sd_cmd: cmd = %X, type = %X, arg = %X, blk_cnt = %d, rlen = %d", cmd, type, arg, blk_cnt, rlen); + + if(blk_cnt > SDHC_BLOCKS_MAX) + { + sdhc_debug(sdhci->reg_base, "%d blocks are too much...", blk_cnt); + return SDHC_EOVERFLOW; + } + + caps = __sd_read32(sdhci->reg_base + SDHC_CAPABILITIES); + if(caps & SDHC_CAP_SDMA && ((u32)buffer % 32 == 0) && blk_cnt > 0 && buffer != NULL) + use_dma = 1; + else + use_dma = 0; + + sdhc_debug(sdhci->reg_base, "enable DMA: %d (buffer mod 32: %d)", use_dma, (u32)buffer % 32); + + mode = mask = 0; + command = (cmd & SDHC_CMD_MASK) << SDHC_CMD_SHIFT; + if(blk_cnt > 0) + { + sdhc_debug(sdhci->reg_base, "block count is > 0; setting up mode register"); + if(use_dma == 1) + mode |= SDHC_CMDMODE_DMA_ENABLE; + mode |= SDHC_CMDMODE_MULTIBLOCK; + mode |= SDHC_CMDMODE_ACMD12_ENABLE; + mode |= SDHC_CMDMODE_BLOCKCNT_ENABLE; + if(type & SD_READ) + { + sdhc_debug(sdhci->reg_base, "read operation"); + mask = SDHC_BFR_READ_ENABLE; + mode |= SDHC_CMDMODE_READ; + } + else + { + sdhc_debug(sdhci->reg_base, "write operation"); + mask = SDHC_BFR_WRITE_ENABLE; + mode |= SDHC_CMDMODE_WRITE; + } + + command |= SDHC_CMD_DATA; + } + + if(!(type & SD_RSP_PRESENT)) + command |= SDHC_CMD_NORESP; + else if(type & SD_RSP_136) + command |= SDHC_CMD_RESP_136; + else if(type & SD_RSP_BUSY) + command |= SDHC_CMD_RESP_BUSY; + else + command |= SDHC_CMD_RESP_48; + + if(type & SD_RSP_CRC) + command |= SDHC_CMD_CRC; + + if(type & SD_RSP_IDX) + command |= SDHC_CMD_IDXCHECK; + + + sdhc_debug(sdhci->reg_base, "command = %X, mode = %X", command, mode); + sdhc_debug(sdhci->reg_base, "waiting for command inhibit bits to be cleared.."); + + retval = __sd_wait32_r(sdhci->reg_base + SDHC_PRESENT_STATE, SDHC_PRESENT_CMD_INHIBIT_BOTH); + if(retval < 0) + { + sdhc_error(sdhci->reg_base, "command inhibit bits were never cleared"); + __sd_reset(sdhci, 0); + return retval; + } + + sdhc_debug(sdhci->reg_base, "command inhibit bits cleared, sending command"); + + if(use_dma == 1) + { + sdhc_debug(sdhci->reg_base, "preparing buffer for SDMA transfer"); + if(mask == SDHC_BFR_WRITE_ENABLE) + dc_flushrange(buffer, blk_cnt * BLOCK_SIZE); + else + dc_invalidaterange(buffer, blk_cnt * BLOCK_SIZE); + + __sd_write32(sdhci->reg_base + SDHC_SDMA_ADDR, (u32)buffer); + __sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, 0); + __sd_write16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_STATUS, 0); + __sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE, INTERRUPT_ALL); + __sd_write16(sdhci->reg_base + SDHC_ERROR_INTERRUPT_ENABLE, EINTERRUPT_ALL); + __sd_write32(sdhci->reg_base + SDHC_SDMA_ADDR, (u32 )buffer); + } + + __sd_dumpregs(sdhci); + + if(blk_cnt > 0) + { + sdhc_debug(sdhci->reg_base, "writing bsize = %x, bcount = %x", BLOCK_SIZE, blk_cnt); + __sd_write16(sdhci->reg_base + SDHC_BLOCK_SIZE, (BLOCK_SIZE_512K << 12) | BLOCK_SIZE); + __sd_write16(sdhci->reg_base + SDHC_BLOCK_COUNT, blk_cnt); + } + + sdhc_debug(sdhci->reg_base, "writing mode = %x, arg = %x, cmd = %x", mode, arg, command); + __sd_write32(sdhci->reg_base + SDHC_CMD_ARG, arg); + + // just don't ask.... + __sd_write32(sdhci->reg_base + SDHC_CMD_TRANSFER_MODE, ((u32)command << 16) | mode); + + sdhc_debug(sdhci->reg_base, "writing %08x to %x", ((u32)command << 16) | mode, SDHC_CMD_TRANSFER_MODE); + sdhc_debug(sdhci->reg_base, "mode = %x", __sd_read16(sdhci->reg_base + SDHC_CMD_TRANSFER_MODE)); + + __sd_dumpregs(sdhci); + sdhc_debug(sdhci->reg_base, "waiting until command phase is done"); + retval = __sd_wait32_r(sdhci->reg_base + SDHC_PRESENT_STATE, SDHC_PRESENT_CMD_INHIBIT_CMD); + if(retval < 0) + { + sdhc_error(sdhci->reg_base, "error: command phase not completed"); + __sd_dumpregs(sdhci); + __sd_reset(sdhci, 0); + return retval; + } + sdhc_debug(sdhci->reg_base, "command phase is done"); + + __sd_dumpregs(sdhci); + + for(i = 0; i < 4; i++) + sdhc_debug(sdhci->reg_base, "response %d: %X", i, __sd_read32(sdhci->reg_base + SDHC_RESPONSE + 4*i)); + if(rlen < 4 && type & SD_RSP_PRESENT) + { + sdhc_debug(sdhci->reg_base, "response buffer not big enough for response..."); + } + else if(type & SD_RSP_PRESENT) + { + if(type & SD_RSP_136) + { + u8 *p = (u8 *)response; + + imax = 15 > rlen ? rlen : 15; + + for(i = 0; i < imax; i++) + *p++ = __sd_read8(sdhci->reg_base + SDHC_RESPONSE + i); + + for(i = 0; i < 4; i++) + sdhc_debug(sdhci->reg_base, "response %d: %X", i, __sd_read32(sdhci->reg_base + SDHC_RESPONSE + 4*i)); + } + else + { + response[0] = __sd_read32(sdhci->reg_base + SDHC_RESPONSE); + sdhc_debug(sdhci->reg_base, "response = %08X", response[0]); + } + sdhc_debug(sdhci->reg_base, "copied response to buffer"); + } + + // FIXME: check response and abort on errors? + + if(blk_cnt > 0) + { + sdhc_debug(sdhci->reg_base, "starting transfer of %d %d byte blocks", blk_cnt, BLOCK_SIZE); + if(use_dma == 0) + { + gecko_printf("sdhci: PIO mode is broken, align your buffer and use DMA.\n"); + __sd_reset(sdhci, 0); + return SDHC_EIO; +#if 0 + + sdhc_debug(sdhci->reg_base, "using data port buffer to read/write data"); + len = blk_cnt * BLOCK_SIZE; // as long as BLOCK_SIZE % 4 == 0 len % 4 is (obviously) also 0; this will crash and burn otherwise!! + ptr = (u8 *)buffer; + + while(len > 0) + { + if(len % BLOCK_SIZE == 0) + { + sdhc_debug(sdhci->reg_base, "starting to work on the next block; bytes left = %d", len); + sdhc_debug(sdhci->reg_base, "waiting for data read/write enable bits to be set"); + retval = __sd_wait32(sdhci->reg_base + SDHC_PRESENT_STATE, mask); + if(retval < 0) + { + // FIXME: error handling!! + sdhc_debug(sdhci->reg_base, "timed out"); + sdhc_debug(sdhci->reg_base, "present state = %08x", __sd_read32(sdhci->reg_base + SDHC_PRESENT_STATE)); + __sd_dumpregs(sdhci); + return retval; + } + sdhc_debug(sdhci->reg_base, "data read/write enable bits are set"); + } + if(mask == SDHC_BFR_READ_ENABLE) + *(u32 *)ptr = __sd_read32(sdhci->reg_base + SDHC_DATA); + else + __sd_write32(sdhci->reg_base + SDHC_DATA, *(u32 *)ptr); + ptr += 4; + len -= 4; + } + sdhc_debug(sdhci->reg_base, "all data transferred"); + + sdhc_debug(sdhci->reg_base, "waiting.."); // FIXME: interrupts should be used here, don't even know if this works + //if(mask == SDHC_BFR_READ_ENABLE) + // while(__sd_read32(sdhci->reg_base + SDHC_PRESENT_STATE) & (1 << 9)); + //else + // while(__sd_read32(sdhci->reg_base + SDHC_PRESENT_STATE) & (1 << 2)); + sdhc_debug(sdhci->reg_base, "done"); +#endif + } + else /* SDMA; transfer data in 512 KB blocks (i.e. 1024 512 byte blocks */ + { + u8 *ptr = (u8 *)buffer; + sdhc_debug(sdhci->reg_base, "using SDMA to transfer data"); + + // poor man's interrupts + while(1) + { + sdhc_debug(sdhci->reg_base, "waiting for interrupts..."); + retval = __sd_wait16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, INTERRUPT_DMA | INTERRUPT_TRANSFER_COMPLETE); + if(retval < 0) + { + sdhc_error(sdhci->reg_base, "failed while waiting for transfer complete or DMA interrupts..."); + __sd_reset(sdhci, 0); + return retval; + } + + retval = __sd_read16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS); + + if(retval & INTERRUPT_TRANSFER_COMPLETE) + { + __sd_dumpregs(sdhci); + sdhc_debug(sdhci->reg_base, "transfer completed. disabling interrupts again and returning."); + __sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, 0); + __sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_ENABLE, 0); + return 0; + } + else if(retval & INTERRUPT_DMA) + { + blk_cnt = blk_cnt > (SDMA_BLOCK_SIZE / SDHC_BLOCK_SIZE) ? blk_cnt - (SDMA_BLOCK_SIZE / SDHC_BLOCK_SIZE) : 0; + __sd_dumpregs(sdhci); + sdhc_debug(sdhci->reg_base, "DMA interrupt set, updating next SDMA address"); + sdhc_debug(sdhci->reg_base, "sd blocks left: %d, addr: %08x -> %08x", blk_cnt, ptr, ptr + SDMA_BLOCK_SIZE); + + if(blk_cnt == 0) + { + sdhc_error(sdhci->reg_base, "FATAL ERROR: hc wants to transfer more DMA data but no more blocks are left."); + __sd_reset(sdhci, 0); + return SDHC_EIO; + } + + __sd_write16(sdhci->reg_base + SDHC_NORMAL_INTERRUPT_STATUS, 0); + ptr += SDMA_BLOCK_SIZE; + __sd_write32(sdhci->reg_base + SDHC_SDMA_ADDR, (u32 )ptr); + + sdhc_debug(sdhci->reg_base, "next DMA transfer started."); + __sd_dumpregs(sdhci); + } + } + } + } + + return 0; +} + +void __sd_print_status(sdhci_t *sdhci) +{ + return; +#ifdef SDHC_DEBUG + u32 status; + u32 state; + int retval; + +#if 0 + // FIXME: doesn't work for some reason ?!? + // sdhc0: Card status: 00000900 [|ò¦|ó¦L] + char *state_name[] = { + "IDLE", + "READY", + "IDENT", + "STANDBY", + "TRANSFER", + "DATA (SEND)", + "RECEIVE", + "PROGRAM", + "DISCONNECT" + }; + + sdhc_debug(sdhci->reg_base, "Card status %08x [%s]", status, state_name[state]); +#endif + + + retval = __sd_cmd(sdhci, SD_CMD_SEND_STATUS, SD_R1, (u32)sdhci->rca << 16, 0, NULL, &status, sizeof(status)); + state = (status >> 9) & 0x0f; + gecko_printf("sdhc%d: Card status: %08x [", ((sdhci->reg_base - SD_REG_BASE) / 0x100), status); + + switch(state) + { + case 0: + gecko_printf("IDLE]\n"); + break; + case 1: + gecko_printf("READY]\n"); + break; + case 2: + gecko_printf("IDENT]\n"); + break; + case 3: + gecko_printf("STANDBY]\n"); + break; + case 4: + gecko_printf("TRANSFER]\n"); + break; + case 5: + gecko_printf("DATA (SEND)]\n"); + break; + case 6: + gecko_printf("DATA (RECEIVE)]\n"); + break; + case 7: + gecko_printf("PROGRAM]\n"); + break; + case 8: + gecko_printf("DISCONNECT]\n"); + break; + default: + sdhc_debug(sdhci->reg_base, "Reserved]\n", status); + } + +#endif +} + +int sd_mount(sdhci_t *sdhci) +{ + u32 caps; + s32 retval; + u32 resp[5]; + int tries; + + __sd_dumpregs(sdhci); + retval = __sd_reset(sdhci, 1); + if(retval < 0) + return retval; + + if(!sd_inserted(sdhci)) + return SDHC_ENOCARD; + + caps = __sd_read32(sdhci->reg_base + SDHC_CAPABILITIES); + + retval = __sd_power(sdhci, -1); + if(retval < 0) + return retval; + + retval = __sd_clock(sdhci, 1, 25000); + if(retval < 0) + return retval; + + sdhc_debug(sdhci->reg_base, "resetting card"); + retval = __sd_cmd(sdhci, SD_CMD_RESET_CARD, SD_R0, 0x0, 0, NULL, NULL, 0); + if(retval < 0) + return retval; + + // check for SDHC + retval = __sd_cmd(sdhci, SD_CMD_SEND_IF_COND, SD_R7, 0x1AA, 0, NULL, resp, 6); + if(retval < 0 || (resp[0] & 0xff) != 0xAA) + { + // SDv1 low-capacity card# + sdhc_error(sdhci->reg_base, "SDv1 low-capacity card deteced. resetting controller and card again."); + + __sd_reset(sdhci, 1); + retval = __sd_power(sdhci, -1); + if(retval < 0) + return retval; + + retval = __sd_clock(sdhci, 1, 25000); + if(retval < 0) + return retval; + + retval = __sd_cmd(sdhci, SD_CMD_RESET_CARD, SD_R0, 0x0, 0, NULL, NULL, 0); + if(retval < 0) + return retval; + + sdhci->is_sdhc = 0; + sdhci->ocr = 0; + } + else + { + // SDHC card deteced + sdhc_debug(sdhci->reg_base, "SDv2 card detected."); + sdhci->is_sdhc = 1; + sdhci->ocr = OCR_HCS; + } + + + if(caps & SDHC_CAP_VOLTAGE_33) + sdhci->ocr |= MMC_VDD_32_33|MMC_VDD_33_34; + if(caps & SDHC_CAP_VOLTAGE_30) + sdhci->ocr |= MMC_VDD_29_30|MMC_VDD_30_31; + if(caps & SDHC_CAP_VOLTAGE_18) + sdhci->ocr |= MMC_VDD_165_195; + + sdhc_debug(sdhci->reg_base, "waiting for card to finalize power up"); + tries = 0; + resp[0] = 0; + while(tries++ <= SDHC_WAIT_TIMEOUT_OUTER_MULTIPLY) + { + sdhc_debug(sdhci->reg_base, "attemp %d", tries); + retval = __sd_cmd(sdhci, SD_CMD_APP_SEND_OP_COND, SD_R3, sdhci->ocr, 0, NULL, resp, 6); + sdhc_debug(sdhci->reg_base, " retval = %d, response = %08X", retval, resp[0]); + if(resp[0] & OCR_POWERUP_STATUS) + { + sdhc_debug(sdhci->reg_base, "card power up is done."); + break; + } + udelay(SDHC_WAIT_TIMEOUT_OUTER); + } + + if(!(resp[0] & OCR_POWERUP_STATUS)) + { + sdhc_error(sdhci->reg_base, "powerup failed, resetting controller."); + __sd_reset(sdhci, 1); + return SDHC_EIO; + } + + sdhci->ocr = resp[0]; + + if(sdhci->ocr & OCR_CCS) + { + sdhc_debug(sdhci->reg_base, "SDHC card detected, using block instead of byte offset address mode"); + sdhci->is_sdhc = 1; + } + else + { + sdhc_debug(sdhci->reg_base, "low-capacity SD card detected. using byte offset address mode."); + sdhci->is_sdhc = 0; + } + + sdhc_debug(sdhci->reg_base, "sending ALL_SEND_CID command to get connected card"); + retval = __sd_cmd(sdhci, SD_CMD_ALL_SEND_CID, SD_R3, 0, 0, NULL, resp, 6); + if(retval < 0) + { + sdhc_error(sdhci->reg_base, "__sd_cmd returned %d, resetting controller.", retval); + __sd_reset(sdhci, 1); + return SDHC_EIO; + } + + sdhci->cid = resp[0]; + + sdhc_debug(sdhci->reg_base, "CID: %08X, requesting RCA", sdhci->cid); + retval = __sd_cmd(sdhci, SD_CMD_SEND_RELATIVE_ADDR, SD_R6, 0, 0, NULL, resp, 6); + if(retval < 0) + { + sdhc_error(sdhci->reg_base, "failed at getting RCA (%d), resetting controller.", retval); + __sd_reset(sdhci, 1); + return SDHC_EIO; + } + + sdhci->rca = (resp[0] >> 16) & 0xffff; + sdhc_debug(sdhci->reg_base, "RCA: %04X", sdhci->rca); + + __sd_print_status(sdhci); + + sd_select(sdhci); + + __sd_print_status(sdhci); + + retval = __sd_cmd(sdhci, SD_CMD_SET_BLOCKLEN, SD_R1, BLOCK_SIZE, 0, NULL, NULL, 0); + if(retval < 0) + { + sdhc_debug(sdhci->reg_base, "failed to set the block length to 512bytes (%d)", retval); + return retval; + } + + __sd_print_status(sdhci); + + sd_select(sdhci); + + sdhc_debug(sdhci->reg_base, "setting bus width to 4"); + __sd_write8(sdhci->reg_base + SDHC_HOST_CONTROL, __sd_read8(sdhci->reg_base + SDHC_HOST_CONTROL) | SDHC_HCR_BUSWIDTH_4); + retval = __sd_cmd(sdhci, SD_CMD_APP_SET_BUS_WIDTH, SD_R1, 2, 0, NULL, NULL, 0); + if(retval < 0) + { + sdhc_debug(sdhci->reg_base, "failed to set bus width to 4: %d", retval); + return retval; + } + + sdhci->is_mounted = 1; + + return 0; +} + +int sd_inserted(sdhci_t *sdhci) +{ + return (__sd_read32(sdhci->reg_base + SDHC_PRESENT_STATE) & SDHC_CARD_INSERTED) == SDHC_CARD_INSERTED; +} + +#ifndef _READONLY + +int sd_protected(sdhci_t *sdhci) +{ + return (__sd_read32(sdhci->reg_base + SDHC_PRESENT_STATE) & SDHC_WRITE_PROTECT) == SDHC_WRITE_PROTECT; +} + +#endif + +int sd_init(sdhci_t *sdhci, int slot) +{ + memset(sdhci, 0, sizeof *sdhci); + + if(slot > 1) + return SDHC_EINVAL; + + sdhci->reg_base = SD_REG_BASE + slot * 0x100; + return 0; +} + +int sd_cmd(sdhci_t *sdhci, u32 cmd, u32 type, u32 arg, u32 blk_cnt, void *buffer, u32 *response, u8 rlen) +{ + return __sd_cmd(sdhci, cmd, type, arg, blk_cnt, buffer, response, rlen); +} + +int sd_select(sdhci_t *sdhci) +{ + int retval; + + if(sdhci->is_selected == 1) + return 0; + + retval = __sd_cmd(sdhci, SD_CMD_SELECT_CARD, SD_R1B, (u32)sdhci->rca << 16, 0, NULL, NULL, 0); + + if(retval < 0) + { + sdhc_debug(sdhci->reg_base, "selecting card failed with %d", retval); + return retval; + } + + + sdhci->is_selected = 1; + sdhc_debug(sdhci->reg_base, "card selected"); + + return 0; +} + +int sd_read(sdhci_t *sdhci, u32 start_block, u32 blk_cnt, void *buffer) +{ + int retval; + u32 response; + + if(sdhci->is_mounted != 1) + return SDHC_EINVAL; + + retval = sd_select(sdhci); + if(retval < 0) + return retval; + + __sd_print_status(sdhci); + + if(sdhci->is_sdhc == 0) + start_block *= 512; + + retval = __sd_cmd(sdhci, SD_CMD_READ_MULTIPLE_BLOCK, SD_R1 | SD_READ, start_block, blk_cnt, buffer, &response, sizeof(response)); + + if(retval < 0) + sdhc_debug(sdhci->reg_base, "reading blocks failed with %d.", retval); + __sd_print_status(sdhci); + + return retval; +} + +#if _READONLY == 1 + +int sd_write(sdhci_t *sdhci, u32 start_block, u32 blk_cnt, const void *buffer) +{ + int retval; + u32 response; + + if(sdhci->is_mounted != 1) + return SDHC_EINVAL; + + retval = sd_select(sdhci); + if(retval < 0) + return retval; + + __sd_print_status(sdhci); + + if(sdhci->is_sdhc == 0) + start_block *= 512; + + retval = __sd_cmd(sdhci, SD_CMD_WRITE_MULTIPLE_BLOCK, SD_R1, start_block, blk_cnt, (void *)buffer, &response, sizeof(response)); + + if(retval < 0) + sdhc_debug(sdhci->reg_base, "writing blocks failed with %d.", retval); + __sd_print_status(sdhci); + + return retval; +} + +#endif diff --git a/sdhc.h b/sdhc.h new file mode 100644 index 0000000..4290216 --- /dev/null +++ b/sdhc.h @@ -0,0 +1,40 @@ +#ifndef __SDHC_H__ +#define __SDHC_H__ + +#include "types.h" + +#define SDHC_ENOCARD -0x1001 +#define SDHC_ESTRANGE -0x1002 +#define SDHC_EOVERFLOW -0x1003 +#define SDHC_ETIMEDOUT -0x1004 +#define SDHC_EINVAL -0x1005 +#define SDHC_EIO -0x1006 + +typedef struct +{ + u32 reg_base; + + u8 is_sdhc; + u8 is_selected; + u8 is_mounted; + + u16 rca; + u32 ocr; + u32 cid; +} sdhci_t; + +int sd_init(sdhci_t *sdhci, int slot); +int sd_reset(sdhci_t *sdhci); +int sd_inserted(sdhci_t *sdhci); +int sd_protected(sdhci_t *sdhci); +int sd_mount(sdhci_t *sdhci); +int sd_cmd(sdhci_t *sdhci, u32 cmd, u32 rsp_type, u32 arg, u32 blk_cnt, void *buffer, u32 *response, u8 rlen); +int sd_select(sdhci_t *sdhci); +int sd_read(sdhci_t *sdhci, u32 start_block, u32 blk_cnt, void *buffer); +int sd_write(sdhci_t *sdhci, u32 start_block, u32 blk_cnt, const void *buffer); + +u8 __sd_read8(u32 addr); +u16 __sd_read16(u32 addr); +u32 __sd_read32(u32 addr); + +#endif diff --git a/start.S b/start.S new file mode 100644 index 0000000..9b3aed9 --- /dev/null +++ b/start.S @@ -0,0 +1,177 @@ +.arm + +.extern _main +.extern __got_start +.extern __got_end +.extern __bss_start +.extern __bss_end +.extern __stack_addr +.globl _start +.globl debug_output +.globl panic +.globl delay +.globl read32, write32 +.globl read16, write16 +.globl read8, write8 +.globl getcpuid + +.section .init + +_start: + @ Get real address of _start + sub r4, pc, #8 + @ Subtract offset to get the address that we were loaded at + ldr r0, =_start + sub r4, r4, r0 + @ Output 0x42 to the debug port + mov r0, #0x42 + bl debug_output + + @ Set up a stack + ldr sp, =__stack_addr + add sp, r4 + + @ perform boot2v3 memory controller poke + bl memctrl_do_sub_sub_poke + + @ Output 0x43 to the debug port + mov r0, #0x43 + bl debug_output + + @ relocate the GOT entries + ldr r1, =__got_start + add r1, r4 + ldr r2, =__got_end + add r2, r4 +got_loop: + @ check for the end + cmp r1, r2 + beq done_got + @ read the GOT entry + ldr r3, [r1] + @ add our base address + add r3, r4 + str r3, [r1] + @ move on + add r1, r1, #4 + b got_loop + +done_got: + @ clear BSS + ldr r1, =__bss_start + add r1, r4 + ldr r2, =__bss_end + add r2, r4 + mov r3, #0 +bss_loop: + @ check for the end + cmp r1, r2 + beq done_bss + @ clear the word and move on + str r3, [r1] + add r1, r1, #4 + b bss_loop + +done_bss: + mov r0, #0x44 + bl debug_output + @ take the plunge + mov r0, r4 + bl _main + @ _main returned! Go to whatever address it returned... + mov pc, r0 + +memctrl_do_sub_sub_poke: + stmdb sp!, {lr} + ldr r0, =0x163 @ reg_address + mov r1, #0x4C @ address + bl memctrl_sub_poke + ldr r0, =0x163 @ read address back (flush?) + bl memctrl_sub_peek + ldr r0, =0x162 @ reg_data + mov r1, #1 @ data + bl memctrl_sub_poke + ldmia sp!, {pc} + +memctrl_sub_poke: + ldr r2, =0xD8B4000 + strh r0, [r2, #0x74] @ reg_address <= address + ldrh r0, [r2, #0x74] @ read reg_address back + strh r1, [r2, #0x76] @ reg_data <= data + mov pc, lr + +memctrl_sub_peek: + ldr r2, =0xD8B4000 + strh r0, [r2, #0x74] @ reg_address <= address + ldrh r0, [r2, #0x74] @ read reg_address back + ldrh r0, [r2, #0x76] @ data <= reg_data + mov pc, lr + +.pool + +debug_output: + @ load address of port + mov r3, #0xd800000 + @ load old value + ldr r2, [r3, #0xe0] + @ clear debug byte + bic r2, r2, #0xFF0000 + @ insert new value + and r0, r0, #0xFF + orr r2, r2, r0, LSL #16 + @ store back + str r2, [r3, #0xe0] + mov pc, lr + +panic: + mov r4, r0 +_panic: + mov r0, r4 + bl debug_output + ldr r0, =6175000 + bl delay + mov r0, #0x00 + bl debug_output + ldr r0, =6175000 + bl delay + b _panic + +@ the speed of this seems to decrease wildly with certain (non-)alignments +@ probably some prefetch buffer / cache / DRAM junk + .balign 64 +delay: + cmp r0, #0 + moveq pc, lr +1: + subs r0, r0, #1 + bne 1b + mov pc, lr + +read32: + ldr r0, [r0] + mov pc, lr + +write32: + str r1, [r0] + mov pc, lr + +read16: + ldrh r0, [r0] + mov pc, lr + +write16: + strh r1, [r0] + mov pc, lr + +read8: + ldrb r0, [r0] + mov pc, lr + +write8: + strb r1, [r0] + mov pc, lr + +getcpuid: + mrc p15, 0, r0, c0, c0 + mov pc, lr + \ No newline at end of file diff --git a/start.h b/start.h new file mode 100644 index 0000000..fe82649 --- /dev/null +++ b/start.h @@ -0,0 +1,13 @@ +#ifndef __START_H__ +#define __START_H__ + +#include "types.h" + +void delay(u32 delay); + +#define udelay(d) delay(247*(d)/10) + +void debug_output(u8 byte); +void panic(u8 code); + +#endif diff --git a/string.c b/string.c new file mode 100644 index 0000000..7d5481c --- /dev/null +++ b/string.c @@ -0,0 +1,94 @@ +/* + * linux/lib/string.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include "string.h" + +size_t strnlen(const char *s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} + +size_t strlen(const char *s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} + +char *strncpy(char *dst, const char *src, size_t n) +{ + char *ret = dst; + + while (n && (*dst++ = *src++)) + n--; + + while (n--) + *dst++ = 0; + + return ret; +} + +char *strcpy(char *dst, const char *src) +{ + char *ret = dst; + + while ((*dst++ = *src++)) + ; + + return ret; +} + +int strcmp(const char *p, const char *q) +{ + for (;;) { + unsigned char a, b; + a = *p++; + b = *q++; + if (a == 0 || a != b) + return a - b; + } +} + +void *memset(void *dst, int x, size_t n) +{ + unsigned char *p; + + for (p = dst; n; n--) + *p++ = x; + + return dst; +} + +void *memcpy(void *dst, const void *src, size_t n) +{ + unsigned char *p; + const unsigned char *q; + + for (p = dst, q = src; n; n--) + *p++ = *q++; + + return dst; +} + +int memcmp(const void *s1, const void *s2, size_t n) +{ + unsigned char *us1 = (unsigned char *) s1; + unsigned char *us2 = (unsigned char *) s2; + while (n-- != 0) { + if (*us1 != *us2) + return (*us1 < *us2) ? -1 : +1; + us1++; + us2++; + } + return 0; +} + diff --git a/string.h b/string.h new file mode 100644 index 0000000..8a1f13e --- /dev/null +++ b/string.h @@ -0,0 +1,15 @@ +#ifndef _STRING_H_ +#define _STRING_H_ + +#include "types.h" + +char *strcpy(char *, const char *); +char *strncpy(char *, const char *, size_t); +int strcmp(const char *, const char *); +size_t strlen(const char *); +size_t strnlen(const char *, size_t); +void *memset(void *, int, size_t); +void *memcpy(void *, const void *, size_t); +int memcmp(const void *s1, const void *s2, size_t n); + +#endif diff --git a/stub.c b/stub.c new file mode 100644 index 0000000..e702860 --- /dev/null +++ b/stub.c @@ -0,0 +1,327 @@ +#include "types.h" +#include "utils.h" +#include "start.h" +#include "hollywood.h" +#include "sdhc.h" +#include "string.h" +#include "memory.h" +#include "elf.h" +#include "gecko.h" +#include "ff.h" +#include "lcd.h" + +void hexline(void *addr, int len) +{ + u8 *p = (u8*)addr; + while(len--) { + gecko_printf("%02x",*p++); + } +} + +typedef struct { + u32 hdrsize; + u32 loadersize; + u32 elfsize; + u32 argument; +} ioshdr; + +int dogecko; + +void boot2_loadelf(u8 *elf) { + if(dogecko) + gecko_puts("Loading boot2 ELF...\n"); + + if(memcmp("\x7F" "ELF\x01\x02\x01\x61\x01",elf,9)) { + if(dogecko) + gecko_printf("Invalid ELF header! 0x%02x 0x%02x 0x%02x 0x%02x\n",elf[0], elf[1], elf[2], elf[3]); + panic(0xE3); + } + + Elf32_Ehdr *ehdr = (Elf32_Ehdr*)elf; + if(ehdr->e_phoff == 0) { + if(dogecko) + gecko_printf("ELF has no program headers!\n"); + panic(0xE4); + } + int count = ehdr->e_phnum; + Elf32_Phdr *phdr = (Elf32_Phdr*)(elf + ehdr->e_phoff); + if(dogecko) + gecko_printf("PHDRS at %p\n",phdr); + while(count--) + { + if(phdr->p_type != PT_LOAD) { + if(dogecko) + gecko_printf("Skipping PHDR of type %d\n",phdr->p_type); + } else { + void *src = elf + phdr->p_offset; + if(dogecko) + gecko_printf("LOAD %p -> %p [0x%x]\n",src, phdr->p_paddr, phdr->p_filesz); + memcpy(phdr->p_paddr, src, phdr->p_filesz); + } + phdr++; + } + if(dogecko) + gecko_puts("Done!\n"); +} + +#define BOOT_FILE "/system/iosboot.bin" + +FATFS fatfs; + +void turn_stuff_on(void) +{ + clear32(HW_GPIO1OUT, 0x10); + udelay(100); + set32(HW_RESETS, 0x7FFFFCF); +} + +void reset_audio(u8 flag) +{ + + // GPIO2IN is probably mislabeled + if(flag) + clear32(HW_DIFLAGS, 0x180); + else + mask32(HW_DIFLAGS, 0x80, 0x100); + + clear32(HW_GPIO2IN, 0x80000000); + udelay(2); + clear32(HW_GPIO2IN, 0x40000000); + + if(flag) { + clear32(HW_GPIO2IN, 0x10000000); + mask32(HW_GPIO2DIR, 0x7FFFFFF, 0x4B0FFCE); + } else { + mask32(HW_GPIO2DIR, 0x7FFFFFF, 0x4640FC0); + } + udelay(10); + set32(HW_GPIO2IN, 0x40000000); + udelay(500); + set32(HW_GPIO2IN, 0x80000000); + udelay(2); +} + +void regs_setup(void) +{ + u8 hwood_ver, hwood_hi, hwood_lo; + hwood_ver = read32(0xd800214); + hwood_hi = hwood_ver >> 4; //R0 + hwood_lo = hwood_ver & 0xF; //R1 + + *(u32*)0xFFFF897C = *(u32*)0xFFFF86D0; + set32(HW_EXICTRL, EXICTRL_ENABLE_EXI); + mem_protect(1, (void*)0x13420000, (void*)0x1fffffff); + clear32(HW_EXICTRL, 0x10); + if(hwood_hi == 0) + write32(0xd8b0010, 0); + write32(0xd8b0010, 0); + if(hwood_hi == 1 && hwood_lo == 0) + mask32(0xd800140, 0x0000FFF0, 1); + set32(0xd80018C, 0x400); + set32(0xd80018C, 0x800); + + reset_audio(0); + //boot2_sub_FFFF5D08(0); + //boot2_sub_FFFF5C40(hwood_hi); + //boot2_sub_FFFF6AA8(); + + turn_stuff_on(); + // what do these two pokes do? no clue. Not needed but I'm leaving them in anyway. + write32(0xd8001e0, 0x65244A); //? + write32(0xd8001e4, 0x46A024); //? + + clear32(HW_GPIO1OWNER, 0x10); + set32(HW_GPIO1DIR, 0x10); + //write32(HW_ALARM,0); + //write32(HW_ALARM,0); +} + +void hex32(u32 x) { + int i; + u8 b; + for(i=0;i<8;i++) { + b = x >> 28; + if(b > 9) + lcd_putchar(b-10+'a'); + else + lcd_putchar(b+'0'); + x <<= 4; + } +} + +extern void *__end; + +void *_main(void *base) +{ + FRESULT fres; + + ioshdr *hdr = (ioshdr*)base; + ioshdr *iosboot; + u8 *elf; + + elf = (u8*) base; + elf += hdr->hdrsize + hdr->loadersize; + + dogecko = 1; + + debug_output(0xF1); + mem_setswap(1); + + write32(HW_IRQENABLE, 0); + + clear32(HW_GPIO1DIR, 0x80); + clear32(HW_GPIO1OWNER, 0x80); + udelay(10000); + if(read32(HW_GPIO1IN) & 0x80) { + dogecko = 0; + goto boot2; + } + + // NOTE: END DEBUG CRITICAL ZONE + + lcd_init(); + + regs_setup(); + //debug_output(0x50); + //debug_output(0x51); + debug_output(0xF8); + + gecko_init(); + + debug_output(0xF9); + + lcd_puts("BootMii v0.1\n"); + + if(dogecko) { + gecko_puts("Hello, world from Starlet again!\n"); + gecko_puts("BootMii here, version 0.1\n"); + gecko_printf("BOOT2 header (@%p):\n",hdr); + gecko_printf(" Header size: %08x\n", hdr->hdrsize); + gecko_printf(" Loader size: %08x\n", hdr->loadersize); + gecko_printf(" ELF size: %08x\n", hdr->elfsize); + gecko_printf(" Argument: %08x\n", hdr->argument); + + gecko_printf("ELF at %p\n",elf); + + gecko_puts("Trying to mount SD...\n"); + } + + fres = f_mount(0, &fatfs); + + if(fres != FR_OK) { + if(dogecko) + gecko_printf("Error %d while trying to mount SD\n", fres); + debug_output(0x12); + debug_output(fres&0xFF); + goto boot2; + } + + //debug_output(0xF2); + + if(dogecko) + gecko_puts("Trying to open SD:" BOOT_FILE "\n"); + + FIL fd; + u32 read; + + fres = f_open(&fd, BOOT_FILE, FA_READ); + if(fres != FR_OK) { + if(dogecko) + gecko_printf("Error %d while trying to open file\n", fres); + //debug_output(0x13); + //debug_output(fres&0xFF); + goto boot2; + } + + lcd_puts("."); + + // NOTE: END CRITICAL ZONE + // anything from here to boot2: shouldn't be able to cause a brick + + debug_output(0xF2); + + iosboot = (ioshdr *)ALIGN_FORWARD(((u32)&__end) + 0x100, 0x100); + + if(dogecko) + gecko_printf("Trying to read IOSBOOT header to %p\n", iosboot); + + fres = f_read(&fd, iosboot, sizeof(ioshdr), &read); + if(fres != FR_OK) { + if(dogecko) + gecko_printf("Error %d while trying to read file header\n", fres); + //debug_output(0x14); + //debug_output(fres&0xFF); + goto boot2; + } + if(read != sizeof(ioshdr)) { + if(dogecko) + gecko_printf("Got %d bytes, expected %d\n", read, sizeof(ioshdr)); + //debug_output(0x24); + goto boot2; + } + + lcd_puts("."); + //debug_output(0xF5); + + if(dogecko) { + gecko_printf("IOSBOOT header (@%p):\n",iosboot); + gecko_printf(" Header size: %08x\n", iosboot->hdrsize); + gecko_printf(" Loader size: %08x\n", iosboot->loadersize); + gecko_printf(" ELF size: %08x\n", iosboot->elfsize); + gecko_printf(" Argument: %08x\n", iosboot->argument); + } + + u32 totalsize = iosboot->hdrsize + iosboot->loadersize + iosboot->elfsize; + + if(dogecko) { + gecko_printf("Total IOSBOOT size: 0x%x\n", totalsize); + + gecko_printf("Trying to read IOSBOOT to %p\n", iosboot); + } + + fres = f_read(&fd, iosboot+1, totalsize-sizeof(ioshdr), &read); + if(fres != FR_OK) { + if(dogecko) + gecko_printf("Error %d while trying to read file header\n", fres); + //debug_output(0x15); + //debug_output(fres&0xFF); + goto boot2; + } + if(read != (totalsize-sizeof(ioshdr))) { + if(dogecko) + gecko_printf("Got %d bytes, expected %d\n", read, (totalsize-sizeof(ioshdr))); + //debug_output(0x25); + goto boot2; + } + + lcd_puts("."); + + //debug_output(0xF6); + + if(dogecko) { + gecko_puts("IOSBOOT read\n"); + gecko_printf("Setting argument to %p\n", base); + } + iosboot->argument = (u32)base; + + void *entry = (void*)(((u32)iosboot) + iosboot->hdrsize); + + lcd_puts(" \x7e IOSBOOT \n"); + + if(dogecko) + gecko_printf("Launching IOSBOOT @ %p\n",entry); + + debug_output(0xF3); + + return entry; + +boot2: + debug_output(0xC8); + if(dogecko) + gecko_puts("Couldn't load from SD, falling back to boot2\n"); + lcd_puts(" \x7e BOOT2 \n"); + boot2_loadelf(elf); + debug_output(0xC9); + return (void *) 0xFFFF0000; + +} diff --git a/stub.ld b/stub.ld new file mode 100644 index 0000000..9c85281 --- /dev/null +++ b/stub.ld @@ -0,0 +1,104 @@ +OUTPUT_FORMAT("elf32-bigarm") +OUTPUT_ARCH(arm) +EXTERN(_start) +ENTRY(_start) + +__base_addr = 0; + +SECTIONS +{ + . = __base_addr; + .header : + { + __header = .; + /* Entry point (offset) */ + LONG(__code_start); + /* Loader size */ + LONG(__loader_size); + /* ELF size */ + LONG(0); + /* Boot argument? */ + LONG(0); + . = ALIGN(64); + } + + __code_start = .; + + .init : + { + *(.init) + . = ALIGN(4); + } + + .got : + { + __got_start = .; + *(.got.*) + *(.got) + . = ALIGN(4); + __got_end = . ; + } + + .text : + { + *(.text.*) + *(.gnu.warning) + *(.gnu.linkonce.t*) + *(.glue_7) + *(.glue_7t) + . = ALIGN(4); + } + + __text_end = . ; + + .rodata : + { + *(.rodata) + *all.rodata*(*) + *(.roda) + *(.rodata.*) + *(.gnu.linkonce.r*) + . = ALIGN(4); + } + + .data : + { + *(.data) + *(.data.*) + *(.gnu.linkonce.d*) + . = ALIGN(4); + } + + .bss : + { + __bss_start = . ; + *(.dynbss) + *(.gnu.linkonce.b*) + *(.bss*) + *(.sbss*) + *(COMMON) + . = ALIGN(4); + __bss_end = . ; + } + + . = ALIGN(64); + .stack : + { + __stack_end = .; + . += 0x800; + LONG(0); + . = ALIGN(64); + __stack_addr = .; + } + + __end = .; + +} +__loader_size = __end - __code_start; + +PROVIDE (__stack_end = __stack_end); +PROVIDE (__stack_addr = __stack_addr); +PROVIDE (__got_start = __got_start); +PROVIDE (__got_end = __got_end); +PROVIDE (__bss_start = __bss_start); +PROVIDE (__bss_end = __bss_end); diff --git a/types.h b/types.h new file mode 100644 index 0000000..78e16b3 --- /dev/null +++ b/types.h @@ -0,0 +1,28 @@ +#ifndef __TYPES_H__ +#define __TYPES_H__ + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +typedef signed char s8; +typedef signed short s16; +typedef signed int s32; +typedef signed long long s64; + +typedef volatile unsigned char vu8; +typedef volatile unsigned short vu16; +typedef volatile unsigned int vu32; +typedef volatile unsigned long long vu64; + +typedef volatile signed char vs8; +typedef volatile signed short vs16; +typedef volatile signed int vs32; +typedef volatile signed long long vs64; + +typedef s32 size_t; + +#define NULL ((void *)0) + +#endif diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..a72fb2e --- /dev/null +++ b/utils.c @@ -0,0 +1,40 @@ +#include "types.h" +#include "utils.h" +#include "gecko.h" +#include "vsprintf.h" + +static char ascii(char s) { + if(s < 0x20) return '.'; + if(s > 0x7E) return '.'; + return s; +} + +void hexdump(void *d, int len) { + u8 *data; + int i, off; + data = (u8*)d; + for (off=0; off=len) gecko_printf(" "); + else gecko_printf("%02x ",data[off+i]); + + gecko_printf(" "); + for(i=0; i<16; i++) + if((i+off)>=len) gecko_printf(" "); + else gecko_printf("%c",ascii(data[off+i])); + gecko_printf("\n"); + } +} + +int sprintf(char *str, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vsprintf(str, fmt, args); + va_end(args); + return i; +} + diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..cdf97b6 --- /dev/null +++ b/utils.h @@ -0,0 +1,187 @@ +#ifndef __UTILS_H__ +#define __UTILS_H__ + +static inline u32 read32(u32 addr) +{ + u32 data; + __asm__ volatile ("ldr\t%0, [%1]" : "=r" (data) : "r" (addr)); + return data; +} + +static inline void write32(u32 addr, u32 data) +{ + __asm__ volatile ("str\t%0, [%1]" : : "r" (data), "r" (addr)); +} + +static inline u32 set32(u32 addr, u32 set) +{ + u32 data; + __asm__ volatile ( + "ldr\t%0, [%1]\n" + "\torr\t%0, %2\n" + "\tstr\t%0, [%1]" + : "=&r" (data) + : "r" (addr), "r" (set) + ); + return data; +} + +static inline u32 clear32(u32 addr, u32 clear) +{ + u32 data; + __asm__ volatile ( + "ldr\t%0, [%1]\n" + "\tbic\t%0, %2\n" + "\tstr\t%0, [%1]" + : "=&r" (data) + : "r" (addr), "r" (clear) + ); + return data; +} + + +static inline u32 mask32(u32 addr, u32 clear, u32 set) +{ + u32 data; + __asm__ volatile ( + "ldr\t%0, [%1]\n" + "\tbic\t%0, %3\n" + "\torr\t%0, %2\n" + "\tstr\t%0, [%1]" + : "=&r" (data) + : "r" (addr), "r" (set), "r" (clear) + ); + return data; +} + +static inline u16 read16(u32 addr) +{ + u32 data; + __asm__ volatile ("ldrh\t%0, [%1]" : "=r" (data) : "r" (addr)); + return data; +} + +static inline void write16(u32 addr, u16 data) +{ + __asm__ volatile ("strh\t%0, [%1]" : : "r" (data), "r" (addr)); +} + +static inline u16 set16(u32 addr, u16 set) +{ + u16 data; + __asm__ volatile ( + "ldrh\t%0, [%1]\n" + "\torr\t%0, %2\n" + "\tstrh\t%0, [%1]" + : "=&r" (data) + : "r" (addr), "r" (set) + ); + return data; +} + +static inline u16 clear16(u32 addr, u16 clear) +{ + u16 data; + __asm__ volatile ( + "ldrh\t%0, [%1]\n" + "\tbic\t%0, %2\n" + "\tstrh\t%0, [%1]" + : "=&r" (data) + : "r" (addr), "r" (clear) + ); + return data; +} + + +static inline u16 mask16(u32 addr, u16 clear, u16 set) +{ + u16 data; + __asm__ volatile ( + "ldrh\t%0, [%1]\n" + "\tbic\t%0, %3\n" + "\torr\t%0, %2\n" + "\tstrh\t%0, [%1]" + : "=&r" (data) + : "r" (addr), "r" (set), "r" (clear) + ); + return data; +} + +static inline u8 read8(u32 addr) +{ + u32 data; + __asm__ volatile ("ldrb\t%0, [%1]" : "=r" (data) : "r" (addr)); + return data; +} + +static inline void write8(u32 addr, u8 data) +{ + __asm__ volatile ("strb\t%0, [%1]" : : "r" (data), "r" (addr)); +} + +static inline u8 set8(u32 addr, u8 set) +{ + u8 data; + __asm__ volatile ( + "ldrb\t%0, [%1]\n" + "\torr\t%0, %2\n" + "\tstrb\t%0, [%1]" + : "=&r" (data) + : "r" (addr), "r" (set) + ); + return data; +} + +static inline u8 clear8(u32 addr, u8 clear) +{ + u8 data; + __asm__ volatile ( + "ldrb\t%0, [%1]\n" + "\tbic\t%0, %2\n" + "\tstrb\t%0, [%1]" + : "=&r" (data) + : "r" (addr), "r" (clear) + ); + return data; +} + + +static inline u8 mask8(u32 addr, u8 clear, u8 set) +{ + u8 data; + __asm__ volatile ( + "ldrb\t%0, [%1]\n" + "\tbic\t%0, %3\n" + "\torr\t%0, %2\n" + "\tstrb\t%0, [%1]" + : "=&r" (data) + : "r" (addr), "r" (set), "r" (clear) + ); + return data; +} + +#define STACK_ALIGN(type, name, cnt, alignment) \ +u8 _al__##name[((sizeof(type)*(cnt)) + (alignment) + \ +(((sizeof(type)*(cnt))%(alignment)) > 0 ? ((alignment) - \ +((sizeof(type)*(cnt))%(alignment))) : 0))]; \ +type *name = (type*)(((u32)(_al__##name)) + ((alignment) - (( \ +(u32)(_al__##name))&((alignment)-1)))) + +#define ATTRIBUTE_ALIGN(v) __attribute__((aligned(v))) + + +/* + * These functions are guaranteed to copy by reading from src and writing to dst in -bit units + * If size is not aligned, the remaining bytes are not copied + */ +void memset32(void *dst, u32 value, u32 size); +void memcpy32(void *dst, void *src, u32 size); +void memset16(void *dst, u16 value, u32 size); +void memcpy16(void *dst, void *src, u32 size); +void memset8(void *dst, u8 value, u32 size); +void memcpy8(void *dst, void *src, u32 size); + +void hexdump(void *d, int len); +int sprintf(char *str, const char *fmt, ...); + +#endif diff --git a/utils_asm.S b/utils_asm.S new file mode 100644 index 0000000..e403100 --- /dev/null +++ b/utils_asm.S @@ -0,0 +1,62 @@ +.arm + +.globl memcpy32 +.globl memcpy16 +.globl memcpy8 +.globl memset32 +.globl memset16 +.globl memset8 + +.text + +memcpy32: + bics r2, #3 + bxeq lr +1: ldr r3, [r1],#4 + str r3, [r0],#4 + subs r2, #4 + bne 1b + bx lr + +memset32: + bics r2, #3 + bxeq lr +1: str r1, [r0],#4 + subs r2, #4 + bne 1b + bx lr + +memcpy16: + bics r2, #1 + bxeq lr +1: ldr r3, [r1],#2 + str r3, [r0],#2 + subs r2, #2 + bne 1b + bx lr + +memset16: + bics r2, #1 + bxeq lr +1: str r1, [r0],#2 + subs r2, #2 + bne 1b + bx lr + +memcpy8: + cmp r2, #0 + bxeq lr +1: ldr r3, [r1],#1 + str r3, [r0],#1 + subs r2, #1 + bne 1b + bx lr + +memset8: + cmp r2, #0 + bxeq lr +1: str r1, [r0],#1 + subs r2, #1 + bne 1b + bx lr + diff --git a/vsprintf.c b/vsprintf.c new file mode 100644 index 0000000..12cce95 --- /dev/null +++ b/vsprintf.c @@ -0,0 +1,291 @@ +/* + * linux/lib/vsprintf.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ +/* + * Wirzenius wrote this portably, Torvalds fucked it up :-) + */ + +#include +#include "string.h" + +static inline int isdigit(int c) +{ + return c >= '0' && c <= '9'; +} + +static inline int isxdigit(int c) +{ + return (c >= '0' && c <= '9') + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F'); +} + +static inline int islower(int c) +{ + return c >= 'a' && c <= 'z'; +} + +static inline int toupper(int c) +{ + if (islower(c)) + c -= 'a'-'A'; + return c; +} + +static int skip_atoi(const char **s) +{ + int i=0; + + while (isdigit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SPECIAL 32 /* 0x */ +#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */ + +#define do_div(n,base) ({ \ +int __res; \ +__res = ((unsigned long) n) % (unsigned) base; \ +n = ((unsigned long) n) / (unsigned) base; \ +__res; }) + +static char * number(char * str, long num, int base, int size, int precision + ,int type) +{ + char c,sign,tmp[66]; + const char *digits="0123456789abcdefghijklmnopqrstuvwxyz"; + int i; + + if (type & LARGE) + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 36) + return 0; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++]='0'; + else while (num != 0) + tmp[i++] = digits[do_div(num,base)]; + if (i > precision) + precision = i; + size -= precision; + if (!(type&(ZEROPAD+LEFT))) + while(size-->0) + *str++ = ' '; + if (sign) + *str++ = sign; + if (type & SPECIAL) { + if (base==8) + *str++ = '0'; + else if (base==16) { + *str++ = '0'; + *str++ = digits[33]; + } + } + if (!(type & LEFT)) + while (size-- > 0) + *str++ = c; + while (i < precision--) + *str++ = '0'; + while (i-- > 0) + *str++ = tmp[i]; + while (size-- > 0) + *str++ = ' '; + return str; +} + +int vsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + unsigned long num; + int i, base; + char * str; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + + for (str=buf ; *fmt ; ++fmt) { + if (*fmt != '%') { + *str++ = *fmt; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': flags |= LEFT; goto repeat; + case '+': flags |= PLUS; goto repeat; + case ' ': flags |= SPACE; goto repeat; + case '#': flags |= SPECIAL; goto repeat; + case '0': flags |= ZEROPAD; goto repeat; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + ++fmt; + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + *str++ = ' '; + *str++ = (unsigned char) va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + continue; + + case 's': + s = va_arg(args, char *); + if (!s) + s = ""; + + len = strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + continue; + + case 'p': + if (field_width == -1) { + field_width = 2*sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, + (unsigned long) va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + + case 'n': + if (qualifier == 'l') { + long * ip = va_arg(args, long *); + *ip = (str - buf); + } else { + int * ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + *str++ = '%'; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'X': + flags |= LARGE; + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + continue; + } + if (qualifier == 'l') + num = va_arg(args, unsigned long); + else if (qualifier == 'h') { + num = (unsigned short) va_arg(args, int); + if (flags & SIGN) + num = (short) num; + } else if (flags & SIGN) + num = va_arg(args, int); + else + num = va_arg(args, unsigned int); + str = number(str, num, base, field_width, precision, flags); + } + *str = '\0'; + return str-buf; +} diff --git a/vsprintf.h b/vsprintf.h new file mode 100644 index 0000000..04b171a --- /dev/null +++ b/vsprintf.h @@ -0,0 +1,3 @@ +#include + +int vsprintf(char *buf, const char *fmt, va_list args);