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 __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 <contrib/dev/acpica/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 *bled_set; 66 char *mled_set; 67 char *tled_set; 68 char *wled_set; 69 70 char *brn_get; 71 char *brn_set; 72 char *brn_up; 73 char *brn_dn; 74 75 char *lcd_get; 76 char *lcd_set; 77 78 char *disp_get; 79 char *disp_set; 80 }; 81 82 struct acpi_asus_led { 83 struct acpi_asus_softc *sc; 84 struct cdev *cdev; 85 int busy; 86 int state; 87 enum { 88 ACPI_ASUS_LED_BLED, 89 ACPI_ASUS_LED_MLED, 90 ACPI_ASUS_LED_TLED, 91 ACPI_ASUS_LED_WLED, 92 } type; 93 }; 94 95 struct acpi_asus_softc { 96 device_t dev; 97 ACPI_HANDLE handle; 98 99 struct acpi_asus_model *model; 100 struct sysctl_ctx_list sysctl_ctx; 101 struct sysctl_oid *sysctl_tree; 102 103 struct acpi_asus_led s_bled; 104 struct acpi_asus_led s_mled; 105 struct acpi_asus_led s_tled; 106 struct acpi_asus_led s_wled; 107 108 int s_brn; 109 int s_disp; 110 int s_lcd; 111 }; 112 113 /* 114 * We can identify Asus laptops from the string they return 115 * as a result of calling the ATK0100 'INIT' method. 116 */ 117 static struct acpi_asus_model acpi_asus_models[] = { 118 { 119 .name = "xxN", 120 .mled_set = "MLED", 121 .wled_set = "WLED", 122 .lcd_get = "\\BKLT", 123 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 124 .brn_get = "GPLV", 125 .brn_set = "SPLV", 126 .disp_get = "\\ADVG", 127 .disp_set = "SDSP" 128 }, 129 { 130 .name = "A1x", 131 .mled_set = "MLED", 132 .lcd_get = "\\BKLI", 133 .lcd_set = "\\_SB.PCI0.ISA.EC0._Q10", 134 .brn_up = "\\_SB.PCI0.ISA.EC0._Q0E", 135 .brn_dn = "\\_SB.PCI0.ISA.EC0._Q0F" 136 }, 137 { 138 .name = "A2x", 139 .mled_set = "MLED", 140 .wled_set = "WLED", 141 .lcd_get = "\\BAOF", 142 .lcd_set = "\\Q10", 143 .brn_get = "GPLV", 144 .brn_set = "SPLV", 145 .disp_get = "\\INFB", 146 .disp_set = "SDSP" 147 }, 148 { 149 .name = "D1x", 150 .mled_set = "MLED", 151 .lcd_get = "\\GP11", 152 .lcd_set = "\\Q0D", 153 .brn_up = "\\Q0C", 154 .brn_dn = "\\Q0B", 155 .disp_get = "\\INFB", 156 .disp_set = "SDSP" 157 }, 158 { 159 .name = "L2D", 160 .mled_set = "MLED", 161 .wled_set = "WLED", 162 .brn_up = "\\Q0E", 163 .brn_dn = "\\Q0F", 164 .lcd_get = "\\SGP0", 165 .lcd_set = "\\Q10" 166 }, 167 { 168 .name = "L3C", 169 .mled_set = "MLED", 170 .wled_set = "WLED", 171 .brn_get = "GPLV", 172 .brn_set = "SPLV", 173 .lcd_get = "\\GL32", 174 .lcd_set = "\\_SB.PCI0.PX40.ECD0._Q10" 175 }, 176 { 177 .name = "L3D", 178 .mled_set = "MLED", 179 .wled_set = "WLED", 180 .brn_get = "GPLV", 181 .brn_set = "SPLV", 182 .lcd_get = "\\BKLG", 183 .lcd_set = "\\Q10" 184 }, 185 { 186 .name = "L3H", 187 .mled_set = "MLED", 188 .wled_set = "WLED", 189 .brn_get = "GPLV", 190 .brn_set = "SPLV", 191 .lcd_get = "\\_SB.PCI0.PM.PBC", 192 .lcd_set = "EHK", 193 .disp_get = "\\_SB.INFB", 194 .disp_set = "SDSP" 195 }, 196 { 197 .name = "L4R", 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 = "\\_SB.PCI0.P0P1.VGA.GETD", 205 .disp_set = "SDSP" 206 }, 207 { 208 .name = "L5x", 209 .mled_set = "MLED", 210 .tled_set = "TLED", 211 .lcd_get = "\\BAOF", 212 .lcd_set = "\\Q0D", 213 .brn_get = "GPLV", 214 .brn_set = "SPLV", 215 .disp_get = "\\INFB", 216 .disp_set = "SDSP" 217 }, 218 { 219 .name = "L8L" 220 /* Only has hotkeys, apparantly */ 221 }, 222 { 223 .name = "M1A", 224 .mled_set = "MLED", 225 .brn_up = "\\_SB.PCI0.PX40.EC0.Q0E", 226 .brn_dn = "\\_SB.PCI0.PX40.EC0.Q0F", 227 .lcd_get = "\\PNOF", 228 .lcd_set = "\\_SB.PCI0.PX40.EC0.Q10" 229 }, 230 { 231 .name = "M2E", 232 .mled_set = "MLED", 233 .wled_set = "WLED", 234 .brn_get = "GPLV", 235 .brn_set = "SPLV", 236 .lcd_get = "\\GP06", 237 .lcd_set = "\\Q10" 238 }, 239 { 240 .name = "M6N", 241 .mled_set = "MLED", 242 .wled_set = "WLED", 243 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 244 .lcd_get = "\\_SB.BKLT", 245 .brn_set = "SPLV", 246 .brn_get = "GPLV", 247 .disp_set = "SDSP", 248 .disp_get = "\\SSTE" 249 }, 250 { 251 .name = "M6R", 252 .mled_set = "MLED", 253 .wled_set = "WLED", 254 .brn_get = "GPLV", 255 .brn_set = "SPLV", 256 .lcd_get = "\\_SB.PCI0.SBSM.SEO4", 257 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 258 .disp_get = "\\SSTE", 259 .disp_set = "SDSP" 260 }, 261 { 262 .name = "S1x", 263 .mled_set = "MLED", 264 .wled_set = "WLED", 265 .lcd_get = "\\PNOF", 266 .lcd_set = "\\_SB.PCI0.PX40.Q10", 267 .brn_get = "GPLV", 268 .brn_set = "SPLV" 269 }, 270 { 271 .name = "S2x", 272 .mled_set = "MLED", 273 .lcd_get = "\\BKLI", 274 .lcd_set = "\\_SB.PCI0.ISA.EC0._Q10", 275 .brn_up = "\\_SB.PCI0.ISA.EC0._Q0B", 276 .brn_dn = "\\_SB.PCI0.ISA.EC0._Q0A" 277 }, 278 { 279 .name = "V6V", 280 .bled_set = "BLED", 281 .tled_set = "TLED", 282 .wled_set = "WLED", 283 .lcd_get = "\\BKLT", 284 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10", 285 .brn_get = "GPLV", 286 .brn_set = "SPLV", 287 .disp_get = "\\_SB.PCI0.P0P1.VGA.GETD", 288 .disp_set = "SDSP" 289 }, 290 291 { .name = NULL } 292 }; 293 294 /* 295 * Samsung P30/P35 laptops have an Asus ATK0100 gadget interface, 296 * but they can't be probed quite the same way as Asus laptops. 297 */ 298 static struct acpi_asus_model acpi_samsung_models[] = { 299 { 300 .name = "P30", 301 .wled_set = "WLED", 302 .brn_up = "\\_SB.PCI0.LPCB.EC0._Q68", 303 .brn_dn = "\\_SB.PCI0.LPCB.EC0._Q69", 304 .lcd_get = "\\BKLT", 305 .lcd_set = "\\_SB.PCI0.LPCB.EC0._Q0E" 306 }, 307 308 { .name = NULL } 309 }; 310 311 static struct { 312 char *name; 313 char *description; 314 int method; 315 } acpi_asus_sysctls[] = { 316 { 317 .name = "lcd_backlight", 318 .method = ACPI_ASUS_METHOD_LCD, 319 .description = "state of the lcd backlight" 320 }, 321 { 322 .name = "lcd_brightness", 323 .method = ACPI_ASUS_METHOD_BRN, 324 .description = "brightness of the lcd panel" 325 }, 326 { 327 .name = "video_output", 328 .method = ACPI_ASUS_METHOD_DISP, 329 .description = "display output state" 330 }, 331 332 { .name = NULL } 333 }; 334 335 ACPI_SERIAL_DECL(asus, "ACPI ASUS extras"); 336 337 /* Function prototypes */ 338 static int acpi_asus_probe(device_t dev); 339 static int acpi_asus_attach(device_t dev); 340 static int acpi_asus_detach(device_t dev); 341 342 static void acpi_asus_led(struct acpi_asus_led *led, int state); 343 static void acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused); 344 345 static int acpi_asus_sysctl(SYSCTL_HANDLER_ARGS); 346 static int acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method); 347 static int acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method); 348 static int acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val); 349 350 static void acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context); 351 352 static device_method_t acpi_asus_methods[] = { 353 DEVMETHOD(device_probe, acpi_asus_probe), 354 DEVMETHOD(device_attach, acpi_asus_attach), 355 DEVMETHOD(device_detach, acpi_asus_detach), 356 357 { 0, 0 } 358 }; 359 360 static driver_t acpi_asus_driver = { 361 "acpi_asus", 362 acpi_asus_methods, 363 sizeof(struct acpi_asus_softc) 364 }; 365 366 static devclass_t acpi_asus_devclass; 367 368 DRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, 0, 0); 369 MODULE_DEPEND(acpi_asus, acpi, 1, 1, 1); 370 371 static int 372 acpi_asus_probe(device_t dev) 373 { 374 struct acpi_asus_model *model; 375 struct acpi_asus_softc *sc; 376 struct sbuf *sb; 377 ACPI_BUFFER Buf; 378 ACPI_OBJECT Arg, *Obj; 379 ACPI_OBJECT_LIST Args; 380 static char *asus_ids[] = { "ATK0100", NULL }; 381 382 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 383 384 if (acpi_disabled("asus") || 385 ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids) == NULL) 386 return (ENXIO); 387 388 sc = device_get_softc(dev); 389 sc->dev = dev; 390 sc->handle = acpi_get_handle(dev); 391 392 Arg.Type = ACPI_TYPE_INTEGER; 393 Arg.Integer.Value = 0; 394 395 Args.Count = 1; 396 Args.Pointer = &Arg; 397 398 Buf.Pointer = NULL; 399 Buf.Length = ACPI_ALLOCATE_BUFFER; 400 401 AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf); 402 Obj = Buf.Pointer; 403 404 /* 405 * The Samsung P30 returns a null-pointer from INIT, we 406 * can identify it from the 'ODEM' string in the DSDT. 407 */ 408 if (Obj->String.Pointer == NULL) { 409 ACPI_STATUS status; 410 ACPI_TABLE_HEADER th; 411 412 status = AcpiGetTableHeader(ACPI_TABLE_DSDT, 1, &th); 413 if (ACPI_FAILURE(status)) { 414 device_printf(dev, "Unsupported (Samsung?) laptop\n"); 415 AcpiOsFree(Buf.Pointer); 416 return (ENXIO); 417 } 418 419 if (strncmp("ODEM", th.OemTableId, 4) == 0) { 420 sc->model = &acpi_samsung_models[0]; 421 device_set_desc(dev, "Samsung P30 Laptop Extras"); 422 AcpiOsFree(Buf.Pointer); 423 return (0); 424 } 425 } 426 427 sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 428 if (sb == NULL) 429 return (ENOMEM); 430 431 /* 432 * Asus laptops are simply identified by name, easy! 433 */ 434 for (model = acpi_asus_models; model->name != NULL; model++) { 435 if (strncmp(Obj->String.Pointer, model->name, 3) == 0) { 436 437 good: 438 sbuf_printf(sb, "Asus %s Laptop Extras", 439 Obj->String.Pointer); 440 sbuf_finish(sb); 441 442 sc->model = model; 443 device_set_desc_copy(dev, sbuf_data(sb)); 444 445 sbuf_delete(sb); 446 AcpiOsFree(Buf.Pointer); 447 return (0); 448 } 449 450 /* 451 * Some models look exactly the same as other models, but have 452 * their own ids. If we spot these, set them up with the same 453 * details as the models they're like, possibly dealing with 454 * small differences. 455 * 456 * XXX: there must be a prettier way to do this! 457 */ 458 else if (strncmp(model->name, "xxN", 3) == 0 && 459 (strncmp(Obj->String.Pointer, "M3N", 3) == 0 || 460 strncmp(Obj->String.Pointer, "S1N", 3) == 0)) 461 goto good; 462 else if (strncmp(model->name, "A1x", 3) == 0 && 463 strncmp(Obj->String.Pointer, "A1", 2) == 0) 464 goto good; 465 else if (strncmp(model->name, "A2x", 3) == 0 && 466 strncmp(Obj->String.Pointer, "A2", 2) == 0) 467 goto good; 468 else if (strncmp(model->name, "D1x", 3) == 0 && 469 strncmp(Obj->String.Pointer, "D1", 2) == 0) 470 goto good; 471 else if (strncmp(model->name, "L3H", 3) == 0 && 472 strncmp(Obj->String.Pointer, "L2E", 3) == 0) 473 goto good; 474 else if (strncmp(model->name, "L5x", 3) == 0 && 475 strncmp(Obj->String.Pointer, "L5", 2) == 0) 476 goto good; 477 else if (strncmp(model->name, "M2E", 3) == 0 && 478 (strncmp(Obj->String.Pointer, "M2", 2) == 0 || 479 strncmp(Obj->String.Pointer, "L4E", 3) == 0)) 480 goto good; 481 else if (strncmp(model->name, "S1x", 3) == 0 && 482 (strncmp(Obj->String.Pointer, "L8", 2) == 0 || 483 strncmp(Obj->String.Pointer, "S1", 2) == 0)) 484 goto good; 485 else if (strncmp(model->name, "S2x", 3) == 0 && 486 (strncmp(Obj->String.Pointer, "J1", 2) == 0 || 487 strncmp(Obj->String.Pointer, "S2", 2) == 0)) 488 goto good; 489 490 /* L2B is like L3C but has no lcd_get method */ 491 else if (strncmp(model->name, "L3C", 3) == 0 && 492 strncmp(Obj->String.Pointer, "L2B", 3) == 0) { 493 model->lcd_get = NULL; 494 goto good; 495 } 496 497 /* A3G is like M6R but with a different lcd_get method */ 498 else if (strncmp(model->name, "M6R", 3) == 0 && 499 strncmp(Obj->String.Pointer, "A3G", 3) == 0) { 500 model->lcd_get = "\\BLFG"; 501 goto good; 502 } 503 504 /* M2N and W1N are like xxN with added WLED */ 505 else if (strncmp(model->name, "xxN", 3) == 0 && 506 (strncmp(Obj->String.Pointer, "M2N", 3) == 0 || 507 strncmp(Obj->String.Pointer, "W1N", 3) == 0)) { 508 model->wled_set = "WLED"; 509 goto good; 510 } 511 512 /* M5N and S5N are like xxN without MLED */ 513 else if (strncmp(model->name, "xxN", 3) == 0 && 514 (strncmp(Obj->String.Pointer, "M5N", 3) == 0 || 515 strncmp(Obj->String.Pointer, "S5N", 3) == 0)) { 516 model->mled_set = NULL; 517 goto good; 518 } 519 } 520 521 sbuf_printf(sb, "Unsupported Asus laptop: %s\n", Obj->String.Pointer); 522 sbuf_finish(sb); 523 524 device_printf(dev, sbuf_data(sb)); 525 526 sbuf_delete(sb); 527 AcpiOsFree(Buf.Pointer); 528 529 return (ENXIO); 530 } 531 532 static int 533 acpi_asus_attach(device_t dev) 534 { 535 struct acpi_asus_softc *sc; 536 struct acpi_softc *acpi_sc; 537 538 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 539 540 sc = device_get_softc(dev); 541 acpi_sc = acpi_device_get_parent_softc(dev); 542 543 /* Build sysctl tree */ 544 sysctl_ctx_init(&sc->sysctl_ctx); 545 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 546 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), 547 OID_AUTO, "asus", CTLFLAG_RD, 0, ""); 548 549 /* Hook up nodes */ 550 for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) { 551 if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method)) 552 continue; 553 554 SYSCTL_ADD_PROC(&sc->sysctl_ctx, 555 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 556 acpi_asus_sysctls[i].name, 557 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, 558 sc, i, acpi_asus_sysctl, "I", 559 acpi_asus_sysctls[i].description); 560 } 561 562 /* Attach leds */ 563 if (sc->model->bled_set) { 564 sc->s_bled.busy = 0; 565 sc->s_bled.sc = sc; 566 sc->s_bled.type = ACPI_ASUS_LED_BLED; 567 sc->s_bled.cdev = 568 led_create((led_t *)acpi_asus_led, &sc->s_bled, "bled"); 569 } 570 571 if (sc->model->mled_set) { 572 sc->s_mled.busy = 0; 573 sc->s_mled.sc = sc; 574 sc->s_mled.type = ACPI_ASUS_LED_MLED; 575 sc->s_mled.cdev = 576 led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled"); 577 } 578 579 if (sc->model->tled_set) { 580 sc->s_tled.busy = 0; 581 sc->s_tled.sc = sc; 582 sc->s_tled.type = ACPI_ASUS_LED_TLED; 583 sc->s_tled.cdev = 584 led_create((led_t *)acpi_asus_led, &sc->s_tled, "tled"); 585 } 586 587 if (sc->model->wled_set) { 588 sc->s_wled.busy = 0; 589 sc->s_wled.sc = sc; 590 sc->s_wled.type = ACPI_ASUS_LED_WLED; 591 sc->s_wled.cdev = 592 led_create((led_t *)acpi_asus_led, &sc->s_wled, "wled"); 593 } 594 595 /* Activate hotkeys */ 596 AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL); 597 598 /* Handle notifies */ 599 AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, 600 acpi_asus_notify, dev); 601 602 return (0); 603 } 604 605 static int 606 acpi_asus_detach(device_t dev) 607 { 608 struct acpi_asus_softc *sc; 609 610 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 611 612 sc = device_get_softc(dev); 613 614 /* Turn the lights off */ 615 if (sc->model->bled_set) 616 led_destroy(sc->s_bled.cdev); 617 618 if (sc->model->mled_set) 619 led_destroy(sc->s_mled.cdev); 620 621 if (sc->model->tled_set) 622 led_destroy(sc->s_tled.cdev); 623 624 if (sc->model->wled_set) 625 led_destroy(sc->s_wled.cdev); 626 627 /* Remove notify handler */ 628 AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, 629 acpi_asus_notify); 630 631 /* Free sysctl tree */ 632 sysctl_ctx_free(&sc->sysctl_ctx); 633 634 return (0); 635 } 636 637 static void 638 acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused) 639 { 640 struct acpi_asus_softc *sc; 641 char *method; 642 int state; 643 644 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 645 646 sc = led->sc; 647 648 switch (led->type) { 649 case ACPI_ASUS_LED_BLED: 650 method = sc->model->bled_set; 651 state = led->state; 652 break; 653 case ACPI_ASUS_LED_MLED: 654 method = sc->model->mled_set; 655 656 /* Note: inverted */ 657 state = !led->state; 658 break; 659 case ACPI_ASUS_LED_TLED: 660 method = sc->model->tled_set; 661 state = led->state; 662 break; 663 case ACPI_ASUS_LED_WLED: 664 method = sc->model->wled_set; 665 state = led->state; 666 break; 667 default: 668 printf("acpi_asus_led: invalid LED type %d\n", 669 (int)led->type); 670 return; 671 } 672 673 acpi_SetInteger(sc->handle, method, state); 674 led->busy = 0; 675 } 676 677 static void 678 acpi_asus_led(struct acpi_asus_led *led, int state) 679 { 680 681 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 682 683 if (led->busy) 684 return; 685 686 led->busy = 1; 687 led->state = state; 688 689 AcpiOsQueueForExecution(OSD_PRIORITY_LO, 690 (void *)acpi_asus_led_task, led); 691 } 692 693 static int 694 acpi_asus_sysctl(SYSCTL_HANDLER_ARGS) 695 { 696 struct acpi_asus_softc *sc; 697 int arg; 698 int error = 0; 699 int function; 700 int method; 701 702 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 703 704 sc = (struct acpi_asus_softc *)oidp->oid_arg1; 705 function = oidp->oid_arg2; 706 method = acpi_asus_sysctls[function].method; 707 708 ACPI_SERIAL_BEGIN(asus); 709 arg = acpi_asus_sysctl_get(sc, method); 710 error = sysctl_handle_int(oidp, &arg, 0, req); 711 712 /* Sanity check */ 713 if (error != 0 || req->newptr == NULL) 714 goto out; 715 716 /* Update */ 717 error = acpi_asus_sysctl_set(sc, method, arg); 718 719 out: 720 ACPI_SERIAL_END(asus); 721 return (error); 722 } 723 724 static int 725 acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method) 726 { 727 int val = 0; 728 729 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 730 ACPI_SERIAL_ASSERT(asus); 731 732 switch (method) { 733 case ACPI_ASUS_METHOD_BRN: 734 val = sc->s_brn; 735 break; 736 case ACPI_ASUS_METHOD_DISP: 737 val = sc->s_disp; 738 break; 739 case ACPI_ASUS_METHOD_LCD: 740 val = sc->s_lcd; 741 break; 742 } 743 744 return (val); 745 } 746 747 static int 748 acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg) 749 { 750 ACPI_STATUS status = AE_OK; 751 752 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 753 ACPI_SERIAL_ASSERT(asus); 754 755 switch (method) { 756 case ACPI_ASUS_METHOD_BRN: 757 if (arg < 0 || arg > 15) 758 return (EINVAL); 759 760 if (sc->model->brn_set) 761 status = acpi_SetInteger(sc->handle, 762 sc->model->brn_set, arg); 763 else { 764 while (arg != 0) { 765 status = AcpiEvaluateObject(sc->handle, 766 (arg > 0) ? sc->model->brn_up : 767 sc->model->brn_dn, NULL, NULL); 768 (arg > 0) ? arg-- : arg++; 769 } 770 } 771 772 if (ACPI_SUCCESS(status)) 773 sc->s_brn = arg; 774 775 break; 776 case ACPI_ASUS_METHOD_DISP: 777 if (arg < 0 || arg > 7) 778 return (EINVAL); 779 780 status = acpi_SetInteger(sc->handle, 781 sc->model->disp_set, arg); 782 783 if (ACPI_SUCCESS(status)) 784 sc->s_disp = arg; 785 786 break; 787 case ACPI_ASUS_METHOD_LCD: 788 if (arg < 0 || arg > 1) 789 return (EINVAL); 790 791 if (strncmp(sc->model->name, "L3H", 3) != 0) 792 status = AcpiEvaluateObject(sc->handle, 793 sc->model->lcd_set, NULL, NULL); 794 else 795 status = acpi_SetInteger(sc->handle, 796 sc->model->lcd_set, 0x7); 797 798 if (ACPI_SUCCESS(status)) 799 sc->s_lcd = arg; 800 801 break; 802 } 803 804 return (0); 805 } 806 807 static int 808 acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method) 809 { 810 ACPI_STATUS status; 811 812 switch (method) { 813 case ACPI_ASUS_METHOD_BRN: 814 if (sc->model->brn_get) { 815 /* GPLV/SPLV models */ 816 status = acpi_GetInteger(sc->handle, 817 sc->model->brn_get, &sc->s_brn); 818 if (ACPI_SUCCESS(status)) 819 return (TRUE); 820 } else if (sc->model->brn_up) { 821 /* Relative models */ 822 status = AcpiEvaluateObject(sc->handle, 823 sc->model->brn_up, NULL, NULL); 824 if (ACPI_FAILURE(status)) 825 return (FALSE); 826 827 status = AcpiEvaluateObject(sc->handle, 828 sc->model->brn_dn, NULL, NULL); 829 if (ACPI_FAILURE(status)) 830 return (FALSE); 831 832 return (TRUE); 833 } 834 return (FALSE); 835 case ACPI_ASUS_METHOD_DISP: 836 if (sc->model->disp_get) { 837 status = acpi_GetInteger(sc->handle, 838 sc->model->disp_get, &sc->s_disp); 839 if (ACPI_SUCCESS(status)) 840 return (TRUE); 841 } 842 return (FALSE); 843 case ACPI_ASUS_METHOD_LCD: 844 if (sc->model->lcd_get && 845 strncmp(sc->model->name, "L3H", 3) != 0) { 846 status = acpi_GetInteger(sc->handle, 847 sc->model->lcd_get, &sc->s_lcd); 848 if (ACPI_SUCCESS(status)) 849 return (TRUE); 850 } 851 else if (sc->model->lcd_get) { 852 ACPI_BUFFER Buf; 853 ACPI_OBJECT Arg[2], Obj; 854 ACPI_OBJECT_LIST Args; 855 856 /* L3H is a bit special */ 857 Arg[0].Type = ACPI_TYPE_INTEGER; 858 Arg[0].Integer.Value = 0x02; 859 Arg[1].Type = ACPI_TYPE_INTEGER; 860 Arg[1].Integer.Value = 0x03; 861 862 Args.Count = 2; 863 Args.Pointer = Arg; 864 865 Buf.Length = sizeof(Obj); 866 Buf.Pointer = &Obj; 867 868 status = AcpiEvaluateObject(sc->handle, 869 sc->model->lcd_get, &Args, &Buf); 870 if (ACPI_SUCCESS(status) && 871 Obj.Type == ACPI_TYPE_INTEGER) { 872 sc->s_lcd = Obj.Integer.Value >> 8; 873 return (TRUE); 874 } 875 } 876 return (FALSE); 877 } 878 return (FALSE); 879 } 880 881 static void 882 acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context) 883 { 884 struct acpi_asus_softc *sc; 885 struct acpi_softc *acpi_sc; 886 887 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 888 889 sc = device_get_softc((device_t)context); 890 acpi_sc = acpi_device_get_parent_softc(sc->dev); 891 892 ACPI_SERIAL_BEGIN(asus); 893 if ((notify & ~0x10) <= 15) { 894 sc->s_brn = notify & ~0x10; 895 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n"); 896 } else if ((notify & ~0x20) <= 15) { 897 sc->s_brn = notify & ~0x20; 898 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n"); 899 } else if (notify == 0x33) { 900 sc->s_lcd = 1; 901 ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n"); 902 } else if (notify == 0x34) { 903 sc->s_lcd = 0; 904 ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n"); 905 } else { 906 /* Notify devd(8) */ 907 acpi_UserNotify("ASUS", h, notify); 908 } 909 ACPI_SERIAL_END(asus); 910 } 911