1 | /* $NetBSD: fujbp_acpi.c,v 1.4 2014/02/25 18:30:09 pooka Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2010, 2011 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Gregoire Sutre. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /* |
33 | * ACPI Fujitsu Driver. |
34 | * |
35 | * Together with fujhk(4), this driver provides support for the ACPI devices |
36 | * FUJ02B1 and FUJ02E3 that are commonly found in Fujitsu LifeBooks. The |
37 | * driver does not support all features of these devices, in particular |
38 | * volume control is not implemented. |
39 | * |
40 | * Information regarding the behavior of these devices was obtained from the |
41 | * source code of the Linux and FreeBSD drivers, as well as from experiments on |
42 | * a Fujitsu LifeBook P7120. |
43 | * |
44 | * The FUJ02B1 device is used to control the brightness level of the internal |
45 | * display, the state (on/off) of the internal pointer, and the volume level of |
46 | * the internal speakers or headphones. |
47 | * |
48 | * The FUJ02B1 device provides the following methods (or only a subset): |
49 | * |
50 | * GSIF supported hotkey status bits (bitmask for GHKS) |
51 | * GHKS active hotkeys (bit field) |
52 | * {G,S}BLL get/set the brightness level of the internal display |
53 | * {G,S}VOL get/set the volume level of the internal speakers |
54 | * {G,S}MOU get/set the switch state of the internal pointer |
55 | * RBLL brightness radix (number of brightness levels) |
56 | * RVOL volume radix (number of volume levels) |
57 | * |
58 | * Notifications are delivered to the FUJ02B1 device when functions hotkeys |
59 | * (brightness, pointer) are released. However, these notifications seem to be |
60 | * purely informative: the BIOS already made the hardware changes corresponding |
61 | * to the hotkey. |
62 | * |
63 | * Each bit in the value returned by GHKS remains set until the corresponding |
64 | * get method (GBLL, GMOU or GVOL) is called. |
65 | * |
66 | * The FUJ02E3 device manages the laptop hotkeys (such as the `Eco' button) and |
67 | * provides additional services (such as backlight on/off control) through the |
68 | * FUNC method. |
69 | * |
70 | * The FUJ02E3 device provides the following methods (or only a subset): |
71 | * |
72 | * GIRB get next hotkey code from buffer |
73 | * FUNC general-purpose method (four arguments) |
74 | * |
75 | * Notifications are delivered to the FUJ02E3 device when hotkeys are pressed |
76 | * and when they are released. The BIOS stores the corresponding codes in a |
77 | * FIFO buffer, that can be read with the GIRB method. |
78 | */ |
79 | |
80 | #include <sys/cdefs.h> |
81 | __KERNEL_RCSID(0, "$NetBSD: fujbp_acpi.c,v 1.4 2014/02/25 18:30:09 pooka Exp $" ); |
82 | |
83 | #include <sys/param.h> |
84 | #include <sys/device.h> |
85 | #include <sys/module.h> |
86 | #include <sys/mutex.h> |
87 | #include <sys/sysctl.h> |
88 | |
89 | #include <dev/acpi/acpireg.h> |
90 | #include <dev/acpi/acpivar.h> |
91 | |
92 | #define _COMPONENT ACPI_RESOURCE_COMPONENT |
93 | ACPI_MODULE_NAME ("fujbp_acpi" ) |
94 | |
95 | /* |
96 | * Notification value, bits returned by the GHKS method, and |
97 | * modification status bits (from GBLL, GMOU, GIRB), respectively. |
98 | */ |
99 | #define FUJITSU_BP_NOTIFY 0x80 |
100 | #define FUJITSU_BP_HKS_BRIGHTNESS __BIT(0) |
101 | #define FUJITSU_BP_HKS_POINTER __BIT(3) |
102 | #define FUJITSU_BP_MODMASK 0xc0000000 |
103 | |
104 | /* |
105 | * ACPI Fujitsu brightness & pointer controller capabilities (methods). |
106 | */ |
107 | #define FUJITSU_BP_CAP_GHKS __BIT(0) |
108 | #define FUJITSU_BP_CAP_RBLL __BIT(1) |
109 | #define FUJITSU_BP_CAP_GBLL __BIT(2) |
110 | #define FUJITSU_BP_CAP_SBLL __BIT(3) |
111 | #define FUJITSU_BP_CAP_GMOU __BIT(4) |
112 | #define FUJITSU_BP_CAP_SMOU __BIT(5) |
113 | |
114 | /* |
115 | * fujitsu_bp_softc: |
116 | * |
117 | * Software state of an ACPI Fujitsu brightness & pointer controller. |
118 | * Valid brightness levels range from 0 to (sc_brightness_nlevels - 1). |
119 | */ |
120 | struct fujitsu_bp_softc { |
121 | device_t sc_dev; |
122 | struct acpi_devnode *sc_node; |
123 | struct sysctllog *sc_log; |
124 | kmutex_t sc_mtx; |
125 | uint16_t sc_caps; |
126 | uint8_t sc_brightness_nlevels; |
127 | }; |
128 | |
129 | static const char * const fujitsu_bp_hid[] = { |
130 | "FUJ02B1" , |
131 | NULL |
132 | }; |
133 | |
134 | static int fujitsu_bp_match(device_t, cfdata_t, void *); |
135 | static void fujitsu_bp_attach(device_t, device_t, void *); |
136 | static int fujitsu_bp_detach(device_t, int); |
137 | static bool fujitsu_bp_suspend(device_t, const pmf_qual_t *); |
138 | static bool fujitsu_bp_resume(device_t, const pmf_qual_t *); |
139 | static void fujitsu_bp_brightness_up(device_t); |
140 | static void fujitsu_bp_brightness_down(device_t); |
141 | static uint16_t fujitsu_bp_capabilities(const struct acpi_devnode *); |
142 | static void fujitsu_bp_notify_handler(ACPI_HANDLE, uint32_t, void *); |
143 | static void fujitsu_bp_event_callback(void *); |
144 | static void fujitsu_bp_sysctl_setup(struct fujitsu_bp_softc *); |
145 | static int fujitsu_bp_sysctl_brightness(SYSCTLFN_PROTO); |
146 | static int fujitsu_bp_sysctl_pointer(SYSCTLFN_PROTO); |
147 | static int fujitsu_bp_get_hks(struct fujitsu_bp_softc *, uint32_t *); |
148 | static int fujitsu_bp_init_brightness(struct fujitsu_bp_softc *,uint8_t*); |
149 | static int fujitsu_bp_get_brightness(struct fujitsu_bp_softc *,uint8_t *); |
150 | static int fujitsu_bp_set_brightness(struct fujitsu_bp_softc *, uint8_t); |
151 | static int fujitsu_bp_get_pointer(struct fujitsu_bp_softc *, bool *); |
152 | static int fujitsu_bp_set_pointer(struct fujitsu_bp_softc *, bool); |
153 | static bool fujitsu_bp_cap(ACPI_HANDLE, const char *, ACPI_OBJECT_TYPE); |
154 | |
155 | CFATTACH_DECL_NEW(fujbp, sizeof(struct fujitsu_bp_softc), |
156 | fujitsu_bp_match, fujitsu_bp_attach, fujitsu_bp_detach, NULL); |
157 | |
158 | static int |
159 | fujitsu_bp_match(device_t parent, cfdata_t match, void *aux) |
160 | { |
161 | struct acpi_attach_args *aa = aux; |
162 | |
163 | if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) |
164 | return 0; |
165 | |
166 | return acpi_match_hid(aa->aa_node->ad_devinfo, fujitsu_bp_hid); |
167 | } |
168 | |
169 | static void |
170 | fujitsu_bp_attach(device_t parent, device_t self, void *aux) |
171 | { |
172 | struct fujitsu_bp_softc *sc = device_private(self); |
173 | struct acpi_attach_args *aa = aux; |
174 | struct acpi_devnode *ad = aa->aa_node; |
175 | |
176 | aprint_naive(": Fujitsu Brightness & Pointer\n" ); |
177 | aprint_normal(": Fujitsu Brightness & Pointer\n" ); |
178 | |
179 | sc->sc_dev = self; |
180 | sc->sc_node = ad; |
181 | sc->sc_log = NULL; |
182 | sc->sc_caps = fujitsu_bp_capabilities(ad); |
183 | |
184 | mutex_init(&sc->sc_mtx, MUTEX_DEFAULT, IPL_NONE); |
185 | |
186 | if (fujitsu_bp_init_brightness(sc, &sc->sc_brightness_nlevels)) |
187 | sc->sc_brightness_nlevels = 0; |
188 | |
189 | (void)acpi_register_notify(sc->sc_node, fujitsu_bp_notify_handler); |
190 | (void)pmf_device_register(self, fujitsu_bp_suspend, fujitsu_bp_resume); |
191 | |
192 | fujitsu_bp_sysctl_setup(sc); |
193 | |
194 | (void)pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP, |
195 | fujitsu_bp_brightness_up, true); |
196 | |
197 | (void)pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, |
198 | fujitsu_bp_brightness_down, true); |
199 | } |
200 | |
201 | static int |
202 | fujitsu_bp_detach(device_t self, int flags) |
203 | { |
204 | struct fujitsu_bp_softc *sc = device_private(self); |
205 | |
206 | pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, |
207 | fujitsu_bp_brightness_down, true); |
208 | |
209 | pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_UP, |
210 | fujitsu_bp_brightness_up, true); |
211 | |
212 | pmf_device_deregister(self); |
213 | |
214 | if (sc->sc_log != NULL) |
215 | sysctl_teardown(&sc->sc_log); |
216 | |
217 | acpi_deregister_notify(sc->sc_node); |
218 | mutex_destroy(&sc->sc_mtx); |
219 | |
220 | return 0; |
221 | } |
222 | |
223 | /* |
224 | * On some LifeBook models, a call to the SMOU method is required to make the |
225 | * internal pointer work after resume. On the P7120, the internal pointer is |
226 | * always enabled after resume. If it was disabled before suspend, the BIOS |
227 | * apparently believes that it is still disabled after resume. |
228 | * |
229 | * To prevent these problems, we disable the internal pointer on suspend and |
230 | * enable it on resume. |
231 | */ |
232 | static bool |
233 | fujitsu_bp_suspend(device_t self, const pmf_qual_t *qual) |
234 | { |
235 | struct fujitsu_bp_softc *sc = device_private(self); |
236 | |
237 | mutex_enter(&sc->sc_mtx); |
238 | (void)fujitsu_bp_set_pointer(sc, false); |
239 | mutex_exit(&sc->sc_mtx); |
240 | |
241 | return true; |
242 | } |
243 | |
244 | static bool |
245 | fujitsu_bp_resume(device_t self, const pmf_qual_t *qual) |
246 | { |
247 | struct fujitsu_bp_softc *sc = device_private(self); |
248 | |
249 | mutex_enter(&sc->sc_mtx); |
250 | (void)fujitsu_bp_set_pointer(sc, true); |
251 | mutex_exit(&sc->sc_mtx); |
252 | |
253 | return true; |
254 | } |
255 | |
256 | static void |
257 | fujitsu_bp_brightness_up(device_t self) |
258 | { |
259 | struct fujitsu_bp_softc *sc = device_private(self); |
260 | uint8_t level; |
261 | |
262 | mutex_enter(&sc->sc_mtx); |
263 | |
264 | if (fujitsu_bp_get_brightness(sc, &level) == 0 && |
265 | level < (uint8_t)(sc->sc_brightness_nlevels - 1)) |
266 | (void)fujitsu_bp_set_brightness(sc, level + 1); |
267 | |
268 | mutex_exit(&sc->sc_mtx); |
269 | } |
270 | |
271 | static void |
272 | fujitsu_bp_brightness_down(device_t self) |
273 | { |
274 | struct fujitsu_bp_softc *sc = device_private(self); |
275 | uint8_t level; |
276 | |
277 | mutex_enter(&sc->sc_mtx); |
278 | |
279 | if (fujitsu_bp_get_brightness(sc, &level) == 0 && level > 0) |
280 | (void)fujitsu_bp_set_brightness(sc, level - 1); |
281 | |
282 | mutex_exit(&sc->sc_mtx); |
283 | } |
284 | |
285 | static uint16_t |
286 | fujitsu_bp_capabilities(const struct acpi_devnode *ad) |
287 | { |
288 | uint16_t caps; |
289 | |
290 | caps = 0; |
291 | |
292 | if (fujitsu_bp_cap(ad->ad_handle, "GHKS" , ACPI_TYPE_INTEGER)) |
293 | caps |= FUJITSU_BP_CAP_GHKS; |
294 | |
295 | if (fujitsu_bp_cap(ad->ad_handle, "RBLL" , ACPI_TYPE_INTEGER)) |
296 | caps |= FUJITSU_BP_CAP_RBLL; |
297 | |
298 | if (fujitsu_bp_cap(ad->ad_handle, "GBLL" , ACPI_TYPE_INTEGER)) |
299 | caps |= FUJITSU_BP_CAP_GBLL; |
300 | |
301 | if (fujitsu_bp_cap(ad->ad_handle, "SBLL" , ACPI_TYPE_METHOD)) |
302 | caps |= FUJITSU_BP_CAP_SBLL; |
303 | |
304 | if (fujitsu_bp_cap(ad->ad_handle, "GMOU" , ACPI_TYPE_INTEGER)) |
305 | caps |= FUJITSU_BP_CAP_GMOU; |
306 | |
307 | if (fujitsu_bp_cap(ad->ad_handle, "SMOU" , ACPI_TYPE_METHOD)) |
308 | caps |= FUJITSU_BP_CAP_SMOU; |
309 | |
310 | return caps; |
311 | } |
312 | |
313 | static void |
314 | fujitsu_bp_notify_handler(ACPI_HANDLE handle, uint32_t evt, void *context) |
315 | { |
316 | struct fujitsu_bp_softc *sc = device_private(context); |
317 | static const int handler = OSL_NOTIFY_HANDLER; |
318 | |
319 | switch (evt) { |
320 | |
321 | case FUJITSU_BP_NOTIFY: |
322 | (void)AcpiOsExecute(handler, fujitsu_bp_event_callback, sc); |
323 | break; |
324 | |
325 | default: |
326 | aprint_debug_dev(sc->sc_dev, "unknown notify 0x%02X\n" , evt); |
327 | } |
328 | } |
329 | |
330 | static void |
331 | fujitsu_bp_event_callback(void *arg) |
332 | { |
333 | struct fujitsu_bp_softc *sc = arg; |
334 | int error; |
335 | uint32_t hks; |
336 | uint8_t level; |
337 | bool state; |
338 | |
339 | if (fujitsu_bp_get_hks(sc, &hks)) |
340 | return; |
341 | |
342 | if (hks & FUJITSU_BP_HKS_BRIGHTNESS) { |
343 | mutex_enter(&sc->sc_mtx); |
344 | error = fujitsu_bp_get_brightness(sc, &level); |
345 | mutex_exit(&sc->sc_mtx); |
346 | if (!error) |
347 | aprint_verbose_dev(sc->sc_dev, |
348 | "brightness level is now: %" PRIu8"\n" , level); |
349 | } |
350 | |
351 | if (hks & FUJITSU_BP_HKS_POINTER) { |
352 | mutex_enter(&sc->sc_mtx); |
353 | error = fujitsu_bp_get_pointer(sc, &state); |
354 | mutex_exit(&sc->sc_mtx); |
355 | if (!error) |
356 | aprint_verbose_dev(sc->sc_dev, |
357 | "internal pointer is now: %s\n" , |
358 | state ? "enabled" : "disabled" ); |
359 | } |
360 | } |
361 | |
362 | static void |
363 | fujitsu_bp_sysctl_setup(struct fujitsu_bp_softc *sc) |
364 | { |
365 | const struct sysctlnode *rnode; |
366 | int access; |
367 | uint8_t dummy_level; |
368 | bool dummy_state; |
369 | bool brightness, pointer; |
370 | |
371 | brightness = (fujitsu_bp_get_brightness(sc, &dummy_level) == 0); |
372 | pointer = (fujitsu_bp_get_pointer(sc, &dummy_state) == 0); |
373 | |
374 | if (brightness || pointer) { |
375 | if ((sysctl_createv(&sc->sc_log, 0, NULL, &rnode, |
376 | 0, CTLTYPE_NODE, "acpi" , NULL, |
377 | NULL, 0, NULL, 0, |
378 | CTL_HW, CTL_CREATE, CTL_EOL)) != 0) |
379 | goto fail; |
380 | |
381 | if ((sysctl_createv(&sc->sc_log, 0, &rnode, &rnode, |
382 | 0, CTLTYPE_NODE, device_xname(sc->sc_dev), |
383 | SYSCTL_DESCR("Fujitsu brightness & pointer controls" ), |
384 | NULL, 0, NULL, 0, |
385 | CTL_CREATE, CTL_EOL)) != 0) |
386 | goto fail; |
387 | } |
388 | |
389 | if (brightness) { |
390 | if (sc->sc_caps & FUJITSU_BP_CAP_SBLL) |
391 | access = CTLFLAG_READWRITE; |
392 | else |
393 | access = CTLFLAG_READONLY; |
394 | |
395 | (void)sysctl_createv(&sc->sc_log, 0, &rnode, NULL, |
396 | access, CTLTYPE_INT, "brightness" , |
397 | SYSCTL_DESCR("Internal DFP brightness level" ), |
398 | fujitsu_bp_sysctl_brightness, 0, (void *)sc, 0, |
399 | CTL_CREATE, CTL_EOL); |
400 | } |
401 | |
402 | if (pointer) { |
403 | if (sc->sc_caps & FUJITSU_BP_CAP_SMOU) |
404 | access = CTLFLAG_READWRITE; |
405 | else |
406 | access = CTLFLAG_READONLY; |
407 | |
408 | (void)sysctl_createv(&sc->sc_log, 0, &rnode, NULL, |
409 | access, CTLTYPE_BOOL, "pointer" , |
410 | SYSCTL_DESCR("Internal pointer switch state" ), |
411 | fujitsu_bp_sysctl_pointer, 0, (void *)sc, 0, |
412 | CTL_CREATE, CTL_EOL); |
413 | } |
414 | |
415 | return; |
416 | |
417 | fail: |
418 | aprint_error_dev(sc->sc_dev, "couldn't add sysctl nodes\n" ); |
419 | } |
420 | |
421 | static int |
422 | fujitsu_bp_sysctl_brightness(SYSCTLFN_ARGS) |
423 | { |
424 | struct sysctlnode node; |
425 | struct fujitsu_bp_softc *sc; |
426 | int val, error; |
427 | uint8_t level; |
428 | |
429 | node = *rnode; |
430 | sc = node.sysctl_data; |
431 | |
432 | mutex_enter(&sc->sc_mtx); |
433 | error = fujitsu_bp_get_brightness(sc, &level); |
434 | mutex_exit(&sc->sc_mtx); |
435 | |
436 | if (error) |
437 | return error; |
438 | |
439 | val = (int)level; |
440 | node.sysctl_data = &val; |
441 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
442 | if (error || newp == NULL) |
443 | return error; |
444 | |
445 | if (val < 0 || val > (uint8_t)(sc->sc_brightness_nlevels - 1)) |
446 | return EINVAL; |
447 | |
448 | mutex_enter(&sc->sc_mtx); |
449 | error = fujitsu_bp_set_brightness(sc, (uint8_t)val); |
450 | mutex_exit(&sc->sc_mtx); |
451 | |
452 | return error; |
453 | } |
454 | |
455 | static int |
456 | fujitsu_bp_sysctl_pointer(SYSCTLFN_ARGS) |
457 | { |
458 | struct sysctlnode node; |
459 | struct fujitsu_bp_softc *sc; |
460 | bool val; |
461 | int error; |
462 | |
463 | node = *rnode; |
464 | sc = node.sysctl_data; |
465 | |
466 | mutex_enter(&sc->sc_mtx); |
467 | error = fujitsu_bp_get_pointer(sc, &val); |
468 | mutex_exit(&sc->sc_mtx); |
469 | |
470 | if (error) |
471 | return error; |
472 | |
473 | node.sysctl_data = &val; |
474 | error = sysctl_lookup(SYSCTLFN_CALL(&node)); |
475 | if (error || newp == NULL) |
476 | return error; |
477 | |
478 | mutex_enter(&sc->sc_mtx); |
479 | error = fujitsu_bp_set_pointer(sc, val); |
480 | mutex_exit(&sc->sc_mtx); |
481 | |
482 | return error; |
483 | } |
484 | |
485 | static int |
486 | fujitsu_bp_get_hks(struct fujitsu_bp_softc *sc, uint32_t *valuep) |
487 | { |
488 | ACPI_HANDLE hdl = sc->sc_node->ad_handle; |
489 | ACPI_INTEGER val; |
490 | ACPI_STATUS rv; |
491 | |
492 | if (!(sc->sc_caps & FUJITSU_BP_CAP_GHKS)) |
493 | return ENODEV; |
494 | |
495 | rv = acpi_eval_integer(hdl, "GHKS" , &val); |
496 | if (ACPI_FAILURE(rv)) { |
497 | aprint_error_dev(sc->sc_dev, "failed to evaluate %s.%s: %s\n" , |
498 | acpi_name(hdl), "GHKS" , AcpiFormatException(rv)); |
499 | return EIO; |
500 | } |
501 | |
502 | *valuep = (uint32_t)val; |
503 | |
504 | return 0; |
505 | } |
506 | |
507 | static int |
508 | fujitsu_bp_init_brightness(struct fujitsu_bp_softc *sc, uint8_t *valuep) |
509 | { |
510 | ACPI_HANDLE hdl = sc->sc_node->ad_handle; |
511 | ACPI_INTEGER val; |
512 | ACPI_STATUS rv; |
513 | |
514 | if (!(sc->sc_caps & FUJITSU_BP_CAP_RBLL)) |
515 | return ENODEV; |
516 | |
517 | rv = acpi_eval_integer(hdl, "RBLL" , &val); |
518 | if (ACPI_FAILURE(rv)) { |
519 | aprint_error_dev(sc->sc_dev, "failed to evaluate %s.%s: %s\n" , |
520 | acpi_name(hdl), "RBLL" , AcpiFormatException(rv)); |
521 | return EIO; |
522 | } |
523 | |
524 | if (val > UINT8_MAX) |
525 | return ERANGE; |
526 | |
527 | *valuep = (uint8_t)val; |
528 | |
529 | return 0; |
530 | } |
531 | |
532 | static int |
533 | fujitsu_bp_get_brightness(struct fujitsu_bp_softc *sc, uint8_t *valuep) |
534 | { |
535 | ACPI_HANDLE hdl = sc->sc_node->ad_handle; |
536 | ACPI_INTEGER val; |
537 | ACPI_STATUS rv; |
538 | |
539 | if (!(sc->sc_caps & FUJITSU_BP_CAP_GBLL)) |
540 | return ENODEV; |
541 | |
542 | rv = acpi_eval_integer(hdl, "GBLL" , &val); |
543 | if (ACPI_FAILURE(rv)) { |
544 | aprint_error_dev(sc->sc_dev, "failed to evaluate %s.%s: %s\n" , |
545 | acpi_name(hdl), "GBLL" , AcpiFormatException(rv)); |
546 | return EIO; |
547 | } |
548 | |
549 | /* Clear modification bits. */ |
550 | val &= ~FUJITSU_BP_MODMASK; |
551 | |
552 | if (val > UINT8_MAX) |
553 | return ERANGE; |
554 | |
555 | *valuep = (uint8_t)val; |
556 | |
557 | return 0; |
558 | } |
559 | |
560 | static int |
561 | fujitsu_bp_set_brightness(struct fujitsu_bp_softc *sc, uint8_t val) |
562 | { |
563 | ACPI_HANDLE hdl = sc->sc_node->ad_handle; |
564 | ACPI_STATUS rv; |
565 | |
566 | if (!(sc->sc_caps & FUJITSU_BP_CAP_SBLL)) |
567 | return ENODEV; |
568 | |
569 | rv = acpi_eval_set_integer(hdl, "SBLL" , val); |
570 | |
571 | if (ACPI_FAILURE(rv)) { |
572 | aprint_error_dev(sc->sc_dev, "failed to evaluate %s.%s: %s\n" , |
573 | acpi_name(hdl), "SBLL" , AcpiFormatException(rv)); |
574 | return EIO; |
575 | } |
576 | |
577 | return 0; |
578 | } |
579 | |
580 | static int |
581 | fujitsu_bp_get_pointer(struct fujitsu_bp_softc *sc, bool *valuep) |
582 | { |
583 | ACPI_HANDLE hdl = sc->sc_node->ad_handle; |
584 | ACPI_INTEGER val; |
585 | ACPI_STATUS rv; |
586 | |
587 | if (!(sc->sc_caps & FUJITSU_BP_CAP_GMOU)) |
588 | return ENODEV; |
589 | |
590 | rv = acpi_eval_integer(hdl, "GMOU" , &val); |
591 | if (ACPI_FAILURE(rv)) { |
592 | aprint_error_dev(sc->sc_dev, "failed to evaluate %s.%s: %s\n" , |
593 | acpi_name(hdl), "GMOU" , AcpiFormatException(rv)); |
594 | return EIO; |
595 | } |
596 | |
597 | /* Clear modification bits. */ |
598 | val &= ~FUJITSU_BP_MODMASK; |
599 | |
600 | if (val > 1) |
601 | return ERANGE; |
602 | |
603 | *valuep = (bool)val; |
604 | |
605 | return 0; |
606 | } |
607 | |
608 | static int |
609 | fujitsu_bp_set_pointer(struct fujitsu_bp_softc *sc, bool val) |
610 | { |
611 | ACPI_HANDLE hdl = sc->sc_node->ad_handle; |
612 | ACPI_STATUS rv; |
613 | |
614 | if (!(sc->sc_caps & FUJITSU_BP_CAP_SMOU)) |
615 | return ENODEV; |
616 | |
617 | rv = acpi_eval_set_integer(hdl, "SMOU" , val); |
618 | |
619 | if (ACPI_FAILURE(rv)) { |
620 | aprint_error_dev(sc->sc_dev, "failed to evaluate %s.%s: %s\n" , |
621 | acpi_name(hdl), "SMOU" , AcpiFormatException(rv)); |
622 | return EIO; |
623 | } |
624 | |
625 | return 0; |
626 | } |
627 | |
628 | /* |
629 | * fujitusu_bp_cap: |
630 | * |
631 | * Returns true if and only if (a) the object handle.path exists and |
632 | * (b) this object is a method or has the given type. |
633 | */ |
634 | static bool |
635 | fujitsu_bp_cap(ACPI_HANDLE handle, const char *path, ACPI_OBJECT_TYPE type) |
636 | { |
637 | ACPI_HANDLE hdl; |
638 | ACPI_OBJECT_TYPE typ; |
639 | |
640 | KASSERT(handle != NULL); |
641 | |
642 | if (ACPI_FAILURE(AcpiGetHandle(handle, path, &hdl))) |
643 | return false; |
644 | |
645 | if (ACPI_FAILURE(AcpiGetType(hdl, &typ))) |
646 | return false; |
647 | |
648 | if (typ != ACPI_TYPE_METHOD && typ != type) |
649 | return false; |
650 | |
651 | return true; |
652 | } |
653 | |
654 | MODULE(MODULE_CLASS_DRIVER, fujbp, NULL); |
655 | |
656 | #ifdef _MODULE |
657 | #include "ioconf.c" |
658 | #endif |
659 | |
660 | static int |
661 | fujbp_modcmd(modcmd_t cmd, void *aux) |
662 | { |
663 | int rv = 0; |
664 | |
665 | switch (cmd) { |
666 | |
667 | case MODULE_CMD_INIT: |
668 | |
669 | #ifdef _MODULE |
670 | rv = config_init_component(cfdriver_ioconf_fujbp, |
671 | cfattach_ioconf_fujbp, cfdata_ioconf_fujbp); |
672 | #endif |
673 | break; |
674 | |
675 | case MODULE_CMD_FINI: |
676 | |
677 | #ifdef _MODULE |
678 | rv = config_fini_component(cfdriver_ioconf_fujbp, |
679 | cfattach_ioconf_fujbp, cfdata_ioconf_fujbp); |
680 | #endif |
681 | break; |
682 | |
683 | default: |
684 | rv = ENOTTY; |
685 | } |
686 | |
687 | return rv; |
688 | } |
689 | |