123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773 |
- /* 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")));
|