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