mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-03 23:09:16 +00:00
fusee: add support for SDMMC write operations
This commit is contained in:
parent
553cd236f2
commit
2e362d93da
2 changed files with 102 additions and 1 deletions
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in a new issue