Newer
Older
* Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define IBM_VERSION "0.12a"
/*
*
* 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels
* 2005-03-17 0.11 support for 600e, 770x
* thanks to Jamie Lentin <lentinj@dial.pipex.com>
* support for 770e, G41
* G40 and G41 don't have a thinklight
* temperatures no longer experimental
* experimental brightness control
* experimental volume control
* experimental fan enable/disable
* 2005-01-16 0.10 fix module loading on R30, R31
* 2005-01-16 0.9 support for 570, R30, R31
* ultrabay support on A22p, A3x
* limit arg for cmos, led, beep, drop experimental status
* more capable led control on A21e, A22p, T20-22, X20
* experimental temperatures and fan speed
* experimental embedded controller register dump
* mark more functions as __init, drop incorrect __exit
* use MODULE_VERSION
* thanks to Henrik Brix Andersen <brix@gentoo.org>
* fix parameter passing on module loading
* thanks to Rusty Russell <rusty@rustcorp.com.au>
* thanks to Jim Radford <radford@blackbean.org>
* 2004-11-08 0.8 fix init error case, don't return from a macro
* thanks to Chris Wright <chrisw@osdl.org>
* 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20
* fix led control on A21e
* 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device
* 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20
* proc file format changed
* video_switch command
* experimental cmos control
* experimental led control
* experimental acpi sounds
* 2004-09-16 0.4 support for module parameters
* hotkey mask can be prefixed by 0x
* video output switching
* video expansion control
* ultrabay eject support
* removed lcd brightness/on/off control, didn't work
* 2004-08-17 0.3 support for R40
* lcd off, brightness control
* thinklight on/off
* 2004-08-14 0.2 support for T series, X20
* bluetooth enable/disable
* hotkey events disabled by default
* removed fan control, currently useless
* 2004-08-09 0.1 initial release, support for X series
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acnamesp.h>
#define IBM_NAME "ibm"
#define IBM_DESC "IBM ThinkPad ACPI Extras"
#define IBM_FILE "ibm_acpi"
#define IBM_URL "http://ibm-acpi.sf.net/"
MODULE_AUTHOR("Borislav Deianov");
MODULE_DESCRIPTION(IBM_DESC);
MODULE_VERSION(IBM_VERSION);
MODULE_LICENSE("GPL");
#define IBM_DIR IBM_NAME
#define IBM_LOG IBM_FILE ": "
#define IBM_ERR KERN_ERR IBM_LOG
#define IBM_NOTICE KERN_NOTICE IBM_LOG
#define IBM_INFO KERN_INFO IBM_LOG
#define IBM_DEBUG KERN_DEBUG IBM_LOG
#define IBM_MAX_ACPI_ARGS 3
#define __unused __attribute__ ((unused))
static int experimental;
module_param(experimental, int, 0);
static acpi_handle root_handle = NULL;
#define IBM_HANDLE(object, parent, paths...) \
static acpi_handle object##_handle; \
static acpi_handle *object##_parent = &parent##_handle; \
static char *object##_path; \
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*
* The following models are supported to various degrees:
*
* 570, 600e, 600x, 770e, 770x
* A20m, A21e, A21m, A21p, A22p, A30, A30p, A31, A31p
* G40, G41
* R30, R31, R32, R40, R40e, R50, R50e, R50p, R51
* T20, T21, T22, T23, T30, T40, T40p, T41, T41p, T42, T42p, T43
* X20, X21, X22, X23, X24, X30, X31, X40
*
* The following models have no supported features:
*
* 240, 240x, i1400
*
* Still missing DSDTs for the following models:
*
* A20p, A22e, A22m
* R52
* S31
* T43p
*/
IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */
"\\_SB.PCI.ISA.EC", /* 570 */
"\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */
"\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */
"\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */
"\\_SB.PCI0.ICH3.EC0", /* R31 */
"\\_SB.PCI0.LPC.EC", /* all others */
);
IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */
"\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */
"\\_SB.PCI0.VID0", /* 770e */
"\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */
"\\_SB.PCI0.AGP.VID", /* all others */
); /* R30, R31 */
IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */
IBM_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, T4x, X31, X40 */
"\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */
"\\CMS", /* R40, R40e */
); /* all others */
#ifdef CONFIG_ACPI_IBM_DOCK
IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */
"\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */
"\\_SB.PCI0.PCI1.DOCK", /* all others */
"\\_SB.PCI.ISA.SLCE", /* 570 */
); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */
"\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */
"\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */
); /* A21e, R30, R31 */
IBM_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */
"_EJ0", /* all others */
); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */
IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */
"\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */
); /* all others */
IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */
"_EJ0", /* 770x */
); /* all others */
/* don't list other alternatives as we install a notify handler on the 570 */
IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */
IBM_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */
"^HKEY", /* R30, R31 */
"HKEY", /* all others */
); /* 570 */
IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */
IBM_HANDLE(ledb, ec, "LEDB"); /* G4x */
IBM_HANDLE(led, ec, "SLED", /* 570 */
"SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */
"LED", /* all others */
); /* R30, R31 */
IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */
IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */
IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */
IBM_HANDLE(fans, ec, "FANS"); /* X31, X40 */
IBM_HANDLE(gfan, ec, "GFAN", /* 570 */
"\\FSPD", /* 600e/x, 770e, 770x */
); /* all others */
IBM_HANDLE(sfan, ec, "SFAN", /* 570 */
"JFNS", /* 770x-JL */
); /* all others */
#define IBM_HKEY_HID "IBM0068"
#define IBM_PCI_HID "PNP0A03"
int (*init) (void);
int (*read) (char *);
int (*write) (char *);
void (*exit) (void);
void (*notify) (struct ibm_struct *, u32);
acpi_handle *handle;
int type;
struct acpi_device *device;
int driver_registered;
int proc_created;
int init_called;
int notify_installed;
int experimental;
};
static struct proc_dir_entry *proc_dir = NULL;
#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off")
#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled")
#define strlencmp(a,b) (strncmp((a), (b), strlen(b)))
static int acpi_evalf(acpi_handle handle,
void *res, char *method, char *fmt, ...)
{
char *fmt0 = fmt;
struct acpi_object_list params;
union acpi_object in_objs[IBM_MAX_ACPI_ARGS];
struct acpi_buffer result, *resultp;
union acpi_object out_obj;
acpi_status status;
va_list ap;
char res_type;
int success;
int quiet;
if (!*fmt) {
printk(IBM_ERR "acpi_evalf() called with empty format\n");
return 0;
}
if (*fmt == 'q') {
quiet = 1;
fmt++;
} else
quiet = 0;
res_type = *(fmt++);
params.count = 0;
params.pointer = &in_objs[0];
va_start(ap, fmt);
while (*fmt) {
char c = *(fmt++);
switch (c) {
case 'd': /* int */
in_objs[params.count].integer.value = va_arg(ap, int);
in_objs[params.count++].type = ACPI_TYPE_INTEGER;
break;
/* add more types as needed */
default:
printk(IBM_ERR "acpi_evalf() called "
"with invalid format character '%c'\n", c);
return 0;
}
}
va_end(ap);
if (res_type != 'v') {
result.length = sizeof(out_obj);
result.pointer = &out_obj;
resultp = &result;
} else
resultp = NULL;
status = acpi_evaluate_object(handle, method, ¶ms, resultp);
if (res)
*(int *)res = out_obj.integer.value;
success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER;
break;
/* add more types as needed */
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
default:
printk(IBM_ERR "acpi_evalf() called "
"with invalid format character '%c'\n", res_type);
return 0;
}
if (!success && !quiet)
printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n",
method, fmt0, status);
return success;
}
static void __unused acpi_print_int(acpi_handle handle, char *method)
{
int i;
if (acpi_evalf(handle, &i, method, "d"))
printk(IBM_INFO "%s = 0x%x\n", method, i);
else
printk(IBM_ERR "error calling %s\n", method);
}
static char *next_cmd(char **cmds)
{
char *start = *cmds;
char *end;
while ((end = strchr(start, ',')) && end == start)
start = end + 1;
if (!end)
return NULL;
*end = 0;
*cmds = end + 1;
return start;
}
static int ibm_acpi_driver_init(void)
{
printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION);
printk(IBM_INFO "%s\n", IBM_URL);
return 0;
}
static int driver_read(char *p)
{
int len = 0;
len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC);
len += sprintf(p + len, "version:\t%s\n", IBM_VERSION);
return len;
}
static int hotkey_supported;
static int hotkey_mask_supported;
static int hotkey_orig_status;
static int hotkey_orig_mask;
static int hotkey_get(int *status, int *mask)
{
if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))
return 0;
if (hotkey_mask_supported)
if (!acpi_evalf(hkey_handle, mask, "DHKN", "d"))
return 0;
return 1;
static int hotkey_set(int status, int mask)
{
int i;
if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status))
return 0;
if (hotkey_mask_supported)
for (i = 0; i < 32; i++) {
int bit = ((1 << i) & mask) != 0;
if (!acpi_evalf(hkey_handle,
NULL, "MHKM", "vdd", i + 1, bit))
return 0;
}
static int hotkey_init(void)
/* hotkey not supported on 570 */
hotkey_supported = hkey_handle != NULL;
if (hotkey_supported) {
/* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
A30, R30, R31, T20-22, X20-21, X22-24 */
hotkey_mask_supported =
acpi_evalf(hkey_handle, NULL, "DHKN", "qv");
if (!hotkey_get(&hotkey_orig_status, &hotkey_orig_mask))
return -ENODEV;
static int hotkey_read(char *p)
if (!hotkey_supported) {
len += sprintf(p + len, "status:\t\tnot supported\n");
return len;
}
if (!hotkey_get(&status, &mask))
return -EIO;
len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));
if (hotkey_mask_supported) {
len += sprintf(p + len, "mask:\t\t0x%04x\n", mask);
len += sprintf(p + len,
"commands:\tenable, disable, reset, <mask>\n");
} else {
len += sprintf(p + len, "mask:\t\tnot supported\n");
len += sprintf(p + len, "commands:\tenable, disable, reset\n");
}
return len;
}
static int hotkey_write(char *buf)
{
int status, mask;
char *cmd;
int do_cmd = 0;
if (!hotkey_get(&status, &mask))
return -EIO;
while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "enable") == 0) {
status = 1;
} else if (strlencmp(cmd, "disable") == 0) {
status = 0;
} else if (strlencmp(cmd, "reset") == 0) {
status = hotkey_orig_status;
mask = hotkey_orig_mask;
} else if (sscanf(cmd, "0x%x", &mask) == 1) {
/* mask set */
} else if (sscanf(cmd, "%x", &mask) == 1) {
/* mask set */
} else
return -EINVAL;
do_cmd = 1;
}
if (do_cmd && !hotkey_set(status, mask))
static void hotkey_exit(void)
if (hotkey_supported)
hotkey_set(hotkey_orig_status, hotkey_orig_mask);
}
static void hotkey_notify(struct ibm_struct *ibm, u32 event)
{
int hkey;
if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d"))
acpi_bus_generate_event(ibm->device, event, hkey);
else {
printk(IBM_ERR "unknown hotkey event %d\n", event);
acpi_bus_generate_event(ibm->device, event, 0);
static int bluetooth_supported;
static int bluetooth_init(void)
/* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
G4x, R30, R31, R40e, R50e, T20-22, X20-21 */
bluetooth_supported = hkey_handle &&
acpi_evalf(hkey_handle, NULL, "GBDC", "qv");
static int bluetooth_status(void)
if (!bluetooth_supported ||
!acpi_evalf(hkey_handle, &status, "GBDC", "d"))
static int bluetooth_read(char *p)
int status = bluetooth_status();
if (!bluetooth_supported)
len += sprintf(p + len, "status:\t\tnot supported\n");
else if (!(status & 1))
len += sprintf(p + len, "status:\t\tnot installed\n");
else {
len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1));
len += sprintf(p + len, "commands:\tenable, disable\n");
}
return len;
}
static int bluetooth_write(char *buf)
int status = bluetooth_status();
if (!bluetooth_supported)
return -ENODEV;
while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "enable") == 0) {
status |= 2;
} else if (strlencmp(cmd, "disable") == 0) {
status &= ~2;
} else
return -EINVAL;
do_cmd = 1;
}
if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
static int wan_supported;
static int wan_init(void)
{
wan_supported = hkey_handle &&
acpi_evalf(hkey_handle, NULL, "GWAN", "qv");
return 0;
}
static int wan_status(void)
{
int status;
if (!wan_supported ||
!acpi_evalf(hkey_handle, &status, "GWAN", "d"))
status = 0;
return status;
}
static int wan_read(char *p)
{
int len = 0;
int status = wan_status();
if (!wan_supported)
len += sprintf(p + len, "status:\t\tnot supported\n");
else if (!(status & 1))
len += sprintf(p + len, "status:\t\tnot installed\n");
else {
len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1));
len += sprintf(p + len, "commands:\tenable, disable\n");
}
return len;
}
static int wan_write(char *buf)
{
int status = wan_status();
char *cmd;
int do_cmd = 0;
if (!wan_supported)
return -ENODEV;
while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "enable") == 0) {
status |= 2;
} else if (strlencmp(cmd, "disable") == 0) {
status &= ~2;
} else
return -EINVAL;
do_cmd = 1;
}
if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status))
return -EIO;
return 0;
}
static int video_supported;
static int video_orig_autosw;
#define VIDEO_570 1
#define VIDEO_770 2
#define VIDEO_NEW 3
static int video_init(void)
int ivga;
if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga)
/* G41, assume IVGA doesn't change */
vid_handle = vid2_handle;
if (!vid_handle)
/* video switching not supported on R30, R31 */
video_supported = 0;
else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd"))
/* 570 */
video_supported = VIDEO_570;
else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd"))
/* 600e/x, 770e, 770x */
video_supported = VIDEO_770;
else
/* all others */
video_supported = VIDEO_NEW;
static int video_status(void)
if (video_supported == VIDEO_570) {
if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87))
status = i & 3;
} else if (video_supported == VIDEO_770) {
if (acpi_evalf(NULL, &i, "\\VCDL", "d"))
status |= 0x01 * i;
if (acpi_evalf(NULL, &i, "\\VCDC", "d"))
status |= 0x02 * i;
} else if (video_supported == VIDEO_NEW) {
acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1);
if (acpi_evalf(NULL, &i, "\\VCDC", "d"))
status |= 0x02 * i;
acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0);
if (acpi_evalf(NULL, &i, "\\VCDL", "d"))
status |= 0x01 * i;
if (acpi_evalf(NULL, &i, "\\VCDD", "d"))
status |= 0x08 * i;
}
return status;
}
static int video_autosw(void)
{
int autosw = 0;
if (video_supported == VIDEO_570)
acpi_evalf(vid_handle, &autosw, "SWIT", "d");
else if (video_supported == VIDEO_770 || video_supported == VIDEO_NEW)
acpi_evalf(vid_handle, &autosw, "^VDEE", "d");
static int video_read(char *p)
int status = video_status();
int autosw = video_autosw();
if (!video_supported) {
len += sprintf(p + len, "status:\t\tnot supported\n");
return len;
}
len += sprintf(p + len, "status:\t\tsupported\n");
len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0));
len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1));
if (video_supported == VIDEO_NEW)
len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3));
len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0));
len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n");
len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n");
if (video_supported == VIDEO_NEW)
len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n");
len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n");
len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n");
return len;
}
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
static int video_switch(void)
{
int autosw = video_autosw();
int ret;
if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
return -EIO;
ret = video_supported == VIDEO_570 ?
acpi_evalf(ec_handle, NULL, "_Q16", "v") :
acpi_evalf(vid_handle, NULL, "VSWT", "v");
acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw);
return ret;
}
static int video_expand(void)
{
if (video_supported == VIDEO_570)
return acpi_evalf(ec_handle, NULL, "_Q17", "v");
else if (video_supported == VIDEO_770)
return acpi_evalf(vid_handle, NULL, "VEXP", "v");
else
return acpi_evalf(NULL, NULL, "\\VEXP", "v");
}
static int video_switch2(int status)
{
int ret;
if (video_supported == VIDEO_570) {
ret = acpi_evalf(NULL, NULL,
"\\_SB.PHS2", "vdd", 0x8b, status | 0x80);
} else if (video_supported == VIDEO_770) {
int autosw = video_autosw();
if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
return -EIO;
ret = acpi_evalf(vid_handle, NULL,
"ASWT", "vdd", status * 0x100, 0);
acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw);
} else {
ret = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) &&
acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1);
}
return ret;
}
static int video_write(char *buf)
if (!video_supported)
return -ENODEV;
enable = disable = 0;
while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "lcd_enable") == 0) {
enable |= 0x01;
} else if (strlencmp(cmd, "lcd_disable") == 0) {
disable |= 0x01;
} else if (strlencmp(cmd, "crt_enable") == 0) {
enable |= 0x02;
} else if (strlencmp(cmd, "crt_disable") == 0) {
disable |= 0x02;
} else if (video_supported == VIDEO_NEW &&
strlencmp(cmd, "dvi_enable") == 0) {
} else if (video_supported == VIDEO_NEW &&
strlencmp(cmd, "dvi_disable") == 0) {
disable |= 0x08;
} else if (strlencmp(cmd, "auto_enable") == 0) {
if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1))
return -EIO;
} else if (strlencmp(cmd, "auto_disable") == 0) {
if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 0))
return -EIO;
} else if (strlencmp(cmd, "video_switch") == 0) {
return -EIO;
} else if (strlencmp(cmd, "expand_toggle") == 0) {
return -EIO;
} else
return -EINVAL;
}
if (enable || disable) {
status = (video_status() & 0x0f & ~disable) | enable;
if (!video_switch2(status))
static void video_exit(void)
acpi_evalf(vid_handle, NULL, "_DOS", "vd", video_orig_autosw);
static int light_supported;
static int light_status_supported;
static int light_init(void)
/* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */
light_supported = (cmos_handle || lght_handle) && !ledb_handle;
if (light_supported)
/* light status not supported on
570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */
light_status_supported = acpi_evalf(ec_handle, NULL,
"KBLT", "qv");
static int light_read(char *p)
if (!light_supported) {
len += sprintf(p + len, "status:\t\tnot supported\n");
} else if (!light_status_supported) {
len += sprintf(p + len, "status:\t\tunknown\n");
len += sprintf(p + len, "commands:\ton, off\n");
} else {
if (!acpi_evalf(ec_handle, &status, "KBLT", "d"))
return -EIO;
len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0));
len += sprintf(p + len, "commands:\ton, off\n");
}
static int light_write(char *buf)
{
int cmos_cmd, lght_cmd;
char *cmd;
int success;
if (!light_supported)
return -ENODEV;
while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "on") == 0) {
cmos_cmd = 0x0c;
lght_cmd = 1;
} else if (strlencmp(cmd, "off") == 0) {
cmos_cmd = 0x0d;
lght_cmd = 0;
} else
return -EINVAL;
acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) :
acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd);
if (!success)
return -EIO;
}
return 0;
}
static int _sta(acpi_handle handle)
{
int status;
if (!handle || !acpi_evalf(handle, &status, "_STA", "d"))
status = 0;
return status;
}
#ifdef CONFIG_ACPI_IBM_DOCK
static int dock_read(char *p)
{
int len = 0;
int docked = dock_docked();
if (!dock_handle)
len += sprintf(p + len, "status:\t\tnot supported\n");
else if (!docked)
len += sprintf(p + len, "status:\t\tundocked\n");
else {
len += sprintf(p + len, "status:\t\tdocked\n");
len += sprintf(p + len, "commands:\tdock, undock\n");
}
return len;
}
static int dock_write(char *buf)
while ((cmd = next_cmd(&buf))) {
if (strlencmp(cmd, "undock") == 0) {
if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) ||
!acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1))
return -EIO;
} else if (strlencmp(cmd, "dock") == 0) {
if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1))
return -EIO;
} else
return -EINVAL;
}
return 0;
static void dock_notify(struct ibm_struct *ibm, u32 event)
{
int docked = dock_docked();
int pci = ibm->hid && strstr(ibm->hid, IBM_PCI_HID);
if (event == 1 && !pci) /* 570 */
acpi_bus_generate_event(ibm->device, event, 1); /* button */
else if (event == 1 && pci) /* 570 */
acpi_bus_generate_event(ibm->device, event, 3); /* dock */
else if (event == 3 && docked)
acpi_bus_generate_event(ibm->device, event, 1); /* button */
acpi_bus_generate_event(ibm->device, event, 2); /* undock */
acpi_bus_generate_event(ibm->device, event, 3); /* dock */
else {
printk(IBM_ERR "unknown dock event %d, status %d\n",
event, _sta(dock_handle));
acpi_bus_generate_event(ibm->device, event, 0); /* unknown */
static int bay_status_supported;
static int bay_status2_supported;
static int bay_eject_supported;
static int bay_eject2_supported;
static int bay_init(void)
bay_status_supported = bay_handle &&
acpi_evalf(bay_handle, NULL, "_STA", "qv");
bay_status2_supported = bay2_handle &&
acpi_evalf(bay2_handle, NULL, "_STA", "qv");
bay_eject_supported = bay_handle && bay_ej_handle &&
(strlencmp(bay_ej_path, "_EJ0") == 0 || experimental);
bay_eject2_supported = bay2_handle && bay2_ej_handle &&
(strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental);
#define bay_occupied(b) (_sta(b##_handle) & 1)
static int bay_read(char *p)
int occupied = bay_occupied(bay);