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