Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/pc/adaptec.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


## diffname pc/adaptec.c 1993/0114
## diff -e /dev/null /n/bootesdump/1993/0114/sys/src/9/pc/adaptec.c
0a
/*
 * Adaptec AHA-154[02]B Intelligent Host Adapter.
 */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"

#include "ureg.h"

/*
 * Uniprocessors can't (don't need to) lock.
 */
#define lock(l)
#define unlock(l)

enum {
	NCtlr		= 1,
	NTarget		= 8,		/* targets per controller */

	Port		= 0x330,	/* factory defaults: I/O port */
	CtlrID		= 7,		/*	adapter SCSI id */
	Irq		= 11,		/*	interrupt request level */
};

enum {					/* registers */
	Rc		= 0,		/* WO: control */
	Rs		= 0,		/* RO: status */
	Rcdo		= 1,		/* WO: command/data out */
	Rdi		= 1,		/* RO: data in */
	Rif		= 2,		/* RO: interrupt flags */
};

enum {					/* Rc */
	Scrst		= 0x10,		/* SCSI bus reset */
	Irst		= 0x20,		/* interrupt reset */
	Srst		= 0x40,		/* soft reset */
	Hrst		= 0x80,		/* hard reset */
};

enum {					/* Rs */
	Invdcmd		= 0x01,		/* invalid host adapter command */
	Df		= 0x04,		/* data in port full */
	Cdf		= 0x08,		/* command/data port full */
	Idle		= 0x10,		/* SCSI host adapter idle */
	Init		= 0x20,		/* mailbox initialisation required */
	Diagf		= 0x40,		/* internal diagnostic failure */
	Stst		= 0x80,		/* self testing in progress */
};

enum {					/* Rcdo */
	Cnop		= 0x00,		/* no operation */
	Cmbinit		= 0x01,		/* mailbox initialisation */
	Cstart		= 0x02,		/* start SCSI command */
	Cbios		= 0x03,		/* start PC/AT BIOS command */
	Cinquiry	= 0x04,		/* adapter inquiry */
	Cmboie		= 0x05,		/* enable mailbox out available interrupt */
	Cselection	= 0x06,		/* set selection timeout */
	Cbuson		= 0x07,		/* set bus-on time */
	Cbusoff		= 0x08,		/* set bus-off time */
	Ctransfer	= 0x09,		/* set transfer speed */
	Cdevices	= 0x0A,		/* return installed devices */
	Cconfiguration	= 0x0B,		/* return configuration data */
	Ctenable	= 0x0C,		/* enable target mode */
	Csetup		= 0x0D,		/* return setup data */
	Cwbuff		= 0x1A,		/* write adapter channel 2 buffer */
	Crbuff		= 0x1B,		/* read adapter channel 2 buffer */
	Cwfifo		= 0x1C,		/* write adapter FIFO buffer */
	Crfifo		= 0x1D,		/* read adapter FIFO buffer */
	Cecho		= 0x1F,		/* ECHO command data */
	Cdiag		= 0x20,		/* adapter diagnostic */
	Coptions	= 0x21,		/* set host adapter options */
};

enum {					/* Rif */
	Mbif		= 0x01,		/* mailbox in full */
	Mboa		= 0x02,		/* mailbox out available */
	Hacc		= 0x04,		/* host adapter command complete */
	Scrd		= 0x08,		/* SCSI reset detected */
	Ai		= 0x80,		/* any interrupt */
};

typedef struct {
	uchar	cmd;			/* command */
	uchar	msb;			/* CCB pointer MSB */
	uchar	mid;			/* CCB pointer MID */
	uchar	lsb;			/* CCB pointer LSB */
} Mbox;

enum {					/* mailbox commands */
	Mbfree		= 0x00,		/* mailbox is free */

	Mbostart	= 0x01,		/* start SCSI or adapter command */
	Mboabort	= 0x02,		/* abort SCSI or adapter command */

	Mbiok		= 0x01,		/* CCB completed without error */
	Mbiabort	= 0x02,		/* CCB aborted by host */
	Mbinx		= 0x03,		/* aborted CCB not found */
	Mbierror	= 0x04,		/* CCB completed with error */
};

typedef struct {
	uchar	op;			/* command control block operation code */
	uchar	ctl;			/* address and direction control */
	uchar	cmdlen;			/* SCSI command length */
	uchar	reqlen;			/* request sense allocation length */
	uchar	datalen[3];		/* data length (MSB, MID, LSB) */
	uchar	dataptr[3];		/* data pointer (MSB, MID, LSB) */
	uchar	linkptr[3];		/* link pointer (MSB, MID, LSB) */
	uchar	linkid;			/* command linking identifier */
	uchar	hastat;			/* host adapter status */
	uchar	tarstat;		/* target device status */
	uchar	reserved[2];
	uchar	cs[12+0xFF];		/* SCSI command and sense bytes */
} Ccb;

enum {					/* op */
	OInitiator	= 0x00,		/* initiator CCB */
	OTarget		= 0x01,		/* target CCB */
	Osg		= 0x02,		/* initiator CCB with scatter/gather */
	Ordl		= 0x03,		/* initiator CCB, residual data length returned */
	Osgrdl		= 0x04,		/* initiator CCB, both of the above */
	Obdr		= 0x81,		/* bus device reset */
};

enum {					/* ctl */
	CCBdatain	= 0x08,		/* inbound data transfer, length is checked */
	CCBdataout	= 0x10,		/* outbound data transfer, length is checked */
};

enum {					/* hastat */
	Eok		= 0x00,		/* no host adapter detected error */
	Etimeout	= 0x11,		/* selection timeout */
	Elength		= 0x12,		/* data over/under run */
	Ebusfree	= 0x13,		/* unexpected bus free */
	Ephase		= 0x14,		/* target bus phase sequence error */
	Eopcode		= 0x16,		/* invalid CCB opcode */
	Elink		= 0x17,		/* linked CCB does not have same LUN */
	Edirection	= 0x18,		/* invalid target direction received from host */
	Eduplicate	= 0x19,		/* duplicate CCB received in target mode */
	Esegment	= 0x1A,		/* invalid CCB or segment list parameter */
};

enum {					/* tarstat */
	Sgood		= 0x00,		/* good status */
	Scheck		= 0x02,		/* check status */
	Sbusy		= 0x08,		/* LUN busy */
};

typedef struct Target Target;
typedef struct Ctlr Ctlr;

struct Target {
	QLock;
	Rendez;

	Ccb	ccb;
	ulong	paddr;			/* physical address of ccb */
	uchar	*sense;			/* address of returned sense data */

	int	done;
	Target	*active;		/* link on active list */
};

struct Ctlr {
	Lock;

	ulong	port;			/* I/O port */
	ulong	id;			/* adapter SCSI id */

	uchar	cmd[5];			/* adapter command out */
	uchar	cmdlen;			/* adapter command out length */
	uchar	data[256];		/* adapter command data in */
	uchar	datalen;		/* adapter command data in length */

	Mbox	mb[NTarget+NTarget];	/* mailbox out + mailbox in */

	Mbox	*mbox;			/* current mailbox out index into mb */
	Mbox	*mbix;			/* current mailbox in index into mb */

	Target	target[NTarget];
	Target	*active;		/* link on active list */
};

static Ctlr softctlr[NCtlr];

/*
 * Issue a command to the controller. The command and its length is
 * contained in ctlr->cmd and ctlr->cmdlen. If any data is to be
 * returned, ctlr->datalen should be non-0, and the returned data will
 * be placed in ctlr->data.
 * If we see Hacc set, bail out, we'll process
 * the invalid command at interrupt time.
 */
static void
issue(Ctlr *ctlr)
{
	int len;

	len = 0;
	while(len < ctlr->cmdlen){
		if((inb(ctlr->port+Rs) & Cdf) == 0){
			outb(ctlr->port+Rcdo, ctlr->cmd[len]);
			len++;
		}

		if(inb(ctlr->port+Rif) & Hacc)
			return;
	}

	if(ctlr->datalen){
		len = 0;
		while(len < ctlr->datalen){
			if(inb(ctlr->port+Rs) & Df){
				ctlr->data[len] = inb(ctlr->port+Rdi);
				len++;
			}

			if(inb(ctlr->port+Rif) & Hacc)
				return;
		}
	}
}

static int
done(void *arg)
{
	return ((Target*)arg)->done;
}

static int
scsiio(int bus, Scsi *p, int rw)
{
	Ctlr *ctlr;
	Target *tp;
	ushort status;
	ulong len, s;

	/*
	 * Wait for the target to become free,
	 * then set it up. The Adaptec will allow us to
	 * queue multiple transactions per target, but
	 * gives no guarantee about ordering, so we just
	 * allow one per target.
	 */
	ctlr = &softctlr[bus];
	tp = &ctlr->target[p->target];

	qlock(tp);
	if(waserror()){
		qunlock(tp);
		nexterror();
	}

	/*
	 * If this is a request-sense and we have valid sense data
	 * from the last command, return it immediately.
	 * A pox on these weird enum names and the WD33C93A status
	 * codes.
	 */
	if(p->cmd.base[0] == ScsiExtsens && tp->sense){
		len = 8+tp->sense[7];
		memmove(p->data.ptr, tp->sense, len);
		p->data.ptr += len;
		tp->sense = 0;

		qunlock(tp);
		poperror();
		return 0x6000;
	}
	tp->sense = 0;

	/*
	 * Fill in the ccb. 
	 */
	tp->ccb.op = Ordl;
	tp->ccb.ctl = (p->target<<5)|p->lun;

	len = p->cmd.lim - p->cmd.base;
	tp->ccb.cmdlen = len;
	memmove(tp->ccb.cs, p->cmd.base, len);

	tp->ccb.reqlen = 0xFF;

	len = p->data.lim - p->data.base;
	tp->ccb.datalen[0] = (len>>16) & 0xFF;
	tp->ccb.datalen[1] = (len>>8) & 0xFF;
	tp->ccb.datalen[2] = len;
	if(len == 0)
		tp->ccb.ctl |= CCBdataout|CCBdatain;
	else if(rw == ScsiIn)
		tp->ccb.ctl |= CCBdatain;
	else
		tp->ccb.ctl |= CCBdataout;

	len = PADDR(p->data.base);
	tp->ccb.dataptr[0] = (len>>16) & 0xFF;
	tp->ccb.dataptr[1] = (len>>8) & 0xFF;
	tp->ccb.dataptr[2] = len;

	tp->ccb.linkptr[0] = tp->ccb.linkptr[1] = tp->ccb.linkptr[2] = 0;
	tp->ccb.linkid = 0;

	tp->ccb.hastat = tp->ccb.tarstat = 0;
	tp->ccb.reserved[0] = tp->ccb.reserved[1] = 0;

	/*
	 * Link the target onto the beginning of the
	 * ctlr active list and start the request.
	 * The interrupt routine has to be able to take
	 * requests off the queue in any order.
	 */
	s = splhi();
	lock(ctlr);

	tp->done = 0;
	tp->active = ctlr->active;
	ctlr->active = tp;

	ctlr->mbox->msb = (tp->paddr>>16) & 0xFF;
	ctlr->mbox->mid = (tp->paddr>>8) & 0xFF;
	ctlr->mbox->lsb = tp->paddr & 0xFF;
	ctlr->mbox->cmd = Mbostart;

	ctlr->cmd[0] = Cstart;
	ctlr->cmdlen = 1;
	ctlr->datalen = 0;
	issue(ctlr);

	ctlr->mbox++;
	if(ctlr->mbox >= &ctlr->mb[NTarget])
		ctlr->mbox = ctlr->mb;

	unlock(ctlr);
	splx(s);

	/*
	 * Wait for the request to complete
	 * and return the status.
	 */
	tsleep(tp, done, tp, 60*5*1000);
	if(done(tp) == 0)
		print("adaptec%d: timeout cmd=#%2.2ux\n", p->target, tp->ccb.cs[0]);

	if((status = (tp->ccb.hastat<<8)) == 0)
		status = 0x6000;
	status |= tp->ccb.tarstat;
	len = (tp->ccb.datalen[0]<<16)|(tp->ccb.datalen[1]<<8)|tp->ccb.datalen[2];
	p->data.ptr = p->data.lim - len;

	/*
	 * If the command returned sense data, keep a note
	 * of where it is for a subsequent request-sense command.
	 */
	if(tp->ccb.tarstat == Scheck && tp->ccb.hastat == Eok)
		tp->sense = &tp->ccb.cs[tp->ccb.cmdlen];

	qunlock(tp);
	poperror();

	return status;
}

int
scsiexec(Scsi *p, int rw)
{
	USED(rw);

	if(p->target == CtlrID)
		return 0x6002;

	return p->status = scsiio(0, p, rw);
}

static void
interrupt(Ureg *ur)
{
	Ctlr *ctlr = &softctlr[0];
	uchar rif, rs;
	Target *tp, **l;
	ulong paddr;

	USED(ur);

	lock(ctlr);

	/*
	 * Save and clear the interrupt(s). The only
	 * interrupts expected are Hacc, which we ignore,
	 * and Mbif which means something completed.
	 */
	rif = inb(ctlr->port+Rif);
	rs = inb(ctlr->port+Rs);
	outb(ctlr->port+Rc, Irst);
	if(rif & ~(Ai|Hacc|Mbif))
		print("adaptec%d: interrupt #%2.2ux\n", 0, rif);
	if((rif & Hacc) && (rs & Invdcmd))
		print("adaptec%d: invdcmd #%2.2ux, len %d\n", 0, ctlr->cmd[0], ctlr->cmdlen);

	/*
	 * Look for something in the mail.
	 * If there is, try to find the recipient from the
	 * ccb address, take it off the active list and
	 * wakeup whoever.
	 */
	while(ctlr->mbix->cmd){
		paddr = (ctlr->mbix->msb<<16)|(ctlr->mbix->mid<<8)|ctlr->mbix->lsb;
		l = &ctlr->active;
		for(tp = *l; tp; tp = tp->active){
			if(tp->paddr == paddr)
				break;
			l = &tp->active;
		}
		if(tp == 0)
			panic("adaptec%d: no target for ccb #%lux\n", 0, paddr);
		*l = tp->active;

		ctlr->mbix->cmd = 0;

		tp->done = 1;
		wakeup(tp);

		ctlr->mbix++;
		if(ctlr->mbix >= &ctlr->mb[NTarget+NTarget])
			ctlr->mbix = &ctlr->mb[NTarget];
	}

	unlock(ctlr);
}

static void
reset(Ctlr *ctlr, ulong port, ulong id)
{
	ulong paddr;
	int i;

	/*
	 * Initialise the software controller and set the board
	 * scanning the mailboxes.
	 * Need code here to tidy things up if we're
	 * resetting after being active.
	 */
	memset(ctlr, 0, sizeof(Ctlr));
	ctlr->port = port;
	ctlr->id = id;
	for(i = 0; i < NTarget; i++){
		ctlr->target[i].paddr = PADDR(&ctlr->target[i].ccb);
		ctlr->mbox = ctlr->mb;
		ctlr->mbix = &ctlr->mb[NTarget];
	}

	ctlr->cmd[0] = Cmbinit;
	paddr = PADDR(ctlr->mb);
	ctlr->cmd[1] = NTarget;
	ctlr->cmd[2] = (paddr>>16) & 0xFF;
	ctlr->cmd[3] = (paddr>>8) & 0xFF;
	ctlr->cmd[4] = paddr & 0xFF;
	ctlr->cmdlen = 5;
	ctlr->datalen = 0;
	issue(ctlr);
}

void
resetscsi(void)
{
	uchar rs;
	ulong port;

	/*
	 * For the moment assume the factory default settings.
	 */
	port = Port;

	/*
	 * Attempt to soft-reset the board and reset
	 * the SCSI bus. If the board state doesn't settle to
	 * idle with mailbox initialisation required, either
	 * it isn't an Adaptec or it's broken.
	 */
	outb(port+Rc, Srst|Scrst);
	delay(500);
	if((rs = inb(port+Rs)) != (Init|Idle)){
		print("adaptec%d: reset status #%2.2ux\n", 0, rs);
		return;
	}

	setvec(Int0vec+Irq, interrupt);
	reset(&softctlr[0], port, CtlrID);
}

/*
 * Known to devscsi.c.
 */
int scsidebugs[8];
int scsiownid = CtlrID;

void
initscsi(void)
{
}

/*
 * Quick hack. Need to do a better job of dynamic initialisation
 * for machines with peculiar memory/cache restictions.
 * Also, what about 16Mb address limit on the Adaptec?
 */
static ulong bufdatasize;

void
scsibufreset(ulong datasize)
{
	bufdatasize = datasize;
}

Scsibuf *
scsibuf(void)
{
	Scsibuf *b;

	b = smalloc(sizeof(*b));
	b->virt = smalloc(bufdatasize);
	b->phys = (void *)(PADDR(b->virt));
	return b;
}

void
scsifree(Scsibuf *b)
{
	free(b->virt);
	free(b);
}

/*
 * Hack for devvid
 */
Scsibuf *
scsialloc(ulong n)
{
	Scsibuf *b;

	b = smalloc(sizeof(*b));
	b->virt = smalloc(n);
	b->phys = (void *)(PADDR(b->virt));
	return b;
}
.
## diffname pc/adaptec.c 1993/0119
## diff -e /n/bootesdump/1993/0114/sys/src/9/pc/adaptec.c /n/bootesdump/1993/0119/sys/src/9/pc/adaptec.c
371a
	if(ctlr->port == 0)
		error(Enodev);
.
370c
	Ctlr *ctlr = &softctlr[0];
.
## diffname pc/adaptec.c 1993/0220
## diff -e /n/bootesdump/1993/0119/sys/src/9/pc/adaptec.c /n/bootesdump/1993/0220/sys/src/9/pc/adaptec.c
488c
		/*print("adaptec%d: reset status #%2.2ux\n", 0, rs);/**/
.
## diffname pc/adaptec.c 1993/0223
## diff -e /n/bootesdump/1993/0220/sys/src/9/pc/adaptec.c /n/bootesdump/1993/0223/sys/src/9/pc/adaptec.c
490d
487,488c
	if(inb(port+Rs) != (Init|Idle))
.
471d
## diffname pc/adaptec.c 1993/0915
## diff -e /n/bootesdump/1993/0223/sys/src/9/pc/adaptec.c /n/fornaxdump/1993/0915/sys/src/brazil/pc/adaptec.c
493,546c
	return aha1542exec;
.
489,491c
	setvec(Int0vec+isa->irq, interrupt);
	reset(&softctlr[0], isa->port, CtlrID);
.
486,487c
	if(inb(isa->port+Rs) != (Init|Idle))
		return 0;
.
484c
	outb(isa->port+Rc, Hrst|Scrst);
.
479c
	 * Attempt to hard-reset the board and reset
.
476c
	if(isaconfig("scsi", 0, &aha1542) == 0)
		return 0;
.
474c
	 * See if we have any configuration info.
	 * Only one controller for now.
.
471c
	ISAConf *isa = &aha1542;
.
468,469c
static ISAConf aha1542 = {
	"aha1542",
	Port,
	Irq,
	0,
	0,
};

int (*
aha1542reset(void))(Scsi*, int)
.
433c
	if(active.machs > 1)
		unlock(ctlr);
.
390c
	if(active.machs > 1)
		lock(ctlr);
.
367,368c
static int
aha1542exec(Scsi *p, int rw)
.
337c
	if(active.machs > 1)
		unlock(ctlr);
.
317c
	if(active.machs > 1)
		lock(ctlr);
.
14,19d
2c
 * Adaptec AHA-154[02][BC] Intelligent Host Adapter.
 * Needs work:
 *	handle multiple controllers;
 *	set options from user-level;
 *	split out to handle ultrastor too;
.
## diffname pc/adaptec.c 1993/1113 # deleted
## diff -e /n/fornaxdump/1993/0915/sys/src/brazil/pc/adaptec.c /n/fornaxdump/1993/1113/sys/src/brazil/pc/adaptec.c
1,505d

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.