| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931 |
- /*
- * Copyright (C) 2006 by egnite Software GmbH.
- * Copyright (C) 2008, 2011-2012 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 Multimedia Card Interface.
- *
- * This simple implementation supports reading a single
- * 3.3V MultiMedia Card in slot A or B only.
- *
- * \verbatim
- *
- * $Log$
- * Revision 1.3 2008/10/03 11:28:58 haraldkipp
- * Corrected and modified initialization of MultiMedia and SD Cards.
- *
- * Revision 1.2 2008/08/11 06:59:04 haraldkipp
- * BSD types replaced by stdint types (feature request #1282721).
- *
- * Revision 1.1 2006/09/05 12:34:21 haraldkipp
- * Support for hardware MultiMedia Card interface added.
- * SD Cards are currently not supported.
- *
- *
- * \endverbatim
- */
- #include <cfg/arch.h>
- #include <cfg/arch/gpio.h>
- #include <cfg/mmci.h>
- #include <errno.h>
- #include <string.h>
- #include <stdlib.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>
- #include <dev/at91_mci.h>
- #if 0
- /* Use for local debugging. */
- #define NUTDEBUG
- #include <stdio.h>
- #endif
- /*!
- * \addtogroup xgAt91Mci
- */
- /*@{*/
- #ifndef MMC_BLOCK_SIZE
- #define MMC_BLOCK_SIZE 512
- #endif
- #ifndef MCI_INI_BITRATE
- /* MMC starts in open drain mode with 400 kHz max. clock. */
- #define MCI_INI_BITRATE 400000
- #endif
- #ifndef MCI_MMC_BITRATE
- /* Max. clock for MMC in push-pull mode is 20 MHz. */
- #define MCI_MMC_BITRATE 20000000
- #endif
- #ifndef MCI_SDC_BITRATE
- /* Max. clock for SD-Card is 25 MHz. */
- #define MCI_SDC_BITRATE 25000000
- #endif
- #ifndef MMCARD_VRANGE
- #define MMCARD_VRANGE (MMCARD_32_33V | MMCARD_31_32V | MMCARD_30_31V)
- #endif
- #ifdef MCI_SLOTA
- #ifndef MMC_PINS_A
- #define MMC_PINS_A _BV(PA6_MCDA0_A) | _BV(PA7_MCCDA_A) | _BV(PA8_MCCK_A) | _BV(PA9_MCDA1_A) | _BV(PA10_MCDA2_A) | _BV(PA11_MCDA3_A)
- #endif
- #ifndef MMC_PINS_B
- #define MMC_PINS_B 0
- #endif
- #else
- #ifndef MMC_PINS_A
- #define MMC_PINS_A _BV(PA8_MCCK_A)
- #endif
- #ifndef MMC_PINS_B
- #define MMC_PINS_B _BV(PA1_MCCDB_B) | _BV(PA0_MCDB0_B) | _BV(PA5_MCDB1_B) | _BV(PA4_MCDB2_B) | _BV(PA3_MCDB3_B)
- #endif
- #endif
- #define MCICMD_ALL_SEND_CID (MMCMD_ALL_SEND_CID | MCI_MAXLAT | MCI_RSPTYP_136)
- #define MCICMD_DESELECT_CARD (MMCMD_SELECT_CARD)
- #define MCICMD_GO_IDLE_STATE (MMCMD_GO_IDLE_STATE)
- #define MCICMD_READ_SINGLE_BLOCK (MMCMD_READ_SINGLE_BLOCK | MCI_TRCMD_START | MCI_TRDIR | MCI_MAXLAT | MCI_RSPTYP_48)
- #define MCICMD_SELECT_CARD (MMCMD_SELECT_CARD | MCI_MAXLAT | MCI_RSPTYP_48)
- #define MCICMD_SEND_APP_CMD (MMCMD_SEND_APP_CMD | MCI_MAXLAT | MCI_RSPTYP_48)
- #define MCICMD_SEND_APP_OP_COND (MMCMD_SEND_APP_OP_COND | MCI_MAXLAT | MCI_RSPTYP_48)
- #define MCICMD_SEND_OP_COND (MMCMD_SEND_OP_COND | MCI_MAXLAT | MCI_RSPTYP_48)
- #define MCICMD_SEND_RELATIVE_ADDR (MMCMD_SEND_RELATIVE_ADDR | MCI_MAXLAT | MCI_RSPTYP_48)
- #define MCICMD_SEND_STATUS (MMCMD_SEND_STATUS | MCI_MAXLAT | MCI_RSPTYP_48)
- #define MCICMD_SET_BLOCKLEN (MMCMD_SET_BLOCKLEN | MCI_MAXLAT | MCI_RSPTYP_48)
- #define MCICMD_WRITE_BLOCK (MMCMD_WRITE_BLOCK | MCI_TRCMD_START | MCI_MAXLAT | MCI_RSPTYP_48)
- #define MCICMD_IERROR (MCI_RTOE | MCI_RENDE | MCI_RDIRE | MCI_RINDE)
- #define MCICMD_ERROR (MCI_UNRE | MCI_OVRE | MCI_DTOE | MCI_DCRCE | MCI_RCRCE | MCICMD_IERROR)
- #define MCIFLG_SDCARD 0x00000001
- #define MCIFLG_4BIT 0x00000010
- /*!
- * \brief Local card interface information.
- */
- typedef struct _MCIFC {
- /*! \brief Configuration flags. */
- uint32_t ifc_config;
- /*! \brief Operating conditions. */
- uint32_t ifc_opcond;
- /*! \brief Relative card address. */
- uint32_t ifc_reladdr;
- /*! \brief Pointer to sector buffer. */
- uint8_t *ifc_buff;
- /*! \brief MMC response. */
- uint32_t ifc_resp[4];
- /*! \brief Card identification. */
- uint32_t ifc_cid[4];
- } MCIFC;
- /*!
- * \brief Local multimedia card mount information.
- */
- typedef struct _MCIFCB {
- /*! \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];
- } MCIFCB;
- /*
- * 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 Get divider for a given MCI clock rate.
- *
- * \param clk Requested clock rate.
- */
- static uint32_t At91MciClockDiv(uint32_t clk)
- {
- uint32_t rc;
- /* MCI is driven by MCK/2. */
- rc = NutArchClockGet(NUT_HWCLK_PERIPHERAL) / 2;
- /* Compensate rounding error, but do not care about 10kHz. */
- rc += clk - 10000;
- /* Calculate the divider. */
- rc /= clk;
- /* Actual divider is 1 less, avoid underflow. */
- if (rc) {
- rc -= 1;
- }
- /* In reality, overflow will only happen when the caller requests
- a unrealistic low MCI clock. */
- if (rc > 255) {
- rc = 255;
- }
- return rc;
- }
- /*!
- * \brief Reset the MCI hardware.
- *
- * \param init If 0, the current settings will be kept, otherwise initial
- * values are loaded.
- */
- static void At91MciReset(int init)
- {
- uint32_t mode;
- uint32_t dtmo;
- uint32_t slot;
- /* Enable MCI clock. */
- outr(PMC_PCER, _BV(MCI_ID));
- if (init) {
- outr(MCI_IDR, 0xFFFFFFFF);
- /* Set initial configuration. */
- dtmo = MCI_DTOMUL_1M | MCI_DTOCYC;
- mode = MCI_RDPROOF | MCI_WRPROOF | (2 << MCI_PWSDIV_LSB);
- /* Slow start. */
- mode |= At91MciClockDiv(MCI_INI_BITRATE) << MCI_CLKDIV_LSB;
- #ifdef MCI_SLOTA
- slot = MCI_SDCSEL_SLOTA;
- #else
- slot = MCI_SDCSEL_SLOTB;
- #endif
- } else {
- /* Retrieve current configuration. */
- dtmo = inr(MCI_DTOR);
- mode = inr(MCI_MR) & 0xffff;
- slot = inr(MCI_SDCR);
- }
- /* Disable and software reset. */
- outr(MCI_CR, MCI_MCIDIS | MCI_SWRST);
- /* Set configuration values. */
- outr(MCI_DTOR, dtmo);
- outr(MCI_MR, mode);
- outr(MCI_SDCR, slot);
- /* Enable card interface. */
- outr(MCI_CR, MCI_MCIEN | MCI_PWSEN);
- }
- static void At91MciEnablePins(void)
- {
- /* Disable PIO lines used for MCI. */
- outr(PIOA_PDR, MMC_PINS_A | MMC_PINS_B);
- /* Enable peripherals. */
- outr(PIOA_ASR, MMC_PINS_A);
- outr(PIOA_BSR, MMC_PINS_B);
- }
- static void At91MciDisablePins(void)
- {
- #ifdef MCI0_PIN_SHARING
- /* Enable PIO input lines used for MCI. */
- outr(PIOA_ODR, MMC_PINS_A | MMC_PINS_B);
- outr(PIOA_PER, MMC_PINS_A | MMC_PINS_B);
- #endif
- }
- /*!
- * \brief Initialize MMC hardware interface.
- *
- * This function is automatically executed during during device
- * registration via NutRegisterDevice().
- *
- * \param dev Identifies the device to initialize.
- */
- static int At91MciInit(NUTDEVICE * dev)
- {
- /* Initialize the MCI hardware. */
- At91MciReset(1);
- return 0;
- }
- /*!
- * \brief Send command to multimedia card.
- *
- * \param ifc Specifies the hardware interface.
- * \param cmd Command code. See MMCMD_ macros.
- * \param param Optional command parameter.
- *
- * \return MCI status.
- */
- static uint32_t At91MciTxCmd(MCIFC * ifc, uint32_t cmd, uint32_t param)
- {
- uint32_t sr;
- uint32_t rl;
- uint32_t i;
- uint32_t wfs = MCI_CMDRDY;
- uint32_t ces = MCICMD_IERROR;
- /*
- * Disable PDC.
- */
- #ifdef NUTDEBUG
- printf("[Cmd%lu,%lx]", cmd & MCI_CMDNB, param);
- #endif
- outr(MCI_PTCR, PDC_TXTDIS | PDC_RXTDIS);
- outr(MCI_MR, inr(MCI_MR) & ~(MCI_BLKLEN | MCI_PDCMODE));
- outr(MCI_RCR, 0);
- outr(MCI_RNCR, 0);
- outr(MCI_TCR, 0);
- outr(MCI_TNCR, 0);
- if (cmd & MCI_TRCMD_START) {
- /* Data transfer. Make sure that the buffer is set. */
- if (ifc->ifc_buff == NULL) {
- errno = EINVAL;
- return MCI_OVRE | MCI_UNRE;
- }
- /* Set block length and PDC mode. */
- outr(MCI_MR, (inr(MCI_MR) & ~MCI_BLKLEN) | (MMC_BLOCK_SIZE << MCI_BLKLEN_LSB) | MCI_PDCMODE);
- if (cmd & MCI_TRDIR) {
- /* Set PDC address and counter for reading. */
- outr(MCI_RPR, (uint32_t) ifc->ifc_buff);
- outr(MCI_RCR, MMC_BLOCK_SIZE / 4);
- /* Enable PDC read. */
- outr(MCI_PTCR, PDC_RXTEN);
- /* We will wait until end of data transmission. */
- wfs = MCI_ENDRX;
- } else {
- /* Set PDC address and counter for writing. */
- outr(MCI_TPR, (uint32_t) ifc->ifc_buff);
- outr(MCI_TCR, MMC_BLOCK_SIZE / 4);
- /* We will wait until data block ended. */
- wfs = MCI_BLKE;
- }
- }
- /* Set card parameter and command. */
- outr(MCI_ARGR, param);
- outr(MCI_CMDR, cmd);
- /* When writing, enable PDC after sending the command. */
- if ((cmd & (MCI_TRCMD_START | MCI_TRDIR)) == MCI_TRCMD_START) {
- outr(MCI_PTCR, PDC_TXTEN);
- }
- /* Determine the number of words of the expected response. */
- switch (cmd & MCI_RSPTYP) {
- case MCI_RSPTYP_48:
- rl = 2;
- break;
- case MCI_RSPTYP_136:
- rl = 4;
- break;
- default:
- rl = 0;
- break;
- }
- /* Wait for MCI_CMDRDY, MCI_ENDRX or MCI_BLKE. */
- while (((sr = inr(MCI_SR)) & wfs) == 0);
- /* Check for error. */
- if (sr & ces) {
- return sr;
- }
- /* Read the resonse. */
- for (i = 0; i < rl; i++) {
- ifc->ifc_resp[i] = inr(MCI_RSPR);
- }
- #ifdef NUTDEBUG
- printf("[Sta=%lx]", sr);
- if (rl) {
- printf("[Rsp");
- for (i = 0; i < rl; i++) {
- printf(" %lx", ifc->ifc_resp[i]);
- }
- putchar(']');
- }
- putchar('\n');
- #endif
- /* When writing, wait until the card is not busy. */
- if (wfs & MCI_BLKE) {
- while ((inr(MCI_SR) & MCI_NOTBUSY) == 0);
- }
- /* Do we need this? */
- ifc->ifc_buff = NULL;
- return sr;
- }
- /*!
- * \brief Discover available cards.
- *
- * Currently this has been tested for SanDisk SD Cards and several
- * MultiMedia Cards. It doesn't seem to work with RS-MMC, though.
- *
- * \param ifc Specifies the hardware interface.
- *
- * \return 0 on success, -1 otherwise.
- */
- static int At91MciDiscover(MCIFC * ifc)
- {
- uint32_t sr;
- uint32_t clk = MCI_MMC_BITRATE;
- uint32_t opd = 0;
- int tmo;
- At91MciEnablePins();
- /* Put all cards in idle state. */
- At91MciTxCmd(ifc, MCICMD_GO_IDLE_STATE | MCI_SPCMD_INIT, 0);
- At91MciTxCmd(ifc, MCICMD_GO_IDLE_STATE, 0);
- /* Poll SDC operating conditions. */
- for (tmo = 1000; --tmo;) {
- sr = At91MciTxCmd(ifc, MCICMD_SEND_APP_CMD, 0);
- if ((ifc->ifc_resp[0] & (1 << 8)) != 0 && (sr & MCICMD_IERROR) == 0) {
- sr = At91MciTxCmd(ifc, MCICMD_SEND_APP_OP_COND, MMCARD_VRANGE);
- if ((sr & MCICMD_IERROR) == 0) {
- ifc->ifc_opcond = ifc->ifc_resp[0];
- if (ifc->ifc_resp[0] & MMCOP_NBUSY) {
- ifc->ifc_config |= MCIFLG_SDCARD;
- break;
- }
- }
- }
- NutSleep(1);
- }
- if (tmo == 0) {
- /* No SDC. Put all cards back in idle state and try MMC. */
- opd = MCI_OPDCMD;
- At91MciTxCmd(ifc, MCICMD_GO_IDLE_STATE, 0);
- /* Poll MMC operating conditions. */
- for (tmo = 100; --tmo;) {
- sr = At91MciTxCmd(ifc, MCICMD_SEND_OP_COND | opd, MMCARD_VRANGE);
- ifc->ifc_opcond = ifc->ifc_resp[0];
- if (ifc->ifc_resp[0] & MMCOP_NBUSY) {
- break;
- }
- NutSleep(1);
- }
- }
- if (tmo == 0) {
- /* No valid card. */
- At91MciDisablePins();
- return -1;
- }
- /* Discover cards. */
- ifc->ifc_reladdr = 0;
- for (tmo = 50; --tmo;) {
- sr = At91MciTxCmd(ifc, MCICMD_ALL_SEND_CID | opd, 0);
- memcpy(ifc->ifc_cid, ifc->ifc_resp, sizeof(ifc->ifc_cid));
- if (sr & MCI_RTOE) {
- /* No more cards. */
- break;
- }
- if (ifc->ifc_config & MCIFLG_SDCARD) {
- /* SD Card will send an address. */
- ifc->ifc_reladdr = 0;
- } else {
- /* MultiMedia Card will receive an address. */
- ifc->ifc_reladdr++;
- }
- At91MciTxCmd(ifc, MCICMD_SEND_RELATIVE_ADDR | opd, ifc->ifc_reladdr << 16);
- if (ifc->ifc_config & MCIFLG_SDCARD) {
- /* Store SD Card address. */
- ifc->ifc_reladdr = ifc->ifc_resp[0] >> 16;
- /* SD Cards can run at higher clock rates. */
- clk = MCI_SDC_BITRATE;
- }
- }
- At91MciDisablePins();
- if (ifc->ifc_reladdr) {
- /* Switch to high speed transfer. */
- outr(MCI_MR, (inr(MCI_MR) & ~MCI_CLKDIV) | (At91MciClockDiv(clk) << MCI_CLKDIV_LSB));
- return 0;
- }
- return -1;
- }
- /*!
- * \brief Read a single block.
- *
- * \param ifc Specifies the hardware interface.
- * \param blk Block number to read or verify.
- * \param buf Data buffer. Receives the data being read from the card.
- *
- * \return 0 on success, -1 otherwise.
- */
- static int At91MciReadSingle(MCIFC * ifc, uint32_t blk, uint8_t * buf)
- {
- int rc = -1;
- uint32_t sr;
- /* Gain mutex access. */
- NutEventWait(&mutex, 0);
- At91MciEnablePins();
- sr = At91MciTxCmd(ifc, MCICMD_SELECT_CARD, ifc->ifc_reladdr << 16);
- if ((sr & MCICMD_ERROR) == 0) {
- sr = At91MciTxCmd(ifc, MCICMD_SET_BLOCKLEN, MMC_BLOCK_SIZE);
- if ((sr & MCICMD_ERROR) == 0) {
- ifc->ifc_buff = buf;
- sr = At91MciTxCmd(ifc, MCICMD_READ_SINGLE_BLOCK, blk * MMC_BLOCK_SIZE);
- if ((sr & MCICMD_ERROR) == 0) {
- rc = 0;
- }
- }
- At91MciTxCmd(ifc, MCICMD_DESELECT_CARD, 0);
- }
- /* Release mutex access. */
- At91MciDisablePins();
- 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 the data to be sent to the card.
- *
- * \return 0 on success, -1 otherwise.
- */
- static int At91MciWriteSingle(MCIFC * ifc, uint32_t blk, const uint8_t * buf)
- {
- int rc = -1;
- uint32_t sr;
- /* Gain mutex access. */
- NutEventWait(&mutex, 0);
- At91MciEnablePins();
- sr = At91MciTxCmd(ifc, MCICMD_SELECT_CARD, ifc->ifc_reladdr << 16);
- if ((sr & MCICMD_ERROR) == 0) {
- sr = At91MciTxCmd(ifc, MCICMD_SET_BLOCKLEN, MMC_BLOCK_SIZE);
- if ((sr & MCICMD_ERROR) == 0) {
- ifc->ifc_buff = (uint8_t *)buf;
- sr = At91MciTxCmd(ifc, MCICMD_WRITE_BLOCK, blk * MMC_BLOCK_SIZE);
- if ((sr & MCICMD_ERROR) == 0) {
- rc = 0;
- }
- }
- At91MciTxCmd(ifc, MCICMD_DESELECT_CARD, 0);
- }
- /* Release mutex access. */
- At91MciDisablePins();
- NutEventPost(&mutex);
- return rc;
- }
- /*!
- * \brief Read data blocks from a mounted partition.
- *
- * \param nfp Pointer to a ::NUTFILE structure, obtained by a previous
- * call to At91MciMount().
- * \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.
- */
- static int At91MciBlockRead(NUTFILE * nfp, void *buffer, int num)
- {
- MCIFCB *fcb = (MCIFCB *) nfp->nf_fcb;
- uint32_t blk = fcb->fcb_blknum;
- NUTDEVICE *dev = (NUTDEVICE *) nfp->nf_dev;
- MCIFC *ifc = (MCIFC *) dev->dev_icb;
- int rt;
- if (buffer == 0) {
- buffer = fcb->fcb_blkbuf;
- }
- blk += fcb->fcb_part.part_sect_offs;
- for (rt = 10; --rt >= 0;) {
- if (At91MciReadSingle(ifc, blk, buffer) == 0) {
- return 1;
- }
- At91MciReset(0);
- }
- return -1;
- }
- /*!
- * \brief Write data blocks to a mounted partition.
- *
- * \param nfp Pointer to a \ref NUTFILE structure, obtained by a previous
- * call to At91MciMount().
- * \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.
- */
- static int At91MciBlockWrite(NUTFILE * nfp, const void *buffer, int num)
- {
- MCIFCB *fcb = (MCIFCB *) nfp->nf_fcb;
- uint32_t blk = fcb->fcb_blknum;
- NUTDEVICE *dev = (NUTDEVICE *) nfp->nf_dev;
- MCIFC *ifc = (MCIFC *) dev->dev_icb;
- if (buffer == 0) {
- buffer = fcb->fcb_blkbuf;
- }
- blk += fcb->fcb_part.part_sect_offs;
- if (At91MciWriteSingle(ifc, blk, buffer) == 0) {
- return 1;
- }
- return -1;
- }
- /*!
- * \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.
- *
- * \return 0 on success, -1 otherwise.
- */
- static int At91MciUnmount(NUTFILE * nfp)
- {
- int rc = -1;
- if (nfp) {
- MCIFCB *fcb = (MCIFCB *) nfp->nf_fcb;
- if (fcb) {
- rc = fcb->fcb_fsdev->dev_ioctl(fcb->fcb_fsdev, FS_VOL_UNMOUNT, NULL);
- NutHeapFree(fcb);
- }
- NutHeapFree(nfp);
- }
- return rc;
- }
- /*!
- * \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.
- */
- static NUTFILE *At91MciMount(NUTDEVICE * dev, const char *name, int mode, int acc)
- {
- int partno = 0;
- int i;
- NUTDEVICE *fsdev;
- NUTFILE *nfp;
- MCIFCB *fcb;
- DOSPART *part;
- MCIFC *ifc = (MCIFC *) dev->dev_icb;
- FSCP_VOL_MOUNT mparm;
- if (At91MciDiscover(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++;
- }
- }
- #ifdef NUTDEBUG
- printf("['%s'-PART%d]", name, partno);
- #endif
- /*
- * 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 = NutHeapAllocClear(sizeof(MCIFCB))) == 0) {
- errno = ENOMEM;
- return NUTFILE_EOF;
- }
- fcb->fcb_fsdev = fsdev;
- /* Initialize MMC access mutex semaphore. */
- NutEventPost(&mutex);
- /* Read MBR. */
- if (At91MciReadSingle(ifc, 0, fcb->fcb_blkbuf)) {
- NutHeapFree(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) {
- NutHeapFree(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) {
- NutHeapFree(fcb);
- return NUTFILE_EOF;
- }
- }
- if ((nfp = NutHeapAlloc(sizeof(NUTFILE))) == 0) {
- NutHeapFree(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)) {
- At91MciUnmount(nfp);
- return NUTFILE_EOF;
- }
- return nfp;
- }
- /*!
- * \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_MEDIAAVAIL
- * - \ref NUTBLKDEV_MEDIACHANGE
- * - \ref NUTBLKDEV_INFO
- * - \ref NUTBLKDEV_SEEK
- * - \ref MMCARD_GETOCR
- *
- * \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.
- */
- static int At91MciIOCtrl(NUTDEVICE * dev, int req, void *conf)
- {
- int rc = 0;
- MCIFC *ifc = (MCIFC *) dev->dev_icb;
- switch (req) {
- case NUTBLKDEV_MEDIAAVAIL:
- *((int *) conf) = 1;
- break;
- case NUTBLKDEV_MEDIACHANGE:
- *((int *) conf) = 0;
- break;
- case NUTBLKDEV_INFO:
- {
- BLKPAR_INFO *par = (BLKPAR_INFO *) conf;
- MCIFCB *fcb = (MCIFCB *) 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;
- MCIFCB *fcb = (MCIFCB *) par->par_nfp->nf_fcb;
- fcb->fcb_blknum = par->par_blknum;
- }
- break;
- case MMCARD_GETOCR:
- *((uint32_t *) conf) = ifc->ifc_opcond;
- break;
- default:
- rc = -1;
- break;
- }
- return rc;
- }
- static MCIFC mci0_info;
- /*!
- * \brief Multimedia card device information structure.
- *
- * A pointer to this structure must be passed to NutRegisterDevice()
- * to bind this driver to the Nut/OS kernel. An application may then
- * call
- * /verbatim
- * _open("MCI0:", _O_RDWR | _O_BINARY);
- * /endverbatim
- * to mount the first active primary partition with any previously
- * registered file system driver (typically devPhat0).
- */
- NUTDEVICE devAt91Mci0 = {
- 0, /*!< Pointer to next device, dev_next. */
- {'M', 'C', 'I', '0', 0, 0, 0, 0, 0}
- , /*!< Unique device name, dev_name. */
- 0, /*!< Type of device, dev_type. Obsolete. */
- 0, /*!< Base address, dev_base. Unused. */
- 0, /*!< First interrupt number, dev_irq. Unused. */
- &mci0_info, /*!< Interface control block, dev_icb. */
- 0, /*!< Driver control block used by the low level part, dev_dcb. */
- At91MciInit, /*!< Driver initialization routine, dev_init. */
- At91MciIOCtrl, /*!< Driver specific control function, dev_ioctl. */
- At91MciBlockRead, /*!< Read data from a file, dev_read. */
- At91MciBlockWrite, /*!< Write data to a file, dev_write. */
- At91MciMount, /*!< Mount a file system, dev_open. */
- At91MciUnmount, /*!< Unmount a file system, dev_close. */
- 0, /*!< Return file size, dev_size. */
- 0, /*!< Select function, optional, not yet implemented */
- };
- /*@}*/
|