/**
 * SD Card RAW
 * http://www.moty22.co.uk/sd.php
 */

#include "sd_card_raw.h"
#include "sd_card_raw_cbk.h"

#define SD_CARD_MAX_RETRIES             (5000)

static uint8_t Is_SDHC = 0; //standard sd

static uint8_t SD_Card_Send_Command(uint8_t frame1, uint32_t adrs, uint8_t frame2);

/**
 * SD Card command
 */
static uint8_t SD_Card_Send_Command(uint8_t frame1, uint32_t adrs, uint8_t frame2)
{
   uint16_t i;
   uint8_t res;

   SD_Card_Write_SPI(0xFF);
   SD_Card_Write_SPI(frame1 | 0x40); //first 2 bits are 01
   SD_Card_Write_SPI((adrs & 0xFF000000) >> 24); //first of the 4 bytes address
   SD_Card_Write_SPI((adrs & 0x00FF0000) >> 16);
   SD_Card_Write_SPI((adrs & 0x0000FF00) >> 8);
   SD_Card_Write_SPI(adrs & 0x000000FF);
   SD_Card_Write_SPI(frame2 | 1); //CRC and last bit 1

   for (i = 0; i < SD_CARD_MAX_RETRIES; i++)   // wait for OK
   {
      res = SD_Card_Write_SPI(0xFF);
      if (res != 0xFF)
      {
         break;
      }
   }
   return res;
}

/**
 * Refer to sd_card_raw.h
 */
void SD_Card_Initialize(void)
{
   uint8_t i;
   uint8_t r[4];
   uint8_t res;
   uint16_t retries;

   SD_Card_Set_Slow_Clock();
   SD_Card_Chip_Select(1);

   for (i = 0; i < 16; i++)
   {
      SD_Card_Write_SPI(0xFF); // min 100 clocks
   }
   SD_Card_Chip_Select(0); // Enabled for SPI mode

   /* Reset the SD Card */
   retries = SD_CARD_MAX_RETRIES;
   do
   {
      res = SD_Card_Send_Command(0x00, 0, 0x95);
   }while((res != 1) && retries--);
   if (res != 1)
   {
      SD_Card_Error(__LINE__);
   }

   /* Read the OCR register */
   res = SD_Card_Send_Command(58, 0, 0xFF);
   if(res != 1)
   {
      SD_Card_Error(__LINE__);
   }
   //read CCS in the OCR - SD or Is_SDHC
   r[0] = SD_Card_Write_SPI(0xFF);
   r[1] = SD_Card_Write_SPI(0xFF);
   r[2] = SD_Card_Write_SPI(0xFF);
   r[3] = SD_Card_Write_SPI(0xFF); //rest of R3
   Is_SDHC = r[0] & 0x40;
   if (Is_SDHC)
   {
      SD_Card_Error(__LINE__);
   }
   /* check that the SDCard supports 3.3V */
   if((r[1] & 0x03) != 0x03)
   {
      SD_Card_Error(__LINE__);
   }

   /* Start the card initialization process */
   retries = SD_CARD_MAX_RETRIES;
   do
   {
      res = SD_Card_Send_Command(1, 0, 0xFF);
   }while(((res & 0x01) != 0) && retries--);

   /* The command 55 shall precede all application specific commands */
   SD_Card_Send_Command(55, 0, 0xFF);

   /* Start the SD Card mode ACMD41 */
   retries = SD_CARD_MAX_RETRIES;
   do
   {
      res = SD_Card_Send_Command(41, 0, 0xFF);
   }while(((res & 0x01) != 0) && retries--);

   //Set sector size to 512
   res = SD_Card_Send_Command(16, SD_CARD_BLOCK_SIZE, 0xFF);
   if(res)
   {
      SD_Card_Error(__LINE__);
   }

   SD_Card_Chip_Select(1);
   SD_Card_Set_Fast_Clock();
}

/**
 * Refer to sd_card_raw.h
 */
void SD_Card_Write(uint32_t loc, uint8_t* data)
{
   uint16_t r;
   uint16_t i;
   uint16_t retries = SD_CARD_MAX_RETRIES;

   SD_Card_Chip_Select(0);

   r = SD_Card_Send_Command(25, loc, 0xFF); //sector write
   if (r != 0)
   {
      SD_Card_Error(__LINE__);
   }
   SD_Card_Write_SPI(0xFF);
   SD_Card_Write_SPI(0xFF);
   SD_Card_Write_SPI(0xFF);

   SD_Card_Write_SPI(0xFC);  //multi sector token byte
   for (i = 0; i < SD_CARD_BLOCK_SIZE; i++)
   {
      SD_Card_Write_SPI(data[i]);
   }
   SD_Card_Write_SPI(0xFF);  // CRC
   SD_Card_Write_SPI(0xFF);  // CRC

   do
   {
      r = SD_Card_Write_SPI(0xFF);
   }while(retries-- && (0xFF == r));

   if ((r & 0x0F) == 0x05)
   {
      //data accepted = 0101
      for (i = 10000; i > 0; i--)
      {
         r = SD_Card_Write_SPI(0xFF);
         if (r)
         {
            break;
         }
      }
   }
   else
   {
      SD_Card_Error(__LINE__);
   }

   while (SD_Card_Write_SPI(0xFF) != 0xFF);   // while busy

   SD_Card_Write_SPI(0xFD);  //stop transfer token byte

   SD_Card_Write_SPI(0xFF);
   SD_Card_Write_SPI(0xFF);
   while (SD_Card_Write_SPI(0xFF) != 0xFF); // while busy

   SD_Card_Chip_Select(1);
}

/**
 * Refer to sd_card_raw.h
 */
void SD_Card_Read(uint32_t loc, uint8_t* data)
{
   uint16_t i;
   uint8_t r;

   SD_Card_Chip_Select(0);

   r = SD_Card_Send_Command(17, loc, 0xFF);   //read single-sector
   if (r != 0)
   {
      SD_Card_Error(__LINE__);
   }

   // wait for not busy
   do
   {
      r = SD_Card_Write_SPI(0xFF);
   }while(r == 0xFF);

   if(r != 0xFE)
   {
      SD_Card_Error(__LINE__);
   }

   for (i = 0; i < SD_CARD_BLOCK_SIZE; i++)
   {
      data[i] = SD_Card_Write_SPI(0xFF);
   }
   SD_Card_Write_SPI(0xFF);  //discard of CRC
   SD_Card_Write_SPI(0xFF);

   SD_Card_Chip_Select(1);

   SD_Card_Write_SPI(0xFF);
}
