mirror of
https://gitee.com/Vancouver2017/luban-lite.git
synced 2025-12-16 17:18:56 +00:00
408 lines
9.9 KiB
C
408 lines
9.9 KiB
C
/*
|
|
* Copyright (C) 2015 Endless Mobile, Inc.
|
|
*
|
|
* Author: Thomas_li <thomas_li@realsil.com.cn>
|
|
*/
|
|
#include <stdio.h>
|
|
#include <aic_core.h>
|
|
#include <nimble/host/src/ble_hs_hci_priv.h>
|
|
#include <nimble/host/src/ble_hs_priv.h>
|
|
#include <realtek_bt_drv.h>
|
|
|
|
#include "inc/fw_rtl8733bs_d7b8_0da7.h"
|
|
#include "inc/config_rtl8733bs.h"
|
|
|
|
static const struct realtek_bt_module module_table[] = {
|
|
/* 8723BS */
|
|
{
|
|
.flags = RTL_FLAG_LMPSUBV | RTL_FLAG_HCIREV | RTL_FLAG_HCIVER | RTL_FLAG_HCIBUS,
|
|
.bus = HCI_UART,
|
|
.hci_ver = 0x6,
|
|
.hci_rev = 0xb,
|
|
.lmp_subver = RTL_ROM_LMP_8723B,
|
|
.config_needed = true,
|
|
.has_rom_version = true,
|
|
},
|
|
|
|
/* 8733BS */
|
|
{
|
|
.flags = RTL_FLAG_LMPSUBV | RTL_FLAG_HCIREV | RTL_FLAG_HCIVER | RTL_FLAG_HCIBUS,
|
|
.bus = HCI_UART,
|
|
.hci_ver = 0xb,
|
|
.hci_rev = 0xf,
|
|
.lmp_subver = RTL_ROM_LMP_8723B,
|
|
.config_needed = true,
|
|
.has_rom_version = true,
|
|
.fw = {
|
|
.data = fw_rtl8733bs_d7b8_0da7,
|
|
.len = sizeof(fw_rtl8733bs_d7b8_0da7)
|
|
},
|
|
.cfg = {
|
|
.data = config_rtl8733bs,
|
|
.len = sizeof(config_rtl8733bs)
|
|
}
|
|
},
|
|
|
|
/* 8723B */
|
|
{
|
|
.flags = RTL_FLAG_LMPSUBV | RTL_FLAG_HCIREV,
|
|
.hci_rev = 0xb,
|
|
.lmp_subver = RTL_ROM_LMP_8723B,
|
|
.config_needed = false,
|
|
.has_rom_version = true,
|
|
},
|
|
|
|
/* 8723D */
|
|
{
|
|
.flags = RTL_FLAG_LMPSUBV | RTL_FLAG_HCIREV,
|
|
.hci_rev = 0xd,
|
|
.lmp_subver = RTL_ROM_LMP_8723B,
|
|
.config_needed = true,
|
|
.has_rom_version = true,
|
|
},
|
|
|
|
/* 8723DS */
|
|
{
|
|
.flags = RTL_FLAG_LMPSUBV | RTL_FLAG_HCIREV | RTL_FLAG_HCIVER | RTL_FLAG_HCIBUS,
|
|
.bus = HCI_UART,
|
|
.hci_ver = 0x8,
|
|
.hci_rev = 0xd,
|
|
.lmp_subver = RTL_ROM_LMP_8723B,
|
|
.config_needed = true,
|
|
.has_rom_version = true,
|
|
},
|
|
|
|
/* 8723DU */
|
|
{
|
|
.flags = RTL_FLAG_LMPSUBV | RTL_FLAG_HCIREV,
|
|
.hci_rev = 0x826C,
|
|
.lmp_subver = RTL_ROM_LMP_8723D,
|
|
.config_needed = true,
|
|
.has_rom_version = true,
|
|
},
|
|
};
|
|
|
|
static struct realtek_bt_module *realtek_bt_get_module(u8 bus)
|
|
{
|
|
struct ble_hci_ip_rd_local_ver_rp rsp;
|
|
u8 hci_ver, lmp_ver __attribute__((unused));
|
|
u16 hci_rev, lmp_subver;
|
|
int ret = 0;
|
|
int i;
|
|
|
|
ret = ble_hs_hci_cmd_tx(BLE_HCI_OP(BLE_HCI_OGF_INFO_PARAMS,
|
|
BLE_HCI_OCF_IP_RD_LOCAL_VER), NULL,
|
|
0, &rsp, sizeof(rsp));
|
|
if (ret) {
|
|
pr_err("hci op read local version failed (%d)\n", ret);
|
|
return NULL;
|
|
}
|
|
|
|
hci_ver = rsp.hci_ver;
|
|
lmp_ver = rsp.lmp_ver;
|
|
hci_rev = le16_to_cpu(rsp.hci_rev);
|
|
lmp_subver = le16_to_cpu(rsp.lmp_subver);
|
|
|
|
pr_debug("hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x\n",
|
|
hci_ver, hci_rev, lmp_ver, lmp_subver);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(module_table); i++) {
|
|
if ((module_table[i].flags & RTL_FLAG_LMPSUBV) &&
|
|
(module_table[i].lmp_subver != lmp_subver))
|
|
continue;
|
|
if ((module_table[i].flags & RTL_FLAG_HCIREV) &&
|
|
(module_table[i].hci_rev != hci_rev))
|
|
continue;
|
|
if ((module_table[i].flags & RTL_FLAG_HCIVER) &&
|
|
(module_table[i].hci_ver != hci_ver))
|
|
continue;
|
|
if ((module_table[i].flags & RTL_FLAG_HCIBUS) &&
|
|
(module_table[i].bus != bus))
|
|
continue;
|
|
|
|
break;
|
|
}
|
|
|
|
if (i >= ARRAY_SIZE(module_table))
|
|
return NULL;
|
|
|
|
return (struct realtek_bt_module *)&module_table[i];
|
|
}
|
|
|
|
static int realtek_bt_read_local_version(void)
|
|
{
|
|
struct ble_hci_ip_rd_local_ver_rp rsp;
|
|
int ret = 0;
|
|
|
|
memset(&rsp, 0, sizeof(rsp));
|
|
ret = ble_hs_hci_cmd_tx(HCI_OP_READ_LOCAL_VERSION, NULL, 0, &rsp, sizeof(rsp));
|
|
if (ret) {
|
|
pr_err("HCI_OP_READ_LOCAL_VERSION failed (%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
pr_info("examining hci_ver=%02x hci_rev=%04x lmp_ver=%02x lmp_subver=%04x\n",
|
|
rsp.hci_ver, rsp.hci_rev, rsp.lmp_ver, rsp.lmp_subver);
|
|
pr_info("fw version 0x%04x%04x\n", rsp.hci_rev, rsp.lmp_subver);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int realtek_bt_read_rom_version(u8 *version)
|
|
{
|
|
struct realtek_bt_rom_version_evt rom_version;
|
|
int ret = 0;
|
|
|
|
/* Read realtek ROM version command */
|
|
ret = ble_hs_hci_cmd_tx(0xfc6d, NULL, 0, &rom_version, sizeof(rom_version));
|
|
if (ret) {
|
|
pr_err("Read ROM version failed (%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
pr_info("rom version=%x\n", rom_version.version);
|
|
|
|
*version = rom_version.version;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int realtek_bt_download_firmware(struct realtek_bt_patch *fw)
|
|
{
|
|
struct realtek_bt_dowmload_cmd *dl_cmd;
|
|
struct realtek_bt_dowmload_response dl_rsp;
|
|
unsigned char *data = fw->data;
|
|
int frag_num = fw->len / RTL_FRAG_LEN + 1;
|
|
int frag_len = RTL_FRAG_LEN;
|
|
int ret = 0;
|
|
int i;
|
|
|
|
dl_cmd = malloc(sizeof(struct realtek_bt_dowmload_cmd));
|
|
if (!dl_cmd)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < frag_num; i++) {
|
|
pr_info("download fw (%d/%d)\n", i, frag_num);
|
|
if (i > 0x7f)
|
|
dl_cmd->index = (i & 0x7f) + 1;
|
|
else
|
|
dl_cmd->index = i;
|
|
|
|
if (i == (frag_num - 1)) {
|
|
dl_cmd->index |= 0x80; /* data end */
|
|
frag_len = fw->len % RTL_FRAG_LEN;
|
|
}
|
|
memcpy(dl_cmd->data, data, frag_len);
|
|
|
|
/* Send download command */
|
|
ret = ble_hs_hci_cmd_tx(0xfc20, dl_cmd, frag_len + 1, &dl_rsp, sizeof(dl_rsp));
|
|
if (ret) {
|
|
pr_err("download fw command failed (%d)\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
data += RTL_FRAG_LEN;
|
|
}
|
|
|
|
ret = realtek_bt_read_local_version();
|
|
if (ret) {
|
|
pr_err("read local version failed\n");
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
free(dl_cmd);
|
|
return ret;
|
|
}
|
|
|
|
static int realtek_bt_module_init(struct realtek_bt_device *btdev)
|
|
{
|
|
int ret;
|
|
|
|
btdev->module = realtek_bt_get_module(HCI_UART);
|
|
if (!btdev->module) {
|
|
pr_warn("The firmware is up to date.\n");
|
|
return -1;
|
|
}
|
|
|
|
if (btdev->module->has_rom_version) {
|
|
ret = realtek_bt_read_rom_version(&btdev->rom_version);
|
|
if (ret) {
|
|
pr_err("read rom version failed (%d)\n", ret);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (btdev->module->fw.len <= 0) {
|
|
pr_err("firmware file %s not found\n", btdev->module->fw.name);
|
|
return -1;
|
|
}
|
|
|
|
if (btdev->module->config_needed) {
|
|
if (btdev->module->cfg.len <= 0) {
|
|
pr_err("config file %s not found\n", btdev->module->cfg.name);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int realtek_bt_convert_baudrate(u32 device_baudrate)
|
|
{
|
|
switch (device_baudrate) {
|
|
case 0x0252a00a:
|
|
return 230400;
|
|
|
|
case 0x05f75004:
|
|
return 921600;
|
|
|
|
case 0x00005004:
|
|
return 1000000;
|
|
|
|
case 0x04928002:
|
|
case 0x01128002:
|
|
return 1500000;
|
|
|
|
case 0x00005002:
|
|
return 2000000;
|
|
|
|
case 0x0000b001:
|
|
return 2500000;
|
|
|
|
case 0x04928001:
|
|
return 3000000;
|
|
|
|
case 0x052a6001:
|
|
return 3500000;
|
|
|
|
case 0x00005001:
|
|
return 4000000;
|
|
|
|
case 0x0252c014:
|
|
default:
|
|
return 115200;
|
|
}
|
|
}
|
|
|
|
static int realtek_bt_get_uart_settings(struct realtek_bt_device *btdev,
|
|
unsigned int *baudrate,
|
|
u32 *device_baudrate, bool *flow_control)
|
|
{
|
|
struct realtek_bt_vendor_config *config;
|
|
struct realtek_bt_vendor_config_entry *entry;
|
|
int i, total_data_len;
|
|
bool found = false;
|
|
|
|
total_data_len = btdev->module->cfg.len - 6;
|
|
if (total_data_len <= 0) {
|
|
pr_warn("no config loaded\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
config = (struct realtek_bt_vendor_config *)btdev->module->cfg.data;
|
|
if (le32_to_cpu(config->signature) != RTL_CONFIG_MAGIC) {
|
|
pr_err("invalid config magic\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (total_data_len < le16_to_cpu(config->total_len)) {
|
|
pr_err("config is too short\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < total_data_len; ) {
|
|
entry = ((void *)config->entry) + i;
|
|
|
|
switch (le16_to_cpu(entry->offset)) {
|
|
case 0xc:
|
|
if (entry->len < sizeof(*device_baudrate)) {
|
|
pr_err("invalid UART config entry\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
*device_baudrate = get_unaligned_le32(entry->data);
|
|
*baudrate = realtek_bt_convert_baudrate(*device_baudrate);
|
|
|
|
if (entry->len >= 13)
|
|
*flow_control = !!(entry->data[12] & BIT(2));
|
|
else
|
|
*flow_control = false;
|
|
|
|
found = true;
|
|
break;
|
|
|
|
default:
|
|
pr_debug("skipping config entry 0x%x (len %u)\n", le16_to_cpu(entry->offset), entry->len);
|
|
break;
|
|
}
|
|
|
|
i += sizeof(*entry) + entry->len;
|
|
}
|
|
|
|
if (!found) {
|
|
pr_err("no UART config entry found\n");
|
|
return -ENOENT;
|
|
}
|
|
|
|
pr_debug("device baudrate = 0x%08x\n", *device_baudrate);
|
|
pr_debug("controller baudrate = %u\n", *baudrate);
|
|
pr_debug("flow control %d\n", *flow_control);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void realtek_bt_init(void)
|
|
{
|
|
struct realtek_bt_device btdev;
|
|
u32 device_baudrate, baudrate;
|
|
bool flow_control;
|
|
int ret = 0;
|
|
|
|
ret = realtek_bt_module_init(&btdev);
|
|
if (ret) {
|
|
pr_err("realtek bluetooth initialize failed.\n");
|
|
return;
|
|
}
|
|
|
|
ret = realtek_bt_get_uart_settings(&btdev, &baudrate, &device_baudrate, &flow_control);
|
|
if (ret) {
|
|
pr_err("realtek bluetooth get uart settings failed.\n");
|
|
return;
|
|
}
|
|
|
|
//ret = ble_hs_hci_cmd_tx(0xfc17, &device_baudrate, sizeof(device_baudrate), NULL, 0);
|
|
//if (ret) {
|
|
// pr_err("set baud rate command failed(%d)\n", ret);
|
|
// return;
|
|
//}
|
|
|
|
/* Give the device some time to set up the new baudrate. */
|
|
aicos_udelay(10000);
|
|
|
|
//serdev_device_set_baudrate(h5->hu->serdev, baudrate);
|
|
//serdev_device_set_flow_control(h5->hu->serdev, flow_control);
|
|
|
|
ret = realtek_bt_download_firmware(&btdev.module->fw);
|
|
if (ret) {
|
|
pr_err("realtek bluetooth download firmware failed.\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
int realtek_bt_shutdown(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* According to the vendor driver, BT must be reset on close to avoid
|
|
* firmware crash.
|
|
*/
|
|
ret = ble_hs_hci_cmd_tx(HCI_OP_RESET, NULL, 0, NULL, 0);
|
|
if (ret) {
|
|
pr_err("HCI reset during shutdown failed(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|