753 lines
21 KiB
C
753 lines
21 KiB
C
/* Copyright (c) 2009-2011, 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
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/msm_audio.h>
|
|
#include <mach/qdsp5v2/audio_dev_ctl.h>
|
|
#include <mach/dal.h>
|
|
#include <linux/kthread.h>
|
|
#include <linux/completion.h>
|
|
#include <linux/wait.h>
|
|
#include <mach/qdsp5v2/voice.h>
|
|
#include <mach/debug_mm.h>
|
|
|
|
struct voice_data {
|
|
void *handle; /* DALRPC handle */
|
|
void *cb_handle; /* DALRPC callback handle */
|
|
int network; /* Network information */
|
|
int dev_state;/*READY, CHANGE, REL_DONE,INIT*/
|
|
int voc_state;/*INIT, CHANGE, RELEASE, ACQUIRE */
|
|
struct mutex voc_lock;
|
|
struct mutex vol_lock;
|
|
int voc_event;
|
|
int dev_event;
|
|
atomic_t rel_start_flag;
|
|
atomic_t acq_start_flag;
|
|
atomic_t chg_start_flag;
|
|
struct task_struct *task;
|
|
struct completion complete;
|
|
wait_queue_head_t dev_wait;
|
|
wait_queue_head_t voc_wait;
|
|
uint32_t device_events;
|
|
/* cache the values related to Rx and Tx */
|
|
struct device_data dev_rx;
|
|
struct device_data dev_tx;
|
|
/* these default values are for all devices */
|
|
uint32_t default_mute_val;
|
|
uint32_t default_vol_val;
|
|
uint32_t default_sample_val;
|
|
/* call status */
|
|
int v_call_status; /* Start or End */
|
|
s32 max_rx_vol[VOC_RX_VOL_ARRAY_NUM]; /* [0] is for NB, [1] for WB */
|
|
s32 min_rx_vol[VOC_RX_VOL_ARRAY_NUM];
|
|
};
|
|
|
|
static struct voice_data voice;
|
|
|
|
static int voice_cmd_device_info(struct voice_data *);
|
|
static int voice_cmd_acquire_done(struct voice_data *);
|
|
static void voice_auddev_cb_function(u32 evt_id,
|
|
union auddev_evt_data *evt_payload,
|
|
void *private_data);
|
|
|
|
static int voice_cmd_change(void)
|
|
{
|
|
|
|
struct voice_header hdr;
|
|
struct voice_data *v = &voice;
|
|
int err;
|
|
|
|
hdr.id = CMD_DEVICE_CHANGE;
|
|
hdr.data_len = 0;
|
|
|
|
MM_DBG("\n"); /* Macro prints the file name and function */
|
|
|
|
err = dalrpc_fcn_5(VOICE_DALRPC_CMD, v->handle, &hdr,
|
|
sizeof(struct voice_header));
|
|
|
|
if (err)
|
|
MM_ERR("Voice change command failed\n");
|
|
return err;
|
|
}
|
|
|
|
static void voice_auddev_cb_function(u32 evt_id,
|
|
union auddev_evt_data *evt_payload,
|
|
void *private_data)
|
|
{
|
|
struct voice_data *v = &voice;
|
|
int rc = 0, i;
|
|
|
|
MM_INFO("auddev_cb_function, evt_id=%d, dev_state=%d, voc_state=%d\n",
|
|
evt_id, v->dev_state, v->voc_state);
|
|
if ((evt_id != AUDDEV_EVT_START_VOICE) ||
|
|
(evt_id != AUDDEV_EVT_END_VOICE)) {
|
|
if (evt_payload == NULL) {
|
|
MM_ERR(" evt_payload is NULL pointer\n");
|
|
return;
|
|
}
|
|
}
|
|
switch (evt_id) {
|
|
case AUDDEV_EVT_START_VOICE:
|
|
if ((v->dev_state == DEV_INIT) ||
|
|
(v->dev_state == DEV_REL_DONE)) {
|
|
v->v_call_status = VOICE_CALL_START;
|
|
if ((v->dev_rx.enabled == VOICE_DEV_ENABLED)
|
|
&& (v->dev_tx.enabled == VOICE_DEV_ENABLED)) {
|
|
v->dev_state = DEV_READY;
|
|
MM_DBG("dev_state into ready\n");
|
|
wake_up(&v->dev_wait);
|
|
}
|
|
if (v->voc_state == VOICE_CHANGE) {
|
|
MM_DBG("voc_state is in VOICE_CHANGE\n");
|
|
v->voc_state = VOICE_ACQUIRE;
|
|
}
|
|
}
|
|
break;
|
|
case AUDDEV_EVT_DEV_CHG_VOICE:
|
|
if (v->dev_state == DEV_READY) {
|
|
v->dev_rx.enabled = VOICE_DEV_DISABLED;
|
|
v->dev_tx.enabled = VOICE_DEV_DISABLED;
|
|
v->dev_state = DEV_CHANGE;
|
|
mutex_lock(&voice.voc_lock);
|
|
if (v->voc_state == VOICE_ACQUIRE) {
|
|
/* send device change to modem */
|
|
voice_cmd_change();
|
|
mutex_unlock(&voice.voc_lock);
|
|
msm_snddev_enable_sidetone(v->dev_rx.dev_id,
|
|
0);
|
|
/* block to wait for CHANGE_START */
|
|
rc = wait_event_interruptible(
|
|
v->voc_wait, (v->voc_state == VOICE_CHANGE)
|
|
|| (atomic_read(&v->chg_start_flag) == 1)
|
|
|| (atomic_read(&v->rel_start_flag) == 1));
|
|
} else {
|
|
mutex_unlock(&voice.voc_lock);
|
|
MM_ERR(" Voice is not at ACQUIRE state\n");
|
|
}
|
|
} else if ((v->dev_state == DEV_INIT) ||
|
|
(v->dev_state == DEV_REL_DONE)) {
|
|
v->dev_rx.enabled = VOICE_DEV_DISABLED;
|
|
v->dev_tx.enabled = VOICE_DEV_DISABLED;
|
|
} else
|
|
MM_ERR(" device is not at proper state\n");
|
|
break;
|
|
case AUDDEV_EVT_DEV_RDY:
|
|
/* update the dev info */
|
|
if (evt_payload->voc_devinfo.dev_type == DIR_RX) {
|
|
for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; i++) {
|
|
v->max_rx_vol[i] =
|
|
evt_payload->voc_devinfo.max_rx_vol[i];
|
|
v->min_rx_vol[i] =
|
|
evt_payload->voc_devinfo.min_rx_vol[i];
|
|
}
|
|
}
|
|
if (v->dev_state == DEV_CHANGE) {
|
|
if (evt_payload->voc_devinfo.dev_type == DIR_RX) {
|
|
v->dev_rx.dev_acdb_id =
|
|
evt_payload->voc_devinfo.acdb_dev_id;
|
|
v->dev_rx.sample =
|
|
evt_payload->voc_devinfo.dev_sample;
|
|
v->dev_rx.dev_id =
|
|
evt_payload->voc_devinfo.dev_id;
|
|
v->dev_rx.enabled = VOICE_DEV_ENABLED;
|
|
} else {
|
|
v->dev_tx.dev_acdb_id =
|
|
evt_payload->voc_devinfo.acdb_dev_id;
|
|
v->dev_tx.sample =
|
|
evt_payload->voc_devinfo.dev_sample;
|
|
v->dev_tx.enabled = VOICE_DEV_ENABLED;
|
|
v->dev_tx.dev_id =
|
|
evt_payload->voc_devinfo.dev_id;
|
|
}
|
|
if ((v->dev_rx.enabled == VOICE_DEV_ENABLED) &&
|
|
(v->dev_tx.enabled == VOICE_DEV_ENABLED)) {
|
|
v->dev_state = DEV_READY;
|
|
MM_DBG("dev state into ready\n");
|
|
voice_cmd_device_info(v);
|
|
wake_up(&v->dev_wait);
|
|
mutex_lock(&voice.voc_lock);
|
|
if (v->voc_state == VOICE_CHANGE) {
|
|
v->dev_event = DEV_CHANGE_READY;
|
|
complete(&v->complete);
|
|
}
|
|
mutex_unlock(&voice.voc_lock);
|
|
}
|
|
} else if ((v->dev_state == DEV_INIT) ||
|
|
(v->dev_state == DEV_REL_DONE)) {
|
|
if (evt_payload->voc_devinfo.dev_type == DIR_RX) {
|
|
v->dev_rx.dev_acdb_id =
|
|
evt_payload->voc_devinfo.acdb_dev_id;
|
|
v->dev_rx.sample =
|
|
evt_payload->voc_devinfo.dev_sample;
|
|
v->dev_rx.dev_id =
|
|
evt_payload->voc_devinfo.dev_id;
|
|
v->dev_rx.enabled = VOICE_DEV_ENABLED;
|
|
} else {
|
|
v->dev_tx.dev_acdb_id =
|
|
evt_payload->voc_devinfo.acdb_dev_id;
|
|
v->dev_tx.sample =
|
|
evt_payload->voc_devinfo.dev_sample;
|
|
v->dev_tx.dev_id =
|
|
evt_payload->voc_devinfo.dev_id;
|
|
v->dev_tx.enabled = VOICE_DEV_ENABLED;
|
|
}
|
|
if ((v->dev_rx.enabled == VOICE_DEV_ENABLED) &&
|
|
(v->dev_tx.enabled == VOICE_DEV_ENABLED) &&
|
|
(v->v_call_status == VOICE_CALL_START)) {
|
|
v->dev_state = DEV_READY;
|
|
MM_DBG("dev state into ready\n");
|
|
voice_cmd_device_info(v);
|
|
wake_up(&v->dev_wait);
|
|
mutex_lock(&voice.voc_lock);
|
|
if (v->voc_state == VOICE_CHANGE) {
|
|
v->dev_event = DEV_CHANGE_READY;
|
|
complete(&v->complete);
|
|
}
|
|
mutex_unlock(&voice.voc_lock);
|
|
}
|
|
} else
|
|
MM_ERR("Receive READY not at the proper state =%d\n",
|
|
v->dev_state);
|
|
break;
|
|
case AUDDEV_EVT_DEVICE_VOL_MUTE_CHG:
|
|
if (evt_payload->voc_devinfo.dev_type == DIR_TX)
|
|
v->dev_tx.mute =
|
|
evt_payload->voc_vm_info.dev_vm_val.mute;
|
|
else
|
|
v->dev_rx.volume = evt_payload->
|
|
voc_vm_info.dev_vm_val.vol;
|
|
/* send device info */
|
|
voice_cmd_device_info(v);
|
|
break;
|
|
case AUDDEV_EVT_REL_PENDING:
|
|
/* recover the tx mute and rx volume to the default values */
|
|
if (v->dev_state == DEV_READY) {
|
|
if (atomic_read(&v->rel_start_flag)) {
|
|
atomic_dec(&v->rel_start_flag);
|
|
if (evt_payload->voc_devinfo.dev_type == DIR_RX)
|
|
v->dev_rx.enabled = VOICE_DEV_DISABLED;
|
|
else
|
|
v->dev_tx.enabled = VOICE_DEV_DISABLED;
|
|
v->dev_state = DEV_REL_DONE;
|
|
wake_up(&v->dev_wait);
|
|
break;
|
|
}
|
|
mutex_lock(&voice.voc_lock);
|
|
if ((v->voc_state == VOICE_RELEASE) ||
|
|
(v->voc_state == VOICE_INIT)) {
|
|
if (evt_payload->voc_devinfo.dev_type
|
|
== DIR_RX) {
|
|
v->dev_rx.enabled = VOICE_DEV_DISABLED;
|
|
} else {
|
|
v->dev_tx.enabled = VOICE_DEV_DISABLED;
|
|
}
|
|
v->dev_state = DEV_REL_DONE;
|
|
mutex_unlock(&voice.voc_lock);
|
|
wake_up(&v->dev_wait);
|
|
} else {
|
|
/* send device change to modem */
|
|
voice_cmd_change();
|
|
mutex_unlock(&voice.voc_lock);
|
|
rc = wait_event_interruptible(
|
|
v->voc_wait, (v->voc_state == VOICE_CHANGE)
|
|
|| (atomic_read(&v->chg_start_flag) == 1)
|
|
|| (atomic_read(&v->rel_start_flag) == 1));
|
|
if (atomic_read(&v->rel_start_flag) == 1)
|
|
atomic_dec(&v->rel_start_flag);
|
|
/* clear Rx/Tx to Disable */
|
|
if (evt_payload->voc_devinfo.dev_type == DIR_RX)
|
|
v->dev_rx.enabled = VOICE_DEV_DISABLED;
|
|
else
|
|
v->dev_tx.enabled = VOICE_DEV_DISABLED;
|
|
v->dev_state = DEV_REL_DONE;
|
|
wake_up(&v->dev_wait);
|
|
}
|
|
} else if ((v->dev_state == DEV_INIT) ||
|
|
(v->dev_state == DEV_REL_DONE)) {
|
|
if (evt_payload->voc_devinfo.dev_type == DIR_RX)
|
|
v->dev_rx.enabled = VOICE_DEV_DISABLED;
|
|
else
|
|
v->dev_tx.enabled = VOICE_DEV_DISABLED;
|
|
}
|
|
break;
|
|
case AUDDEV_EVT_END_VOICE:
|
|
/* recover the tx mute and rx volume to the default values */
|
|
v->dev_tx.mute = v->default_mute_val;
|
|
v->dev_rx.volume = v->default_vol_val;
|
|
|
|
if (v->dev_rx.enabled == VOICE_DEV_ENABLED)
|
|
msm_snddev_enable_sidetone(v->dev_rx.dev_id, 0);
|
|
|
|
if ((v->dev_state == DEV_READY) ||
|
|
(v->dev_state == DEV_CHANGE)) {
|
|
if (atomic_read(&v->rel_start_flag)) {
|
|
atomic_dec(&v->rel_start_flag);
|
|
v->v_call_status = VOICE_CALL_END;
|
|
v->dev_state = DEV_REL_DONE;
|
|
wake_up(&v->dev_wait);
|
|
break;
|
|
}
|
|
mutex_lock(&voice.voc_lock);
|
|
if ((v->voc_state == VOICE_RELEASE) ||
|
|
(v->voc_state == VOICE_INIT)) {
|
|
v->v_call_status = VOICE_CALL_END;
|
|
v->dev_state = DEV_REL_DONE;
|
|
mutex_unlock(&voice.voc_lock);
|
|
wake_up(&v->dev_wait);
|
|
} else {
|
|
/* send mute and default volume value to MCAD */
|
|
voice_cmd_device_info(v);
|
|
/* send device change to modem */
|
|
voice_cmd_change();
|
|
mutex_unlock(&voice.voc_lock);
|
|
/* block to wait for RELEASE_START
|
|
or CHANGE_START */
|
|
rc = wait_event_interruptible(
|
|
v->voc_wait, (v->voc_state == VOICE_CHANGE)
|
|
|| (atomic_read(&v->chg_start_flag) == 1)
|
|
|| (atomic_read(&v->rel_start_flag) == 1));
|
|
if (atomic_read(&v->rel_start_flag) == 1)
|
|
atomic_dec(&v->rel_start_flag);
|
|
/* set voice call to END state */
|
|
v->v_call_status = VOICE_CALL_END;
|
|
v->dev_state = DEV_REL_DONE;
|
|
wake_up(&v->dev_wait);
|
|
}
|
|
} else
|
|
v->v_call_status = VOICE_CALL_END;
|
|
break;
|
|
case AUDDEV_EVT_FREQ_CHG:
|
|
MM_DBG("Voice Driver got sample rate change Event\n");
|
|
MM_DBG("sample rate %d\n", evt_payload->freq_info.sample_rate);
|
|
MM_DBG("dev_type %d\n", evt_payload->freq_info.dev_type);
|
|
MM_DBG("acdb_dev_id %d\n", evt_payload->freq_info.acdb_dev_id);
|
|
if (v->dev_state == DEV_READY) {
|
|
v->dev_tx.enabled = VOICE_DEV_DISABLED;
|
|
v->dev_state = DEV_CHANGE;
|
|
mutex_lock(&voice.voc_lock);
|
|
if (v->voc_state == VOICE_ACQUIRE) {
|
|
msm_snddev_enable_sidetone(v->dev_rx.dev_id,
|
|
0);
|
|
/* send device change to modem */
|
|
voice_cmd_change();
|
|
mutex_unlock(&voice.voc_lock);
|
|
/* block to wait for CHANGE_START */
|
|
rc = wait_event_interruptible(
|
|
v->voc_wait, (v->voc_state == VOICE_CHANGE)
|
|
|| (atomic_read(&v->chg_start_flag) == 1)
|
|
|| (atomic_read(&v->rel_start_flag) == 1));
|
|
} else {
|
|
mutex_unlock(&voice.voc_lock);
|
|
MM_ERR(" Voice is not at ACQUIRE state\n");
|
|
}
|
|
} else if ((v->dev_state == DEV_INIT) ||
|
|
(v->dev_state == DEV_REL_DONE)) {
|
|
v->dev_tx.enabled = VOICE_DEV_DISABLED;
|
|
} else
|
|
MM_ERR("Event not at the proper state =%d\n",
|
|
v->dev_state);
|
|
break;
|
|
default:
|
|
MM_ERR("UNKNOWN EVENT\n");
|
|
}
|
|
return;
|
|
}
|
|
EXPORT_SYMBOL(voice_auddev_cb_function);
|
|
|
|
static void remote_cb_function(void *context, u32 param,
|
|
void *evt_buf, u32 len)
|
|
{
|
|
struct voice_header *hdr;
|
|
struct voice_data *v = context;
|
|
|
|
hdr = (struct voice_header *)evt_buf;
|
|
|
|
MM_INFO("len=%d id=%d\n", len, hdr->id);
|
|
|
|
if (len <= 0) {
|
|
MM_ERR("unexpected event with length %d \n", len);
|
|
return;
|
|
}
|
|
|
|
switch (hdr->id) {
|
|
case EVENT_ACQUIRE_START:
|
|
atomic_inc(&v->acq_start_flag);
|
|
wake_up(&v->dev_wait);
|
|
v->voc_event = VOICE_ACQUIRE_START;
|
|
v->network = ((struct voice_network *)evt_buf)->network_info;
|
|
complete(&v->complete);
|
|
break;
|
|
case EVENT_RELEASE_START:
|
|
/* If ACQUIRED come in before the RELEASE,
|
|
* will only services the RELEASE */
|
|
atomic_inc(&v->rel_start_flag);
|
|
wake_up(&v->voc_wait);
|
|
wake_up(&v->dev_wait);
|
|
v->voc_event = VOICE_RELEASE_START;
|
|
complete(&v->complete);
|
|
break;
|
|
case EVENT_CHANGE_START:
|
|
atomic_inc(&v->chg_start_flag);
|
|
wake_up(&v->voc_wait);
|
|
v->voc_event = VOICE_CHANGE_START;
|
|
complete(&v->complete);
|
|
break;
|
|
case EVENT_NETWORK_RECONFIG:
|
|
/* send network change to audio_dev,
|
|
if sample rate is less than 16k,
|
|
otherwise, send acquire done */
|
|
v->voc_event = VOICE_NETWORK_RECONFIG;
|
|
v->network = ((struct voice_network *)evt_buf)->network_info;
|
|
complete(&v->complete);
|
|
break;
|
|
default:
|
|
MM_ERR("Undefined event %d \n", hdr->id);
|
|
}
|
|
|
|
}
|
|
|
|
static int voice_cmd_init(struct voice_data *v)
|
|
{
|
|
|
|
struct voice_init cmd;
|
|
int err;
|
|
|
|
MM_DBG("\n"); /* Macro prints the file name and function */
|
|
|
|
cmd.hdr.id = CMD_VOICE_INIT;
|
|
cmd.hdr.data_len = sizeof(struct voice_init) -
|
|
sizeof(struct voice_header);
|
|
cmd.cb_handle = v->cb_handle;
|
|
|
|
err = dalrpc_fcn_5(VOICE_DALRPC_CMD, v->handle, &cmd,
|
|
sizeof(struct voice_init));
|
|
|
|
if (err)
|
|
MM_ERR("Voice init command failed\n");
|
|
return err;
|
|
}
|
|
|
|
static int voice_cmd_acquire_done(struct voice_data *v)
|
|
{
|
|
struct voice_header hdr;
|
|
int err;
|
|
|
|
hdr.id = CMD_ACQUIRE_DONE;
|
|
hdr.data_len = 0;
|
|
|
|
MM_INFO("\n"); /* Macro prints the file name and function */
|
|
|
|
/* Enable HW sidetone if device supports it */
|
|
msm_snddev_enable_sidetone(v->dev_rx.dev_id, 1);
|
|
|
|
err = dalrpc_fcn_5(VOICE_DALRPC_CMD, v->handle, &hdr,
|
|
sizeof(struct voice_header));
|
|
|
|
if (err)
|
|
MM_ERR("Voice acquire done command failed\n");
|
|
return err;
|
|
}
|
|
|
|
static int voice_cmd_device_info(struct voice_data *v)
|
|
{
|
|
struct voice_device cmd;
|
|
int err, vol;
|
|
|
|
MM_INFO("tx_dev=%d, rx_dev=%d, tx_sample=%d, tx_mute=%d\n",
|
|
v->dev_tx.dev_acdb_id, v->dev_rx.dev_acdb_id,
|
|
v->dev_tx.sample, v->dev_tx.mute);
|
|
|
|
mutex_lock(&voice.vol_lock);
|
|
|
|
cmd.hdr.id = CMD_DEVICE_INFO;
|
|
cmd.hdr.data_len = sizeof(struct voice_device) -
|
|
sizeof(struct voice_header);
|
|
cmd.tx_device = v->dev_tx.dev_acdb_id;
|
|
cmd.rx_device = v->dev_rx.dev_acdb_id;
|
|
if (v->network == NETWORK_WCDMA_WB)
|
|
vol = v->min_rx_vol[VOC_WB_INDEX] +
|
|
((v->max_rx_vol[VOC_WB_INDEX] -
|
|
v->min_rx_vol[VOC_WB_INDEX]) * v->dev_rx.volume)/100;
|
|
else
|
|
vol = v->min_rx_vol[VOC_NB_INDEX] +
|
|
((v->max_rx_vol[VOC_NB_INDEX] -
|
|
v->min_rx_vol[VOC_NB_INDEX]) * v->dev_rx.volume)/100;
|
|
cmd.rx_volume = (u32)vol; /* in mb */
|
|
cmd.rx_mute = 0;
|
|
cmd.tx_mute = v->dev_tx.mute;
|
|
cmd.rx_sample = v->dev_rx.sample/1000;
|
|
cmd.tx_sample = v->dev_tx.sample/1000;
|
|
|
|
MM_DBG("rx_vol=%d, rx_sample=%d\n", cmd.rx_volume, v->dev_rx.sample);
|
|
|
|
err = dalrpc_fcn_5(VOICE_DALRPC_CMD, v->handle, &cmd,
|
|
sizeof(struct voice_device));
|
|
|
|
mutex_unlock(&voice.vol_lock);
|
|
|
|
if (err)
|
|
MM_ERR("Voice device command failed\n");
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL(voice_cmd_device_info);
|
|
|
|
void voice_change_sample_rate(struct voice_data *v)
|
|
{
|
|
int freq = 48000;
|
|
int rc = 0;
|
|
|
|
MM_DBG("network =%d, vote freq=%d\n", v->network, freq);
|
|
if (freq != v->dev_tx.sample) {
|
|
rc = msm_snddev_request_freq(&freq, 0,
|
|
SNDDEV_CAP_TX, AUDDEV_CLNT_VOC);
|
|
if (rc >= 0) {
|
|
v->dev_tx.sample = freq;
|
|
MM_DBG(" vote for freq=%d successfully \n", freq);
|
|
} else
|
|
MM_ERR(" voting for freq=%d failed.\n", freq);
|
|
}
|
|
}
|
|
|
|
static int voice_thread(void *data)
|
|
{
|
|
struct voice_data *v = (struct voice_data *)data;
|
|
int rc = 0;
|
|
|
|
MM_INFO("voice_thread() start\n");
|
|
|
|
while (!kthread_should_stop()) {
|
|
wait_for_completion(&v->complete);
|
|
init_completion(&v->complete);
|
|
|
|
MM_DBG(" voc_event=%d, voice state =%d, dev_event=%d\n",
|
|
v->voc_event, v->voc_state, v->dev_event);
|
|
switch (v->voc_event) {
|
|
case VOICE_ACQUIRE_START:
|
|
/* check if dev_state = READY */
|
|
/* if ready, send device_info and acquire_done */
|
|
/* if not ready, block to wait the dev_state = READY */
|
|
if ((v->voc_state == VOICE_INIT) ||
|
|
(v->voc_state == VOICE_RELEASE)) {
|
|
if (v->dev_state == DEV_READY) {
|
|
mutex_lock(&voice.voc_lock);
|
|
voice_change_sample_rate(v);
|
|
rc = voice_cmd_device_info(v);
|
|
rc = voice_cmd_acquire_done(v);
|
|
v->voc_state = VOICE_ACQUIRE;
|
|
mutex_unlock(&voice.voc_lock);
|
|
broadcast_event(
|
|
AUDDEV_EVT_VOICE_STATE_CHG,
|
|
VOICE_STATE_INCALL, SESSION_IGNORE);
|
|
} else {
|
|
rc = wait_event_interruptible(
|
|
v->dev_wait,
|
|
(v->dev_state == DEV_READY)
|
|
|| (atomic_read(&v->rel_start_flag)
|
|
== 1));
|
|
if (atomic_read(&v->rel_start_flag)
|
|
== 1) {
|
|
v->voc_state = VOICE_RELEASE;
|
|
atomic_dec(&v->rel_start_flag);
|
|
msm_snddev_withdraw_freq(0,
|
|
SNDDEV_CAP_TX, AUDDEV_CLNT_VOC);
|
|
broadcast_event(
|
|
AUDDEV_EVT_VOICE_STATE_CHG,
|
|
VOICE_STATE_OFFCALL,
|
|
SESSION_IGNORE);
|
|
} else {
|
|
mutex_lock(&voice.voc_lock);
|
|
voice_change_sample_rate(v);
|
|
rc = voice_cmd_device_info(v);
|
|
rc = voice_cmd_acquire_done(v);
|
|
v->voc_state = VOICE_ACQUIRE;
|
|
mutex_unlock(&voice.voc_lock);
|
|
broadcast_event(
|
|
AUDDEV_EVT_VOICE_STATE_CHG,
|
|
VOICE_STATE_INCALL,
|
|
SESSION_IGNORE);
|
|
}
|
|
}
|
|
} else
|
|
MM_ERR("Get this event at the wrong state\n");
|
|
if (atomic_read(&v->acq_start_flag))
|
|
atomic_dec(&v->acq_start_flag);
|
|
break;
|
|
case VOICE_RELEASE_START:
|
|
MM_DBG("broadcast voice call end\n");
|
|
broadcast_event(AUDDEV_EVT_VOICE_STATE_CHG,
|
|
VOICE_STATE_OFFCALL, SESSION_IGNORE);
|
|
if ((v->dev_state == DEV_REL_DONE) ||
|
|
(v->dev_state == DEV_INIT)) {
|
|
v->voc_state = VOICE_RELEASE;
|
|
msm_snddev_withdraw_freq(0, SNDDEV_CAP_TX,
|
|
AUDDEV_CLNT_VOC);
|
|
} else {
|
|
/* wait for the dev_state = RELEASE */
|
|
rc = wait_event_interruptible(v->dev_wait,
|
|
(v->dev_state == DEV_REL_DONE)
|
|
|| (atomic_read(&v->acq_start_flag) == 1));
|
|
if (atomic_read(&v->acq_start_flag) == 1)
|
|
atomic_dec(&v->acq_start_flag);
|
|
v->voc_state = VOICE_RELEASE;
|
|
msm_snddev_withdraw_freq(0, SNDDEV_CAP_TX,
|
|
AUDDEV_CLNT_VOC);
|
|
}
|
|
if (atomic_read(&v->rel_start_flag))
|
|
atomic_dec(&v->rel_start_flag);
|
|
break;
|
|
case VOICE_CHANGE_START:
|
|
if (v->voc_state == VOICE_ACQUIRE)
|
|
v->voc_state = VOICE_CHANGE;
|
|
else
|
|
MM_ERR("Get this event at the wrong state\n");
|
|
wake_up(&v->voc_wait);
|
|
if (atomic_read(&v->chg_start_flag))
|
|
atomic_dec(&v->chg_start_flag);
|
|
break;
|
|
case VOICE_NETWORK_RECONFIG:
|
|
if ((v->voc_state == VOICE_ACQUIRE)
|
|
|| (v->voc_state == VOICE_CHANGE)) {
|
|
voice_change_sample_rate(v);
|
|
rc = voice_cmd_device_info(v);
|
|
rc = voice_cmd_acquire_done(v);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (v->dev_event) {
|
|
case DEV_CHANGE_READY:
|
|
if (v->voc_state == VOICE_CHANGE) {
|
|
mutex_lock(&voice.voc_lock);
|
|
msm_snddev_enable_sidetone(v->dev_rx.dev_id,
|
|
1);
|
|
/* update voice state */
|
|
v->voc_state = VOICE_ACQUIRE;
|
|
v->dev_event = 0;
|
|
mutex_unlock(&voice.voc_lock);
|
|
broadcast_event(AUDDEV_EVT_VOICE_STATE_CHG,
|
|
VOICE_STATE_INCALL, SESSION_IGNORE);
|
|
} else {
|
|
mutex_lock(&voice.voc_lock);
|
|
v->dev_event = 0;
|
|
mutex_unlock(&voice.voc_lock);
|
|
MM_ERR("Get this event at the wrong state\n");
|
|
}
|
|
break;
|
|
default:
|
|
mutex_lock(&voice.voc_lock);
|
|
v->dev_event = 0;
|
|
mutex_unlock(&voice.voc_lock);
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int __init voice_init(void)
|
|
{
|
|
int rc, i;
|
|
struct voice_data *v = &voice;
|
|
MM_INFO("\n"); /* Macro prints the file name and function */
|
|
|
|
mutex_init(&voice.voc_lock);
|
|
mutex_init(&voice.vol_lock);
|
|
v->handle = NULL;
|
|
v->cb_handle = NULL;
|
|
|
|
/* set default value */
|
|
v->default_mute_val = 1; /* default is mute */
|
|
v->default_vol_val = 0;
|
|
v->default_sample_val = 8000;
|
|
for (i = 0; i < VOC_RX_VOL_ARRAY_NUM; i++) {
|
|
v->max_rx_vol[i] = 0;
|
|
v->min_rx_vol[i] = 0;
|
|
}
|
|
v->network = NETWORK_GSM;
|
|
|
|
/* initialize dev_rx and dev_tx */
|
|
memset(&v->dev_tx, 0, sizeof(struct device_data));
|
|
memset(&v->dev_rx, 0, sizeof(struct device_data));
|
|
v->dev_rx.volume = v->default_vol_val;
|
|
v->dev_tx.mute = v->default_mute_val;
|
|
|
|
v->dev_state = DEV_INIT;
|
|
v->voc_state = VOICE_INIT;
|
|
atomic_set(&v->rel_start_flag, 0);
|
|
atomic_set(&v->acq_start_flag, 0);
|
|
v->dev_event = 0;
|
|
v->voc_event = 0;
|
|
init_completion(&voice.complete);
|
|
init_waitqueue_head(&v->dev_wait);
|
|
init_waitqueue_head(&v->voc_wait);
|
|
|
|
/* get device handle */
|
|
rc = daldevice_attach(VOICE_DALRPC_DEVICEID,
|
|
VOICE_DALRPC_PORT_NAME,
|
|
VOICE_DALRPC_CPU,
|
|
&v->handle);
|
|
if (rc) {
|
|
MM_ERR("Voc DALRPC call to Modem attach failed\n");
|
|
goto done;
|
|
}
|
|
|
|
/* Allocate the callback handle */
|
|
v->cb_handle = dalrpc_alloc_cb(v->handle, remote_cb_function, v);
|
|
if (v->cb_handle == NULL) {
|
|
MM_ERR("Allocate Callback failure\n");
|
|
goto err;
|
|
}
|
|
|
|
/* setup the callback */
|
|
rc = voice_cmd_init(v);
|
|
if (rc)
|
|
goto err1;
|
|
|
|
v->device_events = AUDDEV_EVT_DEV_CHG_VOICE |
|
|
AUDDEV_EVT_DEV_RDY |
|
|
AUDDEV_EVT_REL_PENDING |
|
|
AUDDEV_EVT_START_VOICE |
|
|
AUDDEV_EVT_END_VOICE |
|
|
AUDDEV_EVT_DEVICE_VOL_MUTE_CHG |
|
|
AUDDEV_EVT_FREQ_CHG;
|
|
|
|
MM_DBG(" to register call back \n");
|
|
/* register callback to auddev */
|
|
auddev_register_evt_listner(v->device_events, AUDDEV_CLNT_VOC,
|
|
0, voice_auddev_cb_function, v);
|
|
|
|
/* create and start thread */
|
|
v->task = kthread_run(voice_thread, v, "voice");
|
|
if (IS_ERR(v->task)) {
|
|
rc = PTR_ERR(v->task);
|
|
v->task = NULL;
|
|
} else
|
|
goto done;
|
|
|
|
err1: dalrpc_dealloc_cb(v->handle, v->cb_handle);
|
|
err:
|
|
daldevice_detach(v->handle);
|
|
v->handle = NULL;
|
|
done:
|
|
return rc;
|
|
}
|
|
|
|
late_initcall(voice_init);
|