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