Squashed backup commit (no shallow-clone history issues) of xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream commit 9eece662e) plus all fixes applied to get everpal_defconfig building clean against a modern (GCC 15.2.0) toolchain -- see the previous commit message on the lineage-23.2 branch (also in this repo history is not available here since this is a squashed snapshot; full incremental history lives in the local DL380 checkout and the upstream GitHub remote). Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
412 lines
11 KiB
C
412 lines
11 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (c) 2019 MediaTek Inc.
|
|
*/
|
|
|
|
#include <linux/io.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_gpio.h>
|
|
#include "bt.h"
|
|
|
|
/*******************************************************************************
|
|
* C O N S T A N T S
|
|
********************************************************************************
|
|
*/
|
|
#define BT_DBG_PROCNAME "driver/bt_dbg"
|
|
#define BUF_LEN_MAX 384
|
|
#define BT_DBG_DUMP_BUF_SIZE 1024
|
|
#define BT_DBG_PASSWD "4w2T8M65K5?2af+a "
|
|
#define BT_DBG_USER_TRX_PREFIX "[user-trx] "
|
|
|
|
/*******************************************************************************
|
|
* D A T A T Y P E S
|
|
********************************************************************************
|
|
*/
|
|
typedef int(*BT_DEV_DBG_FUNC) (int par1, int par2, int par3);
|
|
typedef struct {
|
|
BT_DEV_DBG_FUNC func;
|
|
bool turn_off_availavle; // function can be work when bt off
|
|
} tBT_DEV_DBG_STRUCT;
|
|
|
|
/*******************************************************************************
|
|
* P U B L I C D A T A
|
|
********************************************************************************
|
|
*/
|
|
bool g_bt_trace_pt = FALSE;
|
|
|
|
/*******************************************************************************
|
|
* F U N C T I O N D E C L A R A T I O N S
|
|
********************************************************************************
|
|
*/
|
|
static int bt_dbg_get_bt_state(int par1, int par2, int par3);
|
|
static int bt_dbg_setlog_level(int par1, int par2, int par3);
|
|
static int bt_dbg_is_adie_work(int par1, int par2, int par3);
|
|
static int bt_dbg_trace_pt(int par1, int par2, int par3);
|
|
|
|
/*******************************************************************************
|
|
* P R I V A T E D A T A
|
|
********************************************************************************
|
|
*/
|
|
extern struct bt_dbg_st g_bt_dbg_st;
|
|
extern UINT32 gBtDbgLevel;
|
|
static struct proc_dir_entry *g_bt_dbg_entry;
|
|
static struct mutex g_bt_lock;
|
|
static char g_bt_dump_buf[BT_DBG_DUMP_BUF_SIZE];
|
|
static char *g_bt_dump_buf_ptr;
|
|
static int g_bt_dump_buf_len;
|
|
static bool g_bt_turn_on = FALSE;
|
|
static bool g_bt_dbg_enable = FALSE;
|
|
|
|
static const tBT_DEV_DBG_STRUCT bt_dev_dbg_struct[] = {
|
|
[0xb] = {bt_dbg_setlog_level, TRUE},
|
|
[0xe] = {bt_dbg_get_bt_state, TRUE},
|
|
[0x12] = {bt_dbg_is_adie_work, TRUE},
|
|
[0x15] = {bt_dbg_trace_pt, FALSE},
|
|
};
|
|
|
|
/*******************************************************************************
|
|
* F U N C T I O N S
|
|
********************************************************************************
|
|
*/
|
|
|
|
void _bt_dbg_reset_dump_buf(void)
|
|
{
|
|
memset(g_bt_dump_buf, '\0', BT_DBG_DUMP_BUF_SIZE);
|
|
g_bt_dump_buf_ptr = g_bt_dump_buf;
|
|
g_bt_dump_buf_len = 0;
|
|
}
|
|
|
|
int bt_dbg_get_bt_state(int par1, int par2, int par3)
|
|
{
|
|
// 0x01: bt on, 0x00: bt off
|
|
BT_LOG_PRT_INFO("g_bt_turn_on[%d]\n", g_bt_turn_on);
|
|
_bt_dbg_reset_dump_buf();
|
|
g_bt_dump_buf[0] = g_bt_turn_on;
|
|
g_bt_dump_buf[1] = '\0';
|
|
g_bt_dump_buf_len = 2;
|
|
return 0;
|
|
}
|
|
|
|
int bt_dbg_trace_pt(int par1, int par2, int par3)
|
|
{
|
|
if(par2 == 0)
|
|
g_bt_trace_pt = FALSE;
|
|
else
|
|
g_bt_trace_pt = TRUE;
|
|
return 0;
|
|
}
|
|
|
|
int bt_dbg_is_adie_work(int par1, int par2, int par3)
|
|
{
|
|
int ret = 1, adie_state = 0;
|
|
|
|
ret = mtk_wcn_wmt_adie_workable();
|
|
if (ret == 0)
|
|
adie_state = 1; // power on a-die fail, may be evb without DTB
|
|
else
|
|
adie_state = 0; // power on a-die pass
|
|
|
|
BT_LOG_PRT_INFO("ret[%d], adie_state[%d]", ret, adie_state);
|
|
_bt_dbg_reset_dump_buf();
|
|
g_bt_dump_buf[0] = (adie_state == 0 ? '0' : '1'); // '0': adie pass, '1': adie fail
|
|
g_bt_dump_buf[1] = '\0';
|
|
g_bt_dump_buf_len = 2;
|
|
return 0;
|
|
}
|
|
|
|
int bt_dbg_setlog_level(int par1, int par2, int par3)
|
|
{
|
|
if (par2 < BT_LOG_ERR || par2 > BT_LOG_DBG) {
|
|
gBtDbgLevel = BT_LOG_INFO;
|
|
} else {
|
|
gBtDbgLevel = par2;
|
|
}
|
|
BT_LOG_PRT_INFO("gBtDbgLevel = %d\n", gBtDbgLevel);
|
|
return 0;
|
|
}
|
|
|
|
ssize_t bt_dbg_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
|
|
{
|
|
int ret = 0;
|
|
int dump_len;
|
|
|
|
BT_LOG_PRT_INFO("%s: count[%zd]\n", __func__, count);
|
|
ret = mutex_lock_killable(&g_bt_lock);
|
|
if (ret) {
|
|
BT_LOG_PRT_ERR("dump_lock fail!!\n");
|
|
return ret;
|
|
}
|
|
|
|
if (g_bt_dump_buf_len == 0)
|
|
goto exit;
|
|
|
|
if (*f_pos == 0)
|
|
g_bt_dump_buf_ptr = g_bt_dump_buf;
|
|
|
|
dump_len = g_bt_dump_buf_len >= count ? count : g_bt_dump_buf_len;
|
|
ret = copy_to_user(buf, g_bt_dump_buf_ptr, dump_len);
|
|
if (ret) {
|
|
BT_LOG_PRT_ERR("copy to dump info buffer failed, ret:%d\n", ret);
|
|
ret = -EFAULT;
|
|
goto exit;
|
|
}
|
|
|
|
*f_pos += dump_len;
|
|
g_bt_dump_buf_len -= dump_len;
|
|
g_bt_dump_buf_ptr += dump_len;
|
|
BT_LOG_PRT_INFO("%s: after read, wmt for dump info buffer len(%d)\n", __func__, g_bt_dump_buf_len);
|
|
|
|
ret = dump_len;
|
|
exit:
|
|
|
|
mutex_unlock(&g_bt_lock);
|
|
return ret;
|
|
}
|
|
|
|
int _osal_strtol(const char *str, unsigned int adecimal, long *res)
|
|
{
|
|
if (sizeof(long) == 4)
|
|
return kstrtou32(str, adecimal, (unsigned int *) res);
|
|
else
|
|
return kstrtol(str, adecimal, res);
|
|
}
|
|
|
|
void bt_dbg_user_trx_cb(char *buf, int len)
|
|
{
|
|
unsigned char *ptr = g_bt_dbg_st.rx_buf;
|
|
unsigned int i = 0, evt_len = 0;
|
|
int ret = 0;
|
|
|
|
/* 1. bluedroid use partial read, this callback will enter several times
|
|
2. this function read and parse the command_complete event */
|
|
memcpy(&g_bt_dbg_st.rx_buf[g_bt_dbg_st.rx_len], buf, len);
|
|
g_bt_dbg_st.rx_len += len;
|
|
|
|
// check the complete packet is read out by bluedroid
|
|
if(g_bt_dbg_st.rx_len != (g_bt_dbg_st.rx_buf[2] + 3))
|
|
return;
|
|
|
|
// if this event is not the desire one, skip and reset buffer
|
|
if((g_bt_dbg_st.rx_buf[4] + (g_bt_dbg_st.rx_buf[5] << 8)) != g_bt_dbg_st.trx_opcode) {
|
|
g_bt_dbg_st.rx_len = 0;
|
|
memset(g_bt_dbg_st.rx_buf, 0, sizeof(g_bt_dbg_st.rx_buf));
|
|
return;
|
|
}
|
|
|
|
// desire rx event is received, write to read buffer as string
|
|
evt_len = g_bt_dbg_st.rx_len;
|
|
BT_LOG_PRT_INFO_RAW(g_bt_dbg_st.rx_buf, evt_len, "%s: len[%ud], RxEvt: ", __func__, evt_len);
|
|
if(evt_len * 5 + 2 > BT_DBG_DUMP_BUF_SIZE)
|
|
return;
|
|
|
|
_bt_dbg_reset_dump_buf();
|
|
for (i = 0; i < evt_len; i++) {
|
|
ret = snprintf(g_bt_dump_buf + 5*i, 6, "0x%02X ", ptr[i]);
|
|
if (ret < 0) {
|
|
BT_LOG_PRT_ERR("error snprintf return value = [%d]\n", ret);
|
|
return;
|
|
}
|
|
}
|
|
|
|
g_bt_dump_buf[5*evt_len] = '\n';
|
|
g_bt_dump_buf[5*evt_len + 1] = '\0';
|
|
g_bt_dump_buf_len = 5*evt_len + 1;
|
|
|
|
// complete trx process
|
|
complete(&g_bt_dbg_st.trx_comp);
|
|
}
|
|
|
|
void bt_dbg_user_trx_proc(char *cmd_raw)
|
|
{
|
|
#define LEN_64 64
|
|
unsigned char hci_cmd[LEN_64];
|
|
unsigned int len = 0;
|
|
long tmp = 0;
|
|
char *ptr = NULL, *pRaw = NULL;
|
|
|
|
// Parse command raw data
|
|
memset(hci_cmd, 0, sizeof(hci_cmd));
|
|
pRaw = cmd_raw;
|
|
ptr = cmd_raw;
|
|
while(*ptr != '\0' && pRaw != NULL) {
|
|
if (len > LEN_64 - 1) {
|
|
BT_LOG_PRT_INFO("%s: skip since cmd length exceed!", __func__);
|
|
return;
|
|
}
|
|
ptr = strsep(&pRaw, " ");
|
|
if (ptr != NULL) {
|
|
_osal_strtol(ptr, 16, &tmp);
|
|
hci_cmd[len++] = (unsigned char)tmp;
|
|
}
|
|
}
|
|
|
|
// Initialize rx variables
|
|
g_bt_dbg_st.rx_len = 0;
|
|
g_bt_dbg_st.trx_opcode = hci_cmd[1] + (hci_cmd[2] << 8);
|
|
memset(g_bt_dbg_st.rx_buf, 0, sizeof(g_bt_dbg_st.rx_buf));
|
|
BT_LOG_PRT_INFO_RAW(hci_cmd, len, "%s: len[%ud], TxCmd: ", __func__, len);
|
|
|
|
// Send command and wait for command_complete event
|
|
g_bt_dbg_st.trx_enable = TRUE;
|
|
send_hci_frame(hci_cmd, len);
|
|
if (!wait_for_completion_timeout(&g_bt_dbg_st.trx_comp, msecs_to_jiffies(2000)))
|
|
BT_LOG_PRT_ERR("%s: wait event timeout!", __func__);
|
|
g_bt_dbg_st.trx_enable = FALSE;
|
|
}
|
|
|
|
ssize_t bt_dbg_write(struct file *filp, const char __user *buffer, size_t count, loff_t *f_pos)
|
|
{
|
|
bool is_passwd = FALSE, is_turn_on = FALSE;
|
|
size_t len = count;
|
|
char buf[256], *pBuf;
|
|
int x = 0, y = 0, z = 0;
|
|
long res = 0;
|
|
char* pToken = NULL;
|
|
char* pDelimiter = " \t";
|
|
|
|
if (len <= 0 || len >= sizeof(buf)) {
|
|
BT_LOG_PRT_ERR("input handling fail!\n");
|
|
len = sizeof(buf) - 1;
|
|
return -1;
|
|
}
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
if (copy_from_user(buf, buffer, len))
|
|
return -EFAULT;
|
|
buf[len] = '\0';
|
|
BT_LOG_PRT_INFO("g_bt_turn_on[%d], dbg_enable[%d], len[%d], data = %s\n",
|
|
g_bt_turn_on, g_bt_dbg_enable, (int)len, buf);
|
|
|
|
/* Check debug function is enabled or not
|
|
* - not enable yet: user should enable it
|
|
* - already enabled: user can disable it
|
|
*/
|
|
if (len > strlen(BT_DBG_PASSWD) &&
|
|
0 == memcmp(buf, BT_DBG_PASSWD, strlen(BT_DBG_PASSWD))) {
|
|
is_passwd = TRUE;
|
|
if (0 == memcmp(buf + strlen(BT_DBG_PASSWD), "ON", strlen("ON")))
|
|
is_turn_on = TRUE;
|
|
}
|
|
if(!g_bt_dbg_enable) {
|
|
if(is_passwd && is_turn_on)
|
|
g_bt_dbg_enable = TRUE;
|
|
return len;
|
|
} else {
|
|
if(is_passwd && !is_turn_on) {
|
|
g_bt_dbg_enable = FALSE;
|
|
return len;
|
|
}
|
|
}
|
|
|
|
/* Mode 1: User trx flow: send command, get response */
|
|
if (0 == memcmp(buf, BT_DBG_USER_TRX_PREFIX, strlen(BT_DBG_USER_TRX_PREFIX))) {
|
|
if(!g_bt_turn_on) // only work when bt on
|
|
return len;
|
|
buf[len - 1] = '\0';
|
|
bt_dbg_user_trx_proc(buf + strlen(BT_DBG_USER_TRX_PREFIX));
|
|
return len;
|
|
}
|
|
|
|
/* Mode 2: Debug cmd flow, parse three parameters */
|
|
pBuf = buf;
|
|
pToken = strsep(&pBuf, pDelimiter);
|
|
if (pToken != NULL) {
|
|
_osal_strtol(pToken, 16, &res);
|
|
x = (int)res;
|
|
} else {
|
|
x = 0;
|
|
}
|
|
|
|
pToken = strsep(&pBuf, "\t\n ");
|
|
if (pToken != NULL) {
|
|
_osal_strtol(pToken, 16, &res);
|
|
y = (int)res;
|
|
BT_LOG_PRT_INFO("y = 0x%08x\n", y);
|
|
} else {
|
|
y = 3000;
|
|
/*efuse, register read write default value */
|
|
if (0x5 == x || 0x6 == x)
|
|
y = 0x80000000;
|
|
}
|
|
|
|
pToken = strsep(&pBuf, "\t\n ");
|
|
if (pToken != NULL) {
|
|
_osal_strtol(pToken, 16, &res);
|
|
z = (int)res;
|
|
} else {
|
|
z = 10;
|
|
/*efuse, register read write default value */
|
|
if (0x5 == x || 0x6 == x)
|
|
z = 0xffffffff;
|
|
}
|
|
|
|
BT_LOG_PRT_INFO("x(0x%08x), y(0x%08x), z(0x%08x)\n", x, y, z);
|
|
if (ARRAY_SIZE(bt_dev_dbg_struct) > x && NULL != bt_dev_dbg_struct[x].func) {
|
|
if(!g_bt_turn_on && !bt_dev_dbg_struct[x].turn_off_availavle) {
|
|
BT_LOG_PRT_WARN("command id(0x%08x) only work when bt on!\n", x);
|
|
} else {
|
|
(*bt_dev_dbg_struct[x].func) (x, y, z);
|
|
}
|
|
} else {
|
|
BT_LOG_PRT_WARN("command id(0x%08x) no handler defined!\n", x);
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
int bt_dev_dbg_init(void)
|
|
{
|
|
int i_ret = 0;
|
|
#if (LINUX_VERSION_CODE <= KERNEL_VERSION(5,6,0))
|
|
static const struct file_operations bt_dbg_fops = {
|
|
.owner = THIS_MODULE,
|
|
.read = bt_dbg_read,
|
|
.write = bt_dbg_write,
|
|
};
|
|
#else
|
|
static const struct proc_ops bt_dbg_fops = {
|
|
.proc_read = bt_dbg_read,
|
|
.proc_write = bt_dbg_write,
|
|
};
|
|
#endif
|
|
|
|
// initialize debug function struct
|
|
g_bt_dbg_st.trx_enable = FALSE;
|
|
g_bt_dbg_st.trx_opcode = 0;
|
|
g_bt_dbg_st.trx_cb = bt_dbg_user_trx_cb;
|
|
init_completion(&g_bt_dbg_st.trx_comp);
|
|
|
|
g_bt_dbg_entry = proc_create(BT_DBG_PROCNAME, 0664, NULL, &bt_dbg_fops);
|
|
if (g_bt_dbg_entry == NULL) {
|
|
BT_LOG_PRT_ERR("Unable to create [%s] bt proc entry\n", BT_DBG_PROCNAME);
|
|
i_ret = -1;
|
|
}
|
|
|
|
mutex_init(&g_bt_lock);
|
|
|
|
BT_LOG_PRT_INFO("create [%s] done\n", BT_DBG_PROCNAME);
|
|
return i_ret;
|
|
}
|
|
|
|
int bt_dev_dbg_deinit(void)
|
|
{
|
|
mutex_destroy(&g_bt_lock);
|
|
|
|
if (g_bt_dbg_entry != NULL) {
|
|
proc_remove(g_bt_dbg_entry);
|
|
g_bt_dbg_entry = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int bt_dev_dbg_set_state(bool turn_on)
|
|
{
|
|
g_bt_turn_on = turn_on;
|
|
return 0;
|
|
}
|