wkj | e1dddc0 | 2004-06-17 01:46:29 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Now thread-safe. |
| 3 | * |
| 4 | * The codeqlock guarantees that once codes != nil, that pointer will never |
| 5 | * change nor become invalid. |
| 6 | * |
| 7 | * The QLock in the Scsi structure moderates access to the raw device. |
| 8 | * We should probably export some of the already-locked routines, but |
| 9 | * there hasn't been a need. |
| 10 | */ |
| 11 | |
| 12 | #include <u.h> |
| 13 | #include <libc.h> |
| 14 | #include <disk.h> |
| 15 | |
| 16 | int scsiverbose; |
| 17 | |
| 18 | #define codefile "/sys/lib/scsicodes" |
| 19 | |
| 20 | static char *codes; |
| 21 | static QLock codeqlock; |
| 22 | |
| 23 | static void |
| 24 | getcodes(void) |
| 25 | { |
| 26 | Dir *d; |
| 27 | int n, fd; |
| 28 | |
| 29 | if(codes != nil) |
| 30 | return; |
| 31 | |
| 32 | qlock(&codeqlock); |
| 33 | if(codes != nil) { |
| 34 | qunlock(&codeqlock); |
| 35 | return; |
| 36 | } |
| 37 | |
| 38 | if((d = dirstat(codefile)) == nil || (fd = open(codefile, OREAD)) < 0) { |
| 39 | qunlock(&codeqlock); |
| 40 | return; |
| 41 | } |
| 42 | |
| 43 | codes = malloc(1+d->length+1); |
| 44 | if(codes == nil) { |
| 45 | close(fd); |
| 46 | qunlock(&codeqlock); |
| 47 | free(d); |
| 48 | return; |
| 49 | } |
| 50 | |
| 51 | codes[0] = '\n'; /* for searches */ |
| 52 | n = readn(fd, codes+1, d->length); |
| 53 | close(fd); |
| 54 | free(d); |
| 55 | |
| 56 | if(n < 0) { |
| 57 | free(codes); |
| 58 | codes = nil; |
| 59 | qunlock(&codeqlock); |
| 60 | return; |
| 61 | } |
| 62 | codes[n] = '\0'; |
| 63 | qunlock(&codeqlock); |
| 64 | } |
| 65 | |
| 66 | char* |
| 67 | scsierror(int asc, int ascq) |
| 68 | { |
| 69 | char *p, *q; |
| 70 | static char search[32]; |
| 71 | static char buf[128]; |
| 72 | |
| 73 | getcodes(); |
| 74 | |
| 75 | if(codes) { |
| 76 | sprint(search, "\n%.2ux%.2ux ", asc, ascq); |
| 77 | if(p = strstr(codes, search)) { |
| 78 | p += 6; |
| 79 | if((q = strchr(p, '\n')) == nil) |
| 80 | q = p+strlen(p); |
| 81 | snprint(buf, sizeof buf, "%.*s", (int)(q-p), p); |
| 82 | return buf; |
| 83 | } |
| 84 | |
| 85 | sprint(search, "\n%.2ux00", asc); |
| 86 | if(p = strstr(codes, search)) { |
| 87 | p += 6; |
| 88 | if((q = strchr(p, '\n')) == nil) |
| 89 | q = p+strlen(p); |
| 90 | snprint(buf, sizeof buf, "(ascq #%.2ux) %.*s", ascq, (int)(q-p), p); |
| 91 | return buf; |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | sprint(buf, "scsi #%.2ux %.2ux", asc, ascq); |
| 96 | return buf; |
| 97 | } |
| 98 | |
| 99 | |
| 100 | static int |
| 101 | _scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io, int dolock) |
| 102 | { |
| 103 | uchar resp[16]; |
| 104 | int n; |
| 105 | long status; |
| 106 | |
| 107 | if(dolock) |
| 108 | qlock(&s->lk); |
| 109 | if(write(s->rawfd, cmd, ccount) != ccount) { |
| 110 | werrstr("cmd write: %r"); |
| 111 | if(dolock) |
| 112 | qunlock(&s->lk); |
| 113 | return -1; |
| 114 | } |
| 115 | |
| 116 | switch(io){ |
| 117 | case Sread: |
| 118 | n = read(s->rawfd, data, dcount); |
| 119 | if(n < 0 && scsiverbose) |
| 120 | fprint(2, "dat read: %r: cmd 0x%2.2uX\n", cmd[0]); |
| 121 | break; |
| 122 | case Swrite: |
| 123 | n = write(s->rawfd, data, dcount); |
| 124 | if(n != dcount && scsiverbose) |
| 125 | fprint(2, "dat write: %r: cmd 0x%2.2uX\n", cmd[0]); |
| 126 | break; |
| 127 | default: |
| 128 | case Snone: |
| 129 | n = write(s->rawfd, resp, 0); |
| 130 | if(n != 0 && scsiverbose) |
| 131 | fprint(2, "none write: %r: cmd 0x%2.2uX\n", cmd[0]); |
| 132 | break; |
| 133 | } |
| 134 | |
| 135 | memset(resp, 0, sizeof(resp)); |
| 136 | if(read(s->rawfd, resp, sizeof(resp)) < 0) { |
| 137 | werrstr("resp read: %r\n"); |
| 138 | if(dolock) |
| 139 | qunlock(&s->lk); |
| 140 | return -1; |
| 141 | } |
| 142 | if(dolock) |
| 143 | qunlock(&s->lk); |
| 144 | |
| 145 | resp[sizeof(resp)-1] = '\0'; |
| 146 | status = atoi((char*)resp); |
| 147 | if(status == 0) |
| 148 | return n; |
| 149 | |
| 150 | werrstr("cmd %2.2uX: status %luX dcount %d n %d", cmd[0], status, dcount, n); |
| 151 | return -1; |
| 152 | } |
| 153 | |
| 154 | int |
| 155 | scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io) |
| 156 | { |
| 157 | return _scsicmd(s, cmd, ccount, data, dcount, io, 1); |
| 158 | } |
| 159 | |
| 160 | static int |
| 161 | _scsiready(Scsi *s, int dolock) |
| 162 | { |
| 163 | uchar cmd[6], resp[16]; |
| 164 | int status, i; |
| 165 | |
| 166 | if(dolock) |
| 167 | qlock(&s->lk); |
| 168 | for(i=0; i<3; i++) { |
| 169 | memset(cmd, 0, sizeof(cmd)); |
| 170 | cmd[0] = 0x00; /* unit ready */ |
| 171 | if(write(s->rawfd, cmd, sizeof(cmd)) != sizeof(cmd)) { |
| 172 | if(scsiverbose) |
| 173 | fprint(2, "ur cmd write: %r\n"); |
| 174 | goto bad; |
| 175 | } |
| 176 | write(s->rawfd, resp, 0); |
| 177 | if(read(s->rawfd, resp, sizeof(resp)) < 0) { |
| 178 | if(scsiverbose) |
| 179 | fprint(2, "ur resp read: %r\n"); |
| 180 | goto bad; |
| 181 | } |
| 182 | resp[sizeof(resp)-1] = '\0'; |
| 183 | status = atoi((char*)resp); |
| 184 | if(status == 0 || status == 0x02) { |
| 185 | if(dolock) |
| 186 | qunlock(&s->lk); |
| 187 | return 0; |
| 188 | } |
| 189 | if(scsiverbose) |
| 190 | fprint(2, "target: bad status: %x\n", status); |
| 191 | bad:; |
| 192 | } |
| 193 | if(dolock) |
| 194 | qunlock(&s->lk); |
| 195 | return -1; |
| 196 | } |
| 197 | |
| 198 | int |
| 199 | scsiready(Scsi *s) |
| 200 | { |
| 201 | return _scsiready(s, 1); |
| 202 | } |
| 203 | |
| 204 | int |
| 205 | scsi(Scsi *s, uchar *cmd, int ccount, void *v, int dcount, int io) |
| 206 | { |
| 207 | uchar req[6], sense[255], *data; |
| 208 | int tries, code, key, n; |
| 209 | char *p; |
| 210 | |
| 211 | data = v; |
| 212 | SET(key); SET(code); |
| 213 | qlock(&s->lk); |
| 214 | for(tries=0; tries<2; tries++) { |
| 215 | n = _scsicmd(s, cmd, ccount, data, dcount, io, 0); |
| 216 | if(n >= 0) { |
| 217 | qunlock(&s->lk); |
| 218 | return n; |
| 219 | } |
| 220 | |
| 221 | /* |
| 222 | * request sense |
| 223 | */ |
| 224 | memset(req, 0, sizeof(req)); |
| 225 | req[0] = 0x03; |
| 226 | req[4] = sizeof(sense); |
| 227 | memset(sense, 0xFF, sizeof(sense)); |
| 228 | if((n=_scsicmd(s, req, sizeof(req), sense, sizeof(sense), Sread, 0)) < 14) |
| 229 | if(scsiverbose) |
| 230 | fprint(2, "reqsense scsicmd %d: %r\n", n); |
| 231 | |
| 232 | if(_scsiready(s, 0) < 0) |
| 233 | if(scsiverbose) |
| 234 | fprint(2, "unit not ready\n"); |
| 235 | |
| 236 | key = sense[2]; |
| 237 | code = sense[12]; |
| 238 | if(code == 0x17 || code == 0x18) { /* recovered errors */ |
| 239 | qunlock(&s->lk); |
| 240 | return dcount; |
| 241 | } |
| 242 | if(code == 0x28 && cmd[0] == 0x43) { /* get info and media changed */ |
| 243 | s->nchange++; |
| 244 | s->changetime = time(0); |
| 245 | continue; |
| 246 | } |
| 247 | } |
| 248 | |
| 249 | /* drive not ready, or medium not present */ |
| 250 | if(cmd[0] == 0x43 && key == 2 && (code == 0x3a || code == 0x04)) { |
| 251 | s->changetime = 0; |
| 252 | qunlock(&s->lk); |
| 253 | return -1; |
| 254 | } |
| 255 | qunlock(&s->lk); |
| 256 | |
| 257 | if(cmd[0] == 0x43 && key == 5 && code == 0x24) /* blank media */ |
| 258 | return -1; |
| 259 | |
| 260 | p = scsierror(code, sense[13]); |
| 261 | |
| 262 | werrstr("cmd #%.2ux: %s", cmd[0], p); |
| 263 | |
| 264 | if(scsiverbose) |
| 265 | fprint(2, "scsi cmd #%.2ux: %.2ux %.2ux %.2ux: %s\n", cmd[0], key, code, sense[13], p); |
| 266 | |
rsc | cbeb0b2 | 2006-04-01 19:24:03 +0000 | [diff] [blame] | 267 | /* if(key == 0) */ |
| 268 | /* return dcount; */ |
wkj | e1dddc0 | 2004-06-17 01:46:29 +0000 | [diff] [blame] | 269 | return -1; |
| 270 | } |
| 271 | |
| 272 | Scsi* |
| 273 | openscsi(char *dev) |
| 274 | { |
| 275 | Scsi *s; |
| 276 | int rawfd, ctlfd, l, n; |
| 277 | char *name, *p, buf[512]; |
| 278 | |
| 279 | l = strlen(dev)+1+3+1; |
| 280 | name = malloc(l); |
| 281 | if(name == nil) |
| 282 | return nil; |
| 283 | |
| 284 | snprint(name, l, "%s/raw", dev); |
| 285 | if((rawfd = open(name, ORDWR)) < 0) { |
| 286 | free(name); |
| 287 | return nil; |
| 288 | } |
| 289 | |
| 290 | snprint(name, l, "%s/ctl", dev); |
| 291 | if((ctlfd = open(name, ORDWR)) < 0) { |
| 292 | free(name); |
| 293 | Error: |
| 294 | close(rawfd); |
| 295 | return nil; |
| 296 | } |
| 297 | free(name); |
| 298 | |
| 299 | n = readn(ctlfd, buf, sizeof buf); |
| 300 | close(ctlfd); |
| 301 | if(n <= 0) |
| 302 | goto Error; |
| 303 | |
| 304 | if(strncmp(buf, "inquiry ", 8) != 0 || (p = strchr(buf, '\n')) == nil) |
| 305 | goto Error; |
| 306 | *p = '\0'; |
| 307 | |
| 308 | if((p = strdup(buf+8)) == nil) |
| 309 | goto Error; |
| 310 | |
| 311 | s = malloc(sizeof(*s)); |
| 312 | if(s == nil) { |
| 313 | Error1: |
| 314 | free(p); |
| 315 | goto Error; |
| 316 | } |
| 317 | memset(s, 0, sizeof(*s)); |
| 318 | |
| 319 | s->rawfd = rawfd; |
| 320 | s->inquire = p; |
| 321 | s->changetime = time(0); |
| 322 | |
| 323 | if(scsiready(s) < 0) |
| 324 | goto Error1; |
| 325 | |
| 326 | return s; |
| 327 | } |