qcacld-3.0: Update hdd hang data

Whenever a recovery is triggered in the hdd layer update the
various vdev data for hang event:
	1) Vdev id
	2) vdev opmode
	3) vdev state and substate

if the reason for recovery is max scan failures update the last
scan reject, reason and the vdev id on which scan is rejected.

Change-Id: I84928e56bac1fe58e7eada0a0574b2f23124ae65
CRs-Fixed: 2651694
diff --git a/Kbuild b/Kbuild
index 55af7d3..6ecc05c 100644
--- a/Kbuild
+++ b/Kbuild
@@ -277,6 +277,10 @@
 HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_cfr.o
 endif
 
+ifeq ($(CONFIG_WLAN_HANG_EVENT), y)
+HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_hang_event.o
+endif
+
 ###### OSIF_SYNC ########
 SYNC_DIR := os_if/sync
 SYNC_INC_DIR := $(SYNC_DIR)/inc
diff --git a/core/hdd/inc/wlan_hdd_hang_event.h b/core/hdd/inc/wlan_hdd_hang_event.h
new file mode 100644
index 0000000..3376b2c
--- /dev/null
+++ b/core/hdd/inc/wlan_hdd_hang_event.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef WLAN_HDD_HANG_EVENT_H
+#define WLAN_HDD_HANG_EVENT_H
+#include <qdf_hang_event_notifier.h>
+#include <wlan_hdd_main.h>
+
+#ifdef WLAN_HANG_EVENT
+/**
+ * wlan_hdd_hang_event_notifier_register() - HDD hang event notifier register
+ * @hdd_ctx: HDD context
+ *
+ * This function registers hdd layer notifier for the hang event notifier chain.
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS wlan_hdd_hang_event_notifier_register(struct hdd_context *hdd_ctx);
+/**
+ * wlan_hdd_hang_event_notifier_unregister() - HDD hang event notifier
+ * unregister
+ * @hdd_ctx: HDD context
+ *
+ * This function unregisters hdd layer notifier for the hang event notifier
+ * chain.
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS wlan_hdd_hang_event_notifier_unregister(void);
+#else
+static inline
+QDF_STATUS wlan_hdd_hang_event_notifier_register(struct hdd_context *hdd_ctx)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static inline
+QDF_STATUS wlan_hdd_hang_event_notifier_unregister(void)
+{
+	return QDF_STATUS_SUCCESS;
+}
+#endif
+#endif
diff --git a/core/hdd/src/wlan_hdd_hang_event.c b/core/hdd/src/wlan_hdd_hang_event.c
new file mode 100644
index 0000000..2f0ab34
--- /dev/null
+++ b/core/hdd/src/wlan_hdd_hang_event.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <qdf_hang_event_notifier.h>
+#include <qdf_notifier.h>
+#include <wlan_hdd_hang_event.h>
+#include <wlan_objmgr_vdev_obj.h>
+#include "wlan_hdd_object_manager.h"
+#include <qdf_types.h>
+
+struct hdd_hang_event_fixed_param  {
+	uint32_t tlv_header;
+	uint8_t vdev_id;
+	uint8_t vdev_opmode;
+	uint8_t vdev_state;
+	uint8_t vdev_substate;
+} qdf_packed;
+
+struct hdd_scan_fixed_param {
+	uint32_t tlv_header;
+	uint8_t last_scan_reject_vdev_id;
+	enum scan_reject_states last_scan_reject_reason;
+	unsigned long last_scan_reject_timestamp;
+	uint8_t scan_reject_cnt;
+} qdf_packed;
+
+static int wlan_hdd_recovery_notifier_call(struct notifier_block *block,
+					   unsigned long state,
+					   void *data)
+{
+	qdf_notif_block *notif_block = qdf_container_of(block, qdf_notif_block,
+							notif_block);
+	struct hdd_context *hdd_ctx;
+	struct qdf_notifer_data *hdd_hang_data = data;
+	uint8_t *hdd_buf_ptr;
+	struct hdd_adapter *adapter;
+	uint32_t total_len;
+	struct wlan_objmgr_vdev *vdev;
+	struct hdd_hang_event_fixed_param *cmd;
+	struct hdd_scan_fixed_param *cmd_scan;
+
+	if (!data)
+		return NOTIFY_STOP_MASK;
+
+	hdd_ctx = notif_block->priv_data;
+	if (!hdd_ctx)
+		return NOTIFY_STOP_MASK;
+
+	if (hdd_hang_data->offset >= QDF_WLAN_MAX_HOST_OFFSET)
+		return NOTIFY_STOP_MASK;
+
+	if (state == QDF_SCAN_ATTEMPT_FAILURES) {
+		total_len = sizeof(*cmd_scan);
+		hdd_buf_ptr = hdd_hang_data->hang_data + hdd_hang_data->offset;
+		cmd_scan = (struct hdd_scan_fixed_param *)hdd_buf_ptr;
+		QDF_HANG_EVT_SET_HDR(&cmd_scan->tlv_header,
+				     HANG_EVT_TAG_OS_IF_SCAN,
+		QDF_HANG_GET_STRUCT_TLVLEN(struct hdd_scan_fixed_param));
+		cmd_scan->last_scan_reject_vdev_id =
+					hdd_ctx->last_scan_reject_vdev_id;
+		cmd_scan->last_scan_reject_reason =
+					hdd_ctx->last_scan_reject_reason;
+		cmd_scan->scan_reject_cnt =
+					hdd_ctx->scan_reject_cnt;
+		hdd_hang_data->offset += total_len;
+	}
+
+	hdd_for_each_adapter_dev_held(hdd_ctx, adapter) {
+		vdev = hdd_objmgr_get_vdev(adapter);
+		if (!vdev) {
+			dev_put(adapter->dev);
+			continue;
+		}
+		total_len = sizeof(*cmd);
+		hdd_buf_ptr = hdd_hang_data->hang_data + hdd_hang_data->offset;
+		cmd = (struct hdd_hang_event_fixed_param *)hdd_buf_ptr;
+		QDF_HANG_EVT_SET_HDR(&cmd->tlv_header,
+				     HANG_EVT_TAG_OS_IF,
+		QDF_HANG_GET_STRUCT_TLVLEN(struct hdd_hang_event_fixed_param));
+		cmd->vdev_id = wlan_vdev_get_id(vdev);
+		cmd->vdev_opmode = wlan_vdev_mlme_get_opmode(vdev);
+		cmd->vdev_state = wlan_vdev_mlme_get_state(vdev);
+		cmd->vdev_substate = wlan_vdev_mlme_get_substate(vdev);
+		hdd_hang_data->offset += total_len;
+		hdd_objmgr_put_vdev(vdev);
+		dev_put(adapter->dev);
+	}
+
+	return NOTIFY_OK;
+}
+
+static qdf_notif_block hdd_recovery_notifier = {
+	.notif_block.notifier_call = wlan_hdd_recovery_notifier_call,
+};
+
+QDF_STATUS wlan_hdd_hang_event_notifier_register(struct hdd_context *hdd_ctx)
+{
+	hdd_recovery_notifier.priv_data = hdd_ctx;
+	return qdf_hang_event_register_notifier(&hdd_recovery_notifier);
+}
+
+QDF_STATUS wlan_hdd_hang_event_notifier_unregister(void)
+{
+	return qdf_hang_event_unregister_notifier(&hdd_recovery_notifier);
+}
diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c
index 197e680..267a837 100644
--- a/core/hdd/src/wlan_hdd_main.c
+++ b/core/hdd/src/wlan_hdd_main.c
@@ -179,6 +179,7 @@
 #include "wlan_pkt_capture_ucfg_api.h"
 #include <wlan_hdd_sar_limits.h>
 #include "cfg_nan_api.h"
+#include <wlan_hdd_hang_event.h>
 
 #ifdef MODULE
 #define WLAN_MODULE_NAME  module_name(THIS_MODULE)
@@ -12355,6 +12356,7 @@
 
 	hdd_hastings_bt_war_initialize(hdd_ctx);
 
+	wlan_hdd_hang_event_notifier_register(hdd_ctx);
 	return 0;
 
 cds_disable:
@@ -12379,6 +12381,7 @@
 
 	hdd_enter();
 
+	wlan_hdd_hang_event_notifier_unregister();
 	/* De-init features */
 	hdd_features_deinit(hdd_ctx);