* Written by grefen@?????
* Based on scsi drivers by Julian Elischer (julian@tfs.com)
* $Id: ch.c,v 1.7 1993/12/19 00:54:49 wollman Exp $
#include <scsi/scsi_all.h>
#include <scsi/scsi_changer.h>
#include <scsi/scsiconf.h>
static errval
ch_mode_sense(u_int32
, u_int32
);
struct scsi_xfer ch_scsi_xfer
[NCH
];
u_int32 ch_xfer_block_wait
[NCH
];
#define MODE(z) ( (minor(z) & 0x0F) )
#define UNIT(z) ( (minor(z) >> 4) )
* This driver is so simple it uses all the default services
struct scsi_device ch_switch
=
struct scsi_link
*sc_link
; /* all the inter level info */
u_int16 chmo
; /* Offset of first CHM */
u_int16 chms
; /* No. of CHM */
u_int16 slots
; /* No. of Storage Elements */
u_int16 sloto
; /* Offset of first SE */
u_int16 imexs
; /* No. of Import/Export Slots */
u_int16 imexo
; /* Offset of first IM/EX */
u_int16 drives
; /* No. of CTS */
u_int16 driveo
; /* Offset of first CTS */
u_int16 rot
; /* CHM can rotate */
u_long op_matrix
; /* possible opertaions */
u_int16 lsterr
; /* details of lasterror */
u_char stor
; /* posible Storage locations */
static u_int32 next_ch_unit
= 0;
* The routine called by the low level scsi routine when it discovers
* a device suitable for this driver.
struct scsi_link
*sc_link
;
SC_DEBUG(sc_link
, SDEV_DB2
, ("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_link
= sc_link
;
sc_link
->device
= &ch_switch
;
sc_link
->dev_unit
= unit
;
* 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 :- offline\n", unit
);
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
);
ch_data
[unit
].initialized
= 1;
/* XXX ??? is this the right return val? */
struct scsi_link
*sc_link
;
* 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
);
* Make sure the device has been initialised
if (!ch_data
[unit
].initialized
)
sc_link
= ch_data
[unit
].sc_link
;
SC_DEBUG(sc_link
, SDEV_DB1
, ("chopen: dev=0x%x (unit %d (of %d))\n"
* Catch any unit attention errors.
scsi_test_unit_ready(sc_link
, SCSI_SILENT
);
sc_link
->flags
|= SDEV_OPEN
;
* Check that it is still responding and ok.
if (errcode
= (scsi_test_unit_ready(sc_link
, 0))) {
printf("ch%d: not ready\n", unit
);
sc_link
->flags
&= ~SDEV_OPEN
;
* Make sure data is loaded
if (errcode
= (ch_mode_sense(unit
, SCSI_NOSLEEP
| SCSI_NOMASK
))) {
printf("ch%d: scsi changer :- offline\n", unit
);
sc_link
->flags
&= ~SDEV_OPEN
;
ch_data
[unit
].flags
= CH_OPEN
;
* close the device.. only called if we are the LAST
* occurence of an open device
unsigned char unit
, mode
;
struct scsi_link
*sc_link
;
sc_link
= ch_data
[unit
].sc_link
;
SC_DEBUG(sc_link
, SDEV_DB1
, ("Closing device"));
sc_link
->flags
&= ~SDEV_OPEN
;
* 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
;
struct scsi_link
*sc_link
;
* Find the device that the user is talking about
flags
= 0; /* give error messages, act on errors etc. */
sc_link
= ch_data
[unit
].sc_link
;
struct chop
*ch
= (struct chop
*) arg
;
SC_DEBUG(sc_link
, SDEV_DB2
,
("[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 scsi_do_ioctl(sc_link
, cmd
, arg
, mode
);
return (ret
? ESUCCESS
: EIO
);
ch_getelem(unit
, stat
, type
, from
, data
, flags
)
u_int32 unit
, from
, 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
= scsi_scsi_cmd(ch_data
[unit
].sc_link
,
(struct scsi_generic
*) &scsi_cmd
,
SCSI_DATA_IN
| flags
) != ESUCCESS
)) {
*stat
= ch_data
[unit
].lsterr
;
bcopy(elbuf
+ 16, data
, 16);
bcopy(elbuf
+ 16, data
, 16); /*Just a hack sh */
ch_move(unit
, stat
, chm
, from
, to
, flags
)
u_int32 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
= scsi_scsi_cmd(ch_data
[unit
].sc_link
,
(struct scsi_generic
*) &scsi_cmd
,
*stat
= ch_data
[unit
].lsterr
;
ch_position(unit
, stat
, chm
, to
, flags
)
u_int32 unit
, 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
= scsi_scsi_cmd(ch_data
[unit
].sc_link
,
(struct scsi_generic
*) &scsi_cmd
,
*stat
= ch_data
[unit
].lsterr
;
#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
struct scsi_link
*sc_link
= ch_data
[unit
].sc_link
;
* First check if we have it all loaded
if (sc_link
->flags
& SDEV_MEDIA_LOADED
)
/* sc_link->flags &= ~SDEV_MEDIA_LOADED; *//*XXX */
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
);
if (errcode
= scsi_scsi_cmd(sc_link
,
(struct scsi_generic
*) &scsi_cmd
,
sizeof(struct scsi_mode_sense
),
flags
| SCSI_DATA_IN
) != 0) {
if (!(flags
& SCSI_SILENT
))
printf("ch%d: could not mode sense\n", unit
);
sc_link
->flags
|= SDEV_MEDIA_LOADED
;
* To avoid alignment problems
/* XXX - 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
printf("\nmode_sense %d\n", l
);
for (i
= 0; i
< l
+ 4; i
++) {
printf("%x%c", scsi_sense
[i
], i
% 8 == 7 ? '\n' : ':');
u_int32 pc
= (*b
++) & 0x3f;
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
);
SC_DEBUG(sc_link
, SDEV_DB2
,
(" cht(%d-%d)slot(%d-%d)imex(%d-%d)cts(%d-%d) %s rotate\n",
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"));