.. | .. |
---|
1 | | -/* SPDX-License-Identifier: GPL-2.0 */ |
---|
2 | 1 | /* |
---|
3 | 2 | * DHD debugability Linux os layer |
---|
4 | 3 | * |
---|
5 | | - * Copyright (C) 1999-2019, Broadcom Corporation |
---|
6 | | - * |
---|
| 4 | + * <<Broadcom-WL-IPTag/Open:>> |
---|
| 5 | + * |
---|
| 6 | + * Portions of this code are copyright (c) 2022 Cypress Semiconductor Corporation |
---|
| 7 | + * |
---|
| 8 | + * Copyright (C) 1999-2017, Broadcom Corporation |
---|
| 9 | + * |
---|
7 | 10 | * Unless you and Broadcom execute a separate written software license |
---|
8 | 11 | * agreement governing use of this software, this software is licensed to you |
---|
9 | 12 | * under the terms of the GNU General Public License version 2 (the "GPL"), |
---|
10 | 13 | * available at http://www.broadcom.com/licenses/GPLv2.php, with the |
---|
11 | 14 | * following added to such license: |
---|
12 | | - * |
---|
| 15 | + * |
---|
13 | 16 | * As a special exception, the copyright holders of this software give you |
---|
14 | 17 | * permission to link this software with independent modules, and to copy and |
---|
15 | 18 | * distribute the resulting executable under terms of your choice, provided that |
---|
.. | .. |
---|
17 | 20 | * the license of that module. An independent module is a module which is not |
---|
18 | 21 | * derived from this software. The special exception does not apply to any |
---|
19 | 22 | * modifications of the software. |
---|
20 | | - * |
---|
| 23 | + * |
---|
21 | 24 | * Notwithstanding the above, under no circumstances may you combine this |
---|
22 | 25 | * software in any way with any other Broadcom software provided under a license |
---|
23 | 26 | * other than the GPL, without Broadcom's express prior written consent. |
---|
24 | 27 | * |
---|
25 | | - * $Id: dhd_debug_linux.c 554441 2015-05-05 18:45:58Z $ |
---|
| 28 | + * $Id: dhd_debug_linux.c 696754 2017-04-28 00:21:58Z $ |
---|
26 | 29 | */ |
---|
27 | 30 | |
---|
28 | 31 | #include <typedefs.h> |
---|
29 | 32 | #include <osl.h> |
---|
30 | 33 | #include <bcmutils.h> |
---|
31 | 34 | #include <bcmendian.h> |
---|
32 | | -#ifndef CUSTOMER_IPCAM |
---|
33 | | -#include <bcmpcie.h> |
---|
34 | | -#endif |
---|
35 | 35 | #include <dngl_stats.h> |
---|
36 | 36 | #include <dhd.h> |
---|
37 | 37 | #include <dhd_dbg.h> |
---|
.. | .. |
---|
61 | 61 | {1, WIFI_EVENT_DRIVER_EAPOL_FRAME_RECEIVED, "DRIVER EAPOL RX"}, |
---|
62 | 62 | {2, WIFI_EVENT_DRIVER_SCAN_REQUESTED, "SCAN_REQUESTED"}, |
---|
63 | 63 | {2, WIFI_EVENT_DRIVER_SCAN_COMPLETE, "SCAN COMPELETE"}, |
---|
64 | | - {3, WIFI_EVENT_DRIVER_SCAN_RESULT_FOUND, "SCAN RESULT FOUND"} |
---|
| 64 | + {3, WIFI_EVENT_DRIVER_SCAN_RESULT_FOUND, "SCAN RESULT FOUND"}, |
---|
| 65 | + {2, WIFI_EVENT_DRIVER_PNO_ADD, "PNO ADD"}, |
---|
| 66 | + {2, WIFI_EVENT_DRIVER_PNO_REMOVE, "PNO REMOVE"}, |
---|
| 67 | + {2, WIFI_EVENT_DRIVER_PNO_NETWORK_FOUND, "PNO NETWORK FOUND"}, |
---|
| 68 | + {2, WIFI_EVENT_DRIVER_PNO_SCAN_REQUESTED, "PNO SCAN_REQUESTED"}, |
---|
| 69 | + {1, WIFI_EVENT_DRIVER_PNO_SCAN_RESULT_FOUND, "PNO SCAN RESULT FOUND"}, |
---|
| 70 | + {1, WIFI_EVENT_DRIVER_PNO_SCAN_COMPLETE, "PNO SCAN COMPELETE"} |
---|
65 | 71 | }; |
---|
66 | 72 | |
---|
67 | 73 | static void |
---|
.. | .. |
---|
72 | 78 | dbg_ring_send_sub_t ring_sub_send; |
---|
73 | 79 | ndev = dhd_linux_get_primary_netdev(dhdp); |
---|
74 | 80 | if (!ndev) |
---|
| 81 | + return; |
---|
| 82 | + if (!VALID_RING(ring_id)) |
---|
75 | 83 | return; |
---|
76 | 84 | if (ring_send_sub_cb[ring_id]) { |
---|
77 | 85 | ring_sub_send = ring_send_sub_cb[ring_id]; |
---|
.. | .. |
---|
95 | 103 | dbg_ring_poll_worker(struct work_struct *work) |
---|
96 | 104 | { |
---|
97 | 105 | struct delayed_work *d_work = to_delayed_work(work); |
---|
| 106 | + bool sched = TRUE; |
---|
| 107 | + dhd_dbg_ring_t *ring; |
---|
| 108 | +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) |
---|
| 109 | +#pragma GCC diagnostic push |
---|
| 110 | +#pragma GCC diagnostic ignored "-Wcast-qual" |
---|
| 111 | +#endif // endif |
---|
98 | 112 | linux_dbgring_info_t *ring_info = |
---|
99 | 113 | container_of(d_work, linux_dbgring_info_t, work); |
---|
| 114 | +#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) |
---|
| 115 | +#pragma GCC diagnostic pop |
---|
| 116 | +#endif // endif |
---|
100 | 117 | dhd_pub_t *dhdp = ring_info->dhdp; |
---|
101 | 118 | int ringid = ring_info->ring_id; |
---|
102 | 119 | dhd_dbg_ring_status_t ring_status; |
---|
103 | 120 | void *buf; |
---|
104 | 121 | dhd_dbg_ring_entry_t *hdr; |
---|
105 | 122 | uint32 buflen, rlen; |
---|
| 123 | + unsigned long flags; |
---|
106 | 124 | |
---|
| 125 | + ring = &dhdp->dbg->dbg_rings[ringid]; |
---|
| 126 | + DHD_DBG_RING_LOCK(ring->lock, flags); |
---|
107 | 127 | dhd_dbg_get_ring_status(dhdp, ringid, &ring_status); |
---|
108 | | - if (ring_status.written_bytes > ring_status.read_bytes) |
---|
109 | | - buflen = ring_status.written_bytes - ring_status.read_bytes; |
---|
110 | | - else if (ring_status.written_bytes < ring_status.read_bytes) |
---|
111 | | - buflen = 0xFFFFFFFF + ring_status.written_bytes - |
---|
112 | | - ring_status.read_bytes; |
---|
113 | | - else |
---|
| 128 | + |
---|
| 129 | + if (ring->wp > ring->rp) { |
---|
| 130 | + buflen = ring->wp - ring->rp; |
---|
| 131 | + } else if (ring->wp < ring->rp) { |
---|
| 132 | + buflen = ring->ring_size - ring->rp + ring->wp; |
---|
| 133 | + } else { |
---|
114 | 134 | goto exit; |
---|
115 | | - buf = MALLOC(dhdp->osh, buflen); |
---|
| 135 | + } |
---|
| 136 | + |
---|
| 137 | + if (buflen > ring->ring_size) { |
---|
| 138 | + goto exit; |
---|
| 139 | + } |
---|
| 140 | + |
---|
| 141 | + buf = MALLOCZ(dhdp->osh, buflen); |
---|
116 | 142 | if (!buf) { |
---|
117 | 143 | DHD_ERROR(("%s failed to allocate read buf\n", __FUNCTION__)); |
---|
118 | | - return; |
---|
| 144 | + sched = FALSE; |
---|
| 145 | + goto exit; |
---|
119 | 146 | } |
---|
120 | | - rlen = dhd_dbg_ring_pull(dhdp, ringid, buf, buflen); |
---|
| 147 | + |
---|
| 148 | + rlen = dhd_dbg_pull_from_ring(dhdp, ringid, buf, buflen); |
---|
| 149 | + |
---|
| 150 | + if (!ring->sched_pull) { |
---|
| 151 | + ring->sched_pull = TRUE; |
---|
| 152 | + } |
---|
| 153 | + |
---|
121 | 154 | hdr = (dhd_dbg_ring_entry_t *)buf; |
---|
122 | 155 | while (rlen > 0) { |
---|
123 | 156 | ring_status.read_bytes += ENTRY_LENGTH(hdr); |
---|
124 | 157 | /* offset fw ts to host ts */ |
---|
125 | 158 | hdr->timestamp += ring_info->tsoffset; |
---|
126 | 159 | debug_data_send(dhdp, ringid, hdr, ENTRY_LENGTH(hdr), |
---|
127 | | - ring_status); |
---|
| 160 | + ring_status); |
---|
128 | 161 | rlen -= ENTRY_LENGTH(hdr); |
---|
129 | | - hdr = (dhd_dbg_ring_entry_t *)((void *)hdr + ENTRY_LENGTH(hdr)); |
---|
| 162 | + hdr = (dhd_dbg_ring_entry_t *)((char *)hdr + ENTRY_LENGTH(hdr)); |
---|
130 | 163 | } |
---|
131 | 164 | MFREE(dhdp->osh, buf, buflen); |
---|
132 | 165 | |
---|
133 | | - if (!ring_info->interval) |
---|
134 | | - return; |
---|
135 | | - dhd_dbg_get_ring_status(dhdp, ring_info->ring_id, &ring_status); |
---|
136 | | - |
---|
137 | 166 | exit: |
---|
138 | | - if (ring_info->interval) { |
---|
| 167 | + if (sched) { |
---|
139 | 168 | /* retrigger the work at same interval */ |
---|
140 | | - if (ring_status.written_bytes == ring_status.read_bytes) |
---|
| 169 | + if ((ring_status.written_bytes == ring_status.read_bytes) && |
---|
| 170 | + (ring_info->interval)) { |
---|
141 | 171 | schedule_delayed_work(d_work, ring_info->interval); |
---|
142 | | - else |
---|
143 | | - schedule_delayed_work(d_work, 0); |
---|
| 172 | + } |
---|
144 | 173 | } |
---|
| 174 | + |
---|
| 175 | + DHD_DBG_RING_UNLOCK(ring->lock, flags); |
---|
145 | 176 | |
---|
146 | 177 | return; |
---|
147 | 178 | } |
---|
.. | .. |
---|
173 | 204 | int ret = BCME_OK; |
---|
174 | 205 | int ring_id; |
---|
175 | 206 | linux_dbgring_info_t *os_priv, *ring_info; |
---|
176 | | - uint32 ms; |
---|
177 | 207 | |
---|
178 | 208 | ring_id = dhd_dbg_find_ring_id(dhdp, ring_name); |
---|
179 | 209 | if (!VALID_RING(ring_id)) |
---|
.. | .. |
---|
194 | 224 | return BCME_ERROR; |
---|
195 | 225 | ring_info = &os_priv[ring_id]; |
---|
196 | 226 | ring_info->log_level = log_level; |
---|
197 | | - if (ring_id == FW_VERBOSE_RING_ID || ring_id == FW_EVENT_RING_ID) { |
---|
198 | | - ring_info->tsoffset = local_clock(); |
---|
199 | | - if (dhd_wl_ioctl_get_intiovar(dhdp, "rte_timesync", &ms, WLC_GET_VAR, |
---|
200 | | - FALSE, 0)) |
---|
201 | | - DHD_ERROR(("%s rte_timesync failed\n", __FUNCTION__)); |
---|
202 | | - do_div(ring_info->tsoffset, 1000000); |
---|
203 | | - ring_info->tsoffset -= ms; |
---|
204 | | - } |
---|
| 227 | + |
---|
205 | 228 | if (time_intval == 0 || log_level == 0) { |
---|
206 | 229 | ring_info->interval = 0; |
---|
207 | 230 | cancel_delayed_work_sync(&ring_info->work); |
---|
208 | 231 | } else { |
---|
209 | 232 | ring_info->interval = msecs_to_jiffies(time_intval * MSEC_PER_SEC); |
---|
| 233 | + cancel_delayed_work_sync(&ring_info->work); |
---|
210 | 234 | schedule_delayed_work(&ring_info->work, ring_info->interval); |
---|
211 | 235 | } |
---|
212 | 236 | |
---|
.. | .. |
---|
257 | 281 | if (!os_priv) |
---|
258 | 282 | return BCME_ERROR; |
---|
259 | 283 | |
---|
260 | | - max_log_level = MAX(os_priv[FW_VERBOSE_RING_ID].log_level, |
---|
261 | | - os_priv[FW_EVENT_RING_ID].log_level); |
---|
| 284 | + max_log_level = os_priv[FW_VERBOSE_RING_ID].log_level; |
---|
| 285 | + |
---|
262 | 286 | if (max_log_level == SUPPRESS_LOG_LEVEL) { |
---|
263 | 287 | /* suppress the logging in FW not to wake up host while device in suspend mode */ |
---|
264 | 288 | ret = dhd_iovar(dhdp, 0, "logtrace", (char *)&enable, sizeof(enable), NULL, 0, |
---|
265 | | - TRUE); |
---|
| 289 | + TRUE); |
---|
266 | 290 | if (ret < 0 && (ret != BCME_UNSUPPORTED)) { |
---|
267 | 291 | DHD_ERROR(("logtrace is failed : %d\n", ret)); |
---|
268 | 292 | } |
---|
.. | .. |
---|
305 | 329 | { |
---|
306 | 330 | int ret = BCME_OK, i; |
---|
307 | 331 | dhd_dbg_ring_entry_t msg_hdr; |
---|
308 | | - log_conn_event_t event_data; |
---|
| 332 | + log_conn_event_t *event_data = (log_conn_event_t *)data; |
---|
309 | 333 | linux_dbgring_info_t *os_priv, *ring_info = NULL; |
---|
310 | 334 | |
---|
311 | 335 | if (!VALID_RING(ring_id)) |
---|
312 | 336 | return BCME_UNSUPPORTED; |
---|
313 | | - |
---|
314 | 337 | os_priv = dhd_dbg_get_priv(dhdp); |
---|
315 | | - if (!os_priv) |
---|
316 | | - return BCME_UNSUPPORTED; |
---|
317 | 338 | |
---|
318 | | - ring_info = &os_priv[ring_id]; |
---|
| 339 | + if (os_priv) { |
---|
| 340 | + ring_info = &os_priv[ring_id]; |
---|
| 341 | + } else |
---|
| 342 | + return BCME_NORESOURCE; |
---|
319 | 343 | |
---|
320 | 344 | memset(&msg_hdr, 0, sizeof(dhd_dbg_ring_entry_t)); |
---|
321 | 345 | |
---|
.. | .. |
---|
325 | 349 | msg_hdr.flags |= DBG_RING_ENTRY_FLAGS_HAS_BINARY; |
---|
326 | 350 | msg_hdr.timestamp = local_clock(); |
---|
327 | 351 | /* convert to ms */ |
---|
328 | | - do_div(msg_hdr.timestamp, 1000000); |
---|
329 | | - msg_hdr.len = sizeof(event_data); |
---|
330 | | - event_data.event = *((uint16 *)(data)); |
---|
| 352 | + msg_hdr.timestamp = DIV_U64_BY_U32(msg_hdr.timestamp, NSEC_PER_MSEC); |
---|
| 353 | + msg_hdr.len = data_len; |
---|
331 | 354 | /* filter the event for higher log level with current log level */ |
---|
332 | 355 | for (i = 0; i < ARRAYSIZE(dhd_event_map); i++) { |
---|
333 | | - if ((dhd_event_map[i].tag == event_data.event) && |
---|
| 356 | + if ((dhd_event_map[i].tag == event_data->event) && |
---|
334 | 357 | dhd_event_map[i].log_level > ring_info->log_level) { |
---|
335 | 358 | return ret; |
---|
336 | 359 | } |
---|
337 | 360 | } |
---|
338 | 361 | } |
---|
339 | | - ret = dhd_dbg_ring_push(dhdp, ring_id, &msg_hdr, &event_data); |
---|
| 362 | + ret = dhd_dbg_push_to_ring(dhdp, ring_id, &msg_hdr, event_data); |
---|
340 | 363 | if (ret) { |
---|
341 | 364 | DHD_ERROR(("%s : failed to push data into the ring (%d) with ret(%d)\n", |
---|
342 | 365 | __FUNCTION__, ring_id, ret)); |
---|
.. | .. |
---|
345 | 368 | return ret; |
---|
346 | 369 | } |
---|
347 | 370 | |
---|
| 371 | +#ifdef DBG_PKT_MON |
---|
| 372 | +int |
---|
| 373 | +dhd_os_dbg_attach_pkt_monitor(dhd_pub_t *dhdp) |
---|
| 374 | +{ |
---|
| 375 | + return dhd_dbg_attach_pkt_monitor(dhdp, dhd_os_dbg_monitor_tx_pkts, |
---|
| 376 | + dhd_os_dbg_monitor_tx_status, dhd_os_dbg_monitor_rx_pkts); |
---|
| 377 | +} |
---|
| 378 | + |
---|
| 379 | +int |
---|
| 380 | +dhd_os_dbg_start_pkt_monitor(dhd_pub_t *dhdp) |
---|
| 381 | +{ |
---|
| 382 | + return dhd_dbg_start_pkt_monitor(dhdp); |
---|
| 383 | +} |
---|
| 384 | + |
---|
| 385 | +int |
---|
| 386 | +dhd_os_dbg_monitor_tx_pkts(dhd_pub_t *dhdp, void *pkt, uint32 pktid) |
---|
| 387 | +{ |
---|
| 388 | + return dhd_dbg_monitor_tx_pkts(dhdp, pkt, pktid); |
---|
| 389 | +} |
---|
| 390 | + |
---|
| 391 | +int |
---|
| 392 | +dhd_os_dbg_monitor_tx_status(dhd_pub_t *dhdp, void *pkt, uint32 pktid, |
---|
| 393 | + uint16 status) |
---|
| 394 | +{ |
---|
| 395 | + return dhd_dbg_monitor_tx_status(dhdp, pkt, pktid, status); |
---|
| 396 | +} |
---|
| 397 | + |
---|
| 398 | +int |
---|
| 399 | +dhd_os_dbg_monitor_rx_pkts(dhd_pub_t *dhdp, void *pkt) |
---|
| 400 | +{ |
---|
| 401 | + return dhd_dbg_monitor_rx_pkts(dhdp, pkt); |
---|
| 402 | +} |
---|
| 403 | + |
---|
| 404 | +int |
---|
| 405 | +dhd_os_dbg_stop_pkt_monitor(dhd_pub_t *dhdp) |
---|
| 406 | +{ |
---|
| 407 | + return dhd_dbg_stop_pkt_monitor(dhdp); |
---|
| 408 | +} |
---|
| 409 | + |
---|
| 410 | +int |
---|
| 411 | +dhd_os_dbg_monitor_get_tx_pkts(dhd_pub_t *dhdp, void __user *user_buf, |
---|
| 412 | + uint16 req_count, uint16 *resp_count) |
---|
| 413 | +{ |
---|
| 414 | + return dhd_dbg_monitor_get_tx_pkts(dhdp, user_buf, req_count, resp_count); |
---|
| 415 | +} |
---|
| 416 | + |
---|
| 417 | +int |
---|
| 418 | +dhd_os_dbg_monitor_get_rx_pkts(dhd_pub_t *dhdp, void __user *user_buf, |
---|
| 419 | + uint16 req_count, uint16 *resp_count) |
---|
| 420 | +{ |
---|
| 421 | + return dhd_dbg_monitor_get_rx_pkts(dhdp, user_buf, req_count, resp_count); |
---|
| 422 | +} |
---|
| 423 | + |
---|
| 424 | +int |
---|
| 425 | +dhd_os_dbg_detach_pkt_monitor(dhd_pub_t *dhdp) |
---|
| 426 | +{ |
---|
| 427 | + return dhd_dbg_detach_pkt_monitor(dhdp); |
---|
| 428 | +} |
---|
| 429 | +#endif /* DBG_PKT_MON */ |
---|
| 430 | + |
---|
348 | 431 | int |
---|
349 | 432 | dhd_os_dbg_get_feature(dhd_pub_t *dhdp, int32 *features) |
---|
350 | 433 | { |
---|
351 | 434 | int ret = BCME_OK; |
---|
352 | 435 | *features = 0; |
---|
| 436 | +#ifdef DEBUGABILITY |
---|
353 | 437 | *features |= DBG_MEMORY_DUMP_SUPPORTED; |
---|
354 | 438 | if (FW_SUPPORTED(dhdp, logtrace)) { |
---|
355 | 439 | *features |= DBG_CONNECT_EVENT_SUPPORTED; |
---|
.. | .. |
---|
358 | 442 | if (FW_SUPPORTED(dhdp, hchk)) { |
---|
359 | 443 | *features |= DBG_HEALTH_CHECK_SUPPORTED; |
---|
360 | 444 | } |
---|
| 445 | +#ifdef DBG_PKT_MON |
---|
| 446 | + if (FW_SUPPORTED(dhdp, d11status)) { |
---|
| 447 | + *features |= DBG_PACKET_FATE_SUPPORTED; |
---|
| 448 | + } |
---|
| 449 | +#endif /* DBG_PKT_MON */ |
---|
| 450 | +#endif /* DEBUGABILITY */ |
---|
361 | 451 | return ret; |
---|
362 | 452 | } |
---|
363 | 453 | |
---|
.. | .. |
---|
367 | 457 | linux_dbgring_info_t *ring_info; |
---|
368 | 458 | |
---|
369 | 459 | ring_info = &((linux_dbgring_info_t *)os_priv)[ring_id]; |
---|
370 | | - if (ring_info->interval != 0) |
---|
371 | | - schedule_delayed_work(&ring_info->work, 0); |
---|
| 460 | + cancel_delayed_work(&ring_info->work); |
---|
| 461 | + schedule_delayed_work(&ring_info->work, 0); |
---|
372 | 462 | } |
---|
373 | 463 | |
---|
374 | 464 | int |
---|
.. | .. |
---|
405 | 495 | int ring_id; |
---|
406 | 496 | /* free os_dbg data */ |
---|
407 | 497 | os_priv = dhd_dbg_get_priv(dhdp); |
---|
| 498 | + if (!os_priv) |
---|
| 499 | + return; |
---|
408 | 500 | /* abort pending any job */ |
---|
409 | 501 | for (ring_id = DEBUG_RING_ID_INVALID + 1; ring_id < DEBUG_RING_ID_MAX; ring_id++) { |
---|
410 | 502 | ring_info = &os_priv[ring_id]; |
---|