|
@@ -39,7 +39,7 @@
|
|
|
|
|
|
#include "routerboot.h"
|
|
#include "routerboot.h"
|
|
|
|
|
|
-#define RB_HARDCONFIG_VER "0.05"
|
|
|
|
|
|
+#define RB_HARDCONFIG_VER "0.06"
|
|
#define RB_HC_PR_PFX "[rb_hardconfig] "
|
|
#define RB_HC_PR_PFX "[rb_hardconfig] "
|
|
|
|
|
|
/* ID values for hardware settings */
|
|
/* ID values for hardware settings */
|
|
@@ -76,6 +76,17 @@
|
|
#define RB_HW_OPT_HAS_TS_FOR_ADC BIT(22)
|
|
#define RB_HW_OPT_HAS_TS_FOR_ADC BIT(22)
|
|
#define RB_HW_OPT_HAS_PLC BIT(29)
|
|
#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 struct kobject *hc_kobj;
|
|
static u8 *hc_buf; // ro buffer after init(): no locking required
|
|
static u8 *hc_buf; // ro buffer after init(): no locking required
|
|
static size_t hc_buflen;
|
|
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);
|
|
loff_t off, size_t count);
|
|
|
|
|
|
static struct hc_wlan_attr {
|
|
static struct hc_wlan_attr {
|
|
|
|
+ const u16 erd_tag_id;
|
|
struct bin_attribute battr;
|
|
struct bin_attribute battr;
|
|
u16 pld_ofs;
|
|
u16 pld_ofs;
|
|
u16 pld_len;
|
|
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),
|
|
.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
|
|
* 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
|
|
* 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)
|
|
void *outbuf, size_t *outlen)
|
|
{
|
|
{
|
|
u16 lzo_ofs, lzo_len;
|
|
u16 lzo_ofs, lzo_len;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
/* Find embedded tag */
|
|
/* 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) {
|
|
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;
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -461,10 +484,10 @@ fail:
|
|
* that magic number is a payload that must be appended to the hc_lzor_prefix,
|
|
* 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 resulting blob is LZO-compressed. In the LZO decompression result,
|
|
* the RB_MAGIC_ERD magic number (aligned) must be located. Following that
|
|
* 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.
|
|
* 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)
|
|
void *outbuf, size_t *outlen)
|
|
{
|
|
{
|
|
u16 rle_ofs, rle_len;
|
|
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 (ret) {
|
|
if (LZO_E_INPUT_NOT_CONSUMED == 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");
|
|
pr_debug(RB_HC_PR_PFX "LZOR: LZO EOF before buffer end - this may be harmless\n");
|
|
} else {
|
|
} else {
|
|
@@ -520,9 +541,9 @@ static int hc_wlan_data_unpack_lzor(const u8 *inbuf, size_t inlen,
|
|
templen -= (u8 *)needle - tempbuf;
|
|
templen -= (u8 *)needle - tempbuf;
|
|
|
|
|
|
/* Past magic. Look for tag node */
|
|
/* 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) {
|
|
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;
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -542,7 +563,7 @@ fail:
|
|
return ret;
|
|
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)
|
|
void *outbuf, size_t *outlen)
|
|
{
|
|
{
|
|
const u8 *lbuf;
|
|
const u8 *lbuf;
|
|
@@ -562,23 +583,25 @@ static int hc_wlan_data_unpack(const size_t tofs, size_t tlen,
|
|
/* Skip magic */
|
|
/* Skip magic */
|
|
lbuf += sizeof(magic);
|
|
lbuf += sizeof(magic);
|
|
tlen -= 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;
|
|
break;
|
|
case RB_MAGIC_ERD:
|
|
case RB_MAGIC_ERD:
|
|
/* Skip magic */
|
|
/* Skip magic */
|
|
lbuf += sizeof(magic);
|
|
lbuf += sizeof(magic);
|
|
tlen -= 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;
|
|
break;
|
|
default:
|
|
default:
|
|
/*
|
|
/*
|
|
* If the RB_ID_WLAN_DATA payload doesn't start with a
|
|
* If the RB_ID_WLAN_DATA payload doesn't start with a
|
|
* magic number, the payload itself is the raw RLE-encoded
|
|
* 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;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -633,7 +656,7 @@ static ssize_t hc_wlan_data_bin_read(struct file *filp, struct kobject *kobj,
|
|
if (!outbuf)
|
|
if (!outbuf)
|
|
return -ENOMEM;
|
|
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) {
|
|
if (ret) {
|
|
kfree(outbuf);
|
|
kfree(outbuf);
|
|
return ret;
|
|
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)
|
|
int __init rb_hardconfig_init(struct kobject *rb_kobj)
|
|
{
|
|
{
|
|
|
|
+ struct kobject *hc_wlan_kobj;
|
|
struct mtd_info *mtd;
|
|
struct mtd_info *mtd;
|
|
- size_t bytes_read, buflen;
|
|
|
|
|
|
+ size_t bytes_read, buflen, outlen;
|
|
const u8 *buf;
|
|
const u8 *buf;
|
|
- int i, ret;
|
|
|
|
|
|
+ void *outbuf;
|
|
|
|
+ int i, j, ret;
|
|
u32 magic;
|
|
u32 magic;
|
|
|
|
|
|
hc_buf = NULL;
|
|
hc_buf = NULL;
|
|
hc_kobj = NULL;
|
|
hc_kobj = NULL;
|
|
|
|
+ hc_wlan_kobj = NULL;
|
|
|
|
|
|
// TODO allow override
|
|
// TODO allow override
|
|
mtd = get_mtd_device_nm(RB_MTD_HARD_CONFIG);
|
|
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 */
|
|
/* Account for skipped magic */
|
|
hc_attrs[i].pld_ofs += sizeof(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) {
|
|
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 */
|
|
/* All other tags are published via standard attributes */
|
|
else {
|
|
else {
|