xref: /minix3/minix/drivers/net/dpeth/3c501.c (revision e4dbab1e5368dc2124168836ba46a7d3ff6414b0)
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 **/
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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