1 /* $NetBSD: aed.c,v 1.42 2025/01/12 05:56:59 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.42 2025/01/12 05:56:59 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 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 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 return; 168 } 169 170 /* 171 * Given a keyboard ADB event, record the keycode and call the key 172 * repeat handler, optionally passing the event through the mouse 173 * button emulation handler first. Pass mouse events directly to 174 * the handoff function. 175 */ 176 int 177 aed_input(adb_event_t *event) 178 { 179 adb_event_t new_event = *event; 180 int rv = aed_sc->sc_open; 181 182 switch (event->def_addr) { 183 case ADBADDR_KBD: 184 if (aed_sc->sc_options & AED_MSEMUL) { 185 rv = aed_emulate_mouse(&new_event); 186 } else 187 aed_dokeyupdown(&new_event); 188 break; 189 case ADBADDR_MS: 190 event->u.m.buttons |= aed_sc->sc_buttons; 191 new_event.u.m.buttons |= aed_sc->sc_buttons; 192 aed_handoff(&new_event); 193 break; 194 default: /* God only knows. */ 195 #ifdef DIAGNOSTIC 196 panic("aed: received event from unsupported device!"); 197 #endif 198 rv = 0; 199 break; 200 } 201 202 return (rv); 203 } 204 205 /* 206 * Handles mouse button emulation via the keyboard. If the emulation 207 * modifier key is down, left and right arrows will generate 2nd and 208 * 3rd mouse button events while the 1, 2, and 3 keys will generate 209 * the corresponding mouse button event. 210 */ 211 static int 212 aed_emulate_mouse(adb_event_t *event) 213 { 214 static int emulmodkey_down; 215 adb_event_t new_event; 216 int result = 0; 217 218 if (event->u.k.key == ADBK_KEYDOWN(ADBK_OPTION)) { 219 emulmodkey_down = 1; 220 } else if (event->u.k.key == ADBK_KEYUP(ADBK_OPTION)) { 221 /* key up */ 222 emulmodkey_down = 0; 223 if (aed_sc->sc_buttons & 0xfe) { 224 aed_sc->sc_buttons &= 1; 225 new_event.def_addr = ADBADDR_MS; 226 new_event.u.m.buttons = aed_sc->sc_buttons; 227 new_event.u.m.dx = new_event.u.m.dy = 0; 228 microtime(&new_event.timestamp); 229 aed_handoff(&new_event); 230 } 231 } else if (emulmodkey_down) { 232 switch(event->u.k.key) { 233 #ifdef ALTXBUTTONS 234 case ADBK_KEYDOWN(ADBK_1): 235 result = 1; 236 aed_sc->sc_buttons |= 1; /* left down */ 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 case ADBK_KEYUP(ADBK_1): 244 result = 1; 245 aed_sc->sc_buttons &= ~1; /* left up */ 246 new_event.def_addr = ADBADDR_MS; 247 new_event.u.m.buttons = aed_sc->sc_buttons; 248 new_event.u.m.dx = new_event.u.m.dy = 0; 249 microtime(&new_event.timestamp); 250 aed_handoff(&new_event); 251 break; 252 #endif 253 case ADBK_KEYDOWN(ADBK_LEFT): 254 #ifdef ALTXBUTTONS 255 case ADBK_KEYDOWN(ADBK_2): 256 #endif 257 result = 1; 258 aed_sc->sc_buttons |= 2; /* middle down */ 259 new_event.def_addr = ADBADDR_MS; 260 new_event.u.m.buttons = aed_sc->sc_buttons; 261 new_event.u.m.dx = new_event.u.m.dy = 0; 262 microtime(&new_event.timestamp); 263 aed_handoff(&new_event); 264 break; 265 case ADBK_KEYUP(ADBK_LEFT): 266 #ifdef ALTXBUTTONS 267 case ADBK_KEYUP(ADBK_2): 268 #endif 269 result = 1; 270 aed_sc->sc_buttons &= ~2; /* middle up */ 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_KEYDOWN(ADBK_RIGHT): 278 #ifdef ALTXBUTTONS 279 case ADBK_KEYDOWN(ADBK_3): 280 #endif 281 result = 1; 282 aed_sc->sc_buttons |= 4; /* right down */ 283 new_event.def_addr = ADBADDR_MS; 284 new_event.u.m.buttons = aed_sc->sc_buttons; 285 new_event.u.m.dx = new_event.u.m.dy = 0; 286 microtime(&new_event.timestamp); 287 aed_handoff(&new_event); 288 break; 289 case ADBK_KEYUP(ADBK_RIGHT): 290 #ifdef ALTXBUTTONS 291 case ADBK_KEYUP(ADBK_3): 292 #endif 293 result = 1; 294 aed_sc->sc_buttons &= ~4; /* right up */ 295 new_event.def_addr = ADBADDR_MS; 296 new_event.u.m.buttons = aed_sc->sc_buttons; 297 new_event.u.m.dx = new_event.u.m.dy = 0; 298 microtime(&new_event.timestamp); 299 aed_handoff(&new_event); 300 break; 301 case ADBK_KEYDOWN(ADBK_UP): 302 result = 1; 303 break; 304 case ADBK_KEYUP(ADBK_UP): 305 result = 1; 306 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_UP); 307 break; 308 case ADBK_KEYDOWN(ADBK_DOWN): 309 result = 1; 310 break; 311 case ADBK_KEYUP(ADBK_DOWN): 312 result = 1; 313 pmf_event_inject(NULL, PMFE_DISPLAY_BRIGHTNESS_DOWN); 314 break; 315 case ADBK_KEYUP(ADBK_SHIFT): 316 case ADBK_KEYDOWN(ADBK_SHIFT): 317 case ADBK_KEYUP(ADBK_CONTROL): 318 case ADBK_KEYDOWN(ADBK_CONTROL): 319 case ADBK_KEYUP(ADBK_FLOWER): 320 case ADBK_KEYDOWN(ADBK_FLOWER): 321 /* ctrl, shift, cmd */ 322 aed_dokeyupdown(event); 323 break; 324 default: 325 if (event->u.k.key & 0x80) 326 /* ignore keyup */ 327 break; 328 329 /* key down */ 330 new_event = *event; 331 332 /* send option-down */ 333 new_event.u.k.key = ADBK_KEYDOWN(ADBK_OPTION); 334 new_event.bytes[0] = new_event.u.k.key; 335 microtime(&new_event.timestamp); 336 aed_dokeyupdown(&new_event); 337 338 /* send key-down */ 339 new_event.u.k.key = event->bytes[0]; 340 new_event.bytes[0] = new_event.u.k.key; 341 microtime(&new_event.timestamp); 342 aed_dokeyupdown(&new_event); 343 344 /* send key-up */ 345 new_event.u.k.key = 346 ADBK_KEYUP(ADBK_KEYVAL(event->bytes[0])); 347 microtime(&new_event.timestamp); 348 new_event.bytes[0] = new_event.u.k.key; 349 aed_dokeyupdown(&new_event); 350 351 /* send option-up */ 352 new_event.u.k.key = ADBK_KEYUP(ADBK_OPTION); 353 new_event.bytes[0] = new_event.u.k.key; 354 microtime(&new_event.timestamp); 355 aed_dokeyupdown(&new_event); 356 break; 357 } 358 } else { 359 aed_dokeyupdown(event); 360 } 361 362 return result; 363 } 364 365 /* 366 * Keyboard autorepeat timeout function. Sends key up/down events 367 * for the repeating key and schedules the next call at sc_rptinterval 368 * ticks in the future. 369 */ 370 static void 371 aed_kbdrpt(void *kstate) 372 { 373 struct aed_softc *sc = (struct aed_softc *)kstate; 374 375 sc->sc_rptevent.bytes[0] |= 0x80; 376 microtime(&sc->sc_rptevent.timestamp); 377 aed_handoff(&sc->sc_rptevent); /* do key up */ 378 379 sc->sc_rptevent.bytes[0] &= 0x7f; 380 microtime(&sc->sc_rptevent.timestamp); 381 aed_handoff(&sc->sc_rptevent); /* do key down */ 382 383 if (sc->sc_repeating == sc->sc_rptevent.u.k.key) { 384 callout_reset(&sc->sc_repeat_ch, sc->sc_rptinterval, 385 aed_kbdrpt, kstate); 386 } 387 } 388 389 390 /* 391 * Cancels the currently repeating key event if there is one, schedules 392 * a new repeating key event if needed, and hands the event off to the 393 * appropriate subsystem. 394 */ 395 static void 396 aed_dokeyupdown(adb_event_t *event) 397 { 398 int kbd_key; 399 400 kbd_key = ADBK_KEYVAL(event->u.k.key); 401 if (ADBK_PRESS(event->u.k.key) && keyboard[kbd_key][0] != 0) { 402 /* ignore shift & control */ 403 if (aed_sc->sc_repeating != -1) { 404 callout_stop(&aed_sc->sc_repeat_ch); 405 } 406 aed_sc->sc_rptevent = *event; 407 aed_sc->sc_repeating = kbd_key; 408 callout_reset(&aed_sc->sc_repeat_ch, aed_sc->sc_rptdelay, 409 aed_kbdrpt, (void *)aed_sc); 410 } else { 411 if (aed_sc->sc_repeating != -1) { 412 aed_sc->sc_repeating = -1; 413 callout_stop(&aed_sc->sc_repeat_ch); 414 } 415 aed_sc->sc_rptevent = *event; 416 } 417 aed_handoff(event); 418 } 419 420 /* 421 * Place the event in the event queue if a requesting device is open 422 * and we are not polling, otherwise, pass it up to the console driver. 423 */ 424 static void 425 aed_handoff(adb_event_t *event) 426 { 427 if (aed_sc->sc_open && !adb_polling) 428 aed_enqevent(event); 429 } 430 431 /* 432 * Place the event in the event queue and wakeup any waiting processes. 433 */ 434 static void 435 aed_enqevent(adb_event_t *event) 436 { 437 int s; 438 439 s = splvm(); 440 441 #ifdef DIAGNOSTIC 442 if (aed_sc->sc_evq_tail < 0 || aed_sc->sc_evq_tail >= AED_MAX_EVENTS) 443 panic("adb: event queue tail is out of bounds"); 444 445 if (aed_sc->sc_evq_len < 0 || aed_sc->sc_evq_len > AED_MAX_EVENTS) 446 panic("adb: event queue len is out of bounds"); 447 #endif 448 449 if (aed_sc->sc_evq_len == AED_MAX_EVENTS) { 450 splx(s); 451 return; /* Oh, well... */ 452 } 453 aed_sc->sc_evq[(aed_sc->sc_evq_len + aed_sc->sc_evq_tail) % 454 AED_MAX_EVENTS] = *event; 455 aed_sc->sc_evq_len++; 456 457 selnotify(&aed_sc->sc_selinfo, 0, 0); 458 if (aed_sc->sc_ioproc) 459 psignal(aed_sc->sc_ioproc, SIGIO); 460 461 splx(s); 462 } 463 464 int 465 aedopen(dev_t dev, int flag, int mode, struct lwp *l) 466 { 467 struct aed_softc *sc; 468 int s; 469 470 sc = device_lookup_private(&aed_cd, minor(dev)); 471 if (sc == NULL) 472 return (ENXIO); 473 474 s = splvm(); 475 if (sc->sc_open) { 476 splx(s); 477 return (EBUSY); 478 } 479 aed_sc->sc_evq_tail = 0; 480 aed_sc->sc_evq_len = 0; 481 aed_sc->sc_open = 1; 482 aed_sc->sc_ioproc = l->l_proc; 483 splx(s); 484 485 return 0; 486 } 487 488 489 int 490 aedclose(dev_t dev, int flag, int mode, struct lwp *l) 491 { 492 int s; 493 494 s = splvm(); 495 aed_sc->sc_open = 0; 496 aed_sc->sc_ioproc = NULL; 497 splx(s); 498 499 return (0); 500 } 501 502 503 int 504 aedread(dev_t dev, struct uio *uio, int flag) 505 { 506 int s, error; 507 int willfit; 508 int total; 509 int firstmove; 510 int moremove; 511 512 if (uio->uio_resid < sizeof(adb_event_t)) 513 return (EMSGSIZE); /* close enough. */ 514 515 s = splvm(); 516 if (aed_sc->sc_evq_len == 0) { 517 splx(s); 518 return (0); 519 } 520 willfit = howmany(uio->uio_resid, sizeof(adb_event_t)); 521 total = (aed_sc->sc_evq_len < willfit) ? aed_sc->sc_evq_len : willfit; 522 523 firstmove = (aed_sc->sc_evq_tail + total > AED_MAX_EVENTS) 524 ? (AED_MAX_EVENTS - aed_sc->sc_evq_tail) : total; 525 526 error = uiomove((void *) & aed_sc->sc_evq[aed_sc->sc_evq_tail], 527 firstmove * sizeof(adb_event_t), uio); 528 if (error) { 529 splx(s); 530 return (error); 531 } 532 moremove = total - firstmove; 533 534 if (moremove > 0) { 535 error = uiomove((void *) & aed_sc->sc_evq[0], 536 moremove * sizeof(adb_event_t), uio); 537 if (error) { 538 splx(s); 539 return (error); 540 } 541 } 542 aed_sc->sc_evq_tail = (aed_sc->sc_evq_tail + total) % AED_MAX_EVENTS; 543 aed_sc->sc_evq_len -= total; 544 splx(s); 545 return (0); 546 } 547 548 int 549 aedioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l) 550 { 551 switch (cmd) { 552 case ADBIOC_DEVSINFO: { 553 adb_devinfo_t *di; 554 ADBDataBlock adbdata; 555 int totaldevs; 556 int adbaddr; 557 int i; 558 559 di = (void *)data; 560 561 /* Initialize to no devices */ 562 for (i = 0; i < 16; i++) 563 di->dev[i].addr = -1; 564 565 totaldevs = CountADBs(); 566 for (i = 1; i <= totaldevs; i++) { 567 adbaddr = GetIndADB(&adbdata, i); 568 di->dev[adbaddr].addr = adbaddr; 569 di->dev[adbaddr].default_addr = (int)(adbdata.origADBAddr); 570 di->dev[adbaddr].handler_id = (int)(adbdata.devType); 571 } 572 573 /* Must call ADB Manager to get devices now */ 574 break; 575 } 576 577 case ADBIOC_GETREPEAT:{ 578 adb_rptinfo_t *ri; 579 580 ri = (void *)data; 581 ri->delay_ticks = aed_sc->sc_rptdelay; 582 ri->interval_ticks = aed_sc->sc_rptinterval; 583 break; 584 } 585 586 case ADBIOC_SETREPEAT:{ 587 adb_rptinfo_t *ri; 588 589 ri = (void *) data; 590 aed_sc->sc_rptdelay = ri->delay_ticks; 591 aed_sc->sc_rptinterval = ri->interval_ticks; 592 break; 593 } 594 595 case ADBIOC_RESET: 596 /* Do nothing for now */ 597 break; 598 599 case ADBIOC_LISTENCMD: 600 /* adb_listencmd_t *lc = data; */ 601 602 default: 603 return (EINVAL); 604 } 605 return (0); 606 } 607 608 609 int 610 aedpoll(dev_t dev, int events, struct lwp *l) 611 { 612 int s, revents; 613 614 revents = events & (POLLOUT | POLLWRNORM); 615 616 if ((events & (POLLIN | POLLRDNORM)) == 0) 617 return (revents); 618 619 s = splvm(); 620 if (aed_sc->sc_evq_len > 0) 621 revents |= events & (POLLIN | POLLRDNORM); 622 else 623 selrecord(l, &aed_sc->sc_selinfo); 624 splx(s); 625 626 return (revents); 627 } 628 629 static void 630 filt_aedrdetach(struct knote *kn) 631 { 632 int s; 633 634 s = splvm(); 635 selremove_knote(&aed_sc->sc_selinfo, kn); 636 splx(s); 637 } 638 639 static int 640 filt_aedread(struct knote *kn, long hint) 641 { 642 643 kn->kn_data = aed_sc->sc_evq_len * sizeof(adb_event_t); 644 return (kn->kn_data > 0); 645 } 646 647 static const struct filterops aedread_filtops = { 648 .f_flags = FILTEROP_ISFD, 649 .f_attach = NULL, 650 .f_detach = filt_aedrdetach, 651 .f_event = filt_aedread, 652 }; 653 654 int 655 aedkqfilter(dev_t dev, struct knote *kn) 656 { 657 int s; 658 659 switch (kn->kn_filter) { 660 case EVFILT_READ: 661 kn->kn_fop = &aedread_filtops; 662 s = splvm(); 663 selrecord_knote(&aed_sc->sc_selinfo, kn); 664 splx(s); 665 break; 666 667 case EVFILT_WRITE: 668 kn->kn_fop = &seltrue_filtops; 669 break; 670 671 default: 672 return (EINVAL); 673 } 674 675 return (0); 676 } 677 678 static void 679 aed_brightness_down(device_t dev) 680 { 681 int level, step; 682 683 level = brightness; 684 if (level <= 4) /* logarithmic brightness curve. */ 685 step = 1; 686 else 687 step = BRIGHTNESS_STEP; 688 689 level = uimax(BRIGHTNESS_MIN, level - step); 690 brightness = pm_set_brightness(level); 691 } 692 693 static void 694 aed_brightness_up(device_t dev) 695 { 696 int level, step; 697 698 level = brightness; 699 if (level <= 4) /* logarithmic brightness curve. */ 700 step = 1; 701 else 702 step = BRIGHTNESS_STEP; 703 704 level = uimin(BRIGHTNESS_MAX, level + step); 705 brightness = pm_set_brightness(level); 706 } 707 708 static void 709 aed_display_on(device_t dev) 710 { 711 (void)pm_set_brightness(brightness); 712 } 713 714 static void 715 aed_display_off(device_t dev) 716 { 717 (void)pm_set_brightness(0); 718 } 719