blob: a73defd988f7367638a3ea4a3340a2dda9d2f540 [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2011, The Linux Foundation. All rights reserved.
Manish Dewangan881ef4c2011-09-14 11:58:57 +05302 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/debugfs.h>
15#include <linux/delay.h>
16#include <linux/fs.h>
17#include <linux/kthread.h>
18#include <linux/miscdevice.h>
19#include <linux/module.h>
20#include <linux/wakelock.h>
21#include <linux/slab.h>
22#include <linux/uaccess.h>
23
24#include <mach/debug_mm.h>
25#include <mach/msm_rpcrouter.h>
26
27#include "audmgr_new.h"
28
29#define VOICELOOPBACK_PROG 0x300000B8
30#define VOICELOOP_VERS 0x00010001
31
32#define VOICELOOPBACK_START_PROC 2
33#define VOICELOOPBACK_STOP_PROC 3
34
35#define RPC_TYPE_REQUEST 0
36#define RPC_TYPE_REPLY 1
37
38#define RPC_STATUS_FAILURE 0
39#define RPC_STATUS_SUCCESS 1
40#define RPC_STATUS_REJECT 1
41
42#define RPC_COMMON_HDR_SZ (sizeof(uint32_t) * 2)
43#define RPC_REQUEST_HDR_SZ (sizeof(struct rpc_request_hdr))
44#define RPC_REPLY_HDR_SZ (sizeof(uint32_t) * 3)
45
46#define MAX_LEN 32
47
48struct audio {
49 struct msm_rpc_endpoint *rpc_endpt;
50 uint32_t rpc_prog;
51 uint32_t rpc_ver;
52 uint32_t rpc_status;
53 struct audmgr audmgr;
54
55 struct dentry *dentry;
56
57 struct mutex lock;
58
59 struct task_struct *task;
60
61 wait_queue_head_t wait;
62 int enabled;
63 int thread_exit;
64};
65
66static struct audio the_audio;
67
68static int audio_voice_loopback_thread(void *data)
69{
70 struct audio *audio = data;
71 struct rpc_request_hdr *rpc_hdr = NULL;
72 int rpc_hdr_len;
73
74 MM_DBG("\n");
75
76 while (!kthread_should_stop()) {
77 if (rpc_hdr != NULL) {
78 kfree(rpc_hdr);
79 rpc_hdr = NULL;
80 }
81
82 if (audio->thread_exit)
83 break;
84
85 rpc_hdr_len = msm_rpc_read(audio->rpc_endpt,
86 (void **) &rpc_hdr,
87 -1,
88 -1);
89 if (rpc_hdr_len < 0) {
90 MM_ERR("RPC read failed %d\n", rpc_hdr_len);
91 break;
92 } else if (rpc_hdr_len < RPC_COMMON_HDR_SZ) {
93 continue;
94 } else {
95 uint32_t rpc_type = be32_to_cpu(rpc_hdr->type);
96 if (rpc_type == RPC_TYPE_REPLY) {
97 struct rpc_reply_hdr *rpc_reply =
98 (void *) rpc_hdr;
99 uint32_t reply_status;
100
101 reply_status =
102 be32_to_cpu(rpc_reply->reply_stat);
103
104 if (reply_status == RPC_ACCEPTSTAT_SUCCESS)
105 audio->rpc_status = \
106 RPC_STATUS_SUCCESS;
107 else {
108 audio->rpc_status = \
109 RPC_STATUS_REJECT;
110 MM_ERR("RPC reply status denied\n");
111 }
112 wake_up(&audio->wait);
113 } else {
114 MM_ERR("Unexpected RPC type %d\n", rpc_type);
115 }
116 }
117 }
118 kfree(rpc_hdr);
119 rpc_hdr = NULL;
120
121 MM_DBG("Audio Voice Looopback thread stopped\n");
122
123 return 0;
124}
125
126static int audio_voice_loopback_start(struct audio *audio)
127{
128 int rc = 0;
129 struct audmgr_config cfg;
130 struct rpc_request_hdr rpc_hdr;
131
132 MM_DBG("\n");
133
134 cfg.tx_rate = RPC_AUD_DEF_SAMPLE_RATE_8000;
135 cfg.rx_rate = RPC_AUD_DEF_SAMPLE_RATE_8000;
136 cfg.def_method = RPC_AUD_DEF_METHOD_VOICE;
137 cfg.codec = RPC_AUD_DEF_CODEC_VOC_CDMA;
138 cfg.snd_method = RPC_SND_METHOD_VOICE;
139 rc = audmgr_enable(&audio->audmgr, &cfg);
140 if (rc < 0) {
141 MM_ERR("audmgr open failed, freeing instance\n");
142 rc = -EINVAL;
143 goto done;
144 }
145
146 memset(&rpc_hdr, 0, sizeof(rpc_hdr));
147
148 msm_rpc_setup_req(&rpc_hdr,
149 audio->rpc_prog,
150 audio->rpc_ver,
151 VOICELOOPBACK_START_PROC);
152
153 audio->rpc_status = RPC_STATUS_FAILURE;
154 rc = msm_rpc_write(audio->rpc_endpt,
155 &rpc_hdr,
156 sizeof(rpc_hdr));
157 if (rc >= 0) {
158 rc = wait_event_timeout(audio->wait,
159 (audio->rpc_status != RPC_STATUS_FAILURE),
160 1 * HZ);
161 if (rc > 0) {
162 if (audio->rpc_status != RPC_STATUS_SUCCESS) {
163 MM_ERR("Start loopback failed %d\n", rc);
164 rc = -EBUSY;
165 } else {
166 rc = 0;
167 }
168 } else {
169 MM_ERR("Wait event for acquire failed %d\n", rc);
170 rc = -EBUSY;
171 }
172 } else {
173 audmgr_disable(&audio->audmgr);
174 MM_ERR("RPC write for start loopback failed %d\n", rc);
175 rc = -EBUSY;
176 }
177done:
178 return rc;
179}
180
181static int audio_voice_loopback_stop(struct audio *audio)
182{
183 int rc = 0;
184 struct rpc_request_hdr rpc_hdr;
185
186 MM_DBG("\n");
187
188 memset(&rpc_hdr, 0, sizeof(rpc_hdr));
189
190 msm_rpc_setup_req(&rpc_hdr,
191 audio->rpc_prog,
192 audio->rpc_ver,
193 VOICELOOPBACK_STOP_PROC);
194
195 audio->rpc_status = RPC_STATUS_FAILURE;
196 audio->thread_exit = 1;
197 rc = msm_rpc_write(audio->rpc_endpt,
198 &rpc_hdr,
199 sizeof(rpc_hdr));
200 if (rc >= 0) {
201
202 rc = wait_event_timeout(audio->wait,
203 (audio->rpc_status != RPC_STATUS_FAILURE),
204 1 * HZ);
205 if (rc > 0) {
206 MM_DBG("Wait event for release succeeded\n");
207 rc = 0;
208 } else {
209 MM_ERR("Wait event for release failed %d\n", rc);
210 }
211 } else {
212 MM_ERR("RPC write for release failed %d\n", rc);
213 }
214
215 audmgr_disable(&audio->audmgr);
216
217 return rc;
218}
219
220static int audio_voice_loopback_open(struct audio *audio_info)
221{
222 int rc = 0;
223
224 MM_DBG("\n");
225
226 rc = audmgr_open(&audio_info->audmgr);
227 if (rc) {
228 MM_ERR("audmgr open failed, freeing instance\n");
229 rc = -EINVAL;
230 goto done;
231 }
232
233 audio_info->rpc_endpt = msm_rpc_connect_compatible(VOICELOOPBACK_PROG,
234 VOICELOOP_VERS,
235 MSM_RPC_UNINTERRUPTIBLE);
236 if (IS_ERR(audio_info->rpc_endpt)) {
237 MM_ERR("VOICE LOOPBACK RPC connect\
238 failed ver 0x%x\n",
239 VOICELOOP_VERS);
240 rc = PTR_ERR(audio_info->rpc_endpt);
241 audio_info->rpc_endpt = NULL;
242 rc = -EINVAL;
243 } else {
244 MM_DBG("VOICE LOOPBACK connect succeeded ver 0x%x\n",
245 VOICELOOP_VERS);
246 audio_info->thread_exit = 0;
247 audio_info->task = kthread_run(audio_voice_loopback_thread,
248 audio_info,
249 "audio_voice_loopback");
250 if (IS_ERR(audio_info->task)) {
251 MM_ERR("voice loopback thread create failed\n");
252 rc = PTR_ERR(audio_info->task);
253 audio_info->task = NULL;
254 msm_rpc_close(audio_info->rpc_endpt);
255 audio_info->rpc_endpt = NULL;
256 rc = -EINVAL;
257 }
258 audio_info->rpc_prog = VOICELOOPBACK_PROG;
259 audio_info->rpc_ver = VOICELOOP_VERS;
260 }
261done:
262 return rc;
263}
264
265static int audio_voice_loopback_close(struct audio *audio_info)
266{
267 MM_DBG("\n");
268 msm_rpc_close(audio_info->rpc_endpt);
269 audio_info->rpc_endpt = NULL;
270 audmgr_close(&audio_info->audmgr);
271 audio_info->task = NULL;
272 return 0;
273}
274
275static ssize_t audio_voice_loopback_debug_write(struct file *file,
276 const char __user *buf,
277 size_t cnt, loff_t *ppos)
278{
279 char lbuf[MAX_LEN];
280 int rc = 0;
281
282 if (cnt > (MAX_LEN - 1))
283 return -EINVAL;
284
285 memset(&lbuf[0], 0, sizeof(lbuf));
286
287 rc = copy_from_user(lbuf, buf, cnt);
288 if (rc) {
289 MM_ERR("Unable to copy data from user space\n");
290 return -EFAULT;
291 }
292
293 lbuf[cnt] = '\0';
294
295 if (!strncmp(&lbuf[0], "1", cnt-1)) {
296 mutex_lock(&the_audio.lock);
297 if (!the_audio.enabled) {
298 rc = audio_voice_loopback_open(&the_audio);
299 if (!rc) {
300 rc = audio_voice_loopback_start(&the_audio);
301 if (rc < 0) {
302 the_audio.enabled = 0;
303 audio_voice_loopback_close(&the_audio);
304 } else {
305 the_audio.enabled = 1;
306 }
307 }
308 }
309 mutex_unlock(&the_audio.lock);
310 } else if (!strncmp(lbuf, "0", cnt-1)) {
311 mutex_lock(&the_audio.lock);
312 if (the_audio.enabled) {
313 audio_voice_loopback_stop(&the_audio);
314 audio_voice_loopback_close(&the_audio);
315 the_audio.enabled = 0;
316 }
317 mutex_unlock(&the_audio.lock);
318 } else {
319 rc = -EINVAL;
320 }
321
322 if (rc == 0) {
323 rc = cnt;
324 } else {
325 MM_INFO("rc = %d\n", rc);
326 MM_INFO("\nWrong command: Use =>\n");
327 MM_INFO("-------------------------\n");
328 MM_INFO("To Start Loopback:: echo \"1\">/sys/kernel/debug/\
329 voice_loopback\n");
330 MM_INFO("To Stop Loopback:: echo \"0\">/sys/kernel/debug/\
331 voice_loopback\n");
332 MM_INFO("------------------------\n");
333 }
334
335 return rc;
336}
337
338static ssize_t audio_voice_loopback_debug_open(struct inode *inode,
339 struct file *file)
340{
341 file->private_data = inode->i_private;
342 MM_DBG("Audio Voiceloop debugfs opened\n");
343 return 0;
344}
345
346static const struct file_operations voice_loopback_debug_fops = {
347 .write = audio_voice_loopback_debug_write,
348 .open = audio_voice_loopback_debug_open,
349};
350
351static int __init audio_init(void)
352{
353 int rc = 0;
354 memset(&the_audio, 0, sizeof(the_audio));
355
356 mutex_init(&the_audio.lock);
357
358 init_waitqueue_head(&the_audio.wait);
359
360 the_audio.dentry = debugfs_create_file("voice_loopback",
361 S_IFREG | S_IRUGO,
362 NULL,
363 NULL, &voice_loopback_debug_fops);
364 if (IS_ERR(the_audio.dentry))
365 MM_ERR("debugfs_create_file failed\n");
366
367 return rc;
368}
369late_initcall(audio_init);