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), ð_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