/* scat_vddc.c 1.2 86/01/05 */
** VDDC Driver - Versabus to SMD direct interface version.
** Written for TAHOE vmunix, CCI-WDC 9/1/83.
** Modified June 1984 to use scatter/gather.
#include "../tahoe/mtpr.h"
#include "../tahoe/pte.h"
#include "../tahoevba/vbavar.h"
#include "../tahoevba/vddc.h"
int vddebug
= 1; /* if = 1, error messages are printed on the console */
int vdintflg
= 0; /* if = 1, interrupts are handled by the driver,
* otherwise they are just ignored. (during setup) */
static struct size FSD
[] = {
9600, 0, /* minor 0/ 8/16/24 = fsd0a - fsd3a - cyl 0 - 59*/
12000, 9600, /* minor 1/ 9/17/25 = fsd0b - fsd3b - cyl 60 - 134*/
108480, 21600, /* minor 2/10/18/26 = fsd0c - fsd3c - cyl 135 - 812*/
1600, 130080, /* minor 3/11/19/27 = fsd0d - fsd3d - cyl 813 - 822*/
130080, 0, /* minor 4/12/20/28 = fsd0e - fsd3e - cyl 0 - 812*/
131680, 0, /* minor 5/13/21/29 = fsd0f - fsd3f - cyl 0 - 822*/
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
static struct size SMD
[]= {
20064, 0, /* minor 32/40/48/56 = smd0a - smd3a cyl 0- 65 */
13680, 20064, /* minor 33/41/49/57 = smd0b - smd3b cyl 66- 110 */
214928, 33744, /* minor 34/42/50/58 = smd0c - smd3c cyl 111-817 */
1520, 248672, /* minor 35/43/51/59 = smd0d - smd3d cyl 818-822 */
248672, 0, /* minor 36/44/52/60 = smd0e - smd3e cyl 0-817 */
250192, 0, /* minor 37/45/53/61 = smd0f - smd3f cyl 0-822 */
0, 0, /* minor 38/46/54/62 = smd0g - smd3g */
0, 0, /* minor 39/47/55/63 = smd0h - smd3h */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
static struct size XFSD
[] = {
20352, 0, /* minor 64/72/80/88 = xfsd0a - xfsd3a cyl 0- 52 */
20352, 20352, /* minor 65/73/81/89 = xfsd0b - xfsd3b cyl 53- 105 */
230400, 40704, /* minor 66/74/82/90 = xfsd0c - xfsd3c cyl 106-705 */
1920, 271104, /* minor 67/75/83/91 = xfsd0d - xfsd3d cyl 706-710 */
271104, 0, /* minor 68/76/84/92 = xfsd0e - xfsd3e cyl 0-705 */
273024, 0, /* minor 69/77/85/93 = xfsd0f - xfsd3f cyl 0-710 */
0, 0, /* minor 70/78/86/94 = xfsd0g - xfsd3g */
0, 0, /* minor 71/79/87/95 = xfsd0h - xfsd3h */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
0, 0, /* Non existent minor device */
/* Layout of major/minor number assignments for the VDDC devices.
/* +----------------+------+---+-----+
/* | Major device # | TYPE | D | FLS |
/* +----------------+------+---+-----+
/* | | |_____ File system # ( 0-7 )
/* | |__________ Drive # (0-3)
/* |________________ Drive type ( 0-FSD, 1-SMD )
/********************************************************/
#define VDUNIT(x) ((minor(x) & 0x18) >> 3)
#define DRVTYPE(x) ((minor(x) & 0xe0) >> 5) (obsolete)
#define FLSYS(x) (minor(x) & 0x07)
#define PHYS(x) ( vtoph( &proc[2], (int) (x) ) )
/* Drive types should be in order of drive capacity for auto-configuration */
/* e.g: smallest capacity = drive type 0, highest capacity = type NXPDRV-1 */
short dtype
; /* type as in byte 5 (drive) of iopb */
char *name
; /* drive name for autoconf */
16, 10, 16*10, 823, FSD
, 0, "160 Mb FSD " ,
16, 19, 16*19, 823, SMD
, 1, "300 Mb SMD " ,
16, 24, 16*24, 711, XFSD
, 2, "340 Mb FSD "
struct vba_ctlr
*vdminfo
[NVDDC
];
struct vba_device
*vddinfo
[NUNIT
];
int vdprobe(); /* See if VDDC is really there */
int vd_setup(); /* Called from vdprobe */
int vdslave(); /* See if drive is really there */
int vdstrategy(); /* VDDC strategy routine */
int vdstart(); /* Top level interface to device queue */
int vdintr(); /* Top Level ISR */
int vdread(); /* raw i/o read routine */
int vdwrite(); /* raw i/o write routine */
int vddump(); /* dump routine */
int vdsize(); /* sizes for swapconfig */
struct vba_driver vddriver
=
vdprobe
, vdslave
, vdattach
, vddgo
, vdstd
,
"smd/fsd", vddinfo
, "VSMD controller ", vdminfo
struct buf vdutab
[NUNIT
];
struct buf rvdbuf
[NUNIT
];
char track
; /* all 8 bits */
char sector
; /* low order 5 bits */
short cylinder
; /* low order 12 bits */
**********************************/
char *memadr
; /* memory address */
long wcount
; /* 16 bit word count */
struct dskadr disk
; /* disk address */
long scat
[MAXBPTE
*2+1]; /* gather/scatter trailer */
char *addr
; /* data buffer to be filled on sector*/
long nsectors
; /* # of sectors to be formatted */
** Reset / Configure Trailer
long ncyl
; /* # cylinders */
long nsurfaces
; /* # surfaces */
}; /* # of sectors is defined by VDDC */
/* to be 32/track of 512 data bytes each */
struct fmt_dcb
*nxtdcb
; /* next dcb in chain or End of Chain */
short intflg
; /* interrupt settings and flags */
short opcode
; /* DCB Command code etc... */
long operrsta
; /* Error & Status info */
short fill
; /* not used */
char devselect
; /* Drive selection */
char trailcnt
; /* Trailer Word Count */
long err_memadr
; /* Error memory address */
short err_wcount
; /* Error word count */
short err_track
; /* Error track/sector */
short err_cyl
; /* Error cylinder adr */
struct trrw rwtrail
; /* read/write trailer */
struct trfmt fmtrail
; /* format trailer */
struct treset resetrail
; /* reset/configure trailer */
struct trseek seektrail
; /* seek trailer */
struct fmt_dcb
*firstdcb
; /* first dcb in chain */
struct fmt_dcb
*procdcb
; /* dcb being processed */
struct fmt_dcb
*intdcb
; /* dcb causing interrupt */
long vddcstat
; /* VDDC status */
struct fmt_mdcb
*mdcb
= &mdcbx
;
struct fmt_dcb dcbx
[NVDDC
];
#define POLLTILLDONE(x) { vdtimeout = 1000*(x); \
uncache((char *)&dcb->operrsta); \
while (! (dcb->operrsta & DCBCMP)) { \
uncache((char *)&dcb->operrsta); \
printf("VDDC controller timeout\n"); \
** See if the controller is really there.
** if TRUE - initialize the controller.
if ( badaddr(cntrl_vaddr
,2) ) return(0); /* no controller */
if (vd_setup(cntrl_vaddr
)) /* initialize the controller */
else return(0); /* initialization error */
register struct fmt_dcb
*dcb
= &dcbx
[0];
VDDC_RESET(cntrl_vaddr
); /* Reset the controller */
/* Burn some time ...... needed after accessing reset port */
/* setup & issue INIT to initialize VDDC */
dcb
->nxtdcb
= (struct fmt_dcb
*)0;
mdcb
->firstdcb
= (struct fmt_dcb
*)PHYS(dcb
);
VDDC_ATTENTION(cntrl_vaddr
,PHYS(mdcb
) ) /* do it */
POLLTILLDONE(1) /* poll till done */
if (dcb
->operrsta
& HRDERR
) {
printf("VDDC INIT error. Status = %x\n",dcb
->operrsta
);
/* setup & issue DIAGNOSE */
dcb
->nxtdcb
= (struct fmt_dcb
*)0;
mdcb
->firstdcb
= (struct fmt_dcb
*)PHYS(dcb
);
VDDC_ATTENTION(cntrl_vaddr
,PHYS(mdcb
) ) /* do it */
POLLTILLDONE(1) /* poll till done */
if (dcb
->operrsta
& HRDERR
) {
printf("VDDC DIAGNOSE error. Status = %x\n",dcb
->operrsta
);
/* Start drives command */
/* dcb->opcode = VDSTART;
/* dcb->nxtdcb = (struct fmt_dcb *)0;
/* mdcb->firstdcb = (struct fmt_dcb *)PHYS(dcb);
/* VDDC_ATTENTION(cntrl_vaddr,PHYS(mdcb) ) /* do it */
/* POLLTILLDONE(20) /* poll till done */
/* if (dcb->operrsta & HRDERR) {
/* printf("VDDC START DRIVES error. Status = %x\n",dcb->operrsta);
* See if a drive is really there
* Try to Reset/Configure the drive, then test its status.
register struct vba_device
*ui
;
register caddr_t cntrl_vaddr
;
register struct fmt_dcb
*dcb
= &dcbx
[0];
register struct vdst
*st
;
** check drive status - see if drive exists.
dcb
->devselect
= (char)ui
->ui_slave
;
mdcb
->firstdcb
= (struct fmt_dcb
*)PHYS(dcb
);
VDDC_ATTENTION(cntrl_vaddr
,PHYS(mdcb
)) /* do it */
if (dcb->operrsta & HRDERR) {
printf("VDDC STATUS error. Status = %x, drive %d\n",dcb->operrsta,ui->ui_slave);
uncache((char *)&mdcb
->vddcstat
);
if (mdcb
->vddcstat
& DRVNRDY
) return(0); /* not ready-> non existent */
* drive is alive, now get its type!
* Seek on all drive types starting from the largest one.
* a sucessful seek to the last sector/cylinder/track verifies
* the drive type connected to this port.
for (dsktype
= NVDDRV
-1; dsktype
>= 0; dsktype
--) {
dcb
->opcode
= RSTCFG
; /* configure drive command */
dcb
->trail
.resetrail
.ncyl
= st
->ncyl
;
dcb
->trail
.resetrail
.nsurfaces
= st
->ntrak
;
dcb
->devselect
= (char)ui
->ui_slave
;
mdcb
->firstdcb
= (struct fmt_dcb
*)PHYS(dcb
);
VDDC_ATTENTION(cntrl_vaddr
,PHYS(mdcb
) ) /* do it */
if (dcb
->operrsta
& HRDERR
) {
printf("VDDC RESET/CONFIGURE error. Status = %x\n",dcb
->operrsta
);
mdcb
->firstdcb
= (struct fmt_dcb
*)PHYS(dcb
);
dcb
->devselect
= (char)ui
->ui_slave
;
dcb
->trail
.rwtrail
.memadr
= (char *)PHYS(vdbuf
);
dcb
->trail
.rwtrail
.wcount
= SECTSIZ
;
dcb
->trail
.rwtrail
.disk
.cylinder
= st
->ncyl
-2;
dcb
->trail
.rwtrail
.disk
.track
= st
->ntrak
-1;
dcb
->trail
.rwtrail
.disk
.sector
= 0;
VDDC_ATTENTION(cntrl_vaddr
,PHYS(mdcb
) ) /* do it */
if ( (dcb
->operrsta
& HRDERR
) == 0)
/* found the drive type! */
/* If reached here, a drive which is not defined in the
* 'vdst' tables is connected. Cannot set it's type.
printf("VDDC error, unrecognized drive type, drive %d\n",ui
->ui_slave
);
vddriver
.ud_dname
= st
->name
;
register struct vba_device
*ui
;
register struct vba_ctlr
*um
;
register struct size
*sizep
;
vdintflg
= 1; /* enable interrupts handling by the driver */
blocks
= (bp
->b_bcount
+ DEV_BSIZE
- 1) >> DEV_BSHIFT
;
if (bp
->b_bcount
> NBPG
*MAXBPTE
) {
printf ("VDDC I/O length error: %d\n", bp
->b_bcount
);
unit
= VDUNIT(bp
->b_dev
);
if (ui
== 0 || ui
->ui_alive
== 0) goto bad1
;
index
= FLSYS(bp
->b_dev
); /* get file system index */
sizep
= vdst
[ui
->ui_type
].sizes
;
(dkblock(bp
)+blocks
> sizep
[index
].nblocks
)) /* disk overflow */
dp
= &vdutab
[ui
->ui_unit
];
bp
->b_resid
= bp
->b_blkno
; /* block # plays same role as
cylinder # for disksort, as long
as increasing blocks correspond to
increasing cylinders on disk */
if (dp
->b_active
== 0) { /* unit is on controller queue */
/* put the device on the controller queue */
dp
->b_forw
= NULL
; /* end of queue indicator */
um
= ui
->ui_mi
; /* get controller structure !! */
if (um
->um_tab
.b_actf
== NULL
) /* controller queue is empty */
um
->um_tab
.b_actl
->b_forw
= dp
; /* add into queue */
um
->um_tab
.b_actl
= dp
; /* update queue tail */
bp
= &ui
->ui_mi
->um_tab
; /* controller structure addr */
if (bp
->b_actf
&& /* cntrl queue not empty */
bp
->b_active
== 0) /* controller not active */
(void) vdstart(ui
->ui_mi
);/* go start I/O */
* Start up a transfer on a drive.
register struct vba_ctlr
*um
;
register struct buf
*bp
, *dp
;
register struct fmt_dcb
*dcb
= &dcbx
[um
->um_ctlr
];
struct size
*sizep
; /* Pointer to one of the tables */
register struct vdst
*st
;
register int index
; /* Index in the relevant table */
register int phadr
; /* Buffer's physical address */
register caddr_t cntrl_vaddr
= um
->um_addr
;
register struct proc
*this_proc
;
register long phaddr
, vaddr
, length
, i
;
* Pull a request off the controller queue
if ((dp
= um
->um_tab
.b_actf
) == NULL
)
if ((bp
= dp
->b_actf
) == NULL
) {
dp
->b_active
= 0; /* device removed from ctlr queue */
um
->um_tab
.b_actf
= dp
->b_forw
;
* Mark controller busy, and
* prepare a command packet for the controller.
unit
= VDUNIT(bp
->b_dev
);
st
= &vdst
[vddinfo
[unit
]->ui_type
];
index
= FLSYS(bp
->b_dev
);
mdcb
->firstdcb
= (struct fmt_dcb
*)PHYS(dcb
);
dcb
->intflg
= INTDUN
; /* interrupt on completion */
dcb
->devselect
= VDUNIT(bp
->b_dev
);
sblock
= sizep
[index
].block0
+ bp
->b_blkno
;
dcb
->trail
.rwtrail
.disk
.cylinder
= (short)(sblock
/ st
->nspc
);
dcb
->trail
.rwtrail
.disk
.track
=(char)((sblock
% st
->nspc
) / st
->nsect
);
dcb
->trail
.rwtrail
.disk
.sector
= (char)(sblock
*2 % (st
->nsect
*2));
if (bp
->b_flags
& B_DIRTY
|| bp
->b_proc
==0) this_proc
= &proc
[2];
else this_proc
= bp
->b_proc
;
phaddr
= vtoph(this_proc
, bp
->b_un
.b_addr
); /* start addresses */
vaddr
= (int)bp
->b_un
.b_addr
;
length
= (bp
->b_bcount
+1) & ~1; /* total # of bytes */
printf("\nvaddr=%x length=%x\n", vaddr
, length
);
dcb
->trail
.rwtrail
.memadr
= (char *)phaddr
; /* default trailer */
printf("%x ", dcb
->trail
.rwtrail
.memadr
);
i
= imin ( NBPG
-(phaddr
&PGOFSET
),length
); /* bytes in this page */
dcb
->trail
.rwtrail
.wcount
= i
/2;
if (i
!= 0x400) printf("/%x ", i
/2);
* If all required bytes fit into one page frame, that's it.
* Otherwise we have to generate a scatter/gather trailer.
longp
= dcb
->trail
.rwtrail
.scat
; /* 1'st pair address */
i
= imin ( NBPG
-(phaddr
&PGOFSET
),length
);
*longp
++ = vtoph (this_proc
, vaddr
);
*longp
++ = 0; /* End of list */
*longp
= 0; /* End of list */
if (bp
->b_flags
& B_READ
) { /* Read or read&scatter */
if (dcb
->trailcnt
== 3) dcb
->opcode
= RD
;
} else { /* Write or gather&write */
if (dcb
->trailcnt
== 3) dcb
->opcode
= WD
;
while (longp
>= (long *)dcb
)
printf("%x\n", *longp
--);
VDDC_ATTENTION(cntrl_vaddr
,PHYS(mdcb
)) /* do it */
* Handle a disk interrupt.
register struct buf
*bp
, *dp
;
register struct vba_ctlr
*um
= vdminfo
[vdnum
];
register struct fmt_dcb
*dcb
= &dcbx
[vdnum
];
if (intenable
== 0 || vdintflg
== 0) /* ignore all interrupts */
if (um
->um_tab
.b_active
== NULL
) return;/* unexpected interrupt */
uncache((char *)&mdcb
->intdcb
);
uncache((char *)&dcb
->operrsta
);
if ( mdcb
->intdcb
!= (struct fmt_dcb
*)PHYS(dcb
)) { /* dcb causing interrupt */
printf("VDDC error - dcb causing interrupt (%x) is different from expected dcb (%x) Interrupt ignored\n", mdcb
->intdcb
,dcb
);
if (! (dcb
->operrsta
& DCBCMP
)) { /* unexpected interrupt */
printf("VDDC Unexpected interrupt, DCB completed not set. Status = %x\n",dcb
->operrsta
);
dp
= um
->um_tab
.b_actf
; /* device queue head in ctlr queue */
bp
= dp
->b_actf
; /* first buffer in device queue */
if (dcb
->operrsta
& HRDERR
) {
printf("VDDC hard error - dcb status = %x\n",dcb
->operrsta
);
else if (dcb
->operrsta
& SFTERR
)
printf("Soft error on VDDC,status = %x, dev=%x, block # %d\n",
dcb
->operrsta
, bp
->b_dev
, bp
->b_blkno
);
* If this was a read, we have to purge the data cache for the
* rigth virtual pages. It could be nice to just change the
* relevant process's data key but this key is in PCB, in _u,
* which can be on the disk right now. And besides, what about
* reads into the system space? There's no key to change there.
if (bp
->b_flags
& B_READ
) {
if (dcb
->opcode
== RAS
) { /* Multiple pages read */
for (cnt
=bp
->b_bcount
; cnt
>=0; cnt
-= NBPG
)
mtpr ((caddr_t
)bp
->b_un
.b_addr
+cnt
-1, P1DC
);
if ( ((int)bp
->b_un
.b_addr
& PGOFSET
) != 0 )
mtpr ((caddr_t
)bp
->b_un
.b_addr
, P1DC
);
} else { /* Only one page read */
mtpr ((caddr_t
)bp
->b_un
.b_addr
, P1DC
);
if (dp
->b_forw
!= NULL
) { /* more than 1 unit on queue */
um
->um_tab
.b_actf
= dp
->b_forw
; /* next device on ctlr queue */
dp
->b_forw
= um
->um_tab
.b_actl
->b_forw
; /* be last in queue */
um
->um_tab
.b_actl
->b_forw
= dp
; /* last points now to dp */
um
->um_tab
.b_actl
= dp
; /* pointer in ctlr structure */
dp
->b_actf
= bp
->av_forw
; /* remove first from queue */
bp
->b_resid
= 0; /* All data read here */
vdstart(um
); /* start requests for next device on queue */
register int unit
= VDUNIT(dev
);
if (unit
>= NUNIT
) return ( ENXIO
);
(physio(vdstrategy
, &rvdbuf
[unit
], dev
, B_READ
, minphys
, uio
));
register int unit
= VDUNIT(dev
);
if (unit
>= NUNIT
) return (ENXIO
);
(physio(vdstrategy
, &rvdbuf
[unit
], dev
, B_WRITE
,minphys
, uio
));
#define DUMPSIZE 32 /* Up to 32k at a time - controller limit */
* Dump the main memory to the given device.
register struct vba_ctlr
*um
;
register struct fmt_dcb
*dcb
= &dcbx
[0];
register struct vdst
*st
;
register caddr_t cntrl_vaddr
;
register struct size
*sizep
; /* Pointer to one of the tables */
int index
,sblock
,blkcount
,thiscount
;
um
= (vddinfo
[unit
])->ui_mi
;
st
= &vdst
[(vddinfo
[unit
])->ui_type
];
dcb
= &dcbx
[um
->um_ctlr
];
cntrl_vaddr
= um
->um_addr
;
blkcount
= maxfree
- 2; /* In 1k byte pages */
if (dumplo
+ blkcount
> sizep
[index
].nblocks
) return(EINVAL
);
sblock
= sizep
[index
].block0
+ dumplo
;
thiscount
= MIN (blkcount
, DUMPSIZE
);
mdcb
->firstdcb
= (struct fmt_dcb
*)PHYS(dcb
);
dcb
->trail
.rwtrail
.memadr
= (char *)memaddr
;
dcb
->trail
.rwtrail
.wcount
= thiscount
*512;
dcb
->trail
.rwtrail
.disk
.cylinder
= (short)(sblock
/st
->nspc
);
dcb
->trail
.rwtrail
.disk
.track
= (char)((sblock
% st
->nspc
)
dcb
->trail
.rwtrail
.disk
.sector
= (char)(sblock
*2 % (st
->nsect
*2));
VDDC_ATTENTION(cntrl_vaddr
,PHYS(mdcb
) ) /* do it */
if (dcb
->operrsta
& HRDERR
) {
printf("VDDC DUMP error. Status = %x\n",
memaddr
+= thiscount
*NBPG
;
register struct vba_device
*ui
;
register unit
= VDUNIT(dev
);
if (ui
== 0 || ui
->ui_alive
== 0 || ui
->ui_type
>= NVDDRV
)
register struct vba_device
*ui
;
register unit
= VDUNIT(dev
);
if (ui
== 0 || ui
->ui_alive
== 0 || ui
->ui_type
>= NVDDRV
)
return vdst
[ui
->ui_type
].sizes
[FLSYS(dev
)].nblocks
;