BSD 4_3_Net_2 release
[unix-history] / usr / src / sys / vax / uba / ps.c
/*-
* Copyright (c) 1982, 1986 The 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. 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.
*
* @(#)ps.c 7.7 (Berkeley) 5/9/91
*/
/*
* Evans and Sutherland Picture System 2 driver -- Bill Reeves.
*/
/*
* Still to be done:
* WAIT_HIT
*/
#include "ps.h"
#if NPS > 0
#define EXTERNAL_SYNC
#include "../include/pte.h"
#include "sys/param.h"
#include "sys/systm.h"
#include "sys/ioctl.h"
#include "sys/map.h"
#include "sys/buf.h"
#include "sys/conf.h"
#include "sys/user.h"
#include "sys/uio.h"
#include "ubareg.h"
#include "ubavar.h"
#include "psreg.h"
int psprobe(), psattach(), psextsync();
int psclockintr(), pssystemintr(), psdeviceintr(), psdmaintr();
struct uba_device *psdinfo[NPS];
u_short psstd[] = { 0 };
struct uba_driver psdriver =
{ psprobe, 0, psattach, 0, psstd, "ps", psdinfo };
#define PSUNIT(dev) (minor(dev))
#define MAXAUTOREFRESH 4
#define MAXAUTOMAP 4
#define MAXDBSIZE (0177777/2)
#define PSPRI (PZERO+1)
#define PSWAIT(psaddr) { \
register short i = 20000, j; \
while (i-- != 0 && ((j = psaddr->ps_iostat) & DIOREADY) == 0) \
;\
}
struct psrefresh {
enum {
SINGLE_STEP_RF,
AUTO_RF,
TIME_RF
} state;
enum {
RUNNING_RF,
SYNCING_RF,
WAITING_MAP,
STOPPED_RF
} mode;
u_short sraddrs[MAXAUTOREFRESH];
short nsraddrs;
short srcntr;
char waiting;
char stop;
int icnt;
int timecnt;
};
struct psdbuffer {
enum {
ON_DB,
OFF_DB
} state;
u_short dbaddrs[2];
u_short dbsize;
short rbuffer;
};
struct psmap {
enum {
SINGLE_STEP_MAP,
AUTO_MAP
} state;
enum {
RUNNING_MAP,
WAITING_RF,
WAITING_START,
STOPPED_MAP
} mode;
u_short maddrs[MAXAUTOMAP];
short nmaddrs;
short mcntr;
short outputstart;
char waiting;
char stop;
int icnt;
};
/*
* PS2 software state.
*/
struct ps {
char ps_open; /* device is open */
uid_t ps_uid; /* uid of device owner */
struct psrefresh ps_refresh; /* refresh state */
struct psdbuffer ps_dbuffer; /* double buffering state */
struct psmap ps_map; /* segment map state */
int ps_clockticks; /* clock ints between refresh */
int ps_clockmiss; /* clock ints w/o refresh */
int ps_clockcnt; /* count of clock interrupts */
int ps_hitcnt; /* count of hit interrupts */
int ps_strayintr; /* count of stray interrupts */
int ps_icnt; /* count of system interrupts */
/* BEGIN GROT */
int ps_lastrequest;
int ps_lastrequest2;
int ps_lastfunnyrequest;
int ps_funnycnt;
/* END GROT */
} ps[NPS];
psprobe(reg)
caddr_t reg;
{
register int br, cvec;
register struct psdevice *psaddr = (struct psdevice *)reg;
#ifdef lint
br = 0; cvec = br; br = cvec;
psclockintr((dev_t)0); pssystemintr((dev_t)0);
psdeviceintr((dev_t)0); psdmaintr((dev_t)0);
psextsync(0, 0);
#endif
psaddr->ps_iostat = PSRESET;
DELAY(200);
psaddr->ps_addr = RTCIE;
PSWAIT(psaddr); psaddr->ps_data = 01;
psaddr->ps_iostat = PSIE;
psaddr->ps_addr = RTCSR;
PSWAIT(psaddr); psaddr->ps_data = SYNC|RUN;
DELAY(200000);
psaddr->ps_addr = RTCREQ;
PSWAIT(psaddr); psaddr->ps_data = 01;
psaddr->ps_iostat = 0;
psaddr->ps_iostat = PSRESET;
return (sizeof (struct psdevice));
}
/*ARGSUSED*/
psattach(ui)
struct uba_device *ui;
{
}
psopen(dev)
dev_t dev;
{
register struct ps *psp;
register struct uba_device *ui;
register int unit = PSUNIT(dev);
if (unit >= NPS || (psp = &ps[minor(dev)])->ps_open ||
(ui = psdinfo[unit]) == 0 || ui->ui_alive == 0)
return (ENXIO);
psp->ps_open = 1;
psp->ps_uid = u.u_uid;
psp->ps_strayintr = 0;
psp->ps_refresh.state = SINGLE_STEP_RF;
psp->ps_refresh.mode = STOPPED_RF;
psp->ps_refresh.waiting = 0;
psp->ps_refresh.stop = 0;
psp->ps_dbuffer.state = OFF_DB;
psp->ps_map.state = SINGLE_STEP_MAP;
psp->ps_map.mode = STOPPED_MAP;
psp->ps_map.waiting = 0;
psp->ps_map.stop = 0;
psp->ps_clockticks = 0;
psp->ps_clockmiss = 0;
psp->ps_refresh.icnt = psp->ps_map.icnt = psp->ps_clockcnt = 0;
psp->ps_hitcnt = 0;
psp->ps_icnt = 0;
maptouser(ui->ui_addr);
return (0);
}
psclose(dev)
dev_t dev;
{
register struct psdevice *psaddr =
(struct psdevice *)psdinfo[PSUNIT(dev)]->ui_addr;
ps[PSUNIT(dev)].ps_open = 0;
psaddr->ps_iostat = 0; /* clear IENABLE */
PSWAIT(psaddr); psaddr->ps_addr = RFSR; /* set in auto refresh mode */
PSWAIT(psaddr); psaddr->ps_data = AUTOREF;
unmaptouser((caddr_t)psaddr);
return (0);
}
/*ARGSUSED*/
psread(dev, uio)
dev_t dev;
struct uio *uio;
{
}
/*ARGSUSED*/
pswrite(dev, uio)
dev_t dev;
struct uio *uio;
{
}
/*ARGSUSED*/
psioctl(dev, cmd, data, flag)
register caddr_t data;
{
register struct uba_device *ui = psdinfo[PSUNIT(dev)];
register struct ps *psp = &ps[PSUNIT(dev)];
int *waddr = *(int **)data;
int n, arg, i, error = 0;
switch (cmd) {
case PSIOGETADDR:
*(caddr_t *)data = ui->ui_addr;
break;
case PSIOAUTOREFRESH:
n = fuword((caddr_t)waddr++);
if (n == -1)
return (EFAULT);
if (n < 0 || n > MAXAUTOREFRESH)
return (EINVAL);
for (i = 0; i < n; i++) {
if ((arg = fuword((caddr_t)waddr++)) == -1)
return (EFAULT);
psp->ps_refresh.sraddrs[i] = arg;
}
psp->ps_refresh.state = AUTO_RF;
psp->ps_refresh.nsraddrs = n;
psp->ps_refresh.srcntr = 0;
psp->ps_refresh.mode = WAITING_MAP;
break;
case PSIOAUTOMAP:
n = fuword((caddr_t)waddr++);
if (n == -1)
return (EFAULT);
if (n < 0 || n > MAXAUTOMAP)
return (EINVAL);
for (i = 0; i < n; i++) {
if ((arg = fuword((caddr_t)waddr++)) == -1)
return (EFAULT);
psp->ps_map.maddrs[i] = arg;
}
if ((arg = fuword((caddr_t)waddr++)) == -1)
return (EFAULT);
psp->ps_map.outputstart = arg;
psp->ps_map.state = AUTO_MAP;
psp->ps_map.nmaddrs = n;
psp->ps_map.mcntr = 0;
psp->ps_map.mode = WAITING_START;
break;
case PSIOSINGLEREFRESH:
psp->ps_refresh.state = SINGLE_STEP_RF;
break;
case PSIOSINGLEMAP:
psp->ps_map.state = SINGLE_STEP_MAP;
break;
case PSIODOUBLEBUFFER:
if ((arg = fuword((caddr_t)waddr++)) == -1)
return (EFAULT);
psp->ps_dbuffer.dbaddrs[0] = arg;
if ((arg = fuword((caddr_t)waddr++)) == -1)
return (EFAULT);
if (arg <= 0 || arg > MAXDBSIZE)
return (EINVAL);
psp->ps_dbuffer.dbsize = arg;
psp->ps_dbuffer.dbaddrs[1] = psp->ps_dbuffer.dbaddrs[0]+arg;
psp->ps_dbuffer.state = ON_DB;
psp->ps_dbuffer.rbuffer = 0;
break;
case PSIOSINGLEBUFFER:
psp->ps_dbuffer.state = OFF_DB;
break;
case PSIOTIMEREFRESH:
if (psp->ps_refresh.state != SINGLE_STEP_RF)
return (EINVAL);
if ((arg = fuword((caddr_t)waddr++)) == -1)
return (EFAULT);
psp->ps_refresh.state = TIME_RF;
psp->ps_refresh.timecnt = arg;
break;
case PSIOWAITREFRESH:
if (psp->ps_refresh.mode != RUNNING_RF) /* not running */
return (0); /* dont wait */
/* fall into ... */
case PSIOSTOPREFRESH:
if (cmd == PSIOSTOPREFRESH) {
if (psp->ps_refresh.mode == STOPPED_RF &&
psp->ps_refresh.state != TIME_RF)
return (0);
psp->ps_refresh.stop = 1;
}
(void) spl5();
psp->ps_refresh.waiting = 1;
while (psp->ps_refresh.waiting)
if (error = tsleep(&psp->ps_refresh.waiting,
PSPRI | PCATCH, devwait, 0))
break;
(void) spl0();
if (error)
return (error);
if (cmd == PSIOSTOPREFRESH)
psp->ps_refresh.mode = STOPPED_RF;
if (psp->ps_refresh.state == TIME_RF)
psp->ps_refresh.state = SINGLE_STEP_RF;
break;
case PSIOWAITMAP:
if (psp->ps_map.mode != RUNNING_MAP) /* not running */
return (0); /* dont wait */
/* fall into ... */
case PSIOSTOPMAP:
if (cmd == PSIOSTOPMAP)
psp->ps_map.stop = 1;
(void) spl5();
psp->ps_map.waiting = 1;
while (psp->ps_map.waiting)
if (error = tsleep(&psp->ps_map.waiting, PSPRI | PCATCH,
devwait, 0))
break;
(void) spl0();
break;
default:
return (ENOTTY);
break;
}
return (error);
}
#define SAVEPSADDR(psaddr, savepsaddr) { \
register short i, xx1; \
xx1 = splclock(); \
i = psaddr->ps_addr; \
while ((psaddr->ps_iostat & DIOREADY) == 0) \
; \
savepsaddr = psaddr->ps_data; \
splx(xx1); \
}
#define RESTORPSADDR(psaddr, savepsaddr) { \
register short xx2; \
xx2 = splclock(); \
while ((psaddr->ps_iostat & DIOREADY) == 0) \
;\
psaddr->ps_addr = savepsaddr; \
splx(xx2); \
}
psclockintr(dev)
dev_t dev;
{
register struct psdevice *psaddr =
(struct psdevice *)psdinfo[PSUNIT(dev)]->ui_addr;
register struct ps *psp = &ps[PSUNIT(dev)];
int savepsaddr;
if (!psp->ps_open)
return;
psp->ps_clockcnt++;
SAVEPSADDR(psaddr, savepsaddr);
#ifndef EXTERNAL_SYNC
if (psp->ps_refresh.state == AUTO_RF) {
if (psp->ps_refresh.mode == SYNCING_RF &&
psp->ps_refresh.state != TIME_RF) {
(void) psrfnext(psp, psaddr);
} else {
psp->ps_clockticks++;
psp->ps_clockmiss++;
}
}
#endif
PSWAIT(psaddr); psaddr->ps_addr = RTCREQ;
PSWAIT(psaddr); psaddr->ps_data = 01; /* clear the request bits */
RESTORPSADDR(psaddr, savepsaddr);
}
/*ARGSUSED*/
pssystemintr(dev)
dev_t dev;
{
register struct psdevice *psaddr =
(struct psdevice *)psdinfo[PSUNIT(dev)]->ui_addr;
register struct ps *psp = &ps[PSUNIT(dev)];
short request, tmp;
register int savepsaddr, x;
if (!psp->ps_open)
return;
psp->ps_icnt++;
SAVEPSADDR(psaddr, savepsaddr);
PSWAIT(psaddr); psaddr->ps_addr = SYSREQ;
PSWAIT(psaddr); request = psaddr->ps_data;
request = request&0377;
psp->ps_lastrequest2 = psp->ps_lastrequest;
psp->ps_lastrequest = request;
if (request &~ (HALT_REQ|RFSTOP_REQ|HIT_REQ)) {
psp->ps_lastfunnyrequest = request;
psp->ps_funnycnt++;
}
PSWAIT(psaddr); psaddr->ps_addr = SYSREQ;
tmp = request&(~(HALT_REQ|MOSTOP_REQ)); /* acknowledge */
PSWAIT(psaddr); psaddr->ps_data = tmp;
if (request & (MOSTOP_REQ|HALT_REQ)) { /* Map stopped */
psp->ps_map.icnt++;
psmapstop(psaddr, psp, request);/* kill it dead */
if (psp->ps_map.waiting) {
psp->ps_map.waiting = 0;
wakeup(&psp->ps_map.waiting);
if (psp->ps_map.stop) {
psp->ps_map.stop = 0;
goto tryrf;
}
}
if (psp->ps_map.state == AUTO_MAP && !psmapnext(psp, psaddr)) {
psp->ps_map.mcntr = 0;
/* prepare for next round */
pssetmapbounds(psp, psaddr);
if (psp->ps_refresh.state == AUTO_RF) {
if (psp->ps_refresh.mode == WAITING_MAP){
if (psp->ps_dbuffer.state == ON_DB)
/* fill other db */
psdbswitch(psp, psaddr);
else
psp->ps_map.mode = WAITING_RF;
#ifdef EXTERNAL_SYNC
x = splclock();
#endif
(void) psrfnext(psp, psaddr);
#ifdef EXTERNAL_SYNC
splx(x);
#endif
} else
psp->ps_map.mode = WAITING_RF;
} else { /* no auto refresh */
if (psp->ps_dbuffer.state == ON_DB)
/* fill other db */
psdbswitch(psp, psaddr);
else
(void) psmapnext(psp, psaddr);
}
}
}
tryrf:
if (request & RFSTOP_REQ) { /* Refresh stopped */
psp->ps_refresh.icnt++;
if (psp->ps_refresh.state == TIME_RF)
if (--psp->ps_refresh.timecnt > 0)
goto tryhit;
psrfstop(psaddr, psp);
if (psp->ps_refresh.waiting) {
psp->ps_refresh.waiting = 0;
wakeup(&psp->ps_refresh.waiting);
if (psp->ps_refresh.stop) {
psp->ps_refresh.stop = 0;
goto tryhit;
}
}
if (psp->ps_refresh.state == AUTO_RF)
if (!psrfnext(psp, psaddr)) { /* at end of refresh cycle */
if (psp->ps_map.state == AUTO_MAP &&
psp->ps_map.mode == WAITING_RF) {
if (psp->ps_dbuffer.state == ON_DB)
psdbswitch(psp, psaddr);
else
(void) psmapnext(psp, psaddr);
}
psp->ps_refresh.srcntr = 0;
#ifdef EXTERNAL_SYNC
x = splclock();
#endif
psp->ps_refresh.mode = SYNCING_RF;
if (psp->ps_clockticks)
(void) psrfnext(psp, psaddr);
psp->ps_clockticks = 0;
#ifdef EXTERNAL_SYNC
splx(x);
#endif
}
}
tryhit:
if (request & HIT_REQ) /* Hit request */
psp->ps_hitcnt++;
if (request == 0)
psp->ps_strayintr++;
RESTORPSADDR(psaddr, savepsaddr);
}
psrfnext(psp, psaddr)
register struct ps *psp;
register struct psdevice *psaddr;
{
u_short start, last;
if (psp->ps_refresh.srcntr < psp->ps_refresh.nsraddrs) {
psrfstart(psp->ps_refresh.sraddrs[psp->ps_refresh.srcntr++],
0, psp, psaddr);
return (1);
}
if (psp->ps_refresh.srcntr == psp->ps_refresh.nsraddrs &&
psp->ps_dbuffer.state == ON_DB) {
start = psp->ps_dbuffer.dbaddrs[psp->ps_dbuffer.rbuffer];
last = start+psp->ps_dbuffer.dbsize;
psrfstart(start, last, psp, psaddr);
psp->ps_refresh.srcntr++; /* flag for after dbuffer */
return (1);
}
return (0);
}
psrfstart(dfaddr, last, psp, psaddr)
u_short dfaddr, last;
register struct ps *psp;
register struct psdevice *psaddr;
{
short dummy;
PSWAIT(psaddr); psaddr->ps_addr = RFASA;
PSWAIT(psaddr); psaddr->ps_data = dfaddr;
PSWAIT(psaddr);
if (last != 0)
psaddr->ps_data = last;
else
dummy = psaddr->ps_data;/* just access to get to status reg */
PSWAIT(psaddr); psaddr->ps_data = RFSTART; /* may want | here */
psp->ps_refresh.mode = RUNNING_RF;
}
/*ARGSUSED*/
psrfstop(psaddr, psp)
register struct psdevice *psaddr;
register struct ps *psp;
{
PSWAIT(psaddr); psaddr->ps_addr = RFSR;
PSWAIT(psaddr); psaddr->ps_data = 0;
}
psdbswitch(psp, psaddr)
register struct ps *psp;
register struct psdevice *psaddr;
{
psp->ps_dbuffer.rbuffer = !psp->ps_dbuffer.rbuffer;
pssetmapbounds(psp, psaddr);
(void) psmapnext(psp, psaddr);
}
psmapnext(psp, psaddr)
register struct ps *psp;
register struct psdevice *psaddr;
{
if (psp->ps_map.mcntr < psp->ps_map.nmaddrs) {
psmapstart(psp->ps_map.maddrs[psp->ps_map.mcntr++],
psp, psaddr);
return (1);
}
return (0);
}
pssetmapbounds(psp, psaddr)
register struct ps *psp;
register struct psdevice *psaddr;
{
u_short start, last;
PSWAIT(psaddr); psaddr->ps_addr = MAOL;
PSWAIT(psaddr);
if (psp->ps_dbuffer.state == ON_DB) {
start = psp->ps_dbuffer.dbaddrs[!psp->ps_dbuffer.rbuffer];
last = start+psp->ps_dbuffer.dbsize-2; /* 2 for halt cmd */
psaddr->ps_data = last;
PSWAIT(psaddr); psaddr->ps_data = start;
} else {
start = psaddr->ps_data; /* dummy: don't update limit */
PSWAIT(psaddr); psaddr->ps_data = psp->ps_map.outputstart;
}
}
psmapstart(dfaddr, psp, psaddr)
u_short dfaddr;
register struct ps *psp;
register struct psdevice *psaddr;
{
PSWAIT(psaddr); psaddr->ps_addr = MAIA;
PSWAIT(psaddr); psaddr->ps_data = dfaddr;
PSWAIT(psaddr); psaddr->ps_data = MAO|MAI; /* may want more here */
psp->ps_map.mode = RUNNING_MAP;
}
int pskillcnt = 1;
psmapstop(psaddr, psp, request)
register struct psdevice *psaddr;
register struct ps *psp;
short request;
{
register int i;
request &= HALT_REQ|MOSTOP_REQ; /* overkill?? */
for (i = 0; i < pskillcnt; i++) {
PSWAIT(psaddr); psaddr->ps_addr = MASR;
PSWAIT(psaddr); psaddr->ps_data = IOUT; /* zero MAI & MAO */
PSWAIT(psaddr); psaddr->ps_addr = MAIA;
PSWAIT(psaddr); psaddr->ps_data = 0; /* 0 input addr reg */
PSWAIT(psaddr); psaddr->ps_addr = MAOA;
PSWAIT(psaddr); psaddr->ps_data = 0; /* 0 output addr reg */
PSWAIT(psaddr); psaddr->ps_addr = SYSREQ;
PSWAIT(psaddr); psaddr->ps_data = request;
}
psp->ps_map.mode = STOPPED_MAP;
}
/*ARGSUSED*/
psdeviceintr(dev)
dev_t dev;
{
printf("ps device intr\n");
}
/*ARGSUSED*/
psdmaintr(dev)
dev_t dev;
{
printf("ps dma intr\n");
}
/*ARGSUSED*/
psreset(uban)
int uban;
{
}
/*ARGSUSED*/
psextsync(PC, PS)
{
register int n;
register struct psdevice *psaddr;
register struct ps *psp;
register int savepsaddr;
#ifdef EXTERNAL_SYNC
for (psp = ps, n = 0; n < NPS; psp++, n++) {
if (!psp->ps_open)
continue;
if (psp->ps_refresh.mode == SYNCING_RF &&
psp->ps_refresh.state != TIME_RF) {
psaddr = (struct psdevice *)psdinfo[n]->ui_addr;
SAVEPSADDR(psaddr, savepsaddr);
(void) psrfnext(psp, psaddr);
RESTORPSADDR(psaddr, savepsaddr);
} else {
psp->ps_clockticks++;
psp->ps_clockmiss++;
}
}
#endif
}
#endif