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