diff options
Diffstat (limited to 'appl/lib/scsiio.b')
| -rw-r--r-- | appl/lib/scsiio.b | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/appl/lib/scsiio.b b/appl/lib/scsiio.b new file mode 100644 index 00000000..152a22dd --- /dev/null +++ b/appl/lib/scsiio.b @@ -0,0 +1,317 @@ +implement ScsiIO; + +# adapted from /sys/src/libdisk on Plan 9: subject to Lucent Public License 1.02 + +include "sys.m"; + sys: Sys; + +include "bufio.m"; + bufio: Bufio; + Iobuf: import bufio; + +include "daytime.m"; + daytime: Daytime; + +include "scsiio.m"; + +scsiverbose := 0; + +Codefile: con "/lib/scsicodes"; + +Code: adt { + v: int; # (asc<<8) | ascq + s: string; +}; +codes: array of Code; + +init(verbose: int) +{ + sys = load Sys Sys->PATH; + bufio = load Bufio Bufio->PATH; + daytime = load Daytime Daytime->PATH; + + scsiverbose = verbose; + getcodes(); +} + +getcodes() +{ + fd := bufio->open(Codefile, Sys->OREAD); + if(fd == nil) + return; + + codes = array[256] of Code; + nc := 0; + while((s := fd.gets('\n')) != nil){ + if(s[0] == '#' || s[0] == '\n') + continue; + s = s[0: len s-1]; # trim '\n' + m: string; + for(i := 0; i < len s; i++) + if(s[i] == ' '){ + m = s[i+1:]; + break; + } + c := Code(tohex(s), m); + if(nc >= len codes){ + ct := array[nc + 20] of Code; + ct[0:] = codes; + codes = ct; + } + codes[nc++] = c; + } + codes = codes[0:nc]; +} + +tohex(s: string): int +{ + n := 0; + j := 0; + for(i := 0; i < len s && j < 4; i++){ + if(s[i] == '/') + continue; + d := hex(s[i]); + if(d < 0) + return -1; + n = (n<<4) | d; + j++; + } + return n; +} + +hex(c: int): int +{ + if(c >= '0' && c <= '9') + return c-'0'; + if(c >= 'A' && c <= 'F') + return c-'A' + 10; + if(c >= 'a' && c <= 'f') + return c-'a' + 10; + return -1; +} + +scsierror(asc: int, ascq: int): string +{ + t := -1; + for(i := 0; i < len codes; i++){ + if(codes[i].v == ((asc<<8) | ascq)) + return codes[i].s; + if(codes[i].v == (asc<<8)) + t = i; + } + if(t >= 0) + return sys->sprint("(ascq #%.2ux) %s", ascq, codes[t].s); + return sys->sprint("scsi #%.2ux %.2ux", asc, ascq); +} + +_scsicmd(s: ref Scsi, cmd: array of byte, data: array of byte, io: int, dolock: int): int +{ + if(dolock) + qlock(s); + dcount := len data; + if(sys->write(s.rawfd, cmd, len cmd) != len cmd) { + sys->werrstr("cmd write: %r"); + if(dolock) + qunlock(s); + return -1; + } + + n: int; + resp := array[16] of byte; + case io { + Sread => + n = sys->read(s.rawfd, data, dcount); + if(n < 0 && scsiverbose) + sys->fprint(sys->fildes(2), "dat read: %r: cmd %#2.2uX\n", int cmd[0]); + Swrite => + n = sys->write(s.rawfd, data, dcount); + if(n != dcount && scsiverbose) + sys->fprint(sys->fildes(2), "dat write: %r: cmd %#2.2uX\n", int cmd[0]); + Snone or * => + n = sys->write(s.rawfd, resp, 0); + if(n != 0 && scsiverbose) + sys->fprint(sys->fildes(2), "none write: %r: cmd %#2.2uX\n", int cmd[0]); + } + + m := sys->read(s.rawfd, resp, len resp); + if(dolock) + qunlock(s); + if(m < 0){ + sys->werrstr("resp read: %r\n"); + return -1; + } + status := int string resp[0:m]; + if(status == 0) + return n; + + sys->werrstr(sys->sprint("cmd %2.2uX: status %uX dcount %d n %d", int cmd[0], status, dcount, n)); + return -1; +} + +Scsi.rawcmd(s: self ref Scsi, cmd: array of byte, data: array of byte, io: int): int +{ + return _scsicmd(s, cmd, data, io, 1); +} + +_scsiready(s: ref Scsi, dolock: int): int +{ + if(dolock) + qlock(s); + for(i:=0; i<3; i++) { + cmd := array[6] of {0 => byte 16r00, * => byte 0}; # test unit ready + if(sys->write(s.rawfd, cmd, len cmd) != len cmd) { + if(scsiverbose) + sys->fprint(sys->fildes(2), "ur cmd write: %r\n"); + continue; + } + resp := array[16] of byte; + sys->write(s.rawfd, resp, 0); + m := sys->read(s.rawfd, resp, len resp); + if(m < 0){ + if(scsiverbose) + sys->fprint(sys->fildes(2), "ur resp read: %r\n"); + continue; # retry + } + status := int string resp[0:m]; + if(status == 0 || status == 16r02) { + if(dolock) + qunlock(s); + return 0; + } + if(scsiverbose) + sys->fprint(sys->fildes(2), "target: bad status: %x\n", status); + } + if(dolock) + qunlock(s); + return -1; +} + +Scsi.ready(s: self ref Scsi): int +{ + return _scsiready(s, 1); +} + +Scsi.cmd(s: self ref Scsi, cmd: array of byte, data: array of byte, io: int): int +{ + dcount := len data; + code := 0; + key := 0; + qlock(s); + sense: array of byte; + for(tries:=0; tries<2; tries++) { + n := _scsicmd(s, cmd, data, io, 0); + if(n >= 0) { + qunlock(s); + return n; + } + + # + # request sense + # + sense = array[255] of {* => byte 16rFF}; # TO DO: usb mass storage devices might inist on less + req := array[6] of {0 => byte 16r03, 4 => byte len sense, * => byte 0}; + if((n=_scsicmd(s, req, sense, Sread, 0)) < 14) + if(scsiverbose) + sys->fprint(sys->fildes(2), "reqsense scsicmd %d: %r\n", n); + + if(_scsiready(s, 0) < 0) + if(scsiverbose) + sys->fprint(sys->fildes(2), "unit not ready\n"); + + key = int sense[2]; + code = int sense[12]; + if(code == 16r17 || code == 16r18) { # recovered errors + qunlock(s); + return dcount; + } + if(code == 16r28 && int cmd[0] == 16r43) { # get info and media changed + s.nchange++; + s.changetime = daytime->now(); + continue; + } + } + + # drive not ready, or medium not present + if(cmd[0] == byte 16r43 && key == 2 && (code == 16r3a || code == 16r04)) { + s.changetime = 0; + qunlock(s); + return -1; + } + qunlock(s); + + if(cmd[0] == byte 16r43 && key == 5 && code == 16r24) # blank media + return -1; + + p := scsierror(code, int sense[13]); + + sys->werrstr(sys->sprint("cmd #%.2ux: %s", int cmd[0], p)); + + if(scsiverbose) + sys->fprint(sys->fildes(2), "scsi cmd #%.2ux: %.2ux %.2ux %.2ux: %s\n", int cmd[0], key, code, int sense[13], p); + +# if(key == 0) +# return dcount; + return -1; +} + +Scsi.open(dev: string): ref Scsi +{ + rawfd := sys->open(dev+"/raw", Sys->ORDWR); + if(rawfd == nil) + return nil; + ctlfd := sys->open(dev+"/ctl", Sys->ORDWR); + if(ctlfd == nil) + return nil; + + buf := array[512] of byte; + n := readn(ctlfd, buf, len buf); + if(n < 8){ + if(n >= 0) + sys->werrstr("error reading ctl file"); + return nil; + } + ctlfd = nil; + + for(i := 0; i < n; i++) + if(buf[i] == byte '\n') + break; + inq := string buf[0:i]; + if(i >= n || inq[0:8] != "inquiry "){ + sys->werrstr("invalid inquiry string"); + return nil; + } + s := ref Scsi; + s.lock = chan[1] of int; + s.rawfd = rawfd; + s.inquire = inq[8:]; + s.changetime = daytime->now(); + + if(s.ready() < 0) + return nil; + + return s; +} + +qlock(s: ref Scsi) +{ + s.lock <-= 1; +} + +qunlock(s: ref Scsi) +{ + <-s.lock; +} + +readn(fd: ref Sys->FD, buf: array of byte, nb: int): int +{ + for(nr := 0; nr < nb;){ + n := sys->read(fd, buf[nr:], nb-nr); + if(n <= 0){ + if(nr == 0) + return n; + break; + } + nr += n; + } + return nr; +} |
