*BSD News Article 5939


Return to BSD News archive

Newsgroups: comp.unix.bsd
Path: sserve!manuel!munnari.oz.au!network.ucsd.edu!swrinde!cs.utexas.edu!sun-barr!ames!agate!tfs.com!tfs.com!julian
From: julian@tfs.com (Julian Elischer)
Subject: New scsi system beta3 (part 5 of       10)
Message-ID: <1992Oct3.040155.13763@tfs.com>
Organization: TRW Financial Systems
Date: Sat, 3 Oct 1992 04:01:55 GMT
Lines: 1315

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	sys/chio.h
#	scsi/scsi_changer.h
#	scsi/ch.c
#
echo x - sys/chio.h
sed 's/^X//' >sys/chio.h << 'END-of-sys/chio.h'
X/*
X * Copyright (c) 1982, 1986 The Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by the University of
X *	California, Berkeley and its contributors.
X * 4. Neither the name of the University nor the names of its contributors
X *    may be used to endorse or promote products derived from this software
X *    without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X *
X *	@(#)chio.h	7.6 (Berkeley) 2/5/91
X */
X
X/* This is a "convertet" mtio.h from 386BSD 
X   Stefan Grefen grefen@goofy.zdv.uni-mainz.de 
X */
X
X/*
X * Structures and definitions for changer io control commands
X */
X#ifndef _CHIO_H_
X#define _CHIO_H_
X
X#define CH_INVERT		0x10000
X#define CH_ADDR_MASK		0xffff
Xstruct chop {
X	short	ch_op;		/* operations defined below */
X	short	result;		/* The result		    */
X	union {
X	   struct {
X		int chm;		/* Transport element */
X		int from;
X		int to;
X	   } move;
X	   struct {
X		int chm;		/* Transport element */
X		int to;
X	   } position; 
X	   struct {
X	        short   chmo;                   /* Offset of first CHM */
X	        short   chms;                   /* No. of CHM */
X	        short   slots;                  /* No. of Storage Elements */
X                short   sloto;                  /* Offset of first SE */
X                short   imexs;                  /* No. of Import/Export Slots */
X                short   imexo;                  /* Offset of first IM/EX */
X                short   drives;                 /* No. of CTS */
X                short   driveo;                 /* Offset of first CTS */
X                short   rot;                    /* CHM can rotate */
X	   } getparam;
X	   struct {
X		int type;
X#define CH_CHM	1
X#define CH_STOR	2
X#define CH_IMEX	3
X#define CH_CTS	4
X		int from;
X		struct {
X			u_char elema_1;
X			u_char elema_0;
X			u_char full:1;
X			u_char rsvd:1;
X			u_char except:1;
X			u_char :5;
X			u_char rsvd2;
X			union {
X				struct {
X				u_char add_sense_code;
X				u_char add_sense_code_qualifier;
X				} specs;
X				short add_sense;
X/* WARINING LSB only */
X#define CH_CHOLDER	0x0290	/* Cartridge holder is missing */
X#define CH_STATUSQ	0x0390	/* Status is questionable */
X#define CH_CTS_CLOSED	0x0490	/* CTS door is closed */
X
X			} ch_add_sense;
X			u_char rsvd3[3];
X			u_char :6;
X			u_char invert:1;
X			u_char svalid:1;
X			u_char source_1;
X			u_char source_0;
X			u_char rsvd4[4];
X			} elem_data;
X		} get_elem_stat;
X	} u;
X};
X
X/* operations */
X#define CHMOVE				1
X#define CHPOSITION			2
X#define CHGETPARAM			3
X#define CHGETELEM			4
X
X
X/* Changer IO control command */
X#define	CHIOOP	_IOWR('c', 1, struct chop)	/* do a mag tape op */
X#endif
END-of-sys/chio.h
echo x - scsi/scsi_changer.h
sed 's/^X//' >scsi/scsi_changer.h << 'END-of-scsi/scsi_changer.h'
X/*
X * HISTORY
X * $Log: scsi_tape.h,v $
X * 
X */
X
X/*
X * SCSI changer interface description
X */
X
X/*
X * Written by Stefan Grefen   (grefen@goofy.zdv.uni-mainz.de soon grefen@convex.com)
X * based on the SCSI System by written Julian Elischer (julian@tfs.com)
X * for TRW Financial Systems.
X *
X * TRW Financial Systems, in accordance with their agreement with Carnegie
X * Mellon University, makes this software available to CMU to distribute
X * or use in any manner that they see fit as long as this message is kept with 
X * the software. For this reason TFS also grants any other persons or
X * organisations permission to use or modify this software.
X *
X * TFS supplies this software to be publicly redistributed
X * on the understanding that TFS is not responsible for the correct
X * functioning of this software in any circumstances.
X *
X */
X
X/*
X * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
X */
X
X/*
X * SCSI command format
X */
Xstruct scsi_read_element_status
X{
X        u_char  op_code;
X	u_char	element_type_code:4;
X	u_char  voltag:1;
X        u_char  lun:3;
X	u_char	starting_element_addr[2];
X	u_char	number_of_elements[2];
X	u_char	resv1;
X	u_char	allocation_length[3];
X	u_char	resv2;
X        u_char  link:1;
X        u_char  flag:1;
X        u_char  :6;
X};
X#define RE_ALL_ELEMENTS			0
X#define RE_MEDIUM_TRANSPORT_ELEMENT	1
X#define RE_STORAGE_ELEMENT		2
X#define RE_IMPORT_EXPORT		3
X#define RE_DATA_TRANSFER_ELEMENT	4
X
Xstruct scsi_move_medium
X{
X	u_char  op_code;
X	u_char	:5;
X	u_char	lun:3;
X	u_char  transport_element_address[2];
X	u_char  source_address[2];
X	u_char  destination_address[2];
X	u_char  rsvd[2];
X	u_char  invert:1;
X	u_char	:7;
X        u_char  link:1;
X        u_char  flag:1;
X        u_char  :6;
X};
X
Xstruct scsi_position_to_element
X{
X	u_char  op_code;
X	u_char	:5;
X	u_char	lun:3;
X	u_char  transport_element_address[2];
X	u_char  source_address[2];
X	u_char  rsvd[2];
X	u_char  invert:1;
X	u_char	:7;
X        u_char  link:1;
X        u_char  flag:1;
X        u_char  :6;
X};
X	
X/*
X * Opcodes
X */
X#define POSITION_TO_ELEMENT     0x2b
X#define MOVE_MEDIUM             0xa5
X#define READ_ELEMENT_STATUS     0xb8
X
Xstruct scsi_element_status_data 
X{
X	u_char	first_element_reported[2];
X	u_char	number_of_elements_reported[2];
X	u_char  rsvd;
X	u_char	byte_count_of_report[3];
X};
X
Xstruct element_status_page 
X{
X	u_char	element_type_code;
X	u_char	:5;
X	u_char avoltag:1;
X	u_char pvoltag:1;
X	u_char element_descriptor_length[2];
X	u_char rsvd;
X	u_char byte_count_of_descriptor_data[3];
X};
X
END-of-scsi/scsi_changer.h
echo x - scsi/ch.c
sed 's/^X//' >scsi/ch.c << 'END-of-scsi/ch.c'
X/* 
X * Mach Operating System
X * Copyright (c) 1990 Carnegie-Mellon University
X * Copyright (c) 1989 Carnegie-Mellon University
X * All rights reserved.  The CMU software License Agreement specifies
X * the terms and conditions for use and redistribution.
X */
X/*
X * HISTORY
X * 
X * 
X */
X
X#include	<sys/types.h>
X#include	<ch.h>
X
X#include <sys/param.h>
X#include <sys/systm.h>
X
X#include <sys/errno.h>
X#include <sys/ioctl.h>
X#include <sys/buf.h>
X#include <sys/proc.h>
X#include <sys/user.h>
X#include <sys/chio.h>
X
X#if defined(OSF)
X#define SECSIZE	512
X#endif /* defined(OSF) */
X
X#include <scsi/scsi_all.h>
X#include <scsi/scsi_changer.h>
X#include <scsi/scsiconf.h>
X
X
Xstruct  scsi_xfer ch_scsi_xfer[NCH];
Xint     ch_xfer_block_wait[NCH];
X
X
X#define PAGESIZ 	4096
X#define STQSIZE		4
X#define	CH_RETRIES	4
X
X
X#define MODE(z)		(  (minor(z) & 0x0F) )
X#define UNIT(z)		(  (minor(z) >> 4) )
X
X#ifndef	MACH
X#define ESUCCESS 0
X#endif	MACH
X
Xint	ch_info_valid[NCH];	/* the info about the device is valid */
Xint	ch_initialized[NCH] ;
Xint	ch_debug = 1;
X
Xint chattach();
Xint ch_done();
Xstruct	ch_data
X{
X	int	flags;
X	struct	scsi_switch *sc_sw;	/* address of scsi low level switch */
X	int	ctlr;			/* so they know which one we want */
X	int	targ;			/* our scsi target ID */
X	int	lu;			/* out scsi lu */
X	short	chmo;			/* Offset of first CHM */
X	short	chms;			/* No. of CHM */
X	short	slots;			/* No. of Storage Elements */
X	short	sloto;			/* Offset of first SE */
X	short	imexs;			/* No. of Import/Export Slots */
X	short	imexo;			/* Offset of first IM/EX */
X	short	drives;			/* No. of CTS */
X	short	driveo;			/* Offset of first CTS */
X	short	rot;			/* CHM can rotate */
X	u_long	op_matrix;		/* possible opertaions */
X	u_short lsterr;		/* details of lasterror */
X	u_char	stor;			/* posible Storage locations */
X}ch_data[NCH];
X
X#define CH_OPEN		0x01
X#define CH_KNOWN	0x02
X
Xstatic	int	next_ch_unit = 0;
X/***********************************************************************\
X* The routine called by the low level scsi routine when it discovers	*
X* A device suitable for this driver					*
X\***********************************************************************/
X
Xint	chattach(ctlr,targ,lu,scsi_switch)
Xstruct	scsi_switch *scsi_switch;
X{
X	int	unit,i,stat;
X	unsigned char *tbl;
X
X	if(scsi_debug & PRINTROUTINES) printf("chattach: ");
X	/*******************************************************\
X	* Check we have the resources for another drive		*
X	\*******************************************************/
X	unit = next_ch_unit++;
X	if( unit >= NCH)
X	{
X		printf("Too many scsi changers..(%d > %d) reconfigure kernel",(unit + 1),NCH);
X		return(0);
X	}
X	/*******************************************************\
X	* Store information needed to contact our base driver	*
X	\*******************************************************/
X	ch_data[unit].sc_sw	=	scsi_switch;
X	ch_data[unit].ctlr	=	ctlr;
X	ch_data[unit].targ	=	targ;
X	ch_data[unit].lu	=	lu;
X
X	/*******************************************************\
X	* Use the subdriver to request information regarding	*
X	* the drive. We cannot use interrupts yet, so the	*
X	* request must specify this.				*
X	\*******************************************************/
X	if((ch_mode_sense(unit,  SCSI_NOSLEEP |  SCSI_NOMASK /*| SCSI_SILENT*/)))
X	{
X		printf("	ch%d: scsi changer, %d slot(s) %d drive(s) %d arm(s) %d i/e-slot(s) \n",
X			unit, ch_data[unit].slots, ch_data[unit].drives, ch_data[unit].chms, ch_data[unit].imexs);
X		stat=CH_KNOWN;
X	}
X	else
X	{
X		printf("	ch%d: scsi changer :- offline\n", unit);
X		stat=CH_OPEN;
X	}
X	ch_initialized[unit] = stat;
X
X	return;
X
X}
X
X
X
X/*******************************************************\
X*	open the device.				*
X\*******************************************************/
Xchopen(dev)
X{
X	int errcode = 0;
X	int unit,mode;
X
X	unit = UNIT(dev);
X	mode = MODE(dev);
X
X	/*******************************************************\
X	* Check the unit is legal                               *
X	\*******************************************************/
X	if ( unit >= NCH )
X	{
X		printf("ch %d  > %d\n",unit,NCH);
X		errcode = ENXIO;
X		return(errcode);
X	}
X	/*******************************************************\
X	* Only allow one at a time				*
X	\*******************************************************/
X	if(ch_data[unit].flags & CH_OPEN)
X	{
X		printf("CH%d already open\n",unit);
X		errcode = ENXIO;
X		goto bad;
X	}
X	
X	if(ch_debug||(scsi_debug & (PRINTROUTINES | TRACEOPENS)))
X		printf("chopen: dev=0x%x (unit %d (of %d))\n"
X				,   dev,      unit,   NCH);
X	/*******************************************************\
X	* Make sure the device has been initialised		*
X	\*******************************************************/
X
X	if (!ch_initialized[unit])
X		return(ENXIO);
X	if (ch_initialized[unit]!=CH_KNOWN) {
X		if((ch_mode_sense(unit,  SCSI_NOSLEEP |  SCSI_NOMASK /*| SCSI_SILENT*/)))
X		{
X			ch_initialized[unit]=CH_KNOWN;
X		}
X	  else
X		{
X			printf("  ch%d: scsi changer :- offline\n", unit);
X			return(ENXIO);
X		}
X	}
X	/*******************************************************\
X	* Check that it is still responding and ok.		*
X	\*******************************************************/
X
X	if(ch_debug || (scsi_debug & TRACEOPENS))
X		printf("device is ");
X	if (!(ch_req_sense(unit, 0)))
X	{
X		errcode = ENXIO;
X		if(ch_debug || (scsi_debug & TRACEOPENS))
X			printf("not responding\n");
X		goto bad;
X	}
X	if(ch_debug || (scsi_debug & TRACEOPENS))
X		printf("ok\n");
X
X	if(!(ch_test_ready(unit,0)))
X	{
X		printf("ch%d not ready\n",unit);
X		return(EIO);
X	}
X
X	ch_info_valid[unit] = TRUE;
X
X	/*******************************************************\
X	* Load the physical device parameters			*
X	\*******************************************************/
X
X	ch_data[unit].flags = CH_OPEN;
X	return(errcode);
Xbad:
X	return(errcode);
X}
X
X/*******************************************************\
X* close the device.. only called if we are the LAST	*
X* occurence of an open device				*
X\*******************************************************/
Xchclose(dev)
X{
X	unsigned char unit,mode;
X
X	unit = UNIT(dev);
X	mode = MODE(dev);
X
X	if(scsi_debug & TRACEOPENS)
X		printf("Closing device");
X	ch_data[unit].flags = 0;
X	return(0);
X}
X
X
X/*******************************************************\
X* Actually translate the requested transfer into	*
X* one the physical driver can understand		*
X* The transfer is described by a buf and will include	*
X* only one physical transfer.				*
X\*******************************************************/
X
Xint	chstrategy(bp)
Xstruct	buf	*bp;
X{
X	struct	buf	*dp;
X	unsigned char unit;
X	unsigned int opri;
X
X	bp->b_error = EIO;
X
X	bp->b_flags |= B_ERROR;
X
X	/*******************************************************\
X	* Correctly set the buf to indicate a completed xfer	*
X	\*******************************************************/
X	iodone(bp);
X	return;
X}
X
X
X/***************************************************************\
X* chstart                                                       *
X* This routine is also called after other non-queued requests	*
X* have been made of the scsi driver, to ensure that the queue	*
X* continues to be drained.					*
X\***************************************************************/
X/* chstart() is called at splbio */
Xchstart(unit)
X{
X	int			drivecount;
X	register struct buf	*bp = 0;
X	register struct buf	*dp;
X	struct	scsi_xfer	*xs;
X	int			blkno, nblk;
X
X
X	if(scsi_debug & PRINTROUTINES) printf("chstart%d ",unit);
X	/*******************************************************\
X	* See if there is a buf to do and we are not already	*
X	* doing one						*
X	\*******************************************************/
X	xs=&ch_scsi_xfer[unit];
X	if(xs->flags & INUSE)
X	{
X		return;    /* unit already underway */
X	}
X	if(ch_xfer_block_wait[unit]) /* a special awaits, let it proceed first */
X	{
X		wakeup(&ch_xfer_block_wait[unit]);
X		return;
X	}
X
X	return;
X
X}
X
X
X/*******************************************************\
X* This routine is called by the scsi interrupt when	*
X* the transfer is complete.
X\*******************************************************/
Xint	ch_done(unit,xs)
Xint	unit;
Xstruct	scsi_xfer	*xs;
X{
X	struct	buf		*bp;
X	int	retval;
X
X	if(ch_debug||(scsi_debug & PRINTROUTINES)) printf("ch_done%d ",unit);
X	if (! (xs->flags & INUSE))
X		panic("scsi_xfer not in use!");
X	wakeup(xs);
X}
X/*******************************************************\
X* Perform special action on behalf of the user		*
X* Knows about the internals of this device		*
X\*******************************************************/
Xchioctl(dev, cmd, arg, mode)
Xdev_t dev;
Xint cmd;
Xcaddr_t arg;
X{
X	/* struct ch_cmd_buf *args;*/
X	union scsi_cmd *scsi_cmd;
X	register i,j;
X	unsigned int opri;
X	int errcode = 0;
X	unsigned char unit;
X	int number,flags,ret;
X
X	/*******************************************************\
X	* Find the device that the user is talking about	*
X	\*******************************************************/
X	flags = 0;	/* give error messages, act on errors etc. */
X	unit = UNIT(dev);
X
X	switch(cmd)
X	{
X		case CHIOOP: {
X			struct chop *ch=(struct chop *) arg;
X			if (ch_debug)
X				printf("[chtape_chop: %x]\n", ch->ch_op);
X
X			 switch ((short)(ch->ch_op)) {
X				case CHGETPARAM:
X					ch->u.getparam.chmo=	 ch_data[unit].chmo;
X					ch->u.getparam.chms=   ch_data[unit].chms;
X					ch->u.getparam.sloto=  ch_data[unit].sloto;
X					ch->u.getparam.slots=  ch_data[unit].slots;
X					ch->u.getparam.imexo=  ch_data[unit].imexo;
X					ch->u.getparam.imexs=  ch_data[unit].imexs;
X					ch->u.getparam.driveo= ch_data[unit].driveo;
X					ch->u.getparam.drives= ch_data[unit].drives;
X					ch->u.getparam.rot=    ch_data[unit].rot;
X					ch->result=0;
X					return 0;
X					break;
X				case CHPOSITION: 
X				 return ch_position(unit,&ch->result,ch->u.position.chm,
X						ch->u.position.to,
X						flags);
X				case CHMOVE: 
X				 return ch_move(unit,&ch->result, ch->u.position.chm,
X					ch->u.move.from, ch->u.move.to,
X								flags);
X				case CHGETELEM:
X				 return ch_getelem(unit,&ch->result, ch->u.get_elem_stat.type,
X					ch->u.get_elem_stat.from, &ch->u.get_elem_stat.elem_data,
X									flags);
X				default:
X				 return EINVAL;
X		}
X
X	 }
X#ifdef	NOT_NOW
X	/*******************************************************\
X	* This is a direct command for the scsi driver, pass	*
X	* it on..						*
X	\*******************************************************/
X	case A_CMD:
X		args = (struct ch_cmd_buf *) arg;
X		ch_cmd(args->byte[0], args->byte[1], args->byte, args->byte[3], args->byte[4], args->byte[5], args->byte[6], args->byte[7]);
X		break;
X	/*******************************************************\
X	* This is a low level scsi command ... just do it	*
X	\*******************************************************/
X	case A_SCSI: 
X		/* will not work yet... fix it later.... 
X		errcode = ch_scsi_cmd(UNIT(dev),
X				arg,
X				/* len */ 10,
X				ccb_data,
X				sizeof(ccb_data),
X				100000,
X				0);
X		break;
X#endif	NOT_NOW
X	default:
X	 return EINVAL;
X	}
X
X	return(ret?ESUCCESS:EIO);
X}
X
Xch_getelem(unit,stat,type,from,data,flags)
Xint unit,from,flags;
Xshort *stat;
Xchar *data;
X{
X	struct scsi_read_element_status scsi_cmd;
X	char elbuf[32];
X	int ret;
X
X	bzero(&scsi_cmd, sizeof(scsi_cmd));
X	scsi_cmd.op_code = READ_ELEMENT_STATUS;
X	scsi_cmd.element_type_code=type;
X	scsi_cmd.starting_element_addr[0]=(from>>8)&0xff;
X	scsi_cmd.starting_element_addr[1]=from&0xff;
X	scsi_cmd.number_of_elements[1]=1;
X	scsi_cmd.allocation_length[2]=32;
X	
X	if ((ret=ch_scsi_cmd(unit,
X			&scsi_cmd,
X			sizeof(scsi_cmd),
X			elbuf,
X			32,
X			100000,
X			flags) !=ESUCCESS)) {
X			*stat=ch_data[unit].lsterr;
X			bcopy(elbuf+16,data,16);
X			return ret;
X			}
X	bcopy(elbuf+16,data,16); /*Just a hack sh */
X	return ret;
X}
X
Xch_move(unit,stat,chm,from,to,flags)
Xint unit,chm,from,to,flags;
Xshort *stat;
X{
X	struct scsi_move_medium scsi_cmd;
X	int ret;
X
X	bzero(&scsi_cmd, sizeof(scsi_cmd));
X	scsi_cmd.op_code = MOVE_MEDIUM;
X	scsi_cmd.transport_element_address[0]=(chm>>8)&0xff;
X	scsi_cmd.transport_element_address[1]=chm&0xff;
X	scsi_cmd.source_address[0]=(from>>8)&0xff;
X	scsi_cmd.source_address[1]=from&0xff;
X	scsi_cmd.destination_address[0]=(to>>8)&0xff;
X	scsi_cmd.destination_address[1]=to&0xff;
X	scsi_cmd.invert=(chm&CH_INVERT)?1:0;
X	if ((ret=ch_scsi_cmd(unit,
X			&scsi_cmd,
X			sizeof(scsi_cmd),
X			NULL,
X			0,
X			100000,
X			flags) !=ESUCCESS)) {
X			*stat=ch_data[unit].lsterr;
X			return ret;
X			}
X	return ret;
X}
X
Xch_position(unit,stat,chm,to,flags)
Xint unit,chm,to,flags;
Xshort *stat;
X{
X	struct scsi_position_to_element scsi_cmd;
X	int ret;
X
X	bzero(&scsi_cmd, sizeof(scsi_cmd));
X	scsi_cmd.op_code = POSITION_TO_ELEMENT;
X	scsi_cmd.transport_element_address[0]=(chm>>8)&0xff;
X	scsi_cmd.transport_element_address[1]=chm&0xff;
X	scsi_cmd.source_address[0]=(to>>8)&0xff;
X	scsi_cmd.source_address[1]=to&0xff;
X	scsi_cmd.invert=(chm&CH_INVERT)?1:0;
X	if ((ret=ch_scsi_cmd(unit,
X			&scsi_cmd,
X			sizeof(scsi_cmd),
X			NULL,
X			0,
X			100000,
X			flags) !=ESUCCESS)) {
X			*stat=ch_data[unit].lsterr;
X			return ret;
X			}
X	return ret;
X}
X
X/*******************************************************\
X* Check with the device that it is ok, (via scsi driver)*
X\*******************************************************/
Xch_req_sense(unit, flags)
Xint	flags;
X{
X	struct	scsi_sense_data sense;
X	struct scsi_sense scsi_cmd;
X
X	bzero(&scsi_cmd, sizeof(scsi_cmd));
X	scsi_cmd.op_code = REQUEST_SENSE;
X	scsi_cmd.length = sizeof(sense);
X
X	if (ch_scsi_cmd(unit,
X			&scsi_cmd,
X			sizeof(struct	scsi_sense),
X			&sense,
X			sizeof(sense),
X			100000,
X			flags | SCSI_DATA_IN) != 0)
X	{
X		return(FALSE);
X	}
X	else 
X		return(TRUE);
X}
X
X/*******************************************************\
X* Get scsi driver to send a "are you ready" command	*
X\*******************************************************/
Xch_test_ready(unit,flags)
Xint	unit,flags;
X{
X	struct scsi_test_unit_ready scsi_cmd;
X
X	bzero(&scsi_cmd, sizeof(scsi_cmd));
X	scsi_cmd.op_code = TEST_UNIT_READY;
X
X	if (ch_scsi_cmd(unit,
X			&scsi_cmd,
X			sizeof(struct	scsi_test_unit_ready),
X			0,
X			0,
X			100000,
X			flags) != 0) {
X		return(FALSE);
X	} else 
X		return(TRUE);
X}
X
X
X#ifdef	__STDC__
X#define b2tol(a)	(((unsigned)(a##_1) << 8) + (unsigned)a##_0 )
X#else
X#define b2tol(a)	(((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0 )
X#endif
X
X/*******************************************************\
X* Get the scsi driver to send a full inquiry to the	*
X* device and use the results to fill out the global 	*
X* parameter structure.					*
X\*******************************************************/
Xch_mode_sense(unit, flags)
Xint	unit,flags;
X{
X	struct scsi_mode_sense scsi_cmd;
X	u_char scsi_sense[128];	/* Can't use scsi_mode_sense_data because of */
X				/* missing block descriptor 		     */
X	u_char *b;
X	int i,l;
X
X	/*******************************************************\
X	* First check if we have it all loaded			*
X	\*******************************************************/
X	if (ch_info_valid[unit]==CH_KNOWN) return(TRUE);
X	/*******************************************************\
X	* First do a mode sense 				*
X	\*******************************************************/
X	ch_info_valid[unit] &= ~CH_KNOWN;
X	for(l=1;l>=0;l--) {
X		bzero(&scsi_cmd, sizeof(scsi_cmd));
X		scsi_cmd.op_code = MODE_SENSE;
X		scsi_cmd.dbd = l;
X		scsi_cmd.page_code = 0x3f;	/* All Pages */
X		scsi_cmd.length = sizeof(scsi_sense);
X	/*******************************************************\
X	* do the command, but we don't need the results		*
X	* just print them for our interest's sake		*
X	\*******************************************************/
X		if (ch_scsi_cmd(unit,
X			&scsi_cmd,
X			sizeof(struct	scsi_mode_sense),
X			&scsi_sense,
X			sizeof(scsi_sense),
X			5000,
X			flags | SCSI_DATA_IN) == 0) {
X				ch_info_valid[unit] = CH_KNOWN;
X				break;
X			}
X	}
X	if (ch_info_valid[unit]!=CH_KNOWN)   {
X		if(!(flags & SCSI_SILENT))
X			printf("could not mode sense for unit %d\n", unit);
X		return(FALSE);
X	}
X	l=scsi_sense[0]-3;
X	b=&scsi_sense[4];
X	/*****************************\
X	* To avoid alignment problems *
X	\*****************************/
X/*FIX THIS FOR MSB */
X#define p2copy(valp)	 (valp[1]+ (valp[0]<<8));valp+=2
X#define p4copy(valp)	 (valp[3]+ (valp[2]<<8) + (valp[1]<<16) + (valp[0]<<24));valp+=4
X#if 0
X	printf("\nmode_sense %d\n",l);
X	for(i=0;i<l+4;i++) {
X		printf("%x%c",scsi_sense[i],i%8==7?'\n':':');
X	}
X	printf("\n");
X#endif
X	for(i=0;i<l;) {
X		int pc=(*b++)&0x3f;
X		int pl=*b++;
X		u_char *bb=b;
X		switch(pc) {
X			case 0x1d:
X				ch_data[unit].chmo  =p2copy(bb);
X				ch_data[unit].chms  =p2copy(bb);
X				ch_data[unit].sloto =p2copy(bb);
X				ch_data[unit].slots =p2copy(bb);
X				ch_data[unit].imexo =p2copy(bb);
X				ch_data[unit].imexs =p2copy(bb);
X				ch_data[unit].driveo =p2copy(bb);
X				ch_data[unit].drives =p2copy(bb);
X				break;
X			case 0x1e:
X				ch_data[unit].rot = (*b)&1;
X				break;
X			case 0x1f:
X				ch_data[unit].stor = *b&0xf;
X				bb+=2;
X				ch_data[unit].stor =p4copy(bb);
X				break;
X			default:
X				break;
X		}
X		b+=pl;
X		i+=pl+2;
X	}
X	if (ch_debug)
X	{
X		printf("unit %d: cht(%d-%d)slot(%d-%d)imex(%d-%d)cts(%d-%d) %s rotate\n",
X				unit,
X				ch_data[unit].chmo,
X				ch_data[unit].chms,
X				ch_data[unit].sloto,
X				ch_data[unit].slots,
X				ch_data[unit].imexo,
X				ch_data[unit].imexs,
X				ch_data[unit].driveo,
X				ch_data[unit].drives,
X				ch_data[unit].rot?"can":"can't");
X	}
X	return(TRUE);
X}
X
X/*******************************************************\
X* ask the scsi driver to perform a command for us.	*
X* Call it through the switch table, and tell it which	*
X* sub-unit we want, and what target and lu we wish to	*
X* talk to. Also tell it where to find the command	*
X* how long int is.					*
X* Also tell it where to read/write the data, and how	*
X* long the data is supposed to be			*
X\*******************************************************/
Xint	ch_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags)
X
Xint	unit,flags;
Xstruct	scsi_generic *scsi_cmd;
Xint	cmdlen;
Xint	timeout;
Xu_char	*data_addr;
Xint	datalen;
X{
X	struct	scsi_xfer *xs;
X	int	retval;
X	int	s;
X
X	if(ch_debug||(scsi_debug & PRINTROUTINES)) printf("\nch_scsi_cmd%d %x",
X				unit,scsi_cmd->opcode);
X	if(ch_data[unit].sc_sw)	/* If we have a scsi driver */
X	{
X
X		xs = &(ch_scsi_xfer[unit]);
X		if(!(flags & SCSI_NOMASK))
X			s = splbio();
X		ch_xfer_block_wait[unit]++;	/* there is someone waiting */
X		while (xs->flags & INUSE)
X		{
X			sleep(&ch_xfer_block_wait[unit],PRIBIO+1);
X		}
X		ch_xfer_block_wait[unit]--;
X		xs->flags = INUSE;
X		if(!(flags & SCSI_NOMASK))
X			splx(s);
X
X		/*******************************************************\
X		* Fill out the scsi_xfer structure			*
X		\*******************************************************/
X		xs->flags	|=	flags;
X		xs->adapter	=	ch_data[unit].ctlr;
X		xs->targ	=	ch_data[unit].targ;
X		xs->lu		=	ch_data[unit].lu;
X		xs->retries	=	CH_RETRIES;
X		xs->timeout	=	timeout;
X		xs->cmd		=	scsi_cmd;
X		xs->cmdlen	=	cmdlen;
X		xs->data	=	data_addr;
X		xs->datalen	=	datalen;
X		xs->resid	=	datalen;
X		xs->when_done	=	(flags & SCSI_NOMASK)
X					?(int (*)())0
X					:ch_done;
X		xs->done_arg	=	unit;
X		xs->done_arg2	=	(int)xs;
Xretry:		xs->error	=	XS_NOERROR;
X		xs->bp		=	0;
X		ch_data[unit].lsterr=0;
X		retval = (*(ch_data[unit].sc_sw->scsi_cmd))(xs);
X		switch(retval)
X		{
X		case	SUCCESSFULLY_QUEUED:
X			while(!(xs->flags & ITSDONE))
X				sleep(xs,PRIBIO+1);
X
X		case	HAD_ERROR:
X		case	COMPLETE:
X			switch(xs->error)
X			{
X			case	XS_NOERROR:
X				retval = ESUCCESS;
X				break;
X			case	XS_SENSE:
X				retval = (ch_interpret_sense(unit,xs));
X				break;
X			case	XS_DRIVER_STUFFUP:
X				retval = EIO;
X				break;
X			case    XS_TIMEOUT:
X				if(xs->retries-- )
X				{
X					xs->flags &= ~ITSDONE;
X					goto retry;
X				}
X				retval = EIO;
X				break;
X			case    XS_BUSY:
X				if(xs->retries-- )
X				{
X					xs->flags &= ~ITSDONE;
X					goto retry;
X				}
X				retval = EIO;
X				break;
X			default:
X				retval = EIO;
X				printf("st%d: unknown error category from scsi driver\n"
X					,unit);
X				break;
X			}	
X			break;
X		case 	TRY_AGAIN_LATER:
X			if(xs->retries-- )
X			{
X				xs->flags &= ~ITSDONE;
X				goto retry;
X			}
X			retval = EIO;
X			break;
X		default:
X			retval = EIO;
X		}
X		xs->flags = 0;	/* it's free! */
X		chstart(unit);
X	}
X	else
X	{
X		printf("chd: not set up\n",unit);
X		return(EINVAL);
X	}
X	return(retval);
X}
X/***************************************************************\
X* Look at the returned sense and act on the error and detirmine	*
X* The unix error number to pass back... (0 = report no error)	*
X\***************************************************************/
X
Xint	ch_interpret_sense(unit,xs)
Xint	unit;
Xstruct	scsi_xfer	*xs;
X{
X	struct	scsi_sense_data *sense;
X	int	key;
X	int	silent = xs->flags & SCSI_SILENT;
X
X	/***************************************************************\
X	* If errors are ok, report a success				*
X	\***************************************************************/
X	if(xs->flags & SCSI_ERR_OK) return(ESUCCESS);
X
X	/***************************************************************\
X	* Get the sense fields and work out what CLASS			*
X	\***************************************************************/
X	sense = &(xs->sense);
X	switch(sense->error_class)
X	{
X	/***************************************************************\
X	* If it's class 7, use the extended stuff and interpret the key	*
X	\***************************************************************/
X	case 7:
X		{
X		key=sense->ext.extended.sense_key;
X		if(sense->ext.extended.ili)
X			if(!silent)
X			{
X				printf("length error ");
X			}
X			if(sense->valid)
X				xs->resid = ntohl(*((long *)sense->ext.extended.info));
X				if(xs->bp)
X				{
X					xs->bp->b_flags |= B_ERROR;
X					return(ESUCCESS);
X				}
X		if(sense->ext.extended.eom)
X			if(!silent) printf("end of medium ");
X		if(sense->ext.extended.filemark)
X			if(!silent) printf("filemark ");
X		if(ch_debug)
X		{
X			printf("code%x class%x valid%x\n"
X					,sense->error_code
X					,sense->error_class
X					,sense->valid);
X			printf("seg%x key%x ili%x eom%x fmark%x\n"
X					,sense->ext.extended.segment
X					,sense->ext.extended.sense_key
X					,sense->ext.extended.ili
X					,sense->ext.extended.eom
X					,sense->ext.extended.filemark);
X			printf("info: %x %x %x %x followed by %d extra bytes\n"
X					,sense->ext.extended.info[0]
X					,sense->ext.extended.info[1]
X					,sense->ext.extended.info[2]
X					,sense->ext.extended.info[3]
X					,sense->ext.extended.extra_len);
X			printf("extra: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n"
X					,sense->ext.extended.extra_bytes[0]
X					,sense->ext.extended.extra_bytes[1]
X					,sense->ext.extended.extra_bytes[2]
X					,sense->ext.extended.extra_bytes[3]
X					,sense->ext.extended.extra_bytes[4]
X					,sense->ext.extended.extra_bytes[5]
X					,sense->ext.extended.extra_bytes[6]
X					,sense->ext.extended.extra_bytes[7]
X					,sense->ext.extended.extra_bytes[8]
X					,sense->ext.extended.extra_bytes[9]
X					,sense->ext.extended.extra_bytes[10]
X					,sense->ext.extended.extra_bytes[11]
X					,sense->ext.extended.extra_bytes[12]
X					,sense->ext.extended.extra_bytes[13]
X					,sense->ext.extended.extra_bytes[14]
X					,sense->ext.extended.extra_bytes[15]);
X				    
X		}
X		switch(key)
X		{
X		case	0x0:
X			return(ESUCCESS);
X		case	0x1:
X			if(!silent)
X			{
X				printf("st%d: soft error(corrected) ", unit); 
X				if(sense->valid)
X				{
X			   		printf("block no. %d (decimal)\n",
X			  		(sense->ext.extended.info[0] <<24)|
X			  		(sense->ext.extended.info[1] <<16)|
X			  		(sense->ext.extended.info[2] <<8)|
X			  		(sense->ext.extended.info[3] ));
X				}
X			 	else
X				{
X			 		printf("\n");
X				}
X			}
X			return(ESUCCESS);
X		case	0x2:
X			if(!silent) printf("st%d: not ready\n ", unit); 
X			ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
X								sense->ext.extended.info[13] ;
X			return(ENODEV);
X		case	0x3:
X			if(!silent)
X			{
X				printf("st%d: medium error ", unit); 
X				if(sense->valid)
X				{
X			   		printf("block no. %d (decimal)\n",
X			  		(sense->ext.extended.info[0] <<24)|
X			  		(sense->ext.extended.info[1] <<16)|
X			  		(sense->ext.extended.info[2] <<8)|
X			  		(sense->ext.extended.info[3] ));
X				}
X			 	else
X				{
X			 		printf("\n");
X				}
X			}
X			return(EIO);
X		case	0x4:
X			if(!silent) printf("st%d: non-media hardware failure\n ",
X				unit); 
X			ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
X								sense->ext.extended.info[13] ;
X			return(EIO);
X		case	0x5:
X			if(!silent) printf("st%d: illegal request\n ", unit); 
X			ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
X								sense->ext.extended.info[13] ;
X			return(EINVAL);
X		case	0x6:
X			if(!silent) printf("st%d: Unit attention.\n ", unit); 
X			ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
X								sense->ext.extended.info[13] ;
X			ch_info_valid[unit] = FALSE;
X			if (ch_data[unit].flags & CH_OPEN) /* TEMP!!!! */
X				return(EIO);
X			else
X				return(ESUCCESS);
X		case	0x7:
X			if(!silent)
X			{
X				printf("st%d: attempted protection violation "
X								, unit); 
X				if(sense->valid)
X				{
X			   		printf("block no. %d (decimal)\n",
X			  		(sense->ext.extended.info[0] <<24)|
X			  		(sense->ext.extended.info[1] <<16)|
X			  		(sense->ext.extended.info[2] <<8)|
X			  		(sense->ext.extended.info[3] ));
X				}
X			 	else
X				{
X			 		printf("\n");
X				}
X			}
X			return(EACCES);
X		case	0x8:
X			if(!silent)
X			{
X				printf("st%d: block wrong state (worm)\n "
X							, unit); 
X				if(sense->valid)
X				{
X			   		printf("block no. %d (decimal)\n",
X			  		(sense->ext.extended.info[0] <<24)|
X			  		(sense->ext.extended.info[1] <<16)|
X			  		(sense->ext.extended.info[2] <<8)|
X			  		(sense->ext.extended.info[3] ));
X				}
X			 	else
X				{
X			 		printf("\n");
X				}
X			}
X			return(EIO);
X		case	0x9:
X			if(!silent) printf("st%d: vendor unique\n",
X				unit); 
X			return(EIO);
X		case	0xa:
X			if(!silent) printf("st%d: copy aborted\n ",
X				unit); 
X			return(EIO);
X		case	0xb:
X			if(!silent) printf("st%d: command aborted\n ",
X				unit); 
X			ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
X								sense->ext.extended.info[13] ;
X			return(EIO);
X		case	0xc:
X			if(!silent)
X			{
X				printf("st%d: search returned\n ", unit); 
X				if(sense->valid)
X				{
X			   		printf("block no. %d (decimal)\n",
X			  		(sense->ext.extended.info[0] <<24)|
X			  		(sense->ext.extended.info[1] <<16)|
X			  		(sense->ext.extended.info[2] <<8)|
X			  		(sense->ext.extended.info[3] ));
X				}
X			 	else
X				{
X			 		printf("\n");
X				}
X			}
X			return(ESUCCESS);
X		case	0xd:
X			if(!silent) printf("st%d: volume overflow\n ",
X				unit); 
X			return(ENOSPC);
X		case	0xe:
X			if(!silent)
X			{
X			 	printf("st%d: verify miscompare\n ", unit); 
X				if(sense->valid)
X				{
X			   		printf("block no. %d (decimal)\n",
X			  		(sense->ext.extended.info[0] <<24)|
X			  		(sense->ext.extended.info[1] <<16)|
X			  		(sense->ext.extended.info[2] <<8)|
X			  		(sense->ext.extended.info[3] ));
X				}
X			 	else
X				{
X			 		printf("\n");
X				}
X			}
X			return(EIO);
X		case	0xf:
X			if(!silent) printf("st%d: unknown error key\n ",
X				unit); 
X			return(EIO);
X		}
X		break;
X	}
X	/***************************************************************\
X	* If it's NOT class 7, just report it.				*
X	\***************************************************************/
X	case 0:
X	case 1:
X	case 2:
X	case 3:
X	case 4:
X	case 5:
X	case 6:
X		{
X			if(!silent) printf("st%d: error class %d code %d\n",
X				unit,
X				sense->error_class,
X				sense->error_code);
X		if(sense->valid)
X			if(!silent) printf("block no. %d (decimal)\n",
X			(sense->ext.unextended.blockhi <<16),
X			+ (sense->ext.unextended.blockmed <<8),
X			+ (sense->ext.unextended.blocklow ));
X		}
X		return(EIO);
X	}
X}
X
X
X
END-of-scsi/ch.c
exit