wlan: Add sysfs entry for BT profile indication

Add the sysfs entry for the BT profile indication with two
parameters profile and profile mode.
    --> echo <BT_PROFILE> <PROFILE_MODE> > /sys/kernel/wlan/bt_profile
    where BT_PROFILE: BT_PROFILE_MAX, BT_PRPFILE_SCO etc
          MODE      : ENABLE/DISABLE
    f.e: echo  BT_PROFILE_MAX ENABLE > /sys/kernel/wlan/bt_profile

Change-Id: Ieb7f24035a552438fe90896e26e3c92e70325d6c
CRs-Fixed: 2762531
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index 6a92340..49b3ead 100644
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -214,6 +214,9 @@
 //wait time for beacon miss rate.
 #define BCN_MISS_RATE_TIME 500
 
+//max size for BT profile indication cmd
+#define MAX_USER_COMMAND_SIZE_BT_PROFILE_IND_CMD 24
+
 /*
  * Android DRIVER command structures
  */
@@ -247,6 +250,19 @@
 static int hdd_ParseUserParams(tANI_U8 *pValue, tANI_U8 **ppArg);
 
 #endif /* WLAN_FEATURE_RMC */
+
+#ifdef FEATURE_WLAN_SW_PTA
+/* BT profile sysfile entry obj */
+static struct kobject *driver_kobject;
+static ssize_t hdd_sysfs_bt_profile_ind_cmd_store(struct kobject *kobj,
+                                                  struct kobj_attribute *attr,
+                                                  const char *buf,
+                                                  size_t count);
+static struct kobj_attribute bt_profile_attribute =
+    __ATTR(bt_profile, 0220, NULL,
+           hdd_sysfs_bt_profile_ind_cmd_store);
+#endif
+
 void wlan_hdd_restart_timer_cb(v_PVOID_t usrDataForCallback);
 void hdd_set_wlan_suspend_mode(bool suspend);
 void hdd_set_vowifi_mode(hdd_context_t *hdd_ctx, bool enable);
@@ -3892,6 +3908,135 @@
 	return len;
 }
 
+#ifdef FEATURE_WLAN_SW_PTA
+static void hdd_sysfs_bt_profile_create(hdd_context_t* hdd_ctx)
+{
+	if(!hdd_ctx->cfg_ini->is_sw_pta_enabled)
+		return;
+
+	driver_kobject = kobject_create_and_add(WLAN_MODULE_NAME, kernel_kobj);
+	if (!driver_kobject) {
+		hddLog(VOS_TRACE_LEVEL_ERROR,
+		       "%s:could not allocate driver kobject",
+		       __func__);
+		return;
+	}
+
+	if(sysfs_create_file(driver_kobject, &bt_profile_attribute.attr))
+		hddLog(VOS_TRACE_LEVEL_ERROR,
+		       "%s:Failed to create BT profile sysfs entry", __func__);
+}
+
+static void hdd_sysfs_bt_profile_destroy(hdd_context_t* hdd_ctx)
+{
+	if(!hdd_ctx->cfg_ini->is_sw_pta_enabled)
+		return;
+
+	sysfs_remove_file(driver_kobject, &bt_profile_attribute.attr);
+	if (driver_kobject) {
+		kobject_put(driver_kobject);
+		driver_kobject = NULL;
+	}
+}
+
+static int hdd_sysfs_validate_and_copy_buf(char *dest_buf, size_t dest_buf_size,
+					   char const *source_buf,
+					   size_t source_buf_size)
+{
+	if (source_buf_size > (dest_buf_size - 1)) {
+		hddLog(VOS_TRACE_LEVEL_ERROR,
+		       "%s:Command length is larger than %zu bytes",
+			   __func__, dest_buf_size);
+		return -EINVAL;
+	}
+
+	/* sysfs already provides kernel space buffer so copy from user
+	 * is not needed. Doing this extra copy operation just to ensure
+	 * the local buf is properly null-terminated.
+	 */
+	strlcpy(dest_buf, source_buf, dest_buf_size);
+
+	/* default 'echo' cmd takes new line character to here */
+	if (dest_buf[source_buf_size - 1] == '\n')
+		dest_buf[source_buf_size - 1] = '\0';
+
+	return 0;
+}
+
+static ssize_t __hdd_sysfs_bt_profile_ind_cmd_store(hdd_context_t *hdd_ctx,
+						    const char *buf,
+						    size_t count)
+{
+	char buf_local[MAX_USER_COMMAND_SIZE_BT_PROFILE_IND_CMD + 1];
+	char *sptr, *token, *profile, *profile_mode;
+	int ret;
+
+	ENTER();
+
+	if (wlan_hdd_validate_context(hdd_ctx))
+		return -EINVAL;
+
+	ret = hdd_sysfs_validate_and_copy_buf(buf_local, sizeof(buf_local),
+					      buf, count);
+	if (ret)
+		return -EINVAL;
+
+	sptr = buf_local;
+	/* Get BT profile */
+	token = strsep(&sptr, " ");
+
+	if (!token)
+		return -EINVAL;
+	profile = token;
+
+	token = NULL;
+	/* Get BT profile mode */
+	token = strsep(&sptr, " ");
+
+	if (!token)
+		return -EINVAL;
+
+	profile_mode = token;
+
+	hddLog(VOS_TRACE_LEVEL_INFO, "%s:profile = %s, profile_mode = %s",
+	       __func__, profile, profile_mode);
+
+	EXIT();
+	return count;
+}
+
+static ssize_t hdd_sysfs_bt_profile_ind_cmd_store(struct kobject *kobj,
+						  struct kobj_attribute *attr,
+						  const char *buf,
+						  size_t count)
+{
+	hdd_context_t *pHddCtx = NULL;
+	ssize_t err_size = 0;
+
+	pHddCtx = (hdd_context_t *)vos_get_context(VOS_MODULE_ID_HDD,
+			vos_get_global_context(VOS_MODULE_ID_HDD, NULL));
+
+	if (!pHddCtx) {
+		hddLog(VOS_TRACE_LEVEL_FATAL, "HDD Context is NULL");
+		return -EINVAL;
+	}
+
+	err_size = __hdd_sysfs_bt_profile_ind_cmd_store(pHddCtx, buf, count);
+
+	return err_size;
+}
+#else
+static inline
+void hdd_sysfs_bt_profile_create(hdd_context_t* pHddCtx)
+{
+}
+
+static inline
+void hdd_sysfs_bt_profile_destroy(hdd_context_t* pHddCtx)
+{
+}
+#endif
+
 static int hdd_driver_command(hdd_adapter_t *pAdapter,
                               hdd_priv_data_t *ppriv_data)
 {
@@ -8126,6 +8271,8 @@
           return -EINVAL;
    }
 
+   hdd_sysfs_bt_profile_create(pHddCtx);
+
    set_bit(DEVICE_IFACE_OPENED, &pAdapter->event_flags);
    if (hdd_connIsConnected(WLAN_HDD_GET_STATION_CTX_PTR(pAdapter))) 
    {
@@ -8303,6 +8450,8 @@
        wlan_hdd_stop_mon(pHddCtx, true);
    }
 
+   hdd_sysfs_bt_profile_destroy(pHddCtx);
+
    hddLog(VOS_TRACE_LEVEL_INFO, "%s: Disabling OS Tx queues", __func__);
 
    /* Disable TX on the interface, after this hard_start_xmit() will not