From 3f3316bcf09577f12a5e814315769e0498d2fda1 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Thu, 18 Jan 2001 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 2001-01-18 --- ip/devip.c | 42 +- ip/tcp.c | 54 +- pc/etherwavelan.c | 1827 ++++++++++++++++++++++++++------------------- 3 files changed, 1151 insertions(+), 772 deletions(-) diff --git a/ip/devip.c b/ip/devip.c index db6d134ce95f000564378836b36bd435947c2424..dbcb1cf5ff6e657f38b7f874086906846617894e 100644 --- a/ip/devip.c +++ b/ip/devip.c @@ -647,6 +647,37 @@ setladdr(Conv* c) findlocalip(c->p->f, c->laddr, c->raddr); } +/* + * set a local port making sure the quad of raddr,rport,laddr,lport is unique + */ +static void +setluniqueport(Conv* c, int lport) +{ + Proto *p; + Conv *xp; + int x; + + p = c->p; + + qlock(p); + for(x = 0; x < p->nc; x++){ + xp = p->conv[x]; + if(xp == nil) + break; + if((xp->state == Connected || xp->state == Announced) + && xp->lport == lport + && xp->rport == c->rport + && ipcmp(xp->raddr, c->raddr) == 0 + && ipcmp(xp->laddr, c->laddr) == 0){ + qunlock(p); + error("address in use"); + } + } + c->lport = lport; + qunlock(p); +} + + /* * pick a local port and set it */ @@ -707,6 +738,7 @@ static void setladdrport(Conv* c, char* str, int announcing) { char *p; + ushort lport; /* * ignore restricted part if it exists. it's @@ -725,8 +757,10 @@ setladdrport(Conv* c, char* str, int announcing) c->lport = 0; if(*p != '*'){ - c->lport = atoi(p); - if(c->lport == 0) + lport = atoi(p); + if(lport != 0) + setluniqueport(c, lport); + else setlport(c); } } @@ -819,6 +853,8 @@ connectctlmsg(Proto *x, Conv *c, Cmdbuf *cb) char* Fsstdannounce(Conv* c, char* argv[], int argc) { + memset(c->raddr, 0, sizeof(c->raddr)); + c->rport = 0; switch(argc){ default: return "bad args to announce"; @@ -866,7 +902,7 @@ announcectlmsg(Proto *x, Conv *c, Cmdbuf *cb) } /* - * called by protocol bide routine to set addresses + * called by protocol bind routine to set addresses */ char* Fsstdbind(Conv* c, char* argv[], int argc) diff --git a/ip/tcp.c b/ip/tcp.c index bf9cf1c50c8941df700fa16bc93ee575e57256b3..4885d55e808a69f15c26dee04b8cc4fa07f7a9df 100644 --- a/ip/tcp.c +++ b/ip/tcp.c @@ -333,9 +333,9 @@ tcpstate(Conv *c, char *state, int n) s = (Tcpctl*)(c->ptcl); return snprint(state, n, - "%s srtt %d mdev %d cwin %d swin %d timer.start %d timer.count %d\n", + "%s srtt %d mdev %d cwin %d swin %d rwin %d timer.start %d timer.count %d\n", tcpstates[s->state], s->srtt, s->mdev, - s->cwind, s->snd.wnd, + s->cwind, s->snd.wnd, s->rcv.wnd, s->timer.start, s->timer.count); } @@ -1082,6 +1082,7 @@ update(Conv *s, Tcp *seg) tpriv = s->p->priv; tcb = (Tcpctl*)s->ptcl; + /* if everything has been acked, force output(?) */ if(seq_gt(seg->ack, tcb->snd.nxt)) { tcb->flags |= FORCE; return; @@ -1092,8 +1093,8 @@ update(Conv *s, Tcp *seg) seg->len == 0 && seg->wnd == tcb->snd.wnd ) { /* this is a pure ack w/o window update */ -// print("dupack %lud ack %lud sndwnd %d advwin %d\n", -// tcb->snd.dupacks, seg->ack, tcb->snd.wnd, seg->wnd); + netlog(s->p->f, Logtcpmsg, "dupack %lud ack %lud sndwnd %d advwin %d\n", + tcb->snd.dupacks, seg->ack, tcb->snd.wnd, seg->wnd); if(++tcb->snd.dupacks == TCPREXMTTHRESH) { /* @@ -1102,15 +1103,21 @@ update(Conv *s, Tcp *seg) */ tcb->snd.recovery = 1; tcb->snd.rxt = tcb->snd.nxt; -// print("fast rxt %lud, nxt %lud\n", tcb->snd.una, tcb->snd.nxt); + netlog(s->p->f, Logtcpmsg, "fast rxt %lud, nxt %lud\n", tcb->snd.una, tcb->snd.nxt); tcprxmit(s); } else { /* do reno tcp here. */ } } + /* + * update our send window if this is a new ack (ignore old packets + * even if the ack is new) + */ if(seq_ge(seg->ack,tcb->snd.wl2)) - if(seq_gt(seg->seq,tcb->snd.wl1) || (seg->seq == tcb->snd.wl1)) { + if(seq_ge(seg->seq,tcb->snd.wl1)) { + + /* a closed window opened, start retransmitting. why? - presotto */ if(seg->wnd != 0 && tcb->snd.wnd == 0) tcb->snd.ptr = tcb->snd.una; @@ -1129,9 +1136,8 @@ update(Conv *s, Tcp *seg) if(!tcb->snd.recovery || seq_ge(seg->ack, tcb->snd.rxt)) { tcb->snd.dupacks = 0; tcb->snd.recovery = 0; - } else { -// print("rxt next %lud, cwin %ud\n", seg->ack, tcb->cwind); - } + } else + netlog(s->p->f, Logtcp, "rxt next %lud, cwin %ud\n", seg->ack, tcb->cwind); /* Compute the new send window size */ acked = seg->ack - tcb->snd.una; @@ -1554,16 +1560,23 @@ tcpiput(Proto *tcp, uchar*, Block *bp) bp = nil; } tcb->rcv.nxt += length; + + /* + * update our rcv window + */ tcprcvwin(s); /* - * force an ack if we've got 2 segs - * and the user isn't backing up + * force an ack if we've got 2 segs since we + * last acked. */ - if(tcb->rcv.nxt - tcb->last_ack >= 2*tcb->mss && - qlen(s->rq) < 8*tcb->mss){ + if(tcb->rcv.nxt - tcb->last_ack >= 2*tcb->mss) tcb->flags |= FORCE; - } + + /* + * turn on the acktimer if there's something + * to ack + */ if(tcb->acktimer.state != TimerON) tcpgo(tpriv, &tcb->acktimer); @@ -1711,6 +1724,9 @@ tcpoutput(Conv *s) usable -= sent; } ssize = sndcnt-sent; + if(ssize && usable < 2) + netlog(s->p->f, Logtcp, "throttled snd.wnd 0x%ux cwind 0x%ux\n", + tcb->snd.wnd, tcb->cwind); if(usable < ssize) ssize = usable; if(tcb->mss < ssize) @@ -1782,8 +1798,8 @@ tcpoutput(Conv *s) seg.flags |= FIN; dsize--; } - netlog(f, Logtcp, "qcopy: dlen %d blen %d sndcnt %d qlen %d sent %d rp[0] %d\n", - dsize, BLEN(bp), sndcnt, qlen(s->wq), sent, bp->rp[0]); +/* netlog(f, Logtcp, "qcopy: dlen %d blen %d sndcnt %d qlen %d sent %d rp[0] %d\n", + dsize, BLEN(bp), sndcnt, qlen(s->wq), sent, bp->rp[0]); */ } if(sent+dsize == sndcnt) @@ -1989,11 +2005,6 @@ tcprxmit(Conv *s) * We should be halving the slow start thershhold (down to one * mss) but leaving it at mss seems to work well enough */ -// win = (tcb->cwindsnd.wnd)?tcb->cwind:tcb->snd.wnd/ tcb->mss; -// win = win/2; -// if ( win < 2 ) -// win = 2; -// tcb->ssthresh = win * tcb->mss; tcb->ssthresh = tcb->mss; /* @@ -2032,6 +2043,7 @@ tcptimeout(void *arg) localclose(s, Etimedout); break; } + netlog(s->p->f, Logtcp, "timeout rexmit 0x%lux\n", tcb->snd.una); tcprxmit(s); tpriv->stats[RetransTimeouts]++; tcb->snd.dupacks = 0; diff --git a/pc/etherwavelan.c b/pc/etherwavelan.c index ec7cd678482b86f824d9bcddd7755ed1088b2117..a795c5861fec208624401692571b7c3fd6ae22d9 100644 --- a/pc/etherwavelan.c +++ b/pc/etherwavelan.c @@ -1,10 +1,16 @@ /* - * Port for WaveLAN I PCMCIA cards running on 2.4 GHz - * important: only works for WaveLAN I and PCMCIA 2.4 GHz cards - * Based on Linux driver by Anthony D. Joseph MIT a.o. - * We have not added the frequency, encryption and NWID selection stuff, this - * can be done with the WaveLAN provided DOS programs: instconf.exe, setconf.exe, wfreqsel.exe, etc. - * Gerard Smit 07/22/98 + Lucent Wavelan IEEE 802.11 pcmcia. + There is almost no documentation for the card. + the driver is done using both the freebsd and the linux + driver 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. + */ #include "u.h" @@ -16,880 +22,1205 @@ #include "../port/error.h" #include "../port/netif.h" #include "etherif.h" -#include "etherwavelan.h" -static void wavelan_receive(Ether *ether); -static int txstart(Ether *ether); +#define DEBUG if(1)print -static void -hacr_write_slow(int base, uchar hacr) +typedef struct Ctlr Ctlr; +typedef struct Wltv Wltv; +typedef struct WFrame WFrame; +typedef struct Stats Stats; +typedef struct WStats WStats; + +struct WStats { - outb(HACR(base), hacr); - /* delay might only be needed sometimes */ - delay(1); -} /* hacr_write_slow */ + 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; +}; -static long -ifstat(Ether* ether, void* a, long n, ulong offset) -{ - Ctlr *ctlr; - int len; - char *p; +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; +}; - ctlr = ether->ctlr; - p = malloc(READSTR); - len = snprint(p, READSTR, "interrupts: %lud\n", ctlr->interrupts); - len += snprint(p+len, READSTR-len, "upinterrupts: %lud\n", ctlr->upinterrupts); - len += snprint(p+len, READSTR-len, "dninterrupts: %lud\n", ctlr->dninterrupts); - len += snprint(p+len, READSTR-len, "int_errors: %lud\n", ctlr->int_errors); - len += snprint(p+len, READSTR-len, "read_errors: %lud\n", ctlr->read_errors); - len += snprint(p+len, READSTR-len, "out_packets: %lud\n", ctlr->out_packets); - len += snprint(p+len, READSTR-len, "tx_too_long: %lud\n", ctlr->tx_too_long); - len += snprint(p+len, READSTR-len, "tx_DMA_underrun: %lud\n", ctlr->tx_DMA_underrun); - len += snprint(p+len, READSTR-len, "tx_carrier_error: %lud\n", ctlr->tx_carrier_error); - len += snprint(p+len, READSTR-len, "tx_congestion: %lud\n", ctlr->tx_congestion); - len += snprint(p+len, READSTR-len, "tx_heart_beat: %lud\n", ctlr->tx_heart_beat); - len += snprint(p+len, READSTR-len, "rx_overflow: %lud\n", ctlr->rx_overflow); - len += snprint(p+len, READSTR-len, "rx_overrun: %lud\n", ctlr->rx_overrun); - len += snprint(p+len, READSTR-len, "rx_crc_error: %lud\n", ctlr->rx_crc); - len += snprint(p+len, READSTR-len, "rx_no_sfd: %lud\n", ctlr->rx_no_sfd); - len += snprint(p+len, READSTR-len, "rx_dropped: %lud\n", ctlr->rx_dropped); - len += snprint(p+len, READSTR-len, "tx_packets: %lud\n", ctlr->tx_packets); - len += snprint(p+len, READSTR-len, "rx_packets: %lud\n", ctlr->rx_packets); - snprint(p+len, READSTR-len, "in_packets: %lud\n", ctlr->in_packets); +// Lucent's Length-Type-Value records to talk to the wavelan. +// most operational parameters are read/set using this. +enum +{ + WTyp_Stats = 0xf100, + WTyp_Ptype = 0xfc00, + WTyp_Mac = 0xfc01, + WTyp_WantName= 0xfc02, + WTyp_Chan = 0xfc03, + WTyp_NetName = 0xfc04, + WTyp_ApDens = 0xfc06, + WTyp_MaxLen = 0xfc07, + WTyp_PM = 0xfc09, + WTyp_PMWait = 0xfc0c, + WTyp_NodeName= 0xfc0e, + WTyp_Tick = 0xfce0, + WTyp_RtsThres= 0xfc83, + WTyp_TxRate = 0xfc84, + WTx1Mbps = 0x0, + WTx2Mbps = 0x1, + WTxAuto = 0x3, + WTyp_Prom = 0xfc85, +}; - n = readstr(offset, a, n, p); - free(p); - return n; -} -static void -attach(Ether* ether) + +// Controller +enum { - Ctlr *ctlr; + WDfltIRQ = 3, // default irq + WDfltIOB = 0x100, // default IO base + + WIOLen = 0x40, // Hermes IO length + + WTmOut = 65536, // Cmd time out + + WPTypeManaged = 1, + WPTypeWDS = 2, + WPTypeAdHoc = 3, + WDfltPType = WPTypeManaged, + + WDfltApDens = 1, + WDfltRtsThres = 2347, // == disabled + WDfltTxRate = WTxAuto, // 2Mbps + + WMaxLen = 2304, + WNameLen = 32, + + // Wavelan hermes registers + WR_Cmd = 0x00, + WCmdIni = 0x0000, + WCmdEna = 0x0001, + WCmdDis = 0x0002, + WCmdMalloc = 0x000a, + WCmdAskStats= 0x0011, + WCmdMsk = 0x003f, + WCmdAccRd = 0x0021, + WCmdAccWr = 0x0121, + WCmdTxFree = 0x000b|0x0100, + WR_Parm0 = 0x02, + WR_Parm1 = 0x04, + WR_Parm2 = 0x06, + WR_Sts = 0x08, + WR_InfoId = 0x10, + WR_Sel0 = 0x18, + WR_Sel1 = 0x1a, + WR_Off0 = 0x1c, + WR_Off1 = 0x1e, + WBusyOff = 0x8000, + WErrOff = 0x4000, + WResSts = 0x7f00, + WR_RXId = 0x20, + WR_Alloc = 0x22, + WR_EvSts = 0x30, + WR_IntEna = 0x32, + WCmdEv = 0x0010, + WRXEv = 0x0001, + WTXEv = 0x0002, + WTxErrEv = 0x0004, + WAllocEv = 0x0008, + WInfoEv = 0x0080, + WIDropEv = 0x2000, + WTickEv = 0x8000, + WEvs = WRXEv|WTXEv|WAllocEv|WInfoEv|WIDropEv, + + WR_EvAck = 0x34, + WR_Data0 = 0x36, + WR_Data1 = 0x38, + + // Frame stuff + + WF_Err = 0x0003, + WF_1042 = 0x2000, + WF_Tunnel = 0x4000, + WF_WMP = 0x6000, + + WF_Data = 0x0008, + + WSnapK1 = 0xaa, + WSnapK2 = 0x00, + WSnapCtlr = 0x03, + WSnap0 = (WSnapK1|(WSnapK1<<8)), + WSnap1 = (WSnapK2|(WSnapCtlr<<8)), + WSnapHdrLen = 6, + + WF_802_11_Off = 0x44, + WF_802_3_Off = 0x2e, +}; - ctlr = ether->ctlr; - ilock(&ctlr->wlock); - if(ctlr->attached){ - iunlock(&ctlr->wlock); - return; - } - ctlr->attached = 1; - iunlock(&ctlr->wlock); -} +#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)) -static void -interrupt_handler(Ether *ether, uchar status) +struct Wltv { - Ctlr *ctlr; - int status0; - int tx_status; - int base; - ctlr = ether->ctlr; - base = ctlr->port; + ushort len; + ushort type; + union + { + struct { + ushort val; + ushort pad; + }; + struct { + uchar addr[8]; + }; + struct { + char s[17*2]; + }; + struct { + char name[WNameLen]; + }; + }; +}; - status0 = status; +// What the driver thinks. Not what the card thinks. +struct Stats +{ + ulong nints; + ulong nrx; + ulong ntx; + ulong ntxrq; + ulong nrxerr; + ulong ntxerr; + ulong nalloc; // allocation (reclaim) events + ulong ninfo; + ulong nidrop; + ulong nwatchdogs; // transmits time outs, actually +}; - /* Return if no actual interrupt from i82593 */ - if(!(status0 & SR0_INTERRUPT)) { - print("Wavelan: Interrupt from dead card\n"); - return; - } - ctlr->status = status0; /* Save current status (for commands) */ - if (status0 & SR0_RECEPTION) { - if((status0 & SR0_EVENT_MASK) == SR0_STOP_REG_HIT) { - print("wavelan: receive buffer overflow\n"); - ctlr->rx_overflow++; - outb(LCCR(base), CR0_INT_ACK | OP0_NOP); /* Acknowledge the interrupt */ - return; - } - wavelan_receive(ether); - if (status0 & SR0_EXECUTION) - print("wavelan_cs: interrupt is both rx and tx, status0 = %x\n", - status0); - outb(LCCR(base), CR0_INT_ACK | OP0_NOP); /* Acknowledge the interrupt */ - return; - } - if (!(status0 & SR0_EXECUTION)) { - print("wavelan_cs: interrupt is neither rx or tx, status0 = %x\n", - status0); - outb(LCCR(base), CR0_INT_ACK | OP0_NOP); /* Acknowledge the interrupt */ - return; - } - /* interrupt due to configure_done or IA_setup_done */ - if ((status0 & SR0_EVENT_MASK) == SR0_CONFIGURE_DONE || - (status0 & SR0_EVENT_MASK) == SR0_IA_SETUP_DONE) { - outb(LCCR(base), CR0_INT_ACK | OP0_NOP); /* Acknowledge the interrupt */ - return; - } - /* so a transmit interrupt is remaining */ - if((status0 & SR0_EVENT_MASK) == SR0_TRANSMIT_DONE || - (status0 & SR0_EVENT_MASK) == SR0_RETRANSMIT_DONE) { - tx_status = inb(LCSR(base)); - tx_status |= (inb(LCSR(base)) << 8); - if (!(tx_status & TX_OK)) { - if (tx_status & TX_FRTL) { - print("wavelan_cs: frame too long\n"); - ctlr->tx_too_long++; - } - if (tx_status & TX_UND_RUN) { - /* print("wavelan_csd: DMA underrun\n"); */ - ctlr->tx_DMA_underrun++; - } - if (tx_status & TX_LOST_CTS) { - /* print("wavelan: no CTS\n"); */ - ctlr->tx_carrier_error++; - } - if (tx_status & TX_LOST_CRS) { - /* print("wavelan: lost CRS\n"); */ - ctlr->tx_carrier_error++; - } - if (tx_status & TX_DEFER) { - /* print("wavelan: channel jammed\n"); */ - ctlr->tx_congestion++; - } - if (tx_status & TX_COLL) { - if (tx_status & TX_MAX_COL) { - /* print("wavelan_cs: channel congestion\n"); */ - ctlr->tx_congestion++; - } - } - if (tx_status & TX_HRT_BEAT) { - /* print("wavelan_cs: heart beat\n"); */ - ctlr->tx_heart_beat++; - } - } - - ctlr->tx_packets++; - ctlr->txbusy = 0; - outb(LCCR(base), CR0_INT_ACK | OP0_NOP); /* Acknowledge the interrupt */ - txstart(ether); /* start new transfer if any */ - return; - } - print("wavelan: unknown interrupt\n"); - outb(LCCR(base), CR0_INT_ACK | OP0_NOP); /* Acknowledge the interrupt */ +struct Ctlr +{ + Lock; + Rendez timer; + + int attached; + int slot; + int iob; + int ptype; + int apdensity; + int rtsthres; + int txbusy; + int txrate; + int txdid; + int txmid; + int txtmout; + int maxlen; + int chan; + int pmena; + int pmwait; + + char netname[WNameLen]; + char wantname[WNameLen]; + char nodename[WNameLen]; + WFrame txf; + uchar txbuf[1536]; + int txlen; + Stats; + WStats; +}; -} +// w_... routines do not ilock the Ctlr and should +// be called locked. + +static void +w_intdis(Ctlr* ctlr) +{ + csr_outs(ctlr, WR_IntEna, 0); + csr_ack(ctlr, 0xffff); +} -static Block* -rbpalloc(Block* (*f)(int)) +static void +w_intena(Ctlr* ctlr) { - Block *bp; - ulong addr; + csr_outs(ctlr, WR_IntEna, WEvs); +} - /* - * The receive buffers must be on a 32-byte - * boundary for EISA busmastering. - */ - if(bp = f(ROUNDUP(sizeof(Etherpkt), 4) + 31)){ - addr = (ulong)bp->base; - addr = ROUNDUP(addr, 32); - bp->rp = (uchar*)addr; +static int +w_cmd(Ctlr *ctlr, ushort cmd, ushort arg) +{ + int i; + int rc; + csr_outs(ctlr, WR_Parm0, arg); + csr_outs(ctlr, WR_Cmd, cmd); + for (i = 0; itype)){ + DEBUG("wavelan: access read failed\n"); + return -1; } - - /* Busy wait while the LAN controller executes the command. - * Interrupts had better be enabled (or it will be a long wait). - * (We could enable just the WaveLAN's IRQ..., we do not bother this is only for setup commands) - */ - for(spin = 0; (spin < 10000000); spin++) - ; - outb(LCCR(base), CR0_STATUS_0 | OP0_NOP); - status = inb(LCSR(base)); - if(status & SR0_INTERRUPT){ - if (((status & SR0_EVENT_MASK) == SR0_CONFIGURE_DONE) || - ((status & SR0_EVENT_MASK) == SR0_IA_SETUP_DONE) || - ((status & SR0_EVENT_MASK) == SR0_EXECUTION_ABORTED) || - ((status & SR0_EVENT_MASK) == SR0_DIAGNOSE_PASSED)) - outb(LCCR(base), CR0_INT_ACK | OP0_NOP); /* acknowledge interrupt */ - else - interrupt_handler(ether, status); - } else { - print("wavelan_cmd: %s timeout, status0 = 0x%uX\n", str, status); - outb(OP0_ABORT, LCCR(base)); - spin = 0; - while(spin++ < 250) /* wait for the command to execute */ - delay(1); - return 0; + if (w_seek(ctlr,l->type,0,1)){ + DEBUG("wavelan: seek failed\n"); + return -1; } - if((status & SR0_EVENT_MASK) != result){ - print("wavelan_cmd: %s failed, status0 = 0x%uX\n", str, status); - return 0; + len = csr_ins(ctlr, WR_Data1); + if (len > l->len) + return -1; + l->len = len; + if ((code=csr_ins(ctlr, WR_Data1)) != l->type){ + DEBUG("wavelan: type %x != code %x\n",l->type,code); + return -1; } - return 1; -} /* wavelan_cmd */ + p = &l->val; + len--; + for (i=0; itype,0,1)) + return; + csr_outs(ctlr, WR_Data1, l->len); + csr_outs(ctlr, WR_Data1, l->type); + p = &l->val; + len = l->len-1; + for (i=0; itype); } -static int -read_ringbuf(Ether *ether, int addr, uchar *buf, int len) -{ Ctlr *ctlr; - int base; - int ring_ptr = addr; - int chunk_len; - uchar *buf_ptr = buf; - ctlr = ether->ctlr; - base = ctlr->port; - - /* If buf is NULL, just increment the ring buffer pointer */ - if (buf == 0) - return((ring_ptr - RX_BASE + len) % RX_SIZE + RX_BASE); - while (len > 0) { - /* Position the Program I/O Register at the ring buffer pointer */ - outb(PIORL(base), ring_ptr & 0xff); - outb(PIORH(base), ((ring_ptr >> 8) & PIORH_MASK)); - /* First, determine how much we can read without wrapping around the - ring buffer */ - if ((addr + len) < (RX_BASE + RX_SIZE)) - chunk_len = len; - else - chunk_len = RX_BASE + RX_SIZE - addr; - insb(PIOP(base), buf_ptr, chunk_len); - buf_ptr += chunk_len; - len -= chunk_len; - ring_ptr = (ring_ptr - RX_BASE + chunk_len) % RX_SIZE + RX_BASE; - } - return(ring_ptr); -} /* read_ringbuf */ +static void +ltv_outs(Ctlr* ctlr, int type, ushort val) +{ + Wltv l; -static void -wavelan_hardware_send_packet(Ether *ether, void *buf, short length) + l.type = type; + l.val = val; + l.len = 2; + w_outltv(ctlr, &l); +} + +static ushort +ltv_ins(Ctlr* ctlr, int type) { - Ctlr *ctlr; - int base; - register ushort xmtdata_base = TX_BASE; - ctlr = ether->ctlr; - base = ctlr->port; + Wltv l; - outb(PIORL(base), xmtdata_base & 0xff); - outb(PIORH(base), ((xmtdata_base >> 8) & PIORH_MASK) | PIORH_SEL_TX); - outb(PIOP(base), length & 0xff); /* lsb */ - outb(PIOP(base), length >> 8); /* msb */ - outsb(PIOP(base), buf, length); /* Send the data */ - outb(PIOP(base), OP0_NOP); /* Indicate end of transmit chain */ + l.type = type; + l.len = 2; + l.val = 0; + w_inltv(ctlr, &l); + return l.val; +} - /* Reset the transmit DMA pointer */ - hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); - outb(HACR(base), HACR_DEFAULT); - /* Send the transmit command */ - wavelan_cmd(ether, base, "wavelan_hardware_send_packet(): transmit", OP0_TRANSMIT, - SR0_NO_RESULT); -} /* wavelan_hardware_send_packet */ +static void +ltv_outstr(Ctlr* ctlr, int type, char *val) +{ + Wltv l; + int len; + len = (strlen(val)+1)&~1; + memset(&l,0,sizeof(l)); + l.type = type; + l.len = (len/2)+2; + l.val = len; // l.s[0] and l.s[1] + strncpy(l.s+2,val,strlen(val)); + w_outltv(ctlr, &l); +} +static char* +ltv_inname(Ctlr* ctlr, int type) +{ + static char unk[] = "unknown"; + static Wltv l; + + memset(&l,0,sizeof(l)); + l.type = type; + l.len = WNameLen/2+2; + if (w_inltv(ctlr, &l)) + return unk; + if (l.name[2] == 0) + return unk; + return l.name+2; +} -static int -txstart(Ether *ether) -{ - Ctlr *ctlr; - Block *bp; - int base, length; - int status; - ctlr = ether->ctlr; - base = ctlr->port; - - for(;;) { - if(ctlr->txbp){ - bp = ctlr->txbp; - ctlr->txbp = 0; - } - else{ - bp = qget(ether->oq); - if(bp == nil) - break; - } - length = BLEN(bp); - length = (ETH_ZLEN < length) ? length : ETH_ZLEN; - outb(LCCR(base), OP0_NOP | CR0_STATUS_3); - status = inb(LCSR(base)); - if ((status & SR3_EXEC_STATE_MASK) == SR3_EXEC_IDLE) { - wavelan_hardware_send_packet(ether, bp->rp, length); - freeb(bp); - ctlr->out_packets++; +static int +w_read(Ctlr* ctlr, int type, int off, void* buf, ulong len) +{ + ushort *p = (ushort*)buf; + int i, n; + + n=0; + if (w_seek(ctlr, type, off, 1) == 0){ + len /= 2; + for (i = 0; i < len; i++){ + p[i] = csr_ins(ctlr, WR_Data1); + n += 2; } - else{ - ctlr->txbp = bp; - if(ctlr->txbusy == 0){ - ctlr->txbusy = 1; - } - break; + } else + DEBUG("wavelan: w_read: seek failed"); + + return n; +} + + +static int +w_write(Ctlr* ctlr, int type, int off, void* buf, ulong len) +{ + ushort *p = (ushort*)buf; + ulong l = len / 2; + int i,tries; + + for (tries=0; tries < WTmOut; tries++){ + if (w_seek(ctlr, type, off, 0)){ + DEBUG("wavelan: w_write: seek failed\n"); + return 0; } + for (i = 0; i < l; i++) + csr_outs(ctlr, WR_Data0, p[i]); + 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; -} /* wavelan txstart */ +} + +static int +w_alloc(Ctlr* ctlr, int len) +{ + int rc; + int i,j; + + if (w_cmd(ctlr, WCmdMalloc, len)==0) + for (i = 0; ictlr; - ctlr = ether->ctlr; - base = ctlr->port; + if (!ctlr) + return -1; - rp = (rfp - 5 + RX_SIZE) % RX_SIZE; - outb(PIORL(base), rp & 0xff); - outb(PIORH(base), ((rp >> 8) & PIORH_MASK)); - len = inb(PIOP(base)); - len |= inb(PIOP(base)) << 8; + w_intdis(ctlr); + w_cmd(ctlr, WCmdDis, 0); + w_intdis(ctlr); + if(w_cmd(ctlr, WCmdIni, 0)) + return -1; - if (len > 1600) { /* Sanity check on size */ - print("wavelan_cs: Received frame too large, rfp %d rp %d len 0x%x\n", - rfp, rp, len); + 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_RtsThres, ctlr->rtsthres); + ltv_outs(ctlr, WTyp_TxRate, ctlr->txrate); + ltv_outs(ctlr, WTyp_ApDens, ctlr->apdensity); + ltv_outs(ctlr, WTyp_PM, ctlr->pmena); + ltv_outs(ctlr, WTyp_PMWait, ctlr->pmwait); + 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); + l.type = WTyp_Mac; + l.len = 4; + memmove(l.addr, ether->ea, Eaddrlen); + w_outltv(ctlr, &l); + + ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0)); + + // BUG: set multicast addresses + + if (w_cmd(ctlr, WCmdEna, 0)){ + DEBUG("wavelan: Enable failed"); return -1; } - - if(len < 7){ - print("wavelan_start_of_frame: Received null frame, rfp %d len 0x%x\n", rfp, len); - return(-1); - } - /* Wrap around buffer */ - if(len > ((wrap - (rfp - len) + RX_SIZE) % RX_SIZE)) { /* magic formula ! */ - print("wavelan_start_of_frame: wrap around buffer, wrap %d rfp %d len 0x%x\n",wrap, rfp, len); - 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; - return((rp - len + RX_SIZE) % RX_SIZE); -} /* wv_start_of_frame */ +} -static void wavelan_read(Ether *ether, int fd_p, int sksize) -{ - Ctlr *ctlr; - Block *bp; - uchar stats[3]; - ctlr = ether->ctlr; +static void +w_rxdone(Ether* ether) +{ + Ctlr* ctlr = (Ctlr*) ether->ctlr; + ushort sp; + WFrame f; + Block* bp=0; + ulong l; + Etherpkt* ep; + + sp = csr_ins(ctlr, WR_RXId); + l = w_read(ctlr, sp, 0, &f, sizeof(f)); + if (l == 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: + l = f.dlen + WSnapHdrLen; + bp = iallocb(ETHERHDRSIZE + l + 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, l+2) == 0){ + DEBUG("wavelan: read 802.11 error\n"); + goto rxerror; + } + bp->wp += l+2; + bp = trimblock(bp, 0, f.dlen+ETHERHDRSIZE); + break; + default: + l = ETHERHDRSIZE + f.dlen + 2; + bp = iallocb(l); + if (!bp) + goto rxerror; + if (w_read(ctlr, sp, WF_802_3_Off, bp->wp, l) == 0){ + DEBUG("wavelan: read 800.3 error\n"); + goto rxerror; + } + bp->wp += l; + } - ctlr->rx_packets++; + ctlr->nrx++; + etheriq(ether,bp,1); + return; - if ((bp = rbpalloc(allocb)) == 0){ - print("wavelan: could not rbpalloc(%d).\n", sksize); - ctlr->rx_dropped++; - return; - } else { - fd_p = read_ringbuf(ether, fd_p, ctlr->rbp->rp, sksize); - ctlr->rbp->wp = ctlr->rbp->rp + sksize; - /* read signal level, silence level and signal quality bytes */ - read_ringbuf(ether, (fd_p+4) % RX_SIZE+RX_BASE, stats, 3); - /* - * Hand the packet to the Network Module - */ - etheriq(ether, ctlr->rbp, 1); - ctlr->in_packets++; - ctlr->rbp = bp; - return; - } -} /* wavelan_read */ + rxerror: + freeb(bp); + ctlr->nrxerr++; +} -static void -wavelan_receive(Ether *ether) +static int +w_txstart(Ether* ether, int again) { - Ctlr *ctlr; - int base; - int newrfp, rp, len, f_start, status; - int i593_rfp, stat_ptr; - uchar c[4]; - - ctlr = ether->ctlr; - base = ctlr->port; - - /* Get the new receive frame pointer from the i82593 chip */ - outb(LCCR(base), CR0_STATUS_2 | OP0_NOP); - i593_rfp = inb(LCSR(base)); - i593_rfp |= inb(LCSR(base)) << 8; - i593_rfp %= RX_SIZE; - - /* Get the new receive frame pointer from the WaveLAN card. - * It is 3 bytes more than the increment of the i82593 receive - * frame pointer, for each packet. This is because it includes the - * 3 roaming bytes added by the mmc. - */ - newrfp = inb(RPLL(base)); - newrfp |= inb(RPLH(base)) << 8; - newrfp %= RX_SIZE; - -// print("wavelan_cs: i593_rfp %d stop %d newrfp %d ctlr->rfp %d\n", -// i593_rfp, ctlr->stop, newrfp, ctlr->rfp); - - if (newrfp == ctlr->rfp) - print("wavelan_cs: odd RFPs: i593_rfp %d stop %d newrfp %d ctlr->rfp %d\n", - i593_rfp, ctlr->stop, newrfp, ctlr->rfp); - - while(newrfp != ctlr->rfp) { - rp = newrfp; - /* Find the first frame by skipping backwards over the frames */ - while (((f_start = wavelan_start_of_frame(ether,rp, newrfp)) != ctlr->rfp) && (f_start != -1)) - rp = f_start; - if(f_start == -1){ - print("wavelan_cs: cannot find start of frame "); - print(" i593_rfp %d stop %d newrfp %d ctlr->rfp %d\n", - i593_rfp, ctlr->stop, newrfp, ctlr->rfp); - ctlr->rfp = rp; - continue; - } - stat_ptr = (rp - 7 + RX_SIZE) % RX_SIZE; - read_ringbuf(ether, stat_ptr, c, 4); - status = c[0] | (c[1] << 8); - len = c[2] | (c[3] << 8); - if(!(status & RX_RCV_OK)) { - if(status & RX_NO_SFD) ctlr->rx_no_sfd++; - if(status & RX_CRC_ERR) ctlr->rx_crc++; - if(status & RX_OVRRUN) ctlr->rx_overrun++; - - print("wavelan_cs: packet not received ok, status = 0x%x\n", status); - } else wavelan_read(ether, f_start, len - 2); + Etherpkt* ep; + Ctlr* ctlr = (Ctlr*) ether->ctlr; + Block* bp; + int txid; + if (ctlr == 0 || ctlr->attached == 0 ) + return -1; + if (ctlr->txbusy && again==0) + return -1; - ctlr->rfp = rp; /* one packet processed, skip it */ + txid = ctlr->txdid; + if (again){ + bp = 0; // a watchdog reenabled the card. + goto retry; // must retry a previously failed tx. } - /* - * Update the frame stop register, but set it to less than - * the full 8K to allow space for 3 bytes of signal strength - * per packet. - */ - ctlr->stop = (i593_rfp + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE; - outb(LCCR(base), OP0_SWIT_TO_PORT_1 | CR0_CHNL); - outb(LCCR(base), CR1_STOP_REG_UPDATE | (ctlr->stop >> RX_SIZE_SHIFT)); - outb(LCCR(base), OP1_SWIT_TO_PORT_0); -} /* wavelan_receive */ + bp = qget(ether->oq); + if (bp == 0) + return 0; + ep = (Etherpkt*) bp->rp; + ctlr->txbusy = 1; + + // BUG: only IP/ARP/RARP seem to be ok for 802.3 + // Other packets should be just copied to the board. + // The driver is not doing so, though. + // Besides, the Block should be used instead of txbuf, + // to save a memory copy. + memset(ctlr->txbuf,0,sizeof(ctlr->txbuf)); + memset(&ctlr->txf,0,sizeof(ctlr->txf)); + ctlr->txf.framectl = WF_Data; + memmove(ctlr->txf.addr1, ep->d, Eaddrlen); + memmove(ctlr->txf.addr2, ep->s, Eaddrlen); + memmove(ctlr->txf.dstaddr, ep->d, Eaddrlen); + memmove(ctlr->txf.srcaddr, ep->s, Eaddrlen); + memmove(&ctlr->txf.type,ep->type,2); + ctlr->txlen = BLEN(bp); + ctlr->txf.dlen = ctlr->txlen - WSnapHdrLen; + hnputs((uchar*)&ctlr->txf.dat[0], WSnap0); + hnputs((uchar*)&ctlr->txf.dat[1], WSnap1); + hnputs((uchar*)&ctlr->txf.len, ctlr->txlen - WSnapHdrLen); + if (ctlr->txlen - ETHERHDRSIZE > 1536){ + print("wavelan: txbuf overflow"); + freeb(bp); + return -1; + } + memmove(ctlr->txbuf, bp->rp+sizeof(ETHERHDRSIZE)+10, + ctlr->txlen - ETHERHDRSIZE ); +retry: + w_write(ctlr, txid, 0, &ctlr->txf, sizeof(ctlr->txf)); + + w_write(ctlr, txid, WF_802_11_Off, ctlr->txbuf, + ctlr->txlen - ETHERHDRSIZE + 2); + if (w_cmd(ctlr, WCmdTxFree, txid)){ + DEBUG("wavelan: transmit failed\n"); + ctlr->txbusy=0; // added + ctlr->ntxerr++; + freeb(bp); + return -1; + } + ctlr->txtmout = 2; + freeb(bp); + return 0; +} static void -interrupt(Ureg*, void* arg) +w_txdone(Ctlr* ctlr, int sts) { - Ether *ether; - Ctlr *ctlr; - int base; - - ether = arg; - ctlr = ether->ctlr; - base = ctlr->port; - - ilock(&ctlr->wlock); - - ctlr->interrupts++; + int b = ctlr->txbusy; + + ctlr->txbusy = 0; + ctlr->txtmout= 0; + if (sts & WTxErrEv){ + ctlr->ntxerr++; + if (sts&1) // it was a watchdog, stay busy to retry. + ctlr->txbusy = b; + } else + ctlr->ntx++; +} - outb(LCCR(base), CR0_STATUS_0 | OP0_NOP); - interrupt_handler(ether, inb(LCSR(base))); - - iunlock(&ctlr->wlock); +static int +w_stats(Ctlr* ctlr) +{ + int sp,i; + ushort rc; + Wltv l; + ulong* p = (ulong*)&ctlr->WStats; + ulong* pend= (ulong*)&ctlr->end; + + sp = csr_ins(ctlr, WR_InfoId); + l.type = l.len = 0; + w_read(ctlr, sp, 0, &l, 4); + if (l.type == WTyp_Stats){ + l.len--; + for (i = 0; i < l.len && p < pend ; i++){ + rc = csr_ins(ctlr, WR_Data1); + if (rc > 0xf000) + rc = ~rc & 0xffff; + p[i] += rc; + } + return 0; + } + return -1; +} -}; /* wavelan interrupt */ +static void +w_intr(Ether *ether) +{ + int rc, txid, i; + Ctlr* ctlr = (Ctlr*) ether->ctlr; + if (ctlr->attached == 0){ + csr_ack(ctlr, 0xffff); + csr_outs(ctlr, WR_IntEna, 0); + return; + } + for(i=0; i<7; i++){ + csr_outs(ctlr, WR_IntEna, 0); + rc = csr_ins(ctlr, WR_EvSts); + csr_ack(ctlr, ~WEvs); // Not interested on 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_intena(ctlr); + w_txstart(ether,0); + } + } +// 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 -promiscuous() +w_timer(void* arg) { - ; -}; - -static void -multicast() -{ - ; -}; + Ether* ether = (Ether*) arg; + Ctlr* ctlr = (Ctlr*)ether->ctlr; + int tick=0; + + for(;;){ + tsleep(&ctlr->timer, return0, 0, 50); + ctlr = (Ctlr*)ether->ctlr; + if (ctlr == 0) + break; + if (ctlr->attached == 0) + continue; + tick++; + + ilock(&ctlr->Lock); + + // 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) + w_intr(ether); + + if (tick % 10 == 0) { + if (ctlr->txtmout && --ctlr->txtmout == 0){ + ctlr->nwatchdogs++; + w_txdone(ctlr, WTxErrEv|1); // 1: keep it busy + if (w_enable(ether)) + DEBUG("wavelan: wdog enable failed\n"); + if (ctlr->txbusy) + w_txstart(ether,1); + } + if (tick % 120 == 0) + if (ctlr->txbusy == 0) + w_cmd(ctlr, WCmdAskStats, WTyp_Stats); + } + iunlock(&ctlr->Lock); + } + pexit("terminated",0); +} -static void -mmc_read(int base, uchar o, uchar *b, int n) +static void* +emalloc(ulong size) { - while (n-- > 0) { - while (inb(HASR(base)) & HASR_MMI_BUSY) ; /* Wait for MMC to go idle */ - outb(MMR(base), o << 1); /* Set the read address */ - o++; + void *r=malloc(size); + if (!r) + error(Enomem); + memset(r,0,size); + return r; +} - outb(MMD(base), 0); /* Required dummy write */ - while (inb(HASR(base)) & HASR_MMI_BUSY) ; /* Wait for MMC to go idle */ - *b++ = (uchar)(inb(MMD(base))); /* Now do the actual read */ - } -} /* mmc_read */ static void -fee_wait(int base, int del, int numb) -{ int count = 0; - while ((count++ < numb) && (mmc_in(base, MMC_EECTRL) & MMR_FEE_STATUS_BUSY)) - delay(del); - if (count==numb) print("Wavelan: fee wait timed out\n"); +multicast(void*, uchar*, int) +{ + // BUG: to be added. } -static void -mmc_write_b(int base, uchar o, uchar b) +static void +attach(Ether* ether) { - while (inb(HASR(base)) & HASR_MMI_BUSY) ; /* Wait for MMC to go idle */ - outb(MMR(base), (uchar)((o << 1) | MMR_MMI_WR)); - outb(MMD(base), (uchar)(b)); -} /* mmc_write_b */ + Ctlr* ctlr; + char name[NAMELEN]; + int rc; -static void -mmc_write(int base, uchar o, uchar *b, int n) -{ - o += n; - b += n; - while (n-- > 0 ) - mmc_write_b(base, --o, *(--b)); -} /* mmc_write */ + if (ether->ctlr == 0) + return; + snprint(name, NAMELEN, "#l%dtimer", ether->ctlrno); + ctlr = (Ctlr*) ether->ctlr; + if (ctlr->attached == 0){ + ilock(&ctlr->Lock); + rc = w_enable(ether); + iunlock(&ctlr->Lock); + if(rc == 0){ + ctlr->attached = 1; + kproc(name, w_timer, ether); + } else + print("#l%d: enable failed\n",ether->ctlrno); + } +} -static void wavelan_mmc_init(Ether *ether, int port) +static long +ifstat(Ether* ether, void* a, long n, ulong offset) { - Ctlr *ctlr; - mmw_t m; - ctlr = ether->ctlr; - - memset(&m, 0x00, sizeof(m)); - - /* - * Set default modem control parameters. - * See NCR document 407-0024326 Rev. A. - */ - m.mmw_jabber_enable = 0x01; - m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN; - m.mmw_ifs = 0x20; - m.mmw_mod_delay = 0x04; - m.mmw_jam_time = 0x38; - m.mmw_encr_enable = 0; - m.mmw_des_io_invert = 0; - m.mmw_freeze = 0; - m.mmw_decay_prm = 0; - m.mmw_decay_updat_prm = 0; - m.mmw_loopt_sel = MMW_LOOPT_SEL_UNDEFINED; - m.mmw_thr_pre_set = 0x04; /* PCMCIA */ - m.mmw_quality_thr = 0x03; - m.mmw_netw_id_l = ctlr->nwid[1]; /* use nwid of PSA memory */ - m.mmw_netw_id_h = ctlr->nwid[0]; - - mmc_write(port, 0, (uchar *)&m, 37); /* size of mmw_t == 37 */ - - /* Start the modem's receive unit on version 2.00 */ - /* 2.4 Gz: half-card ver */ - /* 2.4 Gz */ - /* 2.4 Gz: position ch # */ - mmc_write_b(port, MMC_EEADDR, 0x0f); /* 2.4 Gz: named ch, wc=16 */ - mmc_write_b(port, MMC_EECTRL,MMC_EECTRL_DWLD | /* 2.4 Gz: Download Synths */ - MMC_EECTRL_EEOP_READ); /* 2.4 Gz: Read EEPROM */ - fee_wait(port, 10, 100); /* 2.4 Gz: wait for download */ - /* 2.4 Gz */ - mmc_write_b(port, MMC_EEADDR,0x61); /* 2.4 Gz: default pwr, wc=2 */ - mmc_write_b(port, MMC_EECTRL,MMC_EECTRL_DWLD | /* 2.4 Gz: Download Xmit Pwr */ - MMC_EECTRL_EEOP_READ); /* 2.4 Gz: Read EEPROM */ - fee_wait(port, 10, 100); /* 2.4 Gz: wait for download */ - -} /* wavelan_mmc_init */ - - -int wavelan_diag(Ether *ether, int port) -{ - if (wavelan_cmd(ether, port, "wavelan_diag(): diagnose", OP0_DIAGNOSE, - SR0_DIAGNOSE_PASSED)){ + Ctlr* ctlr = (Ctlr*) ether->ctlr; + char* p; + int l; + + if (n == 0 || ctlr == 0){ return 0; } - print("wavelan_cs: i82593 Self Test failed!\n"); - return 1; -} /* wavelan_diag */ - - - -int wavelan_hw_config(int base, Ether *ether) -{ - struct i82593_conf_block cfblk; - - memset(&cfblk, 0x00, sizeof(struct i82593_conf_block)); - cfblk.d6mod = FALSE; /* Run in i82593 advanced mode */ - cfblk.fifo_limit = 6; /* = 48 bytes rx and tx fifo thresholds */ - cfblk.forgnesi = FALSE; /* 0=82C501, 1=AMD7992B compatibility */ - cfblk.fifo_32 = 0; - cfblk.throttle_enb = TRUE; - cfblk.contin = TRUE; /* enable continuous mode */ - cfblk.cntrxint = FALSE; /* enable continuous mode receive interrupts */ - cfblk.addr_len = WAVELAN_ADDR_SIZE; - cfblk.acloc = TRUE; /* Disable source addr insertion by i82593 */ - cfblk.preamb_len = 2; /* 7 byte preamble */ - cfblk.loopback = FALSE; - cfblk.lin_prio = 0; /* conform to 802.3 backoff algoritm */ - cfblk.exp_prio = 0; /* conform to 802.3 backoff algoritm */ - cfblk.bof_met = 0; /* conform to 802.3 backoff algoritm */ - cfblk.ifrm_spc = 6; /* 96 bit times interframe spacing */ - cfblk.slottim_low = 0x10 & 0x7; /* 512 bit times slot time */ - cfblk.slottim_hi = 0x10 >> 3; - cfblk.max_retr = 15; - cfblk.prmisc = FALSE; /* Promiscuous mode ?? */ - cfblk.bc_dis = FALSE; /* Enable broadcast reception */ - cfblk.crs_1 = TRUE; /* Transmit without carrier sense */ - cfblk.nocrc_ins = FALSE; /* i82593 generates CRC */ - cfblk.crc_1632 = FALSE; /* 32-bit Autodin-II CRC */ - cfblk.crs_cdt = FALSE; /* CD not to be interpreted as CS */ - cfblk.cs_filter = 0; /* CS is recognized immediately */ - cfblk.crs_src = FALSE; /* External carrier sense */ - cfblk.cd_filter = 0; /* CD is recognized immediately */ - cfblk.min_fr_len = 64 >> 2; /* Minimum frame length 64 bytes */ - cfblk.lng_typ = FALSE; /* Length field > 1500 = type field */ - cfblk.lng_fld = TRUE; /* Disable 802.3 length field check */ - cfblk.rxcrc_xf = TRUE; /* Don't transfer CRC to memory */ - cfblk.artx = TRUE; /* Disable automatic retransmission */ - cfblk.sarec = TRUE; /* Disable source addr trig of CD */ - cfblk.tx_jabber = TRUE; /* Disable jabber jam sequence */ - cfblk.hash_1 = FALSE; /* Use bits 0-5 in mc address hash */ - cfblk.lbpkpol = TRUE; /* Loopback pin active high */ - cfblk.fdx = FALSE; /* Disable full duplex operation */ - cfblk.dummy_6 = 0x3f; /* all ones */ - cfblk.mult_ia = FALSE; /* No multiple individual addresses */ - cfblk.dis_bof = FALSE; /* Disable the backoff algorithm ?! */ - cfblk.dummy_1 = TRUE; /* set to 1 */ - cfblk.tx_ifs_retrig = 3; /* Hmm... Disabled */ - cfblk.mc_all = FALSE; /* No multicast all mode */ - cfblk.rcv_mon = 0; /* Monitor mode disabled */ - cfblk.frag_acpt = TRUE;/* Do not accept fragments */ - cfblk.tstrttrs = FALSE; /* No start transmission threshold */ - cfblk.fretx = TRUE; /* FIFO automatic retransmission */ - cfblk.syncrqs = TRUE; /* Synchronous DRQ deassertion... */ - cfblk.sttlen = TRUE; /* 6 byte status registers */ - cfblk.rx_eop = TRUE; /* Signal EOP on packet reception */ - cfblk.tx_eop = TRUE; /* Signal EOP on packet transmission */ - cfblk.rbuf_size = RX_SIZE>>11; /* Set receive buffer size */ - cfblk.rcvstop = TRUE; /* Enable Receive Stop Register */ - - outb(PIORL(base), (TX_BASE & 0xff)); - outb(PIORH(base), (((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX)); - outb(PIOP(base), (sizeof(struct i82593_conf_block) & 0xff)); /* lsb */ - outb(PIOP(base), (sizeof(struct i82593_conf_block) >> 8)); /* msb */ - outsb(PIOP(base), ((char *) &cfblk), sizeof(struct i82593_conf_block)); - - /* reset transmit DMA pointer */ - hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); - outb(HACR(base), HACR_DEFAULT); - if(!wavelan_cmd(ether, base, "wavelan_hw_config(): configure", OP0_CONFIGURE, - SR0_CONFIGURE_DONE)) - return(FALSE); - - /* Initialize adapter's ethernet MAC address */ - outb(PIORL(base), (TX_BASE & 0xff)); - outb(PIORH(base), (((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX)); - outb(PIOP(base), WAVELAN_ADDR_SIZE); /* byte count lsb */ - outb(PIOP(base), 0); /* byte count msb */ - outsb(PIOP(base), ðer->ea[0], WAVELAN_ADDR_SIZE); - /* reset transmit DMA pointer */ - hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET); - outb(HACR(base), HACR_DEFAULT); - if(!wavelan_cmd(ether, base, "wavelan_hw_config(): ia-setup", OP0_IA_SETUP, SR0_IA_SETUP_DONE)) - return(FALSE); - return(TRUE); -} /* wavelan_hw_config */ + p = malloc(READSTR); + l = snprint(p, READSTR, "Interrupts: %lud\n", + ctlr->nints); + l+= snprint(p+l, READSTR-l, "TxPackets: %lud\n", + ctlr->ntx); + l+= snprint(p+l, READSTR-l, "RxPackets: %lud\n", + ctlr->nrx); + l+= snprint(p+l, READSTR-l, "TxErrors: %lud\n", + ctlr->ntxerr); + l+= snprint(p+l, READSTR-l, "RxErrors: %lud\n", + ctlr->nrxerr); + l+= snprint(p+l, READSTR-l, "TxRequests: %lud\n", + ctlr->ntxrq); + l+= snprint(p+l, READSTR-l, "AllocEvs: %lud\n", + ctlr->nalloc); + l+= snprint(p+l, READSTR-l, "InfoEvs: %lud\n", + ctlr->ninfo); + l+= snprint(p+l, READSTR-l, "InfoDrop: %lud\n", + ctlr->nidrop); + l+= snprint(p+l, READSTR-l, "WatchDogs: %lud\n", + ctlr->nwatchdogs); + if (ctlr->attached) + l+= snprint(p+l, READSTR-l, "Card attached"); + else + l+= snprint(p+l, READSTR-l, "Card not attached"); + if (ctlr->txbusy) + l+= snprint(p+l, READSTR-l, ", tx busy\n"); + else + l+= snprint(p+l, READSTR-l, "\n"); + + // real card stats + ilock(&ctlr->Lock); + l+= snprint(p+l, READSTR-l, "\nCard stats: \n"); + l+= snprint(p+l, READSTR-l, "Status: %ux\n", + csr_ins(ctlr, WR_Sts)); + l+= snprint(p+l, READSTR-l, "Event status: %ux\n", + csr_ins(ctlr, WR_EvSts)); + l+= snprint(p+l, READSTR-l, "Port type: %d\n", + ltv_ins(ctlr, WTyp_Ptype)); + l+= snprint(p+l, READSTR-l, "Transmit rate: %d\n", + ltv_ins(ctlr, WTyp_TxRate)); + l+= snprint(p+l, READSTR-l, "Channel: %d\n", + ltv_ins(ctlr, WTyp_Chan)); + l+= snprint(p+l, READSTR-l, "AP density: %d\n", + ltv_ins(ctlr, WTyp_ApDens)); + l+= snprint(p+l, READSTR-l, "Promiscuous mode: %d\n", + ltv_ins(ctlr, WTyp_Prom)); + + l+= snprint(p+l, READSTR-l, "SSID name: %s\n", + ltv_inname(ctlr, WTyp_NetName)); + l+= snprint(p+l, READSTR-l, "Net name: %s\n", + ltv_inname(ctlr, WTyp_WantName)); + l+= snprint(p+l, READSTR-l, "Node name: %s\n", + ltv_inname(ctlr, WTyp_NodeName)); + iunlock(&ctlr->Lock); + + l+= snprint(p+l, READSTR-l, "ntxuframes: %lud\n", + ctlr->ntxuframes); + l+= snprint(p+l, READSTR-l, "ntxmframes: %lud\n", + ctlr->ntxmframes); + l+= snprint(p+l, READSTR-l, "ntxfrags: %lud\n", + ctlr->ntxfrags); + l+= snprint(p+l, READSTR-l, "ntxubytes: %lud\n", + ctlr->ntxubytes); + l+= snprint(p+l, READSTR-l, "ntxmbytes: %lud\n", + ctlr->ntxmbytes); + l+= snprint(p+l, READSTR-l, "ntxdeferred: %lud\n", + ctlr->ntxdeferred); + l+= snprint(p+l, READSTR-l, "ntxsretries: %lud\n", + ctlr->ntxsretries); + l+= snprint(p+l, READSTR-l, "ntxmultiretries: %lud\n", + ctlr->ntxmultiretries); + l+= snprint(p+l, READSTR-l, "ntxretrylimit: %lud\n", + ctlr->ntxretrylimit); + l+= snprint(p+l, READSTR-l, "ntxdiscards: %lud\n", + ctlr->ntxdiscards); + l+= snprint(p+l, READSTR-l, "nrxuframes: %lud\n", + ctlr->nrxuframes); + l+= snprint(p+l, READSTR-l, "nrxmframes: %lud\n", + ctlr->nrxmframes); + l+= snprint(p+l, READSTR-l, "nrxfrags: %lud\n", + ctlr->nrxfrags); + l+= snprint(p+l, READSTR-l, "nrxubytes: %lud\n", + ctlr->nrxubytes); + l+= snprint(p+l, READSTR-l, "nrxmbytes: %lud\n", + ctlr->nrxmbytes); + l+= snprint(p+l, READSTR-l, "nrxfcserr: %lud\n", + ctlr->nrxfcserr); + l+= snprint(p+l, READSTR-l, "nrxdropnobuf: %lud\n", + ctlr->nrxdropnobuf); + l+= snprint(p+l, READSTR-l, "nrxdropnosa: %lud\n", + ctlr->nrxdropnosa); + l+= snprint(p+l, READSTR-l, "nrxcantdecrypt: %lud\n", + ctlr->nrxcantdecrypt); + l+= snprint(p+l, READSTR-l, "nrxmsgfrag: %lud\n", + ctlr->nrxmsgfrag); + snprint(p+l, READSTR-l, "nrxmsgbadfrag: %lud\n", + ctlr->nrxmsgbadfrag); + n = readstr(offset, a, n, p); + free(p); + return n; +} -static void -wavelan_graceful_shutdown(Ether *ether, int base) -{ - int status; - - /* First, send the LAN controller a stop receive command */ - wavelan_cmd(ether, base, "wavelan_graceful_shutdown(): stop-rcv", OP0_STOP_RCV, - SR0_NO_RESULT); - /* Then, spin until the receive unit goes idle */ - do { - outb(LCCR(base), (OP0_NOP | CR0_STATUS_3)); - status = inb(LCSR(base)); - } while((status & SR3_RCV_STATE_MASK) != SR3_RCV_IDLE); - - /* Now, spin until the chip finishes executing its current command */ - do { - outb(LCCR(base), (OP0_NOP | CR0_STATUS_3)); - status = inb(LCSR(base)); - } while ((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE); -} /* wavelan_graceful_shutdown */ +/* from ../port/netif.c + * BUG?: make me a library function. + */ +static char* +matchtoken(char *p, char *token) +{ + int n; + n = strlen(token); + if(strncmp(p, token, n)) + return 0; + p += n; + if(*p == 0) + return p; + if(*p != ' ' && *p != '\t' && *p != '\n') + return 0; + while(*p == ' ' || *p == '\t' || *p == '\n') + p++; + return p; +} -static void -wavelan_ru_start(Ether *ether, int base) +static void +termtoken(char *tok) +{ + char *p = tok; + + while(*p != 0 && *p != ' ' && *p != '\t' && *p != '\n') + p++; + *p = 0; +} + +#define min(a,b) (((a)<(b))?(a):(b)) +static long +ctl(Ether* ether, void* buf, long n) { + int i; + char *p; Ctlr *ctlr; - ctlr = ether->ctlr; + Cmdbuf *cb; + + if((ctlr = ether->ctlr) == nil) + error(Enonexist); + if(ctlr->attached == 0) + error(Eshutdown); + + cb = parsecmd(buf, n); + if(cb->nf < 2) + error(Ebadctl); + + ilock(&ctlr->Lock); + if(waserror()){ + iunlock(&ctlr->Lock); + free(cb); + nexterror(); + } - /* - * We need to start from a quiescent state. To do so, we could check - * if the card is already running, but instead we just try to shut - * it down. First, we disable reception (in case it was already enabled). - */ + if(strcmp(cb->f[0], "ssid") == 0) + strncpy(ctlr->netname, cb->f[1], WNameLen); + else if(strcmp(cb->f[0], "net") == 0) + strncpy(ctlr->wantname, cb->f[1], WNameLen); + else if(strcmp(cb->f[0], "node") == 0) + strncpy(ctlr->nodename, cb->f[1], WNameLen); + else if(strcmp(cb->f[0], "chan") == 0){ + i = atoi(cb->f[1]); + if (i < 1 || i > 16 ) + error("invalid wavelan channel"); + ctlr->chan = i; + } + else if(strcmp(cb->f[0], "ptype") == 0){ + i = atoi(cb->f[1]); + if (i < 1 || i > 3 ) + error("invalid wavelan port type"); + ctlr->ptype = i; + } + else + error(Ebadctl); + if(ctlr->txbusy) + w_txdone(ctlr, WTxErrEv|1); // retry later. + w_enable(ether); - wavelan_graceful_shutdown(ether, base); + iunlock(&ctlr->Lock); + poperror(); + free(cb); - /* Now we know that no command is being executed. */ + return n; +} - /* Set the receive frame pointer and stop pointer */ - ctlr->rfp = 0; - outb(LCCR(base), OP0_SWIT_TO_PORT_1 | CR0_CHNL); +static void +transmit(Ether* ether) +{ + Ctlr* ctlr = ether->ctlr; - /* Reset ring management. This sets the receive frame pointer to 1 */ - outb(LCCR(base), OP1_RESET_RING_MNGMT); - ctlr->stop = (0 + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE; - outb(LCCR(base), CR1_STOP_REG_UPDATE | (ctlr->stop >> RX_SIZE_SHIFT)); - outb(LCCR(base), OP1_INT_ENABLE); - outb(LCCR(base), OP1_SWIT_TO_PORT_0); + if (ctlr == 0) + return; - /* Reset receive DMA pointer */ - outb(HACR(base), HACR_PWR_STAT | HACR_RX_DMA_RESET); - delay(100); - outb(HACR(base), HACR_PWR_STAT); - delay(100); + ilock(&ctlr->Lock); + ctlr->ntxrq++; + w_txstart(ether,0); + iunlock(&ctlr->Lock); +} - /* Receive DMA on channel 1 */ - wavelan_cmd(ether, base, "wavelan_ru_start(): rcv-enable", - (CR0_CHNL | OP0_RCV_ENABLE), SR0_NO_RESULT); +static void +promiscuous(void* arg, int on) +{ + Ether* ether = (Ether*)arg; + Ctlr* ctlr = ether->ctlr; + + if (ctlr == nil) + error("card not found"); + if (ctlr->attached == 0) + error("card not attached"); + ilock(&ctlr->Lock); + ltv_outs(ctlr, WTyp_Prom, (on?1:0)); + iunlock(&ctlr->Lock); +} -} /* wavelan_ru_start */ -static void -transmit(Ether* ether) + +static void +interrupt(Ureg* ,void* arg) { - Ctlr *ctlr; - ctlr = ether->ctlr; + Ether* ether = (Ether*) arg; + Ctlr* ctlr = (Ctlr*) ether->ctlr; - ilock(&ctlr->wlock); - txstart(ether); - iunlock(&ctlr->wlock); + if (ctlr == 0) + return; + ilock(&ctlr->Lock); + ctlr->nints++; + w_intr(ether); + iunlock(&ctlr->Lock); } - static int reset(Ether* ether) { - int slot, p; - int port; - char *pp; - Ctlr *ctlr; - PCMmap *m; - - ctlr = ether->ctlr = malloc(sizeof(Ctlr)); - ilock(&ctlr->wlock); + Ctlr* ctlr; + Wltv ltv; + int i; - if(ether->port == 0) - ether->port = 0x280; - port = ether->port; - - if((slot = pcmspecial(ether->type, ether)) < 0) { - print("could not find the PCMCIA WaveLAN card.\n"); + if (ether->ctlr){ + print("#l%d: only one card supported\n", ether->ctlrno); return -1; } - if(ioalloc(port, 0x20, 0, "wavelan") < 0){ - print("wavelan: port %d in use\n", port); - return -1; + ether->arg = ctlr = (Ctlr*)emalloc(sizeof(Ctlr)); + ilock(&ctlr->Lock); + + if (ether->port==0) + ether->port=WDfltIOB; + ctlr->iob = ether->port; + if (ether->irq==0) + ether->irq=WDfltIRQ; + if ((ctlr->slot = pcmspecial("WaveLAN/IEEE", ether))<0){ + DEBUG("no wavelan found\n"); + goto abort; } - - print("#l%dWaveLAN: slot %d, port 0x%ulX irq %ld type %s\n", ether->ctlrno, slot, ether->port, ether->irq, ether->type); - - /* create a receive buffer */ - ctlr->rbp = rbpalloc(allocb); - -/* map a piece of memory (Attribute memory) first */ - m = pcmmap(slot, 0, 0x5000, 1); - if (m==0) { - iofree(port); - return 1; + DEBUG("#l%d: port=0x%lx irq=%ld\n", + ether->ctlrno, ether->port, ether->irq); + + *ctlr->netname = *ctlr->wantname = *ctlr->nodename = 0; + for (i=0; i < ether->nopt; i++){ + if (strncmp(ether->opt[i],"ssid=",4) == 0) + strncpy(ctlr->netname,ðer->opt[i][4],WNameLen); + if (strncmp(ether->opt[i],"net=",4) == 0) + strncpy(ctlr->wantname,ðer->opt[i][4],WNameLen); + if (strncmp(ether->opt[i],"node=",5) == 0) + strncpy(ctlr->nodename,ðer->opt[i][5],WNameLen); } -/* read ethernet address from the card and put in ether->ea */ - pp = (char*)(KZERO|m->isa) + 0x0E00 + 2*0x10; - for(p = 0; pea); p++) - ether->ea[p] = (uchar) (*(pp+2*p))&0xFF; - -// print("wavelan: dump of PSA memory\n"); -// pp = (char *) (KZERO|m->isa) + 0x0E00; -// for(p=0; p<64; p++) { -// print("%2uX ", (*(pp+2*p)&0xFF)); -// if (p%16==15) print("\n"); -// } - -/* read nwid from PSA into ctlr->nwid */ - pp = (char*)(KZERO|m->isa) + 0x0E00; - ctlr->nwid[0] = *(pp+2*0x23); - ctlr->nwid[1] = *(pp+2*0x24); - -/* access the configuration option register */ - pp = (char *)(KZERO|m->isa) + 0x4000; - *pp = *pp | COR_SW_RESET; - delay(5); - *pp = (COR_LEVEL_IRQ | COR_CONFIG); - delay(5); - - hacr_write_slow(port, HACR_RESET); - outb(HACR(port), HACR_DEFAULT); - - if(inb(HASR(port)) & HASR_NO_CLK) { - iofree(port); - print("wavelan: modem not connected\n"); - return 1; + ctlr->netname[WNameLen-1] = 0; + ctlr->wantname[WNameLen-1] = 0; + ctlr->nodename[WNameLen-1] =0; + + if (ioalloc(ether->port,WIOLen,0,"wavelan")<0){ + print("#l%d: port 0x%lx in use\n", + ether->ctlrno, ether->port); + goto abort; } - wavelan_mmc_init(ether, port); /* initialize modem */ - - outb(LCCR(port), OP0_RESET); /* reset the LAN controller */ - delay(10); - - if (wavelan_hw_config(port, ether) == FALSE){ - iofree(port); - return 1; + w_intdis(ctlr); + if (w_cmd(ctlr,WCmdIni,0)){ + print("#l%d: init failed\n", ether->ctlrno); + goto abort; } - - if (wavelan_diag(ether, port) == 1){ - iofree(port); - return 1; + w_intdis(ctlr); + ltv_outs(ctlr, WTyp_Tick, 8); + + ltv.type = WTyp_Mac; + ltv.len = 4; + if (w_inltv(ctlr, <v)){ + print("#l%d: unable to read mac addr\n", + ether->ctlrno); + goto abort; } - wavelan_ru_start(ether, port); - - print("wavelan: init done; receiver started\n"); - - iunlock(&ctlr->wlock); - - ctlr->port = port; - ether->port = port; - ether->mbps = 2; /* 2 Mpbs */ + memmove(ether->ea, ltv.addr, Eaddrlen); + DEBUG("#l%d: %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX\n", + ether->ctlrno, + ether->ea[0], ether->ea[1], ether->ea[2], + ether->ea[3], ether->ea[4], ether->ea[5]); + + ctlr->chan = ltv_ins(ctlr, WTyp_Chan); + + ctlr->ptype = WDfltPType; + ctlr->apdensity = WDfltApDens; + ctlr->rtsthres = WDfltRtsThres; + ctlr->txrate = WDfltTxRate; + ctlr->maxlen = WMaxLen; + ctlr->pmena = 0; + ctlr->pmwait= 100; + // link to ether + ether->ctlr = ctlr; + ether->mbps = 10; ether->attach = attach; - ether->transmit = transmit; ether->interrupt = interrupt; + ether->transmit = transmit; ether->ifstat = ifstat; - + ether->ctl = ctl; ether->promiscuous = promiscuous; ether->multicast = multicast; ether->arg = ether; - return 0; /* reset succeeded */ + DEBUG("#l%d: irq %ld port %lx type %s", + ether->ctlrno, ether->irq, ether->port, ether->type); + DEBUG(" %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]); + + iunlock(&ctlr->Lock); + return 0; + +abort: + iunlock(&ctlr->Lock); + free(ctlr); + ether->ctlr = nil; + return -1; } void etherwavelanlink(void) { - addethercard("WaveLAN", reset); + addethercard("wavelan", reset); }