PageRenderTime 107ms CodeModel.GetById 13ms app.highlight 86ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/acpi/ibm_acpi.c

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