fusee: add support for SDMMC write operations

This commit is contained in:
Kate J. Temkin 2018-05-04 17:14:12 -06:00
parent 553cd236f2
commit 2e362d93da
2 changed files with 102 additions and 1 deletions

View file

@ -241,6 +241,8 @@ enum sdmmc_command {
CMD_SET_BLKLEN = 16, CMD_SET_BLKLEN = 16,
CMD_READ_SINGLE_BLOCK = 17, CMD_READ_SINGLE_BLOCK = 17,
CMD_READ_MULTIPLE_BLOCK = 18, CMD_READ_MULTIPLE_BLOCK = 18,
CMD_WRITE_SINGLE_BLOCK = 24,
CMD_WRITE_MULTIPLE_BLOCK = 25,
CMD_APP_SEND_OP_COND = 41, CMD_APP_SEND_OP_COND = 41,
CMD_APP_COMMAND = 55, CMD_APP_COMMAND = 55,
@ -473,6 +475,10 @@ static const char *sdmmc_get_command_string(enum sdmmc_command command)
return "CMD_APP_COMMAND"; return "CMD_APP_COMMAND";
case CMD_APP_SEND_OP_COND: case CMD_APP_SEND_OP_COND:
return "CMD_APP_SEND_OP_COND"; return "CMD_APP_SEND_OP_COND";
case CMD_WRITE_SINGLE_BLOCK:
return "CMD_WRITE_SINGLE_BLOCK";
case CMD_WRITE_MULTIPLE_BLOCK:
return "CMD_WRITE_MULTIPLE_BLOCK";
// For commands with low numbers, read them string from the relevant array. // For commands with low numbers, read them string from the relevant array.
default: default:
@ -2242,6 +2248,9 @@ int sdmmc_init(struct mmc *mmc, enum sdmmc_controller controller)
// Use DMA, by default. // Use DMA, by default.
mmc->use_dma = true; mmc->use_dma = true;
// Don't allow writing unless the caller explicitly enables it.
mmc->write_enable = SDMMC_WRITE_DISABLED;
// Default to relative address of zero. // Default to relative address of zero.
mmc->relative_address = 0; mmc->relative_address = 0;
@ -2297,7 +2306,7 @@ int sdmmc_select_partition(struct mmc *mmc, enum sdmmc_partition partition)
/** /**
* Reads a sector or sectors from a given SD card. * Reads a sector or sectors from a given SD/MMC card.
* *
* @param mmc The MMC device to work with. * @param mmc The MMC device to work with.
* @param buffer The output buffer to target. * @param buffer The output buffer to target.
@ -2323,6 +2332,62 @@ int sdmmc_read(struct mmc *mmc, void *buffer, uint32_t block, unsigned int count
return sdmmc_send_command(mmc, command, MMC_RESPONSE_LEN48, MMC_CHECKS_ALL, extent, NULL, count, false, count > 1, buffer); return sdmmc_send_command(mmc, command, MMC_RESPONSE_LEN48, MMC_CHECKS_ALL, extent, NULL, count, false, count > 1, buffer);
} }
/**
* Releases the SDMMC write lockout, enabling access to the card.
* Note that by default, setting this to WRITE_ENABLED will not allow access to eMMC.
* Check the source for a third constant that can be used to enable eMMC writes.
*
* @param perms The permissions to apply-- typically WRITE_DISABLED or WRITE_ENABLED.
*/
void sdmmc_set_write_enable(struct mmc *mmc, enum sdmmc_write_permission perms)
{
mmc->write_enable = perms;
}
/**
* Writes a sector or sectors to a given SD/MMC card.
*
* @param mmc The MMC device to work with.
* @param buffer The input buffer to write.
* @param block The sector number to write from.
* @param count The number of sectors to write.
*
* @return 0 on success, or an error code on failure.
*/
int sdmmc_write(struct mmc *mmc, void *buffer, uint32_t block, unsigned int count)
{
// Sanity check variables: we're especially careful about allowing writes to the switch eMMC.
bool is_emmc = (mmc->controller == SWITCH_EMMC);
bool allow_mmc_write = (mmc->write_enable == SDMMC_WRITE_ENABLED_INCLUDING_EMMC);
uint32_t command = (count == 1) ? CMD_WRITE_SINGLE_BLOCK : CMD_WRITE_MULTIPLE_BLOCK;
// Determine the argument, which indicates which address we're reading/writing.
uint32_t extent = block;
// If we don't have an explict write enable, don't allow writes.
if (mmc->write_enable == SDMMC_WRITE_DISABLED) {
mmc_print(mmc, "tried to write to an external card, but write was not enabled!");
return EACCES;
}
// Explicitly protect the switch's eMMC to prevent bricks.
if (is_emmc && !allow_mmc_write) {
mmc_print(mmc, "cowardly refusing to write to the switch's eMMMC");
return EACCES;
}
// If this card uses byte addressing rather than sector addressing,
// multiply by the block size.
if (!mmc->uses_block_addressing) {
extent *= sdmmc_get_block_size(mmc, false);
}
// Execute the relevant read.
return sdmmc_send_command(mmc, command, MMC_RESPONSE_LEN48, MMC_CHECKS_ALL, extent, NULL, count, true, count > 1, buffer);
}
/** /**
* Checks to see whether an SD card is present. * Checks to see whether an SD card is present.

View file

@ -71,6 +71,18 @@ enum sdmmc_controller {
SWITCH_EMMC = 3 SWITCH_EMMC = 3
}; };
/**
* Write permission modes for SD cards.
*/
enum sdmmc_write_permission {
SDMMC_WRITE_DISABLED,
SDMMC_WRITE_ENABLED,
/* use this with the utmost caution so you don't wind up with a brick */
SDMMC_WRITE_ENABLED_INCLUDING_EMMC,
};
/** /**
* Primary data structure describing a Fusée MMC driver. * Primary data structure describing a Fusée MMC driver.
@ -83,6 +95,7 @@ struct mmc {
unsigned int timeout; unsigned int timeout;
enum sdmmc_card_type card_type; enum sdmmc_card_type card_type;
bool use_dma; bool use_dma;
enum sdmmc_write_permission write_enable;
/* Card properties */ /* Card properties */
uint8_t cid[15]; uint8_t cid[15];
@ -164,6 +177,29 @@ int sdmmc_select_partition(struct mmc *mmc, enum sdmmc_partition partition);
int sdmmc_read(struct mmc *mmc, void *buffer, uint32_t sector, unsigned int count); int sdmmc_read(struct mmc *mmc, void *buffer, uint32_t sector, unsigned int count);
/**
* Releases the SDMMC write lockout, enabling access to the card.
* Note that by default, setting this to WRITE_ENABLED will not allow access to eMMC.
* Check the source for a third constant that can be used to enable eMMC writes.
*
* @param perms The permissions to apply-- typically WRITE_DISABLED or WRITE_ENABLED.
*/
void sdmmc_set_write_enable(struct mmc *mmc, enum sdmmc_write_permission perms);
/**
* Writes a sector or sectors to a given SD/MMC card.
*
* @param mmc The MMC device to work with.
* @param buffer The input buffer to write.
* @param block The sector number to write from.
* @param count The number of sectors to write.
*
* @return 0 on success, or an error code on failure.
*/
int sdmmc_write(struct mmc *mmc, void *buffer, uint32_t block, unsigned int count);
/** /**
* Checks to see whether an SD card is present. * Checks to see whether an SD card is present.
* *