/*#define NOERROR */#define MOREASM/* * SY6522 disk driver */#include "sys/param.h"#include "sys/config.h"#include "sys/mmu.h"#include "sys/types.h"#include "sys/sysmacros.h"#include "sys/dir.h"#include "sys/signal.h"#include "sys/user.h"#include "sys/errno.h"#include "sys/utsname.h"#include "sys/buf.h"#include "sys/elog.h"#include "sys/erec.h"#include "sys/iobuf.h"#include "sys/systm.h"#include "sys/var.h"#include "sys/ttold.h"#include "setjmp.h"#include "sys/profile.h"#include "sys/pport.h"#include "sys/d_profile.h"#include "sys/cops.h"#include "sys/swapsz.h"#ifdef notdef		/* these are in d_profile.h */#define logical(x)	(minor(x) & 7)		/* eight logicals per phys */#define interleave(x)	(minor(x) & 0x8)	/* interleave bit for swaping */#define physical(x)	((minor(x) & 0xF0) >> 4)/* 10 physical devs */#endifchar pro_secmap[NSEC] = {/*11*/	0, 15, 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1};	/* Logical Units */	/* The first 100 blocks are reserved for the boot program and	   are inaccessible via unix.	 */#define	MAXBOOT	100struct prlmap {	daddr_t	pm_beg;		/* Base address in blocks */	daddr_t	pm_len;		/* Number of blocks in logical device */} prlmap[] = {/* a */	{PRNSWAP+101,	16955},	/* root filesystem on 10 Meg. disk *//* b */	{101,	PRNSWAP},	/* swap area (2400 blocks) *//* c */	{PRNSWAP+101,	7227},	/* root filesystem on 5 Meg. disk *//* d */	{9728,	9728},		/* 2nd filesystem on 10 Meg. disk *//* e */	{0,	0},		/* unused *//* f */	{0,	7168},		/* old root filesystem (old a) *//* g */	{7168,	2496},		/* old swap (old b) *//* h */	{101,	19355},		/* f.s. using entire 10 Meg. disk */};/* THESE MAY REPLACE f AND g ABOVE *//* f 	{4101,	15355},		/* alternate root f.s. on 10 Meg. disk *//* g 	{101,	4000},		/* alternate swap */struct iostat prostat[NPPDEVS];struct iobuf protab = tabinit(PR0,prostat);	/* active buffer header */struct buf rprobuf;				/* Raw input-output buffer */struct proheader rphbuf;struct proheader phbuf;#ifndef NOERROR#define ERROR(x) printf("HARD DISK ERROR "); printf xchar *pro_lefmt = "ASSERTION (%s) FAILED IN PROC %s ";#define ASSERT(e, p, m, x) if (!(e)) {\printf(pro_lefmt,"e","p");\printf m;\printf ("\n");\x;};#else#define ERROR(x)#define ASSERT(e, p, m, x)#endif/* Contrast change ok flag.  Maintained by the disk driver.  When 0 the * parallel port is not in use and may be switched to allow console contrast * changes.  If the contrast is waiting for the disk then 'l2_rcflag' is one. * When convenient and this is set the disk will call l2ramp. */int ppinuse;extern char l2_rcflag;/* * proopen - check for existence of controller */proopen(dev){	int i;	register struct device_d *devp;	int prointr();	extern char slot[];	i = physical(dev);	if (i) {		/* for expansion slot check slot number and type */		if (!PPOK(i) || (slot[PPSLOT(i)] != PR0)) {			u.u_error = ENXIO;			return 1;		}	}	devp = pro_da[i];	u.u_error = 0;	if (iocheck(&devp->d_ifr)) {		{ asm(" nop "); }		if (prodata[i].pd_da != devp) {	/* not already setup */			if (setppint((prodata[i].pd_da = devp),prointr))				goto fail;			if (proinit(&prodata[i])) {				freeppin(devp);				goto fail;			}		}	} else {	fail:		u.u_error = ENXIO;		prodata[i].pd_da = (struct device_d *)0;		return 1;	}	return 0;}/* * prostrategy - set up to start the transfer */prostrategy(bp)register struct buf *bp;{	int pun = physical(bp->b_dev);	register struct prodata *p = &prodata[pun];	register struct buf *up;	if (!p->pd_da) {		printf("Attempt to read/write unopened profile device\n");		printf("bp=%x dev=%x (Unit %d)\n",bp,bp->b_dev,pun);		p->pd_err = "device not open";		goto haderr;	}	if (bp->b_blkno >= 0 &&	   (bp->b_blkno < prlmap[logical(bp->b_dev)].pm_len)) {		bp->av_forw = (struct buf *)NULL; /* last of all bufs and */		bp->ul_forw = (struct buf *)NULL; /* last of units bufs */		SPL6();		/* must be highest of all ints for this code*/		if (protab.b_actf == NULL)			protab.b_actf = bp;	/* empty - put on front */		else			protab.b_actl->av_forw = bp; /* else put at end */		protab.b_actl = bp;		if (p->pd_actv == 0) {		/* controller inactive */			p->pd_actv = bp;	/* start of unit blk list */	/* Since we might fail before ever getting an interrupt	 * we must be prepared to do the buffer cleanup here also.	 */			while (prostart(p)) {	/* start up the transfer */				bp->b_resid = bp->b_bcount;				bp->b_flags |= B_ERROR;				prorelse(p,bp);			}		} else {			/* link onto unit list */			for (up=p->pd_actv; up->ul_forw; up=up->ul_forw)				;			up->ul_forw = bp;		}						SPL0();		return;	}	p->pd_err = "invalid blkno";haderr:	bp->b_resid = bp->b_bcount;	bp->b_flags |= B_ERROR;	iodone(bp);	return;}/* * Release finished buffer and unlink from list.  Two lists are maintained. * The av_forw pointers are used to link all the buffers in use by the driver * onto the protab iobuf header.  The av_back pointers (dubbed ul_forw) are * used to link together the buffers into unit lists (headed by the prodata * entry for that unit). */prorelse (p, bp)register struct prodata *p;register struct buf *bp;{	register struct buf *up;	if (protab.b_actf == bp) {	/* first buffer */		if ((protab.b_actf = bp->av_forw) == (struct buf *)0)			protab.b_actl == (struct buf *)0;	} else {			/* middle or last buffer */		for (up=protab.b_actf; up->av_forw != bp; up=up->av_forw)			if (!up->av_forw) panic("prorelse: buf list error");		up->av_forw = bp->av_forw;		if (!up->av_forw && (protab.b_actl == bp))			protab.b_actl = up;	}	p->pd_actv = bp->ul_forw;	/* next buf for this unit */	iodone(bp);}/* * proinit - initialize drive first time or after severe error */proinit(p)	struct prodata *p;{	register char zero = 0;	register struct device_d *devp = p->pd_da;	int pl;	pl = spl6();	devp->d_acr = zero;	devp->d_pcr = 0x6B;	/* set controller CA2 pulse mode strobe */	devp->d_ddra = zero;	/* set port A bits to input **/	if (devp == PPADDR)		devp->d_ddrb &= 0x5C;	/* set BSY and OCD to input */	else		devp->d_ddrb &= 0xFC;	/* two or four port cards */	devp->d_ddrb |= 0x1C;	/* set port B bits 2,3,4 to out */	devp->d_irb &= ~DEN;	/* set enable = true */	devp->d_irb |= CMD|DRW;	/* set command = false  set direction = in */	devp->d_t2cl = zero;	devp->d_t2ch = zero;	devp->d_ier = FIRQ|FCA1;	zero = devp->d_irb;	if (zero & OCD) {		p->pd_state = SERR;		splx(pl);		return 1;	}	p->pd_state = SCMD;	splx(pl);	return 0;}/* * proread - process read from disk */proread(dev)dev_t dev;{	physio(prostrategy, &rprobuf, dev, B_READ);}/* * prowrite - process write to disk */prowrite(dev)dev_t dev;{	physio(prostrategy, &rprobuf, dev, B_WRITE);}/* * prostart - initiate the next logical io operation */prostart(p)register struct prodata *p;{	register struct buf *bp = p->pd_actv;	if (!bp)		return 0;	ASSERT(bp&&p,prostart,("bp=x%x p=x%x",bp,p),while(1))	p->pd_limit = prlmap[logical(bp->b_dev)].pm_beg;/* logical offset */	p->pd_blkno = bp->b_blkno + p->pd_limit;	/* starting blk # */#ifndef UNISOFT	if (p->pd_blkno <= MAXBOOT)		/* don't allow access to boot */		return(-1);#endif UNISOFT	p->pd_limit += prlmap[logical(bp->b_dev)].pm_len; /* max blk # + 1*/	p->pd_bcount = bp->b_bcount;	p->pd_addr = bp->b_un.b_addr;	return procmd(bp->b_flags, bp->b_dev, p->pd_blkno, (unsigned)p->pd_bcount);}/* * Procmd - initiate next physical io operation */procmd(func, dev, bn, ct)	register daddr_t bn;	unsigned ct;{	register int pun = physical(dev);	register struct prodata *p = &prodata[pun];	register struct cmd *pc;	ASSERT(ct!=0,Procmd,("ct=%d",ct),while(1))#ifndef UNISOFT	if (bn <= MAXBOOT)			/* check again to be sure */		return(-1);#endif UNISOFT	if (p->pd_state == SERR) {		if (proinit(p))			/* initialize drive */			return -1;	}		/* controller should not be busy now */		/* Build Command (ok to send extra bytes on write cmd) */	pc = &p->pd_cmdb;	pc->p_cmd = (func & B_READ) ? PROREAD : PROWRITE;	pc->p_high = bn >> 16;	pc->p_mid = bn >> 8;	if (interleave(dev) == 0)		pc->p_low = bn;	else		pc->p_low = (bn & 0xF0) | pro_secmap[bn&0xF];	pc->p_retry = 10;	pc->p_thold = 3;	p->pd_state = SCMD;	p->pd_nxtst = SCMD;	if (prochk(p,SCMD)) {		/* sync controller to cmd state */		if (!prochk(p,SCMD))	/* if it failed it should work now */			return 0;		p->pd_err = "cant force disk into CMD state";		p->pd_state = SERR;		return -1;	}	return 0;}prointr(pun)int pun;{		register struct device_d *devp;	/* a5 */	register char *cp;		/* a4 */	register char csum;		/* d7 */	/*NOTUSED*/	register char zero = 0;		/* d6 */	register struct buf *bp;	register struct prodata *p = &prodata[pun];	register short i;	struct proheader *ph;	devp = p->pd_da;	(void) spl6();	/* added April 4/84 to prevent panic in prorelse */		/* changed from spl5 August 30/84 to fix multi-user bug on 2/10 */	/* ASSERT((stats&BSY)==BSY,prointr,("disk %d busy: state=%d, irb=x%x",pun,p->pd_state,stats),return(devp->d_ifr = devp->d_ifr)) */#ifdef lint	csum = 0;	i = csum;#endif	if ((bp = p->pd_actv) == 0) {	/* spurious interrupt */	 	/* printf("Spurious INT on profile dev %d [at %x]\n",pun,devp); */		devp->d_ddrb |= 0x80;	/* setup for a reset */		devp->d_irb |= 0x80;		devp->d_ddrb &= 0x7F;		devp->d_ifr = devp->d_ifr;		/* reset interrupt trap */		/* proinit(p); */		return;	}	/* ASSERT(bp!=0,prointr,("dsk=%d ier=x%x ifr=x%x state=%d",pun,devp->d_ier&255,devp->d_ifr&255,p->pd_state),return devp->d_ifr=0x7F) */	ASSERT((p->pd_state!=SERR&&p->pd_state!=SSTOP),prointr,("b_flags=0x%x",bp->b_flags),while(1))	ASSERT(physical(bp->b_dev)==pun,prointr,("dev=x%x unit=%d",bp->b_dev,pun),while(1))	devp->d_ifr = devp->d_ifr;		/* reset interrupt trap */		/*		 * Note that IO operations fail when OCD.		 * This may result in a `panic'.  Allowing it to		 * block and be restarted has it problems also.		 */	if (devp->d_irb & OCD) {		/* cable disconnected ? */		p->pd_err = "Open Cable Disconnect";		goto haderr;	}	if (p->pd_state == SCMD) {		/* Send command */		devp->d_irb &= ~DRW;	/* set dir=out */		devp->d_ddra = 0xFF;	/* set port A bits to output */					/* Now send command */		cp = (char *)(&p->pd_cmdb);		if (*cp == PROREAD)			p->pd_nxtst = SRDBLK;		else			p->pd_nxtst = SWRTD;		devp->d_ira = *cp++; devp->d_ira = *cp++; devp->d_ira = *cp++;		devp->d_ira = *cp++; devp->d_ira = *cp++; devp->d_ira = *cp;		devp->d_irb |= DRW;		devp->d_ddra = zero;		if (prochk(p,p->pd_nxtst)) {			p->pd_err = "failed to issue cmd to disk";			goto haderr;		}		return;		/* will send data or get status next */	}	if (p->pd_state == SRDBLK || p->pd_state == SFINI) {/* Read status word */		devp->d_irb |= DRW;		devp->d_ddra = zero;		p->pd_sbuf = 0;		cp = (char *)(&p->pd_sbuf);		*cp++ = devp->d_ira;		*cp++ = devp->d_ira;		*cp++ = devp->d_ira;		*cp = devp->d_ira;		p->pd_sbuf &= ~STATMSK;	/* mask off redund stat bits */		if (p->pd_sbuf) {			ERROR(("dev %d: state=%d status=x%x\n",p-prodata,p->pd_state,p->pd_sbuf));			p->pd_err = "bad status";			goto haderr;		}		if (p->pd_state == SRDBLK) { /* Read successful so pickup data*/			ASSERT(p->pd_bcount>0,prointr,(""),goto haderr)			i = sizeof(rphbuf) - 1;		/* sizeof header */			cp = (char *)(&rphbuf);			do *cp++ = devp->d_ira;			while (--i != -1);			cp = p->pd_addr;			i = min(SECSIZE, (unsigned)p->pd_bcount);#ifdef MOREASM			if ((i & 3) == 0) {				i = (i >> 2) - 1;				do {					asm (" movb a5@(9),a4@+ ");					asm (" movb a5@(9),a4@+ ");					asm (" movb a5@(9),a4@+ ");					asm (" movb a5@(9),a4@+ ");					/* asm (" movb a5@(9),d6 "); */					/* asm (" movb d6,a4@+ "); */					/* asm (" eorb d6,d7 "); */					/* asm (" movb a5@(9),d6 "); */					/* asm (" movb d6,a4@+ ");  */					/* asm (" eorb d6,d7 "); */					/* asm (" movb a5@(9),d6 "); */					/* asm (" movb d6,a4@+ ");  */					/* asm (" eorb d6,d7 "); */					/* asm (" movb a5@(9),d6 "); */					/* asm (" movb d6,a4@+ ");  */					/* asm (" eorb d6,d7 "); */				} while (--i != -1);	/* optimizes to DBRA */			} else {#endif				i--;				do {					asm (" movb a5@(9),a4@+ ");					/* asm (" movb a5@(9),d6 "); */					/* asm (" movb d6,a4@+ "); */					/* asm (" eorb d6,d7 "); */				} while (--i != -1);#ifdef MOREASM			}#endif		}	/* Determine if IO operation is completed or spans another block.  */		p->pd_bcount -= min(SECSIZE, (unsigned)p->pd_bcount);		if (p->pd_bcount <= 0 || ++p->pd_blkno >= p->pd_limit) {			bp->b_resid = p->pd_bcount;			prorelse(p,bp);			if (p-prodata == 0) ppinuse = 0;	/* contrast */			if (prostart(p)) {	/* startup next buf in chain */				p->pd_err = "prostart failed on next blk";				goto haderr;			}			return;		/* next op started or no next op */		}		p->pd_addr += SECSIZE;	/* setup for next block of io trans */		if(procmd(bp->b_flags,bp->b_dev,p->pd_blkno,(unsigned)p->pd_bcount)){			p->pd_err = "Procmd failed to continue operation";			goto haderr;		}		return;			/* end of i/o or beg of next blk */	}	if (p->pd_state == SWRTD) {	/* send data */		p->pd_nxtst = SFINI;		ASSERT(p->pd_bcount>0,prointr,(""),while(1))		devp->d_irb &= ~DRW;	/* set dir=out */		devp->d_ddra = 0xFF;	/* set port A bits to output */		ph = &phbuf;		ph->ph_fileid = p->pd_blkno ?0 :0xAAAA;		i = sizeof(phbuf) - 1;		cp = (char *)(ph);		do devp->d_ira = *cp++;		while (--i != -1);		cp = (char *)p->pd_addr;	/* place to get data from */		i = min(SECSIZE, (unsigned)p->pd_bcount);#ifdef MOREASM		if ((i & 3) == 0) {			i = (i >> 2) - 1;			do {				asm (" movb a4@+,a5@(9) ");				asm (" movb a4@+,a5@(9) ");				asm (" movb a4@+,a5@(9) ");				asm (" movb a4@+,a5@(9) ");				/* asm (" movb a4@+,d6 ");	*/				/* asm (" eorb d6,d7 ");	*/				/* asm (" movb d6,a5@(9) ");	*/				/* asm (" movb a4@+,d6 ");	*/				/* asm (" eorb d6,d7 ");	*/				/* asm (" movb d6,a5@(9) ");	*/				/* asm (" movb a4@+,d6 ");	*/				/* asm (" eorb d6,d7 ");	*/				/* asm (" movb d6,a5@(9) ");	*/				/* asm (" movb a4@+,d6 ");	*/				/* asm (" eorb d6,d7 ");	*/				/* asm (" movb d6,a5@(9) ");	*/			} while (--i != -1);		/* optimizes to DBRA */		} else {#endif			i--;			do {				asm (" movb a4@+,a5@(9) ");				/* asm (" movb a4@+,d6 "); */				/* asm (" eorb d6,d7 "); */				/* asm (" movb d6,a5@(9) "); */			} while (--i != -1);#ifdef MOREASM		}#endif		if (prochk(p,SPERFORM)) {			p->pd_err = "didn't get to perfrom state";			goto haderr;		}		return;			/* will pick up status next intr */	}	p->pd_err = "invalid state";haderr:	do {		bp->b_resid = p->pd_bcount;		bp->b_flags |= B_ERROR;		ERROR(("dev %d: %s\n",p-prodata,p->pd_err));		prorelse(p,bp);		p->pd_state = SERR;	} while (prostart(p));}/* * Get in sync with disk. * (subroutine FINDD2 & CHKRSP from 'profrom.text' document) * Expects enable==true and Direction==in at start. * If disk response is 'state' then returns 0 (success) * otherwise fails (returns -1 if timeout and cur state if bad state). */prochk(p, state)	register struct prodata *p;{	register struct device_d *devp = p->pd_da;	register zero = 0;	register i;	int resp;/* while((devp->d_irb&BSY)==0); */	ASSERT((devp->d_irb&BSY)==BSY,prochk,("state=%d [waiting]",state),goto err)	/* while ((devp->d_irb&BSY)==0); */	if (state == SCMD && (p-prodata == 0)) {/* PP inuse */		if (l2_rcflag)		/* ramp in progress */			l2ramp(2);	/* so help it along */		ppinuse = 1;	}	devp->d_irb |= DRW;		/* set input mode */	devp->d_ddra = zero;		/* set port A bits to input */	devp->d_irb &= ~CMD;		/* set cmd and enable bufs */	i = RSPTIME;			/* about 1ms */	while (devp->d_irb&BSY && i--);	/* wait sig that resp byte is ready*/	resp = PIDL;			/* reply to use if resp byte wrong */	if (i > 0) {			/* didn't timeout */		i = devp->d_ira;	/* get response from disk */		if (i == state)		/* got correct state */			resp = PGO;	/* reply to use if resp byte correct*/		devp->d_irb &= ~(DRW|CMD);	/* set dir=out cmd=true */		devp->d_ddra = 0xFF;		/* set port A bits to output */		devp->d_ira = resp;		/* send reply (GO or RESET) */		devp->d_ier = FIRQ|FCA1;	/* enable interrupts */		devp->d_irb |= CMD;		/* sig disk to read resp */		p->pd_state = p->pd_nxtst;		/* setup next state */		return (i == state) ?0 :i;	}err:	if (p-prodata == 0) ppinuse = 0;	/* reset ppinuse flag */	p->pd_state = SERR;	p->pd_err = "EXCESSIVE DISK DELAY -- (is the drive plugged in??)";	ERROR(("dev %d: %s\n",p-prodata,p->pd_err));	devp->d_ddra = zero;		/* set port A bits to input */	devp->d_irb |= CMD|DRW;		/* set dir=in, disable buffers */	devp->d_ier = ~FIRQ;		/* disable all interrupts */	return (-1);}/* ARGSUSED */proioctl(dev, cmd, addr, flag)dev_t dev;int cmd;caddr_t addr;int flag;	/*NOTUSED*/{	struct prodata *p = &prodata[physical(dev)];	switch (cmd) {	case TIOCGETP:		if (copyout((caddr_t)&p->pd_flags, addr, sizeof(p->pd_flags)))			u.u_error = EFAULT;		break;	case TIOCSETP:		if (copyin(addr, (caddr_t)&p->pd_flags, sizeof(p->pd_flags)))			u.u_error = EFAULT;		break;	default:		u.u_error = ENOTTY;	}}proprint(dev, str)char *str;{	printf("%s on pro drive %d, slice %d\n", str, (dev>>4)&0xF, dev&7);}