Import of dpeth 3c501/3c509b/.. ethernet driver by

Giovanni Falzoni <fgalzoni@inwind.it>.
This commit is contained in:
Ben Gras 2005-06-29 10:16:46 +00:00
parent 81081a4063
commit 6be8c4d8a3
25 changed files with 4424 additions and 13 deletions

View file

@ -23,4 +23,4 @@ all install depend clean:
cd ./printer && $(MAKE) $@
cd ./rtl8139 && $(MAKE) $@
cd ./fxp && $(MAKE) $@
cd ./dpeth && $(MAKE) $@

428
drivers/dpeth/3c501.c Normal file
View file

@ -0,0 +1,428 @@
/*
** File: 3c501.c Jan. 14, 1997
**
** Author: Giovanni Falzoni <gfalzoni@inwind.it>
**
** This file contains specific implementation of the ethernet
** device driver for 3Com Etherlink (3c501) boards. This is a
** very old board and its performances are very poor for today
** network environments.
**
** $Log$
** Revision 1.1 2005/06/29 10:16:46 beng
** Import of dpeth 3c501/3c509b/.. ethernet driver by
** Giovanni Falzoni <fgalzoni@inwind.it>.
**
** Revision 2.0 2005/06/26 16:16:46 lsodgf0
** Initial revision for Minix 3.0.6
**
** $Id$
*/
#include "drivers.h"
#include <minix/com.h>
#include <net/hton.h>
#include <net/gen/ether.h>
#include <net/gen/eth_io.h>
#include "dp.h"
#if (ENABLE_NETWORKING == 1 && ENABLE_3C501 == 1)
#include "3c501.h"
static unsigned char StationAddress[SA_ADDR_LEN] = {0, 0, 0, 0, 0, 0,};
static buff_t *TxBuff = NULL;
/*
** Name: void el1_getstats(dpeth_t *dep)
** Function: Reads statistics counters from board.
**/
static void el1_getstats(dpeth_t * dep)
{
return; /* Nothing to do */
}
/*
** Name: void el1_reset(dpeth_t *dep)
** Function: Reset function specific for Etherlink hardware.
*/
static void el1_reset(dpeth_t * dep)
{
int ix;
for (ix = 0; ix < 8; ix += 1) /* Resets the board */
outb_el1(dep, EL1_CSR, ECSR_RESET);
outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS);
/* Set Ethernet Address on controller */
outb_el1(dep, EL1_CSR, ECSR_LOOP); /* Loopback mode */
for (ix = EL1_ADDRESS; ix < SA_ADDR_LEN; ix += 1)
outb_el1(dep, ix, StationAddress[ix]);
lock();
/* Enable DMA/Interrupt, gain control of Buffer */
outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS);
/* Clear RX packet area */
outw_el1(dep, EL1_RECVPTR, 0);
/* Enable transmit/receive configuration and flush pending interrupts */
outb_el1(dep, EL1_XMIT, EXSR_IDLE | EXSR_16JAM | EXSR_JAM | EXSR_UNDER);
outb_el1(dep, EL1_RECV, dep->de_recv_mode);
inb_el1(dep, EL1_RECV);
inb_el1(dep, EL1_XMIT);
dep->de_flags &= NOT(DEF_XMIT_BUSY);
unlock();
return; /* Done */
}
/*
** Name: void el1_dumpstats(dpeth_t *dep, int port, vir_bytes size)
** Function: Dumps counter on screen (support for console display).
*/
static void el1_dumpstats(dpeth_t * dep)
{
return;
}
/*
** Name: void el1_mode_init(dpeth_t *dep)
** Function: Initializes receicer mode
*/
static void el1_mode_init(dpeth_t * dep)
{
if (dep->de_flags & DEF_BROAD) {
dep->de_recv_mode = ERSR_BROAD | ERSR_RMASK;
} else if (dep->de_flags & DEF_PROMISC) {
dep->de_recv_mode = ERSR_ALL | ERSR_RMASK;
} else if (dep->de_flags & DEF_MULTI) {
dep->de_recv_mode = ERSR_MULTI | ERSR_RMASK;
} else {
dep->de_recv_mode = ERSR_NONE | ERSR_RMASK;
}
outb_el1(dep, EL1_RECV, dep->de_recv_mode);
inb_el1(dep, EL1_RECV);
return;
}
/*
** Name: void el1_recv(dpeth_t *dep, int from, int size)
** Function: Receive function. Called from interrupt handler to
** unload recv. buffer or from main (packet to client)
*/
static void el1_recv(dpeth_t * dep, int from, int size)
{
buff_t *rxptr;
while ((dep->de_flags & DEF_READING) && (rxptr = dep->de_recvq_head)) {
/* Remove buffer from queue and free buffer */
lock();
if (dep->de_recvq_tail == dep->de_recvq_head)
dep->de_recvq_head = dep->de_recvq_tail = NULL;
else
dep->de_recvq_head = rxptr->next;
unlock();
/* Copy buffer to user area */
mem2user(dep, rxptr);
/* Reply information */
dep->de_read_s = rxptr->size;
dep->de_flags |= DEF_ACK_RECV;
dep->de_flags &= NOT(DEF_READING);
/* Return buffer to the idle pool */
free_buff(dep, rxptr);
}
return;
}
/*
** Name: void el1_send(dpeth_t *dep, int from_int, int pktsize)
** Function: Send function. Called from main to transit a packet or
** from interrupt handler when a new packet was queued.
*/
static void el1_send(dpeth_t * dep, int from_int, int pktsize)
{
buff_t *txbuff;
clock_t now;
if (from_int == FALSE) {
if ((txbuff = alloc_buff(dep, pktsize + sizeof(buff_t))) != NULL) {
/* Fill transmit buffer from user area */
txbuff->next = NULL;
txbuff->size = pktsize;
txbuff->client = dep->de_client;
user2mem(dep, txbuff);
} else
panic(dep->de_name, "out of memory for Tx", NO_NUM);
} else if ((txbuff = dep->de_xmitq_head) != NULL) {
/* Get first packet in queue */
lock();
if (dep->de_xmitq_tail == dep->de_xmitq_head)
dep->de_xmitq_head = dep->de_xmitq_tail = NULL;
else
dep->de_xmitq_head = txbuff->next;
unlock();
pktsize = txbuff->size;
} else
panic(dep->de_name, "should not be sending ", NO_NUM);
if ((dep->de_flags & DEF_XMIT_BUSY)) {
if (from_int) panic(dep->de_name, "should not be sending ", NO_NUM);
getuptime(&now);
if ((now - dep->de_xmit_start) > 4) {
/* Transmitter timed out */
DEBUG(printf("3c501: transmitter timed out ... \n"));
dep->de_stat.ets_sendErr += 1;
dep->de_flags &= NOT(DEF_XMIT_BUSY);
el1_reset(dep);
}
/* Queue packet */
lock(); /* Queue packet to receive queue */
if (dep->de_xmitq_head == NULL)
dep->de_xmitq_head = txbuff;
else
dep->de_xmitq_tail->next = txbuff;
dep->de_xmitq_tail = txbuff;
unlock();
} else {
/* Save for retransmission */
TxBuff = txbuff;
dep->de_flags |= (DEF_XMIT_BUSY | DEF_ACK_SEND);
/* Setup board for packet loading */
lock(); /* Buffer to processor */
outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS);
inb_el1(dep, EL1_RECV); /* Clears any spurious interrupt */
inb_el1(dep, EL1_XMIT);
outw_el1(dep, EL1_RECVPTR, 0); /* Clears RX packet area */
/* Loads packet */
outw_el1(dep, EL1_XMITPTR, (EL1_BFRSIZ - pktsize));
outsb(dep->de_data_port, SELF, txbuff->buffer, pktsize);
/* Starts transmitter */
outw_el1(dep, EL1_XMITPTR, (EL1_BFRSIZ - pktsize));
outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_XMIT); /* There it goes... */
unlock();
getuptime(&dep->de_xmit_start);
dep->de_flags &= NOT(DEF_SENDING);
}
return;
}
/*
** Name: void el1_stop(dpeth_t *dep)
** Function: Stops board and disable interrupts.
*/
static void el1_stop(dpeth_t * dep)
{
int ix;
DEBUG(printf("%s: stopping Etherlink ....\n", dep->de_name));
for (ix = 0; ix < 8; ix += 1) /* Reset board */
outb_el1(dep, EL1_CSR, ECSR_RESET);
outb_el1(dep, EL1_CSR, ECSR_SYS);
sys_irqdisable(&dep->de_hook); /* Disable interrupt */
return;
}
/*
** Name: void el1_interrupt(dpeth_t *dep)
** Function: Interrupt handler. Acknwledges transmit interrupts
** or unloads receive buffer to memory queue.
*/
static void el1_interrupt(dpeth_t * dep)
{
u16_t csr, isr;
int pktsize;
buff_t *rxptr;
csr = inb_el1(dep, EL1_CSR);
if ((csr & ECSR_XMIT) && (dep->de_flags & DEF_XMIT_BUSY)) {
/* Got a transmit interrupt */
isr = inb_el1(dep, EL1_XMIT);
if ((isr & (EXSR_16JAM | EXSR_UNDER | EXSR_JAM)) || !(isr & EXSR_IDLE)) {
DEBUG(printf("3c501: got xmit interrupt (ASR=0x%02X XSR=0x%02X)\n", csr, isr));
if (isr & EXSR_JAM) {
/* Sending, packet got a collision */
dep->de_stat.ets_collision += 1;
/* Put pointer back to beginning of packet */
outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS);
outw_el1(dep, EL1_XMITPTR, (EL1_BFRSIZ - TxBuff->size));
/* And retrigger transmission */
outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_XMIT);
return;
} else if ((isr & EXSR_16JAM) || !(isr & EXSR_IDLE)) {
dep->de_stat.ets_sendErr += 1;
} else if (isr & EXSR_UNDER) {
dep->de_stat.ets_fifoUnder += 1;
}
DEBUG(printf("3c501: got xmit interrupt (0x%02X)\n", isr));
el1_reset(dep);
} else {
/** if (inw_el1(dep, EL1_XMITPTR) == EL1_BFRSIZ) **/
/* Packet transmitted successfully */
dep->de_stat.ets_packetT += 1;
dep->bytes_Tx += (long) (TxBuff->size);
free_buff(dep, TxBuff);
dep->de_flags &= NOT(DEF_XMIT_BUSY);
if ((dep->de_flags & DEF_SENDING) && dep->de_xmitq_head) {
/* Pending transmit request available in queue */
el1_send(dep, TRUE, 0);
if (dep->de_flags & (DEF_XMIT_BUSY | DEF_ACK_SEND))
return;
}
}
} else if ((csr & (ECSR_RECV | ECSR_XMTBSY)) == (ECSR_RECV | ECSR_XMTBSY)) {
/* Got a receive interrupt */
isr = inb_el1(dep, EL1_RECV);
pktsize = inw_el1(dep, EL1_RECVPTR);
if ((isr & ERSR_RERROR) || (isr & ERSR_STALE)) {
DEBUG(printf("Rx0 (ASR=0x%02X RSR=0x%02X size=%d)\n", csr, isr, pktsize));
dep->de_stat.ets_recvErr += 1;
} else if (pktsize < ETH_MIN_PACK_SIZE || pktsize > ETH_MAX_PACK_SIZE) {
DEBUG(printf("Rx1 (ASR=0x%02X RSR=0x%02X size=%d)\n", csr, isr, pktsize));
dep->de_stat.ets_recvErr += 1;
} else if ((rxptr = alloc_buff(dep, pktsize + sizeof(buff_t))) == NULL) {
/* Memory not available. Drop packet */
dep->de_stat.ets_fifoOver += 1;
} else if (isr & (ERSR_GOOD | ERSR_ANY)) {
/* Got a good packet. Read it from buffer */
outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS);
outw_el1(dep, EL1_XMITPTR, 0);
insb(dep->de_data_port, SELF, rxptr->buffer, pktsize);
rxptr->next = NULL;
rxptr->size = pktsize;
dep->de_stat.ets_packetR += 1;
dep->bytes_Rx += (long) pktsize;
lock(); /* Queue packet to receive queue */
if (dep->de_recvq_head == NULL)
dep->de_recvq_head = rxptr;
else
dep->de_recvq_tail->next = rxptr;
dep->de_recvq_tail = rxptr;
unlock();
/* Reply to pending Receive requests, if any */
el1_recv(dep, TRUE, 0);
}
} else { /* Nasty condition, should never happen */
DEBUG(
printf("3c501: got interrupt with status 0x%02X\n"
" de_flags=0x%04X XSR=0x%02X RSR=0x%02X \n"
" xmit buffer = 0x%4X recv buffer = 0x%4X\n",
csr, dep->de_flags,
inb_el1(dep, EL1_RECV),
inb_el1(dep, EL1_XMIT),
inw_el1(dep, EL1_XMITPTR),
inw_el1(dep, EL1_RECVPTR))
);
el1_reset(dep);
}
/* Move into receive mode */
outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_RECV);
outw_el1(dep, EL1_RECVPTR, 0);
/* Be sure that interrupts are cleared */
inb_el1(dep, EL1_RECV);
inb_el1(dep, EL1_XMIT);
return;
}
/*
** Name: void el1_init(dpeth_t *dep)
** Function: Initalizes board hardware and driver data structures.
*/
static void el1_init(dpeth_t * dep)
{
int ix;
dep->de_irq &= NOT(DEI_DEFAULT); /* Strip the default flag. */
dep->de_offset_page = 0;
dep->de_data_port = dep->de_base_port + EL1_DATAPORT;
el1_reset(dep); /* Reset and initialize board */
/* Start receiver (default mode) */
outw_el1(dep, EL1_RECVPTR, 0);
outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_RECV);
/* Initializes buffer pool */
init_buff(dep, NULL);
el1_mode_init(dep);
printf("%s: Etherlink (%s) at %X:%d - ",
dep->de_name, "3c501", dep->de_base_port, dep->de_irq);
for (ix = 0; ix < SA_ADDR_LEN; ix += 1)
printf("%02X%c", (dep->de_address.ea_addr[ix] = StationAddress[ix]),
ix < SA_ADDR_LEN - 1 ? ':' : '\n');
/* Device specific functions */
dep->de_recvf = el1_recv;
dep->de_sendf = el1_send;
dep->de_flagsf = el1_mode_init;
dep->de_resetf = el1_reset;
dep->de_getstatsf = el1_getstats;
dep->de_dumpstatsf = el1_dumpstats;
dep->de_interruptf = el1_interrupt;
return; /* Done */
}
/*
** Name: int el1_probe(dpeth_t *dep)
** Function: Checks for presence of the board.
*/
PUBLIC int el1_probe(dpeth_t * dep)
{
int ix;
for (ix = 0; ix < 8; ix += 1) /* Reset the board */
outb_el1(dep, EL1_CSR, ECSR_RESET);
outb_el1(dep, EL1_CSR, ECSR_SYS); /* Leaves buffer to system */
/* Check station address */
for (ix = 0; ix < SA_ADDR_LEN; ix += 1) {
outw_el1(dep, EL1_XMITPTR, ix);
StationAddress[ix] = inb_el1(dep, EL1_SAPROM);
}
if (StationAddress[0] != 0x02 || /* Etherlink Station address */
StationAddress[1] != 0x60 || /* MUST be 02:60:8c:xx:xx:xx */
StationAddress[2] != 0x8C)
return FALSE; /* No Etherlink board at this address */
dep->de_ramsize = 0; /* RAM size is meaningless */
dep->de_linmem = 0L; /* Access is via I/O port */
/* Device specific functions */
dep->de_initf = el1_init;
dep->de_stopf = el1_stop;
return TRUE; /* Etherlink board found */
}
#endif /* ENABLE_NETWORKING */
/** 3c501.c **/

71
drivers/dpeth/3c501.h Normal file
View file

@ -0,0 +1,71 @@
/*
** File: 3c501.h Jan. 14, 1997
**
** Author: Giovanni Falzoni <gfalzoni@inwind.it>
**
** Interface description for 3Com Etherlink boards
**
** $Log$
** Revision 1.1 2005/06/29 10:16:46 beng
** Import of dpeth 3c501/3c509b/.. ethernet driver by
** Giovanni Falzoni <fgalzoni@inwind.it>.
**
** Revision 2.0 2005/06/26 16:16:46 lsodgf0
** Initial revision for Minix 3.0.6
**
** $Id$
*/
/* The various board command registers */
#define EL1_ADDRESS 0x00 /* Board station address, 6 bytes */
#define EL1_RECV 0x06 /* Board Receive Config/Status Reg. */
#define EL1_XMIT 0x07 /* Board Transmit Config/Status Reg. */
#define EL1_XMITPTR 0x08 /* Transmit buffer pointer (word access) */
#define EL1_RECVPTR 0x0A /* Receive buffer pointer (word access) */
#define EL1_SAPROM 0x0C /* Window on Station Addr prom */
#define EL1_CSR 0x0E /* Board Command/Status Register */
#define EL1_DATAPORT 0x0F /* Window on packet buffer (Data Port) */
/* Bits in EL1_RECV, interrupt enable on write, status when read */
#define ERSR_NONE 0x00 /* Match mode in bits 5-6 (wo) */
#define ERSR_ALL 0x40 /* Promiscuous receive (wo) */
#define ERSR_BROAD 0x80 /* Station address plus broadcast (wo) */
#define ERSR_MULTI 0x80 /* Station address plus multicast 0xC0 */
#define ERSR_STALE 0x80 /* Receive status previously read (ro) */
#define ERSR_GOOD 0x20 /* Well formed packets only (rw) */
#define ERSR_ANY 0x10 /* Any packet, even with errors (rw) */
#define ERSR_SHORT 0x08 /* Short frame (rw) */
#define ERSR_DRIBBLE 0x04 /* Dribble error (rw) */
#define ERSR_FCS 0x02 /* CRC error (rw) */
#define ERSR_OVER 0x01 /* Data overflow (rw) */
#define ERSR_RERROR (ERSR_SHORT|ERSR_DRIBBLE|ERSR_FCS|ERSR_OVER)
#define ERSR_RMASK (ERSR_GOOD|ERSR_RERROR)/*(ERSR_GOOD|ERSR_ANY|ERSR_RERROR)*/
/* Bits in EL1_XMIT, interrupt enable on write, status when read */
#define EXSR_IDLE 0x08 /* Transmit idle (send completed) */
#define EXSR_16JAM 0x04 /* Packet sending got 16 collisions */
#define EXSR_JAM 0x02 /* Packet sending got a collision */
#define EXSR_UNDER 0x01 /* Data underflow in sending */
/* Bits in EL1_CSR (Configuration Status Register) */
#define ECSR_RESET 0x80 /* Reset the controller (wo) */
#define ECSR_XMTBSY 0x80 /* Transmitter busy (ro) */
#define ECSR_RIDE 0x01 /* Request interrupt/DMA enable (rw) */
#define ECSR_DMA 0x20 /* DMA request (rw) */
#define ECSR_EDMA 0x10 /* DMA done (ro) */
#define ECSR_CRC 0x02 /* Causes CRC error on transmit (wo) */
#define ECSR_RCVBSY 0x01 /* Receive in progress (ro) */
#define ECSR_LOOP (3<<2) /* 2 bit field in bits 2,3, loopback */
#define ECSR_RECV (2<<2) /* Gives buffer to receiver (rw) */
#define ECSR_XMIT (1<<2) /* Gives buffer to transmit (rw) */
#define ECSR_SYS (0<<2) /* Gives buffer to processor (wo) */
#define EL1_BFRSIZ 2048 /* Number of bytes in board buffer */
#define inb_el1(dep,reg) (inb(dep->de_base_port+(reg)))
#define inw_el1(dep,reg) (inw(dep->de_base_port+(reg)))
#define outb_el1(dep,reg,data) (outb(dep->de_base_port+(reg),data))
#define outw_el1(dep,reg,data) (outw(dep->de_base_port+(reg),data))
/** 3c501.h **/

175
drivers/dpeth/3c503.c Normal file
View file

@ -0,0 +1,175 @@
/*
** File: 3c503.c Dec. 20, 1996
**
** Author: Giovanni Falzoni <gfalzoni@inwind.it>
**
** Driver for the Etherlink II boards. Works in shared memory mode.
** Programmed I/O could be used as well but would result in poor
** performances. This file contains only the board specific code,
** the rest is in 8390.c Code specific for ISA bus only
**
** $Log$
** Revision 1.1 2005/06/29 10:16:46 beng
** Import of dpeth 3c501/3c509b/.. ethernet driver by
** Giovanni Falzoni <fgalzoni@inwind.it>.
**
** Revision 2.0 2005/06/26 16:16:46 lsodgf0
** Initial revision for Minix 3.0.6
**
** $Id$
*/
#include "drivers.h"
#include <net/gen/ether.h>
#include <net/gen/eth_io.h>
#include "dp.h"
#if ENABLE_NETWORKING == 1 && ENABLE_3C503 == 1
#include "8390.h"
#include "3c503.h"
/*
** Name: void el2_init(dpeth_t *dep);
** Function: Initalize hardware and data structures.
*/
static void el2_init(dpeth_t * dep)
{
int ix, irq;
int sendq_nr;
int cntr;
/* Map the address PROM to lower I/O address range */
cntr = inb_el2(dep, EL2_CNTR);
outb_el2(dep, EL2_CNTR, cntr | ECNTR_SAPROM);
/* Read station address from PROM */
for (ix = EL2_EA0; ix <= EL2_EA5; ix += 1)
dep->de_address.ea_addr[ix] = inb_el2(dep, ix);
/* Map the 8390 back to lower I/O address range */
outb_el2(dep, EL2_CNTR, cntr);
/* Enable memory, but turn off interrupts until we are ready */
outb_el2(dep, EL2_CFGR, ECFGR_IRQOFF);
dep->de_data_port = dep->de_dp8390_port = dep->de_base_port;
dep->de_prog_IO = FALSE; /* Programmed I/O not yet available */
/* Check width of data bus */
outb_el2(dep, DP_CR, CR_PS_P0 | CR_NO_DMA | CR_STP);
outb_el2(dep, DP_DCR, 0);
outb_el2(dep, DP_CR, CR_PS_P2 | CR_NO_DMA | CR_STP);
dep->de_16bit = (inb_el2(dep, DP_DCR) & DCR_WTS) != 0;
outb_el2(dep, DP_CR, CR_PS_P0 | CR_NO_DMA | CR_STP);
/* Allocate one send buffer (1.5kb) per 8kb of on board memory. */
/* Only 8kb of 3c503/16 boards are used to avoid specific routines */
sendq_nr = dep->de_ramsize / 0x2000;
if (sendq_nr < 1)
sendq_nr = 1;
else if (sendq_nr > SENDQ_NR)
sendq_nr = SENDQ_NR;
dep->de_sendq_nr = sendq_nr;
for (ix = 0; ix < sendq_nr; ix++)
dep->de_sendq[ix].sq_sendpage = (ix * SENDQ_PAGES) + EL2_SM_START_PG;
dep->de_startpage = (ix * SENDQ_PAGES) + EL2_SM_START_PG;
dep->de_stoppage = EL2_SM_STOP_PG;
outb_el2(dep, EL2_STARTPG, dep->de_startpage);
outb_el2(dep, EL2_STOPPG, dep->de_stoppage);
/* Point the vector pointer registers somewhere ?harmless?. */
outb_el2(dep, EL2_VP2, 0xFF); /* Point at the ROM restart location */
outb_el2(dep, EL2_VP1, 0xFF); /* 0xFFFF:0000 (from original sources) */
outb_el2(dep, EL2_VP0, 0x00); /* - What for protected mode? */
/* Set interrupt level for 3c503 */
irq = (dep->de_irq &= ~DEI_DEFAULT); /* Strip the default flag. */
if (irq == 9) irq = 2;
if (irq < 2 || irq > 5) panic(dep->de_name, "bad 3c503 irq configuration", irq);
outb_el2(dep, EL2_IDCFG, (0x04 << irq));
outb_el2(dep, EL2_DRQCNT, 0x08); /* Set burst size to 8 */
outb_el2(dep, EL2_DMAAH, EL2_SM_START_PG); /* Put start of TX */
outb_el2(dep, EL2_DMAAL, 0x00); /* buffer in the GA DMA reg */
outb_el2(dep, EL2_CFGR, ECFGR_NORM); /* Enable shared memory */
ns_init(dep); /* Initialize DP controller */
printf("%s: Etherlink II%s (%s) at %X:%d:%05lX - ",
dep->de_name, dep->de_16bit ? "/16" : "", "3c503",
dep->de_base_port, dep->de_irq,
dep->de_linmem + dep->de_offset_page);
for (ix = 0; ix < SA_ADDR_LEN; ix += 1)
printf("%02X%c", dep->de_address.ea_addr[ix],
ix < SA_ADDR_LEN - 1 ? ':' : '\n');
return;
}
/*
** Name: void el2_stop(dpeth_t *dep);
** Function: Stops board by disabling interrupts.
*/
static void el2_stop(dpeth_t * dep)
{
outb_el2(dep, EL2_CFGR, ECFGR_IRQOFF);
sys_irqdisable(&dep->de_hook); /* disable interrupts */
return;
}
/*
** Name: void el2_probe(dpeth_t *dep);
** Function: Probe for the presence of an EtherLink II card.
** Initialize memory addressing if card detected.
*/
int el2_probe(dpeth_t * dep)
{
int iobase, membase;
int thin;
/* Thin ethernet or AUI? */
thin = (dep->de_linmem & 1) ? ECNTR_AUI : ECNTR_THIN;
/* Location registers should have 1 bit set */
if (!(iobase = inb_el2(dep, EL2_IOBASE))) return FALSE;
if (!((membase = inb_el2(dep, EL2_MEMBASE)) & 0xF0)) return FALSE;
if ((iobase & (iobase - 1)) || (membase & (membase - 1))) return FALSE;
/* Resets board */
outb_el2(dep, EL2_CNTR, ECNTR_RESET | thin);
milli_delay(1);
outb_el2(dep, EL2_CNTR, thin);
milli_delay(5);
/* Map the address PROM to lower I/O address range */
outb_el2(dep, EL2_CNTR, ECNTR_SAPROM | thin);
if (inb_el2(dep, EL2_EA0) != 0x02 || /* Etherlink II Station address */
inb_el2(dep, EL2_EA1) != 0x60 || /* MUST be 02:60:8c:xx:xx:xx */
inb_el2(dep, EL2_EA2) != 0x8C)
return FALSE; /* No Etherlink board at this address */
/* Map the 8390 back to lower I/O address range */
outb_el2(dep, EL2_CNTR, thin);
/* Setup shared memory addressing for 3c503 */
dep->de_linmem = ((membase & 0xC0) ? EL2_BASE_0D8000 : EL2_BASE_0C8000) +
((membase & 0xA0) ? (EL2_BASE_0CC000 - EL2_BASE_0C8000) : 0x0000);
/* Shared memory starts at 0x2000 (8kb window) */
dep->de_offset_page = (EL2_SM_START_PG * DP_PAGESIZE);
dep->de_linmem -= dep->de_offset_page;
dep->de_ramsize = (EL2_SM_STOP_PG - EL2_SM_START_PG) * DP_PAGESIZE;
/* Board initialization and stop functions */
dep->de_initf = el2_init;
dep->de_stopf = el2_stop;
return TRUE;
}
#endif /* ENABLE_NETWORKING && ENABLE_3C503 */
/** 3c503.c **/

72
drivers/dpeth/3c503.h Normal file
View file

@ -0,0 +1,72 @@
/*
** File: 3c503.h Dec. 20, 1996
**
** Author: Giovanni Falzoni <gfalzoni@inwind.it>
**
** Interface description for 3Com Etherlink II boards
**
** $Log$
** Revision 1.1 2005/06/29 10:16:46 beng
** Import of dpeth 3c501/3c509b/.. ethernet driver by
** Giovanni Falzoni <fgalzoni@inwind.it>.
**
** Revision 2.0 2005/06/26 16:16:46 lsodgf0
** Initial revision for Minix 3.0.6
**
** $Id$
*/
#define EL2_MEMTEST 0 /* Set to 1 for on board memory test */
#define EL2_GA 0x0400 /* Offset of registers in Gate Array */
/* EtherLink II card */
#define EL2_STARTPG (EL2_GA+0x00) /* Start page matching DP_PSTARTPG */
#define EL2_STOPPG (EL2_GA+0x01) /* Stop page matching DP_PSTOPPG */
#define EL2_DRQCNT (EL2_GA+0x02) /* DMA burst count */
#define EL2_IOBASE (EL2_GA+0x03) /* I/O base jumpers (bit coded) */
#define EL2_MEMBASE (EL2_GA+0x04) /* Memory base jumpers (bit coded) */
#define EL2_CFGR (EL2_GA+0x05) /* Configuration Register for GA */
#define EL2_CNTR (EL2_GA+0x06) /* Control(write) and status(read) */
#define EL2_STATUS (EL2_GA+0x07)
#define EL2_IDCFG (EL2_GA+0x08) /* Interrupt/DMA configuration reg */
#define EL2_DMAAH (EL2_GA+0x09) /* DMA address register (High byte) */
#define EL2_DMAAL (EL2_GA+0x0A) /* DMA address register (Low byte) */
#define EL2_VP2 (EL2_GA+0x0B) /* Vector pointer - set to */
#define EL2_VP1 (EL2_GA+0x0C) /* reset address (0xFFFF:0) */
#define EL2_VP0 (EL2_GA+0x0D) /* */
#define EL2_FIFOH (EL2_GA+0x0E) /* FIFO for progr. I/O (High byte) */
#define EL2_FIFOL (EL2_GA+0x0F) /* FIFO for progr. I/O (Low byte) */
#define EL2_EA0 0x00 /* Most significant byte of ethernet address */
#define EL2_EA1 0x01
#define EL2_EA2 0x02
#define EL2_EA3 0x03
#define EL2_EA4 0x04
#define EL2_EA5 0x05 /* Least significant byte of ethernet address */
/* Bits in EL2_CNTR register */
#define ECNTR_RESET 0x01 /* Software Reset */
#define ECNTR_THIN 0x02 /* Onboard transceiver enable */
#define ECNTR_AUI 0x00 /* Onboard transceiver disable */
#define ECNTR_SAPROM 0x04 /* Map the station address prom */
/* Bits in EL2_CFGR register */
#define ECFGR_NORM 0x49 /* Enable 8k shared memory, no DMA, TC int */
#define ECFGR_IRQOFF 0xC9 /* As above, disable 8390 IRQ */
/* Shared memory management parameters */
#define EL2_SM_START_PG 0x20 /* First page of TX buffer */
#define EL2_SM_STOP_PG 0x40 /* Last page +1 of RX ring */
/* Physical addresses where an Etherlink board can be configured */
#define EL2_BASE_0C8000 0x0C8000
#define EL2_BASE_0CC000 0x0CC000
#define EL2_BASE_0D8000 0x0D8000
#define EL2_BASE_0DC000 0x0DC000
#define inb_el2(dep,reg) (inb((dep)->de_base_port+(reg)))
#define outb_el2(dep,reg,data) (outb((dep)->de_base_port+(reg),(data)))
/** 3c503.h **/

603
drivers/dpeth/3c509.c Normal file
View file

@ -0,0 +1,603 @@
/*
** File: 3c509.c Jun. 01, 2000
**
** Author: Giovanni Falzoni <gfalzoni@inwind.it>
**
** This file contains specific implementation of the ethernet
** device driver for 3Com Etherlink III (3c509) boards.
** NOTE: The board has to be setup to disable PnP and to assign
** I/O base and IRQ. The driver is for ISA bus only
**
** $Log$
** Revision 1.1 2005/06/29 10:16:46 beng
** Import of dpeth 3c501/3c509b/.. ethernet driver by
** Giovanni Falzoni <fgalzoni@inwind.it>.
**
** Revision 2.0 2005/06/26 16:16:46 lsodgf0
** Initial revision for Minix 3.0.6
**
** $Id$
*/
#include "drivers.h"
#include <minix/com.h>
#include <net/hton.h>
#include <net/gen/ether.h>
#include <net/gen/eth_io.h>
#include "dp.h"
#if ENABLE_NETWORKING == 1 && ENABLE_3C509 == 1
#include "3c509.h"
static const char *const IfNamesMsg[] = {
"10BaseT", "AUI", "unknown", "BNC",
};
/*
** Name: void el3_update_stats(dpeth_t *dep)
** Function: Reads statistic counters from board
** and updates local counters.
*/
static void el3_update_stats(dpeth_t * dep)
{
/* Disables statistics while reading and switches to the correct window */
outw_el3(dep, REG_CmdStatus, CMD_StatsDisable);
SetWindow(WNO_Statistics);
/* Reads everything, adding values to the local counters */
dep->de_stat.ets_sendErr += inb_el3(dep, REG_TxCarrierLost); /* Reg. 00 */
dep->de_stat.ets_sendErr += inb_el3(dep, REG_TxNoCD); /* Reg. 01 */
dep->de_stat.ets_collision += inb_el3(dep, REG_TxMultColl); /* Reg. 02 */
dep->de_stat.ets_collision += inb_el3(dep, REG_TxSingleColl); /* Reg. 03 */
dep->de_stat.ets_collision += inb_el3(dep, REG_TxLate); /* Reg. 04 */
dep->de_stat.ets_recvErr += inb_el3(dep, REG_RxDiscarded); /* Reg. 05 */
dep->de_stat.ets_packetT += inb_el3(dep, REG_TxFrames); /* Reg. 06 */
dep->de_stat.ets_packetR += inb_el3(dep, REG_RxFrames); /* Reg. 07 */
dep->de_stat.ets_transDef += inb_el3(dep, REG_TxDefer); /* Reg. 08 */
dep->bytes_Rx += (unsigned) inw_el3(dep, REG_RxBytes); /* Reg. 10 */
dep->bytes_Tx += (unsigned) inw_el3(dep, REG_TxBytes); /* Reg. 12 */
/* Goes back to operating window and enables statistics */
SetWindow(WNO_Operating);
outw_el3(dep, REG_CmdStatus, CMD_StatsEnable);
return;
}
/*
** Name: void el3_getstats(dpeth_t *dep)
** Function: Reads statistics counters from board.
*/
static void el3_getstats(dpeth_t * dep)
{
lock();
el3_update_stats(dep);
unlock();
return;
}
/*
** Name: void el3_dodump(dpeth_t *dep)
** Function: Dumps counter on screen (support for console display).
*/
static void el3_dodump(dpeth_t * dep)
{
el3_getstats(dep);
return;
}
/*
** Name: void el3_rx_mode(dpeth_t *dep)
** Function: Initializes receiver mode
*/
static void el3_rx_mode(dpeth_t * dep)
{
dep->de_recv_mode = FilterIndividual;
if (dep->de_flags & DEF_BROAD) dep->de_recv_mode |= FilterBroadcast;
if (dep->de_flags & DEF_MULTI) dep->de_recv_mode |= FilterMulticast;
if (dep->de_flags & DEF_PROMISC) dep->de_recv_mode |= FilterPromiscuous;
outw_el3(dep, REG_CmdStatus, CMD_RxReset);
outw_el3(dep, REG_CmdStatus, CMD_SetRxFilter | dep->de_recv_mode);
outw_el3(dep, REG_CmdStatus, CMD_RxEnable);
return;
}
/*
** Name: void el3_reset(dpeth_t *dep)
** Function: Reset function specific for Etherlink hardware.
*/
static void el3_reset(dpeth_t * dep)
{
return; /* Done */
}
/*
** Name: void el3_write_fifo(dpeth_t * dep, int pktsize);
** Function: Writes a packet from user area to board.
** Remark: Writing a word/dword at a time may result faster
** but is a lot more complicated. Let's go simpler way.
*/
static void el3_write_fifo(dpeth_t * dep, int pktsize)
{
phys_bytes phys_user;
int bytes, ix = 0;
iovec_dat_t *iovp = &dep->de_write_iovec;
int padding = pktsize;
do { /* Writes chuncks of packet from user buffers */
bytes = iovp->iod_iovec[ix].iov_size; /* Size of buffer */
if (bytes > pktsize) bytes = pktsize;
/* Writes from user buffer to Tx FIFO */
outsb(dep->de_data_port, iovp->iod_proc_nr,
(void*)(iovp->iod_iovec[ix].iov_addr), bytes);
if (++ix >= IOVEC_NR) { /* Next buffer of IO vector */
dp_next_iovec(iovp);
ix = 0;
}
/* Till packet done */
} while ((pktsize -= bytes) > 0);
while ((padding++ % sizeof(long)) != 0) outb(dep->de_data_port, 0x00);
return;
}
/*
** Name: void el3_recv(dpeth_t *dep, int fromint, int size)
** Function: Receive function. Called from interrupt handler or
** from main to unload recv. buffer (packet to client)
*/
static void el3_recv(dpeth_t *dep, int fromint, int size)
{
buff_t *rxptr;
while ((dep->de_flags & DEF_READING) && (rxptr = dep->de_recvq_head)) {
lock(); /* Remove buffer from queue */
if (dep->de_recvq_tail == dep->de_recvq_head)
dep->de_recvq_head = dep->de_recvq_tail = NULL;
else
dep->de_recvq_head = rxptr->next;
unlock();
/* Copy buffer to user area and free it */
mem2user(dep, rxptr);
dep->de_read_s = rxptr->size;
dep->de_flags |= DEF_ACK_RECV;
dep->de_flags &= NOT(DEF_READING);
/* Return buffer to the idle pool */
free_buff(dep, rxptr);
}
return;
}
/*
** Name: void el3_rx_complete(dpeth_t * dep);
** Function: Upon receiving a packet, provides status checks
** and if packet is OK copies it to local buffer.
*/
static void el3_rx_complete(dpeth_t * dep)
{
short int RxStatus;
int pktsize;
buff_t *rxptr;
RxStatus = inw_el3(dep, REG_RxStatus);
pktsize = RxStatus & RXS_Length; /* Mask off packet length */
if (RxStatus & RXS_Error) {
/* First checks for receiving errors */
RxStatus &= RXS_ErrType;
switch (RxStatus) { /* Bad packet (see error type) */
case RXS_Dribble:
case RXS_Oversize:
case RXS_Runt: dep->de_stat.ets_recvErr += 1; break;
case RXS_Overrun: dep->de_stat.ets_OVW += 1; break;
case RXS_Framing: dep->de_stat.ets_frameAll += 1; break;
case RXS_CRC: dep->de_stat.ets_CRCerr += 1; break;
}
} else if ((rxptr = alloc_buff(dep, pktsize + sizeof(buff_t))) == NULL) {
/* Memory not available. Drop packet */
dep->de_stat.ets_fifoOver += 1;
} else {
/* Good packet. Read it from FIFO */
insb(dep->de_data_port, SELF, rxptr->buffer, pktsize);
rxptr->next = NULL;
rxptr->size = pktsize;
lock(); /* Queue packet to receive queue */
if (dep->de_recvq_head == NULL)
dep->de_recvq_head = rxptr;
else
dep->de_recvq_tail->next = rxptr;
dep->de_recvq_tail = rxptr;
unlock();
/* Reply to pending Receive requests, if any */
el3_recv(dep, TRUE, pktsize);
}
/* Discard top packet from queue */
outw_el3(dep, REG_CmdStatus, CMD_RxDiscard);
return;
}
/*
** Name: void el3_send(dpeth_t *dep, int count)
** Function: Send function. Called from main to transit a packet or
** from interrupt handler when Tx FIFO gets available.
*/
static void el3_send(dpeth_t * dep, int from_int, int count)
{
clock_t now;
int ix;
short int TxStatus;
getuptime(&now);
if ((dep->de_flags & DEF_XMIT_BUSY) &&
(now - dep->de_xmit_start) > 4) {
DEBUG(printf("3c509: Transmitter timed out. Resetting ....\n");)
dep->de_stat.ets_sendErr += 1;
/* Resets and restars the transmitter */
outw_el3(dep, REG_CmdStatus, CMD_TxReset);
outw_el3(dep, REG_CmdStatus, CMD_TxEnable);
dep->de_flags &= NOT(DEF_XMIT_BUSY);
}
if (!(dep->de_flags & DEF_XMIT_BUSY)) {
/* Writes Transmitter preamble 1st Word (packet len, no ints) */
outw_el3(dep, REG_TxFIFO, count);
/* Writes Transmitter preamble 2nd Word (all zero) */
outw_el3(dep, REG_TxFIFO, 0);
/* Writes packet */
el3_write_fifo(dep, count);
getuptime(&dep->de_xmit_start);
dep->de_flags |= (DEF_XMIT_BUSY | DEF_ACK_SEND);
if (inw_el3(dep, REG_TxFree) > ETH_MAX_PACK_SIZE) {
/* Tx has enough room for a packet of maximum size */
dep->de_flags &= NOT(DEF_XMIT_BUSY | DEF_SENDING);
} else {
/* Interrupt driver when enough room is available */
outw_el3(dep, REG_CmdStatus, CMD_SetTxAvailable | ETH_MAX_PACK_SIZE);
dep->de_flags &= NOT(DEF_SENDING);
}
/* Pops Tx status stack */
for (ix = 4; --ix && (TxStatus = inb_el3(dep, REG_TxStatus)) > 0;) {
if (TxStatus & 0x38) dep->de_stat.ets_sendErr += 1;
if (TxStatus & 0x30)
outw_el3(dep, REG_CmdStatus, CMD_TxReset);
if (TxStatus & 0x3C)
outw_el3(dep, REG_CmdStatus, CMD_TxEnable);
outb_el3(dep, REG_TxStatus, 0);
}
}
return;
}
/*
** Name: void el3_close(dpeth_t *dep)
** Function: Stops board and makes it ready to shut down.
*/
static void el3_close(dpeth_t * dep)
{
/* Disables statistics, Receiver and Transmitter */
outw_el3(dep, REG_CmdStatus, CMD_StatsDisable);
outw_el3(dep, REG_CmdStatus, CMD_RxDisable);
outw_el3(dep, REG_CmdStatus, CMD_TxDisable);
if (dep->de_if_port == BNC_XCVR) {
outw_el3(dep, REG_CmdStatus, CMD_StopIntXcvr);
/* milli_delay(5); */
} else if (dep->de_if_port == TP_XCVR) {
SetWindow(WNO_Diagnostics);
outw_el3(dep, REG_MediaStatus, inw_el3(dep, REG_MediaStatus) &
NOT((MediaLBeatEnable | MediaJabberEnable)));
/* milli_delay(5); */
}
DEBUG(printf("%s: stopping Etherlink ... \n", dep->de_name));
/* Issues a global reset
outw_el3(dep, REG_CmdStatus, CMD_GlobalReset); */
sys_irqdisable(&dep->de_hook); /* Disable interrupt */
return;
}
/*
** Name: void el3_interrupt(dpeth_t *dep)
** Function: Interrupt handler. Acknwledges transmit interrupts
** or unloads receive buffer to memory queue.
*/
static void el3_interrupt(dpeth_t * dep)
{
int loop;
unsigned short isr;
for (loop = 5; loop > 0 && ((isr = inw_el3(dep, REG_CmdStatus)) &
(INT_Latch | INT_RxComplete | INT_UpdateStats)); loop -= 1) {
if (isr & INT_RxComplete) /* Got a new packet */
el3_rx_complete(dep);
if (isr & INT_TxAvailable) { /* Tx has room for big packets */
DEBUG(printf("3c509: got Tx interrupt, Status=0x%04x\n", isr);)
dep->de_flags &= NOT(DEF_XMIT_BUSY);
outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | INT_TxAvailable);
if (dep->de_flags & DEF_SENDING) /* Send pending */
el3_send(dep, TRUE, dep->de_send_s);
}
if (isr & (INT_AdapterFail | INT_RxEarly | INT_UpdateStats)) {
if (isr & INT_UpdateStats) /* Empties statistics */
el3_getstats(dep);
if (isr & INT_RxEarly) /* Not really used. Do nothing */
outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | (INT_RxEarly));
if (isr & INT_AdapterFail) {
/* Adapter error. Reset and re-enable receiver */
DEBUG(printf("3c509: got Rx fail interrupt, Status=0x%04x\n", isr);)
el3_rx_mode(dep);
outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | INT_AdapterFail);
}
}
/* Acknowledge interrupt */
outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | (INT_Latch | INT_Requested));
}
return;
}
/*
** Name: unsigned el3_read_eeprom(port_t port, unsigned address);
** Function: Reads the EEPROM at specified address
*/
static unsigned el3_read_eeprom(port_t port, unsigned address)
{
unsigned int result;
int bit;
address |= EL3_READ_EEPROM;
outb(port, address);
milli_delay(5); /* Allows EEPROM reads */
for (result = 0, bit = 16; bit > 0; bit -= 1) {
result = (result << 1) | (inb(port) & 0x0001);
}
return result;
}
/*
** Name: void el3_read_StationAddress(dpeth_t *dep)
** Function: Reads station address from board
*/
static void el3_read_StationAddress(dpeth_t * dep)
{
unsigned int ix, rc;
for (ix = EE_3COM_NODE_ADDR; ix < SA_ADDR_LEN;) {
/* Accesses with word No. */
rc = el3_read_eeprom(dep->de_id_port, ix / 2);
/* Swaps bytes of word */
dep->de_address.ea_addr[ix++] = (rc >> 8) & 0xFF;
dep->de_address.ea_addr[ix++] = rc & 0xFF;
}
return;
}
/*
** Name: void el3_open(dpeth_t *dep)
** Function: Initalizes board hardware and driver data structures.
*/
static void el3_open(dpeth_t * dep)
{
unsigned int AddrCfgReg, ResCfgReg;
unsigned int ix;
el3_read_StationAddress(dep); /* Get ethernet address */
/* Get address and resource configurations */
AddrCfgReg = el3_read_eeprom(dep->de_id_port, EE_ADDR_CFG);
ResCfgReg = el3_read_eeprom(dep->de_id_port, EE_RESOURCE_CFG);
outb(dep->de_id_port, EL3_ACTIVATE); /* Activate the board */
/* Gets xcvr configuration */
dep->de_if_port = AddrCfgReg & EL3_CONFIG_XCVR_MASK;
AddrCfgReg = ((AddrCfgReg & EL3_CONFIG_IOBASE_MASK) << 4) + EL3_IO_BASE_ADDR;
if (AddrCfgReg != dep->de_base_port)
panic(dep->de_name, "Bad I/O port for Etherlink board", NO_NUM);
ResCfgReg >>= 12;
dep->de_irq &= NOT(DEI_DEFAULT); /* Strips the default flag */
if (ResCfgReg != dep->de_irq) panic(dep->de_name, "Bad IRQ for Etherlink board", NO_NUM);
SetWindow(WNO_Setup);
/* Reset transmitter and receiver */
outw_el3(dep, REG_CmdStatus, CMD_TxReset);
outw_el3(dep, REG_CmdStatus, CMD_RxReset);
/* Enable the adapter */
outb_el3(dep, REG_CfgControl, EL3_EnableAdapter);
/* Disable Status bits */
outw_el3(dep, REG_CmdStatus, CMD_SetStatusEnab + 0x00);
/* Set "my own" address */
SetWindow(WNO_StationAddress);
for (ix = 0; ix < 6; ix += 1)
outb_el3(dep, REG_SA0_1 + ix, dep->de_address.ea_addr[ix]);
/* Start Transceivers as required */
if (dep->de_if_port == BNC_XCVR) {
/* Start internal transceiver for Coaxial cable */
outw_el3(dep, REG_CmdStatus, CMD_StartIntXcvr);
milli_delay(5);
} else if (dep->de_if_port == TP_XCVR) {
/* Start internal transceiver for Twisted pair cable */
SetWindow(WNO_Diagnostics);
outw_el3(dep, REG_MediaStatus,
inw_el3(dep, REG_MediaStatus) | (MediaLBeatEnable | MediaJabberEnable));
}
/* Switch to the statistic window, and clear counts (by reading) */
SetWindow(WNO_Statistics);
for (ix = REG_TxCarrierLost; ix <= REG_TxDefer; ix += 1) inb_el3(dep, ix);
inw_el3(dep, REG_RxBytes);
inw_el3(dep, REG_TxBytes);
/* Switch to operating window for normal use */
SetWindow(WNO_Operating);
/* Receive individual address & broadcast. (Mofified later by rx_mode) */
outw_el3(dep, REG_CmdStatus, CMD_SetRxFilter |
(FilterIndividual | FilterBroadcast));
/* Turn on statistics */
outw_el3(dep, REG_CmdStatus, CMD_StatsEnable);
/* Enable transmitter and receiver */
outw_el3(dep, REG_CmdStatus, CMD_TxEnable);
outw_el3(dep, REG_CmdStatus, CMD_RxEnable);
/* Enable all the status bits */
outw_el3(dep, REG_CmdStatus, CMD_SetStatusEnab | 0xFF);
/* Acknowledge all interrupts to clear adapter. Enable interrupts */
outw_el3(dep, REG_CmdStatus, CMD_Acknowledge | 0xFF);
outw_el3(dep, REG_CmdStatus, CMD_SetIntMask |
(INT_Latch | INT_TxAvailable | INT_RxComplete | INT_UpdateStats));
/* Ready to operate, sets the environment for eth_task */
dep->de_data_port = dep->de_base_port;
/* Allocates Rx/Tx buffers */
init_buff(dep, NULL);
/* Device specific functions */
dep->de_recvf = el3_recv;
dep->de_sendf = el3_send;
dep->de_flagsf = el3_rx_mode;
dep->de_resetf = el3_reset;
dep->de_getstatsf = el3_getstats;
dep->de_dumpstatsf = el3_dodump;
dep->de_interruptf = el3_interrupt;
printf("%s: Etherlink III (%s) at %X:%d, %s port - ",
dep->de_name, "3c509", dep->de_base_port, dep->de_irq,
IfNamesMsg[dep->de_if_port >> 14]);
for (ix = 0; ix < SA_ADDR_LEN; ix += 1)
printf("%02X%c", dep->de_address.ea_addr[ix],
ix < SA_ADDR_LEN - 1 ? ':' : '\n');
return; /* Done */
}
/*
** Name: unsigned int el3_checksum(port_t port);
** Function: Reads EEPROM and computes checksum.
*/
static unsigned short el3_checksum(port_t port)
{
unsigned short rc, checksum, address;
unsigned char lo, hi;
for (checksum = address = 0; address < 15; address += 1) {
rc = el3_read_eeprom(port, address);
lo = rc & 0xFF;
hi = (rc >> 8) & 0xFF;
if ((address == EE_PROD_ID && (rc & EE_PROD_ID_MASK) != EL3_PRODUCT_ID) ||
(address == EE_3COM_CODE && rc != EL3_3COM_CODE))
return address;
if (address == EE_ADDR_CFG ||
address == EE_RESOURCE_CFG ||
address == EE_SW_CONFIG_INFO) {
lo ^= hi;
hi = 0;
} else {
hi ^= lo;
lo = 0;
}
rc = ((unsigned) hi << 8) + lo;
checksum ^= rc;
}
rc = el3_read_eeprom(port, address);
return(checksum ^= rc); /* If OK checksum is 0 */
}
/*
** Name: void el3_write_id(port_t port);
** Function: Writes the ID sequence to the board.
*/
static void el3_write_id(port_t port)
{
int ix, pattern;
outb(port, 0); /* Selects the ID port */
outb(port, 0); /* Resets hardware pattern generator */
for (pattern = ix = 0x00FF; ix > 0; ix -= 1) {
outb(port, pattern);
pattern <<= 1;
pattern = (pattern & 0x0100) ? pattern ^ 0xCF : pattern;
}
return;
}
/*
** Name: int el3_probe(dpeth_t *dep)
** Function: Checks for presence of the board.
*/
PUBLIC int el3_probe(dpeth_t * dep)
{
port_t id_port;
/* Don't ask me what is this for !! */
outb(0x0279, 0x02); /* Select PnP config control register. */
outb(0x0A79, 0x02); /* Return to WaitForKey state. */
/* Tests I/O ports in the 0x1xF range for a valid ID port */
for (id_port = 0x110; id_port < 0x200; id_port += 0x10) {
outb(id_port, 0x00);
outb(id_port, 0xFF);
if (inb(id_port) & 0x01) break;
}
if (id_port == 0x200) return 0; /* No board responding */
el3_write_id(id_port);
outb(id_port, EL3_ID_GLOBAL_RESET); /* Reset the board */
milli_delay(5); /* Technical reference says 162 micro sec. */
el3_write_id(id_port);
outb(id_port, EL3_SET_TAG_REGISTER);
milli_delay(5);
dep->de_id_port = id_port; /* Stores ID port No. */
dep->de_ramsize = /* RAM size is meaningless */
dep->de_offset_page = 0;
dep->de_linmem = 0L; /* Access is via I/O port */
/* Device specific functions */
dep->de_initf = el3_open;
dep->de_stopf = el3_close;
return(el3_checksum(id_port) == 0); /* Etherlink board found/not found */
}
#endif /* ENABLE_NETWORKING */
/** 3c509.c **/

164
drivers/dpeth/3c509.h Normal file
View file

@ -0,0 +1,164 @@
/*
** File: 3c509.h Jun. 01, 2000
**
** Author: Giovanni Falzoni <gfalzoni@inwind.it>
**
** Interface description for 3Com Etherlink III board.
**
** $Log$
** Revision 1.1 2005/06/29 10:16:46 beng
** Import of dpeth 3c501/3c509b/.. ethernet driver by
** Giovanni Falzoni <fgalzoni@inwind.it>.
**
** Revision 2.0 2005/06/26 16:16:46 lsodgf0
** Initial revision for Minix 3.0.6
**
** $Id$
*/
/* Command codes */
#define CMD_GlobalReset 0x0000 /* resets adapter (power up status) */
#define CMD_SelectWindow (1<<11) /* select register window */
#define CMD_StartIntXcvr (2<<11) /* start internal transciver */
#define CMD_RxDisable (3<<11) /* rx disable */
#define CMD_RxEnable (4<<11) /* rx enable */
#define CMD_RxReset (5<<11) /* rx reset */
#define CMD_RxDiscard (8<<11) /* rx discard top packet */
#define CMD_TxEnable (9<<11) /* tx enable */
#define CMD_TxDisable (10<<11) /* tx disable */
#define CMD_TxReset (11<<11) /* tx reset */
#define CMD_Acknowledge (13<<11) /* acknowledge interrupt */
#define CMD_SetIntMask (14<<11) /* set interrupt mask */
#define CMD_SetStatusEnab (15<<11) /* set read zero mask */
#define CMD_SetRxFilter (16<<11) /* set rx filter */
#define CMD_SetTxAvailable (18<<11) /* set tx available threshold */
#define CMD_StatsEnable (21<<11) /* statistics enable */
#define CMD_StatsDisable (22<<11) /* statistics disable */
#define CMD_StopIntXcvr (23<<11) /* start internal transciver */
/* Status register bits (INT for interrupt sources, ST for the rest) */
#define INT_Latch 0x0001 /* interrupt latch */
#define INT_AdapterFail 0x0002 /* adapter failure */
#define INT_TxComplete 0x0004 /* tx complete */
#define INT_TxAvailable 0x0008 /* tx available */
#define INT_RxComplete 0x0010 /* rx complete */
#define INT_RxEarly 0x0020 /* rx early */
#define INT_Requested 0x0040 /* interrupt requested */
#define INT_UpdateStats 0x0080 /* update statistics */
/* Rx Status register bits */
#define RXS_Error 0x4000 /* error in packet */
#define RXS_Length 0x07FF /* bytes in RxFIFO */
#define RXS_ErrType 0x3800 /* Rx error type, bit 13-11 */
#define RXS_Overrun 0x0000 /* overrun error */
#define RXS_Oversize 0x0800 /* oversize packet error */
#define RXS_Dribble 0x1000 /* dribble bit (not an error) */
#define RXS_Runt 0x1800 /* runt packet error */
#define RXS_Framing 0x2000 /* framing error */
#define RXS_CRC 0x2800 /* CRC error */
/* Tx Status register bits */
/* Window Numbers */
#define WNO_Setup 0x0000 /* setup/configuration */
#define WNO_Operating 0x0001 /* operating set */
#define WNO_StationAddress 0x0002 /* station address setup/read */
#define WNO_Diagnostics 0x0004 /* diagnostics */
#define WNO_Statistics 0x0006 /* statistics */
/* Register offsets - Window 1 (WNO_Operating) */
#define REG_CmdStatus 0x000E /* command/status */
#define REG_TxFree 0x000C /* free transmit bytes */
#define REG_TxStatus 0x000B /* transmit status (byte) */
#define REG_RxStatus 0x0008 /* receive status */
#define REG_RxFIFO 0x0000 /* RxFIFO read */
#define REG_TxFIFO 0x0000 /* TxFIFO write */
/* Register offsets - Window 0 (WNO_Setup) */
#define REG_CfgControl 0x0004 /* configuration control */
/* Register offsets - Window 2 (WNO_StationAddress) */
#define REG_SA0_1 0x0000 /* station address bytes 0,1 */
/* Register offsets - Window 3 (WNO_FIFO) */
/* Register offsets - Window 4 (WNO_Diagnostics) */
#define REG_MediaStatus 0x000A /* media type/status */
/* Register offsets - Window 5 (WNO_Readable) */
/* Register offsets - Window 6 (WNO_Statistics) */
#define REG_TxBytes 0x000C /* tx bytes ok */
#define REG_RxBytes 0x000A /* rx bytes ok */
#define REG_TxDefer 0x0008 /* tx frames deferred (byte) */
#define REG_RxFrames 0x0007 /* rx frames ok (byte) */
#define REG_TxFrames 0x0006 /* tx frames ok (byte) */
#define REG_RxDiscarded 0x0005 /* rx frames discarded (byte) */
#define REG_TxLate 0x0004 /* tx frames late coll. (byte) */
#define REG_TxSingleColl 0x0003 /* tx frames one coll. (byte) */
#define REG_TxMultColl 0x0002 /* tx frames mult. coll. (byte) */
#define REG_TxNoCD 0x0001 /* tx frames no CDheartbt (byte) */
#define REG_TxCarrierLost 0x0000 /* tx frames carrier lost (byte) */
/* Various command arguments */
#define FilterIndividual 0x0001 /* individual address */
#define FilterMulticast 0x0002 /* multicast/group addresses */
#define FilterBroadcast 0x0004 /* broadcast address */
#define FilterPromiscuous 0x0008 /* promiscuous mode */
/* Resource Configuration Register bits */
#define EL3_CONFIG_IRQ_MASK 0xF000
/* Address Configuration Register bits */
#define EL3_CONFIG_XCVR_MASK 0xC000
#define EL3_CONFIG_IOBASE_MASK 0x001F
#define TP_XCVR 0x0000
#define BNC_XCVR 0xC000
#define AUI_XCVR 0x4000
#define EL3_IO_BASE_ADDR 0x200
/* Transmit Preamble */
/* Bits in various diagnostics registers */
#define MediaLBeatEnable 0x0080 /* link beat enable (TP) */
#define MediaJabberEnable 0x0040 /* jabber enable (TP) */
/* Board identification codes, byte swapped in Rev 0 */
#define EL3_3COM_CODE 0x6D50 /* EISA manufacturer code */
#define EL3_PRODUCT_ID 0x9050 /* Product ID for ISA board */
/* EEProm access */
#define EE_3COM_NODE_ADDR 0x00
#define EE_PROD_ID 0x03
#define EE_MANUFACTURING_DATA 0x04
#define EE_3COM_CODE 0x07
#define EE_ADDR_CFG 0x08
#define EE_RESOURCE_CFG 0x09
#define EE_SW_CONFIG_INFO 0x0D
#define EE_PROD_ID_MASK 0xF0FF /* Mask off revision nibble */
/* Contention logic */
#define EL3_READ_EEPROM 0x80
#define EL3_ID_GLOBAL_RESET 0xC0
#define EL3_SET_TAG_REGISTER 0xD0
#define EL3_ACTIVATE_AND_SET_IO 0xE0
#define EL3_ACTIVATE 0xFF
/* Software Configuration Register bits */
/* Configuration Control Register bits */
#define EL3_EnableAdapter 0x01
/* EL3 access macros */
#define inb_el3(dep,reg) (inb((dep)->de_base_port+(reg)))
#define inw_el3(dep,reg) (inw((dep)->de_base_port+(reg)))
#define outb_el3(dep,reg,data) (outb((dep)->de_base_port+(reg),(data)))
#define outw_el3(dep,reg,data) (outw((dep)->de_base_port+(reg),(data)))
#define SetWindow(win) \
outw(dep->de_base_port+REG_CmdStatus,CMD_SelectWindow|(win))
/** 3c509.h **/

739
drivers/dpeth/8390.c Normal file
View file

@ -0,0 +1,739 @@
/*
** File: 8390.c May 02, 2000
**
** Author: Giovanni Falzoni <gfalzoni@inwind.it>
**
** This file contains an ethernet device driver for NICs
** equipped with the National Semiconductor NS 8390 chip.
** It has to be associated with the board specific driver.
** Rewritten from Minix 2.0.0 ethernet driver dp8390.c
** to extract the NS 8390 common functions.
**
** $Log$
** Revision 1.1 2005/06/29 10:16:46 beng
** Import of dpeth 3c501/3c509b/.. ethernet driver by
** Giovanni Falzoni <fgalzoni@inwind.it>.
**
** Revision 2.0 2005/06/26 16:16:46 lsodgf0
** Initial revision for Minix 3.0.6
**
** $Id$
*/
#include "drivers.h"
#include <minix/com.h>
#include <net/hton.h>
#include <net/gen/ether.h>
#include <net/gen/eth_io.h>
#include "dp.h"
#if (ENABLE_NETWORKING == 1 && ENABLE_DP8390 == 1)
#define PIO16 0 /* NOTE: pio 16 functions missing */
#include "8390.h"
static const char RdmaErrMsg[] = "remote dma failed to complete";
/*
** Name: void ns_rw_setup(dpeth_t *dep, int mode, int size, u16_t offset);
** Function: Sets the board for reading/writing.
*/
static void ns_rw_setup(dpeth_t *dep, int mode, int size, u16_t offset)
{
if (mode == CR_DM_RW) outb_reg0(dep, DP_ISR, ISR_RDC);
outb_reg0(dep, DP_RBCR0, size & 0xFF);
outb_reg0(dep, DP_RBCR1, (size >> 8) & 0xFF);
outb_reg0(dep, DP_RSAR0, offset & 0xFF);
outb_reg0(dep, DP_RSAR1, (offset >> 8) & 0xFF);
mode |= (CR_PS_P0 | CR_STA);
outb_reg0(dep, DP_CR, mode);
return;
}
/*
** Name: void ns_start_xmit(dpeth_t *dep, int size, int pageno);
** Function: Sets the board for for transmitting and fires it.
*/
static void ns_start_xmit(dpeth_t * dep, int size, int pageno)
{
outb_reg0(dep, DP_TPSR, pageno);
outb_reg0(dep, DP_TBCR1, size >> 8);
outb_reg0(dep, DP_TBCR0, size & 0xFF);
outb_reg0(dep, DP_CR, CR_NO_DMA | CR_STA | CR_TXP); /* Fires transmission */
return;
}
/*
** Name: void mem_getblock(dpeth_t *dep, u16_t offset,
** int size, void *dst)
** Function: Reads a block of packet from board (shared memory).
*/
static void mem_getblock(dpeth_t *dep, u16_t offset, int size, void *dst)
{
sys_datacopy(dep->de_memsegm, dep->de_linmem + offset,
SELF, (vir_bytes)dst, size);
return;
}
/*
** Name: void mem_nic2user(dpeth_t *dep, int pageno, int pktsize);
** Function: Copies a packet from board to user area (shared memory).
*/
static void mem_nic2user(dpeth_t * dep, int pageno, int pktsize)
{
phys_bytes offset, phys_user;
iovec_dat_t *iovp = &dep->de_read_iovec;
int bytes, ix = 0;
/* Computes shared memory address (skipping receive header) */
offset = pageno * DP_PAGESIZE + sizeof(dp_rcvhdr_t);
do { /* Reads chuncks of packet into user area */
bytes = iovp->iod_iovec[ix].iov_size; /* Size of a chunck */
if (bytes > pktsize) bytes = pktsize;
/* Reads from board to user area */
if ((offset + bytes) > (dep->de_stoppage * DP_PAGESIZE)) {
/* Circular buffer wrap-around */
bytes = dep->de_stoppage * DP_PAGESIZE - offset;
sys_datacopy(dep->de_memsegm, dep->de_linmem + offset,
iovp->iod_proc_nr, iovp->iod_iovec[ix].iov_addr, bytes);
pktsize -= bytes;
phys_user += bytes;
bytes = iovp->iod_iovec[ix].iov_size - bytes;
if (bytes > pktsize) bytes = pktsize;
offset = dep->de_startpage * DP_PAGESIZE;
}
sys_datacopy(dep->de_memsegm, dep->de_linmem + offset,
iovp->iod_proc_nr, iovp->iod_iovec[ix].iov_addr, bytes);
offset += bytes;
if (++ix >= IOVEC_NR) { /* Next buffer of IO vector */
dp_next_iovec(iovp);
ix = 0;
}
/* Till packet done */
} while ((pktsize -= bytes) > 0);
return;
}
/*
** Name: void mem_user2nic(dpeth_t *dep, int pageno, int pktsize)
** Function: Copies a packet from user area to board (shared memory).
*/
static void mem_user2nic(dpeth_t *dep, int pageno, int pktsize)
{
phys_bytes offset, phys_user;
iovec_dat_t *iovp = &dep->de_write_iovec;
int bytes, ix = 0;
/* Computes shared memory address */
offset = dep->de_linmem + pageno * DP_PAGESIZE;
do { /* Reads chuncks of packet from user area */
bytes = iovp->iod_iovec[ix].iov_size; /* Size of chunck */
if (bytes > pktsize) bytes = pktsize;
/* Reads from user area to board (shared memory) */
sys_datacopy(iovp->iod_proc_nr, iovp->iod_iovec[ix].iov_addr,
dep->de_memsegm, dep->de_linmem + offset, bytes);
offset += bytes;
if (++ix >= IOVEC_NR) { /* Next buffer of IO vector */
dp_next_iovec(iovp);
ix = 0;
}
/* Till packet done */
} while ((pktsize -= bytes) > 0);
return;
}
/*
** Name: void pio_getblock(dpeth_t *dep, u16_t offset,
** int size, void *dst)
** Function: Reads a block of packet from board (Prog. I/O).
*/
static void pio_getblock(dpeth_t *dep, u16_t offset, int size, void *dst)
{
/* Sets up board for reading */
ns_rw_setup(dep, CR_DM_RR, size, offset);
#if PIO16 == 0
insb(dep->de_data_port, SELF, dst, size);
#else
if (dep->de_16bit == TRUE) {
insw(dep->de_data_port, dst, size);
} else {
insb(dep->de_data_port, dst, size);
}
#endif
return;
}
/*
** Name: void pio_nic2user(dpeth_t *dep, int pageno, int pktsize)
** Function: Copies a packet from board to user area (Prog. I/O).
*/
static void pio_nic2user(dpeth_t *dep, int pageno, int pktsize)
{
phys_bytes phys_user;
iovec_dat_t *iovp = &dep->de_read_iovec;
unsigned offset; int bytes, ix = 0;
/* Computes memory address (skipping receive header) */
offset = pageno * DP_PAGESIZE + sizeof(dp_rcvhdr_t);
/* Sets up board for reading */
ns_rw_setup(dep, CR_DM_RR, ((offset + pktsize) > (dep->de_stoppage * DP_PAGESIZE)) ?
(dep->de_stoppage * DP_PAGESIZE) - offset : pktsize, offset);
do { /* Reads chuncks of packet into user area */
bytes = iovp->iod_iovec[ix].iov_size; /* Size of a chunck */
if (bytes > pktsize) bytes = pktsize;
if ((offset + bytes) > (dep->de_stoppage * DP_PAGESIZE)) {
/* Circular buffer wrap-around */
bytes = dep->de_stoppage * DP_PAGESIZE - offset;
insb(dep->de_data_port, iovp->iod_proc_nr, (void*)(iovp->iod_iovec[ix].iov_addr), bytes);
pktsize -= bytes;
iovp->iod_iovec[ix].iov_addr += bytes;
bytes = iovp->iod_iovec[ix].iov_size - bytes;
if (bytes > pktsize) bytes = pktsize;
offset = dep->de_startpage * DP_PAGESIZE;
ns_rw_setup(dep, CR_DM_RR, pktsize, offset);
}
insb(dep->de_data_port, iovp->iod_proc_nr, (void*)(iovp->iod_iovec[ix].iov_addr), bytes);
offset += bytes;
if (++ix >= IOVEC_NR) { /* Next buffer of IO vector */
dp_next_iovec(iovp);
ix = 0;
}
/* Till packet done */
} while ((pktsize -= bytes) > 0);
return;
}
/*
** Name: void pio_user2nic(dpeth_t *dep, int pageno, int pktsize)
** Function: Copies a packet from user area to board (Prog. I/O).
*/
static void pio_user2nic(dpeth_t *dep, int pageno, int pktsize)
{
phys_bytes phys_user;
iovec_dat_t *iovp = &dep->de_write_iovec;
int bytes, ix = 0;
/* Sets up board for writing */
ns_rw_setup(dep, CR_DM_RW, pktsize, pageno * DP_PAGESIZE);
do { /* Reads chuncks of packet from user area */
bytes = iovp->iod_iovec[ix].iov_size; /* Size of chunck */
if (bytes > pktsize) bytes = pktsize;
outsb(dep->de_data_port, iovp->iod_proc_nr,
(void*)(iovp->iod_iovec[ix].iov_addr), bytes);
if (++ix >= IOVEC_NR) { /* Next buffer of I/O vector */
dp_next_iovec(iovp);
ix = 0;
}
/* Till packet done */
} while ((pktsize -= bytes) > 0);
for (ix = 0; ix < 100; ix += 1) {
if (inb_reg0(dep, DP_ISR) & ISR_RDC) break;
}
if (ix == 100) {
panic(dep->de_name, RdmaErrMsg, NO_NUM);
}
return;
}
/*
** Name: void ns_stats(dpeth_t * dep)
** Function: Updates counters reading from device
*/
static void ns_stats(dpeth_t * dep)
{
dep->de_stat.ets_CRCerr += inb_reg0(dep, DP_CNTR0);
dep->de_stat.ets_recvErr += inb_reg0(dep, DP_CNTR1);
dep->de_stat.ets_fifoOver += inb_reg0(dep, DP_CNTR2);
return;
}
/*
** Name: void ns_dodump(dpeth_t * dep)
** Function: Displays statistics (a request from F5 key).
*/
static void ns_dodump(dpeth_t * dep)
{
ns_stats(dep); /* Forces reading fo counters from board */
return;
}
/*
** Name: void ns_reinit(dpeth_t *dep)
** Function: Updates receiver configuration.
*/
static void ns_reinit(dpeth_t * dep)
{
int dp_reg = 0;
if (dep->de_flags & DEF_PROMISC) dp_reg |= RCR_AB | RCR_PRO | RCR_AM;
if (dep->de_flags & DEF_BROAD) dp_reg |= RCR_AB;
if (dep->de_flags & DEF_MULTI) dp_reg |= RCR_AM;
outb_reg0(dep, DP_CR, CR_PS_P0);
outb_reg0(dep, DP_RCR, dp_reg);
return;
}
/*
** Name: void ns_send(dpeth_t * dep, int from_int, int size)
** Function: Transfers packet to device and starts sending.
*/
static void ns_send(dpeth_t * dep, int from_int, int size)
{
int queue;
if (queue = dep->de_sendq_head, dep->de_sendq[queue].sq_filled) {
if (from_int) panic(dep->de_name, "should not be sending ", NO_NUM);
dep->de_send_s = size;
return;
}
(dep->de_user2nicf) (dep, dep->de_sendq[queue].sq_sendpage, size);
dep->bytes_Tx += (long) size;
dep->de_sendq[queue].sq_filled = TRUE;
dep->de_flags |= (DEF_XMIT_BUSY | DEF_ACK_SEND);
if (dep->de_sendq_tail == queue) { /* there it goes.. */
ns_start_xmit(dep, size, dep->de_sendq[queue].sq_sendpage);
} else
dep->de_sendq[queue].sq_size = size;
if (++queue == dep->de_sendq_nr) queue = 0;
dep->de_sendq_head = queue;
dep->de_flags &= NOT(DEF_SENDING);
return;
}
/*
** Name: void ns_reset(dpeth_t *dep)
** Function: Resets device.
*/
static void ns_reset(dpeth_t * dep)
{
int ix;
/* Stop chip */
outb_reg0(dep, DP_CR, CR_STP | CR_NO_DMA);
outb_reg0(dep, DP_RBCR0, 0);
outb_reg0(dep, DP_RBCR1, 0);
for (ix = 0; ix < 0x1000 && ((inb_reg0(dep, DP_ISR) & ISR_RST) == 0); ix += 1)
/* Do nothing */ ;
outb_reg0(dep, DP_TCR, TCR_1EXTERNAL | TCR_OFST);
outb_reg0(dep, DP_CR, CR_STA | CR_NO_DMA);
outb_reg0(dep, DP_TCR, TCR_NORMAL | TCR_OFST);
/* Acknowledge the ISR_RDC (remote dma) interrupt. */
for (ix = 0; ix < 0x1000 && ((inb_reg0(dep, DP_ISR) & ISR_RDC) == 0); ix += 1)
/* Do nothing */ ;
outb_reg0(dep, DP_ISR, inb_reg0(dep, DP_ISR) & NOT(ISR_RDC));
/* Reset the transmit ring. If we were transmitting a packet, we
* pretend that the packet is processed. Higher layers will
* retransmit if the packet wasn't actually sent. */
dep->de_sendq_head = dep->de_sendq_tail = 0;
for (ix = 0; ix < dep->de_sendq_nr; ix++)
dep->de_sendq[ix].sq_filled = FALSE;
ns_send(dep, TRUE, dep->de_send_s);
return;
}
/*
** Name: void ns_recv(dpeth_t *dep, int fromint, int size)
** Function: Gets a packet from device
*/
static void ns_recv(dpeth_t *dep, int fromint, int size)
{
dp_rcvhdr_t header;
dp_rcvhdr_t dummy;
unsigned pageno, curr, next;
vir_bytes length;
int packet_processed = FALSE;
#ifdef ETH_IGN_PROTO
u16_t eth_type;
#endif
pageno = inb_reg0(dep, DP_BNRY) + 1;
if (pageno == dep->de_stoppage) pageno = dep->de_startpage;
do {
/* */
outb_reg0(dep, DP_CR, CR_PS_P1);
curr = inb_reg1(dep, DP_CURR);
outb_reg0(dep, DP_CR, CR_PS_P0 | CR_NO_DMA | CR_STA);
if (curr == pageno) break;
(dep->de_getblockf) (dep, pageno * DP_PAGESIZE, sizeof(header), &header);
#ifdef ETH_IGN_PROTO
(dep->de_getblockf) (dep, pageno * DP_PAGESIZE + sizeof(header) + 2 * sizeof(ether_addr_t), sizeof(eth_type), &eth_type);
#endif
length = (header.dr_rbcl | (header.dr_rbch << 8)) - sizeof(dp_rcvhdr_t);
next = header.dr_next;
if (length < ETH_MIN_PACK_SIZE || length > ETH_MAX_PACK_SIZE) {
printf("%s: packet with strange length arrived: %d\n", dep->de_name, length);
dep->de_stat.ets_recvErr += 1;
next = curr;
} else if (next < dep->de_startpage || next >= dep->de_stoppage) {
printf("%s: strange next page\n", dep->de_name);
dep->de_stat.ets_recvErr += 1;
next = curr;
#ifdef ETH_IGN_PROTO
} else if (eth_type == eth_ign_proto) {
/* Hack: ignore packets of a given protocol */
static int first = TRUE;
if (first) {
first = FALSE;
printf("%s: dropping proto %04x packet\n", dep->de_name, ntohs(eth_ign_proto));
}
next = curr;
#endif
} else if (header.dr_status & RSR_FO) {
/* This is very serious, issue a warning and reset buffers */
printf("%s: fifo overrun, resetting receive buffer\n", dep->de_name);
dep->de_stat.ets_fifoOver += 1;
next = curr;
} else if ((header.dr_status & RSR_PRX) && (dep->de_flags & DEF_ENABLED)) {
if (!(dep->de_flags & DEF_READING)) break;
(dep->de_nic2userf) (dep, pageno, length);
dep->de_read_s = length;
dep->de_flags |= DEF_ACK_RECV;
dep->de_flags &= NOT(DEF_READING);
packet_processed = TRUE;
}
dep->bytes_Rx += (long) length;
dep->de_stat.ets_packetR += 1;
outb_reg0(dep, DP_BNRY, (next == dep->de_startpage ? dep->de_stoppage : next) - 1);
pageno = next;
} while (!packet_processed);
#if 0
if ((dep->de_flags & (DEF_READING | DEF_STOPPED)) == (DEF_READING | DEF_STOPPED))
/* The chip is stopped, and all arrived packets delivered */
(*dep->de_resetf) (dep);
dep->de_flags &= NOT(DEF_STOPPED);
#endif
return;
}
/*
** Name: void ns_interrupt(dpeth_t * dep)
** Function: Handles interrupt.
*/
static void ns_interrupt(dpeth_t * dep)
{
int isr, tsr;
int size, queue;
while ((isr = inb_reg0(dep, DP_ISR)) != 0) {
outb_reg0(dep, DP_ISR, isr);
if (isr & (ISR_PTX | ISR_TXE)) {
tsr = inb_reg0(dep, DP_TSR);
if (tsr & TSR_PTX) {
dep->de_stat.ets_packetT++;
}
if (tsr & TSR_COL) dep->de_stat.ets_collision++;
if (tsr & (TSR_ABT | TSR_FU)) {
dep->de_stat.ets_fifoUnder++;
}
if ((isr & ISR_TXE) || (tsr & (TSR_CRS | TSR_CDH | TSR_OWC))) {
printf("%s: got send Error (0x%02X)\n", dep->de_name, tsr);
dep->de_stat.ets_sendErr++;
}
queue = dep->de_sendq_tail;
if (!(dep->de_sendq[queue].sq_filled)) { /* Hardware bug? */
printf("%s: transmit interrupt, but not sending\n", dep->de_name);
continue;
}
dep->de_sendq[queue].sq_filled = FALSE;
if (++queue == dep->de_sendq_nr) queue = 0;
dep->de_sendq_tail = queue;
if (dep->de_sendq[queue].sq_filled) {
ns_start_xmit(dep, dep->de_sendq[queue].sq_size,
dep->de_sendq[queue].sq_sendpage);
}
if (dep->de_flags & DEF_SENDING) {
ns_send(dep, TRUE, dep->de_send_s);
}
}
if (isr & ISR_PRX) {
ns_recv(dep, TRUE, 0);
}
if (isr & ISR_RXE) {
printf("%s: got recv Error (0x%04X)\n", dep->de_name, inb_reg0(dep, DP_RSR));
dep->de_stat.ets_recvErr++;
}
if (isr & ISR_CNT) {
dep->de_stat.ets_CRCerr += inb_reg0(dep, DP_CNTR0);
dep->de_stat.ets_recvErr += inb_reg0(dep, DP_CNTR1);
dep->de_stat.ets_fifoOver += inb_reg0(dep, DP_CNTR2);
}
if (isr & ISR_OVW) {
printf("%s: got overwrite warning\n", dep->de_name);
}
if (isr & ISR_RDC) {
/* Nothing to do */
}
if (isr & ISR_RST) {
/* This means we got an interrupt but the ethernet
* chip is shutdown. We set the flag DEF_STOPPED, and
* continue processing arrived packets. When the
* receive buffer is empty, we reset the dp8390. */
printf("%s: network interface stopped\n", dep->de_name);
dep->de_flags |= DEF_STOPPED;
break;
}
}
if ((dep->de_flags & (DEF_READING | DEF_STOPPED)) == (DEF_READING | DEF_STOPPED)) {
/* The chip is stopped, and all arrived packets delivered */
ns_reset(dep);
dep->de_flags &= NOT(DEF_STOPPED);
}
return;
}
/*
** Name: void ns_init(dpeth_t *dep)
** Function: Initializes the NS 8390
*/
void ns_init(dpeth_t * dep)
{
int dp_reg;
int ix;
/* NS8390 initialization (as recommended in National Semiconductor specs) */
outb_reg0(dep, DP_CR, CR_PS_P0 | CR_STP | CR_NO_DMA); /* 0x21 */
#if PIO16 == 0
outb_reg0(dep, DP_DCR, (DCR_BYTEWIDE | DCR_LTLENDIAN | DCR_8BYTES | DCR_BMS));
#else
outb_reg0(dep, DP_DCR, (((dep->de_16bit) ? DCR_WORDWIDE : DCR_BYTEWIDE) |
DCR_LTLENDIAN | DCR_8BYTES | DCR_BMS));
#endif
outb_reg0(dep, DP_RBCR0, 0);
outb_reg0(dep, DP_RBCR1, 0);
outb_reg0(dep, DP_RCR, RCR_MON); /* Sets Monitor mode */
outb_reg0(dep, DP_TCR, TCR_INTERNAL); /* Sets Loopback mode 1 */
outb_reg0(dep, DP_PSTART, dep->de_startpage);
outb_reg0(dep, DP_PSTOP, dep->de_stoppage);
outb_reg0(dep, DP_BNRY, dep->de_stoppage - 1);
outb_reg0(dep, DP_ISR, 0xFF); /* Clears Interrupt Status Register */
outb_reg0(dep, DP_IMR, 0); /* Clears Interrupt Mask Register */
/* Copies station address in page 1 registers */
outb_reg0(dep, DP_CR, CR_PS_P1 | CR_NO_DMA); /* Selects Page 1 */
for (ix = 0; ix < SA_ADDR_LEN; ix += 1) /* Initializes address */
outb_reg1(dep, DP_PAR0 + ix, dep->de_address.ea_addr[ix]);
for (ix = DP_MAR0; ix <= DP_MAR7; ix += 1) /* Initializes address */
outb_reg1(dep, ix, 0xFF);
outb_reg1(dep, DP_CURR, dep->de_startpage);
outb_reg1(dep, DP_CR, CR_PS_P0 | CR_NO_DMA); /* Selects Page 0 */
inb_reg0(dep, DP_CNTR0); /* Resets counters by reading them */
inb_reg0(dep, DP_CNTR1);
inb_reg0(dep, DP_CNTR2);
dp_reg = IMR_PRXE | IMR_PTXE | IMR_RXEE | IMR_TXEE | IMR_OVWE | IMR_CNTE;
outb_reg0(dep, DP_ISR, 0xFF); /* Clears Interrupt Status Register */
outb_reg0(dep, DP_IMR, dp_reg); /* Sets Interrupt Mask register */
dp_reg = 0;
if (dep->de_flags & DEF_PROMISC) dp_reg |= RCR_AB | RCR_PRO | RCR_AM;
if (dep->de_flags & DEF_BROAD) dp_reg |= RCR_AB;
if (dep->de_flags & DEF_MULTI) dp_reg |= RCR_AM;
outb_reg0(dep, DP_RCR, dp_reg); /* Sets receive as requested */
outb_reg0(dep, DP_TCR, TCR_NORMAL); /* Sets transmitter */
outb_reg0(dep, DP_CR, CR_STA | CR_NO_DMA); /* Starts board */
/* Initializes the send queue. */
for (ix = 0; ix < dep->de_sendq_nr; ix += 1)
dep->de_sendq[ix].sq_filled = 0;
dep->de_sendq_head = dep->de_sendq_tail = 0;
/* Device specific functions */
if (!dep->de_prog_IO) {
dep->de_user2nicf = mem_user2nic;
dep->de_nic2userf = mem_nic2user;
dep->de_getblockf = mem_getblock;
} else {
#if PIO16 == 0
dep->de_user2nicf = pio_user2nic;
dep->de_nic2userf = pio_nic2user;
dep->de_getblockf = pio_getblock;
#else
#error Missing I/O functions for pio 16 bits
#endif
}
dep->de_recvf = ns_recv;
dep->de_sendf = ns_send;
dep->de_flagsf = ns_reinit;
dep->de_resetf = ns_reset;
dep->de_getstatsf = ns_stats;
dep->de_dumpstatsf = ns_dodump;
dep->de_interruptf = ns_interrupt;
return; /* Done */
}
#if PIO16 == 1
/*
** Name: void dp_pio16_user2nic(dpeth_t *dep, int pageno, int pktsize)
** Function: Copies a packet from user area to board (Prog. I/O, 16bits).
*/
static void dp_pio16_user2nic(dpeth_t *dep, int pageno, int pktsize)
{
u8_t two_bytes[2];
phys_bytes phys_user, phys_2bytes = vir2phys(two_bytes);
vir_bytes ecount = (pktsize + 1) & NOT(0x0001);
int bytes, ix = 0, odd_byte = 0;
iovec_dat_t *iovp = &dep->de_write_iovec;
outb_reg0(dep, DP_ISR, ISR_RDC);
dp_read_setup(dep, ecount, pageno * DP_PAGESIZE);
do {
bytes = iovp->iod_iovec[ix].iov_size;
if (bytes > pktsize) bytes = pktsize;
phys_user = numap(iovp->iod_proc_nr, iovp->iod_iovec[ix].iov_addr, bytes);
if (!phys_user) panic(dep->de_name, UmapErrMsg, NO_NUM);
if (odd_byte) {
phys_copy(phys_user, phys_2bytes + 1, (phys_bytes) 1);
out_word(dep->de_data_port, *(u16_t *)two_bytes);
pktsize--;
bytes--;
phys_user++;
odd_byte = 0;
if (!bytes) continue;
}
ecount = bytes & NOT(0x0001);
if (ecount != 0) {
phys_outsw(dep->de_data_port, phys_user, ecount);
pktsize -= ecount;
bytes -= ecount;
phys_user += ecount;
}
if (bytes) {
phys_copy(phys_user, phys_2bytes, (phys_bytes) 1);
pktsize--;
bytes--;
phys_user++;
odd_byte = 1;
}
if (++ix >= IOVEC_NR) { /* Next buffer of I/O vector */
dp_next_iovec(iovp);
ix = 0;
}
} while (bytes > 0);
if (odd_byte) out_word(dep->de_data_port, *(u16_t *) two_bytes);
for (ix = 0; ix < 100; ix++) {
if (inb_reg0(dep, DP_ISR) & ISR_RDC) break;
}
if (ix == 100) {
panic(dep->de_name, RdmaErrMsg, NO_NUM);
}
return;
}
/*
** Name: void dp_pio16_nic2user(dpeth_t *dep, int pageno, int pktsize)
** Function: Copies a packet from board to user area (Prog. I/O, 16bits).
*/
static void dp_pio16_nic2user(dpeth_t * dep, int nic_addr, int count)
{
phys_bytes phys_user;
vir_bytes ecount;
int bytes, i;
u8_t two_bytes[2];
phys_bytes phys_2bytes;
int odd_byte;
ecount = (count + 1) & ~1;
phys_2bytes = vir2phys(two_bytes);
odd_byte = 0;
dp_read_setup(dep, ecount, nic_addr);
i = 0;
while (count > 0) {
if (i >= IOVEC_NR) {
dp_next_iovec(iovp);
i = 0;
continue;
}
bytes = iovp->iod_iovec[i].iov_size;
if (bytes > count) bytes = count;
phys_user = numap(iovp->iod_proc_nr,
iovp->iod_iovec[i].iov_addr, bytes);
if (!phys_user) panic(dep->de_name, UmapErrMsg, NO_NUM);
if (odd_byte) {
phys_copy(phys_2bytes + 1, phys_user, (phys_bytes) 1);
count--;
bytes--;
phys_user++;
odd_byte = 0;
if (!bytes) continue;
}
ecount = bytes & ~1;
if (ecount != 0) {
phys_insw(dep->de_data_port, phys_user, ecount);
count -= ecount;
bytes -= ecount;
phys_user += ecount;
}
if (bytes) {
*(u16_t *) two_bytes = in_word(dep->de_data_port);
phys_copy(phys_2bytes, phys_user, (phys_bytes) 1);
count--;
bytes--;
phys_user++;
odd_byte = 1;
}
}
return;
}
#endif /* PIO16 == 1 */
#endif /* ENABLE_NETWORKING && ENABLE_DP8390 */
/** end 8390.c **/

172
drivers/dpeth/8390.h Normal file
View file

@ -0,0 +1,172 @@
/*
** File: 8390.h May 02, 2000
**
** Author: Giovanni Falzoni <gfalzoni@inwind.it>
**
** National Semiconductor NS 8390 Network Interface Controller
**
** $Log$
** Revision 1.1 2005/06/29 10:16:46 beng
** Import of dpeth 3c501/3c509b/.. ethernet driver by
** Giovanni Falzoni <fgalzoni@inwind.it>.
**
** Revision 2.0 2005/06/26 16:16:46 lsodgf0
** Initial revision for Minix 3.0.6
**
** $Id$
*/
#define DP_PAGESIZE 256 /* NS 8390 page size */
#define SENDQ_PAGES 6 /* SENDQ_PAGES * DP_PAGESIZE >= 1514 bytes */
/* Page 0, read/write ------------- */
#define DP_CR 0x00 /* Command Register RW */
#define DP_CLDA0 0x01 /* Current Local Dma Address 0 RO */
#define DP_PSTART 0x01 /* Page Start Register WO */
#define DP_CLDA1 0x02 /* Current Local Dma Address 1 RO */
#define DP_PSTOP 0x02 /* Page Stop Register WO */
#define DP_BNRY 0x03 /* Boundary Pointer RW */
#define DP_TSR 0x04 /* Transmit Status Register RO */
#define DP_TPSR 0x04 /* Transmit Page Start Register WO */
#define DP_NCR 0x05 /* No. of Collisions Register RO */
#define DP_TBCR0 0x05 /* Transmit Byte Count Reg. 0 WO */
#define DP_FIFO 0x06 /* Fifo RO */
#define DP_TBCR1 0x06 /* Transmit Byte Count Reg. 1 WO */
#define DP_ISR 0x07 /* Interrupt Status Register RW */
#define DP_CRDA0 0x08 /* Current Remote Dma Addr.Low RO */
#define DP_RSAR0 0x08 /* Remote Start Address Low WO */
#define DP_CRDA1 0x09 /* Current Remote Dma Addr.High RO */
#define DP_RSAR1 0x09 /* Remote Start Address High WO */
#define DP_RBCR0 0x0A /* Remote Byte Count Low WO */
#define DP_RBCR1 0x0B /* Remote Byte Count Hihg WO */
#define DP_RSR 0x0C /* Receive Status Register RO */
#define DP_RCR 0x0C /* Receive Config. Register WO */
#define DP_CNTR0 0x0D /* Tally Counter 0 RO */
#define DP_TCR 0x0D /* Transmit Config. Register WO */
#define DP_CNTR1 0x0E /* Tally Counter 1 RO */
#define DP_DCR 0x0E /* Data Configuration Register WO */
#define DP_CNTR2 0x0F /* Tally Counter 2 RO */
#define DP_IMR 0x0F /* Interrupt Mask Register WO */
/* Page 1, read/write -------------- */
/* DP_CR 0x00 Command Register */
#define DP_PAR0 0x01 /* Physical Address Register 0 */
#define DP_PAR1 0x02 /* Physical Address Register 1 */
#define DP_PAR2 0x03 /* Physical Address Register 2 */
#define DP_PAR3 0x04 /* Physical Address Register 3 */
#define DP_PAR4 0x05 /* Physical Address Register 4 */
#define DP_PAR5 0x06 /* Physical Address Register 5 */
#define DP_CURR 0x07 /* Current Page Register */
#define DP_MAR0 0x08 /* Multicast Address Register 0 */
#define DP_MAR1 0x09 /* Multicast Address Register 1 */
#define DP_MAR2 0x0A /* Multicast Address Register 2 */
#define DP_MAR3 0x0B /* Multicast Address Register 3 */
#define DP_MAR4 0x0C /* Multicast Address Register 4 */
#define DP_MAR5 0x0D /* Multicast Address Register 5 */
#define DP_MAR6 0x0E /* Multicast Address Register 6 */
#define DP_MAR7 0x0F /* Multicast Address Register 7 */
/* Bits in dp_cr */
#define CR_STP 0x01 /* Stop: software reset */
#define CR_STA 0x02 /* Start: activate NIC */
#define CR_TXP 0x04 /* Transmit Packet */
#define CR_DMA 0x38 /* Mask for DMA control */
#define CR_DM_RR 0x08 /* DMA: Remote Read */
#define CR_DM_RW 0x10 /* DMA: Remote Write */
#define CR_DM_SP 0x18 /* DMA: Send Packet */
#define CR_NO_DMA 0x20 /* DMA: Stop Remote DMA Operation */
#define CR_PS 0xC0 /* Mask for Page Select */
#define CR_PS_P0 0x00 /* Register Page 0 */
#define CR_PS_P1 0x40 /* Register Page 1 */
#define CR_PS_P2 0x80 /* Register Page 2 */
/* Bits in dp_isr */
#define ISR_MASK 0x3F
#define ISR_PRX 0x01 /* Packet Received with no errors */
#define ISR_PTX 0x02 /* Packet Transmitted with no errors */
#define ISR_RXE 0x04 /* Receive Error */
#define ISR_TXE 0x08 /* Transmit Error */
#define ISR_OVW 0x10 /* Overwrite Warning */
#define ISR_CNT 0x20 /* Counter Overflow */
#define ISR_RDC 0x40 /* Remote DMA Complete */
#define ISR_RST 0x80 /* Reset Status */
/* Bits in dp_imr */
#define IMR_PRXE 0x01 /* Packet Received Enable */
#define IMR_PTXE 0x02 /* Packet Transmitted Enable */
#define IMR_RXEE 0x04 /* Receive Error Enable */
#define IMR_TXEE 0x08 /* Transmit Error Enable */
#define IMR_OVWE 0x10 /* Overwrite Warning Enable */
#define IMR_CNTE 0x20 /* Counter Overflow Enable */
#define IMR_RDCE 0x40 /* DMA Complete Enable */
/* Bits in dp_dcr */
#define DCR_WTS 0x01 /* Word Transfer Select */
#define DCR_BYTEWIDE 0x00 /* WTS: byte wide transfers */
#define DCR_WORDWIDE 0x01 /* WTS: word wide transfers */
#define DCR_BOS 0x02 /* Byte Order Select */
#define DCR_LTLENDIAN 0x00 /* BOS: Little Endian */
#define DCR_BIGENDIAN 0x02 /* BOS: Big Endian */
#define DCR_LAS 0x04 /* Long Address Select */
#define DCR_BMS 0x08 /* Burst Mode Select */
#define DCR_AR 0x10 /* Autoinitialize Remote */
#define DCR_FTS 0x60 /* Fifo Threshold Select */
#define DCR_2BYTES 0x00 /* Fifo Threshold: 2 bytes */
#define DCR_4BYTES 0x20 /* Fifo Threshold: 4 bytes */
#define DCR_8BYTES 0x40 /* Fifo Threshold: 8 bytes */
#define DCR_12BYTES 0x60 /* Fifo Threshold: 12 bytes */
/* Bits in dp_tcr */
#define TCR_CRC 0x01 /* Inhibit CRC */
#define TCR_ELC 0x06 /* Encoded Loopback Control */
#define TCR_NORMAL 0x00 /* ELC: Normal Operation */
#define TCR_INTERNAL 0x02 /* ELC: Internal Loopback */
#define TCR_0EXTERNAL 0x04 /* ELC: External Loopback LPBK=0 */
#define TCR_1EXTERNAL 0x06 /* ELC: External Loopback LPBK=1 */
#define TCR_ATD 0x08 /* Auto Transmit */
#define TCR_OFST 0x10 /* Collision Offset Enable */
/* Bits in dp_tsr */
#define TSR_PTX 0x01 /* Packet Transmitted (without error) */
#define TSR_DFR 0x02 /* Transmit Deferred */
#define TSR_COL 0x04 /* Transmit Collided */
#define TSR_ABT 0x08 /* Transmit Aborted */
#define TSR_CRS 0x10 /* Carrier Sense Lost */
#define TSR_FU 0x20 /* FIFO Underrun */
#define TSR_CDH 0x40 /* CD Heartbeat */
#define TSR_OWC 0x80 /* Out of Window Collision */
/* Bits in dp_rcr */
#define RCR_SEP 0x01 /* Save Errored Packets */
#define RCR_AR 0x02 /* Accept Runt Packets */
#define RCR_AB 0x04 /* Accept Broadcast */
#define RCR_AM 0x08 /* Accept Multicast */
#define RCR_PRO 0x10 /* Physical Promiscuous */
#define RCR_MON 0x20 /* Monitor Mode */
/* Bits in dp_rsr */
#define RSR_PRX 0x01 /* Packet Received Intact */
#define RSR_CRC 0x02 /* CRC Error */
#define RSR_FAE 0x04 /* Frame Alignment Error */
#define RSR_FO 0x08 /* FIFO Overrun */
#define RSR_MPA 0x10 /* Missed Packet */
#define RSR_PHY 0x20 /* Multicast Address Match !! */
#define RSR_DIS 0x40 /* Receiver Disabled */
/* Some macros to simplify accessing the dp8390 */
#define inb_reg0(dep,reg) (inb(dep->de_dp8390_port+reg))
#define outb_reg0(dep,reg,data) (outb(dep->de_dp8390_port+reg,data))
#define inb_reg1(dep,reg) (inb(dep->de_dp8390_port+reg))
#define outb_reg1(dep,reg,data) (outb(dep->de_dp8390_port+reg,data))
typedef struct dp_rcvhdr {
u8_t dr_status; /* Copy of rsr */
u8_t dr_next; /* Pointer to next packet */
u8_t dr_rbcl; /* Receive Byte Count Low */
u8_t dr_rbch; /* Receive Byte Count High */
} dp_rcvhdr_t;
void ns_init(dpeth_t *);
/** 8390.h **/

52
drivers/dpeth/Makefile Normal file
View file

@ -0,0 +1,52 @@
##
## Makefile for ISA ethernet drivers May 02, 2000
##
## $Log$
## Revision 1.1 2005/06/29 10:16:46 beng
## Import of dpeth 3c501/3c509b/.. ethernet driver by
## Giovanni Falzoni <fgalzoni@inwind.it>.
##
## Revision 2.0 2005/06/26 16:16:46 lsodgf0
## Initial revision for Minix 3.0.6
##
## $Id$
## Programs, flags, etc.
DRIVER = dpeth
debug = 0
CC = exec cc
LD = $(CC)
CPPFLAGS= -I.. -I/usr/include -Ddebug=$(debug)
CFLAGS = -ws $(CPPFLAGS)
LDFLAGS = -i -o $@
SRCS = 3c501.c 3c509.c 3c503.c ne.c wd.c 8390.c devio.c netbuff.c dp.c
OBJS = 3c501.o 3c509.o 3c503.o ne.o wd.o 8390.o devio.o netbuff.o dp.o
LIBS = -lutils -lsys # -ltimers
## Build rules
all build: $(DRIVER)
$(DRIVER): $(OBJS)
$(CC) $(OBJS) $(LIBS) $(LDFLAGS)
install -S 4kw $(DRIVER)
## Install with other drivers
install: /usr/sbin/drivers/$(DRIVER)
/usr/sbin/drivers/$(DRIVER): $(DRIVER)
install -o root -cs $? $@
## Generate dependencies
depend:
/usr/bin/mkdep "$(CC) -E $(CPPFLAGS)" *.c > .depend
## Clean directory
clean:
@rm -f $(DRIVER) *.o *.BAK
include .depend
## end

38
drivers/dpeth/README Normal file
View file

@ -0,0 +1,38 @@
This is my implementation of a new network task
for the Minix kernel. I did it initially to handle
a 3c501 board (Etherlink), but those board are so
unstable that it is not worth using them except for
learning how to implement a driver. When I got a
3c509b board (Etherlink III) it was easier to
write the code to handle them.
The Minix code in 'dp8390.c' is too specific for the
National chip set, so what I did was to remove as
much as I needed of the code dependant from the chip
and produce a generic task that, I hope, will be able
to handle many more cards.
$Log$
Revision 1.1 2005/06/29 10:16:46 beng
Import of dpeth 3c501/3c509b/.. ethernet driver by
Giovanni Falzoni <fgalzoni@inwind.it>.
Revision 1.3 2004/04/14 12:49:07 lsodgf0
Changes for porting to Minix 2.0.4 run on BOCHS
Revision 1.2 2002/03/25 14:16:09 lsodgf0
The driver for the NEx000 has been rewritten to be
operational with the ACCTON 18xx (an NE1000 clone)
The I/O routines for 16 bit cards are still untested..
Revision 1.1 2002/02/09 09:35:09 lsodgf0
Initial revision
The package is not fully tested, i.e. I had only 3Com
boards (3c501, 3c503, 3c503/16 and 3c509b) and WD8003.
I got also a NE1000 clone but it was not fully
operational and I could not appreciate the results.
For this reason the changes done to the interface
to I/O for 8 and 16 bits are not tested.
$Id$

141
drivers/dpeth/devio.c Normal file
View file

@ -0,0 +1,141 @@
/*
** File: devio.c Jun. 11, 2005
**
** Author: Giovanni Falzoni <gfalzoni@inwind.it>
**
** This file contains the routines for readind/writing
** from/to the device registers.
**
** $Log$
** Revision 1.1 2005/06/29 10:16:46 beng
** Import of dpeth 3c501/3c509b/.. ethernet driver by
** Giovanni Falzoni <fgalzoni@inwind.it>.
**
** Revision 2.0 2005/06/26 16:16:46 lsodgf0
** Initial revision for Minix 3.0.6
**
** $Id$
*/
#include "drivers.h"
#include <net/gen/ether.h>
#include <net/gen/eth_io.h>
#include "dp.h"
#if ENABLE_NETWORKING == 1
#if USE_IOPL == 0
static void warning(const char *type, int err)
{
printf("Warning: eth#0 sys_%s failed (%d)\n", type, err);
return;
}
/*
** Name: unsigned int inb(unsigned short int port);
** Function: Reads a byte from specified i/o port.
*/
PUBLIC unsigned int inb(unsigned short port)
{
unsigned int value;
int rc;
if ((rc = sys_inb(port, &value)) != OK) warning("inb", rc);
return value;
}
/*
** Name: unsigned int inw(unsigned short int port);
** Function: Reads a word from specified i/o port.
*/
PUBLIC unsigned int inw(unsigned short port)
{
unsigned int value;
int rc;
if ((rc = sys_inw(port, &value)) != OK) warning("inw", rc);
return value;
}
/*
** Name: unsigned int insb(unsigned short int port, int proc_nr, void *buffer, int count);
** Function: Reads a sequence of bytes from specified i/o port to user space buffer.
*/
PUBLIC void insb(unsigned short int port, int proc_nr, void *buffer, int count)
{
int rc;
if ((rc = sys_insb(port, proc_nr, buffer, count)) != OK)
warning("insb", rc);
return;
}
/*
** Name: unsigned int insw(unsigned short int port, int proc_nr, void *buffer, int count);
** Function: Reads a sequence of words from specified i/o port to user space buffer.
*/
PUBLIC void insw(unsigned short int port, int proc_nr, void *buffer, int count)
{
int rc;
if ((rc = sys_insw(port, proc_nr, buffer, count)) != OK)
warning("insw", rc);
return;
}
/*
** Name: void outb(unsigned short int port, unsigned long value);
** Function: Writes a byte to specified i/o port.
*/
PUBLIC void outb(unsigned short port, unsigned long value)
{
int rc;
if ((rc = sys_outb(port, value)) != OK) warning("outb", rc);
return;
}
/*
** Name: void outw(unsigned short int port, unsigned long value);
** Function: Writes a word to specified i/o port.
*/
PUBLIC void outw(unsigned short port, unsigned long value)
{
int rc;
if ((rc = sys_outw(port, value)) != OK) warning("outw", rc);
return;
}
/*
** Name: void outsb(unsigned short int port, int proc_nr, void *buffer, int count);
** Function: Writes a sequence of bytes from user space to specified i/o port.
*/
PUBLIC void outsb(unsigned short port, int proc_nr, void *buffer, int count)
{
int rc;
if ((rc = sys_outsb(port, proc_nr, buffer, count)) != OK)
warning("outsb", rc);
return;
}
/*
** Name: void outsw(unsigned short int port, int proc_nr, void *buffer, int count);
** Function: Writes a sequence of bytes from user space to specified i/o port.
*/
PUBLIC void outsw(unsigned short port, int proc_nr, void *buffer, int count)
{
int rc;
if ((rc = sys_outsw(port, proc_nr, buffer, count)) != OK)
warning("outsw", rc);
return;
}
#else
#error To be implemented
#endif /* USE_IOPL */
#endif /* ENABLE_NETWORKING */
/** devio.c **/

641
drivers/dpeth/dp.c Normal file
View file

@ -0,0 +1,641 @@
/*
** File: eth.c Version 1.00, Jan. 14, 1997
**
** Author: Giovanni Falzoni <gfalzoni@inwind.it>
**
** This file contains the ethernet device driver main task.
** It has to be integrated with the board specific drivers.
** It is a rewriting of Minix 2.0.0 ethernet driver (dp8390.c)
** to remove bord specific code. It should operate (I hope)
** with any board driver.
**
** The valid messages and their parameters are:
**
** m_type DL_PORT DL_PROC DL_COUNT DL_MODE DL_ADDR
** +------------+---------+---------+--------+-------+---------+
** | HARD_INT | | | | | | NOTIFICATION|0
** +------------+---------+---------+--------+-------+---------+
** | SYN_ALARM | | | | | | NOTIFICATION|1
** +------------+---------+---------+--------+-------+---------+
** | HARD_STOP | | | | | | NOTIFICATION|4
** +------------+---------+---------+--------+-------+---------+
** | FKEY_PRESSED | | | | | (99)
** +------------+---------+---------+--------+-------+---------+
** | DL_WRITE | port nr | proc nr | count | mode | address | (3)
** +------------+---------+---------+--------+-------+---------+
** | DL_WRITEV | port nr | proc nr | count | mode | address | (4)
** +------------+---------+---------+--------+-------+---------+
** | DL_READ | port nr | proc nr | count | | address | (5)
** +------------+---------+---------+--------+-------+---------+
** | DL_READV | port nr | proc nr | count | | address | (6)
** +------------+---------+---------+--------+-------+---------+
** | DL_INIT | port nr | proc nr | | mode | address | (7)
** +------------+---------+---------+--------+-------+---------+
** | DL_STOP | port_nr | | | | | (8)
** +------------+---------+---------+--------+-------+---------+
** | DL_GETSTAT | port nr | proc nr | | | address | (9)
** +------------+---------+---------+--------+-------+---------+
**
** The messages sent are:
**
** m-type DL_PORT DL_PROC DL_COUNT DL_STAT DL_CLCK
** +------------+---------+---------+--------+---------+---------+
** |DL_TASK_REPL| port nr | proc nr |rd-count| err|stat| clock | (21)
** +------------+---------+---------+--------+---------+---------+
**
** m_type m3_i1 m3_i2 m3_ca1
** +------------+---------+---------+---------------+
** |DL_INIT_REPL| port nr |last port| ethernet addr | (20)
** +------------+---------+---------+---------------+
**
** $Log$
** Revision 1.1 2005/06/29 10:16:46 beng
** Import of dpeth 3c501/3c509b/.. ethernet driver by
** Giovanni Falzoni <fgalzoni@inwind.it>.
**
** Revision 2.0 2005/06/26 16:16:46 lsodgf0
** Initial revision for Minix 3.0.6
**
** $Id$
*/
#include "drivers.h"
#include <minix/keymap.h>
#include <net/hton.h>
#include <net/gen/ether.h>
#include <net/gen/eth_io.h>
#include "dp.h"
#if ENABLE_NETWORKING == 1
/*
** Local data
*/
static dpeth_t de_table[DE_PORT_NR];
static int dpeth_tasknr = ANY;
typedef struct dp_conf { /* Configuration description structure */
port_t dpc_port;
int dpc_irq;
phys_bytes dpc_mem;
char *dpc_envvar;
} dp_conf_t;
/* Device default configuration */
static dp_conf_t dp_conf[DE_PORT_NR] = {
/* I/O port, IRQ, Buff addr, Env. var, Buf. selector */
{ 0x300, 5, 0xC8000, "DPETH0", },
{ 0x280, 10, 0xCC000, "DPETH1", },
};
static const char CopyErrMsg[] = "unable to read/write user data";
static const char PortErrMsg[] = "illegal port";
static const char RecvErrMsg[] = "receive failed";
static const char SendErrMsg[] = "send failed";
static const char SizeErrMsg[] = "illegal packet size";
static const char TypeErrMsg[] = "illegal message type";
static const char DevName[] = "eth#?";
/*
** Name: void reply(dpeth_t *dep, int err)
** Function: Fills a DL_TASK_REPLY reply message and sends it.
*/
static void reply(dpeth_t * dep, int err)
{
message reply;
int status = FALSE;
if (dep->de_flags & DEF_ACK_SEND) status |= DL_PACK_SEND;
if (dep->de_flags & DEF_ACK_RECV) status |= DL_PACK_RECV;
reply.m_type = DL_TASK_REPLY;
reply.DL_PORT = dep - de_table;
reply.DL_PROC = dep->de_client;
reply.DL_STAT = status | ((u32_t) err << 16);
reply.DL_COUNT = dep->de_read_s;
getuptime(&reply.DL_CLCK);
DEBUG(printf("\t reply %d (%ld)\n", reply.m_type, reply.DL_STAT));
if ((status = send(dep->de_client, &reply)) != OK)
panic(dep->de_name, SendErrMsg, dep->de_client);
dep->de_read_s = 0;
dep->de_flags &= NOT(DEF_ACK_SEND | DEF_ACK_RECV);
return;
}
/*
** Name: void dp_confaddr(dpeth_t *dep)
** Function: Chechs environment for a User defined ethernet address.
*/
static void dp_confaddr(dpeth_t * dep)
{
static char ea_fmt[] = "x:x:x:x:x:x";
char ea_key[16];
int ix;
long val;
strcpy(ea_key, dp_conf[dep - de_table].dpc_envvar);
strcat(ea_key, "_EA");
for (ix = 0; ix < SA_ADDR_LEN; ix++) {
val = dep->de_address.ea_addr[ix];
if (env_parse(ea_key, ea_fmt, ix, &val, 0x00L, 0xFFL) != EP_SET)
break;
dep->de_address.ea_addr[ix] = val;
}
if (ix != 0 && ix != SA_ADDR_LEN)
/* It's all or nothing, force a panic */
env_parse(ea_key, "?", 0, &val, 0L, 0L);
return;
}
/*
** Name: void update_conf(dpeth_t *dep, dp_conf_t *dcp)
** Function: Gets the default settings from 'dp_conf' table and
** modifies them from the environment.
*/
static void update_conf(dpeth_t * dep, dp_conf_t * dcp)
{
static char dpc_fmt[] = "x:d:x";
long val;
dep->de_mode = DEM_SINK;
val = dcp->dpc_port; /* Get I/O port address */
switch (env_parse(dcp->dpc_envvar, dpc_fmt, 0, &val, 0x000L, 0x3FFL)) {
case EP_OFF: dep->de_mode = DEM_DISABLED; break;
case EP_ON:
case EP_SET: dep->de_mode = DEM_ENABLED; break;
}
dep->de_base_port = val;
val = dcp->dpc_irq | DEI_DEFAULT; /* Get Interrupt line (IRQ) */
env_parse(dcp->dpc_envvar, dpc_fmt, 1, &val, 0L, (long) NR_IRQ_VECTORS - 1);
dep->de_irq = val;
val = dcp->dpc_mem; /* Get shared memory address */
env_parse(dcp->dpc_envvar, dpc_fmt, 2, &val, 0L, LONG_MAX);
dep->de_linmem = val;
return;
}
/*
** Name: void do_dump(message *mp)
** Function: Displays statistics on screen (SFx key from console)
*/
static void do_dump(message *mp)
{
dpeth_t *dep;
int port;
printf("\n\n");
for (port = 0, dep = de_table; port < DE_PORT_NR; port += 1, dep += 1) {
if (dep->de_mode == DEM_DISABLED) continue;
printf("%s statistics:\t\t", dep->de_name);
/* Network interface status */
printf("Status: 0x%04x\n\n", dep->de_flags);
(*dep->de_dumpstatsf) (dep);
/* Transmitted/received bytes */
printf("Tx bytes:%10ld\t", dep->bytes_Tx);
printf("Rx bytes:%10ld\n", dep->bytes_Rx);
/* Transmitted/received packets */
printf("Tx OK: %8ld\t", dep->de_stat.ets_packetT);
printf("Rx OK: %8ld\n", dep->de_stat.ets_packetR);
/* Transmit/receive errors */
printf("Tx Err: %8ld\t", dep->de_stat.ets_sendErr);
printf("Rx Err: %8ld\n", dep->de_stat.ets_recvErr);
/* Transmit unnerruns/receive overrruns */
printf("Tx Und: %8ld\t", dep->de_stat.ets_fifoUnder);
printf("Rx Ovr: %8ld\n", dep->de_stat.ets_fifoOver);
/* Transmit collisions/receive CRC errors */
printf("Tx Coll: %8ld\t", dep->de_stat.ets_collision);
printf("Rx CRC: %8ld\n", dep->de_stat.ets_CRCerr);
}
return;
}
/*
** Name: void get_userdata(int user_proc, vir_bytes user_addr, int count, void *loc_addr)
** Function: Copies data from user area.
*/
static void get_userdata(int user_proc, vir_bytes user_addr, int count, void *loc_addr)
{
int rc;
vir_bytes len;
len = (count > IOVEC_NR ? IOVEC_NR : count) * sizeof(iovec_t);
if ((rc = sys_datacopy(user_proc, user_addr, SELF, (vir_bytes)loc_addr, len)) != OK)
panic(DevName, CopyErrMsg, rc);
return;
}
/*
** Name: void do_first_init(dpeth_t *dep, dp_conf_t *dcp);
** Function: Init action to setup task
*/
static void do_first_init(dpeth_t *dep, dp_conf_t *dcp)
{
if (dep->de_linmem != 0) {
dep->de_memsegm = BIOS_SEG;
/* phys2seg(&dep->de_memsegm, &dep->de_memoffs, dep->de_linmem); */
} else
dep->de_linmem = 0xFFFF0000;
/* Make sure statisics are cleared */
memset((void *) &(dep->de_stat), 0, sizeof(eth_stat_t));
/* Device specific initialization */
(*dep->de_initf) (dep);
/* Set the interrupt handler policy */
sys_irqsetpolicy(dep->de_irq, IRQ_REENABLE, &dep->de_hook);
sys_irqenable(&dep->de_hook);
return;
}
/*
** Name: void do_init(message *mp)
** Function: Checks for hardware presence.
** Provides initialization of hardware and data structures
*/
static void do_init(message * mp)
{
int port;
dpeth_t *dep;
dp_conf_t *dcp;
message reply_mess;
port = mp->DL_PORT;
if (port >= 0 && port < DE_PORT_NR) {
dep = &de_table[port];
dcp = &dp_conf[port];
strcpy(dep->de_name, DevName);
dep->de_name[4] = '0' + port;
if (dep->de_mode == DEM_DISABLED) {
update_conf(dep, dcp); /* First time thru */
if (dep->de_mode == DEM_ENABLED &&
!el1_probe(dep) && /* Probe for 3c501 */
!wdeth_probe(dep) && /* Probe for WD80x3 */
!ne_probe(dep) && /* Probe for NEx000 */
!el2_probe(dep) && /* Probe for 3c503 */
!el3_probe(dep)) { /* Probe for 3c509 */
printf("%s: warning no ethernet card found at 0x%04X\n",
dep->de_name, dep->de_base_port);
dep->de_mode = DEM_DISABLED;
}
}
/* 'de_mode' may change if probe routines fail, test again */
switch (dep->de_mode) {
case DEM_DISABLED:
/* Device is configured OFF or hardware probe failed */
port = ENXIO;
break;
case DEM_ENABLED:
/* Device is present and probed */
if (dep->de_flags == DEF_EMPTY) {
/* These actions only the first time */
do_first_init(dep, dcp);
dep->de_flags |= DEF_ENABLED;
}
dep->de_flags &= NOT(DEF_PROMISC | DEF_MULTI | DEF_BROAD);
if (mp->DL_MODE & DL_PROMISC_REQ)
dep->de_flags |= DEF_PROMISC | DEF_MULTI | DEF_BROAD;
if (mp->DL_MODE & DL_MULTI_REQ) dep->de_flags |= DEF_MULTI;
if (mp->DL_MODE & DL_BROAD_REQ) dep->de_flags |= DEF_BROAD;
(*dep->de_flagsf) (dep);
dep->de_client = mp->m_source;
break;
case DEM_SINK:
/* Device not present (sink mode) */
memset(dep->de_address.ea_addr, 0, sizeof(ether_addr_t));
dp_confaddr(dep); /* Station address from env. */
break;
default: break;
}
*(ether_addr_t *) reply_mess.m3_ca1 = dep->de_address;
} else /* Port number is out of range */
port = ENXIO;
reply_mess.m_type = DL_INIT_REPLY;
reply_mess.m3_i1 = port;
reply_mess.m3_i2 = DE_PORT_NR;
DEBUG(printf("\t reply %d\n", reply_mess.m_type));
if (send(mp->m_source, &reply_mess) != OK) /* Can't send */
panic(dep->de_name, SendErrMsg, mp->m_source);
return;
}
/*
** Name: void dp_next_iovec(iovec_dat_t *iovp)
** Function: Retrieves data from next iovec element.
*/
PUBLIC void dp_next_iovec(iovec_dat_t * iovp)
{
iovp->iod_iovec_s -= IOVEC_NR;
iovp->iod_iovec_addr += IOVEC_NR * sizeof(iovec_t);
get_userdata(iovp->iod_proc_nr, iovp->iod_iovec_addr,
iovp->iod_iovec_s, iovp->iod_iovec);
return;
}
/*
** Name: int calc_iovec_size(iovec_dat_t *iovp)
** Function: Compute the size of a request.
*/
static int calc_iovec_size(iovec_dat_t * iovp)
{
int size, ix;
size = ix = 0;
do {
size += iovp->iod_iovec[ix].iov_size;
if (++ix >= IOVEC_NR) {
dp_next_iovec(iovp);
ix = 0;
}
/* Till all vectors added */
} while (ix < iovp->iod_iovec_s);
return size;
}
/*
** Name: void do_vwrite(message *mp, int vectored)
** Function:
*/
static void do_vwrite(message * mp, int vectored)
{
int port, size;
dpeth_t *dep;
port = mp->DL_PORT;
if (port < 0 || port >= DE_PORT_NR) /* Check for illegal port number */
panic(dep->de_name, PortErrMsg, port);
dep = &de_table[port];
dep->de_client = mp->DL_PROC;
if (dep->de_mode == DEM_ENABLED) {
if (dep->de_flags & DEF_SENDING) /* Is sending in progress? */
panic(dep->de_name, "send already in progress ", NO_NUM);
dep->de_write_iovec.iod_proc_nr = mp->DL_PROC;
if (vectored) {
get_userdata(mp->DL_PROC, (vir_bytes) mp->DL_ADDR,
mp->DL_COUNT, dep->de_write_iovec.iod_iovec);
dep->de_write_iovec.iod_iovec_s = mp->DL_COUNT;
dep->de_write_iovec.iod_iovec_addr = (vir_bytes) mp->DL_ADDR;
size = calc_iovec_size(&dep->de_write_iovec);
} else {
dep->de_write_iovec.iod_iovec[0].iov_addr = (vir_bytes) mp->DL_ADDR;
dep->de_write_iovec.iod_iovec[0].iov_size = size = mp->DL_COUNT;
dep->de_write_iovec.iod_iovec_s = 1;
dep->de_write_iovec.iod_iovec_addr = 0;
}
if (size < ETH_MIN_PACK_SIZE || size > ETH_MAX_PACK_SIZE)
panic(dep->de_name, SizeErrMsg, size);
dep->de_flags |= DEF_SENDING;
(*dep->de_sendf) (dep, FALSE, size);
} else if (dep->de_mode == DEM_SINK)
dep->de_flags |= DEF_ACK_SEND;
reply(dep, OK);
return;
}
/*
** Name: void do_vread(message *mp, int vectored)
** Function:
*/
static void do_vread(message * mp, int vectored)
{
int port, size;
dpeth_t *dep;
port = mp->DL_PORT;
if (port < 0 || port >= DE_PORT_NR) /* Check for illegal port number */
panic(dep->de_name, PortErrMsg, port);
dep = &de_table[port];
dep->de_client = mp->DL_PROC;
if (dep->de_mode == DEM_ENABLED) {
if (dep->de_flags & DEF_READING) /* Reading in progress */
panic(dep->de_name, "read already in progress", NO_NUM);
dep->de_read_iovec.iod_proc_nr = mp->DL_PROC;
if (vectored) {
get_userdata(mp->DL_PROC, (vir_bytes) mp->DL_ADDR,
mp->DL_COUNT, dep->de_read_iovec.iod_iovec);
dep->de_read_iovec.iod_iovec_s = mp->DL_COUNT;
dep->de_read_iovec.iod_iovec_addr = (vir_bytes) mp->DL_ADDR;
size = calc_iovec_size(&dep->de_read_iovec);
} else {
dep->de_read_iovec.iod_iovec[0].iov_addr = (vir_bytes) mp->DL_ADDR;
dep->de_read_iovec.iod_iovec[0].iov_size = size = mp->DL_COUNT;
dep->de_read_iovec.iod_iovec_s = 1;
dep->de_read_iovec.iod_iovec_addr = 0;
}
if (size < ETH_MAX_PACK_SIZE) panic(dep->de_name, SizeErrMsg, size);
dep->de_flags |= DEF_READING;
(*dep->de_recvf) (dep, FALSE, size);
#if 0
if ((dep->de_flags & (DEF_READING | DEF_STOPPED)) == (DEF_READING | DEF_STOPPED))
/* The chip is stopped, and all arrived packets delivered */
(*dep->de_resetf) (dep);
dep->de_flags &= NOT(DEF_STOPPED);
#endif
}
reply(dep, OK);
return;
}
/*
** Name: void do_getstat(message *mp)
** Function: Reports device statistics.
*/
static void do_getstat(message * mp)
{
int port, rc;
dpeth_t *dep;
port = mp->DL_PORT;
if (port < 0 || port >= DE_PORT_NR) /* Check for illegal port number */
panic(dep->de_name, PortErrMsg, port);
dep = &de_table[port];
dep->de_client = mp->DL_PROC;
if (dep->de_mode == DEM_ENABLED) (*dep->de_getstatsf) (dep);
if ((rc = sys_datacopy(SELF, (vir_bytes)&dep->de_stat,
mp->DL_PROC, (vir_bytes)mp->DL_ADDR,
(vir_bytes) sizeof(dep->de_stat))) != OK)
panic(DevName, CopyErrMsg, rc);
reply(dep, OK);
return;
}
/*
** Name: void do_stop(message *mp)
** Function: Stops network interface.
*/
static void do_stop(message * mp)
{
int port;
dpeth_t *dep;
port = mp->DL_PORT;
if (port < 0 || port >= DE_PORT_NR) /* Check for illegal port number */
panic(dep->de_name, PortErrMsg, port);
dep = &de_table[port];
if (dep->de_mode == DEM_ENABLED && (dep->de_flags & DEF_ENABLED)) {
/* Stop device */
(dep->de_stopf) (dep);
dep->de_flags = DEF_EMPTY;
dep->de_mode = DEM_DISABLED;
}
return;
}
static void do_watchdog(void *message)
{
DEBUG(printf("\t no reply"));
return;
}
/*
** Name: int dpeth_task(void)
** Function: Main entry for dp task
*/
PUBLIC int main(void)
{
message m;
dpeth_t *dep;
int rc, fkeys, sfkeys;
/* Get precess number */
if ((rc = getprocnr(&dpeth_tasknr)) != OK)
panic(DevName, "getprocnr() failed", rc);
#if defined USE_IOPL
/* Request direct access to hardware I/O ports */
if ((rc = sys_enable_iop(dpeth_tasknr)) != OK)
panic(DevName, "sys_enable_iop() failed", rc);
#endif
/* Request function key for debug dumps */
fkeys = sfkeys = 0; bit_set(sfkeys, 8);
if ((fkey_map(fkeys, sfkeys)) != OK)
printf("%s: couldn't program Shift+F8 key (%d)\n", DevName, errno);
#ifdef ETH_IGN_PROTO
{
static u16_t eth_ign_proto = 0;
long val;
val = 0xFFFF;
env_parse("ETH_IGN_PROTO", "x", 0, &val, 0x0000L, 0xFFFFL);
eth_ign_proto = htons((u16_t) val);
}
#endif
printf("DPETH: ethernet driver task initialized (process No. %d)\n", dpeth_tasknr);
while (TRUE) {
if ((rc = receive(ANY, &m)) != OK) panic(dep->de_name, RecvErrMsg, rc);
DEBUG(printf("eth: got message %d, ", m.m_type));
switch (m.m_type) {
case DL_WRITE: /* Write message to device */
do_vwrite(&m, FALSE);
break;
case DL_WRITEV: /* Write message to device */
do_vwrite(&m, TRUE);
break;
case DL_READ: /* Read message from device */
do_vread(&m, FALSE);
break;
case DL_READV: /* Read message from device */
do_vread(&m, TRUE);
break;
case DL_INIT: /* Initialize device */
do_init(&m);
break;
case DL_GETSTAT: /* Get device statistics */
do_getstat(&m);
break;
case SYN_ALARM: /* to be defined */
do_watchdog(&m);
break;
case DL_STOP: /* Stop device */
do_stop(&m);
break;
case HARD_STOP: /* Shut down */
for (rc = 0; rc < DE_PORT_NR; rc += 1) {
if (de_table[rc].de_mode == DEM_ENABLED) {
m.m_type = DL_STOP;
m.DL_PORT = rc;
do_stop(&m);
}
}
sys_exit(0);
break;
case HARD_INT: /* Interrupt from device */
for (dep = de_table; dep < &de_table[DE_PORT_NR]; dep += 1) {
/* If device is enabled and interrupt pending */
if (dep->de_mode == DEM_ENABLED) {
/* dep->de_int_pending = FALSE; */
(*dep->de_interruptf) (dep);
if (dep->de_flags & (DEF_ACK_SEND | DEF_ACK_RECV))
reply(dep, OK);
/* enable_irq(&dep->de_hook); */
}
}
break;
case FKEY_PRESSED: /* Function key pressed */
do_dump(&m);
break;
default: /* Invalid message type */
panic(DevName, TypeErrMsg, m.m_type);
break;
}
}
return OK; /* Never reached, but keeps compiler happy */
}
#else
int main(void) { return 0; }
#endif /* ENABLE_NETWORKING */
/** dp.c **/

269
drivers/dpeth/dp.h Normal file
View file

@ -0,0 +1,269 @@
/*
** File: eth.h Version 1.00, Jan. 14, 1997
**
** Author: Giovanni Falzoni <gfalzoni@inwind.it>
**
** Interface description for ethernet device driver
**
** $Log$
** Revision 1.1 2005/06/29 10:16:46 beng
** Import of dpeth 3c501/3c509b/.. ethernet driver by
** Giovanni Falzoni <fgalzoni@inwind.it>.
**
** Revision 2.0 2005/06/26 16:16:46 lsodgf0
** Initial revision for Minix 3.0.6
**
** $Id$
*/
#undef ENABLE_3C501
#undef ENABLE_3C503
#undef ENABLE_3C509
#undef ENABLE_NE2000
#undef ENABLE_WDETH
#undef ENABLE_DP8390
#define ENABLE_NETWORKING ENABLE_DPETH /** (from /usr/include/minix/config.h **/
#define ENABLE_3C501 1 /* enable 3Com Etherlink I board */
#define ENABLE_3C503 1 /* enable 3Com Etherlink II board */
#define ENABLE_3C509 1 /* enable 3Com Etherlink III board */
#define ENABLE_NE2000 1 /* enable Novell N2000 board */
#define ENABLE_WDETH 1 /* enable Western Digital WD80x3 */
#define ENABLE_DP8390 (ENABLE_3C503|ENABLE_WDETH|ENABLE_NE2000)
#define HAVE_BUFFERS (ENABLE_3C501|ENABLE_3C509)
#undef NULL
#define NULL ((void *)0)
#define NOT(x) (~(x))
#if debug == 1
# define DEBUG(statm) statm
#else
# define DEBUG(statm)
#endif
typedef struct _m_hdr_t { /* Buffer handling header */
struct _m_hdr_t *next;
int size;
} m_hdr_t;
typedef struct _buff_t { /* Receive/Transmit buffer header */
struct _buff_t *next;
int size;
int client;
char buffer[2];
} buff_t;
struct dpeth;
struct iovec_dat;
typedef void (*dp_eth_t) (struct dpeth *);
typedef void (*dp_send_recv_t) (struct dpeth *, int, int);
#if ENABLE_DP8390 == 1
typedef void (*dp_user2nicf_t) (struct dpeth *, int, int);
typedef void (*dp_nic2userf_t) (struct dpeth *, int, int);
typedef void (*dp_getblock_t) (struct dpeth *, u16_t, int, void *);
#endif
#define DE_PORT_NR 2 /* Number of devices supported */
#define SENDQ_NR 2 /* Size of the send queue */
#define IOVEC_NR 16 /* Number of IOVEC entries at a time */
typedef struct iovec_dat {
iovec_t iod_iovec[IOVEC_NR];
int iod_iovec_s;
int iod_proc_nr;
vir_bytes iod_iovec_addr;
} iovec_dat_t;
typedef struct dpeth {
/* The de_base_port field is the starting point of the probe. The
* conf routine also fills de_linmem and de_irq. If the probe routine
* knows the irq and/or memory address because they are hardwired in
* the board, the probe should modify these fields. Futhermore, the
* probe routine should also fill in de_initf and de_stopf fields
* with the appropriate function pointers and set de_prog_IO iff
* programmed I/O is to be used.
*
* The initf function fills the following fields. Only cards that do
* programmed I/O fill in the de_data_port field. In addition, the
* init routine has to fill in the sendq data structures. */
/* Board hardware interface */
port_t de_base_port;
port_t de_data_port; /* For boards using Prog. I/O for xmit/recv */
int de_irq;
/* int de_int_pending; */
int de_hook; /* V306 irq_hook_t de_hook; */
char de_name[8];
#define DEI_DEFAULT 0x8000
phys_bytes de_linmem; /* For boards using shared memory */
unsigned short de_memsegm;
vir_bytes de_memoffs;
int de_ramsize; /* Size of on board memory */
int de_offset_page; /* Offset of shared memory page */
/* Board specific functions */
dp_eth_t de_initf;
dp_eth_t de_stopf;
dp_eth_t de_resetf;
dp_eth_t de_flagsf;
dp_eth_t de_getstatsf;
dp_eth_t de_dumpstatsf;
dp_eth_t de_interruptf;
dp_send_recv_t de_recvf;
dp_send_recv_t de_sendf;
ether_addr_t de_address; /* Ethernet Address */
eth_stat_t de_stat; /* Ethernet Statistics */
unsigned long bytes_Tx; /* Total bytes sent/received */
unsigned long bytes_Rx;
#define SA_ADDR_LEN sizeof(ether_addr_t)
int de_flags; /* Send/Receive mode (Configuration) */
#define DEF_EMPTY 0x0000
#define DEF_READING 0x0001
#define DEF_RECV_BUSY 0x0002
#define DEF_ACK_RECV 0x0004
#define DEF_SENDING 0x0010
#define DEF_XMIT_BUSY 0x0020
#define DEF_ACK_SEND 0x0040
#define DEF_PROMISC 0x0100
#define DEF_MULTI 0x0200
#define DEF_BROAD 0x0400
#define DEF_ENABLED 0x2000
#define DEF_STOPPED 0x4000
int de_mode; /* Status of the Interface */
#define DEM_DISABLED 0x0000
#define DEM_SINK 0x0001
#define DEM_ENABLED 0x0002
/* Temporary storage for RECV/SEND requests */
iovec_dat_t de_read_iovec;
iovec_dat_t de_write_iovec;
vir_bytes de_read_s;
vir_bytes de_send_s;
int de_client;
/*
message de_sendmsg;
iovec_dat_t de_tmp_iovec;
*/
#if ENABLE_DP8390 == 1
/* For use by NS DP8390 driver */
port_t de_dp8390_port;
int de_prog_IO;
int de_16bit;
int de_startpage;
int de_stoppage;
/* Do it yourself send queue */
struct sendq {
int sq_filled; /* This buffer contains a packet */
int sq_size; /* with this size */
int sq_sendpage; /* starting page of the buffer */
} de_sendq[SENDQ_NR];
int de_sendq_nr;
int de_sendq_head; /* Enqueue at the head */
int de_sendq_tail; /* Dequeue at the tail */
dp_user2nicf_t de_user2nicf;
dp_nic2userf_t de_nic2userf;
dp_getblock_t de_getblockf;
#endif
#if ENABLE_3C509 == 1
/* For use by 3Com Etherlink III (3c509) driver */
port_t de_id_port;
port_t de_if_port;
#endif
#if ENABLE_3C501 == 1 || ENABLE_3C509 == 1
/* For use by 3Com Etherlink (3c501 and 3c509) driver */
buff_t *de_recvq_head;
buff_t *de_recvq_tail;
buff_t *de_xmitq_head;
buff_t *de_xmitq_tail;
u16_t de_recv_mode;
clock_t de_xmit_start;
#endif
} dpeth_t;
/*
* Function definitions
*/
/* dp.c */
void dp_next_iovec(iovec_dat_t * iovp);
/* devio.c */
#if defined USE_IOPL
#include <ibm/portio.h>
#else
unsigned int inb(unsigned short int);
unsigned int inw(unsigned short int);
void insb(unsigned short int, int, void *, int);
void insw(unsigned short int, int, void *, int);
void outb(unsigned short int, unsigned long);
void outw(unsigned short int, unsigned long);
void outsb(unsigned short int, int, void *, int);
void outsw(unsigned short int, int, void *, int);
#endif
/* netbuff.c */
void *alloc_buff(dpeth_t *, int);
void free_buff(dpeth_t *, void *);
void init_buff(dpeth_t *, buff_t **);
void mem2user(dpeth_t *, buff_t *);
void user2mem(dpeth_t *, buff_t *);
/* 3c501.c */
#if ENABLE_3C501 == 1
int el1_probe(dpeth_t *);
#else
#define el1_probe(x) (0)
#endif
/* 3c503.c */
#if ENABLE_3C503 == 1
int el2_probe(dpeth_t *);
#else
#define el2_probe(x) (0)
#endif
/* 3c509.c */
#if ENABLE_3C509 == 1
int el3_probe(dpeth_t *);
#else
#define el3_probe(x) (0)
#endif
/* ne.c */
#if ENABLE_NE2000 == 1
int ne_probe(dpeth_t * dep);
#else
#define ne_probe(x) (0)
#endif
/* wd.c */
#if ENABLE_WDETH == 1
int wdeth_probe(dpeth_t * dep);
#else
#define wdeth_probe(x) (0)
#endif
#define lock() sys_irqdisable(&dep->de_hook);
#define unlock() sys_irqenable(&dep->de_hook);
#define milli_delay(t) tickdelay(1)
/** dp.h **/

201
drivers/dpeth/ne.c Normal file
View file

@ -0,0 +1,201 @@
/*
** File: ne.c Jun. 08, 2000
**
** Driver for the NE*000 ethernet cards and derivates.
** This file contains only the ne specific code,
** the rest is in 8390.c Code specific for ISA bus only
**
** Created: March 15, 1994 by Philip Homburg <philip@cs.vu.nl>
** PchId: ne2000.c,v 1.4 1996/01/19 23:30:34 philip Exp
**
** Modified: Jun. 08, 2000 by Giovanni Falzoni <gfalzoni@inwind.it>
** Adapted to interface new main network task.
**
** $Log$
** Revision 1.1 2005/06/29 10:16:46 beng
** Import of dpeth 3c501/3c509b/.. ethernet driver by
** Giovanni Falzoni <fgalzoni@inwind.it>.
**
** Revision 2.0 2005/06/26 16:16:46 lsodgf0
** Initial revision for Minix 3.0.6
**
** $Id$
*/
#include "drivers.h"
#include <net/gen/ether.h>
#include <net/gen/eth_io.h>
#include "dp.h"
#if (ENABLE_NETWORKING == 1 && ENABLE_NE2000 == 1)
#include "8390.h"
#include "ne.h"
/*
** Name: void ne_reset(dpeth_t * dep);
** Function: Resets the board and checks if reset cycle completes
*/
static int ne_reset(dpeth_t * dep)
{
int count = 0;
/* Reset the ethernet card */
outb_ne(dep, NE_RESET, inb_ne(dep, NE_RESET));
do {
if (++count > 10) return FALSE; /* 20 mSecs. timeout */
milli_delay(2);
} while ((inb_ne(dep, DP_ISR) & ISR_RST) == 0);
return TRUE;
}
/*
** Name: void ne_close(dpeth_t * dep);
** Function: Stops the board by resetting it and masking interrupts.
*/
static void ne_close(dpeth_t * dep)
{
(void)ne_reset(dep);
outb_ne(dep, DP_ISR, 0xFF);
sys_irqdisable(&dep->de_hook);
return;
}
/*
** Name: void ne_init(dpeth_t * dep);
** Function: Initialize the board making it ready to work.
*/
static void ne_init(dpeth_t * dep)
{
int ix;
dep->de_data_port = dep->de_base_port + NE_DATA;
if (dep->de_16bit) {
dep->de_ramsize = NE2000_SIZE;
dep->de_offset_page = NE2000_START / DP_PAGESIZE;
} else {
dep->de_ramsize = NE1000_SIZE;
dep->de_offset_page = NE1000_START / DP_PAGESIZE;
}
/* Allocates two send buffers from onboard RAM */
dep->de_sendq_nr = SENDQ_NR;
for (ix = 0; ix < SENDQ_NR; ix += 1) {
dep->de_sendq[ix].sq_sendpage = dep->de_offset_page + ix * SENDQ_PAGES;
}
/* Remaining onboard RAM allocated for receiving */
dep->de_startpage = dep->de_offset_page + ix * SENDQ_PAGES;
dep->de_stoppage = dep->de_offset_page + dep->de_ramsize / DP_PAGESIZE;
/* Can't override the default IRQ. */
dep->de_irq &= NOT(DEI_DEFAULT);
ns_init(dep); /* Initialize DP controller */
printf("%s: NE%d000 (%dkB RAM) at %X:%d - ",
dep->de_name,
dep->de_16bit ? 2 : 1,
dep->de_ramsize / 1024,
dep->de_base_port, dep->de_irq);
for (ix = 0; ix < SA_ADDR_LEN; ix += 1)
printf("%02X%c", dep->de_address.ea_addr[ix], ix < SA_ADDR_LEN - 1 ? ':' : '\n');
return;
}
/*
** Name: int ne_probe(dpeth_t * dep);
** Function: Probe for the presence of a NE*000 card by testing
** whether the board is reachable through the dp8390.
** Note that the NE1000 is an 8bit card and has a memory
** region distict from the 16bit NE2000.
*/
PUBLIC int ne_probe(dpeth_t * dep)
{
int ix, wd, loc1, loc2;
char EPROM[32];
static const struct {
unsigned char offset;
unsigned char value;
} InitSeq[] =
{
{ /* Selects page 0. */
DP_CR, (CR_NO_DMA | CR_PS_P0 | CR_STP),
},
{ /* Set byte-wide access and 8 bytes burst mode. */
DP_DCR, (DCR_8BYTES | DCR_BMS),
},
{ /* Clears the count registers. */
DP_RBCR0, 0x00, }, { DP_RBCR1, 0x00,
},
{ /* Mask completion irq. */
DP_IMR, 0x00, }, { DP_ISR, 0xFF,
},
{ /* Set receiver to monitor */
DP_RCR, RCR_MON,
},
{ /* and transmitter to loopback mode. */
DP_TCR, TCR_INTERNAL,
},
{ /* Transmit 32 bytes */
DP_RBCR0, 32, }, { DP_RBCR1, 0,
},
{ /* DMA starting at 0x0000. */
DP_RSAR0, 0x00, }, { DP_RSAR1, 0x00,
},
{ /* Start board (reads) */
DP_CR, (CR_PS_P0 | CR_DM_RR | CR_STA),
},
};
dep->de_dp8390_port = dep->de_base_port + NE_DP8390;
if ((loc1 = inb_ne(dep, NE_DP8390)) == 0xFF) return FALSE;
/* Check if the dp8390 is really there */
outb_ne(dep, DP_CR, CR_STP | CR_NO_DMA | CR_PS_P1);
loc2 = inb_ne(dep, DP_MAR5); /* Saves one byte of the address */
outb_ne(dep, DP_MAR5, 0xFF); /* Write 0xFF to it (same offset as DP_CNTR0) */
outb_ne(dep, DP_CR, CR_NO_DMA | CR_PS_P0); /* Back to page 0 */
inb_ne(dep, DP_CNTR0); /* Reading counter resets it */
if (inb_ne(dep, DP_CNTR0) != 0) {
outb_ne(dep, NE_DP8390, loc1); /* Try to restore modified bytes */
outb_ne(dep, DP_TCR, loc2);
return FALSE;
}
/* Try to reset the board */
if (ne_reset(dep) == FALSE) return FALSE;
/* Checks whether the board is 8/16bits and a real NE*000 or clone */
for (ix = 0; ix < sizeof(InitSeq)/sizeof(InitSeq[0]); ix += 1) {
outb_ne(dep, InitSeq[ix].offset, InitSeq[ix].value);
}
for (ix = 0, wd = 1; ix < 32; ix += 2) {
EPROM[ix + 0] = inb_ne(dep, NE_DATA);
EPROM[ix + 1] = inb_ne(dep, NE_DATA);
/* NE2000s and clones read same value for even and odd addresses */
if (EPROM[ix + 0] != EPROM[ix + 1]) wd = 0;
}
if (wd == 1) { /* Normalize EPROM contents for NE2000 */
for (ix = 0; ix < 16; ix += 1) EPROM[ix] = EPROM[ix * 2];
}
/* Real NE*000 and good clones have '0x57' at locations 14 and 15 */
if (EPROM[14] != 0x57 || EPROM[15] != 0x57) return FALSE;
/* Setup the ethernet address. */
for (ix = 0; ix < SA_ADDR_LEN; ix += 1) {
dep->de_address.ea_addr[ix] = EPROM[ix];
}
dep->de_16bit = wd;
dep->de_linmem = 0; /* Uses Programmed I/O only */
dep->de_prog_IO = 1;
dep->de_initf = ne_init;
dep->de_stopf = ne_close;
return TRUE;
}
#endif /* ENABLE_NETWORKING && ENABLE_NE2000 */
/** ne.c **/

37
drivers/dpeth/ne.h Normal file
View file

@ -0,0 +1,37 @@
/*
** File: ne.h
**
** Created: March 15, 1994 by Philip Homburg <philip@cs.vu.nl>
** $PchId: ne2000.h,v 1.2 1995/12/22 08:42:31 philip Exp $
**
** $Log$
** Revision 1.1 2005/06/29 10:16:46 beng
** Import of dpeth 3c501/3c509b/.. ethernet driver by
** Giovanni Falzoni <fgalzoni@inwind.it>.
**
** Revision 2.0 2005/06/26 16:16:46 lsodgf0
** Initial revision for Minix 3.0.6
**
** $Id$
*/
#ifndef NE2000_H
#define NE2000_H
#define NE_DP8390 0x00
#define NE_DATA 0x10
#define NE_RESET 0x1F
#define NE1000_START 0x2000
#define NE1000_SIZE 0x2000
#define NE2000_START 0x4000
#define NE2000_SIZE 0x4000
#define inb_ne(dep, reg) (inb(dep->de_base_port+reg))
#define outb_ne(dep, reg, data) (outb(dep->de_base_port+reg, data))
#define inw_ne(dep, reg) (inw(dep->de_base_port+reg))
#define outw_ne(dep, reg, data) (outw(dep->de_base_port+reg, data))
#endif /* NE2000_H */
/** ne.h **/

176
drivers/dpeth/netbuff.c Normal file
View file

@ -0,0 +1,176 @@
/*
** File: netbuff.c Jun. 10, 2000
**
** Author: Giovanni Falzoni <gfalzoni@inwind.it>
**
** This file contains specific implementation of buffering
** for network packets.
**
** $Log$
** Revision 1.1 2005/06/29 10:16:46 beng
** Import of dpeth 3c501/3c509b/.. ethernet driver by
** Giovanni Falzoni <fgalzoni@inwind.it>.
**
** Revision 2.0 2005/06/26 16:16:46 lsodgf0
** Initial revision for Minix 3.0.6
**
** $Id$
*/
#include "drivers.h"
#include <net/gen/ether.h>
#include <net/gen/eth_io.h>
#include "dp.h"
#if ENABLE_NETWORKING == 1 && HAVE_BUFFERS == 1
static m_hdr_t *allocptr = NULL;
static char tx_rx_buff[8192];
/*
** Name: void *alloc_buff(dpeth_t *dep, int size)
** Function: Allocates a buffer from the common pool.
*/
PUBLIC void *alloc_buff(dpeth_t *dep, int size)
{
m_hdr_t *ptr, *wrk = allocptr;
int units = ((size + sizeof(m_hdr_t) - 1) / sizeof(m_hdr_t)) + 1;
lock();
for (ptr = wrk->next;; wrk = ptr, ptr = ptr->next) {
if (ptr->size >= units) {
/* Memory is available, carve requested size from pool */
if (ptr->size == units) {
wrk->next = ptr->next;
} else {
/* Get memory from top address */
ptr->size -= units;
ptr += ptr->size;
ptr->size = units;
}
allocptr = wrk;
unlock();
return ptr + 1;
}
if (ptr == allocptr) break;
}
unlock();
return NULL; /* No memory available */
}
/*
** Name: void free_buff(dpeth_t *dep, void *blk)
** Function: Returns a buffer to the common pool.
*/
PUBLIC void free_buff(dpeth_t *dep, void *blk)
{
m_hdr_t *wrk, *ptr = (m_hdr_t *) blk - 1;
lock(); /* Scan linked list for the correct place */
for (wrk = allocptr; !(ptr > wrk && ptr < wrk->next); wrk = wrk->next)
if (wrk >= wrk->next && (ptr > wrk || ptr < wrk->next)) break;
/* Check if adjacent block is free and join blocks */
if (ptr + ptr->size == wrk->next) {
ptr->size += wrk->next->size;
ptr->next = wrk->next->next;
} else
ptr->next = wrk->next;
if (wrk + wrk->size == ptr) {
wrk->size += ptr->size;
wrk->next = ptr->next;
} else
wrk->next = ptr;
allocptr = wrk; /* Point allocptr to block just released */
unlock();
return;
}
/*
** Name: void init_buff(dpeth_t *dep, buff_t **tx_buff)
** Function: Initalizes driver data structures.
*/
PUBLIC void init_buff(dpeth_t *dep, buff_t **tx_buff)
{
/* Initializes buffer pool */
if (allocptr == NULL) {
m_hdr_t *rx = (m_hdr_t *) tx_rx_buff;
rx->next = allocptr = rx;
rx->size = 0;
rx += 1;
rx->next = NULL;
rx->size = (sizeof(tx_rx_buff) / sizeof(m_hdr_t)) - 1;
free_buff(dep, rx + 1);
dep->de_recvq_tail = dep->de_recvq_head = NULL;
if (tx_buff != NULL) {
*tx_buff = alloc_buff(dep, ETH_MAX_PACK_SIZE + sizeof(buff_t));
(*tx_buff)->size = 0;
}
}
return; /* Done */
}
/*
** Name: void mem2user(dpeth_t *dep, buff_t *rxbuff);
** Function: Copies a packet from local buffer to user area.
*/
PUBLIC void mem2user(dpeth_t *dep, buff_t *rxbuff)
{
phys_bytes phys_user;
int bytes, ix = 0;
iovec_dat_t *iovp = &dep->de_read_iovec;
int pktsize = rxbuff->size;
char *buffer = rxbuff->buffer;
do { /* Reads chuncks of packet into user buffers */
bytes = iovp->iod_iovec[ix].iov_size; /* Size of buffer */
if (bytes > pktsize) bytes = pktsize;
/* Reads from Rx buffer to user area */
sys_datacopy(SELF, (vir_bytes)buffer, iovp->iod_proc_nr,
iovp->iod_iovec[ix].iov_addr, bytes);
buffer += bytes;
if (++ix >= IOVEC_NR) { /* Next buffer of IO vector */
dp_next_iovec(iovp);
ix = 0;
}
/* Till packet done */
} while ((pktsize -= bytes) > 0);
return;
}
/*
** Name: void user2mem(dpeth_t *dep, buff_t *txbuff)
** Function: Copies a packet from user area to local buffer.
*/
PUBLIC void user2mem(dpeth_t *dep, buff_t *txbuff)
{
phys_bytes phys_user;
int bytes, ix = 0;
iovec_dat_t *iovp = &dep->de_write_iovec;
int pktsize = txbuff->size;
char *buffer = txbuff->buffer;
do { /* Reads chuncks of packet from user buffers */
bytes = iovp->iod_iovec[ix].iov_size; /* Size of buffer */
if (bytes > pktsize) bytes = pktsize;
sys_datacopy(iovp->iod_proc_nr, iovp->iod_iovec[ix].iov_addr,
SELF, (vir_bytes)buffer, bytes);
buffer += bytes;
if (++ix >= IOVEC_NR) { /* Next buffer of IO vector */
dp_next_iovec(iovp);
ix = 0;
}
/* Till packet done */
} while ((pktsize -= bytes) > 0);
return;
}
#endif /* ENABLE_NETWORKING */
/** netbuff.c **/

323
drivers/dpeth/wd.c Normal file
View file

@ -0,0 +1,323 @@
/*
** File: wd.c
**
** Driver for the Ethercard (WD80x3) and derivates
** This file contains only the wd80x3 specific code,
** the rest is in 8390.c
**
** Created: March 14, 1994 by Philip Homburg
** PchId:
**
** Modified: Jun. 08, 2000 by Giovanni Falzoni <gfalzoni@inwind.it>
** Adaptation to interfacing new main network task.
**
** $Log$
** Revision 1.1 2005/06/29 10:16:46 beng
** Import of dpeth 3c501/3c509b/.. ethernet driver by
** Giovanni Falzoni <fgalzoni@inwind.it>.
**
** Revision 2.0 2005/06/26 16:16:46 lsodgf0
** Initial revision for Minix 3.0.6
**
** $Id$
*/
#include "drivers.h"
#include <net/gen/ether.h>
#include <net/gen/eth_io.h>
#include "dp.h"
#if (ENABLE_NETWORKING == 1 && ENABLE_WDETH == 1)
#include "8390.h"
#include "wd.h"
#define WET_ETHERNET 0x01 /* Ethernet transceiver */
#define WET_STARLAN 0x02 /* Starlan transceiver */
#define WET_INTERF_CHIP 0x04 /* has a WD83C583 interface chip */
#define WET_BRD_16BIT 0x08 /* 16 bit board */
#define WET_SLT_16BIT 0x10 /* 16 bit slot */
#define WET_790 0x20 /* '790 chip */
static const int we_int_table[8] = {9, 3, 5, 7, 10, 11, 15, 4};
static const int we_790int_table[8] = {0, 9, 3, 5, 7, 10, 11, 15};
_PROTOTYPE(static void we_init, (dpeth_t * dep));
_PROTOTYPE(static void we_stop, (dpeth_t * dep));
_PROTOTYPE(static int we_aliasing, (dpeth_t * dep));
_PROTOTYPE(static int we_interface_chip, (dpeth_t * dep));
_PROTOTYPE(static int we_16bitboard, (dpeth_t * dep));
_PROTOTYPE(static int we_16bitslot, (dpeth_t * dep));
_PROTOTYPE(static int we_ultra, (dpeth_t * dep));
/*===========================================================================*
* wdeth_probe *
*===========================================================================*/
int wdeth_probe(dep)
dpeth_t *dep;
{
int sum;
if (dep->de_linmem == 0)
return FALSE; /* No shared memory, so no WD board */
sum = inb_we(dep, EPL_EA0) + inb_we(dep, EPL_EA1) +
inb_we(dep, EPL_EA2) + inb_we(dep, EPL_EA3) +
inb_we(dep, EPL_EA4) + inb_we(dep, EPL_EA5) +
inb_we(dep, EPL_TLB) + inb_we(dep, EPL_CHKSUM);
if ((sum & 0xFF) != 0xFF)
return FALSE; /* No ethernet board at this address */
dep->de_initf = we_init;
dep->de_stopf = we_stop;
dep->de_prog_IO = 0;
return TRUE;
}
/*===========================================================================*
* we_init *
*===========================================================================*/
static void we_init(dep)
dpeth_t *dep;
{
int i, int_indx, int_nr;
int tlb, rambit, revision;
int icr, irr, hwr, b, gcr;
int we_type;
int sendq_nr;
for (i = 0; i < 6; i += 1) {
dep->de_address.ea_addr[i] = inb_we(dep, EPL_EA0 + i);
}
dep->de_dp8390_port = dep->de_base_port + EPL_DP8390;
dep->de_16bit = 0;
we_type = 0;
we_type |= WET_ETHERNET; /* assume ethernet */
if (we_ultra(dep)) we_type |= WET_790;
if (!we_aliasing(dep)) {
if (we_interface_chip(dep)) we_type |= WET_INTERF_CHIP;
if (we_16bitboard(dep)) {
we_type |= WET_BRD_16BIT;
if (we_16bitslot(dep)) we_type |= WET_SLT_16BIT;
}
}
if (we_type & WET_SLT_16BIT) dep->de_16bit = 1;
/* Look at the on board ram size. */
tlb = inb_we(dep, EPL_TLB);
revision = tlb & E_TLB_REV;
rambit = tlb & E_TLB_RAM;
if (dep->de_ramsize != 0) {
/* Size set from boot environment. */
} else if (revision < 2) {
dep->de_ramsize = 0x2000; /* 8K */
if (we_type & WET_BRD_16BIT)
dep->de_ramsize = 0x4000; /* 16K */
else if ((we_type & WET_INTERF_CHIP) &&
inb_we(dep, EPL_ICR) & E_ICR_MEMBIT) {
dep->de_ramsize = 0x8000; /* 32K */
}
} else {
if (we_type & WET_BRD_16BIT) {
/* 32K or 16K */
dep->de_ramsize = rambit ? 0x8000 : 0x4000;
} else {
/* 32K or 8K */
dep->de_ramsize = rambit ? 0x8000 : 0x2000;
}
}
if (we_type & WET_790) {
outb_we(dep, EPL_MSR, E_MSR_RESET);
if ((we_type & (WET_BRD_16BIT | WET_SLT_16BIT)) ==
(WET_BRD_16BIT | WET_SLT_16BIT)) {
outb_we(dep, EPL_LAAR, E_LAAR_LAN16E | E_LAAR_MEM16E);
}
} else if (we_type & WET_BRD_16BIT) {
if (we_type & WET_SLT_16BIT) {
outb_we(dep, EPL_LAAR, E_LAAR_A19 | E_LAAR_SOFTINT |
E_LAAR_LAN16E | E_LAAR_MEM16E);
} else {
outb_we(dep, EPL_LAAR, E_LAAR_A19 | E_LAAR_SOFTINT |
E_LAAR_LAN16E);
}
}
if (we_type & WET_790) {
outb_we(dep, EPL_MSR, E_MSR_MENABLE);
hwr = inb_we(dep, EPL_790_HWR);
outb_we(dep, EPL_790_HWR, hwr | E_790_HWR_SWH);
b = inb_we(dep, EPL_790_B);
outb_we(dep, EPL_790_B, ((dep->de_linmem >> 13) & 0x0f) |
((dep->de_linmem >> 11) & 0x40) | (b & 0xb0));
outb_we(dep, EPL_790_HWR, hwr & ~E_790_HWR_SWH);
} else {
outb_we(dep, EPL_MSR, E_MSR_RESET);
outb_we(dep, EPL_MSR, E_MSR_MENABLE |
((dep->de_linmem >> 13) & E_MSR_MEMADDR));
}
if ((we_type & WET_INTERF_CHIP) && !(we_type & WET_790)) {
icr = inb_we(dep, EPL_ICR);
irr = inb_we(dep, EPL_IRR);
int_indx = (icr & E_ICR_IR2) | ((irr & (E_IRR_IR0 | E_IRR_IR1)) >> 5);
int_nr = we_int_table[int_indx];
DEBUG(printf("%s: encoded irq= %d\n", dep->de_name, int_nr));
if (dep->de_irq & DEI_DEFAULT) dep->de_irq = int_nr;
outb_we(dep, EPL_IRR, irr | E_IRR_IEN);
}
if (we_type & WET_790) {
hwr = inb_we(dep, EPL_790_HWR);
outb_we(dep, EPL_790_HWR, hwr | E_790_HWR_SWH);
gcr = inb_we(dep, EPL_790_GCR);
outb_we(dep, EPL_790_HWR, hwr & ~E_790_HWR_SWH);
int_indx = ((gcr & E_790_GCR_IR2) >> 4) |
((gcr & (E_790_GCR_IR1 | E_790_GCR_IR0)) >> 2);
int_nr = we_790int_table[int_indx];
DEBUG(printf("%s: encoded irq= %d\n", dep->de_name, int_nr));
if (dep->de_irq & DEI_DEFAULT) dep->de_irq = int_nr;
icr = inb_we(dep, EPL_790_ICR);
outb_we(dep, EPL_790_ICR, icr | E_790_ICR_EIL);
}
/* Strip the "default flag." */
dep->de_irq &= ~DEI_DEFAULT;
dep->de_offset_page = 0; /* Shared memory starts at 0 */
/* Allocate one send buffer (1.5KB) per 8KB of on board memory.
sendq_nr = dep->de_ramsize / 0x2000;
if (sendq_nr < 1)
sendq_nr = 1;
else if (sendq_nr > SENDQ_NR) */
sendq_nr = SENDQ_NR;
dep->de_sendq_nr = sendq_nr;
for (i = 0; i < sendq_nr; i++) {
dep->de_sendq[i].sq_sendpage = i * SENDQ_PAGES;
}
dep->de_startpage = i * SENDQ_PAGES;
dep->de_stoppage = dep->de_ramsize / DP_PAGESIZE;
ns_init(dep); /* Initialize DP controller */
printf("%s: WD80%d3 (%dkB RAM) at %X:%d:%lX - ",
dep->de_name,
we_type & WET_BRD_16BIT ? 1 : 0,
dep->de_ramsize / 1024,
dep->de_base_port,
dep->de_irq,
dep->de_linmem);
for (i = 0; i < SA_ADDR_LEN; i += 1)
printf("%02X%c", dep->de_address.ea_addr[i],
i < SA_ADDR_LEN - 1 ? ':' : '\n');
return;
}
/*===========================================================================*
* we_stop *
*===========================================================================*/
static void we_stop(dep)
dpeth_t *dep;
{
if (dep->de_16bit) outb_we(dep, EPL_LAAR, E_LAAR_A19 | E_LAAR_LAN16E);
outb_we(dep, EPL_MSR, E_MSR_RESET);
outb_we(dep, EPL_MSR, 0);
sys_irqdisable(&dep->de_hook);
return;
}
/*===========================================================================*
* we_aliasing *
*===========================================================================*/
static int we_aliasing(dep)
dpeth_t *dep;
{
/* Determine whether wd8003 hardware performs register aliasing. This implies
* an old WD8003E board. */
if (inb_we(dep, EPL_REG1) != inb_we(dep, EPL_EA1)) return 0;
if (inb_we(dep, EPL_REG2) != inb_we(dep, EPL_EA2)) return 0;
if (inb_we(dep, EPL_REG3) != inb_we(dep, EPL_EA3)) return 0;
if (inb_we(dep, EPL_REG4) != inb_we(dep, EPL_EA4)) return 0;
if (inb_we(dep, EPL_REG7) != inb_we(dep, EPL_CHKSUM)) return 0;
return 1;
}
/*===========================================================================*
* we_interface_chip *
*===========================================================================*/
static int we_interface_chip(dep)
dpeth_t *dep;
{
/* Determine if the board has an interface chip. */
outb_we(dep, EPL_GP2, 0x35);
if (inb_we(dep, EPL_GP2) != 0x35) return 0;
outb_we(dep, EPL_GP2, 0x3A);
if (inb_we(dep, EPL_GP2) != 0x3A) return 0;
return 1;
}
/*===========================================================================*
* we_16bitboard *
*===========================================================================*/
static int we_16bitboard(dep)
dpeth_t *dep;
{
/* Determine whether the board is capable of doing 16 bit memory moves.
* If the 16 bit enable bit is unchangable by software we'll assume an
* 8 bit board.
*/
int icr;
u8_t tlb;
icr = inb_we(dep, EPL_ICR);
outb_we(dep, EPL_ICR, icr ^ E_ICR_16BIT);
if (inb_we(dep, EPL_ICR) == icr) {
tlb = inb_we(dep, EPL_TLB);
DEBUG(printf("%s: tlb= 0x%x\n", dep->de_name, tlb));
return tlb == E_TLB_EB || tlb == E_TLB_E ||
tlb == E_TLB_SMCE || tlb == E_TLB_SMC8216C;
}
outb_we(dep, EPL_ICR, icr);
return 1;
}
/*===========================================================================*
* we_16bitslot *
*===========================================================================*/
static int we_16bitslot(dep)
dpeth_t *dep;
{
/* Determine if the 16 bit board in plugged into a 16 bit slot. */
return !!(inb_we(dep, EPL_ICR) & E_ICR_16BIT);
}
/*===========================================================================*
* we_ultra *
*===========================================================================*/
static int we_ultra(dep)
dpeth_t *dep;
{
/* Determine if we has an '790 chip. */
u8_t tlb;
tlb = inb_we(dep, EPL_TLB);
return tlb == E_TLB_SMC8216C;
}
#endif /* ENABLE_NETWORKING && ENABLE_WDETH */
/** wd.c **/

103
drivers/dpeth/wd.h Normal file
View file

@ -0,0 +1,103 @@
/*
** File: wd.h
**
** Created: before Dec 28, 1992 by Philip Homburg
** $PchId: wdeth.h,v 1.4 1995/12/22 08:36:57 philip Exp $
**
** $Log$
** Revision 1.1 2005/06/29 10:16:46 beng
** Import of dpeth 3c501/3c509b/.. ethernet driver by
** Giovanni Falzoni <fgalzoni@inwind.it>.
**
** Revision 2.0 2005/06/26 16:16:46 lsodgf0
** Initial revision for Minix 3.0.6
**
** $Id$
*/
#ifndef WDETH_H
#define WDETH_H
/* Western Digital Ethercard Plus, or WD8003E card. */
#define EPL_REG0 0x0 /* Control(write) and status(read) */
#define EPL_REG1 0x1
#define EPL_REG2 0x2
#define EPL_REG3 0x3
#define EPL_REG4 0x4
#define EPL_REG5 0x5
#define EPL_REG6 0x6
#define EPL_REG7 0x7
#define EPL_EA0 0x8 /* Most significant eaddr byte */
#define EPL_EA1 0x9
#define EPL_EA2 0xA
#define EPL_EA3 0xB
#define EPL_EA4 0xC
#define EPL_EA5 0xD /* Least significant eaddr byte */
#define EPL_TLB 0xE
#define EPL_CHKSUM 0xF /* sum from epl_ea0 upto here is 0xFF */
#define EPL_DP8390 0x10 /* NatSemi chip */
#define EPL_MSR EPL_REG0/* memory select register */
#define EPL_ICR EPL_REG1/* interface configuration register */
#define EPL_IRR EPL_REG4/* interrupt request register (IRR) */
#define EPL_790_HWR EPL_REG4/* '790 hardware support register */
#define EPL_LAAR EPL_REG5/* LA address register (write only) */
#define EPL_790_ICR EPL_REG6/* '790 interrupt control register */
#define EPL_GP2 EPL_REG7/* general purpose register 2 */
#define EPL_790_B EPL_EA3 /* '790 memory register */
#define EPL_790_GCR EPL_EA5 /* '790 General Control Register */
/* Bits in EPL_MSR */
#define E_MSR_MEMADDR 0x3F /* Bits SA18-SA13, SA19 implicit 1 */
#define E_MSR_MENABLE 0x40 /* Memory Enable */
#define E_MSR_RESET 0x80 /* Software Reset */
/* Bits in EPL_ICR */
#define E_ICR_16BIT 0x01 /* 16 bit bus */
#define E_ICR_IR2 0x04 /* bit 2 of encoded IRQ */
#define E_ICR_MEMBIT 0x08 /* 583 mem size mask */
/* Bits in EPL_IRR */
#define E_IRR_IR0 0x20 /* bit 0 of encoded IRQ */
#define E_IRR_IR1 0x40 /* bit 1 of encoded IRQ */
#define E_IRR_IEN 0x80 /* enable interrupts */
/* Bits in EPL_LAAR */
#define E_LAAR_A19 0x01 /* address lines for above 1M ram */
#define E_LAAR_A20 0x02 /* address lines for above 1M ram */
#define E_LAAR_A21 0x04 /* address lines for above 1M ram */
#define E_LAAR_A22 0x08 /* address lines for above 1M ram */
#define E_LAAR_A23 0x10 /* address lines for above 1M ram */
#define E_LAAR_SOFTINT 0x20 /* enable software interrupt */
#define E_LAAR_LAN16E 0x40 /* enables 16 bit RAM for LAN */
#define E_LAAR_MEM16E 0x80 /* enables 16 bit RAM for host */
/* Bits and values in EPL_TLB */
#define E_TLB_EB 0x05 /* WD8013EB */
#define E_TLB_E 0x27 /* WD8013 Elite */
#define E_TLB_SMCE 0x29 /* SMC Elite 16 */
#define E_TLB_SMC8216C 0x2B /* SMC 8216 C */
#define E_TLB_REV 0x1F /* revision mask */
#define E_TLB_SOFT 0x20 /* soft config */
#define E_TLB_RAM 0x40 /* extra ram bit */
/* Bits in EPL_790_HWR */
#define E_790_HWR_SWH 0x80 /* switch register set */
/* Bits in EPL_790_ICR */
#define E_790_ICR_EIL 0x01 /* enable interrupts */
/* Bits in EPL_790_GCR when E_790_HWR_SWH is set in EPL_790_HWR */
#define E_790_GCR_IR0 0x04 /* bit 0 of encoded IRQ */
#define E_790_GCR_IR1 0x08 /* bit 1 of encoded IRQ */
#define E_790_GCR_IR2 0x40 /* bit 2 of encoded IRQ */
#define inb_we(dep, reg) (inb(dep->de_base_port+reg))
#define outb_we(dep, reg, data) (outb(dep->de_base_port+reg, data))
#endif /* WDETH_H */
/** wd.h **/

View file

@ -37,17 +37,19 @@
#define IS_PROC_NR 5 /* information server */
#define TTY 6 /* terminal (TTY) driver */
#define MEMORY 8 /* memory driver (RAM disk, null, etc.) */
#define AT_WINI (MEMORY + ENABLE_AT_WINI) /* AT Winchester */
#define FLOPPY (AT_WINI + ENABLE_FLOPPY) /* floppy disk */
#define PRINTER (FLOPPY + ENABLE_PRINTER) /* Centronics */
#define USR8139 (PRINTER + ENABLE_RTL8139) /* Realtek RTL8139 */
#define FXP (USR8139 + ENABLE_FXP) /* Intel Pro/100 */
#define INIT_PROC_NR (FXP + 1) /* init -- goes multiuser */
#define AT_WINI (MEMORY + ENABLE_AT_WINI) /* AT Winchester */
#define FLOPPY (AT_WINI + ENABLE_FLOPPY) /* floppy disk */
#define PRINTER (FLOPPY + ENABLE_PRINTER) /* Centronics */
#define USR8139 (PRINTER + ENABLE_RTL8139) /* Realtek RTL8139 */
#define FXP (USR8139 + ENABLE_FXP) /* Intel Pro/100 */
#define DPETH (FXP + ENABLE_DPETH) /* ISA Network task */
#define INIT_PROC_NR (DPETH + 1) /* init -- goes multiuser */
/* Number of processes contained in the system image. */
#define IMAGE_SIZE (NR_TASKS + \
5 + ENABLE_AT_WINI + ENABLE_FLOPPY + \
ENABLE_PRINTER + ENABLE_RTL8139 + ENABLE_FXP + 1 )
ENABLE_PRINTER + ENABLE_RTL8139 + ENABLE_FXP + \
ENABLE_DPETH + 1 )
/*===========================================================================*

View file

@ -32,7 +32,7 @@
#endif
/* Number of slots in the process table for non-kernel processes. */
#define NR_PROCS 64
#define NR_PROCS 64
/* The buffer cache should be made as large as you can afford. */
#if (MACHINE == IBM_PC && _WORD_SIZE == 2)
@ -92,7 +92,7 @@
#define DMA_SECTORS 1 /* DMA buffer size (must be >= 1) */
/* Enable or disable networking drivers. */
#define ENABLE_DP8390 0 /* enable DP8390 ethernet driver */
#define ENABLE_DPETH 1 /* enable DP8390 ethernet driver */
#define ENABLE_WDETH 0 /* add Western Digital WD80x3 */
#define ENABLE_NE2000 0 /* add Novell NE1000/NE2000 */
#define ENABLE_3C503 0 /* add 3Com Etherlink II (3c503) */

View file

@ -256,7 +256,7 @@ _phys_insb:
mov edx, 8(ebp) ! port to read from
mov edi, 12(ebp) ! destination addr
mov ecx, 16(ebp) ! byte count
shr ecx, 1 ! word count
! shr ecx, 1 ! word count
rep insb ! input many bytes
pop es
pop edi

View file

@ -84,6 +84,9 @@ PUBLIC struct system_image image[] = {
#endif
#if ENABLE_FXP
{ FXP, 0, 0, 2, 0, SYSTEM_CALL_MASK, ALLOW_ALL_MASK, "FXP" },
#endif
#if ENABLE_DPETH
{ DPETH, 0, 0, 2, 0, SYSTEM_CALL_MASK, ALLOW_ALL_MASK, "DPETH" },
#endif
{ INIT_PROC_NR, 0, 0, USER_Q, 0, USER_CALL_MASK, USER_PROC_SENDMASK, "INIT" },
};

View file

@ -138,9 +138,9 @@ PUBLIC void irqtab_dmp()
"clock", /* 00 */
"keyboard", /* 01 */
"cascade", /* 02 */
"eth/rs232", /* 03 */
"rs232", /* 03 */
"rs232", /* 04 */
"xt_wini", /* 05 */
"NIC(eth)", /* 05 */
"floppy", /* 06 */
"printer", /* 07 */
"", /* 08 */

View file

@ -18,6 +18,7 @@ PROGRAMS= ../kernel/kernel \
../drivers/printer/printer \
../drivers/rtl8139/rtl8139 \
../drivers/fxp/fxp \
../drivers/dpeth/dpeth \
../servers/init/init \
# bootdev.img