Coherent4.2.10/conf/hai/src/haiscsi.c

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

/* (-lgl
 *	Coherent 386 release 4.2
 *	Copyright (c) 1982, 1994 by Mark Williams Company.
 *	All rights reserved. May not be copied without permission.
 *	For copying permission and licensing info, write licensing@mwc.com
 -lgl) */
/***********************************************************************
 *  Module: haiscsi.c
 *
 *  This is the interface between the Coherent kernel, the host
 *  adapter module and the SCSI device modules. It's just a simple
 *  dispatcher that determines which routine to call based upon the
 *  calling device's Target ID.  The target ID should be set in bits
 *  4-6 of the device's minor number.  
 *
 *  Copyright (c) 1993, Christopher Sean Hilton, All Rights Reserved.
 *
 *  Last Modified: Wed Jun 15 11:17:00 1994 by [chris]
 */

/***********************************************************************
 * $Id$
 *
 * $Log$
 */

static char rcsid[] = "@(#) haiscsi.c $Revision$";
 
#include <sys/coherent.h>

#include <stddef.h>

#include <errno.h>
#include <sys/buf.h>
#include <sys/cmn_err.h>
#include <sys/con.h>
#include <sys/haiioctl.h>
#include <sys/haiscsi.h>
#include <sys/io.h>
#include <sys/sched.h>
#include <sys/stat.h>
#include <sys/uproc.h>

#include <coh/proc.h>

#define LOCAL	static
#if __GNUC__
#define Register
#else
#define Register register
#endif

/* Configurable variables - see /etc/conf/hai/Space.c. */
extern int HAI_DISK;
extern int HAI_TAPE;
extern int HAI_CDROM;

/*
 * HAI_HAINDEX is the configurable variable to select the host adapter that
 * This variable is the index into the master host adapter table that is
 * set up in the scsi_load routine.
 *
 * 0 = Adaptec 154x.
 * 1 = Seagate/Future Domain 8-bit.
 */

extern int HAI_HAINDEX;		/* Host adapter index */

/*
 * Device function tables.
 */

extern dca_p    sddca;		/* Fixed disk control routines */
extern dca_p    ctdca;		/* Cartridge tape control routines */

#if _HAICD_OK
extern dca_p    cdrdca;		/* CD-ROM control routines */
#endif

dca_p  mdca [MAXDEVS];		/* Initialized by setup_mdca(). */

/*
 * Host adapter function tables. If you add a host adapter, the
 * function table pointer goes here.
 * HAI_HAINDEX refers to the type of host adapter. To add 
 * host adapters add an extern pointer to its function table here
 * and then append that pointers address to the end of this table.
 */

extern haft_t	* _154x_haftp;	/* Adaptec AHA-154x and compatibles */
extern haft_t	* ss_haftp;	/* Future Domain TMC-950/9C50 based hosts */

static haft_t	* haftp;	/* Host adapter function table */


static char *errstr[] = {
	"No sense",
	"Recovered error",
	"Not ready",
	"Medium error",
	"Hardware error",
	"Illegal request",
	"Unit attention",
	"Data protect",
	"Blank check",
	"Vendor unique error",
	"Copy Aborted",
	"Aborted command",
	"Equal",
	"Volume overflow",
	"Miscompare",
	"Reserved"
};

/* char iofailmsg[] = "%s: status(0x%x)"; */

/***********************************************************************
 *  int scsi_open(dev_t dev, int mode)
 *
 *  Open a device on the SCSI bus at target ID: tid(dev). This is
 *  Accomplished by calling the open routine at mdca[tid(dev)]->d_open
 */

#if __USE_PROTO__
LOCAL void scsi_open(Register dev_t dev, unsigned mode)
#else
LOCAL void 
scsi_open(dev, mode)
Register dev_t   dev;
unsigned mode;
#endif
{
	Register dca_p d = mdca[tid(dev)];

	if (!haftp->hf_present || !d)
		set_user_error (EINVAL);
	else
		(* d->d_open) (dev, mode);
}   /* scsi_open() */

/***********************************************************************
 *  void scsi_close()
 *
 *  Close entry point for all devices at major index SCSIMAJOR.
 */

#if __USE_PROTO__
LOCAL void scsi_close(Register dev_t dev)
#else
LOCAL void 
scsi_close(dev)
Register dev_t dev;
#endif
{
	Register dca_p d = mdca[tid(dev)];

	if (!haftp->hf_present || !d)
		set_user_error (EINVAL);
	else
		(* d->d_close)(dev);
}   /* scsi_close() */

/***********************************************************************
 *  void scsi_block()
 *
 *  Block Entry point.
 */

#if __USE_PROTO__
LOCAL void scsi_block(Register BUF *bp)
#else
LOCAL void scsi_block(bp)
Register BUF *bp;
#endif
{
	Register dca_p d = mdca[tid(bp->b_dev)];

	if (!haftp->hf_present || !d) {
		bp->b_resid = bp->b_count;
		bp->b_flag |= BFERR;
		bdone(bp);
	}
	else
		(*(d->d_block))(bp);
}   /* scsi_block() */

/***********************************************************************
 *  void scsi_read()
 *
 *  Read entry point.
 */

#if __USE_PROTO__
LOCAL void scsi_read(Register dev_t dev, Register IO *iop)
#else
LOCAL void scsi_read(dev, iop)
Register dev_t  dev;
Register IO  	*iop;
#endif
{
	Register dca_p d = mdca [tid(dev)];

	if (!haftp->hf_present || !d)
		set_user_error (EINVAL);
	else
		(* d->d_read)(dev, iop);
}   /* scsi_read() */

/***********************************************************************
 *  int scsi_write()
 *
 *  Write entry point.
 */

#if __USE_PROTO__
LOCAL void scsi_write(Register dev_t dev, IO  *iop)
#else
LOCAL void scsi_write(dev, iop)
Register dev_t  dev;
IO  *iop;
#endif
{
	Register dca_p d = mdca[tid(dev)];
	if (!haftp->hf_present || !d)
		set_user_error (EINVAL);
	else
		(* d->d_write)(dev, iop);
}   /* scsi_write() */

/***********************************************************************
 *  scsi_ioctl()
 *
 *  IO Control entry point.
 */

#if __USE_PROTO__
LOCAL void scsi_ioctl(Register dev_t dev, int cmd, char	vec[])
#else
LOCAL void scsi_ioctl(dev, cmd, vec)
Register dev_t  dev;
int		cmd;
char		vec[];
#endif
{
	Register dca_p d = mdca[tid(dev)];

	if (!haftp->hf_present || !d)
		set_user_error (EINVAL);
	else
		(*d->d_ioctl)(dev, cmd, vec);
}   /* scsi_ioctl() */

/*
 * scsi_timer() -- Timeout service entry point.
 *
 * Called once per second at the request of the host adapter driver. 
 * This routine just downcalls the timer routine for the host adapter.
 * or turns off timer processing if there isn't a timer routine.
 */

#if __USE_PROTO__
LOCAL void scsi_timer(void)
#else
LOCAL void 
scsi_timer()
#endif
{
    if (haftp->hf_timer)
	(*(haftp->hf_timer))();
    else
	drvl[SCSIMAJOR]. d_time = 0;
}

/***********************************************************************
 * setup_mdca
 *
 * Load mdca table based on globals HAI_DISK and HAI_TAPE.
 */

#if __USE_PROTO__
LOCAL void setup_mdca(void)
#else
LOCAL void 
setup_mdca()
#endif
{
	int	id,
		mask;

	for (id = 0; id < 8; id ++) {
		mask = 1 << id;
		if (HAI_DISK & mask)
			mdca[id] = sddca;
		if (HAI_TAPE & mask)
			mdca[id] = ctdca;
#if _HAICD_OK
		if (HAI_CDROM & mask)
			mdca[id] = cdrdca;
#endif
	}
}

/***********************************************************************
 *  scsi_load()
 *
 *  Load Entry point.
 */

#if __USE_PROTO__
LOCAL int scsi_load(void)
#else
LOCAL int 
scsi_load()
#endif
{
	Register int	id;
	Register dca_p  d;

	cmn_err(CE_CONT, "Host Adapter Independent SCSI Driver v1.9\n");

	/* Determine which host adapter is in use. */
	switch (HAI_HAINDEX) {
	case HAI_154X_INDEX:
		haftp = _154x_haftp;
		break;
	case HAI_SS_INDEX:
		haftp = ss_haftp;
		break;
	default:
		cmn_err (CE_WARN, "haiscsi - unrecognized host adapter type");
		return 0;
	}

	/* Make sure the selected host adapter has a function table. */
	if (!haftp) {
	    	cmn_err(CE_WARN,
		  "Host adapter configuration invalid. Initialization failed.");
		return 0;
        }

	haload(haftp);
	if (!haftp->hf_present) {
		cmn_err(CE_WARN, "Host Adapter Initialization failed.");
		return 0;
        }

	setup_mdca();

	for (id = 0; id < MAXDEVS; ++id) {
		if ((d = mdca[id]) && d->d_load) {
			if (!(*(d->d_load))(haftp, id)) {
				cmn_err (CE_WARN,
				  "SCSI target device at id %d did not load.",
				  id);
			}
		}
	}
	return 1;
}   /* scsi_load() */

/***********************************************************************
 *  scsi_unload()
 *
 *  SCSI unload routine.
 */

#if __USE_PROTO__
LOCAL int scsi_unload(void)
#else
LOCAL int 
scsi_unload()
#endif
{
	Register int	id;
	Register dca_p  d;

	for (id = 0; id < MAXDEVS; ++id)
		if ((d = mdca[id]) && d->d_unload)
			(*(d->d_unload))(id);

	return 1;
}   /* scsi_unload() */

/***********************************************************************
 *  Utility Routines
 */

/***********************************************************************
 *  char *swapbytes()
 *
 *  Swap bytes in an object from big to little endian or vice versa.
 */

#if __USE_PROTO__
char *swapbytes(void *mem, size_t size)
#else
char *swapbytes(mem, size)
char	*mem;
size_t  size;
#endif
{
	Register char *p = mem;
	Register char *q = p + size - 1;

	while (q > p) {
		*p ^= *q;
		*q ^= *p;
		*p ^= *q;
		p++;
		q--;
	}
	return mem;
}   /* swapbytes() */

/***********************************************************************
 *  cpycdb()
 *
 *  Copy a SCSI Command/Data Block. Return the number of bytes copied.
 */

#if __USE_PROTO__
int cpycdb(Register cdb_p dst, Register cdb_p src)
#else
int cpycdb(dst, src)
Register cdb_p dst;
Register cdb_p src;
#endif
{
		switch (src->g0.opcode & GROUPMASK) {
		case GROUP0:
			memcpy(dst, src, sizeof(g0cmd_t));
			return sizeof(g0cmd_t);
		case GROUP1:
		case GROUP2:
			memcpy(dst, src, sizeof(g1cmd_t));
			return sizeof(g1cmd_t);
		case GROUP5:
			memcpy(dst, src, sizeof(g5cmd_t));
			return sizeof(g5cmd_t);
		default:
			return 0;
	}
}   /* cpycdb() */

/***********************************************************************
 *  dumpmem()
 *
 *  Dump memory from (p) for (s) bytes.
 */

static char hexchars[] = "0123456789abcdef";
static char linebuf[] = "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
	"| ................\n";

#if __USE_PROTO__
void dumpmem(char m[], unsigned char *p, size_t	s)
#else
void
dumpmem(m, p, s)
char		m[];
unsigned char *	p;
size_t		s;
#endif
{
	Register int i;
	char *l;

	if (m)
		cmn_err(CE_CONT, "!%s", m);
	cmn_err(CE_CONT, "! (0x%x)\n", (unsigned) p);
	memset(linebuf, ' ', sizeof(linebuf) - 2);
	linebuf[48] = '|';
	l = linebuf;
	for (i = 0; i < s; ++i, ++p) {
		*l++ = hexchars[(*p >> 4) & 0x0f];
		*l++ = hexchars[*p & 0x0f];
		*l++ = ' ';
		linebuf[50 + (i & 15)] = (*p >= ' ' && *p <= '~') ? *p : '.';
		if ((i & 15) == 15) {
			cmn_err(CE_CONT, "!%s", linebuf);
			memset(linebuf, ' ', sizeof(linebuf) - 2);
			linebuf[48] = '|';
			l = linebuf;
		}
	}
	if ((s & 15) != 0)
		cmn_err(CE_CONT, "!%s", linebuf);
}   /* dumpmem() */

/***********************************************************************
 *  scsidone()
 *
 *  Wake up processes sleeping on SRB (r).
 */

#if __USE_PROTO__
LOCAL void scsidone(Register srb_p r)
#else
LOCAL void
scsidone(r)
Register srb_p	r;
#endif
{
	wakeup((char *)(&(r->status)));
}

/***********************************************************************
 *  reqsense()
 *
 *  Issue a request sense command device loaded into the target/lun
 *  fields of the given srb r.  Uses v_sleep().
 */

#if __USE_PROTO__
void reqsense(haft_t * haft, Register srb_p r)
#else
void
reqsense(haft, r)
haft_t * haft;
Register srb_p r;
#endif
{
	int		s;
	unsigned short  status;
	unsigned short  tries;
	unsigned short  timeout;
	bufaddr_t	buf;
	unsigned short  xferdir;
	void 		(*cleanup) __PROTO((srb_p));
	cdb_t		cdb;

	if (r->status == ST_CHKCOND) {
		status = ST_CHKCOND;
		tries = r->tries;
		timeout = r->timeout;
		memcpy(&buf, &(r->buf), sizeof(bufaddr_t));
		xferdir = r->xferdir;
		cleanup = r->cleanup;
		memcpy(&cdb, &(r->cdb), sizeof(cdb_t));

		r->timeout = 4;
		r->tries = 10;
		r->buf.space = KRNL_ADDR;
		r->buf.addr.caddr = (caddr_t) r->sensebuf;
		r->buf.size = sizeof(r->sensebuf);
		r->xferdir = DMAREAD;
		r->cleanup = scsidone;
		memset(&(r->cdb), 0, sizeof(cdb_t));
		r->cdb.g0.opcode = 0x03;
		r->cdb.g0.lun_lba = (r->lun << 5);
		r->cdb.g0.xfr_len = r->buf.size;
		s = sphi();
		startscsi(haft, r);
		while (r->status == ST_PENDING) {
			if (x_sleep((char *)(&(r->status)), pritape,
			  slpriSigCatch, "reqsense")) {
				set_user_error (EINTR);
				status = ST_USRABRT;
				break;
			}
		}
		spl(s);

		r->tries = tries;
		r->timeout = timeout;
		memcpy(&(r->buf), &buf, sizeof(bufaddr_t));
		r->xferdir = xferdir;
		r->cleanup = cleanup;
		memcpy(&(r->cdb), &(cdb), sizeof(cdb_t));
		r->status = status;
	}
}   /* reqsense() */

/***********************************************************************
 *  doscsi()
 *
 *  An alternative to startscsi() which handles everything including
 *  any request sense commands necessary if the command failed. All
 *  information is returned in given srb. Note:  you can only use
 *  this routine when the u structure for a process is available (from
 *  an open, close, read, write, or ioctl routine). Since this calls
 *  sleep it will screw things up something fierce if you call it from
 *  a load, unload, block, timer or interrupt routine. Also note
 *  that some host adapters do the sense part this automatically.
 */
#if __USE_PROTO__
void doscsi(haft_t * haft,
	    srb_p r, 
	    int retrylimit,
	    int schedPri, 
	    int sleepPri,
	    char reason[])
#else
void
doscsi(haft, r, retrylimit, schedPri, sleepPri, reason)
haft_t *	haft;
srb_p		r;
int		retrylimit;
int		schedPri;
int		sleepPri;
char		reason[];
#endif
{
	int	 s;

	r->cleanup = scsidone;
	for (r->tries = 0; r->tries < retrylimit; ) {
		if (startscsi(haft, r)) {
			s = sphi();
			while (r->status == ST_PENDING) {
				if (x_sleep ((char *)(& r->status), schedPri,
				  sleepPri, reason)) {
					abortscsi(haft, r);
					r->status = ST_USRABRT;
					set_user_error (EINTR);
				}
			}
			spl(s);
			if (r->status == ST_GOOD
			  || r->status == ST_USRABRT
			  || r->status == ST_DRVABRT)
				return;

			if (r->status == ST_CHKCOND)
				reqsense(haft, r);
		}
		else
			r->status = ST_TIMEOUT;
	}
}   /* doscsi() */

/***********************************************************************
 *  printsense()
 *
 *  Print out the results in the given sense buffer. This is done
 *  in English, almost.
 */

#if __USE_PROTO__
void printsense(Register dev_t dev, Register char msg[], Register extsense_p e)
#else
void
printsense(dev, msg, e)
Register dev_t	  dev;
Register char	   *msg;
Register extsense_p e;
#endif
{
	long info;
	if ((e->errorcode & 0x70) != 0x70)
		cmn_err(CE_WARN,
			"(%d ,%d) %s: Bad sensekey",
			major(dev),
			minor(dev),
			msg);
	else {
		if ((e->errorcode & 0x80) != 0x80)
			cmn_err(CE_WARN,
				"(%d, %d) %s: %s - key: (0x%x)",
				major(dev),
				minor(dev),
				msg,
				errstr[e->sensekey & 0x0f],
				(e->sensekey & 0xe0));
		else {
			info = (long) e->info;
			flip(info);
			cmn_err(CE_WARN,
				"(%d, %d) %s: %s - addr: %d key: (0x%x)",
				major(dev),
				minor(dev),
				msg,
				errstr[e->sensekey & 0x0f],
				info,
				(e->sensekey & 0xe0));
		}
	}
}   /* printsense() */

/***********************************************************************
 *  printerror()
 *  
 *  Print an error after command completion. Be silent if the command
 *  was aborted by the user.
 */

#if __USE_PROTO__
int printerror(Register srb_p r, Register char msg[])
#else
int
printerror(r, msg)
Register srb_p r;
Register char *msg;
#endif
{
	if (r->status == ST_USRABRT)
		return 0;
	else {
		if (r->status != ST_CHKCOND)
			cmn_err(CE_WARN,
				"(%d, %d) %s: status(0x%x)",
				major(r->dev),
				minor(r->dev),
				msg,
				r->status);
		else
			printsense(r->dev, msg, (extsense_p) r->sensebuf);
		return 1;
	}
}   /* printerror() */

/***********************************************************************
 *  haiioctl()  --	  I/O Controls common to all devices.
 *  
 *  This function provides I/O Control functions common to all SCSI
 *  devices. The chain of operation should be as follows:
 *  
 *  You:
 *	  1)  Make sure that the device is in an appropriate state to
 *		  perform the I/O Control. (It might not be a good idea to
 *		  format the disk drive with the root partition).
 *  
 *	  2)  Call haiioctl() with your srb and cmd from I/O Control.
 */

#if __USE_PROTO__
void haiioctl(Register srb_p r, /* Device's srb */
	      Register int cmd,	/* Command to do */
	      Register char vec[]) /* Additional information (if needed) */
#else
void
haiioctl(r, cmd, vec)
Register srb_p  r;		/* Device's srb */
Register int	cmd;		/* Command to do */
Register char   *vec;		/* Additional information (if needed) */
#endif
{
	haiusercdb_t h;
	
	switch (cmd) {
	case HAIINQUIRY:
	case HAIMDSNS0:
	case HAIMDSLCT0:
	case HAIMDSNS2:
	case HAIMDSLCT2:
		set_user_error (ENXIO);
		return;

	case HAIUSERCDB:
		if (!ukcopy(vec, &h, sizeof(haiusercdb_t)))
			return;
		r->buf.space = USER_ADDR;
		r->buf.addr.caddr = vec + sizeof(haiusercdb_t);
		r->buf.size = h.buflen;
		r->xferdir = h.xferdir;
		r->timeout = h.timeout;
		memcpy(&(r->cdb), &(h.cdb), sizeof(cdb_t));
		doscsi(haftp, r, 1, pritty, slpriSigCatch, "haiioctl");
		putusd((((haiusercdb_p) vec)->status), r->status);
		putusd((((haiusercdb_p) vec)->hastat), r->hastat);
		kucopy(r->sensebuf, ((haiusercdb_p) vec)->sensebuf,
		  HAI_SENSELEN);
		if (r->status != ST_GOOD)
			set_user_error (ENXIO);
		return;
	
	default:
		set_user_error (ENXIO);
		break;
	}
}   /* haiioctl() */

/***********************************************************************
 *  hainonblk()	 --  Block entry point for devices that shouldn't
 *					  have block entry points.
 *  
 *  Since this is a multiplexing driver and some devices behind it
 *  will have block entry points and some shouldn't. ALL do.
 */

#if __USE_PROTO__
void hainonblk(BUF *bp)
#else
void
hainonblk(bp)
BUF	*bp;
#endif
{
	bp->b_flag |= BFERR;
	bdone (bp);
}   /* hainonblk() */

CON scsicon = {
	DFBLK | DFCHR,
	SCSIMAJOR,
	scsi_open,		/* Open entry point */
	scsi_close,		/* Close entry point */
	scsi_block,		/* Block entry point. */
	scsi_read,		/* Read Entry point */
	scsi_write,		/* write entry point */
	scsi_ioctl,		/* IO control entry point */
	NULL,			/* No powerfail entry (yet?) */
	scsi_timer,		/* timeout entry point */
	scsi_load,		/* Load entry point */
	scsi_unload,		/* Unload entry point */
	NULL			/* No poll entry yet either. */
};
/* End of file: haiscsi.c */