xref: /wlan-dirver/qca-wifi-host-cmn/dp/wifi3.0/dp_wdi_event.c (revision f28396d060cff5c6519f883cb28ae0116ce479f1)
1 /*
2  * Copyright (c) 2017-2020 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 	struct dp_pdev *txrx_pdev =
173 		dp_get_pdev_from_soc_pdev_id_wifi3((struct dp_soc *)soc,
174 						   pdev_id);
175 	wdi_event_subscribe *event_cb_sub =
176 		(wdi_event_subscribe *) event_cb_sub_handle;
177 
178 	if (!txrx_pdev) {
179 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
180 			"Invalid txrx_pdev in %s", __func__);
181 		return -EINVAL;
182 	}
183 	if (!event_cb_sub) {
184 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
185 			"Invalid callback in %s", __func__);
186 		return -EINVAL;
187 	}
188 	if ((!event) || (event >= WDI_EVENT_LAST) || (event < WDI_EVENT_BASE)) {
189 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
190 			"Invalid event in %s", __func__);
191 		return -EINVAL;
192 	}
193 	dp_set_pktlog_wifi3(txrx_pdev, event, true);
194 	event_index = event - WDI_EVENT_BASE;
195 	wdi_sub = txrx_pdev->wdi_event_list[event_index];
196 
197 	/*
198 	 *  Check if it is the first subscriber of the event
199 	 */
200 	if (!wdi_sub) {
201 		wdi_sub = event_cb_sub;
202 		wdi_sub->priv.next = NULL;
203 		wdi_sub->priv.prev = NULL;
204 		txrx_pdev->wdi_event_list[event_index] = wdi_sub;
205 		return 0;
206 	}
207 	event_cb_sub->priv.next = wdi_sub;
208 	event_cb_sub->priv.prev = NULL;
209 	wdi_sub->priv.prev = event_cb_sub;
210 	txrx_pdev->wdi_event_list[event_index] = event_cb_sub;
211 	return 0;
212 
213 }
214 
215 /*
216  * dp_wdi_event_unsub() - WDI event unsubscribe
217  * @soc: soc handle
218  * @pdev_id: id of pdev
219  * @event_cb_sub_handle: subscribed event handle
220  * @event: Event to be unsubscribe
221  *
222  *
223  * Return: 0 for success. nonzero for failure.
224  */
225 int
226 dp_wdi_event_unsub(
227 	struct cdp_soc_t *soc, uint8_t pdev_id,
228 	wdi_event_subscribe *event_cb_sub_handle,
229 	uint32_t event)
230 {
231 	uint32_t event_index = event - WDI_EVENT_BASE;
232 	struct dp_pdev *txrx_pdev =
233 		dp_get_pdev_from_soc_pdev_id_wifi3((struct dp_soc *)soc,
234 						   pdev_id);
235 	wdi_event_subscribe *event_cb_sub =
236 		(wdi_event_subscribe *) event_cb_sub_handle;
237 
238 	if (!txrx_pdev || !event_cb_sub) {
239 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
240 			"Invalid callback or pdev in %s", __func__);
241 		return -EINVAL;
242 	}
243 
244 	dp_set_pktlog_wifi3(txrx_pdev, event, false);
245 
246 	if (!event_cb_sub->priv.prev) {
247 		txrx_pdev->wdi_event_list[event_index] = event_cb_sub->priv.next;
248 	} else {
249 		event_cb_sub->priv.prev->priv.next = event_cb_sub->priv.next;
250 	}
251 	if (event_cb_sub->priv.next) {
252 		event_cb_sub->priv.next->priv.prev = event_cb_sub->priv.prev;
253 	}
254 
255 	return 0;
256 }
257 
258 
259 /*
260  * dp_wdi_event_attach() - Attach wdi event
261  * @txrx_pdev: DP pdev handle
262  *
263  * Return: 0 for success. nonzero for failure.
264  */
265 int
266 dp_wdi_event_attach(struct dp_pdev *txrx_pdev)
267 {
268 	if (!txrx_pdev) {
269 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
270 			"Invalid device in %s\nWDI event attach failed",
271 			__func__);
272 		return -EINVAL;
273 	}
274 	/* Separate subscriber list for each event */
275 	txrx_pdev->wdi_event_list = (wdi_event_subscribe **)
276 		qdf_mem_malloc(
277 			sizeof(wdi_event_subscribe *) * WDI_NUM_EVENTS);
278 	if (!txrx_pdev->wdi_event_list) {
279 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
280 			"Insufficient memory for the WDI event lists");
281 		return -EINVAL;
282 	}
283 	return 0;
284 }
285 
286 
287 /*
288  * dp_wdi_event_detach() - Detach WDI event
289  * @txrx_pdev: DP pdev handle
290  *
291  * Return: 0 for success. nonzero for failure.
292  */
293 int
294 dp_wdi_event_detach(struct dp_pdev *txrx_pdev)
295 {
296 	int i;
297 	wdi_event_subscribe *wdi_sub;
298 	if (!txrx_pdev) {
299 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
300 			"Invalid device in %s\nWDI attach failed", __func__);
301 		return -EINVAL;
302 	}
303 	if (!txrx_pdev->wdi_event_list) {
304 		return -EINVAL;
305 	}
306 	for (i = 0; i < WDI_NUM_EVENTS; i++) {
307 		wdi_sub = txrx_pdev->wdi_event_list[i];
308 		/* Delete all the subscribers */
309 		dp_wdi_event_del_subs(wdi_sub, i);
310 	}
311 	qdf_mem_free(txrx_pdev->wdi_event_list);
312 	return 0;
313 }
314 #endif /* CONFIG_WIN */
315