1 /* $NetBSD: aed.c,v 1.43 2025/04/03 01:54:46 nat Exp $ */ 2 3 /* 4 * Copyright (c) 2024 Nathanial Sloss <nathanialsloss (at) 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 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 #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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 713 aed_display_on(device_t dev) 714 { 715 (void)pm_set_brightness(brightness); 716 } 717 718 static void 719 aed_display_off(device_t dev) 720 { 721 (void)pm_set_brightness(0); 722 } 723