7617a0440c
every error message and every probe message).
982 lines
25 KiB
C
982 lines
25 KiB
C
/*
|
|
* Written by Julian Elischer (julian@tfs.com)
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <ch.h>
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/errno.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/user.h>
|
|
#include <sys/chio.h>
|
|
|
|
#if defined(OSF)
|
|
#define SECSIZE 512
|
|
#endif /* defined(OSF) */
|
|
|
|
#include <scsi/scsi_all.h>
|
|
#include <scsi/scsi_changer.h>
|
|
#include <scsi/scsiconf.h>
|
|
|
|
|
|
struct scsi_xfer ch_scsi_xfer[NCH];
|
|
int ch_xfer_block_wait[NCH];
|
|
|
|
|
|
#define PAGESIZ 4096
|
|
#define STQSIZE 4
|
|
#define CH_RETRIES 4
|
|
|
|
|
|
#define MODE(z) ( (minor(z) & 0x0F) )
|
|
#define UNIT(z) ( (minor(z) >> 4) )
|
|
|
|
#ifndef MACH
|
|
#define ESUCCESS 0
|
|
#endif MACH
|
|
|
|
int ch_info_valid[NCH]; /* the info about the device is valid */
|
|
int ch_initialized[NCH] ;
|
|
int ch_debug = 1;
|
|
|
|
int chattach();
|
|
int ch_done();
|
|
struct ch_data
|
|
{
|
|
int flags;
|
|
struct scsi_switch *sc_sw; /* address of scsi low level switch */
|
|
int ctlr; /* so they know which one we want */
|
|
int targ; /* our scsi target ID */
|
|
int lu; /* out scsi lu */
|
|
short chmo; /* Offset of first CHM */
|
|
short chms; /* No. of CHM */
|
|
short slots; /* No. of Storage Elements */
|
|
short sloto; /* Offset of first SE */
|
|
short imexs; /* No. of Import/Export Slots */
|
|
short imexo; /* Offset of first IM/EX */
|
|
short drives; /* No. of CTS */
|
|
short driveo; /* Offset of first CTS */
|
|
short rot; /* CHM can rotate */
|
|
u_long op_matrix; /* possible opertaions */
|
|
u_short lsterr; /* details of lasterror */
|
|
u_char stor; /* posible Storage locations */
|
|
}ch_data[NCH];
|
|
|
|
#define CH_OPEN 0x01
|
|
#define CH_KNOWN 0x02
|
|
|
|
static int next_ch_unit = 0;
|
|
/***********************************************************************\
|
|
* The routine called by the low level scsi routine when it discovers *
|
|
* A device suitable for this driver *
|
|
\***********************************************************************/
|
|
|
|
int chattach(ctlr,targ,lu,scsi_switch)
|
|
struct scsi_switch *scsi_switch;
|
|
{
|
|
int unit,i,stat;
|
|
unsigned char *tbl;
|
|
|
|
if(scsi_debug & PRINTROUTINES) printf("chattach: ");
|
|
/*******************************************************\
|
|
* Check we have the resources for another drive *
|
|
\*******************************************************/
|
|
unit = next_ch_unit++;
|
|
if( unit >= NCH)
|
|
{
|
|
printf("Too many scsi changers..(%d > %d) reconfigure kernel\n",(unit + 1),NCH);
|
|
return(0);
|
|
}
|
|
/*******************************************************\
|
|
* Store information needed to contact our base driver *
|
|
\*******************************************************/
|
|
ch_data[unit].sc_sw = scsi_switch;
|
|
ch_data[unit].ctlr = ctlr;
|
|
ch_data[unit].targ = targ;
|
|
ch_data[unit].lu = lu;
|
|
|
|
/*******************************************************\
|
|
* Use the subdriver to request information regarding *
|
|
* the drive. We cannot use interrupts yet, so the *
|
|
* request must specify this. *
|
|
\*******************************************************/
|
|
if((ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK /*| SCSI_SILENT*/)))
|
|
{
|
|
printf("ch%d: scsi changer, %d slot(s) %d drive(s) %d arm(s) %d i/e-slot(s)\n",
|
|
unit, ch_data[unit].slots, ch_data[unit].drives, ch_data[unit].chms, ch_data[unit].imexs);
|
|
stat=CH_KNOWN;
|
|
}
|
|
else
|
|
{
|
|
printf("ch%d: scsi changer :- offline\n", unit);
|
|
stat=CH_OPEN;
|
|
}
|
|
ch_initialized[unit] = stat;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******************************************************\
|
|
* open the device. *
|
|
\*******************************************************/
|
|
chopen(dev)
|
|
{
|
|
int errcode = 0;
|
|
int unit,mode;
|
|
|
|
unit = UNIT(dev);
|
|
mode = MODE(dev);
|
|
|
|
/*******************************************************\
|
|
* Check the unit is legal *
|
|
\*******************************************************/
|
|
if ( unit >= NCH )
|
|
{
|
|
printf("ch%d: ch %d > %d\n",unit,unit,NCH);
|
|
errcode = ENXIO;
|
|
return(errcode);
|
|
}
|
|
/*******************************************************\
|
|
* Only allow one at a time *
|
|
\*******************************************************/
|
|
if(ch_data[unit].flags & CH_OPEN)
|
|
{
|
|
printf("ch%d: already open\n",unit);
|
|
errcode = ENXIO;
|
|
goto bad;
|
|
}
|
|
|
|
if(ch_debug||(scsi_debug & (PRINTROUTINES | TRACEOPENS)))
|
|
printf("chopen: dev=0x%x (unit %d (of %d))\n"
|
|
, dev, unit, NCH);
|
|
/*******************************************************\
|
|
* Make sure the device has been initialised *
|
|
\*******************************************************/
|
|
|
|
if (!ch_initialized[unit])
|
|
return(ENXIO);
|
|
if (ch_initialized[unit]!=CH_KNOWN) {
|
|
if((ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK /*| SCSI_SILENT*/)))
|
|
{
|
|
ch_initialized[unit]=CH_KNOWN;
|
|
}
|
|
else
|
|
{
|
|
printf("ch%d: scsi changer :- offline\n", unit);
|
|
return(ENXIO);
|
|
}
|
|
}
|
|
/*******************************************************\
|
|
* Check that it is still responding and ok. *
|
|
\*******************************************************/
|
|
|
|
if(ch_debug || (scsi_debug & TRACEOPENS))
|
|
printf("device is ");
|
|
if (!(ch_req_sense(unit, 0)))
|
|
{
|
|
errcode = ENXIO;
|
|
if(ch_debug || (scsi_debug & TRACEOPENS))
|
|
printf("not responding\n");
|
|
goto bad;
|
|
}
|
|
if(ch_debug || (scsi_debug & TRACEOPENS))
|
|
printf("ok\n");
|
|
|
|
if(!(ch_test_ready(unit,0)))
|
|
{
|
|
printf("ch%d: not ready\n",unit);
|
|
return(EIO);
|
|
}
|
|
|
|
ch_info_valid[unit] = TRUE;
|
|
|
|
/*******************************************************\
|
|
* Load the physical device parameters *
|
|
\*******************************************************/
|
|
|
|
ch_data[unit].flags = CH_OPEN;
|
|
return(errcode);
|
|
bad:
|
|
return(errcode);
|
|
}
|
|
|
|
/*******************************************************\
|
|
* close the device.. only called if we are the LAST *
|
|
* occurence of an open device *
|
|
\*******************************************************/
|
|
chclose(dev)
|
|
{
|
|
unsigned char unit,mode;
|
|
|
|
unit = UNIT(dev);
|
|
mode = MODE(dev);
|
|
|
|
if(scsi_debug & TRACEOPENS)
|
|
printf("Closing device");
|
|
ch_data[unit].flags = 0;
|
|
return(0);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************\
|
|
* chstart *
|
|
* This routine is also called after other non-queued requests *
|
|
* have been made of the scsi driver, to ensure that the queue *
|
|
* continues to be drained. *
|
|
\***************************************************************/
|
|
/* chstart() is called at splbio */
|
|
chstart(unit)
|
|
{
|
|
int drivecount;
|
|
register struct buf *bp = 0;
|
|
register struct buf *dp;
|
|
struct scsi_xfer *xs;
|
|
int blkno, nblk;
|
|
|
|
|
|
if(scsi_debug & PRINTROUTINES) printf("chstart%d ",unit);
|
|
/*******************************************************\
|
|
* See if there is a buf to do and we are not already *
|
|
* doing one *
|
|
\*******************************************************/
|
|
xs=&ch_scsi_xfer[unit];
|
|
if(xs->flags & INUSE)
|
|
{
|
|
return; /* unit already underway */
|
|
}
|
|
if(ch_xfer_block_wait[unit]) /* a special awaits, let it proceed first */
|
|
{
|
|
wakeup(&ch_xfer_block_wait[unit]);
|
|
return;
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
/*******************************************************\
|
|
* This routine is called by the scsi interrupt when *
|
|
* the transfer is complete.
|
|
\*******************************************************/
|
|
int ch_done(unit,xs)
|
|
int unit;
|
|
struct scsi_xfer *xs;
|
|
{
|
|
struct buf *bp;
|
|
int retval;
|
|
|
|
if(ch_debug||(scsi_debug & PRINTROUTINES)) printf("ch_done%d ",unit);
|
|
if (! (xs->flags & INUSE))
|
|
panic("scsi_xfer not in use!");
|
|
wakeup(xs);
|
|
}
|
|
/*******************************************************\
|
|
* Perform special action on behalf of the user *
|
|
* Knows about the internals of this device *
|
|
\*******************************************************/
|
|
chioctl(dev, cmd, arg, mode)
|
|
dev_t dev;
|
|
int cmd;
|
|
caddr_t arg;
|
|
{
|
|
/* struct ch_cmd_buf *args;*/
|
|
union scsi_cmd *scsi_cmd;
|
|
register i,j;
|
|
unsigned int opri;
|
|
int errcode = 0;
|
|
unsigned char unit;
|
|
int number,flags,ret;
|
|
|
|
/*******************************************************\
|
|
* Find the device that the user is talking about *
|
|
\*******************************************************/
|
|
flags = 0; /* give error messages, act on errors etc. */
|
|
unit = UNIT(dev);
|
|
|
|
switch(cmd)
|
|
{
|
|
case CHIOOP: {
|
|
struct chop *ch=(struct chop *) arg;
|
|
if (ch_debug)
|
|
printf("[chtape_chop: %x]\n", ch->ch_op);
|
|
|
|
switch ((short)(ch->ch_op)) {
|
|
case CHGETPARAM:
|
|
ch->u.getparam.chmo= ch_data[unit].chmo;
|
|
ch->u.getparam.chms= ch_data[unit].chms;
|
|
ch->u.getparam.sloto= ch_data[unit].sloto;
|
|
ch->u.getparam.slots= ch_data[unit].slots;
|
|
ch->u.getparam.imexo= ch_data[unit].imexo;
|
|
ch->u.getparam.imexs= ch_data[unit].imexs;
|
|
ch->u.getparam.driveo= ch_data[unit].driveo;
|
|
ch->u.getparam.drives= ch_data[unit].drives;
|
|
ch->u.getparam.rot= ch_data[unit].rot;
|
|
ch->result=0;
|
|
return 0;
|
|
break;
|
|
case CHPOSITION:
|
|
return ch_position(unit,&ch->result,ch->u.position.chm,
|
|
ch->u.position.to,
|
|
flags);
|
|
case CHMOVE:
|
|
return ch_move(unit,&ch->result, ch->u.position.chm,
|
|
ch->u.move.from, ch->u.move.to,
|
|
flags);
|
|
case CHGETELEM:
|
|
return ch_getelem(unit,&ch->result, ch->u.get_elem_stat.type,
|
|
ch->u.get_elem_stat.from, &ch->u.get_elem_stat.elem_data,
|
|
flags);
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
|
|
}
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
|
|
return(ret?ESUCCESS:EIO);
|
|
}
|
|
|
|
ch_getelem(unit,stat,type,from,data,flags)
|
|
int unit,from,flags;
|
|
short *stat;
|
|
char *data;
|
|
{
|
|
struct scsi_read_element_status scsi_cmd;
|
|
char elbuf[32];
|
|
int ret;
|
|
|
|
bzero(&scsi_cmd, sizeof(scsi_cmd));
|
|
scsi_cmd.op_code = READ_ELEMENT_STATUS;
|
|
scsi_cmd.byte2 = type;
|
|
scsi_cmd.starting_element_addr[0]=(from>>8)&0xff;
|
|
scsi_cmd.starting_element_addr[1]=from&0xff;
|
|
scsi_cmd.number_of_elements[1]=1;
|
|
scsi_cmd.allocation_length[2]=32;
|
|
|
|
if ((ret=ch_scsi_cmd(unit,
|
|
&scsi_cmd,
|
|
sizeof(scsi_cmd),
|
|
elbuf,
|
|
32,
|
|
100000,
|
|
flags) !=ESUCCESS)) {
|
|
*stat=ch_data[unit].lsterr;
|
|
bcopy(elbuf+16,data,16);
|
|
return ret;
|
|
}
|
|
bcopy(elbuf+16,data,16); /*Just a hack sh */
|
|
return ret;
|
|
}
|
|
|
|
ch_move(unit,stat,chm,from,to,flags)
|
|
int unit,chm,from,to,flags;
|
|
short *stat;
|
|
{
|
|
struct scsi_move_medium scsi_cmd;
|
|
int ret;
|
|
|
|
bzero(&scsi_cmd, sizeof(scsi_cmd));
|
|
scsi_cmd.op_code = MOVE_MEDIUM;
|
|
scsi_cmd.transport_element_address[0]=(chm>>8)&0xff;
|
|
scsi_cmd.transport_element_address[1]=chm&0xff;
|
|
scsi_cmd.source_address[0]=(from>>8)&0xff;
|
|
scsi_cmd.source_address[1]=from&0xff;
|
|
scsi_cmd.destination_address[0]=(to>>8)&0xff;
|
|
scsi_cmd.destination_address[1]=to&0xff;
|
|
scsi_cmd.invert=(chm&CH_INVERT)?1:0;
|
|
if ((ret=ch_scsi_cmd(unit,
|
|
&scsi_cmd,
|
|
sizeof(scsi_cmd),
|
|
NULL,
|
|
0,
|
|
100000,
|
|
flags) !=ESUCCESS)) {
|
|
*stat=ch_data[unit].lsterr;
|
|
return ret;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
ch_position(unit,stat,chm,to,flags)
|
|
int unit,chm,to,flags;
|
|
short *stat;
|
|
{
|
|
struct scsi_position_to_element scsi_cmd;
|
|
int ret;
|
|
|
|
bzero(&scsi_cmd, sizeof(scsi_cmd));
|
|
scsi_cmd.op_code = POSITION_TO_ELEMENT;
|
|
scsi_cmd.transport_element_address[0]=(chm>>8)&0xff;
|
|
scsi_cmd.transport_element_address[1]=chm&0xff;
|
|
scsi_cmd.source_address[0]=(to>>8)&0xff;
|
|
scsi_cmd.source_address[1]=to&0xff;
|
|
scsi_cmd.invert=(chm&CH_INVERT)?1:0;
|
|
if ((ret=ch_scsi_cmd(unit,
|
|
&scsi_cmd,
|
|
sizeof(scsi_cmd),
|
|
NULL,
|
|
0,
|
|
100000,
|
|
flags) !=ESUCCESS)) {
|
|
*stat=ch_data[unit].lsterr;
|
|
return ret;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************\
|
|
* Check with the device that it is ok, (via scsi driver)*
|
|
\*******************************************************/
|
|
ch_req_sense(unit, flags)
|
|
int flags;
|
|
{
|
|
struct scsi_sense_data sense;
|
|
struct scsi_sense scsi_cmd;
|
|
|
|
bzero(&scsi_cmd, sizeof(scsi_cmd));
|
|
scsi_cmd.op_code = REQUEST_SENSE;
|
|
scsi_cmd.length = sizeof(sense);
|
|
|
|
if (ch_scsi_cmd(unit,
|
|
&scsi_cmd,
|
|
sizeof(struct scsi_sense),
|
|
&sense,
|
|
sizeof(sense),
|
|
100000,
|
|
flags | SCSI_DATA_IN) != 0)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
else
|
|
return(TRUE);
|
|
}
|
|
|
|
/*******************************************************\
|
|
* Get scsi driver to send a "are you ready" command *
|
|
\*******************************************************/
|
|
ch_test_ready(unit,flags)
|
|
int unit,flags;
|
|
{
|
|
struct scsi_test_unit_ready scsi_cmd;
|
|
|
|
bzero(&scsi_cmd, sizeof(scsi_cmd));
|
|
scsi_cmd.op_code = TEST_UNIT_READY;
|
|
|
|
if (ch_scsi_cmd(unit,
|
|
&scsi_cmd,
|
|
sizeof(struct scsi_test_unit_ready),
|
|
0,
|
|
0,
|
|
100000,
|
|
flags) != 0) {
|
|
return(FALSE);
|
|
} else
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
#ifdef __STDC__
|
|
#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 )
|
|
#else
|
|
#define b2tol(a) (((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0 )
|
|
#endif
|
|
|
|
/*******************************************************\
|
|
* Get the scsi driver to send a full inquiry to the *
|
|
* device and use the results to fill out the global *
|
|
* parameter structure. *
|
|
\*******************************************************/
|
|
ch_mode_sense(unit, flags)
|
|
int unit,flags;
|
|
{
|
|
struct scsi_mode_sense scsi_cmd;
|
|
u_char scsi_sense[128]; /* Can't use scsi_mode_sense_data because of */
|
|
/* missing block descriptor */
|
|
u_char *b;
|
|
int i,l;
|
|
|
|
/*******************************************************\
|
|
* First check if we have it all loaded *
|
|
\*******************************************************/
|
|
if (ch_info_valid[unit]==CH_KNOWN) return(TRUE);
|
|
/*******************************************************\
|
|
* First do a mode sense *
|
|
\*******************************************************/
|
|
ch_info_valid[unit] &= ~CH_KNOWN;
|
|
for(l=1;l>=0;l--) {
|
|
bzero(&scsi_cmd, sizeof(scsi_cmd));
|
|
scsi_cmd.op_code = MODE_SENSE;
|
|
scsi_cmd.byte2 = SMS_DBD;
|
|
scsi_cmd.page = 0x3f; /* All Pages */
|
|
scsi_cmd.length = sizeof(scsi_sense);
|
|
/*******************************************************\
|
|
* do the command, but we don't need the results *
|
|
* just print them for our interest's sake *
|
|
\*******************************************************/
|
|
if (ch_scsi_cmd(unit,
|
|
&scsi_cmd,
|
|
sizeof(struct scsi_mode_sense),
|
|
&scsi_sense,
|
|
sizeof(scsi_sense),
|
|
5000,
|
|
flags | SCSI_DATA_IN) == 0) {
|
|
ch_info_valid[unit] = CH_KNOWN;
|
|
break;
|
|
}
|
|
}
|
|
if (ch_info_valid[unit]!=CH_KNOWN) {
|
|
if(!(flags & SCSI_SILENT))
|
|
printf("ch%d: could not mode sense\n", unit);
|
|
return(FALSE);
|
|
}
|
|
l=scsi_sense[0]-3;
|
|
b=&scsi_sense[4];
|
|
/*****************************\
|
|
* To avoid alignment problems *
|
|
\*****************************/
|
|
/*FIX THIS FOR MSB */
|
|
#define p2copy(valp) (valp[1]+ (valp[0]<<8));valp+=2
|
|
#define p4copy(valp) (valp[3]+ (valp[2]<<8) + (valp[1]<<16) + (valp[0]<<24));valp+=4
|
|
#if 0
|
|
printf("\nmode_sense %d\n",l);
|
|
for(i=0;i<l+4;i++) {
|
|
printf("%x%c",scsi_sense[i],i%8==7?'\n':':');
|
|
}
|
|
printf("\n");
|
|
#endif
|
|
for(i=0;i<l;) {
|
|
int pc=(*b++)&0x3f;
|
|
int pl=*b++;
|
|
u_char *bb=b;
|
|
switch(pc) {
|
|
case 0x1d:
|
|
ch_data[unit].chmo =p2copy(bb);
|
|
ch_data[unit].chms =p2copy(bb);
|
|
ch_data[unit].sloto =p2copy(bb);
|
|
ch_data[unit].slots =p2copy(bb);
|
|
ch_data[unit].imexo =p2copy(bb);
|
|
ch_data[unit].imexs =p2copy(bb);
|
|
ch_data[unit].driveo =p2copy(bb);
|
|
ch_data[unit].drives =p2copy(bb);
|
|
break;
|
|
case 0x1e:
|
|
ch_data[unit].rot = (*b)&1;
|
|
break;
|
|
case 0x1f:
|
|
ch_data[unit].stor = *b&0xf;
|
|
bb+=2;
|
|
ch_data[unit].stor =p4copy(bb);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
b+=pl;
|
|
i+=pl+2;
|
|
}
|
|
if (ch_debug)
|
|
{
|
|
printf("unit %d: cht(%d-%d)slot(%d-%d)imex(%d-%d)cts(%d-%d) %s rotate\n",
|
|
unit,
|
|
ch_data[unit].chmo,
|
|
ch_data[unit].chms,
|
|
ch_data[unit].sloto,
|
|
ch_data[unit].slots,
|
|
ch_data[unit].imexo,
|
|
ch_data[unit].imexs,
|
|
ch_data[unit].driveo,
|
|
ch_data[unit].drives,
|
|
ch_data[unit].rot?"can":"can't");
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
/*******************************************************\
|
|
* ask the scsi driver to perform a command for us. *
|
|
* Call it through the switch table, and tell it which *
|
|
* sub-unit we want, and what target and lu we wish to *
|
|
* talk to. Also tell it where to find the command *
|
|
* how long int is. *
|
|
* Also tell it where to read/write the data, and how *
|
|
* long the data is supposed to be *
|
|
\*******************************************************/
|
|
int ch_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags)
|
|
|
|
int unit,flags;
|
|
struct scsi_generic *scsi_cmd;
|
|
int cmdlen;
|
|
int timeout;
|
|
u_char *data_addr;
|
|
int datalen;
|
|
{
|
|
struct scsi_xfer *xs;
|
|
int retval;
|
|
int s;
|
|
|
|
if(ch_debug||(scsi_debug & PRINTROUTINES)) printf("\nch_scsi_cmd%d %x",
|
|
unit,scsi_cmd->opcode);
|
|
if(ch_data[unit].sc_sw) /* If we have a scsi driver */
|
|
{
|
|
|
|
xs = &(ch_scsi_xfer[unit]);
|
|
if(!(flags & SCSI_NOMASK))
|
|
s = splbio();
|
|
ch_xfer_block_wait[unit]++; /* there is someone waiting */
|
|
while (xs->flags & INUSE)
|
|
{
|
|
sleep(&ch_xfer_block_wait[unit],PRIBIO+1);
|
|
}
|
|
ch_xfer_block_wait[unit]--;
|
|
xs->flags = INUSE;
|
|
if(!(flags & SCSI_NOMASK))
|
|
splx(s);
|
|
|
|
/*******************************************************\
|
|
* Fill out the scsi_xfer structure *
|
|
\*******************************************************/
|
|
xs->flags |= flags;
|
|
xs->adapter = ch_data[unit].ctlr;
|
|
xs->targ = ch_data[unit].targ;
|
|
xs->lu = ch_data[unit].lu;
|
|
xs->retries = CH_RETRIES;
|
|
xs->timeout = timeout;
|
|
xs->cmd = scsi_cmd;
|
|
xs->cmdlen = cmdlen;
|
|
xs->data = data_addr;
|
|
xs->datalen = datalen;
|
|
xs->resid = datalen;
|
|
xs->when_done = (flags & SCSI_NOMASK)
|
|
?(int (*)())0
|
|
:ch_done;
|
|
xs->done_arg = unit;
|
|
xs->done_arg2 = (int)xs;
|
|
retry: xs->error = XS_NOERROR;
|
|
xs->bp = 0;
|
|
ch_data[unit].lsterr=0;
|
|
retval = (*(ch_data[unit].sc_sw->scsi_cmd))(xs);
|
|
switch(retval)
|
|
{
|
|
case SUCCESSFULLY_QUEUED:
|
|
while(!(xs->flags & ITSDONE))
|
|
sleep(xs,PRIBIO+1);
|
|
|
|
case HAD_ERROR:
|
|
case COMPLETE:
|
|
switch(xs->error)
|
|
{
|
|
case XS_NOERROR:
|
|
retval = ESUCCESS;
|
|
break;
|
|
case XS_SENSE:
|
|
retval = (ch_interpret_sense(unit,xs));
|
|
break;
|
|
case XS_DRIVER_STUFFUP:
|
|
retval = EIO;
|
|
break;
|
|
case XS_TIMEOUT:
|
|
if(xs->retries-- )
|
|
{
|
|
xs->flags &= ~ITSDONE;
|
|
goto retry;
|
|
}
|
|
retval = EIO;
|
|
break;
|
|
case XS_BUSY:
|
|
if(xs->retries-- )
|
|
{
|
|
xs->flags &= ~ITSDONE;
|
|
goto retry;
|
|
}
|
|
retval = EIO;
|
|
break;
|
|
default:
|
|
retval = EIO;
|
|
printf("ch%d: unknown error category from scsi driver\n"
|
|
,unit);
|
|
break;
|
|
}
|
|
break;
|
|
case TRY_AGAIN_LATER:
|
|
if(xs->retries-- )
|
|
{
|
|
xs->flags &= ~ITSDONE;
|
|
goto retry;
|
|
}
|
|
retval = EIO;
|
|
break;
|
|
default:
|
|
retval = EIO;
|
|
}
|
|
xs->flags = 0; /* it's free! */
|
|
chstart(unit);
|
|
}
|
|
else
|
|
{
|
|
printf("ch%d: not set up\n",unit);
|
|
return(EINVAL);
|
|
}
|
|
return(retval);
|
|
}
|
|
/***************************************************************\
|
|
* Look at the returned sense and act on the error and detirmine *
|
|
* The unix error number to pass back... (0 = report no error) *
|
|
\***************************************************************/
|
|
|
|
int ch_interpret_sense(unit,xs)
|
|
int unit;
|
|
struct scsi_xfer *xs;
|
|
{
|
|
struct scsi_sense_data *sense;
|
|
int key;
|
|
int silent = xs->flags & SCSI_SILENT;
|
|
|
|
/***************************************************************\
|
|
* If errors are ok, report a success *
|
|
\***************************************************************/
|
|
if(xs->flags & SCSI_ERR_OK) return(ESUCCESS);
|
|
|
|
/***************************************************************\
|
|
* Get the sense fields and work out what CLASS *
|
|
\***************************************************************/
|
|
sense = &(xs->sense);
|
|
switch(sense->error_code & SSD_ERRCODE)
|
|
{
|
|
/***************************************************************\
|
|
* If it's class 7, use the extended stuff and interpret the key *
|
|
\***************************************************************/
|
|
case 0x70:
|
|
{
|
|
key=sense->ext.extended.flags & SSD_KEY;
|
|
if(sense->ext.extended.flags & SSD_ILI)
|
|
if(!silent)
|
|
{
|
|
printf("length error ");
|
|
}
|
|
if(sense->error_code & SSD_ERRCODE_VALID)
|
|
xs->resid = ntohl(*((long *)sense->ext.extended.info));
|
|
if(xs->bp)
|
|
{
|
|
xs->bp->b_flags |= B_ERROR;
|
|
return(ESUCCESS);
|
|
}
|
|
if(sense->ext.extended.flags & SSD_EOM)
|
|
if(!silent) printf("end of medium ");
|
|
if(sense->ext.extended.flags & SSD_FILEMARK)
|
|
if(!silent) printf("filemark ");
|
|
if(ch_debug)
|
|
{
|
|
printf("code%x valid%x\n"
|
|
,sense->error_code & SSD_ERRCODE
|
|
,sense->error_code & SSD_ERRCODE_VALID);
|
|
printf("seg%x key%x ili%x eom%x fmark%x\n"
|
|
,sense->ext.extended.segment
|
|
,sense->ext.extended.flags & SSD_KEY
|
|
,sense->ext.extended.flags & SSD_ILI
|
|
,sense->ext.extended.flags & SSD_EOM
|
|
,sense->ext.extended.flags & SSD_FILEMARK);
|
|
printf("info: %x %x %x %x followed by %d extra bytes\n"
|
|
,sense->ext.extended.info[0]
|
|
,sense->ext.extended.info[1]
|
|
,sense->ext.extended.info[2]
|
|
,sense->ext.extended.info[3]
|
|
,sense->ext.extended.extra_len);
|
|
printf("extra: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n"
|
|
,sense->ext.extended.extra_bytes[0]
|
|
,sense->ext.extended.extra_bytes[1]
|
|
,sense->ext.extended.extra_bytes[2]
|
|
,sense->ext.extended.extra_bytes[3]
|
|
,sense->ext.extended.extra_bytes[4]
|
|
,sense->ext.extended.extra_bytes[5]
|
|
,sense->ext.extended.extra_bytes[6]
|
|
,sense->ext.extended.extra_bytes[7]
|
|
,sense->ext.extended.extra_bytes[8]
|
|
,sense->ext.extended.extra_bytes[9]
|
|
,sense->ext.extended.extra_bytes[10]
|
|
,sense->ext.extended.extra_bytes[11]
|
|
,sense->ext.extended.extra_bytes[12]
|
|
,sense->ext.extended.extra_bytes[13]
|
|
,sense->ext.extended.extra_bytes[14]
|
|
,sense->ext.extended.extra_bytes[15]);
|
|
|
|
}
|
|
switch(key)
|
|
{
|
|
case 0x0:
|
|
return(ESUCCESS);
|
|
case 0x1:
|
|
if(!silent)
|
|
{
|
|
printf("ch%d: soft error(corrected)", unit);
|
|
if(sense->error_code & SSD_ERRCODE_VALID)
|
|
{
|
|
printf(" block no. %d (decimal)",
|
|
(sense->ext.extended.info[0] <<24)|
|
|
(sense->ext.extended.info[1] <<16)|
|
|
(sense->ext.extended.info[2] <<8)|
|
|
(sense->ext.extended.info[3] ));
|
|
}
|
|
printf("\n");
|
|
}
|
|
return(ESUCCESS);
|
|
case 0x2:
|
|
if(!silent) printf("ch%d: not ready\n", unit);
|
|
ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
|
|
sense->ext.extended.info[13] ;
|
|
return(ENODEV);
|
|
case 0x3:
|
|
if(!silent)
|
|
{
|
|
printf("ch%d: medium error", unit);
|
|
if(sense->error_code & SSD_ERRCODE_VALID)
|
|
{
|
|
printf(" block no. %d (decimal)",
|
|
(sense->ext.extended.info[0] <<24)|
|
|
(sense->ext.extended.info[1] <<16)|
|
|
(sense->ext.extended.info[2] <<8)|
|
|
(sense->ext.extended.info[3] ));
|
|
}
|
|
printf("\n");
|
|
}
|
|
return(EIO);
|
|
case 0x4:
|
|
if(!silent) printf("ch%d: non-media hardware failure\n",
|
|
unit);
|
|
ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
|
|
sense->ext.extended.info[13] ;
|
|
return(EIO);
|
|
case 0x5:
|
|
if(!silent) printf("ch%d: illegal request\n", unit);
|
|
ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
|
|
sense->ext.extended.info[13] ;
|
|
return(EINVAL);
|
|
case 0x6:
|
|
if(!silent) printf("ch%d: Unit attention\n", unit);
|
|
ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
|
|
sense->ext.extended.info[13] ;
|
|
ch_info_valid[unit] = FALSE;
|
|
if (ch_data[unit].flags & CH_OPEN) /* TEMP!!!! */
|
|
return(EIO);
|
|
else
|
|
return(ESUCCESS);
|
|
case 0x7:
|
|
if(!silent)
|
|
{
|
|
printf("ch%d: attempted protection violation"
|
|
, unit);
|
|
if(sense->error_code & SSD_ERRCODE_VALID)
|
|
{
|
|
printf(" block no. %d (decimal)\n",
|
|
(sense->ext.extended.info[0] <<24)|
|
|
(sense->ext.extended.info[1] <<16)|
|
|
(sense->ext.extended.info[2] <<8)|
|
|
(sense->ext.extended.info[3] ));
|
|
}
|
|
printf("\n");
|
|
}
|
|
return(EACCES);
|
|
case 0x8:
|
|
if(!silent)
|
|
{
|
|
printf("ch%d: block wrong state (worm)"
|
|
, unit);
|
|
if(sense->error_code & SSD_ERRCODE_VALID)
|
|
{
|
|
printf(" block no. %d (decimal)",
|
|
(sense->ext.extended.info[0] <<24)|
|
|
(sense->ext.extended.info[1] <<16)|
|
|
(sense->ext.extended.info[2] <<8)|
|
|
(sense->ext.extended.info[3] ));
|
|
}
|
|
printf("\n");
|
|
}
|
|
return(EIO);
|
|
case 0x9:
|
|
if(!silent) printf("ch%d: vendor unique\n", unit);
|
|
return(EIO);
|
|
case 0xa:
|
|
if(!silent) printf("ch%d: copy aborted\n", unit);
|
|
return(EIO);
|
|
case 0xb:
|
|
if(!silent) printf("ch%d: command aborted\n", unit);
|
|
ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
|
|
sense->ext.extended.info[13] ;
|
|
return(EIO);
|
|
case 0xc:
|
|
if(!silent)
|
|
{
|
|
printf("ch%d: search returned", unit);
|
|
if(sense->error_code & SSD_ERRCODE_VALID)
|
|
{
|
|
printf(" block no. %d (decimal)",
|
|
(sense->ext.extended.info[0] <<24)|
|
|
(sense->ext.extended.info[1] <<16)|
|
|
(sense->ext.extended.info[2] <<8)|
|
|
(sense->ext.extended.info[3] ));
|
|
}
|
|
printf("\n");
|
|
}
|
|
return(ESUCCESS);
|
|
case 0xd:
|
|
if(!silent) printf("ch%d: volume overflow\n", unit);
|
|
return(ENOSPC);
|
|
case 0xe:
|
|
if(!silent)
|
|
{
|
|
printf("ch%d: verify miscompare", unit);
|
|
if(sense->error_code & SSD_ERRCODE_VALID)
|
|
{
|
|
printf(" block no. %d (decimal)",
|
|
(sense->ext.extended.info[0] <<24)|
|
|
(sense->ext.extended.info[1] <<16)|
|
|
(sense->ext.extended.info[2] <<8)|
|
|
(sense->ext.extended.info[3] ));
|
|
}
|
|
printf("\n");
|
|
}
|
|
return(EIO);
|
|
case 0xf:
|
|
if(!silent) printf("ch%d: unknown error key\n", unit);
|
|
return(EIO);
|
|
}
|
|
break;
|
|
}
|
|
/***************************************************************\
|
|
* If it's NOT class 7, just report it. *
|
|
\***************************************************************/
|
|
default:
|
|
{
|
|
if(!silent)
|
|
{
|
|
printf("ch%d: error code %d",
|
|
unit,
|
|
sense->error_code & SSD_ERRCODE);
|
|
if(sense->error_code & SSD_ERRCODE_VALID)
|
|
{
|
|
printf(" block no. %d (decimal)",
|
|
(sense->ext.unextended.blockhi <<16),
|
|
+ (sense->ext.unextended.blockmed <<8),
|
|
+ (sense->ext.unextended.blocklow ));
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
return(EIO);
|
|
}
|
|
}
|
|
|
|
|
|
|