1 /*
2  * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 /**
21  * DOC: wlan_hdd_sta_info.c
22  *
23  * Store and manage station info structure.
24  *
25  */
26 
27 #include <wlan_hdd_includes.h>
28 #include "wlan_hdd_sta_info.h"
29 
30 #define HDD_MAX_PEERS 32
31 
sta_info_string_from_dbgid(wlan_sta_info_dbgid id)32 char *sta_info_string_from_dbgid(wlan_sta_info_dbgid id)
33 {
34 	static const char *strings[] = {
35 				"STA_INFO_ID_RESERVED",
36 				"STA_INFO_CFG80211_GET_LINK_PROPERTIES",
37 				"STA_INFO_SOFTAP_INSPECT_TX_EAP_PKT",
38 				"STA_INFO_SOFTAP_CHECK_WAIT_FOR_TX_EAP_PKT",
39 				"STA_INFO_SOFTAP_INSPECT_DHCP_PACKET",
40 				"STA_INFO_SOFTAP_HARD_START_XMIT",
41 				"STA_INFO_SOFTAP_INIT_TX_RX_STA",
42 				"STA_INFO_SOFTAP_RX_PACKET_CBK",
43 				"STA_INFO_SOFTAP_REGISTER_STA",
44 				"STA_INFO_GET_CACHED_STATION_REMOTE",
45 				"STA_INFO_HDD_GET_STATION_REMOTE",
46 				"STA_INFO_WLAN_HDD_CFG80211_GET_STATION",
47 				"STA_INFO_SOFTAP_DEAUTH_CURRENT_STA",
48 				"STA_INFO_SOFTAP_DEAUTH_ALL_STA",
49 				"STA_INFO_CFG80211_DEL_STATION",
50 				"STA_INFO_HDD_CLEAR_ALL_STA",
51 				"STA_INFO_FILL_STATION_INFO",
52 				"STA_INFO_HOSTAPD_SAP_EVENT_CB",
53 				"STA_INFO_SAP_INDICATE_DISCONNECT_FOR_STA",
54 				"STA_INFO_IS_PEER_ASSOCIATED",
55 				"STA_INFO_SAP_SET_TWO_INTS_GETNONE",
56 				"STA_INFO_SAP_GETASSOC_STAMACADDR",
57 				"STA_INFO_SOFTAP_GET_STA_INFO",
58 				"STA_INFO_GET_SOFTAP_LINKSPEED",
59 				"STA_INFO_CONNECTION_IN_PROGRESS_ITERATOR",
60 				"STA_INFO_SOFTAP_STOP_BSS",
61 				"STA_INFO_SOFTAP_CHANGE_STA_STATE",
62 				"STA_INFO_CLEAR_CACHED_STA_INFO",
63 				"STA_INFO_ATTACH_DETACH",
64 				"STA_INFO_SHOW",
65 				"STA_INFO_SOFTAP_IPA_RX_PKT_CALLBACK",
66 				"STA_INFO_WLAN_HDD_CFG80211_DUMP_STATION",
67 				"STA_INFO_SON_GET_DATRATE_INFO",
68 				"STA_INFO_ID_MAX"};
69 	int32_t num_dbg_strings = QDF_ARRAY_SIZE(strings);
70 
71 	if (id >= num_dbg_strings) {
72 		char *ret = "";
73 
74 		hdd_err("Debug string not found for debug id %d", id);
75 		return ret;
76 	}
77 
78 	return (char *)strings[id];
79 }
80 
hdd_sta_info_init(struct hdd_sta_info_obj * sta_info_container)81 QDF_STATUS hdd_sta_info_init(struct hdd_sta_info_obj *sta_info_container)
82 {
83 	if (!sta_info_container) {
84 		hdd_err("Parameter null");
85 		return QDF_STATUS_E_INVAL;
86 	}
87 
88 	qdf_spinlock_create(&sta_info_container->sta_obj_lock);
89 	qdf_list_create(&sta_info_container->sta_obj, HDD_MAX_PEERS);
90 
91 	return QDF_STATUS_SUCCESS;
92 }
93 
hdd_sta_info_deinit(struct hdd_sta_info_obj * sta_info_container)94 void hdd_sta_info_deinit(struct hdd_sta_info_obj *sta_info_container)
95 {
96 	if (!sta_info_container) {
97 		hdd_err("Parameter null");
98 		return;
99 	}
100 
101 	qdf_list_destroy(&sta_info_container->sta_obj);
102 	qdf_spinlock_destroy(&sta_info_container->sta_obj_lock);
103 }
104 
hdd_sta_info_attach(struct hdd_sta_info_obj * sta_info_container,struct hdd_station_info * sta_info)105 QDF_STATUS hdd_sta_info_attach(struct hdd_sta_info_obj *sta_info_container,
106 			       struct hdd_station_info *sta_info)
107 {
108 	if (!sta_info_container || !sta_info) {
109 		hdd_err("Parameter(s) null");
110 		return QDF_STATUS_E_INVAL;
111 	}
112 
113 	qdf_spin_lock_bh(&sta_info_container->sta_obj_lock);
114 
115 	hdd_take_sta_info_ref(sta_info_container, sta_info, false,
116 			      STA_INFO_ATTACH_DETACH);
117 	qdf_list_insert_front(&sta_info_container->sta_obj,
118 			      &sta_info->sta_node);
119 	sta_info->is_attached = true;
120 
121 	qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock);
122 
123 	return QDF_STATUS_SUCCESS;
124 }
125 
hdd_sta_info_detach(struct hdd_sta_info_obj * sta_info_container,struct hdd_station_info ** sta_info)126 void hdd_sta_info_detach(struct hdd_sta_info_obj *sta_info_container,
127 			 struct hdd_station_info **sta_info)
128 {
129 	struct hdd_station_info *info;
130 
131 	if (!sta_info_container || !sta_info) {
132 		hdd_err("Parameter(s) null");
133 		return;
134 	}
135 
136 	info = *sta_info;
137 
138 	if (!info)
139 		return;
140 
141 	qdf_spin_lock_bh(&sta_info_container->sta_obj_lock);
142 
143 	if (info->is_attached) {
144 		info->is_attached = false;
145 		hdd_put_sta_info_ref(sta_info_container, sta_info, false,
146 				     STA_INFO_ATTACH_DETACH);
147 	} else {
148 		hdd_info("Stainfo is already detached");
149 	}
150 
151 	qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock);
152 }
153 
hdd_get_sta_info_by_id(struct hdd_sta_info_obj * sta_info_container,const int idx,wlan_sta_info_dbgid sta_info_dbgid)154 struct hdd_station_info *hdd_get_sta_info_by_id(
155 				struct hdd_sta_info_obj *sta_info_container,
156 				const int idx,
157 				wlan_sta_info_dbgid sta_info_dbgid)
158 {
159 	struct hdd_station_info *sta_info = NULL;
160 	int i = 0;
161 
162 	if (!sta_info_container) {
163 		hdd_err("Parameter(s) null");
164 		return NULL;
165 	}
166 
167 	qdf_spin_lock_bh(&sta_info_container->sta_obj_lock);
168 
169 	qdf_list_for_each(&sta_info_container->sta_obj, sta_info, sta_node) {
170 		if (qdf_is_macaddr_broadcast(&sta_info->sta_mac))
171 			continue;
172 		if (i == idx) {
173 			hdd_take_sta_info_ref(sta_info_container,
174 					      sta_info, false, sta_info_dbgid);
175 			qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock);
176 			return sta_info;
177 		}
178 		i++;
179 	}
180 
181 	qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock);
182 
183 	return NULL;
184 }
185 
hdd_get_sta_info_by_mac(struct hdd_sta_info_obj * sta_info_container,const uint8_t * mac_addr,wlan_sta_info_dbgid sta_info_dbgid)186 struct hdd_station_info *hdd_get_sta_info_by_mac(
187 				struct hdd_sta_info_obj *sta_info_container,
188 				const uint8_t *mac_addr,
189 				wlan_sta_info_dbgid sta_info_dbgid)
190 {
191 	struct hdd_station_info *sta_info = NULL;
192 
193 	if (!mac_addr || !sta_info_container ||
194 	    qdf_is_macaddr_zero((struct qdf_mac_addr *)mac_addr)) {
195 		hdd_err("Parameter(s) null");
196 		return NULL;
197 	}
198 
199 	qdf_spin_lock_bh(&sta_info_container->sta_obj_lock);
200 
201 	qdf_list_for_each(&sta_info_container->sta_obj, sta_info, sta_node) {
202 		if (qdf_is_macaddr_equal(&sta_info->sta_mac,
203 					 (struct qdf_mac_addr *)mac_addr) ||
204 		    qdf_is_macaddr_equal(&sta_info->mld_addr,
205 					 (struct qdf_mac_addr *)mac_addr)) {
206 			hdd_take_sta_info_ref(sta_info_container,
207 					      sta_info, false, sta_info_dbgid);
208 			qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock);
209 			return sta_info;
210 		}
211 	}
212 
213 	qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock);
214 
215 	return NULL;
216 }
217 
hdd_take_sta_info_ref(struct hdd_sta_info_obj * sta_info_container,struct hdd_station_info * sta_info,bool lock_required,wlan_sta_info_dbgid sta_info_dbgid)218 void hdd_take_sta_info_ref(struct hdd_sta_info_obj *sta_info_container,
219 			   struct hdd_station_info *sta_info,
220 			   bool lock_required,
221 			   wlan_sta_info_dbgid sta_info_dbgid)
222 {
223 	if (!sta_info_container || !sta_info) {
224 		hdd_err("Parameter(s) null");
225 		return;
226 	}
227 
228 	if (sta_info_dbgid >= STA_INFO_ID_MAX) {
229 		hdd_err("Invalid sta_info debug id %d", sta_info_dbgid);
230 		return;
231 	}
232 
233 	if (lock_required)
234 		qdf_spin_lock_bh(&sta_info_container->sta_obj_lock);
235 
236 	qdf_atomic_inc(&sta_info->ref_cnt);
237 	qdf_atomic_inc(&sta_info->ref_cnt_dbgid[sta_info_dbgid]);
238 
239 	if (lock_required)
240 		qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock);
241 }
242 
243 void
hdd_put_sta_info_ref(struct hdd_sta_info_obj * sta_info_container,struct hdd_station_info ** sta_info,bool lock_required,wlan_sta_info_dbgid sta_info_dbgid)244 hdd_put_sta_info_ref(struct hdd_sta_info_obj *sta_info_container,
245 		     struct hdd_station_info **sta_info, bool lock_required,
246 		     wlan_sta_info_dbgid sta_info_dbgid)
247 {
248 	struct hdd_station_info *info;
249 	struct qdf_mac_addr addr;
250 
251 	if (!sta_info_container || !sta_info) {
252 		hdd_err("Parameter(s) null");
253 		return;
254 	}
255 
256 	info = *sta_info;
257 
258 	if (!info) {
259 		hdd_err("station info NULL");
260 		return;
261 	}
262 
263 	if (sta_info_dbgid >= STA_INFO_ID_MAX) {
264 		hdd_err("Invalid sta_info debug id %d", sta_info_dbgid);
265 		return;
266 	}
267 
268 	if (lock_required)
269 		qdf_spin_lock_bh(&sta_info_container->sta_obj_lock);
270 
271 	/*
272 	 * In case the put_ref is called more than twice for a single take_ref,
273 	 * this will result in either a BUG or page fault. In both the cases,
274 	 * the root cause would be known and the buggy put_ref can be taken
275 	 * care of.
276 	 */
277 	if (!qdf_atomic_read(&info->ref_cnt_dbgid[sta_info_dbgid])) {
278 		hdd_err("Sta_info ref count put is detected without get for debug id %s",
279 			sta_info_string_from_dbgid(sta_info_dbgid));
280 
281 		QDF_BUG(0);
282 	}
283 
284 	qdf_atomic_dec(&info->ref_cnt);
285 	qdf_atomic_dec(&info->ref_cnt_dbgid[sta_info_dbgid]);
286 
287 	if (qdf_atomic_read(&info->ref_cnt)) {
288 		if (lock_required)
289 			qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock);
290 		return;
291 	}
292 
293 	qdf_copy_macaddr(&addr, &info->sta_mac);
294 	if (info->assoc_req_ies.len) {
295 		qdf_mem_free(info->assoc_req_ies.ptr);
296 		info->assoc_req_ies.ptr = NULL;
297 		info->assoc_req_ies.len = 0;
298 	}
299 
300 	qdf_list_remove_node(&sta_info_container->sta_obj, &info->sta_node);
301 	qdf_mem_free(info);
302 	*sta_info = NULL;
303 
304 	if (lock_required)
305 		qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock);
306 
307 	hdd_nofl_debug("STA_INFO: " QDF_MAC_ADDR_FMT " freed",
308 		       QDF_MAC_ADDR_REF(addr.bytes));
309 }
310 
hdd_clear_cached_sta_info(struct hdd_adapter * adapter)311 void hdd_clear_cached_sta_info(struct hdd_adapter *adapter)
312 {
313 	struct hdd_station_info *sta_info = NULL, *tmp = NULL;
314 
315 	if (!adapter) {
316 		hdd_err("Parameter null");
317 		return;
318 	}
319 
320 	hdd_for_each_sta_ref_safe(adapter->cache_sta_info_list, sta_info, tmp,
321 				  STA_INFO_CLEAR_CACHED_STA_INFO) {
322 		hdd_sta_info_detach(&adapter->cache_sta_info_list, &sta_info);
323 		hdd_put_sta_info_ref(&adapter->cache_sta_info_list, &sta_info,
324 				     true, STA_INFO_CLEAR_CACHED_STA_INFO);
325 	}
326 }
327 
328 QDF_STATUS
hdd_get_front_sta_info_no_lock(struct hdd_sta_info_obj * sta_info_container,struct hdd_station_info ** out_sta_info)329 hdd_get_front_sta_info_no_lock(struct hdd_sta_info_obj *sta_info_container,
330 			       struct hdd_station_info **out_sta_info)
331 {
332 	QDF_STATUS status;
333 	qdf_list_node_t *node;
334 
335 	*out_sta_info = NULL;
336 
337 	status = qdf_list_peek_front(&sta_info_container->sta_obj, &node);
338 
339 	if (QDF_IS_STATUS_ERROR(status))
340 		return status;
341 
342 	*out_sta_info =
343 		qdf_container_of(node, struct hdd_station_info, sta_node);
344 
345 	return QDF_STATUS_SUCCESS;
346 }
347 
348 QDF_STATUS
hdd_get_next_sta_info_no_lock(struct hdd_sta_info_obj * sta_info_container,struct hdd_station_info * current_sta_info,struct hdd_station_info ** out_sta_info)349 hdd_get_next_sta_info_no_lock(struct hdd_sta_info_obj *sta_info_container,
350 			      struct hdd_station_info *current_sta_info,
351 			      struct hdd_station_info **out_sta_info)
352 {
353 	QDF_STATUS status;
354 	qdf_list_node_t *node;
355 
356 	if (!current_sta_info)
357 		return QDF_STATUS_E_INVAL;
358 
359 	*out_sta_info = NULL;
360 
361 	status = qdf_list_peek_next(&sta_info_container->sta_obj,
362 				    &current_sta_info->sta_node,
363 				    &node);
364 
365 	if (QDF_IS_STATUS_ERROR(status))
366 		return status;
367 
368 	*out_sta_info =
369 		qdf_container_of(node, struct hdd_station_info, sta_node);
370 
371 	return QDF_STATUS_SUCCESS;
372 }
373 
374