Merge "asoc: msm: Fix zero size pointer issue"
diff --git a/asoc/msm-pcm-voice-v2.c b/asoc/msm-pcm-voice-v2.c
index b82c587..7447d0c 100644
--- a/asoc/msm-pcm-voice-v2.c
+++ b/asoc/msm-pcm-voice-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -519,6 +519,22 @@
 	return ret;
 }
 
+static int msm_voice_mbd_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = voc_get_mbd_enable();
+	return 0;
+}
+
+static int msm_voice_mbd_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	bool enable = ucontrol->value.integer.value[0];
+
+	voc_set_mbd_enable(enable);
+
+	return 0;
+}
 
 
 static const char * const tty_mode[] = {"OFF", "HCO", "VCO", "FULL"};
@@ -661,6 +677,8 @@
 	},
 	SOC_SINGLE_MULTI_EXT("Voice Sidetone Enable", SND_SOC_NOPM, 0, 1, 0, 1,
 			     msm_voice_sidetone_get, msm_voice_sidetone_put),
+	SOC_SINGLE_BOOL_EXT("Voice Mic Break Enable", 0, msm_voice_mbd_get,
+				msm_voice_mbd_put),
 };
 
 static const struct snd_pcm_ops msm_pcm_ops = {
diff --git a/dsp/q6voice.c b/dsp/q6voice.c
index fee9665..4d86def 100644
--- a/dsp/q6voice.c
+++ b/dsp/q6voice.c
@@ -71,6 +71,9 @@
 static int voice_send_mvm_cal_network_cmd(struct voice_data *v);
 static int voice_send_mvm_media_type_cmd(struct voice_data *v);
 static int voice_send_mvm_cvd_version_cmd(struct voice_data *v);
+static int voice_send_mvm_event_class_cmd(struct voice_data *v,
+					   uint32_t event_id,
+					   uint32_t class_id);
 static int voice_send_cvs_data_exchange_mode_cmd(struct voice_data *v);
 static int voice_send_cvs_packet_exchange_config_cmd(struct voice_data *v);
 static int voice_set_packet_exchange_mode_and_config(uint32_t session_id,
@@ -757,6 +760,70 @@
 	return ret;
 }
 
+static int voice_send_mvm_event_class_cmd(struct voice_data *v,
+					   uint32_t event_id,
+					   uint32_t class_id)
+{
+	struct vss_inotify_cmd_event_class_t mvm_event;
+	int ret = 0;
+	void *apr_mvm = NULL;
+	u16 mvm_handle = 0;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	apr_mvm = common.apr_q6_mvm;
+	if (!apr_mvm) {
+		pr_err("%s: apr_mvm is NULL.\n", __func__);
+		return -EINVAL;
+	}
+
+	memset(&mvm_event, 0, sizeof(mvm_event));
+	mvm_handle = voice_get_mvm_handle(v);
+	mvm_event.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	mvm_event.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(mvm_event) - APR_HDR_SIZE);
+	mvm_event.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	mvm_event.hdr.dest_port = mvm_handle;
+	mvm_event.hdr.token = 0;
+	mvm_event.hdr.opcode = event_id;
+	mvm_event.class_id = class_id;
+
+	v->mvm_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_event);
+	if (ret < 0) {
+		pr_err("%s: Error %d sending %x event\n", __func__, ret,
+			event_id);
+		goto fail;
+	}
+
+	ret = wait_event_timeout(v->mvm_wait,
+				(v->mvm_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout %d\n", __func__, ret);
+		ret = -ETIMEDOUT;
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+	return 0;
+fail:
+	return ret;
+}
+
 static int voice_send_dual_control_cmd(struct voice_data *v)
 {
 	int ret = 0;
@@ -4259,6 +4326,23 @@
 	return ret;
 }
 
+static void voice_mic_break_work_fn(struct work_struct *work)
+{
+	int ret = 0;
+	char event[25] = "";
+	struct voice_data *v = container_of(work, struct voice_data,
+						voice_mic_break_work);
+
+	snprintf(event, sizeof(event), "MIC_BREAK_STATUS=%s",
+			v->mic_break_status ? "TRUE" : "FALSE");
+
+	mutex_lock(&common.common_lock);
+	ret = q6core_send_uevent(common.uevent_data, event);
+	if (ret)
+		pr_err("%s: Send UEvent %s failed :%d\n", __func__, event, ret);
+	mutex_unlock(&common.common_lock);
+}
+
 static int voice_setup_vocproc(struct voice_data *v)
 {
 	int ret = 0;
@@ -4354,6 +4438,11 @@
 	if (v->hd_enable)
 		voice_send_hd_cmd(v, v->hd_enable);
 
+	if (common.mic_break_enable)
+		voice_send_mvm_event_class_cmd(v,
+			VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS,
+			VSS_ICOMMON_EVENT_CLASS_VOICE_ACTIVITY_UPDATE);
+
 	rtac_add_voice(voice_get_cvs_handle(v),
 		voice_get_cvp_handle(v),
 		v->dev_rx.port_id, v->dev_tx.port_id,
@@ -5095,6 +5184,11 @@
 	voice_send_cvp_deregister_dev_cfg_cmd(v);
 	voice_send_cvs_deregister_cal_cmd(v);
 
+	if (common.mic_break_enable)
+		voice_send_mvm_event_class_cmd(v,
+			VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS,
+			VSS_ICOMMON_EVENT_CLASS_VOICE_ACTIVITY_UPDATE);
+
 	/* destrop cvp session */
 	cvp_destroy_session_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
 						APR_HDR_LEN(APR_HDR_SIZE),
@@ -6682,6 +6776,70 @@
 EXPORT_SYMBOL(voc_get_route_flag);
 
 /**
+ * voc_get_mbd_enable -
+ *       Retrieve mic break detection enable state
+ *
+ * Returns true if mic break detection is enabled or false if disabled
+ */
+bool voc_get_mbd_enable(void)
+{
+	bool enable = false;
+
+	mutex_lock(&common.common_lock);
+	enable = common.mic_break_enable;
+	mutex_unlock(&common.common_lock);
+
+	return enable;
+}
+EXPORT_SYMBOL(voc_get_mbd_enable);
+
+/**
+ * voc_set_mbd_enable -
+ *       Set mic break detection enable state
+ *
+ * @enable: mic break detection state to set
+ *
+ * Returns 0
+ */
+uint8_t voc_set_mbd_enable(bool enable)
+{
+	struct voice_data *v = NULL;
+	struct voice_session_itr itr;
+	bool check_and_send_event = false;
+	uint32_t event_id = VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS;
+	uint32_t class_id = VSS_ICOMMON_EVENT_CLASS_VOICE_ACTIVITY_UPDATE;
+
+	mutex_lock(&common.common_lock);
+	if (common.mic_break_enable != enable)
+		check_and_send_event = true;
+	common.mic_break_enable = enable;
+	mutex_unlock(&common.common_lock);
+
+	if (!check_and_send_event)
+		return 0;
+
+	if (!enable)
+		event_id = VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS;
+
+	memset(&itr, 0, sizeof(itr));
+
+	voice_itr_init(&itr, ALL_SESSION_VSID);
+	while (voice_itr_get_next_session(&itr, &v)) {
+		if (v != NULL) {
+			mutex_lock(&v->lock);
+			if (is_voc_state_active(v->voc_state)) {
+				voice_send_mvm_event_class_cmd(v, event_id,
+								class_id);
+			}
+			mutex_unlock(&v->lock);
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(voc_set_mbd_enable);
+
+/**
  * voc_end_voice_call -
  *       command to end voice call
  *
@@ -7221,6 +7379,7 @@
 	uint32_t *ptr = NULL;
 	struct common_data *c = NULL;
 	struct voice_data *v = NULL;
+	struct vss_evt_voice_activity *voice_act_update = NULL;
 	int i = 0;
 	struct vss_iversion_rsp_get_t *version_rsp = NULL;
 
@@ -7329,6 +7488,8 @@
 			case VSS_IMVM_CMD_STANDBY_VOICE:
 			case VSS_IHDVOICE_CMD_ENABLE:
 			case VSS_IHDVOICE_CMD_DISABLE:
+			case VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS:
+			case VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS:
 				pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
 				v->mvm_state = CMD_STATUS_SUCCESS;
 				v->async_err = ptr[1];
@@ -7433,7 +7594,34 @@
 			v->mvm_state = CMD_STATUS_SUCCESS;
 			wake_up(&v->mvm_wait);
 		}
+	} else if (data->opcode == VSS_ICOMMON_EVT_VOICE_ACTIVITY_UPDATE) {
+		if (data->payload_size ==
+				sizeof(struct vss_evt_voice_activity)) {
+			voice_act_update =
+				(struct vss_evt_voice_activity *)
+				data->payload;
+
+			/* Drop notifications other than Mic Break */
+			if ((voice_act_update->activity
+				     != VSS_ICOMMON_VOICE_ACTIVITY_MIC_BREAK)
+				&& (voice_act_update->activity
+				     != VSS_ICOMMON_VOICE_ACITIVTY_MIC_UNBREAK))
+				return 0;
+
+			switch (voice_act_update->activity) {
+			case VSS_ICOMMON_VOICE_ACTIVITY_MIC_BREAK:
+				v->mic_break_status = true;
+				break;
+			case VSS_ICOMMON_VOICE_ACITIVTY_MIC_UNBREAK:
+				v->mic_break_status = false;
+				break;
+			}
+
+			if (c->mic_break_enable)
+				schedule_work(&(v->voice_mic_break_work));
+		}
 	}
+
 	return 0;
 }
 
@@ -9521,6 +9709,14 @@
 }
 EXPORT_SYMBOL(is_voc_initialized);
 
+static void voc_release_uevent_data(struct kobject *kobj)
+{
+	struct audio_uevent_data *data = container_of(kobj,
+						      struct audio_uevent_data,
+						      kobj);
+	kfree(data);
+}
+
 int __init voice_init(void)
 {
 	int rc = 0, i = 0;
@@ -9561,6 +9757,18 @@
 
 	mutex_init(&common.common_lock);
 
+	common.uevent_data = kzalloc(sizeof(*(common.uevent_data)), GFP_KERNEL);
+	if (!common.uevent_data)
+		return -ENOMEM;
+
+	/*
+	 * Set release function to cleanup memory related to kobject
+	 * before initializing the kobject.
+	 */
+	common.uevent_data->ktype.release = voc_release_uevent_data;
+	q6core_init_uevent_data(common.uevent_data, "q6voice_uevent");
+	common.mic_break_enable = false;
+
 	/* Initialize session id with vsid */
 	init_session_id();
 
@@ -9601,6 +9809,9 @@
 
 		common.voice[i].voc_state = VOC_INIT;
 
+		INIT_WORK(&common.voice[i].voice_mic_break_work,
+				voice_mic_break_work_fn);
+
 		init_waitqueue_head(&common.voice[i].mvm_wait);
 		init_waitqueue_head(&common.voice[i].cvs_wait);
 		init_waitqueue_head(&common.voice[i].cvp_wait);
@@ -9621,6 +9832,7 @@
 
 void voice_exit(void)
 {
+	q6core_destroy_uevent_data(common.uevent_data);
 	voice_delete_cal_data();
 	free_cal_map_table();
 }
diff --git a/include/dsp/q6voice.h b/include/dsp/q6voice.h
index a41a2db..86be4e1 100644
--- a/include/dsp/q6voice.h
+++ b/include/dsp/q6voice.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -15,6 +15,7 @@
 #include <linux/msm_ion.h>
 #include <sound/voice_params.h>
 #include <dsp/rtac.h>
+#include <dsp/q6core.h>
 #include <ipc/apr.h>
 
 #define MAX_VOC_PKT_SIZE 642
@@ -504,6 +505,19 @@
 #define VSS_IHDVOICE_CMD_ENABLE				0x000130A2
 #define VSS_IHDVOICE_CMD_DISABLE			0x000130A3
 
+/* To listen for events from MVM module */
+#define VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS		0x00012E56
+/* To cancel listening for events from MVM module */
+#define VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS		0x00012E57
+/* To receive ongoing voice activity notification */
+#define VSS_ICOMMON_EVENT_CLASS_VOICE_ACTIVITY_UPDATE	0x000131EF
+/* Voice activity notification from MVM */
+#define VSS_ICOMMON_EVT_VOICE_ACTIVITY_UPDATE		0x000131F0
+/* Mic path is broken. */
+#define VSS_ICOMMON_VOICE_ACTIVITY_MIC_BREAK		0x000131F3
+/* Mic path is restored. */
+#define VSS_ICOMMON_VOICE_ACITIVTY_MIC_UNBREAK		0x000131F4
+
 enum msm_audio_voc_rate {
 		VOC_0_RATE, /* Blank frame */
 		VOC_8_RATE, /* 1/8 rate    */
@@ -729,6 +743,33 @@
 	uint32_t mem_handle;
 } __packed;
 
+/*
+ * Payload structure for VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS and
+ * VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS commands.
+ */
+struct vss_inotify_cmd_event_class_t {
+	struct apr_hdr hdr;
+	/* Event class ID to listen for. */
+	uint32_t class_id;
+} __packed;
+
+/* Payload structure for the VSS_ICOMMOM_EVT_VOICE_ACTIVITY_UPDATE event. */
+struct vss_evt_voice_activity {
+	uint32_t activity;
+	/*
+	 * Specifies the voice acitivity.
+	 * @values
+	 *     #VSS_ICOMMON_VOICE_ACTIVITY_VPTX_MUTE
+	 *     #VSS_ICOMMON_VOICE_ACTIVITY_VPTX_UNMUTE
+	 *     #VSS_ICOMMON_VOICE_ACTIVITY_MIC_BREAK
+	 *     #VSS_ICOMMON_VOICE_ACITIVTY_MIC_UNBREAK
+	 *     #VSS_ICOMMON_VOICE_ACTIVITY_UI_STREAM_TX_MUTE
+	 *     #VSS_ICOMMON_VOICE_ACTIVITY_UI_STREAM_TX_UNMUTE
+	 *     #VSS_ICOMMON_VOICE_ACTIVITY_UI_VOCPROC_TX_MUTE
+	 *     #VSS_ICOMMON_VOICE_ACTIVITY_UI_VOCPROC_TX_UNMUTE
+	 */
+} __packed;
+
 /* TO CVS commands */
 #define VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION	0x00011140
 /**< Wait for APRV2_IBASIC_RSP_RESULT response. */
@@ -1872,6 +1913,9 @@
 	struct incall_music_info music_info;
 
 	struct voice_rec_route_state rec_route_state;
+
+	bool mic_break_status;
+	struct work_struct voice_mic_break_work;
 };
 
 struct cal_mem {
@@ -1934,6 +1978,8 @@
 	struct shared_mem_info source_tracking_sh_mem;
 	struct vss_isourcetrack_activity_data_t sourceTrackingResponse;
 	bool sidetone_enable;
+	bool mic_break_enable;
+	struct audio_uevent_data *uevent_data;
 };
 
 struct voice_session_itr {
@@ -2023,6 +2069,8 @@
 int voc_get_rx_device_mute(uint32_t session_id);
 int voc_set_route_flag(uint32_t session_id, uint8_t path_dir, uint8_t set);
 uint8_t voc_get_route_flag(uint32_t session_id, uint8_t path_dir);
+bool voc_get_mbd_enable(void);
+uint8_t voc_set_mbd_enable(bool enable);
 int voc_enable_dtmf_rx_detection(uint32_t session_id, uint32_t enable);
 void voc_disable_dtmf_det_on_active_sessions(void);
 int voc_alloc_cal_shared_memory(void);