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
12 #include <minix/drivers.h>
13 #include <minix/netdriver.h>
14 #include "dp.h"
15
16 #if (ENABLE_3C501 == 1)
17
18 #include "3c501.h"
19
20 static unsigned char StationAddress[SA_ADDR_LEN] = {0, 0, 0, 0, 0, 0,};
21 static buff_t *TxBuff = NULL;
22
23 /*
24 ** Name: el1_getstats
25 ** Function: Reads statistics counters from board.
26 **/
el1_getstats(dpeth_t * dep)27 static void el1_getstats(dpeth_t * dep)
28 {
29
30 /* Nothing to do */
31 }
32
33 /*
34 ** Name: el1_reset
35 ** Function: Reset function specific for Etherlink hardware.
36 */
el1_reset(dpeth_t * dep)37 static void el1_reset(dpeth_t * dep)
38 {
39 unsigned int ix;
40
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);
44
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]);
49
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);
60 }
61
62 /*
63 ** Name: el1_dumpstats
64 ** Function: Dumps counter on screen (support for console display).
65 */
el1_dumpstats(dpeth_t * UNUSED (dep))66 static void el1_dumpstats(dpeth_t * UNUSED(dep))
67 {
68
69 }
70
71 /*
72 ** Name: el1_mode_init
73 ** Function: Initializes receicer mode
74 */
el1_mode_init(dpeth_t * dep)75 static void el1_mode_init(dpeth_t * dep)
76 {
77
78 if (dep->de_flags & DEF_BROAD) {
79 dep->de_recv_mode = ERSR_BROAD | ERSR_RMASK;
80
81 } else if (dep->de_flags & DEF_PROMISC) {
82 dep->de_recv_mode = ERSR_ALL | ERSR_RMASK;
83
84 } else if (dep->de_flags & DEF_MULTI) {
85 dep->de_recv_mode = ERSR_MULTI | ERSR_RMASK;
86
87 } else {
88 dep->de_recv_mode = ERSR_NONE | ERSR_RMASK;
89 }
90 outb_el1(dep, EL1_RECV, dep->de_recv_mode);
91 inb_el1(dep, EL1_RECV);
92 }
93
94 /*
95 ** Name: el1_recv
96 ** Function: Receive function. Called from interrupt handler to
97 ** unload recv. buffer or from main (packet to client)
98 */
el1_recv(dpeth_t * dep,struct netdriver_data * data,size_t max)99 static ssize_t el1_recv(dpeth_t *dep, struct netdriver_data *data, size_t max)
100 {
101 buff_t *rxptr;
102 size_t size;
103
104 if ((rxptr = dep->de_recvq_head) == NULL)
105 return SUSPEND;
106
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;
110 else
111 dep->de_recvq_head = rxptr->next;
112
113 /* Copy buffer to user area */
114 size = MIN((size_t)rxptr->size, max);
115
116 netdriver_copyout(data, 0, rxptr->buffer, size);
117
118 /* Return buffer to the idle pool */
119 free_buff(dep, rxptr);
120
121 return size;
122 }
123
124 /*
125 ** Name: el1_send
126 ** Function: Send function.
127 */
el1_send(dpeth_t * dep,struct netdriver_data * data,size_t size)128 static int el1_send(dpeth_t *dep, struct netdriver_data *data, size_t size)
129 {
130 buff_t *txbuff;
131 clock_t now;
132
133 if (dep->de_flags & DEF_XMIT_BUSY) {
134 now = getticks();
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. */
141 } else
142 return SUSPEND;
143 }
144
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");
148
149 /* Fill transmit buffer from user area */
150 txbuff->next = NULL;
151 txbuff->size = size;
152
153 netdriver_copyin(data, 0, txbuff->buffer, size);
154
155 /* Save for retransmission */
156 TxBuff = txbuff;
157 dep->de_flags |= DEF_XMIT_BUSY;
158
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 */
164
165 /* Loads packet */
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... */
171
172 dep->de_xmit_start = getticks();
173
174 return OK;
175 }
176
177 /*
178 ** Name: el1_stop
179 ** Function: Stops board and disable interrupts.
180 */
el1_stop(dpeth_t * dep)181 static void el1_stop(dpeth_t * dep)
182 {
183 int ix;
184
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 */
190 }
191
192 /*
193 ** Name: el1_interrupt
194 ** Function: Interrupt handler. Acknwledges transmit interrupts
195 ** or unloads receive buffer to memory queue.
196 */
el1_interrupt(dpeth_t * dep)197 static void el1_interrupt(dpeth_t * dep)
198 {
199 u16_t csr, isr;
200 int pktsize;
201 buff_t *rxptr;
202
203 csr = inb_el1(dep, EL1_CSR);
204 if ((csr & ECSR_XMIT) && (dep->de_flags & DEF_XMIT_BUSY)) {
205
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);
218 return;
219
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);
224 }
225 DEBUG(printf("3c501: got xmit interrupt (0x%02X)\n", isr));
226 el1_reset(dep);
227 } else {
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);
233 netdriver_send();
234 if (dep->de_flags & DEF_XMIT_BUSY)
235 return;
236 }
237
238 } else if ((csr & (ECSR_RECV | ECSR_XMTBSY)) == (ECSR_RECV | ECSR_XMTBSY)) {
239
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",
245 csr, isr, pktsize));
246 netdriver_stat_ierror(1);
247
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",
251 csr, isr, pktsize));
252 netdriver_stat_ierror(1);
253
254 } else if ((rxptr = alloc_buff(dep, pktsize + sizeof(buff_t))) == NULL) {
255 /* Memory not available. Drop packet */
256 netdriver_stat_ierror(1);
257
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);
263 rxptr->next = NULL;
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;
269 else
270 dep->de_recvq_tail->next = rxptr;
271 dep->de_recvq_tail = rxptr;
272
273 /* Reply to pending Receive requests, if any */
274 netdriver_recv();
275 }
276 } else { /* Nasty condition, should never happen */
277 DEBUG(
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",
281 csr, dep->de_flags,
282 inb_el1(dep, EL1_RECV),
283 inb_el1(dep, EL1_XMIT),
284 inw_el1(dep, EL1_XMITPTR),
285 inw_el1(dep, EL1_RECVPTR))
286 );
287 el1_reset(dep);
288 }
289
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);
296 }
297
298 /*
299 ** Name: el1_init
300 ** Function: Initalizes board hardware and driver data structures.
301 */
el1_init(dpeth_t * dep)302 static void el1_init(dpeth_t * dep)
303 {
304 unsigned int ix;
305
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;
309
310 el1_reset(dep); /* Reset and initialize board */
311
312 /* Start receiver (default mode) */
313 outw_el1(dep, EL1_RECVPTR, 0);
314 outb_el1(dep, EL1_CSR, ECSR_RIDE | ECSR_RECV);
315
316 /* Initializes buffer pool */
317 init_buff(dep, NULL);
318 el1_mode_init(dep);
319
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');
325
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;
334 }
335
336 /*
337 ** Name: el1_probe
338 ** Function: Checks for presence of the board.
339 */
el1_probe(dpeth_t * dep)340 int el1_probe(dpeth_t * dep)
341 {
342 unsigned int ix;
343
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 */
347
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);
352 }
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 */
357
358 dep->de_ramsize = 0; /* RAM size is meaningless */
359 dep->de_linmem = 0L; /* Access is via I/O port */
360
361 /* Device specific functions */
362 dep->de_initf = el1_init;
363 dep->de_stopf = el1_stop;
364
365 return TRUE; /* Etherlink board found */
366 }
367
368 #endif /* ENABLE_3C501 */
369
370 /** 3c501.c **/
371