Plan 9 from Bell Labs’s /usr/web/sources/contrib/rsc/ipw2200/etheripw2200.c

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


/*
 * Intel PRO/Wireless 2200BG/2915ABG driver.
 * 
 * Written using FreeBSD iwi and Linux ipw2200
 * drivers as documentation.
 *
 * Not complete.  Doesn't send or receive packets, though
 * the bulk of the code is present.  Need to fiddle with wireless
 * headers and the like.  Also need to figure out channel scanning
 * etc.
 *
 * TO DO:
 *	- better story for 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"

#define MIN(a, b)	((a) < (b) ? (a) : (b))

enum
{
	/* device constants */
	NCmdRing	= 8,
	NTdRing		= 64,
	NRdRing		= 32,
	Neeprom		= 128,
	Rbsz			= 3000,

	/* registers */
	Intr			= 0x0008,
	IntrMask		= 0x000C,
		IntrRx		= 0x00000002,
		IntrStatusChg		= 0x00000010,
		IntrBeaconExp		= 0x00000020,
		IntrCmd	= 0x00000800,
		IntrTx1	= 0x00001000,
		IntrTx2	= 0x00002000,
		IntrTx3	= 0x00004000,
		IntrTx4	= 0x00008000,
		IntrTx	= 0x0000F000,
		IntrSlmodeCdone	= 0x00010000,
		IntrPrepPwdown	= 0x00100000,
		IntrFwInit		= 0x01000000,
		IntrDisPhyDone		= 0x02000000,
		IntrRadioOff		= 0x04000000,
		IntrFatalError		= 0x40000000,
		IntrParityError		= 0x80000000,
		
		IntrDefault		=
			IntrRx | IntrCmd | IntrTx | IntrFwInit |
			IntrRadioOff | IntrFatalError | IntrParityError,
			
	IndirectAddr	= 0x0010,
	IndirectData	= 0x0014,
	AutoincAddr	= 0x0018,
	AutoincData 	= 0x001C,
	Rst			= 0x0020,
		RstPrincetonReset	= 0x00000001,
		RstSwReset		= 0x00000080,
		RstMasterDisabled	= 0x00000100,
		RstStopMaster		= 0x00000200,
	Ctl			= 0x0024,
		CtlClockReady		= 0x00000001,
		CtlAllowStandby	= 0x00000002,
		CtlInit			= 0x00000004,
	Io			= 0x0030,
		IoRadioEnabled		= 0x00000001,
	CmdBase		= 0x0200,
	CmdSize		= 0x0204,
	Tx1Base		= 0x0208,
	Tx1Size		= 0x020C,
	Tx2Base		= 0x0210,
	Tx2Size		= 0x0214,
	Tx3Base		= 0x0218,
	Tx3Size		= 0x021C,
	Tx4Base		= 0x0220,
	Tx4Size		= 0x0224,
	CmdReadX	= 0x0280,
	Tx1ReadX		= 0x0284,
	Tx2ReadX		= 0x0288,
	Tx3ReadX		= 0x028C,
	Tx4ReadX		= 0x0290,
	RxReadX		= 0x02A0,
	RxBase		= 0x0500,
	Table0Size	= 0x0700,
	Table0Base	= 0x0704,
	CurTxRate	= Table0Base,
	GetTable0Base	= 0x0380,
	CurTable1Base	= 0x0384,
	CurTable2Base	= 0x0388,
	CurTable3Base	= 0x038C,
	
	ErrorLog		= 0x0610,

	CmdWriteX	= 0x0F80,
	Tx1WriteX		= 0x0F84,
	Tx2WriteX		= 0x0F88,
	Tx3WriteX		= 0x0F8C,
	Tx4WriteX		= 0x0F90,
	RxWriteX		= 0x0FA0,
	ReadInt		= 0x0FF4,
		ReadIntInitHost		= 0x20000000,

	/* indirect registers */
	EeCtl		= 0x00300040,
		EeClock		= 1,
		EeSelect	= 2,
		EeOut		= 4,
		EeIn		= 0x10,
	
	/* eeprom offsets */
	EeMac		= 0x21,

	/* header types */
	HdrTypeData	= 0,
	HdrTypeCmd	= 1,
	HdrTypeNotify	= 3,
	HdrTypeFrame	= 9,

	/* header flags */
	HdrFlagIrq	= 0x04,

	/* Cmd.type */
	CmdEnable		= 2,
	CmdSetConfig	= 6,
	CmdSetEssid		= 8,
	CmdSetMacAddr	= 11,
	CmdSetRtsThreshold	= 15,
	CmdSetPower	= 17,
	CmdSetWepKey	= 18,
	CmdAssoc	= 21,
	CmdSetRates	= 22,
	CmdScanAbort	= 23,
	CmdScan	= 26,
	CmdDisable	= 33,
	CmdSetIV		= 34,
	CmdSetTxPower	= 35,
	CmdSetSensitivity	= 42,
	
	/* modes */
	Mode11a	= 0,
	Mode11b	= 1,
	Mode11g	= 2,
	
	/* Rates.type */
	RatesNegotiated	= 0,
	RatesSupported	= 1,
	
	/* Power.power */
	PowerMax			= 20,
	
	/* Power.mode */
	PowerModeCam	= 0,

	/* Assoc.type */
	Dissociate		= 2,

	/* Assoc.auth */
	AuthOpen		= 0,
	AuthShared	= 1,
	AuthNone		= 3,

	/* Scan.type */
	ScanPassive	= 0x11,
	ScanDirected	= 0x22,
	ScanBroadcast	= 0x33,
	ScanBDirected	= 0x44,
	
	/* Scan.channels indicies */
	Chan5Ghz	= 0<<6,
	Chan2Ghz	= 1<<6,

	/* Wepkey.cmd */
	WepkeySetKey	= 8,
	
	/* Notify.type */
	NotifyTypeAssociation	= 10,
	NotifyTypeAuth	= 11,
	NotifyTypeScanChannel	= 12,
	NotifyTypeScanComplete	= 13,
	NotifyTypeBeacon	= 17,
	NotifyTypeCalibration	= 20,
	NotifyTypeNoise	= 25,

	/* NotifyAuth.state */
	Deauthenticated	= 0,
	Authenticated	= 9,

	/* NotifyAssociation.state */
	Dissociated	= 0,
	Associated	= 12,
	
	/* Td.cmd */
	TdCmdTx		= 0x0B,
	
	/* Td.flags */
	TdFlagShPremable	= 0x04,
	TdFlagNoWep		= 0x20,
	TdFlagNeedAck		= 0x80,

	/* Td.nseg */
	MaxSeg			= 6,

	/* firmware command blocks */
	CbDefaultCtl		= 0x8CEA0000,
	CbMaxData		= 8191,

	/* driver constants */
	Nrate 		= 16,
	
	ModeSta		= 0,
	ModeIbss		= 1,

	/* Ctlr.state flags */
	CmdPending	= 1<<0,
	Scanning		= 1<<1,
	ScanAbort		= 1<<2,
	RfDisabled	= 1<<3,
	FwInited		= 1<<4,
	FwIbss		= 1<<5,
	Wep			= 1<<6,
	ScanComplete	= 1<<7,
	Okay			= 1<<8,

	/* generic 802.11 constants */
	Waddrlen		= 6,
	Wkeylen		= 16,
	Wessidlen		= 32,
	Wnwep		= 4,
	
	/* firmware file header size */
	FwHeader		= 8,
};

typedef struct Assoc Assoc;
typedef struct Cmd Cmd;
typedef struct Config Config;
typedef struct Frame Frame;
typedef struct Fwstation Fwstation;
typedef struct Hdr Hdr;
typedef struct Notify Notify;
typedef struct NotifyNoise NotifyNoise;
typedef struct NotifyAuth NotifyAuth;
typedef struct NotifyAssoc NotifyAssoc;
typedef struct NotifyScanChannel NotifyScanChannel;
typedef struct NotifyScanComplete NotifyScanComplete;
typedef struct Power Power;
typedef struct Rd Rd;
typedef struct Rates Rates;
typedef struct Scan Scan;
typedef struct Td Td;
typedef struct WFrame WFrame;
typedef struct WKey WKey;
typedef struct WepKey WepKey;
typedef struct WQFrame4 WQFrame4;

/*
 * IEEE 802.11 structures.
 */
#pragma pack on
struct WFrame
{
	u8int	fc[2];
	u8int	dur[2];
	u8int	addr1[Waddrlen];
	u8int	addr2[Waddrlen];
	u8int	addr3[Waddrlen];
	u8int	seq[2];
};

struct WQFrame4
{
	u8int	fc[2];
	u8int	dur[2];
	u8int	addr1[Waddrlen];
	u8int	addr2[Waddrlen];
	u8int	addr3[Waddrlen];
	u8int	seq[2];
	u8int	addr4[Waddrlen];
	u8int	qos[2];
};
#pragma pack off

/*
 * Device structures.
 */
#pragma pack on
struct Hdr
{
	u8int	type;
	u8int	seq;
	u8int	flags;
	u8int	pad;
};

struct Cmd
{
	Hdr		hdr;
	u8int	type;
	u8int	len;
	u16int	reserved;
	u8int	data[120];
};

struct Notify
{
	u32int	reserved[2];
	u8int	type;
	u8int	flags;
	u16int	len;
};

struct NotifyNoise
{
	u32int	value;
};

struct NotifyAuth
{
	u8int	state;
};

struct NotifyAssoc
{
	u8int	state;
	WFrame	frame;
	u16int	capinfo;
	u16int	status;
	u16int	associd;
};

struct NotifyScanChannel
{
	u8int	nchan;
	u8int	reserved[47];
};

struct NotifyScanComplete
{
	u8int	type;
	u8int	nchan;
	u8int	status;
	u8int	reserved;
};

struct Frame
{
	u32int	reserved;
	u8int	parenttsf[4];
	u8int	chan;
	u8int	status;
	u8int	rate;
	u8int	rssi;
	u8int	agc;
	u8int	rssidbm;
	u16int	signal;
	u16int	noise;
	u8int	antenna;
	u8int	control;
	u8int	rtsctsrate;
	u8int	rtsctsseen;
	u16int	len;
};

struct Td
{
/* 0 */
	Hdr		hdr;
	u32int	workptr;
/* 8 */
	u8int	station;
	u8int	reserved[3];
	u8int	cmd;
	u8int	seq;
	u16int	len;
/* 16 */
	u8int	priority;
	u8int	flags;
	u8int	xflags;
	u8int	weptxkey;
/* 20 */
	u8int	wepkey[Wkeylen];
/* 36 */
	u8int	rate;
	u8int	antenna;
	u8int	reserved2[10];
/* 48 */
	WQFrame4	wqhdr;
/* 80 */
	u32int	iv[2];
/* 88 */
	u32int	nseg;
/* 92 */
	u32int	segaddr[MaxSeg];
/* 116 */
	u32int	seglen[MaxSeg];
/* 140 */
};

struct Rates
{
	u8int	mode;
	u8int	nrates;
	u8int	type;
	u8int	reserved;
	u8int	rates[12];
};

struct Power
{
	u8int	nchan;
	u8int	mode;
	struct {
		u8int	chan;
		u8int	power;
	} chan[37];
};

struct Assoc
{
	u8int	chan;
	u8int	auth;
	u8int	type;
	u8int	reserved;
	u16int	policy;
	u8int	plen;
	u8int	mode;
	u8int	bssid[Waddrlen];
	u8int	tstamp[8];
	u16int	capinfo;
	u16int	lintval;
	u16int	intval;
	u8int	dst[Waddrlen];
	u16int	atim_window;
	u8int	smr;
	u8int	reserved1;
	u16int	reserved2;
};

struct Scan
{
	u32int	index;
	u8int	channels[54];
	u8int	type[26];
	u8int	reserved[4];
	u16int	passive;
	u16int	directed;
	u16int	broadcast;
	u16int	bdirected;
};

struct Config
{
	u8int	bluetoothcoexistence;
	u8int	reserved1;
	u8int	answerbroadcastprobe;
	u8int	allowinvalidframes;

	u8int	enablemulticast;
	u8int	excludeunicastunencrypted;
	u8int	disableunicastdecryption;
	u8int	excludemulticastunencrypted;

	u8int	disablemulticastdecryption;
	u8int	antenna;
	u8int	passcrctohost;
	u8int	bgautodetect;

	u8int	enablectstoself;
	u8int	enablemulticastfiltering;
	u8int	bluetooththreshold;
	u8int	reserved2;

	u8int	allowbeaconandproberesp;
	u8int	allowmgt;
	u8int	noisereported;
	u8int	reserved3;
};

struct WepKey
{
	u8int	cmd;
	u8int	seq;
	u8int	idx;
	u8int	len;
	u8int	key[Wkeylen];
};

struct Fwstation
{
	u8int	mac[Eaddrlen];
	u8int	reserved;
	u8int	supportmode;
};
#pragma pack off

struct WKey
{
	char	key[Wkeylen];
	int	len;
};

/*
 * Driver data
 */
typedef struct Ctlr Ctlr;
struct Ctlr
{
	Lock;
	Lock	tlock;

	int	port;
	Pcidev*	pcidev;
	Ctlr*	next;
	int	active;
	int	alloc;
	QLock	alock;
	int	id;
	int	mbps;
	uchar	ea[Eaddrlen];
	ushort	eeprom[Neeprom];
	uint	*mmio;
	uint	state;
	uint	rxwaiting;

	Rendez	cmddone;
	Rendez	scanning;
	Rendez	fwinited;
	Rendez	rxrendez;
	
	Config cfg;
	Scan	scan;
	Assoc	assoc;

	Cmd	cmd[NCmdRing];
	uint	cmdlo;
	uint	cmdhi;
	uint	cmdpend;

	Td	td[NTdRing];
	Block	*tb[NTdRing];
	uint	txlo;
	uint	txhi;

	Block	*rb[NRdRing];
	uint	rxlo;
	uint	rxhi;
	
	uint	opmode;
	uint	rtsthreshold;
	
	char	essid[Wessidlen];
	int		essidlen;
	
	WKey	keys[4];
};

extern uchar	ipw2200_bootcode[];
extern uint	ipw2200_bootlen;
extern uchar	ipw2200_bsscode[];
extern uint	ipw2200_bsslen;
extern uchar	ipw2200_ibsscode[];
extern uint	ipw2200_ibsslen;
extern uchar	ipw2200_monitorcode[];
extern uint	ipw2200_monitorlen;
extern uchar	ipw2200_ucode_bsscode[];
extern uint	ipw2200_ucode_bsslen;
extern uchar	ipw2200_ucode_ibsscode[];
extern uint	ipw2200_ucode_ibsslen;
extern uchar	ipw2200_ucode_monitorcode[];
extern uint	ipw2200_ucode_monitorlen;

static Ctlr *ctlrhead;
static Ctlr *ctlrtail;

static u8int rates11a[] = { 12, 18, 24, 36, 48, 72, 96, 108 };
static u8int rates11b[] = { 2, 4, 11, 22 };
static u8int rates11g[] = { 130, 132, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108 };

static Config defaultcfg =
{
.bluetoothcoexistence = 1,
.bgautodetect	= 1,
.enablemulticast = 1,
.disableunicastdecryption = 1,
//.disablemulticastdecryption = 1,
.allowmgt = 1,
};

#define r32(c, r)	((c)->mmio[(r)/4])
#define r16(c, r)	(((u16int*)(c)->mmio)[(r)/2])
#define r8(c, r)	(((u8int*)(c)->mmio)[(r)])

#define w32(c, r, v)	(r32(c, r) = (v))
#define w16(c, r, v)	(r16(c, r) = (v))
#define w8(c, r, v)	(r8(c, r) = (v))

static int	reset(Ctlr*);
static int	init(Ctlr*);
static void	stop(Ctlr*);
static void	stopmaster(Ctlr*);
static void	interrupt(Ureg*, void*);
static int	abortscan(Ctlr*);
static int	loaducode(Ctlr*, void*, int);
static int	loadfirmware(Ctlr*, void*, int);
static int	config(Ctlr*);
static int	setchan(Ctlr*, int);
static int	init(Ctlr*);
static int	dissociate(Ctlr*);
static void	transmit(Ether*);

static void
iw32(Ctlr *ctlr, u32int r, u32int v)
{
	w32(ctlr, IndirectAddr, r);
	w32(ctlr, IndirectData, v);
}

static void
iw16(Ctlr *ctlr, u32int r, u16int v)
{
	w32(ctlr, IndirectAddr, r);
	w16(ctlr, IndirectData, v);
}

static void
iw8(Ctlr *ctlr, u32int r, u8int v)
{
	w32(ctlr, IndirectAddr, r);
	w8(ctlr, IndirectData, v);
}

static u32int
ir32(Ctlr *ctlr, u32int r)
{
	w32(ctlr, IndirectAddr, r);
	return r32(ctlr, IndirectData);
}

static u16int
ir16(Ctlr *ctlr, u32int r)
{
	w32(ctlr, IndirectAddr, r);
	return r16(ctlr, IndirectData);
}

static u8int
ir8(Ctlr *ctlr, u32int r)
{
	w32(ctlr, IndirectAddr, r);
	return r8(ctlr, IndirectData);
}

/* XXX check if this matches any other driver */
static void
eectl(Ctlr *ctlr, int v)
{
	iw32(ctlr, EeCtl, v);
	microdelay(1);
}

static void
eew(Ctlr *ctlr, int b)
{
	if(b)
		b = EeOut;
	eectl(ctlr, EeSelect|b);
	eectl(ctlr, EeSelect|EeClock|b);
}

static int
eer(Ctlr *ctlr)
{
	eectl(ctlr, EeSelect|EeClock);
	eectl(ctlr, EeSelect);
	return ir32(ctlr, EeCtl) & EeIn;
}

static ushort
eeread(Ctlr *ctlr, int addr)
{
	int bit;
	ushort x;
	
	/* reset */
	eectl(ctlr, 0);
	eew(ctlr, 0);
	eectl(ctlr, EeSelect);
	
	/* start bit*/
	eew(ctlr, 1);
	
	/* read - 10*/
	eew(ctlr, 1);
	eew(ctlr, 0);
	
	/* address */
	for(bit=0x80; bit>0; bit>>=1)
		eew(ctlr, addr&bit);
	eectl(ctlr, EeSelect);
	
	/* read data */
	x = 0;
	for(bit=0x8000; bit>0; bit>>=1)
		if(eer(ctlr))
			x |= bit;
	eectl(ctlr, 0);
	
	eectl(ctlr, EeSelect);
	eectl(ctlr, 0);
	eectl(ctlr, EeClock);
	return x;
}

/*
 * Rendezvous predicates.
 */
static int
isdone(void *v)
{
	Ctlr *ctlr;

	ctlr = v;
	return r32(ctlr, CmdReadX) == ctlr->cmdpend;
}

static int
isfwinited(void *v)
{
	Ctlr *ctlr;
	
	ctlr = v;
	return ctlr->state&FwInited;
}

static  int
isscanning(void *v)
{
	Ctlr *ctlr;
	
	ctlr = v;
	return ctlr->state&Scanning;
}

static int
rxwaiting(void *v)
{
	Ctlr *ctlr;

	ctlr = v;
	if(!(ctlr->state&Okay))
		return 0;
	return ctlr->rxwaiting;
}

static void
printhex(void *v, int n)
{
	int i;
	uint *a;

	a = v;
	for(i=0; i*4<n; i++){
		print(" %.8ux", a[i]);
		if(i%8 == 7)
			print("\n");
	}
	if(i%8)
		print("\n");
}

static char*
cmdname(int type)
{
static char buf[20];

	switch(type){
	case 2: return "Enable";
	case 6: return "SetConfig";
	case 8: return "SetESSID";
	case 11: return "SetMAC";
	case 15: return "SetRtsThreshold";
	case 17: return "SetPower";
	case 18: return "SetWepKey";
	case 21: return "SetAssoc";
	case 22: return "SetRates";
	case 23: return "ScanAbort";
	case 26: return "Scan";
	case 33: return "Disable";
	case 34: return "SetIV";
	case 35: return "SetTxPower";
	case 42: return "SetSensitivity";
	default:
		sprint(buf, "%d", type);
		return buf;
	}
}

/*
 * Issue a command.  We never get ahead of ourselves
 * issuing multiple pending commands.
 */
static void
acmd(Ctlr *ctlr, uchar type, void *data, uint len)
{
	Cmd *cmd;

print("hcmd: type=%s len=%d lo=%d hi=%d\n", cmdname(type), len, r32(ctlr, CmdReadX), r32(ctlr, CmdWriteX));
printhex(data, len);
	cmd = &ctlr->cmd[ctlr->cmdhi];
	memset(cmd, 0, sizeof *cmd);
	if(++ctlr->cmdhi == NCmdRing)
		ctlr->cmdhi = 0;
	ctlr->cmdpend = ctlr->cmdhi;
	cmd->hdr.type = HdrTypeCmd;
	cmd->hdr.flags = HdrFlagIrq;
	cmd->type = type;
	cmd->len = len;
	memmove(cmd->data, data, len);
	coherence();
	w32(ctlr, CmdWriteX, ctlr->cmdhi);
}

/*
 * Issue a command and wait for it.
 */
static int
cmd(Ctlr *ctlr, uchar cmd, void *data, uint len)
{
	if(r32(ctlr, CmdReadX) != r32(ctlr, CmdWriteX))
		tsleep(&ctlr->cmddone, isdone, ctlr, 1000);
	if(r32(ctlr, CmdReadX) != r32(ctlr, CmdWriteX)){
		iprint("cmd still pending\n");
		w32(ctlr, CmdReadX, r32(ctlr, CmdWriteX));
	}

	acmd(ctlr, cmd, data, len);
	tsleep(&ctlr->cmddone, isdone, ctlr, 1000);
	if(r32(ctlr, CmdReadX) != ctlr->cmdpend){
		iprint("ipw2200: cmd timeout\n");
		return -1;
	}
	return 0;
}

/*
 * Reset controller - okay for interrupts
 */
static int
reset(Ctlr* ctlr)
{
	int i, ntry;

	iprint("reset...");
	stopmaster(ctlr);

	w32(ctlr, Ctl, r32(ctlr, Ctl)|CtlInit);
	w32(ctlr, ReadInt, ReadIntInitHost);

	for(ntry=0; ntry<1000; ntry++){
		if(r32(ctlr, Ctl) & CtlClockReady)
			break;
		microdelay(200);
	}

	if(ntry == 1000){
		print("ipw2200: timeout\n");
		return -1;
	}

	w32(ctlr, Rst, r32(ctlr, Rst)|RstSwReset);

	microdelay(10);

	w32(ctlr, Ctl, r32(ctlr, Ctl)|CtlInit);

	w32(ctlr, AutoincAddr, 0);
	for(i=0; i<0xC000; i++)
		w32(ctlr, AutoincData, 0);

	for(i=0; i<Neeprom; i++)
		ctlr->eeprom[i] = eeread(ctlr, i);
	ctlr->mbps = 11;

	for(i=0; i<6; i+=2){
		ctlr->ea[i] = ctlr->eeprom[EeMac+i/2];
		ctlr->ea[i+1] = ctlr->eeprom[EeMac+i/2]>>8;
	}
		
	ctlr->cfg = defaultcfg;

	return 0;
}

/*
 * Stop controller - okay for interrupts.
 */
static void
stop(Ctlr *ctlr)
{
	stopmaster(ctlr);
	w32(ctlr, Rst, RstSwReset);
	
	ctlr->state &= ~Okay;
	wakeup(&ctlr->rxrendez);
	/* XXX free anything in rings */
}

/*
 * Stop master - okay for interrupts.
 */
static void
stopmaster(Ctlr *ctlr)
{
	int i;

	w32(ctlr, IntrMask, 0);
	w32(ctlr, Rst, RstStopMaster);
	for(i=0; i<5; i++){
		if(r32(ctlr, Rst) & RstMasterDisabled)
			break;
		microdelay(10);
	}
	if(i == 5)
		print("ipw2200: master timeout\n");
	
	w32(ctlr, Rst, r32(ctlr, Rst)|RstPrincetonReset);
	ctlr->state &= ~FwInited;
}

static void
printerror(Ctlr *ctlr)
{
	int i, j;
	u32int len, log, x[7];

	print("fw error log\n");
	log = r32(ctlr, ErrorLog);
	len = ir32(ctlr, log);
	w32(ctlr, AutoincAddr, log+4);
	for(i=0; i<len; i++){
		for(j=0; j<7; j++)
			x[j] = r32(ctlr, AutoincData);
		print("  %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux\n",
			x[0], x[1], x[2], x[3], x[4], x[5], x[6]);
	}
}

static void
interrupt(Ureg*, void *arg)
{
	int i;
	Block *bp;
	Ctlr *ctlr;
	Ether *edev;
	u32int cause, handled;

	edev = arg;
	ctlr = edev->ctlr;
	
	ilock(ctlr);
	cause = r32(ctlr, Intr);
	handled = 0;
	if(cause == 0 || cause == 0xFFFFFFFF){
		iunlock(ctlr);
		return;
	}

print("ipw interrupt %.8ux\n", cause);
//	w32(ctlr, IntrMask, 0);
	if(cause & (IntrFatalError|IntrParityError)){
		handled |=  IntrFatalError|IntrParityError;
		print("fatal error: rx %d %d\n", r32(ctlr, RxReadX), r32(ctlr, RxWriteX));
		for(i=0; i<NRdRing; i++){
			bp = ctlr->rb[i];
			print("\t%p %p %.8ux\n", bp, bp ? bp->wp : nil, r32(ctlr, RxBase+i*4));
		}
		iprint("ipw2200: fatal error %.8ux\n", cause);
		printerror(ctlr);
		stop(ctlr);
	}
	
	if(cause & IntrRadioOff){
iprint("R");
		handled |= IntrRadioOff;
		stop(ctlr);
	}
	
	if(cause & IntrFwInit){
		handled |= IntrFwInit;
		// ctlr->fwintr++;
iprint("F");
		ctlr->state |= FwInited;
		wakeup(&ctlr->fwinited);
	}
	
	if(cause & IntrCmd){
		handled |= IntrCmd;
		// ctlr->cmdintr++;
iprint("C");
		ctlr->cmdlo = r32(ctlr, CmdReadX);
		ctlr->state &= ~CmdPending;
		wakeup(&ctlr->cmddone);
	}
	
	if(cause & IntrTx){
		handled |= IntrTx;
iprint("T");
		// ctlr->txintr++;
	}
	
	if(cause & IntrRx){
		handled |= IntrRx;
iprint("R");
		// ctlr->rxintr++;
		wakeup(&ctlr->rxrendez);
	}
	
	w32(ctlr, Intr, cause);
	w32(ctlr, IntrMask, IntrDefault);
	if(cause & ~handled)
		iprint("ipw2200: did not handle %#.8ux\n", cause & ~handled);
	iunlock(ctlr);

	if(cause & IntrTx)
		transmit(edev);
}

/*
 * Abort scan - NOT okay for interrupts.
 */
static int
abortscan(Ctlr *ctlr)
{
	ctlr->state |= ScanAbort;
	return cmd(ctlr, CmdScanAbort, nil, 0);
}

static struct {
	int card;
	int mbps;
} rateconv[] = {
	10,	2,
	20,	4,
	55,	11,
	110,	22,
	13,	12,
	15,	18,
	5,	24,
	7,	36,
	9,	48,
	11,	72,
	1,	96,
	3,	108,
};

static int
rate2rate(int r)
{
	int i;
	
	for(i=0; i<nelem(rateconv); i++)
		if(rateconv[i].card == r)
			return rateconv[i].mbps;
	return 0;
}

/*
 * Load microcode - okay for interrupts but not expected.
 */
static int
loaducode(Ctlr *ctlr, void *uc, int size)
{
	int i;
	u16int *w;

	/* XXX pull out - common w/ stopmaster */
	w32(ctlr, Rst, r32(ctlr, Rst)|RstStopMaster);
	for(i=0; i<5; i++){
		if(r32(ctlr, Rst) & RstMasterDisabled)
			break;
		microdelay(10);
	}
	if(i == 5){
		print("ipw2200: master timeout\n");
		return -1;
	}

	/* XXX mighty big delays - sleep? */
	iw32(ctlr, 0x3000E0, 0x80000000);
	microdelay(5000);

	w32(ctlr, Rst, r32(ctlr, Rst)&~RstPrincetonReset);
	microdelay(5000);

	iw32(ctlr, 0x3000E0, 0);
	microdelay(1000);

	iw32(ctlr, 0x300004, 1);
	microdelay(1000);

	iw32(ctlr, 0x300004, 0);
	microdelay(1000);

	iw8(ctlr, 0x200000, 0x00);
	iw8(ctlr, 0x200000, 0x40);
	microdelay(1000);

	/* write microcode into adapter memory */
	for(w=uc; size>0; w++, size-=2)
		iw16(ctlr, 0x200010, *w);

	iw8(ctlr, 0x200000, 0x00);
	iw8(ctlr, 0x200000, 0x80);

	/* wait for a response in the microcode queue */
	for(i=0; i<100; i++){
		if(ir8(ctlr, 0x200000) & 1)
			break;
		microdelay(100);
	}
	if(i == 100){
		print("ipw2200: microcode intialization timeout: %#.8ux %#.2ux\n", 
			ir32(ctlr, 0x200000), ir8(ctlr, 0x200000));
		for(i=0; i<7; i++)
			print("%.8ux...", ir32(ctlr, 0x200004));
		return -1;
	}

	/* empty microcode queue */
	for(i=0; i<7; i++)
		ir32(ctlr, 0x200004);
	
	iw8(ctlr, 0x200000, 0x00);
	return 0;
}

/*
 * Load firmware - NOT okay for interrupts.
 */
#define LE32(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24)
static int
loadfirmware(Ctlr *ctlr, void *fw, int size)
{
	int i;
	u32int sentinel, ctl, src, dst, len, mlen;
	uchar *p, *end;

	if((ulong)fw&3){
		print("ipw2200: bad firmware alignment\n");
		return -1;
	}

	ilock(ctlr);
	ctlr->state &= ~FwInited;
	
	/* Tell the adapter where the command blocks are stored */
	iw32(ctlr, 0x3000A0, 0x27000);

	/*
	 * Store command blocks into adapter's internal memory using register
	 * indirections. The adapter will read the firmware image through DMA
	 * using information stored in command blocks.
	 */
	src = PADDR(fw);
	p = fw;
	end = p+size;
	w32(ctlr, AutoincAddr, 0x27000);
	while(p < end){
		dst = LE32(p);
		len = LE32(p+4);
		p += 8+len;
		src += 8;
		while(len > 0){
			mlen = MIN(len, CbMaxData);
			ctl = CbDefaultCtl | mlen;
			w32(ctlr, AutoincData, ctl);
			w32(ctlr, AutoincData, src);
			w32(ctlr, AutoincData, dst);
			w32(ctlr, AutoincData, ctl^src^dst);
			src += mlen;
			dst += mlen;
			len -= mlen;
		}
	}

	/* Write a fictive final command block (sentinel) */
	sentinel = r32(ctlr, AutoincAddr);
	w32(ctlr, AutoincData, 0);

	w32(ctlr, Rst, r32(ctlr, Rst)&~(RstMasterDisabled|RstStopMaster));
	
	/* Tell the adapter to start processing command blocks */
	iw32(ctlr, 0x3000A4, 0x540100);

	/* Wait until the adapter has processed all command blocks */
	for(i=0; i<400; i++){
		if(ir32(ctlr, 0x3000D0) >= sentinel)
			break;
		microdelay(100);
	}
	if(i == 400){
		iprint("ipw2200: loadfirmware timeout\n");
		iunlock(ctlr);
		return -1;
	}

	/* We're done with command blocks processing */
	iw32(ctlr, 0x3000A4, 0x540C00);

	/* Allow interrupts so we know when the firmware is inited */
	w32(ctlr, IntrMask, IntrDefault);

	/* Tell the adapter to initialize the firmware */
	w32(ctlr, Rst, 0);
	w32(ctlr, Ctl, r32(ctlr, Ctl)|CtlAllowStandby);

	/* Wait at most one second for firmware initialization to complete */
	iunlock(ctlr);
	tsleep(&ctlr->fwinited, isfwinited, ctlr, 1000);
	if(!(ctlr->state&FwInited)){
		iprint("ipw2200: fw not inited\n");
		return -1;
	}
	return 0;
}

static int scan(Ctlr*);
/*
 * Configure card - NOT okay for interrupts.
 */
static int
config(Ctlr *ctlr)
{
	int i;
	u32int data;
	Power power;
	Rates rates;
	WepKey wepkey;
	WKey *key;

	if(cmd(ctlr, CmdSetMacAddr, ctlr->ea, Eaddrlen))
		return -1;
	
	if(cmd(ctlr, CmdSetConfig, &ctlr->cfg, sizeof ctlr->cfg))
		return -1;
	
	data = PowerModeCam;
	if(cmd(ctlr, CmdSetPower, &data, sizeof data))
		return -1;
	
	data = ctlr->rtsthreshold;
	if(cmd(ctlr, CmdSetRtsThreshold, &data, sizeof data))
		return -1;

	if(ctlr->opmode == ModeIbss){
		power.nchan = 11;
		for(i=0; i<11; i++){
			power.chan[i].chan = i+1;
			power.chan[i].power = PowerMax;
		}

		power.mode = Mode11b;
		if(cmd(ctlr, CmdSetTxPower, &power, sizeof power))
			return -1;
			
		power.mode = Mode11g;
		if(cmd(ctlr, CmdSetTxPower, &power, sizeof power))
			return -1;
	}

	rates.mode = Mode11g;
	rates.type = RatesSupported;
	rates.nrates = nelem(rates11g);
	memmove(rates.rates, rates11g, sizeof rates11g);
	if(cmd(ctlr, CmdSetRates, &rates, sizeof rates))
		return -1;

	switch(ctlr->id){
	case 0x4220:
		break;
	default:
		rates.mode = Mode11a;
		rates.type = RatesSupported;
		rates.nrates = nelem(rates11a);
		memmove(rates.rates, rates11a, sizeof rates11a);
		if(cmd(ctlr, CmdSetRates, &rates, sizeof rates))
			return -1;
		break;
	}
	
	if(ctlr->essidlen){
		if(cmd(ctlr, CmdSetEssid, ctlr->essid, ctlr->essidlen))
			return -1;
	}

	data = 0;
	if(!waserror()){
		randomread(&data, sizeof data);
		poperror();
	}
	if(cmd(ctlr, CmdSetIV, &data, sizeof data))
		return -1;

	if(ctlr->state & Wep){
		for(i=0; i<Wnwep; i++){
			key = &ctlr->keys[i];
			wepkey.cmd = WepkeySetKey;
			wepkey.idx = i;
			wepkey.len = key->len;
			wepkey.seq = 0;
			memset(wepkey.key, 0, sizeof wepkey.key);
			memmove(wepkey.key, key->key, key->len);
			if(cmd(ctlr, CmdSetWepKey, &wepkey, sizeof wepkey))
				return -1;
		}
	}

	if(cmd(ctlr, CmdEnable, nil, 0))
		return -1;

	/*
	 * Must start scan immediately -- card misbehaves if we wait.
	 */
	scan(ctlr);
	return 0;
}

static void replenish(Ctlr*);

/*
 * Initialize controller.
 */
static int
init(Ctlr *ctlr)
{
	int i;
	Block *bp;

iprint("init...");
	stop(ctlr);
	if(reset(ctlr))
		goto error;
	if(loadfirmware(ctlr, ipw2200_bootcode+FwHeader, ipw2200_bootlen-FwHeader))
		goto error;
	if(loaducode(ctlr, ipw2200_ucode_bsscode+FwHeader, ipw2200_ucode_bsslen-FwHeader))
		goto error;

	stopmaster(ctlr);
	
	ctlr->cmdlo = 0;
	ctlr->cmdhi = 0;
	w32(ctlr, CmdBase, PADDR(ctlr->cmd));
	w32(ctlr, CmdSize, NCmdRing);
	w32(ctlr, CmdReadX, ctlr->cmdlo);
	w32(ctlr, CmdWriteX, ctlr->cmdhi);

	ctlr->txlo = 0;
	ctlr->txhi = 0;
	w32(ctlr, Tx1Base, PADDR(ctlr->td));
	w32(ctlr, Tx1Size, NTdRing);
	w32(ctlr, Tx1ReadX, ctlr->txlo);
	w32(ctlr, Tx1WriteX, ctlr->txhi);

	w32(ctlr, Tx2Base, PADDR(ctlr->td));
	w32(ctlr, Tx2Size, NTdRing);
	w32(ctlr, Tx2ReadX, 0);
	w32(ctlr, Tx2WriteX, 0);

	w32(ctlr, Tx3Base, PADDR(ctlr->td));
	w32(ctlr, Tx3Size, NTdRing);
	w32(ctlr, Tx3ReadX, 0);
	w32(ctlr, Tx3WriteX, 0);

	w32(ctlr, Tx4Base, PADDR(ctlr->td));
	w32(ctlr, Tx4Size, NTdRing);
	w32(ctlr, Tx4ReadX, 0);
	w32(ctlr, Tx4WriteX, 0);

	ctlr->rxlo = 0;
	ctlr->rxhi = 0;
	replenish(ctlr);
	for(i=0; i<NRdRing; i++){
		if(bp = ctlr->rb[i])
			w32(ctlr, RxBase+i*4, PADDR(bp->wp));
		else
			w32(ctlr, RxBase+i*4, 0);
	}

	w32(ctlr, RxReadX, ctlr->rxlo);
	w32(ctlr, RxWriteX, ctlr->rxhi);

	if(loadfirmware(ctlr, ipw2200_bsscode, ipw2200_bsslen))
		goto error;

	ctlr->state &= ~(Scanning|ScanComplete|ScanAbort|Associated);
	ctlr->state |= Okay;

	if(config(ctlr))
		goto error;

	return 0;

error:
	stop(ctlr);
	return -1;
}

/*
 * NOT okay for interrupts
 */
static int
dissociate(Ctlr *ctlr)
{
	Assoc assoc;

	memset(&assoc, 0, sizeof assoc);
	assoc.type = Dissociate;
	return cmd(ctlr, CmdAssoc, &assoc, sizeof assoc);
}

static int
setchannel(Ctlr *ctlr, int channel)
{
	Scan *scan;

	scan = &ctlr->scan;
	memset(scan, 0, sizeof(Scan));
	memset(scan->type, ScanPassive, sizeof scan->type);
	scan->passive = 2000;
	scan->channels[0] = 1 | Chan2Ghz;
	scan->channels[1] = channel;
	acmd(ctlr, CmdScan, scan, sizeof(Scan));
	return 0;
}
	
static int
associate(Ctlr *ctlr)
{
	Assoc *assoc;

	memset(&ctlr->assoc, 0, sizeof ctlr->assoc);
	assoc = &ctlr->assoc;
	assoc->mode = Mode11g;
	assoc->capinfo = 1; // CapEss;
	assoc->chan = 1;	// channel 
	/* XXX set assoc->bssid! */
	acmd(ctlr, CmdAssoc, assoc, sizeof(Assoc));
	return 0;
}

static int
scan(Ctlr *ctlr)
{
	int i;
	uchar *p;
	Scan *scan;

	memset(&ctlr->scan, 0, sizeof ctlr->scan);
	scan = &ctlr->scan;

	if(ctlr->essidlen){
		scan->bdirected = 100;	/* XXX ctlr->dwelltime */
		memset(scan->type, ScanBDirected, sizeof scan->type);
	}else{
		scan->broadcast = 100;
		memset(scan->type, ScanBroadcast, sizeof scan->type);
	}

	scan->passive = 20;
//	scan->directed = 20;
	scan->bdirected = 20;
	scan->broadcast = 20;

	p = scan->channels;
//	*p++ = Chan5Ghz | 0;
	*p++ = Chan2Ghz | 11;
	for(i=1; i<=11; i++)
		*p++ = i;

	acmd(ctlr, CmdScan, scan, sizeof(Scan));
	return 0;
}

static void
filltd(Td *td, Block *bp)
{
	/* copy ethernet header on bp to Td.wqhdr */
	
	
	/* fill out td */
	memset(td, 0, sizeof *td);
	td->hdr.type = HdrTypeData;
	td->hdr.flags = HdrFlagIrq;
	td->len = BLEN(bp);
	td->flags = TdFlagNeedAck;
	td->nseg = 1;
	td->segaddr[0] = PADDR(bp->rp);
	td->seglen[0] = BLEN(bp);
}

static void
transmit(Ether* edev)
{
	uint x;
	Block *bp;
	Ctlr *ctlr;

	ctlr = edev->ctlr;
	ilock(&ctlr->tlock);
	
	/*
	 * Free any completed packets.
	 */
	x = ctlr->txlo;
	while(x != r32(ctlr, Tx1ReadX)){
		bp = ctlr->tb[x];
		ctlr->tb[x] = nil;
		freeb(bp);
iprint("!");
		if(++x == NTdRing)
			x = 0;
	}
	ctlr->txlo = x;

	/*
	 * Try to fill the ring back up.
	 */
	x = ctlr->txhi;
	while((x+1)%NTdRing != r32(ctlr, Tx1ReadX)){
		if((bp = qget(edev->oq)) == nil)
			break;
		ctlr->tb[x] = bp;
		filltd(&ctlr->td[x], bp);
iprint(">");
		if(++x == NTdRing)
			x = 0;
	}
	ctlr->txhi = x;
	w32(ctlr, Tx1WriteX, x);
	iunlock(&ctlr->tlock);
}

static Lock rblock;
static Block *rbpool;

static Block*
rballoc(void)
{
	Block *bp;
	
	ilock(&rblock);
	if((bp = rbpool) != nil){
		rbpool = rbpool->next;
		bp->next = nil;
	}
	iunlock(&rblock);
	return iallocb(Rbsz);
}

static void
rbfree(Block *bp)
{
	bp->rp = bp->lim - Rbsz;
	bp->wp = bp->rp;
	
	ilock(&rblock);
	bp->next = rbpool;
	rbpool = bp;
	iunlock(&rblock);
}

static void
replenish(Ctlr *ctlr)
{
	uint x;
	Block *bp;

	print("replenish %d %d\n", ctlr->rxhi, r32(ctlr, RxReadX));
	x = ctlr->rxhi;
	while((x+1)%NRdRing != ctlr->rxlo){
		if((bp = ctlr->rb[x]) == nil){
			if((bp = rballoc()) == nil)
				break;
iprint("#");
			ctlr->rb[x] = bp;
		}
		w32(ctlr, RxBase+x*4, PADDR(bp->wp));
		if(++x == NRdRing)
			x = 0;
	}
	ctlr->rxhi = x;
	print("replenish => %d\n", x);
	w32(ctlr, RxWriteX, x);
}

static void
rxnotify(Ctlr *ctlr, Notify *n)
{
	NotifyScanChannel *chan;
	NotifyScanComplete *done;
	NotifyAuth *auth;
	NotifyAssoc *assoc;

print("notify: subtype=%.2ux flags=%.2ux size=%d\n", n->type, n->flags, n->len);
printhex(n+1, n->len);
	switch(n->type){
	case NotifyTypeAssociation:
		assoc = (NotifyAssoc*)(n+1);
		print("assoc %d %d\n", assoc->state, assoc->status);
		switch(assoc->state){
		case Authenticated:
			print("assoc auth\n");
			break;
		case Associated:
			print("assoc yes\n");
			break;
		case Dissociated:
			print("assoc no\n");
			break;
		}
		break;
		
	case NotifyTypeAuth:
		auth = (NotifyAuth*)(n+1);
		switch(auth->state){
		case Authenticated:
			print("authenticated\n");
			break;
		case Deauthenticated:
			print("deauthenticated\n");
			break;
		default:
			print("auth state %d\n", auth->state);
			break;
		}
		break;
	
	case NotifyTypeScanChannel:
		chan = (NotifyScanChannel*)(n+1);
		print("scanning %d\n", chan->nchan);
		break;

	case NotifyTypeScanComplete:
		done = (NotifyScanComplete*)(n+1);
		USED(done);
		USED(ctlr);
	//	setchan(ctlr, ctlr->chan);
		print("scan done\n");
		break;
	
	case NotifyTypeBeacon:
	case NotifyTypeCalibration:
	case NotifyTypeNoise:
		print("notify %d\n", n->type);
		break;
	}
}

static int
rxframe(Ether *edev, Block *bp)
{	
	int i;
	uint *x;
	Hdr *h;
	Frame *f;
	WFrame *wf;
	
	h = (Hdr*)bp->rp;
	f = (Frame*)(h+1);
	print("rx len=%d chan=%d rssi=%d\n", 
		f->len, f->chan, f->rssi);
	if((uchar*)(f+1)+f->len > bp->lim){
		print("pkt too long\n");
		return 0;
	}
	wf = (WFrame*)(f+1);
	bp->rp = (uchar*)(wf+1);
	x = (uint*)wf;
	print("wframe:");
	for(i=0; i<8; i++)
		print(" %.8ux", x[i]);
	print("\ndata: ");
	x = (uint*)bp->rp;
	for(i=0; i<8; i++)
		print(" %.8ux", x[i]);
	print("\n");
	etheriq(edev, bp, 1);
	return 1;
}

static void
rxproc(void *arg)
{
	int avail;
	uint x, end;
	Block *bp;
	Ctlr *ctlr;
	Ether *edev;
	Hdr *hdr;

	edev = arg;
	ctlr = edev->ctlr;
	replenish(ctlr);
	for(;;){
	//	ctlr->rxsleep++;
		sleep(&ctlr->rxrendez, rxwaiting, ctlr);
		if(!(ctlr->state&Okay))
			continue;
		ctlr->rxwaiting = 0;
		
		x = ctlr->rxlo;
		end = r32(ctlr, RxReadX);

		if(0 <= end && end < NRdRing)
		while(x != end){
			bp = ctlr->rb[x];
			if(bp == nil){	/* oops! */
				iprint("?");
				break;
			}
iprint("<");
			hdr = (Hdr*)bp->rp;
			switch(hdr->type){
			case HdrTypeNotify:
				rxnotify(ctlr, (Notify*)(hdr+1));
				break;
			case HdrTypeFrame:
			//	ctlr->rxpackets++;
				if(rxframe(edev, bp))
					ctlr->rb[x] = nil;
				break;
			}
			if(ctlr->rb[x]){
				rbfree(ctlr->rb[x]);
				ctlr->rb[x] = nil;
			}
			w32(ctlr, RxBase+x*4, 0);
			if(++x == NRdRing)
				x = 0;
		}
print("rxproc: lo %d nlo %d end %d hi %d\n", ctlr->rxlo, x, end, ctlr->rxhi);
print("rxproc: card %d %d\n", r32(ctlr, RxReadX), r32(ctlr, RxWriteX));
		ctlr->rxlo = x;
		
		avail = (ctlr->rxhi+NRdRing - end) % NRdRing;
		if(avail < NRdRing/2)
			replenish(ctlr);
	}
}

static void
attach(Ether* edev)
{
	int i;
	Ctlr *ctlr;

	ctlr = edev->ctlr;
	qlock(&ctlr->alock);
	if(ctlr->alloc){
		qunlock(&ctlr->alock);
		return;
	}
	
	if(reset(ctlr))
		goto error;

	ctlr->rtsthreshold = 0x900;

/*
	ctlr->opmode = Station
	ctlr->xstate = XXX;
	caps = XXX;
 */

	if(init(ctlr))
		goto error;

	kproc("ipw2200rxproc", rxproc, edev);
	ctlr->alloc = 1;
	qunlock(&ctlr->alock);
	return;

error:
	stop(ctlr);
	for(i=0; i<NRdRing; i++){
		if(ctlr->rb[i]){
			freeb(ctlr->rb[i]);
			ctlr->rb[i] = nil;
		}
	}
	qunlock(&ctlr->alock);
}

static void
shutdown(Ether* ether)
{
	Ctlr *ctlr;

iprint("shutdown...");
	ctlr = ether->ctlr;
	qlock(&ctlr->alock);
	if(!ctlr->alloc){
		qunlock(&ctlr->alock);
		return;
	}
	stop(ctlr);
	free(ctlr->cmd);
	free(ctlr->td);
/*	free(ctlr->rd); */
	ctlr->alloc = 0;
	qunlock(&ctlr->alock);
}

static long
ifstat(Ether* edev, void* a, long n, ulong offset)
{
	char *p;
	int i, l, m, x;
	Ctlr *ctlr;
	
	ctlr = edev->ctlr;
	if(ctlr == nil)
		error(Enonexist);
	if(n == 0)
		return 0;

	m = READSTR*2;
	p = malloc(m);

iprint("ctlr %p.", ctlr);
	l = 0;
	l += snprint(p+l, m-l, "CurTxRate: %ud\n", r32(ctlr, CurTxRate));
	l += snprint(p+l, m-l, "ctlr cmd: %ud-%ud\n", ctlr->cmdlo, ctlr->cmdhi);
	x = r32(ctlr, CmdReadX);
	if(x < 0 || x >= NCmdRing)
		x = 0;
iprint("x %d.", x);
iprint("addr %p.", &ctlr->cmd[x]);
iprint("t %d.", ctlr->cmd[x].hdr.type);
iprint("t %d.", ctlr->cmd[x].type);
	l += snprint(p+l, m-l, "card cmd: %ud-%ud (current %ud %ud)\n", x, r32(ctlr, CmdWriteX), ctlr->cmd[x].hdr.type, ctlr->cmd[x].type);
	l += snprint(p+l, m-l, "ctlr tx1: %ud-%ud\n", ctlr->txlo, ctlr->txhi);
	l += snprint(p+l, m-l, "card tx1: %ud-%ud\n", r32(ctlr, Tx1ReadX), r32(ctlr, Tx1WriteX));
	l += snprint(p+l, m-l, "ctlr rx: %ud-%ud\n", ctlr->rxlo, ctlr->rxhi);
	l += snprint(p+l, m-l, "card rx: %ud-%ud\n", r32(ctlr, RxReadX), r32(ctlr, RxWriteX));
	for(i=0; i<NRdRing; i++)
		l += snprint(p+l, m-l, "rxbase%d: %#.8ux\n", i, r32(ctlr, RxBase+i*4));
	l += snprint(p+l, m-l, "ctlr rx: %ud-%ud\n", ctlr->rxlo, ctlr->rxhi);
	l += snprint(p+l, m-l, "ctlr tx: %ud-%ud\n", ctlr->txlo, ctlr->txhi);


	l += snprint(p+l, m-l, "rom:");
	for(i = 0; i < Neeprom; i++){
		if(i && (i&7)==0)
			l += snprint(p+l, m-l, "\n    ");
		l += snprint(p+l, m-l, " %4.4ux", ctlr->eeprom[i]);
	}
	l += snprint(p+l, m-l, "\n");

	USED(l);
	n = readstr(offset, a, n, p);
	free(p);
	return n;
}

enum
{
	CMabortscan,
	CMassociate,
	CMchannel,
	CMconfig,
	CMdissociate,
	CMessid,
	CMinit,
	CMnop,
	CMrssi,
	CMscan,
	CMstop,
};

static Cmdtab ipw2200msg[] =
{
	CMabortscan,	"abortscan",	1,
	CMassociate,	"associate",	1,
	CMchannel,	"channel",		2,
	CMconfig,		"config",		1,
	CMdissociate,	"dissociate",	1,
	CMessid,		"essid",		2,
	CMinit,		"init",		1,
	CMnop,		"nop",		0,
	CMrssi,		"rssi",		2,
	CMscan,		"scan",		1,
	CMstop,		"stop",		1,
};

static long
wctl(Ether* edev, void* buf, long n)
{
	u32int x;
	Cmdbuf *cb;
	Cmdtab *ct;
	Ctlr *ctlr;
	
	ctlr = edev->ctlr;
	if(ctlr == nil)
		error(Enonexist);

	cb = parsecmd(buf, n);
	if(waserror()){
		free(cb);
		nexterror();
	}
	ct = lookupcmd(cb, ipw2200msg, nelem(ipw2200msg));
	switch(ct->index){
	case CMabortscan:
		if(abortscan(ctlr))
			error("error in abortscan");
		break;
	case CMassociate:
		if(associate(ctlr))
			error("error in associate");
		break;
	case CMchannel:
		if(setchannel(ctlr, atoi(cb->f[1])))
			error("error in setchannel");
		break;
	case CMconfig:
		if(config(ctlr))
			error("error in config");
		break;
	case CMdissociate:
		if(dissociate(ctlr))
			error("error dissociating");
		break;
	case CMessid:
		if(strlen(cb->f[1]) >= sizeof ctlr->essid)
			error("essid too long");
		strcpy(ctlr->essid, cb->f[1]);
		ctlr->essidlen = strlen(ctlr->essid);
		iprint("essid %q\n", ctlr->essid);
		if(cmd(ctlr, CmdSetEssid, ctlr->essid, ctlr->essidlen))
			error("error setting essid");
		break;
	case CMinit:
		if(init(ctlr))
			error("error initing");
		break;
	case CMnop:
		break;
	case CMrssi:
		x = strtol(cb->f[1], 0, 0);
		acmd(ctlr, CmdSetSensitivity, &x, sizeof x);
		break;
	case CMscan:
		if(scan(ctlr))
			error("error scan");
		break;
	case CMstop:
		stop(ctlr);
		break;
	}
	free(cb);
	poperror();
	return n;
}

static void
ipw2200pci(void)
{
	int id;
	void *mem;
	Ctlr *ctlr;
	Pcidev *p;
	
	p = nil;
	while(p = pcimatch(p, 0, 0)){
		if(p->ccrb != 0x02)
			continue;
		id = (p->did<<16)|p->vid;
		switch(id){
		default:
			continue;
		case (0x4220<<16)|0x8086:	/* Intel 2200BG */
		case (0x4223<<16)|0x8086:	/* Intel 2915ABG */
		case (0x4224<<16)|0x8086:	/* Intel 2915ABG */
			break;
		}

print("found %ux\n", id);
		mem = vmap(p->mem[0].bar&~0x0F, p->mem[0].size);
		if(mem == nil){
			print("etheripw2200: can't map %#.8lux\n", p->mem[0].bar);
			continue;
		}

		ctlr = malloc(sizeof(Ctlr));
		if(ctlr == nil)
			return;
		ctlr->port = p->mem[0].bar&~0xF;
		ctlr->pcidev = p;
		ctlr->id = id;
		ctlr->mmio = mem;
		
		if(reset(ctlr)){
			free(ctlr);
			continue;
		}
		pcisetbme(p);

		if(ctlrhead != nil)
			ctlrtail->next = ctlr;
		else
			ctlrhead = ctlr;
		ctlrtail = ctlr;
	}
}

static int
ipw2200pnp(Ether* edev)
{
	Ctlr *ctlr;
	uchar ea[Eaddrlen];
	
	if(ctlrhead == nil)
		ipw2200pci();
	
	/*
	 * Any adapter matches if no edev->port is supplied,
	 * otherwise the ports must match.
	 */
	for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
		if(ctlr->active)
			continue;
		if(edev->port == 0 || edev->port == ctlr->port){
			ctlr->active = 1;
			break;
		}
	}
	if(ctlr == nil)
		return -1;

	edev->ctlr = ctlr;
	edev->port = ctlr->port;
	edev->irq = ctlr->pcidev->intl;
	edev->tbdf = ctlr->pcidev->tbdf;
	edev->mbps = ctlr->mbps;

	/*
	 * Check if the adapter's station address is to be overridden.
	 * If not, read it from the EEPROM and set in ether->ea prior to
	 * loading the station address in the hardware.
	 */
	memset(ea, 0, Eaddrlen);
	if(memcmp(ea, edev->ea, Eaddrlen) == 0)
		memmove(edev->ea, ctlr->ea, Eaddrlen);

	/*
	 * Linkage to the generic ethernet driver.
	 */
	edev->attach = attach;
	edev->transmit = transmit;
	edev->interrupt = interrupt;
	edev->ifstat = ifstat;
	edev->ctl = wctl;
	edev->shutdown = shutdown;
	edev->arg = edev;

	return 0;
}

void
etheripw2200link(void)
{
	addethercard("ipw2200", ipw2200pnp);
}


Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].