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>
This commit is contained in:
msli-dev 2026-10-03 07:02:44 +08:00 • committed by GUIDINGLI
parent 693513f034
commit 844ef9c678
3 changed files with 101 additions and 5 deletions

View file

@ -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"

View file

@ -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)

View file

@ -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);
};
/****************************************************************************