acpi_ibm.c revision 138825
1/*- 2 * Copyright (c) 2004 Takanori Watanabe 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/sys/dev/acpi_support/acpi_ibm.c 138825 2004-12-13 23:31:46Z njl $ 27 */ 28 29#include "opt_acpi.h" 30#include <sys/param.h> 31#include <sys/kernel.h> 32#include <sys/bus.h> 33#include <machine/cpufunc.h> 34#include "acpi.h" 35#include "acpi_if.h" 36#include <sys/module.h> 37#include <dev/acpica/acpivar.h> 38#include <sys/sysctl.h> 39#include <machine/clock.h> 40 41#define _COMPONENT ACPI_OEM 42ACPI_MODULE_NAME("IBM") 43 44#define IBM_RTC_MISCKEY 0x65 45#define IBM_RTC_BRIGHTNESS 0x6c 46#define IBM_RTC_MASK_BRI 0x7 47#define IBM_RTC_MASK_BRKEY 0x40 48#define IBM_RTC_KEYLIGHT 0x66 49#define IBM_RTC_MASK_KEYLIGHT 0x10 50#define IBM_RTC_VOLUME 0x6e 51#define IBM_RTC_MASK_VOL 0xf 52#define IBM_RTC_MASK_MUTE 0x40 53#define IBM_RTC_MASK_VOLKEY 0x80 54 55#define IBM_NAME_GET_WIRELESS "GBDC" 56#define IBM_NAME_SET_WIRELESS "SBDC" 57#define IBM_NAME_INTERFACE_VERSION "MHKV" 58#define IBM_NAME_AVAIL_MASK "MHKA" 59#define IBM_NAME_CURRENT_MASK "MHKN" 60#define IBM_NAME_MODIFY_MASK "MHKM" 61#define IBM_NAME_GET_EVENT "MHKP" 62#define IBM_NAME_ENABLE "MHKC" 63#if 0 64/* TPX31 Specific? */ 65#define IBM_UCMS_VOLDN 0x0 66#define IBM_UCMS_VOLUP 0x1 67#define IBM_UCMS_MUTE 0x2 68#define IBM_UCMS_BRIUP 0x4 69#define IBM_UCMS_BRIDN 0x5 70#define IBM_UCMS_KEYLIGHT 0xe 71#endif 72 73struct acpi_ibm_softc { 74 unsigned int ibm_version; 75 unsigned int ibm_availmask; 76 unsigned int ibm_initialmask; 77 int ibm_enable; 78 int device_flag; 79#define IBM_MHKN_AVAIL 1 80#define IBM_MHKM_AVAIL 2 81 struct sysctl_oid *oid_bluetooth; 82 struct sysctl_oid *oid_wlan; 83}; 84 85static int acpi_ibm_probe(device_t dev); 86static int acpi_ibm_attach(device_t dev); 87static int acpi_ibm_detach(device_t dev); 88static void 89acpi_ibm_notify_handler(ACPI_HANDLE h, UINT32 notify, 90 void *context); 91static int sysctl_acpi_ibm_mask_handler(SYSCTL_HANDLER_ARGS); 92static int sysctl_acpi_ibm_enable_handler(SYSCTL_HANDLER_ARGS); 93static int sysctl_acpi_ibm_misckey_handler(SYSCTL_HANDLER_ARGS); 94static int sysctl_acpi_ibm_volume_handler(SYSCTL_HANDLER_ARGS); 95static int sysctl_acpi_ibm_mute_handler(SYSCTL_HANDLER_ARGS); 96static int sysctl_acpi_ibm_brightness_handler(SYSCTL_HANDLER_ARGS); 97static int sysctl_acpi_ibm_keylight_handler(SYSCTL_HANDLER_ARGS); 98static int sysctl_acpi_ibm_wireless_handler(SYSCTL_HANDLER_ARGS); 99static int acpi_ibm_enable_mask(device_t dev, int val); 100 101static device_method_t acpi_ibm_methods[] = { 102 /* Device interface */ 103 DEVMETHOD(device_probe, acpi_ibm_probe), 104 DEVMETHOD(device_attach, acpi_ibm_attach), 105 DEVMETHOD(device_detach, acpi_ibm_detach), 106 107 {0, 0} 108}; 109 110static driver_t acpi_ibm_driver = { 111 "acpi_ibm", 112 acpi_ibm_methods, 113 sizeof(struct acpi_ibm_softc), 114}; 115 116static devclass_t acpi_ibm_devclass; 117 118DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass, 119 0, 0); 120MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1); 121static char *ibm_id[] = {"IBM0068", NULL}; 122 123static int 124acpi_ibm_probe(device_t dev) 125{ 126 struct acpi_ibm_softc *sc; 127 int ret = ENXIO; 128 129 sc = device_get_softc(dev); 130 131 if (ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_id)) { 132 device_set_desc(dev, "IBM ThinkPad Button"); 133 ret = 0; 134 } 135 return (ret); 136} 137 138static int 139acpi_ibm_call_two_method(device_t dev, char *name, int val1, int val2) 140{ 141 ACPI_OBJECT arg [2]; 142 ACPI_OBJECT_LIST args = {.Count = 2,.Pointer = arg}; 143 arg[0].Type = ACPI_TYPE_INTEGER; 144 arg[0].Integer.Value = val1; 145 arg[1].Type = ACPI_TYPE_INTEGER; 146 arg[1].Integer.Value = val2; 147 return AcpiEvaluateObject(acpi_get_handle(dev), name, &args, NULL); 148} 149 150static int 151acpi_ibm_attach(device_t dev) 152{ 153 struct acpi_ibm_softc *sc; 154 ACPI_STATUS status; 155 ACPI_HANDLE h; 156 int dummy; 157 struct sysctl_oid *oid; 158 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 159 160 sc = device_get_softc(dev); 161 sc->device_flag = 0; 162 if (ACPI_FAILURE 163 (acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_INTERFACE_VERSION, &sc->ibm_version))) { 164 sc->ibm_version = 0; 165 } 166 device_printf(dev, "Version %x\n", sc->ibm_version); 167 if (ACPI_FAILURE 168 (acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_AVAIL_MASK, &sc->ibm_availmask))) 169 sc->ibm_availmask = 0xffffffff; 170 171 if (ACPI_FAILURE 172 (acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_CURRENT_MASK, &sc->ibm_initialmask))) 173 sc->ibm_initialmask = 0xffffffff; 174 else 175 sc->device_flag |= IBM_MHKN_AVAIL; 176 177 if (ACPI_SUCCESS(status = AcpiGetHandle(acpi_get_handle(dev), IBM_NAME_MODIFY_MASK, &h))) 178 sc->device_flag |= IBM_MHKM_AVAIL; 179 else 180 printf("%s\n", AcpiFormatException(status)); 181 182 device_printf(dev, "Available Mask %x\n", sc->ibm_availmask); 183 device_printf(dev, "Initial Mask %x\n", sc->ibm_initialmask); 184 /* Install Specific Handler */ 185 status = AcpiInstallNotifyHandler(acpi_get_handle(dev), ACPI_DEVICE_NOTIFY, acpi_ibm_notify_handler, dev); 186 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 187 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 188 OID_AUTO, "key_mask", CTLTYPE_INT | CTLFLAG_RW, 189 dev, 0, 190 sysctl_acpi_ibm_mask_handler, "I", "Hot key mask"); 191 SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 192 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 193 OID_AUTO, "version", CTLFLAG_RD, 194 &sc->ibm_version, 0, "Interface version"); 195 SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 196 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 197 OID_AUTO, "avail_mask", CTLFLAG_RD, 198 &sc->ibm_availmask, 0, "Available Key mask"); 199 if (ACPI_FAILURE(acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_ENABLE, 1))) 200 goto fail; 201 sc->ibm_enable = 1; 202 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 203 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 204 OID_AUTO, "enable", CTLTYPE_INT | CTLFLAG_RW, 205 dev, 0, 206 sysctl_acpi_ibm_enable_handler, "I", "Hot key enable"); 207 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 208 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 209 OID_AUTO, "misckey", CTLTYPE_INT | CTLFLAG_RD, 210 dev, 0, 211 sysctl_acpi_ibm_misckey_handler, "I", "Key Status: Poll me"); 212 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 213 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 214 OID_AUTO, "brightness", CTLTYPE_INT | CTLFLAG_RD, 215 dev, 0, 216 sysctl_acpi_ibm_brightness_handler, "I", "Brightness"); 217 218 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 219 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 220 OID_AUTO, "volume", CTLTYPE_INT | CTLFLAG_RD, 221 dev, 0, 222 sysctl_acpi_ibm_volume_handler, "I", "Volume"); 223 224 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 225 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 226 OID_AUTO, "mute", CTLTYPE_INT | CTLFLAG_RD, 227 dev, 0, 228 sysctl_acpi_ibm_mute_handler, "I", "Muting"); 229 230 231 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 232 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 233 OID_AUTO, "keylight", CTLTYPE_INT | CTLFLAG_RD, 234 dev, 0, 235 sysctl_acpi_ibm_keylight_handler, "I", "Key Light"); 236 if (ACPI_SUCCESS(acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_GET_WIRELESS, &dummy))) { 237 oid = SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 238 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 239 OID_AUTO, "bluetooth", CTLTYPE_INT | CTLFLAG_RW, dev, 0, 240 sysctl_acpi_ibm_wireless_handler, "I", "Bluetooth Enable"); 241 sc->oid_bluetooth = oid; 242 oid = SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 243 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 244 OID_AUTO, "wlan", CTLTYPE_INT | CTLFLAG_RW, dev, 0, 245 sysctl_acpi_ibm_wireless_handler, "I", "WLAN Enable"); 246 sc->oid_wlan = oid; 247 } 248 return_VALUE(0); 249fail: 250 device_printf(dev, "FAILED\n"); 251 AcpiRemoveNotifyHandler(acpi_get_handle(dev), ACPI_DEVICE_NOTIFY, acpi_ibm_notify_handler); 252 return_VALUE(EINVAL); 253} 254 255static int 256acpi_ibm_detach(device_t dev) 257{ 258 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 259 260 struct acpi_ibm_softc *sc = device_get_softc(dev); 261 acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_ENABLE, 0); 262 acpi_ibm_enable_mask(dev, sc->ibm_initialmask); 263 264 AcpiRemoveNotifyHandler(acpi_get_handle(dev), ACPI_DEVICE_NOTIFY, acpi_ibm_notify_handler); 265 return_VALUE(0); 266} 267#if 0 268static int 269acpi_ibm_suspend(device_t dev) 270{ 271 struct acpi_ibm_softc *sc = device_get_softc(dev); 272 return_VALUE(0); 273} 274 275static int 276acpi_ibm_resume(device_t dev) 277{ 278 return (0); 279} 280#endif 281static void 282acpi_ibm_notify_handler(ACPI_HANDLE h, UINT32 notify, 283 void *context) 284{ 285 int mhkp , arg, type; 286 device_t dev = context; 287 struct acpi_ibm_softc *sc = device_get_softc(dev); 288 289 printf("IBM:NOTIFY:%x\n", notify); 290 if (notify != 0x80) { 291 printf("Unknown notify\n"); 292 } 293 for (;;) { 294 295 acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_GET_EVENT, &mhkp); 296 297 if (mhkp == 0) { 298 break; 299 } 300 printf("notify:%x\n", mhkp); 301 302 type = (mhkp >> 12) & 0xf; 303 arg = mhkp & 0xfff; 304 switch (type) { 305 case 1: 306 if (!(sc->ibm_availmask & (1 << (arg - 1)))) { 307 printf("Unknown key %d\n", arg); 308 break; 309 } 310 acpi_UserNotify("IBM", h, (arg & 0xff)); 311 break; 312 default: 313 break; 314 } 315 } 316} 317 318static int 319acpi_ibm_enable_mask(device_t dev, int val) 320{ 321 int i; 322 struct acpi_ibm_softc *sc = device_get_softc(dev); 323 324 if (!(sc->device_flag | IBM_MHKM_AVAIL)) { 325 return -1; 326 } 327 for (i = 0; i < 32; i++) { 328 acpi_ibm_call_two_method(dev, IBM_NAME_MODIFY_MASK, i + 1, 1); 329 if (!((1 << i) & val)) 330 acpi_ibm_call_two_method(dev, IBM_NAME_MODIFY_MASK, i + 1, 0); 331 } 332 return 0; 333} 334 335static int 336sysctl_acpi_ibm_mask_handler(SYSCTL_HANDLER_ARGS) 337{ 338 device_t dev = arg1; 339 int val = 0xffffffff; 340 int error = 0; 341 342 struct acpi_ibm_softc *sc = device_get_softc(dev); 343 344 if (sc->device_flag & IBM_MHKN_AVAIL) 345 acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_CURRENT_MASK, &val); 346 347 error = sysctl_handle_int(oidp, &val, 0, req); 348 349 if (error || !req->newptr) 350 return error; 351 352 val &= sc->ibm_availmask; 353 val |= sc->ibm_initialmask; 354 355 acpi_ibm_enable_mask(dev, val); 356 357 return 0; 358} 359 360static int 361sysctl_acpi_ibm_misckey_handler(SYSCTL_HANDLER_ARGS) 362{ 363 int val , error; 364 val = rtcin(IBM_RTC_MISCKEY); 365 error = sysctl_handle_int(oidp, &val, 0, req); 366 if (error || !req->newptr) 367 return error; 368 return 0; 369} 370 371 372static int 373sysctl_acpi_ibm_brightness_handler(SYSCTL_HANDLER_ARGS) 374{ 375 int val , error; 376 val = rtcin(IBM_RTC_BRIGHTNESS); 377 val &= IBM_RTC_MASK_BRI; 378 error = sysctl_handle_int(oidp, &val, 0, req); 379 if (error || !req->newptr) 380 return error; 381 return 0; 382} 383 384static int 385sysctl_acpi_ibm_mute_handler(SYSCTL_HANDLER_ARGS) 386{ 387 int val , error; 388 val = rtcin(IBM_RTC_VOLUME); 389 val = ((val & IBM_RTC_MASK_MUTE) == IBM_RTC_MASK_MUTE); 390 391 error = sysctl_handle_int(oidp, &val, 0, req); 392 if (error || !req->newptr) 393 return error; 394 return 0; 395} 396 397static int 398sysctl_acpi_ibm_keylight_handler(SYSCTL_HANDLER_ARGS) 399{ 400 int val , error; 401 val = ((rtcin(IBM_RTC_KEYLIGHT) & IBM_RTC_MASK_KEYLIGHT) 402 == IBM_RTC_MASK_KEYLIGHT); 403 404 error = sysctl_handle_int(oidp, &val, 0, req); 405 if (error || !req->newptr) 406 return error; 407 return 0; 408} 409 410static int 411sysctl_acpi_ibm_volume_handler(SYSCTL_HANDLER_ARGS) 412{ 413 int val , error; 414 val = rtcin(IBM_RTC_VOLUME); 415 val &= IBM_RTC_MASK_VOL; 416 error = sysctl_handle_int(oidp, &val, 0, req); 417 if (error || !req->newptr) 418 return error; 419 return 0; 420} 421 422static int 423sysctl_acpi_ibm_enable_handler(SYSCTL_HANDLER_ARGS) 424{ 425 device_t dev = arg1; 426 struct acpi_ibm_softc *sc = device_get_softc(dev); 427 int error = 0; 428 429 error = sysctl_handle_int(oidp, &sc->ibm_enable, 0, req); 430 431 if (error || !req->newptr) 432 return error; 433 434 if (sc->ibm_enable) 435 sc->ibm_enable = 1; 436 else 437 sc->ibm_enable = 0; 438 acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_ENABLE, sc->ibm_enable); 439 440 return 0; 441} 442 443static int 444sysctl_acpi_ibm_wireless_handler(SYSCTL_HANDLER_ARGS) 445{ 446 device_t dev = arg1; 447 struct acpi_ibm_softc *sc = device_get_softc(dev); 448 int error = 0, val, oldval, mask; 449 if (sc->oid_bluetooth == oidp) { 450 mask = 2; 451 } else if (sc->oid_wlan == oidp) { 452 mask = 4; 453 } else { 454 printf("WARNING: wrong handler invoked\n"); 455 return ENOENT; 456 } 457 458 acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_GET_WIRELESS, &oldval); 459 val = !((oldval & mask) == 0); 460 error = sysctl_handle_int(oidp, &val, 0, req); 461 462 if (error || !req->newptr) 463 return error; 464 oldval &= (~mask); 465 if (val) 466 oldval |= mask; 467 acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_SET_WIRELESS, oldval); 468 return 0; 469 470 471} 472