dhd_debug_linux.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. /*
  2. * DHD debugability Linux os layer
  3. *
  4. * <<Broadcom-WL-IPTag/Open:>>
  5. *
  6. * Portions of this code are copyright (c) 2020 Cypress Semiconductor Corporation
  7. *
  8. * Copyright (C) 1999-2020, Broadcom Corporation
  9. *
  10. * Unless you and Broadcom execute a separate written software license
  11. * agreement governing use of this software, this software is licensed to you
  12. * under the terms of the GNU General Public License version 2 (the "GPL"),
  13. * available at http://www.broadcom.com/licenses/GPLv2.php, with the
  14. * following added to such license:
  15. *
  16. * As a special exception, the copyright holders of this software give you
  17. * permission to link this software with independent modules, and to copy and
  18. * distribute the resulting executable under terms of your choice, provided that
  19. * you also meet, for each linked independent module, the terms and conditions of
  20. * the license of that module. An independent module is a module which is not
  21. * derived from this software. The special exception does not apply to any
  22. * modifications of the software.
  23. *
  24. * Notwithstanding the above, under no circumstances may you combine this
  25. * software in any way with any other Broadcom software provided under a license
  26. * other than the GPL, without Broadcom's express prior written consent.
  27. *
  28. * $Id: dhd_debug_linux.c 696754 2017-04-28 00:21:58Z $
  29. */
  30. #include <typedefs.h>
  31. #include <osl.h>
  32. #include <bcmutils.h>
  33. #include <bcmendian.h>
  34. #include <dngl_stats.h>
  35. #include <dhd.h>
  36. #include <dhd_dbg.h>
  37. #include <dhd_debug.h>
  38. #include <net/cfg80211.h>
  39. #include <wl_cfgvendor.h>
  40. typedef void (*dbg_ring_send_sub_t)(void *ctx, const int ring_id, const void *data,
  41. const uint32 len, const dhd_dbg_ring_status_t ring_status);
  42. typedef void (*dbg_urgent_noti_sub_t)(void *ctx, const void *data,
  43. const uint32 len, const uint32 fw_len);
  44. static dbg_ring_send_sub_t ring_send_sub_cb[DEBUG_RING_ID_MAX];
  45. static dbg_urgent_noti_sub_t urgent_noti_sub_cb;
  46. typedef struct dhd_dbg_os_ring_info {
  47. dhd_pub_t *dhdp;
  48. int ring_id;
  49. int log_level;
  50. unsigned long interval;
  51. struct delayed_work work;
  52. uint64 tsoffset;
  53. } linux_dbgring_info_t;
  54. struct log_level_table dhd_event_map[] = {
  55. {1, WIFI_EVENT_DRIVER_EAPOL_FRAME_TRANSMIT_REQUESTED, "DRIVER EAPOL TX REQ"},
  56. {1, WIFI_EVENT_DRIVER_EAPOL_FRAME_RECEIVED, "DRIVER EAPOL RX"},
  57. {2, WIFI_EVENT_DRIVER_SCAN_REQUESTED, "SCAN_REQUESTED"},
  58. {2, WIFI_EVENT_DRIVER_SCAN_COMPLETE, "SCAN COMPELETE"},
  59. {3, WIFI_EVENT_DRIVER_SCAN_RESULT_FOUND, "SCAN RESULT FOUND"},
  60. {2, WIFI_EVENT_DRIVER_PNO_ADD, "PNO ADD"},
  61. {2, WIFI_EVENT_DRIVER_PNO_REMOVE, "PNO REMOVE"},
  62. {2, WIFI_EVENT_DRIVER_PNO_NETWORK_FOUND, "PNO NETWORK FOUND"},
  63. {2, WIFI_EVENT_DRIVER_PNO_SCAN_REQUESTED, "PNO SCAN_REQUESTED"},
  64. {1, WIFI_EVENT_DRIVER_PNO_SCAN_RESULT_FOUND, "PNO SCAN RESULT FOUND"},
  65. {1, WIFI_EVENT_DRIVER_PNO_SCAN_COMPLETE, "PNO SCAN COMPELETE"}
  66. };
  67. static void
  68. debug_data_send(dhd_pub_t *dhdp, int ring_id, const void *data, const uint32 len,
  69. const dhd_dbg_ring_status_t ring_status)
  70. {
  71. struct net_device *ndev;
  72. dbg_ring_send_sub_t ring_sub_send;
  73. ndev = dhd_linux_get_primary_netdev(dhdp);
  74. if (!ndev)
  75. return;
  76. if (!VALID_RING(ring_id))
  77. return;
  78. if (ring_send_sub_cb[ring_id]) {
  79. ring_sub_send = ring_send_sub_cb[ring_id];
  80. ring_sub_send(ndev, ring_id, data, len, ring_status);
  81. }
  82. }
  83. static void
  84. dhd_os_dbg_urgent_notifier(dhd_pub_t *dhdp, const void *data, const uint32 len)
  85. {
  86. struct net_device *ndev;
  87. ndev = dhd_linux_get_primary_netdev(dhdp);
  88. if (!ndev)
  89. return;
  90. if (urgent_noti_sub_cb) {
  91. urgent_noti_sub_cb(ndev, data, len, dhdp->soc_ram_length);
  92. }
  93. }
  94. static void
  95. dbg_ring_poll_worker(struct work_struct *work)
  96. {
  97. struct delayed_work *d_work = to_delayed_work(work);
  98. bool sched = TRUE;
  99. dhd_dbg_ring_t *ring;
  100. #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
  101. #pragma GCC diagnostic push
  102. #pragma GCC diagnostic ignored "-Wcast-qual"
  103. #endif // endif
  104. linux_dbgring_info_t *ring_info =
  105. container_of(d_work, linux_dbgring_info_t, work);
  106. #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
  107. #pragma GCC diagnostic pop
  108. #endif // endif
  109. dhd_pub_t *dhdp = ring_info->dhdp;
  110. int ringid = ring_info->ring_id;
  111. dhd_dbg_ring_status_t ring_status;
  112. void *buf;
  113. dhd_dbg_ring_entry_t *hdr;
  114. uint32 buflen, rlen;
  115. unsigned long flags;
  116. ring = &dhdp->dbg->dbg_rings[ringid];
  117. DHD_DBG_RING_LOCK(ring->lock, flags);
  118. dhd_dbg_get_ring_status(dhdp, ringid, &ring_status);
  119. if (ring->wp > ring->rp) {
  120. buflen = ring->wp - ring->rp;
  121. } else if (ring->wp < ring->rp) {
  122. buflen = ring->ring_size - ring->rp + ring->wp;
  123. } else {
  124. goto exit;
  125. }
  126. if (buflen > ring->ring_size) {
  127. goto exit;
  128. }
  129. buf = MALLOCZ(dhdp->osh, buflen);
  130. if (!buf) {
  131. DHD_ERROR(("%s failed to allocate read buf\n", __FUNCTION__));
  132. sched = FALSE;
  133. goto exit;
  134. }
  135. rlen = dhd_dbg_pull_from_ring(dhdp, ringid, buf, buflen);
  136. if (!ring->sched_pull) {
  137. ring->sched_pull = TRUE;
  138. }
  139. hdr = (dhd_dbg_ring_entry_t *)buf;
  140. while (rlen > 0) {
  141. ring_status.read_bytes += ENTRY_LENGTH(hdr);
  142. /* offset fw ts to host ts */
  143. hdr->timestamp += ring_info->tsoffset;
  144. debug_data_send(dhdp, ringid, hdr, ENTRY_LENGTH(hdr),
  145. ring_status);
  146. rlen -= ENTRY_LENGTH(hdr);
  147. hdr = (dhd_dbg_ring_entry_t *)((char *)hdr + ENTRY_LENGTH(hdr));
  148. }
  149. MFREE(dhdp->osh, buf, buflen);
  150. exit:
  151. if (sched) {
  152. /* retrigger the work at same interval */
  153. if ((ring_status.written_bytes == ring_status.read_bytes) &&
  154. (ring_info->interval)) {
  155. schedule_delayed_work(d_work, ring_info->interval);
  156. }
  157. }
  158. DHD_DBG_RING_UNLOCK(ring->lock, flags);
  159. return;
  160. }
  161. int
  162. dhd_os_dbg_register_callback(int ring_id, dbg_ring_send_sub_t callback)
  163. {
  164. if (!VALID_RING(ring_id))
  165. return BCME_RANGE;
  166. ring_send_sub_cb[ring_id] = callback;
  167. return BCME_OK;
  168. }
  169. int
  170. dhd_os_dbg_register_urgent_notifier(dhd_pub_t *dhdp, dbg_urgent_noti_sub_t urgent_noti_sub)
  171. {
  172. if (!dhdp || !urgent_noti_sub)
  173. return BCME_BADARG;
  174. urgent_noti_sub_cb = urgent_noti_sub;
  175. return BCME_OK;
  176. }
  177. int
  178. dhd_os_start_logging(dhd_pub_t *dhdp, char *ring_name, int log_level,
  179. int flags, int time_intval, int threshold)
  180. {
  181. int ret = BCME_OK;
  182. int ring_id;
  183. linux_dbgring_info_t *os_priv, *ring_info;
  184. ring_id = dhd_dbg_find_ring_id(dhdp, ring_name);
  185. if (!VALID_RING(ring_id))
  186. return BCME_UNSUPPORTED;
  187. DHD_DBGIF(("%s , log_level : %d, time_intval : %d, threshod %d Bytes\n",
  188. __FUNCTION__, log_level, time_intval, threshold));
  189. /* change the configuration */
  190. ret = dhd_dbg_set_configuration(dhdp, ring_id, log_level, flags, threshold);
  191. if (ret) {
  192. DHD_ERROR(("dhd_set_configuration is failed : %d\n", ret));
  193. return ret;
  194. }
  195. os_priv = dhd_dbg_get_priv(dhdp);
  196. if (!os_priv)
  197. return BCME_ERROR;
  198. ring_info = &os_priv[ring_id];
  199. ring_info->log_level = log_level;
  200. if (time_intval == 0 || log_level == 0) {
  201. ring_info->interval = 0;
  202. cancel_delayed_work_sync(&ring_info->work);
  203. } else {
  204. ring_info->interval = msecs_to_jiffies(time_intval * MSEC_PER_SEC);
  205. cancel_delayed_work_sync(&ring_info->work);
  206. schedule_delayed_work(&ring_info->work, ring_info->interval);
  207. }
  208. return ret;
  209. }
  210. int
  211. dhd_os_reset_logging(dhd_pub_t *dhdp)
  212. {
  213. int ret = BCME_OK;
  214. int ring_id;
  215. linux_dbgring_info_t *os_priv, *ring_info;
  216. os_priv = dhd_dbg_get_priv(dhdp);
  217. if (!os_priv)
  218. return BCME_ERROR;
  219. /* Stop all rings */
  220. for (ring_id = DEBUG_RING_ID_INVALID + 1; ring_id < DEBUG_RING_ID_MAX; ring_id++) {
  221. DHD_DBGIF(("%s: Stop ring buffer %d\n", __FUNCTION__, ring_id));
  222. ring_info = &os_priv[ring_id];
  223. /* cancel any pending work */
  224. cancel_delayed_work_sync(&ring_info->work);
  225. /* log level zero makes stop logging on that ring */
  226. ring_info->log_level = 0;
  227. ring_info->interval = 0;
  228. /* change the configuration */
  229. ret = dhd_dbg_set_configuration(dhdp, ring_id, 0, 0, 0);
  230. if (ret) {
  231. DHD_ERROR(("dhd_set_configuration is failed : %d\n", ret));
  232. return ret;
  233. }
  234. }
  235. return ret;
  236. }
  237. #define SUPPRESS_LOG_LEVEL 1
  238. int
  239. dhd_os_suppress_logging(dhd_pub_t *dhdp, bool suppress)
  240. {
  241. int ret = BCME_OK;
  242. int max_log_level;
  243. int enable = (suppress) ? 0 : 1;
  244. linux_dbgring_info_t *os_priv;
  245. os_priv = dhd_dbg_get_priv(dhdp);
  246. if (!os_priv)
  247. return BCME_ERROR;
  248. max_log_level = os_priv[FW_VERBOSE_RING_ID].log_level;
  249. if (max_log_level == SUPPRESS_LOG_LEVEL) {
  250. /* suppress the logging in FW not to wake up host while device in suspend mode */
  251. ret = dhd_iovar(dhdp, 0, "logtrace", (char *)&enable, sizeof(enable), NULL, 0,
  252. TRUE);
  253. if (ret < 0 && (ret != BCME_UNSUPPORTED)) {
  254. DHD_ERROR(("logtrace is failed : %d\n", ret));
  255. }
  256. }
  257. return ret;
  258. }
  259. int
  260. dhd_os_get_ring_status(dhd_pub_t *dhdp, int ring_id, dhd_dbg_ring_status_t *dbg_ring_status)
  261. {
  262. return dhd_dbg_get_ring_status(dhdp, ring_id, dbg_ring_status);
  263. }
  264. int
  265. dhd_os_trigger_get_ring_data(dhd_pub_t *dhdp, char *ring_name)
  266. {
  267. int ret = BCME_OK;
  268. int ring_id;
  269. linux_dbgring_info_t *os_priv, *ring_info;
  270. ring_id = dhd_dbg_find_ring_id(dhdp, ring_name);
  271. if (!VALID_RING(ring_id))
  272. return BCME_UNSUPPORTED;
  273. os_priv = dhd_dbg_get_priv(dhdp);
  274. if (os_priv) {
  275. ring_info = &os_priv[ring_id];
  276. if (ring_info->interval) {
  277. cancel_delayed_work_sync(&ring_info->work);
  278. }
  279. schedule_delayed_work(&ring_info->work, 0);
  280. } else {
  281. DHD_ERROR(("%s : os_priv is NULL\n", __FUNCTION__));
  282. ret = BCME_ERROR;
  283. }
  284. return ret;
  285. }
  286. int
  287. dhd_os_push_push_ring_data(dhd_pub_t *dhdp, int ring_id, void *data, int32 data_len)
  288. {
  289. int ret = BCME_OK, i;
  290. dhd_dbg_ring_entry_t msg_hdr;
  291. log_conn_event_t *event_data = (log_conn_event_t *)data;
  292. linux_dbgring_info_t *os_priv, *ring_info = NULL;
  293. if (!VALID_RING(ring_id))
  294. return BCME_UNSUPPORTED;
  295. os_priv = dhd_dbg_get_priv(dhdp);
  296. if (os_priv) {
  297. ring_info = &os_priv[ring_id];
  298. } else
  299. return BCME_NORESOURCE;
  300. memset(&msg_hdr, 0, sizeof(dhd_dbg_ring_entry_t));
  301. if (ring_id == DHD_EVENT_RING_ID) {
  302. msg_hdr.type = DBG_RING_ENTRY_EVENT_TYPE;
  303. msg_hdr.flags |= DBG_RING_ENTRY_FLAGS_HAS_TIMESTAMP;
  304. msg_hdr.flags |= DBG_RING_ENTRY_FLAGS_HAS_BINARY;
  305. msg_hdr.timestamp = local_clock();
  306. /* convert to ms */
  307. msg_hdr.timestamp = DIV_U64_BY_U32(msg_hdr.timestamp, NSEC_PER_MSEC);
  308. msg_hdr.len = data_len;
  309. /* filter the event for higher log level with current log level */
  310. for (i = 0; i < ARRAYSIZE(dhd_event_map); i++) {
  311. if ((dhd_event_map[i].tag == event_data->event) &&
  312. dhd_event_map[i].log_level > ring_info->log_level) {
  313. return ret;
  314. }
  315. }
  316. }
  317. ret = dhd_dbg_push_to_ring(dhdp, ring_id, &msg_hdr, event_data);
  318. if (ret) {
  319. DHD_ERROR(("%s : failed to push data into the ring (%d) with ret(%d)\n",
  320. __FUNCTION__, ring_id, ret));
  321. }
  322. return ret;
  323. }
  324. #ifdef DBG_PKT_MON
  325. int
  326. dhd_os_dbg_attach_pkt_monitor(dhd_pub_t *dhdp)
  327. {
  328. return dhd_dbg_attach_pkt_monitor(dhdp, dhd_os_dbg_monitor_tx_pkts,
  329. dhd_os_dbg_monitor_tx_status, dhd_os_dbg_monitor_rx_pkts);
  330. }
  331. int
  332. dhd_os_dbg_start_pkt_monitor(dhd_pub_t *dhdp)
  333. {
  334. return dhd_dbg_start_pkt_monitor(dhdp);
  335. }
  336. int
  337. dhd_os_dbg_monitor_tx_pkts(dhd_pub_t *dhdp, void *pkt, uint32 pktid)
  338. {
  339. return dhd_dbg_monitor_tx_pkts(dhdp, pkt, pktid);
  340. }
  341. int
  342. dhd_os_dbg_monitor_tx_status(dhd_pub_t *dhdp, void *pkt, uint32 pktid,
  343. uint16 status)
  344. {
  345. return dhd_dbg_monitor_tx_status(dhdp, pkt, pktid, status);
  346. }
  347. int
  348. dhd_os_dbg_monitor_rx_pkts(dhd_pub_t *dhdp, void *pkt)
  349. {
  350. return dhd_dbg_monitor_rx_pkts(dhdp, pkt);
  351. }
  352. int
  353. dhd_os_dbg_stop_pkt_monitor(dhd_pub_t *dhdp)
  354. {
  355. return dhd_dbg_stop_pkt_monitor(dhdp);
  356. }
  357. int
  358. dhd_os_dbg_monitor_get_tx_pkts(dhd_pub_t *dhdp, void __user *user_buf,
  359. uint16 req_count, uint16 *resp_count)
  360. {
  361. return dhd_dbg_monitor_get_tx_pkts(dhdp, user_buf, req_count, resp_count);
  362. }
  363. int
  364. dhd_os_dbg_monitor_get_rx_pkts(dhd_pub_t *dhdp, void __user *user_buf,
  365. uint16 req_count, uint16 *resp_count)
  366. {
  367. return dhd_dbg_monitor_get_rx_pkts(dhdp, user_buf, req_count, resp_count);
  368. }
  369. int
  370. dhd_os_dbg_detach_pkt_monitor(dhd_pub_t *dhdp)
  371. {
  372. return dhd_dbg_detach_pkt_monitor(dhdp);
  373. }
  374. #endif /* DBG_PKT_MON */
  375. int
  376. dhd_os_dbg_get_feature(dhd_pub_t *dhdp, int32 *features)
  377. {
  378. int ret = BCME_OK;
  379. *features = 0;
  380. #ifdef DEBUGABILITY
  381. *features |= DBG_MEMORY_DUMP_SUPPORTED;
  382. if (FW_SUPPORTED(dhdp, logtrace)) {
  383. *features |= DBG_CONNECT_EVENT_SUPPORTED;
  384. *features |= DBG_VERBOSE_LOG_SUPPORTED;
  385. }
  386. if (FW_SUPPORTED(dhdp, hchk)) {
  387. *features |= DBG_HEALTH_CHECK_SUPPORTED;
  388. }
  389. #ifdef DBG_PKT_MON
  390. if (FW_SUPPORTED(dhdp, d11status)) {
  391. *features |= DBG_PACKET_FATE_SUPPORTED;
  392. }
  393. #endif /* DBG_PKT_MON */
  394. #endif /* DEBUGABILITY */
  395. return ret;
  396. }
  397. static void
  398. dhd_os_dbg_pullreq(void *os_priv, int ring_id)
  399. {
  400. linux_dbgring_info_t *ring_info;
  401. ring_info = &((linux_dbgring_info_t *)os_priv)[ring_id];
  402. cancel_delayed_work(&ring_info->work);
  403. schedule_delayed_work(&ring_info->work, 0);
  404. }
  405. int
  406. dhd_os_dbg_attach(dhd_pub_t *dhdp)
  407. {
  408. int ret = BCME_OK;
  409. linux_dbgring_info_t *os_priv, *ring_info;
  410. int ring_id;
  411. /* os_dbg data */
  412. os_priv = MALLOCZ(dhdp->osh, sizeof(*os_priv) * DEBUG_RING_ID_MAX);
  413. if (!os_priv)
  414. return BCME_NOMEM;
  415. for (ring_id = DEBUG_RING_ID_INVALID + 1; ring_id < DEBUG_RING_ID_MAX;
  416. ring_id++) {
  417. ring_info = &os_priv[ring_id];
  418. INIT_DELAYED_WORK(&ring_info->work, dbg_ring_poll_worker);
  419. ring_info->dhdp = dhdp;
  420. ring_info->ring_id = ring_id;
  421. }
  422. ret = dhd_dbg_attach(dhdp, dhd_os_dbg_pullreq, dhd_os_dbg_urgent_notifier, os_priv);
  423. if (ret)
  424. MFREE(dhdp->osh, os_priv, sizeof(*os_priv) * DEBUG_RING_ID_MAX);
  425. return ret;
  426. }
  427. void
  428. dhd_os_dbg_detach(dhd_pub_t *dhdp)
  429. {
  430. linux_dbgring_info_t *os_priv, *ring_info;
  431. int ring_id;
  432. /* free os_dbg data */
  433. os_priv = dhd_dbg_get_priv(dhdp);
  434. if (!os_priv)
  435. return;
  436. /* abort pending any job */
  437. for (ring_id = DEBUG_RING_ID_INVALID + 1; ring_id < DEBUG_RING_ID_MAX; ring_id++) {
  438. ring_info = &os_priv[ring_id];
  439. if (ring_info->interval) {
  440. ring_info->interval = 0;
  441. cancel_delayed_work_sync(&ring_info->work);
  442. }
  443. }
  444. MFREE(dhdp->osh, os_priv, sizeof(*os_priv) * DEBUG_RING_ID_MAX);
  445. return dhd_dbg_detach(dhdp);
  446. }