From 52fa019155329898f564f9f0264159728d263271 Mon Sep 17 00:00:00 2001 From: Marek Puzyniak Date: Tue, 24 Sep 2013 14:06:24 +0200 Subject: ath10k: make monitor vdev down before stoping it Following sequence causes FW crash: -monitor vdev up, -monitor vdev stop, -monitor vdev delete. Making monitor vdev down before stoping it works ok: -monitor vdev up, -monitor vdev down, -monitor vdev stop, -monitor vdev delete. Signed-off-by: Marek Puzyniak Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 99a9bad3f39..df4d29981c4 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -562,12 +562,9 @@ static int ath10k_monitor_stop(struct ath10k *ar) lockdep_assert_held(&ar->conf_mutex); - /* For some reasons, ath10k_wmi_vdev_down() here couse - * often ath10k_wmi_vdev_stop() to fail. Next we could - * not run monitor vdev and driver reload - * required. Don't see such problems we skip - * ath10k_wmi_vdev_down() here. - */ + ret = ath10k_wmi_vdev_down(ar, ar->monitor_vdev_id); + if (ret) + ath10k_warn("Monitor vdev down failed: %d\n", ret); ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); if (ret) -- cgit v1.2.3 From ce42870efbf186523c2998478a444254fc280956 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Thu, 26 Sep 2013 17:47:05 +0200 Subject: ath10k: introduce dynamic WMI structures This is the initial framework to implement dynamic WMI API in order to support new firmware (from so called 10.X branch) The realization is to have a static WMI cmd map for each of the firmwares, registered upom wmi init. This patch creates such map for MAIN FW, updates wmi_cmd_send() calls to take as a parameter the map value instead of direct WMI enum. As soon as complete 10.X API will be on place, we will introduce the FW IE mechanics to dynamicaly identify which FW is being used and based on that we will use correct map, API, structures, etc. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 4 + drivers/net/wireless/ath/ath10k/wmi.c | 230 +++++++++++++++++++++++++++------ drivers/net/wireless/ath/ath10k/wmi.h | 112 ++++++++++++++++ 3 files changed, 305 insertions(+), 41 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 292ad4577c9..acfee7c51b9 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -107,6 +107,7 @@ struct ath10k_wmi { struct completion service_ready; struct completion unified_ready; wait_queue_head_t tx_credits_wq; + struct wmi_cmd_map *cmd; }; struct ath10k_peer_stat { @@ -268,6 +269,9 @@ enum ath10k_fw_features { /* wmi_mgmt_rx_hdr contains extra RSSI information */ ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX = 0, + /* firmware from 10X branch */ + ATH10K_FW_FEATURE_WMI_10X = 1, + /* keep last */ ATH10K_FW_FEATURE_COUNT, }; diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 6803ead9b9c..1296dbe7d17 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -23,6 +23,128 @@ #include "wmi.h" #include "mac.h" +/* MAIN WMI cmd track */ +static struct wmi_cmd_map wmi_cmd_map = { + .init_cmdid = WMI_INIT_CMDID, + .start_scan_cmdid = WMI_START_SCAN_CMDID, + .stop_scan_cmdid = WMI_STOP_SCAN_CMDID, + .scan_chan_list_cmdid = WMI_SCAN_CHAN_LIST_CMDID, + .scan_sch_prio_tbl_cmdid = WMI_SCAN_SCH_PRIO_TBL_CMDID, + .pdev_set_regdomain_cmdid = WMI_PDEV_SET_REGDOMAIN_CMDID, + .pdev_set_channel_cmdid = WMI_PDEV_SET_CHANNEL_CMDID, + .pdev_set_param_cmdid = WMI_PDEV_SET_PARAM_CMDID, + .pdev_pktlog_enable_cmdid = WMI_PDEV_PKTLOG_ENABLE_CMDID, + .pdev_pktlog_disable_cmdid = WMI_PDEV_PKTLOG_DISABLE_CMDID, + .pdev_set_wmm_params_cmdid = WMI_PDEV_SET_WMM_PARAMS_CMDID, + .pdev_set_ht_cap_ie_cmdid = WMI_PDEV_SET_HT_CAP_IE_CMDID, + .pdev_set_vht_cap_ie_cmdid = WMI_PDEV_SET_VHT_CAP_IE_CMDID, + .pdev_set_dscp_tid_map_cmdid = WMI_PDEV_SET_DSCP_TID_MAP_CMDID, + .pdev_set_quiet_mode_cmdid = WMI_PDEV_SET_QUIET_MODE_CMDID, + .pdev_green_ap_ps_enable_cmdid = WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID, + .pdev_get_tpc_config_cmdid = WMI_PDEV_GET_TPC_CONFIG_CMDID, + .pdev_set_base_macaddr_cmdid = WMI_PDEV_SET_BASE_MACADDR_CMDID, + .vdev_create_cmdid = WMI_VDEV_CREATE_CMDID, + .vdev_delete_cmdid = WMI_VDEV_DELETE_CMDID, + .vdev_start_request_cmdid = WMI_VDEV_START_REQUEST_CMDID, + .vdev_restart_request_cmdid = WMI_VDEV_RESTART_REQUEST_CMDID, + .vdev_up_cmdid = WMI_VDEV_UP_CMDID, + .vdev_stop_cmdid = WMI_VDEV_STOP_CMDID, + .vdev_down_cmdid = WMI_VDEV_DOWN_CMDID, + .vdev_set_param_cmdid = WMI_VDEV_SET_PARAM_CMDID, + .vdev_install_key_cmdid = WMI_VDEV_INSTALL_KEY_CMDID, + .peer_create_cmdid = WMI_PEER_CREATE_CMDID, + .peer_delete_cmdid = WMI_PEER_DELETE_CMDID, + .peer_flush_tids_cmdid = WMI_PEER_FLUSH_TIDS_CMDID, + .peer_set_param_cmdid = WMI_PEER_SET_PARAM_CMDID, + .peer_assoc_cmdid = WMI_PEER_ASSOC_CMDID, + .peer_add_wds_entry_cmdid = WMI_PEER_ADD_WDS_ENTRY_CMDID, + .peer_remove_wds_entry_cmdid = WMI_PEER_REMOVE_WDS_ENTRY_CMDID, + .peer_mcast_group_cmdid = WMI_PEER_MCAST_GROUP_CMDID, + .bcn_tx_cmdid = WMI_BCN_TX_CMDID, + .pdev_send_bcn_cmdid = WMI_PDEV_SEND_BCN_CMDID, + .bcn_tmpl_cmdid = WMI_BCN_TMPL_CMDID, + .bcn_filter_rx_cmdid = WMI_BCN_FILTER_RX_CMDID, + .prb_req_filter_rx_cmdid = WMI_PRB_REQ_FILTER_RX_CMDID, + .mgmt_tx_cmdid = WMI_MGMT_TX_CMDID, + .prb_tmpl_cmdid = WMI_PRB_TMPL_CMDID, + .addba_clear_resp_cmdid = WMI_ADDBA_CLEAR_RESP_CMDID, + .addba_send_cmdid = WMI_ADDBA_SEND_CMDID, + .addba_status_cmdid = WMI_ADDBA_STATUS_CMDID, + .delba_send_cmdid = WMI_DELBA_SEND_CMDID, + .addba_set_resp_cmdid = WMI_ADDBA_SET_RESP_CMDID, + .send_singleamsdu_cmdid = WMI_SEND_SINGLEAMSDU_CMDID, + .sta_powersave_mode_cmdid = WMI_STA_POWERSAVE_MODE_CMDID, + .sta_powersave_param_cmdid = WMI_STA_POWERSAVE_PARAM_CMDID, + .sta_mimo_ps_mode_cmdid = WMI_STA_MIMO_PS_MODE_CMDID, + .pdev_dfs_enable_cmdid = WMI_PDEV_DFS_ENABLE_CMDID, + .pdev_dfs_disable_cmdid = WMI_PDEV_DFS_DISABLE_CMDID, + .roam_scan_mode = WMI_ROAM_SCAN_MODE, + .roam_scan_rssi_threshold = WMI_ROAM_SCAN_RSSI_THRESHOLD, + .roam_scan_period = WMI_ROAM_SCAN_PERIOD, + .roam_scan_rssi_change_threshold = WMI_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + .roam_ap_profile = WMI_ROAM_AP_PROFILE, + .ofl_scan_add_ap_profile = WMI_ROAM_AP_PROFILE, + .ofl_scan_remove_ap_profile = WMI_OFL_SCAN_REMOVE_AP_PROFILE, + .ofl_scan_period = WMI_OFL_SCAN_PERIOD, + .p2p_dev_set_device_info = WMI_P2P_DEV_SET_DEVICE_INFO, + .p2p_dev_set_discoverability = WMI_P2P_DEV_SET_DISCOVERABILITY, + .p2p_go_set_beacon_ie = WMI_P2P_GO_SET_BEACON_IE, + .p2p_go_set_probe_resp_ie = WMI_P2P_GO_SET_PROBE_RESP_IE, + .p2p_set_vendor_ie_data_cmdid = WMI_P2P_SET_VENDOR_IE_DATA_CMDID, + .ap_ps_peer_param_cmdid = WMI_AP_PS_PEER_PARAM_CMDID, + .ap_ps_peer_uapsd_coex_cmdid = WMI_AP_PS_PEER_UAPSD_COEX_CMDID, + .peer_rate_retry_sched_cmdid = WMI_PEER_RATE_RETRY_SCHED_CMDID, + .wlan_profile_trigger_cmdid = WMI_WLAN_PROFILE_TRIGGER_CMDID, + .wlan_profile_set_hist_intvl_cmdid = + WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + .wlan_profile_get_profile_data_cmdid = + WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + .wlan_profile_enable_profile_id_cmdid = + WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + .wlan_profile_list_profile_id_cmdid = + WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + .pdev_suspend_cmdid = WMI_PDEV_SUSPEND_CMDID, + .pdev_resume_cmdid = WMI_PDEV_RESUME_CMDID, + .add_bcn_filter_cmdid = WMI_ADD_BCN_FILTER_CMDID, + .rmv_bcn_filter_cmdid = WMI_RMV_BCN_FILTER_CMDID, + .wow_add_wake_pattern_cmdid = WMI_WOW_ADD_WAKE_PATTERN_CMDID, + .wow_del_wake_pattern_cmdid = WMI_WOW_DEL_WAKE_PATTERN_CMDID, + .wow_enable_disable_wake_event_cmdid = + WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + .wow_enable_cmdid = WMI_WOW_ENABLE_CMDID, + .wow_hostwakeup_from_sleep_cmdid = WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + .rtt_measreq_cmdid = WMI_RTT_MEASREQ_CMDID, + .rtt_tsf_cmdid = WMI_RTT_TSF_CMDID, + .vdev_spectral_scan_configure_cmdid = + WMI_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID, + .vdev_spectral_scan_enable_cmdid = WMI_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + .request_stats_cmdid = WMI_REQUEST_STATS_CMDID, + .set_arp_ns_offload_cmdid = WMI_SET_ARP_NS_OFFLOAD_CMDID, + .network_list_offload_config_cmdid = + WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID, + .gtk_offload_cmdid = WMI_GTK_OFFLOAD_CMDID, + .csa_offload_enable_cmdid = WMI_CSA_OFFLOAD_ENABLE_CMDID, + .csa_offload_chanswitch_cmdid = WMI_CSA_OFFLOAD_CHANSWITCH_CMDID, + .chatter_set_mode_cmdid = WMI_CHATTER_SET_MODE_CMDID, + .peer_tid_addba_cmdid = WMI_PEER_TID_ADDBA_CMDID, + .peer_tid_delba_cmdid = WMI_PEER_TID_DELBA_CMDID, + .sta_dtim_ps_method_cmdid = WMI_STA_DTIM_PS_METHOD_CMDID, + .sta_uapsd_auto_trig_cmdid = WMI_STA_UAPSD_AUTO_TRIG_CMDID, + .sta_keepalive_cmd = WMI_STA_KEEPALIVE_CMD, + .echo_cmdid = WMI_ECHO_CMDID, + .pdev_utf_cmdid = WMI_PDEV_UTF_CMDID, + .dbglog_cfg_cmdid = WMI_DBGLOG_CFG_CMDID, + .pdev_qvit_cmdid = WMI_PDEV_QVIT_CMDID, + .pdev_ftm_intg_cmdid = WMI_PDEV_FTM_INTG_CMDID, + .vdev_set_keepalive_cmdid = WMI_VDEV_SET_KEEPALIVE_CMDID, + .vdev_get_keepalive_cmdid = WMI_VDEV_GET_KEEPALIVE_CMDID, + .force_fw_hang_cmdid = WMI_FORCE_FW_HANG_CMDID, + .gpio_config_cmdid = WMI_GPIO_CONFIG_CMDID, + .gpio_output_cmdid = WMI_GPIO_OUTPUT_CMDID, +}; + +/* TODO: 10.X WMI cmd track */ + int ath10k_wmi_wait_for_service_ready(struct ath10k *ar) { int ret; @@ -64,7 +186,7 @@ static void ath10k_wmi_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) } static int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb, - enum wmi_cmd_id cmd_id) + u32 cmd_id) { struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); struct wmi_cmd_hdr *cmd_hdr; @@ -144,7 +266,7 @@ static void ath10k_wmi_op_ep_tx_credits(struct ath10k *ar) } static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, - enum wmi_cmd_id cmd_id) + u32 cmd_id) { int ret = -EINVAL; @@ -1055,7 +1177,7 @@ static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb) return 0; } -static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb) +static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; enum wmi_event_id id; @@ -1174,14 +1296,31 @@ static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb) dev_kfree_skb(skb); } +static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb) +{ + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + ath10k_warn("Firmware 10.X is not yet supported\n"); + else + ath10k_wmi_main_process_rx(ar, skb); +} + /* WMI Initialization functions */ int ath10k_wmi_attach(struct ath10k *ar) { + int ret = 0; + + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + ath10k_warn("Firmware 10.X is not yet supported\n"); + ret = -ENOTSUPP; + } else { + ar->wmi.cmd = &wmi_cmd_map; + } + init_completion(&ar->wmi.service_ready); init_completion(&ar->wmi.unified_ready); init_waitqueue_head(&ar->wmi.tx_credits_wq); - return 0; + return ret; } void ath10k_wmi_detach(struct ath10k *ar) @@ -1237,7 +1376,8 @@ int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x\n", rd, rd2g, rd5g, ctl2g, ctl5g); - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_REGDOMAIN_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_set_regdomain_cmdid); } int ath10k_wmi_pdev_set_channel(struct ath10k *ar, @@ -1267,7 +1407,8 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar, "wmi set channel mode %d freq %d\n", arg->mode, arg->freq); - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_CHANNEL_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_set_channel_cmdid); } int ath10k_wmi_pdev_suspend_target(struct ath10k *ar) @@ -1282,7 +1423,7 @@ int ath10k_wmi_pdev_suspend_target(struct ath10k *ar) cmd = (struct wmi_pdev_suspend_cmd *)skb->data; cmd->suspend_opt = WMI_PDEV_SUSPEND; - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SUSPEND_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid); } int ath10k_wmi_pdev_resume_target(struct ath10k *ar) @@ -1293,7 +1434,7 @@ int ath10k_wmi_pdev_resume_target(struct ath10k *ar) if (skb == NULL) return -ENOMEM; - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_RESUME_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_resume_cmdid); } int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id, @@ -1312,7 +1453,7 @@ int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id, ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n", id, value); - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_PARAM_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid); } int ath10k_wmi_cmd_init(struct ath10k *ar) @@ -1382,7 +1523,7 @@ int ath10k_wmi_cmd_init(struct ath10k *ar) memcpy(&cmd->resource_config, &config, sizeof(config)); ath10k_dbg(ATH10K_DBG_WMI, "wmi init\n"); - return ath10k_wmi_cmd_send(ar, buf, WMI_INIT_CMDID); + return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid); } static int ath10k_wmi_start_scan_calc_len(const struct wmi_start_scan_arg *arg) @@ -1543,7 +1684,7 @@ int ath10k_wmi_start_scan(struct ath10k *ar, } ath10k_dbg(ATH10K_DBG_WMI, "wmi start scan\n"); - return ath10k_wmi_cmd_send(ar, skb, WMI_START_SCAN_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->start_scan_cmdid); } void ath10k_wmi_start_scan_init(struct ath10k *ar, @@ -1603,7 +1744,7 @@ int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg) ath10k_dbg(ATH10K_DBG_WMI, "wmi stop scan reqid %d req_type %d vdev/scan_id %d\n", arg->req_id, arg->req_type, arg->u.scan_id); - return ath10k_wmi_cmd_send(ar, skb, WMI_STOP_SCAN_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->stop_scan_cmdid); } int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, @@ -1628,7 +1769,7 @@ int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, "WMI vdev create: id %d type %d subtype %d macaddr %pM\n", vdev_id, type, subtype, macaddr); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_CREATE_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_create_cmdid); } int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id) @@ -1646,20 +1787,20 @@ int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id) ath10k_dbg(ATH10K_DBG_WMI, "WMI vdev delete id %d\n", vdev_id); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DELETE_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_delete_cmdid); } static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, const struct wmi_vdev_start_request_arg *arg, - enum wmi_cmd_id cmd_id) + u32 cmd_id) { struct wmi_vdev_start_request_cmd *cmd; struct sk_buff *skb; const char *cmdname; u32 flags = 0; - if (cmd_id != WMI_VDEV_START_REQUEST_CMDID && - cmd_id != WMI_VDEV_RESTART_REQUEST_CMDID) + if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid && + cmd_id != ar->wmi.cmd->vdev_restart_request_cmdid) return -EINVAL; if (WARN_ON(arg->ssid && arg->ssid_len == 0)) return -EINVAL; @@ -1668,9 +1809,9 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid))) return -EINVAL; - if (cmd_id == WMI_VDEV_START_REQUEST_CMDID) + if (cmd_id == ar->wmi.cmd->vdev_start_request_cmdid) cmdname = "start"; - else if (cmd_id == WMI_VDEV_RESTART_REQUEST_CMDID) + else if (cmd_id == ar->wmi.cmd->vdev_restart_request_cmdid) cmdname = "restart"; else return -EINVAL; /* should not happen, we already check cmd_id */ @@ -1721,15 +1862,17 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, int ath10k_wmi_vdev_start(struct ath10k *ar, const struct wmi_vdev_start_request_arg *arg) { - return ath10k_wmi_vdev_start_restart(ar, arg, - WMI_VDEV_START_REQUEST_CMDID); + u32 cmd_id = ar->wmi.cmd->vdev_start_request_cmdid; + + return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id); } int ath10k_wmi_vdev_restart(struct ath10k *ar, const struct wmi_vdev_start_request_arg *arg) { - return ath10k_wmi_vdev_start_restart(ar, arg, - WMI_VDEV_RESTART_REQUEST_CMDID); + u32 cmd_id = ar->wmi.cmd->vdev_restart_request_cmdid; + + return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id); } int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id) @@ -1746,7 +1889,7 @@ int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id) ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_STOP_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_stop_cmdid); } int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid) @@ -1767,7 +1910,7 @@ int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid) "wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n", vdev_id, aid, bssid); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_UP_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_up_cmdid); } int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id) @@ -1785,7 +1928,7 @@ int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id) ath10k_dbg(ATH10K_DBG_WMI, "wmi mgmt vdev down id 0x%x\n", vdev_id); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DOWN_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_down_cmdid); } int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, @@ -1807,7 +1950,7 @@ int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, "wmi vdev id 0x%x set param %d value %d\n", vdev_id, param_id, param_value); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_SET_PARAM_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_set_param_cmdid); } int ath10k_wmi_vdev_install_key(struct ath10k *ar, @@ -1842,7 +1985,8 @@ int ath10k_wmi_vdev_install_key(struct ath10k *ar, ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev install key idx %d cipher %d len %d\n", arg->key_idx, arg->key_cipher, arg->key_len); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_INSTALL_KEY_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->vdev_install_key_cmdid); } int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, @@ -1862,7 +2006,7 @@ int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ATH10K_DBG_WMI, "wmi peer create vdev_id %d peer_addr %pM\n", vdev_id, peer_addr); - return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_CREATE_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_create_cmdid); } int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, @@ -1882,7 +2026,7 @@ int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ATH10K_DBG_WMI, "wmi peer delete vdev_id %d peer_addr %pM\n", vdev_id, peer_addr); - return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_DELETE_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_delete_cmdid); } int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, @@ -1903,7 +2047,7 @@ int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ATH10K_DBG_WMI, "wmi peer flush vdev_id %d peer_addr %pM tids %08x\n", vdev_id, peer_addr, tid_bitmap); - return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_FLUSH_TIDS_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_flush_tids_cmdid); } int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, @@ -1927,7 +2071,7 @@ int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, "wmi vdev %d peer 0x%pM set param %d value %d\n", vdev_id, peer_addr, param_id, param_value); - return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_SET_PARAM_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_set_param_cmdid); } int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, @@ -1948,7 +2092,8 @@ int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, "wmi set powersave id 0x%x mode %d\n", vdev_id, psmode); - return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_MODE_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->sta_powersave_mode_cmdid); } int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, @@ -1970,7 +2115,8 @@ int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ATH10K_DBG_WMI, "wmi sta ps param vdev_id 0x%x param %d value %d\n", vdev_id, param_id, value); - return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_PARAM_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->sta_powersave_param_cmdid); } int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, @@ -1996,7 +2142,8 @@ int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, "wmi ap ps param vdev_id 0x%X param %d value %d mac_addr %pM\n", vdev_id, param_id, value, mac); - return ath10k_wmi_cmd_send(ar, skb, WMI_AP_PS_PEER_PARAM_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->ap_ps_peer_param_cmdid); } int ath10k_wmi_scan_chan_list(struct ath10k *ar, @@ -2049,7 +2196,7 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar, ci->flags |= __cpu_to_le32(flags); } - return ath10k_wmi_cmd_send(ar, skb, WMI_SCAN_CHAN_LIST_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid); } int ath10k_wmi_peer_assoc(struct ath10k *ar, @@ -2108,7 +2255,7 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar, ath10k_dbg(ATH10K_DBG_WMI, "wmi peer assoc vdev %d addr %pM\n", arg->vdev_id, arg->addr); - return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid); } int ath10k_wmi_beacon_send_nowait(struct ath10k *ar, @@ -2128,7 +2275,7 @@ int ath10k_wmi_beacon_send_nowait(struct ath10k *ar, cmd->hdr.bcn_len = __cpu_to_le32(arg->bcn_len); memcpy(cmd->bcn, arg->bcn, arg->bcn_len); - return ath10k_wmi_cmd_send_nowait(ar, skb, WMI_BCN_TX_CMDID); + return ath10k_wmi_cmd_send_nowait(ar, skb, ar->wmi.cmd->bcn_tx_cmdid); } static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params, @@ -2159,7 +2306,8 @@ int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo); ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set wmm params\n"); - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_WMM_PARAMS_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_set_wmm_params_cmdid); } int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) @@ -2175,7 +2323,7 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) cmd->stats_id = __cpu_to_le32(stats_id); ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id); - return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->request_stats_cmdid); } int ath10k_wmi_force_fw_hang(struct ath10k *ar, @@ -2194,5 +2342,5 @@ int ath10k_wmi_force_fw_hang(struct ath10k *ar, ath10k_dbg(ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n", type, delay_ms); - return ath10k_wmi_cmd_send(ar, skb, WMI_FORCE_FW_HANG_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid); } diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 2c52c23107d..b8b446a9430 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -208,6 +208,118 @@ struct wmi_mac_addr { (c_macaddr)[5] = (((pwmi_mac_addr)->word1) >> 8) & 0xff; \ } while (0) +struct wmi_cmd_map { + u32 init_cmdid; + u32 start_scan_cmdid; + u32 stop_scan_cmdid; + u32 scan_chan_list_cmdid; + u32 scan_sch_prio_tbl_cmdid; + u32 pdev_set_regdomain_cmdid; + u32 pdev_set_channel_cmdid; + u32 pdev_set_param_cmdid; + u32 pdev_pktlog_enable_cmdid; + u32 pdev_pktlog_disable_cmdid; + u32 pdev_set_wmm_params_cmdid; + u32 pdev_set_ht_cap_ie_cmdid; + u32 pdev_set_vht_cap_ie_cmdid; + u32 pdev_set_dscp_tid_map_cmdid; + u32 pdev_set_quiet_mode_cmdid; + u32 pdev_green_ap_ps_enable_cmdid; + u32 pdev_get_tpc_config_cmdid; + u32 pdev_set_base_macaddr_cmdid; + u32 vdev_create_cmdid; + u32 vdev_delete_cmdid; + u32 vdev_start_request_cmdid; + u32 vdev_restart_request_cmdid; + u32 vdev_up_cmdid; + u32 vdev_stop_cmdid; + u32 vdev_down_cmdid; + u32 vdev_set_param_cmdid; + u32 vdev_install_key_cmdid; + u32 peer_create_cmdid; + u32 peer_delete_cmdid; + u32 peer_flush_tids_cmdid; + u32 peer_set_param_cmdid; + u32 peer_assoc_cmdid; + u32 peer_add_wds_entry_cmdid; + u32 peer_remove_wds_entry_cmdid; + u32 peer_mcast_group_cmdid; + u32 bcn_tx_cmdid; + u32 pdev_send_bcn_cmdid; + u32 bcn_tmpl_cmdid; + u32 bcn_filter_rx_cmdid; + u32 prb_req_filter_rx_cmdid; + u32 mgmt_tx_cmdid; + u32 prb_tmpl_cmdid; + u32 addba_clear_resp_cmdid; + u32 addba_send_cmdid; + u32 addba_status_cmdid; + u32 delba_send_cmdid; + u32 addba_set_resp_cmdid; + u32 send_singleamsdu_cmdid; + u32 sta_powersave_mode_cmdid; + u32 sta_powersave_param_cmdid; + u32 sta_mimo_ps_mode_cmdid; + u32 pdev_dfs_enable_cmdid; + u32 pdev_dfs_disable_cmdid; + u32 roam_scan_mode; + u32 roam_scan_rssi_threshold; + u32 roam_scan_period; + u32 roam_scan_rssi_change_threshold; + u32 roam_ap_profile; + u32 ofl_scan_add_ap_profile; + u32 ofl_scan_remove_ap_profile; + u32 ofl_scan_period; + u32 p2p_dev_set_device_info; + u32 p2p_dev_set_discoverability; + u32 p2p_go_set_beacon_ie; + u32 p2p_go_set_probe_resp_ie; + u32 p2p_set_vendor_ie_data_cmdid; + u32 ap_ps_peer_param_cmdid; + u32 ap_ps_peer_uapsd_coex_cmdid; + u32 peer_rate_retry_sched_cmdid; + u32 wlan_profile_trigger_cmdid; + u32 wlan_profile_set_hist_intvl_cmdid; + u32 wlan_profile_get_profile_data_cmdid; + u32 wlan_profile_enable_profile_id_cmdid; + u32 wlan_profile_list_profile_id_cmdid; + u32 pdev_suspend_cmdid; + u32 pdev_resume_cmdid; + u32 add_bcn_filter_cmdid; + u32 rmv_bcn_filter_cmdid; + u32 wow_add_wake_pattern_cmdid; + u32 wow_del_wake_pattern_cmdid; + u32 wow_enable_disable_wake_event_cmdid; + u32 wow_enable_cmdid; + u32 wow_hostwakeup_from_sleep_cmdid; + u32 rtt_measreq_cmdid; + u32 rtt_tsf_cmdid; + u32 vdev_spectral_scan_configure_cmdid; + u32 vdev_spectral_scan_enable_cmdid; + u32 request_stats_cmdid; + u32 set_arp_ns_offload_cmdid; + u32 network_list_offload_config_cmdid; + u32 gtk_offload_cmdid; + u32 csa_offload_enable_cmdid; + u32 csa_offload_chanswitch_cmdid; + u32 chatter_set_mode_cmdid; + u32 peer_tid_addba_cmdid; + u32 peer_tid_delba_cmdid; + u32 sta_dtim_ps_method_cmdid; + u32 sta_uapsd_auto_trig_cmdid; + u32 sta_keepalive_cmd; + u32 echo_cmdid; + u32 pdev_utf_cmdid; + u32 dbglog_cfg_cmdid; + u32 pdev_qvit_cmdid; + u32 pdev_ftm_intg_cmdid; + u32 vdev_set_keepalive_cmdid; + u32 vdev_get_keepalive_cmdid; + u32 force_fw_hang_cmdid; + u32 gpio_config_cmdid; + u32 gpio_output_cmdid; +}; + /* * wmi command groups. */ -- cgit v1.2.3 From b7e3adf907f92c2749d2a8b32bff950c2a90b3fb Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Thu, 26 Sep 2013 17:47:06 +0200 Subject: ath10k: add wmi_10x_ definitions Introduce the cmd and events definitions for 10.X FW API and make up the wmi_10x_cmd_map. This is the core of host-firmware WMI interface for 10.X FW branch. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 126 +++++++++++++++++++- drivers/net/wireless/ath/ath10k/wmi.h | 215 +++++++++++++++++++++++++++++++++- 2 files changed, 338 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 1296dbe7d17..cfb20824499 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -143,7 +143,127 @@ static struct wmi_cmd_map wmi_cmd_map = { .gpio_output_cmdid = WMI_GPIO_OUTPUT_CMDID, }; -/* TODO: 10.X WMI cmd track */ +/* 10.X WMI cmd track */ +static struct wmi_cmd_map wmi_10x_cmd_map = { + .init_cmdid = WMI_10X_INIT_CMDID, + .start_scan_cmdid = WMI_10X_START_SCAN_CMDID, + .stop_scan_cmdid = WMI_10X_STOP_SCAN_CMDID, + .scan_chan_list_cmdid = WMI_10X_SCAN_CHAN_LIST_CMDID, + .scan_sch_prio_tbl_cmdid = WMI_CMD_UNDEFINED, + .pdev_set_regdomain_cmdid = WMI_10X_PDEV_SET_REGDOMAIN_CMDID, + .pdev_set_channel_cmdid = WMI_10X_PDEV_SET_CHANNEL_CMDID, + .pdev_set_param_cmdid = WMI_10X_PDEV_SET_PARAM_CMDID, + .pdev_pktlog_enable_cmdid = WMI_10X_PDEV_PKTLOG_ENABLE_CMDID, + .pdev_pktlog_disable_cmdid = WMI_10X_PDEV_PKTLOG_DISABLE_CMDID, + .pdev_set_wmm_params_cmdid = WMI_10X_PDEV_SET_WMM_PARAMS_CMDID, + .pdev_set_ht_cap_ie_cmdid = WMI_10X_PDEV_SET_HT_CAP_IE_CMDID, + .pdev_set_vht_cap_ie_cmdid = WMI_10X_PDEV_SET_VHT_CAP_IE_CMDID, + .pdev_set_dscp_tid_map_cmdid = WMI_10X_PDEV_SET_DSCP_TID_MAP_CMDID, + .pdev_set_quiet_mode_cmdid = WMI_10X_PDEV_SET_QUIET_MODE_CMDID, + .pdev_green_ap_ps_enable_cmdid = WMI_10X_PDEV_GREEN_AP_PS_ENABLE_CMDID, + .pdev_get_tpc_config_cmdid = WMI_10X_PDEV_GET_TPC_CONFIG_CMDID, + .pdev_set_base_macaddr_cmdid = WMI_10X_PDEV_SET_BASE_MACADDR_CMDID, + .vdev_create_cmdid = WMI_10X_VDEV_CREATE_CMDID, + .vdev_delete_cmdid = WMI_10X_VDEV_DELETE_CMDID, + .vdev_start_request_cmdid = WMI_10X_VDEV_START_REQUEST_CMDID, + .vdev_restart_request_cmdid = WMI_10X_VDEV_RESTART_REQUEST_CMDID, + .vdev_up_cmdid = WMI_10X_VDEV_UP_CMDID, + .vdev_stop_cmdid = WMI_10X_VDEV_STOP_CMDID, + .vdev_down_cmdid = WMI_10X_VDEV_DOWN_CMDID, + .vdev_set_param_cmdid = WMI_10X_VDEV_SET_PARAM_CMDID, + .vdev_install_key_cmdid = WMI_10X_VDEV_INSTALL_KEY_CMDID, + .peer_create_cmdid = WMI_10X_PEER_CREATE_CMDID, + .peer_delete_cmdid = WMI_10X_PEER_DELETE_CMDID, + .peer_flush_tids_cmdid = WMI_10X_PEER_FLUSH_TIDS_CMDID, + .peer_set_param_cmdid = WMI_10X_PEER_SET_PARAM_CMDID, + .peer_assoc_cmdid = WMI_10X_PEER_ASSOC_CMDID, + .peer_add_wds_entry_cmdid = WMI_10X_PEER_ADD_WDS_ENTRY_CMDID, + .peer_remove_wds_entry_cmdid = WMI_10X_PEER_REMOVE_WDS_ENTRY_CMDID, + .peer_mcast_group_cmdid = WMI_10X_PEER_MCAST_GROUP_CMDID, + .bcn_tx_cmdid = WMI_10X_BCN_TX_CMDID, + .pdev_send_bcn_cmdid = WMI_10X_PDEV_SEND_BCN_CMDID, + .bcn_tmpl_cmdid = WMI_CMD_UNDEFINED, + .bcn_filter_rx_cmdid = WMI_10X_BCN_FILTER_RX_CMDID, + .prb_req_filter_rx_cmdid = WMI_10X_PRB_REQ_FILTER_RX_CMDID, + .mgmt_tx_cmdid = WMI_10X_MGMT_TX_CMDID, + .prb_tmpl_cmdid = WMI_CMD_UNDEFINED, + .addba_clear_resp_cmdid = WMI_10X_ADDBA_CLEAR_RESP_CMDID, + .addba_send_cmdid = WMI_10X_ADDBA_SEND_CMDID, + .addba_status_cmdid = WMI_10X_ADDBA_STATUS_CMDID, + .delba_send_cmdid = WMI_10X_DELBA_SEND_CMDID, + .addba_set_resp_cmdid = WMI_10X_ADDBA_SET_RESP_CMDID, + .send_singleamsdu_cmdid = WMI_10X_SEND_SINGLEAMSDU_CMDID, + .sta_powersave_mode_cmdid = WMI_10X_STA_POWERSAVE_MODE_CMDID, + .sta_powersave_param_cmdid = WMI_10X_STA_POWERSAVE_PARAM_CMDID, + .sta_mimo_ps_mode_cmdid = WMI_10X_STA_MIMO_PS_MODE_CMDID, + .pdev_dfs_enable_cmdid = WMI_10X_PDEV_DFS_ENABLE_CMDID, + .pdev_dfs_disable_cmdid = WMI_10X_PDEV_DFS_DISABLE_CMDID, + .roam_scan_mode = WMI_10X_ROAM_SCAN_MODE, + .roam_scan_rssi_threshold = WMI_10X_ROAM_SCAN_RSSI_THRESHOLD, + .roam_scan_period = WMI_10X_ROAM_SCAN_PERIOD, + .roam_scan_rssi_change_threshold = + WMI_10X_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + .roam_ap_profile = WMI_10X_ROAM_AP_PROFILE, + .ofl_scan_add_ap_profile = WMI_10X_OFL_SCAN_ADD_AP_PROFILE, + .ofl_scan_remove_ap_profile = WMI_10X_OFL_SCAN_REMOVE_AP_PROFILE, + .ofl_scan_period = WMI_10X_OFL_SCAN_PERIOD, + .p2p_dev_set_device_info = WMI_10X_P2P_DEV_SET_DEVICE_INFO, + .p2p_dev_set_discoverability = WMI_10X_P2P_DEV_SET_DISCOVERABILITY, + .p2p_go_set_beacon_ie = WMI_10X_P2P_GO_SET_BEACON_IE, + .p2p_go_set_probe_resp_ie = WMI_10X_P2P_GO_SET_PROBE_RESP_IE, + .p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNDEFINED, + .ap_ps_peer_param_cmdid = WMI_CMD_UNDEFINED, + .ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNDEFINED, + .peer_rate_retry_sched_cmdid = WMI_10X_PEER_RATE_RETRY_SCHED_CMDID, + .wlan_profile_trigger_cmdid = WMI_10X_WLAN_PROFILE_TRIGGER_CMDID, + .wlan_profile_set_hist_intvl_cmdid = + WMI_10X_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + .wlan_profile_get_profile_data_cmdid = + WMI_10X_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + .wlan_profile_enable_profile_id_cmdid = + WMI_10X_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + .wlan_profile_list_profile_id_cmdid = + WMI_10X_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + .pdev_suspend_cmdid = WMI_10X_PDEV_SUSPEND_CMDID, + .pdev_resume_cmdid = WMI_10X_PDEV_RESUME_CMDID, + .add_bcn_filter_cmdid = WMI_10X_ADD_BCN_FILTER_CMDID, + .rmv_bcn_filter_cmdid = WMI_10X_RMV_BCN_FILTER_CMDID, + .wow_add_wake_pattern_cmdid = WMI_10X_WOW_ADD_WAKE_PATTERN_CMDID, + .wow_del_wake_pattern_cmdid = WMI_10X_WOW_DEL_WAKE_PATTERN_CMDID, + .wow_enable_disable_wake_event_cmdid = + WMI_10X_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + .wow_enable_cmdid = WMI_10X_WOW_ENABLE_CMDID, + .wow_hostwakeup_from_sleep_cmdid = + WMI_10X_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + .rtt_measreq_cmdid = WMI_10X_RTT_MEASREQ_CMDID, + .rtt_tsf_cmdid = WMI_10X_RTT_TSF_CMDID, + .vdev_spectral_scan_configure_cmdid = + WMI_10X_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID, + .vdev_spectral_scan_enable_cmdid = + WMI_10X_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + .request_stats_cmdid = WMI_10X_REQUEST_STATS_CMDID, + .set_arp_ns_offload_cmdid = WMI_CMD_UNDEFINED, + .network_list_offload_config_cmdid = WMI_CMD_UNDEFINED, + .gtk_offload_cmdid = WMI_CMD_UNDEFINED, + .csa_offload_enable_cmdid = WMI_CMD_UNDEFINED, + .csa_offload_chanswitch_cmdid = WMI_CMD_UNDEFINED, + .chatter_set_mode_cmdid = WMI_CMD_UNDEFINED, + .peer_tid_addba_cmdid = WMI_CMD_UNDEFINED, + .peer_tid_delba_cmdid = WMI_CMD_UNDEFINED, + .sta_dtim_ps_method_cmdid = WMI_CMD_UNDEFINED, + .sta_uapsd_auto_trig_cmdid = WMI_CMD_UNDEFINED, + .sta_keepalive_cmd = WMI_CMD_UNDEFINED, + .echo_cmdid = WMI_10X_ECHO_CMDID, + .pdev_utf_cmdid = WMI_10X_PDEV_UTF_CMDID, + .dbglog_cfg_cmdid = WMI_10X_DBGLOG_CFG_CMDID, + .pdev_qvit_cmdid = WMI_10X_PDEV_QVIT_CMDID, + .pdev_ftm_intg_cmdid = WMI_CMD_UNDEFINED, + .vdev_set_keepalive_cmdid = WMI_CMD_UNDEFINED, + .vdev_get_keepalive_cmdid = WMI_CMD_UNDEFINED, + .force_fw_hang_cmdid = WMI_CMD_UNDEFINED, + .gpio_config_cmdid = WMI_10X_GPIO_CONFIG_CMDID, + .gpio_output_cmdid = WMI_10X_GPIO_OUTPUT_CMDID, +}; int ath10k_wmi_wait_for_service_ready(struct ath10k *ar) { @@ -1307,13 +1427,15 @@ static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb) /* WMI Initialization functions */ int ath10k_wmi_attach(struct ath10k *ar) { - int ret = 0; + int ret; if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { ath10k_warn("Firmware 10.X is not yet supported\n"); + ar->wmi.cmd = &wmi_10x_cmd_map; ret = -ENOTSUPP; } else { ar->wmi.cmd = &wmi_cmd_map; + ret = 0; } init_completion(&ar->wmi.service_ready); diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index b8b446a9430..d8414abb348 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -359,7 +359,9 @@ enum wmi_cmd_group { #define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1) #define WMI_EVT_GRP_START_ID(grp_id) (((grp_id) << 12) | 0x1) -/* Command IDs and commande events. */ +#define WMI_CMD_UNDEFINED 0 + +/* Command IDs and command events for MAIN FW. */ enum wmi_cmd_id { WMI_INIT_CMDID = 0x1, @@ -600,6 +602,217 @@ enum wmi_event_id { WMI_GPIO_INPUT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_GPIO), }; +/* Command IDs and command events for 10.X firmware */ +enum wmi_10x_cmd_id { + WMI_10X_START_CMDID = 0x9000, + WMI_10X_END_CMDID = 0x9FFF, + + /* initialize the wlan sub system */ + WMI_10X_INIT_CMDID, + + /* Scan specific commands */ + + WMI_10X_START_SCAN_CMDID = WMI_10X_START_CMDID, + WMI_10X_STOP_SCAN_CMDID, + WMI_10X_SCAN_CHAN_LIST_CMDID, + WMI_10X_ECHO_CMDID, + + /* PDEV(physical device) specific commands */ + WMI_10X_PDEV_SET_REGDOMAIN_CMDID, + WMI_10X_PDEV_SET_CHANNEL_CMDID, + WMI_10X_PDEV_SET_PARAM_CMDID, + WMI_10X_PDEV_PKTLOG_ENABLE_CMDID, + WMI_10X_PDEV_PKTLOG_DISABLE_CMDID, + WMI_10X_PDEV_SET_WMM_PARAMS_CMDID, + WMI_10X_PDEV_SET_HT_CAP_IE_CMDID, + WMI_10X_PDEV_SET_VHT_CAP_IE_CMDID, + WMI_10X_PDEV_SET_BASE_MACADDR_CMDID, + WMI_10X_PDEV_SET_DSCP_TID_MAP_CMDID, + WMI_10X_PDEV_SET_QUIET_MODE_CMDID, + WMI_10X_PDEV_GREEN_AP_PS_ENABLE_CMDID, + WMI_10X_PDEV_GET_TPC_CONFIG_CMDID, + + /* VDEV(virtual device) specific commands */ + WMI_10X_VDEV_CREATE_CMDID, + WMI_10X_VDEV_DELETE_CMDID, + WMI_10X_VDEV_START_REQUEST_CMDID, + WMI_10X_VDEV_RESTART_REQUEST_CMDID, + WMI_10X_VDEV_UP_CMDID, + WMI_10X_VDEV_STOP_CMDID, + WMI_10X_VDEV_DOWN_CMDID, + WMI_10X_VDEV_STANDBY_RESPONSE_CMDID, + WMI_10X_VDEV_RESUME_RESPONSE_CMDID, + WMI_10X_VDEV_SET_PARAM_CMDID, + WMI_10X_VDEV_INSTALL_KEY_CMDID, + + /* peer specific commands */ + WMI_10X_PEER_CREATE_CMDID, + WMI_10X_PEER_DELETE_CMDID, + WMI_10X_PEER_FLUSH_TIDS_CMDID, + WMI_10X_PEER_SET_PARAM_CMDID, + WMI_10X_PEER_ASSOC_CMDID, + WMI_10X_PEER_ADD_WDS_ENTRY_CMDID, + WMI_10X_PEER_REMOVE_WDS_ENTRY_CMDID, + WMI_10X_PEER_MCAST_GROUP_CMDID, + + /* beacon/management specific commands */ + + WMI_10X_BCN_TX_CMDID, + WMI_10X_BCN_PRB_TMPL_CMDID, + WMI_10X_BCN_FILTER_RX_CMDID, + WMI_10X_PRB_REQ_FILTER_RX_CMDID, + WMI_10X_MGMT_TX_CMDID, + + /* commands to directly control ba negotiation directly from host. */ + WMI_10X_ADDBA_CLEAR_RESP_CMDID, + WMI_10X_ADDBA_SEND_CMDID, + WMI_10X_ADDBA_STATUS_CMDID, + WMI_10X_DELBA_SEND_CMDID, + WMI_10X_ADDBA_SET_RESP_CMDID, + WMI_10X_SEND_SINGLEAMSDU_CMDID, + + /* Station power save specific config */ + WMI_10X_STA_POWERSAVE_MODE_CMDID, + WMI_10X_STA_POWERSAVE_PARAM_CMDID, + WMI_10X_STA_MIMO_PS_MODE_CMDID, + + /* set debug log config */ + WMI_10X_DBGLOG_CFG_CMDID, + + /* DFS-specific commands */ + WMI_10X_PDEV_DFS_ENABLE_CMDID, + WMI_10X_PDEV_DFS_DISABLE_CMDID, + + /* QVIT specific command id */ + WMI_10X_PDEV_QVIT_CMDID, + + /* Offload Scan and Roaming related commands */ + WMI_10X_ROAM_SCAN_MODE, + WMI_10X_ROAM_SCAN_RSSI_THRESHOLD, + WMI_10X_ROAM_SCAN_PERIOD, + WMI_10X_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + WMI_10X_ROAM_AP_PROFILE, + WMI_10X_OFL_SCAN_ADD_AP_PROFILE, + WMI_10X_OFL_SCAN_REMOVE_AP_PROFILE, + WMI_10X_OFL_SCAN_PERIOD, + + /* P2P specific commands */ + WMI_10X_P2P_DEV_SET_DEVICE_INFO, + WMI_10X_P2P_DEV_SET_DISCOVERABILITY, + WMI_10X_P2P_GO_SET_BEACON_IE, + WMI_10X_P2P_GO_SET_PROBE_RESP_IE, + + /* AP power save specific config */ + WMI_10X_AP_PS_PEER_PARAM_CMDID, + WMI_10X_AP_PS_PEER_UAPSD_COEX_CMDID, + + /* Rate-control specific commands */ + WMI_10X_PEER_RATE_RETRY_SCHED_CMDID, + + /* WLAN Profiling commands. */ + WMI_10X_WLAN_PROFILE_TRIGGER_CMDID, + WMI_10X_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + WMI_10X_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + WMI_10X_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + WMI_10X_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + + /* Suspend resume command Ids */ + WMI_10X_PDEV_SUSPEND_CMDID, + WMI_10X_PDEV_RESUME_CMDID, + + /* Beacon filter commands */ + WMI_10X_ADD_BCN_FILTER_CMDID, + WMI_10X_RMV_BCN_FILTER_CMDID, + + /* WOW Specific WMI commands*/ + WMI_10X_WOW_ADD_WAKE_PATTERN_CMDID, + WMI_10X_WOW_DEL_WAKE_PATTERN_CMDID, + WMI_10X_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + WMI_10X_WOW_ENABLE_CMDID, + WMI_10X_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + + /* RTT measurement related cmd */ + WMI_10X_RTT_MEASREQ_CMDID, + WMI_10X_RTT_TSF_CMDID, + + /* transmit beacon by value */ + WMI_10X_PDEV_SEND_BCN_CMDID, + + /* F/W stats */ + WMI_10X_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID, + WMI_10X_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + WMI_10X_REQUEST_STATS_CMDID, + + /* GPIO Configuration */ + WMI_10X_GPIO_CONFIG_CMDID, + WMI_10X_GPIO_OUTPUT_CMDID, + + WMI_10X_PDEV_UTF_CMDID = WMI_10X_END_CMDID - 1, +}; + +enum wmi_10x_event_id { + WMI_10X_SERVICE_READY_EVENTID = 0x8000, + WMI_10X_READY_EVENTID, + WMI_10X_START_EVENTID = 0x9000, + WMI_10X_END_EVENTID = 0x9FFF, + + /* Scan specific events */ + WMI_10X_SCAN_EVENTID = WMI_10X_START_EVENTID, + WMI_10X_ECHO_EVENTID, + WMI_10X_DEBUG_MESG_EVENTID, + WMI_10X_UPDATE_STATS_EVENTID, + + /* Instantaneous RSSI event */ + WMI_10X_INST_RSSI_STATS_EVENTID, + + /* VDEV specific events */ + WMI_10X_VDEV_START_RESP_EVENTID, + WMI_10X_VDEV_STANDBY_REQ_EVENTID, + WMI_10X_VDEV_RESUME_REQ_EVENTID, + WMI_10X_VDEV_STOPPED_EVENTID, + + /* peer specific events */ + WMI_10X_PEER_STA_KICKOUT_EVENTID, + + /* beacon/mgmt specific events */ + WMI_10X_HOST_SWBA_EVENTID, + WMI_10X_TBTTOFFSET_UPDATE_EVENTID, + WMI_10X_MGMT_RX_EVENTID, + + /* Channel stats event */ + WMI_10X_CHAN_INFO_EVENTID, + + /* PHY Error specific WMI event */ + WMI_10X_PHYERR_EVENTID, + + /* Roam event to trigger roaming on host */ + WMI_10X_ROAM_EVENTID, + + /* matching AP found from list of profiles */ + WMI_10X_PROFILE_MATCH, + + /* debug print message used for tracing FW code while debugging */ + WMI_10X_DEBUG_PRINT_EVENTID, + /* VI spoecific event */ + WMI_10X_PDEV_QVIT_EVENTID, + /* FW code profile data in response to profile request */ + WMI_10X_WLAN_PROFILE_DATA_EVENTID, + + /*RTT related event ID*/ + WMI_10X_RTT_MEASUREMENT_REPORT_EVENTID, + WMI_10X_TSF_MEASUREMENT_REPORT_EVENTID, + WMI_10X_RTT_ERROR_REPORT_EVENTID, + + WMI_10X_WOW_WAKEUP_HOST_EVENTID, + WMI_10X_DCS_INTERFERENCE_EVENTID, + + /* TPC config for the current operating channel */ + WMI_10X_PDEV_TPC_CONFIG_EVENTID, + + WMI_10X_GPIO_INPUT_EVENTID, + WMI_10X_PDEV_UTF_EVENTID = WMI_10X_END_EVENTID-1, +}; + enum wmi_phy_mode { MODE_11A = 0, /* 11a Mode */ MODE_11G = 1, /* 11b/g Mode */ -- cgit v1.2.3 From 553215592f148ec94151a11d4f13ebf8fb2ca516 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Thu, 26 Sep 2013 17:47:07 +0200 Subject: ath10k: warn if give WMI command is not supported This will show and make it easier to track the API differences in the new AP firmware. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index cfb20824499..8386050b101 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -390,6 +390,12 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, { int ret = -EINVAL; + if (cmd_id == WMI_CMD_UNDEFINED) { + ath10k_warn("wmi command %d is not supported by firmware\n", + cmd_id); + return ret; + } + wait_event_timeout(ar->wmi.tx_credits_wq, ({ /* try to send pending beacons first. they take priority */ ath10k_wmi_tx_beacons_nowait(ar); -- cgit v1.2.3 From 8a6618b00f280a35cef36f6156cb7f80b35553c3 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Thu, 26 Sep 2013 17:47:08 +0200 Subject: ath10k: implement WMI events handling frame for both firmwares We still use the same function handlers for both main and 10.X paths. Next step is to track down and make the split needed. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 131 +++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 8386050b101..ef80291737b 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1212,6 +1212,24 @@ static void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar, ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n"); } +static void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_INST_RSSI_STATS_EVENTID\n"); +} + +static void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_STANDBY_REQ_EVENTID\n"); +} + +static void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n"); +} + static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, struct sk_buff *skb) { @@ -1422,10 +1440,121 @@ static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb) dev_kfree_skb(skb); } +static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_cmd_hdr *cmd_hdr; + enum wmi_10x_event_id id; + u16 len; + + cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID); + + if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL) + return; + + len = skb->len; + + trace_ath10k_wmi_event(id, skb->data, skb->len); + + switch (id) { + case WMI_10X_MGMT_RX_EVENTID: + ath10k_wmi_event_mgmt_rx(ar, skb); + /* mgmt_rx() owns the skb now! */ + return; + case WMI_10X_SCAN_EVENTID: + ath10k_wmi_event_scan(ar, skb); + break; + case WMI_10X_CHAN_INFO_EVENTID: + ath10k_wmi_event_chan_info(ar, skb); + break; + case WMI_10X_ECHO_EVENTID: + ath10k_wmi_event_echo(ar, skb); + break; + case WMI_10X_DEBUG_MESG_EVENTID: + ath10k_wmi_event_debug_mesg(ar, skb); + break; + case WMI_10X_UPDATE_STATS_EVENTID: + ath10k_wmi_event_update_stats(ar, skb); + break; + case WMI_10X_VDEV_START_RESP_EVENTID: + ath10k_wmi_event_vdev_start_resp(ar, skb); + break; + case WMI_10X_VDEV_STOPPED_EVENTID: + ath10k_wmi_event_vdev_stopped(ar, skb); + break; + case WMI_10X_PEER_STA_KICKOUT_EVENTID: + ath10k_wmi_event_peer_sta_kickout(ar, skb); + break; + case WMI_10X_HOST_SWBA_EVENTID: + ath10k_wmi_event_host_swba(ar, skb); + break; + case WMI_10X_TBTTOFFSET_UPDATE_EVENTID: + ath10k_wmi_event_tbttoffset_update(ar, skb); + break; + case WMI_10X_PHYERR_EVENTID: + ath10k_wmi_event_phyerr(ar, skb); + break; + case WMI_10X_ROAM_EVENTID: + ath10k_wmi_event_roam(ar, skb); + break; + case WMI_10X_PROFILE_MATCH: + ath10k_wmi_event_profile_match(ar, skb); + break; + case WMI_10X_DEBUG_PRINT_EVENTID: + ath10k_wmi_event_debug_print(ar, skb); + break; + case WMI_10X_PDEV_QVIT_EVENTID: + ath10k_wmi_event_pdev_qvit(ar, skb); + break; + case WMI_10X_WLAN_PROFILE_DATA_EVENTID: + ath10k_wmi_event_wlan_profile_data(ar, skb); + break; + case WMI_10X_RTT_MEASUREMENT_REPORT_EVENTID: + ath10k_wmi_event_rtt_measurement_report(ar, skb); + break; + case WMI_10X_TSF_MEASUREMENT_REPORT_EVENTID: + ath10k_wmi_event_tsf_measurement_report(ar, skb); + break; + case WMI_10X_RTT_ERROR_REPORT_EVENTID: + ath10k_wmi_event_rtt_error_report(ar, skb); + break; + case WMI_10X_WOW_WAKEUP_HOST_EVENTID: + ath10k_wmi_event_wow_wakeup_host(ar, skb); + break; + case WMI_10X_DCS_INTERFERENCE_EVENTID: + ath10k_wmi_event_dcs_interference(ar, skb); + break; + case WMI_10X_PDEV_TPC_CONFIG_EVENTID: + ath10k_wmi_event_pdev_tpc_config(ar, skb); + break; + case WMI_10X_INST_RSSI_STATS_EVENTID: + ath10k_wmi_event_inst_rssi_stats(ar, skb); + break; + case WMI_10X_VDEV_STANDBY_REQ_EVENTID: + ath10k_wmi_event_vdev_standby_req(ar, skb); + break; + case WMI_10X_VDEV_RESUME_REQ_EVENTID: + ath10k_wmi_event_vdev_resume_req(ar, skb); + break; + case WMI_10X_SERVICE_READY_EVENTID: + ath10k_wmi_service_ready_event_rx(ar, skb); + break; + case WMI_10X_READY_EVENTID: + ath10k_wmi_ready_event_rx(ar, skb); + break; + default: + ath10k_warn("Unknown eventid: %d\n", id); + break; + } + + dev_kfree_skb(skb); +} + + static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb) { if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) - ath10k_warn("Firmware 10.X is not yet supported\n"); + ath10k_wmi_10x_process_rx(ar, skb); else ath10k_wmi_main_process_rx(ar, skb); } -- cgit v1.2.3 From 6f97d256b7c0c624f7cd5881beeabb5c7a3678b8 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Thu, 26 Sep 2013 17:47:09 +0200 Subject: ath10k: split ath10k_wmi_service_ready_event_rx Since the both firmwares we are going to support, have significantly different APIs (WMI and shared structures), it's easier to actually split the whole event handling functions, instead cutting them inside. The fork starts now on ath10k_wmi_process_rx(). Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 65 ++++++++++++++++++++++++++++++++++- drivers/net/wireless/ath/ath10k/wmi.h | 40 +++++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index ef80291737b..ed79d325b61 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1301,6 +1301,69 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, complete(&ar->wmi.service_ready); } +static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar, + struct sk_buff *skb) +{ + struct wmi_service_ready_event_10x *ev = (void *)skb->data; + + if (skb->len < sizeof(*ev)) { + ath10k_warn("Service ready event was %d B but expected %zu B. Wrong firmware version?\n", + skb->len, sizeof(*ev)); + return; + } + + ar->hw_min_tx_power = __le32_to_cpu(ev->hw_min_tx_power); + ar->hw_max_tx_power = __le32_to_cpu(ev->hw_max_tx_power); + ar->ht_cap_info = __le32_to_cpu(ev->ht_cap_info); + ar->vht_cap_info = __le32_to_cpu(ev->vht_cap_info); + ar->fw_version_major = + (__le32_to_cpu(ev->sw_version) & 0xff000000) >> 24; + ar->fw_version_minor = (__le32_to_cpu(ev->sw_version) & 0x00ffffff); + ar->phy_capability = __le32_to_cpu(ev->phy_capability); + ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains); + + if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) { + ath10k_warn("hardware advertises support for more spatial streams than it should (%d > %d)\n", + ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM); + ar->num_rf_chains = WMI_MAX_SPATIAL_STREAM; + } + + ar->ath_common.regulatory.current_rd = + __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd); + + ath10k_debug_read_service_map(ar, ev->wmi_service_bitmap, + sizeof(ev->wmi_service_bitmap)); + + if (strlen(ar->hw->wiphy->fw_version) == 0) { + snprintf(ar->hw->wiphy->fw_version, + sizeof(ar->hw->wiphy->fw_version), + "%u.%u", + ar->fw_version_major, + ar->fw_version_minor); + } + + /* FIXME: it probably should be better to support this. + TODO: Next patch introduce memory chunks. It's a must for 10.x FW */ + if (__le32_to_cpu(ev->num_mem_reqs) > 0) { + ath10k_warn("target requested %d memory chunks; ignoring\n", + __le32_to_cpu(ev->num_mem_reqs)); + } + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi event service ready sw_ver 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n", + __le32_to_cpu(ev->sw_version), + __le32_to_cpu(ev->abi_version), + __le32_to_cpu(ev->phy_capability), + __le32_to_cpu(ev->ht_cap_info), + __le32_to_cpu(ev->vht_cap_info), + __le32_to_cpu(ev->vht_supp_mcs), + __le32_to_cpu(ev->sys_cap_info), + __le32_to_cpu(ev->num_mem_reqs), + __le32_to_cpu(ev->num_rf_chains)); + + complete(&ar->wmi.service_ready); +} + static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_ready_event *ev = (struct wmi_ready_event *)skb->data; @@ -1537,7 +1600,7 @@ static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb) ath10k_wmi_event_vdev_resume_req(ar, skb); break; case WMI_10X_SERVICE_READY_EVENTID: - ath10k_wmi_service_ready_event_rx(ar, skb); + ath10k_wmi_10x_service_ready_event_rx(ar, skb); break; case WMI_10X_READY_EVENTID: ath10k_wmi_ready_event_rx(ar, skb); diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index d8414abb348..a0cfdfd87fa 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -1130,6 +1130,46 @@ struct wmi_service_ready_event { struct wlan_host_mem_req mem_reqs[1]; } __packed; +/* This is the definition from 10.X firmware branch */ +struct wmi_service_ready_event_10x { + __le32 sw_version; + __le32 abi_version; + + /* WMI_PHY_CAPABILITY */ + __le32 phy_capability; + + /* Maximum number of frag table entries that SW will populate less 1 */ + __le32 max_frag_entry; + __le32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE]; + __le32 num_rf_chains; + + /* + * The following field is only valid for service type + * WMI_SERVICE_11AC + */ + __le32 ht_cap_info; /* WMI HT Capability */ + __le32 vht_cap_info; /* VHT capability info field of 802.11ac */ + __le32 vht_supp_mcs; /* VHT Supported MCS Set field Rx/Tx same */ + __le32 hw_min_tx_power; + __le32 hw_max_tx_power; + + struct hal_reg_capabilities hal_reg_capabilities; + + __le32 sys_cap_info; + __le32 min_pkt_size_enable; /* Enterprise mode short pkt enable */ + + /* + * request to host to allocate a chuck of memory and pss it down to FW + * via WM_INIT. FW uses this as FW extesnsion memory for saving its + * data structures. Only valid for low latency interfaces like PCIE + * where FW can access this memory directly (or) by DMA. + */ + __le32 num_mem_reqs; + + struct wlan_host_mem_req mem_reqs[1]; +} __packed; + + #define WMI_SERVICE_READY_TIMEOUT_HZ (5*HZ) #define WMI_UNIFIED_READY_TIMEOUT_HZ (5*HZ) -- cgit v1.2.3 From 08ba7b6b6f4bd6a912d0b6825f4e2d4e7fb4ddac Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Thu, 26 Sep 2013 17:47:10 +0200 Subject: ath10k: drop the fw versioning sanity check It was corrupted and leading to compilation warning core.c: In function 'ath10k_check_fw_version': core.c:79: warning: comparison is always true due to limited range of data type Since we are going to support new FW track in the driver, the sanity check for fw version number would be an overkill then. This is just for information purposes anyway. Reported-by: Geert Uytterhoeven Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 25 ------------------------- drivers/net/wireless/ath/ath10k/hw.h | 6 ------ 2 files changed, 31 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 76906d5a082..31860a60c53 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -59,27 +59,6 @@ static void ath10k_send_suspend_complete(struct ath10k *ar) wake_up(&ar->event_queue); } -static int ath10k_check_fw_version(struct ath10k *ar) -{ - char version[32]; - - if (ar->fw_version_major >= SUPPORTED_FW_MAJOR && - ar->fw_version_minor >= SUPPORTED_FW_MINOR && - ar->fw_version_release >= SUPPORTED_FW_RELEASE && - ar->fw_version_build >= SUPPORTED_FW_BUILD) - return 0; - - snprintf(version, sizeof(version), "%u.%u.%u.%u", - SUPPORTED_FW_MAJOR, SUPPORTED_FW_MINOR, - SUPPORTED_FW_RELEASE, SUPPORTED_FW_BUILD); - - ath10k_warn("WARNING: Firmware version %s is not officially supported.\n", - ar->hw->wiphy->fw_version); - ath10k_warn("Please upgrade to version %s (or newer)\n", version); - - return 0; -} - static int ath10k_init_connect_htc(struct ath10k *ar) { int status; @@ -616,10 +595,6 @@ int ath10k_core_start(struct ath10k *ar) ath10k_info("firmware %s booted\n", ar->hw->wiphy->fw_version); - status = ath10k_check_fw_version(ar); - if (status) - goto err_disconnect_htc; - status = ath10k_wmi_cmd_init(ar); if (status) { ath10k_err("could not send WMI init command (%d)\n", status); diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 8c1be768592..66e721a1afd 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -20,12 +20,6 @@ #include "targaddrs.h" -/* Supported FW version */ -#define SUPPORTED_FW_MAJOR 1 -#define SUPPORTED_FW_MINOR 0 -#define SUPPORTED_FW_RELEASE 0 -#define SUPPORTED_FW_BUILD 636 - /* QCA988X 1.0 definitions (unsupported) */ #define QCA988X_HW_1_0_CHIP_ID_REV 0x0 -- cgit v1.2.3 From b3effe61a1a3b8f20fa4b4d30c4390a6b81a6fc2 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Thu, 26 Sep 2013 17:47:11 +0200 Subject: ath10k: implement host memory chunks 10.X firmware can request a memory pool from host to offload it's own resources. This is a feature designed especially for AP mode where the target has to deal with large number of peers. So we allocate and map a consistent DMA memory which FW can use to store e.g. peer rate contol maps. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 12 ++++ drivers/net/wireless/ath/ath10k/wmi.c | 126 ++++++++++++++++++++++++++++++--- drivers/net/wireless/ath/ath10k/wmi.h | 3 + 3 files changed, 133 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index acfee7c51b9..e2a2658cf84 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -102,12 +102,24 @@ struct ath10k_bmi { bool done_sent; }; +#define ATH10K_MAX_MEM_REQS 16 + +struct ath10k_mem_chunk { + void *vaddr; + dma_addr_t paddr; + u32 len; + u32 req_id; +}; + struct ath10k_wmi { enum ath10k_htc_ep_id eid; struct completion service_ready; struct completion unified_ready; wait_queue_head_t tx_credits_wq; struct wmi_cmd_map *cmd; + + u32 num_mem_chunks; + struct ath10k_mem_chunk mem_chunks[ATH10K_MAX_MEM_REQS]; }; struct ath10k_peer_stat { diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index ed79d325b61..97ba23e78ab 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1230,6 +1230,37 @@ static void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n"); } +static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id, + u32 num_units, u32 unit_len) +{ + dma_addr_t paddr; + u32 pool_size; + int idx = ar->wmi.num_mem_chunks; + + pool_size = num_units * round_up(unit_len, 4); + + if (!pool_size) + return -EINVAL; + + ar->wmi.mem_chunks[idx].vaddr = dma_alloc_coherent(ar->dev, + pool_size, + &paddr, + GFP_ATOMIC); + if (!ar->wmi.mem_chunks[idx].vaddr) { + ath10k_warn("failed to allocate memory chunk\n"); + return -ENOMEM; + } + + memset(ar->wmi.mem_chunks[idx].vaddr, 0, pool_size); + + ar->wmi.mem_chunks[idx].paddr = paddr; + ar->wmi.mem_chunks[idx].len = pool_size; + ar->wmi.mem_chunks[idx].req_id = req_id; + ar->wmi.num_mem_chunks++; + + return 0; +} + static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, struct sk_buff *skb) { @@ -1304,6 +1335,8 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar, struct sk_buff *skb) { + u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i; + int ret; struct wmi_service_ready_event_10x *ev = (void *)skb->data; if (skb->len < sizeof(*ev)) { @@ -1342,13 +1375,50 @@ static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar, ar->fw_version_minor); } - /* FIXME: it probably should be better to support this. - TODO: Next patch introduce memory chunks. It's a must for 10.x FW */ - if (__le32_to_cpu(ev->num_mem_reqs) > 0) { - ath10k_warn("target requested %d memory chunks; ignoring\n", - __le32_to_cpu(ev->num_mem_reqs)); + num_mem_reqs = __le32_to_cpu(ev->num_mem_reqs); + + if (num_mem_reqs > ATH10K_MAX_MEM_REQS) { + ath10k_warn("requested memory chunks number (%d) exceeds the limit\n", + num_mem_reqs); + return; } + if (!num_mem_reqs) + goto exit; + + ath10k_dbg(ATH10K_DBG_WMI, "firmware has requested %d memory chunks\n", + num_mem_reqs); + + for (i = 0; i < num_mem_reqs; ++i) { + req_id = __le32_to_cpu(ev->mem_reqs[i].req_id); + num_units = __le32_to_cpu(ev->mem_reqs[i].num_units); + unit_size = __le32_to_cpu(ev->mem_reqs[i].unit_size); + num_unit_info = __le32_to_cpu(ev->mem_reqs[i].num_unit_info); + + if (num_unit_info & NUM_UNITS_IS_NUM_PEERS) + /* number of units to allocate is number of + * peers, 1 extra for self peer on target */ + /* this needs to be tied, host and target + * can get out of sync */ + num_units = TARGET_NUM_PEERS + 1; + else if (num_unit_info & NUM_UNITS_IS_NUM_VDEVS) + num_units = TARGET_NUM_VDEVS + 1; + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi mem_req_id %d num_units %d num_unit_info %d unit size %d actual units %d\n", + req_id, + __le32_to_cpu(ev->mem_reqs[i].num_units), + num_unit_info, + unit_size, + num_units); + + ret = ath10k_wmi_alloc_host_mem(ar, req_id, num_units, + unit_size); + if (ret) + return; + } + +exit: ath10k_dbg(ATH10K_DBG_WMI, "wmi event service ready sw_ver 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n", __le32_to_cpu(ev->sw_version), @@ -1645,6 +1715,17 @@ int ath10k_wmi_attach(struct ath10k *ar) void ath10k_wmi_detach(struct ath10k *ar) { + int i; + + /* free the host memory chunks requested by firmware */ + for (i = 0; i < ar->wmi.num_mem_chunks; i++) { + dma_free_coherent(ar->dev, + ar->wmi.mem_chunks[i].len, + ar->wmi.mem_chunks[i].vaddr, + ar->wmi.mem_chunks[i].paddr); + } + + ar->wmi.num_mem_chunks = 0; } int ath10k_wmi_connect_htc_service(struct ath10k *ar) @@ -1781,7 +1862,8 @@ int ath10k_wmi_cmd_init(struct ath10k *ar) struct wmi_init_cmd *cmd; struct sk_buff *buf; struct wmi_resource_config config = {}; - u32 val; + u32 len, val; + int i; config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS); config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS); @@ -1834,12 +1916,40 @@ int ath10k_wmi_cmd_init(struct ath10k *ar) config.num_msdu_desc = __cpu_to_le32(TARGET_NUM_MSDU_DESC); config.max_frag_entries = __cpu_to_le32(TARGET_MAX_FRAG_ENTRIES); - buf = ath10k_wmi_alloc_skb(sizeof(*cmd)); + len = sizeof(*cmd) + + (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks); + + buf = ath10k_wmi_alloc_skb(len); if (!buf) return -ENOMEM; cmd = (struct wmi_init_cmd *)buf->data; - cmd->num_host_mem_chunks = 0; + + if (ar->wmi.num_mem_chunks == 0) { + cmd->num_host_mem_chunks = 0; + goto out; + } + + ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n", + __cpu_to_le32(ar->wmi.num_mem_chunks)); + + cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks); + + for (i = 0; i < ar->wmi.num_mem_chunks; i++) { + cmd->host_mem_chunks[i].ptr = + __cpu_to_le32(ar->wmi.mem_chunks[i].paddr); + cmd->host_mem_chunks[i].size = + __cpu_to_le32(ar->wmi.mem_chunks[i].len); + cmd->host_mem_chunks[i].req_id = + __cpu_to_le32(ar->wmi.mem_chunks[i].req_id); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi chunk %d len %d requested, addr 0x%x\n", + i, + cmd->host_mem_chunks[i].size, + cmd->host_mem_chunks[i].ptr); + } +out: memcpy(&cmd->resource_config, &config, sizeof(config)); ath10k_dbg(ATH10K_DBG_WMI, "wmi init\n"); diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index a0cfdfd87fa..56339d2e338 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -1377,6 +1377,9 @@ struct wmi_resource_config { __le32 max_frag_entries; } __packed; +#define NUM_UNITS_IS_NUM_VDEVS 0x1 +#define NUM_UNITS_IS_NUM_PEERS 0x2 + /* strucutre describing host memory chunk. */ struct host_memory_chunk { /* id of the request that is passed up in service ready */ -- cgit v1.2.3 From 5e00d31a0fb74c36f3b174ff0d4914cf09016e6f Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Thu, 26 Sep 2013 17:47:12 +0200 Subject: ath10k: bring back the WMI path for mgmt frames This is still the only way to submit mgmt frames in case of 10.X firmware. This patch introduces wmi_mgmt_tx queue, because of the fact WMI command can block. This is a problem for ath10k_tx_htt(), since it's called from atomic context. The skb queue and worker are introduced to move the mgmt frame handling out of .tx callback context and not block. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 3 ++ drivers/net/wireless/ath/ath10k/core.h | 10 ++++- drivers/net/wireless/ath/ath10k/htt_tx.c | 4 +- drivers/net/wireless/ath/ath10k/mac.c | 70 +++++++++++++++++++++++++++----- drivers/net/wireless/ath/ath10k/mac.h | 2 + drivers/net/wireless/ath/ath10k/wmi.c | 51 +++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/wmi.h | 1 + 7 files changed, 128 insertions(+), 13 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 31860a60c53..bd74dacad1e 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -520,6 +520,9 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work); skb_queue_head_init(&ar->offchan_tx_queue); + INIT_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work); + skb_queue_head_init(&ar->wmi_mgmt_tx_queue); + init_waitqueue_head(&ar->event_queue); INIT_WORK(&ar->restart_work, ath10k_core_restart); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index e2a2658cf84..984db115cb8 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -43,15 +43,17 @@ /* Antenna noise floor */ #define ATH10K_DEFAULT_NOISE_FLOOR -95 +#define ATH10K_MAX_NUM_MGMT_PENDING 16 + struct ath10k; struct ath10k_skb_cb { dma_addr_t paddr; bool is_mapped; bool is_aborted; + u8 vdev_id; struct { - u8 vdev_id; u8 tid; bool is_offchan; @@ -284,6 +286,9 @@ enum ath10k_fw_features { /* firmware from 10X branch */ ATH10K_FW_FEATURE_WMI_10X = 1, + /* firmware support tx frame management over WMI, otherwise it's HTT */ + ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX = 2, + /* keep last */ ATH10K_FW_FEATURE_COUNT, }; @@ -393,6 +398,9 @@ struct ath10k { struct completion offchan_tx_completed; struct sk_buff *offchan_tx_skb; + struct work_struct wmi_mgmt_tx_work; + struct sk_buff_head wmi_mgmt_tx_queue; + enum ath10k_state state; struct work_struct restart_work; diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 3b93c6a01c6..d9335e9d0d0 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -308,7 +308,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) struct sk_buff *txdesc = NULL; struct htt_cmd *cmd; struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); - u8 vdev_id = skb_cb->htt.vdev_id; + u8 vdev_id = skb_cb->vdev_id; int len = 0; int msdu_id = -1; int res; @@ -384,7 +384,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); struct sk_buff *txdesc = NULL; bool use_frags; - u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id; + u8 vdev_id = ATH10K_SKB_CB(msdu)->vdev_id; u8 tid; int prefetch_len, desc_len; int msdu_id = -1; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index df4d29981c4..f0bc707257a 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1486,7 +1486,7 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb) static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - int ret; + int ret = 0; if (ar->htt.target_version_major >= 3) { /* Since HTT 3.0 there is no separate mgmt tx command */ @@ -1494,16 +1494,32 @@ static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb) goto exit; } - if (ieee80211_is_mgmt(hdr->frame_control)) - ret = ath10k_htt_mgmt_tx(&ar->htt, skb); - else if (ieee80211_is_nullfunc(hdr->frame_control)) + if (ieee80211_is_mgmt(hdr->frame_control)) { + if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX, + ar->fw_features)) { + if (skb_queue_len(&ar->wmi_mgmt_tx_queue) >= + ATH10K_MAX_NUM_MGMT_PENDING) { + ath10k_warn("wmi mgmt_tx queue limit reached\n"); + ret = -EBUSY; + goto exit; + } + + skb_queue_tail(&ar->wmi_mgmt_tx_queue, skb); + ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work); + } else { + ret = ath10k_htt_mgmt_tx(&ar->htt, skb); + } + } else if (!test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX, + ar->fw_features) && + ieee80211_is_nullfunc(hdr->frame_control)) { /* FW does not report tx status properly for NullFunc frames * unless they are sent through mgmt tx path. mac80211 sends - * those frames when it detects link/beacon loss and depends on - * the tx status to be correct. */ + * those frames when it detects link/beacon loss and depends + * on the tx status to be correct. */ ret = ath10k_htt_mgmt_tx(&ar->htt, skb); - else + } else { ret = ath10k_htt_tx(&ar->htt, skb); + } exit: if (ret) { @@ -1554,7 +1570,7 @@ void ath10k_offchan_tx_work(struct work_struct *work) hdr = (struct ieee80211_hdr *)skb->data; peer_addr = ieee80211_get_DA(hdr); - vdev_id = ATH10K_SKB_CB(skb)->htt.vdev_id; + vdev_id = ATH10K_SKB_CB(skb)->vdev_id; spin_lock_bh(&ar->data_lock); peer = ath10k_peer_find(ar, vdev_id, peer_addr); @@ -1596,6 +1612,36 @@ void ath10k_offchan_tx_work(struct work_struct *work) } } +void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar) +{ + struct sk_buff *skb; + + for (;;) { + skb = skb_dequeue(&ar->wmi_mgmt_tx_queue); + if (!skb) + break; + + ieee80211_free_txskb(ar->hw, skb); + } +} + +void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work) +{ + struct ath10k *ar = container_of(work, struct ath10k, wmi_mgmt_tx_work); + struct sk_buff *skb; + int ret; + + for (;;) { + skb = skb_dequeue(&ar->wmi_mgmt_tx_queue); + if (!skb) + break; + + ret = ath10k_wmi_mgmt_tx(ar, skb); + if (ret) + ath10k_warn("wmi mgmt_tx failed (%d)\n", ret); + } +} + /************/ /* Scanning */ /************/ @@ -1754,14 +1800,14 @@ static void ath10k_tx(struct ieee80211_hw *hw, ath10k_tx_h_seq_no(skb); } + ATH10K_SKB_CB(skb)->vdev_id = vdev_id; ATH10K_SKB_CB(skb)->htt.is_offchan = false; - ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id; ATH10K_SKB_CB(skb)->htt.tid = tid; if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) { spin_lock_bh(&ar->data_lock); ATH10K_SKB_CB(skb)->htt.is_offchan = true; - ATH10K_SKB_CB(skb)->htt.vdev_id = ar->scan.vdev_id; + ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id; spin_unlock_bh(&ar->data_lock); ath10k_dbg(ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb); @@ -1783,6 +1829,7 @@ void ath10k_halt(struct ath10k *ar) del_timer_sync(&ar->scan.timeout); ath10k_offchan_tx_purge(ar); + ath10k_mgmt_over_wmi_tx_purge(ar); ath10k_peer_cleanup_all(ar); ath10k_core_stop(ar); ath10k_hif_power_down(ar); @@ -1859,7 +1906,10 @@ static void ath10k_stop(struct ieee80211_hw *hw) ar->state = ATH10K_STATE_OFF; mutex_unlock(&ar->conf_mutex); + ath10k_mgmt_over_wmi_tx_purge(ar); + cancel_work_sync(&ar->offchan_tx_work); + cancel_work_sync(&ar->wmi_mgmt_tx_work); cancel_work_sync(&ar->restart_work); } diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h index 6fce9bfb19a..ba1021997b8 100644 --- a/drivers/net/wireless/ath/ath10k/mac.h +++ b/drivers/net/wireless/ath/ath10k/mac.h @@ -34,6 +34,8 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id); void ath10k_reset_scan(unsigned long ptr); void ath10k_offchan_tx_purge(struct ath10k *ar); void ath10k_offchan_tx_work(struct work_struct *work); +void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar); +void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work); void ath10k_halt(struct ath10k *ar); static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 97ba23e78ab..8c223671ecc 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -410,6 +410,57 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, return ret; } +int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) +{ + int ret = 0; + struct wmi_mgmt_tx_cmd *cmd; + struct ieee80211_hdr *hdr; + struct sk_buff *wmi_skb; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + int len; + u16 fc; + + hdr = (struct ieee80211_hdr *)skb->data; + fc = le16_to_cpu(hdr->frame_control); + + if (WARN_ON_ONCE(!ieee80211_is_mgmt(hdr->frame_control))) + return -EINVAL; + + len = sizeof(cmd->hdr) + skb->len; + len = round_up(len, 4); + + wmi_skb = ath10k_wmi_alloc_skb(len); + if (!wmi_skb) + return -ENOMEM; + + cmd = (struct wmi_mgmt_tx_cmd *)wmi_skb->data; + + cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(skb)->vdev_id); + cmd->hdr.tx_rate = 0; + cmd->hdr.tx_power = 0; + cmd->hdr.buf_len = __cpu_to_le32((u32)(skb->len)); + + memcpy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr), ETH_ALEN); + memcpy(cmd->buf, skb->data, skb->len); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n", + wmi_skb, wmi_skb->len, fc & IEEE80211_FCTL_FTYPE, + fc & IEEE80211_FCTL_STYPE); + + /* Send the management frame buffer to the target */ + ret = ath10k_wmi_cmd_send(ar, wmi_skb, ar->wmi.cmd->mgmt_tx_cmdid); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + /* TODO: report tx status to mac80211 - temporary just ACK */ + info->flags |= IEEE80211_TX_STAT_ACK; + ieee80211_tx_status_irqsafe(ar->hw, skb); + + return ret; +} + static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb) { struct wmi_scan_event *event = (struct wmi_scan_event *)skb->data; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 56339d2e338..1c515d68228 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -3483,5 +3483,6 @@ int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id); int ath10k_wmi_force_fw_hang(struct ath10k *ar, enum wmi_force_fw_hang_type type, u32 delay_ms); +int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb); #endif /* _WMI_H_ */ -- cgit v1.2.3 From 12b2b9e33a58ff51a1a41a90e976a863736cc863 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Thu, 26 Sep 2013 17:47:13 +0200 Subject: ath10k: split wmi_cmd_init path Due to API differences in initialization structures for main and 10.x firmwares we need to split the wmi_init_cmd and wmi_resource_config structures. This will be usefull also when setting the correct TARGET values, like: number of peers, vdevs, pdevs etc. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 105 +++++++++++++++++- drivers/net/wireless/ath/ath10k/wmi.h | 195 ++++++++++++++++++++++++++++++++++ 2 files changed, 299 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 8c223671ecc..71ad1b8868b 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1908,7 +1908,7 @@ int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id, return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid); } -int ath10k_wmi_cmd_init(struct ath10k *ar) +static int ath10k_wmi_main_cmd_init(struct ath10k *ar) { struct wmi_init_cmd *cmd; struct sk_buff *buf; @@ -2007,6 +2007,109 @@ out: return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid); } +static int ath10k_wmi_10x_cmd_init(struct ath10k *ar) +{ + struct wmi_init_cmd_10x *cmd; + struct sk_buff *buf; + struct wmi_resource_config_10x config = {}; + u32 len, val; + int i; + + config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS); + config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS); + config.num_peer_keys = __cpu_to_le32(TARGET_NUM_PEER_KEYS); + config.num_tids = __cpu_to_le32(TARGET_NUM_TIDS); + config.ast_skid_limit = __cpu_to_le32(TARGET_AST_SKID_LIMIT); + config.tx_chain_mask = __cpu_to_le32(TARGET_TX_CHAIN_MASK); + config.rx_chain_mask = __cpu_to_le32(TARGET_RX_CHAIN_MASK); + config.rx_timeout_pri_vo = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_vi = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_be = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_bk = __cpu_to_le32(TARGET_RX_TIMEOUT_HI_PRI); + config.rx_decap_mode = __cpu_to_le32(TARGET_RX_DECAP_MODE); + + config.scan_max_pending_reqs = + __cpu_to_le32(TARGET_SCAN_MAX_PENDING_REQS); + + config.bmiss_offload_max_vdev = + __cpu_to_le32(TARGET_BMISS_OFFLOAD_MAX_VDEV); + + config.roam_offload_max_vdev = + __cpu_to_le32(TARGET_ROAM_OFFLOAD_MAX_VDEV); + + config.roam_offload_max_ap_profiles = + __cpu_to_le32(TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES); + + config.num_mcast_groups = __cpu_to_le32(TARGET_NUM_MCAST_GROUPS); + config.num_mcast_table_elems = + __cpu_to_le32(TARGET_NUM_MCAST_TABLE_ELEMS); + + config.mcast2ucast_mode = __cpu_to_le32(TARGET_MCAST2UCAST_MODE); + config.tx_dbg_log_size = __cpu_to_le32(TARGET_TX_DBG_LOG_SIZE); + config.num_wds_entries = __cpu_to_le32(TARGET_NUM_WDS_ENTRIES); + config.dma_burst_size = __cpu_to_le32(TARGET_DMA_BURST_SIZE); + config.mac_aggr_delim = __cpu_to_le32(TARGET_MAC_AGGR_DELIM); + + val = TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK; + config.rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(val); + + config.vow_config = __cpu_to_le32(TARGET_VOW_CONFIG); + + config.num_msdu_desc = __cpu_to_le32(TARGET_NUM_MSDU_DESC); + config.max_frag_entries = __cpu_to_le32(TARGET_MAX_FRAG_ENTRIES); + + len = sizeof(*cmd) + + (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks); + + buf = ath10k_wmi_alloc_skb(len); + if (!buf) + return -ENOMEM; + + cmd = (struct wmi_init_cmd_10x *)buf->data; + + if (ar->wmi.num_mem_chunks == 0) { + cmd->num_host_mem_chunks = 0; + goto out; + } + + ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n", + __cpu_to_le32(ar->wmi.num_mem_chunks)); + + cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks); + + for (i = 0; i < ar->wmi.num_mem_chunks; i++) { + cmd->host_mem_chunks[i].ptr = + __cpu_to_le32(ar->wmi.mem_chunks[i].paddr); + cmd->host_mem_chunks[i].size = + __cpu_to_le32(ar->wmi.mem_chunks[i].len); + cmd->host_mem_chunks[i].req_id = + __cpu_to_le32(ar->wmi.mem_chunks[i].req_id); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi chunk %d len %d requested, addr 0x%x\n", + i, + cmd->host_mem_chunks[i].size, + cmd->host_mem_chunks[i].ptr); + } +out: + memcpy(&cmd->resource_config, &config, sizeof(config)); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi init 10x\n"); + return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid); +} + +int ath10k_wmi_cmd_init(struct ath10k *ar) +{ + int ret; + + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + ret = ath10k_wmi_10x_cmd_init(ar); + else + ret = ath10k_wmi_main_cmd_init(ar); + + return ret; +} + static int ath10k_wmi_start_scan_calc_len(const struct wmi_start_scan_arg *arg) { int len; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 1c515d68228..5383f7eddce 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -1377,6 +1377,189 @@ struct wmi_resource_config { __le32 max_frag_entries; } __packed; +struct wmi_resource_config_10x { + /* number of virtual devices (VAPs) to support */ + __le32 num_vdevs; + + /* number of peer nodes to support */ + __le32 num_peers; + + /* number of keys per peer */ + __le32 num_peer_keys; + + /* total number of TX/RX data TIDs */ + __le32 num_tids; + + /* + * max skid for resolving hash collisions + * + * The address search table is sparse, so that if two MAC addresses + * result in the same hash value, the second of these conflicting + * entries can slide to the next index in the address search table, + * and use it, if it is unoccupied. This ast_skid_limit parameter + * specifies the upper bound on how many subsequent indices to search + * over to find an unoccupied space. + */ + __le32 ast_skid_limit; + + /* + * the nominal chain mask for transmit + * + * The chain mask may be modified dynamically, e.g. to operate AP + * tx with a reduced number of chains if no clients are associated. + * This configuration parameter specifies the nominal chain-mask that + * should be used when not operating with a reduced set of tx chains. + */ + __le32 tx_chain_mask; + + /* + * the nominal chain mask for receive + * + * The chain mask may be modified dynamically, e.g. for a client + * to use a reduced number of chains for receive if the traffic to + * the client is low enough that it doesn't require downlink MIMO + * or antenna diversity. + * This configuration parameter specifies the nominal chain-mask that + * should be used when not operating with a reduced set of rx chains. + */ + __le32 rx_chain_mask; + + /* + * what rx reorder timeout (ms) to use for the AC + * + * Each WMM access class (voice, video, best-effort, background) will + * have its own timeout value to dictate how long to wait for missing + * rx MPDUs to arrive before flushing subsequent MPDUs that have + * already been received. + * This parameter specifies the timeout in milliseconds for each + * class. + */ + __le32 rx_timeout_pri_vi; + __le32 rx_timeout_pri_vo; + __le32 rx_timeout_pri_be; + __le32 rx_timeout_pri_bk; + + /* + * what mode the rx should decap packets to + * + * MAC can decap to RAW (no decap), native wifi or Ethernet types + * THis setting also determines the default TX behavior, however TX + * behavior can be modified on a per VAP basis during VAP init + */ + __le32 rx_decap_mode; + + /* what is the maximum scan requests than can be queued */ + __le32 scan_max_pending_reqs; + + /* maximum VDEV that could use BMISS offload */ + __le32 bmiss_offload_max_vdev; + + /* maximum VDEV that could use offload roaming */ + __le32 roam_offload_max_vdev; + + /* maximum AP profiles that would push to offload roaming */ + __le32 roam_offload_max_ap_profiles; + + /* + * how many groups to use for mcast->ucast conversion + * + * The target's WAL maintains a table to hold information regarding + * which peers belong to a given multicast group, so that if + * multicast->unicast conversion is enabled, the target can convert + * multicast tx frames to a series of unicast tx frames, to each + * peer within the multicast group. + This num_mcast_groups configuration parameter tells the target how + * many multicast groups to provide storage for within its multicast + * group membership table. + */ + __le32 num_mcast_groups; + + /* + * size to alloc for the mcast membership table + * + * This num_mcast_table_elems configuration parameter tells the + * target how many peer elements it needs to provide storage for in + * its multicast group membership table. + * These multicast group membership table elements are shared by the + * multicast groups stored within the table. + */ + __le32 num_mcast_table_elems; + + /* + * whether/how to do multicast->unicast conversion + * + * This configuration parameter specifies whether the target should + * perform multicast --> unicast conversion on transmit, and if so, + * what to do if it finds no entries in its multicast group + * membership table for the multicast IP address in the tx frame. + * Configuration value: + * 0 -> Do not perform multicast to unicast conversion. + * 1 -> Convert multicast frames to unicast, if the IP multicast + * address from the tx frame is found in the multicast group + * membership table. If the IP multicast address is not found, + * drop the frame. + * 2 -> Convert multicast frames to unicast, if the IP multicast + * address from the tx frame is found in the multicast group + * membership table. If the IP multicast address is not found, + * transmit the frame as multicast. + */ + __le32 mcast2ucast_mode; + + /* + * how much memory to allocate for a tx PPDU dbg log + * + * This parameter controls how much memory the target will allocate + * to store a log of tx PPDU meta-information (how large the PPDU + * was, when it was sent, whether it was successful, etc.) + */ + __le32 tx_dbg_log_size; + + /* how many AST entries to be allocated for WDS */ + __le32 num_wds_entries; + + /* + * MAC DMA burst size, e.g., For target PCI limit can be + * 0 -default, 1 256B + */ + __le32 dma_burst_size; + + /* + * Fixed delimiters to be inserted after every MPDU to + * account for interface latency to avoid underrun. + */ + __le32 mac_aggr_delim; + + /* + * determine whether target is responsible for detecting duplicate + * non-aggregate MPDU and timing out stale fragments. + * + * A-MPDU reordering is always performed on the target. + * + * 0: target responsible for frag timeout and dup checking + * 1: host responsible for frag timeout and dup checking + */ + __le32 rx_skip_defrag_timeout_dup_detection_check; + + /* + * Configuration for VoW : + * No of Video Nodes to be supported + * and Max no of descriptors for each Video link (node). + */ + __le32 vow_config; + + /* Number of msdu descriptors target should use */ + __le32 num_msdu_desc; + + /* + * Max. number of Tx fragments per MSDU + * This parameter controls the max number of Tx fragments per MSDU. + * This is sent by the target as part of the WMI_SERVICE_READY event + * and is overriden by the OS shim as required. + */ + __le32 max_frag_entries; +} __packed; + + #define NUM_UNITS_IS_NUM_VDEVS 0x1 #define NUM_UNITS_IS_NUM_PEERS 0x2 @@ -1401,6 +1584,18 @@ struct wmi_init_cmd { struct host_memory_chunk host_mem_chunks[1]; } __packed; +/* _10x stucture is from 10.X FW API */ +struct wmi_init_cmd_10x { + struct wmi_resource_config_10x resource_config; + __le32 num_host_mem_chunks; + + /* + * variable number of host memory chunks. + * This should be the last element in the structure + */ + struct host_memory_chunk host_mem_chunks[1]; +} __packed; + /* TLV for channel list */ struct wmi_chan_list { __le32 tag; /* WMI_CHAN_LIST_TAG */ -- cgit v1.2.3 From ec6a73f00eba1cfca99599bf337ee3c66f89d735 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Thu, 26 Sep 2013 17:47:14 +0200 Subject: ath10k: add TARGET values for 10.x firmware Introduce all TARGET specific values for 10.x firmware. Some of them are common for both firmwares we will support, but to avoid confusion, define everything with prefix 10X_. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/hw.h | 31 +++++++++++++++++++ drivers/net/wireless/ath/ath10k/wmi.c | 58 +++++++++++++++++------------------ 2 files changed, 60 insertions(+), 29 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 66e721a1afd..2de13db5e5d 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -53,6 +53,7 @@ enum ath10k_mcast2ucast_mode { ATH10K_MCAST2UCAST_ENABLED = 1, }; +/* Target specific defines for MAIN firmware */ #define TARGET_NUM_VDEVS 8 #define TARGET_NUM_PEER_AST 2 #define TARGET_NUM_WDS_ENTRIES 32 @@ -87,6 +88,36 @@ enum ath10k_mcast2ucast_mode { #define TARGET_NUM_MSDU_DESC (1024 + 400) #define TARGET_MAX_FRAG_ENTRIES 0 +/* Target specific defines for 10.X firmware */ +#define TARGET_10X_NUM_VDEVS 16 +#define TARGET_10X_NUM_PEER_AST 2 +#define TARGET_10X_NUM_WDS_ENTRIES 32 +#define TARGET_10X_DMA_BURST_SIZE 0 +#define TARGET_10X_MAC_AGGR_DELIM 0 +#define TARGET_10X_AST_SKID_LIMIT 16 +#define TARGET_10X_NUM_PEERS (128 + (TARGET_10X_NUM_VDEVS)) +#define TARGET_10X_NUM_OFFLOAD_PEERS 0 +#define TARGET_10X_NUM_OFFLOAD_REORDER_BUFS 0 +#define TARGET_10X_NUM_PEER_KEYS 2 +#define TARGET_10X_NUM_TIDS 256 +#define TARGET_10X_TX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2)) +#define TARGET_10X_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2)) +#define TARGET_10X_RX_TIMEOUT_LO_PRI 100 +#define TARGET_10X_RX_TIMEOUT_HI_PRI 40 +#define TARGET_10X_RX_DECAP_MODE ATH10K_HW_TXRX_ETHERNET +#define TARGET_10X_SCAN_MAX_PENDING_REQS 4 +#define TARGET_10X_BMISS_OFFLOAD_MAX_VDEV 2 +#define TARGET_10X_ROAM_OFFLOAD_MAX_VDEV 2 +#define TARGET_10X_ROAM_OFFLOAD_MAX_AP_PROFILES 8 +#define TARGET_10X_GTK_OFFLOAD_MAX_VDEV 3 +#define TARGET_10X_NUM_MCAST_GROUPS 0 +#define TARGET_10X_NUM_MCAST_TABLE_ELEMS 0 +#define TARGET_10X_MCAST2UCAST_MODE ATH10K_MCAST2UCAST_DISABLED +#define TARGET_10X_TX_DBG_LOG_SIZE 1024 +#define TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 1 +#define TARGET_10X_VOW_CONFIG 0 +#define TARGET_10X_NUM_MSDU_DESC (1024 + 400) +#define TARGET_10X_MAX_FRAG_ENTRIES 0 /* Number of Copy Engines supported */ #define CE_COUNT 8 diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 71ad1b8868b..637f8d0aa7e 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1451,9 +1451,9 @@ static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar, * peers, 1 extra for self peer on target */ /* this needs to be tied, host and target * can get out of sync */ - num_units = TARGET_NUM_PEERS + 1; + num_units = TARGET_10X_NUM_PEERS + 1; else if (num_unit_info & NUM_UNITS_IS_NUM_VDEVS) - num_units = TARGET_NUM_VDEVS + 1; + num_units = TARGET_10X_NUM_VDEVS + 1; ath10k_dbg(ATH10K_DBG_WMI, "wmi mem_req_id %d num_units %d num_unit_info %d unit size %d actual units %d\n", @@ -2015,48 +2015,48 @@ static int ath10k_wmi_10x_cmd_init(struct ath10k *ar) u32 len, val; int i; - config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS); - config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS); - config.num_peer_keys = __cpu_to_le32(TARGET_NUM_PEER_KEYS); - config.num_tids = __cpu_to_le32(TARGET_NUM_TIDS); - config.ast_skid_limit = __cpu_to_le32(TARGET_AST_SKID_LIMIT); - config.tx_chain_mask = __cpu_to_le32(TARGET_TX_CHAIN_MASK); - config.rx_chain_mask = __cpu_to_le32(TARGET_RX_CHAIN_MASK); - config.rx_timeout_pri_vo = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI); - config.rx_timeout_pri_vi = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI); - config.rx_timeout_pri_be = __cpu_to_le32(TARGET_RX_TIMEOUT_LO_PRI); - config.rx_timeout_pri_bk = __cpu_to_le32(TARGET_RX_TIMEOUT_HI_PRI); - config.rx_decap_mode = __cpu_to_le32(TARGET_RX_DECAP_MODE); + config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS); + config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS); + config.num_peer_keys = __cpu_to_le32(TARGET_10X_NUM_PEER_KEYS); + config.num_tids = __cpu_to_le32(TARGET_10X_NUM_TIDS); + config.ast_skid_limit = __cpu_to_le32(TARGET_10X_AST_SKID_LIMIT); + config.tx_chain_mask = __cpu_to_le32(TARGET_10X_TX_CHAIN_MASK); + config.rx_chain_mask = __cpu_to_le32(TARGET_10X_RX_CHAIN_MASK); + config.rx_timeout_pri_vo = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_vi = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_be = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_bk = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_HI_PRI); + config.rx_decap_mode = __cpu_to_le32(TARGET_10X_RX_DECAP_MODE); config.scan_max_pending_reqs = - __cpu_to_le32(TARGET_SCAN_MAX_PENDING_REQS); + __cpu_to_le32(TARGET_10X_SCAN_MAX_PENDING_REQS); config.bmiss_offload_max_vdev = - __cpu_to_le32(TARGET_BMISS_OFFLOAD_MAX_VDEV); + __cpu_to_le32(TARGET_10X_BMISS_OFFLOAD_MAX_VDEV); config.roam_offload_max_vdev = - __cpu_to_le32(TARGET_ROAM_OFFLOAD_MAX_VDEV); + __cpu_to_le32(TARGET_10X_ROAM_OFFLOAD_MAX_VDEV); config.roam_offload_max_ap_profiles = - __cpu_to_le32(TARGET_ROAM_OFFLOAD_MAX_AP_PROFILES); + __cpu_to_le32(TARGET_10X_ROAM_OFFLOAD_MAX_AP_PROFILES); - config.num_mcast_groups = __cpu_to_le32(TARGET_NUM_MCAST_GROUPS); + config.num_mcast_groups = __cpu_to_le32(TARGET_10X_NUM_MCAST_GROUPS); config.num_mcast_table_elems = - __cpu_to_le32(TARGET_NUM_MCAST_TABLE_ELEMS); + __cpu_to_le32(TARGET_10X_NUM_MCAST_TABLE_ELEMS); - config.mcast2ucast_mode = __cpu_to_le32(TARGET_MCAST2UCAST_MODE); - config.tx_dbg_log_size = __cpu_to_le32(TARGET_TX_DBG_LOG_SIZE); - config.num_wds_entries = __cpu_to_le32(TARGET_NUM_WDS_ENTRIES); - config.dma_burst_size = __cpu_to_le32(TARGET_DMA_BURST_SIZE); - config.mac_aggr_delim = __cpu_to_le32(TARGET_MAC_AGGR_DELIM); + config.mcast2ucast_mode = __cpu_to_le32(TARGET_10X_MCAST2UCAST_MODE); + config.tx_dbg_log_size = __cpu_to_le32(TARGET_10X_TX_DBG_LOG_SIZE); + config.num_wds_entries = __cpu_to_le32(TARGET_10X_NUM_WDS_ENTRIES); + config.dma_burst_size = __cpu_to_le32(TARGET_10X_DMA_BURST_SIZE); + config.mac_aggr_delim = __cpu_to_le32(TARGET_10X_MAC_AGGR_DELIM); - val = TARGET_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK; + val = TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK; config.rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(val); - config.vow_config = __cpu_to_le32(TARGET_VOW_CONFIG); + config.vow_config = __cpu_to_le32(TARGET_10X_VOW_CONFIG); - config.num_msdu_desc = __cpu_to_le32(TARGET_NUM_MSDU_DESC); - config.max_frag_entries = __cpu_to_le32(TARGET_MAX_FRAG_ENTRIES); + config.num_msdu_desc = __cpu_to_le32(TARGET_10X_NUM_MSDU_DESC); + config.max_frag_entries = __cpu_to_le32(TARGET_10X_MAX_FRAG_ENTRIES); len = sizeof(*cmd) + (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks); -- cgit v1.2.3 From 6d1506e788ecf3c423f275036d37a40d1416db42 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Thu, 26 Sep 2013 17:47:15 +0200 Subject: ath10k: introduce dynamic vdev parameters Both firmwares (main and 10.x) have different set of vdev parameters. To stay in sync with FW API, this patch introduces a dynamic registering method. ath10k_wmi_vdev_set_param() takes now indirect u32 value to identify the Vdev parameter it want's to set. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 1 + drivers/net/wireless/ath/ath10k/mac.c | 55 ++++++----- drivers/net/wireless/ath/ath10k/wmi.c | 129 ++++++++++++++++++++++++- drivers/net/wireless/ath/ath10k/wmi.h | 172 ++++++++++++++++++++++++++++++++- 4 files changed, 332 insertions(+), 25 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 984db115cb8..124e7246bb4 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -119,6 +119,7 @@ struct ath10k_wmi { struct completion unified_ready; wait_queue_head_t tx_credits_wq; struct wmi_cmd_map *cmd; + struct wmi_vdev_param_map *vdev_param; u32 num_mem_chunks; struct ath10k_mem_chunk mem_chunks[ATH10K_MAX_MEM_REQS]; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index f0bc707257a..b5ae01de684 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -334,25 +334,29 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr) static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value) { + struct ath10k *ar = arvif->ar; + u32 vdev_param; + if (value != 0xFFFFFFFF) value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold, ATH10K_RTS_MAX); - return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, - WMI_VDEV_PARAM_RTS_THRESHOLD, - value); + vdev_param = ar->wmi.vdev_param->rts_threshold; + return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value); } static int ath10k_mac_set_frag(struct ath10k_vif *arvif, u32 value) { + struct ath10k *ar = arvif->ar; + u32 vdev_param; + if (value != 0xFFFFFFFF) value = clamp_t(u32, arvif->ar->hw->wiphy->frag_threshold, ATH10K_FRAGMT_THRESHOLD_MIN, ATH10K_FRAGMT_THRESHOLD_MAX); - return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, - WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, - value); + vdev_param = ar->wmi.vdev_param->fragmentation_threshold; + return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value); } static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr) @@ -674,6 +678,7 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif, struct ieee80211_bss_conf *info, const u8 self_peer[ETH_ALEN]) { + u32 vdev_param; int ret = 0; lockdep_assert_held(&arvif->ar->conf_mutex); @@ -707,8 +712,8 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif, return; } - ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, - WMI_VDEV_PARAM_ATIM_WINDOW, + vdev_param = arvif->ar->wmi.vdev_param->atim_window; + ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, vdev_param, ATH10K_DEFAULT_ATIM); if (ret) ath10k_warn("Failed to set IBSS ATIM for VDEV:%d ret:%d\n", @@ -1430,6 +1435,7 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) struct ath10k *ar = arvif->ar; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_key_conf *key = info->control.hw_key; + u32 vdev_param; int ret; if (!ieee80211_has_protected(hdr->frame_control)) @@ -1448,8 +1454,8 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d keyidx %d\n", arvif->vdev_id, key->keyidx); - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_DEF_KEYID, + vdev_param = ar->wmi.vdev_param->def_keyid; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, key->keyidx); if (ret) { ath10k_warn("could not update wep keyidx (%d)\n", ret); @@ -1983,6 +1989,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, int ret = 0; u32 value; int bit; + u32 vdev_param; mutex_lock(&ar->conf_mutex); @@ -2044,13 +2051,14 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, goto exit; } - ret = ath10k_wmi_vdev_set_param(ar, 0, WMI_VDEV_PARAM_DEF_KEYID, + vdev_param = ar->wmi.vdev_param->def_keyid; + ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param, arvif->def_wep_key_index); if (ret) ath10k_warn("Failed to set default keyid: %d\n", ret); - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_TX_ENCAP_TYPE, + vdev_param = ar->wmi.vdev_param->tx_encap_type; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, ATH10K_HW_TXRX_NATIVE_WIFI); if (ret) ath10k_warn("Failed to set TX encap: %d\n", ret); @@ -2201,6 +2209,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); int ret = 0; + u32 vdev_param; mutex_lock(&ar->conf_mutex); @@ -2209,8 +2218,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_BEACON_INT) { arvif->beacon_interval = info->beacon_int; - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_BEACON_INTERVAL, + vdev_param = ar->wmi.vdev_param->beacon_interval; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, arvif->beacon_interval); ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d beacon_interval %d\n", @@ -2241,8 +2250,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, "mac vdev %d dtim_period %d\n", arvif->vdev_id, arvif->dtim_period); - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_DTIM_PERIOD, + vdev_param = ar->wmi.vdev_param->dtim_period; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, arvif->dtim_period); if (ret) ath10k_warn("Failed to set dtim period for VDEV: %d\n", @@ -2309,8 +2318,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d cts_prot %d\n", arvif->vdev_id, cts_prot); - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_ENABLE_RTSCTS, + vdev_param = ar->wmi.vdev_param->enable_rtscts; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, cts_prot); if (ret) ath10k_warn("Failed to set CTS prot for VDEV: %d\n", @@ -2328,8 +2337,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d slot_time %d\n", arvif->vdev_id, slottime); - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_SLOT_TIME, + vdev_param = ar->wmi.vdev_param->slot_time; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, slottime); if (ret) ath10k_warn("Failed to set erp slot for VDEV: %d\n", @@ -2347,8 +2356,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, "mac vdev %d preamble %dn", arvif->vdev_id, preamble); - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_PREAMBLE, + vdev_param = ar->wmi.vdev_param->preamble; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, preamble); if (ret) ath10k_warn("Failed to set preamble for VDEV: %d\n", diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 637f8d0aa7e..89a8cb862c8 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -265,6 +265,124 @@ static struct wmi_cmd_map wmi_10x_cmd_map = { .gpio_output_cmdid = WMI_10X_GPIO_OUTPUT_CMDID, }; +/* MAIN WMI VDEV param map */ +static struct wmi_vdev_param_map wmi_vdev_param_map = { + .rts_threshold = WMI_VDEV_PARAM_RTS_THRESHOLD, + .fragmentation_threshold = WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + .beacon_interval = WMI_VDEV_PARAM_BEACON_INTERVAL, + .listen_interval = WMI_VDEV_PARAM_LISTEN_INTERVAL, + .multicast_rate = WMI_VDEV_PARAM_MULTICAST_RATE, + .mgmt_tx_rate = WMI_VDEV_PARAM_MGMT_TX_RATE, + .slot_time = WMI_VDEV_PARAM_SLOT_TIME, + .preamble = WMI_VDEV_PARAM_PREAMBLE, + .swba_time = WMI_VDEV_PARAM_SWBA_TIME, + .wmi_vdev_stats_update_period = WMI_VDEV_STATS_UPDATE_PERIOD, + .wmi_vdev_pwrsave_ageout_time = WMI_VDEV_PWRSAVE_AGEOUT_TIME, + .wmi_vdev_host_swba_interval = WMI_VDEV_HOST_SWBA_INTERVAL, + .dtim_period = WMI_VDEV_PARAM_DTIM_PERIOD, + .wmi_vdev_oc_scheduler_air_time_limit = + WMI_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + .wds = WMI_VDEV_PARAM_WDS, + .atim_window = WMI_VDEV_PARAM_ATIM_WINDOW, + .bmiss_count_max = WMI_VDEV_PARAM_BMISS_COUNT_MAX, + .bmiss_first_bcnt = WMI_VDEV_PARAM_BMISS_FIRST_BCNT, + .bmiss_final_bcnt = WMI_VDEV_PARAM_BMISS_FINAL_BCNT, + .feature_wmm = WMI_VDEV_PARAM_FEATURE_WMM, + .chwidth = WMI_VDEV_PARAM_CHWIDTH, + .chextoffset = WMI_VDEV_PARAM_CHEXTOFFSET, + .disable_htprotection = WMI_VDEV_PARAM_DISABLE_HTPROTECTION, + .sta_quickkickout = WMI_VDEV_PARAM_STA_QUICKKICKOUT, + .mgmt_rate = WMI_VDEV_PARAM_MGMT_RATE, + .protection_mode = WMI_VDEV_PARAM_PROTECTION_MODE, + .fixed_rate = WMI_VDEV_PARAM_FIXED_RATE, + .sgi = WMI_VDEV_PARAM_SGI, + .ldpc = WMI_VDEV_PARAM_LDPC, + .tx_stbc = WMI_VDEV_PARAM_TX_STBC, + .rx_stbc = WMI_VDEV_PARAM_RX_STBC, + .intra_bss_fwd = WMI_VDEV_PARAM_INTRA_BSS_FWD, + .def_keyid = WMI_VDEV_PARAM_DEF_KEYID, + .nss = WMI_VDEV_PARAM_NSS, + .bcast_data_rate = WMI_VDEV_PARAM_BCAST_DATA_RATE, + .mcast_data_rate = WMI_VDEV_PARAM_MCAST_DATA_RATE, + .mcast_indicate = WMI_VDEV_PARAM_MCAST_INDICATE, + .dhcp_indicate = WMI_VDEV_PARAM_DHCP_INDICATE, + .unknown_dest_indicate = WMI_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + .ap_keepalive_min_idle_inactive_time_secs = + WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_idle_inactive_time_secs = + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_unresponsive_time_secs = + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + .ap_enable_nawds = WMI_VDEV_PARAM_AP_ENABLE_NAWDS, + .mcast2ucast_set = WMI_VDEV_PARAM_UNSUPPORTED, + .enable_rtscts = WMI_VDEV_PARAM_ENABLE_RTSCTS, + .txbf = WMI_VDEV_PARAM_TXBF, + .packet_powersave = WMI_VDEV_PARAM_PACKET_POWERSAVE, + .drop_unencry = WMI_VDEV_PARAM_DROP_UNENCRY, + .tx_encap_type = WMI_VDEV_PARAM_TX_ENCAP_TYPE, + .ap_detect_out_of_sync_sleeping_sta_time_secs = + WMI_VDEV_PARAM_UNSUPPORTED, +}; + +/* 10.X WMI VDEV param map */ +static struct wmi_vdev_param_map wmi_10x_vdev_param_map = { + .rts_threshold = WMI_10X_VDEV_PARAM_RTS_THRESHOLD, + .fragmentation_threshold = WMI_10X_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + .beacon_interval = WMI_10X_VDEV_PARAM_BEACON_INTERVAL, + .listen_interval = WMI_10X_VDEV_PARAM_LISTEN_INTERVAL, + .multicast_rate = WMI_10X_VDEV_PARAM_MULTICAST_RATE, + .mgmt_tx_rate = WMI_10X_VDEV_PARAM_MGMT_TX_RATE, + .slot_time = WMI_10X_VDEV_PARAM_SLOT_TIME, + .preamble = WMI_10X_VDEV_PARAM_PREAMBLE, + .swba_time = WMI_10X_VDEV_PARAM_SWBA_TIME, + .wmi_vdev_stats_update_period = WMI_10X_VDEV_STATS_UPDATE_PERIOD, + .wmi_vdev_pwrsave_ageout_time = WMI_10X_VDEV_PWRSAVE_AGEOUT_TIME, + .wmi_vdev_host_swba_interval = WMI_10X_VDEV_HOST_SWBA_INTERVAL, + .dtim_period = WMI_10X_VDEV_PARAM_DTIM_PERIOD, + .wmi_vdev_oc_scheduler_air_time_limit = + WMI_10X_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + .wds = WMI_10X_VDEV_PARAM_WDS, + .atim_window = WMI_10X_VDEV_PARAM_ATIM_WINDOW, + .bmiss_count_max = WMI_10X_VDEV_PARAM_BMISS_COUNT_MAX, + .bmiss_first_bcnt = WMI_VDEV_PARAM_UNSUPPORTED, + .bmiss_final_bcnt = WMI_VDEV_PARAM_UNSUPPORTED, + .feature_wmm = WMI_10X_VDEV_PARAM_FEATURE_WMM, + .chwidth = WMI_10X_VDEV_PARAM_CHWIDTH, + .chextoffset = WMI_10X_VDEV_PARAM_CHEXTOFFSET, + .disable_htprotection = WMI_10X_VDEV_PARAM_DISABLE_HTPROTECTION, + .sta_quickkickout = WMI_10X_VDEV_PARAM_STA_QUICKKICKOUT, + .mgmt_rate = WMI_10X_VDEV_PARAM_MGMT_RATE, + .protection_mode = WMI_10X_VDEV_PARAM_PROTECTION_MODE, + .fixed_rate = WMI_10X_VDEV_PARAM_FIXED_RATE, + .sgi = WMI_10X_VDEV_PARAM_SGI, + .ldpc = WMI_10X_VDEV_PARAM_LDPC, + .tx_stbc = WMI_10X_VDEV_PARAM_TX_STBC, + .rx_stbc = WMI_10X_VDEV_PARAM_RX_STBC, + .intra_bss_fwd = WMI_10X_VDEV_PARAM_INTRA_BSS_FWD, + .def_keyid = WMI_10X_VDEV_PARAM_DEF_KEYID, + .nss = WMI_10X_VDEV_PARAM_NSS, + .bcast_data_rate = WMI_10X_VDEV_PARAM_BCAST_DATA_RATE, + .mcast_data_rate = WMI_10X_VDEV_PARAM_MCAST_DATA_RATE, + .mcast_indicate = WMI_10X_VDEV_PARAM_MCAST_INDICATE, + .dhcp_indicate = WMI_10X_VDEV_PARAM_DHCP_INDICATE, + .unknown_dest_indicate = WMI_10X_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + .ap_keepalive_min_idle_inactive_time_secs = + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_idle_inactive_time_secs = + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_unresponsive_time_secs = + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + .ap_enable_nawds = WMI_10X_VDEV_PARAM_AP_ENABLE_NAWDS, + .mcast2ucast_set = WMI_10X_VDEV_PARAM_MCAST2UCAST_SET, + .enable_rtscts = WMI_10X_VDEV_PARAM_ENABLE_RTSCTS, + .txbf = WMI_VDEV_PARAM_UNSUPPORTED, + .packet_powersave = WMI_VDEV_PARAM_UNSUPPORTED, + .drop_unencry = WMI_VDEV_PARAM_UNSUPPORTED, + .tx_encap_type = WMI_VDEV_PARAM_UNSUPPORTED, + .ap_detect_out_of_sync_sleeping_sta_time_secs = + WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS, +}; + int ath10k_wmi_wait_for_service_ready(struct ath10k *ar) { int ret; @@ -1751,9 +1869,11 @@ int ath10k_wmi_attach(struct ath10k *ar) if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { ath10k_warn("Firmware 10.X is not yet supported\n"); ar->wmi.cmd = &wmi_10x_cmd_map; + ar->wmi.vdev_param = &wmi_10x_vdev_param_map; ret = -ENOTSUPP; } else { ar->wmi.cmd = &wmi_cmd_map; + ar->wmi.vdev_param = &wmi_vdev_param_map; ret = 0; } @@ -2516,11 +2636,18 @@ int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id) } int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, - enum wmi_vdev_param param_id, u32 param_value) + u32 param_id, u32 param_value) { struct wmi_vdev_set_param_cmd *cmd; struct sk_buff *skb; + if (param_id == WMI_VDEV_PARAM_UNSUPPORTED) { + ath10k_dbg(ATH10K_DBG_WMI, + "vdev param %d not supported by firmware\n", + param_id); + return -EINVAL; + } + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); if (!skb) return -ENOMEM; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 5383f7eddce..15185867f6e 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -2695,6 +2695,61 @@ enum wmi_rate_preamble { /* Value to disable fixed rate setting */ #define WMI_FIXED_RATE_NONE (0xff) +struct wmi_vdev_param_map { + u32 rts_threshold; + u32 fragmentation_threshold; + u32 beacon_interval; + u32 listen_interval; + u32 multicast_rate; + u32 mgmt_tx_rate; + u32 slot_time; + u32 preamble; + u32 swba_time; + u32 wmi_vdev_stats_update_period; + u32 wmi_vdev_pwrsave_ageout_time; + u32 wmi_vdev_host_swba_interval; + u32 dtim_period; + u32 wmi_vdev_oc_scheduler_air_time_limit; + u32 wds; + u32 atim_window; + u32 bmiss_count_max; + u32 bmiss_first_bcnt; + u32 bmiss_final_bcnt; + u32 feature_wmm; + u32 chwidth; + u32 chextoffset; + u32 disable_htprotection; + u32 sta_quickkickout; + u32 mgmt_rate; + u32 protection_mode; + u32 fixed_rate; + u32 sgi; + u32 ldpc; + u32 tx_stbc; + u32 rx_stbc; + u32 intra_bss_fwd; + u32 def_keyid; + u32 nss; + u32 bcast_data_rate; + u32 mcast_data_rate; + u32 mcast_indicate; + u32 dhcp_indicate; + u32 unknown_dest_indicate; + u32 ap_keepalive_min_idle_inactive_time_secs; + u32 ap_keepalive_max_idle_inactive_time_secs; + u32 ap_keepalive_max_unresponsive_time_secs; + u32 ap_enable_nawds; + u32 mcast2ucast_set; + u32 enable_rtscts; + u32 txbf; + u32 packet_powersave; + u32 drop_unencry; + u32 tx_encap_type; + u32 ap_detect_out_of_sync_sleeping_sta_time_secs; +}; + +#define WMI_VDEV_PARAM_UNSUPPORTED 0 + /* the definition of different VDEV parameters */ enum wmi_vdev_param { /* RTS Threshold */ @@ -2826,6 +2881,121 @@ enum wmi_vdev_param { WMI_VDEV_PARAM_TX_ENCAP_TYPE, }; +/* the definition of different VDEV parameters */ +enum wmi_10x_vdev_param { + /* RTS Threshold */ + WMI_10X_VDEV_PARAM_RTS_THRESHOLD = 0x1, + /* Fragmentation threshold */ + WMI_10X_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + /* beacon interval in TUs */ + WMI_10X_VDEV_PARAM_BEACON_INTERVAL, + /* Listen interval in TUs */ + WMI_10X_VDEV_PARAM_LISTEN_INTERVAL, + /* muticast rate in Mbps */ + WMI_10X_VDEV_PARAM_MULTICAST_RATE, + /* management frame rate in Mbps */ + WMI_10X_VDEV_PARAM_MGMT_TX_RATE, + /* slot time (long vs short) */ + WMI_10X_VDEV_PARAM_SLOT_TIME, + /* preamble (long vs short) */ + WMI_10X_VDEV_PARAM_PREAMBLE, + /* SWBA time (time before tbtt in msec) */ + WMI_10X_VDEV_PARAM_SWBA_TIME, + /* time period for updating VDEV stats */ + WMI_10X_VDEV_STATS_UPDATE_PERIOD, + /* age out time in msec for frames queued for station in power save */ + WMI_10X_VDEV_PWRSAVE_AGEOUT_TIME, + /* + * Host SWBA interval (time in msec before tbtt for SWBA event + * generation). + */ + WMI_10X_VDEV_HOST_SWBA_INTERVAL, + /* DTIM period (specified in units of num beacon intervals) */ + WMI_10X_VDEV_PARAM_DTIM_PERIOD, + /* + * scheduler air time limit for this VDEV. used by off chan + * scheduler. + */ + WMI_10X_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + /* enable/dsiable WDS for this VDEV */ + WMI_10X_VDEV_PARAM_WDS, + /* ATIM Window */ + WMI_10X_VDEV_PARAM_ATIM_WINDOW, + /* BMISS max */ + WMI_10X_VDEV_PARAM_BMISS_COUNT_MAX, + /* WMM enables/disabled */ + WMI_10X_VDEV_PARAM_FEATURE_WMM, + /* Channel width */ + WMI_10X_VDEV_PARAM_CHWIDTH, + /* Channel Offset */ + WMI_10X_VDEV_PARAM_CHEXTOFFSET, + /* Disable HT Protection */ + WMI_10X_VDEV_PARAM_DISABLE_HTPROTECTION, + /* Quick STA Kickout */ + WMI_10X_VDEV_PARAM_STA_QUICKKICKOUT, + /* Rate to be used with Management frames */ + WMI_10X_VDEV_PARAM_MGMT_RATE, + /* Protection Mode */ + WMI_10X_VDEV_PARAM_PROTECTION_MODE, + /* Fixed rate setting */ + WMI_10X_VDEV_PARAM_FIXED_RATE, + /* Short GI Enable/Disable */ + WMI_10X_VDEV_PARAM_SGI, + /* Enable LDPC */ + WMI_10X_VDEV_PARAM_LDPC, + /* Enable Tx STBC */ + WMI_10X_VDEV_PARAM_TX_STBC, + /* Enable Rx STBC */ + WMI_10X_VDEV_PARAM_RX_STBC, + /* Intra BSS forwarding */ + WMI_10X_VDEV_PARAM_INTRA_BSS_FWD, + /* Setting Default xmit key for Vdev */ + WMI_10X_VDEV_PARAM_DEF_KEYID, + /* NSS width */ + WMI_10X_VDEV_PARAM_NSS, + /* Set the custom rate for the broadcast data frames */ + WMI_10X_VDEV_PARAM_BCAST_DATA_RATE, + /* Set the custom rate (rate-code) for multicast data frames */ + WMI_10X_VDEV_PARAM_MCAST_DATA_RATE, + /* Tx multicast packet indicate Enable/Disable */ + WMI_10X_VDEV_PARAM_MCAST_INDICATE, + /* Tx DHCP packet indicate Enable/Disable */ + WMI_10X_VDEV_PARAM_DHCP_INDICATE, + /* Enable host inspection of Tx unicast packet to unknown destination */ + WMI_10X_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + + /* The minimum amount of time AP begins to consider STA inactive */ + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + + /* + * An associated STA is considered inactive when there is no recent + * TX/RX activity and no downlink frames are buffered for it. Once a + * STA exceeds the maximum idle inactive time, the AP will send an + * 802.11 data-null as a keep alive to verify the STA is still + * associated. If the STA does ACK the data-null, or if the data-null + * is buffered and the STA does not retrieve it, the STA will be + * considered unresponsive + * (see WMI_10X_VDEV_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS). + */ + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + + /* + * An associated STA is considered unresponsive if there is no recent + * TX/RX activity and downlink frames are buffered for it. Once a STA + * exceeds the maximum unresponsive time, the AP will send a + * WMI_10X_STA_KICKOUT event to the host so the STA can be deleted. */ + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + + /* Enable NAWDS : MCAST INSPECT Enable, NAWDS Flag set */ + WMI_10X_VDEV_PARAM_AP_ENABLE_NAWDS, + + WMI_10X_VDEV_PARAM_MCAST2UCAST_SET, + /* Enable/Disable RTS-CTS */ + WMI_10X_VDEV_PARAM_ENABLE_RTSCTS, + + WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS, +}; + /* slot time long */ #define WMI_VDEV_SLOT_TIME_LONG 0x1 /* slot time short */ @@ -3648,7 +3818,7 @@ int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid); int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id); int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, - enum wmi_vdev_param param_id, u32 param_value); + u32 param_id, u32 param_value); int ath10k_wmi_vdev_install_key(struct ath10k *ar, const struct wmi_vdev_install_key_arg *arg); int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, -- cgit v1.2.3 From 226a339ba8f3c53914e640d250feaf4321cfcd04 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Thu, 26 Sep 2013 17:47:16 +0200 Subject: ath10k: introduce dynamic pdev parameters This is done exactly the same way as for vdev. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 1 + drivers/net/wireless/ath/ath10k/mac.c | 10 +-- drivers/net/wireless/ath/ath10k/wmi.c | 115 ++++++++++++++++++++++++- drivers/net/wireless/ath/ath10k/wmi.h | 148 ++++++++++++++++++++++++++++++++- 4 files changed, 265 insertions(+), 9 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 124e7246bb4..d5da8a9f697 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -120,6 +120,7 @@ struct ath10k_wmi { wait_queue_head_t tx_credits_wq; struct wmi_cmd_map *cmd; struct wmi_vdev_param_map *vdev_param; + struct wmi_pdev_param_map *pdev_param; u32 num_mem_chunks; struct ath10k_mem_chunk mem_chunks[ATH10K_MAX_MEM_REQS]; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index b5ae01de684..8684e03b393 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1882,12 +1882,12 @@ static int ath10k_start(struct ieee80211_hw *hw) else if (ar->state == ATH10K_STATE_RESTARTING) ar->state = ATH10K_STATE_RESTARTED; - ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1); + ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->pmf_qos, 1); if (ret) ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n", ret); - ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_DYNAMIC_BW, 0); + ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->dynamic_bw, 0); if (ret) ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n", ret); @@ -2209,7 +2209,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); int ret = 0; - u32 vdev_param; + u32 vdev_param, pdev_param; mutex_lock(&ar->conf_mutex); @@ -2235,8 +2235,8 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, "vdev %d set beacon tx mode to staggered\n", arvif->vdev_id); - ret = ath10k_wmi_pdev_set_param(ar, - WMI_PDEV_PARAM_BEACON_TX_MODE, + pdev_param = ar->wmi.pdev_param->beacon_tx_mode; + ret = ath10k_wmi_pdev_set_param(ar, pdev_param, WMI_BEACON_STAGGERED_MODE); if (ret) ath10k_warn("Failed to set beacon mode for VDEV: %d\n", diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 89a8cb862c8..3460cf44684 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -383,6 +383,111 @@ static struct wmi_vdev_param_map wmi_10x_vdev_param_map = { WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS, }; +static struct wmi_pdev_param_map wmi_pdev_param_map = { + .tx_chain_mask = WMI_PDEV_PARAM_TX_CHAIN_MASK, + .rx_chain_mask = WMI_PDEV_PARAM_RX_CHAIN_MASK, + .txpower_limit2g = WMI_PDEV_PARAM_TXPOWER_LIMIT2G, + .txpower_limit5g = WMI_PDEV_PARAM_TXPOWER_LIMIT5G, + .txpower_scale = WMI_PDEV_PARAM_TXPOWER_SCALE, + .beacon_gen_mode = WMI_PDEV_PARAM_BEACON_GEN_MODE, + .beacon_tx_mode = WMI_PDEV_PARAM_BEACON_TX_MODE, + .resmgr_offchan_mode = WMI_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + .protection_mode = WMI_PDEV_PARAM_PROTECTION_MODE, + .dynamic_bw = WMI_PDEV_PARAM_DYNAMIC_BW, + .non_agg_sw_retry_th = WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + .agg_sw_retry_th = WMI_PDEV_PARAM_AGG_SW_RETRY_TH, + .sta_kickout_th = WMI_PDEV_PARAM_STA_KICKOUT_TH, + .ac_aggrsize_scaling = WMI_PDEV_PARAM_AC_AGGRSIZE_SCALING, + .ltr_enable = WMI_PDEV_PARAM_LTR_ENABLE, + .ltr_ac_latency_be = WMI_PDEV_PARAM_LTR_AC_LATENCY_BE, + .ltr_ac_latency_bk = WMI_PDEV_PARAM_LTR_AC_LATENCY_BK, + .ltr_ac_latency_vi = WMI_PDEV_PARAM_LTR_AC_LATENCY_VI, + .ltr_ac_latency_vo = WMI_PDEV_PARAM_LTR_AC_LATENCY_VO, + .ltr_ac_latency_timeout = WMI_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + .ltr_sleep_override = WMI_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + .ltr_rx_override = WMI_PDEV_PARAM_LTR_RX_OVERRIDE, + .ltr_tx_activity_timeout = WMI_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + .l1ss_enable = WMI_PDEV_PARAM_L1SS_ENABLE, + .dsleep_enable = WMI_PDEV_PARAM_DSLEEP_ENABLE, + .pcielp_txbuf_flush = WMI_PDEV_PARAM_PCIELP_TXBUF_FLUSH, + .pcielp_txbuf_watermark = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN, + .pcielp_txbuf_tmo_en = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN, + .pcielp_txbuf_tmo_value = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE, + .pdev_stats_update_period = WMI_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + .vdev_stats_update_period = WMI_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + .peer_stats_update_period = WMI_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + .bcnflt_stats_update_period = WMI_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + .pmf_qos = WMI_PDEV_PARAM_PMF_QOS, + .arp_ac_override = WMI_PDEV_PARAM_ARP_AC_OVERRIDE, + .arpdhcp_ac_override = WMI_PDEV_PARAM_UNSUPPORTED, + .dcs = WMI_PDEV_PARAM_DCS, + .ani_enable = WMI_PDEV_PARAM_ANI_ENABLE, + .ani_poll_period = WMI_PDEV_PARAM_ANI_POLL_PERIOD, + .ani_listen_period = WMI_PDEV_PARAM_ANI_LISTEN_PERIOD, + .ani_ofdm_level = WMI_PDEV_PARAM_ANI_OFDM_LEVEL, + .ani_cck_level = WMI_PDEV_PARAM_ANI_CCK_LEVEL, + .dyntxchain = WMI_PDEV_PARAM_DYNTXCHAIN, + .proxy_sta = WMI_PDEV_PARAM_PROXY_STA, + .idle_ps_config = WMI_PDEV_PARAM_IDLE_PS_CONFIG, + .power_gating_sleep = WMI_PDEV_PARAM_POWER_GATING_SLEEP, + .fast_channel_reset = WMI_PDEV_PARAM_UNSUPPORTED, + .burst_dur = WMI_PDEV_PARAM_UNSUPPORTED, + .burst_enable = WMI_PDEV_PARAM_UNSUPPORTED, +}; + +static struct wmi_pdev_param_map wmi_10x_pdev_param_map = { + .tx_chain_mask = WMI_10X_PDEV_PARAM_TX_CHAIN_MASK, + .rx_chain_mask = WMI_10X_PDEV_PARAM_RX_CHAIN_MASK, + .txpower_limit2g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT2G, + .txpower_limit5g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT5G, + .txpower_scale = WMI_10X_PDEV_PARAM_TXPOWER_SCALE, + .beacon_gen_mode = WMI_10X_PDEV_PARAM_BEACON_GEN_MODE, + .beacon_tx_mode = WMI_10X_PDEV_PARAM_BEACON_TX_MODE, + .resmgr_offchan_mode = WMI_10X_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + .protection_mode = WMI_10X_PDEV_PARAM_PROTECTION_MODE, + .dynamic_bw = WMI_10X_PDEV_PARAM_DYNAMIC_BW, + .non_agg_sw_retry_th = WMI_10X_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + .agg_sw_retry_th = WMI_10X_PDEV_PARAM_AGG_SW_RETRY_TH, + .sta_kickout_th = WMI_10X_PDEV_PARAM_STA_KICKOUT_TH, + .ac_aggrsize_scaling = WMI_10X_PDEV_PARAM_AC_AGGRSIZE_SCALING, + .ltr_enable = WMI_10X_PDEV_PARAM_LTR_ENABLE, + .ltr_ac_latency_be = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BE, + .ltr_ac_latency_bk = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BK, + .ltr_ac_latency_vi = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VI, + .ltr_ac_latency_vo = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VO, + .ltr_ac_latency_timeout = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + .ltr_sleep_override = WMI_10X_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + .ltr_rx_override = WMI_10X_PDEV_PARAM_LTR_RX_OVERRIDE, + .ltr_tx_activity_timeout = WMI_10X_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + .l1ss_enable = WMI_10X_PDEV_PARAM_L1SS_ENABLE, + .dsleep_enable = WMI_10X_PDEV_PARAM_DSLEEP_ENABLE, + .pcielp_txbuf_flush = WMI_PDEV_PARAM_UNSUPPORTED, + .pcielp_txbuf_watermark = WMI_PDEV_PARAM_UNSUPPORTED, + .pcielp_txbuf_tmo_en = WMI_PDEV_PARAM_UNSUPPORTED, + .pcielp_txbuf_tmo_value = WMI_PDEV_PARAM_UNSUPPORTED, + .pdev_stats_update_period = WMI_10X_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + .vdev_stats_update_period = WMI_10X_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + .peer_stats_update_period = WMI_10X_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + .bcnflt_stats_update_period = + WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + .pmf_qos = WMI_10X_PDEV_PARAM_PMF_QOS, + .arp_ac_override = WMI_PDEV_PARAM_UNSUPPORTED, + .arpdhcp_ac_override = WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE, + .dcs = WMI_10X_PDEV_PARAM_DCS, + .ani_enable = WMI_10X_PDEV_PARAM_ANI_ENABLE, + .ani_poll_period = WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD, + .ani_listen_period = WMI_10X_PDEV_PARAM_ANI_LISTEN_PERIOD, + .ani_ofdm_level = WMI_10X_PDEV_PARAM_ANI_OFDM_LEVEL, + .ani_cck_level = WMI_10X_PDEV_PARAM_ANI_CCK_LEVEL, + .dyntxchain = WMI_10X_PDEV_PARAM_DYNTXCHAIN, + .proxy_sta = WMI_PDEV_PARAM_UNSUPPORTED, + .idle_ps_config = WMI_PDEV_PARAM_UNSUPPORTED, + .power_gating_sleep = WMI_PDEV_PARAM_UNSUPPORTED, + .fast_channel_reset = WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET, + .burst_dur = WMI_10X_PDEV_PARAM_BURST_DUR, + .burst_enable = WMI_10X_PDEV_PARAM_BURST_ENABLE, +}; + int ath10k_wmi_wait_for_service_ready(struct ath10k *ar) { int ret; @@ -1870,10 +1975,12 @@ int ath10k_wmi_attach(struct ath10k *ar) ath10k_warn("Firmware 10.X is not yet supported\n"); ar->wmi.cmd = &wmi_10x_cmd_map; ar->wmi.vdev_param = &wmi_10x_vdev_param_map; + ar->wmi.pdev_param = &wmi_10x_pdev_param_map; ret = -ENOTSUPP; } else { ar->wmi.cmd = &wmi_cmd_map; ar->wmi.vdev_param = &wmi_vdev_param_map; + ar->wmi.pdev_param = &wmi_pdev_param_map; ret = 0; } @@ -2009,12 +2116,16 @@ int ath10k_wmi_pdev_resume_target(struct ath10k *ar) return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_resume_cmdid); } -int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id, - u32 value) +int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value) { struct wmi_pdev_set_param_cmd *cmd; struct sk_buff *skb; + if (id == WMI_PDEV_PARAM_UNSUPPORTED) { + ath10k_warn("pdev param %d not supported by firmware\n", id); + return -EINVAL; + } + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); if (!skb) return -ENOMEM; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 15185867f6e..80ab2f0fbe5 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -2072,6 +2072,60 @@ struct wmi_csa_event { #define VDEV_DEFAULT_STATS_UPDATE_PERIOD 500 #define PEER_DEFAULT_STATS_UPDATE_PERIOD 500 +struct wmi_pdev_param_map { + u32 tx_chain_mask; + u32 rx_chain_mask; + u32 txpower_limit2g; + u32 txpower_limit5g; + u32 txpower_scale; + u32 beacon_gen_mode; + u32 beacon_tx_mode; + u32 resmgr_offchan_mode; + u32 protection_mode; + u32 dynamic_bw; + u32 non_agg_sw_retry_th; + u32 agg_sw_retry_th; + u32 sta_kickout_th; + u32 ac_aggrsize_scaling; + u32 ltr_enable; + u32 ltr_ac_latency_be; + u32 ltr_ac_latency_bk; + u32 ltr_ac_latency_vi; + u32 ltr_ac_latency_vo; + u32 ltr_ac_latency_timeout; + u32 ltr_sleep_override; + u32 ltr_rx_override; + u32 ltr_tx_activity_timeout; + u32 l1ss_enable; + u32 dsleep_enable; + u32 pcielp_txbuf_flush; + u32 pcielp_txbuf_watermark; + u32 pcielp_txbuf_tmo_en; + u32 pcielp_txbuf_tmo_value; + u32 pdev_stats_update_period; + u32 vdev_stats_update_period; + u32 peer_stats_update_period; + u32 bcnflt_stats_update_period; + u32 pmf_qos; + u32 arp_ac_override; + u32 arpdhcp_ac_override; + u32 dcs; + u32 ani_enable; + u32 ani_poll_period; + u32 ani_listen_period; + u32 ani_ofdm_level; + u32 ani_cck_level; + u32 dyntxchain; + u32 proxy_sta; + u32 idle_ps_config; + u32 power_gating_sleep; + u32 fast_channel_reset; + u32 burst_dur; + u32 burst_enable; +}; + +#define WMI_PDEV_PARAM_UNSUPPORTED 0 + enum wmi_pdev_param { /* TX chian mask */ WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1, @@ -2171,6 +2225,97 @@ enum wmi_pdev_param { WMI_PDEV_PARAM_POWER_GATING_SLEEP, }; +enum wmi_10x_pdev_param { + /* TX chian mask */ + WMI_10X_PDEV_PARAM_TX_CHAIN_MASK = 0x1, + /* RX chian mask */ + WMI_10X_PDEV_PARAM_RX_CHAIN_MASK, + /* TX power limit for 2G Radio */ + WMI_10X_PDEV_PARAM_TXPOWER_LIMIT2G, + /* TX power limit for 5G Radio */ + WMI_10X_PDEV_PARAM_TXPOWER_LIMIT5G, + /* TX power scale */ + WMI_10X_PDEV_PARAM_TXPOWER_SCALE, + /* Beacon generation mode . 0: host, 1: target */ + WMI_10X_PDEV_PARAM_BEACON_GEN_MODE, + /* Beacon generation mode . 0: staggered 1: bursted */ + WMI_10X_PDEV_PARAM_BEACON_TX_MODE, + /* + * Resource manager off chan mode . + * 0: turn off off chan mode. 1: turn on offchan mode + */ + WMI_10X_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + /* + * Protection mode: + * 0: no protection 1:use CTS-to-self 2: use RTS/CTS + */ + WMI_10X_PDEV_PARAM_PROTECTION_MODE, + /* Dynamic bandwidth 0: disable 1: enable */ + WMI_10X_PDEV_PARAM_DYNAMIC_BW, + /* Non aggregrate/ 11g sw retry threshold.0-disable */ + WMI_10X_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + /* aggregrate sw retry threshold. 0-disable*/ + WMI_10X_PDEV_PARAM_AGG_SW_RETRY_TH, + /* Station kickout threshold (non of consecutive failures).0-disable */ + WMI_10X_PDEV_PARAM_STA_KICKOUT_TH, + /* Aggerate size scaling configuration per AC */ + WMI_10X_PDEV_PARAM_AC_AGGRSIZE_SCALING, + /* LTR enable */ + WMI_10X_PDEV_PARAM_LTR_ENABLE, + /* LTR latency for BE, in us */ + WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BE, + /* LTR latency for BK, in us */ + WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BK, + /* LTR latency for VI, in us */ + WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VI, + /* LTR latency for VO, in us */ + WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VO, + /* LTR AC latency timeout, in ms */ + WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + /* LTR platform latency override, in us */ + WMI_10X_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + /* LTR-RX override, in us */ + WMI_10X_PDEV_PARAM_LTR_RX_OVERRIDE, + /* Tx activity timeout for LTR, in us */ + WMI_10X_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + /* L1SS state machine enable */ + WMI_10X_PDEV_PARAM_L1SS_ENABLE, + /* Deep sleep state machine enable */ + WMI_10X_PDEV_PARAM_DSLEEP_ENABLE, + /* pdev level stats update period in ms */ + WMI_10X_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + /* vdev level stats update period in ms */ + WMI_10X_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + /* peer level stats update period in ms */ + WMI_10X_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + /* beacon filter status update period */ + WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + /* QOS Mgmt frame protection MFP/PMF 0: disable, 1: enable */ + WMI_10X_PDEV_PARAM_PMF_QOS, + /* Access category on which ARP and DHCP frames are sent */ + WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE, + /* DCS configuration */ + WMI_10X_PDEV_PARAM_DCS, + /* Enable/Disable ANI on target */ + WMI_10X_PDEV_PARAM_ANI_ENABLE, + /* configure the ANI polling period */ + WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD, + /* configure the ANI listening period */ + WMI_10X_PDEV_PARAM_ANI_LISTEN_PERIOD, + /* configure OFDM immunity level */ + WMI_10X_PDEV_PARAM_ANI_OFDM_LEVEL, + /* configure CCK immunity level */ + WMI_10X_PDEV_PARAM_ANI_CCK_LEVEL, + /* Enable/Disable CDD for 1x1 STAs in rate control module */ + WMI_10X_PDEV_PARAM_DYNTXCHAIN, + /* Enable/Disable Fast channel reset*/ + WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET, + /* Set Bursting DUR */ + WMI_10X_PDEV_PARAM_BURST_DUR, + /* Set Bursting Enable*/ + WMI_10X_PDEV_PARAM_BURST_ENABLE, +}; + struct wmi_pdev_set_param_cmd { __le32 param_id; __le32 param_value; @@ -3797,8 +3942,7 @@ int ath10k_wmi_pdev_suspend_target(struct ath10k *ar); int ath10k_wmi_pdev_resume_target(struct ath10k *ar); int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, u16 rd5g, u16 ctl2g, u16 ctl5g); -int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id, - u32 value); +int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value); int ath10k_wmi_cmd_init(struct ath10k *ar); int ath10k_wmi_start_scan(struct ath10k *ar, const struct wmi_start_scan_arg *); void ath10k_wmi_start_scan_init(struct ath10k *ar, struct wmi_start_scan_arg *); -- cgit v1.2.3 From 89b7e766c84020c06f842f390b18d62798dc5d06 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Thu, 26 Sep 2013 17:47:17 +0200 Subject: ath10k: handle FW API differences for scan structures The wmi_start_scan_cmd has an extra filed in our main firmware track, reflact that to not have a mismatch in case of 10.x track. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 19 ++++++-- drivers/net/wireless/ath/ath10k/wmi.h | 82 +++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 4 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 3460cf44684..e7dc911c213 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2341,11 +2341,15 @@ int ath10k_wmi_cmd_init(struct ath10k *ar) return ret; } -static int ath10k_wmi_start_scan_calc_len(const struct wmi_start_scan_arg *arg) +static int ath10k_wmi_start_scan_calc_len(struct ath10k *ar, + const struct wmi_start_scan_arg *arg) { int len; - len = sizeof(struct wmi_start_scan_cmd); + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + len = sizeof(struct wmi_start_scan_cmd_10x); + else + len = sizeof(struct wmi_start_scan_cmd); if (arg->ie_len) { if (!arg->ie) @@ -2405,7 +2409,7 @@ int ath10k_wmi_start_scan(struct ath10k *ar, int len = 0; int i; - len = ath10k_wmi_start_scan_calc_len(arg); + len = ath10k_wmi_start_scan_calc_len(ar, arg); if (len < 0) return len; /* len contains error code here */ @@ -2437,7 +2441,14 @@ int ath10k_wmi_start_scan(struct ath10k *ar, cmd->scan_ctrl_flags = __cpu_to_le32(arg->scan_ctrl_flags); /* TLV list starts after fields included in the struct */ - off = sizeof(*cmd); + /* There's just one filed that differes the two start_scan + * structures - burst_duration, which we are not using btw, + no point to make the split here, just shift the buffer to fit with + given FW */ + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + off = sizeof(struct wmi_start_scan_cmd_10x); + else + off = sizeof(struct wmi_start_scan_cmd); if (arg->n_channels) { channels = (void *)skb->data + off; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 80ab2f0fbe5..7692c14c2d1 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -1715,6 +1715,88 @@ struct wmi_start_scan_cmd { */ } __packed; +/* This is the definition from 10.X firmware branch */ +struct wmi_start_scan_cmd_10x { + /* Scan ID */ + __le32 scan_id; + + /* Scan requestor ID */ + __le32 scan_req_id; + + /* VDEV id(interface) that is requesting scan */ + __le32 vdev_id; + + /* Scan Priority, input to scan scheduler */ + __le32 scan_priority; + + /* Scan events subscription */ + __le32 notify_scan_events; + + /* dwell time in msec on active channels */ + __le32 dwell_time_active; + + /* dwell time in msec on passive channels */ + __le32 dwell_time_passive; + + /* + * min time in msec on the BSS channel,only valid if atleast one + * VDEV is active + */ + __le32 min_rest_time; + + /* + * max rest time in msec on the BSS channel,only valid if at least + * one VDEV is active + */ + /* + * the scanner will rest on the bss channel at least min_rest_time + * after min_rest_time the scanner will start checking for tx/rx + * activity on all VDEVs. if there is no activity the scanner will + * switch to off channel. if there is activity the scanner will let + * the radio on the bss channel until max_rest_time expires.at + * max_rest_time scanner will switch to off channel irrespective of + * activity. activity is determined by the idle_time parameter. + */ + __le32 max_rest_time; + + /* + * time before sending next set of probe requests. + * The scanner keeps repeating probe requests transmission with + * period specified by repeat_probe_time. + * The number of probe requests specified depends on the ssid_list + * and bssid_list + */ + __le32 repeat_probe_time; + + /* time in msec between 2 consequetive probe requests with in a set. */ + __le32 probe_spacing_time; + + /* + * data inactivity time in msec on bss channel that will be used by + * scanner for measuring the inactivity. + */ + __le32 idle_time; + + /* maximum time in msec allowed for scan */ + __le32 max_scan_time; + + /* + * delay in msec before sending first probe request after switching + * to a channel + */ + __le32 probe_delay; + + /* Scan control flags */ + __le32 scan_ctrl_flags; + + /* + * TLV (tag length value ) paramerters follow the scan_cmd structure. + * TLV can contain channel list, bssid list, ssid list and + * ie. the TLV tags are defined above; + */ +} __packed; + + struct wmi_ssid_arg { int len; const u8 *ssid; -- cgit v1.2.3 From 365279167c1ee54c8f4c7cf77752433a3e41b30b Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 27 Sep 2013 19:54:55 +0300 Subject: ath10k: rename board_data in struct ath10k I will use board_data for something else in the following patch so I need to rename it. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 18 +++++++++--------- drivers/net/wireless/ath/ath10k/core.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index bd74dacad1e..494b4c79524 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -215,7 +215,7 @@ static int ath10k_push_board_ext_data(struct ath10k *ar, static int ath10k_download_board_data(struct ath10k *ar) { - const struct firmware *fw = ar->board_data; + const struct firmware *fw = ar->board; u32 board_data_size = QCA988X_BOARD_DATA_SZ; u32 address; int ret; @@ -298,8 +298,8 @@ exit: static void ath10k_core_free_firmware_files(struct ath10k *ar) { - if (ar->board_data && !IS_ERR(ar->board_data)) - release_firmware(ar->board_data); + if (ar->board && !IS_ERR(ar->board)) + release_firmware(ar->board); if (ar->otp && !IS_ERR(ar->otp)) release_firmware(ar->otp); @@ -307,7 +307,7 @@ static void ath10k_core_free_firmware_files(struct ath10k *ar) if (ar->firmware && !IS_ERR(ar->firmware)) release_firmware(ar->firmware); - ar->board_data = NULL; + ar->board = NULL; ar->otp = NULL; ar->firmware = NULL; } @@ -326,11 +326,11 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar) return -EINVAL; } - ar->board_data = ath10k_fetch_fw_file(ar, - ar->hw_params.fw.dir, - ar->hw_params.fw.board); - if (IS_ERR(ar->board_data)) { - ret = PTR_ERR(ar->board_data); + ar->board = ath10k_fetch_fw_file(ar, + ar->hw_params.fw.dir, + ar->hw_params.fw.board); + if (IS_ERR(ar->board)) { + ret = PTR_ERR(ar->board); ath10k_err("could not fetch board data (%d)\n", ret); goto err; } diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index d5da8a9f697..90d93994b31 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -347,7 +347,7 @@ struct ath10k { } fw; } hw_params; - const struct firmware *board_data; + const struct firmware *board; const struct firmware *otp; const struct firmware *firmware; -- cgit v1.2.3 From 958df3a00adb5347a11ef3e11274586e71d3d75b Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 27 Sep 2013 19:55:01 +0300 Subject: ath10k: store separate pointers for firmware data Needed for firmware IE formatted images. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 43 +++++++++++++++++++++++----------- drivers/net/wireless/ath/ath10k/core.h | 8 +++++++ 2 files changed, 37 insertions(+), 14 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 494b4c79524..b8f72f2c34e 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -168,8 +168,7 @@ static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar, return fw; } -static int ath10k_push_board_ext_data(struct ath10k *ar, - const struct firmware *fw) +static int ath10k_push_board_ext_data(struct ath10k *ar) { u32 board_data_size = QCA988X_BOARD_DATA_SZ; u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ; @@ -189,14 +188,14 @@ static int ath10k_push_board_ext_data(struct ath10k *ar, if (board_ext_data_addr == 0) return 0; - if (fw->size != (board_data_size + board_ext_data_size)) { + if (ar->board_len != (board_data_size + board_ext_data_size)) { ath10k_err("invalid board (ext) data sizes %zu != %d+%d\n", - fw->size, board_data_size, board_ext_data_size); + ar->board_len, board_data_size, board_ext_data_size); return -EINVAL; } ret = ath10k_bmi_write_memory(ar, board_ext_data_addr, - fw->data + board_data_size, + ar->board_data + board_data_size, board_ext_data_size); if (ret) { ath10k_err("could not write board ext data (%d)\n", ret); @@ -215,12 +214,11 @@ static int ath10k_push_board_ext_data(struct ath10k *ar, static int ath10k_download_board_data(struct ath10k *ar) { - const struct firmware *fw = ar->board; u32 board_data_size = QCA988X_BOARD_DATA_SZ; u32 address; int ret; - ret = ath10k_push_board_ext_data(ar, fw); + ret = ath10k_push_board_ext_data(ar); if (ret) { ath10k_err("could not push board ext data (%d)\n", ret); goto exit; @@ -232,8 +230,9 @@ static int ath10k_download_board_data(struct ath10k *ar) goto exit; } - ret = ath10k_bmi_write_memory(ar, address, fw->data, - min_t(u32, board_data_size, fw->size)); + ret = ath10k_bmi_write_memory(ar, address, ar->board_data, + min_t(u32, board_data_size, + ar->board_len)); if (ret) { ath10k_err("could not write board data (%d)\n", ret); goto exit; @@ -251,17 +250,16 @@ exit: static int ath10k_download_and_run_otp(struct ath10k *ar) { - const struct firmware *fw = ar->otp; u32 address = ar->hw_params.patch_load_addr; u32 exec_param; int ret; /* OTP is optional */ - if (!ar->otp) + if (!ar->otp_data || !ar->otp_len) return 0; - ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size); + ret = ath10k_bmi_fast_download(ar, address, ar->otp_data, ar->otp_len); if (ret) { ath10k_err("could not write otp (%d)\n", ret); goto exit; @@ -280,13 +278,13 @@ exit: static int ath10k_download_fw(struct ath10k *ar) { - const struct firmware *fw = ar->firmware; u32 address; int ret; address = ar->hw_params.patch_load_addr; - ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size); + ret = ath10k_bmi_fast_download(ar, address, ar->firmware_data, + ar->firmware_len); if (ret) { ath10k_err("could not write fw (%d)\n", ret); goto exit; @@ -308,8 +306,16 @@ static void ath10k_core_free_firmware_files(struct ath10k *ar) release_firmware(ar->firmware); ar->board = NULL; + ar->board_data = NULL; + ar->board_len = 0; + ar->otp = NULL; + ar->otp_data = NULL; + ar->otp_len = 0; + ar->firmware = NULL; + ar->firmware_data = NULL; + ar->firmware_len = 0; } static int ath10k_core_fetch_firmware_files(struct ath10k *ar) @@ -335,6 +341,9 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar) goto err; } + ar->board_data = ar->board->data; + ar->board_len = ar->board->size; + ar->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, ar->hw_params.fw.fw); @@ -344,6 +353,9 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar) goto err; } + ar->firmware_data = ar->firmware->data; + ar->firmware_len = ar->firmware->size; + /* OTP may be undefined. If so, don't fetch it at all */ if (ar->hw_params.fw.otp == NULL) return 0; @@ -357,6 +369,9 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar) goto err; } + ar->otp_data = ar->otp->data; + ar->otp_len = ar->otp->size; + return 0; err: diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 90d93994b31..0c0b229735a 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -348,8 +348,16 @@ struct ath10k { } hw_params; const struct firmware *board; + const void *board_data; + size_t board_len; + const struct firmware *otp; + const void *otp_data; + size_t otp_len; + const struct firmware *firmware; + const void *firmware_data; + size_t firmware_len; struct { struct completion started; -- cgit v1.2.3 From 1a222435a1b0ed2f87f4752abdf03065b574dfac Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 27 Sep 2013 19:55:07 +0300 Subject: ath10k: implement firmware IE container support Firmware IE containers can dynamically provide various information what firmware supports. Also it can embed more than one image so updating firmware is easy, user just needs to update one file in /lib/firmware/. The firmware API 2 or higher will use the IE container format, the current API 1 will not use the new format but it still is supported for some time. FW API 2 files are named as firmware-2.bin (which contains both firmware and otp images) and API 1 files are firmware.bin and otp.bin. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 184 ++++++++++++++++++++++++++++++++- drivers/net/wireless/ath/ath10k/core.h | 2 + drivers/net/wireless/ath/ath10k/hw.h | 19 ++++ drivers/net/wireless/ath/ath10k/wmi.c | 3 +- 4 files changed, 206 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index b8f72f2c34e..7b5dd09fab6 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -318,7 +318,7 @@ static void ath10k_core_free_firmware_files(struct ath10k *ar) ar->firmware_len = 0; } -static int ath10k_core_fetch_firmware_files(struct ath10k *ar) +static int ath10k_core_fetch_firmware_api_1(struct ath10k *ar) { int ret = 0; @@ -379,6 +379,188 @@ err: return ret; } +static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) +{ + size_t magic_len, len, ie_len; + int ie_id, i, index, bit, ret; + struct ath10k_fw_ie *hdr; + const u8 *data; + __le32 *timestamp; + + /* first fetch the firmware file (firmware-*.bin) */ + ar->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, name); + if (IS_ERR(ar->firmware)) { + ath10k_err("Could not fetch firmware file '%s': %ld\n", + name, PTR_ERR(ar->firmware)); + return PTR_ERR(ar->firmware); + } + + data = ar->firmware->data; + len = ar->firmware->size; + + /* magic also includes the null byte, check that as well */ + magic_len = strlen(ATH10K_FIRMWARE_MAGIC) + 1; + + if (len < magic_len) { + ath10k_err("firmware image too small to contain magic: %d\n", + len); + return -EINVAL; + } + + if (memcmp(data, ATH10K_FIRMWARE_MAGIC, magic_len) != 0) { + ath10k_err("Invalid firmware magic\n"); + return -EINVAL; + } + + /* jump over the padding */ + magic_len = ALIGN(magic_len, 4); + + len -= magic_len; + data += magic_len; + + /* loop elements */ + while (len > sizeof(struct ath10k_fw_ie)) { + hdr = (struct ath10k_fw_ie *)data; + + ie_id = le32_to_cpu(hdr->id); + ie_len = le32_to_cpu(hdr->len); + + len -= sizeof(*hdr); + data += sizeof(*hdr); + + if (len < ie_len) { + ath10k_err("Invalid length for FW IE %d (%d < %d)\n", + ie_id, len, ie_len); + return -EINVAL; + } + + switch (ie_id) { + case ATH10K_FW_IE_FW_VERSION: + if (ie_len > sizeof(ar->hw->wiphy->fw_version) - 1) + break; + + memcpy(ar->hw->wiphy->fw_version, data, ie_len); + ar->hw->wiphy->fw_version[ie_len] = '\0'; + + ath10k_dbg(ATH10K_DBG_BOOT, + "found fw version %s\n", + ar->hw->wiphy->fw_version); + break; + case ATH10K_FW_IE_TIMESTAMP: + if (ie_len != sizeof(u32)) + break; + + timestamp = (__le32 *)data; + + ath10k_dbg(ATH10K_DBG_BOOT, "found fw timestamp %d\n", + le32_to_cpup(timestamp)); + break; + case ATH10K_FW_IE_FEATURES: + ath10k_dbg(ATH10K_DBG_BOOT, + "found firmware features ie (%zd B)\n", + ie_len); + + for (i = 0; i < ATH10K_FW_FEATURE_COUNT; i++) { + index = i / 8; + bit = i % 8; + + if (index == ie_len) + break; + + if (data[index] & (1 << bit)) + __set_bit(i, ar->fw_features); + } + + ath10k_dbg_dump(ATH10K_DBG_BOOT, "features", "", + ar->fw_features, + sizeof(ar->fw_features)); + break; + case ATH10K_FW_IE_FW_IMAGE: + ath10k_dbg(ATH10K_DBG_BOOT, + "found fw image ie (%zd B)\n", + ie_len); + + ar->firmware_data = data; + ar->firmware_len = ie_len; + + break; + case ATH10K_FW_IE_OTP_IMAGE: + ath10k_dbg(ATH10K_DBG_BOOT, + "found otp image ie (%zd B)\n", + ie_len); + + ar->otp_data = data; + ar->otp_len = ie_len; + + break; + default: + ath10k_warn("Unknown FW IE: %u\n", + le32_to_cpu(hdr->id)); + break; + } + + /* jump over the padding */ + ie_len = ALIGN(ie_len, 4); + + len -= ie_len; + data += ie_len; + }; + + if (!ar->firmware_data || !ar->firmware_len) { + ath10k_warn("No ATH10K_FW_IE_FW_IMAGE found from %s, skipping\n", + name); + ret = -ENOMEDIUM; + goto err; + } + + /* now fetch the board file */ + if (ar->hw_params.fw.board == NULL) { + ath10k_err("board data file not defined"); + ret = -EINVAL; + goto err; + } + + ar->board = ath10k_fetch_fw_file(ar, + ar->hw_params.fw.dir, + ar->hw_params.fw.board); + if (IS_ERR(ar->board)) { + ret = PTR_ERR(ar->board); + ath10k_err("could not fetch board data (%d)\n", ret); + goto err; + } + + ar->board_data = ar->board->data; + ar->board_len = ar->board->size; + + return 0; + +err: + ath10k_core_free_firmware_files(ar); + return ret; +} + +static int ath10k_core_fetch_firmware_files(struct ath10k *ar) +{ + int ret; + + ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API2_FILE); + if (ret == 0) { + ar->fw_api = 2; + goto out; + } + + ret = ath10k_core_fetch_firmware_api_1(ar); + if (ret) + return ret; + + ar->fw_api = 1; + +out: + ath10k_dbg(ATH10K_DBG_BOOT, "using fw api %d\n", ar->fw_api); + + return 0; +} + static int ath10k_init_download_firmware(struct ath10k *ar) { int ret; diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 0c0b229735a..ce36daa5ff0 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -359,6 +359,8 @@ struct ath10k { const void *firmware_data; size_t firmware_len; + int fw_api; + struct { struct completion started; struct completion completed; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 2de13db5e5d..3fd7b9864d1 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -32,6 +32,25 @@ #define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin" #define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234 +#define ATH10K_FW_API2_FILE "firmware-2.bin" + +/* includes also the null byte */ +#define ATH10K_FIRMWARE_MAGIC "QCA-ATH10K" + +struct ath10k_fw_ie { + __le32 id; + __le32 len; + u8 data[0]; +}; + +enum ath10k_fw_ie_type { + ATH10K_FW_IE_FW_VERSION = 0, + ATH10K_FW_IE_TIMESTAMP = 1, + ATH10K_FW_IE_FEATURES = 2, + ATH10K_FW_IE_FW_IMAGE = 3, + ATH10K_FW_IE_OTP_IMAGE = 4, +}; + /* Known pecularities: * - current FW doesn't support raw rx mode (last tested v599) * - current FW dumps upon raw tx mode (last tested v599) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index e7dc911c213..be75571d21a 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1559,7 +1559,8 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, ar->phy_capability = __le32_to_cpu(ev->phy_capability); ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains); - if (ar->fw_version_build > 636) + /* only manually set fw features when not using FW IE format */ + if (ar->fw_api == 1 && ar->fw_version_build > 636) set_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features); if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) { -- cgit v1.2.3 From a07b5b840c5f4fda31b835806c0095408637f432 Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Sun, 29 Sep 2013 15:54:31 +0200 Subject: ath6kl: fix compilation warning in ath6kl_htc_pipe_conn_service Fix the warning drivers/net/wireless/ath/ath6kl/htc_pipe.c: In function 'ath6kl_htc_pipe_conn_service': drivers/net/wireless/ath/ath6kl/htc_pipe.c:1293:26: warning: integer overflow in expression [-Woverflow] by giving a hint to compiler about unsigned nature of HTC_CONN_FLGS_SET_RECV_ALLOC_MASK Signed-off-by: Vladimir Murzin Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/htc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath6kl/htc.h b/drivers/net/wireless/ath/ath6kl/htc.h index a2c8ff80979..14cab1403dd 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.h +++ b/drivers/net/wireless/ath/ath6kl/htc.h @@ -60,7 +60,7 @@ /* disable credit flow control on a specific service */ #define HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL (1 << 3) #define HTC_CONN_FLGS_SET_RECV_ALLOC_SHIFT 8 -#define HTC_CONN_FLGS_SET_RECV_ALLOC_MASK 0xFF00 +#define HTC_CONN_FLGS_SET_RECV_ALLOC_MASK 0xFF00U /* connect response status codes */ #define HTC_SERVICE_SUCCESS 0 -- cgit v1.2.3 From 8acd3c97ff9ea803513c55f89d3f1832481b5b76 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 2 Oct 2013 08:48:51 +0200 Subject: ath10k: enable 10.x firmware branch support Since the WMI API has been added and we can detect from the FW IEs what firmware variant we deal with, turn on support for 10.x firmware branch in ath10k_wmi_attach(). kvalo: improve the commit log Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index be75571d21a..416dabbc499 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -1970,26 +1970,21 @@ static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb) /* WMI Initialization functions */ int ath10k_wmi_attach(struct ath10k *ar) { - int ret; - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - ath10k_warn("Firmware 10.X is not yet supported\n"); ar->wmi.cmd = &wmi_10x_cmd_map; ar->wmi.vdev_param = &wmi_10x_vdev_param_map; ar->wmi.pdev_param = &wmi_10x_pdev_param_map; - ret = -ENOTSUPP; } else { ar->wmi.cmd = &wmi_cmd_map; ar->wmi.vdev_param = &wmi_vdev_param_map; ar->wmi.pdev_param = &wmi_pdev_param_map; - ret = 0; } init_completion(&ar->wmi.service_ready); init_completion(&ar->wmi.unified_ready); init_waitqueue_head(&ar->wmi.tx_credits_wq); - return ret; + return 0; } void ath10k_wmi_detach(struct ath10k *ar) -- cgit v1.2.3 From c322892fa7084480efb0708d7d8d60d5c83de2eb Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Wed, 2 Oct 2013 08:48:52 +0200 Subject: ath10k: extend the max_scan time It was observed few times, the artificial max_scan limit we are using mainly to detect FW hangs, can be not enough for instance while being associated and during heavy traffic. What we do if the FW won't return with scan results within the max_time time is a scan abort. This is especially visible with 10.X fw which in combination with dual band HW (scanning 32 channels) can end up with hw_scan close to 10 seconds. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 416dabbc499..f9766faf15c 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2522,7 +2522,7 @@ void ath10k_wmi_start_scan_init(struct ath10k *ar, arg->repeat_probe_time = 0; arg->probe_spacing_time = 0; arg->idle_time = 0; - arg->max_scan_time = 5000; + arg->max_scan_time = 20000; arg->probe_delay = 5; arg->notify_scan_events = WMI_SCAN_EVENT_STARTED | WMI_SCAN_EVENT_COMPLETED -- cgit v1.2.3 From 42c3aa6f6ae7de082b7413321e67e3424cc0c002 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 2 Oct 2013 11:03:38 +0200 Subject: ath10k: split tid calculation from tx function Tidy up ath10k_tx(). Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 8684e03b393..7415a6045d3 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1407,6 +1407,20 @@ static void ath10k_reg_notifier(struct wiphy *wiphy, /* TX handlers */ /***************/ +static u8 ath10k_tx_h_get_tid(struct ieee80211_hdr *hdr) +{ + if (ieee80211_is_mgmt(hdr->frame_control)) + return HTT_DATA_TX_EXT_TID_MGMT; + + if (!ieee80211_is_data_qos(hdr->frame_control)) + return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST; + + if (!is_unicast_ether_addr(ieee80211_get_DA(hdr))) + return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST; + + return ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK; +} + /* * Frames sent to the FW have to be in "Native Wifi" format. * Strip the QoS field from the 802.11 header. @@ -1788,14 +1802,7 @@ static void ath10k_tx(struct ieee80211_hw *hw, /* we must calculate tid before we apply qos workaround * as we'd lose the qos control field */ - tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST; - if (ieee80211_is_mgmt(hdr->frame_control)) { - tid = HTT_DATA_TX_EXT_TID_MGMT; - } else if (ieee80211_is_data_qos(hdr->frame_control) && - is_unicast_ether_addr(ieee80211_get_DA(hdr))) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; - } + tid = ath10k_tx_h_get_tid(hdr); /* it makes no sense to process injected frames like that */ if (info->control.vif && -- cgit v1.2.3 From ddb6ad77b4ecfb832982be69f2169165e6a43f0b Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 2 Oct 2013 11:03:39 +0200 Subject: ath10k: split vdev_id calculation from tx function Tidy up ath10k_tx(). Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 7415a6045d3..4b7c9494890 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1421,6 +1421,19 @@ static u8 ath10k_tx_h_get_tid(struct ieee80211_hdr *hdr) return ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK; } +static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar, + struct ieee80211_tx_info *info) +{ + if (info->control.vif) + return ath10k_vif_to_arvif(info->control.vif)->vdev_id; + + if (ar->monitor_enabled) + return ar->monitor_vdev_id; + + ath10k_warn("could not resolve vdev id\n"); + return 0; +} + /* * Frames sent to the FW have to be in "Native Wifi" format. * Strip the QoS field from the 802.11 header. @@ -1785,16 +1798,7 @@ static void ath10k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ath10k *ar = hw->priv; - struct ath10k_vif *arvif = NULL; - u32 vdev_id = 0; - u8 tid; - - if (info->control.vif) { - arvif = ath10k_vif_to_arvif(info->control.vif); - vdev_id = arvif->vdev_id; - } else if (ar->monitor_enabled) { - vdev_id = ar->monitor_vdev_id; - } + u8 tid, vdev_id; /* We should disable CCK RATE due to P2P */ if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE) @@ -1803,6 +1807,7 @@ static void ath10k_tx(struct ieee80211_hw *hw, /* we must calculate tid before we apply qos workaround * as we'd lose the qos control field */ tid = ath10k_tx_h_get_tid(hdr); + vdev_id = ath10k_tx_h_get_vdev_id(ar, info); /* it makes no sense to process injected frames like that */ if (info->control.vif && -- cgit v1.2.3 From 2e761b5a5222071d55ebccf65f93d281e4c11958 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 2 Oct 2013 11:03:40 +0200 Subject: ath10k: remove ce_sendlist_send It is completely pointless to keep this function around. It doesn't do anything different than ce_send except it introduces more overhead. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/ce.c | 34 ---------------------------------- drivers/net/wireless/ath/ath10k/ce.h | 15 --------------- drivers/net/wireless/ath/ath10k/pci.c | 4 ++-- 3 files changed, 2 insertions(+), 51 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index 834e29ea236..9e6daa9c28b 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -338,40 +338,6 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state, return ret; } -int ath10k_ce_sendlist_send(struct ath10k_ce_pipe *ce_state, - void *per_transfer_context, - unsigned int transfer_id, - u32 paddr, unsigned int nbytes, - u32 flags) -{ - struct ath10k_ce_ring *src_ring = ce_state->src_ring; - struct ath10k *ar = ce_state->ar; - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - unsigned int nentries_mask = src_ring->nentries_mask; - unsigned int sw_index; - unsigned int write_index; - int delta, ret = -ENOMEM; - - spin_lock_bh(&ar_pci->ce_lock); - - sw_index = src_ring->sw_index; - write_index = src_ring->write_index; - - delta = CE_RING_DELTA(nentries_mask, write_index, sw_index - 1); - - if (delta >= 1) { - ret = ath10k_ce_send_nolock(ce_state, per_transfer_context, - paddr, nbytes, - transfer_id, flags); - if (ret) - ath10k_warn("CE send failed: %d\n", ret); - } - - spin_unlock_bh(&ar_pci->ce_lock); - - return ret; -} - int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state, void *per_recv_context, u32 buffer) diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h index aec80286834..949b1744870 100644 --- a/drivers/net/wireless/ath/ath10k/ce.h +++ b/drivers/net/wireless/ath/ath10k/ce.h @@ -156,21 +156,6 @@ void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state, void (*send_cb)(struct ath10k_ce_pipe *), int disable_interrupts); -/* - * Queue a "sendlist" of buffers to be sent using gather to a single - * anonymous destination buffer - * ce - which copy engine to use - * sendlist - list of simple buffers to send using gather - * transfer_id - arbitrary ID; reflected to destination - * Returns 0 on success; otherwise an error status. - * - * Implemenation note: Pushes multiple buffers with Gather to Source ring. - */ -int ath10k_ce_sendlist_send(struct ath10k_ce_pipe *ce_state, - void *per_transfer_context, - unsigned int transfer_id, - u32 paddr, unsigned int nbytes, - u32 flags); /*==================Recv=======================*/ diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index dff23d97bed..4f6a9787e2d 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -730,8 +730,8 @@ static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id, pipe_info->num_sends_allowed--; spin_unlock_bh(&pipe_info->pipe_lock); - ret = ath10k_ce_sendlist_send(ce_hdl, nbuf, transfer_id, - skb_cb->paddr, len, flags); + ret = ath10k_ce_send(ce_hdl, nbuf, skb_cb->paddr, len, transfer_id, + flags); if (ret) ath10k_warn("CE send failed: %p\n", nbuf); -- cgit v1.2.3 From 3efcb3b40cc40091661eb376be175a6971616f6b Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 2 Oct 2013 11:03:41 +0200 Subject: ath10k: remove num_sends_allowed The value provided by num_sends_allowed is now derived from CE source ringbuffer state. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/ce.c | 17 ++++++++++++++++- drivers/net/wireless/ath/ath10k/ce.h | 1 + drivers/net/wireless/ath/ath10k/pci.c | 21 +-------------------- drivers/net/wireless/ath/ath10k/pci.h | 3 --- 4 files changed, 18 insertions(+), 24 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index 9e6daa9c28b..e46951b8fb9 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -283,7 +283,7 @@ static int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, if (unlikely(CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) <= 0)) { - ret = -EIO; + ret = -ENOSR; goto exit; } @@ -338,6 +338,21 @@ int ath10k_ce_send(struct ath10k_ce_pipe *ce_state, return ret; } +int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe) +{ + struct ath10k *ar = pipe->ar; + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + int delta; + + spin_lock_bh(&ar_pci->ce_lock); + delta = CE_RING_DELTA(pipe->src_ring->nentries_mask, + pipe->src_ring->write_index, + pipe->src_ring->sw_index - 1); + spin_unlock_bh(&ar_pci->ce_lock); + + return delta; +} + int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state, void *per_recv_context, u32 buffer) diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h index 949b1744870..15d45b5b761 100644 --- a/drivers/net/wireless/ath/ath10k/ce.h +++ b/drivers/net/wireless/ath/ath10k/ce.h @@ -156,6 +156,7 @@ void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state, void (*send_cb)(struct ath10k_ce_pipe *), int disable_interrupts); +int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe); /*==================Recv=======================*/ diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 4f6a9787e2d..f8d59c7b908 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -720,16 +720,6 @@ static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id, "ath10k tx: data: ", nbuf->data, nbuf->len); - /* Make sure we have resources to handle this request */ - spin_lock_bh(&pipe_info->pipe_lock); - if (!pipe_info->num_sends_allowed) { - ath10k_warn("Pipe: %d is full\n", pipe_id); - spin_unlock_bh(&pipe_info->pipe_lock); - return -ENOSR; - } - pipe_info->num_sends_allowed--; - spin_unlock_bh(&pipe_info->pipe_lock); - ret = ath10k_ce_send(ce_hdl, nbuf, skb_cb->paddr, len, transfer_id, flags); if (ret) @@ -741,14 +731,7 @@ static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id, static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ath10k_pci_pipe *pipe_info = &(ar_pci->pipe_info[pipe]); - int ret; - - spin_lock_bh(&pipe_info->pipe_lock); - ret = pipe_info->num_sends_allowed; - spin_unlock_bh(&pipe_info->pipe_lock); - - return ret; + return ath10k_ce_num_free_src_entries(ar_pci->pipe_info[pipe].ce_hdl); } static void ath10k_pci_hif_dump_area(struct ath10k *ar) @@ -863,7 +846,6 @@ static int ath10k_pci_start_ce(struct ath10k *ar) ath10k_pci_ce_send_done, disable_interrupts); completions += attr->src_nentries; - pipe_info->num_sends_allowed = attr->src_nentries - 1; } if (attr->dest_nentries) { @@ -1033,7 +1015,6 @@ static void ath10k_pci_process_ce(struct ath10k *ar) */ spin_lock_bh(&compl->pipe_info->pipe_lock); list_add_tail(&compl->list, &compl->pipe_info->compl_free); - compl->pipe_info->num_sends_allowed += send_done; spin_unlock_bh(&compl->pipe_info->pipe_lock); } diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index 7c49f6f96f7..52fb7b97357 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -178,9 +178,6 @@ struct ath10k_pci_pipe { /* List of free CE completion slots */ struct list_head compl_free; - /* Limit the number of outstanding send requests. */ - int num_sends_allowed; - struct ath10k_pci *ar_pci; struct tasklet_struct intr; }; -- cgit v1.2.3 From fd9c4864a749ba1079fd73bf8394bc15d5c2c892 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 4 Oct 2013 08:13:19 +0200 Subject: ath10k: fix printf format string size_t corresponds to %zu not %d. Compiler was complaining about it. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 7b5dd09fab6..bf85d34e51c 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -402,7 +402,7 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) magic_len = strlen(ATH10K_FIRMWARE_MAGIC) + 1; if (len < magic_len) { - ath10k_err("firmware image too small to contain magic: %d\n", + ath10k_err("firmware image too small to contain magic: %zu\n", len); return -EINVAL; } @@ -429,7 +429,7 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) data += sizeof(*hdr); if (len < ie_len) { - ath10k_err("Invalid length for FW IE %d (%d < %d)\n", + ath10k_err("Invalid length for FW IE %d (%zu < %zu)\n", ie_id, len, ie_len); return -EINVAL; } -- cgit v1.2.3 From 9bab1cc010bf07aed12e3f738528c2f05b53db55 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 4 Oct 2013 08:13:20 +0200 Subject: ath10k: fix possible memory leak in new FW loading Some failpaths did `return` instead of a `goto` leaking requested firmware. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index bf85d34e51c..5acb4048b00 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -404,12 +404,14 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) if (len < magic_len) { ath10k_err("firmware image too small to contain magic: %zu\n", len); - return -EINVAL; + ret = -EINVAL; + goto err; } if (memcmp(data, ATH10K_FIRMWARE_MAGIC, magic_len) != 0) { ath10k_err("Invalid firmware magic\n"); - return -EINVAL; + ret = -EINVAL; + goto err; } /* jump over the padding */ @@ -431,7 +433,8 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) if (len < ie_len) { ath10k_err("Invalid length for FW IE %d (%zu < %zu)\n", ie_id, len, ie_len); - return -EINVAL; + ret = -EINVAL; + goto err; } switch (ie_id) { -- cgit v1.2.3 From 0d1a28f241e917d199ad72644edd0d6bb6726577 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 7 Oct 2013 20:00:36 -0700 Subject: ath10k: fix RX performance when using AP 10.X FW Due to oversight AP 10.X support was merged with Ethernet RX decap mode. Only Native Wifi RX decap mode guarantees IP header is properly aligned and avoids sk_buff data realignment (which is very expensive performance-wise) in mac80211. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/hw.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 3fd7b9864d1..8aeb46d9b53 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -123,7 +123,7 @@ enum ath10k_mcast2ucast_mode { #define TARGET_10X_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2)) #define TARGET_10X_RX_TIMEOUT_LO_PRI 100 #define TARGET_10X_RX_TIMEOUT_HI_PRI 40 -#define TARGET_10X_RX_DECAP_MODE ATH10K_HW_TXRX_ETHERNET +#define TARGET_10X_RX_DECAP_MODE ATH10K_HW_TXRX_NATIVE_WIFI #define TARGET_10X_SCAN_MAX_PENDING_REQS 4 #define TARGET_10X_BMISS_OFFLOAD_MAX_VDEV 2 #define TARGET_10X_ROAM_OFFLOAD_MAX_VDEV 2 -- cgit v1.2.3 From a24b88b56074424a413acc2d2a517fb82f5e7c2f Mon Sep 17 00:00:00 2001 From: Sujith Manoharan Date: Mon, 7 Oct 2013 19:51:57 -0700 Subject: ath10k: Fix bug in max. VHT A-MPDU size For VHT peers, the maximum A-MPDU size has to be calculated from the VHT capabilities element and not the HT-cap. The formula is the same, but a higher value is used in VHT, allowing larger aggregates to be transmitted. The patch contains a workaround for some Netgear/Linksys APs that report Rx A-MPDU factor incorrectly. Tested-by: Kalle Valo Signed-off-by: Sujith Manoharan Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 4b7c9494890..049eca2eb0c 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1033,14 +1033,27 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar, struct wmi_peer_assoc_complete_arg *arg) { const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; + u8 ampdu_factor; if (!vht_cap->vht_supported) return; arg->peer_flags |= WMI_PEER_VHT; - arg->peer_vht_caps = vht_cap->cap; + + ampdu_factor = (vht_cap->cap & + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >> + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; + + /* Workaround: Some Netgear/Linksys 11ac APs set Rx A-MPDU factor to + * zero in VHT IE. Using it would result in degraded throughput. + * arg->peer_max_mpdu at this point contains HT max_mpdu so keep + * it if VHT max_mpdu is smaller. */ + arg->peer_max_mpdu = max(arg->peer_max_mpdu, + (1U << (IEEE80211_HT_MAX_AMPDU_FACTOR + + ampdu_factor)) - 1); + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) arg->peer_flags |= WMI_PEER_80MHZ; -- cgit v1.2.3 From 60631c5c10efbf24a77e1ca1ddecdb4e82ed2833 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 8 Oct 2013 21:45:25 +0300 Subject: ath10k: fix ath10k_debug_start() locking ath10k_debug_start() was not called with conf_mutex, fix that. Also there was a deadlock in ath10k_debug_stop(), rename it to ath10k_debug_destroy() and call it only when the device is destroyed. Reported-by: Mohammed Shafi Shajakhan Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 12 ++++++++++++ drivers/net/wireless/ath/ath10k/debug.c | 15 ++++++++++++++- drivers/net/wireless/ath/ath10k/debug.h | 5 +++++ 3 files changed, 31 insertions(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 5acb4048b00..e016f5141c6 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -737,6 +737,8 @@ EXPORT_SYMBOL(ath10k_core_create); void ath10k_core_destroy(struct ath10k *ar) { + ath10k_debug_destroy(ar); + flush_workqueue(ar->workqueue); destroy_workqueue(ar->workqueue); @@ -748,6 +750,8 @@ int ath10k_core_start(struct ath10k *ar) { int status; + lockdep_assert_held(&ar->conf_mutex); + ath10k_bmi_start(ar); if (ath10k_init_configure_target(ar)) { @@ -836,6 +840,8 @@ EXPORT_SYMBOL(ath10k_core_start); void ath10k_core_stop(struct ath10k *ar) { + lockdep_assert_held(&ar->conf_mutex); + ath10k_debug_stop(ar); ath10k_htc_stop(&ar->htc); ath10k_htt_detach(&ar->htt); @@ -883,15 +889,21 @@ static int ath10k_core_probe_fw(struct ath10k *ar) return ret; } + mutex_lock(&ar->conf_mutex); + ret = ath10k_core_start(ar); if (ret) { ath10k_err("could not init core (%d)\n", ret); ath10k_core_free_firmware_files(ar); ath10k_hif_power_down(ar); + mutex_unlock(&ar->conf_mutex); return ret; } ath10k_core_stop(ar); + + mutex_unlock(&ar->conf_mutex); + ath10k_hif_power_down(ar); return 0; } diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 59615c7f217..760ff2289e3 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -618,6 +618,8 @@ int ath10k_debug_start(struct ath10k *ar) { int ret; + lockdep_assert_held(&ar->conf_mutex); + ret = ath10k_debug_htt_stats_req(ar); if (ret) /* continue normally anyway, this isn't serious */ @@ -628,7 +630,13 @@ int ath10k_debug_start(struct ath10k *ar) void ath10k_debug_stop(struct ath10k *ar) { - cancel_delayed_work_sync(&ar->debug.htt_stats_dwork); + lockdep_assert_held(&ar->conf_mutex); + + /* Must not use _sync to avoid deadlock, we do that in + * ath10k_debug_destroy(). The check for htt_stats_mask is to avoid + * warning from del_timer(). */ + if (ar->debug.htt_stats_mask != 0) + cancel_delayed_work(&ar->debug.htt_stats_dwork); } int ath10k_debug_create(struct ath10k *ar) @@ -662,6 +670,11 @@ int ath10k_debug_create(struct ath10k *ar) return 0; } +void ath10k_debug_destroy(struct ath10k *ar) +{ + cancel_delayed_work_sync(&ar->debug.htt_stats_dwork); +} + #endif /* CONFIG_ATH10K_DEBUGFS */ #ifdef CONFIG_ATH10K_DEBUG diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index fa581486626..46e640a6968 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -46,6 +46,7 @@ extern __printf(1, 2) int ath10k_warn(const char *fmt, ...); int ath10k_debug_start(struct ath10k *ar); void ath10k_debug_stop(struct ath10k *ar); int ath10k_debug_create(struct ath10k *ar); +void ath10k_debug_destroy(struct ath10k *ar); void ath10k_debug_read_service_map(struct ath10k *ar, void *service_map, size_t map_size); @@ -67,6 +68,10 @@ static inline int ath10k_debug_create(struct ath10k *ar) return 0; } +static inline void ath10k_debug_destroy(struct ath10k *ar) +{ +} + static inline void ath10k_debug_read_service_map(struct ath10k *ar, void *service_map, size_t map_size) -- cgit v1.2.3 From e05634ee6c267a0417f72050c378033baca0687a Mon Sep 17 00:00:00 2001 From: Fengguang Wu Date: Tue, 8 Oct 2013 21:48:15 +0300 Subject: ath10k: remove unneded semicolon from ath10k_core_fetch_firmware_api_n() drivers/net/wireless/ath/ath10k/core.c:507:2-3: Unneeded semicolon Removes unneeded semicolon. Generated by: coccinelle/misc/semicolon.cocci Signed-off-by: Fengguang Wu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index e016f5141c6..c5561a935bb 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -507,7 +507,7 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) len -= ie_len; data += ie_len; - }; + } if (!ar->firmware_data || !ar->firmware_len) { ath10k_warn("No ATH10K_FW_IE_FW_IMAGE found from %s, skipping\n", -- cgit v1.2.3 From ebc9abddfb6b930ec54087997570967f062eba4c Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Tue, 15 Oct 2013 09:26:20 +0200 Subject: ath10k: do not warn about unsupported vdev param 10.X firmware does not support WMI_VDEV_PARAM_TX_ENCAP_TYPE. It's a known limitation and we should not warn about this. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 3 ++- drivers/net/wireless/ath/ath10k/wmi.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 049eca2eb0c..97d7111be72 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2085,7 +2085,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, vdev_param = ar->wmi.vdev_param->tx_encap_type; ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, ATH10K_HW_TXRX_NATIVE_WIFI); - if (ret) + /* 10.X firmware does not support this VDEV parameter. Do not warn */ + if (ret && ret != -EOPNOTSUPP) ath10k_warn("Failed to set TX encap: %d\n", ret); if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index f9766faf15c..fa073880389 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2763,7 +2763,7 @@ int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ATH10K_DBG_WMI, "vdev param %d not supported by firmware\n", param_id); - return -EINVAL; + return -EOPNOTSUPP; } skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); -- cgit v1.2.3 From 34957b25e39bbdcfd902f27ae719b8ff50c751f8 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Tue, 15 Oct 2013 09:55:31 +0200 Subject: ath10k: rename WMI_CMD_UNDEFINED Rename WMI_CMD_UNDEFINED to WMI_CMD_UNSUPPORTED. This is more accurate here. Also return -EOPNOTSUPP instead of -EINVAL in wmi_cmd_send(). Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 46 +++++++++++++++++------------------ drivers/net/wireless/ath/ath10k/wmi.h | 2 +- 2 files changed, 24 insertions(+), 24 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index fa073880389..9d4752655e5 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -149,7 +149,7 @@ static struct wmi_cmd_map wmi_10x_cmd_map = { .start_scan_cmdid = WMI_10X_START_SCAN_CMDID, .stop_scan_cmdid = WMI_10X_STOP_SCAN_CMDID, .scan_chan_list_cmdid = WMI_10X_SCAN_CHAN_LIST_CMDID, - .scan_sch_prio_tbl_cmdid = WMI_CMD_UNDEFINED, + .scan_sch_prio_tbl_cmdid = WMI_CMD_UNSUPPORTED, .pdev_set_regdomain_cmdid = WMI_10X_PDEV_SET_REGDOMAIN_CMDID, .pdev_set_channel_cmdid = WMI_10X_PDEV_SET_CHANNEL_CMDID, .pdev_set_param_cmdid = WMI_10X_PDEV_SET_PARAM_CMDID, @@ -182,11 +182,11 @@ static struct wmi_cmd_map wmi_10x_cmd_map = { .peer_mcast_group_cmdid = WMI_10X_PEER_MCAST_GROUP_CMDID, .bcn_tx_cmdid = WMI_10X_BCN_TX_CMDID, .pdev_send_bcn_cmdid = WMI_10X_PDEV_SEND_BCN_CMDID, - .bcn_tmpl_cmdid = WMI_CMD_UNDEFINED, + .bcn_tmpl_cmdid = WMI_CMD_UNSUPPORTED, .bcn_filter_rx_cmdid = WMI_10X_BCN_FILTER_RX_CMDID, .prb_req_filter_rx_cmdid = WMI_10X_PRB_REQ_FILTER_RX_CMDID, .mgmt_tx_cmdid = WMI_10X_MGMT_TX_CMDID, - .prb_tmpl_cmdid = WMI_CMD_UNDEFINED, + .prb_tmpl_cmdid = WMI_CMD_UNSUPPORTED, .addba_clear_resp_cmdid = WMI_10X_ADDBA_CLEAR_RESP_CMDID, .addba_send_cmdid = WMI_10X_ADDBA_SEND_CMDID, .addba_status_cmdid = WMI_10X_ADDBA_STATUS_CMDID, @@ -211,9 +211,9 @@ static struct wmi_cmd_map wmi_10x_cmd_map = { .p2p_dev_set_discoverability = WMI_10X_P2P_DEV_SET_DISCOVERABILITY, .p2p_go_set_beacon_ie = WMI_10X_P2P_GO_SET_BEACON_IE, .p2p_go_set_probe_resp_ie = WMI_10X_P2P_GO_SET_PROBE_RESP_IE, - .p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNDEFINED, - .ap_ps_peer_param_cmdid = WMI_CMD_UNDEFINED, - .ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNDEFINED, + .p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNSUPPORTED, + .ap_ps_peer_param_cmdid = WMI_CMD_UNSUPPORTED, + .ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNSUPPORTED, .peer_rate_retry_sched_cmdid = WMI_10X_PEER_RATE_RETRY_SCHED_CMDID, .wlan_profile_trigger_cmdid = WMI_10X_WLAN_PROFILE_TRIGGER_CMDID, .wlan_profile_set_hist_intvl_cmdid = @@ -242,25 +242,25 @@ static struct wmi_cmd_map wmi_10x_cmd_map = { .vdev_spectral_scan_enable_cmdid = WMI_10X_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, .request_stats_cmdid = WMI_10X_REQUEST_STATS_CMDID, - .set_arp_ns_offload_cmdid = WMI_CMD_UNDEFINED, - .network_list_offload_config_cmdid = WMI_CMD_UNDEFINED, - .gtk_offload_cmdid = WMI_CMD_UNDEFINED, - .csa_offload_enable_cmdid = WMI_CMD_UNDEFINED, - .csa_offload_chanswitch_cmdid = WMI_CMD_UNDEFINED, - .chatter_set_mode_cmdid = WMI_CMD_UNDEFINED, - .peer_tid_addba_cmdid = WMI_CMD_UNDEFINED, - .peer_tid_delba_cmdid = WMI_CMD_UNDEFINED, - .sta_dtim_ps_method_cmdid = WMI_CMD_UNDEFINED, - .sta_uapsd_auto_trig_cmdid = WMI_CMD_UNDEFINED, - .sta_keepalive_cmd = WMI_CMD_UNDEFINED, + .set_arp_ns_offload_cmdid = WMI_CMD_UNSUPPORTED, + .network_list_offload_config_cmdid = WMI_CMD_UNSUPPORTED, + .gtk_offload_cmdid = WMI_CMD_UNSUPPORTED, + .csa_offload_enable_cmdid = WMI_CMD_UNSUPPORTED, + .csa_offload_chanswitch_cmdid = WMI_CMD_UNSUPPORTED, + .chatter_set_mode_cmdid = WMI_CMD_UNSUPPORTED, + .peer_tid_addba_cmdid = WMI_CMD_UNSUPPORTED, + .peer_tid_delba_cmdid = WMI_CMD_UNSUPPORTED, + .sta_dtim_ps_method_cmdid = WMI_CMD_UNSUPPORTED, + .sta_uapsd_auto_trig_cmdid = WMI_CMD_UNSUPPORTED, + .sta_keepalive_cmd = WMI_CMD_UNSUPPORTED, .echo_cmdid = WMI_10X_ECHO_CMDID, .pdev_utf_cmdid = WMI_10X_PDEV_UTF_CMDID, .dbglog_cfg_cmdid = WMI_10X_DBGLOG_CFG_CMDID, .pdev_qvit_cmdid = WMI_10X_PDEV_QVIT_CMDID, - .pdev_ftm_intg_cmdid = WMI_CMD_UNDEFINED, - .vdev_set_keepalive_cmdid = WMI_CMD_UNDEFINED, - .vdev_get_keepalive_cmdid = WMI_CMD_UNDEFINED, - .force_fw_hang_cmdid = WMI_CMD_UNDEFINED, + .pdev_ftm_intg_cmdid = WMI_CMD_UNSUPPORTED, + .vdev_set_keepalive_cmdid = WMI_CMD_UNSUPPORTED, + .vdev_get_keepalive_cmdid = WMI_CMD_UNSUPPORTED, + .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED, .gpio_config_cmdid = WMI_10X_GPIO_CONFIG_CMDID, .gpio_output_cmdid = WMI_10X_GPIO_OUTPUT_CMDID, }; @@ -611,9 +611,9 @@ static void ath10k_wmi_op_ep_tx_credits(struct ath10k *ar) static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id) { - int ret = -EINVAL; + int ret = -EOPNOTSUPP; - if (cmd_id == WMI_CMD_UNDEFINED) { + if (cmd_id == WMI_CMD_UNSUPPORTED) { ath10k_warn("wmi command %d is not supported by firmware\n", cmd_id); return ret; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 7692c14c2d1..78c991aec7f 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -359,7 +359,7 @@ enum wmi_cmd_group { #define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1) #define WMI_EVT_GRP_START_ID(grp_id) (((grp_id) << 12) | 0x1) -#define WMI_CMD_UNDEFINED 0 +#define WMI_CMD_UNSUPPORTED 0 /* Command IDs and command events for MAIN FW. */ enum wmi_cmd_id { -- cgit v1.2.3 From d544943afa678ffe06de295b5420f060389edfc9 Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Tue, 15 Oct 2013 09:55:32 +0200 Subject: ath10k: return better errno for unsupported pdev params Return -EOPNOTSUPP if given parameter is not supported by firmware. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 9d4752655e5..d1e513ef71a 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2119,7 +2119,7 @@ int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value) if (id == WMI_PDEV_PARAM_UNSUPPORTED) { ath10k_warn("pdev param %d not supported by firmware\n", id); - return -EINVAL; + return -EOPNOTSUPP; } skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); -- cgit v1.2.3 From cc4827b97b0a5fec66689c0e511a94e3dad490fc Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 16 Oct 2013 15:44:45 +0300 Subject: ath10k: use workqueue to set WEP TX key Recent WMI/HTC changes made it possible for WMI commands to sleep (if there's not enough HTC TX credits to submit a command). TX path is in an atomic context so calling WMI commands in it is wrong. This simply moves WEP key index update to a worker and fixes the 'scheduling while atomic' bug. This still leaves multiple WEP key handling laggy, i.e. some frames may be TXed with an old/different key (although recipient should still be able to RX them). kvalo: changed the title Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 4 ++- drivers/net/wireless/ath/ath10k/mac.c | 53 +++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 18 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index ce36daa5ff0..cef5455853c 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -215,8 +215,10 @@ struct ath10k_vif { struct ath10k *ar; struct ieee80211_vif *vif; + struct work_struct wep_key_work; struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1]; - u8 def_wep_key_index; + u8 def_wep_key_idx; + u8 def_wep_key_newidx; u16 tx_seq_no; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 97d7111be72..bc0e17bb07c 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1239,7 +1239,7 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw, /* FIXME: why don't we print error if wmi call fails? */ ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); - arvif->def_wep_key_index = 0; + arvif->def_wep_key_idx = 0; } static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif, @@ -1467,6 +1467,30 @@ static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw, skb_pull(skb, IEEE80211_QOS_CTL_LEN); } +static void ath10k_tx_wep_key_work(struct work_struct *work) +{ + struct ath10k_vif *arvif = container_of(work, struct ath10k_vif, + wep_key_work); + int ret, keyidx = arvif->def_wep_key_newidx; + + if (arvif->def_wep_key_idx == keyidx) + return; + + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n", + arvif->vdev_id, keyidx); + + ret = ath10k_wmi_vdev_set_param(arvif->ar, + arvif->vdev_id, + arvif->ar->wmi.vdev_param->def_keyid, + keyidx); + if (ret) { + ath10k_warn("could not update wep keyidx (%d)\n", ret); + return; + } + + arvif->def_wep_key_idx = keyidx; +} + static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -1475,8 +1499,6 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) struct ath10k *ar = arvif->ar; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_key_conf *key = info->control.hw_key; - u32 vdev_param; - int ret; if (!ieee80211_has_protected(hdr->frame_control)) return; @@ -1488,21 +1510,14 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) key->cipher != WLAN_CIPHER_SUITE_WEP104) return; - if (key->keyidx == arvif->def_wep_key_index) - return; - - ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d keyidx %d\n", - arvif->vdev_id, key->keyidx); - - vdev_param = ar->wmi.vdev_param->def_keyid; - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, - key->keyidx); - if (ret) { - ath10k_warn("could not update wep keyidx (%d)\n", ret); + if (key->keyidx == arvif->def_wep_key_idx) return; - } - arvif->def_wep_key_index = key->keyidx; + /* FIXME: Most likely a few frames will be TXed with an old key. Simply + * queueing frames until key index is updated is not an option because + * sk_buff may need more processing to be done, e.g. offchannel */ + arvif->def_wep_key_newidx = key->keyidx; + ieee80211_queue_work(ar->hw, &arvif->wep_key_work); } static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb) @@ -2023,6 +2038,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, arvif->ar = ar; arvif->vif = vif; + INIT_WORK(&arvif->wep_key_work, ath10k_tx_wep_key_work); + if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) { ath10k_warn("Only one monitor interface allowed\n"); ret = -EBUSY; @@ -2078,7 +2095,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, vdev_param = ar->wmi.vdev_param->def_keyid; ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param, - arvif->def_wep_key_index); + arvif->def_wep_key_idx); if (ret) ath10k_warn("Failed to set default keyid: %d\n", ret); @@ -2147,6 +2164,8 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); + cancel_work_sync(&arvif->wep_key_work); + spin_lock_bh(&ar->data_lock); if (arvif->beacon) { dev_kfree_skb_any(arvif->beacon); -- cgit v1.2.3 From 9dad14ae289ec676b9f8a2ee00a3fa18c51c683a Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 16 Oct 2013 15:44:45 +0300 Subject: ath10k: fix add_interface failure handling If something failed along add_interface() setup it was possible to leak a vdev id, vdev and peer. This could end up with leaked FW state or FW crash (assuming add_interface() failure wasn't a result of a crash). kvalo: rebased, whitespace fixes Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 53 ++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 13 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index bc0e17bb07c..16e55bd22fc 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2043,18 +2043,17 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) { ath10k_warn("Only one monitor interface allowed\n"); ret = -EBUSY; - goto exit; + goto err; } bit = ffs(ar->free_vdev_map); if (bit == 0) { ret = -EBUSY; - goto exit; + goto err; } arvif->vdev_id = bit - 1; arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE; - ar->free_vdev_map &= ~(1 << arvif->vdev_id); if (ar->p2p) arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE; @@ -2090,27 +2089,33 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, arvif->vdev_subtype, vif->addr); if (ret) { ath10k_warn("WMI vdev create failed: ret %d\n", ret); - goto exit; + goto err; } + ar->free_vdev_map &= ~BIT(arvif->vdev_id); + vdev_param = ar->wmi.vdev_param->def_keyid; ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param, arvif->def_wep_key_idx); - if (ret) + if (ret) { ath10k_warn("Failed to set default keyid: %d\n", ret); + goto err_vdev_delete; + } vdev_param = ar->wmi.vdev_param->tx_encap_type; ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, ATH10K_HW_TXRX_NATIVE_WIFI); /* 10.X firmware does not support this VDEV parameter. Do not warn */ - if (ret && ret != -EOPNOTSUPP) + if (ret && ret != -EOPNOTSUPP) { ath10k_warn("Failed to set TX encap: %d\n", ret); + goto err_vdev_delete; + } if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr); if (ret) { ath10k_warn("Failed to create peer for AP: %d\n", ret); - goto exit; + goto err_vdev_delete; } } @@ -2119,39 +2124,61 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, value = WMI_STA_PS_RX_WAKE_POLICY_WAKE; ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, value); - if (ret) + if (ret) { ath10k_warn("Failed to set RX wake policy: %d\n", ret); + goto err_peer_delete; + } param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD; value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS; ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, value); - if (ret) + if (ret) { ath10k_warn("Failed to set TX wake thresh: %d\n", ret); + goto err_peer_delete; + } param = WMI_STA_PS_PARAM_PSPOLL_COUNT; value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX; ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, value); - if (ret) + if (ret) { ath10k_warn("Failed to set PSPOLL count: %d\n", ret); + goto err_peer_delete; + } } ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold); - if (ret) + if (ret) { ath10k_warn("failed to set rts threshold for vdev %d (%d)\n", arvif->vdev_id, ret); + goto err_peer_delete; + } ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold); - if (ret) + if (ret) { ath10k_warn("failed to set frag threshold for vdev %d (%d)\n", arvif->vdev_id, ret); + goto err_peer_delete; + } if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) ar->monitor_present = true; -exit: mutex_unlock(&ar->conf_mutex); + return 0; + +err_peer_delete: + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) + ath10k_wmi_peer_delete(ar, arvif->vdev_id, vif->addr); + +err_vdev_delete: + ath10k_wmi_vdev_delete(ar, arvif->vdev_id); + ar->free_vdev_map &= ~BIT(arvif->vdev_id); + +err: + mutex_unlock(&ar->conf_mutex); + return ret; } -- cgit v1.2.3 From 0579119f7deab9bb112a432a0e8acbc6fc91ea14 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 16 Oct 2013 15:44:45 +0300 Subject: ath10k: track vif list internally mac80211 interface interations functions have peculiar locking issues. This patch introduces internal (to ath10k) vif list that will be used for vif iteration purposes. kvalo: remove extra INIT_LIST_HEAD() Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 1 + drivers/net/wireless/ath/ath10k/core.h | 3 +++ drivers/net/wireless/ath/ath10k/mac.c | 3 +++ 3 files changed, 7 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index c5561a935bb..1129994fb10 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -824,6 +824,7 @@ int ath10k_core_start(struct ath10k *ar) goto err_disconnect_htc; ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1; + INIT_LIST_HEAD(&ar->arvifs); return 0; diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index cef5455853c..0934f7633de 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -205,6 +205,8 @@ struct ath10k_peer { #define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ) struct ath10k_vif { + struct list_head list; + u32 vdev_id; enum wmi_vdev_type vdev_type; enum wmi_vdev_subtype vdev_subtype; @@ -404,6 +406,7 @@ struct ath10k { /* protects shared structure data */ spinlock_t data_lock; + struct list_head arvifs; struct list_head peers; wait_queue_head_t peer_mapping_wq; diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 16e55bd22fc..c8e4180e8c3 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -2093,6 +2093,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, } ar->free_vdev_map &= ~BIT(arvif->vdev_id); + list_add(&arvif->list, &ar->arvifs); vdev_param = ar->wmi.vdev_param->def_keyid; ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param, @@ -2175,6 +2176,7 @@ err_peer_delete: err_vdev_delete: ath10k_wmi_vdev_delete(ar, arvif->vdev_id); ar->free_vdev_map &= ~BIT(arvif->vdev_id); + list_del(&arvif->list); err: mutex_unlock(&ar->conf_mutex); @@ -2201,6 +2203,7 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, spin_unlock_bh(&ar->data_lock); ar->free_vdev_map |= 1 << (arvif->vdev_id); + list_del(&arvif->list); if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr); -- cgit v1.2.3 From ad088bfa1e3d05670734839e1a22a69ce4b193fb Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 16 Oct 2013 15:44:46 +0300 Subject: ath10k: fix scheduling while atomic config bug Recent HTC/WMI changes introduced the bug. ath10k was using _atomic iteration function with sleepable functions. mac80211 provides another iteration function but it cannot be safely called in hw_config() callback due to local->iflist_mtx being possibly acquired already. The patch uses internal vif list for iteration purposes and removes/refactors no longer necessary _iter functions. Reported-By: Kalle Valo Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 146 +++++++++++++++------------------- 1 file changed, 62 insertions(+), 84 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index c8e4180e8c3..bd42a144c5b 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -723,35 +723,30 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif, /* * Review this when mac80211 gains per-interface powersave support. */ -static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif) { - struct ath10k_generic_iter *ar_iter = data; - struct ieee80211_conf *conf = &ar_iter->ar->hw->conf; - struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ath10k *ar = arvif->ar; + struct ieee80211_conf *conf = &ar->hw->conf; enum wmi_sta_powersave_param param; enum wmi_sta_ps_mode psmode; int ret; lockdep_assert_held(&arvif->ar->conf_mutex); - if (vif->type != NL80211_IFTYPE_STATION) - return; + if (arvif->vif->type != NL80211_IFTYPE_STATION) + return 0; if (conf->flags & IEEE80211_CONF_PS) { psmode = WMI_STA_PS_MODE_ENABLED; param = WMI_STA_PS_PARAM_INACTIVITY_TIME; - ret = ath10k_wmi_set_sta_ps_param(ar_iter->ar, - arvif->vdev_id, - param, + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, conf->dynamic_ps_timeout); if (ret) { ath10k_warn("Failed to set inactivity time for VDEV: %d\n", arvif->vdev_id); - return; + return ret; } - - ar_iter->ret = ret; } else { psmode = WMI_STA_PS_MODE_DISABLED; } @@ -759,11 +754,14 @@ static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif) ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d psmode %s\n", arvif->vdev_id, psmode ? "enable" : "disable"); - ar_iter->ret = ath10k_wmi_set_psmode(ar_iter->ar, arvif->vdev_id, - psmode); - if (ar_iter->ret) + ret = ath10k_wmi_set_psmode(ar, arvif->vdev_id, psmode); + if (ret) { ath10k_warn("Failed to set PS Mode: %d for VDEV: %d\n", psmode, arvif->vdev_id); + return ret; + } + + return 0; } /**********************/ @@ -1959,9 +1957,10 @@ static void ath10k_stop(struct ieee80211_hw *hw) cancel_work_sync(&ar->restart_work); } -static void ath10k_config_ps(struct ath10k *ar) +static int ath10k_config_ps(struct ath10k *ar) { - struct ath10k_generic_iter ar_iter; + struct ath10k_vif *arvif; + int ret = 0; lockdep_assert_held(&ar->conf_mutex); @@ -1970,17 +1969,17 @@ static void ath10k_config_ps(struct ath10k *ar) * vdevs at this point we must not iterate over this interface list. * This setting will be updated upon add_interface(). */ if (ar->state == ATH10K_STATE_RESTARTED) - return; - - memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); - ar_iter.ar = ar; + return 0; - ieee80211_iterate_active_interfaces_atomic( - ar->hw, IEEE80211_IFACE_ITER_NORMAL, - ath10k_ps_iter, &ar_iter); + list_for_each_entry(arvif, &ar->arvifs, list) { + ret = ath10k_mac_vif_setup_ps(arvif); + if (ret) { + ath10k_warn("could not setup powersave (%d)\n", ret); + break; + } + } - if (ar_iter.ret) - ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret); + return ret; } static int ath10k_config(struct ieee80211_hw *hw, u32 changed) @@ -2882,86 +2881,65 @@ static int ath10k_cancel_remain_on_channel(struct ieee80211_hw *hw) * Both RTS and Fragmentation threshold are interface-specific * in ath10k, but device-specific in mac80211. */ -static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif) -{ - struct ath10k_generic_iter *ar_iter = data; - struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); - u32 rts = ar_iter->ar->hw->wiphy->rts_threshold; - lockdep_assert_held(&arvif->ar->conf_mutex); +static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif; + int ret = 0; /* During HW reconfiguration mac80211 reports all interfaces that were * running until reconfiguration was started. Since FW doesn't have any * vdevs at this point we must not iterate over this interface list. * This setting will be updated upon add_interface(). */ - if (ar_iter->ar->state == ATH10K_STATE_RESTARTED) - return; - - ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d rts_threshold %d\n", - arvif->vdev_id, rts); - - ar_iter->ret = ath10k_mac_set_rts(arvif, rts); - if (ar_iter->ret) - ath10k_warn("Failed to set RTS threshold for VDEV: %d\n", - arvif->vdev_id); -} - -static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) -{ - struct ath10k_generic_iter ar_iter; - struct ath10k *ar = hw->priv; - - memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); - ar_iter.ar = ar; + if (ar->state == ATH10K_STATE_RESTARTED) + return 0; mutex_lock(&ar->conf_mutex); - ieee80211_iterate_active_interfaces_atomic( - hw, IEEE80211_IFACE_ITER_NORMAL, - ath10k_set_rts_iter, &ar_iter); + list_for_each_entry(arvif, &ar->arvifs, list) { + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d rts threshold %d\n", + arvif->vdev_id, value); + + ret = ath10k_mac_set_rts(arvif, value); + if (ret) { + ath10k_warn("could not set rts threshold for vdev %d (%d)\n", + arvif->vdev_id, ret); + break; + } + } mutex_unlock(&ar->conf_mutex); - return ar_iter.ret; + return ret; } -static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) { - struct ath10k_generic_iter *ar_iter = data; - struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); - u32 frag = ar_iter->ar->hw->wiphy->frag_threshold; - - lockdep_assert_held(&arvif->ar->conf_mutex); + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif; + int ret = 0; /* During HW reconfiguration mac80211 reports all interfaces that were * running until reconfiguration was started. Since FW doesn't have any * vdevs at this point we must not iterate over this interface list. * This setting will be updated upon add_interface(). */ - if (ar_iter->ar->state == ATH10K_STATE_RESTARTED) - return; - - ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d fragmentation_threshold %d\n", - arvif->vdev_id, frag); - - ar_iter->ret = ath10k_mac_set_frag(arvif, frag); - if (ar_iter->ret) - ath10k_warn("Failed to set frag threshold for VDEV: %d\n", - arvif->vdev_id); -} - -static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) -{ - struct ath10k_generic_iter ar_iter; - struct ath10k *ar = hw->priv; - - memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); - ar_iter.ar = ar; + if (ar->state == ATH10K_STATE_RESTARTED) + return 0; mutex_lock(&ar->conf_mutex); - ieee80211_iterate_active_interfaces_atomic( - hw, IEEE80211_IFACE_ITER_NORMAL, - ath10k_set_frag_iter, &ar_iter); + list_for_each_entry(arvif, &ar->arvifs, list) { + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d fragmentation threshold %d\n", + arvif->vdev_id, value); + + ret = ath10k_mac_set_rts(arvif, value); + if (ret) { + ath10k_warn("could not set fragmentation threshold for vdev %d (%d)\n", + arvif->vdev_id, ret); + break; + } + } mutex_unlock(&ar->conf_mutex); - return ar_iter.ret; + return ret; } static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) -- cgit v1.2.3 From 588490cf65063ce5f307116163af0bcfb69d7f56 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 16 Oct 2013 15:44:46 +0300 Subject: ath10k: remove unnecessary checks mac80211 interface iteration functions that were used originally iterated over interfaces that weren't re-added to the driver during recovery. Since internal vif list is now used it's safe to remove the safe-guard as internal vif list is based on add/remove_interface function which guarantees that vdev is created in FW before it is iterated over. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 21 --------------------- 1 file changed, 21 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index bd42a144c5b..4273eefffc3 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1964,13 +1964,6 @@ static int ath10k_config_ps(struct ath10k *ar) lockdep_assert_held(&ar->conf_mutex); - /* During HW reconfiguration mac80211 reports all interfaces that were - * running until reconfiguration was started. Since FW doesn't have any - * vdevs at this point we must not iterate over this interface list. - * This setting will be updated upon add_interface(). */ - if (ar->state == ATH10K_STATE_RESTARTED) - return 0; - list_for_each_entry(arvif, &ar->arvifs, list) { ret = ath10k_mac_vif_setup_ps(arvif); if (ret) { @@ -2888,13 +2881,6 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) struct ath10k_vif *arvif; int ret = 0; - /* During HW reconfiguration mac80211 reports all interfaces that were - * running until reconfiguration was started. Since FW doesn't have any - * vdevs at this point we must not iterate over this interface list. - * This setting will be updated upon add_interface(). */ - if (ar->state == ATH10K_STATE_RESTARTED) - return 0; - mutex_lock(&ar->conf_mutex); list_for_each_entry(arvif, &ar->arvifs, list) { ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d rts threshold %d\n", @@ -2918,13 +2904,6 @@ static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) struct ath10k_vif *arvif; int ret = 0; - /* During HW reconfiguration mac80211 reports all interfaces that were - * running until reconfiguration was started. Since FW doesn't have any - * vdevs at this point we must not iterate over this interface list. - * This setting will be updated upon add_interface(). */ - if (ar->state == ATH10K_STATE_RESTARTED) - return 0; - mutex_lock(&ar->conf_mutex); list_for_each_entry(arvif, &ar->arvifs, list) { ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d fragmentation threshold %d\n", -- cgit v1.2.3 From b9ada65d97be58d82941f23dce5adde0d0eec61a Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 16 Oct 2013 15:44:46 +0300 Subject: ath10k: fix ath10k_bss_assoc() to not sleep in atomic context ath10k_bss_assoc() was calling ath10k_peer_assoc(), which can sleep, under atomic rcu_read_lock() and causing scheduing while atomic errors. Workaround that by delaying the call to ath10k_wmi_peer_assoc(). Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/mac.c | 56 +++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 19 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 4273eefffc3..0b1cc516e77 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -1137,26 +1137,25 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar, WARN_ON(phymode == MODE_UNKNOWN); } -static int ath10k_peer_assoc(struct ath10k *ar, - struct ath10k_vif *arvif, - struct ieee80211_sta *sta, - struct ieee80211_bss_conf *bss_conf) +static int ath10k_peer_assoc_prepare(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta, + struct ieee80211_bss_conf *bss_conf, + struct wmi_peer_assoc_complete_arg *arg) { - struct wmi_peer_assoc_complete_arg arg; - lockdep_assert_held(&ar->conf_mutex); - memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg)); + memset(arg, 0, sizeof(*arg)); - ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg); - ath10k_peer_assoc_h_crypto(ar, arvif, &arg); - ath10k_peer_assoc_h_rates(ar, sta, &arg); - ath10k_peer_assoc_h_ht(ar, sta, &arg); - ath10k_peer_assoc_h_vht(ar, sta, &arg); - ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, &arg); - ath10k_peer_assoc_h_phymode(ar, arvif, sta, &arg); + ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, arg); + ath10k_peer_assoc_h_crypto(ar, arvif, arg); + ath10k_peer_assoc_h_rates(ar, sta, arg); + ath10k_peer_assoc_h_ht(ar, sta, arg); + ath10k_peer_assoc_h_vht(ar, sta, arg); + ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, arg); + ath10k_peer_assoc_h_phymode(ar, arvif, sta, arg); - return ath10k_wmi_peer_assoc(ar, &arg); + return 0; } /* can be called only in mac80211 callbacks due to `key_count` usage */ @@ -1166,6 +1165,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, { struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct wmi_peer_assoc_complete_arg peer_arg; struct ieee80211_sta *ap_sta; int ret; @@ -1181,15 +1181,24 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, return; } - ret = ath10k_peer_assoc(ar, arvif, ap_sta, bss_conf); + ret = ath10k_peer_assoc_prepare(ar, arvif, ap_sta, + bss_conf, &peer_arg); if (ret) { - ath10k_warn("Peer assoc failed for %pM\n", bss_conf->bssid); + ath10k_warn("Peer assoc prepare failed for %pM\n: %d", + bss_conf->bssid, ret); rcu_read_unlock(); return; } rcu_read_unlock(); + ret = ath10k_wmi_peer_assoc(ar, &peer_arg); + if (ret) { + ath10k_warn("Peer assoc failed for %pM\n: %d", + bss_conf->bssid, ret); + return; + } + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d up (associated) bssid %pM aid %d\n", arvif->vdev_id, bss_conf->bssid, bss_conf->aid); @@ -1243,13 +1252,22 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw, static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif, struct ieee80211_sta *sta) { + struct wmi_peer_assoc_complete_arg peer_arg; int ret = 0; lockdep_assert_held(&ar->conf_mutex); - ret = ath10k_peer_assoc(ar, arvif, sta, NULL); + ret = ath10k_peer_assoc_prepare(ar, arvif, sta, NULL, &peer_arg); + if (ret) { + ath10k_warn("WMI peer assoc prepare failed for %pM\n", + sta->addr); + return ret; + } + + ret = ath10k_wmi_peer_assoc(ar, &peer_arg); if (ret) { - ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr); + ath10k_warn("Peer assoc failed for STA %pM\n: %d", + sta->addr, ret); return ret; } -- cgit v1.2.3 From 56b84287d14aa74823a9c290d0c5839d38365110 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Wed, 16 Oct 2013 15:44:47 +0300 Subject: ath10k: add might_sleep() to ath10k_wmi_cmd_send() ath10k_wmi_cmd_send() will now sleep if there are no credits available. To make it easier to catch callers in atomic context add might_sleep() to the function. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index d1e513ef71a..77238afbed7 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -613,6 +613,8 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, { int ret = -EOPNOTSUPP; + might_sleep(); + if (cmd_id == WMI_CMD_UNSUPPORTED) { ath10k_warn("wmi command %d is not supported by firmware\n", cmd_id); -- cgit v1.2.3 From 7ede612fd615abcda0cc30e5bef2a70f4cf4f75c Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Mon, 14 Oct 2013 21:18:48 +0200 Subject: ath5k: fix regression in tx status processing The regression was introduced in the following commit: 0967e01e8e713ed2982fb4eba8ba13794e9a6e89 "ath5k: make use of the new rate control API" ath5k_tx_frame_completed saves the intended per-rate retry counts before they are cleared by ieee80211_tx_info_clear_status, however at this point the information in info->status.rates is incomplete. This causes significant throughput degradation and excessive packet loss on links where high bit rates don't work properly. Move the copy from bf->rates a few lines up to ensure that the saved retry counts are updated, and that they are really cleared in info->status.rates after the call to ieee80211_tx_info_clear_status. Cc: stable@vger.kernel.org # 3.10+ Cc: Thomas Huehn Cc: Benjamin Vahl Reported-by: Ben West Signed-off-by: Felix Fietkau Acked-by: Thomas Huehn Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath5k/base.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 48161edec8d..69f58b073e8 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1663,15 +1663,15 @@ ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb, ah->stats.tx_bytes_count += skb->len; info = IEEE80211_SKB_CB(skb); + size = min_t(int, sizeof(info->status.rates), sizeof(bf->rates)); + memcpy(info->status.rates, bf->rates, size); + tries[0] = info->status.rates[0].count; tries[1] = info->status.rates[1].count; tries[2] = info->status.rates[2].count; ieee80211_tx_info_clear_status(info); - size = min_t(int, sizeof(info->status.rates), sizeof(bf->rates)); - memcpy(info->status.rates, bf->rates, size); - for (i = 0; i < ts->ts_final_idx; i++) { struct ieee80211_tx_rate *r = &info->status.rates[i]; -- cgit v1.2.3 From 5bc225acfe6a6226333061107708033a8d181d39 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Fri, 11 Oct 2013 14:09:54 +0200 Subject: ath9k: add noise floor parameter to ath9k_hw_getchan_noise Add nf parameter to ath9k_hw_getchan_noise() in order to compute NF for EXT chains with the same scale of noise floor calculated on CTL chains. ath9k_hw_getchan_noise() will be used in ath_process_fft() for spectral scan on HT40 channels Signed-off-by: Lorenzo Bianconi Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/calib.c | 10 +++++----- drivers/net/wireless/ath/ath9k/calib.h | 3 ++- drivers/net/wireless/ath/ath9k/hw.c | 2 +- drivers/net/wireless/ath/ath9k/link.c | 3 ++- 4 files changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index d8db74b0ef6..278365b8a89 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -63,13 +63,13 @@ static s16 ath9k_hw_get_default_nf(struct ath_hw *ah, return ath9k_hw_get_nf_limits(ah, chan)->nominal; } -s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan) +s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan, + s16 nf) { s8 noise = ATH_DEFAULT_NOISE_FLOOR; - if (chan && chan->noisefloor) { - s8 delta = chan->noisefloor - - ATH9K_NF_CAL_NOISE_THRESH - + if (nf) { + s8 delta = nf - ATH9K_NF_CAL_NOISE_THRESH - ath9k_hw_get_default_nf(ah, chan); if (delta > 0) noise += delta; @@ -392,7 +392,7 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) clear_bit(NFCAL_PENDING, &caldata->cal_flags); ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray); chan->noisefloor = h[0].privNF; - ah->noise = ath9k_hw_getchan_noise(ah, chan); + ah->noise = ath9k_hw_getchan_noise(ah, chan, chan->noisefloor); return true; } EXPORT_SYMBOL(ath9k_hw_getnf); diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h index 3d70b8c2bcd..b8ed95e9a33 100644 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -116,7 +116,8 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, void ath9k_hw_bstuck_nfcal(struct ath_hw *ah); void ath9k_hw_reset_calibration(struct ath_hw *ah, struct ath9k_cal_list *currCal); -s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan); +s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan, + s16 nf); #endif /* CALIB_H */ diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index dcdbab48709..54b04155e43 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1885,7 +1885,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, } else if (caldata) { clear_bit(PAPRD_PACKET_SENT, &caldata->cal_flags); } - ah->noise = ath9k_hw_getchan_noise(ah, chan); + ah->noise = ath9k_hw_getchan_noise(ah, chan, chan->noisefloor); if (fastcc) { r = ath9k_hw_do_fastcc(ah, chan); diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 84a60644f93..b7975195d74 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -518,7 +518,8 @@ void ath_update_survey_nf(struct ath_softc *sc, int channel) if (chan->noisefloor) { survey->filled |= SURVEY_INFO_NOISE_DBM; - survey->noise = ath9k_hw_getchan_noise(ah, chan); + survey->noise = ath9k_hw_getchan_noise(ah, chan, + chan->noisefloor); } } -- cgit v1.2.3 From e07f01e4c7d583adb1ec25e63a52db5fc10a94d3 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Fri, 11 Oct 2013 14:09:55 +0200 Subject: ath9k: add HT40 spectral scan capability Add spectral scan feature on HT40 channels for ath9k. This patch extends previous capability added by Simon Wunderlich Signed-off-by: Lorenzo Bianconi Reviewed-by: Simon Wunderlich Tested-by: Simon Wunderlich Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 29 +++++++ drivers/net/wireless/ath/ath9k/recv.c | 146 ++++++++++++++++++++++++--------- 2 files changed, 137 insertions(+), 38 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 5492a0ce072..1fce36dd908 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -886,6 +886,7 @@ static inline u8 spectral_bitmap_weight(u8 *bins) */ enum ath_fft_sample_type { ATH_FFT_SAMPLE_HT20 = 1, + ATH_FFT_SAMPLE_HT20_40, }; struct fft_sample_tlv { @@ -912,6 +913,34 @@ struct fft_sample_ht20 { u8 data[SPECTRAL_HT20_NUM_BINS]; } __packed; +struct fft_sample_ht20_40 { + struct fft_sample_tlv tlv; + + u8 channel_type; + __be16 freq; + + s8 lower_rssi; + s8 upper_rssi; + + __be64 tsf; + + s8 lower_noise; + s8 upper_noise; + + __be16 lower_max_magnitude; + __be16 upper_max_magnitude; + + u8 lower_max_index; + u8 upper_max_index; + + u8 lower_bitmap_weight; + u8 upper_bitmap_weight; + + u8 max_exp; + + u8 data[SPECTRAL_HT20_40_NUM_BINS]; +} __packed; + void ath9k_tasklet(unsigned long data); int ath_cabq_update(struct ath_softc *); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index a05164166de..e37559a85bf 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -972,14 +972,15 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, { #ifdef CONFIG_ATH9K_DEBUGFS struct ath_hw *ah = sc->sc_ah; - u8 bins[SPECTRAL_HT20_NUM_BINS]; - u8 *vdata = (u8 *)hdr; - struct fft_sample_ht20 fft_sample; + u8 num_bins, *bins, *vdata = (u8 *)hdr; + struct fft_sample_ht20 fft_sample_20; + struct fft_sample_ht20_40 fft_sample_40; + struct fft_sample_tlv *tlv; struct ath_radar_info *radar_info; - struct ath_ht20_mag_info *mag_info; int len = rs->rs_datalen; int dc_pos; - u16 length, max_magnitude; + u16 fft_len, length, freq = ah->curchan->chan->center_freq; + enum nl80211_channel_type chan_type; /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT @@ -997,45 +998,44 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK)) return 0; - /* Variation in the data length is possible and will be fixed later. - * Note that we only support HT20 for now. - * - * TODO: add HT20_40 support as well. - */ - if ((len > SPECTRAL_HT20_TOTAL_DATA_LEN + 2) || - (len < SPECTRAL_HT20_TOTAL_DATA_LEN - 1)) - return 1; - - fft_sample.tlv.type = ATH_FFT_SAMPLE_HT20; - length = sizeof(fft_sample) - sizeof(fft_sample.tlv); - fft_sample.tlv.length = __cpu_to_be16(length); + chan_type = cfg80211_get_chandef_type(&sc->hw->conf.chandef); + if ((chan_type == NL80211_CHAN_HT40MINUS) || + (chan_type == NL80211_CHAN_HT40PLUS)) { + fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN; + num_bins = SPECTRAL_HT20_40_NUM_BINS; + bins = (u8 *)fft_sample_40.data; + } else { + fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN; + num_bins = SPECTRAL_HT20_NUM_BINS; + bins = (u8 *)fft_sample_20.data; + } - fft_sample.freq = __cpu_to_be16(ah->curchan->chan->center_freq); - fft_sample.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); - fft_sample.noise = ah->noise; + /* Variation in the data length is possible and will be fixed later */ + if ((len > fft_len + 2) || (len < fft_len - 1)) + return 1; - switch (len - SPECTRAL_HT20_TOTAL_DATA_LEN) { + switch (len - fft_len) { case 0: /* length correct, nothing to do. */ - memcpy(bins, vdata, SPECTRAL_HT20_NUM_BINS); + memcpy(bins, vdata, num_bins); break; case -1: /* first byte missing, duplicate it. */ - memcpy(&bins[1], vdata, SPECTRAL_HT20_NUM_BINS - 1); + memcpy(&bins[1], vdata, num_bins - 1); bins[0] = vdata[0]; break; case 2: /* MAC added 2 extra bytes at bin 30 and 32, remove them. */ memcpy(bins, vdata, 30); bins[30] = vdata[31]; - memcpy(&bins[31], &vdata[33], SPECTRAL_HT20_NUM_BINS - 31); + memcpy(&bins[31], &vdata[33], num_bins - 31); break; case 1: /* MAC added 2 extra bytes AND first byte is missing. */ bins[0] = vdata[0]; - memcpy(&bins[0], vdata, 30); + memcpy(&bins[1], vdata, 30); bins[31] = vdata[31]; - memcpy(&bins[32], &vdata[33], SPECTRAL_HT20_NUM_BINS - 32); + memcpy(&bins[32], &vdata[33], num_bins - 32); break; default: return 1; @@ -1044,23 +1044,93 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, /* DC value (value in the middle) is the blind spot of the spectral * sample and invalid, interpolate it. */ - dc_pos = SPECTRAL_HT20_NUM_BINS / 2; + dc_pos = num_bins / 2; bins[dc_pos] = (bins[dc_pos + 1] + bins[dc_pos - 1]) / 2; - /* mag data is at the end of the frame, in front of radar_info */ - mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1; + if ((chan_type == NL80211_CHAN_HT40MINUS) || + (chan_type == NL80211_CHAN_HT40PLUS)) { + s8 lower_rssi, upper_rssi; + s16 ext_nf; + u8 lower_max_index, upper_max_index; + u8 lower_bitmap_w, upper_bitmap_w; + u16 lower_mag, upper_mag; + struct ath9k_hw_cal_data *caldata = ah->caldata; + struct ath_ht20_40_mag_info *mag_info; + + if (caldata) + ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan, + caldata->nfCalHist[3].privNF); + else + ext_nf = ATH_DEFAULT_NOISE_FLOOR; + + length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv); + fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40; + fft_sample_40.tlv.length = __cpu_to_be16(length); + fft_sample_40.freq = __cpu_to_be16(freq); + fft_sample_40.channel_type = chan_type; - /* copy raw bins without scaling them */ - memcpy(fft_sample.data, bins, SPECTRAL_HT20_NUM_BINS); - fft_sample.max_exp = mag_info->max_exp & 0xf; + if (chan_type == NL80211_CHAN_HT40PLUS) { + lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); + upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext0); + + fft_sample_40.lower_noise = ah->noise; + fft_sample_40.upper_noise = ext_nf; + } else { + lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext0); + upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); - max_magnitude = spectral_max_magnitude(mag_info->all_bins); - fft_sample.max_magnitude = __cpu_to_be16(max_magnitude); - fft_sample.max_index = spectral_max_index(mag_info->all_bins); - fft_sample.bitmap_weight = spectral_bitmap_weight(mag_info->all_bins); - fft_sample.tsf = __cpu_to_be64(tsf); + fft_sample_40.lower_noise = ext_nf; + fft_sample_40.upper_noise = ah->noise; + } + fft_sample_40.lower_rssi = lower_rssi; + fft_sample_40.upper_rssi = upper_rssi; + + mag_info = ((struct ath_ht20_40_mag_info *)radar_info) - 1; + lower_mag = spectral_max_magnitude(mag_info->lower_bins); + upper_mag = spectral_max_magnitude(mag_info->upper_bins); + fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag); + fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag); + lower_max_index = spectral_max_index(mag_info->lower_bins); + upper_max_index = spectral_max_index(mag_info->upper_bins); + fft_sample_40.lower_max_index = lower_max_index; + fft_sample_40.upper_max_index = upper_max_index; + lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins); + upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins); + fft_sample_40.lower_bitmap_weight = lower_bitmap_w; + fft_sample_40.upper_bitmap_weight = upper_bitmap_w; + fft_sample_40.max_exp = mag_info->max_exp & 0xf; + + fft_sample_40.tsf = __cpu_to_be64(tsf); + + tlv = (struct fft_sample_tlv *)&fft_sample_40; + } else { + u8 max_index, bitmap_w; + u16 magnitude; + struct ath_ht20_mag_info *mag_info; + + length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv); + fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20; + fft_sample_20.tlv.length = __cpu_to_be16(length); + fft_sample_20.freq = __cpu_to_be16(freq); + + fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); + fft_sample_20.noise = ah->noise; + + mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1; + magnitude = spectral_max_magnitude(mag_info->all_bins); + fft_sample_20.max_magnitude = __cpu_to_be16(magnitude); + max_index = spectral_max_index(mag_info->all_bins); + fft_sample_20.max_index = max_index; + bitmap_w = spectral_bitmap_weight(mag_info->all_bins); + fft_sample_20.bitmap_weight = bitmap_w; + fft_sample_20.max_exp = mag_info->max_exp & 0xf; + + fft_sample_20.tsf = __cpu_to_be64(tsf); + + tlv = (struct fft_sample_tlv *)&fft_sample_20; + } - ath_debug_send_fft_sample(sc, &fft_sample.tlv); + ath_debug_send_fft_sample(sc, tlv); return 1; #else return 0; -- cgit v1.2.3 From 95a5992e43046104ca92c6fb93a80d140d1aa7ce Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Mon, 14 Oct 2013 11:06:03 +0200 Subject: ath9k: dfs kill ath9k specyfic code Kill of using ath9k_hw_common() function in dfs detector code. Signed-off-by: Janusz Dziedzic Reviewed-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c | 13 ++++++------- drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h | 8 +++++--- drivers/net/wireless/ath/ath9k/init.c | 4 ++-- 3 files changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c index 491305c81fc..c839a78812d 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c @@ -19,7 +19,7 @@ #include "dfs_pattern_detector.h" #include "dfs_pri_detector.h" -#include "ath9k.h" +#include "../ath.h" /* * tolerated deviation of radar time stamp in usecs on both sides @@ -143,7 +143,6 @@ channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq) { u32 sz, i; struct channel_detector *cd; - struct ath_common *common = ath9k_hw_common(dpd->ah); cd = kmalloc(sizeof(*cd), GFP_ATOMIC); if (cd == NULL) @@ -167,7 +166,7 @@ channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq) return cd; fail: - ath_dbg(common, DFS, + ath_dbg(dpd->common, DFS, "failed to allocate channel_detector for freq=%d\n", freq); channel_detector_exit(dpd, cd); return NULL; @@ -242,7 +241,7 @@ 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) { - ath_dbg(ath9k_hw_common(dpd->ah), DFS, + ath_dbg(dpd->common, DFS, "DFS: radar found on freq=%d: id=%d, pri=%d, " "count=%d, count_false=%d\n", event->freq, pd->rs->type_id, @@ -288,10 +287,10 @@ static struct dfs_pattern_detector default_dpd = { }; struct dfs_pattern_detector * -dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region) +dfs_pattern_detector_init(struct ath_common *common, + enum nl80211_dfs_regions region) { struct dfs_pattern_detector *dpd; - struct ath_common *common = ath9k_hw_common(ah); dpd = kmalloc(sizeof(*dpd), GFP_KERNEL); if (dpd == NULL) @@ -300,7 +299,7 @@ dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region) *dpd = default_dpd; INIT_LIST_HEAD(&dpd->channel_detectors); - dpd->ah = ah; + dpd->common = common; if (dpd->set_dfs_domain(dpd, region)) return dpd; diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h index 90a5abcc426..b09946fed93 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h +++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h @@ -81,7 +81,7 @@ struct dfs_pattern_detector { u8 num_radar_types; u64 last_pulse_ts; /* needed for ath_dbg() */ - struct ath_hw *ah; + struct ath_common *common; const struct radar_detector_specs *radar_spec; struct list_head channel_detectors; @@ -94,10 +94,12 @@ struct dfs_pattern_detector { */ #if defined(CONFIG_ATH9K_DFS_CERTIFIED) extern struct dfs_pattern_detector * -dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region); +dfs_pattern_detector_init(struct ath_common *common, + enum nl80211_dfs_regions region); #else static inline struct dfs_pattern_detector * -dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region) +dfs_pattern_detector_init(struct ath_common *common, + enum nl80211_dfs_regions region) { return NULL; } diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 7df728f3633..ba02ef2ab8a 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -680,7 +680,8 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, sc->sc_ah = ah; pCap = &ah->caps; - sc->dfs_detector = dfs_pattern_detector_init(ah, NL80211_DFS_UNSET); + common = ath9k_hw_common(ah); + sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET); if (!pdata) { ah->ah_flags |= AH_USE_EEPROM; @@ -694,7 +695,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ah->external_reset = pdata->external_reset; } - common = ath9k_hw_common(ah); common->ops = &ah->reg_ops; common->bus_ops = bus_ops; common->ah = ah; -- cgit v1.2.3 From d265214b614aac62cdb8baed7ed3d77494cc9bdc Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Mon, 14 Oct 2013 11:06:04 +0200 Subject: ath9k: dfs move ath_dfs_pool_stats Move ath_dfs_pool_stats to dfs_pattern_detector code to be not specyfic only for ath9k. Signed-off-by: Janusz Dziedzic Reviewed-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/dfs_debug.c | 9 ++++++--- drivers/net/wireless/ath/ath9k/dfs_debug.h | 16 ---------------- drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c | 7 +++++++ drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h | 14 ++++++++++++++ drivers/net/wireless/ath/ath9k/dfs_pri_detector.c | 8 ++++++-- drivers/net/wireless/ath/ath9k/dfs_pri_detector.h | 2 ++ 6 files changed, 35 insertions(+), 21 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/dfs_debug.c b/drivers/net/wireless/ath/ath9k/dfs_debug.c index 821599135d8..990c3763072 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_debug.c +++ b/drivers/net/wireless/ath/ath9k/dfs_debug.c @@ -20,16 +20,16 @@ #include "ath9k.h" #include "dfs_debug.h" +#include "dfs_pattern_detector.h" - -struct ath_dfs_pool_stats global_dfs_pool_stats = { 0 }; +static struct ath_dfs_pool_stats dfs_pool_stats = { 0 }; #define ATH9K_DFS_STAT(s, p) \ len += scnprintf(buf + len, size - len, "%28s : %10u\n", s, \ sc->debug.stats.dfs_stats.p); #define ATH9K_DFS_POOL_STAT(s, p) \ len += scnprintf(buf + len, size - len, "%28s : %10u\n", s, \ - global_dfs_pool_stats.p); + dfs_pool_stats.p); static ssize_t read_file_dfs(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -44,6 +44,9 @@ static ssize_t read_file_dfs(struct file *file, char __user *user_buf, if (buf == NULL) return -ENOMEM; + if (sc->dfs_detector) + dfs_pool_stats = sc->dfs_detector->get_stats(sc->dfs_detector); + len += scnprintf(buf + len, size - len, "DFS support for " "macVersion = 0x%x, macRev = 0x%x: %s\n", hw_ver->macVersion, hw_ver->macRev, diff --git a/drivers/net/wireless/ath/ath9k/dfs_debug.h b/drivers/net/wireless/ath/ath9k/dfs_debug.h index e36810a4b58..0a7ddf4c88c 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_debug.h +++ b/drivers/net/wireless/ath/ath9k/dfs_debug.h @@ -51,25 +51,11 @@ struct ath_dfs_stats { u32 radar_detected; }; -/** - * struct ath_dfs_pool_stats - DFS Statistics for global pools - */ -struct ath_dfs_pool_stats { - u32 pool_reference; - u32 pulse_allocated; - u32 pulse_alloc_error; - u32 pulse_used; - u32 pseq_allocated; - u32 pseq_alloc_error; - u32 pseq_used; -}; #if defined(CONFIG_ATH9K_DFS_DEBUGFS) #define DFS_STAT_INC(sc, c) (sc->debug.stats.dfs_stats.c++) void ath9k_dfs_init_debug(struct ath_softc *sc); -#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++) -#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--) extern struct ath_dfs_pool_stats global_dfs_pool_stats; #else @@ -77,8 +63,6 @@ extern struct ath_dfs_pool_stats global_dfs_pool_stats; #define DFS_STAT_INC(sc, c) do { } while (0) static inline void ath9k_dfs_init_debug(struct ath_softc *sc) { } -#define DFS_POOL_STAT_INC(c) do { } while (0) -#define DFS_POOL_STAT_DEC(c) do { } while (0) #endif /* CONFIG_ATH9K_DFS_DEBUGFS */ #endif /* ATH9K_DFS_DEBUG_H */ diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c index c839a78812d..b0dcd1b72f3 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c @@ -253,6 +253,12 @@ dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event) return false; } +static struct ath_dfs_pool_stats +dpd_get_stats(struct dfs_pattern_detector *dpd) +{ + return global_dfs_pool_stats; +} + static bool dpd_set_domain(struct dfs_pattern_detector *dpd, enum nl80211_dfs_regions region) { @@ -283,6 +289,7 @@ static struct dfs_pattern_detector default_dpd = { .exit = dpd_exit, .set_dfs_domain = dpd_set_domain, .add_pulse = dpd_add_pulse, + .get_stats = dpd_get_stats, .region = NL80211_DFS_UNSET, }; diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h index b09946fed93..9c752977eef 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h +++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h @@ -21,6 +21,19 @@ #include #include +/** + * struct ath_dfs_pool_stats - DFS Statistics for global pools + */ +struct ath_dfs_pool_stats { + u32 pool_reference; + u32 pulse_allocated; + u32 pulse_alloc_error; + u32 pulse_used; + u32 pseq_allocated; + u32 pseq_alloc_error; + u32 pseq_used; +}; + /** * struct pulse_event - describing pulses reported by PHY * @ts: pulse time stamp in us @@ -77,6 +90,7 @@ struct dfs_pattern_detector { bool (*add_pulse)(struct dfs_pattern_detector *dpd, struct pulse_event *pe); + struct ath_dfs_pool_stats (*get_stats)(struct dfs_pattern_detector *dpd); enum nl80211_dfs_regions region; u8 num_radar_types; u64 last_pulse_ts; diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c index c718fc379a1..17b5bf9fd6a 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c +++ b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c @@ -17,10 +17,14 @@ #include #include -#include "ath9k.h" +#include "../ath.h" #include "dfs_pattern_detector.h" #include "dfs_pri_detector.h" -#include "dfs_debug.h" + +struct ath_dfs_pool_stats global_dfs_pool_stats = {}; + +#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++) +#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--) /** * struct pulse_elem - elements in pulse queue diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h index 723962d1abc..79f0fff4d1e 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h +++ b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h @@ -19,6 +19,8 @@ #include +extern struct ath_dfs_pool_stats global_dfs_pool_stats; + /** * struct pri_sequence - sequence of pulses matching one PRI * @head: list_head -- cgit v1.2.3 From 379ce86e1f458281ca014a0053cf9350c7dca4bf Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Mon, 14 Oct 2013 11:06:05 +0200 Subject: ath9k: dfs use CFG80211_CERTIFICATION_ONUS flag Use CFG80211_CERTIFICATION_ONUS flag in the DFS detector code. This is required as a preparation for moving DFS detector code from ath9k to ath module. Signed-off-by: Janusz Dziedzic Reviewed-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c | 3 +++ drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h | 10 ---------- 2 files changed, 3 insertions(+), 10 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c index b0dcd1b72f3..c230ffc2ddc 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c @@ -299,6 +299,9 @@ dfs_pattern_detector_init(struct ath_common *common, { struct dfs_pattern_detector *dpd; + if (!config_enabled(CONFIG_CFG80211_CERTIFICATION_ONUS)) + return NULL; + dpd = kmalloc(sizeof(*dpd), GFP_KERNEL); if (dpd == NULL) return NULL; diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h index 9c752977eef..dde2652b787 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h +++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h @@ -106,17 +106,7 @@ struct dfs_pattern_detector { * @param region: DFS domain to be used, can be NL80211_DFS_UNSET at creation * @return instance pointer on success, NULL otherwise */ -#if defined(CONFIG_ATH9K_DFS_CERTIFIED) extern struct dfs_pattern_detector * dfs_pattern_detector_init(struct ath_common *common, enum nl80211_dfs_regions region); -#else -static inline struct dfs_pattern_detector * -dfs_pattern_detector_init(struct ath_common *common, - enum nl80211_dfs_regions region) -{ - return NULL; -} -#endif /* CONFIG_ATH9K_DFS_CERTIFIED */ - #endif /* DFS_PATTERN_DETECTOR_H */ -- cgit v1.2.3 From ad40d3da1e8683234714c3024219dc722ed1e214 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Mon, 14 Oct 2013 11:06:06 +0200 Subject: ath9k/ath: move dfs pattern detector to ath Move the DFS pattern detector code to the ath module so the other Atheros drivers can make us of it. This makes no functional changes. Signed-off-by: Janusz Dziedzic Reviewed-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/Makefile | 4 +- drivers/net/wireless/ath/ath9k/Makefile | 4 +- drivers/net/wireless/ath/ath9k/dfs.h | 2 +- drivers/net/wireless/ath/ath9k/dfs_debug.c | 2 +- .../net/wireless/ath/ath9k/dfs_pattern_detector.c | 320 --------------- .../net/wireless/ath/ath9k/dfs_pattern_detector.h | 112 ------ drivers/net/wireless/ath/ath9k/dfs_pri_detector.c | 429 --------------------- drivers/net/wireless/ath/ath9k/dfs_pri_detector.h | 77 ---- drivers/net/wireless/ath/dfs_pattern_detector.c | 320 +++++++++++++++ drivers/net/wireless/ath/dfs_pattern_detector.h | 112 ++++++ drivers/net/wireless/ath/dfs_pri_detector.c | 429 +++++++++++++++++++++ drivers/net/wireless/ath/dfs_pri_detector.h | 77 ++++ 12 files changed, 944 insertions(+), 944 deletions(-) delete mode 100644 drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c delete mode 100644 drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h delete mode 100644 drivers/net/wireless/ath/ath9k/dfs_pri_detector.c delete mode 100644 drivers/net/wireless/ath/ath9k/dfs_pri_detector.h create mode 100644 drivers/net/wireless/ath/dfs_pattern_detector.c create mode 100644 drivers/net/wireless/ath/dfs_pattern_detector.h create mode 100644 drivers/net/wireless/ath/dfs_pri_detector.c create mode 100644 drivers/net/wireless/ath/dfs_pri_detector.h (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index 363b05653c7..7d023b0f13b 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -12,7 +12,9 @@ obj-$(CONFIG_ATH_COMMON) += ath.o ath-objs := main.o \ regd.o \ hw.o \ - key.o + key.o \ + dfs_pattern_detector.o \ + dfs_pri_detector.o ath-$(CONFIG_ATH_DEBUG) += debug.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 75ee9e7704c..6205ef5a932 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -14,9 +14,7 @@ ath9k-$(CONFIG_ATH9K_AHB) += ahb.o ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += \ - dfs.o \ - dfs_pattern_detector.o \ - dfs_pri_detector.o + dfs.o ath9k-$(CONFIG_PM_SLEEP) += wow.o obj-$(CONFIG_ATH9K) += ath9k.o diff --git a/drivers/net/wireless/ath/ath9k/dfs.h b/drivers/net/wireless/ath/ath9k/dfs.h index 3c839f06a06..c6fa3d5b5d7 100644 --- a/drivers/net/wireless/ath/ath9k/dfs.h +++ b/drivers/net/wireless/ath/ath9k/dfs.h @@ -17,7 +17,7 @@ #ifndef ATH9K_DFS_H #define ATH9K_DFS_H -#include "dfs_pattern_detector.h" +#include "../dfs_pattern_detector.h" #if defined(CONFIG_ATH9K_DFS_CERTIFIED) /** diff --git a/drivers/net/wireless/ath/ath9k/dfs_debug.c b/drivers/net/wireless/ath/ath9k/dfs_debug.c index 990c3763072..90b8342d1ed 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_debug.c +++ b/drivers/net/wireless/ath/ath9k/dfs_debug.c @@ -20,7 +20,7 @@ #include "ath9k.h" #include "dfs_debug.h" -#include "dfs_pattern_detector.h" +#include "../dfs_pattern_detector.h" static struct ath_dfs_pool_stats dfs_pool_stats = { 0 }; diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c deleted file mode 100644 index c230ffc2ddc..00000000000 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright (c) 2012 Neratec Solutions AG - * - * 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 "dfs_pattern_detector.h" -#include "dfs_pri_detector.h" -#include "../ath.h" - -/* - * tolerated deviation of radar time stamp in usecs on both sides - * TODO: this might need to be HW-dependent - */ -#define PRI_TOLERANCE 16 - -/** - * struct radar_types - contains array of patterns defined for one DFS domain - * @domain: DFS regulatory domain - * @num_radar_types: number of radar types to follow - * @radar_types: radar types array - */ -struct radar_types { - enum nl80211_dfs_regions region; - u32 num_radar_types; - const struct radar_detector_specs *radar_types; -}; - -/* percentage on ppb threshold to trigger detection */ -#define MIN_PPB_THRESH 50 -#define PPB_THRESH(PPB) ((PPB * MIN_PPB_THRESH + 50) / 100) -#define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF) -/* percentage of pulse width tolerance */ -#define WIDTH_TOLERANCE 5 -#define WIDTH_LOWER(X) ((X*(100-WIDTH_TOLERANCE)+50)/100) -#define WIDTH_UPPER(X) ((X*(100+WIDTH_TOLERANCE)+50)/100) - -#define ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB) \ -{ \ - ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX), \ - (PRF2PRI(PMAX) - PRI_TOLERANCE), \ - (PRF2PRI(PMIN) * PRF + PRI_TOLERANCE), PRF, PPB * PRF, \ - PPB_THRESH(PPB), PRI_TOLERANCE, \ -} - -/* radar types as defined by ETSI EN-301-893 v1.5.1 */ -static const struct radar_detector_specs etsi_radar_ref_types_v15[] = { - ETSI_PATTERN(0, 0, 1, 700, 700, 1, 18), - ETSI_PATTERN(1, 0, 5, 200, 1000, 1, 10), - ETSI_PATTERN(2, 0, 15, 200, 1600, 1, 15), - ETSI_PATTERN(3, 0, 15, 2300, 4000, 1, 25), - ETSI_PATTERN(4, 20, 30, 2000, 4000, 1, 20), - ETSI_PATTERN(5, 0, 2, 300, 400, 3, 10), - ETSI_PATTERN(6, 0, 2, 400, 1200, 3, 15), -}; - -static const struct radar_types etsi_radar_types_v15 = { - .region = NL80211_DFS_ETSI, - .num_radar_types = ARRAY_SIZE(etsi_radar_ref_types_v15), - .radar_types = etsi_radar_ref_types_v15, -}; - -/* for now, we support ETSI radar types, FCC and JP are TODO */ -static const struct radar_types *dfs_domains[] = { - &etsi_radar_types_v15, -}; - -/** - * get_dfs_domain_radar_types() - get radar types for a given DFS domain - * @param domain DFS domain - * @return radar_types ptr on success, NULL if DFS domain is not supported - */ -static const struct radar_types * -get_dfs_domain_radar_types(enum nl80211_dfs_regions region) -{ - u32 i; - for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) { - if (dfs_domains[i]->region == region) - return dfs_domains[i]; - } - return NULL; -} - -/** - * struct channel_detector - detector elements for a DFS channel - * @head: list_head - * @freq: frequency for this channel detector in MHz - * @detectors: array of dynamically created detector elements for this freq - * - * Channel detectors are required to provide multi-channel DFS detection, e.g. - * to support off-channel scanning. A pattern detector has a list of channels - * radar pulses have been reported for in the past. - */ -struct channel_detector { - struct list_head head; - u16 freq; - struct pri_detector **detectors; -}; - -/* channel_detector_reset() - reset detector lines for a given channel */ -static void channel_detector_reset(struct dfs_pattern_detector *dpd, - struct channel_detector *cd) -{ - u32 i; - if (cd == NULL) - return; - for (i = 0; i < dpd->num_radar_types; i++) - cd->detectors[i]->reset(cd->detectors[i], dpd->last_pulse_ts); -} - -/* channel_detector_exit() - destructor */ -static void channel_detector_exit(struct dfs_pattern_detector *dpd, - struct channel_detector *cd) -{ - u32 i; - if (cd == NULL) - return; - list_del(&cd->head); - for (i = 0; i < dpd->num_radar_types; i++) { - struct pri_detector *de = cd->detectors[i]; - if (de != NULL) - de->exit(de); - } - kfree(cd->detectors); - kfree(cd); -} - -static struct channel_detector * -channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq) -{ - u32 sz, i; - struct channel_detector *cd; - - cd = kmalloc(sizeof(*cd), GFP_ATOMIC); - if (cd == NULL) - goto fail; - - INIT_LIST_HEAD(&cd->head); - cd->freq = freq; - sz = sizeof(cd->detectors) * dpd->num_radar_types; - cd->detectors = kzalloc(sz, GFP_ATOMIC); - if (cd->detectors == NULL) - goto fail; - - for (i = 0; i < dpd->num_radar_types; i++) { - const struct radar_detector_specs *rs = &dpd->radar_spec[i]; - struct pri_detector *de = pri_detector_init(rs); - if (de == NULL) - goto fail; - cd->detectors[i] = de; - } - list_add(&cd->head, &dpd->channel_detectors); - return cd; - -fail: - ath_dbg(dpd->common, DFS, - "failed to allocate channel_detector for freq=%d\n", freq); - channel_detector_exit(dpd, cd); - return NULL; -} - -/** - * channel_detector_get() - get channel detector for given frequency - * @param dpd instance pointer - * @param freq frequency in MHz - * @return pointer to channel detector on success, NULL otherwise - * - * Return existing channel detector for the given frequency or return a - * newly create one. - */ -static struct channel_detector * -channel_detector_get(struct dfs_pattern_detector *dpd, u16 freq) -{ - struct channel_detector *cd; - list_for_each_entry(cd, &dpd->channel_detectors, head) { - if (cd->freq == freq) - return cd; - } - return channel_detector_create(dpd, freq); -} - -/* - * DFS Pattern Detector - */ - -/* dpd_reset(): reset all channel detectors */ -static void dpd_reset(struct dfs_pattern_detector *dpd) -{ - struct channel_detector *cd; - if (!list_empty(&dpd->channel_detectors)) - list_for_each_entry(cd, &dpd->channel_detectors, head) - channel_detector_reset(dpd, cd); - -} -static void dpd_exit(struct dfs_pattern_detector *dpd) -{ - struct channel_detector *cd, *cd0; - if (!list_empty(&dpd->channel_detectors)) - list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head) - channel_detector_exit(dpd, cd); - kfree(dpd); -} - -static bool -dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event) -{ - u32 i; - struct channel_detector *cd; - - /* - * pulses received for a non-supported or un-initialized - * domain are treated as detected radars for fail-safety - */ - if (dpd->region == NL80211_DFS_UNSET) - return true; - - cd = channel_detector_get(dpd, event->freq); - if (cd == NULL) - return false; - - dpd->last_pulse_ts = event->ts; - /* reset detector on time stamp wraparound, caused by TSF reset */ - if (event->ts < dpd->last_pulse_ts) - dpd_reset(dpd); - - /* do type individual pattern matching */ - for (i = 0; i < dpd->num_radar_types; i++) { - struct pri_detector *pd = cd->detectors[i]; - struct pri_sequence *ps = pd->add_pulse(pd, event); - if (ps != NULL) { - ath_dbg(dpd->common, DFS, - "DFS: radar found on freq=%d: id=%d, pri=%d, " - "count=%d, count_false=%d\n", - event->freq, pd->rs->type_id, - ps->pri, ps->count, ps->count_falses); - channel_detector_reset(dpd, cd); - return true; - } - } - return false; -} - -static struct ath_dfs_pool_stats -dpd_get_stats(struct dfs_pattern_detector *dpd) -{ - return global_dfs_pool_stats; -} - -static bool dpd_set_domain(struct dfs_pattern_detector *dpd, - enum nl80211_dfs_regions region) -{ - const struct radar_types *rt; - struct channel_detector *cd, *cd0; - - if (dpd->region == region) - return true; - - dpd->region = NL80211_DFS_UNSET; - - rt = get_dfs_domain_radar_types(region); - if (rt == NULL) - return false; - - /* delete all channel detectors for previous DFS domain */ - if (!list_empty(&dpd->channel_detectors)) - list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head) - channel_detector_exit(dpd, cd); - dpd->radar_spec = rt->radar_types; - dpd->num_radar_types = rt->num_radar_types; - - dpd->region = region; - return true; -} - -static struct dfs_pattern_detector default_dpd = { - .exit = dpd_exit, - .set_dfs_domain = dpd_set_domain, - .add_pulse = dpd_add_pulse, - .get_stats = dpd_get_stats, - .region = NL80211_DFS_UNSET, -}; - -struct dfs_pattern_detector * -dfs_pattern_detector_init(struct ath_common *common, - enum nl80211_dfs_regions region) -{ - struct dfs_pattern_detector *dpd; - - if (!config_enabled(CONFIG_CFG80211_CERTIFICATION_ONUS)) - return NULL; - - dpd = kmalloc(sizeof(*dpd), GFP_KERNEL); - if (dpd == NULL) - return NULL; - - *dpd = default_dpd; - INIT_LIST_HEAD(&dpd->channel_detectors); - - dpd->common = common; - if (dpd->set_dfs_domain(dpd, region)) - return dpd; - - ath_dbg(common, DFS,"Could not set DFS domain to %d", region); - kfree(dpd); - return NULL; -} -EXPORT_SYMBOL(dfs_pattern_detector_init); diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h deleted file mode 100644 index dde2652b787..00000000000 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2012 Neratec Solutions AG - * - * 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. - */ - -#ifndef DFS_PATTERN_DETECTOR_H -#define DFS_PATTERN_DETECTOR_H - -#include -#include -#include - -/** - * struct ath_dfs_pool_stats - DFS Statistics for global pools - */ -struct ath_dfs_pool_stats { - u32 pool_reference; - u32 pulse_allocated; - u32 pulse_alloc_error; - u32 pulse_used; - u32 pseq_allocated; - u32 pseq_alloc_error; - u32 pseq_used; -}; - -/** - * struct pulse_event - describing pulses reported by PHY - * @ts: pulse time stamp in us - * @freq: channel frequency in MHz - * @width: pulse duration in us - * @rssi: rssi of radar event - */ -struct pulse_event { - u64 ts; - u16 freq; - u8 width; - u8 rssi; -}; - -/** - * struct radar_detector_specs - detector specs for a radar pattern type - * @type_id: pattern type, as defined by regulatory - * @width_min: minimum radar pulse width in [us] - * @width_max: maximum radar pulse width in [us] - * @pri_min: minimum pulse repetition interval in [us] (including tolerance) - * @pri_max: minimum pri in [us] (including tolerance) - * @num_pri: maximum number of different pri for this type - * @ppb: pulses per bursts for this type - * @ppb_thresh: number of pulses required to trigger detection - * @max_pri_tolerance: pulse time stamp tolerance on both sides [us] - */ -struct radar_detector_specs { - u8 type_id; - u8 width_min; - u8 width_max; - u16 pri_min; - u16 pri_max; - u8 num_pri; - u8 ppb; - u8 ppb_thresh; - u8 max_pri_tolerance; -}; - -/** - * struct dfs_pattern_detector - DFS pattern detector - * @exit(): destructor - * @set_dfs_domain(): set DFS domain, resets detector lines upon domain changes - * @add_pulse(): add radar pulse to detector, returns true on detection - * @region: active DFS region, NL80211_DFS_UNSET until set - * @num_radar_types: number of different radar types - * @last_pulse_ts: time stamp of last valid pulse in usecs - * @radar_detector_specs: array of radar detection specs - * @channel_detectors: list connecting channel_detector elements - */ -struct dfs_pattern_detector { - void (*exit)(struct dfs_pattern_detector *dpd); - 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 ath_dfs_pool_stats (*get_stats)(struct dfs_pattern_detector *dpd); - enum nl80211_dfs_regions region; - u8 num_radar_types; - u64 last_pulse_ts; - /* needed for ath_dbg() */ - struct ath_common *common; - - const struct radar_detector_specs *radar_spec; - struct list_head channel_detectors; -}; - -/** - * dfs_pattern_detector_init() - constructor for pattern detector class - * @param region: DFS domain to be used, can be NL80211_DFS_UNSET at creation - * @return instance pointer on success, NULL otherwise - */ -extern struct dfs_pattern_detector * -dfs_pattern_detector_init(struct ath_common *common, - enum nl80211_dfs_regions region); -#endif /* DFS_PATTERN_DETECTOR_H */ diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c deleted file mode 100644 index 17b5bf9fd6a..00000000000 --- a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c +++ /dev/null @@ -1,429 +0,0 @@ -/* - * Copyright (c) 2012 Neratec Solutions AG - * - * 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 "../ath.h" -#include "dfs_pattern_detector.h" -#include "dfs_pri_detector.h" - -struct ath_dfs_pool_stats global_dfs_pool_stats = {}; - -#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++) -#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--) - -/** - * struct pulse_elem - elements in pulse queue - * @ts: time stamp in usecs - */ -struct pulse_elem { - struct list_head head; - u64 ts; -}; - -/** - * pde_get_multiple() - get number of multiples considering a given tolerance - * @return factor if abs(val - factor*fraction) <= tolerance, 0 otherwise - */ -static u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance) -{ - u32 remainder; - u32 factor; - u32 delta; - - if (fraction == 0) - return 0; - - delta = (val < fraction) ? (fraction - val) : (val - fraction); - - if (delta <= tolerance) - /* val and fraction are within tolerance */ - return 1; - - factor = val / fraction; - remainder = val % fraction; - if (remainder > tolerance) { - /* no exact match */ - if ((fraction - remainder) <= tolerance) - /* remainder is within tolerance */ - factor++; - else - factor = 0; - } - return factor; -} - -/** - * DOC: Singleton Pulse and Sequence Pools - * - * Instances of pri_sequence and pulse_elem are kept in singleton pools to - * reduce the number of dynamic allocations. They are shared between all - * instances and grow up to the peak number of simultaneously used objects. - * - * Memory is freed after all references to the pools are released. - */ -static u32 singleton_pool_references; -static LIST_HEAD(pulse_pool); -static LIST_HEAD(pseq_pool); -static DEFINE_SPINLOCK(pool_lock); - -static void pool_register_ref(void) -{ - spin_lock_bh(&pool_lock); - singleton_pool_references++; - DFS_POOL_STAT_INC(pool_reference); - spin_unlock_bh(&pool_lock); -} - -static void pool_deregister_ref(void) -{ - spin_lock_bh(&pool_lock); - singleton_pool_references--; - DFS_POOL_STAT_DEC(pool_reference); - if (singleton_pool_references == 0) { - /* free singleton pools with no references left */ - struct pri_sequence *ps, *ps0; - struct pulse_elem *p, *p0; - - list_for_each_entry_safe(p, p0, &pulse_pool, head) { - list_del(&p->head); - DFS_POOL_STAT_DEC(pulse_allocated); - kfree(p); - } - list_for_each_entry_safe(ps, ps0, &pseq_pool, head) { - list_del(&ps->head); - DFS_POOL_STAT_DEC(pseq_allocated); - kfree(ps); - } - } - spin_unlock_bh(&pool_lock); -} - -static void pool_put_pulse_elem(struct pulse_elem *pe) -{ - spin_lock_bh(&pool_lock); - list_add(&pe->head, &pulse_pool); - DFS_POOL_STAT_DEC(pulse_used); - spin_unlock_bh(&pool_lock); -} - -static void pool_put_pseq_elem(struct pri_sequence *pse) -{ - spin_lock_bh(&pool_lock); - list_add(&pse->head, &pseq_pool); - DFS_POOL_STAT_DEC(pseq_used); - spin_unlock_bh(&pool_lock); -} - -static struct pri_sequence *pool_get_pseq_elem(void) -{ - struct pri_sequence *pse = NULL; - spin_lock_bh(&pool_lock); - if (!list_empty(&pseq_pool)) { - pse = list_first_entry(&pseq_pool, struct pri_sequence, head); - list_del(&pse->head); - DFS_POOL_STAT_INC(pseq_used); - } - spin_unlock_bh(&pool_lock); - return pse; -} - -static struct pulse_elem *pool_get_pulse_elem(void) -{ - struct pulse_elem *pe = NULL; - spin_lock_bh(&pool_lock); - if (!list_empty(&pulse_pool)) { - pe = list_first_entry(&pulse_pool, struct pulse_elem, head); - list_del(&pe->head); - DFS_POOL_STAT_INC(pulse_used); - } - spin_unlock_bh(&pool_lock); - return pe; -} - -static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde) -{ - struct list_head *l = &pde->pulses; - if (list_empty(l)) - return NULL; - return list_entry(l->prev, struct pulse_elem, head); -} - -static bool pulse_queue_dequeue(struct pri_detector *pde) -{ - struct pulse_elem *p = pulse_queue_get_tail(pde); - if (p != NULL) { - list_del_init(&p->head); - pde->count--; - /* give it back to pool */ - pool_put_pulse_elem(p); - } - return (pde->count > 0); -} - -/* remove pulses older than window */ -static void pulse_queue_check_window(struct pri_detector *pde) -{ - u64 min_valid_ts; - struct pulse_elem *p; - - /* there is no delta time with less than 2 pulses */ - if (pde->count < 2) - return; - - if (pde->last_ts <= pde->window_size) - return; - - min_valid_ts = pde->last_ts - pde->window_size; - while ((p = pulse_queue_get_tail(pde)) != NULL) { - if (p->ts >= min_valid_ts) - return; - pulse_queue_dequeue(pde); - } -} - -static bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts) -{ - struct pulse_elem *p = pool_get_pulse_elem(); - if (p == NULL) { - p = kmalloc(sizeof(*p), GFP_ATOMIC); - if (p == NULL) { - DFS_POOL_STAT_INC(pulse_alloc_error); - return false; - } - DFS_POOL_STAT_INC(pulse_allocated); - DFS_POOL_STAT_INC(pulse_used); - } - INIT_LIST_HEAD(&p->head); - p->ts = ts; - list_add(&p->head, &pde->pulses); - pde->count++; - pde->last_ts = ts; - pulse_queue_check_window(pde); - if (pde->count >= pde->max_count) - pulse_queue_dequeue(pde); - return true; -} - -static bool pseq_handler_create_sequences(struct pri_detector *pde, - u64 ts, u32 min_count) -{ - struct pulse_elem *p; - list_for_each_entry(p, &pde->pulses, head) { - struct pri_sequence ps, *new_ps; - struct pulse_elem *p2; - u32 tmp_false_count; - u64 min_valid_ts; - u32 delta_ts = ts - p->ts; - - if (delta_ts < pde->rs->pri_min) - /* ignore too small pri */ - continue; - - if (delta_ts > pde->rs->pri_max) - /* stop on too large pri (sorted list) */ - break; - - /* build a new sequence with new potential pri */ - ps.count = 2; - ps.count_falses = 0; - ps.first_ts = p->ts; - ps.last_ts = ts; - ps.pri = ts - p->ts; - ps.dur = ps.pri * (pde->rs->ppb - 1) - + 2 * pde->rs->max_pri_tolerance; - - p2 = p; - tmp_false_count = 0; - min_valid_ts = ts - ps.dur; - /* check which past pulses are candidates for new sequence */ - list_for_each_entry_continue(p2, &pde->pulses, head) { - u32 factor; - if (p2->ts < min_valid_ts) - /* stop on crossing window border */ - break; - /* check if pulse match (multi)PRI */ - factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri, - pde->rs->max_pri_tolerance); - if (factor > 0) { - ps.count++; - ps.first_ts = p2->ts; - /* - * on match, add the intermediate falses - * and reset counter - */ - ps.count_falses += tmp_false_count; - tmp_false_count = 0; - } else { - /* this is a potential false one */ - tmp_false_count++; - } - } - if (ps.count < min_count) - /* did not reach minimum count, drop sequence */ - continue; - - /* this is a valid one, add it */ - ps.deadline_ts = ps.first_ts + ps.dur; - new_ps = pool_get_pseq_elem(); - if (new_ps == NULL) { - new_ps = kmalloc(sizeof(*new_ps), GFP_ATOMIC); - if (new_ps == NULL) { - DFS_POOL_STAT_INC(pseq_alloc_error); - return false; - } - DFS_POOL_STAT_INC(pseq_allocated); - DFS_POOL_STAT_INC(pseq_used); - } - memcpy(new_ps, &ps, sizeof(ps)); - INIT_LIST_HEAD(&new_ps->head); - list_add(&new_ps->head, &pde->sequences); - } - return true; -} - -/* check new ts and add to all matching existing sequences */ -static u32 -pseq_handler_add_to_existing_seqs(struct pri_detector *pde, u64 ts) -{ - u32 max_count = 0; - struct pri_sequence *ps, *ps2; - list_for_each_entry_safe(ps, ps2, &pde->sequences, head) { - u32 delta_ts; - u32 factor; - - /* first ensure that sequence is within window */ - if (ts > ps->deadline_ts) { - list_del_init(&ps->head); - pool_put_pseq_elem(ps); - continue; - } - - delta_ts = ts - ps->last_ts; - factor = pde_get_multiple(delta_ts, ps->pri, - pde->rs->max_pri_tolerance); - if (factor > 0) { - ps->last_ts = ts; - ps->count++; - - if (max_count < ps->count) - max_count = ps->count; - } else { - ps->count_falses++; - } - } - return max_count; -} - -static struct pri_sequence * -pseq_handler_check_detection(struct pri_detector *pde) -{ - struct pri_sequence *ps; - - if (list_empty(&pde->sequences)) - return NULL; - - list_for_each_entry(ps, &pde->sequences, head) { - /* - * we assume to have enough matching confidence if we - * 1) have enough pulses - * 2) have more matching than false pulses - */ - if ((ps->count >= pde->rs->ppb_thresh) && - (ps->count * pde->rs->num_pri >= ps->count_falses)) - return ps; - } - return NULL; -} - - -/* free pulse queue and sequences list and give objects back to pools */ -static void pri_detector_reset(struct pri_detector *pde, u64 ts) -{ - struct pri_sequence *ps, *ps0; - struct pulse_elem *p, *p0; - list_for_each_entry_safe(ps, ps0, &pde->sequences, head) { - list_del_init(&ps->head); - pool_put_pseq_elem(ps); - } - list_for_each_entry_safe(p, p0, &pde->pulses, head) { - list_del_init(&p->head); - pool_put_pulse_elem(p); - } - pde->count = 0; - pde->last_ts = ts; -} - -static void pri_detector_exit(struct pri_detector *de) -{ - pri_detector_reset(de, 0); - pool_deregister_ref(); - kfree(de); -} - -static struct pri_sequence *pri_detector_add_pulse(struct pri_detector *de, - struct pulse_event *event) -{ - u32 max_updated_seq; - struct pri_sequence *ps; - u64 ts = event->ts; - const struct radar_detector_specs *rs = de->rs; - - /* ignore pulses not within width range */ - if ((rs->width_min > event->width) || (rs->width_max < event->width)) - return NULL; - - if ((ts - de->last_ts) < rs->max_pri_tolerance) - /* if delta to last pulse is too short, don't use this pulse */ - return NULL; - de->last_ts = ts; - - max_updated_seq = pseq_handler_add_to_existing_seqs(de, ts); - - if (!pseq_handler_create_sequences(de, ts, max_updated_seq)) { - pri_detector_reset(de, ts); - return NULL; - } - - ps = pseq_handler_check_detection(de); - - if (ps == NULL) - pulse_queue_enqueue(de, ts); - - return ps; -} - -struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs) -{ - struct pri_detector *de; - - de = kzalloc(sizeof(*de), GFP_ATOMIC); - if (de == NULL) - return NULL; - de->exit = pri_detector_exit; - de->add_pulse = pri_detector_add_pulse; - de->reset = pri_detector_reset; - - INIT_LIST_HEAD(&de->sequences); - INIT_LIST_HEAD(&de->pulses); - de->window_size = rs->pri_max * rs->ppb * rs->num_pri; - de->max_count = rs->ppb * 2; - de->rs = rs; - - pool_register_ref(); - return de; -} diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h deleted file mode 100644 index 79f0fff4d1e..00000000000 --- a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2012 Neratec Solutions AG - * - * 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. - */ - -#ifndef DFS_PRI_DETECTOR_H -#define DFS_PRI_DETECTOR_H - -#include - -extern struct ath_dfs_pool_stats global_dfs_pool_stats; - -/** - * struct pri_sequence - sequence of pulses matching one PRI - * @head: list_head - * @pri: pulse repetition interval (PRI) in usecs - * @dur: duration of sequence in usecs - * @count: number of pulses in this sequence - * @count_falses: number of not matching pulses in this sequence - * @first_ts: time stamp of first pulse in usecs - * @last_ts: time stamp of last pulse in usecs - * @deadline_ts: deadline when this sequence becomes invalid (first_ts + dur) - */ -struct pri_sequence { - struct list_head head; - u32 pri; - u32 dur; - u32 count; - u32 count_falses; - u64 first_ts; - u64 last_ts; - u64 deadline_ts; -}; - -/** - * struct pri_detector - PRI detector element for a dedicated radar type - * @exit(): destructor - * @add_pulse(): add pulse event, returns pri_sequence if pattern was detected - * @reset(): clear states and reset to given time stamp - * @rs: detector specs for this detector element - * @last_ts: last pulse time stamp considered for this element in usecs - * @sequences: list_head holding potential pulse sequences - * @pulses: list connecting pulse_elem objects - * @count: number of pulses in queue - * @max_count: maximum number of pulses to be queued - * @window_size: window size back from newest pulse time stamp in usecs - */ -struct pri_detector { - void (*exit) (struct pri_detector *de); - struct pri_sequence * - (*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; - u64 last_ts; - struct list_head sequences; - struct list_head pulses; - u32 count; - u32 max_count; - u32 window_size; -}; - -struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs); - -#endif /* DFS_PRI_DETECTOR_H */ diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c new file mode 100644 index 00000000000..a1a69c5db40 --- /dev/null +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2012 Neratec Solutions AG + * + * 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 "dfs_pattern_detector.h" +#include "dfs_pri_detector.h" +#include "ath.h" + +/* + * tolerated deviation of radar time stamp in usecs on both sides + * TODO: this might need to be HW-dependent + */ +#define PRI_TOLERANCE 16 + +/** + * struct radar_types - contains array of patterns defined for one DFS domain + * @domain: DFS regulatory domain + * @num_radar_types: number of radar types to follow + * @radar_types: radar types array + */ +struct radar_types { + enum nl80211_dfs_regions region; + u32 num_radar_types; + const struct radar_detector_specs *radar_types; +}; + +/* percentage on ppb threshold to trigger detection */ +#define MIN_PPB_THRESH 50 +#define PPB_THRESH(PPB) ((PPB * MIN_PPB_THRESH + 50) / 100) +#define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF) +/* percentage of pulse width tolerance */ +#define WIDTH_TOLERANCE 5 +#define WIDTH_LOWER(X) ((X*(100-WIDTH_TOLERANCE)+50)/100) +#define WIDTH_UPPER(X) ((X*(100+WIDTH_TOLERANCE)+50)/100) + +#define ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB) \ +{ \ + ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX), \ + (PRF2PRI(PMAX) - PRI_TOLERANCE), \ + (PRF2PRI(PMIN) * PRF + PRI_TOLERANCE), PRF, PPB * PRF, \ + PPB_THRESH(PPB), PRI_TOLERANCE, \ +} + +/* radar types as defined by ETSI EN-301-893 v1.5.1 */ +static const struct radar_detector_specs etsi_radar_ref_types_v15[] = { + ETSI_PATTERN(0, 0, 1, 700, 700, 1, 18), + ETSI_PATTERN(1, 0, 5, 200, 1000, 1, 10), + ETSI_PATTERN(2, 0, 15, 200, 1600, 1, 15), + ETSI_PATTERN(3, 0, 15, 2300, 4000, 1, 25), + ETSI_PATTERN(4, 20, 30, 2000, 4000, 1, 20), + ETSI_PATTERN(5, 0, 2, 300, 400, 3, 10), + ETSI_PATTERN(6, 0, 2, 400, 1200, 3, 15), +}; + +static const struct radar_types etsi_radar_types_v15 = { + .region = NL80211_DFS_ETSI, + .num_radar_types = ARRAY_SIZE(etsi_radar_ref_types_v15), + .radar_types = etsi_radar_ref_types_v15, +}; + +/* for now, we support ETSI radar types, FCC and JP are TODO */ +static const struct radar_types *dfs_domains[] = { + &etsi_radar_types_v15, +}; + +/** + * get_dfs_domain_radar_types() - get radar types for a given DFS domain + * @param domain DFS domain + * @return radar_types ptr on success, NULL if DFS domain is not supported + */ +static const struct radar_types * +get_dfs_domain_radar_types(enum nl80211_dfs_regions region) +{ + u32 i; + for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) { + if (dfs_domains[i]->region == region) + return dfs_domains[i]; + } + return NULL; +} + +/** + * struct channel_detector - detector elements for a DFS channel + * @head: list_head + * @freq: frequency for this channel detector in MHz + * @detectors: array of dynamically created detector elements for this freq + * + * Channel detectors are required to provide multi-channel DFS detection, e.g. + * to support off-channel scanning. A pattern detector has a list of channels + * radar pulses have been reported for in the past. + */ +struct channel_detector { + struct list_head head; + u16 freq; + struct pri_detector **detectors; +}; + +/* channel_detector_reset() - reset detector lines for a given channel */ +static void channel_detector_reset(struct dfs_pattern_detector *dpd, + struct channel_detector *cd) +{ + u32 i; + if (cd == NULL) + return; + for (i = 0; i < dpd->num_radar_types; i++) + cd->detectors[i]->reset(cd->detectors[i], dpd->last_pulse_ts); +} + +/* channel_detector_exit() - destructor */ +static void channel_detector_exit(struct dfs_pattern_detector *dpd, + struct channel_detector *cd) +{ + u32 i; + if (cd == NULL) + return; + list_del(&cd->head); + for (i = 0; i < dpd->num_radar_types; i++) { + struct pri_detector *de = cd->detectors[i]; + if (de != NULL) + de->exit(de); + } + kfree(cd->detectors); + kfree(cd); +} + +static struct channel_detector * +channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq) +{ + u32 sz, i; + struct channel_detector *cd; + + cd = kmalloc(sizeof(*cd), GFP_ATOMIC); + if (cd == NULL) + goto fail; + + INIT_LIST_HEAD(&cd->head); + cd->freq = freq; + sz = sizeof(cd->detectors) * dpd->num_radar_types; + cd->detectors = kzalloc(sz, GFP_ATOMIC); + if (cd->detectors == NULL) + goto fail; + + for (i = 0; i < dpd->num_radar_types; i++) { + const struct radar_detector_specs *rs = &dpd->radar_spec[i]; + struct pri_detector *de = pri_detector_init(rs); + if (de == NULL) + goto fail; + cd->detectors[i] = de; + } + list_add(&cd->head, &dpd->channel_detectors); + return cd; + +fail: + ath_dbg(dpd->common, DFS, + "failed to allocate channel_detector for freq=%d\n", freq); + channel_detector_exit(dpd, cd); + return NULL; +} + +/** + * channel_detector_get() - get channel detector for given frequency + * @param dpd instance pointer + * @param freq frequency in MHz + * @return pointer to channel detector on success, NULL otherwise + * + * Return existing channel detector for the given frequency or return a + * newly create one. + */ +static struct channel_detector * +channel_detector_get(struct dfs_pattern_detector *dpd, u16 freq) +{ + struct channel_detector *cd; + list_for_each_entry(cd, &dpd->channel_detectors, head) { + if (cd->freq == freq) + return cd; + } + return channel_detector_create(dpd, freq); +} + +/* + * DFS Pattern Detector + */ + +/* dpd_reset(): reset all channel detectors */ +static void dpd_reset(struct dfs_pattern_detector *dpd) +{ + struct channel_detector *cd; + if (!list_empty(&dpd->channel_detectors)) + list_for_each_entry(cd, &dpd->channel_detectors, head) + channel_detector_reset(dpd, cd); + +} +static void dpd_exit(struct dfs_pattern_detector *dpd) +{ + struct channel_detector *cd, *cd0; + if (!list_empty(&dpd->channel_detectors)) + list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head) + channel_detector_exit(dpd, cd); + kfree(dpd); +} + +static bool +dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event) +{ + u32 i; + struct channel_detector *cd; + + /* + * pulses received for a non-supported or un-initialized + * domain are treated as detected radars for fail-safety + */ + if (dpd->region == NL80211_DFS_UNSET) + return true; + + cd = channel_detector_get(dpd, event->freq); + if (cd == NULL) + return false; + + dpd->last_pulse_ts = event->ts; + /* reset detector on time stamp wraparound, caused by TSF reset */ + if (event->ts < dpd->last_pulse_ts) + dpd_reset(dpd); + + /* do type individual pattern matching */ + for (i = 0; i < dpd->num_radar_types; i++) { + struct pri_detector *pd = cd->detectors[i]; + struct pri_sequence *ps = pd->add_pulse(pd, event); + if (ps != NULL) { + ath_dbg(dpd->common, DFS, + "DFS: radar found on freq=%d: id=%d, pri=%d, " + "count=%d, count_false=%d\n", + event->freq, pd->rs->type_id, + ps->pri, ps->count, ps->count_falses); + channel_detector_reset(dpd, cd); + return true; + } + } + return false; +} + +static struct ath_dfs_pool_stats +dpd_get_stats(struct dfs_pattern_detector *dpd) +{ + return global_dfs_pool_stats; +} + +static bool dpd_set_domain(struct dfs_pattern_detector *dpd, + enum nl80211_dfs_regions region) +{ + const struct radar_types *rt; + struct channel_detector *cd, *cd0; + + if (dpd->region == region) + return true; + + dpd->region = NL80211_DFS_UNSET; + + rt = get_dfs_domain_radar_types(region); + if (rt == NULL) + return false; + + /* delete all channel detectors for previous DFS domain */ + if (!list_empty(&dpd->channel_detectors)) + list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head) + channel_detector_exit(dpd, cd); + dpd->radar_spec = rt->radar_types; + dpd->num_radar_types = rt->num_radar_types; + + dpd->region = region; + return true; +} + +static struct dfs_pattern_detector default_dpd = { + .exit = dpd_exit, + .set_dfs_domain = dpd_set_domain, + .add_pulse = dpd_add_pulse, + .get_stats = dpd_get_stats, + .region = NL80211_DFS_UNSET, +}; + +struct dfs_pattern_detector * +dfs_pattern_detector_init(struct ath_common *common, + enum nl80211_dfs_regions region) +{ + struct dfs_pattern_detector *dpd; + + if (!config_enabled(CONFIG_CFG80211_CERTIFICATION_ONUS)) + return NULL; + + dpd = kmalloc(sizeof(*dpd), GFP_KERNEL); + if (dpd == NULL) + return NULL; + + *dpd = default_dpd; + INIT_LIST_HEAD(&dpd->channel_detectors); + + dpd->common = common; + if (dpd->set_dfs_domain(dpd, region)) + return dpd; + + ath_dbg(common, DFS,"Could not set DFS domain to %d", region); + kfree(dpd); + return NULL; +} +EXPORT_SYMBOL(dfs_pattern_detector_init); diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.h b/drivers/net/wireless/ath/dfs_pattern_detector.h new file mode 100644 index 00000000000..dde2652b787 --- /dev/null +++ b/drivers/net/wireless/ath/dfs_pattern_detector.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2012 Neratec Solutions AG + * + * 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. + */ + +#ifndef DFS_PATTERN_DETECTOR_H +#define DFS_PATTERN_DETECTOR_H + +#include +#include +#include + +/** + * struct ath_dfs_pool_stats - DFS Statistics for global pools + */ +struct ath_dfs_pool_stats { + u32 pool_reference; + u32 pulse_allocated; + u32 pulse_alloc_error; + u32 pulse_used; + u32 pseq_allocated; + u32 pseq_alloc_error; + u32 pseq_used; +}; + +/** + * struct pulse_event - describing pulses reported by PHY + * @ts: pulse time stamp in us + * @freq: channel frequency in MHz + * @width: pulse duration in us + * @rssi: rssi of radar event + */ +struct pulse_event { + u64 ts; + u16 freq; + u8 width; + u8 rssi; +}; + +/** + * struct radar_detector_specs - detector specs for a radar pattern type + * @type_id: pattern type, as defined by regulatory + * @width_min: minimum radar pulse width in [us] + * @width_max: maximum radar pulse width in [us] + * @pri_min: minimum pulse repetition interval in [us] (including tolerance) + * @pri_max: minimum pri in [us] (including tolerance) + * @num_pri: maximum number of different pri for this type + * @ppb: pulses per bursts for this type + * @ppb_thresh: number of pulses required to trigger detection + * @max_pri_tolerance: pulse time stamp tolerance on both sides [us] + */ +struct radar_detector_specs { + u8 type_id; + u8 width_min; + u8 width_max; + u16 pri_min; + u16 pri_max; + u8 num_pri; + u8 ppb; + u8 ppb_thresh; + u8 max_pri_tolerance; +}; + +/** + * struct dfs_pattern_detector - DFS pattern detector + * @exit(): destructor + * @set_dfs_domain(): set DFS domain, resets detector lines upon domain changes + * @add_pulse(): add radar pulse to detector, returns true on detection + * @region: active DFS region, NL80211_DFS_UNSET until set + * @num_radar_types: number of different radar types + * @last_pulse_ts: time stamp of last valid pulse in usecs + * @radar_detector_specs: array of radar detection specs + * @channel_detectors: list connecting channel_detector elements + */ +struct dfs_pattern_detector { + void (*exit)(struct dfs_pattern_detector *dpd); + 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 ath_dfs_pool_stats (*get_stats)(struct dfs_pattern_detector *dpd); + enum nl80211_dfs_regions region; + u8 num_radar_types; + u64 last_pulse_ts; + /* needed for ath_dbg() */ + struct ath_common *common; + + const struct radar_detector_specs *radar_spec; + struct list_head channel_detectors; +}; + +/** + * dfs_pattern_detector_init() - constructor for pattern detector class + * @param region: DFS domain to be used, can be NL80211_DFS_UNSET at creation + * @return instance pointer on success, NULL otherwise + */ +extern struct dfs_pattern_detector * +dfs_pattern_detector_init(struct ath_common *common, + enum nl80211_dfs_regions region); +#endif /* DFS_PATTERN_DETECTOR_H */ diff --git a/drivers/net/wireless/ath/dfs_pri_detector.c b/drivers/net/wireless/ath/dfs_pri_detector.c new file mode 100644 index 00000000000..43b60817888 --- /dev/null +++ b/drivers/net/wireless/ath/dfs_pri_detector.c @@ -0,0 +1,429 @@ +/* + * Copyright (c) 2012 Neratec Solutions AG + * + * 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 "ath.h" +#include "dfs_pattern_detector.h" +#include "dfs_pri_detector.h" + +struct ath_dfs_pool_stats global_dfs_pool_stats = {}; + +#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++) +#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--) + +/** + * struct pulse_elem - elements in pulse queue + * @ts: time stamp in usecs + */ +struct pulse_elem { + struct list_head head; + u64 ts; +}; + +/** + * pde_get_multiple() - get number of multiples considering a given tolerance + * @return factor if abs(val - factor*fraction) <= tolerance, 0 otherwise + */ +static u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance) +{ + u32 remainder; + u32 factor; + u32 delta; + + if (fraction == 0) + return 0; + + delta = (val < fraction) ? (fraction - val) : (val - fraction); + + if (delta <= tolerance) + /* val and fraction are within tolerance */ + return 1; + + factor = val / fraction; + remainder = val % fraction; + if (remainder > tolerance) { + /* no exact match */ + if ((fraction - remainder) <= tolerance) + /* remainder is within tolerance */ + factor++; + else + factor = 0; + } + return factor; +} + +/** + * DOC: Singleton Pulse and Sequence Pools + * + * Instances of pri_sequence and pulse_elem are kept in singleton pools to + * reduce the number of dynamic allocations. They are shared between all + * instances and grow up to the peak number of simultaneously used objects. + * + * Memory is freed after all references to the pools are released. + */ +static u32 singleton_pool_references; +static LIST_HEAD(pulse_pool); +static LIST_HEAD(pseq_pool); +static DEFINE_SPINLOCK(pool_lock); + +static void pool_register_ref(void) +{ + spin_lock_bh(&pool_lock); + singleton_pool_references++; + DFS_POOL_STAT_INC(pool_reference); + spin_unlock_bh(&pool_lock); +} + +static void pool_deregister_ref(void) +{ + spin_lock_bh(&pool_lock); + singleton_pool_references--; + DFS_POOL_STAT_DEC(pool_reference); + if (singleton_pool_references == 0) { + /* free singleton pools with no references left */ + struct pri_sequence *ps, *ps0; + struct pulse_elem *p, *p0; + + list_for_each_entry_safe(p, p0, &pulse_pool, head) { + list_del(&p->head); + DFS_POOL_STAT_DEC(pulse_allocated); + kfree(p); + } + list_for_each_entry_safe(ps, ps0, &pseq_pool, head) { + list_del(&ps->head); + DFS_POOL_STAT_DEC(pseq_allocated); + kfree(ps); + } + } + spin_unlock_bh(&pool_lock); +} + +static void pool_put_pulse_elem(struct pulse_elem *pe) +{ + spin_lock_bh(&pool_lock); + list_add(&pe->head, &pulse_pool); + DFS_POOL_STAT_DEC(pulse_used); + spin_unlock_bh(&pool_lock); +} + +static void pool_put_pseq_elem(struct pri_sequence *pse) +{ + spin_lock_bh(&pool_lock); + list_add(&pse->head, &pseq_pool); + DFS_POOL_STAT_DEC(pseq_used); + spin_unlock_bh(&pool_lock); +} + +static struct pri_sequence *pool_get_pseq_elem(void) +{ + struct pri_sequence *pse = NULL; + spin_lock_bh(&pool_lock); + if (!list_empty(&pseq_pool)) { + pse = list_first_entry(&pseq_pool, struct pri_sequence, head); + list_del(&pse->head); + DFS_POOL_STAT_INC(pseq_used); + } + spin_unlock_bh(&pool_lock); + return pse; +} + +static struct pulse_elem *pool_get_pulse_elem(void) +{ + struct pulse_elem *pe = NULL; + spin_lock_bh(&pool_lock); + if (!list_empty(&pulse_pool)) { + pe = list_first_entry(&pulse_pool, struct pulse_elem, head); + list_del(&pe->head); + DFS_POOL_STAT_INC(pulse_used); + } + spin_unlock_bh(&pool_lock); + return pe; +} + +static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde) +{ + struct list_head *l = &pde->pulses; + if (list_empty(l)) + return NULL; + return list_entry(l->prev, struct pulse_elem, head); +} + +static bool pulse_queue_dequeue(struct pri_detector *pde) +{ + struct pulse_elem *p = pulse_queue_get_tail(pde); + if (p != NULL) { + list_del_init(&p->head); + pde->count--; + /* give it back to pool */ + pool_put_pulse_elem(p); + } + return (pde->count > 0); +} + +/* remove pulses older than window */ +static void pulse_queue_check_window(struct pri_detector *pde) +{ + u64 min_valid_ts; + struct pulse_elem *p; + + /* there is no delta time with less than 2 pulses */ + if (pde->count < 2) + return; + + if (pde->last_ts <= pde->window_size) + return; + + min_valid_ts = pde->last_ts - pde->window_size; + while ((p = pulse_queue_get_tail(pde)) != NULL) { + if (p->ts >= min_valid_ts) + return; + pulse_queue_dequeue(pde); + } +} + +static bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts) +{ + struct pulse_elem *p = pool_get_pulse_elem(); + if (p == NULL) { + p = kmalloc(sizeof(*p), GFP_ATOMIC); + if (p == NULL) { + DFS_POOL_STAT_INC(pulse_alloc_error); + return false; + } + DFS_POOL_STAT_INC(pulse_allocated); + DFS_POOL_STAT_INC(pulse_used); + } + INIT_LIST_HEAD(&p->head); + p->ts = ts; + list_add(&p->head, &pde->pulses); + pde->count++; + pde->last_ts = ts; + pulse_queue_check_window(pde); + if (pde->count >= pde->max_count) + pulse_queue_dequeue(pde); + return true; +} + +static bool pseq_handler_create_sequences(struct pri_detector *pde, + u64 ts, u32 min_count) +{ + struct pulse_elem *p; + list_for_each_entry(p, &pde->pulses, head) { + struct pri_sequence ps, *new_ps; + struct pulse_elem *p2; + u32 tmp_false_count; + u64 min_valid_ts; + u32 delta_ts = ts - p->ts; + + if (delta_ts < pde->rs->pri_min) + /* ignore too small pri */ + continue; + + if (delta_ts > pde->rs->pri_max) + /* stop on too large pri (sorted list) */ + break; + + /* build a new sequence with new potential pri */ + ps.count = 2; + ps.count_falses = 0; + ps.first_ts = p->ts; + ps.last_ts = ts; + ps.pri = ts - p->ts; + ps.dur = ps.pri * (pde->rs->ppb - 1) + + 2 * pde->rs->max_pri_tolerance; + + p2 = p; + tmp_false_count = 0; + min_valid_ts = ts - ps.dur; + /* check which past pulses are candidates for new sequence */ + list_for_each_entry_continue(p2, &pde->pulses, head) { + u32 factor; + if (p2->ts < min_valid_ts) + /* stop on crossing window border */ + break; + /* check if pulse match (multi)PRI */ + factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri, + pde->rs->max_pri_tolerance); + if (factor > 0) { + ps.count++; + ps.first_ts = p2->ts; + /* + * on match, add the intermediate falses + * and reset counter + */ + ps.count_falses += tmp_false_count; + tmp_false_count = 0; + } else { + /* this is a potential false one */ + tmp_false_count++; + } + } + if (ps.count < min_count) + /* did not reach minimum count, drop sequence */ + continue; + + /* this is a valid one, add it */ + ps.deadline_ts = ps.first_ts + ps.dur; + new_ps = pool_get_pseq_elem(); + if (new_ps == NULL) { + new_ps = kmalloc(sizeof(*new_ps), GFP_ATOMIC); + if (new_ps == NULL) { + DFS_POOL_STAT_INC(pseq_alloc_error); + return false; + } + DFS_POOL_STAT_INC(pseq_allocated); + DFS_POOL_STAT_INC(pseq_used); + } + memcpy(new_ps, &ps, sizeof(ps)); + INIT_LIST_HEAD(&new_ps->head); + list_add(&new_ps->head, &pde->sequences); + } + return true; +} + +/* check new ts and add to all matching existing sequences */ +static u32 +pseq_handler_add_to_existing_seqs(struct pri_detector *pde, u64 ts) +{ + u32 max_count = 0; + struct pri_sequence *ps, *ps2; + list_for_each_entry_safe(ps, ps2, &pde->sequences, head) { + u32 delta_ts; + u32 factor; + + /* first ensure that sequence is within window */ + if (ts > ps->deadline_ts) { + list_del_init(&ps->head); + pool_put_pseq_elem(ps); + continue; + } + + delta_ts = ts - ps->last_ts; + factor = pde_get_multiple(delta_ts, ps->pri, + pde->rs->max_pri_tolerance); + if (factor > 0) { + ps->last_ts = ts; + ps->count++; + + if (max_count < ps->count) + max_count = ps->count; + } else { + ps->count_falses++; + } + } + return max_count; +} + +static struct pri_sequence * +pseq_handler_check_detection(struct pri_detector *pde) +{ + struct pri_sequence *ps; + + if (list_empty(&pde->sequences)) + return NULL; + + list_for_each_entry(ps, &pde->sequences, head) { + /* + * we assume to have enough matching confidence if we + * 1) have enough pulses + * 2) have more matching than false pulses + */ + if ((ps->count >= pde->rs->ppb_thresh) && + (ps->count * pde->rs->num_pri >= ps->count_falses)) + return ps; + } + return NULL; +} + + +/* free pulse queue and sequences list and give objects back to pools */ +static void pri_detector_reset(struct pri_detector *pde, u64 ts) +{ + struct pri_sequence *ps, *ps0; + struct pulse_elem *p, *p0; + list_for_each_entry_safe(ps, ps0, &pde->sequences, head) { + list_del_init(&ps->head); + pool_put_pseq_elem(ps); + } + list_for_each_entry_safe(p, p0, &pde->pulses, head) { + list_del_init(&p->head); + pool_put_pulse_elem(p); + } + pde->count = 0; + pde->last_ts = ts; +} + +static void pri_detector_exit(struct pri_detector *de) +{ + pri_detector_reset(de, 0); + pool_deregister_ref(); + kfree(de); +} + +static struct pri_sequence *pri_detector_add_pulse(struct pri_detector *de, + struct pulse_event *event) +{ + u32 max_updated_seq; + struct pri_sequence *ps; + u64 ts = event->ts; + const struct radar_detector_specs *rs = de->rs; + + /* ignore pulses not within width range */ + if ((rs->width_min > event->width) || (rs->width_max < event->width)) + return NULL; + + if ((ts - de->last_ts) < rs->max_pri_tolerance) + /* if delta to last pulse is too short, don't use this pulse */ + return NULL; + de->last_ts = ts; + + max_updated_seq = pseq_handler_add_to_existing_seqs(de, ts); + + if (!pseq_handler_create_sequences(de, ts, max_updated_seq)) { + pri_detector_reset(de, ts); + return NULL; + } + + ps = pseq_handler_check_detection(de); + + if (ps == NULL) + pulse_queue_enqueue(de, ts); + + return ps; +} + +struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs) +{ + struct pri_detector *de; + + de = kzalloc(sizeof(*de), GFP_ATOMIC); + if (de == NULL) + return NULL; + de->exit = pri_detector_exit; + de->add_pulse = pri_detector_add_pulse; + de->reset = pri_detector_reset; + + INIT_LIST_HEAD(&de->sequences); + INIT_LIST_HEAD(&de->pulses); + de->window_size = rs->pri_max * rs->ppb * rs->num_pri; + de->max_count = rs->ppb * 2; + de->rs = rs; + + pool_register_ref(); + return de; +} diff --git a/drivers/net/wireless/ath/dfs_pri_detector.h b/drivers/net/wireless/ath/dfs_pri_detector.h new file mode 100644 index 00000000000..79f0fff4d1e --- /dev/null +++ b/drivers/net/wireless/ath/dfs_pri_detector.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2012 Neratec Solutions AG + * + * 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. + */ + +#ifndef DFS_PRI_DETECTOR_H +#define DFS_PRI_DETECTOR_H + +#include + +extern struct ath_dfs_pool_stats global_dfs_pool_stats; + +/** + * struct pri_sequence - sequence of pulses matching one PRI + * @head: list_head + * @pri: pulse repetition interval (PRI) in usecs + * @dur: duration of sequence in usecs + * @count: number of pulses in this sequence + * @count_falses: number of not matching pulses in this sequence + * @first_ts: time stamp of first pulse in usecs + * @last_ts: time stamp of last pulse in usecs + * @deadline_ts: deadline when this sequence becomes invalid (first_ts + dur) + */ +struct pri_sequence { + struct list_head head; + u32 pri; + u32 dur; + u32 count; + u32 count_falses; + u64 first_ts; + u64 last_ts; + u64 deadline_ts; +}; + +/** + * struct pri_detector - PRI detector element for a dedicated radar type + * @exit(): destructor + * @add_pulse(): add pulse event, returns pri_sequence if pattern was detected + * @reset(): clear states and reset to given time stamp + * @rs: detector specs for this detector element + * @last_ts: last pulse time stamp considered for this element in usecs + * @sequences: list_head holding potential pulse sequences + * @pulses: list connecting pulse_elem objects + * @count: number of pulses in queue + * @max_count: maximum number of pulses to be queued + * @window_size: window size back from newest pulse time stamp in usecs + */ +struct pri_detector { + void (*exit) (struct pri_detector *de); + struct pri_sequence * + (*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; + u64 last_ts; + struct list_head sequences; + struct list_head pulses; + u32 count; + u32 max_count; + u32 window_size; +}; + +struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs); + +#endif /* DFS_PRI_DETECTOR_H */ -- cgit v1.2.3 From 8a3b6c800a993bbc7559bcfa77a34e4fc633cf0e Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 14 Oct 2013 17:42:07 -0700 Subject: ath: move common dynamic regulatory domain setting to a helper This moves the dynamic regulatory domain selection code into a helper. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/regd.c | 55 +++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 18 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 7d077c752dd..8373650a946 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -356,14 +356,48 @@ static u16 ath_regd_find_country_by_name(char *alpha2) return -1; } +static int __ath_reg_dyn_country(struct wiphy *wiphy, + struct ath_regulatory *reg, + struct regulatory_request *request) +{ + u16 country_code; + + if (!ath_is_world_regd(reg)) + return -EINVAL; + + country_code = ath_regd_find_country_by_name(request->alpha2); + if (country_code == (u16) -1) + return -EINVAL; + + reg->current_rd = COUNTRY_ERD_FLAG; + reg->current_rd |= country_code; + + __ath_regd_init(reg); + + ath_reg_apply_world_flags(wiphy, request->initiator, reg); + + return 0; +} + +static void ath_reg_dyn_country(struct wiphy *wiphy, + struct ath_regulatory *reg, + struct regulatory_request *request) +{ + if (__ath_reg_dyn_country(wiphy, reg, request)) + return; + + printk(KERN_DEBUG "ath: regdomain 0x%0x " + "dynamically updated by %s\n", + reg->current_rd, + reg_initiator_name(request->initiator)); +} + void ath_reg_notifier_apply(struct wiphy *wiphy, struct regulatory_request *request, struct ath_regulatory *reg) { struct ath_common *common = container_of(reg, struct ath_common, regulatory); - u16 country_code; - /* We always apply this */ ath_reg_apply_radar_flags(wiphy); @@ -391,22 +425,7 @@ void ath_reg_notifier_apply(struct wiphy *wiphy, case NL80211_REGDOM_SET_BY_USER: break; case NL80211_REGDOM_SET_BY_COUNTRY_IE: - if (!ath_is_world_regd(reg)) - break; - - country_code = ath_regd_find_country_by_name(request->alpha2); - if (country_code == (u16) -1) - break; - - reg->current_rd = COUNTRY_ERD_FLAG; - reg->current_rd |= country_code; - - printk(KERN_DEBUG "ath: regdomain 0x%0x updated by CountryIE\n", - reg->current_rd); - __ath_regd_init(reg); - - ath_reg_apply_world_flags(wiphy, request->initiator, reg); - + ath_reg_dyn_country(wiphy, reg, request); break; } } -- cgit v1.2.3 From 4da225bbc366a4efd6458636c155b34ac9784d4b Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 14 Oct 2013 17:42:08 -0700 Subject: ath: split user and driver reguluatory hint parsing On the regulatory notifier split up the parsing of the hints coming from drivers or user. We'll treat these separately. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/regd.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 8373650a946..7222eebba47 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -422,6 +422,7 @@ void ath_reg_notifier_apply(struct wiphy *wiphy, sizeof(struct ath_regulatory)); break; case NL80211_REGDOM_SET_BY_DRIVER: + break; case NL80211_REGDOM_SET_BY_USER: break; case NL80211_REGDOM_SET_BY_COUNTRY_IE: -- cgit v1.2.3 From 94e7876daa4396d7a997d57dfcceaab6851bd017 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 14 Oct 2013 17:42:09 -0700 Subject: ath: add support for proper dynamic regulatory hints This enables support for dynamic user regulatory hints. This is enabled only when CFG80211_CERTIFICATION_ONUS is selected. For US and JP this is explicitly disabled unless the systems are being used for strict controlled testing. Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/Kconfig | 17 ++++++++ drivers/net/wireless/ath/regd.c | 84 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index ba81d6292ee..c63d1159db5 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -25,6 +25,23 @@ config ATH_DEBUG Say Y, if you want to debug atheros wireless drivers. Right now only ath9k makes use of this. +config ATH_REG_DYNAMIC_USER_REG_HINTS + bool "Atheros dynamic user regulatory hints" + depends on CFG80211_CERTIFICATION_ONUS + default n + ---help--- + Say N. This should only be enabled in countries where + this feature is explicitly allowed and only on cards that + specifically have been tested for this. + +config ATH_REG_DYNAMIC_USER_CERT_TESTING + bool "Atheros dynamic user regulatory testing" + depends on ATH_REG_DYNAMIC_USER_REG_HINTS && CFG80211_CERTIFICATION_ONUS + default n + ---help--- + Say N. This should only be enabled on systems + undergoing certification testing. + source "drivers/net/wireless/ath/ath5k/Kconfig" source "drivers/net/wireless/ath/ath9k/Kconfig" source "drivers/net/wireless/ath/carl9170/Kconfig" diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 7222eebba47..c00687e0568 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -392,6 +392,89 @@ static void ath_reg_dyn_country(struct wiphy *wiphy, reg_initiator_name(request->initiator)); } +static bool dynamic_country_user_possible(struct ath_regulatory *reg) +{ + if (config_enabled(CONFIG_ATH_REG_DYNAMIC_USER_CERT_TESTING)) + return true; + + switch (reg->country_code) { + case CTRY_UNITED_STATES: + case CTRY_JAPAN1: + case CTRY_JAPAN2: + case CTRY_JAPAN3: + case CTRY_JAPAN4: + case CTRY_JAPAN5: + case CTRY_JAPAN6: + case CTRY_JAPAN7: + case CTRY_JAPAN8: + case CTRY_JAPAN9: + case CTRY_JAPAN10: + case CTRY_JAPAN11: + case CTRY_JAPAN12: + case CTRY_JAPAN13: + case CTRY_JAPAN14: + case CTRY_JAPAN15: + case CTRY_JAPAN16: + case CTRY_JAPAN17: + case CTRY_JAPAN18: + case CTRY_JAPAN19: + case CTRY_JAPAN20: + case CTRY_JAPAN21: + case CTRY_JAPAN22: + case CTRY_JAPAN23: + case CTRY_JAPAN24: + case CTRY_JAPAN25: + case CTRY_JAPAN26: + case CTRY_JAPAN27: + case CTRY_JAPAN28: + case CTRY_JAPAN29: + case CTRY_JAPAN30: + case CTRY_JAPAN31: + case CTRY_JAPAN32: + case CTRY_JAPAN33: + case CTRY_JAPAN34: + case CTRY_JAPAN35: + case CTRY_JAPAN36: + case CTRY_JAPAN37: + case CTRY_JAPAN38: + case CTRY_JAPAN39: + case CTRY_JAPAN40: + case CTRY_JAPAN41: + case CTRY_JAPAN42: + case CTRY_JAPAN43: + case CTRY_JAPAN44: + case CTRY_JAPAN45: + case CTRY_JAPAN46: + case CTRY_JAPAN47: + case CTRY_JAPAN48: + case CTRY_JAPAN49: + case CTRY_JAPAN50: + case CTRY_JAPAN51: + case CTRY_JAPAN52: + case CTRY_JAPAN53: + case CTRY_JAPAN54: + case CTRY_JAPAN55: + case CTRY_JAPAN56: + case CTRY_JAPAN57: + case CTRY_JAPAN58: + case CTRY_JAPAN59: + return false; + } + + return true; +} + +static void ath_reg_dyn_country_user(struct wiphy *wiphy, + struct ath_regulatory *reg, + struct regulatory_request *request) +{ + if (!config_enabled(CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS)) + return; + if (!dynamic_country_user_possible(reg)) + return; + ath_reg_dyn_country(wiphy, reg, request); +} + void ath_reg_notifier_apply(struct wiphy *wiphy, struct regulatory_request *request, struct ath_regulatory *reg) @@ -424,6 +507,7 @@ void ath_reg_notifier_apply(struct wiphy *wiphy, case NL80211_REGDOM_SET_BY_DRIVER: break; case NL80211_REGDOM_SET_BY_USER: + ath_reg_dyn_country_user(wiphy, reg, request); break; case NL80211_REGDOM_SET_BY_COUNTRY_IE: ath_reg_dyn_country(wiphy, reg, request); -- cgit v1.2.3 From 09b029b6b68daa4b961841173d90f267cee9c117 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 14 Oct 2013 17:42:10 -0700 Subject: ath9k: check for NULL rate when using ieee80211_get_rts_cts_rate() ieee80211_get_rts_cts_rate() can return NULL, so don't rely on its members when it does return NULL. Signed-off-by: Rajkumar Manoharan Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/xmit.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index fc76052c072..6bcf62fd3da 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2006,6 +2006,9 @@ static void setup_frame_info(struct ieee80211_hw *hw, fi->keyix = ATH9K_TXKEYIX_INVALID; fi->keytype = keytype; fi->framelen = framelen; + + if (!rate) + return; fi->rtscts_rate = rate->hw_value; if (short_preamble) fi->rtscts_rate |= rate->hw_value_short; -- cgit v1.2.3 From 89f927af7f3389e20c8ad24abfb3d1369f3ffc10 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 14 Oct 2013 17:42:11 -0700 Subject: ath9k: add TX99 support TX99 support enables Specific Absorption Rate (SAR) testing. SAR is the unit of measurement for the amount of radio frequency(RF) absorbed by the body when using a wireless device. The RF exposure limits used are expressed in the terms of SAR, which is a measure of the electric and magnetic field strength and power density for transmitters operating at frequencies from 300 kHz to 100 GHz. Regulatory bodies around the world require that wireless device be evaluated to meet the RF exposure limits set forth in the governmental SAR regulations. In the examples below, for more bit rate options see the iw TX bitrate setting documentation: http://wireless.kernel.org/en/users/Documentation/iw#Modifying_transmit_bitrates Example usage: iw phy phy0 interface add moni0 type monitor ip link set dev moni0 up iw dev moni0 set channel 36 HT40+ iw set bitrates mcs-5 4 echo 10 > /sys/kernel/debug/ieee80211/phy0/ath9k/tx99_power echo 1 > /sys/kernel/debug/ieee80211/phy0/ath9k/tx99 Signed-off-by: Rajkumar Manoharan Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/Kconfig | 20 ++++ drivers/net/wireless/ath/ath9k/ar9002_phy.c | 22 ++++ drivers/net/wireless/ath/ath9k/ar9003_phy.c | 95 +++++++++++++++++ drivers/net/wireless/ath/ath9k/ath9k.h | 10 ++ drivers/net/wireless/ath/ath9k/debug.c | 118 ++++++++++++++++++++ drivers/net/wireless/ath/ath9k/hw-ops.h | 16 +++ drivers/net/wireless/ath/ath9k/hw.h | 4 + drivers/net/wireless/ath/ath9k/init.c | 26 ++--- drivers/net/wireless/ath/ath9k/link.c | 12 ++- drivers/net/wireless/ath/ath9k/main.c | 160 +++++++++++++++++++++++++++- drivers/net/wireless/ath/ath9k/recv.c | 3 + drivers/net/wireless/ath/ath9k/xmit.c | 53 ++++++++- 12 files changed, 522 insertions(+), 17 deletions(-) (limited to 'drivers/net/wireless/ath') diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 7944c25c9a4..32f139e2e89 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -84,6 +84,26 @@ config ATH9K_DFS_CERTIFIED developed. At this point enabling this option won't do anything except increase code size. +config ATH9K_TX99 + bool "Atheros ath9k TX99 testing support" + depends on CFG80211_CERTIFICATION_ONUS + default n + ---help--- + Say N. This should only be enabled on systems undergoing + certification testing and evaluation in a controlled environment. + Enabling this will only enable TX99 support, all other modes of + operation will be disabled. + + TX99 support enables Specific Absorption Rate (SAR) testing. + SAR is the unit of measurement for the amount of radio frequency(RF) + absorbed by the body when using a wireless device. The RF exposure + limits used are expressed in the terms of SAR, which is a measure + of the electric and magnetic field strength and power density for + transmitters operating at frequencies from 300 kHz to 100 GHz. + Regulatory bodies around the world require that wireless device + be evaluated to meet the RF exposure limits set forth in the + governmental SAR regulations. + config ATH9K_LEGACY_RATE_CONTROL bool "Atheros ath9k rate control" depends on ATH9K diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c index 17970d49d85..f087117b2e6 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c @@ -680,6 +680,26 @@ static void ar9002_hw_spectral_scan_wait(struct ath_hw *ah) } } +static void ar9002_hw_tx99_start(struct ath_hw *ah, u32 qnum) +{ + REG_SET_BIT(ah, 0x9864, 0x7f000); + REG_SET_BIT(ah, 0x9924, 0x7f00fe); + REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); + REG_WRITE(ah, AR_CR, AR_CR_RXD); + REG_WRITE(ah, AR_DLCL_IFS(qnum), 0); + REG_WRITE(ah, AR_D_GBL_IFS_SIFS, 20); + REG_WRITE(ah, AR_D_GBL_IFS_EIFS, 20); + REG_WRITE(ah, AR_D_FPCTL, 0x10|qnum); + REG_WRITE(ah, AR_TIME_OUT, 0x00000400); + REG_WRITE(ah, AR_DRETRY_LIMIT(qnum), 0xffffffff); + REG_SET_BIT(ah, AR_QMISC(qnum), AR_Q_MISC_DCU_EARLY_TERM_REQ); +} + +static void ar9002_hw_tx99_stop(struct ath_hw *ah) +{ + REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); +} + void ar9002_hw_attach_phy_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); @@ -701,6 +721,8 @@ void ar9002_hw_attach_phy_ops(struct ath_hw *ah) #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT ops->set_bt_ant_diversity = ar9002_hw_set_bt_ant_diversity; #endif + ops->tx99_start = ar9002_hw_tx99_start; + ops->tx99_stop = ar9002_hw_tx99_stop; ar9002_hw_set_nf_limits(ah); } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index f3adafd3370..11f53589a3f 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -1617,6 +1617,98 @@ static void ar9003_hw_spectral_scan_wait(struct ath_hw *ah) } } +static void ar9003_hw_tx99_start(struct ath_hw *ah, u32 qnum) +{ + REG_SET_BIT(ah, AR_PHY_TEST, PHY_AGC_CLR); + REG_SET_BIT(ah, 0x9864, 0x7f000); + REG_SET_BIT(ah, 0x9924, 0x7f00fe); + REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); + REG_WRITE(ah, AR_CR, AR_CR_RXD); + REG_WRITE(ah, AR_DLCL_IFS(qnum), 0); + REG_WRITE(ah, AR_D_GBL_IFS_SIFS, 20); /* 50 OK */ + REG_WRITE(ah, AR_D_GBL_IFS_EIFS, 20); + REG_WRITE(ah, AR_TIME_OUT, 0x00000400); + REG_WRITE(ah, AR_DRETRY_LIMIT(qnum), 0xffffffff); + REG_SET_BIT(ah, AR_QMISC(qnum), AR_Q_MISC_DCU_EARLY_TERM_REQ); +} + +static void ar9003_hw_tx99_stop(struct ath_hw *ah) +{ + REG_CLR_BIT(ah, AR_PHY_TEST, PHY_AGC_CLR); + REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); +} + +static void ar9003_hw_tx99_set_txpower(struct ath_hw *ah, u8 txpower) +{ + static s16 p_pwr_array[ar9300RateSize] = { 0 }; + unsigned int i; + + if (txpower <= MAX_RATE_POWER) { + for (i = 0; i < ar9300RateSize; i++) + p_pwr_array[i] = txpower; + } else { + for (i = 0; i < ar9300RateSize; i++) + p_pwr_array[i] = MAX_RATE_POWER; + } + + REG_WRITE(ah, 0xa458, 0); + + REG_WRITE(ah, 0xa3c0, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 0)); + REG_WRITE(ah, 0xa3c4, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_54], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_48], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_36], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 0)); + REG_WRITE(ah, 0xa3c8, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 0)); + REG_WRITE(ah, 0xa3cc, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_11S], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_11L], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_5S], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 0)); + REG_WRITE(ah, 0xa3d0, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_5], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_4], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_1_3_9_11_17_19], 8)| + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_0_8_16], 0)); + REG_WRITE(ah, 0xa3d4, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_13], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_12], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_7], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_6], 0)); + REG_WRITE(ah, 0xa3e4, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_21], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_20], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_15], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_14], 0)); + REG_WRITE(ah, 0xa3e8, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_23], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_22], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_23], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_22], 0)); + REG_WRITE(ah, 0xa3d8, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_5], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_4], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_1_3_9_11_17_19], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_0_8_16], 0)); + REG_WRITE(ah, 0xa3dc, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_13], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_12], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_7], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_6], 0)); + REG_WRITE(ah, 0xa3ec, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_21], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_20], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_15], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_14], 0)); +} + void ar9003_hw_attach_phy_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); @@ -1656,6 +1748,9 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah) #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT ops->set_bt_ant_diversity = ar9003_hw_set_bt_ant_diversity; #endif + ops->tx99_start = ar9003_hw_tx99_start; + ops->tx99_stop = ar9003_hw_tx99_stop; + ops->tx99_set_txpower = ar9003_hw_tx99_set_txpower; ar9003_hw_set_nf_limits(ah); ar9003_hw_set_radar_conf(ah); diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 1fce36dd908..4c3bbe4f309 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -778,6 +778,11 @@ struct ath_softc { enum spectral_mode spectral_mode; struct ath_spec_scan spec_config; + struct ieee80211_vif *tx99_vif; + struct sk_buff *tx99_skb; + bool tx99_state; + s16 tx99_power; + #ifdef CONFIG_PM_SLEEP atomic_t wow_got_bmiss_intr; atomic_t wow_sleep_proc_intr; /* in the middle of WoW sleep ? */ @@ -941,6 +946,11 @@ struct fft_sample_ht20_40 { u8 data[SPECTRAL_HT20_40_NUM_BINS]; } __packed; +int ath9k_tx99_init(struct ath_softc *sc); +void ath9k_tx99_deinit(struct ath_softc *sc); +int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb, + struct ath_tx_control *txctl); + void ath9k_tasklet(unsigned long data); int ath_cabq_update(struct ath_softc *); diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 1be2c787aac..83a2c59f680 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -1050,6 +1050,9 @@ static ssize_t write_file_spec_scan_ctl(struct file *file, char buf[32]; ssize_t len; + if (config_enabled(CONFIG_ATH9K_TX99)) + return -EOPNOTSUPP; + len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; @@ -1775,6 +1778,111 @@ void ath9k_deinit_debug(struct ath_softc *sc) } } +static ssize_t read_file_tx99(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[3]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->tx99_state); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_tx99(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + char buf[32]; + bool start; + ssize_t len; + int r; + + if (sc->nvifs > 1) + return -EOPNOTSUPP; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + if (strtobool(buf, &start)) + return -EINVAL; + + if (start == sc->tx99_state) { + if (!start) + return count; + ath_dbg(common, XMIT, "Resetting TX99\n"); + ath9k_tx99_deinit(sc); + } + + if (!start) { + ath9k_tx99_deinit(sc); + return count; + } + + r = ath9k_tx99_init(sc); + if (r) + return r; + + return count; +} + +static const struct file_operations fops_tx99 = { + .read = read_file_tx99, + .write = write_file_tx99, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t read_file_tx99_power(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d (%d dBm)\n", + sc->tx99_power, + sc->tx99_power / 2); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_tx99_power(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + int r; + u8 tx_power; + + r = kstrtou8_from_user(user_buf, count, 0, &tx_power); + if (r) + return r; + + if (tx_power > MAX_RATE_POWER) + return -EINVAL; + + sc->tx99_power = tx_power; + + ath9k_ps_wakeup(sc); + ath9k_hw_tx99_set_txpower(sc->sc_ah, sc->tx99_power); + ath9k_ps_restore(sc); + + return count; +} + +static const struct file_operations fops_tx99_power = { + .read = read_file_tx99_power, + .write = write_file_tx99_power, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath9k_init_debug(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); @@ -1866,5 +1974,15 @@ int ath9k_init_debug(struct ath_hw *ah) debugfs_create_file("btcoex", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_btcoex); #endif + if (config_enabled(CONFIG_ATH9K_TX99) && + AR_SREV_9300_20_OR_LATER(ah)) { + debugfs_create_file("tx99", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_tx99); + debugfs_create_file("tx99_power", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_tx99_power); + } + return 0; } diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index 83f4927aeac..4f9378ddf07 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h @@ -78,6 +78,22 @@ static inline void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah, ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf); } +static inline void ath9k_hw_tx99_start(struct ath_hw *ah, u32 qnum) +{ + ath9k_hw_ops(ah)->tx99_start(ah, qnum); +} + +static inline void ath9k_hw_tx99_stop(struct ath_hw *ah) +{ + ath9k_hw_ops(ah)->tx99_stop(ah); +} + +static inline void ath9k_hw_tx99_set_txpower(struct ath_hw *ah, u8 power) +{ + if (ath9k_hw_ops(ah)->tx99_set_txpower) + ath9k_hw_ops(ah)->tx99_set_txpower(ah, power); +} + #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable) diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 81fcbc75612..9ea24f1cba7 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -703,6 +703,10 @@ struct ath_hw_ops { void (*spectral_scan_trigger)(struct ath_hw *ah); void (*spectral_scan_wait)(struct ath_hw *ah); + void (*tx99_start)(struct ath_hw *ah, u32 qnum); + void (*tx99_stop)(struct ath_hw *ah); + void (*tx99_set_txpower)(struct ath_hw *ah, u8 power); + #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT void (*set_bt_ant_diversity)(struct ath_hw *hw, bool enable); #endif diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index ba02ef2ab8a..e89db64532f 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -682,6 +682,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, common = ath9k_hw_common(ah); sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET); + sc->tx99_power = MAX_RATE_POWER + 1; if (!pdata) { ah->ah_flags |= AH_USE_EEPROM; @@ -785,6 +786,7 @@ err_queues: ath9k_hw_deinit(ah); err_hw: ath9k_eeprom_release(sc); + dev_kfree_skb_any(sc->tx99_skb); return ret; } @@ -842,7 +844,6 @@ static const struct ieee80211_iface_limit if_limits[] = { BIT(NL80211_IFTYPE_P2P_GO) }, }; - static const struct ieee80211_iface_limit if_dfs_limits[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_AP) }, }; @@ -903,17 +904,18 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; - hw->wiphy->interface_modes = - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_WDS) | - BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_MESH_POINT); - - hw->wiphy->iface_combinations = if_comb; - hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + if (!config_enabled(CONFIG_ATH9K_TX99)) { + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_WDS) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT); + hw->wiphy->iface_combinations = if_comb; + hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + } hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index b7975195d74..aed7e29dc50 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -28,6 +28,13 @@ void ath_tx_complete_poll_work(struct work_struct *work) int i; bool needreset = false; + + if (sc->tx99_state) { + ath_dbg(ath9k_hw_common(sc->sc_ah), RESET, + "skip tx hung detection on tx99\n"); + return; + } + for (i = 0; i < IEEE80211_NUM_ACS; i++) { txq = sc->tx.txq_map[i]; @@ -70,7 +77,7 @@ void ath_hw_check(struct work_struct *work) ath9k_ps_wakeup(sc); is_alive = ath9k_hw_check_alive(sc->sc_ah); - if (is_alive && !AR_SREV_9300(sc->sc_ah)) + if ((is_alive && !AR_SREV_9300(sc->sc_ah)) || sc->tx99_state) goto out; else if (!is_alive && AR_SREV_9300(sc->sc_ah)) { ath_dbg(common, RESET, @@ -141,6 +148,9 @@ void ath_hw_pll_work(struct work_struct *work) if (!test_bit(SC_OP_BEACONS, &sc->sc_flags)) return; + if (sc->tx99_state) + return; + ath9k_ps_wakeup(sc); pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah); ath9k_ps_restore(sc); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index c42b55c1fac..98964b02f13 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1047,6 +1047,14 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); + if (config_enabled(CONFIG_ATH9K_TX99)) { + if (sc->nvifs >= 1) { + mutex_unlock(&sc->mutex); + return -EOPNOTSUPP; + } + sc->tx99_vif = vif; + } + ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type); sc->nvifs++; @@ -1075,9 +1083,15 @@ static int ath9k_change_interface(struct ieee80211_hw *hw, struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - ath_dbg(common, CONFIG, "Change Interface\n"); mutex_lock(&sc->mutex); + if (config_enabled(CONFIG_ATH9K_TX99)) { + mutex_unlock(&sc->mutex); + return -EOPNOTSUPP; + } + + ath_dbg(common, CONFIG, "Change Interface\n"); + if (ath9k_uses_beacons(vif->type)) ath9k_beacon_remove_slot(sc, vif); @@ -1107,6 +1121,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); sc->nvifs--; + sc->tx99_vif = NULL; if (ath9k_uses_beacons(vif->type)) ath9k_beacon_remove_slot(sc, vif); @@ -1128,6 +1143,9 @@ static void ath9k_enable_ps(struct ath_softc *sc) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); + if (config_enabled(CONFIG_ATH9K_TX99)) + return; + sc->ps_enabled = true; if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { if ((ah->imask & ATH9K_INT_TIM_TIMER) == 0) { @@ -1144,6 +1162,9 @@ static void ath9k_disable_ps(struct ath_softc *sc) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); + if (config_enabled(CONFIG_ATH9K_TX99)) + return; + sc->ps_enabled = false; ath9k_hw_setpower(ah, ATH9K_PM_AWAKE); if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { @@ -1167,6 +1188,9 @@ void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw) struct ath_common *common = ath9k_hw_common(ah); u32 rxfilter; + if (config_enabled(CONFIG_ATH9K_TX99)) + return; + if (!ath9k_hw_ops(ah)->spectral_scan_trigger) { ath_err(common, "spectrum analyzer not implemented on this hardware\n"); return; @@ -1746,6 +1770,9 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx, unsigned long flags; int pos; + if (config_enabled(CONFIG_ATH9K_TX99)) + return -EOPNOTSUPP; + spin_lock_irqsave(&common->cc_lock, flags); if (idx == 0) ath_update_survey_stats(sc); @@ -1778,6 +1805,9 @@ static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; + if (config_enabled(CONFIG_ATH9K_TX99)) + return; + mutex_lock(&sc->mutex); ah->coverage_class = coverage_class; @@ -2344,6 +2374,134 @@ static void ath9k_channel_switch_beacon(struct ieee80211_hw *hw, sc->csa_vif = vif; } +static void ath9k_tx99_stop(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + + ath_drain_all_txq(sc); + ath_startrecv(sc); + + ath9k_hw_set_interrupts(ah); + ath9k_hw_enable_interrupts(ah); + + ieee80211_wake_queues(sc->hw); + + kfree_skb(sc->tx99_skb); + sc->tx99_skb = NULL; + sc->tx99_state = false; + + ath9k_hw_tx99_stop(sc->sc_ah); + ath_dbg(common, XMIT, "TX99 stopped\n"); +} + +static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc) +{ + static u8 PN9Data[] = {0xff, 0x87, 0xb8, 0x59, 0xb7, 0xa1, 0xcc, 0x24, + 0x57, 0x5e, 0x4b, 0x9c, 0x0e, 0xe9, 0xea, 0x50, + 0x2a, 0xbe, 0xb4, 0x1b, 0xb6, 0xb0, 0x5d, 0xf1, + 0xe6, 0x9a, 0xe3, 0x45, 0xfd, 0x2c, 0x53, 0x18, + 0x0c, 0xca, 0xc9, 0xfb, 0x49, 0x37, 0xe5, 0xa8, + 0x51, 0x3b, 0x2f, 0x61, 0xaa, 0x72, 0x18, 0x84, + 0x02, 0x23, 0x23, 0xab, 0x63, 0x89, 0x51, 0xb3, + 0xe7, 0x8b, 0x72, 0x90, 0x4c, 0xe8, 0xfb, 0xc0}; + u32 len = 1200; + struct ieee80211_hw *hw = sc->hw; + struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *tx_info; + struct sk_buff *skb; + + skb = alloc_skb(len, GFP_KERNEL); + if (!skb) + return NULL; + + skb_put(skb, len); + + memset(skb->data, 0, len); + + hdr = (struct ieee80211_hdr *)skb->data; + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA); + hdr->duration_id = 0; + + memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN); + memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN); + memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); + + hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); + + tx_info = IEEE80211_SKB_CB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->band = hw->conf.chandef.chan->band; + tx_info->flags = IEEE80211_TX_CTL_NO_ACK; + tx_info->control.vif = sc->tx99_vif; + + memcpy(skb->data + sizeof(*hdr), PN9Data, sizeof(PN9Data)); + + return skb; +} + +void ath9k_tx99_deinit(struct ath_softc *sc) +{ + ath_reset(sc); + + ath9k_ps_wakeup(sc); + ath9k_tx99_stop(sc); + ath9k_ps_restore(sc); +} + +int ath9k_tx99_init(struct ath_softc *sc) +{ + struct ieee80211_hw *hw = sc->hw; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ath_tx_control txctl; + int r; + + if (sc->sc_flags & SC_OP_INVALID) { + ath_err(common, + "driver is in invalid state unable to use TX99"); + return -EINVAL; + } + + sc->tx99_skb = ath9k_build_tx99_skb(sc); + if (!sc->tx99_skb) + return -ENOMEM; + + memset(&txctl, 0, sizeof(txctl)); + txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO]; + + ath_reset(sc); + + ath9k_ps_wakeup(sc); + + ath9k_hw_disable_interrupts(ah); + atomic_set(&ah->intr_ref_cnt, -1); + ath_drain_all_txq(sc); + ath_stoprecv(sc); + + sc->tx99_state = true; + + ieee80211_stop_queues(hw); + + if (sc->tx99_power == MAX_RATE_POWER + 1) + sc->tx99_power = MAX_RATE_POWER; + + ath9k_hw_tx99_set_txpower(ah, sc->tx99_power); + r = ath9k_tx99_send(sc, sc->tx99_skb, &txctl); + if (r) { + ath_dbg(common, XMIT, "Failed to xmit TX99 skb\n"); + return r; + } + + ath_dbg(common, XMIT, "TX99 xmit started using %d ( %ddBm)\n", + sc->tx99_power, + sc->tx99_power / 2); + + /* We leave the harware awake as it will be chugging on */ + + return 0; +} + struct ieee80211_ops ath9k_ops = { .tx = ath9k_tx, .start = ath9k_start, diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index e37559a85bf..b1e74683b8d 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -375,6 +375,9 @@ u32 ath_calcrxfilter(struct ath_softc *sc) { u32 rfilt; + if (config_enabled(CONFIG_ATH9K_TX99)) + return 0; + rfilt = ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST | ATH9K_RX_FILTER_MCAST; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 6bcf62fd3da..bea5caa11d4 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1240,12 +1240,13 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, if (bf->bf_next) info.link = bf->bf_next->bf_daddr; else - info.link = 0; + info.link = (sc->tx99_state) ? bf->bf_daddr : 0; if (!bf_first) { bf_first = bf; - info.flags = ATH9K_TXDESC_INTREQ; + if (!sc->tx99_state) + info.flags = ATH9K_TXDESC_INTREQ; if ((tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) || txq == sc->tx.uapsdq) info.flags |= ATH9K_TXDESC_CLRDMASK; @@ -1932,7 +1933,7 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc); } - if (!edma) { + if (!edma || sc->tx99_state) { TX_STAT_INC(txq->axq_qnum, txstart); ath9k_hw_txstart(ah, txq->axq_qnum); } @@ -2360,6 +2361,8 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE); bf->bf_buf_addr = 0; + if (sc->tx99_state) + goto skip_tx_complete; if (bf->bf_state.bfs_paprd) { if (time_after(jiffies, @@ -2372,6 +2375,7 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, ath_debug_stat_tx(sc, bf, ts, txq, tx_flags); ath_tx_complete(sc, skb, tx_flags, txq); } +skip_tx_complete: /* At this point, skb (bf->bf_mpdu) is consumed...make sure we don't * accidentally reference it later. */ @@ -2730,3 +2734,46 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an) ath_txq_unlock(sc, txq); } } + +int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb, + struct ath_tx_control *txctl) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ath_frame_info *fi = get_frame_info(skb); + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_buf *bf; + int padpos, padsize; + + padpos = ieee80211_hdrlen(hdr->frame_control); + padsize = padpos & 3; + + if (padsize && skb->len > padpos) { + if (skb_headroom(skb) < padsize) { + ath_dbg(common, XMIT, + "tx99 padding failed\n"); + return -EINVAL; + } + + skb_push(skb, padsize); + memmove(skb->data, skb->data + padsize, padpos); + } + + fi->keyix = ATH9K_TXKEYIX_INVALID; + fi->framelen = skb->len + FCS_LEN; + fi->keytype = ATH9K_KEY_TYPE_CLEAR; + + bf = ath_tx_setup_buffer(sc, txctl->txq, NULL, skb); + if (!bf) { + ath_dbg(common, XMIT, "tx99 buffer setup failed\n"); + return -EINVAL; + } + + ath_set_rates(sc->tx99_vif, NULL, bf); + + ath9k_hw_set_desc_link(sc->sc_ah, bf->bf_desc, bf->bf_daddr); + ath9k_hw_tx99_start(sc->sc_ah, txctl->txq->axq_qnum); + + ath_tx_send_normal(sc, txctl->txq, NULL, skb); + + return 0; +} -- cgit v1.2.3