1 /*
2 * Copyright (c) 2012-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 #include "ol_txrx_types.h"
20 #include "ol_txrx.h"
21
wdi_event_next_sub(wdi_event_subscribe * wdi_sub)22 static inline wdi_event_subscribe *wdi_event_next_sub(wdi_event_subscribe *
23 wdi_sub)
24 {
25 if (!wdi_sub) {
26 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
27 "Invalid subscriber in %s\n", __func__);
28 return NULL;
29 }
30 return wdi_sub->priv.next;
31 }
32
33 static inline void
wdi_event_del_subs(wdi_event_subscribe * wdi_sub,int event_index)34 wdi_event_del_subs(wdi_event_subscribe *wdi_sub, int event_index)
35 {
36 wdi_event_notify deallocate_sub;
37
38 while (wdi_sub) {
39 wdi_event_subscribe *next = wdi_event_next_sub(wdi_sub);
40 /*
41 * Context is NULL for static allocation of subs
42 * In dynamic allocation case notify the user
43 */
44 if (wdi_sub->context) {
45 deallocate_sub = wdi_sub->context;
46 deallocate_sub(WDI_EVENT_SUB_DEALLOCATE,
47 WDI_EVENT_BASE + event_index);
48 }
49 wdi_sub = next;
50 }
51 /* qdf_mem_free(wdi_sub); */
52 }
53
54 static inline void
wdi_event_iter_sub(struct ol_txrx_pdev_t * pdev,uint32_t event_index,wdi_event_subscribe * wdi_sub,void * data)55 wdi_event_iter_sub(struct ol_txrx_pdev_t *pdev,
56 uint32_t event_index,
57 wdi_event_subscribe *wdi_sub, void *data)
58 {
59 enum WDI_EVENT event = event_index + WDI_EVENT_BASE;
60
61 if (wdi_sub) {
62 do {
63 wdi_sub->callback(pdev, event, data, 0, 0);
64 } while ((wdi_sub = wdi_event_next_sub(wdi_sub)));
65 }
66 }
67
68 void
wdi_event_handler(enum WDI_EVENT event,uint8_t pdev_id,void * data)69 wdi_event_handler(enum WDI_EVENT event,
70 uint8_t pdev_id, void *data)
71 {
72 uint32_t event_index;
73 wdi_event_subscribe *wdi_sub;
74 struct ol_txrx_soc_t *soc = cds_get_context(QDF_MODULE_ID_SOC);
75 ol_txrx_pdev_handle txrx_pdev;
76
77 /*
78 * Input validation
79 */
80 if (!event) {
81 ol_txrx_err("Invalid WDI event");
82 return;
83 }
84 if (!soc)
85 return;
86
87 txrx_pdev = ol_txrx_get_pdev_from_pdev_id(soc, pdev_id);
88 if (!txrx_pdev) {
89 ol_txrx_err("Invalid pdev");
90 return;
91 }
92 /*
93 * There can be NULL data, so no validation for the data
94 * Subscribers must do the sanity based on the requirements
95 */
96 event_index = event - WDI_EVENT_BASE;
97
98 wdi_sub = txrx_pdev->wdi_event_list[event_index];
99
100 /* Find the subscriber */
101 wdi_event_iter_sub(txrx_pdev, event_index, wdi_sub, data);
102 }
103
104 int
wdi_event_sub(struct cdp_soc_t * soc_hdl,uint8_t pdev_id,wdi_event_subscribe * pevent_cb_sub,uint32_t event)105 wdi_event_sub(struct cdp_soc_t *soc_hdl, uint8_t pdev_id,
106 wdi_event_subscribe *pevent_cb_sub, uint32_t event)
107 {
108 uint32_t event_index;
109 wdi_event_subscribe *wdi_sub;
110 struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl);
111 ol_txrx_pdev_handle txrx_pdev = ol_txrx_get_pdev_from_pdev_id(soc,
112 pdev_id);
113 wdi_event_subscribe *event_cb_sub = pevent_cb_sub;
114
115 /* Input validation */
116 if (!txrx_pdev || !txrx_pdev->wdi_event_list) {
117 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
118 "Invalid txrx_pdev or wdi_event_list in %s",
119 __func__);
120 return -EINVAL;
121 }
122 if (!event_cb_sub) {
123 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
124 "Invalid callback in %s", __func__);
125 return -EINVAL;
126 }
127 if ((!event) || (event >= WDI_EVENT_LAST) || (event < WDI_EVENT_BASE)) {
128 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
129 "Invalid event in %s", __func__);
130 return -EINVAL;
131 }
132 /* Input validation */
133 event_index = event - WDI_EVENT_BASE;
134
135 wdi_sub = txrx_pdev->wdi_event_list[event_index];
136 /*
137 * Check if it is the first subscriber of the event
138 */
139 if (!wdi_sub) {
140 wdi_sub = event_cb_sub;
141 wdi_sub->priv.next = NULL;
142 wdi_sub->priv.prev = NULL;
143 txrx_pdev->wdi_event_list[event_index] = wdi_sub;
144 return 0;
145 }
146 event_cb_sub->priv.next = wdi_sub;
147 event_cb_sub->priv.prev = NULL;
148 wdi_sub->priv.prev = event_cb_sub;
149 txrx_pdev->wdi_event_list[event_index] = event_cb_sub;
150
151 return 0;
152 }
153
154 int
wdi_event_unsub(struct cdp_soc_t * soc_hdl,uint8_t pdev_id,wdi_event_subscribe * pevent_cb_sub,uint32_t event)155 wdi_event_unsub(struct cdp_soc_t *soc_hdl, uint8_t pdev_id,
156 wdi_event_subscribe *pevent_cb_sub, uint32_t event)
157 {
158 uint32_t event_index = event - WDI_EVENT_BASE;
159 struct ol_txrx_soc_t *soc = cdp_soc_t_to_ol_txrx_soc_t(soc_hdl);
160 ol_txrx_pdev_handle txrx_pdev = ol_txrx_get_pdev_from_pdev_id(soc,
161 pdev_id);
162
163 wdi_event_subscribe *event_cb_sub = pevent_cb_sub;
164
165 /* Input validation */
166 if (!txrx_pdev) {
167 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
168 "Invalid txrx_pdev in %s", __func__);
169 return -EINVAL;
170 }
171
172 /* Input validation */
173 if (!event_cb_sub) {
174 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
175 "Invalid callback in %s", __func__);
176 return -EINVAL;
177 }
178 if (!event_cb_sub->priv.prev) {
179 txrx_pdev->wdi_event_list[event_index] =
180 event_cb_sub->priv.next;
181 } else {
182 event_cb_sub->priv.prev->priv.next = event_cb_sub->priv.next;
183 }
184 if (event_cb_sub->priv.next)
185 event_cb_sub->priv.next->priv.prev = event_cb_sub->priv.prev;
186
187 /* qdf_mem_free(event_cb_sub); */
188
189 return 0;
190 }
191
wdi_event_attach(struct ol_txrx_pdev_t * txrx_pdev)192 A_STATUS wdi_event_attach(struct ol_txrx_pdev_t *txrx_pdev)
193 {
194 /* Input validation */
195 if (!txrx_pdev) {
196 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
197 "Invalid device in %s\nWDI event attach failed",
198 __func__);
199 return A_ERROR;
200 }
201 /* Separate subscriber list for each event */
202 txrx_pdev->wdi_event_list = (wdi_event_subscribe **)
203 qdf_mem_malloc(
204 sizeof(wdi_event_subscribe *) *
205 WDI_NUM_EVENTS);
206 if (!txrx_pdev->wdi_event_list)
207 return A_NO_MEMORY;
208
209 return A_OK;
210 }
211
wdi_event_detach(struct ol_txrx_pdev_t * txrx_pdev)212 A_STATUS wdi_event_detach(struct ol_txrx_pdev_t *txrx_pdev)
213 {
214 int i;
215 wdi_event_subscribe *wdi_sub;
216
217 if (!txrx_pdev) {
218 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
219 "Invalid device in %s\nWDI detach failed",
220 __func__);
221 return A_ERROR;
222 }
223 if (!txrx_pdev->wdi_event_list) {
224 QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
225 "%s: wdi_event_list is NULL", __func__);
226 return A_ERROR;
227 }
228
229 for (i = 0; i < WDI_NUM_EVENTS; i++) {
230 wdi_sub = txrx_pdev->wdi_event_list[i];
231 if (wdi_sub) {
232 /* Delete all the subscribers */
233 wdi_event_del_subs(wdi_sub, i);
234 }
235 }
236 /* txrx_pdev->wdi_event_list would be non-null */
237 qdf_mem_free(txrx_pdev->wdi_event_list);
238 txrx_pdev->wdi_event_list = NULL;
239 return A_OK;
240 }
241