|
|
1.1 root 1: /* if_hy.c 6.1 83/07/29 */
2:
3: #include "hy.h"
4: #if NHY > 0
5:
6: /*
7: * Network Systems Copropration Hyperchanel interface
8: *
9: * UNTESTED WITH 4.2
10: */
11: #include "../machine/pte.h"
12:
13: #include "../h/param.h"
14: #include "../h/systm.h"
15: #include "../h/mbuf.h"
16: #include "../h/buf.h"
17: #include "../h/protosw.h"
18: #include "../h/socket.h"
19: #include "../h/vmmac.h"
20: #include "../h/errno.h"
21: #include "../h/time.h"
22: #include "../h/kernel.h"
23: #include "../h/ioctl.h"
24:
25: #include "../net/if.h"
26: #include "../net/netisr.h"
27: #include "../net/route.h"
28: #include "../netinet/in.h"
29: #include "../netinet/in_systm.h"
30: #include "../netinet/ip.h"
31: #include "../netinet/ip_var.h"
32:
33: #include "../vax/cpu.h"
34: #include "../vax/mtpr.h"
35: #include "../vaxuba/ubareg.h"
36: #include "../vaxuba/ubavar.h"
37: #include "../vaxif/if_hy.h"
38: #include "../vaxif/if_hyreg.h"
39: #include "../vaxif/if_uba.h"
40:
41: #define HYROUTE
42: #define HYELOG
43: #define HYMTU 576
44:
45: int hyprobe(), hyattach(), hyinit(), hyioctl();
46: int hyoutput(), hyreset(), hywatch();
47: struct uba_device *hyinfo[NHY];
48: u_short hystd[] = { 0772410, 0 };
49: struct uba_driver hydriver =
50: { hyprobe, 0, hyattach, 0, hystd, "hy", hyinfo };
51:
52: /*
53: * Hyperchannel software status per interface.
54: *
55: * Each interface is referenced by a network interface structure,
56: * hy_if, which the routing code uses to locate the interface.
57: * This structure contains the output queue for the interface, its address, ...
58: * We also have, for each interface, a UBA interface structure, which
59: * contains information about the UNIBUS resources held by the interface:
60: * map registers, buffered data paths, etc. Information is cached in this
61: * structure for use by the if_uba.c routines in running the interface
62: * efficiently.
63: */
64: struct hy_softc {
65: struct ifnet hy_if; /* network-visible interface */
66: struct ifuba hy_ifuba; /* UNIBUS resources */
67: short hy_flags; /* flags */
68: short hy_state; /* driver state */
69: int hy_ilen; /* mp length on input */
70: int hy_olen; /* packet length on output */
71: int hy_lastwcr; /* last command's word count */
72: short hy_savedstate; /* saved for reissue after status */
73: short hy_savedcmd; /* saved command for reissue */
74: int hy_savedcount; /* saved byte count for reissue */
75: int hy_savedaddr; /* saved unibus address for reissue */
76: int hy_ntime; /* number of timeouts since last cmd */
77: int hy_retry; /* retry counter */
78: struct hy_stat hy_stat; /* statistics */
79: struct hy_status hy_status; /* status */
80: } hy_softc[NHY];
81:
82: #ifdef HYELOG
83: #define HYE_MAX 0x18
84: u_long hy_elog[(HYE_MAX+1)*4];
85: #endif
86:
87: #ifdef DEBUG
88: #define printL lprintf
89: #define printD if (hy_debug_flag) lprintf
90: int hy_debug_flag = 0;
91: /*
92: * hy_nodebug bit 0x01 set hy_debug_flag on hycancel
93: * hy_nodebug bit 0x02 set hy_debug_flag on command reissue
94: * hy_nodebug bit 0x04 set hy_debug_flag on abnormal interrupt
95: * hy_nodebug bit 0x08 set hy_debug_flag on hyouput
96: * hy_nodebug bit 0x10 set hy_debug_flag on hyouput with associated data
97: */
98: int hy_nodebug = 0x0;
99: #else
100: #define printD hyvoid
101: #endif
102:
103: /*
104: * Requests for service (in order by descending priority).
105: */
106: #define RQ_ENDOP 001 /* end the last adapter function */
107: #define RQ_REISSUE 002 /* reissue previous cmd after status */
108: #define RQ_STATUS 004 /* get the status of the adapter */
109: #define RQ_STATISTICS 010 /* get the statistics of the adapter */
110: #define RQ_MARKDOWN 020 /* mark this adapter port down */
111: #define RQ_MARKUP 040 /* mark this interface up */
112:
113: #define RQ_XASSOC 0100 /* associated data to transmit */
114:
115: /*
116: * Driver states.
117: */
118: #define STARTUP 0 /* initial state (before fully there) */
119: #define IDLE 1 /* idle state */
120: #define STATSENT 2 /* status cmd sent to adapter */
121: #define ENDOPSENT 3 /* end operation cmd sent */
122: #define RECVSENT 4 /* input message cmd sent */
123: #define RECVDATASENT 5 /* input data cmd sent */
124: #define XMITSENT 6 /* transmit message cmd sent */
125: #define XMITDATASENT 7 /* transmit data cmd sent */
126: #define WAITING 8 /* waiting for messages */
127: #define CLEARSENT 9 /* clear wait for message cmd sent */
128: #define MARKPORT 10 /* mark this host's adapter port down issued */
129: #define RSTATSENT 11 /* read statistics cmd sent to adapter */
130:
131: #ifdef DEBUG
132: char *hy_state_names[] = {
133: "Startup",
134: "Idle",
135: "Status Sent",
136: "End op Sent",
137: "Recieve Message Proper Sent",
138: "Recieve Data Sent",
139: "Transmit Message Proper Sent",
140: "Transmit Data Sent",
141: "Wait for Message Sent",
142: "Clear Wait for Message Sent",
143: "Mark Port Down Sent",
144: "Read Statistics Sent"
145: };
146: #endif
147:
148: #define SCANINTERVAL 10 /* seconds */
149: #define MAXINTERVAL 20 /* seconds (max action) */
150:
151: /*
152: * Cause a device interrupt. This code uses a buffer starting at
153: * location zero on the unibus (which is already mapped by the
154: * autoconfigure code in the kernel).
155: */
156: hyprobe(reg)
157: caddr_t reg;
158: {
159: register int br, cvec; /* r11, r10 value-result */
160: register struct hydevice *addr = (struct hydevice *) reg;
161:
162: #ifdef lint
163: br = 0; cvec = br; br = cvec;
164: hyint(0);
165: #endif
166: /*
167: * request adapter status to a buffer starting at unibus location 0
168: */
169: addr->hyd_bar = 0;
170: addr->hyd_wcr = -((sizeof(struct hy_status) + 1) >> 1);
171: addr->hyd_dbuf = HYF_STATUS;
172: #ifdef PI13
173: addr->hyd_csr |= S_GO | S_IE | S_IATTN;
174: #else
175: addr->hyd_csr |= S_GO | S_IE;
176: #endif
177: DELAY(10000);
178: #ifdef PI13
179: addr->hyd_csr |= S_CLRINT; /* clear any stacked interrupts */
180: #endif
181: addr->hyd_csr &= ~(S_IE | S_CLRINT); /* disable further interrupts */
182: return(1);
183: }
184:
185: /*
186: * Interface exists: make available by filling in network interface
187: * record. System will initialize the interface when it is ready
188: * to accept packets.
189: */
190: hyattach(ui)
191: struct uba_device *ui;
192: {
193: register struct hy_softc *is = &hy_softc[ui->ui_unit];
194: register struct ifnet *ifp = &is->hy_if;
195:
196: ifp->if_unit = ui->ui_unit;
197: ifp->if_name = "hy";
198: ifp->if_mtu = HYMTU;
199: is->hy_state = STARTUP; /* don't allow state transitions yet */
200: ifp->if_init = hyinit;
201: ifp->if_ioctl = hyioctl;
202: ifp->if_output = hyoutput;
203: ifp->if_reset = hyreset;
204: ifp->if_watchdog = hywatch;
205: ifp->if_timer = SCANINTERVAL;
206: is->hy_ifuba.ifu_flags = UBA_CANTWAIT;
207: #ifdef notdef
208: is->hy_ifuba.ifu_flags |= UBA_NEEDBDP;
209: #endif
210: if_attach(ifp);
211: }
212:
213: /*
214: * Reset of interface after UNIBUS reset.
215: * If interface is on specified uba, reset its state.
216: */
217: hyreset(unit, uban)
218: int unit, uban;
219: {
220: register struct uba_device *ui = hyinfo[unit];
221:
222: if (unit >= NHY || ui == 0 || ui->ui_alive == 0 ||
223: ui->ui_ubanum != uban)
224: return;
225: printf(" hy%d", unit);
226: hyinit(unit);
227: }
228:
229: /*
230: * Initialization of interface; clear recorded pending
231: * operations, and reinitialize UNIBUS usage.
232: */
233: hyinit(unit)
234: int unit;
235: {
236: register struct hy_softc *is = &hy_softc[unit];
237: register struct uba_device *ui = hyinfo[unit];
238: struct sockaddr_in *sin;
239: int s;
240:
241: sin = (struct sockaddr_in *)&is->hy_if.if_addr;
242: if (in_netof(sin->sin_addr) == 0)
243: return;
244: if (if_ubainit(&is->hy_ifuba, ui->ui_ubanum,
245: sizeof (struct hy_hdr), (int)btoc(HYMTU)) == 0) {
246: #ifdef DEBUG
247: if (hy_nodebug & 4)
248: hy_debug_flag = 1;
249: #endif
250: printf("hy%d: can't initialize\n", unit);
251: is->hy_if.if_flags &= ~IFF_UP;
252: return;
253: }
254: is->hy_if.if_flags |= IFF_RUNNING;
255: /*
256: * Issue wait for message and start the state machine
257: */
258: s = splimp();
259: is->hy_state = IDLE;
260: is->hy_flags = RQ_STATUS | RQ_STATISTICS | RQ_MARKUP;
261: is->hy_retry = 0;
262: hyact(ui);
263: splx(s);
264: }
265:
266: /*
267: * Issue a command to the adapter
268: */
269: hystart(ui, cmd, count, ubaddr)
270: struct uba_device *ui;
271: int cmd, count, ubaddr;
272: {
273: register struct hy_softc *is = &hy_softc[ui->ui_unit];
274: register struct hydevice *addr = (struct hydevice *)ui->ui_addr;
275:
276: #ifdef DEBUG
277: printD("hy%d: hystart cmd = 0x%x count=%d ubaddr=0x%x\n",
278: ui->ui_unit, cmd, count, ubaddr);
279: printD("hy%d: - csr = 0x%b, bar = 0x%x, wcr = 0x%x\n",
280: ui->ui_unit, addr->hyd_csr, HY_CSR_BITS, addr->hyd_bar,
281: addr->hyd_wcr);
282: #endif
283: if (((is->hy_flags & RQ_REISSUE) == 0) &&
284: (cmd != HYF_STATUS) && (cmd != HYF_END_OP) && (cmd != HYF_RSTATS)) {
285: is->hy_savedstate = is->hy_state;
286: is->hy_savedcmd = cmd;
287: is->hy_savedcount = count;
288: is->hy_savedaddr = ubaddr;
289: }
290: addr->hyd_bar = ubaddr & 0xffff;
291: addr->hyd_wcr = is->hy_lastwcr = -((count+1) >> 1);
292: addr->hyd_dbuf = cmd;
293: #ifdef PI13
294: addr->hyd_csr = ((ubaddr >> XBASHIFT) & S_XBA) | S_GO | S_IE | S_IATTN;
295: #else
296: addr->hyd_csr = ((ubaddr >> XBASHIFT) & S_XBA) | S_GO | S_IE;
297: #endif
298: #ifdef DEBUG
299: printD("hy%d: exit hystart - csr = 0x%b, bar = 0x%x, wcr = 0x%x\n",
300: ui->ui_unit, addr->hyd_csr, HY_CSR_BITS, addr->hyd_bar,
301: addr->hyd_wcr);
302: #endif
303: #ifdef HYLOG
304: {
305: struct {
306: u_char hcmd;
307: u_char hstate;
308: short hcount;
309: } hcl;
310:
311: hcl.hcmd = cmd;
312: hcl.hstate = is->hy_state;
313: hcl.hcount = count;
314: hylog(HYL_CMD, sizeof(hcl), (char *)&hcl);
315: }
316: #endif
317: is->hy_ntime = 0;
318: }
319:
320: int hyint_active = 0; /* set during hy interrupt */
321: /*
322: * Hyperchannel interface interrupt.
323: *
324: * An interrupt can occur for many reasons. Examine the status of
325: * the hyperchannel status bits to determine what to do next.
326: *
327: * If input error just drop packet.
328: * Otherwise purge input buffered data path and examine
329: * packet to determine type. Othewise decapsulate
330: * packet based on type and pass to type specific higher-level
331: * input routine.
332: */
333: hyint(unit)
334: int unit;
335: {
336: register struct hy_softc *is = &hy_softc[unit];
337: register struct uba_device *ui = hyinfo[unit];
338: register struct hydevice *addr = (struct hydevice *)ui->ui_addr;
339:
340: if (hyint_active)
341: panic("RECURSIVE HYPERCHANNEL INTERRUPT");
342: hyint_active++;
343: #ifdef DEBUG
344: printD("hy%d: hyint enter - csr = 0x%b, bar = 0x%x, wcr = 0x%x\n",
345: unit, addr->hyd_csr, HY_CSR_BITS, addr->hyd_bar, addr->hyd_wcr);
346: #endif
347: #ifdef HYLOG
348: logit:
349: {
350: struct {
351: u_char hstate;
352: u_char hflags;
353: short hcsr;
354: short hwcr;
355: } hil;
356: hil.hstate = is->hy_state;
357: hil.hflags = is->hy_flags;
358: hil.hcsr = addr->hyd_csr;
359: hil.hwcr = addr->hyd_wcr;
360: hylog(HYL_INT, sizeof(hil), (char *)&hil);
361: }
362: #endif
363: if (HYS_ERROR(addr) && ((addr->hyd_csr & S_ATTN) == 0)) {
364: /*
365: * Error bit set, some sort of error in the interface.
366: *
367: * The adapter sets attn on command completion so that's not
368: * a real error even though the interface considers it one.
369: */
370: #ifdef DEBUG
371: if (hy_nodebug & 4)
372: hy_debug_flag = 1;
373: #endif
374: printf("csr = 0x%b\nbar = 0x%x\nwcr = 0x%x\n",
375: addr->hyd_csr, HY_CSR_BITS, addr->hyd_bar,
376: addr->hyd_wcr);
377: if (addr->hyd_csr & S_NEX) {
378: printf("hy%d: NEX - Non Existant Memory\n", unit);
379: #ifdef PI13
380: addr->hyd_csr |= S_NEX; /* as per PI13 manual */
381: #else
382: addr->hyd_csr &= ~S_NEX;
383: #endif
384: hycancel(ui);
385: #ifdef PI13
386: } else if (addr->hyd_csr & S_POWEROFF) {
387: printf("hy%d: Power Off bit set, trying to reset\n",
388: unit);
389: addr->hyd_csr |= S_POWEROFF;
390: DELAY(100);
391: if (addr->hyd_csr & S_POWEROFF) {
392: if_down(&is->hy_if);
393: is->hy_state = STARTUP;
394: printf(
395: "hy%d: Power Off Error, network shutdown\n",
396: unit);
397: }
398: #endif
399: } else {
400: printf("hy%d: BAR overflow\n", unit);
401: hycancel(ui);
402: }
403: } else if (HYS_NORMAL(addr)) {
404: /*
405: * Normal interrupt, bump state machine unless in state
406: * waiting and no data present (assumed to be word count
407: * zero interrupt or other hardware botch).
408: */
409: if (is->hy_state != WAITING || HYS_RECVDATA(addr))
410: hyact(ui);
411: } else if (HYS_ABNORMAL(addr)) {
412: /*
413: * Abnormal termination.
414: * bump error counts, retry the last function
415: * 'MAXRETRY' times before kicking the bucket.
416: *
417: * Don't reissue the cmd if in certain states, abnormal
418: * on a reissued cmd or max retry exceeded.
419: */
420: #ifdef HYLOG
421: if (hy_log.hyl_enable != hy_log.hyl_onerr) {
422: hy_log.hyl_enable = hy_log.hyl_onerr;
423: goto logit;
424: }
425: #endif
426: #ifdef DEBUG
427: if (hy_nodebug & 4)
428: hy_debug_flag = 1;
429: printD("hy%d: abnormal interrupt, driver state \"%s\" (%d)\n",
430: unit, hy_state_names[is->hy_state], is->hy_state);
431: printD("\tflags 0x%x ilen %d olen %d lastwcr %d retry %d\n",
432: is->hy_flags, is->hy_ilen, is->hy_olen,
433: is->hy_lastwcr, is->hy_retry);
434: printD("\tsaved: state %d count %d cmd 0x%x ptr 0x%x\n",
435: is->hy_savedstate, is->hy_savedcount,
436: is->hy_savedaddr, is->hy_savedcmd);
437: #endif
438: #ifdef PI13
439: addr->hyd_csr &= ~S_C; /* clear the damned PI-13 */
440: #endif
441: if (is->hy_state == XMITSENT || is->hy_state == XMITDATASENT)
442: is->hy_if.if_oerrors++;
443: if (is->hy_state == RECVSENT || is->hy_state == RECVDATASENT)
444: is->hy_if.if_ierrors++;
445: if (is->hy_state == XMITDATASENT ||
446: is->hy_state == RECVSENT ||
447: is->hy_state == RECVDATASENT ||
448: (is->hy_flags & RQ_REISSUE) != 0 || is->hy_retry > MAXRETRY)
449: hycancel(ui);
450: else {
451: #ifdef DEBUG
452: if (hy_nodebug & 2)
453: hy_debug_flag = 1;
454: #endif
455: is->hy_retry++;
456: is->hy_flags |= RQ_ENDOP | RQ_STATUS | RQ_REISSUE;
457: is->hy_state = IDLE;
458: hyact(ui);
459: }
460: } else {
461: /*
462: * Interrupt is neither normal, abnormal, or interface error.
463: * Ignore it. It's either stacked or a word count 0.
464: */
465: #ifdef HYLOG
466: if (hy_log.hyl_enable != hy_log.hyl_onerr) {
467: hy_log.hyl_enable = hy_log.hyl_onerr;
468: goto logit;
469: }
470: #endif
471: #ifdef DEBUG
472: printD("hy%d: possible stacked interrupt ignored\n", unit);
473: #endif
474: }
475: #ifdef DEBUG
476: printD("hy%d: hyint exit\n\n", unit);
477: #endif
478: hyint_active = 0;
479:
480: }
481:
482: /*
483: * Encapsulate a packet of type family for the local net.
484: */
485: hyoutput(ifp, m0, dst)
486: struct ifnet *ifp;
487: struct mbuf *m0;
488: struct sockaddr *dst;
489: {
490: register struct hym_hdr *hym;
491: register struct mbuf *m;
492: #ifdef HYROUTE
493: register struct hyroute *r = &hy_route[ifp->if_unit];
494: #endif
495: short dtype; /* packet type */
496: int dhost; /* destination adapter address */
497: int dlen;
498: int mplen = 0; /* message proper length */
499: short loopback = 0; /* hardware loopback requested */
500: int error = 0;
501: int s;
502:
503: #ifdef DEBUG
504: if (hy_nodebug & 8)
505: hy_debug_flag = 1;
506: #endif
507: dlen = 0;
508: for (m = m0; m; m = m->m_next)
509: dlen += m->m_len;
510: m = m0;
511: switch(dst->sa_family) {
512:
513: #ifdef INET
514: case AF_INET: {
515: register struct ip *ip = mtod(m, struct ip *);
516: register struct sockaddr_in *sin = (struct sockaddr_in *)dst;
517: register long hostaddr = in_lnaof(sin->sin_addr);
518:
519: dhost = hostaddr & 0xffff;
520: dtype = HYLINK_IP;
521: #ifdef DEBUG
522: printD("hy%d: output to host %x, dhost %x\n",
523: ifp->if_unit, sin->sin_addr.s_addr, dhost);
524: #endif
525: /*
526: * Debugging loopback support:
527: * upper byte of 24 bit host number interpreted as follows
528: * 0x00 --> no loopback
529: * 0x01 --> hardware loop through remote adapter
530: * other --> software loop through remote ip layer
531: */
532: if (hostaddr & 0xff0000) {
533: struct in_addr temp;
534:
535: temp = ip->ip_dst;
536: ip->ip_dst = ip->ip_src;
537: ip->ip_src = temp;
538: if ((hostaddr & 0xff0000) == 0x10000)
539: loopback = H_LOOPBK;
540: }
541: /*
542: * If entire packet won't fit in message proper, just
543: * send hyperchannel hardware header and ip header in
544: * message proper. If that won't fit either, just send
545: * the maximum message proper.
546: *
547: * This insures that the associated data is at least a
548: * TCP/UDP header in length and thus prevents potential
549: * problems with very short word counts.
550: */
551: if (dlen > MPSIZE - sizeof (struct hy_hdr)) {
552: mplen = sizeof(struct hy_hdr) + (ip->ip_hl << 2);
553: if (mplen > MPSIZE)
554: mplen = MPSIZE;
555: }
556: break;
557: }
558: #endif
559:
560: default:
561: printf("hy%d: can't handle af%d\n", ifp->if_unit,
562: dst->sa_family);
563: #ifdef DEBUG
564: if (hy_nodebug & 4)
565: hy_debug_flag = 1;
566: #endif
567: error = EAFNOSUPPORT;
568: goto drop;
569: }
570:
571: /*
572: * Add the software and hardware hyperchannel headers.
573: * If there's not enough space in the first mbuf, allocate another.
574: * If that should fail, drop this sucker.
575: * No extra space for headers is allocated.
576: */
577: if (m->m_off > MMAXOFF ||
578: MMINOFF + sizeof(struct hym_hdr) > m->m_off) {
579: m = m_get(M_DONTWAIT, MT_HEADER);
580: if (m == 0) {
581: m = m0;
582: error = ENOBUFS;
583: goto drop;
584: }
585: m->m_next = m0;
586: m->m_off = MMINOFF;
587: m->m_len = sizeof(struct hym_hdr);
588: } else {
589: m->m_off -= sizeof(struct hym_hdr);
590: m->m_len += sizeof(struct hym_hdr);
591: }
592: hym = mtod(m, struct hym_hdr *);
593: hym->hym_mplen = mplen;
594: hym->hym_hdr.hyh_type = dtype;
595: hym->hym_hdr.hyh_off = 0;
596: hym->hym_hdr.hyh_from = htons((u_short)ifp->if_host[0]);
597: hym->hym_hdr.hyh_param = loopback;
598: #ifdef HYROUTE
599: if (r->hyr_lasttime.tv_sec != 0) {
600: register struct hy_hash *rh;
601: register int i;
602:
603: i = HYRHASH(dhost);
604: rh = &r->hyr_hash[i];
605: i = 0;
606: while (rh->hyr_key != dhost) {
607: rh++; i++;
608: if (rh > &r->hyr_hash[HYRSIZE])
609: rh = &r->hyr_hash[0];
610: if (rh->hyr_flags == 0 || i > HYRSIZE)
611: goto notfound;
612: }
613: if (rh->hyr_flags & HYR_GATE) {
614: loopback = 0; /* no hardware loopback on gateways */
615: i = rh->hyr_nextgate;
616: if (i >= rh->hyr_egate)
617: rh->hyr_nextgate = rh->hyr_pgate;
618: else
619: rh->hyr_nextgate++;
620: rh = &r->hyr_hash[r->hyr_gateway[i]];
621: if ((rh->hyr_flags & HYR_DIR) == 0)
622: goto notfound;
623: }
624: hym->hym_hdr.hyh_ctl = rh->hyr_ctl;
625: hym->hym_hdr.hyh_access = rh->hyr_access;
626: hym->hym_hdr.hyh_to = rh->hyr_dst;
627: } else {
628: hym->hym_hdr.hyh_ctl = H_XTRUNKS | H_RTRUNKS;
629: hym->hym_hdr.hyh_access = 0;
630: hym->hym_hdr.hyh_to = htons((u_short)dhost);
631: }
632: #else
633: hym->hym_hdr.hyh_ctl = H_XTRUNKS | H_RTRUNKS;
634: hym->hym_hdr.hyh_access = 0;
635: hym->hym_hdr.hyh_to = htons(dhost);
636: #endif
637:
638: if (hym->hym_mplen) {
639: hym->hym_hdr.hyh_ctl |= H_ASSOC;
640: #ifdef DEBUG
641: if (hy_nodebug & 16)
642: hy_debug_flag = 1;
643: #endif
644: } else
645: hym->hym_hdr.hyh_ctl &= ~H_ASSOC;
646: #ifdef DEBUG
647: printD("hy%d: output mplen=%x ctl=%x access=%x to=%x",
648: ifp->if_unit, hym->hym_mplen, hym->hym_hdr.hyh_ctl,
649: hym->hym_hdr.hyh_access, hym->hym_hdr.hyh_to);
650: printD(" (adapter %x) from=%x param=%x type=%x off=%x\n",
651: hym->hym_hdr.hyh_to_adapter,
652: hym->hym_hdr.hyh_from, hym->hym_hdr.hyh_param,
653: hym->hym_hdr.hyh_type, hym->hym_hdr.hyh_off);
654: #endif
655: s = splimp();
656: if (IF_QFULL(&ifp->if_snd)) {
657: IF_DROP(&ifp->if_snd);
658: error = ENOBUFS;
659: splx(s);
660: goto drop;
661: }
662: IF_ENQUEUE(&ifp->if_snd, m);
663: if (hy_softc[ifp->if_unit].hy_state == WAITING)
664: hyact(hyinfo[ifp->if_unit]);
665: splx(s);
666: return (0);
667: notfound:
668: error = ENETUNREACH; /* XXX */
669: drop:
670: m_freem(m);
671: return (error);
672: }
673:
674: hyact(ui)
675: register struct uba_device *ui;
676: {
677: register struct hy_softc *is = &hy_softc[ui->ui_unit];
678: register struct hydevice *addr = (struct hydevice *)ui->ui_addr;
679:
680: actloop:
681: #ifdef DEBUG
682: printD("hy%d: hyact, enter state \"%s\"\n", ui->ui_unit,
683: hy_state_names[is->hy_state]);
684: #endif
685: switch (is->hy_state) {
686:
687: case STARTUP:
688: goto endintr;
689:
690: case IDLE: {
691: register rq = is->hy_flags;
692:
693: if (rq & RQ_STATUS) {
694: is->hy_flags &= ~RQ_STATUS;
695: is->hy_state = STATSENT;
696: hystart(ui, HYF_STATUS, sizeof (is->hy_status),
697: is->hy_ifuba.ifu_r.ifrw_info);
698: } else if (rq & RQ_ENDOP) {
699: is->hy_flags &= ~RQ_ENDOP;
700: is->hy_state = ENDOPSENT;
701: hystart(ui, HYF_END_OP, 0, 0);
702: } else if (rq & RQ_STATISTICS) {
703: is->hy_flags &= ~RQ_STATISTICS;
704: is->hy_state = RSTATSENT;
705: hystart(ui, HYF_RSTATS, sizeof (is->hy_stat),
706: is->hy_ifuba.ifu_r.ifrw_info);
707: } else if (HYS_RECVDATA(addr)) {
708: is->hy_state = RECVSENT;
709: is->hy_retry = 0;
710: hystart(ui, HYF_INPUTMSG, MPSIZE,
711: is->hy_ifuba.ifu_r.ifrw_info);
712: } else if (rq & RQ_REISSUE) {
713: is->hy_flags &= ~RQ_REISSUE;
714: is->hy_state = is->hy_savedstate;
715: #ifdef DEBUG
716: printD("hy%d: reissue cmd=0x%x count=%d",
717: ui->ui_unit, is->hy_savedcmd, is->hy_savedcount);
718: printD(" ubaddr=0x%x retry=%d\n",
719: is->hy_savedaddr, is->hy_retry);
720: #endif
721: hystart(ui, is->hy_savedcmd, is->hy_savedcount,
722: is->hy_savedaddr);
723: } else {
724: register struct mbuf *m;
725:
726: IF_DEQUEUE(&is->hy_if.if_snd, m);
727: if (m != NULL) {
728: register struct hym_hdr *hym;
729: register int mplen;
730: register int cmd;
731:
732: is->hy_state = XMITSENT;
733: is->hy_retry = 0;
734: hym = mtod(m, struct hym_hdr *);
735: #ifdef HYLOG
736: hylog(HYL_XMIT, sizeof(struct hym_hdr),
737: (char *)hym);
738: #endif
739: mplen = hym->hym_mplen;
740: if (hym->hym_hdr.hyh_to_adapter ==
741: hym->hym_hdr.hyh_from_adapter)
742: cmd = HYF_XMITLOCMSG;
743: else
744: cmd = HYF_XMITMSG;
745: #ifdef DEBUG
746: printD("hy%d: hym_hdr = ", ui->ui_unit);
747: if (hy_debug_flag)
748: hyprintdata((char *)hym,
749: sizeof (struct hym_hdr));
750: #endif
751: /*
752: * Strip off the software part of
753: * the hyperchannel header
754: */
755: m->m_off += sizeof(struct hym_data);
756: m->m_len -= sizeof(struct hym_data);
757: is->hy_olen = if_wubaput(&is->hy_ifuba, m);
758: if (is->hy_ifuba.ifu_flags & UBA_NEEDBDP)
759: UBAPURGE(is->hy_ifuba.ifu_uba,
760: is->hy_ifuba.ifu_w.ifrw_bdp);
761: #ifdef DEBUG
762: printD(
763: "hy%d: sending packet (mplen = %d, hy_olen = %d) data = ",
764: ui->ui_unit, mplen, is->hy_olen);
765: if (hy_debug_flag)
766: hyprintdata(
767: is->hy_ifuba.ifu_w.ifrw_addr,
768: is->hy_olen);
769: #endif
770: hystart(ui, cmd,
771: (mplen == 0) ? is->hy_olen : mplen,
772: is->hy_ifuba.ifu_w.ifrw_info);
773: if (mplen != 0)
774: is->hy_flags |= RQ_XASSOC;
775: } else if (rq & RQ_MARKDOWN) {
776: is->hy_flags &= ~(RQ_MARKUP | RQ_MARKDOWN);
777: is->hy_state = MARKPORT;
778: is->hy_retry = 0;
779: /*
780: * Port number is taken from status data
781: */
782: hystart(ui,
783: (int)(HYF_MARKP0|(PORTNUM(&is->hy_status)<<2)),
784: 0, 0);
785: } else if (rq & RQ_MARKUP) {
786: register struct ifnet *ifp = &is->hy_if;
787: register struct sockaddr_in *sin =
788: (struct sockaddr_in *)&ifp->if_addr;
789:
790: is->hy_flags &= ~RQ_MARKUP;
791: is->hy_retry = 0;
792: /*
793: * Fill in the host number
794: * from the status buffer
795: */
796: printf(
797: "hy%d: unit number 0x%x port %d type %x microcode level 0x%x\n",
798: ui->ui_unit,
799: is->hy_stat.hyc_uaddr,
800: PORTNUM(&is->hy_status),
801: (is->hy_stat.hyc_atype[0]<<8) |
802: is->hy_stat.hyc_atype[1],
803: is->hy_stat.hyc_atype[2]);
804:
805: ifp->if_host[0] =
806: (is->hy_stat.hyc_uaddr << 8) |
807: PORTNUM(&is->hy_status);
808: sin->sin_addr =
809: if_makeaddr(ifp->if_net, ifp->if_host[0]);
810: ifp->if_flags |= IFF_UP;
811: if_rtinit(ifp, RTF_UP);
812: #ifdef HYLOG
813: hylog(HYL_UP, 0, (char *)0);
814: #endif
815: } else {
816: is->hy_state = WAITING;
817: is->hy_retry = 0;
818: hystart(ui, HYF_WAITFORMSG, 0, 0);
819: }
820: }
821: break;
822: }
823:
824: case STATSENT:
825: bcopy(is->hy_ifuba.ifu_r.ifrw_addr, (caddr_t)&is->hy_status,
826: sizeof (struct hy_status));
827: #ifdef DEBUG
828: printD("hy%d: status - %x %x %x %x %x %x %x %x\n",
829: ui->ui_unit, is->hy_status.hys_gen_status,
830: is->hy_status.hys_last_fcn,
831: is->hy_status.hys_resp_trunk,
832: is->hy_status.hys_status_trunk,
833: is->hy_status.hys_recd_resp,
834: is->hy_status.hys_error,
835: is->hy_status.hys_caddr,
836: is->hy_status.hys_pad);
837: #endif
838: is->hy_state = IDLE;
839: #ifdef HYLOG
840: hylog(HYL_STATUS, sizeof (struct hy_status),
841: (char *)&is->hy_status);
842: #endif
843: #ifdef HYELOG
844: {
845: register int i;
846:
847: i = is->hy_status.hys_error;
848: if (i < HYE_MAX)
849: i = HYE_MAX;
850: switch (is->hy_status.hys_last_fcn) {
851: case HYF_XMITLOCMSG:
852: i += HYE_MAX+1; /* fall through */
853: case HYF_XMITLSTDATA:
854: i += HYE_MAX+1; /* fall through */
855: case HYF_XMITMSG:
856: i += HYE_MAX+1;
857: }
858: hy_elog[i]++;
859: }
860: #endif
861: break;
862:
863: case RSTATSENT: {
864: register struct hy_stat *p =
865: (struct hy_stat *)is->hy_ifuba.ifu_r.ifrw_addr;
866:
867: is->hy_stat.hyc_msgcnt = ntohl(p->hyc_msgcnt);
868: is->hy_stat.hyc_dbcnt = ntohl(p->hyc_dbcnt);
869: is->hy_stat.hyc_tbusy = ntohl(p->hyc_tbusy);
870: is->hy_stat.hyc_hwret = ntohl(p->hyc_hwret);
871: is->hy_stat.hyc_crcbad = ntohl(p->hyc_crcbad);
872: is->hy_stat.hyc_mcret = ntohl(p->hyc_mcret);
873: is->hy_stat.hyc_tdabort = ntohl(p->hyc_tdabort);
874: is->hy_stat.hyc_atype[0] = p->hyc_atype[0];
875: is->hy_stat.hyc_atype[1] = p->hyc_atype[1];
876: is->hy_stat.hyc_atype[2] = p->hyc_atype[2];
877: is->hy_stat.hyc_uaddr = p->hyc_uaddr;
878: #ifdef DEBUG
879: printD(
880: "hy%d: statistics - msgcnt %d dbcnt %d hwret %d tbusy %d crcbad %d\n",
881: ui->ui_unit,
882: is->hy_stat.hyc_msgcnt, is->hy_stat.hyc_dbcnt,
883: is->hy_stat.hyc_tbusy, is->hy_stat.hyc_hwret,
884: is->hy_stat.hyc_crcbad);
885: printD(" mcret %d tdabort %d atype %x %x %x uaddr %x\n",
886: is->hy_stat.hyc_mcret, is->hy_stat.hyc_tdabort,
887: is->hy_stat.hyc_atype[0], is->hy_stat.hyc_atype[1],
888: is->hy_stat.hyc_atype[2], is->hy_stat.hyc_uaddr);
889: #endif
890: is->hy_state = IDLE;
891: #ifdef HYLOG
892: hylog(HYL_STATISTICS, sizeof (struct hy_stat),
893: (char *)&is->hy_stat);
894: #endif
895: break;
896: }
897:
898: case CLEARSENT:
899: is->hy_state = IDLE;
900: break;
901:
902: case ENDOPSENT:
903: is->hy_state = IDLE;
904: break;
905:
906: case RECVSENT: {
907: register struct hy_hdr *hyh;
908: register unsigned len;
909:
910: if (is->hy_ifuba.ifu_flags & UBA_NEEDBDP)
911: UBAPURGE(is->hy_ifuba.ifu_uba,
912: is->hy_ifuba.ifu_r.ifrw_bdp);
913: hyh = (struct hy_hdr *) (is->hy_ifuba.ifu_r.ifrw_addr);
914: len = (0xffff & (addr->hyd_wcr - is->hy_lastwcr)) << 1;
915: if (len > MPSIZE) {
916: printf("hy%d: RECVD MP > MPSIZE (%d)\n",
917: ui->ui_unit, len);
918: #ifdef DEBUG
919: hy_debug_flag = 1;
920: printD("hy%d: csr = 0x%b, bar = 0x%x, wcr = 0x%x\n",
921: ui->ui_unit, addr->hyd_csr, HY_CSR_BITS,
922: addr->hyd_bar, addr->hyd_wcr);
923: #endif
924: }
925: #ifdef DEBUG
926: printD("hy%d: recvd mp, len = %d, data = ", ui->ui_unit, len);
927: if (hy_debug_flag)
928: hyprintdata((char *)hyh, len);
929: #endif
930: if (hyh->hyh_ctl & H_ASSOC) {
931: is->hy_state = RECVDATASENT;
932: is->hy_ilen = len;
933: is->hy_retry = 0;
934: hystart(ui, HYF_INPUTDATA,
935: (int)(HYMTU-len+sizeof (struct hy_hdr)),
936: (int)(is->hy_ifuba.ifu_r.ifrw_info + len));
937: } else {
938: hyrecvdata(ui, hyh, (int)len);
939: is->hy_state = IDLE;
940: }
941: break;
942: }
943:
944: case RECVDATASENT: {
945: register struct hy_hdr *hyh;
946: register unsigned len;
947:
948: if (is->hy_ifuba.ifu_flags & UBA_NEEDBDP)
949: UBAPURGE(is->hy_ifuba.ifu_uba,
950: is->hy_ifuba.ifu_r.ifrw_bdp);
951: hyh = (struct hy_hdr *) (is->hy_ifuba.ifu_r.ifrw_addr);
952: len = (0xffff & (addr->hyd_wcr - is->hy_lastwcr)) << 1;
953: #ifdef DEBUG
954: printD("hy%d: recvd assoc data, len = %d, data = ",
955: ui->ui_unit, len);
956: if (hy_debug_flag)
957: hyprintdata((char *)hyh + is->hy_ilen, len);
958: #endif
959: hyrecvdata(ui, hyh, (int)(len + is->hy_ilen));
960: is->hy_state = IDLE;
961: break;
962: }
963:
964: case XMITSENT:
965: if (is->hy_flags & RQ_XASSOC) {
966: register int len;
967:
968: is->hy_flags &= ~RQ_XASSOC;
969: is->hy_state = XMITDATASENT;
970: is->hy_retry = 0;
971: len = (0xffff & (addr->hyd_wcr - is->hy_lastwcr)) << 1;
972: if (len > is->hy_olen) {
973: printf(
974: "hy%d: xmit error - len > hy_olen [%d > %d]\n",
975: ui->ui_unit, len, is->hy_olen);
976: #ifdef DEBUG
977: hy_debug_flag = 1;
978: #endif
979: }
980: hystart(ui, HYF_XMITLSTDATA, is->hy_olen - len,
981: is->hy_ifuba.ifu_w.ifrw_info + len);
982: break;
983: }
984: /* fall through to ... */
985:
986: case XMITDATASENT:
987: hyxmitdata(ui);
988: is->hy_state = IDLE;
989: break;
990:
991: case WAITING: /* wait for message complete or output requested */
992: if (HYS_RECVDATA(addr))
993: is->hy_state = IDLE;
994: else {
995: is->hy_state = CLEARSENT;
996: is->hy_retry = 0;
997: hystart(ui, HYF_CLRWFMSG, 0, 0);
998: }
999: break;
1000:
1001: case MARKPORT:
1002: is->hy_state = STARTUP;
1003: is->hy_if.if_flags &= ~IFF_UP;
1004: goto endintr;
1005:
1006: default:
1007: printf("hy%d: DRIVER BUG - INVALID STATE %d\n",
1008: ui->ui_unit, is->hy_state);
1009: panic("HYPERCHANNEL IN INVALID STATE");
1010: /*NOTREACHED*/
1011: }
1012: if (is->hy_state == IDLE)
1013: goto actloop;
1014: endintr:
1015: ;
1016: #ifdef DEBUG
1017: printD("hy%d: hyact, exit at \"%s\"\n", ui->ui_unit,
1018: hy_state_names[is->hy_state]);
1019: #endif
1020: }
1021:
1022: /*
1023: * Called from device interrupt when receiving data.
1024: * Examine packet to determine type. Decapsulate packet
1025: * based on type and pass to type specific higher-level
1026: * input routine.
1027: */
1028: hyrecvdata(ui, hyh, len)
1029: struct uba_device *ui;
1030: register struct hy_hdr *hyh;
1031: int len;
1032: {
1033: register struct hy_softc *is = &hy_softc[ui->ui_unit];
1034: struct mbuf *m;
1035: register struct ifqueue *inq;
1036:
1037: is->hy_if.if_ipackets++;
1038: #ifdef DEBUG
1039: printD("hy%d: recieved packet, len = %d (actual %d)\n",
1040: ui->ui_unit, len,
1041: len - (hyh->hyh_off + sizeof (struct hy_hdr)));
1042: #endif
1043: #ifdef HYLOG
1044: {
1045: struct {
1046: short hlen;
1047: struct hy_hdr hhdr;
1048: } hh;
1049: hh.hlen = len;
1050: hh.hhdr = *hyh;
1051: hylog(HYL_RECV, sizeof(hh), (char *)&hh);
1052: }
1053: #endif
1054: if (len > HYMTU + MPSIZE || len == 0)
1055: return; /* sanity */
1056: /*
1057: * Pull packet off interface.
1058: */
1059: m = if_rubaget(&is->hy_ifuba, len, 0);
1060: if (m == NULL)
1061: return;
1062: switch (hyh->hyh_type) {
1063:
1064: #ifdef INET
1065: case HYLINK_IP:
1066: /*
1067: * Strip the variable portion of the hyperchannel header
1068: * (fixed portion stripped in if_rubaget).
1069: */
1070: m->m_len -= hyh->hyh_off;
1071: m->m_off += hyh->hyh_off;
1072: schednetisr(NETISR_IP);
1073: inq = &ipintrq;
1074: break;
1075: #endif
1076: default:
1077: m_freem(m);
1078: return;
1079: }
1080: if (IF_QFULL(inq)) {
1081: IF_DROP(inq);
1082: m_freem(m);
1083: } else
1084: IF_ENQUEUE(inq, m);
1085: }
1086:
1087: /*
1088: * Transmit done, release resources, bump counters.
1089: */
1090: hyxmitdata(ui)
1091: struct uba_device *ui;
1092: {
1093: register struct hy_softc *is = &hy_softc[ui->ui_unit];
1094:
1095: is->hy_if.if_opackets++;
1096: if (is->hy_ifuba.ifu_xtofree) {
1097: m_freem(is->hy_ifuba.ifu_xtofree);
1098: is->hy_ifuba.ifu_xtofree = 0;
1099: }
1100: }
1101:
1102: hycancel(ui)
1103: register struct uba_device *ui;
1104: {
1105: register struct hy_softc *is = &hy_softc[ui->ui_unit];
1106:
1107: if (is->hy_ifuba.ifu_xtofree) {
1108: m_freem(is->hy_ifuba.ifu_xtofree);
1109: is->hy_ifuba.ifu_xtofree = 0;
1110: }
1111: #ifdef DEBUG
1112: if (hy_nodebug & 1)
1113: hy_debug_flag = 1;
1114: #endif
1115: #ifdef DEBUG
1116: printD("hy%d: cancel from state \"%s\" cmd=0x%x count=%d ptr=0x%x\n",
1117: ui->ui_unit, hy_state_names[is->hy_state], is->hy_savedcmd,
1118: is->hy_savedcount, is->hy_savedaddr);
1119: printD("\tflags 0x%x ilen %d olen %d lastwcr %d retry %d\n",
1120: is->hy_flags, is->hy_ilen, is->hy_olen, is->hy_lastwcr,
1121: is->hy_retry);
1122: printD("\tsaved: state %d count %d ptr 0x%x cmd 0x%x\n",
1123: is->hy_savedstate, is->hy_savedcount, is->hy_savedaddr,
1124: is->hy_savedcmd);
1125: #endif
1126: is->hy_state = IDLE;
1127: is->hy_flags |= (RQ_ENDOP | RQ_STATUS);
1128: hyact(ui);
1129: }
1130:
1131: #ifdef DEBUG
1132: hyprintdata(cp, len)
1133: register char *cp;
1134: register int len;
1135: {
1136: register int count = 16;
1137: register char *fmt;
1138: static char regfmt[] = "\n\t %x";
1139:
1140: fmt = ®fmt[2];
1141: while (--len >= 0) {
1142: printL(fmt, *cp++ & 0xff);
1143: fmt = ®fmt[2];
1144: if (--count <= 0) {
1145: fmt = ®fmt[0];
1146: count = 16;
1147: }
1148: }
1149: printL("\n");
1150: }
1151: #endif
1152:
1153: hywatch(unit)
1154: int unit;
1155: {
1156: register struct hy_softc *is = &hy_softc[unit];
1157: register struct uba_device *ui = hyinfo[unit];
1158: register struct hydevice *addr = (struct hydevice *)ui->ui_addr;
1159: int s;
1160:
1161: s = splimp();
1162: is->hy_if.if_timer = SCANINTERVAL;
1163: if (is->hy_ntime > 2 && is->hy_state != WAITING &&
1164: is->hy_state != STARTUP && is->hy_state != IDLE) {
1165: printf("hy%d: watchdog timer expired\n", unit);
1166: hycancel(ui);
1167: }
1168: #ifdef PI13
1169: if ((addr->hyd_csr & S_POWEROFF) != 0) {
1170: addr->hyd_csr |= S_POWEROFF;
1171: DELAY(100);
1172: if ((addr->hyd_csr & S_POWEROFF) == 0) {
1173: printf("hy%d: adapter power restored\n", unit);
1174: is->hy_state = IDLE;
1175: is->hy_flags |=
1176: (RQ_MARKUP | RQ_STATISTICS | RQ_ENDOP | RQ_STATUS);
1177: hyact(ui);
1178: }
1179: }
1180: #endif
1181: splx(s);
1182: }
1183:
1184: #ifdef HYLOG
1185: hylog(code, len, ptr)
1186: int code, len;
1187: char *ptr;
1188: {
1189: register unsigned char *p;
1190: int s;
1191:
1192: s = splimp();
1193: if (hy_log.hyl_self != &hy_log) {
1194: hy_log.hyl_eptr = &hy_log.hyl_buf[HYL_SIZE];
1195: hy_log.hyl_ptr = &hy_log.hyl_buf[0];
1196: hy_log.hyl_self = &hy_log;
1197: hy_log.hyl_enable = HYL_DISABLED;
1198: hy_log.hyl_onerr = HYL_CATCH1;
1199: }
1200: if (hy_log.hyl_enable == HYL_DISABLED ||
1201: hy_log.hyl_enable == HYL_CAUGHT1 ||
1202: hy_log.hyl_enable == HYL_CAUGHTSTATUS ||
1203: (hy_log.hyl_enable == HYL_CATCHSTATUS && code != HYL_STATUS))
1204: goto out;
1205: p = hy_log.hyl_ptr;
1206: if (p + len + 2 >= hy_log.hyl_eptr) {
1207: bzero((caddr_t)p, (unsigned)(hy_log.hyl_eptr - p));
1208: p = &hy_log.hyl_buf[0];
1209: if (hy_log.hyl_enable == HYL_CATCH1) {
1210: hy_log.hyl_enable = hy_log.hyl_onerr = HYL_CAUGHT1;
1211: goto out;
1212: }
1213: if (hy_log.hyl_enable == HYL_CATCHSTATUS) {
1214: hy_log.hyl_enable = hy_log.hyl_onerr = HYL_CAUGHTSTATUS;
1215: goto out;
1216: }
1217: }
1218: *p++ = code;
1219: *p++ = len;
1220: bcopy((caddr_t)ptr, (caddr_t)p, (unsigned)len);
1221: hy_log.hyl_ptr = p + len;
1222: out:
1223: splx(s);
1224: }
1225: #endif
1226:
1227: /*ARGSUSED*/
1228: hyioctl(ifp, cmd, data)
1229: register struct ifnet *ifp;
1230: int cmd;
1231: caddr_t data;
1232: {
1233: struct sockaddr_in *sin;
1234: struct ifreq *ifr = (struct ifreq *)data;
1235: int s = splimp(), error = 0;
1236:
1237: switch(cmd) {
1238:
1239: case SIOCSIFADDR:
1240: if (ifp->if_flags & IFF_RUNNING)
1241: if_rtinit(ifp, -1);
1242: sin = (struct sockaddr_in *)&ifr->ifr_addr;
1243: ifp->if_net = in_netof(sin->sin_addr);
1244: sin = (struct sockaddr_in *)&ifp->if_addr;
1245: sin->sin_family = AF_INET;
1246: sin->sin_addr = if_makeaddr(ifp->if_net, ifp->if_host[0]);
1247: if (ifp->if_flags & IFF_RUNNING)
1248: if_rtinit(ifp, RTF_UP);
1249: else
1250: hyinit(ifp->if_unit);
1251: break;
1252:
1253: case HYSETROUTE:
1254: if (!suser()) {
1255: error = EPERM;
1256: goto bad;
1257: }
1258: hy_route[ifp->if_unit] = *(struct hyroute *)ifr->ifr_data;
1259: hy_route[ifp->if_unit].hyr_lasttime = time;
1260: break;
1261:
1262: case HYGETROUTE:
1263: *(struct hyroute *)ifr->ifr_data = hy_route[ifp->if_unit];
1264: break;
1265:
1266: default:
1267: error = EINVAL;
1268: break;
1269: }
1270: bad:
1271: splx(s);
1272: return (error);
1273: }
1274: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.