0707070035050422111006660011710000040000010273220464714144700000700000003143README This is a simple extensible shell (scsish) for poking at scsi
devices, particularly the simpler kinds commonly called toasters.
it is supposed to be self-documenting in use; try the help command.
my use of the moran-dronek /dev/scsi library is still imperfect;
there is still some some debugging showing.
To compile, you first need mk. you then have to pick a system type
to set some flags; currently we support research and sgi.
yours may differ, particularly as no one else has our ansi C compiler for the sgi.
the only problem i would expect is the normal header file crap you get
mixing ansi and non-ansi files. i recommend setting NPROC=1 while debugging hdr files.
if you change (header) files, try putting them in the directory inc
(then others may benefit). To support a new system (say sgi-gcc), just create
a new file sgi-gcc.mk and so on. you may be missing some devices in
your /dev/scsi; the script scsi/gendev may help (but check the major/minor
numbers and permissions).
As for modifying/extending scsish, it has been designed to be not too hard.
Adding a new device means adding a new set of rules (like the other rules)
to mkfile and creating a new directory (say exabyte) and at least two files in it
(dev.c and fns.h). The wren directory is a small example you can clone.
Adding new functions to any device means updating a file list in mkfile,
updating dev.c and fns.h in the device directory. The argument syntax
scheme is arguably pokey, but liveable. at some future point we should probably
switch over to osterhout's tcl.
as always, i invite you send extensions/fixes etc back to
[email protected]
0707070035050453661006660011710000040000010451330457563432000000500000000565TODO | COPY drive NUMBER NUMBER drive NUMBER {/*:COPY sdrive sstart nblocks ddrive dstart:: */
s_copy($2, $3, $4, $5, $6);
}
| READ drive NUMBER {
struct scsi_ret output;
s_read($2, $3, 1, &output);
scsiodump(output.data, 1024);
}
| WRITE drive NUMBER { s_write($2, $3, 1); }
| WRITE drive NUMBER NUMBER { s_write($2, $3, $4); } /*:WRITE drive start n:: */
0707070035050450521006660011710000040000010447750457563431100001300000002057allocate.c #include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "scsi.h"
#include "juke.h"
allocate(char *vol_id, char *buf)
{
int drive, sh;
char nbuf[512];
if(j_rdshelves(buf)) /* read in shelf names */
return(-1);
if(j_getstatus(buf)) /* get the jukebox status */
return(-1);
sh = j_shelfof(vol_id);
if(sh >= 0){
sprintf(buf, "there is an existing '%s' on shelf %d", vol_id, sh);
return(-1);
}
sh = j_shelfof(UNALLOCATED);
if(sh < 0){
sprintf(buf, "no unallocated disks");
return(-1);
}
printf("using unallocated disk from shelf %d\n", sh);
drive = min(nlun+1, NLUN-1);
if(j_shelf_to_drive(sh, SIDEB, drive, buf) < 0)
return(-1);
sprintf(nbuf, "%sb", vol_id);
if(j_wvolid(drive, nbuf, buf))
return(-1);
j_wrshelf = 1;
j_shelf[sh] = strdup(vol_id);
if(j_drive_to_shelf(drive, sh, SIDEB, buf) < 0)
return(-1);
if(j_shelf_to_drive(sh, SIDEA, drive, buf) < 0)
return(-1);
sprintf(nbuf, "%sa", vol_id);
if(j_wvolid(drive, nbuf, buf))
return(-1);
if(j_drive_to_shelf(drive, sh, SIDEA, buf) < 0)
return(-1);
return(0);
}
0707070035050452321006660011710000040000010576150463130011500000700000007736cold.c #include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "scsi.h"
#include "juke.h"
static sort(char *buf);
cold_inv(char type, char *buf)
{
Side side;
int drive, sh, nsh;
int n;
char vol_id[512];
int didit[NSHELF];
if(j_getstatus(buf)) /* get the jukebox status */
return(-1);
printf("getstatus done\n");
/* first clear out nonexistent labels */
n = 0;
for(sh = 0; sh < NSHELF; sh++){
if((j_status.shelf[sh]&0xC0) == 0xC0){
n++;
j_shelf[sh] = "there";
} else
j_shelf[sh] = 0;
didit[sh] = 0;
}
printf("%d disks in shelves.\n", n);
/* second, clear the drives */
for(sh = 0; sh < NSHELF; sh++)
if(j_shelf[sh] == 0)
break;
for(drive = 0; drive < 8; drive++){
if(!j_status.lun[drive].diskin)
continue; /* no disk in drive */
printf("clearing drive %d:\n", drive);
if(j_status.lun[drive].diskindrive && !j_status.lun[drive].shelfvalid){
if(j_drive_to_shelf(drive, sh, SIDEA, buf))
return(-1);
for(sh++; sh < NSHELF; sh++)
if(j_shelf[sh] == 0)
break;
n++;
} else
if(j_drive_to_shelf(drive, -1, SIDEA, buf))
return(-1);
printf("\n");
}
printf("reloading %d disks.\n", n);
side = SIDEA;
drive = min(nlun+1, NLUN-1);
j_wrshelf = 1;
for(sh = 0; sh < NSHELF; sh++){
if(didit[sh])
continue;
j_shelf[sh] = 0;
/* the C0 means disk properly present (not temp) */
if((j_status.shelf[sh]&0xC0) == 0xC0){
if(getvol(sh, drive, vol_id, &side))
errexit(vol_id);
switch(type)
{
case 'c':
for(nsh = 0; j_shelf[nsh]; nsh++)
;
break;
case 's':
case 'r':
while(j_shelf[nsh = nrand(NSHELF)])
;
break;
case 'u':
default:
nsh = sh;
break;
}
printf("%s@%d -> %d\n", vol_id, sh, nsh);
if(j_drive_to_shelf(drive, nsh, side, buf) < 0)
return(-1);
j_shelf[nsh] = strdup(vol_id);
didit[nsh] = 1;
sleep(2);
}
}
printf("process any new disks.\n");
if(warm_inv(buf))
return(-1);
if(type == 's')
return(sort(buf));
return(0);
}
getvol(int sh, int drive, char *vol_id, int *side)
{
int i;
char buf[512];
if(j_shelf_to_drive(sh, SIDEA, drive, vol_id) < 0)
return(-1);
if((i = j_rvolid(drive, buf)) < 0)
goto softerr;
if(i == 0)
*side = SIDEA;
else {
*side = SIDEB;
if(j_drive_to_shelf(drive, sh, SIDEA, vol_id) < 0)
return(-1);
if(j_shelf_to_drive(sh, SIDEB, drive, vol_id) < 0)
return(-1);
if((i = j_rvolid(drive, buf)) < 0)
goto softerr;
}
if(i > 0)
strcpy(vol_id, UNALLOCATED);
else {
strcpy(vol_id, buf);
i = strlen(vol_id)-1;
if(i < 0){
sprintf(buf, "apparently good superblock but null vol_id");
goto softerr;
} else if(vol_id[i] == 'a')
vol_id[i] = 0;
else if(vol_id[i] == 'b'){
vol_id[i] = 0;
*side = !*side;
} else {
sprintf(buf, "vol_id '%s' must end in a or b", vol_id);
strcpy(vol_id, buf);
return(-1);
}
}
return(0);
softerr:
*side = SIDEA;
fprintf(stderr, "error in reading shelf %d: %s; proceeding\n", sh, buf);
sprintf(vol_id, "DISK_ERROR%d", sh);
gen_reset(0, (int *)0, 0, (char **)0, buf);
return(0);
}
static int index[NSHELF];
static
cmp(int *a, int *b)
{
char *sa = j_shelf[*a], *sb = j_shelf[*b];
if((sa == 0) && (sb == 0)) return(0);
if(sa == 0) return(-1);
if(sb == 0) return(1);
return(strcmp(sa, sb));
}
static char *disk[8];
static
sd(int a, int b, char *err)
{
disk[b] = j_shelf[a];
return(j_shelf_to_drive(a, SIDEB, b, err));
}
static
ds(int a, int b, char *err)
{
j_shelf[b] = disk[a];
return(j_drive_to_shelf(a, b, SIDEB, err));
}
static
sort(char *errbuf)
{
int i, j, org;
for(i = 0; i < NSHELF; i++)
index[i] = i;
qsort(index, NSHELF, sizeof index[0], cmp);
for(i = 0; i < NSHELF; i++){
if(index[i] < 0) continue;
if(sd(org = i, NLUN-1, errbuf) < 0)
return(-1);
j = index[i];
index[i] = -1;
while(j != org){
if(sd(j, NLUN-2, errbuf) < 0)
return(-1);
if(ds(NLUN-2, i, errbuf) < 0)
return(-1);
i = j;
if(index[i] < 0)
break;
j = index[i];
index[i] = -1;
}
if(ds(NLUN-1, i, errbuf) < 0)
return(-1);
}
return(0);
}
0707070035050513661006440011710000040000010044130464677201700001000000027722dslib.c /*
|| dslib.c - library routines for /dev/scsi
||
|| Copyright 1988, 1989, by
|| Gene Dronek (Vulcan Laboratory) and
|| Rich Morin (Canta Forda Computer Laboratory).
|| All rights reserved.
*/
#ident "dslib.c: $Revision: 1.1.1.1 $"
#include <stdio.h>
#include <sys/types.h>
#include "dslib.h"
#ifdef aux
#include <sys/vio.h>
#include <sys/scsireq.h>
#endif aux
int dsdebug=0;
long dsreqflags; /* flag bits always set by filldsreq */
#define min(i,j) ( (i) < (j) ? (i) : (j) )
/*
|| Startup/shutdown -----------------------------------------------
*/
static struct context *dsc[FDSIZ];
/*
|| dsopen - open device, set up structures
*/
struct dsreq *
dsopen(opath, oflags)
char *opath;
int oflags;
{
struct dsreq *dsp;
struct context *cp;
int fd;
DSDBG(fprintf(stderr,"dsopen(%s,%x) ", opath, oflags));
fd = open(opath, oflags);
if (fd < 0)
return NULL; /* can't open */
if (dsc[fd] != NULL) /* already in use */
ds_zot("dsopen: fd already in use");
cp = (struct context *) calloc(1, sizeof(struct context));
if (cp == NULL) /* can't allocate */
ds_zot("dsopen: can't allocate space");
dsc[fd] = cp;
cp->dsc_fd = fd;
dsp = &(cp->dsc_dsreq);
dsp->ds_flags = 0;
dsp->ds_time = 10 * 1000; /* 10 second default timeout */
dsp->ds_private = (ulong) cp; /* pointer back to context */
dsp->ds_cmdbuf = cp->dsc_cmd;
dsp->ds_cmdlen = sizeof cp->dsc_cmd;
dsp->ds_databuf = 0;
dsp->ds_datalen = 0;
dsp->ds_sensebuf = cp->dsc_sense;
dsp->ds_senselen = sizeof cp->dsc_sense;
DSDBG(fprintf(stderr,"=>cp %x, dsp %x\n", cp, dsp));
return dsp;
}
/*
|| dsclose - close device, release context struct.
*/
dsclose(dsp)
struct dsreq *dsp;
{
int fd;
struct context *cp;
if (dsp == NULL)
ds_zot("dsclose: dsp is NULL");
cp = (struct context *)dsp->ds_private;
fd = getfd(dsp);
if ( cp == NULL )
ds_zot("dsclose: private is NULL");
cfree(cp);
dsc[fd] = (struct context *)NULL;
return;
}
/*
|| Generic SCSI CCS Command functions ------------------------------------
||
|| dsp dsreq pointer
|| data data buffer pointer
|| datalen data buffer length
|| lba logical block address
|| vu vendor unique bits
*/
/*
|| testunitready00 - issue group 0 "Test Unit Ready" command (0x00)
*/
testunitready00(dsp)
struct dsreq *dsp;
{
fillg0cmd(dsp, CMDBUF(dsp), G0_TEST, 0, 0, 0, 0, 0);
filldsreq(dsp, 0, 0, DSRQ_READ|DSRQ_SENSE);
return(doscsireq(getfd(dsp), dsp));
}
/*
|| requestsense03 - issue group 0 "Request Sense" command (0x03)
*/
requestsense03(dsp, data, datalen, vu)
struct dsreq *dsp;
caddr_t data;
long datalen;
char vu;
{
fillg0cmd(dsp, CMDBUF(dsp), G0_REQU, 0, 0, 0, B1(datalen), B1(vu<<6));
filldsreq(dsp, data, datalen, DSRQ_READ);
return(doscsireq(getfd(dsp), dsp));
}
/*
|| write0a - issue group 0 "Write" command (0x0a)
*/
write0a(dsp, data, datalen, lba, vu)
struct dsreq *dsp;
caddr_t data;
long datalen, lba;
char vu;
{
fillg0cmd(dsp, CMDBUF(dsp), G0_WRIT, B3(lba), B1(datalen), B1(vu<<6));
filldsreq(dsp, data, datalen, DSRQ_READ);
return(doscsireq(getfd(dsp), dsp));
}
/*
|| inquiry12 - issue group 0 "Inquiry" command (0x12)
*/
inquiry12(dsp, data, datalen, vu)
struct dsreq *dsp;
caddr_t data;
long datalen;
char vu;
{
fillg0cmd(dsp, CMDBUF(dsp), G0_INQU, 0, 0, 0, B1(datalen), B1(vu<<6));
filldsreq(dsp, data, datalen, DSRQ_READ|DSRQ_SENSE);
return(doscsireq(getfd(dsp), dsp));
}
/*
|| modeselect15 - issue group 0 "Mode Select" command (0x15)
||
|| save 0 - don't save saveable pages
|| 1 - save saveable pages
*/
modeselect15(dsp, data, datalen, save, vu)
struct dsreq *dsp;
caddr_t data;
long datalen;
char save, vu;
{
fillg0cmd(dsp, CMDBUF(dsp), G0_MSEL, save&1, 0, 0, B1(datalen), B1(vu<<6));
filldsreq(dsp, data, datalen, DSRQ_WRITE|DSRQ_SENSE);
return(doscsireq(getfd(dsp), dsp));
}
/*
|| modesense1a - issue group 0 "Mode Sense" command (0x1a)
||
|| pagectrl 0 - current values
|| 1 - changeable values
|| 2 - default values
|| 3 - saved values
||
|| pagecode 0 - vendor unique
|| 1 - error recovery
|| 2 - disconnect/reconnect
|| 3 - direct access dev. fmt.
|| 4 - rigid disk geometry
|| 5 - flexible disk
|| 6-9 - see specific dev. types
|| 0a - implemented options
|| 0b - medium types supported
|| 3f - return all pages
*/
modesense1a(dsp, data, datalen, pagectrl, pagecode, vu)
struct dsreq *dsp;
caddr_t data;
long datalen;
char pagectrl, pagecode, vu;
{
fillg0cmd(dsp, CMDBUF(dsp), G0_MSEN, 0x10,
((pagectrl&3)<<6) | (pagecode&0x3F),
0, B1(datalen), B1(vu<<6));
filldsreq(dsp, data, datalen, DSRQ_READ|DSRQ_SENSE);
return(doscsireq(getfd(dsp), dsp));
}
/*
|| senddiagnostic1d - issue group 0 "Send Diagnostic" command (0x1d)
||
|| self 0 - run test, hold results
|| 1 - run test, return status
||
|| dofl 0 - device online
|| 1 - device offline
||
|| uofl 0 - unit online
|| 1 - unit offline
*/
senddiagnostic1d(dsp, data, datalen, self, dofl, uofl, vu)
struct dsreq *dsp;
caddr_t data;
long datalen;
char self, dofl, uofl, vu;
{
fillg0cmd(dsp, CMDBUF(dsp), G0_MSEN,
(self&1)<<2 | (dofl&1)<<1 | (uofl&1),
0, B2(datalen), B1(vu<<6));
filldsreq(dsp, data, datalen, DSRQ_READ|DSRQ_SENSE);
return(doscsireq(getfd(dsp), dsp));
}
/*
|| readcapacity25 - issue group 1 "Read Capacity" command (0x25)
||
|| pmi 0 - return last logical block, entire unit
|| 1 - return last logical block, current track
*/
readcapacity25(dsp, data, datalen, lba, pmi, vu)
struct dsreq *dsp;
caddr_t data;
long datalen, lba;
char pmi, vu;
{
fillg1cmd(dsp, CMDBUF(dsp), G1_RCAP, 0, B4(lba), 0, 0, pmi&1, B1(vu<<6));
filldsreq(dsp, data, datalen, DSRQ_READ|DSRQ_SENSE
/* |DSRQ_CTRL2 */ );
/* dsp->ds_time = 100; /* often takes a while */
return(doscsireq(getfd(dsp), dsp));
}
/*
|| readextended28 - issue group 1 "Read Extended" command (0x28)
*/
readextended28(dsp, data, datalen, lba, vu)
struct dsreq *dsp;
caddr_t data;
long datalen, lba;
char vu;
{
fillg1cmd(dsp, CMDBUF(dsp), G1_READ, 0, B4(lba), 0, B2(datalen), B1(vu<<6));
filldsreq(dsp, data, datalen, DSRQ_READ|DSRQ_SENSE
/* |DSRQ_CTRL2 */ );
/* dsp->ds_time = 100; /* often takes a while */
return(doscsireq(getfd(dsp), dsp));
}
/*
|| writeextended2a - issue group 1 "Write Extended" command (0x2a)
*/
writeextended2a(dsp, data, datalen, lba, vu)
struct dsreq *dsp;
caddr_t data;
long datalen, lba;
char vu;
{
fillg1cmd(dsp, CMDBUF(dsp), G1_WRIT, 0, B4(lba), 0, B2(datalen), B1(vu<<6));
filldsreq(dsp, data, datalen, DSRQ_READ|DSRQ_SENSE
/* |DSRQ_CTRL2 */ );
/* dsp->ds_time = 100; /* often takes a while */
return(doscsireq(getfd(dsp), dsp));
}
/*
|| Support functions ----------------------------------------------------
*/
/*
|| fillg0cmd - Fill a Group 0 command buffer
*/
fillg0cmd(dsp, cmd, b0,b1,b2,b3,b4,b5)
struct dsreq *dsp;
uchar_t *cmd, b0,b1,b2,b3,b4,b5;
{
uchar_t *c = cmd;
DSDBG(fprintf(stderr,"fillg0cmd(%x,%x, %02x %02x %02x %02x %02x %02x)\n",
dsp, cmd, b0,b1,b2,b3,b4,b5));
*c++ = b0, *c++ = b1, *c++ = b2, *c++ = b3, *c++ = b4, *c++ = b5;
CMDBUF(dsp) = (caddr_t) cmd;
CMDLEN(dsp) = 6;
}
/*
|| fillg1cmd - Fill a Group 1 command buffer
*/
fillg1cmd(dsp, cmd, b0,b1,b2,b3,b4,b5,b6,b7,b8,b9)
struct dsreq *dsp;
uchar_t *cmd, b0,b1,b2,b3,b4,b5,b6,b7,b8,b9;
{
uchar_t *c = cmd;
DSDBG(fprintf(stderr,
"fillg1cmd(%x,%x, %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x)\n",
dsp, cmd, b0,b1,b2,b3,b4,b5,b6,b7,b8,b9));
*c++ = b0, *c++ = b1, *c++ = b2, *c++ = b3, *c++ = b4, *c++ = b5;
*c++ = b6, *c++ = b7, *c++ = b8, *c++ = b9;
CMDBUF(dsp) = (caddr_t) cmd;
CMDLEN(dsp) = 10;
}
/*
|| filldsreq - Fill a dsreq structure
*/
filldsreq(dsp,data,datalen,flags)
struct dsreq *dsp;
uchar_t *data;
{
DSDBG(fprintf(stderr,"filldsreq(%x,%x,%d,%x) cmdlen %d\n",
dsp,data,datalen,flags,CMDLEN(dsp)));
dsp->ds_flags = flags | dsreqflags |
(((dsdebug&1) ? DSRQ_TRACE : 0) |
((dsdebug&2) ? DSRQ_PRINT : 0));
dsp->ds_time = 10 * 1000; /* default to 10 seconds */
dsp->ds_link = 0;
dsp->ds_synch = 0;
dsp->ds_ret = 0;
DATABUF(dsp) = (caddr_t) data;
DATALEN(dsp) = datalen;
}
/*
|| bprint - print array of bytes, in hex.
*/
#define hex(x) "0123456789ABCDEF" [ (x) & 0xF ]
bprint(s,n,nperline,space)
char *s;
{
int i, x;
char *sp = (space) ? " ": "";
for(i=0;i<n;i++) {
x = s[i];
fprintf(stderr,((i%4==3)?"%c%c%s%s":"%c%c%s"),
hex(x>>4), hex(x), sp, sp);
if ( i%nperline == (nperline - 1) )
fprintf(stderr,"\n");
}
if ( space )
fprintf(stderr,"\n");
}
/*
|| doscsireq - issue scsi command, return status or -1 error.
*/
doscsireq( fd, dsp)
int fd; /* ioctl file descriptor */
struct dsreq *dsp; /* devscsi request packet */
{
int cc;
int retries = 4;
uchar_t sbyte;
DSDBG(fprintf(stderr,"doscsireq(%d,%x) %x ---- %s\n",fd,dsp,
(CMDBUF(dsp))[0],
ds_vtostr( (CMDBUF(dsp))[0], cmdnametab)));
/*
* loop, issuing command
* until done, or further retry pointless
*/
while ( --retries > 0 ) {
caddr_t sp;
sp = SENSEBUF(dsp);
DSDBG(fprintf(stderr,"cmdbuf = ");
bprint(CMDBUF(dsp),CMDLEN(dsp),16,1));
if ( (dsp->ds_flags & DSRQ_WRITE) )
DSDBG(bprint( DATABUF(dsp), min(50,DATALEN(dsp)),16,1 ));
DSDBG(fprintf(stderr,"databuf datalen %x %d\n",DATABUF(dsp), DATALEN(dsp)));
cc = ioctl( fd, DS_ENTER, dsp);
if ( cc < 0) {
ds_panic(dsp, "cannot ioctl fd %d\n",fd);
}
DSDBG(fprintf(stderr,"cmdlen after ioctl=%d\n",CMDLEN(dsp)));
DSDBG(fprintf(stderr,"ioctl=%d ret=%x %s",
cc, RET(dsp),
RET(dsp) ? ds_vtostr(RET(dsp),dsrtnametab) : ""));
DSDBG(if (SENSESENT(dsp)) fprintf(stderr," sensesent=%d",
SENSESENT(dsp)));
DSDBG(fprintf(stderr,
" cmdsent=%d datasent=%d sbyte=%x %s\n",
CMDSENT(dsp), DATASENT(dsp), STATUS(dsp),
ds_vtostr(STATUS(dsp), cmdstatustab)));
DSDBG(if ( FLAGS(dsp) & DSRQ_READ )
bprint( DATABUF(dsp), min(16*16,DATASENT(dsp)), 16,1));
#ifdef aux
/*
* check for AUX bus-error
* we retry with poll-dma
*/
if ( RET(dsp) == DSRT_AGAIN ) {
int n = SDC_RDPOLL|SDC_WRPOLL;
DSDBG(fprintf(stderr,"setting rd/wr-poll"));
cc = ioctl( fd, DS_SET, n); /* set bits */
if ( cc != 0 )
return -1;
}
#endif aux
if ( RET(dsp) == DSRT_NOSEL )
continue; /* retry noselect 3X */
/* decode sense data returned */
if ( SENSESENT(dsp) ) {
DSDBG(
fprintf(stderr, "sense key %x - %s\n",
SENSEKEY(sp),
ds_vtostr( SENSEKEY(sp), sensekeytab));
bprint( SENSEBUF(dsp),
min(100, SENSESENT(dsp)),
16,1);
);
}
DSDBG(fprintf(stderr, "sbyte %x\n", STATUS(dsp)));
/* decode scsi command status byte */
sbyte = STATUS(dsp);
switch (sbyte) {
case 0x08: /* BUSY */
case 0x18: /* RESERV CONFLICT */
sleep(2);
continue;
case 0x00: /* GOOD */
case 0x02: /* CHECK CONDITION */
case 0x10: /* INTERM/GOOD */
default:
return sbyte;
}
}
return -1; /* fail retry limit */
}
/*
|| opttovar - lookup option in table, return var addr (NULL if fail)
*/
int *
opttovar( ostr, table)
char *ostr;
struct opttab{
char *opt;
int *var;
} *table;
{
register struct opttab *tp;
for (tp=table; (tp->var); tp++)
if ( strncmp( ostr, tp->opt, 3) == 0 )
break;
if ( !tp->var )
fprintf(stderr,"unknown option %s", ostr);
return (tp->var);
}
/*
|| ds_vtostr - lookup value in table to return string pointer
*/
char *
ds_vtostr( v, table)
long v;
struct vtab *table;
{
register struct vtab *tp;
for (tp=table; (tp->string); tp++)
if ( v == tp->val )
break;
return (tp->string) ? tp->string : "";
}
/*
|| ds_panic - yelp, leave...
*/
ds_panic( fmt, v)
char *fmt;
int v;
{
extern errno;
fprintf(stderr,fmt,v);
fprintf(stderr,"\nerrno = %d\n",errno);
exit(1);
}
/*
|| ds_zot - go away, with a message.
*/
ds_zot(message)
char *message;
{
fprintf(stderr, "%s\n", message);
exit(1);
}
0707070035050453640407770011710000040000020451370464700560700001000000000000generic 0707070035050453631006660011710000040000010255410464713532400001600000002147generic/dev.c #include <stdio.h>
#include "../scsi.h"
#include "../scsish.h"
#include "fns.h"
static int gen_id(int, int *, int, char **, char *);
static Function fns[] = {
{ "capacity", "capacity [lun=0]", "L?", gen_capacity },
{ "display", "display", "", gen_display },
{ "dev", "dev [type] # dev ? for list", "S?", gen_dev },
{ "help", "help [cmd]", "S?", gen_help },
{ "id", "id [target=0]", "L?", gen_id },
{ "inq", "inq [lun=0]", "L?", gen_inq },
{ "readt", "readt count [lun=0]", "IL?", gen_readt },
{ "reset", "reset", "", gen_reset },
{ "scsi", "scsi bytes... # 6 or 10", "I?I?I?I?I?I?I?I?I?I?", gen_scsi },
{ "sense", "sense [lun=0]", "L?", gen_sense },
{ "start", "start [lun=0]", "L?", gen_start },
{ "stop", "stop [lun=0]", "L?", gen_stop },
{ "testunit", "testunit [lun=0", "L?", gen_tur },
{ 0 }
};
Device genericdev = {
"scsi", "generic scsi",
gen_extsense,
fns
};
static int
gen_id(int niargs, int *iargs, int ncargs, char **cargs, char *err)
{
#pragma ref ncargs
#pragma ref cargs
#pragma ref err
if(niargs == 0)
printf("current SCSI id = %d\n", s_id);
else
scsi_target(iargs[0]);
return(0);
}
0707070035050453621006660011710000040000010255560464677353600001600000002107generic/inq.c #include <stdio.h>
#include "../scsi.h"
#include "../scsish.h"
#include "fns.h"
char *gen_rmb[2] = { "nonremovable", "removable" };
char *gen_devtype[256] = {
"direct access",
"sequential access",
"printer",
"processor",
"worm",
"cd-rom"
};
int
gen_inq(int niargs, int *iargs, int ncargs, char **cargs, char *err)
{
struct scsi_cmd cmd;
struct scsi_return ret;
int n, i;
#pragma ref ncargs
#pragma ref cargs
if(niargs == 0)
for(niargs = 0; niargs < 8; niargs++)
iargs[niargs] = niargs;
for(i = 0; i < niargs; i++){
set6(cmd, 0x12, iargs[i]<<5, 0, 0, 36, 0);
if(n = s_io(0, &cmd, 0, &ret, -36, err))
return(n);
printf("inq(%d,%d): %s %s;", s_id, iargs[i],
gen_rmb[ret.data[1]>>7], gen_devtype[ret.data[0]]);
if(ret.data[4] >= 16){
char buf[256];
fixedstr(&ret.data[8], 8, buf);
printf(" %s", buf);
if(ret.data[4] >= 32){
fixedstr(&ret.data[16], 16, buf);
printf("/%s", buf);
if(ret.data[4] >= 36){
fixedstr(&ret.data[32], 4, buf);
printf(" rev=%s", buf);
}
}
}
printf(" [%d bytes]\n", ret.data[4]);
}
return(0);
}
0707070035050453611006660011710000040000011500150464667004700002000000002650generic/sense.c #include <stdio.h>
#include "../scsi.h"
#include "../scsish.h"
#include "fns.h"
int
gen_sense(int niargs, int *iargs, int ncargs, char **cargs, char *err)
{
struct scsi_cmd cmd;
struct scsi_return ret;
int n;
#pragma ref ncargs
#pragma ref cargs
if(niargs == 0)
iargs[0] = 0;
set6(cmd, 0x03, iargs[0]<<5, 0, 0, 4, 0);
if(n = s_io(0, &cmd, 0, &ret, 4, err))
return(n);
printf("sense(%d,%d): ", s_id, iargs[0]);
if((ret.data[0]&0x7F) == 0)
printf("no error\n");
else {
printf("error class=0x%x, code=0x%x, sense=0x%x",
(ret.data[0]>>4)&7, ret.data[0]&0xF, ret.data[2]&0xF);
if(ret.data[0]&0x80)
printf(", addr=0x%x", ret.data[3]+256L*ret.data[2]+256L*256*ret.data[1]);
printf("\n");
}
return(0);
}
static char *exstab[16] =
{
"no sense",
"recovered error",
"not ready",
"medium error",
"hardware error",
"illegal request",
"unit attention",
"data protect",
"blank check",
"vendor specific (#9)",
"copy aborted",
"aborted command",
"equal",
"volume overflow",
"miscompare",
"reserved (#f)",
};
void
gen_extsense(uchar *data, char *dest, int ndata)
{
int class;
class = (data[0]>>4)&7;
if(class == 7){
if(data[0]&0x80)
sprintf(dest, "extended sense: %s info=#%2.2x#%2.2x#%2.2x#%2.2x", exstab[data[2]&0xF], data[3], data[4], data[5], data[6]);
else
sprintf(dest, "extended sense: %s", exstab[data[2]&0xF]);
} else {
sprintf(dest, "sense: class=#%x, code=#%x", class, data[0]&0xF);
}
}
0707070035050453571006660011710000040000010451570457563432000002000000000613generic/start.c #include <stdio.h>
#include "../scsi.h"
#include "../scsish.h"
#include "fns.h"
int
gen_start(int niargs, int *iargs, int ncargs, char **cargs, char *err)
{
struct scsi_cmd cmd;
struct scsi_return ret;
int n;
#pragma ref ncargs
#pragma ref cargs
if(niargs == 0)
iargs[0] = 0;
set6(cmd, 0x1B, iargs[0]<<5, 0, 0, 1, 0);
if(n = s_io(0, &cmd, 0, &ret, 0, err))
return(n);
return(0);
}
0707070035050453561006660011710000040000010451730457563432000001700000000612generic/stop.c #include <stdio.h>
#include "../scsi.h"
#include "../scsish.h"
#include "fns.h"
int
gen_stop(int niargs, int *iargs, int ncargs, char **cargs, char *err)
{
struct scsi_cmd cmd;
struct scsi_return ret;
int n;
#pragma ref ncargs
#pragma ref cargs
if(niargs == 0)
iargs[0] = 0;
set6(cmd, 0x1B, iargs[0]<<5, 0, 0, 0, 0);
if(n = s_io(0, &cmd, 0, &ret, 0, err))
return(n);
return(0);
}
0707070035050453551006660011710000040000010451750457563432000002300000001115generic/capacity.c #include <stdio.h>
#include "../scsi.h"
#include "../scsish.h"
#include "fns.h"
int
gen_capacity(int niargs, int *iargs, int ncargs, char **cargs, char *err)
{
struct scsi_cmd cmd;
struct scsi_return ret;
int n;
unsigned long ns, ss;
#pragma ref ncargs
#pragma ref cargs
if(niargs == 0)
iargs[0] = 0;
set10(cmd, 0x25, iargs[0]<<5, 0, 0, 0, 0, 0, 0, 0, 0);
if(n = s_io(0, &cmd, 0, &ret, 8, err))
return(n);
ns = longat(&ret.data[0]);
ss = longat(&ret.data[4]);
printf("capacity(%d,%d): %ld blocks of %ld bytes (#%xx#%x)\n", s_id, iargs[0],
ns, ss, ns, ss);
return(0);
}
0707070035050453541006660011710000040000010451770457563432000002200000002323generic/display.c #include <stdio.h>
#include "../scsi.h"
#include "../scsish.h"
#include "fns.h"
extern char *gen_rmb[2];
extern char *gen_devtype[256];
int
gen_display(int niargs, int *iargs, int ncargs, char **cargs, char *err)
{
struct scsi_cmd cmd;
struct scsi_return ret;
int n, i, old_id;
int retv = 0;
char rev[100], vendor[100], product[100];
#pragma ref niargs
#pragma ref iargs
#pragma ref ncargs
#pragma ref cargs
old_id = s_id;
for(s_id = 0; s_id < 8; s_id++){
printf("target %d:\n");
set6(cmd, 0x00, 0, 0, 0, 0, 0);
if(s_io(0, &cmd, 0, &ret, 0, err))
continue;
printf("responded to test unit ready\n");
continue;
for(i = 0; i < 8; i++){
set6(cmd, 0x12, i<<5, 0, 0, 36, 0);
if(n = s_io(0, &cmd, 0, &ret, -36, err)){
retv = n;
break;
}
if(ret.nread >= 16)
fixedstr(&ret.data[8], 8, vendor);
else
sprintf(vendor, "??");
if(ret.nread >= 32)
fixedstr(&ret.data[16], 16, product);
else
sprintf(product, "??");
if(ret.nread >= 16)
fixedstr(&ret.data[32], 4, rev);
else
sprintf(vendor, "??");
printf("\tlun(%d): %s %s, %s/%s rev=%s\n", i,
gen_rmb[ret.data[1]>>7], gen_devtype[ret.data[0]],
vendor, product, rev);
}
}
s_id = old_id;
return(retv);
}
0707070035050453531006660011710000040000011504230464700546400002000000000753generic/reset.c #include <stdio.h>
#include "../scsi.h"
#include "../scsish.h"
#include "fns.h"
#include <scsi.h>
int
gen_reset(int niargs, int *iargs, int ncargs, char **cargs, char *err)
{
struct scsi_cmd cmd;
struct scsi_return ret;
#pragma ref niargs
#pragma ref iargs
#pragma ref ncargs
#pragma ref cargs
set6(cmd, 0, 0, 0, 0, 0, 0);
cmd.bus_id = s_id;
cmd.flags |= SCSI_RESET | SCSI_BRESET;
/* should probably test for some kind of error... */
ss_io(0, &cmd, 0, &ret, 0, err);
return(0);
}
0707070035050453521006660011710000040000010452030457563432100001600000000674generic/tur.c #include <stdio.h>
#include "../scsi.h"
#include "../scsish.h"
#include "fns.h"
int
gen_tur(int niargs, int *iargs, int ncargs, char **cargs, char *err)
{
struct scsi_cmd cmd;
struct scsi_return ret;
int n;
#pragma ref ncargs
#pragma ref cargs
if(niargs == 0)
iargs[0] = 0;
set6(cmd, 0x00, iargs[0]<<5, 0, 0, 0, 0);
if(n = s_io(0, &cmd, 0, &ret, 0, err))
return(n);
printf("(%d,%d): good status\n", s_id, iargs[0]);
return(0);
}
0707070035050453511006660011710000040000010452040457563432100001700000001215generic/scsi.c #include <stdio.h>
#include "../scsi.h"
#include "../scsish.h"
#include "fns.h"
int
gen_scsi(int niargs, int *iargs, int ncargs, char **cargs, char *err)
{
struct scsi_cmd cmd;
struct scsi_return ret;
int n;
#pragma ref ncargs
#pragma ref cargs
switch(niargs)
{
case 6:
set6(cmd, iargs[0], iargs[1], iargs[2], iargs[3], iargs[4], iargs[5]);
break;
case 10:
set10(cmd, iargs[0], iargs[1], iargs[2], iargs[3], iargs[4], iargs[5], iargs[6], iargs[7], iargs[8], iargs[9]);
break;
default:
sprintf(err, "number of bytes (%d) must be 6 or 10\n", niargs);
return(1);
}
if(n = s_io(0, &cmd, 0, &ret, 0, err))
return(n);
return(0);
}
0707070035050453501006660011710000040000010255540464677346100001600000001234generic/fns.h extern int gen_inq(int, int *, int, char **, char *);
extern int gen_dev(int, int *, int, char **, char *);
extern int gen_help(int, int *, int, char **, char *);
extern int gen_sense(int, int *, int, char **, char *);
extern int gen_start(int, int *, int, char **, char *);
extern int gen_stop(int, int *, int, char **, char *);
extern int gen_capacity(int, int *, int, char **, char *);
extern int gen_display(int, int *, int, char **, char *);
extern int gen_reset(int, int *, int, char **, char *);
extern int gen_tur(int, int *, int, char **, char *);
extern int gen_scsi(int, int *, int, char **, char *);
extern int gen_readt(int, int *, int, char **, char *);
0707070035050513641006660011710000040000010255520464677343300002000000002007generic/readt.c #include <stdio.h>
#include "../scsi.h"
#include "../scsish.h"
#include "fns.h"
int
gen_readt(int niargs, int *iargs, int ncargs, char **cargs, char *err)
{
struct scsi_cmd cmd;
struct scsi_return ret;
int n, i;
unsigned long ns, ss;
long bs, addr;
long t1, t2;
#pragma ref ncargs
#pragma ref cargs
if(niargs == 1)
iargs[1] = 0;
set10(cmd, 0x25, iargs[1]<<5, 0, 0, 0, 0, 0, 0, 0, 0);
if(n = s_io(0, &cmd, 0, &ret, 8, err))
return(n);
ns = longat(&ret.data[0]);
ss = longat(&ret.data[4]);
bs = ss? sizeof(ret.data)/ss : 1;
time(&t1);
srand(t1);
addr = nrand(ns-iargs[0])-1;
printf("read(%d,%d): %d blocks @%d (chunk=%dx%d),", s_id, iargs[1], iargs[0], addr, bs, ss);
fflush(stdout);
time(&t1);
for(i = iargs[0]; i > 0; i -= bs){
set10(cmd, 0x28, iargs[1]<<5, addr>>24, addr>>16, addr>>8, addr,
0, 0, bs*0, 0);
if(n = s_io(0, &cmd, 0, &ret, bs*ss, err))
return(n);
addr += bs;
}
time(&t2);
printf(" t=%ds (%.0fKB/s)\n", t2-t1, (iargs[0]*(float)ss/1024.)/((t1 == t2)? 1:t2-t1));
return(0);
}
0707070035050450541006660011710000040000010253650464677245500001400000001727getstatus.c #include <stddef.h>
#include <stdio.h>
#include "scsi.h"
#include "juke.h"
struct Jstatus j_status;
static
dolun(struct Lunstatus *lun, uchar *u)
{
lun->poweron = ((*u)&0x80) == 0;
lun->diskin = ((*u)&0x40) != 0;
lun->ready = ((*u)&0x01) != 0;
u++;
lun->diskindrive = ((*u)&0x80) != 0;
lun->driveshelf = (*u)&0x7F;
u++;
lun->shelfvalid = ((*u)&0x80) != 0;
lun->retshelf = (*u)&0x7F;
}
j_getstatus(char *err)
{
struct scsi_cmd cmd;
struct scsi_return ret;
int i;
set6(cmd, 0x1D, 0, 0, 0, 10, 0);
memset(cmd.data, 0, 10);
cmd.data[0] = 0xE2;
if(s_io(1, &cmd, 10, &ret, 0, err))
return(-1);
set6(cmd, 0x1C, 0, 0, 0, 128, 0);
if(s_io(0, &cmd, 0, &ret, 128, err))
return(-1);
for(i = 0; i < 8; i++)
dolun(&j_status.lun[i], &ret.data[16+4*i]);
for(i = 0; i < NSHELF; i++)
j_status.shelf[i] = ret.data[48+i];
j_status.iounit = ret.data[98];
j_status.carrier = ret.data[99];
j_status.udrive = ret.data[100];
j_status.ldrive = ret.data[101];
return(0);
}
0707070035050422060407770011710000040000020273120464713312700000400000000000inc 0707070035050422051006660011710000040000010273140464713312700001300000000247inc/scsi.h #define SCSI_WR 0x80
#define SCSI_RD 0x40
#define SCSI_BRESET 0x20
#define SCSI_RESET 0x10
#define SCSI_SENSE 0x08
#define SCSI_LTMOUT 0x04
#define SCSI_CERR 0x01
0707070035050450411006660011710000040000010450110457563431200001200000002035iodr_sh.c #include "scsi.h"
#include "juke.h"
j_shelf_to_drive(int sh, Side side, int dr, char *err)
{
struct scsi_cmd cmd;
struct scsi_return ret;
set6(cmd, 0xD6, dr<<5, 0, (sh<<1)|side, 0, 0);
return(s_io(0, &cmd, 0, &ret, 0, err));
}
j_drive_to_shelf(int dr, int sh, Side side, char *err)
{
struct scsi_cmd cmd;
struct scsi_return ret;
if(sh < 0)
set6(cmd, 0xD7, dr<<5, 0, 0, 0, 0);
else
set6(cmd, 0xD7, (dr<<5)|1, 0, (sh<<1)|side, 0, 0);
return(s_io(0, &cmd, 0, &ret, 0, err));
}
int
j_empty_drive(long tlimit, char *buf)
{
int i, tstop;
tstop = time((long *)0) + tlimit;
while(time((long *)0) <= tstop){
setnlun(); /* in case it changes */
/* look for empty drives */
for(i = 0; i < nlun; i++)
if(!j_status.lun[i].diskin)
return(i);
/* look for spun down drives */
for(i = 0; i < nlun; i++){
if(!j_status.lun[i].ready){
if(j_drive_to_shelf(i, -1, SIDEA, buf))
return(-1);
else
return(i);
}
}
sleep(10);
if(j_getstatus(buf)) /* get the jukebox status */
return(-1);
}
return(-1);
}
0707070035050450401006660011710000040000010450120457563431300001400000002643ioshelves.c #include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "scsi.h"
#include "juke.h"
char *j_shelf[NSHELF];
int j_wrshelf = 0;
j_rdshelves(char *err)
{
FILE *fp;
static haveread = 0;
int shno;
char vname[256];
if(haveread)
return(0);
for(shno = 0; shno < NSHELF; shno++)
j_shelf[shno] = 0;
if((fp = fopen(JUKEDIR, "r")) == NULL){
pperror(err, JUKEDIR);
return(-1);
}
while(fscanf(fp, "%d %s\n", &shno, vname) == 2){
if((shno < 0) || (shno >= NSHELF)){
fprintf(stderr, "Warning: bad shelf number in %s: %d (vol_id=%s)\n",
JUKEDIR, shno, vname);
continue;
}
j_shelf[shno] = strdup(vname);
}
fclose(fp);
haveread = 1;
return(0);
}
j_wrshelves(char *err)
{
FILE *fp;
int shno;
if((fp = fopen(JUKEDIR, "w")) == NULL){
pperror(err, JUKEDIR);
return(-1);
}
for(shno = 0; shno < NSHELF; shno++)
if(j_shelf[shno])
fprintf(fp, "%d %s\n", shno, j_shelf[shno]);
fclose(fp);
return(0);
}
int
j_shelfof(char *vol_id)
{
int i;
char buf[512];
for(;;){
for(i = 0; i < NSHELF; i++)
if(j_shelf[i] && (strcmp(j_shelf[i], vol_id) == 0))
return(i);
if((i = warm_inv(buf)) <= 0)
break;
}
if(i < 0)
fprintf(stderr, "jukebox: %s\n", buf);
return(-1);
}
int
j_driveof(char *vol_id)
{
int i, sh;
if((sh = j_shelfof(vol_id)) < 0)
return(-1);
for(i = 0; i < NLUN; i++)
if(j_status.lun[i].shelfvalid && (j_status.lun[i].retshelf == sh))
return(i);
return(-1);
}
0707070035050452251006660011710000040000010447370457563430700000700000004561juke.3 .TH INTERNAL 3
.CT 2 file_io
.SH NAME
jukebox routines
.tr %"
.SH SYNOPSIS
.B "#include %hdr.h%"
.PP
.tr %%
.B "int j_shelf_to_drive(int sh, Side side, int dr, char *err)"
.PP
.B "int j_drive_to_shelf(int dr, int sh, Side side, char *err)"
.PP
.B "int j_empty_drive(int tlim, char *buf)"
.PP
.B "void j_rdshelves(char *buf)"
.PP
.B "int j_getstatus(char *buf)"
.PP
.B "int j_scsiio(struct scsi_cmd *cmd, int ncmd,"
.br
.B "\ \ \ \ \ \ struct scsi_return *ret, int nret, char *err)"
.PP
.B "int j_shelfof(char *vol_id)"
.PP
.B "int j_volid(int dr, char *err)"
.PP
.B "extern char *j_shelf[NSHELF];"
.PP
.B "extern void pperror(char *buf, char *mesg);
.SH DESCRIPTION
.I J_shelf_to_drive
places the disk in shelf
.I sh
in logical drive
.IR dr .
It returns 0 on success;
otherwise an error message is placed in
.I err .
.PP
.I J_drive_to_shelf
places the disk
in logical drive
.IR dr
in shelf
.IR sh .
If
.I sh
is negative,
the disk is returned to its home shelf.
It returns 0 on success;
otherwise an error message is placed in
.IR err .
.PP
.I J_rdshelves
initializes each element of
.I j_shelf
to the volid of the disk on that shelf.
A zero pointer means there is no disk;
a name of
.B UNALLOCATED
means the disk has not been allocated a name yet.
It returns 0 on success;
otherwise an error message is placed in
.IR err .
.PP
.I J_getstatus
initializes
.B j_status
which include the following fields:
.EX
struct Lunstatus lun[NLUN]; /* disk status */
uchar shelf[NSHELF]; /* shelf status */
uchar iounit; /* I/O unit status */
uchar carrier; /* carrier status */
uchar udrive; /* upper drive status */
uchar ldrive; /* lower drive status */
.EE
A return value of 0 implies success;
otherwise \-1 is returned and an error message is placed in
.IR err .
.PP
.I J_scsiio
performs a SCSI transaction.
It sends the command in
.I cmd
and
.I ncmd
data bytes and stores the return status in
.IR ret .
A return value of 0 implies success;
otherwise \-1 is returned and an error message is placed in
.IR err .
.PP
.I J_shelfof
returns the shelf number of the disk labelled
.IR vol_id .
If there is no such disk,
\-1 is returned.
.PP
.I J_volid
returns the volid of the disk on drive
.I dr
in
.IR err .
A return value of 0 implies success;
otherwise \-1 is returned and an error message is placed in
.IR err .
.PP
.I Pperror
returns an error message that is contained in
.IR buf.
.PP
.SH "SEE ALSO"
.SH DIAGNOSTICS
0707070035050453601006660011710000040000010447430457563430700000700000003362juke.h #define NLUN 8
#define NSHELF 50
extern int nlun;
extern void setnlun(void);
extern char *j_shelf[NSHELF];
extern int j_wrshelf; /* need to write out shelves */
extern j_rdshelves(char *err);
extern j_wrshelves(char *err);
extern j_inventory(char cold, int tlim, char *err);
typedef enum { SIDEA = 0, SIDEB = 1 } Side;
struct Lunstatus
{
unsigned int poweron:1; /* is power on ? */
unsigned int diskin:1; /* is disk in drive? */
unsigned int ready:1; /* is disk spun up or spun down? */
unsigned int writeprotect:1; /* is disk write protected? */
unsigned int diskindrive:1; /* is driveshelf a drive number? */
unsigned int shelfvalid:1; /* is retshelf valid? */
uchar driveshelf; /* drive number */
uchar retshelf; /* return shelf */
};
struct Jstatus
{
struct Lunstatus lun[NLUN]; /* disk status */
uchar shelf[NSHELF]; /* shelf status */
uchar iounit; /* I/O unit status */
uchar carrier; /* carrier status */
uchar udrive; /* upper drive status */
uchar ldrive; /* lower drive status */
};
extern struct Jstatus j_status;
extern int j_getstatus(char *err);
extern int j_shelfof(char *vol_id);
extern int j_driveof(char *vol_id);
extern char *strdup(char *);
extern int j_shelf_to_drive(int, Side, int, char *);
extern int j_drive_to_shelf(int, int, Side, char *);
extern int j_empty_drive(long, char *);
extern int j_rvolid(int, char *);
extern int j_wvolid(int, char *, char *);
extern void pperror(char *buf, char *mesg);
extern int reserve_drive(int, char *);
extern int release_drive(int, char *);
extern int cold_inv(char, char *);
extern int warm_inv(char *);
extern int j_load(char *vol_id, char *buf, long tlim);
extern int j_unload(char *vol_id, char *buf);
#define JUKEDIR "/usr/worm/jukedir"
#define UNALLOCATED "<unallocated>"
0707070035050450551007770011710000040000010576210464700562400001000000215126jukebox \ H"