plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

scsi.c (6112B)


      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 
    267 /*	if(key == 0) */
    268 /*		return dcount; */
    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 }