synaptics_tcm_diagnostics.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. /*
  2. * Synaptics TCM touchscreen driver
  3. *
  4. * Copyright (C) 2017-2019 Synaptics Incorporated. All rights reserved.
  5. *
  6. * Copyright (C) 2017-2019 Scott Lin <[email protected]>
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
  19. * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
  20. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
  21. * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
  22. * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
  24. * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
  25. * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  26. * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
  27. * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
  28. * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
  29. * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
  30. * DOLLARS.
  31. */
  32. #include <linux/sched/signal.h>
  33. #include "synaptics_tcm_core.h"
  34. #define SYSFS_DIR_NAME "diagnostics"
  35. enum pingpong_state {
  36. PING = 0,
  37. PONG = 1,
  38. };
  39. struct diag_hcd {
  40. pid_t pid;
  41. unsigned char report_type;
  42. enum pingpong_state state;
  43. struct kobject *sysfs_dir;
  44. struct siginfo sigio;
  45. struct task_struct *task;
  46. struct syna_tcm_buffer ping;
  47. struct syna_tcm_buffer pong;
  48. struct syna_tcm_hcd *tcm_hcd;
  49. };
  50. DECLARE_COMPLETION(diag_remove_complete);
  51. static struct diag_hcd *diag_hcd;
  52. STORE_PROTOTYPE(diag, pid);
  53. SHOW_PROTOTYPE(diag, size);
  54. STORE_PROTOTYPE(diag, type);
  55. SHOW_PROTOTYPE(diag, rows);
  56. SHOW_PROTOTYPE(diag, cols);
  57. SHOW_PROTOTYPE(diag, hybrid);
  58. SHOW_PROTOTYPE(diag, buttons);
  59. static struct device_attribute *attrs[] = {
  60. ATTRIFY(pid),
  61. ATTRIFY(size),
  62. ATTRIFY(type),
  63. ATTRIFY(rows),
  64. ATTRIFY(cols),
  65. ATTRIFY(hybrid),
  66. ATTRIFY(buttons),
  67. };
  68. static ssize_t diag_sysfs_data_show(struct file *data_file,
  69. struct kobject *kobj, struct bin_attribute *attributes,
  70. char *buf, loff_t pos, size_t count);
  71. static struct bin_attribute bin_attr = {
  72. .attr = {
  73. .name = "data",
  74. .mode = 0444,
  75. },
  76. .size = 0,
  77. .read = diag_sysfs_data_show,
  78. };
  79. static ssize_t diag_sysfs_pid_store(struct device *dev,
  80. struct device_attribute *attr, const char *buf, size_t count)
  81. {
  82. int retval;
  83. unsigned int input;
  84. struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
  85. if (kstrtouint(buf, 10, &input))
  86. return -EINVAL;
  87. mutex_lock(&tcm_hcd->extif_mutex);
  88. diag_hcd->pid = input;
  89. if (diag_hcd->pid) {
  90. diag_hcd->task = pid_task(find_vpid(diag_hcd->pid),
  91. PIDTYPE_PID);
  92. if (!diag_hcd->task) {
  93. LOGE(tcm_hcd->pdev->dev.parent,
  94. "Failed to locate task\n");
  95. retval = -EINVAL;
  96. goto exit;
  97. }
  98. }
  99. retval = count;
  100. exit:
  101. mutex_unlock(&tcm_hcd->extif_mutex);
  102. return retval;
  103. }
  104. static ssize_t diag_sysfs_size_show(struct device *dev,
  105. struct device_attribute *attr, char *buf)
  106. {
  107. int retval;
  108. struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
  109. mutex_lock(&tcm_hcd->extif_mutex);
  110. if (diag_hcd->state == PING) {
  111. LOCK_BUFFER(diag_hcd->ping);
  112. retval = snprintf(buf, PAGE_SIZE,
  113. "%u\n",
  114. diag_hcd->ping.data_length);
  115. UNLOCK_BUFFER(diag_hcd->ping);
  116. } else {
  117. LOCK_BUFFER(diag_hcd->pong);
  118. retval = snprintf(buf, PAGE_SIZE,
  119. "%u\n",
  120. diag_hcd->pong.data_length);
  121. UNLOCK_BUFFER(diag_hcd->pong);
  122. }
  123. mutex_unlock(&tcm_hcd->extif_mutex);
  124. return retval;
  125. }
  126. static ssize_t diag_sysfs_type_store(struct device *dev,
  127. struct device_attribute *attr, const char *buf, size_t count)
  128. {
  129. unsigned int input;
  130. struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
  131. if (kstrtouint(buf, 10, &input))
  132. return -EINVAL;
  133. mutex_lock(&tcm_hcd->extif_mutex);
  134. diag_hcd->report_type = (unsigned char)input;
  135. mutex_unlock(&tcm_hcd->extif_mutex);
  136. return count;
  137. }
  138. static ssize_t diag_sysfs_rows_show(struct device *dev,
  139. struct device_attribute *attr, char *buf)
  140. {
  141. int retval;
  142. unsigned int rows;
  143. struct syna_tcm_app_info *app_info;
  144. struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
  145. mutex_lock(&tcm_hcd->extif_mutex);
  146. if (tcm_hcd->id_info.mode != MODE_APPLICATION ||
  147. tcm_hcd->app_status != APP_STATUS_OK) {
  148. retval = -ENODEV;
  149. goto exit;
  150. }
  151. app_info = &tcm_hcd->app_info;
  152. rows = le2_to_uint(app_info->num_of_image_rows);
  153. retval = snprintf(buf, PAGE_SIZE, "%u\n", rows);
  154. exit:
  155. mutex_unlock(&tcm_hcd->extif_mutex);
  156. return retval;
  157. }
  158. static ssize_t diag_sysfs_cols_show(struct device *dev,
  159. struct device_attribute *attr, char *buf)
  160. {
  161. int retval;
  162. unsigned int cols;
  163. struct syna_tcm_app_info *app_info;
  164. struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
  165. mutex_lock(&tcm_hcd->extif_mutex);
  166. if (tcm_hcd->id_info.mode != MODE_APPLICATION ||
  167. tcm_hcd->app_status != APP_STATUS_OK) {
  168. retval = -ENODEV;
  169. goto exit;
  170. }
  171. app_info = &tcm_hcd->app_info;
  172. cols = le2_to_uint(app_info->num_of_image_cols);
  173. retval = snprintf(buf, PAGE_SIZE, "%u\n", cols);
  174. exit:
  175. mutex_unlock(&tcm_hcd->extif_mutex);
  176. return retval;
  177. }
  178. static ssize_t diag_sysfs_hybrid_show(struct device *dev,
  179. struct device_attribute *attr, char *buf)
  180. {
  181. int retval;
  182. unsigned int hybrid;
  183. struct syna_tcm_app_info *app_info;
  184. struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
  185. mutex_lock(&tcm_hcd->extif_mutex);
  186. if (tcm_hcd->id_info.mode != MODE_APPLICATION ||
  187. tcm_hcd->app_status != APP_STATUS_OK) {
  188. retval = -ENODEV;
  189. goto exit;
  190. }
  191. app_info = &tcm_hcd->app_info;
  192. hybrid = le2_to_uint(app_info->has_hybrid_data);
  193. retval = snprintf(buf, PAGE_SIZE, "%u\n", hybrid);
  194. exit:
  195. mutex_unlock(&tcm_hcd->extif_mutex);
  196. return retval;
  197. }
  198. static ssize_t diag_sysfs_buttons_show(struct device *dev,
  199. struct device_attribute *attr, char *buf)
  200. {
  201. int retval;
  202. unsigned int buttons;
  203. struct syna_tcm_app_info *app_info;
  204. struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
  205. mutex_lock(&tcm_hcd->extif_mutex);
  206. if (tcm_hcd->id_info.mode != MODE_APPLICATION ||
  207. tcm_hcd->app_status != APP_STATUS_OK) {
  208. retval = -ENODEV;
  209. goto exit;
  210. }
  211. app_info = &tcm_hcd->app_info;
  212. buttons = le2_to_uint(app_info->num_of_buttons);
  213. retval = snprintf(buf, PAGE_SIZE, "%u\n", buttons);
  214. exit:
  215. mutex_unlock(&tcm_hcd->extif_mutex);
  216. return retval;
  217. }
  218. static ssize_t diag_sysfs_data_show(struct file *data_file,
  219. struct kobject *kobj, struct bin_attribute *attributes,
  220. char *buf, loff_t pos, size_t count)
  221. {
  222. int retval;
  223. unsigned int readlen;
  224. struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
  225. mutex_lock(&tcm_hcd->extif_mutex);
  226. retval = 0;
  227. if (diag_hcd->state == PING) {
  228. LOCK_BUFFER(diag_hcd->ping);
  229. if (diag_hcd->ping.data_length == 0) {
  230. readlen = 0;
  231. goto exit;
  232. }
  233. readlen = MIN(count, diag_hcd->ping.data_length - pos);
  234. if (diag_hcd->ping.data_length) {
  235. retval = secure_memcpy(buf,
  236. count,
  237. &diag_hcd->ping.buf[pos],
  238. diag_hcd->ping.buf_size - pos,
  239. readlen);
  240. }
  241. UNLOCK_BUFFER(diag_hcd->ping);
  242. } else {
  243. LOCK_BUFFER(diag_hcd->pong);
  244. if (diag_hcd->pong.data_length == 0) {
  245. readlen = 0;
  246. goto exit;
  247. }
  248. readlen = MIN(count, diag_hcd->pong.data_length - pos);
  249. if (diag_hcd->pong.data_length) {
  250. retval = secure_memcpy(buf,
  251. count,
  252. &diag_hcd->pong.buf[pos],
  253. diag_hcd->pong.buf_size - pos,
  254. readlen);
  255. }
  256. UNLOCK_BUFFER(diag_hcd->pong);
  257. }
  258. exit:
  259. if (retval < 0) {
  260. LOGE(tcm_hcd->pdev->dev.parent,
  261. "Failed to copy report data\n");
  262. } else {
  263. retval = readlen;
  264. }
  265. mutex_unlock(&tcm_hcd->extif_mutex);
  266. return retval;
  267. }
  268. static void diag_report(void)
  269. {
  270. int retval;
  271. static enum pingpong_state state = PING;
  272. struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd;
  273. if (state == PING) {
  274. LOCK_BUFFER(diag_hcd->ping);
  275. retval = syna_tcm_alloc_mem(tcm_hcd,
  276. &diag_hcd->ping,
  277. tcm_hcd->report.buffer.data_length);
  278. if (retval < 0) {
  279. LOGE(tcm_hcd->pdev->dev.parent,
  280. "Failed to allocate memory for ping.buf\n");
  281. UNLOCK_BUFFER(diag_hcd->ping);
  282. return;
  283. }
  284. retval = secure_memcpy(diag_hcd->ping.buf,
  285. diag_hcd->ping.buf_size,
  286. tcm_hcd->report.buffer.buf,
  287. tcm_hcd->report.buffer.buf_size,
  288. tcm_hcd->report.buffer.data_length);
  289. if (retval < 0) {
  290. LOGE(tcm_hcd->pdev->dev.parent,
  291. "Failed to copy report data\n");
  292. UNLOCK_BUFFER(diag_hcd->ping);
  293. return;
  294. }
  295. diag_hcd->ping.data_length = tcm_hcd->report.buffer.data_length;
  296. UNLOCK_BUFFER(diag_hcd->ping);
  297. diag_hcd->state = state;
  298. state = PONG;
  299. } else {
  300. LOCK_BUFFER(diag_hcd->pong);
  301. retval = syna_tcm_alloc_mem(tcm_hcd,
  302. &diag_hcd->pong,
  303. tcm_hcd->report.buffer.data_length);
  304. if (retval < 0) {
  305. LOGE(tcm_hcd->pdev->dev.parent,
  306. "Failed to allocate memory for pong.buf\n");
  307. UNLOCK_BUFFER(diag_hcd->pong);
  308. return;
  309. }
  310. retval = secure_memcpy(diag_hcd->pong.buf,
  311. diag_hcd->pong.buf_size,
  312. tcm_hcd->report.buffer.buf,
  313. tcm_hcd->report.buffer.buf_size,
  314. tcm_hcd->report.buffer.data_length);
  315. if (retval < 0) {
  316. LOGE(tcm_hcd->pdev->dev.parent,
  317. "Failed to copy report data\n");
  318. UNLOCK_BUFFER(diag_hcd->pong);
  319. return;
  320. }
  321. diag_hcd->pong.data_length = tcm_hcd->report.buffer.data_length;
  322. UNLOCK_BUFFER(diag_hcd->pong);
  323. diag_hcd->state = state;
  324. state = PING;
  325. }
  326. }
  327. static int diag_init(struct syna_tcm_hcd *tcm_hcd)
  328. {
  329. int retval;
  330. int idx;
  331. diag_hcd = kzalloc(sizeof(*diag_hcd), GFP_KERNEL);
  332. if (!diag_hcd) {
  333. LOGE(tcm_hcd->pdev->dev.parent,
  334. "Failed to allocate memory for diag_hcd\n");
  335. return -ENOMEM;
  336. }
  337. diag_hcd->tcm_hcd = tcm_hcd;
  338. diag_hcd->state = PING;
  339. INIT_BUFFER(diag_hcd->ping, false);
  340. INIT_BUFFER(diag_hcd->pong, false);
  341. memset(&diag_hcd->sigio, 0x00, sizeof(diag_hcd->sigio));
  342. diag_hcd->sigio.si_signo = SIGIO;
  343. diag_hcd->sigio.si_code = SI_USER;
  344. diag_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME,
  345. tcm_hcd->sysfs_dir);
  346. if (!diag_hcd->sysfs_dir) {
  347. LOGE(tcm_hcd->pdev->dev.parent,
  348. "Failed to create sysfs directory\n");
  349. retval = -EINVAL;
  350. goto err_sysfs_create_dir;
  351. }
  352. for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) {
  353. retval = sysfs_create_file(diag_hcd->sysfs_dir,
  354. &(*attrs[idx]).attr);
  355. if (retval < 0) {
  356. LOGE(tcm_hcd->pdev->dev.parent,
  357. "Failed to create sysfs file\n");
  358. goto err_sysfs_create_file;
  359. }
  360. }
  361. retval = sysfs_create_bin_file(diag_hcd->sysfs_dir, &bin_attr);
  362. if (retval < 0) {
  363. LOGE(tcm_hcd->pdev->dev.parent,
  364. "Failed to create sysfs bin file\n");
  365. goto err_sysfs_create_bin_file;
  366. }
  367. return 0;
  368. err_sysfs_create_bin_file:
  369. err_sysfs_create_file:
  370. for (idx--; idx >= 0; idx--)
  371. sysfs_remove_file(diag_hcd->sysfs_dir, &(*attrs[idx]).attr);
  372. kobject_put(diag_hcd->sysfs_dir);
  373. err_sysfs_create_dir:
  374. RELEASE_BUFFER(diag_hcd->pong);
  375. RELEASE_BUFFER(diag_hcd->ping);
  376. kfree(diag_hcd);
  377. diag_hcd = NULL;
  378. return retval;
  379. }
  380. static int diag_remove(struct syna_tcm_hcd *tcm_hcd)
  381. {
  382. int idx;
  383. if (!diag_hcd)
  384. goto exit;
  385. sysfs_remove_bin_file(diag_hcd->sysfs_dir, &bin_attr);
  386. for (idx = 0; idx < ARRAY_SIZE(attrs); idx++)
  387. sysfs_remove_file(diag_hcd->sysfs_dir, &(*attrs[idx]).attr);
  388. kobject_put(diag_hcd->sysfs_dir);
  389. RELEASE_BUFFER(diag_hcd->pong);
  390. RELEASE_BUFFER(diag_hcd->ping);
  391. kfree(diag_hcd);
  392. diag_hcd = NULL;
  393. exit:
  394. complete(&diag_remove_complete);
  395. return 0;
  396. }
  397. static int diag_syncbox(struct syna_tcm_hcd *tcm_hcd)
  398. {
  399. if (!diag_hcd)
  400. return 0;
  401. if (tcm_hcd->report.id == diag_hcd->report_type)
  402. diag_report();
  403. return 0;
  404. }
  405. static int diag_reset(struct syna_tcm_hcd *tcm_hcd)
  406. {
  407. int retval;
  408. if (!diag_hcd) {
  409. retval = diag_init(tcm_hcd);
  410. return retval;
  411. }
  412. return 0;
  413. }
  414. static struct syna_tcm_module_cb diag_module = {
  415. .type = TCM_DIAGNOSTICS,
  416. .init = diag_init,
  417. .remove = diag_remove,
  418. .syncbox = diag_syncbox,
  419. .asyncbox = NULL,
  420. .reset = diag_reset,
  421. .suspend = NULL,
  422. .resume = NULL,
  423. .early_suspend = NULL,
  424. };
  425. static int __init diag_module_init(void)
  426. {
  427. return syna_tcm_add_module(&diag_module, true);
  428. }
  429. static void __exit diag_module_exit(void)
  430. {
  431. syna_tcm_add_module(&diag_module, false);
  432. wait_for_completion(&diag_remove_complete);
  433. }
  434. module_init(diag_module_init);
  435. module_exit(diag_module_exit);
  436. MODULE_AUTHOR("Synaptics, Inc.");
  437. MODULE_DESCRIPTION("Synaptics TCM Diagnostics Module");
  438. MODULE_LICENSE("GPL v2");