xref: /freebsd/sys/dev/acpi_support/acpi_asus.c (revision 2357939bc239bd5334a169b62313806178dd8f30)
1 /*-
2  * Copyright (c) 2004 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 
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30 
31 /*
32  * Driver for extra ACPI-controlled gadgets (hotkeys, leds, etc) found on
33  * recent Asus (and Medion) laptops.  Inspired by the Acpi4Asus project which
34  * implements these features in the Linux kernel.
35  *
36  *   <http://sourceforge.net/projects/acpi4asus/>
37  *
38  * Currently should support most features, but could use some more testing.
39  * Particularly the display-switching stuff is a bit hairy.  If you have an
40  * Asus laptop which doesn't appear to be supported, or strange things happen
41  * when using this driver, please report to <acpi@FreeBSD.org>.
42  *
43  */
44 
45 #include "opt_acpi.h"
46 #include <sys/param.h>
47 #include <sys/kernel.h>
48 #include <sys/bus.h>
49 #include <sys/sbuf.h>
50 
51 #include "acpi.h"
52 #include <dev/acpica/acpivar.h>
53 #include <dev/led/led.h>
54 
55 #define _COMPONENT	ACPI_ASUS
56 ACPI_MODULE_NAME("ASUS")
57 
58 struct acpi_asus_model {
59 	char	*name;
60 
61 	char	*mled_set;
62 	char	*tled_set;
63 	char	*wled_set;
64 
65 	char	*brn_get;
66 	char	*brn_set;
67 	char	*brn_up;
68 	char	*brn_dn;
69 
70 	char	*lcd_get;
71 	char	*lcd_set;
72 
73 	char	*disp_get;
74 	char	*disp_set;
75 };
76 
77 struct acpi_asus_softc {
78 	device_t		dev;
79 	ACPI_HANDLE		handle;
80 
81 	struct acpi_asus_model	*model;
82 	struct sysctl_ctx_list	sysctl_ctx;
83 	struct sysctl_oid	*sysctl_tree;
84 
85 	dev_t			s_mled;
86 	dev_t			s_tled;
87 	dev_t			s_wled;
88 
89 	int			s_brn;
90 	int			s_disp;
91 	int			s_lcd;
92 };
93 
94 /* Models we know about */
95 static struct acpi_asus_model acpi_asus_models[] = {
96 	{
97 		.name		= "L2D",
98 		.mled_set	= "MLED",
99 		.wled_set	= "WLED",
100 		.brn_up		= "\\Q0E",
101 		.brn_dn		= "\\Q0F",
102 		.lcd_get	= "\\SGP0",
103 		.lcd_set	= "\\Q10"
104 	},
105 	{
106 		.name		= "L3C",
107 		.mled_set	= "MLED",
108 		.wled_set	= "WLED",
109 		.brn_get	= "GPLV",
110 		.brn_set	= "SPLV",
111 		.lcd_get	= "\\GL32",
112 		.lcd_set	= "\\_SB.PCI0.PX40.ECD0._Q10"
113 	},
114 	{
115 		.name		= "L3D",
116 		.mled_set	= "MLED",
117 		.wled_set	= "WLED",
118 		.brn_get	= "GPLV",
119 		.brn_set	= "SPLV",
120 		.lcd_get	= "\\BKLG",
121 		.lcd_set	= "\\Q10"
122 	},
123 	{
124 		.name		= "L3H",
125 		.mled_set	= "MLED",
126 		.wled_set	= "WLED",
127 		.brn_get	= "GPLV",
128 		.brn_set	= "SPLV",
129 		.lcd_get	= "\\_SB.PCI0.PM.PBC",
130 		.lcd_set	= "EHK",
131 		.disp_get	= "\\_SB.INFB",
132 		.disp_set	= "SDSP"
133 	},
134 	{
135 		.name		= "L8L"
136 		/* Only has hotkeys, apparantly */
137 	},
138 	{
139 		.name		= "M1A",
140 		.mled_set	= "MLED",
141 		.brn_up		= "\\_SB.PCI0.PX40.EC0.Q0E",
142 		.brn_dn		= "\\_SB.PCI0.PX40.EC0.Q0F",
143 		.lcd_get	= "\\PNOF",
144 		.lcd_set	= "\\_SB.PCI0.PX40.EC0.Q10"
145 	},
146 	{
147 		.name		= "M2E",
148 		.mled_set	= "MLED",
149 		.wled_set	= "WLED",
150 		.brn_get	= "GPLV",
151 		.brn_set	= "SPLV",
152 		.lcd_get	= "\\GP06",
153 		.lcd_set	= "\\Q10"
154 	},
155 	{
156 		.name		= "P30",
157 		.wled_set	= "WLED",
158 		.brn_up		= "\\_SB.PCI0.LPCB.EC0._Q68",
159 		.brn_dn		= "\\_SB.PCI0.LPCB.EC0._Q69",
160 		.lcd_get	= "\\BKLT",
161 		.lcd_set	= "\\_SB.PCI0.LPCB.EC0._Q0E"
162 	},
163 
164 	{ .name = NULL }
165 };
166 
167 /* Function prototypes */
168 static int	acpi_asus_probe(device_t dev);
169 static int	acpi_asus_attach(device_t dev);
170 static int	acpi_asus_detach(device_t dev);
171 
172 static void	acpi_asus_mled(device_t dev, int state);
173 static void	acpi_asus_tled(device_t dev, int state);
174 static void	acpi_asus_wled(device_t dev, int state);
175 
176 static int	acpi_asus_sysctl_brn(SYSCTL_HANDLER_ARGS);
177 static int	acpi_asus_sysctl_lcd(SYSCTL_HANDLER_ARGS);
178 static int	acpi_asus_sysctl_disp(SYSCTL_HANDLER_ARGS);
179 
180 static void	acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context);
181 
182 static device_method_t acpi_asus_methods[] = {
183 	DEVMETHOD(device_probe,	 acpi_asus_probe),
184 	DEVMETHOD(device_attach, acpi_asus_attach),
185 	DEVMETHOD(device_detach, acpi_asus_detach),
186 
187 	{ 0, 0 }
188 };
189 
190 static driver_t acpi_asus_driver = {
191 	"acpi_asus",
192 	acpi_asus_methods,
193 	sizeof(struct acpi_asus_softc)
194 };
195 
196 static devclass_t acpi_asus_devclass;
197 
198 DRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, 0, 0);
199 MODULE_DEPEND(acpi_asus, acpi, 1, 1, 1);
200 
201 static int
202 acpi_asus_probe(device_t dev)
203 {
204 	struct acpi_asus_model	*model;
205 	struct acpi_asus_softc	*sc;
206 	struct sbuf		*sb;
207 	ACPI_BUFFER		Buf;
208 	ACPI_OBJECT		Arg, *Obj;
209 	ACPI_OBJECT_LIST	Args;
210 
211 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
212 
213 	if (!acpi_disabled("asus") &&
214 	    acpi_get_type(dev) == ACPI_TYPE_DEVICE &&
215 	    acpi_MatchHid(dev, "ATK0100")) {
216 		sc = device_get_softc(dev);
217 		sc->dev = dev;
218 		sc->handle = acpi_get_handle(dev);
219 
220 		sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
221 
222 		if (sb == NULL)
223 			return (ENOMEM);
224 
225 		Arg.Type = ACPI_TYPE_INTEGER;
226 		Arg.Integer.Value = 0;
227 
228 		Args.Count = 1;
229 		Args.Pointer = &Arg;
230 
231 		Buf.Pointer = NULL;
232 		Buf.Length = ACPI_ALLOCATE_BUFFER;
233 
234 		AcpiEvaluateObject(sc->handle, "INIT", &Args, &Buf);
235 
236 		Obj = Buf.Pointer;
237 
238 		for (model = acpi_asus_models; model->name != NULL; model++)
239 			if (strcmp(Obj->String.Pointer, model->name) == 0) {
240 				sbuf_printf(sb, "Asus %s Laptop Extras",
241 						Obj->String.Pointer);
242 				sbuf_finish(sb);
243 
244 				sc->model = model;
245 				device_set_desc(dev, sbuf_data(sb));
246 
247 				sbuf_delete(sb);
248 				AcpiOsFree(Buf.Pointer);
249 				return (0);
250 			}
251 
252 		sbuf_printf(sb, "Unsupported Asus laptop detected: %s\n",
253 				Obj->String.Pointer);
254 		sbuf_finish(sb);
255 
256 		device_printf(dev, sbuf_data(sb));
257 
258 		sbuf_delete(sb);
259 		AcpiOsFree(Buf.Pointer);
260 	}
261 
262 	return (ENXIO);
263 }
264 
265 static int
266 acpi_asus_attach(device_t dev)
267 {
268 	struct acpi_asus_softc	*sc;
269 	struct acpi_softc	*acpi_sc;
270 
271 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
272 
273 	sc = device_get_softc(dev);
274 	acpi_sc = acpi_device_get_parent_softc(dev);
275 
276 	/* Build sysctl tree */
277 	sysctl_ctx_init(&sc->sysctl_ctx);
278 	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
279 	    SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree),
280 	    OID_AUTO, "asus", CTLFLAG_RD, 0, "");
281 
282 	/* Attach leds */
283 	if (sc->model->mled_set)
284 		sc->s_mled = led_create((led_t *)acpi_asus_mled, dev, "mled");
285 
286 	if (sc->model->tled_set)
287 		sc->s_tled = led_create((led_t *)acpi_asus_tled, dev, "tled");
288 
289 	if (sc->model->wled_set)
290 		sc->s_wled = led_create((led_t *)acpi_asus_wled, dev, "wled");
291 
292 	/* Attach brightness for GPLV/SPLV models */
293 	if (sc->model->brn_get &&
294 	    ACPI_SUCCESS(acpi_GetInteger(sc->handle,
295 		sc->model->brn_get, &sc->s_brn)))
296 		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
297 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
298 		    "lcd_brightness", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
299 		    acpi_asus_sysctl_brn, "I", "brightness of the lcd panel");
300 
301 	/* Attach brightness for other models */
302 	if (sc->model->brn_up &&
303 	    ACPI_SUCCESS(AcpiEvaluateObject(sc->handle,
304 		sc->model->brn_up, NULL, NULL)) &&
305 	    ACPI_SUCCESS(AcpiEvaluateObject(sc->handle,
306 		sc->model->brn_dn, NULL, NULL)))
307 		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
308 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
309 		    "lcd_brightness", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
310 		    acpi_asus_sysctl_brn, "I", "brightness of the lcd panel");
311 
312 	/* Attach display switching */
313 	if (sc->model->disp_get &&
314 	    ACPI_SUCCESS(acpi_GetInteger(sc->handle,
315 		sc->model->disp_get, &sc->s_disp)))
316 		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
317 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
318 		    "video_output", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
319 		    acpi_asus_sysctl_disp, "I", "display output state");
320 
321 	/* Attach LCD state, easy for most models... */
322 	if (sc->model->lcd_get &&
323 	    strncmp(sc->model->name, "L3H", 3) != 0 &&
324 	    ACPI_SUCCESS(acpi_GetInteger(sc->handle,
325 		sc->model->lcd_get, &sc->s_lcd)))
326 		SYSCTL_ADD_PROC(&sc->sysctl_ctx,
327 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
328 		    "lcd_backlight", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
329 		    acpi_asus_sysctl_lcd, "I", "state of the lcd backlight");
330 
331 	/* ...a nightmare for the L3H */
332 	else if (sc->model->lcd_get) {
333 		ACPI_BUFFER		Buf;
334 		ACPI_OBJECT		Arg[2], Obj;
335 		ACPI_OBJECT_LIST	Args;
336 
337 		Arg[0].Type = ACPI_TYPE_INTEGER;
338 		Arg[0].Integer.Value = 0x02;
339 		Arg[1].Type = ACPI_TYPE_INTEGER;
340 		Arg[1].Integer.Value = 0x03;
341 
342 		Args.Count = 2;
343 		Args.Pointer = Arg;
344 
345 		Buf.Length = sizeof(Obj);
346 		Buf.Pointer = &Obj;
347 
348 		if (ACPI_SUCCESS(AcpiEvaluateObject(sc->handle,
349 			sc->model->lcd_get, &Args, &Buf)) &&
350 		    Obj.Type == ACPI_TYPE_INTEGER) {
351 			sc->s_lcd = Obj.Integer.Value >> 8;
352 
353 			SYSCTL_ADD_PROC(&sc->sysctl_ctx,
354 			    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
355 			    "lcd_backlight", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
356 			    acpi_asus_sysctl_lcd, "I",
357 			    "state of the lcd backlight");
358 		}
359 	}
360 
361 	/* Activate hotkeys */
362 	AcpiEvaluateObject(sc->handle, "BSTS", NULL, NULL);
363 
364 	/* Handle notifies */
365 	AcpiInstallNotifyHandler(sc->handle,
366 	    ACPI_SYSTEM_NOTIFY, acpi_asus_notify, dev);
367 
368 	return (0);
369 }
370 
371 static int
372 acpi_asus_detach(device_t dev)
373 {
374 	struct acpi_asus_softc	*sc;
375 
376 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
377 
378 	sc = device_get_softc(dev);
379 
380 	/* Turn the lights off */
381 	if (sc->model->mled_set)
382 		led_destroy(sc->s_mled);
383 
384 	if (sc->model->tled_set)
385 		led_destroy(sc->s_tled);
386 
387 	if (sc->model->wled_set)
388 		led_destroy(sc->s_wled);
389 
390 	/* Remove notify handler */
391 	AcpiRemoveNotifyHandler(sc->handle,
392 	    ACPI_SYSTEM_NOTIFY, acpi_asus_notify);
393 
394 	/* Free sysctl tree */
395 	sysctl_ctx_free(&sc->sysctl_ctx);
396 
397 	return (0);
398 }
399 
400 static void
401 acpi_asus_mled(device_t dev, int state)
402 {
403 	struct acpi_asus_softc	*sc;
404 	ACPI_OBJECT		Arg;
405 	ACPI_OBJECT_LIST	Args;
406 
407 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
408 
409 	sc = device_get_softc(dev);
410 
411 	Arg.Type = ACPI_TYPE_INTEGER;
412 	Arg.Integer.Value = !state;	/* Inverted, yes! */
413 
414 	Args.Count = 1;
415 	Args.Pointer = &Arg;
416 
417 	AcpiEvaluateObject(sc->handle, sc->model->mled_set, &Args, NULL);
418 }
419 
420 static void
421 acpi_asus_tled(device_t dev, int state)
422 {
423 	struct acpi_asus_softc	*sc;
424 	ACPI_OBJECT		Arg;
425 	ACPI_OBJECT_LIST	Args;
426 
427 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
428 
429 	sc = device_get_softc(dev);
430 
431 	Arg.Type = ACPI_TYPE_INTEGER;
432 	Arg.Integer.Value = state;
433 
434 	Args.Count = 1;
435 	Args.Pointer = &Arg;
436 
437 	AcpiEvaluateObject(sc->handle, sc->model->tled_set, &Args, NULL);
438 }
439 
440 static void
441 acpi_asus_wled(device_t dev, int state)
442 {
443 	struct acpi_asus_softc	*sc;
444 	ACPI_OBJECT		Arg;
445 	ACPI_OBJECT_LIST	Args;
446 
447 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
448 
449 	sc = device_get_softc(dev);
450 
451 	Arg.Type = ACPI_TYPE_INTEGER;
452 	Arg.Integer.Value = state;
453 
454 	Args.Count = 1;
455 	Args.Pointer = &Arg;
456 
457 	AcpiEvaluateObject(sc->handle, sc->model->wled_set, &Args, NULL);
458 }
459 
460 static int
461 acpi_asus_sysctl_brn(SYSCTL_HANDLER_ARGS)
462 {
463 	struct acpi_asus_softc	*sc;
464 	ACPI_OBJECT		Arg;
465 	ACPI_OBJECT_LIST	Args;
466 	int			brn, err;
467 
468 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
469 
470 	sc = (struct acpi_asus_softc *)oidp->oid_arg1;
471 
472 	/* Sanity check */
473 	brn = sc->s_brn;
474 	err = sysctl_handle_int(oidp, &brn, 0, req);
475 
476 	if ((err != 0) || (req->newptr == NULL))
477 		return (err);
478 
479 	if ((brn < 0) || (brn > 15))
480 		return (EINVAL);
481 
482 	/* Keep track and update */
483 	sc->s_brn = brn;
484 
485 	Arg.Type = ACPI_TYPE_INTEGER;
486 	Arg.Integer.Value = brn;
487 
488 	Args.Count = 1;
489 	Args.Pointer = &Arg;
490 
491 	if (sc->model->brn_set)
492 		AcpiEvaluateObject(sc->handle,
493 		    sc->model->brn_set, &Args, NULL);
494 	else {
495 		brn -= sc->s_brn;
496 
497 		while (brn != 0) {
498 			AcpiEvaluateObject(sc->handle,(brn > 0) ?
499 			    sc->model->brn_up : sc->model->brn_dn,
500 			    NULL, NULL);
501 
502 			(brn > 0) ? brn-- : brn++;
503 		}
504 	}
505 
506 	return (0);
507 }
508 
509 static int
510 acpi_asus_sysctl_lcd(SYSCTL_HANDLER_ARGS)
511 {
512 	struct acpi_asus_softc	*sc;
513 	int			lcd, err;
514 
515 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
516 
517 	sc = (struct acpi_asus_softc *)oidp->oid_arg1;
518 
519 	/* Sanity check */
520 	lcd = sc->s_lcd;
521 	err = sysctl_handle_int(oidp, &lcd, 0, req);
522 
523 	if ((err != 0) || (req->newptr == NULL))
524 		return (err);
525 
526 	if ((lcd < 0) || (lcd > 1))
527 		return (EINVAL);
528 
529 	/* Keep track and update */
530 	sc->s_lcd = lcd;
531 
532 	/* Most models just need a lcd_set evaluated, the L3H is trickier */
533 	if (strncmp(sc->model->name, "L3H", 3) != 0)
534 		AcpiEvaluateObject(sc->handle,
535 		    sc->model->lcd_set, NULL, NULL);
536 	else {
537 		ACPI_OBJECT		Arg;
538 		ACPI_OBJECT_LIST	Args;
539 
540 		Arg.Type = ACPI_TYPE_INTEGER;
541 		Arg.Integer.Value = 0x07;
542 
543 		Args.Count = 1;
544 		Args.Pointer = &Arg;
545 
546 		AcpiEvaluateObject(sc->handle,
547 		    sc->model->lcd_set, &Args, NULL);
548 	}
549 
550 	return (0);
551 }
552 
553 static int
554 acpi_asus_sysctl_disp(SYSCTL_HANDLER_ARGS)
555 {
556 	struct acpi_asus_softc	*sc;
557 	ACPI_OBJECT		Arg;
558 	ACPI_OBJECT_LIST	Args;
559 	int			disp, err;
560 
561 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
562 
563 	sc = (struct acpi_asus_softc *)oidp->oid_arg1;
564 
565 	/* Sanity check */
566 	disp = sc->s_disp;
567 	err = sysctl_handle_int(oidp, &disp, 0, req);
568 
569 	if ((err != 0) || (req->newptr == NULL))
570 		return (err);
571 
572 	if ((disp < 0) || (disp > 7))
573 		return (EINVAL);
574 
575 	/* Keep track and update */
576 	sc->s_disp = disp;
577 
578 	Arg.Type = ACPI_TYPE_INTEGER;
579 	Arg.Integer.Value = disp;
580 
581 	Args.Count = 1;
582 	Args.Pointer = &Arg;
583 
584 	AcpiEvaluateObject(sc->handle,
585 	    sc->model->disp_set, &Args, NULL);
586 
587 	return (0);
588 }
589 
590 static void
591 acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context)
592 {
593 	struct acpi_asus_softc	*sc;
594 	struct acpi_softc	*acpi_sc;
595 
596 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
597 
598 	sc = device_get_softc((device_t)context);
599 	acpi_sc = acpi_device_get_parent_softc(sc->dev);
600 
601 	if ((notify & ~0x10) <= 15) {
602 		sc->s_brn = (notify & ~0x10);
603 		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness increased\n");
604 	} else if ((notify & ~0x20) <= 15) {
605 		sc->s_brn = (notify & ~0x20);
606 		ACPI_VPRINT(sc->dev, acpi_sc, "Brightness decreased\n");
607 	} else if (notify == 0x33) {
608 		sc->s_lcd = 1;
609 		ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned on\n");
610 	} else if (notify == 0x34) {
611 		sc->s_lcd = 0;
612 		ACPI_VPRINT(sc->dev, acpi_sc, "LCD turned off\n");
613 	} else {
614 		/* Notify devd(8) */
615 		acpi_UserNotify("ASUS", h, notify);
616 	}
617 }
618