9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
* [9fans] kernel config changes
@ 2002-12-13  8:32 Russ Cox
  2002-12-13 16:32 ` Jack Johnson
  2003-01-24  2:44 ` John Packer
  0 siblings, 2 replies; 5+ messages in thread
From: Russ Cox @ 2002-12-13  8:32 UTC (permalink / raw)
  To: 9fans

There is a new kernel config file section called "bootdir" that lists
the files to be placed in #//boot.  There are no binaries in #/ itself
anymore.

A typical bootdir section might look like

	bootdir
		bootpcdisk.out boot
		/386/bin/ip/ipconfig
		/386/bin/auth/factotum
		/386/bin/disk/kfs
		/386/bin/cfs

which says that /boot will contain four files: boot, ipconfig,
factotum, kfs, and cfs, copied from the paths listed.

The kernels have always special cased boot$CONF.out to get it listed
as /boot.  It is now /boot/boot, and no longer a special case.

Now that everything is in /boot, initcode had to change for all the
kernels; /boot/boot itself had to change to exec /boot/ipconfig
(instead of /ipconfig), etc.  Finally, auth_getkey changed.  It looks
for /factotum and then /boot/factotum, to deal with either type of
kernel.

The boot file /386/bin/ip/ipconfig (e.g.) turns into
_386_bin_ip_ipconfig.root.s and then _386_bin_ip_ipconfig.root.8.
It's a bit of an ugly name, but mk clean will get rid of them.  You
could imagine just using the last path element, but that might lead to
weird build inconsistencies if kernels were using two different files
with the same last element.  One unfortunate consequence of the big
name is that _386_bin_auth_factotum.root.s is too long for the current
file servers to deal with.  You could use lnfs to get around this, but
I didn't want to require lnfs for the kernel build, so we have a rule
in portmkfile that copies factotum and ipconfig to factotum.hack and
ipconfig.hack and then the bootdir section actually looks like:

	bootdir
		bootpcdisk.out boot
		ipconfig.hack ipconfig
		factotum.hack factotum
		/386/bin/disk/kfs
		/386/bin/cfs

sadly.  That will go away when we get long file names.

The pc initcode is also changed.  Whereas before it used to do

	exec("/boot", ["boot", 0]);

in hand-coded assembly (pc/initcode.s), it now does

	open("#c/cons", OREAD);
	open("#c/cons", OWRITE);
	open("#c/cons", OWRITE);
	bind("#c", "/dev", MAFTER);
	bind("#ec", "/env", MAFTER);
	bind("#e", "/env", MCREATE|MAFTER);
	bind("#s", "/srv", MREPL|MCREATE);
	execl("/boot/boot", "/boot/boot", 0);

in C (port/initcode.c).  The point is to set up enough of a namespace
that /boot/boot can be a shell script.  Eventually the other
architectures should change too, though it's not urgent.  If someone
wants to do the bitsy, send me mail and I'll let you know what it took
to get one of the old undistributed mips kernels changed over.  (The
compiler setup is similar.)

As a much bigger example, I built a kernel called pcext the other
night.  It uses rx and trampoline to connect through a Plan 9 gateway
machine to the root file server.  It uses this bootdir:

	bootdir
		bootext boot
		/386/bin/rc
		/rc/lib/rcmain
		/386/bin/bind
		/386/bin/cat
		/386/bin/cp
		/386/bin/echo
		/386/bin/mount
		/386/bin/sleep
		factotum.hack factotum
		/386/bin/ip/ipconfig
		/386/bin/cfs
		/386/bin/aux/srvthru

(and does require lnfs to build).

Srvthru is the obvious program -- it connects to rx,
authenticates, starts trampoline to the real file server,
and then optionally pushes cfs onto the connection.

The point is that bootext is a shell script, which
makes it easier to tweak than when /boot was a
binary building out of /sys/src/9/boot.

	#!/boot/rc -m /boot/rcmain

	cd /boot
	cp '#r/rtc' '#c/time'
	bind -a /boot /bin
	bind -a '#I' /net
	bind -a '#l0' /net
	bind -a '#S' /dev
	bind '#p' /proc
	bind '#d' /fd
	bind -a /boot /
	ipconfig
	echo 'authdom=cs.bell-labs.com auth=204.178.31.3' >>/net/ndb
	factotum -sfactotum -u -a 204.178.31.3
	cargs=()
	if(~ $#cfs 1 && ! ~ $cfs off)
		cargs=(-c $cfs)
	srvthru $cargs tcp!204.178.31.2!17009 il!emelie!9fs boot emelie
	mount -c /srv/boot /root
	bind -ac /root /
	bind -c /root/mnt /mnt
	mount -a '#s/factotum' /mnt
	rootdir=/root
	rootspec=''
	/386/init -t
	/boot/rc -m/boot/rcmain -i	# in case init fails

It takes care of everything /boot traditionally
does, using srvthru as the way to establish /srv/boot
(and also /srv/emelie, just for kicks).

Russ



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [9fans] kernel config changes
  2002-12-13  8:32 [9fans] kernel config changes Russ Cox
@ 2002-12-13 16:32 ` Jack Johnson
  2003-01-24  2:44 ` John Packer
  1 sibling, 0 replies; 5+ messages in thread
From: Jack Johnson @ 2002-12-13 16:32 UTC (permalink / raw)
  To: 9fans

On Fri, 2002-12-13 at 00:32, Russ Cox wrote:
> The point is that bootext is a shell script, which
> makes it easier to tweak than when /boot was a
> binary building out of /sys/src/9/boot.

The stuff that can be accomplished in Plan 9 with shell scripts never
ceases to amaze me.

-Jack



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [9fans] kernel config changes
  2002-12-13  8:32 [9fans] kernel config changes Russ Cox
  2002-12-13 16:32 ` Jack Johnson
@ 2003-01-24  2:44 ` John Packer
  2003-01-24 16:52   ` Russ Cox
  1 sibling, 1 reply; 5+ messages in thread
From: John Packer @ 2003-01-24  2:44 UTC (permalink / raw)
  To: 9fans


On Friday, December 13, 2002, at 03:32  AM, Russ Cox wrote:

> The pc initcode is also changed.  Whereas before it used to do
>
> 	exec("/boot", ["boot", 0]);
>
> in hand-coded assembly (pc/initcode.s), it now does
>
> 	open("#c/cons", OREAD);
> 	open("#c/cons", OWRITE);
> 	open("#c/cons", OWRITE);
> 	bind("#c", "/dev", MAFTER);
> 	bind("#ec", "/env", MAFTER);
> 	bind("#e", "/env", MCREATE|MAFTER);
> 	bind("#s", "/srv", MREPL|MCREATE);
> 	execl("/boot/boot", "/boot/boot", 0);
>
> in C (port/initcode.c).  The point is to set up enough of a namespace
> that /boot/boot can be a shell script.  Eventually the other
> architectures should change too, though it's not urgent.  If someone
> wants to do the bitsy, send me mail and I'll let you know what it took
> to get one of the old undistributed mips kernels changed over.  (The
> compiler setup is similar.)


Could you describe what you did for the mips kernels? I would
like to know, both for the bitsy kernel and for some mips
related work I've been planning to do.

John



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [9fans] kernel config changes
  2003-01-24  2:44 ` John Packer
@ 2003-01-24 16:52   ` Russ Cox
  2003-01-26 22:40     ` john
  0 siblings, 1 reply; 5+ messages in thread
From: Russ Cox @ 2003-01-24 16:52 UTC (permalink / raw)
  To: 9fans

There's now both port/initcode.c, which does almost
what I posted (I've since noticed that the exec cmd line
args get passed into the main function, now called startboot):

#include <u.h>
#include <libc.h>

char cons[] = "#c/cons";
char boot[] = "/boot/boot";
char dev[] = "/dev";
char c[] = "#c";
char e[] = "#e";
char ec[] = "#ec";
char s[] = "#s";
char srv[] = "/srv";
char env[] = "/env";

void
startboot(char *argv0, char **argv)
{
	open(cons, OREAD);
	open(cons, OWRITE);
	open(cons, OWRITE);
	bind(c, dev, MAFTER);
	bind(ec, env, MAFTER);
	bind(e, env, MCREATE|MAFTER);
	bind(s, srv, MREPL|MCREATE);
	exec(boot, argv);
	for(;;);
}

On the MIPS there is still an assembly stub
that initializes R30 (SB).

TEXT	_main(SB), $8
	MOVW	$setR30(SB), R30
	MOVW	$boot(SB), R1
	ADD	$12, R29, R2	/* get a pointer to 0(FP) */
	MOVW	R1, 4(R29)
	MOVW	R2, 8(R29)
	JAL	startboot(SB)

The 386 uses just the C code -- no assembly required.

You should be able to do almost exactly the same
thing for the bitsy -- SB is R12.

Russ


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [9fans] kernel config changes
  2003-01-24 16:52   ` Russ Cox
@ 2003-01-26 22:40     ` john
  0 siblings, 0 replies; 5+ messages in thread
From: john @ 2003-01-26 22:40 UTC (permalink / raw)
  To: 9fans

[-- Attachment #1: Type: text/plain, Size: 1404 bytes --]

Russ, Nemo:

I've updated the bitsy kernel. Attached is a diff and the file init9.s,
and the other modified files (as I think Nemo prefers). I had to make
a small change to wavelan.c, but the mkfile should probably be
changed to use ../port/wavelan.c instead.

I'm not sure the bootdir section needs anything other than
bootbitsy.out. Anything more than what I have makes the
kernel too large to boot.

John


diff /n/dump/2003/0118/sys/src/9/bitsy/bitsy /sys/src/9/bitsy/bitsy
41d40
< 	paqfs.root
49a49,52
 >
 > bootdir
 > 	bootbitsy.out boot
 > 	/arm/bin/paqfs paqfs
Only in /sys/src/9/bitsy: init9.s
diff /n/dump/2003/0118/sys/src/9/bitsy/mkfile /sys/src/9/bitsy/mkfile
50d49
< 	boot$CONF.root.$O\
93a93
 > <|../port/mkbootrules $CONF
95,97c95,98
< init.h:	initcode /sys/src/libc/9syscall/sys.h
< 	$AS initcode
< 	$LD -l -s -R4 -o init.out initcode.$O
---
 > init.h:	../port/initcode.c init9.s
 > 	$CC ../port/initcode.c
 > 	$AS init9.s
 > 	$LD -l -R1 -o init.out init9.$O initcode.$O /arm/lib/libc.a
99c100
< 	 xd -r -1x init.out |
---
 > 	 strip < init.out | xd -1x |
Common subdirectories: /n/dump/2003/0118/sys/src/9/bitsy/paqfiles and 
/sys/src/9/bitsy/paqfiles
diff /n/dump/2003/0118/sys/src/9/bitsy/wavelan.c 
/sys/src/9/bitsy/wavelan.c
658c658
< 				w_cmd(ctlr, WCmdAskStats, WTyp_Stats);
---
 > 				w_cmd(ctlr, WCmdEnquire, WTyp_Stats);




[-- Attachment #2.1: Type: text/plain, Size: 341 bytes --]

The following attachment had content that we can't
prove to be harmless.  To avoid possible automatic
execution, we changed the content headers.
The original header was:

	Content-Disposition: attachment;
	filename=init9.s
	Content-Transfer-Encoding: 7bit
	Content-Type: application/octet-stream;
	x-unix-mode=0644;
	name="init9.s"

[-- Attachment #2.2: init9.s.suspect --]
[-- Type: application/octet-stream, Size: 193 bytes --]

TEXT	_main(SB),$8
	MOVW	$setR12(SB), R12	/* load the SB */
	MOVW	$boot(SB), R0

	ADD	$12, R13, R1	/* get a pointer to 0(FP) */

	MOVW	R0, 4(R13)
	MOVW	R1, 8(R13)

	BL	startboot(SB)


[-- Attachment #3: Type: text/plain, Size: 4 bytes --]




[-- Attachment #4: mkfile --]
[-- Type: application/octet-stream, Size: 2565 bytes --]

CONF=bitsy
CONFLIST=bitsy

objtype=arm
</$objtype/mkfile
p=9

DEVS=`{rc ../port/mkdevlist $CONF}

PORT=\
	alarm.$O\
	alloc.$O\
	allocb.$O\
	auth.$O\
	cache.$O\
	chan.$O\
	dev.$O\
	fault.$O\
	latin1.$O\
	rebootcmd.$O\
	page.$O\
	parse.$O\
	pgrp.$O\
	portclock.$O\
	print.$O\
	proc.$O\
	nulledf.$O\
	qio.$O\
	qlock.$O\
	segment.$O\
	swap.$O\
	sysfile.$O\
	sysproc.$O\
	taslock.$O\
	tod.$O\
	xalloc.$O\

OBJ=\
	l.$O\
	main.$O\
	trap.$O\
	clock.$O\
	mmu.$O\
	power.$O\
	random.$O\
	sa1110dma.$O\
	screen.$O\
	$DEVS\
	$PORT\
	fpi.$O\
	fpiarm.$O\
	fpimem.$O\
	defont.$O\

LIB=\
	/$objtype/lib/libmemlayer.a\
	/$objtype/lib/libmemdraw.a\
	/$objtype/lib/libdraw.a\
	/$objtype/lib/libc.a\
	/$objtype/lib/libsec.a\
	/$objtype/lib/libip.a\

size:VQ: $p$CONF
	ls -l $p$CONF|sed 's/^[^ ]+ [^ ]+ [^ ]+ [^ ]+ [^ ]+ ([^ ]+).*/load ram 0xc0008000 \1/'
	echo load kernel
	echo !xms /sys/src/9/bitsy/^$p$CONF
	echo jump 0xc0008010

acid:VQ:
	echo acid -lmap s9bitsy

$p$CONF:	$OBJ $CONF.c $LIB /arm/inflate
	$CC $CFLAGS '-DKERNDATE='`{date -n} $CONF.c
	$LD -o $target -H5 -R4 -T0xC0008010 -l $OBJ $CONF.$O $LIB
	$LD -o s$target -R4 -T0xC0008010 -l $OBJ $CONF.$O $LIB
	gzip $target
	cat /arm/inflate $target.gz > $target
	echo the boot loader misses the last 12 bytes >> $target
	rm $target.gz

install:V:	$p$CONF paqdisk
	test -d /n/once/arm && cp $p$CONF /n/once/arm/$p$CONF
	test -d /n/once/arm && cp s$p$CONF /n/once/arm/s$p$CONF
	test -d /n/once/arm && cp paqdisk /n/once/arm/paqdisk
	# 9fs lookout && cp $p$CONF /n/lookout/$objtype/$p$CONF

installkern:V:	$p$CONF
	cp $p$CONF /$objtype/$p$CONF
	cp s$p$CONF /$objtype/s$p$CONF

<../boot/bootmkfile
<../port/portmkfile
<|../port/mkbootrules $CONF

init.h:	../port/initcode.c init9.s
	$CC ../port/initcode.c
	$AS init9.s
	$LD -l -R1 -o init.out init9.$O initcode.$O /arm/lib/libc.a
	{echo 'uchar initcode[]={'
	 strip < init.out | xd -1x |
		sed -e 's/^[0-9a-f]+ //' -e 's/ ([0-9a-f][0-9a-f])/0x\1,/g'
	 echo '};'} > init.h

clock.$O:	/$objtype/include/ureg.h

devether.$O: ../port/netif.h
wavelan.$O: wavelan.c wavelan.h ../pc/wavelan.h

fpi.$O: fpi.h
fpiarm.$O: fpi.h
fpimem.$O: fpi.h

screen.$O: gamma.h

paqdisk:V:
	rm -fr armpaq
	mkdir armpaq
	cd armpaq
	disk/mkfs -a /sys/lib/sysconfig/proto/armpaqproto|disk/mkext -d .
	mkpaqfs -o ../paqdisk
	cd ..
	echo load ramdisk
	echo !xms /sys/src/9/bitsy/paqdisk

bitsy.clean:
	rm -rf armpaq $p$CONF s$p$CONF paqdisk $CONF.c boot$CONF.c ../boot/libboot.a5


[-- Attachment #5: Type: text/plain, Size: 4 bytes --]




[-- Attachment #6: bitsy --]
[-- Type: application/octet-stream, Size: 503 bytes --]

dev
	root
	cons
	dup
	env
	ether	netif
	flash
	ip	arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium inferno
	kprof
	mnt
	µc
	pcmcia	cis
	draw
	penmouse
	pipe
	proc
	realtime	realtimesub edf
	cap
	srv
	ssl
	uart
	uda1341
	sd

ip
	il
	tcp
	udp
	ipifc
	icmp
	icmp6
	gre
	ipmux

link
	etherwavelan wavelan
	ethermedium

misc
	uartsa1110
	sdata

port
	int cpuserver = 1;

boot cpu
boot
	paq

bootdir
	bootbitsy.out boot
	/arm/bin/paqfs paqfs

[-- Attachment #7: Type: text/plain, Size: 4 bytes --]




[-- Attachment #8: wavelan.c --]
[-- Type: text/plain, Size: 26834 bytes --]

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


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2003-01-26 22:40 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-12-13  8:32 [9fans] kernel config changes Russ Cox
2002-12-13 16:32 ` Jack Johnson
2003-01-24  2:44 ` John Packer
2003-01-24 16:52   ` Russ Cox
2003-01-26 22:40     ` john

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).