/*-
* Copyright (c) 1988 Regents of the University of California.
* 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. 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.
*
* @(#)qd.c 7.1 (Berkeley) 6/28/91
*/
/************************************************************************
* *
* Copyright (c) 1985-1988 by *
* Digital Equipment Corporation, Maynard, MA *
* All rights reserved. *
* *
* This software is furnished under a license and may be used and *
* copied only in accordance with the terms of such license and *
* with the inclusion of the above copyright notice. This *
* software or any other copies thereof may not be provided or *
* otherwise made available to any other person. No title to and *
* ownership of the software is hereby transferred. *
* *
* The information in this software is subject to change without *
* notice and should not be construed as a commitment by Digital *
* Equipment Corporation. *
* *
* Digital assumes no responsibility for the use or reliability *
* of its software on equipment which is not supplied by Digital. *
* *
*************************************************************************/
/*
* QDSS driver status flags for tracking operational state
*/
struct qdflags {
u_int inuse; /* which minor dev's are in use now */
u_int config; /* I/O page register content */
u_int mapped; /* user mapping status word */
u_int kernel_loop; /* if kernel console is redirected */
u_int user_dma; /* DMA from user space in progress */
u_short pntr_id; /* type code of pointing device */
u_short duart_imask; /* shadowing for duart intrpt mask reg */
u_short adder_ie; /* shadowing for adder intrpt enbl reg */
u_short curs_acc; /* cursor acceleration factor */
u_short curs_thr; /* cursor acceleration threshold level */
u_short tab_res; /* tablet resolution factor */
u_short selmask; /* mask for active qd select entries */
};
/*
* Softc struct to keep track of all states in this driver.
*/
struct qd_softc {
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
bus_dma_tag_t sc_dmat;
};
/*
* bit definitions for 'inuse' entry
*/
#define CONS_DEV 0x01
#define GRAPHIC_DEV 0x04
/*
* bit definitions for 'mapped' member of flag structure
*/
#define MAPDEV 0x01 /* hardware is mapped */
#define MAPDMA 0x02 /* DMA buffer mapped */
#define MAPEQ 0x04 /* event queue buffer mapped */
#define MAPSCR 0x08 /* scroll param area mapped */
#define MAPCOLOR 0x10 /* color map writing buffer mapped */
/*
* reference to an array of "uba_device" structures built by the auto
* configuration program. The uba_device structure describes the device
* sufficiently for the driver to talk to it. The auto configuration code
* fills in the uba_device structures (located in ioconf.c) from user
* maintained info.
*/
struct uba_device *qdinfo[NQD]; /* array of pntrs to each QDSS's */
struct tty *qd_tty[NQD*4]; /* teletype structures for each.. */
volatile char *qvmem[NQD];
volatile struct pte *QVmap[NQD];
#define CHUNK (64 * 1024)
#define QMEMSIZE (1024 * 1024 * 4) /* 4 meg */
/*
* static storage used by multiple functions in this code
*/
int Qbus_unmap[NQD]; /* Qbus mapper release code */
struct qdmap qdmap[NQD]; /* QDSS register map structure */
struct qdflags qdflags[NQD]; /* QDSS register map structure */
void *qdbase[NQD]; /* base address of each QDSS unit */
short qdopened[NQD]; /* graphics device is open exclusive use */
/*
* the array "event_shared[]" is made up of a number of event queue buffers
* equal to the number of QDSS's configured into the running kernel (NQD).
* Each event queue buffer begins with an event queue header (struct qdinput)
* followed by a group of event queue entries (struct _vs_event). The array
* "*eq_header[]" is an array of pointers to the start of each event queue
* buffer in "event_shared[]".
*/
#define EQSIZE ((EVENT_BUFSIZE * NQD) + 512)
char event_shared[EQSIZE]; /* reserve space for event bufs */
struct qdinput *eq_header[NQD]; /* event queue header pntrs */
/*
* This allocation method reserves enough memory pages for NQD shared DMA I/O
* buffers. Each buffer must consume an integral number of memory pages to
* guarantee that a following buffer will begin on a page boundary. Also,
* enough space is allocated so that the FIRST I/O buffer can start at the
* 1st page boundary after "&DMA_shared". Page boundaries are used so that
* memory protections can be turned on/off for individual buffers.
*/
#define IOBUFSIZE ((DMA_BUFSIZ * NQD) + 512)
/*
* The driver assists a client in scroll operations by loading dragon
* registers from an interrupt service routine. The loading is done using
* parameters found in memory shared between the driver and its client.
* The scroll parameter structures are ALL located in the same memory page
* for reasons of memory economy.
*/
char scroll_shared[2 * 512]; /* reserve space for scroll structs */
struct scroll *scroll[NQD]; /* pointers to scroll structures */
/*
* the driver is programmable to provide the user with color map write
* services at VSYNC interrupt time. At interrupt time the driver loads
* the color map with any user-requested load data found in shared memory
*/
#define COLOR_SHARED ((COLOR_BUFSIZ * NQD) + 512)
char color_shared[COLOR_SHARED]; /* reserve space: color bufs */
struct color_buf *color_buf[NQD]; /* pointers to color bufs */
struct selinfo qdrsel[NQD]; /* process waiting for select */
struct _vs_cursor cursor[NQD]; /* console cursor */
int qdcount = 0; /* count of successfully probed qd's */
int nNQD = NQD;
int DMAbuf_size = DMA_BUFSIZ;
int QDlast_DMAtype; /* type of the last DMA operation */
/*
* macro to get system time. Used to time stamp event queue entries
*/
#define TOY ((time.tv_sec * 100) + (time.tv_usec / 10000))
#define QDPRIOR (PZERO-1) /* must be negative */
#define FALSE 0
#ifdef TRUE
#undef TRUE
#endif
#define TRUE ~FALSE
#define BAD -1
#define GOOD 0
/*
* macro to create a system virtual page number from system virtual adrs
*/
#define VTOP(x) (((int)x & ~0xC0000000) >> VAX_PGSHIFT)
/*
* QDSS register address offsets from start of QDSS address space
*/
#define QDSIZE (52 * 1024) /* size of entire QDSS foot print */
#define TMPSIZE (16 * 1024) /* template RAM is 8k SHORT WORDS */
#define TMPSTART 0x8000 /* offset of template RAM from base adrs */
#define REGSIZE (5 * 512) /* regs touch 2.5k (5 pages) of addr space */
#define REGSTART 0xC000 /* offset of reg pages from base adrs */
#define ADDER (REGSTART+0x000)
#define DGA (REGSTART+0x200)
#define DUART (REGSTART+0x400)
#define MEMCSR (REGSTART+0x800)
#define CLRSIZE (3 * 512) /* color map size */
#define CLRSTART (REGSTART+0xA00) /* color map start offset from base */
/* 0x0C00 really */
#define RED (CLRSTART+0x000)
#define BLUE (CLRSTART+0x200)
#define GREEN (CLRSTART+0x400)
/*
* QDSS minor device numbers. The *real* minor device numbers are in
* the bottom two bits of the major/minor device spec. Bits 2 and up are
* used to specify the QDSS device number (ie: which one?)
*/
/*
* LK-201 state storage for input console keyboard conversion to ASCII
*/
struct q_keyboard {
int shift; /* state variables */
int cntrl;
int lock;
int lastcode; /* last keycode typed */
unsigned kup[8]; /* bits for each keycode*/
unsigned dkeys[8]; /* down/up mode keys */
char last; /* last character */
} q_keyboard;
/*
* tty settings on first open
*/
#define IFLAG (BRKINT|ISTRIP|IXON|IXANY|ICRNL|IMAXBEL)
#define OFLAG (OPOST|OXTABS|ONLCR)
#define LFLAG (ISIG|ICANON|ECHO|IEXTEN)
#define CFLAG (PARENB|CREAD|CS7|CLOCAL)
/*
* Kernel virtual addresses where we can map in the QBUS io page and the
* QDSS memory during qdcninit. pmap_bootstrap fills this in.
*/
void *qd_ubaio;
/* This is the QDSS unit 0 CSR. It is hard-coded in here so that the
* QDSS can be used as the console. The console routines don't get
* any config info. The ROM also autodetects at this address, so
* the console QDSS should be at this address. Furthermore, nothing
* else shuld be at this address instead because that would confuse the
* ROM and this driver.
*/
#define QDSSCSR 0x1F00
volatile u_short *qdaddr; /* Virtual address for QDSS CSR */
/*
* This flag is set to 1 if the console initialization (qdcninit)
* has been performed on qd0. That initialization is required and must
* be done before the device probe routine.
*/
int qd0cninited = 0, qd0iscons = 0;
/*
* Do early check if the qdss is console. If not; don't allocate
* any memory for it in bootstrap.
*/
void
qdearly(void)
{
extern vaddr_t virtual_avail;
int tmp;
/* Make sure we're running on a system that can have a QDSS */
if (vax_boardtype == VAX_BTYP_630) {
/* Now check some undocumented flag */
if ((*(int *)(0x200B801E) & 0x60) == 0)
/* The KA630 isn't using a QDSS as the console,
* so we won't either */
return;
} else if (vax_boardtype != VAX_BTYP_650)
return;
/* How to check for console on KA650? We assume that if there is a
* QDSS, it is console.
*/
#define QIOPAGE 0x20000000 /* XXX */
#define UBAIOPAGES 16
tmp = QIOPAGE + ubdevreg(QDSSCSR);
if (badaddr((void *)tmp, sizeof(short)))
return;
/*
* Configure QDSS into Q memory and make it intrpt.
*
* side effects: QDSS gets mapped into Qbus memory space at the first
* vacant 64kb boundary counting back from the top of
* Qbus memory space (qvmem+4mb)
*
* return: QDSS bus request level and vector address returned in
* registers by UNIX convention.
*
*/
static int
qd_match(device_t parent, cfdata_t match, void *aux)
{
struct qd_softc ssc;
struct qd_softc *sc = &ssc;
struct uba_attach_args *ua = aux;
struct uba_softc *uh = device_private(parent);
int unit;
volatile struct dga *dga; /* pointer to gate array structure */
int vector;
#ifdef notdef
int *ptep; /* page table entry pointer */
void *phys_adr; /* physical QDSS base adrs */
u_int mapix;
#endif
/* Create a "fake" softc with only a few fields used. */
sc->sc_iot = ua->ua_iot;
sc->sc_ioh = ua->ua_ioh;
sc->sc_dmat = ua->ua_dmat;
/*
* calculate board unit number from I/O page register address
*/
unit = (int) (((int)sc->sc_ioh >> 1) & 0x0007);
/*
* QDSS regs must be mapped to Qbus memory space at a 64kb
* physical boundary. The Qbus memory space is mapped into
* the system memory space at config time. After config
* runs, "qvmem[0]" (ubavar.h) holds the system virtual adrs
* of the start of Qbus memory. The Qbus memory page table
* is found via an array of pte ptrs called "QVmap[]" (ubavar.h)
* which is also loaded at config time. These are the
* variables used below to find a vacant 64kb boundary in
* Qbus memory, and load its corresponding physical adrs
* into the QDSS's I/O page CSR.
*/
/*
* Only if QD is the graphics device.
*/
/* if this QDSS is NOT the console, then do init here.. */
if (unit != 0) {
printf("qd: can't support two qdss's (yet)\n");
#ifdef notdef /* can't test */
if (v_consputc != qdputc || unit != 0) {
*(short *)qdmap[unit].memcsr |= SYNC_ON;
}
#endif /*notdef*/
} else {
/* We are dealing with qd0 */
if (!qd0cninited) {
/*
* qd0 has not been initialized as the console.
* We need to do some initialization now
*
* XXX
* However, if the QDSS is not the console then
* that stupid undocumented bit (see qdcnprobe)
* is cleared. Then the QDSS refuses to work.
* (What did the ROM do to it!?)
* XXX
*/
return 0;
#if 0
qdaddr = (void *)reg;
/* Lame probe for QDSS. Should be ok for qd0 */
if (badaddr((void *)qdaddr, sizeof(short)))
return 0;
qdcninit(NULL);
#endif
}
}
/*
* The QDSS interrupts at HEX vectors xx0 (DMA) xx4
* (ADDER) and xx8 (DUART). Therefore, we take three
* vectors from the vector pool, and then continue
* to take them until we get a xx0 HEX vector. The
* pool provides vectors in contiguous descending
* order.
*/
vector = (uh->uh_lastiv -= 4*3); /* take three vectors */
while (vector & 0x0F) { /* if lo nibble != 0.. */
/* ..take another vector */
vector = (uh->uh_lastiv -= 4);
}
/*
* init structures used in kbd/mouse interrupt service. This code must
* come after the "init_shared()" routine has run since that routine
* inits the eq_header[unit] structure used here.
*/
case QD_GETEVENT:
/*
* extract the oldest event from the event queue
*/
if (ISEMPTY(eq_header[unit])) {
event = (struct _vs_event *) datap;
event->vse_device = VSE_NULL;
break;
}
event = (struct _vs_event *) GETBEGIN(eq_header[unit]);
s = spl5();
GETEND(eq_header[unit]);
splx(s);
memcpy(datap, (void *)event, sizeof(struct _vs_event));
break;
case QD_RESET:
/*
* init the dragon stuff, DUART, and driver variables
*/
init_shared(unit); /* init shared memory */
setup_dragon(unit); /* init the ADDER/VIPER stuff */
clear_qd_screen(unit);
ldcursor(unit, cons_cursor); /* load default cursor map */
ldfont(unit); /* load the console font */
setup_input(unit); /* init the DUART */
break;
case QD_SET:
/*
* init the DUART and driver variables
*/
init_shared(unit);
setup_input(unit);
break;
case QD_CLRSCRN:
/*
* clear the QDSS screen. (NOTE that this reinits the dragon)
*/
#ifdef notdef /* has caused problems and isn't necessary */
setup_dragon(unit);
clear_qd_screen(unit);
#endif
break;
case QD_WTCURSOR:
/*
* load a cursor into template RAM
*/
ldcursor(unit, (short *)datap);
break;
case QD_RDCURSOR:
temp = (short *) qdmap[unit].template;
/*
* cursor is 32 WORDS from the end of the 8k WORD...
* ...template space
*/
temp += (8 * 1024) - 32;
for (i = 0; i < 32; ++i, datap += sizeof(short))
*(short *)datap = *temp++;
break;
case QD_POSCURSOR:
/*
* position the mouse cursor
*/
dga = (struct dga *) qdmap[unit].dga;
pos = (struct _vs_cursor *) datap;
s = spl5();
dga->x_cursor = TRANX(pos->x);
dga->y_cursor = TRANY(pos->y);
eq_header[unit]->curs_pos.x = pos->x;
eq_header[unit]->curs_pos.y = pos->y;
splx(s);
break;
case QD_PRGCURSOR:
/*
* set the cursor acceleration factor
*/
curs = (struct prg_cursor *) datap;
s = spl5();
qdflags[unit].curs_acc = curs->acc_factor;
qdflags[unit].curs_thr = curs->threshold;
splx(s);
break;
case QD_MAPDEVICE:
/*
* enable 'user write' to device pages
*/
qdflags[unit].mapped |= MAPDEV;
qd = (struct qdmap *) &qdmap[unit];
/*
* enable user write to template RAM
*/
mapix = VTOP((int)qd->template) - VTOP(qvmem[0]);
ptep = (int *)(QVmap[0] + mapix);
for (i = 0; i < vax_btop(TMPSIZE); i++, ptep++)
*ptep = (*ptep & ~PG_PROT) | PG_RW | PG_V;
/*
* enable user write to registers
*/
mapix = VTOP((int)qd->adder) - VTOP(qvmem[0]);
ptep = (int *)(QVmap[0] + mapix);
for (i = 0; i < vax_btop(REGSIZE); i++, ptep++)
*ptep = (*ptep & ~PG_PROT) | PG_RW | PG_V;
/*
* enable user write to color maps
*/
mapix = VTOP((int)qd->red) - VTOP(qvmem[0]);
ptep = (int *)(QVmap[0] + mapix);
for (i = 0; i < vax_btop(CLRSIZE); i++, ptep++)
*ptep = (*ptep & ~PG_PROT) | PG_RW | PG_V;
#ifdef notyet
/*
* Ragge 999620:
* Can't map in the graphic buffer into user space for now.
* The best way to fix this is to convert this driver to wscons.
*/
case QD_MAPIOBUF:
/*
* do setup for DMA by user process
*
* set 'user write enable' bits for DMA buffer
*/
qdflags[unit].mapped |= MAPDMA;
ptep = (int *) ((VTOP(DMAheader[unit]) * 4)
+ (mfpr(PR_SBR) | 0x80000000));
for (i = 0; i < vax_btop(DMAbuf_size); i++, ptep++)
*ptep = (*ptep & ~PG_PROT) | PG_RW | PG_V;
mtpr(0, PR_TBIA); /* invalidate translation buffer */
/*
* set up QBUS map registers for DMA
*/
DMAheader[unit]->QBAreg =
uballoc(uh, (void *)DMAheader[unit], DMAbuf_size, 0);
if (DMAheader[unit]->QBAreg == 0)
printf("qd%d: qdioctl: QBA setup error\n", unit);
Qbus_unmap[unit] = DMAheader[unit]->QBAreg;
DMAheader[unit]->QBAreg &= 0x3FFFF;
/*
* return I/O buf adr
*/
*(int *)datap = (int) DMAheader[unit];
break;
#endif
case QD_MAPSCROLL:
/*
* map the shared scroll param area and enable scroll interpts
*/
qdflags[unit].mapped |= MAPSCR;
ptep = (int *) ((VTOP(scroll[unit]) * 4)
+ (mfpr(PR_SBR) | 0x80000000));
/*
* allow user write to scroll area
*/
*ptep = (*ptep & ~PG_PROT) | PG_RW | PG_V;
mtpr(0, PR_TBIA); /* invalidate translation buf */
scroll[unit]->status = 0;
adder = (struct adder *) qdmap[unit].adder;
qdflags[unit].adder_ie |= FRAME_SYNC;
adder->interrupt_enable = qdflags[unit].adder_ie;
*(int *)datap = (int) scroll[unit]; /* return scroll area */
break;
case QD_MAPEVENT:
/*
* give user write access to the event queue
*/
qdflags[unit].mapped |= MAPEQ;
ptep = (int *) ((VTOP(eq_header[unit]) * 4)
+ (mfpr(PR_SBR) | 0x80000000));
/*
* allow user write to 1K event queue
*/
*ptep = (*ptep & ~PG_PROT) | PG_RW | PG_V; ptep++;
*ptep = (*ptep & ~PG_PROT) | PG_RW | PG_V;
mtpr(0, PR_TBIA); /* clr CPU translation buf */
/*
* return event queue address
*/
*(int *)datap = (int)eq_header[unit];
break;
case QD_PRGKBD:
/*
* pass caller's programming commands to LK201
*/
duart = (struct duart *)qdmap[unit].duart;
cmdbuf = (struct prgkbd *)datap; /* pnt to kbd cmd buf */
/*
* send command
*/
for (i = 1000; i > 0; --i) {
if (duart->statusA&XMT_RDY) {
duart->dataA = cmdbuf->cmd;
break;
}
}
if (i == 0) {
printf("qd%d: qdioctl: timeout on XMT_RDY [1]\n", unit);
break;
}
/*
* send param1?
*/
if (cmdbuf->cmd & LAST_PARAM)
break;
for (i = 1000; i > 0; --i) {
if (duart->statusA&XMT_RDY) {
duart->dataA = cmdbuf->param1;
break;
}
}
if (i == 0) {
printf("qd%d: qdioctl: timeout on XMT_RDY [2]\n", unit);
break;
}
/*
* send param2?
*/
if (cmdbuf->param1 & LAST_PARAM)
break;
for (i = 1000; i > 0; --i) {
if (duart->statusA&XMT_RDY) {
duart->dataA = cmdbuf->param2;
break;
}
}
if (i == 0) {
printf("qd%d: qdioctl: timeout on XMT_RDY [3]\n", unit);
break;
}
break;
case QD_PRGMOUSE:
/*
* pass caller's programming commands to the mouse
*/
duart = (struct duart *) qdmap[unit].duart;
for (i = 1000; i > 0; --i) {
if (duart->statusB&XMT_RDY) {
duart->dataB = *datap;
break;
}
}
if (i == 0) {
printf("qd%d: qdioctl: timeout on XMT_RDY [4]\n", unit);
}
break;
case QD_RDCONFIG:
/*
* get QDSS configuration word and return it
*/
*(short *)datap = qdflags[unit].config;
break;
case QD_KERN_LOOP:
case QD_KERN_UNLOOP:
/*
* vestige from ultrix. BSD uses TIOCCONS to redirect
* kernel console output.
*/
break;
case QD_PRGTABLET:
/*
* program the tablet
*/
duart = (struct duart *) qdmap[unit].duart;
for (i = 1000; i > 0; --i) {
if (duart->statusB&XMT_RDY) {
duart->dataB = *datap;
break;
}
}
if (i == 0) {
printf("qd%d: qdioctl: timeout on XMT_RDY [5]\n", unit);
}
break;
case QD_PRGTABRES:
/*
* program the tablet report resolution factor
*/
qdflags[unit].tab_res = *(short *)datap;
break;
default:
/*
* service tty ioctl's
*/
if (!(minor_dev & 0x02)) {
tp = qd_tty[minor_dev];
error =
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &qdread_filtops;
break;
case EVFILT_WRITE:
kn->kn_fop = &qdwrite_filtops;
break;
default:
return (EINVAL);
}
kn->kn_hook = (void *)(intptr_t) dev;
s = spl5();
selrecord_knote(&qdrsel[unit], kn);
splx(s);
return (0);
}
void qd_strategy(struct buf *bp);
/*ARGSUSED*/
int
qdwrite(dev_t dev, struct uio *uio, int flag)
{
struct tty *tp;
int minor_dev;
int unit;
minor_dev = minor(dev);
unit = (minor_dev >> 2) & 0x07;
if (((minor_dev&0x03) != 0x02) && (qdflags[unit].inuse&CONS_DEV)) {
/*
* this is the console...
*/
tp = qd_tty[minor_dev];
return ((*tp->t_linesw->l_write)(tp, uio, flag));
} else if (qdflags[unit].inuse & GRAPHIC_DEV) {
/*
* this is a DMA xfer from user space
*/
return (physio(qd_strategy, NULL, dev, B_WRITE, minphys, uio));
}
return (ENXIO);
}
/*ARGSUSED*/
int
qdread(dev_t dev, struct uio *uio, int flag)
{
struct tty *tp;
int minor_dev;
int unit;
minor_dev = minor(dev);
unit = (minor_dev >> 2) & 0x07;
if ((minor_dev & 0x03) != 0x02 && qdflags[unit].inuse & CONS_DEV) {
/*
* this is the console
*/
tp = qd_tty[minor_dev];
return ((*tp->t_linesw->l_read)(tp, uio, flag));
} else if (qdflags[unit].inuse & GRAPHIC_DEV) {
/*
* this is a bitmap-to-processor xfer
*/
return (physio(qd_strategy, NULL, dev, B_READ, minphys, uio));
}
return (ENXIO);
}
/***************************************************************
*
* qd_strategy()... strategy routine to do DMA
*
***************************************************************/
void
qd_strategy(struct buf *bp)
{
volatile struct dga *dga;
volatile struct adder *adder;
int unit;
int QBAreg;
int s;
int cookie;
struct uba_softc *uh;
/*
* Start output to the console screen
*/
void qdstart(tp)
struct tty *tp;
{
int which_unit, unit, c;
int s;
unit = minor(tp->t_dev);
which_unit = (unit >> 2) & 0x3;
unit &= 0x03;
s = spl5();
/*
* If it's currently active, or delaying, no need to do anything.
*/
if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))
goto out;
/*
* Display chars until the queue is empty.
* Drop input from anything but the console
* device on the floor.
*
* XXX - this loop is done at spltty.
*
*/
while (tp->t_outq.c_cc) {
c = getc(&tp->t_outq);
if (unit == 0)
blitc(which_unit, (u_char)c);
}
ttypull(tp);
tp->t_state &= ~TS_BUSY;
out:
splx(s);
} /* qdstart */
/*ARGSUSED*/
void
qdstop(struct tty *tp, int flag)
{
int s;
s = spl5(); /* block intrpts during state modification */
if (tp->t_state & TS_BUSY) {
if ((tp->t_state & TS_TTSTOP) == 0)
tp->t_state |= TS_FLUSH;
else
tp->t_state &= ~TS_BUSY;
}
splx(s);
}
/*
* Output a character to the QDSS screen
*/
void
blitc(int unit, u_char chr)
{
volatile struct adder *adder;
volatile struct dga *dga;
int i;
int nograph = !(qdflags[unit].inuse&GRAPHIC_DEV);
static short inescape[NQD];
adder = (struct adder *)qdmap[unit].adder;
dga = (struct dga *) qdmap[unit].dga;
/*
* BSD comment: this (&=0177) defeats the extended character
* set code for the glass tty, but if i had the time i would
* spend it ripping out the code completely. This driver
* is too big for its own good.
*/
chr &= 0177;
/*
* Cursor addressing (so vi will work).
* Decode for "\E=%.%." cursor motion description.
* Corresponds to type "qdcons" in /etc/termcap:
*
* qd|qdss|qdcons|qdss glass tty (4.4 BSD):\
* :am:do=^J:le=^H:bs:cm=\E=%.%.:cl=1^Z:co#128:li#57::nd=^L:up=^K:
*
*/
if (inescape[unit] && nograph) {
switch (inescape[unit]++) {
case 1:
if (chr != '=') {
/* abort escape sequence */
inescape[unit] = 0;
blitc(unit, chr);
}
return;
case 2:
/* position row */
cursor[unit].y = CHAR_HEIGHT * chr;
if (cursor[unit].y > 863 - CHAR_HEIGHT)
cursor[unit].y = 863 - CHAR_HEIGHT;
dga->y_cursor = TRANY(cursor[unit].y);
return;
case 3:
/* position column */
cursor[unit].x = CHAR_WIDTH * chr;
if (cursor[unit].x > 1024 - CHAR_WIDTH)
cursor[unit].x = 1023 - CHAR_WIDTH;
dga->x_cursor = TRANX(cursor[unit].x);
inescape[unit] = 0;
return;
default:
inescape[unit] = 0;
blitc(unit, chr);
}
}
switch (chr) {
case '\r': /* return char */
cursor[unit].x = 0;
if (nograph)
dga->x_cursor = TRANX(cursor[unit].x);
return;
case '\t': /* tab char */
for (i = 8 - ((cursor[unit].x >> 3) & 0x07); i > 0; --i) {
blitc(unit, ' ');
}
return;
case '\n': /* line feed char */
if ((cursor[unit].y += CHAR_HEIGHT) > (863 - CHAR_HEIGHT)) {
if (nograph) {
cursor[unit].y -= CHAR_HEIGHT;
scroll_up(adder);
} else
cursor[unit].y = 0;
}
if (nograph)
dga->y_cursor = TRANY(cursor[unit].y);
return;
case '\b': /* backspace char */
if (cursor[unit].x > 0) {
cursor[unit].x -= CHAR_WIDTH;
if (nograph)
dga->x_cursor = TRANX(cursor[unit].x);
}
return;
case CTRL('k'): /* cursor up */
if (nograph && cursor[unit].y > 0) {
cursor[unit].y -= CHAR_HEIGHT;
dga->y_cursor = TRANY(cursor[unit].y);
}
return;
case CTRL('^'): /* home cursor */
if (nograph) {
cursor[unit].x = 0;
dga->x_cursor = TRANX(cursor[unit].x);
cursor[unit].y = 0;
dga->y_cursor = TRANY(cursor[unit].y);
}
return;
case CTRL('l'): /* cursor right */
if (nograph && cursor[unit].x < 1023 - CHAR_WIDTH) {
cursor[unit].x += CHAR_WIDTH;
dga->x_cursor = TRANX(cursor[unit].x);
}
return;
case CTRL('z'): /* clear screen */
if (nograph) {
setup_dragon(unit);
clear_qd_screen(unit);
/* home cursor - termcap seems to assume this */
cursor[unit].x = 0;
dga->x_cursor = TRANX(cursor[unit].x);
cursor[unit].y = 0;
dga->y_cursor = TRANY(cursor[unit].y);
}
return;
case '\033': /* start escape sequence */
if (nograph)
inescape[unit] = 1;
return;
default:
if ((chr < ' ') || (chr > '~'))
return;
}
/*
* setup VIPER operand control registers
*/
write_ID(adder, CS_UPDATE_MASK, 0x0001); /* select plane #0 */
write_ID(adder, SRC1_OCR_B,
EXT_NONE | INT_SOURCE | ID | BAR_SHIFT_DELAY);
write_ID(adder, CS_UPDATE_MASK, 0x00FE); /* select other planes */
write_ID(adder, SRC1_OCR_B,
EXT_SOURCE | INT_NONE | NO_ID | BAR_SHIFT_DELAY);
write_ID(adder, CS_UPDATE_MASK, 0x00FF); /* select all planes */
write_ID(adder, DST_OCR_B,
EXT_NONE | INT_NONE | NO_ID | NO_BAR_SHIFT_DELAY);
write_ID(adder, MASK_1, 0xFFFF);
write_ID(adder, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 1);
write_ID(adder, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);
adder->x_clip_min = 0;
adder->x_clip_max = 1024;
adder->y_clip_min = 0;
adder->y_clip_max = 864;
/*
* load DESTINATION origin and vectors
*/
adder->fast_dest_dy = 0;
adder->slow_dest_dx = 0;
adder->error_1 = 0;
adder->error_2 = 0;
adder->rasterop_mode = DST_WRITE_ENABLE | NORMAL;
(void)wait_status(adder, RASTEROP_COMPLETE);
adder->destination_x = cursor[unit].x;
adder->fast_dest_dx = CHAR_WIDTH;
adder->destination_y = cursor[unit].y;
adder->slow_dest_dy = CHAR_HEIGHT;
/*
* load SOURCE origin and vectors
*/
if ((chr - ' ') > (CHARS - 1)) {
printf("Invalid character (x)%x in blitc\n",chr);
chr = ' ';
}
/*
* X position is modulo the number of characters per line
*/
adder->source_1_x = FONT_X +
(((chr - ' ') % (MAX_SCREEN_X/CHAR_WIDTH)) * CHAR_WIDTH);
/*
* Point to either first or second row
*/
adder->source_1_y = 2048 - 15 *
(((chr - ' ')/(MAX_SCREEN_X/CHAR_WIDTH)) + 1);
adder->source_1_dx = CHAR_WIDTH;
adder->source_1_dy = CHAR_HEIGHT;
write_ID(adder, LU_FUNCTION_R1, FULL_SRC_RESOLUTION | LF_SOURCE);
adder->cmd = RASTEROP | OCRB | 0 | S1E | DTE;
/*
* update console cursor coordinates
*/
cursor[unit].x += CHAR_WIDTH;
if (nograph)
dga->x_cursor = TRANX(cursor[unit].x);
if (cursor[unit].x > (1024 - CHAR_WIDTH)) {
blitc(unit, '\r');
blitc(unit, '\n');
}
} /* blitc */
/*
* INTERRUPT SERVICE ROUTINES
*/
/*
* Service "DMA DONE" interrupt condition
*/
static void
qddint(void *arg)
{
device_t dv = arg;
struct DMAreq_header *header;
struct DMAreq *request;
volatile struct dga *dga;
volatile struct adder *adder;
int cookie; /* DMA adrs for QDSS */
int unit = device_unit(dv);
if (dga->csr & BUS_ERR)
printf("qd%d: qddint: DMA hardware bus error.\n", unit);
}
/*
* if this was a DMA from user space...
*/
if (qdflags[unit].user_dma) {
qdflags[unit].user_dma = 0;
wakeup((void *)&qdflags[unit].user_dma);
return;
}
/*
* if we're doing DMA request queue services, field the error condition
*/
if (dga->csr & DMA_ERR) {
DMA_SETERROR(header); /* flag error in header status word */
DMA_CLRACTIVE(header);
header->DMAreq[header->oldest].DMAdone |= HARD_ERROR;
header->newest = header->oldest;
header->used = 0;
/* check for unexpected interrupt */
if (DMA_ISEMPTY(header))
return;
DMA_GETEND(header); /* update request queue indices */
/*
* if no more DMA pending, wake up "select" client and exit
*/
if (DMA_ISEMPTY(header)) {
selnotify(&qdrsel[unit], 0, 0);
DMA_CLRACTIVE(header); /* flag DMA done */
return;
}
/*
* ADDER interrupt service routine
*/
static void
qdaint(void *arg)
{
device_t dv = arg;
volatile struct adder *adder;
struct color_buf *cbuf;
int i;
struct rgb *rgbp;
volatile short *red;
volatile short *green;
volatile short *blue;
int unit = device_unit(dv);
(void)spl4(); /* allow interval timer in */
adder = (struct adder *) qdmap[unit].adder;
/*
* service the vertical blank interrupt (VSYNC bit) by loading
* any pending color map load request
*/
if (adder->status & VSYNC) {
adder->status &= ~VSYNC; /* clear the interrupt */
cbuf = color_buf[unit];
if (cbuf->status & LOAD_COLOR_MAP) {
red = (short *) qdmap[unit].red;
green = (short *) qdmap[unit].green;
blue = (short *) qdmap[unit].blue;
for ( c = 1; c <= 0x10; c <<= 1) {
if (c & (a ^ b)) {
if (c == T_LEFT_BUTTON)
event->vse_key = VSE_T_LEFT_BUTTON;
else if (c == T_FRONT_BUTTON)
event->vse_key = VSE_T_FRONT_BUTTON;
else if (c == T_RIGHT_BUTTON)
event->vse_key = VSE_T_RIGHT_BUTTON;
else if (c == T_BACK_BUTTON)
event->vse_key = VSE_T_BACK_BUTTON;
break;
}
}
/* set bit = button depressed */
if (c & a)
event->vse_direction = VSE_KBTDOWN;
else
event->vse_direction = VSE_KBTUP;
}
/* refresh last report */
last_rep[unit] = current_rep[unit];
} /* get last byte of report */
} /* pick up tablet input */
} /* while input available.. */
/*
* do select wakeup
*/
if (do_wakeup) {
selnotify(&qdrsel[unit], 0, 0);
do_wakeup = 0;
}
} else {
/*
* if the graphic device is not turned on, this is console input
*/
if (qdpolling)
return;
if (unit >= qd_cd.cd_ndevs || device_lookup(&qd_cd, unit) == NULL)
return; /* no such device or address */
tp = qd_tty[unit << 2];
/*
* Get a character from the keyboard.
*/
while (duart->statusA&RCV_RDY) {
key = duart->dataA;
key &= 0xFF;
/*
* Check for various keyboard errors
*/
if (key == LK_POWER_ERROR || key == LK_KDOWN_ERROR ||
key == LK_INPUT_ERROR || key == LK_OUTPUT_ERROR) {
printf("qd%d: qdiint: Keyboard error, code = %x\n",qd,key);
return;
}
if (key < LK_LOWEST)
return;
/*
* See if its a state change key */
switch (key) {
case LOCK:
q_keyboard.lock ^= 0xffff; /* toggle */
if (q_keyboard.lock)
led_control(qd, LK_LED_ENABLE,
LK_LED_LOCK);
else
led_control(qd, LK_LED_DISABLE,
LK_LED_LOCK);
return;
case SHIFT:
q_keyboard.shift ^= 0xFFFF;
return;
case CNTRL:
q_keyboard.cntrl ^= 0xFFFF;
return;
case ALLUP:
q_keyboard.cntrl = 0;
q_keyboard.shift = 0;
return;
case REPEAT:
chr = q_keyboard.last;
break;
/*
* Test for cntrl characters. If set, see if the character
* is eligible to become a control character. */
/*
*
* Clear the QDSS screen
*
* >>> NOTE <<<
*
* This code requires that certain adder initialization be valid. To
* assure that this requirement is satisfied, this routine should be
* called only after calling the "setup_dragon()" function.
*
* Clear the bitmap a piece at a time. Since the fast scroll clear
* only clears the current displayed portion of the bitmap put a
* temporary value in the y limit register so we can access whole
* bitmap
*
*/
void
clear_qd_screen(int unit)
{
volatile struct adder *adder;
adder = (struct adder *) qdmap[unit].adder;
/*
* load the mouse cursor's template RAM bitmap
*/
void
ldcursor(int unit, short *bitmap)
{
volatile struct dga *dga;
volatile short *temp;
int i;
int curs;
if (dga->csr & CURS_ENB) { /* if the cursor is enabled.. */
curs = -1; /* ..note that.. */
dga->csr &= ~CURS_ENB; /* ..and shut it off */
} else
curs = 0;
dga->csr &= ~CURS_ENB; /* shut off the cursor */
temp += (8 * 1024) - 32; /* cursor is 32 WORDS from the end */
/* ..of the 8k WORD template space */
for (i = 0; i < 32; ++i)
*temp++ = *bitmap++;
if (curs) { /* if cursor was enabled.. */
dga->csr |= CURS_ENB; /* ..turn it back on */
}
} /* ldcursor */
/*
* Put the console font in the QDSS off-screen memory
*/
void
ldfont(int unit)
{
volatile struct adder *adder;
/*
* Figure out how many characters can be stored on one "line" of
* offscreen memory.
*/
max_chars_line = MAX_SCREEN_X/(CHAR_WIDTH*2);
if ((CHARS/2 + CHARS%2) < max_chars_line)
max_chars_line = CHARS/2 + CHARS%2;
/*
* iteratively do the processor to bitmap xfer */
for (i = 0; i < ROWS; ++i) {
/* PTOB a scan line */
for (j = 0, k = i; j < max_chars_line; ++j) {
/* PTOB one scan of a char cell */
packed = q_font[k];
k += ROWS;
packed |= ((short)q_font[k] << 8);
k += ROWS;
/*
* (XXX XXX XXX - should remove)
*
* Copy the second row of characters. Subtract the first
* row from the total number. Divide this quantity by 2
* because 2 chars are stored in a short in the PTOB loop
* below. Figure out how many characters can be stored on
* one "line" of offscreen memory
*/
max_chars_line = MAX_SCREEN_X/(CHAR_WIDTH*2);
if ((CHARS/2 + CHARS%2) < max_chars_line)
return;
max_chars_line = (CHARS/2 + CHARS%2) - max_chars_line; /* 95 - 64 */
/* Paranoia check to see if 3rd row may be needed */
if (max_chars_line > (MAX_SCREEN_X/(CHAR_WIDTH*2)))
max_chars_line = MAX_SCREEN_X/(CHAR_WIDTH*2);
/*
* iteratively do the processor to bitmap xfer
*/
for (i = 0; i < ROWS; ++i) {
/*
* PTOB a scan line
*/
for (j = 0, k = i; j < max_chars_line; ++j) {
/*
* PTOB one scan of a char cell
*/
packed = q_font[k + FONT_OFFSET];
k += ROWS;
packed |= ((short)q_font[k + FONT_OFFSET] << 8);
k += ROWS;
(void)wait_status(adder, TX_READY);
adder->id_data = packed;
}
}
} /* ldfont */
/*
* Disable or enable polling. This is used when entering or leaving the
* kernel debugger.
*/
void
qdcnpollc(dev_t dev, int onoff)
{
qdpolling = onoff;
}
/*
* Get a character from the LK201 (polled)
*/
int
qdcngetc(dev_t dev)
{
short key;
char chr;
volatile struct duart *duart;
duart = (struct duart *) qdmap[0].duart;
/*
* Get a character from the keyboard.
*/
LOOP:
while (!(duart->statusA&RCV_RDY))
;
if (chr < ' ' && chr > '~') /* if input is non-displayable */
return(0); /* ..then pitch it! */
q_keyboard.last = chr;
/*
* Check for special function keys */
if (chr & 0x80) /* pitch the function keys */
return(0);
else
return(chr);
} /* qdgetc */
/*
* led_control()... twiddle LK-201 LED's
*/
void
led_control(int unit, int cmd, int led_mask)
{
int i;
volatile struct duart *duart;
duart = (struct duart *)qdmap[unit].duart;
for (i = 1000; i > 0; --i) {
if (duart->statusA&XMT_RDY) {
duart->dataA = cmd;
break;
}
}
for (i = 1000; i > 0; --i) {
if (duart->statusA&XMT_RDY) {
duart->dataA = led_mask;
break;
}
}
return;
/*
* init the ADDER, VIPER, bitmaps, & color map
*/
void
setup_dragon(int unit)
{
volatile struct adder *adder;
volatile struct dga *dga;
volatile short *memcsr;
int i;
short top; /* clipping/scrolling boundaries */
short bottom;
short right;
short left;
volatile short *red; /* color map pointers */
volatile short *green;
volatile short *blue;
/*
* init for setup
*/
adder = (struct adder *) qdmap[unit].adder;
dga = (struct dga *) qdmap[unit].dga;
memcsr = (short *) qdmap[unit].memcsr;
dga->csr &= ~(DMA_IE | 0x700); /* halt DMA and kill the intrpts */
*memcsr = SYNC_ON; /* blank screen and turn off LED's */
adder->command = CANCEL;
/*
* set monitor timing
*/
adder->x_scan_count_0 = 0x2800;
adder->x_scan_count_1 = 0x1020;
adder->x_scan_count_2 = 0x003A;
adder->x_scan_count_3 = 0x38F0;
adder->x_scan_count_4 = 0x6128;
adder->x_scan_count_5 = 0x093A;
adder->x_scan_count_6 = 0x313C;
adder->sync_phase_adj = 0x0100;
adder->x_scan_conf = 0x00C8;
/*
* got a bug in second pass ADDER! lets take care of it
*
* normally, just use the code in the following bug fix code, but to
* make repeated demos look pretty, load the registers as if there was
* no bug and then test to see if we are getting sync
*/
adder->y_scan_count_0 = 0x135F;
adder->y_scan_count_1 = 0x3363;
adder->y_scan_count_2 = 0x2366;
adder->y_scan_count_3 = 0x0388;
/*
* if no sync, do the bug fix code
*/
if (wait_status(adder, VSYNC) == BAD) {
/* first load all Y scan registers with very short frame and
* wait for scroll service. This guarantees at least one SYNC
* to fix the pass 2 Adder initialization bug (synchronizes
* XCINCH with DMSEEDH)
*/
adder->y_scan_count_0 = 0x01;
adder->y_scan_count_1 = 0x01;
adder->y_scan_count_2 = 0x01;
adder->y_scan_count_3 = 0x01;
/*
* delay at least 1 full frame time
*/
(void)wait_status(adder, VSYNC);
(void)wait_status(adder, VSYNC);
/*
* now load the REAL sync values (in reverse order just to
* be safe.
*/
adder->y_scan_count_3 = 0x0388;
adder->y_scan_count_2 = 0x2366;
adder->y_scan_count_1 = 0x3363;
adder->y_scan_count_0 = 0x135F;
}
*memcsr = SYNC_ON | UNBLANK; /* turn off leds and turn on video */
/*
* zero the index registers
*/
adder->x_index_pending = 0;
adder->y_index_pending = 0;
adder->x_index_new = 0;
adder->y_index_new = 0;
adder->x_index_old = 0;
adder->y_index_old = 0;
adder->pause = 0;
/*
* set rasterop mode to normal pen down
*/
adder->rasterop_mode = DST_WRITE_ENABLE | DST_INDEX_ENABLE | NORMAL;
/*
* set the rasterop registers to a default values
*/
adder->source_1_dx = 1;
adder->source_1_dy = 1;
adder->source_1_x = 0;
adder->source_1_y = 0;
adder->destination_x = 0;
adder->destination_y = 0;
adder->fast_dest_dx = 1;
adder->fast_dest_dy = 0;
adder->slow_dest_dx = 0;
adder->slow_dest_dy = 1;
adder->error_1 = 0;
adder->error_2 = 0;
/*
* scale factor = UNITY
*/
adder->fast_scale = UNITY;
adder->slow_scale = UNITY;
/*
* set the source 2 parameters
*/
adder->source_2_x = 0;
adder->source_2_y = 0;
adder->source_2_size = 0x0022;
/*
* initialize plane addresses for eight vipers
*/
write_ID(adder, CS_UPDATE_MASK, 0x0001);
write_ID(adder, PLANE_ADDRESS, 0x0000);
write_ID(adder, CS_UPDATE_MASK, 0x0002);
write_ID(adder, PLANE_ADDRESS, 0x0001);
write_ID(adder, CS_UPDATE_MASK, 0x0004);
write_ID(adder, PLANE_ADDRESS, 0x0002);
write_ID(adder, CS_UPDATE_MASK, 0x0008);
write_ID(adder, PLANE_ADDRESS, 0x0003);
write_ID(adder, CS_UPDATE_MASK, 0x0010);
write_ID(adder, PLANE_ADDRESS, 0x0004);
write_ID(adder, CS_UPDATE_MASK, 0x0020);
write_ID(adder, PLANE_ADDRESS, 0x0005);
write_ID(adder, CS_UPDATE_MASK, 0x0040);
write_ID(adder, PLANE_ADDRESS, 0x0006);
write_ID(adder, CS_UPDATE_MASK, 0x0080);
write_ID(adder, PLANE_ADDRESS, 0x0007);
/*
* initialize the external registers.
*/
write_ID(adder, CS_UPDATE_MASK, 0x00FF);
write_ID(adder, CS_SCROLL_MASK, 0x00FF);
/*
* initialize resolution mode
*/
write_ID(adder, MEMORY_BUS_WIDTH, 0x000C); /* bus width = 16 */
write_ID(adder, RESOLUTION_MODE, 0x0000); /* one bit/pixel */
/*
* initialize viper registers
*/
write_ID(adder, SCROLL_CONSTANT, SCROLL_ENABLE|VIPER_LEFT|VIPER_UP);
write_ID(adder, SCROLL_FILL, 0x0000);
/*
* set clipping and scrolling limits to full screen
*/
for (i = 1000, adder->status = 0;
i > 0 && !(adder->status&ADDRESS_COMPLETE); --i)
;
if (i == 0)
printf("qd%d: setup_dragon: timeout on ADDRESS_COMPLETE\n",unit);
top = 0;
bottom = 2048;
left = 0;
right = 1024;
adder->x_clip_min = left;
adder->x_clip_max = right;
adder->y_clip_min = top;
adder->y_clip_max = bottom;
adder->scroll_x_min = left;
adder->scroll_x_max = right;
adder->scroll_y_min = top;
adder->scroll_y_max = bottom;
(void)wait_status(adder, VSYNC); /* wait at LEAST 1 full frame */
(void)wait_status(adder, VSYNC);
adder->x_index_pending = left;
adder->y_index_pending = top;
adder->x_index_new = left;
adder->y_index_new = top;
adder->x_index_old = left;
adder->y_index_old = top;
for (i = 1000, adder->status = 0; i > 0 &&
!(adder->status&ADDRESS_COMPLETE) ; --i)
;
if (i == 0)
printf("qd%d: setup_dragon: timeout on ADDRESS_COMPLETE\n",unit);
write_ID(adder, LEFT_SCROLL_MASK, 0x0000);
write_ID(adder, RIGHT_SCROLL_MASK, 0x0000);
/*
* set source and the mask register to all ones (ie: white) o
*/
write_ID(adder, SOURCE, 0xFFFF);
write_ID(adder, MASK_1, 0xFFFF);
write_ID(adder, VIPER_Z_LOAD | FOREGROUND_COLOR_Z, 255);
write_ID(adder, VIPER_Z_LOAD | BACKGROUND_COLOR_Z, 0);
/*
* initialize Operand Control Register banks for fill command
*/
write_ID(adder, SRC1_OCR_A, EXT_NONE | INT_M1_M2 | NO_ID | WAIT);
write_ID(adder, SRC2_OCR_A, EXT_NONE | INT_SOURCE | NO_ID | NO_WAIT);
write_ID(adder, DST_OCR_A, EXT_NONE | INT_NONE | NO_ID | NO_WAIT);
write_ID(adder, SRC1_OCR_B, EXT_NONE | INT_SOURCE | NO_ID | WAIT);
write_ID(adder, SRC2_OCR_B, EXT_NONE | INT_M1_M2 | NO_ID | NO_WAIT);
write_ID(adder, DST_OCR_B, EXT_NONE | INT_NONE | NO_ID | NO_WAIT);
/*
* init Logic Unit Function registers, (these are just common values,
* and may be changed as required).
*/
write_ID(adder, LU_FUNCTION_R1, FULL_SRC_RESOLUTION | LF_SOURCE);
write_ID(adder, LU_FUNCTION_R2, FULL_SRC_RESOLUTION | LF_SOURCE |
INV_M1_M2);
write_ID(adder, LU_FUNCTION_R3, FULL_SRC_RESOLUTION | LF_D_OR_S);
write_ID(adder, LU_FUNCTION_R4, FULL_SRC_RESOLUTION | LF_D_XOR_S);
/*
* load the color map for black & white
*/
for (i = 0, adder->status = 0; i < 10000 && !(adder->status&VSYNC); ++i)
;
if (i == 0)
printf("qd%d: setup_dragon: timeout on VSYNC\n", unit);
red = (short *) qdmap[unit].red;
green = (short *) qdmap[unit].green;
blue = (short *) qdmap[unit].blue;
/*
* Init the DUART and set defaults in input
*/
void
setup_input(int unit)
{
volatile struct duart *duart; /* DUART register structure pointer */
int i, bits;
char id_byte;
/*
* init keyboard defaults (DUART channel A)
*/
for (i = 500; i > 0; --i) {
if (duart->statusA&XMT_RDY) {
duart->dataA = LK_DEFAULTS;
break;
}
}
for (i = 100000; i > 0; --i) {
if (duart->statusA&RCV_RDY) {
break;
}
}
if (duart->dataA) /* flush the ACK */
;
/*
* identify the pointing device
*/
for (i = 500; i > 0; --i) {
if (duart->statusB&XMT_RDY) {
duart->dataB = SELF_TEST;
break;
}
}
/*
* wait for 1st byte of self test report */
for (i = 100000; i > 0; --i) {
if (duart->statusB&RCV_RDY) {
break;
}
}
if (i == 0) {
printf("qd[%d]: setup_input: timeout on 1st byte of self test\n"
,unit);
goto OUT;
}
if (duart->dataB)
;
/*
* wait for ID byte of self test report
*/
for (i = 100000; i > 0; --i) {
if (duart->statusB&RCV_RDY) {
break;
}
}
if (i == 0) {
printf("qd[%d]: setup_input: timeout on 2nd byte of self test\n", unit);
goto OUT;
}
id_byte = duart->dataB;
/*
* wait for other bytes to come in
*/
for (i = 100000; i > 0; --i) {
if (duart->statusB & RCV_RDY) {
if (duart->dataB)
;
break;
}
}
if (i == 0) {
printf("qd[%d]: setup_input: timeout on 3rd byte of self test\n", unit);
goto OUT;
}
for (i = 100000; i > 0; --i) {
if (duart->statusB&RCV_RDY) {
if (duart->dataB)
;
break;
}
}
if (i == 0) {
printf("qd[%d]: setup_input: timeout on 4th byte of self test\n", unit);
goto OUT;
}
/*
* flag pointing device type and set defaults
*/
for (i=100000; i>0; --i)
; /*XXX*/
if ((id_byte & 0x0F) != TABLET_ID) {
qdflags[unit].pntr_id = MOUSE_ID;
for (i = 500; i > 0; --i) {
if (duart->statusB&XMT_RDY) {
duart->dataB = INC_STREAM_MODE;
break;
}
}
}
else {
qdflags[unit].pntr_id = TABLET_ID;
for (i = 500; i > 0; --i) {
if (duart->statusB&XMT_RDY) {
duart->dataB = T_STREAM;
break;
}
}
}
OUT:
duart->imask = qdflags[unit].duart_imask;
} /* setup_input */
/*
* delay for at least one display frame time
*
* return: BAD means that we timed out without ever seeing the
* vertical sync status bit
* GOOD otherwise
*/
int
wait_status(volatile struct adder *adder, int mask)
{
int i;
for (i = 10000, adder->status = 0 ; i > 0 &&
!(adder->status&mask) ; --i)
;
if (i == 0) {
printf("wait_status: timeout polling for 0x%x in adder->status\n", mask);
return(BAD);
}
return(GOOD);
} /* wait_status */
/*
* write out onto the ID bus
*/
void
write_ID(volatile struct adder *adder, short adrs, short data)
{
int i;
for (i = 100000, adder->status = 0 ;
i > 0 && !(adder->status&ADDRESS_COMPLETE) ; --i)
;
if (i == 0)
goto ERR;
for (i = 100000, adder->status = 0 ;
i > 0 && !(adder->status&TX_READY) ; --i)
;
if (i > 0) {
adder->id_data = data;
adder->command = ID_LOAD | adrs;
return ;
}
ERR:
printf("write_ID: timeout trying to write to VIPER\n");
return ;