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

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


## diffname mpc/etherwavelan.c 1999/0623
## diff -e /dev/null /n/emeliedump/1999/0623/sys/src/brazil/mpc/etherwavelan.c
0a
/*
 * 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
 */

#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"
#include "etherwavelan.h"


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


static int
reset(Ether* ether)
{
	int slot, p;
	int port;
	char *pp;
	Ctlr *ctlr;
	PCMmap *m;

	ctlr = ether->ctlr = malloc(sizeof(Ctlr)); 
	ilock(&ctlr->wlock);

	if(ether->port == 0)
		ether->port = 0x280;
	port = ether->port;

	if((slot = pcmspecial(ether->type, ether)) < 0) {
		print("could not find the PCMCIA WaveLAN card.\n"); 
		return -1;
	}


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

void
etherwavelanlink(void)
{
	addethercard("WaveLAN", reset);
}
.
## diffname mpc/etherwavelan.c 1999/0624
## diff -e /n/emeliedump/1999/0623/sys/src/brazil/mpc/etherwavelan.c /n/emeliedump/1999/0624/sys/src/brazil/mpc/etherwavelan.c
690c

// super sleazy - but it will do for now
//	outsb(PIOP(base), ((char *) &cfblk), sizeof(struct i82593_conf_block));
	{
		uchar buf[16];
		int i;
		for(i=0; i<16; i++)
			buf[i] = ((char *) &cfblk)[(i&~3)+(3-(i&3))];
		outsb(PIOP(base), buf, sizeof(struct i82593_conf_block));
	}
.
## diffname mpc/etherwavelan.c 1999/0625
## diff -e /n/emeliedump/1999/0624/sys/src/brazil/mpc/etherwavelan.c /n/emeliedump/1999/0625/sys/src/brazil/mpc/etherwavelan.c
686,698c
		outb(PIORL(base), (TX_BASE & 0xff));
		outb(PIORH(base), (((TX_BASE >> 8) & PIORH_MASK) | PIORH_SEL_TX));
		outb(PIOP(base), (sizeof(buf) & 0xff));    /* lsb */
		outb(PIOP(base), (sizeof(buf) >> 8));	/* msb */
		outsb(PIOP(base), buf, sizeof(buf));
.
631,684c
		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));
	} else {
		/*
 		 * The following are the values that result in the above on
		 * the 386.  Sleazy, but these values don't change and I
		 * don't have any docs anyway
		 */
		uchar buf[16] = { 0x86, 0x80, 0x2e, 0x00, 0x60, 0x00, 0xf2, 0x08,
						  0x00, 0x40, 0xbe, 0x00, 0x3f, 0x47, 0xf1, 0x24};
.
629c
	
	/* the i82593_conf_block uses bit fields which are not
	 * machine independent.  In particular this code does not work
	 * on the powerPC (bigendian)
	 */
	if(0) {
		struct i82593_conf_block cfblk;
.
## diffname mpc/etherwavelan.c 1999/0921
## diff -e /n/emeliedump/1999/0625/sys/src/brazil/mpc/etherwavelan.c /n/emeliedump/1999/0921/sys/src/brazil/mpc/etherwavelan.c
508c
outb(LCCR(base), CR0_STATUS_0 | OP0_NOP);
print("status = %ux\n", inb(LCSR(base)));
.
488a

	newrfp = inb(RPLL(base));
	newrfp |= inb(RPLH(base)) << 8;
	newrfp %= RX_SIZE;

if(newrfp != ctlr->rfp)
print("after wavelan_cs: left over = %d\n", (newrfp - ctlr->rfp + RX_SIZE) % RX_SIZE);
	return 1;
.
485a
//print("ctlr->stop = %ux\n", ctlr->stop);
.
468a
print("%ld: len = %d status = %ux %ux %ux\n", m->ticks, len, status, newrfp, rp);
.
450a
		return 0;
	}
.
448c
	if (newrfp == ctlr->rfp) {
.
445,446c
if(0) print("before wavelan_cs: i593_rfp %d stop %d newrfp %d ctlr->rfp %d\n", i593_rfp, ctlr->stop, newrfp, ctlr->rfp);
.
418c
static int 
.
399c
	if ((bp = rbpalloc(iallocb)) == 0){		
.
293a
print("%ld: send packet %d\n", m->ticks, length);

.
22c
static int wavelan_receive(Ether *ether);
.
## diffname mpc/etherwavelan.c 1999/0929
## diff -e /n/emeliedump/1999/0921/sys/src/brazil/mpc/etherwavelan.c /n/emeliedump/1999/0929/sys/src/brazil/mpc/etherwavelan.c
493a

//delay(100);
.
472a
//delay(100);

.
450c
		if(0) print("wavelan_cs: odd RFPs:  i593_rfp %d stop %d newrfp %d ctlr->rfp %d\n",
.
405a
//print("%d+%d = %d\n", fd_p, sksize, fd_p+sksize);
//delay(100);
.
276c
			chunk_len = RX_BASE + RX_SIZE - ring_ptr;
		if(chunk_len > 256)
			chunk_len = 256;
.
273c
		if ((ring_ptr + len) < (RX_BASE + RX_SIZE))
.
270a
		delay(1);
.
262a
print("read_ringbuf\n");
.
107c
		while(wavelan_receive(ether))
			;
.
## diffname mpc/etherwavelan.c 1999/0930
## diff -e /n/emeliedump/1999/0929/sys/src/brazil/mpc/etherwavelan.c /n/emeliedump/1999/0930/sys/src/brazil/mpc/etherwavelan.c
532,533d
510,511c
	if(newrfp != ctlr->rfp)
		print("after wavelan_cs: left over = %d\n", (newrfp - ctlr->rfp + RX_SIZE) % RX_SIZE);
}

.
504,505c
if(0){ 
.
499d
479,480c
if(0) print("%ld: len = %d status = %ux %ux %ux\n", m->ticks, len, status, newrfp, rp);
.
411,412d
299,300d
280,282c
		slowinsb(PIOP(base), buf_ptr, chunk_len);
.
273d
264d
254a
/*
 * a standard insb causes the wavelan card to not be able
 * to simultanisouly receive a packet out of the ether.
 * It appears that the card runs out of bandwidth to it's
 * internal memory
 */
void
slowinsb(int port, void *buf, int len)
{
	int i, j, n;
	uchar *p, *q;

	p = (uchar*)(port + ISAMEM);
	q = buf;
	n = m->delayloop/2000;
	if(n == 0)
		n = 1;
	for(i = 0; i < len; i++){
		*q++ = *p;
		// a small delay of about .5micro seconds
		for(j=0; j<n; j++)
			;
	}
}

.
## diffname mpc/etherwavelan.c 2001/0504
## diff -e /n/emeliedump/1999/0930/sys/src/brazil/mpc/etherwavelan.c /n/emeliedump/2001/0504/sys/src/9/mpc/etherwavelan.c
870c
	ctlr->rbp = rbpalloc(iallocb);
	if(ctlr->rbp == nil)
		panic("can't reset ethernet: out of memory");
.
## diffname mpc/etherwavelan.c 2001/0527 # deleted
## diff -e /n/emeliedump/2001/0504/sys/src/9/mpc/etherwavelan.c /n/emeliedump/2001/0527/sys/src/9/mpc/etherwavelan.c
1,946d

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].