audio_calibration.c 15 KB

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