spi_mmc.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010 by egnite GmbH
00003  *
00004  * All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  *
00010  * 1. Redistributions of source code must retain the above copyright
00011  *    notice, this list of conditions and the following disclaimer.
00012  * 2. Redistributions in binary form must reproduce the above copyright
00013  *    notice, this list of conditions and the following disclaimer in the
00014  *    documentation and/or other materials provided with the distribution.
00015  * 3. Neither the name of the copyright holders nor the names of
00016  *    contributors may be used to endorse or promote products derived
00017  *    from this software without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00020  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00021  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00022  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00023  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00024  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00025  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
00026  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
00027  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
00028  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
00029  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00030  * SUCH DAMAGE.
00031  *
00032  * For additional information see http://www.ethernut.de/
00033  */
00034 
00049 #include <cfg/mmci.h>
00050 #include <sys/nutdebug.h>
00051 
00052 #if 0
00053 /* Use for local debugging. */
00054 #define NUTDEBUG
00055 #include <stdio.h>
00056 #endif
00057 
00058 #include <errno.h>
00059 #include <string.h>
00060 #include <stdlib.h>
00061 #include <memdebug.h>
00062 
00063 #include <sys/heap.h>
00064 #include <sys/timer.h>
00065 #include <sys/event.h>
00066 #include <fs/dospart.h>
00067 #include <fs/fs.h>
00068 
00069 #include <dev/blockdev.h>
00070 #include <dev/spibus.h>
00071 #include <dev/mmcard.h>
00072 
00077 
00078 #ifndef MMC_BLOCK_SIZE
00079 
00085 #define MMC_BLOCK_SIZE          512
00086 #endif
00087 
00088 #ifndef MMC_MAX_INIT_POLLS
00089 
00096 #define MMC_MAX_INIT_POLLS      512
00097 #endif
00098 
00099 #ifndef MMC_MAX_RESET_RETRIES
00100 
00105 #define MMC_MAX_RESET_RETRIES   2
00106 #endif
00107 
00108 #ifndef MMC_MAX_WRITE_RETRIES
00109 
00114 #define MMC_MAX_WRITE_RETRIES   2
00115 #endif
00116 
00117 #ifndef MMC_MAX_READ_RETRIES
00118 
00123 #define MMC_MAX_READ_RETRIES    MMC_MAX_WRITE_RETRIES
00124 #endif
00125 
00126 #ifndef MMC_MAX_CMDACK_POLLS
00127 
00134 #define MMC_MAX_CMDACK_POLLS    1024
00135 #endif
00136 
00137 #ifndef MMC_MAX_READY_POLLS
00138 
00145 #define MMC_MAX_READY_POLLS     800
00146 #endif
00147 
00151 typedef struct _MMCFCB {
00154     NUTDEVICE *fcb_fsdev;
00155 
00158     DOSPART fcb_part;
00159 
00167     uint32_t fcb_address;
00168 
00178     uint8_t fcb_blkbuf[MMC_BLOCK_SIZE];
00179 } MMCFCB;
00180 
00181 #ifdef NUTDEBUG
00182 static void DumpData(uint8_t *buf, size_t len)
00183 {
00184     int i;
00185     int j;
00186 
00187     for (i = 0; i < len; i += 16) {
00188         printf("\n%03x ", i);
00189         for (j = 0; j < 16; j++) {
00190             if (i + j < len) {
00191                 printf("%02x ", buf[i + j]);
00192             } else {
00193                 printf("   ");
00194             }
00195         }
00196         for (j = 0; j < 16; j++) {
00197             if (i + j < len) {
00198                 if (buf[i + j] >= 32 && buf[i + j] < 127) {
00199                     putchar(buf[i + j]);
00200                 } else {
00201                     putchar('.');
00202                 }
00203             } else {
00204                 break;
00205             }
00206         }
00207     }
00208     putchar('\n');
00209 }
00210 #endif
00211 
00219 static int CardWaitRdy(NUTSPINODE * node)
00220 {
00221     int poll = MMC_MAX_READY_POLLS;
00222     uint8_t b;
00223     NUTSPIBUS *bus = (NUTSPIBUS *) node->node_bus;
00224 
00225     do {
00226         (*bus->bus_transfer) (node, NULL, &b, 1);
00227         if (b == 0xFF) {
00228             return 0;
00229         }
00230 #ifdef NUTDEBUG
00231         putchar('b');
00232 #endif
00233         if (poll < MMC_MAX_READY_POLLS / 4) {
00234             NutSleep(1);
00235         }
00236     } while (poll--);
00237 
00238     return -1;
00239 }
00240 
00248 static uint8_t CardRxTkn(NUTSPINODE * node)
00249 {
00250     uint8_t rc;
00251     int poll = MMC_MAX_CMDACK_POLLS;
00252     NUTSPIBUS *bus = (NUTSPIBUS *) node->node_bus;
00253 
00254     do {
00255         (*bus->bus_transfer) (node, NULL, &rc, 1);
00256         if (rc != 0xFF) {
00257             break;
00258         }
00259 #ifdef NUTDEBUG
00260         putchar('w');
00261 #endif
00262         if (poll < 3 * MMC_MAX_CMDACK_POLLS / 4) {
00263             NutSleep(1);
00264         }
00265     } while (poll--);
00266 #ifdef NUTDEBUG
00267     printf("[R%02x]", rc);
00268 #endif
00269 
00270     return rc;
00271 }
00272 
00294 static uint16_t CardTxCommand(NUTSPINODE * node, uint8_t cmd, uint32_t param, int len)
00295 {
00296     uint16_t rc = 0xFFFF;
00297     int retries = 4;
00298     uint8_t txb[7];
00299     uint8_t rxb;
00300     NUTSPIBUS *bus;
00301 
00302     bus = (NUTSPIBUS *) node->node_bus;
00303 
00304     /* Send command and parameter. */
00305     txb[0] = 0xFF;
00306     txb[1] = MMCMD_HOST | cmd;
00307     txb[2] = (uint8_t) (param >> 24);
00308     txb[3] = (uint8_t) (param >> 16);
00309     txb[4] = (uint8_t) (param >> 8);
00310     txb[5] = (uint8_t) param;
00311     /* We are running with CRC disabled. However, the reset command must
00312     ** be send with a valid CRC. Fortunately this command is sent with a
00313     ** fixed parameter value of zero, which results in a fixed CRC value. */
00314     txb[6] = MMCMD_RESET_CRC;
00315 
00316     while (rc == 0xFFFF && retries--) {
00317         if ((*bus->bus_alloc) (node, 1000)) {
00318             break;
00319         }
00320         if (CardWaitRdy(node) == 0) {
00321 #ifdef NUTDEBUG
00322             printf("\n[CMD%u,%lu]", cmd, param);
00323 #endif
00324             (*bus->bus_transfer) (node, txb, NULL, sizeof(txb));
00325             rxb = CardRxTkn(node);
00326             if (rxb != 0xFF) {
00327                 rc = rxb;
00328                 if (len == 0) {
00329                     break;
00330                 }
00331                 if (len == 2) {
00332                     (*bus->bus_transfer) (node, NULL, &rxb, 1);
00333                     rc <<= 8;
00334                     rc |= rxb;
00335                 }
00336             }
00337         }
00338         (*bus->bus_release) (node);
00339     }
00340     return rc;
00341 }
00342 
00353 static int CardInit(NUTSPINODE * node)
00354 {
00355     int i;
00356     int j;
00357     uint8_t rsp;
00358     NUTSPIBUS *bus;
00359 
00360     bus = (NUTSPIBUS *) node->node_bus;
00361 
00362     /*
00363      * Switch to idle state and wait until initialization is running
00364      * or idle state is reached.
00365      */
00366     for (i = 0; i < MMC_MAX_RESET_RETRIES; i++) {
00367         rsp = CardTxCommand(node, MMCMD_GO_IDLE_STATE, 0, 1);
00368         for (j = 0; j < MMC_MAX_INIT_POLLS; j++) {
00369             rsp = CardTxCommand(node, MMCMD_SEND_OP_COND, 0, 1);
00370             if (rsp == MMR1_IDLE_STATE) {
00371                 return 0;
00372             }
00373             if (j > MMC_MAX_INIT_POLLS / 4) {
00374                 NutSleep(1);
00375             }
00376         }
00377     }
00378     return -1;
00379 }
00380 
00395 static int CardRxData(NUTSPINODE * node, uint8_t cmd, uint32_t param, uint8_t *buf, int len)
00396 {
00397     int rc = -1;
00398     uint8_t rsp;
00399     NUTSPIBUS *bus;
00400     int retries = MMC_MAX_READ_RETRIES;
00401 
00402     /* Sanity checks. */
00403     NUTASSERT(node != NULL);
00404     NUTASSERT(node->node_bus != NULL);
00405 
00406     bus = (NUTSPIBUS *) node->node_bus;
00407     while (rc && retries--) {
00408         rsp = (uint8_t)CardTxCommand(node, cmd, param, 0);
00409         if (rsp != 0xFF) {
00410             if (rsp == 0 && CardRxTkn(node) == 0xFE) {
00411                 /* Data transfer. */
00412                 (*bus->bus_transfer) (node, NULL, buf, len);
00413                 /* Ignore the CRC. */
00414                 (*bus->bus_transfer) (node, NULL, NULL, 2);
00415                 rc = 0;
00416             }
00417             (*bus->bus_release) (node);
00418         }
00419     }
00420     return rc;
00421 }
00422 
00437 static int CardTxData(NUTSPINODE * node, uint8_t cmd, uint32_t param, CONST uint8_t *buf, int len)
00438 {
00439     int rc = -1;
00440     uint8_t rsp;
00441     NUTSPIBUS *bus;
00442     int retries = MMC_MAX_WRITE_RETRIES;
00443 
00444     /* Sanity checks. */
00445     NUTASSERT(node != NULL);
00446     NUTASSERT(node->node_bus != NULL);
00447 
00448     bus = (NUTSPIBUS *) node->node_bus;
00449     while (rc && retries--) {
00450         rsp = (uint8_t)CardTxCommand(node, cmd, param, 0);
00451         if (rsp != 0xFF) {
00452             if (rsp == 0) {
00453                 uint8_t tkn[2] = { 0xFF, 0xFE };
00454 
00455                 /* Send start token. */
00456                 (*bus->bus_transfer) (node, &tkn, NULL, sizeof(tkn));
00457                 /* Data transfer. */
00458                 (*bus->bus_transfer) (node, buf, NULL, len);
00459                 /* Send dummy CRC. */
00460                 (*bus->bus_transfer) (node, NULL, NULL, 2);
00461                 /* Get response. */
00462                 if (CardRxTkn(node) == 0xE5) {
00463                     rc = 0;
00464                 }
00465             }
00466             (*bus->bus_release) (node);
00467         }
00468     }
00469     return rc;
00470 }
00471 
00487 int SpiMmcBlockRead(NUTFILE * nfp, void *buffer, int num)
00488 {
00489     MMCFCB *fcb;
00490     NUTDEVICE *dev;
00491     MEMCARDSUPP *msc;
00492 
00493     /* Sanity checks. */
00494     NUTASSERT(nfp != NULL);
00495     NUTASSERT(nfp->nf_fcb != NULL);
00496     NUTASSERT(nfp->nf_dev != NULL);
00497     fcb = (MMCFCB *) nfp->nf_fcb;
00498     dev = (NUTDEVICE *) nfp->nf_dev;
00499     NUTASSERT(dev->dev_dcb != NULL);
00500     msc = (MEMCARDSUPP *) dev->dev_dcb;
00501 
00502     /* Make sure there was no card change. */
00503     if (msc->mcs_cf == 0) {
00504         /* Activate the write indicator. */
00505         msc->mcs_act(NUTMC_IND_READ);
00506         /* Use the internal buffer if none is provided. */
00507         if (buffer == NULL) {
00508             buffer = fcb->fcb_blkbuf;
00509         }
00510         /* Transfer the data from the card. */
00511         if (CardRxData(dev->dev_icb, MMCMD_READ_SINGLE_BLOCK, fcb->fcb_address, buffer, MMC_BLOCK_SIZE) == 0) {
00512             /* Deactivate the indicator. */
00513             msc->mcs_act(NUTMC_IND_OFF);
00514 #ifdef NUTDEBUG
00515             DumpData(buffer, MMC_BLOCK_SIZE);
00516 #endif
00517             return 1;
00518         }
00519     }
00520     /* Activate the error indicator. */
00521     msc->mcs_act(NUTMC_IND_ERROR);
00522     return -1;
00523 }
00524 
00540 int SpiMmcBlockWrite(NUTFILE * nfp, CONST void *buffer, int num)
00541 {
00542     MMCFCB *fcb;
00543     NUTDEVICE *dev;
00544     MEMCARDSUPP *msc;
00545 
00546     /* Sanity checks. */
00547     NUTASSERT(nfp != NULL);
00548     NUTASSERT(nfp->nf_fcb != NULL);
00549     NUTASSERT(nfp->nf_dev != NULL);
00550     fcb = (MMCFCB *) nfp->nf_fcb;
00551     dev = (NUTDEVICE *) nfp->nf_dev;
00552     NUTASSERT(dev->dev_dcb != NULL);
00553     msc = (MEMCARDSUPP *) dev->dev_dcb;
00554 
00555     /* Make sure there was no card change. */
00556     if (msc->mcs_cf == 0) {
00557         /* Activate the write indicator. */
00558         msc->mcs_act(NUTMC_IND_WRITE);
00559         /* Use the internal buffer if none is provided. */
00560         if (buffer == NULL) {
00561             buffer = fcb->fcb_blkbuf;
00562         }
00563         /* Transfer the data to the card. */
00564         if (CardTxData(dev->dev_icb, MMCMD_WRITE_BLOCK, fcb->fcb_address, buffer, MMC_BLOCK_SIZE) == 0) {
00565             /* Deactivate the indicator. */
00566             msc->mcs_act(NUTMC_IND_OFF);
00567             return 1;
00568         }
00569     }
00570     /* Activate the error indicator. */
00571     msc->mcs_act(NUTMC_IND_ERROR);
00572     return -1;
00573 }
00574 
00575 #ifdef __HARVARD_ARCH__
00576 
00595 int SpiMmcBlockWrite_P(NUTFILE * nfp, PGM_P buffer, int num)
00596 {
00597     return -1;
00598 }
00599 #endif
00600 
00601 int SpiMmcUnmount(NUTFILE * nfp);
00602 
00628 NUTFILE *SpiMmcMount(NUTDEVICE * dev, CONST char *name, int mode, int acc)
00629 {
00630     int partno = 0;
00631     int i;
00632     NUTDEVICE *fsdev;
00633     NUTFILE *nfp;
00634     MMCFCB *fcb;
00635     DOSPART *part;
00636     MEMCARDSUPP *msc = (MEMCARDSUPP *) dev->dev_dcb;
00637     FSCP_VOL_MOUNT mparm;
00638     NUTSPINODE * node = (NUTSPINODE *) dev->dev_icb;
00639 
00640     /* Return an error if no card is detected. */
00641     if ((msc->mcs_sf & NUTMC_SF_CD) == 0) {
00642         errno = ENODEV;
00643         return NUTFILE_EOF;
00644     }
00645     /* Clear the card change flag. */
00646     msc->mcs_cf = 0;
00647 
00648     /* Set the card in SPI mode. */
00649     if (CardInit(node)) {
00650         errno = ENODEV;
00651         return NUTFILE_EOF;
00652     }
00653 
00654     /* Parse the name for a partition number and a file system driver. */
00655     if (*name) {
00656         partno = atoi(name);
00657         do {
00658             name++;
00659         } while (*name && *name != '/');
00660         if (*name == '/') {
00661             name++;
00662         }
00663     }
00664 
00665     /*
00666      * Check the list of registered devices for the given name of the
00667      * files system driver. If none has been specified, get the first
00668      * file system driver in the list. Hopefully the application
00669      * registered one only.
00670      */
00671     for (fsdev = nutDeviceList; fsdev; fsdev = fsdev->dev_next) {
00672         if (*name == 0) {
00673             if (fsdev->dev_type == IFTYP_FS) {
00674                 break;
00675             }
00676         } else if (strcmp(fsdev->dev_name, name) == 0) {
00677             break;
00678         }
00679     }
00680 
00681     if (fsdev == 0) {
00682 #ifdef NUTDEBUG
00683         printf("[NoFS'%s]", name);
00684 #endif
00685         errno = ENODEV;
00686         return NUTFILE_EOF;
00687     }
00688 
00689     if ((fcb = calloc(1, sizeof(MMCFCB))) == 0) {
00690         errno = ENOMEM;
00691         return NUTFILE_EOF;
00692     }
00693     fcb->fcb_fsdev = fsdev;
00694 
00695     /* Read MBR. */
00696     if (CardRxData(node, MMCMD_READ_SINGLE_BLOCK, 0, fcb->fcb_blkbuf, MMC_BLOCK_SIZE)) {
00697         free(fcb);
00698         return NUTFILE_EOF;
00699     }
00700 #ifdef NUTDEBUG
00701     DumpData(fcb->fcb_blkbuf, MMC_BLOCK_SIZE);
00702 #endif
00703     /* Check for the cookie at the end of this sector. */
00704     if (fcb->fcb_blkbuf[DOSPART_MAGICPOS] != 0x55 || fcb->fcb_blkbuf[DOSPART_MAGICPOS + 1] != 0xAA) {
00705         free(fcb);
00706         return NUTFILE_EOF;
00707     }
00708     /* Check for the partition table. */
00709     if(fcb->fcb_blkbuf[DOSPART_TYPEPOS] == 'F' && 
00710        fcb->fcb_blkbuf[DOSPART_TYPEPOS + 1] == 'A' &&
00711        fcb->fcb_blkbuf[DOSPART_TYPEPOS + 2] == 'T') {
00712         /* No partition table. Assume FAT12 and 32MB size. */
00713         fcb->fcb_part.part_type = PTYPE_FAT12;
00714         fcb->fcb_part.part_sect_offs = 0;
00715         fcb->fcb_part.part_sects = 65536; /* How to find out? */
00716     }
00717     else {
00718         /* Read partition table. */
00719         part = (DOSPART *) & fcb->fcb_blkbuf[DOSPART_SECTORPOS];
00720 #ifdef NUTDEBUG
00721         for (i = 0; i < 4; i++) {
00722             printf("- State 0x%02x\n", part[i].part_state);
00723             printf("  Type  0x%02x\n", part[i].part_type);
00724             printf("  Start %lu\n", part[i].part_sect_offs);
00725             printf("  Size  %lu\n", part[i].part_sects);
00726         }
00727 #endif
00728         for (i = 1; i <= 4; i++) {
00729             if (partno) {
00730                 if (i == partno) {
00731                     /* Found specified partition number. */
00732                     fcb->fcb_part = *part;
00733                     break;
00734                 }
00735             } else if (part->part_state & 0x80) {
00736                 /* Located first active partition. */
00737                 fcb->fcb_part = *part;
00738                 break;
00739             }
00740             part++;
00741         }
00742 
00743         if (fcb->fcb_part.part_type == PTYPE_EMPTY) {
00744             free(fcb);
00745             return NUTFILE_EOF;
00746         }
00747     }
00748 
00749     if ((nfp = NutHeapAlloc(sizeof(NUTFILE))) == 0) {
00750         free(fcb);
00751         errno = ENOMEM;
00752         return NUTFILE_EOF;
00753     }
00754     nfp->nf_next = 0;
00755     nfp->nf_dev = dev;
00756     nfp->nf_fcb = fcb;
00757 
00758     /*
00759      * Mount the file system volume.
00760      */
00761     mparm.fscp_bmnt = nfp;
00762     mparm.fscp_part_type = fcb->fcb_part.part_type;
00763     if (fsdev->dev_ioctl(fsdev, FS_VOL_MOUNT, &mparm)) {
00764         SpiMmcUnmount(nfp);
00765         return NUTFILE_EOF;
00766     }
00767     return nfp;
00768 }
00769 
00780 int SpiMmcUnmount(NUTFILE * nfp)
00781 {
00782     int rc;
00783     MMCFCB *fcb;
00784     NUTDEVICE *dev;
00785 
00786     /* Sanity checks. */
00787     NUTASSERT(nfp != NULL);
00788     NUTASSERT(nfp->nf_fcb != NULL);
00789     NUTASSERT(nfp->nf_dev != NULL);
00790     fcb = (MMCFCB *) nfp->nf_fcb;
00791     dev = (NUTDEVICE *) nfp->nf_dev;
00792     NUTASSERT(dev->dev_dcb != NULL);
00793 
00794     /* Intentionally we do not check the card change flag here to allow the
00795     ** file system to release all claimed resources. */
00796     rc = fcb->fcb_fsdev->dev_ioctl(fcb->fcb_fsdev, FS_VOL_UNMOUNT, NULL);
00797     free(fcb);
00798     free(nfp);
00799 
00800     return rc;
00801 }
00802 
00826 int SpiMmcIOCtl(NUTDEVICE * dev, int req, void *conf)
00827 {
00828     int rc = 0;
00829     NUTSPINODE * node = (NUTSPINODE *) dev->dev_icb;
00830     MEMCARDSUPP *msc = (MEMCARDSUPP *) dev->dev_dcb;
00831 
00832     switch (req) {
00833     case NUTBLKDEV_MEDIAAVAIL:
00834         {
00835             int *flg = (int *) conf;
00836             *flg = msc->mcs_sf & NUTMC_SF_CD;
00837         }
00838         break;
00839     case NUTBLKDEV_MEDIACHANGE:
00840         {
00841             int *flg = (int *) conf;
00842             *flg = msc->mcs_cf;
00843         }
00844         break;
00845     case NUTBLKDEV_INFO:
00846         {
00847             BLKPAR_INFO *par = (BLKPAR_INFO *) conf;
00848             MMCFCB *fcb = (MMCFCB *) par->par_nfp->nf_fcb;
00849 
00850             par->par_nblks = fcb->fcb_part.part_sects;
00851             par->par_blksz = MMC_BLOCK_SIZE;
00852             par->par_blkbp = fcb->fcb_blkbuf;
00853         }
00854         break;
00855     case NUTBLKDEV_SEEK:
00856         {
00857             BLKPAR_SEEK *par = (BLKPAR_SEEK *) conf;
00858             MMCFCB *fcb = (MMCFCB *) par->par_nfp->nf_fcb;
00859 
00860             /* Caclaulate the byte offset. */
00861             fcb->fcb_address = (par->par_blknum + fcb->fcb_part.part_sect_offs) << 9;
00862         }
00863         break;
00864     case MMCARD_GETSTATUS:
00865         {
00866             uint16_t *s = (uint16_t *) conf;
00867 
00868             *s = CardTxCommand(node, MMCMD_SEND_STATUS, 0, 2);
00869         }
00870         break;
00871     case MMCARD_GETOCR:
00872         {
00873             NUTSPIBUS *bus = (NUTSPIBUS *) node->node_bus;
00874 
00875             if (CardTxCommand(node, MMCMD_READ_OCR, 0, 0) == MMR1_IDLE_STATE) {
00876                 uint32_t * ocr = (uint32_t *) conf;
00877                 uint_fast8_t i;
00878                 uint8_t rxb;
00879 
00880                 for (i = 0; i < 4; i++) {
00881                     (*bus->bus_transfer) (node, NULL, &rxb, 1);
00882                     *ocr <<= 8;
00883                     *ocr |= rxb;
00884                 }
00885 
00886             } else {
00887                 rc = 1;
00888             }
00889             (*bus->bus_release) (node);
00890         }
00891         break;
00892     case MMCARD_GETCID:
00893         rc = CardRxData(node, MMCMD_SEND_CID, 0, (uint8_t *) conf, sizeof(MMC_CID));
00894         break;
00895     case MMCARD_GETCSD:
00896         rc = CardRxData(node, MMCMD_SEND_CSD, 0, (uint8_t *) conf, sizeof(MMC_CSD));
00897         break;
00898     case MMCARD_GETEXTCSD:
00899         rc = CardRxData(node, MMCMD_SEND_EXTCSD, 0, (uint8_t *) conf, sizeof(MMC_CSD));
00900         break;
00901     default:
00902         rc = -1;
00903         break;
00904     }
00905     return rc;
00906 }
00907 
00919 int SpiMmcInit(NUTDEVICE * dev)
00920 {
00921     return 0;
00922 }
00923 

© 2000-2010 by contributors - visit http://www.ethernut.de/