Home | History | Annotate | Download | only in acpi_drv
      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