Mmcsd.c

From Mech
Revision as of 18:57, 11 February 2009 by Mat Kotowsky (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search
/////////////////////////////////////////////////////////////////////////
////                           MMCSD.c                               ////
////                                                                 ////
////    This is a low-level driver for MMC and SD cards.             ////
////                                                                 ////
//// --User Functions--                                              ////
////                                                                 ////
//// mmcsd_init(): Initializes the media.                            ////
////                                                                 ////
//// mmcsd_read_byte(a, p)                                           ////
////  Reads a byte from the MMC/SD card at location a, saves to      ////
////  pointer p.  Returns 0 if OK, non-zero if error.                ////
////                                                                 ////
//// mmcsd_read_data(a, n, p)                                        ////
////  Reads n bytes of data from the MMC/SD card starting at address ////
////  a, saves result to pointer p.  Returns 0 if OK, non-zero if    ////
////  error.                                                         ////
////                                                                 ////
//// mmcsd_flush_buffer()                                            ////
////  The two user write functions (mmcsd_write_byte() and           ////
////  mmcsd_write_data()) maintain a buffer to speed up the writing  ////
////  process.  Whenever a read or write is performed, the write     ////
////  buffer is loaded with the specified page and only the          ////
////  contents of this buffer is changed.  If any future writes      ////
////  cross a page boundary then the buffer in RAM is written        ////
////  to the MMC/SD and then the next page is loaded into the        ////
////  buffer.  mmcsd_flush_buffer() forces the contents in RAM       ////
////  to the MMC/SD card.  Returns 0 if OK, non-zero if errror.      ////
////                                                                 ////
//// mmcsd_write_byte(a, d)                                          ////
////  Writes data byte d to the MMC/SD address a.  Intelligently     ////
////  manages a write buffer, therefore you may need to call         ////
////  mmcsd_flush_buffer() to flush the buffer.                      ////
////                                                                 ////
//// mmcsd_write_data(a, n, p)                                       ////
////  Writes n bytes of data from pointer p to the MMC/SD card       ////
////  starting at address a.  This function intelligently manages    ////
////  a write buffer, therefore if you may need to call              ////
////  mmcsd_flush_buffer() to flush any buffered characters.         ////
////  returns 0 if OK, non-zero if error.                            ////
////                                                                 ////
//// mmcsd_read_block(a, s, p)                                       ////
////  Reads an entire page from the SD/MMC.  Keep in mind that the   ////
////  start of the read has to be aligned to a block                 ////
////  (Address % 512 = 0).  Therefore s must be evenly divisible by  ////
////  512.  At the application level it is much more effecient to    ////
////  to use mmcsd_read_data() or mmcsd_read_byte().  Returns 0      ////
////  if successful, non-zero if error.                              ////
////                                                                 ////
//// mmcsd_write_block(a, s, p):                                     ////
////  Writes an entire page to the SD/MMC.  This will write an       ////
////  entire page to the SD/MMC, so the address and size must be     ////
////  evenly  divisble by 512.  At the application level it is much  ////
////  more effecient to use mmcsd_write_data() or mmcsd_write_byte().////
////  Returns 0 if successful, non-zero if error.                    ////
////                                                                 ////
//// mmcsd_print_cid(): Displays all data in the Card Identification ////
////                     Register. Note this only works on SD cards. ////
////                                                                 ////
//// mmcsd_print_csd(): Displays all data in the Card Specific Data  ////
////                     Register. Note this only works on SD cards. ////
////                                                                 ////
////                                                                 ////
//// --Non-User Functions--                                          ////
////                                                                 ////
//// mmcsd_go_idle_state(): Sends the GO_IDLE_STATE command to the   ////
////                        SD/MMC.                                  ////
//// mmcsd_send_op_cond(): Sends the SEND_OP_COND command to the     ////
////                        SD. Note this command only works on SD.  ////
//// mmcsd_send_if_cond(): Sends the SEND_IF_COND command to the     ////
////                        SD. Note this command only works on SD.  ////
//// mmcsd_sd_status(): Sends the SD_STATUS command to the SD. Note  ////
////                     This command only works on SD cards.        ////
//// mmcsd_send_status(): Sends the SEND_STATUS command to the       ////
////                       SD/MMC.                                   ////
//// mmcsd_set_blocklen(): Sends the SET_BLOCKLEN command along with ////
////                        the desired block length.                ////
//// mmcsd_app_cmd(): Sends the APP_CMD command to the SD. This only ////
////                   works on SD cards and is used just before any ////
////                   SD-only command (e.g. send_op_cond()).        ////
//// mmcsd_read_ocr(): Sends the READ_OCR command to the SD/MMC.     ////
//// mmcsd_crc_on_off(): Sends the CRC_ON_OFF command to the SD/MMC  ////
////                      along with a bit to turn the CRC on/off.   ////
//// mmcsd_send_cmd(): Sends a command and argument to the SD/MMC.   ////
//// mmcsd_get_r1(): Waits for an R1 response from the SD/MMC and    ////
////                  then saves the response to a buffer.           ////
//// mmcsd_get_r2(): Waits for an R2 response from the SD/MMC and    ////
////                  then saves the response to a buffer.           ////
//// mmcsd_get_r3(): Waits for an R3 response from the SD/MMC and    ////
////                  then saves the response to a buffer.           ////
//// mmcsd_get_r7(): Waits for an R7 response from the SD/MMC and    ////
////                  then saves the response to a buffer.           ////
//// mmcsd_wait_for_token(): Waits for a specified token from the    ////
////                          SD/MMC.                                ////
//// mmcsd_crc7(): Generates a CRC7 using a pointer to some data,    ////
////                and how many bytes long the data is.             ////
//// mmcsd_crc16(): Generates a CRC16 using a pointer to some data,  ////
////                and how many bytes long the data is.             ////
////                                                                 ////
/////////////////////////////////////////////////////////////////////////
////        (C) Copyright 2007 Custom Computer Services              ////
//// This source code may only be used by licensed users of the CCS  ////
//// C compiler.  This source code may only be distributed to other  ////
//// licensed users of the CCS C compiler.  No other use,            ////
//// reproduction or distribution is permitted without written       ////
//// permission.  Derivative programs created using this software    ////
//// in object code form are not restricted in any way.              ////
/////////////////////////////////////////////////////////////////////////

#ifndef MMCSD_C
#define MMCSD_C

/////////////////////
////             ////
//// User Config ////
////             ////
/////////////////////

#ifndef MMCSD_PIN_SCL
 #define MMCSD_PIN_SCL     PIN_C3 //o
 #define MMCSD_PIN_SDI     PIN_C4 //i
 #define MMCSD_PIN_SDO     PIN_C5 //o
 #define MMCSD_PIN_SELECT  PIN_C2 //o
#endif

#use spi(MASTER, DI=MMCSD_PIN_SDI, DO=MMCSD_PIN_SDO, CLK=MMCSD_PIN_SCL, BITS=8, MSB_FIRST, IDLE=1, stream=mmcsd_spi)

////////////////////////
////                ////
//// Useful Defines ////
////                ////
////////////////////////

enum MMCSD_err
   {MMCSD_GOODEC = 0,
   MMCSD_IDLE = 0x01,
   MMCSD_ERASE_RESET = 0x02,
   MMCSD_ILLEGAL_CMD = 0x04,
   MMCSD_CRC_ERR = 0x08,
   MMCSD_ERASE_SEQ_ERR = 0x10,
   MMCSD_ADDR_ERR = 0x20,
   MMCSD_PARAM_ERR = 0x40,
   RESP_TIMEOUT = 0x80};

#define GO_IDLE_STATE 0
#define SEND_OP_COND 1
#define SEND_IF_COND 8
#define SEND_CSD 9
#define SEND_CID 10
#define SD_STATUS 13
#define SEND_STATUS 13
#define SET_BLOCKLEN 16
#define READ_SINGLE_BLOCK 17
#define WRITE_BLOCK 24
#define SD_SEND_OP_COND 41
#define APP_CMD 55
#define READ_OCR 58
#define CRC_ON_OFF 59

#define IDLE_TOKEN 0x01
#define DATA_START_TOKEN 0xFE

#define MMCSD_MAX_BLOCK_SIZE 512

////////////////////////
///                  ///
/// Global Variables ///
///                  ///
////////////////////////

int g_mmcsd_buffer[MMCSD_MAX_BLOCK_SIZE];

int1 g_CRC_enabled;
int1 g_MMCSDBufferChanged;

int32 g_mmcsdBufferAddress;

enum _card_type{SD, MMC} g_card_type;

/////////////////////////////
////                     ////
//// Function Prototypes ////
////                     ////
/////////////////////////////

MMCSD_err mmcsd_init();
MMCSD_err mmcsd_read_data(int32 address, int16 size, int* ptr);
MMCSD_err mmcsd_read_block(int32 address, int16 size, int* ptr);
MMCSD_err mmcsd_write_data(int32 address, int16 size, int* ptr);
MMCSD_err mmcsd_write_block(int32 address, int16 size, int* ptr);
MMCSD_err mmcsd_go_idle_state(void);
MMCSD_err mmcsd_send_op_cond(void);
MMCSD_err mmcsd_send_if_cond(int r7[]);
MMCSD_err mmcsd_print_csd();
MMCSD_err mmcsd_print_cid();
MMCSD_err mmcsd_sd_status(int r2[]);
MMCSD_err mmcsd_send_status(int r2[]);
MMCSD_err mmcsd_set_blocklen(int32 blocklen);
MMCSD_err mmcsd_read_single_block(int32 address);
MMCSD_err mmcsd_write_single_block(int32 address);
MMCSD_err mmcsd_sd_send_op_cond(void);
MMCSD_err mmcsd_app_cmd(void);
MMCSD_err mmcsd_read_ocr(int* r1);
MMCSD_err mmcsd_crc_on_off(int1 crc_enabled);
MMCSD_err mmcsd_send_cmd(int cmd, int32 arg);
MMCSD_err mmcsd_get_r1(void);
MMCSD_err mmcsd_get_r2(int r2[]);
MMCSD_err mmcsd_get_r3(int r3[]);
MMCSD_err mmcsd_get_r7(int r7[]);
MMCSD_err mmcsd_wait_for_token(int token);
unsigned int8 mmcsd_crc7(char *data, unsigned int8 length);
unsigned int16 mmcsd_crc16(char *data, unsigned int8 length);
void mmcsd_select();
void mmcsd_deselect();

/// Fast Functions ! ///

MMCSD_err mmcsd_load_buffer(void);
MMCSD_err mmcsd_flush_buffer(void);
MMCSD_err mmcsd_move_buffer(int32 new_addr);
MMCSD_err mmcsd_read_byte(int32 addr, char* data);
MMCSD_err mmcsd_write_byte(int32 addr, char data);

//////////////////////////////////
////                          ////
//// Function Implementations ////
////                          ////
//////////////////////////////////

MMCSD_err mmcsd_init()
{
   int
      i,
      r1;

   g_CRC_enabled = TRUE;
   g_mmcsdBufferAddress = 0;

   output_drive(MMCSD_PIN_SCL);
   output_drive(MMCSD_PIN_SDO);
   output_drive(MMCSD_PIN_SELECT);
   output_float(MMCSD_PIN_SDI);

   mmcsd_deselect();
   delay_ms(15);
   
   /* begin initialization */
   i = 0;
   do
   {
      delay_ms(1);
      mmcsd_select();
      r1=mmcsd_go_idle_state();
      mmcsd_deselect();
      i++;
      if(i == 0xFF)
      {
         mmcsd_deselect();
         return r1;
      }
   } while(!bit_test(r1, 0));

   i = 0;
   do
   {
      delay_ms(1);
      mmcsd_select();
      r1=mmcsd_send_op_cond();
      mmcsd_deselect();
      i++;
      if(i == 0xFF)
      {
         mmcsd_deselect();
         return r1;
      }
   } while(r1 & MMCSD_IDLE);

   /* figure out if we have an SD or MMC */
   mmcsd_select();
   r1=mmcsd_app_cmd();
   r1=mmcsd_sd_send_op_cond();
   mmcsd_deselect();

   /* an mmc will return an 0x04 here */
   if(r1 == 0x04)
      g_card_type = MMC;
   else
      g_card_type = SD;

   /* set block length to 512 bytes */
   mmcsd_select();
   r1 = mmcsd_set_blocklen(MMCSD_MAX_BLOCK_SIZE);
   if(r1 != MMCSD_GOODEC)
   {
      mmcsd_deselect();
      return r1;
   }
   mmcsd_deselect();

   /* turn CRCs off to speed up reading/writing */
   mmcsd_select();
   r1 = mmcsd_crc_on_off(0);
   if(r1 != MMCSD_GOODEC)
   {
      mmcsd_deselect();
      return r1;
   }
   mmcsd_deselect();

   r1 = mmcsd_load_buffer();

   return r1;
}

MMCSD_err mmcsd_read_data(int32 address, int16 size, int* ptr)
{
   MMCSD_err r1;
   int16 i;  // counter for loops

   for(i = 0; i < size; i++)
   {
      r1 = mmcsd_read_byte(address++, ptr++);
      if(r1 != MMCSD_GOODEC)
         return r1;
   }
   
   return MMCSD_GOODEC;
}

MMCSD_err mmcsd_read_block(int32 address, int16 size, int* ptr)
{  
   MMCSD_err ec;
   int16 i; // counter for loops

   // send command
   mmcsd_select();
   ec = mmcsd_read_single_block(address);
   if(ec != MMCSD_GOODEC)
   {
      mmcsd_deselect();
      return ec;
   }
   
   // wait for the data start token
   ec = mmcsd_wait_for_token(DATA_START_TOKEN);
   if(ec != MMCSD_GOODEC)
   {
      mmcsd_deselect();
      return ec;
   }
   
   // read in the data
   for(i = 0; i < size; i += 1)
      ptr[i] = spi_xfer(mmcsd_spi, 0xFF);

   if(g_CRC_enabled)
   {
      /* check the crc */
      if(make16(spi_xfer(mmcsd_spi, 0xFF), spi_xfer(mmcsd_spi, 0xFF)) != mmcsd_crc16(g_mmcsd_buffer, MMCSD_MAX_BLOCK_SIZE))
      {
         mmcsd_deselect();
         return MMCSD_CRC_ERR;
      }
   }
   else
   {
      /* have the card transmit the CRC, but ignore it */
      spi_xfer(mmcsd_spi, 0xFF);
      spi_xfer(mmcsd_spi, 0xFF);
   }
   mmcsd_deselect();

   return MMCSD_GOODEC;
}

MMCSD_err mmcsd_write_data(int32 address, int16 size, int* ptr)
{
   MMCSD_err ec;
   int16 i;  // counter for loops
  
   for(i = 0; i < size; i++)
   {
      ec = mmcsd_write_byte(address++, *ptr++);
      if(ec != MMCSD_GOODEC)
         return ec;
   }
   
   return MMCSD_GOODEC;
}

MMCSD_err mmcsd_write_block(int32 address, int16 size, int* ptr)
{ 
   MMCSD_err ec;
   int16 i;

   // send command
   mmcsd_select();
   ec = mmcsd_write_single_block(address);
   if(ec != MMCSD_GOODEC)
   {
      mmcsd_deselect();
      return ec;
   }
   
   // send a data start token
   spi_xfer(mmcsd_spi, DATA_START_TOKEN);
   
   // send all the data
   for(i = 0; i < size; i += 1)
      spi_xfer(mmcsd_spi, ptr[i]);

   // if the CRC is enabled we have to calculate it, otherwise just send an 0xFFFF
   if(g_CRC_enabled)
      spi_xfer(mmcsd_spi, mmcsd_crc16(ptr, size));
   else
   {
      spi_xfer(mmcsd_spi, 0xFF);
      spi_xfer(mmcsd_spi, 0xFF);
   }
   
   // get the error code back from the card; "data accepted" is 0bXXX00101
   ec = mmcsd_get_r1();
   if(ec & 0x0A)
   {
      mmcsd_deselect();
      return ec;
   }
   
   // wait for the line to go back high, this indicates that the write is complete
   while(spi_xfer(mmcsd_spi, 0xFF) == 0);
   mmcsd_deselect();

   return MMCSD_GOODEC;
}

MMCSD_err mmcsd_go_idle_state(void)
{
   mmcsd_send_cmd(GO_IDLE_STATE, 0);
   
   return mmcsd_get_r1();
}

MMCSD_err mmcsd_send_op_cond(void)
{
   mmcsd_send_cmd(SEND_OP_COND, 0);
   
   return mmcsd_get_r1();
}

MMCSD_err mmcsd_send_if_cond(int r7[])
{
   mmcsd_send_cmd(SEND_IF_COND, 0x45A);

   return mmcsd_get_r7(r7);
}

MMCSD_err mmcsd_print_csd()
{  
   int
      buf[16],
      i,
      r1;

   // MMCs don't support this command
   if(g_card_type == MMC)
      return MMCSD_PARAM_ERR;

   mmcsd_select();   
   mmcsd_send_cmd(SEND_CSD, 0);
   r1 = mmcsd_get_r1();
   if(r1 != MMCSD_GOODEC)
   {
      mmcsd_deselect();
      return r1;
   }
   
   r1 = mmcsd_wait_for_token(DATA_START_TOKEN);
   if(r1 != MMCSD_GOODEC)
   {
      mmcsd_deselect();
      return r1;
   }

   for(i = 0; i < 16; i++)
      buf[i] = spi_xfer(mmcsd_spi, 0xFF);
   mmcsd_deselect();

   printf("\r\nCSD_STRUCTURE: %X", (buf[0] & 0x0C) >> 2);
   printf("\r\nTAAC: %X", buf[1]);
   printf("\r\nNSAC: %X", buf[2]);
   printf("\r\nTRAN_SPEED: %X", buf[3]);
   printf("\r\nCCC: %lX", (make16(buf[4], buf[5]) & 0xFFF0) >> 4);
   printf("\r\nREAD_BL_LEN: %X", buf[5] & 0x0F);   
   printf("\r\nREAD_BL_PARTIAL: %X", (buf[6] & 0x80) >> 7);
   printf("\r\nWRITE_BLK_MISALIGN: %X", (buf[6] & 0x40) >> 6);
   printf("\r\nREAD_BLK_MISALIGN: %X", (buf[6] & 0x20) >> 5);
   printf("\r\nDSR_IMP: %X", (buf[6] & 0x10) >> 4);
   printf("\r\nC_SIZE: %lX", (((buf[6] & 0x03) << 10) | (buf[7] << 2) | ((buf[8] & 0xC0) >> 6)));
   printf("\r\nVDD_R_CURR_MIN: %X", (buf[8] & 0x38) >> 3);
   printf("\r\nVDD_R_CURR_MAX: %X", buf[8] & 0x07);
   printf("\r\nVDD_W_CURR_MIN: %X", (buf[9] & 0xE0) >> 5);
   printf("\r\nVDD_W_CURR_MAX: %X", (buf[9] & 0x1C) >> 2);
   printf("\r\nC_SIZE_MULT: %X", ((buf[9] & 0x03) << 1) | ((buf[10] & 0x80) >> 7));
   printf("\r\nERASE_BLK_EN: %X", (buf[10] & 0x40) >> 6);
   printf("\r\nSECTOR_SIZE: %X", ((buf[10] & 0x3F) << 1) | ((buf[11] & 0x80) >> 7));
   printf("\r\nWP_GRP_SIZE: %X", buf[11] & 0x7F);
   printf("\r\nWP_GRP_ENABLE: %X", (buf[12] & 0x80) >> 7);
   printf("\r\nR2W_FACTOR: %X", (buf[12] & 0x1C) >> 2);
   printf("\r\nWRITE_BL_LEN: %X", ((buf[12] & 0x03) << 2) | ((buf[13] & 0xC0) >> 6));
   printf("\r\nWRITE_BL_PARTIAL: %X", (buf[13] & 0x20) >> 5);
   printf("\r\nFILE_FORMAT_GRP: %X", (buf[14] & 0x80) >> 7);
   printf("\r\nCOPY: %X", (buf[14] & 0x40) >> 6);
   printf("\r\nPERM_WRITE_PROTECT: %X", (buf[14] & 0x20) >> 5);
   printf("\r\nTMP_WRITE_PROTECT: %X", (buf[14] & 0x10) >> 4);
   printf("\r\nFILE_FORMAT: %X", (buf[14] & 0x0C) >> 2);
   printf("\r\nCRC: %X", buf[15]);

   return r1;
}

MMCSD_err mmcsd_print_cid()
{
   int
      buf[16],
      i,
      r1;

   // MMCs don't support this command
   if(g_card_type == MMC)
      return MMCSD_PARAM_ERR;
   
   mmcsd_select();
   mmcsd_send_cmd(SEND_CID, 0);
   r1 = mmcsd_get_r1();
   if(r1 != MMCSD_GOODEC)
   {
      mmcsd_deselect();
      return r1;
   }
   r1 = mmcsd_wait_for_token(DATA_START_TOKEN);
   if(r1 != MMCSD_GOODEC)
   {
      mmcsd_deselect();
      return r1;
   }
   
   for(i = 0; i < 16; i++)
      buf[i] = spi_xfer(mmcsd_spi, 0xFF);
   mmcsd_deselect();
   
   printf("\r\nManufacturer ID: %X", buf[0]);
   printf("\r\nOEM/Application ID: %c%c", buf[1], buf[2]);
   printf("\r\nOEM/Application ID: %c%c%c%c%c", buf[3], buf[4], buf[5], buf[6], buf[7]);
   printf("\r\nProduct Revision: %X", buf[8]);
   printf("\r\nSerial Number: %X%X%X%X", buf[9], buf[10], buf[11], buf[12]);
   printf("\r\nManufacturer Date Code: %X%X", buf[13] & 0x0F, buf[14]);
   printf("\r\nCRC-7 Checksum: %X", buf[15]);

   return r1;
}

MMCSD_err mmcsd_sd_status(int r2[])
{
   int i;

   mmcsd_select();
   mmcsd_send_cmd(APP_CMD, 0);
   r2[0]=mmcsd_get_r1();
   mmcsd_deselect();

   mmcsd_select();
   mmcsd_send_cmd(SD_STATUS, 0);

   for(i = 0; i < 64; i++)
      spi_xfer(mmcsd_spi, 0xFF);      

   mmcsd_deselect();

   return mmcsd_get_r2(r2);
}

MMCSD_err mmcsd_send_status(int r2[])
{
   mmcsd_send_cmd(SEND_STATUS, 0);   
   
   return mmcsd_get_r2(r2);
}

MMCSD_err mmcsd_set_blocklen(int32 blocklen)
{
   mmcsd_send_cmd(SET_BLOCKLEN, blocklen);
   
   return mmcsd_get_r1();
}

MMCSD_err mmcsd_read_single_block(int32 address)
{
   mmcsd_send_cmd(READ_SINGLE_BLOCK, address);
   
   return mmcsd_get_r1();
}

MMCSD_err mmcsd_write_single_block(int32 address)
{
   mmcsd_send_cmd(WRITE_BLOCK, address);
  
   return mmcsd_get_r1();
}

MMCSD_err mmcsd_sd_send_op_cond(void)
{
   mmcsd_send_cmd(SD_SEND_OP_COND, 0);
   
   return mmcsd_get_r1();
}

MMCSD_err mmcsd_app_cmd(void)
{
   mmcsd_send_cmd(APP_CMD, 0);
   
   return mmcsd_get_r1();
}

MMCSD_err mmcsd_read_ocr(int r3[])
{
   mmcsd_send_cmd(READ_OCR, 0);
   
   return mmcsd_get_r3(r3);
}

MMCSD_err mmcsd_crc_on_off(int1 crc_enabled)
{
   mmcsd_send_cmd(CRC_ON_OFF, crc_enabled);
   
   g_CRC_enabled = crc_enabled;
   
   return mmcsd_get_r1();
}

MMCSD_err mmcsd_send_cmd(int cmd, int32 arg)
{   
   int packet[6]; // the entire command, argument, and crc in one variable
  
   // construct the packet
   // every command on an SD card is or'ed with 0x40
   packet[0] = cmd | 0x40;
   packet[1] = make8(arg, 3);
   packet[2] = make8(arg, 2);
   packet[3] = make8(arg, 1);
   packet[4] = make8(arg, 0);

   // calculate the crc if needed
   if(g_CRC_enabled)
      packet[5] = mmcsd_crc7(packet, 5);
   else
      packet[5] = 0xFF;

   // transfer the command and argument, with an extra 0xFF hacked in there
   spi_xfer(mmcsd_spi, packet[0]);
   spi_xfer(mmcsd_spi, packet[1]);
   spi_xfer(mmcsd_spi, packet[2]);
   spi_xfer(mmcsd_spi, packet[3]);
   spi_xfer(mmcsd_spi, packet[4]);
   spi_xfer(mmcsd_spi, packet[5]);

   return MMCSD_GOODEC;
}

MMCSD_err mmcsd_get_r1(void)
{
   int
      response = 0,  // place to hold the response coming back from the SPI line
      timeout = 0xFF; // maximum amount loops to wait for idle before getting impatient and leaving the function with an error code
    
   // loop until timeout == 0
   while(timeout)
   {
      // read what's on the SPI line
      //  the SD/MMC requires that you leave the line high when you're waiting for data from it
      response = spi_xfer(mmcsd_spi, 0xFF);
      
      // check to see if we got a response
      if(response != 0xFF)
      {   
         // fill in the response that we got and leave the function
         return response;
      }

      // wait for a little bit longer
      timeout--;
   }
   
   // for some reason, we didn't get a response back from the card
   //  return the proper error codes
   return RESP_TIMEOUT;
}

MMCSD_err mmcsd_get_r2(int r2[])
{
   r2[1] = mmcsd_get_r1();
   
   r2[0] = spi_xfer(mmcsd_spi, 0xFF);
   
   return 0;
}

MMCSD_err mmcsd_get_r3(int r3[])
{
   return mmcsd_get_r7(r3);
}

MMCSD_err mmcsd_get_r7(int r7[])
{
   int i;   // counter for loop
   
   // the top byte of r7 is r1
   r7[4]=mmcsd_get_r1();
   
   // fill in the other 4 bytes
   for(i = 0; i < 4; i++)
      r7[3 - i] = spi_xfer(mmcsd_spi, 0xFF);

   return r7[4];
}

MMCSD_err mmcsd_wait_for_token(int token)
{
   MMCSD_err r1;
   
   // get a token
   r1 = mmcsd_get_r1();
   
   // check to see if the token we recieved was the one that we were looking for
   if(r1 == token)
      return MMCSD_GOODEC;
   
   // if that wasn't right, return the error
   return r1;   
}

unsigned int8 mmcsd_crc7(char *data, unsigned int8 length)
{
   unsigned int8 i, ibit, c, crc;
    
   crc = 0x00;                                                                // Set initial value

   for (i = 0; i < length; i++, data++)
   {
      c = *data;

      for (ibit = 0; ibit < 8; ibit++)
      {
         crc = crc << 1;
         if ((c ^ crc) & 0x80) crc = crc ^ 0x09;                              // ^ is XOR
         c = c << 1;
      }

       crc = crc & 0x7F;
   }

   shift_left(&crc, 1, 1);                                                    // MMC card stores the result in the top 7 bits so shift them left 1
                                                                              // Should shift in a 1 not a 0 as one of the cards I have won't work otherwise
   return crc;
}

unsigned int16 mmcsd_crc16(char *data, unsigned int8 length)
{
   unsigned int8 i, ibit, c;

   unsigned int16 crc;

   crc = 0x0000;                                                                // Set initial value

   for (i = 0; i < length; i++, data++)
   {
      c = *data;

      for (ibit = 0; ibit < 8; ibit++)
      {
         crc = crc << 1;
         if ((c ^ crc) & 0x8000) crc = crc ^ 0x1021;                              // ^ is XOR
         c = c << 1;
      }

       crc = crc & 0x7FFF;
   }

   shift_left(&crc, 2, 1);                                                    // MMC card stores the result in the top 7 bits so shift them left 1
                                                                              // Should shift in a 1 not a 0 as one of the cards I have won't work otherwise
   return crc;
}

void mmcsd_select()
{
   output_low(MMCSD_PIN_SELECT);
}

void mmcsd_deselect()
{
   spi_xfer(mmcsd_spi, 0xFF);
   output_high(MMCSD_PIN_SELECT);
}

MMCSD_err mmcsd_load_buffer(void)
{
   g_MMCSDBufferChanged = FALSE;
   return(mmcsd_read_block(g_mmcsdBufferAddress, MMCSD_MAX_BLOCK_SIZE, g_mmcsd_buffer));
}

MMCSD_err mmcsd_flush_buffer(void)
{
   if (g_MMCSDBufferChanged)
   {
      g_MMCSDBufferChanged = FALSE;
      return(mmcsd_write_block(g_mmcsdBufferAddress, MMCSD_MAX_BLOCK_SIZE, g_mmcsd_buffer));
   }
   return(0);  //ok
}

MMCSD_err mmcsd_move_buffer(int32 new_addr)
{
   MMCSD_err ec = MMCSD_GOODEC;
   int32
      //cur_block,
      new_block;
   
   // make sure we're still on the same block
   //cur_block = g_mmcsdBufferAddress - (g_mmcsdBufferAddress % MMCSD_MAX_BLOCK_SIZE);
   new_block = new_addr - (new_addr % MMCSD_MAX_BLOCK_SIZE);
   
   //if(cur_block != new_block)
   if(g_mmcsdBufferAddress != new_block)
   {
      // dump the old buffer
      if (g_MMCSDBufferChanged)
      {
         ec = mmcsd_flush_buffer();
         if(ec != MMCSD_GOODEC)
            return ec;
         g_MMCSDBufferChanged = FALSE;
      }
         
      // figure out the best place for a block
      g_mmcsdBufferAddress = new_block;

      // load up a new buffer
      ec = mmcsd_load_buffer();
   }
   
   return ec;
}

MMCSD_err mmcsd_read_byte(int32 addr, char* data)
{
   MMCSD_err ec;
   
   ec = mmcsd_move_buffer(addr);
   if(ec != MMCSD_GOODEC)
   {
     return ec;
   }
 
   *data = g_mmcsd_buffer[addr % MMCSD_MAX_BLOCK_SIZE];

   return MMCSD_GOODEC;
}

MMCSD_err mmcsd_write_byte(int32 addr, char data)
{  
   MMCSD_err ec;
   ec = mmcsd_move_buffer(addr);
   if(ec != MMCSD_GOODEC)
     return ec;
   
   g_mmcsd_buffer[addr % MMCSD_MAX_BLOCK_SIZE] = data;
   
   g_MMCSDBufferChanged = TRUE;

   return MMCSD_GOODEC;
}

#endif