ossc/software/sys_controller/ulibSD/sd_io.c

508 lines
15 KiB
C

/*
* File: sd_io.c
* Author: Nelson Lombardo
* Year: 2015
* e-mail: nelson.lombardo@gmail.com
* License at the end of file.
*/
#include "sd_io.h"
#ifdef _M_IX86 // For use over x86
/*****************************************************************************/
/* Private Methods Prototypes - Direct work with PC file */
/*****************************************************************************/
/**
* \brief Get the total numbers of sectors in SD card.
* \param dev Device descriptor.
* \return Quantity of sectors. Zero if fail.
*/
DWORD __SD_Sectors (SD_DEV* dev);
/*****************************************************************************/
/* Private Methods - Direct work with PC file */
/*****************************************************************************/
DWORD __SD_Sectors (SD_DEV *dev)
{
if (dev->fp == NULL) return(0); // Fail
else {
fseek(dev->fp, 0L, SEEK_END);
return (((DWORD)(ftell(dev->fp)))/((DWORD)512)-1);
}
}
#else // For use with uControllers
/******************************************************************************
Private Methods Prototypes - Direct work with SD card
******************************************************************************/
/**
\brief Simple function to calculate power of two.
\param e Exponent.
\return Math function result.
*/
DWORD __SD_Power_Of_Two(BYTE e);
/**
\brief Assert the SD card (SPI CS low).
*/
inline void __SD_Assert (void);
/**
\brief Deassert the SD (SPI CS high).
*/
inline void __SD_Deassert (void);
/**
\brief Change to max the speed transfer.
\param throttle
*/
void __SD_Speed_Transfer (BYTE throttle);
/**
\brief Send SPI commands.
\param cmd Command to send.
\param arg Argument to send.
\return R1 response.
*/
BYTE __SD_Send_Cmd(BYTE cmd, DWORD arg);
/**
\brief Write a data block on SD card.
\param dat Storage the data to transfer.
\param token Inidicates the type of transfer (single or multiple).
*/
SDRESULTS __SD_Write_Block(SD_DEV *dev, void *dat, BYTE token);
/**
\brief Get the total numbers of sectors in SD card.
\param dev Device descriptor.
\return Quantity of sectors. Zero if fail.
*/
DWORD __SD_Sectors (SD_DEV *dev);
/******************************************************************************
Private Methods - Direct work with SD card
******************************************************************************/
DWORD __SD_Power_Of_Two(BYTE e)
{
DWORD partial = 1;
BYTE idx;
for(idx=0; idx!=e; idx++) partial *= 2;
return(partial);
}
inline void __SD_Assert(void){
SPI_CS_Low();
}
inline void __SD_Deassert(void){
SPI_CS_High();
}
void __SD_Speed_Transfer(BYTE throttle) {
if(throttle == HIGH) SPI_Freq_High();
else SPI_Freq_Low();
}
BYTE __SD_Send_Cmd(BYTE cmd, DWORD arg)
{
BYTE wiredata[10];
BYTE crc, res;
int timer_set;
//printf("Sending SD CMD 0x%x with arg 0x%x\n", cmd, arg);
// ACMD«n» is the command sequense of CMD55-CMD«n»
if(cmd & 0x80) {
cmd &= 0x7F;
res = __SD_Send_Cmd(CMD55, 0);
if (res > 1) return (res);
}
// Select the card
__SD_Deassert();
SPI_R(NULL, 4);
__SD_Assert();
// Send complete command set
wiredata[0] = cmd; // Start and command index
wiredata[1] = (arg >> 24); // Arg[31-24]
wiredata[2] = (arg >> 16); // Arg[23-16]
wiredata[3] = (arg >> 8 ); // Arg[15-08]
wiredata[4] = (arg >> 0 ); // Arg[07-00]
// CRC?
crc = 0x01; // Dummy CRC and stop
if(cmd == CMD0) crc = 0x95; // Valid CRC for CMD0(0)
if(cmd == CMD8) crc = 0x87; // Valid CRC for CMD8(0x1AA)
wiredata[5] = crc;
SPI_W(wiredata, 6);
// Receive command response
// Wait for a valid response in timeout of 5 milliseconds
timer_set = SPI_Timer_On(5);
do {
SPI_R(&res, 1);
} while((res & 0x80)&&(SPI_Timer_Status()==TRUE));
if (timer_set == 0)
SPI_Timer_Off();
// Return with the response value
//printf("CMD_res: %u\n", res);
return(res);
}
SDRESULTS __SD_Write_Block(SD_DEV *dev, void *dat, BYTE token)
{
WORD idx;
BYTE line;
// Send token (single or multiple)
SPI_WW(token);
// Single block write?
if(token != 0xFD)
{
// Send block data
for(idx=0; idx!=SD_BLK_SIZE; idx++) SPI_WW(*((BYTE*)dat + idx));
/* Dummy CRC */
SPI_WW(0xFF);
SPI_WW(0xFF);
// If not accepted, returns the reject error
if((SPI_RW(0xFF) & 0x1F) != 0x05) return(SD_REJECT);
}
#ifdef SD_IO_WRITE_WAIT_BLOCKER
// Waits until finish of data programming (blocked)
while(SPI_RW(0xFF)==0);
return(SD_OK);
#else
// Waits until finish of data programming with a timeout
SPI_Timer_On(SD_IO_WRITE_TIMEOUT_WAIT);
do {
line = SPI_RW(0xFF);
} while((line==0)&&(SPI_Timer_Status()==TRUE));
SPI_Timer_Off();
#ifdef SD_IO_DBG_COUNT
dev->debug.write++;
#endif
if(line==0) return(SD_BUSY);
else return(SD_OK);
#endif
}
DWORD __SD_Sectors (SD_DEV *dev)
{
BYTE csd[18];
BYTE tkn;
DWORD ss = 0;
WORD C_SIZE = 0;
BYTE C_SIZE_MULT = 0;
BYTE READ_BL_LEN = 0;
int timer_set;
if(__SD_Send_Cmd(CMD9, 0)==0)
{
// Wait for response
timer_set = SPI_Timer_On(5); // Wait for data packet (timeout of 5ms)
do {
SPI_R(&tkn, 1);
} while((tkn==0xFF)&&(SPI_Timer_Status()==TRUE));
if (timer_set == 0)
SPI_Timer_Off();
if(tkn!=0xFE)
return 0;
// TODO: CRC check
SPI_R(csd, 18);
if(dev->cardtype & SDCT_SD1)
{
ss = csd[0];
// READ_BL_LEN[83:80]: max. read data block length
READ_BL_LEN = (csd[5] & 0x0F);
// C_SIZE [73:62]
C_SIZE = (csd[6] & 0x03);
C_SIZE <<= 8;
C_SIZE |= (csd[7]);
C_SIZE <<= 2;
C_SIZE |= ((csd[8] >> 6) & 0x03);
// C_SIZE_MULT [49:47]
C_SIZE_MULT = (csd[9] & 0x03);
C_SIZE_MULT <<= 1;
C_SIZE_MULT |= ((csd[10] >> 7) & 0x01);
}
else if(dev->cardtype & SDCT_SD2)
{
// C_SIZE [69:48]
C_SIZE = (csd[7] & 0x3F);
C_SIZE <<= 8;
C_SIZE |= (csd[8] & 0xFF);
C_SIZE <<= 8;
C_SIZE |= (csd[9] & 0xFF);
// C_SIZE_MULT [--]. don't exits
C_SIZE_MULT = 17; //C_SIZE_MULT+2 = 19
printf("csize: %u\n", C_SIZE);
}
ss = (C_SIZE + 1);
// SD_BLK_SIZE = 2^9
//ss *= __SD_Power_Of_Two(C_SIZE_MULT + 2 + READ_BL_LEN - 9);
ss *= 1 << (C_SIZE_MULT + 2 + READ_BL_LEN - 9);
//ss /= SD_BLK_SIZE;
return (ss);
} else return (0); // Error
}
#endif // Private methods for uC
/******************************************************************************
Public Methods - Direct work with SD card
******************************************************************************/
SDRESULTS SD_Init(SD_DEV *dev)
{
BYTE initdata[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
#if defined(_M_IX86) // x86
dev->fp = fopen(dev->fn, "r+");
if (dev->fp == NULL)
return (SD_ERROR);
else
{
dev->last_sector = __SD_Sectors(dev);
#ifdef SD_IO_DBG_COUNT
dev->debug.read = 0;
dev->debug.write = 0;
#endif
return (SD_OK);
}
#else // uControllers
BYTE n, cmd, ct, ocr[4];
BYTE idx;
BYTE init_trys;
ct = 0;
for(init_trys=0; ((init_trys!=SD_INIT_TRYS)&&(!ct)); init_trys++)
{
// Initialize SPI for use with the memory card
SPI_Init();
__SD_Deassert();
SPI_Freq_Low();
// 80 dummy clocks
//for(idx = 0; idx != 10; idx++) SPI_RW(0xFF);
SPI_W(initdata, sizeof(initdata));
/*SPI_Timer_On(500);
while(SPI_Timer_Status()==TRUE);
SPI_Timer_Off();*/
dev->mount = FALSE;
/*SPI_Timer_On(500);
while ((__SD_Send_Cmd(CMD0, 0) != 1)&&(SPI_Timer_Status()==TRUE));
SPI_Timer_Off();*/
if (__SD_Send_Cmd(CMD0, 0) != 1)
continue;
// Idle state
// SD version 2?
if (__SD_Send_Cmd(CMD8, 0x1AA) == 1) {
// Get trailing return value of R7 resp
SPI_R(ocr, 4);
// VDD range of 2.7-3.6V is OK?
if ((ocr[2] == 0x01)&&(ocr[3] == 0xAA))
{
// Wait for leaving idle state (ACMD41 with HCS bit)...
SPI_Timer_On(1000);
while ((SPI_Timer_Status()==TRUE)&&(__SD_Send_Cmd(ACMD41, 1UL << 30)));
// CCS in the OCR?
if ((SPI_Timer_Status()==TRUE)&&(__SD_Send_Cmd(CMD58, 0) == 0))
{
SPI_R(ocr, 4);
// SD version 2?
ct = (ocr[0] & 0x40) ? SDCT_SD2 | SDCT_BLOCK : SDCT_SD2;
}
SPI_Timer_Off();
}
} else {
// SD version 1 or MMC?
if (__SD_Send_Cmd(ACMD41, 0) <= 1)
{
// SD version 1
ct = SDCT_SD1;
cmd = ACMD41;
} else {
// MMC version 3
ct = SDCT_MMC;
cmd = CMD1;
}
// Wait for leaving idle state
SPI_Timer_On(250);
while((SPI_Timer_Status()==TRUE)&&(__SD_Send_Cmd(cmd, 0)));
if(SPI_Timer_Status()==FALSE) ct = 0;
SPI_Timer_Off();
if(__SD_Send_Cmd(CMD59, 0)) ct = 0; // Deactivate CRC check (default)
if(__SD_Send_Cmd(CMD16, 512)) ct = 0; // Set R/W block length to 512 bytes
}
}
if(ct) {
dev->cardtype = ct;
dev->mount = TRUE;
dev->last_sector = __SD_Sectors(dev) - 1;
printf("lastsec %lu\n", dev->last_sector);
#ifdef SD_IO_DBG_COUNT
dev->debug.read = 0;
dev->debug.write = 0;
#endif
__SD_Speed_Transfer(HIGH); // High speed transfer
}
SPI_Release();
return (ct ? SD_OK : SD_NOINIT);
#endif
}
SDRESULTS SD_Read(SD_DEV *dev, void *dat, DWORD sector, WORD ofs, WORD cnt)
{
#if defined(_M_IX86) // x86
// Check the sector query
if((sector > dev->last_sector)||(cnt == 0)) return(SD_PARERR);
if(dev->fp!=NULL)
{
if (fseek(dev->fp, ((512 * sector) + ofs), SEEK_SET)!=0)
return(SD_ERROR);
else {
if(fread(dat, 1, (cnt - ofs),dev->fp)==(cnt - ofs))
{
#ifdef SD_IO_DBG_COUNT
dev->debug.read++;
#endif
return(SD_OK);
}
else return(SD_ERROR);
}
} else {
return(SD_ERROR);
}
#else // uControllers
SDRESULTS res;
BYTE tkn;
WORD remaining;
res = SD_ERROR;
if ((sector > dev->last_sector)||(cnt == 0)) return(SD_PARERR);
// Convert sector number to byte address (sector * SD_BLK_SIZE) for SDC1
if (!(dev->cardtype & SDCT_BLOCK))
sector *= SD_BLK_SIZE;
if (__SD_Send_Cmd(CMD17, sector) == 0) {
SPI_Timer_On(100); // Wait for data packet (timeout of 100ms)
do {
SPI_R(&tkn, 1);
} while((tkn==0xFF)&&(SPI_Timer_Status()==TRUE));
SPI_Timer_Off();
// Token of single block?
if(tkn==0xFE) {
// Size block (512 bytes) + CRC (2 bytes) - offset - bytes to count
remaining = SD_BLK_SIZE + 2 - ofs - cnt;
// Skip offset
if(ofs) {
SPI_R(NULL, ofs);
}
// I receive the data and I write in user's buffer
SPI_R((BYTE*)dat, cnt);
// Skip remaining
// TODO: CRC
SPI_R(NULL, remaining);
res = SD_OK;
}
}
SPI_Release();
#ifdef SD_IO_DBG_COUNT
dev->debug.read++;
#endif
return(res);
#endif
}
#ifdef SD_IO_WRITE
SDRESULTS SD_Write(SD_DEV *dev, void *dat, DWORD sector)
{
#if defined(_M_IX86) // x86
// Query ok?
if(sector > dev->last_sector) return(SD_PARERR);
if(dev->fp != NULL)
{
if(fseek(dev->fp, SD_BLK_SIZE * sector, SEEK_SET)!=0)
return(SD_ERROR);
else {
if(fwrite(dat, 1, SD_BLK_SIZE, dev->fp)==SD_BLK_SIZE)
{
#ifdef SD_IO_DBG_COUNT
dev->debug.write++;
#endif
return(SD_OK);
}
else return(SD_ERROR);
}
} else return(SD_ERROR);
#else // uControllers
// Query ok?
if(sector > dev->last_sector) return(SD_PARERR);
// Convert sector number to byte address (sector * SD_BLK_SIZE) for SDC1
if (!(dev->cardtype & SDCT_BLOCK))
sector *= SD_BLK_SIZE;
// Single block write (token <- 0xFE)
// Convert sector number to bytes address (sector * SD_BLK_SIZE)
if(__SD_Send_Cmd(CMD24, sector)==0)
return(__SD_Write_Block(dev, dat, 0xFE));
else
return(SD_ERROR);
#endif
}
#endif
SDRESULTS SD_Status(SD_DEV *dev)
{
#if defined(_M_IX86)
return((dev->fp == NULL) ? SD_OK : SD_NORESPONSE);
#else
return(__SD_Send_Cmd(CMD0, 0) ? SD_OK : SD_NORESPONSE);
#endif
}
// «sd_io.c» is part of:
/*----------------------------------------------------------------------------/
/ ulibSD - Library for SD cards semantics (C)Nelson Lombardo, 2015
/-----------------------------------------------------------------------------/
/ ulibSD library is a free software that opened under license policy of
/ following conditions.
/
/ Copyright (C) 2015, ChaN, all right reserved.
/
/ 1. Redistributions of source code must retain the above copyright notice,
/ this condition and the following disclaimer.
/
/ This software is provided by the copyright holder and contributors "AS IS"
/ and any warranties related to this software are DISCLAIMED.
/ The copyright owner or contributors be NOT LIABLE for any damages caused
/ by use of this software.
/----------------------------------------------------------------------------*/
// Derived from Mister Chan works on FatFs code (http://elm-chan.org/fsw/ff/00index_e.html):
/*----------------------------------------------------------------------------/
/ FatFs - FAT file system module R0.11 (C)ChaN, 2015
/-----------------------------------------------------------------------------/
/ FatFs module is a free software that opened under license policy of
/ following conditions.
/
/ Copyright (C) 2015, ChaN, all right reserved.
/
/ 1. Redistributions of source code must retain the above copyright notice,
/ this condition and the following disclaimer.
/
/ This software is provided by the copyright holder and contributors "AS IS"
/ and any warranties related to this software are DISCLAIMED.
/ The copyright owner or contributors be NOT LIABLE for any damages caused
/ by use of this software.
/----------------------------------------------------------------------------*/