diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath11k/wmi.c')
| -rw-r--r-- | drivers/net/wireless/ath/ath11k/wmi.c | 633 | 
1 files changed, 581 insertions, 52 deletions
| diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 5ae2ef4680d6..6b68ccf65e39 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -73,6 +73,14 @@ struct wmi_tlv_dma_buf_release_parse {  	bool meta_data_done;  }; +struct wmi_tlv_fw_stats_parse { +	const struct wmi_stats_event *ev; +	const struct wmi_per_chain_rssi_stats *rssi; +	struct ath11k_fw_stats *stats; +	int rssi_num; +	bool chain_rssi_done; +}; +  static const struct wmi_tlv_policy wmi_tlv_policies[] = {  	[WMI_TAG_ARRAY_BYTE]  		= { .min_len = 0 }, @@ -120,6 +128,8 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = {  		= { .min_len = sizeof(struct wmi_peer_assoc_conf_event) },  	[WMI_TAG_STATS_EVENT]  		= { .min_len = sizeof(struct wmi_stats_event) }, +	[WMI_TAG_RFKILL_EVENT] = { +		.min_len = sizeof(struct wmi_rfkill_state_change_ev) },  	[WMI_TAG_PDEV_CTL_FAILSAFE_CHECK_EVENT]  		= { .min_len = sizeof(struct wmi_pdev_ctl_failsafe_chk_event) },  	[WMI_TAG_HOST_SWFDA_EVENT] = { @@ -128,6 +138,12 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = {  		.min_len = sizeof(struct wmi_probe_resp_tx_status_event) },  	[WMI_TAG_VDEV_DELETE_RESP_EVENT] = {  		.min_len = sizeof(struct wmi_vdev_delete_resp_event) }, +	[WMI_TAG_OBSS_COLOR_COLLISION_EVT] = { +		.min_len = sizeof(struct wmi_obss_color_collision_event) }, +	[WMI_TAG_11D_NEW_COUNTRY_EVENT] = { +		.min_len = sizeof(struct wmi_11d_new_cc_ev) }, +	[WMI_TAG_PER_CHAIN_RSSI_STATS] = { +		.min_len = sizeof(struct wmi_per_chain_rssi_stats) },  };  #define PRIMAP(_hw_mode_) \ @@ -249,6 +265,8 @@ static int ath11k_wmi_cmd_send_nowait(struct ath11k_pdev_wmi *wmi, struct sk_buf  	cmd_hdr = (struct wmi_cmd_hdr *)skb->data;  	cmd_hdr->cmd_id = cmd; +	trace_ath11k_wmi_cmd(ab, cmd_id, skb->data, skb->len); +  	memset(skb_cb, 0, sizeof(*skb_cb));  	ret = ath11k_htc_send(&ab->htc, wmi->eid, skb); @@ -267,21 +285,39 @@ int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb,  {  	struct ath11k_wmi_base *wmi_sc = wmi->wmi_ab;  	int ret = -EOPNOTSUPP; +	struct ath11k_base *ab = wmi_sc->ab;  	might_sleep(); -	wait_event_timeout(wmi_sc->tx_credits_wq, ({ -		ret = ath11k_wmi_cmd_send_nowait(wmi, skb, cmd_id); +	if (ab->hw_params.credit_flow) { +		wait_event_timeout(wmi_sc->tx_credits_wq, ({ +			ret = ath11k_wmi_cmd_send_nowait(wmi, skb, cmd_id); -		if (ret && test_bit(ATH11K_FLAG_CRASH_FLUSH, &wmi_sc->ab->dev_flags)) -			ret = -ESHUTDOWN; +			if (ret && test_bit(ATH11K_FLAG_CRASH_FLUSH, +					    &wmi_sc->ab->dev_flags)) +				ret = -ESHUTDOWN; -		(ret != -EAGAIN); -	}), WMI_SEND_TIMEOUT_HZ); +			(ret != -EAGAIN); +			}), WMI_SEND_TIMEOUT_HZ); +	} else { +		wait_event_timeout(wmi->tx_ce_desc_wq, ({ +			ret = ath11k_wmi_cmd_send_nowait(wmi, skb, cmd_id); + +			if (ret && test_bit(ATH11K_FLAG_CRASH_FLUSH, +					    &wmi_sc->ab->dev_flags)) +				ret = -ESHUTDOWN; + +			(ret != -ENOBUFS); +			}), WMI_SEND_TIMEOUT_HZ); +	}  	if (ret == -EAGAIN)  		ath11k_warn(wmi_sc->ab, "wmi command %d timeout\n", cmd_id); +	if (ret == -ENOBUFS) +		ath11k_warn(wmi_sc->ab, "ce desc not available for wmi command %d\n", +			    cmd_id); +  	return ret;  } @@ -315,6 +351,7 @@ ath11k_pull_mac_phy_cap_svc_ready_ext(struct ath11k_pdev_wmi *wmi_handle,  				      struct ath11k_pdev *pdev)  {  	struct wmi_mac_phy_capabilities *mac_phy_caps; +	struct ath11k_base *ab = wmi_handle->wmi_ab->ab;  	struct ath11k_band_cap *cap_band;  	struct ath11k_pdev_cap *pdev_cap = &pdev->cap;  	u32 phy_map; @@ -346,6 +383,10 @@ ath11k_pull_mac_phy_cap_svc_ready_ext(struct ath11k_pdev_wmi *wmi_handle,  	pdev->pdev_id = mac_phy_caps->pdev_id;  	pdev_cap->supported_bands |= mac_phy_caps->supported_bands;  	pdev_cap->ampdu_density = mac_phy_caps->ampdu_density; +	ab->target_pdev_ids[ab->target_pdev_count].supported_bands = +		mac_phy_caps->supported_bands; +	ab->target_pdev_ids[ab->target_pdev_count].pdev_id = mac_phy_caps->pdev_id; +	ab->target_pdev_count++;  	/* Take non-zero tx/rx chainmask. If tx/rx chainmask differs from  	 * band to band for a single radio, need to see how this should be @@ -485,6 +526,8 @@ static int ath11k_pull_service_ready_tlv(struct ath11k_base *ab,  	cap->default_dbs_hw_mode_index = ev->default_dbs_hw_mode_index;  	cap->num_msdu_desc = ev->num_msdu_desc; +	ath11k_dbg(ab, ATH11K_DBG_WMI, "wmi sys cap info 0x%x\n", cap->sys_cap_info); +  	return 0;  } @@ -1244,7 +1287,8 @@ int ath11k_wmi_pdev_set_param(struct ath11k *ar, u32 param_id,  	return ret;  } -int ath11k_wmi_pdev_set_ps_mode(struct ath11k *ar, int vdev_id, u32 enable) +int ath11k_wmi_pdev_set_ps_mode(struct ath11k *ar, int vdev_id, +				enum wmi_sta_ps_mode psmode)  {  	struct ath11k_pdev_wmi *wmi = ar->wmi;  	struct wmi_pdev_set_ps_mode_cmd *cmd; @@ -1259,7 +1303,7 @@ int ath11k_wmi_pdev_set_ps_mode(struct ath11k *ar, int vdev_id, u32 enable)  	cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_STA_POWERSAVE_MODE_CMD) |  			  FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);  	cmd->vdev_id = vdev_id; -	cmd->sta_ps_mode = enable; +	cmd->sta_ps_mode = psmode;  	ret = ath11k_wmi_cmd_send(wmi, skb, WMI_STA_POWERSAVE_MODE_CMDID);  	if (ret) { @@ -1269,7 +1313,7 @@ int ath11k_wmi_pdev_set_ps_mode(struct ath11k *ar, int vdev_id, u32 enable)  	ath11k_dbg(ar->ab, ATH11K_DBG_WMI,  		   "WMI vdev set psmode %d vdev id %d\n", -		   enable, vdev_id); +		   psmode, vdev_id);  	return ret;  } @@ -1612,6 +1656,15 @@ int ath11k_wmi_bcn_tmpl(struct ath11k *ar, u32 vdev_id,  	void *ptr;  	int ret, len;  	size_t aligned_len = roundup(bcn->len, 4); +	struct ieee80211_vif *vif; +	struct ath11k_vif *arvif = ath11k_mac_get_arvif(ar, vdev_id); + +	if (!arvif) { +		ath11k_warn(ar->ab, "failed to find arvif with vdev id %d\n", vdev_id); +		return -EINVAL; +	} + +	vif = arvif->vif;  	len = sizeof(*cmd) + sizeof(*bcn_prb_info) + TLV_HDR_SIZE + aligned_len; @@ -1624,8 +1677,12 @@ int ath11k_wmi_bcn_tmpl(struct ath11k *ar, u32 vdev_id,  			  FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);  	cmd->vdev_id = vdev_id;  	cmd->tim_ie_offset = offs->tim_offset; -	cmd->csa_switch_count_offset = offs->cntdwn_counter_offs[0]; -	cmd->ext_csa_switch_count_offset = offs->cntdwn_counter_offs[1]; + +	if (vif->csa_active) { +		cmd->csa_switch_count_offset = offs->cntdwn_counter_offs[0]; +		cmd->ext_csa_switch_count_offset = offs->cntdwn_counter_offs[1]; +	} +  	cmd->buf_len = bcn->len;  	ptr = skb->data + sizeof(*cmd); @@ -1689,7 +1746,8 @@ int ath11k_wmi_vdev_install_key(struct ath11k *ar,  	tlv = (struct wmi_tlv *)(skb->data + sizeof(*cmd));  	tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) |  		      FIELD_PREP(WMI_TLV_LEN, key_len_aligned); -	memcpy(tlv->value, (u8 *)arg->key_data, key_len_aligned); +	if (arg->key_data) +		memcpy(tlv->value, (u8 *)arg->key_data, key_len_aligned);  	ret = ath11k_wmi_cmd_send(wmi, skb, WMI_VDEV_INSTALL_KEY_CMDID);  	if (ret) { @@ -1762,7 +1820,7 @@ ath11k_wmi_copy_peer_flags(struct wmi_peer_assoc_complete_cmd *cmd,  		cmd->peer_flags |= WMI_PEER_AUTH;  	if (param->need_ptk_4_way) {  		cmd->peer_flags |= WMI_PEER_NEED_PTK_4_WAY; -		if (!hw_crypto_disabled) +		if (!hw_crypto_disabled && param->is_assoc)  			cmd->peer_flags &= ~WMI_PEER_AUTH;  	}  	if (param->need_gtk_2_way) @@ -2069,7 +2127,7 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar,  	void *ptr;  	int i, ret, len;  	u32 *tmp_ptr; -	u8 extraie_len_with_pad = 0; +	u16 extraie_len_with_pad = 0;  	struct hint_short_ssid *s_ssid = NULL;  	struct hint_bssid *hint_bssid = NULL; @@ -2088,7 +2146,7 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar,  		len += sizeof(*bssid) * params->num_bssid;  	len += TLV_HDR_SIZE; -	if (params->extraie.len) +	if (params->extraie.len && params->extraie.len <= 0xFFFF)  		extraie_len_with_pad =  			roundup(params->extraie.len, sizeof(u32));  	len += extraie_len_with_pad; @@ -2137,6 +2195,8 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar,  	cmd->num_ssids = params->num_ssids;  	cmd->ie_len = params->extraie.len;  	cmd->n_probes = params->n_probes; +	ether_addr_copy(cmd->mac_addr.addr, params->mac_addr.addr); +	ether_addr_copy(cmd->mac_mask.addr, params->mac_mask.addr);  	ptr += sizeof(*cmd); @@ -2195,7 +2255,7 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar,  		      FIELD_PREP(WMI_TLV_LEN, len);  	ptr += TLV_HDR_SIZE; -	if (params->extraie.len) +	if (extraie_len_with_pad)  		memcpy(ptr, params->extraie.ptr,  		       params->extraie.len); @@ -2386,6 +2446,8 @@ int ath11k_wmi_send_scan_chan_list_cmd(struct ath11k *ar,  					    tchan_info->reg_class_id);  			*reg2 |= FIELD_PREP(WMI_CHAN_REG_INFO2_ANT_MAX,  					    tchan_info->antennamax); +			*reg2 |= FIELD_PREP(WMI_CHAN_REG_INFO2_MAX_TX_PWR, +					    tchan_info->maxregpower);  			ath11k_dbg(ar->ab, ATH11K_DBG_WMI,  				   "WMI chan scan list chan[%d] = %u, chan_info->info %8x\n", @@ -2754,6 +2816,42 @@ out:  	return ret;  } +int ath11k_wmi_send_set_current_country_cmd(struct ath11k *ar, +					    struct wmi_set_current_country_params *param) +{ +	struct ath11k_pdev_wmi *wmi = ar->wmi; +	struct wmi_set_current_country_cmd *cmd; +	struct sk_buff *skb; +	int ret; + +	skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); +	if (!skb) +		return -ENOMEM; + +	cmd = (struct wmi_set_current_country_cmd *)skb->data; +	cmd->tlv_header = +		FIELD_PREP(WMI_TLV_TAG, WMI_TAG_SET_CURRENT_COUNTRY_CMD) | +		FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + +	cmd->pdev_id = ar->pdev->pdev_id; +	memcpy(&cmd->new_alpha2, ¶m->alpha2, 3); +	ret = ath11k_wmi_cmd_send(wmi, skb, WMI_SET_CURRENT_COUNTRY_CMDID); + +	ath11k_dbg(ar->ab, ATH11K_DBG_WMI, +		   "set current country pdev id %d alpha2 %c%c\n", +		   ar->pdev->pdev_id, +		   param->alpha2[0], +		   param->alpha2[1]); + +	if (ret) { +		ath11k_warn(ar->ab, +			    "failed to send WMI_SET_CURRENT_COUNTRY_CMDID: %d\n", ret); +		dev_kfree_skb(skb); +	} + +	return ret; +} +  int  ath11k_wmi_send_thermal_mitigation_param_cmd(struct ath11k *ar,  					     struct thermal_mitigation_params *param) @@ -2818,6 +2916,75 @@ ath11k_wmi_send_thermal_mitigation_param_cmd(struct ath11k *ar,  	return ret;  } +int ath11k_wmi_send_11d_scan_start_cmd(struct ath11k *ar, +				       struct wmi_11d_scan_start_params *param) +{ +	struct ath11k_pdev_wmi *wmi = ar->wmi; +	struct wmi_11d_scan_start_cmd *cmd; +	struct sk_buff *skb; +	int ret; + +	skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); +	if (!skb) +		return -ENOMEM; + +	cmd = (struct wmi_11d_scan_start_cmd *)skb->data; +	cmd->tlv_header = +		FIELD_PREP(WMI_TLV_TAG, WMI_TAG_11D_SCAN_START_CMD) | +		FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + +	cmd->vdev_id = param->vdev_id; +	cmd->scan_period_msec = param->scan_period_msec; +	cmd->start_interval_msec = param->start_interval_msec; +	ret = ath11k_wmi_cmd_send(wmi, skb, WMI_11D_SCAN_START_CMDID); + +	ath11k_dbg(ar->ab, ATH11K_DBG_WMI, +		   "send 11d scan start vdev id %d period %d ms internal %d ms\n", +		   cmd->vdev_id, +		   cmd->scan_period_msec, +		   cmd->start_interval_msec); + +	if (ret) { +		ath11k_warn(ar->ab, +			    "failed to send WMI_11D_SCAN_START_CMDID: %d\n", ret); +		dev_kfree_skb(skb); +	} + +	return ret; +} + +int ath11k_wmi_send_11d_scan_stop_cmd(struct ath11k *ar, u32 vdev_id) +{ +	struct ath11k_pdev_wmi *wmi = ar->wmi; +	struct wmi_11d_scan_stop_cmd *cmd; +	struct sk_buff *skb; +	int ret; + +	skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); +	if (!skb) +		return -ENOMEM; + +	cmd = (struct wmi_11d_scan_stop_cmd *)skb->data; +	cmd->tlv_header = +		FIELD_PREP(WMI_TLV_TAG, WMI_TAG_11D_SCAN_STOP_CMD) | +		FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + +	cmd->vdev_id = vdev_id; +	ret = ath11k_wmi_cmd_send(wmi, skb, WMI_11D_SCAN_STOP_CMDID); + +	ath11k_dbg(ar->ab, ATH11K_DBG_WMI, +		   "send 11d scan stop vdev id %d\n", +		   cmd->vdev_id); + +	if (ret) { +		ath11k_warn(ar->ab, +			    "failed to send WMI_11D_SCAN_STOP_CMDID: %d\n", ret); +		dev_kfree_skb(skb); +	} + +	return ret; +} +  int ath11k_wmi_pdev_pktlog_enable(struct ath11k *ar, u32 pktlog_filter)  {  	struct ath11k_pdev_wmi *wmi = ar->wmi; @@ -3428,6 +3595,56 @@ int ath11k_wmi_fils_discovery(struct ath11k *ar, u32 vdev_id, u32 interval,  }  static void +ath11k_wmi_obss_color_collision_event(struct ath11k_base *ab, struct sk_buff *skb) +{ +	const void **tb; +	const struct wmi_obss_color_collision_event *ev; +	struct ath11k_vif *arvif; +	int ret; + +	tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); +	if (IS_ERR(tb)) { +		ret = PTR_ERR(tb); +		ath11k_warn(ab, "failed to parse tlv: %d\n", ret); +		return; +	} + +	rcu_read_lock(); + +	ev = tb[WMI_TAG_OBSS_COLOR_COLLISION_EVT]; +	if (!ev) { +		ath11k_warn(ab, "failed to fetch obss color collision ev"); +		goto exit; +	} + +	arvif = ath11k_mac_get_arvif_by_vdev_id(ab, ev->vdev_id); +	if (!arvif) { +		ath11k_warn(ab, "failed to find arvif with vedv id %d in obss_color_collision_event\n", +			    ev->vdev_id); +		goto exit; +	} + +	switch (ev->evt_type) { +	case WMI_BSS_COLOR_COLLISION_DETECTION: +		ieeee80211_obss_color_collision_notify(arvif->vif, ev->obss_color_bitmap); +		ath11k_dbg(ab, ATH11K_DBG_WMI, +			   "OBSS color collision detected vdev:%d, event:%d, bitmap:%08llx\n", +			   ev->vdev_id, ev->evt_type, ev->obss_color_bitmap); +		break; +	case WMI_BSS_COLOR_COLLISION_DISABLE: +	case WMI_BSS_COLOR_FREE_SLOT_TIMER_EXPIRY: +	case WMI_BSS_COLOR_FREE_SLOT_AVAILABLE: +		break; +	default: +		ath11k_warn(ab, "received unknown obss color collision detection event\n"); +	} + +exit: +	kfree(tb); +	rcu_read_unlock(); +} + +static void  ath11k_fill_band_to_mac_param(struct ath11k_base  *soc,  			      struct wmi_host_pdev_band_to_mac *band_to_mac)  { @@ -4144,6 +4361,7 @@ static int ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse(struct ath11k_base *soc,  	svc_rdy_ext->param.num_phy = svc_rdy_ext->soc_hal_reg_caps->num_phy;  	soc->num_radios = 0; +	soc->target_pdev_count = 0;  	phy_id_map = svc_rdy_ext->pref_hw_mode_caps.phy_id_map;  	while (phy_id_map && soc->num_radios < MAX_RADIOS) { @@ -4781,6 +4999,7 @@ static int wmi_process_mgmt_tx_comp(struct ath11k *ar, u32 desc_id,  	struct sk_buff *msdu;  	struct ieee80211_tx_info *info;  	struct ath11k_skb_cb *skb_cb; +	int num_mgmt;  	spin_lock_bh(&ar->txmgmt_idr_lock);  	msdu = idr_find(&ar->txmgmt_idr, desc_id); @@ -4804,10 +5023,19 @@ static int wmi_process_mgmt_tx_comp(struct ath11k *ar, u32 desc_id,  	ieee80211_tx_status_irqsafe(ar->hw, msdu); +	num_mgmt = atomic_dec_if_positive(&ar->num_pending_mgmt_tx); +  	/* WARN when we received this event without doing any mgmt tx */ -	if (atomic_dec_if_positive(&ar->num_pending_mgmt_tx) < 0) +	if (num_mgmt < 0)  		WARN_ON_ONCE(1); +	ath11k_dbg(ar->ab, ATH11K_DBG_WMI, +		   "wmi mgmt tx comp pending %d desc id %d\n", +		   num_mgmt, desc_id); + +	if (!num_mgmt) +		wake_up(&ar->txmgmt_empty_waitq); +  	return 0;  } @@ -5343,47 +5571,107 @@ ath11k_wmi_pull_bcn_stats(const struct wmi_bcn_stats *src,  	dst->tx_bcn_outage_cnt = src->tx_bcn_outage_cnt;  } -int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, -			     struct ath11k_fw_stats *stats) +static int ath11k_wmi_tlv_rssi_chain_parse(struct ath11k_base *ab, +					   u16 tag, u16 len, +					   const void *ptr, void *data)  { -	const void **tb; -	const struct wmi_stats_event *ev; -	const void *data; -	int i, ret; -	u32 len = skb->len; +	struct wmi_tlv_fw_stats_parse *parse = data; +	const struct wmi_stats_event *ev = parse->ev; +	struct ath11k_fw_stats *stats = parse->stats; +	struct ath11k *ar; +	struct ath11k_vif *arvif; +	struct ieee80211_sta *sta; +	struct ath11k_sta *arsta; +	const struct wmi_rssi_stats *stats_rssi = (const struct wmi_rssi_stats *)ptr; +	int j, ret = 0; -	tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, len, GFP_ATOMIC); -	if (IS_ERR(tb)) { -		ret = PTR_ERR(tb); -		ath11k_warn(ab, "failed to parse tlv: %d\n", ret); -		return ret; +	if (tag != WMI_TAG_RSSI_STATS) +		return -EPROTO; + +	rcu_read_lock(); + +	ar = ath11k_mac_get_ar_by_pdev_id(ab, ev->pdev_id); +	stats->stats_id = WMI_REQUEST_RSSI_PER_CHAIN_STAT; + +	ath11k_dbg(ab, ATH11K_DBG_WMI, +		   "wmi stats vdev id %d mac %pM\n", +		   stats_rssi->vdev_id, stats_rssi->peer_macaddr.addr); + +	arvif = ath11k_mac_get_arvif(ar, stats_rssi->vdev_id); +	if (!arvif) { +		ath11k_warn(ab, "not found vif for vdev id %d\n", +			    stats_rssi->vdev_id); +		ret = -EPROTO; +		goto exit; +	} + +	ath11k_dbg(ab, ATH11K_DBG_WMI, +		   "wmi stats bssid %pM vif %pK\n", +		   arvif->bssid, arvif->vif); + +	sta = ieee80211_find_sta_by_ifaddr(ar->hw, +					   arvif->bssid, +					   NULL); +	if (!sta) { +		ath11k_warn(ab, "not found station for bssid %pM\n", +			    arvif->bssid); +		ret = -EPROTO; +		goto exit; +	} + +	arsta = (struct ath11k_sta *)sta->drv_priv; + +	BUILD_BUG_ON(ARRAY_SIZE(arsta->chain_signal) > +		     ARRAY_SIZE(stats_rssi->rssi_avg_beacon)); + +	for (j = 0; j < ARRAY_SIZE(arsta->chain_signal); j++) { +		arsta->chain_signal[j] = stats_rssi->rssi_avg_beacon[j]; +		ath11k_dbg(ab, ATH11K_DBG_WMI, +			   "wmi stats beacon rssi[%d] %d data rssi[%d] %d\n", +			   j, +			   stats_rssi->rssi_avg_beacon[j], +			   j, +			   stats_rssi->rssi_avg_data[j]);  	} -	ev = tb[WMI_TAG_STATS_EVENT]; -	data = tb[WMI_TAG_ARRAY_BYTE]; -	if (!ev || !data) { +exit: +	rcu_read_unlock(); +	return ret; +} + +static int ath11k_wmi_tlv_fw_stats_data_parse(struct ath11k_base *ab, +					      struct wmi_tlv_fw_stats_parse *parse, +					      const void *ptr, +					      u16 len) +{ +	struct ath11k_fw_stats *stats = parse->stats; +	const struct wmi_stats_event *ev = parse->ev; +	struct ath11k *ar; +	struct ath11k_vif *arvif; +	struct ieee80211_sta *sta; +	struct ath11k_sta *arsta; +	int i, ret = 0; +	const void *data = ptr; + +	if (!ev) {  		ath11k_warn(ab, "failed to fetch update stats ev"); -		kfree(tb);  		return -EPROTO;  	} -	ath11k_dbg(ab, ATH11K_DBG_WMI, -		   "wmi stats update ev pdev_id %d pdev %i vdev %i bcn %i\n", -		   ev->pdev_id, -		   ev->num_pdev_stats, ev->num_vdev_stats, -		   ev->num_bcn_stats); - -	stats->pdev_id = ev->pdev_id;  	stats->stats_id = 0; +	rcu_read_lock(); + +	ar = ath11k_mac_get_ar_by_pdev_id(ab, ev->pdev_id); +  	for (i = 0; i < ev->num_pdev_stats; i++) {  		const struct wmi_pdev_stats *src;  		struct ath11k_fw_stats_pdev *dst;  		src = data;  		if (len < sizeof(*src)) { -			kfree(tb); -			return -EPROTO; +			ret = -EPROTO; +			goto exit;  		}  		stats->stats_id = WMI_REQUEST_PDEV_STAT; @@ -5407,12 +5695,29 @@ int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb,  		src = data;  		if (len < sizeof(*src)) { -			kfree(tb); -			return -EPROTO; +			ret = -EPROTO; +			goto exit;  		}  		stats->stats_id = WMI_REQUEST_VDEV_STAT; +		arvif = ath11k_mac_get_arvif(ar, src->vdev_id); +		if (arvif) { +			sta = ieee80211_find_sta_by_ifaddr(ar->hw, +							   arvif->bssid, +							   NULL); +			if (sta) { +				arsta = (struct ath11k_sta *)sta->drv_priv; +				arsta->rssi_beacon = src->beacon_snr; +				ath11k_dbg(ab, ATH11K_DBG_WMI, +					   "wmi stats vdev id %d snr %d\n", +					   src->vdev_id, src->beacon_snr); +			} else { +				ath11k_warn(ab, "not found station for bssid %pM\n", +					    arvif->bssid); +			} +		} +  		data += sizeof(*src);  		len -= sizeof(*src); @@ -5430,8 +5735,8 @@ int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb,  		src = data;  		if (len < sizeof(*src)) { -			kfree(tb); -			return -EPROTO; +			ret = -EPROTO; +			goto exit;  		}  		stats->stats_id = WMI_REQUEST_BCN_STAT; @@ -5447,8 +5752,67 @@ int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb,  		list_add_tail(&dst->list, &stats->bcn);  	} -	kfree(tb); -	return 0; +exit: +	rcu_read_unlock(); +	return ret; +} + +static int ath11k_wmi_tlv_fw_stats_parse(struct ath11k_base *ab, +					 u16 tag, u16 len, +					 const void *ptr, void *data) +{ +	struct wmi_tlv_fw_stats_parse *parse = data; +	int ret = 0; + +	switch (tag) { +	case WMI_TAG_STATS_EVENT: +		parse->ev = (struct wmi_stats_event *)ptr; +		parse->stats->pdev_id = parse->ev->pdev_id; +		break; +	case WMI_TAG_ARRAY_BYTE: +		ret = ath11k_wmi_tlv_fw_stats_data_parse(ab, parse, ptr, len); +		break; +	case WMI_TAG_PER_CHAIN_RSSI_STATS: +		parse->rssi = (struct wmi_per_chain_rssi_stats *)ptr; + +		if (parse->ev->stats_id & WMI_REQUEST_RSSI_PER_CHAIN_STAT) +			parse->rssi_num = parse->rssi->num_per_chain_rssi_stats; + +		ath11k_dbg(ab, ATH11K_DBG_WMI, +			   "wmi stats id 0x%x num chain %d\n", +			   parse->ev->stats_id, +			   parse->rssi_num); +		break; +	case WMI_TAG_ARRAY_STRUCT: +		if (parse->rssi_num && !parse->chain_rssi_done) { +			ret = ath11k_wmi_tlv_iter(ab, ptr, len, +						  ath11k_wmi_tlv_rssi_chain_parse, +						  parse); +			if (ret) { +				ath11k_warn(ab, "failed to parse rssi chain %d\n", +					    ret); +				return ret; +			} +			parse->chain_rssi_done = true; +		} +		break; +	default: +		break; +	} +	return ret; +} + +int ath11k_wmi_pull_fw_stats(struct ath11k_base *ab, struct sk_buff *skb, +			     struct ath11k_fw_stats *stats) +{ +	struct wmi_tlv_fw_stats_parse parse = { }; + +	stats->stats_id = 0; +	parse.stats = stats; + +	return ath11k_wmi_tlv_iter(ab, skb->data, skb->len, +				   ath11k_wmi_tlv_fw_stats_parse, +				   &parse);  }  size_t ath11k_wmi_fw_stats_num_vdevs(struct list_head *head) @@ -5810,15 +6174,79 @@ static void ath11k_wmi_op_ep_tx_credits(struct ath11k_base *ab)  	wake_up(&ab->wmi_ab.tx_credits_wq);  } +static int ath11k_reg_11d_new_cc_event(struct ath11k_base *ab, struct sk_buff *skb) +{ +	const struct wmi_11d_new_cc_ev *ev; +	const void **tb; +	int ret; + +	tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); +	if (IS_ERR(tb)) { +		ret = PTR_ERR(tb); +		ath11k_warn(ab, "failed to parse tlv: %d\n", ret); +		return ret; +	} + +	ev = tb[WMI_TAG_11D_NEW_COUNTRY_EVENT]; +	if (!ev) { +		kfree(tb); +		ath11k_warn(ab, "failed to fetch 11d new cc ev"); +		return -EPROTO; +	} + +	spin_lock_bh(&ab->base_lock); +	memcpy(&ab->new_alpha2, &ev->new_alpha2, 2); +	spin_unlock_bh(&ab->base_lock); + +	ath11k_dbg(ab, ATH11K_DBG_WMI, "wmi 11d new cc %c%c\n", +		   ab->new_alpha2[0], +		   ab->new_alpha2[1]); + +	kfree(tb); + +	queue_work(ab->workqueue, &ab->update_11d_work); + +	return 0; +} +  static void ath11k_wmi_htc_tx_complete(struct ath11k_base *ab,  				       struct sk_buff *skb)  { +	struct ath11k_pdev_wmi *wmi = NULL; +	u32 i; +	u8 wmi_ep_count; +	u8 eid; + +	eid = ATH11K_SKB_CB(skb)->eid;  	dev_kfree_skb(skb); + +	if (eid >= ATH11K_HTC_EP_COUNT) +		return; + +	wmi_ep_count = ab->htc.wmi_ep_count; +	if (wmi_ep_count > ab->hw_params.max_radios) +		return; + +	for (i = 0; i < ab->htc.wmi_ep_count; i++) { +		if (ab->wmi_ab.wmi[i].eid == eid) { +			wmi = &ab->wmi_ab.wmi[i]; +			break; +		} +	} + +	if (wmi) +		wake_up(&wmi->tx_ce_desc_wq);  }  static bool ath11k_reg_is_world_alpha(char *alpha)  { -	return alpha[0] == '0' && alpha[1] == '0'; +	if (alpha[0] == '0' && alpha[1] == '0') +		return true; + +	if (alpha[0] == 'n' && alpha[1] == 'a') +		return true; + +	return false;  }  static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *skb) @@ -5911,7 +6339,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk  		ar = ab->pdevs[pdev_idx].ar;  		kfree(ab->new_regd[pdev_idx]);  		ab->new_regd[pdev_idx] = regd; -		ieee80211_queue_work(ar->hw, &ar->regd_update_work); +		queue_work(ab->workqueue, &ar->regd_update_work);  	} else {  		/* This regd would be applied during mac registration and is  		 * held constant throughout for regd intersection purpose @@ -6111,6 +6539,7 @@ static void ath11k_vdev_start_resp_event(struct ath11k_base *ab, struct sk_buff  static void ath11k_bcn_tx_status_event(struct ath11k_base *ab, struct sk_buff *skb)  { +	struct ath11k_vif *arvif;  	u32 vdev_id, tx_status;  	if (ath11k_pull_bcn_tx_status_ev(ab, skb->data, skb->len, @@ -6118,6 +6547,17 @@ static void ath11k_bcn_tx_status_event(struct ath11k_base *ab, struct sk_buff *s  		ath11k_warn(ab, "failed to extract bcn tx status");  		return;  	} + +	rcu_read_lock(); +	arvif = ath11k_mac_get_arvif_by_vdev_id(ab, vdev_id); +	if (!arvif) { +		ath11k_warn(ab, "invalid vdev id %d in bcn_tx_status", +			    vdev_id); +		rcu_read_unlock(); +		return; +	} +	ath11k_mac_bcn_tx_event(arvif); +	rcu_read_unlock();  }  static void ath11k_vdev_stopped_event(struct ath11k_base *ab, struct sk_buff *skb) @@ -6398,6 +6838,7 @@ static void ath11k_peer_sta_kickout_event(struct ath11k_base *ab, struct sk_buff  	struct ieee80211_sta *sta;  	struct ath11k_peer *peer;  	struct ath11k *ar; +	u32 vdev_id;  	if (ath11k_pull_peer_sta_kickout_ev(ab, skb, &arg) != 0) {  		ath11k_warn(ab, "failed to extract peer sta kickout event"); @@ -6413,10 +6854,15 @@ static void ath11k_peer_sta_kickout_event(struct ath11k_base *ab, struct sk_buff  	if (!peer) {  		ath11k_warn(ab, "peer not found %pM\n",  			    arg.mac_addr); +		spin_unlock_bh(&ab->base_lock);  		goto exit;  	} -	ar = ath11k_mac_get_ar_by_vdev_id(ab, peer->vdev_id); +	vdev_id = peer->vdev_id; + +	spin_unlock_bh(&ab->base_lock); + +	ar = ath11k_mac_get_ar_by_vdev_id(ab, vdev_id);  	if (!ar) {  		ath11k_warn(ab, "invalid vdev id in peer sta kickout ev %d",  			    peer->vdev_id); @@ -6437,7 +6883,6 @@ static void ath11k_peer_sta_kickout_event(struct ath11k_base *ab, struct sk_buff  	ieee80211_report_low_ack(sta, 10);  exit: -	spin_unlock_bh(&ab->base_lock);  	rcu_read_unlock();  } @@ -6893,6 +7338,40 @@ exit:  	kfree(tb);  } +static void ath11k_rfkill_state_change_event(struct ath11k_base *ab, +					     struct sk_buff *skb) +{ +	const struct wmi_rfkill_state_change_ev *ev; +	const void **tb; +	int ret; + +	tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); +	if (IS_ERR(tb)) { +		ret = PTR_ERR(tb); +		ath11k_warn(ab, "failed to parse tlv: %d\n", ret); +		return; +	} + +	ev = tb[WMI_TAG_RFKILL_EVENT]; +	if (!ev) { +		kfree(tb); +		return; +	} + +	ath11k_dbg(ab, ATH11K_DBG_MAC, +		   "wmi tlv rfkill state change gpio %d type %d radio_state %d\n", +		   ev->gpio_pin_num, +		   ev->int_type, +		   ev->radio_state); + +	spin_lock_bh(&ab->base_lock); +	ab->rfkill_radio_on = (ev->radio_state == WMI_RFKILL_RADIO_STATE_ON); +	spin_unlock_bh(&ab->base_lock); + +	queue_work(ab->workqueue, &ab->rfkill_work); +	kfree(tb); +} +  static void  ath11k_wmi_pdev_temperature_event(struct ath11k_base *ab,  				  struct sk_buff *skb) @@ -7046,6 +7525,13 @@ static void ath11k_wmi_event_wow_wakeup_host(struct ath11k_base *ab, struct sk_b  	complete(&ab->wow.wakeup_completed);  } +static void +ath11k_wmi_diag_event(struct ath11k_base *ab, +		      struct sk_buff *skb) +{ +	trace_ath11k_wmi_diag(ab, skb->data, skb->len); +} +  static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)  {  	struct wmi_cmd_hdr *cmd_hdr; @@ -7054,6 +7540,8 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)  	cmd_hdr = (struct wmi_cmd_hdr *)skb->data;  	id = FIELD_GET(WMI_CMD_HDR_CMD_ID, (cmd_hdr->cmd_id)); +	trace_ath11k_wmi_event(ab, id, skb->data, skb->len); +  	if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL)  		goto out; @@ -7138,6 +7626,9 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)  	case WMI_OFFLOAD_PROB_RESP_TX_STATUS_EVENTID:  		ath11k_probe_resp_tx_status_event(ab, skb);  		break; +	case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID: +		ath11k_wmi_obss_color_collision_event(ab, skb); +		break;  	/* add Unsupported events here */  	case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID:  	case WMI_PEER_OPER_MODE_CHANGE_EVENTID: @@ -7157,6 +7648,15 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)  	case WMI_WOW_WAKEUP_HOST_EVENTID:  		ath11k_wmi_event_wow_wakeup_host(ab, skb);  		break; +	case WMI_11D_NEW_COUNTRY_EVENTID: +		ath11k_reg_11d_new_cc_event(ab, skb); +		break; +	case WMI_RFKILL_STATE_CHANGE_EVENTID: +		ath11k_rfkill_state_change_event(ab, skb); +		break; +	case WMI_DIAG_EVENTID: +		ath11k_wmi_diag_event(ab, skb); +		break;  	/* TODO: Add remaining events */  	default:  		ath11k_dbg(ab, ATH11K_DBG_WMI, "Unknown eventid: 0x%x\n", id); @@ -7199,6 +7699,7 @@ static int ath11k_connect_pdev_htc_service(struct ath11k_base *ab,  	ab->wmi_ab.wmi_endpoint_id[pdev_idx] = conn_resp.eid;  	ab->wmi_ab.wmi[pdev_idx].eid = conn_resp.eid;  	ab->wmi_ab.max_msg_len[pdev_idx] = conn_resp.max_msg_len; +	init_waitqueue_head(&ab->wmi_ab.wmi[pdev_idx].tx_ce_desc_wq);  	return 0;  } @@ -7414,3 +7915,31 @@ int ath11k_wmi_wow_enable(struct ath11k *ar)  	return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_WOW_ENABLE_CMDID);  } + +int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar, +				 const u8 mac_addr[ETH_ALEN]) +{ +	struct sk_buff *skb; +	struct wmi_scan_prob_req_oui_cmd *cmd; +	u32 prob_req_oui; +	int len; + +	prob_req_oui = (((u32)mac_addr[0]) << 16) | +		       (((u32)mac_addr[1]) << 8) | mac_addr[2]; + +	len = sizeof(*cmd); +	skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len); +	if (!skb) +		return -ENOMEM; + +	cmd = (struct wmi_scan_prob_req_oui_cmd *)skb->data; +	cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, +				     WMI_TAG_SCAN_PROB_REQ_OUI_CMD) | +			  FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); +	cmd->prob_req_oui = prob_req_oui; + +	ath11k_dbg(ar->ab, ATH11K_DBG_WMI, "wmi scan prob req oui %d\n", +		   prob_req_oui); + +	return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_SCAN_PROB_REQ_OUI_CMDID); +} |