1 /* $NetBSD: aed.c,v 1.43 2025/04/03 01:54:46 nat Exp $ */
2
3 /*
4 * Copyright (c) 2024 Nathanial Sloss <nathanialsloss@yahoo.com.au>
5 * All rights reserved.
6 *
7 * Copyright (C) 1994 Bradley A. Grantham
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: aed.c,v 1.43 2025/04/03 01:54:46 nat Exp $");
33
34 #include "opt_adb.h"
35
36 #include <sys/param.h>
37 #include <sys/device.h>
38 #include <sys/fcntl.h>
39 #include <sys/poll.h>
40 #include <sys/select.h>
41 #include <sys/proc.h>
42 #include <sys/signalvar.h>
43 #include <sys/systm.h>
44 #include <sys/conf.h>
45
46 #include <machine/autoconf.h>
47 #include <machine/cpu.h>
48 #include <machine/keyboard.h>
49
50 #include <mac68k/mac68k/macrom.h>
51 #include <mac68k/dev/adbvar.h>
52 #include <mac68k/dev/aedvar.h>
53 #include <mac68k/dev/akbdvar.h>
54 #include <mac68k/dev/pm_direct.h>
55
56 #define BRIGHTNESS_MAX 31
57 #define BRIGHTNESS_MIN 0
58 #define BRIGHTNESS_STEP 4
59
60 /*
61 * Function declarations.
62 */
63 static int aedmatch(device_t, cfdata_t, void *);
64 static void aedattach(device_t, device_t, void *);
65 static int aed_emulate_mouse(adb_event_t *);
66 static void aed_kbdrpt(void *);
67 static void aed_dokeyupdown(adb_event_t *);
68 static void aed_handoff(adb_event_t *);
69 static void aed_enqevent(adb_event_t *);
70
71 static void aed_display_on(device_t);
72 static void aed_display_off(device_t);
73 static void aed_brightness_down(device_t);
74 static void aed_brightness_up(device_t);
75
76 /*
77 * Local variables.
78 */
79 static struct aed_softc *aed_sc;
80 static int aed_options = 0 | AED_MSEMUL;
81 static int brightness = BRIGHTNESS_MAX;
82
83 /* Driver definition */
84 CFATTACH_DECL_NEW(aed, sizeof(struct aed_softc),
85 aedmatch, aedattach, NULL, NULL);
86
87 extern struct cfdriver aed_cd;
88
89 dev_type_open(aedopen);
90 dev_type_close(aedclose);
91 dev_type_read(aedread);
92 dev_type_ioctl(aedioctl);
93 dev_type_poll(aedpoll);
94 dev_type_kqfilter(aedkqfilter);
95
96 const struct cdevsw aed_cdevsw = {
97 .d_open = aedopen,
98 .d_close = aedclose,
99 .d_read = aedread,
100 .d_write = nullwrite,
101 .d_ioctl = aedioctl,
102 .d_stop = nostop,
103 .d_tty = notty,
104 .d_poll = aedpoll,
105 .d_mmap = nommap,
106 .d_kqfilter = aedkqfilter,
107 .d_discard = nodiscard,
108 .d_flag = 0
109 };
110
111 static int
aedmatch(device_t parent,cfdata_t cf,void * aux)112 aedmatch(device_t parent, cfdata_t cf, void *aux)
113 {
114 struct adb_attach_args *aa_args = (struct adb_attach_args *)aux;
115 static int aed_matched;
116
117 /* Allow only one instance. */
118 if ((aa_args->origaddr == 0) && (!aed_matched)) {
119 aed_matched = 1;
120 return (1);
121 } else
122 return (0);
123 }
124
125 static void
aedattach(device_t parent,device_t self,void * aux)126 aedattach(device_t parent, device_t self, void *aux)
127 {
128 struct adb_attach_args *aa_args = (struct adb_attach_args *)aux;
129 struct aed_softc *sc = device_private(self);
130
131 callout_init(&sc->sc_repeat_ch, 0);
132 selinit(&sc->sc_selinfo);
133
134 sc->origaddr = aa_args->origaddr;
135 sc->adbaddr = aa_args->adbaddr;
136 sc->handler_id = aa_args->handler_id;
137
138 sc->sc_evq_tail = 0;
139 sc->sc_evq_len = 0;
140
141 sc->sc_rptdelay = 20;
142 sc->sc_rptinterval = 6;
143 sc->sc_repeating = -1; /* not repeating */
144
145 /* Pull in the options flags. */
146 sc->sc_options = (device_cfdata(self)->cf_flags | aed_options);
147
148 sc->sc_ioproc = NULL;
149
150 sc->sc_buttons = 0;
151
152 sc->sc_open = 0;
153
154 aed_sc = sc;
155
156 pmf_event_register(self, PMFE_DISPLAY_ON,
157 aed_display_on, TRUE);
158 pmf_event_register(self, PMFE_DISPLAY_OFF,
159 aed_display_off, TRUE);
160 pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP,
161 aed_brightness_up, TRUE);
162 pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
163 aed_brightness_down, TRUE);
164
165 printf("ADB Event device\n");
166
167 #if 1
168 (void)config_found(self, &aa_args, NULL, CFARGS_NONE);
169 #endif
170
171 return;
172 }
173
174 /*
175 * Given a keyboard ADB event, record the keycode and call the key
176 * repeat handler, optionally passing the event through the mouse
177 * button emulation handler first. Pass mouse events directly to
178 * the handoff function.
179 */
180 int
aed_input(adb_event_t * event)181 aed_input(adb_event_t *event)
182 {
183 adb_event_t new_event = *event;
184 int rv = aed_sc->sc_open;
185
186 switch (event->def_addr) {
187 case ADBADDR_KBD:
188 if (aed_sc->sc_options & AED_MSEMUL) {
189 rv = aed_emulate_mouse(&new_event);
190 } else
191 aed_dokeyupdown(&new_event);
192 break;
193 case ADBADDR_MS:
194 event->u.m.buttons |= aed_sc->sc_buttons;
195 new_event.u.m.buttons |= aed_sc->sc_buttons;
196 aed_handoff(&new_event);
197 break;
198 default: /* God only knows. */
199 #ifdef DIAGNOSTIC
200 panic("aed: received event from unsupported device!");
201 #endif
202 rv = 0;
203 break;
204 }
205
206 return (rv);
207 }
208
209 /*
210 * Handles mouse button emulation via the keyboard. If the emulation
211 * modifier key is down, left and right arrows will generate 2nd and
212 * 3rd mouse button events while the 1, 2, and 3 keys will generate
213 * the corresponding mouse button event.
214 */
215 static int
aed_emulate_mouse(adb_event_t * event)216 aed_emulate_mouse(adb_event_t *event)
217 {
218 static int emulmodkey_down;
219 adb_event_t new_event;
220 int result = 0;
221
222 if (event->u.k.key == ADBK_KEYDOWN(ADBK_OPTION)) {
223 emulmodkey_down = 1;
224 } else if (event->u.k.key == ADBK_KEYUP(ADBK_OPTION)) {
225 /* key up */
226 emulmodkey_down = 0;
227 if (aed_sc->sc_buttons & 0xfe) {
228 aed_sc->sc_buttons &= 1;
229 new_event.def_addr = ADBADDR_MS;
230 new_event.u.m.buttons = aed_sc->sc_buttons;
231 new_event.u.m.dx = new_event.u.m.dy = 0;
232 microtime(&new_event.timestamp);
233 aed_handoff(&new_event);
234 }
235 } else if (emulmodkey_down) {
236 switch(event->u.k.key) {
237 #ifdef ALTXBUTTONS
238 case ADBK_KEYDOWN(ADBK_1):
239 result = 1;
240 aed_sc->sc_buttons |= 1; /* left down */
241 new_event.def_addr = ADBADDR_MS;
242 new_event.u.m.buttons = aed_sc->sc_buttons;
243 new_event.u.m.dx = new_event.u.m.dy = 0;
244 microtime(&new_event.timestamp);
245 aed_handoff(&new_event);
246 break;
247 case ADBK_KEYUP(ADBK_1):
248 result = 1;
249 aed_sc->sc_buttons &= ~1; /* left up */
250 new_event.def_addr = ADBADDR_MS;
251 new_event.u.m.buttons = aed_sc->sc_buttons;
252 new_event.u.m.dx = new_event.u.m.dy = 0;
253 microtime(&new_event.timestamp);
254 aed_handoff(&new_event);
255 break;
256 #endif
257 case ADBK_KEYDOWN(ADBK_LEFT):
258 #ifdef ALTXBUTTONS
259 case ADBK_KEYDOWN(ADBK_2):
260 #endif
261 result = 1;
262 aed_sc->sc_buttons |= 2; /* middle down */
263 new_event.def_addr = ADBADDR_MS;
264 new_event.u.m.buttons = aed_sc->sc_buttons;
265 new_event.u.m.dx = new_event.u.m.dy = 0;
266 microtime(&new_event.timestamp);
267 aed_handoff(&new_event);
268 break;
269 case ADBK_KEYUP(ADBK_LEFT):
270 #ifdef ALTXBUTTONS
271 case ADBK_KEYUP(ADBK_2):
272 #endif
273 result = 1;
274 aed_sc->sc_buttons &= ~2; /* middle up */
275 new_event.def_addr = ADBADDR_MS;
276 new_event.u.m.buttons = aed_sc->sc_buttons;
277 new_event.u.m.dx = new_event.u.m.dy = 0;
278 microtime(&new_event.timestamp);
279 aed_handoff(&new_event);
280 break;
281 case ADBK_KEYDOWN(ADBK_RIGHT):
282 #ifdef ALTXBUTTONS
283 case ADBK_KEYDOWN(ADBK_3):
284 #endif
285 result = 1;
286 aed_sc->sc_buttons |= 4; /* right down */
287 new_event.def_addr = ADBADDR_MS;
288 new_event.u.m.buttons = aed_sc->sc_buttons;
289 new_event.u.m.dx = new_event.u.m.dy = 0;
290 microtime(&new_event.timestamp);
291 aed_handoff(&new_event);
292 break;
293 case ADBK_KEYUP(ADBK_RIGHT):
294 #ifdef ALTXBUTTONS
295 case ADBK_KEYUP(ADBK_3):
296 #endif
297 result = 1;
298 aed_sc->sc_buttons &= ~4; /* right up */
299 new_event.def_addr = ADBADDR_MS;
300 new_event.u.m.buttons = aed_sc->sc_buttons;
301 new_event.u.m.dx = new_event.u.m.dy = 0;
302 microtime(&new_event.timestamp);
303 aed_handoff(&new_event);
304 break;
305 case ADBK_KEYDOWN(ADBK_UP):
306 result = 1;
307 break;
308 case ADBK_KEYUP(ADBK_UP):
309 result = 1;
310 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP);
311 break;
312 case ADBK_KEYDOWN(ADBK_DOWN):
313 result = 1;
314 break;
315 case ADBK_KEYUP(ADBK_DOWN):
316 result = 1;
317 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN);
318 break;
319 case ADBK_KEYUP(ADBK_SHIFT):
320 case ADBK_KEYDOWN(ADBK_SHIFT):
321 case ADBK_KEYUP(ADBK_CONTROL):
322 case ADBK_KEYDOWN(ADBK_CONTROL):
323 case ADBK_KEYUP(ADBK_FLOWER):
324 case ADBK_KEYDOWN(ADBK_FLOWER):
325 /* ctrl, shift, cmd */
326 aed_dokeyupdown(event);
327 break;
328 default:
329 if (event->u.k.key & 0x80)
330 /* ignore keyup */
331 break;
332
333 /* key down */
334 new_event = *event;
335
336 /* send option-down */
337 new_event.u.k.key = ADBK_KEYDOWN(ADBK_OPTION);
338 new_event.bytes[0] = new_event.u.k.key;
339 microtime(&new_event.timestamp);
340 aed_dokeyupdown(&new_event);
341
342 /* send key-down */
343 new_event.u.k.key = event->bytes[0];
344 new_event.bytes[0] = new_event.u.k.key;
345 microtime(&new_event.timestamp);
346 aed_dokeyupdown(&new_event);
347
348 /* send key-up */
349 new_event.u.k.key =
350 ADBK_KEYUP(ADBK_KEYVAL(event->bytes[0]));
351 microtime(&new_event.timestamp);
352 new_event.bytes[0] = new_event.u.k.key;
353 aed_dokeyupdown(&new_event);
354
355 /* send option-up */
356 new_event.u.k.key = ADBK_KEYUP(ADBK_OPTION);
357 new_event.bytes[0] = new_event.u.k.key;
358 microtime(&new_event.timestamp);
359 aed_dokeyupdown(&new_event);
360 break;
361 }
362 } else {
363 aed_dokeyupdown(event);
364 }
365
366 return result;
367 }
368
369 /*
370 * Keyboard autorepeat timeout function. Sends key up/down events
371 * for the repeating key and schedules the next call at sc_rptinterval
372 * ticks in the future.
373 */
374 static void
aed_kbdrpt(void * kstate)375 aed_kbdrpt(void *kstate)
376 {
377 struct aed_softc *sc = (struct aed_softc *)kstate;
378
379 sc->sc_rptevent.bytes[0] |= 0x80;
380 microtime(&sc->sc_rptevent.timestamp);
381 aed_handoff(&sc->sc_rptevent); /* do key up */
382
383 sc->sc_rptevent.bytes[0] &= 0x7f;
384 microtime(&sc->sc_rptevent.timestamp);
385 aed_handoff(&sc->sc_rptevent); /* do key down */
386
387 if (sc->sc_repeating == sc->sc_rptevent.u.k.key) {
388 callout_reset(&sc->sc_repeat_ch, sc->sc_rptinterval,
389 aed_kbdrpt, kstate);
390 }
391 }
392
393
394 /*
395 * Cancels the currently repeating key event if there is one, schedules
396 * a new repeating key event if needed, and hands the event off to the
397 * appropriate subsystem.
398 */
399 static void
aed_dokeyupdown(adb_event_t * event)400 aed_dokeyupdown(adb_event_t *event)
401 {
402 int kbd_key;
403
404 kbd_key = ADBK_KEYVAL(event->u.k.key);
405 if (ADBK_PRESS(event->u.k.key) && keyboard[kbd_key][0] != 0) {
406 /* ignore shift & control */
407 if (aed_sc->sc_repeating != -1) {
408 callout_stop(&aed_sc->sc_repeat_ch);
409 }
410 aed_sc->sc_rptevent = *event;
411 aed_sc->sc_repeating = kbd_key;
412 callout_reset(&aed_sc->sc_repeat_ch, aed_sc->sc_rptdelay,
413 aed_kbdrpt, (void *)aed_sc);
414 } else {
415 if (aed_sc->sc_repeating != -1) {
416 aed_sc->sc_repeating = -1;
417 callout_stop(&aed_sc->sc_repeat_ch);
418 }
419 aed_sc->sc_rptevent = *event;
420 }
421 aed_handoff(event);
422 }
423
424 /*
425 * Place the event in the event queue if a requesting device is open
426 * and we are not polling, otherwise, pass it up to the console driver.
427 */
428 static void
aed_handoff(adb_event_t * event)429 aed_handoff(adb_event_t *event)
430 {
431 if (aed_sc->sc_open && !adb_polling)
432 aed_enqevent(event);
433 }
434
435 /*
436 * Place the event in the event queue and wakeup any waiting processes.
437 */
438 static void
aed_enqevent(adb_event_t * event)439 aed_enqevent(adb_event_t *event)
440 {
441 int s;
442
443 s = splvm();
444
445 #ifdef DIAGNOSTIC
446 if (aed_sc->sc_evq_tail < 0 || aed_sc->sc_evq_tail >= AED_MAX_EVENTS)
447 panic("adb: event queue tail is out of bounds");
448
449 if (aed_sc->sc_evq_len < 0 || aed_sc->sc_evq_len > AED_MAX_EVENTS)
450 panic("adb: event queue len is out of bounds");
451 #endif
452
453 if (aed_sc->sc_evq_len == AED_MAX_EVENTS) {
454 splx(s);
455 return; /* Oh, well... */
456 }
457 aed_sc->sc_evq[(aed_sc->sc_evq_len + aed_sc->sc_evq_tail) %
458 AED_MAX_EVENTS] = *event;
459 aed_sc->sc_evq_len++;
460
461 selnotify(&aed_sc->sc_selinfo, 0, 0);
462 if (aed_sc->sc_ioproc)
463 psignal(aed_sc->sc_ioproc, SIGIO);
464
465 splx(s);
466 }
467
468 int
aedopen(dev_t dev,int flag,int mode,struct lwp * l)469 aedopen(dev_t dev, int flag, int mode, struct lwp *l)
470 {
471 struct aed_softc *sc;
472 int s;
473
474 sc = device_lookup_private(&aed_cd, minor(dev));
475 if (sc == NULL)
476 return (ENXIO);
477
478 s = splvm();
479 if (sc->sc_open) {
480 splx(s);
481 return (EBUSY);
482 }
483 aed_sc->sc_evq_tail = 0;
484 aed_sc->sc_evq_len = 0;
485 aed_sc->sc_open = 1;
486 aed_sc->sc_ioproc = l->l_proc;
487 splx(s);
488
489 return 0;
490 }
491
492
493 int
aedclose(dev_t dev,int flag,int mode,struct lwp * l)494 aedclose(dev_t dev, int flag, int mode, struct lwp *l)
495 {
496 int s;
497
498 s = splvm();
499 aed_sc->sc_open = 0;
500 aed_sc->sc_ioproc = NULL;
501 splx(s);
502
503 return (0);
504 }
505
506
507 int
aedread(dev_t dev,struct uio * uio,int flag)508 aedread(dev_t dev, struct uio *uio, int flag)
509 {
510 int s, error;
511 int willfit;
512 int total;
513 int firstmove;
514 int moremove;
515
516 if (uio->uio_resid < sizeof(adb_event_t))
517 return (EMSGSIZE); /* close enough. */
518
519 s = splvm();
520 if (aed_sc->sc_evq_len == 0) {
521 splx(s);
522 return (0);
523 }
524 willfit = howmany(uio->uio_resid, sizeof(adb_event_t));
525 total = (aed_sc->sc_evq_len < willfit) ? aed_sc->sc_evq_len : willfit;
526
527 firstmove = (aed_sc->sc_evq_tail + total > AED_MAX_EVENTS)
528 ? (AED_MAX_EVENTS - aed_sc->sc_evq_tail) : total;
529
530 error = uiomove((void *) & aed_sc->sc_evq[aed_sc->sc_evq_tail],
531 firstmove * sizeof(adb_event_t), uio);
532 if (error) {
533 splx(s);
534 return (error);
535 }
536 moremove = total - firstmove;
537
538 if (moremove > 0) {
539 error = uiomove((void *) & aed_sc->sc_evq[0],
540 moremove * sizeof(adb_event_t), uio);
541 if (error) {
542 splx(s);
543 return (error);
544 }
545 }
546 aed_sc->sc_evq_tail = (aed_sc->sc_evq_tail + total) % AED_MAX_EVENTS;
547 aed_sc->sc_evq_len -= total;
548 splx(s);
549 return (0);
550 }
551
552 int
aedioctl(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)553 aedioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
554 {
555 switch (cmd) {
556 case ADBIOC_DEVSINFO: {
557 adb_devinfo_t *di;
558 ADBDataBlock adbdata;
559 int totaldevs;
560 int adbaddr;
561 int i;
562
563 di = (void *)data;
564
565 /* Initialize to no devices */
566 for (i = 0; i < 16; i++)
567 di->dev[i].addr = -1;
568
569 totaldevs = CountADBs();
570 for (i = 1; i <= totaldevs; i++) {
571 adbaddr = GetIndADB(&adbdata, i);
572 di->dev[adbaddr].addr = adbaddr;
573 di->dev[adbaddr].default_addr = (int)(adbdata.origADBAddr);
574 di->dev[adbaddr].handler_id = (int)(adbdata.devType);
575 }
576
577 /* Must call ADB Manager to get devices now */
578 break;
579 }
580
581 case ADBIOC_GETREPEAT:{
582 adb_rptinfo_t *ri;
583
584 ri = (void *)data;
585 ri->delay_ticks = aed_sc->sc_rptdelay;
586 ri->interval_ticks = aed_sc->sc_rptinterval;
587 break;
588 }
589
590 case ADBIOC_SETREPEAT:{
591 adb_rptinfo_t *ri;
592
593 ri = (void *) data;
594 aed_sc->sc_rptdelay = ri->delay_ticks;
595 aed_sc->sc_rptinterval = ri->interval_ticks;
596 break;
597 }
598
599 case ADBIOC_RESET:
600 /* Do nothing for now */
601 break;
602
603 case ADBIOC_LISTENCMD:
604 /* adb_listencmd_t *lc = data; */
605
606 default:
607 return (EINVAL);
608 }
609 return (0);
610 }
611
612
613 int
aedpoll(dev_t dev,int events,struct lwp * l)614 aedpoll(dev_t dev, int events, struct lwp *l)
615 {
616 int s, revents;
617
618 revents = events & (POLLOUT | POLLWRNORM);
619
620 if ((events & (POLLIN | POLLRDNORM)) == 0)
621 return (revents);
622
623 s = splvm();
624 if (aed_sc->sc_evq_len > 0)
625 revents |= events & (POLLIN | POLLRDNORM);
626 else
627 selrecord(l, &aed_sc->sc_selinfo);
628 splx(s);
629
630 return (revents);
631 }
632
633 static void
filt_aedrdetach(struct knote * kn)634 filt_aedrdetach(struct knote *kn)
635 {
636 int s;
637
638 s = splvm();
639 selremove_knote(&aed_sc->sc_selinfo, kn);
640 splx(s);
641 }
642
643 static int
filt_aedread(struct knote * kn,long hint)644 filt_aedread(struct knote *kn, long hint)
645 {
646
647 kn->kn_data = aed_sc->sc_evq_len * sizeof(adb_event_t);
648 return (kn->kn_data > 0);
649 }
650
651 static const struct filterops aedread_filtops = {
652 .f_flags = FILTEROP_ISFD,
653 .f_attach = NULL,
654 .f_detach = filt_aedrdetach,
655 .f_event = filt_aedread,
656 };
657
658 int
aedkqfilter(dev_t dev,struct knote * kn)659 aedkqfilter(dev_t dev, struct knote *kn)
660 {
661 int s;
662
663 switch (kn->kn_filter) {
664 case EVFILT_READ:
665 kn->kn_fop = &aedread_filtops;
666 s = splvm();
667 selrecord_knote(&aed_sc->sc_selinfo, kn);
668 splx(s);
669 break;
670
671 case EVFILT_WRITE:
672 kn->kn_fop = &seltrue_filtops;
673 break;
674
675 default:
676 return (EINVAL);
677 }
678
679 return (0);
680 }
681
682 static void
aed_brightness_down(device_t dev)683 aed_brightness_down(device_t dev)
684 {
685 int level, step;
686
687 level = brightness;
688 if (level <= 4) /* logarithmic brightness curve. */
689 step = 1;
690 else
691 step = BRIGHTNESS_STEP;
692
693 level = uimax(BRIGHTNESS_MIN, level - step);
694 brightness = pm_set_brightness(level);
695 }
696
697 static void
aed_brightness_up(device_t dev)698 aed_brightness_up(device_t dev)
699 {
700 int level, step;
701
702 level = brightness;
703 if (level <= 4) /* logarithmic brightness curve. */
704 step = 1;
705 else
706 step = BRIGHTNESS_STEP;
707
708 level = uimin(BRIGHTNESS_MAX, level + step);
709 brightness = pm_set_brightness(level);
710 }
711
712 static void
aed_display_on(device_t dev)713 aed_display_on(device_t dev)
714 {
715 (void)pm_set_brightness(brightness);
716 }
717
718 static void
aed_display_off(device_t dev)719 aed_display_off(device_t dev)
720 {
721 (void)pm_set_brightness(0);
722 }
723