classmate-laptop.c 17 KB

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