Browse Source

Plan 9 from Bell Labs 2010-02-28

David du Colombier 14 years ago
parent
commit
595c512bdd
1 changed files with 424 additions and 0 deletions
  1. 424 0
      sys/src/9/kw/sdscsi.c

+ 424 - 0
sys/src/9/kw/sdscsi.c

@@ -0,0 +1,424 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+#include "../port/sd.h"
+
+static int
+scsitest(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;
+
+	return r->unit->dev->ifc->rio(r);
+}
+
+int
+scsiverify(SDunit* unit)
+{
+	SDreq *r;
+	int i, status;
+	uchar *inquiry;
+
+	if((r = malloc(sizeof(SDreq))) == nil)
+		return 0;
+	if((inquiry = sdmalloc(sizeof(unit->inquiry))) == nil){
+		free(r);
+		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;
+	if(unit->dev->ifc->rio(r) != SDok){
+		free(r);
+		return 0;
+	}
+	memmove(unit->inquiry, inquiry, r->dlen);
+	free(inquiry);
+
+	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;
+}
+
+static int
+scsirio(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;
+	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;
+
+			while(waserror())
+				;
+			tsleep(&up->sleep, return0, 0, 500);
+			poperror();
+			scsitest(r);
+			return 2;
+		default:
+			break;
+		}
+		break;
+	case SDok:
+		return 0;
+	}
+	return -1;
+}
+
+int
+scsionline(SDunit* unit)
+{
+	SDreq *r;
+	uchar *p;
+	int ok, retries;
+
+	if((r = malloc(sizeof(SDreq))) == nil)
+		return 0;
+	if((p = sdmalloc(8)) == nil){
+		free(r);
+		return 0;
+	}
+
+	ok = 0;
+
+	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;
+		memset(r->cmd, 0, sizeof(r->cmd));
+		r->cmd[0] = 0x25;
+		r->cmd[1] = r->lun<<5;
+		r->clen = 10;
+		r->data = p;
+		r->dlen = 8;
+		r->flags = 0;
+
+		r->status = ~0;
+		switch(scsirio(r)){
+		default:
+			break;
+		case 0:
+			unit->sectors = (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
+			unit->secsize = (p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7];
+
+			/*
+			 * 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(unit->secsize == 2352)
+				unit->secsize = 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(unit->sectors != 0)
+				unit->sectors++;
+			ok = 1;
+			break;
+		case 1:
+			ok = 1;
+			break;
+		case 2:
+			continue;
+		}
+		break;
+	}
+	free(p);
+	free(r);
+
+	if(ok)
+		return ok+retries;
+	else
+		return 0;
+}
+
+int
+scsiexec(SDunit* unit, int write, uchar* cmd, int clen, void* data, int* dlen)
+{
+	SDreq *r;
+	int status;
+
+	if((r = malloc(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.
+	 */
+	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;
+	}
+	sdfree(r);
+
+	return status;
+}
+
+static void
+scsifmt10(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 void
+scsifmt16(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;
+}
+
+long
+scsibio(SDunit* unit, int lun, int write, void* data, long nb, uvlong bno)
+{
+	SDreq *r;
+	long rlen;
+
+	if((r = malloc(sizeof(SDreq))) == nil)
+		error(Enomem);
+	r->unit = unit;
+	r->lun = lun;
+again:
+	r->write = write;
+	if(bno >= (1ULL<<32))
+		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;
+	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;
+	}
+	free(r);
+
+	return rlen;
+}
+