ibm_acpi.c 27 KB

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