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
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;
|
|
}
|
|
|
|
|
|
|