You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by ac...@apache.org on 2023/01/30 17:09:30 UTC
[nuttx] branch master updated: drivers/mmcsd: Add MMC_IOC_CMD ioctl
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 <he...@xiaomi.com>
AuthorDate: Mon Dec 5 19:54:31 2022 +0800
drivers/mmcsd: Add MMC_IOC_CMD ioctl
Signed-off-by: helei8 <he...@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
****************************************************************************/