classmate-laptop.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. /*
  2. * Copyright (C) 2009 Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
  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. * You should have received a copy of the GNU General Public License along
  15. * with this program; if not, write to the Free Software Foundation, Inc.,
  16. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  17. */
  18. #include <linux/init.h>
  19. #include <linux/module.h>
  20. #include <linux/workqueue.h>
  21. #include <acpi/acpi_drivers.h>
  22. #include <linux/backlight.h>
  23. #include <linux/input.h>
  24. MODULE_LICENSE("GPL");
  25. struct cmpc_accel {
  26. int sensitivity;
  27. };
  28. #define CMPC_ACCEL_SENSITIVITY_DEFAULT 5
  29. /*
  30. * Generic input device code.
  31. */
  32. typedef void (*input_device_init)(struct input_dev *dev);
  33. static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name,
  34. input_device_init idev_init)
  35. {
  36. struct input_dev *inputdev;
  37. int error;
  38. inputdev = input_allocate_device();
  39. if (!inputdev)
  40. return -ENOMEM;
  41. inputdev->name = name;
  42. inputdev->dev.parent = &acpi->dev;
  43. idev_init(inputdev);
  44. error = input_register_device(inputdev);
  45. if (error) {
  46. input_free_device(inputdev);
  47. return error;
  48. }
  49. dev_set_drvdata(&acpi->dev, inputdev);
  50. return 0;
  51. }
  52. static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
  53. {
  54. struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
  55. input_unregister_device(inputdev);
  56. return 0;
  57. }
  58. /*
  59. * Accelerometer code.
  60. */
  61. static acpi_status cmpc_start_accel(acpi_handle handle)
  62. {
  63. union acpi_object param[2];
  64. struct acpi_object_list input;
  65. acpi_status status;
  66. param[0].type = ACPI_TYPE_INTEGER;
  67. param[0].integer.value = 0x3;
  68. param[1].type = ACPI_TYPE_INTEGER;
  69. input.count = 2;
  70. input.pointer = param;
  71. status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
  72. return status;
  73. }
  74. static acpi_status cmpc_stop_accel(acpi_handle handle)
  75. {
  76. union acpi_object param[2];
  77. struct acpi_object_list input;
  78. acpi_status status;
  79. param[0].type = ACPI_TYPE_INTEGER;
  80. param[0].integer.value = 0x4;
  81. param[1].type = ACPI_TYPE_INTEGER;
  82. input.count = 2;
  83. input.pointer = param;
  84. status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
  85. return status;
  86. }
  87. static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val)
  88. {
  89. union acpi_object param[2];
  90. struct acpi_object_list input;
  91. param[0].type = ACPI_TYPE_INTEGER;
  92. param[0].integer.value = 0x02;
  93. param[1].type = ACPI_TYPE_INTEGER;
  94. param[1].integer.value = val;
  95. input.count = 2;
  96. input.pointer = param;
  97. return acpi_evaluate_object(handle, "ACMD", &input, NULL);
  98. }
  99. static acpi_status cmpc_get_accel(acpi_handle handle,
  100. unsigned char *x,
  101. unsigned char *y,
  102. unsigned char *z)
  103. {
  104. union acpi_object param[2];
  105. struct acpi_object_list input;
  106. struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, 0 };
  107. unsigned char *locs;
  108. acpi_status status;
  109. param[0].type = ACPI_TYPE_INTEGER;
  110. param[0].integer.value = 0x01;
  111. param[1].type = ACPI_TYPE_INTEGER;
  112. input.count = 2;
  113. input.pointer = param;
  114. status = acpi_evaluate_object(handle, "ACMD", &input, &output);
  115. if (ACPI_SUCCESS(status)) {
  116. union acpi_object *obj;
  117. obj = output.pointer;
  118. locs = obj->buffer.pointer;
  119. *x = locs[0];
  120. *y = locs[1];
  121. *z = locs[2];
  122. kfree(output.pointer);
  123. }
  124. return status;
  125. }
  126. static void cmpc_accel_handler(struct acpi_device *dev, u32 event)
  127. {
  128. if (event == 0x81) {
  129. unsigned char x, y, z;
  130. acpi_status status;
  131. status = cmpc_get_accel(dev->handle, &x, &y, &z);
  132. if (ACPI_SUCCESS(status)) {
  133. struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
  134. input_report_abs(inputdev, ABS_X, x);
  135. input_report_abs(inputdev, ABS_Y, y);
  136. input_report_abs(inputdev, ABS_Z, z);
  137. input_sync(inputdev);
  138. }
  139. }
  140. }
  141. static ssize_t cmpc_accel_sensitivity_show(struct device *dev,
  142. struct device_attribute *attr,
  143. char *buf)
  144. {
  145. struct acpi_device *acpi;
  146. struct input_dev *inputdev;
  147. struct cmpc_accel *accel;
  148. acpi = to_acpi_device(dev);
  149. inputdev = dev_get_drvdata(&acpi->dev);
  150. accel = dev_get_drvdata(&inputdev->dev);
  151. return sprintf(buf, "%d\n", accel->sensitivity);
  152. }
  153. static ssize_t cmpc_accel_sensitivity_store(struct device *dev,
  154. struct device_attribute *attr,
  155. const char *buf, size_t count)
  156. {
  157. struct acpi_device *acpi;
  158. struct input_dev *inputdev;
  159. struct cmpc_accel *accel;
  160. unsigned long sensitivity;
  161. int r;
  162. acpi = to_acpi_device(dev);
  163. inputdev = dev_get_drvdata(&acpi->dev);
  164. accel = dev_get_drvdata(&inputdev->dev);
  165. r = strict_strtoul(buf, 0, &sensitivity);
  166. if (r)
  167. return r;
  168. accel->sensitivity = sensitivity;
  169. cmpc_accel_set_sensitivity(acpi->handle, sensitivity);
  170. return strnlen(buf, count);
  171. }
  172. struct device_attribute cmpc_accel_sensitivity_attr = {
  173. .attr = { .name = "sensitivity", .mode = 0660 },
  174. .show = cmpc_accel_sensitivity_show,
  175. .store = cmpc_accel_sensitivity_store
  176. };
  177. static int cmpc_accel_open(struct input_dev *input)
  178. {
  179. struct acpi_device *acpi;
  180. acpi = to_acpi_device(input->dev.parent);
  181. if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle)))
  182. return 0;
  183. return -EIO;
  184. }
  185. static void cmpc_accel_close(struct input_dev *input)
  186. {
  187. struct acpi_device *acpi;
  188. acpi = to_acpi_device(input->dev.parent);
  189. cmpc_stop_accel(acpi->handle);
  190. }
  191. static void cmpc_accel_idev_init(struct input_dev *inputdev)
  192. {
  193. set_bit(EV_ABS, inputdev->evbit);
  194. input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0);
  195. input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0);
  196. input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0);
  197. inputdev->open = cmpc_accel_open;
  198. inputdev->close = cmpc_accel_close;
  199. }
  200. static int cmpc_accel_add(struct acpi_device *acpi)
  201. {
  202. int error;
  203. struct input_dev *inputdev;
  204. struct cmpc_accel *accel;
  205. accel = kmalloc(sizeof(*accel), GFP_KERNEL);
  206. if (!accel)
  207. return -ENOMEM;
  208. accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
  209. cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity);
  210. error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
  211. if (error)
  212. goto failed_file;
  213. error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel",
  214. cmpc_accel_idev_init);
  215. if (error)
  216. goto failed_input;
  217. inputdev = dev_get_drvdata(&acpi->dev);
  218. dev_set_drvdata(&inputdev->dev, accel);
  219. return 0;
  220. failed_input:
  221. device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
  222. failed_file:
  223. kfree(accel);
  224. return error;
  225. }
  226. static int cmpc_accel_remove(struct acpi_device *acpi, int type)
  227. {
  228. struct input_dev *inputdev;
  229. struct cmpc_accel *accel;
  230. inputdev = dev_get_drvdata(&acpi->dev);
  231. accel = dev_get_drvdata(&inputdev->dev);
  232. device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
  233. return cmpc_remove_acpi_notify_device(acpi);
  234. }
  235. static const struct acpi_device_id cmpc_accel_device_ids[] = {
  236. {"ACCE0000", 0},
  237. {"", 0}
  238. };
  239. MODULE_DEVICE_TABLE(acpi, cmpc_accel_device_ids);
  240. static struct acpi_driver cmpc_accel_acpi_driver = {
  241. .owner = THIS_MODULE,
  242. .name = "cmpc_accel",
  243. .class = "cmpc_accel",
  244. .ids = cmpc_accel_device_ids,
  245. .ops = {
  246. .add = cmpc_accel_add,
  247. .remove = cmpc_accel_remove,
  248. .notify = cmpc_accel_handler,
  249. }
  250. };
  251. /*
  252. * Tablet mode code.
  253. */
  254. static acpi_status cmpc_get_tablet(acpi_handle handle,
  255. unsigned long long *value)
  256. {
  257. union acpi_object param;
  258. struct acpi_object_list input;
  259. unsigned long long output;
  260. acpi_status status;
  261. param.type = ACPI_TYPE_INTEGER;
  262. param.integer.value = 0x01;
  263. input.count = 1;
  264. input.pointer = &param;
  265. status = acpi_evaluate_integer(handle, "TCMD", &input, &output);
  266. if (ACPI_SUCCESS(status))
  267. *value = output;
  268. return status;
  269. }
  270. static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)
  271. {
  272. unsigned long long val = 0;
  273. struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
  274. if (event == 0x81) {
  275. if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val)))
  276. input_report_switch(inputdev, SW_TABLET_MODE, !val);
  277. }
  278. }
  279. static void cmpc_tablet_idev_init(struct input_dev *inputdev)
  280. {
  281. unsigned long long val = 0;
  282. struct acpi_device *acpi;
  283. set_bit(EV_SW, inputdev->evbit);
  284. set_bit(SW_TABLET_MODE, inputdev->swbit);
  285. acpi = to_acpi_device(inputdev->dev.parent);
  286. if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
  287. input_report_switch(inputdev, SW_TABLET_MODE, !val);
  288. }
  289. static int cmpc_tablet_add(struct acpi_device *acpi)
  290. {
  291. return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet",
  292. cmpc_tablet_idev_init);
  293. }
  294. static int cmpc_tablet_remove(struct acpi_device *acpi, int type)
  295. {
  296. return cmpc_remove_acpi_notify_device(acpi);
  297. }
  298. static int cmpc_tablet_resume(struct acpi_device *acpi)
  299. {
  300. struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
  301. unsigned long long val = 0;
  302. if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
  303. input_report_switch(inputdev, SW_TABLET_MODE, !val);
  304. return 0;
  305. }
  306. static const struct acpi_device_id cmpc_tablet_device_ids[] = {
  307. {"TBLT0000", 0},
  308. {"", 0}
  309. };
  310. MODULE_DEVICE_TABLE(acpi, cmpc_tablet_device_ids);
  311. static struct acpi_driver cmpc_tablet_acpi_driver = {
  312. .owner = THIS_MODULE,
  313. .name = "cmpc_tablet",
  314. .class = "cmpc_tablet",
  315. .ids = cmpc_tablet_device_ids,
  316. .ops = {
  317. .add = cmpc_tablet_add,
  318. .remove = cmpc_tablet_remove,
  319. .resume = cmpc_tablet_resume,
  320. .notify = cmpc_tablet_handler,
  321. }
  322. };
  323. /*
  324. * Backlight code.
  325. */
  326. static acpi_status cmpc_get_brightness(acpi_handle handle,
  327. unsigned long long *value)
  328. {
  329. union acpi_object param;
  330. struct acpi_object_list input;
  331. unsigned long long output;
  332. acpi_status status;
  333. param.type = ACPI_TYPE_INTEGER;
  334. param.integer.value = 0xC0;
  335. input.count = 1;
  336. input.pointer = &param;
  337. status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
  338. if (ACPI_SUCCESS(status))
  339. *value = output;
  340. return status;
  341. }
  342. static acpi_status cmpc_set_brightness(acpi_handle handle,
  343. unsigned long long value)
  344. {
  345. union acpi_object param[2];
  346. struct acpi_object_list input;
  347. acpi_status status;
  348. unsigned long long output;
  349. param[0].type = ACPI_TYPE_INTEGER;
  350. param[0].integer.value = 0xC0;
  351. param[1].type = ACPI_TYPE_INTEGER;
  352. param[1].integer.value = value;
  353. input.count = 2;
  354. input.pointer = param;
  355. status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
  356. return status;
  357. }
  358. static int cmpc_bl_get_brightness(struct backlight_device *bd)
  359. {
  360. acpi_status status;
  361. acpi_handle handle;
  362. unsigned long long brightness;
  363. handle = bl_get_data(bd);
  364. status = cmpc_get_brightness(handle, &brightness);
  365. if (ACPI_SUCCESS(status))
  366. return brightness;
  367. else
  368. return -1;
  369. }
  370. static int cmpc_bl_update_status(struct backlight_device *bd)
  371. {
  372. acpi_status status;
  373. acpi_handle handle;
  374. handle = bl_get_data(bd);
  375. status = cmpc_set_brightness(handle, bd->props.brightness);
  376. if (ACPI_SUCCESS(status))
  377. return 0;
  378. else
  379. return -1;
  380. }
  381. static struct backlight_ops cmpc_bl_ops = {
  382. .get_brightness = cmpc_bl_get_brightness,
  383. .update_status = cmpc_bl_update_status
  384. };
  385. static int cmpc_bl_add(struct acpi_device *acpi)
  386. {
  387. struct backlight_device *bd;
  388. bd = backlight_device_register("cmpc_bl", &acpi->dev,
  389. acpi->handle, &cmpc_bl_ops);
  390. bd->props.max_brightness = 7;
  391. dev_set_drvdata(&acpi->dev, bd);
  392. return 0;
  393. }
  394. static int cmpc_bl_remove(struct acpi_device *acpi, int type)
  395. {
  396. struct backlight_device *bd;
  397. bd = dev_get_drvdata(&acpi->dev);
  398. backlight_device_unregister(bd);
  399. return 0;
  400. }
  401. static const struct acpi_device_id cmpc_device_ids[] = {
  402. {"IPML200", 0},
  403. {"", 0}
  404. };
  405. MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);
  406. static struct acpi_driver cmpc_bl_acpi_driver = {
  407. .owner = THIS_MODULE,
  408. .name = "cmpc",
  409. .class = "cmpc",
  410. .ids = cmpc_device_ids,
  411. .ops = {
  412. .add = cmpc_bl_add,
  413. .remove = cmpc_bl_remove
  414. }
  415. };
  416. /*
  417. * Extra keys code.
  418. */
  419. static int cmpc_keys_codes[] = {
  420. KEY_UNKNOWN,
  421. KEY_WLAN,
  422. KEY_SWITCHVIDEOMODE,
  423. KEY_BRIGHTNESSDOWN,
  424. KEY_BRIGHTNESSUP,
  425. KEY_VENDOR,
  426. KEY_MAX
  427. };
  428. static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
  429. {
  430. struct input_dev *inputdev;
  431. int code = KEY_MAX;
  432. if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))
  433. code = cmpc_keys_codes[event & 0x0F];
  434. inputdev = dev_get_drvdata(&dev->dev);;
  435. input_report_key(inputdev, code, !(event & 0x10));
  436. }
  437. static void cmpc_keys_idev_init(struct input_dev *inputdev)
  438. {
  439. int i;
  440. set_bit(EV_KEY, inputdev->evbit);
  441. for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++)
  442. set_bit(cmpc_keys_codes[i], inputdev->keybit);
  443. }
  444. static int cmpc_keys_add(struct acpi_device *acpi)
  445. {
  446. return cmpc_add_acpi_notify_device(acpi, "cmpc_keys",
  447. cmpc_keys_idev_init);
  448. }
  449. static int cmpc_keys_remove(struct acpi_device *acpi, int type)
  450. {
  451. return cmpc_remove_acpi_notify_device(acpi);
  452. }
  453. static const struct acpi_device_id cmpc_keys_device_ids[] = {
  454. {"FnBT0000", 0},
  455. {"", 0}
  456. };
  457. MODULE_DEVICE_TABLE(acpi, cmpc_keys_device_ids);
  458. static struct acpi_driver cmpc_keys_acpi_driver = {
  459. .owner = THIS_MODULE,
  460. .name = "cmpc_keys",
  461. .class = "cmpc_keys",
  462. .ids = cmpc_keys_device_ids,
  463. .ops = {
  464. .add = cmpc_keys_add,
  465. .remove = cmpc_keys_remove,
  466. .notify = cmpc_keys_handler,
  467. }
  468. };
  469. /*
  470. * General init/exit code.
  471. */
  472. static int cmpc_init(void)
  473. {
  474. int r;
  475. r = acpi_bus_register_driver(&cmpc_keys_acpi_driver);
  476. if (r)
  477. goto failed_keys;
  478. r = acpi_bus_register_driver(&cmpc_bl_acpi_driver);
  479. if (r)
  480. goto failed_bl;
  481. r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver);
  482. if (r)
  483. goto failed_tablet;
  484. r = acpi_bus_register_driver(&cmpc_accel_acpi_driver);
  485. if (r)
  486. goto failed_accel;
  487. return r;
  488. failed_accel:
  489. acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
  490. failed_tablet:
  491. acpi_bus_unregister_driver(&cmpc_bl_acpi_driver);
  492. failed_bl:
  493. acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
  494. failed_keys:
  495. return r;
  496. }
  497. static void cmpc_exit(void)
  498. {
  499. acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
  500. acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
  501. acpi_bus_unregister_driver(&cmpc_bl_acpi_driver);
  502. acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
  503. }
  504. module_init(cmpc_init);
  505. module_exit(cmpc_exit);