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