1 /*- 2 * Copyright (c) 2002 Sean Bullington <seanATstalker.org> 3 * 2003-2005 Anish Mistry <amistry@am-productions.biz> 4 * 2004 Mark Santcroos <marks@ripe.net> 5 * All Rights Reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include "opt_acpi.h" 34 #include <sys/param.h> 35 #include <sys/kernel.h> 36 #include <sys/bus.h> 37 #include <sys/module.h> 38 #include <sys/sysctl.h> 39 40 #include <contrib/dev/acpica/acpi.h> 41 #include <dev/acpica/acpivar.h> 42 43 /* Hooks for the ACPI CA debugging infrastructure */ 44 #define _COMPONENT ACPI_OEM 45 ACPI_MODULE_NAME("Fujitsu") 46 47 /* Change and update bits for the buttons */ 48 #define MOUSE_UPDATED_BIT 0x80000000 49 #define VOLUME_MUTE_BIT 0x40000000 50 #define VOLUME_CHANGE_BIT 0x80000000 51 #define BRIGHTNESS_CHANGE_BIT 0x80000000 52 53 /* Values of settings */ 54 #define GENERAL_SETTING_BITS 0x0fffffff 55 #define MOUSE_SETTING_BITS GENERAL_SETTING_BITS 56 #define VOLUME_SETTING_BITS GENERAL_SETTING_BITS 57 #define BRIGHTNESS_SETTING_BITS GENERAL_SETTING_BITS 58 59 /* Possible state changes */ 60 #define VOLUME_CHANGED 1 61 #define BRIGHT_CHANGED 2 62 #define MOUSE_CHANGED 3 63 64 /* sysctl values */ 65 #define FN_MUTE 0 66 #define FN_POINTER_ENABLE 1 67 #define FN_LCD_BRIGHTNESS 2 68 #define FN_VOLUME 3 69 70 /* Methods */ 71 #define METHOD_GBLL 1 72 #define METHOD_GMOU 2 73 #define METHOD_GVOL 3 74 #define METHOD_MUTE 4 75 76 /* Notify event */ 77 #define ACPI_NOTIFY_STATUS_CHANGED 0x80 78 79 /* 80 * Holds a control method name and its associated integer value. 81 * Only used for no-argument control methods which return a value. 82 */ 83 struct int_nameval { 84 char *name; 85 int value; 86 }; 87 88 /* 89 * Driver extension for the FUJITSU ACPI driver. 90 */ 91 struct acpi_fujitsu_softc { 92 device_t dev; 93 ACPI_HANDLE handle; 94 95 /* Control methods */ 96 struct int_nameval _sta, /* unused */ 97 gbll, /* brightness */ 98 ghks, /* unused */ 99 gmou, /* mouse */ 100 gsif, /* unused */ 101 gvol, /* volume */ 102 rbll, /* unused */ 103 rvol; /* unused */ 104 105 /* State variables */ 106 uint8_t bIsMuted; /* Is volume muted */ 107 uint8_t bIntPtrEnabled; /* Is internal ptr enabled */ 108 uint32_t lastValChanged; /* The last value updated */ 109 110 /* sysctl tree */ 111 struct sysctl_ctx_list sysctl_ctx; 112 struct sysctl_oid *sysctl_tree; 113 }; 114 115 /* Driver entry point forward declarations. */ 116 static int acpi_fujitsu_probe(device_t dev); 117 static int acpi_fujitsu_attach(device_t dev); 118 static int acpi_fujitsu_detach(device_t dev); 119 static int acpi_fujitsu_suspend(device_t dev); 120 static int acpi_fujitsu_resume(device_t dev); 121 122 static void acpi_fujitsu_notify_status_changed(void *arg); 123 static void acpi_fujitsu_notify_handler(ACPI_HANDLE h, uint32_t notify, void *context); 124 static int acpi_fujitsu_sysctl(SYSCTL_HANDLER_ARGS); 125 126 /* Utility function declarations */ 127 static uint8_t acpi_fujitsu_update(struct acpi_fujitsu_softc *sc); 128 static uint8_t acpi_fujitsu_init(struct acpi_fujitsu_softc *sc); 129 130 /* Driver/Module specific structure definitions. */ 131 static device_method_t acpi_fujitsu_methods[] = { 132 /* Device interface */ 133 DEVMETHOD(device_probe, acpi_fujitsu_probe), 134 DEVMETHOD(device_attach, acpi_fujitsu_attach), 135 DEVMETHOD(device_detach, acpi_fujitsu_detach), 136 DEVMETHOD(device_suspend, acpi_fujitsu_suspend), 137 DEVMETHOD(device_resume, acpi_fujitsu_resume), 138 {0, 0} 139 }; 140 141 static driver_t acpi_fujitsu_driver = { 142 "acpi_fujitsu", 143 acpi_fujitsu_methods, 144 sizeof(struct acpi_fujitsu_softc), 145 }; 146 147 /* Prototype for function buttons for getting/setting a value. */ 148 static int acpi_fujitsu_method_get(struct acpi_fujitsu_softc *sc, int method); 149 static int acpi_fujitsu_method_set(struct acpi_fujitsu_softc *sc, int method, int value); 150 151 static char *fujitsu_ids[] = { "FUJ02B1", NULL }; 152 153 ACPI_SERIAL_DECL(fujitsu, "Fujitsu Function Buttons"); 154 155 /* sysctl names and function calls */ 156 static struct { 157 char *name; 158 int method; 159 char *description; 160 } sysctl_table[] = { 161 { 162 .name = "mute", 163 .method = METHOD_MUTE, 164 .description = "Speakers/headphones mute status" 165 }, 166 { 167 .name = "pointer_enable", 168 .method = METHOD_GMOU, 169 .description = "Enable and disable the internal pointer" 170 }, 171 { 172 .name = "lcd_brightness", 173 .method = METHOD_GBLL, 174 .description = "Brightness level of the LCD panel" 175 }, 176 { 177 .name = "volume", 178 .method = METHOD_GVOL, 179 .description = "Speakers/headphones volume level" 180 }, 181 182 { NULL, 0, NULL } 183 }; 184 185 static devclass_t acpi_fujitsu_devclass; 186 DRIVER_MODULE(acpi_fujitsu, acpi, acpi_fujitsu_driver, 187 acpi_fujitsu_devclass, 0, 0); 188 MODULE_DEPEND(acpi_fujitsu, acpi, 1, 1, 1); 189 MODULE_VERSION(acpi_fujitsu, 1); 190 191 static int 192 acpi_fujitsu_probe(device_t dev) 193 { 194 195 if (acpi_disabled("fujitsu") || 196 ACPI_ID_PROBE(device_get_parent(dev), dev, fujitsu_ids) == NULL || 197 device_get_unit(dev) != 0) 198 return (ENXIO); 199 200 device_set_desc(dev, "Fujitsu Function Buttons"); 201 202 return (0); 203 } 204 205 static int 206 acpi_fujitsu_attach(device_t dev) 207 { 208 struct acpi_fujitsu_softc *sc; 209 210 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 211 212 sc = device_get_softc(dev); 213 sc->dev = dev; 214 sc->handle = acpi_get_handle(dev); 215 216 /* Install notification handler */ 217 AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 218 acpi_fujitsu_notify_handler, sc); 219 220 /* Snag our default values for the buttons / button states. */ 221 ACPI_SERIAL_BEGIN(fujitsu); 222 if (!acpi_fujitsu_init(sc)) 223 device_printf(dev, "Couldn't initialize button states!\n"); 224 ACPI_SERIAL_END(fujitsu); 225 226 return (0); 227 } 228 229 /* 230 * Called when the system is being suspended, simply 231 * set an event to be signalled when we wake up. 232 */ 233 static int 234 acpi_fujitsu_suspend(device_t dev) 235 { 236 237 return (0); 238 } 239 240 static int 241 acpi_fujitsu_resume(device_t dev) 242 { 243 struct acpi_fujitsu_softc *sc; 244 ACPI_STATUS status; 245 246 sc = device_get_softc(dev); 247 248 /* 249 * The pointer needs to be re-enabled for 250 * some revisions of the P series (2120). 251 */ 252 ACPI_SERIAL_BEGIN(fujitsu); 253 254 status = acpi_SetInteger(sc->handle, "SMOU", 1); 255 if (ACPI_FAILURE(status)) 256 device_printf(sc->dev, "Couldn't enable pointer\n"); 257 258 ACPI_SERIAL_END(fujitsu); 259 260 return (0); 261 } 262 263 static void 264 acpi_fujitsu_notify_status_changed(void *arg) 265 { 266 struct acpi_fujitsu_softc *sc; 267 268 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 269 270 sc = (struct acpi_fujitsu_softc *)arg; 271 272 /* 273 * Since our notify function is called, we know something has 274 * happened. So the only reason for acpi_fujitsu_update to fail 275 * is if we can't find what has changed or an error occurs. 276 */ 277 ACPI_SERIAL_BEGIN(fujitsu); 278 acpi_fujitsu_update(sc); 279 ACPI_SERIAL_END(fujitsu); 280 } 281 282 283 static void 284 acpi_fujitsu_notify_handler(ACPI_HANDLE h, uint32_t notify, void *context) 285 { 286 struct acpi_fujitsu_softc *sc; 287 288 ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); 289 290 sc = (struct acpi_fujitsu_softc *)context; 291 292 switch (notify) { 293 case ACPI_NOTIFY_STATUS_CHANGED: 294 AcpiOsQueueForExecution(OSD_PRIORITY_LO, 295 acpi_fujitsu_notify_status_changed, sc); 296 break; 297 default: 298 /* unknown notification value */ 299 break; 300 } 301 } 302 303 static int 304 acpi_fujitsu_detach(device_t dev) 305 { 306 struct acpi_fujitsu_softc *sc; 307 308 sc = device_get_softc(dev); 309 AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 310 acpi_fujitsu_notify_handler); 311 312 sysctl_ctx_free(&sc->sysctl_ctx); 313 314 return (0); 315 } 316 317 /* 318 * Initializes the names of the ACPI control methods and grabs 319 * the current state of all of the ACPI buttons into the softc. 320 */ 321 static uint8_t 322 acpi_fujitsu_init(struct acpi_fujitsu_softc *sc) 323 { 324 struct acpi_softc *acpi_sc; 325 int i; 326 327 ACPI_SERIAL_ASSERT(fujitsu); 328 329 /* Setup all of the names for each control method */ 330 sc->_sta.name = "_STA"; 331 sc->gbll.name = "GBLL"; 332 sc->ghks.name = "GHKS"; 333 sc->gmou.name = "GMOU"; 334 sc->gsif.name = "GSIF"; 335 sc->gvol.name = "GVOL"; 336 sc->rbll.name = "RBLL"; 337 sc->rvol.name = "RVOL"; 338 339 /* Build the sysctl tree */ 340 acpi_sc = acpi_device_get_parent_softc(sc->dev); 341 sysctl_ctx_init(&sc->sysctl_ctx); 342 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 343 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), 344 OID_AUTO, "fujitsu", CTLFLAG_RD, 0, ""); 345 346 for (i = 0; sysctl_table[i].name != NULL; i++) { 347 SYSCTL_ADD_PROC(&sc->sysctl_ctx, 348 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 349 sysctl_table[i].name, 350 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, 351 sc, i, acpi_fujitsu_sysctl, "I", 352 sysctl_table[i].description); 353 } 354 355 /* Set the buttons to their initial states */ 356 if (!acpi_fujitsu_update(sc)) { 357 device_printf(sc->dev, "Couldn't init button states\n"); 358 return (FALSE); 359 } 360 361 return (TRUE); 362 } 363 364 static int 365 acpi_fujitsu_sysctl(SYSCTL_HANDLER_ARGS) 366 { 367 struct acpi_fujitsu_softc *sc; 368 int method; 369 int arg; 370 int function_num, error = 0; 371 372 sc = (struct acpi_fujitsu_softc *)oidp->oid_arg1; 373 function_num = oidp->oid_arg2; 374 method = sysctl_table[function_num].method; 375 376 ACPI_SERIAL_BEGIN(fujitsu); 377 378 /* Get the current value */ 379 arg = acpi_fujitsu_method_get(sc, method); 380 error = sysctl_handle_int(oidp, &arg, 0, req); 381 382 if (error != 0 || req->newptr == NULL) 383 goto out; 384 385 /* Update the value */ 386 error = acpi_fujitsu_method_set(sc, method, arg); 387 388 out: 389 ACPI_SERIAL_END(fujitsu); 390 return (error); 391 } 392 393 static int 394 acpi_fujitsu_method_get(struct acpi_fujitsu_softc *sc, int method) 395 { 396 struct int_nameval nv; 397 ACPI_STATUS status; 398 399 ACPI_SERIAL_ASSERT(fujitsu); 400 401 switch (method) { 402 case METHOD_GBLL: 403 nv = sc->gbll; 404 break; 405 case METHOD_GMOU: 406 nv = sc->gmou; 407 break; 408 case METHOD_GVOL: 409 case METHOD_MUTE: 410 nv = sc->gvol; 411 break; 412 default: 413 return (FALSE); 414 } 415 416 status = acpi_GetInteger(sc->handle, nv.name, &nv.value); 417 if (ACPI_FAILURE(status)) { 418 device_printf(sc->dev, "Couldn't query method\n"); 419 return (FALSE); 420 } 421 422 if (method == METHOD_MUTE) { 423 sc->bIsMuted = (uint8_t)((nv.value & VOLUME_MUTE_BIT) != 0); 424 return (sc->bIsMuted); 425 } 426 427 nv.value &= GENERAL_SETTING_BITS; 428 return (nv.value); 429 } 430 431 static int 432 acpi_fujitsu_method_set(struct acpi_fujitsu_softc *sc, int method, int value) 433 { 434 struct int_nameval nv; 435 ACPI_STATUS status; 436 char *control; 437 int changed; 438 439 ACPI_SERIAL_ASSERT(fujitsu); 440 441 switch (method) { 442 case METHOD_GBLL: 443 changed = BRIGHT_CHANGED; 444 control = "SBLL"; 445 nv = sc->gbll; 446 break; 447 case METHOD_GMOU: 448 changed = MOUSE_CHANGED; 449 control = "SMOU"; 450 nv = sc->gmou; 451 break; 452 case METHOD_GVOL: 453 case METHOD_MUTE: 454 changed = VOLUME_CHANGED; 455 control = "SVOL"; 456 nv = sc->gvol; 457 break; 458 default: 459 return (EINVAL); 460 } 461 462 if (method == METHOD_MUTE) { 463 if (value == 1) 464 value = nv.value | VOLUME_MUTE_BIT; 465 else if (value == 0) 466 value = nv.value & ~VOLUME_MUTE_BIT; 467 else 468 return (EINVAL); 469 } 470 471 status = acpi_SetInteger(sc->handle, control, value); 472 if (ACPI_FAILURE(status)) { 473 device_printf(sc->dev, "Couldn't update %s\n", control); 474 return (EINVAL); 475 } 476 477 sc->lastValChanged = changed; 478 return (0); 479 } 480 481 /* 482 * Query each of the ACPI control methods that contain information we're 483 * interested in. We check the return values from the control methods and 484 * adjust any state variables if they should be adjusted. 485 */ 486 static uint8_t 487 acpi_fujitsu_update(struct acpi_fujitsu_softc *sc) 488 { 489 struct acpi_softc *acpi_sc; 490 491 acpi_sc = acpi_device_get_parent_softc(sc->dev); 492 493 ACPI_SERIAL_ASSERT(fujitsu); 494 495 /* System Volume Level */ 496 if (ACPI_FAILURE(acpi_GetInteger(sc->handle, 497 sc->gvol.name, &(sc->gvol.value)))) { 498 device_printf(sc->dev, "Couldn't query volume level\n"); 499 return (FALSE); 500 } 501 502 if (sc->gvol.value & VOLUME_CHANGE_BIT) { 503 sc->bIsMuted = 504 (uint8_t)((sc->gvol.value & VOLUME_MUTE_BIT) != 0); 505 506 /* Clear the modification bit */ 507 sc->gvol.value &= VOLUME_SETTING_BITS; 508 509 if (sc->bIsMuted) { 510 acpi_UserNotify("FUJITSU", sc->handle, FN_MUTE); 511 ACPI_VPRINT(sc->dev, acpi_sc, "Volume is now mute\n"); 512 } else 513 ACPI_VPRINT(sc->dev, acpi_sc, "Volume is now %d\n", 514 sc->gvol.value); 515 516 acpi_UserNotify("FUJITSU", sc->handle, FN_VOLUME); 517 518 sc->lastValChanged = VOLUME_CHANGED; 519 } 520 521 /* Internal mouse pointer (eraserhead) */ 522 if (ACPI_FAILURE(acpi_GetInteger(sc->handle, 523 sc->gmou.name, &(sc->gmou.value)))) { 524 device_printf(sc->dev, "Couldn't query pointer state\n"); 525 return (FALSE); 526 } 527 528 if (sc->gmou.value & MOUSE_UPDATED_BIT) { 529 sc->bIntPtrEnabled = (uint8_t)(sc->gmou.value & 0x1); 530 531 /* Clear the modification bit */ 532 sc->gmou.value &= MOUSE_SETTING_BITS; 533 534 acpi_UserNotify("FUJITSU", sc->handle, FN_POINTER_ENABLE); 535 536 ACPI_VPRINT(sc->dev, acpi_sc, "Internal pointer is now %s\n", 537 (sc->bIntPtrEnabled) ? "enabled" : "disabled"); 538 539 sc->lastValChanged = MOUSE_CHANGED; 540 } 541 542 /* Screen Brightness Level */ 543 if (ACPI_FAILURE(acpi_GetInteger(sc->handle, 544 sc->gbll.name, &(sc->gbll.value)))) { 545 device_printf(sc->dev, "Couldn't query brightness level\n"); 546 return (FALSE); 547 } 548 549 if (sc->gbll.value & BRIGHTNESS_CHANGE_BIT) { 550 /* No state to record here. */ 551 552 /* Clear the modification bit */ 553 sc->gbll.value &= BRIGHTNESS_SETTING_BITS; 554 555 acpi_UserNotify("FUJITSU", sc->handle, FN_LCD_BRIGHTNESS); 556 557 ACPI_VPRINT(sc->dev, acpi_sc, "Brightness level is now %d\n", 558 sc->gbll.value); 559 560 sc->lastValChanged = BRIGHT_CHANGED; 561 } 562 563 return (TRUE); 564 } 565