/* Copyright (c) 2011-2013, 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #define MVS_PROG 0x30000014 #define MVS_VERS 0x00030001 #define MVS_VERS_COMP_VER2 0x00060001 #define MVS_VERS_COMP_VER3 0x00030001 #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_PCM_WB_UL 23 #define MVS_FRAME_MODE_PCM_WB_DL 24 #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 in_wait; wait_queue_head_t out_wait; struct mutex lock; struct mutex in_lock; struct mutex out_lock; struct wake_lock suspend_lock; struct pm_qos_request pm_qos_req; struct completion complete; }; static struct audio_mvs_info_type audio_mvs_info; static int audio_mvs_setup_mode(struct audio_mvs_info_type *audio) { int rc = 0; MM_DBG("\n"); /* Macro prints the file name and function */ 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) { MM_DBG("RPC write for set amr mode done\n"); /* 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) { MM_DBG("RPC write for set dtx done\n"); rc = 0; } } else { MM_ERR("RPC write for set amr mode failed %d\n", 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_PCM_WB: { audio->rate_type = MVS_AMR_MODE_UNDEF; audio->frame_mode = MVS_FRAME_MODE_PCM_WB_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); MM_DBG("audio->mvs_mode %d audio->rate_type %d\n", audio->mvs_mode, 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) { MM_DBG("RPC write for set voc mode done\n"); /* Save the MVS configuration information. */ audio->frame_mode = MVS_FRAME_MODE_VOC_RX; rc = 0; } else { MM_ERR("RPC write for set voc mode failed %d\n", 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); MM_DBG("mode of g711:%d\n", 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) { MM_DBG("RPC write for set g711 mode done\n"); /* Save the MVS configuration information. */ audio->frame_mode = MVS_FRAME_MODE_G711_DL; rc = 0; } else { MM_ERR("RPC write for set g711 mode failed %d\n", 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); MM_DBG("mode of g729:%d\n", 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) { MM_DBG("RPC write for set g729 mode done\n"); /* Save the MVS configuration information. */ audio->frame_mode = MVS_FRAME_MODE_G729A_DL; rc = 0; } else { MM_ERR("RPC write for set g729 mode failed %d\n", 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); MM_DBG("mode of g722:%d\n", 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) { MM_DBG("RPC write for set g722 mode done\n"); /* 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); MM_DBG("mode of g711A:%d\n", 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) { MM_DBG("RPC write for set g711A mode done\n"); /* 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) { MM_DBG("RPC write for set dtx done\n"); rc = 0; } rc = 0; } else { MM_ERR("RPC write for set g711A mode failed %d\n", 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); MM_DBG("mode of EFR, FR and HR:%d\n", 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) { MM_DBG("RPC write for set EFR, FR and HR mode done\n"); /* 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 { MM_ERR("RPC write for set EFR, FR" "and HR mode failed %d\n", rc); } break; } default: rc = -EINVAL; MM_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; MM_DBG("\n"); /* 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) { MM_DBG("RPC write for enable done\n"); rc = wait_event_timeout(audio->mode_wait, (audio->rpc_status != RPC_STATUS_FAILURE), 10 * HZ); if (rc > 0) { MM_DBG("Wait event for enable succeeded\n"); rc = audio_mvs_setup_mode(audio); if (rc < 0) { MM_ERR("Unknown MVS mode %d\n", audio->mvs_mode); } MM_ERR("rc value after mode setup: %d\n", rc); } else { MM_ERR("Wait event for enable failed %d\n", rc); } } else { MM_ERR("RPC write for enable failed %d\n", rc); } return rc; } static int audio_mvs_start(struct audio_mvs_info_type *audio) { int rc = 0; struct audio_mvs_acquire_msg acquire_msg; MM_DBG("\n"); /* Prevent sleep. */ wake_lock(&audio->suspend_lock); pm_qos_update_request(&audio->pm_qos_req, msm_cpuidle_get_deep_idle_latency()); /* 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) { MM_DBG("RPC write for acquire done\n"); 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 { MM_ERR("Wait event for acquire failed %d\n", rc); rc = -EBUSY; } } else { MM_ERR("RPC write for acquire failed %d\n", 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; MM_DBG("\n"); /* 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) { MM_DBG("RPC write for release done\n"); rc = wait_event_timeout(audio->mode_wait, (audio->rpc_status != RPC_STATUS_FAILURE), 1 * HZ); if (rc > 0) { MM_DBG("Wait event for release succeeded\n"); audio->state = AUDIO_MVS_STOPPED; /* Un-block read in case it is waiting for data. */ wake_up(&audio->out_wait); rc = 0; } else { MM_ERR("Wait event for release failed %d\n", rc); } } else { MM_ERR("RPC write for release failed %d\n", rc); } /* Allow sleep. */ pm_qos_update_request(&audio->pm_qos_req, PM_QOS_DEFAULT_VALUE); wake_unlock(&audio->suspend_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; MM_DBG("\n"); switch (procedure) { case MVS_EVENT_CB_TYPE_PROC: { struct audio_mvs_cb_func_args *args = data; struct rpc_reply_hdr reply_hdr; MM_DBG("MVS CB CB_FUNC_ID 0x%x\n", be32_to_cpu(args->cb_func_id)); if (be32_to_cpu(args->valid_ptr)) { uint32_t event_type = be32_to_cpu(args->event); MM_DBG("MVS CB event type %d\n", 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); MM_DBG("MVS CB command status %d\n", 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); MM_DBG("MVS CB mode status %d\n", mode_status); if (mode_status == AUDIO_MVS_MODE_READY) { audio->rpc_status = RPC_STATUS_SUCCESS; wake_up(&audio->mode_wait); } } else { MM_ERR("MVS CB unknown event type %d\n", event_type); } } else { MM_ERR("MVS CB event pointer not valid\n"); } /* 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) MM_ERR("RPC write for response failed %d\n", 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; MM_DBG("MVS UL CB_FUNC_ID 0x%x\n", be32_to_cpu(*args)); args++; pkt_len = be32_to_cpu(*args); MM_DBG("UL pkt_len %d\n", 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; MM_DBG("UL valid_ptr 0x%x\n", be32_to_cpu(*args)); args++; frame_mode = be32_to_cpu(*args); MM_DBG("UL frame_mode %d\n", frame_mode); args++; MM_DBG("UL frame_mode %d\n", be32_to_cpu(*args)); args++; MM_DBG("UL frame_mode %d\n", be32_to_cpu(*args)); args++; MM_DBG("UL mvs_mode %d\n", be32_to_cpu(*args)); args++; MM_DBG("UL buf_free_cnt %d\n", 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); MM_DBG("UL AMR frame_type %d\n", be32_to_cpu(*args)); } else if (frame_mode == MVS_FRAME_MODE_PCM_UL) { /* PCM doesn't have frame_type */ buf_node->frame.frame_type = 0; } else if (frame_mode == MVS_FRAME_MODE_VOC_TX) { /* Extracting EVRC current buffer frame rate*/ buf_node->frame.frame_type = be32_to_cpu(*args); pr_debug("%s: UL EVRC frame_type %d\n", __func__, be32_to_cpu(*args)); } else if (frame_mode == MVS_FRAME_MODE_G711_UL) { /* Extract G711 frame type. */ buf_node->frame.frame_type = be32_to_cpu(*args); MM_DBG("UL G711 frame_type %d\n", 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); MM_DBG("UL G729 frame_type %d\n", 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); MM_DBG("UL G722 frame_type %d\n", 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); MM_DBG("UL G711A frame_type %d\n", 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); MM_DBG("UL EFR,FR,HR frame_type %d\n", be32_to_cpu(*args)); } else { MM_DBG("UL Unknown frame mode %d\n", frame_mode); } list_add_tail(&buf_node->list, &audio->out_queue); } else { MM_ERR("UL data dropped, read is slow\n"); } 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) MM_ERR("RPC write for UL response failed %d\n", 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; MM_DBG("MVS DL CB CB_FUNC_ID 0x%x\n", be32_to_cpu(args->cb_func_id)); frame_mode = be32_to_cpu(args->frame_mode); MM_DBG("DL frame_mode %d\n", 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); MM_DBG("frame mode %d buf_node->frame.len %d\n", frame_mode, buf_node->frame.len); 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(buf_node->frame.frame_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 { MM_ERR("DL Unknown frame mode %d\n", frame_mode); } list_add_tail(&buf_node->list, &audio->free_in_queue); } else { MM_DBG("No DL data available to send to MVS\n"); 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); wake_up(&audio->in_wait); 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) MM_ERR("RPC write for DL response failed %d\n", rc); break; } default: MM_ERR("Unknown CB type %d\n", procedure); } } static int audio_mvs_thread(void *data) { struct audio_mvs_info_type *audio = data; struct rpc_request_hdr *rpc_hdr = NULL; MM_DBG("\n"); while (!kthread_should_stop()) { int rpc_hdr_len = msm_rpc_read(audio->rpc_endpt, (void **) &rpc_hdr, -1, -1); if (rpc_hdr_len < 0) { MM_ERR("RPC read failed %d\n", rpc_hdr_len); break; } else if ((rpc_hdr_len == 0) && (audio->state == AUDIO_MVS_CLOSED)) { 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); MM_ERR("RPC reply status denied\n"); } } 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 { MM_ERR("Unexpected RPC type %d\n", rpc_type); } } kfree(rpc_hdr); rpc_hdr = NULL; } complete_and_exit(&audio->complete, 0); MM_DBG("MVS thread stopped\n"); 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; MM_DBG("\n"); /* 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 { MM_ERR("No memory for IO buffers\n"); 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 { MM_ERR("No memory for IO buffers\n"); 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; MM_DBG("\n"); 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_release(struct inode *inode, struct file *file) { struct audio_mvs_info_type *audio = file->private_data; MM_DBG("\n"); mutex_lock(&audio->lock); if (audio->state == AUDIO_MVS_STARTED) audio_mvs_stop(audio); audio->state = AUDIO_MVS_CLOSED; msm_rpc_read_wakeup(audio->rpc_endpt); wait_for_completion(&audio->complete); msm_rpc_close(audio->rpc_endpt); audio->task = NULL; audio_mvs_free_buf(audio); mutex_unlock(&audio->lock); MM_DBG("Release done\n"); 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; MM_DBG("\n"); 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 { MM_ERR("Copy to user retuned %d", rc); rc = -EFAULT; } list_add_tail(&buf_node->list, &audio->free_out_queue); } else { MM_ERR("Read count %d < sizeof(frame) %d", count, sizeof(struct msm_audio_mvs_frame)); rc = -ENOMEM; } } else { MM_ERR("Read performed in state %d\n", audio->state); rc = -EPERM; } mutex_unlock(&audio->out_lock); } else if (rc == 0) { MM_ERR("No UL data available\n"); rc = -ETIMEDOUT; } else { MM_ERR("Read was interrupted\n"); 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; MM_DBG("\n"); rc = wait_event_interruptible_timeout(audio->in_wait, (!list_empty(&audio->free_in_queue) || audio->state == AUDIO_MVS_STOPPED), 1 * HZ); if (rc > 0) { 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 { MM_ERR("No free DL buffs\n"); } } else { MM_ERR("Write count %d > sizeof(frame) %d", count, sizeof(struct msm_audio_mvs_frame)); rc = -ENOMEM; } } else { MM_ERR("Write performed in invalid state %d\n", audio->state); rc = -EPERM; } mutex_unlock(&audio->in_lock); } else if (rc == 0) { MM_ERR("%s: No free DL buffs\n", __func__); rc = -ETIMEDOUT; } else { MM_ERR("%s: write was interrupted\n", __func__); rc = -ERESTARTSYS; } 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; MM_DBG("\n"); switch (cmd) { case AUDIO_GET_MVS_CONFIG: { struct msm_audio_mvs_config config; MM_DBG("GET_MVS_CONFIG mvs_mode %d rate_type %d\n", config.mvs_mode, config.rate_type); 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 MM_ERR("Config copy failed %d\n", rc); break; } case AUDIO_SET_MVS_CONFIG: { struct msm_audio_mvs_config config; MM_DBG("IOCTL SET_MVS_CONFIG\n"); 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 { MM_ERR("Set confg called in state %d\n", audio->state); rc = -EPERM; } mutex_unlock(&audio->lock); } else { MM_ERR("Config copy failed %d\n", rc); } break; } case AUDIO_START: { MM_DBG("IOCTL START\n"); 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 { MM_ERR("Start called in invalid state %d\n", audio->state); rc = -EPERM; } mutex_unlock(&audio->lock); break; } case AUDIO_STOP: { MM_DBG("IOCTL STOP\n"); mutex_lock(&audio->lock); if (audio->state == AUDIO_MVS_STARTED) { rc = audio_mvs_stop(audio); } else { MM_ERR("Stop called in invalid state %d\n", audio->state); rc = -EPERM; } mutex_unlock(&audio->lock); break; } default: { MM_ERR("Unknown IOCTL %d\n", cmd); } } return rc; } static int audio_mvs_open(struct inode *inode, struct file *file) { int rc = 0; MM_DBG("\n"); mutex_lock(&audio_mvs_info.lock); if (audio_mvs_info.state != AUDIO_MVS_CLOSED) { MM_ERR("MVS driver exists, state %d\n", audio_mvs_info.state); rc = -EBUSY; mutex_unlock(&audio_mvs_info.lock); goto done; } mutex_unlock(&audio_mvs_info.lock); audio_mvs_info.rpc_endpt = msm_rpc_connect_compatible(MVS_PROG, MVS_VERS_COMP_VER2, MSM_RPC_UNINTERRUPTIBLE); if (IS_ERR(audio_mvs_info.rpc_endpt)) { MM_ERR("MVS RPC connect failed ver 0x%x\n", MVS_VERS_COMP_VER2); audio_mvs_info.rpc_endpt = msm_rpc_connect_compatible(MVS_PROG, MVS_VERS_COMP_VER3, MSM_RPC_UNINTERRUPTIBLE); if (IS_ERR(audio_mvs_info.rpc_endpt)) { MM_ERR("MVS RPC connect failed ver 0x%x\n", MVS_VERS_COMP_VER3); } else { MM_DBG("MVS RPC connect succeeded ver 0x%x\n", MVS_VERS_COMP_VER3); audio_mvs_info.rpc_prog = MVS_PROG; audio_mvs_info.rpc_ver = MVS_VERS_COMP_VER3; } } else { MM_DBG("MVS RPC connect succeeded ver 0x%x\n", MVS_VERS_COMP_VER2); audio_mvs_info.rpc_prog = MVS_PROG; audio_mvs_info.rpc_ver = MVS_VERS_COMP_VER2; } audio_mvs_info.task = kthread_run(audio_mvs_thread, &audio_mvs_info, "audio_mvs"); if (IS_ERR(audio_mvs_info.task)) { MM_ERR("MVS thread create failed\n"); 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; } mutex_lock(&audio_mvs_info.lock); 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 { MM_ERR("MVS thread and RPC end point do not exist\n"); rc = -ENODEV; } mutex_unlock(&audio_mvs_info.lock); done: 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) { 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.in_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); init_completion(&audio_mvs_info.complete); wake_lock_init(&audio_mvs_info.suspend_lock, WAKE_LOCK_SUSPEND, "audio_mvs_suspend"); pm_qos_add_request(&audio_mvs_info.pm_qos_req, PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); return misc_register(&audio_mvs_misc); } static void __exit audio_mvs_exit(void) { MM_DBG("\n"); wake_lock_destroy(&audio_mvs_info.suspend_lock); misc_deregister(&audio_mvs_misc); } module_init(audio_mvs_init); module_exit(audio_mvs_exit); MODULE_DESCRIPTION("MSM MVS driver"); MODULE_LICENSE("GPL v2");