From b883250b42adfbfab013f265fa5073a3a3455b8b Mon Sep 17 00:00:00 2001 From: batt Date: Tue, 6 Jul 2010 15:59:49 +0000 Subject: [PATCH] Update SD driver in order to use the new KBlock API. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@4001 38d2e660-2303-0410-9eaa-f027e97ec537 --- bertos/cfg/cfg_sd.h | 75 ++++++++++ bertos/drv/sd.c | 332 +++++++++++++++++++++++--------------------- bertos/drv/sd.h | 97 ++++++------- 3 files changed, 292 insertions(+), 212 deletions(-) create mode 100644 bertos/cfg/cfg_sd.h diff --git a/bertos/cfg/cfg_sd.h b/bertos/cfg/cfg_sd.h new file mode 100644 index 00000000..8c40f827 --- /dev/null +++ b/bertos/cfg/cfg_sd.h @@ -0,0 +1,75 @@ +/** + * \file + * + * + * \brief Configuration file Secure Digital module. + * + * + * \author Francesco Sacchi + */ + +#ifndef CFG_SD_H +#define CFG_SD_H + +/** + * Module logging level. + * + * $WIZ$ type = "enum" + * $WIZ$ value_list = "log_level" + */ +#define SD_LOG_LEVEL LOG_LVL_ERR + +/** + * Module logging format. + * + * $WIZ$ type = "enum" + * $WIZ$ value_list = "log_format" + */ +#define SD_LOG_FORMAT LOG_FMT_VERBOSE + + +/** + * Enable autoassignment of SD driver to disk drive number 0 of FatFs module. + * $WIZ$ type = "boolean" + * $WIZ$ conditional_deps = "fat" + */ +#define CONFIG_SD_AUTOASSIGN_FAT 1 + +/** + * Enable backward compatibility for sd_init(). + * If enabled, sd_init() will allocate internally an Sd context, + * otherwise sd_init() will need the context to be passed explicitly. + * + * $WIZ$ type = "boolean" + */ +#define CONFIG_SD_OLD_INIT 1 + +#endif /* CFG_SD_H */ diff --git a/bertos/drv/sd.c b/bertos/drv/sd.c index 364c8d48..ca48fb3b 100644 --- a/bertos/drv/sd.c +++ b/bertos/drv/sd.c @@ -38,10 +38,15 @@ #include "sd.h" #include "hw/hw_sd.h" #include +#include #include #include +#include "cfg/cfg_sd.h" + +#define LOG_LEVEL SD_LOG_LEVEL +#define LOG_FORMAT SD_LOG_FORMAT #include #include @@ -65,20 +70,12 @@ typedef struct CardCSD #define SD_DEFAULT_BLOCKLEN 512 -/** - * SPI communication channel. - */ -static KFile *fd; - -/** - * Current SD status. - */ -static bool sd_status; - #define SD_BUSY_TIMEOUT ms_to_ticks(200) -static bool sd_select(bool state) +static bool sd_select(Sd *sd, bool state) { + KFile *fd = sd->ch; + if (state) { ticks_t start = timer_clock(); @@ -106,13 +103,13 @@ static bool sd_select(bool state) } } -static int16_t sd_waitR1(void) +static int16_t sd_waitR1(Sd *sd) { uint8_t datain; for (int i = 0; i < TIMEOUT_NAC; i++) { - datain = kfile_getc(fd); + datain = kfile_getc(sd->ch); if (datain != 0xff) return (int16_t)datain; } @@ -120,8 +117,9 @@ static int16_t sd_waitR1(void) return EOF; } -static int16_t sd_sendCommand(uint8_t cmd, uint32_t param, uint8_t crc) +static int16_t sd_sendCommand(Sd *sd, uint8_t cmd, uint32_t param, uint8_t crc) { + KFile *fd = sd->ch; /* The 7th bit of command must be a 1 */ kfile_putc(cmd | 0x40, fd); @@ -133,15 +131,17 @@ static int16_t sd_sendCommand(uint8_t cmd, uint32_t param, uint8_t crc) kfile_putc(crc, fd); - return sd_waitR1(); + return sd_waitR1(sd); } -static bool sd_getBlock(void *buf, size_t len) +static bool sd_getBlock(Sd *sd, void *buf, size_t len) { uint8_t token; uint16_t crc; + KFile *fd = sd->ch; + for (int i = 0; i < TIMEOUT_NAC; i++) { token = kfile_getc(fd); @@ -171,10 +171,10 @@ static bool sd_getBlock(void *buf, size_t len) return false; } -#define SD_SELECT() \ +#define SD_SELECT(sd) \ do \ { \ - if (!sd_select(true)) \ + if (!sd_select((sd), true)) \ { \ LOG_ERR("%s failed, card busy\n", __func__); \ return EOF; \ @@ -184,37 +184,44 @@ while (0) #define SD_SETBLOCKLEN 0x50 -static int16_t sd_setBlockLen(uint32_t newlen) +static int16_t sd_setBlockLen(Sd *sd, uint32_t newlen) { - SD_SELECT(); + SD_SELECT(sd); - int16_t r1 = sd_sendCommand(SD_SETBLOCKLEN, newlen, 0); + sd->r1 = sd_sendCommand(sd, SD_SETBLOCKLEN, newlen, 0); - sd_select(false); - return r1; + sd_select(sd, false); + return sd->r1; } #define SD_SEND_CSD 0x49 -static int16_t sd_getCSD(CardCSD *csd) +static int16_t sd_getCSD(Sd *sd, CardCSD *csd) { - SD_SELECT(); + SD_SELECT(sd); - int16_t r1 = sd_sendCommand(SD_SEND_CSD, 0, 0); + sd->r1 = sd_sendCommand(sd, SD_SEND_CSD, 0, 0); - if (r1) + if (sd->r1) { - LOG_ERR("send_csd failed: %04X\n", r1); - sd_select(false); - return r1; + LOG_ERR("send_csd failed: %04X\n", sd->r1); + sd_select(sd, false); + return sd->r1; } uint8_t buf[16]; - bool res = sd_getBlock(buf, sizeof(buf)); - sd_select(false); + bool res = sd_getBlock(sd, buf, sizeof(buf)); + sd_select(sd, false); if (res) { + #if LOG_LEVEL >= LOG_LVL_INFO + LOG_INFO("CSD: ["); + for (int i = 0; i < 16; i++) + kprintf("%02X ", buf[i]); + kprintf("]\n"); + #endif + uint16_t mult = (1L << ((((buf[9] & 0x03) << 1) | ((buf[10] & 0x80) >> 7)) + 2)); uint16_t c_size = (((uint16_t)(buf[6] & 0x03)) << 10) | (((uint16_t)buf[7]) << 2) | (((uint16_t)(buf[8] & 0xC0)) >> 6); @@ -233,44 +240,62 @@ static int16_t sd_getCSD(CardCSD *csd) #define SD_READ_SINGLEBLOCK 0x51 -static int16_t sd_readBlock(void *buf, uint32_t addr) +static size_t sd_readDirect(struct KBlock *b, block_idx_t idx, void *buf, size_t offset, size_t size) { - SD_SELECT(); + Sd *sd = SD_CAST(b); + LOG_INFO("reading from block %ld, offset %d, size %d\n", idx, offset, size); - int16_t r1 = sd_sendCommand(SD_READ_SINGLEBLOCK, addr, 0); + if ((sd->r1 = sd_setBlockLen(sd, size))) + { + LOG_ERR("setBlockLen failed: %04X\n", sd->r1); + return 0; + } + + SD_SELECT(sd); + sd->r1 = sd_sendCommand(sd, SD_READ_SINGLEBLOCK, idx * SD_DEFAULT_BLOCKLEN + offset, 0); - if (r1) + if (sd->r1) { - LOG_ERR("read single block failed: %04X\n", r1); - sd_select(false); - return r1; + LOG_ERR("read single block failed: %04X\n", sd->r1); + sd_select(sd, false); + return 0; } - bool res = sd_getBlock(buf, SD_DEFAULT_BLOCKLEN); - sd_select(false); + bool res = sd_getBlock(sd, buf, size); + sd_select(sd, false); if (!res) { LOG_ERR("read single block failed reading data\n"); - return EOF; + return 0; } else - return 0; + return size; } #define SD_WRITE_SINGLEBLOCK 0x58 #define SD_DATA_ACCEPTED 0x05 -static int16_t sd_writeBlock(const void *buf, uint32_t addr) +static int sd_writeBlock(KBlock *b, block_idx_t idx, const void *buf) { - SD_SELECT(); + Sd *sd = SD_CAST(b); + KFile *fd = sd->ch; + + LOG_INFO("writing block %ld\n", idx); + if ((sd->r1 = sd_setBlockLen(sd, SD_DEFAULT_BLOCKLEN))) + { + LOG_ERR("setBlockLen failed: %04X\n", sd->r1); + return sd->r1; + } - int16_t r1 = sd_sendCommand(SD_WRITE_SINGLEBLOCK, addr, 0); + SD_SELECT(sd); - if (r1) + sd->r1 = sd_sendCommand(sd, SD_WRITE_SINGLEBLOCK, idx * SD_DEFAULT_BLOCKLEN, 0); + + if (sd->r1) { - LOG_ERR("write single block failed: %04X\n", r1); - sd_select(false); - return r1; + LOG_ERR("write single block failed: %04X\n", sd->r1); + sd_select(sd, false); + return sd->r1; } kfile_putc(SD_STARTTOKEN, fd); @@ -279,7 +304,7 @@ static int16_t sd_writeBlock(const void *buf, uint32_t addr) kfile_putc(0, fd); kfile_putc(0, fd); uint8_t dataresp = (kfile_getc(fd) & 0x1F); - sd_select(false); + sd_select(sd, false); // FIXME: sometimes dataresp is 0, find out why. if (dataresp != SD_DATA_ACCEPTED) @@ -292,14 +317,11 @@ static int16_t sd_writeBlock(const void *buf, uint32_t addr) } -bool sd_test(void) +bool sd_test(Sd *sd) { - CardCSD csd; - sd_getCSD(&csd); - uint8_t buf[SD_DEFAULT_BLOCKLEN]; - if (sd_readBlock(buf, 0) != 0) + if (sd_readDirect(&sd->b, 0, buf, 0, sd->b.blk_size) != sd->b.blk_size) return false; kputchar('\n'); @@ -311,15 +333,15 @@ bool sd_test(void) kputchar('\n'); } - if (sd_writeBlock(buf, 0) != 0) + if (sd_writeBlock(&sd->b, 0, buf) != 0) return false; memset(buf, 0, sizeof(buf)); - if (sd_readBlock(buf, 0) != 0) + if (sd_readDirect(&sd->b, 0, buf, 0, sd->b.blk_size) != sd->b.blk_size) return false; kputchar('\n'); - for (int i = 0; i < SD_DEFAULT_BLOCKLEN; i++) + for (block_idx_t i = 0; i < sd->b.blk_size; i++) { kprintf("%02X ", buf[i]); buf[i] = i; @@ -330,6 +352,42 @@ bool sd_test(void) return true; } +static int sd_error(KBlock *b) +{ + Sd *sd = SD_CAST(b); + return sd->r1; +} + +static int sd_clearerr(KBlock *b) +{ + Sd *sd = SD_CAST(b); + sd->r1 = 0; + return 0; +} + +static const KBlockVTable sd_unbuffered_vt = +{ + .readDirect = sd_readDirect, + .writeBlock = sd_writeBlock, + + .error = sd_error, + .clearerr = sd_clearerr, +}; + +static const KBlockVTable sd_buffered_vt = +{ + .readDirect = sd_readDirect, + .writeBlock = sd_writeBlock, + + .readBuf = kblock_swReadBuf, + .writeBuf = kblock_swWriteBuf, + .load = kblock_swLoad, + .store = kblock_swStore, + + .error = sd_error, + .clearerr = sd_clearerr, +}; + #define SD_GO_IDLE_STATE 0x40 #define SD_GO_IDLE_STATE_CRC 0x95 #define SD_SEND_OP_COND 0x41 @@ -339,12 +397,13 @@ bool sd_test(void) #define SD_INIT_TIMEOUT ms_to_ticks(1000) #define SD_IDLE_RETRIES 4 -bool sd_init(KFile *_fd) +static bool sd_blockInit(Sd *sd, KFile *ch) { - uint16_t r1; - - ASSERT(_fd); - fd = _fd; + ASSERT(sd); + ASSERT(ch); + memset(sd, 0, sizeof(*sd)); + DB(sd->b.priv.type = KBT_SD); + sd->ch = ch; SD_CS_INIT(); SD_CS_OFF(); @@ -354,22 +413,22 @@ bool sd_init(KFile *_fd) /* Give 80 clk pulses to wake up the card */ for (int i = 0; i < 10; i++) - kfile_putc(0xff, fd); - kfile_flush(fd); + kfile_putc(0xff, ch); + kfile_flush(ch); for (int i = 0; i < SD_IDLE_RETRIES; i++) { - SD_SELECT(); - r1 = sd_sendCommand(SD_GO_IDLE_STATE, 0, SD_GO_IDLE_STATE_CRC); - sd_select(false); + SD_SELECT(sd); + sd->r1 = sd_sendCommand(sd, SD_GO_IDLE_STATE, 0, SD_GO_IDLE_STATE_CRC); + sd_select(sd, false); - if (r1 == SD_IN_IDLE) + if (sd->r1 == SD_IN_IDLE) break; } - if (r1 != SD_IN_IDLE) + if (sd->r1 != SD_IN_IDLE) { - LOG_ERR("go_idle_state failed: %04X\n", r1); + LOG_ERR("go_idle_state failed: %04X\n", sd->r1); return false; } @@ -378,120 +437,71 @@ bool sd_init(KFile *_fd) /* Wait for card to start */ do { - SD_SELECT(); - r1 = sd_sendCommand(SD_SEND_OP_COND, 0, SD_SEND_OP_COND_CRC); - sd_select(false); + SD_SELECT(sd); + sd->r1 = sd_sendCommand(sd, SD_SEND_OP_COND, 0, SD_SEND_OP_COND_CRC); + sd_select(sd, false); cpu_relax(); } - while (r1 != 0 && timer_clock() - start < SD_INIT_TIMEOUT); + while (sd->r1 != 0 && timer_clock() - start < SD_INIT_TIMEOUT); - if (r1) + if (sd->r1) { - LOG_ERR("send_op_cond failed: %04X\n", r1); + LOG_ERR("send_op_cond failed: %04X\n", sd->r1); return false; } - r1 = sd_setBlockLen(SD_DEFAULT_BLOCKLEN); + sd->r1 = sd_setBlockLen(sd, SD_DEFAULT_BLOCKLEN); - if (r1) + if (sd->r1) { - LOG_ERR("setBlockLen failed: %04X\n", r1); + LOG_ERR("setBlockLen failed: %04X\n", sd->r1); return false; } - sd_status = !r1; - return sd_status; -} - -DSTATUS sd_disk_initialize(BYTE drv) -{ - return sd_disk_status(drv); -} - -DSTATUS sd_disk_status(BYTE drv) -{ - ASSERT(!drv); + CardCSD csd; + sd->r1 = sd_getCSD(sd, &csd); + if (sd->r1) + { + LOG_ERR("getCSD failed: %04X\n", sd->r1); + return false; + } - if (sd_status) - return RES_OK; - else - return STA_NOINIT; -} + sd->b.blk_size = SD_DEFAULT_BLOCKLEN; + sd->b.blk_cnt = csd.block_num * (csd.block_len / SD_DEFAULT_BLOCKLEN); + LOG_INFO("blk_size %d, blk_cnt %ld\n", sd->b.blk_size, sd->b.blk_cnt); -DRESULT sd_disk_read(BYTE drv, BYTE* buf, DWORD sector, BYTE count) -{ - ASSERT(!drv); - if (!sd_status) - return RES_NOTRDY; +#if CONFIG_SD_AUTOASSIGN_FAT + disk_assignDrive(&sd->b, 0); +#endif - while (count--) - { - if (sd_readBlock(buf, sector * SD_DEFAULT_BLOCKLEN)) - return RES_ERROR; - buf += SD_DEFAULT_BLOCKLEN; - sector++; - } - return RES_OK; + return true; } -DRESULT sd_disk_write(BYTE drv, const BYTE* buf, DWORD sector, BYTE count) +bool sd_initUnbuf(Sd *sd, KFile *ch) { - ASSERT(!drv); - - if (!sd_status) - return RES_NOTRDY; - - while (count--) + if (sd_blockInit(sd, ch)) { - if (sd_writeBlock(buf, sector * SD_DEFAULT_BLOCKLEN)) - return RES_ERROR; - buf += SD_DEFAULT_BLOCKLEN; - sector++; + sd->b.priv.vt = &sd_unbuffered_vt; + return true; } - return RES_OK; + else + return false; } -DRESULT sd_disk_ioctl(BYTE drv, BYTE cmd, void* buf) -{ - ASSERT(!drv); - - if (!sd_status) - return RES_NOTRDY; +static uint8_t sd_buf[SD_DEFAULT_BLOCKLEN]; - switch (cmd) +bool sd_initBuf(Sd *sd, KFile *ch) +{ + if (sd_blockInit(sd, ch)) { - case CTRL_SYNC: - return RES_OK; - - case GET_SECTOR_SIZE: - *(WORD *)buf = SD_DEFAULT_BLOCKLEN; - return RES_OK; - - case GET_SECTOR_COUNT: - { - CardCSD csd; - if (sd_getCSD(&csd)) - return RES_ERROR; - else - { - *(DWORD *)buf = csd.block_num; - return RES_OK; - } - - } - - case GET_BLOCK_SIZE: - *(DWORD *)buf = 1; - return RES_OK; - - default: - LOG_ERR("unknown command: [%d]\n", cmd); - return RES_PARERR; + sd->b.priv.buf = sd_buf; + sd->b.priv.flags |= KB_BUFFERED; + sd->b.priv.vt = &sd_buffered_vt; + sd->b.priv.vt->load(&sd->b, 0); + return true; } + else + return false; } -DWORD get_fattime(void) -{ - return 0; -} diff --git a/bertos/drv/sd.h b/bertos/drv/sd.h index 207dcfd2..85e7b11c 100644 --- a/bertos/drv/sd.h +++ b/bertos/drv/sd.h @@ -39,80 +39,75 @@ * $WIZ$ module_name = "sd" * $WIZ$ module_depends = "kfile", "timer" * $WIZ$ module_hw = "bertos/hw/hw_sd.h" + * $WIZ$ module_configuration = "bertos/cfg/cfg_sd.h" */ #ifndef DRV_SD_H #define DRV_SD_H -#include "cfg/cfg_fat.h" - #include +#include #include -bool sd_test(void); +#include "cfg/cfg_sd.h" -/** - * Initializes the SD driver. - * - * \param _fd A pointer to a kfile where the SD will read/write to. - * \return true if initialization succeds, false otherwise. - */ -bool sd_init(KFile *_fd); -#if CONFIG_FAT_DRIVES == 1 +typedef struct Sd +{ + KBlock b; + KFile *ch; ///< SPI communication channel + uint16_t r1; +} Sd; - /** - * Same as sd_disk_status. - * - * Card initialization must be done with sd_init. - */ - #define sd_disk_initialize disk_initialize +bool sd_initUnbuf(Sd *sd, KFile *ch); +bool sd_initBuf(Sd *sd, KFile *ch); + +#if CONFIG_SD_OLD_INIT + #warning "Deprecated: this API will be removed in the next major release," + #warning "please disable CONFIG_SD_OLD_INIT and pass explicitly the SD context to sd_init()." /** - * Return the status of the disk. + * Initializes the SD driver. * - * \param drv The number of the drive to initialize. Currently only drive 0 is allowed. - * \return RES_OK if the sd card was correctly initialized by a previous call to sd_init(), STA_NOINIT otherwise. - */ - #define sd_disk_status disk_status - /** - * Read \a count sectors from SD card. + * \param ch A pointer to a SPI channel where the SD will read/write to. * - * \param drv The drive number to read from. Only 0 is supported. - * \param buf A buffer to store read data. You can get sector size using sd_disk_ioctl. - * \param sector Start sector number. - * \param count The number of sectors to read. - * \return RES_OK if the function succeded, RES_ERROR if any error occurred, RES_NOTRDY if the disk is not initialized. + * \return true if initialization succeds, false otherwise. * - * \sa diskio.h + * \note This API is deprecated, disable CONFIG_SD_OLD_INIT and + * use the new one instead. */ - #define sd_disk_read disk_read - - #if CONFIG_FAT_FS_READONLY == 0 - - /** - * Write \a count sectors to SD card. - * - * \param drv The drive number to read from. Only 0 is supported. - * \param buf The data to be written. - * \param sector Start sector number. - * \param count The number of sectors to write. - * \return RES_OK if the function succeded, RES_ERROR if any error occurred, RES_NOTRDY if the disk is not initialized. - * - * \sa diskio.h - */ - #define sd_disk_write disk_write - #endif + #define sd_init(ch) {static struct Sd sd; sd_initUnbuf(&sd, (ch));} + +#else /** - * Interface to send device independant commands to the device. + * Initializes the SD driver. * - * \sa diskio.h and related documentation for further explanations. + * \param sd The SD KBlock context. + * \param ch A pointer to a SPI channel where the SD will read/write to. + * \param buffered Set to true if you want the KBlock to be buffered, + * to false otherwise. The FatFs module does not require the device + * to be buffered because it has an internal cache. This will save + * 512 bytes of RAM in this case. + * + * \return true if initialization succeds, false otherwise. */ - #define sd_disk_ioctl disk_ioctl + #define sd_init(sd, ch, buffered) ((buffered) ? sd_initBuf((sd), (ch)) : sd_initUnbuf((sd), (ch))) + +#endif + + +#define KBT_SD MAKE_ID('S', 'D', 'B', 'K') + +bool sd_test(Sd *sd); + +INLINE Sd *SD_CAST(KBlock *b) +{ + ASSERT(b->priv.type == KBT_SD); + return (Sd *)b; +} -#endif /* CONFIG_FAT_DRIVES == 1 */ #endif /* DRV_SD_H */ -- 2.25.1