|
|
1.1 root 1: /*
2: * This file is derived from a number of files, as denoted below,
3: * to create an Excelan driver compatible with the 4.3BSD-tahoe release.
4: */
5:
6: /*
7: * Copyright (c) 1989 The Regents of the University of California.
8: * All rights reserved.
9: *
10: * This code is derived from software contributed to Berkeley by
11: * Excelan Inc.
12: *
13: * Redistribution and use in source and binary forms are permitted
14: * provided that the above copyright notice and this paragraph are
15: * duplicated in all such forms and that any documentation,
16: * advertising materials, and other materials related to such
17: * distribution and use acknowledge that the software was developed
18: * by the University of California, Berkeley. The name of the
19: * University may not be used to endorse or promote products derived
20: * from this software without specific prior written permission.
21: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
22: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
23: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
24: *
25: * @(#)if_ex.c 7.2 (Berkeley) 4/22/89
26: */
27:
28: #include "ex.h"
29:
30: #if NEX > 0
31:
32: /*
33: * Excelan EXOS 202(VME) & 203(QBUS) Link Level Ethernet Interface Drivers
34: */
35: #include "param.h"
36: #include "systm.h"
37: #include "mbuf.h"
38: #include "malloc.h"
39: #include "buf.h"
40: #include "protosw.h"
41: #include "socket.h"
42: #include "vmmac.h"
43: #include "ioctl.h"
44: #include "errno.h"
45: #include "vmparam.h"
46: #include "syslog.h"
47: #include "uio.h"
48:
49: #include "../net/if.h"
50: #include "../net/netisr.h"
51: #include "../net/route.h"
52:
53: #ifdef INET
54: #include "../netinet/in.h"
55: #include "../netinet/in_systm.h"
56: #include "../netinet/in_var.h"
57: #include "../netinet/ip.h"
58: #include "../netinet/if_ether.h"
59: #endif
60:
61: #ifdef NS
62: #include "../netns/ns.h"
63: #include "../netns/ns_if.h"
64: #endif
65:
66: #include "../tahoe/cpu.h"
67: #include "../tahoe/pte.h"
68: #include "../tahoe/mtpr.h"
69:
70: #include "../tahoevba/vbavar.h"
71: #include "if_exreg.h"
72: /*
73: * Copyright (c) 1989 The Regents of the University of California.
74: * All rights reserved.
75: *
76: * Redistribution and use in source and binary forms are permitted
77: * provided that the above copyright notice and this paragraph are
78: * duplicated in all such forms and that any documentation,
79: * advertising materials, and other materials related to such
80: * distribution and use acknowledge that the software was developed
81: * by the University of California, Berkeley. The name of the
82: * University may not be used to endorse or promote products derived
83: * from this software without specific prior written permission.
84: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
85: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
86: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
87: *
88: * @(#)if_vba.h 1.1 (Berkeley) 3/9/89
89: */
90:
91: struct ifvba {
92: struct mbuf *iff_mbuf; /* associated mbuf to free */
93: caddr_t iff_buffer; /* contiguous memory for data, kernel address */
94: u_long iff_physaddr; /* contiguous memory for data, phys address */
95: };
96:
97:
98: #ifdef KERNEL
99: struct mbuf *if_vbaget();
100: #endif
101:
102:
103:
104: #define NH2X 32 /* Host to eXcelan request buffers */
105:
106: #define NX2H 16 /* eXcelan to Host reply buffers */
107: #define NREC 16 /* Number of RECeive buffers */
108: #define NTRB 4 /* Number of TRansmit Buffers */
109: #define NVBI (NREC + NTRB)
110:
111: #define EXWATCHINTVL 10 /* call exwatch every x secs */
112:
113: int exprobe(), exslave(), exattach(), exintr(), exstart();
114: struct vba_device *exinfo[NEX];
115:
116: long exstd[] = { 0 };
117:
118:
119: struct vba_driver exdriver =
120: { exprobe, 0, exattach, exstart, exstd, "ex", exinfo };
121: int exinit(),exoutput(),exioctl(),exreset(),exwatch();
122: struct ex_msg *exgetcbuf();
123: int ex_ncall = 0; /* counts calls to exprobe */
124: u_long busoff;
125:
126: /*
127: * Ethernet software status per interface.
128: *
129: * Each interface is referenced by a network interface structure, xs_if, which
130: * the routing code uses to locate the interface. This structure contains the
131: * output queue for the interface, its address, ... NOTE: To configure multiple
132: * controllers, the sizeof this structure must be a multiple of 16 (xs_h2xhdr).
133: */
134: struct ex_softc {
135: struct arpcom xs_ac; /* Ethernet common part */
136: #define xs_if xs_ac.ac_if /* network-visible interface */
137: #define xs_addr xs_ac.ac_enaddr /* hardware Ethernet address */
138: int xs_flags; /* private flags */
139: #define EX_XPENDING 1 /* xmit rqst pending on EXOS */
140: #define EX_STATPENDING (1<<1) /* stats rqst pending on EXOS */
141: #define EX_RUNNING (1<<2) /* board is running */
142: #define EX_SETADDR (1<<3) /* physaddr has been changed */
143: int xs_cvec; /* probe stores cvec here */
144: short xs_enetunit; /* unit number for enet filtering */
145: short xs_enetinit; /* enet inetrface is initialized */
146: struct ex_msg *xs_h2xnext; /* host pointer to request queue */
147: struct ex_msg *xs_x2hnext; /* host pointer to reply queue */
148: u_long xs_qbaddr; /* map info for structs below */
149: struct ex_shm {
150: /* the following structures are always mapped in */
151: u_short sm_h2xhdr; /* EXOS's request queue header */
152: u_short sm_x2hhdr; /* EXOS's reply queue header */
153: struct ex_msg sm_h2xent[NH2X];/* request msg buffers */
154: struct ex_msg sm_x2hent[NX2H];/* reply msg buffers */
155: struct ex_conf sm_cm; /* configuration message */
156: struct ex_stat sm_xsa; /* EXOS writes stats here */
157: /* end mapped area */
158: } *xs_shm; /* host pointer to shared area */
159: #define xs_h2xhdr xs_shm->sm_h2xhdr
160: #define xs_x2hhdr xs_shm->sm_x2hhdr
161: #define xs_h2xent xs_shm->sm_h2xent
162: #define xs_x2hent xs_shm->sm_x2hent
163: #define xs_cm xs_shm->sm_cm
164: #define xs_xsa xs_shm->sm_xsa
165: #define BUSADDR(x) (0x3D000000 | (((u_long)kvtophys(x))&0xFFFFFF))
166: #define P_BUSADDR(x) (0x3D000000 | (((u_long)kvtophys(x))&0xFFFFF0))
167: #define INCORE_BASE(p) (((u_long)(p)->xs_shm) & 0xFFFFFFF0)
168: /* we will arrange that the shared memory begins on a 16 byte boundary */
169: #define RVAL_OFF(n) (((char *)&(((struct ex_shm *)0)->n))-(char *)0)
170: #define LVAL_OFF(n) (((char *)(((struct ex_shm *)0)->n))-(char *)0)
171: #define H2XHDR_OFFSET RVAL_OFF(sm_h2xhdr)
172: #define X2HHDR_OFFSET RVAL_OFF(sm_x2hhdr)
173: #define H2XENT_OFFSET LVAL_OFF(sm_h2xent)
174: #define X2HENT_OFFSET LVAL_OFF(sm_x2hent)
175: #define CM_OFFSET RVAL_OFF(sm_cm)
176: #define SA_OFFSET RVAL_OFF(sm_xsa)
177: struct ifvba xs_vbinfo[NVBI];/* Bus Resources (low core) */
178: struct ifvba *xs_pkblist; /* free list of above */
179: #define GetPkBuf(b, v) ((v = (b)->mb_pkb = xs->xs_pkblist),\
180: (xs->xs_pkblist = (struct ifvba *)(v)->iff_mbuf))
181: #define FreePkBuf(v) (((v)->iff_mbuf = (struct mbuf *)xs->xs_pkblist),\
182: (xs->xs_pkblist = v))
183: char xs_nrec; /* number of pending receive buffers */
184: char xs_ntrb; /* number of pending transmit buffers */
185: } ex_softc[NEX];
186:
187: int ex_padcheck = sizeof (struct ex_softc);
188:
189: exprobe(reg, vi)
190: caddr_t reg;
191: struct vba_device *vi;
192: {
193: register br, cvec; /* r12, r11 value-result */
194: register struct exdevice *exaddr = (struct exdevice *)reg;
195: int i;
196:
197: if (badaddr((caddr_t)exaddr, 2))
198: return 0;
199: /*
200: * Reset EXOS and run self-test (should complete within 2 seconds).
201: */
202: movow(&exaddr->ex_porta, EX_RESET);
203: for (i = 1000000; i; i--) {
204: uncache(&(exaddr->ex_portb));
205: if (exaddr->ex_portb & EX_TESTOK)
206: break;
207: }
208: if ((exaddr->ex_portb & EX_TESTOK) == 0)
209: return 0;
210: br = 0x15;
211: cvec = --vi->ui_hd->vh_lastiv;
212: ex_softc[vi->ui_unit].xs_cvec = cvec;
213: ex_ncall++;
214: return (sizeof(struct exdevice));
215: }
216:
217: /*
218: * Interface exists: make available by filling in network interface record.
219: * System will initialize the interface when it is ready to accept packets.
220: * A NET_ADDRS command is done to get the ethernet address.
221: */
222: exattach(ui)
223: register struct vba_device *ui;
224: {
225: register struct ex_softc *xs = &ex_softc[ui->ui_unit];
226: register struct ifnet *ifp = &xs->xs_if;
227: register struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
228: register struct ex_msg *bp;
229:
230: ifp->if_unit = ui->ui_unit;
231: ifp->if_name = "ex";
232: ifp->if_mtu = ETHERMTU;
233: ifp->if_init = exinit;
234: ifp->if_ioctl = exioctl;
235: ifp->if_output = exoutput;
236: ifp->if_reset = exreset;
237: ifp->if_flags = IFF_BROADCAST;
238:
239: /*
240: * Note: extra memory gets returned by if_vbareserve()
241: * first, so, being page alligned, it is also 16-byte alligned.
242: */
243: if (if_vbareserve(xs->xs_vbinfo, NVBI, EXMAXRBUF,
244: (caddr_t *)&xs->xs_shm, sizeof(*xs->xs_shm)) == 0)
245: return;
246: /*
247: * Temporarily map queues in order to configure EXOS
248: */
249: xs->xs_qbaddr = INCORE_BASE(xs);
250: exconfig(ui, 0); /* without interrupts */
251: if (xs->xs_cm.cm_cc)
252: return; /* bad conf */
253: /*
254: * Get Ethernet address.
255: */
256: if ((bp = exgetcbuf(xs, LLNET_ADDRS)) == (struct ex_msg *)0)
257: panic("exattach");
258: bp->mb_na.na_mask = READ_OBJ;
259: bp->mb_na.na_slot = PHYSSLOT;
260: bp->mb_status |= MH_EXOS;
261: movow(&exaddr->ex_portb, EX_NTRUPT);
262: bp = xs->xs_x2hnext;
263: while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */
264: printf("ex%d: HW %c.%c NX %c.%c, hardware address %s\n",
265: ui->ui_unit, xs->xs_cm.cm_vc[2], xs->xs_cm.cm_vc[3],
266: xs->xs_cm.cm_vc[0], xs->xs_cm.cm_vc[1],
267: ether_sprintf(bp->mb_na.na_addrs));
268: bcopy((caddr_t)bp->mb_na.na_addrs, (caddr_t)xs->xs_addr,
269: sizeof(xs->xs_addr));
270: if_attach(ifp);
271: }
272:
273: /*
274: * Reset of interface after BUS reset.
275: * If interface is on specified vba, reset its state.
276: */
277: exreset(unit)
278: int unit;
279: {
280: register struct vba_device *ui;
281:
282: if (unit >= NEX || (ui = exinfo[unit]) == 0 || ui->ui_alive == 0)
283: return;
284: printf(" ex%d", unit);
285: ex_softc[unit].xs_if.if_flags &= ~IFF_RUNNING;
286: ex_softc[unit].xs_flags &= ~EX_RUNNING;
287:
288: exinit(unit);
289: }
290:
291: /*
292: * Initialization of interface; clear recorded pending operations, and
293: * reinitialize BUS usage. Called at boot time, and at ifconfig time via
294: * exioctl, with interrupts disabled.
295: */
296: exinit(unit)
297: int unit;
298: {
299: register struct ex_softc *xs = &ex_softc[unit];
300: register struct vba_device *ui = exinfo[unit];
301: register struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
302: register struct ifnet *ifp = &xs->xs_if;
303: register struct sockaddr_in *sin;
304: register struct ex_msg *bp;
305: int s;
306:
307: /* not yet, if address still unknown */
308: if (ifp->if_addrlist == (struct ifaddr *)0)
309: return;
310: if (xs->xs_flags & EX_RUNNING)
311: return;
312:
313: xs->xs_qbaddr = INCORE_BASE(xs);
314: exconfig(ui, 4); /* with vectored interrupts*/
315:
316: /*
317: * Put EXOS on the Ethernet, using NET_MODE command
318: */
319: if ((bp = exgetcbuf(xs, LLNET_MODE)) == (struct ex_msg *)0)
320: panic("exinit");
321: bp->mb_nm.nm_mask = WRITE_OBJ;
322: bp->mb_nm.nm_optn = 0;
323: bp->mb_nm.nm_mode = MODE_PERF;
324: bp->mb_status |= MH_EXOS;
325: movow(&exaddr->ex_portb, EX_NTRUPT);
326: bp = xs->xs_x2hnext;
327: while ((bp->mb_status & MH_OWNER) == MH_EXOS) /* poll for reply */
328: ;
329: bp->mb_length = MBDATALEN;
330: bp->mb_status |= MH_EXOS; /* free up buffer */
331: movow(&exaddr->ex_portb, EX_NTRUPT);
332: xs->xs_x2hnext = xs->xs_x2hnext->mb_next;
333:
334: ifp->if_watchdog = exwatch;
335: ifp->if_timer = EXWATCHINTVL;
336: s = splimp(); /* are interrupts disabled here, anyway? */
337: exhangrcv(unit);
338: xs->xs_if.if_flags |= IFF_RUNNING;
339: xs->xs_flags |= EX_RUNNING;
340: if (xs->xs_flags & EX_SETADDR)
341: ex_setaddr((u_char *)0, unit);
342: exstart(&ex_softc[unit].xs_if); /* start transmits */
343: splx(s); /* are interrupts disabled here, anyway? */
344: }
345:
346: /*
347: * Reset, test, and configure EXOS. It is called by exinit, and exattach.
348: * Returns 0 if successful, 1 if self-test failed.
349: */
350: exconfig(ui, itype)
351: struct vba_device *ui;
352: int itype;
353: {
354: register int unit = ui->ui_unit;
355: register struct ex_softc *xs = &ex_softc[unit];
356: register struct exdevice *exaddr = (struct exdevice *) ui->ui_addr;
357: register struct ex_conf *cm = &xs->xs_cm;
358: register struct ex_msg *bp;
359: register struct ifvba *pkb;
360: int i;
361: u_long shiftreg;
362: static u_char cmaddr[8] = {0xFF, 0xFF, 0, 0};
363:
364: xs->xs_flags = 0;
365: /*
366: * Reset EXOS, wait for self-test to complete
367: */
368: movow(&exaddr->ex_porta, EX_RESET);
369: do {
370: uncache(&exaddr->ex_portb);
371: } while ((exaddr->ex_portb & EX_TESTOK) == 0) ;
372: /*
373: * Set up configuration message.
374: */
375: cm->cm_1rsrv = 1;
376: cm->cm_cc = 0xFF;
377: cm->cm_opmode = 0; /* link-level controller mode */
378: cm->cm_dfo = 0x0101; /* enable host data order conversion */
379: cm->cm_dcn1 = 1;
380: cm->cm_2rsrv[0] = cm->cm_2rsrv[1] = 0;
381: cm->cm_ham = 3; /* absolute address mode */
382: cm->cm_3rsrv = 0;
383: cm->cm_mapsiz = 0;
384: cm->cm_byteptrn[0] = 0x01; /* EXOS deduces data order of host */
385: cm->cm_byteptrn[1] = 0x03; /* by looking at this pattern */
386: cm->cm_byteptrn[2] = 0x07;
387: cm->cm_byteptrn[3] = 0x0F;
388: cm->cm_wordptrn[0] = 0x0103;
389: cm->cm_wordptrn[1] = 0x070F;
390: cm->cm_lwordptrn = 0x0103070F;
391: for (i=0; i<20; i++) cm->cm_rsrvd[i] = 0;
392: cm->cm_mba = 0xFFFFFFFF;
393: cm->cm_nproc = 0xFF;
394: cm->cm_nmbox = 0xFF;
395: cm->cm_nmcast = 0xFF;
396: cm->cm_nhost = 1;
397: cm->cm_h2xba = P_BUSADDR(xs->xs_qbaddr);
398: cm->cm_h2xhdr = H2XHDR_OFFSET;
399: cm->cm_h2xtyp = 0; /* should never wait for rqst buffer */
400: cm->cm_x2hba = cm->cm_h2xba;
401: cm->cm_x2hhdr = X2HHDR_OFFSET;
402: cm->cm_x2htyp = itype; /* 0 for none, 4 for vectored */
403: cm->cm_x2haddr = xs->xs_cvec; /* ivec allocated in exprobe */
404: /*
405: * Set up message queues and headers.
406: * First the request queue
407: */
408: for (bp = &xs->xs_h2xent[0]; bp < &xs->xs_h2xent[NH2X]; bp++) {
409: bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs));
410: bp->mb_rsrv = 0;
411: bp->mb_length = MBDATALEN;
412: bp->mb_status = MH_HOST;
413: bp->mb_next = bp+1;
414: }
415: xs->xs_h2xhdr = xs->xs_h2xent[NH2X-1].mb_link = (u_short)H2XENT_OFFSET;
416: xs->xs_h2xnext = xs->xs_h2xent[NH2X-1].mb_next = xs->xs_h2xent;
417:
418: /* Now the reply queue. */
419: for (bp = &xs->xs_x2hent[0]; bp < &xs->xs_x2hent[NX2H]; bp++) {
420: bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs));
421: bp->mb_rsrv = 0;
422: bp->mb_length = MBDATALEN;
423: bp->mb_status = MH_EXOS;
424: bp->mb_next = bp+1;
425: }
426: xs->xs_x2hhdr = xs->xs_x2hent[NX2H-1].mb_link = (u_short)X2HENT_OFFSET;
427: xs->xs_x2hnext = xs->xs_x2hent[NX2H-1].mb_next = xs->xs_x2hent;
428: xs->xs_nrec = 0;
429: xs->xs_ntrb = 0;
430: xs->xs_pkblist = xs->xs_vbinfo + NVBI - 1;
431: for (pkb = xs->xs_pkblist; pkb > xs->xs_vbinfo; pkb--)
432: pkb->iff_mbuf = (struct mbuf *)(pkb - 1);
433: xs->xs_vbinfo[0].iff_mbuf = 0;
434:
435: /*
436: * Write config msg address to EXOS and wait for configuration to
437: * complete (guaranteed response within 2 seconds).
438: */
439: shiftreg = P_BUSADDR(xs->xs_qbaddr) + CM_OFFSET;
440: for (i = 4; i < 8; i++) {
441: cmaddr[i] = (u_char)(shiftreg & 0xFF);
442: shiftreg >>= 8;
443: }
444: for (i = 0; i < 8; i++) {
445: do {
446: uncache(&exaddr->ex_portb);
447: } while (exaddr->ex_portb & EX_UNREADY) ;
448: DELAY(500);
449: movow(&exaddr->ex_portb, cmaddr[i]);
450: }
451: for (i = 500000; i; --i) {
452: DELAY(10);
453: uncache(&cm->cm_cc);
454: if (cm->cm_cc != 0xFF)
455: break;
456: }
457: if (cm->cm_cc)
458: printf("ex%d: configuration failed; cc=%x\n", unit, cm->cm_cc);
459: }
460:
461: /*
462: * Start or re-start output on interface. Get another datagram to send off of
463: * the interface queue, and map it to the interface before starting the output.
464: * This routine is called by exinit(), exoutput(), and excdint(). In all cases,
465: * interrupts by EXOS are disabled.
466: */
467: exstart(ifp)
468: struct ifnet *ifp;
469: {
470: int unit = ifp->if_unit;
471: struct vba_device *ui = exinfo[unit];
472: register struct ex_softc *xs = &ex_softc[unit];
473: struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
474: register struct ex_msg *bp;
475: register struct mbuf *m;
476: int len;
477: register struct ifvba *pkb;
478: struct mbuf *m0 = 0;
479: register int nb = 0, tlen = 0;
480: union l_util {
481: u_long l;
482: struct i86_long i;
483: } l_util;
484:
485: if (xs->xs_ntrb >= NTRB)
486: return;
487: if (xs->xs_pkblist == 0) {
488: printf("ex%d: vbinfo exhausted, would panic", unit);
489: return;
490: }
491: IF_DEQUEUE(&xs->xs_if.if_snd, m);
492: if (m == 0)
493: return;
494: /*
495: * Get a transmit request.
496: */
497: if ((bp = exgetcbuf(xs, LLRTRANSMIT)) == (struct ex_msg *)0) {
498: m_freem(m);
499: printf("exstart: no command buffers\n");
500: return;
501: }
502: xs->xs_ntrb++;
503: GetPkBuf(bp, pkb);
504: pkb->iff_mbuf = m; /* save mbuf pointer to free when done */
505: /*
506: * point directly to the first group of mbufs to be transmitted. The
507: * hardware can only support NFRAGMENTS descriptors.
508: */
509: while (m && ((nb < NFRAGMENTS-1) || (m->m_next == 0)) ) {
510: l_util.l = BUSADDR(mtod(m, caddr_t));
511: bp->mb_et.et_blks[nb].bb_len = (u_short)m->m_len;
512: bp->mb_et.et_blks[nb].bb_addr = l_util.i;
513: if (l_util.l + m->m_len > BUSADDR(VB_MAXADDR24)) {
514: /* Here, the phys memory for the mbuf is out
515: of range for the vmebus to talk to it */
516: if (m == pkb->iff_mbuf)
517: pkb->iff_mbuf = 0;
518: break;
519: }
520: tlen += m->m_len;
521: m0 = m;
522: m = m->m_next;
523: nb++;
524: }
525:
526: /* 0 end of chain pointed to by iff_mbuf, to be freed when xmit done */
527: if (m0)
528: m0->m_next = 0;
529:
530: /*
531: * if not all of the descriptors would fit then merge remaining data
532: * into the transmit buffer, and point to it. Note: the mbufs are freed
533: * during the merge, they do not have to be freed when we get the
534: * transmit interrupt.
535: */
536: if (m) {
537: if (m == pkb->iff_mbuf) {
538: printf("ex%d: exstart insanity\n", unit);
539: pkb->iff_mbuf = 0;
540: }
541: len = if_vbaput(pkb->iff_buffer, m);
542: l_util.l = BUSADDR(pkb->iff_buffer);
543: bp->mb_et.et_blks[nb].bb_len = (u_short)len;
544: bp->mb_et.et_blks[nb].bb_addr = l_util.i;
545: tlen += len;
546: nb++;
547: }
548:
549: /*
550: * If the total length of the packet is too small,
551: * pad the last fragment. (May run into very obscure problems)
552: */
553: if (tlen < sizeof(struct ether_header) + ETHERMIN) {
554: len = (ETHERMIN + sizeof(struct ether_header)) - tlen;
555: bp->mb_et.et_blks[nb-1].bb_len += (u_short)len;
556: tlen += len;
557: #ifdef notdef
558: if (l_util.l + m->m_len > BUSADDR(VB_MAXADDR24)) {
559: must copy last frag into private buffer
560: }
561: #endif
562: }
563:
564: /* set number of fragments in descriptor */
565: bp->mb_et.et_nblock = nb;
566: bp->mb_status |= MH_EXOS;
567: movow(&exaddr->ex_portb, EX_NTRUPT);
568: }
569:
570: /*
571: * interrupt service routine.
572: */
573: exintr(unit)
574: int unit;
575: {
576: register struct ex_softc *xs = &ex_softc[unit];
577: register struct ex_msg *bp = xs->xs_x2hnext;
578: struct vba_device *ui = exinfo[unit];
579: struct exdevice *exaddr = (struct exdevice *)ui->ui_addr;
580: struct ex_msg *next_bp;
581:
582: while ((bp->mb_status & MH_OWNER) == MH_HOST) {
583: switch (bp->mb_rqst) {
584: case LLRECEIVE:
585: if (--xs->xs_nrec < 0) {
586: printf("ex%d: internal receive check\n", unit);
587: xs->xs_nrec = 0;
588: }
589: exrecv(unit, bp);
590: FreePkBuf(bp->mb_pkb);
591: bp->mb_pkb = (struct ifvba *)0;
592: exhangrcv(unit);
593: break;
594:
595: case LLTRANSMIT:
596: case LLRTRANSMIT:
597: if (--xs->xs_ntrb < 0) {
598: printf("ex%d: internal transmit check\n", unit);
599: xs->xs_ntrb = 0;
600: }
601: xs->xs_if.if_opackets++;
602: if (bp->mb_rply == LL_OK || bp->mb_rply == LLXM_NSQE)
603: ;
604: else if (bp->mb_rply & LLXM_1RTRY)
605: xs->xs_if.if_collisions++;
606: else if (bp->mb_rply & LLXM_RTRYS)
607: xs->xs_if.if_collisions += 2; /* guess */
608: else if (bp->mb_rply & LLXM_ERROR)
609: if (xs->xs_if.if_oerrors++ % 100 == 0)
610: printf("ex%d: 100 transmit errors=%b\n",
611: unit, bp->mb_rply, XMIT_BITS);
612: if (bp->mb_pkb->iff_mbuf) {
613: m_freem(bp->mb_pkb->iff_mbuf);
614: bp->mb_pkb->iff_mbuf = (struct mbuf *)0;
615: }
616: FreePkBuf(bp->mb_pkb);
617: bp->mb_pkb = (struct ifvba *)0;
618: exstart(&xs->xs_if);
619: exhangrcv(unit);
620: break;
621:
622: case LLNET_STSTCS:
623: xs->xs_if.if_ierrors += xs->xs_xsa.sa_crc;
624: xs->xs_flags &= ~EX_STATPENDING;
625: case LLNET_ADDRS:
626: case LLNET_RECV:
627: if (bp->mb_rply == LL_OK || bp->mb_rply == LLXM_NSQE)
628: ;
629: else
630: printf("ex%d: %s, request 0x%x, reply 0x%x\n",
631: unit, "unsucessful stat or address change",
632: bp->mb_rqst, bp->mb_rply);
633: break;
634:
635: default:
636: printf("ex%d: unknown reply 0x%x", unit, bp->mb_rqst);
637: }
638: bp->mb_length = MBDATALEN;
639: next_bp = bp->mb_next;
640: bp->mb_status |= MH_EXOS; /* free up buffer */
641: bp = next_bp; /* paranoia about race */
642: movow(&exaddr->ex_portb, EX_NTRUPT); /* tell EXOS about it */
643: }
644: xs->xs_x2hnext = bp;
645: }
646:
647: /*
648: * Get a request buffer, fill in standard values, advance pointer.
649: */
650: struct ex_msg *
651: exgetcbuf(xs, req)
652: struct ex_softc *xs;
653: int req;
654: {
655: register struct ex_msg *bp;
656: struct ifvba *pkb;
657: int s = splimp();
658:
659: bp = xs->xs_h2xnext;
660: if ((bp->mb_status & MH_OWNER) == MH_EXOS) {
661: splx(s);
662: return (struct ex_msg *)0;
663: }
664: xs->xs_h2xnext = bp->mb_next;
665: bp->mb_1rsrv = 0;
666: bp->mb_rqst = req;
667: bp->mb_length = MBDATALEN;
668: bp->mb_pkb = (struct ifvba *)0;
669: splx(s);
670: return bp;
671: }
672:
673: /*
674: * Process Ethernet receive completion: If input error just drop packet,
675: * otherwise examine packet to determine type. If can't determine length from
676: * type, then have to drop packet, otherwise decapsulate packet based on type
677: * and pass to type-specific higher-level input routine.
678: */
679: exrecv(unit, bp)
680: int unit;
681: register struct ex_msg *bp;
682: {
683: register struct ex_softc *xs = &ex_softc[unit];
684: register struct ether_header *eh;
685: register struct mbuf *m;
686: register struct ifqueue *inq;
687: int len, off, resid;
688: struct ifnet *ifp = &xs->xs_if;
689: int s;
690:
691: xs->xs_if.if_ipackets++;
692: /* total length - header - crc */
693: len = bp->mb_er.er_blks[0].bb_len - sizeof(struct ether_header) - 4;
694: if (bp->mb_rply != LL_OK) {
695: if (xs->xs_if.if_ierrors++ % 100 == 0)
696: printf("ex%d: 100 receive errors=%b\n",
697: unit, bp->mb_rply, RECV_BITS);
698: return;
699: }
700: eh = (struct ether_header *)(bp->mb_pkb->iff_buffer);
701:
702: /*
703: * Deal with trailer protocol: if type is PUP trailer get true type from
704: * first 16-bit word past data. Remember that type was trailer by
705: * setting off.
706: */
707: eh->ether_type = ntohs((u_short)eh->ether_type);
708: #define exdataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off))))
709: if (eh->ether_type >= ETHERTYPE_TRAIL &&
710: eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) {
711: off = (eh->ether_type - ETHERTYPE_TRAIL) * 512;
712: if (off >= ETHERMTU)
713: return; /* sanity */
714: eh->ether_type = ntohs(*exdataaddr(eh, off, u_short *));
715: resid = ntohs(*(exdataaddr(eh, off+2, u_short *)));
716: if (off + resid > len)
717: return; /* sanity */
718: len = off + resid;
719: } else
720: off = 0;
721: if (len == 0)
722: return;
723: /*
724: * Pull packet off interface. Off is nonzero if packet
725: * has trailing header; if_vbaget will then force this header
726: * information to be at the front, but we still have to drop
727: * the type and length which are at the front of any trailer data.
728: */
729: m = if_vbaget(bp->mb_pkb->iff_buffer, len, off, ifp);
730: if (m == 0)
731: return;
732: if (off) {
733: m->m_off += 2 * sizeof (u_short);
734: m->m_len -= 2 * sizeof (u_short);
735: *(mtod(m, struct ifnet **)) = ifp;
736: }
737:
738: switch (eh->ether_type) {
739: #ifdef INET
740: case ETHERTYPE_IP:
741: schednetisr(NETISR_IP);
742: inq = &ipintrq;
743: break;
744:
745: case ETHERTYPE_ARP:
746: arpinput((struct arpcom *)ifp, m);
747: return;
748: #endif
749: #ifdef NS
750: case ETHERTYPE_NS:
751: schednetisr(NETISR_NS);
752: inq = &nsintrq;
753: break;
754:
755: #endif
756: default:
757: m_freem(m);
758: return;
759: }
760:
761: s = splimp();
762: if (IF_QFULL(inq)) {
763: IF_DROP(inq);
764: m_freem(m);
765: } else
766: IF_ENQUEUE(inq, m);
767: splx(s);
768: return;
769: }
770:
771: /*
772: * Hang a receive request. This routine is called by exinit and excdint,
773: * with interrupts disabled in both cases.
774: */
775: exhangrcv(unit)
776: int unit;
777: {
778: register struct ex_softc *xs = &ex_softc[unit];
779: register struct ex_msg *bp;
780: register struct ifvba *pkb;
781: short mustint = 0;
782: union l_util {
783: u_long l;
784: struct i86_long i;
785: } l_util;
786:
787: while (xs->xs_nrec < NREC) {
788: if (xs->xs_pkblist == (struct ifvba *)0)
789: break;
790: if ((bp = exgetcbuf(xs, LLRECEIVE)) == (struct ex_msg *)0) {
791: break;
792: }
793: GetPkBuf(bp, pkb);
794: pkb->iff_mbuf = 0;
795: xs->xs_nrec += 1;
796: bp->mb_er.er_nblock = 1;
797: bp->mb_er.er_blks[0].bb_len = EXMAXRBUF;
798: l_util.l = BUSADDR(pkb->iff_buffer);
799: bp->mb_er.er_blks[0].bb_addr = l_util.i;
800: bp->mb_status |= MH_EXOS;
801: mustint = 1;
802: }
803: if (mustint == 0)
804: return;
805: movow(&((struct exdevice *)exinfo[unit]->ui_addr)->ex_portb, EX_NTRUPT);
806: }
807:
808: /*
809: * Ethernet output routine.
810: * Encapsulate a packet of type family for the local net.
811: * Use trailer local net encapsulation if enough data in first
812: * packet leaves a multiple of 512 bytes of data in remainder.
813: * Assumes that ifp is actually pointer to arpcom structure.
814: */
815:
816: exoutput(ifp, m0, dst)
817: register struct ifnet *ifp;
818: struct mbuf *m0;
819: struct sockaddr *dst;
820: {
821: short type;
822: int s, error = 0;
823: u_char edst[6];
824: struct in_addr idst;
825: register struct mbuf *m = m0;
826: struct mbuf *mcopy = 0;
827: register struct ether_header *eh;
828: int usetrailers, off = 0, totlen;
829: #define ac ((struct arpcom *)ifp)
830:
831: if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
832: error = ENETDOWN;
833: goto bad;
834: }
835: switch (dst->sa_family) {
836:
837: #ifdef INET
838: case AF_INET:
839: idst = ((struct sockaddr_in *)dst)->sin_addr;
840: if (!arpresolve(ac, m, &idst, edst, &usetrailers))
841: return (0); /* if not yet resolved */
842: off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
843: if (usetrailers && off > 0 && (off & 0x1ff) == 0 &&
844: m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
845: type = ETHERTYPE_TRAIL + (off>>9);
846: m->m_off -= 2 * sizeof (u_short);
847: m->m_len += 2 * sizeof (u_short);
848: *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP);
849: *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
850: goto gottrailertype;
851: }
852: type = ETHERTYPE_IP;
853: goto gottype;
854: #endif
855: #ifdef NS
856: case AF_NS:
857: type = ETHERTYPE_NS;
858: bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
859: (caddr_t)edst, sizeof (edst));
860: goto gottype;
861: #endif
862: case AF_UNSPEC:
863: eh = (struct ether_header *)dst->sa_data;
864: bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst));
865: type = eh->ether_type;
866: goto gottype;
867:
868: default:
869: printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit,
870: dst->sa_family);
871: error = EAFNOSUPPORT;
872: goto bad;
873: }
874:
875: gottrailertype:
876: /*
877: * Packet to be sent as trailer: move first packet
878: * (control information) to end of chain.
879: */
880: while (m->m_next)
881: m = m->m_next;
882: m->m_next = m0;
883: m = m0->m_next;
884: m0->m_next = 0;
885: m0 = m;
886:
887: gottype:
888: /*
889: * Add local net header. If no space in first mbuf,
890: * allocate another.
891: */
892: if (m->m_off > MMAXOFF ||
893: MMINOFF + sizeof (struct ether_header) > m->m_off) {
894: m = m_get(M_DONTWAIT, MT_HEADER);
895: if (m == 0) {
896: error = ENOBUFS;
897: goto bad;
898: }
899: m->m_next = m0;
900: m->m_off = MMINOFF;
901: m->m_len = sizeof (struct ether_header);
902: } else {
903: m->m_off -= sizeof (struct ether_header);
904: m->m_len += sizeof (struct ether_header);
905: }
906: eh = mtod(m, struct ether_header *);
907: type = htons((u_short)type);
908: bcopy((caddr_t)&type,(caddr_t)&eh->ether_type,
909: sizeof(eh->ether_type));
910: bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst));
911: bcopy((caddr_t)ac->ac_enaddr, (caddr_t)eh->ether_shost,
912: sizeof(eh->ether_shost));
913: /*
914: * Queue message on interface, and start output if interface
915: * not yet active.
916: */
917: s = splimp();
918: if (IF_QFULL(&ifp->if_snd)) {
919: IF_DROP(&ifp->if_snd);
920: splx(s);
921: error = ENOBUFS;
922: goto bad;
923: }
924: IF_ENQUEUE(&ifp->if_snd, m);
925: error = exstart(ifp);
926: splx(s);
927: return (error);
928:
929: bad:
930: if (m)
931: m_freem(m);
932: return (error);
933: }
934:
935: /*
936: * Watchdog routine (currently not used). Might use this to get stats from EXOS.
937: */
938: exwatch(unit)
939: int unit;
940: {
941: struct exdevice *exaddr = (struct exdevice *)exinfo[unit]->ui_addr;
942: register struct ex_softc *xs = &ex_softc[unit];
943: register struct ex_msg *bp;
944: int s = splimp();
945:
946: if (xs->xs_flags & EX_STATPENDING)
947: goto exspnd;
948: if ((bp = exgetcbuf(xs, LLNET_STSTCS)) == (struct ex_msg *)0) {
949: splx(s);
950: return;
951: }
952: xs->xs_flags |= EX_STATPENDING;
953: bp->mb_ns.ns_mask = READ_OBJ;
954: bp->mb_ns.ns_rsrv = 0;
955: bp->mb_ns.ns_nobj = 8;
956: bp->mb_ns.ns_xobj = 0;
957: bp->mb_ns.ns_bufp = P_BUSADDR(xs->xs_qbaddr) + SA_OFFSET;
958: bp->mb_status |= MH_EXOS;
959: movow(&exaddr->ex_portb, EX_NTRUPT);
960: exspnd: splx(s);
961: xs->xs_if.if_timer = EXWATCHINTVL;
962: }
963:
964: /*
965: * Process an ioctl request.
966: */
967: exioctl(ifp, cmd, data)
968: register struct ifnet *ifp;
969: int cmd;
970: caddr_t data;
971: {
972: register struct ifaddr *ifa = (struct ifaddr *)data;
973: register struct ex_softc *xs = &ex_softc[ifp->if_unit];
974: int s = splimp(), error = 0;
975:
976: switch (cmd) {
977:
978: case SIOCSIFADDR:
979: ifp->if_flags |= IFF_UP;
980: exinit(ifp->if_unit);
981:
982: switch (ifa->ifa_addr.sa_family) {
983: #ifdef INET
984: case AF_INET:
985: ((struct arpcom *)ifp)->ac_ipaddr =
986: IA_SIN(ifa)->sin_addr;
987: arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
988: break;
989: #endif
990: #ifdef NS
991: case AF_NS:
992: {
993: register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
994:
995: if (ns_nullhost(*ina))
996: ina->x_host = *(union ns_host *)(xs->xs_addr);
997: else
998: ex_setaddr(ina->x_host.c_host,ifp->if_unit);
999: break;
1000: }
1001: #endif
1002: }
1003: break;
1004:
1005: case SIOCSIFFLAGS:
1006: if ((ifp->if_flags & IFF_UP) == 0 &&
1007: xs->xs_flags & EX_RUNNING) {
1008: movow(&((struct exdevice *)
1009: (exinfo[ifp->if_unit]->ui_addr))->ex_porta, EX_RESET);
1010: xs->xs_flags &= ~EX_RUNNING;
1011: } else if (ifp->if_flags & IFF_UP &&
1012: (xs->xs_flags & EX_RUNNING) == 0)
1013: exinit(ifp->if_unit);
1014: break;
1015:
1016: default:
1017: error = EINVAL;
1018: }
1019: splx(s);
1020: return (error);
1021: }
1022:
1023: /*
1024: * set ethernet address for unit
1025: */
1026: ex_setaddr(physaddr, unit)
1027: u_char *physaddr;
1028: int unit;
1029: {
1030: register struct ex_softc *xs = &ex_softc[unit];
1031:
1032: if (physaddr) {
1033: xs->xs_flags |= EX_SETADDR;
1034: bcopy((caddr_t)physaddr, (caddr_t)xs->xs_addr, 6);
1035: }
1036: ex_setmulti((u_char *)xs->xs_addr, unit, PHYSSLOT);
1037: }
1038:
1039: /*
1040: * Enable multicast reception for unit.
1041: */
1042: ex_setmulti(linkaddr, unit, slot)
1043: u_char *linkaddr;
1044: int unit, slot;
1045: {
1046: register struct ex_softc *xs = &ex_softc[unit];
1047: struct vba_device *ui = exinfo[unit];
1048: register struct exdevice *addr= (struct exdevice *)ui->ui_addr;
1049: register struct ex_msg *bp;
1050:
1051: if (!(xs->xs_flags & EX_RUNNING))
1052: return;
1053: bp = exgetcbuf(xs, LLNET_ADDRS);
1054: bp->mb_na.na_mask = READ_OBJ|WRITE_OBJ;
1055: bp->mb_na.na_slot = slot;
1056: bcopy((caddr_t)linkaddr, (caddr_t)bp->mb_na.na_addrs, 6);
1057: bp->mb_status |= MH_EXOS;
1058: movow(&addr->ex_portb, EX_NTRUPT);
1059: bp = xs->xs_x2hnext;
1060: while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */
1061: #ifdef DEBUG
1062: log(LOG_DEBUG, "ex%d: %s %s (slot %d)\n", unit,
1063: (slot == PHYSSLOT ? "reset addr" : "add multicast"
1064: ether_sprintf(bp->mb_na.na_addrs), slot);
1065: #endif
1066: /*
1067: * Now, re-enable reception on slot.
1068: */
1069: bp = exgetcbuf(xs, LLNET_RECV);
1070: bp->mb_nr.nr_mask = ENABLE_RCV|READ_OBJ|WRITE_OBJ;
1071: bp->mb_nr.nr_slot = slot;
1072: bp->mb_status |= MH_EXOS;
1073: movow(&addr->ex_portb, EX_NTRUPT);
1074: bp = xs->xs_x2hnext;
1075: while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */
1076: ;
1077: }
1078: /*
1079: * Copyright (c) 1989 The Regents of the University of California.
1080: * All rights reserved.
1081: *
1082: * Redistribution and use in source and binary forms are permitted
1083: * provided that the above copyright notice and this paragraph are
1084: * duplicated in all such forms and that any documentation,
1085: * advertising materials, and other materials related to such
1086: * distribution and use acknowledge that the software was developed
1087: * by the University of California, Berkeley. The name of the
1088: * University may not be used to endorse or promote products derived
1089: * from this software without specific prior written permission.
1090: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1091: * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1092: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1093: *
1094: * @(#)if_vba.c 1.2 (Berkeley) 4/22/89
1095: */
1096:
1097: #ifdef notdef
1098: #include "param.h"
1099: #include "systm.h"
1100: #include "mbuf.h"
1101: #include "buf.h"
1102: #include "cmap.h"
1103: #include "vmmac.h"
1104: #include "socket.h"
1105:
1106: #include "../tahoe/mtpr.h"
1107: #include "../tahoe/pte.h"
1108:
1109: #include "../tahoevba/vbavar.h"
1110:
1111: #include "../net/if.h"
1112: #include "../netinet/in.h"
1113: #include "../netinet/if_ether.h"
1114: #endif
1115:
1116: static
1117: if_vbareserve(ifvba0, n, bufsize, extra, extrasize)
1118: struct ifvba *ifvba0;
1119: register int n;
1120: int bufsize;
1121: caddr_t *extra;
1122: int extrasize;
1123: {
1124: register caddr_t cp;
1125: register struct pte *pte;
1126: register struct ifvba *ifvba = ifvba0;
1127: struct ifvba *vlim = ifvba + n;
1128:
1129: n = roundup(extrasize + (n * bufsize), NBPG);
1130: cp = (caddr_t)malloc((u_long)n, M_DEVBUF, M_NOWAIT);
1131: if ((n + kvtophys(cp)) > VB_MAXADDR24) {
1132: free(cp, M_DEVBUF);
1133: cp = 0;
1134: }
1135: if (cp == 0) {
1136: printf("No memory for device buffer(s)\n");
1137: return (0);
1138: }
1139: /*
1140: * Make raw buffer pages uncacheable.
1141: */
1142: pte = kvtopte(cp);
1143: for (n = btoc(n); n--; pte++)
1144: pte->pg_nc = 1;
1145: mtpr(TBIA, 0);
1146: if (extra) {
1147: *extra = cp;
1148: cp += extrasize;
1149: }
1150: for (; ifvba < vlim; ifvba++) {
1151: ifvba->iff_buffer = cp;
1152: ifvba->iff_physaddr = kvtophys(cp);
1153: cp += bufsize;
1154: }
1155: return (1);
1156: }
1157: /*
1158: * Routine to copy from VERSAbus memory into mbufs.
1159: *
1160: * Warning: This makes the fairly safe assumption that
1161: * mbufs have even lengths.
1162: */
1163: static struct mbuf *
1164: if_vbaget(rxbuf, totlen, off0, ifp)
1165: u_char *rxbuf;
1166: int totlen, off0;
1167: struct ifnet *ifp;
1168: {
1169: register u_char *cp, *mcp;
1170: register struct mbuf *m;
1171: struct mbuf *top = 0, **mp = ⊤
1172: int len, off = off0;
1173:
1174: cp = rxbuf + sizeof (struct ether_header);
1175: while (totlen > 0) {
1176: MGET(m, M_DONTWAIT, MT_DATA);
1177: if (m == 0)
1178: goto bad;
1179: if (off) {
1180: len = totlen - off;
1181: cp = rxbuf + sizeof (struct ether_header) + off;
1182: } else
1183: len = totlen;
1184: if (len >= NBPG) {
1185: MCLGET(m);
1186: if (m->m_len == CLBYTES)
1187: m->m_len = len = MIN(len, CLBYTES);
1188: else
1189: m->m_len = len = MIN(MLEN, len);
1190: } else {
1191: m->m_len = len = MIN(MLEN, len);
1192: m->m_off = MMINOFF;
1193: }
1194: mcp = mtod(m, u_char *);
1195: if (ifp) {
1196: /*
1197: * Prepend interface pointer to first mbuf.
1198: */
1199: *(mtod(m, struct ifnet **)) = ifp;
1200: mcp += sizeof (ifp);
1201: len -= sizeof (ifp);
1202: ifp = (struct ifnet *)0;
1203: }
1204: bcopy(cp, mcp, (u_int)len);
1205: cp += len;
1206: *mp = m;
1207: mp = &m->m_next;
1208: if (off == 0) {
1209: totlen -= len;
1210: continue;
1211: }
1212: off += len;
1213: if (off == totlen) {
1214: cp = rxbuf + sizeof (struct ether_header);
1215: off = 0;
1216: totlen = off0;
1217: }
1218: }
1219: return (top);
1220: bad:
1221: m_freem(top);
1222: return (0);
1223: }
1224:
1225: static
1226: if_vbaput(ifu, m0)
1227: caddr_t ifu;
1228: struct mbuf *m0;
1229: {
1230: register struct mbuf *m = m0;
1231: register caddr_t cp = ifu;
1232:
1233: while (m) {
1234: bcopy(mtod(m, caddr_t), cp, (u_int)m->m_len);
1235: cp += m->m_len;
1236: MFREE(m, m0);
1237: m = m0;
1238: }
1239: if ((int)cp & 1)
1240: *cp++ = 0;
1241: return (cp - ifu);
1242: }
1243: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.