blob: c895fa78831542330cc6d8909683db1ff5c8419f [file] [log] [blame]
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +05301/* Copyright (c) 2014, 2016-2017 The Linux Foundation. All rights reserved.
2 *
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#include <linux/slab.h>
14#include <linux/fs.h>
15#include <linux/module.h>
16#include <linux/miscdevice.h>
17#include <linux/uaccess.h>
18#include <linux/mutex.h>
19#include <linux/msm_ion.h>
Laxminath Kasam605b42f2017-08-01 22:02:15 +053020#include <dsp/msm_audio_ion.h>
21#include <dsp/audio_calibration.h>
22#include <dsp/audio_cal_utils.h>
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +053023
24struct audio_cal_client_info {
25 struct list_head list;
26 struct audio_cal_callbacks *callbacks;
27};
28
29struct audio_cal_info {
30 struct mutex common_lock;
31 struct mutex cal_mutex[MAX_CAL_TYPES];
32 struct list_head client_info[MAX_CAL_TYPES];
33 int ref_count;
34};
35
36static struct audio_cal_info audio_cal;
37
38
39static bool callbacks_are_equal(struct audio_cal_callbacks *callback1,
40 struct audio_cal_callbacks *callback2)
41{
42 bool ret = true;
43 struct audio_cal_callbacks *call1 = callback1;
44 struct audio_cal_callbacks *call2 = callback2;
45
46 pr_debug("%s\n", __func__);
47
48 if ((call1 == NULL) && (call2 == NULL))
49 ret = true;
50 else if ((call1 == NULL) || (call2 == NULL))
51 ret = false;
52 else if ((call1->alloc != call2->alloc) ||
53 (call1->dealloc != call2->dealloc) ||
54 (call1->pre_cal != call2->pre_cal) ||
55 (call1->set_cal != call2->set_cal) ||
56 (call1->get_cal != call2->get_cal) ||
57 (call1->post_cal != call2->post_cal))
58 ret = false;
59 return ret;
60}
61
62int audio_cal_deregister(int num_cal_types,
63 struct audio_cal_reg *reg_data)
64{
65 int ret = 0;
66 int i = 0;
67 struct list_head *ptr, *next;
68 struct audio_cal_client_info *client_info_node = NULL;
69
70 pr_debug("%s\n", __func__);
71
72 if (reg_data == NULL) {
73 pr_err("%s: reg_data is NULL!\n", __func__);
74 ret = -EINVAL;
75 goto done;
76 } else if ((num_cal_types <= 0) ||
77 (num_cal_types > MAX_CAL_TYPES)) {
78 pr_err("%s: num_cal_types of %d is Invalid!\n",
79 __func__, num_cal_types);
80 ret = -EINVAL;
81 goto done;
82 }
83
84 for (; i < num_cal_types; i++) {
85 if ((reg_data[i].cal_type < 0) ||
86 (reg_data[i].cal_type >= MAX_CAL_TYPES)) {
87 pr_err("%s: cal type %d at index %d is Invalid!\n",
88 __func__, reg_data[i].cal_type, i);
89 ret = -EINVAL;
90 continue;
91 }
92
93 mutex_lock(&audio_cal.cal_mutex[reg_data[i].cal_type]);
94 list_for_each_safe(ptr, next,
95 &audio_cal.client_info[reg_data[i].cal_type]) {
96
97 client_info_node = list_entry(ptr,
98 struct audio_cal_client_info, list);
99 if (callbacks_are_equal(client_info_node->callbacks,
100 &reg_data[i].callbacks)) {
101 list_del(&client_info_node->list);
102 kfree(client_info_node->callbacks);
103 client_info_node->callbacks = NULL;
104 kfree(client_info_node);
105 client_info_node = NULL;
106 break;
107 }
108 }
109 mutex_unlock(&audio_cal.cal_mutex[reg_data[i].cal_type]);
110 }
111done:
112 return ret;
113}
114
115
116int audio_cal_register(int num_cal_types,
117 struct audio_cal_reg *reg_data)
118{
119 int ret = 0;
120 int i = 0;
121 struct audio_cal_client_info *client_info_node = NULL;
122 struct audio_cal_callbacks *callback_node = NULL;
123
124 pr_debug("%s\n", __func__);
125
126 if (reg_data == NULL) {
127 pr_err("%s: callbacks are NULL!\n", __func__);
128 ret = -EINVAL;
129 goto done;
130 } else if ((num_cal_types <= 0) ||
131 (num_cal_types > MAX_CAL_TYPES)) {
132 pr_err("%s: num_cal_types of %d is Invalid!\n",
133 __func__, num_cal_types);
134 ret = -EINVAL;
135 goto done;
136 }
137
138 for (; i < num_cal_types; i++) {
139 if ((reg_data[i].cal_type < 0) ||
140 (reg_data[i].cal_type >= MAX_CAL_TYPES)) {
141 pr_err("%s: cal type %d at index %d is Invalid!\n",
142 __func__, reg_data[i].cal_type, i);
143 ret = -EINVAL;
144 goto err;
145 }
146
147 client_info_node = kmalloc(sizeof(*client_info_node),
148 GFP_KERNEL);
149 if (client_info_node == NULL) {
150 ret = -ENOMEM;
151 goto err;
152 }
153 INIT_LIST_HEAD(&client_info_node->list);
154
155 callback_node = kmalloc(sizeof(*callback_node),
156 GFP_KERNEL);
157 if (callback_node == NULL) {
158 ret = -ENOMEM;
159 goto err;
160 }
161
162 memcpy(callback_node, &reg_data[i].callbacks,
163 sizeof(*callback_node));
164 client_info_node->callbacks = callback_node;
165
166 mutex_lock(&audio_cal.cal_mutex[reg_data[i].cal_type]);
167 list_add_tail(&client_info_node->list,
168 &audio_cal.client_info[reg_data[i].cal_type]);
169 mutex_unlock(&audio_cal.cal_mutex[reg_data[i].cal_type]);
170 }
171done:
172 return ret;
173err:
174 audio_cal_deregister(num_cal_types, reg_data);
175 return ret;
176}
177
178static int call_allocs(int32_t cal_type,
179 size_t cal_type_size, void *data)
180{
181 int ret = 0;
182 int ret2 = 0;
183 struct list_head *ptr, *next;
184 struct audio_cal_client_info *client_info_node = NULL;
185
186 pr_debug("%s\n", __func__);
187
188 list_for_each_safe(ptr, next,
189 &audio_cal.client_info[cal_type]) {
190
191 client_info_node = list_entry(ptr,
192 struct audio_cal_client_info, list);
193
194 if (client_info_node->callbacks->alloc == NULL)
195 continue;
196
197 ret2 = client_info_node->callbacks->
198 alloc(cal_type, cal_type_size, data);
199 if (ret2 < 0) {
200 pr_err("%s: alloc failed!\n", __func__);
201 ret = ret2;
202 }
203 }
204 return ret;
205}
206
207static int call_deallocs(int32_t cal_type,
208 size_t cal_type_size, void *data)
209{
210 int ret = 0;
211 int ret2 = 0;
212 struct list_head *ptr, *next;
213 struct audio_cal_client_info *client_info_node = NULL;
214
215 pr_debug("%s cal type %d\n", __func__, cal_type);
216
217 list_for_each_safe(ptr, next,
218 &audio_cal.client_info[cal_type]) {
219
220 client_info_node = list_entry(ptr,
221 struct audio_cal_client_info, list);
222
223 if (client_info_node->callbacks->dealloc == NULL)
224 continue;
225
226 ret2 = client_info_node->callbacks->
227 dealloc(cal_type, cal_type_size, data);
228 if (ret2 < 0) {
229 pr_err("%s: dealloc failed!\n", __func__);
230 ret = ret2;
231 }
232 }
233 return ret;
234}
235
236static int call_pre_cals(int32_t cal_type,
237 size_t cal_type_size, void *data)
238{
239 int ret = 0;
240 int ret2 = 0;
241 struct list_head *ptr, *next;
242 struct audio_cal_client_info *client_info_node = NULL;
243
244 pr_debug("%s cal type %d\n", __func__, cal_type);
245
246 list_for_each_safe(ptr, next,
247 &audio_cal.client_info[cal_type]) {
248
249 client_info_node = list_entry(ptr,
250 struct audio_cal_client_info, list);
251
252 if (client_info_node->callbacks->pre_cal == NULL)
253 continue;
254
255 ret2 = client_info_node->callbacks->
256 pre_cal(cal_type, cal_type_size, data);
257 if (ret2 < 0) {
258 pr_err("%s: pre_cal failed!\n", __func__);
259 ret = ret2;
260 }
261 }
262 return ret;
263}
264
265static int call_post_cals(int32_t cal_type,
266 size_t cal_type_size, void *data)
267{
268 int ret = 0;
269 int ret2 = 0;
270 struct list_head *ptr, *next;
271 struct audio_cal_client_info *client_info_node = NULL;
272
273 pr_debug("%s cal type %d\n", __func__, cal_type);
274
275 list_for_each_safe(ptr, next,
276 &audio_cal.client_info[cal_type]) {
277
278 client_info_node = list_entry(ptr,
279 struct audio_cal_client_info, list);
280
281 if (client_info_node->callbacks->post_cal == NULL)
282 continue;
283
284 ret2 = client_info_node->callbacks->
285 post_cal(cal_type, cal_type_size, data);
286 if (ret2 < 0) {
287 pr_err("%s: post_cal failed!\n", __func__);
288 ret = ret2;
289 }
290 }
291 return ret;
292}
293
294static int call_set_cals(int32_t cal_type,
295 size_t cal_type_size, void *data)
296{
297 int ret = 0;
298 int ret2 = 0;
299 struct list_head *ptr, *next;
300 struct audio_cal_client_info *client_info_node = NULL;
301
302 pr_debug("%s cal type %d\n", __func__, cal_type);
303
304 list_for_each_safe(ptr, next,
305 &audio_cal.client_info[cal_type]) {
306
307 client_info_node = list_entry(ptr,
308 struct audio_cal_client_info, list);
309
310 if (client_info_node->callbacks->set_cal == NULL)
311 continue;
312
313 ret2 = client_info_node->callbacks->
314 set_cal(cal_type, cal_type_size, data);
315 if (ret2 < 0) {
316 pr_err("%s: set_cal failed!\n", __func__);
317 ret = ret2;
318 }
319 }
320 return ret;
321}
322
323static int call_get_cals(int32_t cal_type,
324 size_t cal_type_size, void *data)
325{
326 int ret = 0;
327 int ret2 = 0;
328 struct list_head *ptr, *next;
329 struct audio_cal_client_info *client_info_node = NULL;
330
331 pr_debug("%s cal type %d\n", __func__, cal_type);
332
333 list_for_each_safe(ptr, next,
334 &audio_cal.client_info[cal_type]) {
335
336 client_info_node = list_entry(ptr,
337 struct audio_cal_client_info, list);
338
339 if (client_info_node->callbacks->get_cal == NULL)
340 continue;
341
342 ret2 = client_info_node->callbacks->
343 get_cal(cal_type, cal_type_size, data);
344 if (ret2 < 0) {
345 pr_err("%s: get_cal failed!\n", __func__);
346 ret = ret2;
347 }
348 }
349 return ret;
350}
351
352static int audio_cal_open(struct inode *inode, struct file *f)
353{
354 int ret = 0;
355
356 pr_debug("%s\n", __func__);
357
358 mutex_lock(&audio_cal.common_lock);
359 audio_cal.ref_count++;
360 mutex_unlock(&audio_cal.common_lock);
361
362 return ret;
363}
364
365static void dealloc_all_clients(void)
366{
367 int i = 0;
368 struct audio_cal_type_dealloc dealloc_data;
369
370 pr_debug("%s\n", __func__);
371
372 dealloc_data.cal_hdr.version = VERSION_0_0;
373 dealloc_data.cal_hdr.buffer_number = ALL_CAL_BLOCKS;
374 dealloc_data.cal_data.mem_handle = -1;
375
376 for (; i < MAX_CAL_TYPES; i++)
377 call_deallocs(i, sizeof(dealloc_data), &dealloc_data);
378}
379
380static int audio_cal_release(struct inode *inode, struct file *f)
381{
382 int ret = 0;
383
384 pr_debug("%s\n", __func__);
385
386 mutex_lock(&audio_cal.common_lock);
387 audio_cal.ref_count--;
388 if (audio_cal.ref_count <= 0) {
389 audio_cal.ref_count = 0;
390 dealloc_all_clients();
391 }
392 mutex_unlock(&audio_cal.common_lock);
393
394 return ret;
395}
396
397static long audio_cal_shared_ioctl(struct file *file, unsigned int cmd,
398 void __user *arg)
399{
400 int ret = 0;
401 int32_t size;
402 struct audio_cal_basic *data = NULL;
403
404 pr_debug("%s\n", __func__);
405
406 switch (cmd) {
407 case AUDIO_ALLOCATE_CALIBRATION:
408 case AUDIO_DEALLOCATE_CALIBRATION:
409 case AUDIO_PREPARE_CALIBRATION:
410 case AUDIO_SET_CALIBRATION:
411 case AUDIO_GET_CALIBRATION:
412 case AUDIO_POST_CALIBRATION:
413 break;
414 default:
415 pr_err("%s: ioctl not found!\n", __func__);
416 ret = -EFAULT;
417 goto done;
418 }
419
420 if (copy_from_user(&size, (void *)arg, sizeof(size))) {
421 pr_err("%s: Could not copy size value from user\n", __func__);
422 ret = -EFAULT;
423 goto done;
424 } else if ((size < sizeof(struct audio_cal_basic))
425 || (size > MAX_IOCTL_CMD_SIZE)) {
426 pr_err("%s: Invalid size sent to driver: %d, max size is %d, min size is %zd\n",
427 __func__, size, MAX_IOCTL_CMD_SIZE,
428 sizeof(struct audio_cal_basic));
429 ret = -EINVAL;
430 goto done;
431 }
432
433 data = kmalloc(size, GFP_KERNEL);
434 if (data == NULL) {
435 ret = -ENOMEM;
436 goto done;
437 } else if (copy_from_user(data, (void *)arg, size)) {
438 pr_err("%s: Could not copy data from user\n",
439 __func__);
440 ret = -EFAULT;
441 goto done;
442 } else if ((data->hdr.cal_type < 0) ||
443 (data->hdr.cal_type >= MAX_CAL_TYPES)) {
444 pr_err("%s: cal type %d is Invalid!\n",
445 __func__, data->hdr.cal_type);
446 ret = -EINVAL;
447 goto done;
448 } else if ((data->hdr.cal_type_size <
449 sizeof(struct audio_cal_type_basic)) ||
450 (data->hdr.cal_type_size >
451 get_user_cal_type_size(data->hdr.cal_type))) {
452 pr_err("%s: cal type size %d is Invalid! Max is %zd!\n",
453 __func__, data->hdr.cal_type_size,
454 get_user_cal_type_size(data->hdr.cal_type));
455 ret = -EINVAL;
456 goto done;
457 } else if (data->cal_type.cal_hdr.buffer_number < 0) {
458 pr_err("%s: cal type %d Invalid buffer number %d!\n",
459 __func__, data->hdr.cal_type,
460 data->cal_type.cal_hdr.buffer_number);
461 ret = -EINVAL;
462 goto done;
Asish Bhattacharya84f7f732017-07-25 16:29:27 +0530463 } else if ((data->hdr.cal_type_size + sizeof(data->hdr)) > size) {
464 pr_err("%s: cal type hdr size %zd + cal type size %d is greater than user buffer size %d\n",
465 __func__, sizeof(data->hdr), data->hdr.cal_type_size,
466 size);
467 ret = -EFAULT;
468 goto done;
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530469 }
470
471
472 mutex_lock(&audio_cal.cal_mutex[data->hdr.cal_type]);
473
474 switch (cmd) {
475 case AUDIO_ALLOCATE_CALIBRATION:
476 ret = call_allocs(data->hdr.cal_type,
477 data->hdr.cal_type_size, &data->cal_type);
478 break;
479 case AUDIO_DEALLOCATE_CALIBRATION:
480 ret = call_deallocs(data->hdr.cal_type,
481 data->hdr.cal_type_size, &data->cal_type);
482 break;
483 case AUDIO_PREPARE_CALIBRATION:
484 ret = call_pre_cals(data->hdr.cal_type,
485 data->hdr.cal_type_size, &data->cal_type);
486 break;
487 case AUDIO_SET_CALIBRATION:
488 ret = call_set_cals(data->hdr.cal_type,
489 data->hdr.cal_type_size, &data->cal_type);
490 break;
491 case AUDIO_GET_CALIBRATION:
492 ret = call_get_cals(data->hdr.cal_type,
493 data->hdr.cal_type_size, &data->cal_type);
494 break;
495 case AUDIO_POST_CALIBRATION:
496 ret = call_post_cals(data->hdr.cal_type,
497 data->hdr.cal_type_size, &data->cal_type);
498 break;
499 }
500
501 if (cmd == AUDIO_GET_CALIBRATION) {
502 if (data->hdr.cal_type_size == 0)
503 goto unlock;
504 if (data == NULL)
505 goto unlock;
Asish Bhattacharya84f7f732017-07-25 16:29:27 +0530506 if (copy_to_user(arg, data,
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530507 sizeof(data->hdr) + data->hdr.cal_type_size)) {
508 pr_err("%s: Could not copy cal type to user\n",
509 __func__);
510 ret = -EFAULT;
511 goto unlock;
512 }
513 }
514
515unlock:
516 mutex_unlock(&audio_cal.cal_mutex[data->hdr.cal_type]);
517done:
518 kfree(data);
519 return ret;
520}
521
522static long audio_cal_ioctl(struct file *f,
523 unsigned int cmd, unsigned long arg)
524{
525 return audio_cal_shared_ioctl(f, cmd, (void __user *)arg);
526}
527
528#ifdef CONFIG_COMPAT
529
530#define AUDIO_ALLOCATE_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \
531 200, compat_uptr_t)
532#define AUDIO_DEALLOCATE_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \
533 201, compat_uptr_t)
534#define AUDIO_PREPARE_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \
535 202, compat_uptr_t)
536#define AUDIO_SET_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \
537 203, compat_uptr_t)
538#define AUDIO_GET_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \
539 204, compat_uptr_t)
540#define AUDIO_POST_CALIBRATION32 _IOWR(CAL_IOCTL_MAGIC, \
541 205, compat_uptr_t)
542
543static long audio_cal_compat_ioctl(struct file *f,
544 unsigned int cmd, unsigned long arg)
545{
546 unsigned int cmd64;
547 int ret = 0;
548
549 switch (cmd) {
550 case AUDIO_ALLOCATE_CALIBRATION32:
551 cmd64 = AUDIO_ALLOCATE_CALIBRATION;
552 break;
553 case AUDIO_DEALLOCATE_CALIBRATION32:
554 cmd64 = AUDIO_DEALLOCATE_CALIBRATION;
555 break;
556 case AUDIO_PREPARE_CALIBRATION32:
557 cmd64 = AUDIO_PREPARE_CALIBRATION;
558 break;
559 case AUDIO_SET_CALIBRATION32:
560 cmd64 = AUDIO_SET_CALIBRATION;
561 break;
562 case AUDIO_GET_CALIBRATION32:
563 cmd64 = AUDIO_GET_CALIBRATION;
564 break;
565 case AUDIO_POST_CALIBRATION32:
566 cmd64 = AUDIO_POST_CALIBRATION;
567 break;
568 default:
569 pr_err("%s: ioctl not found!\n", __func__);
570 ret = -EFAULT;
571 goto done;
572 }
573
574 ret = audio_cal_shared_ioctl(f, cmd64, compat_ptr(arg));
575done:
576 return ret;
577}
578#endif
579
580static const struct file_operations audio_cal_fops = {
581 .owner = THIS_MODULE,
582 .open = audio_cal_open,
583 .release = audio_cal_release,
584 .unlocked_ioctl = audio_cal_ioctl,
585#ifdef CONFIG_COMPAT
586 .compat_ioctl = audio_cal_compat_ioctl,
587#endif
588};
589
590struct miscdevice audio_cal_misc = {
591 .minor = MISC_DYNAMIC_MINOR,
592 .name = "msm_audio_cal",
593 .fops = &audio_cal_fops,
594};
595
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530596int __init audio_cal_init(void)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530597{
598 int i = 0;
599
600 pr_debug("%s\n", __func__);
601
602 memset(&audio_cal, 0, sizeof(audio_cal));
603 mutex_init(&audio_cal.common_lock);
604 for (; i < MAX_CAL_TYPES; i++) {
605 INIT_LIST_HEAD(&audio_cal.client_info[i]);
606 mutex_init(&audio_cal.cal_mutex[i]);
607 }
608
609 return misc_register(&audio_cal_misc);
610}
611
Asish Bhattacharya5faacb32017-12-04 17:23:15 +0530612void audio_cal_exit(void)
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530613{
614 int i = 0;
615 struct list_head *ptr, *next;
616 struct audio_cal_client_info *client_info_node;
617
618 for (; i < MAX_CAL_TYPES; i++) {
619 list_for_each_safe(ptr, next,
620 &audio_cal.client_info[i]) {
621 client_info_node = list_entry(ptr,
622 struct audio_cal_client_info, list);
623 list_del(&client_info_node->list);
624 kfree(client_info_node->callbacks);
625 client_info_node->callbacks = NULL;
626 kfree(client_info_node);
627 client_info_node = NULL;
628 }
629 }
Laxminath Kasam8b1366a2017-10-05 01:44:16 +0530630 misc_deregister(&audio_cal_misc);
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530631}
632
Asish Bhattacharya8e2277f2017-07-20 18:31:55 +0530633
634MODULE_DESCRIPTION("SoC QDSP6v2 Audio Calibration driver");
635MODULE_LICENSE("GPL v2");