You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

263 lines
6.1 KiB

/* vim: set ts=4 sw=4 fenc=cp936 et fdm=marker: */
/*
* mmc driver
*
* ChangeLog:
* 2022/03/29: surenyi926 add
*
* $Revision$
*/
#include <stddef.h>
#include <string.h>
#include "mmcDevice.h"
#include "mmcPlatform.h"
#include "mmc.h"
static int __get_capacity(struct mmc_device *mmc, void *arg)
{
uint64_t *cap = arg;
if (cap) {
*cap += mmc->capacity;
}
return 0;
}
size_t mmcGetCapacity()
{
uint64_t cap = 0;
mmcForEachDevice(__get_capacity, &cap);
return (cap);
}
static int __devices_start(struct mmc_device *mmc, void *arg)
{
int *ndev = arg;
if (mmcDeviceStart(mmc) == 0) {
*ndev = *ndev + 1;
}
return 0;
}
int mmcInit()
{
int ndevs = 0;
mmcPlatformInit();
mmcForEachDevice(__devices_start, &ndevs);
return ndevs > 0 ? 0 : -1;
}
void mmcClose()
{
mmcDeviceRemoveAll();
mmcPlatformClose();
}
static inline int mmc_read_blocks(struct mmc_device *mmc, size_t start, size_t nblocks, void *buffer)
{
struct mmc_cmd cmd;
struct mmc_data data;
if (mmc->driver && mmc->driver->read_blocks)
return mmc->driver->read_blocks(mmc, start, nblocks, buffer);
switch (nblocks) {
case 0:
return 0;
case 1:
cmd.cmdidx = 17; /* CMD17: read single block */
break;
default:
cmd.cmdidx = 18; /* CMD18: read multiple blocks */
break;
}
if ((mmc->ocr & OCR_HCS) == OCR_HCS) {
cmd.cmdarg = start;
} else {
cmd.cmdarg = start * mmc->read_bl_len;
}
cmd.resp_type = MMC_RSP_R1;
data.dest = buffer;
data.blocks = nblocks;
data.blocksize = mmc->read_bl_len;
data.flags = MMC_DATA_READ;
if (mmc_send_command(mmc, &cmd, &data)) {
mmcPrintf("%s: read off %d, blocks %d failed\r\n", mmc_device_name(mmc), start, nblocks);
return -1;
}
if (nblocks > 1) {
cmd.cmdidx = 12; /* CMD12: stop transmission */
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_R1b;
if (mmc_send_command(mmc, &cmd, NULL)) {
return -2;
}
}
return 0;
}
static inline int mmc_write_blocks(struct mmc_device *mmc, size_t start, size_t nblocks, void *buffer)
{
struct mmc_cmd cmd;
struct mmc_data data;
if (mmc->driver && mmc->driver->write_blocks)
return mmc->driver->write_blocks(mmc, start, nblocks, buffer);
switch (nblocks) {
case 0:
return 0;
case 1:
cmd.cmdidx = 24; /* CMD24: write single block */
break;
default:
cmd.cmdidx = 25; /* CMD25: write multiple blocks */
break;
}
if ((mmc->ocr & OCR_HCS) == OCR_HCS) {
cmd.cmdarg = start;
} else {
cmd.cmdarg = start * mmc->write_bl_len;
}
cmd.resp_type = MMC_RSP_R1;
data.src = buffer;
data.blocks = nblocks;
data.blocksize = mmc->write_bl_len;
data.flags = MMC_DATA_WRITE;
if (mmc_send_command(mmc, &cmd, &data)) {
mmcPrintf("%s: write off %d, blocks %d failed\r\n", mmc_device_name(mmc), start, nblocks);
return -2;
}
if (nblocks > 1) {
cmd.cmdidx = 12; /* CMD12: stop transmission */
cmd.cmdarg = 0;
cmd.resp_type = MMC_RSP_R1b;
mmc_send_command(mmc, &cmd, NULL);
}
if (mmcSendStatus(mmc, 1000)) {
mmcPrintf("%s: status failed\r\n", mmc_device_name(mmc));
return -3;
}
return 0;
}
int mmcReadBlocks(size_t off, size_t blocks, void *buffer)
{
return mmcBlocksXfer(off, blocks, buffer, mmc_read_blocks);
}
int mmcWriteBlocks(size_t off, size_t blocks, void *buffer)
{
return mmcBlocksXfer(off, blocks, buffer, mmc_write_blocks);
}
int mmcRead(uint64_t off, void *buffer, size_t len)
{
uint8_t block[MMC_BLKSIZE], *bp = buffer;
uint64_t rem = off % MMC_BLKSIZE;
size_t blkoff;
int n, reqsz = len;
blkoff = off / MMC_BLKSIZE;
if (rem > 0) {
if (mmcReadBlocks(blkoff, 1, block) != 1) {
return -1;
}
n = MMC_BLKSIZE - rem;
if (n > len)
n = len;
memcpy(bp, block + rem, n);
bp += n;
len -= n;
++blkoff;
}
if (len >= MMC_BLKSIZE) {
size_t blknum = len / MMC_BLKSIZE;
n = mmcReadBlocks(blkoff, blknum, bp);
if (n < 0) {
return reqsz - len;
}
rem = n * MMC_BLKSIZE;
len -= rem;
bp += rem;
if (n < blknum) {
goto end;
}
blkoff += n;
}
if (len > 0) {
if (mmcReadBlocks(blkoff, 1, block) != 1) {
goto end;
}
memcpy(bp, block, len);
len = 0;
}
end:
return reqsz - len;
}
int mmcWrite(uint64_t off, void *buffer, size_t len)
{
uint8_t block[MMC_BLKSIZE], *bp = buffer;
uint64_t rem = off % MMC_BLKSIZE;
size_t blkoff;
int n, reqsz = len;
blkoff = off / MMC_BLKSIZE;
if (rem > 0) {
if (mmcReadBlocks(blkoff, 1, block) != 1) {
return -1;
}
n = MMC_BLKSIZE - rem;
if (n > len)
n = len;
memcpy(block + rem, bp, n);
if (mmcWriteBlocks(blkoff, 1, block) != 1) {
return -2;
}
bp += n;
len -= n;
++blkoff;
}
if (len >= MMC_BLKSIZE) {
size_t blknum = len / MMC_BLKSIZE;
n = mmcWriteBlocks(blkoff, blknum, bp);
if (n < 0) {
return reqsz - len;
}
rem = n * MMC_BLKSIZE;
len -= rem;
bp += rem;
if (n < blknum) {
goto end;
}
blkoff += n;
}
if (len > 0) {
if (mmcReadBlocks(blkoff, 1, block) != 1) {
goto end;
}
memcpy(block, bp, len);
if (mmcWriteBlocks(blkoff, 1, block) != 1) {
goto end;
}
len = 0;
}
end:
return reqsz - len;
}