[PATCH v6 3/3] mmc-utils: lsmmc: Add mmc list command
Torstein Eide <[email protected]> Mon, 10 Aug 2026 17:34:42 +0200
| Newsgroups | org.kernel.vger.linux-mmc |
|---|---|
| Message-ID | <[email protected]> |
Add a 'mmc list' command that scans /sys/bus/mmc/devices/, resolves each card's sysfs path, and prints a one-line summary per device. find_block_devname() maps a sysfs device path back to its mmcblkN name by scanning /sys/class/block/mmcblkN/device symlinks. print_list_entry() parses the CID register and formats a fixed-width table row with device name, /dev path, bus type, manufacturer, product, revision, serial number, and manufacturing date. The header is printed once before the first result. Devices without a readable cid file are skipped silently via access() so unrelated sysfs entries do not produce spurious error messages. Signed-off-by: Torstein Eide <[email protected]> Reviewed-by: Avri Altman <[email protected]> --- docs/HOWTO.rst | 12 +++++ lsmmc.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++++ mmc.c | 6 ++- mmc_cmds.h | 1 + 4 files changed, 151 insertions(+), 1 deletion(-) diff --git a/docs/HOWTO.rst b/docs/HOWTO.rst index 3739a0a..0b486e4 100644 --- a/docs/HOWTO.rst +++ b/docs/HOWTO.rst @@ -90,6 +90,18 @@ Running mmc-utils sysfs: /sys/devices/platform/fe320000.mmc/mmc_host/mmc1/mmc1:aaaa SCR Register: 0235800000000000 + ``list`` + List all MMC/SD devices present on the system. Output is a table with + columns: DEVICE (sysfs name), DEV (/dev path), TYPE (MMC or SD), + MANUFACTURER, PRODUCT, REV, SERIAL, and DATE. + + Example:: + + $ mmc list + DEVICE DEV TYPE MANUFACTURER PRODUCT REV SERIAL DATE + mmc0:0001 /dev/mmcblk0 MMC Samsung MAG4FA 1.0 0x1a2b3c4d 2021-jan + mmc1:aaaa /dev/mmcblk1 SD SanDisk SP32G 8.0 0x5e6f7a8b 2020-mar + ``ffu <image name> <device> [chunk-bytes]`` Default mode. Run Field Firmware Update with `<image name>` on `<device>`. `[chunk-bytes]` is optional and defaults to its max - 512k. Should be in decimal bytes and sector aligned. diff --git a/lsmmc.c b/lsmmc.c index 3139a7d..4167095 100644 --- a/lsmmc.c +++ b/lsmmc.c @@ -229,6 +229,38 @@ fallback: return strdup(name); } +static char *find_block_devname(const char *sysfs_devpath) +{ + DIR *d; + struct dirent *ent; + + d = opendir("/sys/class/block"); + if (!d) + return NULL; + + while ((ent = readdir(d)) != NULL) { + char linkpath[PATH_MAX]; + char resolved[PATH_MAX]; + + if (strncmp(ent->d_name, "mmcblk", 6) != 0) + continue; + if (strchr(ent->d_name + 6, 'p')) + continue; + + snprintf(linkpath, sizeof(linkpath), + "/sys/class/block/%s/device", ent->d_name); + if (realpath(linkpath, resolved) == NULL) + continue; + if (strcmp(resolved, sysfs_devpath) == 0) { + closedir(d); + return strdup(ent->d_name); + } + } + + closedir(d); + return NULL; +} + /* MMC/SD file parsing functions */ static char *read_file_at(const char *dir, const char *name) { @@ -2290,6 +2322,107 @@ static int do_read_reg(int argc, char **argv, enum REG_TYPE reg) return ret; } +static void print_list_entry(struct config *cfg, const char *devname, + const char *blkdev, char *cid) +{ + char *mfr; + char devnode[32]; + + snprintf(devnode, sizeof(devnode), "/dev/%s", blkdev ? blkdev : "?"); + + if (cfg->bus == SD) { + struct sd_cid c; + + parse_sd_cid(cid, &c); + mfr = get_manufacturer(cfg, c.mid); + printf("%-14s %-14s SD %-20s %-10s %u.%u 0x%08x %u-%s\n", + devname, devnode, mfr ? mfr : "Unlisted", c.pnm, + c.prv_major, c.prv_minor, c.psn, sd_cid_year(&c), + month_name(c.mdt_month)); + } else { + struct mmc_cid c; + + parse_mmc_cid(cid, &c); + mfr = get_manufacturer(cfg, c.mid); + printf("%-14s %-14s MMC %-20s %-10s %u.%u 0x%08x %u-%s\n", + devname, devnode, mfr ? mfr : "Unlisted", c.pnm, + c.prv_major, c.prv_minor, c.psn, + mmc_cid_year(&c, cfg->ext_csd_rev), + month_name(c.mdt_month)); + } + + free(mfr); +} + +int do_list(int nargs, char **argv) +{ + const char *bus_path = "/sys/bus/mmc/devices"; + DIR *d; + struct dirent *ent; + bool header_printed = false; + + d = opendir(bus_path); + if (!d) { + fprintf(stderr, "Cannot open %s\n", bus_path); + return -1; + } + + struct config cfg = {}; + + /* Probe ids files so any warning appears before table output */ + cfg.bus = SD; + free(get_manufacturer(&cfg, ~0u)); + cfg.bus = MMC; + free(get_manufacturer(&cfg, ~0u)); + + while ((ent = readdir(d)) != NULL) { + char devpath[PATH_MAX]; + char resolved[PATH_MAX]; + char *type, *cid, *blkdev; + + if (!strchr(ent->d_name, ':')) + continue; + + snprintf(devpath, sizeof(devpath), "%s/%s", bus_path, ent->d_name); + if (realpath(devpath, resolved) == NULL) + continue; + + type = read_file_at(resolved, "type"); + if (!type) + continue; + + if (strcmp(type, "MMC") != 0 && strcmp(type, "SD") != 0) { + free(type); + continue; + } + + cid = read_file_at(resolved, "cid"); + if (!cid) { + free(type); + continue; + } + + blkdev = find_block_devname(resolved); + cfg.bus = strcmp(type, "MMC") ? SD : MMC; + + if (!header_printed) { + printf("%-14s %-14s %-5s%-20s %-10s %-5s %-12s %s\n", + "DEVICE", "DEV", "TYPE", "MANUFACTURER", "PRODUCT", + "REV", "SERIAL", "DATE"); + header_printed = true; + } + + print_list_entry(&cfg, ent->d_name, blkdev, cid); + + free(blkdev); + free(cid); + free(type); + } + + closedir(d); + return 0; +} + int do_read_csd(int argc, char **argv) { return do_read_reg(argc, argv, CSD); diff --git a/mmc.c b/mmc.c index fce7eef..b0f1f9b 100644 --- a/mmc.c +++ b/mmc.c @@ -306,6 +306,11 @@ static struct Command commands[] = { "3. Only up to 512K bytes of boot data will be transferred.\n" "4. The MMC will perform a soft reset, if your system cannot handle that do not use the boot operation from mmc-utils.\n", }, + { do_list, 0, + "list", "\n" + "List all MMC/SD devices with their /dev path and CID info.", + NULL + }, { NULL, 0, NULL, NULL } }; @@ -590,4 +595,3 @@ int main(int ac, char **av ) exit(func(nargs, args)); } - diff --git a/mmc_cmds.h b/mmc_cmds.h index 9d5f944..033cc09 100644 --- a/mmc_cmds.h +++ b/mmc_cmds.h @@ -62,6 +62,7 @@ int do_opt_ffu4(int nargs, char **argv); int do_read_scr(int argc, char **argv); int do_read_cid(int argc, char **argv); int do_read_csd(int argc, char **argv); +int do_list(int nargs, char **argv); int do_erase(int nargs, char **argv); int do_general_cmd_read(int nargs, char **argv); int do_softreset(int nargs, char **argv); -- 2.53.0