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