@@ 206,7 206,7 @@ struct Endpt {
int data01; /* 0=DATA0, 1=DATA1 */
uchar eof;
ulong csp;
- uchar isopen; /* ep operations forbidden on open endpoints */
+/* uchar isopen; ep operations forbidden on open endpoints */
uchar mode; /* OREAD, OWRITE, ORDWR */
uchar nbuf; /* number of buffers allowed */
uchar iso;
@@ 645,6 645,7 @@ eptactivate(Endpt *e)
{
ilock(&activends);
if(e->active == 0){
+ XPRINT("activate 0x%p\n", e);
e->active = 1;
e->activef = activends.f;
activends.f = e;
@@ 661,6 662,7 @@ eptdeactivate(Endpt *e)
ilock(&activends);
if(e->active){
e->active = 0;
+ XPRINT("deactivate 0x%p\n", e);
for(l = &activends.f; *l != e; l = &(*l)->activef)
if(*l == nil){
iunlock(&activends);
@@ 808,7 810,7 @@ schedendpt(Endpt *e)
return 0;
ub = &ubus;
- if (e->isopen){
+ if (e->active){
return -1;
}
e->off = 0;
@@ 1445,7 1447,7 @@ usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
/*
* Top level directory contains the name of the device.
*/
- if(c->qid.path == Qtopdir){
+ if(QID(c->qid) == Qtopdir){
if(s == DEVDOTDOT){
mkqid(&q, Qtopdir, 0, QTDIR);
devdir(c, q, "#U", 0, eve, 0555, dp);
@@ 1544,6 1546,7 @@ usbopen(Chan *c, int omode)
f = 0;
if(QID(c->qid) == Qnew){
d = usbnewdevice();
+ XPRINT("usbopen, new dev 0x%p\n", d);
if(d == nil) {
XPRINT("usbopen failed (usbnewdevice)\n");
error(Enodev);
@@ 1553,8 1556,10 @@ usbopen(Chan *c, int omode)
f = 1;
}
- if(c->qid.path < Q3rd)
+ if(QID(c->qid) < Q3rd){
+ XPRINT("usbopen, devopen < Q3rd\n");
return devopen(c, omode, nil, 0, usbgen);
+ }
qlock(&usbstate);
if(waserror()){
@@ 1570,24 1575,26 @@ usbopen(Chan *c, int omode)
d->busy = 1;
if(!f)
incref(d);
+ XPRINT("usbopen, Qctl 0x%p\n", d);
break;
default:
d = usbdevice(c);
s = QID(c->qid) - Qep0;
+ XPRINT("usbopen, default 0x%p, %d\n", d, s);
if(s >= 0 && s < nelem(d->ep)){
Endpt *e;
if((e = d->ep[s]) == nil) {
XPRINT("usbopen failed (endpoint)\n");
error(Enodev);
}
+ XPRINT("usbopen: dev 0x%p, ept 0x%p\n", d, e);
if(schedendpt(e) < 0){
- if (e->isopen)
- error("can't schedule USB endpoint, isopen");
+ if (e->active)
+ error("can't schedule USB endpoint, active");
else
error("can't schedule USB endpoint");
}
- e->isopen++;
eptactivate(e);
}
incref(d);
@@ 1625,27 1632,17 @@ void
usbclose(Chan *c)
{
Udev *d;
- int s;
- if(c->qid.type == QTDIR || c->qid.path < Q3rd)
+ if(c->qid.type == QTDIR || QID(c->qid) < Q3rd)
return;
qlock(&usbstate);
d = usbdevice(c);
- s = QID(c->qid) - Qep0;
- if(s >= 0 && s < nelem(d->ep)){
- Endpt *e;
- if(e = d->ep[s]) {
- if (e->isopen == 1){
- e->isopen = 0;
- eptdeactivate(e);
- unschedendpt(e);
- }
- }
- }
if(QID(c->qid) == Qctl)
d->busy = 0;
- if(c->flag & COPEN)
+ if(c->flag & COPEN){
+ XPRINT("usbclose: freedev 0x%p\n", d);
freedev(d);
+ }
qunlock(&usbstate);
}
@@ 1858,13 1855,12 @@ epstatus(char *s, int n, Endpt *e, int i)
int l;
l = 0;
- l += snprint(s+l, n-l, "%2d %#6.6lux %10lud bytes %10lud blocks",
+ l += snprint(s+l, n-l, "%2d %#6.6lux %10lud bytes %10lud blocks\n",
i, e->csp, e->nbytes, e->nblocks);
if (e->iso && e->toffset){
- l += snprint(s+l, n-l, " %6d buffered %10lud offset %19lld time",
- e->buffered, e->toffset, e->time);
+ l += snprint(s+l, n-l, "bufsize %6d buffered %6d offset %10lud time %19lld\n",
+ e->maxpkt, e->buffered, e->toffset, e->time);
}
- l += snprint(s+l, n-l, "\n");
return l;
}
@@ 2103,7 2099,7 @@ usbwrite(Chan *c, void *a, long n, vlong offset)
qunlock(&usbstate);
nexterror();
}
- if (e->isopen)
+ if (e->active)
error(Eperm);
if(strcmp(fields[2], "bulk") == 0){
Ctlr *ub;
@@ 27,67 27,10 @@
#include "../port/netif.h"
#include "etherif.h"
-#define DEBUG if(1)print
+#define DEBUG if(0){}else print
#define SEEKEYS 0
-typedef struct Ctlr Ctlr;
-typedef struct Wltv Wltv;
-typedef struct WFrame WFrame;
-typedef struct Stats Stats;
-typedef struct WStats WStats;
-typedef struct WKey WKey;
-
-struct WStats
-{
- ulong ntxuframes; // unicast frames
- ulong ntxmframes; // multicast frames
- ulong ntxfrags; // fragments
- ulong ntxubytes; // unicast bytes
- ulong ntxmbytes; // multicast bytes
- ulong ntxdeferred; // deferred transmits
- ulong ntxsretries; // single retries
- ulong ntxmultiretries; // multiple retries
- ulong ntxretrylimit;
- ulong ntxdiscards;
- ulong nrxuframes; // unicast frames
- ulong nrxmframes; // multicast frames
- ulong nrxfrags; // fragments
- ulong nrxubytes; // unicast bytes
- ulong nrxmbytes; // multicast bytes
- ulong nrxfcserr;
- ulong nrxdropnobuf;
- ulong nrxdropnosa;
- ulong nrxcantdecrypt;
- ulong nrxmsgfrag;
- ulong nrxmsgbadfrag;
- ulong end;
-};
-
-struct WFrame
-{
- ushort sts;
- ushort rsvd0;
- ushort rsvd1;
- ushort qinfo;
- ushort rsvd2;
- ushort rsvd3;
- ushort txctl;
- ushort framectl;
- ushort id;
- uchar addr1[Eaddrlen];
- uchar addr2[Eaddrlen];
- uchar addr3[Eaddrlen];
- ushort seqctl;
- uchar addr4[Eaddrlen];
- ushort dlen;
- uchar dstaddr[Eaddrlen];
- uchar srcaddr[Eaddrlen];
- ushort len;
- ushort dat[3];
- ushort type;
-};
-
// Lucent's Length-Type-Value records to talk to the wavelan.
// most operational parameters are read/set using this.
enum
@@ 191,6 134,9 @@ enum
WR_Data0 = 0x36,
WR_Data1 = 0x38,
+ WR_PciCor = 0x26,
+ WR_PciHcr = 0x2E,
+
// Frame stuff
WF_Err = 0x0003,
@@ 212,9 158,62 @@ enum
};
-#define csr_outs(ctlr,r,arg) outs((ctlr)->iob+(r),(arg))
-#define csr_ins(ctlr,r) ins((ctlr)->iob+(r))
-#define csr_ack(ctlr,ev) outs((ctlr)->iob+WR_EvAck,(ev))
+typedef struct Ctlr Ctlr;
+typedef struct Wltv Wltv;
+typedef struct WFrame WFrame;
+typedef struct Stats Stats;
+typedef struct WStats WStats;
+typedef struct WKey WKey;
+
+struct WStats
+{
+ ulong ntxuframes; // unicast frames
+ ulong ntxmframes; // multicast frames
+ ulong ntxfrags; // fragments
+ ulong ntxubytes; // unicast bytes
+ ulong ntxmbytes; // multicast bytes
+ ulong ntxdeferred; // deferred transmits
+ ulong ntxsretries; // single retries
+ ulong ntxmultiretries; // multiple retries
+ ulong ntxretrylimit;
+ ulong ntxdiscards;
+ ulong nrxuframes; // unicast frames
+ ulong nrxmframes; // multicast frames
+ ulong nrxfrags; // fragments
+ ulong nrxubytes; // unicast bytes
+ ulong nrxmbytes; // multicast bytes
+ ulong nrxfcserr;
+ ulong nrxdropnobuf;
+ ulong nrxdropnosa;
+ ulong nrxcantdecrypt;
+ ulong nrxmsgfrag;
+ ulong nrxmsgbadfrag;
+ ulong end;
+};
+
+struct WFrame
+{
+ ushort sts;
+ ushort rsvd0;
+ ushort rsvd1;
+ ushort qinfo;
+ ushort rsvd2;
+ ushort rsvd3;
+ ushort txctl;
+ ushort framectl;
+ ushort id;
+ uchar addr1[Eaddrlen];
+ uchar addr2[Eaddrlen];
+ uchar addr3[Eaddrlen];
+ ushort seqctl;
+ uchar addr4[Eaddrlen];
+ ushort dlen;
+ uchar dstaddr[Eaddrlen];
+ uchar srcaddr[Eaddrlen];
+ ushort len;
+ ushort dat[3];
+ ushort type;
+};
struct WKey
{
@@ 252,6 251,7 @@ struct Wltv
struct Stats
{
ulong nints;
+ ulong ndoubleint;
ulong nrx;
ulong ntx;
ulong ntxrq;
@@ 303,10 303,75 @@ struct Ctlr
int ctlrno;
+ /* for PCI-based devices */
+ Ctlr *next;
+ ushort *mmb;
+ int active;
+ Pcidev *pcidev;
+
Stats;
WStats;
};
+/*
+ * 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].
+ */
+static void
+csr_outs(Ctlr *ctlr, int reg, ushort arg)
+{
+ if(ctlr->mmb)
+ ctlr->mmb[reg] = arg;
+ else
+ outs(ctlr->iob+reg, arg);
+}
+
+static 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.
@@ 343,16 408,36 @@ w_cmd(Ctlr *ctlr, ushort cmd, ushort arg)
if(csr_ins(ctlr, WR_EvSts)&WCmdEv)
break;
if(i==WTmOut){
- print("#l%d: execing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_EvSts));
- return -1;
+ /*
+ * 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) || (rc&WResSts)){
+ 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;
}
@@ 398,7 483,7 @@ w_inltv(Ctlr* ctlr, Wltv* ltv)
return -1;
}
if(ltv->len > 0)
- inss((ctlr)->iob+(WR_Data1), <v->val, ltv->len-1);
+ csr_inss(ctlr, WR_Data1, <v->val, ltv->len-1);
return 0;
}
@@ 408,7 493,7 @@ w_outltv(Ctlr* ctlr, Wltv* ltv)
{
if(w_seek(ctlr,ltv->type, 0, 1))
return;
- outss((ctlr)->iob+(WR_Data1), ltv, ltv->len+1);
+ csr_outss(ctlr, WR_Data1, ltv, ltv->len+1);
w_cmd(ctlr, WCmdAccWr, ltv->type);
}
@@ 486,7 571,7 @@ w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len)
DEBUG("wavelan: w_read: seek failed");
return 0;
}
- inss((ctlr)->iob+(WR_Data1), buf, len/2);
+ csr_inss(ctlr, WR_Data1, buf, len/2);
return len;
}
@@ 502,7 587,7 @@ w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len)
return 0;
}
- outss((ctlr)->iob+(WR_Data0), buf, len/2);
+ csr_outss(ctlr, WR_Data0, buf, len/2);
csr_outs(ctlr, WR_Data0, 0xdead);
csr_outs(ctlr, WR_Data0, 0xbeef);
@@ 763,10 848,8 @@ w_intr(Ether *ether)
return;
}
- csr_outs(ctlr, WR_IntEna, 0);
rc = csr_ins(ctlr, WR_EvSts);
- csr_ack(ctlr, ~WEvs); // Not interested on them
-
+ csr_ack(ctlr, ~WEvs); // Not interested in them
if (rc & WRXEv){
w_rxdone(ether);
csr_ack(ctlr, WRXEv);
@@ 797,8 880,6 @@ w_intr(Ether *ether)
ctlr->nidrop++;
csr_ack(ctlr, WIDropEv);
}
-
- w_intena(ctlr);
w_txstart(ether);
}
@@ 821,7 902,7 @@ w_timer(void* arg)
continue;
ctlr->ticks++;
- ilock(&ctlr->Lock);
+ ilock(ctlr);
// Seems that the card gets frames BUT does
// not send the interrupt; this is a problem because
@@ 838,10 919,10 @@ w_timer(void* arg)
// 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 (csr_ins(ctlr, WR_EvSts)&WEvs){
+ ctlr->tickintr++;
+ w_intr(ether);
+ }
if ((ctlr->ticks % 10) == 0) {
if (ctlr->txtmout && --ctlr->txtmout == 0){
@@ 856,7 937,7 @@ w_timer(void* arg)
if (ctlr->txbusy == 0)
w_cmd(ctlr, WCmdAskStats, WTyp_Stats);
}
- iunlock(&ctlr->Lock);
+ iunlock(ctlr);
}
pexit("terminated",0);
}
@@ 880,9 961,9 @@ attach(Ether* ether)
snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno);
ctlr = (Ctlr*) ether->ctlr;
if (ctlr->attached == 0){
- ilock(&ctlr->Lock);
+ ilock(ctlr);
rc = w_enable(ether);
- iunlock(&ctlr->Lock);
+ iunlock(ctlr);
if(rc == 0){
ctlr->attached = 1;
kproc(name, w_timer, ether);
@@ 921,6 1002,7 @@ ifstat(Ether* ether, void* a, long n, ulong offset)
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);
@@ 951,7 1033,7 @@ ifstat(Ether* ether, void* a, long n, ulong offset)
}
// real card stats
- ilock(&ctlr->Lock);
+ 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));
@@ 990,7 1072,7 @@ ifstat(Ether* ether, void* a, long n, ulong offset)
PRINTSTAT("Transmit key id: %d\n", txid);
}
}
- iunlock(&ctlr->Lock);
+ iunlock(ctlr);
PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes);
PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes);
@@ 1142,16 1224,16 @@ ctl(Ether* ether, void* buf, long n)
if(ctlr->attached == 0)
error(Eshutdown);
- ilock(&ctlr->Lock);
+ ilock(ctlr);
if(option(ctlr, buf, n)){
- iunlock(&ctlr->Lock);
+ iunlock(ctlr);
error(Ebadctl);
}
if(ctlr->txbusy)
w_txdone(ctlr, WTxErrEv);
w_enable(ether);
w_txstart(ether);
- iunlock(&ctlr->Lock);
+ iunlock(ctlr);
return n;
}
@@ 1164,10 1246,10 @@ transmit(Ether* ether)
if (ctlr == 0)
return;
- ilock(&ctlr->Lock);
+ ilock(ctlr);
ctlr->ntxrq++;
w_txstart(ether);
- iunlock(&ctlr->Lock);
+ iunlock(ctlr);
}
static void
@@ 1180,9 1262,9 @@ promiscuous(void* arg, int on)
error("card not found");
if (ctlr->attached == 0)
error("card not attached");
- ilock(&ctlr->Lock);
+ ilock(ctlr);
ltv_outs(ctlr, WTyp_Prom, (on?1:0));
- iunlock(&ctlr->Lock);
+ iunlock(ctlr);
}
static void
@@ 1193,10 1275,10 @@ interrupt(Ureg* ,void* arg)
if (ctlr == 0)
return;
- ilock(&ctlr->Lock);
+ ilock(ctlr);
ctlr->nints++;
w_intr(ether);
- iunlock(&ctlr->Lock);
+ iunlock(ctlr);
}
static char* wavenames[] = {
@@ 1207,57 1289,16 @@ static char* wavenames[] = {
};
static int
-reset(Ether* ether)
+genreset(Ether* ether, Ctlr *ctlr)
{
int i;
+ char *p;
Wltv ltv;
- Ctlr* ctlr;
- char *name, *p;
-
- if((ctlr = malloc(sizeof(Ctlr))) == nil)
- return -1;
-
- ilock(&ctlr->Lock);
- ctlr->ctlrno = ether->ctlrno;
-
- if (ether->port==0)
- ether->port=WDfltIOB;
- ctlr->iob = ether->port;
- if (ether->irq==0)
- ether->irq=WDfltIRQ;
-
- if (ioalloc(ether->port,WIOLen,0,"wavelan")<0){
- print("#l%d: port 0x%lx in use\n",
- ether->ctlrno, ether->port);
- goto abort1;
- }
-
- for (i=0; wavenames[i]; i++)
- if ((ctlr->slot = pcmspecial(wavenames[i], ether))>=0)
- break;
- name = wavenames[i];
-
- if (name == nil){
- for (i=0; i<ether->nopt; i++){
- if (cistrncmp(ether->opt[i], "id=", 3) == 0){
- name = ðer->opt[i][3];
- if ((ctlr->slot = pcmspecial(name, ether)) < 0)
- name = nil;
- }
- }
- if (name == nil){
- DEBUG("no wavelan found\n");
- goto abort;
- }
- }
-
- // DEBUG("#l%d: port=0x%lx irq=%ld\n",
- // ether->ctlrno, ether->port, ether->irq);
w_intdis(ctlr);
if (w_cmd(ctlr,WCmdIni,0)){
print("#l%d: init failed\n", ether->ctlrno);
- goto abort;
+ return -1;
}
w_intdis(ctlr);
ltv_outs(ctlr, WTyp_Tick, 8);
@@ 1288,7 1329,7 @@ reset(Ether* ether)
if (w_inltv(ctlr, <v)){
print("#l%d: unable to read mac addr\n",
ether->ctlrno);
- goto abort;
+ return -1;
}
memmove(ether->ea, ltv.addr, Eaddrlen);
@@ 1321,20 1362,171 @@ reset(Ether* ether)
// ether->ea[0], ether->ea[1], ether->ea[2],
// ether->ea[3], ether->ea[4], ether->ea[5]);
- iunlock(&ctlr->Lock);
return 0;
+}
-abort:
- iofree(ether->port);
-abort1:
- iunlock(&ctlr->Lock);
- free(ctlr);
- ether->ctlr = nil;
- return -1;
+static int
+wavelanpcmciareset(Ether *ether)
+{
+ int i;
+ Ctlr *ctlr;
+
+ if((ctlr = malloc(sizeof(Ctlr))) == nil)
+ return -1;
+
+ ilock(ctlr);
+ ctlr->ctlrno = ether->ctlrno;
+
+ if (ether->port==0)
+ ether->port=WDfltIOB;
+ ctlr->iob = ether->port;
+ if (ether->irq==0)
+ ether->irq=WDfltIRQ;
+
+ if (ioalloc(ether->port,WIOLen,0,"wavelan")<0){
+ print("#l%d: port 0x%lx in use\n",
+ ether->ctlrno, ether->port);
+ goto abort1;
+ }
+
+ /*
+ * If id= is specified, card must match. Otherwise try generic.
+ */
+ ctlr->slot = -1;
+ for(i=0; i<ether->nopt; i++){
+ if(cistrncmp(ether->opt[i], "id=", 3) == 0){
+ if((ctlr->slot = pcmspecial(ðer->opt[i][3], ether)) < 0)
+ return -1;
+ break;
+ }
+ }
+ if(ctlr->slot == -1){
+ for (i=0; wavenames[i]; i++)
+ if((ctlr->slot = pcmspecial(wavenames[i], ether))>=0)
+ break;
+ if(!wavenames[i]){
+ DEBUG("no wavelan found\n");
+ goto abort;
+ }
+ }
+
+ // DEBUG("#l%d: port=0x%lx irq=%ld\n",
+ // ether->ctlrno, ether->port, ether->irq);
+
+ if(genreset(ether, ctlr) < 0){
+ abort:
+ iofree(ether->port);
+ abort1:
+ iunlock(ctlr);
+ free(ctlr);
+ ether->ctlr = nil;
+ return -1;
+ }
+ iunlock(ctlr);
+ return 0;
}
+static struct {
+ int vid;
+ int did;
+} wavelanpci[] = {
+ 0x1260, 0x3873, /* Intersil Prism2.5 */
+};
+
+static Ctlr *ctlrhead, *ctlrtail;
+
+static void
+wavelanpciscan(void)
+{
+ int i;
+ ulong pa;
+ Pcidev *p;
+ Ctlr *ctlr;
+
+ p = nil;
+ while(p = pcimatch(p, 0, 0)){
+ for(i=0; i<nelem(wavelanpci); i++)
+ if(p->vid == wavelanpci[i].vid && p->did == wavelanpci[i].did)
+ break;
+ if(i==nelem(wavelanpci))
+ continue;
+
+ /*
+ * On the Prism, bar[0] is the memory-mapped register address (4KB),
+ */
+ if(p->mem[0].size != 4096){
+ print("wavelanpci: %.4ux %.4ux: unlikely mmio size\n", p->vid, p->did);
+ continue;
+ }
+
+ ctlr = malloc(sizeof(Ctlr));
+ ctlr->pcidev = p;
+ pa = upamalloc(p->mem[0].bar&~0xF, p->mem[0].size, 0);
+ if(pa == 0){
+ print("wavelanpci: %.4ux %.4ux: upamalloc 0x%.8lux %d failed\n", p->vid, p->did, p->mem[0].bar&~0xF, p->mem[0].size);
+ free(ctlr);
+ continue;
+ }
+ ctlr->mmb = (ushort*)KADDR(pa);
+ if(ctlrhead != nil)
+ ctlrtail->next = ctlr;
+ else
+ ctlrhead = ctlr;
+ ctlrtail = ctlr;
+ pcisetbme(p);
+ }
+}
+
+static int
+wavelanpcireset(Ether *ether)
+{
+ int i;
+ Ctlr *ctlr;
+
+ if(ctlrhead == nil)
+ wavelanpciscan();
+
+ /*
+ * Allow plan9.ini to set vid, did?
+ */
+ for(ctlr=ctlrhead; ctlr!=nil; ctlr=ctlr->next)
+ if(ctlr->active == 0)
+ break;
+ if(ctlr == nil)
+ return -1;
+
+ ilock(ctlr);
+ ether->irq = ctlr->pcidev->intl;
+ ether->tbdf = ctlr->pcidev->tbdf;
+
+ /*
+ * Really hard reset.
+ */
+ csr_outs(ctlr, WR_PciCor, 0x0080);
+ delay(250);
+ csr_outs(ctlr, WR_PciCor, 0x0000);
+ delay(500);
+ for(i=0; i<2*10; i++){
+ if(!(csr_ins(ctlr, WR_Cmd)&WCmdBusy))
+ break;
+ delay(100);
+ }
+ if(i >= 2*10)
+ print("wavelan pci %.4ux %.4ux: reset timeout %.4ux\n",
+ ctlr->pcidev->vid, ctlr->pcidev->did, csr_ins(ctlr, WR_Cmd));
+
+ if(genreset(ether, ctlr) < 0){
+ iunlock(ctlr);
+ ether->ctlr = nil;
+ return -1;
+ }
+ iunlock(ctlr);
+ return 0;
+}
+
void
etherwavelanlink(void)
{
- addethercard("wavelan", reset);
+ addethercard("wavelan", wavelanpcmciareset);
+ addethercard("wavelanpci", wavelanpcireset);
}