/* Copyright (c) 2010, 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 "../dal.h" #include "dal_voice.h" #include struct voice_struct { struct dal_client *cvd; struct apr_command_pkt apr_pkt; struct completion compl; }; static struct voice_struct voice; static int cvd_send_response(void) { struct apr_command_pkt *pkt; uint16_t src_addr; uint16_t src_token; uint16_t dst_token; uint16_t dst_addr; pkt = &voice.apr_pkt; src_addr = pkt->dst_addr; dst_addr = pkt->src_addr; src_token = pkt->dst_token; dst_token = pkt->src_token; pkt->header &= ~APR_PKTV1_TYPE_MASK; pkt->header |= APR_SET_FIELD(APR_PKTV1_TYPE, APR_PKTV1_TYPE_EVENT_V); pkt->src_addr = src_addr; pkt->dst_addr = dst_addr; pkt->src_token = src_token; pkt->dst_token = dst_token; pkt->opcode = APR_IBASIC_RSP_RESULT; dal_call(voice.cvd, VOICE_OP_CONTROL, 5, pkt, sizeof(struct apr_command_pkt), pkt, sizeof(u32)); return 0; } static int cvd_process_voice_setup(void) { q6voice_setup(); cvd_send_response(); return 0; } static int cvd_process_voice_teardown(void) { q6voice_teardown(); cvd_send_response(); return 0; } static int cvd_process_set_network(void) { cvd_send_response(); return 0; } static int voice_thread(void *data) { while (!kthread_should_stop()) { wait_for_completion(&voice.compl); init_completion(&voice.compl); switch (voice.apr_pkt.opcode) { case APR_OP_CMD_CREATE: cvd_send_response(); break; case VOICE_OP_CMD_BRINGUP: cvd_process_voice_setup(); break; case APR_OP_CMD_DESTROY: cvd_send_response(); break; case VOICE_OP_CMD_TEARDOWN: cvd_process_voice_teardown(); break; case VOICE_OP_CMD_SET_NETWORK: cvd_process_set_network(); break; default: pr_err("[%s:%s] Undefined event\n", __MM_FILE__, __func__); } } return 0; } static void remote_cb_function(void *data, int len, void *cookie) { struct apr_command_pkt *apr = data + 2*sizeof(uint32_t); memcpy(&voice.apr_pkt, apr, sizeof(struct apr_command_pkt)); if (len <= 0) { pr_err("[%s:%s] unexpected event with length %d\n", __MM_FILE__, __func__, len); return; } pr_debug("[%s:%s] APR = %x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", __MM_FILE__, __func__, apr->header, apr->reserved1, apr->src_addr, apr->dst_addr, apr->ret_addr, apr->src_token, apr->dst_token, apr->ret_token, apr->context, apr->opcode); complete(&voice.compl); } static int __init voice_init(void) { int res = 0; struct task_struct *task; u32 tmp[2]; tmp[0] = sizeof(u32); tmp[1] = 0; voice.cvd = dal_attach(VOICE_DAL_DEVICE, VOICE_DAL_PORT, 0, remote_cb_function, 0); if (!voice.cvd) { pr_err("[%s:%s] audio_init: cannot attach to cvd\n", __MM_FILE__, __func__); res = -ENODEV; goto done; } if (check_version(voice.cvd, VOICE_DAL_VERSION) != 0) { pr_err("[%s:%s] Incompatible cvd version\n", __MM_FILE__, __func__); res = -ENODEV; goto done; } dal_call(voice.cvd, VOICE_OP_INIT, 5, tmp, sizeof(tmp), tmp, sizeof(u32)); init_completion(&voice.compl); task = kthread_run(voice_thread, &voice, "voice_thread"); if (IS_ERR(task)) { pr_err("[%s:%s] Cannot start the voice thread\n", __MM_FILE__, __func__); res = PTR_ERR(task); task = NULL; } else goto done; done: return res; } late_initcall(voice_init);