plan 9 kernel history: overview | file list | diff list

2001/0907/port/devsd.c (diff list | history)

2001/0907/sys/src/9/port/devsd.c:1,14552001/0911/sys/src/9/port/devsd.c:1,1458 (short | long | prev | next)
1994/0913    
/* 
2000/0506    
 * Storage Device. 
1994/0913    
 */ 
2000/0506    
#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" 
1994/0913    
 
2000/1129    
#include "../port/sd.h" 
1995/0403    
 
2000/0506    
extern Dev sddevtab; 
extern SDifc* sdifc[]; 
 
2001/0905    
typedef struct { 
	SDev 	*dt_dev; 
	int		dt_nunits;		/* num units in dev */ 
} dev_t; 
2000/0506    
 
2001/0905    
static dev_t 	*devs;		/* all devices */ 
static QLock	devslock;		/* insertion and removal of devices */ 
static int		ndevs;		/* total number of devices in the system */ 
 
2000/0506    
enum { 
	Rawcmd, 
	Rawdata, 
	Rawstatus, 
1994/0913    
}; 
 
2000/0506    
enum { 
	Qtopdir		= 1,		/* top level directory */ 
	Qtopbase, 
2001/0905    
	Qtopctl = Qtopbase, 
	Qtopstat, 
1994/0913    
 
2000/0506    
	Qunitdir,			/* directory per unit */ 
	Qunitbase, 
	Qctl		= Qunitbase, 
	Qraw, 
	Qpart, 
2001/0613    
 
	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), 
2001/0905    
 
	DevLOG		= 8, 
	NDev		= (1 << DevLOG), 
	DevMASK	= (NDev-1), 
	DevSHIFT = (UnitLOG+PartLOG+TypeLOG), 
 
	Ncmd = 20, 
1994/0913    
}; 
 
2001/0613    
#define TYPE(q)		((((ulong)(q).path)>>TypeSHIFT) & TypeMASK) 
#define PART(q)		((((ulong)(q).path)>>PartSHIFT) & PartMASK) 
#define UNIT(q)		((((ulong)(q).path)>>UnitSHIFT) & UnitMASK) 
2001/0905    
#define DEV(q)			((((ulong)(q).path)>>DevSHIFT) & DevMASK) 
#define QID(d,u, p, t)	(((d)<<DevSHIFT)|((u)<<UnitSHIFT)|\ 
					 ((p)<<PartSHIFT)|((t)<<TypeSHIFT)) 
2000/0506    
 
2001/0905    
 
2000/0506    
static void 
sdaddpart(SDunit* unit, char* name, ulong start, ulong end) 
1994/0913    
{ 
2000/0506    
	SDpart *pp; 
	int i, partno; 
1994/0913    
 
2000/0506    
	/* 
	 * Check name not already used 
	 * and look for a free slot. 
	 */ 
	if(unit->part != nil){ 
		partno = -1; 
2000/0809    
		for(i = 0; i < unit->npart; i++){ 
2000/0506    
			pp = &unit->part[i]; 
			if(!pp->valid){ 
				if(partno == -1) 
					partno = i; 
				break; 
			} 
2001/0527    
			if(strcmp(name, pp->name) == 0){ 
2000/0506    
				if(pp->start == start && pp->end == end) 
					return; 
				error(Ebadctl); 
			} 
		} 
	} 
	else{ 
		if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil) 
			error(Enomem); 
2000/0809    
		unit->npart = SDnpart; 
2000/0506    
		partno = 0; 
	} 
1994/0913    
 
2000/0506    
	/* 
2000/0809    
	 * If no free slot found then increase the 
	 * array size (can't get here with unit->part == nil). 
2000/0506    
	 */ 
2000/0809    
	if(partno == -1){ 
2001/0613    
		if(unit->npart >= NPart) 
			error(Enomem); 
2000/0809    
		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) 
2000/0506    
		error(Eio); 
	pp = &unit->part[partno]; 
	pp->start = start; 
	pp->end = end; 
2001/0527    
	kstrdup(&pp->name, name); 
	kstrdup(&pp->user, eve); 
2000/0506    
	pp->perm = 0640; 
	pp->valid = 1; 
} 
1994/0913    
 
2000/0506    
static void 
sddelpart(SDunit* unit,  char* name) 
{ 
	int i; 
	SDpart *pp; 
1994/0913    
 
2000/0506    
	/* 
	 * Look for the partition to delete. 
	 * Can't delete if someone still has it open. 
	 */ 
	pp = unit->part; 
2000/0809    
	for(i = 0; i < unit->npart; i++){ 
2001/0527    
		if(strcmp(name, pp->name) == 0) 
2000/0506    
			break; 
		pp++; 
	} 
2000/0809    
	if(i >= unit->npart) 
2000/0506    
		error(Ebadctl); 
2001/0527    
	if(strcmp(up->user, pp->user) && !iseve()) 
		error(Eperm); 
2000/0506    
	pp->valid = 0; 
2000/0617    
	pp->vers++; 
2000/0506    
} 
1995/0403    
 
1994/0913    
static int 
2000/0506    
sdinitpart(SDunit* unit) 
1994/0913    
{ 
2000/0617    
	int i, nf; 
2000/0506    
	ulong start, end; 
2000/0530    
	char *f[4], *p, *q, buf[10]; 
1994/0913    
 
2000/0617    
	unit->vers++; 
2000/0506    
	unit->sectors = unit->secsize = 0; 
	if(unit->part){ 
2000/0809    
		for(i = 0; i < unit->npart; i++){ 
2000/0617    
			unit->part[i].valid = 0; 
			unit->part[i].vers++; 
		} 
1999/1230    
	} 
 
2000/0506    
	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; 
1994/0913    
	} 
 
2000/0506    
	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. 
2000/0530    
		 *	sdC0part=dos 63 123123/plan9 123123 456456 
2000/0524    
		 * This happens before /boot sets hostname so the 
		 * partitions will have the null-string for user. 
		 * The gen functions patch it up. 
2000/0506    
		 */ 
2000/0530    
		snprint(buf, sizeof buf, "%spart", unit->name); 
		for(p = getconf(buf); p != nil; p = q){ 
2000/0506    
			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(); 
			} 
		}			 
	} 
 
1994/0913    
	return 1; 
} 
 
2001/0905    
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; 
} 
 
2000/0506    
static SDunit* 
sdgetunit(SDev* sdev, int subno) 
1994/0913    
{ 
2000/0506    
	SDunit *unit; 
2001/0527    
	char buf[32]; 
1994/0913    
 
2000/0506    
	/* 
	 * 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. 
	 */ 
2001/0905    
	qlock(&sdev->unitlock); 
	if (subno > sdev->nunit) { 
		qunlock(&sdev->unitlock); 
		return nil; 
	} 
 
	unit = sdev->unit[subno]; 
2000/0506    
	if(unit == nil){ 
1995/0405    
		/* 
2000/0506    
		 * Probe the unit only once. This decision 
		 * may be a little severe and reviewed later. 
1995/0405    
		 */ 
2001/0905    
		if(sdev->unitflg[subno]){ 
			qunlock(&sdev->unitlock); 
2000/0506    
			return nil; 
		} 
		if((unit = malloc(sizeof(SDunit))) == nil){ 
2001/0905    
			qunlock(&sdev->unitlock); 
2000/0506    
			return nil; 
		} 
2001/0905    
		sdev->unitflg[subno] = 1; 
1995/0329    
 
2000/0506    
		if(sdev->enabled == 0 && sdev->ifc->enable) 
			sdev->ifc->enable(sdev); 
		sdev->enabled = 1; 
1998/0930    
 
2001/0527    
		snprint(buf, sizeof(buf), "%s%d", sdev->name, subno); 
		kstrdup(&unit->name, buf); 
		kstrdup(&unit->user, eve); 
2000/0706    
		unit->perm = 0555; 
2000/0506    
		unit->subno = subno; 
		unit->dev = sdev; 
1995/0405    
 
2000/0506    
		/* 
		 * 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){ 
2001/0905    
			qunlock(&sdev->unitlock); 
2000/0506    
			free(unit); 
			return nil; 
		} 
2001/0905    
		sdev->unit[subno] = unit; 
2000/0506    
	} 
2001/0905    
	qunlock(&sdev->unitlock); 
2000/0506    
	return unit; 
} 
1994/0913    
 
2000/0506    
static void 
sdreset(void) 
{ 
	int i; 
2001/0905    
	SDev *sdev, *tail, *sdlist; 
2000/0506    
 
	/* 
	 * 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. 
	 */ 
2001/0905    
	tail = sdlist = nil; 
2000/0506    
	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; 
2001/0907    
		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); 
2001/0905    
			ndevs++; 
2001/0907    
		} 
		tail->unit = (SDunit **)malloc(tail->nunit * sizeof(SDunit *)); 
		tail->unitflg = (int *)malloc(tail->nunit * sizeof(int)); 
2001/0905    
		ndevs++; 
2000/0506    
	} 
2001/0905    
	 
2000/0506    
	/* 
	 * 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. 
	 */ 
2001/0905    
	if(ndevs == 0) 
2000/0506    
		return; 
	for(i = 0; sdifc[i] != nil; i++){ 
2000/0515    
		/* 
		 * BUG: no check is made here or later when a 
		 * unit is attached that the id and name are set. 
		 */ 
2000/0506    
		if(sdifc[i]->id) 
			sdifc[i]->id(sdlist); 
	} 
2001/0905    
 
	/*  
	  * 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++; 
	} 
2000/0506    
} 
 
static int 
sd2gen(Chan* c, int i, Dir* dp) 
{ 
	Qid q; 
	vlong l; 
	SDpart *pp; 
2000/0706    
	SDperm *perm; 
2000/0506    
	SDunit *unit; 
2001/0905    
	SDev *sdev; 
	int rv; 
2000/0506    
 
2001/0905    
	sdev = sdgetdev(DEV(c->qid)); 
	assert(sdev); 
	unit = sdev->unit[UNIT(c->qid)]; 
 
	rv = -1; 
2000/0506    
	switch(i){ 
	case Qctl: 
2001/0905    
		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qctl),  
			   unit->vers, QTFILE); 
2000/0706    
		perm = &unit->ctlperm; 
2001/0527    
		if(emptystr(perm->user)){ 
			kstrdup(&perm->user, eve); 
2000/0706    
			perm->perm = 0640; 
		} 
		devdir(c, q, "ctl", 0, perm->user, perm->perm, dp); 
2001/0905    
		rv = 1; 
		break; 
 
2000/0506    
	case Qraw: 
2001/0905    
		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qraw),  
			   unit->vers, QTFILE); 
2000/0706    
		perm = &unit->rawperm; 
2001/0527    
		if(emptystr(perm->user)){ 
			kstrdup(&perm->user, eve); 
			perm->perm = DMEXCL|0600; 
2000/0706    
		} 
		devdir(c, q, "raw", 0, perm->user, perm->perm, dp); 
2001/0905    
		rv = 1; 
		break; 
 
2000/0506    
	case Qpart: 
		pp = &unit->part[PART(c->qid)]; 
		l = (pp->end - pp->start) * (vlong)unit->secsize; 
2001/0905    
		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qpart),  
			   unit->vers+pp->vers, QTFILE); 
2001/0527    
		if(emptystr(pp->user)) 
			kstrdup(&pp->user, eve); 
2000/0506    
		devdir(c, q, pp->name, l, pp->user, pp->perm, dp); 
2001/0905    
		rv = 1; 
		break; 
	} 
	 
	decref(&sdev->r); 
	return rv; 
2000/0506    
} 
 
static int 
2001/0905    
sd1gen(Chan* c, int i, Dir* dp) 
2000/0506    
{ 
2001/0905    
	Qid q; 
 
2000/0506    
	switch(i){ 
2001/0905    
	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; 
2000/0506    
	} 
	return -1; 
} 
 
static int 
2001/0527    
sdgen(Chan* c, char*, Dirtab*, int, int s, Dir* dp) 
2000/0506    
{ 
	Qid q; 
	vlong l; 
	int i, r; 
	SDpart *pp; 
	SDunit *unit; 
2001/0905    
	SDev *sdev; 
2000/0506    
 
	switch(TYPE(c->qid)){ 
2001/0905    
	case Qtopdir: { 
2000/0506    
		if(s == DEVDOTDOT){ 
2001/0905    
			mkqid(&q, QID(0, s, 0, Qtopdir), 0, QTDIR); 
2001/0527    
			sprint(up->genbuf, "#%C", sddevtab.dc); 
			devdir(c, q, up->genbuf, 0, eve, 0555, dp); 
2000/0506    
			return 1; 
1994/0913    
		} 
2001/0905    
 
		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; 
2000/0506    
		} 
2001/0905    
		 
		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; 
	} 
 
2000/0506    
	case Qunitdir: 
		if(s == DEVDOTDOT){ 
2001/0905    
			mkqid(&q, QID(0, s, 0, Qtopdir), 0, QTDIR); 
2001/0527    
			sprint(up->genbuf, "#%C", sddevtab.dc); 
			devdir(c, q, up->genbuf, 0, eve, 0555, dp); 
2000/0506    
			return 1; 
		} 
2001/0905    
		 
		if ((sdev = sdgetdev(DEV(c->qid))) == nil) { 
			devdir(c, q, "unavailable", 0, eve, 0, dp); 
			return 1; 
		} 
 
		unit = sdev->unit[UNIT(c->qid)]; 
2000/0506    
		qlock(&unit->ctl); 
2000/0617    
 
		/* 
		 * Check for media change. 
		 * If one has already been detected, sectors will be zero. 
2000/0706    
		 * If there is one waiting to be detected, online 
		 * will return > 1. 
2000/0617    
		 * Online is a bit of a large hammer but does the job. 
		 */ 
2000/0706    
		if(unit->sectors == 0 
		|| (unit->dev->ifc->online && unit->dev->ifc->online(unit) > 1)) 
2000/0506    
			sdinitpart(unit); 
2000/0617    
 
2000/0506    
		i = s+Qunitbase; 
		if(i < Qpart){ 
			r = sd2gen(c, i, dp); 
			qunlock(&unit->ctl); 
2001/0905    
			decref(&sdev->r); 
2000/0506    
			return r; 
		} 
		i -= Qpart; 
2000/0809    
		if(unit->part == nil || i >= unit->npart){ 
2000/0506    
			qunlock(&unit->ctl); 
2001/0905    
			decref(&sdev->r); 
2000/0506    
			break; 
		} 
		pp = &unit->part[i]; 
2000/0617    
		if(!pp->valid){ 
2000/0506    
			qunlock(&unit->ctl); 
2001/0905    
			decref(&sdev->r); 
2000/0506    
			return 0; 
		} 
		l = (pp->end - pp->start) * (vlong)unit->secsize; 
2001/0905    
		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), i, Qpart),  
			    unit->vers+pp->vers, QTFILE); 
2001/0527    
		if(emptystr(pp->user)) 
			kstrdup(&pp->user, eve); 
2000/0506    
		devdir(c, q, pp->name, l, pp->user, pp->perm, dp); 
		qunlock(&unit->ctl); 
2001/0905    
		decref(&sdev->r); 
2000/0506    
		return 1; 
	case Qraw: 
	case Qctl: 
	case Qpart: 
2001/0905    
		if ((sdev = sdgetdev(DEV(c->qid))) == nil) { 
			devdir(c, q, "unavailable", 0, eve, 0, dp); 
			return 1; 
		} 
		unit = sdev->unit[UNIT(c->qid)]; 
2000/0506    
		qlock(&unit->ctl); 
		r = sd2gen(c, TYPE(c->qid), dp); 
		qunlock(&unit->ctl); 
2001/0905    
		decref(&sdev->r); 
2000/0506    
		return r; 
2001/0907    
	case Qtopctl: 
	case Qtopstat: 
		return sd1gen(c, TYPE(c->qid), dp); 
2000/0506    
	default: 
		break; 
1994/0913    
	} 
2000/0506    
 
	return -1; 
1994/0913    
} 
 
1997/0327    
static Chan* 
2000/0506    
sdattach(char* spec) 
1994/0913    
{ 
2000/0506    
	Chan *c; 
	char *p; 
	SDev *sdev; 
2001/0905    
	int idno, subno, i; 
1994/0913    
 
2001/0905    
	if(ndevs == 0 || *spec == '\0'){ 
2000/0506    
		c = devattach(sddevtab.dc, spec); 
2001/0905    
		mkqid(&c->qid, QID(0, 0, 0, Qtopdir), 0, QTDIR); 
2000/0506    
		return c; 
1996/0607    
	} 
1998/0512    
 
2000/0506    
	if(spec[0] != 's' || spec[1] != 'd') 
		error(Ebadspec); 
	idno = spec[2]; 
	subno = strtol(&spec[3], &p, 0); 
	if(p == &spec[3]) 
		error(Ebadspec); 
2001/0905    
 
	qlock(&devslock); 
	for (sdev = nil, i = 0; i != ndevs; i++) 
		if ((sdev = devs[i].dt_dev) != nil && sdev->idno == idno) 
2000/0506    
			break; 
2001/0905    
 
	if (i == ndevs || subno >= sdev->nunit || sdgetunit(sdev, subno) == nil) { 
		qunlock(&devslock); 
2000/0506    
		error(Enonexist); 
2001/0905    
	} 
	incref(&sdev->r); 
	qunlock(&devslock); 
2000/0506    
 
	c = devattach(sddevtab.dc, spec); 
2001/0905    
	mkqid(&c->qid, QID(sdev->idno, subno, 0, Qunitdir), 0, QTDIR); 
	c->dev = (sdev->idno << UnitLOG) + subno; 
	decref(&sdev->r); 
2000/0506    
	return c; 
1994/0913    
} 
 
2001/0527    
static Walkqid* 
sdwalk(Chan* c, Chan* nc, char** name, int nname) 
2000/0506    
{ 
2001/0527    
	return devwalk(c, nc, name, nname, nil, 0, sdgen); 
2000/0506    
} 
 
1997/0327    
static int 
2001/0527    
sdstat(Chan* c, uchar* db, int n) 
1994/0913    
{ 
2001/0527    
	return devstat(c, db, n, nil, 0, sdgen); 
1994/0913    
} 
 
1997/0327    
static Chan* 
2000/0506    
sdopen(Chan* c, int omode) 
1994/0913    
{ 
2000/0506    
	SDpart *pp; 
	SDunit *unit; 
2001/0905    
	SDev *sdev; 
	uchar tp; 
2000/0506    
 
	c = devopen(c, omode, 0, 0, sdgen); 
2001/0905    
	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)]; 
 
2000/0506    
	switch(TYPE(c->qid)){ 
2000/0617    
	case Qctl: 
		c->qid.vers = unit->vers; 
		break; 
2000/0526    
	case Qraw: 
2000/0617    
		c->qid.vers = unit->vers; 
2000/0531    
		if(!canlock(&unit->rawinuse)){ 
			c->flag &= ~COPEN; 
2000/0526    
			error(Einuse); 
2000/0531    
		} 
2000/0526    
		unit->state = Rawcmd; 
		break; 
2000/0506    
	case Qpart: 
		qlock(&unit->ctl); 
		if(waserror()){ 
			qunlock(&unit->ctl); 
			c->flag &= ~COPEN; 
			nexterror(); 
		} 
		pp = &unit->part[PART(c->qid)]; 
2000/0617    
		c->qid.vers = unit->vers+pp->vers; 
2000/0506    
		qunlock(&unit->ctl); 
		poperror(); 
		break; 
	} 
2001/0905    
	decref(&sdev->r); 
2000/0506    
	return c; 
1994/0913    
} 
 
1997/0327    
static void 
2000/0506    
sdclose(Chan* c) 
1994/0913    
{ 
2000/0506    
	SDunit *unit; 
2001/0905    
	SDev *sdev; 
1994/0913    
 
2001/0527    
	if(c->qid.type & QTDIR) 
1994/0913    
		return; 
2000/0506    
	if(!(c->flag & COPEN)) 
		return; 
1994/0913    
 
2000/0506    
	switch(TYPE(c->qid)){ 
	default: 
		break; 
	case Qraw: 
2001/0905    
		sdev = sdgetdev(DEV(c->qid)); 
		if (sdev) { 
			unit = sdev->unit[UNIT(c->qid)]; 
			unlock(&unit->rawinuse); 
			decref(&sdev->r); 
		} 
2000/0506    
		break; 
1996/0607    
	} 
1994/0913    
} 
 
1997/0327    
static long 
2000/0506    
sdbio(Chan* c, int write, char* a, long len, vlong off) 
1994/0913    
{ 
2000/0617    
	int nchange; 
2000/0506    
	long l; 
	uchar *b; 
	SDpart *pp; 
	SDunit *unit; 
2001/0905    
	SDev *sdev; 
2000/0506    
	ulong bno, max, nb, offset; 
1998/0319    
 
2001/0905    
	sdev = sdgetdev(DEV(c->qid)); 
	if (sdev == nil) 
		error(Enonexist); 
	unit = sdev->unit[UNIT(c->qid)]; 
	if (unit == nil) 
		error(Enonexist); 
1994/0913    
 
2000/0617    
	nchange = 0; 
2000/0506    
	qlock(&unit->ctl); 
2000/0617    
	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 */ 
2000/0506    
		qunlock(&unit->ctl); 
2001/0905    
		decref(&sdev->r); 
2000/0506    
		nexterror(); 
	} 
2000/0617    
	pp = &unit->part[PART(c->qid)]; 
	if(unit->vers+pp->vers != c->qid.vers) 
2000/0506    
		error(Eio); 
1994/0913    
 
2000/0506    
	/* 
	 * 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. 
	 */ 
	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); 
2001/0905    
		decref(&sdev->r); 
2000/0506    
		poperror(); 
		return 0; 
	} 
	if(!(unit->inquiry[1] & 0x80)){ 
		qunlock(&unit->ctl); 
		poperror(); 
	} 
1996/0607    
 
2000/0506    
	b = sdmalloc(nb*unit->secsize); 
	if(b == nil) 
		error(Enomem); 
	if(waserror()){ 
		sdfree(b); 
2001/0905    
		if(!(unit->inquiry[1] & 0x80)) 
			decref(&sdev->r);		/* gadverdamme! */ 
2000/0506    
		nexterror(); 
	} 
1996/0607    
 
2000/0506    
	offset = off%unit->secsize; 
	if(write){ 
		if(offset || (len%unit->secsize)){ 
2000/0524    
			l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno); 
			if(l < 0) 
2000/0506    
				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); 
2000/0524    
		l = unit->dev->ifc->bio(unit, 0, 1, b, nb, bno); 
		if(l < 0) 
2000/0506    
			error(Eio); 
		if(l < offset) 
			len = 0; 
		else if(len > l - offset) 
			len = l - offset; 
	} 
	else { 
2000/0524    
		l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno); 
		if(l < 0) 
2000/0506    
			error(Eio); 
		if(l < offset) 
			len = 0; 
		else if(len > l - offset) 
			len = l - offset; 
		memmove(a, b+offset, len); 
	} 
	sdfree(b); 
	poperror(); 
1995/0108    
 
2000/0506    
	if(unit->inquiry[1] & 0x80){ 
		qunlock(&unit->ctl); 
		poperror(); 
	} 
1997/0408    
 
2001/0905    
	decref(&sdev->r); 
2000/0506    
	return len; 
} 
1994/0913    
 
2000/0506    
static long 
sdrio(SDreq* r, void* a, long n) 
1994/0913    
{ 
2000/0506    
	void *data; 
1994/0913    
 
2000/0506    
	if(n >= SDmaxio || n < 0) 
		error(Etoobig); 
1996/0607    
 
2000/0506    
	data = nil; 
	if(n){ 
		if((data = sdmalloc(n)) == nil) 
			error(Enomem); 
		if(r->write) 
			memmove(data, a, n); 
	} 
	r->data = data; 
	r->dlen = n; 
1996/0607    
 
2000/0506    
	if(waserror()){ 
		if(data != nil){ 
			sdfree(data); 
			r->data = nil; 
1995/0417    
		} 
2000/0506    
		nexterror(); 
1995/0417    
	} 
 
2000/0506    
	if(r->unit->dev->ifc->rio(r) != SDok) 
		error(Eio); 
1996/0607    
 
2000/0506    
	if(!r->write && r->rlen > 0) 
		memmove(a, data, r->rlen); 
	if(data != nil){ 
		sdfree(data); 
		r->data = nil; 
	} 
	poperror(); 
1996/0607    
 
2000/0506    
	return r->rlen; 
} 
1994/0913    
 
2000/0506    
static long 
sdread(Chan *c, void *a, long n, vlong off) 
{ 
2001/0905    
	char *p, *e, *buf; 
2000/0506    
	SDpart *pp; 
	SDunit *unit; 
2001/0905    
	SDev *sdev; 
2000/0506    
	ulong offset; 
	int i, l, status; 
1994/0913    
 
2000/0506    
	offset = off; 
	switch(TYPE(c->qid)){ 
	default: 
		error(Eperm); 
2001/0905    
	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; 
 
2000/0506    
	case Qtopdir: 
	case Qunitdir: 
		return devdirread(c, a, n, 0, 0, sdgen); 
2001/0905    
 
2000/0506    
	case Qctl: 
2001/0905    
		sdev = sdgetdev(DEV(c->qid)); 
		if (sdev == nil) 
			error(Enonexist); 
 
		unit = sdev->unit[UNIT(c->qid)]; 
2000/0506    
		p = malloc(READSTR); 
		l = snprint(p, READSTR, "inquiry %.48s\n", 
			(char*)unit->inquiry+8); 
		qlock(&unit->ctl); 
2000/0607    
		/* 
		 * 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); 
2000/0617    
		if(unit->sectors == 0) 
			sdinitpart(unit); 
		if(unit->sectors){ 
2000/0607    
			if(unit->dev->ifc->rctl == nil) 
2000/0506    
				l += snprint(p+l, READSTR-l, 
					"geometry %ld %ld\n", 
					unit->sectors, unit->secsize); 
			pp = unit->part; 
2000/0809    
			for(i = 0; i < unit->npart; i++){ 
2000/0506    
				if(pp->valid) 
					l += snprint(p+l, READSTR-l, 
2001/0527    
						"part %s %lud %lud\n", 
						pp->name, pp->start, pp->end); 
2000/0506    
				pp++; 
			} 
2000/0104    
		} 
2000/0506    
		qunlock(&unit->ctl); 
2001/0905    
		decref(&sdev->r); 
2000/0506    
		l = readstr(offset, a, n, p); 
		free(p); 
		return l; 
2001/0905    
 
2000/0506    
	case Qraw: 
2001/0905    
		sdev = sdgetdev(DEV(c->qid)); 
		if (sdev == nil) 
			error(Enonexist); 
 
		unit = sdev->unit[UNIT(c->qid)]; 
2000/1129    
		qlock(&unit->raw); 
		if(waserror()){ 
			qunlock(&unit->raw); 
2001/0905    
			decref(&sdev->r); 
2000/1129    
			nexterror(); 
		} 
2000/0506    
		if(unit->state == Rawdata){ 
			unit->state = Rawstatus; 
2000/1129    
			i = sdrio(unit->req, a, n); 
2000/0506    
		} 
		else if(unit->state == Rawstatus){ 
			status = unit->req->status; 
			unit->state = Rawcmd; 
			free(unit->req); 
			unit->req = nil; 
2000/1129    
			i = readnum(0, a, n, status, NUMSIZE); 
		} else 
			i = 0; 
		qunlock(&unit->raw); 
2001/0905    
		decref(&sdev->r); 
2000/1129    
		poperror(); 
		return i; 
2001/0905    
 
2000/0506    
	case Qpart: 
		return sdbio(c, 0, a, n, off); 
1994/0913    
	} 
1996/0607    
 
2000/0506    
	return 0; 
1994/0913    
} 
 
2001/0905    
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,	}, 
}; 
 
1994/0913    
static long 
2000/0506    
sdwrite(Chan *c, void *a, long n, vlong off) 
1994/0913    
{ 
2000/0506    
	Cmdbuf *cb; 
	SDreq *req; 
	SDunit *unit; 
2001/0905    
	SDev *sdev; 
2000/0506    
	ulong end, start; 
1994/0913    
 
2000/0506    
	switch(TYPE(c->qid)){ 
	default: 
		error(Eperm); 
2001/0905    
	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; 
	} 
2000/0506    
	case Qctl: 
		cb = parsecmd(a, n); 
2001/0905    
		sdev = sdgetdev(DEV(c->qid)); 
		if (sdev == nil) 
			error(Enonexist); 
		unit = sdev->unit[UNIT(c->qid)]; 
1995/0403    
 
2000/0506    
		qlock(&unit->ctl); 
		if(waserror()){ 
			qunlock(&unit->ctl); 
2001/0905    
			decref(&sdev->r); 
2000/0506    
			free(cb); 
			nexterror(); 
		} 
2000/0617    
		if(unit->vers != c->qid.vers) 
2000/0506    
			error(Eio); 
1994/0913    
 
2000/0506    
		if(cb->nf < 1) 
			error(Ebadctl); 
		if(strcmp(cb->f[0], "part") == 0){ 
2000/0617    
			if(cb->nf != 4) 
2000/0506    
				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); 
2001/0905    
		decref(&sdev->r); 
2000/0506    
		poperror(); 
		free(cb); 
		break; 
2000/0526    
 
2000/0506    
	case Qraw: 
2001/0905    
		sdev = sdgetdev(DEV(c->qid)); 
		if (sdev == nil) 
			error(Enonexist); 
		unit = sdev->unit[UNIT(c->qid)]; 
2000/1129    
		qlock(&unit->raw); 
		if(waserror()){ 
			qunlock(&unit->raw); 
2001/0905    
			decref(&sdev->r); 
2000/1129    
			nexterror(); 
		} 
2000/0526    
		switch(unit->state){ 
		case Rawcmd: 
			if(n < 6 || n > sizeof(req->cmd)) 
2000/0506    
				error(Ebadarg); 
2000/0526    
			if((req = malloc(sizeof(SDreq))) == nil) 
2000/0506    
				error(Enomem); 
			req->unit = unit; 
			memmove(req->cmd, a, n); 
			req->clen = n; 
			req->flags = SDnosense; 
			req->status = ~0; 
 
			unit->req = req; 
			unit->state = Rawdata; 
2000/0526    
			break; 
 
		case Rawstatus: 
			unit->state = Rawcmd; 
			free(unit->req); 
			unit->req = nil; 
			error(Ebadusefd); 
 
		case Rawdata: 
2000/0506    
			if(unit->state != Rawdata) 
				error(Ebadusefd); 
			unit->state = Rawstatus; 
 
			unit->req->write = 1; 
2000/1129    
			n = sdrio(unit->req, a, n); 
1996/0607    
		} 
2000/1129    
		qunlock(&unit->raw); 
2001/0905    
		decref(&sdev->r); 
2000/1129    
		poperror(); 
2001/0527    
		break; 
2000/0506    
	case Qpart: 
		return sdbio(c, 1, a, n, off); 
1994/0913    
	} 
2000/0506    
 
	return n; 
} 
 
2001/0527    
static int 
sdwstat(Chan* c, uchar* dp, int n) 
2000/0506    
{ 
2001/0527    
	Dir d[2]; 
2000/0506    
	SDpart *pp; 
2000/0706    
	SDperm *perm; 
2000/0506    
	SDunit *unit; 
2001/0905    
	SDev *sdev; 
2000/0506    
 
2001/0527    
	if(c->qid.type & QTDIR) 
2001/0905    
		error(Eperm);  
2000/0506    
 
2001/0905    
	sdev = sdgetdev(DEV(c->qid)); 
	if (sdev == nil) 
		error(Enonexist); 
	unit = sdev->unit[UNIT(c->qid)]; 
2000/0506    
	qlock(&unit->ctl); 
	if(waserror()){ 
		qunlock(&unit->ctl); 
2001/0905    
		decref(&sdev->r); 
2000/0506    
		nexterror(); 
1994/0913    
	} 
1996/0607    
 
2000/0706    
	switch(TYPE(c->qid)){ 
	default: 
2000/0506    
		error(Eperm); 
2000/0706    
	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; 
	} 
2000/0506    
 
2001/0527    
	if(strcmp(up->user, perm->user) && !iseve()) 
2000/0706    
		error(Eperm); 
2001/0527    
	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); 
2000/0506    
 
	qunlock(&unit->ctl); 
2001/0905    
	decref(&sdev->r); 
2000/0506    
	poperror(); 
2001/0527    
	return n; 
1994/0913    
} 
2000/0506    
 
2001/0905    
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; 
2001/0911    
		sdev->unit = (SDunit **)malloc(sdev->nunit * sizeof(SDunit *)); 
		sdev->unitflg = (int *)malloc(sdev->nunit * sizeof(int)); 
		assert(sdev->unit && sdev->unitflg); 
2001/0905    
 
		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); 
} 
 
2000/0506    
Dev sddevtab = { 
	'S', 
	"sd", 
 
	sdreset, 
	devinit, 
	sdattach, 
	sdwalk, 
	sdstat, 
	sdopen, 
	devcreate, 
	sdclose, 
	sdread, 
	devbread, 
	sdwrite, 
	devbwrite, 
	devremove, 
	sdwstat, 
2001/0905    
	devpower, 
	sdconfig, 
2000/0506    
}; 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)