mmcdrv.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  1. /****************************************************************************
  2. * This file is part of the MMC device driver.
  3. *
  4. * Copyright (c) 2004 by Michael Fischer. All rights reserved.
  5. *
  6. * Thanks to Sylvain Bissonnette for some of his low level functions.
  7. * Take a look at www.microsyl.com (Led Sign with MMC MemoryCard)
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions
  11. * are met:
  12. *
  13. * 1. Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in the
  17. * documentation and/or other materials provided with the distribution.
  18. * 3. Neither the name of the author nor the names of its contributors may
  19. * be used to endorse or promote products derived from this software
  20. * without specific prior written permission.
  21. *
  22. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  25. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
  26. * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  27. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  28. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  29. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  30. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  31. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  32. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  33. * SUCH DAMAGE.
  34. *
  35. ****************************************************************************
  36. * History:
  37. *
  38. * 10.10.04 mifi First Version
  39. ****************************************************************************/
  40. #define __MMCDRV_C__
  41. #define LOG_MODULE LOG_MMCDRV_MODULE
  42. #include <stdio.h>
  43. #include <string.h>
  44. #include <sys/timer.h>
  45. #include <sys/thread.h>
  46. #include <sys/event.h>
  47. #include <sys/heap.h>
  48. #include "typedefs.h"
  49. #include "portio.h"
  50. #include "mmcdrv.h"
  51. #include "vs10xx.h"
  52. #include "led.h"
  53. #include "log.h"
  54. #include "spidrv.h"
  55. /*==========================================================*/
  56. /* DEFINE: All Structures and Common Constants */
  57. /*==========================================================*/
  58. #define MMC_MAX_SUPPORTED_DEVICE 1
  59. /*
  60. * Drive Flags
  61. */
  62. #define MMC_SUPPORT_LBA 0x0001
  63. #define MMC_SUPPORT_LBA48 0x0002
  64. #define MMC_READ_ONLY 0x4000
  65. #define MMC_READY 0x8000
  66. #define Delay_1ms(_x) NutDelay(_x)
  67. #define SPIDDR DDRB
  68. #define SPIPORT PORTB
  69. #define SPIPIN PINB
  70. /*
  71. PragmaLab: disable PIN-defines (already defined in 'portio.h'
  72. #define SCLK 0x02
  73. #define MOSI 0x04
  74. #define MISO 0x08
  75. #define CS 0x20
  76. #define ENABLE 0x40
  77. end PragmaLab
  78. */
  79. #define MMC_RESET 0
  80. #define MMC_INIT 1
  81. #define MMC_READ_CSD 9
  82. #define MMC_READ_CID 10
  83. typedef struct _drive
  84. {
  85. /*
  86. * Interface values
  87. */
  88. WORD wFlags;
  89. BYTE bDevice;
  90. /*
  91. * LBA value
  92. */
  93. DWORD dTotalSectors;
  94. WORD wSectorSize;
  95. } DRIVE;
  96. /*==========================================================*/
  97. /* DEFINE: Definition of all local Data */
  98. /*==========================================================*/
  99. static HANDLE hMMCSemaphore;
  100. static DRIVE sDrive[MMC_MAX_SUPPORTED_DEVICE];
  101. static MMC_MOUNT_FUNC *pUserMountFunc;
  102. static MMC_MOUNT_FUNC *pUserUnMountFunc;
  103. /*==========================================================*/
  104. /* DEFINE: Definition of all local Procedures */
  105. /*==========================================================*/
  106. /************************************************************/
  107. /* MMCLock */
  108. /************************************************************/
  109. static void MMCLock(void)
  110. {
  111. NutEventWait(&hMMCSemaphore, 0);
  112. } /* MMCLock */
  113. /************************************************************/
  114. /* MMCFree */
  115. /************************************************************/
  116. static void MMCFree(void)
  117. {
  118. NutEventPost(&hMMCSemaphore);
  119. } /* MMCFree */
  120. /************************************************************/
  121. /* MMCSemaInit */
  122. /************************************************************/
  123. static void MMCSemaInit(void)
  124. {
  125. NutEventPost(&hMMCSemaphore);
  126. } /* MMCSemaInit */
  127. /************************************************************
  128. * int MMCDataToken(void)
  129. *
  130. * - pings the card until it gets data token
  131. * - returns one byte of read info (data token)
  132. ************************************************************/
  133. static BYTE MMCDataToken(void)
  134. {
  135. WORD i = 0xffff;
  136. BYTE Byte = 0xff;
  137. while ((Byte != 0xfe) && (--i))
  138. {
  139. Byte = SPIgetByte();
  140. }
  141. return(Byte);
  142. } /* MMCDataToken */
  143. /************************************************************
  144. * unsigned char MMCGet(void)
  145. *
  146. * - pings the card until it gets a non-0xff value
  147. * - returns one byte of read info
  148. ************************************************************/
  149. static BYTE MMCGet(void)
  150. {
  151. WORD i = 0xffff;
  152. BYTE Byte = 0xff;
  153. while ((Byte == 0xff) && (--i))
  154. {
  155. Byte = SPIgetByte();
  156. }
  157. return(Byte);
  158. } /* MMCGet */
  159. /************************************************************
  160. * void MMCCommand(unsigned char command, unsigned int px, unsigned int py)
  161. *
  162. * - send one byte of 0xff, then issue command + params + (fake) crc
  163. * - eat up the one command of nothing after the CRC
  164. ************************************************************/
  165. static void MMCCommand(unsigned char command, unsigned int px, unsigned int py)
  166. {
  167. SPIselect(SPI_DEV_MMC);
  168. SPIputByte(0xff);
  169. SPIputByte(command | 0x40);
  170. SPIputByte((unsigned char)((px >> 8)&0x0ff)); /* high byte of param y */
  171. SPIputByte((unsigned char)(px & 0x00ff)); /* low byte of param y */
  172. SPIputByte((unsigned char)((py >> 8)&0x0ff)); /* high byte of param x */
  173. SPIputByte((unsigned char)(py & 0x00ff)); /* low byte of param x */
  174. SPIputByte(0x95); /* correct CRC for first command in SPI */
  175. /* after that CRC is ignored, so no problem with */
  176. /* always sending 0x95 */
  177. SPIputByte(0xff);
  178. } /* MMCCommand */
  179. /************************************************************/
  180. /* GetCSD */
  181. /************************************************************/
  182. static int GetCSD (DRIVE *pDrive)
  183. {
  184. int i;
  185. int nError = MMC_ERROR;
  186. BYTE bData[16];
  187. WORD wREAD_BL_LEN;
  188. WORD wC_SIZE;
  189. WORD wC_SIZE_MULT;
  190. WORD wDummy;
  191. DWORD dTotalSectors = 0;
  192. MMCCommand(MMC_READ_CSD, 0, 0);
  193. if (MMCDataToken() != 0xfe)
  194. {
  195. LogMsg_P(LOG_ERR, PSTR("error during CSD read"));
  196. }
  197. else
  198. {
  199. for (i=0; i<16; i++)
  200. {
  201. bData[i] = SPIgetByte();
  202. }
  203. SPIputByte(0xff); /* checksum -> don't care about it for now */
  204. SPIputByte(0xff); /* checksum -> don't care about it for now */
  205. SPIdeselect();
  206. /*
  207. * Get the READ_BL_LEN
  208. */
  209. wREAD_BL_LEN = (1 << (bData[5] & 0x0F));
  210. /*
  211. * Get the C_SIZE
  212. */
  213. wC_SIZE = (bData[6] & 0x03);
  214. wC_SIZE = wC_SIZE << 10;
  215. wDummy = bData[7];
  216. wDummy = wDummy << 2;
  217. wC_SIZE |= wDummy;
  218. wDummy = (bData[8] & 0xC0);
  219. wDummy = wDummy >> 6;
  220. wC_SIZE |= wDummy;
  221. /*
  222. * Get the wC_SIZE_MULT
  223. */
  224. wC_SIZE_MULT = (bData[9] & 0x03);
  225. wC_SIZE_MULT |= wC_SIZE_MULT << 1;
  226. wDummy = (bData[10] & 0x80);
  227. wDummy = wDummy >> 7;
  228. wC_SIZE_MULT |= wDummy;
  229. wC_SIZE_MULT = (1 << (wC_SIZE_MULT+2));
  230. dTotalSectors = wC_SIZE+1;
  231. dTotalSectors *= wC_SIZE_MULT;
  232. pDrive->dTotalSectors = dTotalSectors;
  233. pDrive->wSectorSize = wREAD_BL_LEN;
  234. nError = MMC_OK;
  235. }
  236. return(nError);
  237. } /* GetCSD */
  238. #if 0
  239. /************************************************************/
  240. /* GetCID */
  241. /************************************************************/
  242. static void GetCID(void)
  243. {
  244. int i;
  245. BYTE bData[16];
  246. MMCCommand(MMC_READ_CID, 0, 0);
  247. if (MMCDataToken() != 0xfe)
  248. {
  249. printf("MMC: error during CID read\n");
  250. }
  251. else
  252. {
  253. printf("MMC: CID read\n");
  254. }
  255. for (i=0; i<16; i++)
  256. {
  257. bData[i] = SPIgetByte();
  258. }
  259. SPIputByte(0xff); /* checksum -> don't care about it for now */
  260. SPIputByte(0xff); /* checksum -> don't care about it for now */
  261. SPIdeselect();
  262. printf("MMC: Product Name: %c%c%c%c%c%c\n",
  263. bData[3], bData[4], bData[5],
  264. bData[6], bData[7], bData[8]);
  265. } /* GetCID */
  266. #endif
  267. /************************************************************/
  268. /* InitMMCCard */
  269. /* */
  270. /* - flushes card receive buffer */
  271. /* - selects card */
  272. /* - sends the reset command */
  273. /* - sends the initialization command, waits for card ready */
  274. /************************************************************/
  275. static int InitMMCCard(void)
  276. {
  277. WORD i;
  278. /* PragmaLab: disable initit of PINS and SPI, already done in 'SystemInitIO()'
  279. SPIDDR = SCLK + MOSI + CS;
  280. SPIPORT = 0x00;
  281. Delay_1ms(250);
  282. Delay_1ms(250);
  283. SPIPORT |= CS;
  284. SPCR = (1 << SPE) | (1 << MSTR); // enable SPI as master, set clk divider
  285. // set to max speed
  286. Delay_1ms(250);
  287. SPIdeselect();
  288. // start off with 80 bits of high data with card deselected
  289. PragmaLab: why send dummy bytes with card DEselected? This messes up the VS10XX init */
  290. for (i = 0; i < 10; i++)
  291. {
  292. SPIputByte(0xff);
  293. }
  294. /*end PragmaLab */
  295. /* send CMD0 - go to idle state */
  296. MMCCommand(MMC_RESET, 0, 0);
  297. if (MMCGet() != 1)
  298. {
  299. SPIdeselect();
  300. return(MMC_ERROR); // MMC Not detected
  301. }
  302. /* send CMD1 until we get a 0 back, indicating card is done initializing */
  303. i = 0xffff;
  304. while ((SPIgetByte() != 0) && (--i))
  305. {
  306. MMCCommand(MMC_INIT, 0, 0);
  307. }
  308. if (i == 0)
  309. {
  310. SPIdeselect();
  311. return(MMC_ERROR); // Init Fail
  312. }
  313. SPIdeselect();
  314. return(MMC_OK);
  315. } /* InitMMCCard */
  316. /************************************************************/
  317. /* ReadSectors */
  318. /************************************************************/
  319. static int ReadSectors(DRIVE *pDrive, BYTE *pBuffer, DWORD dStartSector, WORD wSectorCount)
  320. {
  321. int nError = MMC_OK;
  322. int nSector;
  323. WORD wDataCount;
  324. DWORD dReadSector;
  325. pDrive = pDrive;
  326. for (nSector=0; nSector<wSectorCount; nSector++)
  327. {
  328. dReadSector = dStartSector + nSector;
  329. MMCCommand(17,(dReadSector>>7) & 0xffff, (dReadSector<<9) & 0xffff);
  330. if (MMCDataToken() != 0xfe)
  331. {
  332. nError = MMC_ERROR;
  333. SPIdeselect();
  334. break;
  335. }
  336. for (wDataCount=0; wDataCount<512; wDataCount++)
  337. { /* read the sector */
  338. *pBuffer = SPIgetByte();
  339. pBuffer++;
  340. }
  341. SPIputByte(0xff); /* checksum -> don't care about it for now */
  342. SPIputByte(0xff); /* checksum -> don't care about it for now */
  343. SPIdeselect();
  344. }
  345. return(nError);
  346. } /* ReadSectors */
  347. #if (MMC_SUPPORT_WRITE == 1)
  348. /************************************************************/
  349. /* WriteSectors */
  350. /************************************************************/
  351. static BYTE WriteSectors(DRIVE *pDrive, BYTE *pBuffer, DWORD dStartSector, WORD wSectorCount)
  352. {
  353. int nError = MMC_OK;
  354. int nSector;
  355. WORD wDataCount;
  356. DWORD dWriteSector;
  357. pDrive = pDrive;
  358. for (nSector=0; nSector<wSectorCount; nSector++)
  359. {
  360. dWriteSector = dStartSector + nSector;
  361. MMCCommand(24, (dWriteSector>>7)& 0xffff, (dWriteSector<<9)& 0xffff);
  362. if (MMCGet() == 0xff)
  363. {
  364. nError = MMC_ERROR;
  365. SPIdeselect();
  366. break;
  367. }
  368. SPIputByte(0xfe); // Send Start Byte
  369. for (wDataCount=0; wDataCount<512; wDataCount++)
  370. { /* read the sector */
  371. SPIputByte(*pBuffer);
  372. pBuffer++;
  373. }
  374. SPIputByte(0xff); /* checksum -> don't care about it for now */
  375. SPIputByte(0xff); /* checksum -> don't care about it for now */
  376. SPIputByte(0xff); /* Read "data response byte" */
  377. wDataCount = 0xffff;
  378. while ((SPIgetByte() == 0x00) && (--wDataCount)); /* wait for write finish */
  379. if (wDataCount == 0)
  380. {
  381. nError = MMC_ERROR;
  382. SPIdeselect();
  383. break;
  384. }
  385. SPIdeselect();
  386. }
  387. return(nError);
  388. }
  389. #endif /* WriteSectors */
  390. /*==========================================================*/
  391. /* DEFINE: All code exported */
  392. /*==========================================================*/
  393. /************************************************************/
  394. /* MMCInit */
  395. /************************************************************/
  396. int MMCInit(int nMMCMode, MMC_MOUNT_FUNC *pMountFunc,
  397. MMC_MOUNT_FUNC *pUnMountFunc)
  398. {
  399. int nError = MMC_OK;
  400. BYTE bIndex;
  401. nMMCMode = nMMCMode;
  402. pUserMountFunc = pMountFunc;
  403. pUserUnMountFunc = pUnMountFunc;
  404. for (bIndex=0; bIndex<MMC_MAX_SUPPORTED_DEVICE; bIndex++)
  405. {
  406. memset((BYTE *) & sDrive[bIndex], 0x00, sizeof(DRIVE));
  407. sDrive[bIndex].bDevice = bIndex;
  408. }
  409. MMCSemaInit();
  410. nError = InitMMCCard();
  411. if (nError == MMC_OK)
  412. {
  413. sDrive[MMC_DRIVE_C].wFlags = MMC_READY;
  414. //GetCID();
  415. }
  416. return(nError);
  417. } /* MMCInit */
  418. /************************************************************/
  419. /* MMCMountAllDevices */
  420. /************************************************************/
  421. int MMCMountAllDevices(int nMMCMode, BYTE *pSectorBuffer)
  422. {
  423. int nError = MMC_ERROR;
  424. DRIVE *pDrive;
  425. nMMCMode = nMMCMode;
  426. pDrive = NULL;
  427. MMCLock();
  428. pDrive = &sDrive[MMC_DRIVE_C];
  429. if (pDrive->wFlags & MMC_READY)
  430. {
  431. nError = GetCSD(pDrive);
  432. }
  433. MMCFree();
  434. if (nError == MMC_OK)
  435. {
  436. nError = MMCReadSectors(MMC_DRIVE_C, pSectorBuffer, 0, 1);
  437. }
  438. return(nError);
  439. } /* MMCMountDevice */
  440. /************************************************************/
  441. /* MMCGetSectorSize */
  442. /************************************************************/
  443. int MMCGetSectorSize(BYTE bDevice)
  444. {
  445. int nSectorSize;
  446. DRIVE *pDrive;
  447. nSectorSize = 0;
  448. MMCLock();
  449. if (bDevice >= MMC_MAX_SUPPORTED_DEVICE)
  450. {
  451. nSectorSize = 0;
  452. }
  453. else
  454. {
  455. pDrive = &sDrive[bDevice];
  456. nSectorSize = pDrive->wSectorSize;
  457. }
  458. MMCFree();
  459. return(nSectorSize);
  460. } /* MMCGetSectorSize */
  461. /************************************************************/
  462. /* MMCIsCDROMDevice */
  463. /************************************************************/
  464. int MMCIsCDROMDevice(BYTE bDevice)
  465. {
  466. return(FALSE);
  467. } /* MMCIsCDROMDevice */
  468. /************************************************************/
  469. /* MMCIsZIPDevice */
  470. /************************************************************/
  471. int MMCIsZIPDevice(BYTE bDevice)
  472. {
  473. return(FALSE);
  474. } /* MMCIsZIPDevice */
  475. /************************************************************/
  476. /* MMCUnMountDevice */
  477. /************************************************************/
  478. int MMCUnMountDevice(BYTE bDevice)
  479. {
  480. return(MMC_OK);
  481. } /* MMCUnMountDevice */
  482. /************************************************************/
  483. /* MMCGetTotalSectors */
  484. /************************************************************/
  485. DWORD MMCGetTotalSectors(BYTE bDevice)
  486. {
  487. DWORD dwTotalSectors;
  488. DRIVE *pDrive;
  489. dwTotalSectors = 0;
  490. MMCLock();
  491. if (bDevice >= MMC_MAX_SUPPORTED_DEVICE)
  492. {
  493. dwTotalSectors = 0;
  494. }
  495. else
  496. {
  497. pDrive = &sDrive[bDevice];
  498. dwTotalSectors = pDrive->dTotalSectors;
  499. //dwTotalSectors -= 64;
  500. }
  501. MMCFree();
  502. return(dwTotalSectors);
  503. } /* MMCGetTotalSectors */
  504. /************************************************************/
  505. /* MMCReadSectors */
  506. /************************************************************/
  507. int MMCReadSectors(BYTE bDevice, void *pData, DWORD dwStartSector, WORD wSectorCount)
  508. {
  509. int nError;
  510. WORD wReadCount;
  511. DRIVE *pDrive = 0;
  512. BYTE *pByte;
  513. nError = MMC_OK;
  514. MMCLock();
  515. if (bDevice >= MMC_MAX_SUPPORTED_DEVICE)
  516. {
  517. nError = MMC_DRIVE_NOT_FOUND;
  518. }
  519. else
  520. {
  521. pDrive = &sDrive[bDevice];
  522. if ((pDrive->wFlags & MMC_READY) == 0)
  523. {
  524. nError = MMC_DRIVE_NOT_FOUND;
  525. }
  526. else
  527. {
  528. if ((dwStartSector + wSectorCount) > pDrive->dTotalSectors)
  529. {
  530. nError = MMC_PARAM_ERROR;
  531. }
  532. }
  533. }
  534. if (nError == MMC_OK)
  535. {
  536. pByte = (BYTE *)pData;
  537. if (wSectorCount != 1)
  538. {
  539. while (wSectorCount > 0)
  540. {
  541. if (wSectorCount < 256)
  542. {
  543. wReadCount = wSectorCount;
  544. }
  545. else
  546. {
  547. wReadCount = 256;
  548. }
  549. nError = ReadSectors(pDrive, pByte, dwStartSector, wReadCount);
  550. if (nError != MMC_OK)
  551. {
  552. break;
  553. }
  554. dwStartSector += wReadCount;
  555. wSectorCount -= wReadCount;
  556. pByte += (wReadCount * pDrive->wSectorSize);
  557. }
  558. }
  559. else
  560. {
  561. nError = ReadSectors(pDrive, pByte, dwStartSector, 1);
  562. }
  563. }
  564. MMCFree();
  565. return(nError);
  566. } /* MMCReadSectors */
  567. #if (MMC_SUPPORT_WRITE == 1)
  568. /************************************************************/
  569. /* MMCWriteSectors */
  570. /************************************************************/
  571. int MMCWriteSectors(BYTE bDevice, void *pData,
  572. DWORD dwStartSector, WORD wSectorCount)
  573. {
  574. int nError;
  575. WORD wWriteCount;
  576. DRIVE *pDrive = 0;
  577. BYTE *pByte;
  578. nError = MMC_OK;
  579. MMCLock();
  580. if (bDevice >= MMC_MAX_SUPPORTED_DEVICE)
  581. {
  582. nError = MMC_DRIVE_NOT_FOUND;
  583. }
  584. else
  585. {
  586. pDrive = &sDrive[bDevice];
  587. if ((dwStartSector + wSectorCount) > pDrive->dTotalSectors)
  588. {
  589. nError = MMC_PARAM_ERROR;
  590. }
  591. if ((pDrive->wFlags & MMC_READY) == 0)
  592. {
  593. nError = MMC_DRIVE_NOT_FOUND;
  594. }
  595. if (pDrive->wFlags & MMC_READ_ONLY)
  596. {
  597. nError = MMC_NOT_SUPPORTED;
  598. }
  599. }
  600. if (nError == MMC_OK)
  601. {
  602. pByte = (BYTE *) pData;
  603. while (wSectorCount > 0)
  604. {
  605. if (wSectorCount < 256)
  606. {
  607. wWriteCount = wSectorCount;
  608. }
  609. else
  610. {
  611. wWriteCount = 256;
  612. }
  613. nError = WriteSectors(pDrive, pByte, dwStartSector, wWriteCount);
  614. if (nError != MMC_OK)
  615. {
  616. break;
  617. }
  618. dwStartSector += wWriteCount;
  619. wSectorCount -= wWriteCount;
  620. pByte += (wWriteCount * MMC_SECTOR_SIZE);
  621. }
  622. }
  623. MMCFree();
  624. return(nError);
  625. } /* MMCWriteSectors */
  626. #endif