From patchwork Mon Aug 24 10:38:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Thibaut X-Patchwork-Id: 1350231 X-Patchwork-Delegate: ynezz@true.cz Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.openwrt.org (client-ip=2001:8b0:10b:1231::1; helo=merlin.infradead.org; envelope-from=openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=quarantine dis=none) header.from=slashdirt.org Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; secure) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=merlin.20170209 header.b=Xo8mr+Au; dkim=fail reason="signature verification failed" (1024-bit key; secure) header.d=slashdirt.org header.i=@slashdirt.org header.a=rsa-sha256 header.s=mail header.b=eI6d0VGT; dkim-atps=neutral Received: from merlin.infradead.org (merlin.infradead.org [IPv6:2001:8b0:10b:1231::1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4BZpYj4kL5z9sRK for ; Mon, 24 Aug 2020 20:40:41 +1000 (AEST) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=merlin.20170209; h=Sender:Content-Transfer-Encoding: Content-Type:Cc:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:Message-Id:Date:Subject:To:From: Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender :Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:List-Owner; bh=Kz55jgBBRcHvXgR6Pwcj7VKJvQVqIy7E0njsmA/Y5gk=; b=Xo8mr+Au4UKj7ZaUvM8aka3KJu GKfWipO5aau6TfW5iqpN9MFTwYVrZs5xLfWmWvyv9sxPHZi2F8ClSPS+BCvo3JM0wbB9pJk0LouF+ Vu8SbqDJLMTZrZQVg3nrcJmd6oksfLH6+rt2ZDRmX9eIuuliN7L5tlNEF+3YO3i6yAD48uqQsRK0x G0IIoTi1Quvejs65mQvDV11wb6PGOHthEo9h2sfzYHn+yygttihFeXZ1wsRks1yFoRJK6q1/GbprP ZLvCSYmGVNJXEjfjvs6nEFmW8jOgJqgAfDvwhTs3P6tAw/hZ0+l2tWGCNOp5dpx0TSwbOvhIkuuL7 3xYtOBBw==; Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.92.3 #3 (Red Hat Linux)) id 1kA9sU-0002oA-9I; Mon, 24 Aug 2020 10:39:02 +0000 Received: from vps.slashdirt.org ([144.91.108.218]) by merlin.infradead.org with esmtps (Exim 4.92.3 #3 (Red Hat Linux)) id 1kA9sR-0002mA-2w for openwrt-devel@lists.openwrt.org; Mon, 24 Aug 2020 10:39:00 +0000 Received: from Chuck.tardis.lan (tardis.herebedragons.eu [171.22.3.161]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by vps.slashdirt.org (Postfix) with ESMTPSA id 9DB84600F5; Mon, 24 Aug 2020 12:38:45 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 vps.slashdirt.org 9DB84600F5 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=slashdirt.org; s=mail; t=1598265527; bh=E3K75dWoA0P0AhqahxNCGSycPdr70rLYzOaZeCHNFBQ=; h=From:To:Cc:Subject:Date:From; b=eI6d0VGTshk4V8/9MozBNQKwFNmP0BtOwPEtmVx8VLUAPBo08bxhbCqCVu+dPYpUM V/mAOB5mQhnQlHodHTS1orJ/dkNJ9vGoya0vSUM4nRyfCtlIOULmcbuKafr9Fr5LrJ JUxRzju576hp5L2JMA/sG0bjHZq1LAz7oOwDs6f8= From: =?utf-8?q?Thibaut_VAR=C3=88NE?= To: openwrt-devel@lists.openwrt.org Subject: [PATCH] generic: platform/mikrotik: implement multi caldata Date: Mon, 24 Aug 2020 12:38:40 +0200 Message-Id: <20200824103840.38920-1-hacks@slashdirt.org> X-Mailer: git-send-email 2.24.3 (Apple Git-128) MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20200824_063859_538246_D5737EBB X-CRM114-Status: GOOD ( 30.74 ) X-Spam-Score: 0.2 (/) X-Spam-Report: SpamAssassin version 3.4.4 on merlin.infradead.org summary: Content analysis details: (0.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- 0.4 NO_DNS_FOR_FROM RBL: Envelope sender has no MX or A DNS records [listed in slashdirt.org. IN A] 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature X-BeenThere: openwrt-devel@lists.openwrt.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: OpenWrt Development List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: =?utf-8?b?QWxlbiBPcGHEjWnEhw==?= , =?utf-8?q?Thib?= =?utf-8?q?aut_VAR=C3=88NE?= , =?utf-8?b?0KjQtdCx0LA=?= =?utf-8?b?0L3QvtCyINCQ0LvQtdC60YHQtdC5?= , John Thomson Sender: "openwrt-devel" Errors-To: openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org MikroTik recently changed again the way they store wlan calibration data on devices. Prior to this change, ERD calibration data for all available radios was stored within a single identifier node ("tag" in RouterBoot parlance). Recent devices have been seen with calibration (and BDF) data stored in separate identifiers within LZOR packing for each radio: this patch addresses this by: 1) ensuring that both variants are properly supported, 2) preserving backward compatibility with existing data consumers, 3) allowing for more than 2 calibration blobs to be exposed via sysfs. Specifically, before this patch, the driver would provide a single sysfs file named /sys/firmware/mikrotik/hard_config/wlan_data that contained whatever calibration data found on the device's flash. After this patch, when executed on a device that uses the old style storage, this behavior is unchanged, but when executed on a device that uses new style storage (for either traditional "ERD" packing or "LZOR" packing), the driver replaces that single file with a folder containing one or more files each containing the data encoded within individual identifiers. As far as OpenWRT is concerned, this means that for devices which are known to exist with both styles of data storage, a suitable hotplug stub could look like this for e.g. the second radio: wdata="/sys/firmware/mikrotik/hard_config/wlan_data" ( [ -f "$wdata" ] && caldata_sysfsload_from_file "$wdata" 0x8000 0x2f20 ) || \ ( [ -d "$wdata" ] && caldata_sysfsload_from_file "$wdata/data_2" 0x0 0x2f20 ) This patch has been tested with LZOR old and new style packing on ipq4019, and with old style on ath79. Tested-by: John Thomson Tested-by: Шебанов Алексей Tested-by: Alen Opačić Signed-off-by: Thibaut VARÈNE Tested-by: Robert Marko --- .../drivers/platform/mikrotik/rb_hardconfig.c | 139 +++++++++++++----- 1 file changed, 106 insertions(+), 33 deletions(-) diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c index 8861814be4..41dea98b5e 100644 --- a/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c +++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c @@ -39,7 +39,7 @@ #include "routerboot.h" -#define RB_HARDCONFIG_VER "0.05" +#define RB_HARDCONFIG_VER "0.06" #define RB_HC_PR_PFX "[rb_hardconfig] " /* ID values for hardware settings */ @@ -76,6 +76,17 @@ #define RB_HW_OPT_HAS_TS_FOR_ADC BIT(22) #define RB_HW_OPT_HAS_PLC BIT(29) +/* + * Tag ID values for ERD data. + * Mikrotik used to pack all calibration data under a single tag id 0x1, but + * recently switched to a new scheme where each radio calibration gets a + * separate tag. The new scheme has tag id bit 15 always set and seems to be + * mutually exclusive with the old scheme. + */ +#define RB_WLAN_ERD_ID_SOLO 0x0001 +#define RB_WLAN_ERD_ID_MULTI_8001 0x8001 +#define RB_WLAN_ERD_ID_MULTI_8201 0x8201 + static struct kobject *hc_kobj; static u8 *hc_buf; // ro buffer after init(): no locking required static size_t hc_buflen; @@ -351,10 +362,22 @@ static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj, loff_t off, size_t count); static struct hc_wlan_attr { + const u16 erd_tag_id; struct bin_attribute battr; u16 pld_ofs; u16 pld_len; -} hc_wlandata_battr = { +} hc_wd_multi_battrs[] = { + { + .erd_tag_id = RB_WLAN_ERD_ID_MULTI_8001, + .battr = __BIN_ATTR(data_0, S_IRUSR, hc_wlan_data_bin_read, NULL, 0), + }, { + .erd_tag_id = RB_WLAN_ERD_ID_MULTI_8201, + .battr = __BIN_ATTR(data_2, S_IRUSR, hc_wlan_data_bin_read, NULL, 0), + } +}; + +static struct hc_wlan_attr hc_wd_solo_battr = { + .erd_tag_id = RB_WLAN_ERD_ID_SOLO, .battr = __BIN_ATTR(wlan_data, S_IRUSR, hc_wlan_data_bin_read, NULL, 0), }; @@ -426,19 +449,19 @@ static struct hc_attr { /* * If the RB_ID_WLAN_DATA payload starts with RB_MAGIC_ERD, then past * that magic number the payload itself contains a routerboot tag node - * locating the LZO-compressed calibration data at id 0x1. + * locating the LZO-compressed calibration data. So far this scheme is only + * known to use a single tag at id 0x1. */ -static int hc_wlan_data_unpack_erd(const u8 *inbuf, size_t inlen, +static int hc_wlan_data_unpack_erd(const u16 tag_id, const u8 *inbuf, size_t inlen, void *outbuf, size_t *outlen) { u16 lzo_ofs, lzo_len; int ret; /* Find embedded tag */ - ret = routerboot_tag_find(inbuf, inlen, 0x1, // always id 1 - &lzo_ofs, &lzo_len); + ret = routerboot_tag_find(inbuf, inlen, tag_id, &lzo_ofs, &lzo_len); if (ret) { - pr_debug(RB_HC_PR_PFX "ERD data not found\n"); + pr_debug(RB_HC_PR_PFX "no ERD data for id 0x%04x\n", tag_id); goto fail; } @@ -461,10 +484,10 @@ fail: * that magic number is a payload that must be appended to the hc_lzor_prefix, * the resulting blob is LZO-compressed. In the LZO decompression result, * the RB_MAGIC_ERD magic number (aligned) must be located. Following that - * magic, there is a routerboot tag node (id 0x1) locating the RLE-encoded + * magic, there is one or more routerboot tag node(s) locating the RLE-encoded * calibration data payload. */ -static int hc_wlan_data_unpack_lzor(const u8 *inbuf, size_t inlen, +static int hc_wlan_data_unpack_lzor(const u16 tag_id, const u8 *inbuf, size_t inlen, void *outbuf, size_t *outlen) { u16 rle_ofs, rle_len; @@ -492,10 +515,8 @@ static int hc_wlan_data_unpack_lzor(const u8 *inbuf, size_t inlen, if (ret) { if (LZO_E_INPUT_NOT_CONSUMED == ret) { /* - * The tag length appears to always be aligned (probably - * because it is the "root" RB_ID_WLAN_DATA tag), thus - * the LZO payload may be padded, which can trigger a - * spurious error which we ignore here. + * The tag length is always aligned thus the LZO payload may be padded, + * which can trigger a spurious error which we ignore here. */ pr_debug(RB_HC_PR_PFX "LZOR: LZO EOF before buffer end - this may be harmless\n"); } else { @@ -520,9 +541,9 @@ static int hc_wlan_data_unpack_lzor(const u8 *inbuf, size_t inlen, templen -= (u8 *)needle - tempbuf; /* Past magic. Look for tag node */ - ret = routerboot_tag_find((u8 *)needle, templen, 0x1, &rle_ofs, &rle_len); + ret = routerboot_tag_find((u8 *)needle, templen, tag_id, &rle_ofs, &rle_len); if (ret) { - pr_debug(RB_HC_PR_PFX "LZOR: RLE data not found\n"); + pr_debug(RB_HC_PR_PFX "LZOR: no RLE data for id 0x%04x\n", tag_id); goto fail; } @@ -542,7 +563,7 @@ fail: return ret; } -static int hc_wlan_data_unpack(const size_t tofs, size_t tlen, +static int hc_wlan_data_unpack(const u16 tag_id, const size_t tofs, size_t tlen, void *outbuf, size_t *outlen) { const u8 *lbuf; @@ -562,23 +583,25 @@ static int hc_wlan_data_unpack(const size_t tofs, size_t tlen, /* Skip magic */ lbuf += sizeof(magic); tlen -= sizeof(magic); - ret = hc_wlan_data_unpack_lzor(lbuf, tlen, outbuf, outlen); + ret = hc_wlan_data_unpack_lzor(tag_id, lbuf, tlen, outbuf, outlen); break; case RB_MAGIC_ERD: /* Skip magic */ lbuf += sizeof(magic); tlen -= sizeof(magic); - ret = hc_wlan_data_unpack_erd(lbuf, tlen, outbuf, outlen); + ret = hc_wlan_data_unpack_erd(tag_id, lbuf, tlen, outbuf, outlen); break; default: /* * If the RB_ID_WLAN_DATA payload doesn't start with a * magic number, the payload itself is the raw RLE-encoded - * calibration data. + * calibration data. Only RB_WLAN_ERD_ID_SOLO makes sense here. */ - ret = routerboot_rle_decode(lbuf, tlen, outbuf, outlen); - if (ret) - pr_debug(RB_HC_PR_PFX "RLE decoding error (%d)\n", ret); + if (RB_WLAN_ERD_ID_SOLO == tag_id) { + ret = routerboot_rle_decode(lbuf, tlen, outbuf, outlen); + if (ret) + pr_debug(RB_HC_PR_PFX "RLE decoding error (%d)\n", ret); + } break; } @@ -633,7 +656,7 @@ static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj, if (!outbuf) return -ENOMEM; - ret = hc_wlan_data_unpack(hc_wattr->pld_ofs, hc_wattr->pld_len, outbuf, &outlen); + ret = hc_wlan_data_unpack(hc_wattr->erd_tag_id, hc_wattr->pld_ofs, hc_wattr->pld_len, outbuf, &outlen); if (ret) { kfree(outbuf); return ret; @@ -655,14 +678,17 @@ static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj, int __init rb_hardconfig_init(struct kobject *rb_kobj) { + struct kobject *hc_wlan_kobj; struct mtd_info *mtd; - size_t bytes_read, buflen; + size_t bytes_read, buflen, outlen; const u8 *buf; - int i, ret; + void *outbuf; + int i, j, ret; u32 magic; hc_buf = NULL; hc_kobj = NULL; + hc_wlan_kobj = NULL; // TODO allow override mtd = get_mtd_device_nm(RB_MTD_HARD_CONFIG); @@ -713,15 +739,62 @@ int __init rb_hardconfig_init(struct kobject *rb_kobj) /* Account for skipped magic */ hc_attrs[i].pld_ofs += sizeof(magic); - /* Special case RB_ID_WLAN_DATA to prep and create the binary attribute */ + /* + * Special case RB_ID_WLAN_DATA to prep and create the binary attribute. + * We first check if the data is "old style" within a single tag (or no tag at all): + * If it is we publish this single blob as a binary attribute child of hc_kobj to + * preserve backward compatibility. + * If it isn't and instead uses multiple ERD tags, we create a subfolder and + * publish the known ones there. + */ if ((RB_ID_WLAN_DATA == hc_attrs[i].tag_id) && hc_attrs[i].pld_len) { - hc_wlandata_battr.pld_ofs = hc_attrs[i].pld_ofs; - hc_wlandata_battr.pld_len = hc_attrs[i].pld_len; - - ret = sysfs_create_bin_file(hc_kobj, &hc_wlandata_battr.battr); - if (ret) - pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n", - hc_wlandata_battr.battr.attr.name, ret); + outlen = RB_ART_SIZE; + outbuf = kmalloc(outlen, GFP_KERNEL); + if (!outbuf) { + pr_warn(RB_HC_PR_PFX "Out of memory parsing WLAN tag\n"); + continue; + } + + /* Test ID_SOLO first, if found: done */ + ret = hc_wlan_data_unpack(RB_WLAN_ERD_ID_SOLO, hc_attrs[i].pld_ofs, hc_attrs[i].pld_len, outbuf, &outlen); + if (!ret) { + hc_wd_solo_battr.pld_ofs = hc_attrs[i].pld_ofs; + hc_wd_solo_battr.pld_len = hc_attrs[i].pld_len; + + ret = sysfs_create_bin_file(hc_kobj, &hc_wd_solo_battr.battr); + if (ret) + pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n", + hc_wd_solo_battr.battr.attr.name, ret); + } + /* Otherwise, create "wlan_data" subtree and publish known data */ + else { + hc_wlan_kobj = kobject_create_and_add("wlan_data", hc_kobj); + if (!hc_wlan_kobj) { + kfree(outbuf); + pr_warn(RB_HC_PR_PFX "Could not create wlan_data sysfs folder\n"); + continue; + } + + for (j = 0; j < ARRAY_SIZE(hc_wd_multi_battrs); j++) { + outlen = RB_ART_SIZE; + ret = hc_wlan_data_unpack(hc_wd_multi_battrs[j].erd_tag_id, + hc_attrs[i].pld_ofs, hc_attrs[i].pld_len, outbuf, &outlen); + if (ret) { + hc_wd_multi_battrs[j].pld_ofs = hc_wd_multi_battrs[j].pld_len = 0; + continue; + } + + hc_wd_multi_battrs[j].pld_ofs = hc_attrs[i].pld_ofs; + hc_wd_multi_battrs[j].pld_len = hc_attrs[i].pld_len; + + ret = sysfs_create_bin_file(hc_wlan_kobj, &hc_wd_multi_battrs[j].battr); + if (ret) + pr_warn(RB_HC_PR_PFX "Could not create wlan_data/%s sysfs entry (%d)\n", + hc_wd_multi_battrs[j].battr.attr.name, ret); + } + } + + kfree(outbuf); } /* All other tags are published via standard attributes */ else {