xref: /wlan-dirver/qca-wifi-host-cmn/os_if/linux/mlme/src/osif_cm_disconnect_rsp.c (revision 901120c066e139c7f8a2c8e4820561fdd83c67ef)
1 /*
2  * Copyright (c) 2012-2015, 2020-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /**
19  * DOC: osif_cm_disconnect_rsp.c
20  *
21  * This file maintains definitaions of disconnect response
22  * functions.
23  */
24 
25 #include <wlan_cfg80211.h>
26 #include <linux/wireless.h>
27 #include "osif_cm_rsp.h"
28 #include "wlan_osif_priv.h"
29 #include "osif_cm_util.h"
30 #include "wlan_mlo_mgr_sta.h"
31 
32 /**
33  * osif_validate_disconnect_and_reset_src_id() - Validate disconnection
34  * and resets source and id
35  * @osif_priv: Pointer to vdev osif priv
36  * @rsp: Disconnect response from connectin manager
37  *
38  * This function validates disconnect response and if the disconnect
39  * response is valid, resets the source and id of the command
40  *
41  * Context: Any context. Takes and releases cmd id spinlock.
42  * Return: QDF_STATUS
43  */
44 
45 static QDF_STATUS
46 osif_validate_disconnect_and_reset_src_id(struct vdev_osif_priv *osif_priv,
47 					  struct wlan_cm_discon_rsp *rsp)
48 {
49 	QDF_STATUS status = QDF_STATUS_SUCCESS;
50 
51 	/* Always drop internal disconnect */
52 	qdf_spinlock_acquire(&osif_priv->cm_info.cmd_id_lock);
53 	if (rsp->req.req.source == CM_INTERNAL_DISCONNECT) {
54 		osif_debug("ignore internal disconnect");
55 		status = QDF_STATUS_E_INVAL;
56 		goto rel_lock;
57 	}
58 
59 	/*
60 	 * Send to kernel only if last osif cmd type is disconnect and
61 	 * cookie match else drop. If cookie match reset the cookie
62 	 * and source
63 	 */
64 	if (rsp->req.cm_id != osif_priv->cm_info.last_id ||
65 	    rsp->req.req.source != osif_priv->cm_info.last_source) {
66 		osif_debug("Ignore as cm_id(0x%x)/src(%d) didn't match stored cm_id(0x%x)/src(%d)",
67 			   rsp->req.cm_id, rsp->req.req.source,
68 			   osif_priv->cm_info.last_id,
69 			   osif_priv->cm_info.last_source);
70 		status = QDF_STATUS_E_INVAL;
71 		goto rel_lock;
72 	}
73 
74 	osif_cm_reset_id_and_src_no_lock(osif_priv);
75 rel_lock:
76 	qdf_spinlock_release(&osif_priv->cm_info.cmd_id_lock);
77 
78 	return status;
79 }
80 
81 #if defined(CFG80211_DISCONNECTED_V2) || \
82 (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0))
83 #ifdef WLAN_FEATURE_11BE_MLO
84 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
85 void
86 osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
87 			    struct net_device *dev,
88 			    enum ieee80211_reasoncode reason,
89 			    bool locally_generated, const u8 *ie,
90 			    size_t ie_len, gfp_t gfp)
91 {
92 	if (wlan_vdev_mlme_is_mlo_vdev(vdev)) {
93 		if (!wlan_vdev_mlme_is_mlo_link_vdev(vdev))
94 			cfg80211_disconnected(dev, reason, ie,
95 					      ie_len, locally_generated, gfp);
96 	} else {
97 		cfg80211_disconnected(dev, reason, ie,
98 				      ie_len, locally_generated, gfp);
99 	}
100 }
101 #else /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
102 void
103 osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
104 			    struct net_device *dev,
105 			    enum ieee80211_reasoncode reason,
106 			    bool locally_generated, const u8 *ie,
107 			    size_t ie_len, gfp_t gfp)
108 {
109 	struct net_device *netdev = dev;
110 	struct vdev_osif_priv *osif_priv = NULL;
111 	struct wlan_objmgr_vdev *assoc_vdev = NULL;
112 
113 	if (!wlan_vdev_mlme_is_mlo_vdev(vdev)) {
114 		cfg80211_disconnected(netdev, reason, ie, ie_len,
115 				      locally_generated, gfp);
116 		return;
117 	}
118 
119 	if (ucfg_mlo_is_mld_disconnected(vdev)) {
120 		assoc_vdev = ucfg_mlo_get_assoc_link_vdev(vdev);
121 		if (!assoc_vdev)
122 			return;
123 		osif_priv  = wlan_vdev_get_ospriv(assoc_vdev);
124 		netdev = osif_priv->wdev->netdev;
125 		cfg80211_disconnected(netdev, reason,
126 				      ie, ie_len,
127 				      locally_generated, gfp);
128 	}
129 }
130 #endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
131 #else /* WLAN_FEATURE_11BE_MLO */
132 void
133 osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
134 			    struct net_device *dev,
135 			    enum ieee80211_reasoncode reason,
136 			    bool locally_generated, const u8 *ie,
137 			    size_t ie_len, gfp_t gfp)
138 {
139 	cfg80211_disconnected(dev, reason, ie, ie_len, locally_generated, gfp);
140 }
141 #endif /* WLAN_FEATURE_11BE_MLO */
142 #else
143 void
144 osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
145 			    struct net_device *dev,
146 			    enum ieee80211_reasoncode reason,
147 			    bool locally_generated, const u8 *ie,
148 			    size_t ie_len, gfp_t gfp)
149 {
150 	cfg80211_disconnected(dev, reason, ie, ie_len, gfp);
151 }
152 #endif
153 
154 static enum ieee80211_reasoncode
155 osif_cm_get_disconnect_reason(struct vdev_osif_priv *osif_priv, uint16_t reason)
156 {
157 	enum ieee80211_reasoncode ieee80211_reason = WLAN_REASON_UNSPECIFIED;
158 
159 	if (reason < REASON_PROP_START)
160 		ieee80211_reason = reason;
161 	/*
162 	 * Applications expect reason code as 0 for beacon miss failure
163 	 * due to backward compatibility. So send ieee80211_reason as 0.
164 	 */
165 	if (reason == REASON_BEACON_MISSED)
166 		ieee80211_reason = 0;
167 
168 	return ieee80211_reason;
169 }
170 
171 #ifdef CONN_MGR_ADV_FEATURE
172 static inline bool
173 osif_is_disconnect_locally_generated(struct wlan_cm_discon_rsp *rsp)
174 {
175 	if (rsp->req.req.source == CM_PEER_DISCONNECT)
176 		return false;
177 
178 	return true;
179 }
180 #else
181 static inline bool
182 osif_is_disconnect_locally_generated(struct wlan_cm_discon_rsp *rsp)
183 {
184 	if (rsp->req.req.source == CM_PEER_DISCONNECT ||
185 	    rsp->req.req.source == CM_SB_DISCONNECT)
186 		return false;
187 
188 	return true;
189 }
190 #endif
191 
192 QDF_STATUS osif_disconnect_handler(struct wlan_objmgr_vdev *vdev,
193 				   struct wlan_cm_discon_rsp *rsp)
194 {
195 	enum ieee80211_reasoncode ieee80211_reason;
196 	struct vdev_osif_priv *osif_priv = wlan_vdev_get_ospriv(vdev);
197 	bool locally_generated = true;
198 	QDF_STATUS status = QDF_STATUS_SUCCESS;
199 	enum qca_disconnect_reason_codes qca_reason;
200 
201 	qca_reason = osif_cm_mac_to_qca_reason(rsp->req.req.reason_code);
202 	ieee80211_reason =
203 		osif_cm_get_disconnect_reason(osif_priv,
204 					      rsp->req.req.reason_code);
205 
206 	locally_generated = osif_is_disconnect_locally_generated(rsp);
207 
208 	osif_nofl_info("%s(vdevid-%d): " QDF_MAC_ADDR_FMT " %sdisconnect " QDF_MAC_ADDR_FMT " cm_id 0x%x source %d reason:%u %s vendor:%u %s",
209 		       osif_priv->wdev->netdev->name,
210 		       rsp->req.req.vdev_id,
211 		       QDF_MAC_ADDR_REF(wlan_vdev_mlme_get_macaddr(vdev)),
212 		       locally_generated ? "locally-generated " : "",
213 		       QDF_MAC_ADDR_REF(rsp->req.req.bssid.bytes),
214 		       rsp->req.cm_id, rsp->req.req.source, ieee80211_reason,
215 		       ucfg_cm_reason_code_to_str(rsp->req.req.reason_code),
216 		       qca_reason,
217 		       osif_cm_qca_reason_to_str(qca_reason));
218 
219 	/* Unlink bss if disconnect is from peer or south bound */
220 	if (rsp->req.req.source == CM_PEER_DISCONNECT ||
221 	    rsp->req.req.source == CM_SB_DISCONNECT)
222 		osif_cm_unlink_bss(vdev, osif_priv, &rsp->req.req.bssid,
223 				   NULL, 0);
224 
225 	status = osif_validate_disconnect_and_reset_src_id(osif_priv, rsp);
226 	if (QDF_IS_STATUS_ERROR(status)) {
227 		osif_cm_disconnect_comp_ind(vdev, rsp, OSIF_NOT_HANDLED);
228 		return status;
229 	}
230 
231 	osif_cm_disconnect_comp_ind(vdev, rsp, OSIF_PRE_USERSPACE_UPDATE);
232 	osif_cm_indicate_disconnect(vdev, osif_priv->wdev->netdev,
233 				    ieee80211_reason,
234 				    locally_generated, rsp->ap_discon_ie.ptr,
235 				    rsp->ap_discon_ie.len,
236 				    qdf_mem_malloc_flags());
237 
238 	osif_cm_disconnect_comp_ind(vdev, rsp, OSIF_POST_USERSPACE_UPDATE);
239 
240 	return status;
241 }
242