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