diff options
author | aiju <aiju@phicode.de> | 2011-07-27 20:07:30 +0200 |
---|---|---|
committer | aiju <aiju@phicode.de> | 2011-07-27 20:07:30 +0200 |
commit | 05d09f086fdb76206ab5ff0e26a1e1bc2f34b6a3 (patch) | |
tree | c62905a4a8467110b426843a683136a835c8be14 /sys/src/cmd/nusb/disk/scsireq.c | |
parent | 5181f2e576dc6b92dfa7dcaa2f60397b931fbc4a (diff) |
nusb: improved
Diffstat (limited to 'sys/src/cmd/nusb/disk/scsireq.c')
-rw-r--r-- | sys/src/cmd/nusb/disk/scsireq.c | 986 |
1 files changed, 986 insertions, 0 deletions
diff --git a/sys/src/cmd/nusb/disk/scsireq.c b/sys/src/cmd/nusb/disk/scsireq.c new file mode 100644 index 000000000..f9994e286 --- /dev/null +++ b/sys/src/cmd/nusb/disk/scsireq.c @@ -0,0 +1,986 @@ +/* + * This is /sys/src/cmd/scuzz/scsireq.c + * changed to add more debug support, to keep + * disk compiling without a scuzz that includes these changes. + * Also, this includes minor tweaks for usb: + * we set req.lun/unit to rp->lun/unit in SRreqsense + * we set the rp->sense[0] bit Sd0valid in SRreqsense + * This does not use libdisk to retrieve the scsi error to make + * user see the diagnostics if we boot with debug enabled. + * + * BUGS: + * no luns + * and incomplete in many other ways + */ + +#include <u.h> +#include <libc.h> +#include <fcall.h> +#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; + +static char *scmdnames[256] = { +[ScmdTur] "Tur", +[ScmdRewind] "Rewind", +[ScmdRsense] "Rsense", +[ScmdFormat] "Format", +[ScmdRblimits] "Rblimits", +[ScmdRead] "Read", +[ScmdWrite] "Write", +[ScmdSeek] "Seek", +[ScmdFmark] "Fmark", +[ScmdSpace] "Space", +[ScmdInq] "Inq", +[ScmdMselect6] "Mselect6", +[ScmdMselect10] "Mselect10", +[ScmdMsense6] "Msense6", +[ScmdMsense10] "Msense10", +[ScmdStart] "Start", +[ScmdRcapacity] "Rcapacity", +[ScmdRcapacity16] "Rcap16", +[ScmdExtread] "Extread", +[ScmdExtwrite] "Extwrite", +[ScmdExtseek] "Extseek", + +[ScmdSynccache] "Synccache", +[ScmdRTOC] "RTOC", +[ScmdRdiscinfo] "Rdiscinfo", +[ScmdRtrackinfo] "Rtrackinfo", +[ScmdReserve] "Reserve", +[ScmdBlank] "Blank", + +[ScmdCDpause] "CDpause", +[ScmdCDstop] "CDstop", +[ScmdCDplay] "CDplay", +[ScmdCDload] "CDload", +[ScmdCDscan] "CDscan", +[ScmdCDstatus] "CDstatus", +[Scmdgetconf] "getconf", +}; + +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.lun = rp->lun; + req.unit = rp->unit; + 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; + if(status != -1) + rp->sense[0] |= Sd0valid; + 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; +} + +extern int diskdebug; + +long +SRread(ScsiReq *rp, void *buf, long nbytes) +{ + uchar cmd[10]; + long n; + + if(rp->lbsize == 0 || (nbytes % rp->lbsize) || nbytes > Maxiosize){ + if(diskdebug) + if (nbytes % rp->lbsize) + fprint(2, "disk: i/o size %ld %% %ld != 0\n", + nbytes, rp->lbsize); + else + fprint(2, "disk: i/o size %ld > %d\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(!(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(rp->lbsize == 0 || (nbytes % rp->lbsize) || nbytes > Maxiosize){ + if(diskdebug) + if (nbytes % rp->lbsize) + fprint(2, "disk: i/o size %ld %% %ld != 0\n", + nbytes, rp->lbsize); + else + fprint(2, "disk: i/o size %ld > %d\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(diskdebug) + fprint(2, "disk: 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) { + rp->offset = offset; + return 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); +} + +long +SRrcapacity16(ScsiReq *rp, uchar *data) +{ + uchar cmd[16]; + uint i; + + i = 32; + memset(cmd, 0, sizeof cmd); + cmd[0] = ScmdRcapacity16; + cmd[1] = 0x10; + cmd[10] = i>>24; + cmd[11] = i>>16; + cmd[12] = i>>8; + cmd[13] = i; + + rp->cmd.p = cmd; + rp->cmd.count = sizeof cmd; + rp->data.p = data; + rp->data.count = i; + rp->data.write = 0; + return SRrequest(rp); +} + +void +scsidebug(int d) +{ + debug = d; + if(debug) + fprint(2, "scsidebug on\n"); +} + +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(""); +// alarm(5*1000); + 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); + } +// alarm(0); + 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; +} + +static char* +seprintcmd(char *s, char* e, char *cmd, int count, int args) +{ + uint c; + + if(count < 6) + return seprint(s, e, "<short cmd>"); + c = cmd[0]; + if(scmdnames[c] != nil) + s = seprint(s, e, "%s", scmdnames[c]); + else + s = seprint(s, e, "cmd:%#02uX", c); + if(args != 0) + switch(c){ + case ScmdRsense: + case ScmdInq: + case ScmdMselect6: + case ScmdMsense6: + s = seprint(s, e, " sz %d", cmd[4]); + break; + case ScmdSpace: + s = seprint(s, e, " code %d", cmd[1]); + break; + case ScmdStart: + s = seprint(s, e, " code %d", cmd[4]); + break; + + } + return s; +} + +static char* +seprintdata(char *s, char *se, uchar *p, int count) +{ + int i; + + if(count == 0) + return s; + for(i = 0; i < 20 && i < count; i++) + s = seprint(s, se, " %02x", p[i]); + return s; +} + +static void +SRdumpReq(ScsiReq *rp) +{ + char buf[128]; + char *s; + char *se; + + se = buf+sizeof(buf); + s = seprint(buf, se, "lun %d ", rp->lun); + s = seprintcmd(s, se, (char*)rp->cmd.p, rp->cmd.count, 1); + s = seprint(s, se, " [%ld]", rp->data.count); + if(rp->cmd.write) + seprintdata(s, se, rp->data.p, rp->data.count); + fprint(2, "scsi⇒ %s\n", buf); +} + +static void +SRdumpRep(ScsiReq *rp) +{ + char buf[128]; + char *s; + char *se; + + se = buf+sizeof(buf); + s = seprint(buf, se, "lun %d ", rp->lun); + s = seprintcmd(s, se, (char*)rp->cmd.p, rp->cmd.count, 0); + switch(rp->status){ + case STok: + s = seprint(s, se, " good [%ld] ", rp->data.count); + if(rp->cmd.write == 0) + s = seprintdata(s, se, rp->data.p, rp->data.count); + break; + case STnomem: + s = seprint(s, se, " buffer allocation failed"); + break; + case STharderr: + s = seprint(s, se, " controller error"); + break; + case STtimeout: + s = seprint(s, se, " bus timeout"); + break; + case STcheck: + s = seprint(s, se, " check condition"); + break; + case STcondmet: + s = seprint(s, se, " condition met/good"); + break; + case STbusy: + s = seprint(s, se, " busy"); + break; + case STintok: + s = seprint(s, se, " intermediate/good"); + break; + case STintcondmet: + s = seprint(s, se, " intermediate/condition met/good"); + break; + case STresconf: + s = seprint(s, se, " reservation conflict"); + break; + case STterminated: + s = seprint(s, se, " command terminated"); + break; + case STqfull: + s = seprint(s, se, " queue full"); + break; + default: + s = seprint(s, se, " sts=%#x", rp->status); + } + USED(s); + fprint(2, "scsi← %s\n", buf); +} + +static char* +scsierr(ScsiReq *rp) +{ + int ec; + + switch(rp->status){ + case 0: + return ""; + case Status_SD: + ec = (rp->sense[12] << 8) | rp->sense[13]; + return scsierrmsg(ec); + case Status_SW: + return "software error"; + case Status_BADARG: + return "bad argument"; + case Status_RO: + return "device is read only"; + default: + return "unknown"; + } +} + +static void +SRdumpErr(ScsiReq *rp) +{ + char buf[128]; + char *se; + + se = buf+sizeof(buf); + seprintcmd(buf, se, (char*)rp->cmd.p, rp->cmd.count, 0); + print("\t%s status: %s\n", buf, scsierr(rp)); +} + +long +SRrequest(ScsiReq *rp) +{ + long n; + int status; + +retry: + if(debug) + SRdumpReq(rp); + if(rp->flags&Fusb) + n = umsrequest(rp->umsc, &rp->cmd, &rp->data, &status); + else + n = request(rp->fd, &rp->cmd, &rp->data, &status); + rp->status = status; + if(status == STok) + rp->data.count = n; + if(debug) + SRdumpRep(rp); + switch(status){ + case STok: + break; + case STcheck: + if(rp->cmd.p[0] != ScmdRsense && SRreqsense(rp) != -1) + rp->status = Status_SD; + if(debug || exabyte) + SRdumpErr(rp); + werrstr("%s", scsierr(rp)); + return -1; + case STbusy: + sleep(1000); /* TODO: try a shorter sleep? */ + goto retry; + default: + if(debug || exabyte) + SRdumpErr(rp); + werrstr("%s", scsierr(rp)); + return -1; + } + return n; +} + +int +SRclose(ScsiReq *rp) +{ + if((rp->flags & Fopen) == 0){ + if(diskdebug) + fprint(2, "disk: closing closed file\n"); + rp->status = Status_BADARG; + return -1; + } + close(rp->fd); + rp->flags = 0; + return 0; +} + +static int +dirdevopen(ScsiReq *rp) +{ + uvlong blocks; + uchar data[8+4+20]; /* 16-byte result: lba, blksize, reserved */ + + memset(data, 0, sizeof data); + if(SRstart(rp, 1) == -1 || SRrcapacity(rp, data) == -1) + return -1; + rp->lbsize = GETBELONG(data+4); + blocks = GETBELONG(data); + if(debug) + fprint(2, "disk: dirdevopen: 10-byte logical block size %lud, " + "# blocks %llud\n", rp->lbsize, blocks); + if(blocks == 0xffffffff){ + if(SRrcapacity16(rp, data) == -1) + return -1; + rp->lbsize = GETBELONG(data + 8); + blocks = (vlong)GETBELONG(data)<<32 | GETBELONG(data + 4); + if(debug) + fprint(2, "disk: dirdevopen: 16-byte logical block size" + " %lud, # blocks %llud\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, "disk: seqdevopen: 10-byte 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, "disk: wormdevopen: 10-byte logical block size %lud\n", + rp->lbsize); + return status; +} + +int +SRopenraw(ScsiReq *rp, char *unit) +{ + char name[128]; + + if(rp->flags & Fopen){ + if(diskdebug) + fprint(2, "disk: opening open file\n"); + rp->status = Status_BADARG; + return -1; + } + memset(rp, 0, sizeof *rp); + rp->unit = unit; + + snprint(name, sizeof 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 Devdir: + case Devcd: + case Devmo: + if(dirdevopen(rp) == -1) + break; + return 0; + + case Devseq: + rp->flags |= Fseqdev; + if(seqdevopen(rp) == -1) + break; + return 0; + + case Devprint: + rp->flags |= Fprintdev; + return 0; + + case Devworm: + rp->flags |= Fwormdev; + if(wormdevopen(rp) == -1) + break; + return 0; + + case Devjuke: + rp->flags |= Fchanger; + return 0; + } + } + SRclose(rp); + return -1; +} |