/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2021 Aidan MacDonald * * 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; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "storage.h" #include "sdmmc.h" #include "sd.h" #include "msc-x1000.h" #include /* #define LOGF_ENABLE */ #include "logf.h" static msc_drv* sd_to_msc[MSC_COUNT]; static long _sd_last_disk_activity = 0; static int sd_init_card(msc_drv* d) { int s; if(s = msc_cmd_go_idle_state(d)) return -100 - s; if(s = msc_cmd_send_if_cond(d)) return -110 - s; if(s = msc_cmd_app_op_cond(d)) return -120 - s; if(s = msc_cmd_all_send_cid(d)) return -130 - s; if(s = msc_cmd_send_rca(d)) return -140 - s; if(s = msc_cmd_send_csd(d)) return -150 - s; if(s = msc_cmd_select_card(d)) return -160 - s; if(s = msc_cmd_set_clr_card_detect(d, 0)) return -170 - s; if(s = msc_cmd_set_bus_width(d, 4)) return -180 - s; if(s = msc_cmd_switch_freq(d)) return -190 - s; d->driver_flags |= MSC_DF_READY; d->cardinfo.initialized = 1; return 0; } static int sd_transfer(msc_drv* d, bool write, unsigned long start, int count, void* buf) { int status = -1; msc_lock(d); if(!d->card_present) goto _exit; /* Hopefully puts the driver into a working state */ if(d->driver_flags & MSC_DF_ERRSTATE) { logf("MSC%d: attempting to reset after ERRSTATE", d->msc_nr); msc_full_reset(d); } /* Init card if needed */ if((d->driver_flags & MSC_DF_READY) == 0) { if(status = sd_init_card(d)) { logf("MSC%d: card init failed (code %d)", d->msc_nr, status); d->driver_flags |= MSC_DF_ERRSTATE; d->cardinfo.initialized = status; goto _exit; } } /* Ensure parameters are within range */ if(count < 1) goto _exit; if(start + count > d->cardinfo.numblocks) goto _exit; do { /* We can only do 65536 blocks at a time */ int xfer_count = count > 0xffff ? 0xffff : count; /* Set block length. I think this is only necessary for non-HCS cards. * HCS cards always use 512 bytes so we shouldn't need it. */ if((d->driver_flags & MSC_DF_HCS_CARD) == 0) if(status = msc_cmd_set_block_len(d, SD_BLOCK_SIZE)) goto _exit; /* TODO - look into using CMD23 to improve transfer performance. * This specifies the number of blocks ahead of time, instead of * relying on CMD12 to stop transmission. CMD12 is still needed * in the event of errors though. */ msc_req req = {0}; req.data = buf; req.nr_blocks = xfer_count; req.block_len = SD_BLOCK_SIZE; req.resptype = MSC_RESP_R1; req.flags = MSC_RF_DATA; if(xfer_count > 1) req.flags |= MSC_RF_AUTO_CMD12; if(write) { req.command = xfer_count == 1 ? SD_WRITE_BLOCK : SD_WRITE_MULTIPLE_BLOCK; req.flags |= MSC_RF_PROG | MSC_RF_WRITE; } else { req.command = xfer_count == 1 ? SD_READ_SINGLE_BLOCK : SD_READ_MULTIPLE_BLOCK; } if(d->driver_flags & MSC_DF_V2_CARD) req.argument = start; else req.argument = start * SD_BLOCK_SIZE; if(status = msc_cmd_exec(d, &req)) goto _exit; /* TODO - properly handle reading the last block of the SD card * This is likely to fail if we're reading near the end because * the SD card will try to read past the last sector and then * signal an error. So we need to ignore that error, but only if * it was expected to occur. (See SD spec sec. 4.3.3, "Block Read") */ if(status = msc_cmd_send_status(d)) goto _exit; /* Advance the buffer and adjust start/count */ buf += xfer_count * SD_BLOCK_SIZE; start += xfer_count; count -= xfer_count; } while(count > 0); _exit: msc_unlock(d); return status; } int sd_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* buf) { return sd_transfer(sd_to_msc[IF_MD_DRV(drive)], false, start, count, buf); } int sd_write_sectors(IF_MD(int drive,) unsigned long start, int count, const void* buf) { return sd_transfer(sd_to_msc[IF_MD_DRV(drive)], true, start, count, (void*)buf); } tCardInfo* card_get_info_target(int card_nr) { /* Defensive measures */ if(card_nr < 0 || card_nr >= MSC_COUNT || sd_to_msc[card_nr] == NULL) { static tCardInfo null_info = { 0 }; return &null_info; } return &sd_to_msc[card_nr]->cardinfo; } int sd_event(long id, intptr_t data) { if(id == SYS_HOTSWAP_EXTRACTED) { msc_drv* d = msc_get_by_drive(data); if(d) msc_full_reset(d); return 0; } else { return storage_event_default_handler(id, data, _sd_last_disk_activity, STORAGE_SD); } } long sd_last_disk_activity(void) { return _sd_last_disk_activity; } bool sd_present(IF_MD_NONVOID(int drive)) { /* Seems that volume_properties() in firmware/common/disk.c may pass * drive = -1 when the SD card is not inserted, so just return false. */ if(drive < 0) return false; return sd_to_msc[IF_MD_DRV(drive)]->card_present; } bool sd_removable(IF_MD_NONVOID(int drive)) { /* Same reason as sd_present() */ if(drive < 0) return false; return sd_to_msc[IF_MD_DRV(drive)]->config->cd_gpio.pin != 0; } #ifndef CONFIG_STORAGE_MULTI static #endif int sd_num_drives(int first_drive) { int n = 0; for(; n < MSC_COUNT; ++n) { msc_drv* d = msc_get(MSC_TYPE_SD, n); if(d == NULL) break; d->drive_nr = first_drive + n; sd_to_msc[n] = d; } for(int i = n; i < MSC_COUNT; ++i) sd_to_msc[i] = NULL; return n; } int sd_init(void) { msc_init(); #ifndef CONFIG_STORAGE_MULTI sd_num_drives(0); #endif return 0; }