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