From 200aded9ff143034eb45d309eef325edd90f9a7a Mon Sep 17 00:00:00 2001 From: KalbeAbbas Date: Mon, 9 Nov 2020 11:35:32 +0500 Subject: [PATCH] IM01 2 --- .github/makecode/blocks.png | Bin 3216 -> 0 bytes .github/workflows/makecode.yml | 29 - .github/workflows/maker.yml | 44 + .gitignore | 8 +- .vscode/settings.json | 23 - .vscode/tasks.json | 30 - Gemfile | 2 - README.md | 21 +- _config.yml | 8 - diskio.h | 38 + dummy.ts | 1 + ff.cpp | 1783 ++++++++++++++++++++++++++++++++ ff.h | 173 ++++ ffconf.h | 14 + icon.png | Bin 0 -> 85960 bytes im01.cpp | 76 ++ im01.ts | 131 +++ main.blocks | 1 - main.ts | 3 - pxt.json | 28 +- sdmm.cpp | 544 ++++++++++ 21 files changed, 2842 insertions(+), 115 deletions(-) delete mode 100644 .github/makecode/blocks.png delete mode 100644 .github/workflows/makecode.yml create mode 100644 .github/workflows/maker.yml delete mode 100644 .vscode/settings.json delete mode 100644 .vscode/tasks.json delete mode 100644 Gemfile delete mode 100644 _config.yml create mode 100644 diskio.h create mode 100644 dummy.ts create mode 100644 ff.cpp create mode 100644 ff.h create mode 100644 ffconf.h create mode 100644 icon.png create mode 100644 im01.cpp create mode 100644 im01.ts delete mode 100644 main.blocks delete mode 100644 main.ts create mode 100644 sdmm.cpp diff --git a/.github/makecode/blocks.png b/.github/makecode/blocks.png deleted file mode 100644 index ff01e321b414c3ebc72ddecdcf1579d28b09cd81..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3216 zcmZ`+c|4R|8y?fdP&CLClA&aamSM7%u@ACTwp4h-7))L&Yhz1fNn>wp}jBz0Q~D>8)<c>@_~Fdvvt3H=)e8Y+eBY0G5zh;G`aB{t$}&x^*@;Hci{z6?Bw$JI z&GLv4Yshb8766&VIgyWKfXw}am5?MGQu7420Rq^3i%=>meZ`oh zSL9B5NGH(aHKQN*@iX=2)&i?P`-e21A)Y$}Gas^<{4rp_nQq{}ir89}StK^Bq)kp| z67b0p+_5<*ZA$J5jlP{2YP;)a9cf7e)pNN(ey{<_Mym4MvmbUJUPsF^!^CeQ7Fz=? z!~fZI!sb2+?{Tjkzh=0&MR~kWwEp8>-}Ub77tHR&+T@J_5yJOpGByu4n1tHRSESyB z;Fjt9n`|WXH;Wn)`_SCeOIa!A_UE-V0-e zlOKhSI_%X!e|ybJNq8i zN$=wUE#pO-AKz`-CuqQ#x$gwV=iH7lDX^}2JO?Ld#5ra!UiTISA-sE%jNSm#9}c0K z%mBy6@Abz3@0jR6bjN{h&vxR$t%Q5k=C#&(*$;|z>*`-mXpwG?GbbjReF6iq z*<{gDEUbZ2xUzWG-P_=9dAXfR&B*z{>VhJvdshEG*frW{mjP$0-##h%ziL!cRO22o z_CVQXuQpB&rPJ=(Q@Zq|QSJLV3e`!wb_n;7 znU+WTDNhY_l|i?gGk;9tdMj_djC*M*wN|=yaM{=vO2Y-+3fot&Yh%Z5?}G7An+0

$Q(D6+LJ5x3s=19?t9aA8s^6vl{Wps_tdI13^R{%i%*nd878viI4kQ7Vb^+ zj-U(yqRkCckM=^U%xp`sJ?8X9j-Ln8(Riva_?ZLkQzp!rdR|$8ix`LBCu6+YUoprz zwn~KX#f~}w*X_xrqPa-8_*&O{VPwrKBVmh26H7EZShIyR`uZi^)-)?6LekbdQM*Lx z<`imevYrNwehO{MxYt*lk;=%fQCwK1&KdW`r;o;`bdQH~wi}x+=tANHL~U^ZRux&Ercp>VHZ)vOY{JdzrE@zvotX$8rda#^vudYU=tp4-q(r>o|c-ik_ZQED$24b2}P?g^(9eY%icq>pV z=52opxm`7bjVnky*`9JWZ7^bed2Lg#$6XEnL+(ilz#ZX(@09YS5{29*+>v~lR3eh# z(fbgT*HH+c2q=jH$B#b%43rUc@bggtn(6DDO+QdvX;X<_mdg#`^^@H!Rrl=5Ch=la?;ih zj{)BE8?$7Y;!2}TMA2!m>}lanM3EUaR5LCX#Kt}ylT3vf;)XT_M&>}8*1iDh%b=wpvr1It?m+8*>V>^Gi4!t=N zsW13D^`UwyDHPWx?C<>T5b9p<2yHFr0$G%a@Wx96w^hZ;kn^tztTBr6~|GK#} z#$KF=#PJoOYj@Vpfd7zKJ#Ar{bc@Sa?xEKk;aN2mI$%j3n;>Bv*(((0PT)dz%*eaQ+EthiH=|H{NN zS3uP(EusPU*mH4yg!}@I6xyfjp*@Y8n#@`e*v^QS%ZDsDZQ0^IbQcZ;v ztMP-h?lqGMvb(?`{`p{5(B1kT*);#5r~H3P+ETFBk0zF@Fd@k^g(CXyV%tbY79+6< zk?sUqgj2!b`#Zq?)O64>GI#m<4W+dkRBs_Pc;h45l zkDXc!4=#o-a%t;I_?RoH6CSQ=qgYZ!eUaV8+DlQgX;RZaM!WOqM3tJFs;r-5)HFJs znboO2i`3!vKQi$3Qi=aLpw{7Q=y0yywBzN%+Zkhv@dm_eE^N^G6T!iPkaLJ+d!>FO phdZ_|yboREJGq)vAMUjy0K4I+buWw}4gNGBI1_8*DnqxJe*u?%29W>& diff --git a/.github/workflows/makecode.yml b/.github/workflows/makecode.yml deleted file mode 100644 index 1aa354c..0000000 --- a/.github/workflows/makecode.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: MakeCode - -on: [push] - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [8.x] - - steps: - - uses: actions/checkout@v1 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - name: npm install - run: | - npm install -g pxt - pxt target microbit - - name: build - run: | - pxt install - pxt build --cloud - env: - CI: true diff --git a/.github/workflows/maker.yml b/.github/workflows/maker.yml new file mode 100644 index 0000000..316cc1d --- /dev/null +++ b/.github/workflows/maker.yml @@ -0,0 +1,44 @@ +name: maker + +on: + push: + branches: + - master + schedule: + - cron: '0 0 5,20 * *' + +jobs: + prepare: + runs-on: ubuntu-latest + if: "! contains(github.event.head_commit.message, '[skip ci]')" + steps: + - run: echo "${{ github.event.head_commit.message }}" + + build: + needs: prepare + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [8.x] + + steps: + - uses: actions/checkout@v1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + + - name: maker pxt.json + uses: datamonsters/replace-action@master + with: + files: 'pxt.json' + replacements: 'core=xinabox-cs11,test.ts=dummy.ts' + - name: maker compilation + run: | + npm install -g pxt + pxt target maker + pxt install + pxt build --cloud + env: + CI: true diff --git a/.gitignore b/.gitignore index d2196e7..26c0c1f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -# MakeCode built node_modules yotta_modules @@ -8,4 +7,9 @@ _site *.db *.tgz .header.json -.simstate.json +main.ts +main.blocks +Gemfile +_config.yml +tsconfig.json +.vscode \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index f8106d4..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "editor.formatOnType": true, - "files.autoSave": "afterDelay", - "files.watcherExclude": { - "**/.git/objects/**": true, - "**/built/**": true, - "**/node_modules/**": true, - "**/yotta_modules/**": true, - "**/yotta_targets": true, - "**/pxt_modules/**": true - }, - "files.associations": { - "*.blocks": "html", - "*.jres": "json" - }, - "search.exclude": { - "**/built": true, - "**/node_modules": true, - "**/yotta_modules": true, - "**/yotta_targets": true, - "**/pxt_modules": true - } -} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 9ee2cf6..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,30 +0,0 @@ - -// A task runner that calls the MakeCode (PXT) compiler -{ - "version": "2.0.0", - "tasks": [{ - "label": "pxt deploy", - "type": "shell", - "command": "pxt deploy --local", - "group": "build", - "problemMatcher": [ "$tsc" ] - }, { - "label": "pxt build", - "type": "shell", - "command": "pxt build --local", - "group": "build", - "problemMatcher": [ "$tsc" ] - }, { - "label": "pxt install", - "type": "shell", - "command": "pxt install", - "group": "build", - "problemMatcher": [ "$tsc" ] - }, { - "label": "pxt clean", - "type": "shell", - "command": "pxt clean", - "group": "test", - "problemMatcher": [ "$tsc" ] - }] -} diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 91ceacd..0000000 --- a/Gemfile +++ /dev/null @@ -1,2 +0,0 @@ -source 'https://rubygems.org' -gem 'github-pages', group: :jekyll_plugins \ No newline at end of file diff --git a/README.md b/README.md index 6246e5c..3728537 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,36 @@ +[![GitHub Issues](https://img.shields.io/github/issues/xinabox/pxt-CS11.svg)](https://github.com/xinabox/pxt-CS11/issues) +![GitHub Commit](https://img.shields.io/github/last-commit/xinabox/pxt-CS11) +![Maintained](https://img.shields.io/maintenance/yes/2020) +![Build status badge](https://github.com/xinabox/pxt-CS11/workflows/maker/badge.svg) +![Developer](https://img.shields.io/badge/Developer-lb-blue) -> Open this page at [https://xinabox.github.io/pxt-im01-2/](https://xinabox.github.io/pxt-im01-2/) +> Open this page at [https://.github.io//](https://.github.io//) ## Use as Extension This repository can be added as an **extension** in MakeCode. -* open [https://makecode.microbit.org/](https://makecode.microbit.org/) +* open []() * click on **New Project** * click on **Extensions** under the gearwheel menu -* search for **https://github.com/xinabox/pxt-im01-2** and import +* search for **https://github.com/** and import -## Edit this project ![Build status badge](https://github.com/xinabox/pxt-im01-2/workflows/MakeCode/badge.svg) +## Edit this project ![Build status badge](https://github.com//workflows/MakeCode/badge.svg) To edit this repository in MakeCode. -* open [https://makecode.microbit.org/](https://makecode.microbit.org/) +* open []() * click on **Import** then click on **Import URL** -* paste **https://github.com/xinabox/pxt-im01-2** and click import +* paste **https://github.com/** and click import ## Blocks preview This image shows the blocks code from the last commit in master. This image may take a few minutes to refresh. -![A rendered view of the blocks](https://github.com/xinabox/pxt-im01-2/raw/master/.github/makecode/blocks.png) +![A rendered view of the blocks](https://github.com//raw/master/.github/makecode/blocks.png) #### Metadata (used for search, rendering) -* for PXT/microbit +* for PXT/ diff --git a/_config.yml b/_config.yml deleted file mode 100644 index f56a029..0000000 --- a/_config.yml +++ /dev/null @@ -1,8 +0,0 @@ -makecode: - target: microbit - platform: microbit - home_url: https://makecode.microbit.org/ -theme: jekyll-theme-slate -include: - - assets - - README.md diff --git a/diskio.h b/diskio.h new file mode 100644 index 0000000..f3b3b5e --- /dev/null +++ b/diskio.h @@ -0,0 +1,38 @@ +#include "ff.h" +#ifndef _DISKIO_DEFINED +#define _DISKIO_DEFINED + +typedef BYTE DSTATUS; + +typedef enum +{ + RES_OK = 0, + RES_ERROR, + RES_WRPRT, + RES_NOTRDY, + RES_PARERR +} DRESULT; + +DSTATUS disk_initialize(BYTE pdrv); +DSTATUS disk_status(BYTE pdrv); +DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count); +DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count); +DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff); + +#define STA_NOINIT 0x01 + +#define STA_PROTECT 0x04 + +#define CTRL_SYNC 0 +#define GET_SECTOR_COUNT 1 +#define GET_SECTOR_SIZE 2 +#define GET_BLOCK_SIZE 3 +#define CTRL_TRIM 4 + +#define CT_MMC 0x01 +#define CT_SD1 0x02 +#define CT_SD2 0x04 +#define CT_SDC (CT_SD1 | CT_SD2) +#define CT_BLOCK 0x08 + +#endif diff --git a/dummy.ts b/dummy.ts new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/dummy.ts @@ -0,0 +1 @@ + diff --git a/ff.cpp b/ff.cpp new file mode 100644 index 0000000..3bb63a4 --- /dev/null +++ b/ff.cpp @@ -0,0 +1,1783 @@ +#include "ff.h" +#include "diskio.h" + +#define MAX_DIR 0x200000 +#define MAX_DIR_EX 0x10000000 +#define MAX_FAT12 0xFF5 +#define MAX_FAT16 0xFFF5 +#define MAX_FAT32 0x0FFFFFF5 + +#define IsLower(c) ((c) >= 'a' && (c) <= 'z') +#define IsDigit(c) ((c) >= '0' && (c) <= '9') + +#define FA_SEEKEND 0x20 +#define FA_MODIFIED 0x40 + +#define AM_VOL 0x08 +#define AM_LFN 0x0F +#define AM_MASK 0x3F + +#define NSFLAG 11 + +#define NS_LAST 0x04 + +#define NS_DOT 0x20 + +#define NS_NONAME 0x80 + +#define BS_JmpBoot 0 + +#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_FATSz16 22 + +#define BPB_TotSec32 32 + +#define BS_FilSysType 54 + +#define BS_55AA 510 + +#define BPB_FATSz32 36 + +#define BPB_FSVer32 42 +#define BPB_RootClus32 44 +#define BPB_FSInfo32 48 + +#define BS_FilSysType32 82 + +#define DIR_Name 0 +#define DIR_Attr 11 + +#define DIR_CrtTime 14 +#define DIR_LstAccDate 18 +#define DIR_FstClusHI 20 +#define DIR_ModTime 22 +#define DIR_FstClusLO 26 +#define DIR_FileSize 28 + +#define SZDIRE 32 +#define DDEM 0xE5 +#define RDDEM 0x05 + +#define FSI_LeadSig 0 +#define FSI_StrucSig 484 +#define FSI_Free_Count 488 +#define FSI_Nxt_Free 492 + +#define MBR_Table 446 +#define SZ_PTE 16 + +#define PTE_StLba 8 + +#define ABORT(fs, res) \ + { \ + fp->err = (BYTE)(res); \ + LEAVE_FF(fs, res); \ + } + +#define LEAVE_FF(fs, res) return res + +#define LD2PD(vol) (BYTE)(vol) +#define LD2PT(vol) 0 + +#define SS(fs) ((UINT)FF_MAX_SS) + +#define GET_FATTIME() ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16) + +#define TBL_DC932 \ + { \ + 0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, 0x00, 0x00 \ + } + +#define MERGE_2STR(a, b) a##b +#define MKCVTBL(hd, cp) MERGE_2STR(hd, cp) + +static FATFS *FatFs[FF_VOLUMES]; +static WORD Fsid; + +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) +#define FREE_NAMBUF() + +static const BYTE DbcTbl[] = MKCVTBL(TBL_DC, FF_CODE_PAGE); + +static WORD ld_word(const BYTE *ptr) +{ + WORD rv; + + rv = ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +static DWORD ld_dword(const BYTE *ptr) +{ + DWORD rv; + + rv = ptr[3]; + rv = rv << 8 | ptr[2]; + rv = rv << 8 | ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +#if !FF_FS_READONLY +static void st_word(BYTE *ptr, WORD val) +{ + *ptr++ = (BYTE)val; + val >>= 8; + *ptr++ = (BYTE)val; +} + +static void st_dword(BYTE *ptr, DWORD val) +{ + *ptr++ = (BYTE)val; + val >>= 8; + *ptr++ = (BYTE)val; + val >>= 8; + *ptr++ = (BYTE)val; + val >>= 8; + *ptr++ = (BYTE)val; +} + +#endif + +static void mem_cpy(void *dst, const void *src, UINT cnt) +{ + BYTE *d = (BYTE *)dst; + const BYTE *s = (const BYTE *)src; + + if (cnt != 0) + { + do + { + *d++ = *s++; + } while (--cnt); + } +} + +static void mem_set(void *dst, int val, UINT cnt) +{ + BYTE *d = (BYTE *)dst; + + do + { + *d++ = (BYTE)val; + } while (--cnt); +} + +static int mem_cmp(const void *dst, const void *src, UINT cnt) +{ + const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; + int r = 0; + + do + { + r = *d++ - *s++; + } while (--cnt && r == 0); + + return r; +} + +static int chk_chr(const char *str, int chr) +{ + while (*str && *str != chr) + str++; + return *str; +} + +static int dbc_1st(BYTE c) +{ + if (c >= DbcTbl[0]) + { + if (c <= DbcTbl[1]) + return 1; + if (c >= DbcTbl[2] && c <= DbcTbl[3]) + return 1; + } + return 0; +} + +static int dbc_2nd(BYTE c) +{ + if (c >= DbcTbl[4]) + { + if (c <= DbcTbl[5]) + return 1; + if (c >= DbcTbl[6] && c <= DbcTbl[7]) + return 1; + if (c >= DbcTbl[8] && c <= DbcTbl[9]) + return 1; + } + return 0; +} + +#if !FF_FS_READONLY +static FRESULT sync_window( + FATFS *fs) +{ + FRESULT res = FR_OK; + + if (fs->wflag) + { + if (disk_write(fs->pdrv, fs->win, fs->winsect, 1) == RES_OK) + { + fs->wflag = 0; + if (fs->winsect - fs->fatbase < fs->fsize) + { + if (fs->n_fats == 2) + disk_write(fs->pdrv, fs->win, fs->winsect + fs->fsize, 1); + } + } + else + { + res = FR_DISK_ERR; + } + } + return res; +} +#endif + +static FRESULT move_window( + FATFS *fs, + LBA_t sect) +{ + FRESULT res = FR_OK; + + if (sect != fs->winsect) + { +#if !FF_FS_READONLY + res = sync_window(fs); +#endif + if (res == FR_OK) + { + if (disk_read(fs->pdrv, fs->win, sect, 1) != RES_OK) + { + sect = (LBA_t)0 - 1; + res = FR_DISK_ERR; + } + fs->winsect = sect; + } + } + return res; +} + +#if !FF_FS_READONLY + +static FRESULT sync_fs( + FATFS *fs) +{ + FRESULT res; + + res = sync_window(fs); + if (res == FR_OK) + { + if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) + { + + mem_set(fs->win, 0, sizeof fs->win); + 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_clst); + st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); + + fs->winsect = fs->volbase + 1; + disk_write(fs->pdrv, fs->win, fs->winsect, 1); + fs->fsi_flag = 0; + } + + if (disk_ioctl(fs->pdrv, CTRL_SYNC, 0) != RES_OK) + res = FR_DISK_ERR; + } + + return res; +} + +#endif + +static LBA_t clst2sect( + FATFS *fs, + DWORD clst) +{ + clst -= 2; + if (clst >= fs->n_fatent - 2) + return 0; + return fs->database + (LBA_t)fs->csize * clst; +} + +static DWORD get_fat( + FFOBJID *obj, + DWORD clst) +{ + UINT wc, bc; + DWORD val; + FATFS *fs = obj->fs; + + if (clst < 2 || clst >= fs->n_fatent) + { + val = 1; + } + else + { + val = 0xFFFFFFFF; + + switch (fs->fs_type) + { + case FS_FAT32: + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) + break; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x0FFFFFFF; + break; + default: + val = 1; + } + } + + return val; +} + +#if !FF_FS_READONLY + +static FRESULT put_fat( + FATFS *fs, + DWORD clst, + DWORD val) +{ + UINT bc; + BYTE *p; + FRESULT res = FR_INT_ERR; + + if (clst >= 2 && clst < fs->n_fatent) + { + switch (fs->fs_type) + { + case FS_FAT32: + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); + if (res != FR_OK) + break; + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) + { + val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst * 4 % SS(fs)) & 0xF0000000); + } + st_dword(fs->win + clst * 4 % SS(fs), val); + fs->wflag = 1; + break; + } + } + return res; +} + +#endif + +#if !FF_FS_READONLY + +static FRESULT remove_chain( + FFOBJID *obj, + DWORD clst, + DWORD pclst) +{ + FRESULT res = FR_OK; + DWORD nxt; + FATFS *fs = obj->fs; + + if (clst < 2 || clst >= fs->n_fatent) + return FR_INT_ERR; + + if (pclst != 0 && (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT || obj->stat != 2)) + { + res = put_fat(fs, pclst, 0xFFFFFFFF); + if (res != FR_OK) + return res; + } + + do + { + nxt = get_fat(obj, clst); + if (nxt == 0) + break; + if (nxt == 1) + return FR_INT_ERR; + if (nxt == 0xFFFFFFFF) + return FR_DISK_ERR; + if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) + { + res = put_fat(fs, clst, 0); + if (res != FR_OK) + return res; + } + if (fs->free_clst < fs->n_fatent - 2) + { + fs->free_clst++; + fs->fsi_flag |= 1; + } + clst = nxt; + } while (clst < fs->n_fatent); + return FR_OK; +} + +static DWORD create_chain( + FFOBJID *obj, + DWORD clst) +{ + DWORD cs, ncl, scl; + FRESULT res; + FATFS *fs = obj->fs; + + if (clst == 0) + { + scl = fs->last_clst; + if (scl == 0 || scl >= fs->n_fatent) + scl = 1; + } + else + { + cs = get_fat(obj, clst); + if (cs < 2) + return 1; + if (cs == 0xFFFFFFFF) + return cs; + if (cs < fs->n_fatent) + return cs; + scl = clst; + } + if (fs->free_clst == 0) + return 0; + { + ncl = 0; + if (scl == clst) + { + ncl = scl + 1; + if (ncl >= fs->n_fatent) + ncl = 2; + cs = get_fat(obj, ncl); + if (cs == 1 || cs == 0xFFFFFFFF) + return cs; + if (cs != 0) + { + cs = fs->last_clst; + if (cs >= 2 && cs < fs->n_fatent) + scl = cs; + ncl = 0; + } + } + if (ncl == 0) + { + ncl = scl; + for (;;) + { + ncl++; + if (ncl >= fs->n_fatent) + { + ncl = 2; + if (ncl > scl) + return 0; + } + cs = get_fat(obj, ncl); + if (cs == 0) + break; + if (cs == 1 || cs == 0xFFFFFFFF) + return cs; + if (ncl == scl) + return 0; + } + } + res = put_fat(fs, ncl, 0xFFFFFFFF); + if (res == FR_OK && clst != 0) + { + res = put_fat(fs, clst, ncl); + } + } + + if (res == FR_OK) + { + fs->last_clst = ncl; + if (fs->free_clst <= fs->n_fatent - 2) + fs->free_clst--; + fs->fsi_flag |= 1; + } + else + { + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; + } + + return ncl; +} + +#endif + +#if !FF_FS_READONLY +static FRESULT dir_clear( + FATFS *fs, + DWORD clst) +{ + LBA_t sect; + UINT n, szb; + BYTE *ibuf; + + if (sync_window(fs) != FR_OK) + return FR_DISK_ERR; + sect = clst2sect(fs, clst); + fs->winsect = sect; + mem_set(fs->win, 0, sizeof fs->win); + { + ibuf = fs->win; + szb = 1; + for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) + ; + } + return (n == fs->csize) ? FR_OK : FR_DISK_ERR; +} +#endif + +static FRESULT dir_sdi( + DIR *dp, + DWORD ofs) +{ + DWORD csz, clst; + FATFS *fs = dp->obj.fs; + + if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) + { + return FR_INT_ERR; + } + dp->dptr = ofs; + clst = dp->obj.sclust; + if (clst == 0 && fs->fs_type >= FS_FAT32) + { + clst = (DWORD)fs->dirbase; + if (FF_FS_EXFAT) + dp->obj.stat = 0; + } + + if (clst == 0) + { + if (ofs / SZDIRE >= fs->n_rootdir) + return FR_INT_ERR; + dp->sect = fs->dirbase; + } + else + { + csz = (DWORD)fs->csize * SS(fs); + while (ofs >= csz) + { + clst = get_fat(&dp->obj, clst); + if (clst == 0xFFFFFFFF) + return FR_DISK_ERR; + if (clst < 2 || clst >= fs->n_fatent) + return FR_INT_ERR; + ofs -= csz; + } + dp->sect = clst2sect(fs, clst); + } + dp->clust = clst; + if (dp->sect == 0) + return FR_INT_ERR; + dp->sect += ofs / SS(fs); + dp->dir = fs->win + (ofs % SS(fs)); + + return FR_OK; +} + +static FRESULT dir_next( + DIR *dp, + int stretch) +{ + DWORD ofs, clst; + FATFS *fs = dp->obj.fs; + + ofs = dp->dptr + SZDIRE; + if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) + dp->sect = 0; + if (dp->sect == 0) + return FR_NO_FILE; + + if (ofs % SS(fs) == 0) + { + dp->sect++; + + if (dp->clust == 0) + { + if (ofs / SZDIRE >= fs->n_rootdir) + { + dp->sect = 0; + return FR_NO_FILE; + } + } + else + { + if ((ofs / SS(fs) & (fs->csize - 1)) == 0) + { + clst = get_fat(&dp->obj, dp->clust); + if (clst <= 1) + return FR_INT_ERR; + if (clst == 0xFFFFFFFF) + return FR_DISK_ERR; + if (clst >= fs->n_fatent) + { + if (!stretch) + { + dp->sect = 0; + return FR_NO_FILE; + } + clst = create_chain(&dp->obj, dp->clust); + if (clst == 0) + return FR_DENIED; + if (clst == 1) + return FR_INT_ERR; + if (clst == 0xFFFFFFFF) + return FR_DISK_ERR; + if (dir_clear(fs, clst) != FR_OK) + return FR_DISK_ERR; + if (FF_FS_EXFAT) + dp->obj.stat |= 4; + } + dp->clust = clst; + dp->sect = clst2sect(fs, clst); + } + } + } + dp->dptr = ofs; + dp->dir = fs->win + ofs % SS(fs); + + return FR_OK; +} + +#if !FF_FS_READONLY + +static FRESULT dir_alloc( + DIR *dp, + UINT nent) +{ + FRESULT res; + UINT n; + FATFS *fs = dp->obj.fs; + + res = dir_sdi(dp, 0); + if (res == FR_OK) + { + n = 0; + do + { + res = move_window(fs, dp->sect); + if (res != FR_OK) + break; + if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) + { + if (++n == nent) + break; + } + else + { + n = 0; + } + res = dir_next(dp, 1); + } while (res == FR_OK); + } + + if (res == FR_NO_FILE) + res = FR_DENIED; + return res; +} + +#endif + +static DWORD ld_clust( + FATFS *fs, + const BYTE *dir) +{ + DWORD cl; + + cl = ld_word(dir + DIR_FstClusLO); + if (fs->fs_type == FS_FAT32) + { + cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16; + } + + return cl; +} + +#if !FF_FS_READONLY +static void st_clust( + FATFS *fs, + BYTE *dir, + DWORD cl) +{ + st_word(dir + DIR_FstClusLO, (WORD)cl); + if (fs->fs_type == FS_FAT32) + { + st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16)); + } +} +#endif + +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT + +#define DIR_READ_FILE(dp) dir_read(dp, 0) +#define DIR_READ_LABEL(dp) dir_read(dp, 1) + +static FRESULT dir_read( + DIR *dp, + int vol) +{ + FRESULT res = FR_NO_FILE; + FATFS *fs = dp->obj.fs; + BYTE attr, b; + + while (dp->sect) + { + res = move_window(fs, dp->sect); + if (res != FR_OK) + break; + b = dp->dir[DIR_Name]; + if (b == 0) + { + res = FR_NO_FILE; + break; + } + { + dp->obj.attr = attr = dp->dir[DIR_Attr] & AM_MASK; + if (b != DDEM && b != '.' && attr != AM_LFN && (int)((attr & ~AM_ARC) == AM_VOL) == vol) + { + break; + } + } + res = dir_next(dp, 0); + if (res != FR_OK) + break; + } + + if (res != FR_OK) + dp->sect = 0; + return res; +} + +#endif + +static FRESULT dir_find( + DIR *dp) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; + BYTE c; + + res = dir_sdi(dp, 0); + if (res != FR_OK) + return res; + do + { + res = move_window(fs, dp->sect); + if (res != FR_OK) + break; + c = dp->dir[DIR_Name]; + if (c == 0) + { + res = FR_NO_FILE; + break; + } + dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK; + if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) + break; + res = dir_next(dp, 0); + } while (res == FR_OK); + + return res; +} + +#if !FF_FS_READONLY + +static FRESULT dir_register( + DIR *dp) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; + + res = dir_alloc(dp, 1); + + if (res == FR_OK) + { + res = move_window(fs, dp->sect); + if (res == FR_OK) + { + mem_set(dp->dir, 0, SZDIRE); + mem_cpy(dp->dir + DIR_Name, dp->fn, 11); + fs->wflag = 1; + } + } + + return res; +} + +#endif + +#if !FF_FS_READONLY && FF_FS_MINIMIZE == 0 + +static FRESULT dir_remove( + DIR *dp) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; + + res = move_window(fs, dp->sect); + if (res == FR_OK) + { + dp->dir[DIR_Name] = DDEM; + fs->wflag = 1; + } + + return res; +} + +#endif + +#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 + +static void get_fileinfo( + DIR *dp, + FILINFO *fno) +{ + UINT si, di; + TCHAR c; + + fno->fname[0] = 0; + if (dp->sect == 0) + return; + + si = di = 0; + while (si < 11) + { + c = (TCHAR)dp->dir[si++]; + if (c == ' ') + continue; + if (c == RDDEM) + c = DDEM; + if (si == 9) + fno->fname[di++] = '.'; + fno->fname[di++] = c; + } + fno->fname[di] = 0; + + fno->fattrib = dp->dir[DIR_Attr]; + fno->fsize = ld_dword(dp->dir + DIR_FileSize); + fno->ftime = ld_word(dp->dir + DIR_ModTime + 0); + fno->fdate = ld_word(dp->dir + DIR_ModTime + 2); +} + +#endif + +static FRESULT create_name( + DIR *dp, + const TCHAR **path) +{ + BYTE c, d, *sfn; + UINT ni, si, i; + const char *p; + + p = *path; + sfn = dp->fn; + mem_set(sfn, ' ', 11); + si = i = 0; + ni = 8; + for (;;) + { + c = (BYTE)p[si++]; + if (c <= ' ') + break; + if (c == '/' || c == '\\') + { + while (p[si] == '/' || p[si] == '\\') + si++; + break; + } + if (c == '.' || i >= ni) + { + if (ni == 11 || c != '.') + return FR_INVALID_NAME; + i = 8; + ni = 11; + continue; + } + if (dbc_1st(c)) + { + d = (BYTE)p[si++]; + if (!dbc_2nd(d) || i >= ni - 1) + return FR_INVALID_NAME; + sfn[i++] = c; + sfn[i++] = d; + } + else + { + if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) + return FR_INVALID_NAME; + if (IsLower(c)) + c -= 0x20; + sfn[i++] = c; + } + } + *path = p + si; + if (i == 0) + return FR_INVALID_NAME; + + if (sfn[0] == DDEM) + sfn[0] = RDDEM; + sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0; + + return FR_OK; +} + +static FRESULT follow_path( + DIR *dp, + const TCHAR *path) +{ + FRESULT res; + BYTE ns; + FATFS *fs = dp->obj.fs; + { + while (*path == '/' || *path == '\\') + path++; + dp->obj.sclust = 0; + } + + if ((UINT)*path < ' ') + { + dp->fn[NSFLAG] = NS_NONAME; + res = dir_sdi(dp, 0); + } + else + { + for (;;) + { + res = create_name(dp, &path); + if (res != FR_OK) + break; + res = dir_find(dp); + ns = dp->fn[NSFLAG]; + if (res != FR_OK) + { + if (res == FR_NO_FILE) + { + if (FF_FS_RPATH && (ns & NS_DOT)) + { + if (!(ns & NS_LAST)) + continue; + dp->fn[NSFLAG] = NS_NONAME; + res = FR_OK; + } + else + { + if (!(ns & NS_LAST)) + res = FR_NO_PATH; + } + } + break; + } + if (ns & NS_LAST) + break; + + if (!(dp->obj.attr & AM_DIR)) + { + res = FR_NO_PATH; + break; + } + { + dp->obj.sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs)); + } + } + } + + return res; +} + +static int get_ldnumber( + const TCHAR **path) +{ + const TCHAR *tp, *tt; + TCHAR tc; + int i, vol = -1; + + tt = tp = *path; + if (!tp) + return vol; + do + tc = *tt++; + while ((UINT)tc >= (FF_USE_LFN ? ' ' : '!') && tc != ':'); + + if (tc == ':') + { + i = FF_VOLUMES; + if (IsDigit(*tp) && tp + 2 == tt) + { + i = (int)*tp - '0'; + } + if (i < FF_VOLUMES) + { + vol = i; + *path = tt; + } + return vol; + } + vol = 0; + return vol; +} + +static UINT check_fs( + FATFS *fs, + LBA_t sect) +{ + fs->wflag = 0; + fs->winsect = (LBA_t)0 - 1; + if (move_window(fs, sect) != FR_OK) + return 4; + + if (ld_word(fs->win + BS_55AA) != 0xAA55) + return 3; + + if (FF_FS_EXFAT && !mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" + "EXFAT ", + 11)) + return 1; + + if (fs->win[BS_JmpBoot] == 0xE9 || fs->win[BS_JmpBoot] == 0xEB || fs->win[BS_JmpBoot] == 0xE8) + { + if (!mem_cmp(fs->win + BS_FilSysType, "FAT", 3)) + return 0; + if (!mem_cmp(fs->win + BS_FilSysType32, "FAT32", 5)) + return 0; + } + return 2; +} + +static UINT find_volume( + FATFS *fs, + UINT part) +{ + UINT fmt, i; + DWORD mbr_pt[4]; + + fmt = check_fs(fs, 0); + if (fmt != 2 && (fmt >= 3 || part == 0)) + return fmt; + + if (FF_MULTI_PARTITION && part > 4) + return 3; + for (i = 0; i < 4; i++) + { + mbr_pt[i] = ld_dword(fs->win + MBR_Table + i * SZ_PTE + PTE_StLba); + } + i = part ? part - 1 : 0; + do + { + fmt = mbr_pt[i] ? check_fs(fs, mbr_pt[i]) : 3; + } while (part == 0 && fmt >= 2 && ++i < 4); + return fmt; +} + +static FRESULT mount_volume( + const TCHAR **path, + FATFS **rfs, + BYTE mode) +{ + int vol; + DSTATUS stat; + LBA_t bsect; + DWORD tsect, sysect, fasize, nclst, szbfat; + WORD nrsv; + FATFS *fs; + UINT fmt; + + *rfs = 0; + vol = get_ldnumber(path); + if (vol < 0) + return FR_INVALID_DRIVE; + + fs = FatFs[vol]; + if (!fs) + return FR_NOT_ENABLED; + *rfs = fs; + + mode &= (BYTE)~FA_READ; + if (fs->fs_type != 0) + { + stat = disk_status(fs->pdrv); + if (!(stat & STA_NOINIT)) + { + if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) + { + return FR_WRITE_PROTECTED; + } + return FR_OK; + } + } + + fs->fs_type = 0; + fs->pdrv = LD2PD(vol); + stat = disk_initialize(fs->pdrv); + if (stat & STA_NOINIT) + { + return FR_NOT_READY; + } + if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) + { + return FR_WRITE_PROTECTED; + } + + fmt = find_volume(fs, LD2PT(vol)); + if (fmt == 4) + return FR_DISK_ERR; + if (fmt >= 2) + return FR_NO_FILESYSTEM; + bsect = fs->winsect; + { + if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) + return FR_NO_FILESYSTEM; + + fasize = ld_word(fs->win + BPB_FATSz16); + if (fasize == 0) + fasize = ld_dword(fs->win + BPB_FATSz32); + fs->fsize = fasize; + + fs->n_fats = fs->win[BPB_NumFATs]; + if (fs->n_fats != 1 && fs->n_fats != 2) + return FR_NO_FILESYSTEM; + fasize *= fs->n_fats; + + fs->csize = fs->win[BPB_SecPerClus]; + if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) + return FR_NO_FILESYSTEM; + + fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); + if (fs->n_rootdir % (SS(fs) / SZDIRE)) + return FR_NO_FILESYSTEM; + + tsect = ld_word(fs->win + BPB_TotSec16); + if (tsect == 0) + tsect = ld_dword(fs->win + BPB_TotSec32); + + nrsv = ld_word(fs->win + BPB_RsvdSecCnt); + if (nrsv == 0) + return FR_NO_FILESYSTEM; + + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); + if (tsect < sysect) + return FR_NO_FILESYSTEM; + nclst = (tsect - sysect) / fs->csize; + if (nclst == 0) + return FR_NO_FILESYSTEM; + fmt = 0; + if (nclst <= MAX_FAT32) + fmt = FS_FAT32; + if (nclst <= MAX_FAT16) + fmt = FS_FAT16; + if (nclst <= MAX_FAT12) + fmt = FS_FAT12; + if (fmt == 0) + return FR_NO_FILESYSTEM; + + fs->n_fatent = nclst + 2; + fs->volbase = bsect; + fs->fatbase = bsect + nrsv; + fs->database = bsect + sysect; + if (fmt == FS_FAT32) + { + if (ld_word(fs->win + BPB_FSVer32) != 0) + return FR_NO_FILESYSTEM; + if (fs->n_rootdir != 0) + return FR_NO_FILESYSTEM; + fs->dirbase = ld_dword(fs->win + BPB_RootClus32); + szbfat = fs->n_fatent * 4; + } + else + { + if (fs->n_rootdir == 0) + return FR_NO_FILESYSTEM; + fs->dirbase = fs->fatbase + fasize; + szbfat = (fmt == FS_FAT16) ? fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); + } + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) + return FR_NO_FILESYSTEM; + +#if !FF_FS_READONLY + + fs->last_clst = fs->free_clst = 0xFFFFFFFF; + fs->fsi_flag = 0x80; +#if (FF_FS_NOFSINFO & 3) != 3 + if (fmt == FS_FAT32 && ld_word(fs->win + BPB_FSInfo32) == 1 && move_window(fs, bsect + 1) == FR_OK) + { + fs->fsi_flag = 0; + if (ld_word(fs->win + BS_55AA) == 0xAA55 && ld_dword(fs->win + FSI_LeadSig) == 0x41615252 && ld_dword(fs->win + FSI_StrucSig) == 0x61417272) + { +#if (FF_FS_NOFSINFO & 1) == 0 + fs->free_clst = ld_dword(fs->win + FSI_Free_Count); +#endif +#if (FF_FS_NOFSINFO & 2) == 0 + fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free); +#endif + } + } +#endif +#endif + } + + fs->fs_type = (BYTE)fmt; + fs->id = ++Fsid; + return FR_OK; +} + +static FRESULT validate( + FFOBJID *obj, + FATFS **rfs) +{ + FRESULT res = FR_INVALID_OBJECT; + + if (obj && obj->fs && obj->fs->fs_type && obj->id == obj->fs->id) + { + if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) + { + res = FR_OK; + } + } + *rfs = (res == FR_OK) ? obj->fs : 0; + return res; +} + +FRESULT f_mount( + FATFS *fs, + const TCHAR *path, + BYTE opt) +{ + FATFS *cfs; + int vol; + FRESULT res; + const TCHAR *rp = path; + + vol = get_ldnumber(&rp); + if (vol < 0) + return FR_INVALID_DRIVE; + cfs = FatFs[vol]; + + if (cfs) + { + cfs->fs_type = 0; + } + + if (fs) + { + fs->fs_type = 0; + } + FatFs[vol] = fs; + + if (opt == 0) + return FR_OK; + + res = mount_volume(&path, &fs, 0); + LEAVE_FF(fs, res); +} + +FRESULT f_open( + FIL *fp, + const TCHAR *path, + BYTE mode) +{ + FRESULT res; + DIR dj; + FATFS *fs; +#if !FF_FS_READONLY + DWORD cl, bcs, clst; + LBA_t sc; + FSIZE_t ofs; +#endif + DEF_NAMBUF + + if (!fp) + return FR_INVALID_OBJECT; + + mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND; + res = mount_volume(&path, &fs, mode); + if (res == FR_OK) + { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); + if (res == FR_OK) + { + if (dj.fn[NSFLAG] & NS_NONAME) + { + res = FR_INVALID_NAME; + } + } + + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) + { + if (res != FR_OK) + { + if (res == FR_NO_FILE) + { + res = dir_register(&dj); + } + mode |= FA_CREATE_ALWAYS; + } + else + { + if (dj.obj.attr & (AM_RDO | AM_DIR)) + { + res = FR_DENIED; + } + else + { + if (mode & FA_CREATE_NEW) + res = FR_EXIST; + } + } + if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) + { + { + + cl = ld_clust(fs, dj.dir); + st_dword(dj.dir + DIR_CrtTime, GET_FATTIME()); + dj.dir[DIR_Attr] = AM_ARC; + st_clust(fs, dj.dir, 0); + st_dword(dj.dir + DIR_FileSize, 0); + fs->wflag = 1; + if (cl != 0) + { + sc = fs->winsect; + res = remove_chain(&dj.obj, cl, 0); + if (res == FR_OK) + { + res = move_window(fs, sc); + fs->last_clst = cl - 1; + } + } + } + } + } + else + { + if (res == FR_OK) + { + if (dj.obj.attr & AM_DIR) + { + res = FR_NO_FILE; + } + else + { + if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) + { + res = FR_DENIED; + } + } + } + } + if (res == FR_OK) + { + if (mode & FA_CREATE_ALWAYS) + mode |= FA_MODIFIED; + fp->dir_sect = fs->winsect; + fp->dir_ptr = dj.dir; + } + + if (res == FR_OK) + { + { + fp->obj.sclust = ld_clust(fs, dj.dir); + fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize); + } + fp->obj.fs = fs; + fp->obj.id = fs->id; + fp->flag = mode; + fp->err = 0; + fp->sect = 0; + fp->fptr = 0; +#if !FF_FS_READONLY + if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) + { + fp->fptr = fp->obj.objsize; + bcs = (DWORD)fs->csize * SS(fs); + clst = fp->obj.sclust; + for (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) + { + clst = get_fat(&fp->obj, clst); + if (clst <= 1) + res = FR_INT_ERR; + if (clst == 0xFFFFFFFF) + res = FR_DISK_ERR; + } + fp->clust = clst; + if (res == FR_OK && ofs % SS(fs)) + { + sc = clst2sect(fs, clst); + if (sc == 0) + { + res = FR_INT_ERR; + } + else + { + fp->sect = sc + (DWORD)(ofs / SS(fs)); + } + } + } +#endif + } + + FREE_NAMBUF(); + } + + if (res != FR_OK) + fp->obj.fs = 0; + + LEAVE_FF(fs, res); +} + +FRESULT f_read( + FIL *fp, + void *buff, + UINT btr, + UINT *br) +{ + FRESULT res; + FATFS *fs; + DWORD clst; + LBA_t sect; + FSIZE_t remain; + UINT rcnt, cc, csect; + BYTE *rbuff = (BYTE *)buff; + + *br = 0; + res = validate(&fp->obj, &fs); + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) + LEAVE_FF(fs, res); + if (!(fp->flag & FA_READ)) + LEAVE_FF(fs, FR_DENIED); + remain = fp->obj.objsize - fp->fptr; + if (btr > remain) + btr = (UINT)remain; + + for (; btr; + btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) + { + if (fp->fptr % SS(fs) == 0) + { + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); + if (csect == 0) + { + if (fp->fptr == 0) + { + clst = fp->obj.sclust; + } + else + { + { + clst = get_fat(&fp->obj, fp->clust); + } + } + if (clst < 2) + ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) + ABORT(fs, FR_DISK_ERR); + fp->clust = clst; + } + sect = clst2sect(fs, fp->clust); + if (sect == 0) + ABORT(fs, FR_INT_ERR); + sect += csect; + cc = btr / SS(fs); + if (cc > 0) + { + if (csect + cc > fs->csize) + { + cc = fs->csize - csect; + } + if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) + ABORT(fs, FR_DISK_ERR); +#if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 + if (fs->wflag && fs->winsect - sect < cc) + { + mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs)); + } +#endif + rcnt = SS(fs) * cc; + continue; + } + fp->sect = sect; + } + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); + if (rcnt > btr) + rcnt = btr; + if (move_window(fs, fp->sect) != FR_OK) + ABORT(fs, FR_DISK_ERR); + mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); + } + + LEAVE_FF(fs, FR_OK); +} + +#if !FF_FS_READONLY + +FRESULT f_write( + FIL *fp, + const void *buff, + UINT btw, + UINT *bw) +{ + FRESULT res; + FATFS *fs; + DWORD clst; + LBA_t sect; + UINT wcnt, cc, csect; + const BYTE *wbuff = (const BYTE *)buff; + + *bw = 0; + res = validate(&fp->obj, &fs); + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) + LEAVE_FF(fs, res); + if (!(fp->flag & FA_WRITE)) + LEAVE_FF(fs, FR_DENIED); + + if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) + { + btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); + } + + for (; btw; + btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize) + { + if (fp->fptr % SS(fs) == 0) + { + csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); + if (csect == 0) + { + if (fp->fptr == 0) + { + clst = fp->obj.sclust; + if (clst == 0) + { + clst = create_chain(&fp->obj, 0); + } + } + else + { + { + clst = create_chain(&fp->obj, fp->clust); + } + } + if (clst == 0) + break; + if (clst == 1) + ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) + ABORT(fs, FR_DISK_ERR); + fp->clust = clst; + if (fp->obj.sclust == 0) + fp->obj.sclust = clst; + } + if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) + ABORT(fs, FR_DISK_ERR); + sect = clst2sect(fs, fp->clust); + if (sect == 0) + ABORT(fs, FR_INT_ERR); + sect += csect; + cc = btw / SS(fs); + if (cc > 0) + { + if (csect + cc > fs->csize) + { + cc = fs->csize - csect; + } + if (disk_write(fs->pdrv, wbuff, sect, cc) != RES_OK) + ABORT(fs, FR_DISK_ERR); +#if FF_FS_MINIMIZE <= 2 + if (fs->winsect - sect < cc) + { + mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs)); + fs->wflag = 0; + } +#endif + wcnt = SS(fs) * cc; + continue; + } + if (fp->fptr >= fp->obj.objsize) + { + if (sync_window(fs) != FR_OK) + ABORT(fs, FR_DISK_ERR); + fs->winsect = sect; + } + fp->sect = sect; + } + wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); + if (wcnt > btw) + wcnt = btw; + if (move_window(fs, fp->sect) != FR_OK) + ABORT(fs, FR_DISK_ERR); + mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); + fs->wflag = 1; + } + + fp->flag |= FA_MODIFIED; + + LEAVE_FF(fs, FR_OK); +} + +FRESULT f_sync( + FIL *fp) +{ + FRESULT res; + FATFS *fs; + DWORD tm; + BYTE *dir; + + res = validate(&fp->obj, &fs); + if (res == FR_OK) + { + if (fp->flag & FA_MODIFIED) + { + + tm = GET_FATTIME(); + { + res = move_window(fs, fp->dir_sect); + if (res == FR_OK) + { + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; + st_clust(fp->obj.fs, dir, fp->obj.sclust); + st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize); + st_dword(dir + DIR_ModTime, tm); + st_word(dir + DIR_LstAccDate, 0); + fs->wflag = 1; + res = sync_fs(fs); + fp->flag &= (BYTE)~FA_MODIFIED; + } + } + } + } + + LEAVE_FF(fs, res); +} + +#endif + +FRESULT f_close( + FIL *fp) +{ + FRESULT res; + FATFS *fs; + +#if !FF_FS_READONLY + res = f_sync(fp); + if (res == FR_OK) +#endif + { + res = validate(&fp->obj, &fs); + if (res == FR_OK) + { + fp->obj.fs = 0; + } + } + return res; +} + +FRESULT f_stat( + const TCHAR *path, + FILINFO *fno) +{ + FRESULT res; + DIR dj; + DEF_NAMBUF + + res = mount_volume(&path, &dj.obj.fs, 0); + if (res == FR_OK) + { + INIT_NAMBUF(dj.obj.fs); + res = follow_path(&dj, path); + if (res == FR_OK) + { + if (dj.fn[NSFLAG] & NS_NONAME) + { + res = FR_INVALID_NAME; + } + else + { + if (fno) + get_fileinfo(&dj, fno); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(dj.obj.fs, res); +} + +FRESULT f_unlink( + const TCHAR *path) +{ + FRESULT res; + DIR dj, sdj; + DWORD dclst = 0; + FATFS *fs; + DEF_NAMBUF + + res = mount_volume(&path, &fs, FA_WRITE); + if (res == FR_OK) + { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); + if (FF_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) + { + res = FR_INVALID_NAME; + } + if (res == FR_OK) + { + if (dj.fn[NSFLAG] & NS_NONAME) + { + res = FR_INVALID_NAME; + } + else + { + if (dj.obj.attr & AM_RDO) + { + res = FR_DENIED; + } + } + if (res == FR_OK) + { + { + dclst = ld_clust(fs, dj.dir); + } + if (dj.obj.attr & AM_DIR) + { + { + sdj.obj.fs = fs; + sdj.obj.sclust = dclst; + res = dir_sdi(&sdj, 0); + if (res == FR_OK) + { + res = DIR_READ_FILE(&sdj); + if (res == FR_OK) + res = FR_DENIED; + if (res == FR_NO_FILE) + res = FR_OK; + } + } + } + } + if (res == FR_OK) + { + res = dir_remove(&dj); + if (res == FR_OK && dclst != 0) + { + res = remove_chain(&dj.obj, dclst, 0); + } + if (res == FR_OK) + res = sync_fs(fs); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} \ No newline at end of file diff --git a/ff.h b/ff.h new file mode 100644 index 0000000..a5483ed --- /dev/null +++ b/ff.h @@ -0,0 +1,173 @@ +#ifndef FF_DEFINED +#define FF_DEFINED 86606 + +#include "ffconf.h" + +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) +#define FF_INTDEF 2 +#include +typedef unsigned int UINT; +typedef unsigned char BYTE; +typedef uint16_t WORD; +typedef uint32_t DWORD; +typedef uint64_t QWORD; +typedef WORD WCHAR; +#else +#define FF_INTDEF 1 +typedef unsigned int UINT; +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; +typedef WORD WCHAR; +#endif + +#ifndef _INC_TCHAR +#define _INC_TCHAR + +typedef char TCHAR; +#define _T(x) x +#define _TEXT(x) x + +#endif +typedef DWORD FSIZE_t; +typedef DWORD LBA_t; + +typedef struct +{ + BYTE fs_type; + BYTE pdrv; + BYTE n_fats; + BYTE wflag; + BYTE fsi_flag; + WORD id; + WORD n_rootdir; + WORD csize; +#if !FF_FS_READONLY + DWORD last_clst; + DWORD free_clst; +#endif + DWORD n_fatent; + DWORD fsize; + LBA_t volbase; + LBA_t fatbase; + LBA_t dirbase; + LBA_t database; + LBA_t winsect; + BYTE win[FF_MAX_SS]; +} FATFS; + +typedef struct +{ + FATFS *fs; + WORD id; + BYTE attr; + BYTE stat; + DWORD sclust; + FSIZE_t objsize; +} FFOBJID; + +typedef struct +{ + FFOBJID obj; + BYTE flag; + BYTE err; + FSIZE_t fptr; + DWORD clust; + LBA_t sect; +#if !FF_FS_READONLY + LBA_t dir_sect; + BYTE *dir_ptr; +#endif +} FIL; + +typedef struct +{ + FFOBJID obj; + DWORD dptr; + DWORD clust; + LBA_t sect; + BYTE *dir; + BYTE fn[12]; +} DIR; + +typedef struct +{ + FSIZE_t fsize; + WORD fdate; + WORD ftime; + BYTE fattrib; + TCHAR fname[12 + 1]; +} FILINFO; + +typedef struct +{ + BYTE fmt; + BYTE n_fat; + UINT align; + UINT n_root; + DWORD au_size; +} MKFS_PARM; + +typedef enum +{ + FR_OK = 0, + FR_DISK_ERR, + FR_INT_ERR, + FR_NOT_READY, + FR_NO_FILE, + FR_NO_PATH, + FR_INVALID_NAME, + FR_DENIED, + FR_EXIST, + FR_INVALID_OBJECT, + FR_WRITE_PROTECTED, + FR_INVALID_DRIVE, + FR_NOT_ENABLED, + FR_NO_FILESYSTEM, + FR_MKFS_ABORTED, + FR_TIMEOUT, + FR_LOCKED, + FR_NOT_ENOUGH_CORE, + FR_TOO_MANY_OPEN_FILES, + FR_INVALID_PARAMETER +} FRESULT; + +FRESULT f_open(FIL *fp, const TCHAR *path, BYTE mode); +FRESULT f_close(FIL *fp); +FRESULT f_read(FIL *fp, void *buff, UINT btr, UINT *br); +FRESULT f_write(FIL *fp, const void *buff, UINT btw, UINT *bw); +FRESULT f_sync(FIL *fp); +FRESULT f_unlink(const TCHAR *path); +FRESULT f_stat(const TCHAR *path, FILINFO *fno); +FRESULT f_mount(FATFS *fs, const TCHAR *path, BYTE opt); + +#define f_size(fp) ((fp)->obj.objsize) + +#define FA_READ 0x01 +#define FA_WRITE 0x02 +#define FA_OPEN_EXISTING 0x00 +#define FA_CREATE_NEW 0x04 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA_OPEN_APPEND 0x30 + +#define CREATE_LINKMAP ((FSIZE_t)0 - 1) + +#define FM_FAT 0x01 +#define FM_FAT32 0x02 +#define FM_EXFAT 0x04 +#define FM_ANY 0x07 +#define FM_SFD 0x08 + +#define FS_FAT12 1 +#define FS_FAT16 2 +#define FS_FAT32 3 +#define FS_EXFAT 4 + +#define AM_RDO 0x01 +#define AM_HID 0x02 +#define AM_SYS 0x04 +#define AM_DIR 0x10 +#define AM_ARC 0x20 + +#endif \ No newline at end of file diff --git a/ffconf.h b/ffconf.h new file mode 100644 index 0000000..ffdcf00 --- /dev/null +++ b/ffconf.h @@ -0,0 +1,14 @@ +#define FFCONF_DEF 86606 +#define FF_FS_READONLY 0 +#define FF_FS_MINIMIZE 0 +#define FF_USE_LABEL 0 +#define FF_CODE_PAGE 932 +#define FF_USE_LFN 0 +#define FF_FS_RPATH 0 +#define FF_VOLUMES 1 +#define FF_MULTI_PARTITION 0 +#define FF_MAX_SS 512 +#define FF_FS_EXFAT 0 +#define FF_NORTC_MON 1 +#define FF_NORTC_MDAY 1 +#define FF_NORTC_YEAR 2019 \ No newline at end of file diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e608636d9377a092c16fa2f3a570d602a5175600 GIT binary patch literal 85960 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 +} + +//% +bool _file(String s, String v, uint8_t x) +{ + FATFS FatFs; + f_mount(&FatFs, "", 0); + FIL Fil; + UINT bw; + FRESULT fr; + fr = f_open(&Fil, (const char *)s->getUTF8Data(), x); + if (fr == FR_OK) + { + f_write(&Fil, (const char *)v->getUTF8Data(), v->getUTF8Size(), &bw); + fr = f_close(&Fil); + if (fr == FR_OK && bw == v->getUTF8Size()) + { + return true; + } + return false; + } + return false; +} + +//% +uint32_t _size(String s) +{ + FATFS FatFs; + f_mount(&FatFs, "", 0); + uint32_t lSize = 0; + FIL Fil; + FRESULT fr; + fr = f_open(&Fil, (const char *)s->getUTF8Data(), FA_READ | FA_WRITE); + lSize = f_size(&Fil); + f_close(&Fil); + return lSize; +} + +//% +bool _exists(String s) +{ + FATFS FatFs; + f_mount(&FatFs, "", 0); + FRESULT fr; + FILINFO fno; + fr = f_stat((const char *)s->getUTF8Data(), &fno); + if (fr == FR_OK) + return true; + return false; +} + +//% +String _read(String s) +{ +} +} // namespace cs11 \ No newline at end of file diff --git a/im01.ts b/im01.ts new file mode 100644 index 0000000..42b9944 --- /dev/null +++ b/im01.ts @@ -0,0 +1,131 @@ +//%color=#444444 icon="\uf07b" +namespace IM01 { + let sdFlag = false + //%block="IM01 size of file %u" + //%u.defl="log.txt" + function sizeOfFile(u: string): number { + u = truncateStringLength(u) + if (sdFlag == false) { + createFolder("CS11") + sdFlag = true + } + return size(u) + } + + //%block="IM01 remove file" + //%u.defl="log.txt" + export function removeFile(u: string): void { + u = truncateStringLength(u) + if (sdFlag == false) { + createFolder("CS11") + sdFlag = true + } + remove(u) + return + } + + //%block="IM01 file %u exists" + //%u.defl="log.txt" + export function fileExists(u: string): boolean { + u = truncateStringLength(u) + if (sdFlag == false) { + createFolder("CS11") + sdFlag = true + } + return exists(u) + } + + //%block="IM01 overwrite file %u with %v" + //%u.defl="log.txt" + export function overwriteFile(u: string, v: string): void { + u = truncateStringLength(u) + if (sdFlag == false) { + createFolder("CS11") + sdFlag = true + } + file(u, v, 0x02 | 0x08) + return + } + + //%block="IM01 append file %u with %v" + //%u.defl="log.txt" + export function appendFile(u: string, v: string): void { + u = truncateStringLength(u) + if (sdFlag == false) { + createFolder("CS11") + sdFlag = true + } + file(u, v, 0x02 | 0x30) + return + } + + //%block="IM01 append file %u with line %v" + //%u.defl="log.txt" + export function appendFileLine(u: string, v: string): void { + u = truncateStringLength(u) + if (sdFlag == false) { + createFolder("CS11") + sdFlag = true + } + file(u, v + "\n", 0x02 | 0x30) + return + } + + //%block="IM01 read file %u" + //%u.defl="log.txt" + export function readFile(u: string): string { + u = truncateStringLength(u) + if (sdFlag == false) { + createFolder("CS11") + sdFlag = true + } + return file_read(u) + } + + //%block="IM01 create folder %u" + function createFolder(u: string): void { + mkdir(u) + return; + } + + //%shim=im01::_mkdir + function mkdir(u: string): void { + return + } + + //%shim=im01::_remove + function remove(u: string): void { + return + } + + //%shim=im01::_file + function file(u: string, v: string, x: number): boolean { + return true + } + + //%shim=im01::_size + function size(u: string): number { + return 1 + } + + //%shim=im01::_exists + function exists(u: string): boolean { + return true + } + + //%shim=im01::_read + function file_read(u: string): string { + return "" + } + + function truncateStringLength(u: string): string { + let i = u.indexOf(".") + let ext = u.substr(i, u.length) + if (i > 8) { + u = u.substr(0, 8) + ext + } + return u + } + + +} \ No newline at end of file diff --git a/main.blocks b/main.blocks deleted file mode 100644 index fd0869b..0000000 --- a/main.blocks +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/main.ts b/main.ts deleted file mode 100644 index ee54a1b..0000000 --- a/main.ts +++ /dev/null @@ -1,3 +0,0 @@ -basic.forever(function () { - -}) diff --git a/pxt.json b/pxt.json index 2163bd2..8f105e4 100644 --- a/pxt.json +++ b/pxt.json @@ -1,22 +1,32 @@ { - "name": "pxt-im01-2", - "version": "0.0.1", - "description": "", + "name": "CS11", + "version": "0.0.8", + "description": "XinaBox CS11 SD Card: SD Card functionality.", + "license": "MIT", "dependencies": { - "core": "*", - "radio": "*" + "core": "*" }, "files": [ "README.md", - "main.blocks", - "main.ts" + "main.ts", + "sdmm.cpp", + "im01.ts", + "im01.cpp", + "diskio.h", + "ff.cpp", + "ff.h", + "ffconf.h" ], "testFiles": [ "test.ts" ], "public": true, "supportedTargets": [ - "microbit" + "maker" ], - "preferredEditor": "tsprj" + "preferredEditor": "blocksprj", + "authors": [ + "Luqmaan Baboo", + "Bjarke Gotfredsen" + ] } diff --git a/sdmm.cpp b/sdmm.cpp new file mode 100644 index 0000000..5194ab0 --- /dev/null +++ b/sdmm.cpp @@ -0,0 +1,544 @@ +#include "ff.h" +#include "diskio.h" + +#include "pxt.h" + + +/* + MISO = P14 + MOSI = P15 + SCK = P13 + CS = P16 +*/ + + +void DO_INIT() +{ + + uBit.io.P14.setDigitalValue(0); +} + + +bool DO() +{ + + return uBit.io.P14.getDigitalValue(); +} + +void DI_INIT() +{ + + uBit.io.P15.setDigitalValue(0); +} + +void DI_H() +{ + + uBit.io.P15.setDigitalValue(1); +} +void DI_L() +{ + + uBit.io.P15.setDigitalValue(0); +} + +void CK_INIT() +{ + + uBit.io.P13.setDigitalValue(0); +} +void CK_H() +{ + + uBit.io.P13.setDigitalValue(1); +} +void CK_L() +{ + + uBit.io.P13.setDigitalValue(0); +} + +void CS_INIT() +{ + + uBit.io.P16.setDigitalValue(0); +} +void CS_H() +{ + + uBit.io.P16.setDigitalValue(1); +} +void CS_L() +{ + + uBit.io.P16.setDigitalValue(0); +} + +void dly_us(UINT n) +{ + sleep_us(n); +} + +#define CMD0 (0) +#define CMD1 (1) +#define ACMD41 (0x80 + 41) +#define CMD8 (8) +#define CMD9 (9) +#define CMD12 (12) +#define CMD16 (16) +#define CMD17 (17) +#define CMD18 (18) +#define ACMD23 (0x80 + 23) +#define CMD24 (24) +#define CMD25 (25) +#define CMD55 (55) +#define CMD58 (58) + +static DSTATUS Stat = STA_NOINIT; + +static BYTE CardType; + +static void xmit_mmc( + const BYTE *buff, + UINT bc) +{ + BYTE d; + + do + { + d = *buff++; + if (d & 0x80) + DI_H(); + else + DI_L(); + CK_H(); + CK_L(); + if (d & 0x40) + DI_H(); + else + DI_L(); + CK_H(); + CK_L(); + if (d & 0x20) + DI_H(); + else + DI_L(); + CK_H(); + CK_L(); + if (d & 0x10) + DI_H(); + else + DI_L(); + CK_H(); + CK_L(); + if (d & 0x08) + DI_H(); + else + DI_L(); + CK_H(); + CK_L(); + if (d & 0x04) + DI_H(); + else + DI_L(); + CK_H(); + CK_L(); + if (d & 0x02) + DI_H(); + else + DI_L(); + CK_H(); + CK_L(); + if (d & 0x01) + DI_H(); + else + DI_L(); + CK_H(); + CK_L(); + } while (--bc); +} + +static void rcvr_mmc( + BYTE *buff, + UINT bc) +{ + BYTE r; + + DI_H(); + + do + { + r = 0; + if (DO()) + r++; + CK_H(); + CK_L(); + r <<= 1; + if (DO()) + r++; + CK_H(); + CK_L(); + r <<= 1; + if (DO()) + r++; + CK_H(); + CK_L(); + r <<= 1; + if (DO()) + r++; + CK_H(); + CK_L(); + r <<= 1; + if (DO()) + r++; + CK_H(); + CK_L(); + r <<= 1; + if (DO()) + r++; + CK_H(); + CK_L(); + r <<= 1; + if (DO()) + r++; + CK_H(); + CK_L(); + r <<= 1; + if (DO()) + r++; + CK_H(); + CK_L(); + *buff++ = r; + } while (--bc); +} + +static int wait_ready(void) +{ + BYTE d; + UINT tmr; + + for (tmr = 5000; tmr; tmr--) + { + rcvr_mmc(&d, 1); + if (d == 0xFF) + break; + dly_us(100); + } + + return tmr ? 1 : 0; +} + +void deselect(void) +{ + BYTE d; + + CS_H(); + rcvr_mmc(&d, 1); +} + +static int select(void) +{ + BYTE d; + + CS_L(); + rcvr_mmc(&d, 1); + if (wait_ready()) + return 1; + + deselect(); + return 0; +} + +static int rcvr_datablock( + BYTE *buff, + UINT btr) +{ + BYTE d[2]; + UINT tmr; + + for (tmr = 1000; tmr; tmr--) + { + rcvr_mmc(d, 1); + if (d[0] != 0xFF) + break; + dly_us(100); + } + if (d[0] != 0xFE) + return 0; + + rcvr_mmc(buff, btr); + rcvr_mmc(d, 2); + + return 1; +} + +static int xmit_datablock( + const BYTE *buff, + BYTE token) +{ + BYTE d[2]; + + if (!wait_ready()) + return 0; + + d[0] = token; + xmit_mmc(d, 1); + if (token != 0xFD) + { + xmit_mmc(buff, 512); + rcvr_mmc(d, 2); + rcvr_mmc(d, 1); + if ((d[0] & 0x1F) != 0x05) + return 0; + } + + return 1; +} + +static BYTE send_cmd( + BYTE cmd, + DWORD arg) +{ + BYTE n, d, buf[6]; + + if (cmd & 0x80) + { + cmd &= 0x7F; + n = send_cmd(CMD55, 0); + if (n > 1) + return n; + } + + if (cmd != CMD12) + { + deselect(); + if (!select()) + return 0xFF; + } + + buf[0] = 0x40 | cmd; + buf[1] = (BYTE)(arg >> 24); + buf[2] = (BYTE)(arg >> 16); + buf[3] = (BYTE)(arg >> 8); + buf[4] = (BYTE)arg; + n = 0x01; + if (cmd == CMD0) + n = 0x95; + if (cmd == CMD8) + n = 0x87; + buf[5] = n; + xmit_mmc(buf, 6); + + if (cmd == CMD12) + rcvr_mmc(&d, 1); + n = 10; + do + rcvr_mmc(&d, 1); + while ((d & 0x80) && --n); + + return d; +} + +DSTATUS disk_status( + BYTE drv) +{ + if (drv) + return STA_NOINIT; + + return Stat; +} + +DSTATUS disk_initialize( + BYTE drv) +{ + BYTE n, ty, cmd, buf[4]; + UINT tmr; + DSTATUS s; + + if (drv) + return RES_NOTRDY; + + dly_us(10000); + CS_INIT(); + CS_H(); + CK_INIT(); + CK_L(); + DI_INIT(); + DO_INIT(); + + for (n = 10; n; n--) + rcvr_mmc(buf, 1); + + ty = 0; + if (send_cmd(CMD0, 0) == 1) + { + if (send_cmd(CMD8, 0x1AA) == 1) + { + rcvr_mmc(buf, 4); + if (buf[2] == 0x01 && buf[3] == 0xAA) + { + for (tmr = 1000; tmr; tmr--) + { + if (send_cmd(ACMD41, 1UL << 30) == 0) + break; + dly_us(1000); + } + if (tmr && send_cmd(CMD58, 0) == 0) + { + rcvr_mmc(buf, 4); + ty = (buf[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; + } + } + } + else + { + if (send_cmd(ACMD41, 0) <= 1) + { + ty = CT_SD1; + cmd = ACMD41; + } + else + { + ty = CT_MMC; + cmd = CMD1; + } + for (tmr = 1000; tmr; tmr--) + { + if (send_cmd(cmd, 0) == 0) + break; + dly_us(1000); + } + if (!tmr || send_cmd(CMD16, 512) != 0) + ty = 0; + } + } + CardType = ty; + s = ty ? 0 : STA_NOINIT; + Stat = s; + + deselect(); + + return s; +} + +DRESULT disk_read( + BYTE drv, + BYTE *buff, + LBA_t sector, + UINT count) +{ + BYTE cmd; + DWORD sect = (DWORD)sector; + + if (disk_status(drv) & STA_NOINIT) + return RES_NOTRDY; + if (!(CardType & CT_BLOCK)) + sect *= 512; + + cmd = count > 1 ? CMD18 : CMD17; + if (send_cmd(cmd, sect) == 0) + { + do + { + if (!rcvr_datablock(buff, 512)) + break; + buff += 512; + } while (--count); + if (cmd == CMD18) + send_cmd(CMD12, 0); + } + deselect(); + + return count ? RES_ERROR : RES_OK; +} + +DRESULT disk_write( + BYTE drv, + const BYTE *buff, + LBA_t sector, + UINT count) +{ + DWORD sect = (DWORD)sector; + + if (disk_status(drv) & STA_NOINIT) + return RES_NOTRDY; + if (!(CardType & CT_BLOCK)) + sect *= 512; + + if (count == 1) + { + if ((send_cmd(CMD24, sect) == 0) && xmit_datablock(buff, 0xFE)) + count = 0; + } + else + { + if (CardType & CT_SDC) + send_cmd(ACMD23, count); + if (send_cmd(CMD25, sect) == 0) + { + do + { + if (!xmit_datablock(buff, 0xFC)) + break; + buff += 512; + } while (--count); + if (!xmit_datablock(0, 0xFD)) + count = 1; + } + } + deselect(); + + return count ? RES_ERROR : RES_OK; +} + +DRESULT disk_ioctl( + BYTE drv, + BYTE ctrl, + void *buff) +{ + DRESULT res; + BYTE n, csd[16]; + DWORD cs; + + if (disk_status(drv) & STA_NOINIT) + return RES_NOTRDY; + + res = RES_ERROR; + switch (ctrl) + { + case CTRL_SYNC: + if (select()) + res = RES_OK; + break; + + case GET_SECTOR_COUNT: + if ((send_cmd(CMD9, 0) == 0) && rcvr_datablock(csd, 16)) + { + if ((csd[0] >> 6) == 1) + { + cs = csd[9] + ((WORD)csd[8] << 8) + ((DWORD)(csd[7] & 63) << 16) + 1; + *(LBA_t *)buff = cs << 10; + } + else + { + n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2; + cs = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1; + *(LBA_t *)buff = cs << (n - 9); + } + res = RES_OK; + } + break; + + case GET_BLOCK_SIZE: + *(DWORD *)buff = 128; + res = RES_OK; + break; + + default: + res = RES_PARERR; + } + + deselect(); + + return res; +}