classmate-laptop.c 14 KB

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