blob: 1f571ca2558a13f5f30437ca479ff4fdb5457b79 [file] [log] [blame]
/*
* Copyright (c) 2016,2017 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 "btmtk_config.h"
#include <linux/version.h>
#include <linux/firmware.h>
#include <linux/slab.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio_func.h>
#include <linux/module.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include <linux/cdev.h>
#include <linux/spinlock.h>
#include <linux/kallsyms.h>
#include <linux/device.h>
/* Define for proce node */
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include "btmtk_define.h"
#include "btmtk_drv.h"
#include "btmtk_sdio.h"
#if SUPPORT_EINT
/* Used for WoBLE on EINT */
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#endif
#ifdef BT_SUPPORT_PMU_EN_CTRL
#include "mt7668_wmt.h"
#endif
#ifdef BT_DRIVER_BUILD_MODULE
void sdio_card_detect(int card_present);
#endif
#if SUPPORT_BT_STEREO
#include <linux/of.h>
#include <linux/of_irq.h>
struct bt_stereo_clk stereo_clk;
unsigned int stereo_irq = 0;
unsigned int clk_flag = 0;
#endif
static dev_t g_devIDfwlog;
static struct class *pBTClass;
static struct device *pBTDev;
struct device *pBTDevfwlog;
static wait_queue_head_t inq;
static wait_queue_head_t fw_log_inq;
static struct fasync_struct *fasync;
/*static int btmtk_woble_state = BTMTK_WOBLE_STATE_UNKNOWN;*/
static struct hci_dev *hdev;
static int need_reset_stack;
static int need_set_i2s = 0;
/* The btmtk_sdio_remove() callback function is called
* when user removes this module from kernel space or ejects
* the card from the slot. The driver handles these 2 cases
* differently.
* If the user is removing the module, a MODULE_SHUTDOWN_REQ
* command is sent to firmware and interrupt will be disabled.
* If the card is removed, there is no need to send command
* or disable interrupt.
*
* The variable 'user_rmmod' is used to distinguish these two
* scenarios. This flag is initialized as FALSE in case the card
* is removed, and will be set to TRUE for module removal when
* module_exit function is called.
*/
/*============================================================================*/
/* Callback Functions */
/*============================================================================*/
static u8 user_rmmod;
struct completion g_done;
unsigned char probe_counter;
unsigned char current_fwdump_file_number;
struct btmtk_private *g_priv;
#define STR_COREDUMP_END "coredump end\n\n"
const u8 READ_ADDRESS_EVENT[] = { 0x0e, 0x0a, 0x01, 0x09, 0x10, 0x00 };
static struct ring_buffer metabuffer;
static struct ring_buffer fwlog_metabuffer;
u8 probe_ready;
/* record firmware version */
static char fw_version_str[FW_VERSION_BUF_SIZE];
static struct proc_dir_entry *g_proc_dir;
/** read_write for proc */
static int btmtk_proc_show(struct seq_file *m, void *v);
static int btmtk_proc_open(struct inode *inode, struct file *file);
static void btmtk_proc_create_new_entry(void);
static int btmtk_sdio_trigger_fw_assert(void);
static char fw_dump_file_name[FW_DUMP_FILE_NAME_SIZE] = {0};
static char event_need_compare[EVENT_COMPARE_SIZE] = {0};
static char event_need_compare_len;
static char event_compare_status;
/*add special header in the beginning of even, stack won't recognize these event*/
struct _OSAL_UNSLEEPABLE_LOCK_ event_compare_status_lock;
struct _OSAL_UNSLEEPABLE_LOCK_ tx_function_lock;
int fw_dump_buffer_full;
int fw_dump_total_read_size;
int fw_dump_total_write_size;
int fw_dump_buffer_used_size;
int fw_dump_task_should_stop;
u8 *fw_dump_ptr;
u8 *fw_dump_read_ptr;
u8 *fw_dump_write_ptr;
struct timeval fw_dump_last_write_time;
int fw_dump_end_checking_task_should_stop;
int fw_is_doing_coredump;
int fw_is_coredump_end_packet;
#if SAVE_FW_DUMP_IN_KERNEL
static struct file *fw_dump_file;
#else
static int fw_dump_file;
#endif
const struct file_operations BT_proc_fops = {
.open = btmtk_proc_open,
.read = seq_read,
.release = single_release,
};
static const struct btmtk_sdio_card_reg btmtk_reg_6630 = {
.cfg = 0x03,
.host_int_mask = 0x04,
.host_intstatus = 0x05,
.card_status = 0x20,
.sq_read_base_addr_a0 = 0x10,
.sq_read_base_addr_a1 = 0x11,
.card_fw_status0 = 0x40,
.card_fw_status1 = 0x41,
.card_rx_len = 0x42,
.card_rx_unit = 0x43,
.io_port_0 = 0x00,
.io_port_1 = 0x01,
.io_port_2 = 0x02,
.int_read_to_clear = false,
.func_num = 2,
.chip_id = 0x6630,
};
static const struct btmtk_sdio_card_reg btmtk_reg_6632 = {
.cfg = 0x03,
.host_int_mask = 0x04,
.host_intstatus = 0x05,
.card_status = 0x20,
.sq_read_base_addr_a0 = 0x10,
.sq_read_base_addr_a1 = 0x11,
.card_fw_status0 = 0x40,
.card_fw_status1 = 0x41,
.card_rx_len = 0x42,
.card_rx_unit = 0x43,
.io_port_0 = 0x00,
.io_port_1 = 0x01,
.io_port_2 = 0x02,
.int_read_to_clear = false,
.func_num = 2,
.chip_id = 0x6632,
};
static const struct btmtk_sdio_card_reg btmtk_reg_7668 = {
.cfg = 0x03,
.host_int_mask = 0x04,
.host_intstatus = 0x05,
.card_status = 0x20,
.sq_read_base_addr_a0 = 0x10,
.sq_read_base_addr_a1 = 0x11,
.card_fw_status0 = 0x40,
.card_fw_status1 = 0x41,
.card_rx_len = 0x42,
.card_rx_unit = 0x43,
.io_port_0 = 0x00,
.io_port_1 = 0x01,
.io_port_2 = 0x02,
.int_read_to_clear = false,
.func_num = 2,
.chip_id = 0x7668,
};
static const struct btmtk_sdio_card_reg btmtk_reg_7666 = {
.cfg = 0x03,
.host_int_mask = 0x04,
.host_intstatus = 0x05,
.card_status = 0x20,
.sq_read_base_addr_a0 = 0x10,
.sq_read_base_addr_a1 = 0x11,
.card_fw_status0 = 0x40,
.card_fw_status1 = 0x41,
.card_rx_len = 0x42,
.card_rx_unit = 0x43,
.io_port_0 = 0x00,
.io_port_1 = 0x01,
.io_port_2 = 0x02,
.int_read_to_clear = false,
.func_num = 2,
.chip_id = 0x7666,
};
static const struct btmtk_sdio_device btmtk_sdio_6630 = {
.helper = "mtmk/sd8688_helper.bin",
.firmware = "mt6632_patch_e1_hdr.bin",
.reg = &btmtk_reg_6630,
.support_pscan_win_report = false,
.sd_blksz_fw_dl = 64,
.supports_fw_dump = false,
};
static const struct btmtk_sdio_device btmtk_sdio_6632 = {
.helper = "mtmk/sd8688_helper.bin",
.firmware = "mt6632_patch_e1_hdr.bin",
.reg = &btmtk_reg_6632,
.support_pscan_win_report = false,
.sd_blksz_fw_dl = 64,
.supports_fw_dump = false,
};
static const struct btmtk_sdio_device btmtk_sdio_7668 = {
.helper = "mtmk/sd8688_helper.bin",
#if CFG_THREE_IN_ONE_FIRMWARE
.firmware = "MT7668_FW",
#else
.firmware = "mt7668_patch_e1_hdr.bin",
.firmware1 = "mt7668_patch_e2_hdr.bin",
#endif
.reg = &btmtk_reg_7668,
.support_pscan_win_report = false,
.sd_blksz_fw_dl = 64,
.supports_fw_dump = false,
};
static const struct btmtk_sdio_device btmtk_sdio_7666 = {
.helper = "mtmk/sd8688_helper.bin",
.firmware = "mt7668_patch_e1_hdr.bin",
.reg = &btmtk_reg_7666,
.support_pscan_win_report = false,
.sd_blksz_fw_dl = 64,
.supports_fw_dump = false,
};
unsigned char *txbuf;
static unsigned char *rxbuf;
static unsigned char *userbuf;
static unsigned char *userbuf_fwlog;
static u32 rx_length;
static struct btmtk_sdio_card *g_card;
#define SDIO_VENDOR_ID_MEDIATEK 0x037A
static const struct sdio_device_id btmtk_sdio_ids[] = {
/* Mediatek SD8688 Bluetooth device */
{ SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x6630),
.driver_data = (unsigned long) &btmtk_sdio_6630 },
{ SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x6632),
.driver_data = (unsigned long) &btmtk_sdio_6632 },
{ SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7668),
.driver_data = (unsigned long) &btmtk_sdio_7668 },
{ SDIO_DEVICE(SDIO_VENDOR_ID_MEDIATEK, 0x7666),
.driver_data = (unsigned long) &btmtk_sdio_7666 },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(sdio, btmtk_sdio_ids);
static void btmtk_sdio_woble_free_setting_struct(struct woble_setting_struct *woble_struct, int count)
{
int i = 0;
for (i = 0; i < count; i++) {
if (woble_struct[i].content) {
pr_info("%s:kfree %d", __func__, i);
kfree(woble_struct[i].content);
woble_struct[i].content = NULL;
woble_struct[i].length = 0;
} else
woble_struct[i].length = 0;
}
}
static void btmtk_sdio_woble_free_setting(void)
{
pr_info("%s begin", __func__);
if (g_card == NULL) {
pr_err("%s: g_data == NULL", __func__);
return;
}
btmtk_sdio_woble_free_setting_struct(g_card->woble_setting_apcf, WOBLE_SETTING_COUNT);
btmtk_sdio_woble_free_setting_struct(g_card->woble_setting_apcf_fill_mac, WOBLE_SETTING_COUNT);
btmtk_sdio_woble_free_setting_struct(g_card->woble_setting_apcf_fill_mac_location, WOBLE_SETTING_COUNT);
btmtk_sdio_woble_free_setting_struct(g_card->woble_setting_radio_off, WOBLE_SETTING_COUNT);
btmtk_sdio_woble_free_setting_struct(g_card->woble_setting_radio_off_status_event, WOBLE_SETTING_COUNT);
btmtk_sdio_woble_free_setting_struct(g_card->woble_setting_radio_off_comp_event, WOBLE_SETTING_COUNT);
btmtk_sdio_woble_free_setting_struct(g_card->woble_setting_radio_on, WOBLE_SETTING_COUNT);
btmtk_sdio_woble_free_setting_struct(g_card->woble_setting_radio_on_status_event, WOBLE_SETTING_COUNT);
btmtk_sdio_woble_free_setting_struct(g_card->woble_setting_radio_on_comp_event, WOBLE_SETTING_COUNT);
if (g_card->woble_setting_len) {
kfree(g_card->woble_setting);
g_card->woble_setting_len = 0;
g_card->woble_setting = NULL;
}
pr_info("%s end", __func__);
}
static int btmtk_sdio_load_woble_block_setting(char *block_name, struct woble_setting_struct *save_content,
int save_content_count, u8 *searchconetnt)
{
int ret = 0;
int i = 0;
long parsing_result = 0;
u8 *search_result = NULL;
u8 *search_end = NULL;
u8 search[32];
u8 temp[128]; /* save for total hex number */
u8 *next_number = NULL;
u8 *next_block = NULL;
u8 number[8];
int temp_len;
memset(search, 0, sizeof(search));
memset(temp, 0, sizeof(temp));
memset(number, 0, sizeof(number));
/* search block name */
for (i = 0; i < WOBLE_SETTING_COUNT; i++) {
temp_len = 0;
snprintf(search, sizeof(search), "%s%02d:", block_name, i); /* ex APCF01 */
search_result = strstr(searchconetnt, search);
if (search_result) {
memset(temp, 0, sizeof(temp));
temp_len = 0;
search_result += strlen(search); /* move to first number */
do {
next_number = NULL;
search_end = strstr(search_result, ",");
if ((search_end - search_result) <= 0) {
pr_info("%s can not find search end, break", __func__);
break;
}
if ((search_end - search_result) > sizeof(number))
break;
memset(number, 0, sizeof(number));
memcpy(number, search_result, search_end - search_result);
if (number[0] == 0x20) /* space */
ret = kstrtol(number + 1, 0, &parsing_result);
else
ret = kstrtol(number, 0, &parsing_result);
if (ret == 0) {
if (temp_len >= sizeof(temp)) {
pr_err("%s: %s data over %zu", __func__, search, sizeof(temp));
break;
}
temp[temp_len] = parsing_result;
temp_len++;
/* find next number */
next_number = strstr(search_end, "0x");
/* find next block */
next_block = strstr(search_end, ":");
} else {
pr_debug("%s:kstrtol ret = %d, search %s", __func__, ret, search);
break;
}
if (next_number == NULL) {
pr_debug("%s: not find next apcf number temp_len %d, break, search %s",
__func__, temp_len, search);
break;
}
if ((next_number > next_block) && (next_block != 0)) {
pr_debug("%s: find next apcf number is over to next block ", __func__);
pr_debug("%s: temp_len %d, break, search %s",
__func__, temp_len, search);
break;
}
search_result = search_end + 1;
} while (1);
} else
pr_debug("%s: %s is not found", __func__, search);
if (temp_len) {
pr_info("%s: %s found", __func__, search);
pr_debug("%s: kzalloc i=%d temp_len=%d", __func__, i, temp_len);
save_content[i].content = kzalloc(temp_len, GFP_KERNEL);
memcpy(save_content[i].content, temp, temp_len);
save_content[i].length = temp_len;
pr_debug("%s:x save_content[%d].length %d temp_len=%d",
__func__, i, save_content[i].length, temp_len);
}
}
return ret;
}
static int btmtk_sdio_load_woble_setting(u8 *image, char *bin_name,
struct device *dev, u32 *code_len, struct btmtk_sdio_card *data)
{
int err;
const struct firmware *fw_entry = NULL;
pr_info("%s: begin woble_setting_file_name = %s", __func__, bin_name);
err = request_firmware(&fw_entry, bin_name, dev);
if (err != 0) {
kfree(image);
image = NULL;
pr_err("%s: request_firmware function for woble setting fail!! error code = %d", __func__, err);
return err;
}
if (fw_entry) {
image = kzalloc(fw_entry->size + 1, GFP_KERNEL); /* w:move to btmtk_usb_free_memory */
pr_info("%s: woble_setting request_firmware size %zu success", __func__, fw_entry->size);
} else {
pr_err("%s: fw_entry is NULL request_firmware may fail!! error code = %d", __func__, err);
return err;
}
if (image == NULL) {
pr_err("%s: kzalloc size %zu failed!!", __func__, fw_entry->size);
*code_len = 0;
return err;
}
memcpy(image, fw_entry->data, fw_entry->size);
image[fw_entry->size] = '\0';
*code_len = fw_entry->size;
pr_info("%s: code_len (%d) assign done", __func__, *code_len);
err = btmtk_sdio_load_woble_block_setting("APCF",
data->woble_setting_apcf, WOBLE_SETTING_COUNT, image);
if (err)
goto LOAD_END;
err = btmtk_sdio_load_woble_block_setting("APCF_ADD_MAC",
data->woble_setting_apcf_fill_mac, WOBLE_SETTING_COUNT, image);
if (err)
goto LOAD_END;
err = btmtk_sdio_load_woble_block_setting("APCF_ADD_MAC_LOCATION",
data->woble_setting_apcf_fill_mac_location, WOBLE_SETTING_COUNT, image);
if (err)
goto LOAD_END;
err = btmtk_sdio_load_woble_block_setting("RADIOOFF",
data->woble_setting_radio_off, WOBLE_SETTING_COUNT, image);
if (err)
goto LOAD_END;
err = btmtk_sdio_load_woble_block_setting("RADIOOFF_STATUS_EVENT",
data->woble_setting_radio_off_status_event, WOBLE_SETTING_COUNT, image);
if (err)
goto LOAD_END;
err = btmtk_sdio_load_woble_block_setting("RADIOOFF_COMPLETE_EVENT",
data->woble_setting_radio_off_comp_event, WOBLE_SETTING_COUNT, image);
if (err)
goto LOAD_END;
err = btmtk_sdio_load_woble_block_setting("RADIOON",
data->woble_setting_radio_on, WOBLE_SETTING_COUNT, image);
if (err)
goto LOAD_END;
err = btmtk_sdio_load_woble_block_setting("RADIOON_STATUS_EVENT",
data->woble_setting_radio_on_status_event, WOBLE_SETTING_COUNT, image);
if (err)
goto LOAD_END;
err = btmtk_sdio_load_woble_block_setting("RADIOON_COMPLETE_EVENT",
data->woble_setting_radio_on_comp_event, WOBLE_SETTING_COUNT, image);
if (err)
goto LOAD_END;
err = btmtk_sdio_load_woble_block_setting("APCF_RESMUE",
data->woble_setting_apcf_resume, WOBLE_SETTING_COUNT, image);
if (err)
goto LOAD_END;
err = btmtk_sdio_load_woble_block_setting("APCF_COMPLETE_EVENT",
data->woble_setting_apcf_resume, WOBLE_SETTING_COUNT, image);
LOAD_END:
if (err)
pr_info("%s: error return %d", __func__, err);
return err;
}
static inline void btmtk_sdio_woble_wake_lock(struct btmtk_sdio_card *data)
{
#if (SUPPORT_UNIFY_WOBLE & SUPPORT_ANDROID)
BTUSB_INFO("%s:", __func__);
wake_lock(&data->woble_wlock);
#endif
}
static inline void btmtk_sdio_woble_wake_unlock(struct btmtk_sdio_card *data)
{
#if (SUPPORT_UNIFY_WOBLE & SUPPORT_ANDROID)
BTUSB_INFO("%s:", __func__);
wake_unlock(&data->woble_wlock);
#endif
}
u32 lock_unsleepable_lock(struct _OSAL_UNSLEEPABLE_LOCK_ *pUSL)
{
spin_lock_irqsave(&(pUSL->lock), pUSL->flag);
return 0;
}
u32 unlock_unsleepable_lock(struct _OSAL_UNSLEEPABLE_LOCK_ *pUSL)
{
spin_unlock_irqrestore(&(pUSL->lock), pUSL->flag);
return 0;
}
static int btmtk_sdio_readb(u32 offset, u32 *val)
{
u32 ret = 0;
if (g_card->func == NULL) {
pr_err("%s g_card->func is NULL\n", __func__);
return -EIO;
}
sdio_claim_host(g_card->func);
*val = sdio_readb(g_card->func, offset, &ret);
sdio_release_host(g_card->func);
return ret;
}
static int btmtk_sdio_writel(u32 offset, u32 val)
{
u32 ret = 0;
if (g_card->func == NULL) {
pr_err("%s g_card->func is NULL\n", __func__);
return -EIO;
}
sdio_claim_host(g_card->func);
sdio_writel(g_card->func, val, offset, &ret);
sdio_release_host(g_card->func);
return ret;
}
static int btmtk_sdio_readl(u32 offset, u32 *val)
{
u32 ret = 0;
if (g_card->func == NULL) {
pr_err("g_card->func is NULL\n");
return -EIO;
}
sdio_claim_host(g_card->func);
*val = sdio_readl(g_card->func, offset, &ret);
sdio_release_host(g_card->func);
return ret;
}
static void btmtk_sdio_set_no_fw_own(struct btmtk_private *priv, bool no_fw_own)
{
if (priv) {
priv->no_fw_own = no_fw_own;
pr_debug("%s set no_fw_own %d\n", __func__, priv->no_fw_own);
} else
pr_debug("%s priv is NULL\n", __func__);
}
static int btmtk_sdio_set_own_back(int owntype)
{
/*Set driver own*/
int ret = 0;
u32 u32LoopCount = 0;
u32 u32ReadCRValue = 0;
u32 ownValue = 0;
u32 set_checkretry = 30;
pr_debug("%s owntype %d\n", __func__, owntype);
if (user_rmmod)
set_checkretry = 1;
if (owntype == FW_OWN && (g_priv)) {
if (g_priv->no_fw_own) {
pr_debug("%s no_fw_own is on, just return \n", __func__);
return ret;
}
}
ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue);
pr_debug("%s btmtk_sdio_readl CHLPCR done\n", __func__);
if (owntype == DRIVER_OWN) {
if ((u32ReadCRValue&0x100) == 0x100) {
pr_debug("%s already driver own 0x%0x, return\n",
__func__, u32ReadCRValue);
return ret;
}
} else if (owntype == FW_OWN) {
if ((u32ReadCRValue&0x100) == 0) {
pr_debug("%s already FW own 0x%0x, return\n",
__func__, u32ReadCRValue);
return ret;
}
}
setretry:
if (owntype == DRIVER_OWN)
ownValue = 0x00000200;
else
ownValue = 0x00000100;
pr_debug("%s write CHLPCR 0x%x\n", __func__, ownValue);
ret = btmtk_sdio_writel(CHLPCR, ownValue);
if (ret) {
ret = -EINVAL;
goto done;
}
pr_debug("%s write CHLPCR 0x%x done\n", __func__, ownValue);
u32LoopCount = 1000;
if (owntype == DRIVER_OWN) {
do {
udelay(1);
ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue);
u32LoopCount--;
pr_debug("%s DRIVER_OWN btmtk_sdio_readl CHLPCR 0x%x\n",
__func__, u32ReadCRValue);
} while ((u32LoopCount > 0) &&
((u32ReadCRValue&0x100) != 0x100));
if ((u32LoopCount == 0) && (0x100 != (u32ReadCRValue&0x100))
&& (set_checkretry > 0)) {
pr_warn("%s retry set_check driver own, CHLPCR 0x%x\n",
__func__, u32ReadCRValue);
set_checkretry--;
mdelay(20);
goto setretry;
}
} else {
do {
udelay(1);
ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue);
u32LoopCount--;
pr_debug("%s FW_OWN btmtk_sdio_readl CHLPCR 0x%x\n",
__func__, u32ReadCRValue);
} while ((u32LoopCount > 0) && ((u32ReadCRValue&0x100) != 0));
if ((u32LoopCount == 0) &&
((u32ReadCRValue&0x100) != 0) &&
(set_checkretry > 0)) {
pr_warn("%s retry set_check FW own, CHLPCR 0x%x\n",
__func__, u32ReadCRValue);
set_checkretry--;
goto setretry;
}
}
pr_debug("%s CHLPCR(0x%x), is 0x%x\n",
__func__, CHLPCR, u32ReadCRValue);
if (owntype == DRIVER_OWN) {
if ((u32ReadCRValue&0x100) == 0x100)
pr_debug("%s check %04x, is 0x100 driver own success\n",
__func__, CHLPCR);
else {
pr_debug("%s check %04x, is %x shuld be 0x100\n",
__func__, CHLPCR, u32ReadCRValue);
ret = EINVAL;
goto done;
}
} else {
if (0x0 == (u32ReadCRValue&0x100))
pr_debug("%s check %04x, bit 8 is 0 FW own success\n",
__func__, CHLPCR);
else{
pr_debug("%s bit 8 should be 0, %04x bit 8 is %04x\n",
__func__, u32ReadCRValue,
(u32ReadCRValue&0x100));
ret = EINVAL;
goto done;
}
}
done:
if (owntype == DRIVER_OWN) {
if (ret)
pr_err("%s set driver own fail\n", __func__);
else
pr_debug("%s set driver own success\n", __func__);
} else if (owntype == FW_OWN) {
if (ret)
pr_err("%s set FW own fail\n", __func__);
else
pr_debug("%s set FW own success\n", __func__);
} else
pr_err("%s unknown type %d\n", __func__, owntype);
return ret;
}
static int btmtk_sdio_enable_interrupt(int enable)
{
u32 ret = 0;
u32 cr_value = 0;
if (enable)
cr_value |= C_FW_INT_EN_SET;
else
cr_value |= C_FW_INT_EN_CLEAR;
ret = btmtk_sdio_writel(CHLPCR, cr_value);
pr_debug("%s enable %d write CHLPCR 0x%08x\n",
__func__, enable, cr_value);
return ret;
}
static int btmtk_sdio_get_rx_unit(struct btmtk_sdio_card *card)
{
u8 reg;
int ret;
reg = sdio_readb(card->func, card->reg->card_rx_unit, &ret);
if (!ret)
card->rx_unit = reg;
return ret;
}
static int btmtk_sdio_enable_host_int_mask(
struct btmtk_sdio_card *card,
u8 mask)
{
int ret;
sdio_writeb(card->func, mask, card->reg->host_int_mask, &ret);
if (ret) {
pr_err("Unable to enable the host interrupt!\n");
ret = -EIO;
}
return ret;
}
static int btmtk_sdio_disable_host_int_mask(
struct btmtk_sdio_card *card,
u8 mask)
{
u8 host_int_mask;
int ret;
host_int_mask = sdio_readb(card->func, card->reg->host_int_mask, &ret);
if (ret)
return -EIO;
host_int_mask &= ~mask;
sdio_writeb(card->func, host_int_mask, card->reg->host_int_mask, &ret);
if (ret < 0) {
pr_err("Unable to disable the host interrupt!\n");
return -EIO;
}
return 0;
}
/*for debug*/
int btmtk_print_buffer_conent(u8 *buf, u32 Datalen)
{
int i = 0;
int print_finish = 0;
/*Print out txbuf data for debug*/
for (i = 0; i <= (Datalen-1); i += 16) {
if ((i+16) <= Datalen) {
pr_debug("%s: %02X%02X%02X%02X%02X %02X%02X%02X%02X%02X %02X%02X%02X%02X%02X %02X\n",
__func__,
buf[i], buf[i+1], buf[i+2], buf[i+3],
buf[i+4], buf[i+5], buf[i+6], buf[i+7],
buf[i+8], buf[i+9], buf[i+10], buf[i+11],
buf[i+12], buf[i+13], buf[i+14], buf[i+15]);
} else {
for (; i < (Datalen); i++)
pr_debug("%s: %02X\n", __func__, buf[i]);
print_finish = 1;
}
if (print_finish)
break;
}
return 0;
}
static int btmtk_sdio_send_tx_data(u8 *buffer, int tx_data_len)
{
int ret = 0;
u8 MultiBluckCount = 0;
u8 redundant = 0;
MultiBluckCount = tx_data_len/SDIO_BLOCK_SIZE;
redundant = tx_data_len % SDIO_BLOCK_SIZE;
if (redundant)
tx_data_len = (MultiBluckCount+1)*SDIO_BLOCK_SIZE;
sdio_claim_host(g_card->func);
ret = sdio_writesb(g_card->func, CTDR, buffer, tx_data_len);
sdio_release_host(g_card->func);
return ret;
}
static int btmtk_sdio_recv_rx_data(void)
{
int ret = 0;
u32 u32ReadCRValue = 0;
int retry_count = 5;
u32 sdio_header_length = 0;
memset(rxbuf, 0, MTK_RXDATA_SIZE);
do {
ret = btmtk_sdio_readl(CHISR, &u32ReadCRValue);
pr_debug("%s: loop Get CHISR 0x%08X\n",
__func__, u32ReadCRValue);
rx_length = (u32ReadCRValue & RX_PKT_LEN) >> 16;
if (rx_length == 0xFFFF) {
pr_warn("%s: 0xFFFF==rx_length, error return -EIO\n",
__func__);
ret = -EIO;
break;
}
if ((RX_DONE&u32ReadCRValue) && rx_length) {
pr_debug("%s: u32ReadCRValue = %08X\n",
__func__, u32ReadCRValue);
u32ReadCRValue &= 0xFFFB;
ret = btmtk_sdio_writel(CHISR, u32ReadCRValue);
pr_debug("%s: write = %08X\n",
__func__, u32ReadCRValue);
sdio_claim_host(g_card->func);
ret = sdio_readsb(g_card->func, rxbuf, CRDR, rx_length);
sdio_release_host(g_card->func);
sdio_header_length = (rxbuf[1] << 8);
sdio_header_length |= rxbuf[0];
if (sdio_header_length != rx_length) {
pr_err("%s sdio header length %d, rx_length %d mismatch\n",
__func__, sdio_header_length,
rx_length);
break;
}
if (sdio_header_length == 0) {
pr_warn("%s: get sdio_header_length = %d\n",
__func__, sdio_header_length);
continue;
}
break;
}
retry_count--;
if (retry_count <= 0) {
pr_warn("%s: retry_count = %d,timeout\n",
__func__, retry_count);
ret = -EIO;
break;
}
/* msleep(1); */
mdelay(3);
pr_debug("%s: retry_count = %d,wait\n", __func__, retry_count);
if (ret)
break;
} while (1);
if (ret)
return -EIO;
return ret;
}
static int btmtk_sdio_send_wmt_reset(void)
{
int ret = 0;
u8 wmt_event[8] = {4, 0xE4, 5, 2, 7, 1, 0, 0};
u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
u8 mtksdio_wmt_reset[9] = {1, 0x6F, 0xFC, 5, 1, 7, 1, 0, 4};
pr_info("%s:\n", __func__);
mtksdio_packet_header[0] = sizeof(mtksdio_packet_header) +
sizeof(mtksdio_wmt_reset);
memcpy(txbuf, mtksdio_packet_header, MTK_SDIO_PACKET_HEADER_SIZE);
memcpy(txbuf+MTK_SDIO_PACKET_HEADER_SIZE, mtksdio_wmt_reset,
sizeof(mtksdio_wmt_reset));
btmtk_sdio_send_tx_data(txbuf,
MTK_SDIO_PACKET_HEADER_SIZE+sizeof(mtksdio_wmt_reset));
btmtk_sdio_recv_rx_data();
/*compare rx data is wmt reset correct response or not*/
if (memcmp(wmt_event, rxbuf+MTK_SDIO_PACKET_HEADER_SIZE,
sizeof(wmt_event)) != 0) {
ret = -EIO;
pr_warn("%s: fail\n", __func__);
}
return ret;
}
static u32 btmtk_sdio_bt_memRegister_read(u32 cr)
{
int retrytime = 300;
u32 result = 0;
u8 wmt_event[15] = {0x04, 0xE4, 0x10, 0x02,
0x08, 0x0C/*0x1C*/, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x80};
/* msleep(1000); */
u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
u8 mtksdio_wmt_cmd[16] = {0x1, 0x6F, 0xFC, 0x0C,
0x01, 0x08, 0x08, 0x00,
0x02, 0x01, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00};
mtksdio_packet_header[0] = sizeof(mtksdio_packet_header)
+ sizeof(mtksdio_wmt_cmd);
pr_info("%s: read cr %x\n", __func__, cr);
memcpy(&mtksdio_wmt_cmd[12], &cr, sizeof(cr));
memcpy(txbuf, mtksdio_packet_header, MTK_SDIO_PACKET_HEADER_SIZE);
memcpy(txbuf + MTK_SDIO_PACKET_HEADER_SIZE, mtksdio_wmt_cmd,
sizeof(mtksdio_wmt_cmd));
btmtk_sdio_send_tx_data(txbuf,
MTK_SDIO_PACKET_HEADER_SIZE + sizeof(mtksdio_wmt_cmd));
btmtk_print_buffer_conent(txbuf,
MTK_SDIO_PACKET_HEADER_SIZE + sizeof(mtksdio_wmt_cmd));
do {
usleep_range(10*1000, 15*1000);
btmtk_sdio_recv_rx_data();
retrytime--;
if (retrytime <= 0)
break;
pr_info("%s: retrytime %d\n", __func__, retrytime);
} while (!rxbuf[0]);
btmtk_print_buffer_conent(rxbuf, rx_length);
/* compare rx data is wmt reset correct response or not */
#if 0
if (memcmp(wmt_event,
rxbuf+MTK_SDIO_PACKET_HEADER_SIZE,
sizeof(wmt_event)) != 0) {
ret = -EIO;
pr_info("%s: fail\n", __func__);
}
#endif
memcpy(&result, rxbuf+MTK_SDIO_PACKET_HEADER_SIZE + sizeof(wmt_event),
sizeof(result));
pr_info("%s: ger cr 0x%x value 0x%x\n", __func__, cr, result);
return result;
}
/* 1:on , 0:off */
static int btmtk_sdio_bt_set_power(u8 onoff)
{
int ret = 0;
int retrytime = 60;
u8 wmt_event[8] = {4, 0xE4, 5, 2, 6, 1, 0, 0};
u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
u8 mtksdio_wmt_cmd[10] = {1, 0x6F, 0xFC, 6, 1, 6, 2, 0, 0, 1};
if (onoff == 0)
retrytime = 3;
mtksdio_packet_header[0] =
sizeof(mtksdio_packet_header) + sizeof(mtksdio_wmt_cmd);
pr_info("%s: onoff %d\n", __func__, onoff);
mtksdio_wmt_cmd[9] = onoff;
memcpy(txbuf, mtksdio_packet_header, MTK_SDIO_PACKET_HEADER_SIZE);
memcpy(txbuf+MTK_SDIO_PACKET_HEADER_SIZE, mtksdio_wmt_cmd,
sizeof(mtksdio_wmt_cmd));
btmtk_sdio_send_tx_data(txbuf,
MTK_SDIO_PACKET_HEADER_SIZE+sizeof(mtksdio_wmt_cmd));
do {
msleep(100);
btmtk_sdio_recv_rx_data();
retrytime--;
if (retrytime <= 0)
break;
if (retrytime < 40)
pr_warn("%s: retry over 2s, retrytime %d\n",
__func__, retrytime);
pr_debug("%s: retrytime %d\n", __func__, retrytime);
} while (!rxbuf[0]);
/*compare rx data is wmt reset correct response or not*/
if (memcmp(wmt_event, rxbuf+MTK_SDIO_PACKET_HEADER_SIZE,
sizeof(wmt_event)) != 0) {
ret = -EIO;
pr_info("%s: fail\n", __func__);
}
return ret;
}
#if BTMTK_BIN_FILE_MODE
static void GetRandomValue(u8 string[6], bool is7668)
{
int iRandom = 0;
pr_info("Enable random generation\n");
/* first random */
get_random_bytes(&iRandom, sizeof(int));
pr_info("iRandom = [%d]", iRandom);
string[0] = (((iRandom>>24|iRandom>>16) & (0xFE)) | (0x02)); /* Must use private bit(1) and no BCMC bit(0) */
/* second random */
get_random_bytes(&iRandom, sizeof(int));
pr_info("iRandom = [%d]", iRandom);
string[1] = ((iRandom>>8) & 0xFF);
/* third random */
get_random_bytes(&iRandom, sizeof(int));
pr_info("iRandom = [%d]", iRandom);
string[5] = (iRandom & 0xFF);
/* */
string[2] = 0x46;
if (is7668)
string[3] = 0x68;
else
string[3] = 0x62;
string[4] = 0x76;
return;
}
static int btmtk_sdio_send_and_check(u8 *cmd, u16 cmd_len,
u8 *event, u16 event_len)
{
int ret = 0;
int retrytime = 60;
int len = 0;
u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
len = MTK_SDIO_PACKET_HEADER_SIZE + cmd_len;
mtksdio_packet_header[0] = (len & 0x0000ff);
mtksdio_packet_header[1] = (len & 0x00ff00) >> 8;
memcpy(txbuf, mtksdio_packet_header, MTK_SDIO_PACKET_HEADER_SIZE);
memcpy(txbuf + MTK_SDIO_PACKET_HEADER_SIZE, cmd, cmd_len);
btmtk_sdio_send_tx_data(txbuf, len);
if (event && (event_len != 0)) {
do {
msleep(100);
btmtk_sdio_recv_rx_data();
retrytime--;
if (retrytime <= 0)
break;
if (retrytime < 40)
pr_warn("%s: retry over 2s, retrytime %d\n",
__func__, retrytime);
} while (!rxbuf[0]);
if (memcmp(event, rxbuf + MTK_SDIO_PACKET_HEADER_SIZE,
event_len) != 0) {
ret = -EIO;
pr_warn("%s: fail\n", __func__);
}
}
return ret;
}
static bool btmtk_is_bin_file_mode(uint8_t *buf, size_t buf_size)
{
char *p_buf = NULL;
char *ptr = NULL, *p = NULL;
bool ret = true;
if (!buf) {
pr_warn("%s: buf is null\n", __func__);
return false;
} else if (buf_size < (strlen(E2P_MODE) + 2)) {
pr_warn("%s: incorrect buf size(%d)\n",
__func__, (int)buf_size);
return false;
}
p_buf = kmalloc(buf_size + 1, GFP_KERNEL);
if (!p_buf) {
pr_warn("%s: alloc p_buf failed\n", __func__);
return false;
}
memcpy(p_buf, buf, buf_size);
p_buf[buf_size] = '\0';
/* find string */
p = ptr = strstr(p_buf, E2P_MODE);
if (!ptr) {
pr_notice("%s: Can't find %s\n", __func__, E2P_MODE);
ret = false;
goto out;
}
if (p > p_buf) {
p--;
while ((*p == ' ') && (p != p_buf))
p--;
if (*p == '#') {
pr_notice("%s: It's not EEPROM - Bin file mode\n", __func__);
ret = false;
goto out;
}
}
/* check access mode */
ptr += (strlen(E2P_MODE) + 1);
if (*ptr != BIN_FILE_MODE) {
pr_notice("%s: It's not EEPROM - Bin file mode, mode: %c\n", __func__, *ptr);
ret = false;
goto out;
}
out:
kfree(p_buf);
return ret;
}
static void btmtk_set_eeprom2ctrler(uint8_t *buf,
size_t buf_size,
bool is7668)
{
int ret = -1;
uint8_t set_bdaddr[] = {0x01, 0x1A, 0xFC, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t set_bdaddr_e[] = {0x04, 0x0E, 0x04, 0x01,
0x1A, 0xFC, 0x00};
uint8_t set_radio[] = {0x01, 0x79, 0xFC, 0x08,
0x07, 0x80, 0x00, 0x06, 0x07, 0x07, 0x00, 0x00};
uint8_t set_radio_e[] = {0x04, 0x0E, 0x04, 0x01,
0x79, 0xFC, 0x00};
uint8_t set_pwr_offset[] = {0x01, 0x93, 0xFC, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t set_pwr_offset_e[] = {0x04, 0x0E, 0x04, 0x01,
0x93, 0xFC, 0x00};
uint8_t set_xtal[] = {0x01, 0x0E, 0xFC, 0x02, 0x00, 0x00};
uint8_t set_xtal_e[] = {0x04, 0x0E, 0x04, 0x01,
0x0E, 0xFC, 0x00};
uint16_t offset = 0;
if (!buf) {
pr_warn("%s: buf is null\n", __func__);
return;
} else if ((is7668 == true && buf_size < 0x389)
|| (is7668 == false && buf_size < 0x133)) {
pr_warn("%s: incorrect buf size(%d)\n",
__func__, (int)buf_size);
return;
}
/* set BD address */
if (is7668)
offset = 0x384;
else
offset = 0x1A;
set_bdaddr[4] = *(buf + offset);
set_bdaddr[5] = *(buf + offset + 1);
set_bdaddr[6] = *(buf + offset + 2);
set_bdaddr[7] = *(buf + offset + 3);
set_bdaddr[8] = *(buf + offset + 4);
set_bdaddr[9] = *(buf + offset + 5);
if (0x0 == set_bdaddr[4] ||
0x0 == set_bdaddr[5] ||
0x0 == set_bdaddr[6] ||
0x0 == set_bdaddr[7] ||
0x0 == set_bdaddr[8] ||
0x0 == set_bdaddr[9]) {
GetRandomValue(&set_bdaddr[4], true);
}
ret = btmtk_sdio_send_and_check(set_bdaddr, sizeof(set_bdaddr),
set_bdaddr_e, sizeof(set_bdaddr_e));
pr_notice("%s: set BDAddress(%02X-%02X-%02X-%02X-%02X-%02X) %s\n",
__func__,
set_bdaddr[9], set_bdaddr[8], set_bdaddr[7],
set_bdaddr[6], set_bdaddr[5], set_bdaddr[4],
ret < 0 ? "fail" : "OK");
/* radio setting - BT power */
if (is7668) {
offset = 0x382;
/* BT default power */
set_radio[4] = (*(buf + offset) & 0x07);
/* BLE default power */
set_radio[8] = (*(buf + offset + 1) & 0x07);
/* TX MAX power */
set_radio[9] = (*(buf + offset) & 0x70) >> 4;
/* TX power sub level */
set_radio[10] = (*(buf + offset + 1) & 0x30) >> 4;
/* BR/EDR power diff mode */
set_radio[11] = (*(buf + offset + 1) & 0xc0) >> 6;
} else {
offset = 0x132;
/* BT default power */
set_radio[4] = *(buf + offset);
/* BLE default power(no this for 7662 in table) */
set_radio[8] = *(buf + offset);
/* TX MAX power */
set_radio[9] = *(buf + offset + 1);
}
ret = btmtk_sdio_send_and_check(set_radio, sizeof(set_radio),
set_radio_e, sizeof(set_radio_e));
pr_notice("%s: set radio(BT/BLE default power: %d/%d MAX power: %d) %s\n",
__func__,
set_radio[4], set_radio[8], set_radio[9],
ret < 0 ? "fail" : "OK");
/*
* BT TX power compensation for low, middle and high
* channel
*/
if (is7668) {
offset = 0x36D;
/* length */
set_pwr_offset[3] = 6;
/* Group 0 CH 0 ~ CH14 */
set_pwr_offset[4] = *(buf + offset);
/* Group 1 CH15 ~ CH27 */
set_pwr_offset[5] = *(buf + offset + 1);
/* Group 2 CH28 ~ CH40 */
set_pwr_offset[6] = *(buf + offset + 2);
/* Group 3 CH41 ~ CH53 */
set_pwr_offset[7] = *(buf + offset + 3);
/* Group 4 CH54 ~ CH66 */
set_pwr_offset[8] = *(buf + offset + 4);
/* Group 5 CH67 ~ CH84 */
set_pwr_offset[9] = *(buf + offset + 5);
} else {
offset = 0x139;
/* length */
set_pwr_offset[3] = 3;
/* low channel */
set_pwr_offset[4] = *(buf + offset);
/* middle channel */
set_pwr_offset[5] = *(buf + offset + 1);
/* high channel */
set_pwr_offset[6] = *(buf + offset + 2);
}
ret = btmtk_sdio_send_and_check(set_pwr_offset, sizeof(set_pwr_offset),
set_pwr_offset_e, sizeof(set_pwr_offset_e));
pr_notice("%s: set power offset(%02X %02X %02X %02X %02X %02X) %s\n",
__func__,
set_pwr_offset[4], set_pwr_offset[5],
set_pwr_offset[6], set_pwr_offset[7],
set_pwr_offset[8], set_pwr_offset[9],
ret < 0 ? "fail" : "OK");
/* XTAL setting */
if (is7668) {
offset = 0xF4;
/* BT default power */
set_xtal[4] = *(buf + offset);
set_xtal[5] = *(buf + offset + 1);
ret = btmtk_sdio_send_and_check(set_xtal, sizeof(set_xtal),
set_xtal_e, sizeof(set_xtal_e));
pr_notice("%s: set XTAL(0x%02X %02X) %s\n",
__func__,
set_xtal[4], set_xtal[5],
ret < 0 ? "fail" : "OK");
}
}
static void btmtk_eeprom_bin_file(struct btmtk_sdio_card *card)
{
char *cfg_file = NULL;
char bin_file[32];
const struct firmware *cfg_fw = NULL;
const struct firmware *bin_fw = NULL;
int ret = -1;
int chipid = card->func->device;
pr_notice("%s: %X series\n", __func__, chipid);
cfg_file = E2P_ACCESS_MODE_SWITCHER;
sprintf(bin_file, E2P_BIN_FILE, chipid);
usleep_range(10*1000, 15*1000);
/* request configure file */
ret = request_firmware(&cfg_fw, cfg_file, &card->func->dev);
if (ret < 0) {
if (ret == -ENOENT)
pr_notice("%s: Configure file not found, ignore EEPROM bin file\n",
__func__);
else
pr_notice("%s: request configure file fail(%d)\n",
__func__, ret);
return;
}
if (btmtk_is_bin_file_mode((uint8_t *)cfg_fw->data, cfg_fw->size) == false)
goto exit2;
usleep_range(10*1000, 15*1000);
/* open bin file for EEPROM */
ret = request_firmware(&bin_fw, bin_file, &card->func->dev);
if (ret < 0) {
pr_notice("%s: request bin file fail(%d)\n",
__func__, ret);
goto exit2;
}
/* set parameters to controller */
btmtk_set_eeprom2ctrler((uint8_t *)bin_fw->data,
bin_fw->size,
(card->func->device == 0x7668 ? true : false));
exit2:
if (cfg_fw)
release_firmware(cfg_fw);
if (bin_fw)
release_firmware(bin_fw);
}
#endif
/* 1:on , 0:off */
static int btmtk_sdio_set_sleep(void)
{
int ret = 0;
u8 wmt_event[8] = {4, 0xE, 4, 1, 0x7A, 0xFC, 0};
u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
u8 mtksdio_wmt_cmd[11] = {1, 0x7A, 0xFC, 7,
/*3:sdio, 5:usb*/03,
/*host non sleep duration*/0x80, 0x02,
/*host non sleep duration*/0x80, 0x02, 0x0, 0x00};
mtksdio_packet_header[0] =
sizeof(mtksdio_packet_header) + sizeof(mtksdio_wmt_cmd);
pr_info("%s begin\n", __func__);
memcpy(txbuf, mtksdio_packet_header, MTK_SDIO_PACKET_HEADER_SIZE);
memcpy(txbuf+MTK_SDIO_PACKET_HEADER_SIZE, mtksdio_wmt_cmd,
sizeof(mtksdio_wmt_cmd));
btmtk_sdio_send_tx_data(txbuf,
MTK_SDIO_PACKET_HEADER_SIZE+sizeof(mtksdio_wmt_cmd));
btmtk_sdio_recv_rx_data();
btmtk_print_buffer_conent(rxbuf, rx_length);
/*compare rx data is wmt reset correct response or not*/
if (memcmp(wmt_event, rxbuf+MTK_SDIO_PACKET_HEADER_SIZE,
sizeof(wmt_event)) != 0) {
ret = -EIO;
pr_info("%s: fail\n", __func__);
}
return ret;
}
static int btmtk_sdio_host_to_card(struct btmtk_private *priv,
u8 *payload, u16 nb)
{
struct btmtk_sdio_card *card = priv->btmtk_dev.card;
int ret = 0;
int i = 0;
u8 MultiBluckCount = 0;
u8 redundant = 0;
if (payload != txbuf) {
memset(txbuf, 0, MTK_TXDATA_SIZE);
memcpy(txbuf, payload, nb);
}
if (!card || !card->func) {
pr_err("card or function is NULL!\n");
return -EINVAL;
}
MultiBluckCount = nb/SDIO_BLOCK_SIZE;
redundant = nb % SDIO_BLOCK_SIZE;
if (redundant)
nb = (MultiBluckCount+1)*SDIO_BLOCK_SIZE;
if (nb < 16)
btmtk_print_buffer_conent(txbuf, nb);
else
btmtk_print_buffer_conent(txbuf, 16);
do {
/* Transfer data to card */
sdio_claim_host(card->func);
ret = sdio_writesb(card->func, CTDR, txbuf, nb);
sdio_release_host(card->func);
if (ret < 0) {
i++;
pr_err("i=%d writesb failed: %d\n", i, ret);
pr_err("hex: %*ph\n", nb, txbuf);
ret = -EIO;
if (i > MAX_WRITE_IOMEM_RETRY)
goto exit;
}
} while (ret);
priv->btmtk_dev.tx_dnld_rdy = false;
exit:
return ret;
}
static int btmtk_sdio_set_i2s_slave(void)
{
int ret = 0;
u8 wmt_event[7] = { 4, 0xE, 4, 1, 0x72, 0xFC, 0 };
u8 cmd[] = { 1, 0x72, 0xFC, 4, 03, 0x10, 0x00, 0x02 };
u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = { 0 };
pr_info("%s\n", __func__);
mtksdio_packet_header[0] = sizeof(mtksdio_packet_header) + sizeof(cmd);
memcpy(txbuf, mtksdio_packet_header, MTK_SDIO_PACKET_HEADER_SIZE);
memcpy(txbuf + MTK_SDIO_PACKET_HEADER_SIZE, cmd, sizeof(cmd));
btmtk_sdio_send_tx_data(txbuf, MTK_SDIO_PACKET_HEADER_SIZE + sizeof(cmd));
btmtk_sdio_recv_rx_data();
pr_debug("%s rx_length %d\n", __func__, rx_length);
pr_debug("%s rx_length %d %02x%02x%02x%02x%02x\%02x%02x%02x%02x%02x%02x%02x\n", __func__,
rx_length, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3], rxbuf[4]
, rxbuf[5], rxbuf[6], rxbuf[7], rxbuf[8], rxbuf[9]
, rxbuf[10], rxbuf[11]);
/* compare rx data is wmt reset correct response or not */
if (memcmp(wmt_event, rxbuf + MTK_SDIO_PACKET_HEADER_SIZE, sizeof(wmt_event)) != 0) {
ret = -EIO;
pr_err("%s: fail\n", __func__);
}
return ret;
}
static int btmtk_sdio_read_pin_mux_setting(u32 *value)
{
int ret = 0;
u8 cmd[] = { 1, 0xD1, 0xFC, 4, 0x54, 0x30, 0x02, 0x81 };
u8 tempvalue = 0;
u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = { 0 };
pr_info("%s\n", __func__);
mtksdio_packet_header[0] = sizeof(mtksdio_packet_header) + sizeof(cmd);
memcpy(txbuf, mtksdio_packet_header, MTK_SDIO_PACKET_HEADER_SIZE);
memcpy(txbuf + MTK_SDIO_PACKET_HEADER_SIZE, cmd, sizeof(cmd));
pr_debug("%s tx buf %02x%02x%02x%02x%02x\%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
__func__, txbuf[0], txbuf[1], txbuf[2], txbuf[3], txbuf[4]
, txbuf[5], txbuf[6], txbuf[7], txbuf[8], txbuf[9]
, txbuf[10], txbuf[11], txbuf[12], txbuf[13], txbuf[14]);
btmtk_sdio_send_tx_data(txbuf, MTK_SDIO_PACKET_HEADER_SIZE + sizeof(cmd));
btmtk_sdio_recv_rx_data();
pr_debug("%s rx_length %d\n", __func__, rx_length);
pr_debug
("%s rx_length %d %02x%02x%02x%02x%02x\%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
__func__, rx_length, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3], rxbuf[4]
, rxbuf[5], rxbuf[6], rxbuf[7], rxbuf[8], rxbuf[9]
, rxbuf[10], rxbuf[11], rxbuf[12], rxbuf[13], rxbuf[14]);
if (rx_length >= 15) {
tempvalue = rxbuf[14];
*value = (rxbuf[14] << 24) + (rxbuf[13] << 16) + (rxbuf[12] << 8) + rxbuf[11];
pr_debug("%s value=%08x\n", __func__, *value);
} else
ret = -EIO;
return ret;
}
static int btmtk_sdio_write_pin_mux_setting(u32 value)
{
int ret = 0;
u8 event[7] = { 4, 0xE, 4, 1, 0xD0, 0xFC, 0 };
u8 cmd[12] = { 1, 0xD0, 0xFC, 8, 0x54, 0x30, 0x02, 0x81, 0, 0, 0, 0 };
u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = { 0 };
pr_info("%s begin\n", __func__);
cmd[8] = (value & 0x000000FF);
cmd[9] = ((value & 0x0000FF00) >> 8);
cmd[10] = ((value & 0x00FF0000) >> 16);
cmd[11] = ((value & 0xFF000000) >> 24);
pr_debug("%s value=%08x begin\n", __func__, value);
mtksdio_packet_header[0] = sizeof(mtksdio_packet_header) + sizeof(cmd);
memcpy(txbuf, mtksdio_packet_header, MTK_SDIO_PACKET_HEADER_SIZE);
memcpy(txbuf + MTK_SDIO_PACKET_HEADER_SIZE, cmd, sizeof(cmd));
pr_debug("%s tx buf %02x%02x%02x%02x%02x\%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
__func__, txbuf[0], txbuf[1], txbuf[2], txbuf[3], txbuf[4],
txbuf[5], txbuf[6], txbuf[7], txbuf[8], txbuf[9],
txbuf[10], txbuf[11], txbuf[12], txbuf[13], txbuf[14], txbuf[15]);
btmtk_sdio_send_tx_data(txbuf, MTK_SDIO_PACKET_HEADER_SIZE + sizeof(cmd));
btmtk_sdio_recv_rx_data();
pr_debug("%s rx_length %d\n", __func__, rx_length);
pr_debug
("%s rx_length %d %02x%02x%02x%02x%02x\%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
__func__, rx_length, rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3], rxbuf[4]
, rxbuf[5], rxbuf[6], rxbuf[7], rxbuf[8], rxbuf[9]
, rxbuf[10], rxbuf[11], rxbuf[12], rxbuf[13], rxbuf[14]);
/* compare rx data is wmt reset correct response or not */
if (memcmp(event, rxbuf + MTK_SDIO_PACKET_HEADER_SIZE, sizeof(event)) != 0) {
ret = -EIO;
pr_err("%s: fail\n", __func__);
}
return ret;
}
static int btmtk_send_rom_patch(u8 *fwbuf, u32 fwlen, int mode)
{
int ret = 0;
u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
int stp_len = 0;
u8 mtkdata_header[MTKDATA_HEADER_SIZE] = {0};
int copy_len = 0;
int Datalen = fwlen;
u32 u32ReadCRValue = 0;
pr_debug("%s fwlen %d, mode = %d\n", __func__, fwlen, mode);
if (fwlen < Datalen) {
pr_err("%s file size = %d,is not corect\n", __func__, fwlen);
return -ENOENT;
}
stp_len = Datalen + MTKDATA_HEADER_SIZE;
mtkdata_header[0] = 0x2;/*ACL data*/
mtkdata_header[1] = 0x6F;
mtkdata_header[2] = 0xFC;
mtkdata_header[3] = ((Datalen+4+1)&0x00FF);
mtkdata_header[4] = ((Datalen+4+1)&0xFF00)>>8;
mtkdata_header[5] = 0x1;
mtkdata_header[6] = 0x1;
mtkdata_header[7] = ((Datalen+1)&0x00FF);
mtkdata_header[8] = ((Datalen+1)&0xFF00)>>8;
mtkdata_header[9] = mode;
/* 0 and 1 is packet length, include MTKSTP_HEADER_SIZE */
mtksdio_packet_header[0] =
(Datalen+4+MTKSTP_HEADER_SIZE+6)&0xFF;
mtksdio_packet_header[1] =
((Datalen+4+MTKSTP_HEADER_SIZE+6)&0xFF00)>>8;
mtksdio_packet_header[2] = 0;
mtksdio_packet_header[3] = 0;
/*
* mtksdio_packet_header[2] and mtksdio_packet_header[3]
* are reserved
*/
pr_debug("%s result %02x %02x\n", __func__,
((Datalen+4+MTKSTP_HEADER_SIZE+6)&0xFF00)>>8,
(Datalen+4+MTKSTP_HEADER_SIZE+6));
memcpy(txbuf+copy_len, mtksdio_packet_header,
MTK_SDIO_PACKET_HEADER_SIZE);
copy_len += MTK_SDIO_PACKET_HEADER_SIZE;
memcpy(txbuf+copy_len, mtkdata_header, MTKDATA_HEADER_SIZE);
copy_len += MTKDATA_HEADER_SIZE;
memcpy(txbuf+copy_len, fwbuf, Datalen);
copy_len += Datalen;
pr_debug("%s txbuf %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
__func__,
txbuf[0], txbuf[1], txbuf[2], txbuf[3], txbuf[4],
txbuf[5], txbuf[6], txbuf[7], txbuf[8], txbuf[9]);
ret = btmtk_sdio_readl(CHIER, &u32ReadCRValue);
pr_debug("%s: CHIER u32ReadCRValue %x, ret %d\n",
__func__, u32ReadCRValue, ret);
ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue);
pr_debug("%s: CHLPCR u32ReadCRValue %x, ret %d\n",
__func__, u32ReadCRValue, ret);
ret = btmtk_sdio_readl(CHISR, &u32ReadCRValue);
pr_debug("%s: 0CHISR u32ReadCRValue %x, ret %d\n",
__func__, u32ReadCRValue, ret);
ret = btmtk_sdio_readl(CHISR, &u32ReadCRValue);
pr_debug("%s: 00CHISR u32ReadCRValue %x, ret %d\n",
__func__, u32ReadCRValue, ret);
btmtk_sdio_send_tx_data(txbuf, copy_len);
ret = btmtk_sdio_recv_rx_data();
return ret;
}
/*
* type: cmd:1, ACL:2
* -------------------------------------------------
* mtksdio hedaer 4 byte| wmt header |
*
*
* data len should less than 512-4-4
*/
static int btmtk_sdio_send_wohci(u8 type, u32 len, u8 *data)
{
u32 ret = 0;
u32 push_in_data_len = 0;
u32 MultiBluckCount = 0;
u32 redundant = 0;
u8 mtk_wmt_header[MTKWMT_HEADER_SIZE] = {0};
u8 mtksdio_packet_header[MTK_SDIO_PACKET_HEADER_SIZE] = {0};
u8 mtk_tx_data[512] = {0};
mtk_wmt_header[0] = type;
mtk_wmt_header[1] = 0x6F;
mtk_wmt_header[2] = 0xFC;
mtk_wmt_header[3] = len;
mtksdio_packet_header[0] =
(len+MTKWMT_HEADER_SIZE+MTK_SDIO_PACKET_HEADER_SIZE)&0xFF;
mtksdio_packet_header[1] =
((len+MTKWMT_HEADER_SIZE+MTK_SDIO_PACKET_HEADER_SIZE)&0xFF00)
>>8;
mtksdio_packet_header[2] = 0;
mtksdio_packet_header[3] = 0;
/*
* mtksdio_packet_header[2] and mtksdio_packet_header[3]
* are reserved
*/
memcpy(mtk_tx_data, mtksdio_packet_header,
sizeof(mtksdio_packet_header));
push_in_data_len += sizeof(mtksdio_packet_header);
memcpy(mtk_tx_data+push_in_data_len, mtk_wmt_header,
sizeof(mtk_wmt_header));
push_in_data_len += sizeof(mtk_wmt_header);
memcpy(mtk_tx_data+push_in_data_len, data, len);
push_in_data_len += len;
memcpy(txbuf, mtk_tx_data, push_in_data_len);
MultiBluckCount = push_in_data_len/4;
redundant = push_in_data_len % 4;
if (redundant)
push_in_data_len = (MultiBluckCount+1)*4;
sdio_claim_host(g_card->func);
ret = sdio_writesb(g_card->func, CTDR, txbuf, push_in_data_len);
sdio_release_host(g_card->func);
pr_info("%s retrun 0x%0x\n", __func__, ret);
return ret;
}
/*
* data event:
* return
* 0:
* patch download is not complete/get patch semaphore fail
* 1:
* patch download is complete by others
* 2
* patch download is not coplete
* 3:(for debug)
* release patch semaphore success
*/
static int btmtk_sdio_need_load_rom_patch(void)
{
u32 ret = -1;
u8 cmd[] = {0x1, 0x17, 0x1, 0x0, 0x1};
u8 event[] = {0x2, 0x17, 0x1, 0x0};
do {
ret = btmtk_sdio_send_wohci(HCI_COMMAND_PKT, sizeof(cmd), cmd);
if (ret) {
pr_err("%s btmtk_sdio_send_wohci return fail ret %d\n",
__func__, ret);
break;
}
ret = btmtk_sdio_recv_rx_data();
if (ret)
break;
if (rx_length == 12) {
if (memcmp(rxbuf+7, event, sizeof(event)) == 0)
return rxbuf[11];
pr_err("%s receive event content is not correct, print receive data\n",
__func__);
btmtk_print_buffer_conent(rxbuf, rx_length);
}
} while (0);
pr_err("%s return ret %d\n", __func__, ret);
return ret;
}
static int btmtk_sdio_set_write_clear(void)
{
u32 u32ReadCRValue = 0;
u32 ret = 0;
ret = btmtk_sdio_readl(CHCR, &u32ReadCRValue);
if (ret) {
pr_err("%s read CHCR error\n", __func__);
ret = EINVAL;
return ret;
}
u32ReadCRValue |= 0x00000002;
btmtk_sdio_writel(CHCR, u32ReadCRValue);
pr_info("%s write CHCR 0x%08X\n", __func__, u32ReadCRValue);
ret = btmtk_sdio_readl(CHCR, &u32ReadCRValue);
pr_info("%s read CHCR 0x%08X\n", __func__, u32ReadCRValue);
if (u32ReadCRValue&0x00000002)
pr_info("%s write clear\n", __func__);
else
pr_info("%s read clear\n", __func__);
return ret;
}
static int btmtk_sdio_set_i2s(void)
{
int ret = 0;
u32 pinmux = 0;
ret = btmtk_sdio_set_i2s_slave();
if (ret) {
pr_err("btmtk_sdio_set_i2s_slave error(%d)\n", ret);
return ret;
}
ret = btmtk_sdio_read_pin_mux_setting(&pinmux);
if (ret) {
pr_err("btmtk_sdio_read_pin_mux_setting error(%d)\n", ret);
return ret;
}
pinmux &= 0x0000FFFF;
pinmux |= 0x22220000;
ret = btmtk_sdio_write_pin_mux_setting(pinmux);
if (ret) {
pr_err("btmtk_sdio_write_pin_mux_setting error(%d)\n", ret);
return ret;
}
pinmux = 0;
ret = btmtk_sdio_read_pin_mux_setting(&pinmux);
if (ret) {
pr_err("btmtk_sdio_read_pin_mux_setting error(%d)\n", ret);
return ret;
}
pr_info("confirm pinmux %04x\n", pinmux);
return ret;
}
static int btmtk_sdio_download_rom_patch(
struct btmtk_sdio_card *card)
{
const struct firmware *fw_firmware = NULL;
const u8 *firmware = NULL;
int firmwarelen, ret = 0;
void *tmpfwbuf = NULL;
u8 *fwbuf;
struct _PATCH_HEADER *patchHdr;
u8 *cDateTime = NULL;
u16 u2HwVer = 0;
u16 u2SwVer = 0;
u32 u4PatchVer = 0;
u32 uhwversion = 0;
u32 u32ReadCRValue = 0;
int RedundantSize = 0;
u32 bufferOffset = 0;
u8 patch_status = 0;
ret = btmtk_sdio_set_own_back(DRIVER_OWN);
if (ret)
return ret;
patch_status = btmtk_sdio_need_load_rom_patch();
pr_debug("%s patch_status %d\n", __func__, patch_status);
uhwversion = btmtk_sdio_bt_memRegister_read(HW_VERSION);
pr_info("%s uhwversion 0x%x\n", __func__, uhwversion);
if (uhwversion == 0x8A00) {
pr_info("%s request_firmware(firmware name %s)\n",
__func__, card->firmware);
ret = request_firmware(&fw_firmware, card->firmware,
&card->func->dev);
if ((ret < 0) || !fw_firmware) {
pr_err("request_firmware(firmware name %s) failed, error code = %d\n",
card->firmware,
ret);
ret = -ENOENT;
goto done;
}
} else {
pr_info("%s request_firmware(firmware name %s)\n",
__func__, card->firmware1);
ret = request_firmware(&fw_firmware,
card->firmware1,
&card->func->dev);
if ((ret < 0) || !fw_firmware) {
pr_err("request_firmware(firmware name %s) failed, error code = %d\n",
card->firmware1, ret);
ret = -ENOENT;
goto done;
}
}
memset(fw_version_str, 0, FW_VERSION_BUF_SIZE);
memcpy(fw_version_str, fw_firmware->data, FW_VERSION_BUF_SIZE - 1);
if (patch_status == PATCH_IS_DOWNLOAD_BT_OTHER ||
patch_status == PATCH_READY) {
pr_info("%s patch is ready no need load patch again\n",
__func__);
ret = btmtk_sdio_readl(0, &u32ReadCRValue);
pr_info("%s read chipid = %x\n", __func__, u32ReadCRValue);
/*Set interrupt output*/
ret = btmtk_sdio_writel(CHIER, FIRMWARE_INT|TX_FIFO_OVERFLOW |
FW_INT_IND_INDICATOR | TX_COMPLETE_COUNT |
TX_UNDER_THOLD | TX_EMPTY | RX_DONE);
if (ret) {
pr_err("Set interrupt output fail(%d)\n", ret);
ret = -EIO;
}
/*enable interrupt output*/
ret = btmtk_sdio_writel(CHLPCR, C_FW_INT_EN_SET);
if (ret) {
pr_err("enable interrupt output fail(%d)\n", ret);
ret = -EIO;
goto done;
}
ret = btmtk_sdio_bt_set_power(1);
if (ret)
return ret;
#if BTMTK_BIN_FILE_MODE
/* Send hci cmd before sleep */
btmtk_eeprom_bin_file(card);
#endif
ret = btmtk_sdio_set_sleep();
btmtk_sdio_set_write_clear();
release_firmware(fw_firmware);
return ret;
}
firmware = fw_firmware->data;
firmwarelen = fw_firmware->size;
pr_debug("Downloading FW image (%d bytes)\n", firmwarelen);
tmpfwbuf = kzalloc(firmwarelen, GFP_KERNEL);
if (!tmpfwbuf) {
ret = -ENOMEM;
goto done;
}
/* Ensure aligned firmware buffer */
memcpy(tmpfwbuf, firmware, firmwarelen);
fwbuf = tmpfwbuf;
/*Display rom patch info*/
patchHdr = (struct _PATCH_HEADER *)fwbuf;
cDateTime = patchHdr->ucDateTime;
u2HwVer = patchHdr->u2HwVer;
u2SwVer = patchHdr->u2SwVer;
u4PatchVer = patchHdr->u4PatchVer;
pr_debug("=====================================\n");
pr_info("===============Patch Info============\n");
pr_info("Built Time = %s\n", cDateTime);
pr_info("Hw Ver = 0x%x\n",
((u2HwVer & 0x00ff) << 8) | ((u2HwVer & 0xff00) >> 8));
pr_info("Sw Ver = 0x%x\n",
((u2SwVer & 0x00ff) << 8) | ((u2SwVer & 0xff00) >> 8));
pr_info("Patch Ver = 0x%04x\n",
((u4PatchVer & 0xff000000) >> 24) |
((u4PatchVer & 0x00ff0000) >> 16));
pr_info("Platform = %c%c%c%c\n",
patchHdr->ucPlatform[0],
patchHdr->ucPlatform[1],
patchHdr->ucPlatform[2],
patchHdr->ucPlatform[3]);
pr_info("Patch start addr = %02x\n", patchHdr->u2PatchStartAddr);
pr_info("=====================================\n");
fwbuf += sizeof(struct _PATCH_HEADER);
pr_debug("%s PATCH_HEADER size %zd\n",
__func__, sizeof(struct _PATCH_HEADER));
firmwarelen -= sizeof(struct _PATCH_HEADER);
ret = btmtk_sdio_readl(0, &u32ReadCRValue);
pr_info("%s read chipid = %x\n", __func__, u32ReadCRValue);
/*Set interrupt output*/
ret = btmtk_sdio_writel(CHIER, FIRMWARE_INT|TX_FIFO_OVERFLOW |
FW_INT_IND_INDICATOR | TX_COMPLETE_COUNT |
TX_UNDER_THOLD | TX_EMPTY | RX_DONE);
if (ret) {
pr_err("Set interrupt output fail(%d)\n", ret);
ret = -EIO;
goto done;
}
/*enable interrupt output*/
ret = btmtk_sdio_writel(CHLPCR, C_FW_INT_EN_SET);
if (ret) {
pr_err("enable interrupt output fail(%d)\n", ret);
ret = -EIO;
goto done;
}
RedundantSize = firmwarelen;
pr_debug("%s firmwarelen %d\n", __func__, firmwarelen);
do {
bufferOffset = firmwarelen - RedundantSize;
if (RedundantSize == firmwarelen &&
RedundantSize >= PATCH_DOWNLOAD_SIZE)
ret = btmtk_send_rom_patch(fwbuf+bufferOffset,
PATCH_DOWNLOAD_SIZE, SDIO_PATCH_DOWNLOAD_FIRST);
else if (RedundantSize == firmwarelen)
ret = btmtk_send_rom_patch(fwbuf+bufferOffset,
RedundantSize, SDIO_PATCH_DOWNLOAD_FIRST);
else if (RedundantSize < PATCH_DOWNLOAD_SIZE) {
ret = btmtk_send_rom_patch(fwbuf+bufferOffset,
RedundantSize, SDIO_PATCH_DOWNLOAD_END);
pr_debug("%s patch downoad last patch part\n",
__func__);
} else
ret = btmtk_send_rom_patch(fwbuf+bufferOffset,
PATCH_DOWNLOAD_SIZE, SDIO_PATCH_DOWNLOAD_CON);
RedundantSize -= PATCH_DOWNLOAD_SIZE;
if (ret) {
pr_err("%s btmtk_send_rom_patch fail\n", __func__);
goto done;
}
pr_debug("%s RedundantSize %d\n", __func__, RedundantSize);
if (RedundantSize <= 0) {
pr_debug("%s patch downoad finish\n", __func__);
break;
}
} while (1);
btmtk_sdio_set_write_clear();
if (btmtk_sdio_need_load_rom_patch() == PATCH_READY)
pr_info("%s patchdownload is done by BT\n", __func__);
ret = btmtk_sdio_send_wmt_reset();
if (ret)
goto done;
ret = btmtk_sdio_bt_set_power(1);
if (ret) {
ret = EINVAL;
goto done;
}
#if BTMTK_BIN_FILE_MODE
/* Send hci cmd before sleep */
btmtk_eeprom_bin_file(card);
#endif
ret = btmtk_sdio_set_sleep();
done:
kfree(tmpfwbuf);
release_firmware(fw_firmware);
if (!ret)
pr_info("%s success\n", __func__);
else
pr_info("%s fail\n", __func__);
return ret;
}
static void btmtk_sdio_for_code_style(void)
{
pr_info("%s vfs_fsync\n", __func__);
#if SAVE_FW_DUMP_IN_KERNEL
if (fw_dump_file)
vfs_fsync(fw_dump_file, 0);
#endif
if (fw_dump_file) {
pr_info("%s : close file %s\n",
__func__, fw_dump_file_name);
#if SAVE_FW_DUMP_IN_KERNEL
filp_close(fw_dump_file, NULL);
/* #endif */
fw_dump_file = NULL;
#endif
fw_is_doing_coredump = false;
fw_is_coredump_end_packet = true;
} else {
fw_is_doing_coredump = false;
pr_info("%s : fw_dump_file is NULL can't close file %s\n",
__func__, fw_dump_file_name);
}
}
static int btmtk_sdio_card_to_host(struct btmtk_private *priv, const u8 *event, const int event_len,
int add_spec_header)
/*event: check event which want to compare*/
/*return value: -x fail, 0 success*/
{
u16 buf_len = 0;
int ret = 0;
struct sk_buff *skb = NULL;
struct sk_buff *fops_skb = NULL;
struct sk_buff *fwlog_fops_skb = NULL;
u32 type;
u32 fourbalignment_len = 0;
u32 dump_len = 0;
char *core_dump_end = NULL;
int i = 0;
u8 reset_event[] = {0x0E, 0x04, 0x01, 0x03, 0x0C, 0x00};
static int print_dump_data_counter;
#if SUPPORT_FW_DUMP
fw_is_coredump_end_packet = false;
if (rx_length > (SDIO_HEADER_LEN+8)) {
if (rxbuf[SDIO_HEADER_LEN] == 0x80) {
dump_len = (rxbuf[SDIO_HEADER_LEN+1]&0x0F)*256
+ rxbuf[SDIO_HEADER_LEN+2];
pr_notice("%s get dump length %d\n",
__func__, dump_len);
if (print_dump_data_counter < PRINT_DUMP_COUNT) {
print_dump_data_counter++;
pr_warn("%s : dump %d %s\n",
__func__, print_dump_data_counter, &rxbuf[SDIO_HEADER_LEN+9]);
}
if (rxbuf[SDIO_HEADER_LEN+5] == 0x6F &&
rxbuf[SDIO_HEADER_LEN+6] == 0xFC) {
fw_is_doing_coredump = true;
#if SAVE_FW_DUMP_IN_KERNEL
if ((fw_dump_total_read_size == 0)
&& (fw_dump_file == NULL)) {
if (current_fwdump_file_number
== probe_counter)
goto FW_DONE;
/* #if SAVE_FW_DUMP_IN_KERNEL */
memset(fw_dump_file_name, 0,
sizeof(fw_dump_file_name));
snprintf(fw_dump_file_name,
sizeof(fw_dump_file_name),
FW_DUMP_FILE_NAME"_%d",
probe_counter);
pr_warn("%s : open file %s\n",
__func__,
fw_dump_file_name);
fw_dump_file = filp_open(
fw_dump_file_name,
O_RDWR | O_CREAT,
0644);
btmtk_sdio_set_no_fw_own(g_priv, TRUE);
print_dump_data_counter = 0;
if (!(IS_ERR(fw_dump_file))) {
current_fwdump_file_number =
probe_counter;
pr_warn("%s : open file %s success\n",
__func__,
fw_dump_file_name);
} else {
pr_warn("%s : open file %s fail\n",
__func__,
fw_dump_file_name);
fw_dump_file = NULL;
}
/* #endif */
}
#endif
fw_dump_total_read_size += dump_len;
#if SAVE_FW_DUMP_IN_KERNEL
if (fw_dump_file && fw_dump_file->f_op == NULL)
pr_warn("%s : fw_dump_file->f_op is NULL\n",
__func__);
if (fw_dump_file && fw_dump_file->f_op->write == NULL)
pr_warn("%s : fw_dump_file->f_op->write is NULL\n",
__func__);
if ((dump_len > 0) && fw_dump_file
&& fw_dump_file->f_op
&& fw_dump_file->f_op->write)
fw_dump_file->f_op->write(fw_dump_file,
&rxbuf[SDIO_HEADER_LEN+9],
dump_len,
&fw_dump_file->f_pos);
#endif
/* This is coredump data, save coredump data to picus_queue */
pr_info("%s : Receive coredump data, move data to fwlog queue for picus", __func__);
buf_len = rx_length-(MTK_SDIO_PACKET_HEADER_SIZE+1);
lock_unsleepable_lock(&(fwlog_metabuffer.spin_lock));
fwlog_fops_skb = bt_skb_alloc(buf_len, GFP_ATOMIC);
memcpy(&fwlog_fops_skb->data[0], &rxbuf[MTK_SDIO_PACKET_HEADER_SIZE+5], buf_len);
fwlog_fops_skb->len = buf_len;
skb_queue_tail(&g_priv->adapter->fwlog_fops_queue, fwlog_fops_skb);
pr_debug("%s fwlog_fops_skb length = %d, buf_len = %d\n",
__func__, fwlog_fops_skb->len, buf_len);
if (skb_queue_empty(&g_priv->adapter->fwlog_fops_queue))
pr_info("%s fwlog_fops_queue is empty empty\n", __func__);
kfree_skb(skb);
wake_up_interruptible(&fw_log_inq);
unlock_unsleepable_lock(&(fwlog_metabuffer.spin_lock));
#ifdef CONFIG_DEBUG_FS
if (dump_len >= sizeof(FW_DUMP_END_EVENT)) {
core_dump_end = strstr(
&rxbuf[SDIO_HEADER_LEN+10],
FW_DUMP_END_EVENT);
pr_warn("%s : core_dump_end %d\n",
__func__, SDIO_HEADER_LEN);
if (core_dump_end)
btmtk_sdio_for_code_style();
}
#endif
}
}
}
#endif
#if SAVE_FW_DUMP_IN_KERNEL
FW_DONE:
#endif
#if SUPPORT_BT_STEREO
if (rxbuf[SDIO_HEADER_LEN] == 0x04
&& rxbuf[SDIO_HEADER_LEN+1] == 0x0E
&& rxbuf[SDIO_HEADER_LEN+2] == 0x04
&& rxbuf[SDIO_HEADER_LEN+3] == 0x01
&& rxbuf[SDIO_HEADER_LEN+4] == 0x02
&& rxbuf[SDIO_HEADER_LEN+5] == 0xFD) {
pr_info("%s: This is btclk event, status:%02x\n",
__func__, rxbuf[SDIO_HEADER_LEN+6]);
buf_len = rx_length-(MTK_SDIO_PACKET_HEADER_SIZE+1);
goto exit;
}
/*receive BT clock data*/
if (rx_length >= (SDIO_HEADER_LEN+13)
&& rxbuf[SDIO_HEADER_LEN] == 0x04
&& rxbuf[SDIO_HEADER_LEN+1] == 0xFF
&& rxbuf[SDIO_HEADER_LEN+3] == 0x41) {
pr_debug("%s: This is btclk data - %d, %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
__func__, rx_length,
rxbuf[SDIO_HEADER_LEN+0], rxbuf[SDIO_HEADER_LEN+1], rxbuf[SDIO_HEADER_LEN+2],
rxbuf[SDIO_HEADER_LEN+3], rxbuf[SDIO_HEADER_LEN+4], rxbuf[SDIO_HEADER_LEN+5],
rxbuf[SDIO_HEADER_LEN+6], rxbuf[SDIO_HEADER_LEN+7], rxbuf[SDIO_HEADER_LEN+8],
rxbuf[SDIO_HEADER_LEN+9], rxbuf[SDIO_HEADER_LEN+10], rxbuf[SDIO_HEADER_LEN+11],
rxbuf[SDIO_HEADER_LEN+12], rxbuf[SDIO_HEADER_LEN+13], rxbuf[SDIO_HEADER_LEN+14],
rxbuf[SDIO_HEADER_LEN+15], rxbuf[SDIO_HEADER_LEN+16], rxbuf[SDIO_HEADER_LEN+17]);
if (rxbuf[SDIO_HEADER_LEN+12] == 0x0) {
u64 intra_clk = 0, clk = 0;
memcpy(&intra_clk, &rxbuf[SDIO_HEADER_LEN+6], 2);
memcpy(&clk, &rxbuf[SDIO_HEADER_LEN+8], 4);
stereo_clk.fw_clk = intra_clk + (clk&0x0FFFFFFC)*3125/10;
clk_flag |= 0x02;
pr_debug("%s: btclk intra:%lx, clk:%lx, fw_clk:%ld\n", __func__, intra_clk, clk, stereo_clk.fw_clk);
} else {
pr_warning("%s: No ACL CONNECTION(%d), disable event and interrupt\n", __func__, rxbuf[SDIO_HEADER_LEN+12]);
}
buf_len = rx_length-(MTK_SDIO_PACKET_HEADER_SIZE+1);
goto exit;
}
#endif
/*receive picus data to fwlog_queue*/
if (rx_length > (SDIO_HEADER_LEN+8)) {
dump_len = (rxbuf[SDIO_HEADER_LEN+1]&0x0F) * 256 + rxbuf[SDIO_HEADER_LEN+2];
pr_debug("%s This is debug log data, length = %d", __func__, dump_len);
if (rxbuf[SDIO_HEADER_LEN+1] == 0xFF && rxbuf[SDIO_HEADER_LEN+3] == 0x50) {
/* picus header is 0x50 */
pr_debug("%s : This is picus data", __func__);
buf_len = rx_length-(MTK_SDIO_PACKET_HEADER_SIZE+1);
lock_unsleepable_lock(&(fwlog_metabuffer.spin_lock));
fwlog_fops_skb = bt_skb_alloc(buf_len, GFP_ATOMIC);
memcpy(&fwlog_fops_skb->data[0], &rxbuf[MTK_SDIO_PACKET_HEADER_SIZE+1], buf_len);
fwlog_fops_skb->len = buf_len;
skb_queue_tail(&g_priv->adapter->fwlog_fops_queue, fwlog_fops_skb);
pr_debug("%s fwlog_fops_skb length = %d, buf_len = %d\n",
__func__, fwlog_fops_skb->len, buf_len);
if (skb_queue_empty(&g_priv->adapter->fwlog_fops_queue))
pr_warn("%s fwlog_fops_queue is empty empty\n", __func__);
kfree_skb(skb);
wake_up_interruptible(&fw_log_inq);
unlock_unsleepable_lock(&(fwlog_metabuffer.spin_lock));
goto exit;
}
}
type = rxbuf[MTK_SDIO_PACKET_HEADER_SIZE];
btmtk_print_buffer_conent(rxbuf, rx_length);
/* Read the length of data to be transferred , not include pkt type*/
buf_len = rx_length-(MTK_SDIO_PACKET_HEADER_SIZE+1);
pr_debug("buf_len : %d\n", buf_len);
if (rx_length <= SDIO_HEADER_LEN) {
pr_warn("invalid packet length: %d\n", buf_len);
ret = -EINVAL;
goto exit;
}
/* Allocate buffer */
/* rx_length = num_blocks * blksz + BTSDIO_DMA_ALIGN*/
skb = bt_skb_alloc(rx_length, GFP_ATOMIC);
if (skb == NULL) {
pr_warn("No free skb\n");
ret = -ENOMEM;
goto exit;
}
pr_debug("%s rx_length %d,buf_len %d\n", __func__, rx_length, buf_len);
memcpy(skb->data, &rxbuf[MTK_SDIO_PACKET_HEADER_SIZE+1], buf_len);
switch (type) {
case HCI_ACLDATA_PKT:
pr_debug("%s data[2] 0x%02x, data[3] 0x%02x\n",
__func__, skb->data[2], skb->data[3]);
buf_len = skb->data[2] + skb->data[3]*256 + 4;
pr_debug("%s acl buf_len %d\n", __func__, buf_len);
break;
case HCI_SCODATA_PKT:
buf_len = skb->data[3] + 3;
break;
case HCI_EVENT_PKT:
buf_len = skb->data[1] + 2;
break;
default:
BTSDIO_INFO_RAW(skb->data, (buf_len < 16 ? buf_len : 16), "%s: skb->data(type %d):", __func__, type);
/* trigger fw core dump */
btmtk_sdio_trigger_fw_assert();
ret = -EINVAL;
goto exit;
}
if ((g_priv->adapter->fops_mode == true) &&
memcmp(skb->data, reset_event, sizeof(reset_event)) == 0)
{
pr_debug("%s get reset complete event!\n", __func__);
need_set_i2s = 1;
}
if (event_compare_status == BTMTK_SDIO_EVENT_COMPARE_STATE_NEED_COMPARE)
BTSDIO_DEBUG_RAW(skb->data, buf_len, "%s: skb->data :", __func__);
if ((buf_len >= sizeof(READ_ADDRESS_EVENT))
&& (event_compare_status == BTMTK_SDIO_EVENT_COMPARE_STATE_NEED_COMPARE)) {
if ((memcmp(skb->data, READ_ADDRESS_EVENT, sizeof(READ_ADDRESS_EVENT)) == 0) && (buf_len == 12)) {
for (i = 0; i < BD_ADDRESS_SIZE; i++)
g_card->bdaddr[i] = skb->data[6 + i];
pr_debug("%s: GET TV BDADDR = %02X:%02X:%02X:%02X:%02X:%02X", __func__,
g_card->bdaddr[0], g_card->bdaddr[1], g_card->bdaddr[2],
g_card->bdaddr[3], g_card->bdaddr[4], g_card->bdaddr[5]);
/*
* event_compare_status =
* BTMTK_SDIO_EVENT_COMPARE_STATE_COMPARE_SUCCESS;
*/
} else
pr_debug("%s READ_ADDRESS_EVENT compare fail buf_len %d\n", __func__, buf_len);
}
if ((!fw_is_doing_coredump) &&
(!fw_is_coredump_end_packet)) {
if (event_compare_status == BTMTK_SDIO_EVENT_COMPARE_STATE_NEED_COMPARE) {
/*compare with tx hci cmd*/
pr_debug("%s buf_len %d, event_need_compare_len %d\n",
__func__, buf_len, event_need_compare_len);
if (buf_len >= event_need_compare_len) {
if (memcmp(skb->data, event_need_compare, event_need_compare_len) == 0) {
event_compare_status = BTMTK_SDIO_EVENT_COMPARE_STATE_COMPARE_SUCCESS;
pr_info("%s compare success and goto exit !\n", __func__);
/* for VTS loopback mode test, the command send by driver event should not report to stack */
kfree_skb(skb);
goto exit;
} else {
pr_debug("%s compare fail\n", __func__);
BTSDIO_DEBUG_RAW(event_need_compare, event_need_compare_len,
"%s: event_need_compare :", __func__);
}
}
}
fops_skb = bt_skb_alloc(buf_len, GFP_ATOMIC);
bt_cb(fops_skb)->pkt_type = type;
memcpy(fops_skb->data, skb->data, buf_len);
{
struct sk_buff *skb_hci = bt_skb_alloc(buf_len, GFP_KERNEL);
skb_put(skb_hci, buf_len);
memcpy(skb_hci->data, fops_skb->data, buf_len);
hci_skb_pkt_type(skb_hci) = type;
ret = hci_recv_frame(hdev, skb_hci);
if (ret < 0)
printk(KERN_ERR "XXX: error recv frame: %d\n", ret);
}
fops_skb->len = buf_len;
lock_unsleepable_lock(&(metabuffer.spin_lock));
skb_queue_tail(&g_priv->adapter->fops_queue, fops_skb);
if (skb_queue_empty(&g_priv->adapter->fops_queue))
pr_info("%s fops_queue is empty\n", __func__);
unlock_unsleepable_lock(&(metabuffer.spin_lock));
kfree_skb(skb);
wake_up_interruptible(&inq);
goto exit;
}
exit:
if (ret) {
pr_debug("%s fail free skb\n", __func__);
kfree_skb(skb);
}
buf_len += 1;
if (buf_len % 4)
fourbalignment_len = buf_len + 4 - (buf_len % 4);
else
fourbalignment_len = buf_len;
rx_length -= fourbalignment_len;
if (rx_length > (MTK_SDIO_PACKET_HEADER_SIZE)) {
memcpy(&rxbuf[MTK_SDIO_PACKET_HEADER_SIZE],
&rxbuf[MTK_SDIO_PACKET_HEADER_SIZE+fourbalignment_len],
rx_length-MTK_SDIO_PACKET_HEADER_SIZE);
}
pr_debug("%s ret %d, rx_length, %d,fourbalignment_len %d <--\n",
__func__, ret, rx_length, fourbalignment_len);
return ret;
}
static int btmtk_sdio_process_int_status(
struct btmtk_private *priv)
{
int ret = 0;
u32 u32rxdatacount = 0;
u32 u32ReadCRValue = 0;
ret = btmtk_sdio_readl(CHISR, &u32ReadCRValue);
pr_debug("%s CHISR 0x%08x\n", __func__, u32ReadCRValue);
if (u32ReadCRValue & FIRMWARE_INT_BIT15) {
btmtk_sdio_set_no_fw_own(g_priv, TRUE);
btmtk_sdio_writel(CHISR, FIRMWARE_INT_BIT15);
}
pr_debug("%s check TX_EMPTY CHISR 0x%08x\n", __func__, u32ReadCRValue);
if (TX_EMPTY&u32ReadCRValue) {
ret = btmtk_sdio_writel(CHISR, (TX_EMPTY | TX_COMPLETE_COUNT));
priv->btmtk_dev.tx_dnld_rdy = true;
pr_debug("%s set tx_dnld_rdy 1\n", __func__);
}
if (RX_DONE&u32ReadCRValue)
ret = btmtk_sdio_recv_rx_data();
if (ret == 0) {
lock_unsleepable_lock(&event_compare_status_lock);
while (rx_length > (MTK_SDIO_PACKET_HEADER_SIZE)) {
btmtk_sdio_card_to_host(priv, NULL, -1, 0);
u32rxdatacount++;
pr_debug("%s u32rxdatacount %d\n",
__func__, u32rxdatacount);
}
unlock_unsleepable_lock(&event_compare_status_lock);
}
btmtk_sdio_enable_interrupt(1);
return 0;
}
static void btmtk_sdio_interrupt(struct sdio_func *func)
{
struct btmtk_private *priv;
struct btmtk_sdio_card *card;
card = sdio_get_drvdata(func);
if (!card)
return;
if (!card->priv)
return;
priv = card->priv;
btmtk_sdio_enable_interrupt(0);
btmtk_interrupt(priv);
}
static int btmtk_sdio_register_dev(struct btmtk_sdio_card *card)
{
struct sdio_func *func;
u32 u32ReadCRValue = 0;
u8 reg;
int ret = 0;
if (!card || !card->func) {
pr_err("Error: card or function is NULL!\n");
ret = -EINVAL;
goto failed;
}
func = card->func;
sdio_claim_host(func);
ret = sdio_enable_func(func);
if (ret) {
pr_err("sdio_enable_func() failed: ret=%d\n", ret);
ret = -EIO;
goto release_host;
}
btmtk_sdio_readb(SDIO_CCCR_IENx, &u32ReadCRValue);
pr_info("before claim irq read SDIO_CCCR_IENx %x, func num %d\n",
u32ReadCRValue, func->num);
ret = sdio_claim_irq(func, btmtk_sdio_interrupt);
if (ret) {
pr_err("sdio_claim_irq failed: ret=%d\n", ret);
ret = -EIO;
goto disable_func;
}
pr_info("sdio_claim_irq success: ret=%d\n", ret);
btmtk_sdio_readb(SDIO_CCCR_IENx, &u32ReadCRValue);
pr_info("after claim irq read SDIO_CCCR_IENx %x\n", u32ReadCRValue);
ret = sdio_set_block_size(card->func, SDIO_BLOCK_SIZE);
if (ret) {
pr_err("cannot set SDIO block size\n");
ret = -EIO;
goto release_irq;
}
reg = sdio_readb(func, card->reg->io_port_0, &ret);
if (ret < 0) {
ret = -EIO;
goto release_irq;
}
card->ioport = reg;
reg = sdio_readb(func, card->reg->io_port_1, &ret);
if (ret < 0) {
ret = -EIO;
goto release_irq;
}
card->ioport |= (reg << 8);
reg = sdio_readb(func, card->reg->io_port_2, &ret);
if (ret < 0) {
ret = -EIO;
goto release_irq;
}
card->ioport |= (reg << 16);
pr_info("SDIO FUNC%d IO port: 0x%x\n", func->num, card->ioport);
if (card->reg->int_read_to_clear) {
reg = sdio_readb(func, card->reg->host_int_rsr, &ret);
if (ret < 0) {
ret = -EIO;
goto release_irq;
}
sdio_writeb(func, reg | 0x3f, card->reg->host_int_rsr, &ret);
if (ret < 0) {
ret = -EIO;
goto release_irq;
}
reg = sdio_readb(func, card->reg->card_misc_cfg, &ret);
if (ret < 0) {
ret = -EIO;
goto release_irq;
}
sdio_writeb(func, reg | 0x10, card->reg->card_misc_cfg, &ret);
if (ret < 0) {
ret = -EIO;
goto release_irq;
}
}
sdio_set_drvdata(func, card);
sdio_release_host(func);
return 0;
release_irq:
sdio_release_irq(func);
disable_func:
sdio_disable_func(func);
release_host:
sdio_release_host(func);
failed:
pr_info("%s fail\n", __func__);
return ret;
}
static int btmtk_sdio_unregister_dev(struct btmtk_sdio_card *card)
{
if (card && card->func) {
sdio_claim_host(card->func);
sdio_release_irq(card->func);
sdio_disable_func(card->func);
sdio_release_host(card->func);
sdio_set_drvdata(card->func, NULL);
}
return 0;
}
static int btmtk_sdio_enable_host_int(struct btmtk_sdio_card *card)
{
int ret;
u32 read_data = 0;
if (!card || !card->func)
return -EINVAL;
sdio_claim_host(card->func);
ret = btmtk_sdio_enable_host_int_mask(card, HIM_ENABLE);
btmtk_sdio_get_rx_unit(card);
if (0) {
typedef int (*fp_sdio_hook)(struct mmc_host *host,
unsigned int width);
fp_sdio_hook func_sdio_hook =
(fp_sdio_hook)kallsyms_lookup_name("mmc_set_bus_width");
unsigned char data = 0;
data = sdio_f0_readb(card->func, SDIO_CCCR_IF, &ret);
if (ret)
pr_info("%s sdio_f0_readb ret %d\n", __func__, ret);
pr_info("%s sdio_f0_readb data 0x%X!\n", __func__, data);
data &= ~SDIO_BUS_WIDTH_MASK;
data |= SDIO_BUS_ASYNC_INT;
card->func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
sdio_f0_writeb(card->func, data, SDIO_CCCR_IF, &ret);
if (ret)
pr_info("%s sdio_f0_writeb ret %d\n", __func__, ret);
pr_info("%s func_sdio_hook at 0x%p!\n",
__func__, func_sdio_hook);
if (func_sdio_hook)
func_sdio_hook(card->func->card->host, MMC_BUS_WIDTH_1);
data = sdio_f0_readb(card->func, SDIO_CCCR_IF, &ret);
if (ret)
pr_info("%s sdio_f0_readb 2 ret %d\n",
__func__, ret);
pr_info("%s sdio_f0_readb2 data 0x%X\n", __func__, data);
}
sdio_release_host(card->func);
/* workaround for some platform no host clock sometimes */
btmtk_sdio_readl(CSDIOCSR, &read_data);
pr_info("%s read CSDIOCSR is 0x%X\n", __func__, read_data);
read_data |= 0x4;
btmtk_sdio_writel(CSDIOCSR, read_data);
pr_info("%s write CSDIOCSR is 0x%X\n", __func__, read_data);
return ret;
}
static int btmtk_sdio_disable_host_int(struct btmtk_sdio_card *card)
{
int ret;
if (!card || !card->func)
return -EINVAL;
sdio_claim_host(card->func);
ret = btmtk_sdio_disable_host_int_mask(card, HIM_DISABLE);
sdio_release_host(card->func);
return ret;
}
static int btmtk_sdio_download_fw(struct btmtk_sdio_card *card)
{
int ret;
pr_info("%s begin\n", __func__);
if (!card || !card->func) {
pr_err("card or function is NULL!\n");
return -EINVAL;
}
sdio_claim_host(card->func);
if (btmtk_sdio_download_rom_patch(card)) {
pr_err("Failed to download firmware!\n");
ret = -EIO;
goto done;
}
/*
* winner or not, with this test the FW synchronizes
* when the
* module can continue its initialization
*/
sdio_release_host(card->func);
return 0;
done:
sdio_release_host(card->func);
return ret;
}
static int btmtk_sdio_push_data_to_metabuffer(
struct ring_buffer *metabuffer,
char *data,
int len,
u8 type,
bool use_type)
{
int remainLen = 0;
if (metabuffer->write_p >= metabuffer->read_p)
remainLen = metabuffer->write_p - metabuffer->read_p;
else
remainLen = META_BUFFER_SIZE -
(metabuffer->read_p - metabuffer->write_p);
if ((remainLen + 1 + len) >= META_BUFFER_SIZE) {
pr_warn("%s copy copyLen %d > META_BUFFER_SIZE(%d), push back to queue\n",
__func__,
(remainLen + 1 + len),
META_BUFFER_SIZE);
return -1;
}
if (use_type) {
metabuffer->buffer[metabuffer->write_p] = type;
metabuffer->write_p++;
}
if (metabuffer->write_p >= META_BUFFER_SIZE)
metabuffer->write_p = 0;
if (metabuffer->write_p + len <= META_BUFFER_SIZE)
memcpy(&metabuffer->buffer[metabuffer->write_p],
data,
len);
else {
memcpy(&metabuffer->buffer[metabuffer->write_p],
data,
META_BUFFER_SIZE - metabuffer->write_p);
memcpy(metabuffer->buffer,
&data[META_BUFFER_SIZE - metabuffer->write_p],
len - (META_BUFFER_SIZE - metabuffer->write_p));
}
metabuffer->write_p += len;
if (metabuffer->write_p >= META_BUFFER_SIZE)
metabuffer->write_p -= META_BUFFER_SIZE;
remainLen += (1 + len);
return 0;
}
static int btmtk_sdio_pull_data_from_metabuffer(
struct ring_buffer *metabuffer,
char __user *buf,
size_t count)
{
int copyLen = 0;
unsigned long ret = 0;
if (metabuffer->write_p >= metabuffer->read_p)
copyLen = metabuffer->write_p - metabuffer->read_p;
else
copyLen = META_BUFFER_SIZE -
(metabuffer->read_p - metabuffer->write_p);
if (copyLen > count)
copyLen = count;
if (metabuffer->read_p + copyLen <= META_BUFFER_SIZE)
ret = copy_to_user(buf,
&metabuffer->buffer[metabuffer->read_p],
copyLen);
else {
ret = copy_to_user(buf,
&metabuffer->buffer[metabuffer->read_p],
META_BUFFER_SIZE - metabuffer->read_p);
if (!ret)
ret = copy_to_user(
&buf[META_BUFFER_SIZE - metabuffer->read_p],
metabuffer->buffer,
copyLen - (META_BUFFER_SIZE-metabuffer->read_p));
}
if (ret)
pr_warn("%s copy to user fail, ret %d\n", __func__, (int)ret);
metabuffer->read_p += (copyLen - ret);
if (metabuffer->read_p >= META_BUFFER_SIZE)
metabuffer->read_p -= META_BUFFER_SIZE;
return (copyLen - ret);
}
#if SUPPORT_EINT
static irqreturn_t btmtk_sdio_woble_isr(int irq, void *dev)
{
struct btmtk_sdio_card *data = (struct btmtk_sdio_card *)dev;
unsigned long wait_powerkey_time = 0;
pr_info("%s begin\n", __func__);
disable_irq_nosync(data->wobt_irq);
atomic_dec(&(data->irq_enable_count));
pr_info("%s:disable BT IRQ\n", __func__);
pr_info("%s:call wake lock\n", __func__);
wait_powerkey_time = msecs_to_jiffies(WAIT_POWERKEY_TIMEOUT);
wake_lock_timeout(&data->eint_wlock, wait_powerkey_time);/*10s*/
pr_info("%s end\n", __func__);
return IRQ_HANDLED;
}
static int btmtk_sdio_RegisterBTIrq(struct btmtk_sdio_card *data)
{
struct device_node *eint_node = NULL;
int interrupts[2];
eint_node = of_find_compatible_node(NULL, NULL, "mediatek,mt7668_bt_ctrl");
pr_info("%s begin\n", __func__);
if (eint_node) {
pr_info("%s Get mt76xx_bt_ctrl compatible node\n", __func__);
data->wobt_irq = irq_of_parse_and_map(eint_node, 0);
pr_info("%s wobt_irq number:%d", __func__, data->wobt_irq);
if (data->wobt_irq) {
of_property_read_u32_array(eint_node, "interrupts",
interrupts, ARRAY_SIZE(interrupts));
data->wobt_irqlevel = interrupts[1];
if (request_irq(data->wobt_irq, btmtk_sdio_woble_isr,
data->wobt_irqlevel, "mt7668_bt_ctrl-eint", data))
pr_info("%s WOBTIRQ LINE NOT AVAILABLE!!\n", __func__);
else {
pr_info("%s disable BT IRQ\n", __func__);
disable_irq_nosync(data->wobt_irq);
}
} else
pr_info("%s can't find mt76xx_bt_ctrl irq\n", __func__);
} else {
data->wobt_irq = 0;
pr_info("%s can't find mt76xx_bt_ctrl compatible node\n", __func__);
}
pr_info("btmtk:%s: end\n", __func__);
return 0;
}
#endif
#if SUPPORT_BT_STEREO
static int btmtk_stereo_irq_handler(int irq, void *dev)
{
/* Get sys clk */
struct timeval tv;
do_gettimeofday(&tv);
stereo_clk.sys_clk = tv.tv_sec*1000000 + tv.tv_usec;
clk_flag = 0x01;
pr_debug("%s: tv_sec %d, tv_usec %d sys_clk %ld\n", __func__, tv.tv_sec, tv.tv_usec, stereo_clk.sys_clk);
return 0;
}
static int btmtk_stereo_reg_irq(void)
{
int ret;
struct device_node *node;
node = of_find_compatible_node(NULL, NULL, "mediatek,connectivity-combo");
if (node) {
stereo_irq = irq_of_parse_and_map(node, 1);
ret = request_irq(stereo_irq, (irq_handler_t) btmtk_stereo_irq_handler,
IRQF_TRIGGER_RISING, "BTSTEREO_ISR_Handler", NULL);
}
if (ret)
pr_err("%s fail(%d)!!! irq_number=%d\n", __func__, ret, stereo_irq);
return ret;
}
static void btmtk_stereo_unreg_irq(void)
{
free_irq(stereo_irq, NULL);
return;
}
#endif
static int btsdio_open(struct hci_dev *hdev)
{
return 0;
}
static int btsdio_close(struct hci_dev *hdev)
{
return 0;
}
static int btsdio_flush(struct hci_dev *hdev)
{
return 0;
}
static int btsdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
skb_queue_tail(&g_priv->adapter->tx_queue, skb);
wake_up_interruptible(&g_priv->main_thread.wait_q);
return 0;
}
static int btmtk_sdio_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
int ret = 0;
struct btmtk_private *priv = NULL;
struct btmtk_sdio_card *card = NULL;
struct btmtk_sdio_device *data = (void *) id->driver_data;
u32 u32ReadCRValue = 0;
probe_counter++;
pr_info("%s Mediatek Bluetooth driver Version=%s\n",
__func__, VERSION);
pr_info("vendor=0x%x, device=0x%x, class=%d, fn=%d, support func_num %d\n",
id->vendor, id->device, id->class,
func->num, data->reg->func_num);
if (func->num != data->reg->func_num) {
pr_info("func num is not match\n");
return -ENODEV;
}
card = devm_kzalloc(&func->dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
card->func = func;
g_card = card;
#if SUPPORT_EINT
btmtk_sdio_RegisterBTIrq(card);
#endif
if (id->driver_data) {
card->helper = data->helper;
card->firmware = data->firmware;
card->firmware1 = data->firmware1;
card->reg = data->reg;
card->sd_blksz_fw_dl = data->sd_blksz_fw_dl;
card->support_pscan_win_report = data->support_pscan_win_report;
card->supports_fw_dump = data->supports_fw_dump;
card->chip_id = data->reg->chip_id;
card->suspend_count = 0;
pr_info("%s chip_id is %x\n", __func__, data->reg->chip_id);
/*allocate memory for woble_setting_file*/
g_card->woble_setting_file_name = kzalloc(MAX_BIN_FILE_NAME_LEN, GFP_KERNEL);
if (!g_card->woble_setting_file_name)
return -1;
memcpy(g_card->woble_setting_file_name,
WOBLE_SETTING_FILE_NAME,
sizeof(WOBLE_SETTING_FILE_NAME));
}
pr_debug("%s func device %X\n", __func__, card->func->device);
pr_debug("%s Call btmtk_sdio_register_dev\n", __func__);
if (btmtk_sdio_register_dev(card) < 0) {
pr_err("Failed to register BT device!\n");
return -ENODEV;
}
pr_debug("%s btmtk_sdio_register_dev success\n", __func__);
/* Disable the interrupts on the card */
btmtk_sdio_enable_host_int(card);
pr_debug("call btmtk_sdio_enable_host_int done\n");
if (btmtk_sdio_download_fw(card)) {
pr_err("Downloading firmware failed!\n");
ret = -ENODEV;
goto unreg_dev;
}
btmtk_sdio_set_i2s();
/* Move from btmtk_fops_open() */
spin_lock_init(&(metabuffer.spin_lock.lock));
spin_lock_init(&(event_compare_status_lock.lock));
spin_lock_init(&(tx_function_lock.lock));
pr_debug("%s spin_lock_init end\n", __func__);
priv = btmtk_add_card(card);
if (!priv) {
pr_err("Initializing card failed!\n");
ret = -ENODEV;
goto unreg_dev;
}
pr_debug("btmtk_add_card success\n");
card->priv = priv;
pr_debug("assign priv done\n");
/* Initialize the interface specific function pointers */
priv->hw_host_to_card = btmtk_sdio_host_to_card;
priv->hw_process_int_status = btmtk_sdio_process_int_status;
priv->hw_set_own_back = btmtk_sdio_set_own_back;
g_priv = priv;
if (!fw_dump_ptr)
fw_dump_ptr = kmalloc(FW_DUMP_BUF_SIZE, GFP_ATOMIC);
if (!fw_dump_ptr) {
ret = -ENODEV;
return ret;
}
memset(&metabuffer.buffer, 0, META_BUFFER_SIZE);
memset(fw_dump_ptr, 0, FW_DUMP_BUF_SIZE);
fw_dump_task_should_stop = 0;
#if SAVE_FW_DUMP_IN_KERNEL
fw_dump_file = NULL;
#endif
fw_dump_read_ptr = fw_dump_ptr;
fw_dump_write_ptr = fw_dump_ptr;
fw_dump_total_read_size = 0;
fw_dump_total_write_size = 0;
fw_dump_buffer_used_size = 0;
fw_dump_buffer_full = 0;
ret = btmtk_sdio_readl(CHLPCR, &u32ReadCRValue);
pr_debug("%s read CHLPCR (0x%08X)\n", __func__, u32ReadCRValue);
pr_debug("%s chipid is (0x%X)\n", __func__, g_card->chip_id);
if (is_support_unify_woble(g_card)) {
memset(g_card->bdaddr, 0, BD_ADDRESS_SIZE);
btmtk_sdio_load_woble_setting(g_card->woble_setting,
g_card->woble_setting_file_name,
&g_card->func->dev,
&g_card->woble_setting_len,
g_card);
}
#if (SUPPORT_UNIFY_WOBLE & SUPPORT_ANDROID)
wake_lock_init(&g_card->woble_wlock, WAKE_LOCK_SUSPEND, "btevent_woble");
#endif
#if SUPPORT_EINT
wake_lock_init(&g_card->eint_wlock, WAKE_LOCK_SUSPEND, "btevent_eint");
#endif
#if SUPPORT_BT_STEREO
btmtk_stereo_reg_irq();
#endif
hdev = hci_alloc_dev();
if (!hdev)
return -ENOMEM;
hdev->bus = HCI_SDIO;
if (id->class == SDIO_CLASS_BT_AMP)
hdev->dev_type = HCI_AMP;
else
hdev->dev_type = HCI_PRIMARY;
SET_HCIDEV_DEV(hdev, &func->dev);
hdev->open = btsdio_open;
hdev->close = btsdio_close;
hdev->flush = btsdio_flush;
hdev->send = btsdio_send_frame;
ret = hci_register_dev(hdev);
if (ret < 0) {
pr_err("XXX: Failed to register HCI: %d\n", ret);
hci_free_dev(hdev);
return ret;
}
pr_info("%s normal end\n", __func__);
probe_ready = true;
return 0;
unreg_dev:
btmtk_sdio_unregister_dev(card);
pr_err("%s fail end\n", __func__);
return ret;
}
static void btmtk_sdio_remove(struct sdio_func *func)
{
struct btmtk_sdio_card *card;
int ret;
pr_info("%s begin user_rmmod %d\n", __func__, user_rmmod);
probe_ready = false;
hci_unregister_dev(hdev);
hci_free_dev(hdev);
#if SUPPORT_BT_STEREO
btmtk_stereo_unreg_irq();
#endif
if (func) {
card = sdio_get_drvdata(func);
if (card) {
/* Send SHUTDOWN command & disable interrupt
* if user removes the module.
*/
if (user_rmmod) {
pr_info("%s begin user_rmmod %d in user mode\n",
__func__, user_rmmod);
btmtk_sdio_set_own_back(DRIVER_OWN);
btmtk_sdio_enable_interrupt(0);
btmtk_sdio_bt_set_power(0);
btmtk_sdio_set_own_back(FW_OWN);
btmtk_sdio_disable_host_int(card);
}
btmtk_sdio_woble_free_setting();
pr_debug("unregester dev\n");
card->priv->surprise_removed = true;
btmtk_sdio_unregister_dev(card);
btmtk_remove_card(card->priv);
need_reset_stack = 1;
}
}
pr_info("%s end\n", __func__);
}
/*
* cmd_type:
* #define HCI_COMMAND_PKT 0x01
* #define HCI_ACLDATA_PKT 0x02
* #define HCI_SCODATA_PKT 0x03
* #define HCI_EVENT_PKT 0x04
* #define HCI_VENDOR_PKT 0xff
*/
static int btmtk_sdio_send_hci_cmd(u8 cmd_type, u8 *cmd, int cmd_len,
const u8 *event, const int event_len,
int total_timeout, bool wait_until)
/*cmd: if cmd is null, don't compare event, just return 0 if send cmd success*/
/* total_timeout: -1 */
/* add_spec_header:0 hci event, 1 use vend specic event header*/
/* return 0 if compare successfully and no need to compare */
/* return < 0 if error*/
/*wait_until: 0:need compare with first event after cmd*/
/*return value: 0 or positive success, -x fail*/
{
int ret = -1;
unsigned long comp_event_timo = 0, start_time = 0;
struct sk_buff *skb = NULL;
if (cmd_len == 0) {
pr_err("%s cmd_len (%d) error return\n", __func__, cmd_len);
return -EINVAL;
}
skb = bt_skb_alloc(cmd_len, GFP_ATOMIC);
bt_cb(skb)->pkt_type = cmd_type;
memcpy(&skb->data[0], cmd, cmd_len);
skb->len = cmd_len;
if (event) {
event_compare_status = BTMTK_SDIO_EVENT_COMPARE_STATE_NEED_COMPARE;
memcpy(event_need_compare, event, event_len);
event_need_compare_len = event_len;
}
skb_queue_tail(&g_priv->adapter->tx_queue, skb);
wake_up_interruptible(&g_priv->main_thread.wait_q);
if (event == NULL)
return 0;
if (event_len > EVENT_COMPARE_SIZE) {
pr_err("%s event_len (%d) > EVENT_COMPARE_SIZE(%d), error\n", __func__, event_len, EVENT_COMPARE_SIZE);
return -1;
}
start_time = jiffies;
/* check HCI event */
comp_event_timo = jiffies + msecs_to_jiffies(total_timeout);
ret = -1;
pr_debug("%s event_need_compare_len %d\n", __func__, event_need_compare_len);
pr_debug("%s event_compare_status %d\n", __func__, event_compare_status);
do {
/* check if event_compare_success */
if (event_compare_status == BTMTK_SDIO_EVENT_COMPARE_STATE_COMPARE_SUCCESS) {
pr_debug("%s compare success\n", __func__);
ret = 0;
break;
}
msleep(100);
} while (time_before(jiffies, comp_event_timo));
event_compare_status = BTMTK_SDIO_EVENT_COMPARE_STATE_NOTHING_NEED_COMPARE;
pr_debug("%s ret %d\n", __func__, ret);
return ret;
}
static int btmtk_sdio_trigger_fw_assert(void)
{
int ret = 0;
u8 cmd[] = { 0x5b, 0xfd, 0x00 };
pr_info("%s begin\n", __func__);
ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd,
sizeof(cmd),
NULL, 0, WOBLE_COMP_EVENT_TIMO, 1);
if (ret != 0)
pr_info("%s ret = %d \n", __func__, ret);
return ret;
}
static int btmtk_sdio_send_set_i2s_slave(void)
{
int ret = 0;
u8 comp_event[] = {0xE, 0x04, 0x01, 0x72, 0xFC, 0x00 };
u8 cmd[] = { 0x72, 0xFC, 4, 03, 0x10, 0x00, 0x02 };
ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd,
sizeof(cmd),
comp_event, sizeof(comp_event), WOBLE_COMP_EVENT_TIMO, 1);
if (ret != 0)
pr_info("%s ret = %d \n", __func__, ret);
return ret;
}
static int btmtk_sdio_send_get_vendor_cap(void)
{
int ret = -1;
u8 get_vendor_cap_cmd[] = { 0x53, 0xFD, 0x00 };
u8 get_vendor_cap_event[] = { 0x0e, 0x12, 0x01, 0x53, 0xFD, 0x00};
pr_debug("%s: begin", __func__);
BTSDIO_DEBUG_RAW(get_vendor_cap_cmd, sizeof(get_vendor_cap_cmd), "%s: send vendor_cap_cmd is:", __func__);
ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, get_vendor_cap_cmd, sizeof(get_vendor_cap_cmd),
get_vendor_cap_event, sizeof(get_vendor_cap_event),
WOBLE_COMP_EVENT_TIMO, 1);
pr_debug("%s: ret %d", __func__, ret);
return ret;
}
static int btmtk_sdio_send_read_BDADDR_cmd(void)
{
u8 cmd[] = { 0x09, 0x10, 0x00 };
int ret = -1;
unsigned char zero[BD_ADDRESS_SIZE];
pr_debug("%s: begin", __func__);
if (g_card == NULL) {
pr_err("%s: g_card == NULL!", __func__);
return -1;
}
memset(zero, 0, sizeof(zero));
if (memcmp(g_card->bdaddr, zero, BD_ADDRESS_SIZE) != 0) {
pr_debug("%s: already got bdaddr %02x%02x%02x%02x%02x%02x, return 0", __func__,
g_card->bdaddr[0], g_card->bdaddr[1], g_card->bdaddr[2],
g_card->bdaddr[3], g_card->bdaddr[4], g_card->bdaddr[5]);
return 0;
}
BTSDIO_DEBUG_RAW(cmd, sizeof(cmd), "%s: send read bd address cmd is:", __func__);
ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd, sizeof(cmd),
READ_ADDRESS_EVENT, sizeof(READ_ADDRESS_EVENT), WOBLE_COMP_EVENT_TIMO, 1);
/*BD address will get in btmtk_sdio_host_to_card*/
pr_debug("%s: ret = %d", __func__, ret);
return ret;
}
static int btmtk_sdio_set_Woble_APCF_filter_parameter(void)
{
int ret = -1;
u8 cmd[] = { 0x57, 0xfd, 0x0a, 0x01, 0x00, 0x5a, 0x20, 0x00, 0x20, 0x00, 0x01, 0x80, 0x00 };
u8 event_complete[] = { 0x0e, 0x07, 0x01, 0x57, 0xfd, 0x00, 0x01/*, 00, 63*/ };
pr_debug("%s: begin", __func__);
ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd, sizeof(cmd),
event_complete, sizeof(event_complete),
WOBLE_COMP_EVENT_TIMO, 1);
if (ret < 0)
pr_err("%s: end ret %d", __func__, ret);
else
ret = 0;
pr_info("%s: end ret=%d", __func__, ret);
return ret;
}
/**
* Set APCF manufacturer data and filter parameter
*/
static int btmtk_sdio_set_Woble_APCF(void)
{
int ret = -1;
int i = 0;
u8 manufactur_data[] = { 0x57, 0xfd, 0x27, 0x06, 0x00, 0x5a,
0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x43, 0x52, 0x4B, 0x54, 0x4D,
0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
u8 event_complete[] = { 0x0e, 0x07, 0x01, 0x57, 0xfd};
pr_debug("%s: begin", __func__);
if (!g_card) {
pr_info("%s: g_card is NULL, return -1", __func__);
return -1;
}
pr_debug("%s: g_card->woble_setting_apcf[0].length %d",
__func__, g_card->woble_setting_apcf[0].length);
/* start to send apcf cmd from woble setting file */
if (g_card->woble_setting_apcf[0].length) {
for (i = 0; i < WOBLE_SETTING_COUNT; i++) {
if (!g_card->woble_setting_apcf[i].length)
continue;
pr_info("%s: g_data->woble_setting_apcf_fill_mac[%d].content[0] = 0x%02x",
__func__, i,
g_card->woble_setting_apcf_fill_mac[i].content[0]);
pr_info("%s: g_data->woble_setting_apcf_fill_mac_location[%d].length = %d",
__func__, i,
g_card->woble_setting_apcf_fill_mac_location[i].length);
if ((g_card->woble_setting_apcf_fill_mac[i].content[0] == 1) &&
g_card->woble_setting_apcf_fill_mac_location[i].length) {
/* need add BD addr to apcf cmd */
memcpy(g_card->woble_setting_apcf[i].content +
(*g_card->woble_setting_apcf_fill_mac_location[i].content),
g_card->bdaddr, BD_ADDRESS_SIZE);
pr_info("%s: apcf %d ,add mac to location %d",
__func__, i,
(*g_card->woble_setting_apcf_fill_mac_location[i].content));
}
pr_info("%s: send APCF %d", __func__, i);
BTSDIO_INFO_RAW(g_card->woble_setting_apcf[i].content, g_card->woble_setting_apcf[i].length,
"woble_setting_apcf");
ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, g_card->woble_setting_apcf[i].content,
g_card->woble_setting_apcf[i].length,
event_complete, sizeof(event_complete), WOBLE_COMP_EVENT_TIMO, 1);
if (ret < 0) {
pr_err("%s: apcf %d error ret %d", __func__, i, ret);
return ret;
}
}
} else { /* use default */
pr_info("%s: use default manufactur data", __func__);
memcpy(manufactur_data + 9, g_card->bdaddr, BD_ADDRESS_SIZE);
BTSDIO_DEBUG_RAW(manufactur_data, sizeof(manufactur_data),
"send manufactur_data ");
ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, manufactur_data,
sizeof(manufactur_data),
event_complete, sizeof(event_complete), WOBLE_COMP_EVENT_TIMO, 1);
if (ret < 0) {
pr_err("%s: manufactur_data error ret %d", __func__, ret);
return ret;
}
ret = btmtk_sdio_set_Woble_APCF_filter_parameter();
}
pr_info("%s: end ret=%d", __func__, ret);
return ret;
}
static int btmtk_sdio_send_woble_settings(struct woble_setting_struct *settings_cmd,
struct woble_setting_struct *settings_event, char *message)
{
int ret = -1;
int i = 0;
pr_info("%s: %s length %d",
__func__, message, settings_cmd->length);
if (g_card->woble_setting_radio_on[0].length) {
for (i = 0; i < WOBLE_SETTING_COUNT; i++) {
if (settings_cmd[i].length) {
pr_info("%s: send %s %d", __func__, message, i);
BTSDIO_INFO_RAW(settings_cmd[i].content,
settings_cmd[i].length, "Raw");
ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, settings_cmd[i].content,
settings_cmd[i].length,
settings_event[i].content,
settings_event[i].length, WOBLE_COMP_EVENT_TIMO, 1);
if (ret) {
pr_err("%s: %s %d return error",
__func__, message, i);
return ret;
}
}
}
}
return ret;
}
static int btmtk_sdio_send_unify_woble_suspend_default_cmd(void)
{
int ret = 0; /* if successful, 0 */
u8 cmd[] = { 0xC9, 0xFC, 0x14, 0x01, 0x20, 0x02, 0x00, 0x01,
0x02, 0x01, 0x00, 0x05, 0x10, 0x01, 0x00, 0x40, 0x06,
0x02, 0x40, 0x5A, 0x02, 0x41, 0x0F };
/*u8 status[] = { 0x0F, 0x04, 0x00, 0x01, 0xC9, 0xFC };*/
u8 comp_event[] = { 0xE6, 0x02, 0x08, 0x00 };
pr_debug("%s: begin", __func__);
ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd,
sizeof(cmd),
comp_event, sizeof(comp_event), WOBLE_COMP_EVENT_TIMO, 1);
if (ret)
pr_err("%s: comp_event return error(%d)", __func__, ret);
return ret;
}
/**
* Set APCF manufacturer data and filter parameter
*/
static int btmtk_sdio_set_Woble_radio_off(void)
{
int ret = -1;
pr_debug("%s: g_data->woble_setting_radio_off[0].length %d",
__func__, g_card->woble_setting_radio_off[0].length);
pr_debug("%s: g_card->woble_setting_radio_off_comp_event[0].length %d",
__func__, g_card->woble_setting_radio_off_comp_event[0].length);
if (g_card->woble_setting_radio_off[0].length) {
if (g_card->woble_setting_radio_off_comp_event[0].length &&
is_support_unify_woble(g_card)) {
ret = btmtk_sdio_send_woble_settings(g_card->woble_setting_radio_off,
g_card->woble_setting_radio_off_comp_event, "radio off");
if (ret) {
pr_err("%s: radio off error", __func__);
return ret;
}
} else
pr_info("%s: woble_setting_radio_off length is %d", __func__,
g_card->woble_setting_radio_off[0].length);
} else {/* use default */
pr_info("%s: use default radio off cmd", __func__);
ret = btmtk_sdio_send_unify_woble_suspend_default_cmd();
}
pr_info("%s: end ret=%d", __func__, ret);
return ret;
}
static int btmtk_sdio_handle_entering_WoBLE_state(void)
{
int ret = -1;
pr_debug("%s: begin", __func__);
if (is_support_unify_woble(g_card)) {
ret = btmtk_sdio_send_get_vendor_cap();
if (ret < 0) {
pr_err("%s: btmtk_sdio_send_get_vendor_cap return fail ret = %d", __func__, ret);
goto Finish;
}
ret = btmtk_sdio_send_read_BDADDR_cmd();
if (ret < 0) {
pr_err("%s: btmtk_sdio_send_read_BDADDR_cmd return fail ret = %d", __func__, ret);
goto Finish;
}
ret = btmtk_sdio_set_Woble_APCF();
if (ret < 0) {
pr_err("%s: btmtk_sdio_set_Woble_APCF return fail %d", __func__, ret);
goto Finish;
}
ret = btmtk_sdio_set_Woble_radio_off();
if (ret < 0) {
pr_err("%s: btmtk_sdio_set_Woble_radio_off return fail %d", __func__, ret);
goto Finish;
}
}
Finish:
if (ret)
btmtk_sdio_woble_wake_lock(g_card);
pr_info("%s: end", __func__);
return ret;
}
static int btmtk_sdio_send_leave_woble_suspend_cmd(void)
{
int ret = 0; /* if successful, 0 */
u8 cmd[] = { 0xC9, 0xFC, 0x05, 0x01, 0x21, 0x02, 0x00, 0x00 };
u8 comp_event[] = { 0xe6, 0x02, 0x08, 0x01 };
BTSDIO_DEBUG_RAW(cmd, sizeof(cmd), "cmd ");
ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd, sizeof(cmd),
comp_event, sizeof(comp_event), WOBLE_COMP_EVENT_TIMO, 1);
if (ret < 0) {
pr_err("%s: failed(%d)", __func__, ret);
} else {
pr_info("%s: OK", __func__);
ret = 0;
}
return ret;
}
static int btmtk_sdio_del_Woble_APCF_inde(void)
{
int ret = -1;
u8 cmd[] = { 0x57, 0xfd, 0x03, 0x01, 0x01, 0x5a };
u8 event[] = { 0x0e, 0x07, 0x01, 0x57, 0xfd, 0x00, 0x01, /* 00, 63 */ };
pr_debug("%s", __func__);
ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd, sizeof(cmd),
event, sizeof(event), WOBLE_COMP_EVENT_TIMO, 1);
if (ret < 0)
pr_err("%s: Got error %d", __func__, ret);
pr_info("%s end ret = %d", __func__, ret);
return ret;
}
static int btmtk_sdio_handle_leaving_WoBLE_state(void)
{
int ret = -1;
if (g_card == NULL) {
pr_err("%s: g_card is NULL return", __func__);
return 0;
}
if (!is_support_unify_woble(g_card)) {
pr_err("%s: do nothing", __func__);
return 0;
}
if (g_card->woble_setting_radio_on[0].length &&
g_card->woble_setting_radio_on_comp_event[0].length &&
g_card->woble_setting_apcf_resume[0].length &&
g_card->woble_setting_apcf_resume_event[0].length) {
/* start to send radio off cmd from woble setting file */
ret = btmtk_sdio_send_woble_settings(g_card->woble_setting_radio_on,
g_card->woble_setting_radio_on_comp_event, "radio on");
if (ret) {
pr_err("%s: wradio on error", __func__);
return ret;
}
ret = btmtk_sdio_send_woble_settings(g_card->woble_setting_apcf_resume,
g_card->woble_setting_apcf_resume_event, "apcf resume");
if (ret) {
pr_err("%s: apcf resume error", __func__);
return ret;
}
} else { /* use default */
ret = btmtk_sdio_send_leave_woble_suspend_cmd();
if (ret) {
pr_err("%s: radio on error", __func__);
return ret;
}
ret = btmtk_sdio_del_Woble_APCF_inde();
if (ret) {
pr_err("%s: del apcf index error", __func__);
return ret;
}
}
if (ret) {
pr_err("%s: woble_setting_radio_on return error", __func__);
return ret;
}
return ret;
}
static int btmtk_sdio_send_apcf_reserved(void)
{
int ret = -1;
u8 reserve_apcf_cmd[] = { 0x5C, 0xFC, 0x01, 0x0A };
u8 reserve_apcf_event[] = { 0x0e, 0x06, 0x01, 0x5C, 0xFC, 0x00 };
ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, reserve_apcf_cmd,
sizeof(reserve_apcf_cmd),
reserve_apcf_event, sizeof(reserve_apcf_event), WOBLE_COMP_EVENT_TIMO, 1);
pr_info("%s: ret %d", __func__, ret);
return ret;
}
static int btmtk_sdio_suspend(struct device *dev)
{
struct sdio_func *func = dev_to_sdio_func(dev);
u8 ret = 0;
mmc_pm_flag_t pm_flags;
pr_info("%s begin\n", __func__);
if (g_card == NULL) {
pr_err("%s: g_card is NULL return", __func__);
return 0;
}
if ((g_card->suspend_count++)) {
pr_warn("%s: Has suspended. suspend_count: %d", __func__, g_card->suspend_count);
pr_info("%s: end", __func__);
return 0;
}
pr_debug("%s start to send DRIVER_OWN\n", __func__);
ret = btmtk_sdio_set_own_back(DRIVER_OWN);
if (ret)
pr_err("%s set driver own fail\n", __func__);
if (!is_support_unify_woble(g_card))
pr_warn("%s: no support", __func__);
else
btmtk_sdio_handle_entering_WoBLE_state();
#if SUPPORT_EINT
if (g_card->wobt_irq != 0 && atomic_read(&(g_card->irq_enable_count)) == 0) {
pr_info("%s:enable BT IRQ:%d\n", __func__, g_card->wobt_irq);
irq_set_irq_wake(g_card->wobt_irq, 1);
enable_irq(g_card->wobt_irq);
atomic_inc(&(g_card->irq_enable_count));
} else
pr_info("%s:irq_enable count:%d\n", __func__, atomic_read(&(g_card->irq_enable_count)));
#endif
if (func) {
pm_flags = sdio_get_host_pm_caps(func);
pr_debug("%s: suspend: PM flags = 0x%x\n",
sdio_func_id(func), pm_flags);
if (!(pm_flags & MMC_PM_KEEP_POWER)) {
pr_err("%s: cannot remain alive while suspended\n",
sdio_func_id(func));
return -EINVAL;
}
} else {
pr_err("sdio_func is not specified\n");
return 0;
}
ret = btmtk_sdio_set_own_back(FW_OWN);
if (ret)
pr_err("%s set fw own fail\n", __func__);
return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
}
static int btmtk_sdio_resume(struct device *dev)
{
u8 ret = 0;
if (g_card == NULL) {
pr_err("%s: g_card is NULL return", __func__);
return 0;
}
g_card->suspend_count--;
if (g_card->suspend_count) {
pr_info("%s: data->suspend_count %d, return 0", __func__, g_card->suspend_count);
return 0;
}
#if SUPPORT_EINT
if (g_card->wobt_irq != 0 && atomic_read(&(g_card->irq_enable_count)) == 1) {
pr_info("%s:disable BT IRQ:%d\n", __func__, g_card->wobt_irq);
atomic_dec(&(g_card->irq_enable_count));
disable_irq_nosync(g_card->wobt_irq);
} else
pr_info("%s:irq_enable count:%d\n", __func__, atomic_read(&(g_card->irq_enable_count)));
#endif
ret = btmtk_sdio_handle_leaving_WoBLE_state();
if (ret)
pr_err("%s: btmtk_sdio_handle_leaving_WoBLE_state return fail %d", __func__, ret);
pr_info("%s end\n", __func__);
return 0;
}
static const struct dev_pm_ops btmtk_sdio_pm_ops = {
.suspend = btmtk_sdio_suspend,
.resume = btmtk_sdio_resume,
};
static struct sdio_driver bt_mtk_sdio = {
.name = "btmtk_sdio",
.id_table = btmtk_sdio_ids,
.probe = btmtk_sdio_probe,
.remove = btmtk_sdio_remove,
.drv = {
.owner = THIS_MODULE,
.pm = &btmtk_sdio_pm_ops,
}
};
static int btmtk_sdio_send_hci_reset(void)
{
int ret = 0;
u8 comp_event[] = {0x0e, 0x04, 0x01, 0x03, 0x0c, 0x00 };
u8 cmd[] = { 0x03, 0x0c, 0x00 };
pr_info("%s begin\n", __func__);
ret = btmtk_sdio_send_hci_cmd(HCI_COMMAND_PKT, cmd,
sizeof(cmd),
comp_event, sizeof(comp_event), WOBLE_COMP_EVENT_TIMO, 1);
if (ret != 0)
pr_info("%s ret = %d \n", __func__, ret);
return ret;
}
static int btmtk_clean_queue(void)
{
struct sk_buff *skb = NULL;
lock_unsleepable_lock(&(metabuffer.spin_lock));
if (!skb_queue_empty(&g_priv->adapter->fops_queue)) {
pr_info("%s clean data in fops_queue\n", __func__);
do {
skb = skb_dequeue(&g_priv->adapter->fops_queue);
if (skb == NULL) {
pr_info("%s skb=NULL error break\n", __func__);
break;
}
kfree_skb(skb);
} while (!skb_queue_empty(&g_priv->adapter->fops_queue));
}
unlock_unsleepable_lock(&(metabuffer.spin_lock));
return 0;
}
static int btmtk_fops_open(struct inode *inode, struct file *file)
{
pr_info("%s begin\n", __func__);
if (!probe_ready) {
pr_err("%s probe_ready is %d return\n",
__func__, probe_ready);
return -EFAULT;
}
metabuffer.read_p = 0;
metabuffer.write_p = 0;
if (g_priv == NULL) {
pr_err("%s g_priv is NULL\n", __func__);
return -ENOENT;
}
if (g_priv->adapter == NULL) {
pr_err("%s g_priv->adapter is NULL\n", __func__);
return -ENOENT;
}
if (g_card) {
if (is_support_unify_woble(g_card))
btmtk_sdio_send_apcf_reserved();
} else
pr_err("%s g_card is NULL\n", __func__);
btmtk_clean_queue();
if (g_priv)
g_priv->adapter->fops_mode = true;
pr_info("%s fops_mode=%d end\n", __func__, g_priv->adapter->fops_mode);
return 0;
}
static int btmtk_fops_close(struct inode *inode, struct file *file)
{
pr_info("%s begin\n", __func__);
if (!probe_ready) {
pr_err("%s probe_ready is %d return\n",
__func__, probe_ready);
return -EFAULT;
}
if (g_priv)
g_priv->adapter->fops_mode = false;
/* for VTS loopback mode test */
btmtk_sdio_send_hci_reset();
btmtk_clean_queue();
pr_info("%s fops_mode=%d end\n", __func__, g_priv->adapter->fops_mode);
return 0;
}
ssize_t btmtk_fops_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
int retval = 0;
struct sk_buff *skb = NULL;
u32 crAddr = 0, crValue = 0, crMask = 0;
static u8 waiting_for_hci_without_packet_type; /* INITIALISED_STATIC: do not initialise statics to 0 */
static u8 hci_packet_type = 0xff;
u32 copy_size = 0;
unsigned long c_result = 0;
if (!probe_ready) {
pr_err("%s probe_ready is %d return\n",
__func__, probe_ready);
return -EFAULT;
}
if (g_priv == NULL) {
pr_info("%s g_priv is NULL\n", __func__);
return -EFAULT;
}
if (g_priv->adapter->fops_mode == 0) {
pr_info("%s fops_mode is 0\n", __func__);
return -EFAULT;
}
#if 0
pr_info("%s : (%d) %02X %02X %02X %02X "
%"02X %02X %02X %02X\n",
__func__, (int)count,
buf[0], buf[1], buf[2], buf[3],
buf[4], buf[5], buf[6], buf[7]);
pr_info("%s print write data", __func__);
if (count > 10)
pr_info(" %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
buf[0], buf[1], buf[2], buf[3], buf[4],
buf[5], buf[6], buf[7], buf[8], buf[9]);
else {
for (i = 0; i < count; i++)
pr_info("%d %02X", i, buf[i]);
}
#endif
memset(userbuf, 0, MTK_TXDATA_SIZE);
c_result = copy_from_user(userbuf, buf, count);
if (c_result != 0) {
pr_err("%s copy_from_user failed!\n", __func__);
return -EFAULT;
}
if (userbuf[0] == 0x7) {
/* write CR */
if (count < 15) {
pr_info("%s count=%zd less than 15, error\n",
__func__, count);
return -EFAULT;
}
crAddr = (userbuf[3]&0xff) + ((userbuf[4]&0xff)<<8)
+ ((userbuf[5]&0xff)<<16) + ((userbuf[6]&0xff)<<24);
crValue = (userbuf[7]&0xff) + ((userbuf[8]&0xff)<<8)
+ ((userbuf[9]&0xff)<<16) + ((userbuf[10]&0xff)<<24);
crMask = (userbuf[11]&0xff) + ((userbuf[12]&0xff)<<8)
+ ((userbuf[13]&0xff)<<16) + ((userbuf[14]&0xff)<<24);
pr_info("%s crAddr=0x%08x crValue=0x%08x crMask=0x%08x\n",
__func__, crAddr, crValue, crMask);
crValue &= crMask;
pr_info("%s write crAddr=0x%08x crValue=0x%08x\n", __func__,
crAddr, crValue);
btmtk_sdio_writel(crAddr, crValue);
retval = count;
} else if (userbuf[0] == 0x8) {
/* read CR */
if (count < 16) {
pr_info("%s count=%zd less than 15, error\n",
__func__, count);
return -EFAULT;
}
crAddr = (userbuf[3]&0xff) + ((userbuf[4]&0xff)<<8) +
((userbuf[5]&0xff)<<16) + ((userbuf[6]&0xff)<<24);
crMask = (userbuf[11]&0xff) + ((userbuf[12]&0xff)<<8) +
((userbuf[13]&0xff)<<16) + ((userbuf[14]&0xff)<<24);
btmtk_sdio_readl(crAddr, &crValue);
pr_info("%s read crAddr=0x%08x crValue=0x%08x crMask=0x%08x\n",
__func__, crAddr, crValue, crMask);
retval = count;
} else {
if (waiting_for_hci_without_packet_type == 1 && count == 1) {
pr_warn("%s: Waiting for hci_without_packet_type, but receive data count is 1!", __func__);
pr_warn("%s: Treat this packet as packet_type", __func__);
memcpy(&hci_packet_type, &userbuf[0], 1);
waiting_for_hci_without_packet_type = 1;
retval = 1;
goto OUT;
}
if (waiting_for_hci_without_packet_type == 0) {
if (count == 1) {
memcpy(&hci_packet_type, &userbuf[0], 1);
waiting_for_hci_without_packet_type = 1;
retval = 1;
goto OUT;
}
}
if (waiting_for_hci_without_packet_type) {
copy_size = count + 1;
skb = bt_skb_alloc(copy_size-1, GFP_ATOMIC);
bt_cb(skb)->pkt_type = hci_packet_type;
memcpy(&skb->data[0], &userbuf[0], copy_size-1);
} else {
copy_size = count;
skb = bt_skb_alloc(copy_size-1, GFP_ATOMIC);
bt_cb(skb)->pkt_type = userbuf[0];
memcpy(&skb->data[0], &userbuf[1], copy_size-1);
}
skb->len = copy_size-1;
skb_queue_tail(&g_priv->adapter->tx_queue, skb);
wake_up_interruptible(&g_priv->main_thread.wait_q);
retval = copy_size;
if (waiting_for_hci_without_packet_type) {
hci_packet_type = 0xff;
waiting_for_hci_without_packet_type = 0;
if (retval > 0)
retval -= 1;
}
}
OUT:
pr_debug("%s end\n", __func__);
return retval;
}
ssize_t btmtk_fops_read(struct file *filp, char __user *buf,
size_t count, loff_t *f_pos)
{
struct sk_buff *skb = NULL;
int copyLen = 0;
u8 hwerr_event[] = { 0x04, 0x10, 0x01, 0xff };
static int send_hw_err_event_count;
if (!probe_ready) {
pr_err("%s probe_ready is %d return\n",
__func__, probe_ready);
return -EFAULT;
}
if (g_priv == NULL) {
pr_info("%s g_priv is NULL\n", __func__);
return -EFAULT;
}
if (g_priv->adapter->fops_mode == 0) {
pr_info("%s fops_mode is 0\n", __func__);
return -EFAULT;
}
if (need_reset_stack == 1) {
pr_warn("%s: Reset BT stack", __func__);
pr_warn("%s: go if send_hw_err_event_count %d", __func__, send_hw_err_event_count);
if (send_hw_err_event_count < sizeof(hwerr_event)) {
if (count < (sizeof(hwerr_event) - send_hw_err_event_count)) {
copyLen = count;
pr_info("call wake_up_interruptible");
wake_up_interruptible(&inq);
} else
copyLen = (sizeof(hwerr_event) - send_hw_err_event_count);
pr_warn("%s: in if copyLen = %d", __func__, copyLen);
if (copy_to_user(buf, hwerr_event + send_hw_err_event_count, copyLen)) {
pr_err("send_hw_err_event_count %d copy to user fail, count = %d, go out",
send_hw_err_event_count, copyLen);
copyLen = -EFAULT;
goto OUT;
}
send_hw_err_event_count += copyLen;
pr_warn("%s: in if send_hw_err_event_count = %d", __func__, send_hw_err_event_count);
if (send_hw_err_event_count >= sizeof(hwerr_event)) {
send_hw_err_event_count = 0;
pr_warn("%s: set need_reset_stack=0", __func__);
need_reset_stack = 0;
}
pr_warn("%s: set call up", __func__);
goto OUT;
} else {
pr_warn("%s: xx set copyLen = -EFAULT", __func__);
copyLen = -EFAULT;
goto OUT;
}
}
if (need_set_i2s == 1) {
pr_info("%s get reset complete and set I2S !\n",__func__ );
btmtk_sdio_send_set_i2s_slave();
need_set_i2s = 0;
}
lock_unsleepable_lock(&(metabuffer.spin_lock));
if (skb_queue_empty(&g_priv->adapter->fops_queue)) {
/* if (filp->f_flags & O_NONBLOCK) { */
if (metabuffer.write_p == metabuffer.read_p) {
unlock_unsleepable_lock(&(metabuffer.spin_lock));
return 0;
}
}
if (need_reset_stack == 1) {
kill_fasync(&fasync, SIGIO, POLL_IN);
need_reset_stack = 0;
pr_info("%s Call kill_fasync and set reset_stack 0\n",
__func__);
return -ENODEV;
}
do {
skb = skb_dequeue(&g_priv->adapter->fops_queue);
if (skb == NULL) {
pr_debug("%s skb=NULL break\n", __func__);
break;
}
#if 0
pr_debug("%s pkt_type %d metabuffer.buffer %d",
__func__, bt_cb(skb)->pkt_type,
metabuffer.buffer[copyLen]);
#endif
btmtk_print_buffer_conent(skb->data, skb->len);
if (btmtk_sdio_push_data_to_metabuffer(&metabuffer, skb->data,
skb->len, bt_cb(skb)->pkt_type, true) < 0) {
skb_queue_head(&g_priv->adapter->fops_queue, skb);
break;
}
kfree_skb(skb);
} while (!skb_queue_empty(&g_priv->adapter->fops_queue));
unlock_unsleepable_lock(&(metabuffer.spin_lock));
return btmtk_sdio_pull_data_from_metabuffer(&metabuffer, buf, count);
OUT:
return copyLen;
}
static int btmtk_fops_fasync(int fd, struct file *file, int on)
{
pr_info("%s: fd = 0x%X, flag = 0x%X\n", __func__, fd, on);
return fasync_helper(fd, file, on, &fasync);
}
unsigned int btmtk_fops_poll(struct file *filp, poll_table *wait)
{
unsigned int mask = 0;
if (!probe_ready) {
pr_err("%s probe_ready is %d return\n",
__func__, probe_ready);
return mask;
}
if (g_priv == NULL) {
pr_err("%s g_priv is NULL\n", __func__);
return -ENODEV;
}
if (metabuffer.write_p != metabuffer.read_p)
mask |= (POLLIN | POLLRDNORM);
if (skb_queue_empty(&g_priv->adapter->fops_queue)) {
poll_wait(filp, &inq, wait);
if (!skb_queue_empty(&g_priv->adapter->fops_queue)) {
mask |= (POLLIN | POLLRDNORM);
/* pr_info("%s poll done\n", __func__); */
}
} else
mask |= (POLLIN | POLLRDNORM);
mask |= (POLLOUT | POLLWRNORM);
/* pr_info("%s poll mask 0x%x\n", __func__,mask); */
return mask;
}
static long btmtk_fops_unlocked_ioctl(struct file *filp,
unsigned int cmd, unsigned long arg)
{
u32 ret = 0;
int err = 0;
#if SUPPORT_BT_STEREO
int cnt = 0;
struct bt_stereo_para stereo_para;
struct sk_buff *skb = NULL;
u8 set_btclk[] = {0x01, 0x02, 0xFD, 0x0B,
0x00, 0x00, /* Handle */
0x00, /* Method */
/* bit0~3 - 0:CVSD remove,1:GPIO,2:In-band with transport*/
/* bit4~7 - 0:Shared memory,1:auto event */
0x00, 0x00, 0x00, 0x00, /* Period = value*0.625ms */
0x09, /* GPIO num - 0x01:BGF_INT_B,0x09:GPIO0 */
0x01, /* trigger mode - 0:Low,1:High */
0x00, 0x00}; /* active slots = value*0.625ms */
#endif
switch(cmd) {
#if SUPPORT_BT_STEREO
case BTMTK_IOCTL_STEREO_GET_CLK:
pr_debug("%s: BTMTK_IOCTL_STEREO_GET_CLK cmd\n", __func__);
for (cnt = 100; cnt > 0; cnt--) {
if (clk_flag == 0x3)
break;
msleep(1);
}
if (clk_flag != 0x3)
return -EAGAIN;
if (copy_to_user((struct bt_stereo_clk __user*)arg, &stereo_clk, sizeof(struct bt_stereo_clk)))
return -EBUSY;
break;
case BTMTK_IOCTL_STEREO_SET_PARA:
pr_debug("%s: BTMTK_IOCTL_STEREO_SET_PARA cmd\n", __func__);
if (copy_from_user(&stereo_para, (struct bt_stereo_para __user*)arg, sizeof(struct bt_stereo_para)))
return -EBUSY;
/* Send and check HCI cmd */
memcpy(&set_btclk[4], &stereo_para.handle, sizeof(stereo_para.handle));
memcpy(&set_btclk[6], &stereo_para.method, sizeof(stereo_para.method));
memcpy(&set_btclk[7], &stereo_para.period, sizeof(stereo_para.period));
memcpy(&set_btclk[13], &stereo_para.active_slots, sizeof(stereo_para.active_slots));
skb = bt_skb_alloc(sizeof(set_btclk)-1, GFP_ATOMIC);
bt_cb(skb)->pkt_type = set_btclk[0];
memcpy(&skb->data[0], &set_btclk[1], sizeof(set_btclk)-1);
skb->len = sizeof(set_btclk)-1;
skb_queue_tail(&g_priv->adapter->tx_queue, skb);
wake_up_interruptible(&g_priv->main_thread.wait_q);
break;
#endif
default:
return -EINVAL;
}
return ret;
}
static int btmtk_fops_openfwlog(struct inode *inode,
struct file *file)
{
if (g_priv == NULL) {
pr_err("%s: ERROR, g_data is NULL!\n", __func__);
return -ENODEV;
}
pr_info("%s: OK\n", __func__);
return 0;
}
static int btmtk_fops_closefwlog(struct inode *inode,
struct file *file)
{
if (g_priv == NULL) {
pr_err("%s: ERROR, g_data is NULL!\n", __func__);
return -ENODEV;
}
pr_info("%s: OK\n", __func__);
return 0;
}
static ssize_t btmtk_fops_readfwlog(struct file *filp,
char __user *buf,
size_t count,
loff_t *f_pos)
{
struct sk_buff *skb = NULL;
if (!probe_ready) {
pr_err("%s probe_ready is %d return\n",
__func__, probe_ready);
return -EFAULT;
}
if (g_priv == NULL) {
pr_info("%s g_priv is NULL\n", __func__);
return -EFAULT;
}
if (g_priv->adapter->fops_mode == 0) {
pr_info("%s fops_mode is 0\n", __func__);
return -EFAULT;
}
lock_unsleepable_lock(&(fwlog_metabuffer.spin_lock));
if (skb_queue_empty(&g_priv->adapter->fwlog_fops_queue)) {
/* if (filp->f_flags & O_NONBLOCK) { */
if (fwlog_metabuffer.write_p == fwlog_metabuffer.read_p) {
unlock_unsleepable_lock(&(fwlog_metabuffer.spin_lock));
return 0;
}
}
if (need_reset_stack == 1) {
kill_fasync(&fasync, SIGIO, POLL_IN);
need_reset_stack = 0;
pr_info("%s Call kill_fasync and set reset_stack 0\n",
__func__);
return -ENODEV;
}
do {
skb = skb_dequeue(&g_priv->adapter->fwlog_fops_queue);
if (skb == NULL) {
pr_debug("%s skb=NULL break\n", __func__);
break;
}
#if 0
pr_debug("%s pkt_type %d metabuffer.buffer %d",
__func__, bt_cb(skb)->pkt_type,
metabuffer.buffer[copyLen]);
#endif
btmtk_print_buffer_conent(skb->data, skb->len);
if (btmtk_sdio_push_data_to_metabuffer(&fwlog_metabuffer, skb->data,
skb->len, bt_cb(skb)->pkt_type, false) < 0) {
skb_queue_head(&g_priv->adapter->fwlog_fops_queue, skb);
break;
}
kfree_skb(skb);
} while (!skb_queue_empty(&g_priv->adapter->fwlog_fops_queue));
unlock_unsleepable_lock(&(fwlog_metabuffer.spin_lock));
return btmtk_sdio_pull_data_from_metabuffer(&fwlog_metabuffer, buf, count);
}
static ssize_t btmtk_fops_writefwlog(
struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
struct sk_buff *skb = NULL;
int length = 0, i, j = 0;
u8 *i_fwlog_buf = kmalloc(HCI_MAX_COMMAND_BUF_SIZE, GFP_KERNEL);
u8 *o_fwlog_buf = kmalloc(HCI_MAX_COMMAND_SIZE, GFP_KERNEL);
const char *val_param = NULL;
unsigned long c_result = 0;
memset(i_fwlog_buf, 0, HCI_MAX_COMMAND_BUF_SIZE);
memset(o_fwlog_buf, 0, HCI_MAX_COMMAND_SIZE);
memset(userbuf_fwlog, 0, MTK_TXDATA_SIZE);
c_result = copy_from_user(userbuf_fwlog, buf, count);
if (c_result != 0) {
pr_err("%s copy_from_user failed!\n", __func__);
return -EFAULT;
}
if (g_priv == NULL) {
pr_info("%s g_priv is NULL\n", __func__);
goto exit;
}
if (count > HCI_MAX_COMMAND_BUF_SIZE) {
pr_err("%s: your command is larger than maximum length, count = %zd\n",
__func__, count);
goto exit;
}
for (i = 0; i < count; i++) {
if (userbuf_fwlog[i] != '=') {
i_fwlog_buf[i] = userbuf_fwlog[i];
pr_debug("%s: tag_param %02x\n",
__func__, i_fwlog_buf[i]);
} else {
val_param = &userbuf_fwlog[i+1];
if (strcmp(i_fwlog_buf, "log_lvl") == 0) {
pr_info("%s: btmtk_log_lvl = %d\n",
__func__, val_param[0] - 48);
#if 0
btmtk_log_lvl = val_param[0] - 48;
#endif
}
goto exit;
}
}
if (i == count) {
/* hci input command format : */
/* echo 01 be fc 01 05 > /dev/stpbtfwlog */
/* We take the data from index three to end. */
val_param = &userbuf_fwlog[0];
}
length = strlen(val_param);
for (i = 0; i < length; i++) {
u8 ret = 0;
u8 temp_str[3] = { 0 };
long res = 0;
if (val_param[i] == ' ' || val_param[i] == '\t'
|| val_param[i] == '\r' || val_param[i] == '\n')
continue;
if ((val_param[i] == '0' && val_param[i + 1] == 'x')
|| (val_param[0] == '0' && val_param[i + 1] == 'X')) {
i++;
continue;
}
if (!(val_param[i] >= '0' && val_param[i] <= '9')
&& !(val_param[i] >= 'A' && val_param[i] <= 'F')
&& !(val_param[i] >= 'a' && val_param[i] <= 'f')) {
break;
}
temp_str[0] = *(val_param+i);
temp_str[1] = *(val_param+i+1);
ret = (u8) (kstrtol((char *)temp_str, 16, &res) & 0xff);
o_fwlog_buf[j++] = res;
i++;
}
length = j;
/*
* Receive command from stpbtfwlog, then Sent hci command
* to controller
*/
pr_debug("%s: hci buff is %02x%02x%02x%02x%02x\n",
__func__, o_fwlog_buf[0], o_fwlog_buf[1],
o_fwlog_buf[2], o_fwlog_buf[3], o_fwlog_buf[4]);
/* check HCI command length */
if (length > HCI_MAX_COMMAND_SIZE) {
pr_err("%s: your command is larger than maximum length, length = %d\n",
__func__, length);
goto exit;
}
pr_debug("%s: hci buff is %02x%02x%02x%02x%02x\n",
__func__, o_fwlog_buf[0], o_fwlog_buf[1],
o_fwlog_buf[2], o_fwlog_buf[3], o_fwlog_buf[4]);
/*
* Receive command from stpbtfwlog, then Sent hci command
* to Stack
*/
skb = bt_skb_alloc(length - 1, GFP_ATOMIC);
bt_cb(skb)->pkt_type = o_fwlog_buf[0];
memcpy(&skb->data[0], &o_fwlog_buf[1], length - 1);
skb->len = length - 1;
skb_queue_tail(&g_priv->adapter->tx_queue, skb);
wake_up_interruptible(&g_priv->main_thread.wait_q);
pr_info("%s write end\n", __func__);
exit:
pr_info("%s exit, length = %d\n", __func__, length);
kfree(i_fwlog_buf);
kfree(o_fwlog_buf);
return count;
}
static unsigned int btmtk_fops_pollfwlog(
struct file *file, poll_table *wait)
{
unsigned int mask = 0;
if (!probe_ready) {
pr_err("%s probe_ready is %d return\n",
__func__, probe_ready);
return mask;
}
if (g_priv == NULL) {
pr_err("%s g_priv is NULL\n", __func__);
return -ENODEV;
}
if (fwlog_metabuffer.write_p != fwlog_metabuffer.read_p)
mask |= (POLLIN | POLLRDNORM);
if (skb_queue_empty(&g_priv->adapter->fwlog_fops_queue)) {
poll_wait(file, &fw_log_inq, wait);
if (!skb_queue_empty(&g_priv->adapter->fwlog_fops_queue)) {
mask |= (POLLIN | POLLRDNORM);
/* pr_info("%s poll done\n", __func__); */
}
} else
mask |= (POLLIN | POLLRDNORM);
mask |= (POLLOUT | POLLWRNORM);
/* pr_info("%s poll mask 0x%x\n", __func__,mask); */
return mask;
}
static long btmtk_fops_unlocked_ioctlfwlog(
struct file *filp, unsigned int cmd,
unsigned long arg)
{
int retval = 0;
pr_info("%s: ->\n", __func__);
if (g_priv == NULL) {
pr_err("%s: ERROR, g_data is NULL!\n", __func__);
return -ENODEV;
}
return retval;
}
/*============================================================================*/
/* Interface Functions : Proc */
/*============================================================================*/
#define __________________________________________Interface_Function_for_Proc
static int btmtk_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "%s\n", fw_version_str);
return 0;
}
static int btmtk_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, btmtk_proc_show, NULL);
}
void btmtk_proc_create_new_entry(void)
{
struct proc_dir_entry *proc_show_entry;
pr_info("proc initialized");
g_proc_dir = proc_mkdir("stpbt", 0);
if (g_proc_dir == 0) {
pr_info("Unable to creat dir");
return;
}
proc_show_entry = proc_create("bt_fw_version", 0644, g_proc_dir, &BT_proc_fops);
}
static int BTMTK_major;
static int BT_majorfwlog;
static struct cdev BTMTK_cdev;
static struct cdev BT_cdevfwlog;
static int BTMTK_devs = 1;
static wait_queue_head_t inq;
const struct file_operations BTMTK_fops = {
.open = btmtk_fops_open,
.release = btmtk_fops_close,
.read = btmtk_fops_read,
.write = btmtk_fops_write,
.poll = btmtk_fops_poll,
.unlocked_ioctl = btmtk_fops_unlocked_ioctl,
.fasync = btmtk_fops_fasync
};
const struct file_operations BT_fopsfwlog = {
.open = btmtk_fops_openfwlog,
.release = btmtk_fops_closefwlog,
.read = btmtk_fops_readfwlog,
.write = btmtk_fops_writefwlog,
.poll = btmtk_fops_pollfwlog,
.unlocked_ioctl = btmtk_fops_unlocked_ioctlfwlog
};
static int BTMTK_init(void)
{
dev_t devID = MKDEV(BTMTK_major, 0);
dev_t devIDfwlog = MKDEV(BT_majorfwlog, 1);
int ret = 0;
int cdevErr = 0;
int major = 0;
int majorfwlog = 0;
pr_info("BTMTK_init\n");
g_card = NULL;
txbuf = NULL;
rxbuf = NULL;
userbuf = NULL;
rx_length = 0;
#if SAVE_FW_DUMP_IN_KERNEL
fw_dump_file = NULL;
#else
fw_dump_file = 0;
#endif
g_priv = NULL;
fw_dump_buffer_full = 0;
fw_dump_total_read_size = 0;
fw_dump_total_write_size = 0;
fw_dump_buffer_used_size = 0;
fw_dump_task_should_stop = 0;
fw_dump_ptr = NULL;
fw_dump_read_ptr = NULL;
fw_dump_write_ptr = NULL;
probe_counter = 0;
fw_dump_end_checking_task_should_stop = 0;
fw_is_doing_coredump = 0;
btmtk_proc_create_new_entry();
ret = alloc_chrdev_region(&devID, 0, 1, "BT_chrdev");
if (ret) {
pr_err("fail to allocate chrdev\n");
return ret;
}
ret = alloc_chrdev_region(&devIDfwlog, 0, 1, "BT_chrdevfwlog");
if (ret) {
pr_err("fail to allocate chrdev\n");
return ret;
}
BTMTK_major = major = MAJOR(devID);
pr_info("major number:%d\n", BTMTK_major);
BT_majorfwlog = majorfwlog = MAJOR(devIDfwlog);
pr_info("BT_majorfwlog number: %d\n", BT_majorfwlog);
cdev_init(&BTMTK_cdev, &BTMTK_fops);
BTMTK_cdev.owner = THIS_MODULE;
cdev_init(&BT_cdevfwlog, &BT_fopsfwlog);
BT_cdevfwlog.owner = THIS_MODULE;
cdevErr = cdev_add(&BTMTK_cdev, devID, BTMTK_devs);
if (cdevErr)
goto error;
cdevErr = cdev_add(&BT_cdevfwlog, devIDfwlog, 1);
if (cdevErr)
goto error;
pr_info("%s driver(major %d) installed.\n",
"BT_chrdev", BTMTK_major);
pr_info("%s driver(major %d) installed.\n",
"BT_chrdevfwlog", BT_majorfwlog);
pBTClass = class_create(THIS_MODULE, "BT_chrdev");
if (IS_ERR(pBTClass)) {
pr_err("class create fail, error code(%ld)\n",
PTR_ERR(pBTClass));
goto err1;
}
pBTDev = device_create(pBTClass, NULL, devID, NULL, BT_NODE);
if (IS_ERR(pBTDev)) {
pr_err("device create fail, error code(%ld)\n",
PTR_ERR(pBTDev));
goto err2;
}
pBTDevfwlog = device_create(pBTClass, NULL,
devIDfwlog, NULL, "stpbtfwlog");
if (IS_ERR(pBTDevfwlog)) {
pr_err("device(stpbtfwlog) create fail, error code(%ld)\n",
PTR_ERR(pBTDevfwlog));
goto err2;
}
pr_info("%s: BT_major %d, BT_majorfwlog %d\n",
__func__, BTMTK_major, BT_majorfwlog);
pr_info("%s: devID %d, devIDfwlog %d\n", __func__, devID, devIDfwlog);
/* init wait queue */
g_devIDfwlog = devIDfwlog;
init_waitqueue_head(&(fw_log_inq));
init_waitqueue_head(&(inq));
return 0;
err2:
if (pBTClass) {
class_destroy(pBTClass);
pBTClass = NULL;
}
err1:
error:
if (cdevErr == 0)
cdev_del(&BTMTK_cdev);
if (ret == 0)
unregister_chrdev_region(devID, BTMTK_devs);
return -1;
}
static void BTMTK_exit(void)
{
dev_t dev = MKDEV(BTMTK_major, 0);
dev_t devIDfwlog = g_devIDfwlog;
pr_info("%s\n", __func__);
if (g_proc_dir != 0) {
g_proc_dir = 0;
remove_proc_entry("bt_fw_version", g_proc_dir);
remove_proc_entry("stpbt", NULL);
pr_info("proc device node and folder removed!!");
return;
}
if (g_card) {
pr_info("%s 1\n", __func__);
if (g_card->woble_setting_file_name) {
pr_info("%s 2\n", __func__);
kfree(g_card->woble_setting_file_name);
pr_info("%s 3\n", __func__);
pr_info("free woble_setting_file_name done\n");
} else {
pr_err("%s no free woble_setting_file_name, g_card is not null\n", __func__);
pr_err("%s woble_setting_file_name is null\n", __func__);
}
} else
pr_err("no free woble_setting_file_name, g_card is null\n");
#if (SUPPORT_UNIFY_WOBLE & SUPPORT_ANDROID)
pr_info("%s 4\n", __func__);
if (g_card)
wake_lock_destroy(&g_card->woble_wlock);
else
pr_err("%s:g_data is NULL, no destroy woble_wlock", __func__);
#endif
pr_info("%s 5\n", __func__);
if (pBTDevfwlog) {
pr_info("%s 6\n", __func__);
device_destroy(pBTClass, devIDfwlog);
pBTDevfwlog = NULL;
}
pr_info("%s 7\n", __func__);
if (pBTDev) {
device_destroy(pBTClass, dev);
pBTDev = NULL;
}
pr_info("%s 8\n", __func__);
if (pBTClass) {
class_destroy(pBTClass);
pBTClass = NULL;
}
pr_info("%s 9\n", __func__);
cdev_del(&BTMTK_cdev);
pr_info("%s 10\n", __func__);
unregister_chrdev_region(dev, 1);
cdev_del(&BT_cdevfwlog);
unregister_chrdev_region(devIDfwlog, 1);
pr_info("%s driver removed.\n", BT_DRIVER_NAME);
}
static int btmtk_sdio_allocate_memory(void)
{
if (txbuf == NULL) {
txbuf = kmalloc(MTK_TXDATA_SIZE, GFP_ATOMIC);
memset(txbuf, 0, MTK_TXDATA_SIZE);
}
if (rxbuf == NULL) {
rxbuf = kmalloc(MTK_RXDATA_SIZE, GFP_ATOMIC);
memset(rxbuf, 0, MTK_RXDATA_SIZE);
}
if (userbuf == NULL) {
userbuf = kmalloc(MTK_TXDATA_SIZE, GFP_ATOMIC);
memset(userbuf, 0, MTK_TXDATA_SIZE);
}
if (userbuf_fwlog == NULL) {
userbuf_fwlog = kmalloc(MTK_TXDATA_SIZE, GFP_ATOMIC);
memset(userbuf_fwlog, 0, MTK_TXDATA_SIZE);
}
return 0;
}
static int btmtk_sdio_free_memory(void)
{
kfree(txbuf);
kfree(rxbuf);
kfree(userbuf);
kfree(userbuf_fwlog);
kfree(fw_dump_ptr);
return 0;
}
static int __init btmtk_sdio_init_module(void)
{
BTMTK_init();
if (btmtk_sdio_allocate_memory() < 0) {
pr_err("%s: allocate memory failed!", __func__);
return -ENOMEM;
}
if (sdio_register_driver(&bt_mtk_sdio) != 0) {
pr_err("SDIO Driver Registration Failed\n");
return -ENODEV;
}
#ifdef BT_SUPPORT_PMU_EN_CTRL
/* Pull PMU_EN to high */
if (!cnnPmuStatusGet()) {
cnnPmuPullUp();
}
#endif
#ifdef BT_DRIVER_BUILD_MODULE
/* Trigger SDIO BUS Rescan in ko module */
sdio_card_detect(1);
#endif
pr_info("SDIO Driver Registration Success\n");
/* Clear the flag in case user removes the card. */
user_rmmod = 0;
return 0;
}
static void __exit btmtk_sdio_exit_module(void)
{
/* Set the flag as user is removing this module. */
user_rmmod = 1;
BTMTK_exit();
sdio_unregister_driver(&bt_mtk_sdio);
btmtk_sdio_free_memory();
}
module_init(btmtk_sdio_init_module);
module_exit(btmtk_sdio_exit_module);