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