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