Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/pc/etherwavelan.c

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


## 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; p<sizeof(ether->ea); 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), &ether->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, &ltv)){
		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,&ether->opt[i][4],WNameLen);
		if (strncmp(ether->opt[i],"net=",4) == 0)
			strncpy(ctlr->wantname,&ether->opt[i][4],WNameLen);
		if (strncmp(ether->opt[i],"node=",5) == 0)
			strncpy(ctlr->nodename,&ether->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; i<WTmOut; i++)
			if (csr_ins(ctlr, WR_EvSts) & WAllocEv){
				csr_ack(ctlr, WAllocEv);
				rc=csr_ins(ctlr, WR_Alloc);
				if (w_seek(ctlr, rc, 0, 0))
					return -1;
				len = len/2;
				for (j=0; j<len; j++)
					csr_outs(ctlr, WR_Data0, 0);
				return rc;
			}
	return -1;
}


static int 
w_enable(Ether* ether)
.
480,489c
static int
w_alloc(Ctlr* ctlr, int len)
{
	int rc;
	int i,j;
.
478a
	DEBUG("wavelan: tx timeout\n");
	return 0;
}
.
477c
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;
.
474,475d
469,472c
	return n;
}
.
464,467c
	} else
		DEBUG("wavelan: w_read: seek failed");
.
452,462c
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;
.
448,450d
445,446c
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;
}
.
436,443c
	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);
}
.
430,434c
static void
ltv_outstr(Ctlr* ctlr, int type, char *val)
{
	Wltv l;
	int len;
.
427,428c
	l.type = type;
	l.len = 2;
	l.val = 0;
	w_inltv(ctlr, &l);
	return l.val;
}
.
421,425c
	Wltv l;
.
418,419c
static ushort
ltv_ins(Ctlr* ctlr, int type)
.
399,416c
	l.type = type;
	l.val = val;
	l.len = 2;
	w_outltv(ctlr, &l);
}
.
397c
static void
ltv_outs(Ctlr* ctlr, int type, ushort val)
{
	Wltv l;
.
395c
	if (w_seek(ctlr,l->type,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; i<len; i++)
		csr_outs(ctlr, WR_Data1, p[i]);
	w_cmd(ctlr,WCmdAccWr,l->type);
}
.
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<len; i++)
		p[i] = csr_ins(ctlr, WR_Data1);
	return 0;
}
.
380,383c
	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;
.
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; i<WTmOut; i++){
		rc = csr_ins(ctlr, off[chan]);
		if ((rc & (WBusyOff|WErrOff)) == 0)
			return 0;
	}
	return -1;
}
.
357,359c
	int i, rc;
	static ushort sel[] = { WR_Sel0, WR_Sel1 };
	static ushort off[] = { WR_Off0, WR_Off1 };
.
354,355c
static int
w_seek(Ctlr* ctlr, ushort id, ushort offset, int chan)
.
352a
	return -1;
}
.
350,351d
331,348d
329a
			if (rc&WResSts)
				break;
			return 0;
.
321,328c
static void
w_intena(Ctlr* ctlr)
{
	csr_outs(ctlr, WR_IntEna, WEvs); 
}

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; i<WTmOut; i++){
		rc = csr_ins(ctlr, WR_EvSts);
		if ( rc&WCmdEv ){
			rc = csr_ins(ctlr, WR_Sts);
			csr_ack(ctlr, WCmdEv);
			if ((rc&WCmdMsk) != (cmd&WCmdMsk))
.
311,319c
static void 
w_intdis(Ctlr* ctlr)
{
	csr_outs(ctlr, WR_IntEna, 0); 
	csr_ack(ctlr, 0xffff);
}
.
309a
// w_... routines do not ilock the Ctlr and should 
// be called locked.
.
301,307c
	char	netname[WNameLen];
	char	wantname[WNameLen];
	char	nodename[WNameLen];
	WFrame	txf;
	uchar	txbuf[1536];
	int		txlen;
	Stats;
	WStats;
};
.
294,299c
	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;
.
288,292c
	Lock;
	Rendez	timer;
.
264,286c
struct Ctlr 
.
254,262c
// 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
};
.
247,252c
	ushort	len;
	ushort	type;
	union 
	{
		struct {
			ushort	val;
			ushort	pad;
		};
		struct {
			uchar	addr[8];
		};
		struct {
			char	s[17*2];
		};
		struct {
			char	name[WNameLen];
		};
	};
};
.
244,245c
struct Wltv
.
213,242c
#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))
.
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 <WNKeys; i++){
		// BUG: despite the checks, this seems to get nothing;
		// although it could be that the card
		// does not allow reading keys for security.
		if (l.key[id].len != 0){
			DEBUG("len=%d ", l.key[id].len);
		}
		if (l.key[id].dat[1] != 0){
			DEBUG("dat1=%s ", l.key[id].dat+1);
		} 
		if (l.key[id].dat[2] != 0){
			DEBUG("dat2=%s ", l.key[id].dat+2);
		} 
		if (l.key[id].dat[0] != 0){
			DEBUG("%s ", l.key[id].dat);
		} 
		else 
			DEBUG("none ");
	}
	DEBUG("\n");
	if (l.key[id].dat[0] == 0)
		return Nilname;
	len = l.key[id].len;
	if (len > 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, &ltv, 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, &ltv);
.
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(&ltv,0,sizeof(ltv));
	ltv.len = WNameLen/2+2;
	ltv.type = type;
	if (w_inltv(ctlr, &ltv))
.
452c
	static Wltv ltv;
.
436,443c
	if(len > sizeof(ltv.s))
		len = sizeof(ltv.s);
	memset(&ltv, 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, &ltv);
.
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, &ltv))
		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, &ltv);
.
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), &ltv->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, &ltv))
			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, &ltv))
			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; i<ether->nopt; i++){
			if (cistrncmp(ether->opt[i], "id=", 3) == 0){
				name = &ether->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; i<WTmOut; i++)
		if(csr_ins(ctlr, WR_EvSts)&WCmdEv)
			break;
	if(i==WTmOut){
		print("#l%d: execing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_EvSts));
		return -1;
.
328a
	for(i=0; i<WTmOut; i++)
		if((csr_ins(ctlr, WR_Cmd)&WCmdBusy) == 0)
			break;
	if(i==WTmOut){
		print("#l%d: issuing cmd %.4ux: %.4ux\n", ctlr->ctlrno, 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; i<nelem(wavelanpci); i++)
			if(p->vid == wavelanpci[i].vid && p->did == wavelanpci[i].did)
				break;
		if(i==nelem(wavelanpci))
			continue;

		/*
		 * On the Prism, bar[0] is the memory-mapped register address (4KB),
		 */
		if(p->mem[0].size != 4096){
			print("wavelanpci: %.4ux %.4ux: unlikely mmio size\n", p->vid, p->did);
			continue;
		}

		ctlr = malloc(sizeof(Ctlr));
		ctlr->pcidev = p;
		pa = upamalloc(p->mem[0].bar&~0xF, p->mem[0].size, 0);
		if(pa == 0){
			print("wavelanpci: %.4ux %.4ux: upamalloc 0x%.8lux %d failed\n", p->vid, p->did, p->mem[0].bar&~0xF, p->mem[0].size);
			free(ctlr);
			continue;
		}
		ctlr->mmb = (ushort*)KADDR(pa);
		if(ctlrhead != nil)
			ctlrtail->next = ctlr;
		else
			ctlrhead = ctlr;
		ctlrtail = ctlr;
		pcisetbme(p);
	}
}

static int
wavelanpcireset(Ether *ether)
{
	int i;
	Ctlr *ctlr;

	if(ctlrhead == nil)
		wavelanpciscan();

	/*
	 * Allow plan9.ini to set vid, did?
	 */
	for(ctlr=ctlrhead; ctlr!=nil; ctlr=ctlr->next)
		if(ctlr->active == 0)
			break;
	if(ctlr == nil)
		return -1;

	ilock(ctlr);
	ether->irq = ctlr->pcidev->intl;
	ether->tbdf = ctlr->pcidev->tbdf;

	/*
	 * Really hard reset.
	 */
	csr_outs(ctlr, WR_PciCor, 0x0080);
	delay(250);
	csr_outs(ctlr, WR_PciCor, 0x0000);
	delay(500);
	for(i=0; i<2*10; i++){
		if(!(csr_ins(ctlr, WR_Cmd)&WCmdBusy))
			break;
		delay(100);
	}
	if(i >= 2*10)
		print("wavelan pci %.4ux %.4ux: reset timeout %.4ux\n",
			ctlr->pcidev->vid, ctlr->pcidev->did, csr_ins(ctlr, WR_Cmd));

	if(genreset(ether, ctlr) < 0){
		iunlock(ctlr);
		ether->ctlr = nil;
		return -1;
	}
	iunlock(ctlr);
	return 0;
}
	
.
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; i<ether->nopt; i++){
		if(cistrncmp(ether->opt[i], "id=", 3) == 0){
			if((ctlr->slot = pcmspecial(&ether->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, &ltv->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; i<IniTmOut; i++){
			if(csr_ins(ctlr, WR_EvSts)&WCmdEv)
				break;
			microdelay(100);
		}
		if(i < IniTmOut)
			if(0) print("#l%d: long cmd %.4ux %d\n", ctlr->ctlrno, cmd, i);
		if(i == IniTmOut){
			print("#l%d: execing cmd %.4ux: %.4ux\n", ctlr->ctlrno, cmd, csr_ins(ctlr, WR_EvSts));
			return -1;
		}
.
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 */
.

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.