nt9856x/code/driver/source/net/18211_nvtfmac/nvt_fw.c
2023-03-28 15:07:53 +08:00

1290 lines
41 KiB
C
Executable File

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/firmware.h>
#include "nvt_fw.h"
#include "nvt_wlan_linux.h"
#include "nvt_diag.h"
//20151015 nash: workaround for calibration which take long time
//#define NVT_WAIT_FW_RDY_TIMEOUT 10000
#define NVT_WAIT_FW_RDY_TIMEOUT 20000
static u64 elf_offset;
struct _nvt_config_patch_file_header nvt_config_patch_file_header;
struct _nvt_config_img_sector_header nvt_config_img_sector_header;
#if (NVT_FW_CFG_DOWNLOAD_BY_CHIPID == 1)
static s8 *nvt_fw_get_name(struct _nvt_bus *, enum nvt_fw_mode);
static struct _nvt_fw_name fw_name_table[] = {
{0x18202, NVT_FW_STA, "nvt/dsw_18202_sta.img"},
{0x18202, NVT_FW_AP, "nvt/dsw_18202_ap.img"},
{0x18202, NVT_FW_HUT, "nvt/dsw_18202_hut.img"},
{0x18211, NVT_FW_STA, "nvt/dsw_18211_sta.img"},
{0x18211, NVT_FW_AP, "nvt/dsw_18211_ap.img"},
{0x18211, NVT_FW_HUT, "nvt/dsw_18211_hut.img"}
};
#endif
//X^8 + X^2 + X^1 + X^0
static const u8 Crc8Table[] = {
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
};
//20160726 nash:
const struct firmware *resume_cfg_fw = NULL;
s32 nvt_fw_get_ver(struct _nvt_bus *nvt_bus, u8 *ver, u32 *ver_len)
{
s32 ret;
//20151218 nash: coverity#48988
s32 pkt_len = 0;
u32 fw_ver;
struct _nvt_diag_req *diag_req = NULL;
struct _nvt_diag_resp *diag_resp = NULL;
diag_req = kzalloc(sizeof(struct _nvt_diag_req), GFP_KERNEL);
if (diag_req == NULL) {
nvt_dbg(FW, "%s: diag_req kzalloc is failed\n", __func__);
ret = -1;
goto alloc_failed;
}
diag_resp = kzalloc(sizeof(struct _nvt_diag_resp), GFP_KERNEL);
if (diag_resp == NULL) {
nvt_dbg(DIAG, "%s: diag_resp kzalloc is failed\n", __func__);
ret = -1;
goto alloc_failed;
}
/* Get firmware type first */
nvt_diag_pack_req(NVT_DIAG_CMD_CLASS_0, NVT_DIAG_GET_FW_VER_CMD, 0, 0,
NULL, diag_req, &pkt_len);
ret = nvt_bus->nvt_wdev_bus_ops.tx_ctrl(nvt_bus,
(u8 *)diag_req, pkt_len);
if (ret < 0) {
nvt_dbg(FW, "%s: failed to send diag req!!\n", __func__);
goto alloc_failed;
}
ret = nvt_bus->nvt_wdev_bus_ops.rx_ctrl(nvt_bus, (u8 *)diag_resp,
sizeof(struct _nvt_diag_resp));
if (ret < 0) {
nvt_dbg(FW, "%s: failed to recv diag resp!!\n", __func__);
goto alloc_failed;
}
fw_ver = *(u32 *)(diag_resp->sel.get_fw_ver.fw_ver);
//20151218 nash: coverity#48968??
//sprintf(ver, "%d", fw_ver);
snprintf(ver, 10, "%d", fw_ver);
*ver_len = strlen(ver);
ret = 0;
alloc_failed:
kfree(diag_req);
kfree(diag_resp);
return ret;
}
static s32 nvt_fw_get_type(struct _nvt_bus *nvt_bus)
{
s32 ret;
//20151218 nash: coverity#48987
s32 pkt_len = 0;
s32 *fw_type;
struct _nvt_diag_req *diag_req = NULL;
struct _nvt_diag_resp *diag_resp = NULL;
diag_req = kzalloc(sizeof(struct _nvt_diag_req), GFP_KERNEL);
if (diag_req == NULL) {
nvt_dbg(FW, "%s: diag_req kzalloc is failed\n", __func__);
ret = -1;
goto alloc_failed;
}
diag_resp = kzalloc(sizeof(struct _nvt_diag_resp), GFP_KERNEL);
if (diag_resp == NULL) {
nvt_dbg(DIAG, "%s: diag_resp kzalloc is failed\n", __func__);
ret = -1;
goto alloc_failed;
}
/* Get firmware type first */
nvt_diag_pack_req(NVT_DIAG_CMD_CLASS_0, NVT_DIAG_GET_FW_VER_CMD, 0, 0,
NULL, diag_req, &pkt_len);
ret = nvt_bus->nvt_wdev_bus_ops.tx_ctrl(nvt_bus,
(u8 *)diag_req, pkt_len);
if (ret < 0) {
nvt_dbg(FW, "%s: failed to send diag req!!\n", __func__);
goto alloc_failed;
}
ret = nvt_bus->nvt_wdev_bus_ops.rx_ctrl(nvt_bus, (u8 *)diag_resp,
sizeof(struct _nvt_diag_resp));
if (ret < 0) {
nvt_dbg(FW, "%s: failed to recv diag resp!!\n", __func__);
goto alloc_failed;
}
fw_type = (u32 *)(diag_resp->sel.get_fw_ver.fw_type);
ret = *fw_type;
nvt_dbg(FW, "%s: FW type=%d\n", __func__, ret);
alloc_failed:
kfree(diag_req);
kfree(diag_resp);
return ret;
}
static s32 nvt_fw_is_elf(Elf32_Ehdr eh)
{
/* ELF magic bytes are 0x7f,'E','L','F'
* Using octal escape sequence to represent 0x7f
*/
if ((eh.e_ident[EI_MAG0] != ELFMAG0) ||
(eh.e_ident[EI_MAG1] != ELFMAG1) ||
(eh.e_ident[EI_MAG2] != ELFMAG2) ||
(eh.e_ident[EI_MAG3] != ELFMAG3) ||
(eh.e_ehsize != sizeof(Elf32_Ehdr))) {
nvt_dbg(FW, "%s: is a ELF\n", __func__);
return 1;
} else {
nvt_dbg(FW, "%s: ELFMAGIC mismatch!\n", __func__);
return 0;
}
}
static s32 nvt_fw_fm_goto_address(struct _nvt_bus *bus, void *entry_addr)
{
s32 ret = 0;
s32 pkt_len;
u32 addr = NVT_FW_FM_GO_ADDR;
struct _nvt_diag_req *diag_req;
diag_req = kzalloc(sizeof(struct _nvt_diag_req), GFP_KERNEL);
if (diag_req == NULL) {
nvt_dbg(DIAG, "%s: kzalloc is failed\n", __func__);
goto alloc_failed;
}
nvt_diag_pack_req(NVT_DIAG_CMD_CLASS_0, NVT_DIAG_WRITE_CMD, addr, 4,
(u8 *)(entry_addr), diag_req, &pkt_len);
ret = bus->nvt_wdev_bus_ops.tx_ctrl(bus, (u8 *)diag_req, pkt_len);
if (ret < 0) {
nvt_dbg(FW, "%s: send diag msg fail\n", __func__);
}
kfree(diag_req);
return ret;
alloc_failed:
return -1;
}
static s32 nvt_fw_crc_check(struct _nvt_bus *bus, u32 addr, s32 cnt, u8 crc,
u32 retry_count)
{
struct _nvt_diag_req *diag_req = NULL;
struct _nvt_diag_resp *diag_resp = NULL;
ssize_t retval = 0;
//20151218 nash: coverity#48985
s32 pkt_len = 0;
u32 crc_value_by_fw = 0;
u32 retry_time = 0;
diag_req = kzalloc(sizeof(struct _nvt_diag_req), GFP_KERNEL);
if (diag_req == NULL) {
nvt_dbg(FW, "%s: kzalloc is failed\n", __func__);
retval = -1;
goto alloc_failed;
}
diag_resp = kzalloc(sizeof(struct _nvt_diag_resp), GFP_KERNEL);
if (diag_resp == NULL) {
nvt_dbg(FW, "%s: kzalloc is failed\n", __func__);
retval = -1;
goto alloc_failed;
}
nvt_diag_pack_req(NVT_DIAG_CMD_CLASS_0, NVT_DIAG_CHECK_SUM_CMD, addr,
cnt, NULL, diag_req, &pkt_len);
do {
bus->nvt_wdev_bus_ops.tx_ctrl(bus, (u8 *)diag_req, pkt_len);
//4. get resp by diag_node
retval = bus->nvt_wdev_bus_ops.rx_ctrl(bus, (u8 *)diag_resp,
sizeof(struct _nvt_diag_resp));
if (retval < 0) {
nvt_dbg(FW, "%s: failed to recv diag resp!!\n",
__func__);
goto alloc_failed;
}
//5. get crc check sum value from driver
//crc_value_by_driver = CRC_CalcBlockDataCRC8(buf,cnt);
//6. get crc check sum value from FW
crc_value_by_fw = diag_resp->sel.cksum_veri.cksum_val;
nvt_dbg(FW, "%s: crc_value_from_driver=%08x\n",
__func__, crc);
nvt_dbg(FW, "%s: crc_value_from_fw=%08x\n",
__func__, crc_value_by_fw);
//20151112 nash: code bug
//if (crc_value_by_fw == crc_value_by_driver) {
if (crc_value_by_fw == crc) {
goto get_correct_crc;
}
} while (retry_time++ < retry_count);
/* time out */
retval = -1;
get_correct_crc:
alloc_failed:
kfree(diag_req);
kfree(diag_resp);
return retval;
}
static s32 nvt_fw_fm_write(struct _nvt_bus *bus,
u32 addr, s32 cnt, s8 *buf)
{
s32 pkt_len;
s32 ret = 0;
struct _nvt_diag_req *diag_req;
diag_req = kzalloc(sizeof(struct _nvt_diag_req), GFP_KERNEL);
if (diag_req == NULL) {
nvt_dbg(DIAG, "%s[0]kzalloc is failed\n", __func__);
ret = -1;
goto alloc_failed;
}
nvt_diag_pack_req(NVT_DIAG_CMD_CLASS_0, NVT_DIAG_WRITE_CMD, addr, cnt,
buf, diag_req, &pkt_len);
ret = bus->nvt_wdev_bus_ops.tx_ctrl(bus, (u8 *)diag_req, pkt_len);
alloc_failed:
kfree(diag_req);
return ret;
}
/**
* nvt_fw_util_calc_crc8 - calculate the CRC value of block data
*
* @data_start: the pointer point to data block
* @init_seed: CRC init value
* @length: copy length *
*
* Return: the CRC value of the input data block
*/
static u8 nvt_fw_util_calc_crc8(u8 *data_start, u8 init_seed,
u32 length)
{
u32 i;
u8 loc_crc = init_seed;
for (i = 0; i < length; i++) {
loc_crc ^= *(data_start + i);
loc_crc = Crc8Table[loc_crc];
}
return loc_crc;
}
static s32 nvt_fw_util_endian_trans(u8 *buff, u32 transfer_len)
{
u8 *buff_p;
u32 count;
buff_p = buff;
count = transfer_len;
while (count > 0) {
if (count >= 4) {
//EndianCnv_32((u32 *)buff_p, 4);
be32_to_cpus((u32 *)buff_p);
count -= 4;
buff_p += 4;
} else if (count >= 2) {
//EndianCnv_16((u16 *)buff_p, 2);
be16_to_cpus((u16 *)buff_p);
count -= 2;
buff_p += 2;
} else {
count -= 1;
buff_p += 1;
}
}
return 0;
}
static u32 nvt_fw_elf_offset_set(u64 offset)
{
elf_offset = offset;
return 0;
}
static u64 nvt_fw_elf_offset_get(void)
{
return elf_offset;
}
static s32 nvt_fw_elf_seek(const u8 *store_buf, u64 offset, u8 cmd)
{
switch (cmd) {
case ELF_SEEK_SET:
nvt_fw_elf_offset_set(offset);
break;
default:
nvt_dbg(FW, "%s: incorrect command\n", __func__);
return -1;
}
return 0;
}
static u16 nvt_fw_elf_read(u8 *read_buf, u8 size, u16 count,
const u8 *store_buf)
{
u16 len = size * count;
u64 offset = nvt_fw_elf_offset_get();
nvt_dbg(FW, "%s: now offset=%lld\n", __func__,
nvt_fw_elf_offset_get());
memcpy(read_buf, store_buf + offset, len);
nvt_fw_elf_offset_set(offset + len);
nvt_dbg(FW, "%s: modified offset=%lld\n", __func__,
nvt_fw_elf_offset_get());
return len;
}
static void nvt_fw_elf_read_header(const u8 *store_buf,
Elf32_Ehdr *elf_header)
{
if (nvt_fw_elf_seek(store_buf, 0, ELF_SEEK_SET) != 0) {
memset((void *)elf_header, 0, sizeof(Elf32_Ehdr));
return;
} else {
if (nvt_fw_elf_read((u8 *)elf_header,
sizeof(Elf32_Ehdr), 1, store_buf) == 0) {
memset((void *)elf_header, 0, sizeof(Elf32_Ehdr));
return;
}
nvt_dbg(FW, "%s: got elf header", __func__);
}
return;
}
static s32 nvt_fw_elf_data_program(struct _nvt_bus *bus,
Elf32_Shdr sh, u8 *fw_buf)
{
u8 *buff;
u16 buff_size = 1024;
u8 crc = 0;
s32 len;
u32 sh_addr = 0;
u32 crc_sh_addr = 0;
u32 crc_range_fw = 0;
u32 transfer_len;
//20161021 nash: retry for crc error
s32 ret;
s32 retry = 3;
buff = kmalloc(buff_size, GFP_ATOMIC);
//20151218 nash: coverity#48956
if (buff == NULL) {
nvt_dbg(ERROR, "%s: kmalloc fail!!\n", __func__);
return -1;
}
//20161021 nash: retry for crc error
do {
crc_sh_addr = (u32)sh.sh_addr;
sh_addr = (u32)sh.sh_addr;
crc_range_fw = sh.sh_size;
len = sh.sh_size;
crc = 0;
nvt_fw_elf_seek(fw_buf, sh.sh_offset, ELF_SEEK_SET);
while (len) {
transfer_len = (len >= buff_size) ? buff_size : len;
transfer_len = nvt_fw_elf_read(buff, 1,
transfer_len, fw_buf);
crc = nvt_fw_util_calc_crc8(buff, crc, transfer_len);
nvt_dbg(FW, "%s: write section to memory\n", __func__);
len -= transfer_len;
nvt_fw_util_endian_trans(buff, (transfer_len+3) &
0xFFFFFFFC);
if (nvt_fw_fm_write(bus, sh_addr,
(transfer_len+3) & 0xFFFFFFFC, buff)) {
ret = -1;
goto failed;
}
sh_addr += transfer_len;
}
/* check if crc value is ok */
ret = nvt_fw_crc_check(bus, crc_sh_addr, crc_range_fw, crc , 3);
if (ret < 0) {
nvt_dbg(ERROR, "%s: crc check is incorrect\n",
__func__);
}
} while (ret != 0 && --retry > 0);
failed:
kfree(buff);
return ret;
}
static s32 nvt_fw_config_program_in_img(struct _nvt_bus *bus,
const u8 *img_buffer)
{
s32 img_total_length = 0;
s32 sector_total_length = 0;
u32 write_addr = 0;
u8 *read_buf = NULL;
u32 actual_read_count = 0 , actual_write_count = 0;
s32 retcode = 0;
u8 crc = 0;
u32 i = 0;
nvt_dbg(FW, "%s: total length=%d\n", __func__,
nvt_config_patch_file_header.total_length);
img_total_length = nvt_config_patch_file_header.total_length -
NVT_CONFIG_PATCH_FILE_HEADER_LEN;
nvt_dbg(FW, "%s: total length=%d\n", __func__,
nvt_config_patch_file_header.total_length);
nvt_dbg(FW, "%s: img_total_length =%d\n", __func__, img_total_length);
if (img_total_length <= 0) {
nvt_dbg(ERROR, "%s: incorrect img_total_length!!\n", __func__);
return -1;
}
nvt_fw_elf_offset_set(0);
read_buf = kmalloc(NVT_CONFIG_IMG_SECTOR_READ_MAX_LEN, GFP_ATOMIC);
if (read_buf == NULL) {
//fix CID #64558 by jerry lin.
nvt_dbg(ERROR, "%s: failed to allocate memory\n", __func__);
return -1;
}
do {
// processing of image sector
// read the sector header of config img
nvt_fw_elf_read((u8 *)(&nvt_config_img_sector_header),
sizeof(struct _nvt_config_img_sector_header), 1, img_buffer);
sector_total_length = nvt_config_img_sector_header.total_length
- sizeof(struct _nvt_config_img_sector_header);
// get starting write address
write_addr = nvt_config_img_sector_header.write_addr;
nvt_dbg(FW, "%s: nvt_config_img_sector_header.total_length=%d\n"
, __func__, nvt_config_img_sector_header.total_length);
nvt_dbg(FW, "%s: sector_total_length=%d\n", __func__,
sector_total_length);
nvt_dbg(FW, "%s: write_addr=%x\n", __func__, write_addr);
if (sector_total_length <= 0) {
nvt_dbg(ERROR, "%s: incorrect sector_total_length!!\n",
__func__);
retcode = -1;
goto error_free;
}
crc = 0;
do {
actual_read_count = actual_write_count = (
sector_total_length >=
NVT_CONFIG_IMG_SECTOR_READ_MAX_LEN)
? NVT_CONFIG_IMG_SECTOR_READ_MAX_LEN
: sector_total_length;
nvt_fw_elf_read(read_buf, 1, actual_read_count,
img_buffer);
nvt_dbg(FW, "%s:write_addr=%x\n", __func__, write_addr);
nvt_dbg(FW, "%s: actual_write_count=%d\n", __func__,
actual_write_count);
nvt_dbg(FW, "%s: read_buf:", __func__);
for (i = 0; i < ((actual_write_count+3) & 0xFFFFFFFC);
i++)
nvt_dbg(FW, "%x ", *(read_buf+i));
crc = nvt_fw_util_calc_crc8(read_buf, crc,
(actual_write_count+3) & 0xFFFFFFFC);
nvt_fw_util_endian_trans(read_buf,
(actual_write_count+3) & 0xFFFFFFFC);
nvt_fw_fm_write(bus, write_addr,
(actual_write_count+3) & 0xFFFFFFFC, read_buf);
sector_total_length -= actual_read_count;
write_addr += actual_read_count;
nvt_dbg(FW, "%s: sector_total_length=%d\n", __func__,
sector_total_length);
nvt_dbg(FW, "%s: write addr=%x\n", __func__, write_addr)
;
} while (sector_total_length > 0);
/* check if crc value is ok */
nvt_dbg(FW, "%s: nvt_config_img_sector_header.write_addr=%x\n",
__func__, nvt_config_img_sector_header.write_addr);
nvt_dbg(FW, "%s: sector total length=%d\n", __func__ ,
nvt_config_img_sector_header.total_length -
sizeof(struct _nvt_config_img_sector_header));
nvt_dbg(FW, "%s: crc_by_driver=%x\n", __func__, crc);
if (nvt_fw_crc_check(bus,
nvt_config_img_sector_header.write_addr,
nvt_config_img_sector_header.total_length -
sizeof(struct _nvt_config_img_sector_header),
crc , 3) == 0) {
nvt_dbg(FW, "%s[]crc check is correct", __func__);
} else {
nvt_dbg(FW, "%s[]crc check is incorrect", __func__);
}
img_total_length -= nvt_config_img_sector_header.total_length;
nvt_dbg(FW, "%s: img_total_length =%d\n", __func__,
img_total_length);
} while (img_total_length > sizeof(struct _nvt_config_img_sector_header)
);
if (img_total_length != 1) {
nvt_dbg(ERROR, "%s: incorrect img_total_length=%d\n", __func__,
img_total_length);
retcode = -1;
}
error_free:
kfree(read_buf);
return retcode;
}
s32 nvt_config_patch_file_crc_check(const struct firmware *img,
struct _nvt_config_patch_file_header *header)
{
u8 crc_seed_init = 0, crc_calc_value = 0, crc_value_by_img = 0;
nvt_dbg(FW, "%s: 3 total length=%d\n", __func__,
nvt_config_patch_file_header.total_length);
crc_calc_value = nvt_fw_util_calc_crc8((u8 *)(img->data+
NVT_CONFIG_PATCH_FILE_HEADER_LEN), crc_seed_init,
img->size - NVT_CONFIG_PATCH_FILE_HEADER_LEN-1);
crc_value_by_img = *(img->data + header->total_length - 1);
nvt_dbg(FW, "%s crc_calc_value=%d, crc value of img=%d\n", __func__,
crc_calc_value, crc_value_by_img);
if (crc_value_by_img != crc_calc_value) {
nvt_dbg(ERROR, "%s config patch file crc error!!\r\n",
__func__);
return -1;
}
return 0;
}
s32 nvt_config_patch_read_file_header(const struct firmware *img,
struct _nvt_config_patch_file_header *header)
{
u8 crc_seed_init = 0, crc_calc_value = 0,
crc_len = NVT_CONFIG_PATCH_FILE_HEADER_LEN-1;
memcpy(header, img->data, sizeof
(struct _nvt_config_patch_file_header));
crc_calc_value = nvt_fw_util_calc_crc8((u8 *)header, crc_seed_init,
crc_len);
nvt_dbg(FW, "%s prj_code=%d, total_length=%d,crc=%d\n", __func__,
header->project_code, header->total_length, header->crc);
nvt_dbg(FW, "%s crc_calc_value=%d, crc value of img header=%d\n",
__func__, crc_calc_value, header->crc);
if (crc_calc_value != header->crc) {
nvt_dbg(ERROR, "%s header CRC error!!\r\n", __func__);
return -1;
}
return 0;
}
static s32 nvt_config_patch_download(struct _nvt_bus *nvt_bus, bool is_resume)
{
s8 *config_path_file_name = NVT_FW_CONFIG_PATCH_FILE_NAME;
s32 error_code = 0;
const struct firmware *config_patch_file = NULL;
/*sanity check*/
if (nvt_bus == NULL) {
nvt_dbg(ERROR, "%s nvt_bus is NULL\r\n", __func__);
return -1;
}
if (is_resume) {
if (resume_cfg_fw == NULL) {
nvt_dbg(ERROR, "%s no config patch file\r\n", __func__);
return -1;
}
config_patch_file = resume_cfg_fw;
} else {
error_code = request_firmware(&config_patch_file,
config_path_file_name, nvt_bus->dev);
if (error_code < 0) {
nvt_dbg(ERROR, "%s failed to request_firmware", __func__);
return -1;
}
}
nvt_dbg(FW, "%s request config patch file succesfully!!\n", __func__);
if (nvt_config_patch_read_file_header(config_patch_file,
&nvt_config_patch_file_header) == -1) {
nvt_dbg(ERROR, "%s failed on crc of img file header\n",
__func__);
error_code = -1;
goto error_handling;
}
nvt_dbg(FW, "%s config patch read file header succesfully!!\n",
__func__);
nvt_dbg(FW, "%s: 1 total length=%d\n", __func__,
nvt_config_patch_file_header.total_length);
if (nvt_config_patch_file_crc_check(config_patch_file,
&nvt_config_patch_file_header) == -1) {
nvt_dbg(ERROR, "%s failed on crc of img file\n", __func__);
error_code = -1;
goto error_handling;
}
nvt_dbg(FW, "%s config patch read file succesfully!!\n", __func__);
nvt_dbg(FW, "%s:2 total length=%d\n", __func__,
nvt_config_patch_file_header.total_length);
if (nvt_fw_config_program_in_img(nvt_bus,
config_patch_file->data+NVT_CONFIG_PATCH_FILE_HEADER_LEN) == -1) {
nvt_dbg(ERROR, "%s failed to config program in img\n",
__func__);
error_code = -1;
goto error_handling;
}
error_handling:
// fix CID #64459 by jerry lin.
release_firmware(config_patch_file);
return error_code;
}
static s32 nvt_fw_program_in_elf(struct _nvt_bus *bus,
const u8 *fw_buffer)
{
Elf32_Ehdr eh; /* elf-header is fixed size */
Elf32_Shdr sh_str;
u8 *sh_name_tbl;
u16 e_shnum;
u16 e_shentsize;
u16 e_shstrndx;
u32 e_shoff;
u32 entry_address;
u32 total_size = 0;
s32 ret = 0;
Elf32_Shdr sh;
nvt_fw_elf_read_header(fw_buffer, &eh);
if (!nvt_fw_is_elf(eh)) {
nvt_dbg(FW, "%s: not ELF file\n", __func__);
ret = -1;
goto invalid_format;
}
//entry address
entry_address = eh.e_entry;
if (eh.e_ident[EI_DATA] == ELFDATA2MSB) {
entry_address = be32_to_cpu(entry_address);
}
nvt_dbg(FW, "%s: entry_point = 0x%08x\n", __func__, entry_address);
nvt_fw_elf_seek(fw_buffer, 0, ELF_SEEK_SET);
if (eh.e_ident[EI_DATA] == ELFDATA2MSB) {
e_shnum = be16_to_cpu(eh.e_shnum);
e_shentsize = be16_to_cpu(eh.e_shentsize);
e_shstrndx = be16_to_cpu(eh.e_shstrndx);
e_shoff = be32_to_cpu(eh.e_shoff);
} else {
e_shnum = eh.e_shnum;
e_shentsize = eh.e_shentsize;
e_shstrndx = eh.e_shstrndx;
e_shoff = eh.e_shoff;
}
nvt_dbg(FW, "%s: e_shoff = %d, e_shentsize*e_shstrndx = %d\n", __func__,
e_shoff, e_shentsize*e_shstrndx);
/*get section name string*/
nvt_fw_elf_seek(fw_buffer, e_shoff + e_shentsize*e_shstrndx,
ELF_SEEK_SET);
nvt_fw_elf_read(((u8 *)(&sh_str)), e_shentsize, 1, fw_buffer);
if (eh.e_ident[EI_DATA] == ELFDATA2MSB) {
be32_to_cpus(&sh_str.sh_name);
be32_to_cpus(&sh_str.sh_type);
be32_to_cpus(&sh_str.sh_flags);
be32_to_cpus(&sh_str.sh_addr);
be32_to_cpus(&sh_str.sh_offset);
be32_to_cpus(&sh_str.sh_size);
be32_to_cpus(&sh_str.sh_link);
be32_to_cpus(&sh_str.sh_info);
be32_to_cpus(&sh_str.sh_addralign);
be32_to_cpus(&sh_str.sh_entsize);
}
nvt_dbg(FW, "%s: sh_str.sh_size=%d\n", __func__, sh_str.sh_size);
if (sh_str.sh_size <= 0) {
nvt_dbg(FW, "%s: sh_str.sh_size is incorrect!!\n", __func__);
ret = -1;
goto invalid_sh_size;
}
sh_name_tbl = kmalloc(sh_str.sh_size, GFP_KERNEL);
if (sh_name_tbl == NULL) {
nvt_dbg(FW, "%s: could not kamlloc sh_name_tbl\n", __func__);
ret = -1;
goto alloc_sh_name_tbl_failed;
}
nvt_fw_elf_seek(fw_buffer, sh_str.sh_offset, ELF_SEEK_SET);
nvt_fw_elf_read(sh_name_tbl, sh_str.sh_size, 1, fw_buffer);
total_size = 0;
while (e_shnum) {
nvt_dbg(FW, "%s: e_shnum=%x, e_shoff=%x\n", __func__, e_shnum,
e_shoff);
nvt_fw_elf_seek(fw_buffer, e_shoff, ELF_SEEK_SET);
if (nvt_fw_elf_read(((u8 *)(&sh)), e_shentsize, 1,
fw_buffer) == 0) {
break;
}
if (eh.e_ident[EI_DATA] == ELFDATA2MSB) {
be32_to_cpus(&sh.sh_name);
be32_to_cpus(&sh.sh_type);
be32_to_cpus(&sh.sh_flags);
be32_to_cpus(&sh.sh_addr);
be32_to_cpus(&sh.sh_offset);
be32_to_cpus(&sh.sh_size);
be32_to_cpus(&sh.sh_link);
be32_to_cpus(&sh.sh_info);
be32_to_cpus(&sh.sh_addralign);
be32_to_cpus(&sh.sh_entsize);
}
/* skip first section and string section */
if ((sh.sh_type == SHT_NULL) || (sh.sh_type == SHT_STRTAB)) {
e_shoff += e_shentsize;
e_shnum--;
continue;
}
/* skip .bss */
if ((sh.sh_flags & SHF_ALLOC) && (sh.sh_type == SHT_PROGBITS)) {
nvt_dbg(FW, "%s: load section: %s\n", __func__,
sh_name_tbl + sh.sh_name);
ret = nvt_fw_elf_data_program(bus, sh, (u8 *)fw_buffer);
if (ret < 0) {
goto fm_download_failed;
}
total_size += ret;
}
e_shnum--;
e_shoff += sizeof(Elf32_Shdr);
}
nvt_dbg(FW, "%s: Total load size=%d\n", __func__, total_size);
nvt_dbg(FW, "%s: starting config patch download...\n" , __func__);
ret = nvt_config_patch_download(bus, false);
if (ret < 0) {
nvt_dbg(ERROR, "%s: config patch file download fail\n",
__func__);
ret = 0;
/* the driver will go on even if the config patch file
download fail.*/
}
nvt_fw_fm_goto_address(bus, &entry_address);
fm_download_failed:
kfree(sh_name_tbl);
alloc_sh_name_tbl_failed:
invalid_sh_size:
invalid_format:
return ret;
}
/**
* nvt_fw_download - Download the firmware when driver is inserted into kernel
* @nvt_bus: nvt bus structure
* @mode: STA, AP, or HUT mode
*
* Return: 0:success, a negative number:fail
*/
s32 nvt_fw_download(struct _nvt_bus *nvt_bus, enum nvt_fw_mode mode)
{
s32 retval = 0;
s32 err;
s8 *fw_name;
s32 fw_type;
const struct firmware *fw = NULL;
struct _nvt_diag_req *diag_req = NULL;
u32 data;
//20151218 nash: coverity#48986
s32 pkt_len = 0;
nvt_dbg(FW, "%s: Start fw downloading\n", __func__);
if (nvt_bus == NULL) {
nvt_dbg(ERROR, "%s: nvt_bus is NULL\n", __func__);
return -1;
}
#if (NVT_FW_CFG_DOWNLOAD_BY_CHIPID == 1)
fw_name = nvt_fw_get_name(nvt_bus, mode);
nvt_dbg(INFO, "The filename of firmware: %s\n", fw_name);
#else
switch (mode) {
case NVT_FW_STA:
fw_name = NVT_FW_NAME_STA;
break;
case NVT_FW_AP:
fw_name = NVT_FW_NAME_AP;
break;
case NVT_FW_HUT:
fw_name = NVT_FW_NAME_HUT;
break;
default:
return -1;
}
#endif
err = request_firmware(&fw, fw_name, nvt_bus->dev);
if (err < 0) {
nvt_dbg(FW, "%s: request_firmware() fail\n", __func__);
return -1;
}
fw_type = nvt_fw_get_type(nvt_bus);
if (fw_type < 0) {
nvt_dbg(ERROR, "%s: get fw type fail\n", __func__);
retval = fw_type;
goto error;
}
//20151127 nash: if fw is not in bootloader, force it to bootloader
if (fw_type != NVT_FW_TYPE_BOOTLOADER) {
diag_req = kzalloc(sizeof(struct _nvt_diag_req), GFP_KERNEL);
if (diag_req == NULL) {
nvt_dbg(DIAG, "%s: kzalloc is for rep failed\n",
__func__);
retval = -1;
goto error;
}
/* trigger warm boot */
data = cpu_to_le32(1);
nvt_diag_pack_req(NVT_DIAG_CMD_CLASS_0, NVT_DIAG_REBOOT_CMD, 0,
4, (u8 *)&data, diag_req, &pkt_len);
retval = nvt_bus->nvt_wdev_bus_ops.tx_ctrl(nvt_bus,
(u8 *)diag_req, pkt_len);
if (retval < 0) {
nvt_dbg(ERROR, "%s: bus tx_ctrl error\n", __func__);
retval = -1;
goto error;
}
}
//20161020 nash: delay time for FW to do warm boot
msleep(10);
err = nvt_fw_program_in_elf(nvt_bus,
(fw->data + NVT_FW_IMAGE_HEADER_LEN));
if (err < 0) {
//nash:TODO
nvt_dbg(FW, "%s: program fw fail\n", __func__);
retval = -1;
goto error;
}
/* Wait FW ready event */
nvt_dbg(INFO, "%s: wait FW ready...\n", __func__);
nvt_bus->fw_rdy_completed = 0;
if (wait_event_timeout(nvt_bus->fw_rdy_wait,
nvt_bus->fw_rdy_completed,
msecs_to_jiffies(NVT_WAIT_FW_RDY_TIMEOUT))
== 0) {
nvt_dbg(ERROR, "%s: wait FW ready timeout\n",
__func__);
//retval = -1;
goto error;
}
nvt_dbg(INFO, "%s: DONE\n", __func__);
nvt_dbg(FW, "%s: download to FW(%d) ok!\n", __func__, mode);
error:
if (fw) {
release_firmware(fw);
}
//20151218 nash: coverity#48966
if (diag_req) {
kfree(diag_req);
}
return retval;
}
/**
* nvt_fw_change_opmode - download firmware to check operation mode
* @nvt_if: nvt_if structure
* @mode: sta, ap, or hut mode
*
* Return 0:success, a negative value:fail
*/
s32 nvt_fw_change_opmode(struct _nvt_if *nvt_if, enum nvt_fw_mode mode)
{
s32 ret;
struct _nvt_bus *nvt_bus;
if (nvt_if == NULL) {
nvt_dbg(ERROR, "%s: nvt_if is NULL\n", __func__);
return -1;
}
//20150806 nash: get correct pointer of nvt_bus
//nvt_bus = dev_get_drvdata(nvt_if->nvt_adapter->nvt_bus.dev);
nvt_bus = nvt_if->nvt_adapter->nvt_bus;
ret = nvt_fw_download(nvt_bus, mode);
if (ret < 0) {
nvt_dbg(ERROR, "%s: fw download fail\n", __func__);
goto alloc_failed;
}
//20150709 nash: record mode
nvt_if->mode = mode;
//20150722 nash: set mac address
nvt_set_macaddress_to_fw(nvt_bus->nvt_adapter);
/* remove network interface when changing to HUT mode */
if (NVT_FW_HUT == mode) {
nvt_del_all_if(nvt_bus->nvt_adapter);
}
alloc_failed:
return ret;
}
/**
* nvt_fw_cold_boot - do firmware cold boot
* @nvt_if: _nvt_if structure
*
*
* Return: 0:success, -1:fail
*/
s32 nvt_fw_cold_boot(struct _nvt_if *nvt_if)
{
struct _nvt_diag_req *diag_req;
struct _nvt_bus *nvt_bus;
s32 ret = 0;
//20151218 nash: coverity#48984
s32 pkt_len = 0;
u32 data;
diag_req = kzalloc(sizeof(struct _nvt_diag_req), GFP_KERNEL);
if (diag_req == NULL) {
nvt_dbg(DIAG, "%s: kzalloc is failed for req\n", __func__);
ret = -1;
goto alloc_failed;
}
nvt_bus = nvt_if->nvt_adapter->nvt_bus;
data = 0;
data = cpu_to_le32(data);
ret = nvt_diag_pack_req(NVT_DIAG_CMD_CLASS_0, NVT_DIAG_REBOOT_CMD, 0x00,
4, (u8 *)&data, diag_req, &pkt_len);
//fix CID #61625 by jerry lin.
if (ret < 0) {
nvt_dbg(FW, "%s: failed to pack diag req!!\n", __func__);
ret = -1;
goto alloc_failed;
}
ret = nvt_bus->nvt_wdev_bus_ops.tx_ctrl(nvt_bus, (u8 *)diag_req,
pkt_len);
if (ret < 0) {
nvt_dbg(FW, "%s: failed to send diag req!!\n", __func__);
ret = -1;
goto alloc_failed;
}
alloc_failed:
kfree(diag_req);
return ret;
}
/**
* nvt_fw_deep_sleep_resume - resume follow for deep sleep
* @nvt_bus: _nvt_bus struct
*
* Return: 0: success, others: fail
*/
s32 nvt_fw_deep_sleep_resume(struct _nvt_bus *nvt_bus)
{
s32 ret;
s32 entry_addr;
if (nvt_bus == NULL) {
return -EINVAL;
}
ret = nvt_config_patch_download(nvt_bus, true);
if (ret) {
nvt_dbg(ERROR, "%s: config patch download fail!!\n",
__func__);
return ret;
}
entry_addr = 4;
ret = nvt_fw_fm_goto_address(nvt_bus, &entry_addr);
if (ret) {
nvt_dbg(ERROR, "%s: goto addr fail!!\n", __func__);
return ret;
}
//waiting FW ready event
nvt_bus->fw_rdy_completed = 0;
if (wait_event_timeout(nvt_bus->fw_rdy_wait,
nvt_bus->fw_rdy_completed,
msecs_to_jiffies(NVT_WAIT_FW_RDY_TIMEOUT)) == 0) {
nvt_dbg(ERROR, "%s: wait FW ready timeout\n", __func__);
return -1;
}
return 0;
}
/**
* nvt_fw_request_cpf - request config patch file before suspend for deep sleep
* @nvt_bus: _nvt_bus struct
*
* Return: 0: success, others: fail
*/
s32 nvt_fw_request_cpf(struct _nvt_bus *nvt_bus)
{
s32 ret;
s8 *config_path_file_name = NVT_FW_CONFIG_PATCH_FILE_NAME;
nvt_dbg(FW, "%s: enter\r\n", __func__);
/*sanity check*/
if (nvt_bus == NULL) {
nvt_dbg(ERROR, "%s nvt_bus is NULL\r\n", __func__);
return -1;
}
ret = request_firmware(&resume_cfg_fw,
config_path_file_name,
nvt_bus->dev);
if (ret < 0) {
nvt_dbg(ERROR, "%s failed to config patch file fail!!",
__func__);
return -1;
}
return 0;
}
/* nvt_set_firmware_capability
* ..note: get f/w capability and update the nvt_fw_capability define
* in nvt_adapter */
void nvt_set_firmware_capability(struct _nvt_adapter *nvt_adapter)
{
s32 val_len = 0;
u8 resp[512] = {0};
u32 buff_size = 512;
u8 wid_val_pos = 0;
struct _nvt_bus *nvt_bus = nvt_adapter->nvt_bus;
struct _nvt_fw_capability *fw_cap;
val_len = nvt_get_wid_value(nvt_bus, WID_FIRMWARE_INFO,
resp, buff_size, &wid_val_pos);
fw_cap = (struct _nvt_fw_capability *)(resp + wid_val_pos);
memcpy(&nvt_adapter->nvt_fw_cap, (u8 *)fw_cap,
sizeof(struct _nvt_fw_capability));
}
/**
* nvt_fw_get_name - get corresponding firmware filename
* @nvt_bus: _nvt_bus struct
* @mode: nvt_fw_mode
*
* Return: the firmware filename
*/
#if (NVT_FW_CFG_DOWNLOAD_BY_CHIPID == 1)
static s8 *nvt_fw_get_name(struct _nvt_bus *nvt_bus, enum nvt_fw_mode mode)
{
int i;
s32 ret;
s32 pkt_len = 0;
s32 *chip_id;
s8 *filename = NULL;
struct _nvt_diag_req *diag_req = NULL;
struct _nvt_diag_resp *diag_resp = NULL;
struct _nvt_fw_name *fw_name_entry = NULL;
diag_req = kzalloc(sizeof(struct _nvt_diag_req), GFP_KERNEL);
if (diag_req == NULL) {
nvt_dbg(FW, "%s: diag_req kzalloc is failed\n", __func__);
ret = -1;
goto alloc_failed;
}
diag_resp = kzalloc(sizeof(struct _nvt_diag_resp), GFP_KERNEL);
if (diag_resp == NULL) {
nvt_dbg(DIAG, "%s: diag_resp kzalloc is failed\n", __func__);
ret = -1;
goto alloc_failed;
}
/* Get chip id first */
nvt_diag_pack_req(NVT_DIAG_CMD_CLASS_0, NVT_DIAG_GET_CHIP_ID_CMD, 0, 0,
NULL, diag_req, &pkt_len);
ret = nvt_bus->nvt_wdev_bus_ops.tx_ctrl(nvt_bus,
(u8 *)diag_req, pkt_len);
if (ret < 0) {
nvt_dbg(FW, "%s: failed to send diag req!!\n", __func__);
goto alloc_failed;
}
ret = nvt_bus->nvt_wdev_bus_ops.rx_ctrl(nvt_bus, (u8 *)diag_resp,
sizeof(struct _nvt_diag_resp));
if (ret < 0) {
nvt_dbg(FW, "%s: failed to recv diag resp!!\n", __func__);
goto alloc_failed;
}
chip_id = (s32 *)(diag_resp->sel.get_chip_id.chip_id);
if (diag_resp->err_code != 0) {
*chip_id = 0x18202;
}
ret = *chip_id;
nvt_dbg(FW, "%s: Chip ID=0x%x\n", __func__, ret);
for (i = 0; i < sizeof(fw_name_table)/sizeof(struct _nvt_fw_name);
i++) {
if (fw_name_table[i].chip_id == *chip_id &&
fw_name_table[i].mode == mode) {
fw_name_entry = &fw_name_table[i];
filename = fw_name_entry->filename;
break;
}
}
alloc_failed:
kfree(diag_req);
kfree(diag_resp);
return filename;
}
#endif