18693SKerry.Shu@Sun.COM /*
28693SKerry.Shu@Sun.COM * CDDL HEADER START
38693SKerry.Shu@Sun.COM *
48693SKerry.Shu@Sun.COM * The contents of this file are subject to the terms of the
58693SKerry.Shu@Sun.COM * Common Development and Distribution License (the "License").
68693SKerry.Shu@Sun.COM * You may not use this file except in compliance with the License.
78693SKerry.Shu@Sun.COM *
88693SKerry.Shu@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98693SKerry.Shu@Sun.COM * or http://www.opensolaris.org/os/licensing.
108693SKerry.Shu@Sun.COM * See the License for the specific language governing permissions
118693SKerry.Shu@Sun.COM * and limitations under the License.
128693SKerry.Shu@Sun.COM *
138693SKerry.Shu@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
148693SKerry.Shu@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158693SKerry.Shu@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
168693SKerry.Shu@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
178693SKerry.Shu@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
188693SKerry.Shu@Sun.COM *
198693SKerry.Shu@Sun.COM * CDDL HEADER END
208693SKerry.Shu@Sun.COM */
218693SKerry.Shu@Sun.COM /*
22*12122SKerry.Shu@Sun.COM * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
238693SKerry.Shu@Sun.COM */
248693SKerry.Shu@Sun.COM
258693SKerry.Shu@Sun.COM /*
268693SKerry.Shu@Sun.COM * Solaris x86 Generic ACPI Video Extensions Hotkey driver
278693SKerry.Shu@Sun.COM */
288693SKerry.Shu@Sun.COM #include <sys/hotkey_drv.h>
29*12122SKerry.Shu@Sun.COM #include <sys/smbios.h>
308693SKerry.Shu@Sun.COM
318693SKerry.Shu@Sun.COM /*
328693SKerry.Shu@Sun.COM * Vendor specific hotkey support list
338693SKerry.Shu@Sun.COM * 1. Toshiba: acpi_toshiba
348693SKerry.Shu@Sun.COM */
358693SKerry.Shu@Sun.COM struct vendor_hotkey_drv vendor_hotkey_drv_list[] = {
368693SKerry.Shu@Sun.COM /* vendor, module name, enable? */
378693SKerry.Shu@Sun.COM {"Toshiba", "acpi_toshiba", B_TRUE},
388693SKerry.Shu@Sun.COM /* Terminator */
398693SKerry.Shu@Sun.COM {NULL, NULL, B_FALSE}
408693SKerry.Shu@Sun.COM };
418693SKerry.Shu@Sun.COM
428693SKerry.Shu@Sun.COM enum vga_output_type {
438693SKerry.Shu@Sun.COM OUTPUT_OTHER,
448693SKerry.Shu@Sun.COM OUTPUT_CRT,
458693SKerry.Shu@Sun.COM OUTPUT_TV,
468693SKerry.Shu@Sun.COM OUTPUT_DVI,
478693SKerry.Shu@Sun.COM OUTPUT_LCD
488693SKerry.Shu@Sun.COM };
498693SKerry.Shu@Sun.COM
508693SKerry.Shu@Sun.COM struct acpi_video_output {
518693SKerry.Shu@Sun.COM struct acpi_drv_dev dev;
528693SKerry.Shu@Sun.COM uint32_t adr;
538693SKerry.Shu@Sun.COM enum vga_output_type type;
548693SKerry.Shu@Sun.COM struct acpi_video_output *next;
558693SKerry.Shu@Sun.COM };
568693SKerry.Shu@Sun.COM
578693SKerry.Shu@Sun.COM struct acpi_video_brightness {
588693SKerry.Shu@Sun.COM struct acpi_drv_dev dev;
598693SKerry.Shu@Sun.COM uint32_t adr;
608693SKerry.Shu@Sun.COM uint32_t nlevel;
618693SKerry.Shu@Sun.COM int *levels;
628693SKerry.Shu@Sun.COM int cur_level;
638693SKerry.Shu@Sun.COM uint32_t cur_level_index;
648693SKerry.Shu@Sun.COM uint32_t output_index;
658693SKerry.Shu@Sun.COM struct acpi_video_brightness *next;
668693SKerry.Shu@Sun.COM };
678693SKerry.Shu@Sun.COM
688693SKerry.Shu@Sun.COM struct acpi_video_switch {
698693SKerry.Shu@Sun.COM struct acpi_drv_dev dev;
708693SKerry.Shu@Sun.COM struct acpi_video_switch *next;
718693SKerry.Shu@Sun.COM };
728693SKerry.Shu@Sun.COM
738693SKerry.Shu@Sun.COM /* ACPI video extension hotkey for video switch and brightness control */
748693SKerry.Shu@Sun.COM static struct acpi_video {
758693SKerry.Shu@Sun.COM struct acpi_video_output *vid_outputs;
768693SKerry.Shu@Sun.COM uint32_t total_outputs;
778693SKerry.Shu@Sun.COM struct acpi_video_brightness *vid_brightness;
788693SKerry.Shu@Sun.COM uint32_t total_brightness;
798693SKerry.Shu@Sun.COM struct acpi_video_switch *vid_switch;
808693SKerry.Shu@Sun.COM uint32_t total_switch;
818693SKerry.Shu@Sun.COM } acpi_video_hotkey;
828693SKerry.Shu@Sun.COM
838693SKerry.Shu@Sun.COM int hotkey_drv_debug = 0;
848693SKerry.Shu@Sun.COM
85*12122SKerry.Shu@Sun.COM static struct acpi_video_smbios_info {
86*12122SKerry.Shu@Sun.COM char *manufacturer;
87*12122SKerry.Shu@Sun.COM char *product;
88*12122SKerry.Shu@Sun.COM } acpi_brightness_get_blacklist[] = {
89*12122SKerry.Shu@Sun.COM { /* Dell AdamoXPS laptop */
90*12122SKerry.Shu@Sun.COM "Dell Inc.",
91*12122SKerry.Shu@Sun.COM "Adamo XPS"
92*12122SKerry.Shu@Sun.COM },
93*12122SKerry.Shu@Sun.COM { /* termination entry */
94*12122SKerry.Shu@Sun.COM NULL,
95*12122SKerry.Shu@Sun.COM NULL
96*12122SKerry.Shu@Sun.COM }
97*12122SKerry.Shu@Sun.COM };
98*12122SKerry.Shu@Sun.COM /*
99*12122SKerry.Shu@Sun.COM * -1 = check acpi_brightness_get_blacklist[].
100*12122SKerry.Shu@Sun.COM * 0 = enable brightness get.
101*12122SKerry.Shu@Sun.COM * 1 = disable brightness get.
102*12122SKerry.Shu@Sun.COM */
103*12122SKerry.Shu@Sun.COM int acpi_brightness_get_disable = -1;
104*12122SKerry.Shu@Sun.COM
105*12122SKerry.Shu@Sun.COM
1068693SKerry.Shu@Sun.COM #define ACPI_METHOD_DOS "_DOS"
1078693SKerry.Shu@Sun.COM #define ACPI_METHOD_DOD "_DOD"
1088693SKerry.Shu@Sun.COM
1098693SKerry.Shu@Sun.COM #define ACPI_DEVNAME_CRT "CRT"
1108693SKerry.Shu@Sun.COM #define ACPI_DEVNAME_LCD "LCD"
1118693SKerry.Shu@Sun.COM #define ACPI_DEVNAME_TV "TV"
1128693SKerry.Shu@Sun.COM #define ACPI_METHOD_ADR "_ADR"
1138693SKerry.Shu@Sun.COM #define ACPI_METHOD_DDC "_DDC"
1148693SKerry.Shu@Sun.COM #define ACPI_METHOD_DCS "_DCS"
1158693SKerry.Shu@Sun.COM #define ACPI_METHOD_DGS "_DGS"
1168693SKerry.Shu@Sun.COM #define ACPI_METHOD_DSS "_DSS"
1178693SKerry.Shu@Sun.COM
1188693SKerry.Shu@Sun.COM #define VIDEO_NOTIFY_SWITCH 0x80
1198693SKerry.Shu@Sun.COM #define VIDEO_NOTIFY_SWITCH_STATUS 0x81
1208693SKerry.Shu@Sun.COM #define VIDEO_NOTIFY_SWITCH_CYCLE 0x82
1218693SKerry.Shu@Sun.COM #define VIDEO_NOTIFY_SWITCH_NEXT 0x83
1228693SKerry.Shu@Sun.COM #define VIDEO_NOTIFY_SWITCH_PREV 0x84
1238693SKerry.Shu@Sun.COM
1248693SKerry.Shu@Sun.COM #define VIDEO_NOTIFY_BRIGHTNESS_CYCLE 0x85
1258693SKerry.Shu@Sun.COM #define VIDEO_NOTIFY_BRIGHTNESS_INC 0x86
1268693SKerry.Shu@Sun.COM #define VIDEO_NOTIFY_BRIGHTNESS_DEC 0x87
1278693SKerry.Shu@Sun.COM #define VIDEO_NOTIFY_BRIGHTNESS_ZERO 0x88
1288693SKerry.Shu@Sun.COM
1298693SKerry.Shu@Sun.COM /* Output device status */
1308693SKerry.Shu@Sun.COM #define ACPI_DRV_DCS_CONNECTOR_EXIST (1 << 0)
1318693SKerry.Shu@Sun.COM #define ACPI_DRV_DCS_ACTIVE (1 << 1)
1328693SKerry.Shu@Sun.COM #define ACPI_DRV_DCS_READY (1 << 2)
1338693SKerry.Shu@Sun.COM #define ACPI_DRV_DCS_FUNCTIONAL (1 << 3)
1348693SKerry.Shu@Sun.COM #define ACPI_DRV_DCS_ATTACHED (1 << 4)
1358693SKerry.Shu@Sun.COM
1368693SKerry.Shu@Sun.COM /* _DOS default value is 1 */
1378693SKerry.Shu@Sun.COM /* _DOS bit 1:0 */
1388693SKerry.Shu@Sun.COM #define VIDEO_POLICY_SWITCH_OS 0x0
1398693SKerry.Shu@Sun.COM #define VIDEO_POLICY_SWITCH_BIOS 0x1
1408693SKerry.Shu@Sun.COM #define VIDEO_POLICY_SWITCH_LOCKED 0x2
1418693SKerry.Shu@Sun.COM #define VIDEO_POLICY_SWITCH_OS_EVENT 0x3
1428693SKerry.Shu@Sun.COM
1438693SKerry.Shu@Sun.COM /* _DOS bit 2 */
1448693SKerry.Shu@Sun.COM #define VIDEO_POLICY_BRIGHTNESS_OS 0x4
1458693SKerry.Shu@Sun.COM #define VIDEO_POLICY_BRIGHTNESS_BIOS 0x0
1468693SKerry.Shu@Sun.COM
1478693SKerry.Shu@Sun.COM /* Set _DOS for video control policy */
1488693SKerry.Shu@Sun.COM static void
acpi_video_set_dos(struct acpi_video * vidp,uint32_t policy)1498693SKerry.Shu@Sun.COM acpi_video_set_dos(struct acpi_video *vidp, uint32_t policy)
1508693SKerry.Shu@Sun.COM {
1518693SKerry.Shu@Sun.COM struct acpi_video_switch *vidsp;
1528693SKerry.Shu@Sun.COM ACPI_STATUS status;
1538693SKerry.Shu@Sun.COM ACPI_OBJECT obj;
1548693SKerry.Shu@Sun.COM ACPI_OBJECT_LIST objlist;
1558693SKerry.Shu@Sun.COM
1568693SKerry.Shu@Sun.COM obj.Type = ACPI_TYPE_INTEGER;
1578693SKerry.Shu@Sun.COM obj.Integer.Value = policy;
1588693SKerry.Shu@Sun.COM objlist.Count = 1;
1598693SKerry.Shu@Sun.COM objlist.Pointer = &obj;
1608693SKerry.Shu@Sun.COM
1618693SKerry.Shu@Sun.COM vidsp = vidp->vid_switch;
1628693SKerry.Shu@Sun.COM while (vidsp != NULL) {
1638693SKerry.Shu@Sun.COM status = AcpiEvaluateObject(vidsp->dev.hdl, ACPI_METHOD_DOS,
1648693SKerry.Shu@Sun.COM &objlist, NULL);
1658693SKerry.Shu@Sun.COM if (ACPI_FAILURE(status))
1668693SKerry.Shu@Sun.COM cmn_err(CE_WARN, "!acpi_video_set_dos failed.");
1678693SKerry.Shu@Sun.COM vidsp = vidsp->next;
1688693SKerry.Shu@Sun.COM }
1698693SKerry.Shu@Sun.COM }
1708693SKerry.Shu@Sun.COM
1718693SKerry.Shu@Sun.COM /*
1728693SKerry.Shu@Sun.COM * Get the current brightness level and index.
1738693SKerry.Shu@Sun.COM */
1748693SKerry.Shu@Sun.COM static int
acpi_video_brightness_get(struct acpi_video_brightness * vidbp)1758693SKerry.Shu@Sun.COM acpi_video_brightness_get(struct acpi_video_brightness *vidbp)
1768693SKerry.Shu@Sun.COM {
1778693SKerry.Shu@Sun.COM int i;
1788693SKerry.Shu@Sun.COM
179*12122SKerry.Shu@Sun.COM if (acpi_brightness_get_disable) {
180*12122SKerry.Shu@Sun.COM /* simply initialize current brightness to the highest level */
181*12122SKerry.Shu@Sun.COM vidbp->cur_level_index = vidbp->nlevel - 1;
182*12122SKerry.Shu@Sun.COM vidbp->cur_level = vidbp->levels[vidbp->cur_level_index];
183*12122SKerry.Shu@Sun.COM return (ACPI_DRV_OK);
184*12122SKerry.Shu@Sun.COM }
185*12122SKerry.Shu@Sun.COM
1868693SKerry.Shu@Sun.COM if (acpica_eval_int(vidbp->dev.hdl, "_BQC", &vidbp->cur_level)
1878693SKerry.Shu@Sun.COM != AE_OK) {
1888693SKerry.Shu@Sun.COM vidbp->cur_level = 0;
1898693SKerry.Shu@Sun.COM return (ACPI_DRV_ERR);
1908693SKerry.Shu@Sun.COM }
1918693SKerry.Shu@Sun.COM
1928693SKerry.Shu@Sun.COM for (i = 0; i < vidbp->nlevel; i++) {
1938693SKerry.Shu@Sun.COM if (vidbp->levels[i] == vidbp->cur_level) {
1948693SKerry.Shu@Sun.COM vidbp->cur_level_index = i;
1958693SKerry.Shu@Sun.COM if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
1968693SKerry.Shu@Sun.COM cmn_err(CE_NOTE, "!acpi_video_brightness_get():"
1978693SKerry.Shu@Sun.COM " cur_level = %d, cur_level_index = %d\n",
1988693SKerry.Shu@Sun.COM vidbp->cur_level, i);
1998693SKerry.Shu@Sun.COM }
2008693SKerry.Shu@Sun.COM break;
2018693SKerry.Shu@Sun.COM }
2028693SKerry.Shu@Sun.COM }
2038693SKerry.Shu@Sun.COM
2048693SKerry.Shu@Sun.COM return (ACPI_DRV_OK);
2058693SKerry.Shu@Sun.COM }
2068693SKerry.Shu@Sun.COM
2078693SKerry.Shu@Sun.COM static int
acpi_video_brightness_set(struct acpi_video_brightness * vidbp,uint32_t level)2088693SKerry.Shu@Sun.COM acpi_video_brightness_set(struct acpi_video_brightness *vidbp, uint32_t level)
2098693SKerry.Shu@Sun.COM {
2108693SKerry.Shu@Sun.COM if (acpi_drv_set_int(vidbp->dev.hdl, "_BCM", vidbp->levels[level])
2118693SKerry.Shu@Sun.COM != AE_OK) {
2128693SKerry.Shu@Sun.COM return (ACPI_DRV_ERR);
2138693SKerry.Shu@Sun.COM }
2148693SKerry.Shu@Sun.COM
2158693SKerry.Shu@Sun.COM vidbp->cur_level = vidbp->levels[level];
2168693SKerry.Shu@Sun.COM vidbp->cur_level_index = level;
2178693SKerry.Shu@Sun.COM
2188693SKerry.Shu@Sun.COM return (ACPI_DRV_OK);
2198693SKerry.Shu@Sun.COM }
2208693SKerry.Shu@Sun.COM
2218693SKerry.Shu@Sun.COM void
hotkey_drv_gen_sysevent(dev_info_t * dip,char * event)2228693SKerry.Shu@Sun.COM hotkey_drv_gen_sysevent(dev_info_t *dip, char *event)
2238693SKerry.Shu@Sun.COM {
2248693SKerry.Shu@Sun.COM int err;
2258693SKerry.Shu@Sun.COM
2268693SKerry.Shu@Sun.COM /* Generate/log EC_ACPIEV sysevent */
2278693SKerry.Shu@Sun.COM err = ddi_log_sysevent(dip, DDI_VENDOR_SUNW, EC_ACPIEV,
2288693SKerry.Shu@Sun.COM event, NULL, NULL, DDI_NOSLEEP);
2298693SKerry.Shu@Sun.COM
2308693SKerry.Shu@Sun.COM if (err != DDI_SUCCESS) {
2318693SKerry.Shu@Sun.COM cmn_err(CE_WARN,
2328693SKerry.Shu@Sun.COM "!failed to log hotkey sysevent, err code %x\n", err);
2338693SKerry.Shu@Sun.COM }
2348693SKerry.Shu@Sun.COM }
2358693SKerry.Shu@Sun.COM
2368693SKerry.Shu@Sun.COM /*ARGSUSED*/
2378693SKerry.Shu@Sun.COM static void
acpi_video_switch_notify(ACPI_HANDLE hdl,uint32_t notify,void * ctx)2388693SKerry.Shu@Sun.COM acpi_video_switch_notify(ACPI_HANDLE hdl, uint32_t notify, void *ctx)
2398693SKerry.Shu@Sun.COM {
2408693SKerry.Shu@Sun.COM if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
2418693SKerry.Shu@Sun.COM cmn_err(CE_NOTE, "!acpi_video_switch_notify: got event 0x%x.\n",
2428693SKerry.Shu@Sun.COM notify);
2438693SKerry.Shu@Sun.COM }
2448693SKerry.Shu@Sun.COM
2458693SKerry.Shu@Sun.COM mutex_enter(acpi_hotkey.hotkey_lock);
2468693SKerry.Shu@Sun.COM switch (notify) {
2478693SKerry.Shu@Sun.COM case VIDEO_NOTIFY_SWITCH:
2488693SKerry.Shu@Sun.COM case VIDEO_NOTIFY_SWITCH_CYCLE:
2498693SKerry.Shu@Sun.COM case VIDEO_NOTIFY_SWITCH_NEXT:
2508693SKerry.Shu@Sun.COM case VIDEO_NOTIFY_SWITCH_PREV:
2518693SKerry.Shu@Sun.COM hotkey_drv_gen_sysevent(acpi_hotkey.dip,
2528693SKerry.Shu@Sun.COM ESC_ACPIEV_DISPLAY_SWITCH);
2538693SKerry.Shu@Sun.COM break;
2548693SKerry.Shu@Sun.COM
2558693SKerry.Shu@Sun.COM case VIDEO_NOTIFY_SWITCH_STATUS:
2568693SKerry.Shu@Sun.COM break;
2578693SKerry.Shu@Sun.COM
2588693SKerry.Shu@Sun.COM default:
2598693SKerry.Shu@Sun.COM if (hotkey_drv_debug) {
2608693SKerry.Shu@Sun.COM cmn_err(CE_NOTE,
2618693SKerry.Shu@Sun.COM "!acpi_video_switch_notify: unknown event 0x%x.\n",
2628693SKerry.Shu@Sun.COM notify);
2638693SKerry.Shu@Sun.COM }
2648693SKerry.Shu@Sun.COM }
2658693SKerry.Shu@Sun.COM mutex_exit(acpi_hotkey.hotkey_lock);
2668693SKerry.Shu@Sun.COM }
2678693SKerry.Shu@Sun.COM
2688693SKerry.Shu@Sun.COM /*ARGSUSED*/
2698693SKerry.Shu@Sun.COM static void
acpi_video_brightness_notify(ACPI_HANDLE hdl,uint32_t notify,void * ctx)2708693SKerry.Shu@Sun.COM acpi_video_brightness_notify(ACPI_HANDLE hdl, uint32_t notify, void *ctx)
2718693SKerry.Shu@Sun.COM {
2728693SKerry.Shu@Sun.COM struct acpi_video_brightness *vidbp = ctx;
2738693SKerry.Shu@Sun.COM
2748693SKerry.Shu@Sun.COM if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
2758693SKerry.Shu@Sun.COM cmn_err(CE_NOTE,
2768693SKerry.Shu@Sun.COM "!acpi_video_brightness_notify: got event 0x%x.\n",
2778693SKerry.Shu@Sun.COM notify);
2788693SKerry.Shu@Sun.COM }
2798693SKerry.Shu@Sun.COM
2808693SKerry.Shu@Sun.COM mutex_enter(acpi_hotkey.hotkey_lock);
2818693SKerry.Shu@Sun.COM switch (notify) {
2828693SKerry.Shu@Sun.COM case VIDEO_NOTIFY_BRIGHTNESS_CYCLE:
2838693SKerry.Shu@Sun.COM case VIDEO_NOTIFY_BRIGHTNESS_INC:
2848693SKerry.Shu@Sun.COM if (vidbp->cur_level_index < vidbp->nlevel - 1) {
2858693SKerry.Shu@Sun.COM if (acpi_video_brightness_set(vidbp,
2868693SKerry.Shu@Sun.COM vidbp->cur_level_index + 1) != ACPI_DRV_OK) {
2878693SKerry.Shu@Sun.COM break;
2888693SKerry.Shu@Sun.COM }
2898693SKerry.Shu@Sun.COM }
2908693SKerry.Shu@Sun.COM acpi_drv_gen_sysevent(&vidbp->dev, ESC_PWRCTL_BRIGHTNESS_UP, 0);
2918693SKerry.Shu@Sun.COM break;
2928693SKerry.Shu@Sun.COM case VIDEO_NOTIFY_BRIGHTNESS_DEC:
2938693SKerry.Shu@Sun.COM if (vidbp->cur_level_index > 0) {
2948693SKerry.Shu@Sun.COM if (acpi_video_brightness_set(vidbp,
2958693SKerry.Shu@Sun.COM vidbp->cur_level_index - 1) != ACPI_DRV_OK) {
2968693SKerry.Shu@Sun.COM break;
2978693SKerry.Shu@Sun.COM }
2988693SKerry.Shu@Sun.COM }
2998693SKerry.Shu@Sun.COM acpi_drv_gen_sysevent(&vidbp->dev, ESC_PWRCTL_BRIGHTNESS_DOWN,
3008693SKerry.Shu@Sun.COM 0);
3018693SKerry.Shu@Sun.COM break;
3028693SKerry.Shu@Sun.COM case VIDEO_NOTIFY_BRIGHTNESS_ZERO:
3038693SKerry.Shu@Sun.COM if (acpi_video_brightness_set(vidbp, 0) != ACPI_DRV_OK) {
3048693SKerry.Shu@Sun.COM break;
3058693SKerry.Shu@Sun.COM }
3068693SKerry.Shu@Sun.COM acpi_drv_gen_sysevent(&vidbp->dev, ESC_PWRCTL_BRIGHTNESS_DOWN,
3078693SKerry.Shu@Sun.COM 0);
3088693SKerry.Shu@Sun.COM break;
3098693SKerry.Shu@Sun.COM
3108693SKerry.Shu@Sun.COM default:
3118693SKerry.Shu@Sun.COM if (hotkey_drv_debug) {
3128693SKerry.Shu@Sun.COM cmn_err(CE_NOTE, "!acpi_video_brightness_notify: "
3138693SKerry.Shu@Sun.COM "unknown event 0x%x.\n", notify);
3148693SKerry.Shu@Sun.COM }
3158693SKerry.Shu@Sun.COM }
3168693SKerry.Shu@Sun.COM mutex_exit(acpi_hotkey.hotkey_lock);
3178693SKerry.Shu@Sun.COM }
3188693SKerry.Shu@Sun.COM
3198693SKerry.Shu@Sun.COM static int
acpi_video_notify_intall(struct acpi_video * vidp)3208693SKerry.Shu@Sun.COM acpi_video_notify_intall(struct acpi_video *vidp)
3218693SKerry.Shu@Sun.COM {
3228693SKerry.Shu@Sun.COM ACPI_STATUS status;
3238693SKerry.Shu@Sun.COM struct acpi_video_switch *vidsp;
3248693SKerry.Shu@Sun.COM struct acpi_video_brightness *vidbp;
3258693SKerry.Shu@Sun.COM int i;
3268693SKerry.Shu@Sun.COM
3278693SKerry.Shu@Sun.COM /* bind video switch notify */
3288693SKerry.Shu@Sun.COM vidsp = vidp->vid_switch;
3298693SKerry.Shu@Sun.COM for (i = 0; i < vidp->total_switch && vidsp != NULL; i++) {
3308693SKerry.Shu@Sun.COM status = AcpiInstallNotifyHandler(vidsp->dev.hdl,
3318693SKerry.Shu@Sun.COM ACPI_DEVICE_NOTIFY, acpi_video_switch_notify, vidsp);
3328693SKerry.Shu@Sun.COM if (ACPI_FAILURE(status)) {
3338693SKerry.Shu@Sun.COM cmn_err(CE_WARN,
3348693SKerry.Shu@Sun.COM "!vids handler install failed = %d, vids = %p.",
3358693SKerry.Shu@Sun.COM status, (void *) vidsp);
3368693SKerry.Shu@Sun.COM }
3378693SKerry.Shu@Sun.COM vidsp = vidsp->next;
3388693SKerry.Shu@Sun.COM }
3398693SKerry.Shu@Sun.COM
3408693SKerry.Shu@Sun.COM /* bind brightness control notify */
3418693SKerry.Shu@Sun.COM vidbp = vidp->vid_brightness;
3428693SKerry.Shu@Sun.COM for (i = 0; i < vidp->total_brightness && vidbp != NULL; i++) {
3438693SKerry.Shu@Sun.COM status = AcpiInstallNotifyHandler(vidbp->dev.hdl,
3448693SKerry.Shu@Sun.COM ACPI_DEVICE_NOTIFY, acpi_video_brightness_notify, vidbp);
3458693SKerry.Shu@Sun.COM if (ACPI_FAILURE(status)) {
3468693SKerry.Shu@Sun.COM cmn_err(CE_WARN,
3478693SKerry.Shu@Sun.COM "!brightness handler install failed = %x, "
3488693SKerry.Shu@Sun.COM "brightness = %p.", status, (void *) vidbp);
3498693SKerry.Shu@Sun.COM }
3508693SKerry.Shu@Sun.COM vidbp = vidbp->next;
3518693SKerry.Shu@Sun.COM }
3528693SKerry.Shu@Sun.COM
3538693SKerry.Shu@Sun.COM return (ACPI_DRV_OK);
3548693SKerry.Shu@Sun.COM }
3558693SKerry.Shu@Sun.COM
3568693SKerry.Shu@Sun.COM static int
acpi_video_notify_unintall(struct acpi_video * vidp)3578693SKerry.Shu@Sun.COM acpi_video_notify_unintall(struct acpi_video *vidp)
3588693SKerry.Shu@Sun.COM {
3598693SKerry.Shu@Sun.COM struct acpi_video_switch *vidsp;
3608693SKerry.Shu@Sun.COM struct acpi_video_brightness *vidbp;
3618693SKerry.Shu@Sun.COM int i;
3628693SKerry.Shu@Sun.COM
3638693SKerry.Shu@Sun.COM /* unbind video switch notify */
3648693SKerry.Shu@Sun.COM vidsp = vidp->vid_switch;
3658693SKerry.Shu@Sun.COM for (i = 0; i < vidp->total_switch && vidsp != NULL; i++) {
3668693SKerry.Shu@Sun.COM (void) AcpiRemoveNotifyHandler(vidsp->dev.hdl,
3678693SKerry.Shu@Sun.COM ACPI_DEVICE_NOTIFY, acpi_video_switch_notify);
3688693SKerry.Shu@Sun.COM vidsp = vidsp->next;
3698693SKerry.Shu@Sun.COM }
3708693SKerry.Shu@Sun.COM
3718693SKerry.Shu@Sun.COM /* unbind brightness control notify */
3728693SKerry.Shu@Sun.COM vidbp = vidp->vid_brightness;
3738693SKerry.Shu@Sun.COM for (i = 0; i < vidp->total_brightness && vidbp != NULL; i++) {
3748693SKerry.Shu@Sun.COM (void) AcpiRemoveNotifyHandler(vidbp->dev.hdl,
3758693SKerry.Shu@Sun.COM ACPI_DEVICE_NOTIFY, acpi_video_brightness_notify);
3768693SKerry.Shu@Sun.COM vidbp = vidbp->next;
3778693SKerry.Shu@Sun.COM }
3788693SKerry.Shu@Sun.COM
3798693SKerry.Shu@Sun.COM return (ACPI_DRV_OK);
3808693SKerry.Shu@Sun.COM }
3818693SKerry.Shu@Sun.COM
3828693SKerry.Shu@Sun.COM static int
acpi_video_free(struct acpi_video * vidp)3838693SKerry.Shu@Sun.COM acpi_video_free(struct acpi_video *vidp)
3848693SKerry.Shu@Sun.COM {
3858693SKerry.Shu@Sun.COM struct acpi_video_switch *vidsp;
3868693SKerry.Shu@Sun.COM struct acpi_video_switch *vidsp_next;
3878693SKerry.Shu@Sun.COM struct acpi_video_brightness *vidbp;
3888693SKerry.Shu@Sun.COM struct acpi_video_brightness *vidbp_next;
3898693SKerry.Shu@Sun.COM struct acpi_video_output *vidop;
3908693SKerry.Shu@Sun.COM struct acpi_video_output *vidop_next;
3918693SKerry.Shu@Sun.COM
3928693SKerry.Shu@Sun.COM /* free video switch objects */
3938693SKerry.Shu@Sun.COM vidsp = vidp->vid_switch;
3948693SKerry.Shu@Sun.COM while (vidsp != NULL) {
3958693SKerry.Shu@Sun.COM vidsp_next = vidsp->next;
3968693SKerry.Shu@Sun.COM kmem_free(vidsp, sizeof (struct acpi_video_switch));
3978693SKerry.Shu@Sun.COM vidsp = vidsp_next;
3988693SKerry.Shu@Sun.COM }
3998693SKerry.Shu@Sun.COM
4008693SKerry.Shu@Sun.COM /* free video brightness control objects */
4018693SKerry.Shu@Sun.COM vidbp = vidp->vid_brightness;
4028693SKerry.Shu@Sun.COM while (vidbp != NULL) {
4038693SKerry.Shu@Sun.COM vidbp_next = vidbp->next;
4048693SKerry.Shu@Sun.COM kmem_free(vidbp, sizeof (struct acpi_video_brightness));
4058693SKerry.Shu@Sun.COM vidbp = vidbp_next;
4068693SKerry.Shu@Sun.COM }
4078693SKerry.Shu@Sun.COM
4088693SKerry.Shu@Sun.COM /* free video output objects */
4098693SKerry.Shu@Sun.COM vidop = vidp->vid_outputs;
4108693SKerry.Shu@Sun.COM while (vidop != NULL) {
4118693SKerry.Shu@Sun.COM vidop_next = vidop->next;
4128693SKerry.Shu@Sun.COM kmem_free(vidop, sizeof (struct acpi_video_output));
4138693SKerry.Shu@Sun.COM vidop = vidop_next;
4148693SKerry.Shu@Sun.COM }
4158693SKerry.Shu@Sun.COM
4168693SKerry.Shu@Sun.COM return (ACPI_DRV_OK);
4178693SKerry.Shu@Sun.COM }
4188693SKerry.Shu@Sun.COM
4198693SKerry.Shu@Sun.COM static int
acpi_video_fini(struct acpi_video * vidp)4208693SKerry.Shu@Sun.COM acpi_video_fini(struct acpi_video *vidp)
4218693SKerry.Shu@Sun.COM {
4228693SKerry.Shu@Sun.COM (void) acpi_video_notify_unintall(vidp);
4238693SKerry.Shu@Sun.COM
4248693SKerry.Shu@Sun.COM return (acpi_video_free(vidp));
4258693SKerry.Shu@Sun.COM }
4268693SKerry.Shu@Sun.COM
4278693SKerry.Shu@Sun.COM static int
acpi_video_enum_output(ACPI_HANDLE hdl,struct acpi_video * vidp)4288693SKerry.Shu@Sun.COM acpi_video_enum_output(ACPI_HANDLE hdl, struct acpi_video *vidp)
4298693SKerry.Shu@Sun.COM {
4308693SKerry.Shu@Sun.COM int adr;
4318693SKerry.Shu@Sun.COM struct acpi_video_brightness *vidbp;
4328693SKerry.Shu@Sun.COM struct acpi_video_output *vidop;
4338693SKerry.Shu@Sun.COM ACPI_BUFFER buf = {ACPI_ALLOCATE_BUFFER, NULL};
4348693SKerry.Shu@Sun.COM ACPI_OBJECT *objp;
4358693SKerry.Shu@Sun.COM
4368693SKerry.Shu@Sun.COM
4378693SKerry.Shu@Sun.COM if (acpica_eval_int(hdl, "_ADR", &adr) != AE_OK)
4388693SKerry.Shu@Sun.COM return (ACPI_DRV_ERR);
4398693SKerry.Shu@Sun.COM
4408693SKerry.Shu@Sun.COM /* Allocate object */
4418693SKerry.Shu@Sun.COM vidop = kmem_zalloc(sizeof (struct acpi_video_output), KM_SLEEP);
4428693SKerry.Shu@Sun.COM vidop->dev.hdl = hdl;
4438693SKerry.Shu@Sun.COM (void) acpi_drv_dev_init(&vidop->dev);
4448693SKerry.Shu@Sun.COM vidop->adr = adr;
4458693SKerry.Shu@Sun.COM vidop->type = adr;
4468693SKerry.Shu@Sun.COM vidop->next = vidp->vid_outputs;
4478693SKerry.Shu@Sun.COM vidp->vid_outputs = vidop;
4488693SKerry.Shu@Sun.COM
4498693SKerry.Shu@Sun.COM if (ACPI_SUCCESS(AcpiEvaluateObjectTyped(hdl, "_BCL",
4508693SKerry.Shu@Sun.COM NULL, &buf, ACPI_TYPE_PACKAGE))) {
4518693SKerry.Shu@Sun.COM int i, j, k, l, m, nlev, tmp;
4528693SKerry.Shu@Sun.COM
4538693SKerry.Shu@Sun.COM vidbp = kmem_zalloc(sizeof (struct acpi_video_brightness),
4548693SKerry.Shu@Sun.COM KM_SLEEP);
4558693SKerry.Shu@Sun.COM vidbp->dev = vidop->dev;
4568693SKerry.Shu@Sun.COM vidop->adr = adr;
4578693SKerry.Shu@Sun.COM vidbp->output_index = vidp->total_outputs;
4588693SKerry.Shu@Sun.COM objp = buf.Pointer;
4598693SKerry.Shu@Sun.COM
4608693SKerry.Shu@Sun.COM /*
4618693SKerry.Shu@Sun.COM * op->nlev will be needed to free op->levels.
4628693SKerry.Shu@Sun.COM */
4638693SKerry.Shu@Sun.COM vidbp->nlevel = nlev = objp->Package.Count;
4648693SKerry.Shu@Sun.COM vidbp->levels = kmem_zalloc(nlev * sizeof (uint32_t), KM_SLEEP);
4658693SKerry.Shu@Sun.COM
4668693SKerry.Shu@Sun.COM /*
4678693SKerry.Shu@Sun.COM * Get all the supported brightness levels.
4688693SKerry.Shu@Sun.COM */
4698693SKerry.Shu@Sun.COM for (i = 0; i < nlev; i++) {
4708693SKerry.Shu@Sun.COM ACPI_OBJECT *o = &objp->Package.Elements[i];
4718693SKerry.Shu@Sun.COM int lev = o->Integer.Value;
4728693SKerry.Shu@Sun.COM
4738693SKerry.Shu@Sun.COM if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
4748693SKerry.Shu@Sun.COM cmn_err(CE_NOTE, "!acpi_video_enum_output() "
4758693SKerry.Shu@Sun.COM "brlev=%d i=%d nlev=%d\n", lev, i, nlev);
4768693SKerry.Shu@Sun.COM }
4778693SKerry.Shu@Sun.COM if (o->Type != ACPI_TYPE_INTEGER) {
4788693SKerry.Shu@Sun.COM continue;
4798693SKerry.Shu@Sun.COM }
4808693SKerry.Shu@Sun.COM vidbp->levels[i] = lev;
4818693SKerry.Shu@Sun.COM }
4828693SKerry.Shu@Sun.COM
4838693SKerry.Shu@Sun.COM /*
4848693SKerry.Shu@Sun.COM * Sort the brightness levels.
4858693SKerry.Shu@Sun.COM */
4868693SKerry.Shu@Sun.COM for (j = 0; j < nlev; j++) {
4878693SKerry.Shu@Sun.COM for (k = 0; k < nlev - 1; k++) {
4888693SKerry.Shu@Sun.COM if (vidbp->levels[k] > vidbp->levels[k+1]) {
4898693SKerry.Shu@Sun.COM tmp = vidbp->levels[k+1];
4908693SKerry.Shu@Sun.COM vidbp->levels[k+1] = vidbp->levels[k];
4918693SKerry.Shu@Sun.COM vidbp->levels[k] = tmp;
4928693SKerry.Shu@Sun.COM }
4938693SKerry.Shu@Sun.COM }
4948693SKerry.Shu@Sun.COM }
4958693SKerry.Shu@Sun.COM
4968693SKerry.Shu@Sun.COM /*
4978693SKerry.Shu@Sun.COM * The first two levels could be duplicated, so remove
4988693SKerry.Shu@Sun.COM * any duplicates.
4998693SKerry.Shu@Sun.COM */
5008693SKerry.Shu@Sun.COM for (l = 0; l < nlev - 1; l++) {
5018693SKerry.Shu@Sun.COM if (vidbp->levels[l] == vidbp->levels[l+1]) {
5028693SKerry.Shu@Sun.COM for (m = l + 1; m < nlev - 1; m++) {
5038693SKerry.Shu@Sun.COM vidbp->levels[m] = vidbp->levels[m+1];
5048693SKerry.Shu@Sun.COM }
5058693SKerry.Shu@Sun.COM nlev--;
5068693SKerry.Shu@Sun.COM }
5078693SKerry.Shu@Sun.COM }
5088693SKerry.Shu@Sun.COM
5098693SKerry.Shu@Sun.COM vidbp->nlevel = nlev;
5108693SKerry.Shu@Sun.COM (void) acpi_video_brightness_get(vidbp);
5118693SKerry.Shu@Sun.COM vidbp->next = vidp->vid_brightness;
5128693SKerry.Shu@Sun.COM vidp->vid_brightness = vidbp;
5138693SKerry.Shu@Sun.COM vidp->total_brightness++;
5148693SKerry.Shu@Sun.COM
5158693SKerry.Shu@Sun.COM AcpiOsFree(objp);
5168693SKerry.Shu@Sun.COM }
5178693SKerry.Shu@Sun.COM
5188693SKerry.Shu@Sun.COM vidp->total_outputs++;
5198693SKerry.Shu@Sun.COM
5208693SKerry.Shu@Sun.COM return (ACPI_DRV_OK);
5218693SKerry.Shu@Sun.COM }
5228693SKerry.Shu@Sun.COM
5238693SKerry.Shu@Sun.COM /*ARGSUSED*/
5248693SKerry.Shu@Sun.COM static ACPI_STATUS
acpi_video_find_and_alloc(ACPI_HANDLE hdl,UINT32 nest,void * ctx,void ** rv)5258693SKerry.Shu@Sun.COM acpi_video_find_and_alloc(ACPI_HANDLE hdl, UINT32 nest, void *ctx,
5268693SKerry.Shu@Sun.COM void **rv)
5278693SKerry.Shu@Sun.COM {
5288693SKerry.Shu@Sun.COM ACPI_HANDLE tmphdl;
5298693SKerry.Shu@Sun.COM ACPI_STATUS err;
5308693SKerry.Shu@Sun.COM ACPI_BUFFER buf = {ACPI_ALLOCATE_BUFFER, NULL};
5318693SKerry.Shu@Sun.COM struct acpi_video *vidp;
5328693SKerry.Shu@Sun.COM struct acpi_video_switch *vidsp;
5338693SKerry.Shu@Sun.COM
5348693SKerry.Shu@Sun.COM err = AcpiGetHandle(hdl, ACPI_METHOD_DOS, &tmphdl);
5358693SKerry.Shu@Sun.COM if (err != AE_OK)
5368693SKerry.Shu@Sun.COM return (AE_OK);
5378693SKerry.Shu@Sun.COM
5388693SKerry.Shu@Sun.COM err = AcpiGetHandle(hdl, ACPI_METHOD_DOD, &tmphdl);
5398693SKerry.Shu@Sun.COM if (err != AE_OK)
5408693SKerry.Shu@Sun.COM return (AE_OK);
5418693SKerry.Shu@Sun.COM
5428693SKerry.Shu@Sun.COM vidp = (struct acpi_video *)ctx;
5438693SKerry.Shu@Sun.COM vidsp = kmem_zalloc(sizeof (struct acpi_video_switch), KM_SLEEP);
5448693SKerry.Shu@Sun.COM vidsp->dev.hdl = hdl;
5458693SKerry.Shu@Sun.COM (void) acpi_drv_dev_init(&vidsp->dev);
5468693SKerry.Shu@Sun.COM vidsp->next = vidp->vid_switch;
5478693SKerry.Shu@Sun.COM vidp->vid_switch = vidsp;
5488693SKerry.Shu@Sun.COM vidp->total_switch++;
5498693SKerry.Shu@Sun.COM
5508693SKerry.Shu@Sun.COM /*
5518693SKerry.Shu@Sun.COM * Enumerate the output devices.
5528693SKerry.Shu@Sun.COM */
5538693SKerry.Shu@Sun.COM while (ACPI_SUCCESS(AcpiGetNextObject(ACPI_TYPE_DEVICE,
5548693SKerry.Shu@Sun.COM hdl, tmphdl, &tmphdl))) {
5558693SKerry.Shu@Sun.COM (void) acpi_video_enum_output(tmphdl, vidp);
5568693SKerry.Shu@Sun.COM }
5578693SKerry.Shu@Sun.COM
5588693SKerry.Shu@Sun.COM if (!ACPI_FAILURE(AcpiGetName(hdl, ACPI_FULL_PATHNAME, &buf))) {
5598693SKerry.Shu@Sun.COM if (buf.Pointer) {
5608693SKerry.Shu@Sun.COM if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
5618693SKerry.Shu@Sun.COM cmn_err(CE_NOTE,
5628693SKerry.Shu@Sun.COM "!acpi video switch hdl = 0x%p, path = %s.",
5638693SKerry.Shu@Sun.COM hdl, (char *)buf.Pointer);
5648693SKerry.Shu@Sun.COM }
5658693SKerry.Shu@Sun.COM AcpiOsFree(buf.Pointer);
5668693SKerry.Shu@Sun.COM }
5678693SKerry.Shu@Sun.COM }
5688693SKerry.Shu@Sun.COM
5698693SKerry.Shu@Sun.COM return (AE_OK);
5708693SKerry.Shu@Sun.COM }
5718693SKerry.Shu@Sun.COM
5728693SKerry.Shu@Sun.COM int
hotkey_brightness_inc(hotkey_drv_t * htkp)5738693SKerry.Shu@Sun.COM hotkey_brightness_inc(hotkey_drv_t *htkp)
5748693SKerry.Shu@Sun.COM {
5758693SKerry.Shu@Sun.COM struct acpi_video *vidp;
5768693SKerry.Shu@Sun.COM struct acpi_video_brightness *vidbp;
5778693SKerry.Shu@Sun.COM
5788693SKerry.Shu@Sun.COM vidp = (struct acpi_video *)htkp->acpi_video;
5798693SKerry.Shu@Sun.COM
5808693SKerry.Shu@Sun.COM for (vidbp = vidp->vid_brightness; vidbp != NULL; vidbp = vidbp->next) {
5818693SKerry.Shu@Sun.COM if (vidbp->cur_level_index < vidbp->nlevel - 1) {
5828693SKerry.Shu@Sun.COM if (acpi_video_brightness_set(vidbp,
5838693SKerry.Shu@Sun.COM vidbp->cur_level_index + 1) != ACPI_DRV_OK) {
5848693SKerry.Shu@Sun.COM return (ACPI_DRV_ERR);
5858693SKerry.Shu@Sun.COM }
5868693SKerry.Shu@Sun.COM }
5878693SKerry.Shu@Sun.COM }
5888693SKerry.Shu@Sun.COM return (ACPI_DRV_OK);
5898693SKerry.Shu@Sun.COM }
5908693SKerry.Shu@Sun.COM
5918693SKerry.Shu@Sun.COM int
hotkey_brightness_dec(hotkey_drv_t * htkp)5928693SKerry.Shu@Sun.COM hotkey_brightness_dec(hotkey_drv_t *htkp)
5938693SKerry.Shu@Sun.COM {
5948693SKerry.Shu@Sun.COM struct acpi_video *vidp;
5958693SKerry.Shu@Sun.COM struct acpi_video_brightness *vidbp;
5968693SKerry.Shu@Sun.COM
5978693SKerry.Shu@Sun.COM vidp = (struct acpi_video *)htkp->acpi_video;
5988693SKerry.Shu@Sun.COM
5998693SKerry.Shu@Sun.COM for (vidbp = vidp->vid_brightness; vidbp != NULL; vidbp = vidbp->next) {
6008693SKerry.Shu@Sun.COM if (vidbp->cur_level_index > 0) {
6018693SKerry.Shu@Sun.COM if (acpi_video_brightness_set(vidbp,
6028693SKerry.Shu@Sun.COM vidbp->cur_level_index - 1) != ACPI_DRV_OK) {
6038693SKerry.Shu@Sun.COM return (ACPI_DRV_ERR);
6048693SKerry.Shu@Sun.COM }
6058693SKerry.Shu@Sun.COM }
6068693SKerry.Shu@Sun.COM }
6078693SKerry.Shu@Sun.COM
6088693SKerry.Shu@Sun.COM return (ACPI_DRV_OK);
6098693SKerry.Shu@Sun.COM }
6108693SKerry.Shu@Sun.COM
6118693SKerry.Shu@Sun.COM /*ARGSUSED*/
6128693SKerry.Shu@Sun.COM int
acpi_video_ioctl(void * p,int cmd,intptr_t arg,int mode,cred_t * cr,int * rval)6138693SKerry.Shu@Sun.COM acpi_video_ioctl(void *p, int cmd, intptr_t arg, int mode, cred_t *cr,
6148693SKerry.Shu@Sun.COM int *rval)
6158693SKerry.Shu@Sun.COM {
6168693SKerry.Shu@Sun.COM struct acpi_video *vidp = p;
6178693SKerry.Shu@Sun.COM struct acpi_video_brightness *vidbp;
6188693SKerry.Shu@Sun.COM int res = 0;
6198693SKerry.Shu@Sun.COM
6208693SKerry.Shu@Sun.COM if (vidp == NULL)
6218693SKerry.Shu@Sun.COM return (ENXIO);
6228693SKerry.Shu@Sun.COM
6238693SKerry.Shu@Sun.COM vidbp = vidp->vid_brightness;
6248693SKerry.Shu@Sun.COM if (vidbp == NULL)
6258693SKerry.Shu@Sun.COM return (ENXIO);
6268693SKerry.Shu@Sun.COM
6278693SKerry.Shu@Sun.COM if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
6288693SKerry.Shu@Sun.COM cmn_err(CE_NOTE, "!acpi_video_ioctl cmd %d\n", cmd);
6298693SKerry.Shu@Sun.COM }
6308693SKerry.Shu@Sun.COM
6318693SKerry.Shu@Sun.COM switch (cmd) {
6328693SKerry.Shu@Sun.COM case ACPI_DRV_IOC_INFO:
6338693SKerry.Shu@Sun.COM {
6348693SKerry.Shu@Sun.COM struct acpi_drv_output_info inf;
6358693SKerry.Shu@Sun.COM
6368693SKerry.Shu@Sun.COM inf.adr = vidbp->adr;
6378693SKerry.Shu@Sun.COM inf.nlev = vidbp->nlevel;
6388693SKerry.Shu@Sun.COM if (copyout(&inf, (void *)arg, sizeof (inf))) {
6398693SKerry.Shu@Sun.COM res = EFAULT;
6408693SKerry.Shu@Sun.COM }
6418693SKerry.Shu@Sun.COM break;
6428693SKerry.Shu@Sun.COM }
6438693SKerry.Shu@Sun.COM
6448693SKerry.Shu@Sun.COM case ACPI_DRV_IOC_LEVELS:
6458693SKerry.Shu@Sun.COM if (copyout(vidbp->levels, (void *)arg,
6468693SKerry.Shu@Sun.COM sizeof (*vidbp->levels) * vidbp->nlevel)) {
6478693SKerry.Shu@Sun.COM res = EFAULT;
6488693SKerry.Shu@Sun.COM }
6498693SKerry.Shu@Sun.COM break;
6508693SKerry.Shu@Sun.COM
6518693SKerry.Shu@Sun.COM case ACPI_DRV_IOC_STATUS:
6528693SKerry.Shu@Sun.COM {
6538693SKerry.Shu@Sun.COM /*
6548693SKerry.Shu@Sun.COM * Need to get the current levels through ACPI first
6558693SKerry.Shu@Sun.COM * then go through array of levels to find index.
6568693SKerry.Shu@Sun.COM */
6578693SKerry.Shu@Sun.COM struct acpi_drv_output_status status;
6588693SKerry.Shu@Sun.COM int i;
6598693SKerry.Shu@Sun.COM
6608693SKerry.Shu@Sun.COM status.state = 0;
6618693SKerry.Shu@Sun.COM status.num_levels = vidbp->nlevel;
6628693SKerry.Shu@Sun.COM status.cur_level = vidbp->cur_level;
6638693SKerry.Shu@Sun.COM for (i = 0; i < vidbp->nlevel; i++) {
6648693SKerry.Shu@Sun.COM if (vidbp->levels[i] == vidbp->cur_level) {
6658693SKerry.Shu@Sun.COM status.cur_level_index = i;
6668693SKerry.Shu@Sun.COM if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
6678693SKerry.Shu@Sun.COM cmn_err(CE_NOTE, "!ACPI_DRV_IOC_STATUS "
6688693SKerry.Shu@Sun.COM "cur_level_index %d\n", i);
6698693SKerry.Shu@Sun.COM }
6708693SKerry.Shu@Sun.COM break;
6718693SKerry.Shu@Sun.COM }
6728693SKerry.Shu@Sun.COM }
6738693SKerry.Shu@Sun.COM if (copyout(&status, (void *)arg, sizeof (status))) {
6748693SKerry.Shu@Sun.COM res = EFAULT;
6758693SKerry.Shu@Sun.COM }
6768693SKerry.Shu@Sun.COM break;
6778693SKerry.Shu@Sun.COM }
6788693SKerry.Shu@Sun.COM
6798693SKerry.Shu@Sun.COM case ACPI_DRV_IOC_SET_BRIGHTNESS: {
6808693SKerry.Shu@Sun.COM int level;
6818693SKerry.Shu@Sun.COM
6828693SKerry.Shu@Sun.COM if (drv_priv(cr)) {
6838693SKerry.Shu@Sun.COM res = EPERM;
6848693SKerry.Shu@Sun.COM break;
6858693SKerry.Shu@Sun.COM }
6868693SKerry.Shu@Sun.COM if (copyin((void *)arg, &level, sizeof (level))) {
6878693SKerry.Shu@Sun.COM res = EFAULT;
6888693SKerry.Shu@Sun.COM break;
6898693SKerry.Shu@Sun.COM }
6908693SKerry.Shu@Sun.COM if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
6918693SKerry.Shu@Sun.COM cmn_err(CE_NOTE,
6928693SKerry.Shu@Sun.COM "!acpi_video_ioctl: set BRIGHTNESS level=%d\n",
6938693SKerry.Shu@Sun.COM level);
6948693SKerry.Shu@Sun.COM }
6958693SKerry.Shu@Sun.COM if (acpi_video_brightness_set(vidbp, level) != ACPI_DRV_OK) {
6968693SKerry.Shu@Sun.COM res = EFAULT;
6978693SKerry.Shu@Sun.COM }
6988693SKerry.Shu@Sun.COM break;
6998693SKerry.Shu@Sun.COM }
7008693SKerry.Shu@Sun.COM
7018693SKerry.Shu@Sun.COM default:
7028693SKerry.Shu@Sun.COM res = EINVAL;
7038693SKerry.Shu@Sun.COM break;
7048693SKerry.Shu@Sun.COM }
7058693SKerry.Shu@Sun.COM
7068693SKerry.Shu@Sun.COM return (res);
7078693SKerry.Shu@Sun.COM }
7088693SKerry.Shu@Sun.COM
7098693SKerry.Shu@Sun.COM /*ARGSUSED*/
7108693SKerry.Shu@Sun.COM int
acpi_drv_hotkey_ioctl(int cmd,intptr_t arg,int mode,cred_t * cr,int * rval)7118693SKerry.Shu@Sun.COM acpi_drv_hotkey_ioctl(int cmd, intptr_t arg, int mode, cred_t *cr,
7128693SKerry.Shu@Sun.COM int *rval)
7138693SKerry.Shu@Sun.COM {
7148693SKerry.Shu@Sun.COM hotkey_drv_t *htkp = &acpi_hotkey;
7158693SKerry.Shu@Sun.COM
7168693SKerry.Shu@Sun.COM switch (htkp->hotkey_method) {
7178693SKerry.Shu@Sun.COM case HOTKEY_METHOD_ACPI_VIDEO:
7188693SKerry.Shu@Sun.COM return (acpi_video_ioctl(htkp->acpi_video, cmd, arg, mode,
7198693SKerry.Shu@Sun.COM cr, rval));
7208693SKerry.Shu@Sun.COM case HOTKEY_METHOD_MISC:
7218693SKerry.Shu@Sun.COM case HOTKEY_METHOD_VENDOR:
7228693SKerry.Shu@Sun.COM return (htkp->vendor_ioctl(htkp, cmd, arg, mode, cr, rval));
7238693SKerry.Shu@Sun.COM case HOTKEY_METHOD_NONE:
7248693SKerry.Shu@Sun.COM default:
7258693SKerry.Shu@Sun.COM return (ENXIO);
7268693SKerry.Shu@Sun.COM }
7278693SKerry.Shu@Sun.COM }
7288693SKerry.Shu@Sun.COM
729*12122SKerry.Shu@Sun.COM static void
acpi_video_check_blacklist(void)730*12122SKerry.Shu@Sun.COM acpi_video_check_blacklist(void)
731*12122SKerry.Shu@Sun.COM {
732*12122SKerry.Shu@Sun.COM smbios_hdl_t *smhdl = NULL;
733*12122SKerry.Shu@Sun.COM id_t smid;
734*12122SKerry.Shu@Sun.COM smbios_system_t smsys;
735*12122SKerry.Shu@Sun.COM smbios_info_t sminfo;
736*12122SKerry.Shu@Sun.COM char *mfg, *product;
737*12122SKerry.Shu@Sun.COM struct acpi_video_smbios_info *pblacklist;
738*12122SKerry.Shu@Sun.COM
739*12122SKerry.Shu@Sun.COM acpi_brightness_get_disable = 0;
740*12122SKerry.Shu@Sun.COM smhdl = smbios_open(NULL, SMB_VERSION, ksmbios_flags, NULL);
741*12122SKerry.Shu@Sun.COM if (smhdl == NULL ||
742*12122SKerry.Shu@Sun.COM ((smid = smbios_info_system(smhdl, &smsys)) == SMB_ERR) ||
743*12122SKerry.Shu@Sun.COM (smbios_info_common(smhdl, smid, &sminfo) == SMB_ERR)) {
744*12122SKerry.Shu@Sun.COM goto done;
745*12122SKerry.Shu@Sun.COM }
746*12122SKerry.Shu@Sun.COM
747*12122SKerry.Shu@Sun.COM mfg = (char *)sminfo.smbi_manufacturer;
748*12122SKerry.Shu@Sun.COM product = (char *)sminfo.smbi_product;
749*12122SKerry.Shu@Sun.COM for (pblacklist = acpi_brightness_get_blacklist;
750*12122SKerry.Shu@Sun.COM pblacklist->manufacturer != NULL; pblacklist++) {
751*12122SKerry.Shu@Sun.COM if ((strcmp(mfg, pblacklist->manufacturer) == 0) &&
752*12122SKerry.Shu@Sun.COM (strcmp(product, pblacklist->product) == 0)) {
753*12122SKerry.Shu@Sun.COM acpi_brightness_get_disable = 1;
754*12122SKerry.Shu@Sun.COM }
755*12122SKerry.Shu@Sun.COM }
756*12122SKerry.Shu@Sun.COM done:
757*12122SKerry.Shu@Sun.COM if (smhdl != NULL)
758*12122SKerry.Shu@Sun.COM smbios_close(smhdl);
759*12122SKerry.Shu@Sun.COM }
760*12122SKerry.Shu@Sun.COM
7618693SKerry.Shu@Sun.COM static int
hotkey_acpi_video_check(hotkey_drv_t * htkp)7628693SKerry.Shu@Sun.COM hotkey_acpi_video_check(hotkey_drv_t *htkp)
7638693SKerry.Shu@Sun.COM {
7648693SKerry.Shu@Sun.COM struct acpi_video *vidp;
7658693SKerry.Shu@Sun.COM
7668693SKerry.Shu@Sun.COM vidp = &acpi_video_hotkey;
7678693SKerry.Shu@Sun.COM bzero(vidp, sizeof (struct acpi_video));
768*12122SKerry.Shu@Sun.COM if (acpi_brightness_get_disable == -1)
769*12122SKerry.Shu@Sun.COM acpi_video_check_blacklist();
7708693SKerry.Shu@Sun.COM /* Find ACPI Video device handle */
7718693SKerry.Shu@Sun.COM if (ACPI_FAILURE(AcpiGetDevices(NULL, acpi_video_find_and_alloc,
7728693SKerry.Shu@Sun.COM vidp, NULL))) {
7738693SKerry.Shu@Sun.COM return (ACPI_DRV_ERR);
7748693SKerry.Shu@Sun.COM }
7758693SKerry.Shu@Sun.COM
7768693SKerry.Shu@Sun.COM htkp->acpi_video = vidp;
7778693SKerry.Shu@Sun.COM if (htkp->hotkey_method == HOTKEY_METHOD_NONE) {
7788693SKerry.Shu@Sun.COM if (acpi_video_notify_intall(vidp) != ACPI_DRV_OK) {
7798693SKerry.Shu@Sun.COM (void) acpi_video_fini(vidp);
7808693SKerry.Shu@Sun.COM htkp->acpi_video = NULL;
7818693SKerry.Shu@Sun.COM return (ACPI_DRV_ERR);
7828693SKerry.Shu@Sun.COM }
7838693SKerry.Shu@Sun.COM }
7848693SKerry.Shu@Sun.COM htkp->hotkey_method |= HOTKEY_METHOD_ACPI_VIDEO;
7858693SKerry.Shu@Sun.COM
7868693SKerry.Shu@Sun.COM acpi_video_set_dos(vidp, VIDEO_POLICY_BRIGHTNESS_OS |
7878693SKerry.Shu@Sun.COM VIDEO_POLICY_SWITCH_OS);
7888693SKerry.Shu@Sun.COM
7898693SKerry.Shu@Sun.COM return (ACPI_DRV_OK);
7908693SKerry.Shu@Sun.COM }
7918693SKerry.Shu@Sun.COM
7928693SKerry.Shu@Sun.COM int
hotkey_init(hotkey_drv_t * htkp)7938693SKerry.Shu@Sun.COM hotkey_init(hotkey_drv_t *htkp)
7948693SKerry.Shu@Sun.COM {
7958693SKerry.Shu@Sun.COM int i;
7968693SKerry.Shu@Sun.COM int modid;
7978693SKerry.Shu@Sun.COM modctl_t *modp;
7988693SKerry.Shu@Sun.COM
7998693SKerry.Shu@Sun.COM htkp->modid = -1;
8008693SKerry.Shu@Sun.COM /* Try to find vendor specific method */
8018693SKerry.Shu@Sun.COM for (i = 0; vendor_hotkey_drv_list[i].module != NULL; i++) {
8028693SKerry.Shu@Sun.COM if (!vendor_hotkey_drv_list[i].enable)
8038693SKerry.Shu@Sun.COM continue;
8048693SKerry.Shu@Sun.COM
8058693SKerry.Shu@Sun.COM if ((modid = modload("drv", vendor_hotkey_drv_list[i].module))
8068693SKerry.Shu@Sun.COM == -1) {
8078693SKerry.Shu@Sun.COM continue;
8088693SKerry.Shu@Sun.COM }
8098693SKerry.Shu@Sun.COM
8108693SKerry.Shu@Sun.COM htkp->modid = modid;
8118693SKerry.Shu@Sun.COM if (hotkey_drv_debug & HOTKEY_DBG_NOTICE) {
8128693SKerry.Shu@Sun.COM cmn_err(CE_NOTE, "!loaded %s specific method.\n",
8138693SKerry.Shu@Sun.COM vendor_hotkey_drv_list[i].vid);
8148693SKerry.Shu@Sun.COM }
8158693SKerry.Shu@Sun.COM }
8168693SKerry.Shu@Sun.COM
8178693SKerry.Shu@Sun.COM /* Check availability of ACPI Video Extension method */
8188693SKerry.Shu@Sun.COM if (htkp->hotkey_method == HOTKEY_METHOD_NONE ||
8198693SKerry.Shu@Sun.COM htkp->check_acpi_video) {
8208693SKerry.Shu@Sun.COM if (hotkey_acpi_video_check(htkp) == ACPI_DRV_OK) {
8218693SKerry.Shu@Sun.COM if (hotkey_drv_debug & HOTKEY_DBG_NOTICE)
8228693SKerry.Shu@Sun.COM cmn_err(CE_NOTE, "!find ACPI video method.\n");
8238693SKerry.Shu@Sun.COM } else
8248693SKerry.Shu@Sun.COM goto fail;
8258693SKerry.Shu@Sun.COM }
8268693SKerry.Shu@Sun.COM
8278693SKerry.Shu@Sun.COM if (htkp->modid != -1) {
8288693SKerry.Shu@Sun.COM modp = mod_hold_by_id(htkp->modid);
8298693SKerry.Shu@Sun.COM mutex_enter(&mod_lock);
8308693SKerry.Shu@Sun.COM modp->mod_ref = 1;
8318693SKerry.Shu@Sun.COM modp->mod_loadflags |= MOD_NOAUTOUNLOAD;
8328693SKerry.Shu@Sun.COM mutex_exit(&mod_lock);
8338693SKerry.Shu@Sun.COM mod_release_mod(modp);
8348693SKerry.Shu@Sun.COM }
8358693SKerry.Shu@Sun.COM
8368693SKerry.Shu@Sun.COM return (ACPI_DRV_OK);
8378693SKerry.Shu@Sun.COM
8388693SKerry.Shu@Sun.COM fail:
8398693SKerry.Shu@Sun.COM if (htkp->vendor_fini != NULL)
8408693SKerry.Shu@Sun.COM htkp->vendor_fini(htkp);
8418693SKerry.Shu@Sun.COM if (htkp->modid != -1)
8428693SKerry.Shu@Sun.COM (void) modunload(htkp->modid);
8438693SKerry.Shu@Sun.COM
8448693SKerry.Shu@Sun.COM return (ACPI_DRV_ERR);
8458693SKerry.Shu@Sun.COM }
8468693SKerry.Shu@Sun.COM
8478693SKerry.Shu@Sun.COM
8488693SKerry.Shu@Sun.COM int
hotkey_fini(hotkey_drv_t * htkp)8498693SKerry.Shu@Sun.COM hotkey_fini(hotkey_drv_t *htkp)
8508693SKerry.Shu@Sun.COM {
8518693SKerry.Shu@Sun.COM modctl_t *modp;
8528693SKerry.Shu@Sun.COM
8538693SKerry.Shu@Sun.COM if (htkp->vendor_fini != NULL)
8548693SKerry.Shu@Sun.COM htkp->vendor_fini(htkp);
8558693SKerry.Shu@Sun.COM if (htkp->acpi_video != NULL)
8568693SKerry.Shu@Sun.COM (void) acpi_video_fini(htkp->acpi_video);
8578693SKerry.Shu@Sun.COM if (htkp->modid != -1) {
8588693SKerry.Shu@Sun.COM modp = mod_hold_by_id(htkp->modid);
8598693SKerry.Shu@Sun.COM mutex_enter(&mod_lock);
8608693SKerry.Shu@Sun.COM modp->mod_ref = 0;
8618693SKerry.Shu@Sun.COM modp->mod_loadflags &= ~MOD_NOAUTOUNLOAD;
8628693SKerry.Shu@Sun.COM mutex_exit(&mod_lock);
8638693SKerry.Shu@Sun.COM mod_release_mod(modp);
8648693SKerry.Shu@Sun.COM (void) modunload(htkp->modid);
8658693SKerry.Shu@Sun.COM }
8668693SKerry.Shu@Sun.COM
8678693SKerry.Shu@Sun.COM return (ACPI_DRV_OK);
8688693SKerry.Shu@Sun.COM }
869