Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/port/devsd.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


## diffname port/devsd.c 1994/0913
## diff -e /dev/null /n/fornaxdump/1994/0913/sys/src/brazil/port/devsd.c
0a
/*
 *  SCSI disc driver
 */
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"
#include	"devtab.h"

enum
{
	LogNpart	= 3,
	Npart		= 1<<LogNpart,
	Ndisk		= 32,
	Nlun		= 8,
};
#define DRIVE(qid)	((qid).path>>LogNpart)
#define PART(qid)	((qid).path&(Npart-1))

typedef struct Part Part;
typedef struct Disk Disk;

struct Part
{
	ulong	beg;
	ulong	end;
	char	name[NAMELEN];
};

struct Disk
{
	QLock;
	Target*	t;
	uchar	lun;
	char	id[NAMELEN];
	char	vol[NAMELEN];

	uchar*	inquire;
	ulong	size;
	ulong	bsize;

	int	npart;
	Part	table[Npart];
};

int	ndisk;
Disk	disk[Ndisk];

static	void	sdrdpart(Disk*);
static	long	sdio(Chan*, int, char*, ulong, ulong);

static int
sdgen(Chan *c, Dirtab *tab, long ntab, long s, Dir *dirp)
{
	Qid qid;
	Disk *d;
	Part *p;
	int unit;
	char name[2*NAMELEN];

	USED(tab, ntab);

	d = disk;
	while(s >= d->npart) {
		s -= d->npart;
		d++;
	}
	unit = d - disk;
	if(unit > ndisk)
		return -1;

	p = d->table+s;
	sprint(name, "%s%s", d->vol, p->name);
	name[NAMELEN-1] = '\0';
	qid = (Qid){(unit<<LogNpart)+s, 0};
	devdir(c, qid, name, (p->end - p->beg) * d->bsize, eve, 0666, dirp);
	return 1;
}

void
sdreset(void)
{
}

void
sdinit(void)
{
	Disk *d;
	ulong s, b;
	int dev, i;

	dev = 0;
	for(;;) {
		d = &disk[ndisk];
		dev = scsiinv(dev, 0, &d->t, &d->inquire, d->id);
		if(dev < 0)
			break;

		if(scsistart(d->t, 0, 1) != STok)
			continue;

		/* Search for other lun's */
		for(i = 0; i < Nlun; i++) {
			d->lun = i;
			scsireqsense(d->t, d->lun, 1);

			/* NCR Raid only seems to answer second capacity
			 * command if lun != 0
			 */
			if(scsicap(d->t, d->lun, &s, &b) != STok) {
				scsireqsense(d->t, 0, 1);
				continue;
			}
			scsireqsense(d->t, 0, 1);

			s = 0;
			b = 0;
			if(scsicap(d->t, d->lun, &s, &b) != STok) {
				scsireqsense(d->t, 0, 1);
				continue;
			}

			if(scsireqsense(d->t, d->lun, 1) != STok)
				continue;

			if(s == 0 || b == 0)
				continue;

			d->size = s;
			d->bsize = b;
			sprint(d->vol, "sd%d", ndisk);

			if(++ndisk >= Ndisk)
				break;
			d++;
			d->t = d[-1].t;
			d->inquire = d[-1].inquire;
			strcpy(d->id, d[-1].id);
		}

		if(ndisk >= Ndisk) {
			print("devsd: configure more disks\n");
			break;
		}
	}
}

Chan*
sdattach(char *spec)
{
	int i;

	for(i = 0; i < ndisk; i++)
		sdrdpart(&disk[i]);
	
	return devattach('w', spec);
}

Chan *
sdclone(Chan *c, Chan *nc)
{
	return devclone(c, nc);
}

int
sdwalk(Chan *c, char *name)
{
	return devwalk(c, name, 0, 0, sdgen);
}

void
sdstat(Chan *c, char *db)
{
	devstat(c, db, 0, 0, sdgen);
}

Chan *
sdopen(Chan *c, int omode)
{
	return devopen(c, omode, 0, 0, sdgen);
}

void
sdcreate(Chan *c, char *name, int omode, ulong perm)
{
	USED(c, name, omode, perm);
	error(Eperm);
}

void
sdremove(Chan *c)
{
	USED(c);
	error(Eperm);
}

void
sdwstat(Chan *c, char *dp)
{
	USED(c, dp);
	error(Eperm);
}

void
sdclose(Chan *c)
{
	Disk *d;
	Part *p;

	if(c->mode != OWRITE && c->mode != ORDWR)
		return;

	d = &disk[DRIVE(c->qid)];
	p = &d->table[PART(c->qid)];
	if(strcmp(p->name, "partition"))
		return;

	sdrdpart(d);
}

long
sdread(Chan *c, void *a, long n, ulong offset)
{
	if(c->qid.path == CHDIR)
		return devdirread(c, a, n, 0, 0, sdgen);

	return sdio(c, 0, a, n, offset);
}

long
sdwrite(Chan *c, char *a, long n, ulong offset)
{
	return sdio(c, 1, a, n, offset);
}

static void
sdrdpart(Disk *d)
{
	Part *p;
	int n, i;
	char *b, *line[Npart+2], *field[3];
	static char MAGIC[] = "plan9 partitions";

	b = scsialloc(d->bsize);
	if(b == 0)
		error(Enomem);

	qlock(d);
	
	p = d->table;

	strcpy(p->name, "disk");
	p->beg = 0;
	p->end = d->size + 1;
	p++;
	strcpy(p->name, "partition");
	p->beg = d->table[0].end - 1;
	p->end = d->table[0].end;
	p++;
	d->npart = 2;

	scsibio(d->t, d->lun, SCSIread, b, 1, d->bsize, d->table[0].end-1);
	b[d->bsize-1] = '\0';

	/*
	 *  parse partition table.
	 */
	n = getfields(b, line, Npart+2, '\n');
	if(n > 0 && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0) {
		for(i = 1; i < n; i++) {
			switch(getfields(line[i], field, 3, ' ')) {
			case 2:
				if(strcmp(field[0], "unit") == 0)
					strncpy(d->vol, field[1], NAMELEN);
				break;	
			case 3:
				strncpy(p->name, field[0], NAMELEN);
				p->beg = strtoul(field[1], 0, 0);
				p->end = strtoul(field[2], 0, 0);
				if(p->beg > p->end || p->beg >= d->table[0].end)
					break;
				p++;
			}
		}
	}
	d->npart = p - d->table;
	scsifree(b);
	qunlock(d);
}

static long
sdio(Chan *c, int write, char *a, ulong len, ulong offset)
{
	Disk *d;
	Part *p;
	uchar *b;
	ulong block, n, max, x;

	d = &disk[DRIVE(c->qid)];
	p = &d->table[PART(c->qid)];

	block = (offset / d->bsize) + p->beg;
	n = (offset + len + d->bsize - 1) / d->bsize + p->beg - block;
	max = SCSImaxxfer / d->bsize;
	if(n > max)
		n = max;
	if(block + n > p->end)
		n = p->end - block;
	if(block >= p->end || n == 0)
		return 0;

	b = scsialloc(n*d->bsize);
	if(b == 0)
		error(Enomem);
	if(waserror()) {
		scsifree(b);
		nexterror();
	}
	offset %= d->bsize;
	if(write) {
		if(offset || len % d->bsize) {
			x = scsibio(d->t, d->lun, SCSIread, b, n, d->bsize, block);
			if(x < n * d->bsize) {
				n = x / d->bsize;
				x = n * d->bsize - offset;
				if(len > x)
					len = x;
			}
		}
		memmove(b + offset, a, len);
		x = scsibio(d->t, d->lun, SCSIwrite, b, n, d->bsize, block);
		if(x < offset)
			len = 0;
		else
		if(len > x - offset)
			len = x - offset;
	}
	else {
		x = scsibio(d->t, d->lun, SCSIread, b, n, d->bsize, block);
		if(x < offset)
			len = 0;
		else
		if(len > x - offset)
			len = x - offset;
		memmove(a, b+offset, len);
	}
	poperror();
	scsifree(b);
	return len;
}
.
## diffname port/devsd.c 1994/1228
## diff -e /n/fornaxdump/1994/0913/sys/src/brazil/port/devsd.c /n/fornaxdump/1994/1228/sys/src/brazil/port/devsd.c
278a
				if(p >= &d->table[Npart])
					break;
.
147a
	print("sd out\n");
.
14c
	LogNpart	= 4,
.
## diffname port/devsd.c 1994/1230
## diff -e /n/fornaxdump/1994/1228/sys/src/brazil/port/devsd.c /n/fornaxdump/1994/1230/sys/src/brazil/port/devsd.c
148d
## diffname port/devsd.c 1995/0108
## diff -e /n/fornaxdump/1994/1230/sys/src/brazil/port/devsd.c /n/fornaxdump/1995/0108/sys/src/brazil/port/devsd.c
235a
}

long
sdbwrite(Chan *c, Block *bp, ulong offset)
{
	return devbwrite(c, bp, offset);
.
231a
Block*
sdbread(Chan *c, long n, ulong offset)
{
	return devbread(c, n, offset);
}

.
## diffname port/devsd.c 1995/0117
## diff -e /n/fornaxdump/1995/0108/sys/src/brazil/port/devsd.c /n/fornaxdump/1995/0117/sys/src/brazil/port/devsd.c
285c
			switch(getfields(line[i], field, 3, " ")) {
.
282c
	n = getfields(b, line, Npart+2, "\n");
.
## diffname port/devsd.c 1995/0324
## diff -e /n/fornaxdump/1995/0117/sys/src/brazil/port/devsd.c /n/fornaxdump/1995/0324/sys/src/brazil/port/devsd.c
214a
print("sdclose: qid 0x%lux drive %d, part %d\n", c->qid, DRIVE(c->qid), PART(c->qid));
.
## diffname port/devsd.c 1995/0325
## diff -e /n/fornaxdump/1995/0324/sys/src/brazil/port/devsd.c /n/fornaxdump/1995/0325/sys/src/brazil/port/devsd.c
227c
	if(c->qid.path & CHDIR)
.
218,221c
	if((c->mode&3) != OREAD && strcmp(p->name, "partition") == 0)
		sdrdpart(d);
.
215d
212c
	if(c->qid.path & CHDIR)
.
202d
200c
sdwstat(Chan*, char*)
.
195d
193c
sdremove(Chan*)
.
188d
186c
sdcreate(Chan*, char*, int, ulong)
.
63,64d
55c
sdgen(Chan *c, Dirtab*, long, long s, Dir *dirp)
.
## diffname port/devsd.c 1995/0329
## diff -e /n/fornaxdump/1995/0325/sys/src/brazil/port/devsd.c /n/fornaxdump/1995/0329/sys/src/brazil/port/devsd.c
105a

			/*
			 * A SCSI target does not support a lun if the
			 * the peripheral device type and qualifier fields
			 * in the response to an inquiry command are 0x7F.
			 */
			memset(inq, 0, sizeof(inq));
			nbytes = sizeof(inq);
			if(scsiinquiry(d->t, d->lun, inq, &nbytes) != STok || inq[0] == 0x7F)
				continue;
.
104a

.
90c
	uchar inq[255];
	int dev, i, nbytes;
.
## diffname port/devsd.c 1995/0403
## diff -e /n/fornaxdump/1995/0329/sys/src/brazil/port/devsd.c /n/fornaxdump/1995/0403/sys/src/brazil/port/devsd.c
321a

	if(write && (d->inquire[0] & 0x1F) == TypeCD)
		error(Eperm);
.
280a
	if((d->inquire[0] & 0x1F) == TypeCD){
		scsifree(b);
		qunlock(d);
		return;
	}

.
144a
			case TypeDA:
			case TypeWO:
			case TypeMO:
				sprint(d->vol, "sd%d", ndisk);
				break;

			case TypeCD:
				sprint(d->vol, "cd%d", ndisk);
				break;

			default:
				continue;
			}

.
143c
			switch(d->inquire[0] & 0x1F){
.
96c
		dev = scsiinv(dev, types, &d->t, &d->inquire, d->id);
.
53a
static int types[] = {
	TypeDA, TypeWO, TypeCD, TypeMO,
	-1,
};

.
11a
enum {
	TypeDA		= 0x00,		/* Direct Access */
	TypeWO		= 0x04,		/* Worm */
	TypeCD		= 0x05,		/* CD-ROM */
	TypeMO		= 0x07,		/* rewriteable Magneto-Optical */
};

.
## diffname port/devsd.c 1995/0404
## diff -e /n/fornaxdump/1995/0403/sys/src/brazil/port/devsd.c /n/fornaxdump/1995/0404/sys/src/brazil/port/devsd.c
169a
print("sd6|");
.
158d
151a
print("sd5/0x%2.2ux|", d->inquire[0]);
.
147,149d
145a
print("sd4/%d/%d|", s, b);
.
143c
				scsireqsense(d->t, 0, 0);
.
131,139d
129a
print("sd3|");
.
119,120d
117a
print("sd2|");
.
112,113c
		if(scsitest(d->t, 0) < 0)
			scsireqsense(d->t, 0, 0);
		if(scsistart(d->t, 0, 1) < 0)
			scsireqsense(d->t, 0, 0);
print("sd1|");
.
62c
	TypeDA, TypeCD, TypeMO,
.
16a
	TYpeMC		= 0x08,		/* Medium Changer */
.
13a
	TypeSA		= 0x01,		/* Sequential Access */
.
## diffname port/devsd.c 1995/0405
## diff -e /n/fornaxdump/1995/0404/sys/src/brazil/port/devsd.c /n/fornaxdump/1995/0405/sys/src/brazil/port/devsd.c
287d
283a
	/*
	 * If the drive wasn't ready when we tried to do a
	 * read-capacity earlier (in sdinit()), try again.
	 * It might be possible to be smarter here, and look at the
	 * response from a test-unit-ready which would show if the
	 * target was in the process of becoming ready.
	 */
	if(d->size == 0 || d->bsize == 0){
		if(scsicap(d->t, d->lun, &d->size, &d->bsize) != STok){
			d->size = d->bsize = 0;
			error(Eio);
		}
	}

.
164d
148,151d
144,146c
			switch(type){
.
142d
140a
			type = scratch[0] & 0x1F;

			/*
			 * Read-capacity is mandatory for TypeDA, TypeMO and TypeCD.
			 * It may return 'not ready' if TypeDA is not spun up,
			 * TypeMO or TypeCD are not loaded or just plain slow getting
			 * their act together after a reset.
			 * If 'not ready' comes back, try starting a TypeDA and punt
			 * the get capacity until the drive is attached.
			 * It might be possible to be smarter here, and look at the
			 * response from a test-unit-ready which would show if the
			 * target was in the process of becoming ready.
			 */
			if(scsicap(d->t, d->lun, &d->size, &d->bsize) != STok) {
				nbytes = sizeof(scratch);
				memset(scratch, 0, nbytes);
				scsireqsense(d->t, 0, scratch, &nbytes, 1);
				if((scratch[2] & 0x0F) != 0x02)
					continue;
				if(type == TypeDA)
					scsistart(d->t, d->lun, 0);
				d->size = d->bsize = 0;
.
134,139c
			if(scratch[0] == 0x7F)
.
130,132c
			nbytes = sizeof(scratch);
			memset(scratch, 0, nbytes);
			if(scsiinquiry(d->t, d->lun, scratch, &nbytes) != STok)
.
123d
114,120c
		/*
		 * Search for all the lun's.
		 */
.
103,104c
	uchar scratch[0xFF], type;
.
## diffname port/devsd.c 1995/0416
## diff -e /n/fornaxdump/1995/0405/sys/src/brazil/port/devsd.c /n/fornaxdump/1995/0416/sys/src/brazil/port/devsd.c
364a

	if(d->bsize == 0)
		error(Eio);
.
287,300d
189,191c
	/*
	 * If the drive wasn't ready when we tried to do a
	 * read-capacity earlier (in sdinit()), try again.
	 * It might be possible to be smarter here, and look at the
	 * response from a test-unit-ready which would show if the
	 * target was in the process of becoming ready.
	 */
	nfound = 0;
	for(i = 0; i < ndisk; i++){	
		d = &disk[i];
		if(waserror()){
			poperror();
			continue;
		}
		if(d->size == 0 || d->bsize == 0){
			if(scsicap(d->t, d->lun, &d->size, &d->bsize) != STok){
				d->size = d->bsize = 0;
				continue;
			}
		}
		sdrdpart(d);
		nfound++;
		poperror();
	}

	if(nfound == 0)
		return 0;
.
187c
	int i, nfound;
	Disk *d;
.
## diffname port/devsd.c 1995/0417
## diff -e /n/fornaxdump/1995/0416/sys/src/brazil/port/devsd.c /n/fornaxdump/1995/0417/sys/src/brazil/port/devsd.c
376,378d
311a
	/*
	 * If the drive wasn't ready when we tried to do a
	 * read-capacity earlier (in sdinit()), try again.
	 * It might be possible to be smarter here, and look at the
	 * response from a test-unit-ready which would show if the
	 * target was in the process of becoming ready.
	 */
	if(d->size == 0 || d->bsize == 0){
		if(scsicap(d->t, d->lun, &d->size, &d->bsize) != STok){
			d->size = d->bsize = 0;
			error(Eio);
		}
	}

.
190,216c
	for(i = 0; i < ndisk; i++)
		sdrdpart(&disk[i]);
	
.
187,188c
	int i;
.
## diffname port/devsd.c 1995/0722
## diff -e /n/fornaxdump/1995/0417/sys/src/brazil/port/devsd.c /n/fornaxdump/1995/0722/sys/src/brazil/port/devsd.c
63c
static int types[] =
{
.
12c
enum
{
.
## diffname port/devsd.c 1995/0909
## diff -e /n/fornaxdump/1995/0722/sys/src/brazil/port/devsd.c /n/fornaxdump/1995/0909/sys/src/brazil/port/devsd.c
149c
				if((scratch[2] & 0x0F) != 0x02 && (scratch[2] & 0x0F) != 0)
.
## diffname port/devsd.c 1996/0223
## diff -e /n/fornaxdump/1995/0909/sys/src/brazil/port/devsd.c /n/fornaxdump/1996/0223/sys/src/brazil/port/devsd.c
10d
## diffname port/devsd.c 1996/0315
## diff -e /n/fornaxdump/1996/0223/sys/src/brazil/port/devsd.c /n/fornaxdump/1996/0315/sys/src/brazil/port/devsd.c
334c
			switch(parsefields(line[i], field, 3, " ")) {
.
331c
	n = parsefields(b, line, Npart+2, "\n");
.
## diffname port/devsd.c 1996/0607
## diff -e /n/fornaxdump/1996/0315/sys/src/brazil/port/devsd.c /n/fornaxdump/1996/0607/sys/src/brazil/port/devsd.c
415c

buggery:
.
407a
		if(x < 0){
			len = -1;
			goto buggery;
		}
.
399a
		if(x < 0){
			len = -1;
			goto buggery;
		}
.
390a
			if(x < 0){
				len = -1;
				goto buggery;
			}
.
382,386c
		return scsierrstr(STnomem);

.
367,369d
353c

	d->partok = 1;

	return STok;
.
334,348c
			if(parsefields(line[i], field, 3, " ") != 3)
				break;
			if(p >= &d->table[Npart])
				break;
			strncpy(p->name, field[0], NAMELEN);
			p->beg = strtoul(field[1], 0, 0);
			p->end = strtoul(field[2], 0, 0);
			if(p->beg > p->end || p->beg >= d->table[0].end)
				break;
			p++;
.
319,324d
309,311d
304,307c
		return scsierrstr(STnomem);
.
301a
	p->end = d->size + 1;

	if((d->inquire[0] & 0x1F) == TypeCD)
		return STok;

.
298c
			return scsierrstr(n);
.
296c
		n = scsicap(d->t, d->lun, &d->size, &d->bsize);
		if(n != STok){
.
289,293c
	 * If the drive wasn't ready when a read-capacity was tried
	 * earlier (in sdinit()), try again.
.
287a
	if(d->partok)
		return STok;

	p = d->table;
	strcpy(p->name, "disk");
	p->beg = 0;
	p->end = 0;
	d->npart = 1;

.
280c
static int
.
270a
	Disk *d;

	d = &disk[DRIVE(c->qid)];

	if((d->inquire[0] & 0x1F) == TypeCD)
		error(Eperm);
.
250a
		qunlock(d);
	}
.
249c
	if((c->mode&3) != OREAD && strcmp(p->name, "partition") == 0){
		qlock(d);
		d->partok = 0;
.
190,191c
	for(i = 0; i < ndisk; i++){
		qlock(&disk[i]);
		if(disk[i].partok == 0)
			sdrdpart(&disk[i]);
		qunlock(&disk[i]);
	}
.
140,142d
60c
static	int	sdrdpart(Disk*);
.
49a
	int	partok;
	ulong	version;
.
## diffname port/devsd.c 1996/0710
## diff -e /n/fornaxdump/1996/0607/sys/src/brazil/port/devsd.c /n/fornaxdump/1996/0710/sys/src/brazil/port/devsd.c
181a
	scsifree(scratch);
.
127c
			nbytes = 0xFF;
.
109a
	scratch = scsialloc(0xFF);
.
106c
	uchar *scratch, type;
.
## diffname port/devsd.c 1996/1225
## diff -e /n/fornaxdump/1996/0710/sys/src/brazil/port/devsd.c /n/fornaxdump/1996/1225/sys/src/brazil/port/devsd.c
72c
sdgen(Chan *c, Dirtab*, int, int s, Dir *dirp)
.
## diffname port/devsd.c 1997/0327
## diff -e /n/fornaxdump/1996/1225/sys/src/brazil/port/devsd.c /n/emeliedump/1997/0327/sys/src/brazil/port/devsd.c
349c
			if(parsefields(line[i], field, nelem(field), " ") != 3)
.
346c
	n = parsefields(b, line, nelem(line), "\n");
.
289,293c
Dev sddevtab = {
	devreset,
	sdinit,
	sdattach,
	devclone,
	sdwalk,
	sdstat,
	sdopen,
	devcreate,
	sdclose,
	sdread,
	devbread,
	sdwrite,
	devbwrite,
	devremove,
	devwstat,
};
.
271,277c
static long
.
262c
static long
.
225,243c
static void
.
219c
static Chan*
.
213c
static void
.
201,207c
static int
.
186c
static Chan*
.
145c
				nbytes = 0xFF;
.
97,102c
static void
.
## diffname port/devsd.c 1997/0408
## diff -e /n/emeliedump/1997/0327/sys/src/brazil/port/devsd.c /n/emeliedump/1997/0408/sys/src/brazil/port/devsd.c
254a
	'w',
	"sd",

.
## diffname port/devsd.c 1998/0319
## diff -e /n/emeliedump/1997/0408/sys/src/brazil/port/devsd.c /n/emeliedump/1998/0319/sys/src/brazil/port/devsd.c
245a
	ulong offset = off;
.
243c
sdwrite(Chan *c, char *a, long n, vlong off)
.
235a
	ulong offset = off;

.
234c
sdread(Chan *c, void *a, long n, vlong off)
.
## diffname port/devsd.c 1998/0325
## diff -e /n/emeliedump/1998/0319/sys/src/brazil/port/devsd.c /n/emeliedump/1998/0325/sys/src/brazil/port/devsd.c
358a
	ulong offset = off;
.
353c
sdio(Chan *c, int write, char *a, ulong len, vlong off)
.
248d
245c
sdwrite(Chan *c, char *a, long n, vlong offset)
.
236d
234c
sdread(Chan *c, void *a, long n, vlong offset)
.
63c
static	long	sdio(Chan*, int, char*, ulong, vlong);
.
## diffname port/devsd.c 1998/0327
## diff -e /n/emeliedump/1998/0325/sys/src/brazil/port/devsd.c /n/emeliedump/1998/0327/sys/src/brazil/port/devsd.c
376c
	offset = off % d->bsize;
.
362,363c
	block = (off / d->bsize) + p->beg;
	n = (off + len + d->bsize - 1) / d->bsize + p->beg - block;
.
357c
	ulong offset;
.
252c
	return sdio(c, 1, a, n, off);
.
244c
sdwrite(Chan *c, char *a, long n, vlong off)
.
240c
	return sdio(c, 0, a, n, off);
.
234c
sdread(Chan *c, void *a, long n, vlong off)
.
## diffname port/devsd.c 1998/0512
## diff -e /n/emeliedump/1998/0327/sys/src/brazil/port/devsd.c /n/emeliedump/1998/0512/sys/src/brazil/port/devsd.c
192c

.
## diffname port/devsd.c 1998/0930
## diff -e /n/emeliedump/1998/0512/sys/src/brazil/port/devsd.c /n/emeliedump/1998/0930/sys/src/brazil/port/devsd.c
144a
				}
.
143c
				switch(scratch[2] & 0x0F){

				case 0x00:
				case 0x01:
				case 0x02:
					break;
				case 0x06:
					if(scratch[12] == 0x28 && scratch[13] == 0)
						break;
					if(scratch[12] == 0x29 && scratch[13] == 0)
						break;
					/*FALLTHROUGH*/
				default:
.
124a

.
## diffname port/devsd.c 1998/1022
## diff -e /n/emeliedump/1998/0930/sys/src/brazil/port/devsd.c /n/emeliedump/1998/1022/sys/src/brazil/port/devsd.c
93c
	l = (p->end - p->beg) * (vlong)d->bsize;
	devdir(c, qid, name, l, eve, 0666, dirp);
.
78a
	vlong l;
.
## diffname port/devsd.c 1999/0228
## diff -e /n/emeliedump/1998/1022/sys/src/brazil/port/devsd.c /n/emeliedump/1999/0228/sys/src/brazil/port/devsd.c
155c
					if(scratch[12] == 0x29 && !scratch[13])
.
153c
					if(scratch[12] == 0x28 && !scratch[13])
.
135,140c
			 * Read-capacity is mandatory for TypeDA, TypeMO and
			 * TypeCD. It may return 'not ready' if TypeDA is not
			 * spun up, TypeMO or TypeCD are not loaded or just
			 * plain slow getting their act together after a reset.
			 * If 'not ready' comes back, try starting a TypeDA and
			 * punt the get capacity until the drive is attached.
.
## diffname port/devsd.c 1999/0320
## diff -e /n/emeliedump/1999/0228/sys/src/brazil/port/devsd.c /n/emeliedump/1999/0320/sys/src/brazil/port/devsd.c
260c
sdwrite(Chan *c, void *a, long n, vlong off)
.
## diffname port/devsd.c 1999/0507
## diff -e /n/emeliedump/1999/0320/sys/src/brazil/port/devsd.c /n/emeliedump/1999/0507/sys/src/brazil/port/devsd.c
45c
	int	lun;
.
## diffname port/devsd.c 1999/0728
## diff -e /n/emeliedump/1999/0507/sys/src/brazil/port/devsd.c /n/emeliedump/1999/0728/sys/src/brazil/port/devsd.c
73a

.
## diffname port/devsd.c 1999/1230
## diff -e /n/emeliedump/1999/0728/sys/src/brazil/port/devsd.c /n/emeliedump/1999/1230/sys/src/9/port/devsd.c
81a
	if(s == DEVDOTDOT){
		devdir(c, qid, "#w", 0, eve, 0555, dirp);
		return 1;
	}

.
## diffname port/devsd.c 2000/0104
## diff -e /n/emeliedump/1999/1230/sys/src/9/port/devsd.c /n/emeliedump/2000/0104/sys/src/9/port/devsd.c
349d
344a
	n = parsefields(b, line, nelem(line), "\n");
	if(n <= 0 || strncmp(line[0], MAGIC, sizeof(MAGIC)-1) != 0){
		/* try the last */
		scsibio(d->t, d->lun, SCSIread, b, 1, d->bsize, d->table[0].end-1);
		b[d->bsize-1] = '\0';
		n = parsefields(b, line, nelem(line), "\n");
		/* only point partition file at last sector if there is one there */
		if(n > 0 && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0){
			d->table[1].beg++;
			d->table[1].end++;
		}
	}
.
343c
	/*
	 *  Read second last sector from disk, null terminate.
	 *  The last sector used to hold the partition tables.
	 *  However, this sector is special on some PC's so we've
	 *  started to use the second last sector as the partition
	 *  table instead.  To avoid reconfiguring all our old systems
	 *  we still check if there is a valid partition table in
	 *  the last sector if none is found in the second last.
	 */
	scsibio(d->t, d->lun, SCSIread, b, 1, d->bsize, d->table[0].end-2);
.
338,339c
	p->beg = d->table[0].end - 2;
	p->end = d->table[0].end - 1;
.
74d
## diffname port/devsd.c 2000/0308
## diff -e /n/emeliedump/2000/0104/sys/src/9/port/devsd.c /n/emeliedump/2000/0308/sys/src/9/port/devsd.c
371c
			if(getfields(line[i], field, nelem(field), 1, " ") != 3)
.
358c
		n = getfields(b, line, nelem(line), 1, "\n");
.
353c
	n = getfields(b, line, nelem(line), 1, "\n");
.
## diffname port/devsd.c 2000/0506
## diff -e /n/emeliedump/2000/0308/sys/src/9/port/devsd.c /n/emeliedump/2000/0506/sys/src/9/port/devsd.c
461a

Dev sddevtab = {
	'S',
	"sd",

	sdreset,
	devinit,
	sdattach,
	sdclone,
	sdwalk,
	sdstat,
	sdopen,
	devcreate,
	sdclose,
	sdread,
	devbread,
	sdwrite,
	devbwrite,
	devremove,
	sdwstat,
};
.
458,460c
	if(unit->changed)
		error(Enonexist);
	pp = &unit->part[PART(c->qid)];
	if(!pp->valid)
		error(Enonexist);
	if(strcmp(up->user, pp->user) && !iseve())
		error(Eperm);

	convM2D(dp, &d);
	strncpy(pp->user, d.uid, NAMELEN);
	pp->perm = d.mode&0777;

	qunlock(&unit->ctl);
	poperror();
.
444,455c

	return n;
}

static void
sdwstat(Chan* c, char* dp)
{
	Dir d;
	SDpart *pp;
	SDunit *unit;

	if((c->qid.path & CHDIR) || TYPE(c->qid) != Qpart)
		error(Eperm);

	unit = sdunit[UNIT(c->qid)];
	qlock(&unit->ctl);
	if(waserror()){
		qunlock(&unit->ctl);
		nexterror();
.
438,442c
		break;
	case Qpart:
		return sdbio(c, 1, a, n, off);
.
432,436c
		else{
			if(unit->pid != up->pid)
				error(Eperm);
			if(unit->state != Rawdata)
				error(Ebadusefd);
			unit->state = Rawstatus;

			unit->req->write = 1;
			return sdrio(unit->req, a, n);
.
430a
			if((req = malloc(sizeof(SDreq))) == nil){
				qunlock(&unit->raw);
				error(Enomem);
			}
			req->unit = unit;
			memmove(req->cmd, a, n);
			req->clen = n;
			req->flags = SDnosense;
			req->status = ~0;

			unit->req = req;
			unit->pid = up->pid;
			unit->state = Rawdata;
.
425,429c
			if(n < 6 || n > sizeof(req->cmd)){
				qunlock(&unit->raw);
				error(Ebadarg);
.
413,423c
		if(cb->nf < 1)
			error(Ebadctl);
		if(strcmp(cb->f[0], "part") == 0){
			if(cb->nf != 4 || unit->npart >= SDnpart)
				error(Ebadctl);
			if(unit->sectors == 0 && !sdinitpart(unit))
				error(Eio);
			start = strtoul(cb->f[2], 0, 0);
			end = strtoul(cb->f[3], 0, 0);
			sdaddpart(unit, cb->f[1], start, end);
		}
		else if(strcmp(cb->f[0], "delpart") == 0){
			if(cb->nf != 2 || unit->part == nil)
				error(Ebadctl);
			sddelpart(unit, cb->f[1]);
		}
		else if(unit->dev->ifc->wctl)
			unit->dev->ifc->wctl(unit, cb);
		else
			error(Ebadctl);
		qunlock(&unit->ctl);
		poperror();
		free(cb);
		break;
	case Qraw:
		unit = sdunit[UNIT(c->qid)];
		if(canqlock(&unit->raw)){
			if(unit->state != Rawcmd){
				qunlock(&unit->raw);
				error(Ebadusefd);
.
403,411c
		qlock(&unit->ctl);
		if(waserror()){
			qunlock(&unit->ctl);
			free(cb);
			nexterror();
		}
		if(unit->changed)
			error(Eio);
.
400,401c
	switch(TYPE(c->qid)){
	default:
		error(Eperm);
	case Qctl:
		cb = parsecmd(a, n);
		unit = sdunit[UNIT(c->qid)];
.
394,398c
	Cmdbuf *cb;
	SDreq *req;
	SDunit *unit;
	ulong end, start;
.
392c
sdwrite(Chan *c, void *a, long n, vlong off)
.
386,388c
	return 0;
.
383,384d
381a
		if(unit->pid != up->pid)
			error(Eperm);
		if(unit->state == Rawdata){
			unit->state = Rawstatus;
			return sdrio(unit->req, a, n);
		}
		else if(unit->state == Rawstatus){
			status = unit->req->status;
			unit->pid = 0;
			unit->state = Rawcmd;
			free(unit->req);
			unit->req = nil;
			qunlock(&unit->raw);
			return readnum(0, a, n, status, NUMSIZE);
		}
		break;
	case Qpart:
		return sdbio(c, 0, a, n, off);
.
364,380c
		qunlock(&unit->ctl);
		l = readstr(offset, a, n, p);
		free(p);
		return l;
	case Qraw:
		unit = sdunit[UNIT(c->qid)];
		if(canqlock(&unit->raw)){
			qunlock(&unit->raw);
			error(Ebadusefd);
.
342,362c
	offset = off;
	switch(TYPE(c->qid)){
	default:
		error(Eperm);
	case Qtopdir:
	case Qunitdir:
		return devdirread(c, a, n, 0, 0, sdgen);
	case Qctl:
		unit = sdunit[UNIT(c->qid)];
		p = malloc(READSTR);
		l = snprint(p, READSTR, "inquiry %.48s\n",
			(char*)unit->inquiry+8);
		qlock(&unit->ctl);
		if(!unit->changed && unit->sectors){
			/*
			 * If there's a device specific routine it must
			 * provide all information pertaining to night geometry
			 * and the garscadden trains.
			 */
			if(unit->dev->ifc->rctl)
				l += unit->dev->ifc->rctl(unit, p+l, READSTR-l);
			else
				l += snprint(p+l, READSTR-l,
					"geometry %ld %ld\n",
					unit->sectors, unit->secsize);
			pp = unit->part;
			for(i = 0; i < SDnpart; i++){
				if(pp->valid)
					l += snprint(p+l, READSTR-l,
						"part %.*s %lud %lud\n",
						NAMELEN, pp->name,
						pp->start, pp->end);
				pp++;
			}
.
335,340c
static long
sdread(Chan *c, void *a, long n, vlong off)
{
	char *p;
	SDpart *pp;
	SDunit *unit;
	ulong offset;
	int i, l, status;
.
331,333c
	return r->rlen;
}
.
328,329c
	if(!r->write && r->rlen > 0)
		memmove(a, data, r->rlen);
	if(data != nil){
		sdfree(data);
		r->data = nil;
	}
	poperror();
.
326c
	if(r->unit->dev->ifc->rio(r) != SDok)
		error(Eio);
.
323a
		nexterror();
.
314,322c
	if(waserror()){
		if(data != nil){
			sdfree(data);
			r->data = nil;
.
308,312c
	data = nil;
	if(n){
		if((data = sdmalloc(n)) == nil)
			error(Enomem);
		if(r->write)
			memmove(data, a, n);
	}
	r->data = data;
	r->dlen = n;
.
305,306c
	if(n >= SDmaxio || n < 0)
		error(Etoobig);
.
300,303c
	void *data;
.
297,298c
static long
sdrio(SDreq* r, void* a, long n)
.
280,295c
	return len;
}
.
276,278c
	if(unit->inquiry[1] & 0x80){
		qunlock(&unit->ctl);
		poperror();
	}
.
271,274c
	offset = off%unit->secsize;
	if(write){
		if(offset || (len%unit->secsize)){
			if((l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno)) < 0)
				error(Eio);
			if(l < (nb*unit->secsize)){
				nb = l/unit->secsize;
				l = nb*unit->secsize - offset;
				if(len > l)
					len = l;
			}
		}
		memmove(b+offset, a, len);
		if((l = unit->dev->ifc->bio(unit, 0, 1, b, nb, bno)) < 0)
			error(Eio);
		if(l < offset)
			len = 0;
		else if(len > l - offset)
			len = l - offset;
	}
	else {
		if((l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno)) < 0)
			error(Eio);
		if(l < offset)
			len = 0;
		else if(len > l - offset)
			len = l - offset;
		memmove(a, b+offset, len);
	}
	sdfree(b);
	poperror();
.
269c
	b = sdmalloc(nb*unit->secsize);
	if(b == nil)
		error(Enomem);
	if(waserror()){
		sdfree(b);
		nexterror();
	}
.
264,267c
	/*
	 * Check the request is within bounds.
	 * Removeable drives are locked throughout the I/O
	 * in case the media changes unexpectedly.
	 * Non-removeable drives are not locked during the I/O
	 * to allow the hardware to optimise if it can; this is
	 * a little fast and loose.
	 * It's assumed that non-removeable media parameters
	 * (sectors, secsize) can't change once the drive has
	 * been brought online.
	 */
	pp = &unit->part[PART(c->qid)];
	bno = (off/unit->secsize) + pp->start;
	nb = ((off+len+unit->secsize-1)/unit->secsize) + pp->start - bno;
	max = SDmaxio/unit->secsize;
	if(nb > max)
		nb = max;
	if(bno+nb > pp->end)
		nb = pp->end - bno;
	if(bno >= pp->end || nb == 0){
		if(write)
			error(Eio);
		qunlock(&unit->ctl);
		poperror();
		return 0;
	}
	if(!(unit->inquiry[1] & 0x80)){
		qunlock(&unit->ctl);
		poperror();
	}
.
261,262c
	qlock(&unit->ctl);
	if(waserror()){
		qunlock(&unit->ctl);
		nexterror();
	}
	if(unit->changed)
		error(Eio);
.
258,259c
	unit = sdunit[UNIT(c->qid)];
.
256a
	long l;
	uchar *b;
	SDpart *pp;
	SDunit *unit;
	ulong bno, max, nb, offset;
.
255c
sdbio(Chan* c, int write, char* a, long len, vlong off)
.
244,250c
	switch(TYPE(c->qid)){
	default:
		break;
	case Qraw:
		unit = sdunit[UNIT(c->qid)];
		if(canqlock(&unit->raw) || unit->pid == up->pid){
			unit->pid = 0;
			unit->state = Rawcmd;
			qunlock(&unit->raw);
		}
		break;
	case Qpart:
		unit = sdunit[UNIT(c->qid)];
		qlock(&unit->ctl);
		if(waserror()){
			qunlock(&unit->ctl);
			c->flag &= ~COPEN;
			nexterror();
		}
		pp = &unit->part[PART(c->qid)];
		pp->nopen--;
		unit->nopen--;
		if(unit->nopen == 0)
			unit->changed = 0;
		qunlock(&unit->ctl);
		poperror();
		break;
.
242a
	if(!(c->flag & COPEN))
		return;
.
238,239c
	SDpart *pp;
	SDunit *unit;
.
236c
sdclose(Chan* c)
.
232c
	SDpart *pp;
	SDunit *unit;

	c = devopen(c, omode, 0, 0, sdgen);
	switch(TYPE(c->qid)){
	default:
		break;
	case Qpart:
		unit = sdunit[UNIT(c->qid)];
		qlock(&unit->ctl);
		if(waserror()){
			qunlock(&unit->ctl);
			c->flag &= ~COPEN;
			nexterror();
		}
		if(unit->changed)
			error(Eio);
		pp = &unit->part[PART(c->qid)];
		pp->nopen++;
		unit->nopen++;
		qunlock(&unit->ctl);
		poperror();
		break;
	}
	return c;
.
230c
sdopen(Chan* c, int omode)
.
226c
	devstat(c, db, nil, 0, sdgen);
.
224c
sdstat(Chan* c, char* db)
.
220c
	return devwalk(c, name, nil, 0, sdgen);
.
218c
sdwalk(Chan* c, char* name)
.
216a
static Chan*
sdclone(Chan* c, Chan* nc)
{
	return devclone(c, nc);
}

.
214c
	if(spec[0] != 's' || spec[1] != 'd')
		error(Ebadspec);
	idno = spec[2];
	subno = strtol(&spec[3], &p, 0);
	if(p == &spec[3])
		error(Ebadspec);
	for(sdev = sdlist; sdev != nil; sdev = sdev->next){
		if(sdev->idno == idno && subno < sdev->nunit)
			break;
	}
	if(sdev == nil || sdgetunit(sdev, subno) == nil)
		error(Enonexist);

	c = devattach(sddevtab.dc, spec);
	c->qid = (Qid){QID(sdev->index+subno, 0, Qunitdir)|CHDIR, 0};
	c->dev = sdev->index+subno;
	return c;
.
207,211c
	if(sdnunit == 0 || *spec == '\0'){
		c = devattach(sddevtab.dc, spec);
		c->qid = (Qid){QID(0, 0, Qtopdir)|CHDIR, 0};
		return c;
.
205c
	Chan *c;
	char *p;
	SDev *sdev;
	int idno, subno;
.
203c
sdattach(char* spec)
.
199c

	return -1;
.
197a
		if(s < sdnunit){
			if(sdunit[s] == nil && sdindex2unit(s) == nil)
				return 0;
			q = (Qid){QID(s, 0, Qunitdir)|CHDIR, 0};
			devdir(c, q, sdunit[s]->name, 0, eve, 0555, dp);
			return 1;
		}
		s -= sdnunit;
		return sd1gen(c, s+Qtopbase, dp);
	case Qunitdir:
		if(s == DEVDOTDOT){
			q = (Qid){QID(0, 0, Qtopdir)|CHDIR, 0};
			snprint(name, NAMELEN, "#%C", sddevtab.dc);
			devdir(c, q, name, 0, eve, 0555, dp);
			return 1;
		}
		unit = sdunit[UNIT(c->qid)];
		qlock(&unit->ctl);
		if(!unit->changed && unit->sectors == 0)
			sdinitpart(unit);
		i = s+Qunitbase;
		if(i < Qpart){
			r = sd2gen(c, i, dp);
			qunlock(&unit->ctl);
			return r;
		}
		i -= Qpart;
		if(unit->npart == 0 || i >= SDnpart){
			qunlock(&unit->ctl);
			break;
		}
		pp = &unit->part[i];
		if(unit->changed || !pp->valid){
			qunlock(&unit->ctl);
			return 0;
		}
		l = (pp->end - pp->start) * (vlong)unit->secsize;
		q = (Qid){QID(UNIT(c->qid), i, Qpart), c->qid.vers};
		devdir(c, q, pp->name, l, pp->user, pp->perm, dp);
		qunlock(&unit->ctl);
		return 1;
	case Qraw:
	case Qctl:
	case Qpart:
		unit = sdunit[UNIT(c->qid)];
		qlock(&unit->ctl);
		r = sd2gen(c, TYPE(c->qid), dp);
		qunlock(&unit->ctl);
		return r;
	default:
		break;
.
196a
		pp = &unit->part[PART(c->qid)];
		l = (pp->end - pp->start) * (vlong)unit->secsize;
		q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qpart), c->qid.vers};
		devdir(c, q, pp->name, l, pp->user, pp->perm, dp);
		return 1;
	}	
	return -1;
}

static int
sd1gen(Chan*, int i, Dir*)
{
	switch(i){
	default:
		return -1;
	}
	return -1;
}

static int
sdgen(Chan* c, Dirtab*, int, int s, Dir* dp)
{
	Qid q;
	vlong l;
	int i, r;
	SDpart *pp;
	SDunit *unit;
	char name[NAMELEN];

	switch(TYPE(c->qid)){
	case Qtopdir:
		if(s == DEVDOTDOT){
			q = (Qid){QID(0, 0, Qtopdir)|CHDIR, 0};
			snprint(name, NAMELEN, "#%C", sddevtab.dc);
			devdir(c, q, name, 0, eve, 0555, dp);
			return 1;
.
194,195c
	/*
	 * Legacy and option code goes here. This will be hard...
	 */

	/*
	 * The maximum number of possible units is known, allocate
	 * placeholders for their datastructures; the units will be
	 * probed and structures allocated when attached.
	 * Allocate controller names for the different types.
	 */
	if(sdnunit == 0)
		return;
	if((sdunit = malloc(sdnunit*sizeof(SDunit*))) == nil)
		return;
	if((sdunitflg = malloc(sdnunit*sizeof(int))) == nil){
		free(sdunit);
		sdunit = nil;
		return;
	}
	for(i = 0; sdifc[i] != nil; i++){
		if(sdifc[i]->id)
			sdifc[i]->id(sdlist);
	}
}

static int
sd2gen(Chan* c, int i, Dir* dp)
{
	Qid q;
	vlong l;
	SDpart *pp;
	SDunit *unit;

	switch(i){
	case Qctl:
		q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qctl), c->qid.vers};
		devdir(c, q, "ctl", 0, eve, 0640, dp);
		return 1;
	case Qraw:
		q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qraw), c->qid.vers};
		devdir(c, q, "raw", 0, eve, 0600, dp);
		return 1;
	case Qpart:
		unit = sdunit[UNIT(c->qid)];
		if(unit->changed)
.
192a
		tail->index = sdnunit;
		sdnunit += tail->nunit;
	}
.
186,191c
static void
sdreset(void)
{
	int i;
	SDev *sdev, *tail;

	/*
	 * Probe all configured controllers and make a list
	 * of devices found, accumulating a possible maximum number
	 * of units attached and marking each device with an index
	 * into the linear top-level directory array of units.
	 */
	tail = nil;
	for(i = 0; sdifc[i] != nil; i++){
		if(sdifc[i]->pnp == nil || (sdev = sdifc[i]->pnp()) == nil)
			continue;
		if(sdlist != nil)
			tail->next = sdev;
		else
			sdlist = sdev;
		for(tail = sdev; tail->next != nil; tail = tail->next){
			sdev->index = sdnunit;
			sdnunit += tail->nunit;
.
182,184c
}
.
178,180c
	return nil;
.
173,176c
	/*
	 * Associate a unit with a given index into the top-level
	 * device directory.
	 * The device will be probed if it has not already been
	 * successfully accessed.
	 */
	for(sdev = sdlist; sdev != nil; sdev = sdev->next){
		if(index >= sdev->index && index < sdev->index+sdev->nunit)
			return sdgetunit(sdev, index-sdev->index);
	}
.
171c
static SDunit*
sdindex2unit(int index)
{
	SDev *sdev;
.
153,169c
	return unit;
}
.
139,151c
		/*
		 * No need to lock anything here as this is only
		 * called before the unit is made available in the
		 * sdunit[] array.
		 */
		if(unit->dev->ifc->verify(unit) == 0){
			qunlock(&sdqlock);
			free(unit);
			return nil;
		}
		sdunit[index] = unit;
	}
	qunlock(&sdqlock);
.
133,137c
		snprint(unit->name, NAMELEN, "%s%d", sdev->name, subno);
		unit->subno = subno;
		unit->dev = sdev;
.
125,131c
		if(sdev->enabled == 0 && sdev->ifc->enable)
			sdev->ifc->enable(sdev);
		sdev->enabled = 1;
.
122,123c
		if(sdunitflg[index]){
			qunlock(&sdqlock);
			return nil;
		}
		if((unit = malloc(sizeof(SDunit))) == nil){
			qunlock(&sdqlock);
			return nil;
		}
		sdunitflg[index] = 1;
.
120c
		 * Probe the unit only once. This decision
		 * may be a little severe and reviewed later.
.
111,118c
	/*
	 * Associate a unit with a given device and sub-unit
	 * number on that device.
	 * The device will be probed if it has not already been
	 * successfully accessed.
	 */
	qlock(&sdqlock);
	index = sdev->index+subno;
	unit = sdunit[index];
	if(unit == nil){
.
107,109c
	int index;
	SDunit *unit;
.
104,105c
static SDunit*
sdgetunit(SDev* sdev, int subno)
.
95,100c
	if(unit->dev->ifc->online)
		unit->dev->ifc->online(unit);
	if(unit->sectors){
		sdaddpart(unit, "data", 0, unit->sectors);
	
		/*
		 * Use partitions passed from boot program,
		 * e.g.
		 *	sdC0=dos 63 123123/plan9 123123 456456
		 */
		for(p = getconf(unit->name); p != nil; p = q){
			if(q = strchr(p, '/'))
				*q++ = '\0';
			nf = getfields(p, f, nelem(f), 1, " \t\r");
			if(nf < 3)
				continue;
		
			start = strtoul(f[1], 0, 0);
			end = strtoul(f[2], 0, 0);
			if(!waserror()){
				sdaddpart(unit, f[0], start, end);
				poperror();
			}
		}			
	}

.
91,93d
86,89c
	if(unit->inquiry[0] & 0xC0)
		return 0;
	switch(unit->inquiry[0] & 0x1F){
	case 0x00:			/* DA */
	case 0x04:			/* WORM */
	case 0x05:			/* CD-ROM */
	case 0x07:			/* MO */
		break;
	default:
		return 0;
.
81,83c
	unit->sectors = unit->secsize = 0;
	unit->npart = 0;
	if(unit->part){
		free(unit->part);
		unit->part = nil;
.
74,79c
	int nf;
	ulong start, end;
	char *f[4], *p, *q;
.
72c
sdinitpart(SDunit* unit)
.
65,69c
	unit->npart--;
	if(unit->npart == 0){
		free(unit->part);
		unit->part = nil;
	}
}
.
62,63c
	/*
	 * Look for the partition to delete.
	 * Can't delete if someone still has it open.
	 * If it's the last valid partition zap the
	 * whole table.
	 */
	pp = unit->part;
	for(i = 0; i < SDnpart; i++){
		if(strncmp(name, pp->name, NAMELEN) == 0)
			break;
		pp++;
	}
	if(i >= SDnpart)
		error(Ebadctl);
	if(pp->nopen)
		error(Einuse);
	pp->valid = 0;
.
59,60c
static void
sddelpart(SDunit* unit,  char* name)
{
	int i;
	SDpart *pp;
.
55,57c
	/*
	 * Check there is a free slot and size and extent are valid.
	 */
	if(partno == -1 || start > end || end > unit->sectors)
		error(Eio);
	pp = &unit->part[partno];
	pp->start = start;
	pp->end = end;
	strncpy(pp->name, name, NAMELEN);
	strncpy(pp->user, eve, NAMELEN);
	pp->perm = 0640;
	pp->valid = 1;
	unit->npart++;
}
.
49,53c
	/*
	 * Check name not already used
	 * and look for a free slot.
	 */
	if(unit->part != nil){
		partno = -1;
		for(i = 0; i < SDnpart; i++){
			pp = &unit->part[i];
			if(!pp->valid){
				if(partno == -1)
					partno = i;
				break;
			}
			if(strcmp(name, pp->name) == 0){
				if(pp->start == start && pp->end == end)
					return;
				error(Ebadctl);
			}
		}
	}
	else{
		if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil)
			error(Enomem);
		partno = 0;
	}
.
43,47c
	SDpart *pp;
	int i, partno;
.
41c
#define TYPE(q)		((q).path & 0x0F)
#define PART(q)		(((q).path>>4) & 0x0F)
#define UNIT(q)		(((q).path>>8) & 0xFF)
#define QID(u, p, t)	(((u)<<8)|((p)<<4)|(t))

static void
sdaddpart(SDunit* unit, char* name, ulong start, ulong end)
.
34,38c
	Qunitdir,			/* directory per unit */
	Qunitbase,
	Qctl		= Qunitbase,
	Qraw,
	Qpart,
.
31,32c
enum {
	Qtopdir		= 1,		/* top level directory */
	Qtopbase,
.
28,29d
21,26c
extern Dev sddevtab;
extern SDifc* sdifc[];

static QLock sdqlock;
static SDev* sdlist;
static SDunit** sdunit;
static int* sdunitflg;
static int sdnunit;

enum {
	Rawcmd,
	Rawdata,
	Rawstatus,
.
11,19c
#include "sd.h"
.
4,9c
#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"
.
2c
 * Storage Device.
.
## diffname port/devsd.c 2000/0515
## diff -e /n/emeliedump/2000/0506/sys/src/9/port/devsd.c /n/emeliedump/2000/0515/sys/src/9/port/devsd.c
301a
		/*
		 * BUG: no check is made here or later when a
		 * unit is attached that the id and name are set.
		 */
.
## diffname port/devsd.c 2000/0524
## diff -e /n/emeliedump/2000/0515/sys/src/9/port/devsd.c /n/emeliedump/2000/0524/sys/src/9/port/devsd.c
875c
	if(strncmp(up->user, pp->user, NAMELEN) && !iseve())
.
626c
		l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno);
		if(l < 0)
.
618c
		l = unit->dev->ifc->bio(unit, 0, 1, b, nb, bno);
		if(l < 0)
.
608c
			l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno);
			if(l < 0)
.
406a
		if(pp->user[0] == '\0')
			strncpy(pp->user, eve, NAMELEN);
.
334a
		if(pp->user[0] == '\0')
			strncpy(pp->user, eve, NAMELEN);
.
158a
		 * This happens before /boot sets hostname so the
		 * partitions will have the null-string for user.
		 * The gen functions patch it up.
.
112a
	if(strncmp(up->user, pp->user, NAMELEN) && !iseve())
		error(Eperm);
.
65c
			if(strncmp(name, pp->name, NAMELEN) == 0){
.
## diffname port/devsd.c 2000/0526
## diff -e /n/emeliedump/2000/0524/sys/src/9/port/devsd.c /n/emeliedump/2000/0526/sys/src/9/port/devsd.c
846,849c
			break;

		case Rawstatus:
			unit->state = Rawcmd;
			free(unit->req);
			unit->req = nil;
			error(Ebadusefd);

		case Rawdata:
.
844d
836d
832,834c
			if((req = malloc(sizeof(SDreq))) == nil)
.
824,830c
		switch(unit->state){
		case Rawcmd:
			if(n < 6 || n > sizeof(req->cmd))
.
821a

.
763d
759d
747,752d
531,535c
		unlock(&unit->rawinuse);
.
494a
	case Qraw:
		unit = sdunit[UNIT(c->qid)];
		if(!canlock(&unit->rawinuse))
			error(Einuse);
		unit->state = Rawcmd;
		break;
.
331c
		devdir(c, q, "raw", 0, eve, CHEXCL|0600, dp);
.
## diffname port/devsd.c 2000/0530
## diff -e /n/emeliedump/2000/0526/sys/src/9/port/devsd.c /n/emeliedump/2000/0530/sys/src/9/port/devsd.c
165c
		snprint(buf, sizeof buf, "%spart", unit->name);
		for(p = getconf(buf); p != nil; p = q){
.
160c
		 *	sdC0part=dos 63 123123/plan9 123123 456456
.
131c
	char *f[4], *p, *q, buf[10];
.
## diffname port/devsd.c 2000/0531
## diff -e /n/emeliedump/2000/0530/sys/src/9/port/devsd.c /n/emeliedump/2000/0531/sys/src/9/port/devsd.c
499a
		}
.
498c
		if(!canlock(&unit->rawinuse)){
			c->flag &= ~COPEN;
.
## diffname port/devsd.c 2000/0607
## diff -e /n/emeliedump/2000/0531/sys/src/9/port/devsd.c /n/emeliedump/2000/0607/sys/src/9/port/devsd.c
725,732c
			if(unit->dev->ifc->rctl == nil)
.
723a
		/*
		 * If there's a device specific routine it must
		 * provide all information pertaining to night geometry
		 * and the garscadden trains.
		 */
		if(unit->dev->ifc->rctl)
			l += unit->dev->ifc->rctl(unit, p+l, READSTR-l);
.
## diffname port/devsd.c 2000/0617
## diff -e /n/emeliedump/2000/0607/sys/src/9/port/devsd.c /n/emeliedump/2000/0617/sys/src/9/port/devsd.c
880c
	if(unit->vers+pp->vers != c->qid.vers)
.
877,878d
798c
			if(cb->nf != 4)
.
792c
		if(unit->vers != c->qid.vers)
.
731c
		if(unit->sectors == 0)
			sdinitpart(unit);
		if(unit->sectors){
.
591d
577c
	pp = &unit->part[PART(c->qid)];
	if(unit->vers+pp->vers != c->qid.vers)
.
573c
	while(waserror()){
		/* notification of media change; go around again */
		if(strcmp(up->error, Eio) == 0 && unit->sectors == 0 && nchange++ == 0){
			sdinitpart(unit);
			continue;
		}

		/* other errors; give up */
.
571a
	nchange = 0;
.
563a
	int nchange;
.
542,557d
527d
515,516c
		c->qid.vers = unit->vers+pp->vers;
.
512,513d
497a
		c->qid.vers = unit->vers;
.
495a
	case Qctl:
		unit = sdunit[UNIT(c->qid)];
		c->qid.vers = unit->vers;
		break;
.
414c
		q = (Qid){QID(UNIT(c->qid), i, Qpart), unit->vers+pp->vers};
.
409c
		if(!pp->valid){
.
404c
		if(unit->part == nil || i >= SDnpart){
.
396a

.
395c

		/*
		 * Check for media change.
		 * If one has already been detected, sectors will be zero.
		 * If there is one waiting to be detected, online will return > 1.
		 * Online is a bit of a large hammer but does the job.
		 */
		if(unit->sectors == 0 || (unit->dev->ifc->online && unit->dev->ifc->online(unit) > 1))
.
340c
		q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qpart), unit->vers+pp->vers};
.
335,337d
331c
		q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qraw), unit->vers};
.
327c
		q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qctl), unit->vers};
.
324a
	unit = sdunit[UNIT(c->qid)];
.
136,137c
		for(i = 0; i < SDnpart; i++){
			unit->part[i].valid = 0;
			unit->part[i].vers++;
		}
.
134d
132a
	unit->vers++;
.
129c
	int i, nf;
.
118,123c
	pp->vers++;
.
115,116d
102,103d
90d
81c
	if(partno == -1)
		error(Ebadctl);
	if(start > end || end > unit->sectors)
.
## diffname port/devsd.c 2000/0706
## diff -e /n/emeliedump/2000/0617/sys/src/9/port/devsd.c /n/emeliedump/2000/0706/sys/src/9/port/devsd.c
880,881c
	strncpy(perm->user, d.uid, NAMELEN);
	perm->perm = (perm->perm & ~0777) | (d.mode & 0777);
.
878a
	if(strncmp(up->user, perm->user, NAMELEN) && !iseve())
		error(Eperm);
.
877a
	case Qctl:
		perm = &unit->ctlperm;
		break;
	case Qraw:
		perm = &unit->rawperm;
		break;
	case Qpart:
		pp = &unit->part[PART(c->qid)];
		if(unit->vers+pp->vers != c->qid.vers)
			error(Enonexist);
		perm = &pp->SDperm;
		break;
	}
.
873,876c
	switch(TYPE(c->qid)){
	default:
.
863c
	if(c->qid.path & CHDIR)
.
860a
	SDperm *perm;
.
394c
		if(unit->sectors == 0
		|| (unit->dev->ifc->online && unit->dev->ifc->online(unit) > 1))
.
391c
		 * If there is one waiting to be detected, online
		 * will return > 1.
.
373c
			if(unit->user[0] == '\0')
				strncpy(unit->user, eve, NAMELEN);
			devdir(c, q, unit->name, 0, unit->user, unit->perm, dp);
.
370,371c
			if((unit = sdunit[s]) == nil){
				if((unit = sdindex2unit(s)) == nil)
					return 0;
			}
.
327c
		perm = &unit->rawperm;
		if(perm->user[0] == '\0'){
			strncpy(perm->user, eve, NAMELEN);
			perm->perm = CHEXCL|0600;
		}
		devdir(c, q, "raw", 0, perm->user, perm->perm, dp);
.
323c
		perm = &unit->ctlperm;
		if(perm->user[0] == '\0'){
			strncpy(perm->user, eve, NAMELEN);
			perm->perm = 0640;
		}
		devdir(c, q, "ctl", 0, perm->user, perm->perm, dp);
.
316a
	SDperm *perm;
.
213a
		strncpy(unit->user, eve, NAMELEN);
		unit->perm = 0555;
.
## diffname port/devsd.c 2000/0809
## diff -e /n/emeliedump/2000/0706/sys/src/9/port/devsd.c /n/emeliedump/2000/0809/sys/src/9/port/devsd.c
752c
			for(i = 0; i < unit->npart; i++){
.
423c
		if(unit->part == nil || i >= unit->npart){
.
128c
		for(i = 0; i < unit->npart; i++){
.
110c
	if(i >= unit->npart)
.
105c
	for(i = 0; i < unit->npart; i++){
.
81,83c
	if(partno == -1){
		if((pp = malloc(sizeof(SDpart)*(unit->npart+SDnpart))) == nil)
			error(Enomem);
		memmove(pp, unit->part, sizeof(SDpart)*unit->npart);
		free(unit->part);
		unit->part = pp;
		partno = unit->npart;
		unit->npart += SDnpart;
	}

	/*
	 * Check size and extent are valid.
	 */
	if(start > end || end > unit->sectors)
.
79c
	 * If no free slot found then increase the
	 * array size (can't get here with unit->part == nil).
.
74a
		unit->npart = SDnpart;
.
58c
		for(i = 0; i < unit->npart; i++){
.
## diffname port/devsd.c 2000/1129
## diff -e /n/emeliedump/2000/0809/sys/src/9/port/devsd.c /n/emeliedump/2000/1129/sys/src/9/port/devsd.c
880c
		qunlock(&unit->raw);
		poperror();
		return n;

.
878c
			n = sdrio(unit->req, a, n);
.
849a
		qlock(&unit->raw);
		if(waserror()){
			qunlock(&unit->raw);
			nexterror();
		}
.
809a

	case Qlog:
		error(Ebadctl);

.
789,791c
			i = readnum(0, a, n, status, NUMSIZE);
		} else
			i = 0;
		qunlock(&unit->raw);
		poperror();
		return i;
.
782c
			i = sdrio(unit->req, a, n);
.
779a
		qlock(&unit->raw);
		if(waserror()){
			qunlock(&unit->raw);
			nexterror();
		}
.
743a
	case Qlog:
		unit = sdunit[UNIT(c->qid)];
		return logread(&unit->log, a, 0, n);
.
636a
	if(unit->log.opens) {
		int i;
		uchar lbuf[1+4+8], *p;
		ulong x[3];

		p = lbuf;
		*p++ = write ? 'w' : 'r';
		x[0] = off>>32;
		x[1] = off;
		x[2] = len;
		for(i=0; i<3; i++) {
			*p++ = x[i]>>24;
			*p++ = x[i]>>16;
			*p++ = x[i]>>8;
			*p++ = x[i];
		}

		logn(&unit->log, 1, lbuf, 1+4+8);
	}

.
570a
	case Qlog:
		unit = sdunit[UNIT(c->qid)];
		logclose(&unit->log);
		break;
.
527a
	case Qlog:
		unit = sdunit[UNIT(c->qid)];
		logopen(&unit->log);
		break;
.
454a
	case Qlog:
.
336a
	case Qlog:
		q = (Qid){QID(UNIT(c->qid), PART(c->qid), Qlog), unit->vers};
		perm = &unit->rawperm;
		if(perm->user[0] == '\0'){
			strncpy(perm->user, eve, NAMELEN);
			perm->perm = 0666;
		}
		devdir(c, q, "log", 0, perm->user, perm->perm, dp);
		return 1;
.
231a
		unit->log.logmask = ~0;
		unit->log.nlog = 16*1024;
		unit->log.minread = 4*1024;

.
36a
	Qlog,
.
13c
#include "../port/sd.h"
.
## diffname port/devsd.c 2001/0213
## diff -e /n/emeliedump/2000/1129/sys/src/9/port/devsd.c /n/emeliedump/2001/0213/sys/src/9/port/devsd.c
126,127d
## diffname port/devsd.c 2001/0527
## diff -e /n/emeliedump/2001/0213/sys/src/9/port/devsd.c /n/emeliedump/2001/0527/sys/src/9/port/devsd.c
1004d
994a
	return n;
.
989,991c
	n = convM2D(dp, n, &d[0], (char*)&d[1]);
	if(n == 0)
		error(Eshortstat);
	kstrdup(&perm->user, d[0].uid);
	perm->perm = (perm->perm & ~0777) | (d[0].mode & 0777);
.
987c
	if(strcmp(up->user, perm->user) && !iseve())
.
960c
	if(c->qid.type & QTDIR)
.
955c
	Dir d[2];
.
952,953c
static int
sdwstat(Chan* c, uchar* dp, int n)
.
943,944c
		break;
.
862,865d
812,814c
						"part %s %lud %lud\n",
						pp->name, pp->start, pp->end);
.
785,787d
658,677d
588,591d
580c
	if(c->qid.type & QTDIR)
.
541,544d
525,530d
522c
	return devstat(c, db, n, nil, 0, sdgen);
.
520c
sdstat(Chan* c, uchar* db, int n)
.
516c
	return devwalk(c, nc, name, nname, nil, 0, sdgen);
.
513,514c
static Walkqid*
sdwalk(Chan* c, Chan* nc, char** name, int nname)
.
508c
	mkqid(&c->qid, QID(sdev->index+subno, 0, Qunitdir), 0, QTDIR);
.
490c
		mkqid(&c->qid, QID(0, 0, Qtopdir), 0, QTDIR);
.
467d
458,460c
		mkqid(&q, QID(UNIT(c->qid), i, Qpart), unit->vers+pp->vers, QTFILE);
		if(emptystr(pp->user))
			kstrdup(&pp->user, eve);
.
422,424c
			mkqid(&q, QID(s, 0, Qtopdir), 0, QTDIR);
			sprint(up->genbuf, "#%C", sddevtab.dc);
			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
.
412,414c
			mkqid(&q, QID(s, 0, Qunitdir), 0, QTDIR);
			if(emptystr(unit->user))
				kstrdup(&unit->user, eve);
.
402,404c
			mkqid(&q, QID(s, 0, Qtopdir), 0, QTDIR);
			sprint(up->genbuf, "#%C", sddevtab.dc);
			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
.
397d
390c
sdgen(Chan* c, char*, Dirtab*, int, int s, Dir* dp)
.
370,372c
		mkqid(&q, QID(UNIT(c->qid), PART(c->qid), Qpart), unit->vers+pp->vers, QTFILE);
		if(emptystr(pp->user))
			kstrdup(&pp->user, eve);
.
361,363c
		if(emptystr(perm->user)){
			kstrdup(&perm->user, eve);
			perm->perm = DMEXCL|0600;
.
359c
		mkqid(&q, QID(UNIT(c->qid), PART(c->qid), Qraw), unit->vers, QTFILE);
.
352,353c
		if(emptystr(perm->user)){
			kstrdup(&perm->user, eve);
.
350c
		mkqid(&q, QID(UNIT(c->qid), PART(c->qid), Qctl), unit->vers, QTFILE);
.
340,348d
231,234d
225,226c
		snprint(buf, sizeof(buf), "%s%d", sdev->name, subno);
		kstrdup(&unit->name, buf);
		kstrdup(&unit->user, eve);
.
195a
	char buf[32];
.
125a
	if(strcmp(up->user, pp->user) && !iseve())
		error(Eperm);
.
120c
		if(strcmp(name, pp->name) == 0)
.
102,103c
	kstrdup(&pp->name, name);
	kstrdup(&pp->user, eve);
.
66c
			if(strcmp(name, pp->name) == 0){
.
42,44c
#define TYPE(q)		(((ulong)(q).path) & 0x0F)
#define PART(q)		((((ulong)(q).path)>>4) & 0x0F)
#define UNIT(q)		((((ulong)(q).path)>>8) & 0xFF)
.
37d
## diffname port/devsd.c 2001/0613
## diff -e /n/emeliedump/2001/0527/sys/src/9/port/devsd.c /n/emeliedump/2001/0613/sys/src/9/port/devsd.c
310a
	if(sdnunit > NUnit)
		sdnunit = NUnit;
.
83a
		if(unit->npart >= NPart)
			error(Enomem);
.
41,44c
#define TYPE(q)		((((ulong)(q).path)>>TypeSHIFT) & TypeMASK)
#define PART(q)		((((ulong)(q).path)>>PartSHIFT) & PartMASK)
#define UNIT(q)		((((ulong)(q).path)>>UnitSHIFT) & UnitMASK)
#define QID(u, p, t)	(((u)<<UnitSHIFT)|((p)<<PartSHIFT)|((t)<<TypeSHIFT))
.
38a

	TypeLOG		= 4,
	NType		= (1<<TypeLOG),
	TypeMASK	= (NType-1),
	TypeSHIFT	= 0,

	PartLOG		= 8,
	NPart		= (1<<PartLOG),
	PartMASK	= (NPart-1),
	PartSHIFT	= TypeLOG,

	UnitLOG		= 8,
	NUnit		= (1<<UnitLOG),
	UnitMASK	= (NUnit-1),
	UnitSHIFT	= (PartLOG+TypeLOG),
.
## diffname port/devsd.c 2001/0905
## diff -e /n/emeliedump/2001/0613/sys/src/9/port/devsd.c /n/emeliedump/2001/0905/sys/src/9/port/devsd.c
981a
	devpower,
	sdconfig,
.
963a
static char
getspec(char base)
{
	while (1) {
		int i;
		SDev *sdev;

		for (i = 0; i != ndevs; i++)
			if ((sdev = devs[i].dt_dev) != nil && (char)sdev->idno == base)
				break;

		if (i == ndevs)
			return base;
		base++;
	}
	return '\0';
}

static int
configure(char *spec, DevConf *cf)
{
	ISAConf isa;
	dev_t *_devs;
	SDev *tail, *sdev, *(*probe)(DevConf *);
	char *p, name[32];
	int i, added_devs;

	if ((p = strchr(cf->type, '/')) != nil)
		*p++ = '\0';

	for(i = 0; sdifc[i] != nil; i++)
		if(!strcmp(sdifc[i]->name, cf->type))
			break;

	if (sdifc[i] == nil)
		error("type not found");
	
	if ((probe = sdifc[i]->probe) == nil)
		error("No probe function");

	if (p) {
		/* Try to find the card on the ISA bus.  This code really belongs
		     in sdata and I'll move it later.  Really! */
		memset(&isa, 0, sizeof(isa));
		isa.port = cf->ports[0].port;
		isa.irq = cf->interrupt;

		if (pcmspecial(p, &isa) < 0)
			error("Cannot find controller");
	}

	qlock(&devslock);
	if (waserror()) {
		qunlock(&devslock);
		nexterror();
	}
	
	for (i = 0; i != ndevs; i++)
		if ((sdev = devs[i].dt_dev) != nil && sdev->idno == *spec)
			break;
	if (i != ndevs)
		error(Eexist);

	if ((sdev = (*probe)(cf)) == nil)
		error("Cannot probe controller");
	poperror();

	added_devs = 0;
	tail = sdev;
	while (tail) {
		added_devs++;
		tail = tail->next;
	}
	
	_devs = (dev_t *)malloc((ndevs + added_devs) * sizeof(dev_t));
	memmove(_devs, devs, ndevs * sizeof(dev_t));
	free(devs);
	devs = _devs;

	while (sdev) {
		/* Assign `spec' to the device */
		*spec = getspec(*spec);
		snprint(name, sizeof(name), "sd%c", *spec);
		kstrdup(&sdev->name, name);
		sdev->idno = *spec;

		devs[ndevs].dt_dev = sdev;
		devs[ndevs].dt_nunits = sdev->nunit;
		sdev = sdev->next;
		devs[ndevs].dt_dev->next = nil;
		ndevs++;
	}

	qunlock(&devslock);
	return 0;
}

static int
unconfigure(char *spec)
{
	int i;	
	SDev *sdev;

	qlock(&devslock);
	if (waserror()) {
		qunlock(&devslock);
		nexterror();
	}

	for (sdev = nil, i = 0; i != ndevs; i++)
		if ((sdev = devs[i].dt_dev) != nil && sdev->idno == *spec)
			break;

	if (i == ndevs)
		error(Enonexist);

	if (sdev->r.ref)
		error(Einuse);

	/* make sure no interrupts arrive anymore before removing resources */
	if (sdev->enabled && sdev->ifc->disable)
		sdev->ifc->disable(sdev);

	/* we're alone and the device tab is locked; make the device unavailable */
	memmove(&devs[i], &devs[ndevs - 1], sizeof(dev_t));
	memset(&devs[ndevs - 1], 0, sizeof(dev_t));
	ndevs--;

	qunlock(&devslock);
	poperror();

	for (i = 0; i != sdev->nunit; i++)
		if (sdev->unit[i]) {
			SDunit *unit = sdev->unit[i];

			free(unit->name);
			free(unit->user);
			free(unit);
		}

	if (sdev->ifc->clear)
		sdev->ifc->clear(sdev);
	return 0;
}

static int
sdconfig(int on, char *spec, DevConf *cf)
{
	return on? configure(spec, cf): unconfigure(spec);
}

.
959a
	decref(&sdev->r);
.
930a
		decref(&sdev->r);
.
927c
	sdev = sdgetdev(DEV(c->qid));
	if (sdev == nil)
		error(Enonexist);
	unit = sdev->unit[UNIT(c->qid)];
.
925c
		error(Eperm); 
.
922a
	SDev *sdev;
.
906a
		decref(&sdev->r);
.
873a
			decref(&sdev->r);
.
870c
		sdev = sdgetdev(DEV(c->qid));
		if (sdev == nil)
			error(Enonexist);
		unit = sdev->unit[UNIT(c->qid)];
.
864a
		decref(&sdev->r);
.
837a
			decref(&sdev->r);
.
833c
		sdev = sdgetdev(DEV(c->qid));
		if (sdev == nil)
			error(Enonexist);
		unit = sdev->unit[UNIT(c->qid)];
.
830a
	case Qtopctl: {
		confdata_t cd;
		char buf[256], *field[Ncmd];
		int nf, i, j;

		memset(&cd, 0, sizeof(confdata_t));
		if(n > sizeof(buf)-1) n = sizeof(buf)-1;
		memmove(buf, a, n);
		buf[n] = '\0';

		cd.o_on = -1;
		cd.o_spec = '\0';
		memset(&cd.o_cf, 0, sizeof(DevConf));

		nf = getfields(buf, field, Ncmd, 1, " \t\n");
		for (i = 0; i < nf; i++) {
			char *opt = field[i++];
			if (i >= nf)
				error(Ebadarg);
			for (j = 0; j != nelem(options); j++)
				if (!strcmp(opt, options[j].option))
					break;
					
			if (j == nelem(options))
				error(Ebadarg);
			options[j].parse(&cd, field[i]);
		}

		if (cd.o_on < 0) 
			error(Ebadarg);

		if (cd.o_on) {
			if (cd.o_spec == '\0' || cd.o_cf.nports == 0 || 
			     cd.o_cf.interrupt == 0 || cd.o_cf.type == nil)
				error(Ebadarg);
		}
		else {
			if (cd.o_spec == '\0')
				error(Ebadarg);
		}

		if (sddevtab.config == nil)
			error("No configuration function");
		sddevtab.config(cd.o_on, cd.o_spec, &cd.o_cf);
		break;
	}
.
825a
	SDev *sdev;
.
819a
typedef struct {
	int		o_on;
	char		*o_spec;
	DevConf	o_cf;
} confdata_t;

static void
parse_switch(confdata_t *cd, char *option)
{
	if (!strcmp("on", option))
		cd->o_on = 1;
	else if (!strcmp("off", option))
		cd->o_on = 0;
	else
		error(Ebadarg);
}

static void
parse_spec(confdata_t *cd, char *option)
{
	if (strlen(option) > 1) 
		error(Ebadarg);
	cd->o_spec = option;
}

static port_t *
getnewport(DevConf *dc)
{
	port_t *p;

	p = (port_t *)malloc((dc->nports + 1) * sizeof(port_t));
	if (dc->nports > 0) {
		memmove(p, dc->ports, dc->nports * sizeof(port_t));
		free(dc->ports);
	}
	dc->ports = p;
	p = &dc->ports[dc->nports++];
	p->size = -1;
	p->port = (ulong)-1;
	return p;
}

static void
parse_port(confdata_t *cd, char *option)
{
	char *e;
	port_t *p;
	
	p = (cd->o_cf.nports == 0 || 
	        cd->o_cf.ports[cd->o_cf.nports -1].port != (ulong)-1)?
			getnewport(&cd->o_cf): &cd->o_cf.ports[cd->o_cf.nports - 1];
	p->port = strtol(option, &e, 0);
	if (e == nil || *e != '\0')
		error(Ebadarg);
}

static void
parse_size(confdata_t *cd, char *option)
{
	char *e;
	port_t *p;

	p = (cd->o_cf.nports == 0 || cd->o_cf.ports[cd->o_cf.nports -1].size != -1)?
			getnewport(&cd->o_cf): &cd->o_cf.ports[cd->o_cf.nports - 1];
	p->size = 	(int)strtol(option, &e, 0);
	if (e == nil || *e != '\0')
		error(Ebadarg);
}

static void
parse_irq(confdata_t *cd, char *option)
{
	char *e;

	cd->o_cf.interrupt = strtoul(option, &e, 0);
	if (e == nil || *e != '\0')
		error(Ebadarg);
}

static void
parse_type(confdata_t *cd, char *option)
{
	cd->o_cf.type = option;
}

static struct {
	char *option;
	void	(*parse)(confdata_t *, char *);
} options[] = {
	{ 	"switch",		parse_switch,	},
	{	"spec",		parse_spec,	},
	{	"port",		parse_port,	},
	{	"size",		parse_size,	},
	{	"irq",			parse_irq,		},
	{	"type",		parse_type,	},
};

.
812a

.
810a
		decref(&sdev->r);
.
795a
			decref(&sdev->r);
.
792c
		sdev = sdgetdev(DEV(c->qid));
		if (sdev == nil)
			error(Enonexist);

		unit = sdev->unit[UNIT(c->qid)];
.
790a

.
787a
		decref(&sdev->r);
.
759c
		sdev = sdgetdev(DEV(c->qid));
		if (sdev == nil)
			error(Enonexist);

		unit = sdev->unit[UNIT(c->qid)];
.
757a

.
754a
	case Qtopstat:
		p = buf = malloc(READSTR);
		assert(p);
		e = p + READSTR;
		qlock(&devslock);
		for (i = 0; i != ndevs; i++) {
			SDev *sdev = devs[i].dt_dev;

			if (sdev->ifc->stat)
				p = sdev->ifc->stat(sdev, p, e);
			else
				p = seprint(e, "%s; no statistics available\n", sdev->name);
		}
		qunlock(&devslock);
		n = readstr(off, a, n, buf);
		free(buf);
		return n;

.
747a
	SDev *sdev;
.
745c
	char *p, *e, *buf;
.
698a
	decref(&sdev->r);
.
655a
		if(!(unit->inquiry[1] & 0x80))
			decref(&sdev->r);		/* gadverdamme! */
.
642a
		decref(&sdev->r);
.
614a
		decref(&sdev->r);
.
602c
	sdev = sdgetdev(DEV(c->qid));
	if (sdev == nil)
		error(Enonexist);
	unit = sdev->unit[UNIT(c->qid)];
	if (unit == nil)
		error(Enonexist);
.
599a
	SDev *sdev;
.
586,587c
		sdev = sdgetdev(DEV(c->qid));
		if (sdev) {
			unit = sdev->unit[UNIT(c->qid)];
			unlock(&unit->rawinuse);
			decref(&sdev->r);
		}
.
575a
	SDev *sdev;
.
568a
	decref(&sdev->r);
.
556d
547d
543d
540,541d
538a
	if ((tp = TYPE(c->qid)) != Qctl && tp != Qraw && tp != Qpart)
		return c;

	sdev = sdgetdev(DEV(c->qid));
	if (sdev == nil)
		error(Enonexist);
	unit = sdev->unit[UNIT(c->qid)];

.
536a
	SDev *sdev;
	uchar tp;
.
515,516c
	mkqid(&c->qid, QID(sdev->idno, subno, 0, Qunitdir), 0, QTDIR);
	c->dev = (sdev->idno << UnitLOG) + subno;
	decref(&sdev->r);
.
512a
	}
	incref(&sdev->r);
	qunlock(&devslock);
.
510,511c

	if (i == ndevs || subno >= sdev->nunit || sdgetunit(sdev, subno) == nil) {
		qunlock(&devslock);
.
507,508c

	qlock(&devslock);
	for (sdev = nil, i = 0; i != ndevs; i++)
		if ((sdev = devs[i].dt_dev) != nil && sdev->idno == idno)
.
497c
		mkqid(&c->qid, QID(0, 0, 0, Qtopdir), 0, QTDIR);
.
495c
	if(ndevs == 0 || *spec == '\0'){
.
493c
	int idno, subno, i;
.
478a
		decref(&sdev->r);
.
475c
		if ((sdev = sdgetdev(DEV(c->qid))) == nil) {
			devdir(c, q, "unavailable", 0, eve, 0, dp);
			return 1;
		}
		unit = sdev->unit[UNIT(c->qid)];
.
470a
		decref(&sdev->r);
.
466c
		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), i, Qpart), 
			    unit->vers+pp->vers, QTFILE);
.
462a
			decref(&sdev->r);
.
457a
			decref(&sdev->r);
.
452a
			decref(&sdev->r);
.
435c
		
		if ((sdev = sdgetdev(DEV(c->qid))) == nil) {
			devdir(c, q, "unavailable", 0, eve, 0, dp);
			return 1;
		}

		unit = sdev->unit[UNIT(c->qid)];
.
430c
			mkqid(&q, QID(0, s, 0, Qtopdir), 0, QTDIR);
.
426,427c
		
		if (i == ndevs) {
			/* Run of the end of the list */
			qunlock(&devslock);
			return -1;
		}

		if ((sdev = devs[i].dt_dev) == nil) {
			qunlock(&devslock);
			return 0;
		}

		incref(&sdev->r);
		qunlock(&devslock);

		if((unit = sdev->unit[s]) == nil)
			if((unit = sdgetunit(sdev, s)) == nil) {
				decref(&sdev->r);
				return 0;
			}

		mkqid(&q, QID(sdev->idno, s, 0, Qunitdir), 0, QTDIR);
		if(emptystr(unit->user))
			kstrdup(&unit->user, eve);
		devdir(c, q, unit->name, 0, unit->user, unit->perm, dp);
		decref(&sdev->r);
		return 1;
	}

.
415,424c

		if (s == 0 || s == 1)
			return sd1gen(c, s + Qtopbase, dp);
		s -= 2;

		qlock(&devslock);
		for (i = 0; i != ndevs; i++) {
			if (s < devs[i].dt_nunits)
				break;
			s -= devs[i].dt_nunits;
.
410c
			mkqid(&q, QID(0, s, 0, Qtopdir), 0, QTDIR);
.
408c
	case Qtopdir: {
.
405a
	SDev *sdev;
.
392,393c
	case Qtopctl:
		mkqid(&q, QID(0, 0, 0, Qtopctl), 0, QTFILE);
		devdir(c, q, "sdctl", 0, eve, 0640, dp);
		return 1;
	case Qtopstat:
		mkqid(&q, QID(0, 0, 0, Qtopstat), 0, QTFILE);
		devdir(c, q, "sdstat", 0, eve, 0640, dp);
		return 1;
.
390a
	Qid q;

.
389c
sd1gen(Chan* c, int i, Dir* dp)
.
383,385c
		rv = 1;
		break;
	}
	
	decref(&sdev->r);
	return rv;
.
379c
		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qpart), 
			   unit->vers+pp->vers, QTFILE);
.
375c
		rv = 1;
		break;

.
368c
		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qraw), 
			   unit->vers, QTFILE);
.
366c
		rv = 1;
		break;

.
359c
		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qctl), 
			   unit->vers, QTFILE);
.
356c
	sdev = sdgetdev(DEV(c->qid));
	assert(sdev);
	unit = sdev->unit[UNIT(c->qid)];

	rv = -1;
.
354a
	SDev *sdev;
	int rv;
.
344a

	/* 
	  * The IDs have been set, unlink the sdlist and copy the spec to
	  * the devtab.
	  */
	devs = (dev_t *)malloc(ndevs * sizeof(dev_t));
	memset(devs, 0, ndevs * sizeof(dev_t));
	i = 0;
	while (sdlist != nil) {
		devs[i].dt_dev = sdlist;
		devs[i].dt_nunits = sdlist->nunit;
		sdlist = sdlist->next;
		devs[i].dt_dev->next = nil;
		i++;
	}
.
328,336d
326c
	if(ndevs == 0)
.
315c
	
.
308,313c
		for(tail = sdev; tail->next != nil; tail = tail->next)
			ndevs++;
		ndevs++;
.
300c
	tail = sdlist = nil;
.
292c
	SDev *sdev, *tail, *sdlist;
.
268,287d
263,264c
	qunlock(&sdev->unitlock);
.
261c
		sdev->unit[subno] = unit;
.
257c
			qunlock(&sdev->unitlock);
.
238c
		sdev->unitflg[subno] = 1;
.
235c
			qunlock(&sdev->unitlock);
.
230,231c
		if(sdev->unitflg[subno]){
			qunlock(&sdev->unitlock);
.
222,224c
	qlock(&sdev->unitlock);
	if (subno > sdev->nunit) {
		qunlock(&sdev->unitlock);
		return nil;
	}

	unit = sdev->unit[subno];
.
212d
208a
static SDev *
sdgetdev(int idno)
{
	SDev *sdev;
	int i;

	qlock(&devslock);
	for (i = 0; i != ndevs; i++)
		if (devs[i].dt_dev->idno == idno)
			break;
	
	if (i == ndevs)
		sdev = nil;
	else {
		sdev = devs[i].dt_dev;
		incref(&sdev->r);
	}
	qunlock(&devslock);
	return sdev;
}

.
60a

.
59c
#define DEV(q)			((((ulong)(q).path)>>DevSHIFT) & DevMASK)
#define QID(d,u, p, t)	(((d)<<DevSHIFT)|((u)<<UnitSHIFT)|\
					 ((p)<<PartSHIFT)|((t)<<TypeSHIFT))
.
53a

	DevLOG		= 8,
	NDev		= (1 << DevLOG),
	DevMASK	= (NDev-1),
	DevSHIFT = (UnitLOG+PartLOG+TypeLOG),

	Ncmd = 20,
.
32a
	Qtopctl = Qtopbase,
	Qtopstat,
.
23a
static dev_t 	*devs;		/* all devices */
static QLock	devslock;		/* insertion and removal of devices */
static int		ndevs;		/* total number of devices in the system */

.
18,22c
typedef struct {
	SDev 	*dt_dev;
	int		dt_nunits;		/* num units in dev */
} dev_t;
.
## diffname port/devsd.c 2001/0907
## diff -e /n/emeliedump/2001/0905/sys/src/9/port/devsd.c /n/emeliedump/2001/0907/sys/src/9/port/devsd.c
568a
	case Qtopctl:
	case Qtopstat:
		return sd1gen(c, TYPE(c->qid), dp);
.
327a
		}
		tail->unit = (SDunit **)malloc(tail->nunit * sizeof(SDunit *));
		tail->unitflg = (int *)malloc(tail->nunit * sizeof(int));
.
326c
		for(tail = sdev; tail->next != nil; tail = tail->next) {
			tail->unit = (SDunit **)malloc(tail->nunit * sizeof(SDunit *));
			tail->unitflg = (int *)malloc(tail->nunit * sizeof(int));
			assert(tail->unit && tail->unitflg);
.
## diffname port/devsd.c 2001/0911
## diff -e /n/emeliedump/2001/0907/sys/src/9/port/devsd.c /n/emeliedump/2001/0911/sys/src/9/port/devsd.c
1368a
		sdev->unit = (SDunit **)malloc(sdev->nunit * sizeof(SDunit *));
		sdev->unitflg = (int *)malloc(sdev->nunit * sizeof(int));
		assert(sdev->unit && sdev->unitflg);
.
## diffname port/devsd.c 2001/0924
## diff -e /n/emeliedump/2001/0911/sys/src/9/port/devsd.c /n/emeliedump/2001/0924/sys/src/9/port/devsd.c
730c
		if(strcmp(up->errstr, Eio) == 0 && unit->sectors == 0 && nchange++ == 0){
.
## diffname port/devsd.c 2001/1106
## diff -e /n/emeliedump/2001/0924/sys/src/9/port/devsd.c /n/emeliedump/2001/1106/sys/src/9/port/devsd.c
1277a
	free(d);
.
1275,1276c
	if(!emptystr(d[0].uid))
		kstrdup(&perm->user, d[0].uid);
	if(d[0].mode != ~0UL)
		perm->perm = (perm->perm & ~0777) | (d[0].mode & 0777);
.
1271a

	d = smalloc(sizeof(Dir)+n);
.
1247a
		free(d);
.
1246a
	d = nil;
.
1233c
	Dir *d;
.
## diffname port/devsd.c 2001/1117
## diff -e /n/emeliedump/2001/1106/sys/src/9/port/devsd.c /n/emeliedump/2001/1117/sys/src/9/port/devsd.c
1103c
		nf = tokenize(buf, field, Ncmd);
.
208c
			nf = tokenize(p, f, nelem(f));
.
## diffname port/devsd.c 2002/0109
## diff -e /n/emeliedump/2001/1117/sys/src/9/port/devsd.c /n/emeliedump/2002/0109/sys/src/9/port/devsd.c
1450a
	devshutdown,
.
## diffname port/devsd.c 2002/0316
## diff -e /n/emeliedump/2002/0109/sys/src/9/port/devsd.c /n/emeliedump/2002/0316/sys/src/9/port/devsd.c
1442c
	if(on)
		return configure(spec, cf);
	return unconfigure(spec);
.
1440c
sdconfig(int on, char* spec, DevConf* cf)
.
1434c
	if(sdev->ifc->clear)
.
1425,1426c
	for(i = 0; i != sdev->nunit; i++)
		if(sdev->unit[i]){
.
1414c
	if(sdev->enabled && sdev->ifc->disable)
.
1410c
	if(sdev->r.ref)
.
1407c
	if(i == ndevs)
.
1403,1404c
	sdev = nil;
	for(i = 0; i != ndevs; i++)
		if((sdev = devs[i].dt_dev) != nil && sdev->idno == *spec)
.
1398c
	if(waserror()){
.
1392c
unconfigure(char* spec)
.
1376c
		sdev->unit = (SDunit **)malloc(sdev->nunit * sizeof(SDunit*));
.
1370c
	while(sdev){
.
1365c
	_devs = (dev_t*)malloc((ndevs + added_devs) * sizeof(dev_t));
.
1360c
	while(tail){
.
1354c
	if((sdev = (*probe)(cf)) == nil)
.
1351c
	if(i != ndevs)
.
1348,1349c
	for(i = 0; i != ndevs; i++)
		if((sdev = devs[i].dt_dev) != nil && sdev->idno == *spec)
.
1343c
	if(waserror()){
.
1338c
		if(pcmspecial(p, &isa) < 0)
.
1331c
	if(p){
.
1328c
	if((probe = sdifc[i]->probe) == nil)
.
1325c
	if(sdifc[i] == nil)
.
1318c
	if((p = strchr(cf->type, '/')) != nil)
.
1314c
	SDev *tail, *sdev, *(*probe)(DevConf*);
.
1310c
configure(char* spec, DevConf* cf)
.
1302c
		if(i == ndevs)
.
1298,1299c
		for(i = 0; i != ndevs; i++)
			if((sdev = devs[i].dt_dev) != nil && (char)sdev->idno == base)
.
1294c
	while(1){
.
1180c
		if(sdev == nil)
.
1130c
		if(sddevtab.config == nil)
.
1125,1126c
		else{
			if(cd.o_spec == '\0')
.
1120,1121c
		if(cd.o_on){
			if(cd.o_spec == '\0' || cd.o_cf.nports == 0 || 
.
1117c
		if(cd.o_on < 0) 
.
1112c
			if(j == nelem(options))
.
1108,1109c
			for(j = 0; j != nelem(options); j++)
				if(!strcmp(opt, options[j].option))
.
1106c
			if(i >= nf)
.
1104c
		for(i = 0; i < nf; i++){
.
1078c
sdwrite(Chan* c, void* a, long n, vlong off)
.
1073c
	{	"irq",		parse_irq,	},
.
1069c
	{ 	"switch",	parse_switch,	},
.
1066,1067c
	char	*option;
	void	(*parse)(confdata_t*, char*);
.
1060c
parse_type(confdata_t* cd, char* option)
.
1055c
	if(e == nil || *e != '\0')
.
1050c
parse_irq(confdata_t* cd, char* option)
.
1042,1045c
	if(cd->o_cf.nports == 0 || cd->o_cf.ports[cd->o_cf.nports-1].size != -1)
		p = getnewport(&cd->o_cf);
	else
		p = &cd->o_cf.ports[cd->o_cf.nports-1];
	p->size = (int)strtol(option, &e, 0);
	if(e == nil || *e != '\0')
.
1037c
parse_size(confdata_t* cd, char* option)
.
1032c
	if(e == nil || *e != '\0')
.
1027,1030c

	if(cd->o_cf.nports == 0 || cd->o_cf.ports[cd->o_cf.nports-1].port != (ulong)-1)
		p = getnewport(&cd->o_cf);
	else
		p = &cd->o_cf.ports[cd->o_cf.nports-1];
.
1023c
parse_port(confdata_t* cd, char* option)
.
1011c
	if(dc->nports > 0){
.
1005,1006c
static port_t*
getnewport(DevConf* dc)
.
1000c
	if(strlen(option) > 1) 
.
998c
parse_spec(confdata_t* cd, char* option)
.
991c
	else if(!strcmp("off", option))
.
989c
	if(!strcmp("on", option))
.
987c
parse_switch(confdata_t* cd, char* option)
.
981,982c
	int	o_on;
	char*	o_spec;
.
891c
			if(sdev->ifc->stat)
.
888c
		for(i = 0; i != ndevs; i++){
.
807c
	else{
.
723c
	if(unit == nil)
.
720c
	if(sdev == nil)
.
650c
	if(sdev == nil)
.
646c
	if((tp = TYPE(c->qid)) != Qctl && tp != Qraw && tp != Qpart)
.
611c
	if(i == ndevs || subno >= sdev->nunit || sdgetunit(sdev, subno) == nil){
.
565c
		if((sdev = sdgetdev(DEV(c->qid))) == nil){
.
515c
		if((sdev = sdgetdev(DEV(c->qid))) == nil){
.
505d
494c
			if((unit = sdgetunit(sdev, s)) == nil){
.
485c
		if ((sdev = devs[i].dt_dev) == nil){
.
479c
		if(i == ndevs){
.
473c
		for(i = 0; i != ndevs; i++){
.
468c
		if(s == 0 || s == 1)
.
460c
	case Qtopdir:
.
365c
	while(sdlist != nil){
.
362c
	devs = (dev_t*)malloc(ndevs * sizeof(dev_t));
.
332,333c
		tail->unit = (SDunit**)malloc(tail->nunit * sizeof(SDunit*));
		tail->unitflg = (int*)malloc(tail->nunit * sizeof(int));
.
326,328c
		for(tail = sdev; tail->next != nil; tail = tail->next){
			tail->unit = (SDunit**)malloc(tail->nunit * sizeof(SDunit*));
			tail->unitflg = (int*)malloc(tail->nunit * sizeof(int));
.
258c
	if(subno > sdev->nunit){
.
237c
	else{
.
235c
	if(i == ndevs)
.
231c
	for(i = 0; i != ndevs; i++)
.
224c
static SDev*
.
71c
#define DEV(q)		((((ulong)(q).path)>>DevSHIFT) & DevMASK)
.
23,25c
static dev_t* devs;			/* all devices */
static QLock devslock;			/* insertion and removal of devices */
static int ndevs;			/* total number of devices in the system */
.
19,20c
	SDev*	dt_dev;
	int	dt_nunits;		/* num units in dev */
.
## diffname port/devsd.c 2002/0330
## diff -e /n/emeliedump/2002/0316/sys/src/9/port/devsd.c /n/emeliedump/2002/0330/sys/src/9/port/devsd.c
784a
	if(offset+len > nb*unit->secsize)
		len = nb*unit->secsize - offset;
.
## diffname port/devsd.c 2002/0402
## diff -e /n/emeliedump/2002/0330/sys/src/9/port/devsd.c /n/emeliedump/2002/0402/sys/src/9/port/devsd.c
700c
			unit->rawinuse = 0;
.
659c
		if(tas(&unit->rawinuse) != 0){
.
## diffname port/devsd.c 2002/0703
## diff -e /n/emeliedump/2002/0402/sys/src/9/port/devsd.c /n/emeliedump/2002/0703/sys/src/9/port/devsd.c
1340c
		isa.irq = cf->intnum;
.
1126c
			     cd.o_cf.intnum == 0 || cd.o_cf.type == nil)
.
1058c
	cd->o_cf.intnum = strtoul(option, &e, 0);
.
## diffname port/devsd.c 2003/0406
## diff -e /n/emeliedump/2002/0703/sys/src/9/port/devsd.c /n/emeliedump/2003/0406/sys/src/9/port/devsd.c
1247c
	if(sdev == nil)
.
1142c
		if(sdev == nil)
.
947c
		if(sdev == nil)
.
908c
		if(sdev == nil)
.
698c
		if(sdev) {
.
607c
		if((sdev = devs[i].dt_dev) != nil && sdev->idno == idno)
.
485c
		if((sdev = devs[i].dt_dev) == nil){
.
474c
			if(s < devs[i].dt_nunits)
.
232c
		if(devs[i].dt_dev->idno == idno)
.
## diffname port/devsd.c 2003/0507
## diff -e /n/emeliedump/2003/0406/sys/src/9/port/devsd.c /n/emeliedump/2003/0507/sys/src/9/port/devsd.c
288a

		if(sdev->enabled == 0 && sdev->ifc->enable)
			sdev->ifc->enable(sdev);
		sdev->enabled = 1;
.
279,282d

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.