xref: /minix3/minix/drivers/net/dpeth/8390.c (revision f7df02e7476731c31f12548e38bcadbaf0233f6a)
1433d6423SLionel Sambuc /*
2433d6423SLionel Sambuc **  File:	8390.c		May  02, 2000
3433d6423SLionel Sambuc **
4433d6423SLionel Sambuc **  Author:	Giovanni Falzoni <gfalzoni@inwind.it>
5433d6423SLionel Sambuc **
6433d6423SLionel Sambuc **  This file contains an ethernet device driver for NICs
7433d6423SLionel Sambuc **  equipped with the National Semiconductor NS 8390 chip.
8433d6423SLionel Sambuc **  It has to be associated with the board specific driver.
9433d6423SLionel Sambuc **  Rewritten from Minix 2.0.0 ethernet driver dp8390.c
10433d6423SLionel Sambuc **  to extract the NS 8390 common functions.
11433d6423SLionel Sambuc */
12433d6423SLionel Sambuc 
13433d6423SLionel Sambuc #include <minix/drivers.h>
1491c4db25SDavid van Moolenbroek #include <minix/netdriver.h>
1591c4db25SDavid van Moolenbroek #include <assert.h>
16433d6423SLionel Sambuc #include "dp.h"
17433d6423SLionel Sambuc 
18433d6423SLionel Sambuc #if (ENABLE_DP8390 == 1)
19433d6423SLionel Sambuc 
20433d6423SLionel Sambuc #include "8390.h"
21433d6423SLionel Sambuc 
22433d6423SLionel Sambuc /*
2391c4db25SDavid van Moolenbroek **  Name:	ns_rw_setup
24433d6423SLionel Sambuc **  Function:	Sets the board for reading/writing.
25433d6423SLionel Sambuc */
ns_rw_setup(const dpeth_t * dep,int mode,int size,u16_t offset)26433d6423SLionel Sambuc static void ns_rw_setup(const dpeth_t *dep, int mode, int size, u16_t offset)
27433d6423SLionel Sambuc {
28433d6423SLionel Sambuc 
29433d6423SLionel Sambuc   if (mode == CR_DM_RW) outb_reg0(dep, DP_ISR, ISR_RDC);
30433d6423SLionel Sambuc   outb_reg0(dep, DP_RBCR0, size & 0xFF);
31433d6423SLionel Sambuc   outb_reg0(dep, DP_RBCR1, (size >> 8) & 0xFF);
32433d6423SLionel Sambuc   outb_reg0(dep, DP_RSAR0, offset & 0xFF);
33433d6423SLionel Sambuc   outb_reg0(dep, DP_RSAR1, (offset >> 8) & 0xFF);
34433d6423SLionel Sambuc   mode |= (CR_PS_P0 | CR_STA);
35433d6423SLionel Sambuc   outb_reg0(dep, DP_CR, mode);
36433d6423SLionel Sambuc }
37433d6423SLionel Sambuc 
38433d6423SLionel Sambuc /*
3991c4db25SDavid van Moolenbroek **  Name:	ns_start_xmit
40433d6423SLionel Sambuc **  Function:	Sets the board for for transmitting and fires it.
41433d6423SLionel Sambuc */
ns_start_xmit(const dpeth_t * dep,int size,int pageno)42433d6423SLionel Sambuc static void ns_start_xmit(const dpeth_t * dep, int size, int pageno)
43433d6423SLionel Sambuc {
44433d6423SLionel Sambuc 
45433d6423SLionel Sambuc   outb_reg0(dep, DP_TPSR, pageno);
46433d6423SLionel Sambuc   outb_reg0(dep, DP_TBCR1, size >> 8);
47433d6423SLionel Sambuc   outb_reg0(dep, DP_TBCR0, size & 0xFF);
48433d6423SLionel Sambuc   outb_reg0(dep, DP_CR, CR_NO_DMA | CR_STA | CR_TXP);	/* Fires transmission */
49433d6423SLionel Sambuc }
50433d6423SLionel Sambuc 
51433d6423SLionel Sambuc /*
5291c4db25SDavid van Moolenbroek **  Name:	mem_getblock
53433d6423SLionel Sambuc **  Function:	Reads a block of packet from board (shared memory).
54433d6423SLionel Sambuc */
mem_getblock(dpeth_t * dep,u16_t offset,int size,void * dst)55433d6423SLionel Sambuc static void mem_getblock(dpeth_t *dep, u16_t offset, int size, void *dst)
56433d6423SLionel Sambuc {
5791c4db25SDavid van Moolenbroek 
58*f7df02e7SDavid van Moolenbroek   assert(size >= 0);
59*f7df02e7SDavid van Moolenbroek   assert(offset + (unsigned int)size <= dep->de_ramsize);
6091c4db25SDavid van Moolenbroek 
6191c4db25SDavid van Moolenbroek   memcpy(dst, dep->de_locmem + offset, size);
62433d6423SLionel Sambuc }
63433d6423SLionel Sambuc 
64433d6423SLionel Sambuc /*
6591c4db25SDavid van Moolenbroek **  Name:	mem_nic2user
66433d6423SLionel Sambuc **  Function:	Copies a packet from board to user area (shared memory).
67433d6423SLionel Sambuc */
mem_nic2user(dpeth_t * dep,int pageno,struct netdriver_data * data,size_t size)6891c4db25SDavid van Moolenbroek static void mem_nic2user(dpeth_t *dep, int pageno, struct netdriver_data *data,
6991c4db25SDavid van Moolenbroek 	size_t size)
70433d6423SLionel Sambuc {
7191c4db25SDavid van Moolenbroek   size_t offset, left;
72433d6423SLionel Sambuc 
73433d6423SLionel Sambuc   /* Computes shared memory address (skipping receive header) */
74433d6423SLionel Sambuc   offset = pageno * DP_PAGESIZE + sizeof(dp_rcvhdr_t);
75433d6423SLionel Sambuc 
7691c4db25SDavid van Moolenbroek   if (offset + size > dep->de_stoppage * DP_PAGESIZE) {
7791c4db25SDavid van Moolenbroek 	left = dep->de_stoppage * DP_PAGESIZE - offset;
7891c4db25SDavid van Moolenbroek 	netdriver_copyout(data, 0, dep->de_locmem + offset, left);
79433d6423SLionel Sambuc 	offset = dep->de_startpage * DP_PAGESIZE;
8091c4db25SDavid van Moolenbroek 	netdriver_copyout(data, left, dep->de_locmem + offset, size - left);
8191c4db25SDavid van Moolenbroek   } else
8291c4db25SDavid van Moolenbroek 	netdriver_copyout(data, 0, dep->de_locmem + offset, size);
83433d6423SLionel Sambuc }
84433d6423SLionel Sambuc 
85433d6423SLionel Sambuc /*
8691c4db25SDavid van Moolenbroek **  Name:	mem_user2nic
87433d6423SLionel Sambuc **  Function:	Copies a packet from user area to board (shared memory).
88433d6423SLionel Sambuc */
mem_user2nic(dpeth_t * dep,int pageno,struct netdriver_data * data,size_t size)8991c4db25SDavid van Moolenbroek static void mem_user2nic(dpeth_t *dep, int pageno, struct netdriver_data *data,
9091c4db25SDavid van Moolenbroek 	size_t size)
91433d6423SLionel Sambuc {
9291c4db25SDavid van Moolenbroek   size_t offset;
93433d6423SLionel Sambuc 
94433d6423SLionel Sambuc   /* Computes shared memory address */
95433d6423SLionel Sambuc   offset = pageno * DP_PAGESIZE;
96433d6423SLionel Sambuc 
9791c4db25SDavid van Moolenbroek   netdriver_copyin(data, 0, dep->de_locmem + offset, size);
98433d6423SLionel Sambuc }
99433d6423SLionel Sambuc 
100433d6423SLionel Sambuc /*
10191c4db25SDavid van Moolenbroek **  Name:	pio_getblock
102433d6423SLionel Sambuc **  Function:	Reads a block of packet from board (Prog. I/O).
103433d6423SLionel Sambuc */
pio_getblock(dpeth_t * dep,u16_t offset,int size,void * dst)104433d6423SLionel Sambuc static void pio_getblock(dpeth_t *dep, u16_t offset, int size, void *dst)
105433d6423SLionel Sambuc {
106433d6423SLionel Sambuc 
107433d6423SLionel Sambuc   /* Sets up board for reading */
108433d6423SLionel Sambuc   ns_rw_setup(dep, CR_DM_RR, size, offset);
109433d6423SLionel Sambuc 
11091c4db25SDavid van Moolenbroek   if (dep->de_16bit == TRUE)
111433d6423SLionel Sambuc 	insw(dep->de_data_port, dst, size);
11291c4db25SDavid van Moolenbroek   else
113433d6423SLionel Sambuc 	insb(dep->de_data_port, dst, size);
114433d6423SLionel Sambuc }
115433d6423SLionel Sambuc 
116433d6423SLionel Sambuc /*
11791c4db25SDavid van Moolenbroek **  Name:	pio_nic2user
118433d6423SLionel Sambuc **  Function:	Copies a packet from board to user area (Prog. I/O).
119433d6423SLionel Sambuc */
pio_nic2user(dpeth_t * dep,int pageno,struct netdriver_data * data,size_t size)12091c4db25SDavid van Moolenbroek static void pio_nic2user(dpeth_t *dep, int pageno, struct netdriver_data *data,
12191c4db25SDavid van Moolenbroek 	size_t size)
122433d6423SLionel Sambuc {
12391c4db25SDavid van Moolenbroek   size_t offset, left;
124433d6423SLionel Sambuc 
125433d6423SLionel Sambuc   /* Computes memory address (skipping receive header) */
126433d6423SLionel Sambuc   offset = pageno * DP_PAGESIZE + sizeof(dp_rcvhdr_t);
127433d6423SLionel Sambuc 
12891c4db25SDavid van Moolenbroek   if (offset + size > dep->de_stoppage * DP_PAGESIZE) {
12991c4db25SDavid van Moolenbroek 	left = dep->de_stoppage * DP_PAGESIZE - offset;
130433d6423SLionel Sambuc 
13191c4db25SDavid van Moolenbroek 	ns_rw_setup(dep, CR_DM_RR, left, offset);
132433d6423SLionel Sambuc 
13391c4db25SDavid van Moolenbroek 	if (dep->de_16bit)
13491c4db25SDavid van Moolenbroek 		netdriver_portinw(data, 0, dep->de_data_port, left);
13591c4db25SDavid van Moolenbroek 	else
13691c4db25SDavid van Moolenbroek 		netdriver_portinb(data, 0, dep->de_data_port, left);
137433d6423SLionel Sambuc 
138433d6423SLionel Sambuc 	offset = dep->de_startpage * DP_PAGESIZE;
13991c4db25SDavid van Moolenbroek   } else
14091c4db25SDavid van Moolenbroek 	left = 0;
141433d6423SLionel Sambuc 
14291c4db25SDavid van Moolenbroek   ns_rw_setup(dep, CR_DM_RR, size - left, offset);
14391c4db25SDavid van Moolenbroek 
14491c4db25SDavid van Moolenbroek   if (dep->de_16bit)
14591c4db25SDavid van Moolenbroek 	netdriver_portinw(data, left, dep->de_data_port, size - left);
14691c4db25SDavid van Moolenbroek   else
14791c4db25SDavid van Moolenbroek 	netdriver_portinb(data, left, dep->de_data_port, size - left);
148433d6423SLionel Sambuc }
149433d6423SLionel Sambuc 
150433d6423SLionel Sambuc /*
15191c4db25SDavid van Moolenbroek **  Name:	pio_user2nic
152433d6423SLionel Sambuc **  Function:	Copies a packet from user area to board (Prog. I/O).
153433d6423SLionel Sambuc */
pio_user2nic(dpeth_t * dep,int pageno,struct netdriver_data * data,size_t size)15491c4db25SDavid van Moolenbroek static void pio_user2nic(dpeth_t *dep, int pageno, struct netdriver_data *data,
15591c4db25SDavid van Moolenbroek 	size_t size)
156433d6423SLionel Sambuc {
15791c4db25SDavid van Moolenbroek   int ix;
158433d6423SLionel Sambuc 
159433d6423SLionel Sambuc   /* Sets up board for writing */
16091c4db25SDavid van Moolenbroek   ns_rw_setup(dep, CR_DM_RW, size, pageno * DP_PAGESIZE);
161433d6423SLionel Sambuc 
16291c4db25SDavid van Moolenbroek   if (dep->de_16bit)
16391c4db25SDavid van Moolenbroek 	netdriver_portoutw(data, 0, dep->de_data_port, size);
16491c4db25SDavid van Moolenbroek   else
16591c4db25SDavid van Moolenbroek 	netdriver_portoutb(data, 0, dep->de_data_port, size);
166433d6423SLionel Sambuc 
167433d6423SLionel Sambuc   for (ix = 0; ix < 100; ix += 1) {
168433d6423SLionel Sambuc 	if (inb_reg0(dep, DP_ISR) & ISR_RDC) break;
169433d6423SLionel Sambuc   }
17091c4db25SDavid van Moolenbroek   if (ix == 100)
17191c4db25SDavid van Moolenbroek 	panic("remote dma failed to complete");
172433d6423SLionel Sambuc }
173433d6423SLionel Sambuc 
174433d6423SLionel Sambuc /*
17591c4db25SDavid van Moolenbroek **  Name:	ns_stats
176433d6423SLionel Sambuc **  Function:	Updates counters reading from device
177433d6423SLionel Sambuc */
ns_stats(dpeth_t * dep)178433d6423SLionel Sambuc static void ns_stats(dpeth_t * dep)
179433d6423SLionel Sambuc {
180433d6423SLionel Sambuc 
181*f7df02e7SDavid van Moolenbroek   netdriver_stat_ierror(inb_reg0(dep, DP_CNTR0));
182*f7df02e7SDavid van Moolenbroek   netdriver_stat_ierror(inb_reg0(dep, DP_CNTR1));
183*f7df02e7SDavid van Moolenbroek   netdriver_stat_ierror(inb_reg0(dep, DP_CNTR2));
184433d6423SLionel Sambuc }
185433d6423SLionel Sambuc 
186433d6423SLionel Sambuc /*
18791c4db25SDavid van Moolenbroek **  Name:	ns_dodump
18891c4db25SDavid van Moolenbroek **  Function:	Displays statistics (a request from a function key).
189433d6423SLionel Sambuc */
ns_dodump(dpeth_t * dep)190433d6423SLionel Sambuc static void ns_dodump(dpeth_t * dep)
191433d6423SLionel Sambuc {
192433d6423SLionel Sambuc 
19391c4db25SDavid van Moolenbroek   ns_stats(dep);		/* Forces reading of counters from board */
194433d6423SLionel Sambuc }
195433d6423SLionel Sambuc 
196433d6423SLionel Sambuc /*
19791c4db25SDavid van Moolenbroek **  Name:	ns_reinit
198433d6423SLionel Sambuc **  Function:	Updates receiver configuration.
199433d6423SLionel Sambuc */
ns_reinit(dpeth_t * dep)200433d6423SLionel Sambuc static void ns_reinit(dpeth_t * dep)
201433d6423SLionel Sambuc {
202433d6423SLionel Sambuc   int dp_reg = 0;
203433d6423SLionel Sambuc 
204433d6423SLionel Sambuc   if (dep->de_flags & DEF_PROMISC) dp_reg |= RCR_AB | RCR_PRO | RCR_AM;
205433d6423SLionel Sambuc   if (dep->de_flags & DEF_BROAD) dp_reg |= RCR_AB;
206433d6423SLionel Sambuc   if (dep->de_flags & DEF_MULTI) dp_reg |= RCR_AM;
207433d6423SLionel Sambuc   outb_reg0(dep, DP_CR, CR_PS_P0);
208433d6423SLionel Sambuc   outb_reg0(dep, DP_RCR, dp_reg);
209433d6423SLionel Sambuc }
210433d6423SLionel Sambuc 
211433d6423SLionel Sambuc /*
21291c4db25SDavid van Moolenbroek **  Name:	ns_send
213433d6423SLionel Sambuc **  Function:	Transfers packet to device and starts sending.
214433d6423SLionel Sambuc */
ns_send(dpeth_t * dep,struct netdriver_data * data,size_t size)21591c4db25SDavid van Moolenbroek static int ns_send(dpeth_t *dep, struct netdriver_data *data, size_t size)
216433d6423SLionel Sambuc {
217*f7df02e7SDavid van Moolenbroek   unsigned int queue;
218433d6423SLionel Sambuc 
21991c4db25SDavid van Moolenbroek   queue = dep->de_sendq_head;
22091c4db25SDavid van Moolenbroek   if (dep->de_sendq[queue].sq_filled)
22191c4db25SDavid van Moolenbroek 	return SUSPEND;
22291c4db25SDavid van Moolenbroek 
22391c4db25SDavid van Moolenbroek   (dep->de_user2nicf)(dep, dep->de_sendq[queue].sq_sendpage, data, size);
224433d6423SLionel Sambuc   dep->bytes_Tx += (long) size;
225433d6423SLionel Sambuc   dep->de_sendq[queue].sq_filled = TRUE;
22691c4db25SDavid van Moolenbroek   dep->de_flags |= DEF_XMIT_BUSY;
227433d6423SLionel Sambuc   if (dep->de_sendq_tail == queue) {	/* there it goes.. */
228433d6423SLionel Sambuc 	ns_start_xmit(dep, size, dep->de_sendq[queue].sq_sendpage);
229433d6423SLionel Sambuc   } else
230433d6423SLionel Sambuc 	dep->de_sendq[queue].sq_size = size;
231433d6423SLionel Sambuc 
232433d6423SLionel Sambuc   if (++queue == dep->de_sendq_nr) queue = 0;
233433d6423SLionel Sambuc   dep->de_sendq_head = queue;
234433d6423SLionel Sambuc 
23591c4db25SDavid van Moolenbroek   return OK;
236433d6423SLionel Sambuc }
237433d6423SLionel Sambuc 
238433d6423SLionel Sambuc /*
23991c4db25SDavid van Moolenbroek **  Name:	ns_reset
240433d6423SLionel Sambuc **  Function:	Resets device.
241433d6423SLionel Sambuc */
ns_reset(dpeth_t * dep)242433d6423SLionel Sambuc static void ns_reset(dpeth_t * dep)
243433d6423SLionel Sambuc {
244*f7df02e7SDavid van Moolenbroek   unsigned int ix;
245433d6423SLionel Sambuc 
246433d6423SLionel Sambuc   /* Stop chip */
247433d6423SLionel Sambuc   outb_reg0(dep, DP_CR, CR_STP | CR_NO_DMA);
248433d6423SLionel Sambuc   outb_reg0(dep, DP_RBCR0, 0);
249433d6423SLionel Sambuc   outb_reg0(dep, DP_RBCR1, 0);
25091c4db25SDavid van Moolenbroek   for (ix = 0; ix < 0x1000 && (inb_reg0(dep, DP_ISR) & ISR_RST) == 0; ix += 1)
251433d6423SLionel Sambuc 	 /* Do nothing */ ;
252433d6423SLionel Sambuc   outb_reg0(dep, DP_TCR, TCR_1EXTERNAL | TCR_OFST);
253433d6423SLionel Sambuc   outb_reg0(dep, DP_CR, CR_STA | CR_NO_DMA);
254433d6423SLionel Sambuc   outb_reg0(dep, DP_TCR, TCR_NORMAL | TCR_OFST);
255433d6423SLionel Sambuc 
256433d6423SLionel Sambuc   /* Acknowledge the ISR_RDC (remote dma) interrupt. */
25791c4db25SDavid van Moolenbroek   for (ix = 0; ix < 0x1000 && (inb_reg0(dep, DP_ISR) & ISR_RDC) == 0; ix += 1)
258433d6423SLionel Sambuc 	 /* Do nothing */ ;
259433d6423SLionel Sambuc   outb_reg0(dep, DP_ISR, inb_reg0(dep, DP_ISR) & NOT(ISR_RDC));
260433d6423SLionel Sambuc 
261433d6423SLionel Sambuc   /* Reset the transmit ring. If we were transmitting a packet, we
262433d6423SLionel Sambuc    * pretend that the packet is processed. Higher layers will
263433d6423SLionel Sambuc    * retransmit if the packet wasn't actually sent. */
264433d6423SLionel Sambuc   dep->de_sendq_head = dep->de_sendq_tail = 0;
265433d6423SLionel Sambuc   for (ix = 0; ix < dep->de_sendq_nr; ix++)
266433d6423SLionel Sambuc 	dep->de_sendq[ix].sq_filled = FALSE;
26791c4db25SDavid van Moolenbroek   netdriver_send();
268433d6423SLionel Sambuc }
269433d6423SLionel Sambuc 
270433d6423SLionel Sambuc /*
27191c4db25SDavid van Moolenbroek **  Name:	ns_recv
272433d6423SLionel Sambuc **  Function:	Gets a packet from device
273433d6423SLionel Sambuc */
ns_recv(dpeth_t * dep,struct netdriver_data * data,size_t max)27491c4db25SDavid van Moolenbroek static ssize_t ns_recv(dpeth_t *dep, struct netdriver_data *data, size_t max)
275433d6423SLionel Sambuc {
276433d6423SLionel Sambuc   dp_rcvhdr_t header;
277433d6423SLionel Sambuc   unsigned pageno, curr, next;
27891c4db25SDavid van Moolenbroek   size_t length;
279433d6423SLionel Sambuc   int packet_processed = FALSE;
280433d6423SLionel Sambuc #ifdef ETH_IGN_PROTO
281433d6423SLionel Sambuc   u16_t eth_type;
282433d6423SLionel Sambuc #endif
283433d6423SLionel Sambuc 
284433d6423SLionel Sambuc   pageno = inb_reg0(dep, DP_BNRY) + 1;
285433d6423SLionel Sambuc   if (pageno == dep->de_stoppage) pageno = dep->de_startpage;
286433d6423SLionel Sambuc 
287433d6423SLionel Sambuc   do {
288433d6423SLionel Sambuc 	/* */
289433d6423SLionel Sambuc 	outb_reg0(dep, DP_CR, CR_PS_P1);
290433d6423SLionel Sambuc 	curr = inb_reg1(dep, DP_CURR);
291433d6423SLionel Sambuc 	outb_reg0(dep, DP_CR, CR_PS_P0 | CR_NO_DMA | CR_STA);
292433d6423SLionel Sambuc 
29391c4db25SDavid van Moolenbroek 	if (curr == pageno)
29491c4db25SDavid van Moolenbroek 		return SUSPEND;
295433d6423SLionel Sambuc 
29691c4db25SDavid van Moolenbroek 	(dep->de_getblockf)(dep, pageno * DP_PAGESIZE, sizeof(header),
29791c4db25SDavid van Moolenbroek 	    &header);
298433d6423SLionel Sambuc #ifdef ETH_IGN_PROTO
29991c4db25SDavid van Moolenbroek 	(dep->de_getblockf)(dep, pageno * DP_PAGESIZE + sizeof(header) +
300*f7df02e7SDavid van Moolenbroek 	    2 * sizeof(netdriver_addr_t), sizeof(eth_type), &eth_type);
301433d6423SLionel Sambuc #endif
30291c4db25SDavid van Moolenbroek 	length = (header.dr_rbcl | (header.dr_rbch << 8)) -
30391c4db25SDavid van Moolenbroek 	    sizeof(dp_rcvhdr_t);
304433d6423SLionel Sambuc 	next = header.dr_next;
305433d6423SLionel Sambuc 
306*f7df02e7SDavid van Moolenbroek 	if (length < NDEV_ETH_PACKET_MIN || length > max) {
30791c4db25SDavid van Moolenbroek 		printf("%s: packet with strange length arrived: %zu\n",
308*f7df02e7SDavid van Moolenbroek 			netdriver_name(), length);
309*f7df02e7SDavid van Moolenbroek 		netdriver_stat_ierror(1);
310433d6423SLionel Sambuc 		next = curr;
311433d6423SLionel Sambuc 
312433d6423SLionel Sambuc 	} else if (next < dep->de_startpage || next >= dep->de_stoppage) {
313*f7df02e7SDavid van Moolenbroek 		printf("%s: strange next page\n", netdriver_name());
314*f7df02e7SDavid van Moolenbroek 		netdriver_stat_ierror(1);
315433d6423SLionel Sambuc 		next = curr;
316433d6423SLionel Sambuc #ifdef ETH_IGN_PROTO
317433d6423SLionel Sambuc 	} else if (eth_type == eth_ign_proto) {
318433d6423SLionel Sambuc 		/* Hack: ignore packets of a given protocol */
319433d6423SLionel Sambuc 		static int first = TRUE;
320433d6423SLionel Sambuc 		if (first) {
321433d6423SLionel Sambuc 			first = FALSE;
32291c4db25SDavid van Moolenbroek 			printf("%s: dropping proto %04x packet\n",
323*f7df02e7SDavid van Moolenbroek 			    netdriver_name(), ntohs(eth_ign_proto));
324433d6423SLionel Sambuc 		}
325433d6423SLionel Sambuc 		next = curr;
326433d6423SLionel Sambuc #endif
327433d6423SLionel Sambuc 	} else if (header.dr_status & RSR_FO) {
328433d6423SLionel Sambuc 		/* This is very serious, issue a warning and reset buffers */
32991c4db25SDavid van Moolenbroek 		printf("%s: fifo overrun, resetting receive buffer\n",
330*f7df02e7SDavid van Moolenbroek 		    netdriver_name());
331*f7df02e7SDavid van Moolenbroek 		netdriver_stat_ierror(1);
332433d6423SLionel Sambuc 		next = curr;
333433d6423SLionel Sambuc 
33491c4db25SDavid van Moolenbroek 	} else if (header.dr_status & RSR_PRX) {
33591c4db25SDavid van Moolenbroek 		(dep->de_nic2userf)(dep, pageno, data, length);
336433d6423SLionel Sambuc 		packet_processed = TRUE;
337433d6423SLionel Sambuc 	}
338433d6423SLionel Sambuc 	dep->bytes_Rx += (long) length;
33991c4db25SDavid van Moolenbroek 	outb_reg0(dep, DP_BNRY,
34091c4db25SDavid van Moolenbroek 	    (next == dep->de_startpage ? dep->de_stoppage : next) - 1);
341433d6423SLionel Sambuc 	pageno = next;
342433d6423SLionel Sambuc   } while (!packet_processed);
34391c4db25SDavid van Moolenbroek 
34491c4db25SDavid van Moolenbroek   return length;
345433d6423SLionel Sambuc }
346433d6423SLionel Sambuc 
347433d6423SLionel Sambuc /*
34891c4db25SDavid van Moolenbroek **  Name:	ns_interrupt
349433d6423SLionel Sambuc **  Function:	Handles interrupt.
350433d6423SLionel Sambuc */
ns_interrupt(dpeth_t * dep)351433d6423SLionel Sambuc static void ns_interrupt(dpeth_t * dep)
352433d6423SLionel Sambuc {
353433d6423SLionel Sambuc   int isr, tsr;
354*f7df02e7SDavid van Moolenbroek   unsigned int queue;
355433d6423SLionel Sambuc 
356433d6423SLionel Sambuc   while ((isr = inb_reg0(dep, DP_ISR)) != 0) {
357433d6423SLionel Sambuc 
358433d6423SLionel Sambuc 	outb_reg0(dep, DP_ISR, isr);
359433d6423SLionel Sambuc 	if (isr & (ISR_PTX | ISR_TXE)) {
360433d6423SLionel Sambuc 
361433d6423SLionel Sambuc 		tsr = inb_reg0(dep, DP_TSR);
362433d6423SLionel Sambuc 		if (tsr & TSR_PTX) {
363*f7df02e7SDavid van Moolenbroek 			/* Packet transmission was successful. */
364433d6423SLionel Sambuc 		}
365*f7df02e7SDavid van Moolenbroek 		if (tsr & TSR_COL)
366*f7df02e7SDavid van Moolenbroek 			netdriver_stat_coll(1);
367433d6423SLionel Sambuc 		if (tsr & (TSR_ABT | TSR_FU)) {
368*f7df02e7SDavid van Moolenbroek 			netdriver_stat_oerror(1);
369433d6423SLionel Sambuc 		}
370433d6423SLionel Sambuc 		if ((isr & ISR_TXE) || (tsr & (TSR_CRS | TSR_CDH | TSR_OWC))) {
37191c4db25SDavid van Moolenbroek 			printf("%s: got send Error (0x%02X)\n",
372*f7df02e7SDavid van Moolenbroek 			    netdriver_name(), tsr);
373*f7df02e7SDavid van Moolenbroek 			netdriver_stat_oerror(1);
374433d6423SLionel Sambuc 		}
375433d6423SLionel Sambuc 		queue = dep->de_sendq_tail;
376433d6423SLionel Sambuc 
377433d6423SLionel Sambuc 		if (!(dep->de_sendq[queue].sq_filled)) { /* Hardware bug? */
37891c4db25SDavid van Moolenbroek 			printf("%s: transmit interrupt, but not sending\n",
379*f7df02e7SDavid van Moolenbroek 			    netdriver_name());
380433d6423SLionel Sambuc 			continue;
381433d6423SLionel Sambuc 		}
382433d6423SLionel Sambuc 		dep->de_sendq[queue].sq_filled = FALSE;
383433d6423SLionel Sambuc 		if (++queue == dep->de_sendq_nr) queue = 0;
384433d6423SLionel Sambuc 		dep->de_sendq_tail = queue;
385433d6423SLionel Sambuc 		if (dep->de_sendq[queue].sq_filled) {
386433d6423SLionel Sambuc 			ns_start_xmit(dep, dep->de_sendq[queue].sq_size,
387433d6423SLionel Sambuc 				dep->de_sendq[queue].sq_sendpage);
388433d6423SLionel Sambuc 		}
38991c4db25SDavid van Moolenbroek 		netdriver_send();
390433d6423SLionel Sambuc 	}
391433d6423SLionel Sambuc 	if (isr & ISR_PRX) {
39291c4db25SDavid van Moolenbroek 		netdriver_recv();
393433d6423SLionel Sambuc 	}
394433d6423SLionel Sambuc 	if (isr & ISR_RXE) {
39591c4db25SDavid van Moolenbroek 		printf("%s: got recv Error (0x%04X)\n",
396*f7df02e7SDavid van Moolenbroek 		    netdriver_name(), inb_reg0(dep, DP_RSR));
397*f7df02e7SDavid van Moolenbroek 		netdriver_stat_ierror(1);
398433d6423SLionel Sambuc 	}
399433d6423SLionel Sambuc 	if (isr & ISR_CNT) {
400*f7df02e7SDavid van Moolenbroek 		ns_stats(dep);
401433d6423SLionel Sambuc 	}
402433d6423SLionel Sambuc 	if (isr & ISR_OVW) {
403*f7df02e7SDavid van Moolenbroek 		printf("%s: got overwrite warning\n", netdriver_name());
404433d6423SLionel Sambuc 	}
405433d6423SLionel Sambuc 	if (isr & ISR_RDC) {
406433d6423SLionel Sambuc 		/* Nothing to do */
407433d6423SLionel Sambuc 	}
408433d6423SLionel Sambuc 	if (isr & ISR_RST) {
40991c4db25SDavid van Moolenbroek 		/* This means we got an interrupt but the ethernet chip is shut
41091c4db25SDavid van Moolenbroek 		 * down. We reset the chip right away, possibly losing received
41191c4db25SDavid van Moolenbroek 		 * packets in the process. There used to be a more elaborate
41291c4db25SDavid van Moolenbroek 		 * approach of resetting only after all pending packets had
41391c4db25SDavid van Moolenbroek 		 * been accepted, but it was broken and this is simpler anyway.
41491c4db25SDavid van Moolenbroek 		 */
415*f7df02e7SDavid van Moolenbroek 		printf("%s: network interface stopped\n",
416*f7df02e7SDavid van Moolenbroek 		    netdriver_name());
41791c4db25SDavid van Moolenbroek 		ns_reset(dep);
418433d6423SLionel Sambuc 		break;
419433d6423SLionel Sambuc 	}
420433d6423SLionel Sambuc   }
421433d6423SLionel Sambuc }
422433d6423SLionel Sambuc 
423433d6423SLionel Sambuc /*
42491c4db25SDavid van Moolenbroek **  Name:	ns_init
425433d6423SLionel Sambuc **  Function:	Initializes the NS 8390
426433d6423SLionel Sambuc */
ns_init(dpeth_t * dep)427433d6423SLionel Sambuc void ns_init(dpeth_t * dep)
428433d6423SLionel Sambuc {
429*f7df02e7SDavid van Moolenbroek   unsigned int dp_reg;
430*f7df02e7SDavid van Moolenbroek   unsigned int ix;
431433d6423SLionel Sambuc 
432433d6423SLionel Sambuc   /* NS8390 initialization (as recommended in National Semiconductor specs) */
433433d6423SLionel Sambuc   outb_reg0(dep, DP_CR, CR_PS_P0 | CR_STP | CR_NO_DMA);	/* 0x21 */
434433d6423SLionel Sambuc   outb_reg0(dep, DP_DCR, (((dep->de_16bit) ? DCR_WORDWIDE : DCR_BYTEWIDE) |
435433d6423SLionel Sambuc 			DCR_LTLENDIAN | DCR_8BYTES | DCR_BMS));
436433d6423SLionel Sambuc   outb_reg0(dep, DP_RBCR0, 0);
437433d6423SLionel Sambuc   outb_reg0(dep, DP_RBCR1, 0);
438433d6423SLionel Sambuc   outb_reg0(dep, DP_RCR, RCR_MON);	/* Sets Monitor mode */
439433d6423SLionel Sambuc   outb_reg0(dep, DP_TCR, TCR_INTERNAL);	/* Sets Loopback mode 1 */
440433d6423SLionel Sambuc   outb_reg0(dep, DP_PSTART, dep->de_startpage);
441433d6423SLionel Sambuc   outb_reg0(dep, DP_PSTOP, dep->de_stoppage);
442433d6423SLionel Sambuc   outb_reg0(dep, DP_BNRY, dep->de_stoppage - 1);
443433d6423SLionel Sambuc   outb_reg0(dep, DP_ISR, 0xFF);	/* Clears Interrupt Status Register */
444433d6423SLionel Sambuc   outb_reg0(dep, DP_IMR, 0);	/* Clears Interrupt Mask Register */
445433d6423SLionel Sambuc 
446433d6423SLionel Sambuc   /* Copies station address in page 1 registers */
447433d6423SLionel Sambuc   outb_reg0(dep, DP_CR, CR_PS_P1 | CR_NO_DMA);	/* Selects Page 1 */
448433d6423SLionel Sambuc   for (ix = 0; ix < SA_ADDR_LEN; ix += 1)	/* Initializes address */
449*f7df02e7SDavid van Moolenbroek 	outb_reg1(dep, DP_PAR0 + ix, dep->de_address.na_addr[ix]);
450433d6423SLionel Sambuc   for (ix = DP_MAR0; ix <= DP_MAR7; ix += 1)	/* Initializes address */
451433d6423SLionel Sambuc 	outb_reg1(dep, ix, 0xFF);
452433d6423SLionel Sambuc 
453433d6423SLionel Sambuc   outb_reg1(dep, DP_CURR, dep->de_startpage);
454433d6423SLionel Sambuc   outb_reg1(dep, DP_CR, CR_PS_P0 | CR_NO_DMA);	/* Selects Page 0 */
455433d6423SLionel Sambuc 
456433d6423SLionel Sambuc   inb_reg0(dep, DP_CNTR0);	/* Resets counters by reading them */
457433d6423SLionel Sambuc   inb_reg0(dep, DP_CNTR1);
458433d6423SLionel Sambuc   inb_reg0(dep, DP_CNTR2);
459433d6423SLionel Sambuc 
460433d6423SLionel Sambuc   dp_reg = IMR_PRXE | IMR_PTXE | IMR_RXEE | IMR_TXEE | IMR_OVWE | IMR_CNTE;
461433d6423SLionel Sambuc   outb_reg0(dep, DP_ISR, 0xFF);	/* Clears Interrupt Status Register */
462433d6423SLionel Sambuc   outb_reg0(dep, DP_IMR, dp_reg);	/* Sets Interrupt Mask register */
463433d6423SLionel Sambuc 
464433d6423SLionel Sambuc   dp_reg = 0;
465433d6423SLionel Sambuc   if (dep->de_flags & DEF_PROMISC) dp_reg |= RCR_AB | RCR_PRO | RCR_AM;
466433d6423SLionel Sambuc   if (dep->de_flags & DEF_BROAD) dp_reg |= RCR_AB;
467433d6423SLionel Sambuc   if (dep->de_flags & DEF_MULTI) dp_reg |= RCR_AM;
468433d6423SLionel Sambuc   outb_reg0(dep, DP_RCR, dp_reg);	/* Sets receive as requested */
469433d6423SLionel Sambuc   outb_reg0(dep, DP_TCR, TCR_NORMAL);	/* Sets transmitter */
470433d6423SLionel Sambuc 
471433d6423SLionel Sambuc   outb_reg0(dep, DP_CR, CR_STA | CR_NO_DMA);	/* Starts board */
472433d6423SLionel Sambuc 
473433d6423SLionel Sambuc   /* Initializes the send queue. */
474433d6423SLionel Sambuc   for (ix = 0; ix < dep->de_sendq_nr; ix += 1)
475433d6423SLionel Sambuc 	dep->de_sendq[ix].sq_filled = 0;
476433d6423SLionel Sambuc   dep->de_sendq_head = dep->de_sendq_tail = 0;
477433d6423SLionel Sambuc 
478433d6423SLionel Sambuc   /* Device specific functions */
479433d6423SLionel Sambuc   if (!dep->de_prog_IO) {
480433d6423SLionel Sambuc 	dep->de_user2nicf = mem_user2nic;
481433d6423SLionel Sambuc 	dep->de_nic2userf = mem_nic2user;
482433d6423SLionel Sambuc 	dep->de_getblockf = mem_getblock;
483433d6423SLionel Sambuc   } else {
484433d6423SLionel Sambuc 	dep->de_user2nicf = pio_user2nic;
485433d6423SLionel Sambuc 	dep->de_nic2userf = pio_nic2user;
486433d6423SLionel Sambuc 	dep->de_getblockf = pio_getblock;
487433d6423SLionel Sambuc   }
488433d6423SLionel Sambuc   dep->de_recvf = ns_recv;
489433d6423SLionel Sambuc   dep->de_sendf = ns_send;
490433d6423SLionel Sambuc   dep->de_flagsf = ns_reinit;
491433d6423SLionel Sambuc   dep->de_resetf = ns_reset;
492433d6423SLionel Sambuc   dep->de_getstatsf = ns_stats;
493433d6423SLionel Sambuc   dep->de_dumpstatsf = ns_dodump;
494433d6423SLionel Sambuc   dep->de_interruptf = ns_interrupt;
495433d6423SLionel Sambuc }
496433d6423SLionel Sambuc 
497433d6423SLionel Sambuc #endif				/* ENABLE_DP8390 */
498433d6423SLionel Sambuc 
499433d6423SLionel Sambuc /** end 8390.c **/
500