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