2 * Copyright (c) 2004 Takanori Watanabe
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
32 #include <sys/kernel.h>
34 #include <machine/cpufunc.h>
37 #include <sys/module.h>
38 #include <dev/acpica/acpivar.h>
39 #include <sys/sysctl.h>
40 #include <machine/clock.h>
42 #define _COMPONENT ACPI_OEM
43 ACPI_MODULE_NAME("IBM")
45 #define IBM_RTC_MISCKEY 0x65
46 #define IBM_RTC_BRIGHTNESS 0x6c
47 #define IBM_RTC_MASK_BRI 0x7
48 #define IBM_RTC_MASK_BRKEY 0x40
49 #define IBM_RTC_KEYLIGHT 0x66
50 #define IBM_RTC_MASK_KEYLIGHT 0x10
51 #define IBM_RTC_VOLUME 0x6e
52 #define IBM_RTC_MASK_VOL 0xf
53 #define IBM_RTC_MASK_MUTE 0x40
54 #define IBM_RTC_MASK_VOLKEY 0x80
56 #define IBM_NAME_GET_WIRELESS "GBDC"
57 #define IBM_NAME_SET_WIRELESS "SBDC"
58 #define IBM_NAME_INTERFACE_VERSION "MHKV"
59 #define IBM_NAME_AVAIL_MASK "MHKA"
60 #define IBM_NAME_CURRENT_MASK "MHKN"
61 #define IBM_NAME_MODIFY_MASK "MHKM"
62 #define IBM_NAME_GET_EVENT "MHKP"
63 #define IBM_NAME_ENABLE "MHKC"
66 #define IBM_UCMS_VOLDN 0x0
67 #define IBM_UCMS_VOLUP 0x1
68 #define IBM_UCMS_MUTE 0x2
69 #define IBM_UCMS_BRIUP 0x4
70 #define IBM_UCMS_BRIDN 0x5
71 #define IBM_UCMS_KEYLIGHT 0xe
74 struct acpi_ibm_softc {
75 unsigned int ibm_version;
76 unsigned int ibm_availmask;
77 unsigned int ibm_initialmask;
80 #define IBM_MHKN_AVAIL 1
81 #define IBM_MHKM_AVAIL 2
82 struct sysctl_oid *oid_bluetooth;
83 struct sysctl_oid *oid_wlan;
86 static int acpi_ibm_probe(device_t dev);
87 static int acpi_ibm_attach(device_t dev);
88 static int acpi_ibm_detach(device_t dev);
90 acpi_ibm_notify_handler(ACPI_HANDLE h, UINT32 notify,
92 static int sysctl_acpi_ibm_mask_handler(SYSCTL_HANDLER_ARGS);
93 static int sysctl_acpi_ibm_enable_handler(SYSCTL_HANDLER_ARGS);
94 static int sysctl_acpi_ibm_misckey_handler(SYSCTL_HANDLER_ARGS);
95 static int sysctl_acpi_ibm_volume_handler(SYSCTL_HANDLER_ARGS);
96 static int sysctl_acpi_ibm_mute_handler(SYSCTL_HANDLER_ARGS);
97 static int sysctl_acpi_ibm_brightness_handler(SYSCTL_HANDLER_ARGS);
98 static int sysctl_acpi_ibm_keylight_handler(SYSCTL_HANDLER_ARGS);
99 static int sysctl_acpi_ibm_wireless_handler(SYSCTL_HANDLER_ARGS);
100 static int acpi_ibm_enable_mask(device_t dev, int val);
102 static device_method_t acpi_ibm_methods[] = {
103 /* Device interface */
104 DEVMETHOD(device_probe, acpi_ibm_probe),
105 DEVMETHOD(device_attach, acpi_ibm_attach),
106 DEVMETHOD(device_detach, acpi_ibm_detach),
111 static driver_t acpi_ibm_driver = {
114 sizeof(struct acpi_ibm_softc),
117 static devclass_t acpi_ibm_devclass;
119 DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass,
121 MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1);
122 static char *ibm_id[] = {"IBM0068", NULL};
125 acpi_ibm_probe(device_t dev)
127 struct acpi_ibm_softc *sc;
130 sc = device_get_softc(dev);
132 if (ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_id)) {
133 device_set_desc(dev, "IBM ThinkPad Button");
140 acpi_ibm_call_two_method(device_t dev, char *name, int val1, int val2)
143 ACPI_OBJECT_LIST args = {.Count = 2,.Pointer = arg};
144 arg[0].Type = ACPI_TYPE_INTEGER;
145 arg[0].Integer.Value = val1;
146 arg[1].Type = ACPI_TYPE_INTEGER;
147 arg[1].Integer.Value = val2;
148 return AcpiEvaluateObject(acpi_get_handle(dev), name, &args, NULL);
152 acpi_ibm_attach(device_t dev)
154 struct acpi_ibm_softc *sc;
158 struct sysctl_oid *oid;
159 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
161 sc = device_get_softc(dev);
164 (acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_INTERFACE_VERSION, &sc->ibm_version))) {
167 device_printf(dev, "Version %x\n", sc->ibm_version);
169 (acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_AVAIL_MASK, &sc->ibm_availmask)))
170 sc->ibm_availmask = 0xffffffff;
173 (acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_CURRENT_MASK, &sc->ibm_initialmask)))
174 sc->ibm_initialmask = 0xffffffff;
176 sc->device_flag |= IBM_MHKN_AVAIL;
178 if (ACPI_SUCCESS(status = AcpiGetHandle(acpi_get_handle(dev), IBM_NAME_MODIFY_MASK, &h)))
179 sc->device_flag |= IBM_MHKM_AVAIL;
181 printf("%s\n", AcpiFormatException(status));
183 device_printf(dev, "Available Mask %x\n", sc->ibm_availmask);
184 device_printf(dev, "Initial Mask %x\n", sc->ibm_initialmask);
185 /* Install Specific Handler */
186 status = AcpiInstallNotifyHandler(acpi_get_handle(dev), ACPI_DEVICE_NOTIFY, acpi_ibm_notify_handler, dev);
187 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
188 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
189 OID_AUTO, "key_mask", CTLTYPE_INT | CTLFLAG_RW,
191 sysctl_acpi_ibm_mask_handler, "I", "Hot key mask");
192 SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
193 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
194 OID_AUTO, "version", CTLFLAG_RD,
195 &sc->ibm_version, 0, "Interface version");
196 SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
197 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
198 OID_AUTO, "avail_mask", CTLFLAG_RD,
199 &sc->ibm_availmask, 0, "Available Key mask");
200 if (ACPI_FAILURE(acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_ENABLE, 1)))
203 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
204 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
205 OID_AUTO, "enable", CTLTYPE_INT | CTLFLAG_RW,
207 sysctl_acpi_ibm_enable_handler, "I", "Hot key enable");
208 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
209 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
210 OID_AUTO, "misckey", CTLTYPE_INT | CTLFLAG_RD,
212 sysctl_acpi_ibm_misckey_handler, "I", "Key Status: Poll me");
213 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
214 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
215 OID_AUTO, "brightness", CTLTYPE_INT | CTLFLAG_RD,
217 sysctl_acpi_ibm_brightness_handler, "I", "Brightness");
219 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
220 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
221 OID_AUTO, "volume", CTLTYPE_INT | CTLFLAG_RD,
223 sysctl_acpi_ibm_volume_handler, "I", "Volume");
225 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
226 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
227 OID_AUTO, "mute", CTLTYPE_INT | CTLFLAG_RD,
229 sysctl_acpi_ibm_mute_handler, "I", "Muting");
232 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
233 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
234 OID_AUTO, "keylight", CTLTYPE_INT | CTLFLAG_RD,
236 sysctl_acpi_ibm_keylight_handler, "I", "Key Light");
237 if (ACPI_SUCCESS(acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_GET_WIRELESS, &dummy))) {
238 oid = SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
239 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
240 OID_AUTO, "bluetooth", CTLTYPE_INT | CTLFLAG_RW, dev, 0,
241 sysctl_acpi_ibm_wireless_handler, "I", "Bluetooth Enable");
242 sc->oid_bluetooth = oid;
243 oid = SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
244 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
245 OID_AUTO, "wlan", CTLTYPE_INT | CTLFLAG_RW, dev, 0,
246 sysctl_acpi_ibm_wireless_handler, "I", "WLAN Enable");
251 device_printf(dev, "FAILED\n");
252 AcpiRemoveNotifyHandler(acpi_get_handle(dev), ACPI_DEVICE_NOTIFY, acpi_ibm_notify_handler);
253 return_VALUE(EINVAL);
257 acpi_ibm_detach(device_t dev)
259 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
261 struct acpi_ibm_softc *sc = device_get_softc(dev);
262 acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_ENABLE, 0);
263 acpi_ibm_enable_mask(dev, sc->ibm_initialmask);
265 AcpiRemoveNotifyHandler(acpi_get_handle(dev), ACPI_DEVICE_NOTIFY, acpi_ibm_notify_handler);
270 acpi_ibm_suspend(device_t dev)
272 struct acpi_ibm_softc *sc = device_get_softc(dev);
277 acpi_ibm_resume(device_t dev)
283 acpi_ibm_notify_handler(ACPI_HANDLE h, UINT32 notify,
286 int mhkp , arg, type;
287 device_t dev = context;
288 struct acpi_ibm_softc *sc = device_get_softc(dev);
290 printf("IBM:NOTIFY:%x\n", notify);
291 if (notify != 0x80) {
292 printf("Unknown notify\n");
296 acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_GET_EVENT, &mhkp);
301 printf("notify:%x\n", mhkp);
303 type = (mhkp >> 12) & 0xf;
307 if (!(sc->ibm_availmask & (1 << (arg - 1)))) {
308 printf("Unknown key %d\n", arg);
311 acpi_UserNotify("IBM", h, (arg & 0xff));
320 acpi_ibm_enable_mask(device_t dev, int val)
323 struct acpi_ibm_softc *sc = device_get_softc(dev);
325 if (!(sc->device_flag | IBM_MHKM_AVAIL)) {
328 for (i = 0; i < 32; i++) {
329 acpi_ibm_call_two_method(dev, IBM_NAME_MODIFY_MASK, i + 1, 1);
330 if (!((1 << i) & val))
331 acpi_ibm_call_two_method(dev, IBM_NAME_MODIFY_MASK, i + 1, 0);
337 sysctl_acpi_ibm_mask_handler(SYSCTL_HANDLER_ARGS)
340 int val = 0xffffffff;
343 struct acpi_ibm_softc *sc = device_get_softc(dev);
345 if (sc->device_flag & IBM_MHKN_AVAIL)
346 acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_CURRENT_MASK, &val);
348 error = sysctl_handle_int(oidp, &val, 0, req);
350 if (error || !req->newptr)
353 val &= sc->ibm_availmask;
354 val |= sc->ibm_initialmask;
356 acpi_ibm_enable_mask(dev, val);
362 sysctl_acpi_ibm_misckey_handler(SYSCTL_HANDLER_ARGS)
365 val = rtcin(IBM_RTC_MISCKEY);
366 error = sysctl_handle_int(oidp, &val, 0, req);
367 if (error || !req->newptr)
374 sysctl_acpi_ibm_brightness_handler(SYSCTL_HANDLER_ARGS)
377 val = rtcin(IBM_RTC_BRIGHTNESS);
378 val &= IBM_RTC_MASK_BRI;
379 error = sysctl_handle_int(oidp, &val, 0, req);
380 if (error || !req->newptr)
386 sysctl_acpi_ibm_mute_handler(SYSCTL_HANDLER_ARGS)
389 val = rtcin(IBM_RTC_VOLUME);
390 val = ((val & IBM_RTC_MASK_MUTE) == IBM_RTC_MASK_MUTE);
392 error = sysctl_handle_int(oidp, &val, 0, req);
393 if (error || !req->newptr)
399 sysctl_acpi_ibm_keylight_handler(SYSCTL_HANDLER_ARGS)
402 val = ((rtcin(IBM_RTC_KEYLIGHT) & IBM_RTC_MASK_KEYLIGHT)
403 == IBM_RTC_MASK_KEYLIGHT);
405 error = sysctl_handle_int(oidp, &val, 0, req);
406 if (error || !req->newptr)
412 sysctl_acpi_ibm_volume_handler(SYSCTL_HANDLER_ARGS)
415 val = rtcin(IBM_RTC_VOLUME);
416 val &= IBM_RTC_MASK_VOL;
417 error = sysctl_handle_int(oidp, &val, 0, req);
418 if (error || !req->newptr)
424 sysctl_acpi_ibm_enable_handler(SYSCTL_HANDLER_ARGS)
427 struct acpi_ibm_softc *sc = device_get_softc(dev);
430 error = sysctl_handle_int(oidp, &sc->ibm_enable, 0, req);
432 if (error || !req->newptr)
439 acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_ENABLE, sc->ibm_enable);
445 sysctl_acpi_ibm_wireless_handler(SYSCTL_HANDLER_ARGS)
448 struct acpi_ibm_softc *sc = device_get_softc(dev);
449 int error = 0, val, oldval, mask;
450 if (sc->oid_bluetooth == oidp) {
452 } else if (sc->oid_wlan == oidp) {
455 printf("WARNING: wrong handler invoked\n");
459 acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_GET_WIRELESS, &oldval);
460 val = !((oldval & mask) == 0);
461 error = sysctl_handle_int(oidp, &val, 0, req);
463 if (error || !req->newptr)
468 acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_SET_WIRELESS, oldval);