code: 9ferno

Download patch

ref: 10cf5557ab4ab54278b6079da5539eb1cc59a597
parent: a069ed018555d165681cf3f02a2f00f565d9150f
author: joe9 <joe9mail@gmail.com>
date: Wed Jul 14 14:28:13 EDT 2021

using 9front sd drivers

--- a/os/pc/sdscsi.c
+++ /dev/null
@@ -1,393 +1,0 @@
-#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:
-		return -1;
-	case SDcheck:
-		if(!(r->flags & SDvalidsense))
-			return -1;
-		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;
-			return -1;
-		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)
-				return 1;
-			if(r->sense[12] != 0x04 || r->sense[13] != 0x01)
-				return -1;
-
-			while(waserror())
-				;
-			tsleep(&up->sleep, return0, 0, 500);
-			poperror();
-			scsitest(r);
-			return 2;
-		default:
-			return -1;
-		}
-	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];
-			if(unit->sectors == 0)
-				continue;
-			/*
-			 * Read-capacity returns the LBA of the last sector,
-			 * therefore the number of sectors must be incremented.
-			 */
-			unit->sectors++;
-			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;
-			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;
-}
-
-long
-scsibio(SDunit* unit, int lun, int write, void* data, long nb, long bno)
-{
-	SDreq *r;
-	long rlen;
-
-	if((r = malloc(sizeof(SDreq))) == nil)
-		error(Enomem);
-	r->unit = unit;
-	r->lun = lun;
-again:
-	r->write = write;
-	if(write == 0)
-		r->cmd[0] = 0x28;
-	else
-		r->cmd[0] = 0x2A;
-	r->cmd[1] = (lun<<5);
-	r->cmd[2] = bno>>24;
-	r->cmd[3] = bno>>16;
-	r->cmd[4] = bno>>8;
-	r->cmd[5] = bno;
-	r->cmd[6] = 0;
-	r->cmd[7] = nb>>8;
-	r->cmd[8] = nb;
-	r->cmd[9] = 0;
-	r->clen = 10;
-	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 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;
-}
-
-SDev*
-scsiid(SDev* sdev, SDifc* ifc)
-{
-	char name[32];
-	static char idno[16] = "0123456789abcdef";
-	static char *p = idno;
-
-	while(sdev){
-		if(sdev->ifc == ifc){
-			sdev->idno = *p++;
-			snprint(name, sizeof(name), "sd%c", sdev->idno);
-			kstrdup(&sdev->name, name);
-			if(p >= &idno[sizeof(idno)])
-				break;
-		}
-		sdev = sdev->next;
-	}
-
-	return nil;
-}
--- a/os/pc/sdvirtio.c
+++ b/os/pc/sdvirtio.c
@@ -67,7 +67,7 @@
 struct Vdesc
 {
 	u64int	addr;
-	u32	len;
+	u32int	len;
 	u16int	flags;
 	u16int	next;
 };
@@ -74,8 +74,8 @@
 
 struct Vused
 {
-	u32	id;
-	u32	len;
+	u32int	id;
+	u32int	len;
 };
 
 struct Vqueue
@@ -128,16 +128,16 @@
 
 struct ScsiCfg
 {
-	u32	num_queues;
-	u32	seg_max;
-	u32	max_sectors;
-	u32	cmd_per_lun;
-	u32	event_info_size;
-	u32	sense_size;
-	u32	cdb_size;
+	u32int	num_queues;
+	u32int	seg_max;
+	u32int	max_sectors;
+	u32int	cmd_per_lun;
+	u32int	event_info_size;
+	u32int	sense_size;
+	u32int	cdb_size;
 	u16int	max_channel;
 	u16int	max_target;
-	u32	max_lun;
+	u32int	max_lun;
 };
 
 static Vqueue*
@@ -336,8 +336,8 @@
 
 	u8int status;
 	struct Vioblkreqhdr {
-		u32	typ;
-		u32	prio;
+		u32int	typ;
+		u32int	prio;
 		u64int	lba;
 	} req;
 
@@ -396,7 +396,7 @@
 	u8int resp[4+4+2+2+SENSESIZE];
 	u8int req[8+8+3+CDBSIZE];
 	int free, head;
-	u32 len;
+	u32int len;
 	Vqueue *q;
 	Vdesc *d;
 	Vdev *vd;
@@ -469,7 +469,7 @@
 		r->status = SDcheck;
 
 	/* sense_len */
-	len = *((u32*)&resp[0]);
+	len = *((u32int*)&resp[0]);
 	if(len > 0){
 		if(len > sizeof(r->sense))
 			len = sizeof(r->sense);
@@ -478,7 +478,7 @@
 	}
 
 	/* data residue */
-	len = *((u32*)&resp[4]);
+	len = *((u32int*)&resp[4]);
 	if(len > r->dlen)
 		r->rlen = 0;
 	else
@@ -611,7 +611,7 @@
 
 	id = 'F';
 	for(vd =  viopnpdevs(TypBlk); vd; vd = vd->next){
-		if(vd->nqueue != 1)
+		if(vd->nqueue == 0)
 			continue;
 
 		if((s = malloc(sizeof(*s))) == nil)
@@ -680,7 +680,6 @@
 
 	viopnp,				/* pnp */
 	nil,				/* legacy */
-	nil,				/* id */
 	vioenable,			/* enable */
 	viodisable,			/* disable */
 
@@ -693,8 +692,6 @@
 	viobio,				/* bio */
 	nil,				/* probe */
 	nil,				/* clear */
-	nil,				/* stat */
 	nil,				/* rtopctl */
 	nil,				/* wtopctl */
-	nil,				/* ataio */
 };
--- a/os/port/devsd.c
+++ b/os/port/devsd.c
@@ -1284,13 +1284,13 @@
 		if(unit->sectors){
 			if(unit->dev->ifc->rctl == nil)
 				l += snprint(p+l, m-l,
-					"geometry %ud %ud\n",
+					"geometry %llud %lud\n",
 					unit->sectors, unit->secsize);
 			pp = unit->part;
 			for(i = 0; i < unit->npart; i++){
 				if(pp->valid)
 					l += snprint(p+l, m-l,
-						"part %s %lud %lud\n",
+						"part %s %llud %llud\n",
 						pp->name, pp->start, pp->end);
 				pp++;
 			}
--- a/os/port/sd.h
+++ b/os/port/sd.h
@@ -17,8 +17,8 @@
 };
 
 struct SDpart {
-	ulong	start;
-	ulong	end;
+	uvlong	start;
+	uvlong	end;
 	SDperm;
 	int	valid;
 	ulong	vers;
@@ -34,7 +34,7 @@
 struct SDunit {
 	SDev*	dev;
 	int	subno;
-	uchar	inquiry[256];		/* format follows SCSI spec */
+	uchar	inquiry[255];		/* format follows SCSI spec */
 	uchar	sense[18];		/* format follows SCSI spec */
 	uchar	rsense[18];		/* support seperate rq sense and inline return */
 	uchar	haversense;
@@ -41,11 +41,11 @@
 	SDperm;
 
 	QLock	ctl;
-	u32	sectors;
-	u32	secsize;
+	uvlong	sectors;
+	ulong	secsize;
 	SDpart*	part;			/* nil or array of size npart */
 	int	npart;
-	u32	vers;
+	ulong	vers;
 	SDperm	ctlperm;
 
 	QLock	raw;			/* raw read or write in progress */
@@ -57,17 +57,15 @@
 	int	nefile;
 };
 
-/* 
+/*
  * Each controller is represented by a SDev.
- * Each controller is responsible for allocating its unit structures.
- * Each controller has at least one unit.
- */ 
+ */
 struct SDev {
 	Ref	r;			/* Number of callers using device */
 	SDifc*	ifc;			/* pnp/legacy */
 	void*	ctlr;
 	int	idno;
-	char*	name;
+	char	name[8];
 	SDev*	next;
 
 	QLock;				/* enable/disable */
@@ -82,8 +80,7 @@
 	char*	name;
 
 	SDev*	(*pnp)(void);
-	SDev*	(*legacy)(int, int);
-	SDev*	(*id)(SDev*);
+	SDev*	(*xxlegacy)(int, int);		/* unused.  remove me */
 	int	(*enable)(SDev*);
 	int	(*disable)(SDev*);
 
@@ -96,7 +93,6 @@
 	long	(*bio)(SDunit*, int, int, void*, long, uvlong);
 	SDev*	(*probe)(DevConf*);
 	void	(*clear)(SDev*);
-	char*	(*stat)(SDev*, char*, char*);
 	char*	(*rtopctl)(SDev*, char*, char*);
 	int	(*wtopctl)(SDev*, Cmdbuf*);
 	int	(*ataio)(SDreq*);
@@ -105,10 +101,10 @@
 struct SDreq {
 	SDunit*	unit;
 	int	lun;
-	int	write;
+	char	write;
 	char	proto;
 	char	ataproto;
-	uchar	cmd[16];
+	uchar	cmd[0x20];
 	int	clen;
 	void*	data;
 	int	dlen;
@@ -117,7 +113,7 @@
 
 	int	status;
 	long	rlen;
-	uchar	sense[256];
+	uchar	sense[32];
 };
 
 enum {
@@ -162,7 +158,7 @@
 	int	(*init)(void);
 	void	(*enable)(void);
 	int	(*inquiry)(char*, int);
-	int	(*cmd)(u32, u32, u32*);
+	int	(*cmd)(u32int, u32int, u32int*);
 	void	(*iosetup)(int, void*, int, int);
 	void	(*io)(int, uchar*, int);
 	char	highspeed;
@@ -180,5 +176,4 @@
 /* sdscsi.c */
 extern int scsiverify(SDunit*);
 extern int scsionline(SDunit*);
-extern long scsibio(SDunit*, int, int, void*, long, long);
-extern SDev* scsiid(SDev*, SDifc*);
+extern long scsibio(SDunit*, int, int, void*, long, uvlong);
--- /dev/null
+++ b/os/port/sdscsi.c
@@ -1,0 +1,461 @@
+#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] = 36;
+	r->clen = 6;
+	r->data = inquiry;
+	r->dlen = 36;
+	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;
+}
+
+static void
+cap10(SDreq *r)
+{
+	r->cmd[0] = 0x25;
+	r->cmd[1] = r->lun<<5;
+	r->clen = 10;
+	r->dlen = 8;
+}
+
+static void
+cap16(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 uint
+belong(uchar *u)
+{
+	return u[0]<<24 | u[1]<<16 | u[2]<<8 | u[3];
+}
+
+static uvlong
+capreply(SDreq *r, ulong *secsize)
+{
+	uchar *u;
+	ulong ss;
+	uvlong s;
+
+	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);
+	}
+	if(secsize)
+		*secsize = ss;
+	return s;
+}
+
+int
+scsionline(SDunit* unit)
+{
+	SDreq *r;
+	uchar *p;
+	ulong ss;
+	uvlong s;
+	int ok, retries;
+	void (*cap)(SDreq*);
+
+	if((r = malloc(sizeof *r)) == nil)
+		return 0;
+	if((p = sdmalloc(32)) == nil){
+		free(r);
+		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);
+
+		switch(scsirio(r)){
+		default:
+			/*
+			 * ATAPI returns error and no sense information
+			 * on media change / no media present.
+			 * count as retries.
+			 */
+			if(retries < 4)
+				continue;
+			break;
+		case 0:
+			s = capreply(r, &ss);
+			if(s == 0xffffffff && cap == cap10){
+				cap = cap16;
+				continue;
+			}
+			if(s == 0xffffffffffffffffLL)
+				s = 0;
+
+			/*
+			 * 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++;
+
+			ok = (unit->sectors != s) ? 2 : 1;
+			unit->sectors = s;
+			unit->secsize = ss;
+			break;
+		case 1:
+			ok = (unit->sectors != 0) ? 2 : 1;
+			unit->sectors = 0;
+			break;
+		case 2:
+			continue;
+		}
+		break;
+	}
+	free(p);
+	free(r);
+
+	/*
+	print("scsionline: %s: ok=%d retries=%d sectors=%llud secsize=%lud\n",
+		unit->name, ok, retries, unit->sectors, unit->secsize);
+	*/
+
+	if(ok)
+		return ok+retries;
+	else
+		return 0;
+}
+
+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;
+
+	r = smalloc(sizeof(SDreq));
+	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;
+	switch(scsirio(r)){
+	default:
+		rlen = -1;
+		break;
+	case 0:
+		/*
+		 * scsi allows commands to return successfully
+		 * but return sense data, indicating that the
+		 * operation didn't proceed as expected.
+		 * (confusing, no).  this allows the raw commands
+		 * to successfully return errors.  but any sense
+		 * data bio sees must be an error.  bomb out.
+		 */
+		if(r->status == SDok && r->rlen > 0
+		&& ((r->flags & SDvalidsense) == 0 || r->sense[2] == 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;
+		}
+		snprint(up->genbuf, sizeof up->genbuf, "%s %.2ux%.2ux%.2ux %lld",
+			Eio, r->sense[2], r->sense[12], r->sense[13], bno);
+		free(r);
+		error(up->genbuf);
+		break;
+	}
+	free(r);
+
+	return rlen;
+}
+