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