Linux MultiMedia Card development
 help / color / mirror / Atom feed
From: Torstein Eide <torsteine@gmail.com>
To: linux-mmc@vger.kernel.org
Cc: Torstein Eide <torsteine+linux@gmail.com>
Subject: [PATCH 3/4] mmc-utils: lsmmc: Add mmc list command
Date: Fri, 15 May 2026 10:34:56 +0200	[thread overview]
Message-ID: <20260515083457.3690804-4-torsteine+linux@gmail.com> (raw)
In-Reply-To: <20260515083457.3690804-1-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.

Signed-off-by: Torstein Eide <torsteine+linux@gmail.com>
---
 docs/HOWTO.rst |  12 ++++
 lsmmc.c        | 167 +++++++++++++++++++++++++++++++++++++++++++++++++
 mmc.c          |   6 +-
 mmc_cmds.h     |   1 +
 4 files changed, 185 insertions(+), 1 deletion(-)

diff --git a/docs/HOWTO.rst b/docs/HOWTO.rst
index 76214a3..167a153 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 3149409..4f378fa 100644
--- a/lsmmc.c
+++ b/lsmmc.c
@@ -253,6 +253,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)
 {
@@ -2203,6 +2235,141 @@ static int do_read_reg(int argc, char **argv, enum REG_TYPE reg)
 	return ret;
 }
 
+static const char *month_name(unsigned int month)
+{
+	static const char *months[] = {
+		"???", "jan", "feb", "mar", "apr", "may", "jun",
+		"jul", "aug", "sep", "oct", "nov", "dec",
+	};
+
+	if (month >= ARRAY_SIZE(months))
+		return "???";
+
+	return months[month];
+}
+
+static void print_list_entry(struct config *cfg, const char *devname,
+			     const char *blkdev, char *cid)
+{
+	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;
+		unsigned int 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,
+		       month_name(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,
+		       month_name(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..01ad181 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 }
 };
 
@@ -567,4 +572,3 @@ int main(int ac, char **av )
 
 	exit(func(nargs, args));
 }
-
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


  parent reply	other threads:[~2026-05-15  8:35 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-15  8:34 [PATCH 0/4] mmc-utils: improve lsmmc usability Torstein Eide
2026-05-15  8:34 ` [PATCH 1/4] mmc-utils: lsmmc: Use external .ids files for manufacturer lookup Torstein Eide
2026-05-15 15:05   ` Avri Altman
2026-05-15  8:34 ` [PATCH 2/4] mmc-utils: lsmmc: Accept /dev and /sys/block paths for register reads Torstein Eide
2026-05-15 15:26   ` Avri Altman
2026-05-15  8:34 ` Torstein Eide [this message]
2026-05-15  8:34 ` [PATCH 4/4] mmc-utils: Add bash completion Torstein Eide
2026-05-15 14:29 ` [PATCH 0/4] mmc-utils: improve lsmmc usability Avri Altman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260515083457.3690804-4-torsteine+linux@gmail.com \
    --to=torsteine@gmail.com \
    --cc=linux-mmc@vger.kernel.org \
    --cc=torsteine+linux@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox