This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push: new 4b4004b874 drivers/mmcsd: Add MMC_IOC_CMD ioctl 4b4004b874 is described below commit 4b4004b874a36c604a611bf5eba8645cfad7b66b Author: helei8 <hel...@xiaomi.com> AuthorDate: Mon Dec 5 19:54:31 2022 +0800 drivers/mmcsd: Add MMC_IOC_CMD ioctl Signed-off-by: helei8 <hel...@xiaomi.com> --- drivers/mmcsd/Kconfig | 6 + drivers/mmcsd/mmcsd_sdio.c | 383 +++++++++++++++++++++++++++++++++++++++++++++ include/nuttx/mmcsd.h | 56 ++++++- 3 files changed, 438 insertions(+), 7 deletions(-) diff --git a/drivers/mmcsd/Kconfig b/drivers/mmcsd/Kconfig index bf6ac955bc..6511a0c40b 100644 --- a/drivers/mmcsd/Kconfig +++ b/drivers/mmcsd/Kconfig @@ -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 diff --git a/drivers/mmcsd/mmcsd_sdio.c b/drivers/mmcsd/mmcsd_sdio.c index 3b8cabf379..a26c4d4953 100644 --- a/drivers/mmcsd/mmcsd_sdio.c +++ b/drivers/mmcsd/mmcsd_sdio.c @@ -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 * diff --git a/include/nuttx/mmcsd.h b/include/nuttx/mmcsd.h index 6ee185e966..9669f3d95d 100644 --- a/include/nuttx/mmcsd.h +++ b/include/nuttx/mmcsd.h @@ -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 ****************************************************************************/