xref: /wlan-dirver/qca-wifi-host-cmn/dp/wifi3.0/dp_wdi_event.c (revision 45c28558a520fd0e975b20c0ad534a0aa7f08021)
1 /*
2  * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022 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 #include "dp_internal.h"
22 #include "qdf_mem.h"   /* qdf_mem_malloc,free */
23 #ifdef WIFI_MONITOR_SUPPORT
24 #include "dp_htt.h"
25 #include <dp_mon.h>
26 #endif
27 #include <qdf_module.h>
28 
29 #ifdef WDI_EVENT_ENABLE
30 /*
31  * dp_wdi_event_next_sub() - Return handle for Next WDI event
32  * @wdi_sub: WDI Event handle
33  *
34  * Return handle for next WDI event in list
35  *
36  * Return: Next WDI event to be subscribe
37  */
38 static inline wdi_event_subscribe *
39 dp_wdi_event_next_sub(wdi_event_subscribe *wdi_sub)
40 {
41 	if (!wdi_sub) {
42 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
43 			"Invalid subscriber in %s", __func__);
44 		return NULL;
45 	}
46 	return wdi_sub->priv.next;
47 }
48 
49 
50 /*
51  * dp_wdi_event_del_subs() -Delete Event subscription
52  * @wdi_sub: WDI Event handle
53  * @event_index: Event index from list
54  *
55  * This API will delete subscribed event from list
56  * Return: None
57  */
58 static inline void
59 dp_wdi_event_del_subs(wdi_event_subscribe *wdi_sub, int event_index)
60 {
61 	/* Subscribers should take care of deletion */
62 }
63 
64 
65 /*
66  * dp_wdi_event_iter_sub() - Iterate through all WDI event in the list
67  * and pass WDI event to callback function
68  * @pdev: DP pdev handle
69  * @event_index: Event index in list
70  * @wdi_event: WDI event handle
71  * @data: pointer to data
72  * @peer_id: peer id number
73  * @status: HTT rx status
74  *
75  *
76  * Return: None
77  */
78 static inline void
79 dp_wdi_event_iter_sub(
80 	struct dp_pdev *pdev,
81 	uint32_t event_index,
82 	wdi_event_subscribe *wdi_sub,
83 	void *data,
84 	uint16_t peer_id,
85 	int status)
86 {
87 	enum WDI_EVENT event = event_index + WDI_EVENT_BASE;
88 
89 	if (wdi_sub) {
90 		do {
91 			wdi_sub->callback(wdi_sub->context, event, data,
92 					peer_id, status);
93 		} while ((wdi_sub = dp_wdi_event_next_sub(wdi_sub)));
94 	}
95 }
96 
97 
98 /*
99  * dp_wdi_event_handler() - Event handler for WDI event
100  * @event: wdi event number
101  * @soc: soc handle
102  * @data: pointer to data
103  * @peer_id: peer id number
104  * @status: HTT rx status
105  * @pdev_id: id of pdev
106  *
107  * It will be called to register WDI event
108  *
109  * Return: None
110  */
111 void
112 dp_wdi_event_handler(
113 	enum WDI_EVENT event,
114 	struct dp_soc *soc,
115 	void *data,
116 	uint16_t peer_id,
117 	int status, uint8_t pdev_id)
118 {
119 	uint32_t event_index;
120 	wdi_event_subscribe *wdi_sub;
121 	struct dp_pdev *txrx_pdev;
122 	struct dp_soc *soc_t = (struct dp_soc *)soc;
123 	txrx_pdev = dp_get_pdev_for_mac_id(soc_t, pdev_id);
124 
125 	if (!event) {
126 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
127 			"Invalid WDI event in %s", __func__);
128 		return;
129 	}
130 	if (!txrx_pdev || txrx_pdev->pdev_deinit) {
131 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
132 			"Invalid pdev in WDI event handler");
133 		return;
134 	}
135 
136 	/*
137 	 *  There can be NULL data, so no validation for the data
138 	 *  Subscribers must do the sanity based on the requirements
139 	 */
140 	event_index = event - WDI_EVENT_BASE;
141 
142 	DP_STATS_INC(txrx_pdev, wdi_event[event_index], 1);
143 	wdi_sub = txrx_pdev->wdi_event_list[event_index];
144 
145 	/* Find the subscriber */
146 	dp_wdi_event_iter_sub(txrx_pdev, event_index, wdi_sub, data,
147 			peer_id, status);
148 }
149 
150 qdf_export_symbol(dp_wdi_event_handler);
151 
152 /*
153  * dp_wdi_event_sub() - Subscribe WDI event
154  * @soc: soc handle
155  * @pdev_id: id of pdev
156  * @event_cb_sub_handle: subscribe event handle
157  * @event: Event to be subscribe
158  *
159  * Return: 0 for success. nonzero for failure.
160  */
161 int
162 dp_wdi_event_sub(
163 	struct cdp_soc_t *soc, uint8_t pdev_id,
164 	wdi_event_subscribe *event_cb_sub_handle,
165 	uint32_t event)
166 {
167 	uint32_t event_index;
168 	wdi_event_subscribe *wdi_sub;
169 	wdi_event_subscribe *wdi_sub_itr;
170 	struct dp_pdev *txrx_pdev =
171 		dp_get_pdev_from_soc_pdev_id_wifi3((struct dp_soc *)soc,
172 						   pdev_id);
173 	wdi_event_subscribe *event_cb_sub =
174 		(wdi_event_subscribe *) event_cb_sub_handle;
175 
176 	if (!txrx_pdev) {
177 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
178 			"Invalid txrx_pdev in %s", __func__);
179 		return -EINVAL;
180 	}
181 	if (!event_cb_sub) {
182 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
183 			"Invalid callback in %s", __func__);
184 		return -EINVAL;
185 	}
186 	if ((!event) || (event >= WDI_EVENT_LAST) || (event < WDI_EVENT_BASE)) {
187 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
188 			"Invalid event in %s", __func__);
189 		return -EINVAL;
190 	}
191 
192 	dp_monitor_set_pktlog_wifi3(txrx_pdev, event, true);
193 	event_index = event - WDI_EVENT_BASE;
194 	wdi_sub = txrx_pdev->wdi_event_list[event_index];
195 
196 	/*
197 	 *  Check if it is the first subscriber of the event
198 	 */
199 	if (!wdi_sub) {
200 		wdi_sub = event_cb_sub;
201 		wdi_sub->priv.next = NULL;
202 		wdi_sub->priv.prev = NULL;
203 		txrx_pdev->wdi_event_list[event_index] = wdi_sub;
204 		return 0;
205 	}
206 
207 	/* Check if event is already subscribed */
208 	wdi_sub_itr = wdi_sub;
209 	do {
210 		if (wdi_sub_itr == event_cb_sub) {
211 			QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
212 				  "Duplicate wdi subscribe event detected %s", __func__);
213 			return 0;
214 		}
215 	} while ((wdi_sub_itr = dp_wdi_event_next_sub(wdi_sub_itr)));
216 
217 	event_cb_sub->priv.next = wdi_sub;
218 	event_cb_sub->priv.prev = NULL;
219 	wdi_sub->priv.prev = event_cb_sub;
220 	txrx_pdev->wdi_event_list[event_index] = event_cb_sub;
221 	return 0;
222 
223 }
224 
225 /*
226  * dp_wdi_event_unsub() - WDI event unsubscribe
227  * @soc: soc handle
228  * @pdev_id: id of pdev
229  * @event_cb_sub_handle: subscribed event handle
230  * @event: Event to be unsubscribe
231  *
232  *
233  * Return: 0 for success. nonzero for failure.
234  */
235 int
236 dp_wdi_event_unsub(
237 	struct cdp_soc_t *soc, uint8_t pdev_id,
238 	wdi_event_subscribe *event_cb_sub_handle,
239 	uint32_t event)
240 {
241 	uint32_t event_index = event - WDI_EVENT_BASE;
242 	struct dp_pdev *txrx_pdev =
243 		dp_get_pdev_from_soc_pdev_id_wifi3((struct dp_soc *)soc,
244 						   pdev_id);
245 	wdi_event_subscribe *event_cb_sub =
246 		(wdi_event_subscribe *) event_cb_sub_handle;
247 
248 	if (!txrx_pdev || !event_cb_sub) {
249 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
250 			"Invalid callback or pdev in %s", __func__);
251 		return -EINVAL;
252 	}
253 
254 	dp_monitor_set_pktlog_wifi3(txrx_pdev, event, false);
255 
256 	if (!event_cb_sub->priv.prev) {
257 		txrx_pdev->wdi_event_list[event_index] = event_cb_sub->priv.next;
258 	} else {
259 		event_cb_sub->priv.prev->priv.next = event_cb_sub->priv.next;
260 	}
261 	if (event_cb_sub->priv.next) {
262 		event_cb_sub->priv.next->priv.prev = event_cb_sub->priv.prev;
263 	}
264 
265 	/* Reset susbscribe event list elems */
266 	event_cb_sub->priv.next = NULL;
267 	event_cb_sub->priv.prev = NULL;
268 
269 	return 0;
270 }
271 
272 
273 /*
274  * dp_wdi_event_attach() - Attach wdi event
275  * @txrx_pdev: DP pdev handle
276  *
277  * Return: 0 for success. nonzero for failure.
278  */
279 int
280 dp_wdi_event_attach(struct dp_pdev *txrx_pdev)
281 {
282 	if (!txrx_pdev) {
283 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
284 			"Invalid device in %s\nWDI event attach failed",
285 			__func__);
286 		return -EINVAL;
287 	}
288 	/* Separate subscriber list for each event */
289 	txrx_pdev->wdi_event_list = (wdi_event_subscribe **)
290 		qdf_mem_malloc(
291 			sizeof(wdi_event_subscribe *) * WDI_NUM_EVENTS);
292 	if (!txrx_pdev->wdi_event_list) {
293 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
294 			"Insufficient memory for the WDI event lists");
295 		return -EINVAL;
296 	}
297 	return 0;
298 }
299 
300 
301 /*
302  * dp_wdi_event_detach() - Detach WDI event
303  * @txrx_pdev: DP pdev handle
304  *
305  * Return: 0 for success. nonzero for failure.
306  */
307 int
308 dp_wdi_event_detach(struct dp_pdev *txrx_pdev)
309 {
310 	int i;
311 	wdi_event_subscribe *wdi_sub;
312 	if (!txrx_pdev) {
313 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
314 			"Invalid device in %s\nWDI attach failed", __func__);
315 		return -EINVAL;
316 	}
317 	if (!txrx_pdev->wdi_event_list) {
318 		return -EINVAL;
319 	}
320 	for (i = 0; i < WDI_NUM_EVENTS; i++) {
321 		wdi_sub = txrx_pdev->wdi_event_list[i];
322 		/* Delete all the subscribers */
323 		dp_wdi_event_del_subs(wdi_sub, i);
324 	}
325 	qdf_mem_free(txrx_pdev->wdi_event_list);
326 	return 0;
327 }
328 #endif /* CONFIG_WIN */
329