acpi_panasonic.c revision 132611
1/*- 2 * Copyright (c) 2003 OGAWA Takaya <t-ogawa@triaez.kaisei.org> 3 * Copyright (c) 2004 Yoshihiro TAKAHASHI <nyan@FreeBSD.org> 4 * All rights Reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: head/sys/dev/acpi_support/acpi_panasonic.c 132611 2004-07-24 20:40:02Z njl $"); 31 32#include "opt_acpi.h" 33#include <sys/param.h> 34#include <sys/kernel.h> 35#include <sys/malloc.h> 36#include <sys/module.h> 37#include <sys/bus.h> 38 39#include "acpi.h" 40#include <dev/acpica/acpivar.h> 41 42/* Debug */ 43#undef ACPI_PANASONIC_DEBUG 44 45/* Operations */ 46#define HKEY_SET 0 47#define HKEY_GET 1 48 49/* Functions */ 50#define HKEY_REG_LCD_BRIGHTNESS 0x04 51#define HKEY_REG_SOUND_MUTE 0x08 52 53/* Field definitions */ 54#define HKEY_LCD_BRIGHTNESS_BITS 4 55#define HKEY_LCD_BRIGHTNESS_DIV ((1 << HKEY_LCD_BRIGHTNESS_BITS) - 1) 56 57struct acpi_panasonic_softc { 58 device_t dev; 59 ACPI_HANDLE handle; 60 61 struct sysctl_ctx_list sysctl_ctx; 62 struct sysctl_oid *sysctl_tree; 63}; 64 65/* Prototype for HKEY functions for getting/setting a value. */ 66typedef int hkey_fn_t(ACPI_HANDLE, int, UINT32 *); 67 68static int acpi_panasonic_probe(device_t dev); 69static int acpi_panasonic_attach(device_t dev); 70static int acpi_panasonic_detach(device_t dev); 71static int acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS); 72static ACPI_INTEGER acpi_panasonic_sinf(ACPI_HANDLE h, ACPI_INTEGER index); 73static void acpi_panasonic_sset(ACPI_HANDLE h, ACPI_INTEGER index, 74 ACPI_INTEGER val); 75static int acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc, 76 ACPI_HANDLE h, UINT32 *arg); 77static void acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc, 78 ACPI_HANDLE h, UINT32 key); 79static void acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify, 80 void *context); 81 82static hkey_fn_t hkey_lcd_brightness_max; 83static hkey_fn_t hkey_lcd_brightness; 84static hkey_fn_t hkey_sound_mute; 85static int lcd_brightness_max = 255; 86 87/* Table of sysctl names and HKEY functions to call. */ 88static struct { 89 char *name; 90 hkey_fn_t *handler; 91} sysctl_table[] = { 92 /* name, handler */ 93 {"lcd_brightness_max", hkey_lcd_brightness_max}, 94 {"lcd_brightness", hkey_lcd_brightness}, 95 {"sound_mute", hkey_sound_mute}, 96 {NULL, NULL} 97}; 98 99static device_method_t acpi_panasonic_methods[] = { 100 DEVMETHOD(device_probe, acpi_panasonic_probe), 101 DEVMETHOD(device_attach, acpi_panasonic_attach), 102 DEVMETHOD(device_detach, acpi_panasonic_detach), 103 104 {0, 0} 105}; 106 107static driver_t acpi_panasonic_driver = { 108 "acpi_panasonic", 109 acpi_panasonic_methods, 110 sizeof(struct acpi_panasonic_softc), 111}; 112 113static devclass_t acpi_panasonic_devclass; 114 115DRIVER_MODULE(acpi_panasonic, acpi, acpi_panasonic_driver, 116 acpi_panasonic_devclass, 0, 0); 117MODULE_DEPEND(acpi_panasonic, acpi, 1, 1, 1); 118 119static int 120acpi_panasonic_probe(device_t dev) 121{ 122 static char *mat_ids[] = { "MAT0019", NULL }; 123 124 if (acpi_disabled("panasonic") || 125 ACPI_ID_PROBE(device_get_parent(dev), dev, mat_ids) == NULL || 126 device_get_unit(dev) != 0) 127 return (ENXIO); 128 129 device_set_desc(dev, "Panasonic Notebook Hotkeys"); 130 return (0); 131} 132 133static int 134acpi_panasonic_attach(device_t dev) 135{ 136 struct acpi_panasonic_softc *sc; 137 struct acpi_softc *acpi_sc; 138 ACPI_STATUS status; 139 int i; 140 141 sc = device_get_softc(dev); 142 sc->dev = dev; 143 sc->handle = acpi_get_handle(dev); 144 145 acpi_sc = acpi_device_get_parent_softc(dev); 146 147 /* Build sysctl tree */ 148 sysctl_ctx_init(&sc->sysctl_ctx); 149 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 150 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, 151 "panasonic", CTLFLAG_RD, 0, ""); 152 for (i = 0; sysctl_table[i].name != NULL; i++) { 153 SYSCTL_ADD_PROC(&sc->sysctl_ctx, 154 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 155 sysctl_table[i].name, 156 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, 157 sc, i, acpi_panasonic_sysctl, "I", ""); 158 } 159 160#if 0 161 /* Activate hotkeys */ 162 status = AcpiEvaluateObject(sc->handle, "", NULL, NULL); 163 if (ACPI_FAILURE(status)) { 164 device_printf(dev, "enable FN keys failed\n"); 165 sysctl_ctx_free(&sc->sysctl_ctx); 166 return (ENXIO); 167 } 168#endif 169 170 /* Handle notifies */ 171 status = AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 172 acpi_panasonic_notify, sc); 173 if (ACPI_FAILURE(status)) { 174 device_printf(dev, "couldn't install notify handler - %s\n", 175 AcpiFormatException(status)); 176 sysctl_ctx_free(&sc->sysctl_ctx); 177 return (ENXIO); 178 } 179 180 return (0); 181} 182 183static int 184acpi_panasonic_detach(device_t dev) 185{ 186 struct acpi_panasonic_softc *sc; 187 188 sc = device_get_softc(dev); 189 190 /* Remove notify handler */ 191 AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 192 acpi_panasonic_notify); 193 194 /* Free sysctl tree */ 195 sysctl_ctx_free(&sc->sysctl_ctx); 196 197 return (0); 198} 199 200static int 201acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS) 202{ 203 struct acpi_panasonic_softc *sc; 204 UINT32 arg; 205 int function, error; 206 hkey_fn_t *handler; 207 208 sc = (struct acpi_panasonic_softc *)oidp->oid_arg1; 209 function = oidp->oid_arg2; 210 handler = sysctl_table[function].handler; 211 212 /* Get the current value from the appropriate function. */ 213 error = handler(sc->handle, HKEY_GET, &arg); 214 if (error != 0) 215 return (error); 216 217 /* Send the current value to the user and return if no new value. */ 218 error = sysctl_handle_int(oidp, &arg, 0, req); 219 if (error != 0 || req->newptr == NULL) 220 return (error); 221 222 /* Set the new value via the appropriate function. */ 223 error = handler(sc->handle, HKEY_SET, &arg); 224 225 return (error); 226} 227 228static ACPI_INTEGER 229acpi_panasonic_sinf(ACPI_HANDLE h, ACPI_INTEGER index) 230{ 231 ACPI_BUFFER buf; 232 ACPI_OBJECT *res; 233 ACPI_INTEGER ret; 234 235 ret = -1; 236 buf.Length = ACPI_ALLOCATE_BUFFER; 237 buf.Pointer = NULL; 238 AcpiEvaluateObject(h, "SINF", NULL, &buf); 239 res = (ACPI_OBJECT *)buf.Pointer; 240 if (res->Type == ACPI_TYPE_PACKAGE) 241 ret = res->Package.Elements[index].Integer.Value; 242 AcpiOsFree(buf.Pointer); 243 244 return (ret); 245} 246 247static void 248acpi_panasonic_sset(ACPI_HANDLE h, ACPI_INTEGER index, ACPI_INTEGER val) 249{ 250 ACPI_OBJECT_LIST args; 251 ACPI_OBJECT obj[2]; 252 253 obj[0].Type = ACPI_TYPE_INTEGER; 254 obj[0].Integer.Value = index; 255 obj[1].Type = ACPI_TYPE_INTEGER; 256 obj[1].Integer.Value = val; 257 args.Count = 2; 258 args.Pointer = obj; 259 AcpiEvaluateObject(h, "SSET", &args, NULL); 260} 261 262static int 263hkey_lcd_brightness_max(ACPI_HANDLE h, int op, UINT32 *val) 264{ 265 266 switch (op) { 267 case HKEY_SET: 268 if (*val < 0 || *val > 255) 269 return (EINVAL); 270 lcd_brightness_max = *val; 271 break; 272 case HKEY_GET: 273 *val = lcd_brightness_max; 274 break; 275 } 276 277 return (0); 278} 279 280static int 281hkey_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *val) 282{ 283 284 switch (op) { 285 case HKEY_SET: 286 if (*val < 0 || *val > lcd_brightness_max) 287 return (EINVAL); 288 acpi_panasonic_sset(h, HKEY_REG_LCD_BRIGHTNESS, *val); 289 break; 290 case HKEY_GET: 291 *val = acpi_panasonic_sinf(h, HKEY_REG_LCD_BRIGHTNESS); 292 break; 293 } 294 295 return (0); 296} 297 298static int 299hkey_sound_mute(ACPI_HANDLE h, int op, UINT32 *val) 300{ 301 302 switch (op) { 303 case HKEY_SET: 304 if (*val != 0 && *val != 1) 305 return (EINVAL); 306 acpi_panasonic_sset(h, HKEY_REG_SOUND_MUTE, *val); 307 break; 308 case HKEY_GET: 309 *val = acpi_panasonic_sinf(h, HKEY_REG_SOUND_MUTE); 310 break; 311 } 312 313 return (0); 314} 315 316static int 317acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc, ACPI_HANDLE h, 318 UINT32 *arg) 319{ 320 ACPI_BUFFER buf; 321 ACPI_OBJECT *res; 322 ACPI_INTEGER val; 323 int status; 324 325 status = ENXIO; 326 327 buf.Length = ACPI_ALLOCATE_BUFFER; 328 buf.Pointer = NULL; 329 AcpiEvaluateObject(h, "HINF", NULL, &buf); 330 res = (ACPI_OBJECT *)buf.Pointer; 331 if (res->Type != ACPI_TYPE_INTEGER) { 332 device_printf(sc->dev, "HINF returned non-integer\n"); 333 goto end; 334 } 335 val = res->Integer.Value; 336#ifdef ACPI_PANASONIC_DEBUG 337 device_printf(sc->dev, "%s button Fn+F%d\n", 338 (val & 0x80) ? "Pressed" : "Released", 339 (int)(val & 0x7f)); 340#endif 341 if ((val & 0x7f) > 0 && (val & 0x7f) < 11) { 342 *arg = val; 343 status = 0; 344 } 345end: 346 if (buf.Pointer) 347 AcpiOsFree(buf.Pointer); 348 349 return (status); 350} 351 352static void 353acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc, ACPI_HANDLE h, 354 UINT32 key) 355{ 356 int arg; 357 358 switch (key) { 359 case 1: 360 /* Decrease LCD brightness. */ 361 hkey_lcd_brightness(h, HKEY_GET, &arg); 362 arg -= lcd_brightness_max / HKEY_LCD_BRIGHTNESS_DIV; 363 if (arg < 0) 364 arg = 0; 365 else if (arg > lcd_brightness_max) 366 arg = lcd_brightness_max; 367 hkey_lcd_brightness(h, HKEY_SET, &arg); 368 break; 369 case 2: 370 /* Increase LCD brightness. */ 371 hkey_lcd_brightness(h, HKEY_GET, &arg); 372 arg += lcd_brightness_max / HKEY_LCD_BRIGHTNESS_DIV; 373 if (arg < 0) 374 arg = 0; 375 else if (arg > lcd_brightness_max) 376 arg = lcd_brightness_max; 377 hkey_lcd_brightness(h, HKEY_SET, &arg); 378 break; 379 case 4: 380 /* Toggle sound mute. */ 381 hkey_sound_mute(h, HKEY_GET, &arg); 382 if (arg) 383 arg = 0; 384 else 385 arg = 1; 386 hkey_sound_mute(h, HKEY_SET, &arg); 387 break; 388 } 389} 390 391static void 392acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify, void *context) 393{ 394 struct acpi_panasonic_softc *sc; 395 UINT32 key; 396 397 sc = (struct acpi_panasonic_softc *)context; 398 399 switch (notify) { 400 case 0x80: 401 if (acpi_panasonic_hkey_event(sc, h, &key) == 0) { 402 acpi_panasonic_hkey_action(sc, h, key); 403 acpi_UserNotify("Panasonic", h, (uint8_t)key); 404 } 405 break; 406 default: 407 device_printf(sc->dev, "unknown notify: %#x\n", notify); 408 break; 409 } 410} 411