* [PATCH 1/1] mmc-utils: lsmmc: Add 'mmc list' command
@ 2026-05-12 17:12 Torstein Eide
0 siblings, 0 replies; only message in thread
From: Torstein Eide @ 2026-05-12 17:12 UTC (permalink / raw)
To: linux-mmc; +Cc: Torstein EIde
From: Torstein EIde <torsteine+linux@gmail.com>
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.
---
docs/HOWTO.rst | 5 ++
lsmmc.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++++
mmc.c | 5 ++
mmc_cmds.h | 1 +
4 files changed, 173 insertions(+)
diff --git a/docs/HOWTO.rst b/docs/HOWTO.rst
index 02edea4..45c440b 100644
--- a/docs/HOWTO.rst
+++ b/docs/HOWTO.rst
@@ -84,6 +84,11 @@ 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 e33d636..ec79841 100644
--- a/lsmmc.c
+++ b/lsmmc.c
@@ -252,6 +252,38 @@ static char *resolve_dev_path(const char *path)
return resolved;
}
+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(char *name)
{
@@ -2197,6 +2229,136 @@ 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)
+{
+ static const char *months[] = {
+ "jan", "feb", "mar", "apr", "may", "jun",
+ "jul", "aug", "sep", "oct", "nov", "dec",
+ "???", "???", "???", "???",
+ };
+ const char *mfr;
+ char devnode[32];
+
+ snprintf(devnode, sizeof(devnode), "/dev/%s", blkdev ? blkdev : "?");
+
+ if (cfg->bus == SD) {
+ unsigned int mid, prv_major, prv_minor, psn, mdt_year, mdt_month, crc;
+ char oid[3], pnm[6];
+
+ parse_bin(cid, "8u16a40a4u4u32u4r8u4u7u1r",
+ &mid, &oid[0], &pnm[0],
+ &prv_major, &prv_minor, &psn,
+ &mdt_year, &mdt_month, &crc);
+ oid[2] = '\0';
+ pnm[5] = '\0';
+
+ mfr = get_manufacturer(cfg, mid);
+ printf("%-14s %-14s SD %-20s %-10s %u.%u 0x%08x %u-%s\n",
+ devname, devnode,
+ mfr ? mfr : "Unlisted",
+ pnm, prv_major, prv_minor,
+ psn, 2000 + mdt_year, months[mdt_month]);
+ } else {
+ unsigned int mid, cbx, oid, prv_major, prv_minor, psn;
+ unsigned int mdt_year, mdt_month, crc;
+ char pnm[7];
+
+ parse_bin(cid, "8u6r2u8u48a4u4u32u4u4u7u1r",
+ &mid, &cbx, &oid, &pnm[0],
+ &prv_major, &prv_minor, &psn,
+ &mdt_year, &mdt_month, &crc);
+ pnm[6] = '\0';
+
+ mfr = get_manufacturer(cfg, mid);
+ printf("%-14s %-14s MMC %-20s %-10s %u.%u 0x%08x %u-%s\n",
+ devname, devnode,
+ mfr ? mfr : "Unlisted",
+ pnm, prv_major, prv_minor,
+ psn, 1997 + mdt_year, months[mdt_month]);
+ }
+}
+
+int do_list(int nargs, char **argv)
+{
+ const char *bus_path = "/sys/bus/mmc/devices";
+ char orig_cwd[PATH_MAX];
+ DIR *d;
+ struct dirent *ent;
+ bool header_printed = false;
+
+ if (!getcwd(orig_cwd, sizeof(orig_cwd))) {
+ fprintf(stderr, "Cannot get current directory.\n");
+ return -1;
+ }
+
+ d = opendir(bus_path);
+ if (!d) {
+ fprintf(stderr, "Cannot open %s\n", bus_path);
+ return -1;
+ }
+
+ while ((ent = readdir(d)) != NULL) {
+ char devpath[PATH_MAX];
+ char resolved[PATH_MAX];
+ char *type, *cid, *blkdev;
+ struct config cfg = {};
+
+ if (!strchr(ent->d_name, ':'))
+ continue;
+
+ snprintf(devpath, sizeof(devpath), "%s/%s", bus_path, ent->d_name);
+ if (realpath(devpath, resolved) == NULL)
+ continue;
+
+ if (chdir(resolved) < 0)
+ continue;
+
+ if (access("cid", R_OK) != 0) {
+ if (chdir(orig_cwd) < 0)
+ break;
+ continue;
+ }
+
+ type = read_file("type");
+ if (!type) {
+ if (chdir(orig_cwd) < 0)
+ break;
+ continue;
+ }
+
+ cid = read_file("cid");
+ if (!cid) {
+ free(type);
+ if (chdir(orig_cwd) < 0)
+ break;
+ continue;
+ }
+
+ blkdev = find_block_devname(resolved);
+ cfg.bus = strcmp(type, "MMC") ? SD : MMC;
+ cfg.ids_dir = orig_cwd;
+
+ 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);
+ if (chdir(orig_cwd) < 0)
+ break;
+ }
+
+ 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 077e901..0953bef 100644
--- a/mmc.c
+++ b/mmc.c
@@ -292,6 +292,11 @@ static struct Command commands[] = {
"4. The MMC will perform a soft reset, if your system cannot handle that do not use the boot operation from mmc-utils.\n",
NULL
},
+ { do_list, 0,
+ "list", "\n"
+ "List all MMC/SD devices with their /dev path and CID info.",
+ NULL
+ },
{ NULL, 0, NULL, NULL }
};
diff --git a/mmc_cmds.h b/mmc_cmds.h
index 407cbe6..508776c 100644
--- a/mmc_cmds.h
+++ b/mmc_cmds.h
@@ -49,6 +49,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
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-05-12 17:12 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-12 17:12 [PATCH 1/1] mmc-utils: lsmmc: Add 'mmc list' command Torstein Eide
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox