xref: /wlan-driver/qca-wifi-host-cmn/os_if/linux/mlme/src/osif_cm_disconnect_rsp.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1*5113495bSYour Name /*
2*5113495bSYour Name  * Copyright (c) 2012-2015, 2020-2021 The Linux Foundation. All rights reserved.
3*5113495bSYour Name  * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4*5113495bSYour Name  *
5*5113495bSYour Name  * Permission to use, copy, modify, and/or distribute this software for any
6*5113495bSYour Name  * purpose with or without fee is hereby granted, provided that the above
7*5113495bSYour Name  * copyright notice and this permission notice appear in all copies.
8*5113495bSYour Name  *
9*5113495bSYour Name  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10*5113495bSYour Name  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11*5113495bSYour Name  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12*5113495bSYour Name  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13*5113495bSYour Name  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14*5113495bSYour Name  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15*5113495bSYour Name  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16*5113495bSYour Name  */
17*5113495bSYour Name 
18*5113495bSYour Name /**
19*5113495bSYour Name  * DOC: osif_cm_disconnect_rsp.c
20*5113495bSYour Name  *
21*5113495bSYour Name  * This file maintains definitaions of disconnect response
22*5113495bSYour Name  * functions.
23*5113495bSYour Name  */
24*5113495bSYour Name 
25*5113495bSYour Name #include <wlan_cfg80211.h>
26*5113495bSYour Name #include <linux/wireless.h>
27*5113495bSYour Name #include "osif_cm_rsp.h"
28*5113495bSYour Name #include "wlan_osif_priv.h"
29*5113495bSYour Name #include "osif_cm_util.h"
30*5113495bSYour Name #include "wlan_mlo_mgr_sta.h"
31*5113495bSYour Name 
32*5113495bSYour Name #define DRIVER_DISCONNECT_REASON \
33*5113495bSYour Name 	QCA_WLAN_VENDOR_ATTR_GET_STATION_INFO_DRIVER_DISCONNECT_REASON
34*5113495bSYour Name #define DRIVER_DISCONNECT_REASON_INDEX \
35*5113495bSYour Name 	QCA_NL80211_VENDOR_SUBCMD_DRIVER_DISCONNECT_REASON_INDEX
36*5113495bSYour Name /**
37*5113495bSYour Name  * osif_validate_disconnect_and_reset_src_id() - Validate disconnection
38*5113495bSYour Name  * and resets source and id
39*5113495bSYour Name  * @osif_priv: Pointer to vdev osif priv
40*5113495bSYour Name  * @rsp: Disconnect response from connectin manager
41*5113495bSYour Name  *
42*5113495bSYour Name  * This function validates disconnect response and if the disconnect
43*5113495bSYour Name  * response is valid, resets the source and id of the command
44*5113495bSYour Name  *
45*5113495bSYour Name  * Context: Any context. Takes and releases cmd id spinlock.
46*5113495bSYour Name  * Return: QDF_STATUS
47*5113495bSYour Name  */
48*5113495bSYour Name 
49*5113495bSYour Name static QDF_STATUS
osif_validate_disconnect_and_reset_src_id(struct vdev_osif_priv * osif_priv,struct wlan_cm_discon_rsp * rsp)50*5113495bSYour Name osif_validate_disconnect_and_reset_src_id(struct vdev_osif_priv *osif_priv,
51*5113495bSYour Name 					  struct wlan_cm_discon_rsp *rsp)
52*5113495bSYour Name {
53*5113495bSYour Name 	QDF_STATUS status = QDF_STATUS_SUCCESS;
54*5113495bSYour Name 
55*5113495bSYour Name 	/* Always drop internal disconnect */
56*5113495bSYour Name 	qdf_spinlock_acquire(&osif_priv->cm_info.cmd_id_lock);
57*5113495bSYour Name 	if (rsp->req.req.source == CM_INTERNAL_DISCONNECT ||
58*5113495bSYour Name 	    rsp->req.req.source == CM_MLO_ROAM_INTERNAL_DISCONNECT ||
59*5113495bSYour Name 	    ucfg_cm_is_link_switch_disconnect_resp(rsp)) {
60*5113495bSYour Name 		osif_debug("ignore internal disconnect");
61*5113495bSYour Name 		status = QDF_STATUS_E_INVAL;
62*5113495bSYour Name 		goto rel_lock;
63*5113495bSYour Name 	}
64*5113495bSYour Name 
65*5113495bSYour Name 	/*
66*5113495bSYour Name 	 * Send to kernel only if last osif cmd type is disconnect and
67*5113495bSYour Name 	 * cookie match else drop. If cookie match reset the cookie
68*5113495bSYour Name 	 * and source
69*5113495bSYour Name 	 */
70*5113495bSYour Name 	if (rsp->req.cm_id != osif_priv->cm_info.last_id ||
71*5113495bSYour Name 	    rsp->req.req.source != osif_priv->cm_info.last_source) {
72*5113495bSYour Name 		osif_debug("Ignore as cm_id(0x%x)/src(%d) didn't match stored cm_id(0x%x)/src(%d)",
73*5113495bSYour Name 			   rsp->req.cm_id, rsp->req.req.source,
74*5113495bSYour Name 			   osif_priv->cm_info.last_id,
75*5113495bSYour Name 			   osif_priv->cm_info.last_source);
76*5113495bSYour Name 		status = QDF_STATUS_E_INVAL;
77*5113495bSYour Name 		goto rel_lock;
78*5113495bSYour Name 	}
79*5113495bSYour Name 
80*5113495bSYour Name 	osif_cm_reset_id_and_src_no_lock(osif_priv);
81*5113495bSYour Name rel_lock:
82*5113495bSYour Name 	qdf_spinlock_release(&osif_priv->cm_info.cmd_id_lock);
83*5113495bSYour Name 
84*5113495bSYour Name 	return status;
85*5113495bSYour Name }
86*5113495bSYour Name 
87*5113495bSYour Name #if defined(CFG80211_DISCONNECTED_V2) || \
88*5113495bSYour Name (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0))
89*5113495bSYour Name #ifdef CONN_MGR_ADV_FEATURE
90*5113495bSYour Name static void
osif_cm_indicate_disconnect_result(struct net_device * dev,enum ieee80211_reasoncode reason,const u8 * ie,size_t ie_len,bool locally_generated,int link_id,gfp_t gfp)91*5113495bSYour Name osif_cm_indicate_disconnect_result(struct net_device *dev,
92*5113495bSYour Name 				   enum ieee80211_reasoncode reason,
93*5113495bSYour Name 				   const u8 *ie, size_t ie_len,
94*5113495bSYour Name 				   bool locally_generated, int link_id,
95*5113495bSYour Name 				   gfp_t gfp)
96*5113495bSYour Name {
97*5113495bSYour Name 	cfg80211_disconnected(dev, reason, ie,
98*5113495bSYour Name 			      ie_len, locally_generated, gfp);
99*5113495bSYour Name }
100*5113495bSYour Name #else
101*5113495bSYour Name #ifdef WLAN_SUPPORT_CFG80211_DISCONNECT_LINK_PARAM
102*5113495bSYour Name static void
osif_cm_indicate_disconnect_result(struct net_device * dev,enum ieee80211_reasoncode reason,const u8 * ie,size_t ie_len,bool locally_generated,int link_id,gfp_t gfp)103*5113495bSYour Name osif_cm_indicate_disconnect_result(struct net_device *dev,
104*5113495bSYour Name 				   enum ieee80211_reasoncode reason,
105*5113495bSYour Name 				   const u8 *ie, size_t ie_len,
106*5113495bSYour Name 				   bool locally_generated, int link_id,
107*5113495bSYour Name 				   gfp_t gfp)
108*5113495bSYour Name {
109*5113495bSYour Name 	cfg80211_disconnected(dev, reason, ie,
110*5113495bSYour Name 			      ie_len, locally_generated, link_id, gfp);
111*5113495bSYour Name }
112*5113495bSYour Name #else
113*5113495bSYour Name static void
osif_cm_indicate_disconnect_result(struct net_device * dev,enum ieee80211_reasoncode reason,const u8 * ie,size_t ie_len,bool locally_generated,int link_id,gfp_t gfp)114*5113495bSYour Name osif_cm_indicate_disconnect_result(struct net_device *dev,
115*5113495bSYour Name 				   enum ieee80211_reasoncode reason,
116*5113495bSYour Name 				   const u8 *ie, size_t ie_len,
117*5113495bSYour Name 				   bool locally_generated, int link_id,
118*5113495bSYour Name 				   gfp_t gfp)
119*5113495bSYour Name {
120*5113495bSYour Name 	cfg80211_disconnected(dev, reason, ie,
121*5113495bSYour Name 			      ie_len, locally_generated, gfp);
122*5113495bSYour Name }
123*5113495bSYour Name #endif /* WLAN_SUPPORT_CFG80211_DISCONNECT_LINK_PARAM */
124*5113495bSYour Name #endif
125*5113495bSYour Name 
126*5113495bSYour Name #ifdef WLAN_FEATURE_11BE_MLO
127*5113495bSYour Name #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
128*5113495bSYour Name void
osif_cm_indicate_disconnect(struct wlan_objmgr_vdev * vdev,struct net_device * dev,enum ieee80211_reasoncode reason,bool locally_generated,const u8 * ie,size_t ie_len,int link_id,gfp_t gfp)129*5113495bSYour Name osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
130*5113495bSYour Name 			    struct net_device *dev,
131*5113495bSYour Name 			    enum ieee80211_reasoncode reason,
132*5113495bSYour Name 			    bool locally_generated, const u8 *ie,
133*5113495bSYour Name 			    size_t ie_len, int link_id, gfp_t gfp)
134*5113495bSYour Name {
135*5113495bSYour Name 	if (wlan_vdev_mlme_is_mlo_vdev(vdev)) {
136*5113495bSYour Name 		if (!wlan_vdev_mlme_is_mlo_link_vdev(vdev))
137*5113495bSYour Name 			osif_cm_indicate_disconnect_result(
138*5113495bSYour Name 					dev, reason, ie,
139*5113495bSYour Name 					ie_len, locally_generated,
140*5113495bSYour Name 					link_id, gfp);
141*5113495bSYour Name 	} else {
142*5113495bSYour Name 		osif_cm_indicate_disconnect_result(
143*5113495bSYour Name 				dev, reason, ie,
144*5113495bSYour Name 				ie_len, locally_generated,
145*5113495bSYour Name 				link_id, gfp);
146*5113495bSYour Name 	}
147*5113495bSYour Name }
148*5113495bSYour Name #else /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
149*5113495bSYour Name 
150*5113495bSYour Name /**
151*5113495bSYour Name  * osif_cm_get_anchor_vdev() - API to get the anchor vdev
152*5113495bSYour Name  * @vdev: Pointer to vdev
153*5113495bSYour Name  *
154*5113495bSYour Name  * Return: If the assoc vdev is available, return it. Otherwise, if the MLD is
155*5113495bSYour Name  * disconnected, return the current vdev. If neither is available, return NULL.
156*5113495bSYour Name  */
osif_cm_get_anchor_vdev(struct wlan_objmgr_vdev * vdev)157*5113495bSYour Name static struct wlan_objmgr_vdev *osif_cm_get_anchor_vdev(
158*5113495bSYour Name 		struct wlan_objmgr_vdev *vdev)
159*5113495bSYour Name {
160*5113495bSYour Name 	struct wlan_objmgr_vdev *assoc_vdev = NULL;
161*5113495bSYour Name 
162*5113495bSYour Name 	assoc_vdev = ucfg_mlo_get_assoc_link_vdev(vdev);
163*5113495bSYour Name 	if (assoc_vdev)
164*5113495bSYour Name 		return assoc_vdev;
165*5113495bSYour Name 	else if (ucfg_mlo_is_mld_disconnected(vdev))
166*5113495bSYour Name 		return vdev;
167*5113495bSYour Name 	else
168*5113495bSYour Name 		return NULL;
169*5113495bSYour Name }
170*5113495bSYour Name 
171*5113495bSYour Name #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 213)) && \
172*5113495bSYour Name 	(LINUX_VERSION_CODE < KERNEL_VERSION(6, 0, 0))
173*5113495bSYour Name /**
174*5113495bSYour Name  * osif_cm_indicate_disconnect_for_non_assoc_link() - Wrapper API to clear
175*5113495bSYour Name  * current bss param of non-assoc link
176*5113495bSYour Name  * @netdev: Pointer to netdev of non-assoc link vdev
177*5113495bSYour Name  * @vdev: Pointer to non-assoc link vdev
178*5113495bSYour Name  *
179*5113495bSYour Name  * Return: None
180*5113495bSYour Name  */
osif_cm_indicate_disconnect_for_non_assoc_link(struct net_device * netdev,struct wlan_objmgr_vdev * vdev)181*5113495bSYour Name static void osif_cm_indicate_disconnect_for_non_assoc_link(
182*5113495bSYour Name 		struct net_device *netdev,
183*5113495bSYour Name 		struct wlan_objmgr_vdev *vdev)
184*5113495bSYour Name {
185*5113495bSYour Name 	int ret;
186*5113495bSYour Name 
187*5113495bSYour Name 	ret = cfg80211_clear_current_bss(netdev);
188*5113495bSYour Name 	if (ret)
189*5113495bSYour Name 		osif_err("cfg80211_clear_current_bss failed for psoc:%d pdev:%d vdev:%d",
190*5113495bSYour Name 			 wlan_vdev_get_psoc_id(vdev),
191*5113495bSYour Name 			 wlan_objmgr_pdev_get_pdev_id(wlan_vdev_get_pdev(vdev)),
192*5113495bSYour Name 			 wlan_vdev_get_id(vdev));
193*5113495bSYour Name }
194*5113495bSYour Name #else
osif_cm_indicate_disconnect_for_non_assoc_link(struct net_device * netdev,struct wlan_objmgr_vdev * vdev)195*5113495bSYour Name static void osif_cm_indicate_disconnect_for_non_assoc_link(
196*5113495bSYour Name 		struct net_device *netdev,
197*5113495bSYour Name 		struct wlan_objmgr_vdev *vdev)
198*5113495bSYour Name {
199*5113495bSYour Name }
200*5113495bSYour Name #endif
201*5113495bSYour Name 
202*5113495bSYour Name void
osif_cm_indicate_disconnect(struct wlan_objmgr_vdev * vdev,struct net_device * dev,enum ieee80211_reasoncode reason,bool locally_generated,const u8 * ie,size_t ie_len,int link_id,gfp_t gfp)203*5113495bSYour Name osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
204*5113495bSYour Name 			    struct net_device *dev,
205*5113495bSYour Name 			    enum ieee80211_reasoncode reason,
206*5113495bSYour Name 			    bool locally_generated, const u8 *ie,
207*5113495bSYour Name 			    size_t ie_len, int link_id, gfp_t gfp)
208*5113495bSYour Name {
209*5113495bSYour Name 	struct net_device *netdev = dev;
210*5113495bSYour Name 	struct vdev_osif_priv *osif_priv = NULL;
211*5113495bSYour Name 	struct wlan_objmgr_vdev *anchor_vdev;
212*5113495bSYour Name 
213*5113495bSYour Name 	if (!wlan_vdev_mlme_is_mlo_vdev(vdev) || (link_id != -1)) {
214*5113495bSYour Name 		osif_cm_indicate_disconnect_result(
215*5113495bSYour Name 				netdev, reason, ie, ie_len,
216*5113495bSYour Name 				locally_generated, link_id, gfp);
217*5113495bSYour Name 		return;
218*5113495bSYour Name 	}
219*5113495bSYour Name 
220*5113495bSYour Name 	anchor_vdev = osif_cm_get_anchor_vdev(vdev);
221*5113495bSYour Name 
222*5113495bSYour Name 	if (vdev != anchor_vdev)
223*5113495bSYour Name 		osif_cm_indicate_disconnect_for_non_assoc_link(netdev, vdev);
224*5113495bSYour Name 
225*5113495bSYour Name 	if (anchor_vdev && ucfg_mlo_is_mld_disconnected(vdev)) {
226*5113495bSYour Name 		/**
227*5113495bSYour Name 		 * Kernel maintains some extra state on the assoc netdev.
228*5113495bSYour Name 		 * If the assoc vdev exists, send disconnected event on the
229*5113495bSYour Name 		 * assoc netdev so that kernel cleans up the extra state.
230*5113495bSYour Name 		 * If the assoc vdev was already removed, kernel would have
231*5113495bSYour Name 		 * already cleaned up the extra state while processing the
232*5113495bSYour Name 		 * disconnected event sent as part of the link removal.
233*5113495bSYour Name 		 */
234*5113495bSYour Name 		osif_priv = wlan_vdev_get_ospriv(anchor_vdev);
235*5113495bSYour Name 		netdev = osif_priv->wdev->netdev;
236*5113495bSYour Name 
237*5113495bSYour Name 		osif_cm_indicate_disconnect_result(
238*5113495bSYour Name 				netdev, reason,
239*5113495bSYour Name 				ie, ie_len,
240*5113495bSYour Name 				locally_generated, link_id, gfp);
241*5113495bSYour Name 	}
242*5113495bSYour Name }
243*5113495bSYour Name #endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
244*5113495bSYour Name #else /* WLAN_FEATURE_11BE_MLO */
245*5113495bSYour Name void
osif_cm_indicate_disconnect(struct wlan_objmgr_vdev * vdev,struct net_device * dev,enum ieee80211_reasoncode reason,bool locally_generated,const u8 * ie,size_t ie_len,int link_id,gfp_t gfp)246*5113495bSYour Name osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
247*5113495bSYour Name 			    struct net_device *dev,
248*5113495bSYour Name 			    enum ieee80211_reasoncode reason,
249*5113495bSYour Name 			    bool locally_generated, const u8 *ie,
250*5113495bSYour Name 			    size_t ie_len, int link_id, gfp_t gfp)
251*5113495bSYour Name {
252*5113495bSYour Name 	osif_cm_indicate_disconnect_result(dev, reason, ie,
253*5113495bSYour Name 					   ie_len, locally_generated,
254*5113495bSYour Name 					   link_id, gfp);
255*5113495bSYour Name }
256*5113495bSYour Name #endif /* WLAN_FEATURE_11BE_MLO */
257*5113495bSYour Name #else
258*5113495bSYour Name void
osif_cm_indicate_disconnect(struct wlan_objmgr_vdev * vdev,struct net_device * dev,enum ieee80211_reasoncode reason,bool locally_generated,const u8 * ie,size_t ie_len,int link_id,gfp_t gfp)259*5113495bSYour Name osif_cm_indicate_disconnect(struct wlan_objmgr_vdev *vdev,
260*5113495bSYour Name 			    struct net_device *dev,
261*5113495bSYour Name 			    enum ieee80211_reasoncode reason,
262*5113495bSYour Name 			    bool locally_generated, const u8 *ie,
263*5113495bSYour Name 			    size_t ie_len, int link_id, gfp_t gfp)
264*5113495bSYour Name {
265*5113495bSYour Name 	cfg80211_disconnected(dev, reason, ie, ie_len, gfp);
266*5113495bSYour Name }
267*5113495bSYour Name #endif
268*5113495bSYour Name 
269*5113495bSYour Name static enum ieee80211_reasoncode
osif_cm_get_disconnect_reason(struct vdev_osif_priv * osif_priv,uint16_t reason)270*5113495bSYour Name osif_cm_get_disconnect_reason(struct vdev_osif_priv *osif_priv, uint16_t reason)
271*5113495bSYour Name {
272*5113495bSYour Name 	enum ieee80211_reasoncode ieee80211_reason = WLAN_REASON_UNSPECIFIED;
273*5113495bSYour Name 
274*5113495bSYour Name 	if (reason < REASON_PROP_START)
275*5113495bSYour Name 		ieee80211_reason = reason;
276*5113495bSYour Name 	/*
277*5113495bSYour Name 	 * Applications expect reason code as 0 for beacon miss failure
278*5113495bSYour Name 	 * due to backward compatibility. So send ieee80211_reason as 0.
279*5113495bSYour Name 	 */
280*5113495bSYour Name 	if (reason == REASON_BEACON_MISSED)
281*5113495bSYour Name 		ieee80211_reason = 0;
282*5113495bSYour Name 
283*5113495bSYour Name 	return ieee80211_reason;
284*5113495bSYour Name }
285*5113495bSYour Name 
286*5113495bSYour Name #ifdef CONN_MGR_ADV_FEATURE
287*5113495bSYour Name static inline bool
osif_is_disconnect_locally_generated(struct wlan_cm_discon_rsp * rsp)288*5113495bSYour Name osif_is_disconnect_locally_generated(struct wlan_cm_discon_rsp *rsp)
289*5113495bSYour Name {
290*5113495bSYour Name 	if (rsp->req.req.source == CM_PEER_DISCONNECT)
291*5113495bSYour Name 		return false;
292*5113495bSYour Name 
293*5113495bSYour Name 	return true;
294*5113495bSYour Name }
295*5113495bSYour Name #else
296*5113495bSYour Name static inline bool
osif_is_disconnect_locally_generated(struct wlan_cm_discon_rsp * rsp)297*5113495bSYour Name osif_is_disconnect_locally_generated(struct wlan_cm_discon_rsp *rsp)
298*5113495bSYour Name {
299*5113495bSYour Name 	if (rsp->req.req.source == CM_PEER_DISCONNECT ||
300*5113495bSYour Name 	    rsp->req.req.source == CM_SB_DISCONNECT)
301*5113495bSYour Name 		return false;
302*5113495bSYour Name 
303*5113495bSYour Name 	return true;
304*5113495bSYour Name }
305*5113495bSYour Name #endif
306*5113495bSYour Name 
307*5113495bSYour Name #ifdef CONN_MGR_ADV_FEATURE
308*5113495bSYour Name /**
309*5113495bSYour Name  * osif_cm_indicate_qca_reason: Send driver disconnect reason to user space
310*5113495bSYour Name  * @osif_priv: osif_priv pointer
311*5113495bSYour Name  * @qca_reason: qca disconnect reason codes
312*5113495bSYour Name  *
313*5113495bSYour Name  * Return: void
314*5113495bSYour Name  */
315*5113495bSYour Name 
316*5113495bSYour Name static void
osif_cm_indicate_qca_reason(struct vdev_osif_priv * osif_priv,enum qca_disconnect_reason_codes qca_reason)317*5113495bSYour Name osif_cm_indicate_qca_reason(struct vdev_osif_priv *osif_priv,
318*5113495bSYour Name 			    enum qca_disconnect_reason_codes qca_reason)
319*5113495bSYour Name {
320*5113495bSYour Name 	struct sk_buff *vendor_event;
321*5113495bSYour Name 
322*5113495bSYour Name 	vendor_event = wlan_cfg80211_vendor_event_alloc(
323*5113495bSYour Name 					osif_priv->wdev->wiphy, osif_priv->wdev,
324*5113495bSYour Name 					NLMSG_HDRLEN + sizeof(qca_reason) +
325*5113495bSYour Name 					NLMSG_HDRLEN,
326*5113495bSYour Name 					DRIVER_DISCONNECT_REASON_INDEX,
327*5113495bSYour Name 					GFP_KERNEL);
328*5113495bSYour Name 	if (!vendor_event) {
329*5113495bSYour Name 		osif_err("cfg80211_vendor_event_alloc failed");
330*5113495bSYour Name 		return;
331*5113495bSYour Name 	}
332*5113495bSYour Name 	if (nla_put_u32(vendor_event, DRIVER_DISCONNECT_REASON, qca_reason)) {
333*5113495bSYour Name 		osif_err("DISCONNECT_REASON put fail");
334*5113495bSYour Name 		kfree_skb(vendor_event);
335*5113495bSYour Name 		return;
336*5113495bSYour Name 	}
337*5113495bSYour Name 
338*5113495bSYour Name 	wlan_cfg80211_vendor_event(vendor_event, GFP_KERNEL);
339*5113495bSYour Name }
340*5113495bSYour Name #else
341*5113495bSYour Name static inline void
osif_cm_indicate_qca_reason(struct vdev_osif_priv * osif_priv,enum qca_disconnect_reason_codes qca_reason)342*5113495bSYour Name osif_cm_indicate_qca_reason(struct vdev_osif_priv *osif_priv,
343*5113495bSYour Name 			    enum qca_disconnect_reason_codes qca_reason)
344*5113495bSYour Name {
345*5113495bSYour Name }
346*5113495bSYour Name #endif
347*5113495bSYour Name 
osif_disconnect_handler(struct wlan_objmgr_vdev * vdev,struct wlan_cm_discon_rsp * rsp)348*5113495bSYour Name QDF_STATUS osif_disconnect_handler(struct wlan_objmgr_vdev *vdev,
349*5113495bSYour Name 				   struct wlan_cm_discon_rsp *rsp)
350*5113495bSYour Name {
351*5113495bSYour Name 	enum ieee80211_reasoncode ieee80211_reason;
352*5113495bSYour Name 	struct vdev_osif_priv *osif_priv = wlan_vdev_get_ospriv(vdev);
353*5113495bSYour Name 	bool locally_generated;
354*5113495bSYour Name 	QDF_STATUS status = QDF_STATUS_SUCCESS;
355*5113495bSYour Name 	enum qca_disconnect_reason_codes qca_reason;
356*5113495bSYour Name 	int link_id = -1;
357*5113495bSYour Name 
358*5113495bSYour Name 	qca_reason = osif_cm_mac_to_qca_reason(rsp->req.req.reason_code);
359*5113495bSYour Name 	ieee80211_reason =
360*5113495bSYour Name 		osif_cm_get_disconnect_reason(osif_priv,
361*5113495bSYour Name 					      rsp->req.req.reason_code);
362*5113495bSYour Name 
363*5113495bSYour Name 	locally_generated = osif_is_disconnect_locally_generated(rsp);
364*5113495bSYour Name 
365*5113495bSYour Name 	osif_nofl_info("%s(vdevid-%d): " QDF_MAC_ADDR_FMT " %s disconnect " QDF_MAC_ADDR_FMT " cmid 0x%x src %d reason:%u %s vendor:%u %s",
366*5113495bSYour Name 		       osif_priv->wdev->netdev->name,
367*5113495bSYour Name 		       rsp->req.req.vdev_id,
368*5113495bSYour Name 		       QDF_MAC_ADDR_REF(wlan_vdev_mlme_get_macaddr(vdev)),
369*5113495bSYour Name 		       locally_generated ? "locally-generated" : "",
370*5113495bSYour Name 		       QDF_MAC_ADDR_REF(rsp->req.req.bssid.bytes),
371*5113495bSYour Name 		       rsp->req.cm_id, rsp->req.req.source, ieee80211_reason,
372*5113495bSYour Name 		       ucfg_cm_reason_code_to_str(rsp->req.req.reason_code),
373*5113495bSYour Name 		       qca_reason,
374*5113495bSYour Name 		       osif_cm_qca_reason_to_str(qca_reason));
375*5113495bSYour Name 
376*5113495bSYour Name 	/* Unlink bss if disconnect is from peer or south bound */
377*5113495bSYour Name 	if (rsp->req.req.source == CM_PEER_DISCONNECT ||
378*5113495bSYour Name 	    rsp->req.req.source == CM_SB_DISCONNECT)
379*5113495bSYour Name 		osif_cm_unlink_bss(vdev, &rsp->req.req.bssid);
380*5113495bSYour Name 
381*5113495bSYour Name 	status = osif_validate_disconnect_and_reset_src_id(osif_priv, rsp);
382*5113495bSYour Name 	if (QDF_IS_STATUS_ERROR(status)) {
383*5113495bSYour Name 		osif_cm_disconnect_comp_ind(vdev, rsp, OSIF_NOT_HANDLED);
384*5113495bSYour Name 		return status;
385*5113495bSYour Name 	}
386*5113495bSYour Name 
387*5113495bSYour Name 	/* Send driver disconnect Reason */
388*5113495bSYour Name 	osif_cm_indicate_qca_reason(osif_priv, qca_reason);
389*5113495bSYour Name 
390*5113495bSYour Name 	/* If disconnect due to ML Reconfig, fill link id */
391*5113495bSYour Name 	if (rsp->req.req.reason_code == REASON_HOST_TRIGGERED_LINK_DELETE)
392*5113495bSYour Name 		link_id = wlan_vdev_get_link_id(vdev);
393*5113495bSYour Name 
394*5113495bSYour Name 	osif_cm_disconnect_comp_ind(vdev, rsp, OSIF_PRE_USERSPACE_UPDATE);
395*5113495bSYour Name 	osif_cm_indicate_disconnect(vdev, osif_priv->wdev->netdev,
396*5113495bSYour Name 				    ieee80211_reason,
397*5113495bSYour Name 				    locally_generated, rsp->ap_discon_ie.ptr,
398*5113495bSYour Name 				    rsp->ap_discon_ie.len,
399*5113495bSYour Name 				    link_id,
400*5113495bSYour Name 				    qdf_mem_malloc_flags());
401*5113495bSYour Name 
402*5113495bSYour Name 	osif_cm_disconnect_comp_ind(vdev, rsp, OSIF_POST_USERSPACE_UPDATE);
403*5113495bSYour Name 
404*5113495bSYour Name 	return status;
405*5113495bSYour Name }
406