1  /*
2   * Copyright (c) 2012-2015, 2020-2021, The Linux Foundation. All rights reserved.
3   * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
4   *
5   * Permission to use, copy, modify, and/or distribute this software for any
6   * purpose with or without fee is hereby granted, provided that the above
7   * copyright notice and this permission notice appear in all copies.
8   *
9   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16   */
17  
18  /**
19   * DOC: Implements init/deinit specific apis of connection manager
20   */
21  
22  #include "wlan_cm_main.h"
23  #include "wlan_cm_roam.h"
24  #include "wlan_cm_main_api.h"
25  #include "wlan_scan_api.h"
26  #include "wlan_mlo_mgr_link_switch.h"
27  
28  #ifdef WLAN_CM_USE_SPINLOCK
29  /**
30   * cm_req_lock_create - Create CM req SM mutex/spinlock
31   * @cm_ctx:  connection manager ctx
32   *
33   * Creates CM SM mutex/spinlock
34   *
35   * Return: void
36   */
37  static inline void
cm_req_lock_create(struct cnx_mgr * cm_ctx)38  cm_req_lock_create(struct cnx_mgr *cm_ctx)
39  {
40  	qdf_spinlock_create(&cm_ctx->cm_req_lock);
41  }
42  
43  /**
44   * cm_req_lock_destroy - Destroy CM SM mutex/spinlock
45   * @cm_ctx:  connection manager ctx
46   *
47   * Destroy CM SM mutex/spinlock
48   *
49   * Return: void
50   */
51  static inline void
cm_req_lock_destroy(struct cnx_mgr * cm_ctx)52  cm_req_lock_destroy(struct cnx_mgr *cm_ctx)
53  {
54  	qdf_spinlock_destroy(&cm_ctx->cm_req_lock);
55  }
56  #else
57  static inline void
cm_req_lock_create(struct cnx_mgr * cm_ctx)58  cm_req_lock_create(struct cnx_mgr *cm_ctx)
59  {
60  	qdf_mutex_create(&cm_ctx->cm_req_lock);
61  }
62  
63  static inline void
cm_req_lock_destroy(struct cnx_mgr * cm_ctx)64  cm_req_lock_destroy(struct cnx_mgr *cm_ctx)
65  {
66  	qdf_mutex_destroy(&cm_ctx->cm_req_lock);
67  }
68  #endif /* WLAN_CM_USE_SPINLOCK */
69  
wlan_cm_init(struct vdev_mlme_obj * vdev_mlme)70  QDF_STATUS wlan_cm_init(struct vdev_mlme_obj *vdev_mlme)
71  {
72  	struct wlan_objmgr_vdev *vdev = vdev_mlme->vdev;
73  	enum QDF_OPMODE op_mode = wlan_vdev_mlme_get_opmode(vdev);
74  	struct wlan_objmgr_psoc *psoc = wlan_vdev_get_psoc(vdev);
75  	QDF_STATUS status;
76  
77  	if (op_mode != QDF_STA_MODE && op_mode != QDF_P2P_CLIENT_MODE)
78  		return QDF_STATUS_SUCCESS;
79  
80  	vdev_mlme->cnx_mgr_ctx = qdf_mem_malloc(sizeof(struct cnx_mgr));
81  	if (!vdev_mlme->cnx_mgr_ctx)
82  		return QDF_STATUS_E_NOMEM;
83  
84  	vdev_mlme->cnx_mgr_ctx->vdev = vdev;
85  	status = mlme_cm_ext_hdl_create(vdev,
86  					&vdev_mlme->cnx_mgr_ctx->ext_cm_ptr);
87  	if (QDF_IS_STATUS_ERROR(status)) {
88  		qdf_mem_free(vdev_mlme->cnx_mgr_ctx);
89  		vdev_mlme->cnx_mgr_ctx = NULL;
90  		return status;
91  	}
92  
93  	status = cm_sm_create(vdev_mlme->cnx_mgr_ctx);
94  	if (QDF_IS_STATUS_ERROR(status)) {
95  		mlme_cm_ext_hdl_destroy(vdev,
96  					vdev_mlme->cnx_mgr_ctx->ext_cm_ptr);
97  		vdev_mlme->cnx_mgr_ctx->ext_cm_ptr = NULL;
98  		qdf_mem_free(vdev_mlme->cnx_mgr_ctx);
99  		vdev_mlme->cnx_mgr_ctx = NULL;
100  		return status;
101  	}
102  	vdev_mlme->cnx_mgr_ctx->max_connect_attempts =
103  					CM_MAX_CONNECT_ATTEMPTS;
104  	vdev_mlme->cnx_mgr_ctx->connect_timeout =
105  					CM_MAX_PER_CANDIDATE_CONNECT_TIMEOUT;
106  	qdf_list_create(&vdev_mlme->cnx_mgr_ctx->req_list, CM_MAX_REQ);
107  	cm_req_lock_create(vdev_mlme->cnx_mgr_ctx);
108  
109  	vdev_mlme->cnx_mgr_ctx->scan_requester_id =
110  		wlan_scan_register_requester(psoc,
111  					     "CM",
112  					     wlan_cm_scan_cb,
113  					     vdev_mlme->cnx_mgr_ctx);
114  	qdf_event_create(&vdev_mlme->cnx_mgr_ctx->disconnect_complete);
115  	cm_req_history_init(vdev_mlme->cnx_mgr_ctx);
116  
117  	return QDF_STATUS_SUCCESS;
118  }
119  
cm_deinit_req_list(struct cnx_mgr * cm_ctx)120  static void cm_deinit_req_list(struct cnx_mgr *cm_ctx)
121  {
122  	uint32_t prefix;
123  	qdf_list_node_t *cur_node = NULL, *next_node = NULL;
124  	struct cm_req *cm_req = NULL;
125  
126  	/*
127  	 * flush unhandled req from the list, this should not happen if SM is
128  	 * handled properly, but in cases of active command timeout
129  	 * (which needs be debugged and avoided anyway) if VDEV/PEER SM
130  	 * is not able to handle the req it may send out of sync command and
131  	 * thus resulting in a unhandled request. Thus to avoid memleak flush
132  	 * all unhandled req before destroying the list.
133  	 */
134  	cm_req_lock_acquire(cm_ctx);
135  	qdf_list_peek_front(&cm_ctx->req_list, &cur_node);
136  	while (cur_node) {
137  		qdf_list_peek_next(&cm_ctx->req_list, cur_node, &next_node);
138  
139  		cm_req = qdf_container_of(cur_node, struct cm_req, node);
140  		prefix = CM_ID_GET_PREFIX(cm_req->cm_id);
141  		qdf_list_remove_node(&cm_ctx->req_list, &cm_req->node);
142  		mlme_info(CM_PREFIX_FMT "flush prefix %x",
143  			  CM_PREFIX_REF(wlan_vdev_get_id(cm_ctx->vdev),
144  					cm_req->cm_id), prefix);
145  		if (prefix == CONNECT_REQ_PREFIX) {
146  			cm_ctx->connect_count--;
147  			cm_free_connect_req_mem(&cm_req->connect_req);
148  		} else if (prefix == ROAM_REQ_PREFIX) {
149  			cm_free_roam_req_mem(&cm_req->roam_req);
150  		} else if (prefix == DISCONNECT_REQ_PREFIX) {
151  			cm_ctx->disconnect_count--;
152  		}
153  		qdf_mem_free(cm_req);
154  
155  		cur_node = next_node;
156  		next_node = NULL;
157  		cm_req = NULL;
158  	}
159  	cm_req_lock_release(cm_ctx);
160  
161  	cm_req_lock_destroy(cm_ctx);
162  	qdf_list_destroy(&cm_ctx->req_list);
163  }
164  
wlan_cm_deinit(struct vdev_mlme_obj * vdev_mlme)165  QDF_STATUS wlan_cm_deinit(struct vdev_mlme_obj *vdev_mlme)
166  {
167  	struct wlan_objmgr_vdev *vdev = vdev_mlme->vdev;
168  	enum QDF_OPMODE op_mode = wlan_vdev_mlme_get_opmode(vdev);
169  	struct wlan_objmgr_psoc *psoc = wlan_vdev_get_psoc(vdev);
170  	wlan_scan_requester scan_requester_id;
171  	struct cnx_mgr *cm_ctx;
172  
173  	if (op_mode != QDF_STA_MODE && op_mode != QDF_P2P_CLIENT_MODE)
174  		return QDF_STATUS_SUCCESS;
175  
176  	cm_ctx = vdev_mlme->cnx_mgr_ctx;
177  	cm_req_history_deinit(cm_ctx);
178  	qdf_event_destroy(&cm_ctx->disconnect_complete);
179  	scan_requester_id = cm_ctx->scan_requester_id;
180  	wlan_scan_unregister_requester(psoc, scan_requester_id);
181  
182  	cm_deinit_req_list(cm_ctx);
183  	cm_sm_destroy(cm_ctx);
184  	mlme_cm_ext_hdl_destroy(vdev, cm_ctx->ext_cm_ptr);
185  	cm_ctx->ext_cm_ptr = NULL;
186  	qdf_mem_free(cm_ctx);
187  	vdev_mlme->cnx_mgr_ctx = NULL;
188  
189  	return QDF_STATUS_SUCCESS;
190  }
191  
192  #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
cm_standby_link_update_mlme_by_bssid(struct wlan_objmgr_vdev * vdev,uint32_t assoc_state,struct wlan_ssid ssid)193  void cm_standby_link_update_mlme_by_bssid(struct wlan_objmgr_vdev *vdev,
194  					  uint32_t assoc_state,
195  					  struct wlan_ssid ssid)
196  {
197  	struct mlo_link_info *link_info;
198  	uint8_t link_info_iter;
199  	struct mlme_info mlme_info;
200  	struct bss_info bss_info;
201  
202  	if (!wlan_vdev_mlme_is_assoc_sta_vdev(vdev))
203  		return;
204  
205  	link_info = mlo_mgr_get_ap_link(vdev);
206  	if (!link_info)
207  		return;
208  
209  	for (link_info_iter = 0; link_info_iter < 3; link_info_iter++) {
210  		if (qdf_is_macaddr_zero(&link_info->ap_link_addr))
211  			break;
212  
213  		if (link_info->vdev_id == WLAN_INVALID_VDEV_ID) {
214  			mlme_info.assoc_state = assoc_state;
215  			qdf_copy_macaddr(&bss_info.bssid,
216  					 &link_info->ap_link_addr);
217  			bss_info.freq = link_info->link_chan_info->ch_freq;
218  			bss_info.ssid.length = ssid.length;
219  			qdf_mem_copy(&bss_info.ssid.ssid, ssid.ssid,
220  				     bss_info.ssid.length);
221  
222  			wlan_scan_update_mlme_by_bssinfo(wlan_vdev_get_pdev(vdev),
223  							 &bss_info, &mlme_info);
224  		}
225  
226  		link_info++;
227  	}
228  }
229  #endif
230