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