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