xref: /csrg-svn/sys/vax/if/if_en.c (revision 5105)
1 /*	if_en.c	4.15	81/11/29	*/
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 }
109 
110 /*
111  * Reset of interface after UNIBUS reset.
112  * If interface is on specified uba, reset its state.
113  */
114 enreset(unit, uban)
115 	int unit, uban;
116 {
117 	register struct uba_device *ui;
118 COUNT(ENRESET);
119 
120 	if (unit >= NEN || (ui = eninfo[unit]) == 0 || ui->ui_alive == 0) {
121 		printf("es%d: not alive\n", unit);
122 		return;
123 	}
124 	if (ui->ui_ubanum != uban)
125 		return;
126 	eninit(unit);
127 }
128 
129 /*
130  * Initialization of interface; clear recorded pending
131  * operations, and reinitialize UNIBUS usage.
132  */
133 eninit(unit)
134 	int unit;
135 {
136 	register struct uba_device *ui;
137 	register struct endevice *addr;
138 	register struct en_softc *es;
139 	int s;
140 
141 	es = &en_softc[unit];
142 	ui = eninfo[unit];
143 	if (if_ubainit(&es->es_ifuba, ui->ui_ubanum,
144 	    sizeof (struct en_header), (int)btop(ENMTU)) == 0) {
145 		printf("es%d: can't initialize\n", unit);
146 		return;
147 	}
148 	addr = (struct endevice *)ui->ui_addr;
149 	addr->en_istat = addr->en_ostat = 0;
150 
151 	/*
152 	 * Hang pending read, start any writes.
153 	 */
154 	s = splimp();
155 	enstart(unit);
156 	enxint(unit);
157 	splx(s);
158 }
159 
160 int	enlastdel = 25;
161 
162 /*
163  * Start or restart output on interface.
164  * If interface is already active, then this is a retransmit
165  * after a collision, and just restuff registers and delay.
166  * If interface is not already active, get another datagram
167  * to send off of the interface queue, and map it to the interface
168  * before starting the output.
169  */
170 enstart(dev)
171 	dev_t dev;
172 {
173         int unit;
174 	struct uba_device *ui;
175 	register struct endevice *addr;
176 	register struct en_softc *es;
177 	struct mbuf *m;
178 	int dest;
179 COUNT(ENSTART);
180 
181 	unit = ENUNIT(dev);
182 	ui = eninfo[unit];
183 	es = &en_softc[unit];
184 	if (es->es_oactive)
185 		goto restart;
186 
187 	/*
188 	 * Not already active: dequeue another request
189 	 * and map it to the UNIBUS.  If no more requests,
190 	 * just return.
191 	 */
192 	IF_DEQUEUE(&es->es_if.if_snd, m);
193 	if (m == 0) {
194 		es->es_oactive = 0;
195 		return;
196 	}
197 	es->es_olen = if_wubaput(&es->es_ifuba, m);
198 
199 	/*
200 	 * Ethernet cannot take back-to-back packets (no
201 	 * buffering in interface.  To avoid overrunning
202 	 * receiver, enforce a small delay (about 1ms) in interface
203 	 * on successive packets sent to same host.
204 	 */
205 	dest = mtod(m, struct en_header *)->en_dhost;
206 	if (es->es_lastx && es->es_lastx == dest)
207 		es->es_delay = enlastdel;
208 	else
209 		es->es_lastx = dest;
210 
211 restart:
212 	/*
213 	 * Have request mapped to UNIBUS for transmission.
214 	 * Purge any stale data from this BDP, and start the otput.
215 	 */
216 	UBAPURGE(es->es_ifuba.ifu_uba, es->es_ifuba.ifu_w.ifrw_bdp);
217 	addr = (struct endevice *)ui->ui_addr;
218 	addr->en_oba = (int)es->es_ifuba.ifu_w.ifrw_addr;
219 	addr->en_odelay = es->es_delay;
220 	addr->en_owc = -((es->es_olen + 1) >> 1);
221 	addr->en_ostat = EN_IEN|EN_GO;
222 	es->es_oactive = 1;
223 }
224 
225 /*
226  * Ethernet interface transmitter interrupt.
227  * Start another output if more data to send.
228  */
229 enxint(unit)
230 	int unit;
231 {
232 	register struct endevice *addr;
233 	register struct uba_device *ui;
234 	register struct en_softc *es;
235 COUNT(ENXINT);
236 
237 	ui = eninfo[unit];
238 	es = &en_softc[unit];
239 	if (es->es_oactive == 0)
240 		return;
241 	addr = (struct endevice *)ui->ui_addr;
242 	es = &en_softc[unit];
243 	es->es_oactive = 0;
244 	es->es_delay = 0;
245 	es->es_mask = ~0;
246 	if (addr->en_ostat&EN_OERROR)
247 		printf("es%d: output error\n", unit);
248 	if (es->es_if.if_snd.ifq_head == 0) {
249 		es->es_lastx = 0;
250 		return;
251 	}
252 	enstart(unit);
253 }
254 
255 /*
256  * Collision on ethernet interface.  Do exponential
257  * backoff, and retransmit.  If have backed off all
258  * the way printing warning diagnostic, and drop packet.
259  */
260 encollide(unit)
261 	int unit;
262 {
263 	register struct en_softc *es;
264 COUNT(ENCOLLIDE);
265 
266 	es = &en_softc[unit];
267 	es->es_if.if_collisions++;
268 	if (es->es_oactive == 0)
269 		return;
270 	if (es->es_mask == 0) {
271 		printf("es%d: send error\n", unit);
272 		enxint(unit);
273 	} else {
274 		es->es_mask <<= 1;
275 		es->es_delay = mfpr(ICR) &~ es->es_mask;
276 		enstart(unit);
277 	}
278 }
279 
280 /*
281  * Ethernet interface receiver interrupt.
282  * If input error just drop packet.
283  * Otherwise purge input buffered data path and examine
284  * packet to determine type.  If can't determine length
285  * from type, then have to drop packet.  Othewise decapsulate
286  * packet based on type and pass to type specific higher-level
287  * input routine.
288  */
289 enrint(unit)
290 	int unit;
291 {
292 	struct endevice *addr;
293 	register struct en_softc *es;
294 	struct en_header *en;
295     	struct mbuf *m;
296 	struct ifqueue *inq;
297 	register int len;
298 	int off;
299 COUNT(ENRINT);
300 
301 	es = &en_softc[unit];
302 	addr = (struct endevice *)eninfo[unit]->ui_addr;
303 
304 	/*
305 	 * Purge BDP; drop error packets.
306 	 */
307 	UBAPURGE(es->es_ifuba.ifu_uba, es->es_ifuba.ifu_r.ifrw_bdp);
308 	if (addr->en_istat&EN_IERROR) {
309 		es->es_if.if_ierrors++;
310 		printf("es%d: input error\n", unit);
311 		goto setup;
312 	}
313 
314 	/*
315 	 * Get pointer to ethernet header (in input buffer).
316 	 * Deal with trailer protocol: if type is PUP trailer
317 	 * get true type from first 16-bit word past data.
318 	 * Remember that type was trailer by setting off.
319 	 */
320 	en = (struct en_header *)(es->es_ifuba.ifu_r.ifrw_addr);
321 #define	endataaddr(en, off, type)	((type)(((caddr_t)((en)+1)+(off))))
322 	if (en->en_type >= ENPUP_TRAIL &&
323 	    en->en_type < ENPUP_TRAIL+ENPUP_NTRAILER) {
324 		off = (en->en_type - ENPUP_TRAIL) * 512;
325 		en->en_type = *endataaddr(en, off, u_short *);
326 		off += 2;
327 	} else
328 		off = 0;
329 
330 	/*
331 	 * Attempt to infer packet length from type;
332 	 * can't deal with packet if can't infer length.
333 	 */
334 	switch (en->en_type) {
335 
336 #ifdef INET
337 	case ENPUP_IPTYPE:
338 		len = endataaddr(en, off, struct ip *)->ip_len;
339 		setipintr();
340 		inq = &ipintrq;
341 		break;
342 #endif
343 
344 	default:
345 		printf("en%d: unknow pkt type 0x%x\n", en->en_type);
346 		goto setup;
347 	}
348 	if (len == 0)
349 		goto setup;
350 
351 	/*
352 	 * Pull packet off interface.  Off is nonzero if packet
353 	 * has trailing header; if_rubaget will then force this header
354 	 * information to be at the front, but we still have to drop
355 	 * the two-byte type which is at the front of the trailer data
356 	 * (which we ``consumed'' above).
357 	 */
358 	m = if_rubaget(&es->es_ifuba, len, off);
359 	if (off) {
360 		m->m_off += 2;
361 		m->m_len -= 2;
362 	}
363 	IF_ENQUEUE(inq, m);
364 
365 setup:
366 	/*
367 	 * Reset for next packet.
368 	 */
369 	addr->en_iba = es->es_ifuba.ifu_r.ifrw_info;
370 	addr->en_iwc = -(sizeof (struct en_header) + ENMTU) >> 1;
371 	addr->en_istat = EN_IEN|EN_GO;
372 }
373 
374 /*
375  * Ethernet output routine.
376  * Encapsulate a packet of type family for the local net.
377  * Use trailer local net encapsulation if enough data in first
378  * packet leaves a multiple of 512 bytes of data in remainder.
379  */
380 enoutput(ifp, m0, pf)
381 	struct ifnet *ifp;
382 	struct mbuf *m0;
383 	int pf;
384 {
385 	int type, dest;
386 	register struct mbuf *m = m0;
387 	register struct en_header *en;
388 	int s;
389 
390 	switch (pf) {
391 
392 #ifdef INET
393 	case PF_INET: {
394 		register struct ip *ip = mtod(m0, struct ip *);
395 		int off;
396 
397 		dest = ip->ip_dst.s_addr >> 24;
398 		off = ip->ip_len - m->m_len;
399 		if (off && off % 512 == 0 && m->m_off >= MMINOFF + 2) {
400 			type = ENPUP_TRAIL + (off>>9);
401 			m->m_off -= 2;
402 			m->m_len += 2;
403 			*mtod(m, u_short *) = ENPUP_IPTYPE;
404 			goto gottrailertype;
405 		}
406 		type = ENPUP_IPTYPE;
407 		off = 0;
408 		goto gottype;
409 		}
410 #endif
411 
412 	default:
413 		printf("en%d: can't encapsulate pf%d\n", ifp->if_unit, pf);
414 		m_freem(m0);
415 		return (0);
416 	}
417 
418 	/*
419 	 * Packet to be sent as trailer: move first packet
420 	 * (control information) to end of chain.
421 	 */
422 gottrailertype:
423 	while (m->m_next)
424 		m = m->m_next;
425 	m->m_next = m0;
426 	m = m0->m_next;
427 	m0->m_next = 0;
428 
429 	/*
430 	 * Add local net header.  If no space in first mbuf,
431 	 * allocate another.
432 	 */
433 gottype:
434 	m0 = m;
435 	if (MMINOFF + sizeof (struct en_header) > m->m_off) {
436 		m = m_get(0);
437 		if (m == 0) {
438 			m_freem(m0);
439 			return (0);
440 		}
441 		m->m_next = m0;
442 		m->m_off = MMINOFF;
443 		m->m_len = sizeof (struct en_header);
444 	} else {
445 		m = m0;
446 		m->m_off -= sizeof (struct en_header);
447 		m->m_len += sizeof (struct en_header);
448 	}
449 	en = mtod(m, struct en_header *);
450 	en->en_shost = ifp->if_host[0];
451 	en->en_dhost = dest;
452 	en->en_type = type;
453 
454 	/*
455 	 * Queue message on interface, and start output if interface
456 	 * not yet active.
457 	 */
458 	s = splimp();
459 	IF_ENQUEUE(&ifp->if_snd, m);
460 	splx(s);
461 	if (en_softc[ifp->if_unit].es_oactive == 0)
462 		enstart(ifp->if_unit);
463 	return (1);
464 }
465