41#include <dev/acpica/acpivar.h> 42 43#define _COMPONENT ACPI_OEM 44ACPI_MODULE_NAME("Panasonic") 45 46/* Debug */ 47#undef ACPI_PANASONIC_DEBUG 48 49/* Operations */ 50#define HKEY_SET 0 51#define HKEY_GET 1 52 53/* Functions */ 54#define HKEY_REG_LCD_BRIGHTNESS_MAX_AC 0x02 55#define HKEY_REG_LCD_BRIGHTNESS_MIN_AC 0x03 56#define HKEY_REG_LCD_BRIGHTNESS_AC 0x04 57#define HKEY_REG_LCD_BRIGHTNESS_MAX_DC 0x05 58#define HKEY_REG_LCD_BRIGHTNESS_MIN_DC 0x06 59#define HKEY_REG_LCD_BRIGHTNESS_DC 0x07 60#define HKEY_REG_SOUND_MUTE 0x08 61 62/* Field definitions */ 63#define HKEY_LCD_BRIGHTNESS_BITS 4 64#define HKEY_LCD_BRIGHTNESS_DIV ((1 << HKEY_LCD_BRIGHTNESS_BITS) - 1) 65 66struct acpi_panasonic_softc { 67 device_t dev; 68 ACPI_HANDLE handle; 69 70 struct sysctl_ctx_list sysctl_ctx; 71 struct sysctl_oid *sysctl_tree; 72 73 eventhandler_tag power_evh; 74}; 75 76/* Prototype for HKEY functions for getting/setting a value. */ 77typedef int hkey_fn_t(ACPI_HANDLE, int, UINT32 *); 78 79static int acpi_panasonic_probe(device_t dev); 80static int acpi_panasonic_attach(device_t dev); 81static int acpi_panasonic_detach(device_t dev); 82static int acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS); 83static ACPI_INTEGER acpi_panasonic_sinf(ACPI_HANDLE h, ACPI_INTEGER index); 84static void acpi_panasonic_sset(ACPI_HANDLE h, ACPI_INTEGER index, 85 ACPI_INTEGER val); 86static int acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc, 87 ACPI_HANDLE h, UINT32 *arg); 88static void acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc, 89 ACPI_HANDLE h, UINT32 key); 90static void acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify, 91 void *context); 92static void acpi_panasonic_power_profile(void *arg); 93 94static hkey_fn_t hkey_lcd_brightness_max; 95static hkey_fn_t hkey_lcd_brightness_min; 96static hkey_fn_t hkey_lcd_brightness; 97static hkey_fn_t hkey_sound_mute; 98ACPI_SERIAL_DECL(panasonic, "ACPI Panasonic extras"); 99 100/* Table of sysctl names and HKEY functions to call. */ 101static struct { 102 char *name; 103 hkey_fn_t *handler; 104} sysctl_table[] = { 105 /* name, handler */ 106 {"lcd_brightness_max", hkey_lcd_brightness_max}, 107 {"lcd_brightness_min", hkey_lcd_brightness_min}, 108 {"lcd_brightness", hkey_lcd_brightness}, 109 {"sound_mute", hkey_sound_mute}, 110 {NULL, NULL} 111}; 112 113static device_method_t acpi_panasonic_methods[] = { 114 DEVMETHOD(device_probe, acpi_panasonic_probe), 115 DEVMETHOD(device_attach, acpi_panasonic_attach), 116 DEVMETHOD(device_detach, acpi_panasonic_detach), 117 118 {0, 0} 119}; 120 121static driver_t acpi_panasonic_driver = { 122 "acpi_panasonic", 123 acpi_panasonic_methods, 124 sizeof(struct acpi_panasonic_softc), 125}; 126 127static devclass_t acpi_panasonic_devclass; 128 129DRIVER_MODULE(acpi_panasonic, acpi, acpi_panasonic_driver, 130 acpi_panasonic_devclass, 0, 0); 131MODULE_DEPEND(acpi_panasonic, acpi, 1, 1, 1); 132 133static int 134acpi_panasonic_probe(device_t dev) 135{ 136 static char *mat_ids[] = { "MAT0019", NULL }; 137 138 if (acpi_disabled("panasonic") || 139 ACPI_ID_PROBE(device_get_parent(dev), dev, mat_ids) == NULL || 140 device_get_unit(dev) != 0) 141 return (ENXIO); 142 143 device_set_desc(dev, "Panasonic Notebook Hotkeys"); 144 return (0); 145} 146 147static int 148acpi_panasonic_attach(device_t dev) 149{ 150 struct acpi_panasonic_softc *sc; 151 struct acpi_softc *acpi_sc; 152 ACPI_STATUS status; 153 int i; 154 155 sc = device_get_softc(dev); 156 sc->dev = dev; 157 sc->handle = acpi_get_handle(dev); 158 159 acpi_sc = acpi_device_get_parent_softc(dev); 160 161 /* Build sysctl tree */ 162 sysctl_ctx_init(&sc->sysctl_ctx); 163 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 164 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, 165 "panasonic", CTLFLAG_RD, 0, ""); 166 for (i = 0; sysctl_table[i].name != NULL; i++) { 167 SYSCTL_ADD_PROC(&sc->sysctl_ctx, 168 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 169 sysctl_table[i].name, 170 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, 171 sc, i, acpi_panasonic_sysctl, "I", ""); 172 } 173 174#if 0 175 /* Activate hotkeys */ 176 status = AcpiEvaluateObject(sc->handle, "", NULL, NULL); 177 if (ACPI_FAILURE(status)) { 178 device_printf(dev, "enable FN keys failed\n"); 179 sysctl_ctx_free(&sc->sysctl_ctx); 180 return (ENXIO); 181 } 182#endif 183 184 /* Handle notifies */ 185 status = AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 186 acpi_panasonic_notify, sc); 187 if (ACPI_FAILURE(status)) { 188 device_printf(dev, "couldn't install notify handler - %s\n", 189 AcpiFormatException(status)); 190 sysctl_ctx_free(&sc->sysctl_ctx); 191 return (ENXIO); 192 } 193 194 /* Install power profile event handler */ 195 sc->power_evh = EVENTHANDLER_REGISTER(power_profile_change, 196 acpi_panasonic_power_profile, sc->handle, 0); 197 198 return (0); 199} 200 201static int 202acpi_panasonic_detach(device_t dev) 203{ 204 struct acpi_panasonic_softc *sc; 205 206 sc = device_get_softc(dev); 207 208 /* Remove power profile event handler */ 209 EVENTHANDLER_DEREGISTER(power_profile_change, sc->power_evh); 210 211 /* Remove notify handler */ 212 AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 213 acpi_panasonic_notify); 214 215 /* Free sysctl tree */ 216 sysctl_ctx_free(&sc->sysctl_ctx); 217 218 return (0); 219} 220 221static int 222acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS) 223{ 224 struct acpi_panasonic_softc *sc; 225 UINT32 arg; 226 int function, error; 227 hkey_fn_t *handler; 228 229 sc = (struct acpi_panasonic_softc *)oidp->oid_arg1; 230 function = oidp->oid_arg2; 231 handler = sysctl_table[function].handler; 232 233 /* Get the current value from the appropriate function. */ 234 ACPI_SERIAL_BEGIN(panasonic); 235 error = handler(sc->handle, HKEY_GET, &arg); 236 if (error != 0) 237 goto out; 238 239 /* Send the current value to the user and return if no new value. */ 240 error = sysctl_handle_int(oidp, &arg, 0, req); 241 if (error != 0 || req->newptr == NULL) 242 goto out; 243 244 /* Set the new value via the appropriate function. */ 245 error = handler(sc->handle, HKEY_SET, &arg); 246 247out: 248 ACPI_SERIAL_END(panasonic); 249 return (error); 250} 251 252static ACPI_INTEGER 253acpi_panasonic_sinf(ACPI_HANDLE h, ACPI_INTEGER index) 254{ 255 ACPI_BUFFER buf; 256 ACPI_OBJECT *res; 257 ACPI_INTEGER ret; 258 259 ACPI_SERIAL_ASSERT(panasonic); 260 ret = -1; 261 buf.Length = ACPI_ALLOCATE_BUFFER; 262 buf.Pointer = NULL; 263 AcpiEvaluateObject(h, "SINF", NULL, &buf); 264 res = (ACPI_OBJECT *)buf.Pointer; 265 if (res->Type == ACPI_TYPE_PACKAGE) 266 ret = res->Package.Elements[index].Integer.Value; 267 AcpiOsFree(buf.Pointer); 268 269 return (ret); 270} 271 272static void 273acpi_panasonic_sset(ACPI_HANDLE h, ACPI_INTEGER index, ACPI_INTEGER val) 274{ 275 ACPI_OBJECT_LIST args; 276 ACPI_OBJECT obj[2]; 277 278 ACPI_SERIAL_ASSERT(panasonic); 279 obj[0].Type = ACPI_TYPE_INTEGER; 280 obj[0].Integer.Value = index; 281 obj[1].Type = ACPI_TYPE_INTEGER; 282 obj[1].Integer.Value = val; 283 args.Count = 2; 284 args.Pointer = obj; 285 AcpiEvaluateObject(h, "SSET", &args, NULL); 286} 287 288static int 289hkey_lcd_brightness_max(ACPI_HANDLE h, int op, UINT32 *val) 290{ 291 int reg; 292 293 ACPI_SERIAL_ASSERT(panasonic); 294 reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ? 295 HKEY_REG_LCD_BRIGHTNESS_MAX_AC : HKEY_REG_LCD_BRIGHTNESS_MAX_DC; 296 297 switch (op) { 298 case HKEY_SET: 299 return (EPERM); 300 break; 301 case HKEY_GET: 302 *val = acpi_panasonic_sinf(h, reg); 303 break; 304 } 305 306 return (0); 307} 308 309static int 310hkey_lcd_brightness_min(ACPI_HANDLE h, int op, UINT32 *val) 311{ 312 int reg; 313 314 ACPI_SERIAL_ASSERT(panasonic); 315 reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ? 316 HKEY_REG_LCD_BRIGHTNESS_MIN_AC : HKEY_REG_LCD_BRIGHTNESS_MIN_DC; 317 318 switch (op) { 319 case HKEY_SET: 320 return (EPERM); 321 break; 322 case HKEY_GET: 323 *val = acpi_panasonic_sinf(h, reg); 324 break; 325 } 326 327 return (0); 328} 329 330static int 331hkey_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *val) 332{ 333 int reg; 334 UINT32 max, min; 335 336 reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ? 337 HKEY_REG_LCD_BRIGHTNESS_AC : HKEY_REG_LCD_BRIGHTNESS_DC; 338 339 ACPI_SERIAL_ASSERT(panasonic); 340 switch (op) { 341 case HKEY_SET: 342 hkey_lcd_brightness_max(h, HKEY_GET, &max); 343 hkey_lcd_brightness_min(h, HKEY_GET, &min); 344 if (*val < min || *val > max) 345 return (EINVAL); 346 acpi_panasonic_sset(h, reg, *val); 347 break; 348 case HKEY_GET: 349 *val = acpi_panasonic_sinf(h, reg); 350 break; 351 } 352 353 return (0); 354} 355 356static int 357hkey_sound_mute(ACPI_HANDLE h, int op, UINT32 *val) 358{ 359 360 ACPI_SERIAL_ASSERT(panasonic); 361 switch (op) { 362 case HKEY_SET: 363 if (*val != 0 && *val != 1) 364 return (EINVAL); 365 acpi_panasonic_sset(h, HKEY_REG_SOUND_MUTE, *val); 366 break; 367 case HKEY_GET: 368 *val = acpi_panasonic_sinf(h, HKEY_REG_SOUND_MUTE); 369 break; 370 } 371 372 return (0); 373} 374 375static int 376acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc, ACPI_HANDLE h, 377 UINT32 *arg) 378{ 379 ACPI_BUFFER buf; 380 ACPI_OBJECT *res; 381 ACPI_INTEGER val; 382 int status; 383 384 ACPI_SERIAL_ASSERT(panasonic); 385 status = ENXIO; 386 387 buf.Length = ACPI_ALLOCATE_BUFFER; 388 buf.Pointer = NULL; 389 AcpiEvaluateObject(h, "HINF", NULL, &buf); 390 res = (ACPI_OBJECT *)buf.Pointer; 391 if (res->Type != ACPI_TYPE_INTEGER) { 392 device_printf(sc->dev, "HINF returned non-integer\n"); 393 goto end; 394 } 395 val = res->Integer.Value; 396#ifdef ACPI_PANASONIC_DEBUG 397 device_printf(sc->dev, "%s button Fn+F%d\n", 398 (val & 0x80) ? "Pressed" : "Released", 399 (int)(val & 0x7f)); 400#endif 401 if ((val & 0x7f) > 0 && (val & 0x7f) < 11) { 402 *arg = val; 403 status = 0; 404 } 405end: 406 if (buf.Pointer) 407 AcpiOsFree(buf.Pointer); 408 409 return (status); 410} 411 412static void 413acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc, ACPI_HANDLE h, 414 UINT32 key) 415{ 416 int arg, max, min; 417 418 ACPI_SERIAL_ASSERT(panasonic); 419 switch (key) { 420 case 1: 421 /* Decrease LCD brightness. */ 422 hkey_lcd_brightness_max(h, HKEY_GET, &max); 423 hkey_lcd_brightness_min(h, HKEY_GET, &min); 424 hkey_lcd_brightness(h, HKEY_GET, &arg); 425 arg -= max / HKEY_LCD_BRIGHTNESS_DIV; 426 if (arg < min) 427 arg = min; 428 else if (arg > max) 429 arg = max; 430 hkey_lcd_brightness(h, HKEY_SET, &arg); 431 break; 432 case 2: 433 /* Increase LCD brightness. */ 434 hkey_lcd_brightness_max(h, HKEY_GET, &max); 435 hkey_lcd_brightness_min(h, HKEY_GET, &min); 436 hkey_lcd_brightness(h, HKEY_GET, &arg); 437 arg += max / HKEY_LCD_BRIGHTNESS_DIV; 438 if (arg < min) 439 arg = min; 440 else if (arg > max) 441 arg = max; 442 hkey_lcd_brightness(h, HKEY_SET, &arg); 443 break; 444 case 4: 445 /* Toggle sound mute. */ 446 hkey_sound_mute(h, HKEY_GET, &arg); 447 if (arg) 448 arg = 0; 449 else 450 arg = 1; 451 hkey_sound_mute(h, HKEY_SET, &arg); 452 break; 453 } 454} 455 456static void 457acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify, void *context) 458{ 459 struct acpi_panasonic_softc *sc; 460 UINT32 key; 461 462 sc = (struct acpi_panasonic_softc *)context; 463 464 switch (notify) { 465 case 0x80: 466 ACPI_SERIAL_BEGIN(panasonic); 467 if (acpi_panasonic_hkey_event(sc, h, &key) == 0) { 468 acpi_panasonic_hkey_action(sc, h, key); 469 acpi_UserNotify("Panasonic", h, (uint8_t)key); 470 } 471 ACPI_SERIAL_END(panasonic); 472 break; 473 default: 474 device_printf(sc->dev, "unknown notify: %#x\n", notify); 475 break; 476 } 477} 478 479static void 480acpi_panasonic_power_profile(void *arg) 481{ 482 ACPI_HANDLE handle; 483 UINT32 brightness; 484 485 handle = (ACPI_HANDLE)arg; 486 487 /* Reset current brightness according to new power state. */ 488 ACPI_SERIAL_BEGIN(panasonic); 489 hkey_lcd_brightness(handle, HKEY_GET, &brightness); 490 hkey_lcd_brightness(handle, HKEY_SET, &brightness); 491 ACPI_SERIAL_END(panasonic); 492}
| 41#include <dev/acpica/acpivar.h> 42 43#define _COMPONENT ACPI_OEM 44ACPI_MODULE_NAME("Panasonic") 45 46/* Debug */ 47#undef ACPI_PANASONIC_DEBUG 48 49/* Operations */ 50#define HKEY_SET 0 51#define HKEY_GET 1 52 53/* Functions */ 54#define HKEY_REG_LCD_BRIGHTNESS_MAX_AC 0x02 55#define HKEY_REG_LCD_BRIGHTNESS_MIN_AC 0x03 56#define HKEY_REG_LCD_BRIGHTNESS_AC 0x04 57#define HKEY_REG_LCD_BRIGHTNESS_MAX_DC 0x05 58#define HKEY_REG_LCD_BRIGHTNESS_MIN_DC 0x06 59#define HKEY_REG_LCD_BRIGHTNESS_DC 0x07 60#define HKEY_REG_SOUND_MUTE 0x08 61 62/* Field definitions */ 63#define HKEY_LCD_BRIGHTNESS_BITS 4 64#define HKEY_LCD_BRIGHTNESS_DIV ((1 << HKEY_LCD_BRIGHTNESS_BITS) - 1) 65 66struct acpi_panasonic_softc { 67 device_t dev; 68 ACPI_HANDLE handle; 69 70 struct sysctl_ctx_list sysctl_ctx; 71 struct sysctl_oid *sysctl_tree; 72 73 eventhandler_tag power_evh; 74}; 75 76/* Prototype for HKEY functions for getting/setting a value. */ 77typedef int hkey_fn_t(ACPI_HANDLE, int, UINT32 *); 78 79static int acpi_panasonic_probe(device_t dev); 80static int acpi_panasonic_attach(device_t dev); 81static int acpi_panasonic_detach(device_t dev); 82static int acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS); 83static ACPI_INTEGER acpi_panasonic_sinf(ACPI_HANDLE h, ACPI_INTEGER index); 84static void acpi_panasonic_sset(ACPI_HANDLE h, ACPI_INTEGER index, 85 ACPI_INTEGER val); 86static int acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc, 87 ACPI_HANDLE h, UINT32 *arg); 88static void acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc, 89 ACPI_HANDLE h, UINT32 key); 90static void acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify, 91 void *context); 92static void acpi_panasonic_power_profile(void *arg); 93 94static hkey_fn_t hkey_lcd_brightness_max; 95static hkey_fn_t hkey_lcd_brightness_min; 96static hkey_fn_t hkey_lcd_brightness; 97static hkey_fn_t hkey_sound_mute; 98ACPI_SERIAL_DECL(panasonic, "ACPI Panasonic extras"); 99 100/* Table of sysctl names and HKEY functions to call. */ 101static struct { 102 char *name; 103 hkey_fn_t *handler; 104} sysctl_table[] = { 105 /* name, handler */ 106 {"lcd_brightness_max", hkey_lcd_brightness_max}, 107 {"lcd_brightness_min", hkey_lcd_brightness_min}, 108 {"lcd_brightness", hkey_lcd_brightness}, 109 {"sound_mute", hkey_sound_mute}, 110 {NULL, NULL} 111}; 112 113static device_method_t acpi_panasonic_methods[] = { 114 DEVMETHOD(device_probe, acpi_panasonic_probe), 115 DEVMETHOD(device_attach, acpi_panasonic_attach), 116 DEVMETHOD(device_detach, acpi_panasonic_detach), 117 118 {0, 0} 119}; 120 121static driver_t acpi_panasonic_driver = { 122 "acpi_panasonic", 123 acpi_panasonic_methods, 124 sizeof(struct acpi_panasonic_softc), 125}; 126 127static devclass_t acpi_panasonic_devclass; 128 129DRIVER_MODULE(acpi_panasonic, acpi, acpi_panasonic_driver, 130 acpi_panasonic_devclass, 0, 0); 131MODULE_DEPEND(acpi_panasonic, acpi, 1, 1, 1); 132 133static int 134acpi_panasonic_probe(device_t dev) 135{ 136 static char *mat_ids[] = { "MAT0019", NULL }; 137 138 if (acpi_disabled("panasonic") || 139 ACPI_ID_PROBE(device_get_parent(dev), dev, mat_ids) == NULL || 140 device_get_unit(dev) != 0) 141 return (ENXIO); 142 143 device_set_desc(dev, "Panasonic Notebook Hotkeys"); 144 return (0); 145} 146 147static int 148acpi_panasonic_attach(device_t dev) 149{ 150 struct acpi_panasonic_softc *sc; 151 struct acpi_softc *acpi_sc; 152 ACPI_STATUS status; 153 int i; 154 155 sc = device_get_softc(dev); 156 sc->dev = dev; 157 sc->handle = acpi_get_handle(dev); 158 159 acpi_sc = acpi_device_get_parent_softc(dev); 160 161 /* Build sysctl tree */ 162 sysctl_ctx_init(&sc->sysctl_ctx); 163 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx, 164 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO, 165 "panasonic", CTLFLAG_RD, 0, ""); 166 for (i = 0; sysctl_table[i].name != NULL; i++) { 167 SYSCTL_ADD_PROC(&sc->sysctl_ctx, 168 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 169 sysctl_table[i].name, 170 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY, 171 sc, i, acpi_panasonic_sysctl, "I", ""); 172 } 173 174#if 0 175 /* Activate hotkeys */ 176 status = AcpiEvaluateObject(sc->handle, "", NULL, NULL); 177 if (ACPI_FAILURE(status)) { 178 device_printf(dev, "enable FN keys failed\n"); 179 sysctl_ctx_free(&sc->sysctl_ctx); 180 return (ENXIO); 181 } 182#endif 183 184 /* Handle notifies */ 185 status = AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 186 acpi_panasonic_notify, sc); 187 if (ACPI_FAILURE(status)) { 188 device_printf(dev, "couldn't install notify handler - %s\n", 189 AcpiFormatException(status)); 190 sysctl_ctx_free(&sc->sysctl_ctx); 191 return (ENXIO); 192 } 193 194 /* Install power profile event handler */ 195 sc->power_evh = EVENTHANDLER_REGISTER(power_profile_change, 196 acpi_panasonic_power_profile, sc->handle, 0); 197 198 return (0); 199} 200 201static int 202acpi_panasonic_detach(device_t dev) 203{ 204 struct acpi_panasonic_softc *sc; 205 206 sc = device_get_softc(dev); 207 208 /* Remove power profile event handler */ 209 EVENTHANDLER_DEREGISTER(power_profile_change, sc->power_evh); 210 211 /* Remove notify handler */ 212 AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 213 acpi_panasonic_notify); 214 215 /* Free sysctl tree */ 216 sysctl_ctx_free(&sc->sysctl_ctx); 217 218 return (0); 219} 220 221static int 222acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS) 223{ 224 struct acpi_panasonic_softc *sc; 225 UINT32 arg; 226 int function, error; 227 hkey_fn_t *handler; 228 229 sc = (struct acpi_panasonic_softc *)oidp->oid_arg1; 230 function = oidp->oid_arg2; 231 handler = sysctl_table[function].handler; 232 233 /* Get the current value from the appropriate function. */ 234 ACPI_SERIAL_BEGIN(panasonic); 235 error = handler(sc->handle, HKEY_GET, &arg); 236 if (error != 0) 237 goto out; 238 239 /* Send the current value to the user and return if no new value. */ 240 error = sysctl_handle_int(oidp, &arg, 0, req); 241 if (error != 0 || req->newptr == NULL) 242 goto out; 243 244 /* Set the new value via the appropriate function. */ 245 error = handler(sc->handle, HKEY_SET, &arg); 246 247out: 248 ACPI_SERIAL_END(panasonic); 249 return (error); 250} 251 252static ACPI_INTEGER 253acpi_panasonic_sinf(ACPI_HANDLE h, ACPI_INTEGER index) 254{ 255 ACPI_BUFFER buf; 256 ACPI_OBJECT *res; 257 ACPI_INTEGER ret; 258 259 ACPI_SERIAL_ASSERT(panasonic); 260 ret = -1; 261 buf.Length = ACPI_ALLOCATE_BUFFER; 262 buf.Pointer = NULL; 263 AcpiEvaluateObject(h, "SINF", NULL, &buf); 264 res = (ACPI_OBJECT *)buf.Pointer; 265 if (res->Type == ACPI_TYPE_PACKAGE) 266 ret = res->Package.Elements[index].Integer.Value; 267 AcpiOsFree(buf.Pointer); 268 269 return (ret); 270} 271 272static void 273acpi_panasonic_sset(ACPI_HANDLE h, ACPI_INTEGER index, ACPI_INTEGER val) 274{ 275 ACPI_OBJECT_LIST args; 276 ACPI_OBJECT obj[2]; 277 278 ACPI_SERIAL_ASSERT(panasonic); 279 obj[0].Type = ACPI_TYPE_INTEGER; 280 obj[0].Integer.Value = index; 281 obj[1].Type = ACPI_TYPE_INTEGER; 282 obj[1].Integer.Value = val; 283 args.Count = 2; 284 args.Pointer = obj; 285 AcpiEvaluateObject(h, "SSET", &args, NULL); 286} 287 288static int 289hkey_lcd_brightness_max(ACPI_HANDLE h, int op, UINT32 *val) 290{ 291 int reg; 292 293 ACPI_SERIAL_ASSERT(panasonic); 294 reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ? 295 HKEY_REG_LCD_BRIGHTNESS_MAX_AC : HKEY_REG_LCD_BRIGHTNESS_MAX_DC; 296 297 switch (op) { 298 case HKEY_SET: 299 return (EPERM); 300 break; 301 case HKEY_GET: 302 *val = acpi_panasonic_sinf(h, reg); 303 break; 304 } 305 306 return (0); 307} 308 309static int 310hkey_lcd_brightness_min(ACPI_HANDLE h, int op, UINT32 *val) 311{ 312 int reg; 313 314 ACPI_SERIAL_ASSERT(panasonic); 315 reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ? 316 HKEY_REG_LCD_BRIGHTNESS_MIN_AC : HKEY_REG_LCD_BRIGHTNESS_MIN_DC; 317 318 switch (op) { 319 case HKEY_SET: 320 return (EPERM); 321 break; 322 case HKEY_GET: 323 *val = acpi_panasonic_sinf(h, reg); 324 break; 325 } 326 327 return (0); 328} 329 330static int 331hkey_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *val) 332{ 333 int reg; 334 UINT32 max, min; 335 336 reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ? 337 HKEY_REG_LCD_BRIGHTNESS_AC : HKEY_REG_LCD_BRIGHTNESS_DC; 338 339 ACPI_SERIAL_ASSERT(panasonic); 340 switch (op) { 341 case HKEY_SET: 342 hkey_lcd_brightness_max(h, HKEY_GET, &max); 343 hkey_lcd_brightness_min(h, HKEY_GET, &min); 344 if (*val < min || *val > max) 345 return (EINVAL); 346 acpi_panasonic_sset(h, reg, *val); 347 break; 348 case HKEY_GET: 349 *val = acpi_panasonic_sinf(h, reg); 350 break; 351 } 352 353 return (0); 354} 355 356static int 357hkey_sound_mute(ACPI_HANDLE h, int op, UINT32 *val) 358{ 359 360 ACPI_SERIAL_ASSERT(panasonic); 361 switch (op) { 362 case HKEY_SET: 363 if (*val != 0 && *val != 1) 364 return (EINVAL); 365 acpi_panasonic_sset(h, HKEY_REG_SOUND_MUTE, *val); 366 break; 367 case HKEY_GET: 368 *val = acpi_panasonic_sinf(h, HKEY_REG_SOUND_MUTE); 369 break; 370 } 371 372 return (0); 373} 374 375static int 376acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc, ACPI_HANDLE h, 377 UINT32 *arg) 378{ 379 ACPI_BUFFER buf; 380 ACPI_OBJECT *res; 381 ACPI_INTEGER val; 382 int status; 383 384 ACPI_SERIAL_ASSERT(panasonic); 385 status = ENXIO; 386 387 buf.Length = ACPI_ALLOCATE_BUFFER; 388 buf.Pointer = NULL; 389 AcpiEvaluateObject(h, "HINF", NULL, &buf); 390 res = (ACPI_OBJECT *)buf.Pointer; 391 if (res->Type != ACPI_TYPE_INTEGER) { 392 device_printf(sc->dev, "HINF returned non-integer\n"); 393 goto end; 394 } 395 val = res->Integer.Value; 396#ifdef ACPI_PANASONIC_DEBUG 397 device_printf(sc->dev, "%s button Fn+F%d\n", 398 (val & 0x80) ? "Pressed" : "Released", 399 (int)(val & 0x7f)); 400#endif 401 if ((val & 0x7f) > 0 && (val & 0x7f) < 11) { 402 *arg = val; 403 status = 0; 404 } 405end: 406 if (buf.Pointer) 407 AcpiOsFree(buf.Pointer); 408 409 return (status); 410} 411 412static void 413acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc, ACPI_HANDLE h, 414 UINT32 key) 415{ 416 int arg, max, min; 417 418 ACPI_SERIAL_ASSERT(panasonic); 419 switch (key) { 420 case 1: 421 /* Decrease LCD brightness. */ 422 hkey_lcd_brightness_max(h, HKEY_GET, &max); 423 hkey_lcd_brightness_min(h, HKEY_GET, &min); 424 hkey_lcd_brightness(h, HKEY_GET, &arg); 425 arg -= max / HKEY_LCD_BRIGHTNESS_DIV; 426 if (arg < min) 427 arg = min; 428 else if (arg > max) 429 arg = max; 430 hkey_lcd_brightness(h, HKEY_SET, &arg); 431 break; 432 case 2: 433 /* Increase LCD brightness. */ 434 hkey_lcd_brightness_max(h, HKEY_GET, &max); 435 hkey_lcd_brightness_min(h, HKEY_GET, &min); 436 hkey_lcd_brightness(h, HKEY_GET, &arg); 437 arg += max / HKEY_LCD_BRIGHTNESS_DIV; 438 if (arg < min) 439 arg = min; 440 else if (arg > max) 441 arg = max; 442 hkey_lcd_brightness(h, HKEY_SET, &arg); 443 break; 444 case 4: 445 /* Toggle sound mute. */ 446 hkey_sound_mute(h, HKEY_GET, &arg); 447 if (arg) 448 arg = 0; 449 else 450 arg = 1; 451 hkey_sound_mute(h, HKEY_SET, &arg); 452 break; 453 } 454} 455 456static void 457acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify, void *context) 458{ 459 struct acpi_panasonic_softc *sc; 460 UINT32 key; 461 462 sc = (struct acpi_panasonic_softc *)context; 463 464 switch (notify) { 465 case 0x80: 466 ACPI_SERIAL_BEGIN(panasonic); 467 if (acpi_panasonic_hkey_event(sc, h, &key) == 0) { 468 acpi_panasonic_hkey_action(sc, h, key); 469 acpi_UserNotify("Panasonic", h, (uint8_t)key); 470 } 471 ACPI_SERIAL_END(panasonic); 472 break; 473 default: 474 device_printf(sc->dev, "unknown notify: %#x\n", notify); 475 break; 476 } 477} 478 479static void 480acpi_panasonic_power_profile(void *arg) 481{ 482 ACPI_HANDLE handle; 483 UINT32 brightness; 484 485 handle = (ACPI_HANDLE)arg; 486 487 /* Reset current brightness according to new power state. */ 488 ACPI_SERIAL_BEGIN(panasonic); 489 hkey_lcd_brightness(handle, HKEY_GET, &brightness); 490 hkey_lcd_brightness(handle, HKEY_SET, &brightness); 491 ACPI_SERIAL_END(panasonic); 492}
|