1 | /*
|
---|
2 | ** File: 3c501.c Jan. 14, 1997
|
---|
3 | **
|
---|
4 | ** Author: Giovanni Falzoni <gfalzoni@inwind.it>
|
---|
5 | **
|
---|
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.
|
---|
10 | **
|
---|
11 | ** $Id: 3c501.c,v 1.3 2005/08/05 19:08:43 beng Exp $
|
---|
12 | */
|
---|
13 |
|
---|
14 | #include "drivers.h"
|
---|
15 | #include <minix/com.h>
|
---|
16 | #include <net/hton.h>
|
---|
17 | #include <net/gen/ether.h>
|
---|
18 | #include <net/gen/eth_io.h>
|
---|
19 | #include "dp.h"
|
---|
20 |
|
---|
21 | #if (ENABLE_3C501 == 1)
|
---|
22 |
|
---|
23 | #include "3c501.h"
|
---|
24 |
|
---|
25 | static unsigned char StationAddress[SA_ADDR_LEN] = {0, 0, 0, 0, 0, 0,};
|
---|
26 | static buff_t *TxBuff = NULL;
|
---|
27 |
|
---|
28 | /*
|
---|
29 | ** Name: void el1_getstats(dpeth_t *dep)
|
---|
30 | ** Function: Reads statistics counters from board.
|
---|
31 | **/
|
---|
32 | static void el1_getstats(dpeth_t * dep)
|
---|
33 | {
|
---|
34 |
|
---|
35 | return; /* Nothing to do */
|
---|
36 | }
|
---|
37 |
|
---|
38 | /*
|
---|
39 | ** Name: void el1_reset(dpeth_t *dep)
|
---|
40 | ** Function: Reset function specific for Etherlink hardware.
|
---|
41 | */
|
---|
42 | static void el1_reset(dpeth_t * dep)
|
---|
43 | {
|
---|
44 | int ix;
|
---|
45 |
|
---|
46 | for (ix = 0; ix < 8; ix += 1) /* Resets the board */
|
---|
47 | outb_el1(dep, EL1_CSR, ECSR_RESET);
|
---|
48 | outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS);
|
---|
49 |
|
---|
50 | /* Set Ethernet Address on controller */
|
---|
51 | outb_el1(dep, EL1_CSR, ECSR_LOOP); /* Loopback mode */
|
---|
52 | for (ix = EL1_ADDRESS; ix < SA_ADDR_LEN; ix += 1)
|
---|
53 | outb_el1(dep, ix, StationAddress[ix]);
|
---|
54 |
|
---|
55 | lock();
|
---|
56 | /* Enable DMA/Interrupt, gain control of Buffer */
|
---|
57 | outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS);
|
---|
58 | /* Clear RX packet area */
|
---|
59 | outw_el1(dep, EL1_RECVPTR, 0);
|
---|
60 | /* Enable transmit/receive configuration and flush pending interrupts */
|
---|
61 | outb_el1(dep, EL1_XMIT, EXSR_IDLE | EXSR_16JAM | EXSR_JAM | EXSR_UNDER);
|
---|
62 | outb_el1(dep, EL1_RECV, dep->de_recv_mode);
|
---|
63 | inb_el1(dep, EL1_RECV);
|
---|
64 | inb_el1(dep, EL1_XMIT);
|
---|
65 | dep->de_flags &= NOT(DEF_XMIT_BUSY);
|
---|
66 | unlock();
|
---|
67 | return; /* Done */
|
---|
68 | }
|
---|
69 |
|
---|
70 | /*
|
---|
71 | ** Name: void el1_dumpstats(dpeth_t *dep, int port, vir_bytes size)
|
---|
72 | ** Function: Dumps counter on screen (support for console display).
|
---|
73 | */
|
---|
74 | static void el1_dumpstats(dpeth_t * dep)
|
---|
75 | {
|
---|
76 |
|
---|
77 | return;
|
---|
78 | }
|
---|
79 |
|
---|
80 | /*
|
---|
81 | ** Name: void el1_mode_init(dpeth_t *dep)
|
---|
82 | ** Function: Initializes receicer mode
|
---|
83 | */
|
---|
84 | static void el1_mode_init(dpeth_t * dep)
|
---|
85 | {
|
---|
86 |
|
---|
87 | if (dep->de_flags & DEF_BROAD) {
|
---|
88 | dep->de_recv_mode = ERSR_BROAD | ERSR_RMASK;
|
---|
89 |
|
---|
90 | } else if (dep->de_flags & DEF_PROMISC) {
|
---|
91 | dep->de_recv_mode = ERSR_ALL | ERSR_RMASK;
|
---|
92 |
|
---|
93 | } else if (dep->de_flags & DEF_MULTI) {
|
---|
94 | dep->de_recv_mode = ERSR_MULTI | ERSR_RMASK;
|
---|
95 |
|
---|
96 | } else {
|
---|
97 | dep->de_recv_mode = ERSR_NONE | ERSR_RMASK;
|
---|
98 | }
|
---|
99 | outb_el1(dep, EL1_RECV, dep->de_recv_mode);
|
---|
100 | inb_el1(dep, EL1_RECV);
|
---|
101 | return;
|
---|
102 | }
|
---|
103 |
|
---|
104 | /*
|
---|
105 | ** Name: void el1_recv(dpeth_t *dep, int from, int size)
|
---|
106 | ** Function: Receive function. Called from interrupt handler to
|
---|
107 | ** unload recv. buffer or from main (packet to client)
|
---|
108 | */
|
---|
109 | static void el1_recv(dpeth_t * dep, int from, int size)
|
---|
110 | {
|
---|
111 | buff_t *rxptr;
|
---|
112 |
|
---|
113 | while ((dep->de_flags & DEF_READING) && (rxptr = dep->de_recvq_head)) {
|
---|
114 |
|
---|
115 | /* Remove buffer from queue and free buffer */
|
---|
116 | lock();
|
---|
117 | if (dep->de_recvq_tail == dep->de_recvq_head)
|
---|
118 | dep->de_recvq_head = dep->de_recvq_tail = NULL;
|
---|
119 | else
|
---|
120 | dep->de_recvq_head = rxptr->next;
|
---|
121 | unlock();
|
---|
122 |
|
---|
123 | /* Copy buffer to user area */
|
---|
124 | mem2user(dep, rxptr);
|
---|
125 |
|
---|
126 | /* Reply information */
|
---|
127 | dep->de_read_s = rxptr->size;
|
---|
128 | dep->de_flags |= DEF_ACK_RECV;
|
---|
129 | dep->de_flags &= NOT(DEF_READING);
|
---|
130 |
|
---|
131 | /* Return buffer to the idle pool */
|
---|
132 | free_buff(dep, rxptr);
|
---|
133 | }
|
---|
134 | return;
|
---|
135 | }
|
---|
136 |
|
---|
137 | /*
|
---|
138 | ** Name: void el1_send(dpeth_t *dep, int from_int, int pktsize)
|
---|
139 | ** Function: Send function. Called from main to transit a packet or
|
---|
140 | ** from interrupt handler when a new packet was queued.
|
---|
141 | */
|
---|
142 | static void el1_send(dpeth_t * dep, int from_int, int pktsize)
|
---|
143 | {
|
---|
144 | buff_t *txbuff;
|
---|
145 | clock_t now;
|
---|
146 |
|
---|
147 | if (from_int == FALSE) {
|
---|
148 |
|
---|
149 | if ((txbuff = alloc_buff(dep, pktsize + sizeof(buff_t))) != NULL) {
|
---|
150 |
|
---|
151 | /* Fill transmit buffer from user area */
|
---|
152 | txbuff->next = NULL;
|
---|
153 | txbuff->size = pktsize;
|
---|
154 | txbuff->client = dep->de_client;
|
---|
155 | user2mem(dep, txbuff);
|
---|
156 | } else
|
---|
157 | panic(dep->de_name, "out of memory for Tx", NO_NUM);
|
---|
158 |
|
---|
159 | } else if ((txbuff = dep->de_xmitq_head) != NULL) {
|
---|
160 |
|
---|
161 | /* Get first packet in queue */
|
---|
162 | lock();
|
---|
163 | if (dep->de_xmitq_tail == dep->de_xmitq_head)
|
---|
164 | dep->de_xmitq_head = dep->de_xmitq_tail = NULL;
|
---|
165 | else
|
---|
166 | dep->de_xmitq_head = txbuff->next;
|
---|
167 | unlock();
|
---|
168 | pktsize = txbuff->size;
|
---|
169 |
|
---|
170 | } else
|
---|
171 | panic(dep->de_name, "should not be sending ", NO_NUM);
|
---|
172 |
|
---|
173 | if ((dep->de_flags & DEF_XMIT_BUSY)) {
|
---|
174 | if (from_int) panic(dep->de_name, "should not be sending ", NO_NUM);
|
---|
175 | getuptime(&now);
|
---|
176 | if ((now - dep->de_xmit_start) > 4) {
|
---|
177 | /* Transmitter timed out */
|
---|
178 | DEBUG(printf("3c501: transmitter timed out ... \n"));
|
---|
179 | dep->de_stat.ets_sendErr += 1;
|
---|
180 | dep->de_flags &= NOT(DEF_XMIT_BUSY);
|
---|
181 | el1_reset(dep);
|
---|
182 | }
|
---|
183 |
|
---|
184 | /* Queue packet */
|
---|
185 | lock(); /* Queue packet to receive queue */
|
---|
186 | if (dep->de_xmitq_head == NULL)
|
---|
187 | dep->de_xmitq_head = txbuff;
|
---|
188 | else
|
---|
189 | dep->de_xmitq_tail->next = txbuff;
|
---|
190 | dep->de_xmitq_tail = txbuff;
|
---|
191 | unlock();
|
---|
192 | } else {
|
---|
193 | /* Save for retransmission */
|
---|
194 | TxBuff = txbuff;
|
---|
195 | dep->de_flags |= (DEF_XMIT_BUSY | DEF_ACK_SEND);
|
---|
196 |
|
---|
197 | /* Setup board for packet loading */
|
---|
198 | lock(); /* Buffer to processor */
|
---|
199 | outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS);
|
---|
200 | inb_el1(dep, EL1_RECV); /* Clears any spurious interrupt */
|
---|
201 | inb_el1(dep, EL1_XMIT);
|
---|
202 | outw_el1(dep, EL1_RECVPTR, 0); /* Clears RX packet area */
|
---|
203 |
|
---|
204 | /* Loads packet */
|
---|
205 | outw_el1(dep, EL1_XMITPTR, (EL1_BFRSIZ - pktsize));
|
---|
206 | outsb(dep->de_data_port, SELF, txbuff->buffer, pktsize);
|
---|
207 | /* Starts transmitter */
|
---|
208 | outw_el1(dep, EL1_XMITPTR, (EL1_BFRSIZ - pktsize));
|
---|
209 | outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_XMIT); /* There it goes... */
|
---|
210 | unlock();
|
---|
211 |
|
---|
212 | getuptime(&dep->de_xmit_start);
|
---|
213 | dep->de_flags &= NOT(DEF_SENDING);
|
---|
214 | }
|
---|
215 | return;
|
---|
216 | }
|
---|
217 |
|
---|
218 | /*
|
---|
219 | ** Name: void el1_stop(dpeth_t *dep)
|
---|
220 | ** Function: Stops board and disable interrupts.
|
---|
221 | */
|
---|
222 | static void el1_stop(dpeth_t * dep)
|
---|
223 | {
|
---|
224 | int ix;
|
---|
225 |
|
---|
226 | DEBUG(printf("%s: stopping Etherlink ....\n", dep->de_name));
|
---|
227 | for (ix = 0; ix < 8; ix += 1) /* Reset board */
|
---|
228 | outb_el1(dep, EL1_CSR, ECSR_RESET);
|
---|
229 | outb_el1(dep, EL1_CSR, ECSR_SYS);
|
---|
230 | sys_irqdisable(&dep->de_hook); /* Disable interrupt */
|
---|
231 | return;
|
---|
232 | }
|
---|
233 |
|
---|
234 | /*
|
---|
235 | ** Name: void el1_interrupt(dpeth_t *dep)
|
---|
236 | ** Function: Interrupt handler. Acknwledges transmit interrupts
|
---|
237 | ** or unloads receive buffer to memory queue.
|
---|
238 | */
|
---|
239 | static void el1_interrupt(dpeth_t * dep)
|
---|
240 | {
|
---|
241 | u16_t csr, isr;
|
---|
242 | int pktsize;
|
---|
243 | buff_t *rxptr;
|
---|
244 |
|
---|
245 | csr = inb_el1(dep, EL1_CSR);
|
---|
246 | if ((csr & ECSR_XMIT) && (dep->de_flags & DEF_XMIT_BUSY)) {
|
---|
247 |
|
---|
248 | /* Got a transmit interrupt */
|
---|
249 | isr = inb_el1(dep, EL1_XMIT);
|
---|
250 | if ((isr & (EXSR_16JAM | EXSR_UNDER | EXSR_JAM)) || !(isr & EXSR_IDLE)) {
|
---|
251 | DEBUG(printf("3c501: got xmit interrupt (ASR=0x%02X XSR=0x%02X)\n", csr, isr));
|
---|
252 | if (isr & EXSR_JAM) {
|
---|
253 | /* Sending, packet got a collision */
|
---|
254 | dep->de_stat.ets_collision += 1;
|
---|
255 | /* Put pointer back to beginning of packet */
|
---|
256 | outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS);
|
---|
257 | outw_el1(dep, EL1_XMITPTR, (EL1_BFRSIZ - TxBuff->size));
|
---|
258 | /* And retrigger transmission */
|
---|
259 | outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_XMIT);
|
---|
260 | return;
|
---|
261 |
|
---|
262 | } else if ((isr & EXSR_16JAM) || !(isr & EXSR_IDLE)) {
|
---|
263 | dep->de_stat.ets_sendErr += 1;
|
---|
264 |
|
---|
265 | } else if (isr & EXSR_UNDER) {
|
---|
266 | dep->de_stat.ets_fifoUnder += 1;
|
---|
267 | }
|
---|
268 | DEBUG(printf("3c501: got xmit interrupt (0x%02X)\n", isr));
|
---|
269 | el1_reset(dep);
|
---|
270 |
|
---|
271 | } else {
|
---|
272 | /** if (inw_el1(dep, EL1_XMITPTR) == EL1_BFRSIZ) **/
|
---|
273 | /* Packet transmitted successfully */
|
---|
274 | dep->de_stat.ets_packetT += 1;
|
---|
275 | dep->bytes_Tx += (long) (TxBuff->size);
|
---|
276 | free_buff(dep, TxBuff);
|
---|
277 | dep->de_flags &= NOT(DEF_XMIT_BUSY);
|
---|
278 | if ((dep->de_flags & DEF_SENDING) && dep->de_xmitq_head) {
|
---|
279 | /* Pending transmit request available in queue */
|
---|
280 | el1_send(dep, TRUE, 0);
|
---|
281 | if (dep->de_flags & (DEF_XMIT_BUSY | DEF_ACK_SEND))
|
---|
282 | return;
|
---|
283 | }
|
---|
284 | }
|
---|
285 |
|
---|
286 | } else if ((csr & (ECSR_RECV | ECSR_XMTBSY)) == (ECSR_RECV | ECSR_XMTBSY)) {
|
---|
287 |
|
---|
288 | /* Got a receive interrupt */
|
---|
289 | isr = inb_el1(dep, EL1_RECV);
|
---|
290 | pktsize = inw_el1(dep, EL1_RECVPTR);
|
---|
291 | if ((isr & ERSR_RERROR) || (isr & ERSR_STALE)) {
|
---|
292 | DEBUG(printf("Rx0 (ASR=0x%02X RSR=0x%02X size=%d)\n", csr, isr, pktsize));
|
---|
293 | dep->de_stat.ets_recvErr += 1;
|
---|
294 |
|
---|
295 | } else if (pktsize < ETH_MIN_PACK_SIZE || pktsize > ETH_MAX_PACK_SIZE) {
|
---|
296 | DEBUG(printf("Rx1 (ASR=0x%02X RSR=0x%02X size=%d)\n", csr, isr, pktsize));
|
---|
297 | dep->de_stat.ets_recvErr += 1;
|
---|
298 |
|
---|
299 | } else if ((rxptr = alloc_buff(dep, pktsize + sizeof(buff_t))) == NULL) {
|
---|
300 | /* Memory not available. Drop packet */
|
---|
301 | dep->de_stat.ets_fifoOver += 1;
|
---|
302 |
|
---|
303 | } else if (isr & (ERSR_GOOD | ERSR_ANY)) {
|
---|
304 | /* Got a good packet. Read it from buffer */
|
---|
305 | outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_SYS);
|
---|
306 | outw_el1(dep, EL1_XMITPTR, 0);
|
---|
307 | insb(dep->de_data_port, SELF, rxptr->buffer, pktsize);
|
---|
308 | rxptr->next = NULL;
|
---|
309 | rxptr->size = pktsize;
|
---|
310 | dep->de_stat.ets_packetR += 1;
|
---|
311 | dep->bytes_Rx += (long) pktsize;
|
---|
312 | lock(); /* Queue packet to receive queue */
|
---|
313 | if (dep->de_recvq_head == NULL)
|
---|
314 | dep->de_recvq_head = rxptr;
|
---|
315 | else
|
---|
316 | dep->de_recvq_tail->next = rxptr;
|
---|
317 | dep->de_recvq_tail = rxptr;
|
---|
318 | unlock();
|
---|
319 |
|
---|
320 | /* Reply to pending Receive requests, if any */
|
---|
321 | el1_recv(dep, TRUE, 0);
|
---|
322 | }
|
---|
323 | } else { /* Nasty condition, should never happen */
|
---|
324 | DEBUG(
|
---|
325 | printf("3c501: got interrupt with status 0x%02X\n"
|
---|
326 | " de_flags=0x%04X XSR=0x%02X RSR=0x%02X \n"
|
---|
327 | " xmit buffer = 0x%4X recv buffer = 0x%4X\n",
|
---|
328 | csr, dep->de_flags,
|
---|
329 | inb_el1(dep, EL1_RECV),
|
---|
330 | inb_el1(dep, EL1_XMIT),
|
---|
331 | inw_el1(dep, EL1_XMITPTR),
|
---|
332 | inw_el1(dep, EL1_RECVPTR))
|
---|
333 | );
|
---|
334 | el1_reset(dep);
|
---|
335 | }
|
---|
336 |
|
---|
337 | /* Move into receive mode */
|
---|
338 | outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_RECV);
|
---|
339 | outw_el1(dep, EL1_RECVPTR, 0);
|
---|
340 | /* Be sure that interrupts are cleared */
|
---|
341 | inb_el1(dep, EL1_RECV);
|
---|
342 | inb_el1(dep, EL1_XMIT);
|
---|
343 | return;
|
---|
344 | }
|
---|
345 |
|
---|
346 | /*
|
---|
347 | ** Name: void el1_init(dpeth_t *dep)
|
---|
348 | ** Function: Initalizes board hardware and driver data structures.
|
---|
349 | */
|
---|
350 | static void el1_init(dpeth_t * dep)
|
---|
351 | {
|
---|
352 | int ix;
|
---|
353 |
|
---|
354 | dep->de_irq &= NOT(DEI_DEFAULT); /* Strip the default flag. */
|
---|
355 | dep->de_offset_page = 0;
|
---|
356 | dep->de_data_port = dep->de_base_port + EL1_DATAPORT;
|
---|
357 |
|
---|
358 | el1_reset(dep); /* Reset and initialize board */
|
---|
359 |
|
---|
360 | /* Start receiver (default mode) */
|
---|
361 | outw_el1(dep, EL1_RECVPTR, 0);
|
---|
362 | outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_RECV);
|
---|
363 |
|
---|
364 | /* Initializes buffer pool */
|
---|
365 | init_buff(dep, NULL);
|
---|
366 | el1_mode_init(dep);
|
---|
367 |
|
---|
368 | printf("%s: Etherlink (%s) at %X:%d - ",
|
---|
369 | dep->de_name, "3c501", dep->de_base_port, dep->de_irq);
|
---|
370 | for (ix = 0; ix < SA_ADDR_LEN; ix += 1)
|
---|
371 | printf("%02X%c", (dep->de_address.ea_addr[ix] = StationAddress[ix]),
|
---|
372 | ix < SA_ADDR_LEN - 1 ? ':' : '\n');
|
---|
373 |
|
---|
374 | /* Device specific functions */
|
---|
375 | dep->de_recvf = el1_recv;
|
---|
376 | dep->de_sendf = el1_send;
|
---|
377 | dep->de_flagsf = el1_mode_init;
|
---|
378 | dep->de_resetf = el1_reset;
|
---|
379 | dep->de_getstatsf = el1_getstats;
|
---|
380 | dep->de_dumpstatsf = el1_dumpstats;
|
---|
381 | dep->de_interruptf = el1_interrupt;
|
---|
382 |
|
---|
383 | return; /* Done */
|
---|
384 | }
|
---|
385 |
|
---|
386 | /*
|
---|
387 | ** Name: int el1_probe(dpeth_t *dep)
|
---|
388 | ** Function: Checks for presence of the board.
|
---|
389 | */
|
---|
390 | PUBLIC int el1_probe(dpeth_t * dep)
|
---|
391 | {
|
---|
392 | int ix;
|
---|
393 |
|
---|
394 | for (ix = 0; ix < 8; ix += 1) /* Reset the board */
|
---|
395 | outb_el1(dep, EL1_CSR, ECSR_RESET);
|
---|
396 | outb_el1(dep, EL1_CSR, ECSR_SYS); /* Leaves buffer to system */
|
---|
397 |
|
---|
398 | /* Check station address */
|
---|
399 | for (ix = 0; ix < SA_ADDR_LEN; ix += 1) {
|
---|
400 | outw_el1(dep, EL1_XMITPTR, ix);
|
---|
401 | StationAddress[ix] = inb_el1(dep, EL1_SAPROM);
|
---|
402 | }
|
---|
403 | if (StationAddress[0] != 0x02 || /* Etherlink Station address */
|
---|
404 | StationAddress[1] != 0x60 || /* MUST be 02:60:8c:xx:xx:xx */
|
---|
405 | StationAddress[2] != 0x8C)
|
---|
406 | return FALSE; /* No Etherlink board at this address */
|
---|
407 |
|
---|
408 | dep->de_ramsize = 0; /* RAM size is meaningless */
|
---|
409 | dep->de_linmem = 0L; /* Access is via I/O port */
|
---|
410 |
|
---|
411 | /* Device specific functions */
|
---|
412 | dep->de_initf = el1_init;
|
---|
413 | dep->de_stopf = el1_stop;
|
---|
414 |
|
---|
415 | return TRUE; /* Etherlink board found */
|
---|
416 | }
|
---|
417 |
|
---|
418 | #endif /* ENABLE_3C501 */
|
---|
419 |
|
---|
420 | /** 3c501.c **/
|
---|