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