xref: /csrg-svn/sys/vax/if/if_en.c (revision 5160)
1 /*	if_en.c	4.16	81/12/02	*/
2 
3 #include "en.h"
4 
5 /*
6  * Xerox prototype (3 Mb) Ethernet interface driver.
7  */
8 
9 #include "../h/param.h"
10 #include "../h/systm.h"
11 #include "../h/mbuf.h"
12 #include "../h/pte.h"
13 #include "../h/buf.h"
14 #include "../h/protosw.h"
15 #include "../h/socket.h"
16 #include "../h/ubareg.h"
17 #include "../h/ubavar.h"
18 #include "../h/enreg.h"
19 #include "../h/cpu.h"
20 #include "../h/mtpr.h"
21 #include "../h/vmmac.h"
22 #include "../net/in.h"
23 #include "../net/in_systm.h"
24 #include "../net/if.h"
25 #include "../net/if_en.h"
26 #include "../net/if_uba.h"
27 #include "../net/ip.h"
28 #include "../net/ip_var.h"
29 
30 #define	ENMTU	1024
31 
32 int	enprobe(), enattach(), enrint(), enxint(), encollide();
33 struct	uba_device *eninfo[NEN];
34 u_short enstd[] = { 0 };
35 struct	uba_driver endriver =
36 	{ enprobe, 0, enattach, 0, enstd, "es", eninfo };
37 #define	ENUNIT(x)	minor(x)
38 
39 int	eninit(),enoutput(),enreset();
40 
41 /*
42  * Ethernet software status per interface.
43  *
44  * Each interface is referenced by a network interface structure,
45  * es_if, which the routing code uses to locate the interface.
46  * This structure contains the output queue for the interface, its address, ...
47  * We also have, for each interface, a UBA interface structure, which
48  * contains information about the UNIBUS resources held by the interface:
49  * map registers, buffered data paths, etc.  Information is cached in this
50  * structure for use by the if_uba.c routines in running the interface
51  * efficiently.
52  */
53 struct	en_softc {
54 	struct	ifnet es_if;		/* network-visible interface */
55 	struct	ifuba es_ifuba;		/* UNIBUS resources */
56 	short	es_delay;		/* current output delay */
57 	short	es_mask;		/* mask for current output delay */
58 	u_char	es_lastx;		/* host last transmitted to */
59 	short	es_oactive;		/* is output active? */
60 	short	es_olen;		/* length of last output */
61 } en_softc[NEN];
62 
63 /*
64  * Do output DMA to determine interface presence and
65  * interrupt vector.  DMA is too short to disturb other hosts.
66  */
67 enprobe(reg)
68 	caddr_t reg;
69 {
70 	register int br, cvec;
71 	register struct endevice *addr = (struct endevice *)reg;
72 
73 COUNT(ENPROBE);
74 #ifdef lint
75 	br = 0; cvec = br; br = cvec;
76 	enrint(0); enxint(0); encollide(0);
77 #endif
78 	addr->en_istat = 0;
79 	addr->en_owc = -1;
80 	addr->en_oba = 0;
81 	addr->en_ostat = EN_IEN|EN_GO;
82 	DELAY(100000);
83 	addr->en_ostat = 0;
84 	return (1);
85 }
86 
87 /*
88  * Interface exists: make available by filling in network interface
89  * record.  System will initialize the interface when it is ready
90  * to accept packets.
91  */
92 enattach(ui)
93 	struct uba_device *ui;
94 {
95 	register struct en_softc *es = &en_softc[ui->ui_unit];
96 COUNT(ENATTACH);
97 
98 	es->es_if.if_unit = ui->ui_unit;
99 	es->es_if.if_mtu = ENMTU;
100 	es->es_if.if_net = ui->ui_flags;
101 	es->es_if.if_host[0] =
102 	    ~(((struct endevice *)eninfo[ui->ui_unit])->en_addr) & 0xff;
103 	es->es_if.if_addr =
104 	    if_makeaddr(es->es_if.if_net, es->es_if.if_host[0]);
105 	es->es_if.if_output = enoutput;
106 	es->es_if.if_init = eninit;
107 	es->es_if.if_ubareset = enreset;
108 	if_attach(&es->es_if);
109 }
110 
111 /*
112  * Reset of interface after UNIBUS reset.
113  * If interface is on specified uba, reset its state.
114  */
115 enreset(unit, uban)
116 	int unit, uban;
117 {
118 	register struct uba_device *ui;
119 COUNT(ENRESET);
120 
121 	if (unit >= NEN || (ui = eninfo[unit]) == 0 || ui->ui_alive == 0) {
122 		printf("es%d: not alive\n", unit);
123 		return;
124 	}
125 	if (ui->ui_ubanum != uban)
126 		return;
127 	eninit(unit);
128 }
129 
130 /*
131  * Initialization of interface; clear recorded pending
132  * operations, and reinitialize UNIBUS usage.
133  */
134 eninit(unit)
135 	int unit;
136 {
137 	register struct uba_device *ui;
138 	register struct endevice *addr;
139 	register struct en_softc *es;
140 	int s;
141 
142 	es = &en_softc[unit];
143 	ui = eninfo[unit];
144 	if (if_ubainit(&es->es_ifuba, ui->ui_ubanum,
145 	    sizeof (struct en_header), (int)btop(ENMTU)) == 0) {
146 		printf("es%d: can't initialize\n", unit);
147 		return;
148 	}
149 	addr = (struct endevice *)ui->ui_addr;
150 	addr->en_istat = addr->en_ostat = 0;
151 
152 	/*
153 	 * Hang pending read, start any writes.
154 	 */
155 	s = splimp();
156 	enstart(unit);
157 	enxint(unit);
158 	splx(s);
159 }
160 
161 int	enlastdel = 25;
162 
163 /*
164  * Start or restart output on interface.
165  * If interface is already active, then this is a retransmit
166  * after a collision, and just restuff registers and delay.
167  * If interface is not already active, get another datagram
168  * to send off of the interface queue, and map it to the interface
169  * before starting the output.
170  */
171 enstart(dev)
172 	dev_t dev;
173 {
174         int unit;
175 	struct uba_device *ui;
176 	register struct endevice *addr;
177 	register struct en_softc *es;
178 	struct mbuf *m;
179 	int dest;
180 COUNT(ENSTART);
181 
182 	unit = ENUNIT(dev);
183 	ui = eninfo[unit];
184 	es = &en_softc[unit];
185 	if (es->es_oactive)
186 		goto restart;
187 
188 	/*
189 	 * Not already active: dequeue another request
190 	 * and map it to the UNIBUS.  If no more requests,
191 	 * just return.
192 	 */
193 	IF_DEQUEUE(&es->es_if.if_snd, m);
194 	if (m == 0) {
195 		es->es_oactive = 0;
196 		return;
197 	}
198 	es->es_olen = if_wubaput(&es->es_ifuba, m);
199 
200 	/*
201 	 * Ethernet cannot take back-to-back packets (no
202 	 * buffering in interface.  To avoid overrunning
203 	 * receiver, enforce a small delay (about 1ms) in interface
204 	 * on successive packets sent to same host.
205 	 */
206 	dest = mtod(m, struct en_header *)->en_dhost;
207 	if (es->es_lastx && es->es_lastx == dest)
208 		es->es_delay = enlastdel;
209 	else
210 		es->es_lastx = dest;
211 
212 restart:
213 	/*
214 	 * Have request mapped to UNIBUS for transmission.
215 	 * Purge any stale data from this BDP, and start the otput.
216 	 */
217 	UBAPURGE(es->es_ifuba.ifu_uba, es->es_ifuba.ifu_w.ifrw_bdp);
218 	addr = (struct endevice *)ui->ui_addr;
219 	addr->en_oba = (int)es->es_ifuba.ifu_w.ifrw_info;
220 	addr->en_odelay = es->es_delay;
221 	addr->en_owc = -((es->es_olen + 1) >> 1);
222 	addr->en_ostat = EN_IEN|EN_GO;
223 	es->es_oactive = 1;
224 }
225 
226 /*
227  * Ethernet interface transmitter interrupt.
228  * Start another output if more data to send.
229  */
230 enxint(unit)
231 	int unit;
232 {
233 	register struct endevice *addr;
234 	register struct uba_device *ui;
235 	register struct en_softc *es;
236 COUNT(ENXINT);
237 
238 	ui = eninfo[unit];
239 	es = &en_softc[unit];
240 	if (es->es_oactive == 0)
241 		return;
242 	addr = (struct endevice *)ui->ui_addr;
243 	es = &en_softc[unit];
244 	es->es_oactive = 0;
245 	es->es_delay = 0;
246 	es->es_mask = ~0;
247 	if (addr->en_ostat&EN_OERROR)
248 		printf("es%d: output error\n", unit);
249 	if (es->es_if.if_snd.ifq_head == 0) {
250 		es->es_lastx = 0;
251 		return;
252 	}
253 	enstart(unit);
254 }
255 
256 /*
257  * Collision on ethernet interface.  Do exponential
258  * backoff, and retransmit.  If have backed off all
259  * the way printing warning diagnostic, and drop packet.
260  */
261 encollide(unit)
262 	int unit;
263 {
264 	register struct en_softc *es;
265 COUNT(ENCOLLIDE);
266 
267 	es = &en_softc[unit];
268 	es->es_if.if_collisions++;
269 	if (es->es_oactive == 0)
270 		return;
271 	if (es->es_mask == 0) {
272 		printf("es%d: send error\n", unit);
273 		enxint(unit);
274 	} else {
275 		es->es_mask <<= 1;
276 		es->es_delay = mfpr(ICR) &~ es->es_mask;
277 		enstart(unit);
278 	}
279 }
280 
281 /*
282  * Ethernet interface receiver interrupt.
283  * If input error just drop packet.
284  * Otherwise purge input buffered data path and examine
285  * packet to determine type.  If can't determine length
286  * from type, then have to drop packet.  Othewise decapsulate
287  * packet based on type and pass to type specific higher-level
288  * input routine.
289  */
290 enrint(unit)
291 	int unit;
292 {
293 	struct endevice *addr;
294 	register struct en_softc *es;
295 	struct en_header *en;
296     	struct mbuf *m;
297 	struct ifqueue *inq;
298 	register int len;
299 	int off;
300 COUNT(ENRINT);
301 
302 	es = &en_softc[unit];
303 	addr = (struct endevice *)eninfo[unit]->ui_addr;
304 
305 	/*
306 	 * Purge BDP; drop error packets.
307 	 */
308 	UBAPURGE(es->es_ifuba.ifu_uba, es->es_ifuba.ifu_r.ifrw_bdp);
309 	if (addr->en_istat&EN_IERROR) {
310 		es->es_if.if_ierrors++;
311 		printf("es%d: input error\n", unit);
312 		goto setup;
313 	}
314 
315 	/*
316 	 * Get pointer to ethernet header (in input buffer).
317 	 * Deal with trailer protocol: if type is PUP trailer
318 	 * get true type from first 16-bit word past data.
319 	 * Remember that type was trailer by setting off.
320 	 */
321 	en = (struct en_header *)(es->es_ifuba.ifu_r.ifrw_addr);
322 #define	endataaddr(en, off, type)	((type)(((caddr_t)((en)+1)+(off))))
323 	if (en->en_type >= ENPUP_TRAIL &&
324 	    en->en_type < ENPUP_TRAIL+ENPUP_NTRAILER) {
325 		off = (en->en_type - ENPUP_TRAIL) * 512;
326 		en->en_type = *endataaddr(en, off, u_short *);
327 		off += 2;
328 	} else
329 		off = 0;
330 
331 	/*
332 	 * Attempt to infer packet length from type;
333 	 * can't deal with packet if can't infer length.
334 	 */
335 	switch (en->en_type) {
336 
337 #ifdef INET
338 	case ENPUP_IPTYPE:
339 		len = endataaddr(en, off, struct ip *)->ip_len;
340 		setipintr();
341 		inq = &ipintrq;
342 		break;
343 #endif
344 
345 	default:
346 		printf("en%d: unknow pkt type 0x%x\n", en->en_type);
347 		goto setup;
348 	}
349 	if (len == 0)
350 		goto setup;
351 
352 	/*
353 	 * Pull packet off interface.  Off is nonzero if packet
354 	 * has trailing header; if_rubaget will then force this header
355 	 * information to be at the front, but we still have to drop
356 	 * the two-byte type which is at the front of the trailer data
357 	 * (which we ``consumed'' above).
358 	 */
359 	m = if_rubaget(&es->es_ifuba, len, off);
360 	if (off) {
361 		m->m_off += 2;
362 		m->m_len -= 2;
363 	}
364 	IF_ENQUEUE(inq, m);
365 
366 setup:
367 	/*
368 	 * Reset for next packet.
369 	 */
370 	addr->en_iba = es->es_ifuba.ifu_r.ifrw_info;
371 	addr->en_iwc = -(sizeof (struct en_header) + ENMTU) >> 1;
372 	addr->en_istat = EN_IEN|EN_GO;
373 }
374 
375 /*
376  * Ethernet output routine.
377  * Encapsulate a packet of type family for the local net.
378  * Use trailer local net encapsulation if enough data in first
379  * packet leaves a multiple of 512 bytes of data in remainder.
380  */
381 enoutput(ifp, m0, pf)
382 	struct ifnet *ifp;
383 	struct mbuf *m0;
384 	int pf;
385 {
386 	int type, dest;
387 	register struct mbuf *m = m0;
388 	register struct en_header *en;
389 	int s;
390 
391 	switch (pf) {
392 
393 #ifdef INET
394 	case PF_INET: {
395 		register struct ip *ip = mtod(m0, struct ip *);
396 		int off;
397 
398 		dest = ip->ip_dst.s_addr >> 24;
399 		off = ip->ip_len - m->m_len;
400 		if (off && off % 512 == 0 && m->m_off >= MMINOFF + 2) {
401 			type = ENPUP_TRAIL + (off>>9);
402 			m->m_off -= 2;
403 			m->m_len += 2;
404 			*mtod(m, u_short *) = ENPUP_IPTYPE;
405 			goto gottrailertype;
406 		}
407 		type = ENPUP_IPTYPE;
408 		off = 0;
409 		goto gottype;
410 		}
411 #endif
412 
413 	default:
414 		printf("en%d: can't encapsulate pf%d\n", ifp->if_unit, pf);
415 		m_freem(m0);
416 		return (0);
417 	}
418 
419 	/*
420 	 * Packet to be sent as trailer: move first packet
421 	 * (control information) to end of chain.
422 	 */
423 gottrailertype:
424 	while (m->m_next)
425 		m = m->m_next;
426 	m->m_next = m0;
427 	m = m0->m_next;
428 	m0->m_next = 0;
429 
430 	/*
431 	 * Add local net header.  If no space in first mbuf,
432 	 * allocate another.
433 	 */
434 gottype:
435 	m0 = m;
436 	if (MMINOFF + sizeof (struct en_header) > m->m_off) {
437 		m = m_get(0);
438 		if (m == 0) {
439 			m_freem(m0);
440 			return (0);
441 		}
442 		m->m_next = m0;
443 		m->m_off = MMINOFF;
444 		m->m_len = sizeof (struct en_header);
445 	} else {
446 		m = m0;
447 		m->m_off -= sizeof (struct en_header);
448 		m->m_len += sizeof (struct en_header);
449 	}
450 	en = mtod(m, struct en_header *);
451 	en->en_shost = ifp->if_host[0];
452 	en->en_dhost = dest;
453 	en->en_type = type;
454 
455 	/*
456 	 * Queue message on interface, and start output if interface
457 	 * not yet active.
458 	 */
459 	s = splimp();
460 	IF_ENQUEUE(&ifp->if_snd, m);
461 	splx(s);
462 	if (en_softc[ifp->if_unit].es_oactive == 0)
463 		enstart(ifp->if_unit);
464 	return (1);
465 }
466