2 ** File: 3c501.c Jan. 14, 1997
4 ** Author: Giovanni Falzoni <gfalzoni@inwind.it>
6 ** This file contains specific implementation of the ethernet
7 ** device driver for 3Com Etherlink (3c501) boards. This is a
8 ** very old board and its performances are very poor for today
9 ** network environments.
12 #include <minix/drivers.h>
13 #include <minix/netdriver.h>
16 #if (ENABLE_3C501 == 1)
20 static unsigned char StationAddress
[SA_ADDR_LEN
] = {0, 0, 0, 0, 0, 0,};
21 static buff_t
*TxBuff
= NULL
;
25 ** Function: Reads statistics counters from board.
27 static void el1_getstats(dpeth_t
* dep
)
35 ** Function: Reset function specific for Etherlink hardware.
37 static void el1_reset(dpeth_t
* dep
)
41 for (ix
= 0; ix
< 8; ix
+= 1) /* Resets the board */
42 outb_el1(dep
, EL1_CSR
, ECSR_RESET
);
43 outb_el1(dep
, EL1_CSR
, ECSR_RIDE
| ECSR_SYS
);
45 /* Set Ethernet Address on controller */
46 outb_el1(dep
, EL1_CSR
, ECSR_LOOP
); /* Loopback mode */
47 for (ix
= EL1_ADDRESS
; ix
< SA_ADDR_LEN
; ix
+= 1)
48 outb_el1(dep
, ix
, StationAddress
[ix
]);
50 /* Enable DMA/Interrupt, gain control of Buffer */
51 outb_el1(dep
, EL1_CSR
, ECSR_RIDE
| ECSR_SYS
);
52 /* Clear RX packet area */
53 outw_el1(dep
, EL1_RECVPTR
, 0);
54 /* Enable transmit/receive configuration and flush pending interrupts */
55 outb_el1(dep
, EL1_XMIT
, EXSR_IDLE
| EXSR_16JAM
| EXSR_JAM
| EXSR_UNDER
);
56 outb_el1(dep
, EL1_RECV
, dep
->de_recv_mode
);
57 inb_el1(dep
, EL1_RECV
);
58 inb_el1(dep
, EL1_XMIT
);
59 dep
->de_flags
&= NOT(DEF_XMIT_BUSY
);
63 ** Name: el1_dumpstats
64 ** Function: Dumps counter on screen (support for console display).
66 static void el1_dumpstats(dpeth_t
* UNUSED(dep
))
72 ** Name: el1_mode_init
73 ** Function: Initializes receicer mode
75 static void el1_mode_init(dpeth_t
* dep
)
78 if (dep
->de_flags
& DEF_BROAD
) {
79 dep
->de_recv_mode
= ERSR_BROAD
| ERSR_RMASK
;
81 } else if (dep
->de_flags
& DEF_PROMISC
) {
82 dep
->de_recv_mode
= ERSR_ALL
| ERSR_RMASK
;
84 } else if (dep
->de_flags
& DEF_MULTI
) {
85 dep
->de_recv_mode
= ERSR_MULTI
| ERSR_RMASK
;
88 dep
->de_recv_mode
= ERSR_NONE
| ERSR_RMASK
;
90 outb_el1(dep
, EL1_RECV
, dep
->de_recv_mode
);
91 inb_el1(dep
, EL1_RECV
);
96 ** Function: Receive function. Called from interrupt handler to
97 ** unload recv. buffer or from main (packet to client)
99 static ssize_t
el1_recv(dpeth_t
*dep
, struct netdriver_data
*data
, size_t max
)
104 if ((rxptr
= dep
->de_recvq_head
) == NULL
)
107 /* Remove buffer from queue and free buffer */
108 if (dep
->de_recvq_tail
== dep
->de_recvq_head
)
109 dep
->de_recvq_head
= dep
->de_recvq_tail
= NULL
;
111 dep
->de_recvq_head
= rxptr
->next
;
113 /* Copy buffer to user area */
114 size
= MIN((size_t)rxptr
->size
, max
);
116 netdriver_copyout(data
, 0, rxptr
->buffer
, size
);
118 /* Return buffer to the idle pool */
119 free_buff(dep
, rxptr
);
126 ** Function: Send function.
128 static int el1_send(dpeth_t
*dep
, struct netdriver_data
*data
, size_t size
)
133 if (dep
->de_flags
& DEF_XMIT_BUSY
) {
135 if ((now
- dep
->de_xmit_start
) > 4) {
136 /* Transmitter timed out */
137 DEBUG(printf("3c501: transmitter timed out ... \n"));
138 netdriver_stat_oerror(1);
139 dep
->de_flags
&= NOT(DEF_XMIT_BUSY
);
140 /* Try sending anyway. */
145 /* Since we may have to retransmit, we need a local copy. */
146 if ((txbuff
= alloc_buff(dep
, size
+ sizeof(buff_t
))) == NULL
)
147 panic("out of memory");
149 /* Fill transmit buffer from user area */
153 netdriver_copyin(data
, 0, txbuff
->buffer
, size
);
155 /* Save for retransmission */
157 dep
->de_flags
|= DEF_XMIT_BUSY
;
159 /* Setup board for packet loading */
160 outb_el1(dep
, EL1_CSR
, ECSR_RIDE
| ECSR_SYS
);
161 inb_el1(dep
, EL1_RECV
); /* Clears any spurious interrupt */
162 inb_el1(dep
, EL1_XMIT
);
163 outw_el1(dep
, EL1_RECVPTR
, 0); /* Clears RX packet area */
166 outw_el1(dep
, EL1_XMITPTR
, (EL1_BFRSIZ
- size
));
167 outsb(dep
->de_data_port
, txbuff
->buffer
, size
);
168 /* Starts transmitter */
169 outw_el1(dep
, EL1_XMITPTR
, (EL1_BFRSIZ
- size
));
170 outb_el1(dep
, EL1_CSR
, ECSR_RIDE
| ECSR_XMIT
); /* There it goes... */
172 dep
->de_xmit_start
= getticks();
179 ** Function: Stops board and disable interrupts.
181 static void el1_stop(dpeth_t
* dep
)
185 DEBUG(printf("%s: stopping Etherlink ....\n", netdriver_name()));
186 for (ix
= 0; ix
< 8; ix
+= 1) /* Reset board */
187 outb_el1(dep
, EL1_CSR
, ECSR_RESET
);
188 outb_el1(dep
, EL1_CSR
, ECSR_SYS
);
189 sys_irqdisable(&dep
->de_hook
); /* Disable interrupt */
193 ** Name: el1_interrupt
194 ** Function: Interrupt handler. Acknwledges transmit interrupts
195 ** or unloads receive buffer to memory queue.
197 static void el1_interrupt(dpeth_t
* dep
)
203 csr
= inb_el1(dep
, EL1_CSR
);
204 if ((csr
& ECSR_XMIT
) && (dep
->de_flags
& DEF_XMIT_BUSY
)) {
206 /* Got a transmit interrupt */
207 isr
= inb_el1(dep
, EL1_XMIT
);
208 if ((isr
& (EXSR_16JAM
| EXSR_UNDER
| EXSR_JAM
)) || !(isr
& EXSR_IDLE
)) {
209 DEBUG(printf("3c501: got xmit interrupt (ASR=0x%02X XSR=0x%02X)\n", csr
, isr
));
210 if (isr
& EXSR_JAM
) {
211 /* Sending, packet got a collision */
212 netdriver_stat_coll(1);
213 /* Put pointer back to beginning of packet */
214 outb_el1(dep
, EL1_CSR
, ECSR_RIDE
| ECSR_SYS
);
215 outw_el1(dep
, EL1_XMITPTR
, (EL1_BFRSIZ
- TxBuff
->size
));
216 /* And retrigger transmission */
217 outb_el1(dep
, EL1_CSR
, ECSR_RIDE
| ECSR_XMIT
);
220 } else if ((isr
& EXSR_16JAM
) || !(isr
& EXSR_IDLE
)) {
221 netdriver_stat_oerror(1);
222 } else if (isr
& EXSR_UNDER
) {
223 netdriver_stat_oerror(1);
225 DEBUG(printf("3c501: got xmit interrupt (0x%02X)\n", isr
));
228 /** if (inw_el1(dep, EL1_XMITPTR) == EL1_BFRSIZ) **/
229 /* Packet transmitted successfully */
230 dep
->bytes_Tx
+= (long) (TxBuff
->size
);
231 free_buff(dep
, TxBuff
);
232 dep
->de_flags
&= NOT(DEF_XMIT_BUSY
);
234 if (dep
->de_flags
& DEF_XMIT_BUSY
)
238 } else if ((csr
& (ECSR_RECV
| ECSR_XMTBSY
)) == (ECSR_RECV
| ECSR_XMTBSY
)) {
240 /* Got a receive interrupt */
241 isr
= inb_el1(dep
, EL1_RECV
);
242 pktsize
= inw_el1(dep
, EL1_RECVPTR
);
243 if ((isr
& ERSR_RERROR
) || (isr
& ERSR_STALE
)) {
244 DEBUG(printf("Rx0 (ASR=0x%02X RSR=0x%02X size=%d)\n",
246 netdriver_stat_ierror(1);
248 } else if (pktsize
< NDEV_ETH_PACKET_MIN
||
249 pktsize
> NDEV_ETH_PACKET_MAX
) {
250 DEBUG(printf("Rx1 (ASR=0x%02X RSR=0x%02X size=%d)\n",
252 netdriver_stat_ierror(1);
254 } else if ((rxptr
= alloc_buff(dep
, pktsize
+ sizeof(buff_t
))) == NULL
) {
255 /* Memory not available. Drop packet */
256 netdriver_stat_ierror(1);
258 } else if (isr
& (ERSR_GOOD
| ERSR_ANY
)) {
259 /* Got a good packet. Read it from buffer */
260 outb_el1(dep
, EL1_CSR
, ECSR_RIDE
| ECSR_SYS
);
261 outw_el1(dep
, EL1_XMITPTR
, 0);
262 insb(dep
->de_data_port
, rxptr
->buffer
, pktsize
);
264 rxptr
->size
= pktsize
;
265 dep
->bytes_Rx
+= (long) pktsize
;
266 /* Queue packet to receive queue */
267 if (dep
->de_recvq_head
== NULL
)
268 dep
->de_recvq_head
= rxptr
;
270 dep
->de_recvq_tail
->next
= rxptr
;
271 dep
->de_recvq_tail
= rxptr
;
273 /* Reply to pending Receive requests, if any */
276 } else { /* Nasty condition, should never happen */
278 printf("3c501: got interrupt with status 0x%02X\n"
279 " de_flags=0x%04X XSR=0x%02X RSR=0x%02X \n"
280 " xmit buffer = 0x%4X recv buffer = 0x%4X\n",
282 inb_el1(dep
, EL1_RECV
),
283 inb_el1(dep
, EL1_XMIT
),
284 inw_el1(dep
, EL1_XMITPTR
),
285 inw_el1(dep
, EL1_RECVPTR
))
290 /* Move into receive mode */
291 outb_el1(dep
, EL1_CSR
, ECSR_RIDE
| ECSR_RECV
);
292 outw_el1(dep
, EL1_RECVPTR
, 0);
293 /* Be sure that interrupts are cleared */
294 inb_el1(dep
, EL1_RECV
);
295 inb_el1(dep
, EL1_XMIT
);
300 ** Function: Initalizes board hardware and driver data structures.
302 static void el1_init(dpeth_t
* dep
)
306 dep
->de_irq
&= NOT(DEI_DEFAULT
); /* Strip the default flag. */
307 dep
->de_offset_page
= 0;
308 dep
->de_data_port
= dep
->de_base_port
+ EL1_DATAPORT
;
310 el1_reset(dep
); /* Reset and initialize board */
312 /* Start receiver (default mode) */
313 outw_el1(dep
, EL1_RECVPTR
, 0);
314 outb_el1(dep
, EL1_CSR
, ECSR_RIDE
| ECSR_RECV
);
316 /* Initializes buffer pool */
317 init_buff(dep
, NULL
);
320 printf("%s: Etherlink (%s) at %X:%d - ",
321 netdriver_name(), "3c501", dep
->de_base_port
, dep
->de_irq
);
322 for (ix
= 0; ix
< SA_ADDR_LEN
; ix
+= 1)
323 printf("%02X%c", (dep
->de_address
.na_addr
[ix
] = StationAddress
[ix
]),
324 ix
< SA_ADDR_LEN
- 1 ? ':' : '\n');
326 /* Device specific functions */
327 dep
->de_recvf
= el1_recv
;
328 dep
->de_sendf
= el1_send
;
329 dep
->de_flagsf
= el1_mode_init
;
330 dep
->de_resetf
= el1_reset
;
331 dep
->de_getstatsf
= el1_getstats
;
332 dep
->de_dumpstatsf
= el1_dumpstats
;
333 dep
->de_interruptf
= el1_interrupt
;
338 ** Function: Checks for presence of the board.
340 int el1_probe(dpeth_t
* dep
)
344 for (ix
= 0; ix
< 8; ix
+= 1) /* Reset the board */
345 outb_el1(dep
, EL1_CSR
, ECSR_RESET
);
346 outb_el1(dep
, EL1_CSR
, ECSR_SYS
); /* Leaves buffer to system */
348 /* Check station address */
349 for (ix
= 0; ix
< SA_ADDR_LEN
; ix
+= 1) {
350 outw_el1(dep
, EL1_XMITPTR
, ix
);
351 StationAddress
[ix
] = inb_el1(dep
, EL1_SAPROM
);
353 if (StationAddress
[0] != 0x02 || /* Etherlink Station address */
354 StationAddress
[1] != 0x60 || /* MUST be 02:60:8c:xx:xx:xx */
355 StationAddress
[2] != 0x8C)
356 return FALSE
; /* No Etherlink board at this address */
358 dep
->de_ramsize
= 0; /* RAM size is meaningless */
359 dep
->de_linmem
= 0L; /* Access is via I/O port */
361 /* Device specific functions */
362 dep
->de_initf
= el1_init
;
363 dep
->de_stopf
= el1_stop
;
365 return TRUE
; /* Etherlink board found */
368 #endif /* ENABLE_3C501 */