ssp_sensorhub.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. /*
  2. * Copyright (C) 2013, Samsung Electronics Co. Ltd. All Rights Reserved.
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. */
  15. #include "ssp.h"
  16. static int ssp_sensorhub_print_data(const char *func_name,
  17. const char *data, int length)
  18. {
  19. char buf[6];
  20. char *log_str;
  21. int log_size = strlen(func_name) + 2 + sizeof(buf) * length + 1;
  22. int i;
  23. log_str = kzalloc(log_size, GFP_ATOMIC);
  24. if (unlikely(!log_str)) {
  25. sensorhub_err("allocate memory for data log err");
  26. return -ENOMEM;
  27. }
  28. for (i = 0; i < length; i++) {
  29. if (i == 0) {
  30. strlcat(log_str, func_name, log_size);
  31. strlcat(log_str, ": ", log_size);
  32. } else {
  33. strlcat(log_str, ", ", log_size);
  34. }
  35. snprintf(buf, sizeof(buf), "%d", (signed char)data[i]);
  36. strlcat(log_str, buf, log_size);
  37. }
  38. pr_info("%s", log_str);
  39. kfree(log_str);
  40. return log_size;
  41. }
  42. static ssize_t ssp_sensorhub_write(struct file *file, const char __user *buf,
  43. size_t count, loff_t *pos)
  44. {
  45. struct ssp_sensorhub_data *hub_data
  46. = container_of(file->private_data,
  47. struct ssp_sensorhub_data, sensorhub_device);
  48. unsigned char instruction;
  49. int ret = 0;
  50. if (unlikely(count < 2)) {
  51. sensorhub_err("library data length err(%d)", count);
  52. return -EINVAL;
  53. }
  54. ssp_sensorhub_print_data(__func__, buf, count);
  55. if (unlikely(hub_data->ssp_data->bSspShutdown)) {
  56. sensorhub_err("stop sending library data(shutdown)");
  57. return -EBUSY;
  58. }
  59. if (buf[0] == MSG2SSP_INST_LIBRARY_REMOVE)
  60. instruction = REMOVE_LIBRARY;
  61. else if (buf[0] == MSG2SSP_INST_LIBRARY_ADD)
  62. instruction = ADD_LIBRARY;
  63. else if (buf[0] == MSG2SSP_INST_LIB_NOTI) {
  64. if (buf[2] == MSG2SSP_AP_STATUS_WAKEUP) {
  65. ret = ssp_send_cmd(hub_data->ssp_data, MSG2SSP_AP_STATUS_WAKEUP);
  66. enable_debug_timer(hub_data->ssp_data);
  67. if (ret != SUCCESS)
  68. pr_err("[SSP] : %s MSG2SSP_AP_STATUS_WAKEUP failed(%d)\n",
  69. __func__, ret);
  70. } else if (buf[2] == MSG2SSP_AP_STATUS_SLEEP) {
  71. disable_debug_timer(hub_data->ssp_data);
  72. ret = ssp_send_cmd(hub_data->ssp_data, MSG2SSP_AP_STATUS_SLEEP);
  73. if (ret != SUCCESS)
  74. pr_err("[SSP] : %s MSG2SSP_AP_STATUS_SLEEP failed(%d)\n",
  75. __func__, ret);
  76. } else if (buf[2] == MSG2SSP_AP_STATUS_POW_CONNECTED) {
  77. ret = ssp_send_cmd(hub_data->ssp_data, MSG2SSP_AP_STATUS_POW_CONNECTED);
  78. if (ret != SUCCESS)
  79. pr_err("[SSP] : %s MSG2SSP_AP_STATUS_POW_CONNECTED failed(%d)\n",
  80. __func__, ret);
  81. } else if (buf[2] == MSG2SSP_AP_STATUS_POW_DISCONNECTED) {
  82. ret = ssp_send_cmd(hub_data->ssp_data, MSG2SSP_AP_STATUS_POW_DISCONNECTED);
  83. if (ret != SUCCESS)
  84. pr_err("[SSP] : %s MSG2SSP_AP_STATUS_POW_DISCONNECTED failed(%d)\n",
  85. __func__, ret);
  86. } else if (buf[2] == MSG2SSP_AP_STATUS_CALL_IDLE) {
  87. ret = ssp_send_cmd(hub_data->ssp_data, MSG2SSP_AP_STATUS_CALL_IDLE);
  88. if (ret != SUCCESS)
  89. pr_err("[SSP] : %s MSG2SSP_AP_STATUS_CALL_IDLE failed(%d)\n",
  90. __func__, ret);
  91. } else if (buf[2] == MSG2SSP_AP_STATUS_CALL_ACTIVE) {
  92. ret = ssp_send_cmd(hub_data->ssp_data, MSG2SSP_AP_STATUS_CALL_ACTIVE);
  93. if (ret != SUCCESS)
  94. pr_err("[SSP] : %s MSG2SSP_AP_STATUS_CALL_ACTIVE failed(%d)\n",
  95. __func__, ret);
  96. } else
  97. pr_err("[SSP] : %s wrong MSG2SSP_INST_LIB_NOTI(%d)\n",
  98. __func__, buf[0]);
  99. return count;
  100. } else
  101. instruction = buf[0];
  102. ret = send_instruction(hub_data->ssp_data, instruction,
  103. (unsigned char)buf[1], (unsigned char *)(buf+2), count-2);
  104. if (unlikely(ret <= 0)) {
  105. sensorhub_err("send library data err(%d)", ret);
  106. /* i2c transfer fail */
  107. if (ret == ERROR)
  108. return -EIO;
  109. /* i2c transfer done but no ack from MCU */
  110. else if (ret == FAIL)
  111. return -EAGAIN;
  112. }
  113. return count;
  114. }
  115. static ssize_t ssp_sensorhub_read(struct file *file, char __user *buf,
  116. size_t count, loff_t *pos)
  117. {
  118. struct ssp_sensorhub_data *hub_data
  119. = container_of(file->private_data,
  120. struct ssp_sensorhub_data, sensorhub_device);
  121. int retries = MAX_DATA_COPY_TRY;
  122. int length = 0;
  123. int ret = 0;
  124. spin_lock_bh(&hub_data->sensorhub_lock);
  125. if (unlikely(list_empty(&hub_data->events_head.list))) {
  126. sensorhub_info("no library data");
  127. goto exit;
  128. }
  129. /* first in first out */
  130. hub_data->first_event
  131. = list_first_entry(&hub_data->events_head.list,
  132. struct sensorhub_event, list);
  133. length = hub_data->first_event->library_length;
  134. /* remove first event from the list */
  135. list_del(&hub_data->first_event->list);
  136. while (retries--) {
  137. ret = copy_to_user(buf,
  138. hub_data->first_event->library_data,
  139. hub_data->first_event->library_length);
  140. if (likely(!ret))
  141. break;
  142. }
  143. if (unlikely(ret)) {
  144. sensorhub_err("read library data err(%d)", ret);
  145. goto exit;
  146. }
  147. ssp_sensorhub_print_data(__func__,
  148. hub_data->first_event->library_data,
  149. hub_data->first_event->library_length);
  150. complete(&hub_data->sensorhub_completion);
  151. exit:
  152. spin_unlock_bh(&hub_data->sensorhub_lock);
  153. return ret ? -ret : length;
  154. }
  155. static long ssp_sensorhub_ioctl(struct file *file, unsigned int cmd,
  156. unsigned long arg)
  157. {
  158. struct ssp_sensorhub_data *hub_data
  159. = container_of(file->private_data,
  160. struct ssp_sensorhub_data, sensorhub_device);
  161. void __user *argp = (void __user *)arg;
  162. int retries = MAX_DATA_COPY_TRY;
  163. int length = hub_data->large_library_length;
  164. int ret = 0;
  165. switch (cmd) {
  166. case IOCTL_READ_LARGE_CONTEXT_DATA:
  167. if (unlikely(!hub_data->large_library_data
  168. || !hub_data->large_library_length)) {
  169. sensorhub_info("no large library data");
  170. return 0;
  171. }
  172. while (retries--) {
  173. ret = copy_to_user(argp,
  174. hub_data->large_library_data,
  175. hub_data->large_library_length);
  176. if (likely(!ret))
  177. break;
  178. }
  179. if (unlikely(ret)) {
  180. sensorhub_err("read large library data err(%d)", ret);
  181. return -ret;
  182. }
  183. ssp_sensorhub_print_data(__func__,
  184. hub_data->large_library_data,
  185. hub_data->large_library_length);
  186. kfree(hub_data->large_library_data);
  187. hub_data->large_library_length = 0;
  188. break;
  189. default:
  190. sensorhub_err("ioctl cmd err(%d)", cmd);
  191. return -EINVAL;
  192. }
  193. return length;
  194. }
  195. static struct file_operations ssp_sensorhub_fops = {
  196. .owner = THIS_MODULE,
  197. .open = nonseekable_open,
  198. .write = ssp_sensorhub_write,
  199. .read = ssp_sensorhub_read,
  200. .unlocked_ioctl = ssp_sensorhub_ioctl,
  201. };
  202. void ssp_sensorhub_report_notice(struct ssp_data *ssp_data, char notice)
  203. {
  204. struct ssp_sensorhub_data *hub_data = ssp_data->hub_data;
  205. input_report_rel(hub_data->sensorhub_input_dev, NOTICE, notice);
  206. input_sync(hub_data->sensorhub_input_dev);
  207. if (notice == MSG2SSP_AP_STATUS_WAKEUP)
  208. sensorhub_info("wake up");
  209. else if (notice == MSG2SSP_AP_STATUS_SLEEP)
  210. sensorhub_info("sleep");
  211. else if (notice == MSG2SSP_AP_STATUS_RESET)
  212. sensorhub_info("reset");
  213. else
  214. sensorhub_err("invalid notice(0x%x)", notice);
  215. }
  216. static void ssp_sensorhub_report_library(struct ssp_sensorhub_data *hub_data)
  217. {
  218. input_report_rel(hub_data->sensorhub_input_dev, DATA, DATA);
  219. input_sync(hub_data->sensorhub_input_dev);
  220. wake_lock_timeout(&hub_data->sensorhub_wake_lock, WAKE_LOCK_TIMEOUT);
  221. }
  222. static void ssp_sensorhub_report_large_library(
  223. struct ssp_sensorhub_data *hub_data)
  224. {
  225. input_report_rel(hub_data->sensorhub_input_dev, LARGE_DATA, LARGE_DATA);
  226. input_sync(hub_data->sensorhub_input_dev);
  227. wake_lock_timeout(&hub_data->sensorhub_wake_lock, WAKE_LOCK_TIMEOUT);
  228. }
  229. static int ssp_sensorhub_list(struct ssp_sensorhub_data *hub_data,
  230. char *dataframe, int start, int end)
  231. {
  232. struct list_head *list;
  233. int length = end - start;
  234. int events = 0;
  235. if (unlikely(length <= 0)) {
  236. sensorhub_err("library length err(%d)", length);
  237. return -EINVAL;
  238. }
  239. ssp_sensorhub_print_data(__func__, dataframe+start, length);
  240. spin_lock_bh(&hub_data->sensorhub_lock);
  241. /* how many events in the list? */
  242. list_for_each(list, &hub_data->events_head.list)
  243. events++;
  244. /* overwrite new event if list is full */
  245. if (unlikely(events >= LIST_SIZE)) {
  246. struct sensorhub_event *oldest_event
  247. = list_first_entry(&hub_data->events_head.list,
  248. struct sensorhub_event, list);
  249. list_del(&oldest_event->list);
  250. sensorhub_info("overwrite event");
  251. }
  252. /* allocate memory for new event */
  253. kfree(hub_data->events[hub_data->event_number].library_data);
  254. hub_data->events[hub_data->event_number].library_data
  255. = kzalloc(length * sizeof(char), GFP_ATOMIC);
  256. if (unlikely(!hub_data->events[hub_data->event_number].library_data)) {
  257. sensorhub_err("allocate memory for library err");
  258. spin_unlock_bh(&hub_data->sensorhub_lock);
  259. return -ENOMEM;
  260. }
  261. /* copy new event into memory */
  262. memcpy(hub_data->events[hub_data->event_number].library_data,
  263. dataframe+start, length);
  264. hub_data->events[hub_data->event_number].library_length = length;
  265. /* add new event into the end of list */
  266. list_add_tail(&hub_data->events[hub_data->event_number].list,
  267. &hub_data->events_head.list);
  268. spin_unlock_bh(&hub_data->sensorhub_lock);
  269. /* not to overflow max list capacity */
  270. if (hub_data->event_number++ >= LIST_SIZE - 1)
  271. hub_data->event_number = 0;
  272. return events + (events >= LIST_SIZE ? 0 : 1);
  273. }
  274. static int ssp_sensorhub_thread(void *arg)
  275. {
  276. struct ssp_sensorhub_data *hub_data = (struct ssp_sensorhub_data *)arg;
  277. int ret = 0;
  278. while (likely(!kthread_should_stop())) {
  279. /* run thread if list is not empty */
  280. wait_event_interruptible(hub_data->sensorhub_wq,
  281. kthread_should_stop() ||
  282. !list_empty(&hub_data->events_head.list));
  283. /* exit thread if kthread should stop */
  284. if (unlikely(kthread_should_stop())) {
  285. sensorhub_info("kthread_stop()");
  286. break;
  287. }
  288. /* report sensorhub event to user */
  289. ssp_sensorhub_report_library(hub_data);
  290. /* wait until transfer finished */
  291. ret = wait_for_completion_timeout(
  292. &hub_data->sensorhub_completion, COMPLETION_TIMEOUT);
  293. if (unlikely(!ret))
  294. sensorhub_err("wait timed out");
  295. else if (unlikely(ret < 0))
  296. sensorhub_err("wait for completion err(%d)", ret);
  297. }
  298. return 0;
  299. }
  300. int ssp_sensorhub_handle_data(struct ssp_data *ssp_data, char *dataframe,
  301. int start, int end)
  302. {
  303. struct ssp_sensorhub_data *hub_data = ssp_data->hub_data;
  304. /* add new sensorhub event into list */
  305. int ret = ssp_sensorhub_list(hub_data, dataframe, start, end);
  306. wake_up(&hub_data->sensorhub_wq);
  307. return ret;
  308. }
  309. static int ssp_sensorhub_receive_large_data(struct ssp_sensorhub_data *hub_data,
  310. unsigned char sub_cmd)
  311. {
  312. static int pos; /* large_library_data current position */
  313. char send_data[2] = { 0, }; /* send data */
  314. char receive_data[5] = { 0, }; /* receive data */
  315. char *msg_data; /* Nth msg data */
  316. int total_length = 0; /* total length */
  317. int msg_length = 0; /* Nth msg length */
  318. int total_msg_number; /* total msg number */
  319. int msg_number; /* current msg number */
  320. int ret = 0;
  321. sensorhub_info("sub_cmd = 0x%x", sub_cmd);
  322. send_data[0] = MSG2SSP_STT;
  323. send_data[1] = sub_cmd;
  324. /* receive_data[0-1] : total length
  325. * receive_data[2] >> 4 : total msg number
  326. * receive_data[2] & 0x0F : current msg number */
  327. ret = ssp_i2c_read(hub_data->ssp_data, send_data, sizeof(send_data),
  328. receive_data, sizeof(receive_data), DEFAULT_RETRIES);
  329. if (unlikely(ret < 0)) {
  330. sensorhub_err("MSG2SSP_STT i2c err(%d)", ret);
  331. return ret;
  332. }
  333. /* get total length */
  334. total_length = ((unsigned int)receive_data[0] << 8)
  335. + (unsigned int)receive_data[1];
  336. sensorhub_info("total length = %d", total_length);
  337. total_msg_number = (int)(receive_data[2] >> 4);
  338. msg_number = (int)(receive_data[2] & 0x0F);
  339. /* if this is the first msg */
  340. if (msg_number <= 1) {
  341. /* empty previous large_library_data */
  342. if (hub_data->large_library_length != 0)
  343. kfree(hub_data->large_library_data);
  344. /* allocate new memory for large_library_data */
  345. hub_data->large_library_data
  346. = kzalloc((total_length * sizeof(char)), GFP_KERNEL);
  347. if (unlikely(!hub_data->large_library_data)) {
  348. sensorhub_err("allocate memory for large library err");
  349. return -ENOMEM;
  350. }
  351. hub_data->large_library_length = total_length;
  352. }
  353. /* get the Nth msg length */
  354. msg_length = ((unsigned int)receive_data[3] << 8)
  355. + (unsigned int)receive_data[4];
  356. sensorhub_info("%dth msg length = %d", msg_number, msg_length);
  357. /* receive the Nth msg data */
  358. send_data[0] = MSG2SSP_SRM;
  359. msg_data = kzalloc((msg_length * sizeof(char)), GFP_KERNEL);
  360. if (unlikely(!msg_data)) {
  361. sensorhub_err("allocate memory for msg data err");
  362. return -ENOMEM;
  363. }
  364. ret = ssp_i2c_read(hub_data->ssp_data, send_data, 1,
  365. msg_data, msg_length, 0);
  366. if (unlikely(ret < 0)) {
  367. sensorhub_err("receive %dth msg err(%d)", msg_number, ret);
  368. kfree(msg_data);
  369. return ret;
  370. }
  371. /* copy the Nth msg data into large_library_data */
  372. memcpy(&hub_data->large_library_data[pos],
  373. &msg_data[0], msg_length * sizeof(char));
  374. kfree(msg_data);
  375. pos += msg_length;
  376. if (msg_number < total_msg_number) {
  377. /* still receiving msg data */
  378. sensorhub_info("current msg length = %d(%d/%d)",
  379. msg_length, msg_number, total_msg_number);
  380. } else {
  381. /* finish receiving msg data */
  382. sensorhub_info("total msg length = %d(%d/%d)",
  383. pos, msg_number, total_msg_number);
  384. pos = 0;
  385. }
  386. return msg_number;
  387. }
  388. int ssp_sensorhub_handle_large_data(struct ssp_data *ssp_data,
  389. unsigned char sub_cmd)
  390. {
  391. struct ssp_sensorhub_data *hub_data = ssp_data->hub_data;
  392. static bool err;
  393. static int current_msg_number = 1;
  394. int total_msg_number = (int)(sub_cmd >> 4);
  395. int msg_number = (int)(sub_cmd & 0x0F);
  396. int ret;
  397. /* skip the rest transfer if error occurs */
  398. if (unlikely(err)) {
  399. if (msg_number <= 1) {
  400. current_msg_number = 1;
  401. err = false;
  402. } else {
  403. return -EIO;
  404. }
  405. }
  406. /* next msg is the right one? */
  407. if (current_msg_number++ != msg_number) {
  408. sensorhub_err("next msg should be %dth but %dth",
  409. current_msg_number - 1, msg_number);
  410. sensorhub_err("skip the rest %d msg transfer",
  411. total_msg_number - msg_number);
  412. err = true;
  413. return -EINVAL;
  414. }
  415. /* receive large library data */
  416. ret = ssp_sensorhub_receive_large_data(hub_data, sub_cmd);
  417. if (unlikely(ret < 0)) {
  418. sensorhub_err("receive large msg err(%d/%d)(%d)",
  419. msg_number, total_msg_number, ret);
  420. sensorhub_err("skip the rest %d msg transfer",
  421. total_msg_number - msg_number);
  422. err = true;
  423. return ret;
  424. }
  425. /* finally ready to go to user */
  426. if (msg_number >= total_msg_number) {
  427. ssp_sensorhub_report_large_library(hub_data);
  428. current_msg_number = 1;
  429. }
  430. return ret;
  431. }
  432. int ssp_sensorhub_initialize(struct ssp_data *ssp_data)
  433. {
  434. struct ssp_sensorhub_data *hub_data;
  435. int ret;
  436. /* allocate memory for sensorhub data */
  437. hub_data = kzalloc(sizeof(*hub_data), GFP_KERNEL);
  438. if (!hub_data) {
  439. sensorhub_err("allocate memory for sensorhub data err");
  440. ret = -ENOMEM;
  441. goto exit;
  442. }
  443. hub_data->ssp_data = ssp_data;
  444. ssp_data->hub_data = hub_data;
  445. /* init wakelock, list, waitqueue, completion and spinlock */
  446. wake_lock_init(&hub_data->sensorhub_wake_lock, WAKE_LOCK_SUSPEND,
  447. "ssp_sensorhub_wake_lock");
  448. INIT_LIST_HEAD(&hub_data->events_head.list);
  449. init_waitqueue_head(&hub_data->sensorhub_wq);
  450. init_completion(&hub_data->sensorhub_completion);
  451. spin_lock_init(&hub_data->sensorhub_lock);
  452. /* allocate sensorhub input device */
  453. hub_data->sensorhub_input_dev = input_allocate_device();
  454. if (!hub_data->sensorhub_input_dev) {
  455. sensorhub_err("allocate sensorhub input device err");
  456. ret = -ENOMEM;
  457. goto err_input_allocate_device_sensorhub;
  458. }
  459. /* set sensorhub input device */
  460. input_set_drvdata(hub_data->sensorhub_input_dev, hub_data);
  461. hub_data->sensorhub_input_dev->name = "ssp_context";
  462. input_set_capability(hub_data->sensorhub_input_dev, EV_REL, DATA);
  463. input_set_capability(hub_data->sensorhub_input_dev, EV_REL, LARGE_DATA);
  464. input_set_capability(hub_data->sensorhub_input_dev, EV_REL, NOTICE);
  465. /* register sensorhub input device */
  466. ret = input_register_device(hub_data->sensorhub_input_dev);
  467. if (ret < 0) {
  468. sensorhub_err("register sensorhub input device err(%d)", ret);
  469. input_free_device(hub_data->sensorhub_input_dev);
  470. goto err_input_register_device_sensorhub;
  471. }
  472. /* register sensorhub misc device */
  473. hub_data->sensorhub_device.minor = MISC_DYNAMIC_MINOR;
  474. hub_data->sensorhub_device.name = "ssp_sensorhub";
  475. hub_data->sensorhub_device.fops = &ssp_sensorhub_fops;
  476. ret = misc_register(&hub_data->sensorhub_device);
  477. if (ret < 0) {
  478. sensorhub_err("register sensorhub misc device err(%d)", ret);
  479. goto err_misc_register;
  480. }
  481. /* create and run sensorhub thread */
  482. hub_data->sensorhub_task = kthread_run(ssp_sensorhub_thread,
  483. (void *)hub_data, "ssp_sensorhub_thread");
  484. if (IS_ERR(hub_data->sensorhub_task)) {
  485. ret = PTR_ERR(hub_data->sensorhub_task);
  486. goto err_kthread_create;
  487. }
  488. return 0;
  489. err_kthread_create:
  490. misc_deregister(&hub_data->sensorhub_device);
  491. err_misc_register:
  492. input_unregister_device(hub_data->sensorhub_input_dev);
  493. err_input_register_device_sensorhub:
  494. err_input_allocate_device_sensorhub:
  495. complete_all(&hub_data->sensorhub_completion);
  496. wake_lock_destroy(&hub_data->sensorhub_wake_lock);
  497. kfree(hub_data);
  498. exit:
  499. return ret;
  500. }
  501. void ssp_sensorhub_remove(struct ssp_data *ssp_data)
  502. {
  503. struct ssp_sensorhub_data *hub_data = ssp_data->hub_data;
  504. ssp_sensorhub_fops.write = NULL;
  505. ssp_sensorhub_fops.read = NULL;
  506. ssp_sensorhub_fops.unlocked_ioctl = NULL;
  507. kthread_stop(hub_data->sensorhub_task);
  508. misc_deregister(&hub_data->sensorhub_device);
  509. input_unregister_device(hub_data->sensorhub_input_dev);
  510. complete_all(&hub_data->sensorhub_completion);
  511. wake_lock_destroy(&hub_data->sensorhub_wake_lock);
  512. kfree(hub_data);
  513. }
  514. MODULE_DESCRIPTION("Seamless Sensor Platform(SSP) sensorhub driver");
  515. MODULE_AUTHOR("Samsung Electronics");
  516. MODULE_LICENSE("GPL");