This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.0'.
[unix-history] / sys / scsi / ch.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);
}
}