/**************************************************************************** * This file is part of the MMC device driver. * * Copyright (c) 2004 by Michael Fischer. All rights reserved. * * Thanks to Sylvain Bissonnette for some of his low level functions. * Take a look at www.microsyl.com (Led Sign with MMC MemoryCard) * * 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 author nor the names of its 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. * **************************************************************************** * History: * * 10.10.04 mifi First Version ****************************************************************************/ #define __MMCDRV_C__ #define LOG_MODULE LOG_MMCDRV_MODULE #include #include #include #include #include #include #include "typedefs.h" #include "portio.h" #include "mmcdrv.h" #include "vs10xx.h" #include "led.h" #include "log.h" #include "spidrv.h" /*==========================================================*/ /* DEFINE: All Structures and Common Constants */ /*==========================================================*/ #define MMC_MAX_SUPPORTED_DEVICE 1 /* * Drive Flags */ #define MMC_SUPPORT_LBA 0x0001 #define MMC_SUPPORT_LBA48 0x0002 #define MMC_READ_ONLY 0x4000 #define MMC_READY 0x8000 #define Delay_1ms(_x) NutDelay(_x) #define SPIDDR DDRB #define SPIPORT PORTB #define SPIPIN PINB /* PragmaLab: disable PIN-defines (already defined in 'portio.h' #define SCLK 0x02 #define MOSI 0x04 #define MISO 0x08 #define CS 0x20 #define ENABLE 0x40 end PragmaLab */ #define MMC_RESET 0 #define MMC_INIT 1 #define MMC_READ_CSD 9 #define MMC_READ_CID 10 typedef struct _drive { /* * Interface values */ WORD wFlags; BYTE bDevice; /* * LBA value */ DWORD dTotalSectors; WORD wSectorSize; } DRIVE; /*==========================================================*/ /* DEFINE: Definition of all local Data */ /*==========================================================*/ static HANDLE hMMCSemaphore; static DRIVE sDrive[MMC_MAX_SUPPORTED_DEVICE]; static MMC_MOUNT_FUNC *pUserMountFunc; static MMC_MOUNT_FUNC *pUserUnMountFunc; /*==========================================================*/ /* DEFINE: Definition of all local Procedures */ /*==========================================================*/ /************************************************************/ /* MMCLock */ /************************************************************/ static void MMCLock(void) { NutEventWait(&hMMCSemaphore, 0); } /* MMCLock */ /************************************************************/ /* MMCFree */ /************************************************************/ static void MMCFree(void) { NutEventPost(&hMMCSemaphore); } /* MMCFree */ /************************************************************/ /* MMCSemaInit */ /************************************************************/ static void MMCSemaInit(void) { NutEventPost(&hMMCSemaphore); } /* MMCSemaInit */ /************************************************************ * int MMCDataToken(void) * * - pings the card until it gets data token * - returns one byte of read info (data token) ************************************************************/ static BYTE MMCDataToken(void) { WORD i = 0xffff; BYTE Byte = 0xff; while ((Byte != 0xfe) && (--i)) { Byte = SPIgetByte(); } return(Byte); } /* MMCDataToken */ /************************************************************ * unsigned char MMCGet(void) * * - pings the card until it gets a non-0xff value * - returns one byte of read info ************************************************************/ static BYTE MMCGet(void) { WORD i = 0xffff; BYTE Byte = 0xff; while ((Byte == 0xff) && (--i)) { Byte = SPIgetByte(); } return(Byte); } /* MMCGet */ /************************************************************ * void MMCCommand(unsigned char command, unsigned int px, unsigned int py) * * - send one byte of 0xff, then issue command + params + (fake) crc * - eat up the one command of nothing after the CRC ************************************************************/ static void MMCCommand(unsigned char command, unsigned int px, unsigned int py) { SPIselect(SPI_DEV_MMC); SPIputByte(0xff); SPIputByte(command | 0x40); SPIputByte((unsigned char)((px >> 8)&0x0ff)); /* high byte of param y */ SPIputByte((unsigned char)(px & 0x00ff)); /* low byte of param y */ SPIputByte((unsigned char)((py >> 8)&0x0ff)); /* high byte of param x */ SPIputByte((unsigned char)(py & 0x00ff)); /* low byte of param x */ SPIputByte(0x95); /* correct CRC for first command in SPI */ /* after that CRC is ignored, so no problem with */ /* always sending 0x95 */ SPIputByte(0xff); } /* MMCCommand */ /************************************************************/ /* GetCSD */ /************************************************************/ static int GetCSD (DRIVE *pDrive) { int i; int nError = MMC_ERROR; BYTE bData[16]; WORD wREAD_BL_LEN; WORD wC_SIZE; WORD wC_SIZE_MULT; WORD wDummy; DWORD dTotalSectors = 0; MMCCommand(MMC_READ_CSD, 0, 0); if (MMCDataToken() != 0xfe) { LogMsg_P(LOG_ERR, PSTR("error during CSD read")); } else { for (i=0; i<16; i++) { bData[i] = SPIgetByte(); } SPIputByte(0xff); /* checksum -> don't care about it for now */ SPIputByte(0xff); /* checksum -> don't care about it for now */ SPIdeselect(); /* * Get the READ_BL_LEN */ wREAD_BL_LEN = (1 << (bData[5] & 0x0F)); /* * Get the C_SIZE */ wC_SIZE = (bData[6] & 0x03); wC_SIZE = wC_SIZE << 10; wDummy = bData[7]; wDummy = wDummy << 2; wC_SIZE |= wDummy; wDummy = (bData[8] & 0xC0); wDummy = wDummy >> 6; wC_SIZE |= wDummy; /* * Get the wC_SIZE_MULT */ wC_SIZE_MULT = (bData[9] & 0x03); wC_SIZE_MULT |= wC_SIZE_MULT << 1; wDummy = (bData[10] & 0x80); wDummy = wDummy >> 7; wC_SIZE_MULT |= wDummy; wC_SIZE_MULT = (1 << (wC_SIZE_MULT+2)); dTotalSectors = wC_SIZE+1; dTotalSectors *= wC_SIZE_MULT; pDrive->dTotalSectors = dTotalSectors; pDrive->wSectorSize = wREAD_BL_LEN; nError = MMC_OK; } return(nError); } /* GetCSD */ #if 0 /************************************************************/ /* GetCID */ /************************************************************/ static void GetCID(void) { int i; BYTE bData[16]; MMCCommand(MMC_READ_CID, 0, 0); if (MMCDataToken() != 0xfe) { printf("MMC: error during CID read\n"); } else { printf("MMC: CID read\n"); } for (i=0; i<16; i++) { bData[i] = SPIgetByte(); } SPIputByte(0xff); /* checksum -> don't care about it for now */ SPIputByte(0xff); /* checksum -> don't care about it for now */ SPIdeselect(); printf("MMC: Product Name: %c%c%c%c%c%c\n", bData[3], bData[4], bData[5], bData[6], bData[7], bData[8]); } /* GetCID */ #endif /************************************************************/ /* InitMMCCard */ /* */ /* - flushes card receive buffer */ /* - selects card */ /* - sends the reset command */ /* - sends the initialization command, waits for card ready */ /************************************************************/ static int InitMMCCard(void) { WORD i; /* PragmaLab: disable initit of PINS and SPI, already done in 'SystemInitIO()' SPIDDR = SCLK + MOSI + CS; SPIPORT = 0x00; Delay_1ms(250); Delay_1ms(250); SPIPORT |= CS; SPCR = (1 << SPE) | (1 << MSTR); // enable SPI as master, set clk divider // set to max speed Delay_1ms(250); SPIdeselect(); // start off with 80 bits of high data with card deselected PragmaLab: why send dummy bytes with card DEselected? This messes up the VS10XX init */ for (i = 0; i < 10; i++) { SPIputByte(0xff); } /*end PragmaLab */ /* send CMD0 - go to idle state */ MMCCommand(MMC_RESET, 0, 0); if (MMCGet() != 1) { SPIdeselect(); return(MMC_ERROR); // MMC Not detected } /* send CMD1 until we get a 0 back, indicating card is done initializing */ i = 0xffff; while ((SPIgetByte() != 0) && (--i)) { MMCCommand(MMC_INIT, 0, 0); } if (i == 0) { SPIdeselect(); return(MMC_ERROR); // Init Fail } SPIdeselect(); return(MMC_OK); } /* InitMMCCard */ /************************************************************/ /* ReadSectors */ /************************************************************/ static int ReadSectors(DRIVE *pDrive, BYTE *pBuffer, DWORD dStartSector, WORD wSectorCount) { int nError = MMC_OK; int nSector; WORD wDataCount; DWORD dReadSector; pDrive = pDrive; for (nSector=0; nSector>7) & 0xffff, (dReadSector<<9) & 0xffff); if (MMCDataToken() != 0xfe) { nError = MMC_ERROR; SPIdeselect(); break; } for (wDataCount=0; wDataCount<512; wDataCount++) { /* read the sector */ *pBuffer = SPIgetByte(); pBuffer++; } SPIputByte(0xff); /* checksum -> don't care about it for now */ SPIputByte(0xff); /* checksum -> don't care about it for now */ SPIdeselect(); } return(nError); } /* ReadSectors */ #if (MMC_SUPPORT_WRITE == 1) /************************************************************/ /* WriteSectors */ /************************************************************/ static BYTE WriteSectors(DRIVE *pDrive, BYTE *pBuffer, DWORD dStartSector, WORD wSectorCount) { int nError = MMC_OK; int nSector; WORD wDataCount; DWORD dWriteSector; pDrive = pDrive; for (nSector=0; nSector>7)& 0xffff, (dWriteSector<<9)& 0xffff); if (MMCGet() == 0xff) { nError = MMC_ERROR; SPIdeselect(); break; } SPIputByte(0xfe); // Send Start Byte for (wDataCount=0; wDataCount<512; wDataCount++) { /* read the sector */ SPIputByte(*pBuffer); pBuffer++; } SPIputByte(0xff); /* checksum -> don't care about it for now */ SPIputByte(0xff); /* checksum -> don't care about it for now */ SPIputByte(0xff); /* Read "data response byte" */ wDataCount = 0xffff; while ((SPIgetByte() == 0x00) && (--wDataCount)); /* wait for write finish */ if (wDataCount == 0) { nError = MMC_ERROR; SPIdeselect(); break; } SPIdeselect(); } return(nError); } #endif /* WriteSectors */ /*==========================================================*/ /* DEFINE: All code exported */ /*==========================================================*/ /************************************************************/ /* MMCInit */ /************************************************************/ int MMCInit(int nMMCMode, MMC_MOUNT_FUNC *pMountFunc, MMC_MOUNT_FUNC *pUnMountFunc) { int nError = MMC_OK; BYTE bIndex; nMMCMode = nMMCMode; pUserMountFunc = pMountFunc; pUserUnMountFunc = pUnMountFunc; for (bIndex=0; bIndexwFlags & MMC_READY) { nError = GetCSD(pDrive); } MMCFree(); if (nError == MMC_OK) { nError = MMCReadSectors(MMC_DRIVE_C, pSectorBuffer, 0, 1); } return(nError); } /* MMCMountDevice */ /************************************************************/ /* MMCGetSectorSize */ /************************************************************/ int MMCGetSectorSize(BYTE bDevice) { int nSectorSize; DRIVE *pDrive; nSectorSize = 0; MMCLock(); if (bDevice >= MMC_MAX_SUPPORTED_DEVICE) { nSectorSize = 0; } else { pDrive = &sDrive[bDevice]; nSectorSize = pDrive->wSectorSize; } MMCFree(); return(nSectorSize); } /* MMCGetSectorSize */ /************************************************************/ /* MMCIsCDROMDevice */ /************************************************************/ int MMCIsCDROMDevice(BYTE bDevice) { return(FALSE); } /* MMCIsCDROMDevice */ /************************************************************/ /* MMCIsZIPDevice */ /************************************************************/ int MMCIsZIPDevice(BYTE bDevice) { return(FALSE); } /* MMCIsZIPDevice */ /************************************************************/ /* MMCUnMountDevice */ /************************************************************/ int MMCUnMountDevice(BYTE bDevice) { return(MMC_OK); } /* MMCUnMountDevice */ /************************************************************/ /* MMCGetTotalSectors */ /************************************************************/ DWORD MMCGetTotalSectors(BYTE bDevice) { DWORD dwTotalSectors; DRIVE *pDrive; dwTotalSectors = 0; MMCLock(); if (bDevice >= MMC_MAX_SUPPORTED_DEVICE) { dwTotalSectors = 0; } else { pDrive = &sDrive[bDevice]; dwTotalSectors = pDrive->dTotalSectors; //dwTotalSectors -= 64; } MMCFree(); return(dwTotalSectors); } /* MMCGetTotalSectors */ /************************************************************/ /* MMCReadSectors */ /************************************************************/ int MMCReadSectors(BYTE bDevice, void *pData, DWORD dwStartSector, WORD wSectorCount) { int nError; WORD wReadCount; DRIVE *pDrive = 0; BYTE *pByte; nError = MMC_OK; MMCLock(); if (bDevice >= MMC_MAX_SUPPORTED_DEVICE) { nError = MMC_DRIVE_NOT_FOUND; } else { pDrive = &sDrive[bDevice]; if ((pDrive->wFlags & MMC_READY) == 0) { nError = MMC_DRIVE_NOT_FOUND; } else { if ((dwStartSector + wSectorCount) > pDrive->dTotalSectors) { nError = MMC_PARAM_ERROR; } } } if (nError == MMC_OK) { pByte = (BYTE *)pData; if (wSectorCount != 1) { while (wSectorCount > 0) { if (wSectorCount < 256) { wReadCount = wSectorCount; } else { wReadCount = 256; } nError = ReadSectors(pDrive, pByte, dwStartSector, wReadCount); if (nError != MMC_OK) { break; } dwStartSector += wReadCount; wSectorCount -= wReadCount; pByte += (wReadCount * pDrive->wSectorSize); } } else { nError = ReadSectors(pDrive, pByte, dwStartSector, 1); } } MMCFree(); return(nError); } /* MMCReadSectors */ #if (MMC_SUPPORT_WRITE == 1) /************************************************************/ /* MMCWriteSectors */ /************************************************************/ int MMCWriteSectors(BYTE bDevice, void *pData, DWORD dwStartSector, WORD wSectorCount) { int nError; WORD wWriteCount; DRIVE *pDrive = 0; BYTE *pByte; nError = MMC_OK; MMCLock(); if (bDevice >= MMC_MAX_SUPPORTED_DEVICE) { nError = MMC_DRIVE_NOT_FOUND; } else { pDrive = &sDrive[bDevice]; if ((dwStartSector + wSectorCount) > pDrive->dTotalSectors) { nError = MMC_PARAM_ERROR; } if ((pDrive->wFlags & MMC_READY) == 0) { nError = MMC_DRIVE_NOT_FOUND; } if (pDrive->wFlags & MMC_READ_ONLY) { nError = MMC_NOT_SUPPORTED; } } if (nError == MMC_OK) { pByte = (BYTE *) pData; while (wSectorCount > 0) { if (wSectorCount < 256) { wWriteCount = wSectorCount; } else { wWriteCount = 256; } nError = WriteSectors(pDrive, pByte, dwStartSector, wWriteCount); if (nError != MMC_OK) { break; } dwStartSector += wWriteCount; wSectorCount -= wWriteCount; pByte += (wWriteCount * MMC_SECTOR_SIZE); } } MMCFree(); return(nError); } /* MMCWriteSectors */ #endif