1 /*
2  * Copyright (c) 2017-2021 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
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 *
dp_wdi_event_next_sub(wdi_event_subscribe * wdi_sub)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  *
57  * Return: None
58  */
59 static inline void
dp_wdi_event_del_subs(wdi_event_subscribe * wdi_sub,int event_index)60 dp_wdi_event_del_subs(wdi_event_subscribe *wdi_sub, int event_index)
61 {
62 	/* Subscribers should take care of deletion */
63 }
64 
65 
66 /**
67  * dp_wdi_event_iter_sub() - Iterate through all WDI event in the list
68  * and pass WDI event to callback function
69  * @pdev: DP pdev handle
70  * @event_index: Event index in list
71  * @wdi_sub: WDI event subscriber
72  * @data: pointer to data
73  * @peer_id: peer id number
74  * @status: HTT rx status
75  *
76  *
77  * Return: None
78  */
79 static inline void
dp_wdi_event_iter_sub(struct dp_pdev * pdev,uint32_t event_index,wdi_event_subscribe * wdi_sub,void * data,uint16_t peer_id,int status)80 dp_wdi_event_iter_sub(
81 	struct dp_pdev *pdev,
82 	uint32_t event_index,
83 	wdi_event_subscribe *wdi_sub,
84 	void *data,
85 	uint16_t peer_id,
86 	int status)
87 {
88 	enum WDI_EVENT event = event_index + WDI_EVENT_BASE;
89 
90 	if (wdi_sub) {
91 		do {
92 			wdi_sub->callback(wdi_sub->context, event, data,
93 					peer_id, status);
94 		} while ((wdi_sub = dp_wdi_event_next_sub(wdi_sub)));
95 	}
96 }
97 
98 
99 void
dp_wdi_event_handler(enum WDI_EVENT event,struct dp_soc * soc,void * data,uint16_t peer_id,int status,uint8_t pdev_id)100 dp_wdi_event_handler(
101 	enum WDI_EVENT event,
102 	struct dp_soc *soc,
103 	void *data,
104 	uint16_t peer_id,
105 	int status, uint8_t pdev_id)
106 {
107 	uint32_t event_index;
108 	wdi_event_subscribe *wdi_sub;
109 	struct dp_pdev *txrx_pdev;
110 	struct dp_soc *soc_t = (struct dp_soc *)soc;
111 	txrx_pdev = dp_get_pdev_for_mac_id(soc_t, pdev_id);
112 
113 	if (!event) {
114 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
115 			"Invalid WDI event in %s", __func__);
116 		return;
117 	}
118 	if (!txrx_pdev || txrx_pdev->pdev_deinit) {
119 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
120 			"Invalid pdev in WDI event handler");
121 		return;
122 	}
123 
124 	/*
125 	 *  There can be NULL data, so no validation for the data
126 	 *  Subscribers must do the sanity based on the requirements
127 	 */
128 	event_index = event - WDI_EVENT_BASE;
129 
130 	DP_STATS_INC(txrx_pdev, wdi_event[event_index], 1);
131 	wdi_sub = txrx_pdev->wdi_event_list[event_index];
132 
133 	/* Find the subscriber */
134 	dp_wdi_event_iter_sub(txrx_pdev, event_index, wdi_sub, data,
135 			peer_id, status);
136 }
137 
138 qdf_export_symbol(dp_wdi_event_handler);
139 
140 int
dp_wdi_event_sub(struct cdp_soc_t * soc,uint8_t pdev_id,wdi_event_subscribe * event_cb_sub_handle,uint32_t event)141 dp_wdi_event_sub(
142 	struct cdp_soc_t *soc, uint8_t pdev_id,
143 	wdi_event_subscribe *event_cb_sub_handle,
144 	uint32_t event)
145 {
146 	uint32_t event_index;
147 	wdi_event_subscribe *wdi_sub;
148 	wdi_event_subscribe *wdi_sub_itr;
149 	struct dp_pdev *txrx_pdev =
150 		dp_get_pdev_from_soc_pdev_id_wifi3((struct dp_soc *)soc,
151 						   pdev_id);
152 	wdi_event_subscribe *event_cb_sub =
153 		(wdi_event_subscribe *) event_cb_sub_handle;
154 
155 	if (!txrx_pdev) {
156 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
157 			"Invalid txrx_pdev in %s", __func__);
158 		return -EINVAL;
159 	}
160 	if (!event_cb_sub) {
161 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
162 			"Invalid callback in %s", __func__);
163 		return -EINVAL;
164 	}
165 	if ((!event) || (event >= WDI_EVENT_LAST) || (event < WDI_EVENT_BASE)) {
166 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
167 			"Invalid event in %s", __func__);
168 		return -EINVAL;
169 	}
170 
171 	dp_monitor_set_pktlog_wifi3(txrx_pdev, event, true);
172 	event_index = event - WDI_EVENT_BASE;
173 	wdi_sub = txrx_pdev->wdi_event_list[event_index];
174 
175 	/*
176 	 *  Check if it is the first subscriber of the event
177 	 */
178 	if (!wdi_sub) {
179 		wdi_sub = event_cb_sub;
180 		wdi_sub->priv.next = NULL;
181 		wdi_sub->priv.prev = NULL;
182 		txrx_pdev->wdi_event_list[event_index] = wdi_sub;
183 		return 0;
184 	}
185 
186 	/* Check if event is already subscribed */
187 	wdi_sub_itr = wdi_sub;
188 	do {
189 		if (wdi_sub_itr == event_cb_sub) {
190 			QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
191 				  "Duplicate wdi subscribe event detected %s", __func__);
192 			return 0;
193 		}
194 	} while ((wdi_sub_itr = dp_wdi_event_next_sub(wdi_sub_itr)));
195 
196 	event_cb_sub->priv.next = wdi_sub;
197 	event_cb_sub->priv.prev = NULL;
198 	wdi_sub->priv.prev = event_cb_sub;
199 	txrx_pdev->wdi_event_list[event_index] = event_cb_sub;
200 	return 0;
201 
202 }
203 
204 int
dp_wdi_event_unsub(struct cdp_soc_t * soc,uint8_t pdev_id,wdi_event_subscribe * event_cb_sub_handle,uint32_t event)205 dp_wdi_event_unsub(
206 	struct cdp_soc_t *soc, uint8_t pdev_id,
207 	wdi_event_subscribe *event_cb_sub_handle,
208 	uint32_t event)
209 {
210 	uint32_t event_index = event - WDI_EVENT_BASE;
211 	struct dp_pdev *txrx_pdev =
212 		dp_get_pdev_from_soc_pdev_id_wifi3((struct dp_soc *)soc,
213 						   pdev_id);
214 	wdi_event_subscribe *event_cb_sub =
215 		(wdi_event_subscribe *) event_cb_sub_handle;
216 
217 	if (!txrx_pdev || !event_cb_sub) {
218 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
219 			"Invalid callback or pdev in %s", __func__);
220 		return -EINVAL;
221 	}
222 
223 	dp_monitor_set_pktlog_wifi3(txrx_pdev, event, false);
224 
225 	if (!event_cb_sub->priv.prev) {
226 		txrx_pdev->wdi_event_list[event_index] = event_cb_sub->priv.next;
227 	} else {
228 		event_cb_sub->priv.prev->priv.next = event_cb_sub->priv.next;
229 	}
230 	if (event_cb_sub->priv.next) {
231 		event_cb_sub->priv.next->priv.prev = event_cb_sub->priv.prev;
232 	}
233 
234 	/* Reset susbscribe event list elems */
235 	event_cb_sub->priv.next = NULL;
236 	event_cb_sub->priv.prev = NULL;
237 
238 	return 0;
239 }
240 
241 
242 int
dp_wdi_event_attach(struct dp_pdev * txrx_pdev)243 dp_wdi_event_attach(struct dp_pdev *txrx_pdev)
244 {
245 	if (!txrx_pdev) {
246 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
247 			"Invalid device in %s\nWDI event attach failed",
248 			__func__);
249 		return -EINVAL;
250 	}
251 	/* Separate subscriber list for each event */
252 	txrx_pdev->wdi_event_list = (wdi_event_subscribe **)
253 		qdf_mem_malloc(
254 			sizeof(wdi_event_subscribe *) * WDI_NUM_EVENTS);
255 	if (!txrx_pdev->wdi_event_list) {
256 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
257 			"Insufficient memory for the WDI event lists");
258 		return -EINVAL;
259 	}
260 	return 0;
261 }
262 
263 int
dp_wdi_event_detach(struct dp_pdev * txrx_pdev)264 dp_wdi_event_detach(struct dp_pdev *txrx_pdev)
265 {
266 	int i;
267 	wdi_event_subscribe *wdi_sub;
268 	if (!txrx_pdev) {
269 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
270 			"Invalid device in %s\nWDI attach failed", __func__);
271 		return -EINVAL;
272 	}
273 	if (!txrx_pdev->wdi_event_list) {
274 		return -EINVAL;
275 	}
276 	for (i = 0; i < WDI_NUM_EVENTS; i++) {
277 		wdi_sub = txrx_pdev->wdi_event_list[i];
278 		/* Delete all the subscribers */
279 		dp_wdi_event_del_subs(wdi_sub, i);
280 	}
281 	qdf_mem_free(txrx_pdev->wdi_event_list);
282 	return 0;
283 }
284 #endif
285