mirror of
https://github.com/apache/nuttx.git
synced 2026-05-23 23:28:29 +08:00
drivers/mmcsd: Add MMC_IOC_CMD ioctl
Signed-off-by: helei8 <helei8@xiaomi.com>
This commit is contained in:
committed by
Alan Carvalho de Assis
parent
ff68d9ac04
commit
4b4004b874
@@ -27,6 +27,12 @@ menuconfig MMCSD
|
||||
|
||||
if MMCSD
|
||||
|
||||
config MMCSD_IOCSUPPORT
|
||||
int "Enable MMC/SD ioctl support"
|
||||
default y
|
||||
---help---
|
||||
Disable it to save some code size if required.
|
||||
|
||||
config MMCSD_NSLOTS
|
||||
int "Number of MMC/SD slots"
|
||||
default 1
|
||||
|
||||
@@ -221,6 +221,12 @@ static int mmcsd_probe(FAR struct mmcsd_state_s *priv);
|
||||
static int mmcsd_removed(FAR struct mmcsd_state_s *priv);
|
||||
static int mmcsd_hwinitialize(FAR struct mmcsd_state_s *priv);
|
||||
static void mmcsd_hwuninitialize(FAR struct mmcsd_state_s *priv);
|
||||
#ifdef CONFIG_MMCSD_IOCSUPPORT
|
||||
static int mmcsd_iocmd(FAR struct mmcsd_state_s *priv,
|
||||
FAR struct mmc_ioc_cmd *ic_ptr);
|
||||
static int mmcsd_multi_iocmd(FAR struct mmcsd_state_s *priv,
|
||||
FAR struct mmc_ioc_multi_cmd *imc_ptr);
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
@@ -2342,6 +2348,30 @@ static int mmcsd_ioctl(FAR struct inode *inode, int cmd, unsigned long arg)
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_MMCSD_IOCSUPPORT
|
||||
case MMC_IOC_CMD: /* MMCSD device ioctl commands */
|
||||
{
|
||||
finfo("MMC_IOC_CMD\n");
|
||||
ret = mmcsd_iocmd(priv, (FAR struct mmc_ioc_cmd *)arg);
|
||||
if (ret != OK)
|
||||
{
|
||||
ferr("ERROR: mmcsd_iocmd failed: %d\n", ret);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MMC_IOC_MULTI_CMD: /* MMCSD device ioctl muti commands */
|
||||
{
|
||||
finfo("MMC_IOC_MULTI_CMD\n");
|
||||
ret = mmcsd_multi_iocmd(priv, (FAR struct mmc_ioc_multi_cmd *)arg);
|
||||
if (ret != OK)
|
||||
{
|
||||
ferr("ERROR: mmcsd_iocmd failed: %d\n", ret);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ret = -ENOTTY;
|
||||
break;
|
||||
@@ -2760,6 +2790,359 @@ static int mmcsd_read_csd(FAR struct mmcsd_state_s *priv)
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mmcsd_general_cmd_write
|
||||
*
|
||||
* Description:
|
||||
* Send cmd56 data, one sector size
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int mmcsd_general_cmd_write(FAR struct mmcsd_state_s *priv,
|
||||
FAR const uint8_t *buffer,
|
||||
off_t startblock)
|
||||
{
|
||||
int ret;
|
||||
|
||||
DEBUGASSERT(priv != NULL && buffer != NULL);
|
||||
|
||||
/* Check if the card is locked or write protected (either via software or
|
||||
* via the mechanical write protect on the card)
|
||||
*/
|
||||
|
||||
if (mmcsd_wrprotected(priv))
|
||||
{
|
||||
ferr("ERROR: Card is locked or write protected\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SDIO_DMA) && defined(CONFIG_ARCH_HAVE_SDIO_PREFLIGHT)
|
||||
/* If we think we are going to perform a DMA transfer, make sure that we
|
||||
* will be able to before we commit the card to the operation.
|
||||
*/
|
||||
|
||||
if ((priv->caps & SDIO_CAPS_DMASUPPORTED) != 0)
|
||||
{
|
||||
ret = SDIO_DMAPREFLIGHT(priv->dev, buffer, priv->blocksize);
|
||||
if (ret != OK)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Verify that the card is ready for the transfer. The card may still be
|
||||
* busy from the preceding write transfer. It would be simpler to check
|
||||
* for write busy at the end of each write, rather than at the beginning of
|
||||
* each read AND write, but putting the busy-wait at the beginning of the
|
||||
* transfer allows for more overlap and, hopefully, better performance
|
||||
*/
|
||||
|
||||
ret = mmcsd_transferready(priv);
|
||||
if (ret != OK)
|
||||
{
|
||||
ferr("ERROR: Card not ready: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Select the block size for the card */
|
||||
|
||||
ret = mmcsd_setblocklen(priv, priv->blocksize);
|
||||
if (ret != OK)
|
||||
{
|
||||
ferr("ERROR: mmcsd_setblocklen failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* If Controller does not need DMA setup before the write then send CMD56
|
||||
* now.
|
||||
*/
|
||||
|
||||
if ((priv->caps & SDIO_CAPS_DMABEFOREWRITE) == 0)
|
||||
{
|
||||
/* Send CMD56, WRITE_BLOCK, and verify good R1 status is returned */
|
||||
|
||||
mmcsd_sendcmdpoll(priv, MMCSD_CMD56WR, startblock);
|
||||
ret = mmcsd_recv_r1(priv, MMCSD_CMD56WR);
|
||||
if (ret != OK)
|
||||
{
|
||||
ferr("ERROR: mmcsd_recv_r1 for CMD56 failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Configure SDIO controller hardware for the write transfer */
|
||||
|
||||
SDIO_BLOCKSETUP(priv->dev, priv->blocksize, 1);
|
||||
SDIO_WAITENABLE(priv->dev,
|
||||
SDIOWAIT_TRANSFERDONE | SDIOWAIT_TIMEOUT | SDIOWAIT_ERROR,
|
||||
MMCSD_BLOCK_WDATADELAY);
|
||||
|
||||
#ifdef CONFIG_SDIO_DMA
|
||||
if ((priv->caps & SDIO_CAPS_DMASUPPORTED) != 0)
|
||||
{
|
||||
ret = SDIO_DMASENDSETUP(priv->dev, buffer, priv->blocksize);
|
||||
if (ret != OK)
|
||||
{
|
||||
finfo("SDIO_DMASENDSETUP: error %d\n", ret);
|
||||
SDIO_CANCEL(priv->dev);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
SDIO_SENDSETUP(priv->dev, buffer, priv->blocksize);
|
||||
}
|
||||
|
||||
/* If Controller needs DMA setup before write then only send CMD24 now. */
|
||||
|
||||
if ((priv->caps & SDIO_CAPS_DMABEFOREWRITE) != 0)
|
||||
{
|
||||
/* Send CMD56, WRITE_BLOCK, and verify good R1 status is returned */
|
||||
|
||||
mmcsd_sendcmdpoll(priv, MMCSD_CMD56WR, startblock);
|
||||
ret = mmcsd_recv_r1(priv, MMCSD_CMD56WR);
|
||||
if (ret != OK)
|
||||
{
|
||||
ferr("ERROR: mmcsd_recv_r1 for CMD56 failed: %d\n", ret);
|
||||
SDIO_CANCEL(priv->dev);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for the transfer to complete */
|
||||
|
||||
ret = mmcsd_eventwait(priv, SDIOWAIT_TIMEOUT | SDIOWAIT_ERROR);
|
||||
if (ret != OK)
|
||||
{
|
||||
ferr("ERROR: CMD56 transfer failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Flag that a write transfer is pending that we will have to check for
|
||||
* write complete at the beginning of the next transfer.
|
||||
*/
|
||||
|
||||
priv->wrbusy = true;
|
||||
|
||||
#if defined(CONFIG_MMCSD_SDIOWAIT_WRCOMPLETE)
|
||||
/* Arm the write complete detection with timeout */
|
||||
|
||||
SDIO_WAITENABLE(priv->dev, SDIOWAIT_WRCOMPLETE | SDIOWAIT_TIMEOUT,
|
||||
MMCSD_BLOCK_WDATADELAY);
|
||||
#endif
|
||||
|
||||
/* On success, return OK */
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mmcsd_general_cmd_read
|
||||
*
|
||||
* Description:
|
||||
* Read cmd56 data, one sector size
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int mmcsd_general_cmd_read(FAR struct mmcsd_state_s *priv,
|
||||
FAR uint8_t *buffer, off_t startblock)
|
||||
{
|
||||
int ret;
|
||||
|
||||
DEBUGASSERT(priv != NULL && buffer != NULL);
|
||||
|
||||
/* Check if the card is locked */
|
||||
|
||||
if (priv->locked)
|
||||
{
|
||||
ferr("ERROR: Card is locked\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SDIO_DMA) && defined(CONFIG_ARCH_HAVE_SDIO_PREFLIGHT)
|
||||
/* If we think we are going to perform a DMA transfer, make sure that we
|
||||
* will be able to before we commit the card to the operation.
|
||||
*/
|
||||
|
||||
if ((priv->caps & SDIO_CAPS_DMASUPPORTED) != 0)
|
||||
{
|
||||
ret = SDIO_DMAPREFLIGHT(priv->dev, buffer, priv->blocksize);
|
||||
if (ret != OK)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Verify that the card is ready for the transfer. The card may still be
|
||||
* busy from the preceding write transfer. It would be simpler to check
|
||||
* for write busy at the end of each write, rather than at the beginning of
|
||||
* each read AND write, but putting the busy-wait at the beginning of the
|
||||
* transfer allows for more overlap and, hopefully, better performance
|
||||
*/
|
||||
|
||||
ret = mmcsd_transferready(priv);
|
||||
if (ret != OK)
|
||||
{
|
||||
ferr("ERROR: Card not ready: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Select the block size for the card */
|
||||
|
||||
ret = mmcsd_setblocklen(priv, priv->blocksize);
|
||||
if (ret != OK)
|
||||
{
|
||||
ferr("ERROR: mmcsd_setblocklen failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Configure SDIO controller hardware for the read transfer */
|
||||
|
||||
SDIO_BLOCKSETUP(priv->dev, priv->blocksize, 1);
|
||||
SDIO_WAITENABLE(priv->dev,
|
||||
SDIOWAIT_TRANSFERDONE | SDIOWAIT_TIMEOUT | SDIOWAIT_ERROR,
|
||||
MMCSD_BLOCK_RDATADELAY);
|
||||
|
||||
#ifdef CONFIG_SDIO_DMA
|
||||
if ((priv->caps & SDIO_CAPS_DMASUPPORTED) != 0)
|
||||
{
|
||||
ret = SDIO_DMARECVSETUP(priv->dev, buffer, priv->blocksize);
|
||||
if (ret != OK)
|
||||
{
|
||||
finfo("SDIO_DMARECVSETUP: error %d\n", ret);
|
||||
SDIO_CANCEL(priv->dev);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
SDIO_RECVSETUP(priv->dev, buffer, priv->blocksize);
|
||||
}
|
||||
|
||||
/* Send CMD56: Read a sector size data and verify that good R1
|
||||
* status is returned.
|
||||
*/
|
||||
|
||||
mmcsd_sendcmdpoll(priv, MMCSD_CMD56RD, startblock);
|
||||
ret = mmcsd_recv_r1(priv, MMCSD_CMD56RD);
|
||||
if (ret != OK)
|
||||
{
|
||||
ferr("ERROR: mmcsd_recv_r1 for CMD56 failed: %d\n", ret);
|
||||
SDIO_CANCEL(priv->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Then wait for the data transfer to complete */
|
||||
|
||||
ret = mmcsd_eventwait(priv, SDIOWAIT_TIMEOUT | SDIOWAIT_ERROR);
|
||||
if (ret != OK)
|
||||
{
|
||||
ferr("ERROR: CMD56 transfer failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Return value: OK */
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MMCSD_IOCSUPPORT
|
||||
/****************************************************************************
|
||||
* Name: mmcsd_iocmd
|
||||
*
|
||||
* Description:
|
||||
* MMCSD device ioctl commands.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int mmcsd_iocmd(FAR struct mmcsd_state_s *priv,
|
||||
FAR struct mmc_ioc_cmd *ic_ptr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
DEBUGASSERT(priv != NULL && ic_ptr != NULL);
|
||||
|
||||
if (!ic_ptr->is_acmd)
|
||||
{
|
||||
uint32_t opcode = ic_ptr->opcode & MMCSD_CMDIDX_MASK;
|
||||
switch (opcode)
|
||||
{
|
||||
case MMCSD_CMDIDX56: /* support general commands */
|
||||
{
|
||||
if (ic_ptr->write_flag)
|
||||
{
|
||||
ret = mmcsd_general_cmd_write(priv,
|
||||
(FAR uint8_t *)(uintptr_t)(ic_ptr->data_ptr),
|
||||
ic_ptr->arg);
|
||||
if (ret != OK)
|
||||
{
|
||||
ferr("mmcsd_iocmd MMCSD_CMDIDX56 write failed.\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = mmcsd_general_cmd_read(priv,
|
||||
(FAR uint8_t *)(uintptr_t)(ic_ptr->data_ptr),
|
||||
ic_ptr->arg);
|
||||
if (ret != OK)
|
||||
{
|
||||
ferr("mmcsd_iocmd MMCSD_CMDIDX56 read failed.\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ferr("mmcsd_iocmd opcode unsupported.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mmcsd_multi_iocmd
|
||||
*
|
||||
* Description:
|
||||
* MMCSD device ioctl multi commands.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int mmcsd_multi_iocmd(FAR struct mmcsd_state_s *priv,
|
||||
FAR struct mmc_ioc_multi_cmd *imc_ptr)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
DEBUGASSERT(priv != NULL && imc_ptr != NULL);
|
||||
|
||||
if (imc_ptr->num_of_cmds > MMC_IOC_MAX_CMDS)
|
||||
{
|
||||
ferr("mmcsd_multi_iocmd too many cmds.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < imc_ptr->num_of_cmds; ++i)
|
||||
{
|
||||
ret = mmcsd_iocmd(priv, &imc_ptr->cmds[i]);
|
||||
if (ret != OK)
|
||||
{
|
||||
ferr("cmds %d failed.\n", i);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: mmcsd_sdinitialize
|
||||
*
|
||||
|
||||
+49
-7
@@ -36,7 +36,12 @@
|
||||
|
||||
/* mmcsd ioctl */
|
||||
|
||||
#define MMC_RPMB_TRANSFER _MMCSDIOC(0x0000)
|
||||
#define MMC_IOC_CMD _MMCSDIOC(0x0000)
|
||||
#define MMC_IOC_MULTI_CMD _MMCSDIOC(0x0001)
|
||||
|
||||
#define MMC_IOC_MAX_BYTES (512L * 1024)
|
||||
#define MMC_IOC_MAX_CMDS 255
|
||||
#define mmc_ioc_cmd_set_data(ic, ptr) (ic).data_ptr = (uint64_t)(unsigned long)(ptr)
|
||||
|
||||
/* rpmb request */
|
||||
|
||||
@@ -49,6 +54,49 @@
|
||||
* Public Types
|
||||
****************************************************************************/
|
||||
|
||||
struct mmc_ioc_cmd
|
||||
{
|
||||
/* Direction of data: nonzero = write, zero = read.
|
||||
* Bit 31 selects 'Reliable Write' for RPMB.
|
||||
*/
|
||||
|
||||
int write_flag;
|
||||
|
||||
/* Application-specific command. true = precede with CMD55 */
|
||||
|
||||
int is_acmd;
|
||||
|
||||
uint32_t opcode;
|
||||
uint32_t arg;
|
||||
uint32_t response[4]; /* CMD response */
|
||||
unsigned int flags;
|
||||
unsigned int blksz;
|
||||
unsigned int blocks;
|
||||
|
||||
/* For 64-bit machines, the next member, ``uint64_t data_ptr``, wants to
|
||||
* be 8-byte aligned. Make sure this struct is the same size when
|
||||
* built for 32-bit.
|
||||
*/
|
||||
|
||||
uint32_t pad;
|
||||
|
||||
/* DAT buffer */
|
||||
|
||||
uint64_t data_ptr;
|
||||
};
|
||||
|
||||
/* struct mmc_ioc_multi_cmd - multi command information
|
||||
* @num_of_cmds: Number of commands to send. Must be equal to or less than
|
||||
* MMC_IOC_MAX_CMDS.
|
||||
* @cmds: Array of commands with length equal to 'num_of_cmds'
|
||||
*/
|
||||
|
||||
struct mmc_ioc_multi_cmd
|
||||
{
|
||||
uint64_t num_of_cmds;
|
||||
struct mmc_ioc_cmd cmds[1];
|
||||
};
|
||||
|
||||
struct mmc_rpmb_frame_s
|
||||
{
|
||||
uint8_t stuff[196];
|
||||
@@ -62,12 +110,6 @@ struct mmc_rpmb_frame_s
|
||||
uint16_t req_resp;
|
||||
};
|
||||
|
||||
struct mmc_rpmb_transfer_s
|
||||
{
|
||||
unsigned int num_of_frames;
|
||||
struct mmc_rpmb_frame_s frames[0];
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions Definitions
|
||||
****************************************************************************/
|
||||
|
||||
Reference in New Issue
Block a user