diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/scuzz |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/scuzz')
-rwxr-xr-x | sys/src/cmd/scuzz/cdaudio.c | 159 | ||||
-rwxr-xr-x | sys/src/cmd/scuzz/cdr.c | 218 | ||||
-rwxr-xr-x | sys/src/cmd/scuzz/changer.c | 62 | ||||
-rwxr-xr-x | sys/src/cmd/scuzz/mkfile | 15 | ||||
-rwxr-xr-x | sys/src/cmd/scuzz/mo.words | 54 | ||||
-rwxr-xr-x | sys/src/cmd/scuzz/scsireq.c | 755 | ||||
-rwxr-xr-x | sys/src/cmd/scuzz/scsireq.h | 212 | ||||
-rwxr-xr-x | sys/src/cmd/scuzz/scuzz.c | 1958 | ||||
-rwxr-xr-x | sys/src/cmd/scuzz/sense.c | 44 |
9 files changed, 3477 insertions, 0 deletions
diff --git a/sys/src/cmd/scuzz/cdaudio.c b/sys/src/cmd/scuzz/cdaudio.c new file mode 100755 index 000000000..4ffffb68d --- /dev/null +++ b/sys/src/cmd/scuzz/cdaudio.c @@ -0,0 +1,159 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +#include "scsireq.h" + +extern Biobuf bout; + +long +SRcdpause(ScsiReq *rp, int resume) +{ + uchar cmd[10]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdCDpause; + cmd[8] = resume; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = cmd; + rp->data.count = 0; + rp->data.write = 1; + return SRrequest(rp); +} + +long +SRcdstop(ScsiReq *rp) +{ + uchar cmd[10]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdCDstop; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = cmd; + rp->data.count = 0; + rp->data.write = 1; + return SRrequest(rp); +} + +static long +_SRcdplay(ScsiReq *rp, long lba, long length) +{ + uchar cmd[12]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdCDplay; + cmd[2] = lba>>24; + cmd[3] = lba>>16; + cmd[4] = lba>>8; + cmd[5] = lba; + cmd[6] = length>>24; + cmd[7] = length>>16; + cmd[8] = length>>8; + cmd[9] = length; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = cmd; + rp->data.count = 0; + rp->data.write = 1; + + return SRrequest(rp); +} + +static struct { + int trackno; + long lba; + long length; +} tracks[100]; +static int ntracks; + +long +SRcdplay(ScsiReq *rp, int raw, long start, long length) +{ + uchar d[100*8+4], *p; + int lba, n, tdl; + + if(raw || start == 0) + return _SRcdplay(rp, start, length); + + ntracks = 0; + if(SRTOC(rp, d, sizeof(d), 0, 0) == -1){ + if(rp->status == STok) + Bprint(&bout, "\t(probably empty)\n"); + return -1; + } + tdl = (d[0]<<8)|d[1]; + for(p = &d[4], n = tdl-2; n; n -= 8, p += 8){ + tracks[ntracks].trackno = p[2]; + lba = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7]; + tracks[ntracks].lba = lba; + if(ntracks > 0) + tracks[ntracks-1].length = lba-tracks[ntracks-1].lba; + ntracks++; + } + if(ntracks > 0) + tracks[ntracks-1].length = 0xFFFFFFFF; + + for(n = 0; n < ntracks; n++){ + if(start != tracks[n].trackno) + continue; + return _SRcdplay(rp, tracks[n].lba, tracks[n].length); + } + + return -1; +} + +long +SRcdload(ScsiReq *rp, int load, int slot) +{ + uchar cmd[12]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdCDload; + if(load) + cmd[4] = 0x03; + else + cmd[4] = 0x02; + cmd[8] = slot; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = cmd; + rp->data.count = 0; + rp->data.write = 1; + return SRrequest(rp); +} + +long +SRcdstatus(ScsiReq *rp, uchar *list, int nbytes) +{ + uchar cmd[12]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdCDstatus; + cmd[8] = nbytes>>8; + cmd[9] = nbytes; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = list; + rp->data.count = nbytes; + rp->data.write = 0; + return SRrequest(rp); +} + +long +SRgetconf(ScsiReq *rp, uchar *list, int nbytes) +{ + uchar cmd[10]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = Scmdgetconf; + cmd[7] = nbytes>>8; + cmd[8] = nbytes; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = list; + rp->data.count = nbytes; + rp->data.write = 0; + return SRrequest(rp); +} diff --git a/sys/src/cmd/scuzz/cdr.c b/sys/src/cmd/scuzz/cdr.c new file mode 100755 index 000000000..f672954ec --- /dev/null +++ b/sys/src/cmd/scuzz/cdr.c @@ -0,0 +1,218 @@ +#include <u.h> +#include <libc.h> + +#include "scsireq.h" + +long +SRblank(ScsiReq *rp, uchar type, uchar track) +{ + uchar cmd[12]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdBlank; + cmd[1] = type; + cmd[2] = track>>24; + cmd[3] = track>>16; + cmd[4] = track>>8; + cmd[5] = track; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = cmd; + rp->data.count = 0; + rp->data.write = 1; + return SRrequest(rp); +} + +long +SRsynccache(ScsiReq *rp) +{ + uchar cmd[10]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdSynccache; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = cmd; + rp->data.count = 0; + rp->data.write = 1; + return SRrequest(rp); +} + +long +SRTOC(ScsiReq *rp, void *data, int nbytes, uchar format, uchar track) +{ + uchar cmd[10]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdRTOC; + cmd[2] = format; + cmd[6] = track; + cmd[7] = nbytes>>8; + cmd[8] = nbytes; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = data; + rp->data.count = nbytes; + rp->data.write = 0; + return SRrequest(rp); +} + +long +SRrdiscinfo(ScsiReq *rp, void *data, int nbytes) +{ + uchar cmd[10]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdRdiscinfo; + cmd[7] = nbytes>>8; + cmd[8] = nbytes; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = data; + rp->data.count = nbytes; + rp->data.write = 0; + return SRrequest(rp); +} + +long +SRrtrackinfo(ScsiReq *rp, void *data, int nbytes, int track) +{ + uchar cmd[10]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdRtrackinfo; + cmd[1] = 0x01; + cmd[2] = track>>24; + cmd[3] = track>>16; + cmd[4] = track>>8; + cmd[5] = track; + cmd[7] = nbytes>>8; + cmd[8] = nbytes; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = data; + rp->data.count = nbytes; + rp->data.write = 0; + return SRrequest(rp); +} + +long +SRfwaddr(ScsiReq *rp, uchar track, uchar mode, uchar npa, uchar *data) +{ + uchar cmd[10]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdFwaddr; + cmd[2] = track; + cmd[3] = mode; + cmd[7] = npa; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = data; + rp->data.count = MaxDirData; + rp->data.write = 0; + return SRrequest(rp); +} + +long +SRtreserve(ScsiReq *rp, long nbytes) +{ + uchar cmd[10]; + long n; + + if((nbytes % rp->lbsize)){ + rp->status = Status_BADARG; + return -1; + } + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdTreserve; + n = nbytes/rp->lbsize; + cmd[5] = n>>24; + cmd[6] = n>>16; + cmd[7] = n>>8; + cmd[8] = n; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = cmd; + rp->data.count = 0; + rp->data.write = 1; + return SRrequest(rp); +} + +long +SRtinfo(ScsiReq *rp, uchar track, uchar *data) +{ + uchar cmd[10]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdTinfo; + cmd[5] = track; + cmd[8] = MaxDirData; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = data; + rp->data.count = MaxDirData; + rp->data.write = 0; + return SRrequest(rp); +} + +long +SRwtrack(ScsiReq *rp, void *buf, long nbytes, uchar track, uchar mode) +{ + uchar cmd[10]; + long m, n; + + if((nbytes % rp->lbsize) || nbytes > maxiosize){ + rp->status = Status_BADARG; + return -1; + } + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdTwrite; + cmd[5] = track; + cmd[6] = mode; + n = nbytes/rp->lbsize; + cmd[7] = n>>8; + cmd[8] = n; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = buf; + rp->data.count = nbytes; + rp->data.write = 1; + m = SRrequest(rp); + if(m < 0) + return -1; + rp->offset += n; + return m; +} + +long +SRmload(ScsiReq *rp, uchar code) +{ + uchar cmd[12]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdMload; + cmd[8] = code; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = cmd; + rp->data.count = 0; + rp->data.write = 1; + return SRrequest(rp); +} + +long +SRfixation(ScsiReq *rp, uchar type) +{ + uchar cmd[10]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdFixation; + cmd[8] = type; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = cmd; + rp->data.count = 0; + rp->data.write = 1; + return SRrequest(rp); +} diff --git a/sys/src/cmd/scuzz/changer.c b/sys/src/cmd/scuzz/changer.c new file mode 100755 index 000000000..80e551ab3 --- /dev/null +++ b/sys/src/cmd/scuzz/changer.c @@ -0,0 +1,62 @@ +#include <u.h> +#include <libc.h> + +#include "scsireq.h" + +long +SReinitialise(ScsiReq *rp) +{ + uchar cmd[6]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdEInitialise; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = cmd; + rp->data.count = 0; + rp->data.write = 1; + return SRrequest(rp); +} + +long +SRmmove(ScsiReq *rp, int transport, int source, int destination, int invert) +{ + uchar cmd[12]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdMMove; + cmd[2] = transport>>8; + cmd[3] = transport; + cmd[4] = source>>8; + cmd[5] = source; + cmd[6] = destination>>8; + cmd[7] = destination; + cmd[10] = invert & 0x01; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = cmd; + rp->data.count = 0; + rp->data.write = 1; + return SRrequest(rp); +} + +long +SRestatus(ScsiReq *rp, uchar type, uchar *list, int nbytes) +{ + uchar cmd[12]; + + memset(cmd, 0, sizeof(cmd)); + cmd[0] = ScmdEStatus; + cmd[1] = type & 0x07; + cmd[4] = 0xFF; + cmd[5] = 0xFF; + cmd[7] = nbytes>>16; + cmd[8] = nbytes>>8; + cmd[9] = nbytes; + rp->cmd.p = cmd; + rp->cmd.count = sizeof(cmd); + rp->data.p = list; + rp->data.count = nbytes; + rp->data.write = 0; + return SRrequest(rp); +} diff --git a/sys/src/cmd/scuzz/mkfile b/sys/src/cmd/scuzz/mkfile new file mode 100755 index 000000000..f0270cd16 --- /dev/null +++ b/sys/src/cmd/scuzz/mkfile @@ -0,0 +1,15 @@ +</$objtype/mkfile + +TARG = scuzz + +HFILES = scsireq.h +OFILES = scuzz.$O cdaudio.$O cdr.$O changer.$O scsireq.$O sense.$O + +BIN=/$objtype/bin +UPDATE=\ + mkfile\ + $HFILES\ + ${OFILES:%.$O=%.c}\ + ${TARG:%=/386/bin/%}\ + +</sys/src/cmd/mkone diff --git a/sys/src/cmd/scuzz/mo.words b/sys/src/cmd/scuzz/mo.words new file mode 100755 index 000000000..662e14d8c --- /dev/null +++ b/sys/src/cmd/scuzz/mo.words @@ -0,0 +1,54 @@ +/* + * these scuzz modesense commands return the following for an hp worm drive: + * + * modesense + * Header + * 00 5E 02 90 00 00 00 08 + * Block 0 + * 00 13 47 70 00 00 08 00 (density 00 blocks 1263472 length 2048) + * Page 01 10 + * 80 05 00 00 00 00 05 00 00 00 + * Page 02 14 + * 20 20 00 00 00 00 00 00 00 20 00 00 00 00 + * Page 08 10 + * 06 00 00 40 00 01 00 01 00 40 + * Page 0A 6 + * 00 00 00 00 00 00 + * Page 0B 6 + * 00 00 02 03 00 00 + * Page 20 10 + * 04 00 00 00 00 00 00 00 00 18 + * Page 21 10 + * 00 00 01 00 02 00 00 00 00 00 + * Page 00 0 + * + * modesense6 + * Header + * 5B 02 90 08 + * Block 0 + * 00 13 47 70 00 00 08 00 (density 00 blocks 1263472 length 2048) + * Page 01 10 + * 80 05 00 00 00 00 05 00 00 00 + * Page 02 14 + * 20 20 00 00 00 00 00 00 00 20 00 00 00 00 + * Page 08 10 + * 06 00 00 40 00 01 00 01 00 40 + * Page 0A 6 + * 00 00 00 00 00 00 + * Page 0B 6 + * 00 00 02 03 00 00 + * Page 20 10 + * 04 00 00 00 00 00 00 00 00 18 + * Page 21 10 + * 00 00 01 00 02 00 00 00 00 00 + * Page 00 0 + * Page 00 0 + * ok 92 + * + * the drive is: + * + * # cat ctl + * inquiry HP C1113J 1.099904323710 8XMO + * geometry 1263472 2048 + * part data 0 1263472 + */ diff --git a/sys/src/cmd/scuzz/scsireq.c b/sys/src/cmd/scuzz/scsireq.c new file mode 100755 index 000000000..5407424f7 --- /dev/null +++ b/sys/src/cmd/scuzz/scsireq.c @@ -0,0 +1,755 @@ +#include <u.h> +#include <libc.h> +/* + * BUGS: + * no luns + * and incomplete in many other ways + */ +#include "scsireq.h" + +enum { + Debug = 0, +}; + +/* + * exabyte tape drives, at least old ones like the 8200 and 8505, + * are dumb: you have to read the exact block size on the tape, + * they don't take 10-byte SCSI commands, and various other fine points. + */ +extern int exabyte, force6bytecmds; + +static int debug = Debug; + +long +SRready(ScsiReq *rp) +{ + uchar cmd[6]; + + memset(cmd, 0, sizeof cmd); + rp->cmd.p = cmd; + rp->cmd.count = sizeof cmd; + rp->data.p = cmd; + rp->data.count = 0; + rp->data.write = 1; + return SRrequest(rp); +} + +long +SRrewind(ScsiReq *rp) +{ + uchar cmd[6]; + + memset(cmd, 0, sizeof cmd); + cmd[0] = ScmdRewind; + rp->cmd.p = cmd; + rp->cmd.count = sizeof cmd; + rp->data.p = cmd; + rp->data.count = 0; + rp->data.write = 1; + if(SRrequest(rp) >= 0){ + rp->offset = 0; + return 0; + } + return -1; +} + +long +SRreqsense(ScsiReq *rp) +{ + uchar cmd[6]; + ScsiReq req; + long status; + + if(rp->status == Status_SD){ + rp->status = STok; + return 0; + } + memset(cmd, 0, sizeof cmd); + cmd[0] = ScmdRsense; + cmd[4] = sizeof(req.sense); + memset(&req, 0, sizeof(req)); + if(rp->flags&Fusb) + req.flags |= Fusb; + req.fd = rp->fd; + req.umsc = rp->umsc; + req.cmd.p = cmd; + req.cmd.count = sizeof cmd; + req.data.p = rp->sense; + req.data.count = sizeof(rp->sense); + req.data.write = 0; + status = SRrequest(&req); + rp->status = req.status; + return status; +} + +long +SRformat(ScsiReq *rp) +{ + uchar cmd[6]; + + memset(cmd, 0, sizeof cmd); + cmd[0] = ScmdFormat; + rp->cmd.p = cmd; + rp->cmd.count = sizeof cmd; + rp->data.p = cmd; + rp->data.count = 6; + rp->data.write = 0; + return SRrequest(rp); +} + +long +SRrblimits(ScsiReq *rp, uchar *list) +{ + uchar cmd[6]; + + memset(cmd, 0, sizeof cmd); + cmd[0] = ScmdRblimits; + rp->cmd.p = cmd; + rp->cmd.count = sizeof cmd; + rp->data.p = list; + rp->data.count = 6; + rp->data.write = 0; + return SRrequest(rp); +} + +static int +dirdevrw(ScsiReq *rp, uchar *cmd, long nbytes) +{ + long n; + + n = nbytes / rp->lbsize; + if(rp->offset <= Max24off && n <= 256 && (rp->flags & Frw10) == 0){ + PUTBE24(cmd+1, rp->offset); + cmd[4] = n; + cmd[5] = 0; + return 6; + } + cmd[0] |= ScmdExtread; + cmd[1] = 0; + PUTBELONG(cmd+2, rp->offset); + cmd[6] = 0; + cmd[7] = n>>8; + cmd[8] = n; + cmd[9] = 0; + return 10; +} + +static int +seqdevrw(ScsiReq *rp, uchar *cmd, long nbytes) +{ + long n; + + /* don't set Cmd1sili; we want the ILI bit instead of a fatal error */ + cmd[1] = rp->flags&Fbfixed? Cmd1fixed: 0; + n = nbytes / rp->lbsize; + PUTBE24(cmd+2, n); + cmd[5] = 0; + return 6; +} + +long +SRread(ScsiReq *rp, void *buf, long nbytes) +{ + uchar cmd[10]; + long n; + + if((nbytes % rp->lbsize) || nbytes > maxiosize){ + if(debug) + if (nbytes % rp->lbsize) + fprint(2, "scuzz: i/o size %ld %% %ld != 0\n", + nbytes, rp->lbsize); + else + fprint(2, "scuzz: i/o size %ld > %ld\n", + nbytes, maxiosize); + rp->status = Status_BADARG; + return -1; + } + + /* set up scsi read cmd */ + cmd[0] = ScmdRead; + if(rp->flags & Fseqdev) + rp->cmd.count = seqdevrw(rp, cmd, nbytes); + else + rp->cmd.count = dirdevrw(rp, cmd, nbytes); + rp->cmd.p = cmd; + rp->data.p = buf; + rp->data.count = nbytes; + rp->data.write = 0; + + /* issue it */ + n = SRrequest(rp); + if(n != -1){ /* it worked? */ + rp->offset += n / rp->lbsize; + return n; + } + + /* request failed; maybe we just read a short record? */ + if (exabyte) { + fprint(2, "read error\n"); + rp->status = STcheck; + return n; + } + if(rp->status != Status_SD || !(rp->sense[0] & Sd0valid)) + return -1; + /* compute # of bytes not read */ + n = GETBELONG(rp->sense+3) * rp->lbsize; + if (debug) + fprint(2, + "SRread: request failed with sense data; sense byte count %ld\n", + n); + if(!(rp->flags & Fseqdev)) + return -1; + + /* device is a tape or something similar */ + if (rp->sense[2] == Sd2filemark || rp->sense[2] == 0x08 || + rp->sense[2] & Sd2ili && n > 0) + rp->data.count = nbytes - n; + else + return -1; + n = rp->data.count; + if (!rp->readblock++ || debug) + fprint(2, "SRread: tape data count %ld%s\n", n, + (rp->sense[2] & Sd2ili? " with ILI": "")); + rp->status = STok; + rp->offset += n / rp->lbsize; + return n; +} + +long +SRwrite(ScsiReq *rp, void *buf, long nbytes) +{ + uchar cmd[10]; + long n; + + if((nbytes % rp->lbsize) || nbytes > maxiosize){ + if(debug) + if (nbytes % rp->lbsize) + fprint(2, "scuzz: i/o size %ld %% %ld != 0\n", + nbytes, rp->lbsize); + else + fprint(2, "scuzz: i/o size %ld > %ld\n", + nbytes, maxiosize); + rp->status = Status_BADARG; + return -1; + } + + /* set up scsi write cmd */ + cmd[0] = ScmdWrite; + if(rp->flags & Fseqdev) + rp->cmd.count = seqdevrw(rp, cmd, nbytes); + else + rp->cmd.count = dirdevrw(rp, cmd, nbytes); + rp->cmd.p = cmd; + rp->data.p = buf; + rp->data.count = nbytes; + rp->data.write = 1; + + /* issue it */ + if((n = SRrequest(rp)) == -1){ + if (exabyte) { + fprint(2, "write error\n"); + rp->status = STcheck; + return n; + } + if(rp->status != Status_SD || rp->sense[2] != Sd2eom) + return -1; + if(rp->sense[0] & Sd0valid){ + n -= GETBELONG(rp->sense+3) * rp->lbsize; + rp->data.count = nbytes - n; + } + else + rp->data.count = nbytes; + n = rp->data.count; + } + rp->offset += n / rp->lbsize; + return n; +} + +long +SRseek(ScsiReq *rp, long offset, int type) +{ + uchar cmd[10]; + + switch(type){ + + case 0: + break; + + case 1: + offset += rp->offset; + if(offset >= 0) + break; + /*FALLTHROUGH*/ + + default: + if(debug) + fprint(2, "scuzz: seek failed\n"); + rp->status = Status_BADARG; + return -1; + } + memset(cmd, 0, sizeof cmd); + if(offset <= Max24off && (rp->flags & Frw10) == 0){ + cmd[0] = ScmdSeek; + PUTBE24(cmd+1, offset & Max24off); + rp->cmd.count = 6; + }else{ + cmd[0] = ScmdExtseek; + PUTBELONG(cmd+2, offset); + rp->cmd.count = 10; + } + rp->cmd.p = cmd; + rp->data.p = cmd; + rp->data.count = 0; + rp->data.write = 1; + SRrequest(rp); + if(rp->status == STok) + return rp->offset = offset; + return -1; +} + +long +SRfilemark(ScsiReq *rp, ulong howmany) +{ + uchar cmd[6]; + + memset(cmd, 0, sizeof cmd); + cmd[0] = ScmdFmark; + PUTBE24(cmd+2, howmany); + rp->cmd.p = cmd; + rp->cmd.count = sizeof cmd; + rp->data.p = cmd; + rp->data.count = 0; + rp->data.write = 1; + return SRrequest(rp); +} + +long +SRspace(ScsiReq *rp, uchar code, long howmany) +{ + uchar cmd[6]; + + memset(cmd, 0, sizeof cmd); + cmd[0] = ScmdSpace; + cmd[1] = code; + PUTBE24(cmd+2, howmany); + rp->cmd.p = cmd; + rp->cmd.count = sizeof cmd; + rp->data.p = cmd; + rp->data.count = 0; + rp->data.write = 1; + /* + * what about rp->offset? + */ + return SRrequest(rp); +} + +long +SRinquiry(ScsiReq *rp) +{ + uchar cmd[6]; + + memset(cmd, 0, sizeof cmd); + cmd[0] = ScmdInq; + cmd[4] = sizeof rp->inquiry; + rp->cmd.p = cmd; + rp->cmd.count = sizeof cmd; + memset(rp->inquiry, 0, sizeof rp->inquiry); + rp->data.p = rp->inquiry; + rp->data.count = sizeof rp->inquiry; + rp->data.write = 0; + if(SRrequest(rp) >= 0){ + rp->flags |= Finqok; + return 0; + } + rp->flags &= ~Finqok; + return -1; +} + +long +SRmodeselect6(ScsiReq *rp, uchar *list, long nbytes) +{ + uchar cmd[6]; + + memset(cmd, 0, sizeof cmd); + cmd[0] = ScmdMselect6; + if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2) + cmd[1] = 0x10; + cmd[4] = nbytes; + rp->cmd.p = cmd; + rp->cmd.count = sizeof cmd; + rp->data.p = list; + rp->data.count = nbytes; + rp->data.write = 1; + return SRrequest(rp); +} + +long +SRmodeselect10(ScsiReq *rp, uchar *list, long nbytes) +{ + uchar cmd[10]; + + memset(cmd, 0, sizeof cmd); + if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2) + cmd[1] = 0x10; + cmd[0] = ScmdMselect10; + cmd[7] = nbytes>>8; + cmd[8] = nbytes; + rp->cmd.p = cmd; + rp->cmd.count = sizeof cmd; + rp->data.p = list; + rp->data.count = nbytes; + rp->data.write = 1; + return SRrequest(rp); +} + +long +SRmodesense6(ScsiReq *rp, uchar page, uchar *list, long nbytes) +{ + uchar cmd[6]; + + memset(cmd, 0, sizeof cmd); + cmd[0] = ScmdMsense6; + cmd[2] = page; + cmd[4] = nbytes; + rp->cmd.p = cmd; + rp->cmd.count = sizeof cmd; + rp->data.p = list; + rp->data.count = nbytes; + rp->data.write = 0; + return SRrequest(rp); +} + +long +SRmodesense10(ScsiReq *rp, uchar page, uchar *list, long nbytes) +{ + uchar cmd[10]; + + memset(cmd, 0, sizeof cmd); + cmd[0] = ScmdMsense10; + cmd[2] = page; + cmd[7] = nbytes>>8; + cmd[8] = nbytes; + rp->cmd.p = cmd; + rp->cmd.count = sizeof cmd; + rp->data.p = list; + rp->data.count = nbytes; + rp->data.write = 0; + return SRrequest(rp); +} + +long +SRstart(ScsiReq *rp, uchar code) +{ + uchar cmd[6]; + + memset(cmd, 0, sizeof cmd); + cmd[0] = ScmdStart; + cmd[4] = code; + rp->cmd.p = cmd; + rp->cmd.count = sizeof cmd; + rp->data.p = cmd; + rp->data.count = 0; + rp->data.write = 1; + return SRrequest(rp); +} + +long +SRrcapacity(ScsiReq *rp, uchar *data) +{ + uchar cmd[10]; + + memset(cmd, 0, sizeof cmd); + cmd[0] = ScmdRcapacity; + rp->cmd.p = cmd; + rp->cmd.count = sizeof cmd; + rp->data.p = data; + rp->data.count = 8; + rp->data.write = 0; + return SRrequest(rp); +} + +static long +request(int fd, ScsiPtr *cmd, ScsiPtr *data, int *status) +{ + long n, r; + char buf[16]; + + /* this was an experiment but it seems to be a good idea */ + *status = STok; + + /* send SCSI command */ + if(write(fd, cmd->p, cmd->count) != cmd->count){ + fprint(2, "scsireq: write cmd: %r\n"); + *status = Status_SW; + return -1; + } + + /* read or write actual data */ + werrstr(""); + if(data->write) + n = write(fd, data->p, data->count); + else { + n = read(fd, data->p, data->count); + if (n < 0) + memset(data->p, 0, data->count); + else if (n < data->count) + memset(data->p + n, 0, data->count - n); + } + if (n != data->count && n <= 0) { + if (debug) + fprint(2, + "request: tried to %s %ld bytes of data for cmd 0x%x but got %r\n", + (data->write? "write": "read"), + data->count, cmd->p[0]); + } else if (n != data->count && (data->write || debug)) + fprint(2, "request: %s %ld of %ld bytes of actual data\n", + (data->write? "wrote": "read"), n, data->count); + + /* read status */ + buf[0] = '\0'; + r = read(fd, buf, sizeof buf-1); + if(exabyte && r <= 0 || !exabyte && r < 0){ + fprint(2, "scsireq: read status: %r\n"); + *status = Status_SW; + return -1; + } + if (r >= 0) + buf[r] = '\0'; + *status = atoi(buf); + if(n < 0 && (exabyte || *status != STcheck)) + fprint(2, "scsireq: status 0x%2.2uX: data transfer: %r\n", + *status); + return n; +} + +long +SRrequest(ScsiReq *rp) +{ + long n; + int status; + +retry: + if(rp->flags&Fusb) + n = umsrequest(rp->umsc, &rp->cmd, &rp->data, &status); + else + n = request(rp->fd, &rp->cmd, &rp->data, &status); + switch(rp->status = status){ + + case STok: + rp->data.count = n; + break; + + case STcheck: + if(rp->cmd.p[0] != ScmdRsense && SRreqsense(rp) != -1) + rp->status = Status_SD; + if (exabyte) + fprint(2, "SRrequest: STcheck, returning -1\n"); + return -1; + + case STbusy: + sleep(1000); + goto retry; + + default: + fprint(2, "status 0x%2.2uX\n", status); + return -1; + } + return n; +} + +int +SRclose(ScsiReq *rp) +{ + if((rp->flags & Fopen) == 0){ + if(debug) + fprint(2, "scuzz: closing closed file\n"); + rp->status = Status_BADARG; + return -1; + } + close(rp->fd); + rp->flags = 0; + return 0; +} + +uint +mkascq(ScsiReq *r) +{ + uchar *u; + + u = r->sense; + return u[2]<<16 | u[12]<<8 | u[13]; +} + +static int +dirdevopen(ScsiReq *rp) +{ + ulong blocks; + uchar data[8]; + + if(SRstart(rp, 1) == -1) + /* + * it's okay for removable media to say + * "check condition: medium not present". + * 3a is "medium not present". + */ + return rp->inquiry[1] & 0x80 && (mkascq(rp) >> 8) == 0x023a? + 0: -1; + memset(data, 0, sizeof data); + if(SRrcapacity(rp, data) == -1) + return -1; + rp->lbsize = GETBELONG(data+4); + blocks = GETBELONG(data); + if(debug) + fprint(2, "scuzz: dirdevopen: logical block size %lud, " + "# blocks %lud\n", rp->lbsize, blocks); + /* some newer dev's don't support 6-byte commands */ + if(blocks > Max24off && !force6bytecmds) + rp->flags |= Frw10; + return 0; +} + +static int +seqdevopen(ScsiReq *rp) +{ + uchar mode[16], limits[6]; + + if(SRrblimits(rp, limits) == -1) + return -1; + if(limits[1] == 0 && limits[2] == limits[4] && limits[3] == limits[5]){ + rp->flags |= Fbfixed; + rp->lbsize = limits[4]<<8 | limits[5]; + if(debug) + fprint(2, "scuzz: seqdevopen: logical block size %lud\n", + rp->lbsize); + return 0; + } + /* + * On some older hardware the optional 10-byte + * modeselect command isn't implemented. + */ + if (force6bytecmds) + rp->flags |= Fmode6; + if(!(rp->flags & Fmode6)){ + /* try 10-byte command first */ + memset(mode, 0, sizeof mode); + mode[3] = 0x10; /* device-specific param. */ + mode[7] = 8; /* block descriptor length */ + /* + * exabytes can't handle this, and + * modeselect(10) is optional. + */ + if(SRmodeselect10(rp, mode, sizeof mode) != -1){ + rp->lbsize = 1; + return 0; /* success */ + } + /* can't do 10-byte commands, back off to 6-byte ones */ + rp->flags |= Fmode6; + } + + /* 6-byte command */ + memset(mode, 0, sizeof mode); + mode[2] = 0x10; /* device-specific param. */ + mode[3] = 8; /* block descriptor length */ + /* + * bsd sez exabytes need this bit (NBE: no busy enable) in + * vendor-specific page (0), but so far we haven't needed it. + mode[12] |= 8; + */ + if(SRmodeselect6(rp, mode, 4+8) == -1) + return -1; + rp->lbsize = 1; + return 0; +} + +static int +wormdevopen(ScsiReq *rp) +{ + long status; + uchar list[MaxDirData]; + + if (SRstart(rp, 1) == -1 || + (status = SRmodesense10(rp, Allmodepages, list, sizeof list)) == -1) + return -1; + /* nbytes = list[0]<<8 | list[1]; */ + + /* # of bytes of block descriptors of 8 bytes each; not even 1? */ + if((list[6]<<8 | list[7]) < 8) + rp->lbsize = 2048; + else + /* last 3 bytes of block 0 descriptor */ + rp->lbsize = GETBE24(list+13); + if(debug) + fprint(2, "scuzz: wormdevopen: logical block size %lud\n", + rp->lbsize); + return status; +} + +int +SRopenraw(ScsiReq *rp, char *unit) +{ + char name[128]; + + if(rp->flags & Fopen){ + if(debug) + fprint(2, "scuzz: opening open file\n"); + rp->status = Status_BADARG; + return -1; + } + memset(rp, 0, sizeof *rp); + rp->unit = unit; + + sprint(name, "%s/raw", unit); + + if((rp->fd = open(name, ORDWR)) == -1){ + rp->status = STtimeout; + return -1; + } + rp->flags = Fopen; + return 0; +} + +int +SRopen(ScsiReq *rp, char *unit) +{ + if(SRopenraw(rp, unit) == -1) + return -1; + SRready(rp); + if(SRinquiry(rp) >= 0){ + switch(rp->inquiry[0]){ + + default: + fprint(2, "unknown device type 0x%.2x\n", rp->inquiry[0]); + rp->status = Status_SW; + break; + + case 0x00: /* Direct access (disk) */ + case 0x05: /* CD-ROM */ + case 0x07: /* rewriteable MO */ + if(dirdevopen(rp) == -1) + break; + return 0; + + case 0x01: /* Sequential eg: tape */ + rp->flags |= Fseqdev; + if(seqdevopen(rp) == -1) + break; + return 0; + + case 0x02: /* Printer */ + rp->flags |= Fprintdev; + return 0; + + case 0x04: /* Worm */ + rp->flags |= Fwormdev; + if(wormdevopen(rp) == -1) + break; + return 0; + + case 0x08: /* medium-changer */ + rp->flags |= Fchanger; + return 0; + } + } + SRclose(rp); + return -1; +} diff --git a/sys/src/cmd/scuzz/scsireq.h b/sys/src/cmd/scuzz/scsireq.h new file mode 100755 index 000000000..f08e33f23 --- /dev/null +++ b/sys/src/cmd/scuzz/scsireq.h @@ -0,0 +1,212 @@ +/* this file is also included by usb/disk and cdfs */ +typedef struct Umsc Umsc; +#pragma incomplete Umsc + +enum { /* fundamental constants/defaults */ + NTargetID = 8, /* number of target IDs */ + CtlrID = 7, /* default controller target ID */ + MaxDirData = 255, /* max. direct data returned */ + LBsize = 512, /* default logical-block size */ +}; + +typedef struct { + uchar *p; + long count; + uchar write; +} ScsiPtr; + +typedef struct { + int flags; + char *unit; /* unit directory */ + int lun; + ulong lbsize; + ulong offset; /* in blocks of lbsize bytes */ + int fd; + Umsc *umsc; /* lun */ + ScsiPtr cmd; + ScsiPtr data; + int status; /* returned status */ + uchar sense[MaxDirData]; /* returned sense data */ + uchar inquiry[MaxDirData]; /* returned inquiry data */ + int readblock; /* flag: read a block since open */ +} ScsiReq; + +enum { /* software flags */ + Fopen = 0x0001, /* open */ + Fseqdev = 0x0002, /* sequential-access device */ + Fwritten = 0x0004, /* device written */ + Fronly = 0x0008, /* device is read-only */ + Fwormdev = 0x0010, /* write-once read-multiple device */ + Fprintdev = 0x0020, /* printer */ + Fbfixed = 0x0040, /* fixed block size */ + Fchanger = 0x0080, /* medium-changer device */ + Finqok = 0x0100, /* inquiry data is OK */ + Fmode6 = 0x0200, /* use 6-byte modeselect */ + Frw10 = 0x0400, /* use 10-byte read/write */ + Fusb = 0x0800, /* USB transparent scsi */ +}; + +enum { + STnomem =-4, /* buffer allocation failed */ + STharderr =-3, /* controller error of some kind */ + STtimeout =-2, /* bus timeout */ + STok = 0, /* good */ + STcheck = 0x02, /* check condition */ + STcondmet = 0x04, /* condition met/good */ + STbusy = 0x08, /* busy */ + STintok = 0x10, /* intermediate/good */ + STintcondmet = 0x14, /* intermediate/condition met/good */ + STresconf = 0x18, /* reservation conflict */ + STterminated = 0x22, /* command terminated */ + STqfull = 0x28, /* queue full */ +}; + +enum { /* status */ + Status_SD = 0x80, /* sense-data available */ + Status_SW = 0x83, /* internal software error */ + Status_BADARG = 0x84, /* bad argument to request */ + Status_RO = 0x85, /* device is read-only */ +}; + +enum { /* SCSI command codes */ + ScmdTur = 0x00, /* test unit ready */ + ScmdRewind = 0x01, /* rezero/rewind */ + ScmdRsense = 0x03, /* request sense */ + ScmdFormat = 0x04, /* format unit */ + ScmdRblimits = 0x05, /* read block limits */ + ScmdRead = 0x08, /* read */ + ScmdWrite = 0x0A, /* write */ + ScmdSeek = 0x0B, /* seek */ + ScmdFmark = 0x10, /* write filemarks */ + ScmdSpace = 0x11, /* space forward/backward */ + ScmdInq = 0x12, /* inquiry */ + ScmdMselect6 = 0x15, /* mode select */ + ScmdMselect10 = 0x55, /* mode select */ + ScmdMsense6 = 0x1A, /* mode sense */ + ScmdMsense10 = 0x5A, /* mode sense */ + ScmdStart = 0x1B, /* start/stop unit */ + ScmdRcapacity = 0x25, /* read capacity */ + ScmdExtread = 0x28, /* extended read */ + ScmdExtwrite = 0x2A, /* extended write */ + ScmdExtseek = 0x2B, /* extended seek */ + + ScmdSynccache = 0x35, /* flush cache */ + ScmdRTOC = 0x43, /* read TOC data */ + ScmdRdiscinfo = 0x51, /* read disc information */ + ScmdRtrackinfo = 0x52, /* read track information */ + ScmdReserve = 0x53, /* reserve track */ + ScmdBlank = 0xA1, /* blank *-RW media */ + + ScmdCDpause = 0x4B, /* pause/resume */ + ScmdCDstop = 0x4E, /* stop play/scan */ + ScmdCDplay = 0xA5, /* play audio */ + ScmdCDload = 0xA6, /* load/unload */ + ScmdCDscan = 0xBA, /* fast forward/reverse */ + ScmdCDstatus = 0xBD, /* mechanism status */ + Scmdgetconf = 0x46, /* get configuration */ + + ScmdEInitialise = 0x07, /* initialise element status */ + ScmdMMove = 0xA5, /* move medium */ + ScmdEStatus = 0xB8, /* read element status */ + ScmdMExchange = 0xA6, /* exchange medium */ + ScmdEposition = 0x2B, /* position to element */ + + ScmdReadDVD = 0xAD, /* read dvd structure */ + ScmdReportKey = 0xA4, /* read dvd key */ + ScmdSendKey = 0xA3, /* write dvd key */ + + ScmdClosetracksess= 0x5B, + ScmdRead12 = 0xA8, + ScmdSetcdspeed = 0xBB, + ScmdReadcd = 0xBE, + + /* vendor-specific */ + ScmdFwaddr = 0xE2, /* first writeable address */ + ScmdTreserve = 0xE4, /* reserve track */ + ScmdTinfo = 0xE5, /* read track info */ + ScmdTwrite = 0xE6, /* write track */ + ScmdMload = 0xE7, /* medium load/unload */ + ScmdFixation = 0xE9, /* fixation */ +}; + +enum { + /* sense data byte 0 */ + Sd0valid = 0x80, /* valid sense data present */ + + /* sense data byte 2 */ + /* incorrect-length indicator, difference in bytes 3—6 */ + Sd2ili = 0x20, + Sd2eom = 0x40, /* end of medium (tape) */ + Sd2filemark = 0x80, /* at a filemark (tape) */ + + /* command byte 1 */ + Cmd1fixed = 1, /* use fixed-length blocks */ + Cmd1sili = 2, /* don't set Sd2ili */ + + /* limit of block #s in 24-bit ccbs */ + Max24off = (1<<21) - 1, /* 2ⁱ - 1 */ + + /* mode pages */ + Allmodepages = 0x3F, +}; + +/* p arguments should be of type uchar* */ +#define GETBELONG(p) ((ulong)(p)[0]<<24 | (ulong)(p)[1]<<16 | (p)[2]<<8 | (p)[3]) +#define PUTBELONG(p, ul) ((p)[0] = (ul)>>24, (p)[1] = (ul)>>16, \ + (p)[2] = (ul)>>8, (p)[3] = (ul)) +#define GETBE24(p) ((ulong)(p)[0]<<16 | (p)[1]<<8 | (p)[2]) +#define PUTBE24(p, ul) ((p)[0] = (ul)>>16, (p)[1] = (ul)>>8, (p)[2] = (ul)) + +extern long maxiosize; + +long SRready(ScsiReq*); +long SRrewind(ScsiReq*); +long SRreqsense(ScsiReq*); +long SRformat(ScsiReq*); +long SRrblimits(ScsiReq*, uchar*); +long SRread(ScsiReq*, void*, long); +long SRwrite(ScsiReq*, void*, long); +long SRseek(ScsiReq*, long, int); +long SRfilemark(ScsiReq*, ulong); +long SRspace(ScsiReq*, uchar, long); +long SRinquiry(ScsiReq*); +long SRmodeselect6(ScsiReq*, uchar*, long); +long SRmodeselect10(ScsiReq*, uchar*, long); +long SRmodesense6(ScsiReq*, uchar, uchar*, long); +long SRmodesense10(ScsiReq*, uchar, uchar*, long); +long SRstart(ScsiReq*, uchar); +long SRrcapacity(ScsiReq*, uchar*); + +long SRblank(ScsiReq*, uchar, uchar); /* MMC CD-R/CD-RW commands */ +long SRsynccache(ScsiReq*); +long SRTOC(ScsiReq*, void*, int, uchar, uchar); +long SRrdiscinfo(ScsiReq*, void*, int); +long SRrtrackinfo(ScsiReq*, void*, int, int); + +long SRcdpause(ScsiReq*, int); /* MMC CD audio commands */ +long SRcdstop(ScsiReq*); +long SRcdload(ScsiReq*, int, int); +long SRcdplay(ScsiReq*, int, long, long); +long SRcdstatus(ScsiReq*, uchar*, int); +long SRgetconf(ScsiReq*, uchar*, int); + +/* old CD-R/CD-RW commands */ +long SRfwaddr(ScsiReq*, uchar, uchar, uchar, uchar*); +long SRtreserve(ScsiReq*, long); +long SRtinfo(ScsiReq*, uchar, uchar*); +long SRwtrack(ScsiReq*, void*, long, uchar, uchar); +long SRmload(ScsiReq*, uchar); +long SRfixation(ScsiReq*, uchar); + +long SReinitialise(ScsiReq*); /* CHANGER commands */ +long SRestatus(ScsiReq*, uchar, uchar*, int); +long SRmmove(ScsiReq*, int, int, int, int); + +long SRrequest(ScsiReq*); +int SRclose(ScsiReq*); +int SRopenraw(ScsiReq*, char*); +int SRopen(ScsiReq*, char*); + +void makesense(ScsiReq*); + +long umsrequest(struct Umsc*, ScsiPtr*, ScsiPtr*, int*); diff --git a/sys/src/cmd/scuzz/scuzz.c b/sys/src/cmd/scuzz/scuzz.c new file mode 100755 index 000000000..02e3d8e9b --- /dev/null +++ b/sys/src/cmd/scuzz/scuzz.c @@ -0,0 +1,1958 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> + +#include "scsireq.h" + +enum { /* fundamental constants/defaults */ + /* + * default & maximum `maximum i/o size'; overridden by -m. + * limits kernel memory consumption. + * 240K is exabyte maximum block size. + */ + MaxIOsize = 240*1024, +}; + +#define MIN(a, b) ((a) < (b) ? (a): (b)) + +static char rwbuf[MaxIOsize]; +static int verbose = 1; + +Biobuf bin, bout; +long maxiosize = MaxIOsize; +int exabyte = 0; +int force6bytecmds = 0; + +typedef struct { + char *name; + long (*f)(ScsiReq *, int, char *[]); + int open; + char *help; +} ScsiCmd; + +static ScsiCmd scsicmd[]; + +static vlong +vlmin(vlong a, vlong b) +{ + if (a < b) + return a; + else + return b; +} + +static long +cmdready(ScsiReq *rp, int argc, char *argv[]) +{ + USED(argc, argv); + return SRready(rp); +} + +static long +cmdrewind(ScsiReq *rp, int argc, char *argv[]) +{ + USED(argc, argv); + return SRrewind(rp); +} + +static long +cmdreqsense(ScsiReq *rp, int argc, char *argv[]) +{ + long nbytes; + + USED(argc, argv); + if((nbytes = SRreqsense(rp)) != -1) + makesense(rp); + return nbytes; +} + +static long +cmdformat(ScsiReq *rp, int argc, char *argv[]) +{ + USED(argc, argv); + return SRformat(rp); +} + +static long +cmdrblimits(ScsiReq *rp, int argc, char *argv[]) +{ + uchar l[6]; + long n; + + USED(argc, argv); + if((n = SRrblimits(rp, l)) == -1) + return -1; + Bprint(&bout, " %2.2uX %2.2uX %2.2uX %2.2uX %2.2uX %2.2uX\n", + l[0], l[1], l[2], l[3], l[4], l[5]); + return n; +} + +static int +mkfile(char *file, int omode, int *pid) +{ + int fd[2]; + + if(*file != '|'){ + *pid = -1; + if(omode == OWRITE) + return create(file, OWRITE, 0666); + else if(omode == OREAD) + return open(file, OREAD); + return -1; + } + + file++; + if(*file == 0 || pipe(fd) == -1) + return -1; + if((*pid = fork()) == -1){ + close(fd[0]); + close(fd[1]); + return -1; + } + if(*pid == 0){ + switch(omode){ + + case OREAD: + dup(fd[0], 1); + break; + + case OWRITE: + dup(fd[0], 0); + break; + } + close(fd[0]); + close(fd[1]); + execl("/bin/rc", "rc", "-c", file, nil); + exits("exec"); + } + close(fd[0]); + return fd[1]; +} + +int +waitfor(int pid) +{ + int msg; + Waitmsg *w; + + while((w = wait()) != nil){ + if(w->pid != pid){ + free(w); + continue; + } + msg = (w->msg[0] != '\0'); + free(w); + return msg; + } + return -1; +} + +static long +cmdread(ScsiReq *rp, int argc, char *argv[]) +{ + long n, iosize, prevsize = 0; + vlong nbytes, total; + int fd, pid; + char *p; + + iosize = maxiosize; + nbytes = ~0ULL >> 1; + switch(argc){ + + default: + rp->status = Status_BADARG; + return -1; + + case 2: + nbytes = strtoll(argv[1], &p, 0); + if(nbytes == 0 && p == argv[1]){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 1: + if((fd = mkfile(argv[0], OWRITE, &pid)) == -1){ + rp->status = Status_BADARG; + return -1; + } + break; + } + print("device native block size=%lud\n", rp->lbsize); + total = 0; + while(nbytes){ + n = vlmin(nbytes, iosize); + if((n = SRread(rp, rwbuf, n)) == -1){ + if(total == 0) + total = -1; + break; + } + if (n == 0) + break; + if (prevsize != n) { + print("tape block size=%ld\n", n); + prevsize = n; + } + if(write(fd, rwbuf, n) != n){ + if(total == 0) + total = -1; + if(rp->status == STok) + rp->status = Status_SW; + break; + } + nbytes -= n; + total += n; + } + close(fd); + if(pid >= 0 && waitfor(pid)){ + rp->status = Status_SW; + return -1; + } + return total; +} + +static long +cmdwrite(ScsiReq *rp, int argc, char *argv[]) +{ + long n, prevsize = 0; + vlong nbytes, total; + int fd, pid; + char *p; + + nbytes = ~0ULL >> 1; + switch(argc){ + + default: + rp->status = Status_BADARG; + return -1; + + case 2: + nbytes = strtoll(argv[1], &p, 0); + if(nbytes == 0 && p == argv[1]){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 1: + if((fd = mkfile(argv[0], OREAD, &pid)) == -1){ + rp->status = Status_BADARG; + return -1; + } + break; + } + total = 0; + while(nbytes){ + n = vlmin(nbytes, maxiosize); + if((n = read(fd, rwbuf, n)) == -1){ + if(total == 0) + total = -1; + break; + } + if (n == 0) + break; + if (prevsize != n) { + print("tape block size=%ld\n", n); + prevsize = n; + } + if(SRwrite(rp, rwbuf, n) != n){ + if(total == 0) + total = -1; + if(rp->status == STok) + rp->status = Status_SW; + break; + } + nbytes -= n; + total += n; + } + close(fd); + if(pid >= 0 && waitfor(pid)){ + rp->status = Status_SW; + return -1; + } + return total; +} + +static long +cmdseek(ScsiReq *rp, int argc, char *argv[]) +{ + char *p; + long offset; + int type; + + type = 0; + switch(argc){ + + default: + rp->status = Status_BADARG; + return -1; + + case 2: + if((type = strtol(argv[1], &p, 0)) == 0 && p == argv[1]){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 1: + if((offset = strtol(argv[0], &p, 0)) == 0 && p == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + break; + } + return SRseek(rp, offset, type); +} + +static long +cmdfilemark(ScsiReq *rp, int argc, char *argv[]) +{ + char *p; + ulong howmany; + + howmany = 1; + if(argc && (howmany = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + return SRfilemark(rp, howmany); +} + +static long +cmdspace(ScsiReq *rp, int argc, char *argv[]) +{ + uchar code; + long howmany; + char option, *p; + + code = 0x00; + howmany = 1; + while(argc && (*argv)[0] == '-'){ + while(option = *++argv[0]){ + switch(option){ + + case '-': + break; + + case 'b': + code = 0x00; + break; + + case 'f': + code = 0x01; + break; + + default: + rp->status = Status_BADARG; + return -1; + } + break; + } + argc--; argv++; + if(option == '-') + break; + } + if(argc && ((howmany = strtol(argv[0], &p, 0)) == 0 && p == argv[0])){ + rp->status = Status_BADARG; + return -1; + } + return SRspace(rp, code, howmany); +} + +static long +cmdinquiry(ScsiReq *rp, int argc, char *argv[]) +{ + long status; + int i, n; + uchar *p; + + USED(argc, argv); + if((status = SRinquiry(rp)) != -1){ + n = rp->inquiry[4]+4; + for(i = 0; i < MIN(8, n); i++) + Bprint(&bout, " %2.2uX", rp->inquiry[i]); + p = &rp->inquiry[8]; + n = MIN(n, sizeof(rp->inquiry)-8); + while(n && (*p == ' ' || *p == '\t' || *p == '\n')){ + n--; + p++; + } + Bprint(&bout, "\t%.*s\n", n, (char*)p); + } + return status; +} + +static long +cmdmodeselect6(ScsiReq *rp, int argc, char *argv[]) +{ + uchar list[MaxDirData]; + long nbytes, ul; + char *p; + + memset(list, 0, sizeof list); + for(nbytes = 0; argc; argc--, argv++, nbytes++){ + if((ul = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + list[nbytes] = ul; + + } + if(!(rp->flags & Finqok) && SRinquiry(rp) == -1) + Bprint(&bout, "warning: couldn't determine whether SCSI-1/SCSI-2 mode"); + return SRmodeselect6(rp, list, nbytes); +} + +static long +cmdmodeselect10(ScsiReq *rp, int argc, char *argv[]) +{ + uchar list[MaxDirData]; + long nbytes, ul; + char *p; + + memset(list, 0, sizeof list); + for(nbytes = 0; argc; argc--, argv++, nbytes++){ + if((ul = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + list[nbytes] = ul; + + } + if(!(rp->flags & Finqok) && SRinquiry(rp) == -1) + Bprint(&bout, "warning: couldn't determine whether SCSI-1/SCSI-2 mode"); + return SRmodeselect10(rp, list, nbytes); +} + +static long +cmdmodesense6(ScsiReq *rp, int argc, char *argv[]) +{ + uchar list[MaxDirData], *lp, page; + long i, n, nbytes, status; + char *p; + + nbytes = sizeof list; + switch(argc){ + + default: + rp->status = Status_BADARG; + return -1; + + case 2: + if((nbytes = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 1: + if((page = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + break; + + case 0: + page = Allmodepages; + break; + } + if((status = SRmodesense6(rp, page, list, nbytes)) == -1) + return -1; + lp = list; + nbytes = list[0]; + Bprint(&bout, " Header\n "); + for(i = 0; i < 4; i++){ /* header */ + Bprint(&bout, " %2.2uX", *lp); + lp++; + } + Bputc(&bout, '\n'); + + if(list[3]){ /* block descriptors */ + for(n = 0; n < list[3]/8; n++){ + Bprint(&bout, " Block %ld\n ", n); + for(i = 0; i < 8; i++) + Bprint(&bout, " %2.2uX", lp[i]); + Bprint(&bout, " (density %2.2uX", lp[0]); + Bprint(&bout, " blocks %d", (lp[1]<<16)|(lp[2]<<8)|lp[3]); + Bprint(&bout, " length %d)", (lp[5]<<16)|(lp[6]<<8)|lp[7]); + lp += 8; + nbytes -= 8; + Bputc(&bout, '\n'); + } + } + + while(nbytes > 0){ /* pages */ + i = *(lp+1); + nbytes -= i+2; + Bprint(&bout, " Page %2.2uX %d\n ", *lp & 0x3F, *(lp+1)); + lp += 2; + for(n = 0; n < i; n++){ + if(n && ((n & 0x0F) == 0)) + Bprint(&bout, "\n "); + Bprint(&bout, " %2.2uX", *lp); + lp++; + } + if(n && (n & 0x0F)) + Bputc(&bout, '\n'); + } + return status; +} + +static long +cmdmodesense10(ScsiReq *rp, int argc, char *argv[]) +{ + uchar *list, *lp, page; + long blen, i, n, nbytes, status; + char *p; + + nbytes = MaxDirData; + switch(argc){ + default: + rp->status = Status_BADARG; + return -1; + + case 2: + if((nbytes = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + case 1: + if((page = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + break; + + case 0: + page = Allmodepages; + break; + } + list = malloc(nbytes); + if(list == 0){ + rp->status = STnomem; + return -1; + } + if((status = SRmodesense10(rp, page, list, nbytes)) == -1) + return -1; + lp = list; + nbytes = ((list[0]<<8)|list[1]); + Bprint(&bout, " Header\n "); + for(i = 0; i < 8; i++){ /* header */ + Bprint(&bout, " %2.2uX", *lp); + lp++; + } + Bputc(&bout, '\n'); + + blen = (list[6]<<8)|list[7]; + if(blen){ /* block descriptors */ + for(n = 0; n < blen/8; n++){ + Bprint(&bout, " Block %ld\n ", n); + for(i = 0; i < 8; i++) + Bprint(&bout, " %2.2uX", lp[i]); + Bprint(&bout, " (density %2.2uX", lp[0]); + Bprint(&bout, " blocks %d", (lp[1]<<16)|(lp[2]<<8)|lp[3]); + Bprint(&bout, " length %d)", (lp[5]<<16)|(lp[6]<<8)|lp[7]); + lp += 8; + nbytes -= 8; + Bputc(&bout, '\n'); + } + } + + /* + * Special for ATA drives, page 0 is the drive info in 16-bit + * chunks, little-endian, 256 in total. No decoding for now. + */ + if(page == 0){ + for(n = 0; n < nbytes; n += 2){ + if(n && ((n & 0x1F) == 0)) + Bprint(&bout, "\n"); + Bprint(&bout, " %4.4uX", (*(lp+1)<<8)|*lp); + lp += 2; + } + Bputc(&bout, '\n'); + } + else + while(nbytes > 0){ /* pages */ + i = *(lp+1); + nbytes -= i+2; + Bprint(&bout, " Page %2.2uX %d\n ", *lp & 0x3F, lp[1]); + lp += 2; + for(n = 0; n < i; n++){ + if(n && ((n & 0x0F) == 0)) + Bprint(&bout, "\n "); + Bprint(&bout, " %2.2uX", *lp); + lp++; + } + if(n && (n & 0x0F)) + Bputc(&bout, '\n'); + } + free(list); + return status; +} + +static long +start(ScsiReq *rp, int argc, char *argv[], uchar code) +{ + char *p; + + if(argc && (code = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + return SRstart(rp, code); +} + +static long +cmdstart(ScsiReq *rp, int argc, char *argv[]) +{ + return start(rp, argc, argv, 1); +} + +static long +cmdstop(ScsiReq *rp, int argc, char *argv[]) +{ + return start(rp, argc, argv, 0); +} + +static long +cmdeject(ScsiReq *rp, int argc, char *argv[]) +{ + return start(rp, argc, argv, 2); +} + +static long +cmdingest(ScsiReq *rp, int argc, char *argv[]) +{ + return start(rp, argc, argv, 3); +} + +static long +cmdcapacity(ScsiReq *rp, int argc, char *argv[]) +{ + uchar d[8]; + long n; + + USED(argc, argv); + if((n = SRrcapacity(rp, d)) == -1) + return -1; + Bprint(&bout, " %ud %ud\n", + d[0]<<24|d[1]<<16|d[2]<<8|d[3], + d[4]<<24|d[5]<<16|d[6]<<8|d[7]); + return n; +} + +static long +cmdblank(ScsiReq *rp, int argc, char *argv[]) +{ + uchar type, track; + char *sp; + + type = track = 0; + switch(argc){ + + default: + rp->status = Status_BADARG; + return -1; + + case 2: + if((type = strtoul(argv[1], &sp, 0)) == 0 && sp == argv[1]){ + rp->status = Status_BADARG; + return -1; + } + if(type > 6){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 1: + if((track = strtoul(argv[0], &sp, 0)) == 0 && sp == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 0: + break; + } + return SRblank(rp, type, track); +} + +static long +cmdsynccache(ScsiReq *rp, int argc, char *argv[]) +{ + USED(argc, argv); + return SRsynccache(rp); +} + +static long +cmdrtoc(ScsiReq *rp, int argc, char *argv[]) +{ + uchar d[100*8+4], format, track, *p; + char *sp; + long n, nbytes; + int tdl; + + format = track = 0; + switch(argc){ + + default: + rp->status = Status_BADARG; + return -1; + + case 2: + if((format = strtoul(argv[1], &sp, 0)) == 0 && sp == argv[1]){ + rp->status = Status_BADARG; + return -1; + } + if(format > 4){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 1: + if((track = strtoul(argv[0], &sp, 0)) == 0 && sp == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 0: + break; + } + if((nbytes = SRTOC(rp, d, sizeof d, format, track)) == -1){ + if(rp->status == STok) + Bprint(&bout, "\t(probably empty)\n"); + return -1; + } + tdl = (d[0]<<8)|d[1]; + switch(format){ + + case 0: + Bprint(&bout, "\ttoc/pma data length: 0x%uX\n", tdl); + Bprint(&bout, "\tfirst track number: %d\n", d[2]); + Bprint(&bout, "\tlast track number: %d\n", d[3]); + for(p = &d[4], n = tdl-2; n; n -= 8, p += 8){ + Bprint(&bout, "\ttrack number: 0x%2.2uX\n", p[2]); + Bprint(&bout, "\t\tcontrol: 0x%2.2uX\n", p[1] & 0x0F); + Bprint(&bout, "\t\tblock address: 0x%uX\n", + (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7]); + } + break; + + case 1: + Bprint(&bout, "\tsessions data length: 0x%uX\n", tdl); + Bprint(&bout, "\tnumber of finished sessions: %d\n", d[2]); + Bprint(&bout, "\tunfinished session number: %d\n", d[3]); + for(p = &d[4], n = tdl-2; n; n -= 8, p += 8){ + Bprint(&bout, "\tsession number: 0x%2.2uX\n", p[0]); + Bprint(&bout, "\t\tfirst track number in session: 0x%2.2uX\n", + p[2]); + Bprint(&bout, "\t\tlogical start address: 0x%uX\n", + (p[5]<<16)|(p[6]<<8)|p[7]); + } + break; + + case 2: + Bprint(&bout, "\tfull TOC data length: 0x%uX\n", tdl); + Bprint(&bout, "\tnumber of finished sessions: %d\n", d[2]); + Bprint(&bout, "\tunfinished session number: %d\n", d[3]); + for(p = &d[4], n = tdl-2; n > 0; n -= 11, p += 11){ + Bprint(&bout, "\tsession number: 0x%2.2uX\n", p[0]); + Bprint(&bout, "\t\tcontrol: 0x%2.2uX\n", p[1] & 0x0F); + Bprint(&bout, "\t\tADR: 0x%2.2uX\n", (p[1]>>4) & 0x0F); + Bprint(&bout, "\t\tTNO: 0x%2.2uX\n", p[2]); + Bprint(&bout, "\t\tPOINT: 0x%2.2uX\n", p[3]); + Bprint(&bout, "\t\tMin: 0x%2.2uX\n", p[4]); + Bprint(&bout, "\t\tSec: 0x%2.2uX\n", p[5]); + Bprint(&bout, "\t\tFrame: 0x%2.2uX\n", p[6]); + Bprint(&bout, "\t\tZero: 0x%2.2uX\n", p[7]); + Bprint(&bout, "\t\tPMIN: 0x%2.2uX\n", p[8]); + Bprint(&bout, "\t\tPSEC: 0x%2.2uX\n", p[9]); + Bprint(&bout, "\t\tPFRAME: 0x%2.2uX\n", p[10]); + } + break; + case 3: + Bprint(&bout, "\tPMA data length: 0x%uX\n", tdl); + for(p = &d[4], n = tdl-2; n > 0; n -= 11, p += 11){ + Bprint(&bout, "\t\tcontrol: 0x%2.2uX\n", p[1] & 0x0F); + Bprint(&bout, "\t\tADR: 0x%2.2uX\n", (p[1]>>4) & 0x0F); + Bprint(&bout, "\t\tTNO: 0x%2.2uX\n", p[2]); + Bprint(&bout, "\t\tPOINT: 0x%2.2uX\n", p[3]); + Bprint(&bout, "\t\tMin: 0x%2.2uX\n", p[4]); + Bprint(&bout, "\t\tSec: 0x%2.2uX\n", p[5]); + Bprint(&bout, "\t\tFrame: 0x%2.2uX\n", p[6]); + Bprint(&bout, "\t\tZero: 0x%2.2uX\n", p[7]); + Bprint(&bout, "\t\tPMIN: 0x%2.2uX\n", p[8]); + Bprint(&bout, "\t\tPSEC: 0x%2.2uX\n", p[9]); + Bprint(&bout, "\t\tPFRAME: 0x%2.2uX\n", p[10]); + } + break; + + case 4: + Bprint(&bout, "\tATIP data length: 0x%uX\n", tdl); + break; + + } + for(n = 0; n < nbytes; n++){ + if(n && ((n & 0x0F) == 0)) + Bprint(&bout, "\n"); + Bprint(&bout, " %2.2uX", d[n]); + } + if(n && (n & 0x0F)) + Bputc(&bout, '\n'); + return nbytes; +} + +static long +cmdrdiscinfo(ScsiReq *rp, int argc, char*[]) +{ + uchar d[MaxDirData]; + int dl; + long n, nbytes; + + switch(argc){ + + default: + rp->status = Status_BADARG; + return -1; + + case 0: + break; + } + if((nbytes = SRrdiscinfo(rp, d, sizeof d)) == -1) + return -1; + + dl = (d[0]<<8)|d[1]; + Bprint(&bout, "\tdata length: 0x%uX\n", dl); + Bprint(&bout, "\tinfo[2] 0x%2.2uX\n", d[2]); + switch(d[2] & 0x03){ + + case 0: + Bprint(&bout, "\t\tEmpty\n"); + break; + + case 1: + Bprint(&bout, "\t\tIncomplete disc (Appendable)\n"); + break; + + case 2: + Bprint(&bout, "\t\tComplete (CD-ROM or last session is closed and has no next session pointer)\n"); + break; + + case 3: + Bprint(&bout, "\t\tReserved\n"); + break; + } + switch((d[2]>>2) & 0x03){ + + case 0: + Bprint(&bout, "\t\tEmpty Session\n"); + break; + + case 1: + Bprint(&bout, "\t\tIncomplete Session\n"); + break; + + case 2: + Bprint(&bout, "\t\tReserved\n"); + break; + + case 3: + Bprint(&bout, "\t\tComplete Session (only possible when disc Status is Complete)\n"); + break; + } + if(d[2] & 0x10) + Bprint(&bout, "\t\tErasable\n"); + Bprint(&bout, "\tNumber of First Track on Disc %ud\n", d[3]); + Bprint(&bout, "\tNumber of Sessions %ud\n", d[4]); + Bprint(&bout, "\tFirst Track Number in Last Session %ud\n", d[5]); + Bprint(&bout, "\tLast Track Number in Last Session %ud\n", d[6]); + Bprint(&bout, "\tinfo[7] 0x%2.2uX\n", d[7]); + if(d[7] & 0x20) + Bprint(&bout, "\t\tUnrestricted Use Disc\n"); + if(d[7] & 0x40) + Bprint(&bout, "\t\tDisc Bar Code Valid\n"); + if(d[7] & 0x80) + Bprint(&bout, "\t\tDisc ID Valid\n"); + Bprint(&bout, "\tinfo[8] 0x%2.2uX\n", d[8]); + switch(d[8]){ + + case 0x00: + Bprint(&bout, "\t\tCD-DA or CD-ROM Disc\n"); + break; + + case 0x10: + Bprint(&bout, "\t\tCD-I Disc\n"); + break; + + case 0x20: + Bprint(&bout, "\t\tCD-ROM XA Disc\n"); + break; + + case 0xFF: + Bprint(&bout, "\t\tUndefined\n"); + break; + + default: + Bprint(&bout, "\t\tReserved\n"); + break; + } + Bprint(&bout, "\tLast Session lead-in Start Time M/S/F: 0x%2.2uX/0x%2.2uX/0x%2.2uX\n", + d[17], d[18], d[19]); + Bprint(&bout, "\tLast Possible Start Time for Start of lead-out M/S/F: 0x%2.2uX/0x%2.2uX/0x%2.2uX\n", + d[21], d[22], d[23]); + + for(n = 0; n < nbytes; n++){ + if(n && ((n & 0x0F) == 0)) + Bprint(&bout, "\n"); + Bprint(&bout, " %2.2uX", d[n]); + } + if(n && (n & 0x0F)) + Bputc(&bout, '\n'); + + return nbytes; +} + +static long +cmdrtrackinfo(ScsiReq *rp, int argc, char *argv[]) +{ + uchar d[MaxDirData], track; + char *sp; + long n, nbytes; + int dl; + + track = 0; + switch(argc){ + + default: + rp->status = Status_BADARG; + return -1; + + case 1: + if((track = strtoul(argv[0], &sp, 0)) == 0 && sp == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 0: + break; + } + if((nbytes = SRrtrackinfo(rp, d, sizeof d, track)) == -1) + return -1; + + dl = (d[0]<<8)|d[1]; + Bprint(&bout, "\tdata length: 0x%uX\n", dl); + Bprint(&bout, "\Track Number %d\n", d[2]); + Bprint(&bout, "\Session Number %d\n", d[3]); + Bprint(&bout, "\tinfo[4] 0x%2.2uX\n", d[5]); + Bprint(&bout, "\t\tTrack Mode 0x%2.2uX: ", d[5] & 0x0F); + switch(d[5] & 0x0F){ + case 0x00: + case 0x02: + Bprint(&bout, "2 audio channels without pre-emphasis\n"); + break; + case 0x01: + case 0x03: + Bprint(&bout, "2 audio channels with pre-emphasis of 50/15µs\n"); + break; + case 0x08: + case 0x0A: + Bprint(&bout, "audio channels without pre-emphasis (reserved in CD-R/RW)\n"); + break; + case 0x09: + case 0x0B: + Bprint(&bout, "audio channels with pre-emphasis of 50/15µs (reserved in CD-R/RW)\n"); + break; + case 0x04: + case 0x06: + Bprint(&bout, "Data track, recorded uninterrupted\n"); + break; + case 0x05: + case 0x07: + Bprint(&bout, "Data track, recorded incremental\n"); + break; + default: + Bprint(&bout, "(mode unknown)\n"); + break; + } + if(d[5] & 0x10) + Bprint(&bout, "\t\tCopy\n"); + if(d[5] & 0x20) + Bprint(&bout, "\t\tDamage\n"); + Bprint(&bout, "\tinfo[6] 0x%2.2uX\n", d[6]); + Bprint(&bout, "\t\tData Mode 0x%2.2uX: ", d[6] & 0x0F); + switch(d[6] & 0x0F){ + case 0x01: + Bprint(&bout, "Mode 1 (ISO/IEC 10149)\n"); + break; + case 0x02: + Bprint(&bout, "Mode 2 (ISO/IEC 10149 or CD-ROM XA)\n"); + break; + case 0x0F: + Bprint(&bout, "Data Block Type unknown (no track descriptor block)\n"); + break; + default: + Bprint(&bout, "(Reserved)\n"); + break; + } + if(d[6] & 0x10) + Bprint(&bout, "\t\tFP\n"); + if(d[6] & 0x20) + Bprint(&bout, "\t\tPacket\n"); + if(d[6] & 0x40) + Bprint(&bout, "\t\tBlank\n"); + if(d[6] & 0x80) + Bprint(&bout, "\t\tRT\n"); + Bprint(&bout, "\tTrack Start Address 0x%8.8uX\n", + (d[8]<<24)|(d[9]<<16)|(d[10]<<8)|d[11]); + if(d[7] & 0x01) + Bprint(&bout, "\tNext Writeable Address 0x%8.8uX\n", + (d[12]<<24)|(d[13]<<16)|(d[14]<<8)|d[15]); + Bprint(&bout, "\tFree Blocks 0x%8.8uX\n", + (d[16]<<24)|(d[17]<<16)|(d[18]<<8)|d[19]); + if((d[6] & 0x30) == 0x30) + Bprint(&bout, "\tFixed Packet Size 0x%8.8uX\n", + (d[20]<<24)|(d[21]<<16)|(d[22]<<8)|d[23]); + Bprint(&bout, "\tTrack Size 0x%8.8uX\n", + (d[24]<<24)|(d[25]<<16)|(d[26]<<8)|d[27]); + + for(n = 0; n < nbytes; n++){ + if(n && ((n & 0x0F) == 0)) + Bprint(&bout, "\n"); + Bprint(&bout, " %2.2uX", d[n]); + } + if(n && (n & 0x0F)) + Bputc(&bout, '\n'); + + return nbytes; +} + +static long +cmdcdpause(ScsiReq *rp, int argc, char *argv[]) +{ + USED(argc, argv); + return SRcdpause(rp, 0); +} + +static long +cmdcdresume(ScsiReq *rp, int argc, char *argv[]) +{ + USED(argc, argv); + return SRcdpause(rp, 1); +} + +static long +cmdcdstop(ScsiReq *rp, int argc, char *argv[]) +{ + USED(argc, argv); + return SRcdstop(rp); +} + +static long +cmdcdplay(ScsiReq *rp, int argc, char *argv[]) +{ + long length, start; + char *sp; + int raw; + + raw = 0; + start = 0; + if(argc && strcmp("-r", argv[0]) == 0){ + raw = 1; + argc--, argv++; + } + + length = 0xFFFFFFFF; + switch(argc){ + + default: + rp->status = Status_BADARG; + return -1; + + case 2: + if(!raw || ((length = strtol(argv[1], &sp, 0)) == 0 && sp == argv[1])){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 1: + if((start = strtol(argv[0], &sp, 0)) == 0 && sp == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 0: + break; + } + + return SRcdplay(rp, raw, start, length); +} + +static long +cmdcdload(ScsiReq *rp, int argc, char *argv[]) +{ + char *p; + ulong slot; + + slot = 0; + if(argc && (slot = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + return SRcdload(rp, 1, slot); +} + +static long +cmdcdunload(ScsiReq *rp, int argc, char *argv[]) +{ + char *p; + ulong slot; + + slot = 0; + if(argc && (slot = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + return SRcdload(rp, 0, slot); +} + +static long +cmdcdstatus(ScsiReq *rp, int argc, char *argv[]) +{ + uchar *list, *lp; + long nbytes, status; + int i, slots; + + USED(argc, argv); + + nbytes = 4096; + list = malloc(nbytes); + if(list == 0){ + rp->status = STnomem; + return -1; + } + status = SRcdstatus(rp, list, nbytes); + if(status == -1){ + free(list); + return -1; + } + + lp = list; + Bprint(&bout, " Header\n "); + for(i = 0; i < 8; i++){ /* header */ + Bprint(&bout, " %2.2uX", *lp); + lp++; + } + Bputc(&bout, '\n'); + + slots = ((list[6]<<8)|list[7])/4; + Bprint(&bout, " Slots\n "); + while(slots--){ + Bprint(&bout, " %2.2uX %2.2uX %2.2uX %2.2uX\n ", + *lp, *(lp+1), *(lp+2), *(lp+3)); + lp += 4; + } + + free(list); + return status; +} + +static long +cmdgetconf(ScsiReq *rp, int argc, char *argv[]) +{ + uchar *list; + long nbytes, status; + + USED(argc, argv); + + nbytes = 4096; + list = malloc(nbytes); + if(list == 0){ + rp->status = STnomem; + return -1; + } + status = SRgetconf(rp, list, nbytes); + if(status == -1){ + free(list); + return -1; + } + /* to be done... */ + free(list); + return status; +} + +static long +cmdfwaddr(ScsiReq *rp, int argc, char *argv[]) +{ + uchar d[MaxDirData], npa, track, mode; + long n; + char *p; + + npa = mode = track = 0; + switch(argc){ + + default: + rp->status = Status_BADARG; + return -1; + + case 3: + if((npa = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 2: + if((mode = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 1: + if((track = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + break; + + case 0: + break; + } + if((n = SRfwaddr(rp, track, mode, npa, d)) == -1) + return -1; + Bprint(&bout, "%ud %ud\n", d[0], (d[1]<<24)|(d[2]<<16)|(d[3]<<8)|d[4]); + return n; +} + +static long +cmdtreserve(ScsiReq *rp, int argc, char *argv[]) +{ + long nbytes; + char *p; + + if(argc != 1 || ((nbytes = strtoul(argv[0], &p, 0)) == 0 && p == argv[0])){ + rp->status = Status_BADARG; + return -1; + } + return SRtreserve(rp, nbytes); +} + +static long +cmdtrackinfo(ScsiReq *rp, int argc, char *argv[]) +{ + uchar d[MaxDirData], track; + long n; + ulong ul; + char *p; + + track = 0; + if(argc && (track = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + if((n = SRtinfo(rp, track, d)) == -1) + return -1; + Bprint(&bout, "buffer length: 0x%uX\n", d[0]); + Bprint(&bout, "number of tracks: 0x%uX\n", d[1]); + ul = (d[2]<<24)|(d[3]<<16)|(d[4]<<8)|d[5]; + Bprint(&bout, "start address: 0x%luX\n", ul); + ul = (d[6]<<24)|(d[7]<<16)|(d[8]<<8)|d[9]; + Bprint(&bout, "track length: 0x%luX\n", ul); + Bprint(&bout, "track mode: 0x%uX\n", d[0x0A] & 0x0F); + Bprint(&bout, "track status: 0x%uX\n", (d[0x0A]>>4) & 0x0F); + Bprint(&bout, "data mode: 0x%uX\n", d[0x0B] & 0x0F); + ul = (d[0x0C]<<24)|(d[0x0D]<<16)|(d[0x0E]<<8)|d[0x0F]; + Bprint(&bout, "free blocks: 0x%luX\n", ul); + return n; +} + +static long +cmdwtrack(ScsiReq *rp, int argc, char *argv[]) +{ + uchar mode, track; + long n, nbytes, total, x; + int fd, pid; + char *p; + + mode = track = 0; + nbytes = 0; + switch(argc){ + + default: + rp->status = Status_BADARG; + return -1; + + case 4: + if((mode = strtoul(argv[3], &p, 0)) == 0 && p == argv[3]){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 3: + if((track = strtoul(argv[2], &p, 0)) == 0 && p == argv[2]){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 2: + if((nbytes = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 1: + if((fd = mkfile(argv[0], OREAD, &pid)) == -1){ + rp->status = Status_BADARG; + return -1; + } + break; + } + total = 0; + n = MIN(nbytes, maxiosize); + if((n = readn(fd, rwbuf, n)) == -1){ + fprint(2, "file read failed %r\n"); + close(fd); + return -1; + } + if((x = SRwtrack(rp, rwbuf, n, track, mode)) != n){ + fprint(2, "wtrack: write incomplete: asked %ld, did %ld\n", n, x); + if(rp->status == STok) + rp->status = Status_SW; + close(fd); + return -1; + } + nbytes -= n; + total += n; + while(nbytes){ + n = MIN(nbytes, maxiosize); + if((n = read(fd, rwbuf, n)) == -1){ + break; + } + if((x = SRwrite(rp, rwbuf, n)) != n){ + fprint(2, "write: write incomplete: asked %ld, did %ld\n", n, x); + if(rp->status == STok) + rp->status = Status_SW; + break; + } + nbytes -= n; + total += n; + } + close(fd); + if(pid >= 0 && waitfor(pid)){ + rp->status = Status_SW; + return -1; + } + return total; +} + +static long +cmdload(ScsiReq *rp, int argc, char *argv[]) +{ + USED(argc, argv); + return SRmload(rp, 0); +} + +static long +cmdunload(ScsiReq *rp, int argc, char *argv[]) +{ + USED(argc, argv); + return SRmload(rp, 1); +} + +static long +cmdfixation(ScsiReq *rp, int argc, char *argv[]) +{ + uchar type; + char *p; + + type = 0; + if(argc && (type = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + return SRfixation(rp, type); +} + +static long +cmdeinit(ScsiReq *rp, int argc, char *argv[]) +{ + USED(argc, argv); + return SReinitialise(rp); +} + +static long +cmdmmove(ScsiReq *rp, int argc, char *argv[]) +{ + int transport, source, destination, invert; + char *p; + + invert = 0; + + switch(argc){ + + default: + rp->status = Status_BADARG; + return -1; + + case 4: + if((invert = strtoul(argv[3], &p, 0)) == 0 && p == argv[3]){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 3: + if((transport = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + if((source = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){ + rp->status = Status_BADARG; + return -1; + } + if((destination = strtoul(argv[2], &p, 0)) == 0 && p == argv[2]){ + rp->status = Status_BADARG; + return -1; + } + break; + } + + return SRmmove(rp, transport, source, destination, invert); +} + +static long +cmdestatus(ScsiReq *rp, int argc, char *argv[]) +{ + uchar *list, *lp, type; + long d, i, n, nbytes, status; + char *p; + + type = 0; + nbytes = 4096; + + switch(argc){ + + default: + rp->status = Status_BADARG; + return -1; + + case 2: + if((nbytes = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){ + rp->status = Status_BADARG; + return -1; + } + /*FALLTHROUGH*/ + + case 1: + if((type = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ + rp->status = Status_BADARG; + return -1; + } + break; + + case 0: + break; + } + + list = malloc(nbytes); + if(list == 0){ + rp->status = STnomem; + return -1; + } + status = SRestatus(rp, type, list, nbytes); + if(status == -1){ + free(list); + return -1; + } + + lp = list; + nbytes = ((lp[5]<<16)|(lp[6]<<8)|lp[7])-8; + Bprint(&bout, " Header\n "); + for(i = 0; i < 8; i++){ /* header */ + Bprint(&bout, " %2.2uX", *lp); + lp++; + } + Bputc(&bout, '\n'); + + while(nbytes > 0){ /* pages */ + i = ((lp[5]<<16)|(lp[6]<<8)|lp[7]); + nbytes -= i+8; + Bprint(&bout, " Type"); + for(n = 0; n < 8; n++) /* header */ + Bprint(&bout, " %2.2uX", lp[n]); + Bprint(&bout, "\n "); + d = (lp[2]<<8)|lp[3]; + lp += 8; + for(n = 0; n < i; n++){ + if(n && (n % d) == 0) + Bprint(&bout, "\n "); + Bprint(&bout, " %2.2uX", *lp); + lp++; + } + if(n && (n % d)) + Bputc(&bout, '\n'); + } + + free(list); + return status; +} + +static long +cmdhelp(ScsiReq *rp, int argc, char *argv[]) +{ + ScsiCmd *cp; + char *p; + + USED(rp); + if(argc) + p = argv[0]; + else + p = 0; + for(cp = scsicmd; cp->name; cp++){ + if(p == 0 || strcmp(p, cp->name) == 0) + Bprint(&bout, "%s\n", cp->help); + } + return 0; +} + +static long +cmdprobe(ScsiReq *rp, int argc, char *argv[]) +{ + char buf[32]; + ScsiReq scsireq; + char *ctlr, *unit; + + USED(argc, argv); + rp->status = STok; + scsireq.flags = 0; + + for(ctlr="CDEFGHIJ0123456789abcdef"; *ctlr; ctlr++) { + /* + * I can guess how many units you have. + * SATA controllers can have more than two drives each. + */ + if(*ctlr >= 'C' && *ctlr <= 'D') + unit = "01"; + else if((*ctlr >= '0' && *ctlr <= '9') + || (*ctlr >= 'a' && *ctlr <= 'f')) + unit = "0123456789abcdef"; /* allow wide scsi */ + else + unit = "01234567"; + + for(; *unit; unit++){ + sprint(buf, "/dev/sd%c%c", *ctlr, *unit); + if(SRopenraw(&scsireq, buf) == -1) + continue; + SRreqsense(&scsireq); + switch(scsireq.status){ + case STok: + case Status_SD: + Bprint(&bout, "%s: ", buf); + cmdinquiry(&scsireq, 0, 0); + break; + } + SRclose(&scsireq); + } + } + return 0; +} + +static long +cmdclose(ScsiReq *rp, int argc, char *argv[]) +{ + USED(argc, argv); + return SRclose(rp); +} + +static long +cmdopen(ScsiReq *rp, int argc, char *argv[]) +{ + int raw; + long status; + + raw = 0; + if(argc && strcmp("-r", argv[0]) == 0){ + raw = 1; + argc--, argv++; + } + if(argc != 1){ + rp->status = Status_BADARG; + return -1; + } + if(raw == 0){ + if((status = SRopen(rp, argv[0])) != -1 && verbose) + Bprint(&bout, "%sblock size: %ld\n", + rp->flags&Fbfixed? "fixed ": "", rp->lbsize); + } + else { + status = SRopenraw(rp, argv[0]); + rp->lbsize = 512; + } + return status; +} + +static ScsiCmd scsicmd[] = { + { "ready", cmdready, 1, /*[0x00]*/ + "ready", + }, + { "rewind", cmdrewind, 1, /*[0x01]*/ + "rewind", + }, + { "rezero", cmdrewind, 1, /*[0x01]*/ + "rezero", + }, + { "reqsense", cmdreqsense, 1, /*[0x03]*/ + "reqsense", + }, + { "format", cmdformat, 0, /*[0x04]*/ + "format", + }, + { "rblimits", cmdrblimits, 1, /*[0x05]*/ + "rblimits", + }, + { "read", cmdread, 1, /*[0x08]*/ + "read [|]file [nbytes]", + }, + { "write", cmdwrite, 1, /*[0x0A]*/ + "write [|]file [nbytes]", + }, + { "seek", cmdseek, 1, /*[0x0B]*/ + "seek offset [whence]", + }, + { "filemark", cmdfilemark, 1, /*[0x10]*/ + "filemark [howmany]", + }, + { "space", cmdspace, 1, /*[0x11]*/ + "space [-f] [-b] [[--] howmany]", + }, + { "inquiry", cmdinquiry, 1, /*[0x12]*/ + "inquiry", + }, + { "modeselect6",cmdmodeselect6, 1, /*[0x15] */ + "modeselect6 bytes...", + }, + { "modeselect", cmdmodeselect10, 1, /*[0x55] */ + "modeselect bytes...", + }, + { "modesense6", cmdmodesense6, 1, /*[0x1A]*/ + "modesense6 [page [nbytes]]", + }, + { "modesense", cmdmodesense10, 1, /*[0x5A]*/ + "modesense [page [nbytes]]", + }, + { "start", cmdstart, 1, /*[0x1B]*/ + "start [code]", + }, + { "stop", cmdstop, 1, /*[0x1B]*/ + "stop", + }, + { "eject", cmdeject, 1, /*[0x1B]*/ + "eject", + }, + { "ingest", cmdingest, 1, /*[0x1B]*/ + "ingest", + }, + { "capacity", cmdcapacity, 1, /*[0x25]*/ + "capacity", + }, + + { "blank", cmdblank, 1, /*[0xA1]*/ + "blank [track/LBA [type]]", + }, +// { "synccache", cmdsynccache, 1, /*[0x35]*/ +// "synccache", +// }, + { "rtoc", cmdrtoc, 1, /*[0x43]*/ + "rtoc [track/session-number [format]]", + }, + { "rdiscinfo", cmdrdiscinfo, 1, /*[0x51]*/ + "rdiscinfo", + }, + { "rtrackinfo", cmdrtrackinfo, 1, /*[0x52]*/ + "rtrackinfo [track]", + }, + + { "cdpause", cmdcdpause, 1, /*[0x4B]*/ + "cdpause", + }, + { "cdresume", cmdcdresume, 1, /*[0x4B]*/ + "cdresume", + }, + { "cdstop", cmdcdstop, 1, /*[0x4E]*/ + "cdstop", + }, + { "cdplay", cmdcdplay, 1, /*[0xA5]*/ + "cdplay [track-number] or [-r [LBA [length]]]", + }, + { "cdload", cmdcdload, 1, /*[0xA6*/ + "cdload [slot]", + }, + { "cdunload", cmdcdunload, 1, /*[0xA6]*/ + "cdunload [slot]", + }, + { "cdstatus", cmdcdstatus, 1, /*[0xBD]*/ + "cdstatus", + }, +// { "getconf", cmdgetconf, 1, /*[0x46]*/ +// "getconf", +// }, + +// { "fwaddr", cmdfwaddr, 1, /*[0xE2]*/ +// "fwaddr [track [mode [npa]]]", +// }, +// { "treserve", cmdtreserve, 1, /*[0xE4]*/ +// "treserve nbytes", +// }, +// { "trackinfo", cmdtrackinfo, 1, /*[0xE5]*/ +// "trackinfo [track]", +// }, +// { "wtrack", cmdwtrack, 1, /*[0xE6]*/ +// "wtrack [|]file [nbytes [track [mode]]]", +// }, +// { "load", cmdload, 1, /*[0xE7]*/ +// "load", +// }, +// { "unload", cmdunload, 1, /*[0xE7]*/ +// "unload", +// }, +// { "fixation", cmdfixation, 1, /*[0xE9]*/ +// "fixation [toc-type]", +// }, + { "einit", cmdeinit, 1, /*[0x07]*/ + "einit", + }, + { "estatus", cmdestatus, 1, /*[0xB8]*/ + "estatus", + }, + { "mmove", cmdmmove, 1, /*[0xA5]*/ + "mmove transport source destination [invert]", + }, + + { "help", cmdhelp, 0, + "help", + }, + { "probe", cmdprobe, 0, + "probe", + }, + { "close", cmdclose, 1, + "close", + }, + { "open", cmdopen, 0, + "open [-r] sddev", + }, + { 0, 0 }, +}; + +#define SEP(c) (((c)==' ')||((c)=='\t')||((c)=='\n')) + +static char * +tokenise(char *s, char **start, char **end) +{ + char *to; + Rune r; + int n; + + while(*s && SEP(*s)) /* skip leading white space */ + s++; + to = *start = s; + while(*s){ + n = chartorune(&r, s); + if(SEP(r)){ + if(to != *start) /* we have data */ + break; + s += n; /* null string - keep looking */ + while(*s && SEP(*s)) + s++; + to = *start = s; + } + else if(r == '\''){ + s += n; /* skip leading quote */ + while(*s){ + n = chartorune(&r, s); + if(r == '\''){ + if(s[1] != '\'') + break; + s++; /* embedded quote */ + } + while (n--) + *to++ = *s++; + } + if(!*s) /* no trailing quote */ + break; + s++; /* skip trailing quote */ + } + else { + while(n--) + *to++ = *s++; + } + } + *end = to; + return s; +} + +static int +parse(char *s, char *fields[], int nfields) +{ + int c, argc; + char *start, *end; + + argc = 0; + c = *s; + while(c){ + s = tokenise(s, &start, &end); + c = *s++; + if(*start == 0) + break; + if(argc >= nfields-1) + return -1; + *end = 0; + fields[argc++] = start; + } + fields[argc] = 0; + return argc; +} + +static void +usage(void) +{ + fprint(2, "usage: %s [-6eq] [-m maxiosize] [[-r] /dev/sdXX]\n", argv0); + exits("usage"); +} + +static struct { + int status; + char* description; +} description[] = { + STnomem, "buffer allocation failed", + STtimeout, "bus timeout", + STharderr, "controller error of some kind", + STok, "good", + STcheck, "check condition", + STcondmet, "condition met/good", + STbusy, "busy ", + STintok, "intermediate/good", + STintcondmet, "intermediate/condition met/good", + STresconf, "reservation conflict", + STterminated, "command terminated", + STqfull, "queue full", + + Status_SD, "sense-data available", + Status_SW, "internal software error", + Status_BADARG, "bad argument to request", + + 0, 0, +}; + +void +main(int argc, char *argv[]) +{ + ScsiReq target; + char *ap, *av[256]; + int ac, i, raw = 0; + ScsiCmd *cp; + long status; + + ARGBEGIN { + case 'e': + exabyte = 1; + /* fallthrough */ + case '6': + force6bytecmds = 1; + break; + case 'm': + ap = ARGF(); + if(ap == nil) + usage(); + maxiosize = atol(ap); + if(maxiosize < 512 || maxiosize > MaxIOsize) + sysfatal("max-xfer < 512 or > %d", MaxIOsize); + break; + case 'r': /* must be last option and not bundled */ + raw++; + break; + case 'q': + verbose = 0; + break; + default: + usage(); + } ARGEND + + if(Binit(&bin, 0, OREAD) == Beof || Binit(&bout, 1, OWRITE) == Beof){ + fprint(2, "%s: can't init bio: %r\n", argv0); + exits("Binit"); + } + + memset(&target, 0, sizeof target); + if (raw) { /* hack for -r */ + ++argc; + --argv; + } + if(argc && cmdopen(&target, argc, argv) == -1) { + fprint(2, "open failed\n"); + usage(); + } + Bflush(&bout); + + while(ap = Brdline(&bin, '\n')){ + ap[Blinelen(&bin)-1] = 0; + switch(ac = parse(ap, av, nelem(av))){ + + default: + for(cp = scsicmd; cp->name; cp++){ + if(strcmp(cp->name, av[0]) == 0) + break; + } + if(cp->name == 0){ + Bprint(&bout, "eh?\n"); + break; + } + if((target.flags & Fopen) == 0 && cp->open){ + Bprint(&bout, "no current target\n"); + break; + } + if((status = (*cp->f)(&target, ac-1, &av[1])) != -1){ + if(verbose) + Bprint(&bout, "ok %ld\n", status); + break; + } + for(i = 0; description[i].description; i++){ + if(target.status != description[i].status) + continue; + if(target.status == Status_SD) + makesense(&target); + else + Bprint(&bout, "%s\n", description[i].description); + break; + } + break; + + case -1: + Bprint(&bout, "eh?\n"); + break; + + case 0: + break; + } + Bflush(&bout); + } + exits(0); +} + +/* USB mass storage fake */ +long +umsrequest(Umsc *umsc, ScsiPtr *cmd, ScsiPtr *data, int *status) +{ + USED(umsc, data, cmd); + *status = STharderr; + return -1; +} diff --git a/sys/src/cmd/scuzz/sense.c b/sys/src/cmd/scuzz/sense.c new file mode 100755 index 000000000..1b4d41850 --- /dev/null +++ b/sys/src/cmd/scuzz/sense.c @@ -0,0 +1,44 @@ +#include <u.h> +#include <libc.h> +#include <bio.h> +#include <disk.h> +#include "scsireq.h" + +extern Biobuf bout; + +static char* key[16] = { + "no sense", + "recovered error", + "not ready", + "medium error", + "hardware error", + "illegal request", + "unit attention", + "data protect", + "blank check", + "vendor specific", + "copy aborted", + "aborted command", + "equal", + "volume overflow", + "miscompare", + "reserved", +}; + +/* + * use libdisk to read /sys/lib/scsicodes + */ +void +makesense(ScsiReq *rp) +{ + char *s; + int i; + + Bprint(&bout, "sense data: %s", key[rp->sense[2] & 0x0F]); + if(rp->sense[7] >= 5 && (s = scsierror(rp->sense[0xc], rp->sense[0xd]))) + Bprint(&bout, ": %s", s); + Bprint(&bout, "\n\t"); + for(i = 0; i < 8+rp->sense[7]; i++) + Bprint(&bout, " %2.2ux", rp->sense[i]); + Bprint(&bout, "\n"); +} |