ibm_acpi.c 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246
  1. /*
  2. * ibm_acpi.c - IBM ThinkPad ACPI Extras
  3. *
  4. *
  5. * Copyright (C) 2004 Borislav Deianov
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  20. *
  21. * Changelog:
  22. *
  23. * 2004-08-09 0.1 initial release, support for X series
  24. * 2004-08-14 0.2 support for T series, X20
  25. * bluetooth enable/disable
  26. * hotkey events disabled by default
  27. * removed fan control, currently useless
  28. * 2004-08-17 0.3 support for R40
  29. * lcd off, brightness control
  30. * thinklight on/off
  31. * 2004-09-16 0.4 support for module parameters
  32. * hotkey mask can be prefixed by 0x
  33. * video output switching
  34. * video expansion control
  35. * ultrabay eject support
  36. * removed lcd brightness/on/off control, didn't work
  37. * 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20
  38. * proc file format changed
  39. * video_switch command
  40. * experimental cmos control
  41. * experimental led control
  42. * experimental acpi sounds
  43. * 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device
  44. * 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20
  45. * fix LED control on A21e
  46. * 2004-11-08 0.8 fix init error case, don't return from a macro
  47. * thanks to Chris Wright <chrisw@osdl.org>
  48. */
  49. #define IBM_VERSION "0.8"
  50. #include <linux/kernel.h>
  51. #include <linux/module.h>
  52. #include <linux/init.h>
  53. #include <linux/types.h>
  54. #include <linux/proc_fs.h>
  55. #include <asm/uaccess.h>
  56. #include <acpi/acpi_drivers.h>
  57. #include <acpi/acnamesp.h>
  58. #define IBM_NAME "ibm"
  59. #define IBM_DESC "IBM ThinkPad ACPI Extras"
  60. #define IBM_FILE "ibm_acpi"
  61. #define IBM_URL "http://ibm-acpi.sf.net/"
  62. #define IBM_DIR IBM_NAME
  63. #define IBM_LOG IBM_FILE ": "
  64. #define IBM_ERR KERN_ERR IBM_LOG
  65. #define IBM_NOTICE KERN_NOTICE IBM_LOG
  66. #define IBM_INFO KERN_INFO IBM_LOG
  67. #define IBM_DEBUG KERN_DEBUG IBM_LOG
  68. #define IBM_MAX_ACPI_ARGS 3
  69. #define __unused __attribute__ ((unused))
  70. static int experimental;
  71. module_param(experimental, int, 0);
  72. static acpi_handle root_handle = NULL;
  73. #define IBM_HANDLE(object, parent, paths...) \
  74. static acpi_handle object##_handle; \
  75. static acpi_handle *object##_parent = &parent##_handle; \
  76. static char *object##_paths[] = { paths }
  77. IBM_HANDLE(ec, root,
  78. "\\_SB.PCI0.ISA.EC", /* A21e, A22p, T20, T21, X20 */
  79. "\\_SB.PCI0.LPC.EC", /* all others */
  80. );
  81. IBM_HANDLE(vid, root,
  82. "\\_SB.PCI0.VID", /* A21e, G40, X30, X40 */
  83. "\\_SB.PCI0.AGP.VID", /* all others */
  84. );
  85. IBM_HANDLE(cmos, root,
  86. "\\UCMS", /* R50, R50p, R51, T4x, X31, X40 */
  87. "\\CMOS", /* A3x, G40, R32, T23, T30, X22, X24, X30 */
  88. "\\CMS", /* R40, R40e */
  89. ); /* A21e, A22p, T20, T21, X20 */
  90. IBM_HANDLE(dock, root,
  91. "\\_SB.GDCK", /* X30, X31, X40 */
  92. "\\_SB.PCI0.DOCK", /* A22p, T20, T21, X20 */
  93. "\\_SB.PCI0.PCI1.DOCK", /* all others */
  94. ); /* A21e, G40, R32, R40, R40e */
  95. IBM_HANDLE(bay, root,
  96. "\\_SB.PCI0.IDE0.SCND.MSTR"); /* all except A21e */
  97. IBM_HANDLE(bayej, root,
  98. "\\_SB.PCI0.IDE0.SCND.MSTR._EJ0"); /* all except A2x, A3x */
  99. IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A22p, T20, T21, X20 */
  100. IBM_HANDLE(hkey, ec, "HKEY"); /* all */
  101. IBM_HANDLE(led, ec, "LED"); /* all except A21e, A22p, T20, T21, X20 */
  102. IBM_HANDLE(sysl, ec, "SYSL"); /* A21e, A22p, T20, T21, X20 */
  103. IBM_HANDLE(bled, ec, "BLED"); /* A22p, T20, T21, X20 */
  104. IBM_HANDLE(beep, ec, "BEEP"); /* all models */
  105. struct ibm_struct {
  106. char *name;
  107. char *hid;
  108. struct acpi_driver *driver;
  109. int (*init) (struct ibm_struct *);
  110. int (*read) (struct ibm_struct *, char *);
  111. int (*write) (struct ibm_struct *, char *);
  112. void (*exit) (struct ibm_struct *);
  113. void (*notify) (struct ibm_struct *, u32);
  114. acpi_handle *handle;
  115. int type;
  116. struct acpi_device *device;
  117. int driver_registered;
  118. int proc_created;
  119. int init_called;
  120. int notify_installed;
  121. int supported;
  122. union {
  123. struct {
  124. int status;
  125. int mask;
  126. } hotkey;
  127. struct {
  128. int autoswitch;
  129. } video;
  130. } state;
  131. int experimental;
  132. };
  133. static struct proc_dir_entry *proc_dir = NULL;
  134. #define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")
  135. #define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
  136. #define strlencmp(a,b) (strncmp((a), (b), strlen(b)))
  137. static int acpi_evalf(acpi_handle handle,
  138. void *res, char *method, char *fmt, ...)
  139. {
  140. char *fmt0 = fmt;
  141. struct acpi_object_list params;
  142. union acpi_object in_objs[IBM_MAX_ACPI_ARGS];
  143. struct acpi_buffer result;
  144. union acpi_object out_obj;
  145. acpi_status status;
  146. va_list ap;
  147. char res_type;
  148. int success;
  149. int quiet;
  150. if (!*fmt) {
  151. printk(IBM_ERR "acpi_evalf() called with empty format\n");
  152. return 0;
  153. }
  154. if (*fmt == 'q') {
  155. quiet = 1;
  156. fmt++;
  157. } else
  158. quiet = 0;
  159. res_type = *(fmt++);
  160. params.count = 0;
  161. params.pointer = &in_objs[0];
  162. va_start(ap, fmt);
  163. while (*fmt) {
  164. char c = *(fmt++);
  165. switch (c) {
  166. case 'd': /* int */
  167. in_objs[params.count].integer.value = va_arg(ap, int);
  168. in_objs[params.count++].type = ACPI_TYPE_INTEGER;
  169. break;
  170. /* add more types as needed */
  171. default:
  172. printk(IBM_ERR "acpi_evalf() called "
  173. "with invalid format character '%c'\n", c);
  174. return 0;
  175. }
  176. }
  177. va_end(ap);
  178. result.length = sizeof(out_obj);
  179. result.pointer = &out_obj;
  180. status = acpi_evaluate_object(handle, method, &params, &result);
  181. switch (res_type) {
  182. case 'd': /* int */
  183. if (res)
  184. *(int *)res = out_obj.integer.value;
  185. success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER;
  186. break;
  187. case 'v': /* void */
  188. success = status == AE_OK;
  189. break;
  190. /* add more types as needed */
  191. default:
  192. printk(IBM_ERR "acpi_evalf() called "
  193. "with invalid format character '%c'\n", res_type);
  194. return 0;
  195. }
  196. if (!success && !quiet)
  197. printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",
  198. method, fmt0, status);
  199. return success;
  200. }
  201. static void __unused acpi_print_int(acpi_handle handle, char *method)
  202. {
  203. int i;
  204. if (acpi_evalf(handle, &i, method, "d"))
  205. printk(IBM_INFO "%s = 0x%x\n", method, i);
  206. else
  207. printk(IBM_ERR "error calling %s\n", method);
  208. }
  209. static char *next_cmd(char **cmds)
  210. {
  211. char *start = *cmds;
  212. char *end;
  213. while ((end = strchr(start, ',')) && end == start)
  214. start = end + 1;
  215. if (!end)
  216. return NULL;
  217. *end = 0;
  218. *cmds = end + 1;
  219. return start;
  220. }
  221. static int driver_init(struct ibm_struct *ibm)
  222. {
  223. printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION);
  224. printk(IBM_INFO "%s\n", IBM_URL);
  225. return 0;
  226. }
  227. static int driver_read(struct ibm_struct *ibm, char *p)
  228. {
  229. int len = 0;
  230. len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC);
  231. len += sprintf(p + len, "version:\t%s\n", IBM_VERSION);
  232. return len;
  233. }
  234. static int hotkey_get(struct ibm_struct *ibm, int *status, int *mask)
  235. {
  236. if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))
  237. return -EIO;
  238. if (ibm->supported) {
  239. if (!acpi_evalf(hkey_handle, mask, "DHKN", "qd"))
  240. return -EIO;
  241. } else {
  242. *mask = ibm->state.hotkey.mask;
  243. }
  244. return 0;
  245. }
  246. static int hotkey_set(struct ibm_struct *ibm, int status, int mask)
  247. {
  248. int i;
  249. if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status))
  250. return -EIO;
  251. if (!ibm->supported)
  252. return 0;
  253. for (i=0; i<32; i++) {
  254. int bit = ((1 << i) & mask) != 0;
  255. if (!acpi_evalf(hkey_handle, NULL, "MHKM", "vdd", i+1, bit))
  256. return -EIO;
  257. }
  258. return 0;
  259. }
  260. static int hotkey_init(struct ibm_struct *ibm)
  261. {
  262. int ret;
  263. ibm->supported = 1;
  264. ret = hotkey_get(ibm,
  265. &ibm->state.hotkey.status,
  266. &ibm->state.hotkey.mask);
  267. if (ret < 0) {
  268. /* mask not supported on A21e, A22p, T20, T21, X20, X22, X24 */
  269. ibm->supported = 0;
  270. ret = hotkey_get(ibm,
  271. &ibm->state.hotkey.status,
  272. &ibm->state.hotkey.mask);
  273. }
  274. return ret;
  275. }
  276. static int hotkey_read(struct ibm_struct *ibm, char *p)
  277. {
  278. int status, mask;
  279. int len = 0;
  280. if (hotkey_get(ibm, &status, &mask) < 0)
  281. return -EIO;
  282. len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
  283. if (ibm->supported) {
  284. len += sprintf(p + len, "mask:\t\t0x%04x\n", mask);
  285. len += sprintf(p + len,
  286. "commands:\tenable, disable, reset, <mask>\n");
  287. } else {
  288. len += sprintf(p + len, "mask:\t\tnot supported\n");
  289. len += sprintf(p + len, "commands:\tenable, disable, reset\n");
  290. }
  291. return len;
  292. }
  293. static int hotkey_write(struct ibm_struct *ibm, char *buf)
  294. {
  295. int status, mask;
  296. char *cmd;
  297. int do_cmd = 0;
  298. if (hotkey_get(ibm, &status, &mask) < 0)
  299. return -ENODEV;
  300. while ((cmd = next_cmd(&buf))) {
  301. if (strlencmp(cmd, "enable") == 0) {
  302. status = 1;
  303. } else if (strlencmp(cmd, "disable") == 0) {
  304. status = 0;
  305. } else if (strlencmp(cmd, "reset") == 0) {
  306. status = ibm->state.hotkey.status;
  307. mask = ibm->state.hotkey.mask;
  308. } else if (sscanf(cmd, "0x%x", &mask) == 1) {
  309. /* mask set */
  310. } else if (sscanf(cmd, "%x", &mask) == 1) {
  311. /* mask set */
  312. } else
  313. return -EINVAL;
  314. do_cmd = 1;
  315. }
  316. if (do_cmd && hotkey_set(ibm, status, mask) < 0)
  317. return -EIO;
  318. return 0;
  319. }
  320. static void hotkey_exit(struct ibm_struct *ibm)
  321. {
  322. hotkey_set(ibm, ibm->state.hotkey.status, ibm->state.hotkey.mask);
  323. }
  324. static void hotkey_notify(struct ibm_struct *ibm, u32 event)
  325. {
  326. int hkey;
  327. if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d"))
  328. acpi_bus_generate_event(ibm->device, event, hkey);
  329. else {
  330. printk(IBM_ERR "unknown hotkey event %d\n", event);
  331. acpi_bus_generate_event(ibm->device, event, 0);
  332. }
  333. }
  334. static int bluetooth_init(struct ibm_struct *ibm)
  335. {
  336. /* bluetooth not supported on A21e, G40, T20, T21, X20 */
  337. ibm->supported = acpi_evalf(hkey_handle, NULL, "GBDC", "qv");
  338. return 0;
  339. }
  340. static int bluetooth_status(struct ibm_struct *ibm)
  341. {
  342. int status;
  343. if (!ibm->supported || !acpi_evalf(hkey_handle, &status, "GBDC", "d"))
  344. status = 0;
  345. return status;
  346. }
  347. static int bluetooth_read(struct ibm_struct *ibm, char *p)
  348. {
  349. int len = 0;
  350. int status = bluetooth_status(ibm);
  351. if (!ibm->supported)
  352. len += sprintf(p + len, "status:\t\tnot supported\n");
  353. else if (!(status & 1))
  354. len += sprintf(p + len, "status:\t\tnot installed\n");
  355. else {
  356. len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1));
  357. len += sprintf(p + len, "commands:\tenable, disable\n");
  358. }
  359. return len;
  360. }
  361. static int bluetooth_write(struct ibm_struct *ibm, char *buf)
  362. {
  363. int status = bluetooth_status(ibm);
  364. char *cmd;
  365. int do_cmd = 0;
  366. if (!ibm->supported)
  367. return -EINVAL;
  368. while ((cmd = next_cmd(&buf))) {
  369. if (strlencmp(cmd, "enable") == 0) {
  370. status |= 2;
  371. } else if (strlencmp(cmd, "disable") == 0) {
  372. status &= ~2;
  373. } else
  374. return -EINVAL;
  375. do_cmd = 1;
  376. }
  377. if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
  378. return -EIO;
  379. return 0;
  380. }
  381. static int video_init(struct ibm_struct *ibm)
  382. {
  383. if (!acpi_evalf(vid_handle,
  384. &ibm->state.video.autoswitch, "^VDEE", "d"))
  385. return -ENODEV;
  386. return 0;
  387. }
  388. static int video_status(struct ibm_struct *ibm)
  389. {
  390. int status = 0;
  391. int i;
  392. acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1);
  393. if (acpi_evalf(NULL, &i, "\\VCDC", "d"))
  394. status |= 0x02 * i;
  395. acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0);
  396. if (acpi_evalf(NULL, &i, "\\VCDL", "d"))
  397. status |= 0x01 * i;
  398. if (acpi_evalf(NULL, &i, "\\VCDD", "d"))
  399. status |= 0x08 * i;
  400. if (acpi_evalf(vid_handle, &i, "^VDEE", "d"))
  401. status |= 0x10 * (i & 1);
  402. return status;
  403. }
  404. static int video_read(struct ibm_struct *ibm, char *p)
  405. {
  406. int status = video_status(ibm);
  407. int len = 0;
  408. len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));
  409. len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));
  410. len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));
  411. len += sprintf(p + len, "auto:\t\t%s\n", enabled(status, 4));
  412. len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable, "
  413. "crt_enable, crt_disable\n");
  414. len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable, "
  415. "auto_enable, auto_disable\n");
  416. len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
  417. return len;
  418. }
  419. static int video_write(struct ibm_struct *ibm, char *buf)
  420. {
  421. char *cmd;
  422. int enable, disable, status;
  423. enable = disable = 0;
  424. while ((cmd = next_cmd(&buf))) {
  425. if (strlencmp(cmd, "lcd_enable") == 0) {
  426. enable |= 0x01;
  427. } else if (strlencmp(cmd, "lcd_disable") == 0) {
  428. disable |= 0x01;
  429. } else if (strlencmp(cmd, "crt_enable") == 0) {
  430. enable |= 0x02;
  431. } else if (strlencmp(cmd, "crt_disable") == 0) {
  432. disable |= 0x02;
  433. } else if (strlencmp(cmd, "dvi_enable") == 0) {
  434. enable |= 0x08;
  435. } else if (strlencmp(cmd, "dvi_disable") == 0) {
  436. disable |= 0x08;
  437. } else if (strlencmp(cmd, "auto_enable") == 0) {
  438. if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
  439. return -EIO;
  440. } else if (strlencmp(cmd, "auto_disable") == 0) {
  441. if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 0))
  442. return -EIO;
  443. } else if (strlencmp(cmd, "video_switch") == 0) {
  444. int autoswitch;
  445. if (!acpi_evalf(vid_handle, &autoswitch, "^VDEE", "d"))
  446. return -EIO;
  447. if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
  448. return -EIO;
  449. if (!acpi_evalf(vid_handle, NULL, "VSWT", "v"))
  450. return -EIO;
  451. if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd",
  452. autoswitch))
  453. return -EIO;
  454. } else if (strlencmp(cmd, "expand_toggle") == 0) {
  455. if (!acpi_evalf(NULL, NULL, "\\VEXP", "v"))
  456. return -EIO;
  457. } else
  458. return -EINVAL;
  459. }
  460. if (enable || disable) {
  461. status = (video_status(ibm) & 0x0f & ~disable) | enable;
  462. if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80))
  463. return -EIO;
  464. if (!acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1))
  465. return -EIO;
  466. }
  467. return 0;
  468. }
  469. static void video_exit(struct ibm_struct *ibm)
  470. {
  471. acpi_evalf(vid_handle, NULL, "_DOS", "vd",
  472. ibm->state.video.autoswitch);
  473. }
  474. static int light_init(struct ibm_struct *ibm)
  475. {
  476. /* kblt not supported on G40, R32, X20 */
  477. ibm->supported = acpi_evalf(ec_handle, NULL, "KBLT", "qv");
  478. return 0;
  479. }
  480. static int light_read(struct ibm_struct *ibm, char *p)
  481. {
  482. int len = 0;
  483. int status = 0;
  484. if (ibm->supported) {
  485. if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
  486. return -EIO;
  487. len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
  488. } else
  489. len += sprintf(p + len, "status:\t\tunknown\n");
  490. len += sprintf(p + len, "commands:\ton, off\n");
  491. return len;
  492. }
  493. static int light_write(struct ibm_struct *ibm, char *buf)
  494. {
  495. int cmos_cmd, lght_cmd;
  496. char *cmd;
  497. int success;
  498. while ((cmd = next_cmd(&buf))) {
  499. if (strlencmp(cmd, "on") == 0) {
  500. cmos_cmd = 0x0c;
  501. lght_cmd = 1;
  502. } else if (strlencmp(cmd, "off") == 0) {
  503. cmos_cmd = 0x0d;
  504. lght_cmd = 0;
  505. } else
  506. return -EINVAL;
  507. success = cmos_handle ?
  508. acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) :
  509. acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd);
  510. if (!success)
  511. return -EIO;
  512. }
  513. return 0;
  514. }
  515. static int _sta(acpi_handle handle)
  516. {
  517. int status;
  518. if (!handle || !acpi_evalf(handle, &status, "_STA", "d"))
  519. status = 0;
  520. return status;
  521. }
  522. #define dock_docked() (_sta(dock_handle) & 1)
  523. static int dock_read(struct ibm_struct *ibm, char *p)
  524. {
  525. int len = 0;
  526. int docked = dock_docked();
  527. if (!dock_handle)
  528. len += sprintf(p + len, "status:\t\tnot supported\n");
  529. else if (!docked)
  530. len += sprintf(p + len, "status:\t\tundocked\n");
  531. else {
  532. len += sprintf(p + len, "status:\t\tdocked\n");
  533. len += sprintf(p + len, "commands:\tdock, undock\n");
  534. }
  535. return len;
  536. }
  537. static int dock_write(struct ibm_struct *ibm, char *buf)
  538. {
  539. char *cmd;
  540. if (!dock_docked())
  541. return -EINVAL;
  542. while ((cmd = next_cmd(&buf))) {
  543. if (strlencmp(cmd, "undock") == 0) {
  544. if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0))
  545. return -EIO;
  546. if (!acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
  547. return -EIO;
  548. } else if (strlencmp(cmd, "dock") == 0) {
  549. if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1))
  550. return -EIO;
  551. } else
  552. return -EINVAL;
  553. }
  554. return 0;
  555. }
  556. static void dock_notify(struct ibm_struct *ibm, u32 event)
  557. {
  558. int docked = dock_docked();
  559. if (event == 3 && docked)
  560. acpi_bus_generate_event(ibm->device, event, 1); /* button */
  561. else if (event == 3 && !docked)
  562. acpi_bus_generate_event(ibm->device, event, 2); /* undock */
  563. else if (event == 0 && docked)
  564. acpi_bus_generate_event(ibm->device, event, 3); /* dock */
  565. else {
  566. printk(IBM_ERR "unknown dock event %d, status %d\n",
  567. event, _sta(dock_handle));
  568. acpi_bus_generate_event(ibm->device, event, 0); /* unknown */
  569. }
  570. }
  571. #define bay_occupied() (_sta(bay_handle) & 1)
  572. static int bay_init(struct ibm_struct *ibm)
  573. {
  574. /* bay not supported on A21e, A22p, A31, A31p, G40, R32, R40e */
  575. ibm->supported = bay_handle && bayej_handle &&
  576. acpi_evalf(bay_handle, NULL, "_STA", "qv");
  577. return 0;
  578. }
  579. static int bay_read(struct ibm_struct *ibm, char *p)
  580. {
  581. int len = 0;
  582. int occupied = bay_occupied();
  583. if (!ibm->supported)
  584. len += sprintf(p + len, "status:\t\tnot supported\n");
  585. else if (!occupied)
  586. len += sprintf(p + len, "status:\t\tunoccupied\n");
  587. else {
  588. len += sprintf(p + len, "status:\t\toccupied\n");
  589. len += sprintf(p + len, "commands:\teject\n");
  590. }
  591. return len;
  592. }
  593. static int bay_write(struct ibm_struct *ibm, char *buf)
  594. {
  595. char *cmd;
  596. while ((cmd = next_cmd(&buf))) {
  597. if (strlencmp(cmd, "eject") == 0) {
  598. if (!ibm->supported ||
  599. !acpi_evalf(bay_handle, NULL, "_EJ0", "vd", 1))
  600. return -EIO;
  601. } else
  602. return -EINVAL;
  603. }
  604. return 0;
  605. }
  606. static void bay_notify(struct ibm_struct *ibm, u32 event)
  607. {
  608. acpi_bus_generate_event(ibm->device, event, 0);
  609. }
  610. static int cmos_read(struct ibm_struct *ibm, char *p)
  611. {
  612. int len = 0;
  613. /* cmos not supported on A21e, A22p, T20, T21, X20 */
  614. if (!cmos_handle)
  615. len += sprintf(p + len, "status:\t\tnot supported\n");
  616. else {
  617. len += sprintf(p + len, "status:\t\tsupported\n");
  618. len += sprintf(p + len, "commands:\t<int>\n");
  619. }
  620. return len;
  621. }
  622. static int cmos_write(struct ibm_struct *ibm, char *buf)
  623. {
  624. char *cmd;
  625. int cmos_cmd;
  626. if (!cmos_handle)
  627. return -EINVAL;
  628. while ((cmd = next_cmd(&buf))) {
  629. if (sscanf(cmd, "%u", &cmos_cmd) == 1) {
  630. /* cmos_cmd set */
  631. } else
  632. return -EINVAL;
  633. if (!acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd))
  634. return -EIO;
  635. }
  636. return 0;
  637. }
  638. static int led_read(struct ibm_struct *ibm, char *p)
  639. {
  640. int len = 0;
  641. len += sprintf(p + len, "commands:\t"
  642. "<int> on, <int> off, <int> blink\n");
  643. return len;
  644. }
  645. static int led_write(struct ibm_struct *ibm, char *buf)
  646. {
  647. char *cmd;
  648. unsigned int led;
  649. int led_cmd, sysl_cmd, bled_a, bled_b;
  650. while ((cmd = next_cmd(&buf))) {
  651. if (sscanf(cmd, "%u", &led) != 1)
  652. return -EINVAL;
  653. if (strstr(cmd, "blink")) {
  654. led_cmd = 0xc0;
  655. sysl_cmd = 2;
  656. bled_a = 2;
  657. bled_b = 1;
  658. } else if (strstr(cmd, "on")) {
  659. led_cmd = 0x80;
  660. sysl_cmd = 1;
  661. bled_a = 2;
  662. bled_b = 0;
  663. } else if (strstr(cmd, "off")) {
  664. led_cmd = sysl_cmd = bled_a = bled_b = 0;
  665. } else
  666. return -EINVAL;
  667. if (led_handle) {
  668. if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
  669. led, led_cmd))
  670. return -EIO;
  671. } else if (led < 2) {
  672. if (acpi_evalf(sysl_handle, NULL, NULL, "vdd",
  673. led, sysl_cmd))
  674. return -EIO;
  675. } else if (led == 2 && bled_handle) {
  676. if (acpi_evalf(bled_handle, NULL, NULL, "vdd",
  677. bled_a, bled_b))
  678. return -EIO;
  679. } else
  680. return -EINVAL;
  681. }
  682. return 0;
  683. }
  684. static int beep_read(struct ibm_struct *ibm, char *p)
  685. {
  686. int len = 0;
  687. len += sprintf(p + len, "commands:\t<int>\n");
  688. return len;
  689. }
  690. static int beep_write(struct ibm_struct *ibm, char *buf)
  691. {
  692. char *cmd;
  693. int beep_cmd;
  694. while ((cmd = next_cmd(&buf))) {
  695. if (sscanf(cmd, "%u", &beep_cmd) == 1) {
  696. /* beep_cmd set */
  697. } else
  698. return -EINVAL;
  699. if (!acpi_evalf(beep_handle, NULL, NULL, "vd", beep_cmd))
  700. return -EIO;
  701. }
  702. return 0;
  703. }
  704. static struct ibm_struct ibms[] = {
  705. {
  706. .name = "driver",
  707. .init = driver_init,
  708. .read = driver_read,
  709. },
  710. {
  711. .name = "hotkey",
  712. .hid = "IBM0068",
  713. .init = hotkey_init,
  714. .read = hotkey_read,
  715. .write = hotkey_write,
  716. .exit = hotkey_exit,
  717. .notify = hotkey_notify,
  718. .handle = &hkey_handle,
  719. .type = ACPI_DEVICE_NOTIFY,
  720. },
  721. {
  722. .name = "bluetooth",
  723. .init = bluetooth_init,
  724. .read = bluetooth_read,
  725. .write = bluetooth_write,
  726. },
  727. {
  728. .name = "video",
  729. .init = video_init,
  730. .read = video_read,
  731. .write = video_write,
  732. .exit = video_exit,
  733. },
  734. {
  735. .name = "light",
  736. .init = light_init,
  737. .read = light_read,
  738. .write = light_write,
  739. },
  740. {
  741. .name = "dock",
  742. .read = dock_read,
  743. .write = dock_write,
  744. .notify = dock_notify,
  745. .handle = &dock_handle,
  746. .type = ACPI_SYSTEM_NOTIFY,
  747. },
  748. {
  749. .name = "bay",
  750. .init = bay_init,
  751. .read = bay_read,
  752. .write = bay_write,
  753. .notify = bay_notify,
  754. .handle = &bay_handle,
  755. .type = ACPI_SYSTEM_NOTIFY,
  756. },
  757. {
  758. .name = "cmos",
  759. .read = cmos_read,
  760. .write = cmos_write,
  761. .experimental = 1,
  762. },
  763. {
  764. .name = "led",
  765. .read = led_read,
  766. .write = led_write,
  767. .experimental = 1,
  768. },
  769. {
  770. .name = "beep",
  771. .read = beep_read,
  772. .write = beep_write,
  773. .experimental = 1,
  774. },
  775. };
  776. #define NUM_IBMS (sizeof(ibms)/sizeof(ibms[0]))
  777. static int dispatch_read(char *page, char **start, off_t off, int count,
  778. int *eof, void *data)
  779. {
  780. struct ibm_struct *ibm = (struct ibm_struct *)data;
  781. int len;
  782. if (!ibm || !ibm->read)
  783. return -EINVAL;
  784. len = ibm->read(ibm, page);
  785. if (len < 0)
  786. return len;
  787. if (len <= off + count)
  788. *eof = 1;
  789. *start = page + off;
  790. len -= off;
  791. if (len > count)
  792. len = count;
  793. if (len < 0)
  794. len = 0;
  795. return len;
  796. }
  797. static int dispatch_write(struct file *file, const char __user *userbuf,
  798. unsigned long count, void *data)
  799. {
  800. struct ibm_struct *ibm = (struct ibm_struct *)data;
  801. char *kernbuf;
  802. int ret;
  803. if (!ibm || !ibm->write)
  804. return -EINVAL;
  805. kernbuf = kmalloc(count + 2, GFP_KERNEL);
  806. if (!kernbuf)
  807. return -ENOMEM;
  808. if (copy_from_user(kernbuf, userbuf, count)) {
  809. kfree(kernbuf);
  810. return -EFAULT;
  811. }
  812. kernbuf[count] = 0;
  813. strcat(kernbuf, ",");
  814. ret = ibm->write(ibm, kernbuf);
  815. if (ret == 0)
  816. ret = count;
  817. kfree(kernbuf);
  818. return ret;
  819. }
  820. static void dispatch_notify(acpi_handle handle, u32 event, void *data)
  821. {
  822. struct ibm_struct *ibm = (struct ibm_struct *)data;
  823. if (!ibm || !ibm->notify)
  824. return;
  825. ibm->notify(ibm, event);
  826. }
  827. static int setup_notify(struct ibm_struct *ibm)
  828. {
  829. acpi_status status;
  830. int ret;
  831. if (!*ibm->handle)
  832. return 0;
  833. ret = acpi_bus_get_device(*ibm->handle, &ibm->device);
  834. if (ret < 0) {
  835. printk(IBM_ERR "%s device not present\n", ibm->name);
  836. return 0;
  837. }
  838. acpi_driver_data(ibm->device) = ibm;
  839. sprintf(acpi_device_class(ibm->device), "%s/%s", IBM_NAME, ibm->name);
  840. status = acpi_install_notify_handler(*ibm->handle, ibm->type,
  841. dispatch_notify, ibm);
  842. if (ACPI_FAILURE(status)) {
  843. printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n",
  844. ibm->name, status);
  845. return -ENODEV;
  846. }
  847. ibm->notify_installed = 1;
  848. return 0;
  849. }
  850. static int ibmacpi_device_add(struct acpi_device *device)
  851. {
  852. return 0;
  853. }
  854. static int register_driver(struct ibm_struct *ibm)
  855. {
  856. int ret;
  857. ibm->driver = kmalloc(sizeof(struct acpi_driver), GFP_KERNEL);
  858. if (!ibm->driver) {
  859. printk(IBM_ERR "kmalloc(ibm->driver) failed\n");
  860. return -1;
  861. }
  862. memset(ibm->driver, 0, sizeof(struct acpi_driver));
  863. sprintf(ibm->driver->name, "%s/%s", IBM_NAME, ibm->name);
  864. ibm->driver->ids = ibm->hid;
  865. ibm->driver->ops.add = &ibmacpi_device_add;
  866. ret = acpi_bus_register_driver(ibm->driver);
  867. if (ret < 0) {
  868. printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n",
  869. ibm->hid, ret);
  870. kfree(ibm->driver);
  871. }
  872. return ret;
  873. }
  874. static int ibm_init(struct ibm_struct *ibm)
  875. {
  876. int ret;
  877. struct proc_dir_entry *entry;
  878. if (ibm->experimental && !experimental)
  879. return 0;
  880. if (ibm->hid) {
  881. ret = register_driver(ibm);
  882. if (ret < 0)
  883. return ret;
  884. ibm->driver_registered = 1;
  885. }
  886. if (ibm->init) {
  887. ret = ibm->init(ibm);
  888. if (ret != 0)
  889. return ret;
  890. ibm->init_called = 1;
  891. }
  892. entry = create_proc_entry(ibm->name, S_IFREG | S_IRUGO | S_IWUSR,
  893. proc_dir);
  894. if (!entry) {
  895. printk(IBM_ERR "unable to create proc entry %s\n", ibm->name);
  896. return -ENODEV;
  897. }
  898. entry->owner = THIS_MODULE;
  899. ibm->proc_created = 1;
  900. entry->data = ibm;
  901. if (ibm->read)
  902. entry->read_proc = &dispatch_read;
  903. if (ibm->write)
  904. entry->write_proc = &dispatch_write;
  905. if (ibm->notify) {
  906. ret = setup_notify(ibm);
  907. if (ret < 0)
  908. return ret;
  909. }
  910. return 0;
  911. }
  912. static void ibm_exit(struct ibm_struct *ibm)
  913. {
  914. if (ibm->notify_installed)
  915. acpi_remove_notify_handler(*ibm->handle, ibm->type,
  916. dispatch_notify);
  917. if (ibm->proc_created)
  918. remove_proc_entry(ibm->name, proc_dir);
  919. if (ibm->init_called && ibm->exit)
  920. ibm->exit(ibm);
  921. if (ibm->driver_registered) {
  922. acpi_bus_unregister_driver(ibm->driver);
  923. kfree(ibm->driver);
  924. }
  925. }
  926. static int ibm_handle_init(char *name,
  927. acpi_handle *handle, acpi_handle parent,
  928. char **paths, int num_paths, int required)
  929. {
  930. int i;
  931. acpi_status status;
  932. for (i=0; i<num_paths; i++) {
  933. status = acpi_get_handle(parent, paths[i], handle);
  934. if (ACPI_SUCCESS(status))
  935. return 0;
  936. }
  937. *handle = NULL;
  938. if (required) {
  939. printk(IBM_ERR "%s object not found\n", name);
  940. return -1;
  941. }
  942. return 0;
  943. }
  944. #define IBM_HANDLE_INIT(object, required) \
  945. ibm_handle_init(#object, &object##_handle, *object##_parent, \
  946. object##_paths, sizeof(object##_paths)/sizeof(char*), required)
  947. static int set_ibm_param(const char *val, struct kernel_param *kp)
  948. {
  949. unsigned int i;
  950. char arg_with_comma[32];
  951. if (strlen(val) > 30)
  952. return -ENOSPC;
  953. strcpy(arg_with_comma, val);
  954. strcat(arg_with_comma, ",");
  955. for (i=0; i<NUM_IBMS; i++)
  956. if (strcmp(ibms[i].name, kp->name) == 0)
  957. return ibms[i].write(&ibms[i], arg_with_comma);
  958. BUG();
  959. return -EINVAL;
  960. }
  961. #define IBM_PARAM(feature) \
  962. module_param_call(feature, set_ibm_param, NULL, NULL, 0)
  963. static void acpi_ibm_exit(void)
  964. {
  965. int i;
  966. for (i=NUM_IBMS-1; i>=0; i--)
  967. ibm_exit(&ibms[i]);
  968. remove_proc_entry(IBM_DIR, acpi_root_dir);
  969. }
  970. static int __init acpi_ibm_init(void)
  971. {
  972. int ret, i;
  973. if (acpi_disabled)
  974. return -ENODEV;
  975. if (!acpi_specific_hotkey_enabled){
  976. printk(IBM_ERR "Using generic hotkey driver\n");
  977. return -ENODEV;
  978. }
  979. /* these handles are required */
  980. if (IBM_HANDLE_INIT(ec, 1) < 0 ||
  981. IBM_HANDLE_INIT(hkey, 1) < 0 ||
  982. IBM_HANDLE_INIT(vid, 1) < 0 ||
  983. IBM_HANDLE_INIT(beep, 1) < 0)
  984. return -ENODEV;
  985. /* these handles have alternatives */
  986. IBM_HANDLE_INIT(lght, 0);
  987. if (IBM_HANDLE_INIT(cmos, !lght_handle) < 0)
  988. return -ENODEV;
  989. IBM_HANDLE_INIT(sysl, 0);
  990. if (IBM_HANDLE_INIT(led, !sysl_handle) < 0)
  991. return -ENODEV;
  992. /* these handles are not required */
  993. IBM_HANDLE_INIT(dock, 0);
  994. IBM_HANDLE_INIT(bay, 0);
  995. IBM_HANDLE_INIT(bayej, 0);
  996. IBM_HANDLE_INIT(bled, 0);
  997. proc_dir = proc_mkdir(IBM_DIR, acpi_root_dir);
  998. if (!proc_dir) {
  999. printk(IBM_ERR "unable to create proc dir %s", IBM_DIR);
  1000. return -ENODEV;
  1001. }
  1002. proc_dir->owner = THIS_MODULE;
  1003. for (i=0; i<NUM_IBMS; i++) {
  1004. ret = ibm_init(&ibms[i]);
  1005. if (ret < 0) {
  1006. acpi_ibm_exit();
  1007. return ret;
  1008. }
  1009. }
  1010. return 0;
  1011. }
  1012. module_init(acpi_ibm_init);
  1013. module_exit(acpi_ibm_exit);
  1014. MODULE_AUTHOR("Borislav Deianov");
  1015. MODULE_DESCRIPTION(IBM_DESC);
  1016. MODULE_LICENSE("GPL");
  1017. IBM_PARAM(hotkey);
  1018. IBM_PARAM(bluetooth);
  1019. IBM_PARAM(video);
  1020. IBM_PARAM(light);
  1021. IBM_PARAM(dock);
  1022. IBM_PARAM(bay);
  1023. IBM_PARAM(cmos);
  1024. IBM_PARAM(led);
  1025. IBM_PARAM(beep);