/* $NetBSD: adb.c,v 1.2 1998/07/02 18:58:32 tsubai Exp $ */ /*- * Copyright (C) 1994 Bradley A. Grantham * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Bradley A. Grantham. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #define spladb splhigh /* XXX */ /* * Function declarations. */ static int adbmatch __P((struct device *, struct cfdata *, void *)); static void adbattach __P((struct device *, struct device *, void *)); /* * Global variables. */ int adb_polling = 0; /* Are we polling? (Debugger mode) */ int adb_initted = 0; /* adb_init() has completed successfully */ #ifdef ADB_DEBUG int adb_debug = 0; /* Output debugging messages */ #endif /* ADB_DEBUG */ volatile u_char *Via1Base; /* * Local variables. */ /* External keyboard translation matrix */ extern unsigned char keyboard[128][3]; /* Event queue definitions */ #if !defined(ADB_MAX_EVENTS) #define ADB_MAX_EVENTS 200 /* Maximum events to be kept in queue */ /* maybe should be higher for slower macs? */ #endif /* !defined(ADB_MAX_EVENTS) */ static adb_event_t adb_evq[ADB_MAX_EVENTS]; /* ADB event queue */ static int adb_evq_tail = 0; /* event queue tail */ static int adb_evq_len = 0; /* event queue length */ /* ADB device state information */ static int adb_isopen = 0; /* Are we queuing events for adb_read? */ static struct selinfo adb_selinfo; /* select() info */ static struct proc *adb_ioproc = NULL; /* process to wakeup */ /* Key repeat parameters */ static int adb_rptdelay = 20; /* ticks before auto-repeat */ static int adb_rptinterval = 6; /* ticks between auto-repeat */ static int adb_repeating = -1; /* key that is auto-repeating */ static adb_event_t adb_rptevent;/* event to auto-repeat */ /* Mouse button state */ static int adb_ms_buttons = 0; /* Driver definition. -- This should probably be a bus... */ struct cfattach adb_ca = { sizeof(struct adb_softc), adbmatch, adbattach }; static int adbmatch(parent, cf, aux) struct device *parent; struct cfdata *cf; void *aux; { struct confargs *ca = aux; if (strcmp(ca->ca_name, "via-cuda") != 0) return 0; if (ca->ca_nreg < 8) return 0; if (ca->ca_nintr < 4) return 0; return 1; } static void adbattach(parent, self, aux) struct device *parent, *self; void *aux; { struct adb_softc *sc = (struct adb_softc *)self; struct confargs *ca = aux; u_long time = -1; extern adb_intr(); ca->ca_reg[0] += ca->ca_baseaddr; sc->sc_regbase = mapiodev(ca->ca_reg[0], ca->ca_reg[1]); Via1Base = sc->sc_regbase; printf(" irq %d\n", ca->ca_intr[0]); adb_polling = 1; adb_init(); kbd_init(); adb_polling = 0; intr_establish(ca->ca_intr[0], IST_LEVEL, IPL_HIGH, adb_intr, sc); } void adb_enqevent(event) adb_event_t *event; { int s; s = spladb(); #ifdef DIAGNOSTIC if (adb_evq_tail < 0 || adb_evq_tail >= ADB_MAX_EVENTS) panic("adb: event queue tail is out of bounds"); if (adb_evq_len < 0 || adb_evq_len > ADB_MAX_EVENTS) panic("adb: event queue len is out of bounds"); #endif if (adb_evq_len == ADB_MAX_EVENTS) { splx(s); return; /* Oh, well... */ } adb_evq[(adb_evq_len + adb_evq_tail) % ADB_MAX_EVENTS] = *event; adb_evq_len++; selwakeup(&adb_selinfo); if (adb_ioproc) psignal(adb_ioproc, SIGIO); splx(s); } void adb_handoff(event) adb_event_t *event; { if (adb_isopen && !adb_polling) { adb_enqevent(event); } else { if (event->def_addr == 2) kbd_intr(event); } } void adb_autorepeat(keyp) void *keyp; { int key = (int)keyp; adb_rptevent.bytes[0] |= 0x80; microtime(&adb_rptevent.timestamp); adb_handoff(&adb_rptevent); /* do key up */ adb_rptevent.bytes[0] &= 0x7f; microtime(&adb_rptevent.timestamp); adb_handoff(&adb_rptevent); /* do key down */ if (adb_repeating == key) { timeout(adb_autorepeat, keyp, adb_rptinterval); } } void adb_dokeyupdown(event) adb_event_t *event; { int adb_key; if (event->def_addr == 2) { adb_key = event->u.k.key & 0x7f; if (!(event->u.k.key & 0x80) && keyboard[event->u.k.key & 0x7f][0] != 0) { /* ignore shift & control */ if (adb_repeating != -1) { untimeout(adb_autorepeat, (void *)adb_rptevent.u.k.key); } adb_rptevent = *event; adb_repeating = adb_key; timeout(adb_autorepeat, (void *)adb_key, adb_rptdelay); } else { if (adb_repeating != -1) { adb_repeating = -1; untimeout(adb_autorepeat, (void *)adb_rptevent.u.k.key); } adb_rptevent = *event; } } adb_handoff(event); } void adb_keymaybemouse(event) adb_event_t *event; { static int optionkey_down = 0; adb_event_t new_event; if (event->u.k.key == ADBK_KEYDOWN(ADBK_OPTION)) { optionkey_down = 1; } else if (event->u.k.key == ADBK_KEYUP(ADBK_OPTION)) { /* key up */ optionkey_down = 0; if (adb_ms_buttons & 0xfe) { adb_ms_buttons &= 1; new_event.def_addr = ADBADDR_MS; new_event.u.m.buttons = adb_ms_buttons; new_event.u.m.dx = new_event.u.m.dy = 0; microtime(&new_event.timestamp); adb_dokeyupdown(&new_event); } } else if (optionkey_down) { #ifdef ALTXBUTTONS if (event->u.k.key == ADBK_KEYDOWN(ADBK_1)) { adb_ms_buttons |= 1; /* left down */ new_event.def_addr = ADBADDR_MS; new_event.u.m.buttons = adb_ms_buttons; new_event.u.m.dx = new_event.u.m.dy = 0; microtime(&new_event.timestamp); adb_dokeyupdown(&new_event); } else if (event->u.k.key == ADBK_KEYUP(ADBK_1)) { adb_ms_buttons &= ~1; /* left up */ new_event.def_addr = ADBADDR_MS; new_event.u.m.buttons = adb_ms_buttons; new_event.u.m.dx = new_event.u.m.dy = 0; microtime(&new_event.timestamp); adb_dokeyupdown(&new_event); } else #endif if (event->u.k.key == ADBK_KEYDOWN(ADBK_LEFT) #ifdef ALTXBUTTONS || event->u.k.key == ADBK_KEYDOWN(ADBK_2) #endif ) { adb_ms_buttons |= 2; /* middle down */ new_event.def_addr = ADBADDR_MS; new_event.u.m.buttons = adb_ms_buttons; new_event.u.m.dx = new_event.u.m.dy = 0; microtime(&new_event.timestamp); adb_dokeyupdown(&new_event); } else if (event->u.k.key == ADBK_KEYUP(ADBK_LEFT) #ifdef ALTXBUTTONS || event->u.k.key == ADBK_KEYUP(ADBK_2) #endif ) { adb_ms_buttons &= ~2; /* middle up */ new_event.def_addr = ADBADDR_MS; new_event.u.m.buttons = adb_ms_buttons; new_event.u.m.dx = new_event.u.m.dy = 0; microtime(&new_event.timestamp); adb_dokeyupdown(&new_event); } else if (event->u.k.key == ADBK_KEYDOWN(ADBK_RIGHT) #ifdef ALTXBUTTONS || event->u.k.key == ADBK_KEYDOWN(ADBK_3) #endif ) { adb_ms_buttons |= 4; /* right down */ new_event.def_addr = ADBADDR_MS; new_event.u.m.buttons = adb_ms_buttons; new_event.u.m.dx = new_event.u.m.dy = 0; microtime(&new_event.timestamp); adb_dokeyupdown(&new_event); } else if (event->u.k.key == ADBK_KEYUP(ADBK_RIGHT) #ifdef ALTXBUTTONS || event->u.k.key == ADBK_KEYUP(ADBK_3) #endif ) { adb_ms_buttons &= ~4; /* right up */ new_event.def_addr = ADBADDR_MS; new_event.u.m.buttons = adb_ms_buttons; new_event.u.m.dx = new_event.u.m.dy = 0; microtime(&new_event.timestamp); adb_dokeyupdown(&new_event); } else if (ADBK_MODIFIER(event->u.k.key)) { /* ctrl, shift, cmd */ adb_dokeyupdown(event); } else if (!(event->u.k.key & 0x80)) { /* key down */ new_event = *event; /* send option-down */ new_event.u.k.key = ADBK_KEYDOWN(ADBK_OPTION); new_event.bytes[0] = new_event.u.k.key; microtime(&new_event.timestamp); adb_dokeyupdown(&new_event); /* send key-down */ new_event.u.k.key = event->bytes[0]; new_event.bytes[0] = new_event.u.k.key; microtime(&new_event.timestamp); adb_dokeyupdown(&new_event); /* send key-up */ new_event.u.k.key = ADBK_KEYUP(ADBK_KEYVAL(event->bytes[0])); microtime(&new_event.timestamp); new_event.bytes[0] = new_event.u.k.key; adb_dokeyupdown(&new_event); /* send option-up */ new_event.u.k.key = ADBK_KEYUP(ADBK_OPTION); new_event.bytes[0] = new_event.u.k.key; microtime(&new_event.timestamp); adb_dokeyupdown(&new_event); } else { /* option-keyup -- do nothing. */ } } else { adb_dokeyupdown(event); } } void adb_processevent(event) adb_event_t *event; { adb_event_t new_event; int i, button_bit, max_byte, mask, buttons; new_event = *event; buttons = 0; switch (event->def_addr) { case ADBADDR_KBD: new_event.u.k.key = event->bytes[0]; new_event.bytes[1] = 0xff; adb_keymaybemouse(&new_event); if (event->bytes[1] != 0xff) { new_event.u.k.key = event->bytes[1]; new_event.bytes[0] = event->bytes[1]; new_event.bytes[1] = 0xff; adb_keymaybemouse(&new_event); } break; case ADBADDR_MS: /* * This should handle both plain ol' Apple mice and mice * that claim to support the Extended Apple Mouse Protocol. */ max_byte = event->byte_count; button_bit = 1; switch (event->hand_id) { case ADBMS_USPEED: /* MicroSpeed mouse */ if (max_byte == 4) buttons = (~event->bytes[2]) & 0xff; else buttons = (event->bytes[0] & 0x80) ? 0 : 1; break; case ADBMS_MSA3: /* Mouse Systems A3 mouse */ if (max_byte == 3) buttons = (~event->bytes[2]) & 0x07; else buttons = (event->bytes[0] & 0x80) ? 0 : 1; break; default: /* Classic Mouse Protocol (up to 2 buttons) */ for (i = 0; i < 2; i++, button_bit <<= 1) /* 0 when button down */ if (!(event->bytes[i] & 0x80)) buttons |= button_bit; else buttons &= ~button_bit; /* Extended Protocol (up to 6 more buttons) */ for (mask = 0x80; i < max_byte; i += (mask == 0x80), button_bit <<= 1) { /* 0 when button down */ if (!(event->bytes[i] & mask)) buttons |= button_bit; else buttons &= ~button_bit; mask = ((mask >> 4) & 0xf) | ((mask & 0xf) << 4); } break; } new_event.u.m.buttons = adb_ms_buttons | buttons; new_event.u.m.dx = ((signed int) (event->bytes[1] & 0x3f)) - ((event->bytes[1] & 0x40) ? 64 : 0); new_event.u.m.dy = ((signed int) (event->bytes[0] & 0x3f)) - ((event->bytes[0] & 0x40) ? 64 : 0); adb_dokeyupdown(&new_event); break; default: /* God only knows. */ adb_dokeyupdown(event); } } int adbopen(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { register int unit; int error = 0; int s; unit = minor(dev); if (unit != 0 || !adb_initted) return (ENXIO); s = spladb(); if (adb_isopen) { splx(s); return (EBUSY); } adb_evq_tail = 0; adb_evq_len = 0; adb_isopen = 1; adb_ioproc = p; splx(s); return (error); } int adbclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { int s = spladb(); adb_isopen = 0; adb_ioproc = NULL; splx(s); return (0); } int adbread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int s, error; int willfit; int total; int firstmove; int moremove; if (uio->uio_resid < sizeof(adb_event_t)) return (EMSGSIZE); /* close enough. */ s = spladb(); if (adb_evq_len == 0) { splx(s); return (0); } willfit = howmany(uio->uio_resid, sizeof(adb_event_t)); total = (adb_evq_len < willfit) ? adb_evq_len : willfit; firstmove = (adb_evq_tail + total > ADB_MAX_EVENTS) ? (ADB_MAX_EVENTS - adb_evq_tail) : total; error = uiomove((caddr_t) & adb_evq[adb_evq_tail], firstmove * sizeof(adb_event_t), uio); if (error) { splx(s); return (error); } moremove = total - firstmove; if (moremove > 0) { error = uiomove((caddr_t) & adb_evq[0], moremove * sizeof(adb_event_t), uio); if (error) { splx(s); return (error); } } adb_evq_tail = (adb_evq_tail + total) % ADB_MAX_EVENTS; adb_evq_len -= total; splx(s); return (0); } int adbwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { return 0; } int adbioctl(dev, cmd, data, flag, p) dev_t dev; int cmd; caddr_t data; int flag; struct proc *p; { switch (cmd) { case ADBIOCDEVSINFO: { adb_devinfo_t *di; ADBDataBlock adbdata; int totaldevs; int adbaddr; int i; di = (void *)data; /* Initialize to no devices */ for (i = 0; i < 16; i++) di->dev[i].addr = -1; totaldevs = CountADBs(); for (i = 1; i <= totaldevs; i++) { adbaddr = GetIndADB(&adbdata, i); di->dev[adbaddr].addr = adbaddr; di->dev[adbaddr].default_addr = adbdata.origADBAddr; di->dev[adbaddr].handler_id = adbdata.devType; } /* Must call ADB Manager to get devices now */ break; } case ADBIOCGETREPEAT:{ adb_rptinfo_t *ri; ri = (void *)data; ri->delay_ticks = adb_rptdelay; ri->interval_ticks = adb_rptinterval; break; } case ADBIOCSETREPEAT:{ adb_rptinfo_t *ri; ri = (void *)data; adb_rptdelay = ri->delay_ticks; adb_rptinterval = ri->interval_ticks; break; } case ADBIOCRESET: adb_init(); break; case ADBIOCLISTENCMD:{ adb_listencmd_t *lc; lc = (void *)data; } default: return (EINVAL); } return (0); } int adbpoll(dev, events, p) dev_t dev; int events; struct proc *p; { int s, revents; revents = events & (POLLOUT | POLLWRNORM); if ((events & (POLLIN | POLLRDNORM)) == 0) return (revents); s = spladb(); if (adb_evq_len > 0) revents |= events & (POLLIN | POLLRDNORM); else selrecord(p, &adb_selinfo); splx(s); return (revents); }