527 lines
14 KiB
C
527 lines
14 KiB
C
/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
|
|
/*
|
|
* common/rtk_fw.c
|
|
*
|
|
* Copyright (C) 2020 Amlogic, Inc. All rights reserved.
|
|
*
|
|
*/
|
|
|
|
/******************************************************************************
|
|
* Copyright (c) 2009-2015 by Hisi.
|
|
* All rights reserved.
|
|
*
|
|
* Create by Chenzetian. 2015-11-16
|
|
*
|
|
******************************************************************************/
|
|
|
|
#include <common.h>
|
|
#include <command.h>
|
|
#include <asm/processor.h>
|
|
#include <linux/compiler.h>
|
|
#include <linux/ctype.h>
|
|
#include <asm/byteorder.h>
|
|
#include <asm/unaligned.h>
|
|
|
|
#include <malloc.h>
|
|
#include <usb.h>
|
|
#include <rtk_fw.h>
|
|
|
|
#define USB_ERROR(fmt, args...) printf(fmt, ##args)
|
|
#define USB_DEBUG(fmt, args...) printf(fmt, ##args)
|
|
#define CHIP_TYPE_RTL8723BU "fw_8723b_b.txt"
|
|
#define CHIP_TYPE_RTL8723DU "fw_8723du_c.txt"
|
|
#define CHIP_TYPE_RTL8822BU "fw_8822b_b.txt"
|
|
#define CODE_MAXSIZE_24K (1024*24) //24K
|
|
#define CODE_MAXSIZE_40K (1024*40) //40K
|
|
#define code_maxsize 23639//56 * 10K
|
|
int fw_8723bu=0;
|
|
int fw_8723du=0;
|
|
int fw_8822bu=0;
|
|
|
|
//patch txt formate: total length(2 bytes) || fw patch || config patch
|
|
static unsigned char lu8Code_RTL8723BU[CODE_MAXSIZE_24K] = {
|
|
#include CHIP_TYPE_RTL8723BU
|
|
};
|
|
static unsigned char lu8Code_RTL8723DU[CODE_MAXSIZE_40K] = {
|
|
#include CHIP_TYPE_RTL8723DU
|
|
};
|
|
static unsigned char lu8Code_RTL8822BU[code_maxsize] = {
|
|
#include CHIP_TYPE_RTL8822BU
|
|
};
|
|
|
|
|
|
firmware_info *firmware_info_init(struct usb_device *dev)
|
|
{
|
|
struct usb_device *udev = dev;
|
|
firmware_info *fw_info;
|
|
|
|
USB_DEBUG("%s[%d]: start.\n", __func__, __LINE__);
|
|
|
|
fw_info = malloc(sizeof(*fw_info));
|
|
memset(fw_info,0,sizeof(*fw_info));
|
|
|
|
if (!fw_info)
|
|
return NULL;
|
|
|
|
if (fw_8723bu) {
|
|
fw_info->fw_data = malloc(CODE_MAXSIZE_24K);
|
|
memset(fw_info->fw_data,0,CODE_MAXSIZE_24K);
|
|
}
|
|
|
|
if (fw_8723du) {
|
|
fw_info->fw_data = malloc(CODE_MAXSIZE_40K);
|
|
memset(fw_info->fw_data,0,CODE_MAXSIZE_40K);
|
|
}
|
|
if (fw_8822bu) {
|
|
fw_info->fw_data = malloc(code_maxsize);
|
|
memset(fw_info->fw_data,0,code_maxsize);
|
|
//USB_DEBUG("zengqiu %s[%d]: start.\n", __func__, __LINE__);
|
|
}
|
|
|
|
if (!fw_info->fw_data) {
|
|
free(fw_info);
|
|
return NULL;
|
|
}
|
|
|
|
fw_info->send_pkt = malloc(PKT_LEN);
|
|
memset(fw_info->send_pkt,0,PKT_LEN);
|
|
if (!fw_info->send_pkt) {
|
|
free(fw_info->fw_data);
|
|
free(fw_info);
|
|
return NULL;
|
|
}
|
|
|
|
fw_info->rcv_pkt = malloc(PKT_LEN);
|
|
memset(fw_info->rcv_pkt,0,PKT_LEN);
|
|
if (!fw_info->rcv_pkt) {
|
|
free(fw_info->fw_data);
|
|
free(fw_info->send_pkt);
|
|
free(fw_info);
|
|
return NULL;
|
|
}
|
|
|
|
fw_info->patch_entry = NULL;
|
|
|
|
|
|
fw_info->intf = NULL;
|
|
fw_info->udev = udev;
|
|
fw_info->pipe_in = usb_rcvintpipe(fw_info->udev, INTR_EP);
|
|
fw_info->pipe_out = usb_sndctrlpipe(fw_info->udev, CTRL_EP);
|
|
fw_info->cmd_hdr = (struct hci_command_hdr *)(fw_info->send_pkt);
|
|
fw_info->evt_hdr = (struct hci_event_hdr *)(fw_info->rcv_pkt);
|
|
fw_info->cmd_cmp = (struct hci_ev_cmd_complete *)(fw_info->rcv_pkt + EVT_HDR_LEN);
|
|
fw_info->req_para = fw_info->send_pkt + CMD_HDR_LEN;
|
|
fw_info->rsp_para = fw_info->rcv_pkt + EVT_HDR_LEN + CMD_CMP_LEN;
|
|
|
|
return fw_info;
|
|
}
|
|
|
|
void firmware_info_destroy(firmware_info *fw_info)
|
|
{
|
|
free(fw_info->fw_data);
|
|
free(fw_info->rcv_pkt);
|
|
free(fw_info->send_pkt);
|
|
free(fw_info);
|
|
}
|
|
int load_firmware(firmware_info *fw_info, unsigned char **buff)
|
|
{
|
|
unsigned int size;
|
|
|
|
if (fw_8723bu) {
|
|
size = lu8Code_RTL8723BU[0] | (lu8Code_RTL8723BU[1] << 8);
|
|
USB_DEBUG("%s,size is %d\n",__func__, size);
|
|
|
|
if (size >= CODE_MAXSIZE_24K) {
|
|
USB_ERROR("%s,size is %d firmware is larger than %d\n",__func__, size, CODE_MAXSIZE_24K);
|
|
return -1;
|
|
}
|
|
|
|
memcpy(*buff, &lu8Code_RTL8723BU[2],size);
|
|
fw_8723bu = 0;
|
|
return size;
|
|
}
|
|
|
|
if (fw_8723du) {
|
|
size = lu8Code_RTL8723DU[0] | (lu8Code_RTL8723DU[1] << 8);
|
|
USB_DEBUG("%s,size is %d\n",__func__, size);
|
|
|
|
if (size >= CODE_MAXSIZE_40K) {
|
|
USB_ERROR("%s,size is %d firmware is larger than %d\n",__func__, size, CODE_MAXSIZE_40K);
|
|
return -1;
|
|
}
|
|
|
|
memcpy(*buff, &lu8Code_RTL8723DU[2],size);
|
|
fw_8723du= 0;
|
|
return size;
|
|
}
|
|
if (fw_8822bu) {
|
|
size = lu8Code_RTL8822BU[0] | (lu8Code_RTL8822BU[1] << 8);
|
|
USB_DEBUG("%s,size is %d\n",__func__, size);
|
|
|
|
memcpy(*buff, &lu8Code_RTL8822BU[2],size);
|
|
fw_8822bu = 0;
|
|
return size;
|
|
}
|
|
return 0;
|
|
}
|
|
int send_hci_cmd(firmware_info *fw_info)
|
|
{
|
|
int ret_val;
|
|
|
|
ret_val = usb_control_msg(
|
|
fw_info->udev, fw_info->pipe_out,
|
|
0, USB_TYPE_CLASS, 0, 0,
|
|
(void *)(fw_info->send_pkt),
|
|
fw_info->pkt_len, MSG_TO);
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
|
|
int rcv_hci_evt(firmware_info *fw_info)
|
|
{
|
|
int /*ret_len = 0,*/ ret_val = 0;
|
|
int i;
|
|
|
|
while (1) {
|
|
for (i = 0; i < 5; i++) {
|
|
ret_val = usb_submit_int_msg(
|
|
fw_info->udev, fw_info->pipe_in,
|
|
(void *)(fw_info->rcv_pkt), PKT_LEN,
|
|
MSG_TO);
|
|
if (ret_val >= 0)
|
|
break;
|
|
|
|
}
|
|
/*USB_DEBUG("fw_info->rcv_pkt : 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x \n",
|
|
fw_info->rcv_pkt[0],
|
|
fw_info->rcv_pkt[1],
|
|
fw_info->rcv_pkt[2],
|
|
fw_info->rcv_pkt[3],
|
|
fw_info->rcv_pkt[4],
|
|
fw_info->rcv_pkt[5],
|
|
fw_info->rcv_pkt[6]);*/
|
|
if (ret_val < 0)
|
|
return ret_val;
|
|
|
|
if (CMD_CMP_EVT == fw_info->evt_hdr->evt) {
|
|
if (fw_info->cmd_hdr->opcode == fw_info->cmd_cmp->opcode)
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
int get_firmware(firmware_info *fw_info)
|
|
{
|
|
/* patch_info *patch_entry = fw_info->patch_entry; */
|
|
|
|
fw_info->fw_len = load_firmware(fw_info, &fw_info->fw_data);
|
|
USB_DEBUG("%s[%d]: fw_len=%d\n", __func__, __LINE__, fw_info->fw_len);
|
|
if (fw_info->fw_len <= 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
int reset_controller(firmware_info* fw_info)
|
|
{
|
|
int ret_val;
|
|
USB_DEBUG("reset_controller \n");
|
|
if (!fw_info)
|
|
return -ENODEV;
|
|
fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VENDOR_FORCE_RESET_AND_PATCHABLE);
|
|
fw_info->cmd_hdr->plen = 0;
|
|
fw_info->pkt_len = CMD_HDR_LEN;
|
|
ret_val = send_hci_cmd(fw_info);
|
|
if (ret_val < 0) {
|
|
USB_DEBUG("%s: Failed to send hci cmd 0x%04x, errno %d \n",
|
|
__func__, fw_info->cmd_hdr->opcode, ret_val);
|
|
return ret_val;
|
|
}
|
|
mdelay(200);
|
|
USB_DEBUG("%s: Wait fw reset for 200ms \n",__func__);
|
|
return ret_val;
|
|
}
|
|
|
|
int download_data(firmware_info *fw_info)
|
|
{
|
|
download_cp *cmd_para;
|
|
download_rp *evt_para;
|
|
unsigned char *pcur;
|
|
int pkt_len, frag_num, frag_len;
|
|
int i, ret_val;
|
|
int ncmd = 1, step = 1;
|
|
|
|
//USB_DEBUG("%s: start\n", __func__);
|
|
|
|
cmd_para = (download_cp *)fw_info->req_para;
|
|
evt_para = (download_rp *)fw_info->rsp_para;
|
|
pcur = fw_info->fw_data;
|
|
pkt_len = CMD_HDR_LEN + sizeof(download_cp);
|
|
frag_num = fw_info->fw_len / PATCH_SEG_MAX + 1;
|
|
frag_len = PATCH_SEG_MAX;
|
|
|
|
cmd_para->index = 0;
|
|
for (i = 0; i < frag_num; i++) {
|
|
if (i == (frag_num - 1)) {
|
|
cmd_para->index |= DATA_END;
|
|
frag_len = fw_info->fw_len % PATCH_SEG_MAX;
|
|
pkt_len -= (PATCH_SEG_MAX - frag_len);
|
|
}
|
|
fw_info->cmd_hdr->opcode = cpu_to_le16(DOWNLOAD_OPCODE);
|
|
fw_info->cmd_hdr->plen = sizeof(unsigned char) + frag_len;
|
|
fw_info->pkt_len = pkt_len;
|
|
memcpy(cmd_para->data, pcur, frag_len);
|
|
|
|
if (step > 0) {
|
|
ret_val = send_hci_cmd(fw_info);
|
|
if (ret_val < 0) {
|
|
//USB_ERROR("%s: Failed to send frag num %d\n", __func__, cmd_para->index);
|
|
return ret_val;
|
|
} else
|
|
//USB_DEBUG("%s: Send frag num %d\n", __func__, cmd_para->index);
|
|
|
|
if (--step > 0 && i < frag_num - 1) {
|
|
//USB_DEBUG("%s: Continue to send frag num %d\n", __func__, cmd_para->index + 1);
|
|
pcur += PATCH_SEG_MAX;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
while (ncmd > 0) {
|
|
ret_val = rcv_hci_evt(fw_info);
|
|
if (ret_val < 0) {
|
|
//USB_ERROR("%s: rcv_hci_evt err %d\n", __func__, ret_val);
|
|
return ret_val;
|
|
} else {
|
|
//USB_DEBUG("%s: Receive acked frag num %d\n", __func__, evt_para->index);
|
|
ncmd--;
|
|
}
|
|
|
|
if (0 != evt_para->status) {
|
|
//USB_ERROR("%s: Receive acked frag num %d, err status %d\n",
|
|
// __func__, ret_val, evt_para->status);
|
|
return -1;
|
|
}
|
|
|
|
if ((evt_para->index & DATA_END) || (evt_para->index == frag_num - 1)) {
|
|
//USB_DEBUG("%s: Receive last acked index %d\n", __func__, evt_para->index);
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
cmd_para->index ++;
|
|
if (cmd_para->index == 0x80)
|
|
cmd_para->index = 1;
|
|
ncmd = step = fw_info->cmd_cmp->ncmd;
|
|
pcur += PATCH_SEG_MAX;
|
|
//USB_DEBUG("%s: HCI command packet num %d\n", __func__, ncmd);
|
|
}
|
|
|
|
/*
|
|
* It is tricky that Host cannot receive DATA_END index from BT
|
|
* controller, at least for 8723au. We are doomed if failed.
|
|
*/
|
|
|
|
end:
|
|
// USB_DEBUG("%s: done, sent %d frag pkts, received %d frag events\n",
|
|
// __func__, cmd_para->index, evt_para->index);
|
|
return fw_info->fw_len;
|
|
}
|
|
|
|
int set_wakeup_device(firmware_info* fw_info)
|
|
{
|
|
struct rtk_eversion_evt *ever_evt;
|
|
int ret_val;
|
|
|
|
static unsigned char bdaddr[7] = {0x00, 0xd7, 0xe3,0x3e,0xf8, 0xe6,0xa0};
|
|
if (!fw_info)
|
|
return -ENODEV;
|
|
|
|
fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VENDOR_SET_WAKE_UP_DEVICE);
|
|
fw_info->cmd_hdr->plen = 7;
|
|
memcpy(fw_info->req_para, bdaddr, 7);
|
|
fw_info->pkt_len = CMD_HDR_LEN + 7;
|
|
|
|
ret_val = send_hci_cmd(fw_info);
|
|
if (ret_val < 0) {
|
|
USB_ERROR("%s: Failed to send hci cmd 0x%04x, errno %d\n",
|
|
__func__, fw_info->cmd_hdr->opcode, ret_val);
|
|
return ret_val;
|
|
}
|
|
|
|
ret_val = rcv_hci_evt(fw_info);
|
|
if (ret_val < 0) {
|
|
USB_ERROR("%s: Failed to receive hci event, errno %d\n",
|
|
__func__, ret_val);
|
|
return ret_val;
|
|
}
|
|
|
|
ever_evt = (struct rtk_eversion_evt *)(fw_info->rsp_para);
|
|
|
|
USB_DEBUG("%s: status %d, eversion %d", __func__, ever_evt->status, ever_evt->version);
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
|
|
int check_chip_fw_version(firmware_info* fw_info)
|
|
{
|
|
struct rtk_localversion_evt *ever_evt;
|
|
int ret_val;
|
|
|
|
if (!fw_info)
|
|
return -ENODEV;
|
|
|
|
fw_info->cmd_hdr->opcode = cpu_to_le16(HCI_VENDOR_READ_LMP_VERSION);
|
|
fw_info->cmd_hdr->plen = 0;
|
|
fw_info->pkt_len = CMD_HDR_LEN;
|
|
|
|
ret_val = send_hci_cmd(fw_info);
|
|
if (ret_val < 0) {
|
|
USB_ERROR("%s: Failed to send hci cmd 0x%04x, errno %d\n",
|
|
__func__, fw_info->cmd_hdr->opcode, ret_val);
|
|
return ret_val;
|
|
}
|
|
|
|
ret_val = rcv_hci_evt(fw_info);
|
|
if (ret_val < 0) {
|
|
USB_ERROR("%s: Failed to receive hci event, errno %d\n",
|
|
__func__, ret_val);
|
|
return ret_val;
|
|
}
|
|
|
|
ever_evt = (struct rtk_localversion_evt *)(fw_info->rsp_para);
|
|
|
|
USB_DEBUG("%s: status %x \n", __func__, ever_evt->status);
|
|
USB_DEBUG("%s: hci_version %x \n", __func__, ever_evt->hci_version);
|
|
USB_DEBUG("%s: hci_revision %x \n", __func__, ever_evt->hci_revision);
|
|
USB_DEBUG("%s: lmp_version %x \n", __func__, ever_evt->lmp_version);
|
|
USB_DEBUG("%s: lmp_subversion %x \n", __func__, ever_evt->lmp_subversion);
|
|
USB_DEBUG("%s: lmp_manufacture %x \n", __func__, ever_evt->lmp_manufacture);
|
|
|
|
return ret_val;
|
|
}
|
|
#if 0 // just fro reference
|
|
void load_rtl_firmware(void){
|
|
firmware_info *fw_info;
|
|
int ret_val,i;
|
|
struct usb_device *dev;
|
|
|
|
usb_init();
|
|
|
|
/* scan all USB Devices */
|
|
dev = NULL;
|
|
for (i=0;i<USB_MAX_DEVICE;i++) {
|
|
dev=usb_get_dev_index(i); /* get device */
|
|
if (dev == NULL)
|
|
return;
|
|
if (dev->devnum != -1) {
|
|
if (!strcmp(dev->mf,"Realtek") && !strcmp(dev->prod,"Bluetooth Radio")) {
|
|
/* Download BT Fireware */
|
|
printf("Now Download BT Firmware.\n");
|
|
fw_info = firmware_info_init(dev);
|
|
get_firmware(fw_info);
|
|
ret_val = download_data(fw_info);
|
|
if (ret_val > 0)
|
|
printf("Download fw patch done, fw len %d\n",ret_val);
|
|
mdelay(10);
|
|
set_wakeup_device(fw_info);
|
|
// set_bt_wakeup(fw_info);
|
|
firmware_info_destroy(fw_info);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
struct usb_device * get_rtl_dev(void){
|
|
int i;
|
|
struct usb_device *dev;
|
|
/* scan all USB Devices */
|
|
dev = NULL;
|
|
for (i = 0; i < USB_MAX_DEVICE; i++) {
|
|
dev=usb_get_dev_index(i); /* get device */
|
|
if (dev == NULL) {
|
|
printf("usb dev is null!!!!\n");
|
|
return NULL;
|
|
}
|
|
printf("i=%d dev->devnum:%d dev->mf:%s dev->prod:%s\n", i, dev->devnum, dev->mf, dev->prod);
|
|
printf("vid is %x, pid is %x \n",le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct));
|
|
/* AML change for haier to match 8723bu bt */
|
|
if (dev->devnum != -1) {
|
|
if (le16_to_cpu(dev->descriptor.idVendor) == 0x0bda
|
|
&& le16_to_cpu(dev->descriptor.idProduct) == 0xb720){
|
|
fw_8723bu = 1;
|
|
return dev;
|
|
}
|
|
if (le16_to_cpu(dev->descriptor.idVendor) == 0x0bda
|
|
&& le16_to_cpu(dev->descriptor.idProduct) == 0xd723){
|
|
fw_8723du = 1;
|
|
return dev;
|
|
}
|
|
if (le16_to_cpu(dev->descriptor.idVendor) == 0x0bda
|
|
&& le16_to_cpu(dev->descriptor.idProduct) == 0xb82c){
|
|
fw_8822bu = 1;
|
|
return dev;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
int load_rtl_firmware_dev(struct usb_device *dev)
|
|
{
|
|
#if 0
|
|
char *reboot_mode = getenv("reboot_mode");
|
|
if (reboot_mode == NULL)
|
|
return 0;
|
|
if (reboot_mode&& strcmp("cold_boot", reboot_mode))
|
|
return 0;
|
|
#endif
|
|
firmware_info *fw_info;
|
|
int ret_val=-1;
|
|
|
|
/* Download BT Fireware */
|
|
USB_DEBUG("Now Download RTK BT Firmware. (version 20181220i_1)\n");
|
|
|
|
fw_info = firmware_info_init(dev);
|
|
if (NULL == fw_info)
|
|
return -1;
|
|
|
|
ret_val = get_firmware(fw_info);
|
|
printf("++++MB:{%s}[%d]...(get_firmware = [%d]).\n", __FUNCTION__, __LINE__, ret_val);
|
|
if (-1 == ret_val) {
|
|
printf("++++MB:{%s}[%d]...get firmware, errno %d \n", __FUNCTION__, __LINE__, ret_val);
|
|
return ret_val;
|
|
}
|
|
|
|
ret_val = check_chip_fw_version(fw_info);
|
|
printf("++++MB:{%s}[%d]...(check_chip_fw_version = [%d]).\n", __FUNCTION__, __LINE__, ret_val);
|
|
if (ret_val < 0) {
|
|
printf("++++MB:{%s}[%d]...Failed to check chip fw version, errno %d \n", __FUNCTION__, __LINE__, ret_val);
|
|
return ret_val;
|
|
}
|
|
|
|
ret_val = reset_controller(fw_info);
|
|
printf("++++MB:{%s}[%d]...(reset_controller = [%d]).\n", __FUNCTION__, __LINE__, ret_val);
|
|
if (ret_val < 0) {
|
|
printf("++++MB:{%s}[%d]...(reset controller error = [%d]).\n", __FUNCTION__, __LINE__, ret_val);
|
|
return ret_val;
|
|
}
|
|
|
|
ret_val = download_data(fw_info);
|
|
printf("++++MB:{%s}[%d]...(download_data = [%d]).\n", __FUNCTION__, __LINE__, ret_val);
|
|
if (ret_val > 0)
|
|
printf("Download fw patch done, fw len %d\n",ret_val);
|
|
else
|
|
{
|
|
firmware_info_destroy(fw_info);
|
|
return ret_val;
|
|
}
|
|
mdelay(10);
|
|
ret_val = set_wakeup_device(fw_info);
|
|
printf("++++MB:{%s}[%d]...(set_wakeup_device = [%d]).\n", __FUNCTION__, __LINE__, ret_val);
|
|
//set_bt_wakeup(fw_info);
|
|
firmware_info_destroy(fw_info);
|
|
return ret_val;
|
|
}
|
|
|