4.3BSD-Reno/src/sys/tahoestand/vdformat/io.c
#ifndef lint
static char sccsid[] = "@(#)io.c 1.7 (Berkeley/CCI) 6/7/88";
#endif
#include "vdfmt.h"
#include "cmd.h"
/*
**
*/
static cmd_text_element nul_table[] = {
{ 0, "", "" }
};
int wait_for_char;
int vdtimeout;
char *clean_up = "Cleaning up... Please wait.\n";
/*
**
*/
poll(wait)
int wait;
{
register struct vddevice *addr = C_INFO->addr;
int tokens[10];
if (kill_processes == false)
wait_for_char = 0;
vdtimeout = wait*1000;
for (;;) {
uncache(&(dcb.operrsta));
if (dcb.operrsta & (DCBS_DONE | DCBS_ABORT))
break;
if (kill_processes == false && input()) {
get_text_cmd(nul_table, tokens);
if (kill_processes == true) {
indent();
print(clean_up);
exdent(1);
}
}
if (vdtimeout-- <= 0) {
if(C_INFO->type == VDTYPE_VDDC)
printf("\nVDDC");
else
printf("\nSMD-E");
printf(": Controller timeout");
abort:
VDABORT(addr, C_INFO->type);
DELAY(30000);
break;
}
DELAY(1000);
}
if ((vdtimeout > 0)) {
if (C_INFO->type == VDTYPE_SMDE) {
for (;;) {
uncache(&(addr->vdcsr));
if ((addr->vdcsr & CS_GO) == 0)
break;
DELAY(1000);
if (vdtimeout-- <= 0) {
printf("\nSMD-E timed out clearing GO");
goto abort;
}
}
DELAY(300);
}
DELAY(500);
}
DELAY(200);
if((dcb.opcode == VDOP_RD) || (dcb.opcode == VDOP_RDRAW))
mtpr(PADC, 0);
uncache(&(dcb.operrsta));
uncache(&(dcb.err_code));
wait_for_char = 1;
}
/*
** Access_with_no_trailer is used to perform controller functions which
** require no data movement.
*/
access_with_no_trailer(function, wait_time)
int function, wait_time;
{
dcb.opcode = function; /* command */
dcb.intflg = DCBINT_NONE;
dcb.nxtdcb = (struct dcb *)0; /* end of chain */
dcb.operrsta = 0;
dcb.devselect = (function == VDOP_START) ? 0 :
((char)cur.drive | lab->d_devflags);
dcb.trailcnt = (char)0;
mdcb.mdcb_head = &dcb;
mdcb.mdcb_status = 0;
VDGO(C_INFO->addr, (u_long)&mdcb, C_INFO->type);
poll(wait_time);
if(vdtimeout <= 0) {
printf(" during startup operation.\n");
_longjmp(abort_environ, 1);
}
return dcb.operrsta;
}
vread(sn, buf, seccnt)
int sn, seccnt;
char *buf;
{
int ret;
ret = vrdwr(sn, buf, seccnt, VDOP_RD);
if (ret == 0)
vd_error("read");
return (ret);
}
vwrite(sn, buf, seccnt)
int sn, seccnt;
char *buf;
{
int ret;
ret = vrdwr(sn, buf, seccnt, VDOP_WD);
if (ret == 0)
vd_error("write");
return (ret);
};
vrdwr(sn, buf, seccnt, op)
int sn, seccnt, op;
char *buf;
{
dskadr dskaddr;
dskaddr.cylinder = sn / lab->d_secpercyl;
sn %= lab->d_secpercyl;
dskaddr.track = sn / lab->d_nsectors;
dskaddr.sector = sn % lab->d_nsectors;
if (access_dsk(buf, &dskaddr, op, seccnt, 1) & DCBS_HARD)
return (0);
return (seccnt);
}
/*
** access_dsk is used by other routines to do reads and writes to the disk.
** The status of the read / write is returned to the caller for processing.
*/
access_dsk(buf, dskaddr, func, count, wait)
char *buf;
dskadr *dskaddr;
int func, count, wait;
{
cur.daddr.cylinder = dskaddr->cylinder;
cur.daddr.track = dskaddr->track;
wait_for_char = 0;
dcb.opcode = func; /* format sector command */
dcb.intflg = DCBINT_NONE;
dcb.nxtdcb = (struct dcb *)0; /* end of chain */
dcb.operrsta = 0;
dcb.devselect = (char)cur.drive | lab->d_devflags;
if(func == VDOP_SEEK) {
dcb.trailcnt = (char)(sizeof(struct trseek) / sizeof(long));
dcb.trail.sktrail.skaddr.cylinder = dskaddr->cylinder;
dcb.trail.sktrail.skaddr.track = dskaddr->track;
dcb.trail.sktrail.skaddr.sector = dskaddr->sector;
} else {
dcb.trailcnt = (char)(sizeof(struct trrw) / sizeof(long));
dcb.trail.rwtrail.memadr = (u_long)buf;
dcb.trail.rwtrail.wcount=count*(lab->d_secsize/sizeof(short));
dcb.trail.rwtrail.disk.cylinder = dskaddr->cylinder;
dcb.trail.rwtrail.disk.track = dskaddr->track;
dcb.trail.rwtrail.disk.sector = dskaddr->sector;
}
mdcb.mdcb_head = &dcb;
mdcb.mdcb_status = 0;
VDGO(C_INFO->addr, (u_long)&mdcb, C_INFO->type);
if(wait) {
poll(10);
if(vdtimeout <= 0) {
printf(" in access_dsk.\n");
_longjmp(abort_environ, 1);
}
}
wait_for_char = 1;
return dcb.operrsta;
}
/*
** Spin_up_drive starts the drives on a controller and waits around for
** the drive to spin up if it is not already spinning.
*/
spin_up_drive()
{
register struct vddevice *addr = C_INFO->addr;
VDRESET(addr, C_INFO->type);
if(C_INFO->type == VDTYPE_SMDE) {
addr->vdcsr = 0;
addr->vdtcf_mdcb = AM_ENPDA;
addr->vdtcf_dcb = AM_ENPDA;
addr->vdtcf_trail = AM_ENPDA;
addr->vdtcf_data = AM_ENPDA;
addr->vdccf = CCF_SEN | 0x8 | CCF_STS |
XMD_32BIT | BSZ_16WRD | CCF_ERR |
CCF_ENP | CCF_EPE | CCF_EDE | CCF_ECE;
}
access_with_no_trailer(VDOP_INIT, 10);
access_with_no_trailer(VDOP_DIAG, 20);
configure_drive(0);
}
/*
** Configure_drive tells the controller what kind of drive is attached
** on a particular line.
*/
configure_drive(pass)
int pass;
{
register struct vddevice *addr = C_INFO->addr;
register i;
top:
dcb.opcode = VDOP_CONFIG; /* command */
dcb.intflg = DCBINT_NONE;
dcb.nxtdcb = (struct dcb *)0; /* end of chain */
dcb.operrsta = 0;
dcb.devselect = cur.drive | lab->d_devflags;
dcb.trail.rstrail.ncyl = lab->d_ncylinders;
dcb.trail.rstrail.nsurfaces = lab->d_ntracks;
if(C_INFO->type == VDTYPE_VDDC)
dcb.trailcnt = (char)2;
else {
dcb.trailcnt = sizeof (struct treset)/sizeof (long);
dcb.trail.rstrail.nsectors = lab->d_nsectors;
dcb.trail.rstrail.slip_sec = lab->d_sparespertrack;
dcb.trail.rstrail.recovery = VDRF_NONE;
addr->vdcylskew = lab->d_cylskew;
addr->vdtrackskew = lab->d_trackskew;
addr->vdsecsize = lab->d_secsize/sizeof(short);
}
mdcb.mdcb_head = &dcb;
mdcb.mdcb_status = 0;
VDGO(addr, (u_long)&mdcb, C_INFO->type);
poll(5);
if(vdtimeout <= 0) {
printf(" during drive configuration.\n");
goto bad;
}
if(dcb.operrsta & VDERR_HARD) {
if (C_INFO->type == VDTYPE_SMDE) {
if (lab->d_devflags == 0) {
lab->d_devflags = VD_ESDI;
goto top;
}
#ifdef notdef
printf("vdstatus %x\n", addr->vdstatus[cur.drive]);
if ((addr->vdstatus[cur.drive] & STA_US) == 0) {
printf("Drive not present\n\n");
goto bad;
}
#endif
}
if ((dcb.operrsta & (DCBS_OCYL|DCBS_NRDY)) == 0) {
printf("drive config error\n");
goto bad;
}
if(pass) {
printf("\nDrive failed to start!\n\n");
goto bad;
}
printf("\ndrive not ready, attempting to spin up...");
access_with_no_trailer(VDOP_START, 62);
for (i = 0; i < 620; i++) {
if (C_INFO->type == VDTYPE_SMDE &&
addr->vdstatus[cur.drive] & STA_UR)
break;
DELAY(100000);
}
printf(" retrying drive configuration\n");
pass++;
lab->d_devflags = 0;
goto top;
}
D_INFO->alive = u_true;
return;
bad:
D_INFO->alive = u_false;
_longjmp(abort_environ, -1);
}
/*
** data_ok checks an error status word for bit patterns
** associated with error conditions from the VDDC controller. If a hardware
** error is present then the problem is reported on the console.
** If a data error is present the a zero is returned.
** If everything is OK then a 1 is returned.
*/
data_ok()
{
register int status = dcb.operrsta;
if(status & HARD_ERROR){
vd_error("data transfer");
printf(" op = 0x%x\n", dcb.opcode);
reset_controller();
dcb.operrsta &= HEADER_ERROR;
}
return (int)(!(status & (DATA_ERROR | HEADER_ERROR)));
}
vd_error(s)
char *s;
{
register int status = dcb.operrsta;
print("%s error at sector %d (cyl %d trk %d sect %d),\n",
s, to_sector(cur.daddr), dcb.err_cyl & 0xfff, dcb.err_trk,
dcb.err_sec);
print(" status=%b", dcb.operrsta, VDERRBITS);
if (C_INFO->type == VDTYPE_SMDE)
printf(", ecode=0x%x", dcb.err_code);
printf(".\n");
}
/*
**
*/
reset_controller()
{
printf("Resetting controller. Please wait...\n");
spin_up_drive();
printf("Controller was reset successfully.\n");
}
/*
**
*/
static int indent_count;
/*
**
*/
indent()
{
indent_count += 2;
}
/*
**
*/
exdent(count)
int count;
{
if(count == -1)
indent_count = 0;
else
indent_count -= count * 2;
if(indent_count < 0)
indent_count = 0;
}
/*
**
*/
/*VARARGS1*/
print(par0, par1, par2, par3, par4, par5, par6)
char *par0, *par1, *par2, *par3, *par4, *par5, *par6;
{
register int count = indent_count;
while(count--)
printf(" ");
printf(par0, par1, par2, par3, par4, par5, par6);
DELAY((strlen(par0) + 20) * 9000);
}