* Written by Julian Elischer (julian@tfs.com)
#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 MODE(z) ( (minor(z) & 0x0F) )
#define UNIT(z) ( (minor(z) >> 4) )
int ch_info_valid
[NCH
]; /* the info about the device is valid */
int ch_initialized
[NCH
] ;
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 */
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
;
if(scsi_debug
& PRINTROUTINES
) printf("chattach: ");
/*******************************************************\
* Check we have the resources for another drive *
\*******************************************************/
printf("Too many scsi changers..(%d > %d) reconfigure kernel\n",(unit
+ 1),NCH
);
/*******************************************************\
* 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
;
/*******************************************************\
* 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
);
printf("ch%d: scsi changer :- offline\n", unit
);
ch_initialized
[unit
] = stat
;
/*******************************************************\
\*******************************************************/
/*******************************************************\
* Check the unit is legal *
\*******************************************************/
printf("ch%d: ch %d > %d\n",unit
,unit
,NCH
);
/*******************************************************\
* Only allow one at a time *
\*******************************************************/
if(ch_data
[unit
].flags
& CH_OPEN
)
printf("ch%d: already open\n",unit
);
if(ch_debug
||(scsi_debug
& (PRINTROUTINES
| TRACEOPENS
)))
printf("chopen: dev=0x%x (unit %d (of %d))\n"
/*******************************************************\
* Make sure the device has been initialised *
\*******************************************************/
if (!ch_initialized
[unit
])
if (ch_initialized
[unit
]!=CH_KNOWN
) {
if((ch_mode_sense(unit
, SCSI_NOSLEEP
| SCSI_NOMASK
/*| SCSI_SILENT*/)))
ch_initialized
[unit
]=CH_KNOWN
;
printf("ch%d: scsi changer :- offline\n", unit
);
/*******************************************************\
* Check that it is still responding and ok. *
\*******************************************************/
if(ch_debug
|| (scsi_debug
& TRACEOPENS
))
if (!(ch_req_sense(unit
, 0)))
if(ch_debug
|| (scsi_debug
& TRACEOPENS
))
printf("not responding\n");
if(ch_debug
|| (scsi_debug
& TRACEOPENS
))
if(!(ch_test_ready(unit
,0)))
printf("ch%d: not ready\n",unit
);
ch_info_valid
[unit
] = TRUE
;
/*******************************************************\
* Load the physical device parameters *
\*******************************************************/
ch_data
[unit
].flags
= CH_OPEN
;
/*******************************************************\
* close the device.. only called if we are the LAST *
* occurence of an open device *
\*******************************************************/
if(scsi_debug
& TRACEOPENS
)
printf("Closing device");
/***************************************************************\
* 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 */
register struct buf
*bp
= 0;
if(scsi_debug
& PRINTROUTINES
) printf("chstart%d ",unit
);
/*******************************************************\
* See if there is a buf to do and we are not already *
\*******************************************************/
return; /* unit already underway */
if(ch_xfer_block_wait
[unit
]) /* a special awaits, let it proceed first */
wakeup(&ch_xfer_block_wait
[unit
]);
/*******************************************************\
* This routine is called by the scsi interrupt when *
* the transfer is complete.
\*******************************************************/
if(ch_debug
||(scsi_debug
& PRINTROUTINES
)) printf("ch_done%d ",unit
);
if (! (xs
->flags
& INUSE
))
panic("scsi_xfer not in use!");
/*******************************************************\
* Perform special action on behalf of the user *
* Knows about the internals of this device *
\*******************************************************/
chioctl(dev
, cmd
, arg
, mode
)
/* struct ch_cmd_buf *args;*/
union scsi_cmd
*scsi_cmd
;
/*******************************************************\
* Find the device that the user is talking about *
\*******************************************************/
flags
= 0; /* give error messages, act on errors etc. */
struct chop
*ch
=(struct chop
*) arg
;
printf("[chtape_chop: %x]\n", ch
->ch_op
);
switch ((short)(ch
->ch_op
)) {
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
;
return ch_position(unit
,&ch
->result
,ch
->u
.position
.chm
,
return ch_move(unit
,&ch
->result
, ch
->u
.position
.chm
,
ch
->u
.move
.from
, ch
->u
.move
.to
,
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
,
return(ret
?ESUCCESS
:EIO
);
ch_getelem(unit
,stat
,type
,from
,data
,flags
)
struct scsi_read_element_status scsi_cmd
;
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= READ_ELEMENT_STATUS
;
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
,
*stat
=ch_data
[unit
].lsterr
;
bcopy(elbuf
+16,data
,16); /*Just a hack sh */
ch_move(unit
,stat
,chm
,from
,to
,flags
)
int unit
,chm
,from
,to
,flags
;
struct scsi_move_medium scsi_cmd
;
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
,
*stat
=ch_data
[unit
].lsterr
;
ch_position(unit
,stat
,chm
,to
,flags
)
struct scsi_position_to_element scsi_cmd
;
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
,
*stat
=ch_data
[unit
].lsterr
;
/*******************************************************\
* Check with the device that it is ok, (via scsi driver)*
\*******************************************************/
ch_req_sense(unit
, 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
);
sizeof(struct scsi_sense
),
flags
| SCSI_DATA_IN
) != 0)
/*******************************************************\
* Get scsi driver to send a "are you ready" command *
\*******************************************************/
ch_test_ready(unit
,flags
)
struct scsi_test_unit_ready scsi_cmd
;
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= TEST_UNIT_READY
;
sizeof(struct scsi_test_unit_ready
),
#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 )
#define b2tol(a) (((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0 )
/*******************************************************\
* Get the scsi driver to send a full inquiry to the *
* device and use the results to fill out the global *
\*******************************************************/
ch_mode_sense(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 */
/*******************************************************\
* 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
;
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 *
\*******************************************************/
sizeof(struct scsi_mode_sense
),
flags
| SCSI_DATA_IN
) == 0) {
ch_info_valid
[unit
] = CH_KNOWN
;
if (ch_info_valid
[unit
]!=CH_KNOWN
) {
if(!(flags
& SCSI_SILENT
))
printf("ch%d: could not mode sense\n", unit
);
/*****************************\
* To avoid alignment problems *
\*****************************/
#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
printf("\nmode_sense %d\n",l
);
printf("%x%c",scsi_sense
[i
],i
%8==7?'\n':':');
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
);
ch_data
[unit
].rot
= (*b
)&1;
ch_data
[unit
].stor
= *b
&0xf;
ch_data
[unit
].stor
=p4copy(bb
);
printf("unit %d: cht(%d-%d)slot(%d-%d)imex(%d-%d)cts(%d-%d) %s rotate\n",
ch_data
[unit
].rot
?"can":"can't");
/*******************************************************\
* 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 *
* 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
)
struct scsi_generic
*scsi_cmd
;
if(ch_debug
||(scsi_debug
& PRINTROUTINES
)) printf("\nch_scsi_cmd%d %x",
if(ch_data
[unit
].sc_sw
) /* If we have a scsi driver */
xs
= &(ch_scsi_xfer
[unit
]);
if(!(flags
& SCSI_NOMASK
))
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
]--;
if(!(flags
& SCSI_NOMASK
))
/*******************************************************\
* Fill out the scsi_xfer structure *
\*******************************************************/
xs
->adapter
= ch_data
[unit
].ctlr
;
xs
->targ
= ch_data
[unit
].targ
;
xs
->lu
= ch_data
[unit
].lu
;
xs
->retries
= CH_RETRIES
;
xs
->when_done
= (flags
& SCSI_NOMASK
)
retry
: xs
->error
= XS_NOERROR
;
retval
= (*(ch_data
[unit
].sc_sw
->scsi_cmd
))(xs
);
case SUCCESSFULLY_QUEUED
:
while(!(xs
->flags
& ITSDONE
))
retval
= (ch_interpret_sense(unit
,xs
));
printf("ch%d: unknown error category from scsi driver\n"
xs
->flags
= 0; /* it's free! */
printf("ch%d: not set up\n",unit
);
/***************************************************************\
* 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
)
struct scsi_sense_data
*sense
;
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 *
\***************************************************************/
switch(sense
->error_code
& SSD_ERRCODE
)
/***************************************************************\
* If it's class 7, use the extended stuff and interpret the key *
\***************************************************************/
key
=sense
->ext
.extended
.flags
& SSD_KEY
;
if(sense
->ext
.extended
.flags
& SSD_ILI
)
if(sense
->error_code
& SSD_ERRCODE_VALID
)
xs
->resid
= ntohl(*((long *)sense
->ext
.extended
.info
));
xs
->bp
->b_flags
|= B_ERROR
;
if(sense
->ext
.extended
.flags
& SSD_EOM
)
if(!silent
) printf("end of medium ");
if(sense
->ext
.extended
.flags
& SSD_FILEMARK
)
if(!silent
) printf("filemark ");
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]);
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] ));
if(!silent
) printf("ch%d: not ready\n", unit
);
ch_data
[unit
].lsterr
=(sense
->ext
.extended
.info
[12]<<8)|
sense
->ext
.extended
.info
[13] ;
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] ));
if(!silent
) printf("ch%d: non-media hardware failure\n",
ch_data
[unit
].lsterr
=(sense
->ext
.extended
.info
[12]<<8)|
sense
->ext
.extended
.info
[13] ;
if(!silent
) printf("ch%d: illegal request\n", unit
);
ch_data
[unit
].lsterr
=(sense
->ext
.extended
.info
[12]<<8)|
sense
->ext
.extended
.info
[13] ;
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!!!! */
printf("ch%d: attempted protection violation"
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("ch%d: block wrong state (worm)"
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] ));
if(!silent
) printf("ch%d: vendor unique\n", unit
);
if(!silent
) printf("ch%d: copy aborted\n", unit
);
if(!silent
) printf("ch%d: command aborted\n", unit
);
ch_data
[unit
].lsterr
=(sense
->ext
.extended
.info
[12]<<8)|
sense
->ext
.extended
.info
[13] ;
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] ));
if(!silent
) printf("ch%d: volume overflow\n", unit
);
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] ));
if(!silent
) printf("ch%d: unknown error key\n", unit
);
/***************************************************************\
* If it's NOT class 7, just report it. *
\***************************************************************/
printf("ch%d: error code %d",
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
));