ChibiOS/RT
2.6.0
mmc_spi.c
Go to the documentation of this file.
00001 /*
00002     ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010,
00003                  2011,2012,2013 Giovanni Di Sirio.
00004 
00005     This file is part of ChibiOS/RT.
00006 
00007     ChibiOS/RT is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 3 of the License, or
00010     (at your option) any later version.
00011 
00012     ChibiOS/RT is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00019 
00020                                       ---
00021 
00022     A special exception to the GPL can be applied should you wish to distribute
00023     a combined work that includes ChibiOS/RT, without being obliged to provide
00024     the source code for any proprietary components. See the file exception.txt
00025     for full details of how and when the exception can be applied.
00026 */
00027 /*
00028    Parts of this file have been contributed by Matthias Blaicher.
00029  */
00030 
00031 /**
00032  * @file    mmc_spi.c
00033  * @brief   MMC over SPI driver code.
00034  *
00035  * @addtogroup MMC_SPI
00036  * @{
00037  */
00038 
00039 #include <string.h>
00040 
00041 #include "ch.h"
00042 #include "hal.h"
00043 
00044 #if HAL_USE_MMC_SPI || defined(__DOXYGEN__)
00045 
00046 /*===========================================================================*/
00047 /* Driver local definitions.                                                 */
00048 /*===========================================================================*/
00049 
00050 /*===========================================================================*/
00051 /* Driver exported variables.                                                */
00052 /*===========================================================================*/
00053 
00054 /*===========================================================================*/
00055 /* Driver local variables and types.                                         */
00056 /*===========================================================================*/
00057 
00058 /* Forward declarations required by mmc_vmt.*/
00059 static bool_t mmc_read(void *instance, uint32_t startblk,
00060                        uint8_t *buffer, uint32_t n);
00061 static bool_t mmc_write(void *instance, uint32_t startblk,
00062                         const uint8_t *buffer, uint32_t n);
00063 
00064 /**
00065  * @brief   Virtual methods table.
00066  */
00067 static const struct MMCDriverVMT mmc_vmt = {
00068   (bool_t (*)(void *))mmc_lld_is_card_inserted,
00069   (bool_t (*)(void *))mmc_lld_is_write_protected,
00070   (bool_t (*)(void *))mmcConnect,
00071   (bool_t (*)(void *))mmcDisconnect,
00072   mmc_read,
00073   mmc_write,
00074   (bool_t (*)(void *))mmcSync,
00075   (bool_t (*)(void *, BlockDeviceInfo *))mmcGetInfo
00076 };
00077 
00078 /**
00079  * @brief   Lookup table for CRC-7 ( based on polynomial x^7 + x^3 + 1).
00080  */
00081 static const uint8_t crc7_lookup_table[256] = {
00082   0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53,
00083   0x6c, 0x65, 0x7e, 0x77, 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26,
00084   0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e, 0x32, 0x3b, 0x20, 0x29,
00085   0x16, 0x1f, 0x04, 0x0d, 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45,
00086   0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14, 0x63, 0x6a, 0x71, 0x78,
00087   0x47, 0x4e, 0x55, 0x5c, 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b,
00088   0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13, 0x7d, 0x74, 0x6f, 0x66,
00089   0x59, 0x50, 0x4b, 0x42, 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a,
00090   0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69, 0x1e, 0x17, 0x0c, 0x05,
00091   0x3a, 0x33, 0x28, 0x21, 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70,
00092   0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, 0x41, 0x48, 0x53, 0x5a,
00093   0x65, 0x6c, 0x77, 0x7e, 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36,
00094   0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67, 0x10, 0x19, 0x02, 0x0b,
00095   0x34, 0x3d, 0x26, 0x2f, 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
00096   0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, 0x6a, 0x63, 0x78, 0x71,
00097   0x4e, 0x47, 0x5c, 0x55, 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d,
00098   0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a, 0x6d, 0x64, 0x7f, 0x76,
00099   0x49, 0x40, 0x5b, 0x52, 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03,
00100   0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b, 0x17, 0x1e, 0x05, 0x0c,
00101   0x33, 0x3a, 0x21, 0x28, 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60,
00102   0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31, 0x46, 0x4f, 0x54, 0x5d,
00103   0x62, 0x6b, 0x70, 0x79
00104 };
00105 
00106 /*===========================================================================*/
00107 /* Driver local functions.                                                   */
00108 /*===========================================================================*/
00109 
00110 static bool_t mmc_read(void *instance, uint32_t startblk,
00111                 uint8_t *buffer, uint32_t n) {
00112 
00113   if (mmcStartSequentialRead((MMCDriver *)instance, startblk))
00114     return CH_FAILED;
00115   while (n > 0) {
00116     if (mmcSequentialRead((MMCDriver *)instance, buffer))
00117       return CH_FAILED;
00118     buffer += MMCSD_BLOCK_SIZE;
00119     n--;
00120   }
00121   if (mmcStopSequentialRead((MMCDriver *)instance))
00122       return CH_FAILED;
00123   return CH_SUCCESS;
00124 }
00125 
00126 static bool_t mmc_write(void *instance, uint32_t startblk,
00127                  const uint8_t *buffer, uint32_t n) {
00128 
00129   if (mmcStartSequentialWrite((MMCDriver *)instance, startblk))
00130       return CH_FAILED;
00131   while (n > 0) {
00132       if (mmcSequentialWrite((MMCDriver *)instance, buffer))
00133           return CH_FAILED;
00134       buffer += MMCSD_BLOCK_SIZE;
00135       n--;
00136   }
00137   if (mmcStopSequentialWrite((MMCDriver *)instance))
00138       return CH_FAILED;
00139   return CH_SUCCESS;
00140 }
00141 
00142 /**
00143  * @brief Calculate the MMC standard CRC-7 based on a lookup table.
00144  *
00145  * @param[in] crc       start value for CRC
00146  * @param[in] buffer    pointer to data buffer
00147  * @param[in] len       length of data
00148  * @return              Calculated CRC
00149  */
00150 static uint8_t crc7(uint8_t crc, const uint8_t *buffer, size_t len) {
00151 
00152   while (len--)
00153     crc = crc7_lookup_table[(crc << 1) ^ (*buffer++)];
00154   return crc;
00155 }
00156 
00157 /**
00158  * @brief   Waits an idle condition.
00159  *
00160  * @param[in] mmcp      pointer to the @p MMCDriver object
00161  *
00162  * @notapi
00163  */
00164 static void wait(MMCDriver *mmcp) {
00165   int i;
00166   uint8_t buf[4];
00167 
00168   for (i = 0; i < 16; i++) {
00169     spiReceive(mmcp->config->spip, 1, buf);
00170     if (buf[0] == 0xFF)
00171       return;
00172   }
00173   /* Looks like it is a long wait.*/
00174   while (TRUE) {
00175     spiReceive(mmcp->config->spip, 1, buf);
00176     if (buf[0] == 0xFF)
00177       break;
00178 #ifdef MMC_NICE_WAITING
00179     /* Trying to be nice with the other threads.*/
00180     chThdSleep(1);
00181 #endif
00182   }
00183 }
00184 
00185 /**
00186  * @brief   Sends a command header.
00187  *
00188  * @param[in] mmcp      pointer to the @p MMCDriver object
00189  * @param[in] cmd       the command id
00190  * @param[in] arg       the command argument
00191  *
00192  * @notapi
00193  */
00194 static void send_hdr(MMCDriver *mmcp, uint8_t cmd, uint32_t arg) {
00195   uint8_t buf[6];
00196 
00197   /* Wait for the bus to become idle if a write operation was in progress.*/
00198   wait(mmcp);
00199 
00200   buf[0] = 0x40 | cmd;
00201   buf[1] = arg >> 24;
00202   buf[2] = arg >> 16;
00203   buf[3] = arg >> 8;
00204   buf[4] = arg;
00205   /* Calculate CRC for command header, shift to right position, add stop bit.*/
00206   buf[5] = ((crc7(0, buf, 5) & 0x7F) << 1) | 0x01;
00207 
00208   spiSend(mmcp->config->spip, 6, buf);
00209 }
00210 
00211 /**
00212  * @brief   Receives a single byte response.
00213  *
00214  * @param[in] mmcp      pointer to the @p MMCDriver object
00215  * @return              The response as an @p uint8_t value.
00216  * @retval 0xFF         timed out.
00217  *
00218  * @notapi
00219  */
00220 static uint8_t recvr1(MMCDriver *mmcp) {
00221   int i;
00222   uint8_t r1[1];
00223 
00224   for (i = 0; i < 9; i++) {
00225     spiReceive(mmcp->config->spip, 1, r1);
00226     if (r1[0] != 0xFF)
00227       return r1[0];
00228   }
00229   return 0xFF;
00230 }
00231 
00232 /**
00233  * @brief   Receives a three byte response.
00234  *
00235  * @param[in] mmcp      pointer to the @p MMCDriver object
00236  * @param[out] buffer   pointer to four bytes wide buffer
00237  * @return              First response byte as an @p uint8_t value.
00238  * @retval 0xFF         timed out.
00239  *
00240  * @notapi
00241  */
00242 static uint8_t recvr3(MMCDriver *mmcp, uint8_t* buffer) {
00243   uint8_t r1;
00244 
00245   r1 = recvr1(mmcp);
00246   spiReceive(mmcp->config->spip, 4, buffer);
00247 
00248   return r1;
00249 }
00250 
00251 /**
00252  * @brief   Sends a command an returns a single byte response.
00253  *
00254  * @param[in] mmcp      pointer to the @p MMCDriver object
00255  * @param[in] cmd       the command id
00256  * @param[in] arg       the command argument
00257  * @return              The response as an @p uint8_t value.
00258  * @retval 0xFF         timed out.
00259  *
00260  * @notapi
00261  */
00262 static uint8_t send_command_R1(MMCDriver *mmcp, uint8_t cmd, uint32_t arg) {
00263   uint8_t r1;
00264 
00265   spiSelect(mmcp->config->spip);
00266   send_hdr(mmcp, cmd, arg);
00267   r1 = recvr1(mmcp);
00268   spiUnselect(mmcp->config->spip);
00269   return r1;
00270 }
00271 
00272 /**
00273  * @brief   Sends a command which returns a five bytes response (R3).
00274  *
00275  * @param[in] mmcp      pointer to the @p MMCDriver object
00276  * @param[in] cmd       the command id
00277  * @param[in] arg       the command argument
00278  * @param[out] response pointer to four bytes wide uint8_t buffer
00279  * @return              The first byte of the response (R1) as an @p
00280  *                      uint8_t value.
00281  * @retval 0xFF         timed out.
00282  *
00283  * @notapi
00284  */
00285 static uint8_t send_command_R3(MMCDriver *mmcp, uint8_t cmd, uint32_t arg,
00286                                uint8_t *response) {
00287   uint8_t r1;
00288 
00289   spiSelect(mmcp->config->spip);
00290   send_hdr(mmcp, cmd, arg);
00291   r1 = recvr3(mmcp, response);
00292   spiUnselect(mmcp->config->spip);
00293   return r1;
00294 }
00295 
00296 /**
00297  * @brief   Reads the CSD.
00298  *
00299  * @param[in] mmcp      pointer to the @p MMCDriver object
00300  * @param[out] csd       pointer to the CSD buffer
00301  *
00302  * @return              The operation status.
00303  * @retval CH_SUCCESS   the operation succeeded.
00304  * @retval CH_FAILED    the operation failed.
00305  *
00306  * @notapi
00307  */
00308 static bool_t read_CxD(MMCDriver *mmcp, uint8_t cmd, uint32_t cxd[4]) {
00309   unsigned i;
00310   uint8_t *bp, buf[16];
00311 
00312   spiSelect(mmcp->config->spip);
00313   send_hdr(mmcp, cmd, 0);
00314   if (recvr1(mmcp) != 0x00) {
00315     spiUnselect(mmcp->config->spip);
00316     return CH_FAILED;
00317   }
00318 
00319   /* Wait for data availability.*/
00320   for (i = 0; i < MMC_WAIT_DATA; i++) {
00321     spiReceive(mmcp->config->spip, 1, buf);
00322     if (buf[0] == 0xFE) {
00323       uint32_t *wp;
00324 
00325       spiReceive(mmcp->config->spip, 16, buf);
00326       bp = buf;
00327       for (wp = &cxd[3]; wp >= cxd; wp--) {
00328         *wp = ((uint32_t)bp[0] << 24) | ((uint32_t)bp[1] << 16) |
00329               ((uint32_t)bp[2] << 8)  | (uint32_t)bp[3];
00330         bp += 4;
00331       }
00332 
00333       /* CRC ignored then end of transaction. */
00334       spiIgnore(mmcp->config->spip, 2);
00335       spiUnselect(mmcp->config->spip);
00336 
00337       return CH_SUCCESS;
00338     }
00339   }
00340   return CH_FAILED;
00341 }
00342 
00343 /**
00344  * @brief   Waits that the card reaches an idle state.
00345  *
00346  * @param[in] mmcp      pointer to the @p MMCDriver object
00347  *
00348  * @notapi
00349  */
00350 static void sync(MMCDriver *mmcp) {
00351   uint8_t buf[1];
00352 
00353   spiSelect(mmcp->config->spip);
00354   while (TRUE) {
00355     spiReceive(mmcp->config->spip, 1, buf);
00356     if (buf[0] == 0xFF)
00357       break;
00358 #ifdef MMC_NICE_WAITING
00359     chThdSleep(1);      /* Trying to be nice with the other threads.*/
00360 #endif
00361   }
00362   spiUnselect(mmcp->config->spip);
00363 }
00364 
00365 /*===========================================================================*/
00366 /* Driver exported functions.                                                */
00367 /*===========================================================================*/
00368 
00369 /**
00370  * @brief   MMC over SPI driver initialization.
00371  * @note    This function is implicitly invoked by @p halInit(), there is
00372  *          no need to explicitly initialize the driver.
00373  *
00374  * @init
00375  */
00376 void mmcInit(void) {
00377 
00378 }
00379 
00380 /**
00381  * @brief   Initializes an instance.
00382  *
00383  * @param[out] mmcp         pointer to the @p MMCDriver object
00384  *
00385  * @init
00386  */
00387 void mmcObjectInit(MMCDriver *mmcp) {
00388 
00389   mmcp->vmt = &mmc_vmt;
00390   mmcp->state = BLK_STOP;
00391   mmcp->config = NULL;
00392   mmcp->block_addresses = FALSE;
00393 }
00394 
00395 /**
00396  * @brief   Configures and activates the MMC peripheral.
00397  *
00398  * @param[in] mmcp      pointer to the @p MMCDriver object
00399  * @param[in] config    pointer to the @p MMCConfig object.
00400  *
00401  * @api
00402  */
00403 void mmcStart(MMCDriver *mmcp, const MMCConfig *config) {
00404 
00405   chDbgCheck((mmcp != NULL) && (config != NULL), "mmcStart");
00406   chDbgAssert((mmcp->state == BLK_STOP) || (mmcp->state == BLK_ACTIVE),
00407               "mmcStart(), #1", "invalid state");
00408 
00409   mmcp->config = config;
00410   mmcp->state = BLK_ACTIVE;
00411 }
00412 
00413 /**
00414  * @brief   Disables the MMC peripheral.
00415  *
00416  * @param[in] mmcp      pointer to the @p MMCDriver object
00417  *
00418  * @api
00419  */
00420 void mmcStop(MMCDriver *mmcp) {
00421 
00422   chDbgCheck(mmcp != NULL, "mmcStop");
00423   chDbgAssert((mmcp->state == BLK_STOP) || (mmcp->state == BLK_ACTIVE),
00424               "mmcStop(), #1", "invalid state");
00425 
00426   spiStop(mmcp->config->spip);
00427   mmcp->state = BLK_STOP;
00428 }
00429 
00430 /**
00431  * @brief   Performs the initialization procedure on the inserted card.
00432  * @details This function should be invoked when a card is inserted and
00433  *          brings the driver in the @p MMC_READY state where it is possible
00434  *          to perform read and write operations.
00435  * @note    It is possible to invoke this function from the insertion event
00436  *          handler.
00437  *
00438  * @param[in] mmcp      pointer to the @p MMCDriver object
00439  *
00440  * @return              The operation status.
00441  * @retval CH_SUCCESS   the operation succeeded and the driver is now
00442  *                      in the @p MMC_READY state.
00443  * @retval CH_FAILED    the operation failed.
00444  *
00445  * @api
00446  */
00447 bool_t mmcConnect(MMCDriver *mmcp) {
00448   unsigned i;
00449   uint8_t r3[4];
00450 
00451   chDbgCheck(mmcp != NULL, "mmcConnect");
00452 
00453   chDbgAssert((mmcp->state == BLK_ACTIVE) || (mmcp->state == BLK_READY),
00454               "mmcConnect(), #1", "invalid state");
00455 
00456   /* Connection procedure in progress.*/
00457   mmcp->state = BLK_CONNECTING;
00458 
00459   /* Slow clock mode and 128 clock pulses.*/
00460   spiStart(mmcp->config->spip, mmcp->config->lscfg);
00461   spiIgnore(mmcp->config->spip, 16);
00462 
00463   /* SPI mode selection.*/
00464   i = 0;
00465   while (TRUE) {
00466     if (send_command_R1(mmcp, MMCSD_CMD_GO_IDLE_STATE, 0) == 0x01)
00467       break;
00468     if (++i >= MMC_CMD0_RETRY)
00469       goto failed;
00470     chThdSleepMilliseconds(10);
00471   }
00472 
00473   /* Try to detect if this is a high capacity card and switch to block
00474      addresses if possible.
00475      This method is based on "How to support SDC Ver2 and high capacity cards"
00476      by ElmChan.*/
00477   if (send_command_R3(mmcp, MMCSD_CMD_SEND_IF_COND,
00478                       MMCSD_CMD8_PATTERN, r3) != 0x05) {
00479 
00480     /* Switch to SDHC mode.*/
00481     i = 0;
00482     while (TRUE) {
00483       if ((send_command_R1(mmcp, MMCSD_CMD_APP_CMD, 0) == 0x01) &&
00484           (send_command_R3(mmcp, MMCSD_CMD_APP_OP_COND,
00485                            0x400001aa, r3) == 0x00))
00486         break;
00487 
00488       if (++i >= MMC_ACMD41_RETRY)
00489         goto failed;
00490       chThdSleepMilliseconds(10);
00491     }
00492 
00493     /* Execute dedicated read on OCR register */
00494     send_command_R3(mmcp, MMCSD_CMD_READ_OCR, 0, r3);
00495 
00496     /* Check if CCS is set in response. Card operates in block mode if set.*/
00497     if (r3[0] & 0x40)
00498       mmcp->block_addresses = TRUE;
00499   }
00500 
00501   /* Initialization.*/
00502   i = 0;
00503   while (TRUE) {
00504     uint8_t b = send_command_R1(mmcp, MMCSD_CMD_INIT, 0);
00505     if (b == 0x00)
00506       break;
00507     if (b != 0x01)
00508       goto failed;
00509     if (++i >= MMC_CMD1_RETRY)
00510       goto failed;
00511     chThdSleepMilliseconds(10);
00512   }
00513 
00514   /* Initialization complete, full speed.*/
00515   spiStart(mmcp->config->spip, mmcp->config->hscfg);
00516 
00517   /* Setting block size.*/
00518   if (send_command_R1(mmcp, MMCSD_CMD_SET_BLOCKLEN,
00519                       MMCSD_BLOCK_SIZE) != 0x00)
00520     goto failed;
00521 
00522   /* Determine capacity.*/
00523   if (read_CxD(mmcp, MMCSD_CMD_SEND_CSD, mmcp->csd))
00524     goto failed;
00525   mmcp->capacity = mmcsdGetCapacity(mmcp->csd);
00526   if (mmcp->capacity == 0)
00527     goto failed;
00528 
00529   if (read_CxD(mmcp, MMCSD_CMD_SEND_CID, mmcp->cid))
00530     goto failed;
00531 
00532   mmcp->state = BLK_READY;
00533   return CH_SUCCESS;
00534 
00535   /* Connection failed, state reset to BLK_ACTIVE.*/
00536 failed:
00537   spiStop(mmcp->config->spip);
00538   mmcp->state = BLK_ACTIVE;
00539   return CH_FAILED;
00540 }
00541 
00542 /**
00543  * @brief   Brings the driver in a state safe for card removal.
00544  *
00545  * @param[in] mmcp      pointer to the @p MMCDriver object
00546  * @return              The operation status.
00547  *
00548  * @retval CH_SUCCESS   the operation succeeded and the driver is now
00549  *                      in the @p MMC_INSERTED state.
00550  * @retval CH_FAILED    the operation failed.
00551  *
00552  * @api
00553  */
00554 bool_t mmcDisconnect(MMCDriver *mmcp) {
00555 
00556   chDbgCheck(mmcp != NULL, "mmcDisconnect");
00557 
00558   chSysLock();
00559   chDbgAssert((mmcp->state == BLK_ACTIVE) || (mmcp->state == BLK_READY),
00560               "mmcDisconnect(), #1", "invalid state");
00561   if (mmcp->state == BLK_ACTIVE) {
00562     chSysUnlock();
00563     return CH_SUCCESS;
00564   }
00565   mmcp->state = BLK_DISCONNECTING;
00566   chSysUnlock();
00567 
00568   /* Wait for the pending write operations to complete.*/
00569   spiStart(mmcp->config->spip, mmcp->config->hscfg);
00570   sync(mmcp);
00571 
00572   spiStop(mmcp->config->spip);
00573   mmcp->state = BLK_ACTIVE;
00574   return CH_SUCCESS;
00575 }
00576 
00577 /**
00578  * @brief   Starts a sequential read.
00579  *
00580  * @param[in] mmcp      pointer to the @p MMCDriver object
00581  * @param[in] startblk  first block to read
00582  *
00583  * @return              The operation status.
00584  * @retval CH_SUCCESS   the operation succeeded.
00585  * @retval CH_FAILED    the operation failed.
00586  *
00587  * @api
00588  */
00589 bool_t mmcStartSequentialRead(MMCDriver *mmcp, uint32_t startblk) {
00590 
00591   chDbgCheck(mmcp != NULL, "mmcStartSequentialRead");
00592   chDbgAssert(mmcp->state == BLK_READY,
00593               "mmcStartSequentialRead(), #1", "invalid state");
00594 
00595   /* Read operation in progress.*/
00596   mmcp->state = BLK_READING;
00597 
00598   /* (Re)starting the SPI in case it has been reprogrammed externally, it can
00599      happen if the SPI bus is shared among multiple peripherals.*/
00600   spiStart(mmcp->config->spip, mmcp->config->hscfg);
00601   spiSelect(mmcp->config->spip);
00602 
00603   if (mmcp->block_addresses)
00604     send_hdr(mmcp, MMCSD_CMD_READ_MULTIPLE_BLOCK, startblk);
00605   else
00606     send_hdr(mmcp, MMCSD_CMD_READ_MULTIPLE_BLOCK, startblk * MMCSD_BLOCK_SIZE);
00607 
00608   if (recvr1(mmcp) != 0x00) {
00609     spiStop(mmcp->config->spip);
00610     mmcp->state = BLK_READY;
00611     return CH_FAILED;
00612   }
00613   return CH_SUCCESS;
00614 }
00615 
00616 /**
00617  * @brief   Reads a block within a sequential read operation.
00618  *
00619  * @param[in] mmcp      pointer to the @p MMCDriver object
00620  * @param[out] buffer   pointer to the read buffer
00621  *
00622  * @return              The operation status.
00623  * @retval CH_SUCCESS   the operation succeeded.
00624  * @retval CH_FAILED    the operation failed.
00625  *
00626  * @api
00627  */
00628 bool_t mmcSequentialRead(MMCDriver *mmcp, uint8_t *buffer) {
00629   int i;
00630 
00631   chDbgCheck((mmcp != NULL) && (buffer != NULL), "mmcSequentialRead");
00632 
00633   if (mmcp->state != BLK_READING)
00634     return CH_FAILED;
00635 
00636   for (i = 0; i < MMC_WAIT_DATA; i++) {
00637     spiReceive(mmcp->config->spip, 1, buffer);
00638     if (buffer[0] == 0xFE) {
00639       spiReceive(mmcp->config->spip, MMCSD_BLOCK_SIZE, buffer);
00640       /* CRC ignored. */
00641       spiIgnore(mmcp->config->spip, 2);
00642       return CH_SUCCESS;
00643     }
00644   }
00645   /* Timeout.*/
00646   spiUnselect(mmcp->config->spip);
00647   spiStop(mmcp->config->spip);
00648   mmcp->state = BLK_READY;
00649   return CH_FAILED;
00650 }
00651 
00652 /**
00653  * @brief   Stops a sequential read gracefully.
00654  *
00655  * @param[in] mmcp      pointer to the @p MMCDriver object
00656  *
00657  * @return              The operation status.
00658  * @retval CH_SUCCESS   the operation succeeded.
00659  * @retval CH_FAILED    the operation failed.
00660  *
00661  * @api
00662  */
00663 bool_t mmcStopSequentialRead(MMCDriver *mmcp) {
00664   static const uint8_t stopcmd[] = {0x40 | MMCSD_CMD_STOP_TRANSMISSION,
00665                                     0, 0, 0, 0, 1, 0xFF};
00666 
00667   chDbgCheck(mmcp != NULL, "mmcStopSequentialRead");
00668 
00669   if (mmcp->state != BLK_READING)
00670     return CH_FAILED;
00671 
00672   spiSend(mmcp->config->spip, sizeof(stopcmd), stopcmd);
00673 /*  result = recvr1(mmcp) != 0x00;*/
00674   /* Note, ignored r1 response, it can be not zero, unknown issue.*/
00675   (void) recvr1(mmcp);
00676 
00677   /* Read operation finished.*/
00678   spiUnselect(mmcp->config->spip);
00679   mmcp->state = BLK_READY;
00680   return CH_SUCCESS;
00681 }
00682 
00683 /**
00684  * @brief   Starts a sequential write.
00685  *
00686  * @param[in] mmcp      pointer to the @p MMCDriver object
00687  * @param[in] startblk  first block to write
00688  *
00689  * @return              The operation status.
00690  * @retval CH_SUCCESS   the operation succeeded.
00691  * @retval CH_FAILED    the operation failed.
00692  *
00693  * @api
00694  */
00695 bool_t mmcStartSequentialWrite(MMCDriver *mmcp, uint32_t startblk) {
00696 
00697   chDbgCheck(mmcp != NULL, "mmcStartSequentialWrite");
00698   chDbgAssert(mmcp->state == BLK_READY,
00699               "mmcStartSequentialWrite(), #1", "invalid state");
00700 
00701   /* Write operation in progress.*/
00702   mmcp->state = BLK_WRITING;
00703 
00704   spiStart(mmcp->config->spip, mmcp->config->hscfg);
00705   spiSelect(mmcp->config->spip);
00706   if (mmcp->block_addresses)
00707     send_hdr(mmcp, MMCSD_CMD_WRITE_MULTIPLE_BLOCK, startblk);
00708   else
00709     send_hdr(mmcp, MMCSD_CMD_WRITE_MULTIPLE_BLOCK,
00710              startblk * MMCSD_BLOCK_SIZE);
00711 
00712   if (recvr1(mmcp) != 0x00) {
00713     spiStop(mmcp->config->spip);
00714     mmcp->state = BLK_READY;
00715     return CH_FAILED;
00716   }
00717   return CH_SUCCESS;
00718 }
00719 
00720 /**
00721  * @brief   Writes a block within a sequential write operation.
00722  *
00723  * @param[in] mmcp      pointer to the @p MMCDriver object
00724  * @param[out] buffer   pointer to the write buffer
00725  *
00726  * @return              The operation status.
00727  * @retval CH_SUCCESS   the operation succeeded.
00728  * @retval CH_FAILED    the operation failed.
00729  *
00730  * @api
00731  */
00732 bool_t mmcSequentialWrite(MMCDriver *mmcp, const uint8_t *buffer) {
00733   static const uint8_t start[] = {0xFF, 0xFC};
00734   uint8_t b[1];
00735 
00736   chDbgCheck((mmcp != NULL) && (buffer != NULL), "mmcSequentialWrite");
00737 
00738   if (mmcp->state != BLK_WRITING)
00739     return CH_FAILED;
00740 
00741   spiSend(mmcp->config->spip, sizeof(start), start);    /* Data prologue.   */
00742   spiSend(mmcp->config->spip, MMCSD_BLOCK_SIZE, buffer);/* Data.            */
00743   spiIgnore(mmcp->config->spip, 2);                     /* CRC ignored.     */
00744   spiReceive(mmcp->config->spip, 1, b);
00745   if ((b[0] & 0x1F) == 0x05) {
00746     wait(mmcp);
00747     return CH_SUCCESS;
00748   }
00749 
00750   /* Error.*/
00751   spiUnselect(mmcp->config->spip);
00752   spiStop(mmcp->config->spip);
00753   mmcp->state = BLK_READY;
00754   return CH_FAILED;
00755 }
00756 
00757 /**
00758  * @brief   Stops a sequential write gracefully.
00759  *
00760  * @param[in] mmcp      pointer to the @p MMCDriver object
00761  *
00762  * @return              The operation status.
00763  * @retval CH_SUCCESS   the operation succeeded.
00764  * @retval CH_FAILED    the operation failed.
00765  *
00766  * @api
00767  */
00768 bool_t mmcStopSequentialWrite(MMCDriver *mmcp) {
00769   static const uint8_t stop[] = {0xFD, 0xFF};
00770 
00771   chDbgCheck(mmcp != NULL, "mmcStopSequentialWrite");
00772 
00773   if (mmcp->state != BLK_WRITING)
00774     return CH_FAILED;
00775 
00776   spiSend(mmcp->config->spip, sizeof(stop), stop);
00777   spiUnselect(mmcp->config->spip);
00778 
00779   /* Write operation finished.*/
00780   mmcp->state = BLK_READY;
00781   return CH_SUCCESS;
00782 }
00783 
00784 /**
00785  * @brief   Waits for card idle condition.
00786  *
00787  * @param[in] mmcp      pointer to the @p MMCDriver object
00788  *
00789  * @return              The operation status.
00790  * @retval CH_SUCCESS   the operation succeeded.
00791  * @retval CH_FAILED    the operation failed.
00792  *
00793  * @api
00794  */
00795 bool_t mmcSync(MMCDriver *mmcp) {
00796 
00797   chDbgCheck(mmcp != NULL, "mmcSync");
00798 
00799   if (mmcp->state != BLK_READY)
00800     return CH_FAILED;
00801 
00802   /* Synchronization operation in progress.*/
00803   mmcp->state = BLK_SYNCING;
00804 
00805   spiStart(mmcp->config->spip, mmcp->config->hscfg);
00806   sync(mmcp);
00807 
00808   /* Synchronization operation finished.*/
00809   mmcp->state = BLK_READY;
00810   return CH_SUCCESS;
00811 }
00812 
00813 /**
00814  * @brief   Returns the media info.
00815  *
00816  * @param[in] mmcp      pointer to the @p MMCDriver object
00817  * @param[out] bdip     pointer to a @p BlockDeviceInfo structure
00818  *
00819  * @return              The operation status.
00820  * @retval CH_SUCCESS   the operation succeeded.
00821  * @retval CH_FAILED    the operation failed.
00822  *
00823  * @api
00824  */
00825 bool_t mmcGetInfo(MMCDriver *mmcp, BlockDeviceInfo *bdip) {
00826 
00827   chDbgCheck((mmcp != NULL) && (bdip != NULL), "mmcGetInfo");
00828 
00829   if (mmcp->state != BLK_READY)
00830     return CH_FAILED;
00831 
00832   bdip->blk_num  = mmcp->capacity;
00833   bdip->blk_size = MMCSD_BLOCK_SIZE;
00834 
00835   return CH_SUCCESS;
00836 }
00837 
00838 /**
00839  * @brief   Erases blocks.
00840  *
00841  * @param[in] mmcp      pointer to the @p MMCDriver object
00842  * @param[in] startblk  starting block number
00843  * @param[in] endblk    ending block number
00844  *
00845  * @return              The operation status.
00846  * @retval CH_SUCCESS   the operation succeeded.
00847  * @retval CH_FAILED    the operation failed.
00848  *
00849  * @api
00850  */
00851 bool_t mmcErase(MMCDriver *mmcp, uint32_t startblk, uint32_t endblk) {
00852 
00853   chDbgCheck((mmcp != NULL), "mmcErase");
00854 
00855   /* Erase operation in progress.*/
00856   mmcp->state = BLK_WRITING;
00857 
00858   /* Handling command differences between HC and normal cards.*/
00859   if (!mmcp->block_addresses) {
00860     startblk *= MMCSD_BLOCK_SIZE;
00861     endblk *= MMCSD_BLOCK_SIZE;
00862   }
00863 
00864   if (send_command_R1(mmcp, MMCSD_CMD_ERASE_RW_BLK_START, startblk))
00865     goto failed;
00866 
00867   if (send_command_R1(mmcp, MMCSD_CMD_ERASE_RW_BLK_END, endblk))
00868     goto failed;
00869 
00870   if (send_command_R1(mmcp, MMCSD_CMD_ERASE, 0))
00871     goto failed;
00872 
00873   mmcp->state = BLK_READY;
00874   return CH_SUCCESS;
00875 
00876   /* Command failed, state reset to BLK_ACTIVE.*/
00877 failed:
00878   spiStop(mmcp->config->spip);
00879   mmcp->state = BLK_READY;
00880   return CH_FAILED;
00881 }
00882 
00883 #endif /* HAL_USE_MMC_SPI */
00884 
00885 /** @} */