Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: PinDetect TextLCD mbed mRotaryEncoder
Revision 0:afb2650fb49a, committed 2012-02-13
- Comitter:
- cicklaus
- Date:
- Mon Feb 13 02:11:20 2012 +0000
- Commit message:
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FATFileSystem/Core/diskio.c Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,138 @@
+/*-----------------------------------------------------------------------*/
+/* 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. */
+/*-----------------------------------------------------------------------*/
+
+//Modified by Thomas Hamilton, Copyright 2010
+
+#include "diskio.h"
+
+DSTATUS disk_initialize(BYTE drv)
+{
+ if (FATFileSystem::DriveArray[drv])
+ {
+ return (DSTATUS)FATFileSystem::DriveArray[drv]->disk_initialize();
+ }
+ else
+ {
+ return STA_NOINIT;
+ }
+}
+
+DSTATUS disk_status(BYTE drv)
+{
+ if (FATFileSystem::DriveArray[drv])
+ {
+ return (DSTATUS)FATFileSystem::DriveArray[drv]->disk_status();
+ }
+ else
+ {
+ return STA_NOINIT;
+ }
+}
+
+DRESULT disk_read(BYTE drv, BYTE* buff, DWORD sector, BYTE count)
+{
+ if (FATFileSystem::DriveArray[drv])
+ {
+ return (DRESULT)FATFileSystem::DriveArray[drv]->disk_read((unsigned char*)buff,
+ (unsigned long)sector, (unsigned char)count);
+ }
+ else
+ {
+ return RES_NOTRDY;
+ }
+}
+
+#if _READONLY == 0
+DRESULT disk_write(BYTE drv, const BYTE* buff, DWORD sector, BYTE count)
+{
+ if (FATFileSystem::DriveArray[drv])
+ {
+ return (DRESULT)FATFileSystem::DriveArray[drv]->disk_write((const unsigned char*)buff,
+ (unsigned long)sector, (unsigned char)count);
+ }
+ else
+ {
+ return RES_NOTRDY;
+ }
+}
+#endif
+
+DRESULT disk_ioctl(BYTE drv, BYTE ctrl, void* buff)
+{
+ switch (ctrl)
+ {
+ case CTRL_SYNC:
+ if (FATFileSystem::DriveArray[drv])
+ {
+ return (DRESULT)FATFileSystem::DriveArray[drv]->disk_sync();
+ }
+ else
+ {
+ return RES_NOTRDY;
+ }
+
+ case GET_SECTOR_SIZE:
+ if (FATFileSystem::DriveArray[drv])
+ {
+ WORD Result = FATFileSystem::DriveArray[drv]->disk_sector_size();
+ if (Result > 0)
+ {
+ *((WORD*)buff) = Result;
+ return RES_OK;
+ }
+ else
+ {
+ return RES_ERROR;
+ }
+ }
+ else
+ {
+ return RES_NOTRDY;
+ }
+
+ case GET_SECTOR_COUNT:
+ if (FATFileSystem::DriveArray[drv])
+ {
+ DWORD Result = FATFileSystem::DriveArray[drv]->disk_sector_count();
+ if (Result > 0)
+ {
+ *((DWORD*)buff) = Result;
+ return RES_OK;
+ }
+ else
+ {
+ return RES_ERROR;
+ }
+ }
+ else
+ {
+ return RES_NOTRDY;
+ }
+
+ case GET_BLOCK_SIZE:
+ if (FATFileSystem::DriveArray[drv])
+ {
+ DWORD Result = FATFileSystem::DriveArray[drv]->disk_block_size();
+ if (Result > 0)
+ {
+ *((DWORD*)buff) = Result;
+ return RES_OK;
+ }
+ else
+ {
+ return RES_ERROR;
+ }
+ }
+ else
+ {
+ return RES_NOTRDY;
+ }
+
+ default:
+ return RES_PARERR;
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FATFileSystem/Core/diskio.h Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,57 @@
+/*-----------------------------------------------------------------------
+/ Low level disk interface module include file R0.06 (C)ChaN, 2007
+/-----------------------------------------------------------------------*/
+
+//Modified by Thomas Hamilton, Copyright 2010
+
+#ifndef _DISKIO
+#define _DISKIO
+
+#define _READONLY 0
+#define _USE_IOCTL 1
+
+#include "integer.h"
+#include "FATFileSystem.h"
+#include <stdio.h>
+
+/* Status of Disk Functions */
+typedef BYTE DSTATUS;
+#define STA_NOINIT 0x01 /* Drive not initialized */
+#define STA_NODISK 0x02 /* No medium in the drive */
+#define STA_PROTECT 0x04 /* Write protected */
+/* 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*);
+/* Command code for disk_ioctrl() */
+#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
+#define MMC_GET_TYPE 10 /* MMC/SDC command */
+#define MMC_GET_CSD 11
+#define MMC_GET_CID 12
+#define MMC_GET_OCR 13
+#define MMC_GET_SDSTAT 14
+#define ATA_GET_REV 20 /* ATA/CF command */
+#define ATA_GET_MODEL 21
+#define ATA_GET_SN 22
+
+#endif
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FATFileSystem/Core/ff.c Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,3555 @@
+/*----------------------------------------------------------------------------/
+/ FatFs - FAT file system module R0.08 (C)ChaN, 2010
+/-----------------------------------------------------------------------------/
+/ FatFs module is a generic FAT file system module for small embedded systems.
+/ This is a free software that opened for education, research and commercial
+/ developments under license policy of following terms.
+/
+/ Copyright (C) 2010, ChaN, all right reserved.
+/
+/ * The FatFs module is a free software and there is NO WARRANTY.
+/ * No restriction on use. You can use, modify and redistribute it for
+/ personal, non-profit or commercial products 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) partition.
+/ 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 algorithm 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 physical 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.
+/
+/ Apr 01,'09 R0.07 Merged Tiny-FatFs as a buffer configuration option.
+/ Added long file name support.
+/ Added multiple code page support.
+/ Added re-entrancy for multitask operation.
+/ Added auto cluster size selection to f_mkfs().
+/ Added rewind option to f_readdir().
+/ Changed result code of critical errors.
+/ Renamed string functions to avoid name collision.
+/ Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg.
+/ Added multiple sector size support.
+/ Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error.
+/ Fixed wrong cache control in f_lseek().
+/ Added relative path feature.
+/ Added f_chdir() and f_chdrive().
+/ Added proper case conversion to extended char.
+/ Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h.
+/ Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH.
+/ Fixed name matching error on the 13 char boundary.
+/ Added a configuration option, _LFN_UNICODE.
+/ Changed f_readdir() to return the SFN with always upper
+/ case on non-LFN cfg.
+/
+/ May 15,'10 R0.08 Added a memory configuration option. (_USE_LFN)
+/ Added file lock feature. (_FS_SHARE)
+/ Added fast seek feature. (_USE_FASTSEEK)
+/ Changed some types on the API, XCHAR->TCHAR.
+/ Changed fname member in the FILINFO structure on Unicode cfg.
+/ String functions support UTF-8 encoding files on Unicode cfg.
+/---------------------------------------------------------------------------*/
+
+//Modified by Thomas Hamilton, Copyright 2010
+
+#include "ff.h" /* FatFs configurations and declarations */
+#include "diskio.h" /* Declarations of low level disk I/O functions */
+
+
+/*--------------------------------------------------------------------------
+
+ Module Private Definitions
+
+---------------------------------------------------------------------------*/
+
+#if _FATFS != 8085
+#error Wrong include file (ff.h).
+#endif
+
+
+/* FAT sub-type boundaries */
+/* Note that the FAT spec by Microsoft says 4085 but Windows works with 4087! */
+#define MIN_FAT16 4086 /* Minimum number of clusters for FAT16 */
+#define MIN_FAT32 65526 /* Minimum number of clusters for FAT32 */
+
+
+/* Definitions corresponds to multiple sector size */
+#if _MAX_SS == 512 /* Single sector size */
+#define SS(fs) 512U
+#elif _MAX_SS == 1024 || _MAX_SS == 2048 || _MAX_SS == 4096 /* Multiple sector size */
+#define SS(fs) ((fs)->ssize)
+#else
+#error Wrong sector size.
+#endif
+
+
+/* Reentrancy related */
+#if _FS_REENTRANT
+#if _USE_LFN == 1
+#error Static LFN work area must not be used in re-entrant configuration.
+#endif
+#define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; }
+#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; }
+
+#else
+#define ENTER_FF(fs)
+#define LEAVE_FF(fs, res) return res
+
+#endif
+
+#define ABORT(fs, res) { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); }
+
+
+/* Character code support macros */
+#define IsUpper(c) (((c)>='A')&&((c)<='Z'))
+#define IsLower(c) (((c)>='a')&&((c)<='z'))
+#define IsDigit(c) (((c)>='0')&&((c)<='9'))
+
+#if _DF1S /* Code page is DBCS */
+
+#ifdef _DF2S /* Two 1st byte areas */
+#define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E))
+#else /* One 1st byte area */
+#define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E)
+#endif
+
+#ifdef _DS3S /* Three 2nd byte areas */
+#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E))
+#else /* Two 2nd byte areas */
+#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E))
+#endif
+
+#else /* Code page is SBCS */
+
+#define IsDBCS1(c) 0
+#define IsDBCS2(c) 0
+
+#endif /* _DF1S */
+
+
+/* Name status flags */
+#define NS 11 /* Offset of name status byte */
+#define NS_LOSS 0x01 /* Out of 8.3 format */
+#define NS_LFN 0x02 /* Force to create LFN entry */
+#define NS_LAST 0x04 /* Last segment */
+#define NS_BODY 0x08 /* Lower case flag (body) */
+#define NS_EXT 0x10 /* Lower case flag (ext) */
+#define NS_DOT 0x20 /* Dot entry */
+
+
+
+/*------------------------------------------------------------*/
+/* Work area */
+
+#if !_DRIVES
+#error Number of drives must not be 0.
+#endif
+static
+WORD Fsid; /* File system mount ID */
+static
+FATFS *FatFs[_DRIVES]; /* Pointer to the file system objects (logical drives) */
+
+#if _FS_RPATH
+static
+BYTE Drive; /* Current drive */
+#endif
+
+
+#if _USE_LFN == 0 /* No LFN */
+#define DEF_NAMEBUF BYTE sfn[12]
+#define INIT_BUF(dobj) (dobj).fn = sfn
+#define FREE_BUF()
+
+#elif _USE_LFN == 1 /* LFN with static LFN working buffer */
+static WCHAR LfnBuf[_MAX_LFN + 1];
+#define DEF_NAMEBUF BYTE sfn[12]
+#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = LfnBuf; }
+#define FREE_BUF()
+
+#elif _USE_LFN == 2 /* LFN with dynamic LFN working buffer on the stack */
+#define DEF_NAMEBUF BYTE sfn[12]; WCHAR lbuf[_MAX_LFN + 1]
+#define INIT_BUF(dobj) { (dobj).fn = sfn; (dobj).lfn = lbuf; }
+#define FREE_BUF()
+
+#elif _USE_LFN == 3 /* LFN with dynamic LFN working buffer on the heap */
+#define DEF_NAMEBUF BYTE sfn[12]; WCHAR *lfn
+#define INIT_BUF(dobj) { lfn = ff_memalloc((_MAX_LFN + 1) * 2); \
+ if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); \
+ (dobj).lfn = lfn; (dobj).fn = sfn; }
+#define FREE_BUF() ff_memfree(lfn)
+
+#else
+#error Wrong LFN configuration.
+#endif
+
+
+
+
+/*--------------------------------------------------------------------------
+
+ Module Private Functions
+
+---------------------------------------------------------------------------*/
+
+
+/*-----------------------------------------------------------------------*/
+/* String functions */
+/*-----------------------------------------------------------------------*/
+
+/* Copy memory to memory */
+static
+void mem_cpy (void* dst, const void* src, int cnt) {
+ BYTE *d = (BYTE*)dst;
+ const BYTE *s = (const BYTE*)src;
+
+#if _WORD_ACCESS == 1
+ while (cnt >= sizeof(int)) {
+ *(int*)d = *(int*)s;
+ d += sizeof(int); s += sizeof(int);
+ cnt -= sizeof(int);
+ }
+#endif
+ while (cnt--)
+ *d++ = *s++;
+}
+
+/* Fill memory */
+static
+void mem_set (void* dst, int val, int cnt) {
+ BYTE *d = (BYTE*)dst;
+
+ while (cnt--)
+ *d++ = (BYTE)val;
+}
+
+/* Compare memory to memory */
+static
+int mem_cmp (const void* dst, const void* src, int cnt) {
+ const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src;
+ int r = 0;
+
+ while (cnt-- && (r = *d++ - *s++) == 0) ;
+ return r;
+}
+
+/* Check if chr is contained in the string */
+static
+int chk_chr (const char* str, int chr) {
+ while (*str && *str != chr) str++;
+ return *str;
+}
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Request/Release grant to access the volume */
+/*-----------------------------------------------------------------------*/
+#if _FS_REENTRANT
+
+static
+int lock_fs (
+ FATFS *fs /* File system object */
+)
+{
+ return ff_req_grant(fs->sobj);
+}
+
+
+static
+void unlock_fs (
+ FATFS *fs, /* File system object */
+ FRESULT res /* Result code to be returned */
+)
+{
+ if (res != FR_NOT_ENABLED &&
+ res != FR_INVALID_DRIVE &&
+ res != FR_INVALID_OBJECT &&
+ res != FR_TIMEOUT) {
+ ff_rel_grant(fs->sobj);
+ }
+}
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* File shareing control functions */
+/*-----------------------------------------------------------------------*/
+#if _FS_SHARE
+
+static
+FRESULT chk_lock ( /* Check if the file can be accessed */
+ FAT_DIR* dj, /* Directory object pointing the file to be checked */
+ int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
+)
+{
+ UINT i, be;
+
+ /* Search file semaphore table */
+ for (i = be = 0; i < _FS_SHARE; i++) {
+ if (dj->fs->flsem[i].ctr) { /* Existing entry */
+ if (dj->fs->flsem[i].clu == dj->sclust && /* The file is found (identified with its location) */
+ dj->fs->flsem[i].idx == dj->index) break;
+ } else { /* Blank entry */
+ be++;
+ }
+ }
+ if (i == _FS_SHARE) /* The file has not been opened */
+ return (be || acc != 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new file? */
+
+ /* The file has been opened. Reject any open against writing file and all write mode open */
+ return (acc || dj->fs->flsem[i].ctr == 0x100) ? FR_LOCKED : FR_OK;
+}
+
+
+static
+int enq_lock ( /* Check if an entry is available for a new file */
+ FATFS* fs /* File system object */
+)
+{
+ UINT i;
+
+ for (i = 0; i < _FS_SHARE && fs->flsem[i].ctr; i++) ;
+ return (i == _FS_SHARE) ? 0 : 1;
+}
+
+
+static
+UINT inc_lock ( /* Increment file open counter and returns its index (0:int error) */
+ FAT_DIR* dj, /* Directory object pointing the file to register or increment */
+ int acc /* Desired access mode (0:Read, !0:Write) */
+)
+{
+ UINT i;
+
+
+ for (i = 0; i < _FS_SHARE; i++) { /* Find the file */
+ if (dj->fs->flsem[i].ctr &&
+ dj->fs->flsem[i].clu == dj->sclust &&
+ dj->fs->flsem[i].idx == dj->index) break;
+ }
+
+ if (i == _FS_SHARE) { /* Not opened. Register it as new. */
+ for (i = 0; i < _FS_SHARE && dj->fs->flsem[i].ctr; i++) ;
+ if (i == _FS_SHARE) return 0; /* No space to register (int err) */
+ dj->fs->flsem[i].clu = dj->sclust;
+ dj->fs->flsem[i].idx = dj->index;
+ }
+
+ if (acc && dj->fs->flsem[i].ctr) return 0; /* Access violation (int err) */
+
+ dj->fs->flsem[i].ctr = acc ? 0x100 : dj->fs->flsem[i].ctr + 1; /* Set semaphore value */
+
+ return i + 1;
+}
+
+
+static
+FRESULT dec_lock ( /* Decrement file open counter */
+ FATFS* fs, /* File system object */
+ UINT i /* Semaphore index */
+)
+{
+ WORD n;
+ FRESULT res;
+
+
+ if (--i < _FS_SHARE) {
+ n = fs->flsem[i].ctr;
+ if (n >= 0x100) n = 0;
+ if (n) n--;
+ fs->flsem[i].ctr = n;
+ res = FR_OK;
+ } else {
+ res = FR_INT_ERR;
+ }
+ return res;
+}
+
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change window offset */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT move_window (
+ FATFS *fs, /* File system object */
+ DWORD sector /* Sector number to make appearance 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
+ if (fs->wflag) { /* Write back dirty window if needed */
+ if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK)
+ return FR_DISK_ERR;
+ fs->wflag = 0;
+ if (wsect < (fs->fatbase + fs->fsize)) { /* In FAT area */
+ BYTE nf;
+ for (nf = fs->n_fats; nf > 1; nf--) { /* Reflect the change to all FAT copies */
+ wsect += fs->fsize;
+ disk_write(fs->drv, fs->win, wsect, 1);
+ }
+ }
+ }
+#endif
+ if (sector) {
+ if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK)
+ return FR_DISK_ERR;
+ fs->winsect = sector;
+ }
+ }
+
+ return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Clean-up cached data */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT sync ( /* FR_OK: successful, FR_DISK_ERR: failed */
+ FATFS *fs /* File system object */
+)
+{
+ FRESULT res;
+
+
+ res = move_window(fs, 0);
+ if (res == FR_OK) {
+ /* Update FSInfo sector if needed */
+ if (fs->fs_type == FS_FAT32 && fs->fsi_flag) {
+ fs->winsect = 0;
+ mem_set(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->drv, fs->win, fs->fsi_sector, 1);
+ fs->fsi_flag = 0;
+ }
+ /* Make sure that no pending write process in the physical drive */
+ if (disk_ioctl(fs->drv, CTRL_SYNC, (void*)0) != RES_OK)
+ res = FR_DISK_ERR;
+ }
+
+ return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Read value of a FAT entry */
+/*-----------------------------------------------------------------------*/
+
+
+DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */
+ FATFS *fs, /* File system object */
+ DWORD clst /* Cluster# to get the link information */
+)
+{
+ UINT wc, bc;
+ BYTE *p;
+
+
+ if (clst < 2 || clst >= fs->n_fatent) /* Chack range */
+ return 1;
+
+ switch (fs->fs_type) {
+ case FS_FAT12 :
+ bc = (UINT)clst; bc += bc / 2;
+ if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break;
+ wc = fs->win[bc % SS(fs)]; bc++;
+ if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break;
+ wc |= fs->win[bc % SS(fs)] << 8;
+ return (clst & 1) ? (wc >> 4) : (wc & 0xFFF);
+
+ case FS_FAT16 :
+ if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)))) break;
+ p = &fs->win[clst * 2 % SS(fs)];
+ return LD_WORD(p);
+
+ case FS_FAT32 :
+ if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)))) break;
+ p = &fs->win[clst * 4 % SS(fs)];
+ return LD_DWORD(p) & 0x0FFFFFFF;
+ }
+
+ return 0xFFFFFFFF; /* An error occurred at the disk I/O layer */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Change value of a FAT entry */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+
+FRESULT put_fat (
+ FATFS *fs, /* File system object */
+ DWORD clst, /* Cluster# to be changed in range of 2 to fs->n_fatent - 1 */
+ DWORD val /* New value to mark the cluster */
+)
+{
+ UINT bc;
+ BYTE *p;
+ FRESULT res;
+
+
+ if (clst < 2 || clst >= fs->n_fatent) { /* Check range */
+ res = FR_INT_ERR;
+
+ } else {
+ switch (fs->fs_type) {
+ case FS_FAT12 :
+ bc = clst; bc += bc / 2;
+ res = move_window(fs, fs->fatbase + (bc / SS(fs)));
+ if (res != FR_OK) break;
+ p = &fs->win[bc % SS(fs)];
+ *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;
+ bc++;
+ fs->wflag = 1;
+ res = move_window(fs, fs->fatbase + (bc / SS(fs)));
+ if (res != FR_OK) break;
+ p = &fs->win[bc % SS(fs)];
+ *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));
+ break;
+
+ case FS_FAT16 :
+ res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));
+ if (res != FR_OK) break;
+ p = &fs->win[clst * 2 % SS(fs)];
+ ST_WORD(p, (WORD)val);
+ break;
+
+ case FS_FAT32 :
+ res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));
+ if (res != FR_OK) break;
+ p = &fs->win[clst * 4 % SS(fs)];
+ val |= LD_DWORD(p) & 0xF0000000;
+ ST_DWORD(p, val);
+ break;
+
+ default :
+ res = FR_INT_ERR;
+ }
+ fs->wflag = 1;
+ }
+
+ return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Remove a cluster chain */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT remove_chain (
+ FATFS *fs, /* File system object */
+ DWORD clst /* Cluster# to remove a chain from */
+)
+{
+ FRESULT res;
+ DWORD nxt;
+
+
+ if (clst < 2 || clst >= fs->n_fatent) { /* Check range */
+ res = FR_INT_ERR;
+
+ } else {
+ res = FR_OK;
+ while (clst < fs->n_fatent) { /* Not a last link? */
+ nxt = get_fat(fs, clst); /* Get cluster status */
+ if (nxt == 0) break; /* Empty cluster? */
+ if (nxt == 1) { res = FR_INT_ERR; break; } /* Internal error? */
+ if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } /* Disk error? */
+ res = put_fat(fs, clst, 0); /* Mark the cluster "empty" */
+ if (res != FR_OK) break;
+ if (fs->free_clust != 0xFFFFFFFF) { /* Update FSInfo */
+ fs->free_clust++;
+ fs->fsi_flag = 1;
+ }
+ clst = nxt; /* Next cluster */
+ }
+ }
+
+ return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Stretch or Create a cluster chain */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
+ FATFS *fs, /* File system object */
+ DWORD clst /* Cluster# to stretch. 0 means create a new chain. */
+)
+{
+ DWORD cs, ncl, scl;
+
+
+ if (clst == 0) { /* Create a new chain */
+ scl = fs->last_clust; /* Get suggested start point */
+ if (!scl || scl >= fs->n_fatent) scl = 1;
+ }
+ else { /* Stretch the current chain */
+ cs = get_fat(fs, clst); /* Check the cluster status */
+ if (cs < 2) return 1; /* It is an invalid cluster */
+ if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */
+ scl = clst;
+ }
+
+ ncl = scl; /* Start cluster */
+ for (;;) {
+ ncl++; /* Next cluster */
+ if (ncl >= fs->n_fatent) { /* Wrap around */
+ ncl = 2;
+ if (ncl > scl) return 0; /* No free cluster */
+ }
+ cs = get_fat(fs, ncl); /* Get the cluster status */
+ if (cs == 0) break; /* Found a free cluster */
+ if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */
+ return cs;
+ if (ncl == scl) return 0; /* No free cluster */
+ }
+
+ if (put_fat(fs, ncl, 0x0FFFFFFF)) /* Mark the new cluster "last link" */
+ return 0xFFFFFFFF;
+ if (clst != 0) { /* Link it to the previous one if needed */
+ if (put_fat(fs, clst, ncl))
+ return 0xFFFFFFFF;
+ }
+
+ fs->last_clust = ncl; /* Update FSINFO */
+ if (fs->free_clust != 0xFFFFFFFF) {
+ fs->free_clust--;
+ fs->fsi_flag = 1;
+ }
+
+ return ncl; /* Return new cluster number */
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get sector# from cluster# */
+/*-----------------------------------------------------------------------*/
+
+
+DWORD clust2sect ( /* !=0: Sector number, 0: Failed - invalid cluster# */
+ FATFS *fs, /* File system object */
+ DWORD clst /* Cluster# to be converted */
+)
+{
+ clst -= 2;
+ if (clst >= (fs->n_fatent - 2)) return 0; /* Invalid cluster# */
+ return clst * fs->csize + fs->database;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Set directory index */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_sdi (
+ FAT_DIR *dj, /* Pointer to directory object */
+ WORD idx /* Directory index number */
+)
+{
+ DWORD clst;
+ WORD ic;
+
+
+ dj->index = idx;
+ clst = dj->sclust;
+ if (clst == 1 || clst >= dj->fs->n_fatent) /* Check start cluster range */
+ return FR_INT_ERR;
+ if (!clst && dj->fs->fs_type == FS_FAT32) /* Replace cluster# 0 with root cluster# if in FAT32 */
+ clst = dj->fs->dirbase;
+
+ if (clst == 0) { /* Static table */
+ dj->clust = clst;
+ if (idx >= dj->fs->n_rootdir) /* Index is out of range */
+ return FR_INT_ERR;
+ dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / 32); /* Sector# */
+ }
+ else { /* Dynamic table */
+ ic = SS(dj->fs) / 32 * dj->fs->csize; /* Entries per cluster */
+ while (idx >= ic) { /* Follow cluster chain */
+ clst = get_fat(dj->fs, clst); /* Get next cluster */
+ if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */
+ if (clst < 2 || clst >= dj->fs->n_fatent) /* Reached to end of table or int error */
+ return FR_INT_ERR;
+ idx -= ic;
+ }
+ dj->clust = clst;
+ dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / 32); /* Sector# */
+ }
+
+ dj->dir = dj->fs->win + (idx % (SS(dj->fs) / 32)) * 32; /* Ptr to the entry in the sector */
+
+ return FR_OK; /* Seek succeeded */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Move directory index next */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_next ( /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not stretch */
+ FAT_DIR *dj, /* Pointer to directory object */
+ int stretch /* 0: Do not stretch table, 1: Stretch table if needed */
+)
+{
+ DWORD clst;
+ WORD i;
+
+
+ i = dj->index + 1;
+ if (!i || !dj->sect) /* Report EOT when index has reached 65535 */
+ return FR_NO_FILE;
+
+ if (!(i % (SS(dj->fs) / 32))) { /* Sector changed? */
+ dj->sect++; /* Next sector */
+
+ if (dj->clust == 0) { /* Static table */
+ if (i >= dj->fs->n_rootdir) /* Report EOT when end of table */
+ return FR_NO_FILE;
+ }
+ else { /* Dynamic table */
+ if (((i / (SS(dj->fs) / 32)) & (dj->fs->csize - 1)) == 0) { /* Cluster changed? */
+ clst = get_fat(dj->fs, dj->clust); /* Get next cluster */
+ if (clst <= 1) return FR_INT_ERR;
+ if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+ if (clst >= dj->fs->n_fatent) { /* When it reached end of dynamic table */
+#if !_FS_READONLY
+ BYTE c;
+ if (!stretch) return FR_NO_FILE; /* When do not stretch, report EOT */
+ clst = create_chain(dj->fs, dj->clust); /* Stretch cluster chain */
+ if (clst == 0) return FR_DENIED; /* No free cluster */
+ if (clst == 1) return FR_INT_ERR;
+ if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+ /* Clean-up stretched table */
+ if (move_window(dj->fs, 0)) return FR_DISK_ERR; /* Flush active window */
+ mem_set(dj->fs->win, 0, SS(dj->fs)); /* Clear window buffer */
+ dj->fs->winsect = clust2sect(dj->fs, clst); /* Cluster start sector */
+ for (c = 0; c < dj->fs->csize; c++) { /* Fill the new cluster with 0 */
+ dj->fs->wflag = 1;
+ if (move_window(dj->fs, 0)) return FR_DISK_ERR;
+ dj->fs->winsect++;
+ }
+ dj->fs->winsect -= c; /* Rewind window address */
+#else
+ return FR_NO_FILE; /* Report EOT */
+#endif
+ }
+ dj->clust = clst; /* Initialize data for new cluster */
+ dj->sect = clust2sect(dj->fs, clst);
+ }
+ }
+ }
+
+ dj->index = i;
+ dj->dir = dj->fs->win + (i % (SS(dj->fs) / 32)) * 32;
+
+ return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN chars in the directory entry */
+
+
+static
+int cmp_lfn ( /* 1:Matched, 0:Not matched */
+ WCHAR *lfnbuf, /* Pointer to the LFN to be compared */
+ BYTE *dir /* Pointer to the directory entry containing a part of LFN */
+)
+{
+ int i, s;
+ WCHAR wc, uc;
+
+
+ i = ((dir[LDIR_Ord] & 0xBF) - 1) * 13; /* Get offset in the LFN buffer */
+ s = 0; wc = 1;
+ do {
+ uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */
+ if (wc) { /* Last char has not been processed */
+ wc = ff_wtoupper(uc); /* Convert it to upper case */
+ if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++])) /* Compare it */
+ return 0; /* Not matched */
+ } else {
+ if (uc != 0xFFFF) return 0; /* Check filler */
+ }
+ } while (++s < 13); /* Repeat until all chars in the entry are checked */
+
+ if ((dir[LDIR_Ord] & 0x40) && wc && lfnbuf[i]) /* Last segment matched but different length */
+ return 0;
+
+ return 1; /* The part of LFN matched */
+}
+
+
+
+static
+int pick_lfn ( /* 1:Succeeded, 0:Buffer overflow */
+ WCHAR *lfnbuf, /* Pointer to the Unicode-LFN buffer */
+ BYTE *dir /* Pointer to the directory entry */
+)
+{
+ int i, s;
+ WCHAR wc, uc;
+
+
+ i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */
+
+ s = 0; wc = 1;
+ do {
+ uc = LD_WORD(dir+LfnOfs[s]); /* Pick an LFN character from the entry */
+ if (wc) { /* Last char has not been processed */
+ if (i >= _MAX_LFN) return 0; /* Buffer overflow? */
+ lfnbuf[i++] = wc = uc; /* Store it */
+ } else {
+ if (uc != 0xFFFF) return 0; /* Check filler */
+ }
+ } while (++s < 13); /* Read all character in the entry */
+
+ if (dir[LDIR_Ord] & 0x40) { /* Put terminator if it is the last LFN part */
+ if (i >= _MAX_LFN) return 0; /* Buffer overflow? */
+ lfnbuf[i] = 0;
+ }
+
+ return 1;
+}
+
+
+#if !_FS_READONLY
+static
+void fit_lfn (
+ const WCHAR *lfnbuf, /* Pointer to the LFN buffer */
+ BYTE *dir, /* Pointer to the directory entry */
+ BYTE ord, /* LFN order (1-20) */
+ BYTE sum /* SFN sum */
+)
+{
+ int i, s;
+ WCHAR wc;
+
+
+ dir[LDIR_Chksum] = sum; /* Set check sum */
+ dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */
+ dir[LDIR_Type] = 0;
+ ST_WORD(dir+LDIR_FstClusLO, 0);
+
+ i = (ord - 1) * 13; /* Get offset in the LFN buffer */
+ s = wc = 0;
+ do {
+ if (wc != 0xFFFF) wc = lfnbuf[i++]; /* Get an effective char */
+ ST_WORD(dir+LfnOfs[s], wc); /* Put it */
+ if (!wc) wc = 0xFFFF; /* Padding chars following last char */
+ } while (++s < 13);
+ if (wc == 0xFFFF || !lfnbuf[i]) ord |= 0x40; /* Bottom LFN part is the start of LFN sequence */
+ dir[LDIR_Ord] = ord; /* Set the LFN order */
+}
+
+#endif
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create numbered name */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+void gen_numname (
+ BYTE *dst, /* Pointer to generated SFN */
+ const BYTE *src, /* Pointer to source SFN to be modified */
+ const WCHAR *lfn, /* Pointer to LFN */
+ WORD seq /* Sequence number */
+)
+{
+ BYTE ns[8], c;
+ int i, j;
+
+
+ mem_cpy(dst, src, 11);
+
+ if (seq > 5) { /* On many collisions, generate a hash number instead of sequential number */
+ do seq = (seq >> 1) + (seq << 15) + (WORD)*lfn++; while (*lfn);
+ }
+
+ /* itoa */
+ i = 7;
+ do {
+ c = (seq % 16) + '0';
+ if (c > '9') c += 7;
+ ns[i--] = c;
+ seq /= 16;
+ } while (seq);
+ ns[i] = '~';
+
+ /* Append the number */
+ for (j = 0; j < i && dst[j] != ' '; j++) {
+ if (IsDBCS1(dst[j])) {
+ if (j == i - 1) break;
+ j++;
+ }
+ }
+ do {
+ dst[j++] = (i < 8) ? ns[i++] : ' ';
+ } while (j < 8);
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Calculate sum of an SFN */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+BYTE sum_sfn (
+ const BYTE *dir /* Ptr to directory entry */
+)
+{
+ BYTE sum = 0;
+ int n = 11;
+
+ do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n);
+ return sum;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Find an object in the directory */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_find (
+ FAT_DIR *dj /* Pointer to the directory object linked to the file name */
+)
+{
+ FRESULT res;
+ BYTE c, *dir;
+#if _USE_LFN
+ BYTE a, ord, sum;
+#endif
+
+ res = dir_sdi(dj, 0); /* Rewind directory object */
+ if (res != FR_OK) return res;
+
+#if _USE_LFN
+ ord = sum = 0xFF;
+#endif
+ do {
+ res = move_window(dj->fs, dj->sect);
+ if (res != FR_OK) break;
+ dir = dj->dir; /* Ptr to the directory entry of current index */
+ c = dir[DIR_Name];
+ if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */
+#if _USE_LFN /* LFN configuration */
+ a = dir[DIR_Attr] & AM_MASK;
+ if (c == 0xE5 || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */
+ ord = 0xFF;
+ } else {
+ if (a == AM_LFN) { /* An LFN entry is found */
+ if (dj->lfn) {
+ if (c & 0x40) { /* Is it start of LFN sequence? */
+ sum = dir[LDIR_Chksum];
+ c &= 0xBF; ord = c; /* LFN start order */
+ dj->lfn_idx = dj->index;
+ }
+ /* Check validity of the LFN entry and compare it with given name */
+ ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
+ }
+ } else { /* An SFN entry is found */
+ if (!ord && sum == sum_sfn(dir)) break; /* LFN matched? */
+ ord = 0xFF; dj->lfn_idx = 0xFFFF; /* Reset LFN sequence */
+ if (!(dj->fn[NS] & NS_LOSS) && !mem_cmp(dir, dj->fn, 11)) break; /* SFN matched? */
+ }
+ }
+#else /* Non LFN configuration */
+ if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */
+ break;
+#endif
+ res = dir_next(dj, 0); /* Next entry */
+ } while (res == FR_OK);
+
+ return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read an object from the directory */
+/*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 1
+static
+FRESULT dir_read (
+ FAT_DIR *dj /* Pointer to the directory object that pointing the entry to be read */
+)
+{
+ FRESULT res;
+ BYTE c, *dir;
+#if _USE_LFN
+ BYTE a, ord = 0xFF, sum = 0xFF;
+#endif
+
+ res = FR_NO_FILE;
+ while (dj->sect) {
+ res = move_window(dj->fs, dj->sect);
+ if (res != FR_OK) break;
+ dir = dj->dir; /* Ptr to the directory entry of current index */
+ c = dir[DIR_Name];
+ if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */
+#if _USE_LFN /* LFN configuration */
+ a = dir[DIR_Attr] & AM_MASK;
+ if (c == 0xE5 || (!_FS_RPATH && c == '.') || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */
+ ord = 0xFF;
+ } else {
+ if (a == AM_LFN) { /* An LFN entry is found */
+ if (c & 0x40) { /* Is it start of LFN sequence? */
+ sum = dir[LDIR_Chksum];
+ c &= 0xBF; ord = c;
+ dj->lfn_idx = dj->index;
+ }
+ /* Check LFN validity and capture it */
+ ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
+ } else { /* An SFN entry is found */
+ if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN? */
+ dj->lfn_idx = 0xFFFF; /* It has no LFN. */
+ break;
+ }
+ }
+#else /* Non LFN configuration */
+ if (c != 0xE5 && (_FS_RPATH || c != '.') && !(dir[DIR_Attr] & AM_VOL)) /* Is it a valid entry? */
+ break;
+#endif
+ res = dir_next(dj, 0); /* Next entry */
+ if (res != FR_OK) break;
+ }
+
+ if (res != FR_OK) dj->sect = 0;
+
+ return res;
+}
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Register an object to the directory */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT dir_register ( /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */
+ FAT_DIR *dj /* Target directory with object name to be created */
+)
+{
+ FRESULT res;
+ BYTE c, *dir;
+#if _USE_LFN /* LFN configuration */
+ WORD n, ne, is;
+ BYTE sn[12], *fn, sum;
+ WCHAR *lfn;
+
+
+ fn = dj->fn; lfn = dj->lfn;
+ mem_cpy(sn, fn, 12);
+
+ if (_FS_RPATH && (sn[NS] & NS_DOT)) return FR_INVALID_NAME; /* Cannot create dot entry */
+
+ if (sn[NS] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */
+ fn[NS] = 0; dj->lfn = 0; /* Find only SFN */
+ for (n = 1; n < 100; n++) {
+ gen_numname(fn, sn, lfn, n); /* Generate a numbered name */
+ res = dir_find(dj); /* Check if the name collides with existing SFN */
+ if (res != FR_OK) break;
+ }
+ if (n == 100) return FR_DENIED; /* Abort if too many collisions */
+ if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */
+ fn[NS] = sn[NS]; dj->lfn = lfn;
+ }
+
+ if (sn[NS] & NS_LFN) { /* When LFN is to be created, reserve an SFN + LFN entries. */
+ for (ne = 0; lfn[ne]; ne++) ;
+ ne = (ne + 25) / 13;
+ } else { /* Otherwise reserve only an SFN entry. */
+ ne = 1;
+ }
+
+ /* Reserve contiguous entries */
+ res = dir_sdi(dj, 0);
+ if (res != FR_OK) return res;
+ n = is = 0;
+ do {
+ res = move_window(dj->fs, dj->sect);
+ if (res != FR_OK) break;
+ c = *dj->dir; /* Check the entry status */
+ if (c == 0xE5 || c == 0) { /* Is it a blank entry? */
+ if (n == 0) is = dj->index; /* First index of the contiguous entry */
+ if (++n == ne) break; /* A contiguous entry that required count is found */
+ } else {
+ n = 0; /* Not a blank entry. Restart to search */
+ }
+ res = dir_next(dj, 1); /* Next entry with table stretch */
+ } while (res == FR_OK);
+
+ if (res == FR_OK && ne > 1) { /* Initialize LFN entry if needed */
+ res = dir_sdi(dj, is);
+ if (res == FR_OK) {
+ sum = sum_sfn(dj->fn); /* Sum of the SFN tied to the LFN */
+ ne--;
+ do { /* Store LFN entries in bottom first */
+ res = move_window(dj->fs, dj->sect);
+ if (res != FR_OK) break;
+ fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum);
+ dj->fs->wflag = 1;
+ res = dir_next(dj, 0); /* Next entry */
+ } while (res == FR_OK && --ne);
+ }
+ }
+
+#else /* Non LFN configuration */
+ res = dir_sdi(dj, 0);
+ if (res == FR_OK) {
+ do { /* Find a blank entry for the SFN */
+ res = move_window(dj->fs, dj->sect);
+ if (res != FR_OK) break;
+ c = *dj->dir;
+ if (c == 0xE5 || c == 0) break; /* Is it a blank entry? */
+ res = dir_next(dj, 1); /* Next entry with table stretch */
+ } while (res == FR_OK);
+ }
+#endif
+
+ if (res == FR_OK) { /* Initialize the SFN entry */
+ res = move_window(dj->fs, dj->sect);
+ if (res == FR_OK) {
+ dir = dj->dir;
+ mem_set(dir, 0, 32); /* Clean the entry */
+ mem_cpy(dir, dj->fn, 11); /* Put SFN */
+#if _USE_LFN
+ dir[DIR_NTres] = *(dj->fn+NS) & (NS_BODY | NS_EXT); /* Put NT flag */
+#endif
+ dj->fs->wflag = 1;
+ }
+ }
+
+ return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Remove an object from the directory */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY && !_FS_MINIMIZE
+static
+FRESULT dir_remove ( /* FR_OK: Successful, FR_DISK_ERR: A disk error */
+ FAT_DIR *dj /* Directory object pointing the entry to be removed */
+)
+{
+ FRESULT res;
+#if _USE_LFN /* LFN configuration */
+ WORD i;
+
+ i = dj->index; /* SFN index */
+ res = dir_sdi(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx)); /* Goto the SFN or top of the LFN entries */
+ if (res == FR_OK) {
+ do {
+ res = move_window(dj->fs, dj->sect);
+ if (res != FR_OK) break;
+ *dj->dir = 0xE5; /* Mark the entry "deleted" */
+ dj->fs->wflag = 1;
+ if (dj->index >= i) break; /* When reached SFN, all entries of the object has been deleted. */
+ res = dir_next(dj, 0); /* Next entry */
+ } while (res == FR_OK);
+ if (res == FR_NO_FILE) res = FR_INT_ERR;
+ }
+
+#else /* Non LFN configuration */
+ res = dir_sdi(dj, dj->index);
+ if (res == FR_OK) {
+ res = move_window(dj->fs, dj->sect);
+ if (res == FR_OK) {
+ *dj->dir = 0xE5; /* Mark the entry "deleted" */
+ dj->fs->wflag = 1;
+ }
+ }
+#endif
+
+ return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Pick a segment and create the object name in directory form */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT create_name (
+ FAT_DIR *dj, /* Pointer to the directory object */
+ const TCHAR **path /* Pointer to pointer to the segment in the path string */
+)
+{
+#ifdef _EXCVT
+ static const BYTE excvt[] = _EXCVT; /* Upper conversion table for extended chars */
+#endif
+
+#if _USE_LFN /* LFN configuration */
+ BYTE b, cf;
+ WCHAR w, *lfn;
+ int i, ni, si, di;
+ const TCHAR *p;
+
+ /* Create LFN in Unicode */
+ si = di = 0;
+ p = *path;
+ lfn = dj->lfn;
+ for (;;) {
+ w = p[si++]; /* Get a character */
+ if (w < ' ' || w == '/' || w == '\\') break; /* Break on end of segment */
+ if (di >= _MAX_LFN) /* Reject too long name */
+ return FR_INVALID_NAME;
+#if !_LFN_UNICODE
+ w &= 0xFF;
+ if (IsDBCS1(w)) { /* If it is a DBC 1st byte */
+ b = p[si++]; /* Get 2nd byte */
+ if (!IsDBCS2(b)) /* Reject invalid code for DBC */
+ return FR_INVALID_NAME;
+ w = (w << 8) + b;
+ }
+ w = ff_convert(w, 1); /* Convert OEM to Unicode */
+ if (!w) return FR_INVALID_NAME; /* Reject invalid code */
+#endif
+ if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal chars for LFN */
+ return FR_INVALID_NAME;
+ lfn[di++] = w; /* Store the Unicode char */
+ }
+ *path = &p[si]; /* Return pointer to the next segment */
+ cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */
+#if _FS_RPATH
+ if ((di == 1 && lfn[di - 1] == '.') || /* Is this a dot entry? */
+ (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) {
+ lfn[di] = 0;
+ for (i = 0; i < 11; i++)
+ dj->fn[i] = (i < di) ? '.' : ' ';
+ dj->fn[i] = cf | NS_DOT; /* This is a dot entry */
+ return FR_OK;
+ }
+#endif
+ while (di) { /* Strip trailing spaces and dots */
+ w = lfn[di - 1];
+ if (w != ' ' && w != '.') break;
+ di--;
+ }
+ if (!di) return FR_INVALID_NAME; /* Reject nul string */
+
+ lfn[di] = 0; /* LFN is created */
+
+ /* Create SFN in directory form */
+ mem_set(dj->fn, ' ', 11);
+ for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */
+ if (si) cf |= NS_LOSS | NS_LFN;
+ while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */
+
+ b = i = 0; ni = 8;
+ for (;;) {
+ w = lfn[si++]; /* Get an LFN char */
+ if (!w) break; /* Break on end of the LFN */
+ if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */
+ cf |= NS_LOSS | NS_LFN; continue;
+ }
+
+ if (i >= ni || si == di) { /* Extension or end of SFN */
+ if (ni == 11) { /* Long extension */
+ cf |= NS_LOSS | NS_LFN; break;
+ }
+ if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */
+ if (si > di) break; /* No extension */
+ si = di; i = 8; ni = 11; /* Enter extension section */
+ b <<= 2; continue;
+ }
+
+ if (w >= 0x80) { /* Non ASCII char */
+#ifdef _EXCVT
+ w = ff_convert(w, 0); /* Unicode -> OEM code */
+ if (w) w = excvt[w - 0x80]; /* Convert extended char to upper (SBCS) */
+#else
+ w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */
+#endif
+ cf |= NS_LFN; /* Force create LFN entry */
+ }
+
+ if (_DF1S && w >= 0x100) { /* Double byte char */
+ if (i >= ni - 1) {
+ cf |= NS_LOSS | NS_LFN; i = ni; continue;
+ }
+ dj->fn[i++] = (BYTE)(w >> 8);
+ } else { /* Single byte char */
+ if (!w || chk_chr("+,;=[]", w)) { /* Replace illegal chars for SFN */
+ w = '_'; cf |= NS_LOSS | NS_LFN; /* Lossy conversion */
+ } else {
+ if (IsUpper(w)) { /* ASCII large capital */
+ b |= 2;
+ } else {
+ if (IsLower(w)) { /* ASCII small capital */
+ b |= 1; w -= 0x20;
+ }
+ }
+ }
+ }
+ dj->fn[i++] = (BYTE)w;
+ }
+
+ if (dj->fn[0] == 0xE5) dj->fn[0] = 0x05; /* If the first char collides with deleted mark, replace it with 0x05 */
+
+ if (ni == 8) b <<= 2;
+ if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) /* Create LFN entry when there are composite capitals */
+ cf |= NS_LFN;
+ if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended char, NT flags are created */
+ if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */
+ if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */
+ }
+
+ dj->fn[NS] = cf; /* SFN is created */
+
+ return FR_OK;
+
+
+#else /* Non-LFN configuration */
+ BYTE b, c, d, *sfn;
+ int ni, si, i;
+ const char *p;
+
+ /* Create file name in directory form */
+ sfn = dj->fn;
+ mem_set(sfn, ' ', 11);
+ si = i = b = 0; ni = 8;
+ p = *path;
+#if _FS_RPATH
+ if (p[si] == '.') { /* Is this a dot entry? */
+ for (;;) {
+ c = (BYTE)p[si++];
+ if (c != '.' || si >= 3) break;
+ sfn[i++] = c;
+ }
+ if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME;
+ *path = &p[si]; /* Return pointer to the next segment */
+ sfn[NS] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of path */
+ return FR_OK;
+ }
+#endif
+ for (;;) {
+ c = (BYTE)p[si++];
+ if (c <= ' ' || c == '/' || c == '\\') break; /* Break on end of segment */
+ if (c == '.' || i >= ni) {
+ if (ni != 8 || c != '.') return FR_INVALID_NAME;
+ i = 8; ni = 11;
+ b <<= 2; continue;
+ }
+ if (c >= 0x80) { /* Extended char */
+#ifdef _EXCVT
+ c = excvt[c - 0x80]; /* Convert extend char (SBCS) */
+#else
+ b |= 3; /* Eliminate NT flag if extended char is exist */
+#if !_DF1S /* ASCII only cfg */
+ return FR_INVALID_NAME;
+#endif
+#endif
+ }
+ if (IsDBCS1(c)) { /* DBC 1st byte? */
+ d = (BYTE)p[si++]; /* Get 2nd byte */
+ if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */
+ return FR_INVALID_NAME;
+ sfn[i++] = c;
+ sfn[i++] = d;
+ } else { /* Single byte code */
+ if (chk_chr("\"*+,:<=>\?[]|\x7F", c)) /* Reject illegal chrs for SFN */
+ return FR_INVALID_NAME;
+ if (IsUpper(c)) { /* ASCII large capital? */
+ b |= 2;
+ } else {
+ if (IsLower(c)) { /* ASCII small capital? */
+ b |= 1; c -= 0x20;
+ }
+ }
+ sfn[i++] = c;
+ }
+ }
+ *path = &p[si]; /* Return pointer to the next segment */
+ c = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of path */
+
+ if (!i) return FR_INVALID_NAME; /* Reject nul string */
+ if (sfn[0] == 0xE5) sfn[0] = 0x05; /* When first char collides with 0xE5, replace it with 0x05 */
+
+ if (ni == 8) b <<= 2;
+ if ((b & 0x03) == 0x01) c |= NS_EXT; /* NT flag (Name extension has only small capital) */
+ if ((b & 0x0C) == 0x04) c |= NS_BODY; /* NT flag (Name body has only small capital) */
+
+ sfn[NS] = c; /* Store NT flag, File name is created */
+
+ return FR_OK;
+#endif
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get file information from directory entry */
+/*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 1
+static
+void get_fileinfo ( /* No return code */
+ FAT_DIR *dj, /* Pointer to the directory object */
+ FILINFO *fno /* Pointer to the file information to be filled */
+)
+{
+ int i;
+ BYTE nt, *dir;
+ TCHAR *p, c;
+
+
+ p = fno->fname;
+ if (dj->sect) {
+ dir = dj->dir;
+ nt = dir[DIR_NTres]; /* NT flag */
+ for (i = 0; i < 8; i++) { /* Copy name body */
+ c = dir[i];
+ if (c == ' ') break;
+ if (c == 0x05) c = (TCHAR)0xE5;
+ if (_USE_LFN && (nt & NS_BODY) && IsUpper(c)) c += 0x20;
+#if _LFN_UNICODE
+ if (IsDBCS1(c) && i < 7 && IsDBCS2(dir[i + 1]))
+ c = (c << 8) | dir[++i];
+ c = ff_convert(c, 1);
+ if (!c) c = '?';
+#endif
+ *p++ = c;
+ }
+ if (dir[8] != ' ') { /* Copy name extension */
+ *p++ = '.';
+ for (i = 8; i < 11; i++) {
+ c = dir[i];
+ if (c == ' ') break;
+ if (_USE_LFN && (nt & NS_EXT) && IsUpper(c)) c += 0x20;
+#if _LFN_UNICODE
+ if (IsDBCS1(c) && i < 10 && IsDBCS2(dir[i + 1]))
+ c = (c << 8) | dir[++i];
+ c = ff_convert(c, 1);
+ if (!c) c = '?';
+#endif
+ *p++ = c;
+ }
+ }
+ fno->fattrib = dir[DIR_Attr]; /* Attribute */
+ fno->fsize = LD_DWORD(dir+DIR_FileSize); /* Size */
+ fno->fdate = LD_WORD(dir+DIR_WrtDate); /* Date */
+ fno->ftime = LD_WORD(dir+DIR_WrtTime); /* Time */
+ }
+ *p = 0;
+
+#if _USE_LFN
+ if (fno->lfname) {
+ TCHAR *tp = fno->lfname;
+ WCHAR w, *lfn;
+
+ i = 0;
+ if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */
+ lfn = dj->lfn;
+ while ((w = *lfn++) != 0) { /* Get an LFN char */
+#if !_LFN_UNICODE
+ w = ff_convert(w, 0); /* Unicode -> OEM conversion */
+ if (!w) { i = 0; break; } /* Could not convert, no LFN */
+ if (_DF1S && w >= 0x100) /* Put 1st byte if it is a DBC */
+ tp[i++] = (TCHAR)(w >> 8);
+#endif
+ if (i >= fno->lfsize - 1) { i = 0; break; } /* Buffer overrun, no LFN */
+ tp[i++] = (TCHAR)w;
+ }
+ }
+ tp[i] = 0; /* Terminator */
+ }
+#endif
+}
+#endif /* _FS_MINIMIZE <= 1 */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Follow a file path */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */
+ FAT_DIR *dj, /* Directory object to return last directory and found object */
+ const TCHAR *path /* Full-path string to find a file or directory */
+)
+{
+ FRESULT res;
+ BYTE *dir, ns;
+
+
+#if _FS_RPATH
+ if (*path == '/' || *path == '\\') { /* There is a heading separator */
+ path++; dj->sclust = 0; /* Strip it and start from the root dir */
+ } else { /* No heading separator */
+ dj->sclust = dj->fs->cdir; /* Start from the current dir */
+ }
+#else
+ if (*path == '/' || *path == '\\') /* Strip heading separator if exist */
+ path++;
+ dj->sclust = 0; /* Start from the root dir */
+#endif
+
+ if ((UINT)*path < ' ') { /* Nul path means the start directory itself */
+ res = dir_sdi(dj, 0);
+ dj->dir = 0;
+
+ } else { /* Follow path */
+ for (;;) {
+ res = create_name(dj, &path); /* Get a segment */
+ if (res != FR_OK) break;
+ res = dir_find(dj); /* Find it */
+ ns = *(dj->fn+NS);
+ if (res != FR_OK) { /* Failed to find the object */
+ if (res != FR_NO_FILE) break; /* Abort if any hard error occured */
+ /* Object not found */
+ if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exit */
+ dj->sclust = 0; dj->dir = 0; /* It is the root dir */
+ res = FR_OK;
+ if (!(ns & NS_LAST)) continue;
+ } else { /* Could not find the object */
+ if (!(ns & NS_LAST)) res = FR_NO_PATH;
+ }
+ break;
+ }
+ if (ns & NS_LAST) break; /* Last segment match. Function completed. */
+ dir = dj->dir; /* There is next segment. Follow the sub directory */
+ if (!(dir[DIR_Attr] & AM_DIR)) { /* Cannot follow because it is a file */
+ res = FR_NO_PATH; break;
+ }
+ dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+ }
+ }
+
+ return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Load boot record and check if it is an FAT boot record */
+/*-----------------------------------------------------------------------*/
+
+static
+BYTE check_fs ( /* 0:The FAT BR, 1:Valid BR but not an FAT, 2:Not a BR, 3:Disk 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->drv, fs->win, sect, 1) != RES_OK) /* Load boot record */
+ return 3;
+ 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 ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */
+ return 0;
+ if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146)
+ return 0;
+
+ return 1;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Make sure that the file system is valid */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT chk_mounted ( /* FR_OK(0): successful, !=0: any error occurred */
+ const TCHAR **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 fmt, b, *tbl;
+ UINT vol;
+ DSTATUS stat;
+ DWORD bsect, fasize, tsect, sysect, nclst, szbfat;
+ WORD nrsv;
+ const TCHAR *p = *path;
+ FATFS *fs;
+
+ /* Get logical drive number from the path name */
+ vol = p[0] - '0'; /* Is there a drive number? */
+ if (vol <= 9 && p[1] == ':') { /* Found a drive number, get and strip it */
+ p += 2; *path = p; /* Return pointer to the path name */
+ } else { /* No drive number is given */
+#if _FS_RPATH
+ vol = Drive; /* Use current drive */
+#else
+ vol = 0; /* Use drive 0 */
+#endif
+ }
+
+ /* Check if the logical drive is valid or not */
+ if (vol >= _DRIVES) /* Is the drive number valid? */
+ return FR_INVALID_DRIVE;
+ *rfs = fs = FatFs[vol]; /* Return pointer to the corresponding file system object */
+ if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */
+
+ ENTER_FF(fs); /* Lock file system */
+
+ if (fs->fs_type) { /* If the logical drive has been mounted */
+ stat = disk_status(fs->drv);
+ if (!(stat & STA_NOINIT)) { /* and the 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 mounted. Following code attempts to mount the volume (initialize the file system object) */
+
+ fs->fs_type = 0; /* Clear the file system object */
+ fs->drv = (BYTE)LD2PD(vol); /* Bind the logical drive and a physical drive */
+ stat = disk_initialize(fs->drv); /* Initialize low level disk I/O layer */
+ if (stat & STA_NOINIT) /* Check if the drive is ready */
+ return FR_NOT_READY;
+#if _MAX_SS != 512 /* Get disk sector size if needed */
+ if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS)
+ return FR_NO_FILESYSTEM;
+#endif
+#if !_FS_READONLY
+ if (chk_wp && (stat & STA_PROTECT)) /* Check disk write protection if needed */
+ return FR_WRITE_PROTECTED;
+#endif
+ /* Search FAT partition on the drive (Supports only generic partitionings, FDISK and SFD) */
+ fmt = check_fs(fs, bsect = 0); /* Check sector 0 if it is a VBR */
+ if (fmt == 1) { /* Not an FAT-VBR, the disk may be partitioned */
+ /* Check the partition listed in top of the partition table */
+ tbl = &fs->win[MBR_Table + LD2PT(vol) * 16]; /* Partition table */
+ if (tbl[4]) { /* Is the partition existing? */
+ bsect = LD_DWORD(&tbl[8]); /* Partition offset in LBA */
+ fmt = check_fs(fs, bsect); /* Check the partition */
+ }
+ }
+ if (fmt == 3) return FR_DISK_ERR;
+ if (fmt) return FR_NO_FILESYSTEM; /* No FAT volume is found */
+
+ /* Following code initializes the file system object */
+
+ if (LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs)) /* (BPB_BytsPerSec must be equal to the physical sector size) */
+ return FR_NO_FILESYSTEM;
+
+ fasize = LD_WORD(fs->win+BPB_FATSz16); /* Number of sectors per FAT */
+ if (!fasize) fasize = LD_DWORD(fs->win+BPB_FATSz32);
+ fs->fsize = fasize;
+
+ fs->n_fats = b = fs->win[BPB_NumFATs]; /* Number of FAT copies */
+ if (b != 1 && b != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */
+ fasize *= b; /* Number of sectors for FAT area */
+
+ fs->csize = b = fs->win[BPB_SecPerClus]; /* Number of sectors per cluster */
+ if (!b || (b & (b - 1))) return FR_NO_FILESYSTEM; /* (Must be 1,2,4...128) */
+
+ fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt); /* Number of root directory entries */
+ if (fs->n_rootdir % (SS(fs) / 32)) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be sector aligned) */
+
+ tsect = LD_WORD(fs->win+BPB_TotSec16); /* Number of sectors on the volume */
+ if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32);
+
+ nrsv = LD_WORD(fs->win+BPB_RsvdSecCnt); /* Number of reserved sectors */
+ if (!nrsv) return FR_NO_FILESYSTEM; /* (BPB_RsvdSecCnt must not be 0) */
+
+ /* Determine the FAT sub type */
+ sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / 32); /* RSV+FAT+DIR */
+ if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */
+ nclst = (tsect - sysect) / fs->csize; /* Number of clusters */
+ if (!nclst) return FR_NO_FILESYSTEM; /* (Invalid volume size) */
+ fmt = FS_FAT12;
+ if (nclst >= MIN_FAT16) fmt = FS_FAT16;
+ if (nclst >= MIN_FAT32) fmt = FS_FAT32;
+
+ /* Boundaries and Limits */
+ fs->n_fatent = nclst + 2; /* Number of FAT entries */
+ fs->database = bsect + sysect; /* Data start sector */
+ fs->fatbase = bsect + nrsv; /* FAT start sector */
+ if (fmt == FS_FAT32) {
+ if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */
+ fs->dirbase = LD_DWORD(fs->win+BPB_RootClus); /* Root directory start cluster */
+ szbfat = fs->n_fatent * 4; /* (Required FAT size) */
+ } else {
+ if (!fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */
+ fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */
+ szbfat = (fmt == FS_FAT16) ? /* (Required FAT size) */
+ fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);
+ }
+ if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) /* (FAT size must not be less than FAT sectors */
+ return FR_NO_FILESYSTEM;
+
+#if !_FS_READONLY
+ /* Initialize cluster allocation information */
+ fs->free_clust = 0xFFFFFFFF;
+ fs->last_clust = 0;
+
+ /* Get fsinfo if available */
+ if (fmt == FS_FAT32) {
+ fs->fsi_flag = 0;
+ fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo);
+ if (disk_read(fs->drv, 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
+ fs->fs_type = fmt; /* FAT sub-type */
+ fs->id = ++Fsid; /* File system mount ID */
+ fs->winsect = 0; /* Invalidate sector cache */
+ fs->wflag = 0;
+#if _FS_RPATH
+ fs->cdir = 0; /* Current directory (root dir) */
+#endif
+#if _FS_SHARE /* Clear file lock semaphores */
+ for (vol = 0; vol < _FS_SHARE; vol++)
+ fs->flsem[vol].ctr = 0;
+#endif
+
+ 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 */
+ 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;
+
+ ENTER_FF(fs); /* Lock file system */
+
+ if (disk_status(fs->drv) & STA_NOINIT)
+ return FR_NOT_READY;
+
+ return FR_OK;
+}
+
+
+
+
+/*--------------------------------------------------------------------------
+
+ Public Functions
+
+--------------------------------------------------------------------------*/
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Mount/Unmount a Logical Drive */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mount (
+ BYTE vol, /* Logical drive number to be mounted/unmounted */
+ FATFS *fs /* Pointer to new file system object (NULL for unmount)*/
+)
+{
+ FATFS *rfs;
+
+
+ if (vol >= _DRIVES) /* Check if the drive number is valid */
+ return FR_INVALID_DRIVE;
+ rfs = FatFs[vol]; /* Get current fs object */
+
+ if (rfs) {
+#if _FS_REENTRANT /* Discard sync object of the current volume */
+ if (!ff_del_syncobj(rfs->sobj)) return FR_INT_ERR;
+#endif
+ rfs->fs_type = 0; /* Clear old fs object */
+ }
+
+ if (fs) {
+ fs->fs_type = 0; /* Clear new fs object */
+#if _FS_REENTRANT /* Create sync object for the new volume */
+ if (!ff_cre_syncobj(vol, &fs->sobj)) return FR_INT_ERR;
+#endif
+ }
+ FatFs[vol] = fs; /* Register new fs object */
+
+ return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Open or Create a File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_open (
+ FAT_FIL *fp, /* Pointer to the blank file object */
+ const TCHAR *path, /* Pointer to the file name */
+ BYTE mode /* Access mode and file open mode flags */
+)
+{
+ FRESULT res;
+ FAT_DIR dj;
+ BYTE *dir;
+ DEF_NAMEBUF;
+
+
+ fp->fs = 0; /* Clear file object */
+
+#if !_FS_READONLY
+ mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW;
+ res = chk_mounted(&path, &dj.fs, (BYTE)(mode & ~FA_READ));
+#else
+ mode &= FA_READ;
+ res = chk_mounted(&path, &dj.fs, 0);
+#endif
+ INIT_BUF(dj);
+ if (res == FR_OK)
+ res = follow_path(&dj, path); /* Follow the file path */
+ dir = dj.dir;
+
+#if !_FS_READONLY /* R/W configuration */
+ if (res == FR_OK) {
+ if (!dir) /* Current dir itself */
+ res = FR_INVALID_NAME;
+#if _FS_SHARE
+ else
+ res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0);
+#endif
+ }
+ /* Create or Open a file */
+ if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
+ DWORD dw, cl;
+
+ if (res != FR_OK) { /* No file, create new */
+ if (res == FR_NO_FILE) /* There is no file to open, create a new entry */
+#if _FS_SHARE
+ res = enq_lock(dj.fs) ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES;
+#else
+ res = dir_register(&dj);
+#endif
+ mode |= FA_CREATE_ALWAYS;
+ dir = dj.dir; /* New entry */
+ }
+ else { /* Any object is already existing */
+ if (mode & FA_CREATE_NEW) { /* Cannot create new */
+ res = FR_EXIST;
+ } else {
+ if (dir[DIR_Attr] & (AM_RDO | AM_DIR)) /* Cannot overwrite it (R/O or DIR) */
+ res = FR_DENIED;
+ }
+ }
+ if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate it if overwrite mode */
+ dw = get_fattime(); /* Created time */
+ ST_DWORD(dir+DIR_CrtTime, dw);
+ dir[DIR_Attr] = 0; /* Reset attribute */
+ ST_DWORD(dir+DIR_FileSize, 0); /* size = 0 */
+ cl = ((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);
+ dj.fs->wflag = 1;
+ if (cl) { /* Remove the cluster chain if exist */
+ dw = dj.fs->winsect;
+ res = remove_chain(dj.fs, cl);
+ if (res == FR_OK) {
+ dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */
+ res = move_window(dj.fs, dw);
+ }
+ }
+ }
+ }
+ else { /* Open an existing file */
+ if (res == FR_OK) { /* Follow succeeded */
+ if (dir[DIR_Attr] & AM_DIR) { /* It is a directory */
+ res = FR_NO_FILE;
+ } else {
+ if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */
+ res = FR_DENIED;
+ }
+ }
+ }
+ if (res == FR_OK) {
+ if (mode & (FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW))
+ mode |= FA__WRITTEN; /* Set file changed flag */
+ fp->dir_sect = dj.fs->winsect; /* Pointer to the directory entry */
+ fp->dir_ptr = dir;
+#if _FS_SHARE
+ fp->lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0);
+ if (!fp->lockid) res = FR_INT_ERR;
+#endif
+ }
+
+#else /* R/O configuration */
+ if (res == FR_OK) { /* Follow succeeded */
+ if (!dir) { /* Current dir itself */
+ res = FR_INVALID_NAME;
+ } else {
+ if (dir[DIR_Attr] & AM_DIR) /* It is a directory */
+ res = FR_NO_FILE;
+ }
+ }
+#endif
+ FREE_BUF();
+
+ if (res == FR_OK) {
+ 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; /* File pointer */
+ fp->dsect = 0;
+#if _USE_FASTSEEK
+ fp->cltbl = 0; /* No cluster link map table */
+#endif
+ fp->fs = dj.fs; fp->id = dj.fs->id; /* Validate file object */
+ }
+
+ LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_read (
+ FAT_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 clst, sect, remain;
+ UINT rcnt, cc;
+ BYTE csect, *rbuff = (BYTE*)buff;
+
+
+ *br = 0; /* Initialize byte counter */
+
+ res = validate(fp->fs, fp->id); /* Check validity of the object */
+ if (res != FR_OK) LEAVE_FF(fp->fs, res);
+ if (fp->flag & FA__ERROR) /* Check abort flag */
+ LEAVE_FF(fp->fs, FR_INT_ERR);
+ if (!(fp->flag & FA_READ)) /* Check access mode */
+ LEAVE_FF(fp->fs, FR_DENIED);
+ 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? */
+ csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */
+ if (!csect) { /* On the cluster boundary? */
+ clst = (fp->fptr == 0) ? /* On the top of the file? */
+ fp->org_clust : get_fat(fp->fs, fp->curr_clust);
+ if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
+ if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+ fp->curr_clust = clst; /* Update current cluster */
+ }
+ sect = clust2sect(fp->fs, fp->curr_clust); /* Get current sector */
+ if (!sect) ABORT(fp->fs, FR_INT_ERR);
+ sect += csect;
+ cc = btr / SS(fp->fs); /* When remaining bytes >= sector size, */
+ if (cc) { /* Read maximum contiguous sectors directly */
+ if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */
+ cc = fp->fs->csize - csect;
+ if (disk_read(fp->fs->drv, rbuff, sect, (BYTE)cc) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+#if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */
+#if _FS_TINY
+ if (fp->fs->wflag && fp->fs->winsect - sect < cc)
+ mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs));
+#else
+ if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc)
+ mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs));
+#endif
+#endif
+ rcnt = SS(fp->fs) * cc; /* Number of bytes transferred */
+ continue;
+ }
+#if !_FS_TINY
+#if !_FS_READONLY
+ if (fp->flag & FA__DIRTY) { /* Write sector I/O buffer if needed */
+ if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+ fp->flag &= ~FA__DIRTY;
+ }
+#endif
+ if (fp->dsect != sect) { /* Fill sector buffer with file data */
+ if (disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+ }
+#endif
+ fp->dsect = sect;
+ }
+ rcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Get partial sector data from sector buffer */
+ if (rcnt > btr) rcnt = btr;
+#if _FS_TINY
+ if (move_window(fp->fs, fp->dsect)) /* Move sector window */
+ ABORT(fp->fs, FR_DISK_ERR);
+ mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */
+#else
+ mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt); /* Pick partial sector */
+#endif
+ }
+
+ LEAVE_FF(fp->fs, FR_OK);
+}
+
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Write File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_write (
+ FAT_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 clst, sect;
+ UINT wcnt, cc;
+ const BYTE *wbuff = (const BYTE*)buff;
+ BYTE csect;
+
+
+ *bw = 0; /* Initialize byte counter */
+
+ res = validate(fp->fs, fp->id); /* Check validity of the object */
+ if (res != FR_OK) LEAVE_FF(fp->fs, res);
+ if (fp->flag & FA__ERROR) /* Check abort flag */
+ LEAVE_FF(fp->fs, FR_INT_ERR);
+ if (!(fp->flag & FA_WRITE)) /* Check access mode */
+ LEAVE_FF(fp->fs, FR_DENIED);
+ if (fp->fsize + btw < fp->fsize) btw = 0; /* 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? */
+ csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */
+ if (!csect) { /* On the cluster boundary? */
+ if (fp->fptr == 0) { /* On the top of the file? */
+ clst = fp->org_clust; /* Follow from the origin */
+ if (clst == 0) /* When there is no cluster chain, */
+ fp->org_clust = clst = create_chain(fp->fs, 0); /* Create a new cluster chain */
+ } else { /* Middle or end of the file */
+ clst = create_chain(fp->fs, fp->curr_clust); /* Follow or stretch cluster chain */
+ }
+ if (clst == 0) break; /* Could not allocate a new cluster (disk full) */
+ if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+ if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+ fp->curr_clust = clst; /* Update current cluster */
+ }
+#if _FS_TINY
+ if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0)) /* Write back data buffer prior to following direct transfer */
+ ABORT(fp->fs, FR_DISK_ERR);
+#else
+ if (fp->flag & FA__DIRTY) { /* Write back data buffer prior to following direct transfer */
+ if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+ fp->flag &= ~FA__DIRTY;
+ }
+#endif
+ sect = clust2sect(fp->fs, fp->curr_clust); /* Get current sector */
+ if (!sect) ABORT(fp->fs, FR_INT_ERR);
+ sect += csect;
+ cc = btw / SS(fp->fs); /* When remaining bytes >= sector size, */
+ if (cc) { /* Write maximum contiguous sectors directly */
+ if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */
+ cc = fp->fs->csize - csect;
+ if (disk_write(fp->fs->drv, wbuff, sect, (BYTE)cc) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+#if _FS_TINY
+ if (fp->fs->winsect - sect < cc) { /* Refill sector cache if it gets dirty by the direct write */
+ mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs));
+ fp->fs->wflag = 0;
+ }
+#else
+ if (fp->dsect - sect < cc) { /* Refill sector cache if it gets dirty by the direct write */
+ mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs));
+ fp->flag &= ~FA__DIRTY;
+ }
+#endif
+ wcnt = SS(fp->fs) * cc; /* Number of bytes transferred */
+ continue;
+ }
+#if _FS_TINY
+ if (fp->fptr >= fp->fsize) { /* Avoid silly buffer filling at growing edge */
+ if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR);
+ fp->fs->winsect = sect;
+ }
+#else
+ if (fp->dsect != sect) { /* Fill sector buffer with file data */
+ if (fp->fptr < fp->fsize &&
+ disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+ }
+#endif
+ fp->dsect = sect;
+ }
+ wcnt = SS(fp->fs) - (fp->fptr % SS(fp->fs)); /* Put partial sector into file I/O buffer */
+ if (wcnt > btw) wcnt = btw;
+#if _FS_TINY
+ if (move_window(fp->fs, fp->dsect)) /* Move sector window */
+ ABORT(fp->fs, FR_DISK_ERR);
+ mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */
+ fp->fs->wflag = 1;
+#else
+ mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt); /* Fit partial sector */
+ fp->flag |= FA__DIRTY;
+#endif
+ }
+
+ if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */
+ fp->flag |= FA__WRITTEN; /* Set file changed flag */
+
+ LEAVE_FF(fp->fs, FR_OK);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Synchronize the File Object */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_sync (
+ FAT_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? */
+#if !_FS_TINY /* Write-back dirty buffer */
+ if (fp->flag & FA__DIRTY) {
+ if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+ LEAVE_FF(fp->fs, FR_DISK_ERR);
+ fp->flag &= ~FA__DIRTY;
+ }
+#endif
+ /* Update the directory entry */
+ res = move_window(fp->fs, fp->dir_sect);
+ if (res == FR_OK) {
+ 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(); /* Update updated time */
+ ST_DWORD(dir+DIR_WrtTime, tim);
+ fp->flag &= ~FA__WRITTEN;
+ fp->fs->wflag = 1;
+ res = sync(fp->fs);
+ }
+ }
+ }
+
+ LEAVE_FF(fp->fs, res);
+}
+
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Close File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_close (
+ FAT_FIL *fp /* Pointer to the file object to be closed */
+)
+{
+ FRESULT res;
+
+#if _FS_READONLY
+ FATFS *fs = fp->fs;
+ res = validate(fs, fp->id);
+ if (res == FR_OK) fp->fs = 0; /* Discard file object */
+ LEAVE_FF(fs, res);
+
+#else
+ res = f_sync(fp); /* Flush cached data */
+#if _FS_SHARE
+ if (res == FR_OK) { /* Decrement open counter */
+#if _FS_REENTRANT
+ res = validate(fp->fs, fp->id);
+ if (res == FR_OK) {
+ res = dec_lock(fp->fs, fp->lockid);
+ unlock_fs(fp->fs, FR_OK);
+ }
+#else
+ res = dec_lock(fp->fs, fp->lockid);
+#endif
+ }
+#endif
+ if (res == FR_OK) fp->fs = 0; /* Discard file object */
+ return res;
+#endif
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Current Drive/Directory */
+/*-----------------------------------------------------------------------*/
+
+#if _FS_RPATH
+
+FRESULT f_chdrive (
+ BYTE drv /* Drive number */
+)
+{
+ if (drv >= _DRIVES) return FR_INVALID_DRIVE;
+
+ Drive = drv;
+
+ return FR_OK;
+}
+
+
+
+
+FRESULT f_chdir (
+ const TCHAR *path /* Pointer to the directory path */
+)
+{
+ FRESULT res;
+ FAT_DIR dj;
+ BYTE *dir;
+ DEF_NAMEBUF;
+
+
+ res = chk_mounted(&path, &dj.fs, 0);
+ if (res == FR_OK) {
+ INIT_BUF(dj);
+ res = follow_path(&dj, path); /* Follow the path */
+ FREE_BUF();
+ if (res == FR_OK) { /* Follow completed */
+ dir = dj.dir; /* Pointer to the entry */
+ if (!dir) {
+ dj.fs->cdir = dj.sclust; /* Start directory itself */
+ } else {
+ if (dir[DIR_Attr] & AM_DIR) /* Reached to the directory */
+ dj.fs->cdir = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+ else
+ res = FR_NO_PATH; /* Reached but a file */
+ }
+ }
+ if (res == FR_NO_FILE) res = FR_NO_PATH;
+ }
+
+ LEAVE_FF(dj.fs, res);
+}
+
+#endif
+
+
+
+#if _FS_MINIMIZE <= 2
+/*-----------------------------------------------------------------------*/
+/* Seek File R/W Pointer */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_lseek (
+ FAT_FIL *fp, /* Pointer to the file object */
+ DWORD ofs /* File pointer from top of file */
+)
+{
+ FRESULT res;
+
+
+ res = validate(fp->fs, fp->id); /* Check validity of the object */
+ if (res != FR_OK) LEAVE_FF(fp->fs, res);
+ if (fp->flag & FA__ERROR) /* Check abort flag */
+ LEAVE_FF(fp->fs, FR_INT_ERR);
+
+#if _USE_FASTSEEK
+ if (fp->cltbl) { /* Fast seek */
+ DWORD cl, pcl, ncl, tcl, dsc, tlen, *tbl = fp->cltbl;
+ BYTE csc;
+
+ tlen = *tbl++;
+ if (ofs == CREATE_LINKMAP) { /* Create link map table */
+ cl = fp->org_clust;
+ if (cl) {
+ do {
+ if (tlen < 4) { /* Not enough table items */
+ res = FR_NOT_ENOUGH_CORE; break;
+ }
+ tcl = cl; ncl = 0;
+ do { /* Get a fragment and store the top and length */
+ pcl = cl; ncl++;
+ cl = get_fat(fp->fs, cl);
+ if (cl <= 1) ABORT(fp->fs, FR_INT_ERR);
+ if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+ } while (cl == pcl + 1);
+ *tbl++ = ncl; *tbl++ = tcl;
+ tlen -= 2;
+ } while (cl < fp->fs->n_fatent);
+ }
+ *tbl = 0; /* Terminate table */
+
+ } else { /* Fast seek */
+ if (ofs > fp->fsize) /* Clip offset at the file size */
+ ofs = fp->fsize;
+ fp->fptr = ofs; /* Set file pointer */
+ if (ofs) {
+ dsc = (ofs - 1) / SS(fp->fs);
+ cl = dsc / fp->fs->csize;
+ for (;;) {
+ ncl = *tbl++;
+ if (!ncl) ABORT(fp->fs, FR_INT_ERR);
+ if (cl < ncl) break;
+ cl -= ncl; tbl++;
+ }
+ fp->curr_clust = cl + *tbl;
+ csc = (BYTE)(dsc & (fp->fs->csize - 1));
+ dsc = clust2sect(fp->fs, fp->curr_clust);
+ if (!dsc) ABORT(fp->fs, FR_INT_ERR);
+ dsc += csc;
+ if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) {
+#if !_FS_TINY
+#if !_FS_READONLY
+ if (fp->flag & FA__DIRTY) { /* Flush dirty buffer if needed */
+ if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+ fp->flag &= ~FA__DIRTY;
+ }
+#endif
+ if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+#endif
+ fp->dsect = dsc;
+ }
+ }
+ }
+ } else
+#endif
+
+ /* Normal Seek */
+ {
+ DWORD clst, bcs, nsect, ifptr;
+
+ 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 = nsect = 0;
+ if (ofs) {
+ bcs = (DWORD)fp->fs->csize * SS(fp->fs); /* Cluster size (byte) */
+ if (ifptr > 0 &&
+ (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */
+ fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */
+ ofs -= fp->fptr;
+ clst = fp->curr_clust;
+ } else { /* When seek to back cluster, */
+ clst = fp->org_clust; /* start from the first cluster */
+#if !_FS_READONLY
+ if (clst == 0) { /* If no cluster chain, create a new chain */
+ clst = create_chain(fp->fs, 0);
+ if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+ if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+ fp->org_clust = clst;
+ }
+#endif
+ fp->curr_clust = clst;
+ }
+ if (clst != 0) {
+ while (ofs > bcs) { /* Cluster following loop */
+#if !_FS_READONLY
+ if (fp->flag & FA_WRITE) { /* Check if in write mode or not */
+ clst = create_chain(fp->fs, clst); /* Force stretch if in write mode */
+ if (clst == 0) { /* When disk gets full, clip file size */
+ ofs = bcs; break;
+ }
+ } else
+#endif
+ clst = get_fat(fp->fs, clst); /* Follow cluster chain if not in write mode */
+ if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+ if (clst <= 1 || clst >= fp->fs->n_fatent) ABORT(fp->fs, FR_INT_ERR);
+ fp->curr_clust = clst;
+ fp->fptr += bcs;
+ ofs -= bcs;
+ }
+ fp->fptr += ofs;
+ if (ofs % SS(fp->fs)) {
+ nsect = clust2sect(fp->fs, clst); /* Current sector */
+ if (!nsect) ABORT(fp->fs, FR_INT_ERR);
+ nsect += ofs / SS(fp->fs);
+ }
+ }
+ }
+ if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) {
+#if !_FS_TINY
+#if !_FS_READONLY
+ if (fp->flag & FA__DIRTY) { /* Flush dirty buffer if needed */
+ if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+ fp->flag &= ~FA__DIRTY;
+ }
+#endif
+ if (disk_read(fp->fs->drv, fp->buf, nsect, 1) != RES_OK)
+ ABORT(fp->fs, FR_DISK_ERR);
+#endif
+ fp->dsect = nsect;
+ }
+#if !_FS_READONLY
+ if (fp->fptr > fp->fsize) { /* Set changed flag if the file size is extended */
+ fp->fsize = fp->fptr;
+ fp->flag |= FA__WRITTEN;
+ }
+#endif
+ }
+
+ LEAVE_FF(fp->fs, res);
+}
+
+
+
+#if _FS_MINIMIZE <= 1
+/*-----------------------------------------------------------------------*/
+/* Create a Directroy Object */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_opendir (
+ FAT_DIR *dj, /* Pointer to directory object to create */
+ const TCHAR *path /* Pointer to the directory path */
+)
+{
+ FRESULT res;
+ BYTE *dir;
+ DEF_NAMEBUF;
+
+
+ res = chk_mounted(&path, &dj->fs, 0);
+ if (res == FR_OK) {
+ INIT_BUF(*dj);
+ res = follow_path(dj, path); /* Follow the path to the directory */
+ FREE_BUF();
+ if (res == FR_OK) { /* Follow completed */
+ dir = dj->dir;
+ if (dir) { /* It is not the current dir */
+ if (dir[DIR_Attr] & AM_DIR) { /* The object is a directory */
+ dj->sclust = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+ } else { /* The object is not a directory */
+ res = FR_NO_PATH;
+ }
+ }
+ if (res == FR_OK) {
+ dj->id = dj->fs->id;
+ res = dir_sdi(dj, 0); /* Rewind dir */
+ }
+ }
+ if (res == FR_NO_FILE) res = FR_NO_PATH;
+ }
+
+ LEAVE_FF(dj->fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read Directory Entry in Sequense */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_readdir (
+ FAT_DIR *dj, /* Pointer to the open directory object */
+ FILINFO *fno /* Pointer to file information to return */
+)
+{
+ FRESULT res;
+ DEF_NAMEBUF;
+
+
+ res = validate(dj->fs, dj->id); /* Check validity of the object */
+ if (res == FR_OK) {
+ if (!fno) {
+ res = dir_sdi(dj, 0);
+ } else {
+ INIT_BUF(*dj);
+ res = dir_read(dj);
+ if (res == FR_NO_FILE) {
+ dj->sect = 0;
+ res = FR_OK;
+ }
+ if (res == FR_OK) { /* A valid entry is found */
+ get_fileinfo(dj, fno); /* Get the object information */
+ res = dir_next(dj, 0); /* Increment index for next */
+ if (res == FR_NO_FILE) {
+ dj->sect = 0;
+ res = FR_OK;
+ }
+ }
+ FREE_BUF();
+ }
+ }
+
+ LEAVE_FF(dj->fs, res);
+}
+
+
+
+#if _FS_MINIMIZE == 0
+/*-----------------------------------------------------------------------*/
+/* Get File Status */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_stat (
+ const TCHAR *path, /* Pointer to the file path */
+ FILINFO *fno /* Pointer to file information to return */
+)
+{
+ FRESULT res;
+ FAT_DIR dj;
+ DEF_NAMEBUF;
+
+
+ res = chk_mounted(&path, &dj.fs, 0);
+ if (res == FR_OK) {
+ INIT_BUF(dj);
+ res = follow_path(&dj, path); /* Follow the file path */
+ if (res == FR_OK) { /* Follow completed */
+ if (dj.dir) /* Found an object */
+ get_fileinfo(&dj, fno);
+ else /* It is root dir */
+ res = FR_INVALID_NAME;
+ }
+ FREE_BUF();
+ }
+
+ LEAVE_FF(dj.fs, res);
+}
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Get Number of Free Clusters */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_getfree (
+ const TCHAR *path, /* Pointer to the logical drive number (root dir) */
+ DWORD *nclst, /* 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, clst, sect, stat;
+ UINT i;
+ BYTE fat, *p;
+
+
+ /* Get drive number */
+ res = chk_mounted(&path, fatfs, 0);
+ if (res == FR_OK) {
+ /* If free_clust is valid, return it without full cluster scan */
+ if ((*fatfs)->free_clust <= (*fatfs)->n_fatent - 2) {
+ *nclst = (*fatfs)->free_clust;
+ } else {
+ /* Get number of free clusters */
+ fat = (*fatfs)->fs_type;
+ n = 0;
+ if (fat == FS_FAT12) {
+ clst = 2;
+ do {
+ stat = get_fat(*fatfs, clst);
+ if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }
+ if (stat == 1) { res = FR_INT_ERR; break; }
+ if (stat == 0) n++;
+ } while (++clst < (*fatfs)->n_fatent);
+ } else {
+ clst = (*fatfs)->n_fatent;
+ sect = (*fatfs)->fatbase;
+ i = 0; p = 0;
+ do {
+ if (!i) {
+ res = move_window(*fatfs, sect++);
+ if (res != FR_OK) break;
+ p = (*fatfs)->win;
+ i = SS(*fatfs);
+ }
+ if (fat == FS_FAT16) {
+ if (LD_WORD(p) == 0) n++;
+ p += 2; i -= 2;
+ } else {
+ if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++;
+ p += 4; i -= 4;
+ }
+ } while (--clst);
+ }
+ (*fatfs)->free_clust = n;
+ if (fat == FS_FAT32) (*fatfs)->fsi_flag = 1;
+ *nclst = n;
+ }
+ }
+ LEAVE_FF(*fatfs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Truncate File */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_truncate (
+ FAT_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) {
+ if (fp->flag & FA__ERROR) { /* Check abort flag */
+ res = FR_INT_ERR;
+ } else {
+ if (!(fp->flag & FA_WRITE)) /* Check access mode */
+ res = FR_DENIED;
+ }
+ }
+ if (res == FR_OK) {
+ 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 */
+ res = remove_chain(fp->fs, fp->org_clust);
+ fp->org_clust = 0;
+ } else { /* When truncate a part of the file, remove remaining clusters */
+ ncl = get_fat(fp->fs, fp->curr_clust);
+ res = FR_OK;
+ if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
+ if (ncl == 1) res = FR_INT_ERR;
+ if (res == FR_OK && ncl < fp->fs->n_fatent) {
+ res = put_fat(fp->fs, fp->curr_clust, 0x0FFFFFFF);
+ if (res == FR_OK) res = remove_chain(fp->fs, ncl);
+ }
+ }
+ }
+ if (res != FR_OK) fp->flag |= FA__ERROR;
+ }
+
+ LEAVE_FF(fp->fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Delete a File or Directory */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_unlink (
+ const TCHAR *path /* Pointer to the file or directory path */
+)
+{
+ FRESULT res;
+ FAT_DIR dj, sdj;
+ BYTE *dir;
+ DWORD dclst;
+ DEF_NAMEBUF;
+
+
+ res = chk_mounted(&path, &dj.fs, 1);
+ if (res == FR_OK) {
+ INIT_BUF(dj);
+ res = follow_path(&dj, path); /* Follow the file path */
+ if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+ res = FR_INVALID_NAME; /* Cannot remove dot entry */
+#if _FS_SHARE
+ if (res == FR_OK) res = chk_lock(&dj, 2); /* Cannot remove open file */
+#endif
+ if (res == FR_OK) { /* The object is accessible */
+ dir = dj.dir;
+ if (!dir) {
+ res = FR_INVALID_NAME; /* Cannot remove the start directory */
+ } else {
+ if (dir[DIR_Attr] & AM_RDO)
+ res = FR_DENIED; /* Cannot remove R/O object */
+ }
+ dclst = ((DWORD)LD_WORD(dir+DIR_FstClusHI) << 16) | LD_WORD(dir+DIR_FstClusLO);
+ if (res == FR_OK && (dir[DIR_Attr] & AM_DIR)) { /* Is it a sub-dir? */
+ if (dclst < 2) {
+ res = FR_INT_ERR;
+ } else {
+ mem_cpy(&sdj, &dj, sizeof(FAT_DIR)); /* Check if the sub-dir is empty or not */
+ sdj.sclust = dclst;
+ res = dir_sdi(&sdj, 2); /* Exclude dot entries */
+ if (res == FR_OK) {
+ res = dir_read(&sdj);
+ if (res == FR_OK /* Not empty dir */
+#if _FS_RPATH
+ || dclst == sdj.fs->cdir /* Current dir */
+#endif
+ ) res = FR_DENIED;
+ if (res == FR_NO_FILE) res = FR_OK; /* Empty */
+ }
+ }
+ }
+ if (res == FR_OK) {
+ res = dir_remove(&dj); /* Remove the directory entry */
+ if (res == FR_OK) {
+ if (dclst) /* Remove the cluster chain if exist */
+ res = remove_chain(dj.fs, dclst);
+ if (res == FR_OK) res = sync(dj.fs);
+ }
+ }
+ }
+ FREE_BUF();
+ }
+ LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create a Directory */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mkdir (
+ const TCHAR *path /* Pointer to the directory path */
+)
+{
+ FRESULT res;
+ FAT_DIR dj;
+ BYTE *dir, n;
+ DWORD dsc, dcl, pcl, tim = get_fattime();
+ DEF_NAMEBUF;
+
+
+ res = chk_mounted(&path, &dj.fs, 1);
+ if (res == FR_OK) {
+ INIT_BUF(dj);
+ res = follow_path(&dj, path); /* Follow the file path */
+ if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */
+ if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT))
+ res = FR_INVALID_NAME;
+ if (res == FR_NO_FILE) { /* Can create a new directory */
+ dcl = create_chain(dj.fs, 0); /* Allocate a cluster for the new directory table */
+ res = FR_OK;
+ if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */
+ if (dcl == 1) res = FR_INT_ERR;
+ if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR;
+ if (res == FR_OK) /* Flush FAT */
+ res = move_window(dj.fs, 0);
+ if (res == FR_OK) { /* Initialize the new directory table */
+ dsc = clust2sect(dj.fs, dcl);
+ dir = dj.fs->win;
+ mem_set(dir, 0, SS(dj.fs));
+ mem_set(dir+DIR_Name, ' ', 8+3); /* Create "." entry */
+ dir[DIR_Name] = '.';
+ dir[DIR_Attr] = AM_DIR;
+ ST_DWORD(dir+DIR_WrtTime, tim);
+ ST_WORD(dir+DIR_FstClusLO, dcl);
+ ST_WORD(dir+DIR_FstClusHI, dcl >> 16);
+ mem_cpy(dir+32, dir, 32); /* Create ".." entry */
+ dir[33] = '.'; pcl = dj.sclust;
+ if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase)
+ pcl = 0;
+ ST_WORD(dir+32+DIR_FstClusLO, pcl);
+ ST_WORD(dir+32+DIR_FstClusHI, pcl >> 16);
+ for (n = dj.fs->csize; n; n--) { /* Write dot entries and clear following sectors */
+ dj.fs->winsect = dsc++;
+ dj.fs->wflag = 1;
+ res = move_window(dj.fs, 0);
+ if (res != FR_OK) break;
+ mem_set(dir, 0, SS(dj.fs));
+ }
+ }
+ if (res == FR_OK) res = dir_register(&dj); /* Register the object to the directoy */
+ if (res != FR_OK) {
+ remove_chain(dj.fs, dcl); /* Could not register, remove cluster chain */
+ } else {
+ dir = dj.dir;
+ dir[DIR_Attr] = AM_DIR; /* Attribute */
+ ST_DWORD(dir+DIR_WrtTime, tim); /* Created time */
+ ST_WORD(dir+DIR_FstClusLO, dcl); /* Table start cluster */
+ ST_WORD(dir+DIR_FstClusHI, dcl >> 16);
+ dj.fs->wflag = 1;
+ res = sync(dj.fs);
+ }
+ }
+ FREE_BUF();
+ }
+
+ LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Attribute */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_chmod (
+ const TCHAR *path, /* Pointer to the file path */
+ BYTE value, /* Attribute bits */
+ BYTE mask /* Attribute mask to change */
+)
+{
+ FRESULT res;
+ FAT_DIR dj;
+ BYTE *dir;
+ DEF_NAMEBUF;
+
+
+ res = chk_mounted(&path, &dj.fs, 1);
+ if (res == FR_OK) {
+ INIT_BUF(dj);
+ res = follow_path(&dj, path); /* Follow the file path */
+ FREE_BUF();
+ if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+ res = FR_INVALID_NAME;
+ if (res == FR_OK) {
+ dir = dj.dir;
+ if (!dir) { /* Is it a root directory? */
+ res = FR_INVALID_NAME;
+ } else { /* File or sub directory */
+ 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 */
+ dj.fs->wflag = 1;
+ res = sync(dj.fs);
+ }
+ }
+ }
+
+ LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Timestamp */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_utime (
+ const TCHAR *path, /* Pointer to the file/directory name */
+ const FILINFO *fno /* Pointer to the time stamp to be set */
+)
+{
+ FRESULT res;
+ FAT_DIR dj;
+ BYTE *dir;
+ DEF_NAMEBUF;
+
+
+ res = chk_mounted(&path, &dj.fs, 1);
+ if (res == FR_OK) {
+ INIT_BUF(dj);
+ res = follow_path(&dj, path); /* Follow the file path */
+ FREE_BUF();
+ if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+ res = FR_INVALID_NAME;
+ if (res == FR_OK) {
+ dir = dj.dir;
+ if (!dir) { /* Root directory */
+ res = FR_INVALID_NAME;
+ } else { /* File or sub-directory */
+ ST_WORD(dir+DIR_WrtTime, fno->ftime);
+ ST_WORD(dir+DIR_WrtDate, fno->fdate);
+ dj.fs->wflag = 1;
+ res = sync(dj.fs);
+ }
+ }
+ }
+
+ LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Rename File/Directory */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_rename (
+ const TCHAR *path_old, /* Pointer to the old name */
+ const TCHAR *path_new /* Pointer to the new name */
+)
+{
+ FRESULT res;
+ FAT_DIR djo, djn;
+ BYTE buf[21], *dir;
+ DWORD dw;
+ DEF_NAMEBUF;
+
+
+ res = chk_mounted(&path_old, &djo.fs, 1);
+ if (res == FR_OK) {
+ djn.fs = djo.fs;
+ INIT_BUF(djo);
+ res = follow_path(&djo, path_old); /* Check old object */
+ if (_FS_RPATH && res == FR_OK && (djo.fn[NS] & NS_DOT))
+ res = FR_INVALID_NAME;
+#if _FS_SHARE
+ if (res == FR_OK) res = chk_lock(&djo, 2);
+#endif
+ if (res == FR_OK) { /* Old object is found */
+ if (!djo.dir) { /* Is root dir? */
+ res = FR_NO_FILE;
+ } else {
+ mem_cpy(buf, djo.dir+DIR_Attr, 21); /* Save the object information except for name */
+ mem_cpy(&djn, &djo, sizeof(FAT_DIR)); /* Check new object */
+ res = follow_path(&djn, path_new);
+ if (res == FR_OK) res = FR_EXIST; /* The new object name is already existing */
+ if (res == FR_NO_FILE) { /* Is it a valid path and no name collision? */
+/* Start critical section that any interruption or error can cause cross-link */
+ res = dir_register(&djn); /* Register the new entry */
+ if (res == FR_OK) {
+ dir = djn.dir; /* Copy object information except for name */
+ mem_cpy(dir+13, buf+2, 19);
+ dir[DIR_Attr] = buf[0] | AM_ARC;
+ djo.fs->wflag = 1;
+ if (djo.sclust != djn.sclust && (dir[DIR_Attr] & AM_DIR)) { /* Update .. entry in the directory if needed */
+ dw = clust2sect(djn.fs, (DWORD)LD_WORD(dir+DIR_FstClusHI) | LD_WORD(dir+DIR_FstClusLO));
+ if (!dw) {
+ res = FR_INT_ERR;
+ } else {
+ res = move_window(djn.fs, dw);
+ dir = djn.fs->win+32; /* .. entry */
+ if (res == FR_OK && dir[1] == '.') {
+ dw = (djn.fs->fs_type == FS_FAT32 && djn.sclust == djn.fs->dirbase) ? 0 : djn.sclust;
+ ST_WORD(dir+DIR_FstClusLO, dw);
+ ST_WORD(dir+DIR_FstClusHI, dw >> 16);
+ djn.fs->wflag = 1;
+ }
+ }
+ }
+ if (res == FR_OK) {
+ res = dir_remove(&djo); /* Remove old entry */
+ if (res == FR_OK)
+ res = sync(djo.fs);
+ }
+ }
+/* End critical section */
+ }
+ }
+ }
+ FREE_BUF();
+ }
+ LEAVE_FF(djo.fs, res);
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _FS_MINIMIZE == 0 */
+#endif /* _FS_MINIMIZE <= 1 */
+#endif /* _FS_MINIMIZE <= 2 */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Forward data to the stream directly (available on only tiny cfg) */
+/*-----------------------------------------------------------------------*/
+#if _USE_FORWARD && _FS_TINY
+
+FRESULT f_forward (
+ FAT_FIL *fp, /* Pointer to the file object */
+ UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */
+ UINT btr, /* Number of bytes to forward */
+ UINT *bf /* Pointer to number of bytes forwarded */
+)
+{
+ FRESULT res;
+ DWORD remain, clst, sect;
+ UINT rcnt;
+ BYTE csect;
+
+
+ *bf = 0; /* Initialize byte counter */
+
+ res = validate(fp->fs, fp->id); /* Check validity of the object */
+ if (res != FR_OK) LEAVE_FF(fp->fs, res);
+ if (fp->flag & FA__ERROR) /* Check error flag */
+ LEAVE_FF(fp->fs, FR_INT_ERR);
+ if (!(fp->flag & FA_READ)) /* Check access mode */
+ LEAVE_FF(fp->fs, FR_DENIED);
+
+ remain = fp->fsize - fp->fptr;
+ if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */
+
+ for ( ; btr && (*func)(0, 0); /* Repeat until all data transferred or stream becomes busy */
+ fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) {
+ csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1)); /* Sector offset in the cluster */
+ if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */
+ if (!csect) { /* On the cluster boundary? */
+ clst = (fp->fptr == 0) ? /* On the top of the file? */
+ fp->org_clust : get_fat(fp->fs, fp->curr_clust);
+ if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
+ if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+ fp->curr_clust = clst; /* Update current cluster */
+ }
+ }
+ sect = clust2sect(fp->fs, fp->curr_clust); /* Get current data sector */
+ if (!sect) ABORT(fp->fs, FR_INT_ERR);
+ sect += csect;
+ if (move_window(fp->fs, sect)) /* Move sector window */
+ ABORT(fp->fs, FR_DISK_ERR);
+ fp->dsect = sect;
+ rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs)); /* Forward data from sector window */
+ if (rcnt > btr) rcnt = btr;
+ rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt);
+ if (!rcnt) ABORT(fp->fs, FR_INT_ERR);
+ }
+
+ LEAVE_FF(fp->fs, FR_OK);
+}
+#endif /* _USE_FORWARD */
+
+
+
+#if _USE_MKFS && !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Create File System on the Drive */
+/*-----------------------------------------------------------------------*/
+#define N_ROOTDIR 512 /* Multiple of 32 */
+#define N_FATS 1 /* 1 or 2 */
+
+
+FRESULT f_mkfs (
+ BYTE drv, /* Logical drive number */
+ BYTE sfd, /* Partitioning rule 0:FDISK, 1:SFD */
+ UINT au /* Allocation unit size [bytes] */
+)
+{
+ static const WORD vst[] = { 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 0};
+ static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512};
+ BYTE fmt, md, *tbl;
+ DWORD n_clst, vs, n;
+ UINT as, i;
+ DWORD b_vol, b_fat, b_dir, b_data; /* Area offset (LBA) */
+ DWORD n_vol, n_rsv, n_fat, n_dir; /* Area size */
+ FATFS *fs;
+ DSTATUS stat;
+
+
+ /* Check mounted drive and clear work area */
+ if (drv >= _DRIVES) return FR_INVALID_DRIVE;
+ 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 _MAX_SS != 512 /* Get disk sector size */
+ if (disk_ioctl(drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS)
+ return FR_DISK_ERR;
+#endif
+ if (disk_ioctl(drv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128)
+ return FR_DISK_ERR;
+ b_vol = (sfd == 1) ? 0 : 63; /* Volume start sector */
+ n_vol -= b_vol;
+ if (au & (au - 1)) au = 0; /* Check validity of the allocation unit size */
+ if (!au) { /* AU auto selection */
+ vs = n_vol / (2000 / (SS(fs) / 512));
+ for (i = 0; vs < vst[i]; i++) ;
+ au = cst[i];
+ }
+ if (_MAX_SS != 512 && au < SS(fs)) au = SS(fs);
+ au /= SS(fs); /* Number of sectors per cluster */
+ if (au == 0) au = 1;
+ if (au > 128) au = 128;
+
+ /* Pre-compute number of clusters and FAT syb-type */
+ n_clst = n_vol / au;
+ fmt = FS_FAT12;
+ if (n_clst >= MIN_FAT16) fmt = FS_FAT16;
+ if (n_clst >= MIN_FAT32) fmt = FS_FAT32;
+
+ /* Determine offset and size of FAT structure */
+ if (fmt == FS_FAT32) {
+ n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs);
+ n_rsv = 32;
+ n_dir = 0;
+ } else {
+ n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4;
+ n_fat = (n_fat + SS(fs) - 1) / SS(fs);
+ n_rsv = 1;
+ n_dir = N_ROOTDIR * 32UL / SS(fs);
+ }
+ b_fat = b_vol + n_rsv; /* FAT area start sector */
+ b_dir = b_fat + n_fat * N_FATS; /* Directory area start sector */
+ b_data = b_dir + n_dir; /* Data area start sector */
+ if (n_vol < b_data + au) return FR_MKFS_ABORTED; /* Too small volume */
+
+ /* Align data start sector to erase block boundary (for flash memory media) */
+ if (disk_ioctl(drv, GET_BLOCK_SIZE, &n) != RES_OK) return FR_DISK_ERR;
+ if (!n || n > 32768) return FR_MKFS_ABORTED;
+ n = (b_data + n - 1) & ~(n - 1); /* Next nearest boundary from current data start */
+ n = (n - b_data) / N_FATS;
+ if (fmt == FS_FAT32) { /* FAT32: Move FAT start */
+ n_rsv += n;
+ b_fat += n;
+ } else { /* FAT12/16: Expand FAT size */
+ n_fat += n;
+ }
+ /* b_dir and b_data are no longer used below */
+
+ /* Determine number of cluster and final check of validity of the FAT sub-type */
+ n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au;
+ if ( (fmt == FS_FAT16 && n_clst < MIN_FAT16)
+ || (fmt == FS_FAT32 && n_clst < MIN_FAT32))
+ return FR_MKFS_ABORTED;
+
+ /* Create partition table if required */
+ if (sfd == 1) {
+ md = 0xF0;
+ } else {
+ DWORD n_disk = b_vol + n_vol;
+
+ mem_set(fs->win, 0, SS(fs));
+ tbl = fs->win+MBR_Table;
+ ST_DWORD(tbl, 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_vol < 0x10000) ? 0x04 : 0x06;
+ else
+ tbl[4] = 0x0c;
+ ST_DWORD(tbl+8, 63); /* Partition start in LBA */
+ ST_DWORD(tbl+12, n_vol); /* Partition size in LBA */
+ ST_WORD(tbl+64, 0xAA55); /* Signature */
+ if (disk_write(drv, fs->win, 0, 1) != RES_OK)
+ return FR_DISK_ERR;
+ md = 0xF8;
+ }
+
+ /* Create VBR */
+ tbl = fs->win; /* Clear buffer */
+ mem_set(tbl, 0, SS(fs));
+ ST_DWORD(tbl+BS_jmpBoot, 0x90FEEB); /* Boot code (jmp $, nop) */
+ as = SS(fs); /* Sector size */
+ ST_WORD(tbl+BPB_BytsPerSec, as);
+ tbl[BPB_SecPerClus] = (BYTE)au; /* Sectors per cluster */
+ ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv); /* Reserved sectors */
+ tbl[BPB_NumFATs] = N_FATS; /* Number of FATs */
+ as = (fmt == FS_FAT32) ? 0 : N_ROOTDIR; /* Number of rootdir entries */
+ ST_WORD(tbl+BPB_RootEntCnt, as);
+ if (n_vol < 0x10000) { /* Number of total sectors */
+ ST_WORD(tbl+BPB_TotSec16, n_vol);
+ } else {
+ ST_DWORD(tbl+BPB_TotSec32, n_vol);
+ }
+ tbl[BPB_Media] = md; /* Media descriptor */
+ 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_vol); /* Hidden sectors */
+ n = get_fattime(); /* Use current time as VSN */
+ if (fmt == FS_FAT32) {
+ ST_DWORD(tbl+BS_VolID32, n); /* VSN */
+ ST_DWORD(tbl+BPB_FATSz32, n_fat); /* Number of sectors per FAT */
+ ST_DWORD(tbl+BPB_RootClus, 2); /* Root directory start cluster (2) */
+ ST_WORD(tbl+BPB_FSInfo, 1); /* FSInfo record offset (VBR+1) */
+ ST_WORD(tbl+BPB_BkBootSec, 6); /* Backup boot record offset (VBR+6) */
+ tbl[BS_DrvNum32] = 0x80; /* Drive number */
+ tbl[BS_BootSig32] = 0x29; /* Extended boot signature */
+ mem_cpy(tbl+BS_VolLab32, "NO NAME FAT32 ", 19); /* Volume label, FAT signature */
+ } else {
+ ST_DWORD(tbl+BS_VolID, n); /* VSN */
+ ST_WORD(tbl+BPB_FATSz16, n_fat); /* Number of sectors per FAT */
+ tbl[BS_DrvNum] = 0x80; /* Drive number */
+ tbl[BS_BootSig] = 0x29; /* Extended boot signature */
+ mem_cpy(tbl+BS_VolLab, "NO NAME FAT ", 19); /* Volume label, FAT signature */
+ }
+ ST_WORD(tbl+BS_55AA, 0xAA55); /* Signature (Offset is fixed here regardless of sector size) */
+ if (disk_write(drv, tbl, b_vol, 1) != RES_OK) /* Original (VBR) */
+ return FR_DISK_ERR;
+ if (fmt == FS_FAT32) /* Backup (VBR+6) */
+ disk_write(drv, tbl, b_vol + 6, 1);
+
+ /* Initialize FAT area */
+ for (i = 0; i < N_FATS; i++) {
+ mem_set(tbl, 0, SS(fs)); /* 1st sector of the FAT */
+ n = md; /* Media descriptor byte */
+ if (fmt != FS_FAT32) {
+ n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00;
+ ST_DWORD(tbl+0, n); /* Reserve cluster #0-1 (FAT12/16) */
+ } else {
+ n |= 0x0FFFFF00;
+ ST_DWORD(tbl+0, n); /* Reserve cluster #0-1 (FAT32) */
+ ST_DWORD(tbl+4, 0x0FFFFFFF);
+ ST_DWORD(tbl+8, 0x0FFFFFFF); /* Reserve cluster #2 for root dir */
+ }
+ if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
+ return FR_DISK_ERR;
+ mem_set(tbl, 0, SS(fs)); /* Fill following FAT entries with zero */
+ for (n = 1; n < n_fat; n++) { /* This loop may take a time on FAT32 volume due to many single sector write */
+ if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
+ return FR_DISK_ERR;
+ }
+ }
+
+ /* Initialize root directory */
+ n = (fmt == FS_FAT32) ? as : n_dir;
+ while (n--) {
+ if (disk_write(drv, tbl, b_fat++, 1) != RES_OK)
+ return FR_DISK_ERR;
+ }
+
+ /* 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_clst - 1);
+ ST_DWORD(tbl+FSI_Nxt_Free, 0xFFFFFFFF);
+ disk_write(drv, tbl, b_vol + 1, 1); /* Original (VBR+1) */
+ disk_write(drv, tbl, b_vol + 7, 1); /* Backup (VBR+7) */
+ }
+
+ return (disk_ioctl(drv, CTRL_SYNC, (void*)0) == RES_OK) ? FR_OK : FR_DISK_ERR;
+}
+
+#endif /* _USE_MKFS && !_FS_READONLY */
+
+
+
+
+#if _USE_STRFUNC
+/*-----------------------------------------------------------------------*/
+/* Get a string from the file */
+/*-----------------------------------------------------------------------*/
+TCHAR* f_gets (
+ TCHAR* buff, /* Pointer to the string buffer to read */
+ int len, /* Size of string buffer (characters) */
+ FAT_FIL* fil /* Pointer to the file object */
+)
+{
+ int n = 0;
+ TCHAR c, *p = buff;
+ BYTE s[2];
+ UINT rc;
+
+
+ while (n < len - 1) { /* Read bytes until buffer gets filled */
+ f_read(fil, s, 1, &rc);
+ if (rc != 1) break; /* Break on EOF or error */
+ c = s[0];
+#if _LFN_UNICODE /* Read a character in UTF-8 encoding */
+ if (c >= 0x80) {
+ if (c < 0xC0) continue; /* Skip stray trailer */
+ if (c < 0xE0) { /* Two-byte sequense */
+ f_read(fil, s, 1, &rc);
+ if (rc != 1) break;
+ c = ((c & 0x1F) << 6) | (s[0] & 0x3F);
+ if (c < 0x80) c = '?';
+ } else {
+ if (c < 0xF0) { /* Three-byte sequense */
+ f_read(fil, s, 2, &rc);
+ if (rc != 2) break;
+ c = (c << 12) | ((s[0] & 0x3F) << 6) | (s[1] & 0x3F);
+ if (c < 0x800) c = '?';
+ } else { /* Reject four-byte sequense */
+ c = '?';
+ }
+ }
+ }
+#endif
+#if _USE_STRFUNC >= 2
+ if (c == '\r') continue; /* Strip '\r' */
+#endif
+ *p++ = c;
+ n++;
+ if (c == '\n') break; /* Break on EOL */
+ }
+ *p = 0;
+ return n ? buff : 0; /* When no data read (eof or error), return with error. */
+}
+
+
+
+#if !_FS_READONLY
+#include <stdarg.h>
+/*-----------------------------------------------------------------------*/
+/* Put a character to the file */
+/*-----------------------------------------------------------------------*/
+int f_putc (
+ TCHAR c, /* A character to be output */
+ FAT_FIL* fil /* Pointer to the file object */
+)
+{
+ UINT bw, btw;
+ BYTE s[3];
+
+
+#if _USE_STRFUNC >= 2
+ if (c == '\n') f_putc ('\r', fil); /* LF -> CRLF conversion */
+#endif
+
+#if _LFN_UNICODE /* Write the character in UTF-8 encoding */
+ if (c < 0x80) { /* 7-bit */
+ s[0] = (BYTE)c;
+ btw = 1;
+ } else {
+ if (c < 0x800) { /* 11-bit */
+ s[0] = (BYTE)(0xC0 | (c >> 6));
+ s[1] = (BYTE)(0x80 | (c & 0x3F));
+ btw = 2;
+ } else { /* 16-bit */
+ s[0] = (BYTE)(0xE0 | (c >> 12));
+ s[1] = (BYTE)(0x80 | ((c >> 6) & 0x3F));
+ s[2] = (BYTE)(0x80 | (c & 0x3F));
+ btw = 3;
+ }
+ }
+#else /* Write the character without conversion */
+ s[0] = (BYTE)c;
+ btw = 1;
+#endif
+ f_write(fil, s, btw, &bw); /* Write the char to the file */
+ return (bw == btw) ? 1 : EOF; /* Return the result */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a string to the file */
+/*-----------------------------------------------------------------------*/
+int f_puts (
+ const TCHAR* str, /* Pointer to the string to be output */
+ FAT_FIL* fil /* Pointer to the file object */
+)
+{
+ int n;
+
+
+ for (n = 0; *str; str++, n++) {
+ if (f_putc(*str, fil) == EOF) return EOF;
+ }
+ return n;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a formatted string to the file */
+/*-----------------------------------------------------------------------*/
+int f_printf (
+ FAT_FIL* fil, /* Pointer to the file object */
+ const TCHAR* str, /* Pointer to the format string */
+ ... /* Optional arguments... */
+)
+{
+ va_list arp;
+ BYTE f, r;
+ UINT i, w;
+ ULONG val;
+ TCHAR c, d, s[16];
+ int 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 character */
+ cc = f_putc(c, fil);
+ if (cc != EOF) cc = 1;
+ continue;
+ }
+ w = f = 0;
+ c = *str++;
+ if (c == '0') { /* Flag: '0' padding */
+ f = 1; c = *str++;
+ }
+ while (IsDigit(c)) { /* Precision */
+ w = w * 10 + c - '0';
+ c = *str++;
+ }
+ if (c == 'l' || c == 'L') { /* Prefix: Size is long int */
+ f |= 2; c = *str++;
+ }
+ if (!c) break;
+ d = c;
+ if (IsLower(d)) d -= 0x20;
+ switch (d) { /* Type is... */
+ case 'S' : /* String */
+ cc = f_puts(va_arg(arp, TCHAR*), fil); continue;
+ case 'C' : /* Character */
+ cc = f_putc((TCHAR)va_arg(arp, int), fil); continue;
+ case 'B' : /* Binary */
+ r = 2; break;
+ case 'O' : /* Octal */
+ r = 8; break;
+ case 'D' : /* Signed decimal */
+ case 'U' : /* Unsigned decimal */
+ r = 10; break;
+ case 'X' : /* Hexdecimal */
+ r = 16; break;
+ default: /* Unknown */
+ cc = f_putc(c, fil); continue;
+ }
+
+ /* Get an argument */
+ val = (f & 2) ? va_arg(arp, long) : ((d == 'D') ? (long)va_arg(arp, int) : va_arg(arp, unsigned int));
+ if (d == 'D' && (val & 0x80000000)) {
+ val = 0 - val;
+ f |= 4;
+ }
+ /* Put it in numeral string */
+ i = 0;
+ do {
+ d = (TCHAR)(val % r); val /= r;
+ if (d > 9) {
+ d += 7;
+ if (c == 'x') d += 0x20;
+ }
+ s[i++] = d + '0';
+ } while (val && i < sizeof(s) / sizeof(s[0]));
+ if (f & 4) s[i++] = '-';
+ cc = 0;
+ while (i < w-- && cc != EOF) {
+ cc = f_putc((TCHAR)((f & 1) ? '0' : ' '), fil);
+ res++;
+ }
+ do {
+ cc = f_putc(s[--i], fil);
+ res++;
+ } while (i && cc != EOF);
+ if (cc != EOF) cc = 0;
+ }
+
+ va_end(arp);
+ return (cc == EOF) ? cc : res;
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _USE_STRFUNC */
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FATFileSystem/Core/ff.h Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,615 @@
+/*---------------------------------------------------------------------------/
+/ FatFs - FAT file system module include file R0.08 (C)ChaN, 2010
+/----------------------------------------------------------------------------/
+/ FatFs module is a generic FAT file system module for small embedded systems.
+/ This is a free software that opened for education, research and commercial
+/ developments under license policy of following trems.
+/
+/ Copyright (C) 2010, ChaN, all right reserved.
+/
+/ * The FatFs module is a free software and there is NO WARRANTY.
+/ * No restriction on use. You can use, modify and redistribute it for
+/ personal, non-profit or commercial product UNDER YOUR RESPONSIBILITY.
+/ * Redistributions of source code must retain the above copyright notice.
+/
+/----------------------------------------------------------------------------*/
+
+//Modified by Thomas Hamilton, Copyright 2010
+
+#ifndef _FATFS
+#define _FATFS 8085 /* Revision ID */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "integer.h" /* Basic integer types */
+#include "ffconf.h" /* FatFs configuration options */
+
+#if _FATFS != _FFCONF
+#error Wrong configuration file (ffconf.h).
+#endif
+
+
+/* DBCS code ranges and SBCS extend char conversion table */
+
+#if _CODE_PAGE == 932 /* Japanese Shift-JIS */
+#define _DF1S 0x81 /* DBC 1st byte range 1 start */
+#define _DF1E 0x9F /* DBC 1st byte range 1 end */
+#define _DF2S 0xE0 /* DBC 1st byte range 2 start */
+#define _DF2E 0xFC /* DBC 1st byte range 2 end */
+#define _DS1S 0x40 /* DBC 2nd byte range 1 start */
+#define _DS1E 0x7E /* DBC 2nd byte range 1 end */
+#define _DS2S 0x80 /* DBC 2nd byte range 2 start */
+#define _DS2E 0xFC /* DBC 2nd byte range 2 end */
+
+#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */
+#define _DF1S 0x81
+#define _DF1E 0xFE
+#define _DS1S 0x40
+#define _DS1E 0x7E
+#define _DS2S 0x80
+#define _DS2E 0xFE
+
+#elif _CODE_PAGE == 949 /* Korean */
+#define _DF1S 0x81
+#define _DF1E 0xFE
+#define _DS1S 0x41
+#define _DS1E 0x5A
+#define _DS2S 0x61
+#define _DS2E 0x7A
+#define _DS3S 0x81
+#define _DS3E 0xFE
+
+#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */
+#define _DF1S 0x81
+#define _DF1E 0xFE
+#define _DS1S 0x40
+#define _DS1E 0x7E
+#define _DS2S 0xA1
+#define _DS2E 0xFE
+
+#elif _CODE_PAGE == 437 /* U.S. (OEM) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 720 /* Arabic (OEM) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 737 /* Greek (OEM) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
+ 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 775 /* Baltic (OEM) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+ 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+ 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 852 /* Latin 2 (OEM) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \
+ 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}
+
+#elif _CODE_PAGE == 855 /* Cyrillic (OEM) */
+#define _DF1S 0
+#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
+ 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \
+ 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 857 /* Turkish (OEM) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
+ 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+ 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 862 /* Hebrew (OEM) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 866 /* Russian (OEM) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
+ 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}
+
+#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
+ 0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF}
+
+#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \
+ 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}
+
+#elif _CODE_PAGE == 1253 /* Greek (Windows) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \
+ 0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF}
+
+#elif _CODE_PAGE == 1254 /* Turkish (Windows) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \
+ 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}
+
+#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 1256 /* Arabic (Windows) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \
+ 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 1257 /* Baltic (Windows) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+ 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}
+
+#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */
+#define _DF1S 0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \
+ 0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+ 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F}
+
+#elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */
+#define _DF1S 0
+
+#else
+#error Unknown code page
+
+#endif
+
+
+
+/* Definitions corresponds to volume management */
+
+#if _MULTI_PARTITION /* Multiple partition configuration */
+#define LD2PD(drv) (Drives[drv].pd) /* Get physical drive# */
+#define LD2PT(drv) (Drives[drv].pt) /* Get partition# */
+typedef struct {
+ BYTE pd; /* Physical drive# */
+ BYTE pt; /* Partition # (0-3) */
+} PARTITION;
+extern const PARTITION Drives[]; /* Logical drive# to physical location conversion table */
+
+#else /* Single partition configuration */
+#define LD2PD(drv) (drv) /* Physical drive# is equal to the logical drive# */
+#define LD2PT(drv) 0 /* Always mounts the 1st partition */
+
+#endif
+
+
+
+/* Type of path name strings on FatFs API */
+
+#if _LFN_UNICODE /* Unicode string */
+#if !_USE_LFN
+#error _LFN_UNICODE must be 0 in non-LFN cfg.
+#endif
+#ifndef _INC_TCHAR
+typedef WCHAR TCHAR;
+#define _T(x) L ## x
+#define _TEXT(x) L ## x
+#endif
+
+#else /* ANSI/OEM string */
+#ifndef _INC_TCHAR
+typedef char TCHAR;
+#define _T(x) x
+#define _TEXT(x) x
+#endif
+
+#endif
+
+
+
+/* Definitions corresponds to file shareing feature */
+
+#if _FS_SHARE
+#if _FS_READONLY
+#error _FS_SHARE must be 0 on R/O cfg.
+#endif
+typedef struct {
+ DWORD clu; /* File ID 1, directory */
+ WORD idx; /* File ID 2, index in the directory */
+ WORD ctr; /* File open counter, 0:none, 0x01..0xFF:read open count, 0x100:in write open */
+} FILESEM;
+#endif
+
+
+
+/* File system object structure (FATFS) */
+
+typedef struct {
+ BYTE fs_type; /* FAT sub-type (0:Not mounted) */
+ BYTE drv; /* Physical drive number */
+ BYTE csize; /* Sectors per cluster (1,2,4...128) */
+ BYTE n_fats; /* Number of FAT copies (1,2) */
+ BYTE wflag; /* win[] dirty flag (1:must be written back) */
+ BYTE fsi_flag; /* fsinfo dirty flag (1:must be written back) */
+ WORD id; /* File system mount ID */
+ WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
+#if _MAX_SS != 512
+ WORD ssize; /* Bytes per sector (512,1024,2048,4096) */
+#endif
+#if _FS_REENTRANT
+ _SYNC_t sobj; /* Identifier of sync object */
+#endif
+#if !_FS_READONLY
+ DWORD last_clust; /* Last allocated cluster */
+ DWORD free_clust; /* Number of free clusters */
+ DWORD fsi_sector; /* fsinfo sector (FAT32) */
+#endif
+#if _FS_RPATH
+ DWORD cdir; /* Current directory start cluster (0:root) */
+#endif
+ DWORD n_fatent; /* Number of FAT entries (= number of clusters + 2) */
+ DWORD fsize; /* Sectors per FAT */
+ DWORD fatbase; /* FAT start sector */
+ DWORD dirbase; /* Root directory start sector (FAT32:Cluster#) */
+ DWORD database; /* Data start sector */
+ DWORD winsect; /* Current sector appearing in the win[] */
+ BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and Data on tiny cfg) */
+#if _FS_SHARE
+ FILESEM flsem[_FS_SHARE]; /* File lock semaphores */
+#endif
+} FATFS;
+
+
+
+/* File object structure (FIL) */
+
+typedef struct {
+ FATFS* fs; /* Pointer to the owner file system object */
+ WORD id; /* Owner file system mount ID */
+ BYTE flag; /* File status flags */
+ BYTE pad1;
+ DWORD fptr; /* File read/write pointer */
+ DWORD fsize; /* File size */
+ DWORD org_clust; /* File start cluster (0 when fsize==0) */
+ DWORD curr_clust; /* Current cluster */
+ DWORD dsect; /* Current data sector */
+#if !_FS_READONLY
+ DWORD dir_sect; /* Sector containing the directory entry */
+ BYTE* dir_ptr; /* Ponter to the directory entry in the window */
+#endif
+#if _USE_FASTSEEK
+ DWORD* cltbl; /* Pointer to the cluster link map table */
+#endif
+#if _FS_SHARE
+ UINT lockid; /* File lock ID */
+#endif
+#if !_FS_TINY
+ BYTE buf[_MAX_SS]; /* File data read/write buffer */
+#endif
+} FAT_FIL;
+
+
+
+/* Directory object structure (FAT_DIR) */
+
+typedef struct {
+ FATFS* fs; /* Pointer to the owner file system object */
+ WORD id; /* Owner file system mount ID */
+ WORD index; /* Current read/write index number */
+ DWORD sclust; /* Table start cluster (0:Root dir) */
+ DWORD clust; /* Current cluster */
+ DWORD sect; /* Current sector */
+ BYTE* dir; /* Pointer to the current SFN entry in the win[] */
+ BYTE* fn; /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
+#if _USE_LFN
+ WCHAR* lfn; /* Pointer to the LFN working buffer */
+ WORD lfn_idx; /* Last matched LFN index number (0xFFFF:No LFN) */
+#endif
+} FAT_DIR;
+
+
+
+/* File status structure (FILINFO) */
+
+typedef struct {
+ DWORD fsize; /* File size */
+ WORD fdate; /* Last modified date */
+ WORD ftime; /* Last modified time */
+ BYTE fattrib; /* Attribute */
+ TCHAR fname[13]; /* Short file name (8.3 format) */
+#if _USE_LFN
+ TCHAR* lfname; /* Pointer to the LFN buffer */
+ int lfsize; /* Size of LFN buffer [chrs] */
+#endif
+} FILINFO;
+
+
+
+/* File function return code (FRESULT) */
+
+typedef enum {
+ FR_OK = 0, /* (0) Succeeded */
+ FR_DISK_ERR, /* (1) A hard error occured in the low level disk I/O layer */
+ FR_INT_ERR, /* (2) Assertion failed */
+ FR_NOT_READY, /* (3) The physical drive cannot work */
+ FR_NO_FILE, /* (4) Could not find the file */
+ FR_NO_PATH, /* (5) Could not find the path */
+ FR_INVALID_NAME, /* (6) The path name format is invalid */
+ FR_DENIED, /* (7) Acces denied due to prohibited access or directory full */
+ FR_EXIST, /* (8) Acces denied due to prohibited access */
+ FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
+ FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
+ FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
+ FR_NOT_ENABLED, /* (12) The volume has no work area */
+ FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume on the physical drive */
+ FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any parameter error */
+ FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
+ FR_LOCKED, /* (16) The operation is rejected according to the file shareing policy */
+ FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
+ FR_TOO_MANY_OPEN_FILES /* (18) Number of open files > _FS_SHARE */
+} FRESULT;
+
+
+
+/*--------------------------------------------------------------*/
+/* FatFs module application interface */
+
+FRESULT f_mount (BYTE, FATFS*); /* Mount/Unmount a logical drive */
+FRESULT f_open (FAT_FIL*, const TCHAR*, BYTE); /* Open or create a file */
+FRESULT f_read (FAT_FIL*, void*, UINT, UINT*); /* Read data from a file */
+FRESULT f_lseek (FAT_FIL*, DWORD); /* Move file pointer of a file object */
+FRESULT f_close (FAT_FIL*); /* Close an open file object */
+FRESULT f_opendir (FAT_DIR*, const TCHAR*); /* Open an existing directory */
+FRESULT f_readdir (FAT_DIR*, FILINFO*); /* Read a directory item */
+FRESULT f_stat (const TCHAR*, FILINFO*); /* Get file status */
+#if !_FS_READONLY
+FRESULT f_write (FAT_FIL*, const void*, UINT, UINT*); /* Write data to a file */
+FRESULT f_getfree (const TCHAR*, DWORD*, FATFS**); /* Get number of free clusters on the drive */
+FRESULT f_truncate (FAT_FIL*); /* Truncate file */
+FRESULT f_sync (FAT_FIL*); /* Flush cached data of a writing file */
+FRESULT f_unlink (const TCHAR*); /* Delete an existing file or directory */
+FRESULT f_mkdir (const TCHAR*); /* Create a new directory */
+FRESULT f_chmod (const TCHAR*, BYTE, BYTE); /* Change attriburte of the file/dir */
+FRESULT f_utime (const TCHAR*, const FILINFO*); /* Change timestamp of the file/dir */
+FRESULT f_rename (const TCHAR*, const TCHAR*); /* Rename/Move a file or directory */
+#endif
+#if _USE_FORWARD
+FRESULT f_forward (FAT_FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*); /* Forward data to the stream */
+#endif
+#if _USE_MKFS
+FRESULT f_mkfs (BYTE, BYTE, UINT); /* Create a file system on the drive */
+#endif
+#if _FS_RPATH
+FRESULT f_chdir (const TCHAR*); /* Change current directory */
+FRESULT f_chdrive (BYTE); /* Change current drive */
+#endif
+#if _USE_STRFUNC
+int f_putc (TCHAR, FAT_FIL*); /* Put a character to the file */
+int f_puts (const TCHAR*, FAT_FIL*); /* Put a string to the file */
+int f_printf (FAT_FIL*, const TCHAR*, ...); /* Put a formatted string to the file */
+TCHAR* f_gets (TCHAR*, int, FAT_FIL*); /* Get a string from the file */
+#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0)
+#define f_error(fp) (((fp)->flag & FA__ERROR) ? 1 : 0)
+#ifndef EOF
+#define EOF (-1)
+#endif
+#endif
+
+
+
+/*--------------------------------------------------------------*/
+/* Additional user defined functions */
+
+/* RTC function */
+#if !_FS_READONLY
+DWORD get_fattime (void);
+#endif
+
+/* Unicode support functions */
+#if _USE_LFN /* Unicode - OEM code conversion */
+WCHAR ff_convert (WCHAR, UINT); /* OEM-Unicode bidirectional conversion */
+WCHAR ff_wtoupper (WCHAR); /* Unicode upper-case conversion */
+#if _USE_LFN == 3 /* Memory functions */
+void* ff_memalloc (UINT); /* Allocate memory block */
+void ff_memfree (void*); /* Free memory block */
+#endif
+#endif
+
+/* Sync functions */
+#if _FS_REENTRANT
+int ff_cre_syncobj (BYTE, _SYNC_t*);/* Create a sync object */
+int ff_del_syncobj (_SYNC_t); /* Delete a sync object */
+int ff_req_grant (_SYNC_t); /* Lock sync object */
+void ff_rel_grant (_SYNC_t); /* Unlock sync object */
+#endif
+
+
+
+
+/*--------------------------------------------------------------*/
+/* Flags and offset address */
+
+
+/* File access control and file status flags (FIL.flag) */
+
+#define FA_READ 0x01
+#define FA_OPEN_EXISTING 0x00
+#define FA__ERROR 0x80
+
+#if !_FS_READONLY
+#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
+
+
+/* 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 */
+#define AM_MASK 0x3F /* Mask of defined bits */
+
+
+/* Fast seek function */
+#define CREATE_LINKMAP 0xFFFFFFFF
+
+
+/* FatFs refers the members in the FAT structures with byte offset instead of
+/ structure member because there are incompatibility of the packing option
+/ between various compilers. */
+
+#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
+#define LDIR_Ord 0
+#define LDIR_Attr 11
+#define LDIR_Type 12
+#define LDIR_Chksum 13
+#define LDIR_FstClusLO 26
+
+
+
+/*--------------------------------*/
+/* Multi-byte word access macros */
+
+#if _WORD_ACCESS == 1 /* Enable word access to the FAT structure */
+#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)
+#else /* Use byte-by-byte access to the FAT structure */
+#define LD_WORD(ptr) (WORD)(((WORD)*(BYTE*)((ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))
+#define LD_DWORD(ptr) (DWORD)(((DWORD)*(BYTE*)((ptr)+3)<<24)|((DWORD)*(BYTE*)((ptr)+2)<<16)|((WORD)*(BYTE*)((ptr)+1)<<8)|*(BYTE*)(ptr))
+#define ST_WORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8)
+#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=(BYTE)(val); *(BYTE*)((ptr)+1)=(BYTE)((WORD)(val)>>8); *(BYTE*)((ptr)+2)=(BYTE)((DWORD)(val)>>16); *(BYTE*)((ptr)+3)=(BYTE)((DWORD)(val)>>24)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FATFS */
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FATFileSystem/Core/ffconf.h Mon Feb 13 02:11:20 2012 +0000 @@ -0,0 +1,163 @@ +//Modified by Thomas Hamilton, Copyright 2010 + +/*---------------------------------------------------------------------------/ +/ FatFs - FAT file system module configuration file R0.08 (C)ChaN, 2010 / +/----------------------------------------------------------------------------/ +/ +/ CAUTION! Do not forget to make clean the project after any changes to +/ the configuration options. +/ +/---------------------------------------------------------------------------*/ + +#ifndef _FFCONF +#define _FFCONF 8085 /* Revision ID */ + +/*---------------------------------------------------------------------------/ +/ Function and Buffer Configurations / +/---------------------------------------------------------------------------*/ + +#define _FS_TINY 0 /* 0:Normal or 1:Tiny */ +/* When _FS_TINY is set to 1, FatFs uses the sector buffer in the file system +/ object instead of the sector buffer in the individual file object for file +/ data transfer. This reduces memory consumption 512 bytes each file object. */ + +#define _FS_READONLY 0 /* 0:Read/Write or 1:Read only */ +/* 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 0 /* 0, 1, 2 or 3 */ +/* 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 2 /* 0:Disable or 1/2:Enable */ +/* To enable string functions, set _USE_STRFUNC to 1 or 2. */ + +#define _USE_MKFS 1 /* 0:Disable or 1:Enable */ +/* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */ + +#define _USE_FORWARD 0 /* 0:Disable or 1:Enable */ +/* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */ + +#define _USE_FASTSEEK 0 /* 0:Disable or 1:Enable */ +/* To enable fast seek feature, set _USE_FASTSEEK to 1. */ + +/*---------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/----------------------------------------------------------------------------*/ + +#define _CODE_PAGE 1 +/* The _CODE_PAGE specifies the OEM code page to be used on the target system. +/ Incorrect setting of the code page can cause a file open failure. +/ +/ 932 - Japanese Shift-JIS (DBCS, OEM, Windows) +/ 936 - Simplified Chinese GBK (DBCS, OEM, Windows) +/ 949 - Korean (DBCS, OEM, Windows) +/ 950 - Traditional Chinese Big5 (DBCS, OEM, Windows) +/ 1250 - Central Europe (Windows) +/ 1251 - Cyrillic (Windows) +/ 1252 - Latin 1 (Windows) +/ 1253 - Greek (Windows) +/ 1254 - Turkish (Windows) +/ 1255 - Hebrew (Windows) +/ 1256 - Arabic (Windows) +/ 1257 - Baltic (Windows) +/ 1258 - Vietnam (OEM, Windows) +/ 437 - U.S. (OEM) +/ 720 - Arabic (OEM) +/ 737 - Greek (OEM) +/ 775 - Baltic (OEM) +/ 850 - Multilingual Latin 1 (OEM) +/ 858 - Multilingual Latin 1 + Euro (OEM) +/ 852 - Latin 2 (OEM) +/ 855 - Cyrillic (OEM) +/ 866 - Russian (OEM) +/ 857 - Turkish (OEM) +/ 862 - Hebrew (OEM) +/ 874 - Thai (OEM, Windows) +/ 1 - ASCII only (Valid for non LFN cfg.) */ + +#define _USE_LFN 0 /* 0 to 3 */ +#define _MAX_LFN 255 /* Maximum LFN length to handle (12 to 255) */ +/* The _USE_LFN option switches the LFN support. +/ +/ 0: Disable LFN. _MAX_LFN and _LFN_UNICODE have no effect. +/ 1: Enable LFN with static working buffer on the bss. NOT REENTRANT. +/ 2: Enable LFN with dynamic working buffer on the STACK. +/ 3: Enable LFN with dynamic working buffer on the HEAP. +/ +/ The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. When enable LFN, +/ Unicode handling functions ff_convert() and ff_wtoupper() must be added +/ to the project. When enable to use heap, memory control functions +/ ff_memalloc() and ff_memfree() must be added to the project. */ + +#define _LFN_UNICODE 0 /* 0:ANSI/OEM or 1:Unicode */ +/* To switch the character code set on FatFs API to Unicode, +/ enable LFN feature and set _LFN_UNICODE to 1. */ + +#define _FS_RPATH 0 /* 0:Disable or 1:Enable */ +/* When _FS_RPATH is set to 1, relative path feature is enabled and f_chdir, +/ f_chdrive function are available. +/ Note that output of the f_readdir fnction is affected by this option. */ + +/*---------------------------------------------------------------------------/ +/ Physical Drive Configurations +/----------------------------------------------------------------------------*/ + +#define _DRIVES 7 +/* Number of volumes (logical drives) to be used. */ + +#define _MAX_SS 512 /* 512, 1024, 2048 or 4096 */ +/* Maximum sector size to be handled. +/ Always set 512 for memory card and hard disk but a larger value may be +/ required for floppy disk (512/1024) and optical disk (512/2048). +/ When _MAX_SS is larger than 512, GET_SECTOR_SIZE command must be implememted +/ to the disk_ioctl function. */ + +#define _MULTI_PARTITION 0 /* 0:Single parition or 1:Multiple partition */ +/* When _MULTI_PARTITION is set to 0, each volume is bound to the same physical +/ drive number and can mount only first primaly partition. When it is set to 1, +/ each volume is tied to the partitions listed in Drives[]. */ + +/*---------------------------------------------------------------------------/ +/ System Configurations +/----------------------------------------------------------------------------*/ + +#define _WORD_ACCESS 0 /* 0 or 1 */ +/* Set 0 first and it is always compatible with all platforms. The _WORD_ACCESS +/ option defines which access method is used to the word data on the FAT volume. +/ +/ 0: Byte-by-byte access. +/ 1: Word access. Do not choose this unless following condition is met. +/ +/ When the byte order on the memory is big-endian or address miss-aligned word +/ access results incorrect behavior, the _WORD_ACCESS must be set to 0. +/ If it is not the case, the value can also be set to 1 to improve the +/ performance and code size. */ + +#define _FS_REENTRANT 0 /* 0:Disable or 1:Enable */ +#define _FS_TIMEOUT 1024 /* Timeout period in unit of time ticks */ +#define _SYNC_t HANDLE /* O/S dependent type of sync object. + e.g. HANDLE, OS_EVENT*, ID and etc.. */ +/* Include a header file here to define O/S system calls */ +/* #include <windows.h>, <ucos_ii.h.h>, <semphr.h> or ohters. */ + +/* The _FS_REENTRANT option switches the reentrancy of the FatFs module. +/ +/ 0: Disable reentrancy. _SYNC_t and _FS_TIMEOUT have no effect. +/ 1: Enable reentrancy. Also user provided synchronization handlers, +/ ff_req_grant, ff_rel_grant, ff_del_syncobj and ff_cre_syncobj +/ function must be added to the project. */ + +#define _FS_SHARE 0 /* 0:Disable or >=1:Enable */ +/* To enable file sharing feature, set _FS_SHARE to >= 1 and also user + provided memory handlers, ff_memalloc and ff_memfree function must be + added to the project. The value defines number of files can be opened + per volume. */ + +#endif \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FATFileSystem/Core/integer.h Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,35 @@
+//Modified by Thomas Hamilton, Copyright 2010
+
+/*--------------------------------------------------------------------*/
+/* Integer type definitions for FatFs module R0.08 (C)ChaN, 2010 */
+/*--------------------------------------------------------------------*/
+
+#ifndef _INTEGER
+#define _INTEGER
+
+#include "stdint.h"
+
+/* 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 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;
+typedef unsigned short WCHAR;
+
+/* 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;
+
+#endif
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FATFileSystem/Interface/FATDirHandle.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,50 @@
+/* mbed Microcontroller Library - FATDirHandle
+ Copyright (c) 2008, sford */
+
+//Modified by Thomas Hamilton, Copyright 2010
+
+#include "FATDirHandle.h"
+
+FATDirHandle::FATDirHandle(FAT_DIR InputDirStr)
+{
+ DirectoryObject = InputDirStr;
+}
+
+int FATDirHandle::closedir()
+{
+ delete this;
+ return 0;
+}
+
+struct dirent* FATDirHandle::readdir()
+{
+ FILINFO FileInfo;
+ FRESULT Result = f_readdir(&DirectoryObject, &FileInfo);
+ if (Result || !FileInfo.fname[0])
+ {
+ return NULL;
+ }
+ else
+ {
+ for (unsigned char i = 0; i < 13; i++)
+ {
+ CurrentEntry.d_name[i] = ((char*)FileInfo.fname)[i];
+ }
+ return &CurrentEntry;
+ }
+}
+
+void FATDirHandle::rewinddir()
+{
+ DirectoryObject.index = 0;
+}
+
+off_t FATDirHandle::telldir()
+{
+ return (off_t)DirectoryObject.index;
+}
+
+void FATDirHandle::seekdir(off_t location)
+{
+ DirectoryObject.index = (WORD)location;
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FATFileSystem/Interface/FATDirHandle.h Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,30 @@
+/* mbed Microcontroller Library - FATDirHandle
+ Copyright (c) 2008, sford */
+
+//Modified by Thomas Hamilton, Copyright 2010
+
+#ifndef MBED_FATDIRHANDLE_H
+#define MBED_FATDIRHANDLE_H
+
+#include "stdint.h"
+#include "ff.h"
+#include "mbed.h"
+#include "DirHandle.h"
+#include <stdio.h>
+
+class FATDirHandle : public DirHandle
+{
+ private:
+ FAT_DIR DirectoryObject;
+ struct dirent CurrentEntry;
+
+ public:
+ FATDirHandle(FAT_DIR InputDirStr);
+ virtual int closedir();
+ virtual struct dirent* readdir();
+ virtual void rewinddir();
+ virtual off_t telldir();
+ virtual void seekdir(off_t location);
+};
+
+#endif
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FATFileSystem/Interface/FATFileHandle.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,89 @@
+/* mbed Microcontroller Library - FATFileHandle
+ Copyright (c) 2008, sford */
+
+//Modified by Thomas Hamilton, Copyright 2010
+
+#include "FATFileHandle.h"
+
+FATFileHandle::FATFileHandle(FAT_FIL InputFilStr)
+{
+ FileObject = InputFilStr;
+}
+
+ssize_t FATFileHandle::write(const void* buffer, size_t length)
+{
+ UINT ByteWritten;
+ if (f_write(&FileObject, buffer, (UINT)length, &ByteWritten))
+ {
+ return -1;
+ }
+ else
+ {
+ return (ssize_t)ByteWritten;
+ }
+}
+
+int FATFileHandle::close()
+{
+ if (f_close(&FileObject))
+ {
+ return -1;
+ }
+ else
+ {
+ delete this;
+ return 0;
+ }
+}
+
+ssize_t FATFileHandle::read(void* buffer, size_t length)
+{
+ UINT ByteRead;
+ if (f_read(&FileObject, buffer, (UINT)length, &ByteRead))
+ {
+ return -1;
+ }
+ else
+ {
+ return (ssize_t)ByteRead;
+ }
+}
+
+int FATFileHandle::isatty()
+{
+ return 0;
+}
+
+off_t FATFileHandle::lseek(off_t offset, int whence)
+{
+ switch (whence)
+ {
+ case SEEK_CUR: offset += FileObject.fptr; break;
+ case SEEK_END: offset += FileObject.fsize; break;
+ }
+ if (f_lseek(&FileObject, (DWORD)offset))
+ {
+ return -1;
+ }
+ else
+ {
+ return (off_t)FileObject.fptr;
+ }
+}
+
+int FATFileHandle::fsync()
+{
+ if (f_sync(&FileObject))
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+off_t FATFileHandle::flen()
+{
+ return (off_t)FileObject.fsize;
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FATFileSystem/Interface/FATFileHandle.h Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,31 @@
+/* mbed Microcontroller Library - FATFileHandle
+ Copyright (c) 2008, sford */
+
+//Modified by Thomas Hamilton, Copyright 2010
+
+#ifndef MBED_FATFILEHANDLE_H
+#define MBED_FATFILEHANDLE_H
+
+#include "stdint.h"
+#include "ff.h"
+#include "mbed.h"
+#include "FileHandle.h"
+#include <stdio.h>
+
+class FATFileHandle : public FileHandle
+{
+ private:
+ FAT_FIL FileObject;
+
+ public:
+ FATFileHandle(FAT_FIL InputFilStr);
+ virtual ssize_t write(const void* buffer, size_t length);
+ virtual int close();
+ virtual ssize_t read(void* buffer, size_t length);
+ virtual int isatty();
+ virtual off_t lseek(off_t offset, int whence);
+ virtual int fsync();
+ virtual off_t flen();
+};
+
+#endif
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FATFileSystem/Interface/FATFileSystem.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,155 @@
+/* mbed Microcontroller Library - FATFileSystem
+ Copyright (c) 2008, sford */
+
+//Modified by Thomas Hamilton, Copyright 2010
+
+#include "FATFileSystem.h"
+
+DWORD get_fattime(void)
+{
+ return 35719201;
+}
+
+FATFileSystem* FATFileSystem::DriveArray[_DRIVES] = {0};
+
+FATFileSystem::FATFileSystem(const char* SystemName) : FileSystemLike(SystemName)
+{
+ for (unsigned char i = 0; i < _DRIVES; i++)
+ {
+ if(!DriveArray[i])
+ {
+ DriveArray[i] = this;
+ Drive = i;
+ f_mount((BYTE)i, &FileSystemObject);
+ return;
+ }
+ }
+}
+
+FATFileSystem::~FATFileSystem()
+{
+ for (unsigned char i = 0; i < _DRIVES; i++)
+ {
+ if (DriveArray[i] == this)
+ {
+ DriveArray[i] = NULL;
+ f_mount((BYTE)i, NULL);
+ }
+ }
+ delete this;
+}
+
+FileHandle* FATFileSystem::open(const char* filename, int flags)
+{
+ FAT_FIL FileObject;
+ char FileName[64];
+ BYTE ModeFlags = 0;
+
+ sprintf(FileName, "%d:/%s", Drive, filename);
+ switch (flags & 3)
+ {
+ case O_RDONLY: ModeFlags = FA_READ; break;
+ case O_WRONLY: ModeFlags = FA_WRITE; break;
+ case O_RDWR: ModeFlags = FA_READ | FA_WRITE; break;
+ }
+ if(flags & O_CREAT)
+ {
+ if(flags & O_TRUNC)
+ {
+ ModeFlags |= FA_CREATE_ALWAYS;
+ }
+ else
+ {
+ ModeFlags |= FA_OPEN_ALWAYS;
+ }
+ }
+ else
+ {
+ ModeFlags |= FA_OPEN_EXISTING;
+ }
+ if (f_open(&FileObject, (const TCHAR*)FileName, ModeFlags))
+ {
+ return NULL;
+ }
+ else
+ {
+ if (flags & O_APPEND)
+ {
+ f_lseek(&FileObject, (DWORD)FileObject.fsize);
+ }
+ return new FATFileHandle(FileObject);
+ }
+}
+
+int FATFileSystem::remove(const char* filename)
+{
+ char FileName[64];
+
+ sprintf(FileName, "%d:/%s", Drive, filename);
+ if (f_unlink((const TCHAR*)FileName))
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+int FATFileSystem::rename(const char* oldname, const char* newname)
+{
+ char OldName[64];
+
+ sprintf(OldName, "%d:/%s", Drive, oldname);
+ if (f_rename((const TCHAR*)OldName, (const TCHAR*)newname))
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+DirHandle* FATFileSystem::opendir(const char* name)
+{
+ FAT_DIR DirectoryObject;
+ char DirectoryName[64];
+
+ sprintf(DirectoryName, "%d:%s", Drive, name);
+ if (f_opendir(&DirectoryObject, (const TCHAR*)DirectoryName))
+ {
+ return NULL;
+ }
+ else
+ {
+ return new FATDirHandle(DirectoryObject);
+ }
+}
+
+int FATFileSystem::mkdir(const char* name, mode_t mode)
+{
+ char DirectoryName[64];
+
+ sprintf(DirectoryName, "%d:%s", Drive, name);
+ if (f_mkdir((const TCHAR*)DirectoryName))
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+int FATFileSystem::format(unsigned int allocationunit)
+{
+ if (f_mkfs(Drive, 0, allocationunit))
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FATFileSystem/Interface/FATFileSystem.h Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,49 @@
+/* mbed Microcontroller Library - FATFileSystem
+ Copyright (c) 2008, sford */
+
+//Modified by Thomas Hamilton, Copyright 2010
+
+#ifndef MBED_FATFILESYSTEM_H
+#define MBED_FATFILESYSTEM_H
+
+#include "stdint.h"
+#include "ff.h"
+#include "mbed.h"
+#include "FileSystemLike.h"
+#include "FATFileHandle.h"
+#include "FATDirHandle.h"
+#include <stdio.h>
+
+class FATFileSystem : public FileSystemLike
+{
+ private:
+ FATFS FileSystemObject;
+ unsigned char Drive;
+
+ public:
+ static FATFileSystem* DriveArray[_DRIVES];
+
+ FATFileSystem(const char* SystemName);
+ virtual ~FATFileSystem();
+
+ int format(unsigned int allocationunit);
+
+ virtual FileHandle* open(const char* filename, int flags);
+ virtual int remove(const char* filename);
+ virtual int rename(const char* oldname, const char* newname);
+ virtual DirHandle* opendir(const char* name);
+ virtual int mkdir(const char* name, mode_t mode);
+
+ virtual unsigned char disk_initialize() { return 0x00; }
+ virtual unsigned char disk_status() { return 0x00; }
+ virtual unsigned char disk_read(unsigned char* buff,
+ unsigned long sector, unsigned char count) = 0;
+ virtual unsigned char disk_write(const unsigned char* buff,
+ unsigned long sector, unsigned char count) = 0;
+ virtual unsigned char disk_sync() { return 0x00; }
+ virtual unsigned long disk_sector_count() = 0;
+ virtual unsigned short disk_sector_size() { return 512; }
+ virtual unsigned long disk_block_size() { return 1; }
+};
+
+#endif
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MODSERIAL/FLUSH.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,47 @@
+/*
+ Copyright (c) 2010 Andy Kirkham
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#include "MODSERIAL.h"
+#include "MACROS.h"
+
+namespace AjK {
+
+void
+MODSERIAL::flushBuffer(IrqType type)
+{
+ uint32_t ier = _IER;
+ switch(type) {
+ case TxIrq: _IER &= ~(1UL << 1); break;
+ case RxIrq: _IER &= ~(1UL << 0); break;
+ }
+ buffer_in[type] = 0;
+ buffer_out[type] = 0;
+ buffer_count[type] = 0;
+ buffer_overflow[type] = 0;
+ switch(type) {
+ case TxIrq: _FCR = MODSERIAL_FIFO_TX_RESET; break;
+ case RxIrq: _FCR = MODSERIAL_FIFO_RX_RESET; break;
+ }
+ _IER = ier;
+}
+
+}; // namespace AjK ends
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MODSERIAL/GETC.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,63 @@
+/*
+ Copyright (c) 2010 Andy Kirkham
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#include "MODSERIAL.h"
+#include "MACROS.h"
+
+namespace AjK {
+
+int
+MODSERIAL::__getc(bool block)
+{
+ // If no buffer is in use fall back to standard RX FIFO usage.
+ // Note, we must block in this case and ignore bool "block"
+ // so as to maintain compat with Mbed Serial.
+ if (buffer_size[RxIrq] == 0 || buffer[RxIrq] == (char *)NULL) {
+ while(! MODSERIAL_RBR_HAS_DATA ) ;
+ return (int)(_RBR & 0xFF);
+ }
+
+ if (block) { while ( MODSERIAL_RX_BUFFER_EMPTY ) ; } // Blocks.
+ else if ( MODSERIAL_RX_BUFFER_EMPTY ) return -1;
+
+ int c = buffer[RxIrq][buffer_out[RxIrq]];
+ buffer_out[RxIrq]++;
+ if (buffer_out[RxIrq] >= buffer_size[RxIrq]) {
+ buffer_out[RxIrq] = 0;
+ }
+
+ // If we have made space in the RX Buffer then copy over
+ // any characters in the RX FIFO that my reside there.
+ // Temporarily disable the RX IRQ so that we do not re-enter
+ // it under interrupts.
+ if ( ! MODSERIAL_RX_BUFFER_FULL ) {
+ uint32_t ier = _IER;
+ _IER &= ~(1UL << 0);
+ isr_rx();
+ _IER = ier;
+ }
+
+ buffer_count[RxIrq]--;
+ return c;
+}
+
+}; // namespace AjK ends
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MODSERIAL/INIT.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,74 @@
+/*
+ Copyright (c) 2010 Andy Kirkham
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#include "MODSERIAL.h"
+#include "MACROS.h"
+
+namespace AjK {
+
+void
+MODSERIAL::init(int txSize, int rxSize)
+{
+ disableIrq();
+
+ callbackInfo.setSerial(this);
+
+ switch(_uidx) {
+ case 0: _base = LPC_UART0; break;
+ case 1: _base = LPC_UART1; break;
+ case 2: _base = LPC_UART2; break;
+ case 3: _base = LPC_UART3; break;
+ default : _base = NULL; break;
+ }
+
+ dmaSendChannel = -1;
+ moddma_p = (void *)NULL;
+
+ if (_base != NULL) {
+ buffer_size[RxIrq] = rxSize;
+ buffer[RxIrq] = rxSize > 0 ? (char *)malloc(buffer_size[RxIrq]) : (char *)NULL;
+ buffer_in[RxIrq] = 0;
+ buffer_out[RxIrq] = 0;
+ buffer_count[RxIrq] = 0;
+ buffer_overflow[RxIrq] = 0;
+ Serial::attach(this, &MODSERIAL::isr_rx, Serial::RxIrq);
+
+ buffer_size[TxIrq] = txSize;
+ buffer[TxIrq] = txSize > 0 ? (char *)malloc(buffer_size[TxIrq]) : (char *)NULL;
+ buffer_in[TxIrq] = 0;
+ buffer_out[TxIrq] = 0;
+ buffer_count[TxIrq] = 0;
+ buffer_overflow[TxIrq] = 0;
+ Serial::attach(this, &MODSERIAL::isr_tx, Serial::TxIrq);
+ }
+ else {
+ error("MODSERIAL must have a defined UART to function.");
+ }
+
+ _FCR = MODSERIAL_FIFO_ENABLE | MODSERIAL_FIFO_RX_RESET | MODSERIAL_FIFO_TX_RESET;
+
+ auto_detect_char = 0;
+
+ enableIrq();
+}
+
+}; // namespace AjK ends
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MODSERIAL/ISR_RX.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,60 @@
+/*
+ Copyright (c) 2010 Andy Kirkham
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#include "MODSERIAL.h"
+#include "MACROS.h"
+
+namespace AjK {
+
+void
+MODSERIAL::isr_rx(void)
+{
+ if (! _base || buffer_size[RxIrq] == 0 || buffer[RxIrq] == (char *)NULL) {
+ _isr[RxIrq].call(&this->callbackInfo);
+ return;
+ }
+
+ while( MODSERIAL_RBR_HAS_DATA ) {
+ rxc = (char)(_RBR & 0xFF);
+ if ( MODSERIAL_RX_BUFFER_FULL ) {
+ buffer_overflow[RxIrq] = rxc; // Oh dear, no room in buffer.
+ _isr[RxOvIrq].call(&this->callbackInfo);
+ }
+ else {
+ if (buffer[RxIrq] != (char *)NULL) {
+ buffer[RxIrq][buffer_in[RxIrq]] = rxc;
+ buffer_count[RxIrq]++;
+ buffer_in[RxIrq]++;
+ if (buffer_in[RxIrq] >= buffer_size[RxIrq]) {
+ buffer_in[RxIrq] = 0;
+ }
+ }
+ _isr[RxIrq].call(&this->callbackInfo);
+ }
+ if (auto_detect_char == rxc) {
+ _isr[RxAutoDetect].call(&this->callbackInfo);
+ }
+ }
+}
+
+}; // namespace AjK ends
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MODSERIAL/ISR_TX.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,54 @@
+/*
+ Copyright (c) 2010 Andy Kirkham
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#include "MODSERIAL.h"
+#include "MACROS.h"
+
+namespace AjK {
+
+void
+MODSERIAL::isr_tx(bool doCallback)
+{
+ if (! _base || buffer_size[TxIrq] == 0 || buffer[TxIrq] == (char *)NULL) {
+ _isr[TxIrq].call(&this->callbackInfo);
+ return;
+ }
+
+ while (! MODSERIAL_TX_BUFFER_EMPTY && MODSERIAL_THR_HAS_SPACE ) {
+ _THR = txc = (uint8_t)(buffer[TxIrq][buffer_out[TxIrq]]);
+ buffer_count[TxIrq]--;
+ buffer_out[TxIrq]++;
+ if (buffer_out[TxIrq] >= buffer_size[TxIrq]) {
+ buffer_out[TxIrq] = 0;
+ }
+ if (doCallback) _isr[TxIrq].call(&this->callbackInfo);
+ }
+
+ if ( MODSERIAL_TX_BUFFER_EMPTY ) {
+ _IER = 1;
+ _isr[TxEmpty].call(&this->callbackInfo);
+ }
+}
+
+}; // namespace AjK ends
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MODSERIAL/MACROS.h Mon Feb 13 02:11:20 2012 +0000 @@ -0,0 +1,70 @@ +/* + Copyright (c) 2010 Andy Kirkham + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef MODSERIAL_MACROS_H +#define MODSERIAL_MACROS_H + +#define MODSERIAL_RBR 0x00 +#define MODSERIAL_THR 0x00 +#define MODSERIAL_DLL 0x00 +#define MODSERIAL_IER 0x04 +#define MODSERIAL_DML 0x04 +#define MODSERIAL_IIR 0x08 +#define MODSERIAL_FCR 0x08 +#define MODSERIAL_LCR 0x0C +#define MODSERIAL_LSR 0x14 +#define MODSERIAL_SCR 0x1C +#define MODSERIAL_ACR 0x20 +#define MODSERIAL_ICR 0x24 +#define MODSERIAL_FDR 0x28 +#define MODSERIAL_TER 0x30 + +#define MODSERIAL_LSR_RDR (1UL << 0) +#define MODSERIAL_LSR_OE (1UL << 1) +#define MODSERIAL_LSR_PE (1UL << 2) +#define MODSERIAL_LSR_FE (1UL << 3) +#define MODSERIAL_LSR_BR (1UL << 4) +#define MODSERIAL_LSR_THRE (1UL << 5) +#define MODSERIAL_LSR_TEMT (1UL << 6) +#define MODSERIAL_LSR_RXFE (1UL << 7) + +#define MODSERIAL_FIFO_ENABLE 1 +#define MODSERIAL_FIFO_RX_RESET 2 +#define MODSERIAL_FIFO_TX_RESET 4 + +#define _RBR *((char *)_base+MODSERIAL_RBR) +#define _THR *((char *)_base+MODSERIAL_THR) +#define _IIR *((char *)_base+MODSERIAL_IIR) +#define _IER *((char *)_base+MODSERIAL_IER) +#define _LSR *((char *)_base+MODSERIAL_LSR) +#define _FCR *((char *)_base+MODSERIAL_FCR) + +#define MODSERIAL_TX_BUFFER_EMPTY (buffer_count[TxIrq]==0) +#define MODSERIAL_RX_BUFFER_EMPTY (buffer_count[RxIrq]==0) +#define MODSERIAL_TX_BUFFER_FULL (buffer_count[TxIrq]==buffer_size[TxIrq]) +#define MODSERIAL_RX_BUFFER_FULL (buffer_count[RxIrq]==buffer_size[RxIrq]) + +#define MODSERIAL_THR_HAS_SPACE ((int)_LSR&MODSERIAL_LSR_THRE) +#define MODSERIAL_TEMT_IS_EMPTY ((int)_LSR&MODSERIAL_LSR_TEMT) +#define MODSERIAL_RBR_HAS_DATA ((int)_LSR&MODSERIAL_LSR_RDR) + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MODSERIAL/MODSERIAL.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,128 @@
+/*
+ Copyright (c) 2010 Andy Kirkham
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+ @file MODSERIAL.h
+ @purpose Extends Serial to provide fully buffered IO
+ @version 1.6
+ @date Nov 2010
+ @author Andy Kirkham
+*/
+
+#include "MODSERIAL.h"
+#include "MACROS.h"
+
+namespace AjK {
+
+MODSERIAL::MODSERIAL(PinName tx, PinName rx, const char *name) : Serial(tx, rx, name)
+{
+ init(MODSERIAL_DEFAULT_TX_BUFFER_SIZE, MODSERIAL_DEFAULT_RX_BUFFER_SIZE);
+}
+
+MODSERIAL::MODSERIAL(PinName tx, PinName rx, int bufferSize, const char *name) : Serial(tx, rx, name)
+{
+ init(bufferSize, bufferSize);
+}
+
+MODSERIAL::MODSERIAL(PinName tx, PinName rx, int txSize, int rxSize, const char *name) : Serial(tx, rx, name)
+{
+ init(txSize, rxSize);
+}
+
+MODSERIAL::~MODSERIAL()
+{
+ disableIrq();
+ if (buffer[0] != NULL) free((char *)buffer[0]);
+ if (buffer[1] != NULL) free((char *)buffer[1]);
+}
+
+bool
+MODSERIAL::txBufferFull(void)
+{
+ return MODSERIAL_TX_BUFFER_FULL;
+}
+
+bool
+MODSERIAL::rxBufferFull(void)
+{
+ return MODSERIAL_RX_BUFFER_FULL;
+}
+
+bool
+MODSERIAL::txBufferEmpty(void)
+{
+ return MODSERIAL_TX_BUFFER_EMPTY;
+}
+
+bool
+MODSERIAL::rxBufferEmpty(void)
+{
+ return MODSERIAL_RX_BUFFER_EMPTY;
+}
+
+bool
+MODSERIAL::txIsBusy(void)
+{
+ return (_LSR & (3UL << 5) == 0) ? true : false;
+}
+
+void
+MODSERIAL::disableIrq(void)
+{
+ switch(_uidx) {
+ case 0: NVIC_DisableIRQ(UART0_IRQn); break;
+ case 1: NVIC_DisableIRQ(UART1_IRQn); break;
+ case 2: NVIC_DisableIRQ(UART2_IRQn); break;
+ case 3: NVIC_DisableIRQ(UART3_IRQn); break;
+ }
+}
+
+void
+MODSERIAL::enableIrq(void)
+{
+ switch(_uidx) {
+ case 0: NVIC_EnableIRQ(UART0_IRQn); break;
+ case 1: NVIC_EnableIRQ(UART1_IRQn); break;
+ case 2: NVIC_EnableIRQ(UART2_IRQn); break;
+ case 3: NVIC_EnableIRQ(UART3_IRQn); break;
+ }
+}
+
+int
+MODSERIAL::rxDiscardLastChar(void)
+{
+ // This function can only be called indirectly from
+ // an rxCallback function. Therefore, we know we
+ // just placed a char into the buffer.
+ char c = buffer[RxIrq][buffer_in[RxIrq]];
+
+ if (buffer_count[RxIrq]) {
+ buffer_count[RxIrq]--;
+ buffer_in[RxIrq]--;
+ if (buffer_in[RxIrq] < 0) {
+ buffer_in[RxIrq] = buffer_size[RxIrq] - 1;
+ }
+ }
+
+ return (int)c;
+}
+
+
+}; // namespace AjK ends
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MODSERIAL/MODSERIAL.h Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,1091 @@
+/*
+ Copyright (c) 2010 Andy Kirkham
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+ @file MODSERIAL.h
+ @purpose Extends Serial to provide fully buffered IO
+ @version see ChangeLog.c
+ @date Nov 2010
+ @author Andy Kirkham
+*/
+
+#ifndef MODSERIAL_H
+#define MODSERIAL_H
+
+/** @defgroup API The MODSERIAL API */
+/** @defgroup MISC Misc MODSERIAL functions */
+/** @defgroup INTERNALS MODSERIAL Internals */
+
+#ifndef MODSERIAL_DEFAULT_RX_BUFFER_SIZE
+#define MODSERIAL_DEFAULT_RX_BUFFER_SIZE 256
+#endif
+
+#ifndef MODSERIAL_DEFAULT_TX_BUFFER_SIZE
+#define MODSERIAL_DEFAULT_TX_BUFFER_SIZE 256
+#endif
+
+#include "mbed.h"
+
+namespace AjK {
+
+// Forward reference.
+class MODSERIAL;
+
+/**
+ * @author Andy Kirkham
+ * @see http://mbed.org/cookbook/MODSERIAL
+ * @see example3a.cpp
+ * @see example3b.cpp
+ * @see API
+ *
+ * <b>MODSERIAL_IRQ_INFO</b> is a class used to pass information (and access to protected
+ * MODSERIAL functions) to IRQ callbacks.
+ */
+class MODSERIAL_IRQ_INFO
+{
+public:
+ friend class MODSERIAL;
+
+ MODSERIAL *serial;
+
+ MODSERIAL_IRQ_INFO() { serial = 0; }
+
+ /** rxDiscardLastChar()
+ *
+ * Remove the last char placed into the rx buffer.
+ * This is an operation that can only be performed
+ * by an rxCallback function.
+ * @ingroup API
+ * @return The byte removed from the buffer.
+ */
+ int rxDiscardLastChar(void);
+
+protected:
+
+ /** setSerial()
+ *
+ * Used internally by MODSERIAL to set the "this" pointer
+ * of the MODSERIAL that created this object.
+ * @ingroup INTERNAL
+ * @param A pointer to a MODSERIAL object instance.
+ */
+ void setSerial(MODSERIAL *s) { serial = s; }
+};
+
+// Forward reference dummy class.
+class MODSERIAL_callback_dummy;
+
+/**
+ * @author Andy Kirkham
+ * @see http://mbed.org/cookbook/MODSERIAL
+ * @see example3a.cpp
+ * @see example3b.cpp
+ * @see API
+ *
+ * <b>MODSERIAL_callback</b> is a class used to hold application callbacks that
+ * MODSERIAL can invoke on certain events.
+ */
+class MODSERIAL_callback
+{
+protected:
+
+ //! C callback function pointer.
+ void (*c_callback)(MODSERIAL_IRQ_INFO *);
+
+ //! C++ callback object/method pointer (the object part).
+ MODSERIAL_callback_dummy *obj_callback;
+
+ //! C++ callback object/method pointer (the method part).
+ void (MODSERIAL_callback_dummy::*method_callback)(MODSERIAL_IRQ_INFO *);
+
+public:
+
+ /** Constructor
+ */
+ MODSERIAL_callback() {
+ c_callback = 0;
+ obj_callback = 0;
+ method_callback = 0;
+ }
+
+ /** attach - Overloaded attachment function.
+ *
+ * Attach a C type function pointer as the callback.
+ *
+ * Note, the callback function prototype must be:-
+ * @code
+ * void myCallbackFunction(MODSERIAL_IRQ_INFO *);
+ * @endcode
+ * @param A C function pointer to call.
+ */
+ void attach(void (*function)(MODSERIAL_IRQ_INFO *) = 0) { c_callback = function; }
+
+ /** attach - Overloaded attachment function.
+ *
+ * Attach a C++ type object/method pointer as the callback.
+ *
+ * Note, the callback method prototype must be:-
+ * @code
+ * public:
+ * void myCallbackFunction(MODSERIAL_IRQ_INFO *);
+ * @endcode
+ * @param A C++ object pointer.
+ * @param A C++ method within the object to call.
+ */
+ template<class T>
+ void attach(T* item, void (T::*method)(MODSERIAL_IRQ_INFO *)) {
+ obj_callback = (MODSERIAL_callback_dummy *)item;
+ method_callback = (void (MODSERIAL_callback_dummy::*)(MODSERIAL_IRQ_INFO *))method;
+ }
+
+ /** call - Overloaded callback initiator.
+ *
+ * call the callback function.
+ *
+ * @param A pointer to a MODSERIAL_IRQ_INFO object.
+ */
+ void call(MODSERIAL_IRQ_INFO *arg) {
+ if (c_callback != 0) {
+ (*c_callback)(arg);
+ }
+ else {
+ if (obj_callback != 0 && method_callback != 0) {
+ (obj_callback->*method_callback)(arg);
+ }
+ }
+ }
+};
+
+/**
+ * @author Andy Kirkham
+ * @see http://mbed.org/cookbook/MODSERIAL
+ * @see http://mbed.org/handbook/Serial
+ * @see example1.cpp
+ * @see example2.cpp
+ * @see example3a.cpp
+ * @see example3b.cpp
+ * @see example_dma.cpp
+ * @see API
+ *
+ * <b>MODSERIAL</b> extends the Mbed library <a href="/handbook/Serial">Serial</a> to provide fully buffered
+ * TX and RX streams. Buffer length is fully customisable.
+ *
+ * Before using MODSERIAL users should be familar with Mbed's standard <a href="/handbook/Serial">Serial</a>
+ * library object. MODSERIAL is a direct "drop in" replacement for <a href="/handbook/Serial">Serial</a>. Where
+ * previously Serial was used, MODSERIAL can be used as adirect replacement instantly offering standard
+ * TX and RX buffering. By default, both TX and RX buffers are 256 bytes in length.
+ *
+ * @image html /media/uploads/mbedofficial/serial_interfaces.png
+ *
+ * Standard example:
+ * @code
+ * #include "mbed.h"
+ * #include "MODSERIAL.h"
+ *
+ * MODSERIAL pc(USBTX, USBRX); // tx, rx
+ *
+ * int main() {
+ * pc.printf("Hello World!");
+ * while(1) {
+ * pc.putc(pc.getc() + 1);
+ * }
+ * }
+ * @endcode
+ *
+ * Example with alternate buffer length:
+ * @code
+ * #include "mbed.h"
+ * #include "MODSERIAL.h"
+ *
+ * // Make TX and RX buffers 512byes in length
+ * MODSERIAL pc(USBTX, USBRX, 512); // tx, rx
+ *
+ * int main() {
+ * pc.printf("Hello World!");
+ * while(1) {
+ * pc.putc(pc.getc() + 1);
+ * }
+ * }
+ * @endcode
+ *
+ * Example with alternate buffer length:
+ * @code
+ * #include "mbed.h"
+ * #include "MODSERIAL.h"
+ *
+ * // Make TX 1024bytes and RX 512byes in length
+ * MODSERIAL pc(USBTX, USBRX, 1024, 512); // tx, rx
+ *
+ * int main() {
+ * pc.printf("Hello World!");
+ * while(1) {
+ * pc.putc(pc.getc() + 1);
+ * }
+ * }
+ * @endcode
+ */
+class MODSERIAL : public Serial
+{
+public:
+
+ // Allow instances of MODSERIAL_IRQ_INFO to use protected properties and methods.
+ friend class MODSERIAL_IRQ_INFO;
+
+ //! A copy of the Serial parity enum
+ /** @see http://mbed.org/projects/libraries/api/mbed/trunk/Serial#Serial.format */
+ enum Parity {
+ None = 0
+ , Odd
+ , Even
+ , Forced1
+ , Forced0
+ };
+
+ //! A copy of the Serial IrqType enum
+ enum IrqType {
+ RxIrq = 0
+ , TxIrq
+ , RxOvIrq
+ , TxOvIrq
+ , TxEmpty
+ , RxAutoDetect
+ , NumOfIrqTypes
+ };
+
+ //! Non-blocking functions return code.
+ enum Result {
+ Ok = 0 /*!< Ok. */
+ , NoMemory = -1 /*!< Memory allocation failed. */
+ , NoChar = -1 /*!< No character in buffer. */
+ , BufferOversize = -2 /*!< Oversized buffer. */
+ };
+
+ /**
+ * The MODSERIAL constructor is used to initialise the serial object.
+ *
+ * @param tx PinName of the TX pin.
+ * @param rx PinName of the TX pin.
+ * @param name An option name for RPC usage.
+ */
+ MODSERIAL(PinName tx, PinName rx, const char *name = NULL);
+
+ /**
+ * The MODSERIAL constructor is used to initialise the serial object.
+ *
+ * @param tx PinName of the TX pin.
+ * @param rx PinName of the TX pin.
+ * @param bufferSize Integer of the TX and RX buffer sizes.
+ * @param name An option name for RPC usage.
+ */
+ MODSERIAL(PinName tx, PinName rx, int bufferSize, const char *name = NULL);
+
+ /**
+ * The MODSERIAL constructor is used to initialise the serial object.
+ *
+ * @param tx PinName of the TX pin.
+ * @param rx PinName of the TX pin.
+ * @param txBufferSize Integer of the TX buffer sizes.
+ * @param rxBufferSize Integer of the RX buffer sizes.
+ * @param name An option name for RPC usage.
+ */
+ MODSERIAL(PinName tx, PinName rx, int txBufferSize, int rxBufferSize, const char *name = NULL);
+
+ virtual ~MODSERIAL();
+
+ /**
+ * Function: attach
+ *
+ * The Mbed standard <a href="/handbook/Serial">Serial</a> library object allows an interrupt callback
+ * to be made when a byte is received by the TX or RX UART hardware. MODSERIAL traps these interrupts
+ * to enable it's buffering system. However, after the byte has been received/sent under interrupt control,
+ * MODSERIAL can callback a user function as a notification of the interrupt. Note, user code should not
+ * directly interact with the Uart hardware, MODSERIAL does that, instead, MODSERIAL API functions should
+ * be used.
+ *
+ * <b>Note</b>, a character is written out then, if there is room in the TX FIFO and the TX buffer is empty,
+ * putc() will put the character directly into THR (the output holding register). If the TX FIFO is full and
+ * cannot accept the character, it is placed into the TX output buffer. The TX interrupts are then enabled
+ * so that when the TX FIFO empties, the TX buffer is then transferred to the THR FIFO. The TxIrq will ONLY
+ * be activated when this transfer of a character from BUFFER to THR FIFO takes place. If your character
+ * throughput is not high bandwidth, then the 16 byte TX FIFO may be enough and the TX output buffer may
+ * never come into play.
+ *
+ * @code
+ * #include "mbed.h"
+ * #include "MODSERIAL.h"
+ *
+ * DigitalOut led1(LED1);
+ * DigitalOut led2(LED2);
+ * DigitalOut led3(LED3);
+ *
+ * // To test, connect p9 to p10 as a loopback.
+ * MODSERIAL pc(p9, p10);
+ *
+ * // This function is called when a character goes into the TX buffer.
+ * void txCallback(void) {
+ * led2 = !led2;
+ * }
+ *
+ * // This function is called when a character goes into the RX buffer.
+ * void rxCallback(void) {
+ * led3 = !led3;
+ * }
+ *
+ * int main() {
+ * pc.baud(115200);
+ * pc.attach(&txCallback, MODSERIAL::TxIrq);
+ * pc.attach(&rxCallback, MODSERIAL::RxIrq);
+ *
+ * while(1) {
+ * led1 = !led1;
+ * wait(0.5);
+ * pc.putc('A');
+ * wait(0.5);
+ * }
+ * ]
+ * @endcode
+ *
+ * @ingroup API
+ * @param fptr A pointer to a void function, or 0 to set as none
+ * @param type Which serial interrupt to attach the member function to (Seriall::RxIrq for receive, TxIrq for transmit buffer empty)
+ */
+ void attach(void (*fptr)(MODSERIAL_IRQ_INFO *), IrqType type = RxIrq) { _isr[type].attach(fptr); }
+
+ /**
+ * Function: attach
+ *
+ * The Mbed standard <a href="/handbook/Serial">Serial</a> library object allows an interrupt callback
+ * to be made when a byte is received by the TX or RX UART hardware. MODSERIAL traps these interrupts
+ * to enable it's buffering system. However, after the byte has been received/sent under interrupt control,
+ * MODSERIAL can callback a user function as a notification of the interrupt. Note, user code should not
+ * directly interact with the Uart hardware, MODSERIAL does that, instead, MODSERIAL API functions should
+ * be used.
+ *
+ * <b>Note</b>, a character is written out then, if there is room in the TX FIFO and the TX buffer is empty,
+ * putc() will put the character directly into THR (the output holding register). If the TX FIFO is full and
+ * cannot accept the character, it is placed into the TX output buffer. The TX interrupts are then enabled
+ * so that when the TX FIFO empties, the TX buffer is then transferred to the THR FIFO. The TxIrq will ONLY
+ * be activated when this transfer of a character from BUFFER to THR FIFO takes place. If your character
+ * throughput is not high bandwidth, then the 16 byte TX FIFO may be enough and the TX output buffer may
+ * never come into play.
+ *
+ * @code
+ * #include "mbed.h"
+ * #include "MODSERIAL.h"
+ *
+ * DigitalOut led1(LED1);
+ * DigitalOut led2(LED2);
+ * DigitalOut led3(LED3);
+ *
+ * // To test, connect p9 to p10 as a loopback.
+ * MODSERIAL pc(p9, p10);
+ *
+ * class Foo {
+ * public:
+ * // This method is called when a character goes into the TX buffer.
+ * void txCallback(void) { led2 = !led2; }
+ *
+ * // This method is called when a character goes into the RX buffer.
+ * void rxCallback(void) { led3 = !led3; }
+ * };
+ *
+ * Foo foo;
+ *
+ * int main() {
+ * pc.baud(115200);
+ * pc.attach(&foo, &Foo::txCallback, MODSERIAL::TxIrq);
+ * pc.attach(&foo, &Foo::rxCallback, MODSERIAL::RxIrq);
+ *
+ * while(1) {
+ * led1 = !led1;
+ * wait(0.5);
+ * pc.putc('A');
+ * wait(0.5);
+ * }
+ * ]
+ * @endcode
+ *
+ * @ingroup API
+ * @param tptr A pointer to the object to call the member function on
+ * @param mptr A pointer to the member function to be called
+ * @param type Which serial interrupt to attach the member function to (Seriall::RxIrq for receive, TxIrq for transmit buffer empty)
+ */
+ template<typename T>
+ void attach(T* tptr, void (T::*mptr)(MODSERIAL_IRQ_INFO *), IrqType type = RxIrq) {
+ if((mptr != 0) && (tptr != 0)) {
+ _isr[type].attach(tptr, mptr);
+ }
+ }
+
+ /**
+ * @see attach
+ * @ingroup API
+ */
+ void connect(void (*fptr)(MODSERIAL_IRQ_INFO *), IrqType type = RxIrq) { _isr[RxIrq].attach(fptr); }
+
+ /**
+ * @see attach
+ * @ingroup API
+ */
+ template<typename T>
+ void connect(T* tptr, void (T::*mptr)(MODSERIAL_IRQ_INFO *), IrqType type = RxIrq) {
+ if((mptr != 0) && (tptr != 0)) {
+ _isr[type].attach(tptr, mptr);
+ }
+ }
+
+ /**
+ * Function: writeable
+ *
+ * Determine if there is space available to write a byte
+ *
+ * @ingroup API
+ * @return 1 if there is space to write a character, else 0
+ */
+ int writeable() { return txBufferFull() ? 0 : 1; }
+
+ /**
+ * Function: readable
+ *
+ * Determine if there is a byte available to read
+ *
+ * @ingroup API
+ * @return 1 if there is a character available to read, else 0
+ */
+ int readable() { return rxBufferEmpty() ? 0 : 1; }
+
+ /**
+ * Function: txBufferSane
+ *
+ * Determine if the TX buffer has been initialized.
+ *
+ * @ingroup API
+ * @return true if the buffer is initialized, else false
+ */
+ bool txBufferSane(void) { return buffer[TxIrq] != (char *)NULL ? true : false; }
+
+ /**
+ * Function: rxBufferSane
+ *
+ * Determine if the RX buffer has been initialized.
+ *
+ * @ingroup API
+ * @return true if the buffer is initialized, else false
+ */
+ bool rxBufferSane(void) { return buffer[TxIrq] != (char *)NULL ? true : false; }
+
+ /**
+ * Function: txBufferGetCount
+ *
+ * Returns how many bytes are in the TX buffer
+ *
+ * @ingroup API
+ * @return The number of bytes in the TX buffer
+ */
+ int txBufferGetCount(void) { return buffer_count[TxIrq]; }
+
+ /**
+ * Function: rxBufferGetCount
+ *
+ * Returns how many bytes are in the RX buffer
+ *
+ * @ingroup API
+ * @return The number of bytes in the RX buffer
+ */
+ int rxBufferGetCount(void) { return buffer_count[RxIrq]; }
+
+ /**
+ * Function: txBufferGetSize
+ *
+ * Returns the current size of the TX buffer
+ *
+ * @ingroup API
+ * @return The length iof the TX buffer in bytes
+ */
+ int txBufferGetSize(int size) { return buffer_size[TxIrq]; }
+
+ /**
+ * Function: rxBufferGetSize
+ *
+ * Returns the current size of the RX buffer
+ *
+ * @ingroup API
+ * @return The length iof the RX buffer in bytes
+ */
+ int rxBufferGetSize(int size) { return buffer_size[RxIrq]; }
+
+ /**
+ * Function: txBufferFull
+ *
+ * Is the TX buffer full?
+ *
+ * @ingroup API
+ * @return true if the TX buffer is full, otherwise false
+ */
+ bool txBufferFull(void);
+
+ /**
+ * Function: rxBufferFull
+ *
+ * Is the RX buffer full?
+ *
+ * @ingroup API
+ * @return true if the RX buffer is full, otherwise false
+ */
+ bool rxBufferFull(void);
+
+ /**
+ * Function: txBufferEmpty
+ *
+ * Is the TX buffer empty?
+ *
+ * @ingroup API
+ * @return true if the TX buffer is empty, otherwise false
+ */
+ bool txBufferEmpty(void);
+
+ /**
+ * Function: rxBufferEmpty
+ *
+ * Is the RX buffer empty?
+ *
+ * @ingroup API
+ * @return true if the RX buffer is empty, otherwise false
+ */
+ bool rxBufferEmpty(void);
+
+ /**
+ * Function: txBufferSetSize
+ *
+ * Change the TX buffer size.
+ *
+ * @see Result
+ * @ingroup API
+ * @param size The new TX buffer size in bytes.
+ * @param m Perform a memory sanity check. Errs the Mbed if memory alloc fails.
+ * @return Result Ok on success.
+ */
+ int txBufferSetSize(int size, bool m) { return resizeBuffer(size, TxIrq, m); }
+
+ /**
+ * Function: rxBufferSetSize
+ *
+ * Change the RX buffer size.
+ *
+ * @see Result
+ * @ingroup API
+ * @param size The new RX buffer size in bytes.
+ * @param m Perform a memory sanity check. Errs the Mbed if memory alloc fails.
+ * @return Result Ok on success.
+ */
+ int rxBufferSetSize(int size, bool m) { return resizeBuffer(size, RxIrq, m); }
+
+ /**
+ * Function: txBufferSetSize
+ *
+ * Change the TX buffer size.
+ * Always performs a memory sanity check, halting the Mbed on failure.
+ *
+ * @see Result
+ * @ingroup API
+ * @param size The new TX buffer size in bytes.
+ * @return Result Ok on success.
+ */
+ int txBufferSetSize(int size) { return resizeBuffer(size, TxIrq, true); }
+
+ /**
+ * Function: rxBufferSetSize
+ *
+ * Change the RX buffer size.
+ * Always performs a memory sanity check, halting the Mbed on failure.
+ *
+ * @see Result
+ * @ingroup API
+ * @param size The new RX buffer size in bytes.
+ * @return Result Ok on success.
+ */
+ int rxBufferSetSize(int size) { return resizeBuffer(size, RxIrq, true); }
+
+ /**
+ * Function: txBufferFlush
+ *
+ * Remove all bytes from the TX buffer.
+ * @ingroup API
+ */
+ void txBufferFlush(void) { flushBuffer(TxIrq); }
+
+ /**
+ * Function: rxBufferFlush
+ *
+ * Remove all bytes from the RX buffer.
+ * @ingroup API
+ */
+ void rxBufferFlush(void) { flushBuffer(RxIrq); }
+
+ /**
+ * Function: getcNb
+ *
+ * Like getc() but is non-blocking. If no bytes are in the RX buffer this
+ * function returns Result::NoChar (-1)
+ *
+ * @ingroup API
+ * @return A byte from the RX buffer or Result::NoChar (-1) if bufer empty.
+ */
+ int getcNb() { return __getc(false); }
+
+ /**
+ * Function: getc
+ *
+ * Overloaded version of Serial::getc()
+ *
+ * This function blocks (if the RX buffer is empty the function will wait for a
+ * character to arrive and then return that character).
+ *
+ * @ingroup API
+ * @return A byte from the RX buffer
+ */
+ int getc() { return __getc(true); }
+
+ /**
+ * Function: txGetLastChar
+ *
+ * Rteurn the last byte to pass through the TX interrupt handler.
+ *
+ * @ingroup MISC
+ * @return The byte
+ */
+ char txGetLastChar(void) { return txc; }
+
+ /**
+ * Function: rxGetLastChar
+ *
+ * Return the last byte to pass through the RX interrupt handler.
+ *
+ * @ingroup MISC
+ * @return The byte
+ */
+ char rxGetLastChar(void) { return rxc; }
+
+ /**
+ * Function: txIsBusy
+ *
+ * If the Uart is still actively sending characters this
+ * function will return true.
+ *
+ * @ingroup API
+ * @return bool
+ */
+ bool txIsBusy(void);
+
+ /**
+ * Function: autoDetectChar
+ *
+ * Set the char that, if seen incoming, invokes the AutoDetectChar callback.
+ *
+ * @ingroup API
+ * @param int c The character to detect.
+ */
+ void autoDetectChar(char c) { auto_detect_char = c; }
+
+ /**
+ * Function: move
+ *
+ * Move contents of RX buffer to external buffer. Stops if "end" detected.
+ *
+ * @ingroup API
+ * @param char *s The destination buffer address
+ * @param int max The maximum number of chars to move.
+ * @param char end If this char is detected stop moving.
+ */
+ int move(char *s, int max, char end) {
+ int counter = 0;
+ char c;
+ while(readable()) {
+ c = getc();
+ if (c == end) break;
+ *(s++) = c;
+ counter++;
+ if (counter == max) break;
+ }
+ return counter;
+ }
+
+ /**
+ * Function: move (overloaded)
+ *
+ * Move contents of RX buffer to external buffer. Stops if auto_detect_char detected.
+ *
+ * @ingroup API
+ * @param int max The maximum number of chars to move.
+ * @param char *s The destination buffer address
+ */
+ int move(char *s, int max) {
+ return move(s, max, auto_detect_char);
+ }
+
+ #if 0 // Inhereted from Serial/Stream, for documentation only
+ /**
+ * Function: putc
+ *
+ * Write a character
+ * Inhereted from Serial/Stream
+ *
+ * @see http://mbed.org/projects/libraries/api/mbed/trunk/Serial#Serial.putc
+ * @ingroup API
+ * @param c The character to write to the serial port
+ */
+ int putc(int c);
+ #endif
+
+ #if 0 // Inhereted from Serial/Stream, for documentation only
+ /**
+ * Function: printf
+ *
+ * Write a formated string
+ * Inhereted from Serial/Stream
+ *
+ * @see http://mbed.org/projects/libraries/api/mbed/trunk/Serial#Serial.printf
+ * @ingroup API
+ * @param format A printf-style format string, followed by the variables to use in formating the string.
+ */
+ int printf(const char* format, ...);
+ #endif
+
+ #if 0 // Inhereted from Serial/Stream, for documentation only
+ /**
+ * Function: scanf
+ *
+ * Read a formated string
+ * Inhereted from Serial/Stream
+ *
+ * @see http://mbed.org/projects/libraries/api/mbed/trunk/Serial#Serial.scanf
+ * @ingroup API
+ * @param format - A scanf-style format string, followed by the pointers to variables to store the results.
+ */
+ int scanf(const char* format, ...);
+ #endif
+
+protected:
+ /**
+ * Used to pass information to callbacks.
+ * @ingroup INTERNALS
+ */
+ MODSERIAL_IRQ_INFO callbackInfo;
+
+ /**
+ * Remove the last char placed into the rx buffer.
+ * This is an operation that can only be performed
+ * by an rxCallback function. To protect the buffers
+ * this function is defined protected so that a
+ * regular application cannot call it directly. It
+ * can only be called by the public version within a
+ * MODSERIAL_IRQ_INFO class.
+ * @ingroup INTERNALS
+ * @return The byte removed from the buffer.
+ */
+ int rxDiscardLastChar(void);
+
+private:
+
+ /**
+ * A pointer to the UART peripheral base address being used.
+ * @ingroup INTERNALS
+ */
+ void *_base;
+
+ /**
+ * The last byte to pass through the TX IRQ handler.
+ * @ingroup INTERNALS
+ */
+ volatile char txc;
+
+ /**
+ * The last byte to pass through the RX IRQ handler.
+ * @ingroup INTERNALS
+ */
+ volatile char rxc;
+
+ /**
+ * Pointers to the TX and RX buffers.
+ * @ingroup INTERNALS
+ */
+ volatile char *buffer[2];
+
+ /**
+ * Buffer in pointers.
+ * @ingroup INTERNALS
+ */
+ volatile int buffer_in[2];
+
+ /**
+ * Buffer out pointers.
+ * @ingroup INTERNALS
+ */
+ volatile int buffer_out[2];
+
+ /**
+ * Buffer lengths.
+ * @ingroup INTERNALS
+ */
+ volatile int buffer_size[2];
+
+ /**
+ * Buffer content counters.
+ * @ingroup INTERNALS
+ */
+ volatile int buffer_count[2];
+
+ /**
+ * Buffer overflow.
+ * @ingroup INTERNALS
+ */
+ volatile int buffer_overflow[2];
+
+ /**
+ * Auto-detect character.
+ * @ingroup INTERNALS
+ */
+ volatile char auto_detect_char;
+
+ /**
+ * Callback system.
+ * @ingroup INTERNALS
+ */
+ MODSERIAL_callback _isr[NumOfIrqTypes];
+
+ /**
+ * TX Interrupt Service Routine.
+ * @ingroup INTERNALS
+ */
+ void isr_tx(bool doCallback);
+
+ /**
+ * TX Interrupt Service Routine stub version.
+ * @ingroup INTERNALS
+ */
+ void isr_tx(void) { isr_tx(true); }
+
+
+ /**
+ * RX Interrupt Service Routine.
+ * @ingroup INTERNALS
+ */
+ void isr_rx(void);
+
+ /**
+ * Disable the interrupts for this Uart.
+ * @ingroup INTERNALS
+ */
+ void disableIrq(void);
+
+ /**
+ * Enable the interrupts for this Uart.
+ * @ingroup INTERNALS
+ */
+ void enableIrq(void);
+
+ /**
+ * Get a character from the RX buffer
+ * @ingroup INTERNALS
+ * @param bool True to block (wait for input)
+ * @return A byte from the buffer.
+ */
+ int __getc(bool);
+
+ /**
+ * Put a character from the TX buffer
+ * @ingroup INTERNALS
+ * @param bool True to block (wait for space in the TX buffer if full)
+ * @return 0 on success
+ */
+ int __putc(int c, bool);
+
+ /**
+ * Function: _putc
+ * Overloaded virtual function.
+ */
+ virtual int _putc(int c) { return __putc(c, true); }
+
+ /**
+ * Function: _getc
+ * Overloaded virtual function.
+ */
+ virtual int _getc() { return __getc(true); }
+
+ /**
+ * Function: init
+ * Initialize the MODSERIAL object
+ * @ingroup INTERNALS
+ */
+ void init(int txSize, int rxSize);
+
+ /**
+ * Function: flushBuffer
+ * @ingroup INTERNALS
+ */
+ void flushBuffer(IrqType type);
+
+ /**
+ * Function: resizeBuffer
+ * @ingroup INTERNALS
+ */
+ int resizeBuffer(int size, IrqType type = RxIrq, bool memory_check = true);
+
+ /**
+ * Function: downSizeBuffer
+ * @ingroup INTERNALS
+ */
+ int downSizeBuffer(int size, IrqType type, bool memory_check);
+
+ /**
+ * Function: upSizeBuffer
+ * @ingroup INTERNALS
+ */
+ int upSizeBuffer(int size, IrqType type, bool memory_check);
+
+ /*
+ * If MODDMA is available the compile in code to handle sending
+ * an arbitary char buffer. Note, the parts before teh #ifdef
+ * are declared so that MODSERIAL can access then even if MODDMA
+ * isn't avaiable. Since MODDMA.h is only available at this point
+ * all DMA functionality must be declared inline in the class
+ * definition.
+ */
+public:
+
+ int dmaSendChannel;
+ void *moddma_p;
+
+#ifdef MODDMA_H
+
+ MODDMA_Config *config;
+
+ /**
+ * Set the "void pointer" moddma_p to be a pointer to a
+ * MODDMA controller class instance. Used to manage the
+ * data transfer of DMA configurations.
+ *
+ * @ingroup API
+ * @param p A pointer to "the" instance of MODDMA.
+ */
+ void MODDMA(MODDMA *p) { moddma_p = p; }
+
+ /**
+ * Send a char buffer to the Uarts TX system
+ * using DMA. This blocks regular library
+ * sending.
+ *
+ * @param buffer A char buffer of bytes to send.
+ * @param len The length of the buffer to send.
+ * @param dmaChannel The DMA channel to use, defaults to 7
+ * @return MODDMA::Status MODDMA::ok if all went ok
+ */
+ int dmaSend(char *buffer, int len, int dmaChannel = 7)
+ {
+ if (moddma_p == (void *)NULL) return -2;
+ class MODDMA *dma = (class MODDMA *)moddma_p;
+
+ dmaSendChannel = dmaChannel & 0x7;
+
+ uint32_t conn = MODDMA::UART0_Tx;
+ switch(_uidx) {
+ case 0: conn = MODDMA::UART0_Tx; break;
+ case 1: conn = MODDMA::UART1_Tx; break;
+ case 2: conn = MODDMA::UART2_Tx; break;
+ case 3: conn = MODDMA::UART3_Tx; break;
+ }
+
+ config = new MODDMA_Config;
+ config
+ ->channelNum ( (MODDMA::CHANNELS)(dmaSendChannel & 0x7) )
+ ->srcMemAddr ( (uint32_t) buffer )
+ ->transferSize ( len )
+ ->transferType ( MODDMA::m2p )
+ ->dstConn ( conn )
+ ->attach_tc ( this, &MODSERIAL::dmaSendCallback )
+ ->attach_err ( this, &MODSERIAL::dmaSendCallback )
+ ; // config end
+
+ // Setup the configuration.
+ if (dma->Setup(config) == 0) {
+ return -1;
+ }
+
+ //dma.Enable( MODDMA::Channel_0 );
+ dma->Enable( config->channelNum() );
+ return MODDMA::Ok;
+ }
+
+ /**
+ * Attach a callback to the DMA completion.
+ *
+ * @ingroup API
+ * @param fptr A function pointer to call
+ * @return this
+ */
+ void attach_dmaSendComplete(void (*fptr)(MODSERIAL_IRQ_INFO *)) {
+ _isrDmaSendComplete.attach(fptr);
+ }
+
+ /**
+ * Attach a callback to the DMA completion.
+ *
+ * @ingroup API
+ * @param tptr A template pointer to the calling object
+ * @param mptr A method pointer within the object to call.
+ * @return this
+ */
+ template<typename T>
+ void attach_dmaSendComplete(T* tptr, void (T::*mptr)(MODSERIAL_IRQ_INFO *)) {
+ if((mptr != NULL) && (tptr != NULL)) {
+ _isrDmaSendComplete.attach(tptr, mptr);
+ }
+ }
+
+ MODSERIAL_callback _isrDmaSendComplete;
+
+protected:
+ /**
+ * Callback for dmaSend().
+ */
+ void dmaSendCallback(void)
+ {
+ if (moddma_p == (void *)NULL) return;
+ class MODDMA *dma = (class MODDMA *)moddma_p;
+
+ MODDMA_Config *config = dma->getConfig();
+ dma->haltAndWaitChannelComplete( (MODDMA::CHANNELS)config->channelNum());
+ dma->Disable( (MODDMA::CHANNELS)config->channelNum() );
+ if (dma->irqType() == MODDMA::TcIrq) dma->clearTcIrq();
+ if (dma->irqType() == MODDMA::ErrIrq) dma->clearErrIrq();
+ dmaSendChannel = -1;
+ _isrDmaSendComplete.call(&this->callbackInfo);
+ delete(config);
+ }
+
+#endif // MODDMA_H
+
+};
+
+}; // namespace AjK ends
+
+using namespace AjK;
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MODSERIAL/MODSERIAL_IRQ_INFO.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,38 @@
+/*
+ Copyright (c) 2010 Andy Kirkham
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+
+ @file MODSERIAL_IRQ_INFO.cpp
+ @author Andy Kirkham
+*/
+
+#include "MODSERIAL.h"
+
+namespace AjK {
+
+int
+MODSERIAL_IRQ_INFO::rxDiscardLastChar(void)
+{
+ if (!serial) return -1;
+
+ return serial->rxDiscardLastChar();
+}
+
+}; // namespace AjK ends
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MODSERIAL/PUTC.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,79 @@
+/*
+ Copyright (c) 2010 Andy Kirkham
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#include "MODSERIAL.h"
+#include "MACROS.h"
+
+namespace AjK {
+
+int
+MODSERIAL::__putc(int c, bool block) {
+
+ // If no buffer is in use fall back to standard TX FIFO usage.
+ // Note, we must block in this case and ignore bool "block"
+ // so as to maintain compat with Mbed Serial.
+ if (buffer[TxIrq] == (char *)NULL || buffer_size[TxIrq] == 0) {
+ while (! MODSERIAL_THR_HAS_SPACE) ; // Wait for space in the TX FIFO.
+ _THR = (uint32_t)c;
+ return 0;
+ }
+
+ if ( MODSERIAL_THR_HAS_SPACE && MODSERIAL_TX_BUFFER_EMPTY && dmaSendChannel == -1 ) {
+ _THR = (uint32_t)c;
+ }
+ else {
+ if (block) {
+ uint32_t ier = _IER; _IER = 1;
+ while ( MODSERIAL_TX_BUFFER_FULL ) { // Blocks!
+ // If putc() is called from an ISR then we are stuffed
+ // because in an ISR no bytes from the TX buffer will
+ // get transferred to teh TX FIFOs while we block here.
+ // So, to work around this, instead of sitting in a
+ // loop waiting for space in the TX buffer (which will
+ // never happen in IRQ context), check to see if the
+ // TX FIFO has space available to move bytes from the
+ // TX buffer to TX FIFO to make space. The easiest way
+ // to do this is to poll the isr_tx() function while we
+ // are blocking.
+ isr_tx(false);
+ }
+ _IER = ier;
+ }
+ else if( MODSERIAL_TX_BUFFER_FULL ) {
+ buffer_overflow[TxIrq] = c; // Oh dear, no room in buffer.
+ _isr[TxOvIrq].call(&this->callbackInfo);
+ return -1;
+ }
+ _IER &= ~2;
+ buffer[TxIrq][buffer_in[TxIrq]] = c;
+ buffer_count[TxIrq]++;
+ buffer_in[TxIrq]++;
+ if (buffer_in[TxIrq] >= buffer_size[TxIrq]) {
+ buffer_in[TxIrq] = 0;
+ }
+ _IER |= 2;
+ }
+
+ return 0;
+}
+
+}; // namespace AjK ends
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MODSERIAL/RESIZE.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,123 @@
+/*
+ Copyright (c) 2010 Andy Kirkham
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#include "MODSERIAL.h"
+#include "MACROS.h"
+
+namespace AjK {
+
+int
+MODSERIAL::resizeBuffer(int size, IrqType type, bool memory_check)
+{
+ int rval = Ok;
+
+ // If the requested size is the same as the current size there's nothing to do,
+ // just continue to use the same buffer as it's fine as it is.
+ if (buffer_size[type] == size) return rval;
+
+ // Make sure the ISR cannot use the buffers while we are manipulating them.
+ disableIrq();
+
+ // If the requested buffer size is larger than the current size,
+ // attempt to create a new buffer and use it.
+ if (buffer_size[type] < size) {
+ rval = upSizeBuffer(size, type, memory_check);
+ }
+ else if (buffer_size[type] > size) {
+ rval = downSizeBuffer(size, type, memory_check);
+ }
+
+ // Start the ISR system again with the new buffers.
+ enableIrq();
+
+ return rval;
+}
+
+int
+MODSERIAL::downSizeBuffer(int size, IrqType type, bool memory_check)
+{
+ if (size >= buffer_count[type]) {
+ return BufferOversize;
+ }
+
+ char *s = (char *)malloc(size);
+
+ if (s == (char *)NULL) {
+ if (memory_check) {
+ error("Failed to allocate memory for %s buffer", type == TxIrq ? "TX" : "RX");
+ }
+ return NoMemory;
+ }
+
+ int c, new_in = 0;
+
+ do {
+ c = __getc(false);
+ if (c != -1) s[new_in++] = (char)c;
+ if (new_in >= size) new_in = 0;
+ }
+ while (c != -1);
+
+ free((char *)buffer[type]);
+ buffer[type] = s;
+ buffer_in[type] = new_in;
+ buffer_out[type] = 0;
+ return Ok;
+}
+
+int
+MODSERIAL::upSizeBuffer(int size, IrqType type, bool memory_check)
+{
+ char *s = (char *)malloc(size);
+
+ if (s == (char *)NULL) {
+ if (memory_check) {
+ error("Failed to allocate memory for %s buffer", type == TxIrq ? "TX" : "RX");
+ }
+ return NoMemory;
+ }
+
+ if (buffer_count[type] == 0) { // Current buffer empty?
+ free((char *)buffer[type]);
+ buffer[type] = s;
+ buffer_in[type] = 0;
+ buffer_out[type] = 0;
+ }
+ else { // Copy the current contents into the new buffer.
+ int c, new_in = 0;
+ do {
+ c = __getc(false);
+ if (c != -1) s[new_in++] = (char)c;
+ if (new_in >= size) new_in = 0; // Shouldn't happen, but be sure.
+ }
+ while (c != -1);
+ free((char *)buffer[type]);
+ buffer[type] = s;
+ buffer_in[type] = new_in;
+ buffer_out[type] = 0;
+ }
+
+ buffer_size[type] = size;
+ return Ok;
+}
+
+}; // namespace AjK ends
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PinDetect.lib Mon Feb 13 02:11:20 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/AjK/code/PinDetect/#cb3afc45028b
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SDCard.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,962 @@
+//mbed Microcontroller Library
+//SDCard Interface
+//Copyright 2010
+//Thomas Hamilton
+
+#include "SDCard.h"
+
+SDCard::SDCard(PinName mosi, PinName miso, PinName sck, PinName cs,
+ const char* DiskName) :
+ FATFileSystem(DiskName), DataLines(mosi, miso, sck), ChipSelect(cs),
+ t(0), Timeout(1024), CRCMode(1), Capacity(0), Version(0), Status(0x00)
+ //card always starts uninitialized and in CRC mode; version 1 low-capacity
+ //card protocols are backwards-compatible with all other card protocols
+{
+ DataLines.frequency(100000);
+ //set universal speed
+ ChipSelect.write(1);
+ //deselect the chip
+ GenerateCRCTable(1, 137, CommandCRCTable);
+ //generate the CRC7 lookup table; polynomial x^7 + x^3 + 1 converts to
+ //decimal 137
+ GenerateCRCTable(2, 69665, DataCRCTable);
+ //generate the crc16 lookup table; polynomial x^16 + x^12 + x^5 + 1
+ //converts to decimal 69665
+ Initialize();
+ //run setup operations
+}
+
+SDCard::~SDCard()
+ //delete all tables and card data registers
+{
+ delete[] CommandCRCTable;
+ delete[] DataCRCTable;
+ delete[] OCR;
+ delete[] CSD;
+ delete[] FSR;
+ delete this;
+}
+
+unsigned char SDCard::disk_initialize()
+ //give the FAT module access to the card setup routine
+{
+ if (Status == 0x01)
+ {
+ return Initialize();
+ }
+ else
+ {
+ return Status;
+ }
+}
+unsigned char SDCard::disk_status()
+ //return card initialization and availability status
+{ return Status; }
+unsigned char SDCard::disk_read(
+ unsigned char* buff, unsigned long sector, unsigned char count)
+ //give the FAT module access to the multiple-sector reading function
+{ return Read((unsigned int)sector, count, buff); }
+unsigned char SDCard::disk_write(
+ const unsigned char* buff, unsigned long sector, unsigned char count)
+ //give the FAT module access to the multiple-sector writing function
+{ return Write((unsigned int)sector, count, (unsigned char*)buff); }
+unsigned char SDCard::disk_sync()
+ //the disk is always synchronized, so return "disk ready"
+{ return 0x00; }
+unsigned long SDCard::disk_sector_count()
+ //calculate and return the number of sectors on the card from the CSD
+{
+ switch (CSD[0] & 0xC0)
+ {
+ case 0x00:
+ //calculate sector count as specified for version 1 cards
+ return ((((CSD[6] & 0x03) << 10) | (CSD[7] << 2)
+ | ((CSD[8] & 0xC0) >> 6)) + 1)
+ * (1 << ((((CSD[9] & 0x03) << 1)
+ | ((CSD[10] & 0x80) >> 7)) + 2));
+ case 0x40:
+ //calculate sector count as specified for version 2 cards
+ return ((((CSD[7] & 0x3F) << 16)
+ | (CSD[8] << 8) | CSD[9]) + 1) * 1024;
+ default:
+ return 0;
+ }
+}
+unsigned short SDCard::disk_sector_size()
+ //fix the sector size to 512 bytes for all cards versions
+{ return 512; }
+unsigned long SDCard::disk_block_size()
+ //calculate and return the number of sectors in an erase block from the CSD
+{
+ if (Version)
+ //the erase sector size is the allocation unit for version 2 cards
+ {
+ return 1;
+ }
+ else
+ //calculate the erase sector size for version 1 cards
+ {
+ return (CSD[10] << 1) | (CSD[11] >> 7) + 1;
+ }
+}
+
+unsigned char SDCard::Format(unsigned int AllocationUnit)
+ //call the FAT module formatting function
+{
+ if (format(AllocationUnit))
+ {
+ return 0x01;
+ }
+ else
+ {
+ return 0x00;
+ }
+}
+
+unsigned char SDCard::Log(unsigned char Control, unsigned char Data)
+{
+ static unsigned char Workspace;
+ //work area for card commands and data transactions
+ static unsigned short Index = 0;
+ //store last written byte number of current memory block
+ static unsigned char Mode = 0x00;
+ //store previous operating mode to determine current behavior
+
+ SelectCRCMode(0);
+ //CRC's are not used in raw data mode
+
+ switch (Control)
+ {
+ case 0x00:
+ //control code 0x00 synchronizes the card
+ if (Mode)
+ //if the card is in read or write mode, synchronize the card
+ {
+ ChipSelect.write(0);
+ for (; Index < 512; Index++)
+ //get through the left over space, filling with 0xFF
+ {
+ DataLines.write(0xFF);
+ }
+ DataLines.write(0xFF);
+ DataLines.write(0xFF);
+ //get through the CRC
+ ChipSelect.write(1);
+ if (Mode == 0x01)
+ //if the card is in write mode, finish the current sector
+ //and finalize the writing operation
+ {
+ ChipSelect.write(0);
+ t = 0;
+ do
+ {
+ t++;
+ } while (((DataLines.write(0xFF) & 0x11) != 0x01)
+ && (t < Timeout));
+ //get through the data response token
+ while (!DataLines.write(0xFF));
+ //get through the busy signal
+ DataLines.write(0xFD);
+ DataLines.write(0xFF);
+ //send the stop transmission token
+ while (!DataLines.write(0xFF));
+ //get through the busy signal
+ ChipSelect.write(1);
+ DataLines.write(0xFF);
+ }
+ else
+ //if the card is in read mode, finish the current sector
+ //and finalize the reading operation
+ {
+ Command(12, 0, &Workspace);
+ //send the stop transmission command
+ ChipSelect.write(0);
+ while (!DataLines.write(0xFF));
+ //get through the busy signal
+ ChipSelect.write(1);
+ DataLines.write(0xFF);
+ }
+ Index = 0;
+ Mode = 0x00;
+ //reset the index to the start and switch the mode to
+ //synchronized mode
+ }
+ return 0xFF;
+
+ case 0x01:
+ //control code 1 writes a byte
+ if (Mode != 0x01)
+ //if the previous call was not a write operation, synchronize
+ //the card, start a new write block, and set the function to
+ //write mode
+ {
+ Log(0, 0);
+ Command(25, 0, &Workspace);
+ Mode = 0x01;
+ }
+ if (Index == 0)
+ //if the index is at the start, send the start block token
+ //before the byte
+ {
+ ChipSelect.write(0);
+ DataLines.write(0xFC);
+ DataLines.write(Data);
+ ChipSelect.write(1);
+ Index++;
+ }
+ else if (Index < 511)
+ //if the index is between the boundaries, write the byte
+ {
+ ChipSelect.write(0);
+ DataLines.write(Data);
+ ChipSelect.write(1);
+ Index++;
+ }
+ else
+ //if the index is at the last address, get through the CRC,
+ //data response token, and busy signal and reset the index
+ {
+ ChipSelect.write(0);
+ DataLines.write(Data);
+ DataLines.write(0xFF);
+ DataLines.write(0xFF);
+ t = 0;
+ do
+ {
+ t++;
+ } while (((DataLines.write(0xFF) & 0x11) != 0x01)
+ && (t < Timeout));
+ while (!DataLines.write(0xFF));
+ ChipSelect.write(1);
+ Index = 0;
+ }
+ return 0xFF;
+
+ case 0x02:
+ //control code 2 reads a byte
+ if (Mode != 0x02)
+ //if the previous call was not a read operation, synchronise
+ //the card, start a new read block, and set the function to
+ //read mode
+ {
+ Log(0, 0);
+ Command(18, 0, &Workspace);
+ Mode = 0x02;
+ }
+ if (Index == 0)
+ //if the index is at the start, get the start block token
+ //and read the first byte
+ {
+ ChipSelect.write(0);
+ t = 0;
+ do
+ {
+ t++;
+ } while ((DataLines.write(0xFF) != 0xFE)
+ && (t < Timeout));
+ Workspace = DataLines.write(0xFF);
+ ChipSelect.write(1);
+ Index++;
+ return Workspace;
+ }
+ else if (Index < 511)
+ //if the index is between the boundaries, read the byte
+ {
+ ChipSelect.write(0);
+ Workspace = DataLines.write(0xFF);
+ ChipSelect.write(1);
+ Index++;
+ return Workspace;
+ }
+ else
+ //if the index is at the last address, get through the CRC and
+ //reset the index
+ {
+ ChipSelect.write(0);
+ Workspace = DataLines.write(0xFF);
+ DataLines.write(0xFF);
+ DataLines.write(0xFF);
+ ChipSelect.write(1);
+ Index = 0;
+ return Workspace;
+ }
+
+ default:
+ //undefined control codes will only return stuff bits
+ return 0xFF;
+ }
+}
+
+unsigned char SDCard::Write(unsigned int Address, unsigned char* Data)
+{
+ unsigned char Workspace[2];
+ //work area for card commands and data transactions
+
+ if (Capacity)
+ //send the single-block write command addressed for high-capacity cards
+ {
+ Command(24, Address, Workspace);
+ }
+ else
+ //send the single-block write command addressed for low-capacity cards
+ {
+ Command(24, Address * 512, Workspace);
+ }
+ if (Workspace[0])
+ //if a command error occurs, return "parameter error"
+ { return 0x04; }
+ DataCRC(512, Data, Workspace);
+ //calculate the CRC16
+ ChipSelect.write(0);
+ DataLines.write(0xFE);
+ //write start block token
+ for (unsigned short i = 0; i < 512; i++)
+ //write the data to the addressed card sector
+ {
+ DataLines.write(Data[i]);
+ }
+ DataLines.write(Workspace[0]);
+ DataLines.write(Workspace[1]);
+ //write the data CRC16
+ t = 0;
+ do
+ {
+ Workspace[0] = DataLines.write(0xFF);
+ t++;
+ } while (((Workspace[0] & 0x11) != 0x01) && (t < Timeout));
+ //gather the data block response token
+ while (!DataLines.write(0xFF));
+ //get through the busy signal
+ ChipSelect.write(1);
+ DataLines.write(0xFF);
+ if (((Workspace[0] & 0x1F) != 0x05) || (t == Timeout))
+ //if the data response token indicates error, return write error
+ { return 0x01; }
+ else
+ { return 0x00; }
+}
+unsigned char SDCard::Write(
+ unsigned int Address, unsigned char SectorCount, unsigned char* Data)
+{
+ unsigned char Workspace[5];
+ //work area for card commands and data transactions
+ static unsigned char CurrentSectorCount = 1;
+ //store the last write sector count
+
+ if (SectorCount != CurrentSectorCount)
+ //set the expected number of write blocks if its different from
+ //previous multiple-block write operations
+ {
+ Command(55, 0, Workspace);
+ Command(23, SectorCount, Workspace);
+ if (Workspace[0])
+ { return 0x04; }
+ CurrentSectorCount = SectorCount;
+ }
+ if (Capacity)
+ //send the multiple-block write command addressed for high-capacity
+ //cards
+ {
+ Command(25, Address, Workspace);
+ }
+ else
+ //send the multiple-block write command addressed for low-capacity
+ //cards
+ {
+ Command(25, Address * 512, Workspace);
+ }
+ if (Workspace[0])
+ //if a command error occurs, return "parameter error"
+ { return 0x04; }
+ Workspace[4] = 0x00;
+ //initialize the error detection variable
+ for (unsigned char i = 0; i < SectorCount; i++)
+ //write each data sector
+ {
+ DataCRC(512, &Data[i * 512], Workspace);
+ //calculate the CRC16
+ ChipSelect.write(0);
+ DataLines.write(0xFC);
+ //send multiple write block start token
+ for (unsigned int j = i * 512; j < (i + 1) * 512; j++)
+ //write each data block
+ {
+ DataLines.write(Data[j]);
+ }
+ DataLines.write(Workspace[0]);
+ DataLines.write(Workspace[1]);
+ //write the CRC16
+ t = 0;
+ do
+ {
+ Workspace[0] = DataLines.write(0xFF);
+ t++;
+ } while (((Workspace[0] & 0x11) != 0x01) && (t < Timeout));
+ //gather the data block response token
+ while (!DataLines.write(0xFF));
+ //get through the busy signal
+ ChipSelect.write(1);
+ Workspace[4] |= Workspace[0];
+ //record if any write errors that are detected in the data response
+ //tokens
+ if (t == Timeout)
+ //if a block write operation times out, stop operations
+ { break; }
+ }
+ ChipSelect.write(0);
+ DataLines.write(0xFD);
+ DataLines.write(0xFF);
+ //send the stop transmission token
+ while (!DataLines.write(0xFF));
+ //get through the busy signal
+ ChipSelect.write(1);
+ DataLines.write(0xFF);
+ if (((Workspace[4] & 0x1F) != 0x05) || (t == Timeout))
+ //if a data response token indicated an error, return "write error"
+ { return 0x01; }
+ else
+ { return 0x00; }
+}
+
+unsigned char SDCard::Read(unsigned int Address, unsigned char* Data)
+{
+ unsigned char Workspace[4];
+ //work area for card commands and data transactions
+
+ if (Capacity)
+ //send the single-block read command addressed for high-capacity cards
+ {
+ Command(17, Address, Workspace);
+ }
+ else
+ //send the single-block read command addressed for low-capacity cards
+ {
+ Command(17, Address * 512, Workspace);
+ }
+ if (Workspace[0])
+ //if a command error occurs, return "parameter error"
+ { return 0x04; }
+ ChipSelect.write(0);
+ t = 0;
+ do
+ {
+ t++;
+ } while ((DataLines.write(0xFF) != 0xFE) && (t < Timeout));
+ //get to the start block token
+ if (t == Timeout) {
+ ChipSelect.write(1); DataLines.write(0xFF); return 0x01; }
+ for (unsigned short i = 0; i < 512; i++)
+ {
+ Data[i] = DataLines.write(0xFF);
+ }
+ //read the data from the addressed card sector
+ Workspace[2] = DataLines.write(0xFF);
+ Workspace[3] = DataLines.write(0xFF);
+ //read the CRC16
+ ChipSelect.write(1);
+ DataLines.write(0xFF);
+ DataCRC(512, Data, Workspace);
+ //calculate the CRC16
+ if (CRCMode && ((Workspace[0] != Workspace[2])
+ || (Workspace[1] != Workspace[3])))
+ //if the CRC is invalid, return "read error"
+ { return 0x01; }
+ else
+ { return 0x00; }
+}
+unsigned char SDCard::Read(
+ unsigned int Address, unsigned char SectorCount, unsigned char* Data)
+{
+ unsigned char Workspace[5];
+ //work area for card commands and data transactions
+
+ if (Capacity)
+ //send the multiple-block read command addressed for high-capacity
+ //cards
+ {
+ Command(18, Address, Workspace);
+ }
+ else
+ //send the multiple-block read command addressed for low-capacity
+ //cards
+ {
+ Command(18, Address * 512, Workspace);
+ }
+ if (Workspace[0])
+ //if a command error occurs, return "parameter error"
+ { return 0; }
+ Workspace[4] = 0x00;
+ //initialize error detection variable
+ for (unsigned char i = 0; i < SectorCount; i++)
+ //read each data sector
+ {
+ ChipSelect.write(0);
+ t = 0;
+ do
+ {
+ t++;
+ } while ((DataLines.write(0xFF) != 0xFE) && (t < Timeout));
+ //get to the data block start token
+ if (t == Timeout)
+ {
+ break;
+ }
+ //if a block read operation times out, stop operations
+ for (unsigned int j = i * 512; j < (i + 1) * 512; j++)
+ {
+ Data[j] = DataLines.write(0xFF);
+ }
+ //read the data block
+ Workspace[2] = DataLines.write(0xFF);
+ Workspace[3] = DataLines.write(0xFF);
+ //read the data CRC from the card
+ ChipSelect.write(1);
+ DataCRC(512, &Data[i * 512], Workspace);
+ //calculate the CRC16 for each read data block
+ Workspace[4] |= (CRCMode && ((Workspace[0] != Workspace[2])
+ || (Workspace[1] != Workspace[3])));
+ //record if any invalid CRCs are detected during the
+ //transaction
+ }
+ Command(12, 0, Workspace);
+ //send the stop transmission command
+ ChipSelect.write(0);
+ while (!DataLines.write(0xFF));
+ //get through the busy signal
+ ChipSelect.write(1);
+ DataLines.write(0xFF);
+ if ((Workspace[4]) || (t == Timeout))
+ //if an invalid CRC was detected, return "read error"
+ { return 0x01; }
+ else
+ { return 0x00; }
+}
+
+unsigned char SDCard::SelectCRCMode(bool Mode)
+{
+ unsigned char Response;
+
+ if (CRCMode != Mode)
+ //only send command if CRCMode has been changed
+ {
+ t = 0;
+ do
+ {
+ Command(59, Mode, &Response);
+ //send the set CRC mode command
+ t++;
+ } while (Response && (t < Timeout));
+ CRCMode = Mode;
+ }
+ if (t == Timeout)
+ //if the command times out, return "error"
+ { return 0x01; }
+ else
+ { return 0x00; }
+}
+
+void SDCard::SetTimeout(unsigned int Retries)
+{
+ Timeout = Retries;
+ //Set the global number of times for operations to be retried
+}
+
+unsigned char SDCard::Initialize()
+{
+ unsigned char Workspace[5];
+ //work area for card commands and data transactions
+
+ for (unsigned char i = 0; i < 16; i++)
+ //clock card at least 74 times to power up
+ {
+ DataLines.write(0xFF);
+ }
+
+ t = 0;
+ do
+ {
+ Command(0, 0, Workspace);
+ //send the reset command to put the card into SPI mode
+ t++;
+ } while ((Workspace[0] != 0x01) && (t < Timeout));
+ //check for command acceptance
+ if (t == Timeout) { Status = 0x01; return Status; }
+
+ t = 0;
+ do
+ {
+ Command(59, 1, Workspace);
+ //turn on CRCs
+ t++;
+ } while ((Workspace[0] != 0x01) && (Workspace[0] != 0x05)
+ && (t < Timeout));
+ //the set CRC mode command is not valid for all cards in idle state
+ if (t == Timeout) { Status = 0x01; return Status; }
+
+ t = 0;
+ do
+ {
+ Command(8, 426, Workspace);
+ //the voltage bits are 0x01 for 2.7V - 3.6V, the check pattern is
+ //0xAA, 0x000001AA converts to decimal 426
+ t++;
+ } while (((Workspace[0] != 0x01) || ((Workspace[3] & 0x0F) != 0x01) ||
+ (Workspace[4] != 0xAA)) && (Workspace[0] != 0x05) && (t < Timeout));
+ //check the version, voltage acceptance, and check pattern
+ if (t == Timeout) { Status = 0x01; return Status; }
+ Version = Workspace[0] != 0x05;
+ //store the card version
+
+ if (!Version)
+ {
+ t = 0;
+ do
+ {
+ Command(16, 512, Workspace);
+ //set the data-block length to 512 bytes
+ t++;
+ } while (Workspace[0] && (t < Timeout));
+ if (t == Timeout) { Status = 0x01; return Status; }
+ }
+
+ t = 0;
+ do
+ {
+ Command(58, 0, Workspace);
+ //check the OCR
+ t++;
+ } while (((Workspace[0] != 0x01) ||
+ !((Workspace[2] & 0x20) || (Workspace[2] & 0x10))) && (t < Timeout));
+ //check for the correct operating voltage, 3.3V
+ if (t == Timeout) { Status = 0x01; return Status; }
+
+ t = 0;
+ do
+ {
+ Command(55, 0, Workspace);
+ //initialize card command is application-specific
+ Command(41, 1073741824, Workspace);
+ //specify host supports high capacity cards, 0x40000000 converts to
+ //decimal 1073741824d
+ t++;
+ } while (Workspace[0] && (t < Timeout));
+ //check if the card is ready
+ if (t == Timeout) { Status = 0x01; return Status; }
+
+ if (SelectCRCMode(1))
+ //turn on CRCs for all cards
+ { Status = 0x01; return Status; }
+
+ t = 0;
+ do
+ {
+ Command(58, 0, Workspace);
+ //check the OCR again
+ t++;
+ } while ((Workspace[0] || !(Workspace[1] & 0x80)) && (t < Timeout));
+ //check the power up status
+ if (t == Timeout) { Status = 0x01; return Status; }
+ for (unsigned char i = 0; i < 4; i++)
+ //record the OCR
+ {
+ OCR[i] = Workspace[i + 1];
+ }
+ Capacity = (OCR[0] & 0x40) == 0x40;
+ //record the capacity
+
+ t = 0;
+ do
+ {
+ do
+ {
+ Command(9, 0, Workspace);
+ //read the CSD
+ t++;
+ } while (Workspace[0] && (t < Timeout));
+ if (t == Timeout) { Status = 0x01; return Status; }
+ ChipSelect.write(0);
+ do
+ {
+ t++;
+ } while ((DataLines.write(0xFF) != 0xFE) && (t < Timeout));
+ //get to the start data block token
+ if (t == Timeout) { ChipSelect.write(1); DataLines.write(0xFF);
+ Status = 0x01; return Status; }
+ for (unsigned char i = 0; i < 16; i++)
+ //record the CSD
+ {
+ CSD[i] = DataLines.write(0xFF);
+ }
+ Workspace[2] = DataLines.write(0xFF);
+ Workspace[3] = DataLines.write(0xFF);
+ //save the CSD CRC16
+ ChipSelect.write(1);
+ DataLines.write(0xFF);
+ DataCRC(16, CSD, Workspace);
+ //calculate the CSD CRC16
+ Workspace[4] = 0;
+ for (unsigned char i = 0; i < 15; i++)
+ {
+ Workspace[4] = CommandCRCTable[Workspace[4]] ^ CSD[i];
+ }
+ Workspace[4] = CommandCRCTable[Workspace[4]] | 0x01;
+ //calculate the CSD CRC7
+ t++;
+ } while (((Workspace[0] != Workspace[2]) || (Workspace[1] != Workspace[3])
+ || (Workspace[4] != CSD[15])) && (t < Timeout));
+ //perform all CSD CRCs
+ if (t == Timeout) { Status = 0x01; return Status; }
+
+ if (((CSD[3] & 0x07) > 0x02) ||
+ (((CSD[3] & 0x78) > 0x30) && ((CSD[3] & 0x07) > 0x01)))
+ //read the CSD card speed bits and speed up card operations
+ {
+ DataLines.frequency(25000000);
+ //maximum frequency is 25MHz
+ }
+ else
+ {
+ Workspace[0] = 1;
+ for (unsigned char i = 0; i < (CSD[3] & 0x07); i++)
+ {
+ Workspace[0] *= 10;
+ //the first three bits are a power of ten multiplier for speed
+ }
+ switch (CSD[3] & 0x78)
+ {
+ case 0x08: DataLines.frequency(Workspace[0] * 100000); break;
+ case 0x10: DataLines.frequency(Workspace[0] * 120000); break;
+ case 0x18: DataLines.frequency(Workspace[0] * 140000); break;
+ case 0x20: DataLines.frequency(Workspace[0] * 150000); break;
+ case 0x28: DataLines.frequency(Workspace[0] * 200000); break;
+ case 0x30: DataLines.frequency(Workspace[0] * 250000); break;
+ case 0x38: DataLines.frequency(Workspace[0] * 300000); break;
+ case 0x40: DataLines.frequency(Workspace[0] * 350000); break;
+ case 0x48: DataLines.frequency(Workspace[0] * 400000); break;
+ case 0x50: DataLines.frequency(Workspace[0] * 450000); break;
+ case 0x58: DataLines.frequency(Workspace[0] * 500000); break;
+ case 0x60: DataLines.frequency(Workspace[0] * 550000); break;
+ case 0x68: DataLines.frequency(Workspace[0] * 600000); break;
+ case 0x70: DataLines.frequency(Workspace[0] * 700000); break;
+ case 0x78: DataLines.frequency(Workspace[0] * 800000); break;
+ default: break;
+ }
+ }
+
+ if (CSD[4] & 0x40)
+ //check for switch command class support
+ {
+ t = 0;
+ do
+ {
+ Command(6, 2147483649, Workspace);
+ //switch to high-speed mode (SDR25, 50MHz)
+ t++;
+ } while (Workspace[0] && (Workspace[0] != 0x04) && (t < Timeout));
+ //some cards that support switch class commands respond with
+ //"illegal command"
+ if (t == Timeout) { Status = 0x01; return Status; }
+ if (!Workspace[0])
+ {
+ do
+ {
+ ChipSelect.write(0);
+ do
+ {
+ t++;
+ } while ((DataLines.write(0xFF) != 0xFE) && (t < Timeout));
+ //get to the start data block token
+ if (t == Timeout) { ChipSelect.write(1);
+ DataLines.write(0xFF); Status = 0x01; return Status; }
+ for (unsigned char i = 0; i < 64; i++)
+ //gather the function status register
+ {
+ FSR[i] = DataLines.write(0xFF);
+ }
+ Workspace[2] = DataLines.write(0xFF);
+ Workspace[3] = DataLines.write(0xFF);
+ //record the CRC16
+ ChipSelect.write(1);
+ DataLines.write(0xFF);
+ DataCRC(64, FSR, Workspace);
+ //calculate the CRC16
+ t++;
+ } while (((Workspace[0] != Workspace[2])
+ || (Workspace[1] != Workspace[3])) && (t < Timeout));
+ //perform the CRC
+ if (t == Timeout) { Status = 0x01; return Status; }
+ if ((FSR[13] & 0x02) && ((FSR[16] & 0x0F) == 0x01))
+ {
+ DataLines.frequency(50000000);
+ //increase the speed if the function switch was successful
+ }
+ }
+ }
+
+ if (SelectCRCMode(0))
+ { Status = 0x01; return Status; }
+ //turn off CRCs
+
+ Status = 0x00;
+ return Status;
+}
+
+void SDCard::Command(
+ unsigned char Index, unsigned int Argument, unsigned char* Response)
+{
+ CommandCRC(&Index, &Argument, Response);
+ //calculate the CRC7
+ ChipSelect.write(0);
+ //assert chip select low to synchronize the command
+ DataLines.write(0x40 | Index);
+ //the index is assumed valid, commands start with bits 01
+ DataLines.write(((char*)&Argument)[3]);
+ DataLines.write(((char*)&Argument)[2]);
+ DataLines.write(((char*)&Argument)[1]);
+ DataLines.write(((char*)&Argument)[0]);
+ //send the argument bytes in order from MSB to LSB
+ DataLines.write(*Response);
+ //send the CRC7
+ t = 0;
+ do
+ {
+ Response[0] = DataLines.write(0xFF);
+ //clock the card high to let it run operations, the first byte
+ //will be busy (all high), the response will be sent later
+ t++;
+ } while ((Response[0] & 0x80) && (t < Timeout));
+ //check for a response by testing if the first bit is low
+ if ((Index == 8) || (Index == 13) || (Index == 58))
+ //if the command returns a larger response, get the rest of it
+ {
+ for (unsigned char i = 1; i < 5; i++)
+ {
+ Response[i] = DataLines.write(0xFF);
+ }
+ }
+ ChipSelect.write(1);
+ //assert chip select high to synchronize command
+ DataLines.write(0xFF);
+ //clock the deselected card high to complete processing for some cards
+}
+
+void SDCard::CommandCRC(
+ unsigned char* IndexPtr, unsigned int* ArgumentPtr, unsigned char* Result)
+{
+ if (CRCMode)
+ //only calculate if data-checks are desired
+ {
+ Result[0] =
+ CommandCRCTable[
+ CommandCRCTable[
+ CommandCRCTable[
+ CommandCRCTable[
+ CommandCRCTable[
+ *IndexPtr | 0x40
+ ] ^ ((char*)ArgumentPtr)[3]
+ ] ^ ((char*)ArgumentPtr)[2]
+ ] ^ ((char*)ArgumentPtr)[1]
+ ] ^ ((char*)ArgumentPtr)[0]
+ ] | 0x01;
+ //calculate the CRC7, SD protocol requires the last bit to be high
+ }
+ else
+ //if no data-checking is desired, the return bits are not used
+ {
+ Result[0] = 0xFF;
+ }
+}
+
+void SDCard::DataCRC(
+ unsigned short Length, unsigned char* Data, unsigned char* Result)
+{
+ if (CRCMode)
+ //only calculate if data-checks are desired
+ {
+ unsigned char Reference;
+ //store the current CRC16 lookup value
+ Result[0] = 0x00;
+ Result[1] = 0x00;
+ //initialize the result carrier
+ for (unsigned short i = 0; i < Length; i++)
+ //step through each byte of the data to be checked
+ {
+ Reference = Result[0];
+ //record the current CRC16 lookup for both bytes
+ Result[0] = DataCRCTable[2 * Reference] ^ Result[1];
+ //the first byte result is XORed with the old second byte
+ Result[1] = DataCRCTable[(2 * Reference) + 1] ^ Data[i];
+ //the second byte result is XORed with the new data byte
+ }
+ for (unsigned char i = 0; i < 2; i++)
+ //the final result must be XORed with two 0x00 bytes
+ {
+ Reference = Result[0];
+ Result[0] = DataCRCTable[2 * Reference] ^ Result[1];
+ Result[1] = DataCRCTable[(2 * Reference) + 1];
+ }
+ }
+ else
+ //if no data-checking is desired, the return bits are not used
+ {
+ Result[0] = 0xFF;
+ Result[1] = 0xFF;
+ }
+}
+
+void SDCard::GenerateCRCTable(unsigned char Size,
+ unsigned long long Generator, unsigned char* Table)
+{
+ unsigned char Index[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
+ //this will hold information from the generator; the position indicates
+ //the order of the encountered 1, the value indicates its position in
+ //the generator, the 9th entry indicates the number of 1's encountered
+
+ for (unsigned char i = 0; i < 64; i++)
+ //shift generator left until the first bit is high
+ {
+ if (((char*)&Generator)[7] & 0x80)
+ { break; }
+ Generator = Generator << 1;
+ }
+ for (unsigned char i = 0; i < Size; i++)
+ //initialize table
+ {
+ Table[i] = 0x00;
+ }
+ for (unsigned char i = 0; i < 8; i++)
+ //increment through each generator bit
+ {
+ if ((0x80 >> i) & ((unsigned char*)&Generator)[7])
+ //if a 1 is encountered in the generator, record its order and
+ //location and increment the counter
+ {
+ Index[Index[8]] = i;
+ Index[8]++;
+ }
+ for (unsigned char j = 0; j < (0x01 << i); j++)
+ //each bit doubles the number of XOR operations
+ {
+ for (unsigned char k = 0; k < Size; k++)
+ //we need to precalculate each byte in the CRC table
+ {
+ Table[(Size * ((0x01 << i) + j)) + k]
+ = Table[(Size * j) + k];
+ //each power of two is equal to all previous entries with
+ //an added XOR with the generator on the leftmost bit and
+ //on each succeeding 1 in the generator
+ for (unsigned char l = 0; l < Index[8]; l++)
+ //increment through the encountered generator 1s
+ {
+ Table[(Size * ((0x01 << i) + j)) + k] ^=
+ (((unsigned char*)&Generator)[7-k]
+ << (i + 1 - Index[l]));
+ Table[(Size * ((0x01 << i) + j)) + k] ^=
+ (((unsigned char*)&Generator)[6-k]
+ >> (7 - i + Index[l]));
+ //XOR the new bit and the new generator 1s
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/SDCard.h Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,120 @@
+//mbed Microcontroller Library
+//SDCard Interface
+//Copyright 2010
+//Thomas Hamilton
+
+#ifndef SDCardLibrary
+#define SDCardLibrary
+
+#include "stdint.h"
+#include "mbed.h"
+#include "FATFileSystem.h"
+
+class SDCard : public FATFileSystem
+{
+ private:
+ SPI DataLines;
+ DigitalOut ChipSelect;
+ //physical chip interface
+
+ unsigned int t;
+ //timeout counter
+ unsigned int Timeout;
+ //timeout limit
+ bool CRCMode;
+ //low: CRCs disabled, high: CRCs enabled
+ bool Capacity;
+ //low: low-capacity, high: high-capacity
+ bool Version;
+ //low: version 1, high: version 2
+ unsigned char Status;
+ //0x00: Ready, 0x01: not initialized
+ unsigned char FSR[64];
+ //function status register
+ unsigned char CSD[16];
+ //card-specific data register
+ unsigned char OCR[4];
+ //operating conditions register
+ unsigned char DataCRCTable[512];
+ //CRC16 CCITT lookup table
+ unsigned char CommandCRCTable[256];
+ //CRC7 SD command lookup table
+
+ unsigned char Initialize();
+ //complete all initialization operations
+ void Command(unsigned char Index,
+ unsigned int Argument, unsigned char* Response);
+ //sends the SD command with the given Index and Argument to the SD
+ //card and stores the SD Response
+ void CommandCRC(unsigned char* IndexPtr,
+ unsigned int* ArgumentPtr, unsigned char* Result);
+ //calculates the SD proprietary CRC7 result of an SD command
+ //(composed of a command index and argument) and stores the one-
+ //byte solution in Result
+ void DataCRC(
+ unsigned short Length, unsigned char* Data, unsigned char* Result);
+ //calculates the CRC16 CCITT result of the number of bytes in Data
+ //given by Length and stores the two-byte solution in Result
+ //assumes DataCRCTable has already been calculated
+ void GenerateCRCTable(unsigned char Size,
+ unsigned long long Generator, unsigned char* Table);
+ //pre-calculates CRC results from the given Generator for efficient
+ //checking; assumes pre-allocated Table is large enough to hold the
+ //number of bytes given in Size
+
+ virtual unsigned char disk_initialize();
+ virtual unsigned char disk_status();
+ virtual unsigned char disk_read(unsigned char* buff,
+ unsigned long sector, unsigned char count);
+ virtual unsigned char disk_write(const unsigned char* buff,
+ unsigned long sector, unsigned char count);
+ virtual unsigned char disk_sync();
+ virtual unsigned long disk_sector_count();
+ virtual unsigned short disk_sector_size();
+ virtual unsigned long disk_block_size();
+ //FAT system virtual functions that are called by the FAT module
+
+ public:
+ SDCard(PinName mosi, PinName miso, PinName sck, PinName cs,
+ const char* DiskName);
+ //constructor needs SPI pins, DigitalOut pins, and a directory name
+ virtual ~SDCard();
+ //destructor deallocates tables and registers
+ unsigned char Format(unsigned int AllocationUnit);
+ //formats the card FAT with given AllocationUnit in sectors; the
+ //maximum is 32768 sectors
+ unsigned char Log(unsigned char Control, unsigned char Data);
+ //multipurpose single-byte raw data-logging method with three modes
+ //Control description
+ // 0 synchronizes card and resets internal counter to
+ // finalize read or write operations
+ // 1 successively write Data to a raw byte address in order
+ // starting at address 0
+ // 2 successively read and return a raw data byte in order
+ // starting at address 0
+ //return byte from sync or write operations and input Data of sync
+ //or read operations are not used; writing with this function will
+ //deformat the drive
+ unsigned char Write(unsigned int Address, unsigned char* Data);
+ //write the first 512 byte sector of Data to the card at the given
+ //Address
+ unsigned char Write(unsigned int Address,
+ unsigned char SectorCount, unsigned char* Data);
+ //write the first given SectorCount of 512 byte sectors of Data to
+ //the card at the given Address
+ unsigned char Read(unsigned int Address, unsigned char* Data);
+ //read the first 512 byte sector from card at the given Address
+ //into Data
+ unsigned char Read(unsigned int Address,
+ unsigned char SectorCount, unsigned char* Data);
+ //read the first given SectorCount of 512 byte sectors from the
+ //card at the given Address into Data
+ unsigned char SelectCRCMode(bool Mode);
+ //toggle CRC mode; low: CRCs disabled, high: CRCs enabled, default:
+ //CRCs disabled
+ void SetTimeout(unsigned int Retries);
+ //change the number of retries for interface functions; increase if
+ //lines are unreliable; default: 1024, minimum: 1024
+};
+
+#endif
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TextLCD.lib Mon Feb 13 02:11:20 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/simon/code/TextLCD/#e4cb7ddee0d3
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mRotaryEncoder.lib Mon Feb 13 02:11:20 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/charly/code/mRotaryEncoder/#75ddffaf3721
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,873 @@
+
+#include <scmRTOS.h>
+#include "mbed.h"
+#include <iostream>
+#include "TextLCD.h"
+//#include "DebounceIn.h"
+#include <math.h>
+#include <string>
+#define MESSAGE_BUFFER_SIZE 1024
+#include "time.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "MODSERIAL.h"
+#include "stdint.h"
+#include "DirHandle.h"
+#include "SDCard.h"
+#include "mRotaryEncoder.h"
+
+SDCard Logger(p5, p6, p7, p8, "SDCard");
+mRotaryEncoder wheel(p16, p17, p15,PullUp,1500);
+Serial Computer(USBTX, USBRX);
+DigitalOut LED(LED1);
+MODSERIAL gsm(p9,p10);
+
+AnalogIn WindVoltage(p20);
+TextLCD lcd(p22, p23, p24, p25, p26, p27);
+
+
+
+char Emergency[40];
+char line= '\n';
+char c;
+char str[40];
+char Hi;
+struct tm t;
+int timecount = 0;
+char buffer1[32];
+char buf[18];
+char buf1[40];
+char buf2[4];
+char buf3[256];
+char buf4[20];
+char * pch;
+char ctr= 0x1A;
+char Key[]="OK";
+int Status = 0;
+int Status1=0;
+int Status2=0;
+int Status3=0;
+int Status4=0;
+int Status5=0;
+int Status6=0;
+int Status7=0;
+
+float FinalVoltage=0;
+char SMS1[] ="#Read Volt\r\n";
+char SMS2[] ="#Read Current\r\n";
+char SMS3[] ="#Read All\r\n";
+char SMS4[] ="#Start Log\r\n";
+char SMS5[] ="#Stop Log\r\n";
+char SMS6[] ="#Change Emergency\r\n";
+char SMS7[] ="#Reset Emergerncy\r\n";
+char SMS[15];
+int logging =0;
+int logon = 0;
+int logoff = 0;
+int log_set =0;
+int log_start = 0;
+
+int counter=0;
+int counter1=0;
+bool enc_pressed;
+bool enc_rotated;
+
+
+// OS timer/interrupt
+OS::TEventFlag GSM;
+OS::TEventFlag GSM1;
+OS::TEventFlag GSM2;
+OS::TEventFlag GSM3;
+OS::TEventFlag GSM4;
+OS::TEventFlag GSMSTART;
+OS::TEventFlag GSMSTOP;
+OS::TEventFlag LOGSTOP;
+OS::TEventFlag LOGSTART;
+//---------------------------------------------------------------------------
+
+
+void toggle()
+{
+switch (counter1) {
+
+ default :
+ // wait(0.5);
+ lcd.cls();
+ lcd.locate(0,0); //Default State, Show time normally.
+ lcd.printf("%s", buffer1);
+ counter1 = 0;
+ break;
+
+ case 2 :
+ lcd.cls();
+ lcd.locate(0,0);
+ lcd.printf("Voltage");
+
+ switch (counter) {
+ case 1 :
+ lcd.cls();
+ lcd.locate(0,0);
+ lcd.printf("The Voltage is 3.00V");
+ wait(2);
+ counter=0;
+ break;
+
+ }
+ wait(0.5);
+ break;
+
+
+ case 4 :
+ lcd.cls();
+ lcd.locate(0,0);
+ lcd.printf("Current");
+
+ switch (counter) {
+ case 1 :
+ lcd.cls();
+ lcd.locate(0,0);
+ lcd.printf("The Current is 2.34A");
+ wait(2);
+ counter=0;
+ break;
+
+ }
+ wait(0.5);
+ break;
+
+ case 6 :
+ lcd.cls();
+ lcd.locate(0,0);
+ lcd.printf("On log SD card");
+
+ switch (counter) {
+ case 1 :
+ lcd.cls();
+ lcd.locate(0,0);
+ lcd.printf("Logging commence");
+ wait(2);
+ counter=0;
+ break;
+
+ }
+ wait(0.5);
+ break;
+
+
+ case 8 :
+ lcd.cls();
+ lcd.locate(0,0);
+ lcd.printf("Off log SD card");
+
+ switch (counter) {
+ case 1 :
+ lcd.cls();
+ lcd.locate(0,0);
+ lcd.printf("Logging aborted");
+ wait(2);
+ counter=0;
+ break;
+
+ }
+ wait(0.5);
+ break;
+
+ case 10 :
+ lcd.cls();
+ lcd.locate(0,0);
+ lcd.printf("Reset SMS Alert");
+
+ switch (counter) {
+ case 1 :
+ lcd.cls();
+ lcd.locate(0,0);
+ lcd.printf("Do you want to reset?");
+ break;
+ case 2 :
+ lcd.cls();
+ lcd.locate(0,0);
+ lcd.printf("Yes, reset done.");
+ counter=0;
+ break;
+
+ }
+ wait(0.5);
+ break;
+
+ case 12 :
+ lcd.cls();
+ lcd.locate(0,0);
+ lcd.printf("Emergency number");
+
+ switch (counter) {
+ case 1 :
+ lcd.cls();
+ lcd.locate(0,0);
+ lcd.printf("Number:999");
+ wait(5);
+ counter=0;
+ break;
+
+ }
+ wait(0.5);
+ break;
+
+
+
+
+ }
+}
+
+void trigger_sw() {
+ enc_pressed = true; // just set the flag, rest is done outside isr
+}
+
+//interrup-Handler for rotary-encoder rotation
+void trigger_rotated() {
+ enc_rotated = true; // just set the flag, rest is done outside isr
+}
+void Push() {
+ if (enc_pressed==true) {
+ enc_pressed=false;
+ counter++;
+ }
+
+}
+
+void Turn() {
+ if (enc_rotated == true) {
+ enc_rotated = false;
+
+ counter1++;
+ }
+
+}
+
+void TimeMessage() //Get character (e.g. Printf the Words from serial)
+ {
+ strcpy (str,&Hi);
+ do {
+ c=Computer.getc();
+ Computer.putc (c);
+ strcat (str,&c);
+ }while (c != ' ');
+ }
+
+void GSM_CLEAR_SMS() //Delete 1st SMS
+{
+ gsm.printf("AT+CMGD=0,1\r\n");
+ wait(0.2);
+}
+
+
+
+void GSM_GETCHAR() //Get character (e.g. Printf the Words from serial)
+{
+ do {
+ c=gsm.getc();
+ Computer.putc (c);
+ }while (c != line);
+}
+
+void GSM_GETCHAR1() //Get character (e.g. Printf the Words from serial)
+{
+ strcpy (str,&Hi);
+ do {
+ c=gsm.getc();
+ Computer.putc (c);
+ strcat (str,&c);
+
+ }while (c != line);
+ Computer.printf("%s\n\r",str);
+ // Status = strcmp (SMS1,str);
+ // pc.printf("%d\n\r", Status);
+
+}
+
+void Replyvolt()
+{
+ gsm.rxBufferFlush();
+ gsm.printf("AT+CMGS=%s\r\n",buf4); //Send SMS
+ GSM_GETCHAR();
+ gsm.printf("%s The Voltage is %.2f V %c", buffer1, FinalVoltage,ctr);
+}
+
+void Replycurent()
+{
+ gsm.rxBufferFlush();
+ gsm.printf("AT+CMGS=%s\r\n",buf4); //Send SMS
+ GSM_GETCHAR();
+ gsm.printf("%s The Current is %.2f V %c", buffer1, FinalVoltage,ctr);
+}
+
+void Replyall()
+{
+ gsm.rxBufferFlush();
+ gsm.printf("AT+CMGS=%s\r\n",buf4); //Send SMS
+ GSM_GETCHAR();
+ gsm.printf("%s Voltage: %.2f V, Current: %.2f A %c", buffer1,FinalVoltage, FinalVoltage,ctr);
+}
+
+void Replylogstart()
+{
+ gsm.rxBufferFlush();
+ gsm.printf("AT+CMGS=%s\r\n",buf4); //Send SMS
+ GSM_GETCHAR();
+ if(logging ==1)
+ {
+
+ gsm.printf(" Logging of SD CARD: Start %c",ctr);
+
+ GSMSTART.Signal();
+ }
+ if(logging == 0)
+ {
+ gsm.printf("%s Logging of SD Card: Has Started since %c", buffer1, FinalVoltage,ctr);
+ }
+
+}
+
+void Replylogstop()
+{
+ gsm.rxBufferFlush();
+ gsm.printf("AT+CMGS=%s\r\n",buf4); //Send SMS
+ GSM_GETCHAR();
+ if(logging ==0)
+ {
+
+ gsm.printf("Logging of SD CARD: Stopped %c", ctr);
+ GSMSTOP.Signal();
+ }
+ if(logging == 1)
+ {
+ gsm.printf("%s Logging of SD Card: Has Stopped since %c", buffer1,ctr);
+ }
+
+}
+
+
+void CheckSMSMSG()
+{
+Status1 = strcmp (SMS1,str);
+Status2 = strcmp (SMS2,str);
+Status3 = strcmp (SMS3,str);
+Status4 = strcmp (SMS4,str);
+Status5 = strcmp (SMS5,str);
+Status6 = strcmp (SMS6,str);
+Status7 = strcmp (SMS7,str);
+
+if(Status1 == 0)
+{
+Replyvolt();
+}
+
+if(Status2 == 0)
+{
+Replycurent();
+}
+
+if(Status3 == 0)
+{
+Replyall();
+}
+if(Status4 == 0)
+{
+ GSM3.SignalISR();
+}
+if(Status5 == 0)
+{
+ GSM4.SignalISR();
+}
+
+if(Status6 == 0)
+{
+}
+
+if(Status7 == 0)
+{
+}
+else
+{
+ Computer.printf("Random SMS\n\r");}
+}
+
+
+void GSM_CHECK_MSG() //Check if msg is OK,Error.
+{
+ Status = strcmp (Key,buf1);
+//pc.printf("%d\n\r", Status); //See the error Number
+}
+
+
+
+void ExtractNum() //Extract the number from the sender's sms
+{
+ pch = strtok (buf3," ,.-");
+ pch = strtok (NULL, " ,.-");
+ //pc.printf ("pch is %s\n\r",pch); //See Number extracted
+ strcpy (buf4,pch);
+ Computer.printf("%s\n\r", buf4); //Number extracted in "+6512345678"
+}
+
+
+
+void ReceiveMessage()
+{
+ gsm.rxBufferFlush();
+ gsm.printf("AT+CMGR=\"1\"\n\r"); //Read Message.
+ gsm.scanf("%s",buf);
+
+ // pc.printf("%s\r\n",buf);
+ gsm.scanf("%s",buf1);
+ // pc.printf("%s\n\r",buf1);
+
+ GSM_CHECK_MSG(); //Check For reply. If Ok, no sms. if error, no sms.
+}
+
+
+void GSM_STARTUP()
+{
+
+ gsm.baud(9600);
+ Computer.baud(9600);
+ gsm.format(8, Serial::None, 1);
+
+ gsm.rxBufferFlush();
+ Computer.printf("\n\rSetting Up GSM\n\r");
+ Computer.printf("---------------\n\r\n\r");
+ gsm.printf("AT+CMGF=\"1\"\r\n");
+ gsm.scanf("%s",buf);
+ // pc.printf("buf1 is: %s\r\n",buf);
+
+ gsm.scanf("%s",buf3); //scanf OK
+ Computer.printf("%s\r\n",buf3);
+ GSM_CLEAR_SMS();
+ do
+ {
+ Computer.printf("Enter Emergency Number[Space]: ");
+ TimeMessage();
+ Computer.printf("\n\r\n\r");
+ Computer.printf("Is this your Number[y/n]: %s\n\r", str);
+ strcpy (Emergency,str);
+ c=Computer.getc();
+ Computer.printf("\n\r\n\r");
+ }while(c !='y');
+ Computer.printf("...GSM Setup Done\n\r\n\r\n\r");
+ gsm.rxBufferFlush();
+}
+
+/*void TimeMessage() //Get character (e.g. Printf the Words from serial)
+ {
+ strcpy (str,&Hi);
+ do {
+ c=pc.getc();
+ pc.putc (c);
+ strcat (str,&c);
+ }while (c != ' ');
+ }*/
+
+void time_setup() {
+ Computer.baud(9600);
+
+ Computer.printf("\n\rSetting Up Time\n\r");
+ Computer.printf("---------------\n\r\n\r");
+
+ // get the current time from the terminal
+
+ do{
+ Computer.printf("Enter current date and time:\n\r");
+ Computer.printf("YYYY MM DD HH MM SS\n\r");
+
+ TimeMessage();
+ t.tm_year = atoi (str);
+ TimeMessage();
+ t.tm_mon = atoi (str);
+ TimeMessage();
+ t.tm_mday= atoi (str);
+ TimeMessage();
+ t.tm_hour = atoi (str);
+ TimeMessage();
+ t.tm_min = atoi (str);
+ TimeMessage();
+ t.tm_sec = atoi (str);
+
+ Computer.printf("\n\r\n\r");
+ Computer.printf("Is the time correct [y/n]\n\r");
+ c=Computer.getc();
+ Computer.printf("\n\r");
+ }while(c !='y');
+
+ // adjust for tm structure required values
+ t.tm_year = t.tm_year - 1900;
+ t.tm_mon = t.tm_mon - 1;
+
+ // set the time
+ set_time(mktime(&t));
+
+ Computer.printf("...Time Setting Done\n\r\n\r");
+
+}
+
+void message() //Get character (e.g. Printf the Words from serial)
+{
+
+ do {
+ c=Computer.getc();
+ Computer.putc (c);
+ strcat (str,&c);
+
+ }while (c != '.');
+ Computer.printf("\n\r\n\r");
+
+
+}
+
+void Sending()
+{
+ if(Status==36)
+ {
+ time_t seconds = time(NULL);
+ gsm.scanf("%s",buf2);
+ gsm.scanf("%s",buf3); //Scan ["READ","+6512345678",,"12/01/18,23:54:25+32"]
+ ExtractNum(); //to extract number
+
+ Computer.printf("The Message is:\n\r");
+ GSM_GETCHAR();
+
+ GSM_GETCHAR1(); //Compare sms(e.g.SMSing "#ReadVolt" will give u voltage reading)
+ GSM_GETCHAR();
+
+ GSM_CLEAR_SMS(); //Delete 1st Message
+ gsm.rxBufferFlush();
+
+ gsm.rxBufferFlush();
+ }
+ }
+void View_TIME()
+{
+ do{
+ time_t seconds = time(NULL);
+ strftime(buffer1, 32, "%a %d/%m/%Y %I:%M:%S %p", localtime(&seconds));
+ lcd.printf("%s\n", buffer1);
+ wait(1);
+ timecount = timecount + 1;
+ lcd.cls();
+ }while(timecount !=3);
+ timecount = 0;
+}
+
+void View_Voltage()
+{
+ do{
+ FinalVoltage = WindVoltage * 3.3* 2.926;
+ lcd.printf("Voltage: %.2f V", FinalVoltage );
+ wait(1);
+ timecount = timecount + 1;
+ lcd.cls();
+ }while(timecount != 3);
+ timecount = 0;
+}
+
+void SD_card_setup()
+{
+Computer.printf("\n\rDo you want to start Logging[y/n]:\n\r");
+do{
+c=Computer.getc();
+ if(c=='y')
+ {
+ logging = 0;
+ log_set= 1;
+ log_start = 1;
+ }
+ if(c=='n')
+ {
+ logging = 1;
+ log_set = 1;
+ }
+ }while(log_set != 1);
+}
+
+void SD_CARD()
+{
+ if(logging == 0)
+ {
+ // Logger.SelectCRCMode(1);
+ FILE *fp = fopen("/SDCard/myfile.csv", "a+");
+ if(log_start == 1)
+ {fprintf(fp,"%s, Logging Start\n\r",buffer1);
+ log_start = 0;}
+ if(log_start == 2)
+ { fprintf(fp,"%s, Logging Stopped \n\r",buffer1);
+ log_start = 0;
+ logging = 1;
+ }
+ else
+ {
+ fprintf(fp,"%s, Voltage:, %.2f V\n\r",buffer1,FinalVoltage );}
+ fclose(fp);
+ printf("logging\n\r");
+ }
+
+}
+// process types
+typedef OS::process<OS::pr0, 800> TProc1;
+typedef OS::process<OS::pr1, 1000> TProc2;
+typedef OS::process<OS::pr2, 1500> TProc3;
+typedef OS::process<OS::pr3, 1000> TProc4;
+typedef OS::process<OS::pr4, 1000> TProc5;
+typedef OS::process<OS::pr5, 1000> TProc6;
+typedef OS::process<OS::pr6, 1000> TProc7;
+typedef OS::process<OS::pr7, 1000> TProc8;
+typedef OS::process<OS::pr8, 1000> TProc9;
+typedef OS::process<OS::pr9, 1000> TProc10;
+typedef OS::process<OS::pr10, 1000> TProc11;
+typedef OS::process<OS::pr11, 1000> TProc12;
+typedef OS::process<OS::pr12, 1000> TProc13;
+typedef OS::process<OS::pr13, 3000> TProc14;
+
+// process objects
+TProc1 Proc1;
+TProc2 Proc2;
+TProc3 Proc3;
+TProc4 Proc4;
+TProc5 Proc5;
+TProc6 Proc6;
+TProc7 Proc7;
+TProc8 Proc8;
+TProc9 Proc9;
+TProc10 Proc10;
+TProc11 Proc11;
+TProc12 Proc12;
+TProc13 Proc13;
+TProc14 Proc14;
+
+
+
+//---------------------------------------------------------------------------
+
+
+long long count = 0;
+
+int main() {
+ Logger.SelectCRCMode(1);
+ Computer.baud(9600);
+ printf("\nInitialising ...\n");
+ gsm.baud(9600);
+
+ time_setup();
+ GSM_STARTUP();
+ SD_card_setup();
+ printf("\n\rSAMS Settup Done\n\r");
+ wait(0.5);
+ printf("\n\rRunning Program\n\r");
+ wheel.attachSW(&trigger_sw);
+ wheel.attachROT(&trigger_rotated);
+
+
+ OS::Run();
+}
+
+//---------------------------------------------------------------------------
+template<> OS_PROCESS void TProc1::Exec() {
+ for (;;) {
+
+ toggle();
+
+ // if(counter == 1)
+ // {
+// printf("Hi\n\r");
+ // }
+ Sleep(2000);
+
+ }
+}
+
+//---------------------------------------------------------------------------
+template<> OS_PROCESS void TProc2::Exec() {
+ for (;;) {
+ GSM.Wait();
+ ReceiveMessage();
+ GSM1.SignalISR();
+ Sleep(2000);
+
+ }
+}
+
+//---------------------------------------------------------------------------
+template<> OS_PROCESS void TProc3::Exec() {
+ for (;;) {
+ GSM1.Wait();
+ Sending();
+ gsm.rxBufferFlush();
+ GSM2.SignalISR();
+ Sleep(2000);
+
+
+ }
+}
+//---------------------------------------------------------------------------
+template<> OS_PROCESS void TProc4::Exec() {
+ for (;;) {
+ if(Status==36){
+ GSM2.Wait();
+ CheckSMSMSG(); //Reply SMS
+ Computer.printf("Sent SMS\n\r");
+ gsm.rxBufferFlush();
+ gsm.rxBufferFlush();
+ GSM_CLEAR_SMS();
+ gsm.rxBufferFlush();}
+
+
+ Sleep(2000);
+
+
+ }
+}
+//---------------------------------------------------------------------------
+template<> OS_PROCESS void TProc5::Exec() {
+ for (;;) {
+
+ GSM3.Wait();
+ Replylogstart();
+
+ Sleep(2000);
+
+
+ }
+}
+
+//---------------------------------------------------------------------------
+template<> OS_PROCESS void TProc6::Exec() {
+ for (;;) {
+
+
+ GSM4.Wait();
+ Replylogstop();
+ Sleep(2000);
+
+
+ }
+}
+//---------------------------------------------------------------------------
+template<> OS_PROCESS void TProc7::Exec() {
+ for (;;) {
+
+
+ GSMSTART.Wait();
+ logging = 0;
+ // logon = 1;
+ log_start = 1;
+ printf("start\n\r");
+ // LOGSTART.Signal();
+ Sleep(2000);
+
+
+ }
+}
+//---------------------------------------------------------------------------
+template<> OS_PROCESS void TProc8::Exec() {
+ for (;;) {
+
+
+ GSMSTOP.Wait();
+ // logging = 1;
+ log_start = 2;
+ // logon = 1;
+ Sleep(2000);
+
+
+ }
+}
+//---------------------------------------------------------------------------
+template<> OS_PROCESS void TProc9::Exec() {
+ for (;;) {
+
+ LOGSTART.Wait();
+ lcd.printf("Logging of SD CARD: Start\n");
+
+
+
+ Sleep(2000);
+ }
+
+}
+
+
+
+
+//---------------------------------------------------------------------------
+template<> OS_PROCESS void TProc10::Exec() {
+ for (;;) {
+
+ Sleep(2000);
+
+ }
+}
+
+
+//---------------------------------------------------------------------------
+template<> OS_PROCESS void TProc11::Exec() {
+ for (;;) {
+ FinalVoltage = WindVoltage * 3.3* 2.926;
+ printf("p2\n\r");
+ Sleep(2000);
+
+ }
+}
+
+//---------------------------------------------------------------------------
+template<> OS_PROCESS void TProc12::Exec() {
+ for (;;) {
+
+ //Sleep(2000);
+ // lcd.cls();
+ // lcd.printf("Voltage: %.2f\n", FinalVoltage);
+ // printf("[2] %lld\n\r", count);
+ Sleep(2000);
+
+ }
+}
+
+//---------------------------------------------------------------------------
+template<> OS_PROCESS void TProc13::Exec() {
+ for (;;) {
+ // LOGSTART.Wait();
+ // SD_CARD();
+ // Sleep(1500);
+ Sleep(2000);
+
+ }
+}
+
+//---------------------------------------------------------------------------
+template<> OS_PROCESS void TProc14::Exec() {
+ for (;;) {
+ printf("SDCARD\n\r");
+ SD_CARD();
+ Sleep(1500);
+
+ }
+}
+//---------------------------------------------------------------------------
+void OS::SystemTimerUserHook() {
+ count++;
+ if(gsm.rxBufferGetCount() != 0)
+
+ { GSM.Signal();}
+ time_t seconds = time(NULL);
+ strftime(buffer1, 32, "%a %d/%m/%Y %I:%M:%S %p", localtime(&seconds));
+ Push();
+ Turn();
+ wheel.attachSW(&trigger_sw);
+ wheel.attachROT(&trigger_rotated);
+
+
+
+
+}
+
+//---------------------------------------------------------------------------
+void OS::IdleProcessUserHook() {
+ __WFI();
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Mon Feb 13 02:11:20 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/3944f1e2fa4f
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/scmRTOS/Common/OS_Kernel.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,115 @@
+//******************************************************************************
+//*
+//* FULLNAME: Single-Chip Microcontroller Real-Time Operating System
+//*
+//* NICKNAME: scmRTOS
+//*
+//* PURPOSE: OS Kernel Source
+//*
+//* Version: 3.10
+//*
+//* $Revision: 256 $
+//* $Date:: 2010-01-22 #$
+//*
+//* Copyright (c) 2003-2010, Harry E. Zhurov
+//*
+//* Permission is hereby granted, free of charge, to any person
+//* obtaining a copy of this software and associated documentation
+//* files (the "Software"), to deal in the Software without restriction,
+//* including without limitation the rights to use, copy, modify, merge,
+//* publish, distribute, sublicense, and/or sell copies of the Software,
+//* and to permit persons to whom the Software is furnished to do so,
+//* subject to the following conditions:
+//*
+//* The above copyright notice and this permission notice shall be included
+//* in all copies or substantial portions of the Software.
+//*
+//* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+//* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+//* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+//* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+//* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+//* THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//*
+//* =================================================================
+//* See http://scmrtos.sourceforge.net for documentation, latest
+//* information, license and contact details.
+//* =================================================================
+//*
+//******************************************************************************
+
+#include "scmRTOS.h"
+
+using namespace OS;
+//------------------------------------------------------------------------------
+OS::TKernel OS::Kernel;
+
+//------------------------------------------------------------------------------
+#if scmRTOS_CONTEXT_SWITCH_SCHEME == 0
+void TKernel::Sched()
+{
+ byte NextPrty = GetHighPriority(ReadyProcessMap);
+ if(NextPrty != CurProcPriority)
+ {
+ TStackItem* Next_SP = ProcessTable[NextPrty]->StackPointer;
+ TStackItem** Curr_SP_addr = &(ProcessTable[CurProcPriority]->StackPointer);
+ CurProcPriority = NextPrty;
+ OS_ContextSwitcher(Curr_SP_addr, Next_SP);
+ }
+}
+#else
+//------------------------------------------------------------------------------
+void TKernel::Sched()
+{
+ byte NextPrty = GetHighPriority(ReadyProcessMap);
+ if(NextPrty != CurProcPriority)
+ {
+ SchedProcPriority = NextPrty;
+
+ RaiseContextSwitch();
+ do
+ {
+ EnableContextSwitch();
+ DUMMY_INSTR();
+ DisableContextSwitch();
+ }
+ while(!IsContextSwitchDone());
+ }
+}
+//------------------------------------------------------------------------------
+TStackItem* OS_ContextSwitchHook(TStackItem* sp) { return OS::Kernel.ContextSwitchHook(sp); }
+//------------------------------------------------------------------------------
+#endif // scmRTOS_CONTEXT_SWITCH_SCHEME
+//------------------------------------------------------------------------------
+void TBaseProcess::Sleep(TTimeout timeout)
+{
+ TCritSect cs;
+
+ Kernel.ProcessTable[Kernel.CurProcPriority]->Timeout = timeout;
+ Kernel.SetProcessUnready(Kernel.CurProcPriority);
+ Kernel.Scheduler();
+}
+//------------------------------------------------------------------------------
+void OS::WakeUpProcess(TBaseProcess& p)
+{
+ TCritSect cs;
+
+ if(p.Timeout)
+ {
+ p.Timeout = 0;
+ Kernel.SetProcessReady(p.Priority);
+ Kernel.Scheduler();
+ }
+}
+//------------------------------------------------------------------------------
+void OS::ForceWakeUpProcess(TBaseProcess& p)
+{
+ TCritSect cs;
+
+ p.Timeout = 0;
+ Kernel.SetProcessReady(p.Priority);
+ Kernel.Scheduler();
+}
+//------------------------------------------------------------------------------
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/scmRTOS/Common/OS_Kernel.h Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,439 @@
+//******************************************************************************
+//*
+//* FULLNAME: Single-Chip Microcontroller Real-Time Operating System
+//*
+//* NICKNAME: scmRTOS
+//*
+//* PURPOSE: OS Kernel Header. Declarations And Definitions
+//*
+//* Version: 3.10
+//*
+//* $Revision: 256 $
+//* $Date:: 2010-01-22 #$
+//*
+//* Copyright (c) 2003-2010, Harry E. Zhurov
+//*
+//* Permission is hereby granted, free of charge, to any person
+//* obtaining a copy of this software and associated documentation
+//* files (the "Software"), to deal in the Software without restriction,
+//* including without limitation the rights to use, copy, modify, merge,
+//* publish, distribute, sublicense, and/or sell copies of the Software,
+//* and to permit persons to whom the Software is furnished to do so,
+//* subject to the following conditions:
+//*
+//* The above copyright notice and this permission notice shall be included
+//* in all copies or substantial portions of the Software.
+//*
+//* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+//* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+//* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+//* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+//* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+//* THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//*
+//* =================================================================
+//* See http://scmrtos.sourceforge.net for documentation, latest
+//* information, license and contact details.
+//* =================================================================
+//*
+//*****************************************************************************
+
+#ifndef OS_KERNEL_H
+#define OS_KERNEL_H
+
+#include <stddef.h>
+#include <commdefs.h>
+#include <usrlib.h>
+
+//------------------------------------------------------------------------------
+
+//==============================================================================
+extern "C" void OS_Start(TStackItem* sp);
+
+#if scmRTOS_CONTEXT_SWITCH_SCHEME == 0
+ extern "C" void OS_ContextSwitcher(TStackItem** Curr_SP, TStackItem* Next_SP);
+#else
+ extern "C" TStackItem* OS_ContextSwitchHook(TStackItem* sp);
+#endif
+
+//==============================================================================
+
+//------------------------------------------------------------------------------
+//
+//
+// NAME : OS
+//
+// PURPOSE : Namespace for all OS stuff
+//
+// DESCRIPTION: Includes: Kernel,
+// Processes,
+// Mutexes,
+// Event Flags,
+// Byte-wide Channels,
+// Arbitrary-type Channels,
+// Messages
+//
+namespace OS
+{
+ class TBaseProcess;
+
+ INLINE inline void SetPrioTag(TProcessMap& pm, const TProcessMap PrioTag) { pm |= PrioTag; }
+ INLINE inline void ClrPrioTag(TProcessMap& pm, const TProcessMap PrioTag) { pm &= ~PrioTag; }
+
+ //--------------------------------------------------------------------------
+ //
+ // NAME : TKernel
+ //
+ /// Implements kernel-level operations such as
+ /// process management, process-level scheduling,
+ /// ISR-level scheduling, system timing.
+ //
+ // DESCRIPTION:
+ //
+ //
+ class TKernel
+ {
+ //-----------------------------------------------------------
+ //
+ // Declarations
+ //
+
+
+ friend class TISRW;
+ friend class TISRW_SS;
+ friend class TBaseProcess;
+ friend class TMutex;
+ friend class TEventFlag;
+ friend class TChannel;
+ friend class TBaseMessage;
+
+ template<typename T, word size, class S> friend class channel;
+ template<typename T> friend class message;
+
+ friend void Run();
+ friend void WakeUpProcess(TBaseProcess& p);
+ friend void ForceWakeUpProcess(TBaseProcess& p);
+ friend inline bool IsProcessSleeping(const TBaseProcess& p);
+ friend inline bool IsProcessSuspended(const TBaseProcess& p);
+ friend inline dword GetTickCount();
+
+ //-----------------------------------------------------------
+ //
+ // Data
+ //
+ private:
+ byte CurProcPriority;
+ TProcessMap ReadyProcessMap;
+ TBaseProcess* ProcessTable[scmRTOS_PROCESS_COUNT+1];
+ volatile byte ISR_NestCount;
+
+ #if scmRTOS_CONTEXT_SWITCH_SCHEME == 1
+ byte SchedProcPriority;
+ #endif
+
+ #if scmRTOS_SYSTEM_TICKS_ENABLE == 1
+ volatile dword SysTickCount;
+ #endif
+
+ //-----------------------------------------------------------
+ //
+ // Functions
+ //
+ public:
+ INLINE TKernel()
+ : CurProcPriority(pr0)
+ , ReadyProcessMap( (1 << (scmRTOS_PROCESS_COUNT + 1)) - 1) // set all processes ready
+ , ISR_NestCount(0)
+ {
+ }
+
+ private:
+ INLINE inline void RegisterProcess(TBaseProcess* const p);
+
+ void Sched();
+ INLINE void Scheduler() { if(ISR_NestCount) return; else Sched(); }
+ INLINE inline void SchedISR();
+
+ #if scmRTOS_CONTEXT_SWITCH_SCHEME == 1
+ INLINE inline bool IsContextSwitchDone() const volatile;
+ #endif
+ INLINE void SetProcessReady (const byte pr) { TProcessMap PrioTag = GetPrioTag(pr); SetPrioTag( ReadyProcessMap, PrioTag); }
+ INLINE void SetProcessUnready(const byte pr) { TProcessMap PrioTag = GetPrioTag(pr); ClrPrioTag( ReadyProcessMap, PrioTag); }
+
+ public:
+ INLINE inline void SystemTimer();
+ #if scmRTOS_CONTEXT_SWITCH_SCHEME == 1
+ INLINE inline TStackItem* ContextSwitchHook(TStackItem* sp);
+ #endif
+
+ }; // End of TKernel class definition
+ //--------------------------------------------------------------------------
+ extern TKernel Kernel;
+
+ //--------------------------------------------------------------------------
+ //
+ /// BaseProcess
+ ///
+ /// Implements base class-type for application processes
+ //
+ // DESCRIPTION:
+ //
+ //
+ class TBaseProcess
+ {
+ friend class TKernel;
+ friend class TISRW;
+ friend class TISRW_SS;
+ friend class TEventFlag;
+ friend class TMutex;
+ friend class TBaseMessage;
+
+ template<typename T, word size, class S> friend class channel;
+ template<typename T> friend class message;
+
+
+ friend void Run();
+ friend void WakeUpProcess(TBaseProcess& p);
+ friend void ForceWakeUpProcess(TBaseProcess& p);
+ friend bool IsProcessSleeping(const TBaseProcess& p);
+ friend bool IsProcessSuspended(const TBaseProcess& p);
+
+ public:
+ #if SEPARATE_RETURN_STACK == 0
+ TBaseProcess( TStackItem* Stack, word stack_size, TPriority pr, void (*exec)() );
+ #else
+ TBaseProcess( TStackItem* Stack, TStackItem* RStack, TPriority pr, void (*exec)() );
+ #endif
+
+ static void Sleep(TTimeout timeout = 0);
+
+ protected:
+ TStackItem* StackPointer;
+ word StackSize;
+ TTimeout Timeout;
+ TPriority Priority;
+
+ };
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ //
+ /// process
+ ///
+ /// Implements template for application processes instantiation
+ //
+ // DESCRIPTION:
+ //
+ //
+ #if SEPARATE_RETURN_STACK == 0
+
+ template<TPriority pr, word stack_size>
+ class process : public TBaseProcess
+ {
+ public:
+ INLINE_PROCESS_CTOR process();
+#ifdef DEBUG_STACK
+word UsedStackSize();
+#endif //DEBUG_STACK
+ OS_PROCESS static void Exec();
+
+ private:
+ TStackItem Stack[stack_size/sizeof(TStackItem)];
+ };
+
+ template<TPriority pr, word stack_size>
+ OS::process<pr, stack_size>::process() : TBaseProcess( &Stack[stack_size/sizeof(TStackItem)]
+ , stack_size // debug
+ , pr
+ , reinterpret_cast<void (*)()>(Exec) )
+ {
+ }
+#ifdef DEBUG_STACK
+template<TPriority pr, word stack_size>
+word process<pr,stack_size>::UsedStackSize()
+{
+TStackItem* Idx = Stack;
+while(*(Idx++) == STACK_FILL_CONST);
+return ((Stack + (StackSize/sizeof(TStackItem))) - Idx)*sizeof(TStackItem);
+}
+#endif //DEBUG_STACK
+ #else
+
+ template<TPriority pr, word stack_size, word rstack_size>
+ class process : public TBaseProcess
+ {
+ public:
+ INLINE_PROCESS_CTOR process();
+
+ OS_PROCESS static void Exec();
+
+ private:
+ TStackItem Stack [stack_size/sizeof(TStackItem)];
+ TStackItem RStack[rstack_size/sizeof(TStackItem)];
+ };
+
+ template<TPriority pr, word stack_size, word rstack_size>
+ process<pr, stack_size, rstack_size>::process() : TBaseProcess( &Stack[stack_size/sizeof(TStackItem)]
+ , &RStack[rstack_size/sizeof(TStackItem)]
+ , pr
+ , reinterpret_cast<void (*)()>(Exec))
+ {
+ }
+
+ #endif
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ //
+ // Miscellaneous
+ //
+ //
+ INLINE inline void Run();
+ INLINE inline void LockSystemTimer() { TCritSect cs; LOCK_SYSTEM_TIMER(); }
+ INLINE inline void UnlockSystemTimer() { TCritSect cs; UNLOCK_SYSTEM_TIMER(); }
+ void WakeUpProcess(TBaseProcess& p);
+ void ForceWakeUpProcess(TBaseProcess& p);
+ INLINE inline void Sleep(TTimeout t = 0) { TBaseProcess::Sleep(t); }
+
+ INLINE inline bool IsProcessSleeping(const TBaseProcess& p)
+ {
+ TCritSect cs;
+ if(p.Timeout)
+ return true;
+ else
+ return false;
+ }
+
+ INLINE inline bool IsProcessSuspended(const TBaseProcess& p)
+ {
+ TCritSect cs;
+ if(Kernel.ReadyProcessMap & GetPrioTag(p.Priority))
+ return false;
+ else
+ return true;
+ }
+ //--------------------------------------------------------------------------
+
+#if scmRTOS_SYSTEM_TICKS_ENABLE == 1
+ INLINE inline dword GetTickCount() { TCritSect cs; return Kernel.SysTickCount; }
+#endif
+
+#if scmRTOS_SYSTIMER_HOOK_ENABLE == 1
+ INLINE_SYS_TIMER_HOOK void SystemTimerUserHook();
+#endif
+
+#if scmRTOS_CONTEXT_SWITCH_USER_HOOK_ENABLE == 1
+ INLINE_CONTEXT_SWITCH_HOOK void ContextSwitchUserHook();
+#endif
+
+}
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+//
+/// Register Process
+///
+/// Places pointer to process in kernel's process table
+//
+void OS::TKernel::RegisterProcess(OS::TBaseProcess* const p)
+{
+ ProcessTable[p->Priority] = p;
+}
+//------------------------------------------------------------------------------
+//
+/// System Timer Implementation
+///
+/// Performs process's timeouts checking and
+/// moving processes to ready-to-run state
+//
+void OS::TKernel::SystemTimer()
+{
+ SYS_TIMER_CRIT_SECT();
+#if scmRTOS_SYSTEM_TICKS_ENABLE == 1
+ SysTickCount++;
+#endif
+
+#if scmRTOS_PRIORITY_ORDER == 0
+ const byte BaseIndex = 0;
+#else
+ const byte BaseIndex = 1;
+#endif
+
+ for(byte i = BaseIndex; i < (scmRTOS_PROCESS_COUNT + BaseIndex); i++)
+ {
+ TBaseProcess* p = ProcessTable[i];
+
+ if(p->Timeout > 0)
+ {
+ if(--p->Timeout == 0)
+ {
+ SetProcessReady(p->Priority);
+ }
+ }
+ }
+}
+//------------------------------------------------------------------------------
+//
+/// ISR optimized scheduler
+///
+/// !!! IMPORTANT: This function must be call from ISR services only !!!
+//
+//
+#if scmRTOS_CONTEXT_SWITCH_SCHEME == 0
+void OS::TKernel::SchedISR()
+{
+ byte NextPrty = GetHighPriority(ReadyProcessMap);
+ if(NextPrty != CurProcPriority)
+ {
+ TStackItem* Next_SP = ProcessTable[NextPrty]->StackPointer;
+ TStackItem** Curr_SP_addr = &(ProcessTable[CurProcPriority]->StackPointer);
+ CurProcPriority = NextPrty;
+ OS_ContextSwitcher(Curr_SP_addr, Next_SP);
+ }
+}
+#else
+void OS::TKernel::SchedISR()
+{
+ byte NextPrty = GetHighPriority(ReadyProcessMap);
+ if(NextPrty != CurProcPriority)
+ {
+ SchedProcPriority = NextPrty;
+ RaiseContextSwitch();
+ }
+}
+//------------------------------------------------------------------------------
+bool OS::TKernel::IsContextSwitchDone() const volatile
+{
+ byte cur = CurProcPriority; ///< reading to temporary vars is performed due to
+ byte sched = SchedProcPriority; ///< suppress warning about order of volatile access
+ return cur == sched;
+}
+//------------------------------------------------------------------------------
+TStackItem* OS::TKernel::ContextSwitchHook(TStackItem* sp)
+{
+ ProcessTable[CurProcPriority]->StackPointer = sp;
+ sp = ProcessTable[SchedProcPriority]->StackPointer;
+
+#if scmRTOS_CONTEXT_SWITCH_USER_HOOK_ENABLE == 1
+ ContextSwitchUserHook();
+#endif
+
+ CurProcPriority = SchedProcPriority;
+ return sp;
+}
+//------------------------------------------------------------------------------
+#endif // scmRTOS_CONTEXT_SWITCH_SCHEME
+
+//-----------------------------------------------------------------------------
+/// Start Operation
+INLINE inline void OS::Run()
+{
+ TStackItem* sp = Kernel.ProcessTable[pr0]->StackPointer;
+ OS_Start(sp);
+}
+
+#include <OS_Services.h>
+
+#endif // OS_KERNEL_H
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/scmRTOS/Common/OS_Services.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,298 @@
+//******************************************************************************
+//*
+//* FULLNAME: Single-Chip Microcontroller Real-Time Operating System
+//*
+//* NICKNAME: scmRTOS
+//*
+//* PURPOSE: OS Services Source
+//*
+//* Version: 3.10
+//*
+//* $Revision: 256 $
+//* $Date:: 2010-01-22 #$
+//*
+//* Copyright (c) 2003-2010, Harry E. Zhurov
+//*
+//* Permission is hereby granted, free of charge, to any person
+//* obtaining a copy of this software and associated documentation
+//* files (the "Software"), to deal in the Software without restriction,
+//* including without limitation the rights to use, copy, modify, merge,
+//* publish, distribute, sublicense, and/or sell copies of the Software,
+//* and to permit persons to whom the Software is furnished to do so,
+//* subject to the following conditions:
+//*
+//* The above copyright notice and this permission notice shall be included
+//* in all copies or substantial portions of the Software.
+//*
+//* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+//* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+//* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+//* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+//* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+//* THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//*
+//* =================================================================
+//* See http://scmrtos.sourceforge.net for documentation, latest
+//* information, license and contact details.
+//* =================================================================
+//*
+//******************************************************************************
+
+#include "scmRTOS.h"
+
+using namespace OS;
+
+//------------------------------------------------------------------------------
+//
+//
+// TEventFlag
+//
+//
+bool OS::TEventFlag::Wait(TTimeout timeout)
+{
+ TCritSect cs;
+
+ if(Value) // if flag already signaled
+ {
+ Value = efOff; // clear flag
+ return true;
+ }
+ else
+ {
+ TBaseProcess* p = Kernel.ProcessTable[Kernel.CurProcPriority];
+ p->Timeout = timeout;
+ TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
+
+ SetPrioTag(ProcessMap, PrioTag); // put current process to the wait map
+ ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map
+
+ Kernel.Scheduler();
+
+ p->Timeout = 0;
+
+ if( !(ProcessMap & PrioTag) ) // if waked up by signal() or signal_ISR()
+ return true;
+
+ ClrPrioTag(ProcessMap, PrioTag); // otherwise waked up by timeout or by
+ return false; // OS::ForceWakeUpProcess(), remove process from the wait map
+ }
+}
+//------------------------------------------------------------------------------
+void OS::TEventFlag::Signal()
+{
+ TCritSect cs;
+ if(ProcessMap) // if any process waits for event
+ {
+ TProcessMap Timeouted = Kernel.ReadyProcessMap; // Process has its tag set in ReadyProcessMap if timeout expired
+ // or it was waked up by OS::ForceWakeUpProcess()
+
+ if( ProcessMap & ~Timeouted ) // if any process has to be waked up
+ {
+ SetPrioTag(Kernel.ReadyProcessMap, ProcessMap); // place all waiting processes to the ready map
+ ClrPrioTag(ProcessMap, ~Timeouted); // remove all non-timeouted processes from the waiting map.
+ // Used to check that process waked up by signal() or signalISR()
+ // and not by timeout or OS::ForceWakeUpProcess()
+ Kernel.Scheduler();
+ return;
+ }
+ }
+ Value = efOn;
+}
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+//
+//
+// TMutex
+//
+//
+void OS::TMutex::Lock()
+{
+ TCritSect cs;
+
+ TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
+ while(ValueTag)
+ {
+ SetPrioTag(ProcessMap, PrioTag); // mutex already locked by another process, put current process to the wait map
+ ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map
+
+ Kernel.Scheduler();
+ }
+ ValueTag = PrioTag; // mutex has been successfully locked
+}
+//------------------------------------------------------------------------------
+void OS::TMutex::Unlock()
+{
+ TCritSect cs;
+
+ TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
+ if(ValueTag != PrioTag) return; // the only process that had locked mutex can unlock the mutex
+ ValueTag = 0;
+ if(ProcessMap)
+ {
+ byte pr = GetHighPriority(ProcessMap);
+ PrioTag = GetPrioTag(pr);
+ ClrPrioTag(ProcessMap, PrioTag); // remove next ready process from the wait map
+ SetPrioTag(Kernel.ReadyProcessMap, PrioTag); // place next process to the ready map
+ Kernel.Scheduler();
+ }
+}
+//------------------------------------------------------------------------------
+void OS::TMutex::UnlockISR()
+{
+ TCritSect cs;
+
+ ValueTag = 0;
+ if(ProcessMap)
+ {
+ byte pr = GetHighPriority(ProcessMap);
+ TProcessMap PrioTag = GetPrioTag(pr);
+ ClrPrioTag(ProcessMap, PrioTag); // remove next ready process from the wait map
+ SetPrioTag(Kernel.ReadyProcessMap, PrioTag); // place next process to the ready map
+ }
+}
+//------------------------------------------------------------------------------
+
+
+//------------------------------------------------------------------------------
+//
+//
+// TChannel
+//
+//
+void OS::TChannel::CheckWaiters(TProcessMap& pm)
+{
+ if(pm)
+ {
+ byte pr = GetHighPriority(pm);
+ TProcessMap PrioTag = GetPrioTag(pr);
+ ClrPrioTag(pm, PrioTag); // remove next ready process from the wait map
+ SetPrioTag(Kernel.ReadyProcessMap, PrioTag); // place next process to the ready map
+ Kernel.Scheduler();
+ }
+}
+//------------------------------------------------------------------------------
+void OS::TChannel::Push(byte x)
+{
+ TCritSect cs;
+
+ while (!Cbuf.get_free_size())
+ {
+ TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
+ SetPrioTag (ProducersProcessMap, PrioTag); // channel is full, put current process to the wait map
+ ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map
+ Kernel.Scheduler(); // wait until waked-up by Pop() or Read()
+ }
+
+ Cbuf.put(x);
+ CheckWaiters(ConsumersProcessMap);
+}
+//------------------------------------------------------------------------------
+byte OS::TChannel::Pop()
+{
+ TCritSect cs;
+ byte x;
+
+ while(!Cbuf.get_count())
+ {
+ TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
+ SetPrioTag(ConsumersProcessMap, PrioTag); // channel is empty, put current process to the wait map
+ ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map
+ Kernel.Scheduler(); // wait until waked up by Push() or Write()
+ }
+ x = Cbuf.get();
+ CheckWaiters(ProducersProcessMap);
+ return x;
+}
+//------------------------------------------------------------------------------
+void OS::TChannel::Write(const byte* data, const byte count)
+{
+ TCritSect cs;
+
+ while(Cbuf.get_free_size() < count)
+ {
+ TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
+ SetPrioTag(ProducersProcessMap, PrioTag); // channel has not enough space, put current process to the wait map
+ ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map
+ Kernel.Scheduler(); // wait until waked up by Read() or Pop()
+ }
+
+ Cbuf.write(data, count);
+ CheckWaiters(ConsumersProcessMap);
+}
+//------------------------------------------------------------------------------
+void OS::TChannel::Read(byte* const data, const byte count)
+{
+ TCritSect cs;
+
+ while(Cbuf.get_count() < count)
+ {
+ TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
+ SetPrioTag(ConsumersProcessMap, PrioTag); // channel doesn't contain enough data, put current process to the wait map
+ ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map
+ Kernel.Scheduler(); // wait until waked up by Write() or Push()
+ }
+
+ Cbuf.read(data, count);
+ CheckWaiters(ProducersProcessMap);
+}
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+//
+// OS::message template
+//
+// Function-members implementation
+//
+//
+//------------------------------------------------------------------------------
+bool OS::TBaseMessage::wait(TTimeout timeout)
+{
+ TCritSect cs;
+
+ if(NonEmpty) // message alredy send
+ {
+ NonEmpty = false;
+ return true;
+ }
+ else
+ {
+ TBaseProcess* p = Kernel.ProcessTable[Kernel.CurProcPriority];
+ p->Timeout = timeout;
+ TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
+
+ SetPrioTag(ProcessMap, PrioTag); // put current process to the wait map
+ ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map
+ Kernel.Scheduler(); // wait until wake up
+
+ p->Timeout = 0;
+ if( !(ProcessMap & PrioTag) ) // if waked up by send() or sendISR()
+ return true;
+
+ ClrPrioTag(ProcessMap, PrioTag); // otherwise waked up by timeout or by
+ return false; // OS::ForceWakeUpProcess(), remove process from wait map
+ }
+}
+//------------------------------------------------------------------------------
+void OS::TBaseMessage::send()
+{
+ TCritSect cs;
+
+ if(ProcessMap)
+ {
+ TProcessMap Timeouted = Kernel.ReadyProcessMap; // Process has its tag set in ReadyProcessMap if timeout expired,
+ // or it was waked up by OS::ForceWakeUpProcess()
+ if( ProcessMap & ~Timeouted ) // if any process has to be waked up
+ {
+ SetPrioTag(Kernel.ReadyProcessMap, ProcessMap); // place all waiting processes to the ready map
+ ClrPrioTag(ProcessMap, ~Timeouted); // remove all non-timeouted processes from the waiting map.
+ Kernel.Scheduler();
+ return;
+ }
+ }
+
+ NonEmpty = true;
+}
+//------------------------------------------------------------------------------
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/scmRTOS/Common/OS_Services.h Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,467 @@
+//******************************************************************************
+//*
+//* FULLNAME: Single-Chip Microcontroller Real-Time Operating System
+//*
+//* NICKNAME: scmRTOS
+//*
+//* PURPOSE: OS Services Header. Declarations And Definitions
+//*
+//* Version: 3.10
+//*
+//* $Revision: 256 $
+//* $Date:: 2010-01-22 #$
+//*
+//* Copyright (c) 2003-2010, Harry E. Zhurov
+//*
+//* Permission is hereby granted, free of charge, to any person
+//* obtaining a copy of this software and associated documentation
+//* files (the "Software"), to deal in the Software without restriction,
+//* including without limitation the rights to use, copy, modify, merge,
+//* publish, distribute, sublicense, and/or sell copies of the Software,
+//* and to permit persons to whom the Software is furnished to do so,
+//* subject to the following conditions:
+//*
+//* The above copyright notice and this permission notice shall be included
+//* in all copies or substantial portions of the Software.
+//*
+//* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+//* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+//* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+//* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+//* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+//* THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//*
+//* =================================================================
+//* See http://scmrtos.sourceforge.net for documentation, latest
+//* information, license and contact details.
+//* =================================================================
+//*
+//******************************************************************************
+
+#ifndef OS_SERVICES_H
+#define OS_SERVICES_H
+
+namespace OS
+{
+ //--------------------------------------------------------------------------
+ //
+ // NAME : Mutex
+ //
+ /// Binary semaphore for support of mutual exclusion
+ //
+ // DESCRIPTION:
+ //
+ //
+ class TMutex
+ {
+ public:
+ INLINE TMutex() : ProcessMap(0), ValueTag(0) { }
+ void Lock();
+ void Unlock();
+ void UnlockISR();
+
+ INLINE bool LockSoftly() { TCritSect cs; if(ValueTag) return false; else Lock(); return true; }
+ INLINE bool IsLocked() const { TCritSect cs; if(ValueTag) return true; else return false; }
+
+ private:
+ TProcessMap ProcessMap;
+ TProcessMap ValueTag;
+
+ };
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ //
+ /// Event Flag
+ ///
+ /// Intended for processes synchronization and
+ /// event notification one (or more) process by another
+ //
+ // DESCRIPTION:
+ //
+ //
+ class TEventFlag
+ {
+ public:
+ enum TValue { efOn = 1, efOff= 0 }; // prefix 'ef' means: "Event Flag"
+
+ public:
+ INLINE TEventFlag(TValue init_val = efOff) : ProcessMap(0), Value(init_val) { }
+
+ bool Wait(TTimeout timeout = 0);
+ void Signal();
+ INLINE void Clear() { TCritSect cs; Value = efOff; }
+ INLINE inline void SignalISR();
+ INLINE bool IsSignaled() { TCritSect cs; if(Value == efOn) return true; else return false; }
+
+ private:
+ TProcessMap ProcessMap;
+ TValue Value;
+ };
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ //
+ /// TChannel
+ ///
+ /// Byte-wide data channel for transferring "raw" data
+ //
+ // DESCRIPTION:
+ //
+ //
+ class TChannel
+ {
+ public:
+ INLINE TChannel(byte* buf, byte size) : Cbuf(buf, size) { }
+ void Push(byte x);
+ byte Pop();
+ void Write(const byte* data, const byte count);
+ void Read(byte* const data, const byte count);
+
+ INLINE byte GetCount() const { TCritSect cs; return Cbuf.get_count(); }
+
+ private:
+ TProcessMap ProducersProcessMap;
+ TProcessMap ConsumersProcessMap;
+ usr::TCbuf Cbuf;
+
+ private:
+ void CheckWaiters(TProcessMap& pm);
+ };
+ //--------------------------------------------------------------------------
+
+
+ //--------------------------------------------------------------------------
+ //
+ // NAME : channel
+ //
+ // PURPOSE : Data channel for transferring data
+ // objects of arbitrary type
+ //
+ // DESCRIPTION:
+ //
+ //
+ template<typename T, word Size, typename S = byte>
+ /// channel
+ ///
+ /// Data channel for transferring data objects of arbitrary type
+ class channel
+ {
+ public:
+ INLINE channel() : ProducersProcessMap(0)
+ , ConsumersProcessMap(0)
+ , pool()
+ {
+ }
+
+ //----------------------------------------------------------------
+ //
+ // Data transfer functions
+ //
+ void write(const T* data, const S cnt);
+ bool read (T* const data, const S cnt, TTimeout timeout = 0);
+
+ void push (const T& item);
+ void push_front(const T& item);
+
+ bool pop (T& item, TTimeout timeout = 0);
+ bool pop_back(T& item, TTimeout timeout = 0);
+
+
+ //----------------------------------------------------------------
+ //
+ // Service functions
+ //
+ INLINE S get_count() const { TCritSect cs; return pool.get_count(); }
+ INLINE S get_free_size() const { TCritSect cs; return pool.get_free_size(); }
+ void flush();
+ //const T& operator[](const S index) { TCritSect cs; return pool[index]; }
+
+
+ private:
+ TProcessMap ProducersProcessMap;
+ TProcessMap ConsumersProcessMap;
+ usr::ring_buffer<T, Size, S> pool;
+
+ private:
+ void CheckWaiters(TProcessMap& pm);
+ };
+
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ //
+ /// message
+ ///
+ /// Template for messages
+ //
+ // DESCRIPTION:
+ //
+ //
+ class TBaseMessage
+ {
+ public:
+ INLINE TBaseMessage() : ProcessMap(0), NonEmpty(false) { }
+
+ bool wait (TTimeout timeout = 0);
+ void send();
+ INLINE inline void sendISR();
+ INLINE bool is_non_empty() const { TCritSect cs; return NonEmpty; }
+ INLINE void reset () { TCritSect cs; NonEmpty = false; }
+
+ private:
+ TProcessMap ProcessMap;
+ bool NonEmpty;
+ };
+ //--------------------------------------------------------------------------
+ template<typename T>
+ class message : public TBaseMessage
+ {
+ public:
+ INLINE message() : TBaseMessage() { }
+ INLINE const T& operator=(const T& msg) { TCritSect cs; Msg = msg; return Msg; }
+ INLINE operator T() const { TCritSect cs; return Msg; }
+
+ private:
+ T Msg;
+ };
+ //--------------------------------------------------------------------------
+}
+//------------------------------------------------------------------------------
+//
+// Function-members implementation
+//
+//------------------------------------------------------------------------------
+void OS::TEventFlag::SignalISR()
+{
+ TCritSect cs;
+ if(ProcessMap) // if any process waits for event
+ {
+ TProcessMap Timeouted = Kernel.ReadyProcessMap; // Process has its tag set in ReadyProcessMap if timeout
+ // expired, or it was waked up by OS::ForceWakeUpProcess()
+ if( ProcessMap & ~Timeouted ) // if any process has to be waked up
+ {
+ SetPrioTag(Kernel.ReadyProcessMap, ProcessMap); // place all waiting processes to the ready map
+ ClrPrioTag(ProcessMap, ~Timeouted); // remove all non-timeouted processes from the waiting map.
+ return;
+ }
+ }
+ Value = efOn;
+}
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+void OS::channel<T, Size, S>::CheckWaiters(TProcessMap& pm)
+{
+ if(pm)
+ {
+ TProcessMap Timeouted = Kernel.ReadyProcessMap;
+
+ SetPrioTag(Kernel.ReadyProcessMap, pm); // place all waiting processes to the ready map
+ ClrPrioTag(pm, ~Timeouted); // remove waiting processes from the wait map
+ Kernel.Scheduler();
+ }
+}
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+void OS::channel<T, Size, S>::push(const T& item)
+{
+ TCritSect cs;
+
+ while(!pool.get_free_size())
+ {
+ TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
+ SetPrioTag(ProducersProcessMap, PrioTag); // channel is full, put current process to the wait map
+ ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map
+ Kernel.Scheduler();
+ }
+
+ pool.push_back(item);
+ CheckWaiters(ConsumersProcessMap);
+}
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+void OS::channel<T, Size, S>::push_front(const T& item)
+{
+ TCritSect cs;
+
+ while(!pool.get_free_size())
+ {
+ TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
+ SetPrioTag(ProducersProcessMap, PrioTag); // channel is full, put current process to the wait map
+ ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map
+ Kernel.Scheduler();
+ }
+
+ pool.push_front(item);
+ CheckWaiters(ConsumersProcessMap);
+}
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+bool OS::channel<T, Size, S>::pop(T& item, TTimeout timeout)
+{
+ TCritSect cs;
+
+ if(pool.get_count())
+ {
+ item = pool.pop();
+ CheckWaiters(ProducersProcessMap);
+ return true;
+ }
+ else
+ {
+ TBaseProcess* p = Kernel.ProcessTable[Kernel.CurProcPriority];
+ p->Timeout = timeout;
+
+ TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
+ for(;;)
+ {
+ SetPrioTag(ConsumersProcessMap, PrioTag); // channel is empty, put current process to the wait map
+ ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map
+ Kernel.Scheduler();
+
+ if(pool.get_count())
+ {
+ p->Timeout = 0;
+ item = pool.pop();
+ CheckWaiters(ProducersProcessMap);
+ return true;
+ }
+
+ if(ConsumersProcessMap & PrioTag) // waked up by timer when timeout expired
+ { // or by OS::ForceWakeUpProcess()
+
+ p->Timeout = 0; // non-zero if waked up by ForceWakeUpProcess()
+ ClrPrioTag(ConsumersProcessMap, PrioTag); // remove current process from the wait map
+ return false;
+ }
+ }
+ }
+}
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+bool OS::channel<T, Size, S>::pop_back(T& item, TTimeout timeout)
+{
+ TCritSect cs;
+
+ if(pool.get_count())
+ {
+ item = pool.pop_back();
+ CheckWaiters(ProducersProcessMap);
+ return true;
+ }
+ else
+ {
+ TBaseProcess* p = Kernel.ProcessTable[Kernel.CurProcPriority];
+ p->Timeout = timeout;
+
+ TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
+ for(;;)
+ {
+ SetPrioTag(ConsumersProcessMap, PrioTag); // channel is empty, put current process to the wait map
+ ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map
+ Kernel.Scheduler();
+
+ if(pool.get_count())
+ {
+ p->Timeout = 0;
+ item = pool.pop_back();
+ CheckWaiters(ProducersProcessMap);
+ return true;
+ }
+
+ if(ConsumersProcessMap & PrioTag) // waked up by timer when timeout expired
+ { // or by OS::ForceWakeUpProcess()
+
+ p->Timeout = 0; // non-zero if waked up by ForceWakeUpProcess()
+ ClrPrioTag(ConsumersProcessMap, PrioTag); // remove current process from the wait map
+ return false;
+ }
+ }
+ }
+}
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+void OS::channel<T, Size, S>::flush()
+{
+ TCritSect cs;
+ pool.flush();
+ CheckWaiters(ProducersProcessMap);
+}
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+void OS::channel<T, Size, S>::write(const T* data, const S count)
+{
+ TCritSect cs;
+
+ while(pool.get_free_size() < count)
+ {
+ TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
+ SetPrioTag(ProducersProcessMap, PrioTag); // channel does not have enough space, put current process to the wait map
+ ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map
+ Kernel.Scheduler();
+ }
+
+ pool.write(data, count);
+ CheckWaiters(ConsumersProcessMap);
+}
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+bool OS::channel<T, Size, S>::read(T* const data, const S count, TTimeout timeout)
+{
+ TCritSect cs;
+
+ TBaseProcess* p = Kernel.ProcessTable[Kernel.CurProcPriority];
+ p->Timeout = timeout;
+
+ TProcessMap PrioTag = GetPrioTag(Kernel.CurProcPriority);
+ while(pool.get_count() < count)
+ {
+ SetPrioTag(ConsumersProcessMap, PrioTag); // channel doesn't contain enough data, put current process to the wait map
+ ClrPrioTag(Kernel.ReadyProcessMap, PrioTag); // remove current process from the ready map
+ Kernel.Scheduler();
+
+ if(ConsumersProcessMap & PrioTag) // waked up by timer when timeout expired
+ { // or by OS::ForceWakeUpProcess()
+
+ p->Timeout = 0; // non-zero if waked up by ForceWakeUpProcess()
+ ClrPrioTag(ConsumersProcessMap, PrioTag); // remove current process from the wait map
+ return false;
+ }
+ }
+
+ p->Timeout = 0;
+ pool.read(data, count);
+ CheckWaiters(ProducersProcessMap);
+
+ return true;
+}
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+//
+// OS::message template
+//
+// Function-members implementation
+//
+//
+//------------------------------------------------------------------------------
+void OS::TBaseMessage::sendISR()
+{
+ TCritSect cs;
+
+ if(ProcessMap)
+ {
+ TProcessMap Timeouted = OS::Kernel.ReadyProcessMap; // Process has it's tag set in ReadyProcessMap if timeout
+ // expired, or it was waked up by OS::ForceWakeUpProcess()
+ if( ProcessMap & ~Timeouted ) // if any process has to be waked up
+ {
+ SetPrioTag(Kernel.ReadyProcessMap, ProcessMap); // place all waiting processes to the ready map
+ ClrPrioTag(ProcessMap, ~Timeouted); // remove all non-timeouted processes from the waiting map.
+ return;
+ }
+ }
+ NonEmpty = true;
+}
+//------------------------------------------------------------------------------
+#endif // OS_SERVICES_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scmRTOS/Common/scmRTOS.h Mon Feb 13 02:11:20 2012 +0000 @@ -0,0 +1,58 @@ +//****************************************************************************** +//* +//* FULLNAME: Single-Chip Microcontroller Real-Time Operating System +//* +//* NICKNAME: scmRTOS +//* +//* PURPOSE: Main RTOS header file +//* +//* Version: 3.10 +//* +//* $Revision: 256 $ +//* $Date:: 2010-01-22 #$ +//* +//* Copyright (c) 2003-2010, Harry E. Zhurov +//* +//* Permission is hereby granted, free of charge, to any person +//* obtaining a copy of this software and associated documentation +//* files (the "Software"), to deal in the Software without restriction, +//* including without limitation the rights to use, copy, modify, merge, +//* publish, distribute, sublicense, and/or sell copies of the Software, +//* and to permit persons to whom the Software is furnished to do so, +//* subject to the following conditions: +//* +//* The above copyright notice and this permission notice shall be included +//* in all copies or substantial portions of the Software. +//* +//* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +//* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +//* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +//* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +//* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +//* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +//* THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +//* +//* ================================================================= +//* See http://scmrtos.sourceforge.net for documentation, latest +//* information, license and contact details. +//* ================================================================= +//* +//****************************************************************************** + +#ifndef scmRTOS_H +#define scmRTOS_H + +//----------------------------------------------------------------------------- +// +// !!! The order of includes is important !!! +// +#include <stddef.h> +#include <commdefs.h> +#include <usrlib.h> +#include <OS_Target.h> + +//------------------------------------------------------------------------------ + + +#endif // scmRTOS_H +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/scmRTOS/Common/scmRTOS_defs.h Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,374 @@
+//******************************************************************************
+//*
+//* FULLNAME: Single-Chip Microcontroller Real-Time Operating System
+//*
+//* NICKNAME: scmRTOS
+//*
+//* PURPOSE: Macros And Common Definitions
+//*
+//* Version: 3.10
+//*
+//* $Revision: 256 $
+//* $Date:: 2010-01-22 #$
+//*
+//* Copyright (c) 2003-2010, Harry E. Zhurov
+//*
+//* Permission is hereby granted, free of charge, to any person
+//* obtaining a copy of this software and associated documentation
+//* files (the "Software"), to deal in the Software without restriction,
+//* including without limitation the rights to use, copy, modify, merge,
+//* publish, distribute, sublicense, and/or sell copies of the Software,
+//* and to permit persons to whom the Software is furnished to do so,
+//* subject to the following conditions:
+//*
+//* The above copyright notice and this permission notice shall be included
+//* in all copies or substantial portions of the Software.
+//*
+//* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+//* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+//* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+//* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+//* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+//* THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//*
+//* =================================================================
+//* See http://scmrtos.sourceforge.net for documentation, latest
+//* information, license and contact details.
+//* =================================================================
+//*
+//******************************************************************************
+
+#ifndef scmRTOS_DEFS_H
+#define scmRTOS_DEFS_H
+
+#include <commdefs.h>
+
+//------------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+//
+//
+/// Macro for Channel Type definition
+//
+//
+#define DefineChannel(Name, Capacity) \
+class Name : public OS::TChannel \
+{ \
+public: \
+ Name() : OS::TChannel(buf, sizeof(buf)) { } \
+ \
+private: \
+ byte buf[Capacity]; \
+ \
+}
+//-----------------------------------------------------------------------------
+//
+// Check CONFIG Macro Definitions
+//
+//
+
+//----------------- scmRTOS_SYSTIMER_NEST_INTS_ENABLE -------------------------
+#ifndef scmRTOS_SYSTIMER_NEST_INTS_ENABLE
+#error "Error: Config macro scmRTOS_SYSTIMER_NEST_INTS_ENABLE must be defined!"
+#endif
+
+#if (scmRTOS_SYSTIMER_NEST_INTS_ENABLE < 0) || (scmRTOS_SYSTIMER_NEST_INTS_ENABLE > 1)
+#error "Error: scmRTOS_SYSTIMER_NEST_INTS_ENABLE must have values 0 or 1 only!"
+#endif
+
+//----------------- scmRTOS_SYSTEM_TICKS_ENABLE -------------------------------
+#ifndef scmRTOS_SYSTEM_TICKS_ENABLE
+#error "Error: Config macro scmRTOS_SYSTEM_TICKS_ENABLE must be defined!"
+#endif
+
+#if (scmRTOS_SYSTEM_TICKS_ENABLE < 0) || (scmRTOS_SYSTEM_TICKS_ENABLE > 1)
+#error "Error: scmRTOS_SYSTEM_TICKS_ENABLE must have values 0 or 1 only!"
+#endif
+
+
+//----------------- scmRTOS_SYSTIMER_HOOK_ENABLE ------------------------------
+#ifndef scmRTOS_SYSTIMER_HOOK_ENABLE
+#error "Error: Config macro scmRTOS_SYSTIMER_HOOK_ENABLE must be defined!"
+#endif
+
+#if (scmRTOS_SYSTIMER_HOOK_ENABLE < 0) || (scmRTOS_SYSTIMER_HOOK_ENABLE > 1)
+#error "Error: scmRTOS_SYSTIMER_HOOK_ENABLE must have values 0 or 1 only!"
+#endif
+
+//-------------- scmRTOS_CONTEXT_SWITCH_USER_HOOK_ENABLE ----------------------
+#ifndef scmRTOS_CONTEXT_SWITCH_USER_HOOK_ENABLE
+#error "Error: Config macro scmRTOS_CONTEXT_SWITCH_USER_HOOK_ENABLE must be defined!"
+#endif
+
+#if (scmRTOS_CONTEXT_SWITCH_USER_HOOK_ENABLE < 0) || (scmRTOS_CONTEXT_SWITCH_USER_HOOK_ENABLE > 1)
+#error "Error: scmRTOS_CONTEXT_SWITCH_USER_HOOK_ENABLE must have values 0 or 1 only!"
+#endif
+
+//----------------- scmRTOS_IDLE_HOOK_ENABLE ----------------------------------
+#ifndef scmRTOS_IDLE_HOOK_ENABLE
+#error "Error: Config macro scmRTOS_IDLE_HOOK_ENABLE must be defined!"
+#endif
+
+#if (scmRTOS_IDLE_HOOK_ENABLE < 0) || (scmRTOS_IDLE_HOOK_ENABLE > 1)
+#error "Error: scmRTOS_IDLE_HOOK_ENABLE must have values 0 or 1 only!"
+#endif
+
+//----------------- scmRTOS_CONTEXT_SWITCH_SCHEME -----------------------------
+#ifndef scmRTOS_CONTEXT_SWITCH_SCHEME
+#error "Error: Config macro scmRTOS_CONTEXT_SWITCH_SCHEME must be defined!"
+#endif
+
+#if (scmRTOS_CONTEXT_SWITCH_SCHEME < 0) || (scmRTOS_CONTEXT_SWITCH_SCHEME > 1)
+#error "Error: scmRTOS_CONTEXT_SWITCH_SCHEME must have values 0 or 1 only!"
+#endif
+
+
+//----------------- scmRTOS_PRIORITY_ORDER ------------------------------------
+#ifndef scmRTOS_PRIORITY_ORDER
+#error "Error: Config macro scmRTOS_PRIORITY_ORDER must be defined!"
+#endif
+
+#if (scmRTOS_PRIORITY_ORDER < 0) || (scmRTOS_PRIORITY_ORDER > 1)
+#error "Error: scmRTOS_PRIORITY_ORDER must have values 0 or 1 only!"
+#endif
+
+//----------------- User Hooks inlining ------------------------------------
+#ifndef INLINE_SYS_TIMER_HOOK
+#define INLINE_SYS_TIMER_HOOK
+#endif
+
+#ifndef INLINE_CONTEXT_SWITCH_HOOK
+#define INLINE_CONTEXT_SWITCH_HOOK
+#endif
+
+
+//-----------------------------------------------------------------------------
+//
+/// Priority and process map type definitions
+//
+//
+namespace OS
+{
+ #if scmRTOS_PROCESS_COUNT < 8
+ typedef byte TProcessMap;
+ #elif scmRTOS_PROCESS_COUNT < 16
+ typedef word TProcessMap;
+ #else
+ typedef dword TProcessMap;
+ #endif
+ //------------------------------------------------------
+#if scmRTOS_PRIORITY_ORDER == 0
+ enum TPriority {
+ #if scmRTOS_PROCESS_COUNT > 0
+ pr0,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 1
+ pr1,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 2
+ pr2,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 3
+ pr3,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 4
+ pr4,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 5
+ pr5,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 6
+ pr6,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 7
+ pr7,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 8
+ pr8,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 9
+ pr9,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 10
+ pr10,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 11
+ pr11,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 12
+ pr12,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 13
+ pr13,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 14
+ pr14,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 15
+ pr15,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 16
+ pr16,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 17
+ pr17,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 18
+ pr18,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 19
+ pr19,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 20
+ pr20,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 21
+ pr21,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 22
+ pr22,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 23
+ pr23,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 24
+ pr24,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 25
+ pr25,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 26
+ pr26,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 27
+ pr27,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 28
+ pr28,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 29
+ pr29,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 30
+ pr30,
+ #endif
+ #if (scmRTOS_PROCESS_COUNT > 31) || (scmRTOS_PROCESS_COUNT < 1)
+ #error "Invalid Process Count specification! Must be from 1 to 31."
+ #endif
+ prIDLE
+ };
+#else // scmRTOS_PRIORITY_ORDER == 1
+ enum TPriority {
+ prIDLE,
+ #if scmRTOS_PROCESS_COUNT > 30
+ pr30,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 29
+ pr29,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 28
+ pr28,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 27
+ pr27,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 26
+ pr26,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 25
+ pr25,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 24
+ pr24,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 23
+ pr23,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 22
+ pr22,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 21
+ pr21,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 20
+ pr20,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 19
+ pr19,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 18
+ pr18,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 17
+ pr17,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 16
+ pr16,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 15
+ pr15,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 14
+ pr14,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 13
+ pr13,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 12
+ pr12,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 11
+ pr11,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 10
+ pr10,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 9
+ pr9,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 8
+ pr8,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 7
+ pr7,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 6
+ pr6,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 5
+ pr5,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 4
+ pr4,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 3
+ pr3,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 2
+ pr2,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 1
+ pr1,
+ #endif
+ #if scmRTOS_PROCESS_COUNT > 0
+ pr0
+ #endif
+ #if (scmRTOS_PROCESS_COUNT > 31) || (scmRTOS_PROCESS_COUNT < 1)
+ #error "Invalid Process Count specification! Must be from 1 to 31."
+ #endif
+ };
+#endif //scmRTOS_PRIORITY_ORDER
+}
+//-----------------------------------------------------------------------------
+//
+// Process's constructor inlining control: default behaviour
+//
+#ifndef INLINE_PROCESS_CTOR
+#define INLINE_PROCESS_CTOR
+#endif
+
+
+//-----------------------------------------------------------------------------
+
+#endif // scmRTOS_DEFS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/scmRTOS/Common/usrlib.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,140 @@
+//******************************************************************************
+//*
+//* FULLNAME: Single-Chip Microcontroller Real-Time Operating System
+//*
+//* NICKNAME: scmRTOS
+//*
+//* PURPOSE: User Suport Library Source
+//*
+//* Version: 3.10
+//*
+//* $Revision: 256 $
+//* $Date:: 2010-01-22 #$
+//*
+//* Copyright (c) 2003-2010, Harry E. Zhurov
+//*
+//* Permission is hereby granted, free of charge, to any person
+//* obtaining a copy of this software and associated documentation
+//* files (the "Software"), to deal in the Software without restriction,
+//* including without limitation the rights to use, copy, modify, merge,
+//* publish, distribute, sublicense, and/or sell copies of the Software,
+//* and to permit persons to whom the Software is furnished to do so,
+//* subject to the following conditions:
+//*
+//* The above copyright notice and this permission notice shall be included
+//* in all copies or substantial portions of the Software.
+//*
+//* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+//* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+//* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+//* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+//* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+//* THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//*
+//* =================================================================
+//* See http://scmrtos.sourceforge.net for documentation, latest
+//* information, license and contact details.
+//* =================================================================
+//*
+//******************************************************************************
+
+#include <usrlib.h>
+#include <commdefs.h>
+
+using namespace usr;
+
+//------------------------------------------------------------------------------
+//
+/// Circular buffer function-member description
+//
+//
+//
+TCbuf::TCbuf(byte* const Address, const byte Size) :
+ buf(Address),
+ size(Size),
+ count(0),
+ first(0),
+ last(0)
+{
+}
+//------------------------------------------------------------------------------
+bool TCbuf::write(const byte* data, const byte Count)
+{
+ if( Count > (size - count) )
+ return false;
+
+ for(byte i = 0; i < Count; i++)
+ push(*(data++));
+
+ return true;
+}
+//------------------------------------------------------------------------------
+void TCbuf::read(byte* data, const byte Count)
+{
+ byte N = Count <= count ? Count : count;
+
+ for(byte i = 0; i < N; i++)
+ data[i] = pop();
+}
+//------------------------------------------------------------------------------
+byte TCbuf::get_byte(const byte index) const
+{
+ byte x = first + index;
+
+ if(x < size)
+ return buf[x];
+ else
+ return buf[x - size];
+}
+
+//------------------------------------------------------------------------------
+bool TCbuf::put(const byte item)
+{
+ if(count == size)
+ return false;
+
+ push(item);
+ return true;
+}
+//------------------------------------------------------------------------------
+byte TCbuf::get()
+{
+ if(count)
+ return pop();
+ else
+ return 0;
+}
+//------------------------------------------------------------------------------
+//
+/// \note
+/// For internal purposes.
+/// Use this function with care - it doesn't perform free size check.
+//
+void TCbuf::push(const byte item)
+{
+ buf[last] = item;
+ last++;
+ count++;
+
+ if(last == size)
+ last = 0;
+}
+//------------------------------------------------------------------------------
+//
+/// \note
+/// For internal purposes.
+/// Use this function with care - it doesn't perform free size check.
+//
+byte TCbuf::pop()
+{
+ byte item = buf[first];
+
+ count--;
+ first++;
+ if(first == size)
+ first = 0;
+
+ return item;
+}
+//------------------------------------------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/scmRTOS/Common/usrlib.h Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,298 @@
+//******************************************************************************
+//*
+//* FULLNAME: Single-Chip Microcontroller Real-Time Operating System
+//*
+//* NICKNAME: scmRTOS
+//*
+//* PURPOSE: User Suport Library Header
+//*
+//* Version: 3.10
+//*
+//* $Revision: 256 $
+//* $Date:: 2010-01-22 #$
+//*
+//* Copyright (c) 2003-2010, Harry E. Zhurov
+//*
+//* Permission is hereby granted, free of charge, to any person
+//* obtaining a copy of this software and associated documentation
+//* files (the "Software"), to deal in the Software without restriction,
+//* including without limitation the rights to use, copy, modify, merge,
+//* publish, distribute, sublicense, and/or sell copies of the Software,
+//* and to permit persons to whom the Software is furnished to do so,
+//* subject to the following conditions:
+//*
+//* The above copyright notice and this permission notice shall be included
+//* in all copies or substantial portions of the Software.
+//*
+//* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+//* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+//* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+//* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+//* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+//* THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//*
+//* =================================================================
+//* See http://scmrtos.sourceforge.net for documentation, latest
+//* information, license and contact details.
+//* =================================================================
+//*
+//******************************************************************************
+
+#ifndef USRLIB_H
+#define USRLIB_H
+
+#include <commdefs.h>
+
+//------------------------------------------------------------------------------
+//
+// DESCRIPTON: user namespace for some useful types and functions
+//
+//
+namespace usr
+{
+ //------------------------------------------------------------------------------
+ //
+ /// The Circular Buffer
+ //
+ /// Byte-wide FIFO.
+ //
+ /// Allows to:
+ /// add byte,
+ /// get byte,
+ /// write bytes from array,
+ /// read bytes to array,
+ /// and some other service actions.
+ //
+ class TCbuf
+ {
+ public:
+ TCbuf(byte* const Address, const byte Size);
+ bool write(const byte* data, const byte Count);
+ void read(byte* const data, const byte Count);
+ byte get_count() const { return count; }
+ byte get_free_size() const { return size - count; }
+ byte get_byte(const byte index) const;
+ void clear() { count = 0; last = first; }
+ bool put(const byte item);
+ byte get();
+
+ private:
+ //------------------------------------------------------------------------------
+ //
+ // DESCRIPTON: For internal purposes
+ //
+ void push(const byte item); ///< Use this function with care - it doesn't perform free size check
+ byte pop(); ///< Use this function with care - it doesn't perform count check
+ //------------------------------------------------------------------------------
+
+ private:
+ byte* buf;
+ byte size;
+ volatile byte count;
+ byte first;
+ byte last;
+ };
+ //------------------------------------------------------------------------------
+
+
+
+ //-----------------------------------------------------------------------
+ //
+ /// The Ring Buffer Template
+ ///
+ /// Carries out FIFO functionality for
+ /// arbitrary data types
+ ///
+ /// Allows to:
+ /// add item to back (default),
+ /// add item to front,
+ /// get item at front (default),
+ /// get item from back,
+ /// write items from array,
+ /// read items to array and some other actions
+ //
+ //
+ //
+ template<typename T, word Size, typename S = byte>
+ class ring_buffer
+ {
+ public:
+ ring_buffer() : Count(0), First(0), Last(0) { }
+
+ //----------------------------------------------------------------
+ //
+ // Data transfer functions
+ //
+ bool write(const T* data, const S cnt);
+ void read(T* const data, const S cnt);
+
+ bool push_back(const T item);
+ bool push_front(const T item);
+
+ T pop_front();
+ T pop_back();
+
+ bool push(const T item) { return push_back(item); }
+ T pop() { return pop_front(); }
+
+ //----------------------------------------------------------------
+ //
+ // Service functions
+ //
+ S get_count() const { return Count; }
+ S get_free_size() const { return Size - Count; }
+ T& operator[](const S index);
+ void flush() { Count = 0; Last = First; }
+
+ private:
+ //--------------------------------------------------------------
+ // DESCRIPTON: For internal purposes
+ // Use this functions with care: it don't perform
+ // free size and count check
+ //
+ void push_item(const T item);
+ void push_item_front(const T item);
+ T pop_item();
+ T pop_item_back();
+ //--------------------------------------------------------------
+
+ private:
+ S Count;
+ S First;
+ S Last;
+ T Buf[Size];
+ };
+ //------------------------------------------------------------------
+}
+//---------------------------------------------------------------------------
+
+
+//------------------------------------------------------------------------------
+//
+// The ring buffer function-member definitions
+//
+//
+//
+template<typename T, word Size, typename S>
+bool usr::ring_buffer<T, Size, S>::write(const T* data, const S cnt)
+{
+ if( cnt > (Size - Count) )
+ return false;
+
+ for(S i = 0; i < cnt; i++)
+ push_item(*(data++));
+
+ return true;
+}
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+void usr::ring_buffer<T, Size, S>::read(T* data, const S cnt)
+{
+ S nItems = cnt <= Count ? cnt : Count;
+
+ for(S i = 0; i < nItems; i++)
+ data[i] = pop_item();
+}
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+T& usr::ring_buffer<T, Size, S>::operator[](const S index)
+{
+ S x = First + index;
+
+ if(x < Size)
+ return Buf[x];
+ else
+ return Buf[x - Size];
+}
+
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+bool usr::ring_buffer<T, Size, S>::push_back(const T item)
+{
+ if(Count == Size)
+ return false;
+
+ push_item(item);
+ return true;
+}
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+bool usr::ring_buffer<T, Size, S>::push_front(const T item)
+{
+ if(Count == Size)
+ return false;
+
+ push_item_front(item);
+ return true;
+}
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+T usr::ring_buffer<T, Size, S>::pop_front()
+{
+ if(Count)
+ return pop_item();
+ else
+ return Buf[First];
+}
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+T usr::ring_buffer<T, Size, S>::pop_back()
+{
+ if(Count)
+ return pop_item_back();
+ else
+ return Buf[First];
+}
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+void usr::ring_buffer<T, Size, S>::push_item(const T item)
+{
+ Buf[Last] = item;
+ Last++;
+ Count++;
+
+ if(Last == Size)
+ Last = 0;
+}
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+void usr::ring_buffer<T, Size, S>::push_item_front(const T item)
+{
+ if(First == 0)
+ First = Size - 1;
+ else
+ --First;
+ Buf[First] = item;
+ Count++;
+}
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+T usr::ring_buffer<T, Size, S>::pop_item()
+{
+ T item = Buf[First];
+
+ Count--;
+ First++;
+ if(First == Size)
+ First = 0;
+
+ return item;
+}
+//------------------------------------------------------------------------------
+template<typename T, word Size, typename S>
+T usr::ring_buffer<T, Size, S>::pop_item_back()
+{
+
+ if(Last == 0)
+ Last = Size - 1;
+ else
+ --Last;
+
+ Count--;
+ return Buf[Last];;
+}
+//------------------------------------------------------------------------------
+
+
+#endif // USRLIB_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/scmRTOS/CortexM3/OS_Target.h Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,236 @@
+//******************************************************************************
+//*
+//* FULLNAME: Single-Chip Microcontroller Real-Time Operating System
+//*
+//* NICKNAME: scmRTOS
+//*
+//* PROCESSOR: ARM Cortex-M3
+//*
+//* TOOLKIT: RVCT (ARM)
+//*
+//* PURPOSE: Target Dependent Stuff Header. Declarations And Definitions
+//*
+//* Version: 3.10
+//*
+//* $Revision: 195 $
+//* $Date:: 2008-06-19 #$
+//*
+//* Copyright (c) 2003-2010, Harry E. Zhurov
+//*
+//* Permission is hereby granted, free of charge, to any person
+//* obtaining a copy of this software and associated documentation
+//* files (the "Software"), to deal in the Software without restriction,
+//* including without limitation the rights to use, copy, modify, merge,
+//* publish, distribute, sublicense, and/or sell copies of the Software,
+//* and to permit persons to whom the Software is furnished to do so,
+//* subject to the following conditions:
+//*
+//* The above copyright notice and this permission notice shall be included
+//* in all copies or substantial portions of the Software.
+//*
+//* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+//* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+//* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+//* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+//* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+//* THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//*
+//* =================================================================
+//* See http://scmrtos.sourceforge.net for documentation, latest
+//* information, license and contact details.
+//* =================================================================
+//*
+//******************************************************************************
+//* Ported by Andrey Chuikin, Copyright (c) 2008-2010
+
+#ifndef scmRTOS_CORTEXM3_H
+#define scmRTOS_CORTEXM3_H
+
+#include <commdefs.h>
+
+//------------------------------------------------------------------------------
+//
+// Compiler and Target checks
+//
+//
+#ifndef __ARMCC_VERSION
+#error "This file should only be compiled with ARM RVCT Compiler"
+#endif // __ARMCC_VERSION
+
+#if __TARGET_ARCH_ARM != 0 || __TARGET_ARCH_THUMB != 4
+#error "This file must be compiled for ARMv7-M (Cortex-M3) processor only."
+#endif
+
+//------------------------------------------------------------------------------
+//
+// Target specific types
+//
+//
+typedef dword TStackItem;
+typedef dword TStatusReg;
+
+//-----------------------------------------------------------------------------
+//
+// Configuration macros
+//
+//
+#define OS_PROCESS __attribute__((__noreturn__))
+#define OS_INTERRUPT
+#define DUMMY_INSTR() __NOP()
+#define INLINE_PROCESS_CTOR INLINE inline
+
+//-----------------------------------------------------------------------------
+//
+// Uncomment macro value below for SystemTimer() run in critical section
+//
+// This is useful (and necessary) when target processor has hardware
+// enabled nested interrups. Cortex-M3 have such interrupts.
+//
+#define SYS_TIMER_CRIT_SECT() TCritSect cs
+
+//-----------------------------------------------------------------------------
+// Separate return stack not required
+#define SEPARATE_RETURN_STACK 0
+
+//-----------------------------------------------------------------------------
+// Software interrupt stack switching not supported in Cortex-M3 port
+// because processor implements hardware stack switching.
+// So, system timer isr wrapper can't be choosen at project level
+//
+#define scmRTOS_ISRW_TYPE TISRW
+
+//-----------------------------------------------------------------------------
+//
+// scmRTOS Context Switch Scheme
+//
+// The macro defines a context switch manner. Value 0 sets direct context
+// switch in the scheduler and in the OS ISRs. This is the primary method.
+// Value 1 sets the second way to switch context - by using of software
+// interrupt. See documentation fo details.
+// Cortex-M3 port supports software interrupt switch method only.
+//
+#define scmRTOS_CONTEXT_SWITCH_SCHEME 1
+
+//-----------------------------------------------------------------------------
+//
+// Include project-level configurations
+// !!! The order of includes is important !!!
+//
+#include "../../scmRTOS_config.h"
+#include "../scmRTOS_TARGET_CFG.h"
+#include <scmRTOS_defs.h>
+#include <LPC17xx.h>
+
+//-----------------------------------------------------------------------------
+//
+// The Critital Section Wrapper
+//
+//
+#define __enable_interrupt() __enable_irq()
+#define __disable_interrupt() __disable_irq()
+
+#define __set_interrupt_state(status) __set_PRIMASK(status)
+#define __get_interrupt_state() __get_PRIMASK()
+
+class TCritSect
+{
+public:
+ TCritSect () : StatusReg(__get_interrupt_state()) { __disable_interrupt(); }
+ ~TCritSect() { __set_interrupt_state(StatusReg); }
+
+private:
+ TStatusReg StatusReg;
+};
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+//
+// Priority stuff
+//
+//
+namespace OS
+{
+INLINE inline OS::TProcessMap GetPrioTag(const byte pr) { return static_cast<OS::TProcessMap> (1 << pr); }
+
+#if scmRTOS_PRIORITY_ORDER == 0
+ INLINE inline byte GetHighPriority(TProcessMap pm)
+ {
+ byte pr = 0;
+
+ while( !(pm & 0x0001) )
+ {
+ pr++;
+ pm >>= 1;
+ }
+ return pr;
+ }
+#else
+ INLINE inline byte GetHighPriority(TProcessMap pm) { return (31 - __clz(pm)); }
+#endif // scmRTOS_PRIORITY_ORDER
+}
+
+//-----------------------------------------------------------------------------
+//
+// Interrupt and Interrupt Service Routines support
+//
+INLINE inline TStatusReg GetInterruptState( ) { return __get_interrupt_state(); }
+INLINE inline void SetInterruptState(TStatusReg sr) { __set_interrupt_state(sr); }
+
+INLINE inline void EnableInterrupts() { __enable_interrupt(); }
+INLINE inline void DisableInterrupts() { __disable_interrupt(); }
+
+
+namespace OS
+{
+ INLINE inline void EnableContextSwitch() { EnableInterrupts(); }
+ INLINE inline void DisableContextSwitch() { DisableInterrupts(); }
+}
+
+#include <OS_Kernel.h>
+
+namespace OS
+{
+ //--------------------------------------------------------------------------
+ //
+ // NAME : OS ISR support
+ //
+ // PURPOSE : Implements common actions on interrupt enter and exit
+ // under the OS
+ //
+ // DESCRIPTION:
+ //
+ //
+ class TISRW
+ {
+ public:
+ INLINE TISRW() { ISR_Enter(); }
+ INLINE ~TISRW() { ISR_Exit(); }
+
+ private:
+ //-----------------------------------------------------
+ INLINE void ISR_Enter()
+ {
+ TCritSect cs;
+ Kernel.ISR_NestCount++;
+ }
+ //-----------------------------------------------------
+ INLINE void ISR_Exit()
+ {
+ TCritSect cs;
+ if(--Kernel.ISR_NestCount) return;
+ Kernel.SchedISR();
+ }
+ //-----------------------------------------------------
+ };
+
+ // No software interrupt stack switching provided,
+ // TISRW_SS declared to be the same as TISRW for porting compability
+ #define TISRW_SS TISRW
+
+} // ns OS
+//-----------------------------------------------------------------------------
+
+#endif // scmRTOS_CORTEXM3_H
+//-----------------------------------------------------------------------------
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/scmRTOS/CortexM3/OS_Target_asm.s Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,178 @@
+; ******************************************************************************
+; *
+; * FULLNAME: Single-Chip Microcontroller Real-Time Operating System
+; *
+; * NICKNAME: scmRTOS
+; *
+; * PROCESSOR: ARM Cortex-M3
+; *
+; * TOOLKIT: EWARM (IAR Systems)
+; *
+; * PURPOSE: Target Dependent Low-Level Stuff
+; *
+; * Version: 3.10
+; *
+; * $Revision: 195 $
+; * $Date:: 2008-06-19 #$
+; *
+; * Copyright (c) 2003-2010, Harry E. Zhurov
+; *
+; * Permission is hereby granted, free of charge, to any person
+; * obtaining a copy of this software and associated documentation
+; * files (the "Software"), to deal in the Software without restriction,
+; * including without limitation the rights to use, copy, modify, merge,
+; * publish, distribute, sublicense, and/or sell copies of the Software,
+; * and to permit persons to whom the Software is furnished to do so,
+; * subject to the following conditions:
+; *
+; * The above copyright notice and this permission notice shall be included
+; * in all copies or substantial portions of the Software.
+; *
+; * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+; * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+; * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+; * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+; * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+; * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+; * THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+; *
+; * =================================================================
+; * See http://scmrtos.sourceforge.net for documentation, latest
+; * information, license and contact details.
+; * =================================================================
+; *
+; ******************************************************************************
+; * Ported by Andrey Chuikin, Copyright (c) 2008-2010
+
+; #include "scmRTOS_TARGET_CFG.h"
+
+; -----------------------------------------------------------------------------
+; PUBLIC FUNCTIONS
+;
+ EXTERN OS_ContextSwitchHook
+
+ EXPORT OS_Start
+ EXPORT PendSV_Handler
+
+; -----------------------------------------------------------------------------
+; EQUATES
+;
+NVIC_INT_CTRL EQU 0xE000ED04 ; Interrupt control state register.
+NVIC_PENDSVSET EQU 0x10000000 ; Value to trigger PendSV exception.
+
+NVIC_SYSPRI14 EQU 0xE000ED22 ; System priority register (priority 14).
+NVIC_PENDSV_PRI EQU 0xFF ; PendSV priority value (lowest).
+NVIC_SYSPRI15 EQU 0xE000ED23 ; System priority register (priority 15).
+NVIC_ST_PRI EQU 0xFF ; SysTick priority value (lowest).
+
+NVIC_ST_CTRL EQU 0xE000E010 ; SysTick Ctrl & Status Reg.
+NVIC_ST_RELOAD EQU 0xE000E014 ; SysTick Reload Value Reg.
+NVIC_ST_CTRL_CLK_SRC EQU 0x00000004 ; Clock Source.
+NVIC_ST_CTRL_INTEN EQU 0x00000002 ; Interrupt enable.
+NVIC_ST_CTRL_ENABLE EQU 0x00000001 ; Counter mode.
+
+; should be the same as in scmRTOS_TARGET_CFG.h
+SYSTICKFREQ EQU 100000000
+SYSTICKINTRATE EQU 1000
+
+; -----------------------------------------------------------------------------
+; CODE GENERATION DIRECTIVES
+;
+ AREA TEXT, CODE, READONLY
+ THUMB
+
+; -----------------------------------------------------------------------------
+; HANDLE PendSV EXCEPTION
+; void PendSV_Handler(void)
+;
+; Note(s) : 1) PendSV is used to cause a context switch. This is a recommended method for performing
+; context switches with Cortex-M3. This is because the Cortex-M3 auto-saves half of the
+; processor context on any exception, and restores same on return from exception. So only
+; saving of R4-R11 is required and fixing up the stack pointers. Using the PendSV exception
+; this way means that context saving and restoring is identical whether it is initiated from
+; a thread or occurs due to an interrupt or exception.
+;
+; 2) Pseudo-code is:
+; a) Get the process SP, if 0 then skip (goto f) the saving part (first context switch);
+; b) Save remaining regs r4-r11 on process stack;
+; c) Call OS_ContextSwitchHook for save current task SP and get new task SP;
+; d) Restore R4-R11 from new process stack;
+; e) Perform exception return which will restore remaining context.
+; f) Get SP for the first context switch (R2 hold it);
+; run SysTick; goto d);
+;
+; 3) On entry into PendSV handler:
+; a) The following have been saved on the process stack (by processor):
+; xPSR, PC, LR, R12, R0-R3
+; b) Processor mode is switched to Handler mode (from Thread mode)
+; c) Stack is Main stack (switched from Process stack)
+;
+; 4) Since PendSV is set to lowest priority in the system (by OS_Start() below), we
+; know that it will only be run when no other exception or interrupt is active, and
+; therefore safe to assume that context being switched out was using the process stack (PSP).
+;
+PendSV_Handler
+ CPSID I ; Prevent interruption during context switch
+ MRS R0, PSP ; PSP is process stack pointer
+ CBZ R0, nosave ; Skip register save the first time
+
+ STMDB R0!, {R4-R11} ; Save remaining regs r4-11 on process stack
+ ; At this point, entire context of process has been saved
+
+ PUSH {R14} ; Save LR exc_return value
+ LDR R1, =OS_ContextSwitchHook ; OS_ContextSwitchHook();
+ BLX R1
+ POP {R14}
+
+ContextRestore
+ ; R0 is new process SP;
+ LDMIA R0!, {R4-R11} ; Restore r4-11 from new process stack
+ MSR PSP, R0 ; Load PSP with new process SP
+ ORR LR, LR, #0x04 ; Ensure exception return uses process stack
+ CPSIE I
+ BX LR ; Exception return will restore remaining context
+nosave
+ MOV R0, R2 ; R2 hold the first task SP
+
+ LDR R1, =NVIC_ST_CTRL ; Enable and run SysTick
+ LDR R2, =(NVIC_ST_CTRL_CLK_SRC :OR: NVIC_ST_CTRL_INTEN :OR: NVIC_ST_CTRL_ENABLE)
+ STR R2, [R1]
+
+ B ContextRestore
+
+
+; -----------------------------------------------------------------------------
+; START MULTITASKING
+; void OS_Start(TStackItem* sp)
+;
+; Note(s) : 1) OS_Start() MUST:
+; a) Setup PendSV and SysTick exception priority to lowest;
+; b) Setup SysTick (reload value);
+; c) Enable interrupts (tasks will run with interrupts enabled).
+;
+OS_Start
+ LDR R1, =NVIC_SYSPRI14 ; Set the PendSV exception priority (lowest)
+ LDR R2, =NVIC_PENDSV_PRI
+ STRB R2, [R1]
+ LDR R1, =NVIC_SYSPRI15 ; Set the SysTick exception priority (lowest)
+ LDR R2, =NVIC_ST_PRI
+ STRB R2, [R1]
+
+ LDR R1, =NVIC_ST_RELOAD ; Setup SysTick
+ LDR R2, =(SYSTICKFREQ/SYSTICKINTRATE-1)
+ STR R2, [R1]
+
+ MOV R2, R0 ; Save the first task stack
+ MOVS R0, #0 ; Set the PSP to 0 for initial context switch call
+ MSR PSP, R0
+
+ LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch)
+ LDR R1, =NVIC_PENDSVSET
+ STR R1, [R0]
+
+ CPSIE I ; Enable interrupts at processor level
+loop
+ B loop ; Should never get here
+
+
+ END
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/scmRTOS/CortexM3/OS_Target_cpp.cpp Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,137 @@
+//******************************************************************************
+//*
+//* FULLNAME: Single-Chip Microcontroller Real-Time Operating System
+//*
+//* NICKNAME: scmRTOS
+//*
+//* PROCESSOR: ARM Cortex-M3
+//*
+//* TOOLKIT: EWARM (IAR Systems)
+//*
+//* PURPOSE: Target Dependent Stuff Source
+//*
+//* Version: 3.10
+//*
+//* $Revision: 195 $
+//* $Date:: 2008-06-19 #$
+//*
+//* Copyright (c) 2003-2010, Harry E. Zhurov
+//*
+//* Permission is hereby granted, free of charge, to any person
+//* obtaining a copy of this software and associated documentation
+//* files (the "Software"), to deal in the Software without restriction,
+//* including without limitation the rights to use, copy, modify, merge,
+//* publish, distribute, sublicense, and/or sell copies of the Software,
+//* and to permit persons to whom the Software is furnished to do so,
+//* subject to the following conditions:
+//*
+//* The above copyright notice and this permission notice shall be included
+//* in all copies or substantial portions of the Software.
+//*
+//* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+//* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+//* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+//* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+//* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+//* THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//*
+//* =================================================================
+//* See http://scmrtos.sourceforge.net for documentation, latest
+//* information, license and contact details.
+//* =================================================================
+//*
+//******************************************************************************
+//* Ported by Andrey Chuikin, Copyright (c) 2008-2010
+
+
+#include <scmRTOS.h>
+
+using namespace OS;
+
+//------------------------------------------------------------------------------
+//
+// OS Process's constructor
+//
+// Performs:
+// * initializing process data;
+// * registering of the process in kernel;
+// * preparing stack frame;
+//
+//
+TBaseProcess::TBaseProcess(TStackItem* Stack, word stack_size, TPriority pr, void (*exec)())
+ : StackPointer(Stack)
+ , StackSize(stack_size)
+ , Timeout(0)
+ , Priority(pr)
+{
+ Kernel.RegisterProcess(this);
+
+#ifdef DEBUG_STACK
+//---------------------------------------------------------------
+// Fill stack area with some value to be able later to define stack usage
+for (StackPointer-=(StackSize/sizeof(TStackItem)); StackPointer < Stack; *(StackPointer++) = STACK_FILL_CONST);
+#endif //DEBUG_STACK
+
+ //---------------------------------------------------------------
+ //
+ // Prepare Process Stack Frame
+ //
+ *(--StackPointer) = 0x01000000L; // xPSR
+ *(--StackPointer) = reinterpret_cast<dword>(exec); // Entry Point
+ StackPointer -= 14; // emulate "push R14,R12,R3,R2,R1,R0,R11-R4"
+
+ // The code below can be used for debug purpose. In this case comment
+ // line above and uncomment block below.
+/*
+ *(--StackPointer) = 0xFFFFFFFEL; // R14 (LR) (init value will cause fault if ever used)
+ *(--StackPointer) = 0x12121212L; // R12
+ *(--StackPointer) = 0x03030303L; // R3
+ *(--StackPointer) = 0x02020202L; // R2
+ *(--StackPointer) = 0x01010101L; // R1
+ *(--StackPointer) = 0x00000000L; // R0
+
+ // Remaining registers saved on process stack
+ *(--StackPointer) = 0x11111111L; // R11
+ *(--StackPointer) = 0x10101010L; // R10
+ *(--StackPointer) = 0x09090909L; // R9
+ *(--StackPointer) = 0x08080808L; // R8
+ *(--StackPointer) = 0x07070707L; // R7
+ *(--StackPointer) = 0x06060606L; // R6
+ *(--StackPointer) = 0x05050505L; // R5
+ *(--StackPointer) = 0x04040404L; // R4
+*/
+}
+//------------------------------------------------------------------------------
+//
+// Idle Process
+//
+typedef process<prIDLE, scmRTOS_IDLE_PROCESS_STACK_SIZE> TIdleProcess;
+
+TIdleProcess IdleProcess;
+
+template<> OS_PROCESS void TIdleProcess::Exec()
+{
+ for(;;)
+ {
+ #if scmRTOS_IDLE_HOOK_ENABLE == 1
+ IdleProcessUserHook();
+ #endif
+ }
+}
+//------------------------------------------------------------------------------
+OS_INTERRUPT void OS::SysTick_Handler()
+{
+ scmRTOS_ISRW_TYPE ISR;
+
+ Kernel.SystemTimer();
+
+#if scmRTOS_SYSTIMER_NEST_INTS_ENABLE == 0
+ DISABLE_NESTED_INTERRUPTS();
+#endif
+
+#if scmRTOS_SYSTIMER_HOOK_ENABLE == 1
+ SystemTimerUserHook();
+#endif
+}
+//------------------------------------------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/scmRTOS/CortexM3/commdefs.h Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,74 @@
+//******************************************************************************
+//*
+//* FULLNAME: Single-Chip Microcontroller Real-Time Operating System
+//*
+//* NICKNAME: scmRTOS
+//*
+//* PROCESSOR: ARM Cortex-M3
+//*
+//* TOOLKIT: EWARM (IAR Systems)
+//*
+//* PURPOSE: Common Type Definitions
+//*
+//* Version: 3.10
+//*
+//* $Revision: 195 $
+//* $Date:: 2008-06-19 #$
+//*
+//* Copyright (c) 2003-2010, Harry E. Zhurov
+//*
+//* Permission is hereby granted, free of charge, to any person
+//* obtaining a copy of this software and associated documentation
+//* files (the "Software"), to deal in the Software without restriction,
+//* including without limitation the rights to use, copy, modify, merge,
+//* publish, distribute, sublicense, and/or sell copies of the Software,
+//* and to permit persons to whom the Software is furnished to do so,
+//* subject to the following conditions:
+//*
+//* The above copyright notice and this permission notice shall be included
+//* in all copies or substantial portions of the Software.
+//*
+//* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+//* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+//* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+//* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+//* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+//* THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//*
+//* =================================================================
+//* See http://scmrtos.sourceforge.net for documentation, latest
+//* information, license and contact details.
+//* =================================================================
+//*
+//******************************************************************************
+//* Ported by Andrey Chuikin, Copyright (c) 2008-2010
+
+
+#ifndef COMMONDEFS_H
+#define COMMONDEFS_H
+
+#define DEBUG_STACK
+#define STACK_FILL_CONST 0xFFF7FFFF
+
+#ifndef __IAR_SYSTEMS_ASM__
+
+typedef unsigned char byte;
+typedef signed char sbyte;
+typedef unsigned short word;
+typedef unsigned long dword;
+
+typedef volatile byte sfr_byte;
+typedef volatile word sfr_word;
+typedef volatile dword sfr_dword;
+
+#define INLINE _Pragma("inline=forced")
+
+#endif // __IAR_SYSTEMS_ASM__
+
+#endif // COMMONDEFS_H
+
+//-----------------------------------------------------------------------------
+
+/*============================================================================*/
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scmRTOS/CortexM3/device.h Mon Feb 13 02:11:20 2012 +0000 @@ -0,0 +1,60 @@ +//****************************************************************************** +//* +//* FULLNAME: Single-Chip Microcontroller Real-Time Operating System +//* +//* NICKNAME: scmRTOS +//* +//* PROCESSOR: ARM Cortex-M3 +//* +//* TOOLKIT: EWARM (IAR Systems) +//* +//* PURPOSE: Device Definitions +//* +//* Version: 3.10 +//* +//* $Revision: 196 $ +//* $Date:: 2008-06-19 #$ +//* +//* Copyright (c) 2003-2010, Harry E. Zhurov +//* +//* Permission is hereby granted, free of charge, to any person +//* obtaining a copy of this software and associated documentation +//* files (the "Software"), to deal in the Software without restriction, +//* including without limitation the rights to use, copy, modify, merge, +//* publish, distribute, sublicense, and/or sell copies of the Software, +//* and to permit persons to whom the Software is furnished to do so, +//* subject to the following conditions: +//* +//* The above copyright notice and this permission notice shall be included +//* in all copies or substantial portions of the Software. +//* +//* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +//* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +//* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +//* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +//* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +//* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +//* THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +//* +//* ================================================================= +//* See http://scmrtos.sourceforge.net for documentation, latest +//* information, license and contact details. +//* ================================================================= +//* +//****************************************************************************** +//* Ported by Andrey Chuikin, Copyright (c) 2008-2010 + +#ifndef DEVICE_H +#define DEVICE_H + +#include <commdefs.h> + +//------------------------------------------------------------------------------ +// Definitions for some processor registers in order to not include specific +// header file for various Cortex-M3 processor derivatives. +#define CPU_ICSR ( ( sfr_dword *) 0xE000ED04 ) // Interrupt Control State Register +#define CPU_SYSTICKCSR ( ( sfr_dword *) 0xE000E010 ) // SysTick Control and Status Register +#define CPU_SYSTICKCSR_EINT 0x02 // Bit for enable/disable SysTick interrupt + + +#endif /* DEVICE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/scmRTOS/scmRTOS_TARGET_CFG.h Mon Feb 13 02:11:20 2012 +0000
@@ -0,0 +1,102 @@
+//******************************************************************************
+//*
+//* FULLNAME: Single-Chip Microcontroller Real-Time Operating System
+//*
+//* NICKNAME: scmRTOS
+//*
+//* PROCESSOR: ARM Cortex-M3
+//*
+//* TOOLKIT: EWARM (IAR Systems)
+//*
+//* PURPOSE: Project Level Target Extensions Config
+//*
+//* Version: 3.10
+//*
+//* $Revision: 196 $
+//* $Date:: 2008-06-19 #$
+//*
+//* Copyright (c) 2003-2010, Harry E. Zhurov
+//*
+//* Permission is hereby granted, free of charge, to any person
+//* obtaining a copy of this software and associated documentation
+//* files (the "Software"), to deal in the Software without restriction,
+//* including without limitation the rights to use, copy, modify, merge,
+//* publish, distribute, sublicense, and/or sell copies of the Software,
+//* and to permit persons to whom the Software is furnished to do so,
+//* subject to the following conditions:
+//*
+//* The above copyright notice and this permission notice shall be included
+//* in all copies or substantial portions of the Software.
+//*
+//* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+//* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+//* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+//* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+//* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+//* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
+//* THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//*
+//* =================================================================
+//* See http://scmrtos.sourceforge.net for documentation, latest
+//* information, license and contact details.
+//* =================================================================
+//*
+//******************************************************************************
+//* mbed port by Igor Skochinsky
+
+#ifndef scmRTOS_TARGET_CFG_H
+#define scmRTOS_TARGET_CFG_H
+
+#include "device.h"
+
+// Define SysTick clock frequency and its interrupt rate in Hz.
+#define SYSTICKFREQ 100000000
+#define SYSTICKINTRATE 1000
+
+//------------------------------------------------------------------------------
+//
+// System Timer stuff
+//
+//
+namespace OS
+{
+extern "C" void SysTick_Handler();
+}
+
+#define LOCK_SYSTEM_TIMER() ( *CPU_SYSTICKCSR &= ~CPU_SYSTICKCSR_EINT )
+#define UNLOCK_SYSTEM_TIMER() ( *CPU_SYSTICKCSR |= CPU_SYSTICKCSR_EINT )
+
+//------------------------------------------------------------------------------
+//
+// Context Switch ISR stuff
+//
+//
+namespace OS
+{
+#if scmRTOS_IDLE_HOOK_ENABLE == 1
+ void IdleProcessUserHook();
+#endif
+
+#if scmRTOS_CONTEXT_SWITCH_SCHEME == 1
+
+ INLINE inline void RaiseContextSwitch() { *CPU_ICSR |= 0x10000000; }
+
+ #define ENABLE_NESTED_INTERRUPTS()
+
+ #if scmRTOS_SYSTIMER_NEST_INTS_ENABLE == 0
+ #define DISABLE_NESTED_INTERRUPTS() TCritSect cs
+ #else
+ #define DISABLE_NESTED_INTERRUPTS()
+ #endif
+
+#else
+ #error "Cortex-M3 port supports software interrupt switch method only!"
+
+#endif // scmRTOS_CONTEXT_SWITCH_SCHEME
+
+}
+//-----------------------------------------------------------------------------
+
+#endif // scmRTOS_TARGET_CFG_H
+//-----------------------------------------------------------------------------
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scmRTOS_config.h Mon Feb 13 02:11:20 2012 +0000 @@ -0,0 +1,133 @@ +//****************************************************************************** +//* +//* FULLNAME: Single-Chip Microcontroller Real-Time Operating System +//* +//* NICKNAME: scmRTOS +//* +//* PROCESSOR: ARM Cortex-M3 +//* +//* TOOLKIT: EWARM (IAR Systems) +//* +//* PURPOSE: Project Level Configuration +//* +//* Version: 3.10 +//* +//* $Revision: 196 $ +//* $Date:: 2008-06-19 #$ +//* +//* Copyright (c) 2003-2010, Harry E. Zhurov +//* +//* Permission is hereby granted, free of charge, to any person +//* obtaining a copy of this software and associated documentation +//* files (the "Software"), to deal in the Software without restriction, +//* including without limitation the rights to use, copy, modify, merge, +//* publish, distribute, sublicense, and/or sell copies of the Software, +//* and to permit persons to whom the Software is furnished to do so, +//* subject to the following conditions: +//* +//* The above copyright notice and this permission notice shall be included +//* in all copies or substantial portions of the Software. +//* +//* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +//* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +//* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +//* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +//* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +//* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +//* THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +//* +//* ================================================================= +//* See http://scmrtos.sourceforge.net for documentation, latest +//* information, license and contact details. +//* ================================================================= +//* +//****************************************************************************** +//* Ported by Andrey Chuikin, Copyright (c) 2008-2010 + +#ifndef scmRTOS_CONFIG_H +#define scmRTOS_CONFIG_H + +#ifndef __IAR_SYSTEMS_ASM__ +#include <commdefs.h> + +typedef word TTimeout; + +#endif // __IAR_SYSTEMS_ASM__ + +#include "device.h" +//------------------------------------------------------------------------------ +// +// Specify scmRTOS Process Count. Must be less than 31 +// +// +#define scmRTOS_PROCESS_COUNT 14 + +//----------------------------------------------------------------------------- +// +// scmRTOS System Timer +// +// Nested Interrupts enable macro. Value 1 means that interrupts are +// globally enabled within System Timer ISR (this is default for Cortex-M3). +// +// +#define scmRTOS_SYSTIMER_NEST_INTS_ENABLE 1 + +//----------------------------------------------------------------------------- +// +// scmRTOS System Timer Tick Counter Enable +// +// +#define scmRTOS_SYSTEM_TICKS_ENABLE 1 + + +//----------------------------------------------------------------------------- +// +// scmRTOS System Timer Hook +// +// +#define scmRTOS_SYSTIMER_HOOK_ENABLE 1 + +//----------------------------------------------------------------------------- +// +// scmRTOS Idle Process Hook +// +// +#define scmRTOS_IDLE_HOOK_ENABLE 1 + +//----------------------------------------------------------------------------- +// +// scmRTOS Idle Process Stack size (in bytes) +// (20 * sizeof(TStackItem)) - it's a minimum allowed value. +// +#define scmRTOS_IDLE_PROCESS_STACK_SIZE (20 * sizeof(TStackItem)) + +//----------------------------------------------------------------------------- +// +// scmRTOS Priority Order +// +// This macro defines the order of the process's priorities. Default, +// the ascending order is used. Alternatively, the descending priority +// order can be used. On some platforms the descending order is preferred +// because of performance. +// +// Default (corresponding to ascending order) value of macro is 0. +// Alternative (corresponding to descending order) value of macro is 1. +// +// On Cortex-M3 the descending order is preferred for performance reason. +// +#define scmRTOS_PRIORITY_ORDER 1 + +//----------------------------------------------------------------------------- +// +// scmRTOS Context Switch User Hook enable +// +// The macro enables/disables user defined hook called from system +// Context Switch Hook function. +// +// +#define scmRTOS_CONTEXT_SWITCH_USER_HOOK_ENABLE 0 + + +#endif // scmRTOS_CONFIG_H +//----------------------------------------------------------------------------- +