2 * RouterBoot helper routines
4 * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org>
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published
8 * by the Free Software Foundation.
11 #define pr_fmt(fmt) "rb: " fmt
13 #include <linux/kernel.h>
14 #include <linux/kobject.h>
15 #include <linux/slab.h>
16 #include <linux/errno.h>
17 #include <linux/routerboot.h>
18 #include <linux/rle.h>
19 #include <linux/lzo.h>
21 #include "routerboot.h"
23 #define RB_BLOCK_SIZE 0x1000
24 #define RB_ART_SIZE 0x10000
25 #define RB_MAGIC_ERD 0x00455244 /* extended radio data */
27 static struct rb_info rb_info
;
29 static u32
get_u32(void *buf
)
33 return ((u32
) p
[3] + ((u32
) p
[2] << 8) + ((u32
) p
[1] << 16) +
37 static u16
get_u16(void *buf
)
41 return (u16
) p
[1] + ((u16
) p
[0] << 8);
45 routerboot_find_magic(u8
*buf
, unsigned int buflen
, u32
*offset
, bool hard
)
47 u32 magic_ref
= hard
? RB_MAGIC_HARD
: RB_MAGIC_SOFT
;
51 while (cur
< buflen
) {
52 magic
= get_u32(buf
+ cur
);
53 if (magic
== magic_ref
) {
65 routerboot_find_tag(u8
*buf
, unsigned int buflen
, u16 tag_id
,
66 u8
**tag_data
, u16
*tag_len
)
81 /* skip magic value */
90 /* skip magic and CRC value */
116 if (id
== RB_ID_TERMINATOR
)
142 rb_find_hard_cfg_tag(u16 tag_id
, u8
**tag_data
, u16
*tag_len
)
144 if (!rb_info
.hard_cfg_data
||
145 !rb_info
.hard_cfg_size
)
148 return routerboot_find_tag(rb_info
.hard_cfg_data
,
149 rb_info
.hard_cfg_size
,
150 tag_id
, tag_data
, tag_len
);
154 rb_get_board_name(void)
160 err
= rb_find_hard_cfg_tag(RB_ID_BOARD_NAME
, &tag
, &tag_len
);
168 rb_get_hw_options(void)
174 err
= rb_find_hard_cfg_tag(RB_ID_HW_OPTIONS
, &tag
, &tag_len
);
182 __rb_get_wlan_data(u16 id
)
192 err
= rb_find_hard_cfg_tag(RB_ID_WLAN_DATA
, &tag
, &tag_len
);
194 pr_err("no calibration data found\n");
198 buf
= kmalloc(RB_ART_SIZE
, GFP_KERNEL
);
200 pr_err("no memory for calibration data\n");
204 magic
= get_u32(tag
);
205 if (magic
== RB_MAGIC_ERD
) {
212 err
= routerboot_find_tag(tag
, tag_len
, id
,
213 &erd_data
, &erd_len
);
215 pr_err("no ERD data found for id %u\n", id
);
219 dst_done
= RB_ART_SIZE
;
220 err
= lzo1x_decompress_safe(erd_data
, erd_len
, buf
, &dst_done
);
222 pr_err("unable to decompress calibration data %d\n",
230 err
= rle_decode((char *) tag
, tag_len
, buf
, RB_ART_SIZE
,
231 &src_done
, &dst_done
);
233 pr_err("unable to decode calibration data\n");
247 rb_get_wlan_data(void)
249 return __rb_get_wlan_data(0);
253 rb_get_ext_wlan_data(u16 id
)
255 return __rb_get_wlan_data(id
);
258 __init
const struct rb_info
*
259 rb_init_info(void *data
, unsigned int size
)
263 if (size
== 0 || (size
% RB_BLOCK_SIZE
) != 0)
266 for (offset
= 0; offset
< size
; offset
+= RB_BLOCK_SIZE
) {
269 magic
= get_u32(data
+ offset
);
272 rb_info
.hard_cfg_offs
= offset
;
276 rb_info
.soft_cfg_offs
= offset
;
281 if (!rb_info
.hard_cfg_offs
) {
282 pr_err("could not find a valid RouterBOOT hard config\n");
286 if (!rb_info
.soft_cfg_offs
) {
287 pr_err("could not find a valid RouterBOOT soft config\n");
291 rb_info
.hard_cfg_size
= RB_BLOCK_SIZE
;
292 rb_info
.hard_cfg_data
= kmemdup(data
+ rb_info
.hard_cfg_offs
,
293 RB_BLOCK_SIZE
, GFP_KERNEL
);
294 if (!rb_info
.hard_cfg_data
)
297 rb_info
.board_name
= rb_get_board_name();
298 rb_info
.hw_options
= rb_get_hw_options();
303 static char *rb_ext_wlan_data
;
306 rb_ext_wlan_data_read(struct file
*filp
, struct kobject
*kobj
,
307 struct bin_attribute
*attr
, char *buf
,
308 loff_t off
, size_t count
)
310 if (off
+ count
> attr
->size
)
313 memcpy(buf
, &rb_ext_wlan_data
[off
], count
);
318 static const struct bin_attribute rb_ext_wlan_data_attr
= {
320 .name
= "ext_wlan_data",
321 .mode
= S_IRUSR
| S_IWUSR
,
323 .read
= rb_ext_wlan_data_read
,
327 static int __init
rb_sysfs_init(void)
329 struct kobject
*rb_kobj
;
332 rb_ext_wlan_data
= rb_get_ext_wlan_data(1);
333 if (rb_ext_wlan_data
== NULL
)
336 rb_kobj
= kobject_create_and_add("routerboot", firmware_kobj
);
337 if (rb_kobj
== NULL
) {
339 pr_err("unable to create sysfs entry\n");
340 goto err_free_wlan_data
;
343 ret
= sysfs_create_bin_file(rb_kobj
, &rb_ext_wlan_data_attr
);
345 pr_err("unable to create sysfs file, %d\n", ret
);
352 kobject_put(rb_kobj
);
354 kfree(rb_ext_wlan_data
);
358 late_initcall(rb_sysfs_init
);