summaryrefslogtreecommitdiff
path: root/sys/src/cmd/scuzz
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/scuzz
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/scuzz')
-rwxr-xr-xsys/src/cmd/scuzz/cdaudio.c159
-rwxr-xr-xsys/src/cmd/scuzz/cdr.c218
-rwxr-xr-xsys/src/cmd/scuzz/changer.c62
-rwxr-xr-xsys/src/cmd/scuzz/mkfile15
-rwxr-xr-xsys/src/cmd/scuzz/mo.words54
-rwxr-xr-xsys/src/cmd/scuzz/scsireq.c755
-rwxr-xr-xsys/src/cmd/scuzz/scsireq.h212
-rwxr-xr-xsys/src/cmd/scuzz/scuzz.c1958
-rwxr-xr-xsys/src/cmd/scuzz/sense.c44
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");
+}