| /* 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); |