| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205 |
- /*
- * Copyright (C) 2005-2007 by egnite Software GmbH. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the copyright holders nor the names of
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * For additional information see http://www.ethernut.de/
- */
- /*!
- * \brief Basic block device driver for multimedia cards.
- *
- * The driver uses SPI mode, but doesn't include any low level hardware
- * access. This must be provided by some additional routines.
- *
- * \verbatim
- *
- * $Log$
- * Revision 1.15 2009/02/13 14:52:05 haraldkipp
- * Include memdebug.h for heap management debugging support.
- *
- * Revision 1.14 2009/02/06 15:40:29 haraldkipp
- * Using newly available strdup() and calloc().
- * Replaced NutHeap routines by standard malloc/free.
- * Replaced pointer value 0 by NULL.
- *
- * Revision 1.13 2009/01/17 11:26:46 haraldkipp
- * Getting rid of two remaining BSD types in favor of stdint.
- * Replaced 'u_int' by 'unsinged int' and 'uptr_t' by 'uintptr_t'.
- *
- * Revision 1.12 2008/08/11 06:59:42 haraldkipp
- * BSD types replaced by stdint types (feature request #1282721).
- *
- * Revision 1.11 2008/07/14 13:09:30 haraldkipp
- * Allow small MultiMedia Cards without partition table.
- *
- * Revision 1.10 2007/08/30 12:15:06 haraldkipp
- * Configurable MMC timings.
- *
- * Revision 1.9 2006/10/08 16:48:09 haraldkipp
- * Documentation fixed
- *
- * Revision 1.8 2006/07/05 08:03:12 haraldkipp
- * Bugfix. Trailing slash in mount path not properly handled.
- * Thanks to Michael Fischer.
- *
- * Revision 1.7 2006/06/18 16:34:46 haraldkipp
- * Mutex deadlock fixed.
- *
- * Revision 1.6 2006/05/25 09:34:21 haraldkipp
- * Added mutual exclusion lock for multithreaded access.
- *
- * Revision 1.5 2006/04/07 12:29:03 haraldkipp
- * Number of read retries increased. Memory hole fixed.
- * Added ioctl(NUTBLKDEV_MEDIAAVAIL).
- * Card change ioctl() will also return 1 if no card is available.
- *
- * Revision 1.4 2006/02/23 15:43:56 haraldkipp
- * Timeout value increased. Some cards have long write latencies.
- *
- * Revision 1.3 2006/01/22 17:36:31 haraldkipp
- * Some cards need more time to enter idle state.
- * Card access now returns an error after card change detection.
- *
- * Revision 1.2 2006/01/19 18:40:08 haraldkipp
- * Timeouts increased and long time sleeps decreased for better performance.
- *
- * Revision 1.1 2006/01/05 16:30:49 haraldkipp
- * First check-in.
- *
- *
- * \endverbatim
- */
- #include <cfg/os.h>
- #include <cfg/mmci.h>
- #if defined(NUTDEBUG)
- #include <stdio.h>
- #endif
- #include <errno.h>
- #include <string.h>
- #include <stdlib.h>
- #include <memdebug.h>
- #include <sys/heap.h>
- #include <sys/timer.h>
- #include <sys/event.h>
- #include <fs/dospart.h>
- #include <fs/fs.h>
- #include <dev/blockdev.h>
- #include <dev/mmcard.h>
- /*!
- * \addtogroup xgMmCard
- */
- /*@{*/
- #ifndef MMC_BLOCK_SIZE
- /*!
- * \brief Block size.
- *
- * Block size in bytes. Do not change unless you are sure that both,
- * the file system and the hardware support it.
- */
- #define MMC_BLOCK_SIZE 512
- #endif
- #ifndef MMC_MAX_INIT_POLLS
- /*!
- * \brief Card init timeout.
- *
- * Max. number of loops waiting for card's idle mode after initialization.
- * An additional delay of 1 ms is added to each loop after one quarter of
- * this value elapsed.
- */
- #define MMC_MAX_INIT_POLLS 512
- #endif
- #ifndef MMC_MAX_INIT_POLLS_SDHC
- /*!
- * \brief Card init timeout for SDHC cards.
- */
- #define MMC_MAX_INIT_POLLS_SDHC 4096
- #endif
- #ifndef MMC_MAX_RESET_POLLS
- /*!
- * \brief Card reset timeout.
- *
- * Max. number of loops waiting for card's idle mode after resetting it.
- */
- #define MMC_MAX_RESET_POLLS 255
- #endif
- #ifndef MMC_MAX_WRITE_POLLS
- /*!
- * \brief Card write timeout.
- *
- * Max. number of loops waiting for card's idle mode after resetting it.
- * An additional delay of 1 ms is added to each loop after 31/32 of this
- * value elapsed.
- */
- #define MMC_MAX_WRITE_POLLS 1024
- #endif
- #ifndef MMC_MAX_WRITE_RETRIES
- /*!
- * \brief Card write retries.
- *
- * Max. number of retries while writing.
- */
- #define MMC_MAX_WRITE_RETRIES 32
- #endif
- #ifndef MMC_MAX_READ_RETRIES
- /*!
- * \brief Card read retries.
- *
- * Max. number of retries while reading.
- */
- #define MMC_MAX_READ_RETRIES 8
- #endif
- #ifndef MMC_MAX_REG_POLLS
- /*!
- * \brief Register read timeout.
- *
- * Max. number of loops while reading a card's register.
- */
- #define MMC_MAX_REG_POLLS 512
- #endif
- #ifndef MMC_MAX_CMDACK_POLLS
- /*!
- * \brief Command acknowledge timeout.
- *
- * Max. number of loops waiting for card's acknowledge of a command.
- * An additional delay of 1 ms is added to each loop after three quarter
- * of this value elapsed.
- */
- #define MMC_MAX_CMDACK_POLLS 1024
- #endif
- #ifndef MMC_MAX_R1_POLLS
- /*!
- * \brief R1 response timeout.
- *
- * Max. number of loops waiting for card's R1 response.
- */
- #define MMC_MAX_R1_POLLS 1024
- #endif
- /*!
- * \brief Local multimedia card mount information.
- */
- typedef struct _MMCFCB {
- /*! \brief Attached file system device.
- */
- NUTDEVICE *fcb_fsdev;
- /*! \brief Partition table entry of the currently mounted partition.
- */
- DOSPART fcb_part;
- /*! \brief Next block number to read.
- *
- * The file system driver will send a NUTBLKDEV_SEEK control command
- * to set this value before calling the read or the write routine.
- *
- * The number is partition relative.
- */
- uint32_t fcb_blknum;
- /*! \brief Internal block buffer.
- *
- * A file system driver may use this one or optionally provide it's
- * own buffers.
- *
- * Minimal systems may share their external bus interface with
- * device I/O lines, in which case the buffer must be located
- * in internal memory.
- */
- uint8_t fcb_blkbuf[MMC_BLOCK_SIZE];
- } MMCFCB;
- /*
- * Several routines call NutSleep, which results in a context switch.
- * This mutual exclusion semaphore takes care, that multiple threads
- * do not interfere with each other.
- */
- static HANDLE mutex;
- /*!
- * \brief Send command to multimedia card.
- *
- * \param ifc Specifies the hardware interface.
- * \param cmd Command code. See MMCMD_ macros.
- * \param param Optional command parameter.
- */
- static void MmCardTxCmd(MMCIFC * ifc, uint8_t cmd, uint32_t param)
- {
- unsigned int tmo = MMC_MAX_CMDACK_POLLS;
- uint8_t ch;
- /* Enable card select. */
- (*ifc->mmcifc_cs) (1);
- /*
- * Repeat sending nothing until we receive nothing. Actually
- * it should be sufficient to send a single 0xFF value, but
- * running a loop seems to fix certain kind of sync problems.
- */
- while ((ch = (*ifc->mmcifc_io) (0xFF)) != 0xFF) {
- if (--tmo == 0) {
- #ifdef NUTDEBUG
- printf("[MMCmd%u Timeout %02X]\n", cmd, ch);
- #endif
- break;
- }
- if (tmo < MMC_MAX_CMDACK_POLLS / 4) {
- NutSleep(1);
- }
- }
- /* Send command and parameter. */
- (*ifc->mmcifc_io) (MMCMD_HOST | cmd);
- (*ifc->mmcifc_io) ((uint8_t) (param >> 24));
- (*ifc->mmcifc_io) ((uint8_t) (param >> 16));
- (*ifc->mmcifc_io) ((uint8_t) (param >> 8));
- (*ifc->mmcifc_io) ((uint8_t) param);
- /*
- * We are running with CRC disabled. However, the reset and excsd commands
- * must be send with a valid CRC. Fortunately these commands are sent with a
- * fixed parameter value of zero, which results in a fixed CRC value
- */
- if (cmd == MMCMD_SEND_IF_COND) {
- (*ifc->mmcifc_io) (MMCMD_IF_COND_CRC);
- } else {
- (*ifc->mmcifc_io) (MMCMD_RESET_CRC);
- }
- }
- /*!
- * \brief Receive an R1 response token from the card.
- *
- * In SPI mode, the card sends an R1 response after every command except
- * after the SEND_STATUS and the READ_OCR commands.
- *
- * \param ifc Specifies the hardware interface.
- *
- * \return R1 response token or 0xFF on timeout.
- */
- static uint8_t MmCardRxR1(MMCIFC * ifc)
- {
- uint8_t rc;
- int i;
- for (i = 0; i < MMC_MAX_R1_POLLS; i++) {
- if ((rc = (*ifc->mmcifc_io) (0xFF)) != 0xFF) {
- break;
- }
- }
- return rc;
- }
- /*!
- * \brief Receive an R2 response token from the card.
- *
- * In SPI mode the card sends this token in response to the SEND_STATUS
- * command.
- *
- * \param ifc Specifies the hardware interface.
- *
- * \return R2 response token or 0xFFFF on timeout.
- */
- static uint16_t MmCardRxR2(MMCIFC * ifc)
- {
- uint16_t rc;
- rc = MmCardRxR1(ifc);
- rc <<= 8;
- rc += (*ifc->mmcifc_io) (0xFF);
- return rc;
- }
- /*!
- * \brief Receive an R3 response token from the card.
- *
- * In SPI mode the card sends this token in response to the READ_OCR
- * command.
- *
- * \param ifc Specifies the hardware interface.
- * \param ocr Points to a buffer which receives the OCR register contents.
- *
- * \return R1 response token or 0xFF on timeout.
- */
- static uint8_t MmCardRxR3(MMCIFC * ifc, uint32_t * ocr)
- {
- uint8_t rc;
- int i;
- /* The first byte is equal to the R1 response. */
- rc = MmCardRxR1(ifc);
- /* Receive the operating condition. */
- for (i = 0; i < 4; i++) {
- *ocr <<= 8;
- *ocr |= (*ifc->mmcifc_io) (0xFF);
- }
- return rc;
- }
- /*!
- * \brief Configure card for SPI mode.
- *
- * \param ifc Specifies the hardware interface.
- *
- * \return 0 on success, -1 otherwise.
- */
- static int MmCardReset(MMCIFC * ifc)
- {
- int i;
- uint8_t rsp;
- /*
- * Initialize the low level card interface.
- */
- if ((*ifc->mmcifc_in) ()) {
- return -1;
- }
- /*
- * 80 bits of ones with deactivated chip select will put the card
- * in SPI mode.
- *
- * /note that spec 2.0 tells us that we need to send the CMD0 with CS inactive
- * to get the card in SPI-mode. This 'old' methode however still seems to work
- * with 2.0 cards
- */
- (*ifc->mmcifc_cs) (0);
- for (i = 0; i < 10; i++) {
- (*ifc->mmcifc_io) (0xFF);
- }
- /*
- * Switch to idle state and wait until initialization is running
- * or idle state is reached.
- */
- for (i = 0; i < MMC_MAX_RESET_POLLS; i++) {
- MmCardTxCmd(ifc, MMCMD_GO_IDLE_STATE, 0);
- rsp = MmCardRxR1(ifc);
- (*ifc->mmcifc_cs) (0);
- if (rsp == MMR1_IDLE_STATE || rsp == MMR1_NOT_IDLE) {
- return 0;
- }
- }
- return -1;
- }
- /*!
- * \brief Initialize the multimedia card.
- *
- * This routine will put a newly powered up card into SPI mode.
- * It is called by MmCardMount().
- *
- * \param ifc Specifies the hardware interface.
- *
- * \return 0 on success, -1 otherwise.
- */
- static int MmCardInit(MMCIFC * ifc)
- {
- int i;
- uint8_t rsp;
- uint8_t ocr[4];
- /*
- * assume BLOCK_MODE addressing for most cards.
- * if the cards turns out to be a SD_HC card, the mode will
- * be set to BYTE_MODE later on
- */
- (*ifc->mmcifc_sm) (MMC_BLOCK_MODE);
- /*
- * Try to switch to SPI mode. Looks like a retry helps to fix
- * certain synchronization problems.
- */
- if (MmCardReset(ifc)) {
- if (MmCardReset(ifc)) {
- #ifdef NUTDEBUG
- printf("[CardReset failed]");
- #endif
- return -1;
- }
- }
- /*
- * Let's check if we deal with a card that
- * support the 2.0 specs by sending the EXTCSD command.
- * If the card response is "illegal Command", we know
- * that the 2.0 specs are not supported.
- * Note that SD-HC cards always support the 2.0 specs
- */
- MmCardTxCmd(ifc, MMCMD_SEND_IF_COND, 0x1AA);
- rsp = MmCardRxR1(ifc);
- /* Receive the data. */
- for (i = 0; i < 4; i++) {
- ocr[i] = (*ifc->mmcifc_io) (0xFF);
- }
- if ((ocr[2] == 0x01) && (ocr[3] == 0xAA)) {
- // The card can work at vdd range of 2.7-3.6V
- #ifdef NUTDEBUG
- printf("[SD -> support for 2.0 specs]");
- #endif
- /*
- * Wait for a really long time until card is initialized
- * and enters idle state.
- */
- for (i = 0; i < MMC_MAX_INIT_POLLS_SDHC; i++) {
- MmCardTxCmd(ifc, MMCMD_SEND_APP_CMD, 0);
- if (MmCardRxR1(ifc) <= MMR1_NOT_IDLE) {
- /* some cards really need a lot of time for their initialisation... */
- NutSleep(10);
- /* ACMD41 with HCS bit */
- MmCardTxCmd(ifc, MMCMD_SEND_APP_OP_COND, 1UL << 30);
- /* check response */
- if (MmCardRxR1(ifc)==MMR1_IDLE_STATE) {
- #ifdef NUTDEBUG
- printf("[CardIdle]");
- #endif
- // Check the CSS bit that tells us if we are dealing with HC-cards
- MmCardTxCmd(ifc, MMCMD_READ_OCR, 0);
- rsp = MmCardRxR1(ifc);
- /* Receive the data. */
- for (i = 0; i < 4; i++) {
- ocr[i] = (*ifc->mmcifc_io) (0xFF);
- }
- if (ocr[0] & 0x40) {
- /* set byte addressing mode for SD-HC cards */
- (*ifc->mmcifc_sm) (MMC_BYTE_MODE);
- #ifdef NUTDEBUG
- printf("[Found SD-HC]");
- #endif
- }
- /* Initialize MMC access mutex semaphore. */
- NutEventPost(&mutex);
- return 0; /* initialisation finished */
- }
- }
- }
- /* Card stays in IDLE state for some reason... */
- #ifdef NUTDEBUG
- printf("[CardInit failed [%d]]", rsp);
- #endif
- return(-1);
- }
- else {
- /*
- * Wait for a really long time until card is initialized
- * and enters idle state.
- */
- for (i = 0; i < MMC_MAX_INIT_POLLS; i++) {
- /*
- * In SPI mode SEND_OP_COND is a dummy, used to poll the card
- * for initialization finished. Thus, there are no parameters
- * and no operation condition data is sent back.
- */
- MmCardTxCmd(ifc, MMCMD_SEND_OP_COND, 0);
- rsp = MmCardRxR1(ifc);
- (*ifc->mmcifc_cs) (0);
- if (rsp == MMR1_IDLE_STATE) {
- #ifdef NUTDEBUG
- printf("[CardIdle]");
- #endif
- /* Initialize MMC access mutex semaphore. */
- NutEventPost(&mutex);
- return 0;
- }
- if (i > MMC_MAX_INIT_POLLS / 4) {
- NutSleep(1);
- }
- }
- #ifdef NUTDEBUG
- printf("[CardInit failed [%d]]", rsp);
- #endif
- }
- return -1;
- }
- /*!
- * \brief Read or verify a single block.
- *
- * \param ifc Specifies the hardware interface.
- * \param blk Block number to read or verify.
- * \param buf Data buffer. Receives the data or is verified against the
- * data being read from the card.
- *
- * \return 0 on success, -1 otherwise.
- */
- static int MmCardReadOrVerify(MMCIFC * ifc, uint32_t blk, uint8_t * buf, int vflg)
- {
- int rc = -1;
- int retries = 64;
- int i;
- uint8_t rsp;
- /* Gain mutex access. */
- NutEventWait(&mutex, 0);
- /*
- * SD_HC cards use BYTE_ADDRESSING, which means that the partion info
- * that indicates the starting address of the BOOT_RECORD is in bytes (and
- * not in sectors like with non SD_HC cards).
- * Please note that the sector size is not officially known at this stage since
- * this code does NOT read the CDS register. However, for SD-HC cards fortunately
- * it is defined that the sectorsize is always 512.
- */
- if ((*ifc->mmcifc_gm) () == MMC_BLOCK_MODE) {
- blk <<= 9;
- }
- while (retries--) {
- MmCardTxCmd(ifc, MMCMD_READ_SINGLE_BLOCK, blk);
- if ((rsp = MmCardRxR1(ifc)) == 0x00) {
- if ((rsp = MmCardRxR1(ifc)) == 0xFE) {
- rc = 0;
- if (vflg) {
- for (i = 0; i < MMC_BLOCK_SIZE; i++) {
- if (*buf != (*ifc->mmcifc_io) (0xFF)) {
- rc = -1;
- }
- buf++;
- }
- } else {
- for (i = 0; i < MMC_BLOCK_SIZE; i++) {
- *buf = (*ifc->mmcifc_io) (0xFF);
- buf++;
- }
- }
- (*ifc->mmcifc_io) (0xff);
- (*ifc->mmcifc_io) (0xff);
- (*ifc->mmcifc_cs) (0);
- break;
- }
- }
- (*ifc->mmcifc_cs) (0);
- }
- /* Release mutex access. */
- NutEventPost(&mutex);
- return rc;
- }
- /*!
- * \brief Write a single block.
- *
- * \param ifc Specifies the hardware interface.
- * \param blk Block number to read or verify.
- * \param buf Pointer to a buffer which holds the data to write.
- *
- * \return 0 on success, -1 otherwise.
- */
- static int MmCardWrite(MMCIFC * ifc, uint32_t blk, const uint8_t * buf)
- {
- int rc = -1;
- int retries = MMC_MAX_WRITE_RETRIES;
- int tmo;
- int i;
- uint8_t rsp;
- /* Gain mutex access. */
- NutEventWait(&mutex, 0);
- /*
- * SD_HC cards use BYTE_ADDRESSING, which means that the partion info
- * that indicates the starting address of the BOOT_RECORD is in bytes (and
- * not in sectors like with non SD_HC cards).
- * Please note that the sector size is not officially known at this stage since
- * this code does NOT read the CDS register. However, for SD-HC cards fortunately
- * it is defined that the sectorsize is always 512.
- */
- if ((*ifc->mmcifc_gm) () == MMC_BLOCK_MODE) {
- blk <<= 9;
- }
- while (retries--) {
- MmCardTxCmd(ifc, MMCMD_WRITE_BLOCK, blk);
- if ((rsp = MmCardRxR1(ifc)) == 0x00) {
- (*ifc->mmcifc_io) (0xFF);
- (*ifc->mmcifc_io) (0xFE);
- for (i = 0; i < MMC_BLOCK_SIZE; i++) {
- (*ifc->mmcifc_io) (*buf);
- buf++;
- }
- // (*ifc->mmcifc_io)(0xFF);
- // (*ifc->mmcifc_io)(0xFF);
- if ((rsp = MmCardRxR1(ifc)) == 0xE5) {
- for (tmo = 0; tmo < MMC_MAX_WRITE_POLLS; tmo++) {
- if ((*ifc->mmcifc_io) (0xFF) == 0xFF) {
- break;
- }
- if (tmo > MMC_MAX_WRITE_POLLS - MMC_MAX_WRITE_POLLS / 32) {
- NutSleep(1);
- }
- }
- if (tmo) {
- rc = 0;
- break;
- }
- #ifdef NUTDEBUG
- printf("[MMCWR Timeout]\n");
- #endif
- }
- }
- (*ifc->mmcifc_cs) (0);
- }
- (*ifc->mmcifc_cs) (0);
- /* Release mutex access. */
- NutEventPost(&mutex);
- return rc;
- }
- /*!
- * \brief Read data blocks from a mounted partition.
- *
- * Applications should not call this function directly, but use the
- * stdio interface.
- *
- * \param nfp Pointer to a ::NUTFILE structure, obtained by a previous
- * call to MmCardMount().
- * \param buffer Pointer to the data buffer to fill.
- * \param num Maximum number of blocks to read. However, reading
- * multiple blocks is not yet supported by this driver.
- *
- * \return The number of blocks actually read. A return value of -1
- * indicates an error.
- */
- int MmCardBlockRead(NUTFILE * nfp, void *buffer, int num)
- {
- MMCFCB *fcb = (MMCFCB *) nfp->nf_fcb;
- uint32_t blk = fcb->fcb_blknum;
- NUTDEVICE *dev = (NUTDEVICE *) nfp->nf_dev;
- MMCIFC *ifc = (MMCIFC *) dev->dev_icb;
- if ((*ifc->mmcifc_cd) () != 1) {
- return -1;
- }
- if (buffer == 0) {
- buffer = fcb->fcb_blkbuf;
- }
- /*
- * when using the filesystem, the sectornumbering is different then when
- * directly accesing the card. For example, the MBR can be found at the
- * sector 0 of the card, but the filesystem's first sector is the sector
- * where the start is of the FAT VolumeID (also called the BOOT SECTOR).
- * This position (or offset) is indicated by reading the partion-table,
- * more specific: by reading the LBA begin info.
- * This offset we need to add here to the sector# we get in as
- * parameter. This way we acces the real sector on the card.
- *
- */
- blk += fcb->fcb_part.part_sect_offs;
- #ifdef MMC_VERIFY_AFTER
- {
- int i;
- /*
- * It would be much better to verify the checksum than to re-read
- * and verify the data block.
- */
- for (i = 0; i < MMC_MAX_READ_RETRIES; i++) {
- if (MmCardReadOrVerify(ifc, blk, buffer, 0) == 0) {
- if (MmCardReadOrVerify(ifc, blk, buffer, 1) == 0) {
- return 1;
- }
- }
- }
- }
- #else
- if (MmCardReadOrVerify(ifc, blk, buffer, 0) == 0) {
- return 1;
- }
- #endif
- return -1;
- }
- /*!
- * \brief Write data blocks to a mounted partition.
- *
- * Applications should not call this function directly, but use the
- * stdio interface.
- *
- * \param nfp Pointer to a \ref NUTFILE structure, obtained by a previous
- * call to MmCardMount().
- * \param buffer Pointer to the data to be written.
- * \param num Maximum number of blocks to write. However, writing
- * multiple blocks is not yet supported by this driver.
- *
- * \return The number of blocks written. A return value of -1 indicates an
- * error.
- */
- int MmCardBlockWrite(NUTFILE * nfp, const void *buffer, int num)
- {
- MMCFCB *fcb = (MMCFCB *) nfp->nf_fcb;
- uint32_t blk = fcb->fcb_blknum;
- NUTDEVICE *dev = (NUTDEVICE *) nfp->nf_dev;
- MMCIFC *ifc = (MMCIFC *) dev->dev_icb;
- if ((*ifc->mmcifc_cd) () != 1) {
- return -1;
- }
- if (buffer == 0) {
- buffer = fcb->fcb_blkbuf;
- }
- blk += fcb->fcb_part.part_sect_offs;
- #ifdef MMC_VERIFY_AFTER
- {
- int i;
- for (i = 0; i < MMC_MAX_READ_RETRIES; i++) {
- if (MmCardWrite(ifc, blk, buffer) == 0) {
- if (MmCardReadOrVerify(ifc, blk, (void *) buffer, 1) == 0) {
- return 1;
- }
- if (MmCardReadOrVerify(ifc, blk, (void *) buffer, 1) == 0) {
- return 1;
- }
- }
- }
- }
- #else
- if (MmCardWrite(ifc, blk, buffer) == 0) {
- return 1;
- }
- #endif
- return -1;
- }
- #ifdef __HARVARD_ARCH__
- /*!
- * \brief Write data blocks from program space to a mounted partition.
- *
- * This function is not yet implemented and will always return -1.
- *
- * Similar to MmCardBlockWrite() except that the data is located in
- * program memory.
- *
- * Applications should not call this function directly, but use the
- * stdio interface.
- *
- * \param nfp File pointer to a previously opened device.
- * \param buffer Pointer to the data bytes in program space to be written.
- * \param num Maximum number of blocks to write. However, writing
- * multiple blocks is not yet supported by this driver.
- *
- * \return The number of blocks written. A return value of -1 indicates an
- * error.
- */
- int MmCardBlockWrite_P(NUTFILE * nfp, PGM_P buffer, int num)
- {
- return -1;
- }
- #endif
- /*!
- * \brief Mount a partition.
- *
- * Nut/OS doesn't provide specific routines for mounting. Instead routines
- * for opening files are used.
- *
- * Applications should not directly call this function, but use the high
- * level stdio routines for opening a file.
- *
- * \param dev Pointer to the MMC device.
- * \param name Partition number followed by a slash followed by a name
- * of the file system device. Both items are optional. If no
- * file system driver name is given, the first file system
- * driver found in the list of registered devices will be
- * used. If no partition number is specified or if partition
- * zero is given, the first active primary partition will be
- * used.
- * \param mode Opening mode. Currently ignored, but
- * \code _O_RDWR | _O_BINARY \endcode should be used for
- * compatibility with future enhancements.
- * \param acc File attributes, ignored.
- *
- * \return Pointer to a newly created file pointer to the mounted
- * partition or NUTFILE_EOF in case of any error.
- */
- NUTFILE *MmCardMount(NUTDEVICE * dev, const char *name, int mode, int acc)
- {
- int partno = 0;
- int i;
- NUTDEVICE *fsdev;
- NUTFILE *nfp;
- MMCFCB *fcb;
- DOSPART *part;
- MMCIFC *ifc = (MMCIFC *) dev->dev_icb;
- FSCP_VOL_MOUNT mparm;
- /* Return an error if no card is detected. */
- if ((*ifc->mmcifc_cd) () == 0) {
- errno = ENODEV;
- return NUTFILE_EOF;
- }
- /* Set the card in SPI mode and check for SD-HC cards. */
- if (MmCardInit(ifc)) {
- errno = ENODEV;
- return NUTFILE_EOF;
- }
- /* Parse the name for a partition number and a file system driver. */
- if (*name) {
- partno = atoi(name);
- do {
- name++;
- } while (*name && *name != '/');
- if (*name == '/') {
- name++;
- }
- }
- /*
- * Check the list of registered devices for the given name of the
- * files system driver. If none has been specified, get the first
- * file system driver in the list. Hopefully the application
- * registered one only.
- */
- for (fsdev = nutDeviceList; fsdev; fsdev = fsdev->dev_next) {
- if (*name == 0) {
- if (fsdev->dev_type == IFTYP_FS) {
- break;
- }
- } else
- if (strcmp(fsdev->dev_name, name) == 0) {
- break;
- }
- }
- if (fsdev == 0) {
- #ifdef NUTDEBUG
- printf("[No FSDriver]");
- #endif
- errno = ENODEV;
- return NUTFILE_EOF;
- }
- if ((fcb = calloc(1, sizeof(MMCFCB))) == 0) {
- errno = ENOMEM;
- return NUTFILE_EOF;
- }
- fcb->fcb_fsdev = fsdev;
- /* Read MBR. */
- if (MmCardReadOrVerify(ifc, 0, fcb->fcb_blkbuf, 0)) {
- free(fcb);
- return NUTFILE_EOF;
- }
- /* Check for the cookie at the end of this sector. */
- if (fcb->fcb_blkbuf[DOSPART_MAGICPOS] != 0x55 || fcb->fcb_blkbuf[DOSPART_MAGICPOS + 1] != 0xAA) {
- #ifdef NUTDEBUG
- printf("[MBR corrupted]");
- #endif
- free(fcb);
- return NUTFILE_EOF;
- }
- /* Check for the partition table. */
- if(fcb->fcb_blkbuf[DOSPART_TYPEPOS] == 'F' &&
- fcb->fcb_blkbuf[DOSPART_TYPEPOS + 1] == 'A' &&
- fcb->fcb_blkbuf[DOSPART_TYPEPOS + 2] == 'T') {
- /* No partition table. Assume FAT12 and 32MB size. */
- fcb->fcb_part.part_type = PTYPE_FAT12;
- fcb->fcb_part.part_sect_offs = 0;
- fcb->fcb_part.part_sects = 65536; /* How to find out? */
- }
- else {
- /* Read partition table. */
- part = (DOSPART *) & fcb->fcb_blkbuf[DOSPART_SECTORPOS];
- for (i = 1; i <= 4; i++) {
- if (partno) {
- if (i == partno) {
- /* Found specified partition number. */
- fcb->fcb_part = *part;
- break;
- }
- } else if (part->part_state & 0x80) {
- /* Located first active partition. */
- fcb->fcb_part = *part;
- break;
- }
- part++;
- }
- if (fcb->fcb_part.part_type == PTYPE_EMPTY) {
- free(fcb);
- return NUTFILE_EOF;
- }
- }
- if ((nfp = NutHeapAlloc(sizeof(NUTFILE))) == 0) {
- free(fcb);
- errno = ENOMEM;
- return NUTFILE_EOF;
- }
- nfp->nf_dev = dev;
- nfp->nf_fcb = fcb;
- /*
- * Mount the file system volume.
- */
- mparm.fscp_bmnt = nfp;
- mparm.fscp_part_type = fcb->fcb_part.part_type;
- if (fsdev->dev_ioctl(fsdev, FS_VOL_MOUNT, &mparm)) {
- MmCardUnmount(nfp);
- return NUTFILE_EOF;
- }
- return nfp;
- }
- /*!
- * \brief Unmount a previously mounted partition.
- *
- * Applications should not directly call this function, but use the high
- * level stdio routines for closing a previously opened file.
- *
- * \return 0 on success, -1 otherwise.
- */
- int MmCardUnmount(NUTFILE * nfp)
- {
- int rc = -1;
- if (nfp) {
- MMCFCB *fcb = (MMCFCB *) nfp->nf_fcb;
- if (fcb) {
- NUTDEVICE *dev = (NUTDEVICE *) nfp->nf_dev;
- MMCIFC *ifc = (MMCIFC *) dev->dev_icb;
- if ((*ifc->mmcifc_cd) () == 1) {
- rc = fcb->fcb_fsdev->dev_ioctl(fcb->fcb_fsdev, FS_VOL_UNMOUNT, NULL);
- }
- free(fcb);
- }
- free(nfp);
- }
- return rc;
- }
- /*!
- * \brief Retrieve contents of a card register.
- *
- * \param ifc Specifies the hardware interface.
- * \param cmd This command is sent to the card to retrieve the contents
- * of a specific register.
- * \param rbp Pointer to the buffer that receives the register contents.
- * \param siz Size of the specified register.
- *
- * \return 0 on success, -1 otherwise.
- */
- static int MmCardGetReg(MMCIFC * ifc, uint8_t cmd, uint8_t * rbp, int siz)
- {
- int rc = -1;
- int retries = MMC_MAX_REG_POLLS;
- int i;
- /* Gain mutex access. */
- NutEventWait(&mutex, 0);
- while (retries--) {
- /* Send the command to the card. This will select the card. */
- MmCardTxCmd(ifc, cmd, 0);
- /* Wait for OK response from the card. */
- if (MmCardRxR1(ifc) == 0x00) {
- /* Wait for data from the card. */
- if (MmCardRxR1(ifc) == 0xFE) {
- for (i = 0; i < siz; i++) {
- *rbp++ = (*ifc->mmcifc_io) (0xFF);
- }
- /* Ignore the CRC. */
- (*ifc->mmcifc_io) (0xFF);
- (*ifc->mmcifc_io) (0xFF);
- /* De-activate card selection. */
- (*ifc->mmcifc_cs) (0);
- /* Return a positive result. */
- rc = 0;
- break;
- }
- }
- /* De-activate card selection. */
- (*ifc->mmcifc_cs) (0);
- }
- /* Release mutex access. */
- NutEventPost(&mutex);
- return rc;
- }
- /*!
- * \brief Perform MMC control functions.
- *
- * This function is called by the ioctl() function of the C runtime
- * library. Applications should not directly call this function.
- *
- * \todo Card change detection should verify the serial card number.
- *
- * \param dev Identifies the device that receives the device-control
- * function.
- * \param req Requested control function. May be set to one of the
- * following constants:
- * - \ref NUTBLKDEV_MEDIACHANGE
- * - \ref NUTBLKDEV_INFO
- * - \ref NUTBLKDEV_SEEK
- * - \ref MMCARD_GETCID
- * - \ref MMCARD_GETCSD
- *
- * \param conf Points to a buffer that contains any data required for
- * the given control function or receives data from that
- * function.
- * \return 0 on success, -1 otherwise.
- */
- int MmCardIOCtl(NUTDEVICE * dev, int req, void *conf)
- {
- int rc = 0;
- MMCIFC *ifc = (MMCIFC *) dev->dev_icb;
- switch (req) {
- case NUTBLKDEV_MEDIAAVAIL:
- {
- int *flg = (int *) conf;
- *flg = (*ifc->mmcifc_cd) ();
- }
- break;
- case NUTBLKDEV_MEDIACHANGE:
- {
- int *flg = (int *) conf;
- if ((*ifc->mmcifc_cd) () != 1) {
- *flg = 1;
- } else {
- *flg = 0;
- }
- }
- break;
- case NUTBLKDEV_INFO:
- {
- BLKPAR_INFO *par = (BLKPAR_INFO *) conf;
- MMCFCB *fcb = (MMCFCB *) par->par_nfp->nf_fcb;
- /*
- * note that we don't read the card's CSD-register for this info,
- * in stead, we use the info that we found in the partition table of
- * the formatted card.
- */
- par->par_nblks = fcb->fcb_part.part_sects;
- par->par_blksz = MMC_BLOCK_SIZE;
- par->par_blkbp = fcb->fcb_blkbuf;
- }
- break;
- case NUTBLKDEV_SEEK:
- {
- BLKPAR_SEEK *par = (BLKPAR_SEEK *) conf;
- MMCFCB *fcb = (MMCFCB *) par->par_nfp->nf_fcb;
- fcb->fcb_blknum = par->par_blknum;
- }
- break;
- case MMCARD_GETSTATUS:
- {
- uint16_t *s = (uint16_t *) conf;
- /* Gain mutex access. */
- NutEventWait(&mutex, 0);
- MmCardTxCmd(ifc, MMCMD_SEND_STATUS, 0);
- *s = MmCardRxR2(ifc);
- /* Release mutex access. */
- NutEventPost(&mutex);
- }
- break;
- case MMCARD_GETOCR:
- /* Gain mutex access. */
- NutEventWait(&mutex, 0);
- MmCardTxCmd(ifc, MMCMD_READ_OCR, 0);
- if (MmCardRxR3(ifc, (uint32_t *) conf) != MMR1_IDLE_STATE) {
- rc = -1;
- }
- /* Release mutex access. */
- NutEventPost(&mutex);
- break;
- case MMCARD_GETCID:
- rc = MmCardGetReg(ifc, MMCMD_SEND_CID, (uint8_t *) conf, sizeof(MMC_CID));
- break;
- case MMCARD_GETCSD:
- rc = MmCardGetReg(ifc, MMCMD_SEND_CSD, (uint8_t *) conf, sizeof(MMC_CSD));
- break;
- default:
- rc = -1;
- break;
- }
- return rc;
- }
- /*!
- * \brief Initialize high level MMC driver.
- *
- * Applications should not directly call this function. It is
- * automatically executed during during device registration by
- * NutRegisterDevice().
- *
- * \param dev Identifies the device to initialize.
- *
- * \return Always zero.
- */
- int MmCardDevInit(NUTDEVICE * dev)
- {
- return 0;
- }
- /*@}*/
|