Commit | Line | Data |
---|---|---|
10f97674 | 1 | /*- |
1b851d53 HT |
2 | * Copyright (c) 2004 Takanori Watanabe |
3 | * Copyright (c) 2005 Markus Brueffer <markus@FreeBSD.org> | |
4 | * All rights reserved. | |
5 | * | |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions | |
8 | * are met: | |
9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer. | |
11 | * 2. Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * | |
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
25 | * SUCH DAMAGE. | |
32af04f7 | 26 | * |
10f97674 | 27 | * $FreeBSD: src/sys/dev/acpi_support/acpi_ibm.c,v 1.19 2009/06/05 18:44:36 jkim |
1b851d53 HT |
28 | */ |
29 | ||
30 | /* | |
10f97674 AP |
31 | * Driver for extra ACPI-controlled gadgets found on IBM ThinkPad laptops. |
32 | * Inspired by the ibm-acpi and tpb projects which implement these features | |
33 | * on Linux. | |
1b851d53 HT |
34 | * |
35 | * acpi-ibm: <http://ibm-acpi.sourceforge.net/> | |
36 | * tpb: <http://www.nongnu.org/tpb/> | |
37 | */ | |
38 | ||
39 | #include "opt_acpi.h" | |
40 | #include <sys/param.h> | |
41 | #include <sys/kernel.h> | |
42 | #include <sys/bus.h> | |
1b851d53 | 43 | #include <sys/sensors.h> |
10f97674 | 44 | #include <machine/cpufunc.h> |
1b851d53 HT |
45 | #include <machine/clock.h> |
46 | ||
47 | #include "acpi.h" | |
da42c799 | 48 | #include "accommon.h" |
10f97674 | 49 | |
1b851d53 | 50 | #include "acpi_if.h" |
10f97674 AP |
51 | #include <sys/module.h> |
52 | #include <dev/acpica5/acpivar.h> | |
53 | #if defined(__FreeBSD__) | |
54 | #include <dev/led/led.h> | |
55 | #endif | |
56 | #include <sys/sysctl.h> | |
57 | #include <bus/isa/rtc.h> | |
1b851d53 HT |
58 | |
59 | #define _COMPONENT ACPI_OEM | |
10f97674 | 60 | ACPI_MODULE_NAME("IBM") |
1b851d53 HT |
61 | |
62 | /* Internal methods */ | |
10f97674 AP |
63 | #define ACPI_IBM_METHOD_EVENTS 1 |
64 | #define ACPI_IBM_METHOD_EVENTMASK 2 | |
65 | #define ACPI_IBM_METHOD_HOTKEY 3 | |
66 | #define ACPI_IBM_METHOD_BRIGHTNESS 4 | |
67 | #define ACPI_IBM_METHOD_VOLUME 5 | |
68 | #define ACPI_IBM_METHOD_MUTE 6 | |
69 | #define ACPI_IBM_METHOD_THINKLIGHT 7 | |
70 | #define ACPI_IBM_METHOD_BLUETOOTH 8 | |
71 | #define ACPI_IBM_METHOD_WLAN 9 | |
72 | #define ACPI_IBM_METHOD_FANSPEED 10 | |
73 | #define ACPI_IBM_METHOD_FANLEVEL 11 | |
74 | #define ACPI_IBM_METHOD_FANSTATUS 12 | |
75 | #define ACPI_IBM_METHOD_THERMAL 13 | |
1b851d53 HT |
76 | |
77 | /* Hotkeys/Buttons */ | |
10f97674 AP |
78 | #define IBM_RTC_HOTKEY1 0x64 |
79 | #define IBM_RTC_MASK_HOME (1 << 0) | |
80 | #define IBM_RTC_MASK_SEARCH (1 << 1) | |
81 | #define IBM_RTC_MASK_MAIL (1 << 2) | |
82 | #define IBM_RTC_MASK_WLAN (1 << 5) | |
83 | #define IBM_RTC_HOTKEY2 0x65 | |
84 | #define IBM_RTC_MASK_THINKPAD (1 << 3) | |
85 | #define IBM_RTC_MASK_ZOOM (1 << 5) | |
86 | #define IBM_RTC_MASK_VIDEO (1 << 6) | |
87 | #define IBM_RTC_MASK_HIBERNATE (1 << 7) | |
88 | #define IBM_RTC_THINKLIGHT 0x66 | |
89 | #define IBM_RTC_MASK_THINKLIGHT (1 << 4) | |
90 | #define IBM_RTC_SCREENEXPAND 0x67 | |
91 | #define IBM_RTC_MASK_SCREENEXPAND (1 << 5) | |
92 | #define IBM_RTC_BRIGHTNESS 0x6c | |
93 | #define IBM_RTC_MASK_BRIGHTNESS (1 << 5) | |
94 | #define IBM_RTC_VOLUME 0x6e | |
95 | #define IBM_RTC_MASK_VOLUME (1 << 7) | |
1b851d53 HT |
96 | |
97 | /* Embedded Controller registers */ | |
10f97674 AP |
98 | #define IBM_EC_BRIGHTNESS 0x31 |
99 | #define IBM_EC_MASK_BRI 0x7 | |
100 | #define IBM_EC_VOLUME 0x30 | |
101 | #define IBM_EC_MASK_VOL 0xf | |
102 | #define IBM_EC_MASK_MUTE (1 << 6) | |
103 | #define IBM_EC_FANSTATUS 0x2F | |
104 | #define IBM_EC_MASK_FANLEVEL 0x3f | |
105 | #define IBM_EC_MASK_FANDISENGAGED (1 << 6) | |
106 | #define IBM_EC_MASK_FANSTATUS (1 << 7) | |
107 | #define IBM_EC_FANSPEED 0x84 | |
1b851d53 HT |
108 | |
109 | /* CMOS Commands */ | |
10f97674 AP |
110 | #define IBM_CMOS_VOLUME_DOWN 0 |
111 | #define IBM_CMOS_VOLUME_UP 1 | |
112 | #define IBM_CMOS_VOLUME_MUTE 2 | |
113 | #define IBM_CMOS_BRIGHTNESS_UP 4 | |
114 | #define IBM_CMOS_BRIGHTNESS_DOWN 5 | |
1b851d53 HT |
115 | |
116 | /* ACPI methods */ | |
10f97674 AP |
117 | #define IBM_NAME_KEYLIGHT "KBLT" |
118 | #define IBM_NAME_WLAN_BT_GET "GBDC" | |
119 | #define IBM_NAME_WLAN_BT_SET "SBDC" | |
120 | #define IBM_NAME_MASK_BT (1 << 1) | |
121 | #define IBM_NAME_MASK_WLAN (1 << 2) | |
122 | #define IBM_NAME_THERMAL_GET "TMP7" | |
123 | #define IBM_NAME_THERMAL_UPDT "UPDT" | |
124 | ||
125 | #define IBM_NAME_EVENTS_STATUS_GET "DHKC" | |
126 | #define IBM_NAME_EVENTS_MASK_GET "DHKN" | |
127 | #define IBM_NAME_EVENTS_STATUS_SET "MHKC" | |
128 | #define IBM_NAME_EVENTS_MASK_SET "MHKM" | |
129 | #define IBM_NAME_EVENTS_GET "MHKP" | |
130 | #define IBM_NAME_EVENTS_AVAILMASK "MHKA" | |
131 | ||
132 | #define IBM_NUM_SENSORS 9 | |
133 | #define IBM_THERMAL_SENSORS 8 | |
1b851d53 HT |
134 | |
135 | #define ABS(x) (((x) < 0)? -(x) : (x)) | |
136 | ||
10f97674 | 137 | struct acpi_ibm_softc { |
1b851d53 HT |
138 | device_t dev; |
139 | ACPI_HANDLE handle; | |
140 | ||
141 | /* Embedded controller */ | |
142 | device_t ec_dev; | |
143 | ACPI_HANDLE ec_handle; | |
144 | ||
145 | /* CMOS */ | |
146 | ACPI_HANDLE cmos_handle; | |
147 | ||
148 | /* Fan status */ | |
149 | ACPI_HANDLE fan_handle; | |
150 | int fan_levels; | |
151 | ||
152 | /* Keylight commands and states */ | |
153 | ACPI_HANDLE light_handle; | |
154 | int light_cmd_on; | |
155 | int light_cmd_off; | |
156 | int light_val; | |
157 | int light_get_supported; | |
158 | int light_set_supported; | |
159 | ||
160 | /* led(4) interface */ | |
161 | struct cdev *led_dev; | |
162 | int led_busy; | |
163 | int led_state; | |
164 | ||
165 | int wlan_bt_flags; | |
166 | int thermal_updt_supported; | |
167 | ||
168 | unsigned int events_availmask; | |
169 | unsigned int events_initialmask; | |
170 | int events_mask_supported; | |
171 | int events_enable; | |
172 | ||
173 | /* sensors(9) related */ | |
174 | struct ksensordev sensordev; | |
10f97674 | 175 | struct ksensor sensors[IBM_NUM_SENSORS]; |
1b851d53 | 176 | |
10f97674 | 177 | struct sysctl_ctx_list sysctl_ctx; |
1b851d53 HT |
178 | struct sysctl_oid *sysctl_tree; |
179 | }; | |
180 | ||
181 | static struct { | |
182 | char *name; | |
183 | int method; | |
184 | char *description; | |
185 | int access; | |
10f97674 | 186 | } acpi_ibm_sysctls[] = { |
1b851d53 HT |
187 | { |
188 | .name = "events", | |
10f97674 | 189 | .method = ACPI_IBM_METHOD_EVENTS, |
1b851d53 HT |
190 | .description = "ACPI events enable", |
191 | .access = CTLTYPE_INT | CTLFLAG_RW | |
192 | }, | |
193 | { | |
194 | .name = "eventmask", | |
10f97674 | 195 | .method = ACPI_IBM_METHOD_EVENTMASK, |
1b851d53 HT |
196 | .description = "ACPI eventmask", |
197 | .access = CTLTYPE_INT | CTLFLAG_RW | |
198 | }, | |
199 | { | |
200 | .name = "hotkey", | |
10f97674 | 201 | .method = ACPI_IBM_METHOD_HOTKEY, |
1b851d53 HT |
202 | .description = "Key Status", |
203 | .access = CTLTYPE_INT | CTLFLAG_RD | |
204 | }, | |
205 | { | |
206 | .name = "lcd_brightness", | |
10f97674 | 207 | .method = ACPI_IBM_METHOD_BRIGHTNESS, |
1b851d53 HT |
208 | .description = "LCD Brightness", |
209 | .access = CTLTYPE_INT | CTLFLAG_RW | |
210 | }, | |
211 | { | |
212 | .name = "volume", | |
10f97674 | 213 | .method = ACPI_IBM_METHOD_VOLUME, |
1b851d53 HT |
214 | .description = "Volume", |
215 | .access = CTLTYPE_INT | CTLFLAG_RW | |
216 | }, | |
217 | { | |
218 | .name = "mute", | |
10f97674 | 219 | .method = ACPI_IBM_METHOD_MUTE, |
1b851d53 HT |
220 | .description = "Mute", |
221 | .access = CTLTYPE_INT | CTLFLAG_RW | |
222 | }, | |
223 | { | |
224 | .name = "thinklight", | |
10f97674 | 225 | .method = ACPI_IBM_METHOD_THINKLIGHT, |
1b851d53 HT |
226 | .description = "Thinklight enable", |
227 | .access = CTLTYPE_INT | CTLFLAG_RW | |
228 | }, | |
229 | { | |
230 | .name = "bluetooth", | |
10f97674 | 231 | .method = ACPI_IBM_METHOD_BLUETOOTH, |
1b851d53 HT |
232 | .description = "Bluetooth enable", |
233 | .access = CTLTYPE_INT | CTLFLAG_RW | |
234 | }, | |
235 | { | |
236 | .name = "wlan", | |
10f97674 | 237 | .method = ACPI_IBM_METHOD_WLAN, |
1b851d53 HT |
238 | .description = "WLAN enable", |
239 | .access = CTLTYPE_INT | CTLFLAG_RD | |
240 | }, | |
10f97674 AP |
241 | { |
242 | .name = "fan_speed", | |
243 | .method = ACPI_IBM_METHOD_FANSPEED, | |
244 | .description = "Fan speed", | |
245 | .access = CTLTYPE_INT | CTLFLAG_RD | |
246 | }, | |
1b851d53 HT |
247 | { |
248 | .name = "fan_level", | |
10f97674 | 249 | .method = ACPI_IBM_METHOD_FANLEVEL, |
1b851d53 HT |
250 | .description = "Fan level", |
251 | .access = CTLTYPE_INT | CTLFLAG_RW | |
252 | }, | |
253 | { | |
254 | .name = "fan", | |
10f97674 | 255 | .method = ACPI_IBM_METHOD_FANSTATUS, |
1b851d53 HT |
256 | .description = "Fan enable", |
257 | .access = CTLTYPE_INT | CTLFLAG_RW | |
258 | }, | |
259 | ||
260 | { NULL, 0, NULL, 0 } | |
261 | }; | |
262 | ||
10f97674 AP |
263 | ACPI_SERIAL_DECL(ibm, "ACPI IBM extras"); |
264 | ||
265 | static int acpi_ibm_probe(device_t dev); | |
266 | static int acpi_ibm_attach(device_t dev); | |
267 | static int acpi_ibm_detach(device_t dev); | |
268 | ||
269 | #if defined(__FreeBSD__) | |
270 | static void ibm_led(void *softc, int onoff); | |
271 | static void ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused); | |
272 | #endif | |
1b851d53 | 273 | |
10f97674 AP |
274 | static int acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS); |
275 | static int acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method); | |
276 | static int acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method); | |
277 | static int acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int val); | |
1b851d53 | 278 | |
10f97674 AP |
279 | static int acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val); |
280 | static void acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context); | |
1b851d53 | 281 | |
10f97674 | 282 | static void acpi_ibm_refresh(void *); |
1b851d53 | 283 | |
10f97674 | 284 | static device_method_t acpi_ibm_methods[] = { |
1b851d53 | 285 | /* Device interface */ |
10f97674 AP |
286 | DEVMETHOD(device_probe, acpi_ibm_probe), |
287 | DEVMETHOD(device_attach, acpi_ibm_attach), | |
288 | DEVMETHOD(device_detach, acpi_ibm_detach), | |
289 | ||
1b851d53 HT |
290 | {0, 0} |
291 | }; | |
292 | ||
10f97674 AP |
293 | static driver_t acpi_ibm_driver = { |
294 | "acpi_ibm", | |
295 | acpi_ibm_methods, | |
296 | sizeof(struct acpi_ibm_softc), | |
1b851d53 HT |
297 | }; |
298 | ||
10f97674 AP |
299 | static devclass_t acpi_ibm_devclass; |
300 | ||
301 | DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass, | |
302 | 0, 0); | |
303 | MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1); | |
304 | static char *ibm_ids[] = {"IBM0057", "IBM0068", NULL}; | |
305 | ||
306 | #if defined(__FreeBSD__) | |
307 | static void | |
308 | ibm_led(void *softc, int onoff) | |
309 | { | |
310 | struct acpi_ibm_softc* sc = (struct acpi_ibm_softc*) softc; | |
311 | ||
312 | ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | |
313 | ||
314 | if (sc->led_busy) | |
315 | return; | |
316 | ||
317 | sc->led_busy = 1; | |
318 | sc->led_state = onoff; | |
1b851d53 | 319 | |
10f97674 AP |
320 | AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)ibm_led_task, sc); |
321 | } | |
322 | ||
323 | static void | |
324 | ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused) | |
325 | { | |
326 | ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | |
327 | ||
328 | ACPI_SERIAL_BEGIN(ibm); | |
329 | acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_THINKLIGHT, sc->led_state); | |
330 | ACPI_SERIAL_END(ibm); | |
331 | ||
332 | sc->led_busy = 0; | |
333 | } | |
334 | #endif | |
1b851d53 HT |
335 | |
336 | static int | |
10f97674 | 337 | acpi_ibm_probe(device_t dev) |
1b851d53 | 338 | { |
10f97674 AP |
339 | if (acpi_disabled("ibm") || |
340 | ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_ids) == NULL || | |
341 | device_get_unit(dev) != 0) | |
1b851d53 HT |
342 | return (ENXIO); |
343 | ||
10f97674 AP |
344 | device_set_desc(dev, "IBM ThinkPad ACPI Extras"); |
345 | ||
1b851d53 HT |
346 | return (0); |
347 | } | |
348 | ||
349 | static int | |
10f97674 | 350 | acpi_ibm_attach(device_t dev) |
1b851d53 | 351 | { |
10f97674 | 352 | struct acpi_ibm_softc *sc; |
1b851d53 | 353 | devclass_t ec_devclass; |
10f97674 AP |
354 | struct acpi_softc *acpi_sc; |
355 | int i; | |
1b851d53 HT |
356 | |
357 | ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); | |
358 | ||
359 | sc = device_get_softc(dev); | |
360 | sc->dev = dev; | |
361 | sc->handle = acpi_get_handle(dev); | |
362 | ||
1b851d53 HT |
363 | /* Look for the first embedded controller */ |
364 | if (!(ec_devclass = devclass_find ("acpi_ec"))) { | |
365 | if (bootverbose) | |
366 | device_printf(dev, "Couldn't find acpi_ec devclass\n"); | |
367 | return (EINVAL); | |
368 | } | |
369 | if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) { | |
370 | if (bootverbose) | |
371 | device_printf(dev, "Couldn't find acpi_ec device\n"); | |
372 | return (EINVAL); | |
373 | } | |
374 | sc->ec_handle = acpi_get_handle(sc->ec_dev); | |
10f97674 AP |
375 | acpi_sc = acpi_device_get_parent_softc(dev); |
376 | ||
377 | ACPI_SERIAL_BEGIN(ibm); | |
1b851d53 | 378 | |
10f97674 | 379 | /* Get the sysctl tree */ |
1b851d53 HT |
380 | sysctl_ctx_init(&sc->sysctl_ctx); |
381 | sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, | |
10f97674 AP |
382 | SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, |
383 | "thinkpad", CTLFLAG_RD, 0, ""); | |
1b851d53 HT |
384 | |
385 | /* Look for event mask and hook up the nodes */ | |
386 | sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle, | |
10f97674 | 387 | IBM_NAME_EVENTS_MASK_GET, &sc->events_initialmask)); |
1b851d53 HT |
388 | |
389 | if (sc->events_mask_supported) { | |
390 | SYSCTL_ADD_INT(&sc->sysctl_ctx, | |
391 | SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, | |
392 | "initialmask", CTLFLAG_RD, | |
393 | &sc->events_initialmask, 0, "Initial eventmask"); | |
394 | ||
395 | /* The availmask is the bitmask of supported events */ | |
396 | if (ACPI_FAILURE(acpi_GetInteger(sc->handle, | |
10f97674 | 397 | IBM_NAME_EVENTS_AVAILMASK, &sc->events_availmask))) |
1b851d53 HT |
398 | sc->events_availmask = 0xffffffff; |
399 | ||
400 | SYSCTL_ADD_INT(&sc->sysctl_ctx, | |
401 | SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, | |
402 | "availmask", CTLFLAG_RD, | |
403 | &sc->events_availmask, 0, "Mask of supported events"); | |
404 | } | |
405 | ||
406 | /* Hook up proc nodes */ | |
10f97674 AP |
407 | for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) { |
408 | if (!acpi_ibm_sysctl_init(sc, acpi_ibm_sysctls[i].method)) | |
1b851d53 HT |
409 | continue; |
410 | ||
411 | SYSCTL_ADD_PROC(&sc->sysctl_ctx, | |
412 | SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, | |
10f97674 AP |
413 | acpi_ibm_sysctls[i].name, acpi_ibm_sysctls[i].access, |
414 | sc, i, acpi_ibm_sysctl, "I", | |
415 | acpi_ibm_sysctls[i].description); | |
1b851d53 | 416 | } |
10f97674 | 417 | ACPI_SERIAL_END(ibm); |
1b851d53 HT |
418 | |
419 | /* Handle notifies */ | |
420 | AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, | |
10f97674 | 421 | acpi_ibm_notify, dev); |
1b851d53 | 422 | |
10f97674 AP |
423 | /* Attach sensors(9). */ |
424 | if (sensor_task_register(sc, acpi_ibm_refresh, 5)) { | |
425 | device_printf(sc->dev, "unable to register update task\n"); | |
426 | return 1; | |
427 | } | |
428 | strlcpy(sc->sensordev.xname, device_get_nameunit(sc->dev), | |
429 | sizeof(sc->sensordev.xname)); | |
1b851d53 | 430 | |
10f97674 AP |
431 | for (i = 0; i < IBM_THERMAL_SENSORS; i++) { |
432 | sc->sensors[i].type = SENSOR_TEMP; | |
433 | sensor_attach(&sc->sensordev, &sc->sensors[i]); | |
434 | } | |
435 | ||
436 | sc->sensors[i].type = SENSOR_FANRPM; | |
437 | sensor_attach(&sc->sensordev, &sc->sensors[i]); | |
1b851d53 | 438 | |
10f97674 | 439 | sensordev_install(&sc->sensordev); |
1b851d53 | 440 | |
10f97674 AP |
441 | #if defined(__FreeBSD__) |
442 | /* Hook up light to led(4) */ | |
443 | if (sc->light_set_supported) | |
444 | sc->led_dev = led_create_state(ibm_led, sc, "thinklight", sc->light_val); | |
445 | #endif | |
1b851d53 HT |
446 | |
447 | return (0); | |
448 | } | |
449 | ||
450 | static int | |
10f97674 | 451 | acpi_ibm_detach(device_t dev) |
1b851d53 HT |
452 | { |
453 | int i; | |
1b851d53 HT |
454 | ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); |
455 | ||
10f97674 | 456 | struct acpi_ibm_softc *sc = device_get_softc(dev); |
1b851d53 HT |
457 | |
458 | /* Disable events and restore eventmask */ | |
10f97674 AP |
459 | ACPI_SERIAL_BEGIN(ibm); |
460 | acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 0); | |
461 | acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK, sc->events_initialmask); | |
462 | ACPI_SERIAL_END(ibm); | |
1b851d53 | 463 | |
10f97674 | 464 | AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_ibm_notify); |
1b851d53 | 465 | |
10f97674 AP |
466 | sensordev_deinstall(&sc->sensordev); |
467 | for (i = 0; i < IBM_THERMAL_SENSORS; i++) | |
468 | sensor_detach(&sc->sensordev, &sc->sensors[i]); | |
469 | sensor_task_unregister(sc); | |
1b851d53 | 470 | |
10f97674 AP |
471 | #if defined(__FreeBSD__) |
472 | if (sc->led_dev != NULL) | |
473 | led_destroy(sc->led_dev); | |
474 | #endif | |
1b851d53 HT |
475 | |
476 | return (0); | |
477 | } | |
478 | ||
479 | static int | |
10f97674 | 480 | acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val) |
1b851d53 | 481 | { |
1b851d53 HT |
482 | ACPI_OBJECT arg[2]; |
483 | ACPI_OBJECT_LIST args; | |
484 | ACPI_STATUS status; | |
485 | ||
486 | ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | |
10f97674 | 487 | ACPI_SERIAL_ASSERT(ibm); |
1b851d53 HT |
488 | |
489 | args.Count = 2; | |
490 | args.Pointer = arg; | |
491 | arg[0].Type = ACPI_TYPE_INTEGER; | |
492 | arg[1].Type = ACPI_TYPE_INTEGER; | |
493 | ||
10f97674 | 494 | for (int i = 0; i < 32; ++i) { |
1b851d53 HT |
495 | arg[0].Integer.Value = i+1; |
496 | arg[1].Integer.Value = (((1 << i) & val) != 0); | |
497 | status = AcpiEvaluateObject(sc->handle, | |
10f97674 | 498 | IBM_NAME_EVENTS_MASK_SET, &args, NULL); |
1b851d53 HT |
499 | |
500 | if (ACPI_FAILURE(status)) | |
501 | return (status); | |
502 | } | |
503 | ||
504 | return (0); | |
505 | } | |
506 | ||
507 | static int | |
10f97674 | 508 | acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS) |
1b851d53 | 509 | { |
10f97674 | 510 | struct acpi_ibm_softc *sc; |
1b851d53 HT |
511 | int arg; |
512 | int error = 0; | |
513 | int function; | |
514 | int method; | |
515 | ||
516 | ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | |
517 | ||
10f97674 | 518 | sc = (struct acpi_ibm_softc *)oidp->oid_arg1; |
1b851d53 | 519 | function = oidp->oid_arg2; |
10f97674 | 520 | method = acpi_ibm_sysctls[function].method; |
1b851d53 | 521 | |
10f97674 AP |
522 | ACPI_SERIAL_BEGIN(ibm); |
523 | arg = acpi_ibm_sysctl_get(sc, method); | |
1b851d53 HT |
524 | error = sysctl_handle_int(oidp, &arg, 0, req); |
525 | ||
526 | /* Sanity check */ | |
527 | if (error != 0 || req->newptr == NULL) | |
528 | goto out; | |
529 | ||
530 | /* Update */ | |
10f97674 | 531 | error = acpi_ibm_sysctl_set(sc, method, arg); |
1b851d53 HT |
532 | |
533 | out: | |
10f97674 | 534 | ACPI_SERIAL_END(ibm); |
1b851d53 HT |
535 | return (error); |
536 | } | |
537 | ||
538 | static int | |
10f97674 | 539 | acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method) |
1b851d53 HT |
540 | { |
541 | ACPI_INTEGER val_ec; | |
542 | int val = 0, key; | |
543 | ||
544 | ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | |
10f97674 | 545 | ACPI_SERIAL_ASSERT(ibm); |
1b851d53 HT |
546 | |
547 | switch (method) { | |
10f97674 AP |
548 | case ACPI_IBM_METHOD_EVENTS: |
549 | acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_GET, &val); | |
1b851d53 HT |
550 | break; |
551 | ||
10f97674 | 552 | case ACPI_IBM_METHOD_EVENTMASK: |
1b851d53 | 553 | if (sc->events_mask_supported) |
10f97674 | 554 | acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_MASK_GET, &val); |
1b851d53 HT |
555 | break; |
556 | ||
10f97674 | 557 | case ACPI_IBM_METHOD_HOTKEY: |
1b851d53 HT |
558 | /* |
559 | * Construct the hotkey as a bitmask as illustrated below. | |
560 | * Note that whenever a key was pressed, the respecting bit | |
561 | * toggles and nothing else changes. | |
562 | * +--+--+-+-+-+-+-+-+-+-+-+-+ | |
563 | * |11|10|9|8|7|6|5|4|3|2|1|0| | |
564 | * +--+--+-+-+-+-+-+-+-+-+-+-+ | |
565 | * | | | | | | | | | | | | | |
566 | * | | | | | | | | | | | +- Home Button | |
567 | * | | | | | | | | | | +--- Search Button | |
568 | * | | | | | | | | | +----- Mail Button | |
569 | * | | | | | | | | +------- Thinkpad Button | |
570 | * | | | | | | | +--------- Zoom (Fn + Space) | |
571 | * | | | | | | +----------- WLAN Button | |
572 | * | | | | | +------------- Video Button | |
573 | * | | | | +--------------- Hibernate Button | |
574 | * | | | +----------------- Thinklight Button | |
575 | * | | +------------------- Screen expand (Fn + F8) | |
576 | * | +--------------------- Brightness | |
577 | * +------------------------ Volume/Mute | |
578 | */ | |
10f97674 AP |
579 | key = rtcin(IBM_RTC_HOTKEY1); |
580 | val = (IBM_RTC_MASK_HOME | IBM_RTC_MASK_SEARCH | IBM_RTC_MASK_MAIL | IBM_RTC_MASK_WLAN) & key; | |
581 | key = rtcin(IBM_RTC_HOTKEY2); | |
582 | val |= (IBM_RTC_MASK_THINKPAD | IBM_RTC_MASK_VIDEO | IBM_RTC_MASK_HIBERNATE) & key; | |
583 | val |= (IBM_RTC_MASK_ZOOM & key) >> 1; | |
584 | key = rtcin(IBM_RTC_THINKLIGHT); | |
585 | val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; | |
586 | key = rtcin(IBM_RTC_SCREENEXPAND); | |
587 | val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; | |
588 | key = rtcin(IBM_RTC_BRIGHTNESS); | |
589 | val |= (IBM_RTC_MASK_BRIGHTNESS & key) << 5; | |
590 | key = rtcin(IBM_RTC_VOLUME); | |
591 | val |= (IBM_RTC_MASK_VOLUME & key) << 4; | |
1b851d53 HT |
592 | break; |
593 | ||
10f97674 AP |
594 | case ACPI_IBM_METHOD_BRIGHTNESS: |
595 | ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); | |
596 | val = val_ec & IBM_EC_MASK_BRI; | |
1b851d53 HT |
597 | break; |
598 | ||
10f97674 AP |
599 | case ACPI_IBM_METHOD_VOLUME: |
600 | ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); | |
601 | val = val_ec & IBM_EC_MASK_VOL; | |
1b851d53 HT |
602 | break; |
603 | ||
10f97674 AP |
604 | case ACPI_IBM_METHOD_MUTE: |
605 | ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); | |
606 | val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE); | |
1b851d53 HT |
607 | break; |
608 | ||
10f97674 | 609 | case ACPI_IBM_METHOD_THINKLIGHT: |
1b851d53 | 610 | if (sc->light_get_supported) |
10f97674 | 611 | acpi_GetInteger(sc->ec_handle, IBM_NAME_KEYLIGHT, &val); |
1b851d53 HT |
612 | else |
613 | val = sc->light_val; | |
614 | break; | |
615 | ||
10f97674 AP |
616 | case ACPI_IBM_METHOD_BLUETOOTH: |
617 | acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); | |
1b851d53 | 618 | sc->wlan_bt_flags = val; |
10f97674 | 619 | val = ((val & IBM_NAME_MASK_BT) != 0); |
1b851d53 HT |
620 | break; |
621 | ||
10f97674 AP |
622 | case ACPI_IBM_METHOD_WLAN: |
623 | acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); | |
1b851d53 | 624 | sc->wlan_bt_flags = val; |
10f97674 | 625 | val = ((val & IBM_NAME_MASK_WLAN) != 0); |
1b851d53 HT |
626 | break; |
627 | ||
10f97674 | 628 | case ACPI_IBM_METHOD_FANSPEED: |
1b851d53 | 629 | if (sc->fan_handle) { |
10f97674 | 630 | if(ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, NULL, &val))) |
1b851d53 HT |
631 | val = -1; |
632 | } | |
633 | else { | |
10f97674 | 634 | ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSPEED, &val_ec, 2); |
1b851d53 HT |
635 | val = val_ec; |
636 | } | |
637 | break; | |
638 | ||
10f97674 | 639 | case ACPI_IBM_METHOD_FANLEVEL: |
1b851d53 | 640 | /* |
10f97674 | 641 | * The IBM_EC_FANSTATUS register works as follows: |
1b851d53 HT |
642 | * Bit 0-5 indicate the level at which the fan operates. Only |
643 | * values between 0 and 7 have an effect. Everything | |
644 | * above 7 is treated the same as level 7 | |
645 | * Bit 6 overrides the fan speed limit if set to 1 | |
646 | * Bit 7 indicates at which mode the fan operates: | |
647 | * manual (0) or automatic (1) | |
648 | */ | |
649 | if (!sc->fan_handle) { | |
10f97674 AP |
650 | ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); |
651 | val = val_ec & IBM_EC_MASK_FANLEVEL; | |
1b851d53 HT |
652 | } |
653 | break; | |
654 | ||
10f97674 | 655 | case ACPI_IBM_METHOD_FANSTATUS: |
1b851d53 | 656 | if (!sc->fan_handle) { |
10f97674 AP |
657 | ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); |
658 | val = (val_ec & IBM_EC_MASK_FANSTATUS) == IBM_EC_MASK_FANSTATUS; | |
1b851d53 HT |
659 | } |
660 | else | |
661 | val = -1; | |
662 | break; | |
663 | } | |
664 | ||
665 | return (val); | |
666 | } | |
667 | ||
668 | static int | |
10f97674 | 669 | acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg) |
1b851d53 | 670 | { |
10f97674 | 671 | int val, step; |
1b851d53 HT |
672 | ACPI_INTEGER val_ec; |
673 | ACPI_OBJECT Arg; | |
674 | ACPI_OBJECT_LIST Args; | |
675 | ACPI_STATUS status; | |
676 | ||
677 | ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | |
10f97674 | 678 | ACPI_SERIAL_ASSERT(ibm); |
1b851d53 HT |
679 | |
680 | switch (method) { | |
10f97674 | 681 | case ACPI_IBM_METHOD_EVENTS: |
1b851d53 HT |
682 | if (arg < 0 || arg > 1) |
683 | return (EINVAL); | |
684 | ||
10f97674 | 685 | status = acpi_SetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_SET, arg); |
1b851d53 HT |
686 | if (ACPI_FAILURE(status)) |
687 | return (status); | |
688 | if (sc->events_mask_supported) | |
10f97674 | 689 | return acpi_ibm_eventmask_set(sc, sc->events_availmask); |
1b851d53 HT |
690 | break; |
691 | ||
10f97674 | 692 | case ACPI_IBM_METHOD_EVENTMASK: |
1b851d53 | 693 | if (sc->events_mask_supported) |
10f97674 | 694 | return acpi_ibm_eventmask_set(sc, arg); |
1b851d53 HT |
695 | break; |
696 | ||
10f97674 | 697 | case ACPI_IBM_METHOD_BRIGHTNESS: |
1b851d53 HT |
698 | if (arg < 0 || arg > 7) |
699 | return (EINVAL); | |
700 | ||
701 | if (sc->cmos_handle) { | |
702 | /* Read the current brightness */ | |
10f97674 | 703 | status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); |
1b851d53 HT |
704 | if (ACPI_FAILURE(status)) |
705 | return (status); | |
10f97674 | 706 | val = val_ec & IBM_EC_MASK_BRI; |
1b851d53 HT |
707 | |
708 | Args.Count = 1; | |
709 | Args.Pointer = &Arg; | |
710 | Arg.Type = ACPI_TYPE_INTEGER; | |
10f97674 | 711 | Arg.Integer.Value = (arg > val) ? IBM_CMOS_BRIGHTNESS_UP : IBM_CMOS_BRIGHTNESS_DOWN; |
1b851d53 HT |
712 | |
713 | step = (arg > val) ? 1 : -1; | |
10f97674 AP |
714 | for (int i = val; i != arg; i += step) { |
715 | status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); | |
1b851d53 HT |
716 | if (ACPI_FAILURE(status)) |
717 | break; | |
718 | } | |
719 | } | |
10f97674 | 720 | return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1); |
1b851d53 HT |
721 | break; |
722 | ||
10f97674 | 723 | case ACPI_IBM_METHOD_VOLUME: |
1b851d53 HT |
724 | if (arg < 0 || arg > 14) |
725 | return (EINVAL); | |
726 | ||
10f97674 | 727 | status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); |
1b851d53 HT |
728 | if (ACPI_FAILURE(status)) |
729 | return (status); | |
730 | ||
731 | if (sc->cmos_handle) { | |
10f97674 | 732 | val = val_ec & IBM_EC_MASK_VOL; |
1b851d53 HT |
733 | |
734 | Args.Count = 1; | |
735 | Args.Pointer = &Arg; | |
736 | Arg.Type = ACPI_TYPE_INTEGER; | |
10f97674 | 737 | Arg.Integer.Value = (arg > val) ? IBM_CMOS_VOLUME_UP : IBM_CMOS_VOLUME_DOWN; |
1b851d53 HT |
738 | |
739 | step = (arg > val) ? 1 : -1; | |
10f97674 AP |
740 | for (int i = val; i != arg; i += step) { |
741 | status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); | |
1b851d53 HT |
742 | if (ACPI_FAILURE(status)) |
743 | break; | |
744 | } | |
745 | } | |
10f97674 | 746 | return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, arg + (val_ec & (~IBM_EC_MASK_VOL)), 1); |
1b851d53 HT |
747 | break; |
748 | ||
10f97674 | 749 | case ACPI_IBM_METHOD_MUTE: |
1b851d53 HT |
750 | if (arg < 0 || arg > 1) |
751 | return (EINVAL); | |
752 | ||
10f97674 | 753 | status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); |
1b851d53 HT |
754 | if (ACPI_FAILURE(status)) |
755 | return (status); | |
756 | ||
757 | if (sc->cmos_handle) { | |
10f97674 | 758 | val = val_ec & IBM_EC_MASK_VOL; |
1b851d53 HT |
759 | |
760 | Args.Count = 1; | |
761 | Args.Pointer = &Arg; | |
762 | Arg.Type = ACPI_TYPE_INTEGER; | |
10f97674 | 763 | Arg.Integer.Value = IBM_CMOS_VOLUME_MUTE; |
1b851d53 | 764 | |
10f97674 | 765 | status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); |
1b851d53 HT |
766 | if (ACPI_FAILURE(status)) |
767 | break; | |
768 | } | |
10f97674 | 769 | return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, (arg==1) ? val_ec | IBM_EC_MASK_MUTE : val_ec & (~IBM_EC_MASK_MUTE), 1); |
1b851d53 HT |
770 | break; |
771 | ||
10f97674 | 772 | case ACPI_IBM_METHOD_THINKLIGHT: |
1b851d53 HT |
773 | if (arg < 0 || arg > 1) |
774 | return (EINVAL); | |
775 | ||
776 | if (sc->light_set_supported) { | |
777 | Args.Count = 1; | |
778 | Args.Pointer = &Arg; | |
779 | Arg.Type = ACPI_TYPE_INTEGER; | |
10f97674 | 780 | Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off; |
1b851d53 | 781 | |
10f97674 | 782 | status = AcpiEvaluateObject(sc->light_handle, NULL, &Args, NULL); |
1b851d53 HT |
783 | if (ACPI_SUCCESS(status)) |
784 | sc->light_val = arg; | |
785 | return (status); | |
786 | } | |
787 | break; | |
788 | ||
10f97674 | 789 | case ACPI_IBM_METHOD_BLUETOOTH: |
1b851d53 HT |
790 | if (arg < 0 || arg > 1) |
791 | return (EINVAL); | |
792 | ||
10f97674 AP |
793 | val = (arg == 1) ? sc->wlan_bt_flags | IBM_NAME_MASK_BT : sc->wlan_bt_flags & (~IBM_NAME_MASK_BT); |
794 | return acpi_SetInteger(sc->handle, IBM_NAME_WLAN_BT_SET, val); | |
1b851d53 HT |
795 | break; |
796 | ||
10f97674 | 797 | case ACPI_IBM_METHOD_FANLEVEL: |
1b851d53 HT |
798 | if (arg < 0 || arg > 7) |
799 | return (EINVAL); | |
800 | ||
801 | if (!sc->fan_handle) { | |
802 | /* Read the current fanstatus */ | |
10f97674 AP |
803 | ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); |
804 | val = val_ec & (~IBM_EC_MASK_FANLEVEL); | |
1b851d53 | 805 | |
10f97674 | 806 | return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, val | arg, 1); |
1b851d53 HT |
807 | } |
808 | break; | |
809 | ||
10f97674 | 810 | case ACPI_IBM_METHOD_FANSTATUS: |
1b851d53 HT |
811 | if (arg < 0 || arg > 1) |
812 | return (EINVAL); | |
813 | ||
814 | if (!sc->fan_handle) { | |
815 | /* Read the current fanstatus */ | |
10f97674 | 816 | ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); |
1b851d53 | 817 | |
10f97674 AP |
818 | return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, |
819 | (arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1); | |
1b851d53 HT |
820 | } |
821 | break; | |
822 | } | |
823 | ||
824 | return (0); | |
825 | } | |
826 | ||
827 | static int | |
10f97674 | 828 | acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method) |
1b851d53 HT |
829 | { |
830 | int dummy; | |
831 | ACPI_OBJECT_TYPE cmos_t; | |
832 | ACPI_HANDLE ledb_handle; | |
833 | ||
834 | switch (method) { | |
10f97674 | 835 | case ACPI_IBM_METHOD_EVENTS: |
1b851d53 HT |
836 | /* Events are disabled by default */ |
837 | return (TRUE); | |
838 | ||
10f97674 | 839 | case ACPI_IBM_METHOD_EVENTMASK: |
1b851d53 HT |
840 | return (sc->events_mask_supported); |
841 | ||
10f97674 AP |
842 | case ACPI_IBM_METHOD_HOTKEY: |
843 | case ACPI_IBM_METHOD_BRIGHTNESS: | |
844 | case ACPI_IBM_METHOD_VOLUME: | |
845 | case ACPI_IBM_METHOD_MUTE: | |
846 | /* EC is required here, which was aready checked before */ | |
1b851d53 HT |
847 | return (TRUE); |
848 | ||
10f97674 | 849 | case ACPI_IBM_METHOD_THINKLIGHT: |
1b851d53 HT |
850 | sc->cmos_handle = NULL; |
851 | sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger( | |
10f97674 AP |
852 | sc->ec_handle, IBM_NAME_KEYLIGHT, &sc->light_val)); |
853 | ||
854 | if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", &sc->light_handle)) || | |
855 | ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", &sc->light_handle)) || | |
856 | ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", &sc->light_handle))) && | |
857 | ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) && | |
858 | cmos_t == ACPI_TYPE_METHOD) { | |
1b851d53 HT |
859 | sc->light_cmd_on = 0x0c; |
860 | sc->light_cmd_off = 0x0d; | |
861 | sc->cmos_handle = sc->light_handle; | |
862 | } | |
10f97674 | 863 | else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", &sc->light_handle))) { |
1b851d53 HT |
864 | sc->light_cmd_on = 1; |
865 | sc->light_cmd_off = 0; | |
866 | } | |
867 | else | |
868 | sc->light_handle = NULL; | |
869 | ||
870 | sc->light_set_supported = (sc->light_handle && | |
10f97674 | 871 | ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", &ledb_handle))); |
1b851d53 HT |
872 | |
873 | if (sc->light_get_supported) | |
874 | return (TRUE); | |
875 | ||
876 | if (sc->light_set_supported) { | |
877 | sc->light_val = 0; | |
878 | return (TRUE); | |
879 | } | |
880 | ||
881 | return (FALSE); | |
882 | ||
10f97674 AP |
883 | case ACPI_IBM_METHOD_BLUETOOTH: |
884 | case ACPI_IBM_METHOD_WLAN: | |
885 | if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &dummy))) | |
1b851d53 HT |
886 | return (TRUE); |
887 | return (FALSE); | |
888 | ||
10f97674 | 889 | case ACPI_IBM_METHOD_FANSPEED: |
1b851d53 HT |
890 | /* |
891 | * Some models report the fan speed in levels from 0-7 | |
892 | * Newer models report it contiguously | |
893 | */ | |
10f97674 AP |
894 | sc->fan_levels = |
895 | (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", &sc->fan_handle)) || | |
896 | ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", &sc->fan_handle))); | |
1b851d53 HT |
897 | return (TRUE); |
898 | ||
10f97674 AP |
899 | case ACPI_IBM_METHOD_FANLEVEL: |
900 | case ACPI_IBM_METHOD_FANSTATUS: | |
1b851d53 HT |
901 | /* |
902 | * Fan status is only supported on those models, | |
903 | * which report fan RPM contiguously, not in levels | |
904 | */ | |
905 | if (sc->fan_levels) | |
906 | return (FALSE); | |
907 | return (TRUE); | |
908 | ||
10f97674 AP |
909 | case ACPI_IBM_METHOD_THERMAL: |
910 | if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_GET, &dummy))) { | |
911 | sc->thermal_updt_supported = ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_UPDT, &dummy)); | |
1b851d53 HT |
912 | return (TRUE); |
913 | } | |
914 | return (FALSE); | |
915 | } | |
916 | return (FALSE); | |
917 | } | |
918 | ||
10f97674 AP |
919 | void |
920 | acpi_ibm_refresh(void *arg) | |
921 | { | |
922 | struct acpi_ibm_softc *sc; | |
923 | char temp_cmd[] = "TMP0"; | |
924 | int i, data, temp[8]; | |
925 | ACPI_INTEGER speed; | |
926 | ||
927 | ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); | |
928 | ||
929 | sc = (struct acpi_ibm_softc *)arg; | |
930 | ||
931 | ACPI_SERIAL_BEGIN(ibm); | |
932 | ||
933 | for (i = 0; i < IBM_THERMAL_SENSORS; ++i) { | |
934 | temp_cmd[3] = '0' + i; | |
935 | ||
936 | /* | |
937 | * The TMPx methods seem to return +/- 128 or 0 | |
938 | * when the respecting sensor is not available | |
939 | */ | |
940 | if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd, | |
941 | &temp[i])) || ABS(temp[i]) == 128 || temp[i] == 0) { | |
942 | sc->sensors[i].flags |= SENSOR_FINVALID; | |
943 | data = 0; | |
944 | } | |
945 | else if (sc->thermal_updt_supported) { | |
946 | /* Temperature is reported in tenth of Kelvin */ | |
947 | sc->sensors[i].value = data * 100000; | |
948 | } | |
949 | sc->sensors[i].value = data * 1000000 + 273150000; | |
950 | } | |
951 | sc->sensors[i].flags &= ~SENSOR_FINVALID; | |
952 | if (sc->fan_handle) { | |
953 | if (ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, | |
954 | NULL, &data))) | |
955 | sc->sensors[i].flags |= SENSOR_FINVALID; | |
956 | data = -1; | |
957 | } | |
958 | else { | |
959 | ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSPEED, &speed, 2); | |
960 | data = speed; | |
961 | } | |
962 | ||
963 | sc->sensors[i].value = data; | |
964 | ||
965 | ACPI_SERIAL_END(ibm); | |
966 | } | |
967 | ||
1b851d53 | 968 | static void |
10f97674 | 969 | acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context) |
1b851d53 HT |
970 | { |
971 | int event, arg, type; | |
972 | device_t dev = context; | |
10f97674 | 973 | struct acpi_ibm_softc *sc = device_get_softc(dev); |
1b851d53 HT |
974 | |
975 | ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); | |
976 | ||
977 | if (notify != 0x80) | |
978 | device_printf(dev, "Unknown notify\n"); | |
979 | ||
980 | for (;;) { | |
10f97674 | 981 | acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_EVENTS_GET, &event); |
1b851d53 HT |
982 | |
983 | if (event == 0) | |
984 | break; | |
985 | ||
10f97674 | 986 | |
1b851d53 HT |
987 | type = (event >> 12) & 0xf; |
988 | arg = event & 0xfff; | |
989 | switch (type) { | |
990 | case 1: | |
991 | if (!(sc->events_availmask & (1 << (arg - 1)))) { | |
992 | device_printf(dev, "Unknown key %d\n", arg); | |
993 | break; | |
994 | } | |
995 | ||
996 | /* Notify devd(8) */ | |
10f97674 | 997 | acpi_UserNotify("IBM", h, (arg & 0xff)); |
1b851d53 HT |
998 | break; |
999 | default: | |
1000 | break; | |
1001 | } | |
1002 | } | |
1003 | } |