1 /*- 2 * Copyright (c) 2004, 2005 Philip Paeps <philip@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 /* 29 * Driver for extra ACPI-controlled gadgets (hotkeys, leds, etc) found on 30 * recent Asus (and Medion) laptops. Inspired by the acpi4asus project which 31 * implements these features in the Linux kernel. 32 * 33 * <http://sourceforge.net/projects/acpi4asus/> 34 * 35 * Currently should support most features, but could use some more testing. 36 * Particularly the display-switching stuff is a bit hairy. If you have an 37 * Asus laptop which doesn't appear to be supported, or strange things happen 38 * when using this driver, please report to <acpi@FreeBSD.org>. 39 */ 40 41 #include "opt_acpi.h" 42 #include <sys/param.h> 43 #include <sys/kernel.h> 44 #include <sys/module.h> 45 #include <sys/bus.h> 46 47 #include <contrib/dev/acpica/include/acpi.h> 48 #include <contrib/dev/acpica/include/accommon.h> 49 50 #include <dev/acpica/acpivar.h> 51 #include <dev/led/led.h> 52 53 /* Methods */ 54 #define ACPI_ASUS_METHOD_BRN 1 55 #define ACPI_ASUS_METHOD_DISP 2 56 #define ACPI_ASUS_METHOD_LCD 3 57 #define ACPI_ASUS_METHOD_CAMERA 4 58 #define ACPI_ASUS_METHOD_CARDRD 5 59 #define ACPI_ASUS_METHOD_WLAN 6 60 61 #define _COMPONENT ACPI_OEM 62 ACPI_MODULE_NAME("ASUS") 63 64 struct acpi_asus_model { 65 char *name; 66 67 char *bled_set; 68 char *dled_set; 69 char *gled_set; 70 char *mled_set; 71 char *tled_set; 72 char *wled_set; 73 74 char *brn_get; 75 char *brn_set; 76 char *brn_up; 77 char *brn_dn; 78 79 char *lcd_get; 80 char *lcd_set; 81 82 char *disp_get; 83 char *disp_set; 84 85 char *cam_get; 86 char *cam_set; 87 88 char *crd_get; 89 char *crd_set; 90 91 char *wlan_get; 92 char *wlan_set; 93 94 void (*n_func)(ACPI_HANDLE, UINT32, void *); 95 96 char *lcdd; 97 void (*lcdd_n_func)(ACPI_HANDLE, UINT32, void *); 98 }; 99 100 struct acpi_asus_led { 101 struct acpi_asus_softc *sc; 102 struct cdev *cdev; 103 int busy; 104 int state; 105 enum { 106 ACPI_ASUS_LED_BLED, 107 ACPI_ASUS_LED_DLED, 108 ACPI_ASUS_LED_GLED, 109 ACPI_ASUS_LED_MLED, 110 ACPI_ASUS_LED_TLED, 111 ACPI_ASUS_LED_WLED, 112 } type; 113 }; 114 115 struct acpi_asus_softc { 116 device_t dev; 117 ACPI_HANDLE handle; 118 ACPI_HANDLE lcdd_handle; 119 120 struct acpi_asus_model *model; 121 struct sysctl_ctx_list sysctl_ctx; 122 struct sysctl_oid *sysctl_tree; 123 124 struct acpi_asus_led s_bled; 125 struct acpi_asus_led s_dled; 126 struct acpi_asus_led s_gled; 127 struct acpi_asus_led s_mled; 128 struct acpi_asus_led s_tled; 129 struct acpi_asus_led s_wled; 130 131 int s_brn; 132 int s_disp; 133 int s_lcd; 134 int s_cam; 135 int s_crd; 136 int s_wlan; 137 }; 138 139 static void acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify, 140 void *context); 141 142 /* 143 * We can identify Asus laptops from the string they return 144 * as a result of calling the ATK0100 'INIT' method. 145 */ 146 static struct acpi_asus_model acpi_asus_models[] = { 147 { 148 .name = "xxN", 149 .mled_set = "MLED", 150 .wled_set = "WLED", 151 .lcd_get = "\\BKLT", 152 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 153 .brn_get = "GPLV", 154 .brn_set = "SPLV", 155 .disp_get = "\\ADVG", 156 .disp_set = "SDSP" 157 }, 158 { 159 .name = "A1x", 160 .mled_set = "MLED", 161 .lcd_get = "\\BKLI", 162 .lcd_set = "\\_SB.PCI0.ISA.EC0._Q10", 163 .brn_up = "\\_SB.PCI0.ISA.EC0._Q0E", 164 .brn_dn = "\\_SB.PCI0.ISA.EC0._Q0F" 165 }, 166 { 167 .name = "A2x", 168 .mled_set = "MLED", 169 .wled_set = "WLED", 170 .lcd_get = "\\BAOF", 171 .lcd_set = "\\Q10", 172 .brn_get = "GPLV", 173 .brn_set = "SPLV", 174 .disp_get = "\\INFB", 175 .disp_set = "SDSP" 176 }, 177 { 178 .name = "A3E", 179 .mled_set = "MLED", 180 .wled_set = "WLED", 181 .lcd_get = "\\_SB.PCI0.SBRG.EC0.RPIN(0x67)", 182 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 183 .brn_get = "GPLV", 184 .brn_set = "SPLV", 185 .disp_get = "\\_SB.PCI0.P0P2.VGA.GETD", 186 .disp_set = "SDSP" 187 }, 188 { 189 .name = "A3F", 190 .mled_set = "MLED", 191 .wled_set = "WLED", 192 .bled_set = "BLED", 193 .lcd_get = "\\_SB.PCI0.SBRG.EC0.RPIN(0x11)", 194 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 195 .brn_get = "GPLV", 196 .brn_set = "SPLV", 197 .disp_get = "\\SSTE", 198 .disp_set = "SDSP" 199 }, 200 { 201 .name = "A3N", 202 .mled_set = "MLED", 203 .bled_set = "BLED", 204 .wled_set = "WLED", 205 .lcd_get = "\\BKLT", 206 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 207 .brn_get = "GPLV", 208 .brn_set = "SPLV", 209 .disp_get = "\\_SB.PCI0.P0P3.VGA.GETD", 210 .disp_set = "SDSP" 211 }, 212 { 213 .name = "A4D", 214 .mled_set = "MLED", 215 .brn_up = "\\_SB_.PCI0.SBRG.EC0._Q0E", 216 .brn_dn = "\\_SB_.PCI0.SBRG.EC0._Q0F", 217 .brn_get = "GPLV", 218 .brn_set = "SPLV", 219 #ifdef notyet 220 .disp_get = "\\_SB_.PCI0.SBRG.EC0._Q10", 221 .disp_set = "\\_SB_.PCI0.SBRG.EC0._Q11" 222 #endif 223 }, 224 { 225 .name = "A6V", 226 .bled_set = "BLED", 227 .mled_set = "MLED", 228 .wled_set = "WLED", 229 .lcd_get = NULL, 230 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 231 .brn_get = "GPLV", 232 .brn_set = "SPLV", 233 .disp_get = "\\_SB.PCI0.P0P3.VGA.GETD", 234 .disp_set = "SDSP" 235 }, 236 { 237 .name = "A8SR", 238 .bled_set = "BLED", 239 .mled_set = "MLED", 240 .wled_set = "WLED", 241 .lcd_get = NULL, 242 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 243 .brn_get = "GPLV", 244 .brn_set = "SPLV", 245 .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD", 246 .disp_set = "SDSP", 247 .lcdd = "\\_SB.PCI0.P0P1.VGA.LCDD", 248 .lcdd_n_func = acpi_asus_lcdd_notify 249 }, 250 { 251 .name = "D1x", 252 .mled_set = "MLED", 253 .lcd_get = "\\GP11", 254 .lcd_set = "\\Q0D", 255 .brn_up = "\\Q0C", 256 .brn_dn = "\\Q0B", 257 .disp_get = "\\INFB", 258 .disp_set = "SDSP" 259 }, 260 { 261 .name = "G2K", 262 .bled_set = "BLED", 263 .dled_set = "DLED", 264 .gled_set = "GLED", 265 .mled_set = "MLED", 266 .tled_set = "TLED", 267 .wled_set = "WLED", 268 .brn_get = "GPLV", 269 .brn_set = "SPLV", 270 .lcd_get = "GBTL", 271 .lcd_set = "SBTL", 272 .disp_get = "\\_SB.PCI0.PCE2.VGA.GETD", 273 .disp_set = "SDSP", 274 }, 275 { 276 .name = "L2D", 277 .mled_set = "MLED", 278 .wled_set = "WLED", 279 .brn_up = "\\Q0E", 280 .brn_dn = "\\Q0F", 281 .lcd_get = "\\SGP0", 282 .lcd_set = "\\Q10" 283 }, 284 { 285 .name = "L3C", 286 .mled_set = "MLED", 287 .wled_set = "WLED", 288 .brn_get = "GPLV", 289 .brn_set = "SPLV", 290 .lcd_get = "\\GL32", 291 .lcd_set = "\\_SB.PCI0.PX40.ECD0._Q10" 292 }, 293 { 294 .name = "L3D", 295 .mled_set = "MLED", 296 .wled_set = "WLED", 297 .brn_get = "GPLV", 298 .brn_set = "SPLV", 299 .lcd_get = "\\BKLG", 300 .lcd_set = "\\Q10" 301 }, 302 { 303 .name = "L3H", 304 .mled_set = "MLED", 305 .wled_set = "WLED", 306 .brn_get = "GPLV", 307 .brn_set = "SPLV", 308 .lcd_get = "\\_SB.PCI0.PM.PBC", 309 .lcd_set = "EHK", 310 .disp_get = "\\_SB.INFB", 311 .disp_set = "SDSP" 312 }, 313 { 314 .name = "L4R", 315 .mled_set = "MLED", 316 .wled_set = "WLED", 317 .brn_get = "GPLV", 318 .brn_set = "SPLV", 319 .lcd_get = "\\_SB.PCI0.SBSM.SEO4", 320 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 321 .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD", 322 .disp_set = "SDSP" 323 }, 324 { 325 .name = "L5x", 326 .mled_set = "MLED", 327 .tled_set = "TLED", 328 .lcd_get = "\\BAOF", 329 .lcd_set = "\\Q0D", 330 .brn_get = "GPLV", 331 .brn_set = "SPLV", 332 .disp_get = "\\INFB", 333 .disp_set = "SDSP" 334 }, 335 { 336 .name = "L8L" 337 /* Only has hotkeys, apparently */ 338 }, 339 { 340 .name = "M1A", 341 .mled_set = "MLED", 342 .brn_up = "\\_SB.PCI0.PX40.EC0.Q0E", 343 .brn_dn = "\\_SB.PCI0.PX40.EC0.Q0F", 344 .lcd_get = "\\PNOF", 345 .lcd_set = "\\_SB.PCI0.PX40.EC0.Q10" 346 }, 347 { 348 .name = "M2E", 349 .mled_set = "MLED", 350 .wled_set = "WLED", 351 .brn_get = "GPLV", 352 .brn_set = "SPLV", 353 .lcd_get = "\\GP06", 354 .lcd_set = "\\Q10" 355 }, 356 { 357 .name = "M6N", 358 .mled_set = "MLED", 359 .wled_set = "WLED", 360 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 361 .lcd_get = "\\_SB.BKLT", 362 .brn_set = "SPLV", 363 .brn_get = "GPLV", 364 .disp_set = "SDSP", 365 .disp_get = "\\SSTE" 366 }, 367 { 368 .name = "M6R", 369 .mled_set = "MLED", 370 .wled_set = "WLED", 371 .brn_get = "GPLV", 372 .brn_set = "SPLV", 373 .lcd_get = "\\_SB.PCI0.SBSM.SEO4", 374 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 375 .disp_get = "\\SSTE", 376 .disp_set = "SDSP" 377 }, 378 { 379 .name = "S1x", 380 .mled_set = "MLED", 381 .wled_set = "WLED", 382 .lcd_get = "\\PNOF", 383 .lcd_set = "\\_SB.PCI0.PX40.Q10", 384 .brn_get = "GPLV", 385 .brn_set = "SPLV" 386 }, 387 { 388 .name = "S2x", 389 .mled_set = "MLED", 390 .lcd_get = "\\BKLI", 391 .lcd_set = "\\_SB.PCI0.ISA.EC0._Q10", 392 .brn_up = "\\_SB.PCI0.ISA.EC0._Q0B", 393 .brn_dn = "\\_SB.PCI0.ISA.EC0._Q0A" 394 }, 395 { 396 .name = "V6V", 397 .bled_set = "BLED", 398 .tled_set = "TLED", 399 .wled_set = "WLED", 400 .lcd_get = "\\BKLT", 401 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 402 .brn_get = "GPLV", 403 .brn_set = "SPLV", 404 .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD", 405 .disp_set = "SDSP" 406 }, 407 { 408 .name = "W5A", 409 .bled_set = "BLED", 410 .lcd_get = "\\BKLT", 411 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 412 .brn_get = "GPLV", 413 .brn_set = "SPLV", 414 .disp_get = "\\_SB.PCI0.P0P2.VGA.GETD", 415 .disp_set = "SDSP" 416 }, 417 { .name = NULL } 418 }; 419 420 /* 421 * Samsung P30/P35 laptops have an Asus ATK0100 gadget interface, 422 * but they can't be probed quite the same way as Asus laptops. 423 */ 424 static struct acpi_asus_model acpi_samsung_models[] = { 425 { 426 .name = "P30", 427 .wled_set = "WLED", 428 .brn_up = "\\_SB.PCI0.LPCB.EC0._Q68", 429 .brn_dn = "\\_SB.PCI0.LPCB.EC0._Q69", 430 .lcd_get = "\\BKLT", 431 .lcd_set = "\\_SB.PCI0.LPCB.EC0._Q0E" 432 }, 433 { .name = NULL } 434 }; 435 436 static void acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context); 437 438 /* 439 * EeePC have an Asus ASUS010 gadget interface, 440 * but they can't be probed quite the same way as Asus laptops. 441 */ 442 static struct acpi_asus_model acpi_eeepc_models[] = { 443 { 444 .name = "EEE", 445 .brn_get = "\\_SB.ATKD.PBLG", 446 .brn_set = "\\_SB.ATKD.PBLS", 447 .cam_get = "\\_SB.ATKD.CAMG", 448 .cam_set = "\\_SB.ATKD.CAMS", 449 .crd_set = "\\_SB.ATKD.CRDS", 450 .crd_get = "\\_SB.ATKD.CRDG", 451 .wlan_get = "\\_SB.ATKD.WLDG", 452 .wlan_set = "\\_SB.ATKD.WLDS", 453 .n_func = acpi_asus_eeepc_notify 454 }, 455 { .name = NULL } 456 }; 457 458 static struct { 459 char *name; 460 char *description; 461 int method; 462 int flag_anybody; 463 } acpi_asus_sysctls[] = { 464 { 465 .name = "lcd_backlight", 466 .method = ACPI_ASUS_METHOD_LCD, 467 .description = "state of the lcd backlight", 468 .flag_anybody = 1 469 }, 470 { 471 .name = "lcd_brightness", 472 .method = ACPI_ASUS_METHOD_BRN, 473 .description = "brightness of the lcd panel", 474 .flag_anybody = 1 475 }, 476 { 477 .name = "video_output", 478 .method = ACPI_ASUS_METHOD_DISP, 479 .description = "display output state", 480 }, 481 { 482 .name = "camera", 483 .method = ACPI_ASUS_METHOD_CAMERA, 484 .description = "internal camera state", 485 }, 486 { 487 .name = "cardreader", 488 .method = ACPI_ASUS_METHOD_CARDRD, 489 .description = "internal card reader state", 490 }, 491 { 492 .name = "wlan", 493 .method = ACPI_ASUS_METHOD_WLAN, 494 .description = "wireless lan state", 495 }, 496 { .name = NULL } 497 }; 498 499 ACPI_SERIAL_DECL(asus, "ACPI ASUS extras"); 500 501 /* Function prototypes */ 502 static int acpi_asus_probe(device_t dev); 503 static int acpi_asus_attach(device_t dev); 504 static int acpi_asus_detach(device_t dev); 505 506 static void acpi_asus_led(struct acpi_asus_led *led, int state); 507 static void acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused); 508 509 static int acpi_asus_sysctl(SYSCTL_HANDLER_ARGS); 510 static int acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method); 511 static int acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method); 512 static int acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val); 513 514 static void acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context); 515 516 static device_method_t acpi_asus_methods[] = { 517 DEVMETHOD(device_probe, acpi_asus_probe), 518 DEVMETHOD(device_attach, acpi_asus_attach), 519 DEVMETHOD(device_detach, acpi_asus_detach), 520 { 0, 0 } 521 }; 522 523 static driver_t acpi_asus_driver = { 524 "acpi_asus", 525 acpi_asus_methods, 526 sizeof(struct acpi_asus_softc) 527 }; 528 529 DRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, 0, 0); 530 MODULE_DEPEND(acpi_asus, acpi, 1, 1, 1); 531 532 static int 533 acpi_asus_probe(device_t dev) 534 { 535 struct acpi_asus_model *model; 536 struct acpi_asus_softc *sc; 537 ACPI_BUFFER Buf; 538 ACPI_OBJECT Arg, *Obj; 539 ACPI_OBJECT_LIST Args; 540 static char *asus_ids[] = { "ATK0100", "ASUS010", NULL }; 541 int rv; 542 char *rstr; 543 544 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 545 546 if (acpi_disabled("asus")) 547 return (ENXIO); 548 rv = ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids, &rstr); 549 if (rv > 0) { 550 return (rv); 551 } 552 553 sc = device_get_softc(dev); 554 sc->dev = dev; 555 sc->handle = acpi_get_handle(dev); 556 557 Arg.Type = ACPI_TYPE_INTEGER; 558 Arg.Integer.Value = 0; 559 560 Args.Count = 1; 561 Args.Pointer = &Arg; 562 563 Buf.Pointer = NULL; 564 Buf.Length = ACPI_ALLOCATE_BUFFER; 565 566 AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf); 567 Obj = Buf.Pointer; 568 569 /* 570 * The Samsung P30 returns a null-pointer from INIT, we 571 * can identify it from the 'ODEM' string in the DSDT. 572 */ 573 if (Obj->String.Pointer == NULL) { 574 ACPI_STATUS status; 575 ACPI_TABLE_HEADER th; 576 577 status = AcpiGetTableHeader(ACPI_SIG_DSDT, 0, &th); 578 if (ACPI_FAILURE(status)) { 579 device_printf(dev, "Unsupported (Samsung?) laptop\n"); 580 AcpiOsFree(Buf.Pointer); 581 return (ENXIO); 582 } 583 584 if (strncmp("ODEM", th.OemTableId, 4) == 0) { 585 sc->model = &acpi_samsung_models[0]; 586 device_set_desc(dev, "Samsung P30 Laptop Extras"); 587 AcpiOsFree(Buf.Pointer); 588 return (rv); 589 } 590 591 /* EeePC */ 592 if (strncmp("ASUS010", rstr, 7) == 0) { 593 sc->model = &acpi_eeepc_models[0]; 594 device_set_desc(dev, "ASUS EeePC"); 595 AcpiOsFree(Buf.Pointer); 596 return (rv); 597 } 598 } 599 600 /* 601 * Asus laptops are simply identified by name, easy! 602 */ 603 for (model = acpi_asus_models; model->name != NULL; model++) { 604 if (strncmp(Obj->String.Pointer, model->name, 3) == 0) { 605 good: 606 sc->model = model; 607 608 device_set_descf(dev, "Asus %s Laptop Extras", 609 Obj->String.Pointer); 610 611 AcpiOsFree(Buf.Pointer); 612 return (rv); 613 } 614 615 /* 616 * Some models look exactly the same as other models, but have 617 * their own ids. If we spot these, set them up with the same 618 * details as the models they're like, possibly dealing with 619 * small differences. 620 * 621 * XXX: there must be a prettier way to do this! 622 */ 623 else if (strncmp(model->name, "xxN", 3) == 0 && 624 (strncmp(Obj->String.Pointer, "M3N", 3) == 0 || 625 strncmp(Obj->String.Pointer, "S1N", 3) == 0)) 626 goto good; 627 else if (strncmp(model->name, "A1x", 3) == 0 && 628 strncmp(Obj->String.Pointer, "A1", 2) == 0) 629 goto good; 630 else if (strncmp(model->name, "A2x", 3) == 0 && 631 strncmp(Obj->String.Pointer, "A2", 2) == 0) 632 goto good; 633 else if (strncmp(model->name, "A3F", 3) == 0 && 634 strncmp(Obj->String.Pointer, "A6F", 3) == 0) 635 goto good; 636 else if (strncmp(model->name, "D1x", 3) == 0 && 637 strncmp(Obj->String.Pointer, "D1", 2) == 0) 638 goto good; 639 else if (strncmp(model->name, "L3H", 3) == 0 && 640 strncmp(Obj->String.Pointer, "L2E", 3) == 0) 641 goto good; 642 else if (strncmp(model->name, "L5x", 3) == 0 && 643 strncmp(Obj->String.Pointer, "L5", 2) == 0) 644 goto good; 645 else if (strncmp(model->name, "M2E", 3) == 0 && 646 (strncmp(Obj->String.Pointer, "M2", 2) == 0 || 647 strncmp(Obj->String.Pointer, "L4E", 3) == 0)) 648 goto good; 649 else if (strncmp(model->name, "S1x", 3) == 0 && 650 (strncmp(Obj->String.Pointer, "L8", 2) == 0 || 651 strncmp(Obj->String.Pointer, "S1", 2) == 0)) 652 goto good; 653 else if (strncmp(model->name, "S2x", 3) == 0 && 654 (strncmp(Obj->String.Pointer, "J1", 2) == 0 || 655 strncmp(Obj->String.Pointer, "S2", 2) == 0)) 656 goto good; 657 658 /* L2B is like L3C but has no lcd_get method */ 659 else if (strncmp(model->name, "L3C", 3) == 0 && 660 strncmp(Obj->String.Pointer, "L2B", 3) == 0) { 661 model->lcd_get = NULL; 662 goto good; 663 } 664 665 /* A3G is like M6R but with a different lcd_get method */ 666 else if (strncmp(model->name, "M6R", 3) == 0 && 667 strncmp(Obj->String.Pointer, "A3G", 3) == 0) { 668 model->lcd_get = "\\BLFG"; 669 goto good; 670 } 671 672 /* M2N and W1N are like xxN with added WLED */ 673 else if (strncmp(model->name, "xxN", 3) == 0 && 674 (strncmp(Obj->String.Pointer, "M2N", 3) == 0 || 675 strncmp(Obj->String.Pointer, "W1N", 3) == 0)) { 676 model->wled_set = "WLED"; 677 goto good; 678 } 679 680 /* M5N and S5N are like xxN without MLED */ 681 else if (strncmp(model->name, "xxN", 3) == 0 && 682 (strncmp(Obj->String.Pointer, "M5N", 3) == 0 || 683 strncmp(Obj->String.Pointer, "S5N", 3) == 0)) { 684 model->mled_set = NULL; 685 goto good; 686 } 687 } 688 689 device_printf(dev, "Unsupported Asus laptop: %s\n", 690 Obj->String.Pointer); 691 692 AcpiOsFree(Buf.Pointer); 693 694 return (ENXIO); 695 } 696 697 static int 698 acpi_asus_attach(device_t dev) 699 { 700 struct acpi_asus_softc *sc; 701 struct acpi_softc *acpi_sc; 702 703 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 704 705 sc = device_get_softc(dev); 706 acpi_sc = acpi_device_get_parent_softc(dev); 707 708 /* Build sysctl tree */ 709 sysctl_ctx_init(&sc->sysctl_ctx); 710 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 711 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), 712 OID_AUTO, "asus", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); 713 714 /* Hook up nodes */ 715 for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) { 716 if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method)) 717 continue; 718 719 if (acpi_asus_sysctls[i].flag_anybody != 0) { 720 SYSCTL_ADD_PROC(&sc->sysctl_ctx, 721 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 722 acpi_asus_sysctls[i].name, 723 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY | 724 CTLFLAG_MPSAFE, sc, i, acpi_asus_sysctl, "I", 725 acpi_asus_sysctls[i].description); 726 } else { 727 SYSCTL_ADD_PROC(&sc->sysctl_ctx, 728 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 729 acpi_asus_sysctls[i].name, 730 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, 731 sc, i, acpi_asus_sysctl, "I", 732 acpi_asus_sysctls[i].description); 733 } 734 } 735 736 /* Attach leds */ 737 if (sc->model->bled_set) { 738 sc->s_bled.busy = 0; 739 sc->s_bled.sc = sc; 740 sc->s_bled.type = ACPI_ASUS_LED_BLED; 741 sc->s_bled.cdev = 742 led_create_state((led_t *)acpi_asus_led, &sc->s_bled, 743 "bled", 1); 744 } 745 746 if (sc->model->dled_set) { 747 sc->s_dled.busy = 0; 748 sc->s_dled.sc = sc; 749 sc->s_dled.type = ACPI_ASUS_LED_DLED; 750 sc->s_dled.cdev = 751 led_create((led_t *)acpi_asus_led, &sc->s_dled, "dled"); 752 } 753 754 if (sc->model->gled_set) { 755 sc->s_gled.busy = 0; 756 sc->s_gled.sc = sc; 757 sc->s_gled.type = ACPI_ASUS_LED_GLED; 758 sc->s_gled.cdev = 759 led_create((led_t *)acpi_asus_led, &sc->s_gled, "gled"); 760 } 761 762 if (sc->model->mled_set) { 763 sc->s_mled.busy = 0; 764 sc->s_mled.sc = sc; 765 sc->s_mled.type = ACPI_ASUS_LED_MLED; 766 sc->s_mled.cdev = 767 led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled"); 768 } 769 770 if (sc->model->tled_set) { 771 sc->s_tled.busy = 0; 772 sc->s_tled.sc = sc; 773 sc->s_tled.type = ACPI_ASUS_LED_TLED; 774 sc->s_tled.cdev = 775 led_create_state((led_t *)acpi_asus_led, &sc->s_tled, 776 "tled", 1); 777 } 778 779 if (sc->model->wled_set) { 780 sc->s_wled.busy = 0; 781 sc->s_wled.sc = sc; 782 sc->s_wled.type = ACPI_ASUS_LED_WLED; 783 sc->s_wled.cdev = 784 led_create_state((led_t *)acpi_asus_led, &sc->s_wled, 785 "wled", 1); 786 } 787 788 /* Activate hotkeys */ 789 AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL); 790 791 /* Handle notifies */ 792 if (sc->model->n_func == NULL) 793 sc->model->n_func = acpi_asus_notify; 794 795 AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, 796 sc->model->n_func, dev); 797 798 /* Find and hook the 'LCDD' object */ 799 if (sc->model->lcdd != NULL && sc->model->lcdd_n_func != NULL) { 800 ACPI_STATUS res; 801 802 sc->lcdd_handle = NULL; 803 res = AcpiGetHandle((sc->model->lcdd[0] == '\\' ? 804 NULL : sc->handle), sc->model->lcdd, &(sc->lcdd_handle)); 805 if (ACPI_SUCCESS(res)) { 806 AcpiInstallNotifyHandler((sc->lcdd_handle), 807 ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func, dev); 808 } else { 809 printf("%s: unable to find LCD device '%s'\n", 810 __func__, sc->model->lcdd); 811 } 812 } 813 814 return (0); 815 } 816 817 static int 818 acpi_asus_detach(device_t dev) 819 { 820 struct acpi_asus_softc *sc; 821 822 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 823 824 sc = device_get_softc(dev); 825 826 /* Turn the lights off */ 827 if (sc->model->bled_set) 828 led_destroy(sc->s_bled.cdev); 829 830 if (sc->model->dled_set) 831 led_destroy(sc->s_dled.cdev); 832 833 if (sc->model->gled_set) 834 led_destroy(sc->s_gled.cdev); 835 836 if (sc->model->mled_set) 837 led_destroy(sc->s_mled.cdev); 838 839 if (sc->model->tled_set) 840 led_destroy(sc->s_tled.cdev); 841 842 if (sc->model->wled_set) 843 led_destroy(sc->s_wled.cdev); 844 845 /* Remove notify handler */ 846 AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, 847 acpi_asus_notify); 848 849 if (sc->lcdd_handle) { 850 KASSERT(sc->model->lcdd_n_func != NULL, 851 ("model->lcdd_n_func is NULL, but lcdd_handle is non-zero")); 852 AcpiRemoveNotifyHandler((sc->lcdd_handle), 853 ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func); 854 } 855 856 /* Free sysctl tree */ 857 sysctl_ctx_free(&sc->sysctl_ctx); 858 859 return (0); 860 } 861 862 static void 863 acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused) 864 { 865 struct acpi_asus_softc *sc; 866 char *method; 867 int state; 868 869 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 870 871 sc = led->sc; 872 873 switch (led->type) { 874 case ACPI_ASUS_LED_BLED: 875 method = sc->model->bled_set; 876 state = led->state; 877 break; 878 case ACPI_ASUS_LED_DLED: 879 method = sc->model->dled_set; 880 state = led->state; 881 break; 882 case ACPI_ASUS_LED_GLED: 883 method = sc->model->gled_set; 884 state = led->state + 1; /* 1: off, 2: on */ 885 break; 886 case ACPI_ASUS_LED_MLED: 887 method = sc->model->mled_set; 888 state = !led->state; /* inverted */ 889 break; 890 case ACPI_ASUS_LED_TLED: 891 method = sc->model->tled_set; 892 state = led->state; 893 break; 894 case ACPI_ASUS_LED_WLED: 895 method = sc->model->wled_set; 896 state = led->state; 897 break; 898 default: 899 printf("acpi_asus_led: invalid LED type %d\n", 900 (int)led->type); 901 return; 902 } 903 904 acpi_SetInteger(sc->handle, method, state); 905 led->busy = 0; 906 } 907 908 static void 909 acpi_asus_led(struct acpi_asus_led *led, int state) 910 { 911 912 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 913 914 if (led->busy) 915 return; 916 917 led->busy = 1; 918 led->state = state; 919 920 AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)acpi_asus_led_task, led); 921 } 922 923 static int 924 acpi_asus_sysctl(SYSCTL_HANDLER_ARGS) 925 { 926 struct acpi_asus_softc *sc; 927 int arg; 928 int error = 0; 929 int function; 930 int method; 931 932 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 933 934 sc = (struct acpi_asus_softc *)oidp->oid_arg1; 935 function = oidp->oid_arg2; 936 method = acpi_asus_sysctls[function].method; 937 938 ACPI_SERIAL_BEGIN(asus); 939 arg = acpi_asus_sysctl_get(sc, method); 940 error = sysctl_handle_int(oidp, &arg, 0, req); 941 942 /* Sanity check */ 943 if (error != 0 || req->newptr == NULL) 944 goto out; 945 946 /* Update */ 947 error = acpi_asus_sysctl_set(sc, method, arg); 948 949 out: 950 ACPI_SERIAL_END(asus); 951 return (error); 952 } 953 954 static int 955 acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method) 956 { 957 int val = 0; 958 959 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 960 ACPI_SERIAL_ASSERT(asus); 961 962 switch (method) { 963 case ACPI_ASUS_METHOD_BRN: 964 val = sc->s_brn; 965 break; 966 case ACPI_ASUS_METHOD_DISP: 967 val = sc->s_disp; 968 break; 969 case ACPI_ASUS_METHOD_LCD: 970 val = sc->s_lcd; 971 break; 972 case ACPI_ASUS_METHOD_CAMERA: 973 val = sc->s_cam; 974 break; 975 case ACPI_ASUS_METHOD_CARDRD: 976 val = sc->s_crd; 977 break; 978 case ACPI_ASUS_METHOD_WLAN: 979 val = sc->s_wlan; 980 break; 981 } 982 983 return (val); 984 } 985 986 static int 987 acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg) 988 { 989 ACPI_STATUS status = AE_OK; 990 ACPI_OBJECT_LIST acpiargs; 991 ACPI_OBJECT acpiarg[1]; 992 993 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 994 ACPI_SERIAL_ASSERT(asus); 995 996 acpiargs.Count = 1; 997 acpiargs.Pointer = acpiarg; 998 acpiarg[0].Type = ACPI_TYPE_INTEGER; 999 acpiarg[0].Integer.Value = arg; 1000 1001 switch (method) { 1002 case ACPI_ASUS_METHOD_BRN: 1003 if (arg < 0 || arg > 15) 1004 return (EINVAL); 1005 1006 if (sc->model->brn_set) 1007 status = acpi_SetInteger(sc->handle, 1008 sc->model->brn_set, arg); 1009 else { 1010 while (arg != 0) { 1011 status = AcpiEvaluateObject(sc->handle, 1012 (arg > 0) ? sc->model->brn_up : 1013 sc->model->brn_dn, NULL, NULL); 1014 (arg > 0) ? arg-- : arg++; 1015 } 1016 } 1017 1018 if (ACPI_SUCCESS(status)) 1019 sc->s_brn = arg; 1020 1021 break; 1022 case ACPI_ASUS_METHOD_DISP: 1023 if (arg < 0 || arg > 7) 1024 return (EINVAL); 1025 1026 status = acpi_SetInteger(sc->handle, 1027 sc->model->disp_set, arg); 1028 1029 if (ACPI_SUCCESS(status)) 1030 sc->s_disp = arg; 1031 1032 break; 1033 case ACPI_ASUS_METHOD_LCD: 1034 if (arg < 0 || arg > 1) 1035 return (EINVAL); 1036 1037 if (strncmp(sc->model->name, "L3H", 3) != 0) 1038 status = AcpiEvaluateObject(sc->handle, 1039 sc->model->lcd_set, NULL, NULL); 1040 else 1041 status = acpi_SetInteger(sc->handle, 1042 sc->model->lcd_set, 0x7); 1043 1044 if (ACPI_SUCCESS(status)) 1045 sc->s_lcd = arg; 1046 1047 break; 1048 case ACPI_ASUS_METHOD_CAMERA: 1049 if (arg < 0 || arg > 1) 1050 return (EINVAL); 1051 1052 status = AcpiEvaluateObject(sc->handle, 1053 sc->model->cam_set, &acpiargs, NULL); 1054 1055 if (ACPI_SUCCESS(status)) 1056 sc->s_cam = arg; 1057 break; 1058 case ACPI_ASUS_METHOD_CARDRD: 1059 if (arg < 0 || arg > 1) 1060 return (EINVAL); 1061 1062 status = AcpiEvaluateObject(sc->handle, 1063 sc->model->crd_set, &acpiargs, NULL); 1064 1065 if (ACPI_SUCCESS(status)) 1066 sc->s_crd = arg; 1067 break; 1068 case ACPI_ASUS_METHOD_WLAN: 1069 if (arg < 0 || arg > 1) 1070 return (EINVAL); 1071 1072 status = AcpiEvaluateObject(sc->handle, 1073 sc->model->wlan_set, &acpiargs, NULL); 1074 1075 if (ACPI_SUCCESS(status)) 1076 sc->s_wlan = arg; 1077 break; 1078 } 1079 1080 return (0); 1081 } 1082 1083 static int 1084 acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method) 1085 { 1086 ACPI_STATUS status; 1087 1088 switch (method) { 1089 case ACPI_ASUS_METHOD_BRN: 1090 if (sc->model->brn_get) { 1091 /* GPLV/SPLV models */ 1092 status = acpi_GetInteger(sc->handle, 1093 sc->model->brn_get, &sc->s_brn); 1094 if (ACPI_SUCCESS(status)) 1095 return (TRUE); 1096 } else if (sc->model->brn_up) { 1097 /* Relative models */ 1098 status = AcpiEvaluateObject(sc->handle, 1099 sc->model->brn_up, NULL, NULL); 1100 if (ACPI_FAILURE(status)) 1101 return (FALSE); 1102 1103 status = AcpiEvaluateObject(sc->handle, 1104 sc->model->brn_dn, NULL, NULL); 1105 if (ACPI_FAILURE(status)) 1106 return (FALSE); 1107 1108 return (TRUE); 1109 } 1110 return (FALSE); 1111 case ACPI_ASUS_METHOD_DISP: 1112 if (sc->model->disp_get) { 1113 status = acpi_GetInteger(sc->handle, 1114 sc->model->disp_get, &sc->s_disp); 1115 if (ACPI_SUCCESS(status)) 1116 return (TRUE); 1117 } 1118 return (FALSE); 1119 case ACPI_ASUS_METHOD_LCD: 1120 if (sc->model->lcd_get) { 1121 if (strncmp(sc->model->name, "L3H", 3) == 0) { 1122 ACPI_BUFFER Buf; 1123 ACPI_OBJECT Arg[2], Obj; 1124 ACPI_OBJECT_LIST Args; 1125 1126 /* L3H is a bit special */ 1127 Arg[0].Type = ACPI_TYPE_INTEGER; 1128 Arg[0].Integer.Value = 0x02; 1129 Arg[1].Type = ACPI_TYPE_INTEGER; 1130 Arg[1].Integer.Value = 0x03; 1131 1132 Args.Count = 2; 1133 Args.Pointer = Arg; 1134 1135 Buf.Length = sizeof(Obj); 1136 Buf.Pointer = &Obj; 1137 1138 status = AcpiEvaluateObject(sc->handle, 1139 sc->model->lcd_get, &Args, &Buf); 1140 if (ACPI_SUCCESS(status) && 1141 Obj.Type == ACPI_TYPE_INTEGER) { 1142 sc->s_lcd = Obj.Integer.Value >> 8; 1143 return (TRUE); 1144 } 1145 } else { 1146 status = acpi_GetInteger(sc->handle, 1147 sc->model->lcd_get, &sc->s_lcd); 1148 if (ACPI_SUCCESS(status)) 1149 return (TRUE); 1150 } 1151 } 1152 return (FALSE); 1153 case ACPI_ASUS_METHOD_CAMERA: 1154 if (sc->model->cam_get) { 1155 status = acpi_GetInteger(sc->handle, 1156 sc->model->cam_get, &sc->s_cam); 1157 if (ACPI_SUCCESS(status)) 1158 return (TRUE); 1159 } 1160 return (FALSE); 1161 case ACPI_ASUS_METHOD_CARDRD: 1162 if (sc->model->crd_get) { 1163 status = acpi_GetInteger(sc->handle, 1164 sc->model->crd_get, &sc->s_crd); 1165 if (ACPI_SUCCESS(status)) 1166 return (TRUE); 1167 } 1168 return (FALSE); 1169 case ACPI_ASUS_METHOD_WLAN: 1170 if (sc->model->wlan_get) { 1171 status = acpi_GetInteger(sc->handle, 1172 sc->model->wlan_get, &sc->s_wlan); 1173 if (ACPI_SUCCESS(status)) 1174 return (TRUE); 1175 } 1176 return (FALSE); 1177 } 1178 return (FALSE); 1179 } 1180 1181 static void 1182 acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1183 { 1184 struct acpi_asus_softc *sc; 1185 struct acpi_softc *acpi_sc; 1186 1187 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1188 1189 sc = device_get_softc((device_t)context); 1190 acpi_sc = acpi_device_get_parent_softc(sc->dev); 1191 1192 ACPI_SERIAL_BEGIN(asus); 1193 if ((notify & ~0x10) <= 15) { 1194 sc->s_brn = notify & ~0x10; 1195 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); 1196 } else if ((notify & ~0x20) <= 15) { 1197 sc->s_brn = notify & ~0x20; 1198 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); 1199 } else if (notify == 0x33) { 1200 sc->s_lcd = 1; 1201 ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n"); 1202 } else if (notify == 0x34) { 1203 sc->s_lcd = 0; 1204 ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n"); 1205 } else if (notify == 0x86) { 1206 acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1); 1207 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); 1208 } else if (notify == 0x87) { 1209 acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1); 1210 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); 1211 } else { 1212 /* Notify devd(8) */ 1213 acpi_UserNotify("ASUS", h, notify); 1214 } 1215 ACPI_SERIAL_END(asus); 1216 } 1217 1218 static void 1219 acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1220 { 1221 struct acpi_asus_softc *sc; 1222 struct acpi_softc *acpi_sc; 1223 1224 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1225 1226 sc = device_get_softc((device_t)context); 1227 acpi_sc = acpi_device_get_parent_softc(sc->dev); 1228 1229 ACPI_SERIAL_BEGIN(asus); 1230 switch (notify) { 1231 case 0x87: 1232 acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1); 1233 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); 1234 break; 1235 case 0x86: 1236 acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1); 1237 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); 1238 break; 1239 } 1240 ACPI_SERIAL_END(asus); 1241 } 1242 1243 static void 1244 acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1245 { 1246 struct acpi_asus_softc *sc; 1247 struct acpi_softc *acpi_sc; 1248 1249 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1250 1251 sc = device_get_softc((device_t)context); 1252 acpi_sc = acpi_device_get_parent_softc(sc->dev); 1253 1254 ACPI_SERIAL_BEGIN(asus); 1255 if ((notify & ~0x20) <= 15) { 1256 sc->s_brn = notify & ~0x20; 1257 ACPI_VPRINT(sc->dev, acpi_sc, 1258 "Brightness increased/decreased\n"); 1259 } else { 1260 /* Notify devd(8) */ 1261 acpi_UserNotify("ASUS-Eee", h, notify); 1262 } 1263 ACPI_SERIAL_END(asus); 1264 } 1265