8dd79199f8
The bes2600 driver is a fork of the upstream cw1200 driver
(drivers/net/wireless/st/cw1200/, ST-Ericsson, Dmitry Tarnyagin
2010-2011). The fork's file headers have three GPL-compliance issues:
1. NO SPDX-License-Identifier on any of 48 source files (cw1200
mainline has them on all 25). kernel.org-mandated since 2017.
2. Original "Copyright (c) 2010, ST-Ericsson" lines stripped from
all files inherited from cw1200, replaced with
"Copyright (c) 2010, Bestechnic" — factually impossible
(Bestechnic did not author the 2010 work) and a GPL-2.0 §1
attribution-preservation violation.
3. The "GPL version 2 as published by the Free Software Foundation"
boilerplate paragraph is redundant alongside SPDX and is the
legacy form modern kernel sources have replaced.
This patch corrects all three for the 48 .c/.h files in bes2600/:
- Adds `// SPDX-License-Identifier: GPL-2.0-only` (or `/* ... */`
for headers) as line 1 of every file.
- Restores `Copyright (c) 2010, ST-Ericsson` + `Author: Dmitry
Tarnyagin <dmitry.tarnyagin@lockless.no>` as the FIRST copyright
chain entry on all 22 files derived from cw1200 (bh.{c,h},
debug.{c,h}, fwio.{c,h}, hwio.{c,h}, main.c, pm.{c,h},
queue.{c,h}, scan.{c,h}, sta.{c,h}, txrx.{c,h}, wsm.{c,h}).
- Keeps `Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.` as
the SECOND chain entry where Bestechnic genuinely contributed.
- Notes "Derived from cw1200_sdio.c" + ST-Ericsson copyright on
bes2600_sdio.c (heavy derivation, not a literal rename).
- Notes "Replaces hwbus.h from cw1200/" + ST-Ericsson copyright
on sbus.h.
- Preserves the prism54/islsm authorship chain on main.c and
bes2600.h (Michael Wu 2006 + Jean-Baptiste Note 2004-2006).
- Drops the GPL-2.0 boilerplate paragraph in favour of SPDX.
No code changes — only file-header comment blocks. Module build is
unaffected (verified by header-only diff scope).
This is a prerequisite for any kernel.org submission attempt. The
existing MODULE_LICENSE("GPL") + MODULE_AUTHOR(Tarnyagin@stericsson.com)
declarations were already present and are unchanged here; the
mismatch between MODULE_AUTHOR and the (since-corrected) per-file
copyrights is now resolved.
975 lines
24 KiB
C
975 lines
24 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Factory calibration loader for BES2600
|
|
*
|
|
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/crc32.h>
|
|
#include <linux/version.h>
|
|
#include "bes2600_factory.h"
|
|
#include "bes_chardev.h"
|
|
#include "bes_log.h"
|
|
|
|
#define LE_CPU_TRANS(val, cvt) (val = cvt(val))
|
|
|
|
#define TRANS_LE16(val) LE_CPU_TRANS(val, __cpu_to_le16)
|
|
#define TRANS_LE32(val) LE_CPU_TRANS(val, __cpu_to_le32)
|
|
|
|
#define TRANS_CPU16(val) LE_CPU_TRANS(val, __le16_to_cpu)
|
|
#define TRANS_CPU32(val) LE_CPU_TRANS(val, __le32_to_cpu)
|
|
|
|
static DEFINE_MUTEX(factory_lock);
|
|
|
|
/*
|
|
* struct device * for request_firmware() context. Set once at SDIO
|
|
* probe via bes2600_factory_set_dev(). NULL is tolerated (falls back
|
|
* to the udev-less firmware-class path) but loses per-device logging.
|
|
*/
|
|
static struct device *bes2600_factory_dev;
|
|
|
|
void bes2600_factory_set_dev(struct device *dev)
|
|
{
|
|
bes2600_factory_dev = dev;
|
|
}
|
|
|
|
/*
|
|
* It is only used for temporary storage.
|
|
* Every time get the factory, it will read from the
|
|
* file and overwrite the original value.
|
|
*/
|
|
static struct factory_t factory_cali_data;
|
|
static struct factory_t *factory_p = NULL;
|
|
|
|
void bes2600_factory_lock(void)
|
|
{
|
|
mutex_lock(&factory_lock);
|
|
}
|
|
|
|
void bes2600_factory_unlock(void)
|
|
{
|
|
mutex_unlock(&factory_lock);
|
|
}
|
|
|
|
u8* bes2600_factory_get_file_buffer(void)
|
|
{
|
|
u8 *file_buffer = NULL;
|
|
|
|
file_buffer = kmalloc(FACTORY_MAX_SIZE, GFP_KERNEL);
|
|
if (!file_buffer) {
|
|
return NULL;
|
|
}
|
|
|
|
return file_buffer;
|
|
}
|
|
|
|
void bes2600_factory_free_file_buffer(u8 *file_buffer)
|
|
{
|
|
if (file_buffer)
|
|
kfree(file_buffer);
|
|
}
|
|
|
|
static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *factory_save_p);
|
|
|
|
static inline uint32_t factory_crc32(const uint8_t *data, uint32_t len)
|
|
{
|
|
u32 crc_le = 0;
|
|
crc_le = crc32_le(0xffffffffL, (uint8_t *)data, len);
|
|
crc_le ^= 0xffffffffL;
|
|
return crc_le;
|
|
}
|
|
|
|
static int bes2600_factory_head_info_check(struct factory_t *factory_data)
|
|
{
|
|
if (!factory_data) {
|
|
bes_err("%s NULL pointer err\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
if (factory_data->head.magic != NVREC_DEV_MAGIC) {
|
|
return -EBADMSG;
|
|
}
|
|
|
|
if ((factory_data->head.version < NVREC_MINI_VERSION) ||
|
|
(factory_data->head.version > NVREC_CURRENT_VERSION)) {
|
|
bes_err("factory version error:%d", factory_data->head.version);
|
|
return -EBADMSG;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int bes2600_factory_crc_check(struct factory_t *factory_data)
|
|
{
|
|
#ifdef FACTORY_CRC_CHECK
|
|
u32 cal_crc = 0;
|
|
u32 crc_len = sizeof(factory_data_t);
|
|
#ifndef STANDARD_FACTORY_EFUSE_FLAG
|
|
crc_len = (crc_len - sizeof(u16) + 3) & (~0x3);
|
|
#endif
|
|
|
|
if (!factory_data) {
|
|
bes_err("%s NULL pointer err \n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
cal_crc = factory_crc32((uint8_t *)(&(factory_data->data)), crc_len);
|
|
if (factory_data->head.crc != cal_crc) {
|
|
bes_err(ES2600_DBG_CHARDEV,
|
|
"bes2600 factory check failed, calc_crc:0x%08x factory_crc: 0x%08x\n",
|
|
cal_crc, factory_data->head.crc);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* factory_section_read_file - Read data of specified length from file
|
|
* @path: path of the file
|
|
* @buffer: storage of read data
|
|
*
|
|
* The maximum file length allowed is 600 bytes.
|
|
* This function does not do crc verification to take into
|
|
* account different storage requirements.
|
|
*
|
|
* Return: length on success, negative error code otherwise.
|
|
*/
|
|
static int factory_section_read_file(char *path, void *buffer)
|
|
{
|
|
const struct firmware *fw;
|
|
int ret;
|
|
|
|
if (!path || !buffer) {
|
|
bes_err("%s NULL pointer err\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
bes_devel("requesting firmware-class %s\n", path);
|
|
|
|
ret = request_firmware(&fw, path, bes2600_factory_dev);
|
|
if (ret) {
|
|
bes_devel("BES2600: request_firmware(%s) failed: %d\n", path, ret);
|
|
return -1;
|
|
}
|
|
|
|
if (fw->size == 0 || fw->size > FACTORY_MAX_SIZE) {
|
|
bes_err("bes2600_factory.txt size check failed, read_size: %zu max_size: %d\n",
|
|
fw->size, FACTORY_MAX_SIZE);
|
|
release_firmware(fw);
|
|
return -1;
|
|
}
|
|
|
|
memcpy(buffer, fw->data, fw->size);
|
|
ret = (int)fw->size;
|
|
release_firmware(fw);
|
|
return ret;
|
|
}
|
|
|
|
static inline int factory_parse(uint8_t *source_buf, struct factory_t *factory)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!source_buf || !factory) {
|
|
bes_err("%s NULL pointer err\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
ret = sscanf(source_buf, STANDARD_FACTORY,\
|
|
&factory->head.magic,\
|
|
&factory->head.version,\
|
|
&factory->head.crc,\
|
|
&factory->data.iQ_offset,\
|
|
&factory->data.freq_cal,\
|
|
&factory->data.freq_cal_flags,\
|
|
&factory->data.tx_power_ch[0],\
|
|
&factory->data.tx_power_ch[1],\
|
|
&factory->data.tx_power_ch[2],\
|
|
&factory->data.tx_power_type,\
|
|
&factory->data.temperature,\
|
|
&factory->data.tx_power_ch_5G[0],\
|
|
&factory->data.tx_power_ch_5G[1],\
|
|
&factory->data.tx_power_ch_5G[2],\
|
|
&factory->data.tx_power_ch_5G[3],\
|
|
&factory->data.tx_power_ch_5G[4],\
|
|
&factory->data.tx_power_ch_5G[5],\
|
|
&factory->data.tx_power_ch_5G[6],\
|
|
&factory->data.tx_power_ch_5G[7],\
|
|
&factory->data.tx_power_ch_5G[8],\
|
|
&factory->data.tx_power_ch_5G[9],\
|
|
&factory->data.tx_power_ch_5G[10],\
|
|
&factory->data.tx_power_ch_5G[11],\
|
|
&factory->data.tx_power_ch_5G[12],\
|
|
&factory->data.tx_power_flags_5G,\
|
|
&factory->data.temperature_5G,\
|
|
&factory->data.bt_tx_power[0],\
|
|
&factory->data.bt_tx_power[1],\
|
|
&factory->data.bt_tx_power[2],\
|
|
&factory->data.bt_tx_power[3]
|
|
#ifdef STANDARD_FACTORY_EFUSE_FLAG
|
|
,&factory->data.select_efuse);
|
|
#else
|
|
);
|
|
#endif
|
|
|
|
#ifndef STANDARD_FACTORY_EFUSE_FLAG
|
|
factory->data.select_efuse = 0;
|
|
#endif
|
|
|
|
if (ret != FACTORY_MEMBER_NUM)
|
|
{
|
|
bes_err("bes2600_factory.txt parse fail\n");
|
|
return -1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int factory_section_read_and_check_file(u8 *file_buf, char* path)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!file_buf || !path) {
|
|
bes_err("%s NULL pointer err\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
ret = factory_section_read_file(path, file_buf);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
memset(&factory_cali_data, 0, sizeof(struct factory_t));
|
|
ret = factory_parse(file_buf, &factory_cali_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = bes2600_factory_head_info_check(&factory_cali_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = bes2600_factory_crc_check(&factory_cali_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
factory_p = &factory_cali_data;
|
|
|
|
bes_devel("open wifi factory section success");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void factory_section_wifi_tx_power_check(struct factory_t *factory_data)
|
|
{
|
|
int i;
|
|
bool inval_v = false;
|
|
|
|
if (!factory_data)
|
|
return ;
|
|
|
|
/* only check cali channel, 11n ch1, ch7, ch13 */
|
|
for (i = 0; i < ARRAY_SIZE(factory_data->data.tx_power_ch); ++i) {
|
|
if (factory_data->data.tx_power_ch[i] == 0x0 ||
|
|
factory_data->data.tx_power_ch[i] > 0x3fff) {
|
|
inval_v = true;
|
|
}
|
|
}
|
|
|
|
if (inval_v)
|
|
bes_warn("tx_power_ch_2g cali, inval calibration value\n");
|
|
print_hex_dump(KERN_DEBUG,
|
|
"tx_power_ch_2g dump", DUMP_PREFIX_NONE, 16, 1,
|
|
factory_data->data.tx_power_ch, sizeof(factory_data->data.tx_power_ch), false);
|
|
|
|
}
|
|
|
|
|
|
static void factory_section_wifi_tx_power_5G_check(struct factory_t *factory_data)
|
|
{
|
|
int i;
|
|
bool inval_v = false;
|
|
|
|
if (!factory_data)
|
|
return ;
|
|
|
|
/* only check cali channel */
|
|
for (i = 0; i < ARRAY_SIZE(factory_data->data.tx_power_ch_5G); ++i) {
|
|
if (factory_data->data.tx_power_ch_5G[i] == 0x0 ||
|
|
factory_data->data.tx_power_ch_5G[i] > 0x3fff) {
|
|
inval_v = true;
|
|
}
|
|
}
|
|
|
|
if (inval_v)
|
|
bes_warn("tx_power_ch_5g cali, inval calibration value\n");
|
|
print_hex_dump(KERN_DEBUG,
|
|
"tx_power_ch_5g dump", DUMP_PREFIX_NONE, 16, 1,
|
|
factory_data->data.tx_power_ch, sizeof(factory_data->data.tx_power_ch), false);
|
|
|
|
}
|
|
|
|
static void factory_section_wifi_freq_cali_check(struct factory_t *factory_data)
|
|
{
|
|
if (!factory_data)
|
|
return ;
|
|
|
|
if (factory_data->data.freq_cal == 0x0 ||
|
|
factory_data->data.freq_cal > 0x1ff) {
|
|
bes_warn("freq cali, inval calibration value\n");
|
|
}
|
|
|
|
print_hex_dump(KERN_DEBUG,
|
|
"wifi freq cali dump dump: ", DUMP_PREFIX_NONE, 16, 1,
|
|
&factory_data->data.freq_cal, sizeof(factory_data->data.freq_cal), false);
|
|
|
|
}
|
|
|
|
static void factory_section_bt_tx_power_check(struct factory_t *factory_data)
|
|
{
|
|
int i;
|
|
bool inval_v = false;
|
|
|
|
if (!factory_data)
|
|
return ;
|
|
|
|
/* bt only check bdr & edr power, (bdr/edr: div, powerlevel) */
|
|
for (i = 0; i < ARRAY_SIZE(factory_data->data.bt_tx_power) - 1; i += 2) {
|
|
if (factory_data->data.bt_tx_power[i] != 0x05) {
|
|
inval_v = true;
|
|
}
|
|
|
|
if (factory_data->data.bt_tx_power[i + 1] == 0x0 ||
|
|
factory_data->data.bt_tx_power[i + 1] > 0x20) {
|
|
inval_v = true;
|
|
}
|
|
}
|
|
|
|
if (inval_v)
|
|
bes_warn("bt tx power cali, inval calibration value\n");
|
|
print_hex_dump(KERN_DEBUG,
|
|
"bt tx power cali dump: ", DUMP_PREFIX_NONE, 16, 1,
|
|
factory_data->data.bt_tx_power, sizeof(factory_data->data.bt_tx_power), false);
|
|
|
|
}
|
|
|
|
void factory_little_endian_cvrt(u8 *data)
|
|
{
|
|
int i;
|
|
struct factory_t *trans_data = NULL;
|
|
if (!data)
|
|
return ;
|
|
|
|
trans_data = (struct factory_t *)data;
|
|
|
|
TRANS_LE16(trans_data->head.magic);
|
|
TRANS_LE16(trans_data->head.version);
|
|
TRANS_LE32(trans_data->head.crc);
|
|
|
|
TRANS_LE16(trans_data->data.freq_cal);
|
|
TRANS_LE32(trans_data->data.iQ_offset);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(trans_data->data.tx_power_ch); i++)
|
|
TRANS_LE16(trans_data->data.tx_power_ch[i]);
|
|
|
|
TRANS_LE16(trans_data->data.temperature);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(trans_data->data.bt_tx_power); i++)
|
|
TRANS_LE32(trans_data->data.bt_tx_power[i]);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(trans_data->data.tx_power_ch_5G); i++)
|
|
TRANS_LE16(trans_data->data.tx_power_ch_5G[i]);
|
|
|
|
TRANS_LE16(trans_data->data.tx_power_flags_5G);
|
|
TRANS_LE16(trans_data->data.temperature_5G);
|
|
TRANS_LE16(trans_data->data.select_efuse);
|
|
|
|
}
|
|
|
|
void bes2600_factory_data_check(u8* data)
|
|
{
|
|
struct factory_t *factory_data = NULL;
|
|
|
|
if (!data)
|
|
return ;
|
|
|
|
factory_data = (struct factory_t *)data;
|
|
|
|
factory_section_wifi_tx_power_check(factory_data);
|
|
factory_section_wifi_tx_power_5G_check(factory_data);
|
|
factory_section_bt_tx_power_check(factory_data);
|
|
factory_section_wifi_freq_cali_check(factory_data);
|
|
|
|
/* In order to support manual value change, recalculate crc before sending */
|
|
factory_data->head.crc =
|
|
factory_crc32((uint8_t *)(&(factory_data->data)), sizeof(factory_data_t));
|
|
|
|
}
|
|
|
|
/*
|
|
* get factory data from file each time, and update factory_p.
|
|
*/
|
|
u8* bes2600_get_factory_cali_data(u8 *file_buffer, u32 *data_len, char *path)
|
|
{
|
|
u8 *ret_p = NULL;
|
|
|
|
if (!file_buffer || !path || !data_len) {
|
|
bes_err("%s NULL pointer\n", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
/* reset factory_p */
|
|
factory_p = NULL;
|
|
|
|
*data_len = sizeof(struct factory_t);
|
|
if (factory_section_read_and_check_file(file_buffer, path) < 0) {
|
|
bes_err("read and check %s error\n", path);
|
|
*data_len = 0;
|
|
return NULL;
|
|
}
|
|
|
|
if (!factory_p) {
|
|
*data_len = 0;
|
|
return NULL;
|
|
}
|
|
|
|
ret_p = (u8 *)factory_p;
|
|
|
|
return ret_p;
|
|
}
|
|
|
|
/**
|
|
* When the calibration file does not exist, a new file is automatically created when
|
|
* the calibration value is written. After writing, update factory_p, if the update is successful,
|
|
* it means the writing is successful, otherwise it fails. At the same time, subsequent calibration
|
|
* values are saved on this basis to avoid duplicating file creation and flushing out previously saved values.
|
|
*/
|
|
static bool bes2600_factory_file_status_read(u8 *file_buffer)
|
|
{
|
|
u8 *factory_temp = NULL;
|
|
uint32_t len;
|
|
bool ret = true;
|
|
|
|
#ifdef FACTORY_SAVE_MULTI_PATH
|
|
factory_temp = bes2600_get_factory_cali_data(file_buffer, &len, FACTORY_PATH);
|
|
if (!factory_temp) {
|
|
bes_warn("get factory cali from first path fali\n");
|
|
factory_temp = bes2600_get_factory_cali_data(file_buffer, &len, FACTORY_DEFAULT_PATH);
|
|
/* clear the flag of the file in the default path, and then create a new file */
|
|
if (factory_temp) {
|
|
((struct factory_t *)factory_temp)->data.tx_power_type = 0xff;
|
|
((struct factory_t *)factory_temp)->data.freq_cal_flags = 0;
|
|
((struct factory_t *)factory_temp)->data.tx_power_flags_5G = 0;
|
|
}
|
|
}
|
|
#else
|
|
factory_temp = bes2600_get_factory_cali_data(file_buffer, &len, FACTORY_PATH);
|
|
#endif
|
|
if (!factory_temp) {
|
|
bes_warn("get factory data fali, check whether the file exists\n");
|
|
ret = false;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* create a new factory.txt file, and set default value */
|
|
static int bes2600_factory_cali_file_hdr_fill(struct factory_t **factory_head)
|
|
{
|
|
u16 tx_power_type = 0xff;
|
|
int i;
|
|
|
|
if (!factory_head)
|
|
return -1;
|
|
|
|
*factory_head = &factory_cali_data;
|
|
memset(*factory_head, 0, sizeof(struct factory_t));
|
|
(*factory_head)->data.tx_power_type = tx_power_type;
|
|
(*factory_head)->head.magic = 0xba80;
|
|
(*factory_head)->head.version = 2;
|
|
|
|
(*factory_head)->data.freq_cal = 0xa0;
|
|
|
|
for (i = 0; i < 3; ++i) {
|
|
(*factory_head)->data.tx_power_ch[i] = 0x1400;
|
|
}
|
|
|
|
for (i = 0; i < 13; ++i) {
|
|
(*factory_head)->data.tx_power_ch_5G[i] = 0x1400;
|
|
}
|
|
|
|
(*factory_head)->data.bt_tx_power[0] = 0x05;
|
|
(*factory_head)->data.bt_tx_power[1] = 0x10;
|
|
(*factory_head)->data.bt_tx_power[2] = 0x05;
|
|
(*factory_head)->data.bt_tx_power[3] = 0x15;
|
|
#ifdef STANDARD_FACTORY_EFUSE_FLAG
|
|
(*factory_head)->data.select_efuse = 0;
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int16_t bes2600_wifi_power_cali_table_write(struct wifi_power_cali_save_t *data_cali)
|
|
{
|
|
u16 mode, band, ch, power_cali, bandwidth;
|
|
int power_index = 0;
|
|
struct factory_t *factory_power_p = NULL;
|
|
u8 *file_buffer = NULL;
|
|
int16_t ret = 0;
|
|
|
|
if (!data_cali) {
|
|
bes_warn("%s: power cali save pointer is NULL\n", __func__);
|
|
return -FACTORY_GET_INPUT_NULL_POINTER;
|
|
}
|
|
|
|
if (!(file_buffer = bes2600_factory_get_file_buffer()))
|
|
return -FACTORY_GET_INPUT_NULL_POINTER;
|
|
|
|
bes2600_factory_lock();
|
|
|
|
/**
|
|
* When it returns true, it means that the factory file has been read.
|
|
* When it returns false, it means that the factory file does not exist,
|
|
* or the operation of reading the text file fails. At this time, a new factory file will be created.
|
|
*/
|
|
if (bes2600_factory_file_status_read(file_buffer)) {
|
|
factory_power_p = factory_p;
|
|
} else {
|
|
if (bes2600_factory_cali_file_hdr_fill(&factory_power_p)) {
|
|
bes_warn("%s, create bes2600_factory.txt fail.", __func__);
|
|
ret = -FACTORY_FACTORY_TXT_CREATE_FAIL;
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
mode = data_cali->mode;
|
|
bandwidth = data_cali->bandwidth;
|
|
band = data_cali->band;
|
|
ch = data_cali->ch;
|
|
power_cali = data_cali->power_cali;
|
|
|
|
bes_devel("%s: mode = %u, bandwidth = %u,, band = %u, ch = %u, power_cali = 0x%04x\n",
|
|
__func__, mode, bandwidth, band, ch, power_cali);
|
|
|
|
/* only in 802.11n 20M msc7 mode, the power calibration value is saved */
|
|
if (bandwidth != 0 || mode != WIFI_RF_11N_MODE) {
|
|
ret = -FACTORY_SAVE_MODE_ERR;
|
|
goto err;
|
|
}
|
|
|
|
/* powerlevel value range: 0 ~ 0x3fff */
|
|
if (power_cali == 0 || power_cali > 0x3fff) {
|
|
ret = -FACTORY_SAVE_POWER_ERR;
|
|
goto err;
|
|
}
|
|
|
|
|
|
if (band == BAND_2G4) {
|
|
switch (ch) {
|
|
case 1:
|
|
power_index = 0;
|
|
break;
|
|
case 7:
|
|
power_index = 1;
|
|
break;
|
|
case 13:
|
|
power_index = 2;
|
|
break;
|
|
default:
|
|
ret = -FACTORY_SAVE_CH_ERR;
|
|
goto err;
|
|
break;
|
|
}
|
|
factory_power_p->data.tx_power_ch[power_index] = power_cali;
|
|
} else if (band == BAND_5G) {
|
|
switch (ch) {
|
|
case 36:
|
|
case 38:
|
|
case 40:
|
|
power_index = 0;
|
|
break;
|
|
case 44:
|
|
case 46:
|
|
case 48:
|
|
power_index = 1;
|
|
break;
|
|
case 52:
|
|
case 54:
|
|
case 56:
|
|
power_index = 2;
|
|
break;
|
|
case 60:
|
|
case 62:
|
|
case 64:
|
|
power_index = 3;
|
|
break;
|
|
case 100:
|
|
case 102:
|
|
case 104:
|
|
power_index = 4;
|
|
break;
|
|
case 108:
|
|
case 110:
|
|
case 112:
|
|
power_index = 5;
|
|
break;
|
|
case 116:
|
|
case 114:
|
|
case 120:
|
|
power_index = 6;
|
|
break;
|
|
case 124:
|
|
case 126:
|
|
case 128:
|
|
power_index = 7;
|
|
break;
|
|
case 132:
|
|
case 134:
|
|
case 136:
|
|
power_index = 8;
|
|
break;
|
|
case 140:
|
|
case 142:
|
|
case 144:
|
|
power_index = 9;
|
|
break;
|
|
case 149:
|
|
case 151:
|
|
case 153:
|
|
power_index = 10;
|
|
break;
|
|
case 157:
|
|
case 159:
|
|
case 161:
|
|
power_index = 11;
|
|
break;
|
|
case 165:
|
|
case 169:
|
|
power_index = 12;
|
|
break;
|
|
default:
|
|
ret = -FACTORY_SAVE_CH_ERR;
|
|
goto err;
|
|
break;
|
|
}
|
|
factory_power_p->data.tx_power_ch_5G[power_index] = power_cali;
|
|
}
|
|
|
|
/* save to file */
|
|
if (bes2600_wifi_cali_table_save(file_buffer, factory_power_p)) {
|
|
ret = -FACTORY_SAVE_WRITE_ERR;
|
|
goto err;
|
|
}
|
|
|
|
err:
|
|
bes2600_factory_free_file_buffer(file_buffer);
|
|
bes2600_factory_unlock();
|
|
return ret;
|
|
|
|
}
|
|
|
|
int16_t bes2600_wifi_cali_freq_write(struct wifi_freq_cali_t *data_cali)
|
|
{
|
|
u16 freq_cali;
|
|
struct factory_t *factory_freq_p = NULL;
|
|
u8 *file_buffer = NULL;
|
|
int16_t ret = 0;
|
|
|
|
if (!data_cali) {
|
|
bes_warn("%s: freq cali save pointer is NULL\n", __func__);
|
|
return -FACTORY_GET_INPUT_NULL_POINTER;
|
|
}
|
|
|
|
if (!(file_buffer = bes2600_factory_get_file_buffer()))
|
|
return -FACTORY_GET_INPUT_NULL_POINTER;
|
|
|
|
bes2600_factory_lock();
|
|
|
|
/**
|
|
* When it returns true, it means that the factory file has been read.
|
|
* When it returns false, it means that the factory file does not exist,
|
|
* or the operation of reading the text file fails. At this time, a new factory file will be created.
|
|
*/
|
|
if (bes2600_factory_file_status_read(file_buffer)) {
|
|
factory_freq_p = factory_p;
|
|
} else {
|
|
if (bes2600_factory_cali_file_hdr_fill(&factory_freq_p)) {
|
|
bes_warn("%s, create bes2600_factory.txt fail.", __func__);
|
|
ret = -FACTORY_FACTORY_TXT_CREATE_FAIL;
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
freq_cali = data_cali->freq_cali;
|
|
data_cali->cali_flag = 1;
|
|
|
|
/* freqOffset value range: 0 ~ 0x1ff */
|
|
if (freq_cali == 0 || freq_cali > 0x1ff) {
|
|
ret = -FACTORY_SAVE_FREQ_ERR;
|
|
goto err;
|
|
}
|
|
|
|
factory_freq_p->data.freq_cal = freq_cali;
|
|
factory_freq_p->data.freq_cal_flags = (u8)(data_cali->cali_flag);
|
|
bes_devel("%s: freq_cali = 0x%04x\n", __func__, data_cali->freq_cali);
|
|
|
|
/* save to file */
|
|
if (bes2600_wifi_cali_table_save(file_buffer, factory_freq_p)) {
|
|
ret = -FACTORY_SAVE_WRITE_ERR;
|
|
goto err;
|
|
}
|
|
|
|
err:
|
|
bes2600_factory_free_file_buffer(file_buffer);
|
|
bes2600_factory_unlock();
|
|
return ret;
|
|
}
|
|
|
|
#ifdef STANDARD_FACTORY_EFUSE_FLAG
|
|
int16_t bes2600_select_efuse_flag_write(uint16_t select_efuse_flag)
|
|
{
|
|
struct factory_t *factory_flag_p = NULL;
|
|
u8 *file_buffer = NULL;
|
|
int16_t ret = 0;
|
|
|
|
if (!(file_buffer = bes2600_factory_get_file_buffer()))
|
|
return -FACTORY_GET_INPUT_NULL_POINTER;
|
|
|
|
bes2600_factory_lock();
|
|
|
|
/**
|
|
* When it returns true, it means that the factory file has been read.
|
|
* When it returns false, it means that the factory file does not exist,
|
|
* or the operation of reading the text file fails. At this time, a new factory file will be created.
|
|
*/
|
|
if (bes2600_factory_file_status_read(file_buffer)) {
|
|
factory_flag_p = factory_p;
|
|
} else {
|
|
if (bes2600_factory_cali_file_hdr_fill(&factory_flag_p)) {
|
|
bes_warn("%s, create bes2600_factory.txt fail.", __func__);
|
|
ret = -FACTORY_FACTORY_TXT_CREATE_FAIL;
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
factory_flag_p->data.select_efuse = select_efuse_flag;
|
|
bes_devel("%s: select_efuse = %x\n", __func__, select_efuse_flag);
|
|
|
|
/* save to file */
|
|
if (bes2600_wifi_cali_table_save(file_buffer, factory_flag_p)) {
|
|
ret = -FACTORY_SAVE_WRITE_ERR;
|
|
goto err;
|
|
}
|
|
|
|
err:
|
|
bes2600_factory_free_file_buffer(file_buffer);
|
|
bes2600_factory_unlock();
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
int16_t vendor_set_power_cali_flag(struct wifi_power_cali_flag_t *cali_flag)
|
|
{
|
|
struct factory_t *factory_power_flag_p = NULL;
|
|
u8 *file_buffer = NULL;
|
|
u16 calied_flag_5g = 1;
|
|
u16 calied_flag_2g = 0;
|
|
int16_t ret = 0;
|
|
|
|
if (!cali_flag) {
|
|
bes_warn("%s: power cali flag save pointer is NULL\n", __func__);
|
|
return -FACTORY_GET_INPUT_NULL_POINTER;
|
|
}
|
|
|
|
if (cali_flag->band != BAND_2G4 && cali_flag->band != BAND_5G) {
|
|
bes_warn("%s: power cali flag save band err\n", __func__);
|
|
return -FACTORY_SET_POWER_CALI_FLAG_ERR;
|
|
}
|
|
|
|
if (!(file_buffer = bes2600_factory_get_file_buffer()))
|
|
return -FACTORY_GET_INPUT_NULL_POINTER;
|
|
|
|
bes2600_factory_lock();
|
|
|
|
if (bes2600_factory_file_status_read(file_buffer)) {
|
|
factory_power_flag_p = factory_p;
|
|
} else {
|
|
bes_warn("%s: factory cali data is not exist\n", __func__);
|
|
ret = -FACTORY_SAVE_FILE_NOT_EXIST;
|
|
goto err;
|
|
}
|
|
|
|
if (cali_flag->band == BAND_2G4) {
|
|
factory_power_flag_p->data.tx_power_type = calied_flag_2g;
|
|
} else {
|
|
factory_power_flag_p->data.tx_power_flags_5G = calied_flag_5g;
|
|
}
|
|
|
|
/* save to file */
|
|
if (bes2600_wifi_cali_table_save(file_buffer, factory_power_flag_p)) {
|
|
ret = -FACTORY_SET_POWER_CALI_FLAG_ERR;
|
|
goto err;
|
|
}
|
|
|
|
err:
|
|
bes2600_factory_free_file_buffer(file_buffer);
|
|
bes2600_factory_unlock();
|
|
return ret;
|
|
|
|
}
|
|
|
|
static inline int factory_build(uint8_t *dest_buf, struct factory_t *factory)
|
|
{
|
|
return snprintf(dest_buf, FACTORY_MAX_SIZE, STANDARD_FACTORY,\
|
|
factory->head.magic,\
|
|
factory->head.version,\
|
|
factory->head.crc,\
|
|
factory->data.iQ_offset,\
|
|
factory->data.freq_cal,\
|
|
factory->data.freq_cal_flags,\
|
|
factory->data.tx_power_ch[0],\
|
|
factory->data.tx_power_ch[1],\
|
|
factory->data.tx_power_ch[2],\
|
|
factory->data.tx_power_type,\
|
|
factory->data.temperature,\
|
|
factory->data.tx_power_ch_5G[0],\
|
|
factory->data.tx_power_ch_5G[1],\
|
|
factory->data.tx_power_ch_5G[2],\
|
|
factory->data.tx_power_ch_5G[3],\
|
|
factory->data.tx_power_ch_5G[4],\
|
|
factory->data.tx_power_ch_5G[5],\
|
|
factory->data.tx_power_ch_5G[6],\
|
|
factory->data.tx_power_ch_5G[7],\
|
|
factory->data.tx_power_ch_5G[8],\
|
|
factory->data.tx_power_ch_5G[9],\
|
|
factory->data.tx_power_ch_5G[10],\
|
|
factory->data.tx_power_ch_5G[11],\
|
|
factory->data.tx_power_ch_5G[12],\
|
|
factory->data.tx_power_flags_5G,\
|
|
factory->data.temperature_5G,\
|
|
factory->data.bt_tx_power[0],\
|
|
factory->data.bt_tx_power[1],\
|
|
factory->data.bt_tx_power[2],\
|
|
factory->data.bt_tx_power[3]
|
|
#ifdef STANDARD_FACTORY_EFUSE_FLAG
|
|
,factory->data.select_efuse);
|
|
#else
|
|
);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Rebuild the serialised calibration blob in file_buffer from the live
|
|
* in-memory factory_save_p. Previously this function also persisted the
|
|
* blob back to FACTORY_PATH via filp_open(O_CREAT) + kernel_write(); that
|
|
* is not acceptable in mainline, so the persistence step has been removed.
|
|
*
|
|
* The in-memory factory_save_p remains authoritative for the duration of
|
|
* the session; on the next probe the firmware-class file is read back
|
|
* read-only via request_firmware(). If cross-reboot persistence of runtime
|
|
* calibration updates becomes a requirement, the expected route is a
|
|
* userspace-facing dump interface (debugfs read-only blob, or nl80211
|
|
* vendor command) that lets userspace read the serialised form and store
|
|
* it under its own privileges.
|
|
*/
|
|
static int bes2600_wifi_cali_table_save(u8 *file_buffer, struct factory_t *factory_save_p)
|
|
{
|
|
int w_size;
|
|
u32 crc_len = sizeof(factory_data_t);
|
|
#ifndef STANDARD_FACTORY_EFUSE_FLAG
|
|
crc_len = (crc_len - sizeof(u16) + 3) & (~0x3);
|
|
#endif
|
|
|
|
bes_devel("enter %s\n", __func__);
|
|
|
|
if (!file_buffer)
|
|
return -ENOMEM;
|
|
|
|
if (!factory_save_p)
|
|
return -ENOENT;
|
|
|
|
/* All initialized to space */
|
|
memset(file_buffer, 32, FACTORY_MAX_SIZE);
|
|
file_buffer[FACTORY_MAX_SIZE - 1] = '\n';
|
|
|
|
factory_save_p->head.crc =
|
|
factory_crc32((uint8_t *)(&(factory_save_p->data)), crc_len);
|
|
|
|
w_size = factory_build(file_buffer, factory_save_p);
|
|
|
|
if (w_size < 0 || w_size > FACTORY_MAX_SIZE) {
|
|
bes_err("%s: build failed! w_size = %d.", __func__, w_size);
|
|
return -ETXTBSY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int16_t vendor_get_power_cali(struct wifi_get_power_cali_t *power_cali)
|
|
{
|
|
u8 *file_buffer = NULL;
|
|
int16_t ret = 0;
|
|
|
|
if (!(file_buffer = bes2600_factory_get_file_buffer()))
|
|
return -FACTORY_GET_INPUT_NULL_POINTER;
|
|
|
|
bes2600_factory_lock();
|
|
|
|
if (!power_cali) {
|
|
ret = -FACTORY_GET_INPUT_NULL_POINTER;
|
|
goto err;
|
|
}
|
|
|
|
if (!bes2600_factory_file_status_read(file_buffer)) {
|
|
ret = -FACTORY_SAVE_FILE_NOT_EXIST;
|
|
goto err;
|
|
}
|
|
|
|
memcpy(power_cali->tx_power_ch, factory_p->data.tx_power_ch, sizeof(power_cali->tx_power_ch));
|
|
memcpy(power_cali->tx_power_ch_5G, factory_p->data.tx_power_ch_5G, sizeof(power_cali->tx_power_ch_5G));
|
|
|
|
err:
|
|
bes2600_factory_free_file_buffer(file_buffer);
|
|
bes2600_factory_unlock();
|
|
return ret;
|
|
}
|
|
|
|
|
|
int16_t vendor_get_freq_cali(struct wifi_freq_cali_t *vendor_freq)
|
|
{
|
|
u8 *file_buffer = NULL;
|
|
int16_t ret = 0;
|
|
|
|
if (!vendor_freq)
|
|
return -FACTORY_GET_INPUT_NULL_POINTER;
|
|
|
|
if (!(file_buffer = bes2600_factory_get_file_buffer()))
|
|
return -FACTORY_GET_INPUT_NULL_POINTER;
|
|
|
|
bes2600_factory_lock();
|
|
|
|
if (!bes2600_factory_file_status_read(file_buffer)) {
|
|
ret = -FACTORY_SAVE_FILE_NOT_EXIST;
|
|
goto err;
|
|
}
|
|
|
|
vendor_freq->status = 0;
|
|
vendor_freq->freq_cali = factory_p->data.freq_cal;
|
|
vendor_freq->cali_flag = factory_p->data.freq_cal_flags;
|
|
|
|
err:
|
|
bes2600_factory_free_file_buffer(file_buffer);
|
|
bes2600_factory_unlock();
|
|
return ret;
|
|
}
|
|
|