henesyinit from p9fd787fda5 years agoHistory
327 lines · 5.9 KB · C
RawDownload
/* * Now thread-safe. * * The codeqlock guarantees that once codes != nil, that pointer will never  * change nor become invalid. * * The QLock in the Scsi structure moderates access to the raw device. * We should probably export some of the already-locked routines, but * there hasn't been a need. */ #include <u.h>#include <libc.h>#include <disk.h> int scsiverbose; #define codefile "/sys/lib/scsicodes" static char *codes;static QLock codeqlock; static voidgetcodes(void){	Dir *d;	int n, fd; 	if(codes != nil)		return; 	qlock(&codeqlock);	if(codes != nil) {		qunlock(&codeqlock);		return;	} 	if((d = dirstat(codefile)) == nil || (fd = open(codefile, OREAD)) < 0) {		qunlock(&codeqlock);		return;	} 	codes = malloc(1+d->length+1);	if(codes == nil) {		close(fd);		qunlock(&codeqlock);		free(d);		return;	} 	codes[0] = '\n';	/* for searches */	n = readn(fd, codes+1, d->length);	close(fd);	free(d); 	if(n < 0) {		free(codes);		codes = nil;		qunlock(&codeqlock);		return;	}	codes[n] = '\0';	qunlock(&codeqlock);}	char*scsierror(int asc, int ascq){	char *p, *q;	static char search[32];	static char buf[128]; 	getcodes(); 	if(codes) {		sprint(search, "\n%.2ux%.2ux ", asc, ascq);		if(p = strstr(codes, search)) {			p += 6;			if((q = strchr(p, '\n')) == nil)				q = p+strlen(p);			snprint(buf, sizeof buf, "%.*s", (int)(q-p), p);			return buf;		} 		sprint(search, "\n%.2ux00", asc);		if(p = strstr(codes, search)) {			p += 6;			if((q = strchr(p, '\n')) == nil)				q = p+strlen(p);			snprint(buf, sizeof buf, "(ascq #%.2ux) %.*s", ascq, (int)(q-p), p);			return buf;		}	} 	sprint(buf, "scsi #%.2ux %.2ux", asc, ascq);	return buf;}  static int_scsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io, int dolock){	uchar resp[16];	int n;	long status; 	if(dolock)		qlock(s);	if(write(s->rawfd, cmd, ccount) != ccount) {		werrstr("cmd write: %r");		if(dolock)			qunlock(s);		return -1;	} 	switch(io){	case Sread:		n = read(s->rawfd, data, dcount);		if(n < 0 && scsiverbose)			fprint(2, "dat read: %r: cmd 0x%2.2uX\n", cmd[0]);		break;	case Swrite:		n = write(s->rawfd, data, dcount);		if(n != dcount && scsiverbose)			fprint(2, "dat write: %r: cmd 0x%2.2uX\n", cmd[0]);		break;	default:	case Snone:		n = write(s->rawfd, resp, 0);		if(n != 0 && scsiverbose)			fprint(2, "none write: %r: cmd 0x%2.2uX\n", cmd[0]);		break;	} 	memset(resp, 0, sizeof(resp));	if(read(s->rawfd, resp, sizeof(resp)) < 0) {		werrstr("resp read: %r\n");		if(dolock)			qunlock(s);		return -1;	}	if(dolock)		qunlock(s); 	resp[sizeof(resp)-1] = '\0';	status = atoi((char*)resp);	if(status == 0)		return n; 	werrstr("cmd %2.2uX: status %luX dcount %d n %d", cmd[0], status, dcount, n);	return -1;} intscsicmd(Scsi *s, uchar *cmd, int ccount, void *data, int dcount, int io){	return _scsicmd(s, cmd, ccount, data, dcount, io, 1);} static int_scsiready(Scsi *s, int dolock){	uchar cmd[6], resp[16];	int status, i; 	if(dolock)		qlock(s);	for(i=0; i<3; i++) {		memset(cmd, 0, sizeof(cmd));		cmd[0] = 0x00;	/* unit ready */		if(write(s->rawfd, cmd, sizeof(cmd)) != sizeof(cmd)) {			if(scsiverbose)				fprint(2, "ur cmd write: %r\n");			goto bad;		}		write(s->rawfd, resp, 0);		if(read(s->rawfd, resp, sizeof(resp)) < 0) {			if(scsiverbose)				fprint(2, "ur resp read: %r\n");			goto bad;		}		resp[sizeof(resp)-1] = '\0';		status = atoi((char*)resp);		if(status == 0 || status == 0x02) {			if(dolock)				qunlock(s);			return 0;		}		if(scsiverbose)			fprint(2, "target: bad status: %x\n", status);	bad:;	}	if(dolock)		qunlock(s);	return -1;} intscsiready(Scsi *s){	return _scsiready(s, 1);} intscsi(Scsi *s, uchar *cmd, int ccount, void *v, int dcount, int io){	uchar req[6], sense[255], *data;	int tries, code, key, n;	char *p; 	data = v;	SET(key, code);	qlock(s);	for(tries=0; tries<2; tries++) {		n = _scsicmd(s, cmd, ccount, data, dcount, io, 0);		if(n >= 0) {			qunlock(s);			return n;		} 		/*		 * request sense		 */		memset(req, 0, sizeof(req));		req[0] = 0x03;		req[4] = sizeof(sense);		memset(sense, 0xFF, sizeof(sense));		if((n=_scsicmd(s, req, sizeof(req), sense, sizeof(sense), Sread, 0)) < 14)			if(scsiverbose)				fprint(2, "reqsense scsicmd %d: %r\n", n);			if(_scsiready(s, 0) < 0)			if(scsiverbose)				fprint(2, "unit not ready\n");			key = sense[2];		code = sense[12];		if(code == 0x17 || code == 0x18) {	/* recovered errors */			qunlock(s);			return dcount;		}		if(code == 0x28 && cmd[0] == 0x43) {	/* get info and media changed */			s->nchange++;			s->changetime = time(0);			continue;		}	} 	/* drive not ready, or medium not present */	if(cmd[0] == 0x43 && key == 2 && (code == 0x3a || code == 0x04)) {		s->changetime = 0;		qunlock(s);		return -1;	}	qunlock(s); 	if(cmd[0] == 0x43 && key == 5 && code == 0x24)	/* blank media */		return -1; 	p = scsierror(code, sense[13]); 	werrstr("cmd #%.2ux: %s", cmd[0], p); 	if(scsiverbose)		fprint(2, "scsi cmd #%.2ux: %.2ux %.2ux %.2ux: %s\n", cmd[0], key, code, sense[13], p); //	if(key == 0)//		return dcount;	return -1;} Scsi*openscsi(char *dev){	Scsi *s;	int rawfd, ctlfd, l, n;	char *name, *p, buf[512]; 	l = strlen(dev)+1+3+1;	name = malloc(l);	if(name == nil)		return nil; 	snprint(name, l, "%s/raw", dev);	if((rawfd = open(name, ORDWR)) < 0) {		free(name);		return nil;	} 	snprint(name, l, "%s/ctl", dev);	if((ctlfd = open(name, ORDWR)) < 0) {		free(name);	Error:		close(rawfd);		return nil;	}	free(name); 	n = readn(ctlfd, buf, sizeof buf);	close(ctlfd);	if(n <= 0)		goto Error; 	if(strncmp(buf, "inquiry ", 8) != 0 || (p = strchr(buf, '\n')) == nil)		goto Error;	*p = '\0'; 	if((p = strdup(buf+8)) == nil)		goto Error; 	s = malloc(sizeof(*s));	if(s == nil) {	Error1:		free(p);		goto Error;	}	memset(s, 0, sizeof(*s)); 	s->rawfd = rawfd;	s->inquire = p;	s->changetime = time(0);		if(scsiready(s) < 0)		goto Error1; 	return s;}