| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035 |
- /*
- * Copyright (C) 2010 by egnite 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 SPI bus block device driver for multimedia cards.
- *
- * The driver provides generic memory card access, but doesn't include
- * low level hardware support like card detection. This must be provided
- * by a hardware specific support module.
- *
- * \verbatim
- *
- * $Id$
- *
- * \endverbatim
- */
- #include <cfg/mmci.h>
- #include <sys/nutdebug.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/spibus.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_RESET_RETRIES
- /*!
- * \brief Card reset timeout.
- *
- * Max. number of loops waiting for card's idle mode after resetting it.
- */
- #define MMC_MAX_RESET_RETRIES 2
- #endif
- #ifndef MMC_MAX_WRITE_RETRIES
- /*!
- * \brief Card write retries.
- *
- * Max. number of retries while writing.
- */
- #define MMC_MAX_WRITE_RETRIES 2
- #endif
- #ifndef MMC_MAX_READ_RETRIES
- /*!
- * \brief Card read retries.
- *
- * Max. number of retries while reading.
- */
- #define MMC_MAX_READ_RETRIES MMC_MAX_WRITE_RETRIES
- #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_READY_POLLS
- /*!
- * \brief Card busy timeout.
- *
- * Max. number of loops waiting for card's ready state.
- * An additional delay of 1 ms is added to each loop after one quarter
- * of this value elapsed.
- */
- #define MMC_MAX_READY_POLLS 800
- #endif
- /* HACK!!!
- Some SPI hardware just shift around data read from MISO pin to the MOSI pin if the SPI data register
- is not set manually. At least on the AT91 platform it is impossible to use DMA transfers just for reading
- and to hold the MOSI line at high level if no tx data is send out at the same time. So we declare a buffer
- filled with 0xFF here, to make sure the MOSI pin is held at high level (0xFF) all the time during a
- read only transfer. This buffer is used in CardRXData and several other places too. The buffer is read only.
- This is a very very nasty hack and waists 512 Bytes of ram, but I don't see a better solution right now!
- */
- #ifndef ELEKTOR_IR1
- /* The EIR uses SPI over SSC with PDC, which requires buffer copying
- into internal SRAM anyway. Another possibility to rid of this buffer
- would be to switch the output line to GPIO and set it high during
- read transfers. This needs to be tested. */
- static uint8_t dummy_tx_buf[MMC_BLOCK_SIZE];
- #else
- #define dummy_tx_buf NULL
- #endif
- /*!
- * \brief Local multimedia card status 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_address;
- /*! \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;
- #ifdef NUTDEBUG
- static void DumpData(uint8_t *buf, size_t len)
- {
- int i;
- int j;
- for (i = 0; i < len; i += 16) {
- printf("\n%03x ", i);
- for (j = 0; j < 16; j++) {
- if (i + j < len) {
- printf("%02x ", buf[i + j]);
- } else {
- printf(" ");
- }
- }
- for (j = 0; j < 16; j++) {
- if (i + j < len) {
- if (buf[i + j] >= 32 && buf[i + j] < 127) {
- putchar(buf[i + j]);
- } else {
- putchar('.');
- }
- } else {
- break;
- }
- }
- }
- putchar('\n');
- }
- #endif
- static uint8_t CardWaitRdy(NUTSPINODE * node)
- {
- int poll = MMC_MAX_READY_POLLS;
- uint8_t b;
- NUTSPIBUS *bus;
- NUTASSERT(node->node_bus != NULL);
- bus = (NUTSPIBUS *) node->node_bus;
- do {
- (*bus->bus_transfer) (node, dummy_tx_buf, &b, 1);
- if (b && b != 0xFF) {
- return b;
- }
- if (poll < MMC_MAX_READY_POLLS / 4) {
- NutSleep(1);
- }
- } while (poll--);
- return 0;
- }
- static NUTSPIBUS *CardAllocate(NUTSPINODE * node)
- {
- int poll = MMC_MAX_READY_POLLS;
- uint8_t b;
- NUTSPIBUS *bus;
- NUTASSERT(node->node_bus != NULL);
- bus = (NUTSPIBUS *) node->node_bus;
- if ((*bus->bus_alloc) (node, 1000) == 0) {
- do {
- (*bus->bus_transfer) (node, dummy_tx_buf, &b, 1);
- if (b == 0xFF) {
- return bus;
- }
- if (poll < MMC_MAX_READY_POLLS / 4) {
- NutSleep(1);
- }
- } while (poll--);
- (*bus->bus_release) (node);
- }
- return NULL;
- }
- /*!
- * \brief Get next token from a multimedia card.
- *
- * \param node Specifies the SPI node.
- *
- * \return The token received or 0xFF if timed out.
- */
- static uint8_t CardRxTkn(NUTSPINODE * node)
- {
- uint8_t rc;
- int poll = MMC_MAX_CMDACK_POLLS;
- NUTSPIBUS *bus = (NUTSPIBUS *) node->node_bus;
- do {
- (*bus->bus_transfer) (node, dummy_tx_buf, &rc, 1);
- if (rc != 0xFF) {
- break;
- }
- #ifdef NUTDEBUG
- putchar('w');
- #endif
- if (poll < 3 * MMC_MAX_CMDACK_POLLS / 4) {
- NutSleep(10);
- }
- } while (poll--);
- #ifdef NUTDEBUG
- printf("[R%02x]", rc);
- #endif
- return rc;
- }
- /*!
- * \brief Send command to a multimedia card.
- *
- * Allocates the bus, transmits the command with the command parameter
- * set to zero and receives a one or two byte response. Before returning
- * to the caller, the bus is typically released. However, if len is
- * neither 1 nor 2, the bus will not be released.
- *
- * In SPI mode, the card sends a 1 byte response after every command
- * except after SEND_STATUS and READ_OCR commands, where 2 or 5 bytes
- * are returned resp.
- *
- * \param node Specifies the SPI node.
- * \param cmd Command code. See MMCMD_ macros.
- * \param len Length of the expected response, either 1, 2 or 0. If 0,
- * then the first byte of the response will be returned and
- * the bus will be kept allocated.
- *
- * \return The 1 or 2 byte response. On time out 0xFFFF is returned and
- * the bus is released, regardless of the expected response length.
- */
- static uint16_t CardTxCommand(NUTSPINODE * node, uint8_t cmd, uint32_t param, int len)
- {
- uint16_t rc = 0xFFFF;
- int retries = 10;
- uint8_t txb[6];
- uint8_t rxb;
- NUTSPIBUS *bus;
- /* Send command and parameter. */
- txb[0] = MMCMD_HOST | cmd;
- txb[1] = (uint8_t) (param >> 24);
- txb[2] = (uint8_t) (param >> 16);
- txb[3] = (uint8_t) (param >> 8);
- txb[4] = (uint8_t) param;
- /* We are running with CRC disabled. However, the reset command must
- ** be send with a valid CRC. Fortunately this command is sent with a
- ** fixed parameter value of zero, which results in a fixed CRC value. */
- if (cmd == 8) {
- txb[5] = MMCMD_IF_COND_CRC;
- } else {
- txb[5] = MMCMD_RESET_CRC;
- }
- do {
- bus = CardAllocate(node);
- if (bus) {
- #ifdef NUTDEBUG
- printf("\n[CMD%u,%lu]", cmd, param);
- #endif
- /* Transmit command to the card. */
- (*bus->bus_transfer) (node, txb, NULL, sizeof(txb));
- /* Receive the response. */
- rxb = CardRxTkn(node);
- /* Check for timeout. */
- if ((rxb & 0x80) == 0) {
- rc = rxb;
- if (len == 0) {
- /* Keep the bus allocated. */
- break;
- }
- if (len == 2) {
- /* R2 response. */
- (*bus->bus_transfer) (node, dummy_tx_buf, &rxb, 1);
- rc <<= 8;
- rc |= rxb;
- }
- /* We are done. */
- retries = 0;
- }
- (*bus->bus_release) (node);
- }
- } while (retries--);
- return rc;
- }
- /*!
- * \brief Initialize the multimedia card.
- *
- * This routine will put a newly powered up card into SPI mode.
- * It is called by SpiMmcMount().
- *
- * \param node Specifies the SPI node.
- *
- * \return 0 on success, -1 otherwise.
- */
- static int CardInit(NUTSPINODE * node)
- {
- NUTSPIBUS *bus;
- int i;
- uint8_t rsp;
- uint8_t rsp7[5];
- MEMCARDSUPP *mcs;
- uint32_t op_cond;
- uint_fast8_t mmc = 0;
- /*
- * Switch to idle state and wait until initialization is running
- * or idle state is reached.
- */
- mcs = (MEMCARDSUPP *) node->node_dcb;
- bus = (NUTSPIBUS *) node->node_bus;
- (*bus->bus_set_rate)(node, 400000);
- /* Send 80 dummy clocks. Some cards need this. */
- (*bus->bus_transfer) (node, NULL, NULL, 10);
- /* Reset card and switch to SPI mode. */
- rsp = CardTxCommand(node, MMCMD_GO_IDLE_STATE, 0, 1);
- /* Send 0x100 for voltage range 2.7 - 3.6V and add pattern 0xAA. */
- rsp = CardTxCommand(node, MMCMD_SEND_IF_COND, 0x1AA, 1);
- /* Default to SD version 1 or MMC. */
- mcs->mcs_sf &= ~NUTMC_SF_HC;
- op_cond = 0;
- if ((rsp & MMR1_ILLEGAL_COMMAND) == 0) {
- /* Card is SD version 2 or later, possibly SDHC. */
- op_cond = 1UL << 30;
- }
- for (i = 0; i < MMC_MAX_INIT_POLLS; i++) {
- /* Send operating conditions. */
- if (mmc) {
- rsp = CardTxCommand(node, MMCMD_SEND_OP_COND, 0, 1);
- if (rsp == MMR1_IDLE_STATE) {
- (*bus->bus_set_rate)(node, 20000000);
- return 0;
- }
- } else {
- CardTxCommand(node, MMCMD_SEND_APP_CMD, 0, 1);
- rsp = CardTxCommand(node, MMCMD_SEND_APP_OP_COND, op_cond, 0);
- (*bus->bus_release) (node);
- if (rsp & MMR1_ILLEGAL_COMMAND) {
- /* Not an SD memory card, try MMC. */
- mmc = 1;
- }
- else if (rsp == MMR1_IDLE_STATE) {
- CardTxCommand(node, MMCMD_READ_OCR, 0, 0);
- (*bus->bus_transfer) (node, dummy_tx_buf, rsp7, 4);
- (*bus->bus_release) (node);
- if (rsp7[0] & 0x80) {
- if (rsp7[0] & 0x40) {
- /* Card is SDHC. */
- mcs->mcs_sf |= NUTMC_SF_HC;
- }
- (*bus->bus_set_rate)(node, 20000000);
- return 0;
- }
- }
- }
- NutSleep(10);
- }
- return -1;
- }
- /*!
- * \brief Read data transaction.
- *
- * This routine is used to read data blocks as well as reading the CSD
- * and CID registers.
- *
- * \param node Specifies the SPI node.
- * \param cmd Command code. See MMCMD_ macros.
- * \param param Command parameter.
- * \param buf Pointer to the data buffer to fill.
- * \param len Number of data bytes to read.
- *
- * \return 0 on success or -1 in case of an error.
- */
- static int CardRxData(NUTSPINODE * node, uint8_t cmd, uint32_t param, uint8_t *buf, int len)
- {
- int rc = -1;
- uint8_t rsp;
- NUTSPIBUS *bus;
- int retries = MMC_MAX_READ_RETRIES;
- /* Sanity checks. */
- NUTASSERT(node != NULL);
- NUTASSERT(node->node_bus != NULL);
- bus = (NUTSPIBUS *) node->node_bus;
- while (rc && retries--) {
- rsp = (uint8_t)CardTxCommand(node, cmd, param, 0);
- if ((rsp & 0x80) == 0) {
- if (rsp == 0) {
- rsp = CardWaitRdy(node);
- if (rsp == 0xFE) {
- /* Data transfer. */
- (*bus->bus_transfer) (node, dummy_tx_buf, buf, len);
- /* Ignore the CRC. */
- (*bus->bus_transfer) (node, dummy_tx_buf, NULL, 2);
- rc = 0;
- }
- }
- /* Send 8 additional clocks and release the bus. */
- (*bus->bus_transfer) (node, NULL, NULL, 1);
- (*bus->bus_release) (node);
- }
- }
- 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 SpiMmcMount().
- * \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 SpiMmcBlockRead(NUTFILE * nfp, void *buffer, int num)
- {
- MMCFCB *fcb;
- NUTDEVICE *dev;
- MEMCARDSUPP *msc;
- /* Sanity checks. */
- NUTASSERT(nfp != NULL);
- NUTASSERT(nfp->nf_fcb != NULL);
- NUTASSERT(nfp->nf_dev != NULL);
- fcb = (MMCFCB *) nfp->nf_fcb;
- dev = (NUTDEVICE *) nfp->nf_dev;
- NUTASSERT(dev->dev_dcb != NULL);
- msc = (MEMCARDSUPP *) dev->dev_dcb;
- /* Make sure there was no card change. */
- if (msc->mcs_cf == 0) {
- /* Activate the write indicator. */
- msc->mcs_act(NUTMC_IND_READ);
- /* Use the internal buffer if none is provided. */
- if (buffer == NULL) {
- buffer = fcb->fcb_blkbuf;
- }
- /* Transfer the data from the card. */
- if (CardRxData(dev->dev_icb, MMCMD_READ_SINGLE_BLOCK, fcb->fcb_address, buffer, MMC_BLOCK_SIZE) == 0) {
- /* Deactivate the indicator. */
- msc->mcs_act(NUTMC_IND_OFF);
- #ifdef NUTDEBUG
- DumpData(buffer, MMC_BLOCK_SIZE);
- #endif
- return 1;
- }
- }
- /* Activate the error indicator. */
- msc->mcs_act(NUTMC_IND_ERROR);
- 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 SpiMmcMount().
- * \param buffer Pointer to the data to be written. However, writing
- * multiple blocks is not yet supported by this driver.
- * \param num Number of blocks to write.
- *
- * \return The number of blocks written. A return value of -1 indicates an
- * error.
- */
- int SpiMmcBlockWrite(NUTFILE * nfp, const void *buffer, int num)
- {
- MMCFCB *fcb;
- NUTDEVICE *dev;
- MEMCARDSUPP *msc;
- NUTSPINODE *node;
- NUTSPIBUS *bus;
- /* Sanity checks. */
- NUTASSERT(nfp != NULL);
- NUTASSERT(nfp->nf_fcb != NULL);
- NUTASSERT(nfp->nf_dev != NULL);
- fcb = (MMCFCB *) nfp->nf_fcb;
- dev = (NUTDEVICE *) nfp->nf_dev;
- NUTASSERT(dev->dev_dcb != NULL);
- msc = (MEMCARDSUPP *) dev->dev_dcb;
- NUTASSERT(dev->dev_icb != NULL);
- node = (NUTSPINODE *) dev->dev_icb;
- NUTASSERT(node->node_bus != NULL);
- bus = (NUTSPIBUS *) node->node_bus;
- /* Make sure there was no card change. */
- if (msc->mcs_cf == 0) {
- /* Activate the write indicator. */
- msc->mcs_act(NUTMC_IND_WRITE);
- /* Use the internal buffer if none is provided. */
- if (buffer == NULL) {
- buffer = fcb->fcb_blkbuf;
- }
- /* Transfer the data to the card. */
- if (num == 1) {
- uint8_t tkn = (uint8_t)CardTxCommand(node, MMCMD_WRITE_BLOCK, fcb->fcb_address, 0);
- if (tkn != 0xFF) {
- if (tkn == 0) {
- /* Send start token. */
- tkn = 0xFE;
- (*bus->bus_transfer) (node, &tkn, NULL, 1);
- /* Data transfer. */
- (*bus->bus_transfer) (node, buffer, NULL, MMC_BLOCK_SIZE);
- /* Send dummy CRC. */
- (*bus->bus_transfer) (node, dummy_tx_buf, NULL, 2);
- /* Get data response. */
- tkn = CardRxTkn(node);
- if (tkn == 0xE5) {
- /* Send 8 additional clocks and release the bus. */
- (*bus->bus_transfer) (node, NULL, NULL, 1);
- (*bus->bus_release) (node);
- /* Deactivate the indicator. */
- msc->mcs_act(NUTMC_IND_OFF);
- return 1;
- }
- }
- (*bus->bus_release) (node);
- }
- } else {
- int i;
- uint8_t tkn;
- uint8_t *bp = (uint8_t *) buffer;
- tkn = (uint8_t)CardTxCommand(node, MMCMD_WRITE_MULTIPLE_BLOCK, fcb->fcb_address, 0);
- if (tkn != 0xFF) {
- if (tkn == 0) {
- for (i = 0; i < num; i++) {
- /* Send start token. */
- tkn = 0xFC;
- (*bus->bus_transfer) (node, &tkn, NULL, 1);
- /* Data transfer. */
- (*bus->bus_transfer) (node, bp, NULL, MMC_BLOCK_SIZE);
- bp += MMC_BLOCK_SIZE;
- /* Send dummy CRC. */
- (*bus->bus_transfer) (node, dummy_tx_buf, NULL, 2);
- /* Get data response. */
- tkn = CardRxTkn(node);
- if (tkn != 0xE5) {
- (*bus->bus_release) (node);
- msc->mcs_act(NUTMC_IND_ERROR);
- return -1;
- }
- for (;;) {
- uint8_t b;
- (*bus->bus_transfer) (node, dummy_tx_buf, &b, 1);
- if (b == 0xFF) {
- break;
- }
- }
- }
- {
- uint8_t b = 0xfd;
- (*bus->bus_transfer) (node, &b, NULL, 1);
- }
- /* Send 8 additional clocks and release the bus. */
- (*bus->bus_transfer) (node, NULL, NULL, 1);
- (*bus->bus_release) (node);
- /* Deactivate the indicator. */
- msc->mcs_act(NUTMC_IND_OFF);
- return num;
- }
- (*bus->bus_release) (node);
- }
- }
- }
- /* Activate the error indicator. */
- msc->mcs_act(NUTMC_IND_ERROR);
- 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 SpiMmcBlockWrite() 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 SpiMmcBlockWrite_P(NUTFILE * nfp, PGM_P buffer, int num)
- {
- return -1;
- }
- #endif
- int SpiMmcUnmount(NUTFILE * nfp);
- /*!
- * \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 *SpiMmcMount(NUTDEVICE * dev, const char *name, int mode, int acc)
- {
- int partno = 0;
- int i;
- NUTDEVICE *fsdev;
- NUTFILE *nfp;
- MMCFCB *fcb;
- DOSPART *part;
- MEMCARDSUPP *msc = (MEMCARDSUPP *) dev->dev_dcb;
- FSCP_VOL_MOUNT mparm;
- NUTSPINODE * node = (NUTSPINODE *) dev->dev_icb;
- /* Return an error if no card is detected. */
- if ((msc->mcs_sf & NUTMC_SF_CD) == 0) {
- errno = ENODEV;
- return NUTFILE_EOF;
- }
- /* Clear the card change flag. */
- msc->mcs_cf = 0;
- /* Set the card in SPI mode. */
- if (CardInit(node)) {
- 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("[NoFS'%s]", name);
- #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 (CardRxData(node, MMCMD_READ_SINGLE_BLOCK, 0, fcb->fcb_blkbuf, MMC_BLOCK_SIZE)) {
- free(fcb);
- return NUTFILE_EOF;
- }
- #ifdef NUTDEBUG
- DumpData(fcb->fcb_blkbuf, MMC_BLOCK_SIZE);
- #endif
- /* Check for the cookie at the end of this sector. */
- if (fcb->fcb_blkbuf[DOSPART_MAGICPOS] != 0x55 || fcb->fcb_blkbuf[DOSPART_MAGICPOS + 1] != 0xAA) {
- 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];
- #ifdef NUTDEBUG
- for (i = 0; i < 4; i++) {
- printf("- State 0x%02x\n", part[i].part_state);
- printf(" Type 0x%02x\n", part[i].part_type);
- printf(" Start %lu\n", part[i].part_sect_offs);
- printf(" Size %lu\n", part[i].part_sects);
- }
- #endif
- 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)) {
- SpiMmcUnmount(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 volume.
- *
- * \param nfp Pointer to a \ref NUTFILE structure, obtained by a previous
- * call to SpiMmcMount().
- * \return 0 on success, -1 otherwise.
- */
- int SpiMmcUnmount(NUTFILE * nfp)
- {
- int rc;
- MMCFCB *fcb;
- /* Sanity checks. */
- NUTASSERT(nfp != NULL);
- NUTASSERT(nfp->nf_fcb != NULL);
- fcb = (MMCFCB *) nfp->nf_fcb;
- /* Intentionally we do not check the card change flag here to allow the
- ** file system to release all claimed resources. */
- rc = fcb->fcb_fsdev->dev_ioctl(fcb->fcb_fsdev, FS_VOL_UNMOUNT, NULL);
- free(fcb);
- free(nfp);
- 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 SpiMmcIOCtl(NUTDEVICE * dev, int req, void *conf)
- {
- int rc = 0;
- NUTSPINODE * node = (NUTSPINODE *) dev->dev_icb;
- MEMCARDSUPP *msc = (MEMCARDSUPP *) dev->dev_dcb;
- switch (req) {
- case NUTBLKDEV_MEDIAAVAIL:
- {
- int *flg = (int *) conf;
- *flg = msc->mcs_sf & NUTMC_SF_CD;
- }
- break;
- case NUTBLKDEV_MEDIACHANGE:
- {
- int *flg = (int *) conf;
- *flg = msc->mcs_cf;
- }
- break;
- case NUTBLKDEV_INFO:
- {
- BLKPAR_INFO *par = (BLKPAR_INFO *) conf;
- MMCFCB *fcb = (MMCFCB *) par->par_nfp->nf_fcb;
- 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;
- /* Caclaulate the byte offset. */
- fcb->fcb_address = par->par_blknum + fcb->fcb_part.part_sect_offs;
- if ((msc->mcs_sf & NUTMC_SF_HC) == 0) {
- fcb->fcb_address <<= 9;
- }
- }
- break;
- case MMCARD_GETSTATUS:
- {
- uint16_t *s = (uint16_t *) conf;
- *s = CardTxCommand(node, MMCMD_SEND_STATUS, 0, 2);
- }
- break;
- case MMCARD_GETOCR:
- {
- NUTSPIBUS *bus = (NUTSPIBUS *) node->node_bus;
- if (CardTxCommand(node, MMCMD_READ_OCR, 0, 0) == MMR1_IDLE_STATE) {
- uint32_t * ocr = (uint32_t *) conf;
- uint_fast8_t i;
- uint8_t rxb;
- for (i = 0; i < 4; i++) {
- (*bus->bus_transfer) (node, dummy_tx_buf, &rxb, 1);
- *ocr <<= 8;
- *ocr |= rxb;
- }
- } else {
- rc = 1;
- }
- (*bus->bus_release) (node);
- }
- break;
- case MMCARD_GETCID:
- rc = CardRxData(node, MMCMD_SEND_CID, 0, (uint8_t *) conf, sizeof(MMC_CID));
- break;
- case MMCARD_GETCSD:
- rc = CardRxData(node, MMCMD_SEND_CSD, 0, (uint8_t *) conf, sizeof(MMC_CSD));
- break;
- case MMCARD_GETEXTCSD:
- rc = CardRxData(node, MMCMD_SEND_EXTCSD, 0, (uint8_t *) conf, sizeof(MMC_CSD));
- break;
- default:
- rc = -1;
- break;
- }
- return rc;
- }
- /*!
- * \brief Initialize 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 SpiMmcInit(NUTDEVICE * dev)
- {
- #ifndef ELEKTOR_IR1
- memset(dummy_tx_buf, 0xFF, MMC_BLOCK_SIZE);
- #endif
- return 0;
- }
- /*@}*/
|