#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 */
#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;
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_battrs = {
+ .erd_tag_id = RB_WLAN_ERD_ID_SOLO,
.battr = __BIN_ATTR(wlan_data, S_IRUSR, hc_wlan_data_bin_read, NULL, 0),
};
/*
* 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 only uses
+ * 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;
}
* 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;
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 {
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;
}
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;
/* 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;
}
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;
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);
/* 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_battrs.pld_ofs = hc_attrs[i].pld_ofs;
+ hc_wd_solo_battrs.pld_len = hc_attrs[i].pld_len;
+
+ ret = sysfs_create_bin_file(hc_kobj, &hc_wd_solo_battrs.battr);
+ if (ret)
+ pr_warn(RB_HC_PR_PFX "Could not create %s sysfs entry (%d)\n",
+ hc_wd_solo_battrs.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 {