## diffname pc/etherwavelan.c 1998/0625 ## diff -e /dev/null /n/emeliedump/1998/0625/sys/src/brazil/pc/etherwavelan.c 0a /* */ #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" static int reset(Ether* ether) { int slot; if(ether->irq == 0) ether->irq = 10; if(ether->port == 0) ether->port = 0x240; if((slot = pcmspecial(ether->type, ether)) < 0) return -1; print("WaveLAN: slot %d, port 0x%uX irq %d\n", slot, ether->port, ether->irq); return -1; } void etherwavelanlink(void) { addethercard("WaveLAN", reset); } . ## diffname pc/etherwavelan.c 1999/0623 ## diff -e /n/emeliedump/1998/0625/sys/src/brazil/pc/etherwavelan.c /n/emeliedump/1999/0623/sys/src/brazil/pc/etherwavelan.c 29c 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) { return 1; } /* 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) { print("wavelan: modem not connected\n"); return 1; } 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) return 1; if (wavelan_diag(ether, port) == 1) return 1; 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 */ ether->attach = attach; ether->transmit = transmit; ether->interrupt = interrupt; ether->ifstat = ifstat; ether->promiscuous = promiscuous; ether->multicast = multicast; ether->arg = ether; return 0; /* reset succeeded */ . 27d 25a } . 24c if((slot = pcmspecial(ether->type, ether)) < 0) { print("could not find the PCMCIA WaveLAN card.\n"); . 22c ether->port = 0x280; port = ether->port; . 19,20c ctlr = ether->ctlr = malloc(sizeof(Ctlr)); ilock(&ctlr->wlock); . 17c int slot, p; int port; char *pp; Ctlr *ctlr; PCMmap *m; . 13a static void wavelan_receive(Ether *ether); static int txstart(Ether *ether); static void hacr_write_slow(int base, uchar hacr) { outb(HACR(base), hacr); /* delay might only be needed sometimes */ delay(1); } /* hacr_write_slow */ static long ifstat(Ether* ether, void* a, long n, ulong offset) { Ctlr *ctlr; int len; char *p; 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); n = readstr(offset, a, n, p); free(p); return n; } static void attach(Ether* ether) { Ctlr *ctlr; ctlr = ether->ctlr; ilock(&ctlr->wlock); if(ctlr->attached){ iunlock(&ctlr->wlock); return; } ctlr->attached = 1; iunlock(&ctlr->wlock); } static void interrupt_handler(Ether *ether, uchar status) { Ctlr *ctlr; int status0; int tx_status; int base; ctlr = ether->ctlr; base = ctlr->port; status0 = status; /* 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 */ } static Block* rbpalloc(Block* (*f)(int)) { Block *bp; ulong addr; /* * 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; } return bp; } static int wavelan_cmd(Ether *ether, int base, char *str, int cmd, int result) { int status; unsigned long spin; /* Spin until the chip finishes executing its current command (if any) */ do { outb(LCCR(base), OP0_NOP | CR0_STATUS_3); status = inb(LCSR(base)); } while ((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE); outb(LCCR(base), cmd); /* Send the command */ if(result == SR0_NO_RESULT) { /* Return immediately, if the command doesn't return a result */ return(TRUE); } /* 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((status & SR0_EVENT_MASK) != result){ print("wavelan_cmd: %s failed, status0 = 0x%uX\n", str, status); return 0; } return 1; } /* wavelan_cmd */ static uchar mmc_in(int base, uchar o) { while (inb(HASR(base)) & HASR_MMI_BUSY) ; /* Wait for MMC to go idle */ outb(MMR(base), o << 1); /* Set the read address */ outb(MMD(base), 0); /* Required dummy write */ while (inb(HASR(base)) & HASR_MMI_BUSY) ; /* Wait for MMC to go idle */ return((uchar) (inb(MMD(base)))); } 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 wavelan_hardware_send_packet(Ether *ether, void *buf, short length) { Ctlr *ctlr; int base; register ushort xmtdata_base = TX_BASE; ctlr = ether->ctlr; base = ctlr->port; 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 */ /* 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 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++; } else{ ctlr->txbp = bp; if(ctlr->txbusy == 0){ ctlr->txbusy = 1; } break; } } return 0; } /* wavelan txstart */ static int wavelan_start_of_frame(Ether *ether, int rfp, int wrap) { Ctlr *ctlr; int base; int rp, len; ctlr = ether->ctlr; base = ctlr->port; 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; 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); 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); } 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; ctlr->rx_packets++; 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 */ static void wavelan_receive(Ether *ether) { 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); ctlr->rfp = rp; /* one packet processed, skip it */ } /* * 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 */ static void interrupt(Ureg*, void* arg) { Ether *ether; Ctlr *ctlr; int base; ether = arg; ctlr = ether->ctlr; base = ctlr->port; ilock(&ctlr->wlock); ctlr->interrupts++; outb(LCCR(base), CR0_STATUS_0 | OP0_NOP); interrupt_handler(ether, inb(LCSR(base))); iunlock(&ctlr->wlock); }; /* wavelan interrupt */ static void promiscuous() { ; }; static void multicast() { ; }; static void mmc_read(int base, uchar o, uchar *b, int n) { 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++; 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"); } static void mmc_write_b(int base, uchar o, uchar b) { 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 */ 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 */ static void wavelan_mmc_init(Ether *ether, int port) { 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)){ 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 */ 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 */ static void wavelan_ru_start(Ether *ether, int base) { Ctlr *ctlr; ctlr = ether->ctlr; /* * 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). */ wavelan_graceful_shutdown(ether, base); /* Now we know that no command is being executed. */ /* Set the receive frame pointer and stop pointer */ ctlr->rfp = 0; outb(LCCR(base), OP0_SWIT_TO_PORT_1 | CR0_CHNL); /* 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); /* Reset receive DMA pointer */ outb(HACR(base), HACR_PWR_STAT | HACR_RX_DMA_RESET); delay(100); outb(HACR(base), HACR_PWR_STAT); delay(100); /* Receive DMA on channel 1 */ wavelan_cmd(ether, base, "wavelan_ru_start(): rcv-enable", (CR0_CHNL | OP0_RCV_ENABLE), SR0_NO_RESULT); } /* wavelan_ru_start */ static void transmit(Ether* ether) { Ctlr *ctlr; ctlr = ether->ctlr; ilock(&ctlr->wlock); txstart(ether); iunlock(&ctlr->wlock); } . 12a #include "etherwavelan.h" . 11d 2a . 1a * 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 . ## diffname pc/etherwavelan.c 1999/0714 ## diff -e /n/emeliedump/1999/0623/sys/src/brazil/pc/etherwavelan.c /n/emeliedump/1999/0714/sys/src/brazil/pc/etherwavelan.c 859a } . 858c if (wavelan_diag(ether, port) == 1){ iofree(port); . 856a } . 855c if (wavelan_hw_config(port, ether) == FALSE){ iofree(port); . 845a iofree(port); . 815a iofree(port); . 806a if(ioalloc(port, 0x20, 0, "wavelan") < 0){ print("wavelan: port %d in use\n", port); return -1; } . ## diffname pc/etherwavelan.c 2001/0118 ## diff -e /n/emeliedump/1999/0714/sys/src/brazil/pc/etherwavelan.c /n/emeliedump/2001/0118/sys/src/9/pc/etherwavelan.c 894c addethercard("wavelan", reset); . 888c 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; . 883c ether->ctl = ctl; . 881a ether->transmit = transmit; . 880d 876,878c 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; } 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; . 874c w_intdis(ctlr); if (w_cmd(ctlr,WCmdIni,0)){ print("#l%d: init failed\n", ether->ctlrno); goto abort; } w_intdis(ctlr); ltv_outs(ctlr, WTyp_Tick, 8); . 872c if (ioalloc(ether->port,WIOLen,0,"wavelan")<0){ print("#l%d: port 0x%lx in use\n", ether->ctlrno, ether->port); goto abort; } . 870c 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); } ctlr->netname[WNameLen-1] = 0; ctlr->wantname[WNameLen-1] = 0; ctlr->nodename[WNameLen-1] =0; . 866,868c 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; . 861,863c static void interrupt(Ureg* ,void* arg) { Ether* ether = (Ether*) arg; Ctlr* ctlr = (Ctlr*) ether->ctlr; if (ctlr == 0) return; ilock(&ctlr->Lock); ctlr->nints++; w_intr(ether); iunlock(&ctlr->Lock); } static int reset(Ether* ether) { Ctlr* ctlr; Wltv ltv; int i; if (ether->ctlr){ print("#l%d: only one card supported\n", ether->ctlrno); return -1; . 858,859d 856d 850,854c 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); } . 847,848c static void promiscuous(void* arg, int on) { Ether* ether = (Ether*)arg; Ctlr* ctlr = ether->ctlr; . 835,845c ilock(&ctlr->Lock); ctlr->ntxrq++; w_txstart(ether,0); iunlock(&ctlr->Lock); } . 828,833c if (ctlr == 0) return; . 817,826c static void transmit(Ether* ether) { Ctlr* ctlr = ether->ctlr; . 814,815c return n; } . 812c iunlock(&ctlr->Lock); poperror(); free(cb); . 810a 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); . 807,809c 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; . 802,804c ilock(&ctlr->Lock); if(waserror()){ iunlock(&ctlr->Lock); free(cb); nexterror(); . 798,800c cb = parsecmd(buf, n); if(cb->nf < 2) error(Ebadctl); . 795,796c if((ctlr = ether->ctlr) == nil) error(Enonexist); if(ctlr->attached == 0) error(Eshutdown); . 793c Cmdbuf *cb; . 789,791c int i; char *p; . 786,787c 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) . 784a static void termtoken(char *tok) { char *p = tok; . 780,782c 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; . 777,778c int n; . 772,775c /* from ../port/netif.c * BUG?: make me a library function. */ static char* matchtoken(char *p, char *token) . 768,770c 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; } . 762,766c 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); . 755,760c // 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)); . 751,753c if (n == 0 || ctlr == 0){ return 0; } 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"); . 749c static long ifstat(Ether* ether, void* a, long n, ulong offset) { Ctlr* ctlr = (Ctlr*) ether->ctlr; char* p; int l; . 747c 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); } } . 741,745c if (ether->ctlr == 0) return; . 738,739c Ctlr* ctlr; char name[NAMELEN]; int rc; . 734,736c static void attach(Ether* ether) . 716,732c // BUG: to be added. } . 699,714c static void multicast(void*, uchar*, int) . 692,697d 686,690d 629,684c void *r=malloc(size); if (!r) error(Enomem); memset(r,0,size); return r; } . 627c static void* emalloc(ulong size) . 625a 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); } . 624a if (csr_ins(ctlr, WR_EvSts)&WEvs) w_intr(ether); . 615,623c // 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. . 613a ilock(&ctlr->Lock); . 612c for(;;){ tsleep(&ctlr->timer, return0, 0, 50); ctlr = (Ctlr*)ether->ctlr; if (ctlr == 0) break; if (ctlr->attached == 0) continue; tick++; . 598,610c static void w_timer(void* arg) { Ether* ether = (Ether*) arg; Ctlr* ctlr = (Ctlr*)ether->ctlr; int tick=0; . 576,596c // 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. . 574c 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); } } . 570,572c int rc, txid, i; Ctlr* ctlr = (Ctlr*) ether->ctlr; . 568c static void w_intr(Ether *ether) . 566a 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; } . 561,565c int sp,i; ushort rc; Wltv l; ulong* p = (ulong*)&ctlr->WStats; ulong* pend= (ulong*)&ctlr->end; . 558,559c 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++; } static int w_stats(Ctlr* ctlr) . 553,556c int b = ctlr->txbusy; . 550,551c static void w_txdone(Ctlr* ctlr, int sts) . 542,547c 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; . 540a 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)); . 539d 535,537c 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; txid = ctlr->txdid; if (again){ bp = 0; // a watchdog reenabled the card. goto retry; // must retry a previously failed tx. . 530,533d 527,528c ctlr->nrx++; etheriq(ether,bp,1); return; rxerror: freeb(bp); ctlr->nrxerr++; } static int w_txstart(Ether* ether, int again) . 521,525c 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; } . 518,519c Ctlr* ctlr = (Ctlr*) ether->ctlr; ushort sp; WFrame f; Block* bp=0; ulong l; Etherpkt* ep; . 516c w_rxdone(Ether* ether) . 514a . 513a } . 512a if (w_cmd(ctlr, WCmdEna, 0)){ DEBUG("wavelan: Enable failed"); return -1; } ctlr->txdid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8); ctlr->txmid = w_alloc(ctlr, 1518 + sizeof(WFrame) + 8); if (ctlr->txdid == -1 || ctlr->txmid == -1) DEBUG("wavelan: alloc failed"); ctlr->txbusy= 0; w_intena(ctlr); return 0; . 511c // BUG: set multicast addresses . 506,509c ltv_outs(ctlr, WTyp_Prom, (ether->prom?1:0)); . 504c 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); . 502c w_intdis(ctlr); w_cmd(ctlr, WCmdDis, 0); w_intdis(ctlr); if(w_cmd(ctlr, WCmdIni, 0)) return -1; . 498,500c if (!ctlr) return -1; . 494,496c Wltv l; Ctlr* ctlr = (Ctlr*) ether->ctlr; . 491,492c if (w_cmd(ctlr, WCmdMalloc, len)==0) 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); } . 391,393c int i,len; ushort *p; . 386,389c static void w_outltv(Ctlr* ctlr, Wltv* l) . 384a p = &l->val; len--; for (i=0; i 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; . 375,378c if (w_seek(ctlr,l->type,0,1)){ DEBUG("wavelan: seek failed\n"); return -1; . 370,372c if (w_cmd(ctlr, WCmdAccRd, l->type)){ DEBUG("wavelan: access read failed\n"); . 364,368c static int w_inltv(Ctlr* ctlr, Wltv* l) { int i, len; ushort *p,code; . 361,362c if (chan != 0 && chan != 1) panic("wavelan: bad chan\n"); csr_outs(ctlr, sel[chan], id); csr_outs(ctlr, off[chan], offset); for (i=0; iiob+(r),(arg)) #define csr_ins(ctlr,r) ins((ctlr)->iob+(r)) #define csr_ack(ctlr,ev) outs((ctlr)->iob+WR_EvAck,(ev)) . 207,211c WF_802_11_Off = 0x44, WF_802_3_Off = 0x2e, }; . 201,205c WSnapK1 = 0xaa, WSnapK2 = 0x00, WSnapCtlr = 0x03, WSnap0 = (WSnapK1|(WSnapK1<<8)), WSnap1 = (WSnapK2|(WSnapCtlr<<8)), WSnapHdrLen = 6, . 195,199c WF_Data = 0x0008, . 192,193c WF_Err = 0x0003, WF_1042 = 0x2000, WF_Tunnel = 0x4000, WF_WMP = 0x6000, . 182,190c // Frame stuff . 176,180c WR_EvAck = 0x34, WR_Data0 = 0x36, WR_Data1 = 0x38, . 174a // 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, . 173c WMaxLen = 2304, WNameLen = 32, . 94,171c WDfltApDens = 1, WDfltRtsThres = 2347, // == disabled WDfltTxRate = WTxAuto, // 2Mbps . 92c WPTypeManaged = 1, WPTypeWDS = 2, WPTypeAdHoc = 3, WDfltPType = WPTypeManaged, . 82,90c WTmOut = 65536, // Cmd time out . 72,80c WIOLen = 0x40, // Hermes IO length . 70c WDfltIRQ = 3, // default irq WDfltIOB = 0x100, // default IO base . 67,68c // Controller enum . 62,65d 40,60c // 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, }; . 33,38c 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; }; . 28,31c 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; }; . 25,26c typedef struct Ctlr Ctlr; typedef struct Wltv Wltv; typedef struct WFrame WFrame; typedef struct Stats Stats; typedef struct WStats WStats; struct WStats . 22,23c #define DEBUG if(1)print . 19d 2,7c 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. . ## diffname pc/etherwavelan.c 2001/0119 ## diff -e /n/emeliedump/2001/0118/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0119/sys/src/9/pc/etherwavelan.c 1056c if(i < 1 || i > 3 ) . 1050c if(i < 1 || i > 16 ) . 1022d ## diffname pc/etherwavelan.c 2001/0121 ## diff -e /n/emeliedump/2001/0119/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0121/sys/src/9/pc/etherwavelan.c 1225a . 1184,1186c if (ctlr->chan == 0) ctlr->chan = ltv_ins(ctlr, WTyp_Chan); . 1145,1152c for (i=0; i < ether->nopt; i++) setopt(ctlr, ether->opt[i], ether->ctlrno); . 1143c ctlr->chan = 0; ctlr->ptype= WDfltPType; ctlr->keyid= 0; . 1116a static void setopt(Ctlr* ctlr, char* opt, int no) { int i; char k[64]; char *kn = &k[3]; WKey *kp; char *ke; int key; if (strncmp(opt,"essid=",6) == 0){ if (ctlr->ptype == 3) strncpy(ctlr->netname,opt+6,WNameLen); else strncpy(ctlr->wantname,opt+6,WNameLen); iprint("essid %d %s\n", ctlr->ptype, opt+6); } else if (strncmp(opt,"station_name=",13) == 0){ // BUG? // In my kernel, it seems that the max length of // opt is 16, and I get "na" as node name // when I say station_name=nautilus. strncpy(ctlr->nodename, opt+13,WNameLen); iprint("nodename %s\n", ctlr->nodename); } else if (strncmp(opt, "channel=",8) == 0){ i = atoi(opt+8); if(i < 1 || i > 16 ) print("#l%d: channel (%d) not in [1-16]\n",no,i); else ctlr->chan = i; iprint("chan %d\n", i); } else if (strncmp(opt, "ptype=",6) == 0){ i = atoi(opt+6); if(i < 1 || i > 3 ) print("#l%d: port type (%d) not in [1-3]\n",no,i); else ctlr->ptype = i; iprint("ptype %d\n", i); } else if (strncmp(opt, "key", 3) == 0){ if (opt[3] == 0 || opt[4] != '='){ print("#l%d: key option is not keyX=xxxx\n", no); return; } strncpy(k,opt,64); key = atoi(kn); if (key < 1 || key > WNKeys){ print("#l%d: key number (%d) not in [1-%d]\n", no, key, WNKeys); return; } ctlr->keyid = key; kp = &ctlr->key[key-1]; ke = opt+5; while (*ke && *ke != ' ' && *ke != '\n' && *ke != '\t') ke++; kp->len = ke - (opt+5); if (kp->len > WKeyLen) kp->len = WKeyLen; memset(kp->dat, 0, sizeof(kp->dat)); strncpy(kp->dat, opt+5, kp->len); if (kp->len > WMinKeyLen) kp->len = WKeyLen; else if (kp->len > 0) kp->len = WMinKeyLen; iprint("key %d <%s> len %d\n", key, kp->dat, kp->len); } } . 1062c w_txdone(ctlr, WTxErrEv|1); // retry tx later. . 1060a . 1058a else if(strncmp(cb->f[0], "key", 3) == 0){ strncpy(k,cb->f[0],64); if (strcmp(cb->f[1], "off") == 0) ctlr->keyid = 0; else if (strcmp(cb->f[1], "exclude") == 0){ ctlr->xclear = 1; } else if (strcmp(cb->f[1], "include") == 0){ ctlr->xclear = 0; } else { key = atoi(kn); if (key < 1 || key > WNKeys) error("key not in [1-4]"); ctlr->keyid = key; kp = &ctlr->key[key-1]; kp->len = strlen(cb->f[1]); if (kp->len > WKeyLen) kp->len = WKeyLen; memset(kp->dat, 0, sizeof(kp->dat)); strncpy(kp->dat, cb->f[1], kp->len); if (kp->len > WMinKeyLen) kp->len = WKeyLen; else if (kp->len > 0) kp->len = WMinKeyLen; } } . 1056c error("port type not in [1-3]"); . 1050c error("channel not in [1-16]\n"); . 1047c } else if(strcmp(cb->f[0], "channel") == 0){ . 1041,1045c if(strcmp(cb->f[0], "essid") == 0){ if (strcmp(cb->f[1],"default") == 0) nessid = ""; else nessid = cb->f[1]; if (ctlr->ptype == 3){ memset(ctlr->netname, 0, sizeof(ctlr->netname)); strncpy(ctlr->netname, nessid, WNameLen); } else { memset(ctlr->wantname, 0, sizeof(ctlr->wantname)); strncpy(ctlr->wantname, nessid, WNameLen); } } else if(strcmp(cb->f[0], "station_name") == 0){ memset(ctlr->nodename, 0, sizeof(ctlr->nodename)); . 1030a . 1023a char *nessid; WKey *kp; char k[64]; char *kn= &k[3]; int key; . 986,1015d 984a #undef PRINTSTR #undef PRINTSTAT . 939,980c PRINTSTAT("ntxuframes: %lud\n", ctlr->ntxuframes); PRINTSTAT("ntxmframes: %lud\n", ctlr->ntxmframes); PRINTSTAT("ntxfrags: %lud\n", ctlr->ntxfrags); PRINTSTAT("ntxubytes: %lud\n", ctlr->ntxubytes); PRINTSTAT("ntxmbytes: %lud\n", ctlr->ntxmbytes); PRINTSTAT("ntxdeferred: %lud\n", ctlr->ntxdeferred); PRINTSTAT("ntxsretries: %lud\n", ctlr->ntxsretries); PRINTSTAT("ntxmultiretries: %lud\n", ctlr->ntxmultiretries); PRINTSTAT("ntxretrylimit: %lud\n", ctlr->ntxretrylimit); PRINTSTAT("ntxdiscards: %lud\n", ctlr->ntxdiscards); PRINTSTAT("nrxuframes: %lud\n", ctlr->nrxuframes); PRINTSTAT("nrxmframes: %lud\n", ctlr->nrxmframes); PRINTSTAT("nrxfrags: %lud\n", ctlr->nrxfrags); PRINTSTAT("nrxubytes: %lud\n", ctlr->nrxubytes); PRINTSTAT("nrxmbytes: %lud\n", ctlr->nrxmbytes); PRINTSTAT("nrxfcserr: %lud\n", ctlr->nrxfcserr); PRINTSTAT("nrxdropnobuf: %lud\n", ctlr->nrxdropnobuf); PRINTSTAT("nrxdropnosa: %lud\n", ctlr->nrxdropnosa); PRINTSTAT("nrxcantdecrypt: %lud\n", ctlr->nrxcantdecrypt); PRINTSTAT("nrxmsgfrag: %lud\n", ctlr->nrxmsgfrag); PRINTSTAT("nrxmsgbadfrag: %lud\n", ctlr->nrxmsgbadfrag); USED(l); . 915,936c PRINTSTR("\nCard stats: \n"); PRINTSTAT("Status: %ux\n", csr_ins(ctlr, WR_Sts)); PRINTSTAT("Event status: %ux\n", csr_ins(ctlr, WR_EvSts)); PRINTSTAT("Port type: %d\n", ltv_ins(ctlr, WTyp_Ptype)); PRINTSTAT("Transmit rate: %d\n", ltv_ins(ctlr, WTyp_TxRate)); PRINTSTAT("Channel: %d\n", ltv_ins(ctlr, WTyp_Chan)); PRINTSTAT("AP density: %d\n", ltv_ins(ctlr, WTyp_ApDens)); PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom)); PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName)); PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName)); PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName)); if (ltv_ins(ctlr, WTyp_HasCrypt) == 0) PRINTSTR("WEP: not supported\n"); else { if (ltv_ins(ctlr, WTyp_Crypt) == 0) PRINTSTR("WEP: disabled\n"); else{ PRINTSTR("WEP: enabled\n"); i = ltv_ins(ctlr, WTyp_XClear); k = ((i) ? "excluded" : "included"); PRINTSTAT("Clear packets: %s\n", k); txid = ltv_ins(ctlr, WTyp_TxKey); PRINTSTAT("Transmit key id: %d\n", txid); if (txid >= 1 && txid <= WNKeys){ k = ltv_inkey(ctlr, txid); if (SEEKEYS && k != nil) PRINTSTAT("Transmit key: %s\n", k); } } } . 912a if (ctlr->keyid){ PRINTSTR("Keys: "); for (i = 0; i < WNKeys; i++) if (ctlr->key[i].len == 0) PRINTSTR("none "); else { if (SEEKEYS == 0) PRINTSTR("set "); else { PRINTSTAT("%s ", ctlr->key[i].dat); } } PRINTSTR("\n"); } . 884,911c l = 0; PRINTSTAT("Interrupts: %lud\n", ctlr->nints); PRINTSTAT("TxPackets: %lud\n", ctlr->ntx); PRINTSTAT("RxPackets: %lud\n", ctlr->nrx); PRINTSTAT("TxErrors: %lud\n", ctlr->ntxerr); PRINTSTAT("RxErrors: %lud\n", ctlr->nrxerr); PRINTSTAT("TxRequests: %lud\n", ctlr->ntxrq); PRINTSTAT("AllocEvs: %lud\n", ctlr->nalloc); PRINTSTAT("InfoEvs: %lud\n", ctlr->ninfo); PRINTSTAT("InfoDrop: %lud\n", ctlr->nidrop); PRINTSTAT("WatchDogs: %lud\n", ctlr->nwatchdogs); k = ((ctlr->attached) ? "attached" : "not attached"); PRINTSTAT("Card %s", k); k = ((ctlr->txbusy)? ", txbusy" : ""); PRINTSTAT("%s\n", k); . 878c int l, i; int txid; char* k; . 872a #define PRINTSTAT(fmt,val) l += snprint(p+l, READSTR-l, (fmt), (val)) #define PRINTSTR(fmt) l += snprint(p+l, READSTR-l, (fmt)) . 818a } . 817c if (w_enable(ether)){ . 536a wep = ltv_ins(ctlr, WTyp_HasCrypt); iprint("wep %d\n", wep); if (wep) if (ctlr->keyid == 0) ltv_outs(ctlr, WTyp_Crypt, 0); else { // BUG? // I think it's ok, but don't know if // the card needs a WEPing access point // just to admit the keys. ltv_outs(ctlr, WTyp_TxKey, ctlr->keyid); memset(&l, 0, sizeof(l)); l.len = sizeof(ctlr->key[0])*WNKeys/2 + 1; l.type= WTyp_Keys; memmove(l.key, &ctlr->key[0], sizeof(l.key[0])*WNKeys); w_outltv(ctlr, &l); ltv_outs(ctlr, WTyp_Crypt, wep); ltv_outs(ctlr, WTyp_XClear, ctlr->xclear); } . 503a ushort wep; . 425a static char* ltv_inkey(Ctlr* ctlr, int id) { static char k[WKeyLen+1]; Wltv l; int len; int i; id--; memset(&l, 0, sizeof(l)); l.type = WTyp_Keys; l.len = sizeof(l.key)/2 + 2; // BUG? if (w_inltv(ctlr, &l)) return Unkname; if (id <0 || id >=WNKeys){ DEBUG("wavelan: ltv_inkey: bad key id\n"); return Unkname; } for (i=0 ; i WKeyLen) len = WKeyLen; memset(k, 0, sizeof(k)); memmove(k, l.key[id].dat, len); k[WKeyLen] = 0; return k; } . 421c return Nilname; . 419c return Unkname; . 412d 408a static char Unkname[] = "who knows"; static char Nilname[] = "card does not tell"; . 261a int keyid; // 0 if not using WEP int xclear; WKey key[WNKeys]; . 216a struct { WKey key[WNKeys]; }; . 197a struct WKey { ushort len; char dat[WKeyLen]; }; . 191a . 132a WNKeys = 4, WKeyLen = 14, WMinKeyLen = 5, . 106a WTyp_Keys = 0xfcb0, WTyp_TxKey = 0xfcb1, WTyp_HasCrypt= 0xfd4f, . 99a WTyp_Crypt = 0xfc20, WTyp_XClear= 0xfc22, . 33a typedef struct WKey WKey; . 28a #define SEEKEYS 1 . 13c WEP is not tested (see BUGs too). . ## diffname pc/etherwavelan.c 2001/0122 ## diff -e /n/emeliedump/2001/0121/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0122/sys/src/9/pc/etherwavelan.c 1408d 1304,1306c Ctlr* ctlr; Wltv ltv; int i; . 1297d 1269d 1261d 1253d 1245d 1238d 1233c int i, key; . 1219,1220c Ether* ether = (Ether*) arg; Ctlr* ctlr = (Ctlr*) ether->ctlr; . 1202,1203c Ether* ether = (Ether*)arg; Ctlr* ctlr = ether->ctlr; . 1170a else if(strcmp(cb->f[0], "pm") == 0){ if(strcmp(cb->f[1], "off") == 0) ctlr->pmena = 0; else if(strcmp(cb->f[1], "on") == 0){ ctlr->pmena = 1; if(cb->nf == 3){ i = atoi(cb->f[2]); // check range here? what are the units? ctlr->pmwait = i; } } else error(Ebadctl); } . 1144c else if(strcmp(cb->f[0], "key") == 0){ . 1095d 1088c int i, key; . 1084d 979,983c Ctlr *ctlr = (Ctlr*) ether->ctlr; char *k, *p; int l, i, txid; . 974c #define PRINTSTR(fmt) l += snprint(p+l, READSTR-l, (fmt)) . 952,954c Ctlr* ctlr; char name[NAMELEN]; int rc; . 879,880c Ether* ether = (Ether*) arg; Ctlr* ctlr = (Ctlr*)ether->ctlr; . 823,824c int rc, txid, i; Ctlr* ctlr = (Ctlr*) ether->ctlr; . 798,802c int sp,i; ushort rc; Wltv l; ulong* p = (ulong*)&ctlr->WStats; ulong* pend= (ulong*)&ctlr->end; . 718,719c Ctlr* ctlr = (Ctlr*) ether->ctlr; Block* bp; . 656,660c Ctlr* ctlr = (Ctlr*) ether->ctlr; ushort sp; WFrame f; Block* bp=0; ulong l; . 616d 600a ltv_outs(ctlr, WTyp_PM, ctlr->pmena); . 599d 579,581c Wltv l; Ctlr* ctlr = (Ctlr*) ether->ctlr; ushort wep; . 527,528c ulong l = len / 2; int i,tries; . 507c int i, n; . 488,489c // else // DEBUG("none "); . 460,461c int i, len; . 436c static char Unkname[] = "who knows"; . 431c l.val = len; // l.s[0] and l.s[1] . 314a . 285,286c int keyid; // 0 if not using WEP int xclear; . 283c int txlen; . 262,276c 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; . 254c ulong nwatchdogs; // transmits time outs, actually . 251c ulong nalloc; // allocation (reclaim) events . 208,209c #define csr_ins(ctlr,r) ins((ctlr)->iob+(r)) #define csr_ack(ctlr,ev) outs((ctlr)->iob+WR_EvAck,(ev)) . 151c WCmdAskStats = 0x0011, . 136c WDfltTxRate = WTxAuto, // 2Mbps . 127c WTmOut = 65536, // Cmd time out . 125c WIOLen = 0x40, // Hermes IO length . 122,123c WDfltIRQ = 3, // default irq WDfltIOB = 0x100, // default IO base . 92,114c 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_Crypt = 0xfc20, WTyp_XClear = 0xfc22, WTyp_Tick = 0xfce0, WTyp_RtsThres = 0xfc83, WTyp_TxRate = 0xfc84, WTx1Mbps = 0x0, WTx2Mbps = 0x1, WTxAuto = 0x3, WTyp_Prom = 0xfc85, WTyp_Keys = 0xfcb0, WTyp_TxKey = 0xfcb1, WTyp_HasCrypt = 0xfd4f, . 66,85c 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; . 50,54c ulong nrxuframes; // unicast frames ulong nrxmframes; // multicast frames ulong nrxfrags; // fragments ulong nrxubytes; // unicast bytes ulong nrxmbytes; // multicast bytes . 40,44c ulong ntxuframes; // unicast frames ulong ntxmframes; // multicast frames ulong ntxfrags; // fragments ulong ntxubytes; // unicast bytes ulong ntxmbytes; // multicast bytes . 36c typedef struct WKey WKey; . ## diffname pc/etherwavelan.c 2001/0123 ## diff -e /n/emeliedump/2001/0122/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0123/sys/src/9/pc/etherwavelan.c 292d 254c ulong nwatchdogs; // transmit time outs, actually . 13c (see BUGs). . ## diffname pc/etherwavelan.c 2001/0124 ## diff -e /n/emeliedump/2001/0123/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0124/sys/src/9/pc/etherwavelan.c 1139c else if(strncmp(cb->f[0], "key", 3) == 0){ . ## diffname pc/etherwavelan.c 2001/0125 ## diff -e /n/emeliedump/2001/0124/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0125/sys/src/9/pc/etherwavelan.c 1334c *ctlr->netname = *ctlr->wantname = 0; strcpy(ctlr->nodename, "wvlancard"); . 1255,1258c // The max. length of an 'opt' is ISAOPTLEN in dat.h. // It should be > 16 to give reasonable name lengths. memset(ctlr->nodename, 0, sizeof(ctlr->nodename)); . 1252c nessid = opt+6; if (ctlr->ptype == 3){ memset(ctlr->netname, 0, sizeof(ctlr->netname)); strncpy(ctlr->netname, nessid, WNameLen); } else{ memset(ctlr->wantname, 0, sizeof(ctlr->wantname)); strncpy(ctlr->wantname, nessid, WNameLen); } . 1249,1250c if (strcmp(opt+6, "default") == 0) nessid = ""; . 1246c int key; . 1242,1243c int i; char k[64], *ke, *nessid; . 1027c if(i == 3) PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName)); else PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName)); . 1022c i = ltv_ins(ctlr, WTyp_Ptype); PRINTSTAT("Port type: %d\n", i); . 628,629c memmove(l.key, &ctlr->key[0], sizeof(l.key[0])*WNKeys); . 430,432c l.len = (sizeof(l.type)+sizeof(l.slen)+sizeof(l.s))/2; l.slen = (len+1) & ~1; strncpy(l.s, val, len); . 427,428c len = strlen(val); if(len > sizeof(l.s)) len = sizeof(l.s); memset(&l, 0, sizeof(l)); . 231c ushort slen; char s[WNameLen]; . 117,118d 113a WTyp_CurName = 0xfd41, . 13d ## diffname pc/etherwavelan.c 2001/0126 ## diff -e /n/emeliedump/2001/0125/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0126/sys/src/9/pc/etherwavelan.c 1308,1311c // if (kp->len > WMinKeyLen) // kp->len = WKeyLen; // else if (kp->len > 0) // kp->len = WMinKeyLen; . 1164,1167c // if (kp->len > WMinKeyLen) // kp->len = WKeyLen; // else if (kp->len > 0) // kp->len = WMinKeyLen; . 12a BUGS: Endian, alignment and mem/io issues? . ## diffname pc/etherwavelan.c 2001/0127 ## diff -e /n/emeliedump/2001/0126/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0127/sys/src/9/pc/etherwavelan.c 1363a delay(500); . 1162c // if (kp->len > WKeyLen) . 595a . 594d 591a delay(100); . 426a delay(100); . 402a delay(100); . 386a delay(100); . 316a csr_outs(ctlr, WR_Parm1, 0); csr_outs(ctlr, WR_Parm2, 0); . 315a if(csr_ins(ctlr, WR_Cmd) & WCmdBusy) print("busy\n"); . 154a WCmdBusy = 0x8000, . ## diffname pc/etherwavelan.c 2001/0201 ## diff -e /n/emeliedump/2001/0127/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0201/sys/src/9/pc/etherwavelan.c 1401a if(ctlr->hascrypt = ltv_ins(ctlr, WTyp_HasCrypt)) ctlr->crypt = 1; . 1373d 1359,1360c for(i = 0; i < ether->nopt; i++){ // // The max. length of an 'opt' is ISAOPTLEN in dat.h. // It should be > 16 to give reasonable name lengths. // strcpy(buf, ether->opt[i]); if(p = strchr(buf, '=')) *p = ' '; option(ctlr, buf, strlen(buf)); } . 1354,1355c ctlr->ptype = WDfltPType; ctlr->txkey = 0; ctlr->keys.len = sizeof(WKey)*WNKeys/2 + 1; ctlr->keys.type = WTyp_Keys; . 1338c if((ctlr = malloc(sizeof(Ctlr))) == nil) return -1; . 1331a Wltv ltv; Ctlr* ctlr; char buf[ISAOPTLEN], *p; . 1329,1330d 1254,1325d 1203,1204d 1201d 1197a return r; } static long ctl(Ether* ether, void* buf, long n) { Ctlr *ctlr; if((ctlr = ether->ctlr) == nil) error(Enonexist); if(ctlr->attached == 0) error(Eshutdown); ilock(&ctlr->Lock); if(option(ctlr, buf, n)){ iunlock(&ctlr->Lock); error(Ebadctl); } . 1196c r = -2; free(cb); . 1193c r = -1; . 1184c else if(cistrcmp(cb->f[1], "on") == 0){ . 1181,1182c else if(cistrcmp(cb->f[0], "pm") == 0){ if(cistrcmp(cb->f[1], "off") == 0) . 1161,1179c else r = -1; . 1159a else r = -1; } else if(cistrncmp(cb->f[0], "key", 3) == 0){ i = atoi(cb->f[0]+3); if(i >= 1 && i <= WNKeys){ ctlr->txkey = i-1; key = &ctlr->keys.keys[ctlr->txkey]; key->len = strlen(cb->f[1]); if (key->len > WKeyLen) key->len = WKeyLen; memset(key->dat, 0, sizeof(key->dat)); memmove(key->dat, cb->f[1], key->len); . 1154,1158c else if(cistrcmp(cb->f[0], "crypt") == 0){ if(cistrcmp(cb->f[1], "off") == 0) ctlr->crypt = 0; else if(cistrcmp(cb->f[1], "on") == 0 && ctlr->hascrypt) ctlr->crypt = 1; else r = -1; } else if(cistrcmp(cb->f[0], "clear") == 0){ if(cistrcmp(cb->f[1], "on") == 0) ctlr->xclear = 0; else if(cistrcmp(cb->f[1], "off") == 0 && ctlr->hascrypt) . 1150,1152c if(i >= 1 && i <= 3 ) ctlr->ptype = i; else r = -1; . 1148c else if(cistrcmp(cb->f[0], "mode") == 0){ . 1144,1146c if(i >= 1 && i <= 16 ) ctlr->chan = i; else r = -1; . 1142c else if(cistrcmp(cb->f[0], "channel") == 0){ . 1138c else if(cistrcmp(cb->f[0], "station") == 0){ . 1135c strncpy(ctlr->wantname, p, WNameLen); . 1133c else{ . 1131c strncpy(ctlr->netname, p, WNameLen); . 1128,1129c p = cb->f[1]; if(ctlr->ptype == 3){ . 1115,1126c r = -1; else if(cistrcmp(cb->f[0], "essid") == 0){ if (cistrcmp(cb->f[1],"default") == 0) p = ""; . 1113d 1107,1110c r = 0; . 1102,1105d 1099,1100c char *p; int i, r; WKey *key; . 1095,1097c static int option(Ctlr* ctlr, char* buf, long n) . 1057,1061d 1018,1024c else if (SEEKEYS == 0) PRINTSTR("set "); else PRINTSTAT("%s ", ctlr->keys.keys[i].dat); } . 1015,1016c for (i = 0; i < WNKeys; i++){ if (ctlr->keys.keys[i].len == 0) . 1013c if (ctlr->txkey){ . 941,952d 627,644c if (ctlr->hascrypt){ ltv_outs(ctlr, WTyp_Crypt, ctlr->crypt); ltv_outs(ctlr, WTyp_TxKey, ctlr->txkey); w_outltv(ctlr, &ctlr->keys); ltv_outs(ctlr, WTyp_XClear, ctlr->xclear); } . 600d 592d 465,513d 434d 409d 392d 320,321d 317,318d 287,289c int hascrypt; // card has encryption int crypt; // encryption off/on int txkey; // transmit key Wltv keys; // default keys int xclear; // exclude clear packets off/on . 239c WKey keys[WNKeys]; . ## diffname pc/etherwavelan.c 2001/0202 ## diff -e /n/emeliedump/2001/0201/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0202/sys/src/9/pc/etherwavelan.c 1155,1156d 1082a else if(cistrcmp(cb->f[0], "txkey") == 0){ if((i = atoi(cb->f[1])) >= 1 && i <= WNKeys) ctlr->txkey = i-1; else r = -1; } . 1070,1071c if((i = atoi(cb->f[0]+3)) >= 1 && i <= WNKeys){ . 1047,1048c if(cistrcmp(cb->f[1], "managed") == 0) ctlr->ptype = 1; else if(cistrcmp(cb->f[1], "wds") == 0) ctlr->ptype = 2; else if(cistrcmp(cb->f[1], "adhoc") == 0) ctlr->ptype = 3; else if((i = atoi(cb->f[1])) >= 1 && i <= 3) . 1040,1041c if((i = atoi(cb->f[1])) >= 1 && i <= 16) . 889c snprint(name, sizeof(name), "#l%dtimer", ether->ctlrno); . 883c char name[64]; . 594d 591d 532d 479d 27d 16d ## diffname pc/etherwavelan.c 2001/0203 ## diff -e /n/emeliedump/2001/0202/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0203/sys/src/9/pc/etherwavelan.c 1283c return 0; . 1266c ether->mbps = 10; . 1261,1262d 1248c DEBUG("#l%d: %2.2uX%2.2uX%2.2uX%2.2uX%2.2uX%2.2uX\n", . 1243c print("#l%d: unable to read mac addr\n", . 1226,1239d 1207a if(ctlr->hascrypt = ltv_ins(ctlr, WTyp_HasCrypt)) ctlr->crypt = 1; . 1202c w_intdis(ctlr); if (w_cmd(ctlr,WCmdIni,0)){ print("#l%d: init failed\n", ether->ctlrno); goto abort; } w_intdis(ctlr); ltv_outs(ctlr, WTyp_Tick, 8); . 1200c DEBUG("#l%d: port=0x%lx irq=%ld\n", . 1195a if (ioalloc(ether->port,WIOLen,0,"wavelan")<0){ print("#l%d: port 0x%lx in use\n", ether->ctlrno, ether->port); goto abort; } . 1190c . 1159c static void . 1106,1107c static long . 1079c } . 1028c } . 894c } . 870c // BUG: to be added. . 863c } . 855c if (ctlr->txbusy) . 840c // issued from the remote) after a few seconds. . 808c // Watcher to ensure that the card still works properly and . 806c } . 802c . 771c . 764c csr_outs(ctlr, WR_IntEna, 0); . 706c ctlr->txbusy=0; // added . 700c w_write(ctlr, txid, 0, &ctlr->txf, sizeof(ctlr->txf)); . 697c memmove(ctlr->txbuf, bp->rp+sizeof(ETHERHDRSIZE)+10, . 676c // The driver is not doing so, though. . 673c . 663,665c if (again){ bp = 0; // a watchdog reenabled the card. goto retry; // must retry a previously failed tx. . 655,656c Block* bp; int txid; . 652d 625,626c bp->wp = bp->rp+(ETHERHDRSIZE+f.dlen); . 529c static int . 484c for (tries=0; tries < WTmOut; tries++){ . 447c . 308c csr_outs(ctlr, WR_IntEna, WEvs); . 301c csr_outs(ctlr, WR_IntEna, 0); . 298c static void . 295c // w_... routines do not ilock the Ctlr and should . 257c struct Ctlr . 220c union . 88,89c // most operational parameters are read/set using this. enum . 37c struct WStats . ## diffname pc/etherwavelan.c 2001/0204 ## diff -e /n/emeliedump/2001/0203/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0204/sys/src/9/pc/etherwavelan.c 845c w_intr(ether); . ## diffname pc/etherwavelan.c 2001/0205 ## diff -e /n/emeliedump/2001/0204/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0205/sys/src/9/pc/etherwavelan.c 14c BUGS: check endian, alignment and mem/io issues. TODO: automatic power management; improve locking; extract signal info; remove copy in transmit. . ## diffname pc/etherwavelan.c 2001/0207 ## diff -e /n/emeliedump/2001/0205/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0207/sys/src/9/pc/etherwavelan.c 1229c strcpy(ctlr->nodename, "Plan 9 STA"); . 924a PRINTSTAT("Ticks: %ud\n", ctlr->ticks); PRINTSTAT("TickIntr: %ud\n", ctlr->tickintr); . 861c if ((ctlr->ticks % 120) == 0) . 851c if ((ctlr->ticks % 10) == 0) { . 849a } . 848c if (csr_ins(ctlr, WR_EvSts)&WEvs){ ctlr->tickintr++; . 829c ctlr->ticks++; . 820d 258a int ticks; int tickintr; . ## diffname pc/etherwavelan.c 2001/0210 ## diff -e /n/emeliedump/2001/0207/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0210/sys/src/9/pc/etherwavelan.c 1271c ctlr->pmwait = 100; ctlr->signal = 1; ctlr->noise = 1; . 972,973c k = ((ctlr->xclear)? "excluded": "included"); . 955a PRINTSTAT("Current Transmit rate: %d\n", ltv_ins(ctlr, WTyp_CurTxRate)); . 917a PRINTSTAT("Signal: %d\n", ctlr->signal-149); PRINTSTAT("Noise: %d\n", ctlr->noise-149); PRINTSTAT("SNR: %ud\n", ctlr->signal-ctlr->noise); . 915c . 913c ether->oerrs = ctlr->ntxerr; ether->crcs = ctlr->nrxfcserr; ether->frames = 0; ether->buffs = ctlr->nrxdropnobuf; ether->overflows = 0; // // Offset must be zero or there's a possibility the // new data won't match the previous read. // if(n == 0 || offset != 0) . 911c int i, l, txid; . 849,852c // if (csr_ins(ctlr, WR_EvSts)&WEvs){ // ctlr->tickintr++; // w_intr(ether); // } . 810a 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); . 808,809c if (rc & WRXEv){ w_rxdone(ether); csr_ack(ctlr, WRXEv); . 777,806c csr_outs(ctlr, WR_IntEna, 0); rc = csr_ins(ctlr, WR_EvSts); csr_ack(ctlr, ~WEvs); // Not interested on them . 772,775d 764c int rc, txid; . 746,750c ltv.len = ltv.type = 0; w_read(ctlr, sp, 0, <v, 4); if (ltv.type == WTyp_Stats){ ltv.len--; for (i = 0; i < ltv.len && p < pend ; i++){ . 739,741c int i, rc, sp; Wltv ltv; . 704a txid = ctlr->txdid; . 703c ctlr->txlen - ETHERHDRSIZE); . 679c // BUG: only IP/ARP/RARP seem to be ok for 802.3 . 667d 649c rxerror: . 646a ctlr->signal = ((ctlr->signal*15)+((f.qinfo>>8) & 0xFF))/16; ctlr->noise = ((ctlr->noise*15)+(f.qinfo & 0xFF))/16; . 642c bp->wp += len; . 638c if (w_read(ctlr, sp, WF_802_3_Off, bp->wp, len) == 0){ . 634,635c len = ETHERHDRSIZE + f.dlen + 2; bp = iallocb(len); . 627c if (w_read(ctlr, sp, WF_802_11_Off, bp->wp, len+2) == 0){ . 618,619c len = f.dlen + WSnapHdrLen; bp = iallocb(ETHERHDRSIZE + len + 2); . 606,607c len = w_read(ctlr, sp, 0, &f, sizeof(f)); if (len == 0){ . 602d 599c int len, sp; . 566,569c ltv.len = 4; ltv.type = WTyp_Mac; memmove(ltv.addr, ether->ea, Eaddrlen); w_outltv(ctlr, <v); . 538c Wltv ltv; . 496,497c outss((ctlr)->iob+(WR_Data0), buf, len/2); . 486,488c int tries; . 480c return len; . 478a return 0; } inss((ctlr)->iob+(WR_Data1), buf, len/2); . 467,477c if (w_seek(ctlr, type, off, 1)){ . 461c return ltv.name+2; . 459c if (ltv.name[2] == 0) . 454,457c memset(<v,0,sizeof(ltv)); ltv.len = WNameLen/2+2; ltv.type = type; if (w_inltv(ctlr, <v)) . 452c static Wltv ltv; . 436,443c if(len > sizeof(ltv.s)) len = sizeof(ltv.s); memset(<v, 0, sizeof(ltv)); ltv.len = (sizeof(ltv.type)+sizeof(ltv.slen)+sizeof(ltv.s))/2; ltv.type = type; ltv.slen = (len+1) & ~1; strncpy(ltv.s, val, len); w_outltv(ctlr, <v); . 432c Wltv ltv; . 430c ltv_outstr(Ctlr* ctlr, int type, char* val) . 422,426c ltv.len = 2; ltv.type = type; ltv.val = 0; if(w_inltv(ctlr, <v)) return -1; return ltv.val; . 420c Wltv ltv; . 417c static int . 411,414c ltv.len = 2; ltv.type = type; ltv.val = val; w_outltv(ctlr, <v); . 409c Wltv ltv; . 397,403c outss((ctlr)->iob+(WR_Data1), ltv, ltv->len+1); w_cmd(ctlr, WCmdAccWr, ltv->type); . 392,395c if(w_seek(ctlr,ltv->type, 0, 1)) . 390c w_outltv(Ctlr* ctlr, Wltv* ltv) . 382,385c if(ltv->len > 0) inss((ctlr)->iob+(WR_Data1), <v->val, ltv->len-1); . 377,379c ltv->len = len; if ((code=csr_ins(ctlr, WR_Data1)) != ltv->type){ DEBUG("wavelan: type %x != code %x\n",ltv->type,code); . 375c if (len > ltv->len) . 370c if (w_seek(ctlr,ltv->type,0,1)){ . 366c if (w_cmd(ctlr, WCmdAccRd, ltv->type)){ . 363,364c int len; ushort code; . 361c w_inltv(Ctlr* ctlr, Wltv* ltv) . 320,321c int i, rc; . 260a int signal; int noise; . 210c #define csr_outs(ctlr,r,arg) outs((ctlr)->iob+(r),(arg)) . 206c WF_802_3_Off = 0x2e, . 129c WTmOut = 65536, // Cmd time out . 127c WIOLen = 0x40, // Hermes IO length . 124,125c WDfltIRQ = 3, // default irq WDfltIOB = 0x100, // default IO base . 117a WTyp_CurTxRate = 0xfd44, // Current TX rate . 108c WTyp_Tick = 0xfce0, . 91c // Lucent's Length-Type-Value records to talk to the wavelan. . 34,35c typedef struct Ctlr Ctlr; typedef struct Wltv Wltv; . 17d 14c BUGS: check endian, alignment and mem/io issues; receive watchdog interrupts. . ## diffname pc/etherwavelan.c 2001/0215 ## diff -e /n/emeliedump/2001/0210/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0215/sys/src/9/pc/etherwavelan.c 1286,1290c // 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]); . 1258,1261d 1214,1215c // DEBUG("#l%d: port=0x%lx irq=%ld\n", // ether->ctlrno, ether->port, ether->irq); . 1147c w_txstart(ether); . 1131a w_txstart(ether); . 1130c w_txdone(ctlr, WTxErrEv); . 1052c ctlr->ptype = WPTypeAdHoc; . 1050c ctlr->ptype = WPTypeWDS; . 1048c ctlr->ptype = WPTypeManaged; . 845,846c w_txstart(ether); . 841c w_txdone(ctlr, WTxErrEv); . 794c w_txstart(ether); . 735c for (i = 0; i < ltv.len && p < pend; i++){ . 728c ulong* pend = (ulong*)&ctlr->end; . 716,718c else . 713,714c ctlr->txtmout = 0; if (sts & WTxErrEv) . 710,711d 704d 702c else{ ctlr->txbusy = 1; ctlr->txtmout = 2; } . 699,700d 697d 693,695c if(w_cmd(ctlr, WCmdReclaim|WCmdTx, ctlr->txdid)){ . 687,691c else{ len = BLEN(bp); off = WF_802_3_Off; ctlr->txf.dlen = len; } w_write(ctlr, ctlr->txdid, 0, &ctlr->txf, sizeof(ctlr->txf)); w_write(ctlr, ctlr->txdid, off, bp->rp, len+2); . 658,685c // // If the packet header type field is > 1500 it is an IP or // ARP datagram, otherwise it is an 802.3 packet. See RFC1042. // memset(&ctlr->txf, 0, sizeof(ctlr->txf)); if(((pkt->type[0]<<8)|pkt->type[1]) > 1500){ ctlr->txf.framectl = WF_Data; memmove(ctlr->txf.addr1, pkt->d, Eaddrlen); memmove(ctlr->txf.addr2, pkt->s, Eaddrlen); memmove(ctlr->txf.dstaddr, pkt->d, Eaddrlen); memmove(ctlr->txf.srcaddr, pkt->s, Eaddrlen); memmove(&ctlr->txf.type, pkt->type, 2); bp->rp += ETHERHDRSIZE; len = BLEN(bp); off = WF_802_11_Off; ctlr->txf.dlen = len+ETHERHDRSIZE-WSnapHdrLen; hnputs((uchar*)&ctlr->txf.dat[0], WSnap0); hnputs((uchar*)&ctlr->txf.dat[1], WSnap1); hnputs((uchar*)&ctlr->txf.len, len+ETHERHDRSIZE-WSnapHdrLen); . 653,656c if((bp = qget(ether->oq)) == nil) return; pkt = (Etherpkt*)bp->rp; . 648,651c if((ctlr = ether->ctlr) == nil || ctlr->attached == 0 || ctlr->txbusy) return; . 643,646c Etherpkt *pkt; Ctlr *ctlr; Block *bp; int len, off; . 640,641c static void w_txstart(Ether* ether) . 575c ctlr->txbusy = 0; . 292d 159d 156a WCmdReclaim = 0x0100, . 152a WCmdTx = 0x000b, . 126c WDfltIOB = 0x180, // default IO base . 17,18c improve locking. . 14a multicast; . 4,5c the driver is done using both the FreeBSD, Linux and original Plan 9 drivers as `documentation'. . ## diffname pc/etherwavelan.c 2001/0216 ## diff -e /n/emeliedump/2001/0215/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0216/sys/src/9/pc/etherwavelan.c 1171,1175d ## diffname pc/etherwavelan.c 2001/0404 ## diff -e /n/emeliedump/2001/0216/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0404/sys/src/9/pc/etherwavelan.c 944a // PRINTSTAT("Base station: %s\n", ltv_inname(ctlr, WTyp_BaseID)); } . 943c else { . 117a WTyp_BaseID = 0xfd42, // ID of the currently connected-to base station . ## diffname pc/etherwavelan.c 2001/0405 ## diff -e /n/emeliedump/2001/0404/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0405/sys/src/9/pc/etherwavelan.c 1195,1196c // DEBUG("#l%d: port=0x%lx irq=%ld\n", // ether->ctlrno, ether->port, ether->irq); . 946c ltv.type = WTyp_BaseID; ltv.len = 4; if (w_inltv(ctlr, <v)) print("#l%d: unable to read base station mac addr\n", ether->ctlrno); l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n", ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]); . 944a Wltv ltv; . 116a WTyp_StationID = 0xfd20, . ## diffname pc/etherwavelan.c 2001/0411 ## diff -e /n/emeliedump/2001/0405/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0411/sys/src/9/pc/etherwavelan.c 917c if (ctlr->hascrypt){ . ## diffname pc/etherwavelan.c 2001/0504 ## diff -e /n/emeliedump/2001/0411/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0504/sys/src/9/pc/etherwavelan.c 32c #define SEEKEYS 0 . ## diffname pc/etherwavelan.c 2001/0505 ## diff -e /n/emeliedump/2001/0504/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0505/sys/src/9/pc/etherwavelan.c 437a . 436a // This should be ltv.slen = len; according to Axel Belinfante . ## diffname pc/etherwavelan.c 2001/0509 ## diff -e /n/emeliedump/2001/0505/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0509/sys/src/9/pc/etherwavelan.c 1202,1203c if ((ctlr->slot = pcmspecial("TrueMobile 1150", ether))<0){ DEBUG("no wavelan found\n"); goto abort; } . ## diffname pc/etherwavelan.c 2001/0527 ## diff -e /n/emeliedump/2001/0509/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0527/sys/src/9/pc/etherwavelan.c 1236c option(ctlr, ether->opt[i], strlen(ether->opt[i])); . 1229,1234c if(p = strchr(ether->opt[i], '=')) . 1207,1208c // DEBUG("#l%d: port=0x%lx irq=%ld\n", // ether->ctlrno, ether->port, ether->irq); . 1202,1205c DEBUG("no wavelan found\n"); goto abort; . 1183a if (ether->ctlr){ print("#l%d: only one card supported\n", ether->ctlrno); return -1; } . 1182c char *p; . 951,957d 948,949c else . 920c if (ctlr->txkey){ . 440d 437,438d 119d 117d 32c #define SEEKEYS 1 . ## diffname pc/etherwavelan.c 2001/0623 ## diff -e /n/emeliedump/2001/0527/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0623/sys/src/9/pc/etherwavelan.c 1269a iofree(ether->port); abort1: . 1190c goto abort1; . ## diffname pc/etherwavelan.c 2001/0907 ## diff -e /n/emeliedump/2001/0623/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0907/sys/src/9/pc/etherwavelan.c 1197,1198c // DEBUG("#l%d: port=0x%lx irq=%ld\n", // ether->ctlrno, ether->port, ether->irq); . 1194,1195c if ((ctlr->slot = pcmspecial("TrueMobile 1150", ether))<0){ DEBUG("no wavelan found\n"); goto abort; } . 1171,1175d 944a ltv.type = WTyp_BaseID; ltv.len = 4; if (w_inltv(ctlr, <v)) print("#l%d: unable to read base station mac addr\n", ether->ctlrno); l += snprint(p+l, READSTR-l, "Base station: %2.2x%2.2x%2.2x%2.2x%2.2x%2.2x\n", ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]); } . 943c else { Wltv ltv; . 915c if (ctlr->hascrypt){ . 435a . 434a // This should be ltv.slen = len; according to Axel Belinfante . 117a WTyp_BaseID = 0xfd42, // ID of the currently connected-to base station . 116a WTyp_StationID = 0xfd20, . 32c #define SEEKEYS 0 . ## diffname pc/etherwavelan.c 2001/0912 ## diff -e /n/emeliedump/2001/0907/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0912/sys/src/9/pc/etherwavelan.c 1220a ctlr->createibss = 0; . 1052a else if(cistrcmp(cb->f[0], "ibss") == 0){ if(cistrcmp(cb->f[1], "on") == 0) ctlr->createibss = 1; else ctlr->createibss = 0; } . 543a ltv_outs(ctlr, WTyp_CreateIBSS, ctlr->createibss); . 276a int createibss; . 121a WTyp_Tick = 0xfce0, . 108c WTyp_CreateIBSS = 0xfc81, . ## diffname pc/etherwavelan.c 2001/0914 ## diff -e /n/emeliedump/2001/0912/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/0914/sys/src/9/pc/etherwavelan.c 462c if(len >= sizeof ltv.s) len = sizeof ltv.s - 1; ltv.s[len] = '\0'; return ltv.s; . 460c len = ltv.slen; if(len == 0 || ltv.s[0] == 0) . 453a int len; . 441c ltv.slen = len; . ## diffname pc/etherwavelan.c 2001/1010 ## diff -e /n/emeliedump/2001/0914/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/1010/sys/src/9/pc/etherwavelan.c 1220a . 1215,1216c for (i=0; wavenames[i]; i++) if ((ctlr->slot = pcmspecial(wavenames[i], ether))>=0) break; name = wavenames[i]; if (name == nil){ for (i=0; inopt; 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){ . 1196c char *name, *p; . 1189a static char* wavenames[] = { "WaveLAN/IEEE", "TrueMobile 1150", nil, }; . ## diffname pc/etherwavelan.c 2001/1015 ## diff -e /n/emeliedump/2001/1010/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/1015/sys/src/9/pc/etherwavelan.c 1207a ctlr->ctlrno = ether->ctlrno; . 1192a "Instant Wireless ; Network PC CARD", . 344c if((rc&WCmdMsk) != (cmd&WCmdMsk) || (rc&WResSts)){ print("#l%d: cmd %.4ux: status %.4ux\n", ctlr->ctlrno, cmd, rc); return -1; } return 0; . 342a rc = csr_ins(ctlr, WR_Sts); csr_ack(ctlr, WCmdEv); . 331,341c for(i=0; ictlrno, cmd, csr_ins(ctlr, WR_EvSts)); return -1; . 328a for(i=0; ictlrno, cmd, csr_ins(ctlr, WR_Cmd)); return -1; } . 303a int ctlrno; . ## diffname pc/etherwavelan.c 2001/1016 ## diff -e /n/emeliedump/2001/1015/sys/src/9/pc/etherwavelan.c /n/emeliedump/2001/1016/sys/src/9/pc/etherwavelan.c 1339c addethercard("wavelan", wavelanpcmciareset); addethercard("wavelanpci", wavelanpcireset); . 1335a 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; ivid == 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; } . 1327,1333c 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; inopt; 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; . 1325a } . 1324d 1291c return -1; . 1260c return -1; . 1217,1256d 1214,1215d 1212a char *p; . 1210c genreset(Ether* ether, Ctlr *ctlr) . 1199c iunlock(ctlr); . 1196c ilock(ctlr); . 1185c iunlock(ctlr); . 1183c ilock(ctlr); . 1170c iunlock(ctlr); . 1167c ilock(ctlr); . 1154c iunlock(ctlr); . 1147c iunlock(ctlr); . 1145c ilock(ctlr); . 993c iunlock(ctlr); . 954c ilock(ctlr); . 923a PRINTSTAT("Double Interrupts: %lud\n", ctlr->ndoubleint); . 885c iunlock(ctlr); . 883c ilock(ctlr); . 859c iunlock(ctlr); . 841,844c if (csr_ins(ctlr, WR_EvSts)&WEvs){ ctlr->tickintr++; w_intr(ether); } . 824c ilock(ctlr); . 800,801d 768,769c csr_ack(ctlr, ~WEvs); // Not interested in them . 766d 505c csr_outss(ctlr, WR_Data0, buf, len/2); . 489c csr_inss(ctlr, WR_Data1, buf, len/2); . 411c csr_outss(ctlr, WR_Data1, ltv, ltv->len+1); . 401c csr_inss(ctlr, WR_Data1, <v->val, ltv->len-1); . 355a 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; } . 352c if((rc&WCmdMsk) != (cmd&WCmdMsk)){ . 346,347c /* * WCmdIni can take a really long time. */ enum { IniTmOut = 2000 }; for(i=0; ictlrno, cmd, i); if(i == IniTmOut){ print("#l%d: execing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_EvSts)); return -1; } . 309a /* * 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); } . 305a /* for PCI-based devices */ Ctlr *next; ushort *mmb; int active; Pcidev *pcidev; . 254a ulong ndoubleint; . 218a 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; }; . 215,217c typedef struct Ctlr Ctlr; typedef struct Wltv Wltv; typedef struct WFrame WFrame; typedef struct Stats Stats; typedef struct WStats WStats; typedef struct WKey WKey; . 193a WR_PciCor = 0x26, WR_PciHcr = 0x2E, . 34,90d 30c #define DEBUG if(0){}else print . ## diffname pc/etherwavelan.c 2002/0403 ## diff -e /n/emeliedump/2001/1016/sys/src/9/pc/etherwavelan.c /n/emeliedump/2002/0403/sys/src/9/pc/etherwavelan.c 1497a ctlr->active = 1; . 1387,1388c // print("#l%d: port 0x%lx in use\n", // ether->ctlrno, ether->port); . 481a USED(code); . 30c #define DEBUG if(1){}else print . ## diffname pc/etherwavelan.c 2002/0606 ## diff -e /n/emeliedump/2002/0403/sys/src/9/pc/etherwavelan.c /n/emeliedump/2002/0606/sys/src/9/pc/etherwavelan.c 1523a } for(i = 0; i < ether->nopt; i++){ if(p = strchr(ether->opt[i], '=')) *p = ' '; w_option(ctlr, ether->opt[i], strlen(ether->opt[i])); . 1520c if(wavelanreset(ether, ctlr) < 0){ . 1484a char *p; . 1425a for(i = 0; i < ether->nopt; i++){ if(p = strchr(ether->opt[i], '=')) *p = ' '; w_option(ctlr, ether->opt[i], strlen(ether->opt[i])); } . 1417c if(wavelanreset(ether, ctlr) < 0){ . 1383a . 1372a char *p; . 392,1369d 32,390d 30c #include "wavelan.h" . 7,19d 1,5c /* Pci/pcmcia code for wavelan.c */ .