diskio_spitf.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. /*-----------------------------------------------------------------------*/
  2. /* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */
  3. /*-----------------------------------------------------------------------*/
  4. /* If a working storage control module is available, it should be */
  5. /* attached to the FatFs via a glue function rather than modifying it. */
  6. /* This is an example of glue functions to attach various exsisting */
  7. /* storage control modules to the FatFs module with a defined API. */
  8. /*-----------------------------------------------------------------------*/
  9. #include "luat_base.h"
  10. #include "luat_spi.h"
  11. #include "luat_timer.h"
  12. #include "luat_gpio.h"
  13. #include "lauxlib.h"
  14. #include "ff.h" /* Obtains integer types */
  15. #include "diskio.h" /* Declarations of disk functions */
  16. #define LUAT_LOG_TAG "luat.fatfs"
  17. #include "luat_log.h"
  18. // 与 diskio_spitf.c 对齐
  19. extern BYTE FATFS_DEBUG; // debug log, 0 -- disable , 1 -- enable
  20. /*--------------------------------------------------------------------------
  21. Module Private Functions
  22. ---------------------------------------------------------------------------*/
  23. /* MMC/SD command (SPI mode) */
  24. #define CMD0 (0) /* GO_IDLE_STATE */
  25. #define CMD1 (1) /* SEND_OP_COND */
  26. #define ACMD41 (0x80+41) /* SEND_OP_COND (SDC) */
  27. #define CMD8 (8) /* SEND_IF_COND */
  28. #define CMD9 (9) /* SEND_CSD */
  29. #define CMD10 (10) /* SEND_CID */
  30. #define CMD12 (12) /* STOP_TRANSMISSION */
  31. #define CMD13 (13) /* SEND_STATUS */
  32. #define ACMD13 (0x80+13) /* SD_STATUS (SDC) */
  33. #define CMD16 (16) /* SET_BLOCKLEN */
  34. #define CMD17 (17) /* READ_SINGLE_BLOCK */
  35. #define CMD18 (18) /* READ_MULTIPLE_BLOCK */
  36. #define CMD23 (23) /* SET_BLOCK_COUNT */
  37. #define ACMD23 (0x80+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
  38. #define CMD24 (24) /* WRITE_BLOCK */
  39. #define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */
  40. #define CMD32 (32) /* ERASE_ER_BLK_START */
  41. #define CMD33 (33) /* ERASE_ER_BLK_END */
  42. #define CMD38 (38) /* ERASE */
  43. #define CMD55 (55) /* APP_CMD */
  44. #define CMD58 (58) /* READ_OCR */
  45. static
  46. DSTATUS Stat = STA_NOINIT; /* Disk status */
  47. static
  48. BYTE CardType; /* b0:MMC, b1:SDv1, b2:SDv2, b3:Block addressing */
  49. // static void dly_us(BYTE us) {
  50. // if (us < 1) {
  51. // return;
  52. // }
  53. // us += 999;
  54. // luat_timer_mdelay(us/1000);
  55. // }
  56. #define dly_us luat_timer_us_delay
  57. /*-----------------------------------------------------------------------*/
  58. /* Transmit bytes to the card (bitbanging) */
  59. /*-----------------------------------------------------------------------*/
  60. static
  61. void xmit_mmc (
  62. const BYTE* buff, /* Data to be sent */
  63. UINT bc, /* Number of bytes to send */
  64. luat_fatfs_spi_t* userdata
  65. )
  66. {
  67. #if 0
  68. BYTE d;
  69. do {
  70. d = *buff++; /* Get a byte to be sent */
  71. if (d & 0x80) DI_H(); else DI_L(); /* bit7 */
  72. CK_H(); CK_L();
  73. if (d & 0x40) DI_H(); else DI_L(); /* bit6 */
  74. CK_H(); CK_L();
  75. if (d & 0x20) DI_H(); else DI_L(); /* bit5 */
  76. CK_H(); CK_L();
  77. if (d & 0x10) DI_H(); else DI_L(); /* bit4 */
  78. CK_H(); CK_L();
  79. if (d & 0x08) DI_H(); else DI_L(); /* bit3 */
  80. CK_H(); CK_L();
  81. if (d & 0x04) DI_H(); else DI_L(); /* bit2 */
  82. CK_H(); CK_L();
  83. if (d & 0x02) DI_H(); else DI_L(); /* bit1 */
  84. CK_H(); CK_L();
  85. if (d & 0x01) DI_H(); else DI_L(); /* bit0 */
  86. CK_H(); CK_L();
  87. } while (--bc);
  88. #endif
  89. if (FATFS_DEBUG)
  90. LLOGD("[FatFS]xmit_mmc bc=%d\r\n", bc);
  91. if(userdata->type == 1){
  92. luat_spi_device_send(userdata->spi_device, (char*)buff, bc);
  93. }else{
  94. luat_spi_send(userdata->spi_id, (const char*)buff, bc);
  95. }
  96. }
  97. /*-----------------------------------------------------------------------*/
  98. /* Receive bytes from the card (bitbanging) */
  99. /*-----------------------------------------------------------------------*/
  100. static
  101. void rcvr_mmc (
  102. BYTE *buff, /* Pointer to read buffer */
  103. UINT bc, /* Number of bytes to receive */
  104. luat_fatfs_spi_t* userdata
  105. )
  106. {
  107. #if 0
  108. BYTE r;
  109. DI_H(); /* Send 0xFF */
  110. do {
  111. r = 0; if (DO) r++; /* bit7 */
  112. CK_H(); CK_L();
  113. r <<= 1; if (DO) r++; /* bit6 */
  114. CK_H(); CK_L();
  115. r <<= 1; if (DO) r++; /* bit5 */
  116. CK_H(); CK_L();
  117. r <<= 1; if (DO) r++; /* bit4 */
  118. CK_H(); CK_L();
  119. r <<= 1; if (DO) r++; /* bit3 */
  120. CK_H(); CK_L();
  121. r <<= 1; if (DO) r++; /* bit2 */
  122. CK_H(); CK_L();
  123. r <<= 1; if (DO) r++; /* bit1 */
  124. CK_H(); CK_L();
  125. r <<= 1; if (DO) r++; /* bit0 */
  126. CK_H(); CK_L();
  127. *buff++ = r; /* Store a received byte */
  128. } while (--bc);
  129. #endif
  130. //u8* buf2 = 0x00;
  131. //u8** buf = &buf2;
  132. // BYTE tmp[bc];
  133. // for(size_t i = 0; i < bc; i++)
  134. // {
  135. // tmp[i] = 0xFF;
  136. // }
  137. //DWORD t = luat_spi_transfer(userdata->spi_id, tmp, buff, bc);
  138. //s32 t = platform_spi_recv(0, buf, bc);
  139. if(userdata->type == 1){
  140. luat_spi_device_recv(userdata->spi_device, (char*)buff, bc);
  141. }else{
  142. luat_spi_recv(userdata->spi_id, (char*)buff, bc);
  143. }
  144. //memcpy(buff, buf2, bc);
  145. //if (FATFS_DEBUG)
  146. // LLOGD("[FatFS]rcvr_mmc first resp byte=%02X, t=%d\r\n", buf2[0], t);
  147. //free(buf2);
  148. }
  149. /*-----------------------------------------------------------------------*/
  150. /* Wait for card ready */
  151. /*-----------------------------------------------------------------------*/
  152. static
  153. int wait_ready (luat_fatfs_spi_t* userdata) /* 1:OK, 0:Timeout */
  154. {
  155. BYTE d;
  156. UINT tmr;
  157. for (tmr = 5000; tmr; tmr--) { /* Wait for ready in timeout of 500ms */
  158. rcvr_mmc(&d, 1, userdata);
  159. if (d == 0xFF) break;
  160. dly_us(100);
  161. }
  162. return tmr ? 1 : 0;
  163. }
  164. /*-----------------------------------------------------------------------*/
  165. /* Deselect the card and release SPI bus */
  166. /*-----------------------------------------------------------------------*/
  167. static
  168. void spi_cs_deselect (luat_fatfs_spi_t* userdata)
  169. {
  170. BYTE d;
  171. //CS_H(); /* Set CS# high */
  172. //platform_pio_op(0, 1 << userdata->spi_cs, 0);
  173. if(userdata->type == 1){
  174. // rcvr_mmc(&d, 1);
  175. }else{
  176. luat_gpio_set(userdata->spi_cs, 1);
  177. rcvr_mmc(&d, 1, userdata); /* Dummy clock (force DO hi-z for multiple slave SPI) */
  178. }
  179. }
  180. /*-----------------------------------------------------------------------*/
  181. /* Select the card and wait for ready */
  182. /*-----------------------------------------------------------------------*/
  183. static
  184. int spi_cs_select (luat_fatfs_spi_t* userdata) /* 1:OK, 0:Timeout */
  185. {
  186. BYTE d;
  187. //CS_L(); /* Set CS# low */
  188. //platform_pio_op(0, 1 << userdata->spi_cs, 1);
  189. if(userdata->type == 1){
  190. rcvr_mmc(&d, 1, userdata);
  191. }else{
  192. luat_gpio_set(userdata->spi_cs, 0);
  193. rcvr_mmc(&d, 1, userdata); /* Dummy clock (force DO enabled) */
  194. }
  195. if (wait_ready(userdata)) return 1; /* Wait for card ready */
  196. spi_cs_deselect(userdata);
  197. return 0; /* Failed */
  198. }
  199. /*-----------------------------------------------------------------------*/
  200. /* Receive a data packet from the card */
  201. /*-----------------------------------------------------------------------*/
  202. static
  203. int rcvr_datablock ( /* 1:OK, 0:Failed */
  204. BYTE *buff, /* Data buffer to store received data */
  205. UINT btr, /* Byte count */
  206. luat_fatfs_spi_t* userdata
  207. )
  208. {
  209. BYTE d[2];
  210. UINT tmr;
  211. for (tmr = 1000; tmr; tmr--) { /* Wait for data packet in timeout of 100ms */
  212. rcvr_mmc(d, 1, userdata);
  213. if (d[0] != 0xFF) break;
  214. dly_us(100);
  215. }
  216. if (d[0] != 0xFE) return 0; /* If not valid data token, return with error */
  217. rcvr_mmc(buff, btr, userdata); /* Receive the data block into buffer */
  218. rcvr_mmc(d, 2, userdata); /* Discard CRC */
  219. return 1; /* Return with success */
  220. }
  221. /*-----------------------------------------------------------------------*/
  222. /* Send a data packet to the card */
  223. /*-----------------------------------------------------------------------*/
  224. static
  225. int xmit_datablock ( /* 1:OK, 0:Failed */
  226. const BYTE *buff, /* 512 byte data block to be transmitted */
  227. BYTE token, /* Data/Stop token */
  228. luat_fatfs_spi_t* userdata
  229. )
  230. {
  231. BYTE d[2];
  232. if (!wait_ready(userdata)) return 0;
  233. d[0] = token;
  234. xmit_mmc(d, 1, userdata); /* Xmit a token */
  235. if (token != 0xFD) { /* Is it data token? */
  236. xmit_mmc(buff, 512, userdata); /* Xmit the 512 byte data block to MMC */
  237. rcvr_mmc(d, 2, userdata); /* Xmit dummy CRC (0xFF,0xFF) */
  238. rcvr_mmc(d, 1, userdata); /* Receive data response */
  239. if ((d[0] & 0x1F) != 0x05) /* If not accepted, return with error */
  240. return 0;
  241. }
  242. return 1;
  243. }
  244. /*-----------------------------------------------------------------------*/
  245. /* Send a command packet to the card */
  246. /*-----------------------------------------------------------------------*/
  247. static
  248. BYTE send_cmd ( /* Returns command response (bit7==1:Send failed)*/
  249. BYTE cmd, /* Command byte */
  250. DWORD arg, /* Argument */
  251. luat_fatfs_spi_t* userdata
  252. )
  253. {
  254. BYTE n, d, buf[6];
  255. if (cmd & 0x80) { /* ACMD<n> is the command sequense of CMD55-CMD<n> */
  256. cmd &= 0x7F;
  257. n = send_cmd(CMD55, 0, userdata);
  258. if (n > 1) return n;
  259. }
  260. /* Select the card and wait for ready except to stop multiple block read */
  261. if (cmd != CMD12) {
  262. spi_cs_deselect(userdata);
  263. if (!spi_cs_select(userdata)) return 0xFF;
  264. }
  265. /* Send a command packet */
  266. buf[0] = 0x40 | cmd; /* Start + Command index */
  267. buf[1] = (BYTE)(arg >> 24); /* Argument[31..24] */
  268. buf[2] = (BYTE)(arg >> 16); /* Argument[23..16] */
  269. buf[3] = (BYTE)(arg >> 8); /* Argument[15..8] */
  270. buf[4] = (BYTE)arg; /* Argument[7..0] */
  271. n = 0x01; /* Dummy CRC + Stop */
  272. if (cmd == CMD0) n = 0x95; /* (valid CRC for CMD0(0)) */
  273. if (cmd == CMD8) n = 0x87; /* (valid CRC for CMD8(0x1AA)) */
  274. buf[5] = n;
  275. xmit_mmc(buf, 6, userdata);
  276. /* Receive command response */
  277. if (cmd == CMD12) rcvr_mmc(&d, 1, userdata); /* Skip a stuff byte when stop reading */
  278. n = 10; /* Wait for a valid response in timeout of 10 attempts */
  279. do
  280. rcvr_mmc(&d, 1, userdata);
  281. while ((d & 0x80) && --n);
  282. return d; /* Return with the response value */
  283. }
  284. /*--------------------------------------------------------------------------
  285. Public Functions
  286. ---------------------------------------------------------------------------*/
  287. /*-----------------------------------------------------------------------*/
  288. /* Get Disk Status */
  289. /*-----------------------------------------------------------------------*/
  290. DSTATUS spitf_status (
  291. luat_fatfs_spi_t* userdata
  292. )
  293. {
  294. //if (drv) return STA_NOINIT;
  295. return Stat;
  296. }
  297. /*-----------------------------------------------------------------------*/
  298. /* Initialize Disk Drive */
  299. /*-----------------------------------------------------------------------*/
  300. DSTATUS spitf_initialize (
  301. luat_fatfs_spi_t* userdata
  302. )
  303. {
  304. BYTE n, ty, cmd, buf[4];
  305. UINT tmr;
  306. DSTATUS s;
  307. //if (drv) return RES_NOTRDY;
  308. //dly_us(10000); /* 10ms */
  309. //CS_INIT(); CS_H(); /* Initialize port pin tied to CS */
  310. //CK_INIT(); CK_L(); /* Initialize port pin tied to SCLK */
  311. //DI_INIT(); /* Initialize port pin tied to DI */
  312. //DO_INIT(); /* Initialize port pin tied to DO */
  313. //luat_spi_close(userdata->spi_id);
  314. //luat_spi_setup(userdata->spi_id, 400*1000/*400khz*/, 0, 0, 8, 1, 1);
  315. spi_cs_deselect(userdata);
  316. for (n = 10; n; n--) rcvr_mmc(buf, 1, userdata); /* Apply 80 dummy clocks and the card gets ready to receive command */
  317. ty = 0;
  318. if (send_cmd(CMD0, 0, userdata) == 1) { /* Enter Idle state */
  319. if (send_cmd(CMD8, 0x1AA, userdata) == 1) { /* SDv2? */
  320. rcvr_mmc(buf, 4, userdata); /* Get trailing return value of R7 resp */
  321. if (buf[2] == 0x01 && buf[3] == 0xAA) { /* The card can work at vdd range of 2.7-3.6V */
  322. for (tmr = 1000; tmr; tmr--) { /* Wait for leaving idle state (ACMD41 with HCS bit) */
  323. if (send_cmd(ACMD41, 1UL << 30, userdata) == 0) break;
  324. dly_us(1000);
  325. }
  326. if (tmr && send_cmd(CMD58, 0, userdata) == 0) { /* Check CCS bit in the OCR */
  327. rcvr_mmc(buf, 4, userdata);
  328. ty = (buf[0] & 0x40) ? CT_SD2 | CT_BLOCK : CT_SD2; /* SDv2 */
  329. }
  330. }
  331. } else { /* SDv1 or MMCv3 */
  332. if (send_cmd(ACMD41, 0, userdata) <= 1) {
  333. ty = CT_SD1; cmd = ACMD41; /* SDv1 */
  334. } else {
  335. ty = CT_MMC; cmd = CMD1; /* MMCv3 */
  336. }
  337. for (tmr = 1000; tmr; tmr--) { /* Wait for leaving idle state */
  338. if (send_cmd(cmd, 0, userdata) == 0) break;
  339. dly_us(1000);
  340. }
  341. if (!tmr || send_cmd(CMD16, 512, userdata) != 0) /* Set R/W block length to 512 */
  342. ty = 0;
  343. }
  344. }
  345. CardType = ty;
  346. s = ty ? 0 : STA_NOINIT;
  347. Stat = s;
  348. spi_cs_deselect(userdata);
  349. //luat_spi_close(userdata->spi_id);
  350. //spi.setup(id,0,0,8,400*1000,1)
  351. //luat_spi_setup(userdata->spi_id, 1000*1000/*1Mhz*/, 0, 0, 8, 1, 1);
  352. return s;
  353. }
  354. /*-----------------------------------------------------------------------*/
  355. /* Read Sector(s) */
  356. /*-----------------------------------------------------------------------*/
  357. DRESULT spitf_read (
  358. luat_fatfs_spi_t* userdata,
  359. BYTE *buff, /* Pointer to the data buffer to store read data */
  360. DWORD sector, /* Start sector number (LBA) */
  361. UINT count /* Sector count (1..128) */
  362. )
  363. {
  364. BYTE cmd;
  365. if (spitf_status(userdata) & STA_NOINIT) return RES_NOTRDY;
  366. if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert LBA to byte address if needed */
  367. cmd = count > 1 ? CMD18 : CMD17; /* READ_MULTIPLE_BLOCK : READ_SINGLE_BLOCK */
  368. if (send_cmd(cmd, sector, userdata) == 0) {
  369. do {
  370. if (!rcvr_datablock(buff, 512, userdata)) break;
  371. buff += 512;
  372. } while (--count);
  373. if (cmd == CMD18) send_cmd(CMD12, 0, userdata); /* STOP_TRANSMISSION */
  374. }
  375. spi_cs_deselect(userdata);
  376. return count ? RES_ERROR : RES_OK;
  377. }
  378. /*-----------------------------------------------------------------------*/
  379. /* Write Sector(s) */
  380. /*-----------------------------------------------------------------------*/
  381. DRESULT spitf_write (
  382. luat_fatfs_spi_t* userdata,
  383. const BYTE *buff, /* Pointer to the data to be written */
  384. DWORD sector, /* Start sector number (LBA) */
  385. UINT count /* Sector count (1..128) */
  386. )
  387. {
  388. if (spitf_status(userdata) & STA_NOINIT) return RES_NOTRDY;
  389. if (!(CardType & CT_BLOCK)) sector *= 512; /* Convert LBA to byte address if needed */
  390. if (count == 1) { /* Single block write */
  391. if ((send_cmd(CMD24, sector, userdata) == 0) /* WRITE_BLOCK */
  392. && xmit_datablock(buff, 0xFE, userdata))
  393. count = 0;
  394. }
  395. else { /* Multiple block write */
  396. if (CardType & CT_SDC) send_cmd(ACMD23, count, userdata);
  397. if (send_cmd(CMD25, sector, userdata) == 0) { /* WRITE_MULTIPLE_BLOCK */
  398. do {
  399. if (!xmit_datablock(buff, 0xFC, userdata)) break;
  400. buff += 512;
  401. } while (--count);
  402. if (!xmit_datablock(0, 0xFD, userdata)) /* STOP_TRAN token */
  403. count = 1;
  404. }
  405. }
  406. spi_cs_deselect(userdata);
  407. return count ? RES_ERROR : RES_OK;
  408. }
  409. /*-----------------------------------------------------------------------*/
  410. /* Miscellaneous Functions */
  411. /*-----------------------------------------------------------------------*/
  412. DRESULT spitf_ioctl (
  413. luat_fatfs_spi_t* userdata,
  414. BYTE ctrl, /* Control code */
  415. void *buff /* Buffer to send/receive control data */
  416. )
  417. {
  418. DRESULT res;
  419. BYTE n, csd[16];
  420. DWORD cs;
  421. if (spitf_status(userdata) & STA_NOINIT) return RES_NOTRDY; /* Check if card is in the socket */
  422. res = RES_ERROR;
  423. switch (ctrl) {
  424. case CTRL_SYNC : /* Make sure that no pending write process */
  425. if (spi_cs_select(userdata)) res = RES_OK;
  426. break;
  427. case GET_SECTOR_COUNT : /* Get number of sectors on the disk (DWORD) */
  428. if ((send_cmd(CMD9, 0, userdata) == 0) && rcvr_datablock(csd, 16, userdata)) {
  429. if ((csd[0] >> 6) == 1) { /* SDC ver 2.00 */
  430. cs = csd[9] + ((WORD)csd[8] << 8) + ((DWORD)(csd[7] & 63) << 16) + 1;
  431. *(DWORD*)buff = cs << 10;
  432. } else { /* SDC ver 1.XX or MMC */
  433. n = (csd[5] & 15) + ((csd[10] & 128) >> 7) + ((csd[9] & 3) << 1) + 2;
  434. cs = (csd[8] >> 6) + ((WORD)csd[7] << 2) + ((WORD)(csd[6] & 3) << 10) + 1;
  435. *(DWORD*)buff = cs << (n - 9);
  436. }
  437. res = RES_OK;
  438. }
  439. break;
  440. case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */
  441. *(DWORD*)buff = 128;
  442. res = RES_OK;
  443. break;
  444. default:
  445. res = RES_PARERR;
  446. }
  447. spi_cs_deselect(userdata);
  448. return res;
  449. }
  450. // DSTATUS spitf_initialize (luat_fatfs_spi_t* userdata);
  451. // DSTATUS ramdisk_status (luat_fatfs_spi_t* userdata);
  452. // DRESULT ramdisk_read (luat_fatfs_spi_t* userdata, BYTE* buff, LBA_t sector, UINT count);
  453. // DRESULT ramdisk_write (luat_fatfs_spi_t* userdata, const BYTE* buff, LBA_t sector, UINT count);
  454. // DRESULT ramdisk_ioctl (luat_fatfs_spi_t* userdata, BYTE cmd, void* buff);
  455. const block_disk_opts_t spitf_disk_opts = {
  456. .initialize = spitf_initialize,
  457. .status = spitf_status,
  458. .read = spitf_read,
  459. .write = spitf_write,
  460. .ioctl = spitf_ioctl,
  461. };
  462. __attribute__((weak)) void luat_spi_set_sdhc_ctrl(
  463. block_disk_t *disk)
  464. {
  465. }
  466. static block_disk_t disk = {0};
  467. DRESULT diskio_open_spitf(BYTE pdrv, luat_fatfs_spi_t* userdata) {
  468. // 暂时只支持单个fatfs实例
  469. disk.opts = &spitf_disk_opts;
  470. disk.userdata = userdata;
  471. luat_spi_set_sdhc_ctrl(&disk);
  472. return diskio_open(pdrv, &disk);
  473. }
  474. //static DWORD get_fattime() {
  475. // how to get?
  476. //}
  477. //--------------------------------------------------------------------------------------