qcacld-3.0: Add sysfs interface for retrieving version information

User-space needs sysfs interface for retrieving version
information for both firmware and driver. Add this new
sysfs interface.

Change-Id: I666aff1868f4d1d954773fae1ae85c1ebd0fdc87
CRs-Fixed: 2153885
diff --git a/Kbuild b/Kbuild
index 7937f99..1fd12bf 100644
--- a/Kbuild
+++ b/Kbuild
@@ -440,6 +440,7 @@
 		$(HDD_SRC_DIR)/wlan_hdd_request_manager.o \
 		$(HDD_SRC_DIR)/wlan_hdd_scan.o \
 		$(HDD_SRC_DIR)/wlan_hdd_softap_tx_rx.o \
+		$(HDD_SRC_DIR)/wlan_hdd_sysfs.o \
 		$(HDD_SRC_DIR)/wlan_hdd_tx_rx.o \
 		$(HDD_SRC_DIR)/wlan_hdd_trace.o \
 		$(HDD_SRC_DIR)/wlan_hdd_wext.o \
diff --git a/core/hdd/inc/wlan_hdd_sysfs.h b/core/hdd/inc/wlan_hdd_sysfs.h
new file mode 100644
index 0000000..8f380a3
--- /dev/null
+++ b/core/hdd/inc/wlan_hdd_sysfs.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
+ *
+ *
+ * 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.
+ */
+
+/**
+ * hdd_sysfs_create_version_interface() - create version interface
+ * @psoc: PSOC ptr
+ *
+ * Return: none
+ */
+void hdd_sysfs_create_version_interface(struct wlan_objmgr_psoc *psoc);
+
+/**
+ * hdd_sysfs_destroy_version_interface() - destroy version interface
+ *
+ * Return: none
+ */
+void hdd_sysfs_destroy_version_interface(void);
diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c
index 77d0b30..e1393c6 100644
--- a/core/hdd/src/wlan_hdd_main.c
+++ b/core/hdd/src/wlan_hdd_main.c
@@ -127,6 +127,7 @@
 #include "sme_power_save_api.h"
 #include "enet.h"
 #include <cdp_txrx_cmn_struct.h>
+#include "wlan_hdd_sysfs.h"
 
 #ifdef CNSS_GENL
 #include <net/cnss_nl.h>
@@ -2491,6 +2492,8 @@
 		hdd_register_policy_manager_callback(
 			hdd_ctx->hdd_psoc);
 
+		hdd_sysfs_create_version_interface(hdd_ctx->hdd_psoc);
+
 		hdd_update_hw_sw_info(hdd_ctx);
 		hdd_ctx->driver_status = DRIVER_MODULES_OPENED;
 		hdd_info("Wlan transitioned (now OPENED)");
@@ -11608,6 +11611,9 @@
 	component_deinit();
 
 	dispatcher_deinit();
+
+	hdd_sysfs_destroy_version_interface();
+
 	hdd_deinit();
 	pld_deinit();
 
diff --git a/core/hdd/src/wlan_hdd_sysfs.c b/core/hdd/src/wlan_hdd_sysfs.c
new file mode 100644
index 0000000..3be1303
--- /dev/null
+++ b/core/hdd/src/wlan_hdd_sysfs.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2017 The Linux Foundation. All rights reserved.
+ *
+ * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
+ *
+ *
+ * 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.
+ */
+
+/**
+ *  DOC: wlan_hdd_sysfs.c
+ *
+ *  WLAN Host Device Driver implementation
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kobject.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include "wlan_hdd_includes.h"
+#include "wlan_hdd_sysfs.h"
+#include "qwlan_version.h"
+#include "cds_api.h"
+
+#define MAX_PSOC_ID_SIZE 10
+
+#ifdef MULTI_IF_NAME
+#define DRIVER_NAME MULTI_IF_NAME
+#else
+#define DRIVER_NAME "wlan"
+#endif
+
+static struct kobject *wlan_kobject;
+static struct kobject *driver_kobject;
+static struct kobject *fw_kobject;
+static struct kobject *psoc_kobject;
+
+static ssize_t __show_driver_version(struct kobject *kobj,
+				     struct kobj_attribute *attr,
+				     char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, QWLAN_VERSIONSTR);
+}
+
+static ssize_t show_driver_version(struct kobject *kobj,
+				   struct kobj_attribute *attr,
+				   char *buf)
+{
+	ssize_t ret_val;
+
+	cds_ssr_protect(__func__);
+	ret_val = __show_driver_version(kobj, attr, buf);
+	cds_ssr_unprotect(__func__);
+
+	return ret_val;
+}
+
+static ssize_t __show_fw_version(struct kobject *kobj,
+				 struct kobj_attribute *attr,
+				 char *buf)
+{
+	const char *hw_version;
+	uint32_t major_spid = 0, minor_spid = 0, siid = 0, crmid = 0;
+	uint32_t sub_id = 0;
+	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
+	int ret;
+
+	ret = wlan_hdd_validate_context(hdd_ctx);
+	if (ret) {
+		hdd_err("hdd ctx is invalid");
+		return ret;
+	}
+
+	hdd_debug("Rcvd req for FW version");
+	hdd_get_fw_version(hdd_ctx, &major_spid, &minor_spid, &siid,
+			   &crmid);
+	sub_id = (hdd_ctx->target_fw_vers_ext & 0xf0000000) >> 28;
+	hw_version = hdd_ctx->target_hw_name;
+
+	return scnprintf(buf, PAGE_SIZE,
+			 "FW:%d.%d.%d.%d.%d HW:%s", major_spid,
+			 minor_spid, siid, crmid, sub_id,
+			 hw_version);
+}
+
+static ssize_t show_fw_version(struct kobject *kobj,
+			       struct kobj_attribute *attr,
+			       char *buf)
+{
+	ssize_t ret_val;
+
+	cds_ssr_protect(__func__);
+	ret_val = __show_fw_version(kobj, attr, buf);
+	cds_ssr_unprotect(__func__);
+
+	return ret_val;
+}
+
+static struct kobj_attribute dr_ver_attribute =
+	__ATTR(driver_version, 0440, show_driver_version, NULL);
+static struct kobj_attribute fw_ver_attribute =
+	__ATTR(version, 0440, show_fw_version, NULL);
+
+void hdd_sysfs_create_version_interface(struct wlan_objmgr_psoc *psoc)
+{
+	int error = 0;
+	uint32_t psoc_id;
+	char buf[MAX_PSOC_ID_SIZE];
+
+	wlan_kobject = kobject_create_and_add("wlan", kernel_kobj);
+	if (!wlan_kobject) {
+		hdd_err("could not allocate wlan kobject");
+		return;
+	}
+
+	driver_kobject = kobject_create_and_add(DRIVER_NAME, wlan_kobject);
+	if (!driver_kobject) {
+		hdd_err("could not allocate driver kobject");
+		goto free_wlan_kobj;
+	}
+
+	error = sysfs_create_file(driver_kobject, &dr_ver_attribute.attr);
+	if (error) {
+		hdd_err("could not create driver sysfs file");
+		goto free_drv_kobj;
+	}
+
+	fw_kobject = kobject_create_and_add("fw", driver_kobject);
+	if (!fw_kobject) {
+		hdd_err("could not allocate fw kobject");
+		goto free_fw_kobj;
+	}
+
+	psoc_id = wlan_psoc_get_nif_phy_version(psoc);
+	scnprintf(buf, PAGE_SIZE, "%d", psoc_id);
+
+	psoc_kobject = kobject_create_and_add(buf, fw_kobject);
+	if (!psoc_kobject) {
+		hdd_err("could not allocate psoc kobject");
+		goto free_fw_kobj;
+	}
+
+	error = sysfs_create_file(psoc_kobject, &fw_ver_attribute.attr);
+	if (error) {
+		hdd_err("could not create fw sysfs file");
+		goto free_psoc_kobj;
+	}
+
+	return;
+
+free_psoc_kobj:
+	kobject_put(psoc_kobject);
+	psoc_kobject = NULL;
+
+free_fw_kobj:
+	kobject_put(fw_kobject);
+	fw_kobject = NULL;
+
+free_drv_kobj:
+	kobject_put(driver_kobject);
+	driver_kobject = NULL;
+
+free_wlan_kobj:
+	kobject_put(wlan_kobject);
+	wlan_kobject = NULL;
+}
+
+void hdd_sysfs_destroy_version_interface(void)
+{
+	if (psoc_kobject) {
+		kobject_put(psoc_kobject);
+		psoc_kobject = NULL;
+		kobject_put(fw_kobject);
+		fw_kobject = NULL;
+		kobject_put(driver_kobject);
+		driver_kobject = NULL;
+		kobject_put(wlan_kobject);
+		wlan_kobject = NULL;
+	}
+}