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