classmate-laptop.c 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (C) 2009 Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
  4. */
  5. #include <linux/init.h>
  6. #include <linux/module.h>
  7. #include <linux/slab.h>
  8. #include <linux/workqueue.h>
  9. #include <linux/acpi.h>
  10. #include <linux/backlight.h>
  11. #include <linux/input.h>
  12. #include <linux/rfkill.h>
  13. #include <linux/sysfs.h>
  14. struct cmpc_accel {
  15. int sensitivity;
  16. int g_select;
  17. int inputdev_state;
  18. };
  19. #define CMPC_ACCEL_DEV_STATE_CLOSED 0
  20. #define CMPC_ACCEL_DEV_STATE_OPEN 1
  21. #define CMPC_ACCEL_SENSITIVITY_DEFAULT 5
  22. #define CMPC_ACCEL_G_SELECT_DEFAULT 0
  23. #define CMPC_ACCEL_HID "ACCE0000"
  24. #define CMPC_ACCEL_HID_V4 "ACCE0001"
  25. #define CMPC_TABLET_HID "TBLT0000"
  26. #define CMPC_IPML_HID "IPML200"
  27. #define CMPC_KEYS_HID "FNBT0000"
  28. /*
  29. * Generic input device code.
  30. */
  31. typedef void (*input_device_init)(struct input_dev *dev);
  32. static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name,
  33. input_device_init idev_init)
  34. {
  35. struct input_dev *inputdev;
  36. int error;
  37. inputdev = input_allocate_device();
  38. if (!inputdev)
  39. return -ENOMEM;
  40. inputdev->name = name;
  41. inputdev->dev.parent = &acpi->dev;
  42. idev_init(inputdev);
  43. error = input_register_device(inputdev);
  44. if (error) {
  45. input_free_device(inputdev);
  46. return error;
  47. }
  48. dev_set_drvdata(&acpi->dev, inputdev);
  49. return 0;
  50. }
  51. static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
  52. {
  53. struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
  54. input_unregister_device(inputdev);
  55. return 0;
  56. }
  57. /*
  58. * Accelerometer code for Classmate V4
  59. */
  60. static acpi_status cmpc_start_accel_v4(acpi_handle handle)
  61. {
  62. union acpi_object param[4];
  63. struct acpi_object_list input;
  64. acpi_status status;
  65. param[0].type = ACPI_TYPE_INTEGER;
  66. param[0].integer.value = 0x3;
  67. param[1].type = ACPI_TYPE_INTEGER;
  68. param[1].integer.value = 0;
  69. param[2].type = ACPI_TYPE_INTEGER;
  70. param[2].integer.value = 0;
  71. param[3].type = ACPI_TYPE_INTEGER;
  72. param[3].integer.value = 0;
  73. input.count = 4;
  74. input.pointer = param;
  75. status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
  76. return status;
  77. }
  78. static acpi_status cmpc_stop_accel_v4(acpi_handle handle)
  79. {
  80. union acpi_object param[4];
  81. struct acpi_object_list input;
  82. acpi_status status;
  83. param[0].type = ACPI_TYPE_INTEGER;
  84. param[0].integer.value = 0x4;
  85. param[1].type = ACPI_TYPE_INTEGER;
  86. param[1].integer.value = 0;
  87. param[2].type = ACPI_TYPE_INTEGER;
  88. param[2].integer.value = 0;
  89. param[3].type = ACPI_TYPE_INTEGER;
  90. param[3].integer.value = 0;
  91. input.count = 4;
  92. input.pointer = param;
  93. status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
  94. return status;
  95. }
  96. static acpi_status cmpc_accel_set_sensitivity_v4(acpi_handle handle, int val)
  97. {
  98. union acpi_object param[4];
  99. struct acpi_object_list input;
  100. param[0].type = ACPI_TYPE_INTEGER;
  101. param[0].integer.value = 0x02;
  102. param[1].type = ACPI_TYPE_INTEGER;
  103. param[1].integer.value = val;
  104. param[2].type = ACPI_TYPE_INTEGER;
  105. param[2].integer.value = 0;
  106. param[3].type = ACPI_TYPE_INTEGER;
  107. param[3].integer.value = 0;
  108. input.count = 4;
  109. input.pointer = param;
  110. return acpi_evaluate_object(handle, "ACMD", &input, NULL);
  111. }
  112. static acpi_status cmpc_accel_set_g_select_v4(acpi_handle handle, int val)
  113. {
  114. union acpi_object param[4];
  115. struct acpi_object_list input;
  116. param[0].type = ACPI_TYPE_INTEGER;
  117. param[0].integer.value = 0x05;
  118. param[1].type = ACPI_TYPE_INTEGER;
  119. param[1].integer.value = val;
  120. param[2].type = ACPI_TYPE_INTEGER;
  121. param[2].integer.value = 0;
  122. param[3].type = ACPI_TYPE_INTEGER;
  123. param[3].integer.value = 0;
  124. input.count = 4;
  125. input.pointer = param;
  126. return acpi_evaluate_object(handle, "ACMD", &input, NULL);
  127. }
  128. static acpi_status cmpc_get_accel_v4(acpi_handle handle,
  129. int16_t *x,
  130. int16_t *y,
  131. int16_t *z)
  132. {
  133. union acpi_object param[4];
  134. struct acpi_object_list input;
  135. struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
  136. int16_t *locs;
  137. acpi_status status;
  138. param[0].type = ACPI_TYPE_INTEGER;
  139. param[0].integer.value = 0x01;
  140. param[1].type = ACPI_TYPE_INTEGER;
  141. param[1].integer.value = 0;
  142. param[2].type = ACPI_TYPE_INTEGER;
  143. param[2].integer.value = 0;
  144. param[3].type = ACPI_TYPE_INTEGER;
  145. param[3].integer.value = 0;
  146. input.count = 4;
  147. input.pointer = param;
  148. status = acpi_evaluate_object(handle, "ACMD", &input, &output);
  149. if (ACPI_SUCCESS(status)) {
  150. union acpi_object *obj;
  151. obj = output.pointer;
  152. locs = (int16_t *) obj->buffer.pointer;
  153. *x = locs[0];
  154. *y = locs[1];
  155. *z = locs[2];
  156. kfree(output.pointer);
  157. }
  158. return status;
  159. }
  160. static void cmpc_accel_handler_v4(struct acpi_device *dev, u32 event)
  161. {
  162. if (event == 0x81) {
  163. int16_t x, y, z;
  164. acpi_status status;
  165. status = cmpc_get_accel_v4(dev->handle, &x, &y, &z);
  166. if (ACPI_SUCCESS(status)) {
  167. struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
  168. input_report_abs(inputdev, ABS_X, x);
  169. input_report_abs(inputdev, ABS_Y, y);
  170. input_report_abs(inputdev, ABS_Z, z);
  171. input_sync(inputdev);
  172. }
  173. }
  174. }
  175. static ssize_t cmpc_accel_sensitivity_show_v4(struct device *dev,
  176. struct device_attribute *attr,
  177. char *buf)
  178. {
  179. struct acpi_device *acpi;
  180. struct input_dev *inputdev;
  181. struct cmpc_accel *accel;
  182. acpi = to_acpi_device(dev);
  183. inputdev = dev_get_drvdata(&acpi->dev);
  184. if (!inputdev)
  185. return -ENXIO;
  186. accel = dev_get_drvdata(&inputdev->dev);
  187. if (!accel)
  188. return -ENXIO;
  189. return sysfs_emit(buf, "%d\n", accel->sensitivity);
  190. }
  191. static ssize_t cmpc_accel_sensitivity_store_v4(struct device *dev,
  192. struct device_attribute *attr,
  193. const char *buf, size_t count)
  194. {
  195. struct acpi_device *acpi;
  196. struct input_dev *inputdev;
  197. struct cmpc_accel *accel;
  198. unsigned long sensitivity;
  199. int r;
  200. acpi = to_acpi_device(dev);
  201. inputdev = dev_get_drvdata(&acpi->dev);
  202. if (!inputdev)
  203. return -ENXIO;
  204. accel = dev_get_drvdata(&inputdev->dev);
  205. if (!accel)
  206. return -ENXIO;
  207. r = kstrtoul(buf, 0, &sensitivity);
  208. if (r)
  209. return r;
  210. /* sensitivity must be between 1 and 127 */
  211. if (sensitivity < 1 || sensitivity > 127)
  212. return -EINVAL;
  213. accel->sensitivity = sensitivity;
  214. cmpc_accel_set_sensitivity_v4(acpi->handle, sensitivity);
  215. return strnlen(buf, count);
  216. }
  217. static struct device_attribute cmpc_accel_sensitivity_attr_v4 = {
  218. .attr = { .name = "sensitivity", .mode = 0660 },
  219. .show = cmpc_accel_sensitivity_show_v4,
  220. .store = cmpc_accel_sensitivity_store_v4
  221. };
  222. static ssize_t cmpc_accel_g_select_show_v4(struct device *dev,
  223. struct device_attribute *attr,
  224. char *buf)
  225. {
  226. struct acpi_device *acpi;
  227. struct input_dev *inputdev;
  228. struct cmpc_accel *accel;
  229. acpi = to_acpi_device(dev);
  230. inputdev = dev_get_drvdata(&acpi->dev);
  231. if (!inputdev)
  232. return -ENXIO;
  233. accel = dev_get_drvdata(&inputdev->dev);
  234. if (!accel)
  235. return -ENXIO;
  236. return sysfs_emit(buf, "%d\n", accel->g_select);
  237. }
  238. static ssize_t cmpc_accel_g_select_store_v4(struct device *dev,
  239. struct device_attribute *attr,
  240. const char *buf, size_t count)
  241. {
  242. struct acpi_device *acpi;
  243. struct input_dev *inputdev;
  244. struct cmpc_accel *accel;
  245. unsigned long g_select;
  246. int r;
  247. acpi = to_acpi_device(dev);
  248. inputdev = dev_get_drvdata(&acpi->dev);
  249. if (!inputdev)
  250. return -ENXIO;
  251. accel = dev_get_drvdata(&inputdev->dev);
  252. if (!accel)
  253. return -ENXIO;
  254. r = kstrtoul(buf, 0, &g_select);
  255. if (r)
  256. return r;
  257. /* 0 means 1.5g, 1 means 6g, everything else is wrong */
  258. if (g_select != 0 && g_select != 1)
  259. return -EINVAL;
  260. accel->g_select = g_select;
  261. cmpc_accel_set_g_select_v4(acpi->handle, g_select);
  262. return strnlen(buf, count);
  263. }
  264. static struct device_attribute cmpc_accel_g_select_attr_v4 = {
  265. .attr = { .name = "g_select", .mode = 0660 },
  266. .show = cmpc_accel_g_select_show_v4,
  267. .store = cmpc_accel_g_select_store_v4
  268. };
  269. static int cmpc_accel_open_v4(struct input_dev *input)
  270. {
  271. struct acpi_device *acpi;
  272. struct cmpc_accel *accel;
  273. acpi = to_acpi_device(input->dev.parent);
  274. accel = dev_get_drvdata(&input->dev);
  275. if (!accel)
  276. return -ENXIO;
  277. cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
  278. cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
  279. if (ACPI_SUCCESS(cmpc_start_accel_v4(acpi->handle))) {
  280. accel->inputdev_state = CMPC_ACCEL_DEV_STATE_OPEN;
  281. return 0;
  282. }
  283. return -EIO;
  284. }
  285. static void cmpc_accel_close_v4(struct input_dev *input)
  286. {
  287. struct acpi_device *acpi;
  288. struct cmpc_accel *accel;
  289. acpi = to_acpi_device(input->dev.parent);
  290. accel = dev_get_drvdata(&input->dev);
  291. cmpc_stop_accel_v4(acpi->handle);
  292. accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
  293. }
  294. static void cmpc_accel_idev_init_v4(struct input_dev *inputdev)
  295. {
  296. set_bit(EV_ABS, inputdev->evbit);
  297. input_set_abs_params(inputdev, ABS_X, -255, 255, 16, 0);
  298. input_set_abs_params(inputdev, ABS_Y, -255, 255, 16, 0);
  299. input_set_abs_params(inputdev, ABS_Z, -255, 255, 16, 0);
  300. inputdev->open = cmpc_accel_open_v4;
  301. inputdev->close = cmpc_accel_close_v4;
  302. }
  303. #ifdef CONFIG_PM_SLEEP
  304. static int cmpc_accel_suspend_v4(struct device *dev)
  305. {
  306. struct input_dev *inputdev;
  307. struct cmpc_accel *accel;
  308. inputdev = dev_get_drvdata(dev);
  309. accel = dev_get_drvdata(&inputdev->dev);
  310. if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN)
  311. return cmpc_stop_accel_v4(to_acpi_device(dev)->handle);
  312. return 0;
  313. }
  314. static int cmpc_accel_resume_v4(struct device *dev)
  315. {
  316. struct input_dev *inputdev;
  317. struct cmpc_accel *accel;
  318. inputdev = dev_get_drvdata(dev);
  319. accel = dev_get_drvdata(&inputdev->dev);
  320. if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) {
  321. cmpc_accel_set_sensitivity_v4(to_acpi_device(dev)->handle,
  322. accel->sensitivity);
  323. cmpc_accel_set_g_select_v4(to_acpi_device(dev)->handle,
  324. accel->g_select);
  325. if (ACPI_FAILURE(cmpc_start_accel_v4(to_acpi_device(dev)->handle)))
  326. return -EIO;
  327. }
  328. return 0;
  329. }
  330. #endif
  331. static int cmpc_accel_add_v4(struct acpi_device *acpi)
  332. {
  333. int error;
  334. struct input_dev *inputdev;
  335. struct cmpc_accel *accel;
  336. accel = kmalloc_obj(*accel);
  337. if (!accel)
  338. return -ENOMEM;
  339. accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
  340. accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
  341. cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
  342. error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
  343. if (error)
  344. goto failed_sensitivity;
  345. accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT;
  346. cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
  347. error = device_create_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
  348. if (error)
  349. goto failed_g_select;
  350. error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4",
  351. cmpc_accel_idev_init_v4);
  352. if (error)
  353. goto failed_input;
  354. inputdev = dev_get_drvdata(&acpi->dev);
  355. dev_set_drvdata(&inputdev->dev, accel);
  356. return 0;
  357. failed_input:
  358. device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
  359. failed_g_select:
  360. device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
  361. failed_sensitivity:
  362. kfree(accel);
  363. return error;
  364. }
  365. static void cmpc_accel_remove_v4(struct acpi_device *acpi)
  366. {
  367. device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
  368. device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
  369. cmpc_remove_acpi_notify_device(acpi);
  370. }
  371. static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4,
  372. cmpc_accel_resume_v4);
  373. static const struct acpi_device_id cmpc_accel_device_ids_v4[] = {
  374. {CMPC_ACCEL_HID_V4, 0},
  375. {"", 0}
  376. };
  377. static struct acpi_driver cmpc_accel_acpi_driver_v4 = {
  378. .name = "cmpc_accel_v4",
  379. .class = "cmpc_accel_v4",
  380. .ids = cmpc_accel_device_ids_v4,
  381. .ops = {
  382. .add = cmpc_accel_add_v4,
  383. .remove = cmpc_accel_remove_v4,
  384. .notify = cmpc_accel_handler_v4,
  385. },
  386. .drv.pm = &cmpc_accel_pm,
  387. };
  388. /*
  389. * Accelerometer code for Classmate versions prior to V4
  390. */
  391. static acpi_status cmpc_start_accel(acpi_handle handle)
  392. {
  393. union acpi_object param[2];
  394. struct acpi_object_list input;
  395. acpi_status status;
  396. param[0].type = ACPI_TYPE_INTEGER;
  397. param[0].integer.value = 0x3;
  398. param[1].type = ACPI_TYPE_INTEGER;
  399. input.count = 2;
  400. input.pointer = param;
  401. status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
  402. return status;
  403. }
  404. static acpi_status cmpc_stop_accel(acpi_handle handle)
  405. {
  406. union acpi_object param[2];
  407. struct acpi_object_list input;
  408. acpi_status status;
  409. param[0].type = ACPI_TYPE_INTEGER;
  410. param[0].integer.value = 0x4;
  411. param[1].type = ACPI_TYPE_INTEGER;
  412. input.count = 2;
  413. input.pointer = param;
  414. status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
  415. return status;
  416. }
  417. static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val)
  418. {
  419. union acpi_object param[2];
  420. struct acpi_object_list input;
  421. param[0].type = ACPI_TYPE_INTEGER;
  422. param[0].integer.value = 0x02;
  423. param[1].type = ACPI_TYPE_INTEGER;
  424. param[1].integer.value = val;
  425. input.count = 2;
  426. input.pointer = param;
  427. return acpi_evaluate_object(handle, "ACMD", &input, NULL);
  428. }
  429. static acpi_status cmpc_get_accel(acpi_handle handle,
  430. unsigned char *x,
  431. unsigned char *y,
  432. unsigned char *z)
  433. {
  434. union acpi_object param[2];
  435. struct acpi_object_list input;
  436. struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
  437. unsigned char *locs;
  438. acpi_status status;
  439. param[0].type = ACPI_TYPE_INTEGER;
  440. param[0].integer.value = 0x01;
  441. param[1].type = ACPI_TYPE_INTEGER;
  442. input.count = 2;
  443. input.pointer = param;
  444. status = acpi_evaluate_object(handle, "ACMD", &input, &output);
  445. if (ACPI_SUCCESS(status)) {
  446. union acpi_object *obj;
  447. obj = output.pointer;
  448. locs = obj->buffer.pointer;
  449. *x = locs[0];
  450. *y = locs[1];
  451. *z = locs[2];
  452. kfree(output.pointer);
  453. }
  454. return status;
  455. }
  456. static void cmpc_accel_handler(struct acpi_device *dev, u32 event)
  457. {
  458. if (event == 0x81) {
  459. unsigned char x, y, z;
  460. acpi_status status;
  461. status = cmpc_get_accel(dev->handle, &x, &y, &z);
  462. if (ACPI_SUCCESS(status)) {
  463. struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
  464. input_report_abs(inputdev, ABS_X, x);
  465. input_report_abs(inputdev, ABS_Y, y);
  466. input_report_abs(inputdev, ABS_Z, z);
  467. input_sync(inputdev);
  468. }
  469. }
  470. }
  471. static ssize_t cmpc_accel_sensitivity_show(struct device *dev,
  472. struct device_attribute *attr,
  473. char *buf)
  474. {
  475. struct acpi_device *acpi;
  476. struct input_dev *inputdev;
  477. struct cmpc_accel *accel;
  478. acpi = to_acpi_device(dev);
  479. inputdev = dev_get_drvdata(&acpi->dev);
  480. if (!inputdev)
  481. return -ENXIO;
  482. accel = dev_get_drvdata(&inputdev->dev);
  483. if (!accel)
  484. return -ENXIO;
  485. return sysfs_emit(buf, "%d\n", accel->sensitivity);
  486. }
  487. static ssize_t cmpc_accel_sensitivity_store(struct device *dev,
  488. struct device_attribute *attr,
  489. const char *buf, size_t count)
  490. {
  491. struct acpi_device *acpi;
  492. struct input_dev *inputdev;
  493. struct cmpc_accel *accel;
  494. unsigned long sensitivity;
  495. int r;
  496. acpi = to_acpi_device(dev);
  497. inputdev = dev_get_drvdata(&acpi->dev);
  498. if (!inputdev)
  499. return -ENXIO;
  500. accel = dev_get_drvdata(&inputdev->dev);
  501. if (!accel)
  502. return -ENXIO;
  503. r = kstrtoul(buf, 0, &sensitivity);
  504. if (r)
  505. return r;
  506. accel->sensitivity = sensitivity;
  507. cmpc_accel_set_sensitivity(acpi->handle, sensitivity);
  508. return strnlen(buf, count);
  509. }
  510. static struct device_attribute cmpc_accel_sensitivity_attr = {
  511. .attr = { .name = "sensitivity", .mode = 0660 },
  512. .show = cmpc_accel_sensitivity_show,
  513. .store = cmpc_accel_sensitivity_store
  514. };
  515. static int cmpc_accel_open(struct input_dev *input)
  516. {
  517. struct acpi_device *acpi;
  518. acpi = to_acpi_device(input->dev.parent);
  519. if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle)))
  520. return 0;
  521. return -EIO;
  522. }
  523. static void cmpc_accel_close(struct input_dev *input)
  524. {
  525. struct acpi_device *acpi;
  526. acpi = to_acpi_device(input->dev.parent);
  527. cmpc_stop_accel(acpi->handle);
  528. }
  529. static void cmpc_accel_idev_init(struct input_dev *inputdev)
  530. {
  531. set_bit(EV_ABS, inputdev->evbit);
  532. input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0);
  533. input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0);
  534. input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0);
  535. inputdev->open = cmpc_accel_open;
  536. inputdev->close = cmpc_accel_close;
  537. }
  538. static int cmpc_accel_add(struct acpi_device *acpi)
  539. {
  540. int error;
  541. struct input_dev *inputdev;
  542. struct cmpc_accel *accel;
  543. accel = kmalloc_obj(*accel);
  544. if (!accel)
  545. return -ENOMEM;
  546. accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
  547. cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity);
  548. error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
  549. if (error)
  550. goto failed_file;
  551. error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel",
  552. cmpc_accel_idev_init);
  553. if (error)
  554. goto failed_input;
  555. inputdev = dev_get_drvdata(&acpi->dev);
  556. dev_set_drvdata(&inputdev->dev, accel);
  557. return 0;
  558. failed_input:
  559. device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
  560. failed_file:
  561. kfree(accel);
  562. return error;
  563. }
  564. static void cmpc_accel_remove(struct acpi_device *acpi)
  565. {
  566. device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
  567. cmpc_remove_acpi_notify_device(acpi);
  568. }
  569. static const struct acpi_device_id cmpc_accel_device_ids[] = {
  570. {CMPC_ACCEL_HID, 0},
  571. {"", 0}
  572. };
  573. static struct acpi_driver cmpc_accel_acpi_driver = {
  574. .name = "cmpc_accel",
  575. .class = "cmpc_accel",
  576. .ids = cmpc_accel_device_ids,
  577. .ops = {
  578. .add = cmpc_accel_add,
  579. .remove = cmpc_accel_remove,
  580. .notify = cmpc_accel_handler,
  581. }
  582. };
  583. /*
  584. * Tablet mode code.
  585. */
  586. static acpi_status cmpc_get_tablet(acpi_handle handle,
  587. unsigned long long *value)
  588. {
  589. union acpi_object param;
  590. struct acpi_object_list input;
  591. unsigned long long output;
  592. acpi_status status;
  593. param.type = ACPI_TYPE_INTEGER;
  594. param.integer.value = 0x01;
  595. input.count = 1;
  596. input.pointer = &param;
  597. status = acpi_evaluate_integer(handle, "TCMD", &input, &output);
  598. if (ACPI_SUCCESS(status))
  599. *value = output;
  600. return status;
  601. }
  602. static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)
  603. {
  604. unsigned long long val = 0;
  605. struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
  606. if (event == 0x81) {
  607. if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) {
  608. input_report_switch(inputdev, SW_TABLET_MODE, !val);
  609. input_sync(inputdev);
  610. }
  611. }
  612. }
  613. static void cmpc_tablet_idev_init(struct input_dev *inputdev)
  614. {
  615. unsigned long long val = 0;
  616. struct acpi_device *acpi;
  617. set_bit(EV_SW, inputdev->evbit);
  618. set_bit(SW_TABLET_MODE, inputdev->swbit);
  619. acpi = to_acpi_device(inputdev->dev.parent);
  620. if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) {
  621. input_report_switch(inputdev, SW_TABLET_MODE, !val);
  622. input_sync(inputdev);
  623. }
  624. }
  625. static int cmpc_tablet_add(struct acpi_device *acpi)
  626. {
  627. return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet",
  628. cmpc_tablet_idev_init);
  629. }
  630. static void cmpc_tablet_remove(struct acpi_device *acpi)
  631. {
  632. cmpc_remove_acpi_notify_device(acpi);
  633. }
  634. #ifdef CONFIG_PM_SLEEP
  635. static int cmpc_tablet_resume(struct device *dev)
  636. {
  637. struct input_dev *inputdev = dev_get_drvdata(dev);
  638. unsigned long long val = 0;
  639. if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val))) {
  640. input_report_switch(inputdev, SW_TABLET_MODE, !val);
  641. input_sync(inputdev);
  642. }
  643. return 0;
  644. }
  645. #endif
  646. static SIMPLE_DEV_PM_OPS(cmpc_tablet_pm, NULL, cmpc_tablet_resume);
  647. static const struct acpi_device_id cmpc_tablet_device_ids[] = {
  648. {CMPC_TABLET_HID, 0},
  649. {"", 0}
  650. };
  651. static struct acpi_driver cmpc_tablet_acpi_driver = {
  652. .name = "cmpc_tablet",
  653. .class = "cmpc_tablet",
  654. .ids = cmpc_tablet_device_ids,
  655. .ops = {
  656. .add = cmpc_tablet_add,
  657. .remove = cmpc_tablet_remove,
  658. .notify = cmpc_tablet_handler,
  659. },
  660. .drv.pm = &cmpc_tablet_pm,
  661. };
  662. /*
  663. * Backlight code.
  664. */
  665. static acpi_status cmpc_get_brightness(acpi_handle handle,
  666. unsigned long long *value)
  667. {
  668. union acpi_object param;
  669. struct acpi_object_list input;
  670. unsigned long long output;
  671. acpi_status status;
  672. param.type = ACPI_TYPE_INTEGER;
  673. param.integer.value = 0xC0;
  674. input.count = 1;
  675. input.pointer = &param;
  676. status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
  677. if (ACPI_SUCCESS(status))
  678. *value = output;
  679. return status;
  680. }
  681. static acpi_status cmpc_set_brightness(acpi_handle handle,
  682. unsigned long long value)
  683. {
  684. union acpi_object param[2];
  685. struct acpi_object_list input;
  686. acpi_status status;
  687. unsigned long long output;
  688. param[0].type = ACPI_TYPE_INTEGER;
  689. param[0].integer.value = 0xC0;
  690. param[1].type = ACPI_TYPE_INTEGER;
  691. param[1].integer.value = value;
  692. input.count = 2;
  693. input.pointer = param;
  694. status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
  695. return status;
  696. }
  697. static int cmpc_bl_get_brightness(struct backlight_device *bd)
  698. {
  699. acpi_status status;
  700. acpi_handle handle;
  701. unsigned long long brightness;
  702. handle = bl_get_data(bd);
  703. status = cmpc_get_brightness(handle, &brightness);
  704. if (ACPI_SUCCESS(status))
  705. return brightness;
  706. else
  707. return -1;
  708. }
  709. static int cmpc_bl_update_status(struct backlight_device *bd)
  710. {
  711. acpi_status status;
  712. acpi_handle handle;
  713. handle = bl_get_data(bd);
  714. status = cmpc_set_brightness(handle, bd->props.brightness);
  715. if (ACPI_SUCCESS(status))
  716. return 0;
  717. else
  718. return -1;
  719. }
  720. static const struct backlight_ops cmpc_bl_ops = {
  721. .get_brightness = cmpc_bl_get_brightness,
  722. .update_status = cmpc_bl_update_status
  723. };
  724. /*
  725. * RFKILL code.
  726. */
  727. static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle,
  728. unsigned long long *value)
  729. {
  730. union acpi_object param;
  731. struct acpi_object_list input;
  732. unsigned long long output;
  733. acpi_status status;
  734. param.type = ACPI_TYPE_INTEGER;
  735. param.integer.value = 0xC1;
  736. input.count = 1;
  737. input.pointer = &param;
  738. status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
  739. if (ACPI_SUCCESS(status))
  740. *value = output;
  741. return status;
  742. }
  743. static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle,
  744. unsigned long long value)
  745. {
  746. union acpi_object param[2];
  747. struct acpi_object_list input;
  748. acpi_status status;
  749. unsigned long long output;
  750. param[0].type = ACPI_TYPE_INTEGER;
  751. param[0].integer.value = 0xC1;
  752. param[1].type = ACPI_TYPE_INTEGER;
  753. param[1].integer.value = value;
  754. input.count = 2;
  755. input.pointer = param;
  756. status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
  757. return status;
  758. }
  759. static void cmpc_rfkill_query(struct rfkill *rfkill, void *data)
  760. {
  761. acpi_status status;
  762. acpi_handle handle;
  763. unsigned long long state;
  764. bool blocked;
  765. handle = data;
  766. status = cmpc_get_rfkill_wlan(handle, &state);
  767. if (ACPI_SUCCESS(status)) {
  768. blocked = state & 1 ? false : true;
  769. rfkill_set_sw_state(rfkill, blocked);
  770. }
  771. }
  772. static int cmpc_rfkill_block(void *data, bool blocked)
  773. {
  774. acpi_status status;
  775. acpi_handle handle;
  776. unsigned long long state;
  777. bool is_blocked;
  778. handle = data;
  779. status = cmpc_get_rfkill_wlan(handle, &state);
  780. if (ACPI_FAILURE(status))
  781. return -ENODEV;
  782. /* Check if we really need to call cmpc_set_rfkill_wlan */
  783. is_blocked = state & 1 ? false : true;
  784. if (is_blocked != blocked) {
  785. state = blocked ? 0 : 1;
  786. status = cmpc_set_rfkill_wlan(handle, state);
  787. if (ACPI_FAILURE(status))
  788. return -ENODEV;
  789. }
  790. return 0;
  791. }
  792. static const struct rfkill_ops cmpc_rfkill_ops = {
  793. .query = cmpc_rfkill_query,
  794. .set_block = cmpc_rfkill_block,
  795. };
  796. /*
  797. * Common backlight and rfkill code.
  798. */
  799. struct ipml200_dev {
  800. struct backlight_device *bd;
  801. struct rfkill *rf;
  802. };
  803. static int cmpc_ipml_add(struct acpi_device *acpi)
  804. {
  805. int retval;
  806. struct ipml200_dev *ipml;
  807. struct backlight_properties props;
  808. ipml = kmalloc_obj(*ipml);
  809. if (ipml == NULL)
  810. return -ENOMEM;
  811. memset(&props, 0, sizeof(struct backlight_properties));
  812. props.type = BACKLIGHT_PLATFORM;
  813. props.max_brightness = 7;
  814. ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev,
  815. acpi->handle, &cmpc_bl_ops,
  816. &props);
  817. if (IS_ERR(ipml->bd)) {
  818. retval = PTR_ERR(ipml->bd);
  819. goto out_bd;
  820. }
  821. ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN,
  822. &cmpc_rfkill_ops, acpi->handle);
  823. /*
  824. * If RFKILL is disabled, rfkill_alloc will return ERR_PTR(-ENODEV).
  825. * This is OK, however, since all other uses of the device will not
  826. * dereference it.
  827. */
  828. if (ipml->rf) {
  829. retval = rfkill_register(ipml->rf);
  830. if (retval) {
  831. rfkill_destroy(ipml->rf);
  832. ipml->rf = NULL;
  833. }
  834. }
  835. dev_set_drvdata(&acpi->dev, ipml);
  836. return 0;
  837. out_bd:
  838. kfree(ipml);
  839. return retval;
  840. }
  841. static void cmpc_ipml_remove(struct acpi_device *acpi)
  842. {
  843. struct ipml200_dev *ipml;
  844. ipml = dev_get_drvdata(&acpi->dev);
  845. backlight_device_unregister(ipml->bd);
  846. if (ipml->rf) {
  847. rfkill_unregister(ipml->rf);
  848. rfkill_destroy(ipml->rf);
  849. }
  850. kfree(ipml);
  851. }
  852. static const struct acpi_device_id cmpc_ipml_device_ids[] = {
  853. {CMPC_IPML_HID, 0},
  854. {"", 0}
  855. };
  856. static struct acpi_driver cmpc_ipml_acpi_driver = {
  857. .name = "cmpc",
  858. .class = "cmpc",
  859. .ids = cmpc_ipml_device_ids,
  860. .ops = {
  861. .add = cmpc_ipml_add,
  862. .remove = cmpc_ipml_remove
  863. }
  864. };
  865. /*
  866. * Extra keys code.
  867. */
  868. static int cmpc_keys_codes[] = {
  869. KEY_UNKNOWN,
  870. KEY_WLAN,
  871. KEY_SWITCHVIDEOMODE,
  872. KEY_BRIGHTNESSDOWN,
  873. KEY_BRIGHTNESSUP,
  874. KEY_VENDOR,
  875. KEY_UNKNOWN,
  876. KEY_CAMERA,
  877. KEY_BACK,
  878. KEY_FORWARD,
  879. KEY_UNKNOWN,
  880. KEY_WLAN, /* NL3: 0x8b (press), 0x9b (release) */
  881. KEY_MAX
  882. };
  883. static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
  884. {
  885. struct input_dev *inputdev;
  886. int code = KEY_MAX;
  887. if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))
  888. code = cmpc_keys_codes[event & 0x0F];
  889. inputdev = dev_get_drvdata(&dev->dev);
  890. input_report_key(inputdev, code, !(event & 0x10));
  891. input_sync(inputdev);
  892. }
  893. static void cmpc_keys_idev_init(struct input_dev *inputdev)
  894. {
  895. int i;
  896. set_bit(EV_KEY, inputdev->evbit);
  897. for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++)
  898. set_bit(cmpc_keys_codes[i], inputdev->keybit);
  899. }
  900. static int cmpc_keys_add(struct acpi_device *acpi)
  901. {
  902. return cmpc_add_acpi_notify_device(acpi, "cmpc_keys",
  903. cmpc_keys_idev_init);
  904. }
  905. static void cmpc_keys_remove(struct acpi_device *acpi)
  906. {
  907. cmpc_remove_acpi_notify_device(acpi);
  908. }
  909. static const struct acpi_device_id cmpc_keys_device_ids[] = {
  910. {CMPC_KEYS_HID, 0},
  911. {"", 0}
  912. };
  913. static struct acpi_driver cmpc_keys_acpi_driver = {
  914. .name = "cmpc_keys",
  915. .class = "cmpc_keys",
  916. .ids = cmpc_keys_device_ids,
  917. .ops = {
  918. .add = cmpc_keys_add,
  919. .remove = cmpc_keys_remove,
  920. .notify = cmpc_keys_handler,
  921. }
  922. };
  923. /*
  924. * General init/exit code.
  925. */
  926. static int cmpc_init(void)
  927. {
  928. int r;
  929. r = acpi_bus_register_driver(&cmpc_keys_acpi_driver);
  930. if (r)
  931. goto failed_keys;
  932. r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver);
  933. if (r)
  934. goto failed_bl;
  935. r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver);
  936. if (r)
  937. goto failed_tablet;
  938. r = acpi_bus_register_driver(&cmpc_accel_acpi_driver);
  939. if (r)
  940. goto failed_accel;
  941. r = acpi_bus_register_driver(&cmpc_accel_acpi_driver_v4);
  942. if (r)
  943. goto failed_accel_v4;
  944. return r;
  945. failed_accel_v4:
  946. acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
  947. failed_accel:
  948. acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
  949. failed_tablet:
  950. acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
  951. failed_bl:
  952. acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
  953. failed_keys:
  954. return r;
  955. }
  956. static void cmpc_exit(void)
  957. {
  958. acpi_bus_unregister_driver(&cmpc_accel_acpi_driver_v4);
  959. acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
  960. acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
  961. acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
  962. acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
  963. }
  964. module_init(cmpc_init);
  965. module_exit(cmpc_exit);
  966. static const struct acpi_device_id cmpc_device_ids[] __maybe_unused = {
  967. {CMPC_ACCEL_HID, 0},
  968. {CMPC_ACCEL_HID_V4, 0},
  969. {CMPC_TABLET_HID, 0},
  970. {CMPC_IPML_HID, 0},
  971. {CMPC_KEYS_HID, 0},
  972. {"", 0}
  973. };
  974. MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);
  975. MODULE_DESCRIPTION("Support for Intel Classmate PC ACPI devices");
  976. MODULE_LICENSE("GPL");