xref: /wlan-dirver/qca-wifi-host-cmn/os_if/linux/mlme/src/osif_cm_disconnect_rsp.c (revision 70a19e16789e308182f63b15c75decec7bf0b342)
1 /*
2  * Copyright (c) 2012-2015, 2020-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022-2023 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 	    rsp->req.req.source == CM_MLO_ROAM_INTERNAL_DISCONNECT) {
55 		osif_debug("ignore internal disconnect");
56 		status = QDF_STATUS_E_INVAL;
57 		goto rel_lock;
58 	}
59 
60 	/*
61 	 * Send to kernel only if last osif cmd type is disconnect and
62 	 * cookie match else drop. If cookie match reset the cookie
63 	 * and source
64 	 */
65 	if (rsp->req.cm_id != osif_priv->cm_info.last_id ||
66 	    rsp->req.req.source != osif_priv->cm_info.last_source) {
67 		osif_debug("Ignore as cm_id(0x%x)/src(%d) didn't match stored cm_id(0x%x)/src(%d)",
68 			   rsp->req.cm_id, rsp->req.req.source,
69 			   osif_priv->cm_info.last_id,
70 			   osif_priv->cm_info.last_source);
71 		status = QDF_STATUS_E_INVAL;
72 		goto rel_lock;
73 	}
74 
75 	osif_cm_reset_id_and_src_no_lock(osif_priv);
76 rel_lock:
77 	qdf_spinlock_release(&osif_priv->cm_info.cmd_id_lock);
78 
79 	return status;
80 }
81 
82 #if defined(CFG80211_DISCONNECTED_V2) || \
83 (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0))
84 #ifdef CONN_MGR_ADV_FEATURE
85 static void
86 osif_cm_indicate_disconnect_result(struct net_device *dev,
87 				   enum ieee80211_reasoncode reason,
88 				   const u8 *ie, size_t ie_len,
89 				   bool locally_generated, int link_id,
90 				   gfp_t gfp)
91 {
92 	cfg80211_disconnected(dev, reason, ie,
93 			      ie_len, locally_generated, gfp);
94 }
95 #else
96 #ifdef WLAN_SUPPORT_CFG80211_DISCONNECT_LINK_PARAM
97 static void
98 osif_cm_indicate_disconnect_result(struct net_device *dev,
99 				   enum ieee80211_reasoncode reason,
100 				   const u8 *ie, size_t ie_len,
101 				   bool locally_generated, int link_id,
102 				   gfp_t gfp)
103 {
104 	cfg80211_disconnected(dev, reason, ie,
105 			      ie_len, locally_generated, link_id, gfp);
106 }
107 #else
108 static void
109 osif_cm_indicate_disconnect_result(struct net_device *dev,
110 				   enum ieee80211_reasoncode reason,
111 				   const u8 *ie, size_t ie_len,
112 				   bool locally_generated, int link_id,
113 				   gfp_t gfp)
114 {
115 	cfg80211_disconnected(dev, reason, ie,
116 			      ie_len, locally_generated, gfp);
117 }
118 #endif /* WLAN_SUPPORT_CFG80211_DISCONNECT_LINK_PARAM */
119 #endif
120 
121 #ifdef WLAN_FEATURE_11BE_MLO
122 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
123 void
124 osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
125 			    struct net_device *dev,
126 			    enum ieee80211_reasoncode reason,
127 			    bool locally_generated, const u8 *ie,
128 			    size_t ie_len, int link_id, gfp_t gfp)
129 {
130 	if (wlan_vdev_mlme_is_mlo_vdev(vdev)) {
131 		if (!wlan_vdev_mlme_is_mlo_link_vdev(vdev))
132 			osif_cm_indicate_disconnect_result(
133 					dev, reason, ie,
134 					ie_len, locally_generated,
135 					link_id, gfp);
136 	} else {
137 		osif_cm_indicate_disconnect_result(
138 				dev, reason, ie,
139 				ie_len, locally_generated,
140 				link_id, gfp);
141 	}
142 }
143 #else /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
144 void
145 osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
146 			    struct net_device *dev,
147 			    enum ieee80211_reasoncode reason,
148 			    bool locally_generated, const u8 *ie,
149 			    size_t ie_len, int link_id, gfp_t gfp)
150 {
151 	struct net_device *netdev = dev;
152 	struct vdev_osif_priv *osif_priv = NULL;
153 	struct wlan_objmgr_vdev *assoc_vdev = NULL;
154 
155 	if (!wlan_vdev_mlme_is_mlo_vdev(vdev) || (link_id != -1)) {
156 		osif_cm_indicate_disconnect_result(
157 				netdev, reason, ie, ie_len,
158 				locally_generated, link_id, gfp);
159 		return;
160 	}
161 
162 	if (ucfg_mlo_is_mld_disconnected(vdev)) {
163 		assoc_vdev = ucfg_mlo_get_assoc_link_vdev(vdev);
164 		if (!assoc_vdev)
165 			return;
166 		osif_priv  = wlan_vdev_get_ospriv(assoc_vdev);
167 		netdev = osif_priv->wdev->netdev;
168 		osif_cm_indicate_disconnect_result(
169 				netdev, reason,
170 				ie, ie_len,
171 				locally_generated, link_id, gfp);
172 	}
173 }
174 #endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
175 #else /* WLAN_FEATURE_11BE_MLO */
176 void
177 osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
178 			    struct net_device *dev,
179 			    enum ieee80211_reasoncode reason,
180 			    bool locally_generated, const u8 *ie,
181 			    size_t ie_len, int link_id, gfp_t gfp)
182 {
183 	osif_cm_indicate_disconnect_result(dev, reason, ie,
184 					   ie_len, locally_generated,
185 					   link_id, gfp);
186 }
187 #endif /* WLAN_FEATURE_11BE_MLO */
188 #else
189 void
190 osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
191 			    struct net_device *dev,
192 			    enum ieee80211_reasoncode reason,
193 			    bool locally_generated, const u8 *ie,
194 			    size_t ie_len, int link_id, gfp_t gfp)
195 {
196 	cfg80211_disconnected(dev, reason, ie, ie_len, gfp);
197 }
198 #endif
199 
200 static enum ieee80211_reasoncode
201 osif_cm_get_disconnect_reason(struct vdev_osif_priv *osif_priv, uint16_t reason)
202 {
203 	enum ieee80211_reasoncode ieee80211_reason = WLAN_REASON_UNSPECIFIED;
204 
205 	if (reason < REASON_PROP_START)
206 		ieee80211_reason = reason;
207 	/*
208 	 * Applications expect reason code as 0 for beacon miss failure
209 	 * due to backward compatibility. So send ieee80211_reason as 0.
210 	 */
211 	if (reason == REASON_BEACON_MISSED)
212 		ieee80211_reason = 0;
213 
214 	return ieee80211_reason;
215 }
216 
217 #ifdef CONN_MGR_ADV_FEATURE
218 static inline bool
219 osif_is_disconnect_locally_generated(struct wlan_cm_discon_rsp *rsp)
220 {
221 	if (rsp->req.req.source == CM_PEER_DISCONNECT)
222 		return false;
223 
224 	return true;
225 }
226 #else
227 static inline bool
228 osif_is_disconnect_locally_generated(struct wlan_cm_discon_rsp *rsp)
229 {
230 	if (rsp->req.req.source == CM_PEER_DISCONNECT ||
231 	    rsp->req.req.source == CM_SB_DISCONNECT)
232 		return false;
233 
234 	return true;
235 }
236 #endif
237 
238 QDF_STATUS osif_disconnect_handler(struct wlan_objmgr_vdev *vdev,
239 				   struct wlan_cm_discon_rsp *rsp)
240 {
241 	enum ieee80211_reasoncode ieee80211_reason;
242 	struct vdev_osif_priv *osif_priv = wlan_vdev_get_ospriv(vdev);
243 	bool locally_generated = true;
244 	QDF_STATUS status = QDF_STATUS_SUCCESS;
245 	enum qca_disconnect_reason_codes qca_reason;
246 	int link_id = -1;
247 
248 	qca_reason = osif_cm_mac_to_qca_reason(rsp->req.req.reason_code);
249 	ieee80211_reason =
250 		osif_cm_get_disconnect_reason(osif_priv,
251 					      rsp->req.req.reason_code);
252 
253 	locally_generated = osif_is_disconnect_locally_generated(rsp);
254 
255 	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",
256 		       osif_priv->wdev->netdev->name,
257 		       rsp->req.req.vdev_id,
258 		       QDF_MAC_ADDR_REF(wlan_vdev_mlme_get_macaddr(vdev)),
259 		       locally_generated ? "locally-generated " : "",
260 		       QDF_MAC_ADDR_REF(rsp->req.req.bssid.bytes),
261 		       rsp->req.cm_id, rsp->req.req.source, ieee80211_reason,
262 		       ucfg_cm_reason_code_to_str(rsp->req.req.reason_code),
263 		       qca_reason,
264 		       osif_cm_qca_reason_to_str(qca_reason));
265 
266 	/* Unlink bss if disconnect is from peer or south bound */
267 	if (rsp->req.req.source == CM_PEER_DISCONNECT ||
268 	    rsp->req.req.source == CM_SB_DISCONNECT)
269 		osif_cm_unlink_bss(vdev, osif_priv, &rsp->req.req.bssid,
270 				   NULL, 0);
271 
272 	status = osif_validate_disconnect_and_reset_src_id(osif_priv, rsp);
273 	if (QDF_IS_STATUS_ERROR(status)) {
274 		osif_cm_disconnect_comp_ind(vdev, rsp, OSIF_NOT_HANDLED);
275 		return status;
276 	}
277 
278 	/* If disconnect due to ML Reconfig, fill link id */
279 	if (rsp->req.req.reason_code == REASON_HOST_TRIGGERED_LINK_DELETE)
280 		link_id = wlan_vdev_get_link_id(vdev);
281 
282 	osif_cm_disconnect_comp_ind(vdev, rsp, OSIF_PRE_USERSPACE_UPDATE);
283 	osif_cm_indicate_disconnect(vdev, osif_priv->wdev->netdev,
284 				    ieee80211_reason,
285 				    locally_generated, rsp->ap_discon_ie.ptr,
286 				    rsp->ap_discon_ie.len,
287 				    link_id,
288 				    qdf_mem_malloc_flags());
289 
290 	osif_cm_disconnect_comp_ind(vdev, rsp, OSIF_POST_USERSPACE_UPDATE);
291 
292 	return status;
293 }
294