Merge ath-next from git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git

ath.git patches for 4.18. Major changes:

ath10k

* add memory dump support for QCA9888 and QCA99X0

* add support to configure channel dwell time

* support new DFS host confirmation feature in the firmware

ath

* update various regulatory mappings

wcn36xx

* various fixes to improve reliability

* add Factory Test Mode support
This commit is contained in:
Kalle Valo 2018-05-30 22:24:44 +03:00
commit 76606886c9
23 changed files with 926 additions and 130 deletions

View File

@ -180,14 +180,11 @@ static void ath10k_ahb_clock_disable(struct ath10k *ar)
{
struct ath10k_ahb *ar_ahb = ath10k_ahb_priv(ar);
if (!IS_ERR_OR_NULL(ar_ahb->cmd_clk))
clk_disable_unprepare(ar_ahb->cmd_clk);
clk_disable_unprepare(ar_ahb->cmd_clk);
if (!IS_ERR_OR_NULL(ar_ahb->ref_clk))
clk_disable_unprepare(ar_ahb->ref_clk);
clk_disable_unprepare(ar_ahb->ref_clk);
if (!IS_ERR_OR_NULL(ar_ahb->rtc_clk))
clk_disable_unprepare(ar_ahb->rtc_clk);
clk_disable_unprepare(ar_ahb->rtc_clk);
}
static int ath10k_ahb_rst_ctrl_init(struct ath10k *ar)

View File

@ -44,6 +44,7 @@
#define WO(_f) ((_f##_OFFSET) >> 2)
#define ATH10K_SCAN_ID 0
#define ATH10K_SCAN_CHANNEL_SWITCH_WMI_EVT_OVERHEAD 10 /* msec */
#define WMI_READY_TIMEOUT (5 * HZ)
#define ATH10K_FLUSH_TIMEOUT_HZ (5 * HZ)
#define ATH10K_CONNECTION_LOSS_HZ (3 * HZ)
@ -175,6 +176,7 @@ struct ath10k_wmi {
struct completion service_ready;
struct completion unified_ready;
struct completion barrier;
struct completion radar_confirm;
wait_queue_head_t tx_credits_wq;
DECLARE_BITMAP(svc_map, WMI_SERVICE_MAX);
struct wmi_cmd_map *cmd;
@ -377,6 +379,21 @@ struct ath10k_dfs_stats {
u32 radar_detected;
};
enum ath10k_radar_confirmation_state {
ATH10K_RADAR_CONFIRMATION_IDLE = 0,
ATH10K_RADAR_CONFIRMATION_INPROGRESS,
ATH10K_RADAR_CONFIRMATION_STOPPED,
};
struct ath10k_radar_found_info {
u32 pri_min;
u32 pri_max;
u32 width_min;
u32 width_max;
u32 sidx_min;
u32 sidx_max;
};
#define ATH10K_MAX_NUM_PEER_IDS (1 << 11) /* htt rx_desc limit */
struct ath10k_peer {
@ -1109,6 +1126,11 @@ struct ath10k {
u32 sta_tid_stats_mask;
/* protected by data_lock */
enum ath10k_radar_confirmation_state radar_conf_state;
struct ath10k_radar_found_info last_radar_info;
struct work_struct radar_confirmation_work;
/* must be last */
u8 drv_priv[0] __aligned(sizeof(void *));
};

View File

@ -701,6 +701,89 @@ static const struct ath10k_mem_region qca988x_hw20_mem_regions[] = {
},
};
static const struct ath10k_mem_region qca99x0_hw20_mem_regions[] = {
{
.type = ATH10K_MEM_REGION_TYPE_DRAM,
.start = 0x400000,
.len = 0x60000,
.name = "DRAM",
.section_table = {
.sections = NULL,
.size = 0,
},
},
{
.type = ATH10K_MEM_REGION_TYPE_REG,
.start = 0x98000,
.len = 0x50000,
.name = "IRAM",
.section_table = {
.sections = NULL,
.size = 0,
},
},
{
.type = ATH10K_MEM_REGION_TYPE_IOSRAM,
.start = 0xC0000,
.len = 0x40000,
.name = "SRAM",
.section_table = {
.sections = NULL,
.size = 0,
},
},
{
.type = ATH10K_MEM_REGION_TYPE_IOREG,
.start = 0x30000,
.len = 0x7000,
.name = "APB REG 1",
.section_table = {
.sections = NULL,
.size = 0,
},
},
{
.type = ATH10K_MEM_REGION_TYPE_IOREG,
.start = 0x3f000,
.len = 0x3000,
.name = "APB REG 2",
.section_table = {
.sections = NULL,
.size = 0,
},
},
{
.type = ATH10K_MEM_REGION_TYPE_IOREG,
.start = 0x43000,
.len = 0x3000,
.name = "WIFI REG",
.section_table = {
.sections = NULL,
.size = 0,
},
},
{
.type = ATH10K_MEM_REGION_TYPE_IOREG,
.start = 0x4A000,
.len = 0x5000,
.name = "CE REG",
.section_table = {
.sections = NULL,
.size = 0,
},
},
{
.type = ATH10K_MEM_REGION_TYPE_IOREG,
.start = 0x80000,
.len = 0x6000,
.name = "SOC REG",
.section_table = {
.sections = NULL,
.size = 0,
},
},
};
static const struct ath10k_mem_region qca9984_hw10_mem_regions[] = {
{
.type = ATH10K_MEM_REGION_TYPE_DRAM,
@ -848,6 +931,21 @@ static const struct ath10k_hw_mem_layout hw_mem_layouts[] = {
.size = ARRAY_SIZE(qca9984_hw10_mem_regions),
},
},
{
.hw_id = QCA9888_HW_2_0_DEV_VERSION,
.region_table = {
.regions = qca9984_hw10_mem_regions,
.size = ARRAY_SIZE(qca9984_hw10_mem_regions),
},
},
{
.hw_id = QCA99X0_HW_2_0_DEV_VERSION,
.region_table = {
.regions = qca99x0_hw20_mem_regions,
.size = ARRAY_SIZE(qca99x0_hw20_mem_regions),
},
},
};
static u32 ath10k_coredump_get_ramdump_size(struct ath10k *ar)

View File

@ -3217,6 +3217,15 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
ar->hw->wiphy->bands[NL80211_BAND_5GHZ]);
}
static void ath10k_stop_radar_confirmation(struct ath10k *ar)
{
spin_lock_bh(&ar->data_lock);
ar->radar_conf_state = ATH10K_RADAR_CONFIRMATION_STOPPED;
spin_unlock_bh(&ar->data_lock);
cancel_work_sync(&ar->radar_confirmation_work);
}
/***************/
/* TX handlers */
/***************/
@ -4333,6 +4342,7 @@ void ath10k_halt(struct ath10k *ar)
ath10k_scan_finish(ar);
ath10k_peer_cleanup_all(ar);
ath10k_stop_radar_confirmation(ar);
ath10k_core_stop(ar);
ath10k_hif_power_down(ar);
@ -4758,6 +4768,8 @@ static int ath10k_start(struct ieee80211_hw *hw)
ath10k_spectral_start(ar);
ath10k_thermal_set_throttling(ar);
ar->radar_conf_state = ATH10K_RADAR_CONFIRMATION_IDLE;
mutex_unlock(&ar->conf_mutex);
return 0;
@ -5675,6 +5687,7 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw,
struct wmi_start_scan_arg arg;
int ret = 0;
int i;
u32 scan_timeout;
mutex_lock(&ar->conf_mutex);
@ -5736,6 +5749,22 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw,
arg.channels[i] = req->channels[i]->center_freq;
}
/* if duration is set, default dwell times will be overwritten */
if (req->duration) {
arg.dwell_time_active = req->duration;
arg.dwell_time_passive = req->duration;
arg.burst_duration_ms = req->duration;
scan_timeout = min_t(u32, arg.max_rest_time *
(arg.n_channels - 1) + (req->duration +
ATH10K_SCAN_CHANNEL_SWITCH_WMI_EVT_OVERHEAD) *
arg.n_channels, arg.max_scan_time + 200);
} else {
/* Add a 200ms margin to account for event/command processing */
scan_timeout = arg.max_scan_time + 200;
}
ret = ath10k_start_scan(ar, &arg);
if (ret) {
ath10k_warn(ar, "failed to start hw scan: %d\n", ret);
@ -5744,10 +5773,8 @@ static int ath10k_hw_scan(struct ieee80211_hw *hw,
spin_unlock_bh(&ar->data_lock);
}
/* Add a 200ms margin to account for event/command processing */
ieee80211_queue_delayed_work(ar->hw, &ar->scan.timeout,
msecs_to_jiffies(arg.max_scan_time +
200));
msecs_to_jiffies(scan_timeout));
exit:
mutex_unlock(&ar->conf_mutex);
@ -8364,6 +8391,8 @@ int ath10k_mac_register(struct ath10k *ar)
}
wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
wiphy_ext_feature_set(ar->hw->wiphy,
NL80211_EXT_FEATURE_SET_SCAN_DWELL);
/*
* on LL hardware queues are managed entirely by the FW

View File

@ -55,6 +55,8 @@ struct wmi_ops {
struct wmi_wow_ev_arg *arg);
int (*pull_echo_ev)(struct ath10k *ar, struct sk_buff *skb,
struct wmi_echo_ev_arg *arg);
int (*pull_dfs_status_ev)(struct ath10k *ar, struct sk_buff *skb,
struct wmi_dfs_status_ev_arg *arg);
int (*pull_svc_avail)(struct ath10k *ar, struct sk_buff *skb,
struct wmi_svc_avail_ev_arg *arg);
@ -188,6 +190,9 @@ struct wmi_ops {
const struct wmi_tdls_peer_update_cmd_arg *arg,
const struct wmi_tdls_peer_capab_arg *cap,
const struct wmi_channel_arg *chan);
struct sk_buff *(*gen_radar_found)
(struct ath10k *ar,
const struct ath10k_radar_found_info *arg);
struct sk_buff *(*gen_adaptive_qcs)(struct ath10k *ar, bool enable);
struct sk_buff *(*gen_pdev_get_tpc_config)(struct ath10k *ar,
u32 param);
@ -395,6 +400,16 @@ ath10k_wmi_pull_echo_ev(struct ath10k *ar, struct sk_buff *skb,
return ar->wmi.ops->pull_echo_ev(ar, skb, arg);
}
static inline int
ath10k_wmi_pull_dfs_status(struct ath10k *ar, struct sk_buff *skb,
struct wmi_dfs_status_ev_arg *arg)
{
if (!ar->wmi.ops->pull_dfs_status_ev)
return -EOPNOTSUPP;
return ar->wmi.ops->pull_dfs_status_ev(ar, skb, arg);
}
static inline enum wmi_txbf_conf
ath10k_wmi_get_txbf_conf_scheme(struct ath10k *ar)
{
@ -1511,4 +1526,21 @@ ath10k_wmi_pdev_get_tpc_table_cmdid(struct ath10k *ar, u32 param)
ar->wmi.cmd->pdev_get_tpc_table_cmdid);
}
static inline int
ath10k_wmi_report_radar_found(struct ath10k *ar,
const struct ath10k_radar_found_info *arg)
{
struct sk_buff *skb;
if (!ar->wmi.ops->gen_radar_found)
return -EOPNOTSUPP;
skb = ar->wmi.ops->gen_radar_found(ar, arg);
if (IS_ERR(skb))
return PTR_ERR(skb);
return ath10k_wmi_cmd_send(ar, skb,
ar->wmi.cmd->radar_found_cmdid);
}
#endif

View File

@ -34,6 +34,7 @@
#define ATH10K_WMI_BARRIER_ECHO_ID 0xBA991E9
#define ATH10K_WMI_BARRIER_TIMEOUT_HZ (3 * HZ)
#define ATH10K_WMI_DFS_CONF_TIMEOUT_HZ (HZ / 6)
/* MAIN WMI cmd track */
static struct wmi_cmd_map wmi_cmd_map = {
@ -199,6 +200,7 @@ static struct wmi_cmd_map wmi_cmd_map = {
.set_cca_params_cmdid = WMI_CMD_UNSUPPORTED,
.pdev_bss_chan_info_request_cmdid = WMI_CMD_UNSUPPORTED,
.pdev_get_tpc_table_cmdid = WMI_CMD_UNSUPPORTED,
.radar_found_cmdid = WMI_CMD_UNSUPPORTED,
};
/* 10.X WMI cmd track */
@ -367,6 +369,7 @@ static struct wmi_cmd_map wmi_10x_cmd_map = {
.set_cca_params_cmdid = WMI_CMD_UNSUPPORTED,
.pdev_bss_chan_info_request_cmdid = WMI_CMD_UNSUPPORTED,
.pdev_get_tpc_table_cmdid = WMI_CMD_UNSUPPORTED,
.radar_found_cmdid = WMI_CMD_UNSUPPORTED,
};
/* 10.2.4 WMI cmd track */
@ -535,6 +538,7 @@ static struct wmi_cmd_map wmi_10_2_4_cmd_map = {
.pdev_bss_chan_info_request_cmdid =
WMI_10_2_PDEV_BSS_CHAN_INFO_REQUEST_CMDID,
.pdev_get_tpc_table_cmdid = WMI_CMD_UNSUPPORTED,
.radar_found_cmdid = WMI_CMD_UNSUPPORTED,
};
/* 10.4 WMI cmd track */
@ -745,6 +749,7 @@ static struct wmi_cmd_map wmi_10_4_cmd_map = {
.tdls_set_state_cmdid = WMI_10_4_TDLS_SET_STATE_CMDID,
.tdls_peer_update_cmdid = WMI_10_4_TDLS_PEER_UPDATE_CMDID,
.tdls_set_offchan_mode_cmdid = WMI_10_4_TDLS_SET_OFFCHAN_MODE_CMDID,
.radar_found_cmdid = WMI_10_4_RADAR_FOUND_CMDID,
};
/* MAIN WMI VDEV param map */
@ -1490,6 +1495,7 @@ static struct wmi_cmd_map wmi_10_2_cmd_map = {
.pdev_get_ani_ofdm_config_cmdid = WMI_CMD_UNSUPPORTED,
.pdev_reserve_ast_entry_cmdid = WMI_CMD_UNSUPPORTED,
.pdev_get_tpc_table_cmdid = WMI_CMD_UNSUPPORTED,
.radar_found_cmdid = WMI_CMD_UNSUPPORTED,
};
static struct wmi_pdev_param_map wmi_10_4_pdev_param_map = {
@ -3683,6 +3689,68 @@ void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, struct sk_buff *skb)
ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n");
}
static void ath10k_radar_detected(struct ath10k *ar)
{
ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "dfs radar detected\n");
ATH10K_DFS_STAT_INC(ar, radar_detected);
/* Control radar events reporting in debugfs file
* dfs_block_radar_events
*/
if (ar->dfs_block_radar_events)
ath10k_info(ar, "DFS Radar detected, but ignored as requested\n");
else
ieee80211_radar_detected(ar->hw);
}
static void ath10k_radar_confirmation_work(struct work_struct *work)
{
struct ath10k *ar = container_of(work, struct ath10k,
radar_confirmation_work);
struct ath10k_radar_found_info radar_info;
int ret, time_left;
reinit_completion(&ar->wmi.radar_confirm);
spin_lock_bh(&ar->data_lock);
memcpy(&radar_info, &ar->last_radar_info, sizeof(radar_info));
spin_unlock_bh(&ar->data_lock);
ret = ath10k_wmi_report_radar_found(ar, &radar_info);
if (ret) {
ath10k_warn(ar, "failed to send radar found %d\n", ret);
goto wait_complete;
}
time_left = wait_for_completion_timeout(&ar->wmi.radar_confirm,
ATH10K_WMI_DFS_CONF_TIMEOUT_HZ);
if (time_left) {
/* DFS Confirmation status event received and
* necessary action completed.
*/
goto wait_complete;
} else {
/* DFS Confirmation event not received from FW.Considering this
* as real radar.
*/
ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
"dfs confirmation not received from fw, considering as radar\n");
goto radar_detected;
}
radar_detected:
ath10k_radar_detected(ar);
/* Reset state to allow sending confirmation on consecutive radar
* detections, unless radar confirmation is disabled/stopped.
*/
wait_complete:
spin_lock_bh(&ar->data_lock);
if (ar->radar_conf_state != ATH10K_RADAR_CONFIRMATION_STOPPED)
ar->radar_conf_state = ATH10K_RADAR_CONFIRMATION_IDLE;
spin_unlock_bh(&ar->data_lock);
}
static void ath10k_dfs_radar_report(struct ath10k *ar,
struct wmi_phyerr_ev_arg *phyerr,
const struct phyerr_radar_report *rr,
@ -3691,8 +3759,10 @@ static void ath10k_dfs_radar_report(struct ath10k *ar,
u32 reg0, reg1, tsf32l;
struct ieee80211_channel *ch;
struct pulse_event pe;
struct radar_detector_specs rs;
u64 tsf64;
u8 rssi, width;
struct ath10k_radar_found_info *radar_info;
reg0 = __le32_to_cpu(rr->reg0);
reg1 = __le32_to_cpu(rr->reg1);
@ -3757,25 +3827,46 @@ static void ath10k_dfs_radar_report(struct ath10k *ar,
ATH10K_DFS_STAT_INC(ar, pulses_detected);
if (!ar->dfs_detector->add_pulse(ar->dfs_detector, &pe)) {
if (!ar->dfs_detector->add_pulse(ar->dfs_detector, &pe, &rs)) {
ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
"dfs no pulse pattern detected, yet\n");
return;
}
radar_detected:
ath10k_dbg(ar, ATH10K_DBG_REGULATORY, "dfs radar detected\n");
ATH10K_DFS_STAT_INC(ar, radar_detected);
if ((test_bit(WMI_SERVICE_HOST_DFS_CHECK_SUPPORT, ar->wmi.svc_map)) &&
ar->dfs_detector->region == NL80211_DFS_FCC) {
/* Consecutive radar indications need not be
* sent to the firmware until we get confirmation
* for the previous detected radar.
*/
spin_lock_bh(&ar->data_lock);
if (ar->radar_conf_state != ATH10K_RADAR_CONFIRMATION_IDLE) {
spin_unlock_bh(&ar->data_lock);
return;
}
ar->radar_conf_state = ATH10K_RADAR_CONFIRMATION_INPROGRESS;
radar_info = &ar->last_radar_info;
/* Control radar events reporting in debugfs file
* dfs_block_radar_events
*/
if (ar->dfs_block_radar_events) {
ath10k_info(ar, "DFS Radar detected, but ignored as requested\n");
radar_info->pri_min = rs.pri_min;
radar_info->pri_max = rs.pri_max;
radar_info->width_min = rs.width_min;
radar_info->width_max = rs.width_max;
/*TODO Find sidx_min and sidx_max */
radar_info->sidx_min = MS(reg0, RADAR_REPORT_REG0_PULSE_SIDX);
radar_info->sidx_max = MS(reg0, RADAR_REPORT_REG0_PULSE_SIDX);
ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
"sending wmi radar found cmd pri_min %d pri_max %d width_min %d width_max %d sidx_min %d sidx_max %d\n",
radar_info->pri_min, radar_info->pri_max,
radar_info->width_min, radar_info->width_max,
radar_info->sidx_min, radar_info->sidx_max);
ieee80211_queue_work(ar->hw, &ar->radar_confirmation_work);
spin_unlock_bh(&ar->data_lock);
return;
}
ieee80211_radar_detected(ar->hw);
radar_detected:
ath10k_radar_detected(ar);
}
static int ath10k_dfs_fft_report(struct ath10k *ar,
@ -4125,6 +4216,47 @@ void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb)
}
}
static int
ath10k_wmi_10_4_op_pull_dfs_status_ev(struct ath10k *ar, struct sk_buff *skb,
struct wmi_dfs_status_ev_arg *arg)
{
struct wmi_dfs_status_ev_arg *ev = (void *)skb->data;
if (skb->len < sizeof(*ev))
return -EPROTO;
arg->status = ev->status;
return 0;
}
static void
ath10k_wmi_event_dfs_status_check(struct ath10k *ar, struct sk_buff *skb)
{
struct wmi_dfs_status_ev_arg status_arg = {};
int ret;
ret = ath10k_wmi_pull_dfs_status(ar, skb, &status_arg);
if (ret) {
ath10k_warn(ar, "failed to parse dfs status event: %d\n", ret);
return;
}
ath10k_dbg(ar, ATH10K_DBG_REGULATORY,
"dfs status event received from fw: %d\n",
status_arg.status);
/* Even in case of radar detection failure we follow the same
* behaviour as if radar is detected i.e to switch to a different
* channel.
*/
if (status_arg.status == WMI_HW_RADAR_DETECTED ||
status_arg.status == WMI_RADAR_DETECTION_FAIL)
ath10k_radar_detected(ar);
complete(&ar->wmi.radar_confirm);
}
void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb)
{
struct wmi_roam_ev_arg arg = {};
@ -5876,6 +6008,9 @@ static void ath10k_wmi_10_4_op_rx(struct ath10k *ar, struct sk_buff *skb)
case WMI_10_4_PDEV_TPC_TABLE_EVENTID:
ath10k_wmi_event_tpc_final_table(ar, skb);
break;
case WMI_10_4_DFS_STATUS_CHECK_EVENTID:
ath10k_wmi_event_dfs_status_check(ar, skb);
break;
default:
ath10k_warn(ar, "Unknown eventid: %d\n", id);
break;
@ -8461,6 +8596,32 @@ ath10k_wmi_10_4_gen_tdls_peer_update(struct ath10k *ar,
return skb;
}
static struct sk_buff *
ath10k_wmi_10_4_gen_radar_found(struct ath10k *ar,
const struct ath10k_radar_found_info *arg)
{
struct wmi_radar_found_info *cmd;
struct sk_buff *skb;
skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd));
if (!skb)
return ERR_PTR(-ENOMEM);
cmd = (struct wmi_radar_found_info *)skb->data;
cmd->pri_min = __cpu_to_le32(arg->pri_min);
cmd->pri_max = __cpu_to_le32(arg->pri_max);
cmd->width_min = __cpu_to_le32(arg->width_min);
cmd->width_max = __cpu_to_le32(arg->width_max);
cmd->sidx_min = __cpu_to_le32(arg->sidx_min);
cmd->sidx_max = __cpu_to_le32(arg->sidx_max);
ath10k_dbg(ar, ATH10K_DBG_WMI,
"wmi radar found pri_min %d pri_max %d width_min %d width_max %d sidx_min %d sidx_max %d\n",
arg->pri_min, arg->pri_max, arg->width_min,
arg->width_max, arg->sidx_min, arg->sidx_max);
return skb;
}
static struct sk_buff *
ath10k_wmi_op_gen_echo(struct ath10k *ar, u32 value)
{
@ -8798,6 +8959,7 @@ static const struct wmi_ops wmi_10_4_ops = {
.pull_svc_rdy = ath10k_wmi_main_op_pull_svc_rdy_ev,
.pull_rdy = ath10k_wmi_op_pull_rdy_ev,
.pull_roam_ev = ath10k_wmi_op_pull_roam_ev,
.pull_dfs_status_ev = ath10k_wmi_10_4_op_pull_dfs_status_ev,
.get_txbf_conf_scheme = ath10k_wmi_10_4_txbf_conf_scheme,
.gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend,
@ -8844,6 +9006,7 @@ static const struct wmi_ops wmi_10_4_ops = {
.gen_tdls_peer_update = ath10k_wmi_10_4_gen_tdls_peer_update,
.gen_pdev_get_tpc_table_cmdid =
ath10k_wmi_10_4_op_gen_pdev_get_tpc_table_cmdid,
.gen_radar_found = ath10k_wmi_10_4_gen_radar_found,
/* shared with 10.2 */
.pull_echo_ev = ath10k_wmi_op_pull_echo_ev,
@ -8906,8 +9069,11 @@ int ath10k_wmi_attach(struct ath10k *ar)
init_completion(&ar->wmi.service_ready);
init_completion(&ar->wmi.unified_ready);
init_completion(&ar->wmi.barrier);
init_completion(&ar->wmi.radar_confirm);
INIT_WORK(&ar->svc_rdy_work, ath10k_wmi_event_service_ready_work);
INIT_WORK(&ar->radar_confirmation_work,
ath10k_radar_confirmation_work);
return 0;
}

View File

@ -969,6 +969,7 @@ struct wmi_cmd_map {
u32 vdev_sifs_trigger_time_cmdid;
u32 pdev_wds_entry_list_cmdid;
u32 tdls_set_offchan_mode_cmdid;
u32 radar_found_cmdid;
};
/*
@ -1803,6 +1804,11 @@ enum wmi_10_4_cmd_id {
WMI_10_4_TDLS_SET_STATE_CMDID,
WMI_10_4_TDLS_PEER_UPDATE_CMDID,
WMI_10_4_TDLS_SET_OFFCHAN_MODE_CMDID,
WMI_10_4_PDEV_SEND_FD_CMDID,
WMI_10_4_ENABLE_FILS_CMDID,
WMI_10_4_PDEV_SET_BRIDGE_MACADDR_CMDID,
WMI_10_4_ATF_GROUP_WMM_AC_CONFIG_REQUEST_CMDID,
WMI_10_4_RADAR_FOUND_CMDID,
WMI_10_4_PDEV_UTF_CMDID = WMI_10_4_END_CMDID - 1,
};
@ -1878,6 +1884,9 @@ enum wmi_10_4_event_id {
WMI_10_4_PDEV_TPC_TABLE_EVENTID,
WMI_10_4_PDEV_WDS_ENTRY_LIST_EVENTID,
WMI_10_4_TDLS_PEER_EVENTID,
WMI_10_4_HOST_SWFDA_EVENTID,
WMI_10_4_ESP_ESTIMATE_EVENTID,
WMI_10_4_DFS_STATUS_CHECK_EVENTID,
WMI_10_4_PDEV_UTF_EVENTID = WMI_10_4_END_EVENTID - 1,
};
@ -3398,6 +3407,25 @@ struct wmi_10_4_phyerr_event {
u8 buf[0];
} __packed;
struct wmi_radar_found_info {
__le32 pri_min;
__le32 pri_max;
__le32 width_min;
__le32 width_max;
__le32 sidx_min;
__le32 sidx_max;
} __packed;
enum wmi_radar_confirmation_status {
/* Detected radar was due to SW pulses */
WMI_SW_RADAR_DETECTED = 0,
WMI_RADAR_DETECTION_FAIL = 1,
/* Real radar detected */
WMI_HW_RADAR_DETECTED = 2,
};
#define PHYERR_TLV_SIG 0xBB
#define PHYERR_TLV_TAG_SEARCH_FFT_REPORT 0xFB
#define PHYERR_TLV_TAG_RADAR_PULSE_SUMMARY 0xF8
@ -6631,6 +6659,10 @@ struct wmi_phyerr_hdr_arg {
const void *phyerrs;
};
struct wmi_dfs_status_ev_arg {
u32 status;
};
struct wmi_svc_rdy_ev_arg {
__le32 min_tx_power;
__le32 max_tx_power;

View File

@ -277,7 +277,7 @@ ath9k_dfs_process_radar_pulse(struct ath_softc *sc, struct pulse_event *pe)
DFS_STAT_INC(sc, pulses_processed);
if (pd == NULL)
return;
if (!pd->add_pulse(pd, pe))
if (!pd->add_pulse(pd, pe, NULL))
return;
DFS_STAT_INC(sc, radar_detected);
ieee80211_radar_detected(sc->hw);

View File

@ -268,7 +268,8 @@ static void dpd_exit(struct dfs_pattern_detector *dpd)
}
static bool
dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event)
dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event,
struct radar_detector_specs *rs)
{
u32 i;
struct channel_detector *cd;
@ -294,6 +295,8 @@ dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event)
struct pri_detector *pd = cd->detectors[i];
struct pri_sequence *ps = pd->add_pulse(pd, event);
if (ps != NULL) {
if (rs != NULL)
memcpy(rs, pd->rs, sizeof(*rs));
ath_dbg(dpd->common, DFS,
"DFS: radar found on freq=%d: id=%d, pri=%d, "
"count=%d, count_false=%d\n",

View File

@ -97,7 +97,8 @@ struct dfs_pattern_detector {
bool (*set_dfs_domain)(struct dfs_pattern_detector *dpd,
enum nl80211_dfs_regions region);
bool (*add_pulse)(struct dfs_pattern_detector *dpd,
struct pulse_event *pe);
struct pulse_event *pe,
struct radar_detector_specs *rs);
struct ath_dfs_pool_stats (*get_stats)(struct dfs_pattern_detector *dpd);
enum nl80211_dfs_regions region;

View File

@ -62,8 +62,9 @@ struct pri_detector {
(*add_pulse)(struct pri_detector *de, struct pulse_event *e);
void (*reset) (struct pri_detector *de, u64 ts);
/* private: internal use only */
const struct radar_detector_specs *rs;
/* private: internal use only */
u64 last_ts;
struct list_head sequences;
struct list_head pulses;

View File

@ -68,12 +68,14 @@ enum CountryCode {
CTRY_AUSTRALIA = 36,
CTRY_AUSTRIA = 40,
CTRY_AZERBAIJAN = 31,
CTRY_BAHAMAS = 44,
CTRY_BAHRAIN = 48,
CTRY_BANGLADESH = 50,
CTRY_BARBADOS = 52,
CTRY_BELARUS = 112,
CTRY_BELGIUM = 56,
CTRY_BELIZE = 84,
CTRY_BERMUDA = 60,
CTRY_BOLIVIA = 68,
CTRY_BOSNIA_HERZ = 70,
CTRY_BRAZIL = 76,
@ -136,8 +138,10 @@ enum CountryCode {
CTRY_MACEDONIA = 807,
CTRY_MALAYSIA = 458,
CTRY_MALTA = 470,
CTRY_MAURITIUS = 480,
CTRY_MEXICO = 484,
CTRY_MONACO = 492,
CTRY_MONTENEGRO = 499,
CTRY_MOROCCO = 504,
CTRY_NEPAL = 524,
CTRY_NETHERLANDS = 528,
@ -159,6 +163,7 @@ enum CountryCode {
CTRY_ROMANIA = 642,
CTRY_RUSSIA = 643,
CTRY_SAUDI_ARABIA = 682,
CTRY_SERBIA = 688,
CTRY_SERBIA_MONTENEGRO = 891,
CTRY_SINGAPORE = 702,
CTRY_SLOVAKIA = 703,
@ -170,11 +175,13 @@ enum CountryCode {
CTRY_SWITZERLAND = 756,
CTRY_SYRIA = 760,
CTRY_TAIWAN = 158,
CTRY_TANZANIA = 834,
CTRY_THAILAND = 764,
CTRY_TRINIDAD_Y_TOBAGO = 780,
CTRY_TUNISIA = 788,
CTRY_TURKEY = 792,
CTRY_UAE = 784,
CTRY_UGANDA = 800,
CTRY_UKRAINE = 804,
CTRY_UNITED_KINGDOM = 826,
CTRY_UNITED_STATES = 840,

View File

@ -35,6 +35,7 @@ enum EnumRd {
FRANCE_RES = 0x31,
FCC3_FCCA = 0x3A,
FCC3_WORLD = 0x3B,
FCC3_ETSIC = 0x3F,
ETSI1_WORLD = 0x37,
ETSI3_ETSIA = 0x32,
@ -44,6 +45,8 @@ enum EnumRd {
ETSI4_ETSIC = 0x38,
ETSI5_WORLD = 0x39,
ETSI6_WORLD = 0x34,
ETSI8_WORLD = 0x3D,
ETSI9_WORLD = 0x3E,
ETSI_RESERVED = 0x33,
MKK1_MKKA = 0x40,
@ -59,6 +62,7 @@ enum EnumRd {
MKK1_MKKA1 = 0x4A,
MKK1_MKKA2 = 0x4B,
MKK1_MKKC = 0x4C,
APL2_FCCA = 0x4D,
APL3_FCCA = 0x50,
APL1_WORLD = 0x52,
@ -67,6 +71,7 @@ enum EnumRd {
APL1_ETSIC = 0x55,
APL2_ETSIC = 0x56,
APL5_WORLD = 0x58,
APL13_WORLD = 0x5A,
APL6_WORLD = 0x5B,
APL7_FCCA = 0x5C,
APL8_WORLD = 0x5D,
@ -168,6 +173,7 @@ static struct reg_dmn_pair_mapping regDomainPairs[] = {
{FCC2_ETSIC, CTL_FCC, CTL_ETSI},
{FCC3_FCCA, CTL_FCC, CTL_FCC},
{FCC3_WORLD, CTL_FCC, CTL_ETSI},
{FCC3_ETSIC, CTL_FCC, CTL_ETSI},
{FCC4_FCCA, CTL_FCC, CTL_FCC},
{FCC5_FCCA, CTL_FCC, CTL_FCC},
{FCC6_FCCA, CTL_FCC, CTL_FCC},
@ -179,6 +185,8 @@ static struct reg_dmn_pair_mapping regDomainPairs[] = {
{ETSI4_WORLD, CTL_ETSI, CTL_ETSI},
{ETSI5_WORLD, CTL_ETSI, CTL_ETSI},
{ETSI6_WORLD, CTL_ETSI, CTL_ETSI},
{ETSI8_WORLD, CTL_ETSI, CTL_ETSI},
{ETSI9_WORLD, CTL_ETSI, CTL_ETSI},
/* XXX: For ETSI3_ETSIA, Was NO_CTL meant for the 2 GHz band ? */
{ETSI3_ETSIA, CTL_ETSI, CTL_ETSI},
@ -188,9 +196,11 @@ static struct reg_dmn_pair_mapping regDomainPairs[] = {
{FCC1_FCCA, CTL_FCC, CTL_FCC},
{APL1_WORLD, CTL_FCC, CTL_ETSI},
{APL2_WORLD, CTL_FCC, CTL_ETSI},
{APL2_FCCA, CTL_FCC, CTL_FCC},
{APL3_WORLD, CTL_FCC, CTL_ETSI},
{APL4_WORLD, CTL_FCC, CTL_ETSI},
{APL5_WORLD, CTL_FCC, CTL_ETSI},
{APL13_WORLD, CTL_ETSI, CTL_ETSI},
{APL6_WORLD, CTL_ETSI, CTL_ETSI},
{APL8_WORLD, CTL_ETSI, CTL_ETSI},
{APL9_WORLD, CTL_ETSI, CTL_ETSI},
@ -289,37 +299,39 @@ static struct reg_dmn_pair_mapping regDomainPairs[] = {
static struct country_code_to_enum_rd allCountries[] = {
{CTRY_DEBUG, NO_ENUMRD, "DB"},
{CTRY_DEFAULT, FCC1_FCCA, "CO"},
{CTRY_ALBANIA, NULL1_WORLD, "AL"},
{CTRY_ALGERIA, NULL1_WORLD, "DZ"},
{CTRY_ALBANIA, ETSI1_WORLD, "AL"},
{CTRY_ALGERIA, APL13_WORLD, "DZ"},
{CTRY_ARGENTINA, FCC3_WORLD, "AR"},
{CTRY_ARMENIA, ETSI4_WORLD, "AM"},
{CTRY_ARUBA, ETSI1_WORLD, "AW"},
{CTRY_AUSTRALIA, FCC2_WORLD, "AU"},
{CTRY_AUSTRALIA, FCC3_WORLD, "AU"},
{CTRY_AUSTRALIA2, FCC6_WORLD, "AU"},
{CTRY_AUSTRIA, ETSI1_WORLD, "AT"},
{CTRY_AZERBAIJAN, ETSI4_WORLD, "AZ"},
{CTRY_BAHAMAS, FCC3_WORLD, "BS"},
{CTRY_BAHRAIN, APL6_WORLD, "BH"},
{CTRY_BANGLADESH, NULL1_WORLD, "BD"},
{CTRY_BANGLADESH, APL1_WORLD, "BD"},
{CTRY_BARBADOS, FCC2_WORLD, "BB"},
{CTRY_BELARUS, ETSI1_WORLD, "BY"},
{CTRY_BELGIUM, ETSI1_WORLD, "BE"},
{CTRY_BELGIUM2, ETSI4_WORLD, "BL"},
{CTRY_BELIZE, APL1_ETSIC, "BZ"},
{CTRY_BERMUDA, FCC3_FCCA, "BM"},
{CTRY_BOLIVIA, APL1_ETSIC, "BO"},
{CTRY_BOSNIA_HERZ, ETSI1_WORLD, "BA"},
{CTRY_BRAZIL, FCC3_WORLD, "BR"},
{CTRY_BRUNEI_DARUSSALAM, APL1_WORLD, "BN"},
{CTRY_BULGARIA, ETSI6_WORLD, "BG"},
{CTRY_BRUNEI_DARUSSALAM, APL6_WORLD, "BN"},
{CTRY_BULGARIA, ETSI1_WORLD, "BG"},
{CTRY_CAMBODIA, ETSI1_WORLD, "KH"},
{CTRY_CANADA, FCC3_FCCA, "CA"},
{CTRY_CANADA2, FCC6_FCCA, "CA"},
{CTRY_CHILE, APL6_WORLD, "CL"},
{CTRY_CHINA, APL1_WORLD, "CN"},
{CTRY_COLOMBIA, FCC1_FCCA, "CO"},
{CTRY_COLOMBIA, FCC3_WORLD, "CO"},
{CTRY_COSTA_RICA, FCC1_WORLD, "CR"},
{CTRY_CROATIA, ETSI1_WORLD, "HR"},
{CTRY_CYPRUS, ETSI1_WORLD, "CY"},
{CTRY_CZECH, ETSI3_WORLD, "CZ"},
{CTRY_CZECH, ETSI1_WORLD, "CZ"},
{CTRY_DENMARK, ETSI1_WORLD, "DK"},
{CTRY_DOMINICAN_REPUBLIC, FCC1_FCCA, "DO"},
{CTRY_ECUADOR, FCC1_WORLD, "EC"},
@ -336,7 +348,7 @@ static struct country_code_to_enum_rd allCountries[] = {
{CTRY_GUAM, FCC1_FCCA, "GU"},
{CTRY_GUATEMALA, FCC1_FCCA, "GT"},
{CTRY_HAITI, ETSI1_WORLD, "HT"},
{CTRY_HONDURAS, NULL1_WORLD, "HN"},
{CTRY_HONDURAS, FCC3_WORLD, "HN"},
{CTRY_HONG_KONG, FCC3_WORLD, "HK"},
{CTRY_HUNGARY, ETSI1_WORLD, "HU"},
{CTRY_ICELAND, ETSI1_WORLD, "IS"},
@ -344,7 +356,7 @@ static struct country_code_to_enum_rd allCountries[] = {
{CTRY_INDONESIA, NULL1_WORLD, "ID"},
{CTRY_IRAN, APL1_WORLD, "IR"},
{CTRY_IRELAND, ETSI1_WORLD, "IE"},
{CTRY_ISRAEL, NULL1_WORLD, "IL"},
{CTRY_ISRAEL, ETSI3_WORLD, "IL"},
{CTRY_ITALY, ETSI1_WORLD, "IT"},
{CTRY_JAMAICA, FCC3_WORLD, "JM"},
@ -409,6 +421,7 @@ static struct country_code_to_enum_rd allCountries[] = {
{CTRY_JORDAN, ETSI2_WORLD, "JO"},
{CTRY_KAZAKHSTAN, NULL1_WORLD, "KZ"},
{CTRY_KENYA, APL1_WORLD, "KE"},
{CTRY_KOREA_NORTH, APL9_WORLD, "KP"},
{CTRY_KOREA_ROC, APL9_WORLD, "KR"},
{CTRY_KOREA_ROC2, APL2_WORLD, "K2"},
@ -420,32 +433,37 @@ static struct country_code_to_enum_rd allCountries[] = {
{CTRY_LITHUANIA, ETSI1_WORLD, "LT"},
{CTRY_LUXEMBOURG, ETSI1_WORLD, "LU"},
{CTRY_MACAU, FCC2_WORLD, "MO"},
{CTRY_MACEDONIA, NULL1_WORLD, "MK"},
{CTRY_MALAYSIA, APL8_WORLD, "MY"},
{CTRY_MACEDONIA, ETSI1_WORLD, "MK"},
{CTRY_MALAYSIA, FCC1_WORLD, "MY"},
{CTRY_MALTA, ETSI1_WORLD, "MT"},
{CTRY_MAURITIUS, ETSI1_WORLD, "MU"},
{CTRY_MEXICO, FCC1_FCCA, "MX"},
{CTRY_MONACO, ETSI4_WORLD, "MC"},
{CTRY_MONTENEGRO, ETSI1_WORLD, "ME"},
{CTRY_MOROCCO, APL4_WORLD, "MA"},
{CTRY_NEPAL, APL1_WORLD, "NP"},
{CTRY_NETHERLANDS, ETSI1_WORLD, "NL"},
{CTRY_NETHERLANDS_ANTILLES, ETSI1_WORLD, "AN"},
{CTRY_NEW_ZEALAND, FCC2_ETSIC, "NZ"},
{CTRY_NEW_ZEALAND, FCC3_ETSIC, "NZ"},
{CTRY_NICARAGUA, FCC3_FCCA, "NI"},
{CTRY_NORWAY, ETSI1_WORLD, "NO"},
{CTRY_OMAN, FCC3_WORLD, "OM"},
{CTRY_PAKISTAN, NULL1_WORLD, "PK"},
{CTRY_PANAMA, FCC1_FCCA, "PA"},
{CTRY_PAPUA_NEW_GUINEA, FCC1_WORLD, "PG"},
{CTRY_PERU, APL1_WORLD, "PE"},
{CTRY_PHILIPPINES, APL1_WORLD, "PH"},
{CTRY_PARAGUAY, FCC3_WORLD, "PY"},
{CTRY_PERU, FCC3_WORLD, "PE"},
{CTRY_PHILIPPINES, FCC3_WORLD, "PH"},
{CTRY_POLAND, ETSI1_WORLD, "PL"},
{CTRY_PORTUGAL, ETSI1_WORLD, "PT"},
{CTRY_PUERTO_RICO, FCC1_FCCA, "PR"},
{CTRY_QATAR, APL1_WORLD, "QA"},
{CTRY_ROMANIA, NULL1_WORLD, "RO"},
{CTRY_RUSSIA, NULL1_WORLD, "RU"},
{CTRY_ROMANIA, ETSI1_WORLD, "RO"},
{CTRY_RUSSIA, ETSI8_WORLD, "RU"},
{CTRY_SAUDI_ARABIA, NULL1_WORLD, "SA"},
{CTRY_SERBIA, ETSI1_WORLD, "RS"},
{CTRY_SERBIA_MONTENEGRO, ETSI1_WORLD, "CS"},
{CTRY_SINGAPORE, APL6_WORLD, "SG"},
{CTRY_SINGAPORE, FCC3_WORLD, "SG"},
{CTRY_SLOVAKIA, ETSI1_WORLD, "SK"},
{CTRY_SLOVENIA, ETSI1_WORLD, "SI"},
{CTRY_SOUTH_AFRICA, FCC3_WORLD, "ZA"},
@ -455,11 +473,13 @@ static struct country_code_to_enum_rd allCountries[] = {
{CTRY_SWITZERLAND, ETSI1_WORLD, "CH"},
{CTRY_SYRIA, NULL1_WORLD, "SY"},
{CTRY_TAIWAN, APL3_FCCA, "TW"},
{CTRY_TANZANIA, APL1_WORLD, "TZ"},
{CTRY_THAILAND, FCC3_WORLD, "TH"},
{CTRY_TRINIDAD_Y_TOBAGO, FCC3_WORLD, "TT"},
{CTRY_TUNISIA, ETSI3_WORLD, "TN"},
{CTRY_TURKEY, ETSI3_WORLD, "TR"},
{CTRY_UKRAINE, NULL1_WORLD, "UA"},
{CTRY_UGANDA, FCC3_WORLD, "UG"},
{CTRY_UKRAINE, ETSI9_WORLD, "UA"},
{CTRY_UAE, NULL1_WORLD, "AE"},
{CTRY_UNITED_KINGDOM, ETSI1_WORLD, "GB"},
{CTRY_UNITED_STATES, FCC3_FCCA, "US"},
@ -472,7 +492,7 @@ static struct country_code_to_enum_rd allCountries[] = {
{CTRY_VENEZUELA, APL2_ETSIC, "VE"},
{CTRY_VIET_NAM, NULL1_WORLD, "VN"},
{CTRY_YEMEN, NULL1_WORLD, "YE"},
{CTRY_ZIMBABWE, NULL1_WORLD, "ZW"},
{CTRY_ZIMBABWE, ETSI1_WORLD, "ZW"},
};
#endif

View File

@ -6,3 +6,5 @@ wcn36xx-y += main.o \
smd.o \
pmc.o \
debug.o
wcn36xx-$(CONFIG_NL80211_TESTMODE) += testmode.o

View File

@ -370,7 +370,9 @@ static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch)
do {
if (READ_ONCE(ctl->desc->ctrl) & WCN36xx_DXE_CTRL_VLD)
break;
if (ctl->skb) {
if (ctl->skb &&
READ_ONCE(ctl->desc->ctrl) & WCN36xx_DXE_CTRL_EOP) {
dma_unmap_single(wcn->dev, ctl->desc->src_addr_l,
ctl->skb->len, DMA_TO_DEVICE);
info = IEEE80211_SKB_CB(ctl->skb);
@ -430,8 +432,12 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev)
WCN36XX_INT_MASK_CHAN_TX_H);
}
wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready high\n");
reap_tx_dxes(wcn, &wcn->dxe_tx_h_ch);
wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready high, reason %08x\n",
int_reason);
if (int_reason & (WCN36XX_CH_STAT_INT_DONE_MASK |
WCN36XX_CH_STAT_INT_ED_MASK))
reap_tx_dxes(wcn, &wcn->dxe_tx_h_ch);
}
if (int_src & WCN36XX_INT_MASK_CHAN_TX_L) {
@ -465,8 +471,12 @@ static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev)
WCN36XX_INT_MASK_CHAN_TX_L);
}
wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready low\n");
reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch);
wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready low, reason %08x\n",
int_reason);
if (int_reason & (WCN36XX_CH_STAT_INT_DONE_MASK |
WCN36XX_CH_STAT_INT_ED_MASK))
reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch);
}
return IRQ_HANDLED;
@ -476,9 +486,8 @@ static irqreturn_t wcn36xx_irq_rx_ready(int irq, void *dev)
{
struct wcn36xx *wcn = (struct wcn36xx *)dev;
disable_irq_nosync(wcn->rx_irq);
wcn36xx_dxe_rx_frame(wcn);
enable_irq(wcn->rx_irq);
return IRQ_HANDLED;
}
@ -512,23 +521,49 @@ out_err:
}
static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn,
struct wcn36xx_dxe_ch *ch)
struct wcn36xx_dxe_ch *ch,
u32 ctrl,
u32 en_mask,
u32 int_mask,
u32 status_reg)
{
struct wcn36xx_dxe_ctl *ctl = ch->head_blk_ctl;
struct wcn36xx_dxe_desc *dxe = ctl->desc;
struct wcn36xx_dxe_desc *dxe;
struct wcn36xx_dxe_ctl *ctl;
dma_addr_t dma_addr;
struct sk_buff *skb;
int ret = 0, int_mask;
u32 value;
u32 int_reason;
int ret;
if (ch->ch_type == WCN36XX_DXE_CH_RX_L) {
value = WCN36XX_DXE_CTRL_RX_L;
int_mask = WCN36XX_DXE_INT_CH1_MASK;
} else {
value = WCN36XX_DXE_CTRL_RX_H;
int_mask = WCN36XX_DXE_INT_CH3_MASK;
wcn36xx_dxe_read_register(wcn, status_reg, &int_reason);
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR, int_mask);
if (int_reason & WCN36XX_CH_STAT_INT_ERR_MASK) {
wcn36xx_dxe_write_register(wcn,
WCN36XX_DXE_0_INT_ERR_CLR,
int_mask);
wcn36xx_err("DXE IRQ reported error on RX channel\n");
}
if (int_reason & WCN36XX_CH_STAT_INT_DONE_MASK)
wcn36xx_dxe_write_register(wcn,
WCN36XX_DXE_0_INT_DONE_CLR,
int_mask);
if (int_reason & WCN36XX_CH_STAT_INT_ED_MASK)
wcn36xx_dxe_write_register(wcn,
WCN36XX_DXE_0_INT_ED_CLR,
int_mask);
if (!(int_reason & (WCN36XX_CH_STAT_INT_DONE_MASK |
WCN36XX_CH_STAT_INT_ED_MASK)))
return 0;
spin_lock(&ch->lock);
ctl = ch->head_blk_ctl;
dxe = ctl->desc;
while (!(READ_ONCE(dxe->ctrl) & WCN36xx_DXE_CTRL_VLD)) {
skb = ctl->skb;
dma_addr = dxe->dst_addr_l;
@ -542,13 +577,16 @@ static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn,
wcn36xx_rx_skb(wcn, skb);
} /* else keep old skb not submitted and use it for rx DMA */
dxe->ctrl = value;
dxe->ctrl = ctrl;
ctl = ctl->next;
dxe = ctl->desc;
}
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, int_mask);
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, en_mask);
ch->head_blk_ctl = ctl;
spin_unlock(&ch->lock);
return 0;
}
@ -559,19 +597,20 @@ void wcn36xx_dxe_rx_frame(struct wcn36xx *wcn)
wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src);
/* RX_LOW_PRI */
if (int_src & WCN36XX_DXE_INT_CH1_MASK) {
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR,
WCN36XX_DXE_INT_CH1_MASK);
wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_l_ch));
}
if (int_src & WCN36XX_DXE_INT_CH1_MASK)
wcn36xx_rx_handle_packets(wcn, &wcn->dxe_rx_l_ch,
WCN36XX_DXE_CTRL_RX_L,
WCN36XX_DXE_INT_CH1_MASK,
WCN36XX_INT_MASK_CHAN_RX_L,
WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_L);
/* RX_HIGH_PRI */
if (int_src & WCN36XX_DXE_INT_CH3_MASK) {
/* Clean up all the INT within this channel */
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR,
WCN36XX_DXE_INT_CH3_MASK);
wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_h_ch));
}
if (int_src & WCN36XX_DXE_INT_CH3_MASK)
wcn36xx_rx_handle_packets(wcn, &wcn->dxe_rx_h_ch,
WCN36XX_DXE_CTRL_RX_H,
WCN36XX_DXE_INT_CH3_MASK,
WCN36XX_INT_MASK_CHAN_RX_H,
WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_H);
if (!int_src)
wcn36xx_warn("No DXE interrupt pending\n");
@ -642,8 +681,8 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
struct sk_buff *skb,
bool is_low)
{
struct wcn36xx_dxe_ctl *ctl = NULL;
struct wcn36xx_dxe_desc *desc = NULL;
struct wcn36xx_dxe_desc *desc_bd, *desc_skb;
struct wcn36xx_dxe_ctl *ctl_bd, *ctl_skb;
struct wcn36xx_dxe_ch *ch = NULL;
unsigned long flags;
int ret;
@ -651,74 +690,75 @@ int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
ch = is_low ? &wcn->dxe_tx_l_ch : &wcn->dxe_tx_h_ch;
spin_lock_irqsave(&ch->lock, flags);
ctl = ch->head_blk_ctl;
ctl_bd = ch->head_blk_ctl;
ctl_skb = ctl_bd->next;
/*
* If skb is not null that means that we reached the tail of the ring
* hence ring is full. Stop queues to let mac80211 back off until ring
* has an empty slot again.
*/
if (NULL != ctl->next->skb) {
if (NULL != ctl_skb->skb) {
ieee80211_stop_queues(wcn->hw);
wcn->queues_stopped = true;
spin_unlock_irqrestore(&ch->lock, flags);
return -EBUSY;
}
ctl->skb = NULL;
desc = ctl->desc;
/* write buffer descriptor */
memcpy(ctl->bd_cpu_addr, bd, sizeof(*bd));
/* Set source address of the BD we send */
desc->src_addr_l = ctl->bd_phy_addr;
desc->dst_addr_l = ch->dxe_wq;
desc->fr_len = sizeof(struct wcn36xx_tx_bd);
desc->ctrl = ch->ctrl_bd;
wcn36xx_dbg(WCN36XX_DBG_DXE, "DXE TX\n");
wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC1 >>> ",
(char *)desc, sizeof(*desc));
wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP,
"BD >>> ", (char *)ctl->bd_cpu_addr,
sizeof(struct wcn36xx_tx_bd));
/* Set source address of the SKB we send */
ctl = ctl->next;
desc = ctl->desc;
if (ctl->bd_cpu_addr) {
if (unlikely(ctl_skb->bd_cpu_addr)) {
wcn36xx_err("bd_cpu_addr cannot be NULL for skb DXE\n");
ret = -EINVAL;
goto unlock;
}
desc->src_addr_l = dma_map_single(wcn->dev,
skb->data,
skb->len,
DMA_TO_DEVICE);
if (dma_mapping_error(wcn->dev, desc->src_addr_l)) {
desc_bd = ctl_bd->desc;
desc_skb = ctl_skb->desc;
ctl_bd->skb = NULL;
/* write buffer descriptor */
memcpy(ctl_bd->bd_cpu_addr, bd, sizeof(*bd));
/* Set source address of the BD we send */
desc_bd->src_addr_l = ctl_bd->bd_phy_addr;
desc_bd->dst_addr_l = ch->dxe_wq;
desc_bd->fr_len = sizeof(struct wcn36xx_tx_bd);
wcn36xx_dbg(WCN36XX_DBG_DXE, "DXE TX\n");
wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC1 >>> ",
(char *)desc_bd, sizeof(*desc_bd));
wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP,
"BD >>> ", (char *)ctl_bd->bd_cpu_addr,
sizeof(struct wcn36xx_tx_bd));
desc_skb->src_addr_l = dma_map_single(wcn->dev,
skb->data,
skb->len,
DMA_TO_DEVICE);
if (dma_mapping_error(wcn->dev, desc_skb->src_addr_l)) {
dev_err(wcn->dev, "unable to DMA map src_addr_l\n");
ret = -ENOMEM;
goto unlock;
}
ctl->skb = skb;
desc->dst_addr_l = ch->dxe_wq;
desc->fr_len = ctl->skb->len;
/* set dxe descriptor to VALID */
desc->ctrl = ch->ctrl_skb;
ctl_skb->skb = skb;
desc_skb->dst_addr_l = ch->dxe_wq;
desc_skb->fr_len = ctl_skb->skb->len;
wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC2 >>> ",
(char *)desc, sizeof(*desc));
(char *)desc_skb, sizeof(*desc_skb));
wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "SKB >>> ",
(char *)ctl->skb->data, ctl->skb->len);
(char *)ctl_skb->skb->data, ctl_skb->skb->len);
/* Move the head of the ring to the next empty descriptor */
ch->head_blk_ctl = ctl->next;
ch->head_blk_ctl = ctl_skb->next;
/* Commit all previous writes and set descriptors to VALID */
wmb();
desc_skb->ctrl = ch->ctrl_skb;
wmb();
desc_bd->ctrl = ch->ctrl_bd;
/*
* When connected and trying to send data frame chip can be in sleep

View File

@ -2236,6 +2236,22 @@ struct wcn36xx_hal_switch_channel_rsp_msg {
} __packed;
struct wcn36xx_hal_process_ptt_msg_req_msg {
struct wcn36xx_hal_msg_header header;
/* Actual FTM Command body */
u8 ptt_msg[0];
} __packed;
struct wcn36xx_hal_process_ptt_msg_rsp_msg {
struct wcn36xx_hal_msg_header header;
/* FTM Command response status */
u32 ptt_msg_resp_status;
/* Actual FTM Command body */
u8 ptt_msg[0];
} __packed;
struct update_edca_params_req_msg {
struct wcn36xx_hal_msg_header header;

View File

@ -26,6 +26,7 @@
#include <linux/soc/qcom/smem_state.h>
#include <linux/soc/qcom/wcnss_ctrl.h>
#include "wcn36xx.h"
#include "testmode.h"
unsigned int wcn36xx_dbg_mask;
module_param_named(debug_mask, wcn36xx_dbg_mask, uint, 0644);
@ -798,6 +799,8 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
if (!is_zero_ether_addr(bss_conf->bssid)) {
vif_priv->is_joining = true;
vif_priv->bss_index = WCN36XX_HAL_BSS_INVALID_IDX;
wcn36xx_smd_set_link_st(wcn, bss_conf->bssid, vif->addr,
WCN36XX_HAL_LINK_PREASSOC_STATE);
wcn36xx_smd_join(wcn, bss_conf->bssid,
vif->addr, WCN36XX_HW_CHANNEL(wcn));
wcn36xx_smd_config_bss(wcn, vif, NULL,
@ -805,6 +808,8 @@ static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw,
} else {
vif_priv->is_joining = false;
wcn36xx_smd_delete_bss(wcn, vif);
wcn36xx_smd_set_link_st(wcn, bss_conf->bssid, vif->addr,
WCN36XX_HAL_LINK_IDLE_STATE);
vif_priv->encrypt_type = WCN36XX_HAL_ED_NONE;
}
}
@ -1142,6 +1147,8 @@ static const struct ieee80211_ops wcn36xx_ops = {
.sta_add = wcn36xx_sta_add,
.sta_remove = wcn36xx_sta_remove,
.ampdu_action = wcn36xx_ampdu_action,
CFG80211_TESTMODE_CMD(wcn36xx_tm_cmd)
};
static int wcn36xx_init_ieee80211(struct wcn36xx *wcn)
@ -1309,6 +1316,12 @@ static int wcn36xx_probe(struct platform_device *pdev)
mutex_init(&wcn->hal_mutex);
mutex_init(&wcn->scan_lock);
ret = dma_set_mask_and_coherent(wcn->dev, DMA_BIT_MASK(32));
if (ret < 0) {
wcn36xx_err("failed to set DMA mask: %d\n", ret);
goto out_wq;
}
INIT_WORK(&wcn->scan_work, wcn36xx_hw_scan_worker);
wcn->smd_channel = qcom_wcnss_open_channel(wcnss, "WLAN_CTRL", wcn36xx_smd_rsp_process, hw);

View File

@ -252,23 +252,29 @@ static int wcn36xx_smd_send_and_wait(struct wcn36xx *wcn, size_t len)
{
int ret = 0;
unsigned long start;
struct wcn36xx_hal_msg_header *hdr =
(struct wcn36xx_hal_msg_header *)wcn->hal_buf;
u16 req_type = hdr->msg_type;
wcn36xx_dbg_dump(WCN36XX_DBG_SMD_DUMP, "HAL >>> ", wcn->hal_buf, len);
init_completion(&wcn->hal_rsp_compl);
start = jiffies;
ret = rpmsg_send(wcn->smd_channel, wcn->hal_buf, len);
if (ret) {
wcn36xx_err("HAL TX failed\n");
wcn36xx_err("HAL TX failed for req %d\n", req_type);
goto out;
}
if (wait_for_completion_timeout(&wcn->hal_rsp_compl,
msecs_to_jiffies(HAL_MSG_TIMEOUT)) <= 0) {
wcn36xx_err("Timeout! No SMD response in %dms\n",
HAL_MSG_TIMEOUT);
wcn36xx_err("Timeout! No SMD response to req %d in %dms\n",
req_type, HAL_MSG_TIMEOUT);
ret = -ETIME;
goto out;
}
wcn36xx_dbg(WCN36XX_DBG_SMD, "SMD command completed in %dms",
wcn36xx_dbg(WCN36XX_DBG_SMD,
"SMD command (req %d, rsp %d) completed in %dms\n",
req_type, hdr->msg_type,
jiffies_to_msecs(jiffies - start));
out:
return ret;
@ -292,12 +298,26 @@ static void init_hal_msg(struct wcn36xx_hal_msg_header *hdr,
msg_body.header.len = sizeof(msg_body); \
} while (0) \
#define INIT_HAL_PTT_MSG(p_msg_body, ppt_msg_len) \
do { \
memset(p_msg_body, 0, sizeof(*p_msg_body) + ppt_msg_len); \
p_msg_body->header.msg_type = WCN36XX_HAL_PROCESS_PTT_REQ; \
p_msg_body->header.msg_version = WCN36XX_HAL_MSG_VERSION0; \
p_msg_body->header.len = sizeof(*p_msg_body) + ppt_msg_len; \
} while (0)
#define PREPARE_HAL_BUF(send_buf, msg_body) \
do { \
memset(send_buf, 0, msg_body.header.len); \
memcpy(send_buf, &msg_body, sizeof(msg_body)); \
} while (0) \
#define PREPARE_HAL_PTT_MSG_BUF(send_buf, p_msg_body) \
do { \
memset(send_buf, 0, p_msg_body->header.len); \
memcpy(send_buf, p_msg_body, p_msg_body->header.len); \
} while (0)
static int wcn36xx_smd_rsp_status_check(void *buf, size_t len)
{
struct wcn36xx_fw_msg_status_rsp *rsp;
@ -754,6 +774,71 @@ out:
return ret;
}
static int wcn36xx_smd_process_ptt_msg_rsp(void *buf, size_t len,
void **p_ptt_rsp_msg)
{
struct wcn36xx_hal_process_ptt_msg_rsp_msg *rsp;
int ret;
ret = wcn36xx_smd_rsp_status_check(buf, len);
if (ret)
return ret;
rsp = (struct wcn36xx_hal_process_ptt_msg_rsp_msg *)buf;
wcn36xx_dbg(WCN36XX_DBG_HAL, "process ptt msg responded with length %d\n",
rsp->header.len);
wcn36xx_dbg_dump(WCN36XX_DBG_HAL_DUMP, "HAL_PTT_MSG_RSP:", rsp->ptt_msg,
rsp->header.len - sizeof(rsp->ptt_msg_resp_status));
if (rsp->header.len > 0) {
*p_ptt_rsp_msg = kmalloc(rsp->header.len, GFP_ATOMIC);
if (!*p_ptt_rsp_msg)
return -ENOMEM;
memcpy(*p_ptt_rsp_msg, rsp->ptt_msg, rsp->header.len);
}
return ret;
}
int wcn36xx_smd_process_ptt_msg(struct wcn36xx *wcn,
struct ieee80211_vif *vif, void *ptt_msg, size_t len,
void **ptt_rsp_msg)
{
struct wcn36xx_hal_process_ptt_msg_req_msg *p_msg_body;
int ret;
mutex_lock(&wcn->hal_mutex);
p_msg_body = kmalloc(
sizeof(struct wcn36xx_hal_process_ptt_msg_req_msg) + len,
GFP_ATOMIC);
if (!p_msg_body) {
ret = -ENOMEM;
goto out_nomem;
}
INIT_HAL_PTT_MSG(p_msg_body, len);
memcpy(&p_msg_body->ptt_msg, ptt_msg, len);
PREPARE_HAL_PTT_MSG_BUF(wcn->hal_buf, p_msg_body);
ret = wcn36xx_smd_send_and_wait(wcn, p_msg_body->header.len);
if (ret) {
wcn36xx_err("Sending hal_process_ptt_msg failed\n");
goto out;
}
ret = wcn36xx_smd_process_ptt_msg_rsp(wcn->hal_buf, wcn->hal_rsp_len,
ptt_rsp_msg);
if (ret) {
wcn36xx_err("process_ptt_msg response failed err=%d\n", ret);
goto out;
}
out:
kfree(p_msg_body);
out_nomem:
mutex_unlock(&wcn->hal_mutex);
return ret;
}
static int wcn36xx_smd_update_scan_params_rsp(void *buf, size_t len)
{
struct wcn36xx_hal_update_scan_params_resp *rsp;
@ -2390,6 +2475,7 @@ int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev,
case WCN36XX_HAL_JOIN_RSP:
case WCN36XX_HAL_UPDATE_SCAN_PARAM_RSP:
case WCN36XX_HAL_CH_SWITCH_RSP:
case WCN36XX_HAL_PROCESS_PTT_RSP:
case WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_RSP:
case WCN36XX_HAL_8023_MULTICAST_LIST_RSP:
case WCN36XX_HAL_START_SCAN_OFFLOAD_RSP:
@ -2430,6 +2516,7 @@ int wcn36xx_smd_rsp_process(struct rpmsg_device *rpdev,
return 0;
}
static void wcn36xx_ind_smd_work(struct work_struct *work)
{
struct wcn36xx *wcn =
@ -2494,24 +2581,24 @@ static void wcn36xx_ind_smd_work(struct work_struct *work)
}
int wcn36xx_smd_open(struct wcn36xx *wcn)
{
int ret = 0;
wcn->hal_ind_wq = create_freezable_workqueue("wcn36xx_smd_ind");
if (!wcn->hal_ind_wq) {
wcn36xx_err("failed to allocate wq\n");
ret = -ENOMEM;
goto out;
}
if (!wcn->hal_ind_wq)
return -ENOMEM;
INIT_WORK(&wcn->hal_ind_work, wcn36xx_ind_smd_work);
INIT_LIST_HEAD(&wcn->hal_ind_queue);
spin_lock_init(&wcn->hal_ind_lock);
return 0;
out:
return ret;
}
void wcn36xx_smd_close(struct wcn36xx *wcn)
{
struct wcn36xx_hal_ind_msg *msg, *tmp;
cancel_work_sync(&wcn->hal_ind_work);
destroy_workqueue(wcn->hal_ind_wq);
list_for_each_entry_safe(msg, tmp, &wcn->hal_ind_queue, list)
kfree(msg);
}

View File

@ -86,6 +86,10 @@ int wcn36xx_smd_send_beacon(struct wcn36xx *wcn, struct ieee80211_vif *vif,
u16 p2p_off);
int wcn36xx_smd_switch_channel(struct wcn36xx *wcn,
struct ieee80211_vif *vif, int ch);
int wcn36xx_smd_process_ptt_msg(struct wcn36xx *wcn,
struct ieee80211_vif *vif,
void *ptt_msg, size_t len,
void **ptt_rsp_msg);
int wcn36xx_smd_update_proberesp_tmpl(struct wcn36xx *wcn,
struct ieee80211_vif *vif,
struct sk_buff *skb);

View File

@ -0,0 +1,149 @@
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* 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 <net/netlink.h>
#include <linux/firmware.h>
#include <net/cfg80211.h>
#include "wcn36xx.h"
#include "testmode.h"
#include "testmode_i.h"
#include "hal.h"
#include "smd.h"
static const struct nla_policy wcn36xx_tm_policy[WCN36XX_TM_ATTR_MAX + 1] = {
[WCN36XX_TM_ATTR_CMD] = { .type = NLA_U16 },
[WCN36XX_TM_ATTR_DATA] = { .type = NLA_BINARY,
.len = WCN36XX_TM_DATA_MAX_LEN },
};
struct build_release_number {
u16 drv_major;
u16 drv_minor;
u16 drv_patch;
u16 drv_build;
u16 ptt_max;
u16 ptt_min;
u16 fw_ver;
} __packed;
static int wcn36xx_tm_cmd_ptt(struct wcn36xx *wcn, struct ieee80211_vif *vif,
struct nlattr *tb[])
{
int ret = 0, buf_len;
void *buf;
struct ftm_rsp_msg *msg, *rsp = NULL;
struct sk_buff *skb;
if (!tb[WCN36XX_TM_ATTR_DATA])
return -EINVAL;
buf = nla_data(tb[WCN36XX_TM_ATTR_DATA]);
buf_len = nla_len(tb[WCN36XX_TM_ATTR_DATA]);
msg = (struct ftm_rsp_msg *)buf;
wcn36xx_dbg(WCN36XX_DBG_TESTMODE,
"testmode cmd wmi msg_id 0x%04X msg_len %d buf %pK buf_len %d\n",
msg->msg_id, msg->msg_body_length,
buf, buf_len);
wcn36xx_dbg_dump(WCN36XX_DBG_TESTMODE_DUMP, "REQ ", buf, buf_len);
if (msg->msg_id == MSG_GET_BUILD_RELEASE_NUMBER) {
struct build_release_number *body =
(struct build_release_number *)
msg->msg_response;
body->drv_major = wcn->fw_major;
body->drv_minor = wcn->fw_minor;
body->drv_patch = wcn->fw_version;
body->drv_build = wcn->fw_revision;
body->ptt_max = 10;
body->ptt_min = 0;
rsp = msg;
rsp->resp_status = 0;
} else {
wcn36xx_dbg(WCN36XX_DBG_TESTMODE,
"PPT Request >> HAL size %d\n",
msg->msg_body_length);
msg->resp_status = wcn36xx_smd_process_ptt_msg(wcn, vif, msg,
msg->msg_body_length, (void *)(&rsp));
wcn36xx_dbg(WCN36XX_DBG_TESTMODE,
"Response status = %d\n",
msg->resp_status);
if (rsp)
wcn36xx_dbg(WCN36XX_DBG_TESTMODE,
"PPT Response << HAL size %d\n",
rsp->msg_body_length);
}
if (!rsp) {
rsp = msg;
wcn36xx_warn("No response! Echoing request with response status %d\n",
rsp->resp_status);
}
wcn36xx_dbg_dump(WCN36XX_DBG_TESTMODE_DUMP, "RSP ",
rsp, rsp->msg_body_length);
skb = cfg80211_testmode_alloc_reply_skb(wcn->hw->wiphy,
nla_total_size(msg->msg_body_length));
if (!skb) {
ret = -ENOMEM;
goto out;
}
ret = nla_put(skb, WCN36XX_TM_ATTR_DATA, rsp->msg_body_length, rsp);
if (ret) {
kfree_skb(skb);
goto out;
}
ret = cfg80211_testmode_reply(skb);
out:
if (rsp != msg)
kfree(rsp);
return ret;
}
int wcn36xx_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len)
{
struct wcn36xx *wcn = hw->priv;
struct nlattr *tb[WCN36XX_TM_ATTR_MAX + 1];
int ret = 0;
unsigned short attr;
wcn36xx_dbg_dump(WCN36XX_DBG_TESTMODE_DUMP, "Data:", data, len);
ret = nla_parse(tb, WCN36XX_TM_ATTR_MAX, data, len,
wcn36xx_tm_policy, NULL);
if (ret)
return ret;
if (!tb[WCN36XX_TM_ATTR_CMD])
return -EINVAL;
attr = nla_get_u16(tb[WCN36XX_TM_ATTR_CMD]);
if (attr != WCN36XX_TM_CMD_PTT)
return -EOPNOTSUPP;
return wcn36xx_tm_cmd_ptt(wcn, vif, tb);
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* 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 "wcn36xx.h"
struct ftm_rsp_msg {
u16 msg_id;
u16 msg_body_length;
u32 resp_status;
u8 msg_response[0];
} __packed;
/* The request buffer of FTM which contains a byte of command and the request */
struct ftm_payload {
u16 ftm_cmd_type;
struct ftm_rsp_msg ftm_cmd_msg;
} __packed;
#define MSG_GET_BUILD_RELEASE_NUMBER 0x32A2
#ifdef CONFIG_NL80211_TESTMODE
int wcn36xx_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
void *data, int len);
#else
static inline int wcn36xx_tm_cmd(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
void *data, int len)
{
return 0;
}
#endif

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* 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.
*/
#define WCN36XX_TM_DATA_MAX_LEN 5000
enum wcn36xx_tm_attr {
__WCN36XX_TM_ATTR_INVALID = 0,
WCN36XX_TM_ATTR_CMD = 1,
WCN36XX_TM_ATTR_DATA = 2,
/* keep last */
__WCN36XX_TM_ATTR_AFTER_LAST,
WCN36XX_TM_ATTR_MAX = __WCN36XX_TM_ATTR_AFTER_LAST - 1,
};
#define WCN36XX_TM_CMD_PTT 3

View File

@ -50,6 +50,8 @@ enum wcn36xx_debug_mask {
WCN36XX_DBG_BEACON_DUMP = 0x00001000,
WCN36XX_DBG_PMC = 0x00002000,
WCN36XX_DBG_PMC_DUMP = 0x00004000,
WCN36XX_DBG_TESTMODE = 0x00008000,
WCN36XX_DBG_TESTMODE_DUMP = 0x00010000,
WCN36XX_DBG_ANY = 0xffffffff,
};