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
acpi_video_set_dos(struct acpi_video * vidp,uint32_t policy)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
acpi_video_brightness_get(struct acpi_video_brightness * vidbp)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
acpi_video_brightness_set(struct acpi_video_brightness * vidbp,uint32_t level)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
hotkey_drv_gen_sysevent(dev_info_t * dip,char * event)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
acpi_video_switch_notify(ACPI_HANDLE hdl,uint32_t notify,void * ctx)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
acpi_video_brightness_notify(ACPI_HANDLE hdl,uint32_t notify,void * ctx)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
acpi_video_notify_intall(struct acpi_video * vidp)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
acpi_video_notify_unintall(struct acpi_video * vidp)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
acpi_video_free(struct acpi_video * vidp)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
acpi_video_fini(struct acpi_video * vidp)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
acpi_video_enum_output(ACPI_HANDLE hdl,struct acpi_video * vidp)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
acpi_video_find_and_alloc(ACPI_HANDLE hdl,UINT32 nest,void * ctx,void ** rv)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
hotkey_brightness_inc(hotkey_drv_t * htkp)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
hotkey_brightness_dec(hotkey_drv_t * htkp)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
acpi_video_ioctl(void * p,int cmd,intptr_t arg,int mode,cred_t * cr,int * rval)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
acpi_drv_hotkey_ioctl(int cmd,intptr_t arg,int mode,cred_t * cr,int * rval)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
acpi_video_check_blacklist(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
hotkey_acpi_video_check(hotkey_drv_t * htkp)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
hotkey_init(hotkey_drv_t * htkp)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
hotkey_fini(hotkey_drv_t * htkp)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