# To unbundle, run this file mkdir scuzz.mine echo scuzz.mine/cdaudio.c sed 's/^X//' >scuzz.mine/cdaudio.c <<'!' X#include X#include X#include X#include "scsireq.h" extern Biobuf bout; long SRcdpause(ScsiReq *rp, int resume) X{ X uchar cmd[10]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdCDpause; X cmd[8] = resume; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = cmd; X rp->data.count = 0; X rp->data.write = 1; X return SRrequest(rp); X} long SRcdstop(ScsiReq *rp) X{ X uchar cmd[10]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdCDstop; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = cmd; X rp->data.count = 0; X rp->data.write = 1; X return SRrequest(rp); X} static long X_SRcdplay(ScsiReq *rp, long lba, long length) X{ X uchar cmd[12]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdCDplay; X cmd[2] = lba>>24; X cmd[3] = lba>>16; X cmd[4] = lba>>8; X cmd[5] = lba; X cmd[6] = length>>24; X cmd[7] = length>>16; X cmd[8] = length>>8; X cmd[9] = length; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = cmd; X rp->data.count = 0; X rp->data.write = 1; X return SRrequest(rp); X} static struct { X int trackno; X long lba; X long length; X} tracks[100]; static int ntracks; long SRcdplay(ScsiReq *rp, int raw, long start, long length) X{ X uchar d[100*8+4], *p; X int lba, n, tdl; X if(raw || start == 0) X return _SRcdplay(rp, start, length); X ntracks = 0; X if(SRTOC(rp, d, sizeof(d), 0, 0) == -1){ X if(rp->status == STok) X Bprint(&bout, "\t(probably empty)\n"); X return -1; X } X tdl = (d[0]<<8)|d[1]; X for(p = &d[4], n = tdl-2; n; n -= 8, p += 8){ X tracks[ntracks].trackno = p[2]; X lba = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7]; X tracks[ntracks].lba = lba; X if(ntracks > 0) X tracks[ntracks-1].length = lba-tracks[ntracks-1].lba; X ntracks++; X } X if(ntracks > 0) X tracks[ntracks-1].length = 0xFFFFFFFF; X for(n = 0; n < ntracks; n++){ X if(start != tracks[n].trackno) X continue; X return _SRcdplay(rp, tracks[n].lba, tracks[n].length); X } X return -1; X} long SRcdload(ScsiReq *rp, int load, int slot) X{ X uchar cmd[12]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdCDload; X if(load) X cmd[4] = 0x03; X else X cmd[4] = 0x02; X cmd[8] = slot; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = cmd; X rp->data.count = 0; X rp->data.write = 1; X return SRrequest(rp); X} long SRcdstatus(ScsiReq *rp, uchar *list, int nbytes) X{ X uchar cmd[12]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdCDstatus; X cmd[8] = nbytes>>8; X cmd[9] = nbytes; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = list; X rp->data.count = nbytes; X rp->data.write = 0; X return SRrequest(rp); X} long SRgetconf(ScsiReq *rp, uchar *list, int nbytes) X{ X uchar cmd[10]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = Scmdgetconf; X cmd[7] = nbytes>>8; X cmd[8] = nbytes; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = list; X rp->data.count = nbytes; X rp->data.write = 0; X return SRrequest(rp); X} ! echo scuzz.mine/cdr.c sed 's/^X//' >scuzz.mine/cdr.c <<'!' X#include X#include X#include "scsireq.h" long SRblank(ScsiReq *rp, uchar type, uchar track) X{ X uchar cmd[12]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdBlank; X cmd[1] = type; X cmd[2] = track>>24; X cmd[3] = track>>16; X cmd[4] = track>>8; X cmd[5] = track; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = cmd; X rp->data.count = 0; X rp->data.write = 1; X return SRrequest(rp); X} long SRsynccache(ScsiReq *rp) X{ X uchar cmd[10]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdSynccache; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = cmd; X rp->data.count = 0; X rp->data.write = 1; X return SRrequest(rp); X} long SRTOC(ScsiReq *rp, void *data, int nbytes, uchar format, uchar track) X{ X uchar cmd[10]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdRTOC; X cmd[2] = format; X cmd[6] = track; X cmd[7] = nbytes>>8; X cmd[8] = nbytes; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = data; X rp->data.count = nbytes; X rp->data.write = 0; X return SRrequest(rp); X} long SRrdiscinfo(ScsiReq *rp, void *data, int nbytes) X{ X uchar cmd[10]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdRdiscinfo; X cmd[7] = nbytes>>8; X cmd[8] = nbytes; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = data; X rp->data.count = nbytes; X rp->data.write = 0; X return SRrequest(rp); X} long SRrtrackinfo(ScsiReq *rp, void *data, int nbytes, int track) X{ X uchar cmd[10]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdRtrackinfo; X cmd[1] = 0x01; X cmd[2] = track>>24; X cmd[3] = track>>16; X cmd[4] = track>>8; X cmd[5] = track; X cmd[7] = nbytes>>8; X cmd[8] = nbytes; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = data; X rp->data.count = nbytes; X rp->data.write = 0; X return SRrequest(rp); X} long SRfwaddr(ScsiReq *rp, uchar track, uchar mode, uchar npa, uchar *data) X{ X uchar cmd[10]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdFwaddr; X cmd[2] = track; X cmd[3] = mode; X cmd[7] = npa; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = data; X rp->data.count = MaxDirData; X rp->data.write = 0; X return SRrequest(rp); X} long SRtreserve(ScsiReq *rp, long nbytes) X{ X uchar cmd[10]; X long n; X if((nbytes % rp->lbsize)){ X rp->status = Status_BADARG; X return -1; X } X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdTreserve; X n = nbytes/rp->lbsize; X cmd[5] = n>>24; X cmd[6] = n>>16; X cmd[7] = n>>8; X cmd[8] = n; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = cmd; X rp->data.count = 0; X rp->data.write = 1; X return SRrequest(rp); X} long SRtinfo(ScsiReq *rp, uchar track, uchar *data) X{ X uchar cmd[10]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdTinfo; X cmd[5] = track; X cmd[8] = MaxDirData; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = data; X rp->data.count = MaxDirData; X rp->data.write = 0; X return SRrequest(rp); X} long SRwtrack(ScsiReq *rp, void *buf, long nbytes, uchar track, uchar mode) X{ X uchar cmd[10]; X long m, n; X if((nbytes % rp->lbsize) || nbytes > MaxIOsize){ X rp->status = Status_BADARG; X return -1; X } X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdTwrite; X cmd[5] = track; X cmd[6] = mode; X n = nbytes/rp->lbsize; X cmd[7] = n>>8; X cmd[8] = n; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = buf; X rp->data.count = nbytes; X rp->data.write = 1; X m = SRrequest(rp); X if(m < 0) X return -1; X rp->offset += n; X return m; X} long SRmload(ScsiReq *rp, uchar code) X{ X uchar cmd[12]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdMload; X cmd[8] = code; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = cmd; X rp->data.count = 0; X rp->data.write = 1; X return SRrequest(rp); X} long SRfixation(ScsiReq *rp, uchar type) X{ X uchar cmd[10]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdFixation; X cmd[8] = type; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = cmd; X rp->data.count = 0; X rp->data.write = 1; X return SRrequest(rp); X} ! echo scuzz.mine/changer.c sed 's/^X//' >scuzz.mine/changer.c <<'!' X#include X#include X#include "scsireq.h" long SReinitialise(ScsiReq *rp) X{ X uchar cmd[6]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdEInitialise; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = cmd; X rp->data.count = 0; X rp->data.write = 1; X return SRrequest(rp); X} long SRmmove(ScsiReq *rp, int transport, int source, int destination, int invert) X{ X uchar cmd[12]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdMMove; X cmd[2] = transport>>8; X cmd[3] = transport; X cmd[4] = source>>8; X cmd[5] = source; X cmd[6] = destination>>8; X cmd[7] = destination; X cmd[10] = invert & 0x01; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = cmd; X rp->data.count = 0; X rp->data.write = 1; X return SRrequest(rp); X} long SRestatus(ScsiReq *rp, uchar type, uchar *list, int nbytes) X{ X uchar cmd[12]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdEStatus; X cmd[1] = type & 0x07; X cmd[4] = 0xFF; X cmd[5] = 0xFF; X cmd[7] = nbytes>>16; X cmd[8] = nbytes>>8; X cmd[9] = nbytes; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = list; X rp->data.count = nbytes; X rp->data.write = 0; X return SRrequest(rp); X} ! echo scuzz.mine/mkfile sed 's/^X//' >scuzz.mine/mkfile <<'!' Xscuzz.mine/scsireq.c <<'!' X#include X#include X/* X * BUGS: X * no luns X * and incomplete in many other ways X */ X#include "scsireq.h" X/* X * Exabyte tape drives, at least old ones like the 8200 and 8505, X * are dumb: you have to read the exact block size on the tape, X * they don't take 10-byte SCSI commands, and various other fine points. X */ enum { X Debug = 0, X Exabyte = 1, X}; extern long maxiosize; long SRready(ScsiReq *rp) X{ X uchar cmd[6]; X memset(cmd, 0, sizeof(cmd)); X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = cmd; X rp->data.count = 0; X rp->data.write = 1; X return SRrequest(rp); X} long SRrewind(ScsiReq *rp) X{ X uchar cmd[6]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdRewind; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = cmd; X rp->data.count = 0; X rp->data.write = 1; X if(SRrequest(rp) >= 0){ X rp->offset = 0; X return 0; X } X return -1; X} long SRreqsense(ScsiReq *rp) X{ X uchar cmd[6]; X ScsiReq req; X long status; X if(rp->status == Status_SD){ X rp->status = STok; X return 0; X } X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdRsense; X cmd[4] = sizeof(req.sense); X memset(&req, 0, sizeof(req)); X req.fd = rp->fd; X req.cmd.p = cmd; X req.cmd.count = sizeof(cmd); X req.data.p = rp->sense; X req.data.count = sizeof(rp->sense); X req.data.write = 0; X status = SRrequest(&req); X rp->status = req.status; X return status; X} long SRformat(ScsiReq *rp) X{ X uchar cmd[6]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdFormat; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = cmd; X rp->data.count = 6; X rp->data.write = 0; X return SRrequest(rp); X} long SRrblimits(ScsiReq *rp, uchar *list) X{ X uchar cmd[6]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdRblimits; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = list; X rp->data.count = 6; X rp->data.write = 0; X return SRrequest(rp); X} static int dirdevrw(ScsiReq *rp, uchar *cmd, long nbytes) X{ X long n; X n = nbytes/rp->lbsize; X if(rp->offset <= 0x1fffff && n <= 256 && (rp->flags & Frw10) == 0){ X cmd[1] = rp->offset>>16; X cmd[2] = rp->offset>>8; X cmd[3] = rp->offset; X cmd[4] = n; X cmd[5] = 0; X return 6; X } X cmd[0] |= ScmdExtread; X cmd[1] = 0; X cmd[2] = rp->offset>>24; X cmd[3] = rp->offset>>16; X cmd[4] = rp->offset>>8; X cmd[5] = rp->offset; X cmd[6] = 0; X cmd[7] = n>>8; X cmd[8] = n; X cmd[9] = 0; X return 10; X} static int seqdevrw(ScsiReq *rp, uchar *cmd, long nbytes) X{ X long n; X cmd[1] = rp->flags&Fbfixed? 0x01: 0x00; X // cmd[1]&2 is the SILI bit: don't report `incorrect' block lengths X n = nbytes/rp->lbsize; X cmd[2] = n>>16; X cmd[3] = n>>8; X cmd[4] = n; X cmd[5] = 0; X return 6; X} long SRread(ScsiReq *rp, void *buf, long nbytes) X{ X uchar cmd[10]; X long n; X if((nbytes % rp->lbsize) || nbytes > maxiosize){ X rp->status = Status_BADARG; X return -1; X } X cmd[0] = ScmdRead; X if(rp->flags & Fseqdev) X rp->cmd.count = seqdevrw(rp, cmd, nbytes); X else X rp->cmd.count = dirdevrw(rp, cmd, nbytes); X rp->cmd.p = cmd; X rp->data.p = buf; X rp->data.count = nbytes; X rp->data.write = 0; X if((n = SRrequest(rp)) == -1){ X // maybe we just read a short record? X if (Exabyte) { X print("read error\n"); X rp->status = STcheck; X return n; X } X if(rp->status != Status_SD || (rp->sense[0] & 0x80) == 0) X return -1; X print("SRread: SRrequest failed with sense data; reading byte count from sense\n"); // DEBUG X n = ((rp->sense[3]<<24) X | (rp->sense[4]<<16) X | (rp->sense[5]<<8) X | rp->sense[6]) X * rp->lbsize; X if(!(rp->flags & Fseqdev)) X return -1; X if(rp->sense[2] == 0x80 || rp->sense[2] == 0x08) X rp->data.count = nbytes - n; X else if(rp->sense[2] == 0x20 && n > 0) X rp->data.count = nbytes - n; X else X return -1; X print("SRread: computing byte count from sense\n"); // DEBUG X n = rp->data.count; X rp->status = STok; X } X rp->offset += n/rp->lbsize; X return n; X} long SRwrite(ScsiReq *rp, void *buf, long nbytes) X{ X uchar cmd[10]; X long n; X if((nbytes % rp->lbsize) || nbytes > maxiosize){ X rp->status = Status_BADARG; X return -1; X } X cmd[0] = ScmdWrite; X if(rp->flags & Fseqdev) X rp->cmd.count = seqdevrw(rp, cmd, nbytes); X else X rp->cmd.count = dirdevrw(rp, cmd, nbytes); X rp->cmd.p = cmd; X rp->data.p = buf; X rp->data.count = nbytes; X rp->data.write = 1; X if((n = SRrequest(rp)) == -1){ X if (Exabyte) { X print("write error\n"); X rp->status = STcheck; X return n; X } X if(rp->status != Status_SD || rp->sense[2] != 0x40) X return -1; X if(rp->sense[0] & 0x80){ X n -= ((rp->sense[3]<<24) X | (rp->sense[4]<<16) X | (rp->sense[5]<<8) X | rp->sense[6]) X * rp->lbsize; X rp->data.count = nbytes - n; X } X else X rp->data.count = nbytes; X n = rp->data.count; X } X rp->offset += n/rp->lbsize; X return n; X} long SRseek(ScsiReq *rp, long offset, int type) X{ X uchar cmd[10]; X switch(type){ X case 0: X break; X case 1: X offset += rp->offset; X if(offset >= 0) X break; X /*FALLTHROUGH*/ X default: X rp->status = Status_BADARG; X return -1; X } X if(offset <= 0x1fffff && (rp->flags & Frw10) == 0){ X cmd[0] = ScmdSeek; X cmd[1] = (offset>>16) & 0x1F; X cmd[2] = offset>>8; X cmd[3] = offset; X cmd[4] = 0; X cmd[5] = 0; X rp->cmd.count = 6; X }else{ X cmd[0] = ScmdExtseek; X cmd[1] = 0; X cmd[2] = offset>>24; X cmd[3] = offset>>16; X cmd[4] = offset>>8; X cmd[5] = offset; X cmd[6] = 0; X cmd[7] = 0; X cmd[8] = 0; X cmd[9] = 0; X rp->cmd.count = 10; X } X rp->cmd.p = cmd; X rp->data.p = cmd; X rp->data.count = 0; X rp->data.write = 1; X SRrequest(rp); X if(rp->status == STok) X return rp->offset = offset; X return -1; X} long SRfilemark(ScsiReq *rp, ulong howmany) X{ X uchar cmd[6]; X cmd[0] = ScmdFmark; X cmd[1] = 0; X cmd[2] = howmany>>16; X cmd[3] = howmany>>8; X cmd[4] = howmany; X cmd[5] = 0; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = cmd; X rp->data.count = 0; X rp->data.write = 1; X return SRrequest(rp); X} long SRspace(ScsiReq *rp, uchar code, long howmany) X{ X uchar cmd[6]; X cmd[0] = ScmdSpace; X cmd[1] = code; X cmd[2] = howmany>>16; X cmd[3] = howmany>>8; X cmd[4] = howmany; X cmd[5] = 0; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = cmd; X rp->data.count = 0; X rp->data.write = 1; X /* X * what about rp->offset? X */ X return SRrequest(rp); X} long SRinquiry(ScsiReq *rp) X{ X uchar cmd[6]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdInq; X cmd[4] = sizeof(rp->inquiry); X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = rp->inquiry; X rp->data.count = sizeof(rp->inquiry); X rp->data.write = 0; X if(SRrequest(rp) >= 0){ X rp->flags |= Finqok; X return 0; X } X rp->flags &= ~Finqok; X return -1; X} long SRmodeselect6(ScsiReq *rp, uchar *list, long nbytes) X{ X uchar cmd[6]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdMselect6; X if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2) X cmd[1] = 0x10; X cmd[4] = nbytes; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = list; X rp->data.count = nbytes; X rp->data.write = 1; X return SRrequest(rp); X} long SRmodeselect10(ScsiReq *rp, uchar *list, long nbytes) X{ X uchar cmd[10]; X memset(cmd, 0, sizeof(cmd)); X if((rp->flags & Finqok) && (rp->inquiry[2] & 0x07) >= 2) X cmd[1] = 0x10; X cmd[0] = ScmdMselect10; X cmd[7] = nbytes>>8; X cmd[8] = nbytes; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = list; X rp->data.count = nbytes; X rp->data.write = 1; X return SRrequest(rp); X} long SRmodesense6(ScsiReq *rp, uchar page, uchar *list, long nbytes) X{ X uchar cmd[6]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdMsense6; X cmd[2] = page; X cmd[4] = nbytes; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = list; X rp->data.count = nbytes; X rp->data.write = 0; X return SRrequest(rp); X} long SRmodesense10(ScsiReq *rp, uchar page, uchar *list, long nbytes) X{ X uchar cmd[10]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdMsense10; X cmd[2] = page; X cmd[7] = nbytes>>8; X cmd[8] = nbytes; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = list; X rp->data.count = nbytes; X rp->data.write = 0; X return SRrequest(rp); X} long SRstart(ScsiReq *rp, uchar code) X{ X uchar cmd[6]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdStart; X cmd[4] = code; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = cmd; X rp->data.count = 0; X rp->data.write = 1; X return SRrequest(rp); X} long SRrcapacity(ScsiReq *rp, uchar *data) X{ X uchar cmd[10]; X memset(cmd, 0, sizeof(cmd)); X cmd[0] = ScmdRcapacity; X rp->cmd.p = cmd; X rp->cmd.count = sizeof(cmd); X rp->data.p = data; X rp->data.count = 8; X rp->data.write = 0; X return SRrequest(rp); X} static long request(int fd, ScsiPtr *cmd, ScsiPtr *data, int *status) X{ X long n; X char buf[16]; X if(write(fd, cmd->p, cmd->count) != cmd->count){ X fprint(2, "scsireq: write cmd: %r\n"); X *status = Status_SW; X return -1; X } X if(data->write) X n = write(fd, data->p, data->count); X else X n = read(fd, data->p, data->count); X if (Debug) X print("request: transferred %ld of %ld bytes of actual data\n", X n, data->count); X /* TODO: this was `< 0'; I made it `<= 0' for the exabyte */ X if(read(fd, buf, sizeof(buf)) <= 0){ X fprint(2, "scsireq: read status: %r\n"); X *status = Status_SW; X return -1; X } X buf[sizeof(buf)-1] = '\0'; X *status = atoi(buf); X if(n < 0 && (Exabyte || *status != STcheck)) X fprint(2, "scsireq: status 0x%2.2uX: data transfer: %r\n", *status); X return n; X} long SRrequest(ScsiReq *rp) X{ X long n; X int status; retry: X n = request(rp->fd, &rp->cmd, &rp->data, &status); X switch(rp->status = status){ X case STok: X rp->data.count = n; X break; X case STcheck: X if(rp->cmd.p[0] != ScmdRsense && SRreqsense(rp) != -1) X rp->status = Status_SD; X print("SRrequest: STcheck, returning -1\n"); // exabyte X return -1; X case STbusy: X sleep(1000); X goto retry; X default: X fprint(2, "status 0x%2.2uX\n", status); X return -1; X } X return n; X} int SRclose(ScsiReq *rp) X{ X if((rp->flags & Fopen) == 0){ X rp->status = Status_BADARG; X return -1; X } X close(rp->fd); X rp->flags = 0; X return 0; X} static int dirdevopen(ScsiReq *rp) X{ X ulong blocks; X uchar data[8]; X if(SRstart(rp, 1) == -1 || SRrcapacity(rp, data) == -1) X return -1; X rp->lbsize = (data[4]<<28)|(data[5]<<16)|(data[6]<<8)|data[7]; X blocks = (data[0]<<24)|(data[1]<<16)|(data[2]<<8)|data[3]; X if(blocks > 0x1fffff && !Exabyte) X rp->flags |= Frw10; /* some newer devices don't support 6-byte commands */ X return 0; X} static int seqdevopen(ScsiReq *rp) X{ X uchar mode[16], limits[6]; X if(SRrblimits(rp, limits) == -1) X return -1; X if(limits[1] || limits[2] != limits[4] || limits[3] != limits[5]){ X memset(mode, 0, sizeof(mode)); X /* X * On some older hardware the optional 10-byte X * modeselect command isn't implemented. X */ X if (Exabyte) X rp->flags |= Fmode6; X if(!(rp->flags & Fmode6)){ X /* 10-byte command */ X mode[3] = 0x10; /* device-specific param. */ X mode[7] = 8; /* block descriptor length */ X /* X * exabytes can't handle this, and X * modeselect(10) is optional. X */ X if(SRmodeselect10(rp, mode, sizeof(mode)) != -1){ X rp->lbsize = 1; X return 0; X } X /* can't do 10-byte commands */ X rp->flags |= Fmode6; X } X /* 6-byte command */ X mode[2] = 0x10; /* device-specific param. */ X mode[3] = 8; /* block descriptor length */ X /* X * bsd sez exabytes need this bit (NBE) in vendor-specific X * page (0), but so far we haven't needed it. X */ X if (0) X mode[12] |= 8; X if(SRmodeselect6(rp, mode, 4+8) == -1) X return -1; X rp->lbsize = 1; X }else{ X rp->flags |= Fbfixed; X rp->lbsize = (limits[4]<<8)|limits[5]; X } X return 0; X} static int wormdevopen(ScsiReq *rp) X{ X uchar list[MaxDirData]; X long status; X if(SRstart(rp, 1) == -1) X return -1; X if((status = SRmodesense10(rp, 0x3F, list, sizeof(list))) == -1) X return -1; X if(((list[6]<<8)|list[3]) < 8) X rp->lbsize = 2048; X else X rp->lbsize = (list[13]<<8)|(list[14]<<8)|list[15]; X return status; X} int SRopenraw(ScsiReq *rp, char *unit) X{ X char name[128]; X if(rp->flags & Fopen){ X rp->status = Status_BADARG; X return -1; X } X memset(rp, 0, sizeof(*rp)); X rp->unit = unit; X sprint(name, "%s/raw", unit); X if((rp->fd = open(name, ORDWR)) == -1){ X rp->status = STtimeout; X return -1; X } X rp->flags = Fopen; X return 0; X} int SRopen(ScsiReq *rp, char *unit) X{ X if(SRopenraw(rp, unit) == -1) X return -1; X SRready(rp); X if(SRinquiry(rp) >= 0){ X switch(rp->inquiry[0]){ X default: X fprint(2, "unknown device type 0x%.2x\n", rp->inquiry[0]); X rp->status = Status_SW; X break; X case 0x00: /* Direct access (disk) */ X case 0x05: /* CD-ROM */ X case 0x07: /* rewriteable MO */ X if(dirdevopen(rp) == -1) X break; X return 0; X case 0x01: /* Sequential eg: tape */ X rp->flags |= Fseqdev; X if(seqdevopen(rp) == -1) X break; X return 0; X case 0x02: /* Printer */ X rp->flags |= Fprintdev; X return 0; X case 0x04: /* Worm */ X rp->flags |= Fwormdev; X if(wormdevopen(rp) == -1) X break; X return 0; X case 0x08: /* medium-changer */ X rp->flags |= Fchanger; X return 0; X } X } X SRclose(rp); X return -1; X} ! echo scuzz.mine/scsireq.h sed 's/^X//' >scuzz.mine/scsireq.h <<'!' enum { /* fundamental constants/defaults */ X NTargetID = 8, /* number of target IDs */ X CtlrID = 7, /* default controller target ID */ X MaxDirData = 255, /* max. direct data returned */ X /* X * default & maximum `maximum i/o size'; overridden by -m. X * was 32*512 then 96*1024. 240K is exabyte maximum block size. X */ X MaxIOsize = 240*1024, X LBsize = 512, /* default logical-block size */ X}; typedef struct { X uchar *p; X long count; X uchar write; X} ScsiPtr; typedef struct { X int flags; X char *unit; /* unit directory */ X int lun; X ulong lbsize; X ulong offset; X int fd; X ScsiPtr cmd; X ScsiPtr data; X int status; /* returned status */ X uchar sense[MaxDirData]; /* returned sense data */ X uchar inquiry[MaxDirData]; /* returned inquiry data */ X} ScsiReq; enum { /* flags */ X Fopen = 0x0001, /* open */ X Fseqdev = 0x0002, /* sequential-access device */ X Fwritten = 0x0004, /* device written */ X Fronly = 0x0008, /* device is read-only */ X Fwormdev = 0x0010, /* write-once read-multiple device */ X Fprintdev = 0x0020, /* printer */ X Fbfixed = 0x0040, /* fixed block size */ X Fchanger = 0x0080, /* medium-changer device */ X Finqok = 0x0100, /* inquiry data is OK */ X Fmode6 = 0x0200, /* use 6-byte modeselect */ X Frw10 = 0x0400, /* use 10-byte read/write */ X}; enum { X STnomem =-4, /* buffer allocation failed */ X STharderr =-3, /* controller error of some kind */ X STtimeout =-2, /* bus timeout */ X STok = 0, /* good */ X STcheck = 0x02, /* check condition */ X STcondmet = 0x04, /* condition met/good */ X STbusy = 0x08, /* busy */ X STintok = 0x10, /* intermediate/good */ X STintcondmet = 0x14, /* intermediate/condition met/good */ X STresconf = 0x18, /* reservation conflict */ X STterminated = 0x22, /* command terminated */ X STqfull = 0x28, /* queue full */ X}; enum { /* status */ X Status_SD = 0x80, /* sense-data available */ X Status_SW = 0x83, /* internal software error */ X Status_BADARG = 0x84, /* bad argument to request */ X Status_RO = 0x85, /* device is read-only */ X}; enum { /* SCSI command codes */ X ScmdTur = 0x00, /* test unit ready */ X ScmdRewind = 0x01, /* rezero/rewind */ X ScmdRsense = 0x03, /* request sense */ X ScmdFormat = 0x04, /* format unit */ X ScmdRblimits = 0x05, /* read block limits */ X ScmdRead = 0x08, /* read */ X ScmdWrite = 0x0A, /* write */ X ScmdSeek = 0x0B, /* seek */ X ScmdFmark = 0x10, /* write filemarks */ X ScmdSpace = 0x11, /* space forward/backward */ X ScmdInq = 0x12, /* inquiry */ X ScmdMselect6 = 0x15, /* mode select */ X ScmdMselect10 = 0x55, /* mode select */ X ScmdMsense6 = 0x1A, /* mode sense */ X ScmdMsense10 = 0x5A, /* mode sense */ X ScmdStart = 0x1B, /* start/stop unit */ X ScmdRcapacity = 0x25, /* read capacity */ X ScmdExtread = 0x28, /* extended read */ X ScmdExtwrite = 0x2A, /* extended write */ X ScmdExtseek = 0x2B, /* extended seek */ X ScmdSynccache = 0x35, /* flush cache */ X ScmdRTOC = 0x43, /* read TOC data */ X ScmdRdiscinfo = 0x51, /* read disc information */ X ScmdRtrackinfo = 0x52, /* read track information */ X ScmdBlank = 0xA1, /* blank CD-RW media */ X ScmdCDpause = 0x4B, /* pause/resume */ X ScmdCDstop = 0x4E, /* stop play/scan */ X ScmdCDplay = 0xA5, /* play audio */ X ScmdCDload = 0xA6, /* load/unload */ X ScmdCDscan = 0xBA, /* fast forward/reverse */ X ScmdCDstatus = 0xBD, /* mechanism status */ X Scmdgetconf = 0x46, /* get configuration */ X ScmdFwaddr = 0xE2, /* first writeable address */ X ScmdTreserve = 0xE4, /* reserve track */ X ScmdTinfo = 0xE5, /* read track info */ X ScmdTwrite = 0xE6, /* write track */ X ScmdMload = 0xE7, /* medium load/unload */ X ScmdFixation = 0xE9, /* fixation */ X ScmdEInitialise = 0x07, /* initialise element status */ X ScmdMMove = 0xA5, /* move medium */ X ScmdEStatus = 0xB8, /* read element status */ X ScmdMExchange = 0xA6, /* exchange medium */ X ScmdEposition = 0x2B, /* position to element */ X ScmdReadDVD = 0xAD, /* read dvd structure */ X ScmdReportKey = 0xA4, /* read dvd key */ X ScmdSendKey = 0xA3, /* write dvd key */ X}; extern long SRready(ScsiReq*); extern long SRrewind(ScsiReq*); extern long SRreqsense(ScsiReq*); extern long SRformat(ScsiReq*); extern long SRrblimits(ScsiReq*, uchar*); extern long SRread(ScsiReq*, void*, long); extern long SRwrite(ScsiReq*, void*, long); extern long SRseek(ScsiReq*, long, int); extern long SRfilemark(ScsiReq*, ulong); extern long SRspace(ScsiReq*, uchar, long); extern long SRinquiry(ScsiReq*); extern long SRmodeselect6(ScsiReq*, uchar*, long); extern long SRmodeselect10(ScsiReq*, uchar*, long); extern long SRmodesense6(ScsiReq*, uchar, uchar*, long); extern long SRmodesense10(ScsiReq*, uchar, uchar*, long); extern long SRstart(ScsiReq*, uchar); extern long SRrcapacity(ScsiReq*, uchar*); extern long SRblank(ScsiReq*, uchar, uchar); /* MMC CD-R/CD-RW commands */ extern long SRsynccache(ScsiReq*); extern long SRTOC(ScsiReq*, void*, int, uchar, uchar); extern long SRrdiscinfo(ScsiReq*, void*, int); extern long SRrtrackinfo(ScsiReq*, void*, int, int); extern long SRcdpause(ScsiReq*, int); /* MMC CD audio commands */ extern long SRcdstop(ScsiReq*); extern long SRcdload(ScsiReq*, int, int); extern long SRcdplay(ScsiReq*, int, long, long); extern long SRcdstatus(ScsiReq*, uchar*, int); extern long SRgetconf(ScsiReq*, uchar*, int); extern long SRfwaddr(ScsiReq*, uchar, uchar, uchar, uchar*); /* old CD-R/CD-RW commands */ extern long SRtreserve(ScsiReq*, long); extern long SRtinfo(ScsiReq*, uchar, uchar*); extern long SRwtrack(ScsiReq*, void*, long, uchar, uchar); extern long SRmload(ScsiReq*, uchar); extern long SRfixation(ScsiReq*, uchar); extern long SReinitialise(ScsiReq*); /* CHANGER commands */ extern long SRestatus(ScsiReq*, uchar, uchar*, int); extern long SRmmove(ScsiReq*, int, int, int, int); extern long SRdvdread(ScsiReq*, uchar*, int, ulong, int); /* DVD commands */ extern long SRdvdreportkey(ScsiReq*, uchar*, int, ulong, int); extern long SRdvdsendkey(ScsiReq*, uchar*, int, int); extern long SRrequest(ScsiReq*); extern int SRclose(ScsiReq*); extern int SRopenraw(ScsiReq*, char*); extern int SRopen(ScsiReq*, char*); extern void makesense(ScsiReq*); ! echo scuzz.mine/scuzz.c sed 's/^X//' >scuzz.mine/scuzz.c <<'!' X#include X#include X#include X#include "scsireq.h" X#define MIN(a, b) ((a) < (b) ? (a): (b)) Biobuf bin, bout; static char rwbuf[MaxIOsize]; static int verbose = 1; long maxiosize = MaxIOsize; typedef struct { X char *name; X long (*f)(ScsiReq *, int, char *[]); X int open; X char *help; X} ScsiCmd; static ScsiCmd scsicmd[]; static long cmdready(ScsiReq *rp, int argc, char *argv[]) X{ X USED(argc, argv); X return SRready(rp); X} static long cmdrewind(ScsiReq *rp, int argc, char *argv[]) X{ X USED(argc, argv); X return SRrewind(rp); X} static long cmdreqsense(ScsiReq *rp, int argc, char *argv[]) X{ X long nbytes; X USED(argc, argv); X if((nbytes = SRreqsense(rp)) != -1) X makesense(rp); X return nbytes; X} static long cmdformat(ScsiReq *rp, int argc, char *argv[]) X{ X USED(argc, argv); X return SRformat(rp); X} static long cmdrblimits(ScsiReq *rp, int argc, char *argv[]) X{ X uchar l[6]; X long n; X USED(argc, argv); X if((n = SRrblimits(rp, l)) == -1) X return -1; X Bprint(&bout, " %2.2uX %2.2uX %2.2uX %2.2uX %2.2uX %2.2uX\n", X l[0], l[1], l[2], l[3], l[4], l[5]); X return n; X} static int mkfile(char *file, int omode, int *pid) X{ X int fd[2]; X if(*file != '|'){ X *pid = -1; X if(omode == OWRITE) X return create(file, OWRITE, 0666); X else if(omode == OREAD) X return open(file, OREAD); X return -1; X } X file++; X if(*file == 0 || pipe(fd) == -1) X return -1; X if((*pid = fork()) == -1){ X close(fd[0]); X close(fd[1]); X return -1; X } X if(*pid == 0){ X switch(omode){ X case OREAD: X dup(fd[0], 1); X break; X case OWRITE: X dup(fd[0], 0); X break; X } X close(fd[0]); X close(fd[1]); X execl("/bin/rc", "rc", "-c", file, nil); X exits("exec"); X } X close(fd[0]); X return fd[1]; X} int waitfor(int pid) X{ X int msg; X Waitmsg *w; X while((w = wait()) != nil){ X if(w->pid != pid){ X free(w); X continue; X } X msg = (w->msg[0] != '\0'); X free(w); X return msg; X } X return -1; X} static long cmdread(ScsiReq *rp, int argc, char *argv[]) X{ X long n, iosize, prevsize = 0; X vlong nbytes, total; X int fd, pid; X char *p; X iosize = maxiosize; X nbytes = ~0ULL >> 1; X switch(argc){ X default: X rp->status = Status_BADARG; X return -1; X case 2: X nbytes = strtoll(argv[1], &p, 0); X if(nbytes == 0 && p == argv[1]){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 1: X if((fd = mkfile(argv[0], OWRITE, &pid)) == -1){ X rp->status = Status_BADARG; X return -1; X } X break; X } X print("bsize=%lud\n", rp->lbsize); X total = 0; X while(nbytes){ X n = MIN(nbytes, iosize); X if((n = SRread(rp, rwbuf, n)) == -1){ X if(total == 0) X total = -1; X break; X } X if (n == 0) X break; X if (prevsize != n) { X print("tape block size=%ld\n", n); X prevsize = n; X } X if(write(fd, rwbuf, n) != n){ X if(total == 0) X total = -1; X if(rp->status == STok) X rp->status = Status_SW; X break; X } X nbytes -= n; X total += n; X } X close(fd); X if(pid >= 0 && waitfor(pid)){ X rp->status = Status_SW; X return -1; X } X return total; X} static long cmdwrite(ScsiReq *rp, int argc, char *argv[]) X{ X long n, prevsize = 0; X vlong nbytes, total; X int fd, pid; X char *p; X nbytes = ~0ULL >> 1; X switch(argc){ X default: X rp->status = Status_BADARG; X return -1; X case 2: X nbytes = strtoll(argv[1], &p, 0); X if(nbytes == 0 && p == argv[1]){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 1: X if((fd = mkfile(argv[0], OREAD, &pid)) == -1){ X rp->status = Status_BADARG; X return -1; X } X break; X } X total = 0; X while(nbytes){ X n = MIN(nbytes, maxiosize); X if((n = read(fd, rwbuf, n)) == -1){ X if(total == 0) X total = -1; X break; X } X if (n == 0) X break; X if (prevsize != n) { X print("tape block size=%ld\n", n); X prevsize = n; X } X if(SRwrite(rp, rwbuf, n) != n){ X if(total == 0) X total = -1; X if(rp->status == STok) X rp->status = Status_SW; X break; X } X nbytes -= n; X total += n; X } X close(fd); X if(pid >= 0 && waitfor(pid)){ X rp->status = Status_SW; X return -1; X } X return total; X} static long cmdseek(ScsiReq *rp, int argc, char *argv[]) X{ X char *p; X long offset; X int type; X type = 0; X switch(argc){ X default: X rp->status = Status_BADARG; X return -1; X case 2: X if((type = strtol(argv[1], &p, 0)) == 0 && p == argv[1]){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 1: X if((offset = strtol(argv[0], &p, 0)) == 0 && p == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X break; X } X return SRseek(rp, offset, type); X} static long cmdfilemark(ScsiReq *rp, int argc, char *argv[]) X{ X char *p; X ulong howmany; X howmany = 1; X if(argc && (howmany = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X return SRfilemark(rp, howmany); X} static long cmdspace(ScsiReq *rp, int argc, char *argv[]) X{ X uchar code; X long howmany; X char option, *p; X code = 0x00; X howmany = 1; X while(argc && (*argv)[0] == '-'){ X while(option = *++argv[0]){ X switch(option){ X case '-': X break; X case 'b': X code = 0x00; X break; X case 'f': X code = 0x01; X break; X default: X rp->status = Status_BADARG; X return -1; X } X break; X } X argc--; argv++; X if(option == '-') X break; X } X if(argc && ((howmany = strtol(argv[0], &p, 0)) == 0 && p == argv[0])){ X rp->status = Status_BADARG; X return -1; X } X return SRspace(rp, code, howmany); X} static long cmdinquiry(ScsiReq *rp, int argc, char *argv[]) X{ X long status; X int i, n; X uchar *p; X USED(argc, argv); X if((status = SRinquiry(rp)) != -1){ X n = rp->inquiry[4]+4; X for(i = 0; i < MIN(8, n); i++) X Bprint(&bout, " %2.2uX", rp->inquiry[i]); X p = &rp->inquiry[8]; X n = MIN(n, sizeof(rp->inquiry)-8); X while(n && (*p == ' ' || *p == '\t' || *p == '\n')){ X n--; X p++; X } X Bprint(&bout, "\t%.*s\n", n, (char*)p); X } X return status; X} static long cmdmodeselect6(ScsiReq *rp, int argc, char *argv[]) X{ X uchar list[MaxDirData]; X long nbytes, ul; X char *p; X memset(list, 0, sizeof(list)); X for(nbytes = 0; argc; argc--, argv++, nbytes++){ X if((ul = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X list[nbytes] = ul; X } X if(!(rp->flags & Finqok) && SRinquiry(rp) == -1) X Bprint(&bout, "warning: couldn't determine whether SCSI-1/SCSI-2 mode"); X return SRmodeselect6(rp, list, nbytes); X} static long cmdmodeselect10(ScsiReq *rp, int argc, char *argv[]) X{ X uchar list[MaxDirData]; X long nbytes, ul; X char *p; X memset(list, 0, sizeof(list)); X for(nbytes = 0; argc; argc--, argv++, nbytes++){ X if((ul = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X list[nbytes] = ul; X } X if(!(rp->flags & Finqok) && SRinquiry(rp) == -1) X Bprint(&bout, "warning: couldn't determine whether SCSI-1/SCSI-2 mode"); X return SRmodeselect10(rp, list, nbytes); X} static long cmdmodesense6(ScsiReq *rp, int argc, char *argv[]) X{ X uchar list[MaxDirData], *lp, page; X long i, n, nbytes, status; X char *p; X nbytes = sizeof(list); X switch(argc){ X default: X rp->status = Status_BADARG; X return -1; X case 2: X if((nbytes = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 1: X if((page = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X break; X case 0: X page = 0x3F; X break; X } X if((status = SRmodesense6(rp, page, list, nbytes)) == -1) X return -1; X lp = list; X nbytes = list[0]; X Bprint(&bout, " Header\n "); X for(i = 0; i < 4; i++){ /* header */ X Bprint(&bout, " %2.2uX", *lp); X lp++; X } X Bputc(&bout, '\n'); X if(list[3]){ /* block descriptors */ X for(n = 0; n < list[3]/8; n++){ X Bprint(&bout, " Block %ld\n ", n); X for(i = 0; i < 8; i++) X Bprint(&bout, " %2.2uX", lp[i]); X Bprint(&bout, " (density %2.2uX", lp[0]); X Bprint(&bout, " blocks %d", (lp[1]<<16)|(lp[2]<<8)|lp[3]); X Bprint(&bout, " length %d)", (lp[5]<<16)|(lp[6]<<8)|lp[7]); X lp += 8; X nbytes -= 8; X Bputc(&bout, '\n'); X } X } X while(nbytes > 0){ /* pages */ X i = *(lp+1); X nbytes -= i+2; X Bprint(&bout, " Page %2.2uX %d\n ", *lp & 0x3F, *(lp+1)); X lp += 2; X for(n = 0; n < i; n++){ X if(n && ((n & 0x0F) == 0)) X Bprint(&bout, "\n "); X Bprint(&bout, " %2.2uX", *lp); X lp++; X } X if(n && (n & 0x0F)) X Bputc(&bout, '\n'); X } X return status; X} static long cmdmodesense10(ScsiReq *rp, int argc, char *argv[]) X{ X uchar *list, *lp, page; X long blen, i, n, nbytes, status; X char *p; X nbytes = MaxDirData; X switch(argc){ X default: X rp->status = Status_BADARG; X return -1; X case 2: X if((nbytes = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 1: X if((page = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X break; X case 0: X page = 0x3F; X break; X } X list = malloc(nbytes); X if(list == 0){ X rp->status = STnomem; X return -1; X } X if((status = SRmodesense10(rp, page, list, nbytes)) == -1) X return -1; X lp = list; X nbytes = ((list[0]<<8)|list[1]); X Bprint(&bout, " Header\n "); X for(i = 0; i < 8; i++){ /* header */ X Bprint(&bout, " %2.2uX", *lp); X lp++; X } X Bputc(&bout, '\n'); X blen = (list[6]<<8)|list[7]; X if(blen){ /* block descriptors */ X for(n = 0; n < blen/8; n++){ X Bprint(&bout, " Block %ld\n ", n); X for(i = 0; i < 8; i++) X Bprint(&bout, " %2.2uX", lp[i]); X Bprint(&bout, " (density %2.2uX", lp[0]); X Bprint(&bout, " blocks %d", (lp[1]<<16)|(lp[2]<<8)|lp[3]); X Bprint(&bout, " length %d)", (lp[5]<<16)|(lp[6]<<8)|lp[7]); X lp += 8; X nbytes -= 8; X Bputc(&bout, '\n'); X } X } X /* X * Special for ATA drives, page 0 is the drive info in 16-bit X * chunks, little-endian, 256 in total. No decoding for now. X */ X if(page == 0){ X for(n = 0; n < nbytes; n += 2){ X if(n && ((n & 0x1F) == 0)) X Bprint(&bout, "\n"); X Bprint(&bout, " %4.4uX", (*(lp+1)<<8)|*lp); X lp += 2; X } X Bputc(&bout, '\n'); X } X else while(nbytes > 0){ /* pages */ X i = *(lp+1); X nbytes -= i+2; X Bprint(&bout, " Page %2.2uX %d\n ", *lp & 0x3F, *(lp+1)); X lp += 2; X for(n = 0; n < i; n++){ X if(n && ((n & 0x0F) == 0)) X Bprint(&bout, "\n "); X Bprint(&bout, " %2.2uX", *lp); X lp++; X } X if(n && (n & 0x0F)) X Bputc(&bout, '\n'); X } X free(list); X return status; X} static long start(ScsiReq *rp, int argc, char *argv[], uchar code) X{ X char *p; X if(argc && (code = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X return SRstart(rp, code); X} static long cmdstart(ScsiReq *rp, int argc, char *argv[]) X{ X return start(rp, argc, argv, 1); X} static long cmdstop(ScsiReq *rp, int argc, char *argv[]) X{ X return start(rp, argc, argv, 0); X} static long cmdeject(ScsiReq *rp, int argc, char *argv[]) X{ X return start(rp, argc, argv, 2); X} static long cmdingest(ScsiReq *rp, int argc, char *argv[]) X{ X return start(rp, argc, argv, 3); X} static long cmdcapacity(ScsiReq *rp, int argc, char *argv[]) X{ X uchar d[8]; X long n; X USED(argc, argv); X if((n = SRrcapacity(rp, d)) == -1) X return -1; X Bprint(&bout, " %ud %ud\n", X d[0]<<24|d[1]<<16|d[2]<<8|d[3], X d[4]<<24|d[5]<<16|d[6]<<8|d[7]); X return n; X} static long cmdblank(ScsiReq *rp, int argc, char *argv[]) X{ X uchar type, track; X char *sp; X type = track = 0; X switch(argc){ X default: X rp->status = Status_BADARG; X return -1; X case 2: X if((type = strtoul(argv[1], &sp, 0)) == 0 && sp == argv[1]){ X rp->status = Status_BADARG; X return -1; X } X if(type < 0 || type > 6){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 1: X if((track = strtoul(argv[0], &sp, 0)) == 0 && sp == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 0: X break; X } X return SRblank(rp, type, track); X} static long cmdsynccache(ScsiReq *rp, int argc, char *argv[]) X{ X USED(argc, argv); X return SRsynccache(rp); X} static long cmdrtoc(ScsiReq *rp, int argc, char *argv[]) X{ X uchar d[100*8+4], format, track, *p; X char *sp; X long n, nbytes; X int tdl; X format = track = 0; X switch(argc){ X default: X rp->status = Status_BADARG; X return -1; X case 2: X if((format = strtoul(argv[1], &sp, 0)) == 0 && sp == argv[1]){ X rp->status = Status_BADARG; X return -1; X } X if(format < 0 || format > 4){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 1: X if((track = strtoul(argv[0], &sp, 0)) == 0 && sp == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 0: X break; X } X if((nbytes = SRTOC(rp, d, sizeof(d), format, track)) == -1){ X if(rp->status == STok) X Bprint(&bout, "\t(probably empty)\n"); X return -1; X } X tdl = (d[0]<<8)|d[1]; X switch(format){ X case 0: X Bprint(&bout, "\ttoc/pma data length: 0x%uX\n", tdl); X Bprint(&bout, "\tfirst track number: %d\n", d[2]); X Bprint(&bout, "\tlast track number: %d\n", d[3]); X for(p = &d[4], n = tdl-2; n; n -= 8, p += 8){ X Bprint(&bout, "\ttrack number: 0x%2.2uX\n", p[2]); X Bprint(&bout, "\t\tcontrol: 0x%2.2uX\n", p[1] & 0x0F); X Bprint(&bout, "\t\tblock address: 0x%uX\n", X (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7]); X } X break; X case 1: X Bprint(&bout, "\tsessions data length: 0x%uX\n", tdl); X Bprint(&bout, "\tnumber of finished sessions: %d\n", d[2]); X Bprint(&bout, "\tunfinished session number: %d\n", d[3]); X for(p = &d[4], n = tdl-2; n; n -= 8, p += 8){ X Bprint(&bout, "\tsession number: 0x%2.2uX\n", p[0]); X Bprint(&bout, "\t\tfirst track number in session: 0x%2.2uX\n", X p[2]); X Bprint(&bout, "\t\tlogical start address: 0x%uX\n", X (p[5]<<16)|(p[6]<<8)|p[7]); X } X break; X case 2: X Bprint(&bout, "\tfull TOC data length: 0x%uX\n", tdl); X Bprint(&bout, "\tnumber of finished sessions: %d\n", d[2]); X Bprint(&bout, "\tunfinished session number: %d\n", d[3]); X for(p = &d[4], n = tdl-2; n > 0; n -= 11, p += 11){ X Bprint(&bout, "\tsession number: 0x%2.2uX\n", p[0]); X Bprint(&bout, "\t\tcontrol: 0x%2.2uX\n", p[1] & 0x0F); X Bprint(&bout, "\t\tADR: 0x%2.2uX\n", (p[1]>>4) & 0x0F); X Bprint(&bout, "\t\tTNO: 0x%2.2uX\n", p[2]); X Bprint(&bout, "\t\tPOINT: 0x%2.2uX\n", p[3]); X Bprint(&bout, "\t\tMin: 0x%2.2uX\n", p[4]); X Bprint(&bout, "\t\tSec: 0x%2.2uX\n", p[5]); X Bprint(&bout, "\t\tFrame: 0x%2.2uX\n", p[6]); X Bprint(&bout, "\t\tZero: 0x%2.2uX\n", p[7]); X Bprint(&bout, "\t\tPMIN: 0x%2.2uX\n", p[8]); X Bprint(&bout, "\t\tPSEC: 0x%2.2uX\n", p[9]); X Bprint(&bout, "\t\tPFRAME: 0x%2.2uX\n", p[10]); X } X break; X case 3: X Bprint(&bout, "\tPMA data length: 0x%uX\n", tdl); X for(p = &d[4], n = tdl-2; n > 0; n -= 11, p += 11){ X Bprint(&bout, "\t\tcontrol: 0x%2.2uX\n", p[1] & 0x0F); X Bprint(&bout, "\t\tADR: 0x%2.2uX\n", (p[1]>>4) & 0x0F); X Bprint(&bout, "\t\tTNO: 0x%2.2uX\n", p[2]); X Bprint(&bout, "\t\tPOINT: 0x%2.2uX\n", p[3]); X Bprint(&bout, "\t\tMin: 0x%2.2uX\n", p[4]); X Bprint(&bout, "\t\tSec: 0x%2.2uX\n", p[5]); X Bprint(&bout, "\t\tFrame: 0x%2.2uX\n", p[6]); X Bprint(&bout, "\t\tZero: 0x%2.2uX\n", p[7]); X Bprint(&bout, "\t\tPMIN: 0x%2.2uX\n", p[8]); X Bprint(&bout, "\t\tPSEC: 0x%2.2uX\n", p[9]); X Bprint(&bout, "\t\tPFRAME: 0x%2.2uX\n", p[10]); X } X break; X case 4: X Bprint(&bout, "\tATIP data length: 0x%uX\n", tdl); X break; X } X for(n = 0; n < nbytes; n++){ X if(n && ((n & 0x0F) == 0)) X Bprint(&bout, "\n"); X Bprint(&bout, " %2.2uX", d[n]); X } X if(n && (n & 0x0F)) X Bputc(&bout, '\n'); X return nbytes; X} static long cmdrdiscinfo(ScsiReq *rp, int argc, char*[]) X{ X uchar d[MaxDirData]; X int dl; X long n, nbytes; X switch(argc){ X default: X rp->status = Status_BADARG; X return -1; X case 0: X break; X } X if((nbytes = SRrdiscinfo(rp, d, sizeof(d))) == -1) X return -1; X dl = (d[0]<<8)|d[1]; X Bprint(&bout, "\tdata length: 0x%uX\n", dl); X Bprint(&bout, "\tinfo[2] 0x%2.2uX\n", d[2]); X switch(d[2] & 0x03){ X case 0: X Bprint(&bout, "\t\tEmpty\n"); X break; X case 1: X Bprint(&bout, "\t\tIncomplete disc (Appendable)\n"); X break; X case 2: X Bprint(&bout, "\t\tComplete (CD-ROM or last session is closed and has no next session pointer)\n"); X break; X case 3: X Bprint(&bout, "\t\tReserved\n"); X break; X } X switch((d[2]>>2) & 0x03){ X case 0: X Bprint(&bout, "\t\tEmpty Session\n"); X break; X case 1: X Bprint(&bout, "\t\tIncomplete Session\n"); X break; X case 2: X Bprint(&bout, "\t\tReserved\n"); X break; X case 3: X Bprint(&bout, "\t\tComplete Session (only possible when disc Status is Complete)\n"); X break; X } X if(d[2] & 0x10) X Bprint(&bout, "\t\tErasable\n"); X Bprint(&bout, "\tNumber of First Track on Disc %ud\n", d[3]); X Bprint(&bout, "\tNumber of Sessions %ud\n", d[4]); X Bprint(&bout, "\tFirst Track Number in Last Session %ud\n", d[5]); X Bprint(&bout, "\tLast Track Number in Last Session %ud\n", d[6]); X Bprint(&bout, "\tinfo[7] 0x%2.2uX\n", d[7]); X if(d[7] & 0x20) X Bprint(&bout, "\t\tUnrestricted Use Disc\n"); X if(d[7] & 0x40) X Bprint(&bout, "\t\tDisc Bar Code Valid\n"); X if(d[7] & 0x80) X Bprint(&bout, "\t\tDisc ID Valid\n"); X Bprint(&bout, "\tinfo[8] 0x%2.2uX\n", d[8]); X switch(d[8]){ X case 0x00: X Bprint(&bout, "\t\tCD-DA or CD-ROM Disc\n"); X break; X case 0x10: X Bprint(&bout, "\t\tCD-I Disc\n"); X break; X case 0x20: X Bprint(&bout, "\t\tCD-ROM XA Disc\n"); X break; X case 0xFF: X Bprint(&bout, "\t\tUndefined\n"); X break; X default: X Bprint(&bout, "\t\tReserved\n"); X break; X } X Bprint(&bout, "\tLast Session lead-in Start Time M/S/F: 0x%2.2uX/0x%2.2uX/0x%2.2uX\n", X d[17], d[18], d[19]); X Bprint(&bout, "\tLast Possible Start Time for Start of lead-out M/S/F: 0x%2.2uX/0x%2.2uX/0x%2.2uX\n", X d[21], d[22], d[23]); X for(n = 0; n < nbytes; n++){ X if(n && ((n & 0x0F) == 0)) X Bprint(&bout, "\n"); X Bprint(&bout, " %2.2uX", d[n]); X } X if(n && (n & 0x0F)) X Bputc(&bout, '\n'); X return nbytes; X} static long cmdrtrackinfo(ScsiReq *rp, int argc, char *argv[]) X{ X uchar d[MaxDirData], track; X char *sp; X long n, nbytes; X int dl; X track = 0; X switch(argc){ X default: X rp->status = Status_BADARG; X return -1; X case 1: X if((track = strtoul(argv[0], &sp, 0)) == 0 && sp == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 0: X break; X } X if((nbytes = SRrtrackinfo(rp, d, sizeof(d), track)) == -1) X return -1; X dl = (d[0]<<8)|d[1]; X Bprint(&bout, "\tdata length: 0x%uX\n", dl); X Bprint(&bout, "\Track Number %d\n", d[2]); X Bprint(&bout, "\Session Number %d\n", d[3]); X Bprint(&bout, "\tinfo[4] 0x%2.2uX\n", d[5]); X Bprint(&bout, "\t\tTrack Mode 0x%2.2uX: ", d[5] & 0x0F); X switch(d[5] & 0x0F){ X case 0x00: X case 0x02: X Bprint(&bout, "2 audio channels without pre-emphasis\n"); X break; X case 0x01: X case 0x03: X Bprint(&bout, "2 audio channels with pre-emphasis of 50/15µs\n"); X break; X case 0x08: X case 0x0A: X Bprint(&bout, "audio channels without pre-emphasis (reserved in CD-R/RW)\n"); X break; X case 0x09: X case 0x0B: X Bprint(&bout, "audio channels with pre-emphasis of 50/15µs (reserved in CD-R/RW)\n"); X break; X case 0x04: X case 0x06: X Bprint(&bout, "Data track, recorded uninterrupted\n"); X break; X case 0x05: X case 0x07: X Bprint(&bout, "Data track, recorded incremental\n"); X break; X default: X Bprint(&bout, "(mode unknown)\n"); X break; X } X if(d[5] & 0x10) X Bprint(&bout, "\t\tCopy\n"); X if(d[5] & 0x20) X Bprint(&bout, "\t\tDamage\n"); X Bprint(&bout, "\tinfo[6] 0x%2.2uX\n", d[6]); X Bprint(&bout, "\t\tData Mode 0x%2.2uX: ", d[6] & 0x0F); X switch(d[6] & 0x0F){ X case 0x01: X Bprint(&bout, "Mode 1 (ISO/IEC 10149)\n"); X break; X case 0x02: X Bprint(&bout, "Mode 2 (ISO/IEC 10149 or CD-ROM XA)\n"); X break; X case 0x0F: X Bprint(&bout, "Data Block Type unknown (no track descriptor block)\n"); X break; X default: X Bprint(&bout, "(Reserved)\n"); X break; X } X if(d[6] & 0x10) X Bprint(&bout, "\t\tFP\n"); X if(d[6] & 0x20) X Bprint(&bout, "\t\tPacket\n"); X if(d[6] & 0x40) X Bprint(&bout, "\t\tBlank\n"); X if(d[6] & 0x80) X Bprint(&bout, "\t\tRT\n"); X Bprint(&bout, "\tTrack Start Address 0x%8.8uX\n", X (d[8]<<24)|(d[9]<<16)|(d[10]<<8)|d[11]); X if(d[7] & 0x01) X Bprint(&bout, "\tNext Writeable Address 0x%8.8uX\n", X (d[12]<<24)|(d[13]<<16)|(d[14]<<8)|d[15]); X Bprint(&bout, "\tFree Blocks 0x%8.8uX\n", X (d[16]<<24)|(d[17]<<16)|(d[18]<<8)|d[19]); X if((d[6] & 0x30) == 0x30) X Bprint(&bout, "\tFixed Packet Size 0x%8.8uX\n", X (d[20]<<24)|(d[21]<<16)|(d[22]<<8)|d[23]); X Bprint(&bout, "\tTrack Size 0x%8.8uX\n", X (d[24]<<24)|(d[25]<<16)|(d[26]<<8)|d[27]); X for(n = 0; n < nbytes; n++){ X if(n && ((n & 0x0F) == 0)) X Bprint(&bout, "\n"); X Bprint(&bout, " %2.2uX", d[n]); X } X if(n && (n & 0x0F)) X Bputc(&bout, '\n'); X return nbytes; X} static long cmdcdpause(ScsiReq *rp, int argc, char *argv[]) X{ X USED(argc, argv); X return SRcdpause(rp, 0); X} static long cmdcdresume(ScsiReq *rp, int argc, char *argv[]) X{ X USED(argc, argv); X return SRcdpause(rp, 1); X} static long cmdcdstop(ScsiReq *rp, int argc, char *argv[]) X{ X USED(argc, argv); X return SRcdstop(rp); X} static long cmdcdplay(ScsiReq *rp, int argc, char *argv[]) X{ X long length, start; X char *sp; X int raw; X raw = 0; X start = 0; X if(argc && strcmp("-r", argv[0]) == 0){ X raw = 1; X argc--, argv++; X } X length = 0xFFFFFFFF; X switch(argc){ X default: X rp->status = Status_BADARG; X return -1; X case 2: X if(!raw || ((length = strtol(argv[1], &sp, 0)) == 0 && sp == argv[1])){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 1: X if((start = strtol(argv[0], &sp, 0)) == 0 && sp == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 0: X break; X } X return SRcdplay(rp, raw, start, length); X} static long cmdcdload(ScsiReq *rp, int argc, char *argv[]) X{ X char *p; X ulong slot; X slot = 0; X if(argc && (slot = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X return SRcdload(rp, 1, slot); X} static long cmdcdunload(ScsiReq *rp, int argc, char *argv[]) X{ X char *p; X ulong slot; X slot = 0; X if(argc && (slot = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X return SRcdload(rp, 0, slot); X} static long cmdcdstatus(ScsiReq *rp, int argc, char *argv[]) X{ X uchar *list, *lp; X long nbytes, status; X int i, slots; X USED(argc, argv); X nbytes = 4096; X list = malloc(nbytes); X if(list == 0){ X rp->status = STnomem; X return -1; X } X status = SRcdstatus(rp, list, nbytes); X if(status == -1){ X free(list); X return -1; X } X lp = list; X Bprint(&bout, " Header\n "); X for(i = 0; i < 8; i++){ /* header */ X Bprint(&bout, " %2.2uX", *lp); X lp++; X } X Bputc(&bout, '\n'); X slots = ((list[6]<<8)|list[7])/4; X Bprint(&bout, " Slots\n "); X while(slots--){ X Bprint(&bout, " %2.2uX %2.2uX %2.2uX %2.2uX\n ", X *lp, *(lp+1), *(lp+2), *(lp+3)); X lp += 4; X } X free(list); X return status; X} static long cmdgetconf(ScsiReq *rp, int argc, char *argv[]) X{ X uchar *list; X long nbytes, status; X USED(argc, argv); X nbytes = 4096; X list = malloc(nbytes); X if(list == 0){ X rp->status = STnomem; X return -1; X } X status = SRgetconf(rp, list, nbytes); X if(status == -1){ X free(list); X return -1; X } X /* to be done... */ X free(list); X return status; X} static long cmdfwaddr(ScsiReq *rp, int argc, char *argv[]) X{ X uchar d[MaxDirData], npa, track, mode; X long n; X char *p; X npa = mode = track = 0; X switch(argc){ X default: X rp->status = Status_BADARG; X return -1; X case 3: X if((npa = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 2: X if((mode = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 1: X if((track = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X break; X case 0: X break; X } X if((n = SRfwaddr(rp, track, mode, npa, d)) == -1) X return -1; X Bprint(&bout, "%ud %ud\n", d[0], (d[1]<<24)|(d[2]<<16)|(d[3]<<8)|d[4]); X return n; X} static long cmdtreserve(ScsiReq *rp, int argc, char *argv[]) X{ X long nbytes; X char *p; X if(argc != 1 || ((nbytes = strtoul(argv[0], &p, 0)) == 0 && p == argv[0])){ X rp->status = Status_BADARG; X return -1; X } X return SRtreserve(rp, nbytes); X} static long cmdtrackinfo(ScsiReq *rp, int argc, char *argv[]) X{ X uchar d[MaxDirData], track; X long n; X ulong ul; X char *p; X track = 0; X if(argc && (track = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X if((n = SRtinfo(rp, track, d)) == -1) X return -1; X Bprint(&bout, "buffer length: 0x%uX\n", d[0]); X Bprint(&bout, "number of tracks: 0x%uX\n", d[1]); X ul = (d[2]<<24)|(d[3]<<16)|(d[4]<<8)|d[5]; X Bprint(&bout, "start address: 0x%luX\n", ul); X ul = (d[6]<<24)|(d[7]<<16)|(d[8]<<8)|d[9]; X Bprint(&bout, "track length: 0x%luX\n", ul); X Bprint(&bout, "track mode: 0x%uX\n", d[0x0A] & 0x0F); X Bprint(&bout, "track status: 0x%uX\n", (d[0x0A]>>4) & 0x0F); X Bprint(&bout, "data mode: 0x%uX\n", d[0x0B] & 0x0F); X ul = (d[0x0C]<<24)|(d[0x0D]<<16)|(d[0x0E]<<8)|d[0x0F]; X Bprint(&bout, "free blocks: 0x%luX\n", ul); X return n; X} static long cmdwtrack(ScsiReq *rp, int argc, char *argv[]) X{ X uchar mode, track; X long n, nbytes, total, x; X int fd, pid; X char *p; X mode = track = 0; X nbytes = 0; X switch(argc){ X default: X rp->status = Status_BADARG; X return -1; X case 4: X if((mode = strtoul(argv[3], &p, 0)) == 0 && p == argv[3]){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 3: X if((track = strtoul(argv[2], &p, 0)) == 0 && p == argv[2]){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 2: X if((nbytes = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 1: X if((fd = mkfile(argv[0], OREAD, &pid)) == -1){ X rp->status = Status_BADARG; X return -1; X } X break; X } X total = 0; X n = MIN(nbytes, maxiosize); X if((n = readn(fd, rwbuf, n)) == -1){ X fprint(2, "file read failed %r\n"); X close(fd); X return -1; X } X if((x = SRwtrack(rp, rwbuf, n, track, mode)) != n){ X fprint(2, "wtrack: write incomplete: asked %ld, did %ld\n", n, x); X if(rp->status == STok) X rp->status = Status_SW; X close(fd); X return -1; X } X nbytes -= n; X total += n; X while(nbytes){ X n = MIN(nbytes, maxiosize); X if((n = read(fd, rwbuf, n)) == -1){ X break; X } X if((x = SRwrite(rp, rwbuf, n)) != n){ X fprint(2, "write: write incomplete: asked %ld, did %ld\n", n, x); X if(rp->status == STok) X rp->status = Status_SW; X break; X } X nbytes -= n; X total += n; X } X close(fd); X if(pid >= 0 && waitfor(pid)){ X rp->status = Status_SW; X return -1; X } X return total; X} static long cmdload(ScsiReq *rp, int argc, char *argv[]) X{ X USED(argc, argv); X return SRmload(rp, 0); X} static long cmdunload(ScsiReq *rp, int argc, char *argv[]) X{ X USED(argc, argv); X return SRmload(rp, 1); X} static long cmdfixation(ScsiReq *rp, int argc, char *argv[]) X{ X uchar type; X char *p; X type = 0; X if(argc && (type = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X return SRfixation(rp, type); X} static long cmdeinit(ScsiReq *rp, int argc, char *argv[]) X{ X USED(argc, argv); X return SReinitialise(rp); X} static long cmdmmove(ScsiReq *rp, int argc, char *argv[]) X{ X int transport, source, destination, invert; X char *p; X invert = 0; X switch(argc){ X default: X rp->status = Status_BADARG; X return -1; X case 4: X if((invert = strtoul(argv[3], &p, 0)) == 0 && p == argv[3]){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 3: X if((transport = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X if((source = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){ X rp->status = Status_BADARG; X return -1; X } X if((destination = strtoul(argv[2], &p, 0)) == 0 && p == argv[2]){ X rp->status = Status_BADARG; X return -1; X } X break; X } X return SRmmove(rp, transport, source, destination, invert); X} static long cmdestatus(ScsiReq *rp, int argc, char *argv[]) X{ X uchar *list, *lp, type; X long d, i, n, nbytes, status; X char *p; X type = 0; X nbytes = 4096; X switch(argc){ X default: X rp->status = Status_BADARG; X return -1; X case 2: X if((nbytes = strtoul(argv[1], &p, 0)) == 0 && p == argv[1]){ X rp->status = Status_BADARG; X return -1; X } X /*FALLTHROUGH*/ X case 1: X if((type = strtoul(argv[0], &p, 0)) == 0 && p == argv[0]){ X rp->status = Status_BADARG; X return -1; X } X break; X case 0: X break; X } X list = malloc(nbytes); X if(list == 0){ X rp->status = STnomem; X return -1; X } X status = SRestatus(rp, type, list, nbytes); X if(status == -1){ X free(list); X return -1; X } X lp = list; X nbytes = ((lp[5]<<16)|(lp[6]<<8)|lp[7])-8; X Bprint(&bout, " Header\n "); X for(i = 0; i < 8; i++){ /* header */ X Bprint(&bout, " %2.2uX", *lp); X lp++; X } X Bputc(&bout, '\n'); X while(nbytes > 0){ /* pages */ X i = ((lp[5]<<16)|(lp[6]<<8)|lp[7]); X nbytes -= i+8; X Bprint(&bout, " Type"); X for(n = 0; n < 8; n++) /* header */ X Bprint(&bout, " %2.2uX", lp[n]); X Bprint(&bout, "\n "); X d = (lp[2]<<8)|lp[3]; X lp += 8; X for(n = 0; n < i; n++){ X if(n && (n % d) == 0) X Bprint(&bout, "\n "); X Bprint(&bout, " %2.2uX", *lp); X lp++; X } X if(n && (n % d)) X Bputc(&bout, '\n'); X } X free(list); X return status; X} static long cmdhelp(ScsiReq *rp, int argc, char *argv[]) X{ X ScsiCmd *cp; X char *p; X USED(rp); X if(argc) X p = argv[0]; X else X p = 0; X for(cp = scsicmd; cp->name; cp++){ X if(p == 0 || strcmp(p, cp->name) == 0) X Bprint(&bout, "%s\n", cp->help); X } X return 0; X} static int atatable[4] = { X 'C', 'D', 'E', 'F', X}; static int scsitable[16] = { X '0', '1', '2', '3', '4', '5', '6', '7', X '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', X}; static int unittable[16] = { X '0', '1', '2', '3', '4', '5', '6', '7', X '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', X}; static long cmdprobe(ScsiReq *rp, int argc, char *argv[]) X{ X char buf[32]; X ScsiReq scsireq; X char *ctlr, *unit; X X USED(argc, argv); X rp->status = STok; X scsireq.flags = 0; X for(ctlr="CDEF0123456789abcdef"; *ctlr; ctlr++) { X /* X * I can guess how many units you have. X */ X if(*ctlr >= 'C' && *ctlr <= 'F') X unit = "01"; X else if((*ctlr >= '0' && *ctlr <= '9') X || (*ctlr >= 'a' && *ctlr <= 'f')) X unit = "0123456789abcdef"; X else X unit = "012345678"; X X for(; *unit; unit++){ X sprint(buf, "/dev/sd%c%c", *ctlr, *unit); X if(SRopenraw(&scsireq, buf) == -1) X /* X return -1; X */ X continue; X SRreqsense(&scsireq); X switch(scsireq.status){ X X default: X break; X X case STok: X case Status_SD: X Bprint(&bout, "%s: ", buf); X cmdinquiry(&scsireq, 0, 0); X break; X } X SRclose(&scsireq); X } X } X return 0; X} static long cmdclose(ScsiReq *rp, int argc, char *argv[]) X{ X USED(argc, argv); X return SRclose(rp); X} static long cmdopen(ScsiReq *rp, int argc, char *argv[]) X{ X int raw; X long status; X raw = 0; X if(argc && strcmp("-r", argv[0]) == 0){ X raw = 1; X argc--, argv++; X } X if(argc != 1){ X rp->status = Status_BADARG; X return -1; X } X if(raw == 0){ X if((status = SRopen(rp, argv[0])) != -1 && verbose) X Bprint(&bout, "%sblock size: %ld\n", X rp->flags&Fbfixed? "fixed ": "", rp->lbsize); X } X else { X status = SRopenraw(rp, argv[0]); X rp->lbsize = 512; X } X return status; X} static ScsiCmd scsicmd[] = { X { "ready", cmdready, 1, /*[0x00]*/ X "ready", X }, X { "rewind", cmdrewind, 1, /*[0x01]*/ X "rewind", X }, X { "rezero", cmdrewind, 1, /*[0x01]*/ X "rezero", X }, X { "reqsense", cmdreqsense, 1, /*[0x03]*/ X "reqsense", X }, X { "format", cmdformat, 0, /*[0x04]*/ X "format", X }, X { "rblimits", cmdrblimits, 1, /*[0x05]*/ X "rblimits", X }, X { "read", cmdread, 1, /*[0x08]*/ X "read [|]file [nbytes]", X }, X { "write", cmdwrite, 1, /*[0x0A]*/ X "write [|]file [nbytes]", X }, X { "seek", cmdseek, 1, /*[0x0B]*/ X "seek offset [whence]", X }, X { "filemark", cmdfilemark, 1, /*[0x10]*/ X "filemark [howmany]", X }, X { "space", cmdspace, 1, /*[0x11]*/ X "space [-f] [-b] [[--] howmany]", X }, X { "inquiry", cmdinquiry, 1, /*[0x12]*/ X "inquiry", X }, X { "modeselect6",cmdmodeselect6, 1, /*[0x15] */ X "modeselect6 bytes...", X }, X { "modeselect", cmdmodeselect10, 1, /*[0x55] */ X "modeselect bytes...", X }, X { "modesense6", cmdmodesense6, 1, /*[0x1A]*/ X "modesense6 [page [nbytes]]", X }, X { "modesense", cmdmodesense10, 1, /*[0x5A]*/ X "modesense [page [nbytes]]", X }, X { "start", cmdstart, 1, /*[0x1B]*/ X "start [code]", X }, X { "stop", cmdstop, 1, /*[0x1B]*/ X "stop", X }, X { "eject", cmdeject, 1, /*[0x1B]*/ X "eject", X }, X { "ingest", cmdingest, 1, /*[0x1B]*/ X "ingest", X }, X { "capacity", cmdcapacity, 1, /*[0x25]*/ X "capacity", X }, X { "blank", cmdblank, 1, /*[0xA1]*/ X "blank [track/LBA [type]]", X }, X// { "synccache", cmdsynccache, 1, /*[0x35]*/ X// "synccache", X// }, X { "rtoc", cmdrtoc, 1, /*[0x43]*/ X "rtoc [track/session-number [format]]", X }, X { "rdiscinfo", cmdrdiscinfo, 1, /*[0x51]*/ X "rdiscinfo", X }, X { "rtrackinfo", cmdrtrackinfo, 1, /*[0x52]*/ X "rtrackinfo [track]", X }, X { "cdpause", cmdcdpause, 1, /*[0x4B]*/ X "cdpause", X }, X { "cdresume", cmdcdresume, 1, /*[0x4B]*/ X "cdresume", X }, X { "cdstop", cmdcdstop, 1, /*[0x4E]*/ X "cdstop", X }, X { "cdplay", cmdcdplay, 1, /*[0xA5]*/ X "cdplay [track-number] or [-r [LBA [length]]]", X }, X { "cdload", cmdcdload, 1, /*[0xA6*/ X "cdload [slot]", X }, X { "cdunload", cmdcdunload, 1, /*[0xA6]*/ X "cdunload [slot]", X }, X { "cdstatus", cmdcdstatus, 1, /*[0xBD]*/ X "cdstatus", X }, X// { "getconf", cmdgetconf, 1, /*[0x46]*/ X// "getconf", X// }, X// { "fwaddr", cmdfwaddr, 1, /*[0xE2]*/ X// "fwaddr [track [mode [npa]]]", X// }, X// { "treserve", cmdtreserve, 1, /*[0xE4]*/ X// "treserve nbytes", X// }, X// { "trackinfo", cmdtrackinfo, 1, /*[0xE5]*/ X// "trackinfo [track]", X// }, X// { "wtrack", cmdwtrack, 1, /*[0xE6]*/ X// "wtrack [|]file [nbytes [track [mode]]]", X// }, X// { "load", cmdload, 1, /*[0xE7]*/ X// "load", X// }, X// { "unload", cmdunload, 1, /*[0xE7]*/ X// "unload", X// }, X// { "fixation", cmdfixation, 1, /*[0xE9]*/ X// "fixation [toc-type]", X// }, X { "einit", cmdeinit, 1, /*[0x07]*/ X "einit", X }, X { "estatus", cmdestatus, 1, /*[0xB8]*/ X "estatus", X }, X { "mmove", cmdmmove, 1, /*[0xA5]*/ X "mmove transport source destination [invert]", X }, X { "help", cmdhelp, 0, X "help", X }, X { "probe", cmdprobe, 0, X "probe", X }, X { "close", cmdclose, 1, X "close", X }, X { "open", cmdopen, 0, X "open [-r] sddev", X }, X { 0, 0 }, X}; X#define SEP(c) (((c)==' ')||((c)=='\t')||((c)=='\n')) static char * tokenise(char *s, char **start, char **end) X{ X char *to; X Rune r; X int n; X while(*s && SEP(*s)) /* skip leading white space */ X s++; X to = *start = s; X while(*s){ X n = chartorune(&r, s); X if(SEP(r)){ X if(to != *start) /* we have data */ X break; X s += n; /* null string - keep looking */ X while(*s && SEP(*s)) X s++; X to = *start = s; X } X else if(r == '\''){ X s += n; /* skip leading quote */ X while(*s){ X n = chartorune(&r, s); X if(r == '\''){ X if(s[1] != '\'') X break; X s++; /* embedded quote */ X } X while (n--) X *to++ = *s++; X } X if(!*s) /* no trailing quote */ X break; X s++; /* skip trailing quote */ X } X else { X while(n--) X *to++ = *s++; X } X } X *end = to; X return s; X} static int parse(char *s, char *fields[], int nfields) X{ X int c, argc; X char *start, *end; X argc = 0; X c = *s; X while(c){ X s = tokenise(s, &start, &end); X c = *s++; X if(*start == 0) X break; X if(argc >= nfields-1) X return -1; X *end = 0; X fields[argc++] = start; X } X fields[argc] = 0; X return argc; X} static void usage(void) X{ X fprint(2, "%s: usage: %s [-q] [-m maxiosize] [/dev/sdXX]\n", argv0, argv0); X exits("usage"); X} static struct { X int status; X char* description; X} description[] = { X STnomem, "buffer allocation failed", X STtimeout, "bus timeout", X STharderr, "controller error of some kind", X STok, "good", X STcheck, "check condition", X STcondmet, "condition met/good", X STbusy, "busy ", X STintok, "intermediate/good", X STintcondmet, "intermediate/condition met/good", X STresconf, "reservation conflict", X STterminated, "command terminated", X STqfull, "queue full", X Status_SD, "sense-data available", X Status_SW, "internal software error", X Status_BADARG, "bad argument to request", X 0, 0, X}; void main(int argc, char *argv[]) X{ X ScsiReq target; X char *ap, *av[256]; X int ac, i; X ScsiCmd *cp; X long status; X ARGBEGIN { X case 'q': X verbose = 0; X break; X case 'm': X ap = ARGF(); X if(ap == nil) X usage(); X maxiosize = atol(ap); X if(maxiosize < 512 || maxiosize > MaxIOsize) X usage(); X break; X default: X usage(); X } ARGEND X if(Binit(&bin, 0, OREAD) == Beof || Binit(&bout, 1, OWRITE) == Beof){ X fprint(2, "%s: can't init bio: %r\n", argv0); X exits("Binit"); X } X memset(&target, 0, sizeof(target)); X if(argc && cmdopen(&target, argc, argv) == -1) { X fprint(2, "open failed\n"); X usage(); X } X Bflush(&bout); X while(ap = Brdline(&bin, '\n')){ X ap[Blinelen(&bin)-1] = 0; X switch(ac = parse(ap, av, nelem(av))){ X default: X for(cp = scsicmd; cp->name; cp++){ X if(strcmp(cp->name, av[0]) == 0) X break; X } X if(cp->name == 0){ X Bprint(&bout, "eh?\n"); X break; X } X if((target.flags & Fopen) == 0 && cp->open){ X Bprint(&bout, "no current target\n"); X break; X } X if((status = (*cp->f)(&target, ac-1, &av[1])) != -1){ X if(verbose) X Bprint(&bout, "ok %ld\n", status); X break; X } X for(i = 0; description[i].description; i++){ X if(target.status != description[i].status) X continue; X if(target.status == Status_SD) X makesense(&target); X else X Bprint(&bout, "%s\n", description[i].description); X break; X } X break; X case -1: X Bprint(&bout, "eh?\n"); X break; X case 0: X break; X } X Bflush(&bout); X } X exits(0); X} ! echo scuzz.mine/sense.c sed 's/^X//' >scuzz.mine/sense.c <<'!' X#include X#include X#include X#include "scsireq.h" static char* key[16] = X{ X "no sense", X "recovered error", X "not ready", X "medium error", X "hardware error", X "illegal request", X "unit attention", X "data protect", X "blank check", X "vendor specific", X "copy aborted", X "aborted command", X "equal", X "volume overflow", X "miscompare", X "reserved", X}; static struct X{ X uchar asc; X uchar ascq; X char* diag; X} code[] = X{ 0x03,0x00, "tray out", 0x04,0x00, "drive not ready", 0x08,0x00, "communication failure", 0x09,0x00, "track following error", 0x11,0x00, "unrecovered read error", 0x15,0x00, "positioning error", 0x17,0x00, "recovered read data with retries", 0x18,0x00, "recovered read with ecc correction", 0x1A,0x00, "parameter list length error", 0x20,0x00, "invalid command", 0x21,0x00, "invalid block address", 0x24,0x00, "illegal field in command list", 0x25,0x00, "invalid lun", 0x26,0x00, "invalid field parameter list", 0x28,0x00, "medium changed", 0x29,0x00, "power-on reset or bus reset occurred", 0x2C,0x00, "command sequence error", 0x31,0x00, "medium format corrupted", 0x33,0x00, "monitor atip error", 0x34,0x00, "absorption control error", 0x3A,0x00, "medium not present", 0x3D,0x00, "invalid bits in identify message", 0x40,0x00, "diagnostic failure", 0x42,0x00, "power-on or self test failure", 0x44,0x00, "internal controller error", 0x47,0x00, "scsi parity error", 0x50,0x00, "write append error", 0x53,0x00, "medium load or eject failed", 0x57,0x00, "unable to read toc, pma or subcode", 0x5A,0x00, "operator medium removal request", 0x63,0x00, "end of user area encountered on this track", 0x64,0x00, "illegal mode for this track", 0x65,0x00, "verify failed", 0x6f,0x01, "copy protection key exchange failure - key not present", 0x6f,0x02, "copy protection key exchange failure - key not established", 0x6f,0x03, "read of scrambled sector without authentication", 0x6f,0x04, "media region code is mismatched to logical unit region", 0x6f,0x05, "drive region must be permanent/region reset count error", 0x6f,0x00, "copy protection key exchange failure", 0x81,0x00, "illegal track", 0x82,0x00, "command now not valid", 0x83,0x00, "medium removal is prevented", 0xA0,0x00, "stopped on non-data block", 0xA1,0x00, "invalid start address", 0xA2,0x00, "attempt to cross track boundary", 0xA3,0x00, "illegal medium", 0xA4,0x00, "disc write-protected", 0xA5,0x00, "application code conflict", 0xA6,0x00, "illegal block-size for command", 0xA7,0x00, "block-size conflict", 0xA8,0x00, "illegal transfer-length", 0xA9,0x00, "request for fixation failed", 0xAA,0x00, "end of medium reached", 0xAB,0x00, "illegal track number", 0xAC,0x00, "data track length error", 0xAD,0x00, "buffer underrun", 0xAE,0x00, "illegal track mode", 0xAF,0x00, "optimum power calibration error", 0xB0,0x00, "calibration area almost full", 0xB1,0x00, "current programme area empty", 0xB2,0x00, "no efm at search address", 0xB3,0x00, "link area encountered", 0xB4,0x00, "calibration area full", 0xB5,0x00, "dummy blocks added", 0xB6,0x00, "block size format conflict", 0xB7,0x00, "current command aborted", 0xD0,0x00, "recovery needed", 0xD1,0x00, "can't recover from track", 0xD2,0x00, "can't recover from program memory area", 0xD3,0x00, "can't recover from leadin area", 0xD4,0x00, "can't recover from leadout area", 0xD5,0x00, "can't recover from optical power calibration area", 0xD6,0x00, "eeprom failure", X}; extern Biobuf bout; void makesense(ScsiReq *rp) X{ X int i; X Bprint(&bout, "sense data: %s", key[rp->sense[2] & 0x0F]); X for(i=0; isense[0x0C]) X if(code[i].ascq == 0 || code[i].ascq == rp->sense[0x0D]) X break; X if(rp->sense[7] >= 5 && i < nelem(code)) X Bprint(&bout, ": %s", code[i].diag); X Bprint(&bout, "\n\t"); X for(i = 0; i < 8+rp->sense[7]; i++) X Bprint(&bout, " %2.2ux", rp->sense[i]); X Bprint(&bout, "\n"); X} !