Commit | Line | Data |
---|---|---|
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 | |
64 | ACPI_MODULE_NAME("ASUS") | |
65 | ||
66 | struct 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 | ||
101 | struct 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 | ||
116 | struct 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 |
139 | static 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 | */ | |
146 | static 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 | */ | |
425 | static 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 |
438 | static 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 | */ | |
444 | static 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 | ||
461 | static 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 |
507 | ACPI_SERIAL_DECL(asus, "ACPI ASUS extras"); |
508 | ||
509 | /* Function prototypes */ | |
bf334cef SW |
510 | static int acpi_asus_probe(device_t dev); |
511 | static int acpi_asus_attach(device_t dev); | |
512 | static int acpi_asus_detach(device_t dev); | |
513 | ||
10f97674 AP |
514 | static void acpi_asus_led(struct acpi_asus_led *led, int state); |
515 | static void acpi_asus_led_task(struct acpi_asus_led *led, int pending __unused); | |
bf334cef SW |
516 | |
517 | static int acpi_asus_sysctl(SYSCTL_HANDLER_ARGS); | |
518 | static int acpi_asus_sysctl_init(struct acpi_asus_softc *sc, int method); | |
519 | static int acpi_asus_sysctl_get(struct acpi_asus_softc *sc, int method); | |
520 | static int acpi_asus_sysctl_set(struct acpi_asus_softc *sc, int method, int val); | |
521 | ||
522 | static void acpi_asus_notify(ACPI_HANDLE h, UINT32 notify, void *context); | |
523 | ||
524 | static 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 | 532 | static 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 | ||
538 | static devclass_t acpi_asus_devclass; | |
539 | ||
aa2b9d05 | 540 | DRIVER_MODULE(acpi_asus, acpi, acpi_asus_driver, acpi_asus_devclass, NULL, NULL); |
bf334cef | 541 | MODULE_DEPEND(acpi_asus, acpi, 1, 1, 1); |
f2c400e8 | 542 | MODULE_DEPEND(acpi_asus, led, 1, 1, 1); |
bf334cef | 543 | |
bf334cef SW |
544 | static int |
545 | acpi_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 | ||
623 | good: | |
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 |
721 | static int |
722 | acpi_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 | ||
832 | static int | |
833 | acpi_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 |
876 | static void |
877 | acpi_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 | ||
922 | static void | |
923 | acpi_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 |
937 | static int |
938 | acpi_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 | ||
963 | out: | |
10f97674 | 964 | ACPI_SERIAL_END(asus); |
bf334cef SW |
965 | return (error); |
966 | } | |
967 | ||
968 | static int | |
969 | acpi_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 | ||
1000 | static int | |
1001 | acpi_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 | ||
1097 | static int | |
1098 | acpi_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 | ||
1195 | static void | |
1196 | acpi_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 | ||
1232 | static void | |
1233 | acpi_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 | ||
1260 | static void | |
1261 | acpi_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 | } |