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