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 ¤t_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