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