1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 /* 25 * Copyright 2015 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org> 26 */ 27 28 /* 29 * Solaris x86 Generic ACPI Video Extensions Hotkey driver 30 */ 31 #include <sys/hotkey_drv.h> 32 #include <sys/smbios.h> 33 34 /* 35 * Vendor specific hotkey support list 36 * 1. Toshiba: acpi_toshiba 37 */ 38 struct vendor_hotkey_drv vendor_hotkey_drv_list[] = { 39 /* vendor, module name, enable? */ 40 {"Toshiba", "acpi_toshiba", B_TRUE}, 41 /* Terminator */ 42 {NULL, NULL, B_FALSE} 43 }; 44 45 enum vga_output_type { 46 OUTPUT_OTHER, 47 OUTPUT_CRT, 48 OUTPUT_TV, 49 OUTPUT_DVI, 50 OUTPUT_LCD 51 }; 52 53 struct acpi_video_output { 54 struct acpi_drv_dev dev; 55 uint32_t adr; 56 enum vga_output_type type; 57 struct acpi_video_output *next; 58 }; 59 60 struct acpi_video_brightness { 61 struct acpi_drv_dev dev; 62 uint32_t adr; 63 uint32_t nlevel; 64 int *levels; 65 int cur_level; 66 uint32_t cur_level_index; 67 uint32_t output_index; 68 struct acpi_video_brightness *next; 69 }; 70 71 struct acpi_video_switch { 72 struct acpi_drv_dev dev; 73 struct acpi_video_switch *next; 74 }; 75 76 /* ACPI video extension hotkey for video switch and brightness control */ 77 static struct acpi_video { 78 struct acpi_video_output *vid_outputs; 79 uint32_t total_outputs; 80 struct acpi_video_brightness *vid_brightness; 81 uint32_t total_brightness; 82 struct acpi_video_switch *vid_switch; 83 uint32_t total_switch; 84 } acpi_video_hotkey; 85 86 int hotkey_drv_debug = 0; 87 88 static struct acpi_video_smbios_info { 89 char *manufacturer; 90 char *product; 91 } acpi_brightness_get_blacklist[] = { 92 { /* Dell AdamoXPS laptop */ 93 "Dell Inc.", 94 "Adamo XPS" 95 }, 96 { /* termination entry */ 97 NULL, 98 NULL 99 } 100 }; 101 /* 102 * -1 = check acpi_brightness_get_blacklist[]. 103 * 0 = enable brightness get. 104 * 1 = disable brightness get. 105 */ 106 int acpi_brightness_get_disable = -1; 107 108 109 #define ACPI_METHOD_DOS "_DOS" 110 #define ACPI_METHOD_DOD "_DOD" 111 112 #define ACPI_DEVNAME_CRT "CRT" 113 #define ACPI_DEVNAME_LCD "LCD" 114 #define ACPI_DEVNAME_TV "TV" 115 #define ACPI_METHOD_ADR "_ADR" 116 #define ACPI_METHOD_DDC "_DDC" 117 #define ACPI_METHOD_DCS "_DCS" 118 #define ACPI_METHOD_DGS "_DGS" 119 #define ACPI_METHOD_DSS "_DSS" 120 121 #define VIDEO_NOTIFY_SWITCH 0x80 122 #define VIDEO_NOTIFY_SWITCH_STATUS 0x81 123 #define VIDEO_NOTIFY_SWITCH_CYCLE 0x82 124 #define VIDEO_NOTIFY_SWITCH_NEXT 0x83 125 #define VIDEO_NOTIFY_SWITCH_PREV 0x84 126 127 #define VIDEO_NOTIFY_BRIGHTNESS_CYCLE 0x85 128 #define VIDEO_NOTIFY_BRIGHTNESS_INC 0x86 129 #define VIDEO_NOTIFY_BRIGHTNESS_DEC 0x87 130 #define VIDEO_NOTIFY_BRIGHTNESS_ZERO 0x88 131 132 /* Output device status */ 133 #define ACPI_DRV_DCS_CONNECTOR_EXIST (1 << 0) 134 #define ACPI_DRV_DCS_ACTIVE (1 << 1) 135 #define ACPI_DRV_DCS_READY (1 << 2) 136 #define ACPI_DRV_DCS_FUNCTIONAL (1 << 3) 137 #define ACPI_DRV_DCS_ATTACHED (1 << 4) 138 139 /* _DOS default value is 1 */ 140 /* _DOS bit 1:0 */ 141 #define VIDEO_POLICY_SWITCH_OS 0x0 142 #define VIDEO_POLICY_SWITCH_BIOS 0x1 143 #define VIDEO_POLICY_SWITCH_LOCKED 0x2 144 #define VIDEO_POLICY_SWITCH_OS_EVENT 0x3 145 146 /* _DOS bit 2 */ 147 #define VIDEO_POLICY_BRIGHTNESS_OS 0x4 148 #define VIDEO_POLICY_BRIGHTNESS_BIOS 0x0 149 150 /* Set _DOS for video control policy */ 151 static void 152 acpi_video_set_dos(struct acpi_video *vidp, uint32_t policy) 153 { 154 struct acpi_video_switch *vidsp; 155 ACPI_STATUS status; 156 ACPI_OBJECT obj; 157 ACPI_OBJECT_LIST objlist; 158 159 obj.Type = ACPI_TYPE_INTEGER; 160 obj.Integer.Value = policy; 161 objlist.Count = 1; 162 objlist.Pointer = &obj; 163 164 vidsp = vidp->vid_switch; 165 while (vidsp != NULL) { 166 status = AcpiEvaluateObject(vidsp->dev.hdl, ACPI_METHOD_DOS, 167 &objlist, NULL); 168 if (ACPI_FAILURE(status)) 169 cmn_err(CE_WARN, "!acpi_video_set_dos failed."); 170 vidsp = vidsp->next; 171 } 172 } 173 174 /* 175 * Get the current brightness level and index. 176 */ 177 static int 178 acpi_video_brightness_get(struct acpi_video_brightness *vidbp) 179 { 180 int i; 181 182 if (acpi_brightness_get_disable) { 183 /* simply initialize current brightness to the highest level */ 184 vidbp->cur_level_index = vidbp->nlevel - 1; 185 vidbp->cur_level = vidbp->levels[vidbp->cur_level_index]; 186 return (ACPI_DRV_OK); 187 } 188 189 if (acpica_eval_int(vidbp->dev.hdl, "_BQC", &vidbp->cur_level) 190 != AE_OK) { 191 vidbp->cur_level = 0; 192 return (ACPI_DRV_ERR); 193 } 194 195 for (i = 0; i < vidbp->nlevel; i++) { 196 if (vidbp->levels[i] == vidbp->cur_level) { 197 vidbp->cur_level_index = i; 198 if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) { 199 cmn_err(CE_NOTE, "!acpi_video_brightness_get():" 200 " cur_level = %d, cur_level_index = %d\n", 201 vidbp->cur_level, i); 202 } 203 break; 204 } 205 } 206 207 return (ACPI_DRV_OK); 208 } 209 210 static int 211 acpi_video_brightness_set(struct acpi_video_brightness *vidbp, uint32_t level) 212 { 213 if (acpi_drv_set_int(vidbp->dev.hdl, "_BCM", vidbp->levels[level]) 214 != AE_OK) { 215 return (ACPI_DRV_ERR); 216 } 217 218 vidbp->cur_level = vidbp->levels[level]; 219 vidbp->cur_level_index = level; 220 221 return (ACPI_DRV_OK); 222 } 223 224 void 225 hotkey_drv_gen_sysevent(dev_info_t *dip, char *event) 226 { 227 int err; 228 229 /* Generate/log EC_ACPIEV sysevent */ 230 err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, EC_ACPIEV, 231 event, NULL, NULL, DDI_NOSLEEP); 232 233 if (err != DDI_SUCCESS) { 234 cmn_err(CE_WARN, 235 "!failed to log hotkey sysevent, err code %x\n", err); 236 } 237 } 238 239 /*ARGSUSED*/ 240 static void 241 acpi_video_switch_notify(ACPI_HANDLE hdl, uint32_t notify, void *ctx) 242 { 243 if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) { 244 cmn_err(CE_NOTE, "!acpi_video_switch_notify: got event 0x%x.\n", 245 notify); 246 } 247 248 mutex_enter(acpi_hotkey.hotkey_lock); 249 switch (notify) { 250 case VIDEO_NOTIFY_SWITCH: 251 case VIDEO_NOTIFY_SWITCH_CYCLE: 252 case VIDEO_NOTIFY_SWITCH_NEXT: 253 case VIDEO_NOTIFY_SWITCH_PREV: 254 hotkey_drv_gen_sysevent(acpi_hotkey.dip, 255 ESC_ACPIEV_DISPLAY_SWITCH); 256 break; 257 258 case VIDEO_NOTIFY_SWITCH_STATUS: 259 break; 260 261 default: 262 if (hotkey_drv_debug) { 263 cmn_err(CE_NOTE, 264 "!acpi_video_switch_notify: unknown event 0x%x.\n", 265 notify); 266 } 267 } 268 mutex_exit(acpi_hotkey.hotkey_lock); 269 } 270 271 /*ARGSUSED*/ 272 static void 273 acpi_video_brightness_notify(ACPI_HANDLE hdl, uint32_t notify, void *ctx) 274 { 275 struct acpi_video_brightness *vidbp = ctx; 276 277 if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) { 278 cmn_err(CE_NOTE, 279 "!acpi_video_brightness_notify: got event 0x%x.\n", 280 notify); 281 } 282 283 mutex_enter(acpi_hotkey.hotkey_lock); 284 switch (notify) { 285 case VIDEO_NOTIFY_BRIGHTNESS_CYCLE: 286 case VIDEO_NOTIFY_BRIGHTNESS_INC: 287 if (vidbp->cur_level_index < vidbp->nlevel - 1) { 288 if (acpi_video_brightness_set(vidbp, 289 vidbp->cur_level_index + 1) != ACPI_DRV_OK) { 290 break; 291 } 292 } 293 acpi_drv_gen_sysevent(&vidbp->dev, ESC_PWRCTL_BRIGHTNESS_UP, 0); 294 break; 295 case VIDEO_NOTIFY_BRIGHTNESS_DEC: 296 if (vidbp->cur_level_index > 0) { 297 if (acpi_video_brightness_set(vidbp, 298 vidbp->cur_level_index - 1) != ACPI_DRV_OK) { 299 break; 300 } 301 } 302 acpi_drv_gen_sysevent(&vidbp->dev, ESC_PWRCTL_BRIGHTNESS_DOWN, 303 0); 304 break; 305 case VIDEO_NOTIFY_BRIGHTNESS_ZERO: 306 if (acpi_video_brightness_set(vidbp, 0) != ACPI_DRV_OK) { 307 break; 308 } 309 acpi_drv_gen_sysevent(&vidbp->dev, ESC_PWRCTL_BRIGHTNESS_DOWN, 310 0); 311 break; 312 313 default: 314 if (hotkey_drv_debug) { 315 cmn_err(CE_NOTE, "!acpi_video_brightness_notify: " 316 "unknown event 0x%x.\n", notify); 317 } 318 } 319 mutex_exit(acpi_hotkey.hotkey_lock); 320 } 321 322 static int 323 acpi_video_notify_intall(struct acpi_video *vidp) 324 { 325 ACPI_STATUS status; 326 struct acpi_video_switch *vidsp; 327 struct acpi_video_brightness *vidbp; 328 int i; 329 330 /* bind video switch notify */ 331 vidsp = vidp->vid_switch; 332 for (i = 0; i < vidp->total_switch && vidsp != NULL; i++) { 333 status = AcpiInstallNotifyHandler(vidsp->dev.hdl, 334 ACPI_DEVICE_NOTIFY, acpi_video_switch_notify, vidsp); 335 if (ACPI_FAILURE(status)) { 336 cmn_err(CE_WARN, 337 "!vids handler install failed = %d, vids = %p.", 338 status, (void *) vidsp); 339 } 340 vidsp = vidsp->next; 341 } 342 343 /* bind brightness control notify */ 344 vidbp = vidp->vid_brightness; 345 for (i = 0; i < vidp->total_brightness && vidbp != NULL; i++) { 346 status = AcpiInstallNotifyHandler(vidbp->dev.hdl, 347 ACPI_DEVICE_NOTIFY, acpi_video_brightness_notify, vidbp); 348 if (ACPI_FAILURE(status)) { 349 cmn_err(CE_WARN, 350 "!brightness handler install failed = %x, " 351 "brightness = %p.", status, (void *) vidbp); 352 } 353 vidbp = vidbp->next; 354 } 355 356 return (ACPI_DRV_OK); 357 } 358 359 static int 360 acpi_video_notify_unintall(struct acpi_video *vidp) 361 { 362 struct acpi_video_switch *vidsp; 363 struct acpi_video_brightness *vidbp; 364 int i; 365 366 /* unbind video switch notify */ 367 vidsp = vidp->vid_switch; 368 for (i = 0; i < vidp->total_switch && vidsp != NULL; i++) { 369 (void) AcpiRemoveNotifyHandler(vidsp->dev.hdl, 370 ACPI_DEVICE_NOTIFY, acpi_video_switch_notify); 371 vidsp = vidsp->next; 372 } 373 374 /* unbind brightness control notify */ 375 vidbp = vidp->vid_brightness; 376 for (i = 0; i < vidp->total_brightness && vidbp != NULL; i++) { 377 (void) AcpiRemoveNotifyHandler(vidbp->dev.hdl, 378 ACPI_DEVICE_NOTIFY, acpi_video_brightness_notify); 379 vidbp = vidbp->next; 380 } 381 382 return (ACPI_DRV_OK); 383 } 384 385 static int 386 acpi_video_free(struct acpi_video *vidp) 387 { 388 struct acpi_video_switch *vidsp; 389 struct acpi_video_switch *vidsp_next; 390 struct acpi_video_brightness *vidbp; 391 struct acpi_video_brightness *vidbp_next; 392 struct acpi_video_output *vidop; 393 struct acpi_video_output *vidop_next; 394 395 /* free video switch objects */ 396 vidsp = vidp->vid_switch; 397 while (vidsp != NULL) { 398 vidsp_next = vidsp->next; 399 kmem_free(vidsp, sizeof (struct acpi_video_switch)); 400 vidsp = vidsp_next; 401 } 402 403 /* free video brightness control objects */ 404 vidbp = vidp->vid_brightness; 405 while (vidbp != NULL) { 406 vidbp_next = vidbp->next; 407 kmem_free(vidbp, sizeof (struct acpi_video_brightness)); 408 vidbp = vidbp_next; 409 } 410 411 /* free video output objects */ 412 vidop = vidp->vid_outputs; 413 while (vidop != NULL) { 414 vidop_next = vidop->next; 415 kmem_free(vidop, sizeof (struct acpi_video_output)); 416 vidop = vidop_next; 417 } 418 419 return (ACPI_DRV_OK); 420 } 421 422 static int 423 acpi_video_fini(struct acpi_video *vidp) 424 { 425 (void) acpi_video_notify_unintall(vidp); 426 427 return (acpi_video_free(vidp)); 428 } 429 430 static int 431 acpi_video_enum_output(ACPI_HANDLE hdl, struct acpi_video *vidp) 432 { 433 int adr; 434 struct acpi_video_brightness *vidbp; 435 struct acpi_video_output *vidop; 436 ACPI_BUFFER buf = {ACPI_ALLOCATE_BUFFER, NULL}; 437 ACPI_OBJECT *objp; 438 439 440 if (acpica_eval_int(hdl, "_ADR", &adr) != AE_OK) 441 return (ACPI_DRV_ERR); 442 443 /* Allocate object */ 444 vidop = kmem_zalloc(sizeof (struct acpi_video_output), KM_SLEEP); 445 vidop->dev.hdl = hdl; 446 (void) acpi_drv_dev_init(&vidop->dev); 447 vidop->adr = adr; 448 vidop->type = adr; 449 vidop->next = vidp->vid_outputs; 450 vidp->vid_outputs = vidop; 451 452 if (ACPI_SUCCESS(AcpiEvaluateObjectTyped(hdl, "_BCL", 453 NULL, &buf, ACPI_TYPE_PACKAGE))) { 454 int i, j, k, l, m, nlev, tmp; 455 456 vidbp = kmem_zalloc(sizeof (struct acpi_video_brightness), 457 KM_SLEEP); 458 vidbp->dev = vidop->dev; 459 vidop->adr = adr; 460 vidbp->output_index = vidp->total_outputs; 461 objp = buf.Pointer; 462 463 /* 464 * op->nlev will be needed to free op->levels. 465 */ 466 vidbp->nlevel = nlev = objp->Package.Count; 467 vidbp->levels = kmem_zalloc(nlev * sizeof (uint32_t), KM_SLEEP); 468 469 /* 470 * Get all the supported brightness levels. 471 */ 472 for (i = 0; i < nlev; i++) { 473 ACPI_OBJECT *o = &objp->Package.Elements[i]; 474 int lev = o->Integer.Value; 475 476 if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) { 477 cmn_err(CE_NOTE, "!acpi_video_enum_output() " 478 "brlev=%d i=%d nlev=%d\n", lev, i, nlev); 479 } 480 if (o->Type != ACPI_TYPE_INTEGER) { 481 continue; 482 } 483 vidbp->levels[i] = lev; 484 } 485 486 /* 487 * Sort the brightness levels. 488 */ 489 for (j = 0; j < nlev; j++) { 490 for (k = 0; k < nlev - 1; k++) { 491 if (vidbp->levels[k] > vidbp->levels[k+1]) { 492 tmp = vidbp->levels[k+1]; 493 vidbp->levels[k+1] = vidbp->levels[k]; 494 vidbp->levels[k] = tmp; 495 } 496 } 497 } 498 499 /* 500 * The first two levels could be duplicated, so remove 501 * any duplicates. 502 */ 503 for (l = 0; l < nlev - 1; l++) { 504 if (vidbp->levels[l] == vidbp->levels[l+1]) { 505 for (m = l + 1; m < nlev - 1; m++) { 506 vidbp->levels[m] = vidbp->levels[m+1]; 507 } 508 nlev--; 509 } 510 } 511 512 vidbp->nlevel = nlev; 513 (void) acpi_video_brightness_get(vidbp); 514 vidbp->next = vidp->vid_brightness; 515 vidp->vid_brightness = vidbp; 516 vidp->total_brightness++; 517 518 AcpiOsFree(objp); 519 } 520 521 vidp->total_outputs++; 522 523 return (ACPI_DRV_OK); 524 } 525 526 /*ARGSUSED*/ 527 static ACPI_STATUS 528 acpi_video_find_and_alloc(ACPI_HANDLE hdl, UINT32 nest, void *ctx, 529 void **rv) 530 { 531 ACPI_HANDLE tmphdl; 532 ACPI_STATUS err; 533 ACPI_BUFFER buf = {ACPI_ALLOCATE_BUFFER, NULL}; 534 struct acpi_video *vidp; 535 struct acpi_video_switch *vidsp; 536 537 err = AcpiGetHandle(hdl, ACPI_METHOD_DOS, &tmphdl); 538 if (err != AE_OK) 539 return (AE_OK); 540 541 err = AcpiGetHandle(hdl, ACPI_METHOD_DOD, &tmphdl); 542 if (err != AE_OK) 543 return (AE_OK); 544 545 vidp = (struct acpi_video *)ctx; 546 vidsp = kmem_zalloc(sizeof (struct acpi_video_switch), KM_SLEEP); 547 vidsp->dev.hdl = hdl; 548 (void) acpi_drv_dev_init(&vidsp->dev); 549 vidsp->next = vidp->vid_switch; 550 vidp->vid_switch = vidsp; 551 vidp->total_switch++; 552 553 /* 554 * Enumerate the output devices. 555 */ 556 while (ACPI_SUCCESS(AcpiGetNextObject(ACPI_TYPE_DEVICE, 557 hdl, tmphdl, &tmphdl))) { 558 (void) acpi_video_enum_output(tmphdl, vidp); 559 } 560 561 if (!ACPI_FAILURE(AcpiGetName(hdl, ACPI_FULL_PATHNAME, &buf))) { 562 if (buf.Pointer) { 563 if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) { 564 cmn_err(CE_NOTE, 565 "!acpi video switch hdl = 0x%p, path = %s.", 566 hdl, (char *)buf.Pointer); 567 } 568 AcpiOsFree(buf.Pointer); 569 } 570 } 571 572 return (AE_OK); 573 } 574 575 int 576 hotkey_brightness_inc(hotkey_drv_t *htkp) 577 { 578 struct acpi_video *vidp; 579 struct acpi_video_brightness *vidbp; 580 581 vidp = (struct acpi_video *)htkp->acpi_video; 582 583 for (vidbp = vidp->vid_brightness; vidbp != NULL; vidbp = vidbp->next) { 584 if (vidbp->cur_level_index < vidbp->nlevel - 1) { 585 if (acpi_video_brightness_set(vidbp, 586 vidbp->cur_level_index + 1) != ACPI_DRV_OK) { 587 return (ACPI_DRV_ERR); 588 } 589 } 590 } 591 return (ACPI_DRV_OK); 592 } 593 594 int 595 hotkey_brightness_dec(hotkey_drv_t *htkp) 596 { 597 struct acpi_video *vidp; 598 struct acpi_video_brightness *vidbp; 599 600 vidp = (struct acpi_video *)htkp->acpi_video; 601 602 for (vidbp = vidp->vid_brightness; vidbp != NULL; vidbp = vidbp->next) { 603 if (vidbp->cur_level_index > 0) { 604 if (acpi_video_brightness_set(vidbp, 605 vidbp->cur_level_index - 1) != ACPI_DRV_OK) { 606 return (ACPI_DRV_ERR); 607 } 608 } 609 } 610 611 return (ACPI_DRV_OK); 612 } 613 614 /*ARGSUSED*/ 615 int 616 acpi_video_ioctl(void *p, int cmd, intptr_t arg, int mode, cred_t *cr, 617 int *rval) 618 { 619 struct acpi_video *vidp = p; 620 struct acpi_video_brightness *vidbp; 621 int res = 0; 622 623 if (vidp == NULL) 624 return (ENXIO); 625 626 vidbp = vidp->vid_brightness; 627 if (vidbp == NULL) 628 return (ENXIO); 629 630 if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) { 631 cmn_err(CE_NOTE, "!acpi_video_ioctl cmd %d\n", cmd); 632 } 633 634 switch (cmd) { 635 case ACPI_DRV_IOC_INFO: 636 { 637 struct acpi_drv_output_info inf; 638 639 inf.adr = vidbp->adr; 640 inf.nlev = vidbp->nlevel; 641 if (copyout(&inf, (void *)arg, sizeof (inf))) { 642 res = EFAULT; 643 } 644 break; 645 } 646 647 case ACPI_DRV_IOC_LEVELS: 648 if (copyout(vidbp->levels, (void *)arg, 649 sizeof (*vidbp->levels) * vidbp->nlevel)) { 650 res = EFAULT; 651 } 652 break; 653 654 case ACPI_DRV_IOC_STATUS: 655 { 656 /* 657 * Need to get the current levels through ACPI first 658 * then go through array of levels to find index. 659 */ 660 struct acpi_drv_output_status status; 661 int i; 662 663 status.state = 0; 664 status.num_levels = vidbp->nlevel; 665 status.cur_level = vidbp->cur_level; 666 for (i = 0; i < vidbp->nlevel; i++) { 667 if (vidbp->levels[i] == vidbp->cur_level) { 668 status.cur_level_index = i; 669 if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) { 670 cmn_err(CE_NOTE, "!ACPI_DRV_IOC_STATUS " 671 "cur_level_index %d\n", i); 672 } 673 break; 674 } 675 } 676 if (copyout(&status, (void *)arg, sizeof (status))) { 677 res = EFAULT; 678 } 679 break; 680 } 681 682 case ACPI_DRV_IOC_SET_BRIGHTNESS: { 683 int level; 684 685 if (drv_priv(cr)) { 686 res = EPERM; 687 break; 688 } 689 if (copyin((void *)arg, &level, sizeof (level))) { 690 res = EFAULT; 691 break; 692 } 693 if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) { 694 cmn_err(CE_NOTE, 695 "!acpi_video_ioctl: set BRIGHTNESS level=%d\n", 696 level); 697 } 698 if (acpi_video_brightness_set(vidbp, level) != ACPI_DRV_OK) { 699 res = EFAULT; 700 } 701 break; 702 } 703 704 default: 705 res = EINVAL; 706 break; 707 } 708 709 return (res); 710 } 711 712 /*ARGSUSED*/ 713 int 714 acpi_drv_hotkey_ioctl(int cmd, intptr_t arg, int mode, cred_t *cr, 715 int *rval) 716 { 717 hotkey_drv_t *htkp = &acpi_hotkey; 718 719 switch (htkp->hotkey_method) { 720 case HOTKEY_METHOD_ACPI_VIDEO: 721 return (acpi_video_ioctl(htkp->acpi_video, cmd, arg, mode, 722 cr, rval)); 723 case HOTKEY_METHOD_MISC: 724 case HOTKEY_METHOD_VENDOR: 725 return (htkp->vendor_ioctl(htkp, cmd, arg, mode, cr, rval)); 726 case HOTKEY_METHOD_NONE: 727 default: 728 return (ENXIO); 729 } 730 } 731 732 static void 733 acpi_video_check_blacklist(void) 734 { 735 smbios_hdl_t *smhdl = NULL; 736 id_t smid; 737 smbios_system_t smsys; 738 smbios_info_t sminfo; 739 char *mfg, *product; 740 struct acpi_video_smbios_info *pblacklist; 741 742 acpi_brightness_get_disable = 0; 743 smhdl = smbios_open(NULL, SMB_VERSION, ksmbios_flags, NULL); 744 if (smhdl == NULL || 745 ((smid = smbios_info_system(smhdl, &smsys)) == SMB_ERR) || 746 (smbios_info_common(smhdl, smid, &sminfo) == SMB_ERR)) { 747 goto done; 748 } 749 750 mfg = (char *)sminfo.smbi_manufacturer; 751 product = (char *)sminfo.smbi_product; 752 for (pblacklist = acpi_brightness_get_blacklist; 753 pblacklist->manufacturer != NULL; pblacklist++) { 754 if ((strcmp(mfg, pblacklist->manufacturer) == 0) && 755 (strcmp(product, pblacklist->product) == 0)) { 756 acpi_brightness_get_disable = 1; 757 } 758 } 759 done: 760 if (smhdl != NULL) 761 smbios_close(smhdl); 762 } 763 764 static int 765 hotkey_acpi_video_check(hotkey_drv_t *htkp) 766 { 767 struct acpi_video *vidp; 768 769 vidp = &acpi_video_hotkey; 770 bzero(vidp, sizeof (struct acpi_video)); 771 if (acpi_brightness_get_disable == -1) 772 acpi_video_check_blacklist(); 773 /* Find ACPI Video device handle */ 774 if (ACPI_FAILURE(AcpiGetDevices(NULL, acpi_video_find_and_alloc, 775 vidp, NULL))) { 776 return (ACPI_DRV_ERR); 777 } 778 779 htkp->acpi_video = vidp; 780 if (htkp->hotkey_method == HOTKEY_METHOD_NONE) { 781 if (acpi_video_notify_intall(vidp) != ACPI_DRV_OK) { 782 (void) acpi_video_fini(vidp); 783 htkp->acpi_video = NULL; 784 return (ACPI_DRV_ERR); 785 } 786 } 787 htkp->hotkey_method |= HOTKEY_METHOD_ACPI_VIDEO; 788 789 acpi_video_set_dos(vidp, VIDEO_POLICY_BRIGHTNESS_OS | 790 VIDEO_POLICY_SWITCH_OS); 791 792 return (ACPI_DRV_OK); 793 } 794 795 int 796 hotkey_init(hotkey_drv_t *htkp) 797 { 798 int i; 799 int modid; 800 modctl_t *modp; 801 802 htkp->modid = -1; 803 /* Try to find vendor specific method */ 804 for (i = 0; vendor_hotkey_drv_list[i].module != NULL; i++) { 805 if (!vendor_hotkey_drv_list[i].enable) 806 continue; 807 808 if ((modid = modload("drv", vendor_hotkey_drv_list[i].module)) 809 == -1) { 810 continue; 811 } 812 813 htkp->modid = modid; 814 if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) { 815 cmn_err(CE_NOTE, "!loaded %s specific method.\n", 816 vendor_hotkey_drv_list[i].vid); 817 } 818 } 819 820 /* Check availability of ACPI Video Extension method */ 821 if (htkp->hotkey_method == HOTKEY_METHOD_NONE || 822 htkp->check_acpi_video) { 823 if (hotkey_acpi_video_check(htkp) == ACPI_DRV_OK) { 824 if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) 825 cmn_err(CE_NOTE, "!find ACPI video method.\n"); 826 } else 827 goto fail; 828 } 829 830 if (htkp->modid != -1) { 831 modp = mod_hold_by_id(htkp->modid); 832 mutex_enter(&mod_lock); 833 modp->mod_ref = 1; 834 modp->mod_loadflags |= MOD_NOAUTOUNLOAD; 835 mutex_exit(&mod_lock); 836 mod_release_mod(modp); 837 } 838 839 /* Create minor node for hotkey device. */ 840 if (ddi_create_minor_node(htkp->dip, "hotkey", S_IFCHR, 841 MINOR_HOTKEY(0), DDI_PSEUDO, 0) == DDI_FAILURE) { 842 if (hotkey_drv_debug & HOTKEY_DBG_WARN) 843 cmn_err(CE_WARN, "hotkey: minor node create failed"); 844 goto fail; 845 } 846 847 return (ACPI_DRV_OK); 848 849 fail: 850 if (htkp->vendor_fini != NULL) 851 htkp->vendor_fini(htkp); 852 if (htkp->modid != -1) 853 (void) modunload(htkp->modid); 854 855 return (ACPI_DRV_ERR); 856 } 857 858 859 int 860 hotkey_fini(hotkey_drv_t *htkp) 861 { 862 modctl_t *modp; 863 864 if (htkp->vendor_fini != NULL) 865 htkp->vendor_fini(htkp); 866 if (htkp->acpi_video != NULL) 867 (void) acpi_video_fini(htkp->acpi_video); 868 if (htkp->modid != -1) { 869 modp = mod_hold_by_id(htkp->modid); 870 mutex_enter(&mod_lock); 871 modp->mod_ref = 0; 872 modp->mod_loadflags &= ~MOD_NOAUTOUNLOAD; 873 mutex_exit(&mod_lock); 874 mod_release_mod(modp); 875 (void) modunload(htkp->modid); 876 } 877 878 return (ACPI_DRV_OK); 879 } 880