## 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), ðer->ea[0], WAVELAN_ADDR_SIZE);
/* reset transmit DMA pointer */
hacr_write_slow(base, HACR_PWR_STAT | HACR_TX_DMA_RESET);
outb(HACR(base), HACR_DEFAULT);
if(!wavelan_cmd(ether, base, "wavelan_hw_config(): ia-setup", OP0_IA_SETUP, SR0_IA_SETUP_DONE))
return(FALSE);
return(TRUE);
} /* wavelan_hw_config */
static void
wavelan_graceful_shutdown(Ether *ether, int base)
{
int status;
/* First, send the LAN controller a stop receive command */
wavelan_cmd(ether, base, "wavelan_graceful_shutdown(): stop-rcv", OP0_STOP_RCV,
SR0_NO_RESULT);
/* Then, spin until the receive unit goes idle */
do {
outb(LCCR(base), (OP0_NOP | CR0_STATUS_3));
status = inb(LCSR(base));
} while((status & SR3_RCV_STATE_MASK) != SR3_RCV_IDLE);
/* Now, spin until the chip finishes executing its current command */
do {
outb(LCCR(base), (OP0_NOP | CR0_STATUS_3));
status = inb(LCSR(base));
} while ((status & SR3_EXEC_STATE_MASK) != SR3_EXEC_IDLE);
} /* wavelan_graceful_shutdown */
static void
wavelan_ru_start(Ether *ether, int base)
{
Ctlr *ctlr;
ctlr = ether->ctlr;
/*
* We need to start from a quiescent state. To do so, we could check
* if the card is already running, but instead we just try to shut
* it down. First, we disable reception (in case it was already enabled).
*/
wavelan_graceful_shutdown(ether, base);
/* Now we know that no command is being executed. */
/* Set the receive frame pointer and stop pointer */
ctlr->rfp = 0;
outb(LCCR(base), OP0_SWIT_TO_PORT_1 | CR0_CHNL);
/* Reset ring management. This sets the receive frame pointer to 1 */
outb(LCCR(base), OP1_RESET_RING_MNGMT);
ctlr->stop = (0 + RX_SIZE - ((RX_SIZE / 64) * 3)) % RX_SIZE;
outb(LCCR(base), CR1_STOP_REG_UPDATE | (ctlr->stop >> RX_SIZE_SHIFT));
outb(LCCR(base), OP1_INT_ENABLE);
outb(LCCR(base), OP1_SWIT_TO_PORT_0);
/* Reset receive DMA pointer */
outb(HACR(base), HACR_PWR_STAT | HACR_RX_DMA_RESET);
delay(100);
outb(HACR(base), HACR_PWR_STAT);
delay(100);
/* Receive DMA on channel 1 */
wavelan_cmd(ether, base, "wavelan_ru_start(): rcv-enable",
(CR0_CHNL | OP0_RCV_ENABLE), SR0_NO_RESULT);
} /* wavelan_ru_start */
static void
transmit(Ether* ether)
{
Ctlr *ctlr;
ctlr = ether->ctlr;
ilock(&ctlr->wlock);
txstart(ether);
iunlock(&ctlr->wlock);
}
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
|