xref: /netbsd-src/sys/arch/mac68k/dev/aed.c (revision 3117ece4fc4a4ca4489ba793710b60b0d26bab6c)
1 /*	$NetBSD: aed.c,v 1.40 2024/09/14 20:59:45 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.40 2024/09/14 20:59:45 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 void	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_brightness_down(device_t);
72 static void	aed_brightness_up(device_t);
73 
74 /*
75  * Local variables.
76  */
77 static struct aed_softc *aed_sc;
78 static int aed_options = 0 | AED_MSEMUL;
79 static int brightness = BRIGHTNESS_MAX;
80 
81 /* Driver definition */
82 CFATTACH_DECL_NEW(aed, sizeof(struct aed_softc),
83     aedmatch, aedattach, NULL, NULL);
84 
85 extern struct cfdriver aed_cd;
86 
87 dev_type_open(aedopen);
88 dev_type_close(aedclose);
89 dev_type_read(aedread);
90 dev_type_ioctl(aedioctl);
91 dev_type_poll(aedpoll);
92 dev_type_kqfilter(aedkqfilter);
93 
94 const struct cdevsw aed_cdevsw = {
95 	.d_open = aedopen,
96 	.d_close = aedclose,
97 	.d_read = aedread,
98 	.d_write = nullwrite,
99 	.d_ioctl = aedioctl,
100 	.d_stop = nostop,
101 	.d_tty = notty,
102 	.d_poll = aedpoll,
103 	.d_mmap = nommap,
104 	.d_kqfilter = aedkqfilter,
105 	.d_discard = nodiscard,
106 	.d_flag = 0
107 };
108 
109 static int
110 aedmatch(device_t parent, cfdata_t cf, void *aux)
111 {
112 	struct adb_attach_args *aa_args = (struct adb_attach_args *)aux;
113 	static int aed_matched;
114 
115 	/* Allow only one instance. */
116         if ((aa_args->origaddr == 0) && (!aed_matched)) {
117 		aed_matched = 1;
118                 return (1);
119         } else
120                 return (0);
121 }
122 
123 static void
124 aedattach(device_t parent, device_t self, void *aux)
125 {
126 	struct adb_attach_args *aa_args = (struct adb_attach_args *)aux;
127 	struct aed_softc *sc = device_private(self);
128 
129 	callout_init(&sc->sc_repeat_ch, 0);
130 	selinit(&sc->sc_selinfo);
131 
132 	sc->origaddr = aa_args->origaddr;
133 	sc->adbaddr = aa_args->adbaddr;
134 	sc->handler_id = aa_args->handler_id;
135 
136 	sc->sc_evq_tail = 0;
137 	sc->sc_evq_len = 0;
138 
139 	sc->sc_rptdelay = 20;
140 	sc->sc_rptinterval = 6;
141 	sc->sc_repeating = -1;          /* not repeating */
142 
143 	/* Pull in the options flags. */
144 	sc->sc_options = (device_cfdata(self)->cf_flags | aed_options);
145 
146 	sc->sc_ioproc = NULL;
147 
148 	sc->sc_buttons = 0;
149 
150 	sc->sc_open = 0;
151 
152 	aed_sc = sc;
153 
154 	pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP,
155 	    aed_brightness_up, TRUE);
156 	pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
157 	    aed_brightness_down, TRUE);
158 
159 	printf("ADB Event device\n");
160 
161 	return;
162 }
163 
164 /*
165  * Given a keyboard ADB event, record the keycode and call the key
166  * repeat handler, optionally passing the event through the mouse
167  * button emulation handler first.  Pass mouse events directly to
168  * the handoff function.
169  */
170 int
171 aed_input(adb_event_t *event)
172 {
173         adb_event_t new_event = *event;
174 	int rv = aed_sc->sc_open;
175 
176 	switch (event->def_addr) {
177 	case ADBADDR_KBD:
178 		if (aed_sc->sc_options & AED_MSEMUL)
179 			aed_emulate_mouse(&new_event);
180 		else
181 			aed_dokeyupdown(&new_event);
182 		break;
183 	case ADBADDR_MS:
184 		event->u.m.buttons |= aed_sc->sc_buttons;
185 		new_event.u.m.buttons |= aed_sc->sc_buttons;
186 		aed_handoff(&new_event);
187 		break;
188 	default:                /* God only knows. */
189 #ifdef DIAGNOSTIC
190 		panic("aed: received event from unsupported device!");
191 #endif
192 		rv = 0;
193 		break;
194 	}
195 
196 	return (rv);
197 }
198 
199 /*
200  * Handles mouse button emulation via the keyboard.  If the emulation
201  * modifier key is down, left and right arrows will generate 2nd and
202  * 3rd mouse button events while the 1, 2, and 3 keys will generate
203  * the corresponding mouse button event.
204  */
205 static void
206 aed_emulate_mouse(adb_event_t *event)
207 {
208 	static int emulmodkey_down;
209 	adb_event_t new_event;
210 
211 	if (event->u.k.key == ADBK_KEYDOWN(ADBK_OPTION)) {
212 		emulmodkey_down = 1;
213 	} else if (event->u.k.key == ADBK_KEYUP(ADBK_OPTION)) {
214 		/* key up */
215 		emulmodkey_down = 0;
216 		if (aed_sc->sc_buttons & 0xfe) {
217 			aed_sc->sc_buttons &= 1;
218 			new_event.def_addr = ADBADDR_MS;
219 			new_event.u.m.buttons = aed_sc->sc_buttons;
220 			new_event.u.m.dx = new_event.u.m.dy = 0;
221 			microtime(&new_event.timestamp);
222 			aed_handoff(&new_event);
223 		}
224 	} else if (emulmodkey_down) {
225 		switch(event->u.k.key) {
226 #ifdef ALTXBUTTONS
227 		case ADBK_KEYDOWN(ADBK_1):
228 			aed_sc->sc_buttons |= 1;	/* left down */
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 			break;
235 		case ADBK_KEYUP(ADBK_1):
236 			aed_sc->sc_buttons &= ~1;	/* left up */
237 			new_event.def_addr = ADBADDR_MS;
238 			new_event.u.m.buttons = aed_sc->sc_buttons;
239 			new_event.u.m.dx = new_event.u.m.dy = 0;
240 			microtime(&new_event.timestamp);
241 			aed_handoff(&new_event);
242 			break;
243 #endif
244 		case ADBK_KEYDOWN(ADBK_LEFT):
245 #ifdef ALTXBUTTONS
246 		case ADBK_KEYDOWN(ADBK_2):
247 #endif
248 			aed_sc->sc_buttons |= 2;	/* middle down */
249 			new_event.def_addr = ADBADDR_MS;
250 			new_event.u.m.buttons = aed_sc->sc_buttons;
251 			new_event.u.m.dx = new_event.u.m.dy = 0;
252 			microtime(&new_event.timestamp);
253 			aed_handoff(&new_event);
254 			break;
255 		case ADBK_KEYUP(ADBK_LEFT):
256 #ifdef ALTXBUTTONS
257 		case ADBK_KEYUP(ADBK_2):
258 #endif
259 			aed_sc->sc_buttons &= ~2;	/* middle up */
260 			new_event.def_addr = ADBADDR_MS;
261 			new_event.u.m.buttons = aed_sc->sc_buttons;
262 			new_event.u.m.dx = new_event.u.m.dy = 0;
263 			microtime(&new_event.timestamp);
264 			aed_handoff(&new_event);
265 			break;
266 		case ADBK_KEYDOWN(ADBK_RIGHT):
267 #ifdef ALTXBUTTONS
268 		case ADBK_KEYDOWN(ADBK_3):
269 #endif
270 			aed_sc->sc_buttons |= 4;	/* right down */
271 			new_event.def_addr = ADBADDR_MS;
272 			new_event.u.m.buttons = aed_sc->sc_buttons;
273 			new_event.u.m.dx = new_event.u.m.dy = 0;
274 			microtime(&new_event.timestamp);
275 			aed_handoff(&new_event);
276 			break;
277 		case ADBK_KEYUP(ADBK_RIGHT):
278 #ifdef ALTXBUTTONS
279 		case ADBK_KEYUP(ADBK_3):
280 #endif
281 			aed_sc->sc_buttons &= ~4;	/* right up */
282 			new_event.def_addr = ADBADDR_MS;
283 			new_event.u.m.buttons = aed_sc->sc_buttons;
284 			new_event.u.m.dx = new_event.u.m.dy = 0;
285 			microtime(&new_event.timestamp);
286 			aed_handoff(&new_event);
287 			break;
288 		case ADBK_KEYUP(ADBK_UP):
289 			pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP);
290 			break;
291 		case ADBK_KEYUP(ADBK_DOWN):
292 			pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN);
293 			break;
294 		case ADBK_KEYUP(ADBK_SHIFT):
295 		case ADBK_KEYDOWN(ADBK_SHIFT):
296 		case ADBK_KEYUP(ADBK_CONTROL):
297 		case ADBK_KEYDOWN(ADBK_CONTROL):
298 		case ADBK_KEYUP(ADBK_FLOWER):
299 		case ADBK_KEYDOWN(ADBK_FLOWER):
300 			/* ctrl, shift, cmd */
301 			aed_dokeyupdown(event);
302 			break;
303 		default:
304 			if (event->u.k.key & 0x80)
305 				/* ignore keyup */
306 				break;
307 
308 			/* key down */
309 			new_event = *event;
310 
311 			/* send option-down */
312 			new_event.u.k.key = ADBK_KEYDOWN(ADBK_OPTION);
313 			new_event.bytes[0] = new_event.u.k.key;
314 			microtime(&new_event.timestamp);
315 			aed_dokeyupdown(&new_event);
316 
317 			/* send key-down */
318 			new_event.u.k.key = event->bytes[0];
319 			new_event.bytes[0] = new_event.u.k.key;
320 			microtime(&new_event.timestamp);
321 			aed_dokeyupdown(&new_event);
322 
323 			/* send key-up */
324 			new_event.u.k.key =
325 				ADBK_KEYUP(ADBK_KEYVAL(event->bytes[0]));
326 			microtime(&new_event.timestamp);
327 			new_event.bytes[0] = new_event.u.k.key;
328 			aed_dokeyupdown(&new_event);
329 
330 			/* send option-up */
331 			new_event.u.k.key = ADBK_KEYUP(ADBK_OPTION);
332 			new_event.bytes[0] = new_event.u.k.key;
333 			microtime(&new_event.timestamp);
334 			aed_dokeyupdown(&new_event);
335 			break;
336 		}
337 	} else {
338 		aed_dokeyupdown(event);
339 	}
340 }
341 
342 /*
343  * Keyboard autorepeat timeout function.  Sends key up/down events
344  * for the repeating key and schedules the next call at sc_rptinterval
345  * ticks in the future.
346  */
347 static void
348 aed_kbdrpt(void *kstate)
349 {
350 	struct aed_softc *sc = (struct aed_softc *)kstate;
351 
352 	sc->sc_rptevent.bytes[0] |= 0x80;
353 	microtime(&sc->sc_rptevent.timestamp);
354 	aed_handoff(&sc->sc_rptevent);	/* do key up */
355 
356 	sc->sc_rptevent.bytes[0] &= 0x7f;
357 	microtime(&sc->sc_rptevent.timestamp);
358 	aed_handoff(&sc->sc_rptevent);	/* do key down */
359 
360 	if (sc->sc_repeating == sc->sc_rptevent.u.k.key) {
361 		callout_reset(&sc->sc_repeat_ch, sc->sc_rptinterval,
362 		    aed_kbdrpt, kstate);
363 	}
364 }
365 
366 
367 /*
368  * Cancels the currently repeating key event if there is one, schedules
369  * a new repeating key event if needed, and hands the event off to the
370  * appropriate subsystem.
371  */
372 static void
373 aed_dokeyupdown(adb_event_t *event)
374 {
375 	int kbd_key;
376 
377 	kbd_key = ADBK_KEYVAL(event->u.k.key);
378 	if (ADBK_PRESS(event->u.k.key) && keyboard[kbd_key][0] != 0) {
379 		/* ignore shift & control */
380 		if (aed_sc->sc_repeating != -1) {
381 			callout_stop(&aed_sc->sc_repeat_ch);
382 		}
383 		aed_sc->sc_rptevent = *event;
384 		aed_sc->sc_repeating = kbd_key;
385 		callout_reset(&aed_sc->sc_repeat_ch, aed_sc->sc_rptdelay,
386 		    aed_kbdrpt, (void *)aed_sc);
387 	} else {
388 		if (aed_sc->sc_repeating != -1) {
389 			aed_sc->sc_repeating = -1;
390 			callout_stop(&aed_sc->sc_repeat_ch);
391 		}
392 		aed_sc->sc_rptevent = *event;
393 	}
394 	aed_handoff(event);
395 }
396 
397 /*
398  * Place the event in the event queue if a requesting device is open
399  * and we are not polling, otherwise, pass it up to the console driver.
400  */
401 static void
402 aed_handoff(adb_event_t *event)
403 {
404 	if (aed_sc->sc_open && !adb_polling)
405 		aed_enqevent(event);
406 }
407 
408 /*
409  * Place the event in the event queue and wakeup any waiting processes.
410  */
411 static void
412 aed_enqevent(adb_event_t *event)
413 {
414 	int s;
415 
416 	s = splvm();
417 
418 #ifdef DIAGNOSTIC
419 	if (aed_sc->sc_evq_tail < 0 || aed_sc->sc_evq_tail >= AED_MAX_EVENTS)
420 		panic("adb: event queue tail is out of bounds");
421 
422 	if (aed_sc->sc_evq_len < 0 || aed_sc->sc_evq_len > AED_MAX_EVENTS)
423 		panic("adb: event queue len is out of bounds");
424 #endif
425 
426 	if (aed_sc->sc_evq_len == AED_MAX_EVENTS) {
427 		splx(s);
428 		return;		/* Oh, well... */
429 	}
430 	aed_sc->sc_evq[(aed_sc->sc_evq_len + aed_sc->sc_evq_tail) %
431 	    AED_MAX_EVENTS] = *event;
432 	aed_sc->sc_evq_len++;
433 
434 	selnotify(&aed_sc->sc_selinfo, 0, 0);
435 	if (aed_sc->sc_ioproc)
436 		psignal(aed_sc->sc_ioproc, SIGIO);
437 
438 	splx(s);
439 }
440 
441 int
442 aedopen(dev_t dev, int flag, int mode, struct lwp *l)
443 {
444 	struct aed_softc *sc;
445 	int s;
446 
447 	sc = device_lookup_private(&aed_cd, minor(dev));
448 	if (sc == NULL)
449 		return (ENXIO);
450 
451 	s = splvm();
452 	if (sc->sc_open) {
453 		splx(s);
454 		return (EBUSY);
455 	}
456 	aed_sc->sc_evq_tail = 0;
457 	aed_sc->sc_evq_len = 0;
458 	aed_sc->sc_open = 1;
459 	aed_sc->sc_ioproc = l->l_proc;
460 	splx(s);
461 
462 	return 0;
463 }
464 
465 
466 int
467 aedclose(dev_t dev, int flag, int mode, struct lwp *l)
468 {
469 	int s;
470 
471 	s = splvm();
472 	aed_sc->sc_open = 0;
473 	aed_sc->sc_ioproc = NULL;
474 	splx(s);
475 
476 	return (0);
477 }
478 
479 
480 int
481 aedread(dev_t dev, struct uio *uio, int flag)
482 {
483 	int s, error;
484 	int willfit;
485 	int total;
486 	int firstmove;
487 	int moremove;
488 
489 	if (uio->uio_resid < sizeof(adb_event_t))
490 		return (EMSGSIZE);	/* close enough. */
491 
492 	s = splvm();
493 	if (aed_sc->sc_evq_len == 0) {
494 		splx(s);
495 		return (0);
496 	}
497 	willfit = howmany(uio->uio_resid, sizeof(adb_event_t));
498 	total = (aed_sc->sc_evq_len < willfit) ? aed_sc->sc_evq_len : willfit;
499 
500 	firstmove = (aed_sc->sc_evq_tail + total > AED_MAX_EVENTS)
501 	    ? (AED_MAX_EVENTS - aed_sc->sc_evq_tail) : total;
502 
503 	error = uiomove((void *) & aed_sc->sc_evq[aed_sc->sc_evq_tail],
504 	    firstmove * sizeof(adb_event_t), uio);
505 	if (error) {
506 		splx(s);
507 		return (error);
508 	}
509 	moremove = total - firstmove;
510 
511 	if (moremove > 0) {
512 		error = uiomove((void *) & aed_sc->sc_evq[0],
513 		    moremove * sizeof(adb_event_t), uio);
514 		if (error) {
515 			splx(s);
516 			return (error);
517 		}
518 	}
519 	aed_sc->sc_evq_tail = (aed_sc->sc_evq_tail + total) % AED_MAX_EVENTS;
520 	aed_sc->sc_evq_len -= total;
521 	splx(s);
522 	return (0);
523 }
524 
525 int
526 aedioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
527 {
528 	switch (cmd) {
529 	case ADBIOC_DEVSINFO: {
530 		adb_devinfo_t *di;
531 		ADBDataBlock adbdata;
532 		int totaldevs;
533 		int adbaddr;
534 		int i;
535 
536 		di = (void *)data;
537 
538 		/* Initialize to no devices */
539 		for (i = 0; i < 16; i++)
540 			di->dev[i].addr = -1;
541 
542 		totaldevs = CountADBs();
543 		for (i = 1; i <= totaldevs; i++) {
544 			adbaddr = GetIndADB(&adbdata, i);
545 			di->dev[adbaddr].addr = adbaddr;
546 			di->dev[adbaddr].default_addr = (int)(adbdata.origADBAddr);
547 			di->dev[adbaddr].handler_id = (int)(adbdata.devType);
548 			}
549 
550 		/* Must call ADB Manager to get devices now */
551 		break;
552 	}
553 
554 	case ADBIOC_GETREPEAT:{
555 		adb_rptinfo_t *ri;
556 
557 		ri = (void *)data;
558 		ri->delay_ticks = aed_sc->sc_rptdelay;
559 		ri->interval_ticks = aed_sc->sc_rptinterval;
560 		break;
561 	}
562 
563 	case ADBIOC_SETREPEAT:{
564 		adb_rptinfo_t *ri;
565 
566 		ri = (void *) data;
567 		aed_sc->sc_rptdelay = ri->delay_ticks;
568 		aed_sc->sc_rptinterval = ri->interval_ticks;
569 		break;
570 	}
571 
572 	case ADBIOC_RESET:
573 		/* Do nothing for now */
574 		break;
575 
576 	case ADBIOC_LISTENCMD:
577 		/* adb_listencmd_t *lc = data; */
578 
579 	default:
580 		return (EINVAL);
581 	}
582 	return (0);
583 }
584 
585 
586 int
587 aedpoll(dev_t dev, int events, struct lwp *l)
588 {
589 	int s, revents;
590 
591 	revents = events & (POLLOUT | POLLWRNORM);
592 
593 	if ((events & (POLLIN | POLLRDNORM)) == 0)
594 		return (revents);
595 
596 	s = splvm();
597 	if (aed_sc->sc_evq_len > 0)
598 		revents |= events & (POLLIN | POLLRDNORM);
599 	else
600 		selrecord(l, &aed_sc->sc_selinfo);
601 	splx(s);
602 
603 	return (revents);
604 }
605 
606 static void
607 filt_aedrdetach(struct knote *kn)
608 {
609 	int s;
610 
611 	s = splvm();
612 	selremove_knote(&aed_sc->sc_selinfo, kn);
613 	splx(s);
614 }
615 
616 static int
617 filt_aedread(struct knote *kn, long hint)
618 {
619 
620 	kn->kn_data = aed_sc->sc_evq_len * sizeof(adb_event_t);
621 	return (kn->kn_data > 0);
622 }
623 
624 static const struct filterops aedread_filtops = {
625 	.f_flags = FILTEROP_ISFD,
626 	.f_attach = NULL,
627 	.f_detach = filt_aedrdetach,
628 	.f_event = filt_aedread,
629 };
630 
631 int
632 aedkqfilter(dev_t dev, struct knote *kn)
633 {
634 	int s;
635 
636 	switch (kn->kn_filter) {
637 	case EVFILT_READ:
638 		kn->kn_fop = &aedread_filtops;
639 		s = splvm();
640 		selrecord_knote(&aed_sc->sc_selinfo, kn);
641 		splx(s);
642 		break;
643 
644 	case EVFILT_WRITE:
645 		kn->kn_fop = &seltrue_filtops;
646 		break;
647 
648 	default:
649 		return (EINVAL);
650 	}
651 
652 	return (0);
653 }
654 
655 static void
656 aed_brightness_down(device_t dev)
657 {
658 	int level, step;
659 
660 	level = brightness;
661 	if (level <= 4) 	 /* logarithmic brightness curve. */
662 		step = 1;
663 	else
664 		step = BRIGHTNESS_STEP;
665 
666 	level = uimax(BRIGHTNESS_MIN, level - step);
667 	brightness = pm_set_brightness(level);
668 }
669 
670 static void
671 aed_brightness_up(device_t dev)
672 {
673 	int level, step;
674 
675 	level = brightness;
676 	if (level <= 4) 	 /* logarithmic brightness curve. */
677 		step = 1;
678 	else
679 		step = BRIGHTNESS_STEP;
680 
681 	level = uimin(BRIGHTNESS_MAX, level + step);
682 	brightness = pm_set_brightness(level);
683 }
684