~kris/9p

9hist

b18742d81bcf26a103f2aa0412d42fa9ef5c6f82 — David du Colombier 25 years ago f12f083
Plan 9 from Bell Labs 2001-09-05
M bitsy/clock.c => bitsy/clock.c +13 -0
@@ 135,3 135,16 @@ void
		}
	}
}

ulong
TK2MS(ulong ticks)
{
	uvlong t, hz;

	t = ticks;
	hz = HZ;
	t *= 1000L;
	t = t/hz;
	ticks = t;
	return ticks;
}

M pc/archmp.c => pc/archmp.c +2 -0
@@ 98,6 98,8 @@ PCArch archmp = {

	mpinit,					/* intrinit */
	mpintrenable,				/* intrenable */
	0,					/* intrvecno */
	0,					/* intrdisable */

	0,					/* clockenable */
};

M pc/clock.c => pc/clock.c +12 -0
@@ 41,3 41,15 @@ microdelay(int microsecs)
	aamloop(microsecs);
}

ulong
TK2MS(ulong ticks)
{
	uvlong t, hz;

	t = ticks;
	hz = HZ;
	t *= 1000L;
	t = t/hz;
	ticks = t;
	return ticks;
}

M pc/dat.h => pc/dat.h +18 -0
@@ 241,6 241,8 @@ struct PCArch

	void	(*intrinit)(void);
	int	(*intrenable)(Vctl*);
	int	(*intrvecno)(int);
	int	(*intrdisable)(int);

	void	(*clockenable)(void);
	uvlong	(*fastclock)(uvlong*);


@@ 278,3 280,19 @@ Mach* machp[MAXMACH];

extern Mach	*m;
#define up	(((Mach*)MACHADDR)->externup)

/*
 *  hardware info about a device
 */
typedef struct {
	ulong	port;	
	int		size;
} port_t;

struct DevConf
{
	ulong	interrupt;	/* interrupt number */
	char		*type;	/* card type, malloced */
	int		nports;	/* Number of ports */
	port_t	*ports;	/* The ports themselves */
};

M pc/devarch.c => pc/devarch.c +18 -0
@@ 52,6 52,9 @@ static Dirtab archdir[Qmax] = {
};
Lock archwlock;	/* the lock is only for changing archdir */
int narchdir = Qbase;
int (*_pcmspecial)(char *, ISAConf *);
void (*_pcmspecialclose)(int);


/*
 * Add a file to the #P listing.  Once added, you can't delete it.


@@ 476,6 479,8 @@ PCArch archgeneric = {

	i8259init,				/* intrinit */
	i8259enable,				/* intrenable */
	i8259vecno,				/* vector number */
	i8259disable,				/* intrdisable */

	i8253enable,				/* clockenable */



@@ 723,3 728,16 @@ fastticks(uvlong *hz)
{
	return (*arch->fastclock)(hz);
}

int
pcmspecial(char *idstr, ISAConf *isa)
{
	return (_pcmspecial  != nil)? _pcmspecial(idstr, isa): -1;
}

void
pcmspecialclose(int a)
{
	if (_pcmspecialclose != nil)
		_pcmspecialclose(a);
}

M pc/devi82365.c => pc/devi82365.c +15 -4
@@ 348,8 348,8 @@ decrefp(PCMslot *pp)
/*
 *  look for a card whose version contains 'idstr'
 */
int
pcmspecial(char *idstr, ISAConf *isa)
static int
pcmcia_pcmspecial(char *idstr, ISAConf *isa)
{
	PCMslot *pp;
	extern char *strstr(char*, char*);


@@ 389,8 389,8 @@ pcmspecial(char *idstr, ISAConf *isa)
	return -1;
}

void
pcmspecialclose(int slotno)
static void
pcmcia_pcmspecialclose(int slotno)
{
	PCMslot *pp;



@@ 579,6 579,17 @@ i82365reset(void)
	PCMslot *pp;
	char buf[32];

	if (!getconf("pcmcia0"))
		return;
	
	if (_pcmspecial) {
		print("#y: PCMCIA and CardBus at the same time?\n");
		return;
	}
	
	_pcmspecial = pcmcia_pcmspecial;
	_pcmspecialclose = pcmcia_pcmspecialclose;

	if(already)
		return;
	already = 1;

A pc/devpccard.c => pc/devpccard.c +1820 -0
@@ 0,0 1,1820 @@
/*
     cardbus and pcmcia (grmph) support.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"

#define MAP(x,o)	(Rmap + (x)*0x8 + o)

enum {
	TI_vid = 0x104c,
	TI_1450_did = 0xAC1B,

	Ricoh_vid = 0x1180,
	Ricoh_476_did = 0x0476,

	Nslots = 4,		/* Maximum number of CardBus slots to use */

	K = 1024,
	M = K * K,

	LegacyAddr = 0x3e0,
	NUMEVENTS = 10,
};

typedef struct {
	ushort	r_vid;
	ushort	r_did;
	char		*r_name;
} variant_t;

static variant_t variant[] = {
{	Ricoh_vid,	Ricoh_476_did,	"Ricoh 476 PCI/Cardbus bridge",	},
{	TI_vid,		TI_1450_did,		"TI PCI-1450 Cardbus Controller",	},
};

/* Cardbus registers */
enum {
	SocketEvent = 0,
	SocketMask = 1,
	SocketState = 2,
	SocketForce = 3,
	SocketControl = 4,
};

enum {
	SS_CCD = 3 << 1,
	SS_POWER = 1 << 3,
	SS_PC16 = 1 << 4,
	SS_CBC = 1 << 5,
	SS_NOTCARD = 1 << 7,
	SS_BADVCC = 1 << 9,
	SS_5V = 1 << 10,
	SS_3V = 1 << 11,
};

enum {
	SC_5V = 0x22,
	SC_3V = 0x33,
};

enum {
	SE_CCD = SS_CCD,
	SE_POWER = SS_POWER,
};

enum {
	PciPCR_IO = 1 << 0,
	PciPCR_MEM = 1 << 1,
	PciPCR_Master = 1 << 2,

	Nbars = 6,
	Ncmd = 10,
	CBIRQ = 9,

	PC16,
	PC32,
};

enum {
	Ti82365,
	Tpd6710,
	Tpd6720,
	Tvg46x,
};

static char *chipname[] = {
[Ti82365]		"Intel 82365SL",
[Tpd6710]	"Cirrus Logic PD6710",
[Tpd6720]	"Cirrus Logic PD6720",
[Tvg46x]		"Vadem VG-46x",
};

/*
 *  Intel 82365SL PCIC controller for the PCMCIA or
 *  Cirrus Logic PD6710/PD6720 which is mostly register compatible
 */
enum
{
	/*
	 *  registers indices
	 */
	Rid=		0x0,		/* identification and revision */
	Ris=		0x1,		/* interface status */
	Rpc=	 	0x2,		/* power control */
	 Foutena=	 (1<<7),	/*  output enable */
	 Fautopower=	 (1<<5),	/*  automatic power switching */
	 Fcardena=	 (1<<4),	/*  PC card enable */
	Rigc= 		0x3,		/* interrupt and general control */
	 Fiocard=	 (1<<5),	/*  I/O card (vs memory) */
	 Fnotreset=	 (1<<6),	/*  reset if not set */	
	 FSMIena=	 (1<<4),	/*  enable change interrupt on SMI */ 
	Rcsc= 		0x4,		/* card status change */
	Rcscic= 	0x5,		/* card status change interrupt config */
	 Fchangeena=	 (1<<3),	/*  card changed */
	 Fbwarnena=	 (1<<1),	/*  card battery warning */
	 Fbdeadena=	 (1<<0),	/*  card battery dead */
	Rwe= 		0x6,		/* address window enable */
	 Fmem16=	 (1<<5),	/*  use A23-A12 to decode address */
	Rio= 		0x7,		/* I/O control */
	 Fwidth16=	 (1<<0),	/*  16 bit data width */
	 Fiocs16=	 (1<<1),	/*  IOCS16 determines data width */
	 Fzerows=	 (1<<2),	/*  zero wait state */
	 Ftiming=	 (1<<3),	/*  timing register to use */
	Riobtm0lo=	0x8,		/* I/O address 0 start low byte */
	Riobtm0hi=	0x9,		/* I/O address 0 start high byte */
	Riotop0lo=	0xa,		/* I/O address 0 stop low byte */
	Riotop0hi=	0xb,		/* I/O address 0 stop high byte */
	Riobtm1lo=	0xc,		/* I/O address 1 start low byte */
	Riobtm1hi=	0xd,		/* I/O address 1 start high byte */
	Riotop1lo=	0xe,		/* I/O address 1 stop low byte */
	Riotop1hi=	0xf,		/* I/O address 1 stop high byte */
	Rmap=		0x10,		/* map 0 */

	/*
	 *  CL-PD67xx extension registers
	 */
	Rmisc1=		0x16,		/* misc control 1 */
	 F5Vdetect=	 (1<<0),
	 Fvcc3V=	 (1<<1),
	 Fpmint=	 (1<<2),
	 Fpsirq=	 (1<<3),
	 Fspeaker=	 (1<<4),
	 Finpack=	 (1<<7),
	Rfifo=		0x17,		/* fifo control */
	 Fflush=	 (1<<7),	/*  flush fifo */
	Rmisc2=		0x1E,		/* misc control 2 */
	 Flowpow=	 (1<<1),	/*  low power mode */
	Rchipinfo=	0x1F,		/* chip information */
	Ratactl=	0x26,		/* ATA control */

	/*
	 *  offsets into the system memory address maps
	 */
	Mbtmlo=		0x0,		/* System mem addr mapping start low byte */
	Mbtmhi=		0x1,		/* System mem addr mapping start high byte */
	 F16bit=	 (1<<7),	/*  16-bit wide data path */
	Mtoplo=		0x2,		/* System mem addr mapping stop low byte */
	Mtophi=		0x3,		/* System mem addr mapping stop high byte */
	 Ftimer1=	 (1<<6),	/*  timer set 1 */
	Mofflo=		0x4,		/* Card memory offset address low byte */
	Moffhi=		0x5,		/* Card memory offset address high byte */
	 Fregactive=	 (1<<6),	/*  attribute memory */

	/*
	 *  configuration registers - they start at an offset in attribute
	 *  memory found in the CIS.
	 */
	Rconfig=	0,
	 Creset=	 (1<<7),	/*  reset device */
	 Clevel=	 (1<<6),	/*  level sensitive interrupt line */
};

/*
 *  read and crack the card information structure enough to set
 *  important parameters like power
 */
/* cis memory walking */
typedef struct Cisdat {
	uchar		*cisbase;
	int			cispos;
	int			cisskip;
	int			cislen;
} Cisdat;

typedef struct {
	char			pi_verstr[512];		/* Version string */
	PCMmap		pi_mmap[4];		/* maps, last is always for the kernel */
	ulong		pi_conf_addr;		/* Config address */
	uchar		pi_conf_present;	/* Config register present */
	int			pi_nctab;			/* In use configuration tables */
	PCMconftab	pi_ctab[8];		/* Configuration tables */
	PCMconftab	*pi_defctab;		/* Default conftab */

	int			pi_port;			/* Actual port usage */
	int			pi_irq;			/* Actual IRQ usage */
} pcminfo_t;

typedef struct {
	QLock;
	variant_t		*cb_variant;		/* Which CardBus chipset */
	Pcidev		*cb_pci;			/* The bridge itself */
	ulong		*cb_regs;			/* Cardbus registers */
	int			cb_ltype;			/* Legacy type */
	int			cb_lindex;		/* Legacy port index address */
	int			cb_ldata;			/* Legacy port data address */
	int			cb_lbase;			/* Base register for this socket */

	int			cb_state;			/* Current state of card */
	int			cb_type;			/* Type of card */
	pcminfo_t	cb_linfo;			/* PCMCIA slot info */

	int			cb_refs;			/* Number of refs to slot */
	QLock		cb_refslock;		/* inc/dev ref lock */
} cb_t;

static int managerstarted;

enum {
	Mshift=	12,
	Mgran=	(1<<Mshift),	/* granularity of maps */
	Mmask=	~(Mgran-1),	/* mask for address bits important to the chip */
};

static cb_t cbslots[Nslots];
static int nslots;

static ulong exponent[8] = { 
	1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 
};

static ulong vmant[16] = {
	10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90,
};

static ulong mantissa[16] = { 
	0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 
};

static char Enocard[] = "No card in slot";

static void cbint(Ureg *, void *);
static int powerup(cb_t *);
static void configure(cb_t *);
static void managecard(cb_t *);
static void cardmanager(void *);
static void eject(cb_t *);
static void interrupt(Ureg *, void *);
static void powerdown(cb_t *cb);
static void unconfigure(cb_t *cb);

static void i82365probe(cb_t *cb, int lindex, int ldata);
static void i82365configure(cb_t *cb);
static PCMmap *isamap(cb_t *cb, ulong offset, int len, int attr);
static void isaunmap(PCMmap* m);
static uchar rdreg(cb_t *cb, int index);
static void wrreg(cb_t *cb, int index, uchar val);
static int readc(Cisdat *cis, uchar *x);
static void tvers1(cb_t *cb, Cisdat *cis, int );
static void tcfig(cb_t *cb, Cisdat *cis, int );
static void tentry(cb_t *cb, Cisdat *cis, int );
static int vcode(int volt);
static int pccard_pcmspecial(char *idstr, ISAConf *isa);
static void pccard_pcmspecialclose(int slotno);

enum {
	CardDetected,
	CardPowered,
	CardEjected,
	CardConfigured,
};

static char *messages[] = {
[CardDetected]		"CardDetected",
[CardPowered]		"CardPowered",
[CardEjected]		"CardEjected",
[CardConfigured]	"CardConfigured",
};

enum {
	SlotEmpty,
	SlotFull,
	SlotPowered,
	SlotConfigured,
};

static char *states[] = {
[SlotEmpty]		"SlotEmpty",
[SlotFull]			"SlotFull",
[SlotPowered]		"SlotPowered",
[SlotConfigured]	"SlotConfigured",
};

static void
engine(cb_t *cb, int message)
{
	//print("engine(%d): %s(%s)\n", 
	//	 (int)(cb - cbslots), states[cb->cb_state], messages[message]);
	switch (cb->cb_state) {
	case SlotEmpty:

		switch (message) {
		case CardDetected:
			cb->cb_state = SlotFull;
			powerup(cb);
			break;
		case CardEjected:
			break;
		default:
			print("#Y%d: Invalid message %s in SlotEmpty state\n",
				(int)(cb - cbslots), messages[message]);
			break;
		}
		break;

	case SlotFull:

		switch (message) {
		case CardPowered:
			cb->cb_state = SlotPowered;
			configure(cb);
			break;
		case CardEjected:
			cb->cb_state = SlotEmpty;
			powerdown(cb);
			break;
		default:
			print("#Y%d: Invalid message %s in SlotFull state\n",
				(int)(cb - cbslots), messages[message]);
			break;
		}
		break;

	case SlotPowered:

		switch (message) {
		case CardConfigured:
			cb->cb_state = SlotConfigured;
			break;
		case CardEjected:
			cb->cb_state = SlotEmpty;
			unconfigure(cb);
			powerdown(cb);
			break;
		default:
			print("#Y%d: Invalid message %s in SlotPowered state\n",
				(int)(cb - cbslots), messages[message]);
			break;
		}
		break;

	case SlotConfigured:

		switch (message) {
		case CardEjected:
			cb->cb_state = SlotEmpty;
			unconfigure(cb);
			powerdown(cb);
			break;
		default:
			print("#Y%d: Invalid message %s in SlotConfigured state\n",
				(int)(cb - cbslots), messages[message]);
			break;
		}
		break;
	}
}

static void
qengine(cb_t *cb, int message)
{
	qlock(cb);
	engine(cb, message);
	qunlock(cb);
}

typedef struct {
	cb_t	*e_cb;
	int	e_message;
} events_t;

static Lock levents;
static events_t events[NUMEVENTS];
static Rendez revents;
static int nevents;

static void
iengine(cb_t *cb, int message)
{
	if (nevents >= NUMEVENTS) {
		print("#Y: Too many events queued, discarding request\n");
		return;
	}
	ilock(&levents);
	events[nevents].e_cb = cb;
	events[nevents].e_message = message;
	nevents++;
	iunlock(&levents);
	wakeup(&revents);
}

static int
eventoccured(void)
{
	return nevents > 0;
}

static void
processevents(void *)
{
	while (1) {
		int message;
		cb_t *cb;

		sleep(&revents, (int (*)(void *))eventoccured, nil);

		cb = nil;
		message = 0;
		ilock(&levents);
		if (nevents > 0) {
			cb = events[0].e_cb;
			message = events[0].e_message;
			nevents--;
			if (nevents > 0)
				memmove(events, &events[1], nevents * sizeof(events_t));
		}
		iunlock(&levents);

		if (cb)
			qengine(cb, message);
	}
}

static void
interrupt(Ureg *, void *)
{
	int i;

	for (i = 0; i != nslots; i++) {
		cb_t *cb = &cbslots[i];
		ulong event, state;

		event= cb->cb_regs[SocketEvent];
		state = cb->cb_regs[SocketState];
		rdreg(cb, Rcsc);	/* Ack the interrupt */

		//	print("interrupt: slot %d, event %.8lX, state %.8lX, (%s)\n", 
		//		(int)(cb - cbslots), event, state, states[cb->cb_state]);

		if (event & SE_CCD) {
			cb->cb_regs[SocketEvent] |= SE_CCD;	/* Ack interrupt */
			if (state & SE_CCD)
				iengine(cb, CardEjected);
			else
				iengine(cb, CardDetected);
		}

		if (event & SE_POWER) {
			cb->cb_regs[SocketEvent] |= SE_POWER;	/* Ack interrupt */
			iengine(cb, CardPowered);
		}
	}
}

void
devpccardlink(void)
{
	static int initialized;
	Pcidev *pci;
	int i;
	uchar intl;

	if (initialized) 
		return;
	initialized = 1;

	if (!getconf("pccard0"))
		return;

	_pcmspecial = pccard_pcmspecial;
	_pcmspecialclose = pccard_pcmspecialclose;

	/* Allocate legacy space */
	if (ioalloc(LegacyAddr, 2, 0, "i82365.0") < 0)
		print("#Y: WARNING: Cannot allocate legacy ports\n");

	/* Find all CardBus controllers */
	pci = nil;
	intl = (uchar)-1;
	while ((pci = pcimatch(pci, 0, 0)) != nil) {
		ulong baddr;
		cb_t *cb;
		int slot;

		for (i = 0; i != nelem(variant); i++)
			if (pci->vid == variant[i].r_vid && pci->did == variant[i].r_did)
				break;
		if (i == nelem(variant))
			continue;

		/* initialize this slot */
		slot = nslots++;
		cb = &cbslots[slot];

		cb->cb_pci = pci;
		cb->cb_variant = &variant[i];
		
		if (intl != -1 && intl != pci->intl)
			intrenable(pci->intl, interrupt, cb, pci->tbdf, "cardbus");
		intl = pci->intl;
		baddr = pcicfgr32(cb->cb_pci, PciBAR0);

		cb->cb_regs = (ulong *)KADDR(upamalloc(baddr, 4096, 0));
		cb->cb_state = SlotEmpty;

		/* Don't really know what to do with this... */
		i82365probe(cb, LegacyAddr, LegacyAddr + 1);

		print("#Y%ld: %s, %.8ulX intl %d (%s%s)\n", cb - cbslots, 
			 variant[i].r_name, baddr, pci->intl,
			 states[cb->cb_state], 
			 (cb->cb_state == SlotEmpty)? "": 
			 (cb->cb_type == PC16)? "PCMCIA": "CB");
	}

	if (nslots == 0)
		return;

	for (i = 0; i != nslots; i++) {
		cb_t *cb = &cbslots[i];

		if ((cb->cb_regs[SocketState] & SE_CCD) == 0)
			engine(cb, CardDetected);
	}

	delay(500);			/* Allow time for power up */

	for (i = 0; i != nslots; i++) {
		cb_t *cb = &cbslots[i];

		if (cb->cb_regs[SocketState] & SE_POWER)
			engine(cb, CardPowered);

		/* Enable interrupt on all events */
		cb->cb_regs[SocketMask] |= 0xF;	
		wrreg(cb, Rcscic, 0xC);
	}
}

static int
powerup(cb_t *cb)
{
	ulong state;
	ushort bcr;

	if ((state = cb->cb_regs[SocketState]) & SS_PC16) {
	
		print("#Y%ld: Probed a PC16 card, powering up card\n", cb - cbslots);
		cb->cb_type = PC16;
		memset(&cb->cb_linfo, 0, sizeof(pcminfo_t));

		/* power up and unreset, wait's are empirical (???) */
		wrreg(cb, Rpc, Fautopower|Foutena|Fcardena);
		delay(300);
		wrreg(cb, Rigc, 0);
		delay(100);
		wrreg(cb, Rigc, Fnotreset);

		return 1;
	}

	if (cb->cb_regs[SocketState] & SS_CCD)
		return 0;

	if ((state & SS_CBC) == 0 || (state & SS_NOTCARD)) {
		print("#Y%ld: No cardbus card inserted\n", cb - cbslots);
		return 0;
	}

	if (state & SS_BADVCC) {
		print("#Y%ld: Bad VCC request to card, powering down card!\n", 
			 cb - cbslots);
		cb->cb_regs[SocketControl] = 0;
		return 0;
	}

	if ((state & SS_3V) == 0 && (state & SS_5V) == 0) {
		print("#Y%ld: Unsupported voltage, powering down card!\n", 
			cb - cbslots);
		cb->cb_regs[SocketControl] = 0;
		return 0;
	}

	print("#Y%ld: card %spowered at %d volt\n", cb - cbslots, 
		(state & SS_POWER)? "": "not ", 
		(state & SS_3V)? 3: (state & SS_5V)? 5: -1);

	/* Power up the card
	 * and make sure the secondary bus is not in reset.
	 */
	cb->cb_regs[SocketControl] = (state & SS_5V)? SC_5V: SC_3V;
	delay(50);
	bcr = pcicfgr16(cb->cb_pci, PciBCR);
	bcr &= ~0x40;
	pcicfgw16(cb->cb_pci, PciBCR, bcr);
	delay(100);

	cb->cb_type = PC32;
	return 1;
}

static void
powerdown(cb_t *cb)
{
	ushort bcr;

	if (cb->cb_type == PC16) {

		wrreg(cb, Rpc, 0);	/* turn off card power */
		wrreg(cb, Rwe, 0);	/* no windows */

		cb->cb_type = -1;
		return;
	}

	bcr = pcicfgr16(cb->cb_pci, PciBCR);
	bcr |= 0x40;
	pcicfgw16(cb->cb_pci, PciBCR, bcr);
	cb->cb_regs[SocketControl] = 0;
	cb->cb_type = -1;
}

static void
configure(cb_t *cb)
{
	int i;
	Pcidev *pci;

	print("configuring slot %d (%s)\n", (int)(cb - cbslots), states[cb->cb_state]);
	if (cb->cb_state == SlotConfigured)
		return;
	engine(cb, CardConfigured);

	delay(50);					/* Emperically established */

	if (cb->cb_type == PC16) {
		i82365configure(cb);
		return;
	}

	/* Scan the CardBus for new PCI devices */
	pciscan(pcicfgr8(cb->cb_pci, PciSBN), &cb->cb_pci->bridge);
	pci = cb->cb_pci->bridge;
	while (pci) {
		ulong size, bar;
		int memindex, ioindex;

		/* Treat the found device as an ordinary PCI card.  It seems that the 
		     CIS is not always present in CardBus cards.  XXX, need to support 
		     multifunction cards */
		memindex = ioindex = 0;
		for (i = 0; i != Nbars; i++) {

			if (pci->mem[i].size == 0) continue;
			if (pci->mem[i].bar & 1) {

				// Allocate I/O space
				if (ioindex > 1) {
					print("#Y%ld: WARNING: Can only configure 2 I/O slots\n", cb - cbslots);
					continue;
				}
				bar = ioreserve(-1, pci->mem[i].size, 0, "cardbus");
				pci->mem[i].bar = bar | 1;
				pcicfgw32(pci, PciBAR0 + i * sizeof(ulong), 
					          pci->mem[i].bar);
				pcicfgw16(cb->cb_pci, PciCBIBR0 + ioindex * 8, bar);
				pcicfgw16(cb->cb_pci, PciCBILR0 + ioindex * 8, 
						 bar + pci->mem[i].size - 1);
				ioindex++;
				continue;
			}

			// Allocating memory space
			if (memindex > 1) {
				print("#Y%ld: WARNING: Can only configure 2 memory slots\n", cb - cbslots);
				continue;
			}

			bar = upamalloc(0, pci->mem[i].size, BY2PG);
			pci->mem[i].bar = bar | (pci->mem[i].bar & 0x80);
			pcicfgw32(pci, PciBAR0 + i * sizeof(ulong), pci->mem[i].bar);
			pcicfgw32(cb->cb_pci, PciCBMBR0 + memindex * 8, bar);
			pcicfgw32(cb->cb_pci, PciCBMLR0 + memindex * 8, 
					  bar + pci->mem[i].size - 1);

			if (pci->mem[i].bar & 0x80)
				/* Enable prefetch */
				pcicfgw16(cb->cb_pci, PciBCR, 
						 pcicfgr16(cb->cb_pci, PciBCR) | 
							          (1 << (8 + memindex)));

			memindex++;
		}

		if ((size = pcibarsize(pci, PciEBAR0)) > 0) {

			if (memindex > 1)
				print("#Y%ld: WARNING: Too many memory spaces, not mapping ROM space\n",
					cb - cbslots);
			else {
				pci->rom.bar = upamalloc(0, size, BY2PG);
				pci->rom.size = size;

				pcicfgw32(pci, PciEBAR0, pci->rom.bar);
				pcicfgw32(cb->cb_pci, PciCBMBR0 + memindex * 8,
						 pci->rom.bar);
				pcicfgw32(cb->cb_pci, PciCBMLR0 + memindex * 8, 
						 pci->rom.bar + pci->rom.size - 1);
			}
		}

		/* Set the basic PCI registers for the device */
		pcicfgw16(pci, PciPCR, 
				 pcicfgr16(pci, PciPCR) | 
				 	PciPCR_IO|PciPCR_MEM|PciPCR_Master);
		pcicfgw8(pci, PciCLS, 8);
		pcicfgw8(pci, PciLTR, 64);

		if (pcicfgr8(pci, PciINTP)) {
			pci->intl = pcicfgr8(cb->cb_pci, PciINTL);
			pcicfgw8(pci, PciINTL, pci->intl);

			/* Route interrupts to INTA#/B# */
			pcicfgw16(cb->cb_pci, PciBCR, 
					  pcicfgr16(cb->cb_pci, PciBCR) & ~(1 << 7));
		}
			
		pci = pci->list;
	}
}

static void
unconfigure(cb_t *cb)
{
	Pcidev *pci;
	int i, ioindex, memindex;

	if (cb->cb_type == PC16) {
		print("#Y%d: Don't know how to unconfigure a PC16 card\n",
			 (int)(cb - cbslots));

		memset(&cb->cb_linfo, 0, sizeof(pcminfo_t));
		return;
	}

	pci = cb->cb_pci->bridge;
	if (pci == nil) 
		return;		/* Not configured */
	cb->cb_pci->bridge = nil;		

	memindex = ioindex = 0;
	while (pci) {
		Pcidev *_pci;

		for (i = 0; i != Nbars; i++) {
			if (pci->mem[i].size == 0) continue;
			if (pci->mem[i].bar & 1) {
				iofree(pci->mem[i].bar & ~1);
				pcicfgw16(cb->cb_pci, PciCBIBR0 + ioindex * 8, 
						 (ushort)-1);
				pcicfgw16(cb->cb_pci, PciCBILR0 + ioindex * 8, 0);
				ioindex++;
				continue;
			}

			upafree(pci->mem[i].bar & ~0xF, pci->mem[i].size);
			pcicfgw32(cb->cb_pci, PciCBMBR0 + memindex * 8, 
				          (ulong)-1);
			pcicfgw32(cb->cb_pci, PciCBMLR0 + memindex * 8, 0);
			pcicfgw16(cb->cb_pci, PciBCR, 
					 pcicfgr16(cb->cb_pci, PciBCR) & 
							       ~(1 << (8 + memindex)));
			memindex++;
		}

		if (pci->rom.bar && memindex < 2) {
			upafree(pci->rom.bar & ~0xF, pci->rom.size);
			pcicfgw32(cb->cb_pci, PciCBMBR0 + memindex * 8, 
					  (ulong)-1);
			pcicfgw32(cb->cb_pci, PciCBMLR0 + memindex * 8, 0);
			memindex++;
		}

		_pci = pci->list;
		free(_pci);
		pci = _pci;
	}
}

static void
i82365configure(cb_t *cb)
{
	int this;
	Cisdat cis;
	PCMmap *m;
	uchar type, link;

	/*
	 * Read all tuples in attribute space.
	 */
	m = isamap(cb, 0, 0, 1);
	if(m == 0)
		return;

	cis.cisbase = KADDR(m->isa);
	cis.cispos = 0;
	cis.cisskip = 2;
	cis.cislen = m->len;

	/* loop through all the tuples */
	for(;;){
		this = cis.cispos;
		if(readc(&cis, &type) != 1)
			break;
		if(type == 0xFF)
			break;
		if(readc(&cis, &link) != 1)
			break;

		switch(type){
		default:
			break;
		case 0x15:
			tvers1(cb, &cis, type);
			break;
		case 0x1A:
			tcfig(cb, &cis, type);
			break;
		case 0x1B:
			tentry(cb, &cis, type);
			break;
		}

		if(link == 0xFF)
			break;
		cis.cispos = this + (2+link);
	}
	isaunmap(m);
}

/*
 *  look for a card whose version contains 'idstr'
 */
static int
pccard_pcmspecial(char *idstr, ISAConf *isa)
{
	int i, irq;
	PCMconftab *ct, *et;
	pcminfo_t *pi;
	cb_t *cb;
	uchar x, we, *p;

	cb = nil;
	for (i = 0; i != nslots; i++) {
		cb = &cbslots[i];

		qlock(cb);
		if (cb->cb_state == SlotConfigured &&
		    cb->cb_type == PC16 && 
		    strstr(cb->cb_linfo.pi_verstr, idstr)) 
			break;
		qunlock(cb);
	}

	if (i == nslots) {
		print("#Y: %s not found\n", idstr);
		return -1;
	}

	pi = &cb->cb_linfo;

	/*
 	  *  configure the PCMslot for IO.  We assume very heavily that we can read
 	  *  configuration info from the CIS.  If not, we won't set up correctly.
 	  */
	irq = isa->irq;
	if(irq == 2)
		irq = 9;

	et = &pi->pi_ctab[pi->pi_nctab];
	ct = nil;
	for(i = 0; i < isa->nopt; i++){
		int index;
		char *cp;

		if(strncmp(isa->opt[i], "index=", 6))
			continue;
		index = strtol(&isa->opt[i][6], &cp, 0);
		if(cp == &isa->opt[i][6] || index >= pi->pi_nctab) {
			qunlock(cb);
			print("#Y%d: Cannot find index %d in conf table\n", 
				 (int)(cb - cbslots), index);
			return -1;
		}
		ct = &pi->pi_ctab[index];
	}

	if(ct == nil){
		PCMconftab *t;

		/* assume default is right */
		if(pi->pi_defctab)
			ct = pi->pi_defctab;
		else
			ct = pi->pi_ctab;
	
		/* try for best match */
		if(ct->nio == 0
		|| ct->io[0].start != isa->port || ((1<<irq) & ct->irqs) == 0){
			for(t = pi->pi_ctab; t < et; t++)
				if(t->nio
				&& t->io[0].start == isa->port
				&& ((1<<irq) & t->irqs)){
					ct = t;
					break;
				}
		}
		if(ct->nio == 0 || ((1<<irq) & ct->irqs) == 0){
			for(t = pi->pi_ctab; t < et; t++)
				if(t->nio && ((1<<irq) & t->irqs)){
					ct = t;
					break;
				}
		}
		if(ct->nio == 0){
			for(t = pi->pi_ctab; t < et; t++)
				if(t->nio){
					ct = t;
					break;
				}
		}
	}

	if(ct == et || ct->nio == 0) {
		qunlock(cb);
		print("#Y%d: No configuration?\n", (int)(cb - cbslots));
		return -1;
	}
	if(isa->port == 0 && ct->io[0].start == 0) {
		qunlock(cb);
		print("#Y%d: No part or start address\n", (int)(cb - cbslots));
		return -1;
	}

	/* route interrupts */
	isa->irq = irq;
	wrreg(cb, Rigc, irq | Fnotreset | Fiocard);

	/* set power and enable device */
	x = vcode(ct->vpp1);
	wrreg(cb, Rpc, x|Fautopower|Foutena|Fcardena);

	/* 16-bit data path */
	if(ct->bit16)
		x = Ftiming|Fiocs16|Fwidth16;
	else
		x = Ftiming;
	if(ct->nio == 2 && ct->io[1].start)
		x |= x<<4;
	wrreg(cb, Rio, x);

	/*
	 * enable io port map 0
	 * the 'top' register value includes the last valid address
	 */
	if(isa->port == 0)
		isa->port = ct->io[0].start;
	we = rdreg(cb, Rwe);
	wrreg(cb, Riobtm0lo, isa->port);
	wrreg(cb, Riobtm0hi, isa->port>>8);
	i = isa->port+ct->io[0].len-1;
	wrreg(cb, Riotop0lo, i);
	wrreg(cb, Riotop0hi, i>>8);
	we |= 1<<6;
	if(ct->nio == 2 && ct->io[1].start){
		wrreg(cb, Riobtm1lo, ct->io[1].start);
		wrreg(cb, Riobtm1hi, ct->io[1].start>>8);
		i = ct->io[1].start+ct->io[1].len-1;
		wrreg(cb, Riotop1lo, i);
		wrreg(cb, Riotop1hi, i>>8);
		we |= 1<<7;
	}
	wrreg(cb, Rwe, we);

	/* only touch Rconfig if it is present */
	if(pi->pi_conf_present & (1<<Rconfig)){
		PCMmap *m;

		/*  Reset adapter */
		m = isamap(cb, pi->pi_conf_addr + Rconfig, 1, 1);
		p = KADDR(m->isa + pi->pi_conf_addr + Rconfig - m->ca);

		/* set configuration and interrupt type */
		x = ct->index;
		if((ct->irqtype & 0x20) && ((ct->irqtype & 0x40)==0 || isa->irq>7))
			x |= Clevel;
		*p = x;
		delay(5);

		isaunmap(m);
	}

	pi->pi_port = isa->port;
	pi->pi_irq = isa->irq;
	qunlock(cb);

	print("#Y%d: %s irq %ld, port %lX\n", (int)(cb - cbslots), pi->pi_verstr, isa->irq, isa->port);
	return (int)(cb - cbslots);
}

static void
pccard_pcmspecialclose(int slotno)
{
	cb_t *cb = &cbslots[slotno];

	wrreg(cb, Rwe, 0);	/* no windows */
}

static int
xcistuple(int slotno, int tuple, int subtuple, void *v, int nv, int attr)
{
	PCMmap *m;
	Cisdat cis;
	int i, l;
	uchar *p;
	uchar type, link, n, c;
	int this, subtype;
	cb_t *cb = &cbslots[slotno];

	m = isamap(cb, 0, 0, attr);
	if(m == 0)
		return -1;

	cis.cisbase = KADDR(m->isa);
	cis.cispos = 0;
	cis.cisskip = attr ? 2 : 1;
	cis.cislen = m->len;

	/* loop through all the tuples */
	for(i = 0; i < 1000; i++){
		this = cis.cispos;
		if(readc(&cis, &type) != 1)
			break;
		if(type == 0xFF)
			break;
		if(readc(&cis, &link) != 1)
			break;
		if(link == 0xFF)
			break;

		n = link;
		if (link > 1 && subtuple != -1) {
			if (readc(&cis, &c) != 1)
				break;
			subtype = c;
			n--;
		} else
			subtype = -1;

		if(type == tuple && subtype == subtuple) {
			p = v;
			for(l=0; l<nv && l<n; l++)
				if(readc(&cis, p++) != 1)
					break;
			isaunmap(m);
			return nv;
		}
		cis.cispos = this + (2+link);
	}
	isaunmap(m);
	return -1;
}

static Chan*
pccardattach(char *spec)
{
	if (!managerstarted) {
		managerstarted = 1;
		kproc("cardbus", processevents, nil);
	}
	return devattach('Y', spec);
}

enum
{
	Qdir,
	Qctl,

	Nents = 1,
};

#define SLOTNO(c)	((ulong)((c->qid.path>>8)&0xff))
#define TYPE(c)	((ulong)(c->qid.path&0xff))
#define QID(s,t)	(((s)<<8)|(t))

static int
pccardgen(Chan *c, char*, Dirtab *, int , int i, Dir *dp)
{
	int slotno;
	Qid qid;
	long len;
	int entry;

	if(i == DEVDOTDOT){
		mkqid(&qid, Qdir, 0, QTDIR);
		devdir(c, qid, "#Y", 0, eve, 0555, dp);
		return 1;
	}

	len = 0;
	if(i >= Nents * nslots) return -1;
	slotno = i / Nents;
	entry = i % Nents;
	if (entry == 0) {
		qid.path = QID(slotno, Qctl);
		snprint(up->genbuf, sizeof up->genbuf, "cb%dctl", slotno);
	}
	else {
		/* Entries for memory regions.  I'll implement them when 
		     needed. (pb) */
	}
	qid.vers = 0;	
	qid.type = QTFILE;
	devdir(c, qid, up->genbuf, len, eve, 0660, dp);
	return 1;
}

static Walkqid*
pccardwalk(Chan *c, Chan *nc, char **name, int nname)
{
	return devwalk(c, nc, name, nname, 0, 0, pccardgen);
}

static int
pccardstat(Chan *c, uchar *db, int n)
{
	return devstat(c, db, n, 0, 0, pccardgen);
}

static void
increfp(cb_t *cb)
{
	qlock(&cb->cb_refslock);
	cb->cb_refs++;
	qunlock(&cb->cb_refslock);
}

static void
decrefp(cb_t *cb)
{
	qlock(&cb->cb_refslock);
	cb->cb_refs--;
	qunlock(&cb->cb_refslock);
}

static Chan*
pccardopen(Chan *c, int omode)
{
	if (c->qid.type & QTDIR){
		if(omode != OREAD)
			error(Eperm);
	} else
		increfp(&cbslots[SLOTNO(c)]);
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

static void
pccardclose(Chan *c)
{
	if(c->flag & COPEN)
		if((c->qid.type & QTDIR) == 0)
			decrefp(&cbslots[SLOTNO(c)]);
}

static long
pccardread(Chan *c, void *a, long n, vlong offset)
{
	cb_t *cb;
	char *buf, *p, *e;

	switch(TYPE(c)){
	case Qdir:
		return devdirread(c, a, n, 0, 0, pccardgen);

	case Qctl:
		buf = p = malloc(READSTR);
		buf[0] = 0;
		e = p + READSTR;
	
		cb = &cbslots[SLOTNO(c)];
		qlock(cb);
		p = seprint(p, e, "slot %ld: %s; ", cb - cbslots, states[cb->cb_state]);

		switch (cb->cb_type) {
		case -1:
			seprint(p, e, "\n");
			break;

		case PC32:
			if (cb->cb_pci->bridge) {
				Pcidev *pci = cb->cb_pci->bridge;
				int i;

				while (pci) {
					p = seprint(p, e, "%.4uX %.4uX; irq %d\n", 
							  pci->vid, pci->did, pci->intl);
					for (i = 0; i != Nbars; i++)
						if (pci->mem[i].size)
							p = seprint(p, e, 
									  "\tmem[%d] %.8uX (%.8uX)\n",
									  i, pci->mem[i].bar, 
									  pci->mem[i].size);
					if (pci->rom.size)
						p = seprint(p, e, "\tROM %.8uX (%.8uX)\n", i, 
								  pci->rom.bar, pci->rom.size);
					pci = pci->list;
				}
			}
			break;

		case PC16:
			if (cb->cb_state == SlotConfigured) {
				pcminfo_t *pi = &cb->cb_linfo;

				p = seprint(p, e, "%s port %X; irq %d;\n",
						  pi->pi_verstr, pi->pi_port,
						  pi->pi_irq);
				for (n = 0; n != pi->pi_nctab; n++) {
					PCMconftab *ct;
					int i;

					ct = &pi->pi_ctab[n];
					p = seprint(p, e, 
						"\tconfiguration[%d] irqs %.4X; vpp %d, %d; %s\n",
							  n, ct->irqs, ct->vpp1, ct->vpp2,
							  (ct == pi->pi_defctab)? "(default);": "");
					for (i = 0; i != ct->nio; i++)
						if (ct->io[i].len > 0)
							p = seprint(p, e, "\t\tio[%d] %.8lX %d\n",
									  i, ct->io[i].start, ct->io[i].len);
				}
			}
			break;
		}
		qunlock(cb);
			
		n = readstr(offset, a, n, buf);
		free(buf);
		return n;
	}
	return 0;
}

static long
pccardwrite(Chan *c, void *v, long n, vlong)
{
	Rune r;
	ulong n0;
	int i, nf;
	char buf[255], *field[Ncmd], *device;
	cb_t *cb;

	n0 = n;
	switch(TYPE(c)){
	case Qctl:
		cb = &cbslots[SLOTNO(c)];
		if(n > sizeof(buf)-1) n = sizeof(buf)-1;
		memmove(buf, v, n);
		buf[n] = '\0';

		nf = getfields(buf, field, Ncmd, 1, " \t\n");
		for (i = 0; i != nf; i++) {
			if (!strcmp(field[i], "down")) {

				if (i + 1 < nf && *field[i + 1] == '#') {
					device = field[++i];
					device += chartorune(&r, device);
					if ((n = devno(r, 1)) >= 0 && devtab[n]->config)
						devtab[n]->config(0, device, nil);
				}
				qengine(cb, CardEjected);
			}
			else if (!strcmp(field[i], "power")) {
				if ((cb->cb_regs[SocketState] & SS_CCD) == 0)
					qengine(cb, CardDetected);
			}
			else
				error(Ebadarg);
		}
		break;
	}
	return n0 - n;
}

Dev pccarddevtab = {
	'Y',
	"cardbus",

	devreset,
	devinit,
	pccardattach,
	pccardwalk,
	pccardstat,
	pccardopen,
	devcreate,
	pccardclose,
	pccardread,
	devbread,
	pccardwrite,
	devbwrite,
	devremove,
	devwstat,
};

static PCMmap *
isamap(cb_t *cb, ulong offset, int len, int attr)
{
	uchar we, bit;
	PCMmap *m, *nm;
	pcminfo_t *pi;
	int i;
	ulong e;

	pi = &cb->cb_linfo;

	/* convert offset to granularity */
	if(len <= 0)
		len = 1;
	e = ROUND(offset+len, Mgran);
	offset &= Mmask;
	len = e - offset;

	/* look for a map that covers the right area */
	we = rdreg(cb, Rwe);
	bit = 1;
	nm = 0;
	for(m = pi->pi_mmap; m < &pi->pi_mmap[nelem(pi->pi_mmap)]; m++){
		if((we & bit))
		if(m->attr == attr)
		if(offset >= m->ca && e <= m->cea){

			m->ref++;
			return m;
		}
		bit <<= 1;
		if(nm == 0 && m->ref == 0)
			nm = m;
	}
	m = nm;
	if(m == 0)
		return 0;

	/* if isa space isn't big enough, free it and get more */
	if(m->len < len){
		if(m->isa){
			umbfree(m->isa, m->len);
			m->len = 0;
		}
		m->isa = PADDR(umbmalloc(0, len, Mgran));
		if(m->isa == 0){
			print("isamap: out of isa space\n");
			return 0;
		}
		m->len = len;
	}

	/* set up new map */
	m->ca = offset;
	m->cea = m->ca + m->len;
	m->attr = attr;
	i = m - pi->pi_mmap;
	bit = 1<<i;
	wrreg(cb, Rwe, we & ~bit);		/* disable map before changing it */
	wrreg(cb, MAP(i, Mbtmlo), m->isa>>Mshift);
	wrreg(cb, MAP(i, Mbtmhi), (m->isa>>(Mshift+8)) | F16bit);
	wrreg(cb, MAP(i, Mtoplo), (m->isa+m->len-1)>>Mshift);
	wrreg(cb, MAP(i, Mtophi), ((m->isa+m->len-1)>>(Mshift+8)));
	offset -= m->isa;
	offset &= (1<<25)-1;
	offset >>= Mshift;
	wrreg(cb, MAP(i, Mofflo), offset);
	wrreg(cb, MAP(i, Moffhi), (offset>>8) | (attr ? Fregactive : 0));
	wrreg(cb, Rwe, we | bit);		/* enable map */
	m->ref = 1;

	return m;
}

static void
isaunmap(PCMmap* m)
{
	m->ref--;
}

/*
 *  reading and writing card registers
 */
static uchar
rdreg(cb_t *cb, int index)
{
	outb(cb->cb_lindex, cb->cb_lbase + index);
	return inb(cb->cb_ldata);
}

static void
wrreg(cb_t *cb, int index, uchar val)
{
	outb(cb->cb_lindex, cb->cb_lbase + index);
	outb(cb->cb_ldata, val);
}

static int
readc(Cisdat *cis, uchar *x)
{
	if(cis->cispos >= cis->cislen)
		return 0;
	*x = cis->cisbase[cis->cisskip*cis->cispos];
	cis->cispos++;
	return 1;
}

static ulong
getlong(Cisdat *cis, int size)
{
	uchar c;
	int i;
	ulong x;

	x = 0;
	for(i = 0; i < size; i++){
		if(readc(cis, &c) != 1)
			break;
		x |= c<<(i*8);
	}
	return x;
}

static void
tcfig(cb_t *cb, Cisdat *cis, int )
{
	uchar size, rasize, rmsize;
	uchar last;
	pcminfo_t *pi;

	if(readc(cis, &size) != 1)
		return;
	rasize = (size&0x3) + 1;
	rmsize = ((size>>2)&0xf) + 1;
	if(readc(cis, &last) != 1)
		return;

	pi = &cb->cb_linfo;
	pi->pi_conf_addr = getlong(cis, rasize);
	pi->pi_conf_present = getlong(cis, rmsize);
}

static void
tvers1(cb_t *cb, Cisdat *cis, int )
{
	uchar c, major, minor, last;
	int  i;
	pcminfo_t *pi;

	pi = &cb->cb_linfo;
	if(readc(cis, &major) != 1)
		return;
	if(readc(cis, &minor) != 1)
		return;
	last = 0;
	for(i = 0; i < sizeof(pi->pi_verstr) - 1; i++){
		if(readc(cis, &c) != 1)
			return;
		if(c == 0)
			c = ';';
		if(c == '\n')
			c = ';';
		if(c == 0xff)
			break;
		if(c == ';' && last == ';')
			continue;
		pi->pi_verstr[i] = c;
		last = c;
	}
	pi->pi_verstr[i] = 0;
}

static ulong
microvolt(Cisdat *cis)
{
	uchar c;
	ulong microvolts;
	ulong exp;

	if(readc(cis, &c) != 1)
		return 0;
	exp = exponent[c&0x7];
	microvolts = vmant[(c>>3)&0xf]*exp;
	while(c & 0x80){
		if(readc(cis, &c) != 1)
			return 0;
		switch(c){
		case 0x7d:
			break;		/* high impedence when sleeping */
		case 0x7e:
		case 0x7f:
			microvolts = 0;	/* no connection */
			break;
		default:
			exp /= 10;
			microvolts += exp*(c&0x7f);
		}
	}
	return microvolts;
}

static ulong
nanoamps(Cisdat *cis)
{
	uchar c;
	ulong nanoamps;

	if(readc(cis, &c) != 1)
		return 0;
	nanoamps = exponent[c&0x7]*vmant[(c>>3)&0xf];
	while(c & 0x80){
		if(readc(cis, &c) != 1)
			return 0;
		if(c == 0x7d || c == 0x7e || c == 0x7f)
			nanoamps = 0;
	}
	return nanoamps;
}

/*
 * only nominal voltage (feature 1) is important for config,
 * other features must read card to stay in sync.
 */
static ulong
power(Cisdat *cis)
{
	uchar feature;
	ulong mv;

	mv = 0;
	if(readc(cis, &feature) != 1)
		return 0;
	if(feature & 1)
		mv = microvolt(cis);
	if(feature & 2)
		microvolt(cis);
	if(feature & 4)
		microvolt(cis);
	if(feature & 8)
		nanoamps(cis);
	if(feature & 0x10)
		nanoamps(cis);
	if(feature & 0x20)
		nanoamps(cis);
	if(feature & 0x40)
		nanoamps(cis);
	return mv/1000000;
}

static ulong
ttiming(Cisdat *cis, int scale)
{
	uchar unscaled;
	ulong nanosecs;

	if(readc(cis, &unscaled) != 1)
		return 0;
	nanosecs = (mantissa[(unscaled>>3)&0xf]*exponent[unscaled&7])/10;
	nanosecs = nanosecs * exponent[scale];
	return nanosecs;
}

static void
timing(Cisdat *cis, PCMconftab *ct)
{
	uchar c, i;

	if(readc(cis, &c) != 1)
		return;
	i = c&0x3;
	if(i != 3)
		ct->maxwait = ttiming(cis, i);		/* max wait */
	i = (c>>2)&0x7;
	if(i != 7)
		ct->readywait = ttiming(cis, i);		/* max ready/busy wait */
	i = (c>>5)&0x7;
	if(i != 7)
		ct->otherwait = ttiming(cis, i);		/* reserved wait */
}

static void
iospaces(Cisdat *cis, PCMconftab *ct)
{
	uchar c;
	int i, nio;

	ct->nio = 0;
	if(readc(cis, &c) != 1)
		return;

	ct->bit16 = ((c>>5)&3) >= 2;
	if(!(c & 0x80)){
		ct->io[0].start = 0;
		ct->io[0].len = 1<<(c&0x1f);
		ct->nio = 1;
		return;
	}

	if(readc(cis, &c) != 1)
		return;

	/*
	 * For each of the range descriptions read the
	 * start address and the length (value is length-1).
	 */
	nio = (c&0xf)+1;
	for(i = 0; i < nio; i++){
		ct->io[i].start = getlong(cis, (c>>4)&0x3);
		ct->io[i].len = getlong(cis, (c>>6)&0x3)+1;
	}
	ct->nio = nio;
}

static void
irq(Cisdat *cis, PCMconftab *ct)
{
	uchar c;

	if(readc(cis, &c) != 1)
		return;
	ct->irqtype = c & 0xe0;
	if(c & 0x10)
		ct->irqs = getlong(cis, 2);
	else
		ct->irqs = 1<<(c&0xf);
	ct->irqs &= 0xDEB8;		/* levels available to card */
}

static void
memspace(Cisdat *cis, int asize, int lsize, int host)
{
	ulong haddress, address, len;

	len = getlong(cis, lsize)*256;
	address = getlong(cis, asize)*256;
	USED(len, address);
	if(host){
		haddress = getlong(cis, asize)*256;
		USED(haddress);
	}
}

static void
tentry(cb_t *cb, Cisdat *cis, int )
{
	uchar c, i, feature;
	PCMconftab *ct;
	pcminfo_t *pi;

	pi = &cb->cb_linfo;
	if(pi->pi_nctab >= nelem(pi->pi_ctab))
		return;
	if(readc(cis, &c) != 1)
		return;
	ct = &pi->pi_ctab[pi->pi_nctab++];

	/* copy from last default config */
	if(pi->pi_defctab)
		*ct = *pi->pi_defctab;

	ct->index = c & 0x3f;

	/* is this the new default? */
	if(c & 0x40)
		pi->pi_defctab = ct;

	/* memory wait specified? */
	if(c & 0x80){
		if(readc(cis, &i) != 1)
			return;
		if(i&0x80)
			ct->memwait = 1;
	}

	if(readc(cis, &feature) != 1)
		return;
	switch(feature&0x3){
	case 1:
		ct->vpp1 = ct->vpp2 = power(cis);
		break;
	case 2:
		power(cis);
		ct->vpp1 = ct->vpp2 = power(cis);
		break;
	case 3:
		power(cis);
		ct->vpp1 = power(cis);
		ct->vpp2 = power(cis);
		break;
	default:
		break;
	}
	if(feature&0x4)
		timing(cis, ct);
	if(feature&0x8)
		iospaces(cis, ct);
	if(feature&0x10)
		irq(cis, ct);
	switch((feature>>5)&0x3){
	case 1:
		memspace(cis, 0, 2, 0);
		break;
	case 2:
		memspace(cis, 2, 2, 0);
		break;
	case 3:
		if(readc(cis, &c) != 1)
			return;
		for(i = 0; i <= (c&0x7); i++)
			memspace(cis, (c>>5)&0x3, (c>>3)&0x3, c&0x80);
		break;
	}
}

static void
i82365probe(cb_t *cb, int lindex, int ldata)
{
	uchar c, id;
	int dev = 0;	/* According to the Ricoh spec 00->3F _and_ 80->BF seem
				     to be the same socket A (ditto for B). */

	outb(lindex, Rid + (dev<<7));
	id = inb(ldata);
	if((id & 0xf0) != 0x80)
		return;		/* not a memory & I/O card */
	if((id & 0x0f) == 0x00)
		return;		/* no revision number, not possible */

	cb->cb_lindex = lindex;
	cb->cb_ldata = ldata;
	cb->cb_ltype = Ti82365;
	cb->cb_lbase = (int)(cb - cbslots) * 0x40;

	switch(id){
	case 0x82:
	case 0x83:
	case 0x84:
		/* could be a cirrus */
		outb(cb->cb_lindex, Rchipinfo + (dev<<7));
		outb(cb->cb_ldata, 0);
		c = inb(cb->cb_ldata);
		if((c & 0xc0) != 0xc0)
			break;
		c = inb(cb->cb_ldata);
		if((c & 0xc0) != 0x00)
			break;
		if(c & 0x20){
			cb->cb_ltype = Tpd6720;
		} else {
			cb->cb_ltype = Tpd6710;
		}
		break;
	}

	/* if it's not a Cirrus, it could be a Vadem... */
	if(cb->cb_ltype == Ti82365){
		/* unlock the Vadem extended regs */
		outb(cb->cb_lindex, 0x0E + (dev<<7));
		outb(cb->cb_lindex, 0x37 + (dev<<7));

		/* make the id register show the Vadem id */
		outb(cb->cb_lindex, 0x3A + (dev<<7));
		c = inb(cb->cb_ldata);
		outb(cb->cb_ldata, c|0xC0);
		outb(cb->cb_lindex, Rid + (dev<<7));
		c = inb(cb->cb_ldata);
		if(c & 0x08)
			cb->cb_ltype = Tvg46x;

		/* go back to Intel compatible id */
		outb(cb->cb_lindex, 0x3A + (dev<<7));
		c = inb(cb->cb_ldata);
		outb(cb->cb_ldata, c & ~0xC0);
	}
}

static int
vcode(int volt)
{
	switch(volt){
	case 5:
		return 1;
	case 12:
		return 2;
	default:
		return 0;
	}
}


M pc/fns.h => pc/fns.h +8 -1
@@ 37,8 37,8 @@ ulong	getcr2(void);
ulong	getcr3(void);
ulong	getcr4(void);
char*	getconf(char*);
int	havecycintr(void);
void	halt(void);
int	havecycintr(void);
int	i8042auxcmd(int);
void	i8042auxenable(void (*)(int, int));
void	i8042reset(void);


@@ 48,6 48,8 @@ void	i8253enable(void);
uvlong	i8253read(uvlong*);
void	i8259init(void);
int	i8259enable(Vctl*);
int	i8259vecno(int);
int	i8259disable(int);
void	idle(void);
void	idlehands(void);
int	inb(int);


@@ 56,12 58,14 @@ ushort	ins(int);
void	inss(int, void*, int);
ulong	inl(int);
void	insl(int, void*, int);
void intrdisable(int, void (*)(Ureg *, void *), void*, int, char*);
void	intrenable(int, void (*)(Ureg*, void*), void*, int, char*);
void	iofree(int);
void	ioinit(void);
int	iounused(int, int);
int	ioalloc(int, int, int, char*);
int	ioreserve(int, int, int, char*);
int	iprint(char*, ...);
int	isaconfig(char*, int, ISAConf*);
void	kbdinit(void);
#define	kmapinval()


@@ 93,6 97,7 @@ int	pcicfgr32(Pcidev*, int);
void	pcicfgw8(Pcidev*, int, int);
void	pcicfgw16(Pcidev*, int, int);
void	pcicfgw32(Pcidev*, int, int);
void pciclrbme(Pcidev*);
void	pcihinv(Pcidev*);
Pcidev* pcimatch(Pcidev*, int, int);
Pcidev* pcimatchtbdf(int);


@@ 102,7 107,9 @@ void	pcmcisread(PCMslot*);
int	pcmcistuple(int, int, int, void*, int);
PCMmap*	pcmmap(int, ulong, int, int);
int	pcmspecial(char*, ISAConf*);
int (*_pcmspecial)(char *, ISAConf *);
void	pcmspecialclose(int);
void (*_pcmspecialclose)(int);
void	pcmunmap(int, PCMmap*);
void	printcpufreq(void);
#define	procrestore(p)

M pc/i8259.c => pc/i8259.c +33 -0
@@ 164,3 164,36 @@ i8259enable(Vctl* v)

	return VectorPIC+irq;
}

int
i8259vecno(int irq)
{
	return VectorPIC+irq;
}

int
i8259disable(int irq)
{
	int irqbit;

	/*
	 * Given an IRQ, disable the corresponding interrupt
	 * in the 8259.
	 */
	if(irq < 0 || irq > MaxIrqPIC){
		print("i8259disable: irq %d out of range\n", irq);
		return -1;
	}
	irqbit = 1<<irq;

	ilock(&i8259lock);
	if(!(i8259mask & irqbit)){
		i8259mask |= irqbit;
		if(irq < 8)
			outb(Int0aux, i8259mask & 0xFF);
		else
			outb(Int1aux, (i8259mask>>8) & 0xFF);
	}
	iunlock(&i8259lock);
	return 0;
}

M pc/pci.c => pc/pci.c +33 -0
@@ 554,9 554,32 @@ pcicfginit(void)

	list = &pciroot;
	for(bno = 0; bno <= pcimaxbno; bno++) {
		int sbno = bno;
		bno = pcilscan(bno, list);

		while(*list)
			list = &(*list)->link;

		if (sbno == 0) {
			Pcidev *pci;

			/*
			  * If we have found a PCI-to-Cardbus bridge, make sure
			  * it has no valid mappings anymore.  
			  */
			pci = pciroot;
			while (pci) {
				if (pci->ccrb == 6 && pci->ccru == 7) {
					ushort bcr;

					/* reset the cardbus */
					bcr = pcicfgr16(pci, PciBCR);
					pcicfgw16(pci, PciBCR, 0x40 | bcr);
					pcicfgw16(pci, PciBCR, bcr);
				}
				pci = pci->link;
			}
		}
	}

	if(pciroot == nil)


@@ 866,3 889,13 @@ pcisetbme(Pcidev* p)
	pcr |= MASen;
	pcicfgw16(p, PciPCR, pcr);
}

void
pciclrbme(Pcidev* p)
{
	int pcr;

	pcr = pcicfgr16(p, PciPCR);
	pcr &= ~MASen;
	pcicfgw16(p, PciPCR, pcr);
}

M pc/sd53c8xx.c => pc/sd53c8xx.c +3 -0
@@ 2059,5 2059,8 @@ SDifc sd53c8xxifc = {
	nil,				/* wctl */

	scsibio,			/* bio */
	nil,				/* probe */
	nil,				/* clear */
	nil,				/* stat */
};


M pc/sdata.c => pc/sdata.c +130 -5
@@ 199,11 199,13 @@ typedef struct Ctlr {

	Pcidev*	pcidev;
	void	(*ienable)(Ctlr*);
	void (*idisable)(Ctlr*);
	SDev*	sdev;

	Drive*	drive[2];

	Prd*	prdt;			/* physical region descriptor table */
	void* prdtbase;

	QLock;				/* current command */
	Drive*	curdrive;


@@ 606,7 608,7 @@ retry:
	atadmamode(drive);	

	if(DEBUG & DbgCONFIG){
		print("dev %2.2uX port %uX config %4.4uX cap %4.4uX",
		print("dev %2.2uX port %uX config %4.4uX capabilities %4.4uX",
			dev, cmdport,
			iconfig, drive->info[Icapabilities]);
		print(" mwdma %4.4uX", drive->info[Imwdma]);


@@ 642,9 644,12 @@ ataprobe(int cmdport, int ctlport, int irq)
	Drive *drive;
	int dev, error, rhi, rlo;

	if(ioalloc(cmdport, 8, 0, "atacmd") < 0)
	if(ioalloc(cmdport, 8, 0, "atacmd") < 0) {
		print("ataprobe: Cannot allocate %X\n", cmdport);
		return nil;
	}
	if(ioalloc(ctlport+As, 1, 0, "atactl") < 0){
		print("ataprobe: Cannot allocate %X\n", ctlport + As);
		iofree(cmdport);
		return nil;
	}


@@ 744,11 749,28 @@ tryedd1:
		free(drive);
		goto release;
	}
	memset(ctlr, 0, sizeof(Ctlr));
	if((sdev = malloc(sizeof(SDev))) == nil){
		free(ctlr);
		free(drive);
		goto release;
	}
	memset(sdev, 0, sizeof(SDev));
	if ((sdev->unit = (SDunit **)malloc(2 * sizeof(SDunit *))) == nil) {
		free(sdev);
		free(ctlr);
		free(drive);
		goto release;
	}
	memset(sdev->unit, 0, sizeof(SDunit *) * 2);
	if ((sdev->unitflg = (int *)malloc(2 * sizeof(int))) == nil) {
		free(sdev->unit);
		free(sdev);
		free(ctlr);
		free(drive);
		goto release;
	}
	memset(sdev->unitflg, 0, sizeof(int) * 2);
	drive->ctlr = ctlr;
	if(dev == Dev0){
		ctlr->drive[0] = drive;


@@ 788,6 810,45 @@ tryedd1:
	return sdev;
}

static void
ataclear(SDev *sdev)
{
	Ctlr* ctlr;

	ctlr = sdev->ctlr;
	iofree(ctlr->cmdport);
	iofree(ctlr->ctlport + As);

	if (ctlr->drive[0])
		free(ctlr->drive[0]);
	if (ctlr->drive[1])
		free(ctlr->drive[1]);
	if (sdev->name)
		free(sdev->name);
	free(sdev->unitflg);
	free(sdev->unit);
	free(ctlr);
	free(sdev);
}

static char *
atastat(SDev *sdev, char *p, char *e)
{
	Ctlr *ctlr = sdev->ctlr;

	return seprint(p, e, "%s ata port %X ctl %X irq %d\n", 
		    	       sdev->name, ctlr->cmdport, ctlr->ctlport, ctlr->irq);
}

static SDev*
ataprobew(DevConf *cf)
{
	if (cf->nports != 2)
		error(Ebadarg);

	return ataprobe(cf->ports[0].port, cf->ports[1].port, cf->interrupt);
}

static int
atasetsense(Drive* drive, int status, int key, int asc, int ascq)
{


@@ 1676,8 1737,10 @@ atapnp(void)
					continue;

				ctlr = sdev->ctlr;
				if(ispc87415)
				if(ispc87415) {
					ctlr->ienable = pc87415ienable;
					print("pc87415disable: not yet implemented\n");
				}

				if(head != nil)
					tail->next = sdev;


@@ 1698,6 1761,42 @@ atapnp(void)
		}
	}

if(0){
	int port;
	ISAConf isa;

	/*
	 * Hack for PCMCIA drives.
	 * This will be tidied once we figure out how the whole
	 * removeable device thing is going to work.
	 */
	memset(&isa, 0, sizeof(isa));
	isa.port = 0x180;		/* change this for your machine */
	isa.irq = 11;			/* change this for your machine */

	port = isa.port+0x0C;
	channel = pcmspecial("MK2001MPL", &isa);
	if(channel == -1)
		channel = pcmspecial("SunDisk", &isa);
	if(channel == -1){
		isa.irq = 10;
		channel = pcmspecial("CF", &isa);
	}
	if(channel == -1){
		isa.irq = 10;
		channel = pcmspecial("OLYMPUS", &isa);
	}
	if(channel == -1){
		port = isa.port+0x204;
		channel = pcmspecial("ATA/ATAPI", &isa);
	}
	if(channel >= 0 && (sdev = ataprobe(isa.port, port, isa.irq)) != nil){
		if(head != nil)
			tail->next = sdev;
		else
			head = sdev;
	}
}
	return head;
}



@@ 1756,9 1855,12 @@ ataenable(SDev* sdev)
	ctlr = sdev->ctlr;

	if(ctlr->bmiba){
#define ALIGN	(4 * 1024)
		if(ctlr->pcidev != nil)
			pcisetbme(ctlr->pcidev);
		ctlr->prdt = xspanalloc(Nprd*sizeof(Prd), 4, 4*1024);
		// ctlr->prdt = xspanalloc(Nprd*sizeof(Prd), 4, 4*1024);
		ctlr->prdtbase = xalloc(Nprd * sizeof(Prd) + ALIGN);
		ctlr->prdt = (Prd *)(((ulong)ctlr->prdtbase + ALIGN) & ~(ALIGN - 1));
	}
	snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name);
	intrenable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name);


@@ 1770,6 1872,26 @@ ataenable(SDev* sdev)
}

static int
atadisable(SDev *sdev)
{
	Ctlr *ctlr;
	char name[32];

	ctlr = sdev->ctlr;
	outb(ctlr->ctlport+Dc, Nien);		/* disable interrupts */
	if (ctlr->idisable)
		ctlr->idisable(ctlr);
	snprint(name, sizeof(name), "%s (%s)", sdev->name, sdev->ifc->name);
	intrdisable(ctlr->irq, atainterrupt, ctlr, ctlr->tbdf, name);
	if (ctlr->bmiba) {
		if (ctlr->pcidev)
			pciclrbme(ctlr->pcidev);
		xfree(ctlr->prdtbase);
	}
	return 0;
}

static int
atarctl(SDunit* unit, char* p, int l)
{
	int n;


@@ 1875,7 1997,7 @@ SDifc sdataifc = {
	atalegacy,			/* legacy */
	ataid,				/* id */
	ataenable,			/* enable */
	nil,				/* disable */
	atadisable,		/* disable */

	scsiverify,			/* verify */
	scsionline,			/* online */


@@ 1884,4 2006,7 @@ SDifc sdataifc = {
	atawctl,			/* wctl */

	scsibio,			/* bio */
	ataprobew,		/* probe */
	ataclear,			/* clear */
	atastat,			/* stat */
};

M pc/sdmylex.c => pc/sdmylex.c +3 -0
@@ 1238,4 1238,7 @@ SDifc sdmylexifc = {
	nil,				/* wctl */

	scsibio,			/* bio */
	nil,				/* probe */
	nil,				/* clear */
	nil,				/* stat */
};

M pc/trap.c => pc/trap.c +24 -0
@@ 50,6 50,30 @@ intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
	iunlock(&vctllock);
}

void
intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name)
{
	Vctl **pv, *v;
	int vno;

	vno = arch->intrvecno(irq);
	ilock(&vctllock);
	pv = &vctl[vno];
	while (*pv && 
		  ((*pv)->irq != irq || (*pv)->tbdf != tbdf || (*pv)->f != f || (*pv)->a != a ||
		   strcmp((*pv)->name, name)))
		pv = &((*pv)->next);
	assert(*pv);

	v = *pv;
	*pv = (*pv)->next;	/* Link out the entry */
	
	if (vctl[vno] == nil)
		arch->intrdisable(irq);
	iunlock(&vctllock);
	xfree(v);
}

static long
irqallocread(Chan*, void *vbuf, long n, vlong offset)
{

M port/dev.c => port/dev.c +13 -0
@@ 388,3 388,16 @@ devwstat(Chan*, uchar*, int)
	error(Eperm);
	return 0;
}

void
devpower(int)
{
	error(Eperm);
}

int
devconfig(int, char *, DevConf *)
{
	error(Eperm);
	return 0;
}

M port/devmnt.c => port/devmnt.c +0 -1
@@ 670,7 670,6 @@ mntread(Chan *c, void *buf, long n, vlong off)
		if(p != e)
			error(Esbadstat);
	}

	return n;
}


M port/devsd.c => port/devsd.c +575 -111
@@ 15,11 15,14 @@
extern Dev sddevtab;
extern SDifc* sdifc[];

static QLock sdqlock;
static SDev* sdlist;
static SDunit** sdunit;
static int* sdunitflg;
static int sdnunit;
typedef struct {
	SDev 	*dt_dev;
	int		dt_nunits;		/* num units in dev */
} dev_t;

static dev_t 	*devs;		/* all devices */
static QLock	devslock;		/* insertion and removal of devices */
static int		ndevs;		/* total number of devices in the system */

enum {
	Rawcmd,


@@ 30,6 33,8 @@ enum {
enum {
	Qtopdir		= 1,		/* top level directory */
	Qtopbase,
	Qtopctl = Qtopbase,
	Qtopstat,

	Qunitdir,			/* directory per unit */
	Qunitbase,


@@ 51,12 56,22 @@ enum {
	NUnit		= (1<<UnitLOG),
	UnitMASK	= (NUnit-1),
	UnitSHIFT	= (PartLOG+TypeLOG),

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

	Ncmd = 20,
};

#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))
#define DEV(q)			((((ulong)(q).path)>>DevSHIFT) & DevMASK)
#define QID(d,u, p, t)	(((d)<<DevSHIFT)|((u)<<UnitSHIFT)|\
					 ((p)<<PartSHIFT)|((t)<<TypeSHIFT))


static void
sdaddpart(SDunit* unit, char* name, ulong start, ulong end)


@@ 206,10 221,30 @@ sdinitpart(SDunit* unit)
	return 1;
}

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;
}

static SDunit*
sdgetunit(SDev* sdev, int subno)
{
	int index;
	SDunit *unit;
	char buf[32];



@@ 219,23 254,27 @@ sdgetunit(SDev* sdev, int subno)
	 * The device will be probed if it has not already been
	 * successfully accessed.
	 */
	qlock(&sdqlock);
	index = sdev->index+subno;
	unit = sdunit[index];
	qlock(&sdev->unitlock);
	if (subno > sdev->nunit) {
		qunlock(&sdev->unitlock);
		return nil;
	}

	unit = sdev->unit[subno];
	if(unit == nil){
		/*
		 * Probe the unit only once. This decision
		 * may be a little severe and reviewed later.
		 */
		if(sdunitflg[index]){
			qunlock(&sdqlock);
		if(sdev->unitflg[subno]){
			qunlock(&sdev->unitlock);
			return nil;
		}
		if((unit = malloc(sizeof(SDunit))) == nil){
			qunlock(&sdqlock);
			qunlock(&sdev->unitlock);
			return nil;
		}
		sdunitflg[index] = 1;
		sdev->unitflg[subno] = 1;

		if(sdev->enabled == 0 && sdev->ifc->enable)
			sdev->ifc->enable(sdev);


@@ 254,42 293,21 @@ sdgetunit(SDev* sdev, int subno)
		 * sdunit[] array.
		 */
		if(unit->dev->ifc->verify(unit) == 0){
			qunlock(&sdqlock);
			qunlock(&sdev->unitlock);
			free(unit);
			return nil;
		}
		sdunit[index] = unit;
		sdev->unit[subno] = unit;
	}
	qunlock(&sdqlock);

	qunlock(&sdev->unitlock);
	return unit;
}

static SDunit*
sdindex2unit(int index)
{
	SDev *sdev;

	/*
	 * 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);
	}

	return nil;

}

static void
sdreset(void)
{
	int i;
	SDev *sdev, *tail;
	SDev *sdev, *tail, *sdlist;

	/*
	 * Probe all configured controllers and make a list


@@ 297,7 315,7 @@ sdreset(void)
	 * of units attached and marking each device with an index
	 * into the linear top-level directory array of units.
	 */
	tail = nil;
	tail = sdlist = nil;
	for(i = 0; sdifc[i] != nil; i++){
		if(sdifc[i]->pnp == nil || (sdev = sdifc[i]->pnp()) == nil)
			continue;


@@ 305,14 323,11 @@ sdreset(void)
			tail->next = sdev;
		else
			sdlist = sdev;
		for(tail = sdev; tail->next != nil; tail = tail->next){
			sdev->index = sdnunit;
			sdnunit += tail->nunit;
		}
		tail->index = sdnunit;
		sdnunit += tail->nunit;
		for(tail = sdev; tail->next != nil; tail = tail->next)
			ndevs++;
		ndevs++;
	}

	
	/*
	 * Legacy and option code goes here. This will be hard...
	 */


@@ 323,17 338,8 @@ sdreset(void)
	 * probed and structures allocated when attached.
	 * Allocate controller names for the different types.
	 */
	if(sdnunit == 0)
		return;
	if(sdnunit > NUnit)
		sdnunit = NUnit;
	if((sdunit = malloc(sdnunit*sizeof(SDunit*))) == nil)
		return;
	if((sdunitflg = malloc(sdnunit*sizeof(int))) == nil){
		free(sdunit);
		sdunit = nil;
	if(ndevs == 0)
		return;
	}
	for(i = 0; sdifc[i] != nil; i++){
		/*
		 * BUG: no check is made here or later when a


@@ 342,6 348,21 @@ sdreset(void)
		if(sdifc[i]->id)
			sdifc[i]->id(sdlist);
	}

	/* 
	  * 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++;
	}
}

static int


@@ 352,45 373,69 @@ sd2gen(Chan* c, int i, Dir* dp)
	SDpart *pp;
	SDperm *perm;
	SDunit *unit;
	SDev *sdev;
	int rv;

	unit = sdunit[UNIT(c->qid)];
	sdev = sdgetdev(DEV(c->qid));
	assert(sdev);
	unit = sdev->unit[UNIT(c->qid)];

	rv = -1;
	switch(i){
	case Qctl:
		mkqid(&q, QID(UNIT(c->qid), PART(c->qid), Qctl), unit->vers, QTFILE);
		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qctl), 
			   unit->vers, QTFILE);
		perm = &unit->ctlperm;
		if(emptystr(perm->user)){
			kstrdup(&perm->user, eve);
			perm->perm = 0640;
		}
		devdir(c, q, "ctl", 0, perm->user, perm->perm, dp);
		return 1;
		rv = 1;
		break;

	case Qraw:
		mkqid(&q, QID(UNIT(c->qid), PART(c->qid), Qraw), unit->vers, QTFILE);
		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qraw), 
			   unit->vers, QTFILE);
		perm = &unit->rawperm;
		if(emptystr(perm->user)){
			kstrdup(&perm->user, eve);
			perm->perm = DMEXCL|0600;
		}
		devdir(c, q, "raw", 0, perm->user, perm->perm, dp);
		return 1;
		rv = 1;
		break;

	case Qpart:
		pp = &unit->part[PART(c->qid)];
		l = (pp->end - pp->start) * (vlong)unit->secsize;
		mkqid(&q, QID(UNIT(c->qid), PART(c->qid), Qpart), unit->vers+pp->vers, QTFILE);
		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), PART(c->qid), Qpart), 
			   unit->vers+pp->vers, QTFILE);
		if(emptystr(pp->user))
			kstrdup(&pp->user, eve);
		devdir(c, q, pp->name, l, pp->user, pp->perm, dp);
		return 1;
	}	
	return -1;
		rv = 1;
		break;
	}
	
	decref(&sdev->r);
	return rv;
}

static int
sd1gen(Chan*, int i, Dir*)
sd1gen(Chan* c, int i, Dir* dp)
{
	Qid q;

	switch(i){
	default:
		return -1;
	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;
	}
	return -1;
}


@@ 403,36 448,70 @@ sdgen(Chan* c, char*, Dirtab*, int, int s, Dir* dp)
	int i, r;
	SDpart *pp;
	SDunit *unit;
	SDev *sdev;

	switch(TYPE(c->qid)){
	case Qtopdir:
	case Qtopdir: {
		if(s == DEVDOTDOT){
			mkqid(&q, QID(s, 0, Qtopdir), 0, QTDIR);
			mkqid(&q, QID(0, s, 0, Qtopdir), 0, QTDIR);
			sprint(up->genbuf, "#%C", sddevtab.dc);
			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
			return 1;
		}
		if(s < sdnunit){
			if((unit = sdunit[s]) == nil){
				if((unit = sdindex2unit(s)) == nil)
					return 0;
			}
			mkqid(&q, QID(s, 0, Qunitdir), 0, QTDIR);
			if(emptystr(unit->user))
				kstrdup(&unit->user, eve);
			devdir(c, q, unit->name, 0, unit->user, unit->perm, dp);
			return 1;

		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;
		}
		
		if (i == ndevs) {
			/* Run of the end of the list */
			qunlock(&devslock);
			return -1;
		}
		s -= sdnunit;
		return sd1gen(c, s+Qtopbase, dp);

		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;
	}

	case Qunitdir:
		if(s == DEVDOTDOT){
			mkqid(&q, QID(s, 0, Qtopdir), 0, QTDIR);
			mkqid(&q, QID(0, s, 0, Qtopdir), 0, QTDIR);
			sprint(up->genbuf, "#%C", sddevtab.dc);
			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
			return 1;
		}
		unit = sdunit[UNIT(c->qid)];
		
		if ((sdev = sdgetdev(DEV(c->qid))) == nil) {
			devdir(c, q, "unavailable", 0, eve, 0, dp);
			return 1;
		}

		unit = sdev->unit[UNIT(c->qid)];
		qlock(&unit->ctl);

		/*


@@ 450,32 529,42 @@ sdgen(Chan* c, char*, Dirtab*, int, int s, Dir* dp)
		if(i < Qpart){
			r = sd2gen(c, i, dp);
			qunlock(&unit->ctl);
			decref(&sdev->r);
			return r;
		}
		i -= Qpart;
		if(unit->part == nil || i >= unit->npart){
			qunlock(&unit->ctl);
			decref(&sdev->r);
			break;
		}
		pp = &unit->part[i];
		if(!pp->valid){
			qunlock(&unit->ctl);
			decref(&sdev->r);
			return 0;
		}
		l = (pp->end - pp->start) * (vlong)unit->secsize;
		mkqid(&q, QID(UNIT(c->qid), i, Qpart), unit->vers+pp->vers, QTFILE);
		mkqid(&q, QID(DEV(c->qid), UNIT(c->qid), i, Qpart), 
			    unit->vers+pp->vers, QTFILE);
		if(emptystr(pp->user))
			kstrdup(&pp->user, eve);
		devdir(c, q, pp->name, l, pp->user, pp->perm, dp);
		qunlock(&unit->ctl);
		decref(&sdev->r);
		return 1;
	case Qraw:
	case Qctl:
	case Qpart:
		unit = sdunit[UNIT(c->qid)];
		if ((sdev = sdgetdev(DEV(c->qid))) == nil) {
			devdir(c, q, "unavailable", 0, eve, 0, dp);
			return 1;
		}
		unit = sdev->unit[UNIT(c->qid)];
		qlock(&unit->ctl);
		r = sd2gen(c, TYPE(c->qid), dp);
		qunlock(&unit->ctl);
		decref(&sdev->r);
		return r;
	default:
		break;


@@ 490,11 579,11 @@ sdattach(char* spec)
	Chan *c;
	char *p;
	SDev *sdev;
	int idno, subno;
	int idno, subno, i;

	if(sdnunit == 0 || *spec == '\0'){
	if(ndevs == 0 || *spec == '\0'){
		c = devattach(sddevtab.dc, spec);
		mkqid(&c->qid, QID(0, 0, Qtopdir), 0, QTDIR);
		mkqid(&c->qid, QID(0, 0, 0, Qtopdir), 0, QTDIR);
		return c;
	}



@@ 504,16 593,23 @@ sdattach(char* spec)
	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)

	qlock(&devslock);
	for (sdev = nil, i = 0; i != ndevs; i++)
		if ((sdev = devs[i].dt_dev) != nil && sdev->idno == idno)
			break;
	}
	if(sdev == nil || sdgetunit(sdev, subno) == nil)

	if (i == ndevs || subno >= sdev->nunit || sdgetunit(sdev, subno) == nil) {
		qunlock(&devslock);
		error(Enonexist);
	}
	incref(&sdev->r);
	qunlock(&devslock);

	c = devattach(sddevtab.dc, spec);
	mkqid(&c->qid, QID(sdev->index+subno, 0, Qunitdir), 0, QTDIR);
	c->dev = sdev->index+subno;
	mkqid(&c->qid, QID(sdev->idno, subno, 0, Qunitdir), 0, QTDIR);
	c->dev = (sdev->idno << UnitLOG) + subno;
	decref(&sdev->r);
	return c;
}



@@ 534,17 630,23 @@ sdopen(Chan* c, int omode)
{
	SDpart *pp;
	SDunit *unit;
	SDev *sdev;
	uchar tp;

	c = devopen(c, omode, 0, 0, sdgen);
	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)];

	switch(TYPE(c->qid)){
	default:
		break;
	case Qctl:
		unit = sdunit[UNIT(c->qid)];
		c->qid.vers = unit->vers;
		break;
	case Qraw:
		unit = sdunit[UNIT(c->qid)];
		c->qid.vers = unit->vers;
		if(!canlock(&unit->rawinuse)){
			c->flag &= ~COPEN;


@@ 553,7 655,6 @@ sdopen(Chan* c, int omode)
		unit->state = Rawcmd;
		break;
	case Qpart:
		unit = sdunit[UNIT(c->qid)];
		qlock(&unit->ctl);
		if(waserror()){
			qunlock(&unit->ctl);


@@ 566,6 667,7 @@ sdopen(Chan* c, int omode)
		poperror();
		break;
	}
	decref(&sdev->r);
	return c;
}



@@ 573,6 675,7 @@ static void
sdclose(Chan* c)
{
	SDunit *unit;
	SDev *sdev;

	if(c->qid.type & QTDIR)
		return;


@@ 583,8 686,12 @@ sdclose(Chan* c)
	default:
		break;
	case Qraw:
		unit = sdunit[UNIT(c->qid)];
		unlock(&unit->rawinuse);
		sdev = sdgetdev(DEV(c->qid));
		if (sdev) {
			unit = sdev->unit[UNIT(c->qid)];
			unlock(&unit->rawinuse);
			decref(&sdev->r);
		}
		break;
	}
}


@@ 597,9 704,15 @@ sdbio(Chan* c, int write, char* a, long len, vlong off)
	uchar *b;
	SDpart *pp;
	SDunit *unit;
	SDev *sdev;
	ulong bno, max, nb, offset;

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

	nchange = 0;
	qlock(&unit->ctl);


@@ 612,6 725,7 @@ sdbio(Chan* c, int write, char* a, long len, vlong off)

		/* other errors; give up */
		qunlock(&unit->ctl);
		decref(&sdev->r);
		nexterror();
	}
	pp = &unit->part[PART(c->qid)];


@@ 640,6 754,7 @@ sdbio(Chan* c, int write, char* a, long len, vlong off)
		if(write)
			error(Eio);
		qunlock(&unit->ctl);
		decref(&sdev->r);
		poperror();
		return 0;
	}


@@ 653,6 768,8 @@ sdbio(Chan* c, int write, char* a, long len, vlong off)
		error(Enomem);
	if(waserror()){
		sdfree(b);
		if(!(unit->inquiry[1] & 0x80))
			decref(&sdev->r);		/* gadverdamme! */
		nexterror();
	}



@@ 696,6 813,7 @@ sdbio(Chan* c, int write, char* a, long len, vlong off)
		poperror();
	}

	decref(&sdev->r);
	return len;
}



@@ 742,9 860,10 @@ sdrio(SDreq* r, void* a, long n)
static long
sdread(Chan *c, void *a, long n, vlong off)
{
	char *p;
	char *p, *e, *buf;
	SDpart *pp;
	SDunit *unit;
	SDev *sdev;
	ulong offset;
	int i, l, status;



@@ 752,11 871,34 @@ sdread(Chan *c, void *a, long n, vlong off)
	switch(TYPE(c->qid)){
	default:
		error(Eperm);
	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;

	case Qtopdir:
	case Qunitdir:
		return devdirread(c, a, n, 0, 0, sdgen);

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

		unit = sdev->unit[UNIT(c->qid)];
		p = malloc(READSTR);
		l = snprint(p, READSTR, "inquiry %.48s\n",
			(char*)unit->inquiry+8);


@@ 785,14 927,21 @@ sdread(Chan *c, void *a, long n, vlong off)
			}
		}
		qunlock(&unit->ctl);
		decref(&sdev->r);
		l = readstr(offset, a, n, p);
		free(p);
		return l;

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

		unit = sdev->unit[UNIT(c->qid)];
		qlock(&unit->raw);
		if(waserror()){
			qunlock(&unit->raw);
			decref(&sdev->r);
			nexterror();
		}
		if(unit->state == Rawdata){


@@ 808,8 957,10 @@ sdread(Chan *c, void *a, long n, vlong off)
		} else
			i = 0;
		qunlock(&unit->raw);
		decref(&sdev->r);
		poperror();
		return i;

	case Qpart:
		return sdbio(c, 0, a, n, off);
	}


@@ 817,24 968,172 @@ sdread(Chan *c, void *a, long n, vlong off)
	return 0;
}

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,	},
};

static long
sdwrite(Chan *c, void *a, long n, vlong off)
{
	Cmdbuf *cb;
	SDreq *req;
	SDunit *unit;
	SDev *sdev;
	ulong end, start;

	switch(TYPE(c->qid)){
	default:
		error(Eperm);
	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;
	}
	case Qctl:
		cb = parsecmd(a, n);
		unit = sdunit[UNIT(c->qid)];
		sdev = sdgetdev(DEV(c->qid));
		if (sdev == nil)
			error(Enonexist);
		unit = sdev->unit[UNIT(c->qid)];

		qlock(&unit->ctl);
		if(waserror()){
			qunlock(&unit->ctl);
			decref(&sdev->r);
			free(cb);
			nexterror();
		}


@@ 862,15 1161,20 @@ sdwrite(Chan *c, void *a, long n, vlong off)
		else
			error(Ebadctl);
		qunlock(&unit->ctl);
		decref(&sdev->r);
		poperror();
		free(cb);
		break;

	case Qraw:
		unit = sdunit[UNIT(c->qid)];
		sdev = sdgetdev(DEV(c->qid));
		if (sdev == nil)
			error(Enonexist);
		unit = sdev->unit[UNIT(c->qid)];
		qlock(&unit->raw);
		if(waserror()){
			qunlock(&unit->raw);
			decref(&sdev->r);
			nexterror();
		}
		switch(unit->state){


@@ 904,6 1208,7 @@ sdwrite(Chan *c, void *a, long n, vlong off)
			n = sdrio(unit->req, a, n);
		}
		qunlock(&unit->raw);
		decref(&sdev->r);
		poperror();
		break;
	case Qpart:


@@ 920,14 1225,19 @@ sdwstat(Chan* c, uchar* dp, int n)
	SDpart *pp;
	SDperm *perm;
	SDunit *unit;
	SDev *sdev;

	if(c->qid.type & QTDIR)
		error(Eperm);
		error(Eperm); 

	unit = sdunit[UNIT(c->qid)];
	sdev = sdgetdev(DEV(c->qid));
	if (sdev == nil)
		error(Enonexist);
	unit = sdev->unit[UNIT(c->qid)];
	qlock(&unit->ctl);
	if(waserror()){
		qunlock(&unit->ctl);
		decref(&sdev->r);
		nexterror();
	}



@@ 957,10 1267,162 @@ sdwstat(Chan* c, uchar* dp, int n)
	perm->perm = (perm->perm & ~0777) | (d[0].mode & 0777);

	qunlock(&unit->ctl);
	decref(&sdev->r);
	poperror();
	return n;
}

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);
}

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


@@ 979,4 1441,6 @@ Dev sddevtab = {
	devbwrite,
	devremove,
	sdwstat,
	devpower,
	sdconfig,
};

M port/lib.h => port/lib.h +1 -0
@@ 122,6 122,7 @@ typedef struct Waitmsg	Waitmsg;
#define QTDIR		0x80		/* type bit for directories */
#define QTAPPEND	0x40		/* type bit for append only files */
#define QTEXCL		0x20		/* type bit for exclusive use files */
#define QTMOUNT		0x10		/* type bit for mounted channel */
#define QTAUTH		0x08		/* type bit for authentication file */
#define QTFILE		0x00		/* plain file */


M port/portclock.c => port/portclock.c +0 -13
@@ 86,16 86,3 @@ portclock(Ureg *ur)
		segclock(ur->pc);
	}
}

ulong
TK2MS(ulong ticks)
{
	uvlong t, hz;

	t = ticks;
	hz = HZ;
	t *= 1000L;
	t = t/hz;
	ticks = t;
	return ticks;
}

M port/portfns.h => port/portfns.h +2 -0
@@ 60,6 60,7 @@ Chan*		devattach(int, char*);
Block*		devbread(Chan*, long, ulong);
long		devbwrite(Chan*, Block*, ulong);
Chan*	devclone(Chan*);
int		devconfig(int, char *, DevConf *);
void		devcreate(Chan*, char*, int, ulong);
void		devdir(Chan*, Qid, char*, vlong, char*, long, Dir*);
long		devdirread(Chan*, char*, long, Dirtab*, int, Devgen*);


@@ 68,6 69,7 @@ void		devinit(void);
int		devno(int, int);
Chan*		devopen(Chan*, int, Dirtab*, int, Devgen*);
void		devpermcheck(char*, ulong, int);
void		devpower(int);
void		devremove(Chan*);
void		devreset(void);
int		devstat(Chan*, uchar*, int, Dirtab*, int, Devgen*);

M port/sd.h => port/sd.h +12 -2
@@ 46,17 46,24 @@ typedef struct SDunit {
	Log log;
} SDunit;

/* 
  * Each controller is represented by a SDev, each controller is responsible
  * for allocating its unit structures.
  */ 
typedef struct SDev {
	Ref		r;			/* Number of callers using device */
	SDifc*	ifc;			/* pnp/legacy */
	void	*ctlr;
	int	idno;
	char	*name;
	int	index;			/* into unit space */
	int	nunit;
	SDev*	next;

	QLock;				/* enable/disable */
	int	enabled;
	int		nunit;		/* Number of units */
	QLock	unitlock;		/* `Loading' of units */
	int		*unitflg;		/* Unit flags */
	SDunit	**unit;		/* Each controller has at least one unit */
} SDev;

typedef struct SDifc {


@@ 75,6 82,9 @@ typedef struct SDifc {
	int	(*wctl)(SDunit*, Cmdbuf*);

	long	(*bio)(SDunit*, int, int, void*, long, long);
	SDev*	(*probe)(DevConf *);
	void		(*clear)(SDev *);
	char*	(*stat)(SDev*, char*, char*);
} SDifc;

typedef struct SDreq {