V10/sys/io/scsi.c

Compare this file to the similar file:
Show the results in this format:

/*
	SCSI Pass-Thru driver for the TD Systems UD? -- Andrew Hume
	Ninth Edition Unix
*/

#include "sys/param.h"
#include "sys/user.h"
#include "sys/buf.h"
#include "sys/systm.h"
#include "sys/pte.h"
#include "sys/map.h"
#include "sys/ubaddr.h"
#include "sys/conf.h"

#include	"sys/uda.h"
#include	"sys/mscp.h"

#include "sys/scsi.h"
#include	<scsi.h>

#define	mscp_scsi	m_fmt

struct udadevice {
	short	udaip;		/* initialization and polling */
	short	udasa;		/* status and address */
};
#define	UDA_ERR		0100000	/* error bit */
#define	UDA_STEP4	0040000	/* step 4 has started */
#define	UDA_STEP3	0020000	/* step 3 has started */
#define	UDA_STEP2	0010000	/* step 2 has started */
#define	UDA_STEP1	0004000	/* step 1 has started */
#define	UDA_GO		0000001	/* start operation, after init */

#define	UDA_OWN		0x80000000	/* UDA owns this descriptor */
#define	UDA_INT		0x40000000	/* allow interrupt on ring transition */

#define WAITPRI 	(PZERO+1)
#define	SCSILTMT	300		/* long timeout in seconds */
#define	SCSISTMT	10		/* short timeout in seconds */
#define	ERROR		0x8000		/* in csr */
#define	SCSIINTR	0700
#define	SETDSC(dsc, fl)	dsc = (dsc&~(UDA_OWN|UDA_INT))|fl

static memset(p, c, n)
	register char *p, c;
	register n;
{
	while(n-- > 0)
		*p++ = c;
}

int scsiopen(), scsiclose(), scsiread(), scsiwrite();

extern struct scsi scsi[];
extern struct ubaddr scsiaddr[];
extern int scsicnt;
struct cdevsw scsicdev =
	cdinit(scsiopen, scsiclose, scsiread, scsiwrite, nodev);
int scsi_dbg = 0;

scsiopen(dev)
	dev_t dev;
{
	register struct scsi *p;

	if((dev = minor(dev)) >= scsicnt) {
		u.u_error = ENODEV;
		return;
	}
	if((p = &scsi[dev])->flag&OPEN) {
		u.u_error = EBUSY;
		return;
	}
	if(((p->addr = (struct udadevice *)ubaddr(&scsiaddr[dev])) == 0)
	  || ubbadaddr(scsiaddr[dev].ubno, (caddr_t)p->addr, sizeof(u_short))) {
		printf("scsi%d absent\n", dev);
		u.u_error = ENODEV;
		return;
	}
	if((p->flag&USED) == 0)
		if(scsistart(dev))
			return;
	p->flag = USED|OPEN|NEXTWR|DONE;
}

scsistart(dev)
	dev_t dev;
{
	register struct scsi *p = &scsi[minor(dev)];

	p->flag |= DONE;
	p->b1 = geteblk();
	p->b1->b_bcount = BUFSIZE;
	p->b1->b_flags = B_BUSY;
	clrbuf(p->b1);
	p->ub1 = ubmbuf(scsiaddr[minor(dev)].ubno, p->b1, USLP);
	p->u1 = ubadbuf(scsiaddr[minor(dev)].ubno, p->b1, p->ub1);
	p->data = (unsigned char *)p->b1->b_un.b_addr;
	p->b2 = geteblk();
	p->b2->b_bcount = BUFSIZE;
	p->b2->b_flags = B_BUSY;
	clrbuf(p->b2);
	p->ub2 = ubmbuf(scsiaddr[minor(dev)].ubno, p->b2, USLP);
	p->u2 = ubadbuf(scsiaddr[minor(dev)].ubno, p->b2, p->ub2);
	p->junk = (struct bag *)p->b2->b_un.b_addr;
	p->timeout = SCSISTMT;
	if(scsiinit(dev) == 0){
		scsiclose(dev);
		p->flag = 0;
		u.u_error = ENXIO;
		return(1);
	}
	printf("scsi%d run\n", minor(dev));
	return(0);
}

scsiclose(dev)
	dev_t dev;
{
	register struct scsi *p = &scsi[minor(dev)];

	if((p->flag&DONE) == 0){	/* wait for I/O to complete */
		(void)tsleep((caddr_t)p, PZERO+1, p->timeout);
	}
	p->flag = USED;
}

scsiwrite(dev)
	dev_t dev;
{
	register count;
	register struct scsi *p = &scsi[minor(dev)];
	register struct mscmd *cmd = &p->junk->cmd.msg;
	unsigned char flag, bus_id;

	if(p->flag&NEXTWR)
		p->flag &= ~NEXTWR;
	else {
		u.u_error = EGREG;
		return;
	}
	if(copyin(u.u_base, &p->tran_id, 4) || copyin(u.u_base+4, &bus_id, 1)
			|| copyin(u.u_base+5, &flag, 1)
			|| copyin(u.u_base+6, &cmd->mscp_scsi, SCSICMD)){
		u.u_error = EFAULT;
		return;
	}
	if(flag & SCSI_RESET){
		if(scsiinit(dev) == 0){
			printf("scsi%d: reset failed\n", minor(dev));
			p->flag = 0;
			u.u_error = ENXIO;
			return;
		}
		printf("scsi%d: reset\n", minor(dev));
		p->flag = USED|OPEN|NEXTWR|DONE;
		u.u_count = 0;
		return;
	}
	p->timeout = (flag&SCSI_LTMOUT)? SCSILTMT:SCSISTMT;
	count = u.u_count - (6+SCSICMD);
	u.u_base += 6+SCSICMD;
	if((count < 0) || (count > SCSIDATA)){
		u.u_error = EINVAL;
		return;
	}
	memset(p->data, 0xEE, BUFSIZE);
	if(flag&SCSI_RD){
		cmd->m__r1 = 0106;
		count = scsilen((unsigned char *)&cmd->mscp_scsi);
	} else if(flag&SCSI_WR){
		if(copyin(u.u_base, p->data, count)){
			u.u_error = EFAULT;
			return;
		}
		cmd->m__r1 = 0107;	
	} else {
		u.u_error = EINVAL;
		return;
	}
	cmd->m_opcd = (flag & SCSI_BRESET)? 0160 : 0130;
	if(scsi_dbg)
		printf("c=%d uc=%d, dir=0%o, bus_id=%d, new count=%d timeout=%d\n",
			count, u.u_count, cmd->m__r1, bus_id, count, p->timeout);
	cmd->m_unit = bus_id;
	cmd->m_bcnt = count;
	cmd->m_fcnt = p->u1;
	p->flag = (p->flag&~DONE)|PEND;
	if(flag&SCSI_SENSE)
		p->flag |= ERRSNSE;
	p->junk->ca.ca_cmdint = 0;
	p->junk->ca.ca_rspint = 0;
	SETDSC(p->junk->ca.ca_cmddsc[0], UDA_OWN);
	bus_id = p->addr->udaip;	/* start controller */
	u.u_count = 0;
}

scsiread(dev)
	dev_t dev;
{
	register struct scsi *p = &scsi[minor(dev)];
	register count;

	if(p->flag&NEXTWR){
		u.u_error = EGREG;
		return;
	} else
		p->flag |= NEXTWR;
	if((p->flag&DONE) == 0){
		if(tsleep((caddr_t)p, PZERO+1, p->timeout) != TS_OK){
			u.u_error = ENXIO;
			return;
		}
	}
	if(p->sa&0x8000){
		printf("scsi%d: error sa=#%x\n", minor(dev), p->sa&0xFFFF);
		u.u_error = EIO;
		scsiinit(dev);
		return;
	}
	count = u.u_count - SCSIRET;
	if(count > p->junk->rsp.msg.m_bcnt)
		count = p->junk->rsp.msg.m_bcnt;
	if((count < 0) || (count > SCSIDATA)){
		u.u_error = EINVAL;
		return;
	}
	((unsigned long *)p->status)[0] = p->tran_id;
	p->status[4] = p->junk->rsp.msg.m_fbbk;
	p->status[5] = p->junk->rsp.msg.m_fbbk>>8;
	p->status[6] = 0;	/* flags */
	p->status[7] = 1;	/* viking */
	((short *)p->status)[4] = p->sa;
	((short *)p->status)[5] = p->junk->rsp.msg.m_sts;
	if(copyout(p->status, u.u_base, SCSIRET)){
		u.u_error = EFAULT;
		return;
	}
	if(count)
		if(copyout(p->data, u.u_base+SCSIRET, count)){
			u.u_error = EFAULT;
			return;
		}
	if(p->junk->rsp.msg.m_sts){
		printf("scsi%d: stat=#%x\n", minor(dev), p->junk->rsp.msg.m_sts);
		u.u_error = (p->junk->rsp.msg.m_sts == 0x80a)? ESRCH : EIO;
		return;
	}
	u.u_count -= SCSIRET+count;
	SETDSC(p->junk->ca.ca_rspdsc[0], UDA_OWN|UDA_INT);
}

scsi0int(dev)
	dev_t dev;
{
	register struct scsi *p = &scsi[minor(dev)];
	register s;

	if((p->flag&PEND) == 0){
		printf("scsi%d: unexpected interrupt\n", minor(dev));
		return;
	}
	if(p->junk->ca.ca_rspint == 0){
		printf("scsi%d: rspint=0 cmdint=%d rsp=#%x cmd=#%x\n", minor(dev), p->junk->ca.ca_cmdint, p->junk->ca.ca_rspdsc[0], p->junk->ca.ca_cmddsc[0]);
		return;
	}
	p->sa = p->addr->udasa;
	s = spl6();
	p->flag = (p->flag&~PEND)|DONE;
	splx(s);
	wakeup((caddr_t)p);
}

/*
 * hardware initialization handshake
 * for simplicity, don't bother with init interrupts
 * returns nonzero if ok
 */

#define	UDA_STEPS	(UDA_ERR|UDA_STEP4|UDA_STEP3|UDA_STEP2|UDA_STEP1)

scsiinit(d)
int d;
{
	register struct scsi *p = &scsi[minor(d)];
	register struct udadevice *udaddr;
	register int i;
	time_t out;
	int s;

	udaddr = p->addr;
	out = time + 11;
	s = spl0();
printf("scsi1");
	udaddr->udaip = 0;
	while((udaddr->udasa & (UDA_ERR|UDA_STEP1)) == 0 && time <= out)
		;
	if((udaddr->udasa & UDA_STEPS) != UDA_STEP1) {
		steperr("1", udaddr->udasa, d);
		splx(s);
		return (0);
	}
	udaddr->udasa = UDA_ERR|(SCSIINTR/4);
	/* no diagnostic wrap, no interrupts during initialization */
printf("2");
	out = time + 21;
	while((udaddr->udasa & (UDA_ERR|UDA_STEP2)) == 0 && time <= out)
		;
	if((udaddr->udasa & UDA_STEPS) != UDA_STEP2) {
		steperr("2", udaddr->udasa, d);
		splx(s);
		return (0);
	}
	i = p->u2;	/* unibus address of bag */
	i += (long)&p->junk->ca.ca_rspdsc[0] - (long)p->junk;
	udaddr->udasa = (short)i;
	out = time + 11;
printf("3");
	while((udaddr->udasa & (UDA_ERR|UDA_STEP3)) == 0 && time <= out)
		;
	if((udaddr->udasa & UDA_STEPS) != UDA_STEP3) {
		steperr("3", udaddr->udasa, d);
		splx(s);
		return (0);
	}
	udaddr->udasa = (i >> 16)&0x3F;
	out = time + 11;
printf("4");
	while((udaddr->udasa & (UDA_ERR|UDA_STEP4)) == 0 && time <= out)
		;
	if((udaddr->udasa & UDA_STEPS) != UDA_STEP4) {
		steperr("4", udaddr->udasa, d);
		splx(s);
		return (0);
	}
	i = p->u2 + (long)&p->junk->rsp.msg.m_crf - (long)p->junk;
	p->junk->ca.ca_rspdsc[0] = UDA_OWN|UDA_INT|i;
	i = p->u2 + (long)&p->junk->cmd.msg.m_crf - (long)p->junk;
	p->junk->ca.ca_cmddsc[0] = i;
	if (udaddr->udasa & UDA_ERR) {
		steperr("5", udaddr->udasa, d);
		splx(s);
		return (0);
	}
	udaddr->udasa = UDA_GO;		/* finish init */
	/* finish cmd packet init */
	p->junk->cmd.msg_len = 44;
	p->junk->cmd.msg.m_crf = (long)p->junk;
	p->junk->cmd.msg.m_opcd = 0130;
	splx(s);
printf(" done\n");
	return (1);
}

steperr(str, sa, dev)
	char *str;
{
	printf("scsi%d: step%s error, sa=#%x\n", minor(dev), str, sa&0xFFFF);
}

/*
	dreck to figure out how much we should read back
*/

scsilen(cmd)
	unsigned char *cmd;
{
	switch(cmd[0])
	{
	case 0x03:	return(cmd[4]);
	case 0x08:	return(cmd[4]*1024);
	case 0x12:	return(cmd[4]);
	case 0x1A:	return(cmd[4]);
	case 0x1C:	return(cmd[3]*256 + cmd[4]);
	case 0x25:	return(8);
	case 0x28:	return(1024*(256*cmd[7] + cmd[8]));
	case 0x2C:	return(6);
	case 0x2D:	return(6);
	case 0x4D:	return(1*(256*cmd[7] + cmd[8]));
	case 0xC2:	return(1024);
	case 0xC3:	return(4096);
	case 0xD3:	return(20);
	}
	return(0);
}