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 := sys->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; }