/* * Copyright (c) 2010-2011 Atheros Communications Inc. * Copyright (c) 2011 Qualcomm Atheros, 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 #include #include "core.h" #include "cfg80211.h" #include "debug.h" #include "hif-ops.h" #include "testmode.h" #include "wmiconfig.h" #include "wmi.h" #include enum ath6kl_tm_attr { __ATH6KL_TM_ATTR_INVALID = 0, ATH6KL_TM_ATTR_CMD = 1, ATH6KL_TM_ATTR_DATA = 2, /* keep last */ __ATH6KL_TM_ATTR_AFTER_LAST, ATH6KL_TM_ATTR_MAX = __ATH6KL_TM_ATTR_AFTER_LAST - 1, }; enum ath6kl_tm_cmd { ATH6KL_TM_CMD_TCMD = 0, ATH6KL_TM_CMD_RX_REPORT = 1, /* not used anymore */ ATH6KL_TM_CMD_WMI_CMD = 0xF000, }; #define AP_ACS_NONE AP_ACS_POLICY_MAX #define ATH6KL_WWAN_FDD 0x0F #define ATH6KL_WWAN_TDD 0xF0 #define ATH6KL_WWAN_BAND 0xFF struct app_lte_coex_wwan_data_t { uint32_t cmd; uint32_t band_info_valid; uint32_t ul_freq; uint32_t ul_bw; uint32_t dl_freq; uint32_t dl_bw; uint32_t tdd_info_valid; uint32_t fr_offset; uint32_t tdd_cfg; uint32_t sub_fr_cfg; uint32_t ul_cfg; uint32_t dl_cfg; uint32_t off_period_valid; uint32_t off_period; }; #define CHAN1 BIT(0) #define CHAN2 BIT(1) #define CHAN3 BIT(2) #define CHAN4 BIT(3) #define CHAN5 BIT(4) #define CHAN6 BIT(5) #define CHAN7 BIT(6) #define CHAN8 BIT(7) #define CHAN9 BIT(8) #define CHAN10 BIT(9) #define CHAN11 BIT(10) #define CHAN12 BIT(11) #define CHAN13 BIT(12) #define LTE_COEX_REF_TDD_ROWS 4 #define LTE_COEX_REF_FDD_ROWS 20 struct _lte_coex_chk { int wwan_min_freq; int wwan_max_freq; int wwan_bw; u32 margin_0_acs_chan_mask; /* margin 0 4db - 8db */ u32 margin_1_acs_chan_mask; /* margin 1 < 4db */ u32 margin_2_acs_chan_mask; /* margin 2 > 8db */ }; struct _lte_coex_chk lte_coex_tdd_lookup_table[LTE_COEX_REF_TDD_ROWS] = { /* wwan_min_freq wwan_max_freq wwan_bw * margin_0_chan_mask * margin_1_chan_mask * margin_2_chan_mask */ /* * For TDD bands margin value is ignored, so all the margins should have the * same channel lists * * */ /* band 40 tdd */ {2300, 2350, 0, (CHAN5 | CHAN6 | CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), (CHAN5 | CHAN6 | CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), (CHAN5 | CHAN6 | CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13)}, {2350, 2370, 0, (CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), (CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), (CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13)}, {2370, 2401, 0, (CHAN10 | CHAN11 | CHAN12 | CHAN13), (CHAN10 | CHAN11 | CHAN12 | CHAN13), (CHAN10 | CHAN11 | CHAN12 | CHAN13)}, /* band 41 tdd */ {2496, 2691, 0, (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN5 | CHAN6 | CHAN7 | CHAN8 | CHAN9), (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN5 | CHAN6 | CHAN7 | CHAN8 | CHAN9), (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN5 | CHAN6 | CHAN7 | CHAN8 | CHAN9)} }; /* Coex mode same for FDD B20 and B7 */ struct _lte_coex_chk lte_coex_fdd_lookup_table[LTE_COEX_REF_FDD_ROWS] = { /* band 7 10 MHz */ {2500, 2525, 10, /* margin 0 4db - 8db channel list */ (CHAN2 | CHAN3 | CHAN4| CHAN5 | CHAN6 | CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 1 < 4db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN4| CHAN5 | CHAN6 | CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 2 > 8db channel list*/ (CHAN4 | CHAN5 | CHAN6 | CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13)}, {2525, 2552, 10, (CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 0 4db - 8db channel list */ (CHAN6 | CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 1 < 4db channel list */ (CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13)}, /* margin 2 > 8db channel list*/ {2552, 2562, 10, (CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 0 4db - 8db channel list */ (CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 1 < 4db channel list */ (CHAN11 | CHAN12 | CHAN13)}, /* margin 2 > 8db channel list*/ {2562, 2568, 10, (CHAN1 | CHAN2 | CHAN3 | CHAN4| CHAN5 | CHAN11 | CHAN12 | CHAN13), /* margin 0 4db - 8db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN4| CHAN5 | CHAN6 | CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 1 < 4db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN13)}, /* margin 2 > 8db channel list*/ {2568, 2570, 10, (CHAN1 | CHAN2 | CHAN3 | CHAN4| CHAN5 | CHAN6), /* margin 0 4db - 8db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN4| CHAN5 | CHAN6 | CHAN7), /* margin 1 < 4db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN4)}, /* margin 2 > 8db channel list*/ /* band 7 20 MHz */ {2500, 2525, 20, (CHAN4 | CHAN5 | CHAN6 | CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 0 4db - 8db channel list */ (CHAN3 | CHAN4 | CHAN5 | CHAN6 | CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 1 < 4db channel list */ (CHAN6 | CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13)}, /* margin 2 > 8db channel list */ {2525, 2552, 20, (CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13),/* margin 0 4db - 8db channel list */ (CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 1 < 4db channel list */ (CHAN11 | CHAN12 | CHAN13)}, /* margin 2 > 8db channel list */ {2552, 2562, 20, (CHAN11 | CHAN12 | CHAN13), /* margin 0 4db - 8db channel list */ (CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 1 < 4db channel list */ (CHAN13)}, /* margin 2 > 8db channel list */ {2562, 2568, 20, (CHAN1 | CHAN2 | CHAN3 | CHAN13), /* margin 0 4db - 8db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN12 | CHAN13), /* margin 1 < 4db channel list */ (CHAN1 | CHAN13)}, /* margin 2 > 8db channel list */ {2568, 2570, 20, (CHAN1 | CHAN2 | CHAN3 | CHAN4), /* margin 0 4db - 8db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN5), /* margin 1 < 4db channel list */ (CHAN1 | CHAN2)}, /* margin 2 > 8db channel list */ /* band 20 10 MHz */ {832, 837, 10, (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN5 | CHAN6), /* margin 0 4db - 8db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN5 | CHAN6 | CHAN7 | CHAN10 | CHAN13), /* margin 1 < 4db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN4)}, /* margin 2 > 8db channel list */ {837, 844, 10, (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN5 | CHAN6 | CHAN7 | CHAN8 | CHAN9), /* margin 0 4db - 8db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN5 | CHAN6 | CHAN7 | CHAN8 | CHAN9 | CHAN10), /* margin 1 < 4db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN5 | CHAN6 | CHAN7)}, /* margin 2 > 8db channel list */ {845, 852, 10, (CHAN6 | CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 0 4db - 8db channel list */ (CHAN6 | CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 1 < 4db channel list */ (CHAN6 | CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12)}, /* margin 2 > 8db channel list */ {852, 857, 10, (CHAN1 | CHAN2 | CHAN3 | CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 0 4db - 8db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 1 < 4db channel list */ (CHAN1 | CHAN2 | CHAN11 | CHAN12 | CHAN13)}, /* margin 2 > 8db channel list */ {857, 862, 10, (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN5 | CHAN6 | CHAN7 | CHAN12 | CHAN13), /* margin 0 4db - 8db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN5 | CHAN6 | CHAN7 | CHAN12 | CHAN13), /* margin 1 < 4db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN5 | CHAN6 | CHAN7 | CHAN12 | CHAN13)}, /* margin 2 > 8db channel list */ /* band 20 20 MHz */ {832, 837, 20, (CHAN1 | CHAN2 | CHAN3 | CHAN4), /* margin 0 4db - 8db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN5), /* margin 1 < 4db channel list */ (CHAN1 | CHAN2)}, /* margin 2 > 8db channel list */ {837, 844, 20, (CHAN2 | CHAN3 | CHAN4 | CHAN5 | CHAN6 | CHAN7), /* margin 0 4db - 8db channel list */ (CHAN2 | CHAN3 | CHAN4 | CHAN5 | CHAN6 | CHAN7 | CHAN8), /* margin 1 < 4db channel list */ (CHAN2 | CHAN3 | CHAN4 | CHAN5)}, /* margin 2 > 8db channel list */ {845, 852, 20, (CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12), /* margin 0 4db - 8db channel list */ (CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 1 < 4db channel list */ (CHAN7 | CHAN8 | CHAN9 | CHAN10)}, /* margin 2 > 8db channel list */ {852, 857, 20, (CHAN1 | CHAN2 | CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 0 4db - 8db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN7 | CHAN8 | CHAN9 | CHAN10 | CHAN11 | CHAN12 | CHAN13), /* margin 1 < 4db channel list */ (CHAN1 | CHAN2 | CHAN10 | CHAN11 | CHAN12 | CHAN13)}, /* margin 2 > 8db channel list */ {857, 862, 20, (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN5 | CHAN6), /* margin 0 4db - 8db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN5 | CHAN6), /* margin 1 < 4db channel list */ (CHAN1 | CHAN2 | CHAN3 | CHAN4 | CHAN5 | CHAN6)}, /* margin 2 > 8db channel list */ /* No lte_coex needed for TDD B38 *{ATH6KL_WWAN_FREQ_2570, ATH6KL_WWAN_FREQ_2620, CH1, CH14, * LTE_COEX_MODE_DISABLED, * LTE_COEX_MODE_DISABLED, AP_ACS_NONE, * ATH6KL_WWAN_B38}, */ }; struct sk_buff *ath6kl_wmi_get_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; } static u32 ath6kl_get_lte_acs_chan_mask(struct ath6kl *ar, u8 index, struct _lte_coex_chk lte_coex_chk[]) { if (ar->lte_margin == 0) return lte_coex_chk[index].margin_0_acs_chan_mask; else if (ar->lte_margin == 1) return lte_coex_chk[index].margin_1_acs_chan_mask; else if (ar->lte_margin == 2) return lte_coex_chk[index].margin_2_acs_chan_mask; return 0; } static void ath6kl_lte_coex_config_acs_mask(struct ath6kl_vif *vif, uint32_t ap_oper_chan_mask, uint8_t index, struct _lte_coex_chk lte_coex_chk[]) { struct ath6kl *ar = vif->ar; uint8_t vif_idx = vif->fw_vif_idx; if (!(ar->lte_coex->acs_chan_mask & ap_oper_chan_mask)) { /* AP up */ if (ar->acs_in_prog) { vif->ap_hold_conn = 1; mod_timer(&vif->ap_restart_timer, jiffies + msecs_to_jiffies(1 * AP_RESTART_TIMER_INVAL)); } else { ar->acs_in_prog = 1; ath6kl_wmi_ap_profile_commit(ar->wmi, vif_idx, &vif->profile); } } } static void ath6kl_lte_coex_set_ap_mode(struct ath6kl *ar, uint8_t index, struct _lte_coex_chk lte_coex_chk[]) { struct ath6kl_vif *vif; uint32_t ap_oper_chan_mask; uint8_t vif_idx = 0; spin_lock_bh(&ar->list_lock); list_for_each_entry(vif, &ar->vif_list, list) { ar->lte_coex->acs_chan_mask = ath6kl_get_lte_acs_chan_mask(ar, index, lte_coex_chk);; if (vif->nw_type != AP_NETWORK || !test_bit(CONNECTED, &vif->flags) ) continue; vif_idx = vif->fw_vif_idx; ap_oper_chan_mask = ar->lte_coex->dev_ctx[vif_idx].ap_oper_chan_mask; /* select wlan band */ if (ap_oper_chan_mask && !ar->sta_bh_override && vif->phy_mode != WMI_11A_MODE) { ath6kl_lte_coex_config_acs_mask(vif, ap_oper_chan_mask, index, lte_coex_chk); } } spin_unlock_bh(&ar->list_lock); } static void ath6kl_setup_wlan_ap_lte_coex_mode(struct ath6kl *ar, int table_look_up_entries, struct _lte_coex_chk lte_coex_chk[]) { int i; if (ar->lte_coex->wwan_operational == 0) return ; ar->lte_coex->acs_chan_mask = AP_ACS_NONE; /* Select wwan band */ for (i = 0; i < table_look_up_entries; i++) { if (ar->lte_coex->wwan_freq >= lte_coex_chk[i].wwan_min_freq && ar->lte_coex->wwan_freq < lte_coex_chk[i].wwan_max_freq) { if (ar->lte_coex->wwan_band == ATH6KL_WWAN_FDD && (ar->lte_coex->wwan_bw != lte_coex_chk[i].wwan_bw)) continue; ath6kl_lte_coex_set_ap_mode(ar, i, lte_coex_chk); break; } } } void ath6kl_lte_coex_update_wwan_data(struct ath6kl *ar, void *wmi_buf) { int table_look_up_entries = 0; struct app_lte_coex_wwan_data_t *wwan = (struct app_lte_coex_wwan_data_t *)wmi_buf; ath6kl_info("LTE_COEX: QMI LTE band info:%d %d %d", wwan->band_info_valid, wwan->ul_freq, wwan->dl_freq); ath6kl_info("LTE_COEX: QMI TDD CFG:%d %d", wwan->tdd_info_valid, wwan->tdd_cfg); ath6kl_info("LTE_COEX: QMI off period:%d %d", wwan->off_period_valid, wwan->off_period); if (!(wwan->band_info_valid == 1 || wwan->tdd_info_valid == 1 || wwan->off_period_valid == 1)) return; ar->lte_coex->wwan_operational = 1; if (wwan->band_info_valid == 1) { ar->lte_coex->wwan_freq = wwan->ul_freq; ar->lte_coex->wwan_bw = wwan->ul_bw; if (wwan->ul_freq == 0) { if (wwan->dl_freq == 0) { ath6kl_dbg(ATH6KL_DBG_LTE_COEX, "LTE_COEX: LTE deactivated. Disable lte_coex"); ar->lte_coex->wwan_state = LTE_COEX_WWAN_STATE_DEACTIVATED; ar->lte_coex->wwan_band = 0; } else { ath6kl_dbg(ATH6KL_DBG_LTE_COEX, "LTE_COEX: LTE Idle Rx"); ar->lte_coex->wwan_freq = wwan->dl_freq; ar->lte_coex->wwan_state = LTE_COEX_WWAN_STATE_IDLE; ar->lte_coex->wwan_band = ATH6KL_WWAN_BAND; } } else { ath6kl_dbg(ATH6KL_DBG_LTE_COEX, "LTE_COEX: LTE Active"); if (wwan->ul_freq == wwan->dl_freq) { ar->lte_coex->wwan_band = ATH6KL_WWAN_TDD; table_look_up_entries = LTE_COEX_REF_TDD_ROWS; } else { ar->lte_coex->wwan_band = ATH6KL_WWAN_FDD; table_look_up_entries = LTE_COEX_REF_FDD_ROWS; } ar->lte_coex->wwan_state = LTE_COEX_WWAN_STATE_CONNECTED; } } if (ar->lte_coex->wwan_state != LTE_COEX_WWAN_STATE_IDLE) { ath6kl_info("wwan state=%d, wwan_band=%d wwan_freq=%d\n", ar->lte_coex->wwan_state, ar->lte_coex->wwan_band, ar->lte_coex->wwan_freq); ath6kl_setup_wlan_ap_lte_coex_mode(ar, table_look_up_entries, (ar->lte_coex->wwan_band & ATH6KL_WWAN_TDD) ? lte_coex_tdd_lookup_table : lte_coex_fdd_lookup_table); } } u32 ath6kl_set_ap_operating_chan_mask(uint32_t chan) { u32 chan_mask = 0, bit = 0; bit = (chan - 2412) / 5; chan_mask = BIT(bit); if (!chan || chan_mask > 0x1fff) chan_mask = 0; return chan_mask; } void ath6kl_lte_coex_update_wlan_data(struct ath6kl_vif *vif, uint32_t chan) { struct ath6kl *ar = vif->ar; int table_look_up_entries = (ar->lte_coex->wwan_band & ATH6KL_WWAN_TDD) ? LTE_COEX_REF_TDD_ROWS : LTE_COEX_REF_FDD_ROWS; if (vif->nw_type == AP_NETWORK) { if (chan && vif->phy_mode != WMI_11A_MODE) { ar->lte_coex->dev_ctx[vif->fw_vif_idx].ap_oper_chan_mask = ath6kl_set_ap_operating_chan_mask(chan); ath6kl_setup_wlan_ap_lte_coex_mode(ar, table_look_up_entries, (ar->lte_coex->wwan_band & ATH6KL_WWAN_TDD) ? lte_coex_tdd_lookup_table : lte_coex_fdd_lookup_table); } else ar->lte_coex->dev_ctx[vif->fw_vif_idx].ap_oper_chan_mask = 0; } } bool ath6kl_check_lte_coex_acs(struct ath6kl *ar, uint16_t *ap_acs_ch, struct ath6kl_vif *cur_vif) { struct ath6kl_vif *tmp_vif; bool ret = false; if (ar->lte_coex && ar->lte_coex->acs_chan_mask != AP_ACS_NONE) { list_for_each_entry(tmp_vif, &ar->vif_list, list) { if (tmp_vif->nw_type == AP_NETWORK) { if (test_bit(CONNECTED, &tmp_vif->flags)) { if (tmp_vif->fw_vif_idx != cur_vif->fw_vif_idx && (tmp_vif->phy_mode != WMI_11A_MODE)) { if (ar->lte_coex->acs_chan_mask & ar->lte_coex->dev_ctx[tmp_vif->fw_vif_idx].ap_oper_chan_mask) *ap_acs_ch = tmp_vif->bss_ch; } } } } if (!*ap_acs_ch) { *ap_acs_ch = cpu_to_le16(AP_ACS_USER_DEFINED); cur_vif->acs_chan_mask = ar->lte_coex->acs_chan_mask; } ar->lte_coex->dev_ctx[cur_vif->fw_vif_idx].ap_oper_chan_mask = 0; ret = true; } return ret; } int ath6kl_lte_coex_init(struct ath6kl *ar) { ath6kl_dbg(ATH6KL_DBG_LTE_COEX, "LTE_COEX: WWAN Coex Module Init"); ar->lte_coex = (struct ath6kl_lte_coex_priv *) kzalloc(sizeof(struct ath6kl_lte_coex_priv), GFP_KERNEL); if (!ar->lte_coex) return -ENOMEM; ar->lte_coex->ar = ar; ar->lte_coex->acs_chan_mask = AP_ACS_NONE; return 0; } void ath6kl_lte_coex_deinit(struct ath6kl *ar) { ath6kl_dbg(ATH6KL_DBG_LTE_COEX, "LTE_COEX: WWAN Coex Module Deinit"); kfree(ar->lte_coex); ar->lte_coex = NULL; } void ath6kl_tm_rx_wmi_event(struct ath6kl *ar, void *buf, size_t buf_len) { struct sk_buff *skb; if (!buf || buf_len == 0) return; skb = cfg80211_testmode_alloc_event_skb(ar->wiphy, buf_len, GFP_KERNEL); if (!skb) { ath6kl_warn("failed to allocate testmode rx skb!\n"); return; } NLA_PUT_U32(skb, ATH6KL_TM_ATTR_CMD, ATH6KL_TM_CMD_WMI_CMD); NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, buf_len, buf); cfg80211_testmode_event(skb, GFP_KERNEL); return; nla_put_failure: kfree_skb(skb); ath6kl_warn("nla_put failed on testmode rx skb!\n"); } void ath6kl_wmicfg_send_stats(struct ath6kl_vif *vif, struct target_stats *stats) { u32 *buff = kzalloc(sizeof(*stats) + 4, GFP_KERNEL); if (buff == NULL) return; buff[0] = WMI_REPORT_STATISTICS_EVENTID; memcpy(buff+1, stats, sizeof(struct target_stats)); ath6kl_tm_rx_wmi_event(vif->ar->wmi->parent_dev, buff, sizeof(struct target_stats)+4); kfree(buff); }