led(4): Turn this into a module. Only used by acpi_thinkpad and acpi_asus.
[dragonfly.git] / sys / dev / acpica / acpi_asus / acpi_asus.c
CommitLineData
bf334cef
SW
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.
32af04f7 25 *
3fe31f5d 26 * $FreeBSD: src/sys/dev/acpi_support/acpi_asus.c,v 1.44 2010/06/11 20:08:20 jkim Exp $
bf334cef
SW
27 */
28
bf334cef
SW
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>
bf334cef 45#include <sys/module.h>
10f97674 46#include <sys/bus.h>
bf334cef 47#include <sys/sbuf.h>
bf334cef
SW
48
49#include "acpi.h"
da42c799 50#include "accommon.h"
bf334cef 51#include "acpivar.h"
10f97674 52
e9283513 53#include <dev/misc/led/led.h>
bf334cef
SW
54
55/* Methods */
56#define ACPI_ASUS_METHOD_BRN 1
57#define ACPI_ASUS_METHOD_DISP 2
58#define ACPI_ASUS_METHOD_LCD 3
10f97674
AP
59#define ACPI_ASUS_METHOD_CAMERA 4
60#define ACPI_ASUS_METHOD_CARDRD 5
61#define ACPI_ASUS_METHOD_WLAN 6
bf334cef
SW
62
63#define _COMPONENT ACPI_OEM
64ACPI_MODULE_NAME("ASUS")
65
66struct acpi_asus_model {
67 char *name;
bf334cef 68 char *bled_set;
10f97674
AP
69 char *dled_set;
70 char *gled_set;
bf334cef
SW
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;
10f97674
AP
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 *);
bf334cef
SW
99};
100
101struct 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,
10f97674
AP
108 ACPI_ASUS_LED_DLED,
109 ACPI_ASUS_LED_GLED,
bf334cef
SW
110 ACPI_ASUS_LED_MLED,
111 ACPI_ASUS_LED_TLED,
112 ACPI_ASUS_LED_WLED,
113 } type;
114};
115
116struct acpi_asus_softc {
117 device_t dev;
118 ACPI_HANDLE handle;
10f97674 119 ACPI_HANDLE lcdd_handle;
bf334cef
SW
120
121 struct acpi_asus_model *model;
122 struct sysctl_ctx_list sysctl_ctx;
123 struct sysctl_oid *sysctl_tree;
bf334cef 124 struct acpi_asus_led s_bled;
10f97674
AP
125 struct acpi_asus_led s_dled;
126 struct acpi_asus_led s_gled;
bf334cef
SW
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;
10f97674
AP
134 int s_cam;
135 int s_crd;
136 int s_wlan;
bf334cef
SW
137};
138
10f97674
AP
139static void acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify,
140 void *context);
141
bf334cef
SW
142/*
143 * We can identify Asus laptops from the string they return
144 * as a result of calling the ATK0100 'INIT' method.
145 */
146static 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 },
10f97674
AP
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 },
bf334cef
SW
200 {
201 .name = "A3N",
202 .mled_set = "MLED",
203 .bled_set = "BLED",
204 .wled_set = "WLED",
10f97674 205 .lcd_get = "\\BKLT",
bf334cef 206 .lcd_set = "\\_SB.PCI0.SBRG.EC0._Q10",
10f97674 207 .brn_get = "GPLV",
bf334cef 208 .brn_set = "SPLV",
10f97674
AP
209 .disp_get = "\\_SB.PCI0.P0P3.VGA.GETD",
210 .disp_set = "SDSP"
bf334cef
SW
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 },
10f97674
AP
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 },
bf334cef
SW
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 },
10f97674
AP
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",
3fe31f5d
SW
270 .lcd_get = "GBTL",
271 .lcd_set = "SBTL",
10f97674
AP
272 .disp_get = "\\_SB.PCI0.PCE2.VGA.GETD",
273 .disp_set = "SDSP",
274 },
bf334cef
SW
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"
10f97674 337 /* Only has hotkeys, apparently */
bf334cef
SW
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 */
425static 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
10f97674
AP
438static void acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context);
439
bf334cef
SW
440/*
441 * EeePC have an Asus ASUS010 gadget interface,
442 * but they can't be probed quite the same way as Asus laptops.
443 */
444static struct acpi_asus_model acpi_eeepc_models[] = {
445 {
446 .name = "EEE",
447 .brn_get = "\\_SB.ATKD.PBLG",
10f97674
AP
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
bf334cef
SW
456 },
457
458 { .name = NULL }
459};
460
461static struct {
462 char *name;
463 char *description;
464 int method;
10f97674 465 int flags;
bf334cef
SW
466} acpi_asus_sysctls[] = {
467 {
468 .name = "lcd_backlight",
469 .method = ACPI_ASUS_METHOD_LCD,
10f97674
AP
470 .description = "state of the lcd backlight",
471 .flags = CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY
bf334cef
SW
472 },
473 {
474 .name = "lcd_brightness",
475 .method = ACPI_ASUS_METHOD_BRN,
10f97674
AP
476 .description = "brightness of the lcd panel",
477 .flags = CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY
bf334cef
SW
478 },
479 {
480 .name = "video_output",
481 .method = ACPI_ASUS_METHOD_DISP,
10f97674
AP
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
bf334cef
SW
502 },
503
504 { .name = NULL }
505};
506
10f97674
AP
507ACPI_SERIAL_DECL(asus, "ACPI ASUS extras");
508
509/* Function prototypes */
bf334cef
SW
510static int acpi_asus_probe(device_t dev);
511static int acpi_asus_attach(device_t dev);
512static int acpi_asus_detach(device_t dev);
513
10f97674
AP
514static void acpi_asus_led(struct acpi_asus_led *led, int state);
515static void acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused);
bf334cef
SW
516
517static int acpi_asus_sysctl(SYSCTL_HANDLER_ARGS);
518static int acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method);
519static int acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method);
520static int acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val);
521
522static void acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context);
523
524static device_method_t acpi_asus_methods[] = {
10f97674 525 DEVMETHOD(device_probe, acpi_asus_probe),
bf334cef
SW
526 DEVMETHOD(device_attach, acpi_asus_attach),
527 DEVMETHOD(device_detach, acpi_asus_detach),
10f97674 528
d3c9c58e 529 DEVMETHOD_END
bf334cef
SW
530};
531
10f97674 532static driver_t acpi_asus_driver = {
bf334cef
SW
533 "acpi_asus",
534 acpi_asus_methods,
10f97674 535 sizeof(struct acpi_asus_softc)
bf334cef
SW
536};
537
538static devclass_t acpi_asus_devclass;
539
aa2b9d05 540DRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, NULL, NULL);
bf334cef 541MODULE_DEPEND(acpi_asus, acpi, 1, 1, 1);
f2c400e8 542MODULE_DEPEND(acpi_asus, led, 1, 1, 1);
bf334cef 543
bf334cef
SW
544static int
545acpi_asus_probe(device_t dev)
546{
547 struct acpi_asus_model *model;
548 struct acpi_asus_softc *sc;
549 struct sbuf *sb;
550 ACPI_BUFFER Buf;
551 ACPI_OBJECT Arg, *Obj;
552 ACPI_OBJECT_LIST Args;
10f97674 553 static char *asus_ids[] = { "ATK0100", "ASUS010", NULL };
bf334cef
SW
554 char *rstr;
555
556 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
557
10f97674
AP
558 if (acpi_disabled("asus"))
559 return (ENXIO);
560 ACPI_SERIAL_INIT(asus);
bf334cef
SW
561 rstr = ACPI_ID_PROBE(device_get_parent(dev), dev, asus_ids);
562 if (rstr == NULL) {
563 return (ENXIO);
564 }
565
bf334cef
SW
566 sc = device_get_softc(dev);
567 sc->dev = dev;
568 sc->handle = acpi_get_handle(dev);
569
bf334cef
SW
570 Arg.Type = ACPI_TYPE_INTEGER;
571 Arg.Integer.Value = 0;
572
573 Args.Count = 1;
574 Args.Pointer = &Arg;
575
576 Buf.Pointer = NULL;
577 Buf.Length = ACPI_ALLOCATE_BUFFER;
578
579 AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf);
580 Obj = Buf.Pointer;
581
582 /*
583 * The Samsung P30 returns a null-pointer from INIT, we
584 * can identify it from the 'ODEM' string in the DSDT.
585 */
586 if (Obj->String.Pointer == NULL) {
587 ACPI_STATUS status;
588 ACPI_TABLE_HEADER th;
589
10f97674 590 status = AcpiGetTableHeader(ACPI_SIG_DSDT, 0, &th);
bf334cef
SW
591 if (ACPI_FAILURE(status)) {
592 device_printf(dev, "Unsupported (Samsung?) laptop\n");
593 AcpiOsFree(Buf.Pointer);
594 return (ENXIO);
595 }
596
597 if (strncmp("ODEM", th.OemTableId, 4) == 0) {
598 sc->model = &acpi_samsung_models[0];
599 device_set_desc(dev, "Samsung P30 Laptop Extras");
600 AcpiOsFree(Buf.Pointer);
601 return (0);
602 }
603
10f97674 604 /* EeePC */
bf334cef
SW
605 if (strncmp("ASUS010", rstr, 7) == 0) {
606 sc->model = &acpi_eeepc_models[0];
607 device_set_desc(dev, "ASUS EeePC");
608 AcpiOsFree(Buf.Pointer);
609 return (0);
610 }
611 }
612
10f97674 613 sb = sbuf_new_auto();
bf334cef
SW
614 if (sb == NULL)
615 return (ENOMEM);
616
617 /*
618 * Asus laptops are simply identified by name, easy!
619 */
620 for (model = acpi_asus_models; model->name != NULL; model++) {
621 if (strncmp(Obj->String.Pointer, model->name, 3) == 0) {
622
623good:
624 sbuf_printf(sb, "Asus %s Laptop Extras",
625 Obj->String.Pointer);
626 sbuf_finish(sb);
627
628 sc->model = model;
629 device_set_desc_copy(dev, sbuf_data(sb));
630
631 sbuf_delete(sb);
632 AcpiOsFree(Buf.Pointer);
633 return (0);
634 }
10f97674 635
bf334cef
SW
636 /*
637 * Some models look exactly the same as other models, but have
638 * their own ids. If we spot these, set them up with the same
639 * details as the models they're like, possibly dealing with
640 * small differences.
641 *
642 * XXX: there must be a prettier way to do this!
643 */
644 else if (strncmp(model->name, "xxN", 3) == 0 &&
645 (strncmp(Obj->String.Pointer, "M3N", 3) == 0 ||
646 strncmp(Obj->String.Pointer, "S1N", 3) == 0))
647 goto good;
648 else if (strncmp(model->name, "A1x", 3) == 0 &&
649 strncmp(Obj->String.Pointer, "A1", 2) == 0)
650 goto good;
651 else if (strncmp(model->name, "A2x", 3) == 0 &&
652 strncmp(Obj->String.Pointer, "A2", 2) == 0)
653 goto good;
10f97674
AP
654 else if (strncmp(model->name, "A3F", 3) == 0 &&
655 strncmp(Obj->String.Pointer, "A6F", 3) == 0)
656 goto good;
bf334cef
SW
657 else if (strncmp(model->name, "D1x", 3) == 0 &&
658 strncmp(Obj->String.Pointer, "D1", 2) == 0)
659 goto good;
660 else if (strncmp(model->name, "L3H", 3) == 0 &&
661 strncmp(Obj->String.Pointer, "L2E", 3) == 0)
662 goto good;
663 else if (strncmp(model->name, "L5x", 3) == 0 &&
664 strncmp(Obj->String.Pointer, "L5", 2) == 0)
665 goto good;
666 else if (strncmp(model->name, "M2E", 3) == 0 &&
667 (strncmp(Obj->String.Pointer, "M2", 2) == 0 ||
668 strncmp(Obj->String.Pointer, "L4E", 3) == 0))
669 goto good;
670 else if (strncmp(model->name, "S1x", 3) == 0 &&
671 (strncmp(Obj->String.Pointer, "L8", 2) == 0 ||
672 strncmp(Obj->String.Pointer, "S1", 2) == 0))
673 goto good;
674 else if (strncmp(model->name, "S2x", 3) == 0 &&
675 (strncmp(Obj->String.Pointer, "J1", 2) == 0 ||
676 strncmp(Obj->String.Pointer, "S2", 2) == 0))
677 goto good;
678
679 /* L2B is like L3C but has no lcd_get method */
680 else if (strncmp(model->name, "L3C", 3) == 0 &&
681 strncmp(Obj->String.Pointer, "L2B", 3) == 0) {
682 model->lcd_get = NULL;
683 goto good;
684 }
685
686 /* A3G is like M6R but with a different lcd_get method */
687 else if (strncmp(model->name, "M6R", 3) == 0 &&
688 strncmp(Obj->String.Pointer, "A3G", 3) == 0) {
689 model->lcd_get = "\\BLFG";
690 goto good;
691 }
692
693 /* M2N and W1N are like xxN with added WLED */
694 else if (strncmp(model->name, "xxN", 3) == 0 &&
695 (strncmp(Obj->String.Pointer, "M2N", 3) == 0 ||
696 strncmp(Obj->String.Pointer, "W1N", 3) == 0)) {
697 model->wled_set = "WLED";
698 goto good;
699 }
700
701 /* M5N and S5N are like xxN without MLED */
702 else if (strncmp(model->name, "xxN", 3) == 0 &&
703 (strncmp(Obj->String.Pointer, "M5N", 3) == 0 ||
704 strncmp(Obj->String.Pointer, "S5N", 3) == 0)) {
705 model->mled_set = NULL;
706 goto good;
707 }
708 }
709
710 sbuf_printf(sb, "Unsupported Asus laptop: %s\n", Obj->String.Pointer);
711 sbuf_finish(sb);
712
a7a95662 713 device_printf(dev, "%s", sbuf_data(sb));
bf334cef
SW
714
715 sbuf_delete(sb);
716 AcpiOsFree(Buf.Pointer);
717
718 return (ENXIO);
719}
720
bf334cef
SW
721static int
722acpi_asus_attach(device_t dev)
723{
724 struct acpi_asus_softc *sc;
725 struct acpi_softc *acpi_sc;
726
727 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
728
729 sc = device_get_softc(dev);
730 acpi_sc = acpi_device_get_parent_softc(dev);
731
732 /* Build sysctl tree */
733 sysctl_ctx_init(&sc->sysctl_ctx);
734 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
735 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
736 OID_AUTO, "asus", CTLFLAG_RD, 0, "");
737
738 /* Hook up nodes */
739 for (int i = 0; acpi_asus_sysctls[i].name != NULL; i++) {
740 if (!acpi_asus_sysctl_init(sc, acpi_asus_sysctls[i].method))
741 continue;
742
743 SYSCTL_ADD_PROC(&sc->sysctl_ctx,
744 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
745 acpi_asus_sysctls[i].name,
10f97674 746 acpi_asus_sysctls[i].flags,
bf334cef
SW
747 sc, i, acpi_asus_sysctl, "I",
748 acpi_asus_sysctls[i].description);
749 }
750
751 /* Attach leds */
bf334cef
SW
752 if (sc->model->bled_set) {
753 sc->s_bled.busy = 0;
754 sc->s_bled.sc = sc;
755 sc->s_bled.type = ACPI_ASUS_LED_BLED;
756 sc->s_bled.cdev =
10f97674
AP
757 led_create_state((led_t *)acpi_asus_led, &sc->s_bled,
758 "bled", 1);
759 }
760
761 if (sc->model->dled_set) {
762 sc->s_dled.busy = 0;
763 sc->s_dled.sc = sc;
764 sc->s_dled.type = ACPI_ASUS_LED_DLED;
765 sc->s_dled.cdev =
766 led_create((led_t *)acpi_asus_led, &sc->s_dled, "dled");
767 }
768
769 if (sc->model->gled_set) {
770 sc->s_gled.busy = 0;
771 sc->s_gled.sc = sc;
772 sc->s_gled.type = ACPI_ASUS_LED_GLED;
773 sc->s_gled.cdev =
774 led_create((led_t *)acpi_asus_led, &sc->s_gled, "gled");
bf334cef
SW
775 }
776
777 if (sc->model->mled_set) {
778 sc->s_mled.busy = 0;
779 sc->s_mled.sc = sc;
780 sc->s_mled.type = ACPI_ASUS_LED_MLED;
781 sc->s_mled.cdev =
782 led_create((led_t *)acpi_asus_led, &sc->s_mled, "mled");
783 }
784
785 if (sc->model->tled_set) {
786 sc->s_tled.busy = 0;
787 sc->s_tled.sc = sc;
788 sc->s_tled.type = ACPI_ASUS_LED_TLED;
789 sc->s_tled.cdev =
10f97674
AP
790 led_create_state((led_t *)acpi_asus_led, &sc->s_tled,
791 "tled", 1);
bf334cef
SW
792 }
793
794 if (sc->model->wled_set) {
795 sc->s_wled.busy = 0;
796 sc->s_wled.sc = sc;
797 sc->s_wled.type = ACPI_ASUS_LED_WLED;
798 sc->s_wled.cdev =
10f97674
AP
799 led_create_state((led_t *)acpi_asus_led, &sc->s_wled,
800 "wled", 1);
bf334cef 801 }
bf334cef
SW
802
803 /* Activate hotkeys */
804 AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL);
805
806 /* Handle notifies */
10f97674
AP
807 if (sc->model->n_func == NULL)
808 sc->model->n_func = acpi_asus_notify;
809
bf334cef 810 AcpiInstallNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
10f97674
AP
811 sc->model->n_func, dev);
812
813 /* Find and hook the 'LCDD' object */
814 if (sc->model->lcdd != NULL && sc->model->lcdd_n_func != NULL) {
815 ACPI_STATUS res;
816
817 sc->lcdd_handle = NULL;
818 res = AcpiGetHandle((sc->model->lcdd[0] == '\\' ?
819 NULL : sc->handle), sc->model->lcdd, &(sc->lcdd_handle));
820 if (ACPI_SUCCESS(res)) {
821 AcpiInstallNotifyHandler((sc->lcdd_handle),
822 ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func, dev);
823 } else {
824 kprintf("%s: unable to find LCD device '%s'\n",
825 __func__, sc->model->lcdd);
826 }
827 }
828
829 return (0);
830}
831
832static int
833acpi_asus_detach(device_t dev)
834{
835 struct acpi_asus_softc *sc;
bf334cef 836
10f97674
AP
837 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
838
839 sc = device_get_softc(dev);
10f97674
AP
840 /* Turn the lights off */
841 if (sc->model->bled_set)
842 led_destroy(sc->s_bled.cdev);
843
844 if (sc->model->dled_set)
845 led_destroy(sc->s_dled.cdev);
846
847 if (sc->model->gled_set)
848 led_destroy(sc->s_gled.cdev);
849
850 if (sc->model->mled_set)
851 led_destroy(sc->s_mled.cdev);
852
853 if (sc->model->tled_set)
854 led_destroy(sc->s_tled.cdev);
855
856 if (sc->model->wled_set)
857 led_destroy(sc->s_wled.cdev);
10f97674
AP
858
859 /* Remove notify handler */
860 AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY,
861 acpi_asus_notify);
862
863 if (sc->lcdd_handle) {
864 KASSERT(sc->model->lcdd_n_func != NULL,
865 ("model->lcdd_n_func is NULL, but lcdd_handle is non-zero"));
866 AcpiRemoveNotifyHandler((sc->lcdd_handle),
867 ACPI_DEVICE_NOTIFY, sc->model->lcdd_n_func);
868 }
869
870 /* Free sysctl tree */
871 sysctl_ctx_free(&sc->sysctl_ctx);
bf334cef
SW
872
873 return (0);
874}
875
10f97674
AP
876static void
877acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused)
878{
879 struct acpi_asus_softc *sc;
880 char *method;
881 int state;
882
883 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
884
885 sc = led->sc;
886
887 switch (led->type) {
888 case ACPI_ASUS_LED_BLED:
889 method = sc->model->bled_set;
890 state = led->state;
891 break;
892 case ACPI_ASUS_LED_DLED:
893 method = sc->model->dled_set;
894 state = led->state;
895 break;
896 case ACPI_ASUS_LED_GLED:
897 method = sc->model->gled_set;
898 state = led->state + 1; /* 1: off, 2: on */
899 break;
900 case ACPI_ASUS_LED_MLED:
901 method = sc->model->mled_set;
902 state = !led->state; /* inverted */
903 break;
904 case ACPI_ASUS_LED_TLED:
905 method = sc->model->tled_set;
906 state = led->state;
907 break;
908 case ACPI_ASUS_LED_WLED:
909 method = sc->model->wled_set;
910 state = led->state;
911 break;
912 default:
913 kprintf("acpi_asus_led: invalid LED type %d\n",
914 (int)led->type);
915 return;
916 }
917
918 acpi_SetInteger(sc->handle, method, state);
919 led->busy = 0;
920}
921
922static void
923acpi_asus_led(struct acpi_asus_led *led, int state)
924{
925
926 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
927
928 if (led->busy)
929 return;
930
931 led->busy = 1;
932 led->state = state;
933
934 AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)acpi_asus_led_task, led);
935}
10f97674 936
bf334cef
SW
937static int
938acpi_asus_sysctl(SYSCTL_HANDLER_ARGS)
939{
940 struct acpi_asus_softc *sc;
941 int arg;
942 int error = 0;
943 int function;
944 int method;
10f97674 945
bf334cef
SW
946 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
947
948 sc = (struct acpi_asus_softc *)oidp->oid_arg1;
949 function = oidp->oid_arg2;
950 method = acpi_asus_sysctls[function].method;
951
10f97674 952 ACPI_SERIAL_BEGIN(asus);
bf334cef
SW
953 arg = acpi_asus_sysctl_get(sc, method);
954 error = sysctl_handle_int(oidp, &arg, 0, req);
955
956 /* Sanity check */
957 if (error != 0 || req->newptr == NULL)
958 goto out;
959
960 /* Update */
961 error = acpi_asus_sysctl_set(sc, method, arg);
962
963out:
10f97674 964 ACPI_SERIAL_END(asus);
bf334cef
SW
965 return (error);
966}
967
968static int
969acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method)
970{
971 int val = 0;
972
973 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
10f97674 974 ACPI_SERIAL_ASSERT(asus);
bf334cef
SW
975
976 switch (method) {
977 case ACPI_ASUS_METHOD_BRN:
978 val = sc->s_brn;
979 break;
980 case ACPI_ASUS_METHOD_DISP:
981 val = sc->s_disp;
982 break;
983 case ACPI_ASUS_METHOD_LCD:
984 val = sc->s_lcd;
985 break;
10f97674
AP
986 case ACPI_ASUS_METHOD_CAMERA:
987 val = sc->s_cam;
988 break;
989 case ACPI_ASUS_METHOD_CARDRD:
990 val = sc->s_crd;
991 break;
992 case ACPI_ASUS_METHOD_WLAN:
993 val = sc->s_wlan;
994 break;
bf334cef
SW
995 }
996
997 return (val);
998}
999
1000static int
1001acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int arg)
1002{
10f97674
AP
1003 ACPI_STATUS status = AE_OK;
1004 ACPI_OBJECT_LIST acpiargs;
1005 ACPI_OBJECT acpiarg[1];
bf334cef
SW
1006
1007 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
10f97674 1008 ACPI_SERIAL_ASSERT(asus);
bf334cef 1009
10f97674
AP
1010 acpiargs.Count = 1;
1011 acpiargs.Pointer = acpiarg;
1012 acpiarg[0].Type = ACPI_TYPE_INTEGER;
1013 acpiarg[0].Integer.Value = arg;
bf334cef
SW
1014
1015 switch (method) {
1016 case ACPI_ASUS_METHOD_BRN:
1017 if (arg < 0 || arg > 15)
1018 return (EINVAL);
1019
1020 if (sc->model->brn_set)
1021 status = acpi_SetInteger(sc->handle,
1022 sc->model->brn_set, arg);
1023 else {
1024 while (arg != 0) {
1025 status = AcpiEvaluateObject(sc->handle,
1026 (arg > 0) ? sc->model->brn_up :
1027 sc->model->brn_dn, NULL, NULL);
1028 (arg > 0) ? arg-- : arg++;
1029 }
1030 }
1031
1032 if (ACPI_SUCCESS(status))
1033 sc->s_brn = arg;
1034
1035 break;
1036 case ACPI_ASUS_METHOD_DISP:
1037 if (arg < 0 || arg > 7)
1038 return (EINVAL);
1039
1040 status = acpi_SetInteger(sc->handle,
1041 sc->model->disp_set, arg);
1042
1043 if (ACPI_SUCCESS(status))
1044 sc->s_disp = arg;
1045
1046 break;
1047 case ACPI_ASUS_METHOD_LCD:
1048 if (arg < 0 || arg > 1)
1049 return (EINVAL);
1050
1051 if (strncmp(sc->model->name, "L3H", 3) != 0)
1052 status = AcpiEvaluateObject(sc->handle,
1053 sc->model->lcd_set, NULL, NULL);
1054 else
1055 status = acpi_SetInteger(sc->handle,
1056 sc->model->lcd_set, 0x7);
1057
1058 if (ACPI_SUCCESS(status))
1059 sc->s_lcd = arg;
1060
1061 break;
10f97674
AP
1062 case ACPI_ASUS_METHOD_CAMERA:
1063 if (arg < 0 || arg > 1)
1064 return (EINVAL);
1065
1066 status = AcpiEvaluateObject(sc->handle,
1067 sc->model->cam_set, &acpiargs, NULL);
1068
1069 if (ACPI_SUCCESS(status))
1070 sc->s_cam = arg;
1071 break;
1072 case ACPI_ASUS_METHOD_CARDRD:
1073 if (arg < 0 || arg > 1)
1074 return (EINVAL);
1075
1076 status = AcpiEvaluateObject(sc->handle,
1077 sc->model->crd_set, &acpiargs, NULL);
1078
1079 if (ACPI_SUCCESS(status))
1080 sc->s_crd = arg;
1081 break;
1082 case ACPI_ASUS_METHOD_WLAN:
1083 if (arg < 0 || arg > 1)
1084 return (EINVAL);
1085
1086 status = AcpiEvaluateObject(sc->handle,
1087 sc->model->wlan_set, &acpiargs, NULL);
1088
1089 if (ACPI_SUCCESS(status))
1090 sc->s_wlan = arg;
1091 break;
bf334cef
SW
1092 }
1093
1094 return (0);
1095}
1096
1097static int
1098acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method)
1099{
1100 ACPI_STATUS status;
1101
1102 switch (method) {
1103 case ACPI_ASUS_METHOD_BRN:
1104 if (sc->model->brn_get) {
1105 /* GPLV/SPLV models */
1106 status = acpi_GetInteger(sc->handle,
1107 sc->model->brn_get, &sc->s_brn);
1108 if (ACPI_SUCCESS(status))
1109 return (TRUE);
1110 } else if (sc->model->brn_up) {
1111 /* Relative models */
1112 status = AcpiEvaluateObject(sc->handle,
1113 sc->model->brn_up, NULL, NULL);
1114 if (ACPI_FAILURE(status))
1115 return (FALSE);
1116
1117 status = AcpiEvaluateObject(sc->handle,
1118 sc->model->brn_dn, NULL, NULL);
1119 if (ACPI_FAILURE(status))
1120 return (FALSE);
1121
1122 return (TRUE);
1123 }
1124 return (FALSE);
1125 case ACPI_ASUS_METHOD_DISP:
1126 if (sc->model->disp_get) {
1127 status = acpi_GetInteger(sc->handle,
1128 sc->model->disp_get, &sc->s_disp);
1129 if (ACPI_SUCCESS(status))
1130 return (TRUE);
1131 }
1132 return (FALSE);
1133 case ACPI_ASUS_METHOD_LCD:
10f97674 1134 if (sc->model->lcd_get) {
3fe31f5d 1135 if (strncmp(sc->model->name, "L3H", 3) == 0) {
10f97674
AP
1136 ACPI_BUFFER Buf;
1137 ACPI_OBJECT Arg[2], Obj;
1138 ACPI_OBJECT_LIST Args;
1139
1140 /* L3H is a bit special */
1141 Arg[0].Type = ACPI_TYPE_INTEGER;
1142 Arg[0].Integer.Value = 0x02;
1143 Arg[1].Type = ACPI_TYPE_INTEGER;
1144 Arg[1].Integer.Value = 0x03;
1145
1146 Args.Count = 2;
1147 Args.Pointer = Arg;
1148
1149 Buf.Length = sizeof(Obj);
1150 Buf.Pointer = &Obj;
1151
1152 status = AcpiEvaluateObject(sc->handle,
1153 sc->model->lcd_get, &Args, &Buf);
1154 if (ACPI_SUCCESS(status) &&
1155 Obj.Type == ACPI_TYPE_INTEGER) {
1156 sc->s_lcd = Obj.Integer.Value >> 8;
1157 return (TRUE);
1158 }
1159 } else {
1160 status = acpi_GetInteger(sc->handle,
1161 sc->model->lcd_get, &sc->s_lcd);
1162 if (ACPI_SUCCESS(status))
1163 return (TRUE);
1164 }
1165 }
1166 return (FALSE);
1167 case ACPI_ASUS_METHOD_CAMERA:
1168 if (sc->model->cam_get) {
bf334cef 1169 status = acpi_GetInteger(sc->handle,
10f97674 1170 sc->model->cam_get, &sc->s_cam);
bf334cef
SW
1171 if (ACPI_SUCCESS(status))
1172 return (TRUE);
1173 }
10f97674
AP
1174 return (FALSE);
1175 case ACPI_ASUS_METHOD_CARDRD:
1176 if (sc->model->crd_get) {
1177 status = acpi_GetInteger(sc->handle,
1178 sc->model->crd_get, &sc->s_crd);
1179 if (ACPI_SUCCESS(status))
1180 return (TRUE);
1181 }
1182 return (FALSE);
1183 case ACPI_ASUS_METHOD_WLAN:
1184 if (sc->model->wlan_get) {
1185 status = acpi_GetInteger(sc->handle,
1186 sc->model->wlan_get, &sc->s_wlan);
1187 if (ACPI_SUCCESS(status))
bf334cef 1188 return (TRUE);
bf334cef
SW
1189 }
1190 return (FALSE);
1191 }
1192 return (FALSE);
1193}
1194
1195static void
1196acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1197{
1198 struct acpi_asus_softc *sc;
1199 struct acpi_softc *acpi_sc;
1200
1201 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1202
1203 sc = device_get_softc((device_t)context);
1204 acpi_sc = acpi_device_get_parent_softc(sc->dev);
1205
10f97674 1206 ACPI_SERIAL_BEGIN(asus);
bf334cef
SW
1207 if ((notify & ~0x10) <= 15) {
1208 sc->s_brn = notify & ~0x10;
1209 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
1210 } else if ((notify & ~0x20) <= 15) {
1211 sc->s_brn = notify & ~0x20;
1212 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
1213 } else if (notify == 0x33) {
1214 sc->s_lcd = 1;
1215 ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n");
1216 } else if (notify == 0x34) {
1217 sc->s_lcd = 0;
1218 ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n");
10f97674
AP
1219 } else if (notify == 0x86) {
1220 acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1);
1221 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
1222 } else if (notify == 0x87) {
1223 acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1);
1224 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
bf334cef
SW
1225 } else {
1226 /* Notify devd(8) */
1227 acpi_UserNotify("ASUS", h, notify);
1228 }
10f97674
AP
1229 ACPI_SERIAL_END(asus);
1230}
1231
1232static void
1233acpi_asus_lcdd_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1234{
1235 struct acpi_asus_softc *sc;
1236 struct acpi_softc *acpi_sc;
1237
1238 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1239
1240 sc = device_get_softc((device_t)context);
1241 acpi_sc = acpi_device_get_parent_softc(sc->dev);
1242
1243 ACPI_SERIAL_BEGIN(asus);
1244 switch (notify) {
1245 case 0x87:
1246 acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn-1);
1247 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
1248 break;
1249 case 0x86:
1250 acpi_asus_sysctl_set(sc, ACPI_ASUS_METHOD_BRN, sc->s_brn+1);
1251 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
1252 break;
d1fb95bd
SW
1253 default:
1254 device_printf(sc->dev, "unknown notify: %#x\n", notify);
1255 break;
10f97674
AP
1256 }
1257 ACPI_SERIAL_END(asus);
1258}
1259
1260static void
1261acpi_asus_eeepc_notify(ACPI_HANDLE h, UINT32 notify, void *context)
1262{
1263 struct acpi_asus_softc *sc;
1264 struct acpi_softc *acpi_sc;
1265
1266 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
1267
1268 sc = device_get_softc((device_t)context);
1269 acpi_sc = acpi_device_get_parent_softc(sc->dev);
1270
1271 ACPI_SERIAL_BEGIN(asus);
1272 if ((notify & ~0x20) <= 15) {
1273 sc->s_brn = notify & ~0x20;
1274 ACPI_VPRINT(sc->dev, acpi_sc,
1275 "Brightness increased/decreased\n");
1276 } else {
1277 /* Notify devd(8) */
1278 acpi_UserNotify("ASUS-Eee", h, notify);
1279 }
1280 ACPI_SERIAL_END(asus);
bf334cef 1281}