1124386Snjl/*- 2124386Snjl * Copyright (c) 2003 Hiroyuki Aizu <aizu@navi.org> 3124386Snjl * All rights reserved. 4124386Snjl * 5124386Snjl * Redistribution and use in source and binary forms, with or without 6124386Snjl * modification, are permitted provided that the following conditions 7124386Snjl * are met: 8124386Snjl * 1. Redistributions of source code must retain the above copyright 9124386Snjl * notice, this list of conditions and the following disclaimer. 10124386Snjl * 2. Redistributions in binary form must reproduce the above copyright 11124386Snjl * notice, this list of conditions and the following disclaimer in the 12124386Snjl * documentation and/or other materials provided with the distribution. 13124386Snjl * 14124386Snjl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15124386Snjl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16124386Snjl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17124386Snjl * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18124386Snjl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19124386Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20124386Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21124386Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22124386Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23124386Snjl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24124386Snjl * SUCH DAMAGE. 25124386Snjl * 26124386Snjl */ 27124386Snjl 28124386Snjl#include <sys/cdefs.h> 29124386Snjl__FBSDID("$FreeBSD: releng/10.3/sys/dev/acpi_support/acpi_toshiba.c 249816 2013-04-23 18:30:33Z jkim $"); 30124386Snjl 31124386Snjl#include "opt_acpi.h" 32124386Snjl#include <sys/param.h> 33124386Snjl#include <sys/kernel.h> 34129882Sphk#include <sys/module.h> 35124386Snjl#include <sys/bus.h> 36124386Snjl 37193530Sjkim#include <contrib/dev/acpica/include/acpi.h> 38193530Sjkim 39124386Snjl#include <dev/acpica/acpivar.h> 40124386Snjl 41138825Snjl#define _COMPONENT ACPI_OEM 42138825SnjlACPI_MODULE_NAME("Toshiba") 43138825Snjl 44124386Snjl/* 45124386Snjl * Toshiba HCI interface definitions 46124386Snjl * 47124386Snjl * HCI is Toshiba's "Hardware Control Interface" which is supposed to 48124386Snjl * be uniform across all their models. Ideally we would just call 49124386Snjl * dedicated ACPI methods instead of using this primitive interface. 50124386Snjl * However, the ACPI methods seem to be incomplete in some areas (for 51124386Snjl * example they allow setting, but not reading, the LCD brightness 52124386Snjl * value), so this is still useful. 53124386Snjl */ 54124386Snjl 55124386Snjl#define METHOD_HCI "GHCI" 56124386Snjl#define METHOD_HCI_ENABLE "ENAB" 57128207Snjl#define METHOD_VIDEO "DSSX" 58124386Snjl 59124386Snjl/* Operations */ 60124386Snjl#define HCI_SET 0xFF00 61124386Snjl#define HCI_GET 0xFE00 62124386Snjl 63124386Snjl/* Return codes */ 64124386Snjl#define HCI_SUCCESS 0x0000 65124386Snjl#define HCI_FAILURE 0x1000 66124386Snjl#define HCI_NOT_SUPPORTED 0x8000 67124386Snjl#define HCI_EMPTY 0x8C00 68124386Snjl 69124386Snjl/* Functions */ 70124386Snjl#define HCI_REG_LCD_BACKLIGHT 0x0002 71124386Snjl#define HCI_REG_FAN 0x0004 72124386Snjl#define HCI_REG_SYSTEM_EVENT 0x0016 73124386Snjl#define HCI_REG_VIDEO_OUTPUT 0x001C 74124386Snjl#define HCI_REG_HOTKEY_EVENT 0x001E 75124386Snjl#define HCI_REG_LCD_BRIGHTNESS 0x002A 76124386Snjl#define HCI_REG_CPU_SPEED 0x0032 77124386Snjl 78124386Snjl/* Field definitions */ 79124386Snjl#define HCI_FAN_SHIFT 7 80124386Snjl#define HCI_LCD_BRIGHTNESS_BITS 3 81124386Snjl#define HCI_LCD_BRIGHTNESS_SHIFT (16 - HCI_LCD_BRIGHTNESS_BITS) 82124386Snjl#define HCI_LCD_BRIGHTNESS_MAX ((1 << HCI_LCD_BRIGHTNESS_BITS) - 1) 83124386Snjl#define HCI_VIDEO_OUTPUT_FLAG 0x0100 84124386Snjl#define HCI_VIDEO_OUTPUT_LCD 0x1 85124386Snjl#define HCI_VIDEO_OUTPUT_CRT 0x2 86124386Snjl#define HCI_VIDEO_OUTPUT_TV 0x4 87124386Snjl#define HCI_CPU_SPEED_BITS 3 88124386Snjl#define HCI_CPU_SPEED_SHIFT (16 - HCI_CPU_SPEED_BITS) 89124386Snjl#define HCI_CPU_SPEED_MAX ((1 << HCI_CPU_SPEED_BITS) - 1) 90124386Snjl 91124386Snjl/* Key press/release events. */ 92124386Snjl#define FN_F1_PRESS 0x013B 93124386Snjl#define FN_F1_RELEASE 0x01BB 94124386Snjl#define FN_F2_PRESS 0x013C 95124386Snjl#define FN_F2_RELEASE 0x01BC 96124386Snjl#define FN_F3_PRESS 0x013D 97124386Snjl#define FN_F3_RELEASE 0x01BD 98124386Snjl#define FN_F4_PRESS 0x013E 99124386Snjl#define FN_F4_RELEASE 0x01BE 100124386Snjl#define FN_F5_PRESS 0x013F 101124386Snjl#define FN_F5_RELEASE 0x01BF 102124386Snjl#define FN_F6_PRESS 0x0140 103124386Snjl#define FN_F6_RELEASE 0x01C0 104124386Snjl#define FN_F7_PRESS 0x0141 105124386Snjl#define FN_F7_RELEASE 0x01C1 106124386Snjl#define FN_F8_PRESS 0x0142 107124386Snjl#define FN_F8_RELEASE 0x01C2 108124386Snjl#define FN_F9_PRESS 0x0143 109124386Snjl#define FN_F9_RELEASE 0x01C3 110124386Snjl#define FN_BS_PRESS 0x010E 111124386Snjl#define FN_BS_RELEASE 0x018E 112124386Snjl#define FN_ESC_PRESS 0x0101 113124386Snjl#define FN_ESC_RELEASE 0x0181 114124386Snjl#define FN_KNJ_PRESS 0x0129 115124386Snjl#define FN_KNJ_RELEASE 0x01A9 116124386Snjl 117124386Snjl/* HCI register definitions. */ 118124386Snjl#define HCI_WORDS 6 /* Number of registers */ 119124386Snjl#define HCI_REG_AX 0 /* Operation, then return value */ 120124386Snjl#define HCI_REG_BX 1 /* Function */ 121124386Snjl#define HCI_REG_CX 2 /* Argument (in or out) */ 122124386Snjl#define HCI_REG_DX 3 /* Unused? */ 123124386Snjl#define HCI_REG_SI 4 /* Unused? */ 124124386Snjl#define HCI_REG_DI 5 /* Unused? */ 125124386Snjl 126124386Snjlstruct acpi_toshiba_softc { 127124386Snjl device_t dev; 128124386Snjl ACPI_HANDLE handle; 129128207Snjl ACPI_HANDLE video_handle; 130124386Snjl struct sysctl_ctx_list sysctl_ctx; 131124386Snjl struct sysctl_oid *sysctl_tree; 132124386Snjl}; 133124386Snjl 134124386Snjl/* Prototype for HCI functions for getting/setting a value. */ 135124386Snjltypedef int hci_fn_t(ACPI_HANDLE, int, UINT32 *); 136124386Snjl 137124386Snjlstatic int acpi_toshiba_probe(device_t dev); 138124386Snjlstatic int acpi_toshiba_attach(device_t dev); 139124386Snjlstatic int acpi_toshiba_detach(device_t dev); 140124386Snjlstatic int acpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS); 141124386Snjlstatic hci_fn_t hci_force_fan; 142124386Snjlstatic hci_fn_t hci_video_output; 143124386Snjlstatic hci_fn_t hci_lcd_brightness; 144124386Snjlstatic hci_fn_t hci_lcd_backlight; 145124386Snjlstatic hci_fn_t hci_cpu_speed; 146124386Snjlstatic int hci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg); 147128207Snjlstatic void hci_key_action(struct acpi_toshiba_softc *sc, ACPI_HANDLE h, 148128207Snjl UINT32 key); 149124386Snjlstatic void acpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify, 150128207Snjl void *context); 151128207Snjlstatic int acpi_toshiba_video_probe(device_t dev); 152128207Snjlstatic int acpi_toshiba_video_attach(device_t dev); 153124386Snjl 154133630SnjlACPI_SERIAL_DECL(toshiba, "ACPI Toshiba Extras"); 155133630Snjl 156124386Snjl/* Table of sysctl names and HCI functions to call. */ 157124386Snjlstatic struct { 158124386Snjl char *name; 159124386Snjl hci_fn_t *handler; 160124386Snjl} sysctl_table[] = { 161124386Snjl /* name, handler */ 162124386Snjl {"force_fan", hci_force_fan}, 163124386Snjl {"video_output", hci_video_output}, 164124415Snjl {"lcd_brightness", hci_lcd_brightness}, 165124386Snjl {"lcd_backlight", hci_lcd_backlight}, 166124415Snjl {"cpu_speed", hci_cpu_speed}, 167124386Snjl {NULL, NULL} 168124386Snjl}; 169124386Snjl 170124386Snjlstatic device_method_t acpi_toshiba_methods[] = { 171124386Snjl DEVMETHOD(device_probe, acpi_toshiba_probe), 172124386Snjl DEVMETHOD(device_attach, acpi_toshiba_attach), 173124386Snjl DEVMETHOD(device_detach, acpi_toshiba_detach), 174124386Snjl 175246128Ssbz DEVMETHOD_END 176124386Snjl}; 177124386Snjl 178124386Snjlstatic driver_t acpi_toshiba_driver = { 179124386Snjl "acpi_toshiba", 180124386Snjl acpi_toshiba_methods, 181124386Snjl sizeof(struct acpi_toshiba_softc), 182124386Snjl}; 183124386Snjl 184124386Snjlstatic devclass_t acpi_toshiba_devclass; 185124386SnjlDRIVER_MODULE(acpi_toshiba, acpi, acpi_toshiba_driver, acpi_toshiba_devclass, 186128207Snjl 0, 0); 187128036SnjlMODULE_DEPEND(acpi_toshiba, acpi, 1, 1, 1); 188124386Snjl 189128207Snjlstatic device_method_t acpi_toshiba_video_methods[] = { 190128207Snjl DEVMETHOD(device_probe, acpi_toshiba_video_probe), 191128207Snjl DEVMETHOD(device_attach, acpi_toshiba_video_attach), 192128207Snjl 193246128Ssbz DEVMETHOD_END 194128207Snjl}; 195128207Snjl 196128207Snjlstatic driver_t acpi_toshiba_video_driver = { 197128207Snjl "acpi_toshiba_video", 198128207Snjl acpi_toshiba_video_methods, 199128207Snjl 0, 200128207Snjl}; 201128207Snjl 202128207Snjlstatic devclass_t acpi_toshiba_video_devclass; 203128207SnjlDRIVER_MODULE(acpi_toshiba_video, acpi, acpi_toshiba_video_driver, 204128207Snjl acpi_toshiba_video_devclass, 0, 0); 205128207SnjlMODULE_DEPEND(acpi_toshiba_video, acpi, 1, 1, 1); 206128207Snjl 207124386Snjlstatic int enable_fn_keys = 1; 208124386SnjlTUNABLE_INT("hw.acpi.toshiba.enable_fn_keys", &enable_fn_keys); 209124386Snjl 210124386Snjl/* 211124386Snjl * HID Model 212124386Snjl * ------------------------------------- 213124386Snjl * TOS6200 Libretto L Series 214124386Snjl * Dynabook Satellite 2455 215124386Snjl * Dynabook SS 3500 216124386Snjl * TOS6207 Dynabook SS2110 Series 217144074Sjhb * TOS6208 SPA40 218124386Snjl */ 219124386Snjlstatic int 220124386Snjlacpi_toshiba_probe(device_t dev) 221124386Snjl{ 222144074Sjhb static char *tosh_ids[] = { "TOS6200", "TOS6207", "TOS6208", NULL }; 223124386Snjl 224131284Snjl if (acpi_disabled("toshiba") || 225131284Snjl ACPI_ID_PROBE(device_get_parent(dev), dev, tosh_ids) == NULL || 226131284Snjl device_get_unit(dev) != 0) 227131284Snjl return (ENXIO); 228124386Snjl 229131284Snjl device_set_desc(dev, "Toshiba HCI Extras"); 230131284Snjl return (0); 231124386Snjl} 232124386Snjl 233124386Snjlstatic int 234124386Snjlacpi_toshiba_attach(device_t dev) 235124386Snjl{ 236124386Snjl struct acpi_toshiba_softc *sc; 237124386Snjl struct acpi_softc *acpi_sc; 238124386Snjl ACPI_STATUS status; 239124386Snjl int i; 240124386Snjl 241124386Snjl sc = device_get_softc(dev); 242124386Snjl sc->dev = dev; 243124386Snjl sc->handle = acpi_get_handle(dev); 244124386Snjl 245124386Snjl acpi_sc = acpi_device_get_parent_softc(dev); 246124386Snjl sysctl_ctx_init(&sc->sysctl_ctx); 247124386Snjl sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 248124386Snjl SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, 249124386Snjl "toshiba", CTLFLAG_RD, 0, ""); 250124386Snjl 251124386Snjl for (i = 0; sysctl_table[i].name != NULL; i++) { 252124386Snjl SYSCTL_ADD_PROC(&sc->sysctl_ctx, 253124386Snjl SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 254124386Snjl sysctl_table[i].name, 255124386Snjl CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, 256124386Snjl sc, i, acpi_toshiba_sysctl, "I", ""); 257124386Snjl } 258124386Snjl 259124386Snjl if (enable_fn_keys != 0) { 260124386Snjl status = AcpiEvaluateObject(sc->handle, METHOD_HCI_ENABLE, 261124386Snjl NULL, NULL); 262124386Snjl if (ACPI_FAILURE(status)) { 263124386Snjl device_printf(dev, "enable FN keys failed\n"); 264124386Snjl sysctl_ctx_free(&sc->sysctl_ctx); 265124386Snjl return (ENXIO); 266124386Snjl } 267124386Snjl AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 268124386Snjl acpi_toshiba_notify, sc); 269124386Snjl } 270124386Snjl 271124386Snjl return (0); 272124386Snjl} 273124386Snjl 274124386Snjlstatic int 275124386Snjlacpi_toshiba_detach(device_t dev) 276124386Snjl{ 277124386Snjl struct acpi_toshiba_softc *sc; 278124386Snjl 279124386Snjl sc = device_get_softc(dev); 280124442Snjl if (enable_fn_keys != 0) { 281124442Snjl AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 282124442Snjl acpi_toshiba_notify); 283124442Snjl } 284124386Snjl sysctl_ctx_free(&sc->sysctl_ctx); 285124386Snjl 286124386Snjl return (0); 287124386Snjl} 288124386Snjl 289124386Snjlstatic int 290124386Snjlacpi_toshiba_sysctl(SYSCTL_HANDLER_ARGS) 291124386Snjl{ 292124386Snjl struct acpi_toshiba_softc *sc; 293124386Snjl UINT32 arg; 294124386Snjl int function, error = 0; 295124386Snjl hci_fn_t *handler; 296124386Snjl 297124386Snjl sc = (struct acpi_toshiba_softc *)oidp->oid_arg1; 298124386Snjl function = oidp->oid_arg2; 299124386Snjl handler = sysctl_table[function].handler; 300124386Snjl 301124386Snjl /* Get the current value from the appropriate function. */ 302133630Snjl ACPI_SERIAL_BEGIN(toshiba); 303124386Snjl error = handler(sc->handle, HCI_GET, &arg); 304124386Snjl if (error != 0) 305133630Snjl goto out; 306124386Snjl 307124386Snjl /* Send the current value to the user and return if no new value. */ 308124386Snjl error = sysctl_handle_int(oidp, &arg, 0, req); 309124386Snjl if (error != 0 || req->newptr == NULL) 310133630Snjl goto out; 311124386Snjl 312124386Snjl /* Set the new value via the appropriate function. */ 313124386Snjl error = handler(sc->handle, HCI_SET, &arg); 314124386Snjl 315133630Snjlout: 316133630Snjl ACPI_SERIAL_END(toshiba); 317124386Snjl return (error); 318124386Snjl} 319124386Snjl 320124386Snjlstatic int 321124386Snjlhci_force_fan(ACPI_HANDLE h, int op, UINT32 *state) 322124386Snjl{ 323124386Snjl int ret; 324124386Snjl 325133630Snjl ACPI_SERIAL_ASSERT(toshiba); 326124386Snjl if (op == HCI_SET) { 327249816Sjkim if (*state > 1) 328124386Snjl return (EINVAL); 329124386Snjl *state <<= HCI_FAN_SHIFT; 330124386Snjl } 331124386Snjl ret = hci_call(h, op, HCI_REG_FAN, state); 332124386Snjl if (ret == 0 && op == HCI_GET) 333124386Snjl *state >>= HCI_FAN_SHIFT; 334124386Snjl return (ret); 335124386Snjl} 336124386Snjl 337124386Snjlstatic int 338124386Snjlhci_video_output(ACPI_HANDLE h, int op, UINT32 *video_output) 339124386Snjl{ 340124386Snjl int ret; 341128207Snjl ACPI_STATUS status; 342124386Snjl 343133630Snjl ACPI_SERIAL_ASSERT(toshiba); 344124386Snjl if (op == HCI_SET) { 345124386Snjl if (*video_output < 1 || *video_output > 7) 346124386Snjl return (EINVAL); 347128207Snjl if (h == NULL) 348128207Snjl return (ENXIO); 349124386Snjl *video_output |= HCI_VIDEO_OUTPUT_FLAG; 350128223Snjl status = acpi_SetInteger(h, METHOD_VIDEO, *video_output); 351128207Snjl if (ACPI_SUCCESS(status)) 352128207Snjl ret = 0; 353128207Snjl else 354128207Snjl ret = ENXIO; 355128207Snjl } else { 356128207Snjl ret = hci_call(h, op, HCI_REG_VIDEO_OUTPUT, video_output); 357128207Snjl if (ret == 0) 358128207Snjl *video_output &= 0xff; 359124386Snjl } 360128207Snjl 361124386Snjl return (ret); 362124386Snjl} 363124386Snjl 364124386Snjlstatic int 365124386Snjlhci_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *brightness) 366124386Snjl{ 367124386Snjl int ret; 368124386Snjl 369133630Snjl ACPI_SERIAL_ASSERT(toshiba); 370124386Snjl if (op == HCI_SET) { 371249816Sjkim if (*brightness > HCI_LCD_BRIGHTNESS_MAX) 372124386Snjl return (EINVAL); 373124386Snjl *brightness <<= HCI_LCD_BRIGHTNESS_SHIFT; 374124386Snjl } 375124386Snjl ret = hci_call(h, op, HCI_REG_LCD_BRIGHTNESS, brightness); 376124386Snjl if (ret == 0 && op == HCI_GET) 377124386Snjl *brightness >>= HCI_LCD_BRIGHTNESS_SHIFT; 378124386Snjl return (ret); 379124386Snjl} 380124386Snjl 381124386Snjlstatic int 382124386Snjlhci_lcd_backlight(ACPI_HANDLE h, int op, UINT32 *backlight) 383124386Snjl{ 384133630Snjl 385133630Snjl ACPI_SERIAL_ASSERT(toshiba); 386124386Snjl if (op == HCI_SET) { 387249816Sjkim if (*backlight > 1) 388124386Snjl return (EINVAL); 389124386Snjl } 390124386Snjl return (hci_call(h, op, HCI_REG_LCD_BACKLIGHT, backlight)); 391124386Snjl} 392124386Snjl 393124386Snjlstatic int 394124386Snjlhci_cpu_speed(ACPI_HANDLE h, int op, UINT32 *speed) 395124386Snjl{ 396124386Snjl int ret; 397124386Snjl 398133630Snjl ACPI_SERIAL_ASSERT(toshiba); 399124386Snjl if (op == HCI_SET) { 400249816Sjkim if (*speed > HCI_CPU_SPEED_MAX) 401124386Snjl return (EINVAL); 402124386Snjl *speed <<= HCI_CPU_SPEED_SHIFT; 403124386Snjl } 404124386Snjl ret = hci_call(h, op, HCI_REG_CPU_SPEED, speed); 405124386Snjl if (ret == 0 && op == HCI_GET) 406124386Snjl *speed >>= HCI_CPU_SPEED_SHIFT; 407124386Snjl return (ret); 408124386Snjl} 409124386Snjl 410124386Snjlstatic int 411124386Snjlhci_call(ACPI_HANDLE h, int op, int function, UINT32 *arg) 412124386Snjl{ 413124386Snjl ACPI_OBJECT_LIST args; 414124386Snjl ACPI_BUFFER results; 415124386Snjl ACPI_OBJECT obj[HCI_WORDS]; 416124386Snjl ACPI_OBJECT *res; 417124386Snjl int status, i, ret; 418124386Snjl 419133630Snjl ACPI_SERIAL_ASSERT(toshiba); 420124386Snjl status = ENXIO; 421124386Snjl 422124386Snjl for (i = 0; i < HCI_WORDS; i++) { 423124386Snjl obj[i].Type = ACPI_TYPE_INTEGER; 424124386Snjl obj[i].Integer.Value = 0; 425124386Snjl } 426124386Snjl obj[HCI_REG_AX].Integer.Value = op; 427124386Snjl obj[HCI_REG_BX].Integer.Value = function; 428124386Snjl if (op == HCI_SET) 429124386Snjl obj[HCI_REG_CX].Integer.Value = *arg; 430124386Snjl 431124386Snjl args.Count = HCI_WORDS; 432124386Snjl args.Pointer = obj; 433124386Snjl results.Pointer = NULL; 434124386Snjl results.Length = ACPI_ALLOCATE_BUFFER; 435124386Snjl if (ACPI_FAILURE(AcpiEvaluateObject(h, METHOD_HCI, &args, &results))) 436124386Snjl goto end; 437124386Snjl res = (ACPI_OBJECT *)results.Pointer; 438124386Snjl if (!ACPI_PKG_VALID(res, HCI_WORDS)) { 439124386Snjl printf("toshiba: invalid package!\n"); 440124386Snjl return (ENXIO); 441124386Snjl } 442124386Snjl 443124386Snjl acpi_PkgInt32(res, HCI_REG_AX, &ret); 444124386Snjl if (ret == HCI_SUCCESS) { 445124386Snjl if (op == HCI_GET) 446124386Snjl acpi_PkgInt32(res, HCI_REG_CX, arg); 447124386Snjl status = 0; 448124386Snjl } else if (function == HCI_REG_SYSTEM_EVENT && op == HCI_GET && 449124386Snjl ret == HCI_NOT_SUPPORTED) { 450124386Snjl /* 451124386Snjl * Sometimes system events are disabled without us requesting 452124386Snjl * it. This workaround attempts to re-enable them. 453133630Snjl * 454133630Snjl * XXX This call probably shouldn't be recursive. Queueing 455133630Snjl * a task via AcpiOsQueueForExecution() might be better. 456124386Snjl */ 457124386Snjl i = 1; 458124386Snjl hci_call(h, HCI_SET, HCI_REG_SYSTEM_EVENT, &i); 459124386Snjl } 460124386Snjl 461124386Snjlend: 462124386Snjl if (results.Pointer != NULL) 463124386Snjl AcpiOsFree(results.Pointer); 464124386Snjl 465124386Snjl return (status); 466124386Snjl} 467124386Snjl 468124386Snjl/* 469124386Snjl * Perform a few actions based on the keypress. Users can extend this 470124386Snjl * functionality by reading the keystrokes we send to devd(8). 471124386Snjl */ 472124386Snjlstatic void 473128207Snjlhci_key_action(struct acpi_toshiba_softc *sc, ACPI_HANDLE h, UINT32 key) 474124386Snjl{ 475124386Snjl UINT32 arg; 476124386Snjl 477133630Snjl ACPI_SERIAL_ASSERT(toshiba); 478124386Snjl switch (key) { 479124386Snjl case FN_F6_RELEASE: 480124386Snjl /* Decrease LCD brightness. */ 481124386Snjl hci_lcd_brightness(h, HCI_GET, &arg); 482124386Snjl if (arg-- == 0) 483124386Snjl arg = 0; 484124386Snjl else 485124386Snjl hci_lcd_brightness(h, HCI_SET, &arg); 486124386Snjl break; 487124386Snjl case FN_F7_RELEASE: 488124386Snjl /* Increase LCD brightness. */ 489124386Snjl hci_lcd_brightness(h, HCI_GET, &arg); 490124386Snjl if (arg++ == 7) 491124386Snjl arg = 7; 492124386Snjl else 493124386Snjl hci_lcd_brightness(h, HCI_SET, &arg); 494124386Snjl break; 495124386Snjl case FN_F5_RELEASE: 496124386Snjl /* Cycle through video outputs. */ 497124386Snjl hci_video_output(h, HCI_GET, &arg); 498124386Snjl arg = (arg + 1) % 7; 499128207Snjl hci_video_output(sc->video_handle, HCI_SET, &arg); 500124386Snjl break; 501124386Snjl case FN_F8_RELEASE: 502124386Snjl /* Toggle LCD backlight. */ 503124386Snjl hci_lcd_backlight(h, HCI_GET, &arg); 504124386Snjl arg = (arg != 0) ? 0 : 1; 505124386Snjl hci_lcd_backlight(h, HCI_SET, &arg); 506124386Snjl break; 507124386Snjl case FN_ESC_RELEASE: 508124386Snjl /* Toggle forcing fan on. */ 509124386Snjl hci_force_fan(h, HCI_GET, &arg); 510124386Snjl arg = (arg != 0) ? 0 : 1; 511124386Snjl hci_force_fan(h, HCI_SET, &arg); 512124386Snjl break; 513124386Snjl } 514124386Snjl} 515124386Snjl 516124386Snjlstatic void 517124386Snjlacpi_toshiba_notify(ACPI_HANDLE h, UINT32 notify, void *context) 518124386Snjl{ 519124386Snjl struct acpi_toshiba_softc *sc; 520124386Snjl UINT32 key; 521124386Snjl 522124386Snjl sc = (struct acpi_toshiba_softc *)context; 523124386Snjl 524124386Snjl if (notify == 0x80) { 525133630Snjl ACPI_SERIAL_BEGIN(toshiba); 526124386Snjl while (hci_call(h, HCI_GET, HCI_REG_SYSTEM_EVENT, &key) == 0) { 527128207Snjl hci_key_action(sc, h, key); 528124386Snjl acpi_UserNotify("TOSHIBA", h, (uint8_t)key); 529124386Snjl } 530133630Snjl ACPI_SERIAL_END(toshiba); 531128207Snjl } else 532124386Snjl device_printf(sc->dev, "unknown notify: 0x%x\n", notify); 533128207Snjl} 534128207Snjl 535128207Snjl/* 536128207Snjl * Toshiba video pseudo-device to provide the DSSX method. 537128207Snjl * 538128207Snjl * HID Model 539128207Snjl * ------------------------------------- 540128207Snjl * TOS6201 Libretto L Series 541128207Snjl */ 542128207Snjlstatic int 543128207Snjlacpi_toshiba_video_probe(device_t dev) 544128207Snjl{ 545131284Snjl static char *vid_ids[] = { "TOS6201", NULL }; 546128207Snjl 547131284Snjl if (acpi_disabled("toshiba") || 548131284Snjl ACPI_ID_PROBE(device_get_parent(dev), dev, vid_ids) == NULL || 549131284Snjl device_get_unit(dev) != 0) 550131284Snjl return (ENXIO); 551128207Snjl 552131284Snjl device_quiet(dev); 553131284Snjl device_set_desc(dev, "Toshiba Video"); 554131284Snjl return (0); 555124386Snjl} 556128207Snjl 557128207Snjlstatic int 558128207Snjlacpi_toshiba_video_attach(device_t dev) 559128207Snjl{ 560128207Snjl struct acpi_toshiba_softc *sc; 561128207Snjl 562128207Snjl sc = devclass_get_softc(acpi_toshiba_devclass, 0); 563128207Snjl if (sc == NULL) 564128207Snjl return (ENXIO); 565128207Snjl sc->video_handle = acpi_get_handle(dev); 566128207Snjl return (0); 567128207Snjl} 568