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$"); 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 57 struct 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. */ 66 typedef int hkey_fn_t(ACPI_HANDLE, int, UINT32 *); 67 68 static int acpi_panasonic_probe(device_t dev); 69 static int acpi_panasonic_attach(device_t dev); 70 static int acpi_panasonic_detach(device_t dev); 71 static int acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS); 72 static ACPI_INTEGER acpi_panasonic_sinf(ACPI_HANDLE h, ACPI_INTEGER index); 73 static void acpi_panasonic_sset(ACPI_HANDLE h, ACPI_INTEGER index, 74 ACPI_INTEGER val); 75 static int acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc, 76 ACPI_HANDLE h, UINT32 *arg); 77 static void acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc, 78 ACPI_HANDLE h, UINT32 key); 79 static void acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify, 80 void *context); 81 82 static hkey_fn_t hkey_lcd_brightness_max; 83 static hkey_fn_t hkey_lcd_brightness; 84 static hkey_fn_t hkey_sound_mute; 85 static int lcd_brightness_max = 255; 86 ACPI_SERIAL_DECL(panasonic, "ACPI Panasonic extras"); 87 88 /* Table of sysctl names and HKEY functions to call. */ 89 static struct { 90 char *name; 91 hkey_fn_t *handler; 92 } sysctl_table[] = { 93 /* name, handler */ 94 {"lcd_brightness_max", hkey_lcd_brightness_max}, 95 {"lcd_brightness", hkey_lcd_brightness}, 96 {"sound_mute", hkey_sound_mute}, 97 {NULL, NULL} 98 }; 99 100 static device_method_t acpi_panasonic_methods[] = { 101 DEVMETHOD(device_probe, acpi_panasonic_probe), 102 DEVMETHOD(device_attach, acpi_panasonic_attach), 103 DEVMETHOD(device_detach, acpi_panasonic_detach), 104 105 {0, 0} 106 }; 107 108 static driver_t acpi_panasonic_driver = { 109 "acpi_panasonic", 110 acpi_panasonic_methods, 111 sizeof(struct acpi_panasonic_softc), 112 }; 113 114 static devclass_t acpi_panasonic_devclass; 115 116 DRIVER_MODULE(acpi_panasonic, acpi, acpi_panasonic_driver, 117 acpi_panasonic_devclass, 0, 0); 118 MODULE_DEPEND(acpi_panasonic, acpi, 1, 1, 1); 119 120 static int 121 acpi_panasonic_probe(device_t dev) 122 { 123 static char *mat_ids[] = { "MAT0019", NULL }; 124 125 if (acpi_disabled("panasonic") || 126 ACPI_ID_PROBE(device_get_parent(dev), dev, mat_ids) == NULL || 127 device_get_unit(dev) != 0) 128 return (ENXIO); 129 130 device_set_desc(dev, "Panasonic Notebook Hotkeys"); 131 return (0); 132 } 133 134 static int 135 acpi_panasonic_attach(device_t dev) 136 { 137 struct acpi_panasonic_softc *sc; 138 struct acpi_softc *acpi_sc; 139 ACPI_STATUS status; 140 int i; 141 142 sc = device_get_softc(dev); 143 sc->dev = dev; 144 sc->handle = acpi_get_handle(dev); 145 146 acpi_sc = acpi_device_get_parent_softc(dev); 147 148 /* Build sysctl tree */ 149 sysctl_ctx_init(&sc->sysctl_ctx); 150 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 151 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, 152 "panasonic", CTLFLAG_RD, 0, ""); 153 for (i = 0; sysctl_table[i].name != NULL; i++) { 154 SYSCTL_ADD_PROC(&sc->sysctl_ctx, 155 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 156 sysctl_table[i].name, 157 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, 158 sc, i, acpi_panasonic_sysctl, "I", ""); 159 } 160 161 #if 0 162 /* Activate hotkeys */ 163 status = AcpiEvaluateObject(sc->handle, "", NULL, NULL); 164 if (ACPI_FAILURE(status)) { 165 device_printf(dev, "enable FN keys failed\n"); 166 sysctl_ctx_free(&sc->sysctl_ctx); 167 return (ENXIO); 168 } 169 #endif 170 171 /* Handle notifies */ 172 status = AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 173 acpi_panasonic_notify, sc); 174 if (ACPI_FAILURE(status)) { 175 device_printf(dev, "couldn't install notify handler - %s\n", 176 AcpiFormatException(status)); 177 sysctl_ctx_free(&sc->sysctl_ctx); 178 return (ENXIO); 179 } 180 181 return (0); 182 } 183 184 static int 185 acpi_panasonic_detach(device_t dev) 186 { 187 struct acpi_panasonic_softc *sc; 188 189 sc = device_get_softc(dev); 190 191 /* Remove notify handler */ 192 AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 193 acpi_panasonic_notify); 194 195 /* Free sysctl tree */ 196 sysctl_ctx_free(&sc->sysctl_ctx); 197 198 return (0); 199 } 200 201 static int 202 acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS) 203 { 204 struct acpi_panasonic_softc *sc; 205 UINT32 arg; 206 int function, error; 207 hkey_fn_t *handler; 208 209 sc = (struct acpi_panasonic_softc *)oidp->oid_arg1; 210 function = oidp->oid_arg2; 211 handler = sysctl_table[function].handler; 212 213 /* Get the current value from the appropriate function. */ 214 ACPI_SERIAL_BEGIN(panasonic); 215 error = handler(sc->handle, HKEY_GET, &arg); 216 if (error != 0) 217 goto out; 218 219 /* Send the current value to the user and return if no new value. */ 220 error = sysctl_handle_int(oidp, &arg, 0, req); 221 if (error != 0 || req->newptr == NULL) 222 goto out; 223 224 /* Set the new value via the appropriate function. */ 225 error = handler(sc->handle, HKEY_SET, &arg); 226 227 out: 228 ACPI_SERIAL_END(panasonic); 229 return (error); 230 } 231 232 static ACPI_INTEGER 233 acpi_panasonic_sinf(ACPI_HANDLE h, ACPI_INTEGER index) 234 { 235 ACPI_BUFFER buf; 236 ACPI_OBJECT *res; 237 ACPI_INTEGER ret; 238 239 ACPI_SERIAL_ASSERT(panasonic); 240 ret = -1; 241 buf.Length = ACPI_ALLOCATE_BUFFER; 242 buf.Pointer = NULL; 243 AcpiEvaluateObject(h, "SINF", NULL, &buf); 244 res = (ACPI_OBJECT *)buf.Pointer; 245 if (res->Type == ACPI_TYPE_PACKAGE) 246 ret = res->Package.Elements[index].Integer.Value; 247 AcpiOsFree(buf.Pointer); 248 249 return (ret); 250 } 251 252 static void 253 acpi_panasonic_sset(ACPI_HANDLE h, ACPI_INTEGER index, ACPI_INTEGER val) 254 { 255 ACPI_OBJECT_LIST args; 256 ACPI_OBJECT obj[2]; 257 258 ACPI_SERIAL_ASSERT(panasonic); 259 obj[0].Type = ACPI_TYPE_INTEGER; 260 obj[0].Integer.Value = index; 261 obj[1].Type = ACPI_TYPE_INTEGER; 262 obj[1].Integer.Value = val; 263 args.Count = 2; 264 args.Pointer = obj; 265 AcpiEvaluateObject(h, "SSET", &args, NULL); 266 } 267 268 static int 269 hkey_lcd_brightness_max(ACPI_HANDLE h, int op, UINT32 *val) 270 { 271 272 ACPI_SERIAL_ASSERT(panasonic); 273 switch (op) { 274 case HKEY_SET: 275 if (*val < 0 || *val > 255) 276 return (EINVAL); 277 lcd_brightness_max = *val; 278 break; 279 case HKEY_GET: 280 *val = lcd_brightness_max; 281 break; 282 } 283 284 return (0); 285 } 286 287 static int 288 hkey_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *val) 289 { 290 291 ACPI_SERIAL_ASSERT(panasonic); 292 switch (op) { 293 case HKEY_SET: 294 if (*val < 0 || *val > lcd_brightness_max) 295 return (EINVAL); 296 acpi_panasonic_sset(h, HKEY_REG_LCD_BRIGHTNESS, *val); 297 break; 298 case HKEY_GET: 299 *val = acpi_panasonic_sinf(h, HKEY_REG_LCD_BRIGHTNESS); 300 break; 301 } 302 303 return (0); 304 } 305 306 static int 307 hkey_sound_mute(ACPI_HANDLE h, int op, UINT32 *val) 308 { 309 310 ACPI_SERIAL_ASSERT(panasonic); 311 switch (op) { 312 case HKEY_SET: 313 if (*val != 0 && *val != 1) 314 return (EINVAL); 315 acpi_panasonic_sset(h, HKEY_REG_SOUND_MUTE, *val); 316 break; 317 case HKEY_GET: 318 *val = acpi_panasonic_sinf(h, HKEY_REG_SOUND_MUTE); 319 break; 320 } 321 322 return (0); 323 } 324 325 static int 326 acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc, ACPI_HANDLE h, 327 UINT32 *arg) 328 { 329 ACPI_BUFFER buf; 330 ACPI_OBJECT *res; 331 ACPI_INTEGER val; 332 int status; 333 334 ACPI_SERIAL_ASSERT(panasonic); 335 status = ENXIO; 336 337 buf.Length = ACPI_ALLOCATE_BUFFER; 338 buf.Pointer = NULL; 339 AcpiEvaluateObject(h, "HINF", NULL, &buf); 340 res = (ACPI_OBJECT *)buf.Pointer; 341 if (res->Type != ACPI_TYPE_INTEGER) { 342 device_printf(sc->dev, "HINF returned non-integer\n"); 343 goto end; 344 } 345 val = res->Integer.Value; 346 #ifdef ACPI_PANASONIC_DEBUG 347 device_printf(sc->dev, "%s button Fn+F%d\n", 348 (val & 0x80) ? "Pressed" : "Released", 349 (int)(val & 0x7f)); 350 #endif 351 if ((val & 0x7f) > 0 && (val & 0x7f) < 11) { 352 *arg = val; 353 status = 0; 354 } 355 end: 356 if (buf.Pointer) 357 AcpiOsFree(buf.Pointer); 358 359 return (status); 360 } 361 362 static void 363 acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc, ACPI_HANDLE h, 364 UINT32 key) 365 { 366 int arg; 367 368 ACPI_SERIAL_ASSERT(panasonic); 369 switch (key) { 370 case 1: 371 /* Decrease LCD brightness. */ 372 hkey_lcd_brightness(h, HKEY_GET, &arg); 373 arg -= lcd_brightness_max / HKEY_LCD_BRIGHTNESS_DIV; 374 if (arg < 0) 375 arg = 0; 376 else if (arg > lcd_brightness_max) 377 arg = lcd_brightness_max; 378 hkey_lcd_brightness(h, HKEY_SET, &arg); 379 break; 380 case 2: 381 /* Increase LCD brightness. */ 382 hkey_lcd_brightness(h, HKEY_GET, &arg); 383 arg += lcd_brightness_max / HKEY_LCD_BRIGHTNESS_DIV; 384 if (arg < 0) 385 arg = 0; 386 else if (arg > lcd_brightness_max) 387 arg = lcd_brightness_max; 388 hkey_lcd_brightness(h, HKEY_SET, &arg); 389 break; 390 case 4: 391 /* Toggle sound mute. */ 392 hkey_sound_mute(h, HKEY_GET, &arg); 393 if (arg) 394 arg = 0; 395 else 396 arg = 1; 397 hkey_sound_mute(h, HKEY_SET, &arg); 398 break; 399 } 400 } 401 402 static void 403 acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify, void *context) 404 { 405 struct acpi_panasonic_softc *sc; 406 UINT32 key; 407 408 sc = (struct acpi_panasonic_softc *)context; 409 410 switch (notify) { 411 case 0x80: 412 ACPI_SERIAL_BEGIN(panasonic); 413 if (acpi_panasonic_hkey_event(sc, h, &key) == 0) { 414 acpi_panasonic_hkey_action(sc, h, key); 415 acpi_UserNotify("Panasonic", h, (uint8_t)key); 416 } 417 ACPI_SERIAL_END(panasonic); 418 break; 419 default: 420 device_printf(sc->dev, "unknown notify: %#x\n", notify); 421 break; 422 } 423 } 424