[][MAC80211][WiFi6/7][app][Add eMMC support in atenl]

[Description]
Add eMMC support in atenl
1. add mtd/mmc open/write function
2. add new opt for specifying partition name and offset
   Usage:
    - atenl -i phy0 -c "sync eeprom all" -p <name>:<offset>
    - <offset> can be specified as decimal (e.g. 2560) or
      hexadecimal (0xa000)

[Release-log]
N/A

Change-Id: I10c19e15d2500cb287793a5e77f1b336eb6870a5
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/9897396
diff --git a/feed/app/atenl/Makefile b/feed/app/atenl/Makefile
index b0b4744..f4246db 100644
--- a/feed/app/atenl/Makefile
+++ b/feed/app/atenl/Makefile
@@ -20,10 +20,11 @@
   CATEGORY:=MTK Properties
   TITLE:=testmode daemon for nl80211
   SUBMENU:=Applications
-  DEPENDS:=+libnl-tiny
+  DEPENDS:=+libnl-tiny +libblkid
 endef
 
 TARGET_CFLAGS += -I$(STAGING_DIR)/usr/include/libnl-tiny
+TARGET_CFLAGS += -lblkid
 
 define Package/atenl/install
 	mkdir -p $(1)/usr/sbin
diff --git a/feed/app/atenl/src/atenl.h b/feed/app/atenl/src/atenl.h
index da3fd72..1cd5a3e 100644
--- a/feed/app/atenl/src/atenl.h
+++ b/feed/app/atenl/src/atenl.h
@@ -92,8 +92,8 @@
 	bool unicast;
 	int sock_eth;
 
-	const char *mtd_part;
-	u32 mtd_offset;
+	const char *flash_part;
+	u32 flash_offset;
 	u8 band_idx;
 	u8 *eeprom_data;
 	int eeprom_fd;
@@ -465,7 +465,7 @@
 		       u16 ch, u16 center_ch1, u16 center_ch2);
 int atenl_nl_process(struct atenl *an, struct atenl_data *data);
 int atenl_nl_process_many(struct atenl *an, struct atenl_data *data);
-int atenl_nl_check_mtd(struct atenl *an);
+int atenl_nl_check_flash(struct atenl *an);
 int atenl_nl_write_eeprom(struct atenl *an, u32 offset, u8 *val, int len);
 int atenl_nl_write_efuse_all(struct atenl *an);
 int atenl_nl_write_ext_eeprom_all(struct atenl *an);
@@ -477,7 +477,7 @@
 void atenl_get_ibf_cal_result(struct atenl *an);
 int atenl_eeprom_init(struct atenl *an, u8 phy_idx);
 void atenl_eeprom_close(struct atenl *an);
-int atenl_eeprom_write_mtd(struct atenl *an);
+int atenl_eeprom_write_flash(struct atenl *an);
 int atenl_eeprom_update_precal(struct atenl *an, int write_offs, int size);
 int atenl_eeprom_read_from_driver(struct atenl *an, u32 offset, int len);
 void atenl_eeprom_cmd_handler(struct atenl *an, u8 phy_idx, char *cmd);
diff --git a/feed/app/atenl/src/eeprom.c b/feed/app/atenl/src/eeprom.c
index 6dd4113..5f060aa 100644
--- a/feed/app/atenl/src/eeprom.c
+++ b/feed/app/atenl/src/eeprom.c
@@ -3,6 +3,8 @@
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <blkid/blkid.h>
 
 #include "atenl.h"
 
@@ -357,8 +359,8 @@
 	char buf[30];
 
 	set_band_val(an, 0, phy_idx, phy_idx);
-	atenl_nl_check_mtd(an);
-	flash_mode = an->mtd_part != NULL;
+	atenl_nl_check_flash(an);
+	flash_mode = an->flash_part != NULL;
 
 	// Get the first main phy index for this chip
 	an->main_phy_idx = phy_idx - an->band_idx;
@@ -430,30 +432,146 @@
 	return 0;
 }
 
-int atenl_eeprom_write_mtd(struct atenl *an)
+int atenl_mtd_open(struct atenl *an, int flags)
 {
-#define TMP_FILE	"/tmp/tmp_eeprom.bin"
-	pid_t pid;
+	char dev[128], buf[16];
+	char *part_num;
+	FILE *f;
+	int fd;
+
+	f = fopen("/proc/mtd", "r");
+	if (!f)
+		return -1;
+
+	while (fgets(dev, sizeof(dev), f)) {
+		if (!strcasestr(dev, an->flash_part))
+			continue;
+
+		part_num = strtok(dev, ":");
+		if (part_num)
+			break;
+	}
+
+	fclose(f);
+
+	if (!part_num)
+		return -1;
+
+	/* mtdblockX emulates an mtd device as a block device.
+	 * Use mtdblockX instead of mtdX to avoid padding & buffer handling.
+	 */
+	snprintf(buf, sizeof(buf), "/dev/mtdblock%s", part_num + 3);
+
+	fd = open(buf, flags);
+
+	return fd;
+}
+
+int atenl_mmc_open(struct atenl *an, int flags)
+{
+	const char *mmc_dev = "/dev/mmcblk0";
+	int nparts, part_num;
+	blkid_partlist plist;
+	blkid_probe probe;
+	int i, fd = -1;
+	char buf[16];
+
+	probe = blkid_new_probe_from_filename(mmc_dev);
+	if (!probe)
+		return -1;
+
+	plist = blkid_probe_get_partitions(probe);
+	if (!plist)
+		goto out;
+
+	nparts = blkid_partlist_numof_partitions(plist);
+	if (!nparts)
+		goto out;
+
+	for (i = 0; i < nparts; i++) {
+		blkid_partition part;
+		const char *name;
+
+		part = blkid_partlist_get_partition(plist, i);
+		if (!part)
+			continue;
+
+		name = blkid_partition_get_name(part);
+		if (!name)
+			continue;
+
+		if (strncasecmp(name, an->flash_part, strlen(an->flash_part)))
+			continue;
+
+		part_num = blkid_partition_get_partno(part);
+		snprintf(buf, sizeof(buf), "%sp%d", mmc_dev, part_num);
+
+		fd = open(buf, flags);
+		if (fd >= 0)
+			break;
+	}
+
+out:
+	blkid_free_probe(probe);
+	return fd;
+}
+
+void atenl_flash_write(struct atenl *an, int fd, u32 size, bool is_mtd)
+{
+	u32 flash_size, offs;
+	int ret;
+
+	flash_size = lseek(fd, 0, SEEK_END);
+	if (size > flash_size)
+		return;
+
+	offs = an->flash_offset;
+	ret = lseek(fd, offs, SEEK_SET);
+	if (ret < 0)
+		return;
+
+	ret = write(fd, an->eeprom_data, size);
+	if (ret < 0)
+		return;
+
+	atenl_info("write to %s partition %s offset 0x%x size 0x%x\n",
+		   is_mtd ? "mtd" : "mmc", an->flash_part, offs, size);
+}
+
+int atenl_eeprom_write_flash(struct atenl *an)
+{
 	u32 size = an->eeprom_size;
-	u32 *precal_info = an->eeprom_data + an->eeprom_size;
-	u32 precal_size = precal_info[0] + precal_info[1];
-	char cmd[100];
+	u32 precal_size, *precal_info;
+	int fd;
 
-	if (an->mtd_part == NULL || !(~an->mtd_offset))
+	/* flash_offset = -1 for binfile mode */
+	if (an->flash_part == NULL || !(~an->flash_offset)) {
+		atenl_err("Flash partition or offset is not specified\n");
 		return 0;
+	}
+
+	precal_info = (u32 *)(an->eeprom_data + size);
+	precal_size = precal_info[0] + precal_info[1];
 
 	if (precal_size)
 		size += PRE_CAL_INFO + precal_size;
 
-	sprintf(cmd, "dd if=%s of=%s bs=1 count=%d", eeprom_file, TMP_FILE, size);
-	system(cmd);
+	fd = atenl_mtd_open(an, O_RDWR | O_SYNC);
+	if (fd >= 0) {
+		atenl_flash_write(an, fd, size, true);
+		goto out;
+	}
 
-	sprintf(cmd, "mtd -p %d write %s %s", an->mtd_offset, TMP_FILE, an->mtd_part);
-	system(cmd);
+	fd = atenl_mmc_open(an, O_RDWR | O_SYNC);
+	if (fd >= 0) {
+		atenl_flash_write(an, fd, size, false);
+		goto out;
+	}
 
-	sprintf(cmd, "rm %s", TMP_FILE);
-	system(cmd);
+	atenl_err("Fail to open %s\n", an->flash_part);
 
+out:
+	close(fd);
 	return 0;
 }
 
@@ -512,7 +630,7 @@
 	atenl_eeprom_init(an, phy_idx);
 
 	if (!strncmp(cmd, "sync eeprom all", 15)) {
-		atenl_eeprom_write_mtd(an);
+		atenl_eeprom_write_flash(an);
 	} else if (!strncmp(cmd, "eeprom", 6)) {
 		char *s = strchr(cmd, ' ');
 
@@ -526,9 +644,9 @@
 			unlink(eeprom_file);
 		} else if (!strncmp(s, "file", 4)) {
 			atenl_info("%s\n", eeprom_file);
-			if (an->mtd_part != NULL)
+			if (an->flash_part != NULL)
 				atenl_info("%s mode\n",
-					   ~an->mtd_offset == 0 ? "Binfile" : "Flash");
+					   ~an->flash_offset == 0 ? "Binfile" : "Flash");
 			else
 				atenl_info("Efuse / Default bin mode\n");
 		} else if (!strncmp(s, "set", 3)) {
@@ -555,7 +673,7 @@
 			s++;
 
 			if (!strncmp(s, "flash", 5)) {
-				atenl_eeprom_write_mtd(an);
+				atenl_eeprom_write_flash(an);
 			} else if (!strncmp(s, "to efuse", 8)) {
 				atenl_eeprom_sync_to_driver(an);
 				atenl_nl_write_efuse_all(an);
diff --git a/feed/app/atenl/src/hqa.c b/feed/app/atenl/src/hqa.c
index 2f25043..678bb93 100644
--- a/feed/app/atenl/src/hqa.c
+++ b/feed/app/atenl/src/hqa.c
@@ -480,7 +480,7 @@
 atenl_hqa_check_efuse_mode(struct atenl *an, struct atenl_data *data)
 {
 	struct atenl_cmd_hdr *hdr = atenl_hdr(data);
-	bool flash_mode = an->mtd_part != NULL;
+	bool flash_mode = an->flash_part != NULL;
 	enum atenl_cmd cmd = data->cmd;
 	u32 mode;
 
diff --git a/feed/app/atenl/src/main.c b/feed/app/atenl/src/main.c
index ef13245..0dbcb41 100644
--- a/feed/app/atenl/src/main.c
+++ b/feed/app/atenl/src/main.c
@@ -87,7 +87,9 @@
 	       "  -h = show help text\n"
 	       "  -i = phy name of driver interface, please use first phy for dbdc\n"
 	       "  -u = use unicast to respond to HQADLL\n"
-	       "  -b = specify your bridge name\n");
+	       "  -b = specify your bridge name\n"
+	       "  -c = eeprom-related command\n"
+	       "  -p = specify the flash partition name and offset (<name>:<offs>)\n");
 	printf("examples:\n"
 	       "  %s -u -i phy0 -b br-lan\n", progname);
 
@@ -124,6 +126,7 @@
 {
 	int opt, phy_idx, ret = 0;
 	char *phy = "phy0", *cmd = NULL;
+	char *token;
 	struct atenl *an;
 
 	progname = argv[0];
@@ -133,7 +136,7 @@
 		return -ENOMEM;
 
 	while(1) {
-		opt = getopt(argc, argv, "hi:uc:b:");
+		opt = getopt(argc, argv, "hi:uc:b:p:");
 		if (opt == -1)
 			break;
 
@@ -154,6 +157,13 @@
 			case 'c':
 				cmd = optarg;
 				break;
+			case 'p':
+				token = strtok(optarg, ":");
+				if (!token)
+					break;
+				an->flash_part = token;
+				an->flash_offset = strtol(strtok(NULL, ":"), NULL, 0);
+				break;
 			default:
 				atenl_err("Not supported option: %c\n", opt);
 				goto out;
diff --git a/feed/app/atenl/src/nl.c b/feed/app/atenl/src/nl.c
index 7796ba9..e019979 100644
--- a/feed/app/atenl/src/nl.c
+++ b/feed/app/atenl/src/nl.c
@@ -1186,7 +1186,7 @@
 	return 0;
 }
 
-static int atenl_nl_check_mtd_cb(struct nl_msg *msg, void *arg)
+static int atenl_nl_check_flash_cb(struct nl_msg *msg, void *arg)
 {
 	struct atenl_nl_priv *nl_priv = (struct atenl_nl_priv *)arg;
 	struct atenl *an = nl_priv->an;
@@ -1203,13 +1203,13 @@
 	if (!tb[MT76_TM_ATTR_MTD_PART] || !tb[MT76_TM_ATTR_MTD_OFFSET])
 		return NL_SKIP;
 
-	an->mtd_part = strdup(nla_get_string(tb[MT76_TM_ATTR_MTD_PART]));
-	an->mtd_offset = nla_get_u32(tb[MT76_TM_ATTR_MTD_OFFSET]);
+	an->flash_part = strdup(nla_get_string(tb[MT76_TM_ATTR_MTD_PART]));
+	an->flash_offset = nla_get_u32(tb[MT76_TM_ATTR_MTD_OFFSET]);
 
 	return NL_SKIP;
 }
 
-int atenl_nl_check_mtd(struct atenl *an)
+int atenl_nl_check_flash(struct atenl *an)
 {
 	struct atenl_nl_priv nl_priv = { .an = an };
 	struct nl_msg *msg;
@@ -1219,9 +1219,14 @@
 		return 2;
 	}
 
+	/* User has a specified flash partition */
+	if (an->flash_part)
+		return 0;
+
 	msg = unl_genl_msg(&nl_priv.unl, NL80211_CMD_TESTMODE, true);
 	nla_put_u32(msg, NL80211_ATTR_WIPHY, get_band_val(an, 0, phy_idx));
-	unl_genl_request(&nl_priv.unl, msg, atenl_nl_check_mtd_cb, (void *)&nl_priv);
+	unl_genl_request(&nl_priv.unl, msg, atenl_nl_check_flash_cb,
+			 (void *)&nl_priv);
 
 	unl_free(&nl_priv.unl);