/* @(#)vs.c 7.7 (MIT) 6/21/90 */
/****************************************************************************
* Copyright (c) 1983, 1984 by *
* DIGITAL EQUIPMENT CORPORATION, Maynard, Massachusetts. *
* This software is furnished on an as-is basis and may be used and copied *
* only with inclusion of the above copyright notice. This software or any *
* other copies thereof may be provided or otherwise made available to *
* others only for non-commercial purposes. No title to or 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 *
* DIGITAL assumes no responsibility for the use or reliability of its *
* software on equipment which is not supplied by DIGITAL. *
****************************************************************************/
#define VSWAITPRI (PZERO+1)
#define VSMAXEVQ 64 /* must be power of 2 */
#define EVROUND(x) ((x) & (VSMAXEVQ - 1))
struct vsBuffArea vsBuff
[NVS
];
int vsprobe(), vsattach();
struct uba_device
*vsdinfo
[NVS
];
struct uba_driver vsdriver
=
{ vsprobe
, 0, vsattach
, 0, vsstd
, "vs", vsdinfo
, 0, 0 };
#define VSUNIT(dev) (minor(dev))
unsigned inited
: 1; /* has this ever been inited? */
unsigned open
: 1; /* only one open, please */
unsigned linkAvail
: 1; /* link is up */
short pgrp
; /* process group for SIGHUP */
int romVersion
; /* rom version */
struct vs_fparm offset
; /* address base */
struct vs_csr csr
; /* saved csr0 */
struct vs_intr irr
; /* saved interrupt reason */
struct vs_kbd krr
; /* saved keyboard */
struct vs_fparm pr
; /* saved parameter regs */
struct proc
*rsel
; /* process waiting for select */
struct vs_fparm vs_nextgo
; /* next packet to go */
short vs_status
; /* status from previous packet */
vsStats stats
; /* statistics */
int vsBuff_ubinfo
; /* ubinfo for vsBuff */
#define printI if (vsIntrPrintfs)printf
#define printD if (vsDebugPrintfs)printf
#define printM if (vsMlpPrintfs) vsMlpPrintfs--,printf
* Tell the system that it's out there, and set up the device's interrupt
* vector. Since we are supporting vs100s and vs125s,
* this is a bit kludgey. The vs100 works much
* as one expects, but the vs125 tries to set all the fiber link
* related bits when you hit VS_IE, ignoring the way the 100 works.
* Also, the vs100 will let you set the interrupt vector, but
* the vs125 ignores this and uses its hard-wired value.
* And there's no sure fire to tell which variant it is.
register int br
, cvec
; /* value-result */
register struct vsdevice
*vsaddr
= (struct vsdevice
*)reg
;
br
= 0; cvec
= br
; br
= cvec
;
cvec
= (uba_hd
[numuba
].uh_lastiv
-= 4*8);
* uh_lastiv is the last free interrupt vector in the
* unibus addapter header (uba_hd).
vsaddr
->vs_csr0
= cvec
>> 2; /* Save the vector for use on next device */
vsaddr
->vs_irr
= 0; /* Csr will only be read if irr == 0 */
vsaddr
->vs_irr
= 0; /* Clear interrupt reason register */
vsaddr
->vs_pr1
= 0; /* Clear function parameter */
vsaddr
->vs_pr2
= 0; /* Clear function parameter */
vsaddr
->vs_ivr
= cvec
; /* set up vector (no-op for vs125) */
if (vsaddr
->vs_csr0
& VS_LNK_AVL
)
return(0); /* light won't go off! */
vsaddr
->vs_csr0
&= ~VS_LNK_TRNS
;
vsaddr
->vs_csr0
|= VS_IE
; /* enable interrupts */
return sizeof(struct vsdevice
);
register struct vs_softc
*vsp
;
register struct vsdevice
*vsaddr
;
vsp
= &vs_softc
[VSUNIT(uip
->ui_unit
)];
vsBuff
[VSUNIT(uip
->ui_unit
)].vsioa
.mbox
.bottom
= 0;
vsp
->vs_nextgo
.fparm_all
= NULL
;
vsaddr
= (struct vsdevice
*) uip
->ui_addr
;
vsaddr
->vs_csr0
|= (VS_IE
| VS_XMIT_ON
);
register struct vs_softc
*vsp
;
register struct uba_device
*uip
;
register struct vsdevice
*vsaddr
;
if (VSUNIT(dev
) >= NVS
|| (vsp
= &vs_softc
[VSUNIT(dev
)])->open
||
(uip
= vsdinfo
[VSUNIT(dev
)]) == 0 || uip
->ui_alive
== 0)
vsaddr
= (struct vsdevice
*) uip
->ui_addr
;
vsb
= &vsBuff
[VSUNIT(dev
)];
printM("vsopen csr0=%x, csr1=%x, csr2=%x, csr3=%x, csr4=%x, csr5=%x, csr6=%x, csr7=%x\n",
vsaddr
->vs_csr0
, vsaddr
->vs_csr1
, vsaddr
->vs_csr2
, vsaddr
->vs_csr3
,
vsaddr
->vs_csr4
, vsaddr
->vs_csr5
, vsaddr
->vs_csr6
, vsaddr
->vs_csr7
);
* Finally! We can now set up the device.
if (!vsp
->inited
&& !(flag
& FNDELAY
)) {
ret
= vsInitDev(dev
, TRUE
);
vsp
->open
= TRUE
; /* we're open */
vsp
->pgrp
= u
.u_procp
->p_pgrp
;
bzero((caddr_t
) &vsp
->stats
, sizeof(vsStats
));
/* initialize user I/O addresses */
vsb
->vsioa
.ioreg
= (short *)vsaddr
;
vsb
->vsioa
.obuff
= vsb
->obuff
;
vsb
->vsioa
.obufflen
= VSBUFFSIZE
;
vsb
->vsioa
.ibuff
= vsb
->ibuff
;
vsb
->vsioa
.iqsize
= VSMAXEVQ
;
/* map io regs into user address space (assume they don't cross a page) */
/* map vsBuff into user address space */
vsBuffpage
= (caddr_t
)((int)vsb
& ~PGOFSET
);
vsBuffnpages
= (((int)vsb
& PGOFSET
) +
(NBPG
-1) + sizeof(struct vsBuffArea
)) >> PGSHIFT
;
vsbuf
.b_proc
= u
.u_procp
;
vsbuf
.b_un
.b_addr
= vsb
->obuff
;
vsbuf
.b_bcount
= VSBUFFSIZE
;
vsp
->vsBuff_ubinfo
= ubasetup(uip
->ui_ubanum
, &vsbuf
, UBA_CANTWAIT
);
vsb
->vsioa
.reloc
= (int) (vsp
->offset
.fparm_all
+ UBAI_ADDR(vsp
->vsBuff_ubinfo
));
register struct uba_device
*uip
= vsdinfo
[VSUNIT(dev
)];
register struct vs_softc
*vsp
= &vs_softc
[VSUNIT(dev
)];
vsaddr
= (struct vsdevice
*) uip
->ui_addr
;
printM("vsclose csr0=%x, csr1=%x, csr2=%x, csr3=%x, csr4=%x, csr5=%x, csr6=%x, csr7=%x\n",
vsaddr
->vs_csr0
, vsaddr
->vs_csr1
, vsaddr
->vs_csr2
, vsaddr
->vs_csr3
,
vsaddr
->vs_csr4
, vsaddr
->vs_csr5
, vsaddr
->vs_csr6
, vsaddr
->vs_csr7
);
vsb
= &vsBuff
[VSUNIT(dev
)];
printf("vs%d: %d errors, %d unsolicited interrupts",
VSUNIT(dev
), vsp
->stats
.errors
, vsp
->stats
.unsolIntr
);
printf(", %d link errors", vsp
->stats
.linkErrors
);
printf(", %d overruns", vsp
->stats
.overruns
);
printf(", csr0 %x, csr1 %x", vsaddr
->vs_csr0
, vsaddr
->vs_csr1
);
vsp
->inited
= FALSE
; /* init on every open */
vsp
->vs_nextgo
.fparm_all
= NULL
;
vsb
->vsioa
.mbox
.bottom
= 0;
if (vsp
->vsBuff_ubinfo
!=0) {
ubarelse(uip
->ui_ubanum
, &vsp
->vsBuff_ubinfo
);
/* unmap io regs into user address space (assume they don't cross a page) */
/* unmap vsBuff into user address space */
vsBuffpage
= (caddr_t
)((int)vsb
& ~PGOFSET
);
vsBuffnpages
= (((int)vsb
&PGOFSET
) +
(NBPG
-1)+ sizeof(struct vsBuffArea
)) >> PGSHIFT
;
vsioctl(dev
, cmd
, addr
, flag
)
register struct uba_device
*uip
= vsdinfo
[VSUNIT(dev
)];
register struct vs_softc
*vsp
= &vs_softc
[VSUNIT(dev
)];
register struct vsdevice
*vsaddr
= (struct vsdevice
*) uip
->ui_addr
;
register struct vsBuffArea
*vsb
= &vsBuff
[VSUNIT(dev
)];
switch(cmd
) { /* things that don't need the device */
/* wait for user I/O operation to complete, then go */
if ((ret
= vsb
->vsioa
.status
) == 0) {
vsp
->vs_nextgo
.fparm_all
= ((struct vs_fparm
*) addr
)->fparm_all
;
error
= tsleep((caddr_t
)vsp
, VSWAITPRI
| PCATCH
,
} while (vsp
->vs_nextgo
.fparm_all
&& error
== 0);
vsaddr
->vs_pr1
= ((struct vs_fparm
*)addr
)->fparm_low
;
vsaddr
->vs_pr2
= ((struct vs_fparm
*)addr
)->fparm_high
;
vsaddr
->vs_csr0
&= ~VS_FCN
; /* clear bits */
vsaddr
->vs_csr0
|= (VS_IE
| (VS_SEND
<< VS_FCSHIFT
) | VS_GO
);
return ((ret
& VS_REASON
) + 128);
/* wait for user I/O operation to complete */
while (vsb
->vsioa
.status
== 0) {
error
= tsleep((caddr_t
) vsp
, VSWAITPRI
| PCATCH
,
case VSIOGETVER
: /* get ROM version */
*(int *) addr
= vsp
->romVersion
;
case VSIOGETSTATS
: /* get statistics block */
*(vsStats
*)addr
= vsp
->stats
;
case VSIOGETIOA
: /* get io addresses */
if (vsp
->vsBuff_ubinfo
==0) {
*((vsIoAddrAddr
*)addr
) = &vsb
->vsioa
;
default: /* a command that could block */
switch(cmd
) { /* Commands that cause an interrupt */
case VSIOINIT
: /* initialize device */
case VSIOSTART
: /* start microcode */
vsAddr
.fparm_all
= *(caddr_t
*)addr
;
vsaddr
->vs_pr1
= vsAddr
.fparm_low
;
vsaddr
->vs_pr2
= vsAddr
.fparm_high
;
vsaddr
->vs_csr0
&= ~VS_FCN
; /* clear bits */
vsaddr
->vs_csr0
|= (VS_IE
| (VS_START
<< VS_FCSHIFT
) | VS_GO
);
error
= tsleep((caddr_t
) vsp
, VSWAITPRI
| PCATCH
, devwait
, 0);
case VSIOABORT
: /* abort a command chain */
vsaddr
->vs_csr0
&= ~VS_FCN
;
vsaddr
->vs_csr0
|= (VS_IE
| (VS_ABORT
<< VS_FCSHIFT
) | VS_GO
);
error
= tsleep((caddr_t
) vsp
, VSWAITPRI
| PCATCH
, devwait
, 0);
case VSIOPWRUP
: /* power-up reset */
vsaddr
->vs_csr0
&= ~VS_FCN
;
vsaddr
->vs_csr0
|= (VS_IE
| (VS_PWRUP
<< VS_FCSHIFT
) | VS_GO
);
error
= tsleep((caddr_t
) vsp
, VSWAITPRI
| PCATCH
, devwait
, 0);
case VSIOBBACTL
: /* enable/disable BBA */
vsaddr
->vs_csr0
&= ~VS_FCN
;
func
= *(int *)addr
== VSIO_ON
? VS_ENABBA
: VS_DISBBA
;
vsaddr
->vs_csr0
|= (VS_IE
| (func
<< VS_FCSHIFT
) | VS_GO
);
error
= tsleep((caddr_t
) vsp
, VSWAITPRI
| PCATCH
, devwait
, 0);
case VSIOFIBCTL
: /* turn the fiber lamp on/off */
if (*(int *)addr
== VSIO_OFF
)
vsaddr
->vs_csr0
&= ~VS_XMIT_ON
;
vsaddr
->vs_csr0
|= (VS_IE
| VS_XMIT_ON
);
error
= tsleep((caddr_t
) vsp
, VSWAITPRI
| PCATCH
, devwait
, 0);
case VSIOFIBRETRY
: /* set fiber retries */
vsaddr
->vs_csr0
&= ~VS_FCN
;
func
= *(int *)addr
== VS_FIB_FINITE
? VS_FINITE
: VS_INFINITE
;
vsaddr
->vs_csr0
|= (VS_IE
| (func
<< VS_FCSHIFT
) | VS_GO
);
error
= tsleep((caddr_t
) vsp
, VSWAITPRI
| PCATCH
, devwait
, 0);
case VSIOSYNC
: /* get synchronized with device */
register struct vsdevice
*vsaddr
;
register struct vs_softc
*vsp
;
register struct vsBuffArea
*vsb
;
if (VSUNIT(dev
) >= NVS
|| (uip
= vsdinfo
[VSUNIT(dev
)]) == 0
printI("vs%d stray interrupt\n", VSUNIT(dev
));
vsaddr
= (struct vsdevice
*) uip
->ui_addr
;
vsp
= &vs_softc
[VSUNIT(dev
)];
vsb
= &vsBuff
[VSUNIT(dev
)];
printM("vsintr csr0=%x, csr1=%x, csr2=%x, csr3=%x, csr4=%x, csr5=%x, csr6=%x, csr7=%x\n",
vsaddr
->vs_csr0
, vsaddr
->vs_csr1
, vsaddr
->vs_csr2
, vsaddr
->vs_csr3
,
vsaddr
->vs_csr4
, vsaddr
->vs_csr5
, vsaddr
->vs_csr6
, vsaddr
->vs_csr7
);
printI("vs%dintr ", VSUNIT(dev
));
* get the information out of the soft registers
vsp
->irr
.intr_reg
= vsaddr
->vs_irr
;
vsp
->krr
.kbd_reg
= vsaddr
->vs_krr
;
vsp
->pr
.fparm_low
= vsaddr
->vs_pr1
;
vsp
->pr
.fparm_high
= vsaddr
->vs_pr2
;
vsp
->csr
.csr_reg
= vsaddr
->vs_csr0
;
if (vsp
->irr
.intr_reason
)
vsaddr
->vs_irr
= 0; /* clear int reason, if any */
vsaddr
->vs_csr0
&= ~VS_OWN
; /* clear owner bit */
if (vsp
->csr
.csr_linkTran
) {
vsaddr
->vs_csr0
&= ~VS_LNK_TRNS
; /* clear the bit */
printI("link transition: ");
if (vsp
->csr
.csr_linkErr
)
if (vsp
->csr
.csr_linkAvail
== vsp
->linkAvail
) { /* flash */
} else if (!vsp
->csr
.csr_linkAvail
&& vsp
->linkAvail
) { /* on -> off */
if (vsp
->open
&& vsp
->pgrp
)
gsignal(vsp
->pgrp
, SIGHUP
);
while ((vsaddr
->vs_csr0
& VS_LNK_TRNS
) && i
)
if (i
== 0) { /* bit stuck */
printI("vs%d: Link Transition bit stuck\n", VSUNIT(dev
));
if (vsp
->open
&& vsp
->pgrp
)
gsignal(vsp
->pgrp
, SIGHUP
);
vsaddr
->vs_csr0
&= ~VS_XMIT_ON
;
vsp
->csr
.csr_linkAvail
= FALSE
;
vsp
->linkAvail
= vsp
->csr
.csr_linkAvail
;
if (vsp
->irr
.intr_error
) {
printI("error 0x%x\n", vsp
->irr
.intr_reg
&0xffff);
/* set status and wake up user if necessary */
if (vsp
->vs_nextgo
.fparm_all
) {
vsp
->vs_status
= vsp
->irr
.intr_reg
;
vsaddr
->vs_pr1
= vsp
->vs_nextgo
.fparm_low
;
vsaddr
->vs_pr2
= vsp
->vs_nextgo
.fparm_high
;
vsp
->vs_nextgo
.fparm_all
= NULL
;
vsaddr
->vs_csr0
&= ~VS_FCN
; /* clear bits */
vsaddr
->vs_csr0
|= (VS_IE
| (VS_SEND
<< VS_FCSHIFT
) | VS_GO
);
vsb
->vsioa
.status
= vsp
->irr
.intr_reg
;
printI("reason is %b\n", vsp
->irr
.intr_reason
, VSIRR_BITS
);
switch(vsp
->irr
.intr_reason
) {
case VS_INT_CD
: /* command done */
/* set status and start a new command if necessary */
if (vsp
->vs_nextgo
.fparm_all
) {
vsp
->vs_status
= vsp
->irr
.intr_reg
;
vsaddr
->vs_pr1
= vsp
->vs_nextgo
.fparm_low
;
vsaddr
->vs_pr2
= vsp
->vs_nextgo
.fparm_high
;
vsp
->vs_nextgo
.fparm_all
= NULL
;
vsaddr
->vs_csr0
&= ~VS_FCN
; /* clear bits */
vsaddr
->vs_csr0
|= (VS_IE
| (VS_SEND
<< VS_FCSHIFT
) | VS_GO
);
vsb
->vsioa
.status
= vsp
->irr
.intr_reg
;
case VS_INT_MM
: /* mouse moved */
return; /* ignore on closed device */
/* no event if inside box */
if (cur
.y
< vsb
->vsioa
.mbox
.bottom
&&
cur
.y
>= vsb
->vsioa
.mbox
.top
&&
cur
.x
< vsb
->vsioa
.mbox
.right
&&
cur
.x
>= vsb
->vsioa
.mbox
.left
)
vsb
->vsioa
.mbox
.bottom
= 0;
if (EVROUND(vsb
->vsioa
.itail
+1) == vsb
->vsioa
.ihead
)
i
= EVROUND(vsb
->vsioa
.itail
-1);
if ((vsb
->vsioa
.itail
!= vsb
->vsioa
.ihead
) &&
(i
!= vsb
->vsioa
.ihead
)) {
if (vep
->vse_type
== VSE_MMOTION
) {
vep
->vse_time
= mfpr(TODR
);
/* put event into queue and do select */
vep
= &vsb
->ibuff
[vsb
->vsioa
.itail
];
vep
->vse_type
= VSE_MMOTION
;
vep
->vse_time
= mfpr(TODR
);
vsb
->vsioa
.itail
= EVROUND(vsb
->vsioa
.itail
+1);
case VS_INT_BE
: /* button event */
return; /* ignore on closed device */
if (vsp
->krr
.kbd_device
== VSE_MOUSE
) {
vsb
->vsioa
.mouse
.x
= cur
.x
;
vsb
->vsioa
.mouse
.y
= cur
.y
;
/* check for room in the queue */
if ((i
= EVROUND(vsb
->vsioa
.itail
+1)) == vsb
->vsioa
.ihead
)
/* put event into queue and do select */
vep
= &vsb
->ibuff
[vsb
->vsioa
.itail
];
vep
->vse_type
= VSE_BUTTON
;
vep
->vse_key
= vsp
->krr
.kbd_key
;
vep
->vse_direction
= vsp
->krr
.kbd_transition
;
vep
->vse_device
= vsp
->krr
.kbd_device
;
vep
->vse_time
= mfpr(TODR
);
vep
->vse_x
= vsb
->vsioa
.mouse
.x
;
vep
->vse_y
= vsb
->vsioa
.mouse
.y
;
case VS_INT_TM
: /* tablet moved */
return; /* ignore on closed device */
if (EVROUND(vsb
->vsioa
.itail
+1) == vsb
->vsioa
.ihead
)
i
= EVROUND(vsb
->vsioa
.itail
-1);
if ((vsb
->vsioa
.itail
!= vsb
->vsioa
.ihead
) &&
(i
!= vsb
->vsioa
.ihead
)) {
if (vep
->vse_type
== VSE_TMOTION
) {
vep
->vse_time
= mfpr(TODR
);
/* put event into queue and do select */
vep
= &vsb
->ibuff
[vsb
->vsioa
.itail
];
vep
->vse_type
= VSE_TMOTION
;
vep
->vse_time
= mfpr(TODR
);
vsb
->vsioa
.itail
= EVROUND(vsb
->vsioa
.itail
+1);
case VS_INT_US
: /* unsolicited */
case VS_INT_ID
: /* Initialization done */
/* save offset from device */
vsp
->offset
.fparm_all
= vsp
->pr
.fparm_all
;
case VS_INT_SE
: /* ucode started */
case VS_INT_PWR
: /* power up complete */
if (vsp
->open
&& vsp
->pgrp
)
gsignal(vsp
->pgrp
, SIGHUP
);
printI("vs%d: unknown interrupt %b\n", VSUNIT(dev
),
vsp
->irr
.intr_reason
, VSIRR_BITS
);
register struct uba_device
*uip
;
register struct vs_softc
*vsp
= vs_softc
;
for (i
= 0; i
< NVS
; i
++, vsp
++) {
if ((uip
= vsdinfo
[i
]) == 0 || uip
->ui_alive
== 0 ||
uip
->ui_ubanum
!= uban
|| vsp
->open
== 0)
if (vsp
->open
&& vsp
->pgrp
)
gsignal(vsp
->pgrp
, SIGHUP
);
register struct vsBuffArea
*vsb
= &vsBuff
[VSUNIT(dev
)];
if (vsb
->vsioa
.ihead
!= vsb
->vsioa
.itail
) {
vs_softc
[VSUNIT(dev
)].rsel
= u
.u_procp
;
return(0); /* can never write */
* Initialize VS100 or SBO.
* Set XMITON. VS100 will respond with link available. SBO won't, so
* don't wait forever; assume everything is OK and warn user.
struct vsdevice
*vsaddr
= (struct vsdevice
*) vsdinfo
[VSUNIT(dev
)]->ui_addr
;
register struct vs_softc
*vsp
= &vs_softc
[VSUNIT(dev
)];
vsaddr
->vs_csr0
|= (VS_IE
| VS_XMIT_ON
); /* turn link on */
error
= tsleep((caddr_t
) vsp
, VSWAITPRI
, SLP_VS_INITF
, 2*hz
);
if (error
== EWOULDBLOCK
) /* timeout */
uprintf("\007This had better be a vs125!\n");
printf("vs%d must be a vs125\n", VSUNIT(dev
));
register struct vsdevice
*vsaddr
;
register struct vs_softc
*vsp
;
vsaddr
= (struct vsdevice
*) vsdinfo
[VSUNIT(dev
)]->ui_addr
;
vsp
= &vs_softc
[VSUNIT(dev
)];
if (error
= vsInitFiber(dev
))
vsaddr
->vs_csr0
&= ~VS_FCN
;
vsaddr
->vs_csr0
|= (VS_IE
| (VS_INIT
<< VS_FCSHIFT
) | VS_GO
);
error
= tsleep((caddr_t
) vsp
, VSWAITPRI
| PCATCH
,
devwait
, retry
? 10*hz
: 0);
if (error
== EWOULDBLOCK
)
printM("vs%d: VS_INIT fails\n", VSUNIT(dev
));
uprintf("vsInitDev %x %x\n",vsaddr
->vs_csr0
, vsaddr
->vs_csr1
);
register struct vs_softc
*vsp
;
if (vsp
->irr
.intr_error
) {
register int ret
= vsp
->irr
.intr_reg
;
printD("\treturning 0x%x\n", ret
);