Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.15-rc1 1966 lines 45 kB view raw
1/* 2 * ibm_acpi.c - IBM ThinkPad ACPI Extras 3 * 4 * 5 * Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net> 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 22#define IBM_VERSION "0.12a" 23 24/* 25 * Changelog: 26 * 27 * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels 28 * 2005-03-17 0.11 support for 600e, 770x 29 * thanks to Jamie Lentin <lentinj@dial.pipex.com> 30 * support for 770e, G41 31 * G40 and G41 don't have a thinklight 32 * temperatures no longer experimental 33 * experimental brightness control 34 * experimental volume control 35 * experimental fan enable/disable 36 * 2005-01-16 0.10 fix module loading on R30, R31 37 * 2005-01-16 0.9 support for 570, R30, R31 38 * ultrabay support on A22p, A3x 39 * limit arg for cmos, led, beep, drop experimental status 40 * more capable led control on A21e, A22p, T20-22, X20 41 * experimental temperatures and fan speed 42 * experimental embedded controller register dump 43 * mark more functions as __init, drop incorrect __exit 44 * use MODULE_VERSION 45 * thanks to Henrik Brix Andersen <brix@gentoo.org> 46 * fix parameter passing on module loading 47 * thanks to Rusty Russell <rusty@rustcorp.com.au> 48 * thanks to Jim Radford <radford@blackbean.org> 49 * 2004-11-08 0.8 fix init error case, don't return from a macro 50 * thanks to Chris Wright <chrisw@osdl.org> 51 * 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20 52 * fix led control on A21e 53 * 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device 54 * 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20 55 * proc file format changed 56 * video_switch command 57 * experimental cmos control 58 * experimental led control 59 * experimental acpi sounds 60 * 2004-09-16 0.4 support for module parameters 61 * hotkey mask can be prefixed by 0x 62 * video output switching 63 * video expansion control 64 * ultrabay eject support 65 * removed lcd brightness/on/off control, didn't work 66 * 2004-08-17 0.3 support for R40 67 * lcd off, brightness control 68 * thinklight on/off 69 * 2004-08-14 0.2 support for T series, X20 70 * bluetooth enable/disable 71 * hotkey events disabled by default 72 * removed fan control, currently useless 73 * 2004-08-09 0.1 initial release, support for X series 74 */ 75 76#include <linux/kernel.h> 77#include <linux/module.h> 78#include <linux/init.h> 79#include <linux/types.h> 80#include <linux/proc_fs.h> 81#include <asm/uaccess.h> 82 83#include <acpi/acpi_drivers.h> 84#include <acpi/acnamesp.h> 85 86#define IBM_NAME "ibm" 87#define IBM_DESC "IBM ThinkPad ACPI Extras" 88#define IBM_FILE "ibm_acpi" 89#define IBM_URL "http://ibm-acpi.sf.net/" 90 91MODULE_AUTHOR("Borislav Deianov"); 92MODULE_DESCRIPTION(IBM_DESC); 93MODULE_VERSION(IBM_VERSION); 94MODULE_LICENSE("GPL"); 95 96#define IBM_DIR IBM_NAME 97 98#define IBM_LOG IBM_FILE ": " 99#define IBM_ERR KERN_ERR IBM_LOG 100#define IBM_NOTICE KERN_NOTICE IBM_LOG 101#define IBM_INFO KERN_INFO IBM_LOG 102#define IBM_DEBUG KERN_DEBUG IBM_LOG 103 104#define IBM_MAX_ACPI_ARGS 3 105 106#define __unused __attribute__ ((unused)) 107 108static int experimental; 109module_param(experimental, int, 0); 110 111static acpi_handle root_handle = NULL; 112 113#define IBM_HANDLE(object, parent, paths...) \ 114 static acpi_handle object##_handle; \ 115 static acpi_handle *object##_parent = &parent##_handle; \ 116 static char *object##_path; \ 117 static char *object##_paths[] = { paths } 118 119/* 120 * The following models are supported to various degrees: 121 * 122 * 570, 600e, 600x, 770e, 770x 123 * A20m, A21e, A21m, A21p, A22p, A30, A30p, A31, A31p 124 * G40, G41 125 * R30, R31, R32, R40, R40e, R50, R50e, R50p, R51 126 * T20, T21, T22, T23, T30, T40, T40p, T41, T41p, T42, T42p, T43 127 * X20, X21, X22, X23, X24, X30, X31, X40 128 * 129 * The following models have no supported features: 130 * 131 * 240, 240x, i1400 132 * 133 * Still missing DSDTs for the following models: 134 * 135 * A20p, A22e, A22m 136 * R52 137 * S31 138 * T43p 139 */ 140 141IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */ 142 "\\_SB.PCI.ISA.EC", /* 570 */ 143 "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */ 144 "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */ 145 "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */ 146 "\\_SB.PCI0.ICH3.EC0", /* R31 */ 147 "\\_SB.PCI0.LPC.EC", /* all others */ 148 ); 149 150IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */ 151 "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */ 152 "\\_SB.PCI0.VID0", /* 770e */ 153 "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */ 154 "\\_SB.PCI0.AGP.VID", /* all others */ 155 ); /* R30, R31 */ 156 157IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */ 158 159IBM_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, T4x, X31, X40 */ 160 "\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */ 161 "\\CMS", /* R40, R40e */ 162 ); /* all others */ 163 164IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */ 165 "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */ 166 "\\_SB.PCI0.PCI1.DOCK", /* all others */ 167 "\\_SB.PCI.ISA.SLCE", /* 570 */ 168 ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */ 169 170IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */ 171 "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */ 172 "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */ 173 ); /* A21e, R30, R31 */ 174 175IBM_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */ 176 "_EJ0", /* all others */ 177 ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */ 178 179IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */ 180 "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */ 181 ); /* all others */ 182 183IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */ 184 "_EJ0", /* 770x */ 185 ); /* all others */ 186 187/* don't list other alternatives as we install a notify handler on the 570 */ 188IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */ 189 190IBM_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */ 191 "^HKEY", /* R30, R31 */ 192 "HKEY", /* all others */ 193 ); /* 570 */ 194 195IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */ 196IBM_HANDLE(ledb, ec, "LEDB"); /* G4x */ 197 198IBM_HANDLE(led, ec, "SLED", /* 570 */ 199 "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ 200 "LED", /* all others */ 201 ); /* R30, R31 */ 202 203IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ 204IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */ 205IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */ 206IBM_HANDLE(fans, ec, "FANS"); /* X31, X40 */ 207 208IBM_HANDLE(gfan, ec, "GFAN", /* 570 */ 209 "\\FSPD", /* 600e/x, 770e, 770x */ 210 ); /* all others */ 211 212IBM_HANDLE(sfan, ec, "SFAN", /* 570 */ 213 "JFNS", /* 770x-JL */ 214 ); /* all others */ 215 216#define IBM_HKEY_HID "IBM0068" 217#define IBM_PCI_HID "PNP0A03" 218 219struct ibm_struct { 220 char *name; 221 char param[32]; 222 223 char *hid; 224 struct acpi_driver *driver; 225 226 int (*init) (void); 227 int (*read) (char *); 228 int (*write) (char *); 229 void (*exit) (void); 230 231 void (*notify) (struct ibm_struct *, u32); 232 acpi_handle *handle; 233 int type; 234 struct acpi_device *device; 235 236 int driver_registered; 237 int proc_created; 238 int init_called; 239 int notify_installed; 240 241 int experimental; 242}; 243 244static struct proc_dir_entry *proc_dir = NULL; 245 246#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") 247#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") 248#define strlencmp(a,b) (strncmp((a), (b), strlen(b))) 249 250static int acpi_evalf(acpi_handle handle, 251 void *res, char *method, char *fmt, ...) 252{ 253 char *fmt0 = fmt; 254 struct acpi_object_list params; 255 union acpi_object in_objs[IBM_MAX_ACPI_ARGS]; 256 struct acpi_buffer result, *resultp; 257 union acpi_object out_obj; 258 acpi_status status; 259 va_list ap; 260 char res_type; 261 int success; 262 int quiet; 263 264 if (!*fmt) { 265 printk(IBM_ERR "acpi_evalf() called with empty format\n"); 266 return 0; 267 } 268 269 if (*fmt == 'q') { 270 quiet = 1; 271 fmt++; 272 } else 273 quiet = 0; 274 275 res_type = *(fmt++); 276 277 params.count = 0; 278 params.pointer = &in_objs[0]; 279 280 va_start(ap, fmt); 281 while (*fmt) { 282 char c = *(fmt++); 283 switch (c) { 284 case 'd': /* int */ 285 in_objs[params.count].integer.value = va_arg(ap, int); 286 in_objs[params.count++].type = ACPI_TYPE_INTEGER; 287 break; 288 /* add more types as needed */ 289 default: 290 printk(IBM_ERR "acpi_evalf() called " 291 "with invalid format character '%c'\n", c); 292 return 0; 293 } 294 } 295 va_end(ap); 296 297 if (res_type != 'v') { 298 result.length = sizeof(out_obj); 299 result.pointer = &out_obj; 300 resultp = &result; 301 } else 302 resultp = NULL; 303 304 status = acpi_evaluate_object(handle, method, &params, resultp); 305 306 switch (res_type) { 307 case 'd': /* int */ 308 if (res) 309 *(int *)res = out_obj.integer.value; 310 success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER; 311 break; 312 case 'v': /* void */ 313 success = status == AE_OK; 314 break; 315 /* add more types as needed */ 316 default: 317 printk(IBM_ERR "acpi_evalf() called " 318 "with invalid format character '%c'\n", res_type); 319 return 0; 320 } 321 322 if (!success && !quiet) 323 printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n", 324 method, fmt0, status); 325 326 return success; 327} 328 329static void __unused acpi_print_int(acpi_handle handle, char *method) 330{ 331 int i; 332 333 if (acpi_evalf(handle, &i, method, "d")) 334 printk(IBM_INFO "%s = 0x%x\n", method, i); 335 else 336 printk(IBM_ERR "error calling %s\n", method); 337} 338 339static char *next_cmd(char **cmds) 340{ 341 char *start = *cmds; 342 char *end; 343 344 while ((end = strchr(start, ',')) && end == start) 345 start = end + 1; 346 347 if (!end) 348 return NULL; 349 350 *end = 0; 351 *cmds = end + 1; 352 return start; 353} 354 355static int driver_init(void) 356{ 357 printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION); 358 printk(IBM_INFO "%s\n", IBM_URL); 359 360 return 0; 361} 362 363static int driver_read(char *p) 364{ 365 int len = 0; 366 367 len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC); 368 len += sprintf(p + len, "version:\t%s\n", IBM_VERSION); 369 370 return len; 371} 372 373static int hotkey_supported; 374static int hotkey_mask_supported; 375static int hotkey_orig_status; 376static int hotkey_orig_mask; 377 378static int hotkey_get(int *status, int *mask) 379{ 380 if (!acpi_evalf(hkey_handle, status, "DHKC", "d")) 381 return 0; 382 383 if (hotkey_mask_supported) 384 if (!acpi_evalf(hkey_handle, mask, "DHKN", "d")) 385 return 0; 386 387 return 1; 388} 389 390static int hotkey_set(int status, int mask) 391{ 392 int i; 393 394 if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status)) 395 return 0; 396 397 if (hotkey_mask_supported) 398 for (i = 0; i < 32; i++) { 399 int bit = ((1 << i) & mask) != 0; 400 if (!acpi_evalf(hkey_handle, 401 NULL, "MHKM", "vdd", i + 1, bit)) 402 return 0; 403 } 404 405 return 1; 406} 407 408static int hotkey_init(void) 409{ 410 /* hotkey not supported on 570 */ 411 hotkey_supported = hkey_handle != NULL; 412 413 if (hotkey_supported) { 414 /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, 415 A30, R30, R31, T20-22, X20-21, X22-24 */ 416 hotkey_mask_supported = 417 acpi_evalf(hkey_handle, NULL, "DHKN", "qv"); 418 419 if (!hotkey_get(&hotkey_orig_status, &hotkey_orig_mask)) 420 return -ENODEV; 421 } 422 423 return 0; 424} 425 426static int hotkey_read(char *p) 427{ 428 int status, mask; 429 int len = 0; 430 431 if (!hotkey_supported) { 432 len += sprintf(p + len, "status:\t\tnot supported\n"); 433 return len; 434 } 435 436 if (!hotkey_get(&status, &mask)) 437 return -EIO; 438 439 len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); 440 if (hotkey_mask_supported) { 441 len += sprintf(p + len, "mask:\t\t0x%04x\n", mask); 442 len += sprintf(p + len, 443 "commands:\tenable, disable, reset, <mask>\n"); 444 } else { 445 len += sprintf(p + len, "mask:\t\tnot supported\n"); 446 len += sprintf(p + len, "commands:\tenable, disable, reset\n"); 447 } 448 449 return len; 450} 451 452static int hotkey_write(char *buf) 453{ 454 int status, mask; 455 char *cmd; 456 int do_cmd = 0; 457 458 if (!hotkey_supported) 459 return -ENODEV; 460 461 if (!hotkey_get(&status, &mask)) 462 return -EIO; 463 464 while ((cmd = next_cmd(&buf))) { 465 if (strlencmp(cmd, "enable") == 0) { 466 status = 1; 467 } else if (strlencmp(cmd, "disable") == 0) { 468 status = 0; 469 } else if (strlencmp(cmd, "reset") == 0) { 470 status = hotkey_orig_status; 471 mask = hotkey_orig_mask; 472 } else if (sscanf(cmd, "0x%x", &mask) == 1) { 473 /* mask set */ 474 } else if (sscanf(cmd, "%x", &mask) == 1) { 475 /* mask set */ 476 } else 477 return -EINVAL; 478 do_cmd = 1; 479 } 480 481 if (do_cmd && !hotkey_set(status, mask)) 482 return -EIO; 483 484 return 0; 485} 486 487static void hotkey_exit(void) 488{ 489 if (hotkey_supported) 490 hotkey_set(hotkey_orig_status, hotkey_orig_mask); 491} 492 493static void hotkey_notify(struct ibm_struct *ibm, u32 event) 494{ 495 int hkey; 496 497 if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) 498 acpi_bus_generate_event(ibm->device, event, hkey); 499 else { 500 printk(IBM_ERR "unknown hotkey event %d\n", event); 501 acpi_bus_generate_event(ibm->device, event, 0); 502 } 503} 504 505static int bluetooth_supported; 506 507static int bluetooth_init(void) 508{ 509 /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, 510 G4x, R30, R31, R40e, R50e, T20-22, X20-21 */ 511 bluetooth_supported = hkey_handle && 512 acpi_evalf(hkey_handle, NULL, "GBDC", "qv"); 513 514 return 0; 515} 516 517static int bluetooth_status(void) 518{ 519 int status; 520 521 if (!bluetooth_supported || 522 !acpi_evalf(hkey_handle, &status, "GBDC", "d")) 523 status = 0; 524 525 return status; 526} 527 528static int bluetooth_read(char *p) 529{ 530 int len = 0; 531 int status = bluetooth_status(); 532 533 if (!bluetooth_supported) 534 len += sprintf(p + len, "status:\t\tnot supported\n"); 535 else if (!(status & 1)) 536 len += sprintf(p + len, "status:\t\tnot installed\n"); 537 else { 538 len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1)); 539 len += sprintf(p + len, "commands:\tenable, disable\n"); 540 } 541 542 return len; 543} 544 545static int bluetooth_write(char *buf) 546{ 547 int status = bluetooth_status(); 548 char *cmd; 549 int do_cmd = 0; 550 551 if (!bluetooth_supported) 552 return -ENODEV; 553 554 while ((cmd = next_cmd(&buf))) { 555 if (strlencmp(cmd, "enable") == 0) { 556 status |= 2; 557 } else if (strlencmp(cmd, "disable") == 0) { 558 status &= ~2; 559 } else 560 return -EINVAL; 561 do_cmd = 1; 562 } 563 564 if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) 565 return -EIO; 566 567 return 0; 568} 569 570static int video_supported; 571static int video_orig_autosw; 572 573#define VIDEO_570 1 574#define VIDEO_770 2 575#define VIDEO_NEW 3 576 577static int video_init(void) 578{ 579 int ivga; 580 581 if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga) 582 /* G41, assume IVGA doesn't change */ 583 vid_handle = vid2_handle; 584 585 if (!vid_handle) 586 /* video switching not supported on R30, R31 */ 587 video_supported = 0; 588 else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) 589 /* 570 */ 590 video_supported = VIDEO_570; 591 else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) 592 /* 600e/x, 770e, 770x */ 593 video_supported = VIDEO_770; 594 else 595 /* all others */ 596 video_supported = VIDEO_NEW; 597 598 return 0; 599} 600 601static int video_status(void) 602{ 603 int status = 0; 604 int i; 605 606 if (video_supported == VIDEO_570) { 607 if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87)) 608 status = i & 3; 609 } else if (video_supported == VIDEO_770) { 610 if (acpi_evalf(NULL, &i, "\\VCDL", "d")) 611 status |= 0x01 * i; 612 if (acpi_evalf(NULL, &i, "\\VCDC", "d")) 613 status |= 0x02 * i; 614 } else if (video_supported == VIDEO_NEW) { 615 acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1); 616 if (acpi_evalf(NULL, &i, "\\VCDC", "d")) 617 status |= 0x02 * i; 618 619 acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0); 620 if (acpi_evalf(NULL, &i, "\\VCDL", "d")) 621 status |= 0x01 * i; 622 if (acpi_evalf(NULL, &i, "\\VCDD", "d")) 623 status |= 0x08 * i; 624 } 625 626 return status; 627} 628 629static int video_autosw(void) 630{ 631 int autosw = 0; 632 633 if (video_supported == VIDEO_570) 634 acpi_evalf(vid_handle, &autosw, "SWIT", "d"); 635 else if (video_supported == VIDEO_770 || video_supported == VIDEO_NEW) 636 acpi_evalf(vid_handle, &autosw, "^VDEE", "d"); 637 638 return autosw & 1; 639} 640 641static int video_read(char *p) 642{ 643 int status = video_status(); 644 int autosw = video_autosw(); 645 int len = 0; 646 647 if (!video_supported) { 648 len += sprintf(p + len, "status:\t\tnot supported\n"); 649 return len; 650 } 651 652 len += sprintf(p + len, "status:\t\tsupported\n"); 653 len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); 654 len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); 655 if (video_supported == VIDEO_NEW) 656 len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); 657 len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0)); 658 len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n"); 659 len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n"); 660 if (video_supported == VIDEO_NEW) 661 len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n"); 662 len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n"); 663 len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); 664 665 return len; 666} 667 668static int video_switch(void) 669{ 670 int autosw = video_autosw(); 671 int ret; 672 673 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) 674 return -EIO; 675 ret = video_supported == VIDEO_570 ? 676 acpi_evalf(ec_handle, NULL, "_Q16", "v") : 677 acpi_evalf(vid_handle, NULL, "VSWT", "v"); 678 acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw); 679 680 return ret; 681} 682 683static int video_expand(void) 684{ 685 if (video_supported == VIDEO_570) 686 return acpi_evalf(ec_handle, NULL, "_Q17", "v"); 687 else if (video_supported == VIDEO_770) 688 return acpi_evalf(vid_handle, NULL, "VEXP", "v"); 689 else 690 return acpi_evalf(NULL, NULL, "\\VEXP", "v"); 691} 692 693static int video_switch2(int status) 694{ 695 int ret; 696 697 if (video_supported == VIDEO_570) { 698 ret = acpi_evalf(NULL, NULL, 699 "\\_SB.PHS2", "vdd", 0x8b, status | 0x80); 700 } else if (video_supported == VIDEO_770) { 701 int autosw = video_autosw(); 702 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) 703 return -EIO; 704 705 ret = acpi_evalf(vid_handle, NULL, 706 "ASWT", "vdd", status * 0x100, 0); 707 708 acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw); 709 } else { 710 ret = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) && 711 acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1); 712 } 713 714 return ret; 715} 716 717static int video_write(char *buf) 718{ 719 char *cmd; 720 int enable, disable, status; 721 722 if (!video_supported) 723 return -ENODEV; 724 725 enable = disable = 0; 726 727 while ((cmd = next_cmd(&buf))) { 728 if (strlencmp(cmd, "lcd_enable") == 0) { 729 enable |= 0x01; 730 } else if (strlencmp(cmd, "lcd_disable") == 0) { 731 disable |= 0x01; 732 } else if (strlencmp(cmd, "crt_enable") == 0) { 733 enable |= 0x02; 734 } else if (strlencmp(cmd, "crt_disable") == 0) { 735 disable |= 0x02; 736 } else if (video_supported == VIDEO_NEW && 737 strlencmp(cmd, "dvi_enable") == 0) { 738 enable |= 0x08; 739 } else if (video_supported == VIDEO_NEW && 740 strlencmp(cmd, "dvi_disable") == 0) { 741 disable |= 0x08; 742 } else if (strlencmp(cmd, "auto_enable") == 0) { 743 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) 744 return -EIO; 745 } else if (strlencmp(cmd, "auto_disable") == 0) { 746 if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 0)) 747 return -EIO; 748 } else if (strlencmp(cmd, "video_switch") == 0) { 749 if (!video_switch()) 750 return -EIO; 751 } else if (strlencmp(cmd, "expand_toggle") == 0) { 752 if (!video_expand()) 753 return -EIO; 754 } else 755 return -EINVAL; 756 } 757 758 if (enable || disable) { 759 status = (video_status() & 0x0f & ~disable) | enable; 760 if (!video_switch2(status)) 761 return -EIO; 762 } 763 764 return 0; 765} 766 767static void video_exit(void) 768{ 769 acpi_evalf(vid_handle, NULL, "_DOS", "vd", video_orig_autosw); 770} 771 772static int light_supported; 773static int light_status_supported; 774 775static int light_init(void) 776{ 777 /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */ 778 light_supported = (cmos_handle || lght_handle) && !ledb_handle; 779 780 if (light_supported) 781 /* light status not supported on 782 570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */ 783 light_status_supported = acpi_evalf(ec_handle, NULL, 784 "KBLT", "qv"); 785 786 return 0; 787} 788 789static int light_read(char *p) 790{ 791 int len = 0; 792 int status = 0; 793 794 if (!light_supported) { 795 len += sprintf(p + len, "status:\t\tnot supported\n"); 796 } else if (!light_status_supported) { 797 len += sprintf(p + len, "status:\t\tunknown\n"); 798 len += sprintf(p + len, "commands:\ton, off\n"); 799 } else { 800 if (!acpi_evalf(ec_handle, &status, "KBLT", "d")) 801 return -EIO; 802 len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); 803 len += sprintf(p + len, "commands:\ton, off\n"); 804 } 805 806 return len; 807} 808 809static int light_write(char *buf) 810{ 811 int cmos_cmd, lght_cmd; 812 char *cmd; 813 int success; 814 815 if (!light_supported) 816 return -ENODEV; 817 818 while ((cmd = next_cmd(&buf))) { 819 if (strlencmp(cmd, "on") == 0) { 820 cmos_cmd = 0x0c; 821 lght_cmd = 1; 822 } else if (strlencmp(cmd, "off") == 0) { 823 cmos_cmd = 0x0d; 824 lght_cmd = 0; 825 } else 826 return -EINVAL; 827 828 success = cmos_handle ? 829 acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) : 830 acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd); 831 if (!success) 832 return -EIO; 833 } 834 835 return 0; 836} 837 838static int _sta(acpi_handle handle) 839{ 840 int status; 841 842 if (!handle || !acpi_evalf(handle, &status, "_STA", "d")) 843 status = 0; 844 845 return status; 846} 847 848#define dock_docked() (_sta(dock_handle) & 1) 849 850static int dock_read(char *p) 851{ 852 int len = 0; 853 int docked = dock_docked(); 854 855 if (!dock_handle) 856 len += sprintf(p + len, "status:\t\tnot supported\n"); 857 else if (!docked) 858 len += sprintf(p + len, "status:\t\tundocked\n"); 859 else { 860 len += sprintf(p + len, "status:\t\tdocked\n"); 861 len += sprintf(p + len, "commands:\tdock, undock\n"); 862 } 863 864 return len; 865} 866 867static int dock_write(char *buf) 868{ 869 char *cmd; 870 871 if (!dock_docked()) 872 return -ENODEV; 873 874 while ((cmd = next_cmd(&buf))) { 875 if (strlencmp(cmd, "undock") == 0) { 876 if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) || 877 !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1)) 878 return -EIO; 879 } else if (strlencmp(cmd, "dock") == 0) { 880 if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1)) 881 return -EIO; 882 } else 883 return -EINVAL; 884 } 885 886 return 0; 887} 888 889static void dock_notify(struct ibm_struct *ibm, u32 event) 890{ 891 int docked = dock_docked(); 892 int pci = ibm->hid && strstr(ibm->hid, IBM_PCI_HID); 893 894 if (event == 1 && !pci) /* 570 */ 895 acpi_bus_generate_event(ibm->device, event, 1); /* button */ 896 else if (event == 1 && pci) /* 570 */ 897 acpi_bus_generate_event(ibm->device, event, 3); /* dock */ 898 else if (event == 3 && docked) 899 acpi_bus_generate_event(ibm->device, event, 1); /* button */ 900 else if (event == 3 && !docked) 901 acpi_bus_generate_event(ibm->device, event, 2); /* undock */ 902 else if (event == 0 && docked) 903 acpi_bus_generate_event(ibm->device, event, 3); /* dock */ 904 else { 905 printk(IBM_ERR "unknown dock event %d, status %d\n", 906 event, _sta(dock_handle)); 907 acpi_bus_generate_event(ibm->device, event, 0); /* unknown */ 908 } 909} 910 911static int bay_status_supported; 912static int bay_status2_supported; 913static int bay_eject_supported; 914static int bay_eject2_supported; 915 916static int bay_init(void) 917{ 918 bay_status_supported = bay_handle && 919 acpi_evalf(bay_handle, NULL, "_STA", "qv"); 920 bay_status2_supported = bay2_handle && 921 acpi_evalf(bay2_handle, NULL, "_STA", "qv"); 922 923 bay_eject_supported = bay_handle && bay_ej_handle && 924 (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental); 925 bay_eject2_supported = bay2_handle && bay2_ej_handle && 926 (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental); 927 928 return 0; 929} 930 931#define bay_occupied(b) (_sta(b##_handle) & 1) 932 933static int bay_read(char *p) 934{ 935 int len = 0; 936 int occupied = bay_occupied(bay); 937 int occupied2 = bay_occupied(bay2); 938 int eject, eject2; 939 940 len += sprintf(p + len, "status:\t\t%s\n", bay_status_supported ? 941 (occupied ? "occupied" : "unoccupied") : 942 "not supported"); 943 if (bay_status2_supported) 944 len += sprintf(p + len, "status2:\t%s\n", occupied2 ? 945 "occupied" : "unoccupied"); 946 947 eject = bay_eject_supported && occupied; 948 eject2 = bay_eject2_supported && occupied2; 949 950 if (eject && eject2) 951 len += sprintf(p + len, "commands:\teject, eject2\n"); 952 else if (eject) 953 len += sprintf(p + len, "commands:\teject\n"); 954 else if (eject2) 955 len += sprintf(p + len, "commands:\teject2\n"); 956 957 return len; 958} 959 960static int bay_write(char *buf) 961{ 962 char *cmd; 963 964 if (!bay_eject_supported && !bay_eject2_supported) 965 return -ENODEV; 966 967 while ((cmd = next_cmd(&buf))) { 968 if (bay_eject_supported && strlencmp(cmd, "eject") == 0) { 969 if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1)) 970 return -EIO; 971 } else if (bay_eject2_supported && 972 strlencmp(cmd, "eject2") == 0) { 973 if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1)) 974 return -EIO; 975 } else 976 return -EINVAL; 977 } 978 979 return 0; 980} 981 982static void bay_notify(struct ibm_struct *ibm, u32 event) 983{ 984 acpi_bus_generate_event(ibm->device, event, 0); 985} 986 987static int cmos_read(char *p) 988{ 989 int len = 0; 990 991 /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, 992 R30, R31, T20-22, X20-21 */ 993 if (!cmos_handle) 994 len += sprintf(p + len, "status:\t\tnot supported\n"); 995 else { 996 len += sprintf(p + len, "status:\t\tsupported\n"); 997 len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n"); 998 } 999 1000 return len; 1001} 1002 1003static int cmos_eval(int cmos_cmd) 1004{ 1005 if (cmos_handle) 1006 return acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd); 1007 else 1008 return 1; 1009} 1010 1011static int cmos_write(char *buf) 1012{ 1013 char *cmd; 1014 int cmos_cmd; 1015 1016 if (!cmos_handle) 1017 return -EINVAL; 1018 1019 while ((cmd = next_cmd(&buf))) { 1020 if (sscanf(cmd, "%u", &cmos_cmd) == 1 && 1021 cmos_cmd >= 0 && cmos_cmd <= 21) { 1022 /* cmos_cmd set */ 1023 } else 1024 return -EINVAL; 1025 1026 if (!cmos_eval(cmos_cmd)) 1027 return -EIO; 1028 } 1029 1030 return 0; 1031} 1032 1033static int led_supported; 1034 1035#define LED_570 1 1036#define LED_OLD 2 1037#define LED_NEW 3 1038 1039static int led_init(void) 1040{ 1041 if (!led_handle) 1042 /* led not supported on R30, R31 */ 1043 led_supported = 0; 1044 else if (strlencmp(led_path, "SLED") == 0) 1045 /* 570 */ 1046 led_supported = LED_570; 1047 else if (strlencmp(led_path, "SYSL") == 0) 1048 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ 1049 led_supported = LED_OLD; 1050 else 1051 /* all others */ 1052 led_supported = LED_NEW; 1053 1054 return 0; 1055} 1056 1057#define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking")) 1058 1059static int led_read(char *p) 1060{ 1061 int len = 0; 1062 1063 if (!led_supported) { 1064 len += sprintf(p + len, "status:\t\tnot supported\n"); 1065 return len; 1066 } 1067 len += sprintf(p + len, "status:\t\tsupported\n"); 1068 1069 if (led_supported == LED_570) { 1070 /* 570 */ 1071 int i, status; 1072 for (i = 0; i < 8; i++) { 1073 if (!acpi_evalf(ec_handle, 1074 &status, "GLED", "dd", 1 << i)) 1075 return -EIO; 1076 len += sprintf(p + len, "%d:\t\t%s\n", 1077 i, led_status(status)); 1078 } 1079 } 1080 1081 len += sprintf(p + len, "commands:\t" 1082 "<led> on, <led> off, <led> blink (<led> is 0-7)\n"); 1083 1084 return len; 1085} 1086 1087/* off, on, blink */ 1088static const int led_sled_arg1[] = { 0, 1, 3 }; 1089static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */ 1090static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */ 1091static const int led_led_arg1[] = { 0, 0x80, 0xc0 }; 1092 1093#define EC_HLCL 0x0c 1094#define EC_HLBL 0x0d 1095#define EC_HLMS 0x0e 1096 1097static int led_write(char *buf) 1098{ 1099 char *cmd; 1100 int led, ind, ret; 1101 1102 if (!led_supported) 1103 return -ENODEV; 1104 1105 while ((cmd = next_cmd(&buf))) { 1106 if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7) 1107 return -EINVAL; 1108 1109 if (strstr(cmd, "off")) { 1110 ind = 0; 1111 } else if (strstr(cmd, "on")) { 1112 ind = 1; 1113 } else if (strstr(cmd, "blink")) { 1114 ind = 2; 1115 } else 1116 return -EINVAL; 1117 1118 if (led_supported == LED_570) { 1119 /* 570 */ 1120 led = 1 << led; 1121 if (!acpi_evalf(led_handle, NULL, NULL, "vdd", 1122 led, led_sled_arg1[ind])) 1123 return -EIO; 1124 } else if (led_supported == LED_OLD) { 1125 /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ 1126 led = 1 << led; 1127 ret = ec_write(EC_HLMS, led); 1128 if (ret >= 0) 1129 ret = 1130 ec_write(EC_HLBL, led * led_exp_hlbl[ind]); 1131 if (ret >= 0) 1132 ret = 1133 ec_write(EC_HLCL, led * led_exp_hlcl[ind]); 1134 if (ret < 0) 1135 return ret; 1136 } else { 1137 /* all others */ 1138 if (!acpi_evalf(led_handle, NULL, NULL, "vdd", 1139 led, led_led_arg1[ind])) 1140 return -EIO; 1141 } 1142 } 1143 1144 return 0; 1145} 1146 1147static int beep_read(char *p) 1148{ 1149 int len = 0; 1150 1151 if (!beep_handle) 1152 len += sprintf(p + len, "status:\t\tnot supported\n"); 1153 else { 1154 len += sprintf(p + len, "status:\t\tsupported\n"); 1155 len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n"); 1156 } 1157 1158 return len; 1159} 1160 1161static int beep_write(char *buf) 1162{ 1163 char *cmd; 1164 int beep_cmd; 1165 1166 if (!beep_handle) 1167 return -ENODEV; 1168 1169 while ((cmd = next_cmd(&buf))) { 1170 if (sscanf(cmd, "%u", &beep_cmd) == 1 && 1171 beep_cmd >= 0 && beep_cmd <= 17) { 1172 /* beep_cmd set */ 1173 } else 1174 return -EINVAL; 1175 if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0)) 1176 return -EIO; 1177 } 1178 1179 return 0; 1180} 1181 1182static int acpi_ec_read(int i, u8 * p) 1183{ 1184 int v; 1185 1186 if (ecrd_handle) { 1187 if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i)) 1188 return 0; 1189 *p = v; 1190 } else { 1191 if (ec_read(i, p) < 0) 1192 return 0; 1193 } 1194 1195 return 1; 1196} 1197 1198static int acpi_ec_write(int i, u8 v) 1199{ 1200 if (ecwr_handle) { 1201 if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v)) 1202 return 0; 1203 } else { 1204 if (ec_write(i, v) < 0) 1205 return 0; 1206 } 1207 1208 return 1; 1209} 1210 1211static int thermal_tmp_supported; 1212static int thermal_updt_supported; 1213 1214static int thermal_init(void) 1215{ 1216 /* temperatures not supported on 570, G4x, R30, R31, R32 */ 1217 thermal_tmp_supported = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); 1218 1219 /* 600e/x, 770e, 770x */ 1220 thermal_updt_supported = acpi_evalf(ec_handle, NULL, "UPDT", "qv"); 1221 1222 return 0; 1223} 1224 1225static int thermal_read(char *p) 1226{ 1227 int len = 0; 1228 1229 if (!thermal_tmp_supported) 1230 len += sprintf(p + len, "temperatures:\tnot supported\n"); 1231 else { 1232 int i, t; 1233 char tmpi[] = "TMPi"; 1234 s8 tmp[8]; 1235 1236 if (thermal_updt_supported) 1237 if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) 1238 return -EIO; 1239 1240 for (i = 0; i < 8; i++) { 1241 tmpi[3] = '0' + i; 1242 if (!acpi_evalf(ec_handle, &t, tmpi, "d")) 1243 return -EIO; 1244 if (thermal_updt_supported) 1245 tmp[i] = (t - 2732 + 5) / 10; 1246 else 1247 tmp[i] = t; 1248 } 1249 1250 len += sprintf(p + len, 1251 "temperatures:\t%d %d %d %d %d %d %d %d\n", 1252 tmp[0], tmp[1], tmp[2], tmp[3], 1253 tmp[4], tmp[5], tmp[6], tmp[7]); 1254 } 1255 1256 return len; 1257} 1258 1259static u8 ecdump_regs[256]; 1260 1261static int ecdump_read(char *p) 1262{ 1263 int len = 0; 1264 int i, j; 1265 u8 v; 1266 1267 len += sprintf(p + len, "EC " 1268 " +00 +01 +02 +03 +04 +05 +06 +07" 1269 " +08 +09 +0a +0b +0c +0d +0e +0f\n"); 1270 for (i = 0; i < 256; i += 16) { 1271 len += sprintf(p + len, "EC 0x%02x:", i); 1272 for (j = 0; j < 16; j++) { 1273 if (!acpi_ec_read(i + j, &v)) 1274 break; 1275 if (v != ecdump_regs[i + j]) 1276 len += sprintf(p + len, " *%02x", v); 1277 else 1278 len += sprintf(p + len, " %02x", v); 1279 ecdump_regs[i + j] = v; 1280 } 1281 len += sprintf(p + len, "\n"); 1282 if (j != 16) 1283 break; 1284 } 1285 1286 /* These are way too dangerous to advertise openly... */ 1287#if 0 1288 len += sprintf(p + len, "commands:\t0x<offset> 0x<value>" 1289 " (<offset> is 00-ff, <value> is 00-ff)\n"); 1290 len += sprintf(p + len, "commands:\t0x<offset> <value> " 1291 " (<offset> is 00-ff, <value> is 0-255)\n"); 1292#endif 1293 return len; 1294} 1295 1296static int ecdump_write(char *buf) 1297{ 1298 char *cmd; 1299 int i, v; 1300 1301 while ((cmd = next_cmd(&buf))) { 1302 if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) { 1303 /* i and v set */ 1304 } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) { 1305 /* i and v set */ 1306 } else 1307 return -EINVAL; 1308 if (i >= 0 && i < 256 && v >= 0 && v < 256) { 1309 if (!acpi_ec_write(i, v)) 1310 return -EIO; 1311 } else 1312 return -EINVAL; 1313 } 1314 1315 return 0; 1316} 1317 1318static int brightness_offset = 0x31; 1319 1320static int brightness_read(char *p) 1321{ 1322 int len = 0; 1323 u8 level; 1324 1325 if (!acpi_ec_read(brightness_offset, &level)) { 1326 len += sprintf(p + len, "level:\t\tunreadable\n"); 1327 } else { 1328 len += sprintf(p + len, "level:\t\t%d\n", level & 0x7); 1329 len += sprintf(p + len, "commands:\tup, down\n"); 1330 len += sprintf(p + len, "commands:\tlevel <level>" 1331 " (<level> is 0-7)\n"); 1332 } 1333 1334 return len; 1335} 1336 1337#define BRIGHTNESS_UP 4 1338#define BRIGHTNESS_DOWN 5 1339 1340static int brightness_write(char *buf) 1341{ 1342 int cmos_cmd, inc, i; 1343 u8 level; 1344 int new_level; 1345 char *cmd; 1346 1347 while ((cmd = next_cmd(&buf))) { 1348 if (!acpi_ec_read(brightness_offset, &level)) 1349 return -EIO; 1350 level &= 7; 1351 1352 if (strlencmp(cmd, "up") == 0) { 1353 new_level = level == 7 ? 7 : level + 1; 1354 } else if (strlencmp(cmd, "down") == 0) { 1355 new_level = level == 0 ? 0 : level - 1; 1356 } else if (sscanf(cmd, "level %d", &new_level) == 1 && 1357 new_level >= 0 && new_level <= 7) { 1358 /* new_level set */ 1359 } else 1360 return -EINVAL; 1361 1362 cmos_cmd = new_level > level ? BRIGHTNESS_UP : BRIGHTNESS_DOWN; 1363 inc = new_level > level ? 1 : -1; 1364 for (i = level; i != new_level; i += inc) { 1365 if (!cmos_eval(cmos_cmd)) 1366 return -EIO; 1367 if (!acpi_ec_write(brightness_offset, i + inc)) 1368 return -EIO; 1369 } 1370 } 1371 1372 return 0; 1373} 1374 1375static int volume_offset = 0x30; 1376 1377static int volume_read(char *p) 1378{ 1379 int len = 0; 1380 u8 level; 1381 1382 if (!acpi_ec_read(volume_offset, &level)) { 1383 len += sprintf(p + len, "level:\t\tunreadable\n"); 1384 } else { 1385 len += sprintf(p + len, "level:\t\t%d\n", level & 0xf); 1386 len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6)); 1387 len += sprintf(p + len, "commands:\tup, down, mute\n"); 1388 len += sprintf(p + len, "commands:\tlevel <level>" 1389 " (<level> is 0-15)\n"); 1390 } 1391 1392 return len; 1393} 1394 1395#define VOLUME_DOWN 0 1396#define VOLUME_UP 1 1397#define VOLUME_MUTE 2 1398 1399static int volume_write(char *buf) 1400{ 1401 int cmos_cmd, inc, i; 1402 u8 level, mute; 1403 int new_level, new_mute; 1404 char *cmd; 1405 1406 while ((cmd = next_cmd(&buf))) { 1407 if (!acpi_ec_read(volume_offset, &level)) 1408 return -EIO; 1409 new_mute = mute = level & 0x40; 1410 new_level = level = level & 0xf; 1411 1412 if (strlencmp(cmd, "up") == 0) { 1413 if (mute) 1414 new_mute = 0; 1415 else 1416 new_level = level == 15 ? 15 : level + 1; 1417 } else if (strlencmp(cmd, "down") == 0) { 1418 if (mute) 1419 new_mute = 0; 1420 else 1421 new_level = level == 0 ? 0 : level - 1; 1422 } else if (sscanf(cmd, "level %d", &new_level) == 1 && 1423 new_level >= 0 && new_level <= 15) { 1424 /* new_level set */ 1425 } else if (strlencmp(cmd, "mute") == 0) { 1426 new_mute = 0x40; 1427 } else 1428 return -EINVAL; 1429 1430 if (new_level != level) { /* mute doesn't change */ 1431 cmos_cmd = new_level > level ? VOLUME_UP : VOLUME_DOWN; 1432 inc = new_level > level ? 1 : -1; 1433 1434 if (mute && (!cmos_eval(cmos_cmd) || 1435 !acpi_ec_write(volume_offset, level))) 1436 return -EIO; 1437 1438 for (i = level; i != new_level; i += inc) 1439 if (!cmos_eval(cmos_cmd) || 1440 !acpi_ec_write(volume_offset, i + inc)) 1441 return -EIO; 1442 1443 if (mute && (!cmos_eval(VOLUME_MUTE) || 1444 !acpi_ec_write(volume_offset, 1445 new_level + mute))) 1446 return -EIO; 1447 } 1448 1449 if (new_mute != mute) { /* level doesn't change */ 1450 cmos_cmd = new_mute ? VOLUME_MUTE : VOLUME_UP; 1451 1452 if (!cmos_eval(cmos_cmd) || 1453 !acpi_ec_write(volume_offset, level + new_mute)) 1454 return -EIO; 1455 } 1456 } 1457 1458 return 0; 1459} 1460 1461static int fan_status_offset = 0x2f; 1462static int fan_rpm_offset = 0x84; 1463 1464static int fan_read(char *p) 1465{ 1466 int len = 0; 1467 int s; 1468 u8 lo, hi, status; 1469 1470 if (gfan_handle) { 1471 /* 570, 600e/x, 770e, 770x */ 1472 if (!acpi_evalf(gfan_handle, &s, NULL, "d")) 1473 return -EIO; 1474 1475 len += sprintf(p + len, "level:\t\t%d\n", s); 1476 } else { 1477 /* all except 570, 600e/x, 770e, 770x */ 1478 if (!acpi_ec_read(fan_status_offset, &status)) 1479 len += sprintf(p + len, "status:\t\tunreadable\n"); 1480 else 1481 len += sprintf(p + len, "status:\t\t%s\n", 1482 enabled(status, 7)); 1483 1484 if (!acpi_ec_read(fan_rpm_offset, &lo) || 1485 !acpi_ec_read(fan_rpm_offset + 1, &hi)) 1486 len += sprintf(p + len, "speed:\t\tunreadable\n"); 1487 else 1488 len += sprintf(p + len, "speed:\t\t%d\n", 1489 (hi << 8) + lo); 1490 } 1491 1492 if (sfan_handle) 1493 /* 570, 770x-JL */ 1494 len += sprintf(p + len, "commands:\tlevel <level>" 1495 " (<level> is 0-7)\n"); 1496 if (!gfan_handle) 1497 /* all except 570, 600e/x, 770e, 770x */ 1498 len += sprintf(p + len, "commands:\tenable, disable\n"); 1499 if (fans_handle) 1500 /* X31, X40 */ 1501 len += sprintf(p + len, "commands:\tspeed <speed>" 1502 " (<speed> is 0-65535)\n"); 1503 1504 return len; 1505} 1506 1507static int fan_write(char *buf) 1508{ 1509 char *cmd; 1510 int level, speed; 1511 1512 while ((cmd = next_cmd(&buf))) { 1513 if (sfan_handle && 1514 sscanf(cmd, "level %d", &level) == 1 && 1515 level >= 0 && level <= 7) { 1516 /* 570, 770x-JL */ 1517 if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) 1518 return -EIO; 1519 } else if (!gfan_handle && strlencmp(cmd, "enable") == 0) { 1520 /* all except 570, 600e/x, 770e, 770x */ 1521 if (!acpi_ec_write(fan_status_offset, 0x80)) 1522 return -EIO; 1523 } else if (!gfan_handle && strlencmp(cmd, "disable") == 0) { 1524 /* all except 570, 600e/x, 770e, 770x */ 1525 if (!acpi_ec_write(fan_status_offset, 0x00)) 1526 return -EIO; 1527 } else if (fans_handle && 1528 sscanf(cmd, "speed %d", &speed) == 1 && 1529 speed >= 0 && speed <= 65535) { 1530 /* X31, X40 */ 1531 if (!acpi_evalf(fans_handle, NULL, NULL, "vddd", 1532 speed, speed, speed)) 1533 return -EIO; 1534 } else 1535 return -EINVAL; 1536 } 1537 1538 return 0; 1539} 1540 1541static struct ibm_struct ibms[] = { 1542 { 1543 .name = "driver", 1544 .init = driver_init, 1545 .read = driver_read, 1546 }, 1547 { 1548 .name = "hotkey", 1549 .hid = IBM_HKEY_HID, 1550 .init = hotkey_init, 1551 .read = hotkey_read, 1552 .write = hotkey_write, 1553 .exit = hotkey_exit, 1554 .notify = hotkey_notify, 1555 .handle = &hkey_handle, 1556 .type = ACPI_DEVICE_NOTIFY, 1557 }, 1558 { 1559 .name = "bluetooth", 1560 .init = bluetooth_init, 1561 .read = bluetooth_read, 1562 .write = bluetooth_write, 1563 }, 1564 { 1565 .name = "video", 1566 .init = video_init, 1567 .read = video_read, 1568 .write = video_write, 1569 .exit = video_exit, 1570 }, 1571 { 1572 .name = "light", 1573 .init = light_init, 1574 .read = light_read, 1575 .write = light_write, 1576 }, 1577 { 1578 .name = "dock", 1579 .read = dock_read, 1580 .write = dock_write, 1581 .notify = dock_notify, 1582 .handle = &dock_handle, 1583 .type = ACPI_SYSTEM_NOTIFY, 1584 }, 1585 { 1586 .name = "dock", 1587 .hid = IBM_PCI_HID, 1588 .notify = dock_notify, 1589 .handle = &pci_handle, 1590 .type = ACPI_SYSTEM_NOTIFY, 1591 }, 1592 { 1593 .name = "bay", 1594 .init = bay_init, 1595 .read = bay_read, 1596 .write = bay_write, 1597 .notify = bay_notify, 1598 .handle = &bay_handle, 1599 .type = ACPI_SYSTEM_NOTIFY, 1600 }, 1601 { 1602 .name = "cmos", 1603 .read = cmos_read, 1604 .write = cmos_write, 1605 }, 1606 { 1607 .name = "led", 1608 .init = led_init, 1609 .read = led_read, 1610 .write = led_write, 1611 }, 1612 { 1613 .name = "beep", 1614 .read = beep_read, 1615 .write = beep_write, 1616 }, 1617 { 1618 .name = "thermal", 1619 .init = thermal_init, 1620 .read = thermal_read, 1621 }, 1622 { 1623 .name = "ecdump", 1624 .read = ecdump_read, 1625 .write = ecdump_write, 1626 .experimental = 1, 1627 }, 1628 { 1629 .name = "brightness", 1630 .read = brightness_read, 1631 .write = brightness_write, 1632 .experimental = 1, 1633 }, 1634 { 1635 .name = "volume", 1636 .read = volume_read, 1637 .write = volume_write, 1638 .experimental = 1, 1639 }, 1640 { 1641 .name = "fan", 1642 .read = fan_read, 1643 .write = fan_write, 1644 .experimental = 1, 1645 }, 1646}; 1647 1648static int dispatch_read(char *page, char **start, off_t off, int count, 1649 int *eof, void *data) 1650{ 1651 struct ibm_struct *ibm = (struct ibm_struct *)data; 1652 int len; 1653 1654 if (!ibm || !ibm->read) 1655 return -EINVAL; 1656 1657 len = ibm->read(page); 1658 if (len < 0) 1659 return len; 1660 1661 if (len <= off + count) 1662 *eof = 1; 1663 *start = page + off; 1664 len -= off; 1665 if (len > count) 1666 len = count; 1667 if (len < 0) 1668 len = 0; 1669 1670 return len; 1671} 1672 1673static int dispatch_write(struct file *file, const char __user * userbuf, 1674 unsigned long count, void *data) 1675{ 1676 struct ibm_struct *ibm = (struct ibm_struct *)data; 1677 char *kernbuf; 1678 int ret; 1679 1680 if (!ibm || !ibm->write) 1681 return -EINVAL; 1682 1683 kernbuf = kmalloc(count + 2, GFP_KERNEL); 1684 if (!kernbuf) 1685 return -ENOMEM; 1686 1687 if (copy_from_user(kernbuf, userbuf, count)) { 1688 kfree(kernbuf); 1689 return -EFAULT; 1690 } 1691 1692 kernbuf[count] = 0; 1693 strcat(kernbuf, ","); 1694 ret = ibm->write(kernbuf); 1695 if (ret == 0) 1696 ret = count; 1697 1698 kfree(kernbuf); 1699 1700 return ret; 1701} 1702 1703static void dispatch_notify(acpi_handle handle, u32 event, void *data) 1704{ 1705 struct ibm_struct *ibm = (struct ibm_struct *)data; 1706 1707 if (!ibm || !ibm->notify) 1708 return; 1709 1710 ibm->notify(ibm, event); 1711} 1712 1713static int __init setup_notify(struct ibm_struct *ibm) 1714{ 1715 acpi_status status; 1716 int ret; 1717 1718 if (!*ibm->handle) 1719 return 0; 1720 1721 ret = acpi_bus_get_device(*ibm->handle, &ibm->device); 1722 if (ret < 0) { 1723 printk(IBM_ERR "%s device not present\n", ibm->name); 1724 return 0; 1725 } 1726 1727 acpi_driver_data(ibm->device) = ibm; 1728 sprintf(acpi_device_class(ibm->device), "%s/%s", IBM_NAME, ibm->name); 1729 1730 status = acpi_install_notify_handler(*ibm->handle, ibm->type, 1731 dispatch_notify, ibm); 1732 if (ACPI_FAILURE(status)) { 1733 printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n", 1734 ibm->name, status); 1735 return -ENODEV; 1736 } 1737 1738 return 0; 1739} 1740 1741static int __init ibm_device_add(struct acpi_device *device) 1742{ 1743 return 0; 1744} 1745 1746static int __init register_driver(struct ibm_struct *ibm) 1747{ 1748 int ret; 1749 1750 ibm->driver = kmalloc(sizeof(struct acpi_driver), GFP_KERNEL); 1751 if (!ibm->driver) { 1752 printk(IBM_ERR "kmalloc(ibm->driver) failed\n"); 1753 return -1; 1754 } 1755 1756 memset(ibm->driver, 0, sizeof(struct acpi_driver)); 1757 sprintf(ibm->driver->name, "%s/%s", IBM_NAME, ibm->name); 1758 ibm->driver->ids = ibm->hid; 1759 ibm->driver->ops.add = &ibm_device_add; 1760 1761 ret = acpi_bus_register_driver(ibm->driver); 1762 if (ret < 0) { 1763 printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n", 1764 ibm->hid, ret); 1765 kfree(ibm->driver); 1766 } 1767 1768 return ret; 1769} 1770 1771static int __init ibm_init(struct ibm_struct *ibm) 1772{ 1773 int ret; 1774 struct proc_dir_entry *entry; 1775 1776 if (ibm->experimental && !experimental) 1777 return 0; 1778 1779 if (ibm->hid) { 1780 ret = register_driver(ibm); 1781 if (ret < 0) 1782 return ret; 1783 ibm->driver_registered = 1; 1784 } 1785 1786 if (ibm->init) { 1787 ret = ibm->init(); 1788 if (ret != 0) 1789 return ret; 1790 ibm->init_called = 1; 1791 } 1792 1793 if (ibm->read) { 1794 entry = create_proc_entry(ibm->name, 1795 S_IFREG | S_IRUGO | S_IWUSR, 1796 proc_dir); 1797 if (!entry) { 1798 printk(IBM_ERR "unable to create proc entry %s\n", 1799 ibm->name); 1800 return -ENODEV; 1801 } 1802 entry->owner = THIS_MODULE; 1803 entry->data = ibm; 1804 entry->read_proc = &dispatch_read; 1805 if (ibm->write) 1806 entry->write_proc = &dispatch_write; 1807 ibm->proc_created = 1; 1808 } 1809 1810 if (ibm->notify) { 1811 ret = setup_notify(ibm); 1812 if (ret < 0) 1813 return ret; 1814 ibm->notify_installed = 1; 1815 } 1816 1817 return 0; 1818} 1819 1820static void ibm_exit(struct ibm_struct *ibm) 1821{ 1822 if (ibm->notify_installed) 1823 acpi_remove_notify_handler(*ibm->handle, ibm->type, 1824 dispatch_notify); 1825 1826 if (ibm->proc_created) 1827 remove_proc_entry(ibm->name, proc_dir); 1828 1829 if (ibm->init_called && ibm->exit) 1830 ibm->exit(); 1831 1832 if (ibm->driver_registered) { 1833 acpi_bus_unregister_driver(ibm->driver); 1834 kfree(ibm->driver); 1835 } 1836} 1837 1838static void __init ibm_handle_init(char *name, 1839 acpi_handle * handle, acpi_handle parent, 1840 char **paths, int num_paths, char **path) 1841{ 1842 int i; 1843 acpi_status status; 1844 1845 for (i = 0; i < num_paths; i++) { 1846 status = acpi_get_handle(parent, paths[i], handle); 1847 if (ACPI_SUCCESS(status)) { 1848 *path = paths[i]; 1849 return; 1850 } 1851 } 1852 1853 *handle = NULL; 1854} 1855 1856#define IBM_HANDLE_INIT(object) \ 1857 ibm_handle_init(#object, &object##_handle, *object##_parent, \ 1858 object##_paths, ARRAY_SIZE(object##_paths), &object##_path) 1859 1860static int set_ibm_param(const char *val, struct kernel_param *kp) 1861{ 1862 unsigned int i; 1863 1864 for (i = 0; i < ARRAY_SIZE(ibms); i++) 1865 if (strcmp(ibms[i].name, kp->name) == 0 && ibms[i].write) { 1866 if (strlen(val) > sizeof(ibms[i].param) - 2) 1867 return -ENOSPC; 1868 strcpy(ibms[i].param, val); 1869 strcat(ibms[i].param, ","); 1870 return 0; 1871 } 1872 1873 return -EINVAL; 1874} 1875 1876#define IBM_PARAM(feature) \ 1877 module_param_call(feature, set_ibm_param, NULL, NULL, 0) 1878 1879IBM_PARAM(hotkey); 1880IBM_PARAM(bluetooth); 1881IBM_PARAM(video); 1882IBM_PARAM(light); 1883IBM_PARAM(dock); 1884IBM_PARAM(bay); 1885IBM_PARAM(cmos); 1886IBM_PARAM(led); 1887IBM_PARAM(beep); 1888IBM_PARAM(ecdump); 1889IBM_PARAM(brightness); 1890IBM_PARAM(volume); 1891IBM_PARAM(fan); 1892 1893static void acpi_ibm_exit(void) 1894{ 1895 int i; 1896 1897 for (i = ARRAY_SIZE(ibms) - 1; i >= 0; i--) 1898 ibm_exit(&ibms[i]); 1899 1900 remove_proc_entry(IBM_DIR, acpi_root_dir); 1901} 1902 1903static int __init acpi_ibm_init(void) 1904{ 1905 int ret, i; 1906 1907 if (acpi_disabled) 1908 return -ENODEV; 1909 1910 if (!acpi_specific_hotkey_enabled) { 1911 printk(IBM_ERR "using generic hotkey driver\n"); 1912 return -ENODEV; 1913 } 1914 1915 /* ec is required because many other handles are relative to it */ 1916 IBM_HANDLE_INIT(ec); 1917 if (!ec_handle) { 1918 printk(IBM_ERR "ec object not found\n"); 1919 return -ENODEV; 1920 } 1921 1922 /* these handles are not required */ 1923 IBM_HANDLE_INIT(vid); 1924 IBM_HANDLE_INIT(vid2); 1925 IBM_HANDLE_INIT(ledb); 1926 IBM_HANDLE_INIT(led); 1927 IBM_HANDLE_INIT(hkey); 1928 IBM_HANDLE_INIT(lght); 1929 IBM_HANDLE_INIT(cmos); 1930 IBM_HANDLE_INIT(dock); 1931 IBM_HANDLE_INIT(pci); 1932 IBM_HANDLE_INIT(bay); 1933 if (bay_handle) 1934 IBM_HANDLE_INIT(bay_ej); 1935 IBM_HANDLE_INIT(bay2); 1936 if (bay2_handle) 1937 IBM_HANDLE_INIT(bay2_ej); 1938 IBM_HANDLE_INIT(beep); 1939 IBM_HANDLE_INIT(ecrd); 1940 IBM_HANDLE_INIT(ecwr); 1941 IBM_HANDLE_INIT(fans); 1942 IBM_HANDLE_INIT(gfan); 1943 IBM_HANDLE_INIT(sfan); 1944 1945 proc_dir = proc_mkdir(IBM_DIR, acpi_root_dir); 1946 if (!proc_dir) { 1947 printk(IBM_ERR "unable to create proc dir %s", IBM_DIR); 1948 return -ENODEV; 1949 } 1950 proc_dir->owner = THIS_MODULE; 1951 1952 for (i = 0; i < ARRAY_SIZE(ibms); i++) { 1953 ret = ibm_init(&ibms[i]); 1954 if (ret >= 0 && *ibms[i].param) 1955 ret = ibms[i].write(ibms[i].param); 1956 if (ret < 0) { 1957 acpi_ibm_exit(); 1958 return ret; 1959 } 1960 } 1961 1962 return 0; 1963} 1964 1965module_init(acpi_ibm_init); 1966module_exit(acpi_ibm_exit);