493 lines · 8.7 KB · C
RawDownload
#include "u.h"#include "lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "ureg.h"#include "error.h" #include "sd.h" static intscsitest(SDreq* r){	r->write = 0;	memset(r->cmd, 0, sizeof(r->cmd));	r->cmd[1] = r->lun<<5;	r->clen = 6;	r->data = nil;	r->dlen = 0;	r->flags = 0; 	r->status = ~0; // cgascreenputs("A", 1);	return r->unit->dev->ifc->rio(r);} intscsiverify(SDunit* unit){	static SDreq *r;	int i, status;	static uchar *inquiry; 	if((r = sdmalloc(r, sizeof(SDreq))) == nil)		return 0;	if((inquiry = sdmalloc(inquiry, sizeof(unit->inquiry))) == nil)		return 0; 	r->unit = unit;	r->lun = 0;		/* ??? */ 	memset(unit->inquiry, 0, sizeof(unit->inquiry));	r->write = 0;	r->cmd[0] = 0x12;	r->cmd[1] = r->lun<<5;	r->cmd[4] = sizeof(unit->inquiry)-1;	r->clen = 6;	r->data = inquiry;	r->dlen = sizeof(unit->inquiry)-1;	r->flags = 0; 	r->status = ~0;// cgascreenputs("B", 1);	if(unit->dev->ifc->rio(r) != SDok){		return 0;	}	memmove(unit->inquiry, inquiry, r->dlen); 	SET(status);	for(i = 0; i < 3; i++){		while((status = scsitest(r)) == SDbusy)			;		if(status == SDok || status != SDcheck)			break;		if(!(r->flags & SDvalidsense))			break;		if((r->sense[2] & 0x0F) != 0x02)			continue; 		/*		 * Unit is 'not ready'.		 * If it is in the process of becoming ready or needs		 * an initialising command, set status so it will be spun-up		 * below.		 * If there's no medium, that's OK too, but don't		 * try to spin it up.		 */		if(r->sense[12] == 0x04){			if(r->sense[13] == 0x02 || r->sense[13] == 0x01){				status = SDok;				break;			}		}		if(r->sense[12] == 0x3A)			break;	} 	if(status == SDok){		/*		 * Try to ensure a direct-access device is spinning.		 * Don't wait for completion, ignore the result.		 */		if((unit->inquiry[0] & 0x1F) == 0){			memset(r->cmd, 0, sizeof(r->cmd));			r->write = 0;			r->cmd[0] = 0x1B;			r->cmd[1] = (r->lun<<5)|0x01;			r->cmd[4] = 1;			r->clen = 6;			r->data = nil;			r->dlen = 0;			r->flags = 0; 			r->status = ~0;			unit->dev->ifc->rio(r);		}	}//	free(r); 	if(status == SDok || status == SDcheck)		return 1;	return 0;} intreturn0(void*){	return 0;} static intscsirio(SDreq* r){	/*	 * Perform an I/O request, returning	 *	-1	failure	 *	 0	ok	 *	 1	no medium present	 *	 2	retry	 * The contents of r may be altered so the	 * caller should re-initialise if necesary.	 */	r->status = ~0;// cgascreenputs("C", 1);	switch(r->unit->dev->ifc->rio(r)){	default:		break;	case SDcheck:		if(!(r->flags & SDvalidsense))			break;		switch(r->sense[2] & 0x0F){		case 0x00:		/* no sense */		case 0x01:		/* recovered error */			return 2;		case 0x06:		/* check condition */			/*			 * 0x28 - not ready to ready transition,			 *	  medium may have changed.			 * 0x29 - power on or some type of reset.			 */			if(r->sense[12] == 0x28 && r->sense[13] == 0)				return 2;			if(r->sense[12] == 0x29)				return 2;			break;		case 0x02:		/* not ready */			/*			 * If no medium present, bail out.			 * If unit is becoming ready, rather than not			 * not ready, wait a little then poke it again.			 */			if(r->sense[12] == 0x3A)				break;			if(r->sense[12] != 0x04 || r->sense[13] != 0x01)				break; 			tsleep(nil, return0, 0, 500);			scsitest(r);			return 2;		default:			break;		}		break;	case SDok:		return 0;	}	return -1;} static voidcap10(SDreq *r){	r->cmd[0] = 0x25;	r->cmd[1] = r->lun<<5;	r->clen = 10;	r->dlen = 8;} static voidcap16(SDreq *r){	uint i; 	i = 32;	r->cmd[0] = 0x9e;	r->cmd[1] = 0x10;	r->cmd[10] = i>>24;	r->cmd[11] = i>>16;	r->cmd[12] = i>>8;	r->cmd[13] = i;	r->clen = 16;	r->dlen = i;} static uintbelong(uchar *u){	return u[0]<<24 | u[1]<<16 | u[2]<<8 | u[3];} static uvlongcapreply(SDreq *r, ulong *secsize){	uchar *u;	ulong ss;	uvlong s; 	*secsize = 0;	u = r->data;	if(r->clen == 16){		s = (uvlong)belong(u)<<32 | belong(u + 4);		ss = belong(u + 8);	}else{		s = belong(u);		ss = belong(u + 4);	}	/*	 * Some ATAPI CD readers lie about the block size.	 * Since we don't read audio via this interface	 * it's okay to always fudge this.	 */	if(ss == 2352)		ss = 2048;	/*	 * Devices with removable media may return 0 sectors	 * when they have empty media (e.g. sata dvd writers);	 * if so, keep the count zero.	 *	 * Read-capacity returns the LBA of the last sector,	 * therefore the number of sectors must be incremented.	 */	if(s != 0)		s++;	*secsize = ss;	return s;} intscsionline(SDunit* unit){	int ok, retries;	void (*cap)(SDreq*);	static SDreq *r;	static uchar *p; 	if((r = sdmalloc(r, sizeof(SDreq))) == nil)		return 0; 	if((p = sdmalloc(p, 32)) == nil)		return 0; 	ok = 0;	cap = cap10;	r->unit = unit;	r->lun = 0;				/* ??? */	for(retries = 0; retries < 10; retries++){		/*		 * Read-capacity is mandatory for DA, WORM, CD-ROM and		 * MO. It may return 'not ready' if type DA is not		 * spun up, type MO or type CD-ROM are not loaded or just		 * plain slow getting their act together after a reset.		 */		r->write = 0;		r->data = p;		r->flags = 0;		memset(r->cmd, 0, sizeof r->cmd);		cap(r); 		r->status = ~0;		switch(scsirio(r)){		default:			break;		case 0:			unit->sectors = capreply(r, &unit->secsize);			if(unit->secsize == 0)				continue;			if(unit->sectors == 0xffffffff && cap == cap10){				cap = cap16;				continue;			}			ok = 1;			break;		case 1:			ok = 1;			break;		case 2:			continue;		}		break;	} 	return ok;} intscsiexec(SDunit* unit, int write, uchar* cmd, int clen, void* data, int* dlen){	static SDreq *r;	int status; 	if((r = sdmalloc(r, sizeof(SDreq))) == nil)		return SDmalloc;	r->unit = unit;	r->lun = cmd[1]>>5;		/* ??? */	r->write = write;	memmove(r->cmd, cmd, clen);	r->clen = clen;	r->data = data;	if(dlen)		r->dlen = *dlen;	r->flags = 0; 	r->status = ~0; 	/*	 * Call the device-specific I/O routine.	 * There should be no calls to 'error()' below this	 * which percolate back up.	 */// cgascreenputs("D", 1);	switch(status = unit->dev->ifc->rio(r)){	case SDok:		if(dlen)			*dlen = r->rlen;		/*FALLTHROUGH*/	case SDcheck:		/*FALLTHROUGH*/	default:		/*		 * It's more complicated than this. There are conditions		 * which are 'ok' but for which the returned status code		 * is not 'SDok'.		 * Also, not all conditions require a reqsense, might		 * need to do a reqsense here and make it available to the		 * caller somehow.		 *		 * Mañana.		 */		break;	} 	return status;} static voidscsifmt10(SDreq *r, int write, int lun, ulong nb, uvlong bno){	uchar *c; 	c = r->cmd;	if(write == 0)		c[0] = 0x28;	else		c[0] = 0x2A;	c[1] = lun<<5;	c[2] = bno>>24;	c[3] = bno>>16;	c[4] = bno>>8;	c[5] = bno;	c[6] = 0;	c[7] = nb>>8;	c[8] = nb;	c[9] = 0; 	r->clen = 10;} static voidscsifmt16(SDreq *r, int write, int lun, ulong nb, uvlong bno){	uchar *c; 	c = r->cmd;	if(write == 0)		c[0] = 0x88;	else		c[0] = 0x8A;	c[1] = lun<<5;		/* so wrong */	c[2] = bno>>56;	c[3] = bno>>48;	c[4] = bno>>40;	c[5] = bno>>32;	c[6] = bno>>24;	c[7] = bno>>16;	c[8] = bno>>8;	c[9] = bno;	c[10] = nb>>24;	c[11] = nb>>16;	c[12] = nb>>8;	c[13] = nb;	c[14] = 0;	c[15] = 0; 	r->clen = 16;} longscsibio(SDunit* unit, int lun, int write, void* data, long nb, uvlong bno){	static SDreq *r;	long rlen; 	if((r = sdmalloc(r, sizeof(SDreq))) == nil)		return SDmalloc; 	r->unit = unit;	r->lun = lun;again:	r->write = write;	if(bno > 0xffffffff)		scsifmt16(r, write, lun, nb, bno);	else		scsifmt10(r, write, lun, nb, bno);	r->data = data;	r->dlen = nb*unit->secsize;	r->flags = 0; 	r->status = ~0;// cgascreenputs("E", 1);	switch(scsirio(r)){	default:		rlen = -1;		break;	case 0:		rlen = r->rlen;		break;	case 2:		rlen = -1;		if(!(r->flags & SDvalidsense))			break;		switch(r->sense[2] & 0x0F){		default:			break;		case 0x01:		/* recovered error */			print("%s: recovered error at sector %llud\n",				unit->name, bno);			rlen = r->rlen;			break;		case 0x06:		/* check condition */			/*			 * Check for a removeable media change.			 * If so, mark it by zapping the geometry info			 * to force an online request.			 */			if(r->sense[12] != 0x28 || r->sense[13] != 0)				break;			if(unit->inquiry[1] & 0x80)				unit->sectors = 0;			break;		case 0x02:		/* not ready */			/*			 * If unit is becoming ready,			 * rather than not not ready, try again.			 */			if(r->sense[12] == 0x04 && r->sense[13] == 0x01)				goto again;			break;		}		break;	} 	return rlen;} SDev*scsiid(SDev* sdev, SDifc* ifc){	static char idno[16] = "0123456789abcdef";	static char *p = idno; 	while(sdev){		if(sdev->ifc == ifc){			sdev->idno = *p++;			if(p >= &idno[sizeof(idno)])				break;		}		sdev = sdev->next;	} 	return nil;}