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