blob: fe335b6563bc9f4f9a28f11cec6646b0b3294cf2 [file] [log] [blame] [edit]
/**
* Copyright (c) 2018 MediaTek Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See http://www.gnu.org/licenses/gpl-2.0.html for more details.
*/
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/input.h>
#include <linux/pm_wakeup.h>
#include <linux/reboot.h>
#include <linux/string.h>
#include "btmtk_define.h"
#include "btmtk_main.h"
#include "btmtk_fw_log.h"
#include "btmtk_chip_if.h"
#if (USE_DEVICE_NODE == 1)
#include "connv3_debug_utility.h"
#include "btmtk_queue.h"
#include "connv3.h"
#include "btmtk_proj_sp.h"
#endif
#define MTKBT_UNSLEEPABLE_LOCK(x, y) spin_lock_irqsave(x, y)
#define MTKBT_UNSLEEPABLE_UNLOCK(x, y) spin_unlock_irqsave(x, y)
/* TODO, need to modify the state mutex for each hci dev*/
static DEFINE_MUTEX(btmtk_chip_state_mutex);
#define CHIP_STATE_MUTEX_LOCK() mutex_lock(&btmtk_chip_state_mutex)
#define CHIP_STATE_MUTEX_UNLOCK() mutex_unlock(&btmtk_chip_state_mutex)
static DEFINE_MUTEX(btmtk_fops_state_mutex);
#define FOPS_MUTEX_LOCK() mutex_lock(&btmtk_fops_state_mutex)
#define FOPS_MUTEX_UNLOCK() mutex_unlock(&btmtk_fops_state_mutex)
#define SECTION_NUM_MAX 10
/**
* Global parameters(mtkbt_)
*/
uint8_t btmtk_log_lvl = BTMTK_LOG_LVL_DEF;
/* To support dynamic mount of interface can be probed */
static int btmtk_intf_num = BT_MCU_MINIMUM_INTERFACE_NUM;
/* To allow g_bdev being sized from btmtk_intf_num setting */
static struct btmtk_dev **g_bdev;
struct btmtk_dev *g_sbdev;
/*btmtk main information*/
static struct btmtk_main_info main_info;
/* State machine table that clarify through each HIF events,
* To specify HIF event on
* Entering / End / Error
*/
#if (USE_DEVICE_NODE == 0)
static const struct btmtk_cif_state g_cif_state[] = {
/* HIF_EVENT_PROBE */
{BTMTK_STATE_PROBE, BTMTK_STATE_WORKING, BTMTK_STATE_DISCONNECT},
/* HIF_EVENT_DISCONNECT */
{BTMTK_STATE_DISCONNECT, BTMTK_STATE_DISCONNECT, BTMTK_STATE_DISCONNECT},
/* HIF_EVENT_SUSPEND */
{BTMTK_STATE_SUSPEND, BTMTK_STATE_SUSPEND, BTMTK_STATE_FW_DUMP},
/* HIF_EVENT_RESUME */
{BTMTK_STATE_RESUME, BTMTK_STATE_WORKING, BTMTK_STATE_FW_DUMP},
/* HIF_EVENT_STANDBY */
{BTMTK_STATE_STANDBY, BTMTK_STATE_STANDBY, BTMTK_STATE_FW_DUMP},
/* HIF_EVENT_SUBSYS_RESET */
{BTMTK_STATE_SUBSYS_RESET, BTMTK_STATE_WORKING, BTMTK_STATE_FW_DUMP},
/* HIF_EVENT_WHOLE_CHIP_RESET */
{BTMTK_STATE_FW_DUMP, BTMTK_STATE_DISCONNECT, BTMTK_STATE_FW_DUMP},
/* HIF_EVENT_FW_DUMP */
{BTMTK_STATE_FW_DUMP, BTMTK_STATE_FW_DUMP, BTMTK_STATE_FW_DUMP},
};
#else //(USE_DEVICE_NODE == 1)
static const struct btmtk_cif_state g_cif_state[] = {
/* HIF_EVENT_PROBE */
{BTMTK_STATE_PROBE, BTMTK_STATE_WORKING, BTMTK_STATE_ERR},
/* HIF_EVENT_DISCONNECT */
{BTMTK_STATE_DISCONNECT, BTMTK_STATE_DISCONNECT, BTMTK_STATE_DISCONNECT},
/* HIF_EVENT_SUSPEND */
{BTMTK_STATE_SUSPEND, BTMTK_STATE_SUSPEND, BTMTK_STATE_ERR},
/* HIF_EVENT_RESUME */
{BTMTK_STATE_RESUME, BTMTK_STATE_WORKING, BTMTK_STATE_ERR},
/* HIF_EVENT_STANDBY */
{BTMTK_STATE_STANDBY, BTMTK_STATE_STANDBY, BTMTK_STATE_ERR},
/* HIF_EVENT_SUBSYS_RESET */
{BTMTK_STATE_SUBSYS_RESET, BTMTK_STATE_WORKING, BTMTK_STATE_ERR},
/* HIF_EVENT_WHOLE_CHIP_RESET */
{BTMTK_STATE_FW_DUMP, BTMTK_STATE_CLOSED, BTMTK_STATE_ERR},
/* HIF_EVENT_FW_DUMP */
{BTMTK_STATE_FW_DUMP, BTMTK_STATE_CLOSED, BTMTK_STATE_ERR},
};
#endif
__weak int btmtk_cif_register(void)
{
BTMTK_WARN("weak function %s not implement", __func__);
return -1;
}
__weak int btmtk_cif_deregister(void)
{
BTMTK_WARN("weak function %s not implement", __func__);
return -1;
}
__weak int btmtk_cif_send_calibration(struct btmtk_dev *bdev)
{
BTMTK_WARN("weak function %s not implement", __func__);
return -1;
}
#if (USE_DEVICE_NODE == 1)
int btmtk_cif_rx_packet_handler(struct hci_dev *hdev, struct sk_buff *skb)
{
return rx_skb_enqueue(skb);
}
#else
__weak int btmtk_cif_rx_packet_handler(struct hci_dev *hdev, struct sk_buff *skb)
{
BTMTK_WARN("weak function %s not implement", __func__);
return -1;
}
#endif
__weak int btmtk_send_apcf_reserved(struct btmtk_dev *bdev)
{
BTMTK_WARN("weak function %s not implement", __func__);
return -1;
}
#if 0
void btmtk_do_gettimeofday(struct timeval *tv)
{
#if (KERNEL_VERSION(4, 19, 85) > LINUX_VERSION_CODE)
do_gettimeofday(tv);
#else
struct timespec64 ts;
ktime_get_real_ts64(&ts);
tv->tv_sec = ts.tv_sec;
tv->tv_usec = ts.tv_nsec/1000;
#endif
}
#endif
void btmtk_getUTCtime(struct bt_utc_struct *utc)
{
#if (KERNEL_VERSION(4, 19, 85) > LINUX_VERSION_CODE)
struct timeval tv;
do_gettimeofday(&tv);
rtc_time_to_tm(tv.tv_sec, &utc->tm);
utc->usec = tv.tv_usec;
#else
struct timespec64 ts;
ktime_get_real_ts64(&ts);
rtc_time64_to_tm(ts.tv_sec, &utc->tm);
utc->usec = ts.tv_nsec/1000;
#endif
utc->tm.tm_year += 1900;
utc->tm.tm_mon += 1;
}
int32_t btmtk_intcmd_set_fw_log(uint8_t flag)
{
u8 fw_log_cmd[8] = { 0x01, 0x5D, 0xFC, 0x04, 0x02, 0x00, 0x02, 0x03 };
int ret;
BTMTK_INFO("%s send flag[0x%02X]", __func__, flag);
fw_log_cmd[7] = flag;
ret = btmtk_main_send_cmd(g_sbdev,
fw_log_cmd, 8, NULL, 0,
0, 0, BTMTK_TX_CMD_FROM_DRV);
if (ret < 0)
BTMTK_ERR("%s faill to send flag[0x%02X]", __func__, flag);
return ret;
}
void btmtk_get_UTC_time_str(char *ts_str)
{
struct bt_utc_struct utc;
btmtk_getUTCtime(&utc);
memset(ts_str, 0, HCI_SNOOP_TS_STR_LEN);
(void)snprintf(ts_str, HCI_SNOOP_TS_STR_LEN,
"%04d%02d%02d-%02d%02d%02d.%06u",
utc.tm.tm_year, utc.tm.tm_mon, utc.tm.tm_mday,
utc.tm.tm_hour, utc.tm.tm_min, utc.tm.tm_sec, utc.usec);
}
/*get 1 byte only*/
int btmtk_efuse_read(struct btmtk_dev *bdev, u16 addr, u8 *value)
{
uint8_t efuse_r[READ_EFUSE_CMD_LEN] = {0x01, 0x6F, 0xFC, 0x0E,
0x01, 0x0D, 0x0A, 0x00, 0x02, 0x04,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00};/*4 sub block number(sub block 0~3)*/
uint8_t efuse_r_event[READ_EFUSE_EVT_HDR_LEN] = {0x04, 0xE4, 0x1E, 0x02, 0x0D, 0x1A, 0x00, 02, 04};
/*check event
*04 E4 LEN(1B) 02 0D LEN(2Byte) 02 04 ADDR(2Byte) VALUE(4B) ADDR(2Byte) VALUE(4Byte)
*ADDR(2Byte) VALUE(4B) ADDR(2Byte) VALUE(4Byte)
*/
int ret = 0;
uint8_t sub_block_addr_in_event = 0;
uint16_t sub_block = (addr / 16) * 4;
uint8_t temp = 0;
efuse_r[READ_EFUSE_CMD_BLOCK_OFFSET] = sub_block & 0xFF;
efuse_r[READ_EFUSE_CMD_BLOCK_OFFSET + 1] = (sub_block & 0xFF00) >> 8;
efuse_r[READ_EFUSE_CMD_BLOCK_OFFSET + 2] = (sub_block + 1) & 0xFF;
efuse_r[READ_EFUSE_CMD_BLOCK_OFFSET + 3] = ((sub_block + 1) & 0xFF00) >> 8;
efuse_r[READ_EFUSE_CMD_BLOCK_OFFSET + 4] = (sub_block + 2) & 0xFF;
efuse_r[READ_EFUSE_CMD_BLOCK_OFFSET + 5] = ((sub_block + 2) & 0xFF00) >> 8;
efuse_r[READ_EFUSE_CMD_BLOCK_OFFSET + 6] = (sub_block + 3) & 0xFF;
efuse_r[READ_EFUSE_CMD_BLOCK_OFFSET + 7] = ((sub_block + 3) & 0xFF00) >> 8;
ret = btmtk_main_send_cmd(bdev,
efuse_r, READ_EFUSE_CMD_LEN,
efuse_r_event, READ_EFUSE_EVT_HDR_LEN,
0, 0, BTMTK_TX_CMD_FROM_DRV);
if (ret) {
BTMTK_WARN("btmtk_main_send_cmd error");
return ret;
}
if (memcmp(bdev->io_buf, efuse_r_event, READ_EFUSE_EVT_HDR_LEN) == 0) {
/*compare rxbuf format ok, compare addr*/
BTMTK_DBG("compare rxbuf format ok");
if (efuse_r[READ_EFUSE_CMD_BLOCK_OFFSET] == bdev->io_buf[9] &&
efuse_r[READ_EFUSE_CMD_BLOCK_OFFSET + 1] == bdev->io_buf[10] &&
efuse_r[READ_EFUSE_CMD_BLOCK_OFFSET + 2] == bdev->io_buf[15] &&
efuse_r[READ_EFUSE_CMD_BLOCK_OFFSET + 3] == bdev->io_buf[16] &&
efuse_r[READ_EFUSE_CMD_BLOCK_OFFSET + 4] == bdev->io_buf[21] &&
efuse_r[READ_EFUSE_CMD_BLOCK_OFFSET + 5] == bdev->io_buf[22] &&
efuse_r[READ_EFUSE_CMD_BLOCK_OFFSET + 6] == bdev->io_buf[27] &&
efuse_r[READ_EFUSE_CMD_BLOCK_OFFSET + 7] == bdev->io_buf[28]) {
BTMTK_DBG("address compare ok");
/*Get value*/
sub_block_addr_in_event = ((addr / 16) / 4);/*cal block num*/
temp = addr % 16;
BTMTK_DBG("address in block %d", temp);
switch (temp) {
case 0:
case 1:
case 2:
case 3:
*value = bdev->io_buf[11 + temp];
break;
case 4:
case 5:
case 6:
case 7:
*value = bdev->io_buf[17 + temp - 4];
break;
case 8:
case 9:
case 10:
case 11:
*value = bdev->io_buf[23 + temp - 8];
break;
case 12:
case 13:
case 14:
case 15:
*value = bdev->io_buf[29 + temp - 12];
break;
}
} else {
BTMTK_WARN("address compare fail");
ret = -1;
}
} else {
BTMTK_WARN("compare rxbuf format fail");
ret = -1;
}
return ret;
}
void btmtk_free_fw_cfg_struct(struct fw_cfg_struct *fw_cfg, int count)
{
int i = 0;
for (i = 0; i < count; i++) {
if (fw_cfg[i].content) {
BTMTK_INFO("%s:kfree %d", __func__, i);
kfree(fw_cfg[i].content);
fw_cfg[i].content = NULL;
fw_cfg[i].length = 0;
} else
fw_cfg[i].length = 0;
}
}
void btmtk_free_setting_file(struct btmtk_dev *bdev)
{
BTMTK_INFO("%s begin", __func__);
if (bdev == NULL) {
BTMTK_ERR("%s: bdev == NULL", __func__);
return;
}
btmtk_free_fw_cfg_struct(&bdev->bt_cfg.picus_filter, 1);
btmtk_free_fw_cfg_struct(&bdev->bt_cfg.picus_enable, 1);
btmtk_free_fw_cfg_struct(bdev->bt_cfg.phase1_wmt_cmd, PHASE1_WMT_CMD_COUNT);
btmtk_free_fw_cfg_struct(bdev->bt_cfg.vendor_cmd, VENDOR_CMD_COUNT);
btmtk_free_fw_cfg_struct(&bdev->bt_cfg.audio_cmd, 1);
memset(&bdev->bt_cfg, 0, sizeof(bdev->bt_cfg));
/* reset pin initial value need to be -1, used to judge after
* disconnected before probe, can't do chip reset
*/
bdev->bt_cfg.dongle_reset_gpio_pin = -1;
}
static void btmtk_initialize_cfg_items(struct btmtk_dev *bdev)
{
BTMTK_INFO("%s begin", __func__);
if (bdev == NULL) {
BTMTK_ERR("%s: bdev is NULL", __func__);
return;
}
bdev->bt_cfg.dongle_reset_gpio_pin = 220;
bdev->bt_cfg.support_dongle_reset = 0;
bdev->bt_cfg.support_full_fw_dump = 0;
bdev->bt_cfg.support_unify_woble = 1;
bdev->bt_cfg.unify_woble_type = 0;
bdev->bt_cfg.support_woble_by_eint = 0;
bdev->bt_cfg.support_woble_for_bt_disable = 0;
bdev->bt_cfg.support_woble_wakelock = 0;
bdev->bt_cfg.reset_stack_after_woble = 0;
bdev->bt_cfg.support_auto_picus = 0;
bdev->bt_cfg.support_picus_to_host = 0;
bdev->bt_cfg.support_bt_single_sku = 0;
bdev->bt_cfg.support_audio_setting = 0;
btmtk_free_fw_cfg_struct(&bdev->bt_cfg.picus_filter, 1);
btmtk_free_fw_cfg_struct(&bdev->bt_cfg.picus_enable, 1);
btmtk_free_fw_cfg_struct(bdev->bt_cfg.phase1_wmt_cmd, PHASE1_WMT_CMD_COUNT);
btmtk_free_fw_cfg_struct(bdev->bt_cfg.vendor_cmd, VENDOR_CMD_COUNT);
btmtk_free_fw_cfg_struct(&bdev->bt_cfg.audio_cmd, 1);
BTMTK_INFO("%s end", __func__);
}
u8 btmtk_get_chip_state(struct btmtk_dev *bdev)
{
u8 state = BTMTK_STATE_INIT;
CHIP_STATE_MUTEX_LOCK();
if (bdev)
state = bdev->interface_state;
else
BTMTK_ERR("%s: bdev is NULL", __func__);
CHIP_STATE_MUTEX_UNLOCK();
return state;
}
void btmtk_set_chip_state(struct btmtk_dev *bdev, u8 new_state)
{
static const char * const state_msg[BTMTK_STATE_MSG_NUM] = {
"UNKNOWN", "INIT", "DISCONNECT", "PROBE", "WORKING", "SUSPEND", "RESUME",
"FW_DUMP", "STANDBY", "SUBSYS_RESET", "SEND_ASSERT", "CLOSED", "ERROR"
};
if (new_state >= BTMTK_STATE_MSG_NUM) {
BTMTK_INFO("%s: new_state invalid(%d)", __func__, new_state);
return;
}
BTMTK_INFO("%s: %s(%d) -> %s(%d) dongle_index[%d]", __func__, state_msg[bdev->interface_state],
bdev->interface_state, state_msg[new_state], new_state, bdev->dongle_index);
CHIP_STATE_MUTEX_LOCK();
bdev->interface_state = new_state;
CHIP_STATE_MUTEX_UNLOCK();
}
u8 btmtk_fops_get_state(struct btmtk_dev *bdev)
{
u8 state = BTMTK_FOPS_STATE_INIT;
FOPS_MUTEX_LOCK();
if (bdev)
state = bdev->fops_state;
else
BTMTK_ERR("%s: bdev is NULL", __func__);
FOPS_MUTEX_UNLOCK();
return state;
}
static void btmtk_fops_set_state(struct btmtk_dev *bdev, u8 new_state)
{
static const char * const fstate_msg[BTMTK_FOPS_STATE_MSG_NUM] = {
"UNKNOWN", "INIT", "OPENING", "OPENED", "CLOSING", "CLOSED",
};
if (new_state >= BTMTK_FOPS_STATE_MSG_NUM) {
BTMTK_INFO("%s: new_state invalid(%d)", __func__, new_state);
return;
}
BTMTK_INFO("%s: FOPS_%s(%d) -> FOPS_%s(%d)", __func__, fstate_msg[bdev->fops_state],
bdev->fops_state, fstate_msg[new_state], new_state);
FOPS_MUTEX_LOCK();
bdev->fops_state = new_state;
FOPS_MUTEX_UNLOCK();
#if (USE_DEVICE_NODE == 1)
if (main_info.hif_hook.fw_log_state)
main_info.hif_hook.fw_log_state(new_state);
#endif
}
void *btmtk_kallsyms_lookup_name(const char *name)
{
void *addr = __symbol_get(name);
if (addr) {
#ifdef CONFIG_ARM
#ifdef CONFIG_THUMB2_KERNEL
/* set bit 0 in address for thumb mode */
addr |= 1;
#endif
#endif
__symbol_put(name);
}
return addr;
}
static void btmtk_main_info_initialize(void)
{
u32 snoop_idx = 0;
memset(&main_info, 0, sizeof(main_info));
for (snoop_idx = 0; snoop_idx < HCI_SNOOP_TYPE_MAX; snoop_idx++)
main_info.snoop[snoop_idx].index = HCI_SNOOP_ENTRY_NUM - 1;
atomic_set(&main_info.chip_reset, BTMTK_RESET_DONE);
atomic_set(&main_info.subsys_reset, BTMTK_RESET_DONE);
#if defined(CONFIG_MP_WAKEUP_SOURCE_SYSFS_STAT)
main_info.fwdump_ws = wakeup_source_register(NULL, "btmtk_fwdump_wakelock");
main_info.woble_ws = wakeup_source_register(NULL, "btmtk_woble_wakelock");
main_info.eint_ws = wakeup_source_register(NULL, "btevent_eint");
#if WAKEUP_BT_IRQ
main_info.irq_ws = wakeup_source_register(NULL, "btevent_irq");
#endif
#elif (defined(LINUX_OS) && (KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE)) || \
(defined(ANDROID_OS) && (KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE))
main_info.fwdump_ws = wakeup_source_register("btmtk_fwdump_wakelock");
main_info.woble_ws = wakeup_source_register("btmtk_woble_wakelock");
main_info.eint_ws = wakeup_source_register("btevent_eint");
#if WAKEUP_BT_IRQ
main_info.irq_ws = wakeup_source_register("btevent_irq");
#endif
#else
main_info.fwdump_ws = wakeup_source_register(NULL, "btmtk_fwdump_wakelock");
main_info.woble_ws = wakeup_source_register(NULL, "btmtk_woble_wakelock");
main_info.eint_ws = wakeup_source_register(NULL, "btevent_eint");
#if WAKEUP_BT_IRQ
main_info.irq_ws = wakeup_source_register(NULL, "btevent_irq");
#endif
#endif
main_info.wmt_over_hci_header[0] = HCI_COMMAND_PKT;
main_info.wmt_over_hci_header[1] = 0x6F;
main_info.wmt_over_hci_header[2] = 0xFC;
main_info.read_iso_packet_size_cmd[0] = HCI_COMMAND_PKT;
main_info.read_iso_packet_size_cmd[1] = 0x98;
main_info.read_iso_packet_size_cmd[2] = 0xFD;
main_info.read_iso_packet_size_cmd[3] = 0x02;
}
struct btmtk_main_info *btmtk_get_main_info(void)
{
return &main_info;
}
int btmtk_get_interface_num(void)
{
return btmtk_intf_num;
}
struct btmtk_dev **btmtk_get_pp_bdev(void)
{
return g_bdev;
}
void btmtk_free_debug_reg_struct(struct debug_reg_struct *debug_reg)
{
int i = 0;
int count = debug_reg->num;
for (i = 0; i < count; i++) {
if (debug_reg->reg[i].content) {
BTMTK_DBG("%s:kfree %d", __func__, i);
kfree(debug_reg->reg[i].content);
debug_reg->reg[i].content = NULL;
debug_reg->reg[i].length = 0;
} else {
debug_reg->reg[i].length = 0;
}
}
kfree(debug_reg->reg);
debug_reg->reg = NULL;
debug_reg->num = 0;
}
static void btmtk_initialize_debug_reg_items(struct btmtk_dev *bdev)
{
BTMTK_INFO("%s begin", __func__);
if (bdev == NULL) {
BTMTK_ERR("%s: bdev is NULL", __func__);
return;
}
#if (USE_DEVICE_NODE == 0)
btmtk_clean_debug_reg_file(bdev);
#endif
bdev->debug_sop_reg_dump.num = 0;
BTMTK_INFO("%s end", __func__);
}
void btmtk_clean_debug_reg_file(struct btmtk_dev *bdev)
{
BTMTK_INFO("%s begin", __func__);
if (bdev == NULL) {
BTMTK_ERR("%s: bdev == NULL", __func__);
return;
}
btmtk_free_debug_reg_struct(&bdev->debug_sop_reg_dump);
BTMTK_INFO("%s end", __func__);
}
int btmtk_load_register(char *block_name, struct debug_reg_struct *save_reg,
u8 *searchcontent, enum debug_reg_index_len index_length)
{
int ret = 0, i = 0;
u16 temp_len = 0;
u32 temp[DEBUG_REG_NUM]; /* save for total hex number */
unsigned long parsing_result = 0;
char *search_result = NULL;
char *search_end = NULL;
char search[SEARCH_LEN];
char *next_block = NULL;
char number[DEBUG_REG_SIZE + 1]; /* 1 is for '\0' */
char *regnum = NULL;
memset(search, 0, SEARCH_LEN);
memset(temp, 0, DEBUG_REG_NUM * sizeof(u32));
memset(number, 0, DEBUG_REG_SIZE + 1);
if (searchcontent == NULL) {
BTMTK_ERR("%s: Searchcontent is NULL", __func__);
return -1;
}
/* search REG NUM */
(void)snprintf(search, SEARCH_LEN, "%sNUM:", block_name);
search_result = strstr((char *)searchcontent, search);
if (search_result) {
search_result = strstr(search_result, ":");
if (search_result == NULL) {
BTMTK_ERR("%s:regnum Incorrect format", __func__);
return -1;
}
search_end = strstr(search_result, ",");
if (search_end == NULL) {
BTMTK_ERR("%s: regnum is NULL", __func__);
return -1;
}
if (search_end - search_result < 0) {
BTMTK_ERR("%s: Incorrect Format in %s", __func__, search);
return -1;
}
regnum = kzalloc((search_end - search_result) * sizeof(char), GFP_KERNEL);
if (regnum == NULL) {
BTMTK_ERR("%s: Allocate memory fail", __func__);
return -ENOMEM;
}
memset(regnum, 0, search_end - search_result);
memcpy(regnum, search_result + 1, search_end - search_result - 1);
regnum[search_end - search_result - 1] = '\0';
ret = kstrtoul(regnum, 0, &parsing_result);
if (ret != 0) {
BTMTK_ERR("%s: %s kstrtoul fail: %d", __func__, regnum, ret);
return -ENOMEM;
}
save_reg->reg = (struct debug_reg *)kzalloc(parsing_result * sizeof(struct debug_reg), GFP_KERNEL);
if (save_reg->reg == NULL) {
BTMTK_ERR("%s: Allocate memory fail", __func__);
return -ENOMEM;
}
save_reg->num = (u32)parsing_result;
BTMTK_INFO("%s: reg num is %d", __func__, save_reg->num);
} else{
BTMTK_ERR("%s: %s is not found", __func__, search);
return ret;
}
/* search block name */
for (i = 0; i < save_reg->num; i++) {
temp_len = 0;
if (index_length == DEBUG_REG_INX_LEN_2) /* EX: POWER_STATUS_01 */
(void)snprintf(search, SEARCH_LEN, "%s%02d:", block_name, i);
else if (index_length == DEBUG_REG_INX_LEN_3) /* EX: POWER_STATUS_001 */
(void)snprintf(search, SEARCH_LEN, "%s%03d:", block_name, i);
else
(void)snprintf(search, SEARCH_LEN, "%s:", block_name);
ret = 0;
search_result = strstr((char *)searchcontent, search);
if (search_result) {
memset(temp, 0, DEBUG_REG_NUM * sizeof(u32));
search_result = strstr(search_result, "0x");
if (search_result == NULL) {
BTMTK_ERR("%s: search_result is NULL", __func__);
return -1;
}
/* find next line as end of this command line, if NULL means last line */
next_block = strstr(search_result, ":");
if (next_block == NULL)
BTMTK_WARN("%s: if NULL means last line", __func__);
do {
search_end = strstr(search_result, ",");
if (search_end == NULL) {
BTMTK_ERR("%s: Search_end is NULL", __func__);
break;
}
if (search_end - search_result != DEBUG_REG_SIZE) {
BTMTK_ERR("%s: Incorrect Format in %s", __func__, search);
break;
}
memset(number, 0, DEBUG_REG_SIZE + 1);
memcpy(number, search_result, DEBUG_REG_SIZE);
ret = kstrtoul(number, 0, &parsing_result);
if (ret == 0) {
if (temp_len >= DEBUG_REG_NUM) {
BTMTK_ERR("%s: %s data over %d", __func__, search, DEBUG_REG_NUM);
break;
}
temp[temp_len] = parsing_result;
temp_len++;
} else {
BTMTK_WARN("%s: %s kstrtoul fail: %d", __func__, search, ret);
break;
}
search_result = strstr(search_end, "0x");
if (search_result == NULL) {
BTMTK_ERR("%s: search_result is NULL", __func__);
break;
}
} while (search_result < next_block || (search_result && next_block == NULL));
} else
BTMTK_DBG("%s: %s is not found in %d", __func__, search, i);
if (temp_len && temp_len < DEBUG_REG_NUM) {
BTMTK_DBG("%s: %s found & stored in %d", __func__, search, i);
save_reg->reg[i].content = (u32 *)kzalloc(temp_len * sizeof(u32), GFP_KERNEL);
if (save_reg->reg[i].content == NULL) {
BTMTK_ERR("%s: Allocate memory fail(%d)", __func__, i);
return -ENOMEM;
}
memcpy(save_reg->reg[i].content, temp, temp_len * sizeof(u32));
save_reg->reg[i].length = temp_len;
BTMTK_DBG("%s: %s has found %d stored , value0 is %08x",
__func__, block_name, temp_len, temp[0]);
}
}
return ret;
}
void btmtk_load_debug_sop_register(char *debug_sop_name, struct device *dev, struct btmtk_dev *bdev)
{
int err;
u32 code_len = 0;
btmtk_initialize_debug_reg_items(bdev);
err = btmtk_load_code_from_setting_files(debug_sop_name, dev, &code_len, bdev);
if (err) {
BTMTK_WARN("btmtk_load_code_from_setting_files failed!!");
#ifdef DEBUG_SOP_NAME
/* load from sdio_debug.h */
if (is_mt7902(bdev->chip_id)) {
err = btmtk_load_register("DEBUG_REG_",
&bdev->debug_sop_reg_dump, DEBUG_SOP_NAME(7902), DEBUG_REG_INX_LEN_3);
if (err)
goto LOAD_END;
}
if (is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id)) {
err = btmtk_load_register("DEBUG_REG_",
&bdev->debug_sop_reg_dump, DEBUG_SOP_NAME(7921), DEBUG_REG_INX_LEN_3);
if (err)
goto LOAD_END;
}
BTMTK_INFO("btmtk_load_debug_sop_register from .h!!");
#endif
} else {
/* load from file */
err = btmtk_load_register("DEBUG_REG_",
&bdev->debug_sop_reg_dump, bdev->setting_file, DEBUG_REG_INX_LEN_3);
if (err)
goto LOAD_END;
BTMTK_INFO("btmtk_load_debug_sop_register from .bin!!");
}
LOAD_END:
/* release setting file memory */
kfree(bdev->setting_file);
bdev->setting_file = NULL;
if (err)
BTMTK_ERR("%s: error return %d", __func__, err);
}
void btmtk_hci_snoop_print_to_log(void)
{
u8 counter, index, snoop_index;
char *snoop_str[HCI_SNOOP_TYPE_MAX] = {
"Command from stack",
"Command to FW",
"Event to stack",
"Event From FW",
"ADV Event to stack",
"ADV Event From FW",
"NOCP Event to stack",
"NOCP Event From FW",
"TX ACL from stack",
"TX ACL to FW",
"RX ACL to stack",
"RX ACL From FW",
"TX ISO from stack",
"TX ISO to FW",
"RX ISO to stack",
"RX ISO From FW"};
for (snoop_index = 0; snoop_index < HCI_SNOOP_TYPE_MAX; snoop_index++) {
BTMTK_INFO("HCI %s Dump: Using A5 A5 to separator the head 32 bytes and the tail 32 bytes data",
snoop_str[snoop_index]);
if (main_info.snoop[snoop_index].index >= (HCI_SNOOP_ENTRY_NUM - 1))
index = 0;
else
index = main_info.snoop[snoop_index].index + 1;
for (counter = 0; counter < HCI_SNOOP_ENTRY_NUM; counter++) {
if (main_info.snoop[snoop_index].len[index] > 0)
BTMTK_INFO_RAW(main_info.snoop[snoop_index].buf[index],
main_info.snoop[snoop_index].len[index],
"time(%s)-act_len(%d)-len(%d):", main_info.snoop[snoop_index].timestamp[index],
main_info.snoop[snoop_index].actual_len[index],
main_info.snoop[snoop_index].len[index]);
index++;
if (index >= HCI_SNOOP_ENTRY_NUM)
index = 0;
}
}
}
void btmtk_hci_snoop_save(unsigned int type, u8 *buf, u32 len)
{
u32 copy_len = HCI_SNOOP_BUF_SIZE;
u32 copy_tail_len = HCI_SNOOP_BUF_SIZE;
u8 separator_char[SEPARATOR_LEN] = {0xA5, 0xA5};
u8 *copy_tail_buf;
if (!buf || len == 0 || type >= HCI_SNOOP_TYPE_MAX) {
BTMTK_ERR("%s, invalid parameters!", __func__);
return;
}
if (main_info.snoop[type].index < HCI_SNOOP_ENTRY_NUM) {
if (len < HCI_SNOOP_BUF_SIZE) {
copy_len = len;
copy_tail_len = 0;
} else if (len > HCI_SNOOP_BUF_SIZE && len <= HCI_SNOOP_BUF_SIZE * 2)
copy_tail_len = len - copy_len;
main_info.snoop[type].len[main_info.snoop[type].index] = copy_len & 0xff;
main_info.snoop[type].actual_len[main_info.snoop[type].index] = len & 0xffff;
btmtk_get_UTC_time_str(main_info.snoop[type].timestamp[main_info.snoop[type].index]);
memset(main_info.snoop[type].buf[main_info.snoop[type].index], 0, HCI_SNOOP_MAX_BUF_SIZE);
memcpy(main_info.snoop[type].buf[main_info.snoop[type].index], buf, copy_len & 0xff);
/* save less then 32 bytes data in the buffer tail, using A5 A5 to
* separator the head 32 bytes data and the tail 32 bytes data
*/
if (copy_tail_len > 0) {
copy_tail_buf = buf + len - copy_tail_len;
main_info.snoop[type].len[main_info.snoop[type].index] +=
(copy_tail_len + SEPARATOR_LEN) & 0xff;
memcpy(main_info.snoop[type].buf[main_info.snoop[type].index] + copy_len, separator_char,
SEPARATOR_LEN);
memcpy(main_info.snoop[type].buf[main_info.snoop[type].index] + copy_len + SEPARATOR_LEN,
copy_tail_buf, copy_tail_len);
}
if (main_info.snoop[type].index == 0)
main_info.snoop[type].index = HCI_SNOOP_ENTRY_NUM;
main_info.snoop[type].index--;
}
}
void btmtk_hci_snoop_print(const u8 *buf, u32 len)
{
u32 copy_len = HCI_SNOOP_BUF_SIZE;
u32 copy_tail_len = HCI_SNOOP_BUF_SIZE;
u8 separator_char[SEPARATOR_LEN] = {0xA5, 0xA5};
const u8 *copy_tail_buf;
u8 hci_snoop_buf[HCI_SNOOP_MAX_BUF_SIZE] = {0};
u16 hci_snoop_len = 0;
if (buf && len > 0) {
if (len < HCI_SNOOP_BUF_SIZE) {
copy_len = len;
copy_tail_len = 0;
} else if (len > HCI_SNOOP_BUF_SIZE && len <= HCI_SNOOP_BUF_SIZE * 2)
copy_tail_len = len - copy_len;
memcpy(hci_snoop_buf, buf, copy_len & 0xff);
hci_snoop_len = copy_len & 0xff;
/* save less then 32 bytes data in the buffer tail, using A5 A5 to
* separator the head 32 bytes data and the tail 32 bytes data
*/
if (copy_tail_len > 0) {
copy_tail_buf = buf + len - copy_tail_len;
hci_snoop_len += (copy_tail_len + SEPARATOR_LEN) & 0xff;
memcpy(hci_snoop_buf + copy_len, separator_char, SEPARATOR_LEN);
memcpy(hci_snoop_buf + copy_len + SEPARATOR_LEN,
copy_tail_buf, copy_tail_len);
}
if (hci_snoop_len > 0)
BTMTK_INFO_RAW(hci_snoop_buf, hci_snoop_len, "act_len(%d)-len(%d)-buf(%p):",
len, hci_snoop_len, buf);
}
}
/* HCI receive mechnism */
static inline struct sk_buff *h4_recv_buf(struct hci_dev *hdev,
struct sk_buff *skb,
const unsigned char *buffer,
int count,
const struct h4_recv_pkt *pkts,
int pkts_count)
{
struct btmtk_dev *bdev = NULL;
/* used for print debug log*/
const unsigned char *buffer_dbg = buffer;
int count_dbg = count;
unsigned char *skb_tmp = NULL;
if (hdev == NULL || buffer == NULL) {
BTMTK_ERR("%s, invalid parameters!", __func__);
return ERR_PTR(-EINVAL);
}
bdev = hci_get_drvdata(hdev);
if (bdev == NULL) {
BTMTK_ERR("%s, bdev is invalid", __func__);
return ERR_PTR(-EINVAL);
}
/* Check for error from previous call */
if (IS_ERR(skb))
skb = NULL;
/* BTMTK_DBG("%s, buffer[0]=0x%02X, count[%d], pkts_count[%d]", __func__, *buffer, count, pkts_count); */
while (count) {
int i, len;
if (!skb) {
for (i = 0; i < pkts_count; i++) {
if (buffer[0] != (&pkts[i])->type)
continue;
skb = bt_skb_alloc((&pkts[i])->maxlen,
GFP_ATOMIC);
if (!skb) {
BTMTK_ERR("%s, alloc skb failed!", __func__);
return ERR_PTR(-ENOMEM);
}
hci_skb_pkt_type(skb) = (&pkts[i])->type;
hci_skb_expect(skb) = (&pkts[i])->hlen;
break;
}
/* Check for invalid packet type */
if (!skb) {
BTMTK_ERR("%s,skb is invalid, buffer[0] = 0x%02X, count[%d]", __func__,
buffer[0], count);
if (is_mt66xx(bdev->chip_id))
btmtk_set_sleep(hdev, FALSE);
else {
btmtk_hci_snoop_print(buffer_dbg, count_dbg);
btmtk_hci_snoop_print(buffer, count);
btmtk_hci_snoop_print_to_log();
}
return ERR_PTR(-EILSEQ);
}
count -= 1;
buffer += 1;
}
len = min_t(uint, hci_skb_expect(skb) - skb->len, count);
/* BTMTK_DBG("%s, hci_skb_expect[%d](hdlen), skb->len[%d], count[%d]", __func__, hci_skb_expect(skb), skb->len, count); */
skb_tmp = skb_put(skb, len);
if (!skb_tmp) {
BTMTK_ERR("%s, skb_put failed. Len = %d!", __func__, len);
kfree_skb(skb);
return ERR_PTR(-ENOMEM);
}
memcpy(skb_tmp, buffer, len);
/* If kernel version > 4.x */
/* skb_put_data(skb, buffer, len); */
count -= len;
buffer += len;
/* Check for partial packet */
if (skb->len < hci_skb_expect(skb))
continue;
for (i = 0; i < pkts_count; i++) {
if (hci_skb_pkt_type(skb) == (&pkts[i])->type)
break;
}
if (i >= pkts_count) {
BTMTK_ERR("%s, pkt type is invalid!", __func__);
if (is_mt66xx(bdev->chip_id))
btmtk_set_sleep(hdev, FALSE);
else {
btmtk_hci_snoop_print(buffer_dbg, count_dbg);
btmtk_hci_snoop_print(buffer, count);
btmtk_hci_snoop_print_to_log();
}
kfree_skb(skb);
return ERR_PTR(-EILSEQ);
}
/* not use hci_skb_expect instead of hlen */
/* because hci_skb_expect will update by +=dlen */
if (skb->len == (&pkts[i])->hlen) {
u16 dlen;
/* BTMTK_DBG("%s begin, skb->len = %d, %d, %d", __func__, skb->len, */
/* (&pkts[i])->hlen, (&pkts[i])->lsize); */
switch ((&pkts[i])->lsize) {
case 0:
/* No variable data length */
dlen = 0;
break;
case 1:
/* Single octet variable length */
dlen = skb->data[(&pkts[i])->loff];
hci_skb_expect(skb) += dlen;
if (skb_tailroom(skb) < dlen) {
BTMTK_ERR("%s, maxlen[%d] skb_tailroom[%d] is not enough, dlen:%d!",
__func__, (&pkts[i])->maxlen, skb_tailroom(skb), dlen);
BTMTK_INFO_RAW(skb->data, skb->len, "%s, send, len = %d", __func__, skb->len);
if (is_mt66xx(bdev->chip_id))
btmtk_set_sleep(hdev, FALSE);
else {
btmtk_hci_snoop_print(skb->data, skb->len);
btmtk_hci_snoop_print(buffer_dbg, count_dbg);
btmtk_hci_snoop_print(buffer, count);
btmtk_hci_snoop_print_to_log();
}
kfree_skb(skb);
return ERR_PTR(-EMSGSIZE);
}
break;
case 2:
/* Double octet variable length */
dlen = get_unaligned_le16(skb->data +
(&pkts[i])->loff);
/* parse ISO packet len*/
if ((&pkts[i])->type == HCI_ISODATA_PKT) {
unsigned char *cp = (unsigned char *)&dlen + 1;
*cp = *cp & 0x3F;
}
hci_skb_expect(skb) += dlen;
if (skb_tailroom(skb) < dlen) {
BTMTK_ERR("%s, maxlen[%d] skb_tailroom[%d] is not enough, dlen:%d!",
__func__, (&pkts[i])->maxlen, skb_tailroom(skb), dlen);
if (is_mt66xx(bdev->chip_id))
btmtk_set_sleep(hdev, FALSE);
else {
btmtk_hci_snoop_print(skb->data, skb->len);
btmtk_hci_snoop_print(buffer_dbg, count_dbg);
btmtk_hci_snoop_print(buffer, count);
btmtk_hci_snoop_print_to_log();
}
kfree_skb(skb);
return ERR_PTR(-EMSGSIZE);
}
break;
default:
/* Unsupported variable length */
BTMTK_ERR("%s, Unsupported variable length!", __func__);
if (is_mt66xx(bdev->chip_id))
btmtk_set_sleep(hdev, FALSE);
else {
btmtk_hci_snoop_print(buffer_dbg, count_dbg);
btmtk_hci_snoop_print(buffer, count);
btmtk_hci_snoop_print_to_log();
}
kfree_skb(skb);
return ERR_PTR(-EILSEQ);
}
if (!dlen) {
/* No more data, complete frame */
(&pkts[i])->recv(hdev, skb);
if (is_mt66xx(bdev->chip_id))
btmtk_set_sleep(hdev, FALSE);
skb = NULL;
}
} else {
/* Complete frame */
(&pkts[i])->recv(hdev, skb);
if (is_mt66xx(bdev->chip_id))
btmtk_set_sleep(hdev, FALSE);
skb = NULL;
}
}
return skb;
}
static const struct h4_recv_pkt mtk_recv_pkts[] = {
{ H4_RECV_ACL, .recv = btmtk_recv_acl },
{ H4_RECV_SCO, .recv = hci_recv_frame },
{ H4_RECV_EVENT, .recv = btmtk_recv_event },
{ H4_RECV_ISO, .recv = btmtk_recv_iso },
#if (USE_DEVICE_NODE == 1)
{ H4_RECV_RHW_READ, .recv = btmtk_recv_rhw},
{ H4_RECV_RHW_WRITE, .recv = btmtk_recv_rhw},
#endif
};
#if ENABLESTP
static inline struct sk_buff *mtk_add_stp(struct btmtk_dev *bdev, struct sk_buff *skb)
{
struct mtk_stp_hdr *shdr;
int dlen, err = 0, type = 0;
u8 stp_crc[STP_CRC_LEN] = {0x00, 0x00};
if (unlikely(skb_headroom(skb) < sizeof(*shdr)) ||
(skb_tailroom(skb) < MTK_STP_TLR_SIZE)) {
BTMTK_DBG("%s, add pskb_expand_head, headroom = %d, tailroom = %d",
__func__, skb_headroom(skb), skb_tailroom(skb));
err = pskb_expand_head(skb, sizeof(*shdr), MTK_STP_TLR_SIZE,
GFP_ATOMIC);
}
dlen = skb->len;
shdr = (void *) skb_push(skb, sizeof(*shdr));
shdr->prefix = 0x80;
shdr->dlen = cpu_to_be16((dlen & 0x0fff) | (type << 12));
shdr->cs = 0;
/* Add the STP trailer
* kernel version > 4.20
* skb_put_zero(skb, MTK_STP_TLR_SIZE);
* kernel version < 4.20
*/
skb_put(skb, STP_CRC_LEN);
return skb;
}
static const unsigned char *
mtk_stp_split(struct btmtk_dev *bdev, const unsigned char *data, int count,
int *sz_h4)
{
struct mtk_stp_hdr *shdr;
/* The cursor is reset when all the data of STP is consumed out */
if (!bdev->stp_dlen && bdev->stp_cursor >= 6) {
bdev->stp_cursor = 0;
BTMTK_ERR("reset cursor = %d\n", bdev->stp_cursor);
}
/* Filling pad until all STP info is obtained */
while (bdev->stp_cursor < 6 && count > 0) {
bdev->stp_pad[bdev->stp_cursor] = *data;
pr_err("fill stp format (%02x, %d, %d)\n",
bdev->stp_pad[bdev->stp_cursor], bdev->stp_cursor, count);
bdev->stp_cursor++;
data++;
count--;
}
/* Retrieve STP info and have a sanity check */
if (!bdev->stp_dlen && bdev->stp_cursor >= 6) {
shdr = (struct mtk_stp_hdr *)&bdev->stp_pad[2];
bdev->stp_dlen = be16_to_cpu(shdr->dlen) & 0x0fff;
pr_err("stp format (%02x, %02x)",
shdr->prefix, bdev->stp_dlen);
/* Resync STP when unexpected data is being read */
if (shdr->prefix != 0x80 || bdev->stp_dlen > 2048) {
BTMTK_ERR("stp format unexpect (%02x, %02x)",
shdr->prefix, bdev->stp_dlen);
BTMTK_ERR("reset cursor = %d\n", bdev->stp_cursor);
bdev->stp_cursor = 2;
bdev->stp_dlen = 0;
}
}
/* Directly quit when there's no data found for H4 can process */
if (count <= 0)
return NULL;
/* Tranlate to how much the size of data H4 can handle so far */
*sz_h4 = min_t(int, count, bdev->stp_dlen);
/* Update the remaining size of STP packet */
bdev->stp_dlen -= *sz_h4;
/* Data points to STP payload which can be handled by H4 */
return data;
}
#endif
int btmtk_recv(struct hci_dev *hdev, const u8 *data, size_t count)
{
struct btmtk_dev *bdev = NULL;
const unsigned char *p_left = data;
int sz_left = count;
int err;
#if ENABLESTP
const unsigned char **p_h4 = NULL;
int sz_h4 = 0, adv = 0;
#endif
if (hdev == NULL || data == NULL) {
BTMTK_ERR("%s, invalid parameters!", __func__);
return -EINVAL;
}
bdev = hci_get_drvdata(hdev);
if (bdev == NULL) {
BTMTK_ERR("%s, bdev is NULL!", __func__);
return -EINVAL;
}
while (sz_left > 0) {
/* The serial data received from MT7622 BT controller is
* at all time padded around with the STP header and tailer.
*
* A full STP packet is looking like
* -----------------------------------
* | STP header | H:4 | STP tailer |
* -----------------------------------
* but it doesn't guarantee to contain a full H:4 packet which
* means that it's possible for multiple STP packets forms a
* full H:4 packet that means extra STP header + length doesn't
* indicate a full H:4 frame, things can fragment. Whose length
* recorded in STP header just shows up the most length the
* H:4 engine can handle currently.
*/
#if ENABLESTP
p_h4 = mtk_stp_split(bdev, p_left, sz_left, &sz_h4);
if (!p_h4)
break;
adv = p_h4 - p_left;
sz_left -= adv;
p_left += adv;
#endif
#if ENABLESTP
bdev->rx_skb = h4_recv_buf(hdev, bdev->rx_skb, p_h4,
sz_h4, mtk_recv_pkts,
ARRAY_SIZE(mtk_recv_pkts));
#else
bdev->rx_skb = h4_recv_buf(hdev, bdev->rx_skb, data,
count, mtk_recv_pkts,
ARRAY_SIZE(mtk_recv_pkts));
#endif
if (IS_ERR(bdev->rx_skb)) {
err = PTR_ERR(bdev->rx_skb);
//BTMTK_ERR("Frame reassembly failed (%d)", err);
bdev->rx_skb = NULL;
return err;
}
#if ENABLESTP
sz_left -= sz_h4;
p_left += sz_h4;
#else
sz_left -= count;
p_left += count;
#endif
}
return 0;
}
#if (USE_DEVICE_NODE == 0)
static int btmtk_set_audio_slave(struct btmtk_dev *bdev)
{
int ret = 0;
u8 audio_cmd[AUDIO_SETTING_CMD_LEN] = { 0x01, 0x72, 0xFC, 0x04, 0x49, 0x00, 0x80, 0x00 };
u8 audio_event[AUDIO_SETTING_EVT_LEN] = { 0x04, 0x0E, 0x04, 0x01, 0x72, 0xFC, 0x00 };
struct fw_cfg_struct *audio_setting = &bdev->bt_cfg.audio_cmd;
BTMTK_INFO("%s enter", __func__);
if (audio_setting->content && audio_setting->length) {
BTMTK_INFO("%s load audio setting from bt.cfg", __func__);
memcpy((audio_cmd + 4), audio_setting->content, audio_setting->length);
} else {
BTMTK_INFO("%s load default audio cmd", __func__);
}
BTMTK_INFO_RAW(audio_cmd, AUDIO_SETTING_CMD_LEN, "%s: Send CMD:", __func__);
ret = btmtk_main_send_cmd(bdev, audio_cmd, AUDIO_SETTING_CMD_LEN,
audio_event, AUDIO_SETTING_EVT_LEN, 0, 0, BTMTK_TX_PKT_FROM_HOST);
if (ret < 0)
BTMTK_ERR("%s: failed(%d)", __func__, ret);
BTMTK_INFO("%s exit", __func__);
return ret;
}
static int btmtk_read_pin_mux_setting(struct btmtk_dev *bdev, const uint8_t *cmd,
const int cmd_len, const uint8_t *event, const int event_len, u32 *value)
{
int ret = 0;
BTMTK_INFO("%s enter", __func__);
ret = btmtk_main_send_cmd(bdev, cmd, cmd_len,
event, event_len, 0, 0, BTMTK_TX_PKT_FROM_HOST);
if (ret < 0)
BTMTK_ERR("%s: failed(%d)", __func__, ret);
*value = (bdev->io_buf[READ_PINMUX_EVT_REAL_LEN - 1] << 24) +
(bdev->io_buf[READ_PINMUX_EVT_REAL_LEN - 2] << 16) +
(bdev->io_buf[READ_PINMUX_EVT_REAL_LEN - 3] << 8) +
bdev->io_buf[READ_PINMUX_EVT_REAL_LEN - 4];
BTMTK_INFO("%s, value=0x%08x", __func__, *value);
return ret;
}
static int btmtk_write_pin_mux_setting(struct btmtk_dev *bdev, uint8_t *cmd,
int cmd_len, const uint8_t *event, const int event_len, u32 value)
{
int ret = 0;
BTMTK_INFO("%s begin, value = 0x%08x", __func__, value);
cmd[cmd_len - 4] = (value & 0x000000FF);
cmd[cmd_len - 3] = ((value & 0x0000FF00) >> 8);
cmd[cmd_len - 2] = ((value & 0x00FF0000) >> 16);
cmd[cmd_len - 1] = ((value & 0xFF000000) >> 24);
ret = btmtk_main_send_cmd(bdev, cmd, cmd_len,
event, event_len, 0, 0, BTMTK_TX_PKT_FROM_HOST);
if (ret < 0)
BTMTK_ERR("%s: failed(%d)", __func__, ret);
BTMTK_INFO("%s exit", __func__);
return ret;
}
static int btmtk_set_audio_pin_mux(struct btmtk_dev *bdev)
{
int ret = 0;
unsigned int i = 0;
u32 pinmux = 0;
u8 read_pinmux_cmd[READ_PINMUX_CMD_LEN] = { 0x01, 0xD1, 0xFC, 0x04, 0x50, 0x50, 0x00, 0x70 };
u8 read_pinmux_event[READ_PINMUX_EVT_CMP_LEN] = { 0x04, 0x0E, 0x08, 0x01, 0xD1, 0xFC };
u8 write_pinmux_cmd[WRITE_PINMUX_CMD_LEN] = { 0x01, 0xD0, 0xFC, 0x08, 0x50, 0x50, 0x00, 0x70,
0x00, 0x10, 0x11, 0x01 };
u8 write_pinmux_event[WRITE_PINMUX_EVT_LEN] = { 0x04, 0x0E, 0x04, 0x01, 0xD0, 0xFC, 0x00 };
for (i = 0; i < PINMUX_REG_NUM; i++) {
pinmux = 0;
if (i == PINMUX_REG_NUM - 1) {
read_pinmux_cmd[READ_PINMUX_CMD_LEN - 4] = 0x54;
write_pinmux_cmd[WRITE_PINMUX_CMD_LEN - 8] = 0x54;
}
ret = btmtk_read_pin_mux_setting(bdev, read_pinmux_cmd, READ_PINMUX_CMD_LEN,
read_pinmux_event, READ_PINMUX_EVT_CMP_LEN, &pinmux);
if (ret) {
BTMTK_ERR("%s, btmtk_read_pin_mux_setting error(%d)", __func__, ret);
goto exit;
}
if (write_pinmux_cmd[WRITE_PINMUX_CMD_LEN - 8] == 0x50) {
pinmux &= 0x00FFFFFF;
pinmux |= 0x11000000;
} else if (write_pinmux_cmd[WRITE_PINMUX_CMD_LEN - 8] == 0x54) {
pinmux &= 0xFFFFF0F0;
pinmux |= 0x00000101;
} else {
BTMTK_ERR("%s, pinmux register is error, write_pinmux_cmd[%d] = 0x%02x",
__func__, WRITE_PINMUX_CMD_LEN - 8,
write_pinmux_cmd[WRITE_PINMUX_CMD_LEN - 8]);
ret = -1;
goto exit;
}
ret = btmtk_write_pin_mux_setting(bdev, write_pinmux_cmd, WRITE_PINMUX_CMD_LEN,
write_pinmux_event, WRITE_PINMUX_EVT_LEN, pinmux);
if (ret) {
BTMTK_ERR("%s, btmtk_write_pin_mux_setting error(%d)", __func__, ret);
goto exit;
}
pinmux = 0;
ret = btmtk_read_pin_mux_setting(bdev, read_pinmux_cmd, READ_PINMUX_CMD_LEN,
read_pinmux_event, READ_PINMUX_EVT_CMP_LEN, &pinmux);
if (ret) {
BTMTK_ERR("%s, btmtk_read_pin_mux_setting error(%d)", __func__, ret);
goto exit;
}
BTMTK_INFO("%s, confirm pinmux register 0x%02x pinmux 0x%08x", __func__,
write_pinmux_cmd[4], pinmux);
}
exit:
return ret;
}
static int btmtk_set_audio_pin_mux_7902(struct btmtk_dev *bdev)
{
int ret = 0;
unsigned int i = 0;
u8 write_pinmux_cmd[WRITE_PINMUX_CMD_LEN_7902] = { 0x01, 0x34, 0xFC, 0x03, 0x02, 0x00, 0x01 };
u8 write_pinmux_event[WRITE_PINMUX_EVT_LEN_7902] = { 0x04, 0x0E, 0x04, 0x01, 0x34, 0xFC, 0x00 };
u8 write_pinmux_pin_num[PINMUX_REG_NUM_7902] = { 0x00, 0x01, 0x02, 0x04 };
u8 write_pinmux_pin_mode[PINMUX_REG_NUM_7902] = { 0x01, 0x01, 0x01, 0x01 };
struct fw_cfg_struct *audio_pinmux_num = &bdev->bt_cfg.audio_pinmux_num;
struct fw_cfg_struct *audio_pinmux_mode = &bdev->bt_cfg.audio_pinmux_mode;
if (audio_pinmux_num->content && audio_pinmux_num->length) {
BTMTK_INFO("%s load audio pinmux num from bt.cfg", __func__);
memcpy(write_pinmux_pin_num, audio_pinmux_num->content, audio_pinmux_num->length);
} else {
BTMTK_INFO("%s load default audio pinmux num", __func__);
}
BTMTK_INFO_RAW(write_pinmux_pin_num, PINMUX_REG_NUM_7902, "%s: Pin NUM:", __func__);
if (audio_pinmux_mode->content && audio_pinmux_mode->length) {
BTMTK_INFO("%s load audio pinmux mode from bt.cfg", __func__);
memcpy(write_pinmux_pin_mode, audio_pinmux_mode->content, audio_pinmux_mode->length);
} else {
BTMTK_INFO("%s load default audio pinmux mode", __func__);
}
BTMTK_INFO_RAW(write_pinmux_pin_mode, PINMUX_REG_NUM_7902, "%s: Pin MODE:", __func__);
for (i = 0; i < PINMUX_REG_NUM_7902; i++) {
write_pinmux_cmd[WRITE_PINMUX_CMD_LEN_7902 - 2] = write_pinmux_pin_num[i];
write_pinmux_cmd[WRITE_PINMUX_CMD_LEN_7902 - 1] = write_pinmux_pin_mode[i];
BTMTK_INFO_RAW(write_pinmux_cmd, WRITE_PINMUX_CMD_LEN_7902, "%s: Send CMD:", __func__);
ret = btmtk_main_send_cmd(bdev, write_pinmux_cmd, WRITE_PINMUX_CMD_LEN_7902,
write_pinmux_event, WRITE_PINMUX_EVT_LEN_7902, 0, 0, BTMTK_TX_PKT_FROM_HOST);
if (ret < 0) {
BTMTK_ERR("%s: failed(%d)", __func__, ret);
goto exit;
}
BTMTK_INFO("%s, confirm pinmux num : 0x%02x, mode :0x%02x", __func__,
write_pinmux_pin_num[i], write_pinmux_pin_mode[i]);
}
exit:
return ret;
}
static int btmtk_set_audio_setting(struct btmtk_dev *bdev)
{
int ret = 0;
if (bdev->bt_cfg.support_audio_setting == true) {
ret = btmtk_set_audio_slave(bdev);
if (ret) {
BTMTK_ERR("%s, btmtk_sdio_set_audio_slave error(%d)", __func__, ret);
return ret;
}
if (is_mt7902(bdev->chip_id))
ret = btmtk_set_audio_pin_mux_7902(bdev);
else
ret = btmtk_set_audio_pin_mux(bdev);
if (ret) {
BTMTK_ERR("%s, btmtk_sdio_set_audio_pin_mux error(%d)", __func__, ret);
return ret;
}
}
return ret;
}
#endif // (USE_DEVICE_NODE == 0)
int btmtk_recv_acl(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btmtk_dev *bdev = NULL;
if (hdev == NULL || skb == NULL) {
BTMTK_ERR("%s, invalid parameters!", __func__);
return -EINVAL;
}
bdev = hci_get_drvdata(hdev);
if (bdev == NULL || bdev->workqueue == NULL) {
BTMTK_ERR("%s, bdev or workqueue is invalid!", __func__);
return -EINVAL;
}
skb_queue_tail(&bdev->rx_q, skb);
queue_work(bdev->workqueue, &bdev->rx_work);
/* remove it, if workqueue can't be scheduled, you can reuse it */
#if 0
skip_pkt = btmtk_dispatch_fwlog(bdev, skb);
if (skip_pkt == 0)
err = hci_recv_frame(hdev, skb);
#endif
return 0;
}
int btmtk_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btmtk_dev *bdev = NULL;
if (hdev == NULL || skb == NULL) {
BTMTK_ERR("%s, invalid parameters!", __func__);
return -EINVAL;
}
bdev = hci_get_drvdata(hdev);
if (bdev == NULL || bdev->workqueue == NULL) {
BTMTK_ERR("%s, bdev or workqueue is invalid!", __func__);
kfree_skb(skb);
return -EINVAL;
}
/* Fix up the vendor event id with 0xff for vendor specific instead
* of 0xe4 so that event send via monitoring socket can be parsed
* properly.
*/
/* if (hdr->evt == 0xe4) {
* BTMTK_DBG("%s hdr->evt is %02x", __func__, hdr->evt);
* hdr->evt = HCI_EV_VENDOR;
* }
*/
//BTMTK_DBG_RAW(skb->data, skb->len, "%s, recv evt(hci_recv_frame)", __func__);
skb_queue_tail(&bdev->rx_q, skb);
queue_work(bdev->workqueue, &bdev->rx_work);
/* remove it, if workqueue can't be scheduled, you can reuse it */
#if 0
skip_pkt = btmtk_dispatch_event(hdev, skb);
if (skip_pkt == 0)
err = hci_recv_frame(hdev, skb);
if (err < 0) {
BTMTK_ERR("%s hci_recv_failed, err = %d", __func__, err);
goto err_out;
}
#endif
return 0;
}
int btmtk_recv_iso(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btmtk_dev *bdev = NULL;
if (hdev == NULL || skb == NULL) {
BTMTK_ERR("%s, invalid parameters!", __func__);
return -EINVAL;
}
bdev = hci_get_drvdata(hdev);
if (bdev == NULL || bdev->workqueue == NULL) {
BTMTK_ERR("%s, bdev or workqueue is invalid!", __func__);
kfree_skb(skb);
return -EINVAL;
}
skb_queue_tail(&bdev->rx_q, skb);
queue_work(bdev->workqueue, &bdev->rx_work);
return 0;
}
#if (USE_DEVICE_NODE == 1)
int btmtk_recv_rhw(struct hci_dev *hdev, struct sk_buff *skb)
{
struct btmtk_dev *bdev = NULL;
if (hdev == NULL || skb == NULL) {
BTMTK_ERR("%s, invalid parameters!", __func__);
return -EINVAL;
}
bdev = hci_get_drvdata(hdev);
if (bdev == NULL || bdev->workqueue == NULL) {
BTMTK_ERR("%s, bdev or workqueue is invalid!", __func__);
kfree_skb(skb);
return -EINVAL;
}
skb_queue_tail(&bdev->rx_q, skb);
queue_work(bdev->workqueue, &bdev->rx_work);
return 0;
}
#endif
int btmtk_main_send_cmd(struct btmtk_dev *bdev, const uint8_t *cmd,
const int cmd_len, const uint8_t *event, const int event_len, int delay,
int retry, int pkt_type)
{
struct sk_buff *skb = NULL;
int ret = 0;
int state = BTMTK_STATE_INIT;
if (bdev == NULL || bdev->hdev == NULL ||
cmd == NULL || cmd_len <= 0) {
BTMTK_ERR("%s, invalid parameters!", __func__);
ret = -EINVAL;
goto exit;
}
if (!is_mt66xx(bdev->chip_id) &&
memcmp(cmd, main_info.wmt_over_hci_header, WMT_OVER_HCI_HEADER_SIZE) &&
pkt_type != BTMTK_TX_ACL_FROM_DRV &&
bdev->power_state != BTMTK_DONGLE_STATE_POWER_ON) {
BTMTK_WARN("%s: chip power isn't on, ignore this command, state is %d",
__func__, bdev->power_state);
goto exit;
}
state = btmtk_get_chip_state(bdev);
if (state == BTMTK_STATE_FW_DUMP) {
BTMTK_WARN("%s: FW dumping ongoing, don't send any cmd to FW!!!", __func__);
ret = -1;
goto exit;
}
skb = alloc_skb(cmd_len + BT_SKB_RESERVE, GFP_ATOMIC);
if (skb == NULL) {
BTMTK_ERR("%s allocate skb failed!!", __func__);
ret = -ENOMEM;
goto exit;
}
/* Reserv for core and drivers use */
skb_reserve(skb, 7);
bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
memcpy(skb->data, cmd, cmd_len);
skb->len = cmd_len;
#if ENABLESTP
skb = mtk_add_stp(bdev, skb);
#endif
/* wmt cmd and download fw patch using wmt cmd with USB interface, need use
* usb_control_msg to recv wmt event;
* other HIF don't use this method to recv wmt event
*/
ret = main_info.hif_hook.send_and_recv(bdev,
skb,
event, event_len,
delay, retry, pkt_type);
if (ret < 0) {
BTMTK_ERR("%s send_and_recv failed!!", __func__);
/* ERRNUM is used to handle when skb has been sent successful,
* but wait related event failed, in this case, we don't need to free skb here,
* otherwise, it will be double free.
*/
if (ret != -ERRNUM) {
kfree_skb(skb);
skb = NULL;
}
}
exit:
BTMTK_DBG("%s end!!", __func__);
return ret;
}
int btmtk_load_code_from_bin(u8 **image, char *bin_name, struct device *dev,
u32 *code_len, u8 retry)
{
const struct firmware *fw_entry = NULL;
int err = 0;
if (!bin_name) {
BTMTK_ERR("%s, invalid parameters!", __func__);
return -1;
}
BTMTK_INFO("%s: load %s", __func__, bin_name);
do {
err = request_firmware(&fw_entry, bin_name, dev);
if (err >= 0) {
break;
} else if (retry <= 0) {
*image = NULL;
BTMTK_INFO("%s: request_firmware %d times fail, maybe file not exist, err = %d",
__func__, 10, err);
return -1;
}
BTMTK_INFO("%s: request_firmware fail, maybe file not exist, err = %d, retry = %d",
__func__, err, retry);
msleep(100);
} while (retry-- > 0);
*image = vmalloc(ALIGN_4(fw_entry->size));
if (*image == NULL) {
*code_len = 0;
BTMTK_ERR("%s: vmalloc failed!! error code = %d", __func__, err);
release_firmware(fw_entry);
return -1;
}
memcpy(*image, fw_entry->data, fw_entry->size);
*code_len = fw_entry->size;
release_firmware(fw_entry);
return 0;
}
static u8 *btmtk_memstr(u8 *buf, u32 buf_len, char *substr)
{
u32 sublen = 0;
int i = 0;
u8 *cur = NULL;
if (buf == NULL || buf_len == 0 || substr == NULL) {
BTMTK_WARN("%s, parameter is invalid!", __func__);
return NULL;
}
sublen = strlen(substr);
cur = buf + buf_len - 1;
for (i = buf_len - 1; i >= 0; i--) {
if (buf_len - 1 - i < sublen) {
cur--;
continue;
}
if (*cur == *substr && memcmp(cur, substr, sublen) == 0)
return cur;
cur--;
}
return NULL;
}
static void btmtk_print_bt_patch_info(struct btmtk_dev *bdev, u8 *fwbuf, u32 fwbuf_len)
{
struct _PATCH_HEADER *patchHdr = NULL;
struct _Global_Descr *globalDesrc = NULL;
u8 *fw_version = NULL;
u32 fw_version_len = 0;
if (fwbuf == NULL) {
BTMTK_WARN("%s, fwbuf is NULL!", __func__);
return;
}
patchHdr = (struct _PATCH_HEADER *)fwbuf;
fw_version = btmtk_memstr(fwbuf, fwbuf_len, FW_VERSION_KEY_WORDS);
if (is_mt6639(bdev->chip_id) || is_mt7902(bdev->chip_id)
|| is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id) || is_mt66xx(bdev->chip_id))
globalDesrc = (struct _Global_Descr *)(fwbuf + FW_ROM_PATCH_HEADER_SIZE);
BTMTK_INFO("[btmtk] =============== Patch Info ==============");
if (fw_version) {
memset(main_info.fw_version_str, 0, FW_VERSION_BUF_SIZE);
fw_version_len = MIN(((u32)(fwbuf + fwbuf_len - fw_version)), FW_VERSION_BUF_SIZE);
memcpy(main_info.fw_version_str, fw_version, fw_version_len);
BTMTK_INFO("[btmtk] fw_version = %s", main_info.fw_version_str);
}
if (patchHdr) {
BTMTK_INFO("[btmtk] Built Time = %s", patchHdr->ucDateTime);
BTMTK_INFO("[btmtk] Hw Ver = 0x%04x", patchHdr->u2HwVer);
BTMTK_INFO("[btmtk] Sw Ver = 0x%04x", patchHdr->u2SwVer);
BTMTK_INFO("[btmtk] Magic Number = 0x%08x", patchHdr->u4MagicNum);
BTMTK_INFO("[btmtk] Platform = %c%c%c%c",
patchHdr->ucPlatform[0],
patchHdr->ucPlatform[1],
patchHdr->ucPlatform[2],
patchHdr->ucPlatform[3]);
} else
BTMTK_WARN("%s, patchHdr is NULL!", __func__);
if (globalDesrc) {
BTMTK_INFO("[btmtk] Patch Ver = 0x%08x", globalDesrc->u4PatchVer);
BTMTK_INFO("[btmtk] Section num = 0x%08x", globalDesrc->u4SectionNum);
} else
BTMTK_WARN("%s, globalDesrc is NULL!", __func__);
BTMTK_INFO("[btmtk] =========================================");
}
static void btmtk_print_wifi_patch_info(struct btmtk_dev *bdev, u8 *fwbuf)
{
struct _PATCH_HEADER *patchHdr = NULL;
struct _Global_Descr *globalDesrc = NULL;
if (fwbuf == NULL) {
BTMTK_WARN("%s, fwbuf is NULL!", __func__);
return;
}
patchHdr = (struct _PATCH_HEADER *)fwbuf;
if (is_mt6639(bdev->chip_id) || is_mt7902(bdev->chip_id)
|| is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id))
globalDesrc = (struct _Global_Descr *)(fwbuf + FW_ROM_PATCH_HEADER_SIZE);
BTMTK_INFO("[btmtk] =============== Wifi Patch Info ==============");
if (patchHdr) {
BTMTK_INFO("[btmtk] Built Time = %s", patchHdr->ucDateTime);
BTMTK_INFO("[btmtk] Hw Ver = 0x%04x",
((patchHdr->u2HwVer & 0x00ff) << 8) | ((patchHdr->u2HwVer & 0xff00) >> 8));
BTMTK_INFO("[btmtk] Sw Ver = 0x%04x",
((patchHdr->u2SwVer & 0x00ff) << 8) | ((patchHdr->u2SwVer & 0xff00) >> 8));
BTMTK_INFO("[btmtk] Magic Number = 0x%08x", be2cpu32(patchHdr->u4MagicNum));
BTMTK_INFO("[btmtk] Platform = %c%c%c%c",
patchHdr->ucPlatform[0],
patchHdr->ucPlatform[1],
patchHdr->ucPlatform[2],
patchHdr->ucPlatform[3]);
} else
BTMTK_WARN("%s, patchHdr is NULL!", __func__);
if (globalDesrc) {
BTMTK_INFO("[btmtk] Patch Ver = 0x%08x",
be2cpu32(globalDesrc->u4PatchVer));
BTMTK_INFO("[btmtk] Section num = 0x%08x",
be2cpu32(globalDesrc->u4SectionNum));
} else
BTMTK_WARN("%s, globalDesrc is NULL!", __func__);
BTMTK_INFO("[btmtk] =========================================");
}
static int btmtk_send_wmt_download_cmd(struct btmtk_dev *bdev, u8 *cmd,
int cmd_len, u8 *event, int event_len, struct _Section_Map *sectionMap,
u8 fw_state, u8 dma_flag, int patch_flag)
{
int payload_len = 0;
int ret = -1;
int i = 0;
u32 revert_SecSpec = 0;
if (bdev == NULL || cmd == NULL || event == NULL || sectionMap == NULL) {
BTMTK_ERR("%s: invalid parameter!", __func__);
return ret;
}
/* need refine this cmd to mtk_wmt_hdr struct*/
/* prepare HCI header */
cmd[0] = 0x01;
cmd[1] = 0x6F;
cmd[2] = 0xFC;
/* prepare WMT header */
cmd[4] = 0x01;
cmd[5] = 0x01; /* opcode */
if (fw_state == 0) {
/* prepare WMT DL cmd */
payload_len = SEC_MAP_NEED_SEND_SIZE + 2;
cmd[3] = (payload_len + 4) & 0xFF; /* length*/
cmd[6] = payload_len & 0xFF;
cmd[7] = (payload_len >> 8) & 0xFF;
cmd[8] = 0x00; /* which is the FW download state 0 */
cmd[9] = dma_flag; /* 1:using DMA to download, 0:using legacy wmt cmd*/
cmd_len = SEC_MAP_NEED_SEND_SIZE + PATCH_HEADER_SIZE;
if (patch_flag == WIFI_DOWNLOAD) {
for (i = 0; i < SECTION_SPEC_NUM; i++) {
revert_SecSpec = be2cpu32(sectionMap->u4SecSpec[i]);
memcpy(&cmd[PATCH_HEADER_SIZE] + i * sizeof(u32), (u8 *)&revert_SecSpec, sizeof(u32));
}
} else
memcpy(&cmd[PATCH_HEADER_SIZE], (u8 *)(sectionMap->u4SecSpec), SEC_MAP_NEED_SEND_SIZE);
BTMTK_INFO_RAW(cmd, cmd_len, "%s: CMD: len[%d]", __func__, cmd_len);
ret = btmtk_main_send_cmd(bdev, cmd, cmd_len,
event, event_len, DELAY_TIMES, RETRY_TIMES, BTMTK_TX_CMD_FROM_DRV);
if (ret < 0) {
BTMTK_ERR("%s: send wmd dl cmd failed, terminate!", __func__);
return PATCH_ERR;
}
if (bdev->recv_evt_len >= event_len)
return bdev->io_buf[PATCH_STATUS];
return PATCH_ERR;
}
BTMTK_ERR("%s: fw state is error!", __func__);
return ret;
}
static int btmtk_load_fw_patch_using_wmt_cmd(struct btmtk_dev *bdev,
u8 *image, u8 *fwbuf, u8 *event, int event_len, u32 patch_len, int offset)
{
int ret = 0;
u32 cur_len = 0;
s32 sent_len;
int first_block = 1;
u8 phase;
int delay = PATCH_DOWNLOAD_PHASE1_2_DELAY_TIME;
int retry = PATCH_DOWNLOAD_PHASE1_2_RETRY;
if (bdev == NULL || image == NULL || fwbuf == NULL) {
BTMTK_WARN("%s, invalid parameters!", __func__);
ret = -1;
goto exit;
}
/* loading rom patch */
while (1) {
s32 sent_len_max = UPLOAD_PATCH_UNIT - PATCH_HEADER_SIZE;
sent_len = (patch_len - cur_len) >= sent_len_max ? sent_len_max : (patch_len - cur_len);
if (sent_len > 0) {
if (first_block == 1) {
if (sent_len < sent_len_max)
phase = PATCH_PHASE3;
else
phase = PATCH_PHASE1;
first_block = 0;
} else if (sent_len == sent_len_max) {
if (patch_len - cur_len == sent_len_max)
phase = PATCH_PHASE3;
else
phase = PATCH_PHASE2;
} else {
phase = PATCH_PHASE3;
}
/* prepare HCI header */
image[0] = 0x02;
image[1] = 0x6F;
image[2] = 0xFC;
image[3] = (sent_len + 5) & 0xFF;
image[4] = ((sent_len + 5) >> 8) & 0xFF;
/* prepare WMT header */
image[5] = 0x01;
image[6] = 0x01;
image[7] = (sent_len + 1) & 0xFF;
image[8] = ((sent_len + 1) >> 8) & 0xFF;
image[9] = phase;
memcpy(&image[10], fwbuf + offset + cur_len, sent_len);
if (phase == PATCH_PHASE3) {
if (is_mt7922(bdev->chip_id) || is_mt7902(bdev->chip_id)) {
/* if secure boot enable, it need take 76ms at less
* for RSA check.
*/
delay = PATCH_DOWNLOAD_PHASE3_SECURE_BOOT_DELAY_TIME;
retry = PATCH_DOWNLOAD_PHASE3_RETRY;
} else {
delay = PATCH_DOWNLOAD_PHASE3_DELAY_TIME;
retry = PATCH_DOWNLOAD_PHASE3_RETRY;
}
}
cur_len += sent_len;
BTMTK_DBG("%s: sent_len = %d, cur_len = %d, phase = %d", __func__,
sent_len, cur_len, phase);
ret = btmtk_main_send_cmd(bdev, image, sent_len + PATCH_HEADER_SIZE,
event, event_len, delay, retry, BTMTK_TX_ACL_FROM_DRV);
if (ret < 0) {
BTMTK_INFO("%s: send patch failed, terminate", __func__);
goto exit;
}
} else
break;
}
exit:
return ret;
}
void btmtk_send_hw_err_to_host(struct btmtk_dev *bdev)
{
struct sk_buff *skb = NULL;
u8 hwerr_event[HWERR_EVT_LEN] = { 0x04, 0x10, 0x01, 0xff };
BTMTK_ERR("%s reset_stack_flag = %d!!", __func__, main_info.reset_stack_flag);
if (main_info.reset_stack_flag) {
skb = alloc_skb(HWERR_EVT_LEN + BT_SKB_RESERVE, GFP_KERNEL);
if (skb == NULL) {
BTMTK_ERR("%s allocate skb failed!!", __func__);
} else {
#if (USE_DEVICE_NODE == 0)
hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
skb->data[0] = hwerr_event[1];
skb->data[1] = hwerr_event[2];
skb->data[2] = main_info.reset_stack_flag;
skb->len = HWERR_EVT_LEN - 1;
BTMTK_DBG_RAW(skb->data, skb->len, "%s: hw err event:", __func__);
hci_recv_frame(bdev->hdev, skb);
#else
/* send to RX buffer instead of hci driver */
skb_reserve(skb, BT_SKB_RESERVE);
hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
skb->data[0] = hwerr_event[1];
skb->data[1] = hwerr_event[2];
skb->data[2] = 0x00;
skb->len = HWERR_EVT_LEN - 1;
BTMTK_DBG_RAW(skb->data, skb->len, "%s: hw err event: ", __func__);
skb_queue_tail(&bdev->rx_q, skb);
queue_work(bdev->workqueue, &bdev->rx_work);
#endif
}
}
}
static int btmtk_parsing_fw_rom_patch(struct btmtk_dev *bdev,
u8 *fwbuf, int patch_flag)
{
int loop_count = 0;
int ret = 0;
u32 section_num = 0;
u32 section_offset = 0;
u32 dl_size = 0;
u32 bin_type = 0;
u8 bin_index = 0;
u8 dma_flag = PATCH_DOWNLOAD_USING_WMT;
struct _Section_Map *sectionMap;
struct _Global_Descr *globalDescr;
if (fwbuf == NULL) {
BTMTK_WARN("%s, fwbuf is NULL!", __func__);
ret = -1;
goto exit;
}
globalDescr = (struct _Global_Descr *)(fwbuf + FW_ROM_PATCH_HEADER_SIZE);
if (patch_flag == WIFI_DOWNLOAD)
section_num = be2cpu32(globalDescr->u4SectionNum);
else
section_num = globalDescr->u4SectionNum;
if (section_num > SECTION_NUM_MAX) {
BTMTK_ERR("%s: section_num 0x%08x is an error value", __func__, section_num);
ret = -1;
goto exit;
}
if (bdev->sectionMap_table) {
kfree(bdev->sectionMap_table);
bdev->sectionMap_table = NULL;
}
bdev->sectionMap_table = kmalloc_array(section_num,
sizeof(struct _Section_Map), GFP_ATOMIC);
do {
sectionMap = (struct _Section_Map *)(fwbuf + FW_ROM_PATCH_HEADER_SIZE +
FW_ROM_PATCH_GD_SIZE + FW_ROM_PATCH_SEC_MAP_SIZE * loop_count);
if (patch_flag == WIFI_DOWNLOAD) {
/* wifi is big-endian */
section_offset = be2cpu32(sectionMap->u4SecOffset);
dl_size = be2cpu32(sectionMap->bin_info_spec.u4DLSize);
if (main_info.hif_hook.dl_dma)
dma_flag = be2cpu32(sectionMap->bin_info_spec.u4DLModeCrcType) & 0xFF;
bin_index = (be2cpu32(sectionMap->bin_info_spec.u4DLModeCrcType) >> 16) & 0xFF;
bin_type = be2cpu32(sectionMap->bin_info_spec.u4SecType);
} else {
/* BT is little-endian */
section_offset = sectionMap->u4SecOffset;
dl_size = sectionMap->bin_info_spec.u4DLSize;
/*
* loop_count = 0: BGF patch
* 1: BT ILM
* only BT ILM support DL DMA for Buzzard
*/
if (main_info.hif_hook.dl_dma)
dma_flag = le2cpu32(sectionMap->bin_info_spec.u4DLModeCrcType) & 0xFF;
bin_index = (le2cpu32(sectionMap->bin_info_spec.u4DLModeCrcType) >> 16) & 0xFF;
bin_type = sectionMap->bin_info_spec.u4SecType;
}
BTMTK_INFO("%s: loop_count = %d, section_offset = 0x%08x, download patch_len = 0x%08x, "
"dl mode = %d, section bin_type = 0x%08x, bin_index =%d\n",
__func__, loop_count, section_offset, dl_size, dma_flag, bin_type, bin_index);
memcpy(&bdev->sectionMap_table[loop_count], sectionMap, sizeof(struct _Section_Map));
} while (++loop_count < section_num);
exit:
return ret;
}
int btmtk_load_fw_by_bin_info(struct btmtk_dev *bdev,
u8 *fwbuf, u32 binInfo, int mode)
{
u8 *pos;
int loop_count = 0;
int ret = 0;
u32 section_num = 0;
u32 section_offset = 0;
u32 dl_size = 0;
u32 temp_type = 0;
u8 temp_index = 0;
int patch_status = 0;
int retry = 20;
u8 dma_flag = PATCH_DOWNLOAD_USING_WMT;
struct _Section_Map *sectionMap;
struct _Global_Descr *globalDescr;
u8 event[LD_PATCH_EVT_LEN] = {0x04, 0xE4, 0x05, 0x02, 0x01, 0x01, 0x00, 0x00}; /* event[7] is status*/
/*
* flag 0 for bin_index u8
* flag 1 for bin_type u32
*/
BTMTK_INFO("%s enter : mode %d expected bin_info = 0x%08x", __func__, mode, binInfo);
if (fwbuf == NULL) {
BTMTK_WARN("%s, fwbuf is NULL!", __func__);
ret = -1;
goto exit;
}
globalDescr = (struct _Global_Descr *)(fwbuf + FW_ROM_PATCH_HEADER_SIZE);
section_num = globalDescr->u4SectionNum;
if (section_num > SECTION_NUM_MAX) {
BTMTK_ERR("%s: section_num 0x%08x is an error value", __func__, section_num);
ret = -1;
goto exit;
}
do {
sectionMap = &bdev->sectionMap_table[loop_count];
temp_index = (le2cpu32(sectionMap->bin_info_spec.u4DLModeCrcType) >> 16) & 0xFF;
temp_type = le2cpu32(sectionMap->bin_info_spec.u4SecType);
if (mode == DOWNLOAD_BY_TYPE) {
if (temp_type == binInfo) {
BTMTK_INFO("%s, download by bin type", __func__);
break;
}
} else {
if (temp_index == (binInfo & 0xFF)) {
BTMTK_INFO("%s, download by bin index", __func__);
break;
}
}
} while (++loop_count < section_num);
if (loop_count == section_num) {
BTMTK_ERR("%s: bin_info 0x%08x is not found", __func__, binInfo);
ret = 0;
goto exit;
}
pos = kmalloc(UPLOAD_PATCH_UNIT, GFP_ATOMIC);
if (!pos) {
BTMTK_ERR("%s: alloc memory failed", __func__);
ret = -1;
goto exit;
}
/* BT is little-endian */
section_offset = sectionMap->u4SecOffset;
dl_size = sectionMap->bin_info_spec.u4DLSize;
/*
* loop_count = 0: BGF patch
* 1: BT ILM
* only BT ILM support DL DMA for Buzzard
*/
if (main_info.hif_hook.dl_dma)
dma_flag = le2cpu32(sectionMap->bin_info_spec.u4DLModeCrcType) & 0xFF;
if (mode == DOWNLOAD_BY_INDEX) {
/* daynamic download need downloaded by wmt cmd */
dma_flag = PATCH_DOWNLOAD_USING_WMT;
}
BTMTK_INFO("%s: section_count = %d, section bin_type = 0x%08x, bin_index = %d\n",
__func__, loop_count, temp_type, temp_index);
if (dl_size > 0) {
retry = 20;
do {
patch_status = btmtk_send_wmt_download_cmd(bdev, pos, 0,
event, LD_PATCH_EVT_LEN - 1, sectionMap, 0, dma_flag, BT_DOWNLOAD);
BTMTK_INFO("%s: patch_status %d", __func__, patch_status);
if (patch_status > PATCH_READY || patch_status == PATCH_ERR) {
BTMTK_ERR("%s: patch_status error", __func__);
ret = -1;
goto err;
} else if (patch_status == PATCH_READY) {
BTMTK_INFO("%s: no need to load rom patch section%d", __func__, loop_count);
goto err;
} else if (patch_status == PATCH_IS_DOWNLOAD_BY_OTHER) {
msleep(100);
retry--;
} else if (patch_status == PATCH_NEED_DOWNLOAD) {
break; /* Download ROM patch directly */
}
} while (retry > 0);
if (patch_status == PATCH_IS_DOWNLOAD_BY_OTHER) {
BTMTK_WARN("%s: Hold by another fun more than 2 seconds", __func__);
ret = -1;
goto err;
}
if (dma_flag == PATCH_DOWNLOAD_USING_DMA && main_info.hif_hook.dl_dma) {
BTMTK_INFO("%s: btmtk_load_fw_patch_using_dma!", __func__);
/* using DMA to download fw patch*/
ret = main_info.hif_hook.dl_dma(bdev,
pos, fwbuf,
dl_size, section_offset);
if (ret < 0) {
BTMTK_ERR("%s: btmtk_load_fw_patch_using_dma failed!", __func__);
goto err;
}
} else {
BTMTK_INFO("%s: btmtk_load_fw_patch_using_wmt_cmd!", __func__);
/* using legacy wmt cmd to download fw patch */
ret = btmtk_load_fw_patch_using_wmt_cmd(bdev, pos, fwbuf, event,
LD_PATCH_EVT_LEN - 1, dl_size, section_offset);
if (ret < 0) {
BTMTK_ERR("%s: btmtk_load_fw_patch_using_wmt_cmd failed!", __func__);
goto err;
}
}
BTMTK_INFO("%s end", __func__);
}
err:
kfree(pos);
pos = NULL;
exit:
return ret;
}
static int btmtk_send_fw_rom_patch_79xx(struct btmtk_dev *bdev,
u8 *fwbuf, int patch_flag)
{
u8 *pos;
int loop_count = 0;
int ret = 0;
u32 section_num = 0;
u32 section_offset = 0;
u32 dl_size = 0;
int patch_status = 0;
int retry = 20;
u8 dma_flag = PATCH_DOWNLOAD_USING_WMT;
struct _Section_Map *sectionMap;
struct _Global_Descr *globalDescr;
u8 event[LD_PATCH_EVT_LEN] = {0x04, 0xE4, 0x05, 0x02, 0x01, 0x01, 0x00, 0x00}; /* event[7] is status*/
#if DEBUG_LD_PATCH_TIME
struct timeval tv_start, tv_bgf, tv_ilm;
u32 dlt_dma = 0, dlt_all = 0;
memset(&tv_start, 0, sizeof(tv_start));
memset(&tv_bgf, 0, sizeof(tv_bgf));
memset(&tv_ilm, 0, sizeof(tv_ilm));
// btmtk_do_gettimeofday(&tv_start);
#endif
if (fwbuf == NULL) {
BTMTK_WARN("%s, fwbuf is NULL!", __func__);
ret = -1;
goto exit;
}
globalDescr = (struct _Global_Descr *)(fwbuf + FW_ROM_PATCH_HEADER_SIZE);
BTMTK_INFO("%s: loading rom patch...\n", __func__);
if (patch_flag == WIFI_DOWNLOAD)
section_num = be2cpu32(globalDescr->u4SectionNum);
else
section_num = globalDescr->u4SectionNum;
BTMTK_INFO("%s: section_num = 0x%08x\n", __func__, section_num);
if (section_num > SECTION_NUM_MAX) {
BTMTK_ERR("%s: section_num 0x%08x is an error value", __func__, section_num);
ret = -1;
goto exit;
}
pos = kmalloc(UPLOAD_PATCH_UNIT, GFP_ATOMIC);
if (!pos) {
BTMTK_ERR("%s: alloc memory failed", __func__);
ret = -1;
goto exit;
}
do {
sectionMap = (struct _Section_Map *)(fwbuf + FW_ROM_PATCH_HEADER_SIZE +
FW_ROM_PATCH_GD_SIZE + FW_ROM_PATCH_SEC_MAP_SIZE * loop_count);
if (patch_flag == WIFI_DOWNLOAD) {
/* wifi is big-endian */
section_offset = be2cpu32(sectionMap->u4SecOffset);
dl_size = be2cpu32(sectionMap->bin_info_spec.u4DLSize);
if (main_info.hif_hook.dl_dma)
dma_flag = be2cpu32(sectionMap->bin_info_spec.u4DLModeCrcType) & 0xFF;
} else {
/* BT & ZB are little-endian */
section_offset = sectionMap->u4SecOffset;
dl_size = sectionMap->bin_info_spec.u4DLSize;
/*
* loop_count = 0: BGF patch
* 1: BT ILM
* only BT ILM support DL DMA for Buzzard
*/
if (main_info.hif_hook.dl_dma)
dma_flag = le2cpu32(sectionMap->bin_info_spec.u4DLModeCrcType) & 0xFF;
}
BTMTK_INFO("%s: loop_count = %d, section_offset = 0x%08x, download patch_len = 0x%08x, dl mode = %d\n",
__func__, loop_count, section_offset, dl_size, dma_flag);
if (dl_size > 0) {
retry = 20;
do {
patch_status = btmtk_send_wmt_download_cmd(bdev, pos, 0,
event, LD_PATCH_EVT_LEN - 1, sectionMap, 0, dma_flag, patch_flag);
BTMTK_INFO("%s: patch_status %d", __func__, patch_status);
if (patch_status > PATCH_READY || patch_status == PATCH_ERR) {
BTMTK_ERR("%s: patch_status error", __func__);
ret = -1;
goto err;
} else if (patch_status == PATCH_READY) {
BTMTK_INFO("%s: no need to load rom patch section%d", __func__, loop_count);
goto next_section;
} else if (patch_status == PATCH_IS_DOWNLOAD_BY_OTHER) {
msleep(100);
retry--;
} else if (patch_status == PATCH_NEED_DOWNLOAD) {
break; /* Download ROM patch directly */
}
} while (retry > 0);
if (patch_status == PATCH_IS_DOWNLOAD_BY_OTHER) {
BTMTK_WARN("%s: Hold by another fun more than 2 seconds", __func__);
ret = -1;
goto err;
}
if (dma_flag == PATCH_DOWNLOAD_USING_DMA && main_info.hif_hook.dl_dma) {
/* using DMA to download fw patch*/
ret = main_info.hif_hook.dl_dma(bdev,
pos, fwbuf,
dl_size, section_offset);
if (ret < 0) {
BTMTK_ERR("%s: btmtk_load_fw_patch_using_dma failed!", __func__);
goto err;
}
} else {
/* using legacy wmt cmd to download fw patch */
ret = btmtk_load_fw_patch_using_wmt_cmd(bdev, pos, fwbuf, event,
LD_PATCH_EVT_LEN - 1, dl_size, section_offset);
if (ret < 0) {
BTMTK_ERR("%s: btmtk_load_fw_patch_using_wmt_cmd failed!", __func__);
goto err;
}
}
}
/* FW Download finished */
/* remove it, comment from fw dl owner
* if (patch_flag == WIFI_DOWNLOAD) {
* if (loop_count == section_num - 1) {
* mdelay(500);
* }
* }
*/
#if DEBUG_LD_PATCH_TIME
if (loop_count == 0) {
// btmtk_do_gettimeofday(&tv_bgf);
} else if (loop_count == 1) {
// btmtk_do_gettimeofday(&tv_ilm);
if (tv_bgf.tv_sec != 0 || tv_bgf.tv_usec != 0) {
if (tv_ilm.tv_sec >= tv_bgf.tv_sec)
dlt_dma = (tv_ilm.tv_sec - tv_bgf.tv_sec) * 1000;
else
dlt_dma = (~(tv_bgf.tv_sec - tv_ilm.tv_sec) + 1) * 1000;
dlt_dma += (tv_ilm.tv_usec - tv_bgf.tv_usec) / 1000;
}
if (tv_ilm.tv_sec >= tv_start.tv_sec)
dlt_all = (tv_ilm.tv_sec - tv_start.tv_sec) * 1000;
else
dlt_all = (~(tv_start.tv_sec - tv_ilm.tv_sec) + 1) * 1000;
dlt_all += (tv_ilm.tv_usec - tv_start.tv_usec) / 1000;
BTMTK_INFO("LD PATCH 1 tv_start: tv_sec:%zu, tv_usec:%zu.",
tv_start.tv_sec, tv_start.tv_usec);
BTMTK_INFO("LD PATCH 2 tv_bgf: tv_sec:%zu, tv_usec:%zu.",
tv_bgf.tv_sec, tv_bgf.tv_usec);
BTMTK_INFO("LD PATCH 3 tv_ilm: tv_sec:%zu, tv_usec:%zu.",
tv_ilm.tv_sec, tv_ilm.tv_usec);
if (dlt_dma != 0)
BTMTK_INFO("LD PATCH time: ILM_DMA:%ums, ALL:%ums.",
dlt_dma, dlt_all);
else
BTMTK_INFO("LD PATCH time: ILM_DMA:%ums.",
dlt_all);
}
#endif
next_section:
continue;
} while (++loop_count < section_num);
err:
kfree(pos);
pos = NULL;
exit:
return ret;
}
int btmtk_dynamic_load_rom_patch(struct btmtk_dev *bdev, u32 binInfo)
{
int ret = 0;
u8 *rom_patch = NULL;
unsigned int rom_patch_len = 0;
if (!bdev) {
BTMTK_ERR("%s, invalid parameters!", __func__);
return -EINVAL;
}
/* should reload bt fw bin name , may be recover by co-dl wifi*/
#if 0
if (bdev->flavor) {
(void)snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN, "BT_RAM_CODE_MT%04x_1a_%x_hdr.bin",
bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1);
} else {
(void)snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN, "BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1);
}
#endif
if (is_mt6639(bdev->chip_id) || is_mt66xx(bdev->chip_id))
(void)snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN,
#if (USE_DEVICE_NODE == 0)
"BT_RAM_CODE_MT6639_2_1_hdr.bin");
#else
"BT_RAM_CODE_MT6639_1_1_hdr.bin");
//"BT_RAM_CODE_MT6639_1_1_nonenc_hdr.bin");
#endif
BTMTK_INFO("%s: rom patch file name is %s", __func__,
bdev->rom_patch_bin_file_name);
btmtk_load_code_from_bin(&rom_patch, bdev->rom_patch_bin_file_name, NULL,
&rom_patch_len, 10);
if (!rom_patch) {
BTMTK_ERR("%s: please assign a rom patch(/etc/firmware/%s)or(/lib/firmware/%s)",
__func__, bdev->rom_patch_bin_file_name, bdev->rom_patch_bin_file_name);
ret = -1;
goto err;
}
/* dynamic download should download section by bin index */
ret = btmtk_load_fw_by_bin_info(bdev, rom_patch, binInfo, DOWNLOAD_BY_INDEX);
if (ret < 0) {
BTMTK_ERR("%s, btmtk_load_rom_patch_connac3 failed!, bin type is 0x%08x",
__func__, binInfo);
ret = -1;
}
err:
if (rom_patch)
vfree(rom_patch);
return ret;
}
int btmtk_load_rom_patch_connac3(struct btmtk_dev *bdev, int patch_flag)
{
int ret = 0;
u8 *rom_patch = NULL;
unsigned int rom_patch_len = 0;
int i = 0;
u32 bt_bin_type[BT_BIN_TYP_NUM] = {0x00000000, 0x00000002, 0x00000003, 0x00000080,
0x00000090, 0x00010000};
//u32 wf_bin_type[] = {0x00000100};
BTMTK_INFO("%s, patch_flag = %d!", __func__, patch_flag);
if (!bdev) {
BTMTK_ERR("%s, invalid parameters!", __func__);
return -EINVAL;
}
if (patch_flag == WIFI_DOWNLOAD) {
/* For 7902, we can't read flavor from controller successfully */
if (bdev->flavor)
/* if flavor equals 1, it represent 7920, else it represent 7921 */
(void)snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN,
"WIFI_MT%04x_patch_mcu_1a_%x_hdr.bin",
bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1);
else
(void)snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN,
"WIFI_MT%04x_patch_mcu_1_%x_hdr.bin",
bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1);
} else if (patch_flag == ZB_DOWNLOAD) {
if (bdev->flavor)
(void)snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN,
"ZB_RAM_CODE_MT%04x_1a_%x_hdr.bin",
bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1);
else
(void)snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN,
"ZB_RAM_CODE_MT%04x_1_%x_hdr.bin",
bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1);
} else if (patch_flag == BT_DOWNLOAD) {
BTMTK_INFO("%s: BT_DOWNLOAD %u", __func__, bdev->chip_id);
if (is_mt6639(bdev->chip_id) || is_mt66xx(bdev->chip_id))
(void)snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN,
#if (USE_DEVICE_NODE == 0)
"BT_RAM_CODE_MT6639_2_1_hdr.bin");
#else
"BT_RAM_CODE_MT6639_1_1_hdr.bin");
//"BT_RAM_CODE_MT6639_1_1_nonenc_hdr.bin");
#endif
} else
BTMTK_ERR("%s: unknow patch_flag", __func__);
BTMTK_INFO("%s: rom patch file name is %s, bt_cfg_file_name is %s", __func__,
bdev->rom_patch_bin_file_name, bdev->bt_cfg_file_name);
btmtk_load_code_from_bin(&rom_patch, bdev->rom_patch_bin_file_name, NULL,
&rom_patch_len, 10);
if (!rom_patch) {
BTMTK_ERR("%s: please assign a rom patch(/etc/firmware/%s)or(/lib/firmware/%s)",
__func__, bdev->rom_patch_bin_file_name, bdev->rom_patch_bin_file_name);
ret = -1;
goto err;
}
if (patch_flag == WIFI_DOWNLOAD) {
/*Display rom patch info*/
btmtk_print_wifi_patch_info(bdev, rom_patch);
ret = btmtk_send_fw_rom_patch_79xx(bdev, rom_patch, patch_flag);
if (ret < 0) {
BTMTK_ERR("%s, btmtk_send_fw_rom_patch_79xx failed!", __func__);
goto err;
}
} else if (patch_flag == ZB_DOWNLOAD) {
/* ZB header is the same to ZB little endian */
btmtk_print_bt_patch_info(bdev, rom_patch, rom_patch_len);
ret = btmtk_send_fw_rom_patch_79xx(bdev, rom_patch, patch_flag);
if (ret < 0) {
BTMTK_ERR("%s, btmtk_send_fw_rom_patch_79xx failed!", __func__);
goto err;
}
} else {
btmtk_print_bt_patch_info(bdev, rom_patch, rom_patch_len);
btmtk_parsing_fw_rom_patch(bdev, rom_patch, patch_flag);
for (i = 0; i < BT_BIN_TYP_NUM; i++) {
ret = btmtk_load_fw_by_bin_info(bdev, rom_patch, bt_bin_type[i],
DOWNLOAD_BY_TYPE);
if (ret < 0) {
BTMTK_ERR("%s failed!, bin type is 0x%08x",
__func__, bt_bin_type[i]);
goto err;
}
#if (USE_DEVICE_NODE == 1)
/* Send efem command before bt cal */
if (bt_bin_type[i] == 0x00000003) {
ret = btmtk_send_connfem_cmd(bdev);
if (ret < 0) {
BTMTK_ERR("%s send connfem fail", __func__);
goto err;
}
}
#endif
}
}
bdev->power_state = BTMTK_DONGLE_STATE_POWER_OFF;
BTMTK_INFO("%s end", __func__);
err:
if (rom_patch)
vfree(rom_patch);
return ret;
}
/* need to remove after modify to using function pointer*/
__weak int32_t bgfsys_bt_patch_dl(void)
{
BTMTK_ERR("No bgfsys_bt_patch_dl function");
return -1;
}
/* need to remove after modify to using function pointer*/
__weak int32_t btmtk_set_sleep(struct hci_dev *hdev, u_int8_t need_wait)
{
//BTMTK_ERR("No btmtk_set_sleep function");
return -1;
}
int btmtk_load_rom_patch_66xx(struct btmtk_dev *bdev)
{
return bgfsys_bt_patch_dl();
}
int btmtk_load_rom_patch(struct btmtk_dev *bdev)
{
int err = -1;
if (!bdev || !bdev->hdev) {
BTMTK_ERR("%s: invalid parameters!", __func__);
return err;
}
if (is_mt6639(bdev->chip_id) || is_mt7902(bdev->chip_id)
|| is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id)) {
err = btmtk_load_rom_patch_connac3(bdev, BT_DOWNLOAD);
if (err < 0) {
BTMTK_ERR("%s: btmtk_load_rom_patch bt patch failed!", __func__);
return err;
}
#if CFG_SUPPORT_BT_DL_ZB_PATCH
if (is_mt7902(bdev->chip_id)) {
err = btmtk_load_rom_patch_connac3(bdev, ZB_DOWNLOAD);
if (err < 0) {
BTMTK_WARN("%s: btmtk_load_rom_patch ZB patch failed!", __func__);
err = 0;
}
}
#endif
#if CFG_SUPPORT_BT_DL_WIFI_PATCH
err = btmtk_load_rom_patch_connac3(bdev, WIFI_DOWNLOAD);
if (err < 0) {
BTMTK_WARN("%s: btmtk_load_rom_patch wifi patch failed!", __func__);
err = 0;
}
#endif
} else if (is_mt66xx(bdev->chip_id)) {
err = btmtk_load_rom_patch_connac3(bdev, BT_DOWNLOAD);
} else
BTMTK_WARN("%s: unknown chip id (%d)", __func__, bdev->chip_id);
BTMTK_DBG("%s: end, err[%d]", __func__, err);
return err;
}
struct btmtk_dev *btmtk_get_dev(void)
{
int i = 0;
struct btmtk_dev *tmp_bdev = NULL;
for (i = 0; i < btmtk_intf_num; i++) {
/* Find empty slot for newly probe interface.
* Judged from load_rom_patch is done and
* Identified chip_id from cap_init.
*/
if (g_bdev[i]->hdev == NULL) {
if (i == 0)
g_bdev[i]->dongle_index = i;
else
g_bdev[i]->dongle_index = g_bdev[i - 1]->dongle_index + 1;
/* reset pin initial value need to be -1, used to judge after
* disconnected before probe, can't do chip reset
*/
g_bdev[i]->bt_cfg.dongle_reset_gpio_pin = -1;
tmp_bdev = g_bdev[i];
/* Hook pre-defined table on state machine */
g_bdev[i]->cif_state = (struct btmtk_cif_state *)g_cif_state;
break;
}
}
BTMTK_INFO("%s use g_bdev[%d]", __func__, i);
return tmp_bdev;
}
void btmtk_release_dev(struct btmtk_dev *bdev)
{
int i = 0;
struct btmtk_dev *tmp_bdev = NULL;
BTMTK_INFO("%s", __func__);
tmp_bdev = bdev;
if (tmp_bdev != NULL) {
for (i = 0; i < btmtk_intf_num; i++) {
/* Find slot on probed interface.
* Judged from load_rom_patch is done and
* Identified chip_id from cap_init.
*/
if (memcmp(tmp_bdev, g_bdev[i], sizeof(*tmp_bdev)) == 0) {
memset(tmp_bdev, 0, sizeof(*tmp_bdev));
/* reset pin initial value need to be -1, used to judge after
* disconnected before probe, can't do chip reset
*/
bdev->bt_cfg.dongle_reset_gpio_pin = -1;
tmp_bdev = NULL;
break;
}
}
}
}
struct btmtk_dev *btmtk_allocate_dev_memory(struct device *dev)
{
struct btmtk_dev *bdev;
size_t len = sizeof(*bdev);
BTMTK_INFO("%s", __func__);
if (dev != NULL)
bdev = devm_kzalloc(dev, len, GFP_KERNEL);
else
bdev = kzalloc(len, GFP_KERNEL);
if (!bdev)
return NULL;
btmtk_set_chip_state(bdev, BTMTK_STATE_INIT);
return bdev;
}
void btmtk_free_dev_memory(struct device *dev, struct btmtk_dev *bdev)
{
BTMTK_INFO("%s", __func__);
if (bdev != NULL) {
if (dev != NULL)
devm_kfree(dev, bdev);
else
kfree(bdev);
}
}
static int btmtk_calibration_flow(struct btmtk_dev *bdev)
{
if (!bdev) {
BTMTK_ERR("%s: bdev is NULL !", __func__);
return -1;
}
btmtk_cif_send_calibration(bdev);
BTMTK_INFO("%s done", __func__);
return 0;
}
int btmtk_send_wmt_power_on_cmd(struct btmtk_dev *bdev)
{
u8 cmd[WMT_POWER_ON_CMD_LEN] = { 0x01, 0x6F, 0xFC, 0x06, 0x01, 0x06, 0x02, 0x00, 0x00, 0x01 };
u8 event[WMT_POWER_ON_EVT_HDR_LEN] = { 0x04, 0xE4, 0x05, 0x02, 0x06, 0x01, 0x00 }; /* event[7] is key */
int ret = -1, retry = RETRY_TIMES;
if (!bdev) {
BTMTK_ERR("%s: bdev is NULL !", __func__);
return ret;
}
retry_again:
ret = btmtk_main_send_cmd(bdev,
cmd, WMT_POWER_ON_CMD_LEN,
event, WMT_POWER_ON_EVT_HDR_LEN,
WMT_DELAY_TIMES, RETRY_TIMES, BTMTK_TX_CMD_FROM_DRV);
if (ret < 0) {
BTMTK_ERR("%s: failed(%d)", __func__, ret);
bdev->power_state = BTMTK_DONGLE_STATE_ERROR;
ret = -1;
} else if (ret == 0 && bdev->recv_evt_len > 0) {
switch (bdev->io_buf[WMT_POWER_ON_EVT_RESULT_OFFSET]) {
case 0: /* successful */
BTMTK_INFO("%s: OK", __func__);
bdev->power_state = BTMTK_DONGLE_STATE_POWER_ON;
break;
case 2: /* TODO:retry */
if (retry > 0) {
/* comment from fw, we need to retry a sec until power on sucessfully. */
retry--;
BTMTK_INFO("%s: need to try again", __func__);
msleep(50);
goto retry_again;
}
break;
default:
BTMTK_WARN("%s: Unknown result: %02X", __func__, bdev->io_buf[WMT_POWER_ON_EVT_RESULT_OFFSET]);
bdev->power_state = BTMTK_DONGLE_STATE_ERROR;
ret = -1;
break;
}
}
return ret;
}
int btmtk_send_wmt_power_off_cmd(struct btmtk_dev *bdev)
{
u8 cmd[WMT_POWER_OFF_CMD_LEN] = { 0x01, 0x6F, 0xFC, 0x06, 0x01, 0x06, 0x02, 0x00, 0x00, 0x00 };
/* To-Do, for event check */
u8 event[WMT_POWER_OFF_EVT_HDR_LEN] = { 0x04, 0xE4, 0x05, 0x02, 0x06, 0x01, 0x00 };
int ret = -1;
if (!bdev) {
BTMTK_ERR("%s: bdev is NULL !", __func__);
return ret;
}
if (bdev->power_state == BTMTK_DONGLE_STATE_POWER_OFF) {
BTMTK_WARN("%s: power_state already power off", __func__);
return 0;
}
ret = btmtk_main_send_cmd(bdev,
cmd, WMT_POWER_OFF_CMD_LEN,
event, WMT_POWER_OFF_EVT_HDR_LEN,
DELAY_TIMES, RETRY_TIMES, BTMTK_TX_CMD_FROM_DRV);
if (ret < 0) {
BTMTK_ERR("%s: failed(%d)", __func__, ret);
bdev->power_state = BTMTK_DONGLE_STATE_ERROR;
return ret;
}
bdev->power_state = BTMTK_DONGLE_STATE_POWER_OFF;
BTMTK_INFO("%s done", __func__);
return ret;
}
/* Check power status, if power is off, try to set power on */
int btmtk_reset_power_on(struct btmtk_dev *bdev)
{
if (bdev->power_state == BTMTK_DONGLE_STATE_POWER_OFF) {
bdev->power_state = BTMTK_DONGLE_STATE_ERROR;
if (btmtk_send_wmt_power_on_cmd(bdev) < 0)
return -1;
bdev->power_state = BTMTK_DONGLE_STATE_POWER_ON;
}
if (bdev->power_state != BTMTK_DONGLE_STATE_POWER_ON) {
BTMTK_WARN("%s: end of Incorrect state:%d", __func__, bdev->power_state);
return -1;
}
BTMTK_INFO("%s: end success", __func__);
return 0;
}
int btmtk_picus_enable(struct btmtk_dev *bdev)
{
u8 dft_enable_cmd[PICUS_ENABLE_CMD_LEN] = { 0x01, 0x5D, 0xFC, 0x04, 0x00, 0x00, 0x02, 0x02 };
u8 *enable_cmd = NULL;
u8 enable_event[PICUS_ENABLE_EVT_HDR_LEN] = { 0x04, 0x0E, 0x08, 0x01, 0x5D, 0xFC, 0x00, 0x00, 0x00 };
int enable_len = 0;
int ret = -1; /* if successful, 0 */
struct fw_cfg_struct *picus_setting = &bdev->bt_cfg.picus_enable;
BTMTK_INFO("%s", __func__);
if (picus_setting->content && picus_setting->length) {
BTMTK_INFO("%s load picus from bt.cfg", __func__);
enable_cmd = picus_setting->content;
enable_len = picus_setting->length;
} else {
enable_cmd = dft_enable_cmd;
enable_len = PICUS_ENABLE_CMD_LEN;
}
BTMTK_INFO_RAW(enable_cmd, enable_len, "%s: Send CMD:", __func__);
if (is_mt6639(bdev->chip_id) || is_mt7902(bdev->chip_id)
|| is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id))
ret = btmtk_main_send_cmd(bdev,
enable_cmd, enable_len,
enable_event, PICUS_ENABLE_EVT_HDR_LEN,
DELAY_TIMES, RETRY_TIMES,
BTMTK_TX_PKT_FROM_HOST);
else
BTMTK_WARN("%s: not support for 0x%x", __func__, bdev->chip_id);
BTMTK_INFO("%s: ret %d", __func__, ret);
return ret;
}
int btmtk_picus_disable(struct btmtk_dev *bdev)
{
u8 dft_disable_cmd[PICUS_DISABLE_CMD_LEN] = { 0x01, 0x5D, 0xFC, 0x04, 0x00, 0x00, 0x02, 0x00 };
u8 dft_disable_event[PICUS_DISABLE_EVT_HDR_LEN] = { 0x04, 0x0E, 0x08, 0x01, 0x5D, 0xFC, 0x00, 0x00, 0x00 };
int ret = -1; /* if successful, 0 */
BTMTK_INFO("%s\n", __func__);
if (is_mt6639(bdev->chip_id) || is_mt7902(bdev->chip_id)
|| is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id))
ret = btmtk_main_send_cmd(bdev,
dft_disable_cmd, PICUS_DISABLE_CMD_LEN,
dft_disable_event, PICUS_DISABLE_EVT_HDR_LEN,
DELAY_TIMES, RETRY_TIMES,
BTMTK_TX_PKT_FROM_HOST);
else
BTMTK_WARN("%s: not support for 0x%x", __func__, bdev->chip_id);
BTMTK_INFO("%s: ret %d", __func__, ret);
return ret;
}
int btmtk_load_fw_cfg_setting(char *block_name, struct fw_cfg_struct *save_content,
int counter, u8 *searchcontent, enum fw_cfg_index_len index_length)
{
int ret = 0, i = 0;
u16 temp_len = 0;
u8 temp[TEMP_LEN]; /* save for total hex number */
unsigned long parsing_result = 0;
char *search_result = NULL, *ptr = NULL;
char *search_end = NULL;
char search[SEARCH_LEN];
char *next_block = NULL;
char number[CHAR2HEX_SIZE + 1]; /* 1 is for '\0' */
memset(search, 0, SEARCH_LEN);
memset(temp, 0, TEMP_LEN);
memset(number, 0, CHAR2HEX_SIZE + 1);
if (searchcontent == NULL) {
BTMTK_ERR("%s: Searchcontent is NULL", __func__);
return -1;
}
/* search block name */
for (i = 0; i < counter; i++) {
temp_len = 0;
if (index_length == FW_CFG_INX_LEN_2) /* EX: APCF01 */
(void)snprintf(search, SEARCH_LEN, "%s%02d:", block_name, i);
else if (index_length == FW_CFG_INX_LEN_3) /* EX: APCF001 */
(void)snprintf(search, SEARCH_LEN, "%s%03d:", block_name, i);
else
(void)snprintf(search, SEARCH_LEN, "%s:", block_name);
ret = 0;
ptr = search_result = strstr((char *)searchcontent, search);
if (search_result) {
/* Add # for comment in bt.cfg */
if (ptr > (char *)searchcontent) {
ptr--;
while ((*ptr == ' ') && (ptr != (char *)searchcontent))
ptr--;
if (*ptr == '#') {
BTMTK_WARN("%s: %s has been ignored", __func__, search);
return -1;
}
}
memset(temp, 0, TEMP_LEN);
search_result = strstr(search_result, "0x");
if (search_result == NULL) {
BTMTK_ERR("%s: search_result is NULL", __func__);
return -1;
}
/* find next line as end of this command line, if NULL means last line */
next_block = strstr(search_result, ":");
if (next_block == NULL)
BTMTK_WARN("%s: if NULL means last line", __func__);
/* Add HCI packet type to front of each command/event */
if (!memcmp(block_name, "APCF", sizeof("APCF")) ||
!memcmp(block_name, "RADIOOFF", sizeof("RADIOOFF")) ||
!memcmp(block_name, "RADIOON", sizeof("RADIOON")) ||
!memcmp(block_name, "APCF_RESUME", sizeof("APCF_RESUME")) ||
!memcmp(block_name, "VENDOR_CMD", sizeof("VENDOR_CMD")) ||
!memcmp(block_name, "PHASE1_WMT_CMD", sizeof("PHASE1_WMT_CMD"))) {
temp[0] = 0x01;
temp_len++;
} else if (!memcmp(block_name, "RADIOOFF_STATUS_EVENT", sizeof("RADIOOFF_STATUS_EVENT")) ||
!memcmp(block_name, "RADIOOFF_COMPLETE_EVENT", sizeof("RADIOOFF_COMPLETE_EVENT")) ||
!memcmp(block_name, "RADIOON_STATUS_EVENT", sizeof("RADIOON_STATUS_EVENT")) ||
!memcmp(block_name, "RADIOON_COMPLETE_EVENT", sizeof("RADIOON_COMPLETE_EVENT"))) {
temp[0] = 0x04;
temp_len++;
}
do {
search_end = strstr(search_result, ",");
if (search_end == NULL) {
BTMTK_ERR("%s: Search_end is NULL", __func__);
break;
}
if (search_end - search_result != CHAR2HEX_SIZE) {
BTMTK_ERR("%s: Incorrect Format in %s", __func__, search);
break;
}
memset(number, 0, CHAR2HEX_SIZE + 1);
memcpy(number, search_result, CHAR2HEX_SIZE);
ret = kstrtoul(number, 0, &parsing_result);
if (ret == 0) {
if (temp_len >= TEMP_LEN) {
BTMTK_ERR("%s: %s data over %d", __func__, search, TEMP_LEN);
break;
}
temp[temp_len] = parsing_result;
temp_len++;
} else {
BTMTK_WARN("%s: %s kstrtoul fail: %d", __func__, search, ret);
break;
}
search_result = strstr(search_end, "0x");
if (search_result == NULL) {
BTMTK_ERR("%s: search_result is NULL", __func__);
break;
}
} while (search_result < next_block || (search_result && next_block == NULL));
} else
BTMTK_DBG("%s: %s is not found in %d", __func__, search, i);
if (temp_len && temp_len < TEMP_LEN) {
BTMTK_INFO("%s: %s found & stored in %d", __func__, search, i);
save_content[i].content = kzalloc(temp_len, GFP_KERNEL);
if (save_content[i].content == NULL) {
BTMTK_ERR("%s: Allocate memory fail(%d)", __func__, i);
return -ENOMEM;
}
memcpy(save_content[i].content, temp, temp_len);
save_content[i].length = temp_len;
BTMTK_DBG_RAW(save_content[i].content, save_content[i].length, "%s", search);
}
}
return ret;
}
int btmtk_load_code_from_setting_files(char *setting_file_name,
struct device *dev, u32 *code_len, struct btmtk_dev *bdev)
{
int err = 0;
const struct firmware *fw_entry = NULL;
*code_len = 0;
if (bdev == NULL) {
BTMTK_ERR("%s: g_data is NULL!!", __func__);
err = -1;
goto end;
}
BTMTK_INFO("%s: begin setting_file_name = %s", __func__, setting_file_name);
err = request_firmware(&fw_entry, setting_file_name, dev);
if (err != 0 || fw_entry == NULL) {
BTMTK_INFO("%s: request_firmware fail, maybe file %s not exist, err = %d, fw_entry = %p",
__func__, setting_file_name, err, fw_entry);
if (fw_entry)
release_firmware(fw_entry);
err = -2;
goto end;
}
BTMTK_INFO("%s: setting file request_firmware size %zu success", __func__, fw_entry->size);
if (bdev->setting_file != NULL) {
kfree(bdev->setting_file);
bdev->setting_file = NULL;
}
bdev->setting_file = kzalloc(fw_entry->size + 1, GFP_KERNEL); /* alloc setting file memory */
if (bdev->setting_file == NULL) {
BTMTK_ERR("%s: kzalloc size %zu failed!!", __func__, fw_entry->size);
release_firmware(fw_entry);
err = -3;
goto end;
}
memcpy(bdev->setting_file, fw_entry->data, fw_entry->size);
bdev->setting_file[fw_entry->size] = '\0';
*code_len = fw_entry->size;
release_firmware(fw_entry);
BTMTK_INFO("%s: setting_file len (%d) assign done", __func__, *code_len);
end:
return err;
}
#if (USE_DEVICE_NODE == 0)
static bool btmtk_parse_bt_cfg_file(char *item_name,
char *text, u8 *searchcontent)
{
bool ret = true;
int temp_len = 0;
char search[SEARCH_LEN];
char *ptr = NULL, *p = NULL;
char *temp = text;
if (text == NULL) {
BTMTK_ERR("%s: text param is invalid!", __func__);
ret = false;
goto out;
}
memset(search, 0, SEARCH_LEN);
(void)snprintf(search, SEARCH_LEN, "%s", item_name); /* EX: SUPPORT_UNIFY_WOBLE */
p = ptr = strstr((char *)searchcontent, search);
if (!ptr) {
BTMTK_ERR("%s: Can't find %s\n", __func__, item_name);
ret = false;
goto out;
}
if (p > (char *)searchcontent) {
p--;
while ((*p == ' ') && (p != (char *)searchcontent))
p--;
if (*p == '#') {
BTMTK_ERR("%s: It's invalid bt cfg item\n", __func__);
ret = false;
goto out;
}
}
p = ptr + strlen(item_name) + 1;
ptr = p;
for (;;) {
switch (*p) {
case '\n':
goto textdone;
default:
*temp++ = *p++;
break;
}
}
textdone:
temp_len = p - ptr;
*temp = '\0';
out:
return ret;
}
static void btmtk_bt_cfg_item_value_to_bool(char *item_value, bool *value)
{
unsigned long text_value = 0;
if (item_value == NULL) {
BTMTK_ERR("%s: item_value is NULL!", __func__);
return;
}
if (kstrtoul(item_value, 10, &text_value) == 0) {
if (text_value == 1)
*value = true;
else
*value = false;
} else {
BTMTK_WARN("%s: kstrtoul failed!", __func__);
}
}
static void btmtk_load_bt_cfg_item(struct bt_cfg_struct *bt_cfg_content,
u8 *searchcontent, struct btmtk_dev *bdev)
{
bool ret = true;
char text[TEXT_LEN]; /* save for search text */
unsigned long text_value = 0;
memset(text, 0, TEXT_LEN);
ret = btmtk_parse_bt_cfg_file(BT_UNIFY_WOBLE, text, searchcontent);
if (ret) {
btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_unify_woble);
BTMTK_INFO("%s: bt_cfg_content->support_unify_woble = %d", __func__,
bt_cfg_content->support_unify_woble);
} else {
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_UNIFY_WOBLE);
}
ret = btmtk_parse_bt_cfg_file(BT_UNIFY_WOBLE_TYPE, text, searchcontent);
if (ret) {
if (kstrtoul(text, 10, &text_value) == 0)
bt_cfg_content->unify_woble_type = text_value;
else
BTMTK_WARN("%s: kstrtoul failed %s!", __func__, BT_UNIFY_WOBLE_TYPE);
} else {
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_UNIFY_WOBLE_TYPE);
}
BTMTK_INFO("%s: bt_cfg_content->unify_woble_type = %d", __func__,
bt_cfg_content->unify_woble_type);
ret = btmtk_parse_bt_cfg_file(BT_WOBLE_BY_EINT, text, searchcontent);
if (ret) {
btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_woble_by_eint);
BTMTK_INFO("%s: bt_cfg_content->support_woble_by_eint = %d", __func__,
bt_cfg_content->support_woble_by_eint);
} else {
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_WOBLE_BY_EINT);
}
ret = btmtk_parse_bt_cfg_file(BT_DONGLE_RESET_PIN, text, searchcontent);
if (ret) {
if (kstrtoul(text, 10, &text_value) == 0)
bt_cfg_content->dongle_reset_gpio_pin = text_value;
else
BTMTK_WARN("%s: kstrtoul failed %s!", __func__, BT_DONGLE_RESET_PIN);
} else {
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_DONGLE_RESET_PIN);
}
BTMTK_INFO("%s: bt_cfg_content->dongle_reset_gpio_pin = %d", __func__,
bt_cfg_content->dongle_reset_gpio_pin);
ret = btmtk_parse_bt_cfg_file(BT_RESET_DONGLE, text, searchcontent);
if (ret) {
btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_dongle_reset);
BTMTK_INFO("%s: bt_cfg_content->support_dongle_reset = %d", __func__,
bt_cfg_content->support_dongle_reset);
} else {
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_RESET_DONGLE);
}
ret = btmtk_parse_bt_cfg_file(BT_FULL_FW_DUMP, text, searchcontent);
if (ret) {
btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_full_fw_dump);
BTMTK_INFO("%s: bt_cfg_content->support_full_fw_dump = %d", __func__,
bt_cfg_content->support_full_fw_dump);
} else {
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_FULL_FW_DUMP);
}
ret = btmtk_parse_bt_cfg_file(BT_WOBLE_WAKELOCK, text, searchcontent);
if (ret) {
btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_woble_wakelock);
BTMTK_INFO("%s: bt_cfg_content->support_woble_wakelock = %d", __func__,
bt_cfg_content->support_woble_wakelock);
} else {
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_WOBLE_WAKELOCK);
}
ret = btmtk_parse_bt_cfg_file(BT_WOBLE_FOR_BT_DISABLE, text, searchcontent);
if (ret) {
btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_woble_for_bt_disable);
BTMTK_INFO("%s: bt_cfg_content->support_woble_for_bt_disable = %d", __func__,
bt_cfg_content->support_woble_for_bt_disable);
} else {
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_WOBLE_FOR_BT_DISABLE);
}
ret = btmtk_parse_bt_cfg_file(BT_RESET_STACK_AFTER_WOBLE, text, searchcontent);
if (ret) {
btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->reset_stack_after_woble);
BTMTK_INFO("%s: bt_cfg_content->reset_stack_after_woble = %d", __func__,
bt_cfg_content->reset_stack_after_woble);
} else {
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_RESET_STACK_AFTER_WOBLE);
}
ret = btmtk_parse_bt_cfg_file(BT_AUTO_PICUS, text, searchcontent);
if (ret) {
btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_auto_picus);
BTMTK_INFO("%s: bt_cfg_content->support_auto_picus = %d", __func__,
bt_cfg_content->support_auto_picus);
if (bt_cfg_content->support_auto_picus == true) {
ret = btmtk_load_fw_cfg_setting(BT_AUTO_PICUS_FILTER,
&bt_cfg_content->picus_filter, 1, searchcontent, FW_CFG_INX_LEN_NONE);
if (ret)
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_AUTO_PICUS_FILTER);
ret = btmtk_load_fw_cfg_setting(BT_AUTO_PICUS_ENABLE,
&bt_cfg_content->picus_enable, 1, searchcontent, FW_CFG_INX_LEN_NONE);
if (ret)
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_AUTO_PICUS_ENABLE);
}
} else {
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_AUTO_PICUS);
}
ret = btmtk_parse_bt_cfg_file(BT_PICUS_TO_HOST, text, searchcontent);
if (ret) {
btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_picus_to_host);
BTMTK_INFO("%s: bt_cfg_content->support_picus_to_host = %d", __func__,
bt_cfg_content->support_picus_to_host);
} else {
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_PICUS_TO_HOST);
}
ret = btmtk_parse_bt_cfg_file(BT_SINGLE_SKU, text, searchcontent);
if (ret) {
btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_bt_single_sku);
BTMTK_INFO("%s: bt_cfg_content->support_bt_single_sku = %d", __func__,
bt_cfg_content->support_bt_single_sku);
} else {
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_SINGLE_SKU);
}
ret = btmtk_parse_bt_cfg_file(BT_AUDIO_SET, text, searchcontent);
if (ret) {
btmtk_bt_cfg_item_value_to_bool(text, &bt_cfg_content->support_audio_setting);
BTMTK_INFO("%s: bt_cfg_content->support_audio_setting = %d", __func__,
bt_cfg_content->support_audio_setting);
if (bt_cfg_content->support_audio_setting == true) {
ret = btmtk_load_fw_cfg_setting(BT_AUDIO_ENABLE_CMD,
&bt_cfg_content->audio_cmd, 1, searchcontent, FW_CFG_INX_LEN_NONE);
if (ret)
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_AUDIO_ENABLE_CMD);
ret = btmtk_load_fw_cfg_setting(BT_AUDIO_PINMUX_NUM,
&bt_cfg_content->audio_pinmux_num, 1, searchcontent, FW_CFG_INX_LEN_NONE);
if (ret)
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_AUDIO_PINMUX_NUM);
ret = btmtk_load_fw_cfg_setting(BT_AUDIO_PINMUX_MODE,
&bt_cfg_content->audio_pinmux_mode, 1, searchcontent, FW_CFG_INX_LEN_NONE);
if (ret)
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_AUDIO_PINMUX_MODE);
}
} else {
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_AUDIO_SET);
bt_cfg_content->support_audio_setting = true; /* default to turn on, for others not update */
BTMTK_WARN("%s: %s default turn on %d!", __func__, BT_AUDIO_SET,
bt_cfg_content->support_audio_setting);
}
ret = btmtk_load_fw_cfg_setting(BT_PHASE1_WMT_CMD, bt_cfg_content->phase1_wmt_cmd,
PHASE1_WMT_CMD_COUNT, searchcontent, FW_CFG_INX_LEN_3);
if (ret)
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_PHASE1_WMT_CMD);
ret = btmtk_load_fw_cfg_setting(BT_VENDOR_CMD, bt_cfg_content->vendor_cmd,
VENDOR_CMD_COUNT, searchcontent, FW_CFG_INX_LEN_3);
if (ret)
BTMTK_WARN("%s: search item %s is invalid!", __func__, BT_VENDOR_CMD);
/* release setting file memory */
if (bdev) {
kfree(bdev->setting_file);
bdev->setting_file = NULL;
}
}
static void btmtk_load_bt_cfg(char *cfg_name, struct device *dev, struct btmtk_dev *bdev)
{
int ret = 0;
u32 code_len = 0;
ret = btmtk_load_code_from_setting_files(cfg_name, dev, &code_len, bdev);
if (ret != 0) {
BTMTK_ERR("btmtk_usb_load_code_from_setting_files %s failed!!", cfg_name);
if (ret != -2)
return;
if (btmtk_load_code_from_setting_files(BT_CFG_NAME, dev, &code_len, bdev) != 0) {
BTMTK_ERR("btmtk_usb_load_code_from_setting_files %s failed!!", BT_CFG_NAME);
return;
}
snprintf(bdev->bt_cfg_file_name, MAX_BIN_FILE_NAME_LEN, "%s", BT_CFG_NAME);
}
btmtk_load_bt_cfg_item(&bdev->bt_cfg, bdev->setting_file, bdev);
}
#endif // (USE_DEVICE_NODE == 0)
#if ENABLESTP
static int btmtk_send_set_stp_cmd(struct btmtk_dev *bdev)
{
u8 cmd[SET_STP_CMD_LEN] = { 0x01, 0x6F, 0xFC, 0x09, 0x01, 0x04, 0x05, 0x00, 0x03, 0x11, 0x0E, 0x00, 0x00};
u8 event[SET_STP_EVT_LEN] = { 0x04, 0xE4, 0x06, 0x02, 0x04, 0x02, 0x00, 0x00, 0x03};
int ret = 0;
ret = btmtk_main_send_cmd(bdev,
cmd, SET_STP_CMD_LEN,
event, SET_STP_EVT_LEN,
0, 0, BTMTK_TX_CMD_FROM_DRV);
BTMTK_INFO("%s done", __func__);
return ret;
}
static int btmtk_send_set_stp1_cmd(struct btmtk_dev *bdev)
{
u8 cmd[SET_STP1_CMD_LEN] = {0x01, 0x6F, 0xFC, 0x0C,
0x01, 0x08, 0x08, 0x00, 0x02, 0x01, 0x00, 0x01, 0x08, 0x00, 0x00, 0x80};
u8 event[SET_STP1_EVT_LEN] = {0x04, 0xE4, 0x10, 0x02, 0x08,
0x0C, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x80, 0x63, 0x76, 0x00, 0x00};
int ret = 0;
ret = btmtk_main_send_cmd(bdev,
cmd, SET_STP1_CMD_LEN,
event, SET_STP1_EVT_LEN, 0, 0,
BTMTK_TX_CMD_FROM_DRV);
BTMTK_INFO("%s done", __func__);
return ret;
}
#endif
int btmtk_cap_init(struct btmtk_dev *bdev)
{
int ret = 0;
BTMTK_DBG("%s start", __func__);
if (!bdev) {
BTMTK_ERR("%s, bdev is NULL!", __func__);
ret = -1;
goto exit;
}
#if (USE_DEVICE_NODE == 1)
bdev->chip_id = 0x6635;
#else
/* Todo read wifi fw version
* int wifi_fw_ver;
* btmtk_cif_write_register(bdev, 0x7C4001C4, 0x00008800);
* btmtk_cif_read_register(bdev, 0x7c4f0004, &wifi_fw_ver);
* BTMTK_ERR("wifi fw_ver = %04X", wifi_fw_ver);
*/
ret = main_info.hif_hook.reg_read(bdev, CHIP_ID, &bdev->chip_id);
if (ret < 0) {
BTMTK_ERR("read chip id failed");
ret = -EIO;
goto exit;
} else {
if (is_mt6639(bdev->chip_id) || is_mt7902(bdev->chip_id)
|| is_mt7922(bdev->chip_id) || is_mt7961(bdev->chip_id)) {
ret = main_info.hif_hook.reg_read(bdev, FLAVOR, &bdev->flavor);
if (ret < 0) {
BTMTK_ERR("read flavor id failed");
ret = -EIO;
goto exit;
}
ret = main_info.hif_hook.reg_read(bdev, FW_VERSION, &bdev->fw_version);
if (ret < 0) {
BTMTK_ERR("read fw version failed");
ret = -EIO;
goto exit;
}
} else {
BTMTK_ERR("Unknown Mediatek device(%04X)\n", bdev->chip_id);
ret = -EIO;
goto exit;
}
}
BTMTK_INFO("%s: Chip ID = 0x%x", __func__, bdev->chip_id);
BTMTK_INFO("%s: flavor = 0x%x", __func__, bdev->flavor);
BTMTK_INFO("%s: FW Ver = 0x%x", __func__, bdev->fw_version);
memset(bdev->rom_patch_bin_file_name, 0, MAX_BIN_FILE_NAME_LEN);
if ((bdev->fw_version & 0xff) == 0xff) {
BTMTK_ERR("%s: failed, wrong FW version : 0x%x !", __func__, bdev->fw_version);
ret = -1;
goto exit;
}
if (is_mt7961(bdev->chip_id)) {
if (bdev->flavor & DUAL_BT_FLAG)
bdev->dualBT = 1;
else
bdev->dualBT = 0;
} else {
bdev->dualBT = 0;
}
/* Bin filename format : "BT_RAM_CODE_MT%04x_%x_%x_hdr.bin"
* $$$$ : chip id
* % : fw version & 0xFF + 1 (in HEX)
*/
if (is_mt7902(bdev->chip_id)) {
/* 7902 can't use the same rule to recognize */
bdev->flavor = 0;
} else {
bdev->flavor = (bdev->flavor & 0x00000080) >> 7;
}
BTMTK_INFO("%s: flavor1 = 0x%x", __func__, bdev->flavor);
/* if flavor equals 1, it represent 7920, else it represent 7921 */
if (bdev->flavor) {
(void)snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN, "BT_RAM_CODE_MT%04x_1a_%x_hdr.bin",
bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1);
(void)snprintf(bdev->bt_cfg_file_name, MAX_BIN_FILE_NAME_LEN, "%s%x_1a_%x.%s", BT_CFG_NAME_PREFIX,
bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1, BT_CFG_NAME_SUFFIX);
} else {
(void)snprintf(bdev->rom_patch_bin_file_name, MAX_BIN_FILE_NAME_LEN, "BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1);
(void)snprintf(bdev->bt_cfg_file_name, MAX_BIN_FILE_NAME_LEN, "%s%x_1_%x.%s", BT_CFG_NAME_PREFIX,
bdev->chip_id & 0xffff, (bdev->fw_version & 0xff) + 1, BT_CFG_NAME_SUFFIX);
}
BTMTK_INFO("%s: rom patch file name is %s, bt_cfg_file_name is %s", __func__,
bdev->rom_patch_bin_file_name, bdev->bt_cfg_file_name);
memset(bdev->bdaddr, 0, BD_ADDRESS_SIZE);
#endif
exit:
return ret;
}
static int btmtk_send_vendor_cfg(struct btmtk_dev *bdev)
{
int ret = 0;
u16 index = 0;
uint8_t event[EVT_HDR_LEN] = { 0x04, 0x0E };
BTMTK_INFO("%s enter", __func__);
for (index = 0; index < VENDOR_CMD_COUNT; index++) {
if (bdev->bt_cfg.vendor_cmd[index].content &&
bdev->bt_cfg.vendor_cmd[index].length) {
ret = btmtk_main_send_cmd(bdev,
bdev->bt_cfg.vendor_cmd[index].content,
bdev->bt_cfg.vendor_cmd[index].length,
event, EVT_HDR_LEN,
0, 0, BTMTK_TX_PKT_FROM_HOST);
if (ret < 0) {
BTMTK_ERR("%s: Send vendor cmd failed(%d)! Index: %d",
__func__, ret, index);
goto exit;
}
BTMTK_INFO_RAW(bdev->bt_cfg.vendor_cmd[index].content,
bdev->bt_cfg.vendor_cmd[index].length, "send vendor cmd");
}
}
exit:
BTMTK_INFO("%s exit", __func__);
return ret;
}
static int btmtk_send_phase1_wmt_cfg(struct btmtk_dev *bdev)
{
int ret = 0;
u16 index = 0;
uint8_t event[EVT_HDR_LEN] = { 0x04, 0xE4 };
BTMTK_INFO("%s", __func__);
for (index = 0; index < PHASE1_WMT_CMD_COUNT; index++) {
if (bdev->bt_cfg.phase1_wmt_cmd[index].content &&
bdev->bt_cfg.phase1_wmt_cmd[index].length) {
ret = btmtk_main_send_cmd(bdev,
bdev->bt_cfg.phase1_wmt_cmd[index].content,
bdev->bt_cfg.phase1_wmt_cmd[index].length,
event, EVT_HDR_LEN,
DELAY_TIMES, RETRY_TIMES, BTMTK_TX_CMD_FROM_DRV);
if (ret < 0) {
BTMTK_ERR("%s: Send phase1 wmt cmd failed(%d)! Index: %d",
__func__, ret, index);
goto exit;
}
BTMTK_INFO_RAW(bdev->bt_cfg.phase1_wmt_cmd[index].content,
bdev->bt_cfg.phase1_wmt_cmd[index].length, "send wmt cmd");
}
}
exit:
BTMTK_INFO("%s exit", __func__);
return ret;
}
int btmtk_send_init_cmds(struct btmtk_dev *bdev)
{
int ret = -1;
struct btmtk_main_info *bmain_info = btmtk_get_main_info();
if (!bdev) {
BTMTK_ERR("%s: bdev is NULL !", __func__);
goto exit;
}
BTMTK_INFO("%s", __func__);
#if ENABLESTP
btmtk_send_set_stp_cmd(bdev);
btmtk_send_set_stp1_cmd(bdev);
#endif
ret = btmtk_calibration_flow(bdev);
if (ret < 0) {
BTMTK_ERR("%s, btmtk_calibration_flow failed!", __func__);
goto exit;
}
ret = btmtk_send_wmt_power_on_cmd(bdev);
if (ret < 0) {
if (bdev->power_state != BTMTK_DONGLE_STATE_POWER_ON) {
BTMTK_ERR("%s, btmtk_send_wmt_power_on_cmd failed!", __func__);
if (main_info.reset_stack_flag == HW_ERR_NONE)
main_info.reset_stack_flag = HW_ERR_CODE_POWER_ON;
}
goto exit;
}
ret = btmtk_send_phase1_wmt_cfg(bdev);
if (ret < 0) {
BTMTK_ERR("btmtk_send_wmt_cfg failed");
goto exit;
}
if (bdev->bt_cfg.support_auto_picus == true &&
(bdev->bt_cfg.support_picus_to_host == true || atomic_read(&bmain_info->fwlog_ref_cnt) != 0)) {
if (btmtk_picus_enable(bdev) < 0) {
BTMTK_ERR("send picus filter param failed");
ret = -1;
goto exit;
}
}
ret = btmtk_send_vendor_cfg(bdev);
if (ret < 0) {
BTMTK_ERR("btmtk_send_vendor_cfg failed");
goto exit;
}
exit:
return ret;
}
int btmtk_send_deinit_cmds(struct btmtk_dev *bdev)
{
int ret = -1;
struct btmtk_main_info *bmain_info = btmtk_get_main_info();
if (!bdev) {
BTMTK_ERR("%s: bdev is NULL !", __func__);
return ret;
}
BTMTK_INFO("%s", __func__);
if (bdev->bt_cfg.support_auto_picus == true &&
(bdev->bt_cfg.support_picus_to_host == true || atomic_read(&bmain_info->fwlog_ref_cnt) != 0)) {
if (btmtk_picus_disable(bdev) < 0) {
BTMTK_ERR("send picus filter param failed");
return -1;
}
}
ret = btmtk_send_wmt_power_off_cmd(bdev);
if (bdev->power_state != BTMTK_DONGLE_STATE_POWER_OFF) {
BTMTK_WARN("Power off failed, reset it");
if (main_info.reset_stack_flag == HW_ERR_NONE)
main_info.reset_stack_flag = HW_ERR_CODE_POWER_OFF;
}
return ret;
}
int btmtk_send_assert_cmd(struct btmtk_dev *bdev)
{
int ret = 0;
int state;
struct sk_buff *skb = NULL;
#if (USE_DEVICE_NODE == 0)
u8 cmd[ASSERT_CMD_LEN] = { 0x01, 0x6F, 0xFC, 0x05, 0x01, 0x02, 0x01, 0x00, 0x08 };
#else
u8 cmd[ASSERT_CMD_LEN] = { 0x01, 0x5B, 0xFD, 0x00 };
#endif
if (!bdev) {
BTMTK_ERR("%s, invalid parameters!", __func__);
ret = -EINVAL;
goto exit;
}
state = btmtk_get_chip_state(bdev);
if (state == BTMTK_STATE_FW_DUMP || state == BTMTK_STATE_SUSPEND ||
state == BTMTK_STATE_SEND_ASSERT || state == BTMTK_STATE_CLOSED) {
BTMTK_WARN("%s: FW dumping already or in suspend state don't send assert, state = %d!!!",
__func__, state);
return ret;
}
BTMTK_INFO("%s: send assert cmd", __func__);
skb = alloc_skb(ASSERT_CMD_LEN + BT_SKB_RESERVE, GFP_ATOMIC);
if (!skb) {
BTMTK_ERR("%s allocate skb failed!!", __func__);
goto exit;
}
bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
memcpy(skb->data, cmd, ASSERT_CMD_LEN);
skb->len = ASSERT_CMD_LEN;
#if (SLEEP_ENABLE == 0)
ret = main_info.hif_hook.send_cmd(bdev, skb, WMT_DELAY_TIMES, RETRY_TIMES, (int)BTMTK_TX_CMD_FROM_DRV);
#else
ret = main_info.hif_hook.send_cmd(bdev, skb, WMT_DELAY_TIMES, RETRY_TIMES, (int)BTMTK_TX_PKT_SEND_DIRECT);
#endif
if (ret < 0) {
BTMTK_ERR("%s failed!!", __func__);
kfree_skb(skb);
skb = NULL;
btmtk_reset_trigger(bdev);
} else {
btmtk_reset_timer_update(bdev);
BTMTK_INFO("%s: OK", __func__);
btmtk_set_chip_state(bdev, BTMTK_STATE_SEND_ASSERT);
}
exit:
return ret;
}
static int btmtk_send_txpower_cmd(struct btmtk_dev *bdev)
{
/**
* TCI Set TX Power Command
* 01 2C FC 0C QQ 00 00 00 XX YY ZZ GG AA BB CC DD
* QQ: EDR init TX power dbm // the value is equal to EDR MAX
* XX: BLE TX power dbm
* YY: EDR MAX TX power dbm
* ZZ: Enable LV9
* GG: 3db diff mode
* AA: [5:4] Indicator // [5] 1: command send to BT1, [4] 1: command send to BT0
* [3:0] Resolution // 0: 1dBm, 1: 0.5dBm, 2: 0.25dBm
* BB: BLE 2M
* CC: BLE S2
* DD: BLE S8
*/
u8 cmd[TXPOWER_CMD_LEN] = { 0x01, 0x2C, 0xFC, 0x0C,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
u8 event[TXPOWER_EVT_LEN] = { 0x04, 0x0E, 0x04, 0x01, 0x2C, 0xFC, 0x00 };
int ret = 0;
cmd[4] = (u8)main_info.PWS.EDR_Max;
cmd[8] = (u8)main_info.PWS.BLE_1M;
cmd[9] = (u8)main_info.PWS.EDR_Max;
cmd[10] = (u8)main_info.PWS.LV9;
cmd[11] = (u8)main_info.PWS.DM;
cmd[12] = (u8)main_info.PWS.IR;
cmd[13] = (u8)main_info.PWS.BLE_2M;
cmd[14] = (u8)main_info.PWS.BLE_LR_S2;
cmd[15] = (u8)main_info.PWS.BLE_LR_S8;
ret = btmtk_main_send_cmd(bdev,
cmd, TXPOWER_CMD_LEN,
event, TXPOWER_EVT_LEN,
DELAY_TIMES, RETRY_TIMES, BTMTK_TX_PKT_FROM_HOST);
if (ret < 0)
BTMTK_ERR("%s failed!!", __func__);
else
BTMTK_INFO("%s: OK", __func__);
return ret;
}
static int btmtk_set_power_value(char *str, int resolution, int is_edr)
{
int power = ERR_PWR, integer = 0, decimal = 0;
char *ptr = NULL;
if (resolution == RES_DOT_25) {
/* XX.YY => XX.YY/0.25 = XX*4 + YY/25 */
if (strstr(str, ".")) {
ptr = strsep(&str, ".");
if (ptr == NULL)
return -1;
if (kstrtoint(ptr, 0, &integer) != 0) {
BTMTK_ERR("Read integer Fail");
return -1;
}
if (kstrtoint(str, 0, &decimal) != 0) {
BTMTK_ERR("Read decimal Fail");
return -1;
}
if (decimal != 25 && decimal != 75 && decimal != 5 && decimal != 50)
return ERR_PWR;
if (decimal == 5)
decimal = 50;
if (integer >= 0)
power = integer * 4 + decimal / 25;
else
power = integer * 4 - decimal / 25;
} else {
if (kstrtoint(str, 0, &integer) != 0) {
BTMTK_ERR("Read integer Fail");
return -1;
}
power = integer * 4;
}
BTMTK_DBG("%s: power = %d", __func__, power);
if (is_edr) {
if (power > EDR_MAX_R2 || power < EDR_MIN_R2)
return ERR_PWR;
if (power >= EDR_MIN_LV9_R2)
main_info.PWS.LV9 = 1;
} else if (!is_edr && (power > BLE_MAX_R2 || power < BLE_MIN_R2))
return ERR_PWR;
} else if (resolution == RES_DOT_5) {
/* XX.YY => XX.YY/0.5 = XX*2 + YY/5 */
if (strstr(str, ".")) {
ptr = strsep(&str, ".");
if (ptr == NULL)
return -1;
if (kstrtoint(ptr, 0, &integer) != 0) {
BTMTK_ERR("Read integer Fail");
return -1;
}
if (kstrtoint(str, 0, &decimal) != 0) {
BTMTK_ERR("Read decimal Fail");
return -1;
}
if (decimal != 5)
return ERR_PWR;
if (integer >= 0)
power = integer * 2 + decimal / 5;
if (integer < 0)
power = integer * 2 - decimal / 5;
} else {
if (kstrtoint(str, 0, &integer) != 0) {
BTMTK_ERR("Read integer Fail");
return -1;
}
power = integer * 2;
}
BTMTK_DBG("%s: power = %d", __func__, power);
if (is_edr) {
if (power > EDR_MAX_R1 || power < EDR_MIN_R1)
return ERR_PWR;
if (power >= EDR_MIN_LV9_R1)
main_info.PWS.LV9 = 1;
} else if (!is_edr && (power > BLE_MAX_R1 || power < BLE_MIN_R1))
return ERR_PWR;
} else if (resolution == RES_1) {
if (kstrtoint(str, 0, &power) != 0) {
BTMTK_ERR("Read power Fail");
return -1;
}
BTMTK_DBG("%s: power = %d", __func__, power);
if (is_edr) {
if (power > EDR_MAX || power < EDR_MIN)
return ERR_PWR;
if (power >= EDR_MIN_LV9)
main_info.PWS.LV9 = 1;
} else if (!is_edr && (power > BLE_MAX || power < BLE_MIN))
return ERR_PWR;
}
return power;
}
static int btmtk_check_power_resolution(char *str)
{
if (str == NULL)
return -1;
if (strstr(str, ".25") || strstr(str, ".75"))
return RES_DOT_25;
if (strstr(str, ".5"))
return RES_DOT_5;
if (!strstr(str, ".") || strstr(str, ".0"))
return RES_1;
return -1;
}
static void btmtk_init_power_setting_struct(void)
{
main_info.PWS.BLE_1M = 0;
main_info.PWS.EDR_Max = 0;
main_info.PWS.LV9 = 0;
main_info.PWS.DM = 0;
main_info.PWS.IR = 0;
main_info.PWS.BLE_2M = 0;
main_info.PWS.BLE_LR_S2 = 0;
main_info.PWS.BLE_LR_S8 = 0;
}
static int btmtk_parse_power_table(char *context)
{
char *ptr = NULL;
int step = 0, temp;
int resolution;
int power;
if (context == NULL) {
BTMTK_ERR("%s context is NULL", __func__);
return -1;
}
BTMTK_INFO("%s", __func__);
btmtk_init_power_setting_struct();
/* Send to BT0? BT1? */
if (strstr(context, "BT0")) {
BTMTK_INFO("Parse power for BT0");
main_info.PWS.IR |= 0x10;
context += strlen("[BT0]");
} else if (strstr(context, "BT1")) {
BTMTK_INFO("Parse power for BT1");
main_info.PWS.IR |= 0x20;
context += strlen("[BT1]");
} else {
BTMTK_ERR("%s BT indicator error", __func__);
return -1;
}
resolution = btmtk_check_power_resolution(context);
if (resolution == -1) {
BTMTK_ERR("Check resolution fail");
return -1;
}
main_info.PWS.IR |= resolution;
BTMTK_INFO("%s: resolution = %d", __func__, resolution);
while ((ptr = strsep(&context, ",")) != NULL) {
while (*ptr == '\t' || *ptr == ' ')
ptr++;
switch (step) {
/* BR_EDR_PWR_MODE */
case CHECK_SINGLE_SKU_PWR_MODE:
if (kstrtoint(ptr, 0, &temp) == 0) {
if (temp == 0 || temp == 1) {
main_info.PWS.DM = temp;
step++;
continue;
} else {
BTMTK_ERR("PWR MODE value wrong");
return -1;
}
} else {
BTMTK_ERR("Read PWR MODE Fail");
return -1;
}
break;
/* Parse EDR MAX */
case CHECK_SINGLE_SKU_EDR_MAX:
power = btmtk_set_power_value(ptr, resolution, 1);
if (power == ERR_PWR) {
BTMTK_ERR("EDR MAX value wrong");
return -1;
}
main_info.PWS.EDR_Max = power;
step++;
break;
/* Parse BLE Default */
case CHECK_SINGLE_SKU_BLE:
power = btmtk_set_power_value(ptr, resolution, 0);
if (power == ERR_PWR) {
BTMTK_ERR("BLE value wrong");
return -1;
}
main_info.PWS.BLE_1M = power;
step++;
break;
/* Parse BLE 2M */
case CHECK_SINGLE_SKU_BLE_2M:
power = btmtk_set_power_value(ptr, resolution, 0);
if (power == ERR_PWR) {
BTMTK_ERR("BLE 2M value wrong");
return -1;
}
main_info.PWS.BLE_2M = power;
step++;
break;
/* Parse BLE long range S2 */
case CHECK_SINGLE_SKU_BLE_LR_S2:
power = btmtk_set_power_value(ptr, resolution, 0);
if (power == ERR_PWR) {
BTMTK_ERR("BLE LR S2 value wrong");
return -1;
}
main_info.PWS.BLE_LR_S2 = power;
step++;
break;
/* Parse BLE long range S8 */
case CHECK_SINGLE_SKU_BLE_LR_S8:
power = btmtk_set_power_value(ptr, resolution, 0);
if (power == ERR_PWR) {
BTMTK_ERR("BLE LR S8 value wrong");
return -1;
}
main_info.PWS.BLE_LR_S8 = power;
step++;
break;
default:
BTMTK_ERR("%s step is wrong: %d", __func__, step);
break;
}
continue;
}
return step;
}
static void btmtk_send_txpower_cmd_to_all_interface(void)
{
int i, ret;
struct btmtk_dev *bdev = NULL;
for (i = 0; i < btmtk_intf_num; i++) {
if (g_bdev[i]->hdev != NULL) {
bdev = g_bdev[i];
BTMTK_INFO("send to %d", i);
ret = btmtk_send_txpower_cmd(bdev);
if (ret < 0)
BTMTK_ERR("Device %d send txpower cmd fail", i);
}
}
}
static void btmtk_requset_country_cb(const struct firmware *fw, void *context)
{
char *ptr, *data, *p_data = NULL;
char *country = NULL;
int ret = 0;
bool find_country = false;
bool read_next = false;
if (fw == NULL) {
BTMTK_ERR("fw is NULL");
return;
}
BTMTK_INFO("%s request %s success", __func__, DEFAULT_COUNTRY_TABLE_NAME);
p_data = data = kzalloc(fw->size, GFP_KERNEL);
if (data == NULL) {
BTMTK_WARN("%s allocate memory fail (data)", __func__);
goto exit;
}
memcpy(data, fw->data, fw->size);
while ((ptr = strsep(&p_data, "\n")) != NULL) {
/* If the '#' in front of the line, ignore this line */
if (*ptr == '#')
continue;
/* Set power for BT1 */
if (read_next) {
if (strncmp(ptr, "[BT1]", 5) == 0) {
ret = btmtk_parse_power_table(ptr);
if (ret != CHECK_SINGLE_SKU_ALL) {
BTMTK_ERR("Parse power fail, ret = %d", ret);
break;
}
btmtk_send_txpower_cmd_to_all_interface();
} else {
BTMTK_INFO("No power data for BT1");
}
break;
}
if (find_country) {
ret = btmtk_parse_power_table(ptr);
/* Check if the next line has power value for BT1 */
read_next = true;
if (ret != CHECK_SINGLE_SKU_ALL) {
BTMTK_ERR("Parse power fail, ret = %d", ret);
continue;
}
btmtk_send_txpower_cmd_to_all_interface();
continue;
}
while ((country = strsep(&ptr, ",[]")) != NULL) {
if (strlen(country) != COUNTRY_CODE_LEN)
continue;
if (strcmp(country, main_info.PWS.country_code) == 0) {
find_country = true;
break;
}
}
}
kfree(data);
if (find_country == false)
BTMTK_ERR("Can't find country in the table");
exit:
release_firmware(fw);
}
static int btmtk_load_country_table(struct btmtk_dev *bdev)
{
int err = 0;
if (bdev->country_file_name)
err = request_firmware_nowait(THIS_MODULE, true,
bdev->country_file_name, NULL, GFP_KERNEL, NULL,
btmtk_requset_country_cb);
else
BTMTK_WARN("%s country_file_name is null", __func__);
return err;
}
void btmtk_set_country_code_from_wifi(char *code)
{
int i;
struct btmtk_dev *bdev = NULL;
if (!code)
return;
if (strlen(code) == COUNTRY_CODE_LEN) {
BTMTK_INFO("%s country code is %s", __func__, code);
memcpy(main_info.PWS.country_code, code, sizeof(main_info.PWS.country_code));
for (i = 0; i < btmtk_intf_num; i++) {
if (g_bdev[i]->hdev != NULL) {
bdev = g_bdev[i];
if (bdev->bt_cfg.support_bt_single_sku) {
btmtk_load_country_table(bdev);
break;
}
}
}
} else {
BTMTK_INFO("%s country code is not valid", __func__);
}
}
/* Pin-Hao: remove export symbol to build 2 same BT driver for SLT */
#if (USE_DEVICE_NODE == 0)
EXPORT_SYMBOL_GPL(btmtk_set_country_code_from_wifi);
#endif
/**
* Kernel HCI Interface Registeration
*/
static int bt_flush(struct hci_dev *hdev)
{
struct btmtk_dev *bdev = hci_get_drvdata(hdev);
if (is_mt66xx(bdev->chip_id))
return main_info.hif_hook.flush(bdev);
else
return 0;
}
static int bt_close(struct hci_dev *hdev)
{
int ret = -1;
int state = BTMTK_STATE_INIT;
unsigned char fstate = BTMTK_FOPS_STATE_INIT;
struct btmtk_dev *bdev = NULL;
if (!hdev) {
BTMTK_ERR("%s: invalid parameters!", __func__);
return ret;
}
bdev = hci_get_drvdata(hdev);
if (!bdev) {
BTMTK_ERR("%s: bdev is invalid!", __func__);
return ret;
}
fstate = btmtk_fops_get_state(bdev);
if (fstate != BTMTK_FOPS_STATE_OPENED) {
BTMTK_WARN("%s: fops is not allow close(%d)", __func__, fstate);
goto err;
}
btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_CLOSING);
state = btmtk_get_chip_state(bdev);
#if (USE_DEVICE_NODE == 1)
if (state == BTMTK_STATE_FW_DUMP || state == BTMTK_STATE_SEND_ASSERT
|| state == BTMTK_STATE_SUBSYS_RESET) {
BTMTK_WARN("%s: fw dump or assert ongoing , can't close yet state[%d]", __func__, state);
if (!wait_for_completion_timeout(&bdev->dump_comp, msecs_to_jiffies(WAIT_FW_DUMP_TIMEOUT)))
BTMTK_ERR("%s: uanble to finish coredump in 15s", __func__);
goto exit;
}
#endif
if (state != BTMTK_STATE_WORKING && state != BTMTK_STATE_STANDBY) {
/* If hif disconnect occurs,
* it will call cif_mutex_lock and release hci device.
* Release hci device will call bt_close.
* It must return with this,
* otherwise the below cif_mutex_lock will cause deadlock
*/
BTMTK_WARN("%s: not in working state and standby state(%d).", __func__, state);
goto exit;
}
if (main_info.hif_hook.cif_mutex_lock)
main_info.hif_hook.cif_mutex_lock(bdev);
state = btmtk_get_chip_state(bdev);
BTMTK_INFO("%s, enter, state[%d]", __func__, state);
if (state != BTMTK_STATE_WORKING && state != BTMTK_STATE_STANDBY) {
/* It's for the case that
* bt_close and hif_disconnect occur at the same time
*/
BTMTK_WARN("%s: not in working state and standby state(%d).", __func__, state);
goto unlock;
}
#if CFG_SUPPORT_DVT || CFG_SUPPORT_HW_DVT
/* Don't send init cmd for DVT
* Such as Lowpower DVT
*/
bdev->power_state = BTMTK_DONGLE_STATE_POWER_OFF;
BTMTK_INFO("%s, SKIP btmtk_send_deinit_cmds", __func__);
#else
if (state != BTMTK_STATE_STANDBY && main_info.reset_stack_flag != HW_ERR_CODE_CORE_DUMP
&& main_info.reset_stack_flag != HW_ERR_CODE_CHIP_RESET) {
ret = btmtk_send_deinit_cmds(bdev);
if (ret < 0) {
BTMTK_ERR("%s, btmtk_send_deinit_cmds failed", __func__);
goto unlock;
}
} else
BTMTK_WARN("%s, SKIP by state[%d], reset_stack_flag[%d]", __func__, state, main_info.reset_stack_flag);
#endif /* CFG_SUPPORT_DVT || CFG_SUPPORT_HW_DVT */
/* Flush RX works */
flush_work(&bdev->rx_work);
flush_work(&bdev->dynamic_fwdl_work);
/* Drop queues */
skb_queue_purge(&bdev->rx_q);
#if (USE_DEVICE_NODE == 0)
main_info.hif_hook.close(hdev);
#endif
unlock:
if (main_info.hif_hook.cif_mutex_unlock)
main_info.hif_hook.cif_mutex_unlock(bdev);
exit:
btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_CLOSED);
#if (USE_DEVICE_NODE == 1)
/* avoid reset start at new bt on */
btmtk_reset_timer_del(bdev);
main_info.hif_hook.close(hdev);
state = btmtk_get_chip_state(bdev);
/* after fops set closed, would not get any rx data */
/* so if chip_state still fw_dump, need to release wakelock and coredump end*/
if (state == BTMTK_STATE_FW_DUMP) {
BTMTK_ERR("%s: end with chip_state still dumping", __func__);
btmtk_fwdump_wake_unlock();
connv3_coredump_end(main_info.hif_hook.coredump_handler, "BT coredump fail");
}
if (state != BTMTK_STATE_DISCONNECT)
btmtk_set_chip_state(bdev, BTMTK_STATE_CLOSED);
#endif
err:
main_info.reset_stack_flag = HW_ERR_NONE;
bdev->get_hci_reset = 0;
BTMTK_INFO("%s: state[%d], reset_stack_flag[%d]", __func__, state, main_info.reset_stack_flag);
return 0;
}
int bt_open(struct hci_dev *hdev)
{
int ret = -1;
int state = BTMTK_STATE_INIT;
unsigned char fstate = BTMTK_FOPS_STATE_INIT;
struct btmtk_dev *bdev = NULL;
void (*rlm_get_alpha2)(char *);
const char *wifi_func_name = "rlm_get_alpha2";
char alpha2[5];
BTMTK_INFO("%s: MTK BT Driver Version : %s", __func__, VERSION);
DUMP_TIME_STAMP("open_start");
if (!hdev) {
BTMTK_ERR("%s: invalid parameters!", __func__);
return -EFAULT;
}
bdev = hci_get_drvdata(hdev);
if (!bdev) {
BTMTK_ERR("%s: bdev is invalid", __func__);
return -EFAULT;
}
fstate = btmtk_fops_get_state(bdev);
if (fstate == BTMTK_FOPS_STATE_OPENED) {
BTMTK_WARN("%s: fops opened!", __func__);
return -EIO;
}
if ((fstate == BTMTK_FOPS_STATE_CLOSING) ||
(fstate == BTMTK_FOPS_STATE_OPENING)) {
BTMTK_WARN("%s: fops open/close is on-going !", __func__);
return -EAGAIN;
}
state = btmtk_get_chip_state(bdev);
if (state == BTMTK_STATE_INIT || state == BTMTK_STATE_DISCONNECT) {
BTMTK_WARN("%s: chip_state[%d] is init or disconnect!", __func__, state);
return -EAGAIN;
}
btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_OPENING);
if (main_info.hif_hook.pre_open) {
ret = main_info.hif_hook.pre_open(bdev);
if (ret < 0) {
BTMTK_ERR("%s: pre_open fail", __func__);
goto failed;
}
}
state = btmtk_get_chip_state(bdev);
if (state != BTMTK_STATE_WORKING && state != BTMTK_STATE_STANDBY) {
BTMTK_WARN("%s: not in working state and standby state(%d).", __func__, state);
ret = -ENODEV;
goto failed;
}
BTMTK_INFO("%s state[%d], fstate[%d]", __func__, state, fstate);
ret = main_info.hif_hook.open(hdev);
if (ret < 0) {
BTMTK_ERR("%s, cif_open failed", __func__);
goto failed;
}
#if CFG_SUPPORT_DVT || CFG_SUPPORT_HW_DVT
/* Don't send init cmd for DVT
* Such as Lowpower DVT
*/
bdev->power_state = BTMTK_DONGLE_STATE_POWER_ON;
BTMTK_INFO("%s, SKIP btmtk_send_init_cmds", __func__);
#else
ret = btmtk_send_init_cmds(bdev);
if (ret < 0) {
BTMTK_ERR("%s, btmtk_send_init_cmds failed", __func__);
goto failed;
}
//ret = btmtk_send_apcf_reserved(bdev);
if (ret < 0) {
BTMTK_ERR("%s, btmtk_send_apcf_reserved failed", __func__);
goto failed;
}
#endif /* CFG_SUPPORT_DVT || CFG_SUPPORT_HW_DVT */
if (main_info.hif_hook.open_done)
main_info.hif_hook.open_done(bdev);
btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_OPENED);
main_info.reset_stack_flag = HW_ERR_NONE;
if (bdev->bt_cfg.support_bt_single_sku) {
rlm_get_alpha2 = (void *)btmtk_kallsyms_lookup_name(wifi_func_name);
if (rlm_get_alpha2) {
rlm_get_alpha2(alpha2);
if (strlen(alpha2) == COUNTRY_CODE_LEN) {
BTMTK_INFO("Wifi set country code %s", alpha2);
memcpy(main_info.PWS.country_code, alpha2, sizeof(main_info.PWS.country_code));
} else {
BTMTK_ERR("Country code length is wrong");
}
} else {
BTMTK_INFO("Wifi didn't set country code");
}
main_info.PWS.country_code[COUNTRY_CODE_LEN] = '\0';
if (strcmp(main_info.PWS.country_code, "") != 0)
btmtk_load_country_table(bdev);
}
DUMP_TIME_STAMP("open_end");
return 0;
failed:
#if (USE_DEVICE_NODE == 1)
main_info.hif_hook.close(hdev);
state = btmtk_get_chip_state(bdev);
/* if state is disconnect means uart_launcher is disconnected, not set to close state */
if (state != BTMTK_STATE_DISCONNECT)
btmtk_set_chip_state(bdev, BTMTK_STATE_CLOSED);
#endif
btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_CLOSED);
return ret;
}
static int bt_setup(struct hci_dev *hdev)
{
int ret = 0;
struct btmtk_dev *bdev = hci_get_drvdata(hdev);
BTMTK_INFO("%s", __func__);
if (is_mt66xx(bdev->chip_id)) {
ret = main_info.hif_hook.open(hdev);
if (ret)
BTMTK_ERR("%s: fail", __func__);
return ret;
}
return 0;
}
static int bt_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
int ret = -1;
int state = BTMTK_STATE_INIT;
unsigned char fstate = BTMTK_FOPS_STATE_INIT;
/* parsing commands */
u8 fw_coredump_cmd[FW_COREDUMP_CMD_LEN] = { 0x01, 0x5B, 0xFD, 0x00 };
u8 fw_coredump_flag = 0;
u8 reset_cmd[HCI_RESET_CMD_LEN] = { 0x01, 0x03, 0x0C, 0x00 };
struct btmtk_dev *bdev = NULL;
unsigned char *skb_tmp = NULL;
if (hdev == NULL || skb == NULL) {
BTMTK_ERR("%s, invalid parameters!", __func__);
return -ENODEV;
}
bdev = hci_get_drvdata(hdev);
if (bdev == NULL) {
BTMTK_ERR("%s, bdev is invalid!", __func__);
return -ENODEV;
}
if (main_info.hif_hook.cif_mutex_lock)
main_info.hif_hook.cif_mutex_lock(bdev);
fstate = btmtk_fops_get_state(bdev);
if (fstate != BTMTK_FOPS_STATE_OPENED) {
BTMTK_WARN("%s: fops is not open yet(%d)!", __func__, fstate);
ret = -ENODEV;
goto exit;
}
state = btmtk_get_chip_state(bdev);
if (state != BTMTK_STATE_WORKING) {
BTMTK_WARN_LIMITTED("%s: chip state is not working state[%d]", __func__, state);
if (state == BTMTK_STATE_DISCONNECT)
ret = -ENODEV;
else
ret = -EAGAIN;
goto exit;
}
if (bdev->power_state == BTMTK_DONGLE_STATE_POWER_OFF) {
BTMTK_WARN("%s: dongle state already power off, do not write", __func__);
ret = -EFAULT;
goto exit;
}
if (main_info.reset_stack_flag) {
BTMTK_WARN("%s: reset_stack_flag (%d)!", __func__, main_info.reset_stack_flag);
ret = -EFAULT;
goto exit;
}
if (!is_mt66xx(bdev->chip_id))
btmtk_dispatch_fwlog_bluetooth_kpi(bdev, skb->data, skb->len, hci_skb_pkt_type(skb));
skb_tmp = skb_push(skb, 1);
if (!skb_tmp) {
BTMTK_ERR("%s, skb_put failed!", __func__);
ret = -ENOMEM;
goto exit;
}
memcpy(skb_tmp, &hci_skb_pkt_type(skb), 1);
#if ENABLESTP
skb = mtk_add_stp(bdev, skb);
#endif
if (!is_mt66xx(bdev->chip_id)) {
/* For Ble ISO packet size */
if (memcmp(skb->data, main_info.read_iso_packet_size_cmd,
READ_ISO_PACKET_SIZE_CMD_HDR_LEN) == 0) {
bdev->iso_threshold = skb->data[READ_ISO_PACKET_SIZE_CMD_HDR_LEN] +
(skb->data[READ_ISO_PACKET_SIZE_CMD_HDR_LEN + 1] << 8);
BTMTK_INFO("%s: Ble iso pkt size is %d", __func__, bdev->iso_threshold);
}
if (hci_skb_pkt_type(skb) == HCI_COMMAND_PKT) {
#if (USE_DEVICE_NODE == 0)
if (bdev->get_hci_reset == 1) {
ret = btmtk_set_audio_setting(bdev);
bdev->get_hci_reset = 0;
if (ret < 0) {
BTMTK_ERR("%s btmtk_set_audio_setting failed!!", __func__);
goto exit;
}
}
#endif
/* save hci cmd pkt for debug */
btmtk_hci_snoop_save(HCI_SNOOP_TYPE_CMD_STACK, skb->data, skb->len);
if (skb->len == FW_COREDUMP_CMD_LEN &&
!memcmp(skb->data, fw_coredump_cmd, FW_COREDUMP_CMD_LEN)) {
BTMTK_INFO("%s: Dongle FW Assert Triggered by BT Stack!", __func__);
fw_coredump_flag = 1;
btmtk_reset_timer_update(bdev);
btmtk_hci_snoop_print_to_log();
} else if (skb->len == HCI_RESET_CMD_LEN &&
!memcmp(skb->data, reset_cmd, HCI_RESET_CMD_LEN))
BTMTK_INFO("%s: got command: 0x03 0C 00 (HCI_RESET)", __func__);
} else if (hci_skb_pkt_type(skb) == HCI_ACLDATA_PKT) {
btmtk_hci_snoop_save(HCI_SNOOP_TYPE_TX_ACL_STACK, skb->data, skb->len);
} else if (hci_skb_pkt_type(skb) == HCI_ISO_PKT) {
btmtk_hci_snoop_save(HCI_SNOOP_TYPE_TX_ISO_STACK, skb->data, skb->len);
}
BTMTK_DBG_RAW(skb->data, skb->len, "%s, send, len = %d ", __func__, skb->len);
ret = main_info.hif_hook.send_cmd(bdev, skb, 0, 0, (int)BTMTK_TX_PKT_FROM_HOST);
if (ret < 0) {
BTMTK_ERR("%s failed!!", __func__);
goto exit;
}
} else {
ret = main_info.hif_hook.send_cmd(bdev, skb, 0, 5, (int)BTMTK_TX_PKT_FROM_HOST);
if (ret < 0) {
BTMTK_ERR("%s failed!!", __func__);
goto exit;
}
}
exit:
if (main_info.hif_hook.cif_mutex_unlock)
main_info.hif_hook.cif_mutex_unlock(bdev);
if (ret >= 0 && fw_coredump_flag == 1)
btmtk_set_chip_state(bdev, BTMTK_STATE_SEND_ASSERT);
return ret;
}
void btmtk_reg_hif_hook(struct hif_hook_ptr *hook)
{
memcpy(&main_info.hif_hook, hook, sizeof(struct hif_hook_ptr));
}
static void btmtk_dynamic_fwdl_work(struct work_struct *work)
{
struct btmtk_dev *bdev = container_of(work, struct btmtk_dev, dynamic_fwdl_work);
BTMTK_INFO("%s enter", __func__);
btmtk_dynamic_load_rom_patch(bdev, bdev->fw_bin_info);
}
static void btmtk_rx_work(struct work_struct *work)
{
int err = 0, skip_pkt = 0;
struct btmtk_dev *bdev = container_of(work, struct btmtk_dev, rx_work);
struct sk_buff *skb;
unsigned char fstate = BTMTK_FOPS_STATE_INIT;
int state = 0;
while ((skb = skb_dequeue(&bdev->rx_q))) {
/* BTMTK_DBG_RAW(skb->data, skb->len, "%s, recv evt", __func__); */
skip_pkt = btmtk_dispatch_fwlog(bdev, skb);
if (skip_pkt != 0) {
/* kfree_skb should be moved to btmtk_dispach_pkt */
kfree_skb(skb);
continue;
}
BTMTK_INFO_RAW(skb->data, skb->len, "%s: len[%d] %02x", __func__,
skb->len, hci_skb_pkt_type(skb));
if (hci_skb_pkt_type(skb) == HCI_EVENT_PKT) {
/* save hci evt pkt for debug */
if (skb->data[0] == 0x3E)
btmtk_hci_snoop_save(HCI_SNOOP_TYPE_ADV_EVT_STACK, skb->data, skb->len);
else if (skb->data[0] == 0x13)
btmtk_hci_snoop_save(HCI_SNOOP_TYPE_NOCP_EVT_STACK, skb->data, skb->len);
else
btmtk_hci_snoop_save(HCI_SNOOP_TYPE_EVT_STACK, skb->data, skb->len);
/* dynamic download for connac3 */
if (skb->data[0] == 0x0E && skb->data[1] == 0x04 &&
skb->data[2] == 0x01 && skb->data[3] == 0x01 &&
skb->data[4] == 0xFE) {
bdev->fw_bin_info = skb->data[5];
/*
* Create a thread to do dynamic fwdl
* dynamic fwdl will block thread to wait for specific event,
* blocking rx_work thread means waited event won't be handled in
* rx_work thread, so here we create a new thread for dynamic fwdl.
*/
schedule_work(&bdev->dynamic_fwdl_work);
BTMTK_DBG_RAW(skb->data, skb->len, "%s: Get dynamic DL EVENT- ", __func__);
/* Drop by driver, don't send to stack */
kfree_skb(skb);
continue;
}
if (main_info.hif_hook.event_filter(bdev, skb)) {
BTMTK_DBG("%s Drop by driver, don't send to stack", __func__);
/* Drop by driver, don't send to stack */
kfree_skb(skb);
continue;
}
} else if (hci_skb_pkt_type(skb) == HCI_ACLDATA_PKT) {
/* save hci acl pkt for debug, not include picus log and coredump*/
if (!(skb->data[0] == 0xFF && skb->data[1] == 0xF0))
btmtk_hci_snoop_save(HCI_SNOOP_TYPE_RX_ACL_STACK, skb->data, skb->len);
} else if (hci_skb_pkt_type(skb) == HCI_ISO_PKT) {
btmtk_hci_snoop_save(HCI_SNOOP_TYPE_RX_ISO_STACK, skb->data, skb->len);
#if (USE_DEVICE_NODE == 1)
} else if (hci_skb_pkt_type(skb) == RHW_WRITE_TYPE
|| hci_skb_pkt_type(skb) == RHW_READ_TYPE) {
main_info.hif_hook.event_filter(bdev, skb);
BTMTK_DBG("%s Drop by driver, don't send to stack", __func__);
/* Drop by driver, don't send to stack */
kfree_skb(skb);
continue;
#endif
}
fstate = btmtk_fops_get_state(bdev);
if (fstate != BTMTK_FOPS_STATE_OPENED) {
/* BT close case, drop by driver, don't send to stack */
kfree_skb(skb);
continue;
}
if (is_mt66xx(bdev->chip_id)) {
err = btmtk_cif_rx_packet_handler(bdev->hdev, skb);
} else {
/* for bluetooth kpi */
btmtk_dispatch_fwlog_bluetooth_kpi(bdev, skb->data, skb->len, hci_skb_pkt_type(skb));
/* If reset stack enabled,
* driver should discard the frames
* when is in suspend/resume state
*/
state = btmtk_get_chip_state(bdev);
if (bdev->bt_cfg.reset_stack_after_woble &&
(state == BTMTK_STATE_SUSPEND || state == BTMTK_STATE_RESUME)) {
kfree_skb(skb);
continue;
}
err = hci_recv_frame(bdev->hdev, skb);
}
if (err < 0) {
if (err != -ENXIO)
BTMTK_ERR("%s btmtk_rx_work failed, err = %d", __func__, err);
return;
}
}
}
void btmtk_free_hci_device(struct btmtk_dev *bdev, int hci_bus_type)
{
unsigned char fstate = BTMTK_FOPS_STATE_INIT;
if (!bdev)
return;
BTMTK_INFO("%s Begin", __func__);
/* Flush RX works */
flush_work(&bdev->rx_work);
if (skb_queue_len(&bdev->rx_q) != 0) {
/* Drop queues */
skb_queue_purge(&bdev->rx_q);
}
if (bdev->workqueue) {
destroy_workqueue(bdev->workqueue);
bdev->workqueue = NULL;
}
if (bdev->hdev) {
hci_free_dev(bdev->hdev);
bdev->hdev = NULL;
}
fstate = btmtk_fops_get_state(bdev);
if (fstate == BTMTK_FOPS_STATE_OPENED || fstate == BTMTK_FOPS_STATE_CLOSING) {
BTMTK_WARN("%s: fstate = %d , set reset_stack_flag", __func__, fstate);
if (main_info.reset_stack_flag == HW_ERR_NONE)
main_info.reset_stack_flag = HW_ERR_CODE_USB_DISC;
}
bdev->get_hci_reset = 0;
BTMTK_INFO("%s End", __func__);
}
int btmtk_allocate_hci_device(struct btmtk_dev *bdev, int hci_bus_type)
{
struct hci_dev *hdev;
int err = 0;
if (!bdev) {
BTMTK_ERR("%s, bdev is NULL!", __func__);
err = -EINVAL;
goto end;
}
BTMTK_INFO("%s", __func__);
/* Add hci device */
hdev = hci_alloc_dev();
if (!hdev) {
BTMTK_ERR("%s, hdev is NULL!", __func__);
err = -ENOMEM;
goto end;
}
hdev->bus = hci_bus_type;
hci_set_drvdata(hdev, bdev);
/* HCI_PRIMARY = 0x00 */
hdev->dev_type = 0x00;
bdev->hdev = hdev;
/* register hci callback */
hdev->open = bt_open;
hdev->close = bt_close;
hdev->flush = bt_flush;
hdev->send = bt_send_frame;
hdev->setup = bt_setup;
init_waitqueue_head(&bdev->p_wait_event_q);
/* rx_work init */
INIT_WORK(&bdev->rx_work, btmtk_rx_work);
INIT_WORK(&bdev->dynamic_fwdl_work, btmtk_dynamic_fwdl_work);
#if (USE_DEVICE_NODE == 1)
INIT_WORK(&bdev->async_trx_work, btmtk_async_trx_work);
#endif
skb_queue_head_init(&bdev->rx_q);
bdev->workqueue = alloc_workqueue("BTMTK_RX_WQ", WQ_HIGHPRI | WQ_UNBOUND |
WQ_MEM_RECLAIM, 1);
if (!bdev->workqueue) {
BTMTK_ERR("%s, bdev->workqueue is NULL!", __func__);
err = -ENOMEM;
goto err0;
}
bdev->get_hci_reset = 0;
BTMTK_INFO("%s done", __func__);
return 0;
err0:
hci_free_dev(hdev);
hdev = NULL;
end:
return err;
}
int btmtk_register_hci_device(struct btmtk_dev *bdev)
{
struct hci_dev *hdev;
int err = 0;
int ret = 0;
hdev = bdev->hdev;
#if (KERNEL_VERSION(5, 10, 20) < LINUX_VERSION_CODE)
set_bit(HCI_QUIRK_NO_SUSPEND_NOTIFIER, &hdev->quirks);
#endif
err = hci_register_dev(hdev);
/* After hci_register_dev completed
* It will set dev_flags to HCI_SETUP
* That cause vendor_lib create socket failed
*/
if (err < 0) {
BTMTK_INFO("%s can't register", __func__);
goto exit;
}
#if CFG_SUPPORT_BLUEZ
#else
/* why need to clear flag HCI_SETUP? */
/* reason: if don't clearflag HCI_SETUP, bluedroid do open will return at
* the case HCI_CHANNEL_USER of hci_sock_bind API, because hci_dev_test_flag(hdev, HCI_SETUP)
* is true, it will goto done, just skip the hci_dev_open
*/
#if (KERNEL_VERSION(4, 4, 0) > LINUX_VERSION_CODE)
ret = test_and_clear_bit(HCI_SETUP, &hdev->dev_flags);
#else
ret = hci_dev_test_and_clear_flag(hdev, HCI_SETUP);
#endif
BTMTK_INFO("%s, the bit value returned is %d", __func__, ret);
#endif /* CFG_SUPPORT_BLUEZ */
exit:
return err;
}
int btmtk_deregister_hci_device(struct btmtk_dev *bdev)
{
int err = 0;
/* when not do hci_register_dev action, we do hci_unregister_dev will crash,
* so we add test_flag to decide whether hci_register_dev has been
* successful or failed, if hci_register_dev success, it will set flag to
* HCI_BREDR_ENABLED, After this flag has been set to HCI_BREDR_ENABLED, we
* can be able to do hci_unregister_dev.
*/
#if (KERNEL_VERSION(4, 1, 0) > LINUX_VERSION_CODE)
if (bdev && bdev->hdev && test_bit(HCI_BREDR_ENABLED, &bdev->hdev->dev_flags)) {
#else
if (bdev && bdev->hdev && hci_dev_test_flag(bdev->hdev, HCI_BREDR_ENABLED)) {
#endif
hci_unregister_dev(bdev->hdev);
BTMTK_INFO("%s end", __func__);
}
return err;
}
static int btmtk_main_allocate_memory(struct btmtk_dev *bdev)
{
int err = -1;
BTMTK_INFO("%s Begin", __func__);
if (bdev->rom_patch_bin_file_name == NULL) {
bdev->rom_patch_bin_file_name = kzalloc(MAX_BIN_FILE_NAME_LEN, GFP_KERNEL);
if (!bdev->rom_patch_bin_file_name) {
BTMTK_ERR("%s: alloc memory fail (bdev->rom_patch_bin_file_name)", __func__);
goto end;
}
}
if (bdev->io_buf == NULL) {
bdev->io_buf = kzalloc(IO_BUF_SIZE, GFP_KERNEL);
if (!bdev->io_buf) {
BTMTK_ERR("%s: alloc memory fail (bdev->io_buf)", __func__);
goto err2;
}
}
if (bdev->bt_cfg_file_name == NULL) {
bdev->bt_cfg_file_name = kzalloc(MAX_BIN_FILE_NAME_LEN, GFP_KERNEL);
if (!bdev->bt_cfg_file_name) {
BTMTK_ERR("%s: alloc memory fail (bdev->bt_cfg_file_name)", __func__);
goto err1;
}
}
if (bdev->country_file_name == NULL) {
bdev->country_file_name = kzalloc(MAX_BIN_FILE_NAME_LEN, GFP_KERNEL);
if (!bdev->country_file_name) {
BTMTK_ERR("%s: alloc memory fail (bdev->country_file_name)", __func__);
goto err0;
}
}
BTMTK_INFO("%s Done", __func__);
return 0;
err0:
kfree(bdev->bt_cfg_file_name);
bdev->bt_cfg_file_name = NULL;
err1:
kfree(bdev->io_buf);
bdev->io_buf = NULL;
err2:
kfree(bdev->rom_patch_bin_file_name);
bdev->rom_patch_bin_file_name = NULL;
end:
return err;
}
static void btmtk_main_free_memory(struct btmtk_dev *bdev)
{
kfree(bdev->rom_patch_bin_file_name);
bdev->rom_patch_bin_file_name = NULL;
kfree(bdev->bt_cfg_file_name);
bdev->bt_cfg_file_name = NULL;
kfree(bdev->country_file_name);
bdev->country_file_name = NULL;
kfree(bdev->io_buf);
bdev->io_buf = NULL;
BTMTK_INFO("%s: Success", __func__);
}
int btmtk_main_cif_initialize(struct btmtk_dev *bdev, int hci_bus)
{
int err = 0;
btmtk_init_node();
btmtk_reset_timer_add(bdev);
err = btmtk_main_allocate_memory(bdev);
if (err < 0) {
BTMTK_ERR("btmtk_main_allocate_memory failed!");
goto end;
}
btmtk_initialize_cfg_items(bdev);
err = btmtk_allocate_hci_device(bdev, hci_bus);
if (err < 0) {
BTMTK_ERR("btmtk_allocate_hci_device failed!");
goto free_mem;
}
err = btmtk_cap_init(bdev);
if (err < 0) {
if (err == -EIO) {
BTMTK_ERR("btmtk_cap_init failed, do chip reset!");
goto end;
} else {
BTMTK_ERR("btmtk_cap_init failed!");
goto free_hci_dev;
}
}
#if (USE_DEVICE_NODE == 0)
btmtk_load_bt_cfg(bdev->bt_cfg_file_name, bdev->intf_dev, bdev);
(void)snprintf(bdev->country_file_name, MAX_BIN_FILE_NAME_LEN,
DEFAULT_COUNTRY_TABLE_NAME);
#endif
#ifdef BTMTK_DEBUG_SOP
#ifdef DEFAULT_DEBUG_SOP_NAME
/* debug sop */
snprintf(bdev->debug_sop_file_name, MAX_BIN_FILE_NAME_LEN,
"%s_%x.bin", DEFAULT_DEBUG_SOP_NAME, bdev->chip_id & 0xffff);
BTMTK_INFO("%s: debug sop file name is %s", __func__,
bdev->debug_sop_file_name);
#if (USE_DEVICE_NODE == 0)
btmtk_load_debug_sop_register(bdev->debug_sop_file_name, bdev->intf_dev, bdev);
#endif
#endif
#endif
return 0;
free_hci_dev:
btmtk_free_hci_device(bdev, hci_bus);
free_mem:
btmtk_main_free_memory(bdev);
end:
return err;
}
void btmtk_main_cif_uninitialize(struct btmtk_dev *bdev, int hci_bus)
{
BTMTK_DBG("%s start", __func__);
btmtk_free_setting_file(bdev);
btmtk_free_hci_device(bdev, hci_bus);
btmtk_main_free_memory(bdev);
btmtk_reset_timer_del(bdev);
#ifdef BTMTK_DEBUG_SOP
btmtk_clean_debug_reg_file(bdev);
#endif
}
int btmtk_main_cif_disconnect_notify(struct btmtk_dev *bdev, int hci_bus)
{
/* need to rewirte when add txqueue, because usb need to add more clear action
* when do whole chip reset, usb need to do clear action in usb_close when disconnect,
* because usb_close will not execute when do chip reset
*/
BTMTK_DBG("%s: start", __func__);
cancel_work_sync(&bdev->rx_work);
cancel_work_sync(&bdev->dynamic_fwdl_work);
#if (USE_DEVICE_NODE == 0)
btmtk_deregister_hci_device(bdev);
#endif
btmtk_main_cif_uninitialize(bdev, hci_bus);
#if (USE_DEVICE_NODE == 1)
if (main_info.hif_hook.coredump_handler) {
BTMTK_INFO("%s: deinit coredump handle", __func__);
connv3_coredump_deinit(main_info.hif_hook.coredump_handler);
}
#endif
bdev->power_state = BTMTK_DONGLE_STATE_POWER_OFF;
/* btmtk_release_dev(bdev); */
return 0;
}
static int btmtk_reboot_notify(struct notifier_block *nb,
unsigned long event, void *unused)
{
int ret = 0;
int i = 0;
int cif_event = 0;
unsigned char fstate = 0;
int state = 0;
struct btmtk_cif_state *cif_state = NULL;
struct btmtk_dev *bdev = NULL;
BTMTK_INFO("%s: btmtk_reboot_notify(%d)", __func__, (int)event);
if (event == SYS_POWER_OFF && main_info.hif_hook.enter_standby != NULL) {
BTMTK_DBG("%s: set woble for standby", __func__);
main_info.hif_hook.enter_standby();
}
if (event == SYS_RESTART) {
BTMTK_INFO("%s: enter", __func__);
for (i = 0; i < btmtk_intf_num; i++) {
/* Find valid dev for already probe interface. */
if (g_bdev[i]->hdev != NULL) {
bdev = g_bdev[i];
fstate = btmtk_fops_get_state(bdev);
if (fstate != BTMTK_FOPS_STATE_OPENED) {
BTMTK_WARN("%s: fops is not opened(%d)", __func__, fstate);
continue;
}
state = btmtk_get_chip_state(bdev);
if (state != BTMTK_STATE_WORKING) {
BTMTK_WARN("%s: not in working(%d).", __func__, state);
continue;
}
cif_event = HIF_EVENT_DISCONNECT;
if (BTMTK_CIF_IS_NULL(bdev, cif_event)) {
/* Error */
BTMTK_WARN("%s parameter is NULL", __func__);
continue;
}
cif_state = &bdev->cif_state[cif_event];
/* Set Entering state */
btmtk_set_chip_state((void *)bdev, cif_state->ops_enter);
btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_CLOSING);
if (main_info.hif_hook.cif_mutex_lock)
main_info.hif_hook.cif_mutex_lock(bdev);
ret = btmtk_send_deinit_cmds(bdev);
if (ret < 0)
BTMTK_ERR("%s, btmtk_send_deinit_cmds failed", __func__);
main_info.hif_hook.close(bdev->hdev);
if (main_info.hif_hook.cif_mutex_unlock)
main_info.hif_hook.cif_mutex_unlock(bdev);
btmtk_fops_set_state(bdev, BTMTK_FOPS_STATE_CLOSED);
/* Set End/Error state */
if (ret == 0)
btmtk_set_chip_state((void *)bdev, cif_state->ops_end);
else
btmtk_set_chip_state((void *)bdev, cif_state->ops_error);
}
}
}
return 0;
}
static struct notifier_block btmtk_reboot_notifier = {
.notifier_call = btmtk_reboot_notify,
.next = NULL,
.priority = 0,
};
static int main_init(void)
{
int i = 0;
BTMTK_INFO("%s", __func__);
/* Check if user changes default minimum supported intf count */
if (btmtk_intf_num < BT_MCU_MINIMUM_INTERFACE_NUM) {
btmtk_intf_num = BT_MCU_MINIMUM_INTERFACE_NUM;
BTMTK_WARN("%s minimum interface is %d", __func__, btmtk_intf_num);
}
BTMTK_INFO("%s supported intf count <%d>", __func__, btmtk_intf_num);
BTMTK_INFO("%s: Register reboot_notifier callback success.", __func__);
/* Is it necessary? bt_close will be called by reboot. */
register_reboot_notifier(&btmtk_reboot_notifier);
g_bdev = kzalloc((sizeof(*g_bdev) * btmtk_intf_num), GFP_KERNEL);
if (!g_bdev) {
BTMTK_WARN("%s insufficient memory", __func__);
return -ENOMEM;
}
for (i = 0; i < btmtk_intf_num; i++) {
g_bdev[i] = btmtk_allocate_dev_memory(NULL);
if (g_bdev[i]) {
/* BTMTK_STATE_UNKNOWN instead? */
/* btmtk_set_chip_state(g_bdev[i], BTMTK_STATE_INIT); */
/* BTMTK_FOPS_STATE_UNKNOWN instead? */
btmtk_fops_set_state(g_bdev[i], BTMTK_FOPS_STATE_INIT);
} else {
return -ENOMEM;
}
}
// Set global variable for btif interface
g_sbdev = g_bdev[0];
btmtk_main_info_initialize();
return 0;
}
static int main_exit(void)
{
int i = 0;
BTMTK_INFO("%s releasing intf count <%d>", __func__, btmtk_intf_num);
if (g_bdev == NULL) {
BTMTK_WARN("%s g_data is NULL", __func__);
return 0;
}
BTMTK_INFO("%s: Unregister reboot_notifier callback success.", __func__);
/* Is it necessary? bt_close will be called by reboot. */
unregister_reboot_notifier(&btmtk_reboot_notifier);
wakeup_source_unregister(main_info.fwdump_ws);
wakeup_source_unregister(main_info.woble_ws);
wakeup_source_unregister(main_info.eint_ws);
#if WAKEUP_BT_IRQ
wakeup_source_unregister(main_info.irq_ws);
#endif
for (i = 0; i < btmtk_intf_num; i++) {
if (g_bdev[i] != NULL)
btmtk_free_dev_memory(NULL, g_bdev[i]);
}
kfree(g_bdev);
return 0;
}
/**
* Kernel Module init/exit Functions
*/
int __init main_driver_init(void)
{
int ret = 0;
int i;
/* Mediatek Driver Version */
BTMTK_INFO("%s: MTK BT Driver Version : %s", __func__, VERSION);
ret = main_init();
if (ret < 0)
return ret;
for (i = 0; i < btmtk_intf_num; i++)
btmtk_set_chip_state(g_bdev[i], BTMTK_STATE_DISCONNECT);
ret = btmtk_cif_register();
if (ret < 0) {
BTMTK_ERR("*** STPBTFWLOG registration failed(%d)! ***", ret);
main_exit();
return ret;
}
if (main_info.hif_hook.init)
ret = main_info.hif_hook.init();
BTMTK_INFO("%s: Done", __func__);
return ret;
}
void __exit main_driver_exit(void)
{
BTMTK_INFO("%s", __func__);
btmtk_deinit_node();
if (main_info.hif_hook.exit)
main_info.hif_hook.exit();
btmtk_cif_deregister();
main_exit();
}
module_init(main_driver_init);
module_exit(main_driver_exit);
/**
* Module Common Information
*/
MODULE_DESCRIPTION("Mediatek Bluetooth Driver");
MODULE_VERSION(VERSION SUBVER);
MODULE_LICENSE("GPL");
module_param(btmtk_intf_num, int, 0444);