1 /*- 2 * Copyright (c) 2004 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 __FBSDID("$FreeBSD$"); 29 30 /* 31 * Driver for extra ACPI-controlled gadgets (hotkeys, leds, etc) found on 32 * recent Asus (and Medion) laptops. Inspired by the acpi4asus project which 33 * implements these features in the Linux kernel. 34 * 35 * <http://sourceforge.net/projects/acpi4asus/> 36 * 37 * Currently should support most features, but could use some more testing. 38 * Particularly the display-switching stuff is a bit hairy. If you have an 39 * Asus laptop which doesn't appear to be supported, or strange things happen 40 * when using this driver, please report to <acpi@FreeBSD.org>. 41 */ 42 43 #include "opt_acpi.h" 44 #include <sys/param.h> 45 #include <sys/kernel.h> 46 #include <sys/module.h> 47 #include <sys/bus.h> 48 #include <sys/sbuf.h> 49 50 #include "acpi.h" 51 #include <dev/acpica/acpivar.h> 52 #include <dev/led/led.h> 53 54 /* Methods */ 55 #define ACPI_ASUS_METHOD_BRN 1 56 #define ACPI_ASUS_METHOD_DISP 2 57 #define ACPI_ASUS_METHOD_LCD 3 58 59 #define _COMPONENT ACPI_OEM 60 ACPI_MODULE_NAME("ASUS") 61 62 struct acpi_asus_model { 63 char *name; 64 65 char *mled_set; 66 char *tled_set; 67 char *wled_set; 68 69 char *brn_get; 70 char *brn_set; 71 char *brn_up; 72 char *brn_dn; 73 74 char *lcd_get; 75 char *lcd_set; 76 77 char *disp_get; 78 char *disp_set; 79 }; 80 81 struct acpi_asus_led { 82 struct acpi_asus_softc *sc; 83 struct cdev *cdev; 84 int busy; 85 int state; 86 enum { 87 ACPI_ASUS_LED_MLED, 88 ACPI_ASUS_LED_TLED, 89 ACPI_ASUS_LED_WLED, 90 } type; 91 }; 92 93 struct acpi_asus_softc { 94 device_t dev; 95 ACPI_HANDLE handle; 96 97 struct acpi_asus_model *model; 98 struct sysctl_ctx_list sysctl_ctx; 99 struct sysctl_oid *sysctl_tree; 100 101 struct acpi_asus_led s_mled; 102 struct acpi_asus_led s_tled; 103 struct acpi_asus_led s_wled; 104 105 int s_brn; 106 int s_disp; 107 int s_lcd; 108 }; 109 110 /* 111 * We can identify Asus laptops from the string they return 112 * as a result of calling the ATK0100 'INIT' method. 113 */ 114 static struct acpi_asus_model acpi_asus_models[] = { 115 { 116 .name = "L2D", 117 .mled_set = "MLED", 118 .wled_set = "WLED", 119 .brn_up = "\\Q0E", 120 .brn_dn = "\\Q0F", 121 .lcd_get = "\\SGP0", 122 .lcd_set = "\\Q10" 123 }, 124 { 125 .name = "L3C", 126 .mled_set = "MLED", 127 .wled_set = "WLED", 128 .brn_get = "GPLV", 129 .brn_set = "SPLV", 130 .lcd_get = "\\GL32", 131 .lcd_set = "\\_SB.PCI0.PX40.ECD0._Q10" 132 }, 133 { 134 .name = "L3D", 135 .mled_set = "MLED", 136 .wled_set = "WLED", 137 .brn_get = "GPLV", 138 .brn_set = "SPLV", 139 .lcd_get = "\\BKLG", 140 .lcd_set = "\\Q10" 141 }, 142 { 143 .name = "L3H", 144 .mled_set = "MLED", 145 .wled_set = "WLED", 146 .brn_get = "GPLV", 147 .brn_set = "SPLV", 148 .lcd_get = "\\_SB.PCI0.PM.PBC", 149 .lcd_set = "EHK", 150 .disp_get = "\\_SB.INFB", 151 .disp_set = "SDSP" 152 }, 153 { 154 .name = "L4R", 155 .mled_set = "MLED", 156 .wled_set = "WLED", 157 .brn_get = "GPLV", 158 .brn_set = "SPLV", 159 .lcd_get = "\\_SB.PCI0.SBSM.SEO4", 160 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 161 .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD", 162 .disp_set = "SDSP" 163 }, 164 { 165 .name = "L8L" 166 /* Only has hotkeys, apparantly */ 167 }, 168 { 169 .name = "M1A", 170 .mled_set = "MLED", 171 .brn_up = "\\_SB.PCI0.PX40.EC0.Q0E", 172 .brn_dn = "\\_SB.PCI0.PX40.EC0.Q0F", 173 .lcd_get = "\\PNOF", 174 .lcd_set = "\\_SB.PCI0.PX40.EC0.Q10" 175 }, 176 { 177 .name = "M2E", 178 .mled_set = "MLED", 179 .wled_set = "WLED", 180 .brn_get = "GPLV", 181 .brn_set = "SPLV", 182 .lcd_get = "\\GP06", 183 .lcd_set = "\\Q10" 184 }, 185 { 186 .name = "M6N", 187 .mled_set = "MLED", 188 .wled_set = "WLED", 189 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 190 .lcd_get = "\\_SB.BKLT", 191 .brn_set = "SPLV", 192 .brn_get = "GPLV", 193 .disp_set = "SDSP", 194 .disp_get = "\\SSTE" 195 }, 196 { 197 .name = "M6R", 198 .mled_set = "MLED", 199 .wled_set = "WLED", 200 .brn_get = "GPLV", 201 .brn_set = "SPLV", 202 .lcd_get = "\\_SB.PCI0.SBSM.SEO4", 203 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 204 .disp_get = "\\SSTE", 205 .disp_set = "SDSP" 206 }, 207 208 { .name = NULL } 209 }; 210 211 /* 212 * Samsung P30/P35 laptops have an Asus ATK0100 gadget interface, 213 * but they can't be probed quite the same way as Asus laptops. 214 */ 215 static struct acpi_asus_model acpi_samsung_models[] = { 216 { 217 .name = "P30", 218 .wled_set = "WLED", 219 .brn_up = "\\_SB.PCI0.LPCB.EC0._Q68", 220 .brn_dn = "\\_SB.PCI0.LPCB.EC0._Q69", 221 .lcd_get = "\\BKLT", 222 .lcd_set = "\\_SB.PCI0.LPCB.EC0._Q0E" 223 }, 224 225 { .name = NULL } 226 }; 227 228 static struct { 229 char *name; 230 char *description; 231 int method; 232 } acpi_asus_sysctls[] = { 233 { 234 .name = "lcd_backlight", 235 .method = ACPI_ASUS_METHOD_LCD, 236 .description = "state of the lcd backlight" 237 }, 238 { 239 .name = "lcd_brightness", 240 .method = ACPI_ASUS_METHOD_BRN, 241 .description = "brightness of the lcd panel" 242 }, 243 { 244 .name = "video_output", 245 .method = ACPI_ASUS_METHOD_DISP, 246 .description = "display output state" 247 }, 248 249 { .name = NULL } 250 }; 251 252 ACPI_SERIAL_DECL(asus, "ACPI ASUS extras"); 253 254 /* Function prototypes */ 255 static int acpi_asus_probe(device_t dev); 256 static int acpi_asus_attach(device_t dev); 257 static int acpi_asus_detach(device_t dev); 258 259 static void acpi_asus_led(struct acpi_asus_led *led, int state); 260 static void acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused); 261 262 static int acpi_asus_sysctl(SYSCTL_HANDLER_ARGS); 263 static int acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method); 264 static int acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method); 265 static int acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val); 266 267 static void acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context); 268 269 static device_method_t acpi_asus_methods[] = { 270 DEVMETHOD(device_probe, acpi_asus_probe), 271 DEVMETHOD(device_attach, acpi_asus_attach), 272 DEVMETHOD(device_detach, acpi_asus_detach), 273 274 { 0, 0 } 275 }; 276 277 static driver_t acpi_asus_driver = { 278 "acpi_asus", 279 acpi_asus_methods, 280 sizeof(struct acpi_asus_softc) 281 }; 282 283 static devclass_t acpi_asus_devclass; 284 285 DRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, 0, 0); 286 MODULE_DEPEND(acpi_asus, acpi, 1, 1, 1); 287 288 static int 289 acpi_asus_probe(device_t dev) 290 { 291 struct acpi_asus_model *model; 292 struct acpi_asus_softc *sc; 293 struct sbuf *sb; 294 ACPI_BUFFER Buf; 295 ACPI_OBJECT Arg, *Obj; 296 ACPI_OBJECT_LIST Args; 297 static char *asus_ids[] = { "ATK0100", NULL }; 298 299 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 300 301 if (acpi_disabled("asus") || 302 ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids) == NULL) 303 return (ENXIO); 304 305 sc = device_get_softc(dev); 306 sc->dev = dev; 307 sc->handle = acpi_get_handle(dev); 308 309 Arg.Type = ACPI_TYPE_INTEGER; 310 Arg.Integer.Value = 0; 311 312 Args.Count = 1; 313 Args.Pointer = &Arg; 314 315 Buf.Pointer = NULL; 316 Buf.Length = ACPI_ALLOCATE_BUFFER; 317 318 AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf); 319 Obj = Buf.Pointer; 320 321 /* 322 * The Samsung P30 returns a null-pointer from INIT, we 323 * can identify it from the 'ODEM' string in the DSDT. 324 */ 325 if (Obj->String.Pointer == NULL) { 326 ACPI_STATUS status; 327 ACPI_TABLE_HEADER th; 328 329 status = AcpiGetTableHeader(ACPI_TABLE_DSDT, 1, &th); 330 if (ACPI_FAILURE(status)) { 331 device_printf(dev, "Unsupported (Samsung?) laptop\n"); 332 AcpiOsFree(Buf.Pointer); 333 return (ENXIO); 334 } 335 336 if (strncmp("ODEM", th.OemTableId, 4) == 0) { 337 sc->model = &acpi_samsung_models[0]; 338 device_set_desc(dev, "Samsung P30 Laptop Extras"); 339 AcpiOsFree(Buf.Pointer); 340 return (0); 341 } 342 } 343 344 sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 345 if (sb == NULL) 346 return (ENOMEM); 347 348 /* 349 * Asus laptops are simply identified by name, easy! 350 */ 351 for (model = acpi_asus_models; model->name != NULL; model++) 352 if (strncmp(Obj->String.Pointer, model->name, 3) == 0) { 353 sbuf_printf(sb, "Asus %s Laptop Extras", model->name); 354 sbuf_finish(sb); 355 356 sc->model = model; 357 device_set_desc_copy(dev, sbuf_data(sb)); 358 359 sbuf_delete(sb); 360 AcpiOsFree(Buf.Pointer); 361 return (0); 362 } 363 364 sbuf_printf(sb, "Unsupported Asus laptop: %s\n", Obj->String.Pointer); 365 sbuf_finish(sb); 366 367 device_printf(dev, sbuf_data(sb)); 368 369 sbuf_delete(sb); 370 AcpiOsFree(Buf.Pointer); 371 372 return (ENXIO); 373 } 374 375 static int 376 acpi_asus_attach(device_t dev) 377 { 378 struct acpi_asus_softc *sc; 379 struct acpi_softc *acpi_sc; 380 381 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 382 383 sc = device_get_softc(dev); 384 acpi_sc = acpi_device_get_parent_softc(dev); 385 386 /* Build sysctl tree */ 387 sysctl_ctx_init(&sc->sysctl_ctx); 388 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 389 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), 390 OID_AUTO, "asus", CTLFLAG_RD, 0, ""); 391 392 /* Hook up nodes */ 393 for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) { 394 if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method)) 395 continue; 396 397 SYSCTL_ADD_PROC(&sc->sysctl_ctx, 398 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 399 acpi_asus_sysctls[i].name, 400 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, 401 sc, i, acpi_asus_sysctl, "I", 402 acpi_asus_sysctls[i].description); 403 } 404 405 /* Attach leds */ 406 if (sc->model->mled_set) { 407 sc->s_mled.busy = 0; 408 sc->s_mled.sc = sc; 409 sc->s_mled.type = ACPI_ASUS_LED_MLED; 410 sc->s_mled.cdev = 411 led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled"); 412 } 413 414 if (sc->model->tled_set) { 415 sc->s_tled.busy = 0; 416 sc->s_tled.sc = sc; 417 sc->s_tled.type = ACPI_ASUS_LED_TLED; 418 sc->s_tled.cdev = 419 led_create((led_t *)acpi_asus_led, &sc->s_tled, "tled"); 420 } 421 422 if (sc->model->wled_set) { 423 sc->s_wled.busy = 0; 424 sc->s_wled.sc = sc; 425 sc->s_wled.type = ACPI_ASUS_LED_WLED; 426 sc->s_wled.cdev = 427 led_create((led_t *)acpi_asus_led, &sc->s_wled, "wled"); 428 } 429 430 /* Activate hotkeys */ 431 AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL); 432 433 /* Handle notifies */ 434 AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, 435 acpi_asus_notify, dev); 436 437 return (0); 438 } 439 440 static int 441 acpi_asus_detach(device_t dev) 442 { 443 struct acpi_asus_softc *sc; 444 445 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 446 447 sc = device_get_softc(dev); 448 449 /* Turn the lights off */ 450 if (sc->model->mled_set) 451 led_destroy(sc->s_mled.cdev); 452 453 if (sc->model->tled_set) 454 led_destroy(sc->s_tled.cdev); 455 456 if (sc->model->wled_set) 457 led_destroy(sc->s_wled.cdev); 458 459 /* Remove notify handler */ 460 AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, 461 acpi_asus_notify); 462 463 /* Free sysctl tree */ 464 sysctl_ctx_free(&sc->sysctl_ctx); 465 466 return (0); 467 } 468 469 static void 470 acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused) 471 { 472 struct acpi_asus_softc *sc; 473 char *method; 474 int state; 475 476 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 477 478 sc = led->sc; 479 480 switch (led->type) { 481 case ACPI_ASUS_LED_MLED: 482 method = sc->model->mled_set; 483 484 /* Note: inverted */ 485 state = !led->state; 486 break; 487 case ACPI_ASUS_LED_TLED: 488 method = sc->model->tled_set; 489 state = led->state; 490 break; 491 case ACPI_ASUS_LED_WLED: 492 method = sc->model->wled_set; 493 state = led->state; 494 break; 495 default: 496 printf("acpi_asus_led: invalid LED type %d\n", 497 (int)led->type); 498 return; 499 } 500 501 acpi_SetInteger(sc->handle, method, state); 502 led->busy = 0; 503 } 504 505 static void 506 acpi_asus_led(struct acpi_asus_led *led, int state) 507 { 508 509 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 510 511 if (led->busy) 512 return; 513 514 led->busy = 1; 515 led->state = state; 516 517 AcpiOsQueueForExecution(OSD_PRIORITY_LO, 518 (void *)acpi_asus_led_task, led); 519 } 520 521 static int 522 acpi_asus_sysctl(SYSCTL_HANDLER_ARGS) 523 { 524 struct acpi_asus_softc *sc; 525 int arg; 526 int error = 0; 527 int function; 528 int method; 529 530 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 531 532 sc = (struct acpi_asus_softc *)oidp->oid_arg1; 533 function = oidp->oid_arg2; 534 method = acpi_asus_sysctls[function].method; 535 536 ACPI_SERIAL_BEGIN(asus); 537 arg = acpi_asus_sysctl_get(sc, method); 538 error = sysctl_handle_int(oidp, &arg, 0, req); 539 540 /* Sanity check */ 541 if (error != 0 || req->newptr == NULL) 542 goto out; 543 544 /* Update */ 545 error = acpi_asus_sysctl_set(sc, method, arg); 546 547 out: 548 ACPI_SERIAL_END(asus); 549 return (error); 550 } 551 552 static int 553 acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method) 554 { 555 int val = 0; 556 557 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 558 ACPI_SERIAL_ASSERT(asus); 559 560 switch (method) { 561 case ACPI_ASUS_METHOD_BRN: 562 val = sc->s_brn; 563 break; 564 case ACPI_ASUS_METHOD_DISP: 565 val = sc->s_disp; 566 break; 567 case ACPI_ASUS_METHOD_LCD: 568 val = sc->s_lcd; 569 break; 570 } 571 572 return (val); 573 } 574 575 static int 576 acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg) 577 { 578 ACPI_STATUS status = AE_OK; 579 580 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 581 ACPI_SERIAL_ASSERT(asus); 582 583 switch (method) { 584 case ACPI_ASUS_METHOD_BRN: 585 if (arg < 0 || arg > 15) 586 return (EINVAL); 587 588 if (sc->model->brn_set) 589 status = acpi_SetInteger(sc->handle, 590 sc->model->brn_set, arg); 591 else { 592 while (arg != 0) { 593 status = AcpiEvaluateObject(sc->handle, 594 (arg > 0) ? sc->model->brn_up : 595 sc->model->brn_dn, NULL, NULL); 596 (arg > 0) ? arg-- : arg++; 597 } 598 } 599 600 if (ACPI_SUCCESS(status)) 601 sc->s_brn = arg; 602 603 break; 604 case ACPI_ASUS_METHOD_DISP: 605 if (arg < 0 || arg > 7) 606 return (EINVAL); 607 608 status = acpi_SetInteger(sc->handle, 609 sc->model->disp_set, arg); 610 611 if (ACPI_SUCCESS(status)) 612 sc->s_disp = arg; 613 614 break; 615 case ACPI_ASUS_METHOD_LCD: 616 if (arg < 0 || arg > 1) 617 return (EINVAL); 618 619 if (strncmp(sc->model->name, "L3H", 3) != 0) 620 status = AcpiEvaluateObject(sc->handle, 621 sc->model->lcd_set, NULL, NULL); 622 else 623 status = acpi_SetInteger(sc->handle, 624 sc->model->lcd_set, 0x7); 625 626 if (ACPI_SUCCESS(status)) 627 sc->s_lcd = arg; 628 629 break; 630 } 631 632 return (0); 633 } 634 635 static int 636 acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method) 637 { 638 ACPI_STATUS status; 639 640 switch (method) { 641 case ACPI_ASUS_METHOD_BRN: 642 if (sc->model->brn_get) { 643 /* GPLV/SPLV models */ 644 status = acpi_GetInteger(sc->handle, 645 sc->model->brn_get, &sc->s_brn); 646 if (ACPI_SUCCESS(status)) 647 return (TRUE); 648 } else if (sc->model->brn_up) { 649 /* Relative models */ 650 status = AcpiEvaluateObject(sc->handle, 651 sc->model->brn_up, NULL, NULL); 652 if (ACPI_FAILURE(status)) 653 return (FALSE); 654 655 status = AcpiEvaluateObject(sc->handle, 656 sc->model->brn_dn, NULL, NULL); 657 if (ACPI_FAILURE(status)) 658 return (FALSE); 659 660 return (TRUE); 661 } 662 return (FALSE); 663 case ACPI_ASUS_METHOD_DISP: 664 if (sc->model->disp_get) { 665 status = acpi_GetInteger(sc->handle, 666 sc->model->disp_get, &sc->s_disp); 667 if (ACPI_SUCCESS(status)) 668 return (TRUE); 669 } 670 return (FALSE); 671 case ACPI_ASUS_METHOD_LCD: 672 if (sc->model->lcd_get && 673 strncmp(sc->model->name, "L3H", 3) != 0) { 674 status = acpi_GetInteger(sc->handle, 675 sc->model->lcd_get, &sc->s_lcd); 676 if (ACPI_SUCCESS(status)) 677 return (TRUE); 678 } 679 else if (sc->model->lcd_get) { 680 ACPI_BUFFER Buf; 681 ACPI_OBJECT Arg[2], Obj; 682 ACPI_OBJECT_LIST Args; 683 684 /* L3H is a bit special */ 685 Arg[0].Type = ACPI_TYPE_INTEGER; 686 Arg[0].Integer.Value = 0x02; 687 Arg[1].Type = ACPI_TYPE_INTEGER; 688 Arg[1].Integer.Value = 0x03; 689 690 Args.Count = 2; 691 Args.Pointer = Arg; 692 693 Buf.Length = sizeof(Obj); 694 Buf.Pointer = &Obj; 695 696 status = AcpiEvaluateObject(sc->handle, 697 sc->model->lcd_get, &Args, &Buf); 698 if (ACPI_SUCCESS(status) && 699 Obj.Type == ACPI_TYPE_INTEGER) { 700 sc->s_lcd = Obj.Integer.Value >> 8; 701 return (TRUE); 702 } 703 } 704 return (FALSE); 705 } 706 return (FALSE); 707 } 708 709 static void 710 acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context) 711 { 712 struct acpi_asus_softc *sc; 713 struct acpi_softc *acpi_sc; 714 715 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 716 717 sc = device_get_softc((device_t)context); 718 acpi_sc = acpi_device_get_parent_softc(sc->dev); 719 720 ACPI_SERIAL_BEGIN(asus); 721 if ((notify & ~0x10) <= 15) { 722 sc->s_brn = notify & ~0x10; 723 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); 724 } else if ((notify & ~0x20) <= 15) { 725 sc->s_brn = notify & ~0x20; 726 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); 727 } else if (notify == 0x33) { 728 sc->s_lcd = 1; 729 ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n"); 730 } else if (notify == 0x34) { 731 sc->s_lcd = 0; 732 ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n"); 733 } else { 734 /* Notify devd(8) */ 735 acpi_UserNotify("ASUS", h, notify); 736 } 737 ACPI_SERIAL_END(asus); 738 } 739