M7350/external/compat-wireless/drivers/net/wireless/ath/ath6kl-3.5/wmi_btcoex.c

350 lines
10 KiB
C
Raw Normal View History

2024-09-09 08:57:42 +00:00
/*
* Copyright (c) 2004-2011 Atheros Communications Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/ip.h>
#include "core.h"
#include "wmi_btcoex.h"
#include "debug.h"
#ifdef CONFIG_ANDROID
#include <mach/socinfo.h>
#endif
#define BDATA_ANTCONF_OFFSET 4069
#define BDATA_BTDEV_OFFSET 4070
#define BT_DEVICE_TYPE_QCOM 2
#define WMI_A2DP_CONFIG_FLAG_ALLOW_OPTIMIZATION (1 << 0)
#define WMI_A2DP_CONFIG_FLAG_IS_EDR_CAPABLE (1 << 1)
#define WMI_A2DP_CONFIG_FLAG_IS_BT_ROLE_MASTER (1 << 2)
#define WMI_A2DP_CONFIG_FLAG_IS_A2DP_HIGH_PRI (1 << 3)
#define WMI_A2DP_CONFIG_FLAG_FIND_BT_ROLE (1 << 4)
#define WMI_A2DP_CONFIG_FLAG_DIS_SCANCONN_STOMP (1 << 5)
#define BTCOEX_A2DP_MAX_BLUETOOTH_TIME_LSB 24
#define BTCOEX_A2DP_EDR_MAX_BLUETOOTH_TIME \
(40 << BTCOEX_A2DP_MAX_BLUETOOTH_TIME_LSB)
#define BTCOEX_A2DP_BDR_MAX_BLUETOOTH_TIME \
(70 << BTCOEX_A2DP_MAX_BLUETOOTH_TIME_LSB)
#define BTCOEX_APMODE_A2DP_MAX_BLUETOOTH_TIME \
(30 << BTCOEX_A2DP_MAX_BLUETOOTH_TIME_LSB)
#define BTCOEX_APMODE_A2DP_BDR_MAX_BLUETOOTH_TIME \
(60 << BTCOEX_A2DP_MAX_BLUETOOTH_TIME_LSB)
#define BTCOEX_A2DP_WLAN_MAX_DUR 25
#define BTCOEX_A2DP_BDR_WLAN_MAX_DUR 20
#define BTCOEX_APMODE_A2DP_BDR_WLAN_MAX_DUR 40
#define BTCOEX_APMODE_A2DP_WLAN_MAX_DUR 70
#define BTCOEX_APMODE_A2DP_MIN_BURST_CNT 10
#define WMI_SCO_CONFIG_FLAG_ALLOW_OPTIMIZATION (1 << 0)
#define WMI_SCO_CONFIG_FLAG_IS_EDR_CAPABLE (1 << 1)
#define WMI_SCO_CONFIG_FLAG_IS_BT_MASTER (1 << 2)
#define WMI_SCO_CONFIG_FLAG_FW_DETECT_OF_PER (1 << 3)
#define WMI_SCO_CONFIG_FLAG_DIS_SCANCONN_STOMP (1 << 4)
static inline struct sk_buff *ath6kl_wmi_btcoex_get_new_buf(u32 size)
{
struct sk_buff *skb;
skb = ath6kl_buf_alloc(size);
if (!skb)
return NULL;
skb_put(skb, size);
if (size)
memset(skb->data, 0, size);
return skb;
}
/* This is a temporary WAR since BTC related NL80211 commands are
* defined in Linux nl80211.h.
*/
#define NL80211_WMI_SET_BT_STATUS 0
#define NL80211_WMI_SET_BT_PARAMS 1
#define NL80211_WMI_SET_BT_FT_ANT 2
#define NL80211_WMI_SET_COLOCATED_BT_DEV 3
#define NL80211_WMI_SET_BT_INQUIRY_PAGE_CONFIG 4
#define NL80211_WMI_SET_BT_SCO_CONFIG 5
#define NL80211_WMI_SET_BT_A2DP_CONFIG 6
#define NL80211_WMI_SET_BT_ACLCOEX_CONFIG 7
#define NL80211_WMI_SET_BT_DEBUG 8
#define NL80211_WMI_SET_BT_OPSTATUS 9
#define NL80211_WMI_GET_BT_CONFIG 10
#define NL80211_WMI_GET_BT_STATS 11
#define NL80211_WMI_SET_BT_HID_CONFIG 12
static int ath6kl_get_wmi_cmd(int nl_cmd)
{
int wmi_cmd = 0;
switch (nl_cmd) {
case NL80211_WMI_SET_BT_STATUS:
ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT status\n");
wmi_cmd = WMI_SET_BT_STATUS_CMDID;
break;
case NL80211_WMI_SET_BT_PARAMS:
ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT params\n");
wmi_cmd = WMI_SET_BT_PARAMS_CMDID;
break;
case NL80211_WMI_SET_BT_FT_ANT:
ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT FT antenna\n");
wmi_cmd = WMI_SET_BTCOEX_FE_ANT_CMDID;
break;
case NL80211_WMI_SET_COLOCATED_BT_DEV:
ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT collocated dev\n");
wmi_cmd = WMI_SET_BTCOEX_COLOCATED_BT_DEV_CMDID;
break;
case NL80211_WMI_SET_BT_INQUIRY_PAGE_CONFIG:
ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT inquiry page\n");
wmi_cmd = WMI_SET_BTCOEX_BTINQUIRY_PAGE_CONFIG_CMDID;
break;
case NL80211_WMI_SET_BT_SCO_CONFIG:
ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT sco config\n");
wmi_cmd = WMI_SET_BTCOEX_SCO_CONFIG_CMDID;
break;
case NL80211_WMI_SET_BT_A2DP_CONFIG:
ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT a2dp config\n");
wmi_cmd = WMI_SET_BTCOEX_A2DP_CONFIG_CMDID;
break;
case NL80211_WMI_SET_BT_ACLCOEX_CONFIG:
ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT acl config\n");
wmi_cmd = WMI_SET_BTCOEX_ACLCOEX_CONFIG_CMDID;
break;
case NL80211_WMI_SET_BT_DEBUG:
ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT bt debug\n");
wmi_cmd = WMI_SET_BTCOEX_DEBUG_CMDID;
break;
case NL80211_WMI_SET_BT_OPSTATUS:
ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT op status\n");
wmi_cmd = WMI_SET_BTCOEX_BT_OPERATING_STATUS_CMDID;
break;
case NL80211_WMI_GET_BT_CONFIG:
ath6kl_dbg(ATH6KL_DBG_WMI, "Get BT config\n");
wmi_cmd = WMI_GET_BTCOEX_CONFIG_CMDID;
break;
case NL80211_WMI_GET_BT_STATS:
ath6kl_dbg(ATH6KL_DBG_WMI, "Get BT status\n");
wmi_cmd = WMI_GET_BTCOEX_STATS_CMDID;
break;
case NL80211_WMI_SET_BT_HID_CONFIG:
ath6kl_dbg(ATH6KL_DBG_WMI, "Set BT hid config\n");
wmi_cmd = WMI_SET_BTCOEX_HID_CONFIG_CMDID;
}
return wmi_cmd;
}
u8 fe_antenna_type(struct ath6kl *ar)
{
/* default setting for McK */
u8 fe_antenna = WMI_BTCOEX_FE_ANT_DUAL_SH_BT_LOW_ISO;
if (ar->version.target_ver == AR6004_HW_3_0_VERSION) {
#ifdef CONFIG_ANDROID
if (machine_is_apq8064_dma() ||
machine_is_apq8064_bueller())
fe_antenna =
WMI_BTCOEX_FE_ANT_DUAL_SH_BT_LOW_ISO;
else
fe_antenna =
WMI_BTCOEX_FE_ANT_DUAL_SH_BT_HIGH_ISO;
#endif
} else {
/* fill in correct antenna configuration from
board data if valid */
if (ar->fw_board[BDATA_ANTCONF_OFFSET])
fe_antenna =
ar->fw_board[BDATA_ANTCONF_OFFSET];
}
switch (fe_antenna) {
case WMI_BTCOEX_FE_ANT_SINGLE:
ath6kl_info("antenna config = single antenna\n");
break;
case WMI_BTCOEX_FE_ANT_DUAL_SH_BT_LOW_ISO:
ath6kl_info("antenna config = daul ant. low isolation\n");
break;
case WMI_BTCOEX_FE_ANT_DUAL_SH_BT_HIGH_ISO:
ath6kl_info("antenna config = dual ant. high isolation\n");
break;
case WMI_BTCOEX_FE_ANT_TRIPLE:
ath6kl_info("antenna config = triple antenna\n");
break;
default:
ath6kl_info("antenna config = invalid config!\n");
break;
}
return fe_antenna;
}
void ath6kl_btcoex_adjust_params(struct ath6kl *ar,
int wmi_cmd, u8 *buf)
{
switch (wmi_cmd) {
case WMI_SET_BTCOEX_FE_ANT_CMDID:
{
struct wmi_set_btcoex_fe_antenna_cmd *cmd =
(struct wmi_set_btcoex_fe_antenna_cmd *)buf;
cmd->fe_antenna_type = fe_antenna_type(ar);
/* disable green tx if it's enabled & BT is on */
ar->green_tx_params.enable = false;
ath6kl_wmi_set_green_tx_params(ar->wmi,
&ar->green_tx_params);
}
break;
case WMI_SET_BTCOEX_COLOCATED_BT_DEV_CMDID:
{
struct wmi_set_btcoex_colocated_bt_dev_cmd *cmd =
(struct wmi_set_btcoex_colocated_bt_dev_cmd *)buf;
/* Regardless of setting, force to use
* qcom-colocated BT. It's the current working mode.
*/
ar->btcoex_info.bt_vendor = BT_DEVICE_TYPE_QCOM;
cmd->colocated_bt_dev = BT_DEVICE_TYPE_QCOM;
}
break;
case WMI_SET_BTCOEX_A2DP_CONFIG_CMDID:
{
struct wmi_set_btcoex_a2dp_config_cmd *cmd =
(struct wmi_set_btcoex_a2dp_config_cmd *)buf;
struct btcoex_a2dp_config *a2dp_config = &cmd->a2dp_config;
struct btcoex_pspoll_a2dp_config *pspoll_config =
&cmd->pspoll_config;
struct ath6kl_vif *vif;
if (ar->btcoex_info.bt_vendor == BT_DEVICE_TYPE_QCOM) {
a2dp_config->a2dp_flags |= cpu_to_le32(
WMI_A2DP_CONFIG_FLAG_ALLOW_OPTIMIZATION);
} else {
a2dp_config->a2dp_flags |= cpu_to_le32(
WMI_A2DP_CONFIG_FLAG_IS_A2DP_HIGH_PRI);
}
if (a2dp_config->a2dp_flags &
WMI_A2DP_CONFIG_FLAG_IS_EDR_CAPABLE) {
/* A2DP EDR config overwrites */
if (a2dp_config->a2dp_flags &
WMI_A2DP_CONFIG_FLAG_IS_BT_ROLE_MASTER) {
a2dp_config->a2dp_flags |= cpu_to_le32(
BTCOEX_A2DP_EDR_MAX_BLUETOOTH_TIME);
/* disable stomping BT during WLAN scan
* or connection
*/
a2dp_config->a2dp_flags |= cpu_to_le32(
WMI_A2DP_CONFIG_FLAG_DIS_SCANCONN_STOMP
);
} else {
/* use BDR parameter for A2DP EDR slave case */
a2dp_config->a2dp_flags |= cpu_to_le32(
BTCOEX_A2DP_BDR_MAX_BLUETOOTH_TIME);
}
pspoll_config->a2dp_wlan_max_dur =
BTCOEX_A2DP_WLAN_MAX_DUR;
} else {
/* A2DP BDR config overwrites */
a2dp_config->a2dp_flags |= cpu_to_le32(
BTCOEX_A2DP_BDR_MAX_BLUETOOTH_TIME);
pspoll_config->a2dp_wlan_max_dur =
BTCOEX_A2DP_BDR_WLAN_MAX_DUR;
}
/* change A2DP parameters for AP mode*/
vif = ath6kl_vif_first(ar);
if (!vif)
return;
if (vif->nw_type == AP_NETWORK) {
if (a2dp_config->a2dp_flags &
WMI_A2DP_CONFIG_FLAG_IS_EDR_CAPABLE) {
pspoll_config->a2dp_wlan_max_dur =
BTCOEX_APMODE_A2DP_WLAN_MAX_DUR;
a2dp_config->a2dp_flags |= cpu_to_le32(
BTCOEX_APMODE_A2DP_MAX_BLUETOOTH_TIME);
} else {
pspoll_config->a2dp_wlan_max_dur =
BTCOEX_APMODE_A2DP_BDR_WLAN_MAX_DUR;
a2dp_config->a2dp_flags |= cpu_to_le32(
BTCOEX_APMODE_A2DP_BDR_MAX_BLUETOOTH_TIME);
}
if (ar->version.target_ver == AR6004_HW_1_3_VERSION) {
pspoll_config->a2dp_min_bus_cnt = cpu_to_le32(
BTCOEX_APMODE_A2DP_MIN_BURST_CNT);
}
}
}
break;
case WMI_SET_BTCOEX_SCO_CONFIG_CMDID:
{
struct wmi_set_btcoex_sco_config_cmd *cmd =
(struct wmi_set_btcoex_sco_config_cmd *)buf;
struct btcoex_sco_config *sco_config = &cmd->sco_config;
if (sco_config->sco_flags &
WMI_SCO_CONFIG_FLAG_IS_EDR_CAPABLE) {
/* disable stomping BT during WLAN scan/connection */
sco_config->sco_flags |= cpu_to_le32(
WMI_SCO_CONFIG_FLAG_DIS_SCANCONN_STOMP);
}
}
break;
}
}
int ath6kl_wmi_send_btcoex_cmd(struct ath6kl *ar,
u8 *buf, int len)
{
struct sk_buff *skb;
u32 nl_cmd;
int wmi_cmd;
struct wmi *wmi = ar->wmi;
nl_cmd = *(u32 *)buf;
buf += sizeof(u32);
len -= sizeof(u32);
wmi_cmd = ath6kl_get_wmi_cmd(nl_cmd);
if (wmi_cmd == 0)
return -ENOMEM;
skb = ath6kl_wmi_btcoex_get_new_buf(len);
if (!skb)
return -ENOMEM;
ath6kl_btcoex_adjust_params(ar, wmi_cmd, buf);
memcpy(skb->data, buf, len);
return ath6kl_wmi_cmd_send(wmi, 0, skb,
(enum wmi_cmd_id)wmi_cmd,
NO_SYNC_WMIFLAG);
}