774 lines
19 KiB
C
774 lines
19 KiB
C
/* sd2iec - SD/MMC to Commodore serial bus interface/controller
|
|
Copyright (C) 2007-2009 Ingo Korb <ingo@akana.de>
|
|
|
|
Inspiration and low-level SD/MMC access based on code from MMC2IEC
|
|
by Lars Pontoppidan et al., see sdcard.c|h and config.h.
|
|
|
|
FAT filesystem access based on code from ChaN and Jim Brain, see ff.c|h.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License only.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
|
sdcard.c: SD/MMC access routines
|
|
|
|
Extended, optimized and cleaned version of code from MMC2IEC,
|
|
original copyright header follows:
|
|
|
|
//
|
|
// Title : SD/MMC Card driver
|
|
// Author : Lars Pontoppidan, Aske Olsson, Pascal Dufour,
|
|
// Date : Jan. 2006
|
|
// Version : 0.42
|
|
// Target MCU : Atmel AVR Series
|
|
//
|
|
// CREDITS:
|
|
// This module is developed as part of a project at the technical univerisity of
|
|
// Denmark, DTU.
|
|
//
|
|
// DESCRIPTION:
|
|
// This SD card driver implements the fundamental communication with a SD card.
|
|
// The driver is confirmed working on 8 MHz and 14.7456 MHz AtMega32 and has
|
|
// been tested successfully with a large number of different SD and MMC cards.
|
|
//
|
|
// DISCLAIMER:
|
|
// The author is in no way responsible for any problems or damage caused by
|
|
// using this code. Use at your own risk.
|
|
//
|
|
// LICENSE:
|
|
// This code is distributed under the GNU Public License
|
|
// which can be found at http://www.gnu.org/licenses/gpl.txt
|
|
//
|
|
|
|
The exported functions in this file are weak-aliased to their corresponding
|
|
versions defined in diskio.h so when this file is the only diskio provider
|
|
compiled in they will be automatically used by the linker.
|
|
|
|
*/
|
|
|
|
#include <avr/io.h>
|
|
#include <avr/interrupt.h>
|
|
#include <avr/pgmspace.h>
|
|
#include <util/crc16.h>
|
|
#include <util/delay.h>
|
|
#include "config.h"
|
|
#include "avrcompat.h"
|
|
#include "crc7.h"
|
|
#include "diskio.h"
|
|
#include "spi.h"
|
|
#include "uart.h"
|
|
#include "sdcard.h"
|
|
#include "fpga_spi.h"
|
|
#ifndef TRUE
|
|
#define TRUE -1
|
|
#endif
|
|
#ifndef FALSE
|
|
#define FALSE 0
|
|
#endif
|
|
|
|
#ifdef CONFIG_TWINSD
|
|
# define MAX_CARDS 2
|
|
#else
|
|
# define MAX_CARDS 1
|
|
#endif
|
|
|
|
// SD/MMC commands
|
|
#define GO_IDLE_STATE 0
|
|
#define SEND_OP_COND 1
|
|
#define SWITCH_FUNC 6
|
|
#define SEND_IF_COND 8
|
|
#define SEND_CSD 9
|
|
#define SEND_CID 10
|
|
#define STOP_TRANSMISSION 12
|
|
#define SEND_STATUS 13
|
|
#define SET_BLOCKLEN 16
|
|
#define READ_SINGLE_BLOCK 17
|
|
#define READ_MULTIPLE_BLOCK 18
|
|
#define WRITE_BLOCK 24
|
|
#define WRITE_MULTIPLE_BLOCK 25
|
|
#define PROGRAM_CSD 27
|
|
#define SET_WRITE_PROT 28
|
|
#define CLR_WRITE_PROT 29
|
|
#define SEND_WRITE_PROT 30
|
|
#define ERASE_WR_BLK_STAR_ADDR 32
|
|
#define ERASE_WR_BLK_END_ADDR 33
|
|
#define ERASE 38
|
|
#define LOCK_UNLOCK 42
|
|
#define APP_CMD 55
|
|
#define GEN_CMD 56
|
|
#define READ_OCR 58
|
|
#define CRC_ON_OFF 59
|
|
|
|
// SD ACMDs
|
|
#define SD_STATUS 13
|
|
#define SD_SEND_NUM_WR_BLOCKS 22
|
|
#define SD_SET_WR_BLK_ERASE_COUNT 23
|
|
#define SD_SEND_OP_COND 41
|
|
#define SD_SET_CLR_CARD_DETECT 42
|
|
#define SD_SEND_SCR 51
|
|
|
|
// R1 status bits
|
|
#define STATUS_IN_IDLE 1
|
|
#define STATUS_ERASE_RESET 2
|
|
#define STATUS_ILLEGAL_COMMAND 4
|
|
#define STATUS_CRC_ERROR 8
|
|
#define STATUS_ERASE_SEQ_ERROR 16
|
|
#define STATUS_ADDRESS_ERROR 32
|
|
#define STATUS_PARAMETER_ERROR 64
|
|
|
|
|
|
/* Card types - cardtype == 0 is MMC */
|
|
#define CARD_SD (1<<0)
|
|
#define CARD_SDHC (1<<1)
|
|
|
|
static uint8_t cardtype[MAX_CARDS];
|
|
|
|
/**
|
|
* getbits - read value from bit buffer
|
|
* @buffer: pointer to the data buffer
|
|
* @start : index of the first bit in the value
|
|
* @bits : number of bits in the value
|
|
*
|
|
* This function returns a value from the memory region passed as
|
|
* buffer, starting with bit "start" and "bits" bit long. The buffer
|
|
* is assumed to be MSB first, passing 0 for start will read starting
|
|
* from the highest-value bit of the first byte of the buffer.
|
|
*/
|
|
static uint32_t getbits(void *buffer, uint16_t start, int8_t bits) {
|
|
uint8_t *buf = buffer;
|
|
uint32_t result = 0;
|
|
|
|
if ((start % 8) != 0) {
|
|
/* Unaligned start */
|
|
result += buf[start / 8] & (0xff >> (start % 8));
|
|
bits -= 8 - (start % 8);
|
|
start += 8 - (start % 8);
|
|
}
|
|
while (bits >= 8) {
|
|
result = (result << 8) + buf[start / 8];
|
|
start += 8;
|
|
bits -= 8;
|
|
}
|
|
if (bits > 0) {
|
|
result = result << bits;
|
|
result = result + (buf[start / 8] >> (8-bits));
|
|
} else if (bits < 0) {
|
|
/* Fraction of a single byte */
|
|
result = result >> -bits;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static uint8_t sdResponse(uint8_t expected)
|
|
{
|
|
unsigned short count = 0x0FFF;
|
|
|
|
while ((spiTransferByte(0xFF) != expected) && count )
|
|
count--;
|
|
|
|
// If count didn't run out, return success
|
|
return (count != 0);
|
|
}
|
|
|
|
static uint8_t sdWaitWriteFinish(void)
|
|
{
|
|
uint32_t count = 0x1FFFF; // wait for quite some time
|
|
|
|
while ((spiTransferByte(0xFF) == 0) && count )
|
|
count--;
|
|
|
|
// If count didn't run out, return success
|
|
return (count != 0);
|
|
}
|
|
|
|
static void deselectCard(uint8_t card) {
|
|
// Send 8 clock cycles
|
|
SPI_SS_HIGH(card);
|
|
spiTransferByte(0xff);
|
|
}
|
|
|
|
/**
|
|
* sendCommand - send a command to the SD card
|
|
* @card : card number to be accessed
|
|
* @command : command to be sent
|
|
* @parameter: parameter to be sent
|
|
* @deselect : Flags if the card should be deselected afterwards
|
|
*
|
|
* This function calculates the correct CRC7 for the command and
|
|
* parameter and transmits all of it to the SD card. If requested
|
|
* the card will be deselected afterwards.
|
|
*/
|
|
static int sendCommand(const uint8_t card,
|
|
const uint8_t command,
|
|
const uint32_t parameter,
|
|
const uint8_t deselect) {
|
|
union {
|
|
uint32_t l;
|
|
uint8_t c[4];
|
|
} long2char;
|
|
|
|
uint8_t i,crc=0,errorcount;
|
|
uint16_t counter;
|
|
|
|
long2char.l = parameter;
|
|
crc = crc7update(0 , 0x40+command);
|
|
crc = crc7update(crc, long2char.c[3]);
|
|
crc = crc7update(crc, long2char.c[2]);
|
|
crc = crc7update(crc, long2char.c[1]);
|
|
crc = crc7update(crc, long2char.c[0]);
|
|
crc = (crc << 1) | 1;
|
|
|
|
errorcount = 0;
|
|
while (errorcount < CONFIG_SD_AUTO_RETRIES) {
|
|
// Select card
|
|
SPI_SS_LOW(card);
|
|
#ifdef CONFIG_TWINSD
|
|
if (card == 0 && command == GO_IDLE_STATE)
|
|
/* Force both cards to SPI mode simultaneously */
|
|
SPI_SS_LOW(1);
|
|
#endif
|
|
|
|
// Transfer command
|
|
spiTransferByte(0x40+command);
|
|
spiTransferLong(parameter);
|
|
spiTransferByte(crc);
|
|
|
|
// Wait for a valid response
|
|
counter = 0;
|
|
do {
|
|
i = spiTransferByte(0xff);
|
|
counter++;
|
|
} while (i & 0x80 && counter < 0x1000);
|
|
|
|
#ifdef CONFIG_TWINSD
|
|
if (card == 0 && command == GO_IDLE_STATE)
|
|
SPI_SS_HIGH(1);
|
|
#endif
|
|
|
|
// Check for CRC error
|
|
// can't reliably retry unless deselect is allowed
|
|
if (deselect && (i & STATUS_CRC_ERROR)) {
|
|
uart_putc('x');
|
|
deselectCard(card);
|
|
errorcount++;
|
|
continue;
|
|
}
|
|
|
|
if (deselect) deselectCard(card);
|
|
break;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
// Extended init sequence for SDHC support
|
|
static uint8_t extendedInit(const uint8_t card) {
|
|
uint8_t i;
|
|
uint32_t answer;
|
|
// Send CMD8: SEND_IF_COND
|
|
// 0b000110101010 == 2.7-3.6V supply, check pattern 0xAA
|
|
i = sendCommand(card, SEND_IF_COND, 0b000110101010, 0);
|
|
if (i > 1) {
|
|
// Card returned an error, ok (MMC oder SD1.x) but not SDHC
|
|
deselectCard(card);
|
|
return TRUE;
|
|
}
|
|
|
|
// No error, continue SDHC initialization
|
|
answer = spiTransferLong(0);
|
|
deselectCard(card);
|
|
|
|
if (((answer >> 8) & 0x0f) != 0b0001) {
|
|
// Card didn't accept our voltage specification
|
|
return FALSE;
|
|
}
|
|
|
|
// Verify echo-back of check pattern
|
|
if ((answer & 0xff) != 0b10101010) {
|
|
// Check pattern mismatch, working but not SD2.0 compliant
|
|
// The specs say we should not use the card, but let's try anyway.
|
|
return TRUE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// SD common initialisation
|
|
static void sdInit(const uint8_t card) {
|
|
uint8_t i;
|
|
uint16_t counter;
|
|
|
|
SD_SPI_OFFLOAD = 0;
|
|
|
|
counter = 0xffff;
|
|
do {
|
|
// Prepare for ACMD, send CMD55: APP_CMD
|
|
i = sendCommand(card, APP_CMD, 0, 1);
|
|
if (i > 1)
|
|
// Command not accepted, could be MMC
|
|
return;
|
|
|
|
// Send ACMD41: SD_SEND_OP_COND
|
|
// 1L<<30 == Host has High Capacity Support
|
|
i = sendCommand(card, SD_SEND_OP_COND, 1L<<30, 1);
|
|
// Repeat while card card accepts command but isn't ready
|
|
} while (i == 1 && --counter > 0);
|
|
|
|
// Ignore failures, there is at least one Sandisk MMC card
|
|
// that accepts CMD55, but not ACMD41.
|
|
if (i == 0)
|
|
/* We know that a card is SD if ACMD41 was accepted. */
|
|
cardtype[card] |= CARD_SD;
|
|
}
|
|
|
|
/* Detect changes of SD card 0 */
|
|
#ifdef SD_CHANGE_VECT
|
|
ISR(SD_CHANGE_VECT) {
|
|
if (SDCARD_DETECT)
|
|
disk_state = DISK_CHANGED;
|
|
else
|
|
disk_state = DISK_REMOVED;
|
|
}
|
|
#endif
|
|
|
|
#ifdef CONFIG_TWINSD
|
|
/* Detect changes of SD card 1 */
|
|
ISR(SD2_CHANGE_VECT) {
|
|
if (SD2_DETECT)
|
|
disk_state = DISK_CHANGED;
|
|
else
|
|
disk_state = DISK_REMOVED;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Public functions
|
|
//
|
|
void sd_init(void) {
|
|
SDCARD_DETECT_SETUP();
|
|
SDCARD_WP_SETUP();
|
|
SD_CHANGE_SETUP();
|
|
#ifdef CONFIG_TWINSD
|
|
/* Initialize the control lines for card 2 */
|
|
SD2_SETUP();
|
|
SD2_CHANGE_SETUP();
|
|
#endif
|
|
}
|
|
void disk_init(void) __attribute__ ((weak, alias("sd_init")));
|
|
|
|
|
|
DSTATUS sd_status(BYTE drv) {
|
|
#ifdef CONFIG_TWINSD
|
|
if (drv != 0) {
|
|
if (SD2_DETECT) {
|
|
if (SD2_PIN & SD2_WP) {
|
|
return STA_PROTECT;
|
|
} else {
|
|
return RES_OK;
|
|
}
|
|
} else {
|
|
return STA_NOINIT|STA_NODISK;
|
|
}
|
|
} else
|
|
#endif
|
|
if (SDCARD_DETECT) {
|
|
// uart_putc('0');
|
|
if (SDCARD_WP) {
|
|
// uart_putc('1');
|
|
return STA_PROTECT;
|
|
} else {
|
|
// uart_putc('2');
|
|
return RES_OK;
|
|
}
|
|
} else {
|
|
// uart_putc('3');
|
|
return STA_NOINIT|STA_NODISK;
|
|
}
|
|
}
|
|
DSTATUS disk_status(BYTE drv) __attribute__ ((weak, alias("sd_status")));
|
|
|
|
|
|
/**
|
|
* sd_initialize - initialize SD card
|
|
* @drv : drive
|
|
*
|
|
* This function tries to initialize the selected SD card.
|
|
*/
|
|
DSTATUS sd_initialize(BYTE drv) {
|
|
uint8_t i;
|
|
uint16_t counter;
|
|
uint32_t answer;
|
|
|
|
if (drv >= MAX_CARDS)
|
|
return STA_NOINIT|STA_NODISK;
|
|
|
|
/* Don't bother initializing a card that isn't there */
|
|
// uart_putc('#');
|
|
if (sd_status(drv) & STA_NODISK)
|
|
return sd_status(drv);
|
|
|
|
/* JLB: Should be in sd_init, but some uIEC versions have
|
|
* IEC lines tied to SPI, so I moved it here to resolve the
|
|
* conflict.
|
|
*/
|
|
spiInit();
|
|
|
|
disk_state = DISK_ERROR;
|
|
|
|
cardtype[drv] = 0;
|
|
|
|
SPI_SS_HIGH(drv);
|
|
|
|
// Send 80 clks
|
|
for (i=0; i<10; i++) {
|
|
spiTransferByte(0xFF);
|
|
}
|
|
|
|
// Reset card
|
|
i = sendCommand(drv, GO_IDLE_STATE, 0, 1);
|
|
if (i != 1) {
|
|
return STA_NOINIT | STA_NODISK;
|
|
}
|
|
|
|
if (!extendedInit(drv))
|
|
return STA_NOINIT | STA_NODISK;
|
|
|
|
sdInit(drv);
|
|
|
|
counter = 0xffff;
|
|
// According to the spec READ_OCR should work at this point
|
|
// without retries. One of my Sandisk-cards thinks otherwise.
|
|
do {
|
|
// Send CMD58: READ_OCR
|
|
i = sendCommand(drv, READ_OCR, 0, 0);
|
|
if (i > 1)
|
|
deselectCard(drv);
|
|
} while (i > 1 && counter-- > 0);
|
|
|
|
if (counter > 0) {
|
|
answer = spiTransferLong(0);
|
|
|
|
// See if the card likes our supply voltage
|
|
if (!(answer & SD_SUPPLY_VOLTAGE)) {
|
|
// The code isn't set up to completely ignore the card,
|
|
// but at least report it as nonworking
|
|
deselectCard(drv);
|
|
return STA_NOINIT | STA_NODISK;
|
|
}
|
|
|
|
// See what card we've got
|
|
if (answer & 0x40000000) {
|
|
cardtype[drv] |= CARD_SDHC;
|
|
}
|
|
}
|
|
|
|
// Keep sending CMD1 (SEND_OP_COND) command until zero response
|
|
counter = 0xffff;
|
|
do {
|
|
i = sendCommand(drv, SEND_OP_COND, 1L<<30, 1);
|
|
counter--;
|
|
} while (i != 0 && counter > 0);
|
|
|
|
if (counter==0) {
|
|
return STA_NOINIT | STA_NODISK;
|
|
}
|
|
|
|
#ifdef CONFIG_SD_DATACRC
|
|
// Enable CRC checking
|
|
// The SD spec says that the host "should" send CRC_ON_OFF before ACMD_SEND_OP_COND.
|
|
// The MMC manual I have says that CRC_ON_OFF isn't allowed before SEND_OP_COND.
|
|
// Let's just hope that all SD cards work with this order. =(
|
|
i = sendCommand(drv, CRC_ON_OFF, 1, 1);
|
|
if (i > 1) {
|
|
return STA_NOINIT | STA_NODISK;
|
|
}
|
|
#endif
|
|
|
|
// Send MMC CMD16(SET_BLOCKLEN) to 512 bytes
|
|
i = sendCommand(drv, SET_BLOCKLEN, 512, 1);
|
|
if (i != 0) {
|
|
return STA_NOINIT | STA_NODISK;
|
|
}
|
|
|
|
// Thats it!
|
|
disk_state = DISK_OK;
|
|
return sd_status(drv);
|
|
}
|
|
DSTATUS disk_initialize(BYTE drv) __attribute__ ((weak, alias("sd_initialize")));
|
|
|
|
|
|
/**
|
|
* sd_read - reads sectors from the SD card to buffer
|
|
* @drv : drive
|
|
* @buffer: pointer to the buffer
|
|
* @sector: first sector to be read
|
|
* @count : number of sectors to be read
|
|
*
|
|
* This function reads count sectors from the SD card starting
|
|
* at sector to buffer. Returns RES_ERROR if an error occured or
|
|
* RES_OK if successful. Up to SD_AUTO_RETRIES will be made if
|
|
* the calculated data CRC does not match the one sent by the
|
|
* card. If there were errors during the command transmission
|
|
* disk_state will be set to DISK_ERROR and no retries are made.
|
|
*/
|
|
DRESULT sd_read(BYTE drv, BYTE *buffer, DWORD sector, BYTE count) {
|
|
uint8_t sec,res,tmp,errorcount;
|
|
uint16_t crc,recvcrc;
|
|
if (drv >= MAX_CARDS)
|
|
return RES_PARERR;
|
|
|
|
for (sec=0;sec<count;sec++) {
|
|
errorcount = 0;
|
|
while (errorcount < CONFIG_SD_AUTO_RETRIES) {
|
|
if (cardtype[drv] & CARD_SDHC)
|
|
res = sendCommand(drv, READ_SINGLE_BLOCK, sector+sec, 0);
|
|
else
|
|
res = sendCommand(drv, READ_SINGLE_BLOCK, (sector+sec) << 9, 0);
|
|
|
|
if (res != 0) {
|
|
uart_putc('?');
|
|
dprintf("SD error: %02x\n", res);
|
|
SPI_SS_HIGH(drv);
|
|
disk_state = DISK_ERROR;
|
|
return RES_ERROR;
|
|
}
|
|
|
|
// Wait for data token
|
|
if (!sdResponse(0xFE)) {
|
|
uart_putc('-');
|
|
SPI_SS_HIGH(drv);
|
|
disk_state = DISK_ERROR;
|
|
return RES_ERROR;
|
|
}
|
|
|
|
uint16_t i;
|
|
#ifdef CONFIG_SD_DATACRC
|
|
BYTE *oldbuffer = buffer;
|
|
#endif
|
|
|
|
// Get data
|
|
crc = 0;
|
|
|
|
if(SD_SPI_OFFLOAD) {
|
|
// uart_putc('O');
|
|
PORTB |= _BV(PB2);
|
|
DDRB |= _BV(PB2);
|
|
_delay_us(1);
|
|
PORTB &= ~_BV(PB2);
|
|
DDRB &= ~_BV(PB7); // tristate SCK
|
|
PORTB |= _BV(PB2);
|
|
DDRB &= ~_BV(PB2);
|
|
_delay_us(1);
|
|
while(!(PINB & _BV(PB2)));
|
|
DDRB |= _BV(PB7);
|
|
DDRB |= _BV(PB2);
|
|
// _delay_us(1);
|
|
deselectCard(drv);
|
|
SD_SPI_OFFLOAD = 0;
|
|
return RES_OK;
|
|
SPDR = 0xff;
|
|
} else {
|
|
// Initiate data exchange over SPI
|
|
SPDR = 0xff;
|
|
|
|
for (i=0; i<512; i++) {
|
|
// Wait until data has been received
|
|
loop_until_bit_is_set(SPSR, SPIF);
|
|
tmp = SPDR;
|
|
// Transmit the next byte while we store the current one
|
|
SPDR = 0xff;
|
|
|
|
*(buffer++) = tmp;
|
|
#ifdef CONFIG_SD_DATACRC
|
|
crc = _crc_xmodem_update(crc, tmp);
|
|
#endif
|
|
}
|
|
}
|
|
// Wait until the first CRC byte is received
|
|
loop_until_bit_is_set(SPSR, SPIF);
|
|
|
|
// Check CRC
|
|
recvcrc = (SPDR << 8) + spiTransferByte(0xff);
|
|
#ifdef CONFIG_SD_DATACRC
|
|
if (recvcrc != crc) {
|
|
uart_putc('X');
|
|
deselectCard(drv);
|
|
errorcount++;
|
|
buffer = oldbuffer;
|
|
continue;
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
deselectCard(drv);
|
|
|
|
if (errorcount >= CONFIG_SD_AUTO_RETRIES) return RES_ERROR;
|
|
}
|
|
return RES_OK;
|
|
}
|
|
DRESULT disk_read(BYTE drv, BYTE *buffer, DWORD sector, BYTE count) __attribute__ ((weak, alias("sd_read")));
|
|
|
|
|
|
|
|
/**
|
|
* sd_write - writes sectors from buffer to the SD card
|
|
* @drv : drive
|
|
* @buffer: pointer to the buffer
|
|
* @sector: first sector to be written
|
|
* @count : number of sectors to be written
|
|
*
|
|
* This function writes count sectors from buffer to the SD card
|
|
* starting at sector. Returns RES_ERROR if an error occured,
|
|
* RES_WPRT if the card is currently write-protected or RES_OK
|
|
* if successful. Up to SD_AUTO_RETRIES will be made if the card
|
|
* signals a CRC error. If there were errors during the command
|
|
* transmission disk_state will be set to DISK_ERROR and no retries
|
|
* are made.
|
|
*/
|
|
DRESULT sd_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count) {
|
|
uint8_t res,sec,errorcount,status;
|
|
uint16_t crc;
|
|
|
|
if (drv >= MAX_CARDS)
|
|
return RES_PARERR;
|
|
|
|
#ifdef CONFIG_TWINSD
|
|
if (drv != 0) {
|
|
if (SD2_PIN & SD2_WP)
|
|
return RES_WRPRT;
|
|
} else
|
|
#endif
|
|
|
|
if (SDCARD_WP) return RES_WRPRT;
|
|
|
|
for (sec=0;sec<count;sec++) {
|
|
errorcount = 0;
|
|
while (errorcount < CONFIG_SD_AUTO_RETRIES) {
|
|
if (cardtype[drv] & CARD_SDHC)
|
|
res = sendCommand(drv, WRITE_BLOCK, sector+sec, 0);
|
|
else
|
|
res = sendCommand(drv, WRITE_BLOCK, (sector+sec)<<9, 0);
|
|
|
|
if (res != 0) {
|
|
uart_putc('C');
|
|
SPI_SS_HIGH(drv);
|
|
disk_state = DISK_ERROR;
|
|
return RES_ERROR;
|
|
}
|
|
|
|
// Send data token
|
|
spiTransferByte(0xFE);
|
|
|
|
uint16_t i;
|
|
const BYTE *oldbuffer = buffer;
|
|
|
|
// Send data
|
|
crc = 0;
|
|
for (i=0; i<512; i++) {
|
|
#ifdef CONFIG_SD_DATACRC
|
|
crc = _crc_xmodem_update(crc, *buffer);
|
|
#endif
|
|
spiTransferByte(*(buffer++));
|
|
}
|
|
|
|
// Send CRC
|
|
spiTransferByte(crc >> 8);
|
|
spiTransferByte(crc & 0xff);
|
|
|
|
// Get and check status feedback
|
|
status = spiTransferByte(0xFF);
|
|
|
|
// Retry if neccessary
|
|
if ((status & 0x0F) != 0x05) {
|
|
uart_putc('X');
|
|
deselectCard(drv);
|
|
errorcount++;
|
|
buffer = oldbuffer;
|
|
continue;
|
|
}
|
|
|
|
// Wait for write finish
|
|
if (!sdWaitWriteFinish()) {
|
|
uart_putc('W');
|
|
SPI_SS_HIGH(drv);
|
|
disk_state = DISK_ERROR;
|
|
return RES_ERROR;
|
|
}
|
|
break;
|
|
}
|
|
deselectCard(drv);
|
|
|
|
if (errorcount >= CONFIG_SD_AUTO_RETRIES) {
|
|
if (!(status & STATUS_CRC_ERROR))
|
|
disk_state = DISK_ERROR;
|
|
return RES_ERROR;
|
|
}
|
|
}
|
|
|
|
return RES_OK;
|
|
}
|
|
DRESULT disk_write(BYTE drv, const BYTE *buffer, DWORD sector, BYTE count) __attribute__ ((weak, alias("sd_write")));
|
|
|
|
DRESULT sd_getinfo(BYTE drv, BYTE page, void *buffer) {
|
|
uint8_t i;
|
|
uint8_t buf[18];
|
|
uint32_t capacity;
|
|
|
|
if (drv >= MAX_CARDS)
|
|
return RES_NOTRDY;
|
|
|
|
if (sd_status(drv) & STA_NODISK)
|
|
return RES_NOTRDY;
|
|
|
|
if (page != 0)
|
|
return RES_ERROR;
|
|
|
|
/* Try to calculate the total number of sectors on the card */
|
|
/* FIXME: Write a generic data read function and merge with sd_read */
|
|
if (sendCommand(drv, SEND_CSD, 0, 0) != 0) {
|
|
deselectCard(drv);
|
|
return RES_ERROR;
|
|
}
|
|
|
|
/* Wait for data token */
|
|
if (!sdResponse(0xfe)) {
|
|
deselectCard(drv);
|
|
return RES_ERROR;
|
|
}
|
|
|
|
for (i=0;i<18;i++) {
|
|
buf[i] = spiTransferByte(0xff);
|
|
}
|
|
deselectCard(drv);
|
|
|
|
if (cardtype[drv] & CARD_SDHC) {
|
|
/* Special CSD for SDHC cards */
|
|
capacity = (1 + getbits(buf,127-69,22)) * 1024;
|
|
} else {
|
|
/* Assume that MMC-CSD 1.0/1.1/1.2 and SD-CSD 1.1 are the same... */
|
|
uint8_t exponent = 2 + getbits(buf, 127-49, 3);
|
|
capacity = 1 + getbits(buf, 127-73, 12);
|
|
exponent += getbits(buf, 127-83,4) - 9;
|
|
while (exponent--) capacity *= 2;
|
|
}
|
|
|
|
diskinfo0_t *di = buffer;
|
|
di->validbytes = sizeof(diskinfo0_t);
|
|
di->disktype = DISK_TYPE_SD;
|
|
di->sectorsize = 2;
|
|
di->sectorcount = capacity;
|
|
|
|
return RES_OK;
|
|
}
|
|
DRESULT disk_getinfo(BYTE drv, BYTE page, void *buffer) __attribute__ ((weak, alias("sd_getinfo")));
|