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 87 /* Table of sysctl names and HKEY functions to call. */ 88 static 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 99 static 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 107 static driver_t acpi_panasonic_driver = { 108 "acpi_panasonic", 109 acpi_panasonic_methods, 110 sizeof(struct acpi_panasonic_softc), 111 }; 112 113 static devclass_t acpi_panasonic_devclass; 114 115 DRIVER_MODULE(acpi_panasonic, acpi, acpi_panasonic_driver, 116 acpi_panasonic_devclass, 0, 0); 117 MODULE_DEPEND(acpi_panasonic, acpi, 1, 1, 1); 118 119 static int 120 acpi_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 133 static int 134 acpi_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 183 static int 184 acpi_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 200 static int 201 acpi_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 228 static ACPI_INTEGER 229 acpi_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 247 static void 248 acpi_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 262 static int 263 hkey_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 280 static int 281 hkey_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 298 static int 299 hkey_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 316 static int 317 acpi_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 } 345 end: 346 if (buf.Pointer) 347 AcpiOsFree(buf.Pointer); 348 349 return (status); 350 } 351 352 static void 353 acpi_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 391 static void 392 acpi_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