1 /*
2 * Copyright (c) 2020, The Linux Foundation. All rights reserved.
3 * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #include <qdf_hang_event_notifier.h>
18 #include <qdf_notifier.h>
19 #include <wlan_hdd_hang_event.h>
20 #include <wlan_objmgr_vdev_obj.h>
21 #include "wlan_hdd_object_manager.h"
22 #include <qdf_types.h>
23
24 struct hdd_hang_event_fixed_param {
25 uint16_t tlv_header;
26 uint8_t vdev_id;
27 uint8_t vdev_opmode;
28 uint8_t vdev_state;
29 uint8_t vdev_substate;
30 } qdf_packed;
31
32 struct hdd_scan_fixed_param {
33 uint16_t tlv_header;
34 uint8_t last_scan_reject_vdev_id;
35 enum scan_reject_states last_scan_reject_reason;
36 unsigned long last_scan_reject_timestamp;
37 uint8_t scan_reject_cnt;
38 } qdf_packed;
39
wlan_hdd_recovery_notifier_call(struct notifier_block * block,unsigned long state,void * data)40 static int wlan_hdd_recovery_notifier_call(struct notifier_block *block,
41 unsigned long state,
42 void *data)
43 {
44 qdf_notif_block *notif_block = qdf_container_of(block, qdf_notif_block,
45 notif_block);
46 struct hdd_context *hdd_ctx;
47 struct qdf_notifer_data *hdd_hang_data = data;
48 uint8_t *hdd_buf_ptr;
49 struct hdd_adapter *adapter, *next_adapter = NULL;
50 uint32_t total_len;
51 struct wlan_objmgr_vdev *vdev;
52 struct hdd_hang_event_fixed_param *cmd;
53 struct hdd_scan_fixed_param *cmd_scan;
54 wlan_net_dev_ref_dbgid dbgid = NET_DEV_HOLD_RECOVERY_NOTIFIER_CALL;
55
56 if (!data)
57 return NOTIFY_STOP_MASK;
58
59 hdd_ctx = notif_block->priv_data;
60 if (!hdd_ctx)
61 return NOTIFY_STOP_MASK;
62
63 if (state == QDF_SCAN_ATTEMPT_FAILURES) {
64 total_len = sizeof(*cmd_scan);
65 hdd_buf_ptr = hdd_hang_data->hang_data + hdd_hang_data->offset;
66 if (hdd_hang_data->offset + total_len > QDF_WLAN_HANG_FW_OFFSET)
67 return NOTIFY_STOP_MASK;
68 cmd_scan = (struct hdd_scan_fixed_param *)hdd_buf_ptr;
69 QDF_HANG_EVT_SET_HDR(&cmd_scan->tlv_header,
70 HANG_EVT_TAG_OS_IF_SCAN,
71 QDF_HANG_GET_STRUCT_TLVLEN(struct hdd_scan_fixed_param));
72 cmd_scan->last_scan_reject_vdev_id =
73 hdd_ctx->last_scan_reject_vdev_id;
74 cmd_scan->last_scan_reject_reason =
75 hdd_ctx->last_scan_reject_reason;
76 cmd_scan->scan_reject_cnt =
77 hdd_ctx->scan_reject_cnt;
78 hdd_hang_data->offset += total_len;
79 }
80
81 hdd_for_each_adapter_dev_held_safe(hdd_ctx, adapter, next_adapter,
82 dbgid) {
83 vdev = hdd_objmgr_get_vdev_by_user(adapter->deflink,
84 WLAN_OSIF_ID);
85 if (!vdev) {
86 hdd_adapter_dev_put_debug(adapter, dbgid);
87 continue;
88 }
89 total_len = sizeof(*cmd);
90 hdd_buf_ptr = hdd_hang_data->hang_data + hdd_hang_data->offset;
91 if (hdd_hang_data->offset + total_len >
92 QDF_WLAN_HANG_FW_OFFSET) {
93 hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID);
94 hdd_adapter_dev_put_debug(adapter, dbgid);
95 if (next_adapter)
96 hdd_adapter_dev_put_debug(next_adapter,
97 dbgid);
98 return NOTIFY_STOP_MASK;
99 }
100 cmd = (struct hdd_hang_event_fixed_param *)hdd_buf_ptr;
101 QDF_HANG_EVT_SET_HDR(&cmd->tlv_header,
102 HANG_EVT_TAG_OS_IF,
103 QDF_HANG_GET_STRUCT_TLVLEN(struct hdd_hang_event_fixed_param));
104 cmd->vdev_id = wlan_vdev_get_id(vdev);
105 cmd->vdev_opmode = wlan_vdev_mlme_get_opmode(vdev);
106 cmd->vdev_state = wlan_vdev_mlme_get_state(vdev);
107 cmd->vdev_substate = wlan_vdev_mlme_get_substate(vdev);
108 hdd_hang_data->offset += total_len;
109 hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID);
110 hdd_adapter_dev_put_debug(adapter, dbgid);
111 }
112
113 return NOTIFY_OK;
114 }
115
116 static qdf_notif_block hdd_recovery_notifier = {
117 .notif_block.notifier_call = wlan_hdd_recovery_notifier_call,
118 };
119
wlan_hdd_hang_event_notifier_register(struct hdd_context * hdd_ctx)120 QDF_STATUS wlan_hdd_hang_event_notifier_register(struct hdd_context *hdd_ctx)
121 {
122 hdd_recovery_notifier.priv_data = hdd_ctx;
123 return qdf_hang_event_register_notifier(&hdd_recovery_notifier);
124 }
125
wlan_hdd_hang_event_notifier_unregister(void)126 QDF_STATUS wlan_hdd_hang_event_notifier_unregister(void)
127 {
128 return qdf_hang_event_unregister_notifier(&hdd_recovery_notifier);
129 }
130