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