388 lines · 7.4 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * 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> enum {	/* commands */	Testrdy		= 0x00,	Reqsense	= 0x03,	Write10		= 0x2a,	Writever10	= 0x2e,	Readtoc		= 0x43, 	/* sense[2] (key) sense codes */	Sensenone	= 0,	Sensenotrdy	= 2,	Sensebadreq	= 5, 	/* sense[12] (asc) sense codes */	Lunnotrdy	= 0x04,	Recovnoecc	= 0x17,	Recovecc	= 0x18,	Badcdb		= 0x24,	Newmedium	= 0x28,	Nomedium	= 0x3a,}; 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) {		snprint(search, sizeof 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;		} 		snprint(search, sizeof 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;		}	} 	snprint(buf, sizeof 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);		/* read toc errors are frequent and not very interesting */		if(n < 0 && (scsiverbose == 1 ||		    scsiverbose == 2 && cmd[0] != Readtoc))			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){	char err[ERRMAX];	uchar cmd[6], resp[16];	int status, i; 	if(dolock)		qlock(s);	werrstr("");	for(i=0; i<3; i++) {		memset(cmd, 0, sizeof(cmd));		cmd[0] = Testrdy;	/* unit ready */		if(write(s->rawfd, cmd, sizeof(cmd)) != sizeof(cmd)) {			if(scsiverbose)				fprint(2, "ur cmd write: %r\n");			werrstr("short unit-ready raw write");			continue;		}		write(s->rawfd, resp, 0);		if(read(s->rawfd, resp, sizeof(resp)) < 0) {			if(scsiverbose)				fprint(2, "ur resp read: %r\n");			continue;		}		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);	}	rerrstr(err, sizeof err);	if(err[0] == '\0')		werrstr("unit did not become ready");	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] = Reqsense;		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] & 0xf;		code = sense[12];			/* asc */		if(code == Recovnoecc || code == Recovecc) { /* recovered errors */			qunlock(s);			return dcount;		} 		/* retry various odd cases */		if(code == Newmedium && cmd[0] == Readtoc) {			/* read toc and media changed */			s->nchange++;			s->changetime = time(0);		} else if((cmd[0] == Write10 || cmd[0] == Writever10) &&		    key == Sensenotrdy &&		    code == Lunnotrdy && sense[13] == 0x08) {			/* long write in progress, per mmc-6 */			tries = 0;			sleep(1);		}	} 	/* drive not ready, or medium not present */	if(cmd[0] == Readtoc && key == Sensenotrdy &&	    (code == Nomedium || code == Lunnotrdy)) {		s->changetime = 0;		qunlock(s);		return -1;	}	qunlock(s); 	if(cmd[0] == Readtoc && key == Sensebadreq && code == Badcdb)		return -1;			/* blank media */ 	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 == Sensenone)//		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) {	Error:		free(name);		close(rawfd);		return nil;	} 	n = readn(ctlfd, buf, sizeof buf);	close(ctlfd);	if(n <= 0) {		if(n == 0)			werrstr("eof on %s", name);		goto Error;	} 	if(strncmp(buf, "inquiry ", 8) != 0 || (p = strchr(buf, '\n')) == nil) {		werrstr("inquiry mal-formatted in %s", name);		goto Error;	}	*p = '\0';	free(name);	name = nil; 	if((p = strdup(buf+8)) == nil)		goto Error; 	s = mallocz(sizeof(*s), 1);	if(s == nil) {	Error1:		free(p);		goto Error;	} 	s->rawfd = rawfd;	s->inquire = p;	s->changetime = time(0);		if(scsiready(s) < 0)		goto Error1; 	return s;} voidclosescsi(Scsi *s){	close(s->rawfd);	free(s->inquire);	free(s);}