audio_calibration.c 15 KB

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