1 /*- 2 * Copyright (c) 2003 Hiroyuki Aizu <aizu@navi.org> 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 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include "opt_acpi.h" 32 #include <sys/param.h> 33 #include <sys/kernel.h> 34 #include <sys/bus.h> 35 36 #include "acpi.h" 37 #include <dev/acpica/acpivar.h> 38 39 /* 40 * Toshiba HCI interface definitions 41 * 42 * HCI is Toshiba's "Hardware Control Interface" which is supposed to 43 * be uniform across all their models. Ideally we would just call 44 * dedicated ACPI methods instead of using this primitive interface. 45 * However, the ACPI methods seem to be incomplete in some areas (for 46 * example they allow setting, but not reading, the LCD brightness 47 * value), so this is still useful. 48 */ 49 50 #define METHOD_HCI "GHCI" 51 #define METHOD_HCI_ENABLE "ENAB" 52 #define METHOD_VIDEO "DSSX" 53 54 /* Operations */ 55 #define HCI_SET 0xFF00 56 #define HCI_GET 0xFE00 57 58 /* Return codes */ 59 #define HCI_SUCCESS 0x0000 60 #define HCI_FAILURE 0x1000 61 #define HCI_NOT_SUPPORTED 0x8000 62 #define HCI_EMPTY 0x8C00 63 64 /* Functions */ 65 #define HCI_REG_LCD_BACKLIGHT 0x0002 66 #define HCI_REG_FAN 0x0004 67 #define HCI_REG_SYSTEM_EVENT 0x0016 68 #define HCI_REG_VIDEO_OUTPUT 0x001C 69 #define HCI_REG_HOTKEY_EVENT 0x001E 70 #define HCI_REG_LCD_BRIGHTNESS 0x002A 71 #define HCI_REG_CPU_SPEED 0x0032 72 73 /* Field definitions */ 74 #define HCI_FAN_SHIFT 7 75 #define HCI_LCD_BRIGHTNESS_BITS 3 76 #define HCI_LCD_BRIGHTNESS_SHIFT (16 - HCI_LCD_BRIGHTNESS_BITS) 77 #define HCI_LCD_BRIGHTNESS_MAX ((1 << HCI_LCD_BRIGHTNESS_BITS) - 1) 78 #define HCI_VIDEO_OUTPUT_FLAG 0x0100 79 #define HCI_VIDEO_OUTPUT_LCD 0x1 80 #define HCI_VIDEO_OUTPUT_CRT 0x2 81 #define HCI_VIDEO_OUTPUT_TV 0x4 82 #define HCI_CPU_SPEED_BITS 3 83 #define HCI_CPU_SPEED_SHIFT (16 - HCI_CPU_SPEED_BITS) 84 #define HCI_CPU_SPEED_MAX ((1 << HCI_CPU_SPEED_BITS) - 1) 85 86 /* Key press/release events. */ 87 #define FN_F1_PRESS 0x013B 88 #define FN_F1_RELEASE 0x01BB 89 #define FN_F2_PRESS 0x013C 90 #define FN_F2_RELEASE 0x01BC 91 #define FN_F3_PRESS 0x013D 92 #define FN_F3_RELEASE 0x01BD 93 #define FN_F4_PRESS 0x013E 94 #define FN_F4_RELEASE 0x01BE 95 #define FN_F5_PRESS 0x013F 96 #define FN_F5_RELEASE 0x01BF 97 #define FN_F6_PRESS 0x0140 98 #define FN_F6_RELEASE 0x01C0 99 #define FN_F7_PRESS 0x0141 100 #define FN_F7_RELEASE 0x01C1 101 #define FN_F8_PRESS 0x0142 102 #define FN_F8_RELEASE 0x01C2 103 #define FN_F9_PRESS 0x0143 104 #define FN_F9_RELEASE 0x01C3 105 #define FN_BS_PRESS 0x010E 106 #define FN_BS_RELEASE 0x018E 107 #define FN_ESC_PRESS 0x0101 108 #define FN_ESC_RELEASE 0x0181 109 #define FN_KNJ_PRESS 0x0129 110 #define FN_KNJ_RELEASE 0x01A9 111 112 /* HCI register definitions. */ 113 #define HCI_WORDS 6 /* Number of registers */ 114 #define HCI_REG_AX 0 /* Operation, then return value */ 115 #define HCI_REG_BX 1 /* Function */ 116 #define HCI_REG_CX 2 /* Argument (in or out) */ 117 #define HCI_REG_DX 3 /* Unused? */ 118 #define HCI_REG_SI 4 /* Unused? */ 119 #define HCI_REG_DI 5 /* Unused? */ 120 121 struct acpi_toshiba_softc { 122 device_t dev; 123 ACPI_HANDLE handle; 124 ACPI_HANDLE video_handle; 125 struct sysctl_ctx_list sysctl_ctx; 126 struct sysctl_oid *sysctl_tree; 127 }; 128 129 /* Prototype for HCI functions for getting/setting a value. */ 130 typedef int hci_fn_t(ACPI_HANDLE, int, UINT32 *); 131 132 static int acpi_toshiba_probe(device_t dev); 133 static int acpi_toshiba_attach(device_t dev); 134 static int acpi_toshiba_detach(device_t dev); 135 static int acpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS); 136 static hci_fn_t hci_force_fan; 137 static hci_fn_t hci_video_output; 138 static hci_fn_t hci_lcd_brightness; 139 static hci_fn_t hci_lcd_backlight; 140 static hci_fn_t hci_cpu_speed; 141 static int hci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg); 142 static void hci_key_action(struct acpi_toshiba_softc *sc, ACPI_HANDLE h, 143 UINT32 key); 144 static void acpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify, 145 void *context); 146 static int acpi_toshiba_video_probe(device_t dev); 147 static int acpi_toshiba_video_attach(device_t dev); 148 149 /* Table of sysctl names and HCI functions to call. */ 150 static struct { 151 char *name; 152 hci_fn_t *handler; 153 } sysctl_table[] = { 154 /* name, handler */ 155 {"force_fan", hci_force_fan}, 156 {"video_output", hci_video_output}, 157 {"lcd_brightness", hci_lcd_brightness}, 158 {"lcd_backlight", hci_lcd_backlight}, 159 {"cpu_speed", hci_cpu_speed}, 160 {NULL, NULL} 161 }; 162 163 static device_method_t acpi_toshiba_methods[] = { 164 DEVMETHOD(device_probe, acpi_toshiba_probe), 165 DEVMETHOD(device_attach, acpi_toshiba_attach), 166 DEVMETHOD(device_detach, acpi_toshiba_detach), 167 168 {0, 0} 169 }; 170 171 static driver_t acpi_toshiba_driver = { 172 "acpi_toshiba", 173 acpi_toshiba_methods, 174 sizeof(struct acpi_toshiba_softc), 175 }; 176 177 static devclass_t acpi_toshiba_devclass; 178 DRIVER_MODULE(acpi_toshiba, acpi, acpi_toshiba_driver, acpi_toshiba_devclass, 179 0, 0); 180 MODULE_DEPEND(acpi_toshiba, acpi, 1, 1, 1); 181 182 static device_method_t acpi_toshiba_video_methods[] = { 183 DEVMETHOD(device_probe, acpi_toshiba_video_probe), 184 DEVMETHOD(device_attach, acpi_toshiba_video_attach), 185 186 {0, 0} 187 }; 188 189 static driver_t acpi_toshiba_video_driver = { 190 "acpi_toshiba_video", 191 acpi_toshiba_video_methods, 192 0, 193 }; 194 195 static devclass_t acpi_toshiba_video_devclass; 196 DRIVER_MODULE(acpi_toshiba_video, acpi, acpi_toshiba_video_driver, 197 acpi_toshiba_video_devclass, 0, 0); 198 MODULE_DEPEND(acpi_toshiba_video, acpi, 1, 1, 1); 199 200 static int enable_fn_keys = 1; 201 TUNABLE_INT("hw.acpi.toshiba.enable_fn_keys", &enable_fn_keys); 202 203 /* 204 * HID Model 205 * ------------------------------------- 206 * TOS6200 Libretto L Series 207 * Dynabook Satellite 2455 208 * Dynabook SS 3500 209 * TOS6207 Dynabook SS2110 Series 210 */ 211 static int 212 acpi_toshiba_probe(device_t dev) 213 { 214 int ret = ENXIO; 215 216 if (!acpi_disabled("toshiba") && 217 acpi_get_type(dev) == ACPI_TYPE_DEVICE && 218 device_get_unit(dev) == 0 && 219 (acpi_MatchHid(dev, "TOS6200") || 220 acpi_MatchHid(dev, "TOS6207"))) { 221 device_set_desc(dev, "Toshiba HCI Extras"); 222 ret = 0; 223 } 224 225 return (ret); 226 } 227 228 static int 229 acpi_toshiba_attach(device_t dev) 230 { 231 struct acpi_toshiba_softc *sc; 232 struct acpi_softc *acpi_sc; 233 ACPI_STATUS status; 234 int i; 235 236 sc = device_get_softc(dev); 237 sc->dev = dev; 238 sc->handle = acpi_get_handle(dev); 239 240 acpi_sc = acpi_device_get_parent_softc(dev); 241 sysctl_ctx_init(&sc->sysctl_ctx); 242 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 243 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, 244 "toshiba", CTLFLAG_RD, 0, ""); 245 246 for (i = 0; sysctl_table[i].name != NULL; i++) { 247 SYSCTL_ADD_PROC(&sc->sysctl_ctx, 248 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 249 sysctl_table[i].name, 250 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, 251 sc, i, acpi_toshiba_sysctl, "I", ""); 252 } 253 254 if (enable_fn_keys != 0) { 255 status = AcpiEvaluateObject(sc->handle, METHOD_HCI_ENABLE, 256 NULL, NULL); 257 if (ACPI_FAILURE(status)) { 258 device_printf(dev, "enable FN keys failed\n"); 259 sysctl_ctx_free(&sc->sysctl_ctx); 260 return (ENXIO); 261 } 262 AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 263 acpi_toshiba_notify, sc); 264 } 265 266 return (0); 267 } 268 269 static int 270 acpi_toshiba_detach(device_t dev) 271 { 272 struct acpi_toshiba_softc *sc; 273 274 sc = device_get_softc(dev); 275 if (enable_fn_keys != 0) { 276 AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 277 acpi_toshiba_notify); 278 } 279 sysctl_ctx_free(&sc->sysctl_ctx); 280 281 return (0); 282 } 283 284 static int 285 acpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS) 286 { 287 struct acpi_toshiba_softc *sc; 288 UINT32 arg; 289 int function, error = 0; 290 hci_fn_t *handler; 291 292 sc = (struct acpi_toshiba_softc *)oidp->oid_arg1; 293 function = oidp->oid_arg2; 294 handler = sysctl_table[function].handler; 295 296 /* Get the current value from the appropriate function. */ 297 error = handler(sc->handle, HCI_GET, &arg); 298 if (error != 0) 299 return (error); 300 301 /* Send the current value to the user and return if no new value. */ 302 error = sysctl_handle_int(oidp, &arg, 0, req); 303 if (error != 0 || req->newptr == NULL) 304 return (error); 305 306 /* Set the new value via the appropriate function. */ 307 error = handler(sc->handle, HCI_SET, &arg); 308 309 return (error); 310 } 311 312 static int 313 hci_force_fan(ACPI_HANDLE h, int op, UINT32 *state) 314 { 315 int ret; 316 317 if (op == HCI_SET) { 318 if (*state < 0 || *state > 1) 319 return (EINVAL); 320 *state <<= HCI_FAN_SHIFT; 321 } 322 ret = hci_call(h, op, HCI_REG_FAN, state); 323 if (ret == 0 && op == HCI_GET) 324 *state >>= HCI_FAN_SHIFT; 325 return (ret); 326 } 327 328 static int 329 hci_video_output(ACPI_HANDLE h, int op, UINT32 *video_output) 330 { 331 int ret; 332 ACPI_STATUS status; 333 334 if (op == HCI_SET) { 335 if (*video_output < 1 || *video_output > 7) 336 return (EINVAL); 337 if (h == NULL) 338 return (ENXIO); 339 *video_output |= HCI_VIDEO_OUTPUT_FLAG; 340 status = acpi_SetInteger(h, METHOD_VIDEO, *video_output); 341 if (ACPI_SUCCESS(status)) 342 ret = 0; 343 else 344 ret = ENXIO; 345 } else { 346 ret = hci_call(h, op, HCI_REG_VIDEO_OUTPUT, video_output); 347 if (ret == 0) 348 *video_output &= 0xff; 349 } 350 351 return (ret); 352 } 353 354 static int 355 hci_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *brightness) 356 { 357 int ret; 358 359 if (op == HCI_SET) { 360 if (*brightness < 0 || *brightness > HCI_LCD_BRIGHTNESS_MAX) 361 return (EINVAL); 362 *brightness <<= HCI_LCD_BRIGHTNESS_SHIFT; 363 } 364 ret = hci_call(h, op, HCI_REG_LCD_BRIGHTNESS, brightness); 365 if (ret == 0 && op == HCI_GET) 366 *brightness >>= HCI_LCD_BRIGHTNESS_SHIFT; 367 return (ret); 368 } 369 370 static int 371 hci_lcd_backlight(ACPI_HANDLE h, int op, UINT32 *backlight) 372 { 373 if (op == HCI_SET) { 374 if (*backlight < 0 || *backlight > 1) 375 return (EINVAL); 376 } 377 return (hci_call(h, op, HCI_REG_LCD_BACKLIGHT, backlight)); 378 } 379 380 static int 381 hci_cpu_speed(ACPI_HANDLE h, int op, UINT32 *speed) 382 { 383 int ret; 384 385 if (op == HCI_SET) { 386 if (*speed < 0 || *speed > HCI_CPU_SPEED_MAX) 387 return (EINVAL); 388 *speed <<= HCI_CPU_SPEED_SHIFT; 389 } 390 ret = hci_call(h, op, HCI_REG_CPU_SPEED, speed); 391 if (ret == 0 && op == HCI_GET) 392 *speed >>= HCI_CPU_SPEED_SHIFT; 393 return (ret); 394 } 395 396 static int 397 hci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg) 398 { 399 ACPI_OBJECT_LIST args; 400 ACPI_BUFFER results; 401 ACPI_OBJECT obj[HCI_WORDS]; 402 ACPI_OBJECT *res; 403 int status, i, ret; 404 405 status = ENXIO; 406 407 for (i = 0; i < HCI_WORDS; i++) { 408 obj[i].Type = ACPI_TYPE_INTEGER; 409 obj[i].Integer.Value = 0; 410 } 411 obj[HCI_REG_AX].Integer.Value = op; 412 obj[HCI_REG_BX].Integer.Value = function; 413 if (op == HCI_SET) 414 obj[HCI_REG_CX].Integer.Value = *arg; 415 416 args.Count = HCI_WORDS; 417 args.Pointer = obj; 418 results.Pointer = NULL; 419 results.Length = ACPI_ALLOCATE_BUFFER; 420 if (ACPI_FAILURE(AcpiEvaluateObject(h, METHOD_HCI, &args, &results))) 421 goto end; 422 res = (ACPI_OBJECT *)results.Pointer; 423 if (!ACPI_PKG_VALID(res, HCI_WORDS)) { 424 printf("toshiba: invalid package!\n"); 425 return (ENXIO); 426 } 427 428 acpi_PkgInt32(res, HCI_REG_AX, &ret); 429 if (ret == HCI_SUCCESS) { 430 if (op == HCI_GET) 431 acpi_PkgInt32(res, HCI_REG_CX, arg); 432 status = 0; 433 } else if (function == HCI_REG_SYSTEM_EVENT && op == HCI_GET && 434 ret == HCI_NOT_SUPPORTED) { 435 /* 436 * Sometimes system events are disabled without us requesting 437 * it. This workaround attempts to re-enable them. 438 */ 439 i = 1; 440 hci_call(h, HCI_SET, HCI_REG_SYSTEM_EVENT, &i); 441 } 442 443 end: 444 if (results.Pointer != NULL) 445 AcpiOsFree(results.Pointer); 446 447 return (status); 448 } 449 450 /* 451 * Perform a few actions based on the keypress. Users can extend this 452 * functionality by reading the keystrokes we send to devd(8). 453 */ 454 static void 455 hci_key_action(struct acpi_toshiba_softc *sc, ACPI_HANDLE h, UINT32 key) 456 { 457 UINT32 arg; 458 459 switch (key) { 460 case FN_F6_RELEASE: 461 /* Decrease LCD brightness. */ 462 hci_lcd_brightness(h, HCI_GET, &arg); 463 if (arg-- == 0) 464 arg = 0; 465 else 466 hci_lcd_brightness(h, HCI_SET, &arg); 467 break; 468 case FN_F7_RELEASE: 469 /* Increase LCD brightness. */ 470 hci_lcd_brightness(h, HCI_GET, &arg); 471 if (arg++ == 7) 472 arg = 7; 473 else 474 hci_lcd_brightness(h, HCI_SET, &arg); 475 break; 476 case FN_F5_RELEASE: 477 /* Cycle through video outputs. */ 478 hci_video_output(h, HCI_GET, &arg); 479 arg = (arg + 1) % 7; 480 hci_video_output(sc->video_handle, HCI_SET, &arg); 481 break; 482 case FN_F8_RELEASE: 483 /* Toggle LCD backlight. */ 484 hci_lcd_backlight(h, HCI_GET, &arg); 485 arg = (arg != 0) ? 0 : 1; 486 hci_lcd_backlight(h, HCI_SET, &arg); 487 break; 488 case FN_ESC_RELEASE: 489 /* Toggle forcing fan on. */ 490 hci_force_fan(h, HCI_GET, &arg); 491 arg = (arg != 0) ? 0 : 1; 492 hci_force_fan(h, HCI_SET, &arg); 493 break; 494 } 495 } 496 497 static void 498 acpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify, void *context) 499 { 500 struct acpi_toshiba_softc *sc; 501 UINT32 key; 502 503 sc = (struct acpi_toshiba_softc *)context; 504 505 if (notify == 0x80) { 506 while (hci_call(h, HCI_GET, HCI_REG_SYSTEM_EVENT, &key) == 0) { 507 hci_key_action(sc, h, key); 508 acpi_UserNotify("TOSHIBA", h, (uint8_t)key); 509 } 510 } else 511 device_printf(sc->dev, "unknown notify: 0x%x\n", notify); 512 } 513 514 /* 515 * Toshiba video pseudo-device to provide the DSSX method. 516 * 517 * HID Model 518 * ------------------------------------- 519 * TOS6201 Libretto L Series 520 */ 521 static int 522 acpi_toshiba_video_probe(device_t dev) 523 { 524 int ret = ENXIO; 525 526 if (!acpi_disabled("toshiba") && 527 acpi_get_type(dev) == ACPI_TYPE_DEVICE && 528 device_get_unit(dev) == 0 && 529 acpi_MatchHid(dev, "TOS6201")) { 530 device_quiet(dev); 531 device_set_desc(dev, "Toshiba Video"); 532 ret = 0; 533 } 534 535 return (ret); 536 } 537 538 static int 539 acpi_toshiba_video_attach(device_t dev) 540 { 541 struct acpi_toshiba_softc *sc; 542 543 sc = devclass_get_softc(acpi_toshiba_devclass, 0); 544 if (sc == NULL) 545 return (ENXIO); 546 sc->video_handle = acpi_get_handle(dev); 547 return (0); 548 } 549