From 844ef9c678c1d00106690bcc8c9eba8bdc90285a Mon Sep 17 00:00:00 2001 From: msli-dev <747640013@qq.com> Date: Sat, 3 Oct 2026 07:02:44 +0800 Subject: [PATCH] mmcsd: Honor optional SDIO host request limits. Add an optional maxrequest callback at the end of sdio_dev_s. A zero or unset callback adds no host-specific limit; nonzero values are byte limits that apply to all request buffers. Combine the host limit with MMCSD_MULTIBLOCK_LIMIT when splitting block reads and writes. Reject a host limit smaller than one block and oversized raw multi-block commands before starting the transfer. Cancel receive setup after a failed CMD23, attempt CMD12 after failed open-ended multi-block reads, and propagate stop-command failures. Keep these generic MMC/SD changes separate from the STM32H7 driver. Assisted-by: Codex:GPT-6 Signed-off-by: msli-dev <747640013@qq.com> --- drivers/mmcsd/Kconfig | 4 ++- drivers/mmcsd/mmcsd_sdio.c | 74 +++++++++++++++++++++++++++++++++++--- include/nuttx/sdio.h | 28 +++++++++++++++ 3 files changed, 101 insertions(+), 5 deletions(-) diff --git a/drivers/mmcsd/Kconfig b/drivers/mmcsd/Kconfig index 648c41adde9..b7e03943c96 100644 --- a/drivers/mmcsd/Kconfig +++ b/drivers/mmcsd/Kconfig @@ -62,7 +62,9 @@ config MMCSD_MULTIBLOCK_LIMIT Default to 0, means no limitation. Block count larger than this limit will be split to multiple multi-block transfer. Set it to 1 will only use single-block transfer mode, and can be used to work around - buggy SDIO drivers that cannot handle multiple block transfers. + buggy SDIO drivers that cannot handle multiple block transfers. If + the SDIO host advertises a smaller maximum request size, the smaller + host limit is used automatically. config MMCSD_MMCSUPPORT bool "MMC cards support" diff --git a/drivers/mmcsd/mmcsd_sdio.c b/drivers/mmcsd/mmcsd_sdio.c index 3ea6b85a6a5..748f773e312 100644 --- a/drivers/mmcsd/mmcsd_sdio.c +++ b/drivers/mmcsd/mmcsd_sdio.c @@ -194,6 +194,7 @@ static int mmcsd_switch(FAR struct mmcsd_state_s *priv, uint32_t arg); /* Transfer helpers *********************************************************/ static bool mmcsd_wrprotected(FAR struct mmcsd_state_s *priv); +static size_t mmcsd_blocklimit(FAR struct mmcsd_state_s *priv); static int mmcsd_eventwait(FAR struct mmcsd_state_s *priv, sdio_eventset_t failevents); static int mmcsd_transferready(FAR struct mmcsd_state_s *priv); @@ -1280,6 +1281,36 @@ static int mmcsd_verifystate(FAR struct mmcsd_state_s *priv, uint32_t state) * Transfer Helpers ****************************************************************************/ +/**************************************************************************** + * Name: mmcsd_blocklimit + * + * Description: + * Return the maximum number of blocks for one transfer. Combine the + * generic MMC/SD configuration limit with a limit advertised by the host + * controller. + * + ****************************************************************************/ + +static size_t mmcsd_blocklimit(FAR struct mmcsd_state_s *priv) +{ + size_t maxrequest = SDIO_MAXREQUEST(priv->dev); + size_t blocklimit = MMCSD_MULTIBLOCK_LIMIT; + + DEBUGASSERT(priv->blocksize > 0); + + if (maxrequest > 0) + { + size_t hostlimit = maxrequest / priv->blocksize; + + if (blocklimit > hostlimit) + { + blocklimit = hostlimit; + } + } + + return blocklimit; +} + /**************************************************************************** * Name: mmcsd_wrprotected * @@ -1715,6 +1746,7 @@ static ssize_t mmcsd_readmultiple(FAR struct mmcsd_part_s *part, { FAR struct mmcsd_state_s *priv = part->priv; size_t nbytes = nblocks << priv->blockshift; + size_t maxrequest = SDIO_MAXREQUEST(priv->dev); #ifdef CONFIG_MMCSD_MMCSUPPORT uint32_t partnum = part - priv->part; #endif @@ -1724,6 +1756,11 @@ static ssize_t mmcsd_readmultiple(FAR struct mmcsd_part_s *part, finfo("startblock=%jd nblocks=%zu\n", (intmax_t)startblock, nblocks); DEBUGASSERT(priv != NULL && buffer != NULL); + if (maxrequest > 0 && nbytes > maxrequest) + { + return -E2BIG; + } + /* Check if the card is locked */ if (priv->locked) @@ -1842,6 +1879,7 @@ static ssize_t mmcsd_readmultiple(FAR struct mmcsd_part_s *part, ret = mmcsd_setblockcount(priv, nblocks); if (ret != OK) { + SDIO_CANCEL(priv->dev); return ret; } } @@ -1865,6 +1903,11 @@ static ssize_t mmcsd_readmultiple(FAR struct mmcsd_part_s *part, if (ret != OK) { ferr("ERROR: CMD18 transfer failed: %d\n", ret); + if (IS_SD(priv->type) && !priv->cmd23support) + { + mmcsd_stoptransmission(priv); + } + return ret; } @@ -1877,6 +1920,7 @@ static ssize_t mmcsd_readmultiple(FAR struct mmcsd_part_s *part, if (ret != OK) { ferr("ERROR: mmcsd_stoptransmission failed: %d\n", ret); + return ret; } } @@ -2094,6 +2138,7 @@ static ssize_t mmcsd_writemultiple(FAR struct mmcsd_part_s *part, { FAR struct mmcsd_state_s *priv = part->priv; size_t nbytes = nblocks << priv->blockshift; + size_t maxrequest = SDIO_MAXREQUEST(priv->dev); #ifdef CONFIG_MMCSD_MMCSUPPORT uint32_t partnum = part - priv->part; #endif @@ -2104,6 +2149,11 @@ static ssize_t mmcsd_writemultiple(FAR struct mmcsd_part_s *part, finfo("startblock=%jd nblocks=%zu\n", (intmax_t)startblock, nblocks); DEBUGASSERT(priv != NULL && buffer != NULL); + if (maxrequest > 0 && nbytes > maxrequest) + { + return -E2BIG; + } + /* Check if the card is locked or write protected (either via software or * via the mechanical write protect on the card) */ @@ -2448,6 +2498,7 @@ static ssize_t mmcsd_read(FAR struct inode *inode, unsigned char *buffer, FAR struct mmcsd_part_s *part; size_t sector; size_t endsector; + size_t blocklimit; ssize_t nread; ssize_t ret = nsectors; @@ -2468,6 +2519,13 @@ static ssize_t mmcsd_read(FAR struct inode *inode, unsigned char *buffer, ret = nsectors; endsector = startsector + nsectors; + blocklimit = mmcsd_blocklimit(priv); + if (blocklimit == 0) + { + mmcsd_unlock(priv); + return -E2BIG; + } + for (sector = startsector; sector < endsector; sector += nread) { /* Read this sector into the user buffer */ @@ -2478,9 +2536,9 @@ static ssize_t mmcsd_read(FAR struct inode *inode, unsigned char *buffer, nread = mmcsd_readsingle(part, buffer, sector); #else nread = endsector - sector; - if (nread > MMCSD_MULTIBLOCK_LIMIT) + if (nread > blocklimit) { - nread = MMCSD_MULTIBLOCK_LIMIT; + nread = blocklimit; } if (nread == 1) @@ -2529,6 +2587,7 @@ static ssize_t mmcsd_write(FAR struct inode *inode, FAR struct mmcsd_part_s *part; size_t sector; size_t endsector; + size_t blocklimit; ssize_t nwrite; ssize_t ret = nsectors; @@ -2549,6 +2608,13 @@ static ssize_t mmcsd_write(FAR struct inode *inode, ret = nsectors; endsector = startsector + nsectors; + blocklimit = mmcsd_blocklimit(priv); + if (blocklimit == 0) + { + mmcsd_unlock(priv); + return -E2BIG; + } + for (sector = startsector; sector < endsector; sector += nwrite) { /* Write this sector into the user buffer */ @@ -2559,9 +2625,9 @@ static ssize_t mmcsd_write(FAR struct inode *inode, nwrite = mmcsd_writesingle(part, buffer, sector); #else nwrite = endsector - sector; - if (nwrite > MMCSD_MULTIBLOCK_LIMIT) + if (nwrite > blocklimit) { - nwrite = MMCSD_MULTIBLOCK_LIMIT; + nwrite = blocklimit; } if (nwrite == 1) diff --git a/include/nuttx/sdio.h b/include/nuttx/sdio.h index ec3e6243ed9..8ce6bf3cc2b 100644 --- a/include/nuttx/sdio.h +++ b/include/nuttx/sdio.h @@ -485,6 +485,28 @@ #define SDIO_CAPS_MMC_HS_MODE 0x40 /* Bit 6=1: Supports eMMC high speed mode */ #define SDIO_CAPS_SD_HS_MODE 0x80 /* Bit 7=1: Supports SD card high speed mode */ +/**************************************************************************** + * Name: SDIO_MAXREQUEST + * + * Description: + * Return the maximum number of bytes that the host can transfer in one + * request. A return value of zero means that the host does not impose an + * additional limit. A nonzero limit applies to all buffers, including + * directly DMA-accessible buffers. Callers must split requests or reject + * commands that cannot be split. A limit smaller than a block cannot + * support block I/O. + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * Maximum request size in bytes, or zero for no host-specific limit. + * + ****************************************************************************/ + +#define SDIO_MAXREQUEST(dev) \ + ((dev)->maxrequest ? (dev)->maxrequest(dev) : 0) + /**************************************************************************** * Name: SDIO_STATUS * @@ -1066,6 +1088,12 @@ struct sdio_dev_s #endif /* CONFIG_SDIO_DMA */ CODE void (*gotextcsd)(FAR struct sdio_dev_s *dev, FAR const uint8_t *buffer); + + /* Optional request limit in bytes; NULL means no additional limit. + * Keep optional extensions at the end for positional initializers. + */ + + CODE size_t (*maxrequest)(FAR struct sdio_dev_s *dev); }; /****************************************************************************