xref: /openbmc/linux/drivers/net/wireless/quantenna/qtnfmac/event.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
1ff233cb5SSergey Matyukevich // SPDX-License-Identifier: GPL-2.0+
2ff233cb5SSergey Matyukevich /* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */
398f44cb0SIgor Mitsyanko 
498f44cb0SIgor Mitsyanko #include <linux/kernel.h>
598f44cb0SIgor Mitsyanko #include <linux/module.h>
698f44cb0SIgor Mitsyanko #include <linux/slab.h>
7946d077aSSergey Matyukevich #include <linux/nospec.h>
898f44cb0SIgor Mitsyanko 
998f44cb0SIgor Mitsyanko #include "cfg80211.h"
1098f44cb0SIgor Mitsyanko #include "core.h"
1198f44cb0SIgor Mitsyanko #include "qlink.h"
1298f44cb0SIgor Mitsyanko #include "bus.h"
1398f44cb0SIgor Mitsyanko #include "trans.h"
1498f44cb0SIgor Mitsyanko #include "util.h"
1598f44cb0SIgor Mitsyanko #include "event.h"
16fac7f9bfSIgor Mitsyanko #include "qlink_util.h"
1798f44cb0SIgor Mitsyanko 
1898f44cb0SIgor Mitsyanko static int
qtnf_event_handle_sta_assoc(struct qtnf_wmac * mac,struct qtnf_vif * vif,const struct qlink_event_sta_assoc * sta_assoc,u16 len)1998f44cb0SIgor Mitsyanko qtnf_event_handle_sta_assoc(struct qtnf_wmac *mac, struct qtnf_vif *vif,
2098f44cb0SIgor Mitsyanko 			    const struct qlink_event_sta_assoc *sta_assoc,
2198f44cb0SIgor Mitsyanko 			    u16 len)
2298f44cb0SIgor Mitsyanko {
2398f44cb0SIgor Mitsyanko 	const u8 *sta_addr;
2498f44cb0SIgor Mitsyanko 	u16 frame_control;
2541bd3d58SToke Høiland-Jørgensen 	struct station_info *sinfo;
2698f44cb0SIgor Mitsyanko 	size_t payload_len;
2798f44cb0SIgor Mitsyanko 	u16 tlv_type;
2898f44cb0SIgor Mitsyanko 	u16 tlv_value_len;
2998f44cb0SIgor Mitsyanko 	const struct qlink_tlv_hdr *tlv;
3041bd3d58SToke Høiland-Jørgensen 	int ret = 0;
3198f44cb0SIgor Mitsyanko 
3298f44cb0SIgor Mitsyanko 	if (unlikely(len < sizeof(*sta_assoc))) {
3398f44cb0SIgor Mitsyanko 		pr_err("VIF%u.%u: payload is too short (%u < %zu)\n",
3498f44cb0SIgor Mitsyanko 		       mac->macid, vif->vifid, len, sizeof(*sta_assoc));
3598f44cb0SIgor Mitsyanko 		return -EINVAL;
3698f44cb0SIgor Mitsyanko 	}
3798f44cb0SIgor Mitsyanko 
3898f44cb0SIgor Mitsyanko 	if (vif->wdev.iftype != NL80211_IFTYPE_AP) {
3998f44cb0SIgor Mitsyanko 		pr_err("VIF%u.%u: STA_ASSOC event when not in AP mode\n",
4098f44cb0SIgor Mitsyanko 		       mac->macid, vif->vifid);
4198f44cb0SIgor Mitsyanko 		return -EPROTO;
4298f44cb0SIgor Mitsyanko 	}
4398f44cb0SIgor Mitsyanko 
4441bd3d58SToke Høiland-Jørgensen 	sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
4541bd3d58SToke Høiland-Jørgensen 	if (!sinfo)
4641bd3d58SToke Høiland-Jørgensen 		return -ENOMEM;
4741bd3d58SToke Høiland-Jørgensen 
4898f44cb0SIgor Mitsyanko 	sta_addr = sta_assoc->sta_addr;
4998f44cb0SIgor Mitsyanko 	frame_control = le16_to_cpu(sta_assoc->frame_control);
5098f44cb0SIgor Mitsyanko 
5198f44cb0SIgor Mitsyanko 	pr_debug("VIF%u.%u: MAC:%pM FC:%x\n", mac->macid, vif->vifid, sta_addr,
5298f44cb0SIgor Mitsyanko 		 frame_control);
5398f44cb0SIgor Mitsyanko 
547a4d3a3bSIgor Mitsyanko 	qtnf_sta_list_add(vif, sta_addr);
5598f44cb0SIgor Mitsyanko 
5641bd3d58SToke Høiland-Jørgensen 	sinfo->assoc_req_ies = NULL;
5741bd3d58SToke Høiland-Jørgensen 	sinfo->assoc_req_ies_len = 0;
5841bd3d58SToke Høiland-Jørgensen 	sinfo->generation = vif->generation;
5998f44cb0SIgor Mitsyanko 
6098f44cb0SIgor Mitsyanko 	payload_len = len - sizeof(*sta_assoc);
6198f44cb0SIgor Mitsyanko 
628b0b5f1bSIgor Mitsyanko 	qlink_for_each_tlv(tlv, sta_assoc->ies, payload_len) {
6398f44cb0SIgor Mitsyanko 		tlv_type = le16_to_cpu(tlv->type);
6498f44cb0SIgor Mitsyanko 		tlv_value_len = le16_to_cpu(tlv->len);
6598f44cb0SIgor Mitsyanko 
6698f44cb0SIgor Mitsyanko 		if (tlv_type == QTN_TLV_ID_IE_SET) {
6718b7470fSIgor Mitsyanko 			const struct qlink_tlv_ie_set *ie_set;
6818b7470fSIgor Mitsyanko 			unsigned int ie_len;
6918b7470fSIgor Mitsyanko 
708b0b5f1bSIgor Mitsyanko 			if (tlv_value_len <
718b0b5f1bSIgor Mitsyanko 			    (sizeof(*ie_set) - sizeof(ie_set->hdr))) {
7241bd3d58SToke Høiland-Jørgensen 				ret = -EINVAL;
7341bd3d58SToke Høiland-Jørgensen 				goto out;
7441bd3d58SToke Høiland-Jørgensen 			}
7518b7470fSIgor Mitsyanko 
7618b7470fSIgor Mitsyanko 			ie_set = (const struct qlink_tlv_ie_set *)tlv;
7718b7470fSIgor Mitsyanko 			ie_len = tlv_value_len -
7818b7470fSIgor Mitsyanko 				(sizeof(*ie_set) - sizeof(ie_set->hdr));
7918b7470fSIgor Mitsyanko 
8018b7470fSIgor Mitsyanko 			if (ie_set->type == QLINK_IE_SET_ASSOC_REQ && ie_len) {
8141bd3d58SToke Høiland-Jørgensen 				sinfo->assoc_req_ies = ie_set->ie_data;
8241bd3d58SToke Høiland-Jørgensen 				sinfo->assoc_req_ies_len = ie_len;
8318b7470fSIgor Mitsyanko 			}
8498f44cb0SIgor Mitsyanko 		}
8598f44cb0SIgor Mitsyanko 	}
8698f44cb0SIgor Mitsyanko 
878b0b5f1bSIgor Mitsyanko 	if (!qlink_tlv_parsing_ok(tlv, sta_assoc->ies, payload_len)) {
888b0b5f1bSIgor Mitsyanko 		pr_err("Malformed TLV buffer\n");
8941bd3d58SToke Høiland-Jørgensen 		ret = -EINVAL;
9041bd3d58SToke Høiland-Jørgensen 		goto out;
9141bd3d58SToke Høiland-Jørgensen 	}
9298f44cb0SIgor Mitsyanko 
9341bd3d58SToke Høiland-Jørgensen 	cfg80211_new_sta(vif->netdev, sta_assoc->sta_addr, sinfo,
9498f44cb0SIgor Mitsyanko 			 GFP_KERNEL);
9598f44cb0SIgor Mitsyanko 
9641bd3d58SToke Høiland-Jørgensen out:
9741bd3d58SToke Høiland-Jørgensen 	kfree(sinfo);
9841bd3d58SToke Høiland-Jørgensen 	return ret;
9998f44cb0SIgor Mitsyanko }
10098f44cb0SIgor Mitsyanko 
10198f44cb0SIgor Mitsyanko static int
qtnf_event_handle_sta_deauth(struct qtnf_wmac * mac,struct qtnf_vif * vif,const struct qlink_event_sta_deauth * sta_deauth,u16 len)10298f44cb0SIgor Mitsyanko qtnf_event_handle_sta_deauth(struct qtnf_wmac *mac, struct qtnf_vif *vif,
10398f44cb0SIgor Mitsyanko 			     const struct qlink_event_sta_deauth *sta_deauth,
10498f44cb0SIgor Mitsyanko 			     u16 len)
10598f44cb0SIgor Mitsyanko {
10698f44cb0SIgor Mitsyanko 	const u8 *sta_addr;
10798f44cb0SIgor Mitsyanko 	u16 reason;
10898f44cb0SIgor Mitsyanko 
10998f44cb0SIgor Mitsyanko 	if (unlikely(len < sizeof(*sta_deauth))) {
11098f44cb0SIgor Mitsyanko 		pr_err("VIF%u.%u: payload is too short (%u < %zu)\n",
11198f44cb0SIgor Mitsyanko 		       mac->macid, vif->vifid, len,
11298f44cb0SIgor Mitsyanko 		       sizeof(struct qlink_event_sta_deauth));
11398f44cb0SIgor Mitsyanko 		return -EINVAL;
11498f44cb0SIgor Mitsyanko 	}
11598f44cb0SIgor Mitsyanko 
11698f44cb0SIgor Mitsyanko 	if (vif->wdev.iftype != NL80211_IFTYPE_AP) {
11798f44cb0SIgor Mitsyanko 		pr_err("VIF%u.%u: STA_DEAUTH event when not in AP mode\n",
11898f44cb0SIgor Mitsyanko 		       mac->macid, vif->vifid);
11998f44cb0SIgor Mitsyanko 		return -EPROTO;
12098f44cb0SIgor Mitsyanko 	}
12198f44cb0SIgor Mitsyanko 
12298f44cb0SIgor Mitsyanko 	sta_addr = sta_deauth->sta_addr;
12398f44cb0SIgor Mitsyanko 	reason = le16_to_cpu(sta_deauth->reason);
12498f44cb0SIgor Mitsyanko 
12598f44cb0SIgor Mitsyanko 	pr_debug("VIF%u.%u: MAC:%pM reason:%x\n", mac->macid, vif->vifid,
12698f44cb0SIgor Mitsyanko 		 sta_addr, reason);
12798f44cb0SIgor Mitsyanko 
1287a4d3a3bSIgor Mitsyanko 	if (qtnf_sta_list_del(vif, sta_addr))
12998f44cb0SIgor Mitsyanko 		cfg80211_del_sta(vif->netdev, sta_deauth->sta_addr,
13098f44cb0SIgor Mitsyanko 				 GFP_KERNEL);
13198f44cb0SIgor Mitsyanko 
13298f44cb0SIgor Mitsyanko 	return 0;
13398f44cb0SIgor Mitsyanko }
13498f44cb0SIgor Mitsyanko 
13598f44cb0SIgor Mitsyanko static int
qtnf_event_handle_bss_join(struct qtnf_vif * vif,const struct qlink_event_bss_join * join_info,u16 len)13698f44cb0SIgor Mitsyanko qtnf_event_handle_bss_join(struct qtnf_vif *vif,
13798f44cb0SIgor Mitsyanko 			   const struct qlink_event_bss_join *join_info,
13898f44cb0SIgor Mitsyanko 			   u16 len)
13998f44cb0SIgor Mitsyanko {
14023781af7SSergey Matyukevich 	struct wiphy *wiphy = priv_to_wiphy(vif->mac);
14123781af7SSergey Matyukevich 	enum ieee80211_statuscode status = le16_to_cpu(join_info->status);
14223781af7SSergey Matyukevich 	struct cfg80211_chan_def chandef;
14323781af7SSergey Matyukevich 	struct cfg80211_bss *bss = NULL;
14423781af7SSergey Matyukevich 	u8 *ie = NULL;
145524d6323SSergey Matyukevich 	size_t payload_len;
146524d6323SSergey Matyukevich 	u16 tlv_type;
147524d6323SSergey Matyukevich 	u16 tlv_value_len;
148524d6323SSergey Matyukevich 	const struct qlink_tlv_hdr *tlv;
149524d6323SSergey Matyukevich 	const u8 *rsp_ies = NULL;
150524d6323SSergey Matyukevich 	size_t rsp_ies_len = 0;
15123781af7SSergey Matyukevich 
15298f44cb0SIgor Mitsyanko 	if (unlikely(len < sizeof(*join_info))) {
15398f44cb0SIgor Mitsyanko 		pr_err("VIF%u.%u: payload is too short (%u < %zu)\n",
15498f44cb0SIgor Mitsyanko 		       vif->mac->macid, vif->vifid, len,
15598f44cb0SIgor Mitsyanko 		       sizeof(struct qlink_event_bss_join));
15698f44cb0SIgor Mitsyanko 		return -EINVAL;
15798f44cb0SIgor Mitsyanko 	}
15898f44cb0SIgor Mitsyanko 
15998f44cb0SIgor Mitsyanko 	if (vif->wdev.iftype != NL80211_IFTYPE_STATION) {
16098f44cb0SIgor Mitsyanko 		pr_err("VIF%u.%u: BSS_JOIN event when not in STA mode\n",
16198f44cb0SIgor Mitsyanko 		       vif->mac->macid, vif->vifid);
16298f44cb0SIgor Mitsyanko 		return -EPROTO;
16398f44cb0SIgor Mitsyanko 	}
16498f44cb0SIgor Mitsyanko 
16524227a9eSSergey Matyukevich 	pr_debug("VIF%u.%u: BSSID:%pM chan:%u status:%u\n",
16624227a9eSSergey Matyukevich 		 vif->mac->macid, vif->vifid, join_info->bssid,
16724227a9eSSergey Matyukevich 		 le16_to_cpu(join_info->chan.chan.center_freq), status);
16823781af7SSergey Matyukevich 
169524d6323SSergey Matyukevich 	if (status != WLAN_STATUS_SUCCESS)
170524d6323SSergey Matyukevich 		goto done;
171524d6323SSergey Matyukevich 
17223781af7SSergey Matyukevich 	qlink_chandef_q2cfg(wiphy, &join_info->chan, &chandef);
17323781af7SSergey Matyukevich 	if (!cfg80211_chandef_valid(&chandef)) {
17423781af7SSergey Matyukevich 		pr_warn("MAC%u.%u: bad channel freq=%u cf1=%u cf2=%u bw=%u\n",
17523781af7SSergey Matyukevich 			vif->mac->macid, vif->vifid,
17624227a9eSSergey Matyukevich 			chandef.chan ? chandef.chan->center_freq : 0,
17723781af7SSergey Matyukevich 			chandef.center_freq1,
17823781af7SSergey Matyukevich 			chandef.center_freq2,
17923781af7SSergey Matyukevich 			chandef.width);
18023781af7SSergey Matyukevich 		status = WLAN_STATUS_UNSPECIFIED_FAILURE;
18123781af7SSergey Matyukevich 		goto done;
18223781af7SSergey Matyukevich 	}
18323781af7SSergey Matyukevich 
18423781af7SSergey Matyukevich 	bss = cfg80211_get_bss(wiphy, chandef.chan, join_info->bssid,
18523781af7SSergey Matyukevich 			       NULL, 0, IEEE80211_BSS_TYPE_ESS,
18623781af7SSergey Matyukevich 			       IEEE80211_PRIVACY_ANY);
18723781af7SSergey Matyukevich 	if (!bss) {
18823781af7SSergey Matyukevich 		pr_warn("VIF%u.%u: add missing BSS:%pM chan:%u\n",
18923781af7SSergey Matyukevich 			vif->mac->macid, vif->vifid,
19023781af7SSergey Matyukevich 			join_info->bssid, chandef.chan->hw_value);
19123781af7SSergey Matyukevich 
1927b0a0e3cSJohannes Berg 		if (!vif->wdev.u.client.ssid_len) {
19323781af7SSergey Matyukevich 			pr_warn("VIF%u.%u: SSID unknown for BSS:%pM\n",
19423781af7SSergey Matyukevich 				vif->mac->macid, vif->vifid,
19598f44cb0SIgor Mitsyanko 				join_info->bssid);
19623781af7SSergey Matyukevich 			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
19723781af7SSergey Matyukevich 			goto done;
19823781af7SSergey Matyukevich 		}
19998f44cb0SIgor Mitsyanko 
2007b0a0e3cSJohannes Berg 		ie = kzalloc(2 + vif->wdev.u.client.ssid_len, GFP_KERNEL);
20123781af7SSergey Matyukevich 		if (!ie) {
20223781af7SSergey Matyukevich 			pr_warn("VIF%u.%u: IE alloc failed for BSS:%pM\n",
20323781af7SSergey Matyukevich 				vif->mac->macid, vif->vifid,
20423781af7SSergey Matyukevich 				join_info->bssid);
20523781af7SSergey Matyukevich 			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
20623781af7SSergey Matyukevich 			goto done;
20723781af7SSergey Matyukevich 		}
20823781af7SSergey Matyukevich 
20923781af7SSergey Matyukevich 		ie[0] = WLAN_EID_SSID;
2107b0a0e3cSJohannes Berg 		ie[1] = vif->wdev.u.client.ssid_len;
2117b0a0e3cSJohannes Berg 		memcpy(ie + 2, vif->wdev.u.client.ssid,
2127b0a0e3cSJohannes Berg 		       vif->wdev.u.client.ssid_len);
21323781af7SSergey Matyukevich 
21423781af7SSergey Matyukevich 		bss = cfg80211_inform_bss(wiphy, chandef.chan,
21523781af7SSergey Matyukevich 					  CFG80211_BSS_FTYPE_UNKNOWN,
21623781af7SSergey Matyukevich 					  join_info->bssid, 0,
21723781af7SSergey Matyukevich 					  WLAN_CAPABILITY_ESS, 100,
2187b0a0e3cSJohannes Berg 					  ie, 2 + vif->wdev.u.client.ssid_len,
21923781af7SSergey Matyukevich 					  0, GFP_KERNEL);
22023781af7SSergey Matyukevich 		if (!bss) {
22123781af7SSergey Matyukevich 			pr_warn("VIF%u.%u: can't connect to unknown BSS: %pM\n",
22223781af7SSergey Matyukevich 				vif->mac->macid, vif->vifid,
22323781af7SSergey Matyukevich 				join_info->bssid);
22423781af7SSergey Matyukevich 			status = WLAN_STATUS_UNSPECIFIED_FAILURE;
22523781af7SSergey Matyukevich 			goto done;
22623781af7SSergey Matyukevich 		}
22723781af7SSergey Matyukevich 	}
228524d6323SSergey Matyukevich 
229524d6323SSergey Matyukevich 	payload_len = len - sizeof(*join_info);
230524d6323SSergey Matyukevich 
2318b0b5f1bSIgor Mitsyanko 	qlink_for_each_tlv(tlv, join_info->ies, payload_len) {
232524d6323SSergey Matyukevich 		tlv_type = le16_to_cpu(tlv->type);
233524d6323SSergey Matyukevich 		tlv_value_len = le16_to_cpu(tlv->len);
23423781af7SSergey Matyukevich 
235524d6323SSergey Matyukevich 		if (tlv_type == QTN_TLV_ID_IE_SET) {
236524d6323SSergey Matyukevich 			const struct qlink_tlv_ie_set *ie_set;
237524d6323SSergey Matyukevich 			unsigned int ie_len;
238524d6323SSergey Matyukevich 
2398b0b5f1bSIgor Mitsyanko 			if (tlv_value_len <
2408b0b5f1bSIgor Mitsyanko 			    (sizeof(*ie_set) - sizeof(ie_set->hdr))) {
241524d6323SSergey Matyukevich 				pr_warn("invalid IE_SET TLV\n");
242524d6323SSergey Matyukevich 				status = WLAN_STATUS_UNSPECIFIED_FAILURE;
243524d6323SSergey Matyukevich 				goto done;
244524d6323SSergey Matyukevich 			}
245524d6323SSergey Matyukevich 
246524d6323SSergey Matyukevich 			ie_set = (const struct qlink_tlv_ie_set *)tlv;
247524d6323SSergey Matyukevich 			ie_len = tlv_value_len -
248524d6323SSergey Matyukevich 				(sizeof(*ie_set) - sizeof(ie_set->hdr));
249524d6323SSergey Matyukevich 
250524d6323SSergey Matyukevich 			switch (ie_set->type) {
251524d6323SSergey Matyukevich 			case QLINK_IE_SET_ASSOC_RESP:
252524d6323SSergey Matyukevich 				if (ie_len) {
253524d6323SSergey Matyukevich 					rsp_ies = ie_set->ie_data;
254524d6323SSergey Matyukevich 					rsp_ies_len = ie_len;
255524d6323SSergey Matyukevich 				}
256524d6323SSergey Matyukevich 				break;
257524d6323SSergey Matyukevich 			default:
258524d6323SSergey Matyukevich 				pr_warn("unexpected IE type: %u\n",
259524d6323SSergey Matyukevich 					ie_set->type);
260524d6323SSergey Matyukevich 				break;
261524d6323SSergey Matyukevich 			}
262524d6323SSergey Matyukevich 		}
263524d6323SSergey Matyukevich 	}
264524d6323SSergey Matyukevich 
2658b0b5f1bSIgor Mitsyanko 	if (!qlink_tlv_parsing_ok(tlv, join_info->ies, payload_len))
2668b0b5f1bSIgor Mitsyanko 		pr_warn("Malformed TLV buffer\n");
26723781af7SSergey Matyukevich done:
268524d6323SSergey Matyukevich 	cfg80211_connect_result(vif->netdev, join_info->bssid, NULL, 0, rsp_ies,
269524d6323SSergey Matyukevich 				rsp_ies_len, status, GFP_KERNEL);
27023781af7SSergey Matyukevich 	if (bss) {
27123781af7SSergey Matyukevich 		if (!ether_addr_equal(vif->bssid, join_info->bssid))
27223781af7SSergey Matyukevich 			ether_addr_copy(vif->bssid, join_info->bssid);
27323781af7SSergey Matyukevich 		cfg80211_put_bss(wiphy, bss);
27423781af7SSergey Matyukevich 	}
27598f44cb0SIgor Mitsyanko 
27623781af7SSergey Matyukevich 	if (status == WLAN_STATUS_SUCCESS)
27798f44cb0SIgor Mitsyanko 		netif_carrier_on(vif->netdev);
27898f44cb0SIgor Mitsyanko 
27923781af7SSergey Matyukevich 	kfree(ie);
28098f44cb0SIgor Mitsyanko 	return 0;
28198f44cb0SIgor Mitsyanko }
28298f44cb0SIgor Mitsyanko 
28398f44cb0SIgor Mitsyanko static int
qtnf_event_handle_bss_leave(struct qtnf_vif * vif,const struct qlink_event_bss_leave * leave_info,u16 len)28498f44cb0SIgor Mitsyanko qtnf_event_handle_bss_leave(struct qtnf_vif *vif,
28598f44cb0SIgor Mitsyanko 			    const struct qlink_event_bss_leave *leave_info,
28698f44cb0SIgor Mitsyanko 			    u16 len)
28798f44cb0SIgor Mitsyanko {
28898f44cb0SIgor Mitsyanko 	if (unlikely(len < sizeof(*leave_info))) {
28998f44cb0SIgor Mitsyanko 		pr_err("VIF%u.%u: payload is too short (%u < %zu)\n",
29098f44cb0SIgor Mitsyanko 		       vif->mac->macid, vif->vifid, len,
29198f44cb0SIgor Mitsyanko 		       sizeof(struct qlink_event_bss_leave));
29298f44cb0SIgor Mitsyanko 		return -EINVAL;
29398f44cb0SIgor Mitsyanko 	}
29498f44cb0SIgor Mitsyanko 
29598f44cb0SIgor Mitsyanko 	if (vif->wdev.iftype != NL80211_IFTYPE_STATION) {
29698f44cb0SIgor Mitsyanko 		pr_err("VIF%u.%u: BSS_LEAVE event when not in STA mode\n",
29798f44cb0SIgor Mitsyanko 		       vif->mac->macid, vif->vifid);
29898f44cb0SIgor Mitsyanko 		return -EPROTO;
29998f44cb0SIgor Mitsyanko 	}
30098f44cb0SIgor Mitsyanko 
30198f44cb0SIgor Mitsyanko 	pr_debug("VIF%u.%u: disconnected\n", vif->mac->macid, vif->vifid);
30298f44cb0SIgor Mitsyanko 
303fd19ecebSSergey Matyukevich 	cfg80211_disconnected(vif->netdev, le16_to_cpu(leave_info->reason),
304fd19ecebSSergey Matyukevich 			      NULL, 0, 0, GFP_KERNEL);
30598f44cb0SIgor Mitsyanko 	netif_carrier_off(vif->netdev);
30698f44cb0SIgor Mitsyanko 
30798f44cb0SIgor Mitsyanko 	return 0;
30898f44cb0SIgor Mitsyanko }
30998f44cb0SIgor Mitsyanko 
31098f44cb0SIgor Mitsyanko static int
qtnf_event_handle_mgmt_received(struct qtnf_vif * vif,const struct qlink_event_rxmgmt * rxmgmt,u16 len)31198f44cb0SIgor Mitsyanko qtnf_event_handle_mgmt_received(struct qtnf_vif *vif,
31298f44cb0SIgor Mitsyanko 				const struct qlink_event_rxmgmt *rxmgmt,
31398f44cb0SIgor Mitsyanko 				u16 len)
31498f44cb0SIgor Mitsyanko {
31598f44cb0SIgor Mitsyanko 	const size_t min_len = sizeof(*rxmgmt) +
31698f44cb0SIgor Mitsyanko 			       sizeof(struct ieee80211_hdr_3addr);
31798f44cb0SIgor Mitsyanko 	const struct ieee80211_hdr_3addr *frame = (void *)rxmgmt->frame_data;
31898f44cb0SIgor Mitsyanko 	const u16 frame_len = len - sizeof(*rxmgmt);
31998f44cb0SIgor Mitsyanko 	enum nl80211_rxmgmt_flags flags = 0;
32098f44cb0SIgor Mitsyanko 
32198f44cb0SIgor Mitsyanko 	if (unlikely(len < min_len)) {
32298f44cb0SIgor Mitsyanko 		pr_err("VIF%u.%u: payload is too short (%u < %zu)\n",
32398f44cb0SIgor Mitsyanko 		       vif->mac->macid, vif->vifid, len, min_len);
32498f44cb0SIgor Mitsyanko 		return -EINVAL;
32598f44cb0SIgor Mitsyanko 	}
32698f44cb0SIgor Mitsyanko 
32798f44cb0SIgor Mitsyanko 	if (le32_to_cpu(rxmgmt->flags) & QLINK_RXMGMT_FLAG_ANSWERED)
32898f44cb0SIgor Mitsyanko 		flags |= NL80211_RXMGMT_FLAG_ANSWERED;
32998f44cb0SIgor Mitsyanko 
33098f44cb0SIgor Mitsyanko 	pr_debug("%s LEN:%u FC:%.4X SA:%pM\n", vif->netdev->name, frame_len,
33198f44cb0SIgor Mitsyanko 		 le16_to_cpu(frame->frame_control), frame->addr2);
33298f44cb0SIgor Mitsyanko 
333fbad963aSSergey Matyukevich 	cfg80211_rx_mgmt(&vif->wdev, le32_to_cpu(rxmgmt->freq), rxmgmt->sig_dbm,
334fbad963aSSergey Matyukevich 			 rxmgmt->frame_data, frame_len, flags);
33598f44cb0SIgor Mitsyanko 
33698f44cb0SIgor Mitsyanko 	return 0;
33798f44cb0SIgor Mitsyanko }
33898f44cb0SIgor Mitsyanko 
33998f44cb0SIgor Mitsyanko static int
qtnf_event_handle_scan_results(struct qtnf_vif * vif,const struct qlink_event_scan_result * sr,u16 len)34098f44cb0SIgor Mitsyanko qtnf_event_handle_scan_results(struct qtnf_vif *vif,
34198f44cb0SIgor Mitsyanko 			       const struct qlink_event_scan_result *sr,
34298f44cb0SIgor Mitsyanko 			       u16 len)
34398f44cb0SIgor Mitsyanko {
34498f44cb0SIgor Mitsyanko 	struct cfg80211_bss *bss;
34598f44cb0SIgor Mitsyanko 	struct ieee80211_channel *channel;
34698f44cb0SIgor Mitsyanko 	struct wiphy *wiphy = priv_to_wiphy(vif->mac);
3475face518SIgor Mitsyanko 	enum cfg80211_bss_frame_type frame_type = CFG80211_BSS_FTYPE_UNKNOWN;
34898f44cb0SIgor Mitsyanko 	size_t payload_len;
34998f44cb0SIgor Mitsyanko 	u16 tlv_type;
35098f44cb0SIgor Mitsyanko 	u16 tlv_value_len;
35198f44cb0SIgor Mitsyanko 	const struct qlink_tlv_hdr *tlv;
35298f44cb0SIgor Mitsyanko 	const u8 *ies = NULL;
35398f44cb0SIgor Mitsyanko 	size_t ies_len = 0;
35498f44cb0SIgor Mitsyanko 
35598f44cb0SIgor Mitsyanko 	if (len < sizeof(*sr)) {
35698f44cb0SIgor Mitsyanko 		pr_err("VIF%u.%u: payload is too short\n", vif->mac->macid,
35798f44cb0SIgor Mitsyanko 		       vif->vifid);
35898f44cb0SIgor Mitsyanko 		return -EINVAL;
35998f44cb0SIgor Mitsyanko 	}
36098f44cb0SIgor Mitsyanko 
36198f44cb0SIgor Mitsyanko 	channel = ieee80211_get_channel(wiphy, le16_to_cpu(sr->freq));
36298f44cb0SIgor Mitsyanko 	if (!channel) {
36398f44cb0SIgor Mitsyanko 		pr_err("VIF%u.%u: channel at %u MHz not found\n",
36498f44cb0SIgor Mitsyanko 		       vif->mac->macid, vif->vifid, le16_to_cpu(sr->freq));
36598f44cb0SIgor Mitsyanko 		return -EINVAL;
36698f44cb0SIgor Mitsyanko 	}
36798f44cb0SIgor Mitsyanko 
36898f44cb0SIgor Mitsyanko 	payload_len = len - sizeof(*sr);
36998f44cb0SIgor Mitsyanko 
3708b0b5f1bSIgor Mitsyanko 	qlink_for_each_tlv(tlv, sr->payload, payload_len) {
37198f44cb0SIgor Mitsyanko 		tlv_type = le16_to_cpu(tlv->type);
37298f44cb0SIgor Mitsyanko 		tlv_value_len = le16_to_cpu(tlv->len);
37398f44cb0SIgor Mitsyanko 
37498f44cb0SIgor Mitsyanko 		if (tlv_type == QTN_TLV_ID_IE_SET) {
37518b7470fSIgor Mitsyanko 			const struct qlink_tlv_ie_set *ie_set;
37618b7470fSIgor Mitsyanko 			unsigned int ie_len;
37718b7470fSIgor Mitsyanko 
3788b0b5f1bSIgor Mitsyanko 			if (tlv_value_len <
3798b0b5f1bSIgor Mitsyanko 			    (sizeof(*ie_set) - sizeof(ie_set->hdr)))
38018b7470fSIgor Mitsyanko 				return -EINVAL;
38118b7470fSIgor Mitsyanko 
38218b7470fSIgor Mitsyanko 			ie_set = (const struct qlink_tlv_ie_set *)tlv;
38318b7470fSIgor Mitsyanko 			ie_len = tlv_value_len -
38418b7470fSIgor Mitsyanko 				(sizeof(*ie_set) - sizeof(ie_set->hdr));
38518b7470fSIgor Mitsyanko 
3865face518SIgor Mitsyanko 			switch (ie_set->type) {
3875face518SIgor Mitsyanko 			case QLINK_IE_SET_BEACON_IES:
3885face518SIgor Mitsyanko 				frame_type = CFG80211_BSS_FTYPE_BEACON;
3895face518SIgor Mitsyanko 				break;
3905face518SIgor Mitsyanko 			case QLINK_IE_SET_PROBE_RESP_IES:
3915face518SIgor Mitsyanko 				frame_type = CFG80211_BSS_FTYPE_PRESP;
3925face518SIgor Mitsyanko 				break;
3935face518SIgor Mitsyanko 			default:
3945face518SIgor Mitsyanko 				frame_type = CFG80211_BSS_FTYPE_UNKNOWN;
3955face518SIgor Mitsyanko 			}
3965face518SIgor Mitsyanko 
39718b7470fSIgor Mitsyanko 			if (ie_len) {
39818b7470fSIgor Mitsyanko 				ies = ie_set->ie_data;
39918b7470fSIgor Mitsyanko 				ies_len = ie_len;
40018b7470fSIgor Mitsyanko 			}
40198f44cb0SIgor Mitsyanko 		}
40298f44cb0SIgor Mitsyanko 	}
40398f44cb0SIgor Mitsyanko 
4048b0b5f1bSIgor Mitsyanko 	if (!qlink_tlv_parsing_ok(tlv, sr->payload, payload_len))
40598f44cb0SIgor Mitsyanko 		return -EINVAL;
40698f44cb0SIgor Mitsyanko 
40798f44cb0SIgor Mitsyanko 	bss = cfg80211_inform_bss(wiphy, channel, frame_type,
40898f44cb0SIgor Mitsyanko 				  sr->bssid, get_unaligned_le64(&sr->tsf),
40998f44cb0SIgor Mitsyanko 				  le16_to_cpu(sr->capab),
41098f44cb0SIgor Mitsyanko 				  le16_to_cpu(sr->bintval), ies, ies_len,
411fbad963aSSergey Matyukevich 				  DBM_TO_MBM(sr->sig_dbm), GFP_KERNEL);
41298f44cb0SIgor Mitsyanko 	if (!bss)
41398f44cb0SIgor Mitsyanko 		return -ENOMEM;
41498f44cb0SIgor Mitsyanko 
41598f44cb0SIgor Mitsyanko 	cfg80211_put_bss(wiphy, bss);
41698f44cb0SIgor Mitsyanko 
41798f44cb0SIgor Mitsyanko 	return 0;
41898f44cb0SIgor Mitsyanko }
41998f44cb0SIgor Mitsyanko 
42098f44cb0SIgor Mitsyanko static int
qtnf_event_handle_scan_complete(struct qtnf_wmac * mac,const struct qlink_event_scan_complete * status,u16 len)42198f44cb0SIgor Mitsyanko qtnf_event_handle_scan_complete(struct qtnf_wmac *mac,
42298f44cb0SIgor Mitsyanko 				const struct qlink_event_scan_complete *status,
42398f44cb0SIgor Mitsyanko 				u16 len)
42498f44cb0SIgor Mitsyanko {
42598f44cb0SIgor Mitsyanko 	if (len < sizeof(*status)) {
42698f44cb0SIgor Mitsyanko 		pr_err("MAC%u: payload is too short\n", mac->macid);
42798f44cb0SIgor Mitsyanko 		return -EINVAL;
42898f44cb0SIgor Mitsyanko 	}
42998f44cb0SIgor Mitsyanko 
43098f44cb0SIgor Mitsyanko 	qtnf_scan_done(mac, le32_to_cpu(status->flags) & QLINK_SCAN_ABORTED);
43198f44cb0SIgor Mitsyanko 
43298f44cb0SIgor Mitsyanko 	return 0;
43398f44cb0SIgor Mitsyanko }
43498f44cb0SIgor Mitsyanko 
43597883695SSergey Matyukevich static int
qtnf_event_handle_freq_change(struct qtnf_wmac * mac,const struct qlink_event_freq_change * data,u16 len)43697883695SSergey Matyukevich qtnf_event_handle_freq_change(struct qtnf_wmac *mac,
43797883695SSergey Matyukevich 			      const struct qlink_event_freq_change *data,
43897883695SSergey Matyukevich 			      u16 len)
43997883695SSergey Matyukevich {
44097883695SSergey Matyukevich 	struct wiphy *wiphy = priv_to_wiphy(mac);
44197883695SSergey Matyukevich 	struct cfg80211_chan_def chandef;
44297883695SSergey Matyukevich 	struct qtnf_vif *vif;
44397883695SSergey Matyukevich 	int i;
44497883695SSergey Matyukevich 
44597883695SSergey Matyukevich 	if (len < sizeof(*data)) {
446fac7f9bfSIgor Mitsyanko 		pr_err("MAC%u: payload is too short\n", mac->macid);
44797883695SSergey Matyukevich 		return -EINVAL;
44897883695SSergey Matyukevich 	}
44997883695SSergey Matyukevich 
450115af851SIgor Mitsyanko 	if (!wiphy->registered)
451115af851SIgor Mitsyanko 		return 0;
452115af851SIgor Mitsyanko 
453fac7f9bfSIgor Mitsyanko 	qlink_chandef_q2cfg(wiphy, &data->chan, &chandef);
454fac7f9bfSIgor Mitsyanko 
455fac7f9bfSIgor Mitsyanko 	if (!cfg80211_chandef_valid(&chandef)) {
4565bf374abSSergey Matyukevich 		pr_err("MAC%u: bad channel freq=%u cf1=%u cf2=%u bw=%u\n",
4575bf374abSSergey Matyukevich 		       mac->macid, chandef.chan->center_freq,
458fac7f9bfSIgor Mitsyanko 		       chandef.center_freq1, chandef.center_freq2,
459fac7f9bfSIgor Mitsyanko 		       chandef.width);
46097883695SSergey Matyukevich 		return -EINVAL;
46197883695SSergey Matyukevich 	}
46297883695SSergey Matyukevich 
463fac7f9bfSIgor Mitsyanko 	pr_debug("MAC%d: new channel ieee=%u freq1=%u freq2=%u bw=%u\n",
464fac7f9bfSIgor Mitsyanko 		 mac->macid, chandef.chan->hw_value, chandef.center_freq1,
465fac7f9bfSIgor Mitsyanko 		 chandef.center_freq2, chandef.width);
46697883695SSergey Matyukevich 
46797883695SSergey Matyukevich 	for (i = 0; i < QTNF_MAX_INTF; i++) {
46897883695SSergey Matyukevich 		vif = &mac->iflist[i];
4694f87d486SSergey Matyukevich 
47097883695SSergey Matyukevich 		if (vif->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED)
47197883695SSergey Matyukevich 			continue;
47297883695SSergey Matyukevich 
4734f87d486SSergey Matyukevich 		if (vif->wdev.iftype == NL80211_IFTYPE_STATION &&
4747b0a0e3cSJohannes Berg 		    !vif->wdev.connected)
4754f87d486SSergey Matyukevich 			continue;
4764f87d486SSergey Matyukevich 
4774f87d486SSergey Matyukevich 		if (!vif->netdev)
4784f87d486SSergey Matyukevich 			continue;
4794f87d486SSergey Matyukevich 
48097883695SSergey Matyukevich 		mutex_lock(&vif->wdev.mtx);
481*b345f063SAloka Dixit 		cfg80211_ch_switch_notify(vif->netdev, &chandef, 0, 0);
48297883695SSergey Matyukevich 		mutex_unlock(&vif->wdev.mtx);
48397883695SSergey Matyukevich 	}
48497883695SSergey Matyukevich 
48597883695SSergey Matyukevich 	return 0;
48697883695SSergey Matyukevich }
48797883695SSergey Matyukevich 
qtnf_event_handle_radar(struct qtnf_vif * vif,const struct qlink_event_radar * ev,u16 len)488b05ee456SIgor Mitsyanko static int qtnf_event_handle_radar(struct qtnf_vif *vif,
489b05ee456SIgor Mitsyanko 				   const struct qlink_event_radar *ev,
490b05ee456SIgor Mitsyanko 				   u16 len)
491b05ee456SIgor Mitsyanko {
492b05ee456SIgor Mitsyanko 	struct wiphy *wiphy = priv_to_wiphy(vif->mac);
493b05ee456SIgor Mitsyanko 	struct cfg80211_chan_def chandef;
494b05ee456SIgor Mitsyanko 
495b05ee456SIgor Mitsyanko 	if (len < sizeof(*ev)) {
496b05ee456SIgor Mitsyanko 		pr_err("MAC%u: payload is too short\n", vif->mac->macid);
497b05ee456SIgor Mitsyanko 		return -EINVAL;
498b05ee456SIgor Mitsyanko 	}
499b05ee456SIgor Mitsyanko 
500b05ee456SIgor Mitsyanko 	if (!wiphy->registered || !vif->netdev)
501b05ee456SIgor Mitsyanko 		return 0;
502b05ee456SIgor Mitsyanko 
503b05ee456SIgor Mitsyanko 	qlink_chandef_q2cfg(wiphy, &ev->chan, &chandef);
504b05ee456SIgor Mitsyanko 
505b05ee456SIgor Mitsyanko 	if (!cfg80211_chandef_valid(&chandef)) {
506b05ee456SIgor Mitsyanko 		pr_err("MAC%u: bad channel f1=%u f2=%u bw=%u\n",
507b05ee456SIgor Mitsyanko 		       vif->mac->macid,
508b05ee456SIgor Mitsyanko 		       chandef.center_freq1, chandef.center_freq2,
509b05ee456SIgor Mitsyanko 		       chandef.width);
510b05ee456SIgor Mitsyanko 		return -EINVAL;
511b05ee456SIgor Mitsyanko 	}
512b05ee456SIgor Mitsyanko 
513b05ee456SIgor Mitsyanko 	pr_info("%s: radar event=%u f1=%u f2=%u bw=%u\n",
514b05ee456SIgor Mitsyanko 		vif->netdev->name, ev->event,
515b05ee456SIgor Mitsyanko 		chandef.center_freq1, chandef.center_freq2,
516b05ee456SIgor Mitsyanko 		chandef.width);
517b05ee456SIgor Mitsyanko 
518b05ee456SIgor Mitsyanko 	switch (ev->event) {
519b05ee456SIgor Mitsyanko 	case QLINK_RADAR_DETECTED:
520b05ee456SIgor Mitsyanko 		cfg80211_radar_event(wiphy, &chandef, GFP_KERNEL);
521b05ee456SIgor Mitsyanko 		break;
522b05ee456SIgor Mitsyanko 	case QLINK_RADAR_CAC_FINISHED:
523b05ee456SIgor Mitsyanko 		if (!vif->wdev.cac_started)
524b05ee456SIgor Mitsyanko 			break;
525b05ee456SIgor Mitsyanko 
526b05ee456SIgor Mitsyanko 		cfg80211_cac_event(vif->netdev, &chandef,
527b05ee456SIgor Mitsyanko 				   NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
528b05ee456SIgor Mitsyanko 		break;
529b05ee456SIgor Mitsyanko 	case QLINK_RADAR_CAC_ABORTED:
530b05ee456SIgor Mitsyanko 		if (!vif->wdev.cac_started)
531b05ee456SIgor Mitsyanko 			break;
532b05ee456SIgor Mitsyanko 
533b05ee456SIgor Mitsyanko 		cfg80211_cac_event(vif->netdev, &chandef,
534b05ee456SIgor Mitsyanko 				   NL80211_RADAR_CAC_ABORTED, GFP_KERNEL);
535b05ee456SIgor Mitsyanko 		break;
536fbb93020SDmitry Lebed 	case QLINK_RADAR_CAC_STARTED:
537fbb93020SDmitry Lebed 		if (vif->wdev.cac_started)
538fbb93020SDmitry Lebed 			break;
539fbb93020SDmitry Lebed 
540fbb93020SDmitry Lebed 		if (!wiphy_ext_feature_isset(wiphy,
541fbb93020SDmitry Lebed 					     NL80211_EXT_FEATURE_DFS_OFFLOAD))
542fbb93020SDmitry Lebed 			break;
543fbb93020SDmitry Lebed 
544fbb93020SDmitry Lebed 		cfg80211_cac_event(vif->netdev, &chandef,
545fbb93020SDmitry Lebed 				   NL80211_RADAR_CAC_STARTED, GFP_KERNEL);
546fbb93020SDmitry Lebed 		break;
547b05ee456SIgor Mitsyanko 	default:
548b05ee456SIgor Mitsyanko 		pr_warn("%s: unhandled radar event %u\n",
549b05ee456SIgor Mitsyanko 			vif->netdev->name, ev->event);
550b05ee456SIgor Mitsyanko 		break;
551b05ee456SIgor Mitsyanko 	}
552b05ee456SIgor Mitsyanko 
553b05ee456SIgor Mitsyanko 	return 0;
554b05ee456SIgor Mitsyanko }
555b05ee456SIgor Mitsyanko 
55647b08e75SSergey Matyukevich static int
qtnf_event_handle_external_auth(struct qtnf_vif * vif,const struct qlink_event_external_auth * ev,u16 len)55747b08e75SSergey Matyukevich qtnf_event_handle_external_auth(struct qtnf_vif *vif,
55847b08e75SSergey Matyukevich 				const struct qlink_event_external_auth *ev,
55947b08e75SSergey Matyukevich 				u16 len)
56047b08e75SSergey Matyukevich {
56147b08e75SSergey Matyukevich 	struct cfg80211_external_auth_params auth = {0};
56247b08e75SSergey Matyukevich 	struct wiphy *wiphy = priv_to_wiphy(vif->mac);
56347b08e75SSergey Matyukevich 	int ret;
56447b08e75SSergey Matyukevich 
56547b08e75SSergey Matyukevich 	if (len < sizeof(*ev)) {
56647b08e75SSergey Matyukevich 		pr_err("MAC%u: payload is too short\n", vif->mac->macid);
56747b08e75SSergey Matyukevich 		return -EINVAL;
56847b08e75SSergey Matyukevich 	}
56947b08e75SSergey Matyukevich 
57047b08e75SSergey Matyukevich 	if (!wiphy->registered || !vif->netdev)
57147b08e75SSergey Matyukevich 		return 0;
57247b08e75SSergey Matyukevich 
57347b08e75SSergey Matyukevich 	if (ev->ssid_len) {
574130f634dSLee Gibson 		int len = clamp_val(ev->ssid_len, 0, IEEE80211_MAX_SSID_LEN);
575130f634dSLee Gibson 
576130f634dSLee Gibson 		memcpy(auth.ssid.ssid, ev->ssid, len);
577130f634dSLee Gibson 		auth.ssid.ssid_len = len;
57847b08e75SSergey Matyukevich 	}
57947b08e75SSergey Matyukevich 
58047b08e75SSergey Matyukevich 	auth.key_mgmt_suite = le32_to_cpu(ev->akm_suite);
58147b08e75SSergey Matyukevich 	ether_addr_copy(auth.bssid, ev->bssid);
58247b08e75SSergey Matyukevich 	auth.action = ev->action;
58347b08e75SSergey Matyukevich 
584b3860e7aSSergey Matyukevich 	pr_debug("%s: external SAE processing: bss=%pM action=%u akm=%u\n",
58547b08e75SSergey Matyukevich 		 vif->netdev->name, auth.bssid, auth.action,
58647b08e75SSergey Matyukevich 		 auth.key_mgmt_suite);
58747b08e75SSergey Matyukevich 
58847b08e75SSergey Matyukevich 	ret = cfg80211_external_auth_request(vif->netdev, &auth, GFP_KERNEL);
58947b08e75SSergey Matyukevich 	if (ret)
59047b08e75SSergey Matyukevich 		pr_warn("failed to offload external auth request\n");
59147b08e75SSergey Matyukevich 
59247b08e75SSergey Matyukevich 	return ret;
59347b08e75SSergey Matyukevich }
59447b08e75SSergey Matyukevich 
595239ce8a7SSergey Matyukevich static int
qtnf_event_handle_mic_failure(struct qtnf_vif * vif,const struct qlink_event_mic_failure * mic_ev,u16 len)596239ce8a7SSergey Matyukevich qtnf_event_handle_mic_failure(struct qtnf_vif *vif,
597239ce8a7SSergey Matyukevich 			      const struct qlink_event_mic_failure *mic_ev,
598239ce8a7SSergey Matyukevich 			      u16 len)
599239ce8a7SSergey Matyukevich {
600239ce8a7SSergey Matyukevich 	struct wiphy *wiphy = priv_to_wiphy(vif->mac);
601239ce8a7SSergey Matyukevich 	u8 pairwise;
602239ce8a7SSergey Matyukevich 
603239ce8a7SSergey Matyukevich 	if (len < sizeof(*mic_ev)) {
604239ce8a7SSergey Matyukevich 		pr_err("VIF%u.%u: payload is too short (%u < %zu)\n",
605239ce8a7SSergey Matyukevich 		       vif->mac->macid, vif->vifid, len,
606239ce8a7SSergey Matyukevich 		       sizeof(struct qlink_event_mic_failure));
607239ce8a7SSergey Matyukevich 		return -EINVAL;
608239ce8a7SSergey Matyukevich 	}
609239ce8a7SSergey Matyukevich 
610239ce8a7SSergey Matyukevich 	if (!wiphy->registered || !vif->netdev)
611239ce8a7SSergey Matyukevich 		return 0;
612239ce8a7SSergey Matyukevich 
613239ce8a7SSergey Matyukevich 	if (vif->wdev.iftype != NL80211_IFTYPE_STATION) {
614239ce8a7SSergey Matyukevich 		pr_err("VIF%u.%u: MIC_FAILURE event when not in STA mode\n",
615239ce8a7SSergey Matyukevich 		       vif->mac->macid, vif->vifid);
616239ce8a7SSergey Matyukevich 		return -EPROTO;
617239ce8a7SSergey Matyukevich 	}
618239ce8a7SSergey Matyukevich 
619239ce8a7SSergey Matyukevich 	pairwise = mic_ev->pairwise ?
620239ce8a7SSergey Matyukevich 		NL80211_KEYTYPE_PAIRWISE : NL80211_KEYTYPE_GROUP;
621239ce8a7SSergey Matyukevich 
622239ce8a7SSergey Matyukevich 	pr_info("%s: MIC error: src=%pM key_index=%u pairwise=%u\n",
623239ce8a7SSergey Matyukevich 		vif->netdev->name, mic_ev->src, mic_ev->key_index, pairwise);
624239ce8a7SSergey Matyukevich 
625239ce8a7SSergey Matyukevich 	cfg80211_michael_mic_failure(vif->netdev, mic_ev->src, pairwise,
626239ce8a7SSergey Matyukevich 				     mic_ev->key_index, NULL, GFP_KERNEL);
627239ce8a7SSergey Matyukevich 
628239ce8a7SSergey Matyukevich 	return 0;
629239ce8a7SSergey Matyukevich }
630239ce8a7SSergey Matyukevich 
63144d09764SSergey Matyukevich static int
qtnf_event_handle_update_owe(struct qtnf_vif * vif,const struct qlink_event_update_owe * owe_ev,u16 len)63244d09764SSergey Matyukevich qtnf_event_handle_update_owe(struct qtnf_vif *vif,
63344d09764SSergey Matyukevich 			     const struct qlink_event_update_owe *owe_ev,
63444d09764SSergey Matyukevich 			     u16 len)
63544d09764SSergey Matyukevich {
63644d09764SSergey Matyukevich 	struct wiphy *wiphy = priv_to_wiphy(vif->mac);
63744d09764SSergey Matyukevich 	struct cfg80211_update_owe_info owe_info = {};
63844d09764SSergey Matyukevich 	const u16 ie_len = len - sizeof(*owe_ev);
63944d09764SSergey Matyukevich 	u8 *ie;
64044d09764SSergey Matyukevich 
64144d09764SSergey Matyukevich 	if (len < sizeof(*owe_ev)) {
64244d09764SSergey Matyukevich 		pr_err("VIF%u.%u: payload is too short (%u < %zu)\n",
64344d09764SSergey Matyukevich 		       vif->mac->macid, vif->vifid, len,
64444d09764SSergey Matyukevich 		       sizeof(struct qlink_event_update_owe));
64544d09764SSergey Matyukevich 		return -EINVAL;
64644d09764SSergey Matyukevich 	}
64744d09764SSergey Matyukevich 
64844d09764SSergey Matyukevich 	if (!wiphy->registered || !vif->netdev)
64944d09764SSergey Matyukevich 		return 0;
65044d09764SSergey Matyukevich 
65144d09764SSergey Matyukevich 	if (vif->wdev.iftype != NL80211_IFTYPE_AP) {
65244d09764SSergey Matyukevich 		pr_err("VIF%u.%u: UPDATE_OWE event when not in AP mode\n",
65344d09764SSergey Matyukevich 		       vif->mac->macid, vif->vifid);
65444d09764SSergey Matyukevich 		return -EPROTO;
65544d09764SSergey Matyukevich 	}
65644d09764SSergey Matyukevich 
65744d09764SSergey Matyukevich 	ie = kzalloc(ie_len, GFP_KERNEL);
65844d09764SSergey Matyukevich 	if (!ie)
65944d09764SSergey Matyukevich 		return -ENOMEM;
66044d09764SSergey Matyukevich 
66144d09764SSergey Matyukevich 	memcpy(owe_info.peer, owe_ev->peer, ETH_ALEN);
66244d09764SSergey Matyukevich 	memcpy(ie, owe_ev->ies, ie_len);
66344d09764SSergey Matyukevich 	owe_info.ie_len = ie_len;
66444d09764SSergey Matyukevich 	owe_info.ie = ie;
6658bb588d9SVeerendranath Jakkam 	owe_info.assoc_link_id = -1;
66644d09764SSergey Matyukevich 
66744d09764SSergey Matyukevich 	pr_info("%s: external OWE processing: peer=%pM\n",
66844d09764SSergey Matyukevich 		vif->netdev->name, owe_ev->peer);
66944d09764SSergey Matyukevich 
67044d09764SSergey Matyukevich 	cfg80211_update_owe_info_event(vif->netdev, &owe_info, GFP_KERNEL);
67144d09764SSergey Matyukevich 	kfree(ie);
67244d09764SSergey Matyukevich 
67344d09764SSergey Matyukevich 	return 0;
67444d09764SSergey Matyukevich }
67544d09764SSergey Matyukevich 
qtnf_event_parse(struct qtnf_wmac * mac,const struct sk_buff * event_skb)67698f44cb0SIgor Mitsyanko static int qtnf_event_parse(struct qtnf_wmac *mac,
67798f44cb0SIgor Mitsyanko 			    const struct sk_buff *event_skb)
67898f44cb0SIgor Mitsyanko {
67998f44cb0SIgor Mitsyanko 	const struct qlink_event *event;
68098f44cb0SIgor Mitsyanko 	struct qtnf_vif *vif = NULL;
68198f44cb0SIgor Mitsyanko 	int ret = -1;
68298f44cb0SIgor Mitsyanko 	u16 event_id;
68398f44cb0SIgor Mitsyanko 	u16 event_len;
684946d077aSSergey Matyukevich 	u8 vifid;
68598f44cb0SIgor Mitsyanko 
68698f44cb0SIgor Mitsyanko 	event = (const struct qlink_event *)event_skb->data;
68798f44cb0SIgor Mitsyanko 	event_id = le16_to_cpu(event->event_id);
68898f44cb0SIgor Mitsyanko 	event_len = le16_to_cpu(event->mhdr.len);
68998f44cb0SIgor Mitsyanko 
690946d077aSSergey Matyukevich 	if (event->vifid >= QTNF_MAX_INTF) {
69198f44cb0SIgor Mitsyanko 		pr_err("invalid vif(%u)\n", event->vifid);
69298f44cb0SIgor Mitsyanko 		return -EINVAL;
69398f44cb0SIgor Mitsyanko 	}
69498f44cb0SIgor Mitsyanko 
695946d077aSSergey Matyukevich 	vifid = array_index_nospec(event->vifid, QTNF_MAX_INTF);
696946d077aSSergey Matyukevich 	vif = &mac->iflist[vifid];
697946d077aSSergey Matyukevich 
69898f44cb0SIgor Mitsyanko 	switch (event_id) {
69998f44cb0SIgor Mitsyanko 	case QLINK_EVENT_STA_ASSOCIATED:
70098f44cb0SIgor Mitsyanko 		ret = qtnf_event_handle_sta_assoc(mac, vif, (const void *)event,
70198f44cb0SIgor Mitsyanko 						  event_len);
70298f44cb0SIgor Mitsyanko 		break;
70398f44cb0SIgor Mitsyanko 	case QLINK_EVENT_STA_DEAUTH:
70498f44cb0SIgor Mitsyanko 		ret = qtnf_event_handle_sta_deauth(mac, vif,
70598f44cb0SIgor Mitsyanko 						   (const void *)event,
70698f44cb0SIgor Mitsyanko 						   event_len);
70798f44cb0SIgor Mitsyanko 		break;
70898f44cb0SIgor Mitsyanko 	case QLINK_EVENT_MGMT_RECEIVED:
70998f44cb0SIgor Mitsyanko 		ret = qtnf_event_handle_mgmt_received(vif, (const void *)event,
71098f44cb0SIgor Mitsyanko 						      event_len);
71198f44cb0SIgor Mitsyanko 		break;
71298f44cb0SIgor Mitsyanko 	case QLINK_EVENT_SCAN_RESULTS:
71398f44cb0SIgor Mitsyanko 		ret = qtnf_event_handle_scan_results(vif, (const void *)event,
71498f44cb0SIgor Mitsyanko 						     event_len);
71598f44cb0SIgor Mitsyanko 		break;
71698f44cb0SIgor Mitsyanko 	case QLINK_EVENT_SCAN_COMPLETE:
71798f44cb0SIgor Mitsyanko 		ret = qtnf_event_handle_scan_complete(mac, (const void *)event,
71898f44cb0SIgor Mitsyanko 						      event_len);
71998f44cb0SIgor Mitsyanko 		break;
72098f44cb0SIgor Mitsyanko 	case QLINK_EVENT_BSS_JOIN:
72198f44cb0SIgor Mitsyanko 		ret = qtnf_event_handle_bss_join(vif, (const void *)event,
72298f44cb0SIgor Mitsyanko 						 event_len);
72398f44cb0SIgor Mitsyanko 		break;
72498f44cb0SIgor Mitsyanko 	case QLINK_EVENT_BSS_LEAVE:
72598f44cb0SIgor Mitsyanko 		ret = qtnf_event_handle_bss_leave(vif, (const void *)event,
72698f44cb0SIgor Mitsyanko 						  event_len);
72798f44cb0SIgor Mitsyanko 		break;
72897883695SSergey Matyukevich 	case QLINK_EVENT_FREQ_CHANGE:
72997883695SSergey Matyukevich 		ret = qtnf_event_handle_freq_change(mac, (const void *)event,
73097883695SSergey Matyukevich 						    event_len);
73197883695SSergey Matyukevich 		break;
732b05ee456SIgor Mitsyanko 	case QLINK_EVENT_RADAR:
733b05ee456SIgor Mitsyanko 		ret = qtnf_event_handle_radar(vif, (const void *)event,
734b05ee456SIgor Mitsyanko 					      event_len);
735b05ee456SIgor Mitsyanko 		break;
73647b08e75SSergey Matyukevich 	case QLINK_EVENT_EXTERNAL_AUTH:
73747b08e75SSergey Matyukevich 		ret = qtnf_event_handle_external_auth(vif, (const void *)event,
73847b08e75SSergey Matyukevich 						      event_len);
73947b08e75SSergey Matyukevich 		break;
740239ce8a7SSergey Matyukevich 	case QLINK_EVENT_MIC_FAILURE:
741239ce8a7SSergey Matyukevich 		ret = qtnf_event_handle_mic_failure(vif, (const void *)event,
742239ce8a7SSergey Matyukevich 						    event_len);
743239ce8a7SSergey Matyukevich 		break;
74444d09764SSergey Matyukevich 	case QLINK_EVENT_UPDATE_OWE:
74544d09764SSergey Matyukevich 		ret = qtnf_event_handle_update_owe(vif, (const void *)event,
74644d09764SSergey Matyukevich 						   event_len);
74744d09764SSergey Matyukevich 		break;
74898f44cb0SIgor Mitsyanko 	default:
74998f44cb0SIgor Mitsyanko 		pr_warn("unknown event type: %x\n", event_id);
75098f44cb0SIgor Mitsyanko 		break;
75198f44cb0SIgor Mitsyanko 	}
75298f44cb0SIgor Mitsyanko 
75398f44cb0SIgor Mitsyanko 	return ret;
75498f44cb0SIgor Mitsyanko }
75598f44cb0SIgor Mitsyanko 
qtnf_event_process_skb(struct qtnf_bus * bus,const struct sk_buff * skb)75698f44cb0SIgor Mitsyanko static int qtnf_event_process_skb(struct qtnf_bus *bus,
75798f44cb0SIgor Mitsyanko 				  const struct sk_buff *skb)
75898f44cb0SIgor Mitsyanko {
75998f44cb0SIgor Mitsyanko 	const struct qlink_event *event;
76098f44cb0SIgor Mitsyanko 	struct qtnf_wmac *mac;
76198f44cb0SIgor Mitsyanko 	int res;
76298f44cb0SIgor Mitsyanko 
76398f44cb0SIgor Mitsyanko 	if (unlikely(!skb || skb->len < sizeof(*event))) {
76498f44cb0SIgor Mitsyanko 		pr_err("invalid event buffer\n");
76598f44cb0SIgor Mitsyanko 		return -EINVAL;
76698f44cb0SIgor Mitsyanko 	}
76798f44cb0SIgor Mitsyanko 
76898f44cb0SIgor Mitsyanko 	event = (struct qlink_event *)skb->data;
76998f44cb0SIgor Mitsyanko 
77098f44cb0SIgor Mitsyanko 	mac = qtnf_core_get_mac(bus, event->macid);
77198f44cb0SIgor Mitsyanko 
77298f44cb0SIgor Mitsyanko 	pr_debug("new event id:%x len:%u mac:%u vif:%u\n",
77398f44cb0SIgor Mitsyanko 		 le16_to_cpu(event->event_id), le16_to_cpu(event->mhdr.len),
77498f44cb0SIgor Mitsyanko 		 event->macid, event->vifid);
77598f44cb0SIgor Mitsyanko 
77698f44cb0SIgor Mitsyanko 	if (unlikely(!mac))
77798f44cb0SIgor Mitsyanko 		return -ENXIO;
77898f44cb0SIgor Mitsyanko 
779237d29f6SIgor Mitsyanko 	rtnl_lock();
78098f44cb0SIgor Mitsyanko 	res = qtnf_event_parse(mac, skb);
781237d29f6SIgor Mitsyanko 	rtnl_unlock();
78298f44cb0SIgor Mitsyanko 
78398f44cb0SIgor Mitsyanko 	return res;
78498f44cb0SIgor Mitsyanko }
78598f44cb0SIgor Mitsyanko 
qtnf_event_work_handler(struct work_struct * work)78698f44cb0SIgor Mitsyanko void qtnf_event_work_handler(struct work_struct *work)
78798f44cb0SIgor Mitsyanko {
78898f44cb0SIgor Mitsyanko 	struct qtnf_bus *bus = container_of(work, struct qtnf_bus, event_work);
78998f44cb0SIgor Mitsyanko 	struct sk_buff_head *event_queue = &bus->trans.event_queue;
79098f44cb0SIgor Mitsyanko 	struct sk_buff *current_event_skb = skb_dequeue(event_queue);
79198f44cb0SIgor Mitsyanko 
79298f44cb0SIgor Mitsyanko 	while (current_event_skb) {
79398f44cb0SIgor Mitsyanko 		qtnf_event_process_skb(bus, current_event_skb);
79498f44cb0SIgor Mitsyanko 		dev_kfree_skb_any(current_event_skb);
79598f44cb0SIgor Mitsyanko 		current_event_skb = skb_dequeue(event_queue);
79698f44cb0SIgor Mitsyanko 	}
79798f44cb0SIgor Mitsyanko }
798