acpi_ibm.c revision 143002
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 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/dev/acpi_support/acpi_ibm.c 143002 2005-03-02 09:22:34Z obrien $"); 29 30#include "opt_acpi.h" 31#include <sys/param.h> 32#include <sys/kernel.h> 33#include <sys/bus.h> 34#include <machine/cpufunc.h> 35#include "acpi.h" 36#include "acpi_if.h" 37#include <sys/module.h> 38#include <dev/acpica/acpivar.h> 39#include <sys/sysctl.h> 40#include <machine/clock.h> 41 42#define _COMPONENT ACPI_OEM 43ACPI_MODULE_NAME("IBM") 44 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 55 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" 64#if 0 65/* TPX31 Specific? */ 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 72#endif 73 74struct acpi_ibm_softc { 75 unsigned int ibm_version; 76 unsigned int ibm_availmask; 77 unsigned int ibm_initialmask; 78 int ibm_enable; 79 int device_flag; 80#define IBM_MHKN_AVAIL 1 81#define IBM_MHKM_AVAIL 2 82 struct sysctl_oid *oid_bluetooth; 83 struct sysctl_oid *oid_wlan; 84}; 85 86static int acpi_ibm_probe(device_t dev); 87static int acpi_ibm_attach(device_t dev); 88static int acpi_ibm_detach(device_t dev); 89static void 90acpi_ibm_notify_handler(ACPI_HANDLE h, UINT32 notify, 91 void *context); 92static int sysctl_acpi_ibm_mask_handler(SYSCTL_HANDLER_ARGS); 93static int sysctl_acpi_ibm_enable_handler(SYSCTL_HANDLER_ARGS); 94static int sysctl_acpi_ibm_misckey_handler(SYSCTL_HANDLER_ARGS); 95static int sysctl_acpi_ibm_volume_handler(SYSCTL_HANDLER_ARGS); 96static int sysctl_acpi_ibm_mute_handler(SYSCTL_HANDLER_ARGS); 97static int sysctl_acpi_ibm_brightness_handler(SYSCTL_HANDLER_ARGS); 98static int sysctl_acpi_ibm_keylight_handler(SYSCTL_HANDLER_ARGS); 99static int sysctl_acpi_ibm_wireless_handler(SYSCTL_HANDLER_ARGS); 100static int acpi_ibm_enable_mask(device_t dev, int val); 101 102static 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), 107 108 {0, 0} 109}; 110 111static driver_t acpi_ibm_driver = { 112 "acpi_ibm", 113 acpi_ibm_methods, 114 sizeof(struct acpi_ibm_softc), 115}; 116 117static devclass_t acpi_ibm_devclass; 118 119DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass, 120 0, 0); 121MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1); 122static char *ibm_id[] = {"IBM0068", NULL}; 123 124static int 125acpi_ibm_probe(device_t dev) 126{ 127 struct acpi_ibm_softc *sc; 128 int ret = ENXIO; 129 130 sc = device_get_softc(dev); 131 132 if (ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_id)) { 133 device_set_desc(dev, "IBM ThinkPad Button"); 134 ret = 0; 135 } 136 return (ret); 137} 138 139static int 140acpi_ibm_call_two_method(device_t dev, char *name, int val1, int val2) 141{ 142 ACPI_OBJECT arg [2]; 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); 149} 150 151static int 152acpi_ibm_attach(device_t dev) 153{ 154 struct acpi_ibm_softc *sc; 155 ACPI_STATUS status; 156 ACPI_HANDLE h; 157 int dummy; 158 struct sysctl_oid *oid; 159 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 160 161 sc = device_get_softc(dev); 162 sc->device_flag = 0; 163 if (ACPI_FAILURE 164 (acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_INTERFACE_VERSION, &sc->ibm_version))) { 165 sc->ibm_version = 0; 166 } 167 device_printf(dev, "Version %x\n", sc->ibm_version); 168 if (ACPI_FAILURE 169 (acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_AVAIL_MASK, &sc->ibm_availmask))) 170 sc->ibm_availmask = 0xffffffff; 171 172 if (ACPI_FAILURE 173 (acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_CURRENT_MASK, &sc->ibm_initialmask))) 174 sc->ibm_initialmask = 0xffffffff; 175 else 176 sc->device_flag |= IBM_MHKN_AVAIL; 177 178 if (ACPI_SUCCESS(status = AcpiGetHandle(acpi_get_handle(dev), IBM_NAME_MODIFY_MASK, &h))) 179 sc->device_flag |= IBM_MHKM_AVAIL; 180 else 181 printf("%s\n", AcpiFormatException(status)); 182 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, 190 dev, 0, 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))) 201 goto fail; 202 sc->ibm_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, 206 dev, 0, 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, 211 dev, 0, 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, 216 dev, 0, 217 sysctl_acpi_ibm_brightness_handler, "I", "Brightness"); 218 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, 222 dev, 0, 223 sysctl_acpi_ibm_volume_handler, "I", "Volume"); 224 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, 228 dev, 0, 229 sysctl_acpi_ibm_mute_handler, "I", "Muting"); 230 231 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, 235 dev, 0, 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"); 247 sc->oid_wlan = oid; 248 } 249 return_VALUE(0); 250fail: 251 device_printf(dev, "FAILED\n"); 252 AcpiRemoveNotifyHandler(acpi_get_handle(dev), ACPI_DEVICE_NOTIFY, acpi_ibm_notify_handler); 253 return_VALUE(EINVAL); 254} 255 256static int 257acpi_ibm_detach(device_t dev) 258{ 259 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 260 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); 264 265 AcpiRemoveNotifyHandler(acpi_get_handle(dev), ACPI_DEVICE_NOTIFY, acpi_ibm_notify_handler); 266 return_VALUE(0); 267} 268#if 0 269static int 270acpi_ibm_suspend(device_t dev) 271{ 272 struct acpi_ibm_softc *sc = device_get_softc(dev); 273 return_VALUE(0); 274} 275 276static int 277acpi_ibm_resume(device_t dev) 278{ 279 return (0); 280} 281#endif 282static void 283acpi_ibm_notify_handler(ACPI_HANDLE h, UINT32 notify, 284 void *context) 285{ 286 int mhkp , arg, type; 287 device_t dev = context; 288 struct acpi_ibm_softc *sc = device_get_softc(dev); 289 290 printf("IBM:NOTIFY:%x\n", notify); 291 if (notify != 0x80) { 292 printf("Unknown notify\n"); 293 } 294 for (;;) { 295 296 acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_GET_EVENT, &mhkp); 297 298 if (mhkp == 0) { 299 break; 300 } 301 printf("notify:%x\n", mhkp); 302 303 type = (mhkp >> 12) & 0xf; 304 arg = mhkp & 0xfff; 305 switch (type) { 306 case 1: 307 if (!(sc->ibm_availmask & (1 << (arg - 1)))) { 308 printf("Unknown key %d\n", arg); 309 break; 310 } 311 acpi_UserNotify("IBM", h, (arg & 0xff)); 312 break; 313 default: 314 break; 315 } 316 } 317} 318 319static int 320acpi_ibm_enable_mask(device_t dev, int val) 321{ 322 int i; 323 struct acpi_ibm_softc *sc = device_get_softc(dev); 324 325 if (!(sc->device_flag | IBM_MHKM_AVAIL)) { 326 return -1; 327 } 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); 332 } 333 return 0; 334} 335 336static int 337sysctl_acpi_ibm_mask_handler(SYSCTL_HANDLER_ARGS) 338{ 339 device_t dev = arg1; 340 int val = 0xffffffff; 341 int error = 0; 342 343 struct acpi_ibm_softc *sc = device_get_softc(dev); 344 345 if (sc->device_flag & IBM_MHKN_AVAIL) 346 acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_CURRENT_MASK, &val); 347 348 error = sysctl_handle_int(oidp, &val, 0, req); 349 350 if (error || !req->newptr) 351 return error; 352 353 val &= sc->ibm_availmask; 354 val |= sc->ibm_initialmask; 355 356 acpi_ibm_enable_mask(dev, val); 357 358 return 0; 359} 360 361static int 362sysctl_acpi_ibm_misckey_handler(SYSCTL_HANDLER_ARGS) 363{ 364 int val , error; 365 val = rtcin(IBM_RTC_MISCKEY); 366 error = sysctl_handle_int(oidp, &val, 0, req); 367 if (error || !req->newptr) 368 return error; 369 return 0; 370} 371 372 373static int 374sysctl_acpi_ibm_brightness_handler(SYSCTL_HANDLER_ARGS) 375{ 376 int val , error; 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) 381 return error; 382 return 0; 383} 384 385static int 386sysctl_acpi_ibm_mute_handler(SYSCTL_HANDLER_ARGS) 387{ 388 int val , error; 389 val = rtcin(IBM_RTC_VOLUME); 390 val = ((val & IBM_RTC_MASK_MUTE) == IBM_RTC_MASK_MUTE); 391 392 error = sysctl_handle_int(oidp, &val, 0, req); 393 if (error || !req->newptr) 394 return error; 395 return 0; 396} 397 398static int 399sysctl_acpi_ibm_keylight_handler(SYSCTL_HANDLER_ARGS) 400{ 401 int val , error; 402 val = ((rtcin(IBM_RTC_KEYLIGHT) & IBM_RTC_MASK_KEYLIGHT) 403 == IBM_RTC_MASK_KEYLIGHT); 404 405 error = sysctl_handle_int(oidp, &val, 0, req); 406 if (error || !req->newptr) 407 return error; 408 return 0; 409} 410 411static int 412sysctl_acpi_ibm_volume_handler(SYSCTL_HANDLER_ARGS) 413{ 414 int val , error; 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) 419 return error; 420 return 0; 421} 422 423static int 424sysctl_acpi_ibm_enable_handler(SYSCTL_HANDLER_ARGS) 425{ 426 device_t dev = arg1; 427 struct acpi_ibm_softc *sc = device_get_softc(dev); 428 int error = 0; 429 430 error = sysctl_handle_int(oidp, &sc->ibm_enable, 0, req); 431 432 if (error || !req->newptr) 433 return error; 434 435 if (sc->ibm_enable) 436 sc->ibm_enable = 1; 437 else 438 sc->ibm_enable = 0; 439 acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_ENABLE, sc->ibm_enable); 440 441 return 0; 442} 443 444static int 445sysctl_acpi_ibm_wireless_handler(SYSCTL_HANDLER_ARGS) 446{ 447 device_t dev = arg1; 448 struct acpi_ibm_softc *sc = device_get_softc(dev); 449 int error = 0, val, oldval, mask; 450 if (sc->oid_bluetooth == oidp) { 451 mask = 2; 452 } else if (sc->oid_wlan == oidp) { 453 mask = 4; 454 } else { 455 printf("WARNING: wrong handler invoked\n"); 456 return ENOENT; 457 } 458 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); 462 463 if (error || !req->newptr) 464 return error; 465 oldval &= (~mask); 466 if (val) 467 oldval |= mask; 468 acpi_SetInteger(acpi_get_handle(dev), IBM_NAME_SET_WIRELESS, oldval); 469 return 0; 470 471 472} 473