xref: /wlan-dirver/qca-wifi-host-cmn/os_if/linux/mlme/src/osif_cm_disconnect_rsp.c (revision 97f44cd39e4ff816eaa1710279d28cf6b9e65ad9)
1 /*
2  * Copyright (c) 2012-2015, 2020 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 
30 /**
31  * osif_validate_disconnect_and_reset_src_id() - Validate disconnection
32  * and resets source and id
33  * @osif_priv: Pointer to vdev osif priv
34  * @rsp: Disconnect response from connectin manager
35  *
36  * This function validates disconnect response and if the disconnect
37  * response is valid, resets the source and id of the command
38  *
39  * Context: Any context. Takes and releases cmd id spinlock.
40  * Return: QDF_STATUS
41  */
42 
43 static QDF_STATUS
44 osif_validate_disconnect_and_reset_src_id(struct vdev_osif_priv *osif_priv,
45 					  struct wlan_cm_discon_rsp *rsp)
46 {
47 	QDF_STATUS status = QDF_STATUS_SUCCESS;
48 
49 	/* Always drop internal disconnect */
50 	qdf_spinlock_acquire(&osif_priv->cm_info.cmd_id_lock);
51 	if (rsp->req.req.source == CM_INTERNAL_DISCONNECT) {
52 		osif_debug("ignore internal disconnect");
53 		status = QDF_STATUS_E_INVAL;
54 		goto rel_lock;
55 	}
56 
57 	/*
58 	 * Send to kernel only if last osif cmd type is disconnect and
59 	 * cookie match else drop. If cookie match reset the cookie
60 	 * and source
61 	 */
62 	if (rsp->req.cm_id != osif_priv->cm_info.last_id ||
63 	    rsp->req.req.source != osif_priv->cm_info.last_source) {
64 		osif_debug("Ignore as cm_id(%d)/src(%d) didn't match stored cm_id(%d)/src(%d)",
65 			   rsp->req.cm_id, rsp->req.req.source,
66 			   osif_priv->cm_info.last_id,
67 			   osif_priv->cm_info.last_source);
68 		status = QDF_STATUS_E_INVAL;
69 		goto rel_lock;
70 	}
71 
72 	osif_cm_reset_id_and_src_no_lock(osif_priv);
73 rel_lock:
74 	qdf_spinlock_release(&osif_priv->cm_info.cmd_id_lock);
75 
76 	return status;
77 }
78 
79 #if defined(CFG80211_DISCONNECTED_V2) || \
80 (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0))
81 static void
82 osif_cm_indicate_disconnect(struct net_device *dev,
83 			    enum ieee80211_reasoncode reason,
84 			    bool locally_generated, const u8 *ie,
85 			    size_t ie_len, gfp_t gfp)
86 {
87 	cfg80211_disconnected(dev, reason, ie, ie_len, locally_generated, gfp);
88 }
89 #else
90 static void
91 osif_cm_indicate_disconnect(struct net_device *dev,
92 			    enum ieee80211_reasoncode reason,
93 			    bool locally_generated, const u8 *ie,
94 			    size_t ie_len, gfp_t gfp)
95 {
96 	cfg80211_disconnected(dev, reason, ie, ie_len, gfp);
97 }
98 #endif
99 
100 static enum ieee80211_reasoncode
101 osif_cm_get_disconnect_reason(struct vdev_osif_priv *osif_priv, uint16_t reason)
102 {
103 	enum ieee80211_reasoncode ieee80211_reason = WLAN_REASON_UNSPECIFIED;
104 
105 	if (reason < REASON_PROP_START)
106 		ieee80211_reason = reason;
107 	/*
108 	 * Applications expect reason code as 0 for beacon miss failure
109 	 * due to backward compatibility. So send ieee80211_reason as 0.
110 	 */
111 	if (reason == REASON_BEACON_MISSED)
112 		ieee80211_reason = 0;
113 
114 	return ieee80211_reason;
115 }
116 
117 QDF_STATUS osif_disconnect_handler(struct wlan_objmgr_vdev *vdev,
118 				   struct wlan_cm_discon_rsp *rsp)
119 {
120 	enum ieee80211_reasoncode ieee80211_reason;
121 	struct vdev_osif_priv *osif_priv = wlan_vdev_get_ospriv(vdev);
122 	bool locally_generated = true;
123 	QDF_STATUS status = QDF_STATUS_SUCCESS;
124 	enum qca_disconnect_reason_codes qca_reason;
125 
126 	qca_reason = osif_cm_mac_to_qca_reason(rsp->req.req.reason_code);
127 	ieee80211_reason =
128 		osif_cm_get_disconnect_reason(osif_priv,
129 					      rsp->req.req.reason_code);
130 
131 	osif_nofl_info("%s(vdevid-%d): " QDF_MAC_ADDR_FMT " %sdisconnect " QDF_MAC_ADDR_FMT " cm_id %d source %d reason:%u %s vendor:%u %s",
132 		       osif_priv->wdev->netdev->name,
133 		       rsp->req.req.vdev_id,
134 		       QDF_MAC_ADDR_REF(wlan_vdev_mlme_get_macaddr(vdev)),
135 		       locally_generated ? "locally-generated " : "",
136 		       QDF_MAC_ADDR_REF(rsp->req.req.bssid.bytes),
137 		       rsp->req.cm_id, rsp->req.req.source, ieee80211_reason,
138 		       ucfg_cm_reason_code_to_str(rsp->req.req.reason_code),
139 		       qca_reason,
140 		       osif_cm_qca_reason_to_str(qca_reason));
141 
142 	/* Unlink bss if disconnect is from peer or south bound */
143 	if (rsp->req.req.source == CM_PEER_DISCONNECT ||
144 	    rsp->req.req.source == CM_SB_DISCONNECT)
145 		osif_cm_unlink_bss(vdev, osif_priv, &rsp->req.req.bssid,
146 				   NULL, 0);
147 
148 	status = osif_validate_disconnect_and_reset_src_id(osif_priv, rsp);
149 	if (QDF_IS_STATUS_ERROR(status)) {
150 		osif_cm_disconnect_comp_ind(vdev, rsp, OSIF_NOT_HANDLED);
151 		return status;
152 	}
153 
154 	if (rsp->req.req.source == CM_PEER_DISCONNECT)
155 		locally_generated = false;
156 
157 	osif_cm_disconnect_comp_ind(vdev, rsp, OSIF_PRE_USERSPACE_UPDATE);
158 	osif_cm_indicate_disconnect(osif_priv->wdev->netdev, ieee80211_reason,
159 				    locally_generated, rsp->ap_discon_ie.ptr,
160 				    rsp->ap_discon_ie.len, GFP_KERNEL);
161 
162 	osif_cm_disconnect_comp_ind(vdev, rsp, OSIF_POST_USERSPACE_UPDATE);
163 
164 	return status;
165 }
166