diff options
| author | tsubai <tsubai@NetBSD.org> | 1998-05-15 10:15:45 +0000 |
|---|---|---|
| committer | tsubai <tsubai@NetBSD.org> | 1998-05-15 10:15:45 +0000 |
| commit | 2be6df07c656dc3c263b28bb8fee733c061fc80f (patch) | |
| tree | 1a9de8d86d84d3e0e8141a09b5405621843f1a0b /sys/arch/macppc/dev | |
| parent | b230bf09a06b3aa79e007f7c5a40d86c3fa5a5d4 (diff) | |
Initial import of macppc port.
Diffstat (limited to 'sys/arch/macppc/dev')
24 files changed, 11538 insertions, 0 deletions
diff --git a/sys/arch/macppc/dev/adb.c b/sys/arch/macppc/dev/adb.c new file mode 100644 index 00000000000..0263a2b4a8c --- /dev/null +++ b/sys/arch/macppc/dev/adb.c @@ -0,0 +1,636 @@ +/* $NetBSD: adb.c,v 1.1 1998/05/15 10:15:47 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 <sys/param.h> +#include <sys/device.h> +#include <sys/fcntl.h> +#include <sys/poll.h> +#include <sys/select.h> +#include <sys/proc.h> +#include <sys/signalvar.h> +#include <sys/systm.h> + +#include <machine/autoconf.h> +#include <machine/keyboard.h> + +#include <macppc/dev/adbvar.h> +#include <macppc/dev/viareg.h> + +#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) + ite_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); +} diff --git a/sys/arch/macppc/dev/adb_direct.c b/sys/arch/macppc/dev/adb_direct.c new file mode 100644 index 00000000000..c7110713989 --- /dev/null +++ b/sys/arch/macppc/dev/adb_direct.c @@ -0,0 +1,2075 @@ +/* $NetBSD: adb_direct.c,v 1.1 1998/05/15 10:15:47 tsubai Exp $ */ + +/* From: adb_direct.c 2.02 4/18/97 jpw */ + +/* + * Copyright (C) 1996, 1997 John P. Wittkoski + * 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 John P. Wittkoski. + * 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. + */ + +/* + * This code is rather messy, but I don't have time right now + * to clean it up as much as I would like. + * But it works, so I'm happy. :-) jpw + */ + +/* + * TO DO: + * - We could reduce the time spent in the adb_intr_* routines + * by having them save the incoming and outgoing data directly + * in the adbInbound and adbOutbound queues, as it would reduce + * the number of times we need to copy the data around. It + * would also make the code more readable and easier to follow. + * - (Related to above) Use the header part of adbCommand to + * reduce the number of copies we have to do of the data. + * - (Related to above) Actually implement the adbOutbound queue. + * This is fairly easy once you switch all the intr routines + * over to using adbCommand structs directly. + * - There is a bug in the state machine of adb_intr_cuda + * code that causes hangs, especially on 030 machines, probably + * because of some timing issues. Because I have been unable to + * determine the exact cause of this bug, I used the timeout function + * to check for and recover from this condition. If anyone finds + * the actual cause of this bug, the calls to timeout and the + * adb_cuda_tickle routine can be removed. + */ + +#include <sys/param.h> +#include <sys/cdefs.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/param.h> +#include <machine/cpu.h> +#include <machine/adbsys.h> + +#include <macppc/dev/viareg.h> +#include <macppc/dev/adbvar.h> +#include <macppc/dev/adb_direct.h> + +#define printf_intr printf + +/* some misc. leftovers */ +#define vPB 0x0000 +#define vPB3 0x08 +#define vPB4 0x10 +#define vPB5 0x20 +#define vSR_INT 0x04 +#define vSR_OUT 0x10 + +/* types of adb hardware that we (will eventually) support */ +#define ADB_HW_UNKNOWN 0x01 /* don't know */ +#define ADB_HW_II 0x02 /* Mac II series */ +#define ADB_HW_IISI 0x03 /* Mac IIsi series */ +#define ADB_HW_PB 0x04 /* PowerBook series */ +#define ADB_HW_CUDA 0x05 /* Machines with a Cuda chip */ + +/* the type of ADB action that we are currently preforming */ +#define ADB_ACTION_NOTREADY 0x01 /* has not been initialized yet */ +#define ADB_ACTION_IDLE 0x02 /* the bus is currently idle */ +#define ADB_ACTION_OUT 0x03 /* sending out a command */ +#define ADB_ACTION_IN 0x04 /* receiving data */ +#define ADB_ACTION_POLLING 0x05 /* polling - II only */ + +/* + * These describe the state of the ADB bus itself, although they + * don't necessarily correspond directly to ADB states. + * Note: these are not really used in the IIsi code. + */ +#define ADB_BUS_UNKNOWN 0x01 /* we don't know yet - all models */ +#define ADB_BUS_IDLE 0x02 /* bus is idle - all models */ +#define ADB_BUS_CMD 0x03 /* starting a command - II models */ +#define ADB_BUS_ODD 0x04 /* the "odd" state - II models */ +#define ADB_BUS_EVEN 0x05 /* the "even" state - II models */ +#define ADB_BUS_ACTIVE 0x06 /* active state - IIsi models */ +#define ADB_BUS_ACK 0x07 /* currently ACKing - IIsi models */ + +/* + * Shortcuts for setting or testing the VIA bit states. + * Not all shortcuts are used for every type of ADB hardware. + */ +#define ADB_SET_STATE_IDLE_II() via_reg_or(VIA1, vBufB, (vPB4 | vPB5)) +#define ADB_SET_STATE_IDLE_IISI() via_reg_and(VIA1, vBufB, ~(vPB4 | vPB5)) +#define ADB_SET_STATE_IDLE_CUDA() via_reg_or(VIA1, vBufB, (vPB4 | vPB5)) +#define ADB_SET_STATE_CMD() via_reg_and(VIA1, vBufB, ~(vPB4 | vPB5)) +#define ADB_SET_STATE_EVEN() write_via_reg(VIA1, vBufB, \ + (read_via_reg(VIA1, vBufB) | vPB4) & ~vPB5) +#define ADB_SET_STATE_ODD() write_via_reg(VIA1, vBufB, \ + (read_via_reg(VIA1, vBufB) | vPB5) & ~vPB4 ) +#define ADB_SET_STATE_ACTIVE() via_reg_or(VIA1, vBufB, vPB5) +#define ADB_SET_STATE_INACTIVE() via_reg_and(VIA1, vBufB, ~vPB5) +#define ADB_SET_STATE_TIP() via_reg_and(VIA1, vBufB, ~vPB5) +#define ADB_CLR_STATE_TIP() via_reg_or(VIA1, vBufB, vPB5) +#define ADB_SET_STATE_ACKON() via_reg_or(VIA1, vBufB, vPB4) +#define ADB_SET_STATE_ACKOFF() via_reg_and(VIA1, vBufB, ~vPB4) +#define ADB_TOGGLE_STATE_ACK_CUDA() via_reg_xor(VIA1, vBufB, vPB4) +#define ADB_SET_STATE_ACKON_CUDA() via_reg_and(VIA1, vBufB, ~vPB4) +#define ADB_SET_STATE_ACKOFF_CUDA() via_reg_or(VIA1, vBufB, vPB4) +#define ADB_SET_SR_INPUT() via_reg_and(VIA1, vACR, ~vSR_OUT) +#define ADB_SET_SR_OUTPUT() via_reg_or(VIA1, vACR, vSR_OUT) +#define ADB_SR() read_via_reg(VIA1, vSR) +#define ADB_VIA_INTR_ENABLE() write_via_reg(VIA1, vIER, 0x84) +#define ADB_VIA_INTR_DISABLE() write_via_reg(VIA1, vIER, 0x04) +#define ADB_VIA_CLR_INTR() write_via_reg(VIA1, vIFR, 0x04) +#define ADB_INTR_IS_OFF (vPB3 == (read_via_reg(VIA1, vBufB) & vPB3)) +#define ADB_INTR_IS_ON (0 == (read_via_reg(VIA1, vBufB) & vPB3)) +#define ADB_SR_INTR_IS_OFF (0 == (read_via_reg(VIA1, vIFR) & vSR_INT)) +#define ADB_SR_INTR_IS_ON (vSR_INT == (read_via_reg(VIA1, \ + vIFR) & vSR_INT)) + +/* + * This is the delay that is required (in uS) between certain + * ADB transactions. The actual timing delay for for each uS is + * calculated at boot time to account for differences in machine speed. + */ +/*#define ADB_DELAY 150*/ +#define ADB_DELAY 1000 + +/* + * Maximum ADB message length; includes space for data, result, and + * device code - plus a little for safety. + */ +#define ADB_MAX_MSG_LENGTH 16 +#define ADB_MAX_HDR_LENGTH 8 + +#define ADB_QUEUE 32 +#define ADB_TICKLE_TICKS 4 + +/* + * A structure for storing information about each ADB device. + */ +struct ADBDevEntry { + void (*ServiceRtPtr) __P((void)); + void *DataAreaAddr; + char devType; + char origAddr; + char currentAddr; +}; + +/* + * Used to hold ADB commands that are waiting to be sent out. + */ +struct adbCmdHoldEntry { + u_char outBuf[ADB_MAX_MSG_LENGTH]; /* our message */ + u_char *saveBuf; /* buffer to know where to save result */ + u_char *compRout; /* completion routine pointer */ + u_char *data; /* completion routine data pointer */ +}; + +/* + * Eventually used for two separate queues, the queue between + * the upper and lower halves, and the outgoing packet queue. + * TO DO: adbCommand can replace all of adbCmdHoldEntry eventually + */ +struct adbCommand { + u_char header[ADB_MAX_HDR_LENGTH]; /* not used yet */ + u_char data[ADB_MAX_MSG_LENGTH]; /* packet data only */ + u_char *saveBuf; /* where to save result */ + u_char *compRout; /* completion routine pointer */ + u_char *compData; /* completion routine data pointer */ + u_int cmd; /* the original command for this data */ + u_int unsol; /* 1 if packet was unsolicited */ + u_int ack_only; /* 1 for no special processing */ +}; + +/* + * A few variables that we need and their initial values. + */ +int adbHardware = ADB_HW_UNKNOWN; +int adbActionState = ADB_ACTION_NOTREADY; +int adbBusState = ADB_BUS_UNKNOWN; +int adbWaiting = 0; /* waiting for return data from the device */ +int adbWriteDelay = 0; /* working on (or waiting to do) a write */ +int adbOutQueueHasData = 0; /* something in the queue waiting to go out */ +int adbNextEnd = 0; /* the next incoming bute is the last (II) */ +int adbSoftPower = 0; /* machine supports soft power */ + +int adbWaitingCmd = 0; /* ADB command we are waiting for */ +u_char *adbBuffer = (long)0; /* pointer to user data area */ +void *adbCompRout = (long)0; /* pointer to the completion routine */ +void *adbCompData = (long)0; /* pointer to the completion routine data */ +long adbFakeInts = 0; /* keeps track of fake ADB interrupts for + * timeouts (II) */ +int adbStarting = 1; /* doing ADBReInit so do polling differently */ +int adbSendTalk = 0; /* the intr routine is sending the talk, not + * the user (II) */ +int adbPolling = 0; /* we are polling for service request */ +int adbPollCmd = 0; /* the last poll command we sent */ + +u_char adbInputBuffer[ADB_MAX_MSG_LENGTH]; /* data input buffer */ +u_char adbOutputBuffer[ADB_MAX_MSG_LENGTH]; /* data output buffer */ +struct adbCmdHoldEntry adbOutQueue; /* our 1 entry output queue */ + +int adbSentChars = 0; /* how many characters we have sent */ +int adbLastDevice = 0; /* last ADB dev we heard from (II ONLY) */ +int adbLastDevIndex = 0; /* last ADB dev loc in dev table (II ONLY) */ +int adbLastCommand = 0; /* the last ADB command we sent (II) */ + +struct ADBDevEntry ADBDevTable[16]; /* our ADB device table */ +int ADBNumDevices; /* num. of ADB devices found with ADBReInit */ + +struct adbCommand adbInbound[ADB_QUEUE]; /* incoming queue */ +int adbInCount = 0; /* how many packets in in queue */ +int adbInHead = 0; /* head of in queue */ +int adbInTail = 0; /* tail of in queue */ +struct adbCommand adbOutbound[ADB_QUEUE]; /* outgoing queue - not used yet */ +int adbOutCount = 0; /* how many packets in out queue */ +int adbOutHead = 0; /* head of out queue */ +int adbOutTail = 0; /* tail of out queue */ + +int tickle_count = 0; /* how many tickles seen for this packet? */ +int tickle_serial = 0; /* the last packet tickled */ +int adb_cuda_serial = 0; /* the current packet */ + +extern struct mac68k_machine_S mac68k_machine; +extern int adb_polling; + +int zshard __P((int)); + +void pm_setup_adb __P((void)); +void pm_check_adb_devices __P((int)); +int pm_adb_op __P((u_char *, void *, void *, int)); +void pm_init_adb_device __P((void)); + +/* + * The following are private routines. + */ +void print_single __P((u_char *)); +void adb_intr __P((void)); +void adb_intr_II __P((void)); +void adb_intr_IIsi __P((void)); +void adb_intr_cuda __P((void)); +void adb_soft_intr __P((void)); +int send_adb_II __P((u_char *, u_char *, void *, void *, int)); +int send_adb_IIsi __P((u_char *, u_char *, void *, void *, int)); +int send_adb_cuda __P((u_char *, u_char *, void *, void *, int)); +void adb_intr_cuda_test __P((void)); +void adb_cuda_tickle __P((void)); +void adb_pass_up __P((struct adbCommand *)); +void adb_op_comprout __P((caddr_t, caddr_t, int)); +void adb_reinit __P((void)); +int count_adbs __P((void)); +int get_ind_adb_info __P((ADBDataBlock *, int)); +int get_adb_info __P((ADBDataBlock *, int)); +int set_adb_info __P((ADBSetInfoBlock *, int)); +void adb_setup_hw_type __P((void)); +int adb_op __P((Ptr, Ptr, Ptr, short)); +int adb_op_sync __P((Ptr, Ptr, Ptr, short)); +void adb_read_II __P((u_char *)); +void adb_hw_setup __P((void)); +void adb_hw_setup_IIsi __P((u_char *)); +void adb_comp_exec __P((void)); +int adb_cmd_result __P((u_char *)); +int adb_cmd_extra __P((u_char *)); +int adb_guess_next_device __P((void)); +int adb_prog_switch_enable __P((void)); +int adb_prog_switch_disable __P((void)); +/* we should create this and it will be the public version */ +int send_adb __P((u_char *, void *, void *)); + +/* + * print_single + * Diagnostic display routine. Displays the hex values of the + * specified elements of the u_char. The length of the "string" + * is in [0]. + */ +void +print_single(thestring) + u_char *thestring; +{ + int x; + + if ((int)(thestring[0]) == 0) { + printf_intr("nothing returned\n"); + return; + } + if (thestring == 0) { + printf_intr("no data - null pointer\n"); + return; + } + if (thestring[0] > 20) { + printf_intr("ADB: ACK > 20 no way!\n"); + thestring[0] = 20; + } + printf_intr("(length=0x%x):", thestring[0]); + for (x = 0; x < thestring[0]; x++) + printf_intr(" 0x%02x", thestring[x + 1]); + printf_intr("\n"); +} + +void +adb_cuda_tickle(void) +{ + volatile int s; + + if (adbActionState == ADB_ACTION_IN) { + if (tickle_serial == adb_cuda_serial) { + if (++tickle_count > 0) { + s = splhigh(); + adbActionState = ADB_ACTION_IDLE; + adbInputBuffer[0] = 0; + ADB_SET_STATE_IDLE_CUDA(); + splx(s); + } + } else { + tickle_serial = adb_cuda_serial; + tickle_count = 0; + } + } else { + tickle_serial = adb_cuda_serial; + tickle_count = 0; + } + + timeout((void *)adb_cuda_tickle, 0, ADB_TICKLE_TICKS); +} + +/* + * called when when an adb interrupt happens + * + * Cuda version of adb_intr + * TO DO: do we want to add some zshard calls in here? + */ +void +adb_intr_cuda(void) +{ + volatile int i, ending; + volatile unsigned int s; + struct adbCommand packet; + + s = splhigh(); /* can't be too careful - might be called */ + /* from a routine, NOT an interrupt */ + + ADB_VIA_CLR_INTR(); /* clear interrupt */ + ADB_VIA_INTR_DISABLE(); /* disable ADB interrupt on IIs. */ + +switch_start: + switch (adbActionState) { + case ADB_ACTION_IDLE: + /* + * This is an unexpected packet, so grab the first (dummy) + * byte, set up the proper vars, and tell the chip we are + * starting to receive the packet by setting the TIP bit. + */ + adbInputBuffer[1] = ADB_SR(); + adb_cuda_serial++; + if (ADB_INTR_IS_OFF) /* must have been a fake start */ + break; + + ADB_SET_SR_INPUT(); + ADB_SET_STATE_TIP(); + + adbInputBuffer[0] = 1; + adbActionState = ADB_ACTION_IN; +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("idle 0x%02x ", adbInputBuffer[1]); +#endif + break; + + case ADB_ACTION_IN: + adbInputBuffer[++adbInputBuffer[0]] = ADB_SR(); + /* intr off means this is the last byte (end of frame) */ + if (ADB_INTR_IS_OFF) + ending = 1; + else + ending = 0; + + if (1 == ending) { /* end of message? */ +#ifdef ADB_DEBUG + if (adb_debug) { + printf_intr("in end 0x%02x ", + adbInputBuffer[adbInputBuffer[0]]); + print_single(adbInputBuffer); + } +#endif + + /* + * Are we waiting AND does this packet match what we + * are waiting for AND is it coming from either the + * ADB or RTC/PRAM sub-device? This section _should_ + * recognize all ADB and RTC/PRAM type commands, but + * there may be more... NOTE: commands are always at + * [4], even for RTC/PRAM commands. + */ + /* set up data for adb_pass_up */ + for (i = 0; i <= adbInputBuffer[0]; i++) + packet.data[i] = adbInputBuffer[i]; + + if ((adbWaiting == 1) && + (adbInputBuffer[4] == adbWaitingCmd) && + ((adbInputBuffer[2] == 0x00) || + (adbInputBuffer[2] == 0x01))) { + packet.saveBuf = adbBuffer; + packet.compRout = adbCompRout; + packet.compData = adbCompData; + packet.unsol = 0; + packet.ack_only = 0; + adb_pass_up(&packet); + + adbWaitingCmd = 0; /* reset "waiting" vars */ + adbWaiting = 0; + adbBuffer = (long)0; + adbCompRout = (long)0; + adbCompData = (long)0; + } else { + packet.unsol = 1; + packet.ack_only = 0; + adb_pass_up(&packet); + } + + + /* reset vars and signal the end of this frame */ + adbActionState = ADB_ACTION_IDLE; + adbInputBuffer[0] = 0; + ADB_SET_STATE_IDLE_CUDA(); + /*ADB_SET_SR_INPUT();*/ + + /* + * If there is something waiting to be sent out, + * the set everything up and send the first byte. + */ + if (adbWriteDelay == 1) { + delay(ADB_DELAY); /* required */ + adbSentChars = 0; + adbActionState = ADB_ACTION_OUT; + /* + * If the interrupt is on, we were too slow + * and the chip has already started to send + * something to us, so back out of the write + * and start a read cycle. + */ + if (ADB_INTR_IS_ON) { + ADB_SET_SR_INPUT(); + ADB_SET_STATE_IDLE_CUDA(); + adbSentChars = 0; + adbActionState = ADB_ACTION_IDLE; + adbInputBuffer[0] = 0; + break; + } + /* + * If we got here, it's ok to start sending + * so load the first byte and tell the chip + * we want to send. + */ + ADB_SET_STATE_TIP(); + ADB_SET_SR_OUTPUT(); + write_via_reg(VIA1, vSR, adbOutputBuffer[adbSentChars + 1]); + } + } else { + ADB_TOGGLE_STATE_ACK_CUDA(); +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("in 0x%02x ", + adbInputBuffer[adbInputBuffer[0]]); +#endif + } + break; + + case ADB_ACTION_OUT: + i = ADB_SR(); /* reset SR-intr in IFR */ +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("intr out 0x%02x ", i); +#endif + + adbSentChars++; + if (ADB_INTR_IS_ON) { /* ADB intr low during write */ +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("intr was on "); +#endif + ADB_SET_SR_INPUT(); /* make sure SR is set to IN */ + ADB_SET_STATE_IDLE_CUDA(); + adbSentChars = 0; /* must start all over */ + adbActionState = ADB_ACTION_IDLE; /* new state */ + adbInputBuffer[0] = 0; + adbWriteDelay = 1; /* must retry when done with + * read */ + delay(ADB_DELAY); + goto switch_start; /* process next state right + * now */ + break; + } + if (adbOutputBuffer[0] == adbSentChars) { /* check for done */ + if (0 == adb_cmd_result(adbOutputBuffer)) { /* do we expect data + * back? */ + adbWaiting = 1; /* signal waiting for return */ + adbWaitingCmd = adbOutputBuffer[2]; /* save waiting command */ + } else { /* no talk, so done */ + /* set up stuff for adb_pass_up */ + for (i = 0; i <= adbInputBuffer[0]; i++) + packet.data[i] = adbInputBuffer[i]; + packet.saveBuf = adbBuffer; + packet.compRout = adbCompRout; + packet.compData = adbCompData; + packet.cmd = adbWaitingCmd; + packet.unsol = 0; + packet.ack_only = 1; + adb_pass_up(&packet); + + /* reset "waiting" vars, just in case */ + adbWaitingCmd = 0; + adbBuffer = (long)0; + adbCompRout = (long)0; + adbCompData = (long)0; + } + + adbWriteDelay = 0; /* done writing */ + adbActionState = ADB_ACTION_IDLE; /* signal bus is idle */ + ADB_SET_SR_INPUT(); + ADB_SET_STATE_IDLE_CUDA(); +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("write done "); +#endif + } else { + write_via_reg(VIA1, vSR, adbOutputBuffer[adbSentChars + 1]); /* send next byte */ + ADB_TOGGLE_STATE_ACK_CUDA(); /* signal byte ready to + * shift */ +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("toggle "); +#endif + } + break; + + case ADB_ACTION_NOTREADY: + printf_intr("adb: not yet initialized\n"); + break; + + default: + printf_intr("intr: unknown ADB state\n"); + } + + ADB_VIA_INTR_ENABLE(); /* enable ADB interrupt on IIs. */ + + splx(s); /* restore */ + + return; +} /* end adb_intr_cuda */ + + +int +send_adb_cuda(u_char * in, u_char * buffer, void *compRout, void *data, int + command) +{ + int i, s, len; + +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("SEND\n"); +#endif + + if (adbActionState == ADB_ACTION_NOTREADY) + return 1; + + /* Don't interrupt while we are messing with the ADB */ + s = splhigh(); + + if ((adbActionState == ADB_ACTION_IDLE) && /* ADB available? */ + (ADB_INTR_IS_OFF)) { /* and no incoming interrupt? */ + } else + if (adbWriteDelay == 0) /* it's busy, but is anything waiting? */ + adbWriteDelay = 1; /* if no, then we'll "queue" + * it up */ + else { + splx(s); + return 1; /* really busy! */ + } + +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("QUEUE\n"); +#endif + if ((long)in == (long)0) { /* need to convert? */ + /* + * Don't need to use adb_cmd_extra here because this section + * will be called ONLY when it is an ADB command (no RTC or + * PRAM) + */ + if ((command & 0x0c) == 0x08) /* copy addl data ONLY if + * doing a listen! */ + len = buffer[0]; /* length of additional data */ + else + len = 0;/* no additional data */ + + adbOutputBuffer[0] = 2 + len; /* dev. type + command + addl. + * data */ + adbOutputBuffer[1] = 0x00; /* mark as an ADB command */ + adbOutputBuffer[2] = (u_char)command; /* load command */ + + for (i = 1; i <= len; i++) /* copy additional output + * data, if any */ + adbOutputBuffer[2 + i] = buffer[i]; + } else + for (i = 0; i <= (in[0] + 1); i++) + adbOutputBuffer[i] = in[i]; + + adbSentChars = 0; /* nothing sent yet */ + adbBuffer = buffer; /* save buffer to know where to save result */ + adbCompRout = compRout; /* save completion routine pointer */ + adbCompData = data; /* save completion routine data pointer */ + adbWaitingCmd = adbOutputBuffer[2]; /* save wait command */ + + if (adbWriteDelay != 1) { /* start command now? */ +#ifdef ADB_DEBUG + if (adb_debug) + printf_intr("out start NOW"); +#endif + delay(ADB_DELAY); + adbActionState = ADB_ACTION_OUT; /* set next state */ + ADB_SET_SR_OUTPUT(); /* set shift register for OUT */ + write_via_reg(VIA1, vSR, adbOutputBuffer[adbSentChars + 1]); /* load byte for output */ + ADB_SET_STATE_ACKOFF_CUDA(); + ADB_SET_STATE_TIP(); /* tell ADB that we want to send */ + } + adbWriteDelay = 1; /* something in the write "queue" */ + + splx(s); + + if ((s & (1 << 18)) || adb_polling) /* XXX were VIA1 interrupts blocked ? */ + /* poll until byte done */ + while ((adbActionState != ADB_ACTION_IDLE) || (ADB_INTR_IS_ON) + || (adbWaiting == 1)) + if (ADB_SR_INTR_IS_ON) { /* wait for "interrupt" */ + adb_intr_cuda(); /* process it */ + adb_soft_intr(); + } + + return 0; +} /* send_adb_cuda */ + + +void +adb_intr_II(void) +{ + panic("adb_intr_II"); +} + + +/* + * send_adb version for II series machines + */ +int +send_adb_II(u_char * in, u_char * buffer, void *compRout, void *data, int command) +{ + panic("send_adb_II"); +} + + +/* + * This routine is called from the II series interrupt routine + * to determine what the "next" device is that should be polled. + */ +int +adb_guess_next_device(void) +{ + int last, i, dummy; + + if (adbStarting) { + /* + * Start polling EVERY device, since we can't be sure there is + * anything in the device table yet + */ + if (adbLastDevice < 1 || adbLastDevice > 15) + adbLastDevice = 1; + if (++adbLastDevice > 15) /* point to next one */ + adbLastDevice = 1; + } else { + /* find the next device using the device table */ + if (adbLastDevice < 1 || adbLastDevice > 15) /* let's be parinoid */ + adbLastDevice = 2; + last = 1; /* default index location */ + + for (i = 1; i < 16; i++) /* find index entry */ + if (ADBDevTable[i].currentAddr == adbLastDevice) { /* look for device */ + last = i; /* found it */ + break; + } + dummy = last; /* index to start at */ + for (;;) { /* find next device in index */ + if (++dummy > 15) /* wrap around if needed */ + dummy = 1; + if (dummy == last) { /* didn't find any other + * device! This can happen if + * there are no devices on the + * bus */ + dummy = 2; + break; + } + /* found the next device */ + if (ADBDevTable[dummy].devType != 0) + break; + } + adbLastDevice = ADBDevTable[dummy].currentAddr; + } + return adbLastDevice; +} + + +/* + * Called when when an adb interrupt happens. + * This routine simply transfers control over to the appropriate + * code for the machine we are running on. + */ +void +adb_intr(void) +{ + switch (adbHardware) { + case ADB_HW_II: + adb_intr_II(); + break; + + case ADB_HW_IISI: + adb_intr_IIsi(); + break; + + case ADB_HW_PB: + break; + + case ADB_HW_CUDA: + adb_intr_cuda(); + break; + + case ADB_HW_UNKNOWN: + break; + } +} + + +/* + * called when when an adb interrupt happens + * + * IIsi version of adb_intr + * + */ +void +adb_intr_IIsi(void) +{ + panic("adb_intr_IIsi"); +} + + +/***************************************************************************** + * if the device is currently busy, and there is no data waiting to go out, then + * the data is "queued" in the outgoing buffer. If we are already waiting, then + * we return. + * in: if (in == 0) then the command string is built from command and buffer + * if (in != 0) then in is used as the command string + * buffer: additional data to be sent (used only if in == 0) + * this is also where return data is stored + * compRout: the completion routine that is called when then return value + * is received (if a return value is expected) + * data: a data pointer that can be used by the completion routine + * command: an ADB command to be sent (used only if in == 0) + * + */ +int +send_adb_IIsi(u_char * in, u_char * buffer, void *compRout, void *data, int + command) +{ + panic("send_adb_IIsi"); +} + + +/* + * adb_pass_up is called by the interrupt-time routines. + * It takes the raw packet data that was received from the + * device and puts it into the queue that the upper half + * processes. It then signals for a soft ADB interrupt which + * will eventually call the upper half routine (adb_soft_intr). + * + * If in->unsol is 0, then this is either the notification + * that the packet was sent (on a LISTEN, for example), or the + * response from the device (on a TALK). The completion routine + * is called only if the user specified one. + * + * If in->unsol is 1, then this packet was unsolicited and + * so we look up the device in the ADB device table to determine + * what it's default service routine is. + * + * If in->ack_only is 1, then we really only need to call + * the completion routine, so don't do any other stuff. + * + * Note that in->data contains the packet header AND data, + * while adbInbound[]->data contains ONLY data. + * + * Note: Called only at interrupt time. Assumes this. + */ +void +adb_pass_up(struct adbCommand *in) +{ + int i, start = 0, len = 0, cmd = 0; + ADBDataBlock block; + + /* temp for testing */ + /*u_char *buffer = 0;*/ + /*u_char *compdata = 0;*/ + /*u_char *comprout = 0;*/ + + if (adbInCount >= ADB_QUEUE) { + printf_intr("adb: ring buffer overflow\n"); + return; + } + + if (in->ack_only) { + len = in->data[0]; + cmd = in->cmd; + start = 0; + } else { + switch (adbHardware) { + case ADB_HW_II: + cmd = in->data[1]; + if (in->data[0] < 2) + len = 0; + else + len = in->data[0]-1; + start = 1; + break; + + case ADB_HW_IISI: + case ADB_HW_CUDA: + /* If it's unsolicited, accept only ADB data for now */ + if (in->unsol) + if (0 != in->data[2]) + return; + cmd = in->data[4]; + if (in->data[0] < 5) + len = 0; + else + len = in->data[0]-4; + start = 4; + break; + + case ADB_HW_PB: + return; /* how does PM handle "unsolicited" messages? */ + + case ADB_HW_UNKNOWN: + return; + } + + /* Make sure there is a valid device entry for this device */ + if (in->unsol) { + /* ignore unsolicited data during adbreinit */ + if (adbStarting) + return; + /* get device's comp. routine and data area */ + if (-1 == get_adb_info(&block, ((cmd & 0xf0) >> 4))) + return; + } + } + + /* + * If this is an unsolicited packet, we need to fill in + * some info so adb_soft_intr can process this packet + * properly. If it's not unsolicited, then use what + * the caller sent us. + */ + if (in->unsol) { + adbInbound[adbInTail].compRout = (void *)block.dbServiceRtPtr; + adbInbound[adbInTail].compData = (void *)block.dbDataAreaAddr; + adbInbound[adbInTail].saveBuf = (void *)adbInbound[adbInTail].data; + } else { + adbInbound[adbInTail].compRout = (void *)in->compRout; + adbInbound[adbInTail].compData = (void *)in->compData; + adbInbound[adbInTail].saveBuf = (void *)in->saveBuf; + } + +#ifdef ADB_DEBUG + if (adb_debug && in->data[1] == 2) + printf_intr("adb: caught error\n"); +#endif + + /* copy the packet data over */ + /* + * TO DO: If the *_intr routines fed their incoming data + * directly into an adbCommand struct, which is passed to + * this routine, then we could eliminate this copy. + */ + for (i = 1; i <= len; i++) + adbInbound[adbInTail].data[i] = in->data[start+i]; + + adbInbound[adbInTail].data[0] = len; + adbInbound[adbInTail].cmd = cmd; + + adbInCount++; + if (++adbInTail >= ADB_QUEUE) + adbInTail = 0; + + /* + * If the debugger is running, call upper half manually. + * Otherwise, trigger a soft interrupt to handle the rest later. + */ + if (adb_polling) + adb_soft_intr(); + else + setsoftadb(); + + return; +} + + +/* + * Called to process the packets after they have been + * placed in the incoming queue. + * + */ +void +adb_soft_intr(void) +{ + int s, i; + int cmd = 0; + u_char *buffer = 0; + u_char *comprout = 0; + u_char *compdata = 0; + +#if 0 + s = splhigh(); + printf_intr("sr: %x\n", (s & 0x0700)); + splx(s); +#endif + +/*delay(2*ADB_DELAY);*/ + + while (adbInCount) { +#ifdef ADB_DEBUG + if (adb_debug & 0x80) + printf_intr("%x %x %x ", + adbInCount, adbInHead, adbInTail); +#endif + /* get the data we need from the queue */ + buffer = adbInbound[adbInHead].saveBuf; + comprout = adbInbound[adbInHead].compRout; + compdata = adbInbound[adbInHead].compData; + cmd = adbInbound[adbInHead].cmd; + + /* copy over data to data area if it's valid */ + /* + * Note that for unsol packets we don't want to copy the + * data anywhere, so buffer was already set to 0. + * For ack_only buffer was set to 0, so don't copy. + */ + if (buffer) + for (i = 0; i <= adbInbound[adbInHead].data[0]; i++) + *(buffer+i) = adbInbound[adbInHead].data[i]; + +#ifdef ADB_DEBUG + if (adb_debug & 0x80) { + printf_intr("%p %p %p %x ", + buffer, comprout, compdata, (short)cmd); + printf_intr("buf: "); + print_single(adbInbound[adbInHead].data); + } +#endif + + /* call default completion routine if it's valid */ + if (comprout) { + int (*f)() = (void *)comprout; + + (*f)(buffer, compdata, cmd); +#if 0 +#ifdef __NetBSD__ + asm(" movml #0xffff,sp@- | save all registers + movl %0,a2 | compdata + movl %1,a1 | comprout + movl %2,a0 | buffer + movl %3,d0 | cmd + jbsr a1@ | go call the routine + movml sp@+,#0xffff | restore all registers" + : + : "g"(compdata), "g"(comprout), + "g"(buffer), "g"(cmd) + : "d0", "a0", "a1", "a2"); +#else /* for macos based testing */ + asm + { + movem.l a0/a1/a2/d0, -(a7) + move.l compdata, a2 + move.l comprout, a1 + move.l buffer, a0 + move.w cmd, d0 + jsr(a1) + movem.l(a7)+, d0/a2/a1/a0 + } +#endif +#endif + } + + s = splhigh(); + adbInCount--; + if (++adbInHead >= ADB_QUEUE) + adbInHead = 0; + splx(s); + + } + return; +} + + +/* + * This is my version of the ADBOp routine. It mainly just calls the + * hardware-specific routine. + * + * data : pointer to data area to be used by compRout + * compRout : completion routine + * buffer : for LISTEN: points to data to send - MAX 8 data bytes, + * byte 0 = # of bytes + * : for TALK: points to place to save return data + * command : the adb command to send + * result : 0 = success + * : -1 = could not complete + */ +int +adb_op(Ptr buffer, Ptr compRout, Ptr data, short command) +{ + int result; + + switch (adbHardware) { + case ADB_HW_II: + result = send_adb_II((u_char *)0, (u_char *)buffer, + (void *)compRout, (void *)data, (int)command); + if (result == 0) + return 0; + else + return -1; + break; + + case ADB_HW_IISI: + result = send_adb_IIsi((u_char *)0, (u_char *)buffer, + (void *)compRout, (void *)data, (int)command); + /* + * I wish I knew why this delay is needed. It usually needs to + * be here when several commands are sent in close succession, + * especially early in device probes when doing collision + * detection. It must be some race condition. Sigh. - jpw + */ + delay(100); + if (result == 0) + return 0; + else + return -1; + break; + +#if 0 + case ADB_HW_PB: + result = pm_adb_op((u_char *)buffer, (void *)compRout, + (void *)data, (int)command); + + if (result == 0) + return 0; + else + return -1; + break; +#endif + + case ADB_HW_CUDA: + result = send_adb_cuda((u_char *)0, (u_char *)buffer, + (void *)compRout, (void *)data, (int)command); + if (result == 0) + return 0; + else + return -1; + break; + + case ADB_HW_UNKNOWN: + default: + return -1; + } +} + + +/* + * adb_hw_setup + * This routine sets up the possible machine specific hardware + * config (mainly VIA settings) for the various models. + */ +void +adb_hw_setup(void) +{ + volatile int i; + u_char send_string[ADB_MAX_MSG_LENGTH]; + + switch (adbHardware) { + case ADB_HW_II: + via_reg(VIA1, vDirB) |= 0x30; /* register B bits 4 and 5: + * outputs */ + via_reg(VIA1, vDirB) &= 0xf7; /* register B bit 3: input */ + via_reg(VIA1, vACR) &= ~vSR_OUT; /* make sure SR is set + * to IN (II, IIsi) */ + adbActionState = ADB_ACTION_IDLE; /* used by all types of + * hardware (II, IIsi) */ + adbBusState = ADB_BUS_IDLE; /* this var. used in II-series + * code only */ + via_reg(VIA1, vIER) = 0x84; /* make sure VIA interrupts + * are on (II, IIsi) */ + ADB_SET_STATE_IDLE_II(); /* set ADB bus state to idle */ + + ADB_VIA_CLR_INTR(); /* clear interrupt */ + break; + + case ADB_HW_IISI: + via_reg(VIA1, vDirB) |= 0x30; /* register B bits 4 and 5: + * outputs */ + via_reg(VIA1, vDirB) &= 0xf7; /* register B bit 3: input */ + via_reg(VIA1, vACR) &= ~vSR_OUT; /* make sure SR is set + * to IN (II, IIsi) */ + adbActionState = ADB_ACTION_IDLE; /* used by all types of + * hardware (II, IIsi) */ + adbBusState = ADB_BUS_IDLE; /* this var. used in II-series + * code only */ + via_reg(VIA1, vIER) = 0x84; /* make sure VIA interrupts + * are on (II, IIsi) */ + ADB_SET_STATE_IDLE_IISI(); /* set ADB bus state to idle */ + + /* get those pesky clock ticks we missed while booting */ + for (i = 0; i < 30; i++) { + delay(ADB_DELAY); + adb_hw_setup_IIsi(send_string); + printf_intr("adb: cleanup: "); + print_single(send_string); + delay(ADB_DELAY); + if (ADB_INTR_IS_OFF) + break; + } + break; + + case ADB_HW_PB: + /* + * XXX - really PM_VIA_CLR_INTR - should we put it in + * pm_direct.h? + */ + via_reg(VIA1, vIFR) = 0x90; /* clear interrupt */ + break; + + case ADB_HW_CUDA: + via_reg_or(VIA1, vDirB, 0x30); /* register B bits 4 and 5: + * outputs */ + via_reg_and(VIA1, vDirB, 0xf7); /* register B bit 3: input */ + via_reg_and(VIA1, vACR, ~vSR_OUT); /* make sure SR is set + * to IN */ + write_via_reg(VIA1, vACR, (read_via_reg(VIA1, vACR) | 0x0c) & ~0x10); + adbActionState = ADB_ACTION_IDLE; /* used by all types of + * hardware */ + adbBusState = ADB_BUS_IDLE; /* this var. used in II-series + * code only */ + write_via_reg(VIA1, vIER, 0x84);/* make sure VIA interrupts + * are on */ + ADB_SET_STATE_IDLE_CUDA(); /* set ADB bus state to idle */ + + /* sort of a device reset */ + i = ADB_SR(); /* clear interrupt */ + ADB_VIA_INTR_DISABLE(); /* no interrupts while clearing */ + ADB_SET_STATE_IDLE_CUDA(); /* reset state to idle */ + delay(ADB_DELAY); + ADB_SET_STATE_TIP(); /* signal start of frame */ + delay(ADB_DELAY); + ADB_TOGGLE_STATE_ACK_CUDA(); + delay(ADB_DELAY); + ADB_CLR_STATE_TIP(); + delay(ADB_DELAY); + ADB_SET_STATE_IDLE_CUDA(); /* back to idle state */ + i = ADB_SR(); /* clear interrupt */ + ADB_VIA_INTR_ENABLE(); /* ints ok now */ + break; + + case ADB_HW_UNKNOWN: + default: + via_reg(VIA1, vIER) = 0x04; /* turn interrupts off - TO + * DO: turn PB ints off? */ + return; + break; + } +} + + +/* + * adb_hw_setup_IIsi + * This is sort of a "read" routine that forces the adb hardware through a read cycle + * if there is something waiting. This helps "clean up" any commands that may have gotten + * stuck or stopped during the boot process. + * + */ +void +adb_hw_setup_IIsi(u_char * buffer) +{ + panic("adb_hw_setup_IIsi"); +} + + + +/* + * adb_reinit sets up the adb stuff + * + */ +void +adb_reinit(void) +{ + u_char send_string[ADB_MAX_MSG_LENGTH]; + int s = 0; + volatile int i, x; + int command; + int result; + int saveptr; /* point to next free relocation address */ + int device; + int nonewtimes; /* times thru loop w/o any new devices */ + ADBDataBlock data; /* temp. holder for getting device info */ + + (void)(&s); /* work around lame GCC bug */ + + /* Make sure we are not interrupted while building the table. */ + if (adbHardware != ADB_HW_PB) /* ints must be on for PB? */ + s = splhigh(); + + ADBNumDevices = 0; /* no devices yet */ + + /* Let intr routines know we are running reinit */ + adbStarting = 1; + + /* + * Initialize the ADB table. For now, we'll always use the same table + * that is defined at the beginning of this file - no mallocs. + */ + for (i = 0; i < 16; i++) + ADBDevTable[i].devType = 0; + + adb_setup_hw_type(); /* setup hardware type */ + + adb_hw_setup(); /* init the VIA bits and hard reset ADB */ + + DELAY(1000); + + /* send an ADB reset first */ + adb_op_sync((Ptr)0, (Ptr)0, (Ptr)0, (short)0x00); + + /* + * Probe for ADB devices. Probe devices 1-15 quickly to determine + * which device addresses are in use and which are free. For each + * address that is in use, move the device at that address to a higher + * free address. Continue doing this at that address until no device + * responds at that address. Then move the last device that was moved + * back to the original address. Do this for the remaining addresses + * that we determined were in use. + * + * When finished, do this entire process over again with the updated + * list of in use addresses. Do this until no new devices have been + * found in 20 passes though the in use address list. (This probably + * seems long and complicated, but it's the best way to detect multiple + * devices at the same address - sometimes it takes a couple of tries + * before the collision is detected.) + */ + + /* initial scan through the devices */ + for (i = 1; i < 16; i++) { + command = (int)(0x0f | ((int)(i & 0x000f) << 4)); /* talk R3 */ + result = adb_op_sync((Ptr)send_string, (Ptr)0, + (Ptr)0, (short)command); + if (0x00 != send_string[0]) { /* anything come back ?? */ + ADBDevTable[++ADBNumDevices].devType = + (u_char)send_string[2]; + ADBDevTable[ADBNumDevices].origAddr = i; + ADBDevTable[ADBNumDevices].currentAddr = i; + ADBDevTable[ADBNumDevices].DataAreaAddr = + (long)0; + ADBDevTable[ADBNumDevices].ServiceRtPtr = (void *)0; + pm_check_adb_devices(i); /* tell pm driver device + * is here */ + } + } + + /* find highest unused address */ + for (saveptr = 15; saveptr > 0; saveptr--) + if (-1 == get_adb_info(&data, saveptr)) + break; + + if (saveptr == 0) /* no free addresses??? */ + saveptr = 15; + +#ifdef ADB_DEBUG + if (adb_debug & 0x80) { + printf_intr("first free is: 0x%02x\n", saveptr); + printf_intr("devices: %i\n", ADBNumDevices); + } +#endif + + nonewtimes = 0; /* no loops w/o new devices */ + while (nonewtimes++ < 11) { + for (i = 1; i <= ADBNumDevices; i++) { + device = ADBDevTable[i].currentAddr; +#ifdef ADB_DEBUG + if (adb_debug & 0x80) + printf_intr("moving device 0x%02x to 0x%02x " + "(index 0x%02x) ", device, saveptr, i); +#endif + + /* send TALK R3 to address */ + command = (int)(0x0f | ((int)(device & 0x000f) << 4)); + adb_op_sync((Ptr)send_string, (Ptr)0, + (Ptr)0, (short)command); + + /* move device to higher address */ + command = (int)(0x0b | ((int)(device & 0x000f) << 4)); + send_string[0] = 2; + send_string[1] = (u_char)(saveptr | 0x60); + send_string[2] = 0xfe; + adb_op_sync((Ptr)send_string, (Ptr)0, + (Ptr)0, (short)command); + + /* send TALK R3 - anything at old address? */ + command = (int)(0x0f | ((int)(device & 0x000f) << 4)); + result = adb_op_sync((Ptr)send_string, (Ptr)0, + (Ptr)0, (short)command); + if (send_string[0] != 0) { + /* new device found */ + /* update data for previously moved device */ + ADBDevTable[i].currentAddr = saveptr; +#ifdef ADB_DEBUG + if (adb_debug & 0x80) + printf_intr("old device at index %i\n",i); +#endif + /* add new device in table */ +#ifdef ADB_DEBUG + if (adb_debug & 0x80) + printf_intr("new device found\n"); +#endif + ADBDevTable[++ADBNumDevices].devType = + (u_char)send_string[2]; + ADBDevTable[ADBNumDevices].origAddr = device; + ADBDevTable[ADBNumDevices].currentAddr = device; + /* These will be set correctly in adbsys.c */ + /* Until then, unsol. data will be ignored. */ + ADBDevTable[ADBNumDevices].DataAreaAddr = + (long)0; + ADBDevTable[ADBNumDevices].ServiceRtPtr = + (void *)0; + /* find next unused address */ + for (x = saveptr; x > 0; x--) + if (-1 == get_adb_info(&data, x)) { + saveptr = x; + break; + } +#ifdef ADB_DEBUG + if (adb_debug & 0x80) + printf_intr("new free is 0x%02x\n", + saveptr); +#endif + nonewtimes = 0; + /* tell pm driver device is here */ + pm_check_adb_devices(device); + } else { +#ifdef ADB_DEBUG + if (adb_debug & 0x80) + printf_intr("moving back...\n"); +#endif + /* move old device back */ + command = (int)(0x0b | ((int)(saveptr & 0x000f) << 4)); + send_string[0] = 2; + send_string[1] = (u_char)(device | 0x60); + send_string[2] = 0xfe; + adb_op_sync((Ptr)send_string, (Ptr)0, + (Ptr)0, (short)command); + } + } + } + +#ifdef ADB_DEBUG + if (adb_debug) { + for (i = 1; i <= ADBNumDevices; i++) { + x = get_ind_adb_info(&data, i); + if (x != -1) + printf_intr("index 0x%x, addr 0x%x, type 0x%x\n", + i, x, data.devType); + } + } +#endif + + /* enable the programmer's switch, if we have one */ + adb_prog_switch_enable(); + + if (0 == ADBNumDevices) /* tell user if no devices found */ + printf_intr("adb: no devices found\n"); + + adbStarting = 0; /* not starting anymore */ +#ifdef ADB_DEBUG + printf_intr("adb: ADBReInit complete\n"); +#endif + + if (adbHardware == ADB_HW_CUDA) + timeout((void *)adb_cuda_tickle, 0, ADB_TICKLE_TICKS); + + if (adbHardware != ADB_HW_PB) /* ints must be on for PB? */ + splx(s); + return; +} + + +#if 0 +/* + * adb_comp_exec + * This is a general routine that calls the completion routine if there is one. + * NOTE: This routine is now only used by pm_direct.c + * All the code in this file (adb_direct.c) uses + * the adb_pass_up routine now. + */ +void +adb_comp_exec(void) +{ + if ((long)0 != adbCompRout) /* don't call if empty return location */ +#ifdef __NetBSD__ + asm(" movml #0xffff,sp@- | save all registers + movl %0,a2 | adbCompData + movl %1,a1 | adbCompRout + movl %2,a0 | adbBuffer + movl %3,d0 | adbWaitingCmd + jbsr a1@ | go call the routine + movml sp@+,#0xffff | restore all registers" + : + : "g"(adbCompData), "g"(adbCompRout), + "g"(adbBuffer), "g"(adbWaitingCmd) + : "d0", "a0", "a1", "a2"); +#else /* for Mac OS-based testing */ + asm { + movem.l a0/a1/a2/d0, -(a7) + move.l adbCompData, a2 + move.l adbCompRout, a1 + move.l adbBuffer, a0 + move.w adbWaitingCmd, d0 + jsr(a1) + movem.l(a7) +, d0/a2/a1/a0 + } +#endif +} +#endif + + +/* + * adb_cmd_result + * + * This routine lets the caller know whether the specified adb command string + * should expect a returned result, such as a TALK command. + * + * returns: 0 if a result should be expected + * 1 if a result should NOT be expected + */ +int +adb_cmd_result(u_char *in) +{ + switch (adbHardware) { + case ADB_HW_II: + /* was it an ADB talk command? */ + if ((in[1] & 0x0c) == 0x0c) + return 0; + return 1; + + case ADB_HW_IISI: + case ADB_HW_CUDA: + /* was it an ADB talk command? */ + if ((in[1] == 0x00) && ((in[2] & 0x0c) == 0x0c)) + return 0; + /* was it an RTC/PRAM read date/time? */ + if ((in[1] == 0x01) && (in[2] == 0x03)) + return 0; + return 1; + + case ADB_HW_PB: + return 1; + + case ADB_HW_UNKNOWN: + default: + return 1; + } +} + + +/* + * adb_cmd_extra + * + * This routine lets the caller know whether the specified adb command string + * may have extra data appended to the end of it, such as a LISTEN command. + * + * returns: 0 if extra data is allowed + * 1 if extra data is NOT allowed + */ +int +adb_cmd_extra(u_char *in) +{ + switch (adbHardware) { + case ADB_HW_II: + if ((in[1] & 0x0c) == 0x08) /* was it a listen command? */ + return 0; + return 1; + + case ADB_HW_IISI: + case ADB_HW_CUDA: + /* + * TO DO: support needs to be added to recognize RTC and PRAM + * commands + */ + if ((in[2] & 0x0c) == 0x08) /* was it a listen command? */ + return 0; + /* add others later */ + return 1; + + case ADB_HW_PB: + return 1; + + case ADB_HW_UNKNOWN: + default: + return 1; + } +} + + +/* + * adb_op_sync + * + * This routine does exactly what the adb_op routine does, except that after + * the adb_op is called, it waits until the return value is present before + * returning. + * + * NOTE: The user specified compRout is ignored, since this routine specifies + * it's own to adb_op, which is why you really called this in the first place + * anyway. + */ +int +adb_op_sync(Ptr buffer, Ptr compRout, Ptr data, short command) +{ + int result; + volatile int flag = 0; + + result = adb_op(buffer, (void *)adb_op_comprout, + (void *)&flag, command); /* send command */ + if (result == 0) /* send ok? */ + while (0 == flag) + /* wait for compl. routine */; + + return result; +} + + +/* + * adb_op_comprout + * + * This function is used by the adb_op_sync routine so it knows when the + * function is done. + */ +void +adb_op_comprout(buffer, compdata, cmd) + caddr_t buffer, compdata; + int cmd; +{ + short *p = (short *)compdata; + + *p = 1; +} + +void +adb_setup_hw_type(void) +{ + long response; + + adbHardware = ADB_HW_CUDA; + return; + + response = 0; /*mac68k_machine.machineid;*/ + + /* + * Determine what type of ADB hardware we are running on. + */ + switch (response) { + case 6: /* II */ + case 7: /* IIx */ + case 8: /* IIcx */ + case 9: /* SE/30 */ + case 11: /* IIci */ + case 22: /* Quadra 700 */ + case 30: /* Centris 650 */ + case 35: /* Quadra 800 */ + case 36: /* Quadra 650 */ + case 52: /* Centris 610 */ + case 53: /* Quadra 610 */ + adbHardware = ADB_HW_II; + printf_intr("adb: using II series hardware support\n"); + break; + case 18: /* IIsi */ + case 20: /* Quadra 900 - not sure if IIsi or not */ + case 23: /* Classic II */ + case 26: /* Quadra 950 - not sure if IIsi or not */ + case 27: /* LC III, Performa 450 */ + case 37: /* LC II, Performa 400/405/430 */ + case 44: /* IIvi */ + case 45: /* Performa 600 */ + case 48: /* IIvx */ + case 62: /* Performa 460/465/467 */ + adbHardware = ADB_HW_IISI; + printf_intr("adb: using IIsi series hardware support\n"); + break; + case 21: /* PowerBook 170 */ + case 25: /* PowerBook 140 */ + case 54: /* PowerBook 145 */ + case 34: /* PowerBook 160 */ + case 84: /* PowerBook 165 */ + case 50: /* PowerBook 165c */ + case 33: /* PowerBook 180 */ + case 71: /* PowerBook 180c */ + case 115: /* PowerBook 150 */ + adbHardware = ADB_HW_PB; + pm_setup_adb(); + printf_intr("adb: using PowerBook 100-series hardware support\n"); + break; + case 29: /* PowerBook Duo 210 */ + case 32: /* PowerBook Duo 230 */ + case 38: /* PowerBook Duo 250 */ + case 72: /* PowerBook 500 series */ + case 77: /* PowerBook Duo 270 */ + case 102: /* PowerBook Duo 280 */ + case 103: /* PowerBook Duo 280c */ + adbHardware = ADB_HW_PB; + pm_setup_adb(); + printf_intr("adb: using PowerBook Duo-series and PowerBook 500-series hardware support\n"); + break; + case 49: /* Color Classic */ + case 56: /* LC 520 */ + case 60: /* Centris 660AV */ + case 78: /* Quadra 840AV */ + case 80: /* LC 550, Performa 550 */ + case 83: /* Color Classic II */ + case 89: /* LC 475, Performa 475/476 */ + case 92: /* LC 575, Performa 575/577/578 */ + case 94: /* Quadra 605 */ + case 98: /* LC 630, Performa 630, Quadra 630 */ + case 99: /* Performa 580(?)/588 */ + adbHardware = ADB_HW_CUDA; + printf_intr("adb: using Cuda series hardware support\n"); + break; + default: + adbHardware = ADB_HW_UNKNOWN; + printf_intr("adb: hardware type unknown for this machine\n"); + printf_intr("adb: ADB support is disabled\n"); + break; + } + + /* + * Determine whether this machine has ADB based soft power. + */ + switch (response) { + case 18: /* IIsi */ + case 20: /* Quadra 900 - not sure if IIsi or not */ + case 26: /* Quadra 950 - not sure if IIsi or not */ + case 44: /* IIvi */ + case 45: /* Performa 600 */ + case 48: /* IIvx */ + case 49: /* Color Classic */ + case 83: /* Color Classic II */ + case 56: /* LC 520 */ + case 78: /* Quadra 840AV */ + case 80: /* LC 550, Performa 550 */ + case 92: /* LC 575, Performa 575/577/578 */ + case 98: /* LC 630, Performa 630, Quadra 630 */ + adbSoftPower = 1; + break; + } +} + +int +count_adbs(void) +{ + int i; + int found; + + found = 0; + + for (i = 1; i < 16; i++) + if (0 != ADBDevTable[i].devType) + found++; + + return found; +} + +int +get_ind_adb_info(ADBDataBlock * info, int index) +{ + if ((index < 1) || (index > 15)) /* check range 1-15 */ + return (-1); + +#ifdef ADB_DEBUG + if (adb_debug & 0x80) + printf_intr("index 0x%x devType is: 0x%x\n", index, + ADBDevTable[index].devType); +#endif + if (0 == ADBDevTable[index].devType) /* make sure it's a valid entry */ + return (-1); + + info->devType = ADBDevTable[index].devType; + info->origADBAddr = ADBDevTable[index].origAddr; + info->dbServiceRtPtr = (Ptr)ADBDevTable[index].ServiceRtPtr; + info->dbDataAreaAddr = (Ptr)ADBDevTable[index].DataAreaAddr; + + return (ADBDevTable[index].currentAddr); +} + +int +get_adb_info(ADBDataBlock * info, int adbAddr) +{ + int i; + + if ((adbAddr < 1) || (adbAddr > 15)) /* check range 1-15 */ + return (-1); + + for (i = 1; i < 15; i++) + if (ADBDevTable[i].currentAddr == adbAddr) { + info->devType = ADBDevTable[i].devType; + info->origADBAddr = ADBDevTable[i].origAddr; + info->dbServiceRtPtr = (Ptr)ADBDevTable[i].ServiceRtPtr; + info->dbDataAreaAddr = ADBDevTable[i].DataAreaAddr; + return 0; /* found */ + } + + return (-1); /* not found */ +} + +int +set_adb_info(ADBSetInfoBlock * info, int adbAddr) +{ + int i; + + if ((adbAddr < 1) || (adbAddr > 15)) /* check range 1-15 */ + return (-1); + + for (i = 1; i < 15; i++) + if (ADBDevTable[i].currentAddr == adbAddr) { + ADBDevTable[i].ServiceRtPtr = + (void *)(info->siServiceRtPtr); + ADBDevTable[i].DataAreaAddr = info->siDataAreaAddr; + return 0; /* found */ + } + + return (-1); /* not found */ + +} + +/* caller should really use machine-independant version: getPramTime */ +/* this version does pseudo-adb access only */ +int +adb_read_date_time(unsigned long *time) +{ + u_char output[ADB_MAX_MSG_LENGTH]; + int result; + volatile int flag = 0; + + switch (adbHardware) { + case ADB_HW_II: + return -1; + + case ADB_HW_IISI: + output[0] = 0x02; /* 2 byte message */ + output[1] = 0x01; /* to pram/rtc device */ + output[2] = 0x03; /* read date/time */ + result = send_adb_IIsi((u_char *)output, (u_char *)output, + (void *)adb_op_comprout, (int *)&flag, (int)0); + if (result != 0) /* exit if not sent */ + return -1; + + while (0 == flag) /* wait for result */ + ; + + *time = (long)(*(long *)(output + 1)); + return 0; + + case ADB_HW_PB: + return -1; + + case ADB_HW_CUDA: + output[0] = 0x02; /* 2 byte message */ + output[1] = 0x01; /* to pram/rtc device */ + output[2] = 0x03; /* read date/time */ + result = send_adb_cuda((u_char *)output, (u_char *)output, + (void *)adb_op_comprout, (void *)&flag, (int)0); + if (result != 0) /* exit if not sent */ + return -1; + + while (0 == flag) /* wait for result */ + ; + + /* *time = (long)(*(long *)(output + 1)) - 2082844800; */ + bcopy(output + 1, time, 4); + *time -= 2082844800; + return 0; + + case ADB_HW_UNKNOWN: + default: + return -1; + } +} + +/* caller should really use machine-independant version: setPramTime */ +/* this version does pseudo-adb access only */ +int +adb_set_date_time(unsigned long time) +{ + u_char output[ADB_MAX_MSG_LENGTH]; + int result; + volatile int flag = 0; + + time += 2082844800; + + switch (adbHardware) { + + case ADB_HW_CUDA: + output[0] = 0x06; /* 6 byte message */ + output[1] = 0x01; /* to pram/rtc device */ + output[2] = 0x09; /* set date/time */ + output[3] = (u_char)(time >> 24); + output[4] = (u_char)(time >> 16); + output[5] = (u_char)(time >> 8); + output[6] = (u_char)(time); + result = send_adb_cuda((u_char *)output, (u_char *)0, + (void *)adb_op_comprout, (void *)&flag, (int)0); + if (result != 0) /* exit if not sent */ + return -1; + + while (0 == flag) /* wait for send to finish */ + ; + + return 0; + + case ADB_HW_II: + case ADB_HW_IISI: + case ADB_HW_PB: + case ADB_HW_UNKNOWN: + default: + return -1; + } +} + + +int +adb_poweroff(void) +{ + u_char output[ADB_MAX_MSG_LENGTH]; + int result; + + if (!adbSoftPower) + return -1; + + switch (adbHardware) { + case ADB_HW_IISI: + output[0] = 0x02; /* 2 byte message */ + output[1] = 0x01; /* to pram/rtc/soft-power device */ + output[2] = 0x0a; /* set date/time */ + result = send_adb_IIsi((u_char *)output, (u_char *)0, + (void *)0, (void *)0, (int)0); + if (result != 0) /* exit if not sent */ + return -1; + + for (;;); /* wait for power off */ + + return 0; + + case ADB_HW_PB: + return -1; + + case ADB_HW_CUDA: + output[0] = 0x02; /* 2 byte message */ + output[1] = 0x01; /* to pram/rtc/soft-power device */ + output[2] = 0x0a; /* set date/time */ + result = send_adb_cuda((u_char *)output, (u_char *)0, + (void *)0, (void *)0, (int)0); + if (result != 0) /* exit if not sent */ + return -1; + + for (;;); /* wait for power off */ + + return 0; + + case ADB_HW_II: /* II models don't do ADB soft power */ + case ADB_HW_UNKNOWN: + default: + return -1; + } +} + +int +adb_prog_switch_enable(void) +{ + u_char output[ADB_MAX_MSG_LENGTH]; + int result; + volatile int flag = 0; + + switch (adbHardware) { + case ADB_HW_IISI: + output[0] = 0x03; /* 3 byte message */ + output[1] = 0x01; /* to pram/rtc/soft-power device */ + output[2] = 0x1c; /* prog. switch control */ + output[3] = 0x01; /* enable */ + result = send_adb_IIsi((u_char *)output, (u_char *)0, + (void *)adb_op_comprout, (void *)&flag, (int)0); + if (result != 0) /* exit if not sent */ + return -1; + + while (0 == flag) /* wait for send to finish */ + ; + + return 0; + + case ADB_HW_PB: + return -1; + + case ADB_HW_II: /* II models don't do prog. switch */ + case ADB_HW_CUDA: /* cuda doesn't do prog. switch TO DO: verify this */ + case ADB_HW_UNKNOWN: + default: + return -1; + } +} + +int +adb_prog_switch_disable(void) +{ + u_char output[ADB_MAX_MSG_LENGTH]; + int result; + volatile int flag = 0; + + switch (adbHardware) { + case ADB_HW_IISI: + output[0] = 0x03; /* 3 byte message */ + output[1] = 0x01; /* to pram/rtc/soft-power device */ + output[2] = 0x1c; /* prog. switch control */ + output[3] = 0x01; /* disable */ + result = send_adb_IIsi((u_char *)output, (u_char *)0, + (void *)adb_op_comprout, (void *)&flag, (int)0); + if (result != 0) /* exit if not sent */ + return -1; + + while (0 == flag) /* wait for send to finish */ + ; + + return 0; + + case ADB_HW_PB: + return -1; + + case ADB_HW_II: /* II models don't do prog. switch */ + case ADB_HW_CUDA: /* cuda doesn't do prog. switch */ + case ADB_HW_UNKNOWN: + default: + return -1; + } +} + +#ifndef MRG_ADB + +int +CountADBs(void) +{ + return (count_adbs()); +} + +void +ADBReInit(void) +{ + adb_reinit(); +} + +int +GetIndADB(ADBDataBlock * info, int index) +{ + return (get_ind_adb_info(info, index)); +} + +int +GetADBInfo(ADBDataBlock * info, int adbAddr) +{ + return (get_adb_info(info, adbAddr)); +} + +int +SetADBInfo(ADBSetInfoBlock * info, int adbAddr) +{ + return (set_adb_info(info, adbAddr)); +} + +int +ADBOp(Ptr buffer, Ptr compRout, Ptr data, short commandNum) +{ + return (adb_op(buffer, compRout, data, commandNum)); +} + +#endif + + + +void +pm_check_adb_devices(x) + int x; +{ +} + +int +setsoftadb() +{ + timeout((void *)adb_soft_intr, NULL, 1); + return 0; +} + +void +kbd_init() +{ + volatile int flag = 0; + int result; + u_char output[16]; + extern void adb_op_comprout(); + + output[0] = 0x03; /* 3-byte message */ + output[1] = 0x01; /* to pram/rtc device */ + output[2] = 0x01; /* cuda autopoll */ + output[3] = 0x01; + result = send_adb_cuda(output, output, adb_op_comprout, + (void *)&flag, 0); + if (result != 0) /* exit if not sent */ + return; + + while (flag == 0); /* wait for result */ +} + +void +powermac_restart() +{ + volatile int flag = 0; + int result; + u_char output[16]; + + output[0] = 0x02; /* 2 byte message */ + output[1] = 0x01; /* to pram/rtc/soft-power device */ + output[2] = 0x11; /* restart */ + result = send_adb_cuda((u_char *)output, (u_char *)0, + (void *)0, (void *)0, (int)0); + if (result != 0) /* exit if not sent */ + return; + + while (1); /* not return */ +} diff --git a/sys/arch/macppc/dev/adb_direct.h b/sys/arch/macppc/dev/adb_direct.h new file mode 100644 index 00000000000..76aff051d1b --- /dev/null +++ b/sys/arch/macppc/dev/adb_direct.h @@ -0,0 +1,53 @@ +/* $NetBSD: adb_direct.h,v 1.1 1998/05/15 10:15:47 tsubai Exp $ */ + +/* + * Copyright (C) 1996 John P. Wittkoski + * 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 John P. Wittkoski. + * 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. + */ +/* From: adb_direct.h 1.4 10/23/96 jpw */ + +/* + * These are public declarations that other routines may need. + */ + +/* types of adb hardware that we (will eventually) support */ +#define ADB_HW_UNKNOWN 0x01 /* don't know */ +#define ADB_HW_II 0x02 /* Mac II series */ +#define ADB_HW_IISI 0x03 /* Mac IIsi series */ +#define ADB_HW_PB 0x04 /* PowerBook series */ +#define ADB_HW_CUDA 0x05 /* Machines with a Cuda chip */ + +int adb_poweroff __P((void)); +int CountADBs __P((void)); +void ADBReInit __P((void)); +int GetIndADB __P((ADBDataBlock *info, int index)); +int GetADBInfo __P((ADBDataBlock *info, int adbAddr)); +int SetADBInfo __P((ADBSetInfoBlock *info, int adbAddr)); +int ADBOp __P((Ptr buffer, Ptr compRout, Ptr data, short commandNum)); +int adb_read_date_time __P((unsigned long *)); +int adb_set_date_time __P((unsigned long)); diff --git a/sys/arch/macppc/dev/adbsys.c b/sys/arch/macppc/dev/adbsys.c new file mode 100644 index 00000000000..4d78551faa8 --- /dev/null +++ b/sys/arch/macppc/dev/adbsys.c @@ -0,0 +1,632 @@ +/* $NetBSD: adbsys.c,v 1.1 1998/05/15 10:15:47 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 <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/cpu.h> + +#include <macppc/dev/viareg.h> +#include <macppc/dev/adbvar.h> + +/* from adb.c */ +void adb_processevent __P((adb_event_t * event)); + +extern void adb_jadbproc __P((void)); + +void +adb_complete(buffer, data_area, adb_command) + caddr_t buffer; + caddr_t data_area; + int adb_command; +{ + adb_event_t event; + ADBDataBlock adbdata; + int adbaddr; + int error; +#ifdef ADB_DEBUG + int i; + + if (adb_debug) + printf("adb: transaction completion\n"); +#endif + + adbaddr = (adb_command & 0xf0) >> 4; + error = GetADBInfo(&adbdata, adbaddr); +#ifdef ADB_DEBUG + if (adb_debug) + printf("adb: GetADBInfo returned %d\n", error); +#endif + + event.addr = adbaddr; + event.hand_id = adbdata.devType; + event.def_addr = adbdata.origADBAddr; + event.byte_count = buffer[0]; + memcpy(event.bytes, buffer + 1, event.byte_count); + +#ifdef ADB_DEBUG + if (adb_debug) { + printf("adb: from %d at %d (org %d) %d:", event.addr, + event.hand_id, event.def_addr, buffer[0]); + for (i = 1; i <= buffer[0]; i++) + printf(" %x", buffer[i]); + printf("\n"); + } +#endif + + microtime(&event.timestamp); + + adb_processevent(&event); +} + +void +adb_msa3_complete(buffer, data_area, adb_command) + caddr_t buffer; + caddr_t data_area; + int adb_command; +{ + adb_event_t event; + ADBDataBlock adbdata; + int adbaddr; + int error; +#ifdef ADB_DEBUG + int i; + + if (adb_debug) + printf("adb: transaction completion\n"); +#endif + + adbaddr = (adb_command & 0xf0) >> 4; + error = GetADBInfo(&adbdata, adbaddr); +#ifdef ADB_DEBUG + if (adb_debug) + printf("adb: GetADBInfo returned %d\n", error); +#endif + + event.addr = adbaddr; + event.hand_id = ADBMS_MSA3; + event.def_addr = adbdata.origADBAddr; + event.byte_count = buffer[0]; + memcpy(event.bytes, buffer + 1, event.byte_count); + +#ifdef ADB_DEBUG + if (adb_debug) { + printf("adb: from %d at %d (org %d) %d:", + event.addr, event.hand_id, event.def_addr, buffer[0]); + for (i = 1; i <= buffer[0]; i++) + printf(" %x", buffer[i]); + printf("\n"); + } +#endif + + microtime(&event.timestamp); + + adb_processevent(&event); +} + +void +adb_mm_nonemp_complete(buffer, data_area, adb_command) + caddr_t buffer; + caddr_t data_area; + int adb_command; +{ + adb_event_t event; + ADBDataBlock adbdata; + int adbaddr; + int error; + +#ifdef ADB_DEBUG + int i; + + if (adb_debug) + printf("adb: transaction completion\n"); +#endif + + adbaddr = (adb_command & 0xf0) >> 4; + error = GetADBInfo(&adbdata, adbaddr); +#ifdef ADB_DEBUG + if (adb_debug) + printf("adb: GetADBInfo returned %d\n", error); +#endif + +#if 0 + printf("adb: from %d at %d (org %d) %d:", event.addr, + event.hand_id, event.def_addr, buffer[0]); + for (i = 1; i <= buffer[0]; i++) + printf(" %x", buffer[i]); + printf("\n"); +#endif + + /* massage the data to look like EMP data */ + if ((buffer[3] & 0x04) == 0x04) + buffer[1] &= 0x7f; + else + buffer[1] |= 0x80; + if ((buffer[3] & 0x02) == 0x02) + buffer[2] &= 0x7f; + else + buffer[2] |= 0x80; + if ((buffer[3] & 0x01) == 0x01) + buffer[3] = 0x00; + else + buffer[3] = 0x80; + + event.addr = adbaddr; + event.hand_id = adbdata.devType; + event.def_addr = adbdata.origADBAddr; + event.byte_count = buffer[0]; + memcpy(event.bytes, buffer + 1, event.byte_count); + +#ifdef ADB_DEBUG + if (adb_debug) { + printf("adb: from %d at %d (org %d) %d:", event.addr, + event.hand_id, event.def_addr, buffer[0]); + for (i = 1; i <= buffer[0]; i++) + printf(" %x", buffer[i]); + printf("\n"); + } +#endif + + microtime(&event.timestamp); + + adb_processevent(&event); +} + +static volatile int extdms_done; + +/* + * initialize extended mouse - probes devices as + * described in _Inside Macintosh, Devices_. + */ +void +extdms_init(totaladbs) + int totaladbs; +{ + ADBDataBlock adbdata; + int adbindex, adbaddr, count; + short cmd; + u_char buffer[9]; + + for (adbindex = 1; adbindex <= totaladbs; adbindex++) { + /* Get the ADB information */ + adbaddr = GetIndADB(&adbdata, adbindex); + if (adbdata.origADBAddr == ADBADDR_MS && + (adbdata.devType == ADBMS_USPEED)) { + /* Found MicroSpeed Mouse Deluxe Mac */ + cmd = ((adbaddr<<4)&0xF0)|0x9; /* listen 1 */ + + /* + * To setup the MicroSpeed, it appears that we can + * send the following command to the mouse and then + * expect data back in the form: + * buffer[0] = 4 (bytes) + * buffer[1], buffer[2] as std. mouse + * buffer[3] = buffer[4] = 0xff when no buttons + * are down. When button N down, bit N is clear. + * buffer[4]'s locking mask enables a + * click to toggle the button down state--sort of + * like the "Easy Access" shift/control/etc. keys. + * buffer[3]'s alternative speed mask enables using + * different speed when the corr. button is down + */ + buffer[0] = 4; + buffer[1] = 0x00; /* Alternative speed */ + buffer[2] = 0x00; /* speed = maximum */ + buffer[3] = 0x10; /* enable extended protocol, + * lower bits = alt. speed mask + * = 0000b + */ + buffer[4] = 0x07; /* Locking mask = 0000b, + * enable buttons = 0111b + */ + extdms_done = 0; + ADBOp((Ptr)buffer, (Ptr)extdms_complete, + (Ptr)&extdms_done, cmd); + while (!extdms_done) + /* busy wait until done */; + } + if (adbdata.origADBAddr == ADBADDR_MS && + (adbdata.devType == ADBMS_100DPI || + adbdata.devType == ADBMS_200DPI)) { + /* found a mouse */ + cmd = ((adbaddr << 4) & 0xf0) | 0x3; + + extdms_done = 0; + cmd = (cmd & 0xf3) | 0x0c; /* talk command */ + ADBOp((Ptr)buffer, (Ptr)extdms_complete, + (Ptr)&extdms_done, cmd); + + /* Wait until done, but no more than 2 secs */ + count = 40000; + while (!extdms_done && count-- > 0) + delay(50); + + if (!extdms_done) { +#ifdef ADB_DEBUG + if (adb_debug) + printf("adb: extdms_init timed out\n"); +#endif + return; + } + + DELAY(1000); + + /* Attempt to initialize Extended Mouse Protocol */ + buffer[2] = '\004'; /* make handler ID 4 */ + extdms_done = 0; + cmd = (cmd & 0xf3) | 0x08; /* listen command */ + ADBOp((Ptr)buffer, (Ptr)extdms_complete, + (Ptr)&extdms_done, cmd); + while (!extdms_done) + /* busy wait until done */; + + /* + * Check to see if successful, if not + * try to initialize it as other types + */ + cmd = ((adbaddr << 4) & 0xf0) | 0x3; + extdms_done = 0; + cmd = (cmd & 0xf3) | 0x0c; /* talk command */ + ADBOp((Ptr)buffer, (Ptr)extdms_complete, + (Ptr)&extdms_done, cmd); + while (!extdms_done) + /* busy wait until done */; + + if (buffer[2] != ADBMS_EXTENDED) { + /* Attempt to initialize as an A3 mouse */ + buffer[2] = 0x03; /* make handler ID 3 */ + extdms_done = 0; + cmd = (cmd & 0xf3) | 0x08; /* listen command */ + ADBOp((Ptr)buffer, (Ptr)extdms_complete, + (Ptr)&extdms_done, cmd); + while (!extdms_done) + /* busy wait until done */; + + /* + * Check to see if successful, if not + * try to initialize it as other types + */ + cmd = ((adbaddr << 4) & 0xf0) | 0x3; + extdms_done = 0; + cmd = (cmd & 0xf3) | 0x0c; /* talk command */ + ADBOp((Ptr)buffer, (Ptr)extdms_complete, + (Ptr)&extdms_done, cmd); + while (!extdms_done) + /* busy wait until done */; + + if (buffer[2] == ADBMS_MSA3) { + /* Initialize as above */ + cmd = ((adbaddr << 4) & 0xF0) | 0xA; + /* listen 2 */ + buffer[0] = 3; + buffer[1] = 0x00; + /* Irrelevant, buffer has 0x77 */ + buffer[2] = 0x07; + /* + * enable 3 button mode = 0111b, + * speed = normal + */ + extdms_done = 0; + ADBOp((Ptr)buffer, (Ptr)extdms_complete, + (Ptr)&extdms_done, cmd); + while (!extdms_done) + /* busy wait until done */; + } else { + /* No special support for this mouse */ + } + } + } + } +} + +void +adb_init() +{ + ADBDataBlock adbdata; + ADBSetInfoBlock adbinfo; + int totaladbs; + int adbindex, adbaddr; + int error, cmd, count, devtype = 0; + u_char buffer[9]; + extern int adb_initted; + + ADBReInit(); + +#ifdef ADB_DEBUG + if (adb_debug) + printf("adb: done with ADBReInit\n"); +#endif + + totaladbs = CountADBs(); + extdms_init(totaladbs); + + /* for each ADB device */ + for (adbindex = 1; adbindex <= totaladbs; adbindex++) { + /* Get the ADB information */ + adbaddr = GetIndADB(&adbdata, adbindex); + + /* Print out the glory */ + printf("adb: "); + switch (adbdata.origADBAddr) { + case ADBADDR_SECURE: + printf("security dongle (%d)", adbdata.devType); + break; + case ADBADDR_MAP: + switch (adbdata.devType) { + case ADB_STDKBD: + printf("standard keyboard"); + break; + case ADB_ISOKBD: + printf("standard keyboard (ISO layout)"); + break; + case ADB_EXTKBD: + extdms_done = 0; + /* talk R1 */ + cmd = (((adbaddr << 4) & 0xf0) | 0x0d ); + ADBOp((Ptr)buffer, (Ptr)extdms_complete, + (Ptr)&extdms_done, cmd); + + /* Wait until done, but no more than 2 secs */ + count = 40000; + while (!extdms_done && count-- > 0) + delay(50); + + if (extdms_done && + buffer[1] == 0x9a && buffer[2] == 0x20) + printf("Mouseman (non-EMP) pseudo keyboard"); + else + printf("extended keyboard"); + break; + case ADB_EXTISOKBD: + printf("extended keyboard (ISO layout)"); + break; + case ADB_KBDII: + printf("keyboard II"); + break; + case ADB_ISOKBDII: + printf("keyboard II (ISO layout)"); + break; + case ADB_PBKBD: + printf("PowerBook keyboard"); + break; + case ADB_PBISOKBD: + printf("PowerBook keyboard (ISO layout)"); + break; + case ADB_ADJKPD: + printf("adjustable keypad"); + break; + case ADB_ADJKBD: + printf("adjustable keyboard"); + break; + case ADB_ADJISOKBD: + printf("adjustable keyboard (ISO layout)"); + break; + case ADB_ADJJAPKBD: + printf("adjustable keyboard (Japanese layout)"); + break; + case ADB_PBEXTISOKBD: + printf("PowerBook extended keyboard (ISO layout)"); + break; + case ADB_PBEXTJAPKBD: + printf("PowerBook extended keyboard (Japanese layout)"); + break; + case ADB_JISKBDII: + printf("keyboard II (JIS)"); + break; + case ADB_PBEXTKBD: + printf("PowerBook extended keyboard"); + break; + case ADB_DESIGNKBD: + printf("extended keyboard"); + break; + default: + printf("mapped device (%d)", adbdata.devType); + break; + } + break; + case ADBADDR_REL: + extdms_done = 0; + /* talk register 3 */ + ADBOp((Ptr)buffer, (Ptr)extdms_complete, + (Ptr)&extdms_done, (adbaddr << 4) | 0xf); + + /* Wait until done, but no more than 2 secs */ + count = 40000; + while (!extdms_done && count-- > 0) + delay(50); + + DELAY(1000); + + if (!extdms_done) { + printf("ghost mouse?"); + break; + } + + devtype = buffer[2]; + switch (devtype) { + case ADBMS_100DPI: + printf("100 dpi mouse"); + break; + case ADBMS_200DPI: + printf("200 dpi mouse"); + break; + case ADBMS_MSA3: + printf("Mouse Systems A3 mouse, default parameters"); + break; + case ADBMS_USPEED: + printf("MicroSpeed mouse, default parameters"); + break; + case ADBMS_EXTENDED: + extdms_done = 0; + /* talk register 1 */ + ADBOp((Ptr)buffer, (Ptr)extdms_complete, + (Ptr)&extdms_done, (adbaddr << 4) | 0xd); + while (!extdms_done) + /* busy-wait until done */; + if (buffer[1] == 0x9a && buffer[2] == 0x20) + printf("Mouseman (non-EMP) mouse"); + else { + printf("extended mouse <%c%c%c%c> " + "%d-button %d dpi ", + buffer[1], buffer[2], + buffer[3], buffer[4], + (int)buffer[8], + (int)*(short *)&buffer[5]); + if (buffer[7] == 1) + printf("mouse"); + else if (buffer[7] == 2) + printf("trackball"); + else + printf("unknown device"); + } + break; + default: + printf("relative positioning device (mouse?) " + "(%d)", adbdata.devType); + break; + } + break; + case ADBADDR_ABS: + switch (adbdata.devType) { + case ADB_ARTPAD: + printf("WACOM ArtPad II"); + break; + default: + printf("abs. pos. device (tablet?) (%d)", + adbdata.devType); + break; + } + break; + case ADBADDR_DATATX: + printf("data transfer device (modem?) (%d)", + adbdata.devType); + break; + case ADBADDR_MISC: + switch (adbdata.devType) { + case ADB_POWERKEY: + printf("Sophisticated Circuits PowerKey"); + break; + default: + printf("misc. device (remote control?) (%d)", + adbdata.devType); + break; + } + break; + default: + printf("unknown type device, (def %d, handler %d)", + adbdata.origADBAddr, adbdata.devType); + break; + } + printf(" at %d\n", adbaddr); + + /* Set completion routine to be NetBSD's */ + if ((adbdata.origADBAddr == ADBADDR_REL) && + (buffer[0] > 0) && (buffer[2] == ADBMS_MSA3)) { + /* Special device handler for the A3 mouse */ + adbinfo.siServiceRtPtr = (Ptr)adb_msa3_complete; + } else if ((adbdata.origADBAddr == ADBADDR_MAP) && + (adbdata.devType == ADB_EXTKBD) && + (buffer[1] == 0x9a) && (buffer[2] == 0x20)) { + /* ignore non-EMP Mouseman pseudo keyboard */ + adbinfo.siServiceRtPtr = (Ptr)0; + } else if ((adbdata.origADBAddr == ADBADDR_REL) && + (devtype == ADBMS_EXTENDED) && + (buffer[1] == 0x9a) && (buffer[2] == 0x20)) { + /* + * Set up non-EMP Mouseman to put button + * bits in 3rd byte instead of sending via + * pseudo keyboard device. + */ + extdms_done = 0; + /* listen register 1 */ + buffer[0] = 2; + buffer[1] = 0x00; + buffer[2] = 0x81; + ADBOp((Ptr)buffer, (Ptr)extdms_complete, + (Ptr)&extdms_done, (adbaddr << 4) | 0x9); + while (!extdms_done) + /* busy-wait until done */; + extdms_done = 0; + /* listen register 1 */ + buffer[0] = 2; + buffer[1] = 0x01; + buffer[2] = 0x81; + ADBOp((Ptr)buffer, (Ptr)extdms_complete, + (Ptr)&extdms_done, (adbaddr << 4) | 0x9); + while (!extdms_done) + /* busy-wait until done */; + extdms_done = 0; + /* listen register 1 */ + buffer[0] = 2; + buffer[1] = 0x02; + buffer[2] = 0x81; + ADBOp((Ptr)buffer, (Ptr)extdms_complete, + (Ptr)&extdms_done, (adbaddr << 4) | 0x9); + while (!extdms_done) + /* busy-wait until done */; + extdms_done = 0; + /* listen register 1 */ + buffer[0] = 2; + buffer[1] = 0x03; + buffer[2] = 0x38; + ADBOp((Ptr)buffer, (Ptr)extdms_complete, + (Ptr)&extdms_done, (adbaddr << 4) | 0x9); + while (!extdms_done) + /* busy-wait until done */; + /* non-EMP Mouseman has special handler */ + adbinfo.siServiceRtPtr = (Ptr)adb_mm_nonemp_complete; + } else { + /* Default completion routine */ + adbinfo.siServiceRtPtr = (Ptr)adb_complete; + } + adbinfo.siDataAreaAddr = NULL; + error = SetADBInfo(&adbinfo, adbaddr); +#ifdef ADB_DEBUG + if (adb_debug) + printf("adb: returned %d from SetADBInfo\n", error); +#endif + } + + adb_initted = 1; +} + + +void +extdms_complete(buffer, compdata, cmd) + caddr_t buffer, compdata; + int cmd; +{ + long *p = (long *)compdata; + + *p= -1; +} diff --git a/sys/arch/macppc/dev/adbvar.h b/sys/arch/macppc/dev/adbvar.h new file mode 100644 index 00000000000..b4aa265f0b3 --- /dev/null +++ b/sys/arch/macppc/dev/adbvar.h @@ -0,0 +1,114 @@ +/* $NetBSD: adbvar.h,v 1.1 1998/05/15 10:15:47 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 <machine/adbsys.h> + +#define ADB_MAXTRACE (NBPG / sizeof(int) - 1) +extern int adb_traceq[ADB_MAXTRACE]; +extern int adb_traceq_tail; +extern int adb_traceq_len; + +typedef struct adb_trace_xlate_s { + int params; + char *string; +} adb_trace_xlate_t; + +extern adb_trace_xlate_t adb_trace_xlations[]; + +#ifdef DEBUG +#ifndef ADB_DEBUG +#define ADB_DEBUG +#endif +extern int adb_debug; +#endif + +typedef caddr_t Ptr; +typedef caddr_t *Handle; + +/* ADB Manager */ +typedef struct { + Ptr siServiceRtPtr; + Ptr siDataAreaAddr; +} ADBSetInfoBlock; +typedef struct { + unsigned char devType; + unsigned char origADBAddr; + Ptr dbServiceRtPtr; + Ptr dbDataAreaAddr; +} ADBDataBlock; + +struct adb_softc { + struct device sc_dev; + char *sc_regbase; +}; + + +/* adb.c */ +void adb_enqevent __P((adb_event_t *event)); +void adb_handoff __P((adb_event_t *event)); +void adb_autorepeat __P((void *keyp)); +void adb_dokeyupdown __P((adb_event_t *event)); +void adb_keymaybemouse __P((adb_event_t *event)); +void adb_processevent __P((adb_event_t *event)); +int adbopen __P((dev_t dev, int flag, int mode, struct proc *p)); +int adbclose __P((dev_t dev, int flag, int mode, struct proc *p)); +int adbread __P((dev_t dev, struct uio *uio, int flag)); +int adbwrite __P((dev_t dev, struct uio *uio, int flag)); +int adbioctl __P((dev_t , int , caddr_t , int , struct proc *)); +int adbpoll __P((dev_t dev, int events, struct proc *p)); + +/* adbsys.c */ +void adb_complete __P((caddr_t buffer, caddr_t data_area, int adb_command)); +void adb_msa3_complete __P((caddr_t buffer, caddr_t data_area, int adb_command)); +void adb_mm_nonemp_complete __P((caddr_t buffer, caddr_t data_area, int adb_command)); +void extdms_init __P((int)); +void extdms_complete __P((caddr_t, caddr_t, int)); + +#ifndef MRG_ADB +/* types of adb hardware that we (will eventually) support */ +#define ADB_HW_UNKNOWN 0x01 /* don't know */ +#define ADB_HW_II 0x02 /* Mac II series */ +#define ADB_HW_IISI 0x03 /* Mac IIsi series */ +#define ADB_HW_PB 0x04 /* PowerBook series */ +#define ADB_HW_CUDA 0x05 /* Machines with a Cuda chip */ + +/* adb_direct.c */ +int adb_poweroff __P((void)); +int CountADBs __P((void)); +void ADBReInit __P((void)); +int GetIndADB __P((ADBDataBlock * info, int index)); +int GetADBInfo __P((ADBDataBlock * info, int adbAddr)); +int SetADBInfo __P((ADBSetInfoBlock * info, int adbAddr)); +int ADBOp __P((Ptr buffer, Ptr compRout, Ptr data, short commandNum)); +int adb_read_date_time __P((unsigned long *t)); +int adb_set_date_time __P((unsigned long t)); +#endif /* !MRG_ADB */ diff --git a/sys/arch/macppc/dev/am79c950.c b/sys/arch/macppc/dev/am79c950.c new file mode 100644 index 00000000000..27c35551a72 --- /dev/null +++ b/sys/arch/macppc/dev/am79c950.c @@ -0,0 +1,820 @@ +/* $NetBSD: am79c950.c,v 1.1 1998/05/15 10:15:47 tsubai Exp $ */ + +/*- + * Copyright (c) 1997 David Huang <khym@bga.com> + * All rights reserved. + * + * Portions of this code are based on code by Denton Gentry <denny1@home.com>, + * Charles M. Hannum, Yanagisawa Takeshi <yanagisw@aa.ap.titech.ac.jp>, and + * Jason R. Thorpe. + * + * 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. 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. + * + */ + +/* + * Driver for the AMD Am79C940 (MACE) ethernet chip, used for onboard + * ethernet on the Centris/Quadra 660av and Quadra 840av. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/buf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/syslog.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/device.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_ether.h> +#include <net/if_media.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/if_inarp.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#endif + +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#if defined(CCITT) && defined(LLC) +#include <sys/socketvar.h> +#include <netccitt/x25.h> +#include <netccitt/pk.h> +#include <netccitt/pk_var.h> +#include <netccitt/pk_extern.h> +#endif + +#include <vm/vm.h> + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#include <machine/pio.h> +#include <machine/bus.h> + +#include <macppc/dev/am79c950reg.h> +#include <macppc/dev/if_mcvar.h> + +hide void mcwatchdog __P((struct ifnet *)); +hide int mcinit __P((struct mc_softc *sc)); +hide int mcstop __P((struct mc_softc *sc)); +hide int mcioctl __P((struct ifnet *ifp, u_long cmd, caddr_t data)); +hide void mcstart __P((struct ifnet *ifp)); +hide void mcreset __P((struct mc_softc *sc)); + +integrate u_int maceput __P((struct mc_softc *sc, struct mbuf *m0)); +integrate void mc_tint __P((struct mc_softc *sc)); +integrate void mace_read __P((struct mc_softc *, caddr_t, int)); +integrate struct mbuf *mace_get __P((struct mc_softc *, caddr_t, int)); +static void mace_calcladrf __P((struct ethercom *ac, u_int8_t *af)); +static inline u_int16_t ether_cmp __P((void *, void *)); +static int mc_mediachange __P((struct ifnet *)); +static void mc_mediastatus __P((struct ifnet *, struct ifmediareq *)); + +/* + * Compare two Ether/802 addresses for equality, inlined and + * unrolled for speed. Use this like bcmp(). + * + * XXX: Add <machine/inlines.h> for stuff like this? + * XXX: or maybe add it to libkern.h instead? + * + * "I'd love to have an inline assembler version of this." + * XXX: Who wanted that? mycroft? I wrote one, but this + * version in C is as good as hand-coded assembly. -gwr + * + * Please do NOT tweak this without looking at the actual + * assembly code generated before and after your tweaks! + */ +static inline u_int16_t +ether_cmp(one, two) + void *one, *two; +{ + register u_int16_t *a = (u_short *) one; + register u_int16_t *b = (u_short *) two; + register u_int16_t diff; + +#ifdef m68k + /* + * The post-increment-pointer form produces the best + * machine code for m68k. This was carefully tuned + * so it compiles to just 8 short (2-byte) op-codes! + */ + diff = *a++ - *b++; + diff |= *a++ - *b++; + diff |= *a++ - *b++; +#else + /* + * Most modern CPUs do better with a single expresion. + * Note that short-cut evaluation is NOT helpful here, + * because it just makes the code longer, not faster! + */ + diff = (a[0] - b[0]) | (a[1] - b[1]) | (a[2] - b[2]); +#endif + + return (diff); +} + +#define ETHER_CMP ether_cmp + +/* + * Interface exists: make available by filling in network interface + * record. System will initialize the interface when it is ready + * to accept packets. + */ +int +mcsetup(sc, lladdr) + struct mc_softc *sc; + u_int8_t *lladdr; +{ + struct ifnet *ifp = &sc->sc_if; + + /* reset the chip and disable all interrupts */ + NIC_PUT(sc, MACE_BIUCC, SWRST); + DELAY(100); + NIC_PUT(sc, MACE_IMR, ~0); + + bcopy(lladdr, sc->sc_enaddr, ETHER_ADDR_LEN); + printf(": address %s\n", ether_sprintf(lladdr)); + + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_ioctl = mcioctl; + ifp->if_start = mcstart; + ifp->if_flags = + IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST; + ifp->if_watchdog = mcwatchdog; + +#if NBPFILTER > 0 + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif + + /* initialize ifmedia structures */ + ifmedia_init(&sc->sc_media, 0, mc_mediachange, mc_mediastatus); + ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_MANUAL, 0, NULL); + ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_MANUAL); + + if_attach(ifp); + ether_ifattach(ifp, lladdr); + + return (0); +} + +hide int +mcioctl(ifp, cmd, data) + struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct mc_softc *sc = ifp->if_softc; + struct ifaddr *ifa; + struct ifreq *ifr; + + int s = splnet(), err = 0; + int temp; + + switch (cmd) { + + case SIOCSIFADDR: + ifa = (struct ifaddr *)data; + ifp->if_flags |= IFF_UP; + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + mcinit(sc); + arp_ifinit(ifp, ifa); + break; +#endif +#ifdef NS + case AF_NS: + { + register struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; + + if (ns_nullhost(*ina)) + ina->x_host = + *(union ns_host *)LLADDR(ifp->if_sadl); + else { + bcopy(ina->x_host.c_host, + LLADDR(ifp->if_sadl), + sizeof(sc->sc_enaddr)); + } + /* Set new address. */ + mcinit(sc); + break; + } +#endif + default: + mcinit(sc); + break; + } + break; + + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && + (ifp->if_flags & IFF_RUNNING) != 0) { + /* + * If interface is marked down and it is running, + * then stop it. + */ + mcstop(sc); + ifp->if_flags &= ~IFF_RUNNING; + } else if ((ifp->if_flags & IFF_UP) != 0 && + (ifp->if_flags & IFF_RUNNING) == 0) { + /* + * If interface is marked up and it is stopped, + * then start it. + */ + (void)mcinit(sc); + } else { + /* + * reset the interface to pick up any other changes + * in flags + */ + temp = ifp->if_flags & IFF_UP; + mcreset(sc); + ifp->if_flags |= temp; + mcstart(ifp); + } + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + ifr = (struct ifreq *) data; + err = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->sc_ethercom) : + ether_delmulti(ifr, &sc->sc_ethercom); + + if (err == ENETRESET) { + /* + * Multicast list has changed; set the hardware + * filter accordingly. But remember UP flag! + */ + temp = ifp->if_flags & IFF_UP; + mcreset(sc); + ifp->if_flags |= temp; + err = 0; + } + break; + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + ifr = (struct ifreq *) data; + err = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); + break; + + default: + err = EINVAL; + } + splx(s); + return (err); +} + +/* + * Encapsulate a packet of type family for the local net. + */ +hide void +mcstart(ifp) + struct ifnet *ifp; +{ + struct mc_softc *sc = ifp->if_softc; + struct mbuf *m; + + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + return; + + while (1) { + if (ifp->if_flags & IFF_OACTIVE) + return; + + IF_DEQUEUE(&ifp->if_snd, m); + if (m == 0) + return; + +#if NBPFILTER > 0 + /* + * If bpf is listening on this interface, let it + * see the packet before we commit it to the wire. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); +#endif + + /* + * Copy the mbuf chain into the transmit buffer. + */ + ifp->if_flags |= IFF_OACTIVE; + maceput(sc, m); + + ifp->if_opackets++; /* # of pkts */ + } +} + +/* + * reset and restart the MACE. Called in case of fatal + * hardware/software errors. + */ +hide void +mcreset(sc) + struct mc_softc *sc; +{ + mcstop(sc); + mcinit(sc); +} + +hide int +mcinit(sc) + struct mc_softc *sc; +{ + int s; + u_int8_t maccc, ladrf[8]; + + if (sc->sc_if.if_flags & IFF_RUNNING) + /* already running */ + return (0); + + s = splnet(); + + NIC_PUT(sc, MACE_BIUCC, sc->sc_biucc); + NIC_PUT(sc, MACE_FIFOCC, sc->sc_fifocc); + NIC_PUT(sc, MACE_IMR, ~0); /* disable all interrupts */ + NIC_PUT(sc, MACE_PLSCC, sc->sc_plscc); + + NIC_PUT(sc, MACE_UTR, RTRD); /* disable reserved test registers */ + + /* set MAC address */ + NIC_PUT(sc, MACE_IAC, ADDRCHG); + while (NIC_GET(sc, MACE_IAC) & ADDRCHG) + ; + NIC_PUT(sc, MACE_IAC, PHYADDR); + bus_space_write_multi_1(sc->sc_regt, sc->sc_regh, MACE_REG(MACE_PADR), + sc->sc_enaddr, ETHER_ADDR_LEN); + + /* set logical address filter */ + mace_calcladrf(&sc->sc_ethercom, ladrf); + + NIC_PUT(sc, MACE_IAC, ADDRCHG); + while (NIC_GET(sc, MACE_IAC) & ADDRCHG) + ; + NIC_PUT(sc, MACE_IAC, LOGADDR); + bus_space_write_multi_1(sc->sc_regt, sc->sc_regh, MACE_REG(MACE_LADRF), + ladrf, 8); + + NIC_PUT(sc, MACE_XMTFC, APADXMT); + /* + * No need to autostrip padding on receive... Ethernet frames + * don't have a length field, unlike 802.3 frames, so the MACE + * can't figure out the length of the packet anyways. + */ + NIC_PUT(sc, MACE_RCVFC, 0); + + maccc = ENXMT | ENRCV; + if (sc->sc_if.if_flags & IFF_PROMISC) + maccc |= PROM; + + NIC_PUT(sc, MACE_MACCC, maccc); + + if (sc->sc_bus_init) + (*sc->sc_bus_init)(sc); + + /* + * Enable all interrupts except receive, since we use the DMA + * completion interrupt for that. + */ + NIC_PUT(sc, MACE_IMR, RCVINTM); + + /* flag interface as "running" */ + sc->sc_if.if_flags |= IFF_RUNNING; + sc->sc_if.if_flags &= ~IFF_OACTIVE; + + splx(s); + return (0); +} + +/* + * close down an interface and free its buffers + * Called on final close of device, or if mcinit() fails + * part way through. + */ +hide int +mcstop(sc) + struct mc_softc *sc; +{ + int s = splnet(); + + NIC_PUT(sc, MACE_BIUCC, SWRST); + DELAY(100); + + sc->sc_if.if_timer = 0; + sc->sc_if.if_flags &= ~(IFF_RUNNING | IFF_UP); + + splx(s); + return (0); +} + +/* + * Called if any Tx packets remain unsent after 5 seconds, + * In all cases we just reset the chip, and any retransmission + * will be handled by higher level protocol timeouts. + */ +hide void +mcwatchdog(ifp) + struct ifnet *ifp; +{ + struct mc_softc *sc = ifp->if_softc; + int temp; + + printf("mcwatchdog: resetting chip\n"); + temp = ifp->if_flags & IFF_UP; + mcreset(sc); + ifp->if_flags |= temp; +} + +/* + * stuff packet into MACE (at splnet) + */ +integrate u_int +maceput(sc, m) + struct mc_softc *sc; + struct mbuf *m; +{ + struct mbuf *n; + u_int len, totlen = 0; + u_char *buff; + + buff = sc->sc_txbuf; + + for (; m; m = n) { + u_char *data = mtod(m, u_char *); + len = m->m_len; + totlen += len; + bcopy(data, buff, len); + buff += len; + MFREE(m, n); + } + + if (totlen > NBPG) + panic("%s: maceput: packet overflow", sc->sc_dev.dv_xname); + +#if 0 + if (totlen < ETHERMIN + sizeof(struct ether_header)) { + int pad = ETHERMIN + sizeof(struct ether_header) - totlen; + bzero(sc->sc_txbuf + totlen, pad); + totlen = ETHERMIN + sizeof(struct ether_header); + } +#endif + + (*sc->sc_putpacket)(sc, totlen); + + sc->sc_if.if_timer = 5; /* 5 seconds to watch for failing to transmit */ + return (totlen); +} + +void +mcintr(arg) + void *arg; +{ + struct mc_softc *sc = arg; + u_int8_t ir; + + ir = NIC_GET(sc, MACE_IR) & ~NIC_GET(sc, MACE_IMR); + if (ir & JAB) { +#ifdef MCDEBUG + printf("%s: jabber error\n", sc->sc_dev.dv_xname); +#endif + sc->sc_if.if_oerrors++; + } + + if (ir & BABL) { +#ifdef MCDEBUG + printf("%s: babble\n", sc->sc_dev.dv_xname); +#endif + sc->sc_if.if_oerrors++; + } + + if (ir & CERR) { + printf("%s: collision error\n", sc->sc_dev.dv_xname); + sc->sc_if.if_collisions++; + } + + /* + * Pretend we have carrier; if we don't this will be cleared + * shortly. + */ + sc->sc_havecarrier = 1; + + if (ir & XMTINT) + mc_tint(sc); + + if (ir & RCVINT) + mc_rint(sc); +} + +integrate void +mc_tint(sc) + struct mc_softc *sc; +{ + u_int8_t xmtrc, xmtfs; + + xmtrc = NIC_GET(sc, MACE_XMTRC); + xmtfs = NIC_GET(sc, MACE_XMTFS); + + if ((xmtfs & XMTSV) == 0) + return; + + if (xmtfs & UFLO) { + printf("%s: underflow\n", sc->sc_dev.dv_xname); + mcreset(sc); + return; + } + + if (xmtfs & LCOL) { + printf("%s: late collision\n", sc->sc_dev.dv_xname); + sc->sc_if.if_oerrors++; + sc->sc_if.if_collisions++; + } + + if (xmtfs & MORE) + /* Real number is unknown. */ + sc->sc_if.if_collisions += 2; + else if (xmtfs & ONE) + sc->sc_if.if_collisions++; + else if (xmtfs & RTRY) { + sc->sc_if.if_collisions += 16; + sc->sc_if.if_oerrors++; + } + + if (xmtfs & LCAR) { + sc->sc_havecarrier = 0; + printf("%s: lost carrier\n", sc->sc_dev.dv_xname); + sc->sc_if.if_oerrors++; + } + + sc->sc_if.if_flags &= ~IFF_OACTIVE; + sc->sc_if.if_timer = 0; + mcstart(&sc->sc_if); +} + +void +mc_rint(sc) + struct mc_softc *sc; +{ +#define rxf sc->sc_rxframe + u_int len; + + len = (rxf.rx_rcvcnt | ((rxf.rx_rcvsts & 0xf) << 8)) - 4; + +#ifdef MCDEBUG + if (rxf.rx_rcvsts & 0xf0) + printf("%s: rcvcnt %02x rcvsts %02x rntpc 0x%02x rcvcc 0x%02x\n", + sc->sc_dev.dv_xname, rxf.rx_rcvcnt, rxf.rx_rcvsts, + rxf.rx_rntpc, rxf.rx_rcvcc); +#endif + + if (rxf.rx_rcvsts & OFLO) { + printf("%s: receive FIFO overflow\n", sc->sc_dev.dv_xname); + sc->sc_if.if_ierrors++; + return; + } + + if (rxf.rx_rcvsts & CLSN) + sc->sc_if.if_collisions++; + + if (rxf.rx_rcvsts & FRAM) { +#ifdef MCDEBUG + printf("%s: framing error\n", sc->sc_dev.dv_xname); +#endif + sc->sc_if.if_ierrors++; + return; + } + + if (rxf.rx_rcvsts & FCS) { +#ifdef MCDEBUG + printf("%s: frame control checksum error\n", sc->sc_dev.dv_xname); +#endif + sc->sc_if.if_ierrors++; + return; + } + + mace_read(sc, rxf.rx_frame, len); +#undef rxf +} + +integrate void +mace_read(sc, pkt, len) + struct mc_softc *sc; + caddr_t pkt; + int len; +{ + struct ifnet *ifp = &sc->sc_if; + struct ether_header *eh = (struct ether_header *)pkt; + struct mbuf *m; + + if (len <= sizeof(struct ether_header) || + len > ETHERMTU + sizeof(struct ether_header)) { +#ifdef MCDEBUG + printf("%s: invalid packet size %d; dropping\n", + sc->sc_dev.dv_xname, len); +#endif + ifp->if_ierrors++; + return; + } + +#if NBPFILTER > 0 + /* + * Check if there's a bpf filter listening on this interface. + * If so, hand off the raw packet to enet, then discard things + * not destined for us (but be sure to keep broadcast/multicast). + */ + if (ifp->if_bpf) { + bpf_tap(ifp->if_bpf, pkt, len); + if ((ifp->if_flags & IFF_PROMISC) != 0 && + (eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */ + ETHER_CMP(eh->ether_dhost, sc->sc_enaddr)) + return; + } +#endif + m = mace_get(sc, pkt, len); + if (m == NULL) { + ifp->if_ierrors++; + return; + } + + ifp->if_ipackets++; + + /* Pass the packet up, with the ether header sort-of removed. */ + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, eh, m); +} + +/* + * Pull data off an interface. + * Len is length of data, with local net header stripped. + * We copy the data into mbufs. When full cluster sized units are present + * we copy into clusters. + */ +integrate struct mbuf * +mace_get(sc, pkt, totlen) + struct mc_softc *sc; + caddr_t pkt; + int totlen; +{ + register struct mbuf *m; + struct mbuf *top, **mp; + int len; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + return (0); + m->m_pkthdr.rcvif = &sc->sc_if; + m->m_pkthdr.len = totlen; + len = MHLEN; + top = 0; + mp = ⊤ + + while (totlen > 0) { + if (top) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) { + m_freem(top); + return 0; + } + len = MLEN; + } + if (totlen >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + m_freem(top); + return 0; + } + len = MCLBYTES; + } + m->m_len = len = min(totlen, len); + bcopy(pkt, mtod(m, caddr_t), len); + pkt += len; + totlen -= len; + *mp = m; + mp = &m->m_next; + } + + return (top); +} + +/* + * Go through the list of multicast addresses and calculate the logical + * address filter. + */ +void +mace_calcladrf(ac, af) + struct ethercom *ac; + u_int8_t *af; +{ + struct ifnet *ifp = &ac->ec_if; + struct ether_multi *enm; + register u_char *cp, c; + register u_int32_t crc; + register int i, len; + struct ether_multistep step; + + /* + * Set up multicast address filter by passing all multicast addresses + * through a crc generator, and then using the high order 6 bits as an + * index into the 64 bit logical address filter. The high order bit + * selects the word, while the rest of the bits select the bit within + * the word. + */ + + *((u_int32_t *)af) = *((u_int32_t *)af + 1) = 0; + + ETHER_FIRST_MULTI(step, ac, enm); + while (enm != NULL) { + if (ETHER_CMP(enm->enm_addrlo, enm->enm_addrhi)) { + /* + * We must listen to a range of multicast addresses. + * For now, just accept all multicasts, rather than + * trying to set only those filter bits needed to match + * the range. (At this time, the only use of address + * ranges is for IP multicast routing, for which the + * range is big enough to require all bits set.) + */ + goto allmulti; + } + + cp = enm->enm_addrlo; + crc = 0xffffffff; + for (len = sizeof(enm->enm_addrlo); --len >= 0;) { + c = *cp++; + for (i = 8; --i >= 0;) { + if ((crc & 0x01) ^ (c & 0x01)) { + crc >>= 1; + crc ^= 0xedb88320; + } else + crc >>= 1; + c >>= 1; + } + } + /* Just want the 6 most significant bits. */ + crc >>= 26; + + /* Set the corresponding bit in the filter. */ + af[crc >> 3] |= 1 << (crc & 7); + + ETHER_NEXT_MULTI(step, enm); + } + ifp->if_flags &= ~IFF_ALLMULTI; + return; + +allmulti: + ifp->if_flags |= IFF_ALLMULTI; + *((u_int32_t *)af) = *((u_int32_t *)af + 1) = 0xffffffff; +} + +int +mc_mediachange(ifp) + struct ifnet *ifp; +{ + return EINVAL; +} + +void +mc_mediastatus(ifp, ifmr) + struct ifnet *ifp; + struct ifmediareq *ifmr; +{ + struct mc_softc *sc = ifp->if_softc; + + if ((ifp->if_flags & IFF_UP) == 0) + return; + + if (sc->sc_havecarrier) + ifmr->ifm_status |= IFM_ACTIVE; +} diff --git a/sys/arch/macppc/dev/am79c950reg.h b/sys/arch/macppc/dev/am79c950reg.h new file mode 100644 index 00000000000..51c9aeedd52 --- /dev/null +++ b/sys/arch/macppc/dev/am79c950reg.h @@ -0,0 +1,200 @@ +/* $NetBSD: am79c950reg.h,v 1.1 1998/05/15 10:15:47 tsubai Exp $ */ + +/*- + * Copyright (c) 1997 David Huang <khym@bga.com> + * 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. 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. + * + */ + +/* + * AMD MACE (Am79C940) register definitions + */ +#define MACE_RCVFIFO 0 /* Receive FIFO [15-00] (read only) */ +#define MACE_XMTFIFO 1 /* Transmit FIFO [15-00] (write only) */ +#define MACE_XMTFC 2 /* Transmit Frame Control (read/write) */ +#define MACE_XMTFS 3 /* Transmit Frame Status (read only) */ +#define MACE_XMTRC 4 /* Transmit Retry Count (read only) */ +#define MACE_RCVFC 5 /* Receive Frame Control (read/write) */ +#define MACE_RCVFS 6 /* Receive Frame Status (4 bytes) (read only) */ +#define MACE_FIFOFC 7 /* FIFO Frame Count (read only) */ +#define MACE_IR 8 /* Interrupt Register (read only) */ +#define MACE_IMR 9 /* Interrupt Mask Register (read/write) */ +#define MACE_PR 10 /* Poll Register (read only) */ +#define MACE_BIUCC 11 /* BIU Configuration Control (read/write) */ +#define MACE_FIFOCC 12 /* FIFO Configuration Control (read/write) */ +#define MACE_MACCC 13 /* MAC Configuration Control (read/write) */ +#define MACE_PLSCC 14 /* PLS Configuration Control (read/write) */ +#define MACE_PHYCC 15 /* PHY Confiuration Control (read/write) */ +#define MACE_CHIPIDL 16 /* Chip ID Register [07-00] (read only) */ +#define MACE_CHIPIDH 17 /* Chip ID Register [15-08] (read only) */ +#define MACE_IAC 18 /* Internal Address Configuration (read/write) */ +/* RESERVED 19 Reserved (read/write as 0) */ +#define MACE_LADRF 20 /* Logical Address Filter (8 bytes) (read/write) */ +#define MACE_PADR 21 /* Physical Address (6 bytes) (read/write) */ +/* RESERVED 22 Reserved (read/write as 0) */ +/* RESERVED 23 Reserved (read/write as 0) */ +#define MACE_MPC 24 /* Missed Packet Count (read only) */ +/* RESERVED 25 Reserved (read/write as 0) */ +#define MACE_RNTPC 26 /* Runt Packet Count (read only) */ +#define MACE_RCVCC 27 /* Receive Collision Count (read only) */ +/* RESERVED 28 Reserved (read/write as 0) */ +#define MACE_UTR 29 /* User Test Register (read/write) */ +#define MACE_RTR1 30 /* Reserved Test Register 1 (read/write as 0) */ +#define MACE_RTR2 31 /* Reserved Test Register 2 (read/write as 0) */ + +#define MACE_NREGS 32 + +/* 2: Transmit Frame Control (XMTFC) */ +#define DRTRY 0x80 /* Disable Retry */ +#define DXMTFCS 0x08 /* Disable Transmit FCS */ +#define APADXMT 0x01 /* Auto Pad Transmit */ + +/* 3: Transmit Frame Status (XMTFS) */ +#define XMTSV 0x80 /* Transmit Status Valid */ +#define UFLO 0x40 /* Underflow */ +#define LCOL 0x20 /* Late Collision */ +#define MORE 0x10 /* More than one retry needed */ +#define ONE 0x08 /* Exactly one retry needed */ +#define DEFER 0x04 /* Transmission deferred */ +#define LCAR 0x02 /* Loss of Carrier */ +#define RTRY 0x01 /* Retry Error */ + +/* 4: Transmit Retry Count (XMTRC) */ +#define EXDEF 0x80 /* Excessive Defer */ +#define XMTRC 0x0f /* Transmit Retry Count */ + +/* 5: Receive Frame Control (RCVFC) */ +#define LLRCV 0x08 /* Low Latency Receive */ +#define MR 0x04 /* Match/Reject */ +#define ASTRPRCV 0x01 /* Auto Strip Receive */ + +/* 6: Receive Frame Status (RCVFS) */ +/* 4 byte register; read 4 times to get all of the bytes */ +/* Read 1: RFS0 - Receive Message Byte Count [7-0] (RCVCNT) */ + +/* Read 2: RFS1 - Receive Status (RCVSTS) */ +#define OFLO 0x80 /* Overflow flag */ +#define CLSN 0x40 /* Collision flag */ +#define FRAM 0x20 /* Framing Error flag */ +#define FCS 0x10 /* FCS Error flag */ +#define RCVCNT 0x0f /* Receive Message Byte Count [11-8] */ + +/* Read 3: RFS2 - Runt Packet Count (RNTPC) [7-0] */ + +/* Read 4: RFS3 - Receive Collision Count (RCVCC) [7-0] */ + +/* 7: FIFO Frame Count (FIFOFC) */ +#define RCVFC 0xf0 /* Receive Frame Count */ +#define XMTFC 0x0f /* Transmit Frame Count */ + +/* 8: Interrupt Register (IR) */ +#define JAB 0x80 /* Jabber Error */ +#define BABL 0x40 /* Babble Error */ +#define CERR 0x20 /* Collision Error */ +#define RCVCCO 0x10 /* Receive Collision Count Overflow */ +#define RNTPCO 0x08 /* Runt Packet Count Overflow */ +#define MPCO 0x04 /* Missed Packet Count Overflow */ +#define RCVINT 0x02 /* Receive Interrupt */ +#define XMTINT 0x01 /* Transmit Interrupt */ + +/* 9: Interrut Mask Register (IMR) */ +#define JABM 0x80 /* Jabber Error Mask */ +#define BABLM 0x40 /* Babble Error Mask */ +#define CERRM 0x20 /* Collision Error Mask */ +#define RCVCCOM 0x10 /* Receive Collision Count Overflow Mask */ +#define RNTPCOM 0x08 /* Runt Packet Count Overflow Mask */ +#define MPCOM 0x04 /* Missed Packet Count Overflow Mask */ +#define RCVINTM 0x02 /* Receive Interrupt Mask */ +#define XMTINTM 0x01 /* Transmit Interrupt Mask */ + +/* 10: Poll Register (PR) */ +#define XMTSV 0x80 /* Transmit Status Valid */ +#define TDTREQ 0x40 /* Transmit Data Transfer Request */ +#define RDTREQ 0x20 /* Receive Data Transfer Request */ + +/* 11: BIU Configuration Control (BIUCC) */ +#define BSWP 0x40 /* Byte Swap */ +#define XMTSP 0x30 /* Transmit Start Point */ +#define XMTSP_4 0x00 /* 4 bytes */ +#define XMTSP_16 0x10 /* 16 bytes */ +#define XMTSP_64 0x20 /* 64 bytes */ +#define XMTSP_112 0x30 /* 112 bytes */ +#define SWRST 0x01 /* Software Reset */ + +/* 12: FIFO Configuration Control (FIFOCC) */ +#define XMTFW 0xc0 /* Transmit FIFO Watermark */ +#define XMTFW_8 0x00 /* 8 write cycles */ +#define XMTFW_16 0x40 /* 16 write cycles */ +#define XMTFW_32 0x80 /* 32 write cycles */ +#define RCVFW 0x30 /* Receive FIFO Watermark */ +#define RCVFW_16 0x00 /* 16 bytes */ +#define RCVFW_32 0x10 /* 32 bytes */ +#define RCVFW_64 0x20 /* 64 bytes */ +#define XMTFWU 0x08 /* Transmit FIFO Watermark Update */ +#define RCVFWU 0x04 /* Receive FIFO Watermark Update */ +#define XMTBRST 0x02 /* Transmit Burst */ +#define RCVBRST 0x01 /* Receive Burst */ + +/* 13: MAC Configuration (MACCC) */ +#define PROM 0x80 /* Promiscuous */ +#define DXMT2PD 0x40 /* Disable Transmit Two Part Deferral */ +#define EMBA 0x20 /* Enable Modified Back-off Algorithm */ +#define DRCVPA 0x08 /* Disable Receive Physical Address */ +#define DRCVBC 0x04 /* Disable Receive Broadcast */ +#define ENXMT 0x02 /* Enable Transmit */ +#define ENRCV 0x01 /* Enable Receive */ + +/* 14: PLS Configuration Control (PLSCC) */ +#define XMTSEL 0x08 /* Transmit Mode Select */ +#define PORTSEL 0x06 /* Port Select */ +#define PORTSEL_AUI 0x00 /* Select AUI */ +#define PORTSEL_10BT 0x02 /* Select 10BASE-T */ +#define PORTSEL_DAI 0x04 /* Select DAI port */ +#define PORTSEL_GPSI 0x06 /* Select GPSI */ +#define ENPLSIO 0x01 /* Enable PLS I/O */ + +/* 15: PHY Configuration (PHYCC) */ +#define LNKFL 0x80 /* Link Fail */ +#define DLNKTST 0x40 /* Disable Link Test */ +#define REVPOL 0x20 /* Reversed Polarity */ +#define DAPC 0x10 /* Disable Auto Polarity Correction */ +#define LRT 0x08 /* Low Receive Threshold */ +#define ASEL 0x04 /* Auto Select */ +#define RWAKE 0x02 /* Remote Wake */ +#define AWAKE 0x01 /* Auto Wake */ + +/* 18: Internal Address Configuration (IAC) */ +#define ADDRCHG 0x80 /* Address Change */ +#define PHYADDR 0x04 /* Physical Address Reset */ +#define LOGADDR 0x02 /* Logical Address Reset */ + +/* 28: User Test Register (UTR) */ +#define RTRE 0x80 /* Reserved Test Register Enable */ +#define RTRD 0x40 /* Reserved Test Register Disable */ +#define RPA 0x20 /* Run Packet Accept */ +#define FCOLL 0x10 /* Force Collision */ +#define RCVFCSE 0x08 /* Receive FCS Enable */ +#define LOOP 0x06 /* Loopback Control */ +#define LOOP_NONE 0x00 /* No Loopback */ +#define LOOP_EXT 0x02 /* External Loopback */ +#define LOOP_INT 0x04 /* Internal Loopback, excludes MENDEC */ +#define LOOP_INT_MENDEC 0x06 /* Internal Loopback, includes MENDEC */ diff --git a/sys/arch/macppc/dev/dbdma.c b/sys/arch/macppc/dev/dbdma.c new file mode 100644 index 00000000000..14bb871657c --- /dev/null +++ b/sys/arch/macppc/dev/dbdma.c @@ -0,0 +1,130 @@ +/* + * Copyright 1996 1995 by Open Software Foundation, Inc. 1997 1996 1995 1994 1993 1992 1991 + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appears in all copies and + * that both the copyright notice and this permission notice appear in + * supporting documentation. + * + * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, + * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include <sys/param.h> +#include <sys/malloc.h> + +#include <machine/pio.h> +#include <macppc/dev/dbdma.h> + +#define eieio() __asm__ volatile("eieio") + + +static int dbdma_alloc_index = 0; +dbdma_command_t *dbdma_alloc_commands = NULL; + +void +dbdma_start(dmap, commands) + dbdma_regmap_t *dmap; + dbdma_command_t *commands; +{ + unsigned long addr = kvtop((vm_offset_t) commands); + + if (addr & 0xf) + panic("dbdma_start command structure not 16-byte aligned"); + + dmap->d_intselect = 0xff; /* Endian magic - clear out interrupts */ + DBDMA_ST4_ENDIAN(&dmap->d_control, + DBDMA_CLEAR_CNTRL( (DBDMA_CNTRL_ACTIVE | + DBDMA_CNTRL_DEAD | + DBDMA_CNTRL_WAKE | + DBDMA_CNTRL_FLUSH | + DBDMA_CNTRL_PAUSE | + DBDMA_CNTRL_RUN ))); + eieio(); + + while (DBDMA_LD4_ENDIAN(&dmap->d_status) & DBDMA_CNTRL_ACTIVE) + eieio(); + + dmap->d_cmdptrhi = 0; eieio();/* 64-bit not yet */ + DBDMA_ST4_ENDIAN(&dmap->d_cmdptrlo, addr); eieio(); + + DBDMA_ST4_ENDIAN(&dmap->d_control, DBDMA_SET_CNTRL(DBDMA_CNTRL_RUN)); + eieio(); +} + +void +dbdma_stop(dmap) + dbdma_regmap_t *dmap; +{ + out32rb(&dmap->d_control, DBDMA_CLEAR_CNTRL(DBDMA_CNTRL_RUN) | + DBDMA_SET_CNTRL(DBDMA_CNTRL_FLUSH)); + + while (in32rb(&dmap->d_status) & + (DBDMA_CNTRL_ACTIVE|DBDMA_CNTRL_FLUSH)); +} + +void +dbdma_flush(dmap) + dbdma_regmap_t *dmap; +{ + out32rb(&dmap->d_control, DBDMA_SET_CNTRL(DBDMA_CNTRL_FLUSH)); + + while (in32rb(&dmap->d_status) & (DBDMA_CNTRL_FLUSH)); +} + +void +dbdma_reset(dmap) + dbdma_regmap_t *dmap; +{ + out32rb(&dmap->d_control, + DBDMA_CLEAR_CNTRL( (DBDMA_CNTRL_ACTIVE | + DBDMA_CNTRL_DEAD | + DBDMA_CNTRL_WAKE | + DBDMA_CNTRL_FLUSH | + DBDMA_CNTRL_PAUSE | + DBDMA_CNTRL_RUN ))); + + while (in32rb(&dmap->d_status) & DBDMA_CNTRL_RUN); +} + +void +dbdma_continue(dmap) + dbdma_regmap_t *dmap; +{ + out32rb(&dmap->d_control, + DBDMA_SET_CNTRL(DBDMA_CNTRL_RUN | DBDMA_CNTRL_WAKE) | + DBDMA_CLEAR_CNTRL(DBDMA_CNTRL_PAUSE | DBDMA_CNTRL_DEAD)); +} + +void +dbdma_pause(dmap) + dbdma_regmap_t *dmap; +{ + DBDMA_ST4_ENDIAN(&dmap->d_control,DBDMA_SET_CNTRL(DBDMA_CNTRL_PAUSE)); + eieio(); + + while (DBDMA_LD4_ENDIAN(&dmap->d_status) & DBDMA_CNTRL_ACTIVE) + eieio(); +} + +dbdma_command_t * +dbdma_alloc(size) + int size; +{ + u_int buf; + + buf = (u_int)malloc(size + 0x0f, M_DEVBUF, M_WAITOK); + buf = (buf + 0x0f) & ~0x0f; + + return (dbdma_command_t *)buf; +} diff --git a/sys/arch/macppc/dev/dbdma.h b/sys/arch/macppc/dev/dbdma.h new file mode 100644 index 00000000000..672ef018a5d --- /dev/null +++ b/sys/arch/macppc/dev/dbdma.h @@ -0,0 +1,211 @@ +/* + * Copyright 1996 1995 by Open Software Foundation, Inc. 1997 1996 1995 1994 1993 1992 1991 + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appears in all copies and + * that both the copyright notice and this permission notice appear in + * supporting documentation. + * + * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, + * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#ifndef _POWERMAC_DBDMA_H_ +#define _POWERMAC_DBDMA_H_ + +#define DBDMA_CMD_OUT_MORE 0 +#define DBDMA_CMD_OUT_LAST 1 +#define DBDMA_CMD_IN_MORE 2 +#define DBDMA_CMD_IN_LAST 3 +#define DBDMA_CMD_STORE_QUAD 4 +#define DBDMA_CMD_LOAD_QUAD 5 +#define DBDMA_CMD_NOP 6 +#define DBDMA_CMD_STOP 7 + +/* Keys */ + +#define DBDMA_KEY_STREAM0 0 +#define DBDMA_KEY_STREAM1 1 +#define DBDMA_KEY_STREAM2 2 +#define DBDMA_KEY_STREAM3 3 + +/* value 4 is reserved */ +#define DBDMA_KEY_REGS 5 +#define DBDMA_KEY_SYSTEM 6 +#define DBDMA_KEY_DEVICE 7 + +#define DBDMA_INT_NEVER 0 +#define DBDMA_INT_IF_TRUE 1 +#define DBDMA_INT_IF_FALSE 2 +#define DBDMA_INT_ALWAYS 3 + +#define DBDMA_BRANCH_NEVER 0 +#define DBDMA_BRANCH_IF_TRUE 1 +#define DBDMA_BRANCH_IF_FALSE 2 +#define DBDMA_BRANCH_ALWAYS 3 + +#define DBDMA_WAIT_NEVER 0 +#define DBDMA_WAIT_IF_TRUE 1 +#define DBDMA_WAIT_IF_FALSE 2 +#define DBDMA_WAIT_ALWAYS 3 + + +/* Channels */ + +#define DBDMA_SCSI0 0x0 +#define DBDMA_CURIO_SCSI DBDMA_SCSI0 +#define DBDMA_FLOPPY 0x1 +#define DBDMA_ETHERNET_TX 0x2 +#define DBDMA_ETHERNET_RV 0x3 +#define DBDMA_SCC_XMIT_A 0x4 +#define DBDMA_SCC_RECV_A 0x5 +#define DBDMA_SCC_XMIT_B 0x6 +#define DBDMA_SCC_RECV_B 0x7 +#define DBDMA_AUDIO_OUT 0x8 +#define DBDMA_AUDIO_IN 0x9 +#define DBDMA_SCSI1 0xA + +/* Control register values (in little endian) */ + +#define DBDMA_STATUS_MASK 0x000000ff /* Status Mask */ +#define DBDMA_CNTRL_BRANCH 0x00000100 + /* 0x200 reserved */ +#define DBDMA_CNTRL_ACTIVE 0x00000400 +#define DBDMA_CNTRL_DEAD 0x00000800 +#define DBDMA_CNTRL_WAKE 0x00001000 +#define DBDMA_CNTRL_FLUSH 0x00002000 +#define DBDMA_CNTRL_PAUSE 0x00004000 +#define DBDMA_CNTRL_RUN 0x00008000 + +#define DBDMA_SET_CNTRL(x) ( ((x) | (x) << 16) ) +#define DBDMA_CLEAR_CNTRL(x) ( (x) << 16) + + +#define DBDMA_REGMAP(channel) \ + (dbdma_regmap_t *)((v_u_char *) POWERMAC_IO(PCI_DMA_BASE_PHYS) \ + + (channel << 8)) + +/* This struct is layout in little endian format */ + +struct dbdma_command { + u_int16_t d_count; + u_int16_t d_command; + u_int32_t d_address; + u_int32_t d_cmddep; + u_int16_t d_resid; + u_int16_t d_status; +}; + +typedef struct dbdma_command dbdma_command_t; + +#define DBDMA_BUILD_CMD(d, cmd, key, interrupt, wait, branch) { \ + dbdma_st16(&(d)->d_command, \ + ((cmd) << 12) | ((key) << 8) | \ + ((interrupt) << 4) | \ + ((branch) << 2) | (wait)); \ + } + +#define DBDMA_BUILD(d, cmd, key, count, address, interrupt, wait, branch) { \ + dbdma_st16(&(d)->d_command, \ + ((cmd) << 12) | ((key) << 8) | \ + ((interrupt) << 4) | \ + ((branch) << 2) | (wait)); \ + dbdma_st16(&(d)->d_count, count); \ + dbdma_st32(&(d)->d_address, address); \ + (d)->d_resid = 0; \ + (d)->d_status = 0; \ + (d)->d_cmddep = 0; \ + } + +static __inline__ void +dbdma_st32(a, x) + volatile u_int32_t *a; + u_int32_t x; +{ + __asm__ volatile + ("stwbrx %0,0,%1" : : "r" (x), "r" (a) : "memory"); +} + +static __inline__ void +dbdma_st16(a, x) + volatile u_int16_t *a; + u_int16_t x; +{ + __asm__ volatile + ("sthbrx %0,0,%1" : : "r" (x), "r" (a) : "memory"); +} + +static __inline__ u_int32_t +dbdma_ld32(a) + volatile u_int32_t *a; +{ + u_int32_t swap; + + __asm__ volatile + ("lwbrx %0,0,%1" : "=r" (swap) : "r" (a)); + + return swap; +} + +static __inline__ u_int16_t +dbdma_ld16(a) + volatile u_int16_t *a; +{ + u_int16_t swap; + + __asm__ volatile + ("lhbrx %0,0,%1" : "=r" (swap) : "r" (a)); + + return swap; +} + +#define DBDMA_LD4_ENDIAN(a) dbdma_ld32(a) +#define DBDMA_ST4_ENDIAN(a, x) dbdma_st32(a, x) + +/* + * DBDMA Channel layout + * + * NOTE - This structure is in little-endian format. + */ + +struct dbdma_regmap { + unsigned long d_control; /* Control Register */ + unsigned long d_status; /* DBDMA Status Register */ + unsigned long d_cmdptrhi; /* MSB of command pointer (not used yet) */ + unsigned long d_cmdptrlo; /* LSB of command pointer */ + unsigned long d_intselect; /* Interrupt Select */ + unsigned long d_branch; /* Branch selection */ + unsigned long d_wait; /* Wait selection */ + unsigned long d_transmode; /* Transfer modes */ + unsigned long d_dataptrhi; /* MSB of Data Pointer */ + unsigned long d_dataptrlo; /* LSB of Data Pointer */ + unsigned long d_reserved; /* Reserved for the moment */ + unsigned long d_branchptrhi; /* MSB of Branch Pointer */ + unsigned long d_branchptrlo; /* LSB of Branch Pointer */ + /* The remaining fields are undefinied and unimplemented */ +}; + +typedef volatile struct dbdma_regmap dbdma_regmap_t; + +/* DBDMA routines */ + +void dbdma_start(dbdma_regmap_t *channel, dbdma_command_t *commands); +void dbdma_stop(dbdma_regmap_t *channel); +void dbdma_flush(dbdma_regmap_t *channel); +void dbdma_reset(dbdma_regmap_t *channel); +void dbdma_continue(dbdma_regmap_t *channel); +void dbdma_pause(dbdma_regmap_t *channel); + +dbdma_command_t *dbdma_alloc(int); /* Allocate command structures */ + +#endif /* !defined(_POWERMAC_DBDMA_H_) */ diff --git a/sys/arch/macppc/dev/esp.c b/sys/arch/macppc/dev/esp.c new file mode 100644 index 00000000000..7752213c5c8 --- /dev/null +++ b/sys/arch/macppc/dev/esp.c @@ -0,0 +1,583 @@ +/* $NetBSD: esp.c,v 1.1 1998/05/15 10:15:48 tsubai Exp $ */ + +/*- + * Copyright (c) 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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 the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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. + */ + +/* + * Copyright (c) 1996 Charles M. Hannum. 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 Charles M. Hannum. + * 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. + */ + +/* + * Copyright (c) 1994 Peter Galbavy + * Copyright (c) 1995 Paul Kranenburg + * 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 Peter Galbavy + * 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. + */ + +/* + * Based on aic6360 by Jarle Greipsland + * + * Acknowledgements: Many of the algorithms used in this driver are + * inspired by the work of Julian Elischer (julian@tfs.com) and + * Charles Hannum (mycroft@duality.gnu.ai.mit.edu). Thanks a million! + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/device.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/queue.h> +#include <sys/malloc.h> + +#include <vm/vm_param.h> /* for trunc_page */ + +#include <dev/scsipi/scsi_all.h> +#include <dev/scsipi/scsipi_all.h> +#include <dev/scsipi/scsiconf.h> +#include <dev/scsipi/scsi_message.h> + +#include <dev/ofw/openfirm.h> + +#include <machine/cpu.h> +#include <machine/autoconf.h> +#include <machine/pio.h> + +#include <dev/ic/ncr53c9xreg.h> +#include <dev/ic/ncr53c9xvar.h> + +#include <macppc/dev/dbdma.h> +#include <macppc/dev/espvar.h> + +void espattach __P((struct device *, struct device *, void *)); +int espmatch __P((struct device *, struct cfdata *, void *)); + +/* Linkup to the rest of the kernel */ +struct cfattach esp_ca = { + sizeof(struct esp_softc), espmatch, espattach +}; + +struct scsipi_adapter esp_switch = { + ncr53c9x_scsi_cmd, + minphys, /* no max at this level; handled by DMA code */ + NULL, + NULL, +}; + +struct scsipi_device esp_dev = { + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ +}; + +/* + * Functions and the switch for the MI code. + */ +u_char esp_read_reg __P((struct ncr53c9x_softc *, int)); +void esp_write_reg __P((struct ncr53c9x_softc *, int, u_char)); +int esp_dma_isintr __P((struct ncr53c9x_softc *)); +void esp_dma_reset __P((struct ncr53c9x_softc *)); +int esp_dma_intr __P((struct ncr53c9x_softc *)); +int esp_dma_setup __P((struct ncr53c9x_softc *, caddr_t *, + size_t *, int, size_t *)); +void esp_dma_go __P((struct ncr53c9x_softc *)); +void esp_dma_stop __P((struct ncr53c9x_softc *)); +int esp_dma_isactive __P((struct ncr53c9x_softc *)); + +struct ncr53c9x_glue esp_glue = { + esp_read_reg, + esp_write_reg, + esp_dma_isintr, + esp_dma_reset, + esp_dma_intr, + esp_dma_setup, + esp_dma_go, + esp_dma_stop, + esp_dma_isactive, + NULL, /* gl_clear_latched_intr */ +}; + +static int espdmaintr __P((struct esp_softc *)); +static void esp_shutdownhook __P((void *)); + +int +espmatch(parent, cf, aux) + struct device *parent; + struct cfdata *cf; + void *aux; +{ + struct confargs *ca = aux; + + if (strcmp(ca->ca_name, "53c94") != 0) + return 0; + + if (ca->ca_nreg != 16) + return 0; + if (ca->ca_nintr != 8) + return 0; + + return 1; +} + +/* + * Attach this instance, and then all the sub-devices + */ +void +espattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + register struct confargs *ca = aux; + struct esp_softc *esc = (void *)self; + struct ncr53c9x_softc *sc = &esc->sc_ncr53c9x; + u_int *reg; + int sz; + + /* + * Set up glue for MI code early; we use some of it here. + */ + sc->sc_glue = &esp_glue; + + esc->sc_node = ca->ca_node; + esc->sc_pri = ca->ca_intr[0]; + printf(" irq %d", esc->sc_pri); + + /* + * Map my registers in. + */ + reg = ca->ca_reg; + esc->sc_reg = mapiodev(ca->ca_baseaddr + reg[0], reg[1]); + esc->sc_dmareg = mapiodev(ca->ca_baseaddr + reg[2], reg[3]); + + /* Allocate 16-byte aligned dma command space */ + esc->sc_dmacmd = dbdma_alloc(sizeof(dbdma_command_t) * 20); + + /* Other settings */ + sc->sc_id = 7; + sz = OF_getprop(ca->ca_node, "clock-frequency", + &sc->sc_freq, sizeof(int)); + if (sz != sizeof(int)) + sc->sc_freq = 25000000; + + /* gimme Mhz */ + sc->sc_freq /= 1000000; + + /* esc->sc_dma->sc_esp = esc;*/ + + /* + * XXX More of this should be in ncr53c9x_attach(), but + * XXX should we really poke around the chip that much in + * XXX the MI code? Think about this more... + */ + + /* + * Set up static configuration info. + */ + sc->sc_cfg1 = sc->sc_id | NCRCFG1_PARENB; + sc->sc_cfg2 = NCRCFG2_SCSI2; /* | NCRCFG2_FE */ + sc->sc_cfg3 = NCRCFG3_CDB; + sc->sc_rev = NCR_VARIANT_NCR53C94; + + /* + * XXX minsync and maxxfer _should_ be set up in MI code, + * XXX but it appears to have some dependency on what sort + * XXX of DMA we're hooked up to, etc. + */ + + /* + * This is the value used to start sync negotiations + * Note that the NCR register "SYNCTP" is programmed + * in "clocks per byte", and has a minimum value of 4. + * The SCSI period used in negotiation is one-fourth + * of the time (in nanoseconds) needed to transfer one byte. + * Since the chip's clock is given in MHz, we have the following + * formula: 4 * period = (1000 / freq) * 4 + */ + sc->sc_minsync = 1000 / sc->sc_freq; + + sc->sc_maxxfer = 64 * 1024; + + /* and the interuppts */ + intr_establish(esc->sc_pri, IST_LEVEL, IPL_BIO, (void *)ncr53c9x_intr, + sc); + + /* Do the common parts of attachment. */ + ncr53c9x_attach(sc, &esp_switch, &esp_dev); + + /* Turn on target selection using the `dma' method */ + ncr53c9x_dmaselect = 1; + + /* Reset SCSI bus when halt. */ + shutdownhook_establish(esp_shutdownhook, sc); +} + +/* + * Glue functions. + */ + +u_char +esp_read_reg(sc, reg) + struct ncr53c9x_softc *sc; + int reg; +{ + struct esp_softc *esc = (struct esp_softc *)sc; + + return in8(&esc->sc_reg[reg * 16]); + /*return (esc->sc_reg[reg * 16]);*/ +} + +void +esp_write_reg(sc, reg, val) + struct ncr53c9x_softc *sc; + int reg; + u_char val; +{ + struct esp_softc *esc = (struct esp_softc *)sc; + u_char v = val; + + out8(&esc->sc_reg[reg * 16], v); + /*esc->sc_reg[reg * 16] = v;*/ +} + +int +esp_dma_isintr(sc) + struct ncr53c9x_softc *sc; +{ + return esp_read_reg(sc, NCR_STAT) & NCRSTAT_INT; +} + +void +esp_dma_reset(sc) + struct ncr53c9x_softc *sc; +{ + struct esp_softc *esc = (struct esp_softc *)sc; + + dbdma_stop(esc->sc_dmareg); + esc->sc_dmaactive = 0; +} + +int +esp_dma_intr(sc) + struct ncr53c9x_softc *sc; +{ + struct esp_softc *esc = (struct esp_softc *)sc; + + return (espdmaintr(esc)); +} + +int +esp_dma_setup(sc, addr, len, datain, dmasize) + struct ncr53c9x_softc *sc; + caddr_t *addr; + size_t *len; + int datain; + size_t *dmasize; +{ + struct esp_softc *esc = (struct esp_softc *)sc; + dbdma_command_t *cmdp; + u_int cmd; + u_int va; + int count, offset; + + cmdp = esc->sc_dmacmd; + cmd = datain ? DBDMA_CMD_IN_MORE : DBDMA_CMD_OUT_MORE; + + count = *dmasize; + + if (count / NBPG > 32) + panic("esp: transfer size >= 128k"); + + esc->sc_dmaaddr = addr; + esc->sc_dmalen = len; + esc->sc_dmasize = count; + + va = (u_int)*esc->sc_dmaaddr; + offset = va & PGOFSET; + + /* if va is not page-aligned, setup the first page */ + if (offset != 0) { + int rest = NBPG - offset; /* the rest of the page */ + + if (count > rest) { /* if continues to next page */ + DBDMA_BUILD(cmdp, cmd, 0, rest, kvtop((caddr_t)va), + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, + DBDMA_BRANCH_NEVER); + count -= rest; + va += rest; + cmdp++; + } + } + + /* now va is page-aligned */ + while (count > NBPG) { + DBDMA_BUILD(cmdp, cmd, 0, NBPG, kvtop((caddr_t)va), + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + count -= NBPG; + va += NBPG; + cmdp++; + } + + /* the last page (count <= NBPG here) */ + cmd = datain ? DBDMA_CMD_IN_LAST : DBDMA_CMD_OUT_LAST; + DBDMA_BUILD(cmdp, cmd , 0, count, kvtop((caddr_t)va), + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + cmdp++; + + DBDMA_BUILD(cmdp, DBDMA_CMD_STOP, 0, 0, 0, + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + + esc->sc_dma_direction = datain ? D_WRITE : 0; + + return 0; +} + +void +esp_dma_go(sc) + struct ncr53c9x_softc *sc; +{ + struct esp_softc *esc = (struct esp_softc *)sc; + + dbdma_start(esc->sc_dmareg, esc->sc_dmacmd); + esc->sc_dmaactive = 1; +} + +void +esp_dma_stop(sc) + struct ncr53c9x_softc *sc; +{ + struct esp_softc *esc = (struct esp_softc *)sc; + + dbdma_stop(esc->sc_dmareg); + esc->sc_dmaactive = 0; +} + +int +esp_dma_isactive(sc) + struct ncr53c9x_softc *sc; +{ + struct esp_softc *esc = (struct esp_softc *)sc; + + return (esc->sc_dmaactive); +} + + +/* + * Pseudo (chained) interrupt from the esp driver to kick the + * current running DMA transfer. I am replying on espintr() to + * pickup and clean errors for now + * + * return 1 if it was a DMA continue. + */ +int +espdmaintr(sc) + struct esp_softc *sc; +{ + struct ncr53c9x_softc *nsc = (struct ncr53c9x_softc *)sc; + int trans, resid; + u_long csr = sc->sc_dma_direction; + +#if 0 + if (csr & D_ERR_PEND) { + DMACSR(sc) &= ~D_EN_DMA; /* Stop DMA */ + DMACSR(sc) |= D_INVALIDATE; + printf("%s: error: csr=%s\n", nsc->sc_dev.dv_xname, + bitmask_snprintf(csr, DMACSRBITS, bits, sizeof(bits))); + return -1; + } +#endif + + /* This is an "assertion" :) */ + if (sc->sc_dmaactive == 0) + panic("dmaintr: DMA wasn't active"); + + /* dbdma_flush(sc->sc_dmareg); */ + + /* DMA has stopped */ + dbdma_stop(sc->sc_dmareg); + sc->sc_dmaactive = 0; + + if (sc->sc_dmasize == 0) { + /* A "Transfer Pad" operation completed */ + NCR_DMA(("dmaintr: discarded %d bytes (tcl=%d, tcm=%d)\n", + NCR_READ_REG(nsc, NCR_TCL) | + (NCR_READ_REG(nsc, NCR_TCM) << 8), + NCR_READ_REG(nsc, NCR_TCL), + NCR_READ_REG(nsc, NCR_TCM))); + return 0; + } + + resid = 0; + /* + * If a transfer onto the SCSI bus gets interrupted by the device + * (e.g. for a SAVEPOINTER message), the data in the FIFO counts + * as residual since the ESP counter registers get decremented as + * bytes are clocked into the FIFO. + */ + if (!(csr & D_WRITE) && + (resid = (NCR_READ_REG(nsc, NCR_FFLAG) & NCRFIFO_FF)) != 0) { + NCR_DMA(("dmaintr: empty esp FIFO of %d ", resid)); + } + + if ((nsc->sc_espstat & NCRSTAT_TC) == 0) { + /* + * `Terminal count' is off, so read the residue + * out of the ESP counter registers. + */ + resid += (NCR_READ_REG(nsc, NCR_TCL) | + (NCR_READ_REG(nsc, NCR_TCM) << 8) | + ((nsc->sc_cfg2 & NCRCFG2_FE) + ? (NCR_READ_REG(nsc, NCR_TCH) << 16) + : 0)); + + if (resid == 0 && sc->sc_dmasize == 65536 && + (nsc->sc_cfg2 & NCRCFG2_FE) == 0) + /* A transfer of 64K is encoded as `TCL=TCM=0' */ + resid = 65536; + } + + trans = sc->sc_dmasize - resid; + if (trans < 0) { /* transferred < 0 ? */ +#if 0 + /* + * This situation can happen in perfectly normal operation + * if the ESP is reselected while using DMA to select + * another target. As such, don't print the warning. + */ + printf("%s: xfer (%d) > req (%d)\n", + sc->sc_dev.dv_xname, trans, sc->sc_dmasize); +#endif + trans = sc->sc_dmasize; + } + + NCR_DMA(("dmaintr: tcl=%d, tcm=%d, tch=%d; trans=%d, resid=%d\n", + NCR_READ_REG(nsc, NCR_TCL), + NCR_READ_REG(nsc, NCR_TCM), + (nsc->sc_cfg2 & NCRCFG2_FE) + ? NCR_READ_REG(nsc, NCR_TCH) : 0, + trans, resid)); + + if (csr & D_WRITE) { + vm_offset_t va = (vm_offset_t)*sc->sc_dmaaddr; + int len = trans; + + va = trunc_page(va); + while (len > 0) { + flushcache((void *)kvtop((caddr_t)va), NBPG); + va += NBPG; + len -= NBPG; + } + } + + *sc->sc_dmalen -= trans; + *sc->sc_dmaaddr += trans; + +#if 0 /* this is not normal operation just yet */ + if (*sc->sc_dmalen == 0 || + nsc->sc_phase != nsc->sc_prevphase) + return 0; + + /* and again */ + dma_start(sc, sc->sc_dmaaddr, sc->sc_dmalen, DMACSR(sc) & D_WRITE); + return 1; +#endif + return 0; +} + +void +esp_shutdownhook(arg) + void *arg; +{ + struct ncr53c9x_softc *sc = arg; + + NCRCMD(sc, NCRCMD_RSTSCSI); +} diff --git a/sys/arch/macppc/dev/espvar.h b/sys/arch/macppc/dev/espvar.h new file mode 100644 index 00000000000..6e14fe97ef7 --- /dev/null +++ b/sys/arch/macppc/dev/espvar.h @@ -0,0 +1,62 @@ +/* $NetBSD: espvar.h,v 1.1 1998/05/15 10:15:48 tsubai Exp $ */ + +/*- + * Copyright (c) 1997 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, + * NASA Ames Research Center. + * + * 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 the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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. + */ + +struct esp_softc { + struct ncr53c9x_softc sc_ncr53c9x; /* glue to MI code */ + + struct intrhand sc_ih; /* intr handler */ + + volatile u_char *sc_reg; /* the registers */ + + dbdma_regmap_t *sc_dmareg; /* DMA registers */ + dbdma_command_t *sc_dmacmd; /* command area for DMA */ + + /* openprom stuff */ + int sc_node; /* PROM node ID */ + int sc_pri; /* SBUS priority */ + + size_t sc_dmasize; + caddr_t *sc_dmaaddr; + size_t *sc_dmalen; + int sc_dmaactive; + int sc_dma_direction; +}; + +#define D_WRITE 1 + diff --git a/sys/arch/macppc/dev/font_8x16.c b/sys/arch/macppc/dev/font_8x16.c new file mode 100644 index 00000000000..4e467acfc96 --- /dev/null +++ b/sys/arch/macppc/dev/font_8x16.c @@ -0,0 +1,556 @@ +/* $NetBSD: font_8x16.c,v 1.1 1998/05/15 10:15:48 tsubai Exp $ */ + +/* + * Copyright (c) 1992, 1993, 1994 Hellmuth Michaelis and Joerg Wunsch + * + * 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 + * Hellmuth Michaelis and Joerg Wunsch + * 4. The name authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. + */ + +/* + * Translated into compiler and human readable for for the Atari-TT port of + * NetBSD by Leo Weppelman. + * + * Reorganized and edited some chars to fit the iso-8859-1 fontset by + * Thomas Gerner + */ + +unsigned char fontdata_8x16[] = { +/* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x01 */ 0x00, 0x18, 0x18, 0x3c, 0x3c, 0x7e, 0x7e, 0xff, + 0xff, 0x7e, 0x7e, 0x3c, 0x3c, 0x18, 0x18, 0x00, +/* 0x02 */ 0x42, 0x99, 0x99, 0x42, 0x42, 0x99, 0x99, 0x42, + 0x42, 0x99, 0x99, 0x42, 0x42, 0x99, 0x99, 0x42, +/* 0x03 */ 0x00, 0x00, 0x90, 0x90, 0xf0, 0x90, 0x90, 0x00, + 0x3e, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, +/* 0x04 */ 0x00, 0x00, 0xf0, 0x80, 0xe0, 0x80, 0x80, 0x00, + 0x1e, 0x10, 0x1c, 0x10, 0x10, 0x00, 0x00, 0x00, +/* 0x05 */ 0x00, 0x00, 0x60, 0x90, 0x80, 0x90, 0x60, 0x00, + 0x1c, 0x12, 0x1c, 0x12, 0x12, 0x00, 0x00, 0x00, +/* 0x06 */ 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0xf0, 0x00, + 0x1e, 0x10, 0x1c, 0x10, 0x10, 0x00, 0x00, 0x00, +/* 0x07 */ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, + 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, +/* 0x09 */ 0x00, 0x00, 0x88, 0x98, 0xa8, 0xc8, 0x88, 0x00, + 0x10, 0x10, 0x10, 0x10, 0x1e, 0x00, 0x00, 0x00, +/* 0x0a */ 0x00, 0x00, 0x88, 0x88, 0x50, 0x50, 0x20, 0x00, + 0x3e, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, +/* 0x0b */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x0c */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +/* 0x0d */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +/* 0x0e */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x0f */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +/* 0x10 */ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x11 */ 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x12 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x13 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, +/* 0x14 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, +/* 0x15 */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +/* 0x16 */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +/* 0x17 */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +/* 0x19 */ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +/* 0x1a */ 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, + 0x0c, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, +/* 0x1b */ 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, + 0x30, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, +/* 0x1c */ 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, + 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, +/* 0x1d */ 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, + 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x1e */ 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, + 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, +/* 0x1f */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* ' ' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* '!' */ 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, + 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +/* '"' */ 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* '#' */ 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, + 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, +/* '$' */ 0x00, 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, + 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, +/* '%' */ 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, + 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, +/* '&' */ 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +/* ''' */ 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* '(' */ 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, +/* ')' */ 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, +/* '*' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, + 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* '+' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* ',' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, +/* '-' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* '.' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +/* '/' */ 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, + 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, +/* '0' */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xce, 0xde, 0xf6, + 0xe6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* '1' */ 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, +/* '2' */ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, + 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, +/* '3' */ 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, + 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* '4' */ 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, + 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, +/* '5' */ 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, + 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* '6' */ 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* '7' */ 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, + 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, +/* '8' */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* '9' */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, + 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, +/* ':' */ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, +/* ';' */ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, +/* '<' */ 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, + 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, +/* '=' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* '>' */ 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, + 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, +/* '?' */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, + 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +/* '@' */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde, + 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 'A' */ 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, + 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +/* 'B' */ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, + 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, +/* 'C' */ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, + 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, +/* 'D' */ 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, +/* 'E' */ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, + 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, +/* 'F' */ 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, + 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, +/* 'G' */ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, + 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, +/* 'H' */ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +/* 'I' */ 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +/* 'J' */ 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, +/* 'K' */ 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, + 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, +/* 'L' */ 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, + 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, +/* 'M' */ 0x00, 0x00, 0xc6, 0xee, 0xfe, 0xfe, 0xd6, 0xc6, + 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +/* 'N' */ 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, + 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +/* 'O' */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 'P' */ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, + 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, +/* 'Q' */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, +/* 'R' */ 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, + 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, +/* 'S' */ 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, + 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 'T' */ 0x00, 0x00, 0x7e, 0x7e, 0x5a, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +/* 'U' */ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 'V' */ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, +/* 'W' */ 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xd6, + 0xd6, 0xfe, 0xee, 0x6c, 0x00, 0x00, 0x00, 0x00, +/* 'X' */ 0x00, 0x00, 0xc6, 0xc6, 0x6c, 0x7c, 0x38, 0x38, + 0x7c, 0x6c, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +/* 'Y' */ 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +/* 'Z' */ 0x00, 0x00, 0xfe, 0xc6, 0x86, 0x0c, 0x18, 0x30, + 0x60, 0xc2, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, +/* '[' */ 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, +/* '\' */ 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, + 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, +/* ']' */ 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, +/* '^' */ 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* '_' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, +/* '`' */ 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 'a' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +/* 'b' */ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, + 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 'c' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 'd' */ 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +/* 'e' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 'f' */ 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, + 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, +/* 'g' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, +/* 'h' */ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, + 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, +/* 'i' */ 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +/* 'j' */ 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, +/* 'k' */ 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, + 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, +/* 'l' */ 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +/* 'm' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xfe, 0xd6, + 0xd6, 0xd6, 0xd6, 0xc6, 0x00, 0x00, 0x00, 0x00, +/* 'n' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, +/* 'o' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 'p' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, +/* 'q' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, +/* 'r' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, + 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, +/* 's' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, + 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 't' */ 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, + 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, +/* 'u' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +/* 'v' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0x6c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, +/* 'w' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xd6, + 0xd6, 0xd6, 0xfe, 0x6c, 0x00, 0x00, 0x00, 0x00, +/* 'x' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, + 0x38, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, +/* 'y' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, +/* 'z' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, + 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, +/* '{' */ 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, + 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, +/* '|' */ 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +/* '}' */ 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, + 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, +/* '~' */ 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x7f */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, + 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x80 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x81 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x82 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x83 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x84 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x85 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x86 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x87 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x88 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x89 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x8a */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x8b */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x8c */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x8d */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x8e */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x8f */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x91 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x92 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x93 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x94 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x95 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x96 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x97 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x98 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x99 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x9a */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x9b */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x9c */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x9d */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x9e */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x9f */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xa0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xa1 */ 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, + 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, +/* 0xa2 */ 0x00, 0x18, 0x18, 0x3c, 0x66, 0x60, 0x60, 0x60, + 0x66, 0x3c, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +/* 0xa3 */ 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, + 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, +/* 0xa4 */ 0xc3, 0x3c, 0x66, 0x42, 0x66, 0x3c, 0xc3, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xa5 */ 0x00, 0x00, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, + 0x7e, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, +/* 0xa6 */ 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, +/* 0xa7 */ 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, + 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, +/* 0xa8 */ 0x00, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xa9 */ 0x00, 0x7c, 0xc6, 0x82, 0x9a, 0xa6, 0xa2, 0xa6, + 0x9a, 0x82, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xaa */ 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xab */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, + 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xac */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, + 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xad */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xae */ 0x00, 0x7c, 0xc6, 0x82, 0xba, 0xa6, 0xba, 0xaa, + 0xa6, 0x82, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xaf */ 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xb0 */ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xb1 */ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, + 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, +/* 0xb2 */ 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xb3 */ 0x00, 0x70, 0xd8, 0x30, 0x30, 0xd8, 0x70, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xb4 */ 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xb5 */ 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, +/* 0xb6 */ 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, + 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, +/* 0xb7 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xb8 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0x00, +/* 0xb9 */ 0x00, 0x30, 0x70, 0xf0, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xba */ 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xbb */ 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, + 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xbc */ 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, + 0x66, 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, 0x00, +/* 0xbd */ 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, + 0x60, 0xdc, 0x86, 0x0c, 0x18, 0x3e, 0x00, 0x00, +/* 0xbe */ 0x00, 0xc0, 0x60, 0xc2, 0x66, 0xcc, 0x18, 0x30, + 0x66, 0xce, 0x9e, 0x3e, 0x06, 0x06, 0x00, 0x00, +/* 0xbf */ 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, + 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xc0 */ 0x18, 0x0c, 0x06, 0x00, 0x38, 0x6c, 0xc6, 0xc6, + 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +/* 0xc1 */ 0x18, 0x30, 0x60, 0x00, 0x38, 0x6c, 0xc6, 0xc6, + 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +/* 0xc2 */ 0x10, 0x38, 0x6c, 0x00, 0x38, 0x6c, 0xc6, 0xc6, + 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +/* 0xc3 */ 0x76, 0xdc, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, + 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +/* 0xc4 */ 0xc6, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, + 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +/* 0xc5 */ 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, + 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +/* 0xc6 */ 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, + 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, +/* 0xc7 */ 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, + 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, +/* 0xc8 */ 0x18, 0x0c, 0x06, 0x00, 0xfe, 0x66, 0x60, 0x7c, + 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, +/* 0xc9 */ 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, + 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, +/* 0xca */ 0x10, 0x38, 0x6c, 0x00, 0xfe, 0x66, 0x60, 0x7c, + 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, +/* 0xcb */ 0x00, 0xc6, 0x00, 0xfe, 0x66, 0x60, 0x60, 0x7c, + 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, +/* 0xcc */ 0x18, 0x0c, 0x06, 0x00, 0x3c, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +/* 0xcd */ 0x18, 0x30, 0x60, 0x00, 0x3c, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +/* 0xce */ 0x10, 0x38, 0x6c, 0x00, 0x3c, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +/* 0xcf */ 0x00, 0x66, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +/* 0xd0 */ 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0xf6, 0x66, + 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, +/* 0xd1 */ 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, + 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, +/* 0xd2 */ 0x18, 0x0c, 0x06, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xd3 */ 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xd4 */ 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xd5 */ 0x76, 0xdc, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xd6 */ 0xc6, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xd7 */ 0x00, 0x00, 0x00, 0x00, 0xc6, 0x6c, 0x38, 0x38, + 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xd8 */ 0x00, 0x06, 0x7e, 0xce, 0xce, 0xce, 0xd6, 0xd6, + 0xe6, 0xe6, 0xe6, 0xfc, 0xc0, 0x00, 0x00, 0x00, +/* 0xd9 */ 0x18, 0x0c, 0x06, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xda */ 0x18, 0x30, 0x60, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xdb */ 0x10, 0x38, 0x6c, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xdc */ 0xc6, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xdd */ 0x18, 0x30, 0x60, 0x00, 0x66, 0x66, 0x66, 0x3c, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +/* 0xde */ 0x00, 0xf0, 0x60, 0x7c, 0x66, 0x66, 0x66, 0x7c, + 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, +/* 0xdf */ 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, + 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, +/* 0xe0 */ 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +/* 0xe1 */ 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +/* 0xe2 */ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +/* 0xe3 */ 0x00, 0x00, 0x76, 0xdc, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +/* 0xe4 */ 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +/* 0xe5 */ 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +/* 0xe6 */ 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xb2, 0x32, + 0x7e, 0xd8, 0xd8, 0x6e, 0x00, 0x00, 0x00, 0x00, +/* 0xe7 */ 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, + 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00, +/* 0xe8 */ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xe9 */ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xea */ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xeb */ 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, + 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xec */ 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +/* 0xed */ 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +/* 0xee */ 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +/* 0xef */ 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, +/* 0xf0 */ 0x00, 0x00, 0x3e, 0x30, 0x18, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xf1 */ 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, +/* 0xf2 */ 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xf3 */ 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xf4 */ 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xf5 */ 0x00, 0x00, 0x76, 0xdc, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xf6 */ 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, +/* 0xf7 */ 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0xf8 */ 0x00, 0x00, 0x00, 0x00, 0x06, 0x7e, 0xce, 0xce, + 0xd6, 0xe6, 0xe6, 0xfc, 0xc0, 0x00, 0x00, 0x00, +/* 0xf9 */ 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +/* 0xfa */ 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +/* 0xfb */ 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +/* 0xfc */ 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, + 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, +/* 0xfd */ 0x00, 0x18, 0x30, 0x60, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, +/* 0xfe */ 0x00, 0x00, 0x00, 0xf0, 0x60, 0x7c, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, +/* 0xff */ 0x00, 0xc6, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, + 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00 +}; diff --git a/sys/arch/macppc/dev/grf.c b/sys/arch/macppc/dev/grf.c new file mode 100644 index 00000000000..155b9d5831c --- /dev/null +++ b/sys/arch/macppc/dev/grf.c @@ -0,0 +1,286 @@ +/* $NetBSD: grf.c,v 1.1 1998/05/15 10:15:48 tsubai Exp $ */ + +/* + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department. + * + * 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 the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + * + * from: Utah $Hdr: grf.c 1.31 91/01/21$ + * + * @(#)grf.c 7.8 (Berkeley) 5/7/91 + */ + +/* + * Graphics display driver for the PowerMacintosh. + * This is the hardware-independent portion of the driver. + * Hardware access is through the grfdev routines below. + */ + +#include <sys/param.h> + +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/file.h> +#include <sys/malloc.h> +#include <sys/mman.h> +#include <sys/poll.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/systm.h> + +#include <vm/vm.h> +#include <vm/vm_kern.h> + +#include <machine/bus.h> +#include <machine/cpu.h> +#include <machine/grfioctl.h> + +#include <macppc/dev/itevar.h> +#include <macppc/dev/grfvar.h> + +#include "grf.h" +#include "ite.h" + +#if NITE == 0 +#define iteon(u,f) +#define iteoff(u,f) +#endif + +int grfmatch __P((struct device *, struct cfdata *, void *)); +void grfattach __P((struct device *, struct device *, void *)); +int grfbusprint(); + +struct cfattach grf_ca = { + sizeof(struct grf_softc), grfmatch, grfattach +}; + +extern struct cfdriver grf_cd; + +#ifdef DEBUG +#define GRF_DEBUG +#endif + +#ifdef GRF_DEBUG +#define GDB_DEVNO 0x01 +#define GDB_MMAP 0x02 +#define GDB_IOMAP 0x04 +#define GDB_LOCK 0x08 +int grfdebug = 0; +#endif + +int +grfmatch(parent, cf, aux) + struct device *parent; + struct cfdata *cf; + void *aux; +{ + struct grf_attach_args *ga = aux; + + if (strcmp(ga->ga_name, "grf") != 0) + return 0; + return 1; +} + +void +grfattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct grf_softc *sc = (struct grf_softc *)self; + struct grf_attach_args *ga = aux; + + printf("\n"); + + /* Load forwarded pointers. */ + sc->sc_display = ga->ga_display; + sc->sc_mode = ga->ga_mode; + + sc->sc_flags = GF_ALIVE; /* XXX bogus */ + + /* + * Attach ite semantics to the grf. Change the name, forward + * everything else. + */ + ga->ga_name = "ite"; + config_found(self, ga, grfbusprint); +} + +/*ARGSUSED*/ +int +grfopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + register struct grf_softc *gp; + int unit; + int error = 0; + + unit = GRFUNIT(dev); + gp = grf_cd.cd_devs[unit]; + + if (unit >= grf_cd.cd_ndevs || (gp->sc_flags & GF_ALIVE) == 0) + return (ENXIO); + + if ((gp->sc_flags & (GF_OPEN | GF_EXCLUDE)) == (GF_OPEN | GF_EXCLUDE)) + return (EBUSY); + + /* + * First open. + * XXX: always put in graphics mode. + */ + if ((gp->sc_flags & GF_OPEN) == 0) { + gp->sc_flags |= GF_OPEN; + /*error = grfon(dev);*/ + } + return (error); +} + +/*ARGSUSED*/ +int +grfclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + register struct grf_softc *gp; + + gp = grf_cd.cd_devs[GRFUNIT(dev)]; + + /*(void)grfoff(dev);*/ + gp->sc_flags &= GF_ALIVE; + + return (0); +} + +/*ARGSUSED*/ +int +grfioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; +{ + struct grf_softc *gp; + struct grfinfo *gm; + int error; + int unit = GRFUNIT(dev); + + gp = grf_cd.cd_devs[unit]; + gm = gp->sc_display; + error = 0; + + switch (cmd) { + case GRFIOCGINFO: + bcopy(gm, data, sizeof(struct grfinfo)); + break; + case GRFIOCON: + error = grfon(dev); + break; + case GRFIOCOFF: + error = grfoff(dev); + break; + default: + error = EINVAL; + break; + } + return (error); +} + +/*ARGSUSED*/ +int +grfpoll(dev, events, p) + dev_t dev; + int events; + struct proc *p; +{ + return (events & (POLLOUT | POLLWRNORM)); +} + +/*ARGSUSED*/ +int +grfmmap(dev, off, prot) + dev_t dev; + int off; + int prot; +{ + struct grf_softc *gp = grf_cd.cd_devs[GRFUNIT(dev)]; + struct grfinfo *gi = gp->sc_display; + + if (off >= 0 && off < gi->gd_devsize) + return (int)gi->gd_devaddr + off; + + return -1; +} + +int +grfon(dev) + dev_t dev; +{ + int unit = GRFUNIT(dev); + struct grf_softc *gp; + + gp = grf_cd.cd_devs[unit]; + + /* + * XXX: iteoff call relies on devices being in same order + * as ITEs and the fact that iteoff only uses the minor part + * of the dev arg. + */ + iteoff(unit, 3); + + return (*gp->sc_mode)(gp, GM_GRFON, NULL); +} + +int +grfoff(dev) + dev_t dev; +{ + int unit = GRFUNIT(dev); + struct grf_softc *gp; + int error; + + gp = grf_cd.cd_devs[unit]; + + error = (*gp->sc_mode)(gp, GM_GRFOFF, NULL); + + /* XXX: see comment for iteoff above */ + iteon(unit, 2); + + return (error); +} diff --git a/sys/arch/macppc/dev/grf_ati.c b/sys/arch/macppc/dev/grf_ati.c new file mode 100644 index 00000000000..c0e10016dde --- /dev/null +++ b/sys/arch/macppc/dev/grf_ati.c @@ -0,0 +1,130 @@ +#include <sys/param.h> +#include <sys/device.h> + +#include <machine/pio.h> +#include <machine/autoconf.h> +#include <machine/grfioctl.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> +#include <dev/ofw/openfirm.h> + +#include <macppc/dev/grfvar.h> + +caddr_t videoaddr; +int videorowbytes; +int videobitdepth; +int videosize; +static struct grfinfo ati_display; + +static void grf_ati_attach __P((struct device *, struct device *, void *)); +static int grf_ati_match __P((struct device *, struct cfdata *, void *)); + +static int ati_mode __P((struct grf_softc *, int, void *)); + +struct grf_ati_softc { + struct device sc_dev; +}; + +struct cfattach grfati_ca = { + sizeof(struct grf_ati_softc), grf_ati_match, grf_ati_attach +}; + +int +grf_ati_match(parent, cf, aux) + struct device *parent; + struct cfdata *cf; + void *aux; +{ + struct pci_attach_args *pa = aux; + + if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY && + PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_VGA && + /* PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_ATI_MACH64_GX && */ + PCI_VENDOR(pa->pa_id) == PCI_VENDOR_ATI) + return 1; + + return 0; +} + +void +grf_ati_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct pci_attach_args *pa = (struct pci_attach_args *)aux; + int csr; + int width, height, node; + u_int reg[5]; + caddr_t regaddr; + struct grf_attach_args ga; + char type[32]; + extern int console_node; + + printf("\n"); + csr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); + + csr |= PCI_COMMAND_MEM_ENABLE; + pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, csr); + + /* + * OF_open("/bandit/ATY,mach64") hangs... so we can use + * framebuffer when it is specified as a console. + */ + + node = console_node; + if (node == -1) + return; + + bzero(type, sizeof(type)); + OF_getprop(node, "device_type", type, sizeof(type)); + if (strcmp(type, "display") != 0) + return; + + OF_getprop(node, "assigned-addresses", reg, sizeof(reg)); + + regaddr = mapiodev(reg[2] + 0x800000 - 0x400, 0x400); + videoaddr = mapiodev(reg[2] + 0x400 , reg[4] - 0x400); + + + /* + * set character color to rgb:ff/ff/ff + */ +#define DAC_W_INDEX 0xc0 +#define DAC_DATA 0xc1 + + out8(regaddr + DAC_W_INDEX, 255); + out8(regaddr + DAC_DATA, 255); + out8(regaddr + DAC_DATA, 255); + out8(regaddr + DAC_DATA, 255); + + ga.ga_name = "grf"; + ga.ga_mode = ati_mode; + ga.ga_display = &ati_display; + + height = videosize >> 16; + + ati_display.gd_regaddr = (caddr_t)reg[2] + 0x800000 - 0x400; + ati_display.gd_regsize = 0x400; + ati_display.gd_fbaddr = (caddr_t)reg[2] + 0x400; + ati_display.gd_fbsize = videorowbytes * height; + ati_display.gd_colors = 1 << videobitdepth; + ati_display.gd_planes = videobitdepth; + ati_display.gd_fbwidth = videorowbytes * 8 / videobitdepth; + ati_display.gd_fbheight = height; + ati_display.gd_fbrowbytes = videorowbytes; + ati_display.gd_devaddr = (caddr_t)reg[2]; + ati_display.gd_devsize = reg[4]; + + config_found(self, &ga, NULL); +} + +int +ati_mode(sc, cmd, aux) + struct grf_softc *sc; + int cmd; + void *aux; +{ + return 0; +} diff --git a/sys/arch/macppc/dev/grf_subr.c b/sys/arch/macppc/dev/grf_subr.c new file mode 100644 index 00000000000..a95a934b60b --- /dev/null +++ b/sys/arch/macppc/dev/grf_subr.c @@ -0,0 +1,59 @@ +/* $NetBSD: grf_subr.c,v 1.1 1998/05/15 10:15:48 tsubai Exp $ */ + +/*- + * Copyright (c) 1996 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * 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 the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``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 FOUNDATION OR CONTRIBUTORS + * 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 <sys/param.h> +#include <sys/device.h> +#include <sys/systm.h> + +#include <machine/bus.h> +#include <machine/grfioctl.h> + +#include <macppc/dev/grfvar.h> + +int +grfbusprint(aux, name) + void *aux; + const char *name; +{ + struct grf_attach_args *ga = aux; + + if (name) + printf("%s at %s", ga->ga_name, name); + + return UNCONF; +} diff --git a/sys/arch/macppc/dev/grfvar.h b/sys/arch/macppc/dev/grfvar.h new file mode 100644 index 00000000000..6638a1496bd --- /dev/null +++ b/sys/arch/macppc/dev/grfvar.h @@ -0,0 +1,82 @@ +/* $NetBSD: grfvar.h,v 1.1 1998/05/15 10:15:48 tsubai Exp $ */ + +/* + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department. + * + * 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 the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + * + * from: Utah $Hdr: grfvar.h 1.9 91/01/21$ + * + * @(#)grfvar.h 7.3 (Berkeley) 5/7/91 + */ + +/* + * State info, per grf instance. + */ +struct grf_softc { + struct device sc_dev; /* device glue */ + struct grfinfo *sc_display; /* hardware description (for ioctl) */ + int sc_flags; /* software flags */ + int (*sc_mode) __P((struct grf_softc *, int, void *)); + /* mode-change on/off/mode function */ +}; + +/* + * Attach grf and ite semantics to Mac video hardware. + */ +struct grf_attach_args { + char *ga_name; /* name of semantics to attach */ + struct grfinfo *ga_display; + int (*ga_mode) __P((struct grf_softc *, int, void *)); + /* mode-change on/off/mode function */ +}; + +/* flags */ +#define GF_ALIVE 0x01 +#define GF_OPEN 0x02 +#define GF_EXCLUDE 0x04 +#define GF_WANTED 0x08 +#define GF_BSDOPEN 0x10 +#define GF_HPUXOPEN 0x20 + +/* requests to mode routine */ +#define GM_GRFON 1 +#define GM_GRFOFF 2 +#define GM_CURRMODE 3 +#define GM_LISTMODES 4 +#define GM_NEWMODE 5 + +/* minor device interpretation */ +#define GRFUNIT(d) ((d) & 0x7) diff --git a/sys/arch/macppc/dev/if_mc.c b/sys/arch/macppc/dev/if_mc.c new file mode 100644 index 00000000000..f3c030404b9 --- /dev/null +++ b/sys/arch/macppc/dev/if_mc.c @@ -0,0 +1,404 @@ +/* $NetBSD: if_mc.c,v 1.1 1998/05/15 10:15:48 tsubai Exp $ */ + +/*- + * Copyright (c) 1997 David Huang <khym@bga.com> + * All rights reserved. + * + * Portions of this code are based on code by Denton Gentry <denny1@home.com> + * and Yanagisawa Takeshi <yanagisw@aa.ap.titech.ac.jp>. + * + * 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. 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. + * + */ + +/* + * Bus attachment and DMA routines for the mc driver (Centris/Quadra + * 660av and Quadra 840av onboard ethernet, based on the AMD Am79C940 + * MACE ethernet chip). Also uses the PSC (Peripheral Subsystem + * Controller) for DMA to and from the MACE. + */ + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/socket.h> +#include <sys/systm.h> + +#include <net/if.h> +#include <net/if_ether.h> +#include <net/if_media.h> + +#include <vm/vm.h> + +#include <dev/ofw/openfirm.h> + +#include <machine/pio.h> +#include <machine/bus.h> +#include <machine/autoconf.h> + +#include <macppc/dev/am79c950reg.h> +#include <macppc/dev/if_mcvar.h> + +#define MC_BUFSIZE 0x800 + +hide int mc_match __P((struct device *, struct cfdata *, void *)); +hide void mc_attach __P((struct device *, struct device *, void *)); +hide void mc_init __P((struct mc_softc *sc)); +hide void mc_putpacket __P((struct mc_softc *sc, u_int len)); +hide int mc_dmaintr __P((void *arg)); +hide void mc_reset_rxdma __P((struct mc_softc *sc)); +hide void mc_reset_txdma __P((struct mc_softc *sc)); +hide void mc_select_utp __P((struct mc_softc *sc)); +hide void mc_select_aui __P((struct mc_softc *sc)); +hide int mc_mediachange __P((struct mc_softc *sc)); + +int mc_supmedia[] = { + IFM_ETHER | IFM_10_T, + IFM_ETHER | IFM_10_5, + /*IFM_ETHER | IFM_AUTO,*/ +}; + +#define N_SUPMEDIA (sizeof(mc_supmedia) / sizeof(int)); + +struct cfattach mc_ca = { + sizeof(struct mc_softc), mc_match, mc_attach +}; + +hide int +mc_match(parent, cf, aux) + struct device *parent; + struct cfdata *cf; + void *aux; +{ + struct confargs *ca = aux; + + if (strcmp(ca->ca_name, "mace") != 0) + return 0; + + /* requires 6 regs */ + if (ca->ca_nreg / sizeof(int) != 6) + return 0; + + /* requires 3 intrs */ + if (ca->ca_nintr / sizeof(int) != 3) + return 0; + + return 1; +} + +hide void +mc_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct confargs *ca = aux; + struct mc_softc *sc = (struct mc_softc *)self; + u_int8_t myaddr[ETHER_ADDR_LEN]; + u_int *reg; + + sc->sc_node = ca->ca_node; + + reg = ca->ca_reg; + reg[0] += ca->ca_baseaddr; + reg[2] += ca->ca_baseaddr; + reg[4] += ca->ca_baseaddr; + + sc->sc_txdma = mapiodev(reg[2], reg[3]); + sc->sc_rxdma = mapiodev(reg[4], reg[5]); + bus_space_map(sc->sc_regt, reg[0], reg[1], 0, &sc->sc_regh); + /* XXX sc_regt is uninitialized */ + sc->sc_tail = 0; + sc->sc_txdmacmd = dbdma_alloc(sizeof(dbdma_command_t) * 2); + sc->sc_rxdmacmd = (void *)dbdma_alloc(sizeof(dbdma_command_t) * 8); + bzero(sc->sc_txdmacmd, sizeof(dbdma_command_t) * 2); + bzero(sc->sc_rxdmacmd, sizeof(dbdma_command_t) * 8); + + printf(": irq %d,%d,%d", + ca->ca_intr[0], ca->ca_intr[1], ca->ca_intr[2]); + + if (OF_getprop(sc->sc_node, "local-mac-address", myaddr, 6) != 6) { + printf(": failed to get MAC address.\n"); + return; + } + + /* allocate memory for transmit buffer and mark it non-cacheable */ + sc->sc_txbuf = malloc(NBPG, M_DEVBUF, M_WAITOK); + sc->sc_txbuf_phys = kvtop(sc->sc_txbuf); + bzero(sc->sc_txbuf, NBPG); + + /* + * allocate memory for receive buffer and mark it non-cacheable + * XXX This should use the bus_dma interface, since the buffer + * needs to be physically contiguous. However, it seems that + * at least on my system, malloc() does allocate contiguous + * memory. If it's not, suggest reducing the number of buffers + * to 2, which will fit in one 4K page. + */ + sc->sc_rxbuf = malloc(MC_NPAGES * NBPG, M_DEVBUF, M_WAITOK); + sc->sc_rxbuf_phys = kvtop(sc->sc_rxbuf); + bzero(sc->sc_rxbuf, MC_NPAGES * NBPG); + + if ((int)sc->sc_txbuf & PGOFSET) + printf("txbuf is not page-aligned\n"); + if ((int)sc->sc_rxbuf & PGOFSET) + printf("rxbuf is not page-aligned\n"); + + sc->sc_bus_init = mc_init; + sc->sc_putpacket = mc_putpacket; + + + /* disable receive DMA */ + dbdma_reset(sc->sc_rxdma); + + /* disable transmit DMA */ + dbdma_reset(sc->sc_txdma); + + /* install interrupt handlers */ + /*intr_establish(ca->ca_intr[1], IST_LEVEL, IPL_NET, mc_dmaintr, sc);*/ + intr_establish(ca->ca_intr[2], IST_LEVEL, IPL_NET, mc_dmaintr, sc); + intr_establish(ca->ca_intr[0], IST_LEVEL, IPL_NET, mcintr, sc); + + sc->sc_biucc = XMTSP_64; + sc->sc_fifocc = XMTFW_16 | RCVFW_64 | XMTFWU | RCVFWU | + XMTBRST | RCVBRST; + /*sc->sc_plscc = PORTSEL_10BT;*/ + sc->sc_plscc = PORTSEL_GPSI | ENPLSIO; + + /* mcsetup returns 1 if something fails */ + if (mcsetup(sc, myaddr)) { + printf("mcsetup returns non zero\n"); + return; + } +#ifdef NOTYET + sc->sc_mediachange = mc_mediachange; + sc->sc_mediastatus = mc_mediastatus; + sc->sc_supmedia = mc_supmedia; + sc->sc_nsupmedia = N_SUPMEDIA; + sc->sc_defaultmedia = IFM_ETHER | IFM_10_T; +#endif +} + +/* Bus-specific initialization */ +hide void +mc_init(sc) + struct mc_softc *sc; +{ + mc_reset_rxdma(sc); + mc_reset_txdma(sc); +} + +hide void +mc_putpacket(sc, len) + struct mc_softc *sc; + u_int len; +{ + dbdma_command_t *cmd = sc->sc_txdmacmd; + + DBDMA_BUILD(cmd, DBDMA_CMD_OUT_LAST, 0, len, sc->sc_txbuf_phys, + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + + dbdma_start(sc->sc_txdma, sc->sc_txdmacmd); +} + +/* + * Interrupt handler for the MACE DMA completion interrupts + */ +int +mc_dmaintr(arg) + void *arg; +{ + struct mc_softc *sc = arg; + int status, offset, statoff; + int datalen, resid; + int i, n; + u_int maccc; + dbdma_command_t *cmd; + + /* We've received some packets from the MACE */ + + /* Loop through, processing each of the packets */ + i = sc->sc_tail; + for (n = 0; n < MC_RXDMABUFS; n++, i++) { + if (i == MC_RXDMABUFS) + i = 0; + + cmd = &sc->sc_rxdmacmd[i]; + status = dbdma_ld16(&cmd->d_status); + resid = dbdma_ld16(&cmd->d_resid); + + /*if ((status & D_ACTIVE) == 0)*/ + if ((status & 0x40) == 0) + continue; + +#if 1 + if (dbdma_ld16(&cmd->d_count) != ETHERMTU + 22) + printf("bad d_count\n"); +#endif + + datalen = dbdma_ld16(&cmd->d_count) - resid; + datalen -= 4; /* 4 == status bytes */ + + if (datalen < 4 + sizeof(struct ether_header)) { + printf("short packet len=%d\n", datalen); + /* continue; */ + goto next; + } + + offset = i * MC_BUFSIZE; + statoff = offset + datalen; + + DBDMA_BUILD_CMD(cmd, DBDMA_CMD_STOP, 0, 0, 0, 0); + __asm __volatile("eieio"); + + sc->sc_rxframe.rx_rcvcnt = sc->sc_rxbuf[statoff + 0]; + sc->sc_rxframe.rx_rcvsts = sc->sc_rxbuf[statoff + 1]; + sc->sc_rxframe.rx_rntpc = sc->sc_rxbuf[statoff + 2]; + sc->sc_rxframe.rx_rcvcc = sc->sc_rxbuf[statoff + 3]; + sc->sc_rxframe.rx_frame = sc->sc_rxbuf + offset; + + flushcache((char *)sc->sc_rxbuf_phys + offset, datalen + 4); + mc_rint(sc); + +next: + DBDMA_BUILD_CMD(cmd, DBDMA_CMD_IN_LAST, 0, DBDMA_INT_ALWAYS, + DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + __asm __volatile("eieio"); + cmd->d_status = 0; + cmd->d_resid = 0; + sc->sc_tail = i + 1; + } + + dbdma_continue(sc->sc_rxdma); + + return 1; +} + +hide void +mc_reset_rxdma(sc) + struct mc_softc *sc; +{ + dbdma_command_t *cmd = sc->sc_rxdmacmd; + dbdma_regmap_t *dmareg = sc->sc_rxdma; + int i; + u_int8_t maccc; + + /* Disable receiver, reset the DMA channels */ + maccc = NIC_GET(sc, MACE_MACCC); + NIC_PUT(sc, MACE_MACCC, maccc & ~ENRCV); + + dbdma_reset(dmareg); + + for (i = 0; i < MC_RXDMABUFS; i++) { + DBDMA_BUILD(cmd, DBDMA_CMD_IN_LAST, 0, ETHERMTU + 22, + sc->sc_rxbuf_phys + MC_BUFSIZE * i, DBDMA_INT_ALWAYS, + DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + cmd++; + } + + DBDMA_BUILD(cmd, DBDMA_CMD_NOP, 0, 0, 0, + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_ALWAYS); + dbdma_st32(&cmd->d_cmddep, kvtop((caddr_t)sc->sc_rxdmacmd)); + cmd++; + + dbdma_start(dmareg, sc->sc_rxdmacmd); + + sc->sc_tail = 0; + + /* Reenable receiver, reenable DMA */ + NIC_PUT(sc, MACE_MACCC, maccc); +} + +hide void +mc_reset_txdma(sc) + struct mc_softc *sc; +{ + dbdma_command_t *cmd = sc->sc_txdmacmd; + dbdma_regmap_t *dmareg = sc->sc_txdma; + u_int8_t maccc; + + /* disable transmitter */ + maccc = NIC_GET(sc, MACE_MACCC); + NIC_PUT(sc, MACE_MACCC, maccc & ~ENXMT); + + dbdma_reset(dmareg); + + DBDMA_BUILD(cmd, DBDMA_CMD_OUT_LAST, 0, 0, sc->sc_txbuf_phys, + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + cmd++; + DBDMA_BUILD(cmd, DBDMA_CMD_STOP, 0, 0, 0, + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + + out32rb(&dmareg->d_cmdptrhi, 0); + out32rb(&dmareg->d_cmdptrlo, kvtop((caddr_t)sc->sc_txdmacmd)); + + /* restore old value */ + NIC_PUT(sc, MACE_MACCC, maccc); +} + +void +mc_select_utp(sc) + struct mc_softc *sc; +{ + sc->sc_plscc = PORTSEL_GPSI | ENPLSIO; +} + +void +mc_select_aui(sc) + struct mc_softc *sc; +{ + sc->sc_plscc = PORTSEL_AUI; +} + +int +mc_mediachange(sc) + struct mc_softc *sc; +{ + struct ifmedia *ifm = &sc->sc_media; + + if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) + return EINVAL; + + switch (IFM_SUBTYPE(ifm->ifm_media)) { + + case IFM_10_T: + mc_select_utp(sc); + break; + + case IFM_10_5: + mc_select_aui(sc); + break; + + default: + return EINVAL; + } + + return 0; +} + +void +mc_mediastatus(sc, ifmr) + struct mc_softc *sc; + struct ifmediareq *ifmr; +{ + if (sc->sc_plscc == PORTSEL_AUI) + ifmr->ifm_active = IFM_ETHER | IFM_10_5; + else + ifmr->ifm_active = IFM_ETHER | IFM_10_T; +} diff --git a/sys/arch/macppc/dev/if_mcvar.h b/sys/arch/macppc/dev/if_mcvar.h new file mode 100644 index 00000000000..a381c18680c --- /dev/null +++ b/sys/arch/macppc/dev/if_mcvar.h @@ -0,0 +1,99 @@ +/* $NetBSD: if_mcvar.h,v 1.1 1998/05/15 10:15:48 tsubai Exp $ */ + +/*- + * Copyright (c) 1997 David Huang <khym@bga.com> + * 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. 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 <macppc/dev/dbdma.h> + +#ifdef DDB +#define integrate +#define hide +#else +#define integrate static /*__inline*/ +#define hide static +#endif + +#define MC_REGSPACING 16 +#define MC_REGSIZE MACE_NREGS * MC_REGSPACING +#define MACE_REG(x) ((x)*MC_REGSPACING) + +#define NIC_GET(sc, reg) (bus_space_read_1((sc)->sc_regt, \ + (sc)->sc_regh, MACE_REG(reg))) +#define NIC_PUT(sc, reg, val) (bus_space_write_1((sc)->sc_regt, \ + (sc)->sc_regh, MACE_REG(reg), (val))) + +#ifndef MC_RXDMABUFS +#define MC_RXDMABUFS 4 +#endif +#if (MC_RXDMABUFS < 2) +#error Must have at least two buffers for DMA! +#endif + +#define MC_NPAGES ((MC_RXDMABUFS * 0x800 + NBPG - 1) / NBPG) + +struct mc_rxframe { + u_int8_t rx_rcvcnt; + u_int8_t rx_rcvsts; + u_int8_t rx_rntpc; + u_int8_t rx_rcvcc; + u_char *rx_frame; +}; + +struct mc_softc { + struct device sc_dev; /* base device glue */ + struct ethercom sc_ethercom; /* Ethernet common part */ +#define sc_if sc_ethercom.ec_if + struct ifmedia sc_media; + + struct mc_rxframe sc_rxframe; + u_int8_t sc_biucc; + u_int8_t sc_fifocc; + u_int8_t sc_plscc; + u_int8_t sc_enaddr[6]; + u_int8_t sc_pad[2]; + int sc_havecarrier; /* carrier status */ + void (*sc_bus_init) __P((struct mc_softc *)); + void (*sc_putpacket) __P((struct mc_softc *, u_int)); + int (*sc_mediachange) __P((struct mc_softc *)); + void (*sc_mediastatus) __P((struct mc_softc *, + struct ifmediareq *)); + + bus_space_tag_t sc_regt; + bus_space_handle_t sc_regh; + + u_char *sc_txbuf, *sc_rxbuf; + int sc_txbuf_phys, sc_rxbuf_phys; + int sc_tail; + + int sc_node; + dbdma_regmap_t *sc_txdma; + dbdma_command_t *sc_txdmacmd; + dbdma_regmap_t *sc_rxdma; + dbdma_command_t *sc_rxdmacmd; +}; + +int mcsetup __P((struct mc_softc *, u_int8_t *)); +void mcintr __P((void *arg)); +void mc_rint __P((struct mc_softc *sc)); diff --git a/sys/arch/macppc/dev/ite.c b/sys/arch/macppc/dev/ite.c new file mode 100644 index 00000000000..7c5e60ebd00 --- /dev/null +++ b/sys/arch/macppc/dev/ite.c @@ -0,0 +1,1380 @@ +/* $NetBSD: ite.c,v 1.1 1998/05/15 10:15:49 tsubai Exp $ */ + +/* + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department. + * + * 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 the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + * + * from: Utah $Hdr: ite.c 1.28 92/12/20$ + * + * @(#)ite.c 8.2 (Berkeley) 1/12/94 + */ + +/* + * ite.c + * + * The ite module handles the system console; that is, stuff printed + * by the kernel and by user programs while "desktop" and X aren't + * running. Some (very small) parts are based on hp300's 4.4 ite.c, + * hence the above copyright. + * + * -- Brad and Lawrence, June 26th, 1994 + * + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <dev/cons.h> +#include <sys/conf.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/tty.h> + +#include <machine/bus.h> +#include <machine/cpu.h> +#include <machine/frame.h> + +#define KEYBOARD_ARRAY +#include <machine/keyboard.h> +#include <machine/adbsys.h> +#include <machine/iteioctl.h> +#include <machine/grfioctl.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +/*#include <powermac/dev/viareg.h>*/ +#include <macppc/dev/itevar.h> +#include <macppc/dev/grfvar.h> + +/*#include "6x10.h"*/ +extern u_char fontdata_8x16[]; +#define CHARWIDTH 8 +#define CHARHEIGHT 16 + +/* Local function prototypes */ +static inline void putpixel1 __P((int, int, int *, int)); +static void putpixel2 __P((int, int, int *, int)); +static void putpixel4 __P((int, int, int *, int)); +static void putpixel8 __P((int, int, int *, int)); +static void putpixel16 __P((int, int, int *, int)); +static void putpixel32 __P((int, int, int *, int)); +static void reversepixel1 __P((int, int, int)); +static void writechar __P((char, int, int, int)); +static void drawcursor __P((void)); +static void erasecursor __P((void)); +static void scrollup __P((void)); +static void scrolldown __P((void)); +static void clear_screen __P((int)); +static void clear_line __P((int)); +static void reset_tabs __P((void)); +static void clear_tabs __P((void)); +static void vt100_reset __P((void)); +static void putc_normal __P((char)); +static void putc_esc __P((char)); +static void putc_gotpars __P((char)); +static void putc_getpars __P((char)); +static void putc_square __P((char)); +static void ite_putchar __P((char)); +static int ite_pollforchar __P((void)); +static int itematch __P((struct device *, struct cfdata *, void *)); +static void iteattach __P((struct device *, struct device *, void *)); + +#define dprintf if (0) printf + +#define ATTR_NONE 0 +#define ATTR_BOLD 1 +#define ATTR_UNDER 2 +#define ATTR_REVERSE 4 + +enum vt100state_e { + ESnormal, /* Nothing yet */ + ESesc, /* Got ESC */ + ESsquare, /* Got ESC [ */ + ESgetpars, /* About to get or getting the parameters */ + ESgotpars, /* Finished getting the parameters */ + ESfunckey, /* Function key */ + EShash, /* DEC-specific stuff (screen align, etc.) */ + ESsetG0, /* Specify the G0 character set */ + ESsetG1, /* Specify the G1 character set */ + ESignore /* Ignore this sequence */ +} vt100state = ESnormal; + +/* From Booter via locore */ +long videoaddr; +long videorowbytes; +long videobitdepth; +u_long videosize; + +/* Calculated by itecninit() */ +static int ite_initted = 0; +static int width, height; /* width, height in pixels */ +static int scrcols, scrrows; /* width, height in characters */ +static int screenrowbytes; /* number of visible bytes per row */ + +/* VT100 emulation */ +#define MAXPARS 16 /* max number of VT100 op parameters */ +static int par[MAXPARS], numpars; /* parameter array, # of parameters */ +static int x = 0, y = 0; /* current VT100 cursor location */ +static int savex, savey; /* saved cursor location */ +static int hanging_cursor; /* cursor waiting for more output */ +static int attr; /* current video attribute */ +static char tab_stops[255]; /* tab stops */ +static int scrreg_top; /* scroll region */ +static int scrreg_bottom; + +/* Console bell parameters */ +static int bell_freq = 1880; /* frequency */ +static int bell_length = 10; /* duration */ +static int bell_volume = 100; /* volume */ + +/* For polled ADB mode */ +static int polledkey; +extern int adb_polling; + +struct tty *ite_tty; /* Our tty */ + +static void (*putpixel) __P((int x, int y, int *c, int num)); +static void (*reversepixel) __P((int x, int y, int num)); + +/* For capslock key functionality */ +#define isealpha(ch) (((ch)>='A'&&(ch)<='Z')||((ch)>='a'&&(ch)<='z')||((ch)>=0xC0&&(ch)<=0xFF)) + +/* + * Bitmap handling functions + */ + +static inline void +putpixel1(xx, yy, c, num) + int xx, yy; + int *c; + int num; +{ + u_int i, mask; + u_char *sc; + + sc = (u_char *)videoaddr; + + i = 7 - (xx & 7); + mask = ~(1 << i); + sc += yy * videorowbytes + (xx >> 3); + while (num--) { + *sc &= mask; + *sc |= (*c++ & 1) << i; + sc += videorowbytes; + } +} + +static void +putpixel2(xx, yy, c, num) + int xx, yy; + int *c; + int num; +{ + u_int i, mask; + u_char *sc; + + sc = (u_char *)videoaddr; + + i = 6 - ((xx & 3) << 1); + mask = ~(3 << i); + sc += yy * videorowbytes + (xx >> 2); + while (num--) { + *sc &= mask; + *sc |= (*c++ & 3) << i; + sc += videorowbytes; + } +} + +static void +putpixel4(xx, yy, c, num) + int xx, yy; + int *c; + int num; +{ + u_int i, mask; + u_char *sc; + + sc = (u_char *)videoaddr; + + i = 4 - ((xx & 1) << 2); + mask = ~(15 << i); + sc += yy * videorowbytes + (xx >> 1); + while (num--) { + *sc &= mask; + *sc |= (*c++ & 15) << i; + sc += videorowbytes; + } +} + +static void +putpixel8(xx, yy, c, num) + int xx, yy; + int *c; + int num; +{ + u_char *sc; + + sc = (u_char *)videoaddr; + + sc += yy * videorowbytes + xx; + while (num--) { + *sc = *c++ & 0xff; + sc += videorowbytes; + } +} + +static void +putpixel16(xx, yy, c, num) + int xx, yy; + int *c; + int num; +{ + u_short *sc; + int videorowshorts; + u_char uc; + + sc = (u_short *)videoaddr; + + videorowshorts = videorowbytes >> 1; + sc += yy * videorowshorts + xx; + while (num--) { + uc = (*c++ & 0xff); + *sc = (uc << 8) | uc; + sc += videorowshorts; + } +} + +static void +putpixel32(xx, yy, c, num) + int xx, yy; + int *c; + int num; +{ + u_long *sc; + int videorowlongs; + u_char uc; + + sc = (u_long *)videoaddr; + + videorowlongs = videorowbytes >> 2; + sc += yy * videorowlongs + xx; + while (num--) { + uc = (*c++ & 0xff); + *sc = (uc << 24) | (uc << 16) | (uc << 8) | uc; + sc += videorowlongs; + } +} + +static void +reversepixel1(xx, yy, num) + int xx, yy, num; +{ + u_int mask; + u_char *sc; + u_long *sl; + u_short *ss; + int videorowshorts; + int videorowlongs; + + sc = (u_char *)videoaddr; + mask = 0; /* Get rid of warning from compiler */ + + switch (videobitdepth) { + case 1: + mask = 1 << (7 - (xx & 7)); + sc += yy * videorowbytes + (xx >> 3); + break; + case 2: + mask = 3 << (6 - ((xx & 3) << 1)); + sc += yy * videorowbytes + (xx >> 2); + break; + case 4: + mask = 15 << (4 - ((xx & 1) << 2)); + sc += yy * videorowbytes + (xx >> 1); + break; + case 8: + mask = 255; + sc += yy * videorowbytes + xx; + break; + case 16: + videorowshorts = videorowbytes >> 1; + ss = (u_short *)videoaddr; + ss += yy * videorowshorts + xx; + while (num--) { + *ss ^= 0xffff; + ss += videorowshorts; + } + return; + case 32: + videorowlongs = videorowbytes >> 2; + sl = (u_long *)videoaddr; + sl += yy * videorowlongs + xx; + while (num--) { + *sl ^= 0xffffffff; + sl += videorowlongs; + } + return; + default: + panic("reversepixel(): unsupported bit depth"); + } + + while (num--) { + *sc ^= mask; + sc += videorowbytes; + } +} + +static void +writechar(ch, x, y, attr) + char ch; + int x, y, attr; +{ + int i, j, mask, rev, col[CHARHEIGHT]; + u_char *c; + + ch &= 0x7F; + x *= CHARWIDTH; + y *= CHARHEIGHT; + + rev = (attr & ATTR_REVERSE) ? 255 : 0; + + /*c = &Font6x10[ch * CHARHEIGHT];*/ + c = &fontdata_8x16[ch * CHARHEIGHT]; + + switch (videobitdepth) { + case 1: + for (j = 0; j < CHARWIDTH; j++) { + mask = 1 << (CHARWIDTH - 1 - j); + for (i = 0; i < CHARHEIGHT; i++) + col[i] = ((c[i] & mask) ? 255 : 0) ^ rev; + putpixel1(x + j, y, col, CHARHEIGHT); + } + if (attr & ATTR_UNDER) { + col[0] = 255; + for (j = 0; j < CHARWIDTH; j++) + putpixel1(x + j, y + CHARHEIGHT - 1, col, 1); + } + break; + case 2: + case 4: + case 8: + case 16: + case 32: + for (j = 0; j < CHARWIDTH; j++) { + mask = 1 << (CHARWIDTH - 1 - j); + for (i = 0; i < CHARHEIGHT; i++) + col[i] = ((c[i] & mask) ? 255 : 0) ^ rev; + putpixel(x + j, y, col, CHARHEIGHT); + } + if (attr & ATTR_UNDER) { + col[0] = 255; + for (j = 0; j < CHARWIDTH; j++) + putpixel(x + j, y + CHARHEIGHT - 1, col, 1); + } + break; + } +} + +static void +drawcursor() +{ + u_int j, X, Y; + + X = x * CHARWIDTH; + Y = y * CHARHEIGHT; + + for (j = 0; j < CHARWIDTH; j++) + reversepixel(X + j, Y, CHARHEIGHT); +} + +static void +erasecursor() +{ + u_int j, X, Y; + + X = x * CHARWIDTH; + Y = y * CHARHEIGHT; + + for (j = 0; j < CHARWIDTH; j++) + reversepixel(X + j, Y, CHARHEIGHT); +} + +static void +scrollup() +{ + u_char *from, *to; + u_int linebytes; + u_short i; + + linebytes = videorowbytes * CHARHEIGHT; + to = (u_char *)videoaddr + (scrreg_top * linebytes); + from = to + linebytes; + + for (i = (scrreg_bottom - scrreg_top) * CHARHEIGHT; i > 0; i--) { + ovbcopy(from, to, screenrowbytes); + from += videorowbytes; + to += videorowbytes; + } + for (i = CHARHEIGHT; i > 0; i--) { + bzero(to, screenrowbytes); + to += videorowbytes; + } +} + +static void +scrolldown() +{ + u_char *from, *to; + u_int linebytes; + u_short i; + + linebytes = videorowbytes * CHARHEIGHT; + to = (u_char *)videoaddr + ((scrreg_bottom + 1) * linebytes); + from = to - linebytes; + + for (i = (scrreg_bottom - scrreg_top) * CHARHEIGHT; i > 0; i--) { + from -= videorowbytes; + to -= videorowbytes; + ovbcopy(from, to, screenrowbytes); + } + for (i = CHARHEIGHT; i > 0; i--) { + to -= videorowbytes; + bzero(to, screenrowbytes); + } +} + +static void +clear_screen(which) + int which; +{ + u_char *p; + u_short len, i; + + p = (u_char *)videoaddr; + + switch (which) { + case 0: /* To end of screen */ + if (x > 0) { + clear_line(0); + if (y < scrrows) + y++; + x = 0; + } + p += y * videorowbytes * CHARHEIGHT; + len = scrrows - y; + break; + case 1: /* To start of screen */ + if (x > 0) { + clear_line(1); + if (y > 0) + y--; + x = 0; + } + len = y; + break; + case 2: /* Whole screen */ + default: + len = scrrows; + break; + } + + for (i = len * CHARHEIGHT; i > 0; i--) { + bzero(p, screenrowbytes); + p += videorowbytes; + } +} + +static void +clear_line(which) + int which; +{ + u_char *to; + u_int linebytes; + int start, end, i; + + + /* + * This routine runs extremely slowly. I don't think it's + * used all that often, except for To end of line. I'll go + * back and speed this up when I speed up the whole ite + * module. --LK + */ + + switch (which) { + default: + case 0: /* To end of line */ + start = x; + end = scrcols; + break; + case 1: /* To start of line */ + start = 0; + end = x; + break; + case 2: /* Whole line */ + linebytes = videorowbytes * CHARHEIGHT; + to = (u_char *)videoaddr + (y * linebytes); + + for (i = CHARHEIGHT; i > 0; i--) { + bzero(to, screenrowbytes); + to += videorowbytes; + } + return; + } + + for (i = start; i < end; i++) + writechar(' ', i, y, ATTR_NONE); +} + +static void +reset_tabs() +{ + int i; + + for (i = 0; i < scrcols; i++) + tab_stops[i] = ((i % 8) == 0); +} + +static void +clear_tabs() +{ + int i; + + for (i = 0; i < scrcols; i++) + tab_stops[i] = 0; +} + +static void +vt100_reset() +{ + reset_tabs(); + scrreg_top = 0; + scrreg_bottom = scrrows - 1; + attr = ATTR_NONE; +} + +static void +putc_normal(ch) + char ch; +{ + switch (ch) { + case '\a': /* Beep */ + /*mac68k_ring_bell(bell_freq, bell_length, bell_volume);*/ + break; + case 127: /* Delete */ + case '\b': /* Backspace */ + if (hanging_cursor) + hanging_cursor = 0; + else if (x > 0) + x--; + break; + case '\t': /* Tab */ + do + x++; + while ((tab_stops[x] == 0) && (x < scrcols)); + break; + case '\n': /* Line feed */ + if (y == scrreg_bottom) + scrollup(); + else + y++; + break; + case '\r': /* Carriage return */ + x = 0; + hanging_cursor = 0; + break; + case '\e': /* Escape */ + vt100state = ESesc; + hanging_cursor = 0; + break; + default: + if (ch >= ' ') { + if (hanging_cursor) { + x = 0; + if (y == scrreg_bottom) + scrollup(); + else + y++; + hanging_cursor = 0; + } + + writechar(ch, x, y, attr); + + if (x == scrcols - 1) + hanging_cursor = 1; + else + x++; + if (x >= scrcols) { /* can we ever get here? */ + x = 0; + y++; + } + } + break; + } +} + +static void +putc_esc(ch) + char ch; +{ + vt100state = ESnormal; + + switch (ch) { + case '[': + vt100state = ESsquare; + break; + case '(': + vt100state = ESsetG0; + break; + case ')': + vt100state = ESsetG1; + break; + case 'E': /* Next line */ + x = 0; + /* FALLTHROUGH */ + case 'D': /* Line feed */ + if (y == scrreg_bottom) + scrollup(); + else + y++; + break; + case 'H': /* Set tab stop */ + tab_stops[x] = 1; + break; + case 'M': /* Cursor up */ + if (y == scrreg_top) + scrolldown(); + else + y--; + break; + case '>': + vt100_reset(); + break; + case '7': /* Save cursor */ + savex = x; + savey = y; + break; + case '8': /* Restore cursor */ + x = savex; + y = savey; + break; + default: + /* Rest not supported */ + break; + } +} + +static void +putc_gotpars(ch) + char ch; +{ + int i; + + vt100state = ESnormal; + switch (ch) { + case 'A': /* Up */ + y -= par[0] ? par[0] : 1; + if (y < scrreg_top) + y = scrreg_top; + break; + case 'B': /* Down */ + y += par[0] ? par[0] : 1; + if (y > scrreg_bottom) + y = scrreg_bottom; + break; + case 'C': /* Right */ + x+= par[0] ? par[0] : 1; + break; + case 'D': /* Left */ + x-= par[0] ? par[0] : 1; + break; + case 'H': /* Set cursor position */ + case 'f': /* Set cursor position */ + x = par[1] - 1; + y = par[0] - 1; + hanging_cursor = 0; + break; + case 'J': /* Clear part of screen */ + clear_screen(par[0]); + break; + case 'K': /* Clear part of line */ + clear_line(par[0]); + break; + case 'L': /* Add line */ + if (scrreg_top < scrreg_bottom) { + i = scrreg_top; + scrreg_top = y; + scrolldown(); + scrreg_top = i; + } else + clear_line(0); + break; + case 'M': /* Delete line */ + if (scrreg_top < scrreg_bottom) { + i = scrreg_top; + scrreg_top = y; + scrollup(); + scrreg_top = i; + } else + clear_line(0); + break; + case 'g': /* Clear tab stops */ + if (numpars >= 1) { + if (par[0] == 3) + clear_tabs(); + else if (par[0] == 0) + tab_stops[x] = 0; + } + break; + case 'm': /* Set attribute */ + for (i = 0; i < numpars; i++) { + switch (par[i]) { + case 0: + attr = ATTR_NONE; + break; + case 1: + attr |= ATTR_BOLD; + break; + case 4: + attr |= ATTR_UNDER; + break; + case 7: + attr |= ATTR_REVERSE; + break; + case 21: + attr &= ~ATTR_BOLD; + break; + case 24: + attr &= ~ATTR_UNDER; + break; + case 27: + attr &= ~ATTR_REVERSE; + break; + } + } + break; + case 'r': /* Set scroll region */ + /* ensure top < bottom, and both within limits */ + if ((numpars > 0) && (par[0] < scrrows)) + scrreg_top = par[0] - 1; + else + scrreg_top = 0; + if ((numpars > 1) && (par[1] <= scrrows) && (par[1] > par[0])) + scrreg_bottom = par[1] - 1; + else + scrreg_bottom = scrrows - 1; + break; + } +} + +static void +putc_getpars(ch) + char ch; +{ + switch (ch) { + case '?': + /* Not supported */ + return; + case '[': + vt100state = ESnormal; + /* Not supported */ + return; + default: + if (ch == ';' && numpars < MAXPARS - 1) + numpars++; + else if (ch >= '0' && ch <= '9') { + par[numpars] *= 10; + par[numpars] += ch - '0'; + } else { + numpars++; + vt100state = ESgotpars; + putc_gotpars(ch); + } + } +} + +static void +putc_square(ch) + char ch; +{ + u_short i; + + for (i = 0; i < MAXPARS; i++) + par[i] = 0; + + numpars = 0; + vt100state = ESgetpars; + + putc_getpars(ch); +} + +static void +ite_putchar(ch) + char ch; +{ + switch (vt100state) { + default: + vt100state = ESnormal; /* FALLTHROUGH */ + case ESnormal: + putc_normal(ch); + break; + case ESesc: + putc_esc(ch); + break; + case ESsquare: + putc_square(ch); + break; + case ESgetpars: + putc_getpars(ch); + break; + case ESgotpars: + putc_gotpars(ch); + break; + } + + if (x >= scrcols) + x = scrcols - 1; + if (x < 0) + x = 0; + if (y >= scrrows) + y = scrrows - 1; + if (y < 0) + y = 0; +} + + +/* + * Keyboard support functions + */ + +static int +ite_pollforchar() +{ + int s; + register int intbits; + + s = splhigh(); + + polledkey = -1; + adb_polling = 1; + + /* pretend we're VIA interrupt dispatcher */ + while (polledkey == -1) { + adb_intr_cuda(); +#if 0 + intbits = via_reg(VIA1, vIFR); + + if (intbits & V1IF_ADBRDY) { + mrg_adbintr(); + via_reg(VIA1, vIFR) = V1IF_ADBRDY; + } + if (intbits & 0x10) { + mrg_pmintr(); + via_reg(VIA1, vIFR) = 0x10; + } +#endif + } + + adb_polling = 0; + + splx(s); + + return polledkey; +} + + +/* + * Autoconfig attachment + */ + +struct cfattach ite_ca = { + sizeof(struct device), itematch, iteattach +}; + +static int +itematch(parent, cf, aux) + struct device *parent; + struct cfdata *cf; + void *aux; +{ + struct grf_attach_args *ga = aux; + + if (strcmp(ga->ga_name, "ite") != 0) + return 0; + + return 1; +} + +static void +iteattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + printf("\n"); +} + +/* + * Tty handling functions + */ + +int +iteopen(dev, mode, devtype, p) + dev_t dev; + int mode; + int devtype; + struct proc *p; +{ + register struct tty *tp; + register int error; + + dprintf("iteopen(): enter(0x%x)\n", (int)dev); + + if (!ite_initted) + return (ENXIO); + + if (ite_tty == NULL) { + tp = ite_tty = ttymalloc(); + tty_attach(tp); + } else + tp = ite_tty; + if ((tp->t_state & (TS_ISOPEN | TS_XCLUDE)) == (TS_ISOPEN | TS_XCLUDE) + && p->p_ucred->cr_uid != 0) + return (EBUSY); + + tp->t_oproc = itestart; + tp->t_param = NULL; + tp->t_dev = dev; + if ((tp->t_state & TS_ISOPEN) == 0) { + ttychars(tp); + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_cflag = CS8 | CREAD; + tp->t_lflag = TTYDEF_LFLAG; + tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; + tp->t_state = TS_ISOPEN | TS_CARR_ON; + ttsetwater(tp); + } + + error = (*linesw[tp->t_line].l_open) (dev, tp); + tp->t_winsize.ws_row = scrrows; + tp->t_winsize.ws_col = scrcols; + + dprintf("iteopen(): exit(%d)\n", error); + return (error); +} + +int +iteclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + dprintf("iteclose: enter (%d)\n", (int)dev); + + (*linesw[ite_tty->t_line].l_close) (ite_tty, flag); + ttyclose(ite_tty); +#if 0 + ttyfree(ite_tty); + ite_tty = (struct tty *) 0; +#endif + + dprintf("iteclose: exit\n"); + return 0; +} + +int +iteread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + dprintf("iteread: enter\n"); + return (*linesw[ite_tty->t_line].l_read) (ite_tty, uio, flag); +} + +int +itewrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + dprintf("itewrite: enter\n"); + return (*linesw[ite_tty->t_line].l_write) (ite_tty, uio, flag); +} + +struct tty * +itetty(dev) + dev_t dev; +{ + return (ite_tty); +} + +int +iteioctl(dev, cmd, addr, flag, p) + dev_t dev; + int cmd; + caddr_t addr; + int flag; + struct proc *p; +{ + register struct tty *tp = ite_tty; + int error; + + dprintf("iteioctl: enter(%d, 0x%x)\n", cmd, cmd); + + error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, addr, flag, p); + if (error >= 0) { + dprintf("iteioctl: exit(%d)\n", error); + return (error); + } + + error = ttioctl(tp, cmd, addr, flag, p); + if (error >= 0) { + dprintf("iteioctl: exit(%d)\n", error); + return (error); + } + + switch (cmd) { + case ITEIOCRINGBELL: + /*return mac68k_ring_bell(bell_freq, bell_length, bell_volume);*/ + case ITEIOCSETBELL: + { + struct bellparams *bp = (void *)addr; + + /* Do some sanity checks. */ + if (bp->freq < 10 || bp->freq > 40000) + return (EINVAL); + if (bp->len < 0 || bp->len > 3600) + return (EINVAL); + if (bp->vol < 0 || bp->vol > 100) + return (EINVAL); + + bell_freq = bp->freq; + bell_length = bp->len; + bell_volume = bp->vol; + return (0); + } + case ITEIOCGETBELL: + { + struct bellparams *bp = (void *)addr; + + bell_freq = bp->freq; + bell_length = bp->len; + bell_volume = bp->vol; + return (0); + } + } + + dprintf("iteioctl: exit(ENOTTY)\n"); + return (ENOTTY); +} + +void +itestart(register struct tty * tp) +{ + register int cc, s; + + s = spltty(); + if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) { + splx(s); + return; + } + tp->t_state |= TS_BUSY; + + cc = tp->t_outq.c_cc; + splx(s); + erasecursor(); + while (cc-- > 0) + ite_putchar(getc(&tp->t_outq)); + drawcursor(); + + s = spltty(); + tp->t_state &= ~TS_BUSY; + splx(s); +} + +void +itestop(struct tty * tp, int flag) +{ + int s; + + s = spltty(); + if (tp->t_state & TS_BUSY) + if ((tp->t_state & TS_TTSTOP) == 0) + tp->t_state |= TS_FLUSH; + splx(s); +} + +int +ite_intr(adb_event_t * event) +{ + static int shift = 0, control = 0, capslock = 0; + int key, press, val, state; + char str[10], *s; + + key = event->u.k.key; + press = ADBK_PRESS(key); + val = ADBK_KEYVAL(key); + +/*printf("ite_intr: (%x %x ", press, val);*/ + if (val == ADBK_SHIFT) + shift = press; + else if (val == ADBK_CAPSLOCK) + capslock = !capslock; + else if (val == ADBK_CONTROL) + control = press; + else if (press) { + switch (val) { + case ADBK_UP: + str[0] = '\e'; + str[1] = 'O'; + str[2] = 'A'; + str[3] = '\0'; + break; + case ADBK_DOWN: + str[0] = '\e'; + str[1] = 'O'; + str[2] = 'B'; + str[3] = '\0'; + break; + case ADBK_RIGHT: + str[0] = '\e'; + str[1] = 'O'; + str[2] = 'C'; + str[3] = '\0'; + break; + case ADBK_LEFT: + str[0] = '\e'; + str[1] = 'O'; + str[2] = 'D'; + str[3] = '\0'; + break; + default: + state = 0; + if (capslock && isealpha(keyboard[val][1])) + state = 1; + if (shift) + state = 1; + if (control) + state = 2; + str[0] = keyboard[val][state]; + str[1] = '\0'; + break; + } + if (adb_polling) + polledkey = str[0]; + else + for (s = str; *s; s++) + (*linesw[ite_tty->t_line].l_rint)(*s, ite_tty); + } +/*printf("%x) ", str[0]);*/ + return 0; +} +/* + * Console functions + */ + +void +itecnprobe(struct consdev * cp) +{ + int maj, unit; + int l; + char type[32]; + extern int console_node; + + if (console_node == -1) + return; + + l = OF_getprop(console_node, "device_type", type, sizeof(type)); + if (l == -1 || l >= sizeof(type) - 1) + return; + + if (strcmp(type, "display") != 0) + return; + + /* locate the major number */ + for (maj = 0; maj < nchrdev; maj++) + if (cdevsw[maj].d_open == iteopen) + break; + + if (maj == nchrdev) + panic("itecnprobe(): did not find iteopen()."); + + unit = 0; /* hardcode first device as console. */ + + /* initialize required fields */ + cp->cn_dev = makedev(maj, unit); + cp->cn_pri = CN_INTERNAL; +} + +void +itereset() +{ + width = videosize & 0xffff; + height = (videosize >> 16) & 0xffff; + scrrows = height / CHARHEIGHT; + scrcols = width / CHARWIDTH; + + switch (videobitdepth) { + default: + case 1: + putpixel = putpixel1; + reversepixel = reversepixel1; + screenrowbytes = (width + 7) >> 3; + break; + case 2: + putpixel = putpixel2; + reversepixel = reversepixel1; + screenrowbytes = (width + 3) >> 2; + break; + case 4: + putpixel = putpixel4; + reversepixel = reversepixel1; + screenrowbytes = (width + 1) >> 1; + break; + case 8: + putpixel = putpixel8; + reversepixel = reversepixel1; + screenrowbytes = width; + break; + case 16: + putpixel = putpixel16; + reversepixel = reversepixel1; + screenrowbytes = width*2; + break; + case 32: + putpixel = putpixel32; + reversepixel = reversepixel1; + screenrowbytes = width*4; + break; + } + + vt100_reset(); +} + +void +itecninit(struct consdev * cp) +{ + int node, options; + int len; + vm_offset_t pa; + u_int reg[5]; + extern int console_node; + + if (ite_initted) + return; + + node = console_node; + if (node == -1) + return; + + OF_getprop(node, "width", &width, sizeof(width)); + OF_getprop(node, "height", &height, sizeof(height)); + OF_getprop(node, "depth", &videobitdepth, sizeof(videobitdepth)); + OF_getprop(node, "linebytes", &videorowbytes, sizeof(videorowbytes)); + OF_getprop(node, "assigned-addresses", reg, sizeof(reg)); + + /* + * XXX This should not be here. + * + * we cannot use kmem_alloc_... + */ + len = videorowbytes * height; + pa = reg[2]; + while (len > 0) { + pmap_enter(pmap_kernel(), pa, pa, + VM_PROT_READ|VM_PROT_WRITE, 1); + pa += NBPG; + len -= NBPG; + } + + videoaddr = reg[2] + 0x400; /* XXX ATI only */ + + videosize = width | (height << 16); + + ite_initted = 1; + itereset(); + iteon(cp->cn_dev, 0); +} + +int +iteon(dev_t dev, int flags) +{ + if (!ite_initted) + return (-1); + + erasecursor(); + clear_screen(2); + drawcursor(); + return 0; +} + +int +iteoff(dev_t dev, int flags) +{ + if (!ite_initted) + return (-1); + + erasecursor(); + clear_screen(2); + return 0; +} + +int +itecngetc(dev_t dev) +{ + /* Oh, man... */ + + return ite_pollforchar(); +} + +void +itecnputc(dev_t dev, int c) +{ + erasecursor(); + ite_putchar(c); + drawcursor(); +} + +struct consdev consdev_ite = { + itecnprobe, + itecninit, + itecngetc, + itecnputc, + nullcnpollc, +}; diff --git a/sys/arch/macppc/dev/itevar.h b/sys/arch/macppc/dev/itevar.h new file mode 100644 index 00000000000..d3412dd1576 --- /dev/null +++ b/sys/arch/macppc/dev/itevar.h @@ -0,0 +1,55 @@ +/* $NetBSD: itevar.h,v 1.1 1998/05/15 10:15:49 tsubai Exp $ */ + +/* + * Copyright (c) 1995 Allen Briggs. 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 Allen Briggs. + * 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 <machine/adbsys.h> + +int ite_intr __P((adb_event_t *event)); +int iteon __P((dev_t dev, int flags)); +int iteoff __P((dev_t dev, int flags)); +void itereset __P((void)); + +#ifndef CN_DEAD +#include <dev/cons.h> +#endif + +void itestop __P((struct tty * tp, int flag)); +void itestart __P((register struct tty * tp)); +int iteopen __P((dev_t dev, int mode, int devtype, struct proc * p)); +int iteclose __P((dev_t dev, int flag, int mode, struct proc * p)); +int iteread __P((dev_t dev, struct uio * uio, int flag)); +int itewrite __P((dev_t dev, struct uio * uio, int flag)); +int iteioctl __P((dev_t, int, caddr_t, int, struct proc *)); +struct tty *itetty __P((dev_t dev)); + +void itecnprobe __P((struct consdev * cp)); +void itecninit __P((struct consdev * cp)); +int itecngetc __P((dev_t dev)); +void itecnputc __P((dev_t dev, int c)); diff --git a/sys/arch/macppc/dev/obio.c b/sys/arch/macppc/dev/obio.c new file mode 100644 index 00000000000..6509076171f --- /dev/null +++ b/sys/arch/macppc/dev/obio.c @@ -0,0 +1,102 @@ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> + +#include <dev/ofw/openfirm.h> + +#include <machine/autoconf.h> + +static void obio_attach __P((struct device *, struct device *, void *)); +static int obio_match __P((struct device *, struct cfdata *, void *)); +static int obio_print __P((void *, const char *)); + +struct obio_softc { + struct device sc_dev; + int sc_node; +}; + + +struct cfattach obio_ca = { + sizeof(struct obio_softc), obio_match, obio_attach +}; + +int +obio_match(parent, cf, aux) + struct device *parent; + struct cfdata *cf; + void *aux; +{ + struct pci_attach_args *pa = aux; + + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_APPLE && + PCI_PRODUCT(pa->pa_id) == 2) + return 1; + + return 0; +} + +/* + * Attach all the sub-devices we can find + */ +void +obio_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct obio_softc *sc = (struct obio_softc *)self; + struct confargs ca; + int node, child, namelen; + u_int reg[20]; + int intr[5]; + char name[32]; + + node = OF_finddevice("/bandit/gc"); /* XXX */ + sc->sc_node = node; + + if (OF_getprop(node, "assigned-addresses", reg, sizeof(reg)) < 12) + return; + ca.ca_baseaddr = reg[2]; + + printf(": addr 0x%x\n", ca.ca_baseaddr); + + for (child = OF_child(node); child; child = OF_peer(child)) { + namelen = OF_getprop(child, "name", name, sizeof(name)); + if (namelen < 0) + continue; + if (namelen >= sizeof(name)) + continue; + + name[namelen] = 0; + ca.ca_name = name; + ca.ca_node = child; + + ca.ca_nreg = OF_getprop(child, "reg", reg, sizeof(reg)); + ca.ca_nintr = OF_getprop(child, "AAPL,interrupts", intr, + sizeof(intr)); + ca.ca_reg = reg; + ca.ca_intr = intr; + + config_found(self, &ca, obio_print); + } +} + +int +obio_print(aux, obio) + void *aux; + const char *obio; +{ + struct confargs *ca = aux; + + if (obio) + printf("%s at %s", ca->ca_name, obio); + + if (ca->ca_nreg > 0) + printf(" offset 0x%x", ca->ca_reg[0]); + + return UNCONF; +} diff --git a/sys/arch/macppc/dev/viareg.h b/sys/arch/macppc/dev/viareg.h new file mode 100644 index 00000000000..0cbc1f450f4 --- /dev/null +++ b/sys/arch/macppc/dev/viareg.h @@ -0,0 +1,250 @@ +/* $NetBSD: viareg.h,v 1.1 1998/05/15 10:15:49 tsubai Exp $ */ + +/*- + * Copyright (C) 1993 Allen K. Briggs, Chris P. Caputo, + * Michael L. Finch, Bradley A. Grantham, and + * Lawrence A. Kesteloot + * 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 the Alice Group. + * 4. The names of the Alice Group or any of its members may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE ALICE GROUP ``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 ALICE GROUP 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. + * + */ +/* + + Prototype VIA control definitions + + 06/04/92,22:33:57 BG Let's see what I can do. + +*/ + + + /* VIA1 data register A */ +#define DA1I_vSCCWrReq 0x80 +#define DA1O_vPage2 0x40 +#define DA1I_CPU_ID1 0x40 +#define DA1O_vHeadSel 0x20 +#define DA1O_vOverlay 0x10 +#define DA1O_vSync 0x08 +#define DA1O_RESERVED2 0x04 +#define DA1O_RESERVED1 0x02 +#define DA1O_RESERVED0 0x01 + + /* VIA1 data register B */ +#define DB1I_Par_Err 0x80 +#define DB1O_vSndEnb 0x80 +#define DB1O_Par_Enb 0x40 +#define DB1O_vFDesk2 0x20 +#define DB1O_vFDesk1 0x10 +#define DB1I_vFDBInt 0x08 +#define DB1O_rTCEnb 0x04 +#define DB1O_rTCCLK 0x02 +#define DB1O_rTCData 0x01 +#define DB1I_rTCData 0x01 + + /* VIA2 data register A */ +#define DA2O_v2Ram1 0x80 +#define DA2O_v2Ram0 0x40 +#define DA2I_v2IRQ0 0x40 +#define DA2I_v2IRQE 0x20 +#define DA2I_v2IRQD 0x10 +#define DA2I_v2IRQC 0x08 +#define DA2I_v2IRQB 0x04 +#define DA2I_v2IRQA 0x02 +#define DA2I_v2IRQ9 0x01 + + /* VIA2 data register B */ +#define DB2O_v2VBL 0x80 +#define DB2O_Par_Test 0x80 +#define DB2I_v2SNDEXT 0x40 +#define DB2I_v2TM0A 0x20 +#define DB2I_v2TM1A 0x10 +#define DB2I_vFC3 0x08 +#define DB2O_vFC3 0x08 +#define DB2O_v2PowerOff 0x04 +#define DB2O_v2BusLk 0x02 +#define DB2O_vCDis 0x01 +#define DB2O_CEnable 0x01 + +/* + * VIA1 interrupts + */ +#define VIA1_T1 6 +#define VIA1_T2 5 +#define VIA1_ADBCLK 4 +#define VIA1_ADBDATA 3 +#define VIA1_ADBRDY 2 +#define VIA1_VBLNK 1 +#define VIA1_ONESEC 0 + +/* VIA1 interrupt bits */ +#define V1IF_IRQ 0x80 +#define V1IF_T1 (1 << VIA1_T1) +#define V1IF_T2 (1 << VIA1_T2) +#define V1IF_ADBCLK (1 << VIA1_ADBCLK) +#define V1IF_ADBDATA (1 << VIA1_ADBDATA) +#define V1IF_ADBRDY (1 << VIA1_ADBRDY) +#define V1IF_VBLNK (1 << VIA1_VBLNK) +#define V1IF_ONESEC (1 << VIA1_ONESEC) + +/* + * VIA2 interrupts + */ +#define VIA2_T1 6 +#define VIA2_T2 5 +#define VIA2_ASC 4 +#define VIA2_SCSIIRQ 3 +#define VIA2_EXPIRQ 2 +#define VIA2_SLOTINT 1 +#define VIA2_SCSIDRQ 0 + +/* VIA2 interrupt bits */ +#define V2IF_IRQ 0x80 +#define V2IF_T1 (1 << VIA2_T1) +#define V2IF_T2 (1 << VIA2_T2) +#define V2IF_ASC (1 << VIA2_ASC) +#define V2IF_SCSIIRQ (1 << VIA2_SCSIIRQ) +#define V2IF_EXPIRQ (1 << VIA2_EXPIRQ) +#define V2IF_SLOTINT (1 << VIA2_SLOTINT) +#define V2IF_SCSIDRQ (1 << VIA2_SCSIDRQ) + +#define VIA1_INTS (V1IF_T1 | V1IF_ADBRDY) +#define VIA2_INTS (V2IF_T1 | V2IF_ASC | V2IF_SCSIIRQ | V2IF_SLOTINT | \ + V2IF_SCSIDRQ) + +#define RBV_INTS (V2IF_T1 | V2IF_ASC | V2IF_SCSIIRQ | V2IF_SLOTINT | \ + V2IF_SCSIDRQ | V1IF_ADBRDY) + +#define ACR_T1LATCH 0x40 + +extern volatile unsigned char *Via1Base; +#define VIA1_addr Via1Base /* at PA 0x50f00000 */ +#define VIA2OFF 1 /* VIA2 addr = VIA1_addr * 0x2000 */ +#define RBVOFF 0x13 /* RBV addr = VIA1_addr * 0x13000 */ + +#define VIA1 0 +extern int VIA2; + + /* VIA interface registers */ +#define vBufA 0x1e00 /* register A */ +#define vBufB 0 /* register B */ +#define vDirA 0x0600 /* data direction register */ +#define vDirB 0x0400 /* data direction register */ +#define vT1C 0x0800 +#define vT1CH 0x0a00 +#define vT1L 0x0c00 +#define vT1LH 0x0e00 +#define vT2C 0x1000 +#define vT2CH 0x1200 +#define vSR 0x1400 /* shift register */ +#define vACR 0x1600 /* aux control register */ +#define vPCR 0x1800 /* peripheral control register */ +#define vIFR 0x1a00 /* interrupt flag register */ +#define vIER 0x1c00 /* interrupt enable register */ + + /* RBV interface registers */ +#define rBufB 0 /* register B */ +#define rBufA 2 /* register A */ +#define rIFR 0x3 /* interrupt flag register (writes?) */ +#define rIER 0x13 /* interrupt enable register */ +#define rMonitor 0x10 /* Monitor type */ +#define rSlotInt 0x12 /* Slot interrupt */ + + /* RBV monitor type flags and masks */ +#define RBVDepthMask 0x07 /* depth in bits */ +#define RBVMonitorMask 0x38 /* Type numbers */ +#define RBVOff 0x40 /* monitor turn off */ +#define RBVMonIDNone 0x38 /* What RBV actually has for no video */ +#define RBVMonIDOff 0x0 /* What rbv_vidstatus() returns for no video */ +#define RBVMonID15BWP 0x08 /* BW portrait */ +#define RBVMonIDRGB 0x10 /* color monitor */ +#define RBVMonIDRGB15 0x28 /* 15 inch RGB */ +#define RBVMonIDBW 0x30 /* No internal video */ + +#define via_reg(v, r) (*(Via1Base + (r))) + +#include <machine/pio.h> + +static __inline void +via_reg_and(ign, reg, val) + int ign, reg, val; +{ + volatile unsigned char *addr = Via1Base + reg; + + out8(addr, in8(addr) & val); +} + +static __inline void +via_reg_or(ign, reg, val) + int ign, reg, val; +{ + volatile unsigned char *addr = Via1Base + reg; + + out8(addr, in8(addr) | val); +} + +static __inline void +via_reg_xor(ign, reg, val) + int ign, reg, val; +{ + volatile unsigned char *addr = Via1Base + reg; + + out8(addr, in8(addr) ^ val); +} + +static __inline int +read_via_reg(ign, reg) + int ign, reg; +{ + volatile unsigned char *addr = Via1Base + reg; + + return in8(addr); +} + +static __inline void +write_via_reg(ign, reg, val) + int ign, reg, val; +{ + volatile unsigned char *addr = Via1Base + reg; + + out8(addr, val); +} + + + +#define vDirA_ADBState 0x30 + +void via_init __P((void)); +int rbv_vidstatus __P((void)); +void via_shutdown __P((void)); +void via_set_modem __P((int)); +int add_nubus_intr __P((int, void (*) __P((void *, int)), void *)); +void enable_nubus_intr __P((void)); +void via1_register_irq __P((int, void (*)(void *), void *)); +void via2_register_irq __P((int, void (*)(void *), void *)); + +extern void (*via1itab[7]) __P((void *)); +extern void (*via2itab[7]) __P((void *)); diff --git a/sys/arch/macppc/dev/z8530tty.c b/sys/arch/macppc/dev/z8530tty.c new file mode 100644 index 00000000000..77d04b576be --- /dev/null +++ b/sys/arch/macppc/dev/z8530tty.c @@ -0,0 +1,1463 @@ +/* $NetBSD: z8530tty.c,v 1.1 1998/05/15 10:15:49 tsubai Exp $ */ + +/*- + * Copyright (c) 1993, 1994, 1995, 1996, 1997, 1998 + * Charles M. Hannum. 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 Charles M. Hannum. + * 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. + */ + +/* + * Copyright (c) 1994 Gordon W. Ross + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory. + * + * 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 the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + * + * @(#)zs.c 8.1 (Berkeley) 7/19/93 + */ + +/* + * Zilog Z8530 Dual UART driver (tty interface) + * + * This is the "slave" driver that will be attached to + * the "zsc" driver for plain "tty" async. serial lines. + * + * Credits, history: + * + * The original version of this code was the sparc/dev/zs.c driver + * as distributed with the Berkeley 4.4 Lite release. Since then, + * Gordon Ross reorganized the code into the current parent/child + * driver scheme, separating the Sun keyboard and mouse support + * into independent child drivers. + * + * RTS/CTS flow-control support was a collaboration of: + * Gordon Ross <gwr@netbsd.org>, + * Bill Studenmund <wrstuden@loki.stanford.edu> + * Ian Dall <Ian.Dall@dsto.defence.gov.au> + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/device.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/malloc.h> +#include <sys/tty.h> +#include <sys/time.h> +#include <sys/kernel.h> +#include <sys/syslog.h> + +#include <dev/ic/z8530reg.h> +#include <machine/z8530var.h> + +#include "locators.h" + +/* + * How many input characters we can buffer. + * The port-specific var.h may override this. + * Note: must be a power of two! + */ +#ifndef ZSTTY_RING_SIZE +#define ZSTTY_RING_SIZE 2048 +#endif + +/* + * Make this an option variable one can patch. + * But be warned: this must be a power of 2! + */ +u_int zstty_rbuf_size = ZSTTY_RING_SIZE; + +/* Stop input when 3/4 of the ring is full; restart when only 1/4 is full. */ +u_int zstty_rbuf_hiwat = (ZSTTY_RING_SIZE * 1) / 4; +u_int zstty_rbuf_lowat = (ZSTTY_RING_SIZE * 3) / 4; + +struct zstty_softc { + struct device zst_dev; /* required first: base device */ + struct tty *zst_tty; + struct zs_chanstate *zst_cs; + + u_int zst_overflows, + zst_floods, + zst_errors; + + int zst_hwflags, /* see z8530var.h */ + zst_swflags; /* TIOCFLAG_SOFTCAR, ... <ttycom.h> */ + + u_int zst_r_hiwat, + zst_r_lowat; + u_char *volatile zst_rbget, + *volatile zst_rbput; + volatile u_int zst_rbavail; + u_char *zst_rbuf, + *zst_ebuf; + + /* + * The transmit byte count and address are used for pseudo-DMA + * output in the hardware interrupt code. PDMA can be suspended + * to get pending changes done; heldtbc is used for this. It can + * also be stopped for ^S; this sets TS_TTSTOP in tp->t_state. + */ + u_char *zst_tba; /* transmit buffer address */ + u_int zst_tbc, /* transmit byte count */ + zst_heldtbc; /* held tbc while xmission stopped */ + + /* Flags to communicate with zstty_softint() */ + volatile u_char zst_rx_flags, /* receiver blocked */ +#define RX_TTY_BLOCKED 0x01 +#define RX_TTY_OVERFLOWED 0x02 +#define RX_IBUF_BLOCKED 0x04 +#define RX_IBUF_OVERFLOWED 0x08 +#define RX_ANY_BLOCK 0x0f + zst_tx_busy, /* working on an output chunk */ + zst_tx_done, /* done with one output chunk */ + zst_tx_stopped, /* H/W level stop (lost CTS) */ + zst_st_check, /* got a status interrupt */ + zst_rx_ready; +}; + +/* Macros to clear/set/test flags. */ +#define SET(t, f) (t) |= (f) +#define CLR(t, f) (t) &= ~(f) +#define ISSET(t, f) ((t) & (f)) + +/* Definition of the driver for autoconfig. */ +#ifdef __BROKEN_INDIRECT_CONFIG +static int zstty_match(struct device *, void *, void *); +#else +static int zstty_match(struct device *, struct cfdata *, void *); +#endif +static void zstty_attach(struct device *, struct device *, void *); + +struct cfattach zstty_ca = { + sizeof(struct zstty_softc), zstty_match, zstty_attach +}; + +extern struct cfdriver zstty_cd; + +struct zsops zsops_tty; + +/* Routines called from other code. */ +cdev_decl(zs); /* open, close, read, write, ioctl, stop, ... */ + +static void zs_shutdown __P((struct zstty_softc *)); +static void zsstart __P((struct tty *)); +static int zsparam __P((struct tty *, struct termios *)); +static void zs_modem __P((struct zstty_softc *zst, int onoff)); +static int zshwiflow __P((struct tty *, int)); +static void zs_hwiflow __P((struct zstty_softc *)); + +#define ZSUNIT(x) (minor(x) & 0x7ffff) +#define ZSDIALOUT(x) (minor(x) & 0x80000) + +/* + * zstty_match: how is this zs channel configured? + */ +#ifdef __BROKEN_INDIRECT_CONFIG +int +zstty_match(parent, vcf, aux) + struct device *parent; + void *vcf, *aux; +{ + struct cfdata *cf = vcf; + struct zsc_attach_args *args = aux; + + /* Exact match is better than wildcard. */ + if (cf->cf_loc[ZSCCF_CHANNEL] == args->channel) + return 2; + + /* This driver accepts wildcard. */ + if (cf->cf_loc[ZSCCF_CHANNEL] == ZSCCF_CHANNEL_DEFAULT) + return 1; + + return 0; +} +#else /* __BROKEN_INDIRECT_CONFIG */ +int +zstty_match(parent, cf, aux) + struct device *parent; + struct cfdata *cf; + void *aux; +{ + struct zsc_attach_args *args = aux; + + /* Exact match is better than wildcard. */ + if (cf->cf_loc[ZSCCF_CHANNEL] == args->channel) + return 2; + + /* This driver accepts wildcard. */ + if (cf->cf_loc[ZSCCF_CHANNEL] == ZSCCF_CHANNEL_DEFAULT) + return 1; + + return 0; +} +#endif /* __BROKEN_INDIRECT_CONFIG */ + +void +zstty_attach(parent, self, aux) + struct device *parent, *self; + void *aux; + +{ + struct zsc_softc *zsc = (void *) parent; + struct zstty_softc *zst = (void *) self; + struct cfdata *cf = self->dv_cfdata; + struct zsc_attach_args *args = aux; + struct zs_chanstate *cs; + struct tty *tp; + int channel, s, tty_unit; + dev_t dev; + + tty_unit = zst->zst_dev.dv_unit; + channel = args->channel; + cs = zsc->zsc_cs[channel]; + cs->cs_private = zst; + cs->cs_ops = &zsops_tty; + + zst->zst_cs = cs; + zst->zst_swflags = cf->cf_flags; /* softcar, etc. */ + zst->zst_hwflags = args->hwflags; + dev = makedev(zs_major, tty_unit); + + if (zst->zst_swflags) + printf(" flags 0x%x", zst->zst_swflags); + + if (ISSET(zst->zst_hwflags, ZS_HWFLAG_CONSOLE)) + printf(" (console)"); + else { +#ifdef KGDB + /* + * Allow kgdb to "take over" this port. Returns true + * if this serial port is in-use by kgdb. + */ + if (zs_check_kgdb(cs, dev)) { + printf(" (kgdb)\n"); + /* + * This is the kgdb port (exclusive use) + * so skip the normal attach code. + */ + return; + } +#endif + } + printf("\n"); + + tp = ttymalloc(); + tp->t_oproc = zsstart; + tp->t_param = zsparam; + tp->t_hwiflow = zshwiflow; + tty_attach(tp); + + zst->zst_tty = tp; + zst->zst_rbuf = malloc(zstty_rbuf_size << 1, M_DEVBUF, M_WAITOK); + zst->zst_ebuf = zst->zst_rbuf + (zstty_rbuf_size << 1); + /* Disable the high water mark. */ + zst->zst_r_hiwat = 0; + zst->zst_r_lowat = 0; + zst->zst_rbget = zst->zst_rbput = zst->zst_rbuf; + zst->zst_rbavail = zstty_rbuf_size; + + /* XXX - Do we need an MD hook here? */ + + /* + * Hardware init + */ + if (ISSET(zst->zst_hwflags, ZS_HWFLAG_CONSOLE)) { + /* Call zsparam similar to open. */ + struct termios t; + + s = splzs(); + + /* Turn on interrupts. */ + cs->cs_creg[1] = cs->cs_preg[1] = ZSWR1_RIE | ZSWR1_SIE; + zs_write_reg(cs, 1, cs->cs_creg[1]); + + /* Fetch the current modem control status, needed later. */ + cs->cs_rr0 = zs_read_csr(cs); + + splx(s); + + /* Setup the "new" parameters in t. */ + t.c_ispeed = 0; + t.c_ospeed = cs->cs_defspeed; + t.c_cflag = cs->cs_defcflag; + /* Make sure zsparam will see changes. */ + tp->t_ospeed = 0; + (void) zsparam(tp, &t); + + s = splzs(); + + /* Make sure DTR is on now. */ + zs_modem(zst, 1); + + splx(s); + } else { + /* Not the console; may need reset. */ + int reset; + + reset = (channel == 0) ? ZSWR9_A_RESET : ZSWR9_B_RESET; + + s = splzs(); + + zs_write_reg(cs, 9, reset); + + /* Will raise DTR in open. */ + zs_modem(zst, 0); + + splx(s); + } +} + + +/* + * Return pointer to our tty. + */ +struct tty * +zstty(dev) + dev_t dev; +{ + struct zstty_softc *zst; + int unit = ZSUNIT(dev); + +#ifdef DIAGNOSTIC + if (unit >= zstty_cd.cd_ndevs) + panic("zstty"); +#endif + zst = zstty_cd.cd_devs[unit]; + return (zst->zst_tty); +} + + +void +zs_shutdown(zst) + struct zstty_softc *zst; +{ + struct zs_chanstate *cs = zst->zst_cs; + struct tty *tp = zst->zst_tty; + int s; + + s = splzs(); + + /* If we were asserting flow control, then deassert it. */ + SET(zst->zst_rx_flags, RX_IBUF_BLOCKED); + zs_hwiflow(zst); + + /* Clear any break condition set with TIOCSBRK. */ + zs_break(cs, 0); + + /* + * Hang up if necessary. Wait a bit, so the other side has time to + * notice even if we immediately open the port again. + */ + if (ISSET(tp->t_cflag, HUPCL)) { + zs_modem(zst, 0); + (void) tsleep(cs, TTIPRI, ttclos, hz); + } + + /* Turn off interrupts if not the console. */ + if (ISSET(zst->zst_hwflags, ZS_HWFLAG_CONSOLE)) + cs->cs_creg[1] = cs->cs_preg[1] = ZSWR1_RIE | ZSWR1_SIE; + else + cs->cs_creg[1] = cs->cs_preg[1] = 0; + zs_write_reg(cs, 1, cs->cs_creg[1]); + + splx(s); +} + +/* + * Open a zs serial (tty) port. + */ +int +zsopen(dev, flags, mode, p) + dev_t dev; + int flags; + int mode; + struct proc *p; +{ + int unit = ZSUNIT(dev); + struct zstty_softc *zst; + struct zs_chanstate *cs; + struct tty *tp; + int s, s2; + int error; + + if (unit >= zstty_cd.cd_ndevs) + return (ENXIO); + zst = zstty_cd.cd_devs[unit]; + if (zst == 0) + return (ENXIO); + tp = zst->zst_tty; + cs = zst->zst_cs; + + /* If KGDB took the line, then tp==NULL */ + if (tp == NULL) + return (EBUSY); + + if (ISSET(tp->t_state, TS_ISOPEN) && + ISSET(tp->t_state, TS_XCLUDE) && + p->p_ucred->cr_uid != 0) + return (EBUSY); + + s = spltty(); + + /* + * Do the following iff this is a first open. + */ + if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { + struct termios t; + + tp->t_dev = dev; + + s2 = splzs(); + + /* Turn on interrupts. */ + cs->cs_creg[1] = cs->cs_preg[1] = ZSWR1_RIE | ZSWR1_SIE; + zs_write_reg(cs, 1, cs->cs_creg[1]); + + /* Fetch the current modem control status, needed later. */ + cs->cs_rr0 = zs_read_csr(cs); + + splx(s2); + + /* + * Initialize the termios status to the defaults. Add in the + * sticky bits from TIOCSFLAGS. + */ + t.c_ispeed = 0; + t.c_ospeed = cs->cs_defspeed; + t.c_cflag = cs->cs_defcflag; + if (ISSET(zst->zst_swflags, TIOCFLAG_CLOCAL)) + SET(t.c_cflag, CLOCAL); + if (ISSET(zst->zst_swflags, TIOCFLAG_CRTSCTS)) + SET(t.c_cflag, CRTSCTS); + if (ISSET(zst->zst_swflags, TIOCFLAG_CDTRCTS)) + SET(t.c_cflag, CDTRCTS); + if (ISSET(zst->zst_swflags, TIOCFLAG_MDMBUF)) + SET(t.c_cflag, MDMBUF); + /* Make sure zsparam will see changes. */ + tp->t_ospeed = 0; + (void) zsparam(tp, &t); + /* + * Note: zsparam has done: cflag, ispeed, ospeed + * so we just need to do: iflag, oflag, lflag, cc + * For "raw" mode, just leave all zeros. + */ + if (!ISSET(zst->zst_hwflags, ZS_HWFLAG_RAW)) { + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_lflag = TTYDEF_LFLAG; + } else { + tp->t_iflag = 0; + tp->t_oflag = 0; + tp->t_lflag = 0; + } + ttychars(tp); + ttsetwater(tp); + + s2 = splzs(); + + /* + * Turn on DTR. We must always do this, even if carrier is not + * present, because otherwise we'd have to use TIOCSDTR + * immediately after setting CLOCAL, which applications do not + * expect. We always assert DTR while the device is open + * unless explicitly requested to deassert it. + */ + zs_modem(zst, 1); + + /* Clear the input ring, and unblock. */ + zst->zst_rbget = zst->zst_rbput = zst->zst_rbuf; + zst->zst_rbavail = zstty_rbuf_size; + zs_iflush(cs); + CLR(zst->zst_rx_flags, RX_ANY_BLOCK); + zs_hwiflow(zst); + + splx(s2); + } + + splx(s); + + error = ttyopen(tp, ZSDIALOUT(dev), ISSET(flags, O_NONBLOCK)); + if (error) + goto bad; + + error = (*linesw[tp->t_line].l_open)(dev, tp); + if (error) + goto bad; + + return (0); + +bad: + if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { + /* + * We failed to open the device, and nobody else had it opened. + * Clean up the state as appropriate. + */ + zs_shutdown(zst); + } + + return (error); +} + +/* + * Close a zs serial port. + */ +int +zsclose(dev, flags, mode, p) + dev_t dev; + int flags; + int mode; + struct proc *p; +{ + struct zstty_softc *zst = zstty_cd.cd_devs[ZSUNIT(dev)]; + struct tty *tp = zst->zst_tty; + + /* XXX This is for cons.c. */ + if (!ISSET(tp->t_state, TS_ISOPEN)) + return 0; + + (*linesw[tp->t_line].l_close)(tp, flags); + ttyclose(tp); + + if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) { + /* + * Although we got a last close, the device may still be in + * use; e.g. if this was the dialout node, and there are still + * processes waiting for carrier on the non-dialout node. + */ + zs_shutdown(zst); + } + + return (0); +} + +/* + * Read/write zs serial port. + */ +int +zsread(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + struct zstty_softc *zst = zstty_cd.cd_devs[ZSUNIT(dev)]; + struct tty *tp = zst->zst_tty; + + return ((*linesw[tp->t_line].l_read)(tp, uio, flags)); +} + +int +zswrite(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + struct zstty_softc *zst = zstty_cd.cd_devs[ZSUNIT(dev)]; + struct tty *tp = zst->zst_tty; + + return ((*linesw[tp->t_line].l_write)(tp, uio, flags)); +} + +int +zsioctl(dev, cmd, data, flag, p) + dev_t dev; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ + struct zstty_softc *zst = zstty_cd.cd_devs[ZSUNIT(dev)]; + struct zs_chanstate *cs = zst->zst_cs; + struct tty *tp = zst->zst_tty; + int error; + int s; + + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + + error = ttioctl(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + +#ifdef ZS_MD_IOCTL + error = ZS_MD_IOCTL; + if (error >= 0) + return (error); +#endif /* ZS_MD_IOCTL */ + + error = 0; + + s = splzs(); + + switch (cmd) { + case TIOCSBRK: + zs_break(cs, 1); + break; + + case TIOCCBRK: + zs_break(cs, 0); + break; + + case TIOCGFLAGS: + *(int *)data = zst->zst_swflags; + break; + + case TIOCSFLAGS: + error = suser(p->p_ucred, &p->p_acflag); + if (error) + break; + zst->zst_swflags = *(int *)data; + break; + + case TIOCSDTR: + zs_modem(zst, 1); + break; + + case TIOCCDTR: + zs_modem(zst, 0); + break; + + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + case TIOCMGET: + default: + error = ENOTTY; + break; + } + + splx(s); + + return (error); +} + +/* + * Start or restart transmission. + */ +static void +zsstart(tp) + struct tty *tp; +{ + struct zstty_softc *zst = zstty_cd.cd_devs[ZSUNIT(tp->t_dev)]; + struct zs_chanstate *cs = zst->zst_cs; + int s; + + s = spltty(); + if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) + goto out; + if (zst->zst_tx_stopped) + goto out; + + if (tp->t_outq.c_cc <= tp->t_lowat) { + if (ISSET(tp->t_state, TS_ASLEEP)) { + CLR(tp->t_state, TS_ASLEEP); + wakeup((caddr_t)&tp->t_outq); + } + selwakeup(&tp->t_wsel); + if (tp->t_outq.c_cc == 0) + goto out; + } + + /* Grab the first contiguous region of buffer space. */ + { + u_char *tba; + int tbc; + + tba = tp->t_outq.c_cf; + tbc = ndqb(&tp->t_outq, 0); + + (void) splzs(); + + zst->zst_tba = tba; + zst->zst_tbc = tbc; + } + + SET(tp->t_state, TS_BUSY); + zst->zst_tx_busy = 1; + +#ifdef ZS_TXDMA + zs_dma_setup(cs, zst->zst_tba, zst->zst_tbc); +#else + /* Enable transmit completion interrupts if necessary. */ + if (!ISSET(cs->cs_preg[1], ZSWR1_TIE)) { + SET(cs->cs_preg[1], ZSWR1_TIE); + cs->cs_creg[1] = cs->cs_preg[1]; + zs_write_reg(cs, 1, cs->cs_creg[1]); + } + + /* Output the first character of the contiguous buffer. */ + { + zs_write_data(cs, *zst->zst_tba); + zst->zst_tbc--; + zst->zst_tba++; + } +#endif +out: + splx(s); + return; +} + +/* + * Stop output, e.g., for ^S or output flush. + */ +void +zsstop(tp, flag) + struct tty *tp; + int flag; +{ + struct zstty_softc *zst = zstty_cd.cd_devs[ZSUNIT(tp->t_dev)]; + int s; + + s = splzs(); + if (ISSET(tp->t_state, TS_BUSY)) { + /* Stop transmitting at the next chunk. */ + zst->zst_tbc = 0; + zst->zst_heldtbc = 0; + if (!ISSET(tp->t_state, TS_TTSTOP)) + SET(tp->t_state, TS_FLUSH); + } + splx(s); +} + +/* + * Set ZS tty parameters from termios. + * XXX - Should just copy the whole termios after + * making sure all the changes could be done. + */ +static int +zsparam(tp, t) + struct tty *tp; + struct termios *t; +{ + struct zstty_softc *zst = zstty_cd.cd_devs[ZSUNIT(tp->t_dev)]; + struct zs_chanstate *cs = zst->zst_cs; + int ospeed, cflag; + u_char tmp3, tmp4, tmp5, tmp15; + int s, error; + + ospeed = t->c_ospeed; + cflag = t->c_cflag; + + /* Check requested parameters. */ + if (ospeed < 0) + return (EINVAL); + if (t->c_ispeed && t->c_ispeed != ospeed) + return (EINVAL); + + /* + * For the console, always force CLOCAL and !HUPCL, so that the port + * is always active. + */ + if (ISSET(zst->zst_swflags, TIOCFLAG_SOFTCAR) || + ISSET(zst->zst_hwflags, ZS_HWFLAG_CONSOLE)) { + SET(cflag, CLOCAL); + CLR(cflag, HUPCL); + } + + /* + * Only whack the UART when params change. + * Some callers need to clear tp->t_ospeed + * to make sure initialization gets done. + */ + if (tp->t_ospeed == ospeed && + tp->t_cflag == cflag) + return (0); + + /* + * Call MD functions to deal with changed + * clock modes or H/W flow control modes. + * The BRG divisor is set now. (reg 12,13) + */ + error = zs_set_speed(cs, ospeed); + if (error) + return (error); + error = zs_set_modes(cs, cflag); + if (error) + return (error); + + /* + * Block interrupts so that state will not + * be altered until we are done setting it up. + * + * Initial values in cs_preg are set before + * our attach routine is called. The master + * interrupt enable is handled by zsc.c + * + */ + s = splzs(); + + cs->cs_rr0_mask = cs->cs_rr0_cts | cs->cs_rr0_dcd; + tmp15 = cs->cs_preg[15]; +#if 0 + if (ISSET(cs->cs_rr0_mask, ZSRR0_DCD)) + SET(tmp15, ZSWR15_DCD_IE); + else + CLR(tmp15, ZSWR15_DCD_IE); + if (ISSET(cs->cs_rr0_mask, ZSRR0_CTS)) + SET(tmp15, ZSWR15_CTS_IE); + else + CLR(tmp15, ZSWR15_CTS_IE); +#else + SET(tmp15, ZSWR15_DCD_IE | ZSWR15_CTS_IE); +#endif + cs->cs_preg[15] = tmp15; + + /* Recompute character size bits. */ + tmp3 = cs->cs_preg[3]; + tmp5 = cs->cs_preg[5]; + CLR(tmp3, ZSWR3_RXSIZE); + CLR(tmp5, ZSWR5_TXSIZE); + switch (ISSET(cflag, CSIZE)) { + case CS5: + SET(tmp3, ZSWR3_RX_5); + SET(tmp5, ZSWR5_TX_5); + break; + case CS6: + SET(tmp3, ZSWR3_RX_6); + SET(tmp5, ZSWR5_TX_6); + break; + case CS7: + SET(tmp3, ZSWR3_RX_7); + SET(tmp5, ZSWR5_TX_7); + break; + case CS8: + SET(tmp3, ZSWR3_RX_8); + SET(tmp5, ZSWR5_TX_8); + break; + } + cs->cs_preg[3] = tmp3; + cs->cs_preg[5] = tmp5; + + /* + * Recompute the stop bits and parity bits. Note that + * zs_set_speed() may have set clock selection bits etc. + * in wr4, so those must preserved. + */ + tmp4 = cs->cs_preg[4]; + CLR(tmp4, ZSWR4_SBMASK | ZSWR4_PARMASK); + if (ISSET(cflag, CSTOPB)) + SET(tmp4, ZSWR4_TWOSB); + else + SET(tmp4, ZSWR4_ONESB); + if (!ISSET(cflag, PARODD)) + SET(tmp4, ZSWR4_EVENP); + if (ISSET(cflag, PARENB)) + SET(tmp4, ZSWR4_PARENB); + cs->cs_preg[4] = tmp4; + + /* And copy to tty. */ + tp->t_ispeed = 0; + tp->t_ospeed = ospeed; + tp->t_cflag = cflag; + + /* + * If nothing is being transmitted, set up new current values, + * else mark them as pending. + */ + if (!cs->cs_heldchange) { + if (zst->zst_tx_busy) { + zst->zst_heldtbc = zst->zst_tbc; + zst->zst_tbc = 0; + cs->cs_heldchange = 1; + } else + zs_loadchannelregs(cs); + } + + if (!ISSET(cflag, CHWFLOW)) { + /* Disable the high water mark. */ + zst->zst_r_hiwat = 0; + zst->zst_r_lowat = 0; + if (ISSET(zst->zst_rx_flags, RX_TTY_OVERFLOWED)) { + CLR(zst->zst_rx_flags, RX_TTY_OVERFLOWED); + zst->zst_rx_ready = 1; + cs->cs_softreq = 1; + } + if (ISSET(zst->zst_rx_flags, RX_TTY_BLOCKED|RX_IBUF_BLOCKED)) { + CLR(zst->zst_rx_flags, RX_TTY_BLOCKED|RX_IBUF_BLOCKED); + zs_hwiflow(zst); + } + } else { + zst->zst_r_hiwat = zstty_rbuf_hiwat; + zst->zst_r_lowat = zstty_rbuf_lowat; + } + + splx(s); + + /* + * Update the tty layer's idea of the carrier bit, in case we changed + * CLOCAL or MDMBUF. We don't hang up here; we only do that by + * explicit request. + */ + (void) (*linesw[tp->t_line].l_modem)(tp, ISSET(cs->cs_rr0, ZSRR0_DCD)); + + if (!ISSET(cflag, CHWFLOW)) { + if (zst->zst_tx_stopped) { + zst->zst_tx_stopped = 0; + zsstart(tp); + } + } + + return (0); +} + +/* + * Raise or lower modem control (DTR/RTS) signals. If a character is + * in transmission, the change is deferred. + */ +static void +zs_modem(zst, onoff) + struct zstty_softc *zst; + int onoff; +{ + struct zs_chanstate *cs = zst->zst_cs; + + if (cs->cs_wr5_dtr == 0) + return; + + if (onoff) + SET(cs->cs_preg[5], cs->cs_wr5_dtr); + else + CLR(cs->cs_preg[5], cs->cs_wr5_dtr); + + if (!cs->cs_heldchange) { + if (zst->zst_tx_busy) { + zst->zst_heldtbc = zst->zst_tbc; + zst->zst_tbc = 0; + cs->cs_heldchange = 1; + } else + zs_loadchannelregs(cs); + } +} + +/* + * Try to block or unblock input using hardware flow-control. + * This is called by kern/tty.c if MDMBUF|CRTSCTS is set, and + * if this function returns non-zero, the TS_TBLOCK flag will + * be set or cleared according to the "block" arg passed. + */ +int +zshwiflow(tp, block) + struct tty *tp; + int block; +{ + struct zstty_softc *zst = zstty_cd.cd_devs[ZSUNIT(tp->t_dev)]; + struct zs_chanstate *cs = zst->zst_cs; + int s; + + if (cs->cs_wr5_rts == 0) + return (0); + + s = splzs(); + if (block) { + if (!ISSET(zst->zst_rx_flags, RX_TTY_BLOCKED)) { + SET(zst->zst_rx_flags, RX_TTY_BLOCKED); + zs_hwiflow(zst); + } + } else { + if (ISSET(zst->zst_rx_flags, RX_TTY_OVERFLOWED)) { + CLR(zst->zst_rx_flags, RX_TTY_OVERFLOWED); + zst->zst_rx_ready = 1; + cs->cs_softreq = 1; + } + if (ISSET(zst->zst_rx_flags, RX_TTY_BLOCKED)) { + CLR(zst->zst_rx_flags, RX_TTY_BLOCKED); + zs_hwiflow(zst); + } + } + splx(s); + return (1); +} + +/* + * Internal version of zshwiflow + * called at splzs + */ +static void +zs_hwiflow(zst) + struct zstty_softc *zst; +{ + struct zs_chanstate *cs = zst->zst_cs; + + if (cs->cs_wr5_rts == 0) + return; + + if (ISSET(zst->zst_rx_flags, RX_ANY_BLOCK)) { + CLR(cs->cs_preg[5], cs->cs_wr5_rts); + CLR(cs->cs_creg[5], cs->cs_wr5_rts); + } else { + SET(cs->cs_preg[5], cs->cs_wr5_rts); + SET(cs->cs_creg[5], cs->cs_wr5_rts); + } + zs_write_reg(cs, 5, cs->cs_creg[5]); +} + + +/**************************************************************** + * Interface to the lower layer (zscc) + ****************************************************************/ + +static void zstty_rxint __P((struct zs_chanstate *)); +static void zstty_txint __P((struct zs_chanstate *)); +static void zstty_stint __P((struct zs_chanstate *)); + +#define integrate static inline +static void zstty_softint __P((struct zs_chanstate *)); +integrate void zstty_rxsoft __P((struct zstty_softc *, struct tty *)); +integrate void zstty_txsoft __P((struct zstty_softc *, struct tty *)); +integrate void zstty_stsoft __P((struct zstty_softc *, struct tty *)); +static void zstty_diag __P((void *)); + +/* + * receiver ready interrupt. + * called at splzs + */ +static void +zstty_rxint(cs) + struct zs_chanstate *cs; +{ + struct zstty_softc *zst = cs->cs_private; + u_char *put, *end; + u_int cc; + u_char rr0, rr1, c; + + end = zst->zst_ebuf; + put = zst->zst_rbput; + cc = zst->zst_rbavail; + + while (cc > 0) { + /* + * First read the status, because reading the received char + * destroys the status of this char. + */ + rr1 = zs_read_reg(cs, 1); + c = zs_read_data(cs); + + if (ISSET(rr1, ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) { + /* Clear the receive error. */ + zs_write_csr(cs, ZSWR0_RESET_ERRORS); + } + + put[0] = c; + put[1] = rr1; + put += 2; + if (put >= end) + put = zst->zst_rbuf; + cc--; + + rr0 = zs_read_csr(cs); + if (!ISSET(rr0, ZSRR0_RX_READY)) + break; + } + + /* + * Current string of incoming characters ended because + * no more data was available or we ran out of space. + * Schedule a receive event if any data was received. + * If we're out of space, turn off receive interrupts. + */ + zst->zst_rbput = put; + zst->zst_rbavail = cc; + if (!ISSET(zst->zst_rx_flags, RX_TTY_OVERFLOWED)) { + zst->zst_rx_ready = 1; + cs->cs_softreq = 1; + } + + /* + * See if we are in danger of overflowing a buffer. If + * so, use hardware flow control to ease the pressure. + */ + if (!ISSET(zst->zst_rx_flags, RX_IBUF_BLOCKED) && + cc < zst->zst_r_hiwat) { + SET(zst->zst_rx_flags, RX_IBUF_BLOCKED); + zs_hwiflow(zst); + } + + /* + * If we're out of space, disable receive interrupts + * until the queue has drained a bit. + */ + if (!cc) { + SET(zst->zst_rx_flags, RX_IBUF_OVERFLOWED); + CLR(cs->cs_preg[1], ZSWR1_RIE); + cs->cs_creg[1] = cs->cs_preg[1]; + zs_write_reg(cs, 1, cs->cs_creg[1]); + } + +#if 0 + printf("%xH%04d\n", zst->zst_rx_flags, zst->zst_rbavail); +#endif +} + +/* + * transmitter ready interrupt. (splzs) + */ +static void +zstty_txint(cs) + struct zs_chanstate *cs; +{ + struct zstty_softc *zst = cs->cs_private; + + /* + * If we've delayed a parameter change, do it now, and restart + * output. + */ + if (cs->cs_heldchange) { + zs_loadchannelregs(cs); + cs->cs_heldchange = 0; + zst->zst_tbc = zst->zst_heldtbc; + zst->zst_heldtbc = 0; + } + + /* Output the next character in the buffer, if any. */ + if (zst->zst_tbc > 0) { + zs_write_data(cs, *zst->zst_tba); + zst->zst_tbc--; + zst->zst_tba++; + } else { + /* Disable transmit completion interrupts if necessary. */ + if (ISSET(cs->cs_preg[1], ZSWR1_TIE)) { + CLR(cs->cs_preg[1], ZSWR1_TIE); + cs->cs_creg[1] = cs->cs_preg[1]; + zs_write_reg(cs, 1, cs->cs_creg[1]); + } + if (zst->zst_tx_busy) { + zst->zst_tx_busy = 0; + zst->zst_tx_done = 1; + cs->cs_softreq = 1; + } + } +} + +/* + * status change interrupt. (splzs) + */ +static void +zstty_stint(cs) + struct zs_chanstate *cs; +{ + struct zstty_softc *zst = cs->cs_private; + u_char rr0, delta; + + rr0 = zs_read_csr(cs); + zs_write_csr(cs, ZSWR0_RESET_STATUS); + + /* + * Check here for console break, so that we can abort + * even when interrupts are locking up the machine. + */ + if (ISSET(rr0, ZSRR0_BREAK) && + ISSET(zst->zst_hwflags, ZS_HWFLAG_CONSOLE)) { + zs_abort(cs); + return; + } + + delta = rr0 ^ cs->cs_rr0; + cs->cs_rr0 = rr0; + if (ISSET(delta, cs->cs_rr0_mask)) { + SET(cs->cs_rr0_delta, delta); + + /* + * Stop output immediately if we lose the output + * flow control signal or carrier detect. + */ + if (ISSET(~rr0, cs->cs_rr0_mask)) { + zst->zst_tbc = 0; + zst->zst_heldtbc = 0; + } + + zst->zst_st_check = 1; + cs->cs_softreq = 1; + } +} + +void +zstty_diag(arg) + void *arg; +{ + struct zstty_softc *zst = arg; + int overflows, floods; + int s; + + s = splzs(); + overflows = zst->zst_overflows; + zst->zst_overflows = 0; + floods = zst->zst_floods; + zst->zst_floods = 0; + zst->zst_errors = 0; + splx(s); + + log(LOG_WARNING, "%s: %d silo overflow%s, %d ibuf flood%s\n", + zst->zst_dev.dv_xname, + overflows, overflows == 1 ? "" : "s", + floods, floods == 1 ? "" : "s"); +} + +integrate void +zstty_rxsoft(zst, tp) + struct zstty_softc *zst; + struct tty *tp; +{ + struct zs_chanstate *cs = zst->zst_cs; + int (*rint) __P((int c, struct tty *tp)) = linesw[tp->t_line].l_rint; + u_char *get, *end; + u_int cc, scc; + u_char rr1; + int code; + int s; + + end = zst->zst_ebuf; + get = zst->zst_rbget; + scc = cc = zstty_rbuf_size - zst->zst_rbavail; + + if (cc == zstty_rbuf_size) { + zst->zst_floods++; + if (zst->zst_errors++ == 0) + timeout(zstty_diag, zst, 60 * hz); + } + + while (cc) { + code = get[0]; + rr1 = get[1]; + if (ISSET(rr1, ZSRR1_DO | ZSRR1_FE | ZSRR1_PE)) { + if (ISSET(rr1, ZSRR1_DO)) { + zst->zst_overflows++; + if (zst->zst_errors++ == 0) + timeout(zstty_diag, zst, 60 * hz); + } + if (ISSET(rr1, ZSRR1_FE)) + SET(code, TTY_FE); + if (ISSET(rr1, ZSRR1_PE)) + SET(code, TTY_PE); + } + if ((*rint)(code, tp) == -1) { + /* + * The line discipline's buffer is out of space. + */ + if (!ISSET(zst->zst_rx_flags, RX_TTY_BLOCKED)) { + /* + * We're either not using flow control, or the + * line discipline didn't tell us to block for + * some reason. Either way, we have no way to + * know when there's more space available, so + * just drop the rest of the data. + */ + get += cc << 1; + if (get >= end) + get -= zstty_rbuf_size << 1; + cc = 0; + } else { + /* + * Don't schedule any more receive processing + * until the line discipline tells us there's + * space available (through comhwiflow()). + * Leave the rest of the data in the input + * buffer. + */ + SET(zst->zst_rx_flags, RX_TTY_OVERFLOWED); + } + break; + } + get += 2; + if (get >= end) + get = zst->zst_rbuf; + cc--; + } + + if (cc != scc) { + zst->zst_rbget = get; + s = splzs(); + cc = zst->zst_rbavail += scc - cc; + /* Buffers should be ok again, release possible block. */ + if (cc >= zst->zst_r_lowat) { + if (ISSET(zst->zst_rx_flags, RX_IBUF_OVERFLOWED)) { + CLR(zst->zst_rx_flags, RX_IBUF_OVERFLOWED); + SET(cs->cs_preg[1], ZSWR1_RIE); + cs->cs_creg[1] = cs->cs_preg[1]; + zs_write_reg(cs, 1, cs->cs_creg[1]); + } + if (ISSET(zst->zst_rx_flags, RX_IBUF_BLOCKED)) { + CLR(zst->zst_rx_flags, RX_IBUF_BLOCKED); + zs_hwiflow(zst); + } + } + splx(s); + } + +#if 0 + printf("%xS%04d\n", zst->zst_rx_flags, zst->zst_rbavail); +#endif +} + +integrate void +zstty_txsoft(zst, tp) + struct zstty_softc *zst; + struct tty *tp; +{ + + CLR(tp->t_state, TS_BUSY); + if (ISSET(tp->t_state, TS_FLUSH)) + CLR(tp->t_state, TS_FLUSH); + else + ndflush(&tp->t_outq, (int)(zst->zst_tba - tp->t_outq.c_cf)); + (*linesw[tp->t_line].l_start)(tp); +} + +integrate void +zstty_stsoft(zst, tp) + struct zstty_softc *zst; + struct tty *tp; +{ + struct zs_chanstate *cs = zst->zst_cs; + u_char rr0, delta; + int s; + + s = splzs(); + rr0 = cs->cs_rr0; + delta = cs->cs_rr0_delta; + cs->cs_rr0_delta = 0; + splx(s); + + if (ISSET(delta, cs->cs_rr0_dcd)) { + /* + * Inform the tty layer that carrier detect changed. + */ + (void) (*linesw[tp->t_line].l_modem)(tp, ISSET(rr0, ZSRR0_DCD)); + } + + if (ISSET(delta, cs->cs_rr0_cts)) { + /* Block or unblock output according to flow control. */ + if (ISSET(rr0, cs->cs_rr0_cts)) { + zst->zst_tx_stopped = 0; + (*linesw[tp->t_line].l_start)(tp); + } else { + zst->zst_tx_stopped = 1; + } + } +} + +/* + * Software interrupt. Called at zssoft + * + * The main job to be done here is to empty the input ring + * by passing its contents up to the tty layer. The ring is + * always emptied during this operation, therefore the ring + * must not be larger than the space after "high water" in + * the tty layer, or the tty layer might drop our input. + * + * Note: an "input blockage" condition is assumed to exist if + * EITHER the TS_TBLOCK flag or zst_rx_blocked flag is set. + */ +static void +zstty_softint(cs) + struct zs_chanstate *cs; +{ + struct zstty_softc *zst = cs->cs_private; + struct tty *tp = zst->zst_tty; + int s; + + s = spltty(); + + if (zst->zst_rx_ready) { + zst->zst_rx_ready = 0; + zstty_rxsoft(zst, tp); + } + + if (zst->zst_st_check) { + zst->zst_st_check = 0; + zstty_stsoft(zst, tp); + } + + if (zst->zst_tx_done) { + zst->zst_tx_done = 0; + zstty_txsoft(zst, tp); + } + + splx(s); +} + +struct zsops zsops_tty = { + zstty_rxint, /* receive char available */ + zstty_stint, /* external/status */ + zstty_txint, /* xmit buffer empty */ + zstty_softint, /* process software interrupt */ +}; + +#ifdef ZS_TXDMA +void +zstty_txdma_int(arg) + void *arg; +{ + struct zs_chanstate *cs = arg; + struct zstty_softc *zst = cs->cs_private; + + zst->zst_tba += zst->zst_tbc; + zst->zst_tbc = 0; + + if (zst->zst_tx_busy) { + zst->zst_tx_busy = 0; + zst->zst_tx_done = 1; + cs->cs_softreq = 1; + } +} +#endif diff --git a/sys/arch/macppc/dev/zs.c b/sys/arch/macppc/dev/zs.c new file mode 100644 index 00000000000..6e2ba794bf7 --- /dev/null +++ b/sys/arch/macppc/dev/zs.c @@ -0,0 +1,1156 @@ +/* $NetBSD: zs.c,v 1.1 1998/05/15 10:15:49 tsubai Exp $ */ + +/* + * Copyright (c) 1996 Bill Studenmund + * Copyright (c) 1995 Gordon W. Ross + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * 4. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Gordon Ross + * + * 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. + */ + +/* + * Zilog Z8530 Dual UART driver (machine-dependent part) + * + * Runs two serial lines per chip using slave drivers. + * Plain tty/async lines use the zs_async slave. + * Sun keyboard/mouse uses the zs_kbd/zs_ms slaves. + * Other ports use their own mice & keyboard slaves. + * + * Credits & history: + * + * With NetBSD 1.1, port-mac68k started using a port of the port-sparc + * (port-sun3?) zs.c driver (which was in turn based on code in the + * Berkeley 4.4 Lite release). Bill Studenmund did the port, with + * help from Allen Briggs and Gordon Ross <gwr@netbsd.org>. Noud de + * Brouwer field-tested the driver at a local ISP. + * + * Bill Studenmund and Gordon Ross then ported the machine-independant + * z8530 driver to work with port-mac68k. NetBSD 1.2 contained an + * intermediate version (mac68k using a local, patched version of + * the m.i. drivers), with NetBSD 1.3 containing a full version. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/device.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/tty.h> +#include <sys/time.h> +#include <sys/kernel.h> +#include <sys/syslog.h> + +#include <dev/cons.h> +#include <dev/ofw/openfirm.h> +#include <dev/ic/z8530reg.h> + +#include <machine/z8530var.h> +#include <machine/autoconf.h> +#include <machine/cpu.h> +#include <machine/pio.h> + +/* Are these in a header file anywhere? */ +/* Booter flags interface */ +#define ZSMAC_RAW 0x01 +#define ZSMAC_LOCALTALK 0x02 +#define ZS_STD_BRG (57600*4) + +#include "zsc.h" /* get the # of zs chips defined */ + +/* + * Some warts needed by z8530tty.c - + */ +int zs_def_cflag = (CREAD | CS8 | HUPCL); +int zs_major = 12; + +/* + * abort detection on console will now timeout after iterating on a loop + * the following # of times. Cheep hack. Also, abort detection is turned + * off after a timeout (i.e. maybe there's not a terminal hooked up). + */ +#define ZSABORT_DELAY 3000000 + +/* The layout of this is hardware-dependent (padding, order). */ +struct zschan { + volatile u_char zc_csr; /* ctrl,status, and indirect access */ + u_char zc_xxx0[15]; + volatile u_char zc_data; /* data */ + u_char zc_xxx1[15]; +}; +struct zsdevice { + /* Yes, they are backwards. */ + struct zschan zs_chan_b; + struct zschan zs_chan_a; +}; + +/* Saved PROM mappings */ +static struct zsdevice *zsaddr[2]; + +/* Flags from cninit() */ +static int zs_hwflags[NZSC][2]; +/* Default speed for each channel */ +static int zs_defspeed[NZSC][2] = { + { 38400, /* tty00 */ + 38400 }, /* tty01 */ +}; +/* console stuff */ +void *zs_conschan = 0; +int zs_consunit; +#ifdef ZS_CONSOLE_ABORT +int zs_cons_canabort = 1; +#else +int zs_cons_canabort = 0; +#endif /* ZS_CONSOLE_ABORT*/ + +/* device to which the console is attached--if serial. */ +/* Mac stuff */ + +static struct zschan *zs_get_chan_addr __P((int zsc_unit, int channel)); +void zs_init __P((void)); +int zs_cn_check_speed __P((int bps)); + +/* + * Even though zsparam will set up the clock multiples, etc., we + * still set them here as: 1) mice & keyboards don't use zsparam, + * and 2) the console stuff uses these defaults before device + * attach. + */ + +static u_char zs_init_reg[16] = { + 0, /* 0: CMD (reset, etc.) */ + 0, /* 1: No interrupts yet. */ + 0, /* IVECT */ + ZSWR3_RX_8 | ZSWR3_RX_ENABLE, + ZSWR4_CLK_X16 | ZSWR4_ONESB | ZSWR4_EVENP, + ZSWR5_TX_8 | ZSWR5_TX_ENABLE, + 0, /* 6: TXSYNC/SYNCLO */ + 0, /* 7: RXSYNC/SYNCHI */ + 0, /* 8: alias for data port */ + ZSWR9_MASTER_IE, + 0, /*10: Misc. TX/RX control bits */ + ZSWR11_TXCLK_BAUD | ZSWR11_RXCLK_BAUD, + 1, /*12: BAUDLO (default=38400) */ + 0, /*13: BAUDHI (default=38400) */ + ZSWR14_BAUD_ENA, + ZSWR15_BREAK_IE | ZSWR15_DCD_IE, +}; + +struct zschan * +zs_get_chan_addr(zs_unit, channel) + int zs_unit, channel; +{ + struct zsdevice *addr; + struct zschan *zc; + + if (zs_unit >= 1) + return NULL; + addr = zsaddr[zs_unit]; + if (addr == NULL) + return NULL; + if (channel == 0) { + zc = &addr->zs_chan_a; + } else { + zc = &addr->zs_chan_b; + } + return (zc); +} + + +/**************************************************************** + * Autoconfig + ****************************************************************/ + +/* Definition of the driver for autoconfig. */ +static int zsc_match __P((struct device *, struct cfdata *, void *)); +static void zsc_attach __P((struct device *, struct device *, void *)); +static int zsc_print __P((void *, const char *name)); + +struct cfattach zsc_ca = { + sizeof(struct zsc_softc), zsc_match, zsc_attach +}; + +extern struct cfdriver zsc_cd; + +int zshard __P((void *)); +int zssoft __P((void *)); +#ifdef ZS_TXDMA +static int zs_txdma_int __P((void *)); +#endif + +void zscnprobe __P((struct consdev *)); +void zscninit __P((struct consdev *)); +int zscngetc __P((dev_t)); +void zscnputc __P((dev_t, int)); +void zscnpollc __P((dev_t, int)); + +/* + * Is the zs chip present? + */ +static int +zsc_match(parent, cf, aux) + struct device *parent; + struct cfdata *cf; + void *aux; +{ + struct confargs *ca = aux; + int unit = cf->cf_unit; + + if (strcmp(ca->ca_name, "escc") != 0) + return 0; + + if (unit > 1) + return 0; + + return 1; +} + +/* + * Attach a found zs. + * + * Match slave number to zs unit number, so that misconfiguration will + * not set up the keyboard as ttya, etc. + */ +static void +zsc_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct zsc_softc *zsc = (void *)self; + struct confargs *ca = aux; + struct zsc_attach_args zsc_args; + volatile struct zschan *zc; + struct xzs_chanstate *xcs; + struct zs_chanstate *cs; + int zsc_unit, channel; + int s, chip, theflags; + int node, intr[2][3]; + u_int regs[6]; + + zsc_unit = zsc->zsc_dev.dv_unit; + node = ca->ca_node; + + node = OF_child(node); /* ch-a */ + + for (channel = 0; channel < 2; channel++) { + OF_getprop(node, "AAPL,interrupts", + intr[channel], sizeof(intr[channel])); + OF_getprop(node, "reg", regs, sizeof(regs)); + regs[0] += ca->ca_baseaddr; + regs[2] += ca->ca_baseaddr; + regs[4] += ca->ca_baseaddr; +#ifdef ZS_TXDMA + zsc->zsc_txdmareg[channel] = mapiodev(regs[2], regs[3]); + zsc->zsc_txdmacmd[channel] = + dbdma_alloc(sizeof(dbdma_command_t) * 3); + bzero(zsc->zsc_txdmacmd[channel], sizeof(dbdma_command_t) * 3); + dbdma_reset(zsc->zsc_txdmareg[channel]); +#endif + node = OF_peer(node); /* ch-b */ + } + zsaddr[0] = mapiodev(regs[0], regs[1]); + + printf(": irq %d,%d\n", intr[0][0], intr[1][0]); + + /* Make sure everything's inited ok. */ + if (zsaddr[zsc_unit] == NULL) + panic("zs_attach: zs%d not mapped\n", zsc_unit); + + if ((zs_hwflags[zsc_unit][0] | zs_hwflags[zsc_unit][1]) & + ZS_HWFLAG_CONSOLE) { + + zs_conschan = zs_get_chan_addr(zsc_unit, minor(cn_tab->cn_dev)); + cn_tab->cn_getc = zscngetc; + cn_tab->cn_putc = zscnputc; + } + + /* + * Initialize software state for each channel. + */ + for (channel = 0; channel < 2; channel++) { + zsc_args.channel = channel; + zsc_args.hwflags = zs_hwflags[zsc_unit][channel]; + xcs = &zsc->xzsc_xcs_store[channel]; + cs = &xcs->xzs_cs; + zsc->zsc_cs[channel] = cs; + + cs->cs_channel = channel; + cs->cs_private = NULL; + cs->cs_ops = &zsops_null; + + zc = zs_get_chan_addr(zsc_unit, channel); + cs->cs_reg_csr = &zc->zc_csr; + cs->cs_reg_data = &zc->zc_data; + + bcopy(zs_init_reg, cs->cs_creg, 16); + bcopy(zs_init_reg, cs->cs_preg, 16); + + /* Current BAUD rate generator clock. */ + cs->cs_brg_clk = ZS_STD_BRG; /* RTxC is 230400*16, so use 230400 */ + cs->cs_defspeed = zs_defspeed[zsc_unit][channel]; + cs->cs_defcflag = zs_def_cflag; + + /* Make these correspond to cs_defcflag (-crtscts) */ + cs->cs_rr0_dcd = ZSRR0_DCD; + cs->cs_rr0_cts = 0; + cs->cs_wr5_dtr = ZSWR5_DTR; + cs->cs_wr5_rts = 0; + +#ifdef __notyet__ + cs->cs_slave_type = ZS_SLAVE_NONE; +#endif + + /* Define BAUD rate stuff. */ + xcs->cs_clocks[0].clk = ZS_STD_BRG * 16; + xcs->cs_clocks[0].flags = ZSC_RTXBRG; + xcs->cs_clocks[1].flags = + ZSC_RTXBRG | ZSC_RTXDIV | ZSC_VARIABLE | ZSC_EXTERN; + xcs->cs_clocks[2].flags = ZSC_TRXDIV | ZSC_VARIABLE; + xcs->cs_clock_count = 3; + if (channel == 0) { + theflags = 0; /*mac68k_machine.modem_flags;*/ + /*xcs->cs_clocks[1].clk = mac68k_machine.modem_dcd_clk;*/ + /*xcs->cs_clocks[2].clk = mac68k_machine.modem_cts_clk;*/ + xcs->cs_clocks[1].clk = 0; + xcs->cs_clocks[2].clk = 0; + } else { + theflags = 0; /*mac68k_machine.print_flags;*/ + xcs->cs_clocks[1].flags = ZSC_VARIABLE; + /* + * Yes, we aren't defining ANY clock source enables for the + * printer's DCD clock in. The hardware won't let us + * use it. But a clock will freak out the chip, so we + * let you set it, telling us to bar interrupts on the line. + */ + /*xcs->cs_clocks[1].clk = mac68k_machine.print_dcd_clk;*/ + /*xcs->cs_clocks[2].clk = mac68k_machine.print_cts_clk;*/ + xcs->cs_clocks[1].clk = 0; + xcs->cs_clocks[2].clk = 0; + } + if (xcs->cs_clocks[1].clk) + zsc_args.hwflags |= ZS_HWFLAG_NO_DCD; + if (xcs->cs_clocks[2].clk) + zsc_args.hwflags |= ZS_HWFLAG_NO_CTS; + + /* Set defaults in our "extended" chanstate. */ + xcs->cs_csource = 0; + xcs->cs_psource = 0; + xcs->cs_cclk_flag = 0; /* Nothing fancy by default */ + xcs->cs_pclk_flag = 0; + + if (theflags & ZSMAC_RAW) { + zsc_args.hwflags |= ZS_HWFLAG_RAW; + printf(" (raw defaults)"); + } + + /* + * XXX - This might be better done with a "stub" driver + * (to replace zstty) that ignores LocalTalk for now. + */ + if (theflags & ZSMAC_LOCALTALK) { + printf(" shielding from LocalTalk"); + cs->cs_defspeed = 1; + cs->cs_creg[ZSRR_BAUDLO] = cs->cs_preg[ZSRR_BAUDLO] = 0xff; + cs->cs_creg[ZSRR_BAUDHI] = cs->cs_preg[ZSRR_BAUDHI] = 0xff; + zs_write_reg(cs, ZSRR_BAUDLO, 0xff); + zs_write_reg(cs, ZSRR_BAUDHI, 0xff); + /* + * If we might have LocalTalk, then make sure we have the + * Baud rate low-enough to not do any damage. + */ + } + + /* + * We used to disable chip interrupts here, but we now + * do that in zscnprobe, just in case MacOS left the chip on. + */ + + xcs->cs_chip = chip; + + /* Stash away a copy of the final H/W flags. */ + xcs->cs_hwflags = zsc_args.hwflags; + + /* + * Look for a child driver for this channel. + * The child attach will setup the hardware. + */ + if (!config_found(self, (void *)&zsc_args, zsc_print)) { + /* No sub-driver. Just reset it. */ + u_char reset = (channel == 0) ? + ZSWR9_A_RESET : ZSWR9_B_RESET; + s = splzs(); + zs_write_reg(cs, 9, reset); + splx(s); + } + } + + /* XXX - Now safe to install interrupt handlers. */ + intr_establish(intr[0][0], IST_LEVEL, IPL_TTY, zshard, NULL); + intr_establish(intr[1][0], IST_LEVEL, IPL_TTY, zshard, NULL); +#ifdef ZS_TXDMA + intr_establish(intr[0][1], IST_LEVEL, IPL_TTY, zs_txdma_int, (void *)0); + intr_establish(intr[1][1], IST_LEVEL, IPL_TTY, zs_txdma_int, (void *)1); +#endif + + /* + * Set the master interrupt enable and interrupt vector. + * (common to both channels, do it on A) + */ + cs = zsc->zsc_cs[0]; + s = splzs(); + /* interrupt vector */ + zs_write_reg(cs, 2, zs_init_reg[2]); + /* master interrupt control (enable) */ + zs_write_reg(cs, 9, zs_init_reg[9]); + splx(s); +} + +static int +zsc_print(aux, name) + void *aux; + const char *name; +{ + struct zsc_attach_args *args = aux; + + if (name != NULL) + printf("%s: ", name); + + if (args->channel != -1) + printf(" channel %d", args->channel); + + return UNCONF; +} + +int +zsmdioctl(cs, cmd, data) + struct zs_chanstate *cs; + u_long cmd; + caddr_t data; +{ + switch (cmd) { + default: + return (-1); + } + return (0); +} + +void +zsmd_setclock(cs) + struct zs_chanstate *cs; +{ + struct xzs_chanstate *xcs = (void *)cs; + + if (cs->cs_channel != 0) + return; + + /* + * If the new clock has the external bit set, then select the + * external source. + */ + /*via_set_modem((xcs->cs_pclk_flag & ZSC_EXTERN) ? 1 : 0);*/ +} + +static int zssoftpending; + +/* + * Our ZS chips all share a common, autovectored interrupt, + * so we have to look at all of them on each interrupt. + */ +int +zshard(arg) + void *arg; +{ + register struct zsc_softc *zsc; + register int unit, rval; + + rval = 0; + for (unit = 0; unit < zsc_cd.cd_ndevs; unit++) { + zsc = zsc_cd.cd_devs[unit]; + if (zsc == NULL) + continue; + rval |= zsc_intr_hard(zsc); + if ((zsc->zsc_cs[0]->cs_softreq) || + (zsc->zsc_cs[1]->cs_softreq)) + { + /* zsc_req_softint(zsc); */ + /* We are at splzs here, so no need to lock. */ + if (zssoftpending == 0) { + zssoftpending = 1; + setsoftserial(); + } + } + } + return (rval); +} + +/* + * Similar scheme as for zshard (look at all of them) + */ +int +zssoft(arg) + void *arg; +{ + register struct zsc_softc *zsc; + register int unit; + + /* This is not the only ISR on this IPL. */ + if (zssoftpending == 0) + return (0); + + /* + * The soft intr. bit will be set by zshard only if + * the variable zssoftpending is zero. + */ + zssoftpending = 0; + + for (unit = 0; unit < zsc_cd.cd_ndevs; ++unit) { + zsc = zsc_cd.cd_devs[unit]; + if (zsc == NULL) + continue; + (void) zsc_intr_soft(zsc); + } + return (1); +} + +#ifdef ZS_TXDMA +int +zs_txdma_int(arg) + void *arg; +{ + int ch = (int)arg; + struct zsc_softc *zsc; + struct zs_chanstate *cs; + int unit = 0; /* XXX */ + extern int zstty_txdma_int(); + + zsc = zsc_cd.cd_devs[unit]; + if (zsc == NULL) + panic("zs_txdma_int"); + + cs = zsc->zsc_cs[ch]; + zstty_txdma_int(cs); + + if (cs->cs_softreq) { + if (zssoftpending == 0) { + zssoftpending = 1; + setsoftserial(); + } + } + return 1; +} + +void +zs_dma_setup(cs, pa, len) + struct zs_chanstate *cs; + caddr_t pa; + int len; +{ + struct zsc_softc *zsc; + dbdma_command_t *cmdp; + int ch = cs->cs_channel; + + zsc = zsc_cd.cd_devs[ch]; + cmdp = zsc->zsc_txdmacmd[ch]; + + DBDMA_BUILD(cmdp, DBDMA_CMD_OUT_LAST, 0, len, kvtop(pa), + DBDMA_INT_ALWAYS, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + cmdp++; + DBDMA_BUILD(cmdp, DBDMA_CMD_STOP, 0, 0, 0, + DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); + + __asm __volatile("eieio"); + + dbdma_start(zsc->zsc_txdmareg[ch], zsc->zsc_txdmacmd[ch]); +} +#endif + +#ifndef ZS_TOLERANCE +#define ZS_TOLERANCE 51 +/* 5% in tenths of a %, plus 1 so that exactly 5% will be ok. */ +#endif + +/* + * check out a rate for acceptability from the internal clock + * source. Used in console config to validate a requested + * default speed. Placed here so that all the speed checking code is + * in one place. + * + * != 0 means ok. + */ +int +zs_cn_check_speed(bps) + int bps; /* target rate */ +{ + int tc, rate; + + tc = BPS_TO_TCONST(ZS_STD_BRG, bps); + if (tc < 0) + return 0; + rate = TCONST_TO_BPS(ZS_STD_BRG, tc); + if (ZS_TOLERANCE > abs(((rate - bps)*1000)/bps)) + return 1; + else + return 0; +} + +/* + * Search through the signal sources in the channel, and + * pick the best one for the baud rate requested. Return + * a -1 if not achievable in tolerance. Otherwise return 0 + * and fill in the values. + * + * This routine draws inspiration from the Atari port's zs.c + * driver in NetBSD 1.1 which did the same type of source switching. + * Tolerance code inspired by comspeed routine in isa/com.c. + * + * By Bill Studenmund, 1996-05-12 + */ +int +zs_set_speed(cs, bps) + struct zs_chanstate *cs; + int bps; /* bits per second */ +{ + struct xzs_chanstate *xcs = (void *) cs; + int i, tc, tc0 = 0, tc1, s, sf = 0; + int src, rate0, rate1, err, tol; + + if (bps == 0) + return (0); + + src = -1; /* no valid source yet */ + tol = ZS_TOLERANCE; + + /* + * Step through all the sources and see which one matches + * the best. A source has to match BETTER than tol to be chosen. + * Thus if two sources give the same error, the first one will be + * chosen. Also, allow for the possability that one source might run + * both the BRG and the direct divider (i.e. RTxC). + */ + for (i = 0; i < xcs->cs_clock_count; i++) { + if (xcs->cs_clocks[i].clk <= 0) + continue; /* skip non-existant or bad clocks */ + if (xcs->cs_clocks[i].flags & ZSC_BRG) { + /* check out BRG at /16 */ + tc1 = BPS_TO_TCONST(xcs->cs_clocks[i].clk >> 4, bps); + if (tc1 >= 0) { + rate1 = TCONST_TO_BPS(xcs->cs_clocks[i].clk >> 4, tc1); + err = abs(((rate1 - bps)*1000)/bps); + if (err < tol) { + tol = err; + src = i; + sf = xcs->cs_clocks[i].flags & ~ZSC_DIV; + tc0 = tc1; + rate0 = rate1; + } + } + } + if (xcs->cs_clocks[i].flags & ZSC_DIV) { + /* + * Check out either /1, /16, /32, or /64 + * Note: for /1, you'd better be using a synchronized + * clock! + */ + int b0 = xcs->cs_clocks[i].clk, e0 = abs(b0-bps); + int b1 = b0 >> 4, e1 = abs(b1-bps); + int b2 = b1 >> 1, e2 = abs(b2-bps); + int b3 = b2 >> 1, e3 = abs(b3-bps); + + if (e0 < e1 && e0 < e2 && e0 < e3) { + err = e0; + rate1 = b0; + tc1 = ZSWR4_CLK_X1; + } else if (e0 > e1 && e1 < e2 && e1 < e3) { + err = e1; + rate1 = b1; + tc1 = ZSWR4_CLK_X16; + } else if (e0 > e2 && e1 > e2 && e2 < e3) { + err = e2; + rate1 = b2; + tc1 = ZSWR4_CLK_X32; + } else { + err = e3; + rate1 = b3; + tc1 = ZSWR4_CLK_X64; + } + + err = (err * 1000)/bps; + if (err < tol) { + tol = err; + src = i; + sf = xcs->cs_clocks[i].flags & ~ZSC_BRG; + tc0 = tc1; + rate0 = rate1; + } + } + } +#ifdef ZSMACDEBUG + zsprintf("Checking for rate %d. Found source #%d.\n",bps, src); +#endif + if (src == -1) + return (EINVAL); /* no can do */ + + /* + * The M.I. layer likes to keep cs_brg_clk current, even though + * we are the only ones who should be touching the BRG's rate. + * + * Note: we are assuming that any ZSC_EXTERN signal source comes in + * on the RTxC pin. Correct for the mac68k obio zsc. + */ + if (sf & ZSC_EXTERN) + cs->cs_brg_clk = xcs->cs_clocks[i].clk >> 4; + else + cs->cs_brg_clk = ZS_STD_BRG; + + /* + * Now we have a source, so set it up. + */ + s = splzs(); + xcs->cs_psource = src; + xcs->cs_pclk_flag = sf; + bps = rate0; + if (sf & ZSC_BRG) { + cs->cs_preg[4] = ZSWR4_CLK_X16; + cs->cs_preg[11]= ZSWR11_RXCLK_BAUD | ZSWR11_TXCLK_BAUD; + if (sf & ZSC_PCLK) { + cs->cs_preg[14] = ZSWR14_BAUD_ENA | ZSWR14_BAUD_FROM_PCLK; + } else { + cs->cs_preg[14] = ZSWR14_BAUD_ENA; + } + tc = tc0; + } else { + cs->cs_preg[4] = tc0; + if (sf & ZSC_RTXDIV) { + cs->cs_preg[11] = ZSWR11_RXCLK_RTXC | ZSWR11_TXCLK_RTXC; + } else { + cs->cs_preg[11] = ZSWR11_RXCLK_TRXC | ZSWR11_TXCLK_TRXC; + } + cs->cs_preg[14]= 0; + tc = 0xffff; + } + /* Set the BAUD rate divisor. */ + cs->cs_preg[12] = tc; + cs->cs_preg[13] = tc >> 8; + splx(s); + +#ifdef ZSMACDEBUG + zsprintf("Rate is %7d, tc is %7d, source no. %2d, flags %4x\n", \ + bps, tc, src, sf); + zsprintf("Registers are: 4 %x, 11 %x, 14 %x\n\n", + cs->cs_preg[4], cs->cs_preg[11], cs->cs_preg[14]); +#endif + + cs->cs_preg[5] |= ZSWR5_RTS; /* Make sure the drivers are on! */ + + /* Caller will stuff the pending registers. */ + return (0); +} + +int +zs_set_modes(cs, cflag) + struct zs_chanstate *cs; + int cflag; /* bits per second */ +{ + struct xzs_chanstate *xcs = (void*)cs; + int s; + + /* + * Make sure we don't enable hfc on a signal line we're ignoring. + * As we enable CTS interrupts only if we have CRTSCTS or CDTRCTS, + * this code also effectivly turns off ZSWR15_CTS_IE. + * + * Also, disable DCD interrupts if we've been told to ignore + * the DCD pin. Happens on mac68k because the input line for + * DCD can also be used as a clock input. (Just set CLOCAL.) + * + * If someone tries to turn an invalid flow mode on, Just Say No + * (Suggested by gwr) + */ + if ((cflag & CDTRCTS) && (cflag & (CRTSCTS | MDMBUF))) + return (EINVAL); + if (xcs->cs_hwflags & ZS_HWFLAG_NO_DCD) { + if (cflag & MDMBUF) + return (EINVAL); + cflag |= CLOCAL; + } + if ((xcs->cs_hwflags & ZS_HWFLAG_NO_CTS) && (cflag & (CRTSCTS | CDTRCTS))) + return (EINVAL); + + /* + * Output hardware flow control on the chip is horrendous: + * if carrier detect drops, the receiver is disabled, and if + * CTS drops, the transmitter is stoped IN MID CHARACTER! + * Therefore, NEVER set the HFC bit, and instead use the + * status interrupt to detect CTS changes. + */ + s = splzs(); + if ((cflag & (CLOCAL | MDMBUF)) != 0) + cs->cs_rr0_dcd = 0; + else + cs->cs_rr0_dcd = ZSRR0_DCD; + /* + * The mac hardware only has one output, DTR (HSKo in Mac + * parlance). In HFC mode, we use it for the functions + * typically served by RTS and DTR on other ports, so we + * have to fake the upper layer out some. + * + * CRTSCTS we use CTS as an input which tells us when to shut up. + * We make no effort to shut up the other side of the connection. + * DTR is used to hang up the modem. + * + * In CDTRCTS, we use CTS to tell us to stop, but we use DTR to + * shut up the other side. + */ + if ((cflag & CRTSCTS) != 0) { + cs->cs_wr5_dtr = ZSWR5_DTR; + cs->cs_wr5_rts = 0; + cs->cs_rr0_cts = ZSRR0_CTS; + } else if ((cflag & CDTRCTS) != 0) { + cs->cs_wr5_dtr = 0; + cs->cs_wr5_rts = ZSWR5_DTR; + cs->cs_rr0_cts = ZSRR0_CTS; + } else if ((cflag & MDMBUF) != 0) { + cs->cs_wr5_dtr = 0; + cs->cs_wr5_rts = ZSWR5_DTR; + cs->cs_rr0_cts = ZSRR0_DCD; + } else { + cs->cs_wr5_dtr = ZSWR5_DTR; + cs->cs_wr5_rts = 0; + cs->cs_rr0_cts = 0; + } + splx(s); + + /* Caller will stuff the pending registers. */ + return (0); +} + + +/* + * Read or write the chip with suitable delays. + * MacII hardware has the delay built in. + * No need for extra delay. :-) However, some clock-chirped + * macs, or zsc's on serial add-on boards might need it. + */ +#define ZS_DELAY() + +u_char +zs_read_reg(cs, reg) + struct zs_chanstate *cs; + u_char reg; +{ + u_char val; + + out8(cs->cs_reg_csr, reg); + ZS_DELAY(); + val = in8(cs->cs_reg_csr); + ZS_DELAY(); + return val; +} + +void +zs_write_reg(cs, reg, val) + struct zs_chanstate *cs; + u_char reg, val; +{ + out8(cs->cs_reg_csr, reg); + ZS_DELAY(); + out8(cs->cs_reg_csr, val); + ZS_DELAY(); +} + +u_char zs_read_csr(cs) + struct zs_chanstate *cs; +{ + register u_char val; + + val = in8(cs->cs_reg_csr); + ZS_DELAY(); + /* make up for the fact CTS is wired backwards */ + val ^= ZSRR0_CTS; + return val; +} + +void zs_write_csr(cs, val) + struct zs_chanstate *cs; + u_char val; +{ + /* Note, the csr does not write CTS... */ + out8(cs->cs_reg_csr, val); + ZS_DELAY(); +} + +u_char zs_read_data(cs) + struct zs_chanstate *cs; +{ + register u_char val; + + val = in8(cs->cs_reg_data); + ZS_DELAY(); + return val; +} + +void zs_write_data(cs, val) + struct zs_chanstate *cs; + u_char val; +{ + out8(cs->cs_reg_data, val); + ZS_DELAY(); +} + +/**************************************************************** + * Console support functions (powermac specific!) + * Note: this code is allowed to know about the layout of + * the chip registers, and uses that to keep things simple. + * XXX - I think I like the mvme167 code better. -gwr + * XXX - Well :-P :-) -wrs + ****************************************************************/ + +#define zscnpollc nullcnpollc +cons_decl(zs); + +static void zs_putc __P((register volatile struct zschan *, int)); +static int zs_getc __P((register volatile struct zschan *)); +extern int zsopen __P(( dev_t dev, int flags, int mode, struct proc *p)); + +/* + * Console functions. + */ + +/* + * zscnprobe is the routine which gets called as the kernel is trying to + * figure out where the console should be. Each io driver which might + * be the console (as defined in mac68k/conf.c) gets probed. The probe + * fills in the consdev structure. Important parts are the device #, + * and the console priority. Values are CN_DEAD (don't touch me), + * CN_NORMAL (I'm here, but elsewhere might be better), CN_INTERNAL + * (the video, better than CN_NORMAL), and CN_REMOTE (pick me!) + * + * As the mac's a bit different, we do extra work here. We mainly check + * to see if we have serial echo going on. Also chould check for default + * speeds. + */ + +/* + * Polled input char. + */ +int +zs_getc(zc) + register volatile struct zschan *zc; +{ + register int s, c, rr0; + + s = splhigh(); + /* Wait for a character to arrive. */ + do { + rr0 = in8(&zc->zc_csr); + ZS_DELAY(); + } while ((rr0 & ZSRR0_RX_READY) == 0); + + c = in8(&zc->zc_data); + ZS_DELAY(); + splx(s); + + /* + * This is used by the kd driver to read scan codes, + * so don't translate '\r' ==> '\n' here... + */ + return (c); +} + +/* + * Polled output char. + */ +void +zs_putc(zc, c) + register volatile struct zschan *zc; + int c; +{ + register int s, rr0; + register long wait = 0; + + s = splhigh(); + /* Wait for transmitter to become ready. */ + do { + rr0 = in8(&zc->zc_csr); + ZS_DELAY(); + } while (((rr0 & ZSRR0_TX_READY) == 0) && (wait++ < 1000000)); + + if ((rr0 & ZSRR0_TX_READY) != 0) { + out8(&zc->zc_data, c); + ZS_DELAY(); + } + splx(s); +} + + +/* + * Polled console input putchar. + */ +int +zscngetc(dev) + dev_t dev; +{ + register volatile struct zschan *zc = zs_conschan; + register int c; + + c = zs_getc(zc); + return (c); +} + +/* + * Polled console output putchar. + */ +void +zscnputc(dev, c) + dev_t dev; + int c; +{ + register volatile struct zschan *zc = zs_conschan; + + zs_putc(zc, c); +} + +/* + * Handle user request to enter kernel debugger. + */ +void +zs_abort(cs) + struct zs_chanstate *cs; +{ + volatile struct zschan *zc = zs_conschan; + int rr0; + register long wait = 0; + + if (zs_cons_canabort == 0) + return; + + /* Wait for end of break to avoid PROM abort. */ + do { + rr0 = in8(&zc->zc_csr); + ZS_DELAY(); + } while ((rr0 & ZSRR0_BREAK) && (wait++ < ZSABORT_DELAY)); + + if (wait > ZSABORT_DELAY) { + zs_cons_canabort = 0; + /* If we time out, turn off the abort ability! */ + } + +#ifdef DDB + Debugger(); +#endif +} + +static int ofccngetc __P((dev_t)); +static void ofccnputc __P((dev_t, int)); + +struct consdev consdev_zs = { + zscnprobe, + zscninit, + ofccngetc, + ofccnputc, + zscnpollc, +}; + +struct consdev *cn_tab = &consdev_zs; + +void +zscnprobe(struct consdev * cp) +{ + int l; + char type[32]; + extern int console_node; + + if (console_node == -1) + return; + + l = OF_getprop(console_node, "device_type", type, sizeof(type)); + if (l == -1 || l >= sizeof(type) - 1) + return; + + if (strcmp(type, "serial") == 0) + cp->cn_pri = CN_REMOTE; +} + + +static int stdin, stdout; + +void +zscninit(cd) + struct consdev *cd; +{ + int chosen; + int sz; + int unit = 0; + char name[32]; + + chosen = OF_finddevice("/chosen"); + if (chosen == -1) + return; + + sz = OF_getprop(chosen, "stdin", &stdin, sizeof(stdin)); + if (sz != sizeof(stdin)) + return; + + sz = OF_getprop(chosen, "stdout", &stdout, sizeof(stdout)); + if (sz != sizeof(stdout)) + return; + + bzero(name, sizeof(name)); + OF_getprop(stdout, "name", name, sizeof(name)); + + if (strcmp(name, "ch-b") == 0) + unit = 1; + + zs_hwflags[0][unit] = ZS_HWFLAG_CONSOLE; + + cd->cn_dev = makedev(zs_major, unit); +} + +static int +ofccngetc(dev) + dev_t dev; +{ + u_char ch; + int sz; + + sz = OF_read(stdin, &ch, 1); + if (sz <= 0) + return -1; + + return ch; +} + +static void +ofccnputc(dev, c) + dev_t dev; + int c; +{ + u_char ch = c; + + OF_write(stdout, &ch, 1); +} |
