blob: e20bf5beb355078d92a13f93ae99adce43f12c3a [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
2 * Copyright (C) 2009 Google, Inc.
3 * Copyright (C) 2009 HTC Corporation
Duy Truong790f06d2013-02-13 16:38:12 -08004 * Copyright (c) 2010, The Linux Foundation. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07005 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
17#include <linux/slab.h>
18#include <linux/fs.h>
19#include <linux/module.h>
20#include <linux/miscdevice.h>
21#include <linux/mutex.h>
22#include <linux/sched.h>
23#include <linux/wait.h>
24#include <linux/uaccess.h>
25
26#include <linux/msm_audio.h>
27#include <linux/msm_audio_amrnb.h>
28#include <mach/msm_qdsp6_audio.h>
29#include "dal_audio_format.h"
30#include <mach/debug_mm.h>
31
32struct amrnb {
33 struct mutex lock;
34 struct msm_audio_amrnb_enc_config_v2 cfg;
35 struct msm_audio_stream_config str_cfg;
36 struct audio_client *audio_client;
37 struct msm_voicerec_mode voicerec_mode;
38};
39
40
41static long q6_amrnb_in_ioctl(struct file *file, unsigned int cmd,
42 unsigned long arg)
43{
44 struct amrnb *amrnb = file->private_data;
45 int rc = 0;
46
47 mutex_lock(&amrnb->lock);
48 switch (cmd) {
49 case AUDIO_SET_VOLUME:
50 pr_debug("[%s:%s] SET_VOLUME\n", __MM_FILE__, __func__);
51 break;
52 case AUDIO_GET_STATS:
53 {
54 struct msm_audio_stats stats;
55 pr_debug("[%s:%s] GET_STATS\n", __MM_FILE__, __func__);
56 memset(&stats, 0, sizeof(stats));
57 if (copy_to_user((void *) arg, &stats, sizeof(stats)))
58 return -EFAULT;
59 return 0;
60 }
61 case AUDIO_START:
62 {
63 uint32_t acdb_id;
64 pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__);
65 if (arg == 0) {
66 acdb_id = 0;
67 } else {
68 if (copy_from_user(&acdb_id, (void *) arg,
69 sizeof(acdb_id))) {
70 rc = -EFAULT;
71 break;
72 }
73 }
74 if (amrnb->audio_client) {
75 rc = -EBUSY;
76 pr_err("[%s:%s] active session already existing\n",
77 __MM_FILE__, __func__);
78 break;
79 } else {
80 amrnb->audio_client = q6audio_open_amrnb(
81 amrnb->str_cfg.buffer_size,
82 amrnb->cfg.band_mode,
83 amrnb->cfg.dtx_enable,
84 amrnb->voicerec_mode.rec_mode,
85 acdb_id);
86 if (!amrnb->audio_client) {
87 pr_err("[%s:%s] amrnb open session failed\n",
88 __MM_FILE__, __func__);
89 kfree(amrnb);
90 rc = -ENOMEM;
91 break;
92 }
93 }
94 break;
95 }
96 case AUDIO_STOP:
97 pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__);
98 break;
99 case AUDIO_FLUSH:
100 break;
101 case AUDIO_SET_INCALL: {
102 pr_debug("[%s:%s] SET_INCALL\n", __MM_FILE__, __func__);
103 if (copy_from_user(&amrnb->voicerec_mode,
104 (void *)arg, sizeof(struct msm_voicerec_mode)))
105 rc = -EFAULT;
106
107 if (amrnb->voicerec_mode.rec_mode != AUDIO_FLAG_READ
108 && amrnb->voicerec_mode.rec_mode !=
109 AUDIO_FLAG_INCALL_MIXED) {
110 amrnb->voicerec_mode.rec_mode = AUDIO_FLAG_READ;
111 pr_err("[%s:%s] Invalid rec_mode\n", __MM_FILE__,
112 __func__);
113 rc = -EINVAL;
114 }
115 break;
116 }
117 case AUDIO_GET_STREAM_CONFIG:
118 if (copy_to_user((void *)arg, &amrnb->str_cfg,
119 sizeof(struct msm_audio_stream_config)))
120 rc = -EFAULT;
121 pr_debug("[%s:%s] GET_STREAM_CONFIG: buffsz=%d, buffcnt = %d\n",
122 __MM_FILE__, __func__, amrnb->str_cfg.buffer_size,
123 amrnb->str_cfg.buffer_count);
124 break;
125 case AUDIO_SET_STREAM_CONFIG:
126 if (copy_from_user(&amrnb->str_cfg, (void *)arg,
127 sizeof(struct msm_audio_stream_config))) {
128 rc = -EFAULT;
129 break;
130 }
131 pr_debug("[%s:%s] SET_STREAM_CONFIG: buffsz=%d, buffcnt = %d\n",
132 __MM_FILE__, __func__, amrnb->str_cfg.buffer_size,
133 amrnb->str_cfg.buffer_count);
134
135 if (amrnb->str_cfg.buffer_size < 768) {
136 pr_err("[%s:%s] Buffer size too small\n", __MM_FILE__,
137 __func__);
138 rc = -EINVAL;
139 break;
140 }
141
142 if (amrnb->str_cfg.buffer_count != 2)
143 pr_info("[%s:%s] Buffer count set to 2\n", __MM_FILE__,
144 __func__);
145 break;
146 case AUDIO_SET_AMRNB_ENC_CONFIG:
147 if (copy_from_user(&amrnb->cfg, (void *) arg,
148 sizeof(struct msm_audio_amrnb_enc_config_v2)))
149 rc = -EFAULT;
150 pr_debug("[%s:%s] SET_AMRNB_ENC_CONFIG\n", __MM_FILE__,
151 __func__);
152 break;
153 case AUDIO_GET_AMRNB_ENC_CONFIG:
154 if (copy_to_user((void *) arg, &amrnb->cfg,
155 sizeof(struct msm_audio_amrnb_enc_config_v2)))
156 rc = -EFAULT;
157 pr_debug("[%s:%s] GET_AMRNB_ENC_CONFIG\n", __MM_FILE__,
158 __func__);
159 break;
160
161 default:
162 rc = -EINVAL;
163 }
164
165 mutex_unlock(&amrnb->lock);
166 pr_debug("[%s:%s] rc= %d\n", __MM_FILE__, __func__, rc);
167 return rc;
168}
169
170static int q6_amrnb_in_open(struct inode *inode, struct file *file)
171{
172 struct amrnb *amrnb;
173
174 pr_info("[%s:%s] open\n", __MM_FILE__, __func__);
175 amrnb = kmalloc(sizeof(struct amrnb), GFP_KERNEL);
176 if (amrnb == NULL) {
177 pr_err("[%s:%s] Could not allocate memory for amrnb driver\n",
178 __MM_FILE__, __func__);
179 return -ENOMEM;
180 }
181
182 mutex_init(&amrnb->lock);
183 file->private_data = amrnb;
184 amrnb->audio_client = NULL;
185 amrnb->str_cfg.buffer_size = 768;
186 amrnb->str_cfg.buffer_count = 2;
187 amrnb->cfg.band_mode = 7;
188 amrnb->cfg.dtx_enable = 3;
189 amrnb->cfg.frame_format = ADSP_AUDIO_FORMAT_AMRNB_FS;
190 amrnb->voicerec_mode.rec_mode = AUDIO_FLAG_READ;
191
192 return 0;
193}
194
195static ssize_t q6_amrnb_in_read(struct file *file, char __user *buf,
196 size_t count, loff_t *pos)
197{
198 struct audio_client *ac;
199 struct audio_buffer *ab;
200 const char __user *start = buf;
201 struct amrnb *amrnb = file->private_data;
202 int xfer = 0;
203 int res;
204
205 pr_debug("[%s:%s] count = %d\n", __MM_FILE__, __func__, count);
206 mutex_lock(&amrnb->lock);
207 ac = amrnb->audio_client;
208 if (!ac) {
209 res = -ENODEV;
210 goto fail;
211 }
212 while (count > xfer) {
213 ab = ac->buf + ac->cpu_buf;
214
215 if (ab->used)
216 wait_event(ac->wait, (ab->used == 0));
217
218 pr_debug("[%s:%s] ab->data = %p, cpu_buf = %d\n", __MM_FILE__,
219 __func__, ab->data, ac->cpu_buf);
220 xfer = ab->actual_size;
221
222 if (copy_to_user(buf, ab->data, xfer)) {
223 pr_err("[%s:%s] copy_to_user failed\n",
224 __MM_FILE__, __func__);
225 res = -EFAULT;
226 goto fail;
227 }
228
229 buf += xfer;
230 count -= xfer;
231
232 ab->used = 1;
233 q6audio_read(ac, ab);
234 ac->cpu_buf ^= 1;
235 }
236
237 res = buf - start;
238fail:
239 mutex_unlock(&amrnb->lock);
240
241 return res;
242}
243
244static int q6_amrnb_in_release(struct inode *inode, struct file *file)
245{
246 int rc = 0;
247 struct amrnb *amrnb = file->private_data;
248
249 mutex_lock(&amrnb->lock);
250 if (amrnb->audio_client)
251 rc = q6audio_close(amrnb->audio_client);
252 mutex_unlock(&amrnb->lock);
253 kfree(amrnb);
254 pr_info("[%s:%s] release\n", __MM_FILE__, __func__);
255 return rc;
256}
257
258static const struct file_operations q6_amrnb_in_fops = {
259 .owner = THIS_MODULE,
260 .open = q6_amrnb_in_open,
261 .read = q6_amrnb_in_read,
262 .release = q6_amrnb_in_release,
263 .unlocked_ioctl = q6_amrnb_in_ioctl,
264};
265
266struct miscdevice q6_amrnb_in_misc = {
267 .minor = MISC_DYNAMIC_MINOR,
268 .name = "msm_amr_in",
269 .fops = &q6_amrnb_in_fops,
270};
271
272static int __init q6_amrnb_in_init(void)
273{
274 return misc_register(&q6_amrnb_in_misc);
275}
276
277device_initcall(q6_amrnb_in_init);