|
ChibiOS/RT
2.6.0 |
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 /** @} */