blob: dc41bf41de41d3d166f891f0e978f4e2a3e3dc1c [file] [log] [blame]
/* Copyright (c) 2010-2011, Code Aurora Forum. 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/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/kthread.h>
#include <linux/list.h>
#include <linux/uaccess.h>
#include <linux/mutex.h>
#include <linux/wakelock.h>
#include <linux/msm_audio_mvs.h>
#include <linux/slab.h>
#include <mach/msm_rpcrouter.h>
#define MVS_PROG 0x30000014
#define MVS_VERS 0x00030001
#define MVS_VERS_COMP_VER4 0x00040001
#define MVS_VERS_COMP_VER5 0x00050001
#define MVS_CLIENT_ID_VOIP 0x00000003
#define MVS_ACQUIRE_PROC 4
#define MVS_ENABLE_PROC 5
#define MVS_RELEASE_PROC 6
#define MVS_AMR_SET_AMR_MODE_PROC 7
#define MVS_AMR_SET_AWB_MODE_PROC 8
#define MVS_VOC_SET_FRAME_RATE_PROC 10
#define MVS_GSM_SET_DTX_MODE_PROC 11
#define MVS_G729A_SET_MODE_PROC 12
#define MVS_G711_GET_MODE_PROC 14
#define MVS_G711_SET_MODE_PROC 15
#define MVS_G711A_GET_MODE_PROC 16
#define MVS_G711A_SET_MODE_PROC 17
#define MVS_G722_SET_MODE_PROC 20
#define MVS_G722_GET_MODE_PROC 21
#define MVS_SET_DTX_MODE_PROC 22
#define MVS_EVENT_CB_TYPE_PROC 1
#define MVS_PACKET_UL_FN_TYPE_PROC 2
#define MVS_PACKET_DL_FN_TYPE_PROC 3
#define MVS_CB_FUNC_ID 0xAAAABBBB
#define MVS_UL_CB_FUNC_ID 0xBBBBCCCC
#define MVS_DL_CB_FUNC_ID 0xCCCCDDDD
#define MVS_FRAME_MODE_VOC_TX 1
#define MVS_FRAME_MODE_VOC_RX 2
#define MVS_FRAME_MODE_AMR_UL 3
#define MVS_FRAME_MODE_AMR_DL 4
#define MVS_FRAME_MODE_GSM_UL 5
#define MVS_FRAME_MODE_GSM_DL 6
#define MVS_FRAME_MODE_HR_UL 7
#define MVS_FRAME_MODE_HR_DL 8
#define MVS_FRAME_MODE_G711_UL 9
#define MVS_FRAME_MODE_G711_DL 10
#define MVS_FRAME_MODE_PCM_UL 13
#define MVS_FRAME_MODE_PCM_DL 14
#define MVS_FRAME_MODE_G729A_UL 17
#define MVS_FRAME_MODE_G729A_DL 18
#define MVS_FRAME_MODE_G711A_UL 19
#define MVS_FRAME_MODE_G711A_DL 20
#define MVS_FRAME_MODE_G722_UL 21
#define MVS_FRAME_MODE_G722_DL 22
#define MVS_PKT_CONTEXT_ISR 0x00000001
#define RPC_TYPE_REQUEST 0
#define RPC_TYPE_REPLY 1
#define RPC_STATUS_FAILURE 0
#define RPC_STATUS_SUCCESS 1
#define RPC_STATUS_REJECT 1
#define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2)
#define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr))
#define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3)
enum audio_mvs_state_type {
AUDIO_MVS_CLOSED,
AUDIO_MVS_OPENED,
AUDIO_MVS_STARTED,
AUDIO_MVS_STOPPED
};
enum audio_mvs_event_type {
AUDIO_MVS_COMMAND,
AUDIO_MVS_MODE,
AUDIO_MVS_NOTIFY
};
enum audio_mvs_cmd_status_type {
AUDIO_MVS_CMD_FAILURE,
AUDIO_MVS_CMD_BUSY,
AUDIO_MVS_CMD_SUCCESS
};
enum audio_mvs_mode_status_type {
AUDIO_MVS_MODE_NOT_AVAIL,
AUDIO_MVS_MODE_INIT,
AUDIO_MVS_MODE_READY
};
enum audio_mvs_pkt_status_type {
AUDIO_MVS_PKT_NORMAL,
AUDIO_MVS_PKT_FAST,
AUDIO_MVS_PKT_SLOW
};
/* Parameters required for MVS acquire. */
struct rpc_audio_mvs_acquire_args {
uint32_t client_id;
uint32_t cb_func_id;
};
struct audio_mvs_acquire_msg {
struct rpc_request_hdr rpc_hdr;
struct rpc_audio_mvs_acquire_args acquire_args;
};
/* Parameters required for MVS enable. */
struct rpc_audio_mvs_enable_args {
uint32_t client_id;
uint32_t mode;
uint32_t ul_cb_func_id;
uint32_t dl_cb_func_id;
uint32_t context;
};
struct audio_mvs_enable_msg {
struct rpc_request_hdr rpc_hdr;
struct rpc_audio_mvs_enable_args enable_args;
};
/* Parameters required for MVS release. */
struct audio_mvs_release_msg {
struct rpc_request_hdr rpc_hdr;
uint32_t client_id;
};
/* Parameters required for setting AMR mode. */
struct audio_mvs_set_amr_mode_msg {
struct rpc_request_hdr rpc_hdr;
uint32_t amr_mode;
};
/* Parameters required for setting DTX. */
struct audio_mvs_set_dtx_mode_msg {
struct rpc_request_hdr rpc_hdr;
uint32_t dtx_mode;
};
/* Parameters required for setting EVRC mode. */
struct audio_mvs_set_voc_mode_msg {
struct rpc_request_hdr rpc_hdr;
uint32_t max_rate;
uint32_t min_rate;
};
/* Parameters for G711 mode */
struct audio_mvs_set_g711_mode_msg {
struct rpc_request_hdr rpc_hdr;
uint32_t g711_mode;
};
/* Parameters for G729 mode */
struct audio_mvs_set_g729_mode_msg {
struct rpc_request_hdr rpc_hdr;
uint32_t g729_mode;
};
/* Parameters for G722 mode */
struct audio_mvs_set_g722_mode_msg {
struct rpc_request_hdr rpc_hdr;
uint32_t g722_mode;
};
/* Parameters for G711A mode */
struct audio_mvs_set_g711A_mode_msg {
struct rpc_request_hdr rpc_hdr;
uint32_t g711A_mode;
};
/* Parameters for EFR FR and HR mode */
struct audio_mvs_set_efr_mode_msg {
struct rpc_request_hdr rpc_hdr;
uint32_t efr_mode;
};
union audio_mvs_event_data {
struct mvs_ev_command_type {
uint32_t event;
uint32_t client_id;
uint32_t cmd_status;
} mvs_ev_command_type;
struct mvs_ev_mode_type {
uint32_t event;
uint32_t client_id;
uint32_t mode_status;
uint32_t mode;
} mvs_ev_mode_type;
struct mvs_ev_notify_type {
uint32_t event;
uint32_t client_id;
uint32_t buf_dir;
uint32_t max_frames;
} mvs_ev_notify_type;
};
struct audio_mvs_cb_func_args {
uint32_t cb_func_id;
uint32_t valid_ptr;
uint32_t event;
union audio_mvs_event_data event_data;
};
struct audio_mvs_frame_info_hdr {
uint32_t frame_mode;
uint32_t mvs_mode;
uint16_t buf_free_cnt;
};
struct audio_mvs_ul_reply {
struct rpc_reply_hdr reply_hdr;
uint32_t valid_pkt_status_ptr;
uint32_t pkt_status;
};
struct audio_mvs_dl_cb_func_args {
uint32_t cb_func_id;
uint32_t valid_ptr;
uint32_t frame_mode;
uint32_t frame_mode_ignore;
struct audio_mvs_frame_info_hdr frame_info_hdr;
uint32_t amr_frame;
uint32_t amr_mode;
};
/*general codec parameters includes AMR, G711A, PCM
G729, VOC and HR vocoders
*/
struct gnr_cdc_param {
uint32_t param1;
uint32_t param2;
uint32_t valid_pkt_status_ptr;
uint32_t pkt_status;
};
/*G711 codec parameter*/
struct g711_param {
uint32_t param1;
uint32_t valid_pkt_status_ptr;
uint32_t pkt_status;
};
union codec_param {
struct gnr_cdc_param gnr_arg;
struct g711_param g711_arg;
};
struct audio_mvs_dl_reply {
struct rpc_reply_hdr reply_hdr;
uint32_t voc_pkt[MVS_MAX_VOC_PKT_SIZE/4];
uint32_t valid_frame_info_ptr;
uint32_t frame_mode;
uint32_t frame_mode_again;
struct audio_mvs_frame_info_hdr frame_info_hdr;
union codec_param cdc_param;
};
struct audio_mvs_buf_node {
struct list_head list;
struct msm_audio_mvs_frame frame;
};
/* Each buffer is 20 ms, queue holds 200 ms of data. */
#define MVS_MAX_Q_LEN 10
struct audio_mvs_info_type {
enum audio_mvs_state_type state;
uint32_t frame_mode;
uint32_t mvs_mode;
uint32_t buf_free_cnt;
uint32_t rate_type;
uint32_t dtx_mode;
struct msm_rpc_endpoint *rpc_endpt;
uint32_t rpc_prog;
uint32_t rpc_ver;
uint32_t rpc_status;
uint8_t *mem_chunk;
struct list_head in_queue;
struct list_head free_in_queue;
struct list_head out_queue;
struct list_head free_out_queue;
struct task_struct *task;
wait_queue_head_t wait;
wait_queue_head_t mode_wait;
wait_queue_head_t out_wait;
struct mutex lock;
struct mutex in_lock;
struct mutex out_lock;
struct wake_lock suspend_lock;
struct wake_lock idle_lock;
};
static struct audio_mvs_info_type audio_mvs_info;
static int audio_mvs_setup_mode(struct audio_mvs_info_type *audio)
{
int rc = 0;
pr_debug("%s:\n", __func__);
switch (audio->mvs_mode) {
case MVS_MODE_AMR:
case MVS_MODE_AMR_WB: {
struct audio_mvs_set_amr_mode_msg set_amr_mode_msg;
struct audio_mvs_set_dtx_mode_msg set_dtx_mode_msg;
/* Set AMR mode. */
memset(&set_amr_mode_msg, 0, sizeof(set_amr_mode_msg));
set_amr_mode_msg.amr_mode = cpu_to_be32(audio->rate_type);
if (audio->mvs_mode == MVS_MODE_AMR) {
msm_rpc_setup_req(&set_amr_mode_msg.rpc_hdr,
audio->rpc_prog,
audio->rpc_ver,
MVS_AMR_SET_AMR_MODE_PROC);
} else {
msm_rpc_setup_req(&set_amr_mode_msg.rpc_hdr,
audio->rpc_prog,
audio->rpc_ver,
MVS_AMR_SET_AWB_MODE_PROC);
}
audio->rpc_status = RPC_STATUS_FAILURE;
rc = msm_rpc_write(audio->rpc_endpt,
&set_amr_mode_msg,
sizeof(set_amr_mode_msg));
if (rc >= 0) {
pr_debug("%s: RPC write for set amr mode done\n",
__func__);
/* Save the MVS configuration information. */
audio->frame_mode = MVS_FRAME_MODE_AMR_DL;
/* Disable DTX. */
memset(&set_dtx_mode_msg, 0, sizeof(set_dtx_mode_msg));
set_dtx_mode_msg.dtx_mode = cpu_to_be32(0);
msm_rpc_setup_req(&set_dtx_mode_msg.rpc_hdr,
audio->rpc_prog,
audio->rpc_ver,
MVS_SET_DTX_MODE_PROC);
audio->rpc_status = RPC_STATUS_FAILURE;
rc = msm_rpc_write(audio->rpc_endpt,
&set_dtx_mode_msg,
sizeof(set_dtx_mode_msg));
if (rc >= 0) {
pr_debug("%s: RPC write for set dtx done\n",
__func__);
rc = 0;
}
} else {
pr_err("%s: RPC write for set amr mode failed %d\n",
__func__, rc);
}
break;
}
case MVS_MODE_PCM:
case MVS_MODE_LINEAR_PCM: {
/* PCM does not have any params to be set.
Save the MVS configuration information. */
audio->rate_type = MVS_AMR_MODE_UNDEF;
audio->frame_mode = MVS_FRAME_MODE_PCM_DL;
break;
}
case MVS_MODE_IS127:
case MVS_MODE_IS733:
case MVS_MODE_4GV_NB:
case MVS_MODE_4GV_WB: {
struct audio_mvs_set_voc_mode_msg set_voc_mode_msg;
/* Set EVRC mode. */
memset(&set_voc_mode_msg, 0, sizeof(set_voc_mode_msg));
set_voc_mode_msg.min_rate = cpu_to_be32(audio->rate_type);
set_voc_mode_msg.max_rate = cpu_to_be32(audio->rate_type);
msm_rpc_setup_req(&set_voc_mode_msg.rpc_hdr,
audio->rpc_prog,
audio->rpc_ver,
MVS_VOC_SET_FRAME_RATE_PROC);
audio->rpc_status = RPC_STATUS_FAILURE;
rc = msm_rpc_write(audio->rpc_endpt,
&set_voc_mode_msg,
sizeof(set_voc_mode_msg));
if (rc >= 0) {
pr_debug("%s: RPC write for set voc mode done\n",
__func__);
/* Save the MVS configuration information. */
audio->frame_mode = MVS_FRAME_MODE_VOC_RX;
rc = 0;
} else {
pr_err("%s: RPC write for set voc mode failed %d\n",
__func__, rc);
}
break;
}
case MVS_MODE_G711: {
struct audio_mvs_set_g711_mode_msg set_g711_mode_msg;
/* Set G711 mode. */
memset(&set_g711_mode_msg, 0, sizeof(set_g711_mode_msg));
set_g711_mode_msg.g711_mode = cpu_to_be32(audio->rate_type);
pr_debug("%s: mode of g711:%d\n",
__func__, set_g711_mode_msg.g711_mode);
msm_rpc_setup_req(&set_g711_mode_msg.rpc_hdr,
audio->rpc_prog,
audio->rpc_ver,
MVS_G711_SET_MODE_PROC);
audio->rpc_status = RPC_STATUS_FAILURE;
rc = msm_rpc_write(audio->rpc_endpt,
&set_g711_mode_msg,
sizeof(set_g711_mode_msg));
if (rc >= 0) {
pr_debug("%s: RPC write for set g711 mode done\n",
__func__);
/* Save the MVS configuration information. */
audio->frame_mode = MVS_FRAME_MODE_G711_DL;
rc = 0;
} else {
pr_err("%s: RPC write for set g711 mode failed %d\n",
__func__, rc);
}
break;
}
case MVS_MODE_G729A: {
struct audio_mvs_set_g729_mode_msg set_g729_mode_msg;
/* Set G729 mode. */
memset(&set_g729_mode_msg, 0, sizeof(set_g729_mode_msg));
set_g729_mode_msg.g729_mode = cpu_to_be32(audio->dtx_mode);
pr_debug("%s: mode of g729:%d\n",
__func__, set_g729_mode_msg.g729_mode);
msm_rpc_setup_req(&set_g729_mode_msg.rpc_hdr,
audio->rpc_prog,
audio->rpc_ver,
MVS_G729A_SET_MODE_PROC);
audio->rpc_status = RPC_STATUS_FAILURE;
rc = msm_rpc_write(audio->rpc_endpt,
&set_g729_mode_msg,
sizeof(set_g729_mode_msg));
if (rc >= 0) {
pr_debug("%s: RPC write for set g729 mode done\n",
__func__);
/* Save the MVS configuration information. */
audio->frame_mode = MVS_FRAME_MODE_G729A_DL;
rc = 0;
} else {
pr_err("%s: RPC write for set g729 mode failed %d\n",
__func__, rc);
}
break;
}
case MVS_MODE_G722: {
struct audio_mvs_set_g722_mode_msg set_g722_mode_msg;
/* Set G722 mode. */
memset(&set_g722_mode_msg, 0, sizeof(set_g722_mode_msg));
set_g722_mode_msg.g722_mode = cpu_to_be32(audio->rate_type);
pr_debug("%s: mode of g722:%d\n",
__func__, set_g722_mode_msg.g722_mode);
msm_rpc_setup_req(&set_g722_mode_msg.rpc_hdr,
audio->rpc_prog,
audio->rpc_ver,
MVS_G722_SET_MODE_PROC);
audio->rpc_status = RPC_STATUS_FAILURE;
rc = msm_rpc_write(audio->rpc_endpt,
&set_g722_mode_msg,
sizeof(set_g722_mode_msg));
if (rc >= 0) {
pr_debug("%s: RPC write for set g722 mode done\n",
__func__);
/* Save the MVS configuration information. */
audio->frame_mode = MVS_FRAME_MODE_G722_DL;
rc = 0;
}
break;
}
case MVS_MODE_G711A: {
struct audio_mvs_set_g711A_mode_msg set_g711A_mode_msg;
struct audio_mvs_set_dtx_mode_msg set_dtx_mode_msg;
/* Set G711A mode. */
memset(&set_g711A_mode_msg, 0, sizeof(set_g711A_mode_msg));
set_g711A_mode_msg.g711A_mode = cpu_to_be32(audio->rate_type);
pr_debug("%s: mode of g711A:%d\n",
__func__, set_g711A_mode_msg.g711A_mode);
msm_rpc_setup_req(&set_g711A_mode_msg.rpc_hdr,
audio->rpc_prog,
audio->rpc_ver,
MVS_G711A_SET_MODE_PROC);
audio->rpc_status = RPC_STATUS_FAILURE;
rc = msm_rpc_write(audio->rpc_endpt,
&set_g711A_mode_msg,
sizeof(set_g711A_mode_msg));
if (rc >= 0) {
pr_debug("%s: RPC write for set g711A mode done\n",
__func__);
/* Save the MVS configuration information. */
audio->frame_mode = MVS_FRAME_MODE_G711A_DL;
/* Set DTX MODE. */
memset(&set_dtx_mode_msg, 0, sizeof(set_dtx_mode_msg));
set_dtx_mode_msg.dtx_mode =
cpu_to_be32((audio->dtx_mode));
msm_rpc_setup_req(&set_dtx_mode_msg.rpc_hdr,
audio->rpc_prog,
audio->rpc_ver,
MVS_SET_DTX_MODE_PROC);
audio->rpc_status = RPC_STATUS_FAILURE;
rc = msm_rpc_write(audio->rpc_endpt,
&set_dtx_mode_msg,
sizeof(set_dtx_mode_msg));
if (rc >= 0) {
pr_debug("%s: RPC write for set dtx done\n",
__func__);
rc = 0;
}
rc = 0;
} else {
pr_err("%s: RPC write for set g711A mode failed %d\n",
__func__, rc);
}
break;
}
case MVS_MODE_EFR:
case MVS_MODE_FR:
case MVS_MODE_HR: {
struct audio_mvs_set_efr_mode_msg set_efr_mode_msg;
/* Set G729 mode. */
memset(&set_efr_mode_msg, 0, sizeof(set_efr_mode_msg));
set_efr_mode_msg.efr_mode = cpu_to_be32(audio->dtx_mode);
pr_debug("%s: mode of EFR, FR and HR:%d\n",
__func__, set_efr_mode_msg.efr_mode);
msm_rpc_setup_req(&set_efr_mode_msg.rpc_hdr,
audio->rpc_prog,
audio->rpc_ver,
MVS_GSM_SET_DTX_MODE_PROC);
audio->rpc_status = RPC_STATUS_FAILURE;
rc = msm_rpc_write(audio->rpc_endpt,
&set_efr_mode_msg,
sizeof(set_efr_mode_msg));
if (rc >= 0) {
pr_debug("%s: RPC write for set EFR, FR and HR mode done\n",
__func__);
/* Save the MVS configuration information. */
if ((audio->mvs_mode == MVS_MODE_EFR) ||
(audio->mvs_mode == MVS_MODE_FR))
audio->frame_mode = MVS_FRAME_MODE_GSM_DL;
if (audio->mvs_mode == MVS_MODE_HR)
audio->frame_mode = MVS_FRAME_MODE_HR_DL;
rc = 0;
} else {
pr_err("%s: RPC write for set EFR, FR and HR mode failed %d\n",
__func__, rc);
}
break;
}
default:
rc = -EINVAL;
pr_err("Default case\n");
}
return rc;
}
static int audio_mvs_setup(struct audio_mvs_info_type *audio)
{
int rc = 0;
struct audio_mvs_enable_msg enable_msg;
pr_debug("%s:\n", __func__);
/* Enable MVS. */
memset(&enable_msg, 0, sizeof(enable_msg));
enable_msg.enable_args.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP);
enable_msg.enable_args.mode = cpu_to_be32(audio->mvs_mode);
enable_msg.enable_args.ul_cb_func_id = cpu_to_be32(MVS_UL_CB_FUNC_ID);
enable_msg.enable_args.dl_cb_func_id = cpu_to_be32(MVS_DL_CB_FUNC_ID);
enable_msg.enable_args.context = cpu_to_be32(MVS_PKT_CONTEXT_ISR);
msm_rpc_setup_req(&enable_msg.rpc_hdr,
audio->rpc_prog,
audio->rpc_ver,
MVS_ENABLE_PROC);
audio->rpc_status = RPC_STATUS_FAILURE;
rc = msm_rpc_write(audio->rpc_endpt, &enable_msg, sizeof(enable_msg));
if (rc >= 0) {
pr_debug("%s: RPC write for enable done\n", __func__);
rc = wait_event_timeout(audio->mode_wait,
(audio->rpc_status != RPC_STATUS_FAILURE),
10 * HZ);
if (rc > 0) {
pr_debug("%s: Wait event for enable succeeded\n",
__func__);
rc = audio_mvs_setup_mode(audio);
if (rc < 0) {
pr_err("%s: Unknown MVS mode %d\n",
__func__, audio->mvs_mode);
}
pr_err("rc value after mode setup: %d\n", rc);
} else {
pr_err("%s: Wait event for enable failed %d\n",
__func__, rc);
}
} else {
pr_err("%s: RPC write for enable failed %d\n", __func__, rc);
}
return rc;
}
static int audio_mvs_start(struct audio_mvs_info_type *audio)
{
int rc = 0;
struct audio_mvs_acquire_msg acquire_msg;
pr_info("%s:\n", __func__);
/* Prevent sleep. */
wake_lock(&audio->suspend_lock);
wake_lock(&audio->idle_lock);
/* Acquire MVS. */
memset(&acquire_msg, 0, sizeof(acquire_msg));
acquire_msg.acquire_args.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP);
acquire_msg.acquire_args.cb_func_id = cpu_to_be32(MVS_CB_FUNC_ID);
msm_rpc_setup_req(&acquire_msg.rpc_hdr,
audio->rpc_prog,
audio->rpc_ver,
MVS_ACQUIRE_PROC);
audio->rpc_status = RPC_STATUS_FAILURE;
rc = msm_rpc_write(audio->rpc_endpt,
&acquire_msg,
sizeof(acquire_msg));
if (rc >= 0) {
pr_debug("%s: RPC write for acquire done\n", __func__);
rc = wait_event_timeout(audio->wait,
(audio->rpc_status != RPC_STATUS_FAILURE),
1 * HZ);
if (rc > 0) {
rc = audio_mvs_setup(audio);
if (rc == 0)
audio->state = AUDIO_MVS_STARTED;
} else {
pr_err("%s: Wait event for acquire failed %d\n",
__func__, rc);
rc = -EBUSY;
}
} else {
pr_err("%s: RPC write for acquire failed %d\n", __func__, rc);
rc = -EBUSY;
}
return rc;
}
static int audio_mvs_stop(struct audio_mvs_info_type *audio)
{
int rc = 0;
struct audio_mvs_release_msg release_msg;
pr_info("%s:\n", __func__);
/* Release MVS. */
memset(&release_msg, 0, sizeof(release_msg));
release_msg.client_id = cpu_to_be32(MVS_CLIENT_ID_VOIP);
msm_rpc_setup_req(&release_msg.rpc_hdr,
audio->rpc_prog,
audio->rpc_ver,
MVS_RELEASE_PROC);
audio->rpc_status = RPC_STATUS_FAILURE;
rc = msm_rpc_write(audio->rpc_endpt, &release_msg, sizeof(release_msg));
if (rc >= 0) {
pr_debug("%s: RPC write for release done\n", __func__);
rc = wait_event_timeout(audio->mode_wait,
(audio->rpc_status != RPC_STATUS_FAILURE),
1 * HZ);
if (rc > 0) {
pr_debug("%s: Wait event for release succeeded\n",
__func__);
audio->state = AUDIO_MVS_STOPPED;
/* Un-block read in case it is waiting for data. */
wake_up(&audio->out_wait);
rc = 0;
} else {
pr_err("%s: Wait event for release failed %d\n",
__func__, rc);
}
} else {
pr_err("%s: RPC write for release failed %d\n", __func__, rc);
}
/* Allow sleep. */
wake_unlock(&audio->suspend_lock);
wake_unlock(&audio->idle_lock);
return rc;
}
static void audio_mvs_process_rpc_request(uint32_t procedure,
uint32_t xid,
void *data,
uint32_t length,
struct audio_mvs_info_type *audio)
{
int rc = 0;
pr_debug("%s:\n", __func__);
switch (procedure) {
case MVS_EVENT_CB_TYPE_PROC: {
struct audio_mvs_cb_func_args *args = data;
struct rpc_reply_hdr reply_hdr;
pr_debug("%s: MVS CB CB_FUNC_ID 0x%x\n",
__func__, be32_to_cpu(args->cb_func_id));
if (be32_to_cpu(args->valid_ptr)) {
uint32_t event_type = be32_to_cpu(args->event);
pr_debug("%s: MVS CB event type %d\n",
__func__, be32_to_cpu(args->event));
if (event_type == AUDIO_MVS_COMMAND) {
uint32_t cmd_status = be32_to_cpu(
args->event_data.mvs_ev_command_type.cmd_status);
pr_debug("%s: MVS CB command status %d\n",
__func__, cmd_status);
if (cmd_status == AUDIO_MVS_CMD_SUCCESS) {
audio->rpc_status = RPC_STATUS_SUCCESS;
wake_up(&audio->wait);
}
} else if (event_type == AUDIO_MVS_MODE) {
uint32_t mode_status = be32_to_cpu(
args->event_data.mvs_ev_mode_type.mode_status);
pr_debug("%s: MVS CB mode status %d\n",
__func__, mode_status);
if (mode_status == AUDIO_MVS_MODE_READY) {
audio->rpc_status = RPC_STATUS_SUCCESS;
wake_up(&audio->mode_wait);
}
} else {
pr_err("%s: MVS CB unknown event type %d\n",
__func__, event_type);
}
} else {
pr_err("%s: MVS CB event pointer not valid\n",
__func__);
}
/* Send ack to modem. */
memset(&reply_hdr, 0, sizeof(reply_hdr));
reply_hdr.xid = cpu_to_be32(xid);
reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY);
reply_hdr.reply_stat = cpu_to_be32(RPCMSG_REPLYSTAT_ACCEPTED);
reply_hdr.data.acc_hdr.accept_stat = cpu_to_be32(
RPC_ACCEPTSTAT_SUCCESS);
reply_hdr.data.acc_hdr.verf_flavor = 0;
reply_hdr.data.acc_hdr.verf_length = 0;
rc = msm_rpc_write(audio->rpc_endpt,
&reply_hdr,
sizeof(reply_hdr));
if (rc < 0)
pr_err("%s: RPC write for response failed %d\n",
__func__, rc);
break;
}
case MVS_PACKET_UL_FN_TYPE_PROC: {
uint32_t *args = data;
uint32_t pkt_len;
uint32_t frame_mode;
struct audio_mvs_ul_reply ul_reply;
struct audio_mvs_buf_node *buf_node = NULL;
pr_debug("%s: MVS UL CB_FUNC_ID 0x%x\n",
__func__, be32_to_cpu(*args));
args++;
pkt_len = be32_to_cpu(*args);
pr_debug("%s: UL pkt_len %d\n", __func__, pkt_len);
args++;
/* Copy the vocoder packets. */
mutex_lock(&audio->out_lock);
if (!list_empty(&audio->free_out_queue)) {
buf_node = list_first_entry(&audio->free_out_queue,
struct audio_mvs_buf_node,
list);
list_del(&buf_node->list);
memcpy(&buf_node->frame.voc_pkt[0], args, pkt_len);
buf_node->frame.len = pkt_len;
pkt_len = ALIGN(pkt_len, 4);
args = args + pkt_len/4;
pr_debug("%s: UL valid_ptr 0x%x\n",
__func__, be32_to_cpu(*args));
args++;
frame_mode = be32_to_cpu(*args);
pr_debug("%s: UL frame_mode %d\n",
__func__, frame_mode);
args++;
pr_debug("%s: UL frame_mode %d\n",
__func__, be32_to_cpu(*args));
args++;
pr_debug("%s: UL frame_mode %d\n",
__func__, be32_to_cpu(*args));
args++;
pr_debug("%s: UL mvs_mode %d\n",
__func__, be32_to_cpu(*args));
args++;
pr_debug("%s: UL buf_free_cnt %d\n",
__func__, be32_to_cpu(*args));
args++;
if (frame_mode == MVS_FRAME_MODE_AMR_UL) {
/* Extract AMR frame type. */
buf_node->frame.frame_type = be32_to_cpu(*args);
pr_debug("%s: UL AMR frame_type %d\n",
__func__, be32_to_cpu(*args));
} else if ((frame_mode == MVS_FRAME_MODE_PCM_UL) ||
(frame_mode == MVS_FRAME_MODE_VOC_TX)) {
/* PCM and EVRC don't have frame_type */
buf_node->frame.frame_type = 0;
} else if (frame_mode == MVS_FRAME_MODE_G711_UL) {
/* Extract G711 frame type. */
buf_node->frame.frame_type = be32_to_cpu(*args);
pr_debug("%s: UL G711 frame_type %d\n",
__func__, be32_to_cpu(*args));
} else if (frame_mode == MVS_FRAME_MODE_G729A_UL) {
/* Extract G729 frame type. */
buf_node->frame.frame_type = be32_to_cpu(*args);
pr_debug("%s: UL G729 frame_type %d\n",
__func__, be32_to_cpu(*args));
} else if (frame_mode == MVS_FRAME_MODE_G722_UL) {
/* Extract G722 frame type. */
buf_node->frame.frame_type = be32_to_cpu(*args);
pr_debug("%s: UL G722 frame_type %d\n",
__func__, be32_to_cpu(*args));
} else if (frame_mode == MVS_FRAME_MODE_G711A_UL) {
/* Extract G711A frame type. */
buf_node->frame.frame_type = be32_to_cpu(*args);
pr_debug("%s: UL G711A frame_type %d\n",
__func__, be32_to_cpu(*args));
} else if ((frame_mode == MVS_FRAME_MODE_GSM_UL) ||
(frame_mode == MVS_FRAME_MODE_HR_UL)) {
/* Extract EFR, FR and HR frame type. */
buf_node->frame.frame_type = be32_to_cpu(*args);
pr_debug("%s: UL EFR,FR,HR frame_type %d\n",
__func__, be32_to_cpu(*args));
} else {
pr_debug("%s: UL Unknown frame mode %d\n",
__func__, frame_mode);
}
list_add_tail(&buf_node->list, &audio->out_queue);
} else {
pr_err("%s: UL data dropped, read is slow\n", __func__);
}
mutex_unlock(&audio->out_lock);
wake_up(&audio->out_wait);
/* Send UL message accept to modem. */
memset(&ul_reply, 0, sizeof(ul_reply));
ul_reply.reply_hdr.xid = cpu_to_be32(xid);
ul_reply.reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY);
ul_reply.reply_hdr.reply_stat = cpu_to_be32(
RPCMSG_REPLYSTAT_ACCEPTED);
ul_reply.reply_hdr.data.acc_hdr.accept_stat = cpu_to_be32(
RPC_ACCEPTSTAT_SUCCESS);
ul_reply.reply_hdr.data.acc_hdr.verf_flavor = 0;
ul_reply.reply_hdr.data.acc_hdr.verf_length = 0;
ul_reply.valid_pkt_status_ptr = cpu_to_be32(0x00000001);
ul_reply.pkt_status = cpu_to_be32(0x00000000);
rc = msm_rpc_write(audio->rpc_endpt,
&ul_reply,
sizeof(ul_reply));
if (rc < 0)
pr_err("%s: RPC write for UL response failed %d\n",
__func__, rc);
break;
}
case MVS_PACKET_DL_FN_TYPE_PROC: {
struct audio_mvs_dl_cb_func_args *args = data;
struct audio_mvs_dl_reply dl_reply;
uint32_t frame_mode;
struct audio_mvs_buf_node *buf_node = NULL;
pr_debug("%s: MVS DL CB CB_FUNC_ID 0x%x\n",
__func__, be32_to_cpu(args->cb_func_id));
frame_mode = be32_to_cpu(args->frame_mode);
pr_debug("%s: DL frame_mode %d\n", __func__, frame_mode);
/* Prepare and send the DL packets to modem. */
memset(&dl_reply, 0, sizeof(dl_reply));
dl_reply.reply_hdr.xid = cpu_to_be32(xid);
dl_reply.reply_hdr.type = cpu_to_be32(RPC_TYPE_REPLY);
dl_reply.reply_hdr.reply_stat = cpu_to_be32(
RPCMSG_REPLYSTAT_ACCEPTED);
dl_reply.reply_hdr.data.acc_hdr.accept_stat = cpu_to_be32(
RPC_ACCEPTSTAT_SUCCESS);
dl_reply.reply_hdr.data.acc_hdr.verf_flavor = 0;
dl_reply.reply_hdr.data.acc_hdr.verf_length = 0;
mutex_lock(&audio->in_lock);
if (!list_empty(&audio->in_queue)) {
buf_node = list_first_entry(&audio->in_queue,
struct audio_mvs_buf_node,
list);
list_del(&buf_node->list);
memcpy(&dl_reply.voc_pkt,
&buf_node->frame.voc_pkt[0],
buf_node->frame.len);
pr_debug("%s:frame mode %d\n", __func__, frame_mode);
if (frame_mode == MVS_FRAME_MODE_AMR_DL) {
dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32(
buf_node->frame.frame_type);
dl_reply.cdc_param.gnr_arg.param2 =
cpu_to_be32(audio->rate_type);
dl_reply.cdc_param.\
gnr_arg.valid_pkt_status_ptr =
cpu_to_be32(0x00000001);
dl_reply.cdc_param.gnr_arg.pkt_status =
cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
} else if (frame_mode == MVS_FRAME_MODE_PCM_DL) {
dl_reply.cdc_param.gnr_arg.param1 = 0;
dl_reply.cdc_param.gnr_arg.param2 = 0;
dl_reply.cdc_param.\
gnr_arg.valid_pkt_status_ptr =
cpu_to_be32(0x00000001);
dl_reply.cdc_param.gnr_arg.pkt_status =
cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
} else if (frame_mode == MVS_FRAME_MODE_VOC_RX) {
dl_reply.cdc_param.gnr_arg.param1 =
cpu_to_be32(audio->rate_type);
dl_reply.cdc_param.gnr_arg.param2 = 0;
dl_reply.cdc_param.\
gnr_arg.valid_pkt_status_ptr =
cpu_to_be32(0x00000001);
dl_reply.cdc_param.gnr_arg.pkt_status =
cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
} else if (frame_mode == MVS_FRAME_MODE_G711_DL) {
dl_reply.cdc_param.g711_arg.param1 =
cpu_to_be32(buf_node->frame.frame_type);
dl_reply.cdc_param.\
g711_arg.valid_pkt_status_ptr =
cpu_to_be32(0x00000001);
dl_reply.cdc_param.g711_arg.pkt_status =
cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
} else if (frame_mode == MVS_FRAME_MODE_G729A_DL) {
dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32(
buf_node->frame.frame_type);
dl_reply.cdc_param.gnr_arg.param2 =
cpu_to_be32(audio->rate_type);
dl_reply.cdc_param.\
gnr_arg.valid_pkt_status_ptr =
cpu_to_be32(0x00000001);
dl_reply.cdc_param.gnr_arg.pkt_status =
cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
} else if (frame_mode == MVS_FRAME_MODE_G722_DL) {
dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32(
buf_node->frame.frame_type);
dl_reply.cdc_param.gnr_arg.param2 =
cpu_to_be32(audio->rate_type);
dl_reply.cdc_param.\
gnr_arg.valid_pkt_status_ptr =
cpu_to_be32(0x00000001);
dl_reply.cdc_param.gnr_arg.pkt_status =
cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
} else if (frame_mode == MVS_FRAME_MODE_G711A_DL) {
dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32(
buf_node->frame.frame_type);
dl_reply.cdc_param.gnr_arg.param2 =
cpu_to_be32(audio->rate_type);
dl_reply.cdc_param.\
gnr_arg.valid_pkt_status_ptr =
cpu_to_be32(0x00000001);
dl_reply.cdc_param.gnr_arg.pkt_status =
cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
} else if ((frame_mode == MVS_FRAME_MODE_GSM_DL) ||
(frame_mode == MVS_FRAME_MODE_HR_DL)) {
dl_reply.cdc_param.gnr_arg.param1 = cpu_to_be32(
buf_node->frame.frame_type);
dl_reply.cdc_param.gnr_arg.param2 =
cpu_to_be32(audio->rate_type);
dl_reply.cdc_param.\
gnr_arg.valid_pkt_status_ptr =
cpu_to_be32(0x00000001);
dl_reply.cdc_param.gnr_arg.pkt_status =
cpu_to_be32(AUDIO_MVS_PKT_NORMAL);
} else {
pr_err("%s: DL Unknown frame mode %d\n",
__func__, frame_mode);
}
list_add_tail(&buf_node->list, &audio->free_in_queue);
} else {
pr_debug("%s: No DL data available to send to MVS\n",
__func__);
if (frame_mode == MVS_FRAME_MODE_G711_DL) {
dl_reply.cdc_param.\
g711_arg.valid_pkt_status_ptr =
cpu_to_be32(0x00000001);
dl_reply.cdc_param.g711_arg.pkt_status =
cpu_to_be32(AUDIO_MVS_PKT_SLOW);
} else {
dl_reply.cdc_param.\
gnr_arg.valid_pkt_status_ptr =
cpu_to_be32(0x00000001);
dl_reply.cdc_param.gnr_arg.pkt_status =
cpu_to_be32(AUDIO_MVS_PKT_SLOW);
}
}
mutex_unlock(&audio->in_lock);
dl_reply.valid_frame_info_ptr = cpu_to_be32(0x00000001);
dl_reply.frame_mode = cpu_to_be32(audio->frame_mode);
dl_reply.frame_mode_again = cpu_to_be32(audio->frame_mode);
dl_reply.frame_info_hdr.frame_mode =
cpu_to_be32(audio->frame_mode);
dl_reply.frame_info_hdr.mvs_mode = cpu_to_be32(audio->mvs_mode);
dl_reply.frame_info_hdr.buf_free_cnt = 0;
rc = msm_rpc_write(audio->rpc_endpt,
&dl_reply,
sizeof(dl_reply));
if (rc < 0)
pr_err("%s: RPC write for DL response failed %d\n",
__func__, rc);
break;
}
default:
pr_err("%s: Unknown CB type %d\n", __func__, procedure);
}
}
static int audio_mvs_thread(void *data)
{
struct audio_mvs_info_type *audio = data;
struct rpc_request_hdr *rpc_hdr = NULL;
pr_info("%s:\n", __func__);
while (!kthread_should_stop()) {
int rpc_hdr_len = msm_rpc_read(audio->rpc_endpt,
(void **) &rpc_hdr,
-1,
-1);
if (rpc_hdr_len < 0) {
pr_err("%s: RPC read failed %d\n",
__func__, rpc_hdr_len);
break;
} else if (rpc_hdr_len < RPC_COMMON_HDR_SZ) {
continue;
} else {
uint32_t rpc_type = be32_to_cpu(rpc_hdr->type);
if (rpc_type == RPC_TYPE_REPLY) {
struct rpc_reply_hdr *rpc_reply =
(void *) rpc_hdr;
uint32_t reply_status;
if (rpc_hdr_len < RPC_REPLY_HDR_SZ)
continue;
reply_status =
be32_to_cpu(rpc_reply->reply_stat);
if (reply_status != RPCMSG_REPLYSTAT_ACCEPTED) {
/* If the command is not accepted, there
* will be no response callback. Wake
* the caller and report error. */
audio->rpc_status = RPC_STATUS_REJECT;
wake_up(&audio->wait);
pr_err("%s: RPC reply status denied\n",
__func__);
}
} else if (rpc_type == RPC_TYPE_REQUEST) {
if (rpc_hdr_len < RPC_REQUEST_HDR_SZ)
continue;
audio_mvs_process_rpc_request(
be32_to_cpu(rpc_hdr->procedure),
be32_to_cpu(rpc_hdr->xid),
(void *) (rpc_hdr + 1),
(rpc_hdr_len - sizeof(*rpc_hdr)),
audio);
} else {
pr_err("%s: Unexpected RPC type %d\n",
__func__, rpc_type);
}
}
kfree(rpc_hdr);
rpc_hdr = NULL;
}
pr_info("%s: MVS thread stopped\n", __func__);
return 0;
}
static int audio_mvs_alloc_buf(struct audio_mvs_info_type *audio)
{
int i = 0;
struct audio_mvs_buf_node *buf_node = NULL;
struct list_head *ptr = NULL;
struct list_head *next = NULL;
pr_debug("%s:\n", __func__);
/* Allocate input buffers. */
for (i = 0; i < MVS_MAX_Q_LEN; i++) {
buf_node = kmalloc(sizeof(struct audio_mvs_buf_node),
GFP_KERNEL);
if (buf_node != NULL) {
list_add_tail(&buf_node->list,
&audio->free_in_queue);
} else {
pr_err("%s: No memory for IO buffers\n",
__func__);
goto err;
}
buf_node = NULL;
}
/* Allocate output buffers. */
for (i = 0; i < MVS_MAX_Q_LEN; i++) {
buf_node = kmalloc(sizeof(struct audio_mvs_buf_node),
GFP_KERNEL);
if (buf_node != NULL) {
list_add_tail(&buf_node->list,
&audio->free_out_queue);
} else {
pr_err("%s: No memory for IO buffers\n",
__func__);
goto err;
}
buf_node = NULL;
}
return 0;
err:
list_for_each_safe(ptr, next, &audio->free_in_queue) {
buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
list_del(&buf_node->list);
kfree(buf_node);
buf_node = NULL;
}
ptr = next = NULL;
list_for_each_safe(ptr, next, &audio->free_out_queue) {
buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
list_del(&buf_node->list);
kfree(buf_node);
buf_node = NULL;
}
return -ENOMEM;
}
static void audio_mvs_free_buf(struct audio_mvs_info_type *audio)
{
struct list_head *ptr = NULL;
struct list_head *next = NULL;
struct audio_mvs_buf_node *buf_node = NULL;
pr_debug("%s:\n", __func__);
mutex_lock(&audio->in_lock);
/* Free input buffers. */
list_for_each_safe(ptr, next, &audio->in_queue) {
buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
list_del(&buf_node->list);
kfree(buf_node);
buf_node = NULL;
}
ptr = next = NULL;
/* Free free_input buffers. */
list_for_each_safe(ptr, next, &audio->free_in_queue) {
buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
list_del(&buf_node->list);
kfree(buf_node);
buf_node = NULL;
}
mutex_unlock(&audio->in_lock);
mutex_lock(&audio->out_lock);
ptr = next = NULL;
/* Free output buffers. */
list_for_each_safe(ptr, next, &audio->out_queue) {
buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
list_del(&buf_node->list);
kfree(buf_node);
buf_node = NULL;
}
/* Free free_ioutput buffers. */
ptr = next = NULL;
list_for_each_safe(ptr, next, &audio->free_out_queue) {
buf_node = list_entry(ptr, struct audio_mvs_buf_node, list);
list_del(&buf_node->list);
kfree(buf_node);
buf_node = NULL;
}
mutex_unlock(&audio->out_lock);
}
static int audio_mvs_open(struct inode *inode, struct file *file)
{
int rc = 0;
pr_info("%s:\n", __func__);
mutex_lock(&audio_mvs_info.lock);
if (audio_mvs_info.state == AUDIO_MVS_CLOSED) {
if (audio_mvs_info.task != NULL ||
audio_mvs_info.rpc_endpt != NULL) {
rc = audio_mvs_alloc_buf(&audio_mvs_info);
if (rc == 0) {
audio_mvs_info.state = AUDIO_MVS_OPENED;
file->private_data = &audio_mvs_info;
}
} else {
pr_err("%s: MVS thread and RPC end point do not exist\n",
__func__);
rc = -ENODEV;
}
} else {
pr_err("%s: MVS driver exists, state %d\n",
__func__, audio_mvs_info.state);
rc = -EBUSY;
}
mutex_unlock(&audio_mvs_info.lock);
return rc;
}
static int audio_mvs_release(struct inode *inode, struct file *file)
{
struct audio_mvs_info_type *audio = file->private_data;
pr_info("%s:\n", __func__);
mutex_lock(&audio->lock);
if (audio->state == AUDIO_MVS_STARTED)
audio_mvs_stop(audio);
audio_mvs_free_buf(audio);
audio->state = AUDIO_MVS_CLOSED;
mutex_unlock(&audio->lock);
pr_debug("%s: Release done\n", __func__);
return 0;
}
static ssize_t audio_mvs_read(struct file *file,
char __user *buf,
size_t count,
loff_t *pos)
{
int rc = 0;
struct audio_mvs_buf_node *buf_node = NULL;
struct audio_mvs_info_type *audio = file->private_data;
pr_debug("%s:\n", __func__);
rc = wait_event_interruptible_timeout(audio->out_wait,
(!list_empty(&audio->out_queue) ||
audio->state == AUDIO_MVS_STOPPED),
1 * HZ);
if (rc > 0) {
mutex_lock(&audio->out_lock);
if ((audio->state == AUDIO_MVS_STARTED) &&
(!list_empty(&audio->out_queue))) {
if (count >= sizeof(struct msm_audio_mvs_frame)) {
buf_node = list_first_entry(&audio->out_queue,
struct audio_mvs_buf_node,
list);
list_del(&buf_node->list);
rc = copy_to_user(buf,
&buf_node->frame,
sizeof(struct msm_audio_mvs_frame));
if (rc == 0) {
rc = buf_node->frame.len +
sizeof(buf_node->frame.frame_type) +
sizeof(buf_node->frame.len);
} else {
pr_err("%s: Copy to user retuned %d",
__func__, rc);
rc = -EFAULT;
}
list_add_tail(&buf_node->list,
&audio->free_out_queue);
} else {
pr_err("%s: Read count %d < sizeof(frame) %d",
__func__, count,
sizeof(struct msm_audio_mvs_frame));
rc = -ENOMEM;
}
} else {
pr_err("%s: Read performed in state %d\n",
__func__, audio->state);
rc = -EPERM;
}
mutex_unlock(&audio->out_lock);
} else if (rc == 0) {
pr_err("%s: No UL data available\n", __func__);
rc = -ETIMEDOUT;
} else {
pr_err("%s: Read was interrupted\n", __func__);
rc = -ERESTARTSYS;
}
return rc;
}
static ssize_t audio_mvs_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *pos)
{
int rc = 0;
struct audio_mvs_buf_node *buf_node = NULL;
struct audio_mvs_info_type *audio = file->private_data;
pr_debug("%s:\n", __func__);
mutex_lock(&audio->in_lock);
if (audio->state == AUDIO_MVS_STARTED) {
if (count <= sizeof(struct msm_audio_mvs_frame)) {
if (!list_empty(&audio->free_in_queue)) {
buf_node =
list_first_entry(&audio->free_in_queue,
struct audio_mvs_buf_node,
list);
list_del(&buf_node->list);
rc = copy_from_user(&buf_node->frame,
buf,
count);
list_add_tail(&buf_node->list,
&audio->in_queue);
} else {
pr_err("%s: No free DL buffs\n", __func__);
}
} else {
pr_err("%s: Write count %d < sizeof(frame) %d",
__func__, count,
sizeof(struct msm_audio_mvs_frame));
rc = -ENOMEM;
}
} else {
pr_err("%s: Write performed in invalid state %d\n",
__func__, audio->state);
rc = -EPERM;
}
mutex_unlock(&audio->in_lock);
return rc;
}
static long audio_mvs_ioctl(struct file *file,
unsigned int cmd,
unsigned long arg)
{
int rc = 0;
struct audio_mvs_info_type *audio = file->private_data;
pr_info("%s:\n", __func__);
switch (cmd) {
case AUDIO_GET_MVS_CONFIG: {
struct msm_audio_mvs_config config;
pr_debug("%s: IOCTL GET_MVS_CONFIG\n", __func__);
mutex_lock(&audio->lock);
config.mvs_mode = audio->mvs_mode;
config.rate_type = audio->rate_type;
mutex_unlock(&audio->lock);
rc = copy_to_user((void *)arg, &config, sizeof(config));
if (rc == 0)
rc = sizeof(config);
else
pr_err("%s: Config copy failed %d\n", __func__, rc);
break;
}
case AUDIO_SET_MVS_CONFIG: {
struct msm_audio_mvs_config config;
pr_debug("%s: IOCTL SET_MVS_CONFIG\n", __func__);
rc = copy_from_user(&config, (void *)arg, sizeof(config));
if (rc == 0) {
mutex_lock(&audio->lock);
if (audio->state == AUDIO_MVS_OPENED) {
audio->mvs_mode = config.mvs_mode;
audio->rate_type = config.rate_type;
audio->dtx_mode = config.dtx_mode;
} else {
pr_err("%s: Set confg called in state %d\n",
__func__, audio->state);
rc = -EPERM;
}
mutex_unlock(&audio->lock);
} else {
pr_err("%s: Config copy failed %d\n", __func__, rc);
}
break;
}
case AUDIO_START: {
pr_debug("%s: IOCTL START\n", __func__);
mutex_lock(&audio->lock);
if (audio->state == AUDIO_MVS_OPENED ||
audio->state == AUDIO_MVS_STOPPED) {
rc = audio_mvs_start(audio);
if (rc != 0)
audio_mvs_stop(audio);
} else {
pr_err("%s: Start called in invalid state %d\n",
__func__, audio->state);
rc = -EPERM;
}
mutex_unlock(&audio->lock);
break;
}
case AUDIO_STOP: {
pr_debug("%s: IOCTL STOP\n", __func__);
mutex_lock(&audio->lock);
if (audio->state == AUDIO_MVS_STARTED) {
rc = audio_mvs_stop(audio);
} else {
pr_err("%s: Stop called in invalid state %d\n",
__func__, audio->state);
rc = -EPERM;
}
mutex_unlock(&audio->lock);
break;
}
default: {
pr_err("%s: Unknown IOCTL %d\n", __func__, cmd);
}
}
return rc;
}
static const struct file_operations audio_mvs_fops = {
.owner = THIS_MODULE,
.open = audio_mvs_open,
.release = audio_mvs_release,
.read = audio_mvs_read,
.write = audio_mvs_write,
.unlocked_ioctl = audio_mvs_ioctl
};
struct miscdevice audio_mvs_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "msm_mvs",
.fops = &audio_mvs_fops
};
static int __init audio_mvs_init(void)
{
int rc;
pr_info("%s:\n", __func__);
memset(&audio_mvs_info, 0, sizeof(audio_mvs_info));
mutex_init(&audio_mvs_info.lock);
mutex_init(&audio_mvs_info.in_lock);
mutex_init(&audio_mvs_info.out_lock);
init_waitqueue_head(&audio_mvs_info.wait);
init_waitqueue_head(&audio_mvs_info.mode_wait);
init_waitqueue_head(&audio_mvs_info.out_wait);
INIT_LIST_HEAD(&audio_mvs_info.in_queue);
INIT_LIST_HEAD(&audio_mvs_info.free_in_queue);
INIT_LIST_HEAD(&audio_mvs_info.out_queue);
INIT_LIST_HEAD(&audio_mvs_info.free_out_queue);
wake_lock_init(&audio_mvs_info.suspend_lock,
WAKE_LOCK_SUSPEND,
"audio_mvs_suspend");
wake_lock_init(&audio_mvs_info.idle_lock,
WAKE_LOCK_IDLE,
"audio_mvs_idle");
audio_mvs_info.rpc_endpt = msm_rpc_connect_compatible(MVS_PROG,
MVS_VERS_COMP_VER5,
MSM_RPC_UNINTERRUPTIBLE);
if (IS_ERR(audio_mvs_info.rpc_endpt)) {
pr_err("%s: MVS RPC connect failed ver 0x%x\n", __func__,
MVS_VERS_COMP_VER5);
audio_mvs_info.rpc_endpt = msm_rpc_connect_compatible(MVS_PROG,
MVS_VERS_COMP_VER4,
MSM_RPC_UNINTERRUPTIBLE);
if (IS_ERR(audio_mvs_info.rpc_endpt)) {
pr_err("%s: MVS RPC connect failed ver 0x%x\n",
__func__, MVS_VERS_COMP_VER4);
audio_mvs_info.rpc_endpt =
msm_rpc_connect_compatible(MVS_PROG,
MVS_VERS,
MSM_RPC_UNINTERRUPTIBLE);
if (IS_ERR(audio_mvs_info.rpc_endpt)) {
pr_err("%s: MVS RPC connect failed ver 0x%x\n",
__func__, MVS_VERS);
rc = PTR_ERR(audio_mvs_info.rpc_endpt);
audio_mvs_info.rpc_endpt = NULL;
goto done;
} else {
pr_debug("%s: MVS RPC connect succeeded ver\
0x%x\n", __func__, MVS_VERS);
audio_mvs_info.rpc_prog = MVS_PROG;
audio_mvs_info.rpc_ver = MVS_VERS;
}
} else {
pr_debug("%s: MVS RPC connect succeeded ver 0x%x\n",
__func__, MVS_VERS_COMP_VER4);
audio_mvs_info.rpc_prog = MVS_PROG;
audio_mvs_info.rpc_ver = MVS_VERS_COMP_VER4;
}
} else {
pr_debug("%s: MVS RPC connect succeeded ver 0x%x\n", __func__,
MVS_VERS_COMP_VER5);
audio_mvs_info.rpc_prog = MVS_PROG;
audio_mvs_info.rpc_ver = MVS_VERS_COMP_VER5;
}
audio_mvs_info.task = kthread_run(audio_mvs_thread,
&audio_mvs_info,
"audio_mvs");
if (IS_ERR(audio_mvs_info.task)) {
pr_err("%s: MVS thread create failed\n", __func__);
rc = PTR_ERR(audio_mvs_info.task);
audio_mvs_info.task = NULL;
msm_rpc_close(audio_mvs_info.rpc_endpt);
audio_mvs_info.rpc_endpt = NULL;
goto done;
}
rc = misc_register(&audio_mvs_misc);
done:
return rc;
}
static void __exit audio_mvs_exit(void)
{
pr_info("%s:\n", __func__);
misc_deregister(&audio_mvs_misc);
}
module_init(audio_mvs_init);
module_exit(audio_mvs_exit);
MODULE_DESCRIPTION("MSM MVS driver");
MODULE_LICENSE("GPL v2");