Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2009 Google, Inc. |
| 3 | * Copyright (C) 2009 HTC Corporation |
Duy Truong | 790f06d | 2013-02-13 16:38:12 -0800 | [diff] [blame] | 4 | * Copyright (c) 2010, The Linux Foundation. All rights reserved. |
Bryan Huntsman | 3f2bc4d | 2011-08-16 17:27:22 -0700 | [diff] [blame] | 5 | * |
| 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 | #include <linux/kthread.h> |
| 26 | #include <linux/time.h> |
| 27 | #include <linux/wait.h> |
| 28 | |
| 29 | #include <linux/msm_audio_qcp.h> |
| 30 | #include <mach/msm_qdsp6_audio.h> |
| 31 | #include "dal_audio_format.h" |
| 32 | #include <mach/debug_mm.h> |
| 33 | |
| 34 | #define EVRC_FC_BUFF_CNT 10 |
| 35 | #define EVRC_READ_TIMEOUT 2000 |
| 36 | struct evrc_fc_buff { |
| 37 | struct mutex lock; |
| 38 | int empty; |
| 39 | void *data; |
| 40 | int size; |
| 41 | int actual_size; |
| 42 | }; |
| 43 | |
| 44 | struct evrc_fc { |
| 45 | struct task_struct *task; |
| 46 | wait_queue_head_t fc_wq; |
| 47 | struct evrc_fc_buff fc_buff[EVRC_FC_BUFF_CNT]; |
| 48 | int buff_index; |
| 49 | }; |
| 50 | |
| 51 | struct evrc { |
| 52 | struct mutex lock; |
| 53 | struct msm_audio_evrc_enc_config cfg; |
| 54 | struct msm_audio_stream_config str_cfg; |
| 55 | struct audio_client *audio_client; |
| 56 | struct msm_voicerec_mode voicerec_mode; |
| 57 | struct evrc_fc *evrc_fc; |
| 58 | }; |
| 59 | |
| 60 | |
| 61 | static int q6_evrc_flowcontrol(void *data) |
| 62 | { |
| 63 | struct audio_client *ac; |
| 64 | struct audio_buffer *ab; |
| 65 | struct evrc *evrc = data; |
| 66 | int buff_index = 0; |
| 67 | int xfer = 0; |
| 68 | struct evrc_fc *fc; |
| 69 | |
| 70 | |
| 71 | ac = evrc->audio_client; |
| 72 | fc = evrc->evrc_fc; |
| 73 | if (!ac) { |
| 74 | pr_err("[%s:%s] audio_client is NULL\n", __MM_FILE__, __func__); |
| 75 | return 0; |
| 76 | } |
| 77 | |
| 78 | while (!kthread_should_stop()) { |
| 79 | ab = ac->buf + ac->cpu_buf; |
| 80 | if (ab->used) |
| 81 | wait_event(ac->wait, (ab->used == 0)); |
| 82 | pr_debug("[%s:%s] ab->data = %p, cpu_buf = %d\n", __MM_FILE__, |
| 83 | __func__, ab->data, ac->cpu_buf); |
| 84 | xfer = ab->actual_size; |
| 85 | |
| 86 | |
| 87 | mutex_lock(&(fc->fc_buff[buff_index].lock)); |
| 88 | if (!fc->fc_buff[buff_index].empty) { |
| 89 | pr_err("[%s:%s] flow control buffer[%d] not read!\n", |
| 90 | __MM_FILE__, __func__, buff_index); |
| 91 | } |
| 92 | |
| 93 | if (fc->fc_buff[buff_index].size < xfer) { |
| 94 | pr_err("[%s:%s] buffer %d too small\n", __MM_FILE__, |
| 95 | __func__, buff_index); |
| 96 | memcpy(fc->fc_buff[buff_index].data, ab->data, |
| 97 | fc->fc_buff[buff_index].size); |
| 98 | fc->fc_buff[buff_index].empty = 0; |
| 99 | fc->fc_buff[buff_index].actual_size = |
| 100 | fc->fc_buff[buff_index].size; |
| 101 | } else { |
| 102 | memcpy(fc->fc_buff[buff_index].data, ab->data, xfer); |
| 103 | fc->fc_buff[buff_index].empty = 0; |
| 104 | fc->fc_buff[buff_index].actual_size = xfer; |
| 105 | } |
| 106 | mutex_unlock(&(fc->fc_buff[buff_index].lock)); |
| 107 | /*wake up client, if any*/ |
| 108 | wake_up(&fc->fc_wq); |
| 109 | |
| 110 | buff_index++; |
| 111 | if (buff_index >= EVRC_FC_BUFF_CNT) |
| 112 | buff_index = 0; |
| 113 | |
| 114 | ab->used = 1; |
| 115 | |
| 116 | q6audio_read(ac, ab); |
| 117 | ac->cpu_buf ^= 1; |
| 118 | } |
| 119 | |
| 120 | return 0; |
| 121 | } |
| 122 | static long q6_evrc_in_ioctl(struct file *file, unsigned int cmd, |
| 123 | unsigned long arg) |
| 124 | { |
| 125 | struct evrc *evrc = file->private_data; |
| 126 | int rc = 0; |
| 127 | int i = 0; |
| 128 | struct evrc_fc *fc; |
| 129 | int size = 0; |
| 130 | |
| 131 | mutex_lock(&evrc->lock); |
| 132 | switch (cmd) { |
| 133 | case AUDIO_SET_VOLUME: |
| 134 | pr_debug("[%s:%s] SET_VOLUME\n", __MM_FILE__, __func__); |
| 135 | break; |
| 136 | case AUDIO_GET_STATS: |
| 137 | { |
| 138 | struct msm_audio_stats stats; |
| 139 | pr_debug("[%s:%s] GET_STATS\n", __MM_FILE__, __func__); |
| 140 | memset(&stats, 0, sizeof(stats)); |
| 141 | if (copy_to_user((void *) arg, &stats, sizeof(stats))) |
| 142 | return -EFAULT; |
| 143 | return 0; |
| 144 | } |
| 145 | case AUDIO_START: |
| 146 | { |
| 147 | uint32_t acdb_id; |
| 148 | pr_debug("[%s:%s] AUDIO_START\n", __MM_FILE__, __func__); |
| 149 | if (arg == 0) { |
| 150 | acdb_id = 0; |
| 151 | } else { |
| 152 | if (copy_from_user(&acdb_id, (void *) arg, |
| 153 | sizeof(acdb_id))) { |
| 154 | rc = -EFAULT; |
| 155 | break; |
| 156 | } |
| 157 | } |
| 158 | if (evrc->audio_client) { |
| 159 | rc = -EBUSY; |
| 160 | pr_err("[%s:%s] active session already existing\n", |
| 161 | __MM_FILE__, __func__); |
| 162 | break; |
| 163 | } else { |
| 164 | evrc->audio_client = q6audio_open_qcp( |
| 165 | evrc->str_cfg.buffer_size, |
| 166 | evrc->cfg.min_bit_rate, |
| 167 | evrc->cfg.max_bit_rate, |
| 168 | evrc->voicerec_mode.rec_mode, |
| 169 | ADSP_AUDIO_FORMAT_EVRC_FS, |
| 170 | acdb_id); |
| 171 | |
| 172 | if (!evrc->audio_client) { |
| 173 | pr_err("[%s:%s] evrc open session failed\n", |
| 174 | __MM_FILE__, __func__); |
| 175 | kfree(evrc); |
| 176 | rc = -ENOMEM; |
| 177 | break; |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | /*allocate flow control buffers*/ |
| 182 | fc = evrc->evrc_fc; |
| 183 | size = evrc->str_cfg.buffer_size; |
| 184 | for (i = 0; i < EVRC_FC_BUFF_CNT; ++i) { |
| 185 | mutex_init(&(fc->fc_buff[i].lock)); |
| 186 | fc->fc_buff[i].empty = 1; |
| 187 | fc->fc_buff[i].data = kmalloc(size, GFP_KERNEL); |
| 188 | if (fc->fc_buff[i].data == NULL) { |
| 189 | pr_err("[%s:%s] No memory for FC buffers\n", |
| 190 | __MM_FILE__, __func__); |
| 191 | rc = -ENOMEM; |
| 192 | goto fc_fail; |
| 193 | } |
| 194 | fc->fc_buff[i].size = size; |
| 195 | fc->fc_buff[i].actual_size = 0; |
| 196 | } |
| 197 | |
| 198 | /*create flow control thread*/ |
| 199 | fc->task = kthread_run(q6_evrc_flowcontrol, |
| 200 | evrc, "evrc_flowcontrol"); |
| 201 | if (IS_ERR(fc->task)) { |
| 202 | rc = PTR_ERR(fc->task); |
| 203 | pr_err("[%s:%s] error creating flow control thread\n", |
| 204 | __MM_FILE__, __func__); |
| 205 | goto fc_fail; |
| 206 | } |
| 207 | break; |
| 208 | fc_fail: |
| 209 | /*free flow control buffers*/ |
| 210 | --i; |
| 211 | for (; i >= 0; i--) { |
| 212 | kfree(fc->fc_buff[i].data); |
| 213 | fc->fc_buff[i].data = NULL; |
| 214 | } |
| 215 | break; |
| 216 | } |
| 217 | case AUDIO_STOP: |
| 218 | pr_debug("[%s:%s] AUDIO_STOP\n", __MM_FILE__, __func__); |
| 219 | break; |
| 220 | case AUDIO_FLUSH: |
| 221 | break; |
| 222 | case AUDIO_SET_INCALL: { |
| 223 | pr_debug("[%s:%s] SET_INCALL\n", __MM_FILE__, __func__); |
| 224 | if (copy_from_user(&evrc->voicerec_mode, |
| 225 | (void *)arg, sizeof(struct msm_voicerec_mode))) |
| 226 | rc = -EFAULT; |
| 227 | |
| 228 | if (evrc->voicerec_mode.rec_mode != AUDIO_FLAG_READ |
| 229 | && evrc->voicerec_mode.rec_mode != |
| 230 | AUDIO_FLAG_INCALL_MIXED) { |
| 231 | evrc->voicerec_mode.rec_mode = AUDIO_FLAG_READ; |
| 232 | pr_err("[%s:%s] Invalid rec_mode\n", __MM_FILE__, |
| 233 | __func__); |
| 234 | rc = -EINVAL; |
| 235 | } |
| 236 | break; |
| 237 | } |
| 238 | case AUDIO_GET_STREAM_CONFIG: |
| 239 | if (copy_to_user((void *)arg, &evrc->str_cfg, |
| 240 | sizeof(struct msm_audio_stream_config))) |
| 241 | rc = -EFAULT; |
| 242 | |
| 243 | pr_debug("[%s:%s] GET_STREAM_CONFIG: buffsz=%d, buffcnt=%d\n", |
| 244 | __MM_FILE__, __func__, evrc->str_cfg.buffer_size, |
| 245 | evrc->str_cfg.buffer_count); |
| 246 | break; |
| 247 | case AUDIO_SET_STREAM_CONFIG: |
| 248 | if (copy_from_user(&evrc->str_cfg, (void *)arg, |
| 249 | sizeof(struct msm_audio_stream_config))) { |
| 250 | rc = -EFAULT; |
| 251 | break; |
| 252 | } |
| 253 | |
| 254 | pr_debug("[%s:%s] SET_STREAM_CONFIG: buffsz=%d, buffcnt=%d\n", |
| 255 | __MM_FILE__, __func__, evrc->str_cfg.buffer_size, |
| 256 | evrc->str_cfg.buffer_count); |
| 257 | |
| 258 | if (evrc->str_cfg.buffer_size < 23) { |
| 259 | pr_err("[%s:%s] Buffer size too small\n", __MM_FILE__, |
| 260 | __func__); |
| 261 | rc = -EINVAL; |
| 262 | break; |
| 263 | } |
| 264 | |
| 265 | if (evrc->str_cfg.buffer_count != 2) |
| 266 | pr_info("[%s:%s] Buffer count set to 2\n", __MM_FILE__, |
| 267 | __func__); |
| 268 | break; |
| 269 | case AUDIO_SET_EVRC_ENC_CONFIG: |
| 270 | if (copy_from_user(&evrc->cfg, (void *) arg, |
| 271 | sizeof(struct msm_audio_evrc_enc_config))) |
| 272 | rc = -EFAULT; |
| 273 | pr_debug("[%s:%s] SET_EVRC_ENC_CONFIG\n", __MM_FILE__, |
| 274 | __func__); |
| 275 | |
| 276 | if (evrc->cfg.min_bit_rate > 4 || evrc->cfg.min_bit_rate < 1) { |
| 277 | pr_err("[%s:%s] invalid min bitrate\n", __MM_FILE__, |
| 278 | __func__); |
| 279 | rc = -EINVAL; |
| 280 | } |
| 281 | if (evrc->cfg.max_bit_rate > 4 || evrc->cfg.max_bit_rate < 1) { |
| 282 | pr_err("[%s:%s] invalid max bitrate\n", __MM_FILE__, |
| 283 | __func__); |
| 284 | rc = -EINVAL; |
| 285 | } |
| 286 | break; |
| 287 | case AUDIO_GET_EVRC_ENC_CONFIG: |
| 288 | if (copy_to_user((void *) arg, &evrc->cfg, |
| 289 | sizeof(struct msm_audio_evrc_enc_config))) |
| 290 | rc = -EFAULT; |
| 291 | pr_debug("[%s:%s] GET_EVRC_ENC_CONFIG\n", __MM_FILE__, |
| 292 | __func__); |
| 293 | break; |
| 294 | |
| 295 | default: |
| 296 | rc = -EINVAL; |
| 297 | } |
| 298 | |
| 299 | mutex_unlock(&evrc->lock); |
| 300 | pr_debug("[%s:%s] rc = %d\n", __MM_FILE__, __func__, rc); |
| 301 | return rc; |
| 302 | } |
| 303 | |
| 304 | static int q6_evrc_in_open(struct inode *inode, struct file *file) |
| 305 | { |
| 306 | struct evrc *evrc; |
| 307 | struct evrc_fc *fc; |
| 308 | int i; |
| 309 | |
| 310 | pr_info("[%s:%s] open\n", __MM_FILE__, __func__); |
| 311 | evrc = kmalloc(sizeof(struct evrc), GFP_KERNEL); |
| 312 | if (evrc == NULL) { |
| 313 | pr_err("[%s:%s] Could not allocate memory for evrc driver\n", |
| 314 | __MM_FILE__, __func__); |
| 315 | return -ENOMEM; |
| 316 | } |
| 317 | |
| 318 | mutex_init(&evrc->lock); |
| 319 | file->private_data = evrc; |
| 320 | evrc->audio_client = NULL; |
| 321 | evrc->str_cfg.buffer_size = 23; |
| 322 | evrc->str_cfg.buffer_count = 2; |
| 323 | evrc->cfg.cdma_rate = CDMA_RATE_FULL; |
| 324 | evrc->cfg.min_bit_rate = 1; |
| 325 | evrc->cfg.max_bit_rate = 4; |
| 326 | evrc->voicerec_mode.rec_mode = AUDIO_FLAG_READ; |
| 327 | |
| 328 | evrc->evrc_fc = kmalloc(sizeof(struct evrc_fc), GFP_KERNEL); |
| 329 | if (evrc->evrc_fc == NULL) { |
| 330 | pr_err("[%s:%s] Could not allocate memory for evrc_fc\n", |
| 331 | __MM_FILE__, __func__); |
| 332 | kfree(evrc); |
| 333 | return -ENOMEM; |
| 334 | } |
| 335 | fc = evrc->evrc_fc; |
| 336 | fc->task = NULL; |
| 337 | fc->buff_index = 0; |
| 338 | for (i = 0; i < EVRC_FC_BUFF_CNT; ++i) { |
| 339 | fc->fc_buff[i].data = NULL; |
| 340 | fc->fc_buff[i].size = 0; |
| 341 | fc->fc_buff[i].actual_size = 0; |
| 342 | } |
| 343 | /*initialize wait queue head*/ |
| 344 | init_waitqueue_head(&fc->fc_wq); |
| 345 | return 0; |
| 346 | } |
| 347 | |
| 348 | static ssize_t q6_evrc_in_read(struct file *file, char __user *buf, |
| 349 | size_t count, loff_t *pos) |
| 350 | { |
| 351 | struct audio_client *ac; |
| 352 | const char __user *start = buf; |
| 353 | struct evrc *evrc = file->private_data; |
| 354 | struct evrc_fc *fc; |
| 355 | int xfer = 0; |
| 356 | int res = 0; |
| 357 | |
| 358 | pr_debug("[%s:%s] count = %d\n", __MM_FILE__, __func__, count); |
| 359 | mutex_lock(&evrc->lock); |
| 360 | ac = evrc->audio_client; |
| 361 | if (!ac) { |
| 362 | res = -ENODEV; |
| 363 | goto fail; |
| 364 | } |
| 365 | fc = evrc->evrc_fc; |
| 366 | while (count > xfer) { |
| 367 | /*wait for buffer to full*/ |
| 368 | if (fc->fc_buff[fc->buff_index].empty != 0) { |
| 369 | res = wait_event_interruptible_timeout(fc->fc_wq, |
| 370 | (fc->fc_buff[fc->buff_index].empty == 0), |
| 371 | msecs_to_jiffies(EVRC_READ_TIMEOUT)); |
| 372 | |
| 373 | pr_debug("[%s:%s] buff_index = %d\n", __MM_FILE__, |
| 374 | __func__, fc->buff_index); |
| 375 | if (res == 0) { |
| 376 | pr_err("[%s:%s] Timeout!\n", __MM_FILE__, |
| 377 | __func__); |
| 378 | res = -ETIMEDOUT; |
| 379 | goto fail; |
| 380 | } else if (res < 0) { |
| 381 | pr_err("[%s:%s] Returning on Interrupt\n", |
| 382 | __MM_FILE__, __func__); |
| 383 | goto fail; |
| 384 | } |
| 385 | } |
| 386 | /*lock the buffer*/ |
| 387 | mutex_lock(&(fc->fc_buff[fc->buff_index].lock)); |
| 388 | xfer = fc->fc_buff[fc->buff_index].actual_size; |
| 389 | |
| 390 | if (xfer > count) { |
| 391 | mutex_unlock(&(fc->fc_buff[fc->buff_index].lock)); |
| 392 | pr_err("[%s:%s] read failed! byte count too small\n", |
| 393 | __MM_FILE__, __func__); |
| 394 | res = -EINVAL; |
| 395 | goto fail; |
| 396 | } |
| 397 | |
| 398 | if (copy_to_user(buf, fc->fc_buff[fc->buff_index].data, xfer)) { |
| 399 | mutex_unlock(&(fc->fc_buff[fc->buff_index].lock)); |
| 400 | pr_err("[%s:%s] copy_to_user failed at index %d\n", |
| 401 | __MM_FILE__, __func__, fc->buff_index); |
| 402 | res = -EFAULT; |
| 403 | goto fail; |
| 404 | } |
| 405 | buf += xfer; |
| 406 | count -= xfer; |
| 407 | |
| 408 | fc->fc_buff[fc->buff_index].empty = 1; |
| 409 | fc->fc_buff[fc->buff_index].actual_size = 0; |
| 410 | |
| 411 | mutex_unlock(&(fc->fc_buff[fc->buff_index].lock)); |
| 412 | ++(fc->buff_index); |
| 413 | if (fc->buff_index >= EVRC_FC_BUFF_CNT) |
| 414 | fc->buff_index = 0; |
| 415 | } |
| 416 | res = buf - start; |
| 417 | |
| 418 | fail: |
| 419 | mutex_unlock(&evrc->lock); |
| 420 | |
| 421 | return res; |
| 422 | } |
| 423 | |
| 424 | static int q6_evrc_in_release(struct inode *inode, struct file *file) |
| 425 | { |
| 426 | int rc = 0; |
| 427 | struct evrc *evrc = file->private_data; |
| 428 | int i = 0; |
| 429 | struct evrc_fc *fc; |
| 430 | |
| 431 | mutex_lock(&evrc->lock); |
| 432 | fc = evrc->evrc_fc; |
| 433 | kthread_stop(fc->task); |
| 434 | fc->task = NULL; |
| 435 | /*free flow control buffers*/ |
| 436 | for (i = 0; i < EVRC_FC_BUFF_CNT; ++i) { |
| 437 | kfree(fc->fc_buff[i].data); |
| 438 | fc->fc_buff[i].data = NULL; |
| 439 | } |
| 440 | kfree(fc); |
| 441 | if (evrc->audio_client) |
| 442 | rc = q6audio_close(evrc->audio_client); |
| 443 | mutex_unlock(&evrc->lock); |
| 444 | kfree(evrc); |
| 445 | pr_info("[%s:%s] release\n", __MM_FILE__, __func__); |
| 446 | return rc; |
| 447 | } |
| 448 | |
| 449 | static const struct file_operations q6_evrc_in_fops = { |
| 450 | .owner = THIS_MODULE, |
| 451 | .open = q6_evrc_in_open, |
| 452 | .read = q6_evrc_in_read, |
| 453 | .release = q6_evrc_in_release, |
| 454 | .unlocked_ioctl = q6_evrc_in_ioctl, |
| 455 | }; |
| 456 | |
| 457 | struct miscdevice q6_evrc_in_misc = { |
| 458 | .minor = MISC_DYNAMIC_MINOR, |
| 459 | .name = "msm_evrc_in", |
| 460 | .fops = &q6_evrc_in_fops, |
| 461 | }; |
| 462 | |
| 463 | static int __init q6_evrc_in_init(void) |
| 464 | { |
| 465 | return misc_register(&q6_evrc_in_misc); |
| 466 | } |
| 467 | |
| 468 | device_initcall(q6_evrc_in_init); |