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