~kris/9p

9hist

bba78dfc3957ae3c01fa0a34f4f14ac03e80b861 — David du Colombier 23 years ago 791cd62
Plan 9 from Bell Labs 2002-11-12
M bitsy/clock.c => bitsy/clock.c +78 -4
@@ 7,6 7,17 @@
#include	"ureg.h"
#include	"../port/error.h"


enum {
	RTCREGS	=	0x90010000,	/* real time clock registers */
	RTSR_al	=	0x01,		/* alarm detected */
	RTSR_hz	=	0x02,		/* 1Hz tick */
	RTSR_ale=	0x04,		/* alarm interrupt enable */
	RTSR_hze=	0x08,		/* 1Hz tick enable */

	Never	=	0xffffffff,
};

typedef struct OSTimer
{
	ulong		osmr[4];	/* match registers */


@@ 16,10 27,21 @@ typedef struct OSTimer
	ulong		oier;		/* timer interrupt enable register */
} OSTimer;

typedef struct RTCregs 
{
	ulong	rtar;	/* alarm */
	ulong	rcnr;	/* count */
	ulong	rttr;	/* trim */
	ulong	dummy;	/* hole */
	ulong	rtsr;	/* status */
} RTCregs;

OSTimer *timerregs = (OSTimer*)OSTIMERREGS;
RTCregs *rtcregs = (RTCregs*)RTCREGS;
static int clockinited;

static void		clockintr(Ureg*, void*);
static void	clockintr(Ureg*, void*);
static void	rtcintr(Ureg*, void*);
static uvlong	when;	/* scheduled time of next interrupt */

long	timeradjust;


@@ 30,16 52,28 @@ enum
	Maxfreq = ClockFreq/10000,	/* At most one interrupt every 100 µs */
};

void
ulong
clockpower(int on)
{
	static ulong savedtime;

	if (on){
		timerregs->ossr |= 1<<0;
		timerregs->oier = 1<<0;
		timerregs->osmr[0] = timerregs->oscr + Minfreq;
	}
		if (rtcregs->rttr == 0){
			rtcregs->rttr = 0x8000; // nominal frequency.
			rtcregs->rcnr = 0;
			rtcregs->rtar = 0xffffffff;
			rtcregs->rtsr |= RTSR_ale;
			rtcregs->rtsr |= RTSR_hze;
		}
		if (rtcregs->rcnr > savedtime)
			return rtcregs->rcnr - savedtime;
	} else
		savedtime = rtcregs->rcnr;
	clockinited = on;
	return 0L;
}

void


@@ 49,7 83,8 @@ clockinit(void)
	ulong id;

	/* map the clock registers */
	timerregs = mapspecial(OSTIMERREGS, 32);
	timerregs = mapspecial(OSTIMERREGS, sizeof(OSTimer));
	rtcregs   = mapspecial(RTCREGS, sizeof(RTCregs));

	/* enable interrupts on match register 0, turn off all others */
	timerregs->ossr |= 1<<0;


@@ 71,6 106,13 @@ clockinit(void)
	when = timerregs->oscr + Minfreq;
	timerregs->osmr[0] = when;

	/* enable RTC interrupts and alarms */
	intrenable(IRQ, IRQrtc, rtcintr, nil, "rtc");
	rtcregs->rttr = 0x8000; 	// make rcnr   1Hz
	rtcregs->rcnr = 0;		// reset counter
	rtcregs->rtsr |= RTSR_al;
	rtcregs->rtsr |= RTSR_ale;

	timersinit();

	clockinited = 1;


@@ 125,6 167,38 @@ clockintr(Ureg *ureg, void*)
}

void
rtcalarm(ulong secs)
{
	vlong t;

	if (t == 0){
		iprint("RTC alarm cancelled\n");
		rtcregs->rtsr &= ~RTSR_ale;
		rtcregs->rtar = 0xffffffff;
	} else {
		t = todget(nil);
		t = t / 1000000000ULL; // nsec to secs
		if (secs < t)
			return;
		secs -= t;
		iprint("RTC alarm set to %uld seconds from now\n", secs);
		rtcregs->rtar = rtcregs->rcnr + secs;
		rtcregs->rtsr|= RTSR_ale;
	}
}

static void
rtcintr(Ureg*, void*)
{
	/* reset interrupt */
	rtcregs->rtsr&= ~RTSR_ale;
	rtcregs->rtsr&= ~RTSR_al;

	rtcregs->rtar = 0;
	iprint("RTC alarm: %lud\n", rtcregs->rcnr);
}

void
delay(int ms)
{
	ulong start;

M bitsy/dat.h => bitsy/dat.h +13 -6
@@ 236,6 236,7 @@ struct PCMslot
	/* status */
	uchar	occupied;	/* card in the slot */
	uchar	configed;	/* card configured */
	uchar	inserted;	/* card just inserted */

	Dev		*dev;	/* set in ctlwrite `configure' */



@@ 256,13 257,19 @@ struct PCMslot
/*
 *  hardware info about a device
 */
typedef struct {
	ulong	port;	
	int	size;
} port_t;

struct DevConf
{
	RWlock;			/* write: configure/unconfigure/suspend; read: normal access */
	ulong	mem;	/* mapped memory address */
	ulong	port;		/* mapped i/o regs */
	int		size;		/* access size */
	int		itype;	/* type of interrupt */
	ulong	irq;		/* interrupt number */
	char		*type;	/* card type, mallocated */
	ulong	mem;		/* mapped memory address */
	port_t	*ports;		/* ports[0]: mapped i/o regs, access size */
	int	nports;		/* always 1 for the bitsy */
	int	itype;		/* type of interrupt */
	ulong	intnum;		/* interrupt number */
	char	*type;		/* card type, mallocated */
};


M bitsy/devether.c => bitsy/devether.c +5 -5
@@ 82,7 82,7 @@ etherconfig(int on, char *spec, DevConf *cf)
	ether = malloc(sizeof(Ether));
	if(ether == nil)
		panic("etherconfig");
	*(DevConf*)ether = *cf;
	ether->DevConf = *cf;

	for(n = 0; cards[n].type; n++){
		if(strcmp(cards[n].type, ether->type) != 0)


@@ 110,16 110,16 @@ etherconfig(int on, char *spec, DevConf *cf)
		ether->maxmtu = ETHERMAXTU;

		if(ether->interrupt != nil)
			intrenable(cf->itype, cf->irq, ether->interrupt, ether, name);
			intrenable(cf->itype, cf->intnum, ether->interrupt, ether, name);

		p = buf;
		e = buf+sizeof(buf);
		p = seprint(p, e, "#l%d: %s: %dMbps port 0x%luX",
			ctlrno, ether->type, ether->mbps, ether->port);
			ctlrno, ether->type, ether->mbps, ether->ports[0].port);
		if(ether->mem)
			p = seprint(p, e, " addr 0x%luX", PADDR(ether->mem));
		if(ether->size)
			p = seprint(p, e, " size 0x%X", ether->size);
		if(ether->ports[0].size)
			p = seprint(p, e, " size 0x%X", ether->ports[0].size);
		p = seprint(p, e, ": %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX",
			ether->ea[0], ether->ea[1], ether->ea[2],
			ether->ea[3], ether->ea[4], ether->ea[5]);

M bitsy/devpcmcia.c => bitsy/devpcmcia.c +90 -9
@@ 6,11 6,20 @@
#include "../port/error.h"
#include "io.h"

/*
 * BUG: insertion events are detected by polling.
 *      Should look into the compaq docs to see if
 *      there's an interrupt for card insertion
 *      there's probably one.
 */

static PCMslot	slot[2];
int nslot = 2;

struct {
	Ref;
	Rendez	event;		// where to wait for card events
	int	evreader;	// there's a reader for events
} pcmcia;

enum


@@ 19,6 28,7 @@ enum
	Qmem,
	Qattr,
	Qctl,
	Qevs,

	Nents = 3,
};


@@ 66,8 76,14 @@ pcmgen(Chan *c, char *, Dirtab * , int, int i, Dir *dp)
		return 1;
	}

	if(i >= Nents*nslot)
	if(i >= Nents*nslot + 1)
		return -1;
	if(i == Nents*nslot){
		len = 0;
		qid.path = PATH(0, Qevs);
		snprint(up->genbuf, sizeof up->genbuf, "pcmevs");
		goto found;
	}

	slotno = i/Nents;
	sp = slot + slotno;


@@ 88,6 104,7 @@ pcmgen(Chan *c, char *, Dirtab * , int, int i, Dir *dp)
		snprint(up->genbuf, sizeof up->genbuf, "pcm%dctl", slotno);
		break;
	}
found:
	qid.vers = 0;
	qid.type = QTFILE;
	devdir(c, qid, up->genbuf, len, eve, 0660, dp);


@@ 242,6 259,48 @@ pcmctlread(void *a, long n, ulong off, PCMslot *sp)
	return n;
}

static int
inserted(void *)
{
	if (slot[0].inserted)
		return 1;
	if (slot[1].inserted)
		return 2;
	return 0;
}

static long
pcmevsread(void *a, long n, ulong off)
{
	int i;
	char *buf = nil;
	char *e;

	if (pcmcia.evreader)
		error("At most one reader");
	off = 0;
	pcmcia.evreader++;
	if (waserror()){
		free(buf);
		pcmcia.evreader--;
		nexterror();
	}
	while((i = inserted(nil)) == 0){
		slotinfo(nil, nil);
		tsleep(&pcmcia.event, inserted, nil, 500);
	}
	pcmcia.evreader--;
	slot[i-1].inserted = 0;
	buf = malloc(READSTR);
	e = buf + READSTR;
	buf[0] = 0;
	seprint(buf, e, "#y/pcm%dctl\n", i-1);
	n = readstr(off, a, n, buf);
	free(buf);
	poperror();
	return n;
}

static long
pcmciaread(Chan *c, void *a, long n, vlong off)
{


@@ 261,6 320,8 @@ pcmciaread(Chan *c, void *a, long n, vlong off)
		if(!sp->occupied)
			error(Eio);
		return pcmread(a, n, offset, sp, sp->attr, OneMeg);
	case Qevs:
		return pcmevsread(a, n, offset);
	case Qctl:
		return pcmctlread(a, n, offset, sp);
	}


@@ 294,6 355,7 @@ pcmctlwrite(char *p, long n, ulong, PCMslot *sp)
	int index, i, dtx;
	Rune r;
	DevConf cf;
	port_t port;

	cmd = parsecmd(p, n);
	if(strcmp(cmd->f[0], "configure") == 0){


@@ 336,9 398,12 @@ pcmctlwrite(char *p, long n, ulong, PCMslot *sp)
		memset(&cf, 0, sizeof cf);
		kstrdup(&cf.type, cmd->f[2]);
		cf.mem = (ulong)sp->mem;
		cf.port = (ulong)sp->regs;
		cf.ports = &port;
		cf.ports[0].port = (ulong)sp->regs;
		cf.ports[0].size = 0;
		cf.nports = 1;
		cf.itype = GPIOfalling;
		cf.irq = bitno(sp == slot ? GPIO_CARD_IRQ0_i : GPIO_CARD_IRQ1_i);
		cf.intnum = bitno(sp == slot ? GPIO_CARD_IRQ0_i : GPIO_CARD_IRQ1_i);
		if(devtab[dtx]->config(1, p, &cf) < 0)
			error("couldn't configure device");
		sp->dev = devtab[dtx];


@@ 388,6 453,8 @@ pcmciawrite(Chan *c, void *a, long n, vlong off)
		if(!sp->occupied)
			error(Eio);
		return pcmwrite(a, n, offset, sp, sp->attr, OneMeg);
	case Qevs:
		break;
	case Qctl:
		if(!sp->occupied)
			error(Eio);


@@ 477,25 544,31 @@ slotinfo(Ureg*, void*)

	if(x & GPIO_OPT_IND_i){
		/* no expansion pack */
		slot[0].occupied = 0;
		slot[1].occupied = 0;
		slot[0].occupied = slot[0].inserted = 0;
		slot[1].occupied = slot[1].inserted = 0;
	} else {
		if(x & GPIO_CARD_IND0_i){
			slot[0].occupied = 0;
			slot[0].occupied = slot[0].inserted = 0;
			slot[0].cisread = 0;
		} else {
			if(slot[0].occupied == 0)
			if(slot[0].occupied == 0){
				slot[0].inserted = 1;
				slot[0].cisread = 0;
			}
			slot[0].occupied = 1;
		}
		if(x & GPIO_CARD_IND1_i){
			slot[1].occupied = 0;
			slot[1].occupied = slot[1].inserted = 0;
			slot[1].cisread = 0;
		} else {
			if(slot[1].occupied == 0)
			if(slot[1].occupied == 0){
				slot[1].inserted = 1;
				slot[1].cisread = 0;
			}
			slot[1].occupied = 1;
		}
		if (inserted(nil))
			wakeup(&pcmcia.event);
	}
}



@@ 629,3 702,11 @@ slottiming(int slotno, int tio, int tattr, int tmem, int fast)
	}
	memconfregs->mecr = x;
}

/* For compat with ../pc devices. Don't use it for the bitsy 
 */
int
pcmspecial(char*, ISAConf*)
{
	return -1;
}

M bitsy/devpenmouse.c => bitsy/devpenmouse.c +1 -0
@@ 474,6 474,7 @@ penmousetrack(int b, int x, int y)
	}
	wakeup(&mouse.r);
	drawactive(1);
	resetsuspendtimer();
}

int

M bitsy/devuda1341.c => bitsy/devuda1341.c +2 -2
@@ 482,10 482,10 @@ static void
audioinit(void)
{
	/* Turn MCP operations off */
	mcpregs = mapspecial(MCPREGS, 0x34);
	mcpregs = mapspecial(MCPREGS, sizeof(MCPregs));
	mcpregs->status &= ~(1<<16);

	sspregs = mapspecial(SSPREGS, 32);
	sspregs = mapspecial(SSPREGS, sizeof(SSPregs));

}


M bitsy/devµc.c => bitsy/devµc.c +20 -6
@@ 51,12 51,12 @@ enum {

Dirtab µcdir[]={
	".",			{ Qdir, 0, QTDIR },	0,	DMDIR|0755,
	"backlight",	{ Qbacklight, 0 },	0,	0664,
	"backlight",		{ Qbacklight, 0 },	0,	0664,
	"battery",		{ Qbattery, 0 },		0,	0664,
	"buttons",		{ Qbuttons, 0 },		0,	0664,
	"cruft",		{ Qcruft, 0 },		0,	0664,
	"kbdin",		{ Qkbdin, 0 },		0,	0664,
	"led",		{ Qled, 0 },			0,	0664,
	"led",			{ Qled, 0 },			0,	0664,
	"version",		{ Qversion, 0 },		0,	0664,
	"power",		{ Qpower, 0 },		0,	0600,
};


@@ 345,6 345,8 @@ static long

#define PUTBCD(n,o) bcdclock[o] = (n % 10) | (((n / 10) % 10)<<4)

static uchar lightdata[16];

static long	 
µcwrite(Chan* c, void* a, long n, vlong)
{


@@ 352,6 354,7 @@ static long
	uchar data[16];
	char str[64];
	int i, j;
	ulong l;
	Rune r;
	extern ulong resumeaddr[];
	extern void power_resume(void);


@@ 374,7 377,14 @@ static long
			*resumeaddr = (ulong)power_resume;
		else if(strncmp(a, "halt", 4) == 0)
			*resumeaddr = 0;
		else
		else if(strncmp(a, "wakeup", 6) == 0){
			cmd = parsecmd(a, n);
			if (cmd->nf != 2)
				error(Ebadarg);
			l = strtoul(cmd->f[1], 0, 0);
			rtcalarm(l);
			return n;
		} else
			error(Ebadarg);
		deepsleep();
		return n;


@@ 391,6 401,7 @@ static long
		sendmsgwithack(BLled, data, cmd->nf);
		break;
	case Qbacklight:
		memmove(lightdata, data, 16);
		sendmsgwithack(BLbacklight, data, cmd->nf);
		break;
	case Qcruft:


@@ 409,9 420,12 @@ void
	if (on == 0)
		return;
	/* maybe dangerous, not holding the lock */
	data[0]= 2;
	data[1]= 1;
	data[2]= 0x80;
	if (lightdata[0] == 0){
		data[0]= 2;
		data[1]= 1;
		data[2]= 0;
	} else
		memmove(data, lightdata, 16);
	_sendmsg(0xd, data, 3);
	wakeup(&ctlr.r);
}

M bitsy/etherwavelan.c => bitsy/etherwavelan.c +10 -4
@@ 9,7 9,7 @@
#include "../port/error.h"
#include "../port/netif.h"
#include "etherif.h"
#include "wavelan.h"
#include "../pc/wavelan.h"

static int
wavelanpcmciareset(Ether *ether)


@@ 22,9 22,15 @@ wavelanpcmciareset(Ether *ether)
	ilock(ctlr);
	ctlr->ctlrno = ether->ctlrno;

	if (ether->port==0)
		ether->port=WDfltIOB;
	ctlr->iob = ether->port;
	if (ether->ports == nil){
		ether->ports = malloc(sizeof(port_t));
		ether->ports[0].port = 0;
		ether->ports[0].size = 0;
		ether->nports= 1;
	}
	if (ether->ports[0].port==0)
		ether->ports[0].port=WDfltIOB;
	ctlr->iob = ether->ports[0].port;

	if(wavelanreset(ether, ctlr) < 0){
		iunlock(ctlr);

M bitsy/fns.h => bitsy/fns.h +3 -2
@@ 1,6 1,5 @@
#include "../port/portfns.h"

void	addclock0link(void (*)(void));
void	audiopower(int);
void	audioamppower(int);
void	audioicpower(int);


@@ 14,7 13,7 @@ void	dcacheinvalidate(void);
int	cistrcmp(char*, char*);
int	cistrncmp(char*, char*, int);
void	clockinit(void);
void	clockpower(int);
ulong	clockpower(int);
#define	coherence()
#define	dcflush(a, b)
void	delay(int);


@@ 93,7 92,9 @@ void	putdac(ulong);
void	putttb(ulong);
void	putpid(ulong);
void	qpanic(char *, ...);
void	resetsuspendtimer(void);
void	rs232power(int);
void	rtcalarm(ulong);
void	uartspecial(Uart*, int, Queue**, Queue**, int (*)(Queue*, int));
void	sa1110_uartsetup(int);
void	screeninit(void);

M bitsy/io.h => bitsy/io.h +11 -0
@@ 323,3 323,14 @@ struct Gpclkregs
	ulong	r3;
};
extern Gpclkregs *gpclkregs;

/*
 * Dont use this on bitsy drivers.
 * This is for compat with code compiled
 * from the ../pc directory
 */

typedef struct Pcidev	Pcidev;
typedef struct ISAConf	ISAConf;
struct Pcidev { int dummy; };
struct ISAConf{ int port; int irq; };

M bitsy/main.c => bitsy/main.c +12 -6
@@ 381,7 381,7 @@ void
machinit(void)
{
	/* set direction of SA1110 io pins and select alternate functions for some */
	gpioregs = mapspecial(GPIOREGS, 32);
	gpioregs = mapspecial(GPIOREGS, sizeof(GPIOregs));
	gpioregs->direction = 
		GPIO_LDD8_o|GPIO_LDD9_o|GPIO_LDD10_o|GPIO_LDD11_o
		|GPIO_LDD12_o|GPIO_LDD13_o|GPIO_LDD14_o|GPIO_LDD15_o


@@ 396,19 396,19 @@ machinit(void)
		|GPIO_SSP_CLK_i;

	/* map in special H3650 io pins */
	egpioreg = mapspecial(EGPIOREGS, 4);
	egpioreg = mapspecial(EGPIOREGS, sizeof(ulong));

	/* map in peripheral pin controller (ssp will need it) */
	ppcregs = mapspecial(PPCREGS, 32);
	ppcregs = mapspecial(PPCREGS, sizeof(PPCregs));

	/* SA1110 power management */
	powerregs = mapspecial(POWERREGS, 32);
	powerregs = mapspecial(POWERREGS, sizeof(PowerRegs));

	/* memory configuraton */
	memconfregs = mapspecial(MEMCONFREGS, 32);
	memconfregs = mapspecial(MEMCONFREGS, sizeof(MemConfRegs));

	/* reset controller */
	resetregs = mapspecial(RESETREGS, 32);
	resetregs = mapspecial(RESETREGS, sizeof(ResetRegs));
}




@@ 523,3 523,9 @@ inl(ulong a)
{
	return *(ulong*)a;
}

char*
getconf(char*)
{
	return nil;
}

M bitsy/power.c => bitsy/power.c +71 -1
@@ 9,6 9,26 @@

/* Power management for the bitsy */


/*
 * Very ugly. This is just to get/set the offset field without
 * touching port/tod.c.
 */
#define TODFREQ	1000000000LL
extern struct {
	ulong	cnt;
	Lock;
	vlong	multiplier;	// t = off + (multiplier*ticks)>>31
	vlong	hz;		// frequency of fast clock
	vlong	last;		// last reading of fast clock
	vlong	off;		// offset from epoch to last
	vlong	lasttime;	// last return value from todget
	vlong	delta;	// add 'delta' each slow clock tick from sstart to send
	ulong	sstart;	// ...
	ulong	send;	// ...
} tod;


/* saved state during power down. 
 * it's only used up to 164/4.
 * it's only used by routines in l.s


@@ 30,6 50,18 @@ Intrregs savedintrregs;

uchar *savedtext;

static vlong
todgetoff(void)
{
	return tod.off;
}

static void
todsetoff(vlong off)
{
	tod.off = off;
}

static void
checkflash(void)
{


@@ 166,6 198,8 @@ deepsleep(void) {
	static int power_pl;
	ulong xsp, xlink;
//	ulong mecr;
	ulong clkd;
	vlong savedtod;
	extern void power_resume(void);

	power_pl = splhi();


@@ 199,8 233,10 @@ deepsleep(void) {
			gpioregs->edgestatus = (1<<IRQgpio0);
			intrregs->icip = (1<<IRQgpio0);
		}
		clockpower(1);
		clkd = clockpower(1);
		gpclkregs->r0 = 1<<0;
		todsetoff( savedtod + clkd * TODFREQ);
		resetsuspendtimer();
		rs232power(1);
		uartpower(1);
		delay(100);


@@ 218,6 254,7 @@ deepsleep(void) {
	}
	cacheflush();
	delay(100);
	savedtod = todgetoff();
	power_down();
	/* no return */
}


@@ 273,6 310,37 @@ blanktimer(void)
	drawactive(0);
}

static ulong suspendtime = 120 * HZ;
static int lastsuspend;

void
resetsuspendtimer(void)
{
	suspendtime = 60 * HZ;
}

static void
suspendtimer(void)
{
	uvlong	now;

	return;	// does not work well.

	if (suspendtime > 0)
		suspendtime--;
	if (suspendtime == 0){
		now = seconds();
		if (now < lastsuspend + 10){
			resetsuspendtimer();
			return;
		}
		lastsuspend = seconds();
		deepsleep();
		lastsuspend = seconds();
		return;
	}
}

void
powerinit(void)
{


@@ 292,6 360,7 @@ powerinit(void)

	*resumeaddr = (ulong) power_resume;
	addclock0link(blanktimer, 1000/HZ);
	addclock0link(suspendtimer, 1000/HZ);
	intrenable(GPIOrising, bitno(GPIO_PWR_ON_i), onoffintr, nil, "on/off");
}



@@ 311,3 380,4 @@ idlehands(void)
		spllo();
	}
}


D bitsy/sd.h => bitsy/sd.h +0 -125
@@ 1,125 0,0 @@
/*
 * Storage Device.
 */
typedef struct SDev SDev;
typedef struct SDifc SDifc;
typedef struct SDpart SDpart;
typedef struct SDperm SDperm;
typedef struct SDreq SDreq;
typedef struct SDunit SDunit;

typedef struct SDperm {
	char	name[KNAMELEN];
	char	user[KNAMELEN];
	ulong	perm;
} SDperm;

typedef struct SDpart {
	ulong	start;
	ulong	end;
	SDperm;
	int	valid;
	ulong	vers;
} SDpart;

typedef struct SDunit {
	SDev*	dev;
	int	subno;
	uchar	inquiry[256];		/* format follows SCSI spec */
	SDperm;
	Rendez	rendez;

	QLock	ctl;
	ulong	sectors;
	ulong	secsize;
	SDpart*	part;			/* nil or array of size npart */
	int	npart;
	ulong	vers;
	SDperm	ctlperm;

	QLock	raw;		/* raw read or write in progress */
	Lock	rawinuse;		/* really just a test-and-set */
	int	state;
	SDreq*	req;
	SDperm	rawperm;

	Log log;
} SDunit;

typedef struct SDev {
	SDifc*	ifc;			/* pnp/legacy */
	void	*ctlr;
	int	idno;
	char	name[KNAMELEN];
	int	index;			/* into unit space */
	int	nunit;
	SDev*	next;

	QLock;				/* enable/disable */
	int	enabled;
} SDev;

struct SDifc {
	char*	name;

	SDev*	(*pnp)(void);
	SDev*	(*legacy)(int, int);
	SDev*	(*id)(SDev*);
	int	(*enable)(SDev*);
	int	(*disable)(SDev*);

	int	(*verify)(SDunit*);
	int	(*online)(SDunit*);
	int	(*rio)(SDreq*);
	int	(*rctl)(SDunit*, char*, int);
	int	(*wctl)(SDunit*, Cmdbuf*);

	long	(*bio)(SDunit*, int, int, void*, long, long);
	SDev*	(*config)(int on, char *spec, DevConf *cf);
};

typedef struct SDreq {
	SDunit*	unit;
	int	lun;
	int	write;
	uchar	cmd[16];
	int	clen;
	void*	data;
	int	dlen;

	int	flags;

	int	status;
	long	rlen;
	uchar	sense[256];
} SDreq;

enum {
	SDnosense	= 0x00000001,
	SDvalidsense	= 0x00010000,
};

enum {
	SDretry		= -5,		/* internal to controllers */
	SDmalloc	= -4,
	SDeio		= -3,
	SDtimeout	= -2,
	SDnostatus	= -1,

	SDok		= 0,

	SDcheck		= 0x02,		/* check condition */
	SDbusy		= 0x08,		/* busy */

	SDmaxio		= 2048*1024,
	SDnpart		= 16,
};

#define sdmalloc(n)	malloc(n)
#define sdfree(p)	free(p)

/* sdscsi.c */
extern int scsiverify(SDunit*);
extern int scsionline(SDunit*);
extern long scsibio(SDunit*, int, int, void*, long, long);
extern SDev* scsiid(SDev*, SDifc*);

M bitsy/sdata.c => bitsy/sdata.c +40 -14
@@ 7,7 7,7 @@
#include "ureg.h"
#include "../port/error.h"

#include "sd.h"
#include "../port/sd.h"


extern SDifc sdataifc;


@@ 253,11 253,6 @@ typedef struct Drive {
} Drive;


char*
getconf(char*)
{
	return nil;
}

static void
atadumpstate(Drive* drive, uchar* cmd, int lba, int count)


@@ 2036,7 2031,7 @@ struct Try {
};

static SDev*
ataconfig(int on, char *, DevConf *cf)
ataprobew(DevConf *cf)
{
	int	cmdport;
	int	ctlport;


@@ 2044,15 2039,13 @@ ataconfig(int on, char *, DevConf *cf)
	SDev*	rc;
	struct Try *try;

	if(on == 0)
		return nil;
	rc = nil;
	for (try = &tries[0]; try->p != 0 || try->c != 0; try++){
		ataitype = cf->itype;
		atairq  = cf->irq;
		cmdport = cf->port + try->p;
		atairq  = cf->intnum;
		cmdport = cf->ports[0].port + try->p;
		ctlport = cmdport + try->c;
		irq = cf->irq;
		irq = cf->intnum;
		rc = ataprobe(cmdport, ctlport, irq);
		if (rc)
			break;


@@ 2060,6 2053,37 @@ ataconfig(int on, char *, DevConf *cf)
	return rc;
}

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

	ctlr = sdev->ctlr;

	if (ctlr->drive[0])
		free(ctlr->drive[0]);
	if (ctlr->drive[1])
		free(ctlr->drive[1]);
	if (sdev->name)
		free(sdev->name);
	if (sdev->unitflg)
		free(sdev->unitflg);
	if (sdev->unit)
		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);
}


SDifc sdataifc = {
	"ata",				/* name */



@@ 2075,7 2099,9 @@ SDifc sdataifc = {
	atarctl,			/* rctl */
	atawctl,			/* wctl */

	atabio,			/* bio */
	ataconfig,		/* config */
	atabio,				/* bio */
	ataprobew,			/* probew */
	ataclear,			/* clear */
	atastat,			/* stat */
};


M bitsy/uartsa1110.c => bitsy/uartsa1110.c +3 -3
@@ 465,7 465,7 @@ sa1110_uartsetup(int console)

	/* external serial port (eia0) */
	p = &sa1110uart[0];
	p->regs = mapspecial(UART3REGS, 64);
	p->regs = mapspecial(UART3REGS, sizeof(Uartregs));
	p->saveregs = xalloc(sizeof(Uartregs));
	/* set eia0 up as a console */
	if(console){


@@ 477,11 477,11 @@ sa1110_uartsetup(int console)
	intrenable(IRQ, IRQuart3, sa1110_uartintr, p, p->name);

	/* port for talking to microcontroller (eia1) */
	gpclkregs = mapspecial(GPCLKREGS, 64);
	gpclkregs = mapspecial(GPCLKREGS, sizeof(Gpclkregs));
	gpclkregs->r0 = Gpclk_sus;	/* set uart mode */

	p = &sa1110uart[1];
	p->regs = mapspecial(UART1REGS, 64);
	p->regs = mapspecial(UART1REGS, sizeof(Uartregs));
	p->saveregs = xalloc(sizeof(Uartregs));
	uartctl(p, "b115200 l8 pn s1");
	µcuart = p;

M bitsy/wavelan.c => bitsy/wavelan.c +1150 -1
@@ 1,1 1,1150 @@
#include "../pc/wavelan.c"
/*
	Lucent Wavelan IEEE 802.11 pcmcia. 
	There is almost no documentation for the card.
	the driver is done using both the FreeBSD, Linux and
	original Plan 9 drivers as `documentation'.

	Has been used with the card plugged in during all up time.
	no cards removals/insertions yet.

	For known BUGS see the comments below. Besides,
	the driver keeps interrupts disabled for just too
	long. When it gets robust, locks should be revisited.

	BUGS: check endian, alignment and mem/io issues;
	      multicast;
	      receive watchdog interrupts.
	TODO: automatic power management;
	      improve locking.
 */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"
#include "../port/netif.h"
#include "etherif.h"
#include "wavelan.h"

/*
 * When we're using a PCI device and memory-mapped I/O, 
 * the registers are spaced out as though each takes 32 bits,
 * even though they are only 16-bit registers.  Thus, 
 * ctlr->mmb[reg] is the right way to access register reg,
 * even though a priori you'd expect to use ctlr->mmb[reg/2].
 */
void
csr_outs(Ctlr *ctlr, int reg, ushort arg)
{
	if(ctlr->mmb)
		ctlr->mmb[reg] = arg;
	else
		outs(ctlr->iob+reg, arg);
}

ushort
csr_ins(Ctlr *ctlr, int reg)
{
	if(ctlr->mmb)
		return ctlr->mmb[reg];
	else
		return ins(ctlr->iob+reg);
}

static void
csr_ack(Ctlr *ctlr, int ev)
{
	csr_outs(ctlr, WR_EvAck, ev);
}

static void
csr_inss(Ctlr *ctlr, int reg, void *dat, int ndat)
{
	ushort *rp, *wp;

	if(ctlr->mmb){
		rp = &ctlr->mmb[reg];
		wp = dat;
		while(ndat-- > 0)
			*wp++ = *rp;
	}else
		inss(ctlr->iob+reg, dat, ndat);
}

static void
csr_outss(Ctlr *ctlr, int reg, void *dat, int ndat)
{
	ushort *rp, *wp;

	if(ctlr->mmb){
		rp = dat;
		wp = &ctlr->mmb[reg];
		while(ndat-- > 0)
			*wp = *rp++;
	}else
		outss(ctlr->iob+reg, dat, ndat);
}

// w_... routines do not ilock the Ctlr and should
// be called locked.

void
w_intdis(Ctlr* ctlr)
{
	csr_outs(ctlr, WR_IntEna, 0);
	csr_ack(ctlr, 0xffff);
}

static void
w_intena(Ctlr* ctlr)
{
	csr_outs(ctlr, WR_IntEna, WEvs);
}

int
w_cmd(Ctlr *ctlr, ushort cmd, ushort arg)
{
	int i, rc;

	for(i=0; i<WTmOut; i++)
		if((csr_ins(ctlr, WR_Cmd)&WCmdBusy) == 0)
			break;
	if(i==WTmOut){
		print("#l%d: issuing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_Cmd));
		return -1;
	}

	csr_outs(ctlr, WR_Parm0, arg);
	csr_outs(ctlr, WR_Cmd, cmd);

	for(i=0; i<WTmOut; i++)
		if(csr_ins(ctlr, WR_EvSts)&WCmdEv)
			break;
	if(i==WTmOut){
		/*
		 * WCmdIni can take a really long time.
		 */
		enum { IniTmOut = 2000 };
		for(i=0; i<IniTmOut; i++){
			if(csr_ins(ctlr, WR_EvSts)&WCmdEv)
				break;
			microdelay(100);
		}
		if(i < IniTmOut)
			if(0) print("#l%d: long cmd %.4ux %d\n", ctlr->ctlrno, cmd, i);
		if(i == IniTmOut){
			print("#l%d: execing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_EvSts));
			return -1;
		}
	}
	rc = csr_ins(ctlr, WR_Sts);
	csr_ack(ctlr, WCmdEv);

	if((rc&WCmdMsk) != (cmd&WCmdMsk)){
		print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc);
		return -1;
	}
	if(rc&WResSts){
		/*
		 * Don't print; this happens on every WCmdAccWr for some reason.
		 */
		if(0) print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc);
		return -1;
	}
	return 0;
}

static int
w_seek(Ctlr* ctlr, ushort id, ushort offset, int chan)
{
	int i, rc;
	static ushort sel[] = { WR_Sel0, WR_Sel1 };
	static ushort off[] = { WR_Off0, WR_Off1 };

	if(chan != 0 && chan != 1)
		panic("wavelan: bad chan\n");
	csr_outs(ctlr, sel[chan], id);
	csr_outs(ctlr, off[chan], offset);
	for (i=0; i<WTmOut; i++){
		rc = csr_ins(ctlr, off[chan]);
		if((rc & (WBusyOff|WErrOff)) == 0)
			return 0;
	}
	return -1;
}

int
w_inltv(Ctlr* ctlr, Wltv* ltv)
{
	int len;
	ushort code;

	if(w_cmd(ctlr, WCmdAccRd, ltv->type)){
		DEBUG("wavelan: access read failed\n");
		return -1;
	}
	if(w_seek(ctlr,ltv->type,0,1)){
		DEBUG("wavelan: seek failed\n");
		return -1;
	}
	len = csr_ins(ctlr, WR_Data1);
	if(len > ltv->len)
		return -1;
	ltv->len = len;
	if((code=csr_ins(ctlr, WR_Data1)) != ltv->type){
		USED(code);
		DEBUG("wavelan: type %x != code %x\n",ltv->type,code);
		return -1;
	}
	if(ltv->len > 0)
		csr_inss(ctlr, WR_Data1, &ltv->val, ltv->len-1);

	return 0;
}

static void
w_outltv(Ctlr* ctlr, Wltv* ltv)
{
	if(w_seek(ctlr,ltv->type, 0, 1))
		return;
	csr_outss(ctlr, WR_Data1, ltv, ltv->len+1);
	w_cmd(ctlr, WCmdAccWr, ltv->type);
}

void
ltv_outs(Ctlr* ctlr, int type, ushort val)
{
	Wltv ltv;

	ltv.len = 2;
	ltv.type = type;
	ltv.val = val;
	w_outltv(ctlr, &ltv);
}

int
ltv_ins(Ctlr* ctlr, int type)
{
	Wltv ltv;

	ltv.len = 2;
	ltv.type = type;
	ltv.val = 0;
	if(w_inltv(ctlr, &ltv))
		return -1;
	return ltv.val;
}

static void
ltv_outstr(Ctlr* ctlr, int type, char* val)
{
	Wltv ltv;
	int len;

	len = strlen(val);
	if(len > sizeof(ltv.s))
		len = sizeof(ltv.s);
	memset(&ltv, 0, sizeof(ltv));
	ltv.len = (sizeof(ltv.type)+sizeof(ltv.slen)+sizeof(ltv.s))/2;
	ltv.type = type;

//	This should be ltv.slen = len; according to Axel Belinfante
	ltv.slen = len;	

	strncpy(ltv.s, val, len);
	w_outltv(ctlr, &ltv);
}

static char Unkname[] = "who knows";
static char Nilname[] = "card does not tell";

static char*
ltv_inname(Ctlr* ctlr, int type)
{
	static Wltv ltv;
	int len;

	memset(&ltv,0,sizeof(ltv));
	ltv.len = WNameLen/2+2;
	ltv.type = type;
	if(w_inltv(ctlr, &ltv))
		return Unkname;
	len = ltv.slen;
	if(len == 0 || ltv.s[0] == 0)
		return Nilname;
	if(len >= sizeof ltv.s)
		len = sizeof ltv.s - 1;
	ltv.s[len] = '\0';
	return ltv.s;
}

static int
w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len)
{
	if(w_seek(ctlr, type, off, 1)){
		DEBUG("wavelan: w_read: seek failed");
		return 0;
	}
	csr_inss(ctlr, WR_Data1, buf, len/2);

	return len;
}

static int
w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len)
{
	int tries;

	for (tries=0; tries < WTmOut; tries++){
		if(w_seek(ctlr, type, off, 0)){
			DEBUG("wavelan: w_write: seek failed\n");
			return 0;
		}

		csr_outss(ctlr, WR_Data0, buf, len/2);

		csr_outs(ctlr, WR_Data0, 0xdead);
		csr_outs(ctlr, WR_Data0, 0xbeef);
		if(w_seek(ctlr, type, off + len, 0)){
			DEBUG("wavelan: write seek failed\n");
			return 0;
		}
		if(csr_ins(ctlr, WR_Data0) == 0xdead)
		if(csr_ins(ctlr, WR_Data0) == 0xbeef)
			return len;
		DEBUG("wavelan: Hermes bug byte.\n");
		return 0;
	}
	DEBUG("wavelan: tx timeout\n");
	return 0;
}

static int
w_alloc(Ctlr* ctlr, int len)
{
	int rc;
	int i,j;

	if(w_cmd(ctlr, WCmdMalloc, len)==0)
		for (i = 0; i<WTmOut; i++)
			if(csr_ins(ctlr, WR_EvSts) & WAllocEv){
				csr_ack(ctlr, WAllocEv);
				rc=csr_ins(ctlr, WR_Alloc);
				if(w_seek(ctlr, rc, 0, 0))
					return -1;
				len = len/2;
				for (j=0; j<len; j++)
					csr_outs(ctlr, WR_Data0, 0);
				return rc;
			}
	return -1;
}

static int
w_enable(Ether* ether)
{
	Wltv ltv;
	Ctlr* ctlr = (Ctlr*) ether->ctlr;

	if(!ctlr)
		return -1;

	w_intdis(ctlr);
	w_cmd(ctlr, WCmdDis, 0);
	w_intdis(ctlr);
	if(w_cmd(ctlr, WCmdIni, 0))
		return -1;
	w_intdis(ctlr);

	ltv_outs(ctlr, WTyp_Tick, 8);
	ltv_outs(ctlr, WTyp_MaxLen, ctlr->maxlen);
	ltv_outs(ctlr, WTyp_Ptype, ctlr->ptype);
 	ltv_outs(ctlr, WTyp_CreateIBSS, ctlr->createibss);
	ltv_outs(ctlr, WTyp_RtsThres, ctlr->rtsthres);
	ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate);
	ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity);
	ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait);
	ltv_outs(ctlr, WTyp_PM, ctlr->pmena);
	if(*ctlr->netname)
		ltv_outstr(ctlr, WTyp_NetName, ctlr->netname);
	if(*ctlr->wantname)
		ltv_outstr(ctlr, WTyp_WantName, ctlr->wantname);
	ltv_outs(ctlr, WTyp_Chan, ctlr->chan);
	if(*ctlr->nodename)
		ltv_outstr(ctlr, WTyp_NodeName, ctlr->nodename);
	ltv.len = 4;
	ltv.type = WTyp_Mac;
	memmove(ltv.addr, ether->ea, Eaddrlen);
	w_outltv(ctlr, &ltv);

	ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0));

	if(ctlr->hascrypt){
		ltv_outs(ctlr, WTyp_Crypt, ctlr->crypt);
		ltv_outs(ctlr, WTyp_TxKey, ctlr->txkey);
		w_outltv(ctlr, &ctlr->keys);
		ltv_outs(ctlr, WTyp_XClear, ctlr->xclear);
	}

	// BUG: set multicast addresses

	if(w_cmd(ctlr, WCmdEna, 0)){
		DEBUG("wavelan: Enable failed");
		return -1;
	}
	ctlr->txdid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8);
	ctlr->txmid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8);
	if(ctlr->txdid == -1 || ctlr->txmid == -1)
		DEBUG("wavelan: alloc failed");
	ctlr->txbusy = 0;
	w_intena(ctlr);
	return 0;
}

static void
w_rxdone(Ether* ether)
{
	Ctlr* ctlr = (Ctlr*) ether->ctlr;
	int len, sp;
	WFrame f;
	Block* bp=0;
	Etherpkt* ep;

	sp = csr_ins(ctlr, WR_RXId);
	len = w_read(ctlr, sp, 0, &f, sizeof(f));
	if(len == 0){
		DEBUG("wavelan: read frame error\n");
		goto rxerror;
	}
	if(f.sts&WF_Err){
		goto rxerror;
	}
	switch(f.sts){
	case WF_1042:
	case WF_Tunnel:
	case WF_WMP:
		len = f.dlen + WSnapHdrLen;
		bp = iallocb(ETHERHDRSIZE + len + 2);
		if(!bp)
			goto rxerror;
		ep = (Etherpkt*) bp->wp;
		memmove(ep->d, f.addr1, Eaddrlen);
		memmove(ep->s, f.addr2, Eaddrlen);
		memmove(ep->type,&f.type,2);
		bp->wp += ETHERHDRSIZE;
		if(w_read(ctlr, sp, WF_802_11_Off, bp->wp, len+2) == 0){
			DEBUG("wavelan: read 802.11 error\n");
			goto rxerror;
		}
		bp->wp = bp->rp+(ETHERHDRSIZE+f.dlen);
		break;
	default:
		len = ETHERHDRSIZE + f.dlen + 2;
		bp = iallocb(len);
		if(!bp)
			goto rxerror;
		if(w_read(ctlr, sp, WF_802_3_Off, bp->wp, len) == 0){
			DEBUG("wavelan: read 800.3 error\n");
			goto rxerror;
		}
		bp->wp += len;
	}

	ctlr->nrx++;
	etheriq(ether,bp,1);
	ctlr->signal = ((ctlr->signal*15)+((f.qinfo>>8) & 0xFF))/16;
	ctlr->noise = ((ctlr->noise*15)+(f.qinfo & 0xFF))/16;
	return;

rxerror:
	freeb(bp);
	ctlr->nrxerr++;
}

static void
w_txstart(Ether* ether)
{
	Etherpkt *pkt;
	Ctlr *ctlr;
	Block *bp;
	int len, off;

	if((ctlr = ether->ctlr) == nil || (ctlr->state & (Attached|Power)) != (Attached|Power) || ctlr->txbusy)
		return;

	if((bp = qget(ether->oq)) == nil)
		return;
	pkt = (Etherpkt*)bp->rp;

	//
	// If the packet header type field is > 1500 it is an IP or
	// ARP datagram, otherwise it is an 802.3 packet. See RFC1042.
	//
	memset(&ctlr->txf, 0, sizeof(ctlr->txf));
	if(((pkt->type[0]<<8)|pkt->type[1]) > 1500){
		ctlr->txf.framectl = WF_Data;
		memmove(ctlr->txf.addr1, pkt->d, Eaddrlen);
		memmove(ctlr->txf.addr2, pkt->s, Eaddrlen);
		memmove(ctlr->txf.dstaddr, pkt->d, Eaddrlen);
		memmove(ctlr->txf.srcaddr, pkt->s, Eaddrlen);
		memmove(&ctlr->txf.type, pkt->type, 2);
		bp->rp += ETHERHDRSIZE;
		len = BLEN(bp);
		off = WF_802_11_Off;
		ctlr->txf.dlen = len+ETHERHDRSIZE-WSnapHdrLen;
		hnputs((uchar*)&ctlr->txf.dat[0], WSnap0);
		hnputs((uchar*)&ctlr->txf.dat[1], WSnap1);
		hnputs((uchar*)&ctlr->txf.len, len+ETHERHDRSIZE-WSnapHdrLen);
	}
	else{
		len = BLEN(bp);
		off = WF_802_3_Off;
		ctlr->txf.dlen = len;
	}
	w_write(ctlr, ctlr->txdid, 0, &ctlr->txf, sizeof(ctlr->txf));
	w_write(ctlr, ctlr->txdid, off, bp->rp, len+2);

	if(w_cmd(ctlr, WCmdReclaim|WCmdTx, ctlr->txdid)){
		DEBUG("wavelan: transmit failed\n");
		ctlr->ntxerr++;
	}
	else{
		ctlr->txbusy = 1;
		ctlr->txtmout = 2;
	}
	freeb(bp);
}

static void
w_txdone(Ctlr* ctlr, int sts)
{
	ctlr->txbusy = 0;
	ctlr->txtmout = 0;
	if(sts & WTxErrEv)
		ctlr->ntxerr++;
	else
		ctlr->ntx++;
}

static int
w_stats(Ctlr* ctlr)
{
	int i, rc, sp;
	Wltv ltv;
	ulong* p = (ulong*)&ctlr->WStats;
	ulong* pend = (ulong*)&ctlr->end;

	sp = csr_ins(ctlr, WR_InfoId);
	ltv.len = ltv.type = 0;
	w_read(ctlr, sp, 0, &ltv, 4);
	if(ltv.type == WTyp_Stats){
		ltv.len--;
		for (i = 0; i < ltv.len && p < pend; i++){
			rc = csr_ins(ctlr, WR_Data1);
			if(rc > 0xf000)
				rc = ~rc & 0xffff;
			p[i] += rc;
		}
		return 0;
	}
	return -1;
}

static void
w_intr(Ether *ether)
{
	int rc, txid;
	Ctlr* ctlr = (Ctlr*) ether->ctlr;

	if((ctlr->state & Power) == 0)
		return;

	if((ctlr->state & Attached) == 0){
		csr_ack(ctlr, 0xffff);
		csr_outs(ctlr, WR_IntEna, 0);
		return;
	}

	rc = csr_ins(ctlr, WR_EvSts);
	csr_ack(ctlr, ~WEvs);	// Not interested in them
	if(rc & WRXEv){
		w_rxdone(ether);
		csr_ack(ctlr, WRXEv);
	}
	if(rc & WTXEv){
		w_txdone(ctlr, rc);
		csr_ack(ctlr, WTXEv);
	}
	if(rc & WAllocEv){
		ctlr->nalloc++;
		txid = csr_ins(ctlr, WR_Alloc);
		csr_ack(ctlr, WAllocEv);
		if(txid == ctlr->txdid){
			if((rc & WTXEv) == 0)
				w_txdone(ctlr, rc);
		}
	}
	if(rc & WInfoEv){
		ctlr->ninfo++;
		w_stats(ctlr);
		csr_ack(ctlr, WInfoEv);
	}
	if(rc & WTxErrEv){
		w_txdone(ctlr, rc);
		csr_ack(ctlr, WTxErrEv);
	}
	if(rc & WIDropEv){
		ctlr->nidrop++;
		csr_ack(ctlr, WIDropEv);
	}
	w_txstart(ether);
}

// Watcher to ensure that the card still works properly and
// to request WStats updates once a minute.
// BUG: it runs much more often, see the comment below.

static void
w_timer(void* arg)
{
	Ether* ether = (Ether*) arg;
	Ctlr* ctlr = (Ctlr*)ether->ctlr;

	ctlr->timerproc = up;
	for(;;){
		tsleep(&ctlr->timer, return0, 0, 50);
		ctlr = (Ctlr*)ether->ctlr;
		if(ctlr == 0)
			break;
		if((ctlr->state & (Attached|Power)) != (Attached|Power))
			continue;
		ctlr->ticks++;

		ilock(ctlr);

		// Seems that the card gets frames BUT does
		// not send the interrupt; this is a problem because
		// I suspect it runs out of receive buffers and
		// stops receiving until a transmit watchdog
		// reenables the card.
		// The problem is serious because it leads to
		// poor rtts.
		// This can be seen clearly by commenting out
		// the next if and doing a ping: it will stop
		// receiving (although the icmp replies are being
		// issued from the remote) after a few seconds.
		// Of course this `bug' could be because I'm reading
		// the card frames in the wrong way; due to the
		// lack of documentation I cannot know.

		if(csr_ins(ctlr, WR_EvSts)&WEvs){
			ctlr->tickintr++;
			w_intr(ether);
		}

		if((ctlr->ticks % 10) == 0) {
			if(ctlr->txtmout && --ctlr->txtmout == 0){
				ctlr->nwatchdogs++;
				w_txdone(ctlr, WTxErrEv);
				if(w_enable(ether)){
					DEBUG("wavelan: wdog enable failed\n");
				}
				w_txstart(ether);
			}
			if((ctlr->ticks % 120) == 0)
			if(ctlr->txbusy == 0)
				w_cmd(ctlr, WCmdAskStats, WTyp_Stats);
		}
		iunlock(ctlr);
	}
	pexit("terminated", 0);
}

void
w_multicast(void*, uchar*, int)
{
	// BUG: to be added.
}

void
w_attach(Ether* ether)
{
	Ctlr* ctlr;
	char name[64];
	int rc;

	if(ether->ctlr == 0)
		return;

	snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno);
	ctlr = (Ctlr*) ether->ctlr;
	if((ctlr->state & Attached) == 0){
		ilock(ctlr);
		rc = w_enable(ether);
		iunlock(ctlr);
		if(rc == 0){
			ctlr->state |= Attached;
			kproc(name, w_timer, ether);
		} else
			print("#l%d: enable failed\n",ether->ctlrno);
	}
}

void
w_detach(Ether* ether)
{
	Ctlr* ctlr;
	char name[64];

	if(ether->ctlr == nil)
		return;

	snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno);
	ctlr = (Ctlr*) ether->ctlr;
	if(ctlr->state & Attached){
		ilock(ctlr);
		w_intdis(ctlr);
		if(ctlr->timerproc){
			if(!postnote(ctlr->timerproc, 1, "kill", NExit))
				print("timerproc note not posted\n");
			print("w_detach, killing 0x%p\n", ctlr->timerproc);
		}
		ctlr->state &= ~Attached;
		iunlock(ctlr);
	}
	ether->ctlr = nil;
}

void
w_power(Ether* ether, int on)
{
	Ctlr *ctlr;

	ctlr = (Ctlr*) ether->ctlr;
	ilock(ctlr);
iprint("w_power %d\n", on);
	if(on){
		if((ctlr->state & Power) == 0){
			if (wavelanreset(ether, ctlr) < 0){
				iprint("w_power: reset failed\n");
				iunlock(ctlr);
				w_detach(ether);
				free(ctlr);
				return;
			}
			if(ctlr->state & Attached)
				w_enable(ether);
			ctlr->state |= Power;
		}
	}else{
		if(ctlr->state & Power){
			if(ctlr->state & Attached)
				w_intdis(ctlr);
			ctlr->state &= ~Power;
		}
	}
	iunlock(ctlr);
}

#define PRINTSTAT(fmt,val)	l += snprint(p+l, READSTR-l, (fmt), (val))
#define PRINTSTR(fmt)		l += snprint(p+l, READSTR-l, (fmt))

long
w_ifstat(Ether* ether, void* a, long n, ulong offset)
{
	Ctlr *ctlr = (Ctlr*) ether->ctlr;
	char *k, *p;
	int i, l, txid;

	ether->oerrs = ctlr->ntxerr;
	ether->crcs = ctlr->nrxfcserr;
	ether->frames = 0;
	ether->buffs = ctlr->nrxdropnobuf;
	ether->overflows = 0;

	//
	// Offset must be zero or there's a possibility the
	// new data won't match the previous read.
	//
	if(n == 0 || offset != 0)
		return 0;

	p = malloc(READSTR);
	l = 0;

	PRINTSTAT("Signal: %d\n", ctlr->signal-149);
	PRINTSTAT("Noise: %d\n", ctlr->noise-149);
	PRINTSTAT("SNR: %ud\n", ctlr->signal-ctlr->noise);
	PRINTSTAT("Interrupts: %lud\n", ctlr->nints);
	PRINTSTAT("Double Interrupts: %lud\n", ctlr->ndoubleint);
	PRINTSTAT("TxPackets: %lud\n", ctlr->ntx);
	PRINTSTAT("RxPackets: %lud\n", ctlr->nrx);
	PRINTSTAT("TxErrors: %lud\n", ctlr->ntxerr);
	PRINTSTAT("RxErrors: %lud\n", ctlr->nrxerr);
	PRINTSTAT("TxRequests: %lud\n", ctlr->ntxrq);
	PRINTSTAT("AllocEvs: %lud\n", ctlr->nalloc);
	PRINTSTAT("InfoEvs: %lud\n", ctlr->ninfo);
	PRINTSTAT("InfoDrop: %lud\n", ctlr->nidrop);
	PRINTSTAT("WatchDogs: %lud\n", ctlr->nwatchdogs);
	PRINTSTAT("Ticks: %ud\n", ctlr->ticks);
	PRINTSTAT("TickIntr: %ud\n", ctlr->tickintr);
	k = ((ctlr->state & Attached) ? "attached" : "not attached");
	PRINTSTAT("Card %s", k);
	k = ((ctlr->state & Power) ? "on" : "off");
	PRINTSTAT("PCardower %s", k);
	k = ((ctlr->txbusy)? ", txbusy" : "");
	PRINTSTAT("%s\n", k);

	if(ctlr->hascrypt){
		PRINTSTR("Keys: ");
		for (i = 0; i < WNKeys; i++){
			if(ctlr->keys.keys[i].len == 0)
				PRINTSTR("none ");
			else if(SEEKEYS == 0)
				PRINTSTR("set ");
			else
				PRINTSTAT("%s ", ctlr->keys.keys[i].dat);
		}
		PRINTSTR("\n");
	}

	// real card stats
	ilock(ctlr);
	PRINTSTR("\nCard stats: \n");
	PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts));
	PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts));
	i = ltv_ins(ctlr, WTyp_Ptype);
	PRINTSTAT("Port type: %d\n", i);
	PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate));
	PRINTSTAT("Current Transmit rate: %d\n",
		ltv_ins(ctlr, WTyp_CurTxRate));
	PRINTSTAT("Channel: %d\n", ltv_ins(ctlr, WTyp_Chan));
	PRINTSTAT("AP density: %d\n", ltv_ins(ctlr, WTyp_ApDens));
	PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom));
	if(i == 3)
		PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName));
	else {
		Wltv ltv;
		PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName));
		ltv.type = WTyp_BaseID;
		ltv.len = 4;
		if(w_inltv(ctlr, &ltv))
			print("#l%d: unable to read base station mac addr\n", ether->ctlrno);
		l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n",
			ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]);
	}
	PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName));
	PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName));
	if(ltv_ins(ctlr, WTyp_HasCrypt) == 0)
		PRINTSTR("WEP: not supported\n");
	else {
		if(ltv_ins(ctlr, WTyp_Crypt) == 0)
			PRINTSTR("WEP: disabled\n");
		else{
			PRINTSTR("WEP: enabled\n");
			k = ((ctlr->xclear)? "excluded": "included");
			PRINTSTAT("Clear packets: %s\n", k);
			txid = ltv_ins(ctlr, WTyp_TxKey);
			PRINTSTAT("Transmit key id: %d\n", txid);
		}
	}
	iunlock(ctlr);

	PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes);
	PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes);
	PRINTSTAT("ntxfrags: %lud\n", ctlr->ntxfrags);
	PRINTSTAT("ntxubytes: %lud\n", ctlr->ntxubytes);
	PRINTSTAT("ntxmbytes: %lud\n", ctlr->ntxmbytes);
	PRINTSTAT("ntxdeferred: %lud\n", ctlr->ntxdeferred);
	PRINTSTAT("ntxsretries: %lud\n", ctlr->ntxsretries);
	PRINTSTAT("ntxmultiretries: %lud\n", ctlr->ntxmultiretries);
	PRINTSTAT("ntxretrylimit: %lud\n", ctlr->ntxretrylimit);
	PRINTSTAT("ntxdiscards: %lud\n", ctlr->ntxdiscards);
	PRINTSTAT("nrxuframes: %lud\n", ctlr->nrxuframes);
	PRINTSTAT("nrxmframes: %lud\n", ctlr->nrxmframes);
	PRINTSTAT("nrxfrags: %lud\n", ctlr->nrxfrags);
	PRINTSTAT("nrxubytes: %lud\n", ctlr->nrxubytes);
	PRINTSTAT("nrxmbytes: %lud\n", ctlr->nrxmbytes);
	PRINTSTAT("nrxfcserr: %lud\n", ctlr->nrxfcserr);
	PRINTSTAT("nrxdropnobuf: %lud\n", ctlr->nrxdropnobuf);
	PRINTSTAT("nrxdropnosa: %lud\n", ctlr->nrxdropnosa);
	PRINTSTAT("nrxcantdecrypt: %lud\n", ctlr->nrxcantdecrypt);
	PRINTSTAT("nrxmsgfrag: %lud\n", ctlr->nrxmsgfrag);
	PRINTSTAT("nrxmsgbadfrag: %lud\n", ctlr->nrxmsgbadfrag);
	USED(l);
	n = readstr(offset, a, n, p);
	free(p);
	return n;
}
#undef PRINTSTR
#undef PRINTSTAT

int
w_option(Ctlr* ctlr, char* buf, long n)
{
	char *p;
	int i, r;
	WKey *key;
	Cmdbuf *cb;

	r = 0;
	print("here\n");
	cb = parsecmd(buf, n);
	for (i = 0; i < cb->nf; i++)
		iprint("cb %s\n", cb->f[i]);

	if(cb->nf < 2)
		r = -1;
	else if(cistrcmp(cb->f[0], "essid") == 0){
		if(cistrcmp(cb->f[1],"default") == 0)
			p = "";
		else
			p = cb->f[1];
		if(ctlr->ptype == 3){
			memset(ctlr->netname, 0, sizeof(ctlr->netname));
			strncpy(ctlr->netname, p, WNameLen);
		}
		else{
			memset(ctlr->wantname, 0, sizeof(ctlr->wantname));
			strncpy(ctlr->wantname, p, WNameLen);
		}
	}
	else if(cistrcmp(cb->f[0], "station") == 0){
		memset(ctlr->nodename, 0, sizeof(ctlr->nodename));
		strncpy(ctlr->nodename, cb->f[1], WNameLen);
	}
	else if(cistrcmp(cb->f[0], "channel") == 0){
		if((i = atoi(cb->f[1])) >= 1 && i <= 16)
			ctlr->chan = i;
		else
			r = -1;
	}
	else if(cistrcmp(cb->f[0], "mode") == 0){
		if(cistrcmp(cb->f[1], "managed") == 0)
			ctlr->ptype = WPTypeManaged;
		else if(cistrcmp(cb->f[1], "wds") == 0)
			ctlr->ptype = WPTypeWDS;
		else if(cistrcmp(cb->f[1], "adhoc") == 0)
			ctlr->ptype = WPTypeAdHoc;
		else if((i = atoi(cb->f[1])) >= 1 && i <= 3)
			ctlr->ptype = i;
		else
			r = -1;
	}
	else if(cistrcmp(cb->f[0], "ibss") == 0){
		if(cistrcmp(cb->f[1], "on") == 0)
			ctlr->createibss = 1;
		else
			ctlr->createibss = 0;
	}
	else if(cistrcmp(cb->f[0], "crypt") == 0){
		if(cistrcmp(cb->f[1], "off") == 0)
			ctlr->crypt = 0;
		else if(cistrcmp(cb->f[1], "on") == 0 && ctlr->hascrypt)
			ctlr->crypt = 1;
		else
			r = -1;
	}
	else if(cistrcmp(cb->f[0], "clear") == 0){
		if(cistrcmp(cb->f[1], "on") == 0)
			ctlr->xclear = 0;
		else if(cistrcmp(cb->f[1], "off") == 0 && ctlr->hascrypt)
			ctlr->xclear = 1;
		else
			r = -1;
	}
	else if(cistrncmp(cb->f[0], "key", 3) == 0){
		if((i = atoi(cb->f[0]+3)) >= 1 && i <= WNKeys){
			ctlr->txkey = i-1;
			key = &ctlr->keys.keys[ctlr->txkey];
			key->len = strlen(cb->f[1]);
			if(key->len > WKeyLen)
				key->len = WKeyLen;
			memset(key->dat, 0, sizeof(key->dat));
			memmove(key->dat, cb->f[1], key->len);
		}
		else
			r = -1;
	}
	else if(cistrcmp(cb->f[0], "txkey") == 0){
		if((i = atoi(cb->f[1])) >= 1 && i <= WNKeys)
			ctlr->txkey = i-1;
		else
			r = -1;
	}
	else if(cistrcmp(cb->f[0], "pm") == 0){
		if(cistrcmp(cb->f[1], "off") == 0)
			ctlr->pmena = 0;
		else if(cistrcmp(cb->f[1], "on") == 0){
			ctlr->pmena = 1;
			if(cb->nf == 3){
				i = atoi(cb->f[2]);
				// check range here? what are the units?
				ctlr->pmwait = i;
			}
		}
		else
			r = -1;
	}
	else
		r = -2;
	free(cb);

	return r;
}

long
w_ctl(Ether* ether, void* buf, long n)
{
	Ctlr *ctlr;

	if((ctlr = ether->ctlr) == nil)
		error(Enonexist);
	if((ctlr->state & Attached) == 0)
		error(Eshutdown);

	ilock(ctlr);
	if(w_option(ctlr, buf, n)){
		iunlock(ctlr);
		error(Ebadctl);
	}
	if(ctlr->txbusy)
		w_txdone(ctlr, WTxErrEv);
	w_enable(ether);
	w_txstart(ether);
	iunlock(ctlr);

	return n;
}

void
w_transmit(Ether* ether)
{
	Ctlr* ctlr = ether->ctlr;

	if(ctlr == 0)
		return;

	ilock(ctlr);
	ctlr->ntxrq++;
	w_txstart(ether);
	iunlock(ctlr);
}

void
w_promiscuous(void* arg, int on)
{
	Ether* ether = (Ether*)arg;
	Ctlr* ctlr = ether->ctlr;

	if(ctlr == nil)
		error("card not found");
	if((ctlr->state & Attached) == 0)
		error("card not attached");
	ilock(ctlr);
	ltv_outs(ctlr, WTyp_Prom, (on?1:0));
	iunlock(ctlr);
}

void
w_interrupt(Ureg* ,void* arg)
{
	Ether* ether = (Ether*) arg;
	Ctlr* ctlr = (Ctlr*) ether->ctlr;

	if(ctlr == 0)
		return;
	ilock(ctlr);
	ctlr->nints++;
	w_intr(ether);
	iunlock(ctlr);
}

int
wavelanreset(Ether* ether, Ctlr *ctlr)
{
	Wltv ltv;

	iprint("wavelanreset, iob 0x%ux\n", ctlr->iob);
	w_intdis(ctlr);
	if(w_cmd(ctlr,WCmdIni,0)){
		iprint("#l%d: init failed\n", ether->ctlrno);
		return -1;
	}
	w_intdis(ctlr);
	ltv_outs(ctlr, WTyp_Tick, 8);

	ctlr->chan = 0;
	ctlr->ptype = WDfltPType;
	ctlr->txkey = 0;
	ctlr->createibss = 0;
	ctlr->keys.len = sizeof(WKey)*WNKeys/2 + 1;
	ctlr->keys.type = WTyp_Keys;
	if(ctlr->hascrypt = ltv_ins(ctlr, WTyp_HasCrypt))
		ctlr->crypt = 1;
	*ctlr->netname = *ctlr->wantname = 0;
	strcpy(ctlr->nodename, "Plan 9 STA");

	ctlr->netname[WNameLen-1] = 0;
	ctlr->wantname[WNameLen-1] = 0;
	ctlr->nodename[WNameLen-1] =0;

	ltv.type = WTyp_Mac;
	ltv.len	= 4;
	if(w_inltv(ctlr, &ltv)){
		iprint("#l%d: unable to read mac addr\n",
			ether->ctlrno);
		return -1;
	}
	memmove(ether->ea, ltv.addr, Eaddrlen);

	if(ctlr->chan == 0)
		ctlr->chan = ltv_ins(ctlr, WTyp_Chan);
	ctlr->apdensity = WDfltApDens;
	ctlr->rtsthres = WDfltRtsThres;
	ctlr->txrate = WDfltTxRate;
	ctlr->maxlen = WMaxLen;
	ctlr->pmena = 0;
	ctlr->pmwait = 100;
	ctlr->signal = 1;
	ctlr->noise = 1;
	ctlr->state |= Power;

	// free old Ctlr struct if resetting after suspend
	if(ether->ctlr && ether->ctlr != ctlr)
		free(ether->ctlr);

	// link to ether
	ether->ctlr = ctlr;
	ether->mbps = 10;
	ether->attach = w_attach;
	ether->detach = w_detach;
	ether->interrupt = w_interrupt;
	ether->transmit = w_transmit;
	ether->ifstat = w_ifstat;
	ether->ctl = w_ctl;
	ether->power = w_power;
	ether->promiscuous = w_promiscuous;
	ether->multicast = w_multicast;
	ether->arg = ether;

	iprint("#l%d: irq %ld port %lx type %s",
		ether->ctlrno, ether->intnum, ether->ports[0].port,	ether->type);
	iprint(" %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX\n",
		ether->ea[0], ether->ea[1], ether->ea[2],
		ether->ea[3], ether->ea[4], ether->ea[5]);

	return 0;
}

char* wavenames[] = {
	"WaveLAN/IEEE",
	"TrueMobile 1150",
	"Instant Wireless ; Network PC CARD",
	"Avaya Wireless PC Card",
	"AirLancer MC-11",
	nil,
};


M bitsy/wavelan.h => bitsy/wavelan.h +0 -3
@@ 1,4 1,1 @@
typedef struct Pcidev Pcidev;
/* not used, but needed in a struct declaration */

#include "../pc/wavelan.h"

M ip/ipifc.c => ip/ipifc.c +1 -1
@@ 579,7 579,7 @@ ipifcrem(Ipifc *ifc, char **argv, int argc, int dolock)

	/*
	 *  find address on this interface and remove from chain.
	 *  for pt to pt we actually specify the remote address at the
	 *  for pt to pt we actually specify the remote address as the
	 *  addresss to remove.
	 */
	l = &ifc->lifc;