1  /*
2   * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
3   * Copyright (c) 2022-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: wlan_vdev_mlme_ser.c
20   * This file contains the APIs to support interface between vdev_mlme and
21   * serialization module
22   */
23  
24  #include <qdf_types.h>
25  #include <qdf_status.h>
26  #include <qdf_mem.h>
27  #include <wlan_serialization_api.h>
28  #include <wlan_objmgr_vdev_obj.h>
29  #include <wlan_cmn.h>
30  #include <wlan_mlme_dbg.h>
31  #include <include/wlan_mlme_cmn.h>
32  #include <wlan_vdev_mlme_api.h>
33  #include <wlan_vdev_mlme_ser_if.h>
34  
35  enum wlan_serialization_status
wlan_vdev_mlme_ser_start_bss(struct wlan_serialization_command * cmd)36  wlan_vdev_mlme_ser_start_bss(struct wlan_serialization_command *cmd)
37  {
38  	struct vdev_mlme_obj *vdev_mlme;
39  
40  	if (!cmd || !cmd->vdev) {
41  		mlme_err("Null input");
42  		return WLAN_SER_CMD_DENIED_UNSPECIFIED;
43  	}
44  
45  	if (!wlan_ser_is_vdev_queue_enabled(cmd->vdev))
46  		return WLAN_SER_CMD_QUEUE_DISABLED;
47  	/*
48  	 * Serialization command filtering logic
49  	 * a. Cancel any existing start bss cmd in the pending queue
50  	 * b. If there is an start bss cmd in active queue and
51  	 * there is no stop bss cmd in pending queue,
52  	 * then explicitly enqueue a stop bss cmd to avoid back to
53  	 * back execution of UP cmd.
54  	 * c. Enqueue the new start bss cmd with serialization
55  	 */
56  	wlan_vdev_mlme_ser_cancel_request(
57  			cmd->vdev,
58  			WLAN_SER_CMD_VDEV_START_BSS,
59  			WLAN_SER_CANCEL_VDEV_NON_SCAN_CMD_TYPE);
60  
61  	if (wlan_serialization_is_cmd_present_in_active_queue(NULL, cmd)) {
62  		vdev_mlme = wlan_vdev_mlme_get_cmpt_obj(cmd->vdev);
63  		if (mlme_vdev_enqueue_exp_ser_cmd(vdev_mlme,
64  						  WLAN_SER_CMD_VDEV_STOP_BSS)) {
65  			mlme_err("Unable to add the exception cmd request");
66  			return WLAN_SER_CMD_DENIED_UNSPECIFIED;
67  		}
68  	}
69  
70  	return wlan_serialization_request(cmd);
71  }
72  
73  enum wlan_serialization_status
wlan_vdev_mlme_ser_stop_bss(struct wlan_serialization_command * cmd)74  wlan_vdev_mlme_ser_stop_bss(struct wlan_serialization_command *cmd)
75  {
76  	uint8_t stop_cmd_pending;
77  	uint8_t ret;
78  
79  	if (!cmd || !cmd->vdev) {
80  		mlme_err("Null input");
81  		return WLAN_SER_CMD_DENIED_UNSPECIFIED;
82  	}
83  
84  	if (!wlan_ser_is_vdev_queue_enabled(cmd->vdev))
85  		return WLAN_SER_CMD_QUEUE_DISABLED;
86  	/*
87  	 * Serialization command filtering logic
88  	 * a. Cancel any existing start/stop/restart command in the pending
89  	 *  queue.
90  	 * b. If there is a stop cmd in active queue then return
91  	 * c. Else enqueue the cmd
92  	 * d. If stop cmd already existed in pending queue then return with
93  	 *  already exists else return the enqueued return value.
94  	 */
95  	stop_cmd_pending =
96  		wlan_serialization_is_cmd_present_in_pending_queue(NULL, cmd);
97  	wlan_vdev_mlme_ser_cancel_request(cmd->vdev,
98  					  WLAN_SER_CMD_NONSCAN,
99  					  WLAN_SER_CANCEL_VDEV_NON_SCAN_NB_CMD);
100  
101  	if (wlan_serialization_is_cmd_present_in_active_queue(NULL, cmd)) {
102  		mlme_debug("Cmd already exist in the active queue");
103  		return WLAN_SER_CMD_DENIED_UNSPECIFIED;
104  	}
105  
106  	ret = wlan_serialization_request(cmd);
107  
108  	if (stop_cmd_pending && ret == WLAN_SER_CMD_PENDING)
109  		return WLAN_SER_CMD_ALREADY_EXISTS;
110  	else
111  		return ret;
112  }
113  
114  enum wlan_serialization_status
wlan_vdev_mlme_ser_vdev_restart(struct wlan_serialization_command * cmd)115  wlan_vdev_mlme_ser_vdev_restart(struct wlan_serialization_command *cmd)
116  {
117  	if (!cmd || !cmd->vdev) {
118  		mlme_err("Null input");
119  		return WLAN_SER_CMD_DENIED_UNSPECIFIED;
120  	}
121  
122  	if (!wlan_ser_is_vdev_queue_enabled(cmd->vdev))
123  		return WLAN_SER_CMD_QUEUE_DISABLED;
124  	/*
125  	 * Serialization command filtering logic
126  	 * a. If there exists START or PDEV/VDEV restart command in the pending
127  	 * queue then ignore this new vdev restart request.
128  	 * b. Else enqueue the new VDEV RESTART cmd
129  	 */
130  	cmd->cmd_type = WLAN_SER_CMD_VDEV_START_BSS;
131  	if (wlan_serialization_is_cmd_present_in_pending_queue(NULL, cmd)) {
132  		mlme_debug("Start cmd already in the pending queue");
133  		return WLAN_SER_CMD_ALREADY_EXISTS;
134  	}
135  
136  	cmd->cmd_type = WLAN_SER_CMD_PDEV_RESTART;
137  	if (wlan_serialization_is_cmd_present_in_pending_queue(NULL, cmd)) {
138  		mlme_debug("Pdev restart already in the pending queue");
139  		return WLAN_SER_CMD_ALREADY_EXISTS;
140  	}
141  
142  	cmd->cmd_type = WLAN_SER_CMD_VDEV_RESTART;
143  	if (wlan_serialization_is_cmd_present_in_pending_queue(NULL, cmd)) {
144  		mlme_debug("Vdev restart already in the pending queue");
145  		return WLAN_SER_CMD_ALREADY_EXISTS;
146  	}
147  
148  	return wlan_serialization_request(cmd);
149  }
150  
wlan_mlme_restart_pdev_iter_cb(struct wlan_objmgr_pdev * pdev,void * object,void * arg)151  static void wlan_mlme_restart_pdev_iter_cb(struct wlan_objmgr_pdev *pdev,
152  					   void *object, void *arg)
153  {
154  	struct wlan_objmgr_vdev *vdev = (struct wlan_objmgr_vdev *)object;
155  	uint8_t *pdev_restart_pending = (uint8_t *)arg;
156  	struct wlan_serialization_command cmd = {0};
157  	uint8_t vdev_id = wlan_vdev_get_id(vdev);
158  
159  	cmd.vdev = vdev;
160  	cmd.cmd_id = vdev_id;
161  	cmd.cmd_type = WLAN_SER_CMD_PDEV_RESTART;
162  	/*
163  	 * Serialization command filtering logic
164  	 * a. Cancel any existing VDEV restart cmd in the pending queue
165  	 * b. If Pdev restart already exist in pending queue then return else
166  	 * enqueue the new PDEV RESTART cmd
167  	 */
168  	wlan_vdev_mlme_ser_cancel_request(
169  			vdev,
170  			WLAN_SER_CMD_VDEV_RESTART,
171  			WLAN_SER_CANCEL_VDEV_NON_SCAN_CMD_TYPE);
172  
173  	if (wlan_serialization_is_cmd_present_in_pending_queue(NULL, &cmd)) {
174  		mlme_debug("Cmd already exist in the pending queue vdev:%u",
175  			   vdev_id);
176  		*pdev_restart_pending = 1;
177  	}
178  }
179  
180  enum wlan_serialization_status
wlan_vdev_mlme_ser_pdev_restart(struct wlan_serialization_command * cmd)181  wlan_vdev_mlme_ser_pdev_restart(struct wlan_serialization_command *cmd)
182  {
183  	struct wlan_objmgr_pdev *pdev;
184  	uint8_t pdev_restart_in_pending = 0;
185  
186  	if (!cmd || !cmd->vdev) {
187  		mlme_err("Null input");
188  		return WLAN_SER_CMD_DENIED_UNSPECIFIED;
189  	}
190  
191  	if (!wlan_ser_is_vdev_queue_enabled(cmd->vdev))
192  		return WLAN_SER_CMD_QUEUE_DISABLED;
193  
194  	pdev = wlan_vdev_get_pdev(cmd->vdev);
195  	wlan_objmgr_pdev_iterate_obj_list(pdev, WLAN_VDEV_OP,
196  					  wlan_mlme_restart_pdev_iter_cb,
197  					  &pdev_restart_in_pending, 0,
198  					  WLAN_MLME_SER_IF_ID);
199  
200  	if (pdev_restart_in_pending)
201  		return WLAN_SER_CMD_ALREADY_EXISTS;
202  
203  	return wlan_serialization_request(cmd);
204  }
205  
206  static void
wlan_mlme_cancel_pending_csa_restart(struct wlan_objmgr_pdev * pdev,void * object,void * arg)207  wlan_mlme_cancel_pending_csa_restart(struct wlan_objmgr_pdev *pdev,
208  				     void *object, void *arg)
209  {
210  	struct wlan_objmgr_vdev *vdev = object;
211  	bool *csa_restart_pending = arg;
212  	struct wlan_serialization_command cmd = {0};
213  	uint8_t vdev_id = wlan_vdev_get_id(vdev);
214  
215  	cmd.vdev = vdev;
216  	cmd.cmd_id = vdev_id;
217  	cmd.cmd_type = WLAN_SER_CMD_PDEV_CSA_RESTART;
218  	if (wlan_serialization_is_cmd_present_in_pending_queue(NULL, &cmd)) {
219  		mlme_debug("Cmd already exist in the pending queue vdev:%u",
220  			   vdev_id);
221  		*csa_restart_pending = true;
222  	}
223  
224  	wlan_vdev_mlme_ser_cancel_request(
225  			vdev,
226  			WLAN_SER_CMD_PDEV_CSA_RESTART,
227  			WLAN_SER_CANCEL_VDEV_NON_SCAN_CMD_TYPE);
228  }
229  
230  static void
wlan_mlme_check_pdev_restart(struct wlan_objmgr_pdev * pdev,void * object,void * arg)231  wlan_mlme_check_pdev_restart(struct wlan_objmgr_pdev *pdev,
232  			     void *object, void *arg)
233  {
234  	struct wlan_objmgr_vdev *vdev = object;
235  	bool *pdev_restart_pending  = arg;
236  	struct wlan_serialization_command cmd = {0};
237  	uint8_t vdev_id = wlan_vdev_get_id(vdev);
238  
239  	cmd.vdev = vdev;
240  	cmd.cmd_id = vdev_id;
241  	cmd.cmd_type = WLAN_SER_CMD_PDEV_RESTART;
242  	if (wlan_serialization_is_cmd_present_in_active_queue(NULL, &cmd)) {
243  		mlme_debug("Pdev restart already in the active queue vdev:%u",
244  			   vdev_id);
245  		*pdev_restart_pending = true;
246  	}
247  }
248  
249  enum wlan_serialization_status
wlan_vdev_mlme_ser_pdev_csa_restart(struct wlan_serialization_command * cmd)250  wlan_vdev_mlme_ser_pdev_csa_restart(struct wlan_serialization_command *cmd)
251  {
252  	struct wlan_objmgr_pdev *pdev;
253  	bool csa_restart_pending = false;
254  	bool pdev_restart_pending = false;
255  	enum wlan_serialization_status ret;
256  
257  	if (!cmd || !cmd->vdev) {
258  		mlme_err("Null input");
259  		return WLAN_SER_CMD_DENIED_UNSPECIFIED;
260  	}
261  
262  	if (!wlan_ser_is_vdev_queue_enabled(cmd->vdev))
263  		return WLAN_SER_CMD_QUEUE_DISABLED;
264  
265  	/*
266  	 * Serialization command filtering logic
267  	 * a. Cancel any existing PDEV CSA restart cmd in the pending queue
268  	 * b. If there exists PDEV RESTART command in the active queue
269  	 *    then deny this request
270  	 * c. If PDEV CSA RESTART cmd already existed in pending queue
271  	 *    then enqueue and return already exists
272  	 * d. Else enqueue this PDEV CSA RESTART cmd
273  	 */
274  	pdev = wlan_vdev_get_pdev(cmd->vdev);
275  	wlan_objmgr_pdev_iterate_obj_list(pdev, WLAN_VDEV_OP,
276  					  wlan_mlme_cancel_pending_csa_restart,
277  					  &csa_restart_pending, 0,
278  					  WLAN_MLME_SER_IF_ID);
279  
280  	wlan_objmgr_pdev_iterate_obj_list(pdev, WLAN_VDEV_OP,
281  					  wlan_mlme_check_pdev_restart,
282  					  &pdev_restart_pending, 0,
283  					  WLAN_MLME_SER_IF_ID);
284  
285  	if (pdev_restart_pending)
286  		return WLAN_SER_CMD_DENIED_UNSPECIFIED;
287  
288  	ret = wlan_serialization_request(cmd);
289  
290  	if (csa_restart_pending && ret == WLAN_SER_CMD_PENDING)
291  		return WLAN_SER_CMD_ALREADY_EXISTS;
292  
293  	return ret;
294  }
295  
296  void
wlan_vdev_mlme_ser_remove_request(struct wlan_objmgr_vdev * vdev,uint32_t cmd_id,enum wlan_serialization_cmd_type cmd_type)297  wlan_vdev_mlme_ser_remove_request(struct wlan_objmgr_vdev *vdev,
298  				  uint32_t cmd_id,
299  				  enum wlan_serialization_cmd_type cmd_type)
300  {
301  	struct wlan_serialization_queued_cmd_info cmd = {0};
302  
303  	mlme_debug("Vdev:%d remove cmd:%d", wlan_vdev_get_id(vdev), cmd_type);
304  
305  	cmd.vdev = vdev;
306  	cmd.cmd_id = cmd_id;
307  	cmd.cmd_type = cmd_type;
308  	cmd.requestor = WLAN_UMAC_COMP_MLME;
309  	cmd.req_type = WLAN_SER_CANCEL_NON_SCAN_CMD;
310  	cmd.queue_type = WLAN_SERIALIZATION_ACTIVE_QUEUE;
311  
312  	/* Inform serialization for command completion */
313  	wlan_serialization_remove_cmd(&cmd);
314  }
315  
316  void
wlan_vdev_mlme_ser_cancel_request(struct wlan_objmgr_vdev * vdev,enum wlan_serialization_cmd_type cmd_type,enum wlan_serialization_cancel_type req_type)317  wlan_vdev_mlme_ser_cancel_request(struct wlan_objmgr_vdev *vdev,
318  				  enum wlan_serialization_cmd_type cmd_type,
319  				  enum wlan_serialization_cancel_type req_type)
320  {
321  	struct wlan_serialization_queued_cmd_info cmd = {0};
322  
323  	cmd.vdev = vdev;
324  	cmd.cmd_type = cmd_type;
325  	cmd.req_type = req_type;
326  	cmd.requestor = WLAN_UMAC_COMP_MLME;
327  	cmd.queue_type = WLAN_SERIALIZATION_PENDING_QUEUE;
328  
329  	wlan_serialization_cancel_request(&cmd);
330  }
331  
332  void
mlme_ser_inc_act_cmd_timeout(struct wlan_serialization_command * cmd)333  mlme_ser_inc_act_cmd_timeout(struct wlan_serialization_command *cmd)
334  {
335  	mlme_debug("Increase timeout of cmd type:%d", cmd->cmd_type);
336  	wlan_serialization_update_timer(cmd);
337  }
338