1*37110Ssklower /* 2*37110Ssklower * Copyright (c) 1989 The Regents of the University of California. 3*37110Ssklower * All rights reserved. 4*37110Ssklower * 5*37110Ssklower * This code is derived from software contributed to Berkeley by 6*37110Ssklower * Excelan Inc. 7*37110Ssklower * 8*37110Ssklower * Redistribution and use in source and binary forms are permitted 9*37110Ssklower * provided that the above copyright notice and this paragraph are 10*37110Ssklower * duplicated in all such forms and that any documentation, 11*37110Ssklower * advertising materials, and other materials related to such 12*37110Ssklower * distribution and use acknowledge that the software was developed 13*37110Ssklower * by the University of California, Berkeley. The name of the 14*37110Ssklower * University may not be used to endorse or promote products derived 15*37110Ssklower * from this software without specific prior written permission. 16*37110Ssklower * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 17*37110Ssklower * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 18*37110Ssklower * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 19*37110Ssklower * 20*37110Ssklower * @(#)if_ex.c 7.1 (Berkeley) 03/09/89 21*37110Ssklower */ 22*37110Ssklower 23*37110Ssklower #include "ex.h" 24*37110Ssklower 25*37110Ssklower #if NEX > 0 26*37110Ssklower 27*37110Ssklower /* 28*37110Ssklower * Excelan EXOS 202(VME) & 203(QBUS) Link Level Ethernet Interface Drivers 29*37110Ssklower */ 30*37110Ssklower #include "param.h" 31*37110Ssklower #include "systm.h" 32*37110Ssklower #include "mbuf.h" 33*37110Ssklower #include "buf.h" 34*37110Ssklower #include "protosw.h" 35*37110Ssklower #include "socket.h" 36*37110Ssklower #include "vmmac.h" 37*37110Ssklower #include "ioctl.h" 38*37110Ssklower #include "errno.h" 39*37110Ssklower #include "vmparam.h" 40*37110Ssklower #include "syslog.h" 41*37110Ssklower #include "uio.h" 42*37110Ssklower 43*37110Ssklower #include "../net/if.h" 44*37110Ssklower #include "../net/netisr.h" 45*37110Ssklower #include "../net/route.h" 46*37110Ssklower 47*37110Ssklower #ifdef INET 48*37110Ssklower #include "../netinet/in.h" 49*37110Ssklower #include "../netinet/in_systm.h" 50*37110Ssklower #include "../netinet/in_var.h" 51*37110Ssklower #include "../netinet/ip.h" 52*37110Ssklower #include "../netinet/if_ether.h" 53*37110Ssklower #endif 54*37110Ssklower 55*37110Ssklower #ifdef NS 56*37110Ssklower #include "../netns/ns.h" 57*37110Ssklower #include "../netns/ns_if.h" 58*37110Ssklower #endif 59*37110Ssklower 60*37110Ssklower #include "../tahoe/cpu.h" 61*37110Ssklower #include "../tahoe/pte.h" 62*37110Ssklower #include "../tahoe/mtpr.h" 63*37110Ssklower 64*37110Ssklower #include "../tahoevba/vbavar.h" 65*37110Ssklower #include "if_exreg.h" 66*37110Ssklower #include "if_vba.h" 67*37110Ssklower 68*37110Ssklower 69*37110Ssklower #define NH2X 32 /* Host to eXcelan request buffers */ 70*37110Ssklower 71*37110Ssklower #define NX2H 16 /* eXcelan to Host reply buffers */ 72*37110Ssklower #define NREC 16 /* Number of RECeive buffers */ 73*37110Ssklower #define NTRB 4 /* Number of TRansmit Buffers */ 74*37110Ssklower #define NVBI (NREC + NTRB) 75*37110Ssklower 76*37110Ssklower #define EXWATCHINTVL 10 /* call exwatch every x secs */ 77*37110Ssklower 78*37110Ssklower int exprobe(), exslave(), exattach(), exintr(), exstart(); 79*37110Ssklower struct vba_device *exinfo[NEX]; 80*37110Ssklower 81*37110Ssklower long exstd[] = { 0 }; 82*37110Ssklower 83*37110Ssklower 84*37110Ssklower struct vba_driver exdriver = 85*37110Ssklower { exprobe, 0, exattach, exstart, exstd, "ex", exinfo }; 86*37110Ssklower int exinit(),ether_output(),exioctl(),exreset(),exwatch(); 87*37110Ssklower struct ex_msg *exgetcbuf(); 88*37110Ssklower int ex_ncall = 0; /* counts calls to exprobe */ 89*37110Ssklower u_long busoff; 90*37110Ssklower 91*37110Ssklower /* 92*37110Ssklower * Ethernet software status per interface. 93*37110Ssklower * 94*37110Ssklower * Each interface is referenced by a network interface structure, xs_if, which 95*37110Ssklower * the routing code uses to locate the interface. This structure contains the 96*37110Ssklower * output queue for the interface, its address, ... NOTE: To configure multiple 97*37110Ssklower * controllers, the sizeof this structure must be a multiple of 16 (xs_h2xhdr). 98*37110Ssklower */ 99*37110Ssklower struct ex_softc { 100*37110Ssklower struct arpcom xs_ac; /* Ethernet common part */ 101*37110Ssklower #define xs_if xs_ac.ac_if /* network-visible interface */ 102*37110Ssklower #define xs_addr xs_ac.ac_enaddr /* hardware Ethernet address */ 103*37110Ssklower int xs_flags; /* private flags */ 104*37110Ssklower #define EX_XPENDING 1 /* xmit rqst pending on EXOS */ 105*37110Ssklower #define EX_STATPENDING (1<<1) /* stats rqst pending on EXOS */ 106*37110Ssklower #define EX_RUNNING (1<<2) /* board is running */ 107*37110Ssklower #define EX_SETADDR (1<<3) /* physaddr has been changed */ 108*37110Ssklower int xs_cvec; /* probe stores cvec here */ 109*37110Ssklower short xs_enetunit; /* unit number for enet filtering */ 110*37110Ssklower short xs_enetinit; /* enet inetrface is initialized */ 111*37110Ssklower struct ex_msg *xs_h2xnext; /* host pointer to request queue */ 112*37110Ssklower struct ex_msg *xs_x2hnext; /* host pointer to reply queue */ 113*37110Ssklower u_long xs_qbaddr; /* map info for structs below */ 114*37110Ssklower /* the following structures are always mapped in */ 115*37110Ssklower u_short xs_h2xhdr; /* EXOS's request queue header */ 116*37110Ssklower u_short xs_x2hhdr; /* EXOS's reply queue header */ 117*37110Ssklower struct ex_msg xs_h2xent[NH2X];/* request msg buffers */ 118*37110Ssklower struct ex_msg xs_x2hent[NX2H];/* reply msg buffers */ 119*37110Ssklower struct confmsg xs_cm; /* configuration message */ 120*37110Ssklower struct stat_array xs_xsa; /* EXOS writes stats here */ 121*37110Ssklower /* end mapped area */ 122*37110Ssklower #define BUSADDR(x) ((0x3D000000 | ((u_long)(kvtophys(x))&0xFFFFFF)) + busoff) 123*37110Ssklower #define P_BUSADDR(x) ((0x3D000000 | ((u_long)(kvtophys(x))&0xFFFFF0)) + busoff) 124*37110Ssklower #define INCORE_BASE(p) (((u_long)(&(p)->xs_h2xhdr)) & 0xFFFFFFF0) 125*37110Ssklower #define RVAL_OFF(n) ((u_long)(&(ex_softc[0].n)) - INCORE_BASE(&ex_softc[0])) 126*37110Ssklower #define LVAL_OFF(n) ((u_long)(ex_softc[0].n) - INCORE_BASE(&ex_softc[0])) 127*37110Ssklower #define H2XHDR_OFFSET RVAL_OFF(xs_h2xhdr) 128*37110Ssklower #define X2HHDR_OFFSET RVAL_OFF(xs_x2hhdr) 129*37110Ssklower #define H2XENT_OFFSET LVAL_OFF(xs_h2xent) 130*37110Ssklower #define X2HENT_OFFSET LVAL_OFF(xs_x2hent) 131*37110Ssklower #define CM_OFFSET RVAL_OFF(xs_cm) 132*37110Ssklower #define SA_OFFSET RVAL_OFF(xs_xsa) 133*37110Ssklower #define FreePkBuf(b) (((b)->iff_mbuf = (struct mbuf *)xs->xs_pkblist),\ 134*37110Ssklower (xs->xs_pkblist = b)) 135*37110Ssklower struct ifvba xs_vbinfo[NVBI];/* Bus Resources (low core) */ 136*37110Ssklower struct ifvba *xs_pkblist; /* free list of above */ 137*37110Ssklower char xs_nrec; /* number of pending receive buffers */ 138*37110Ssklower char xs_ntrb; /* number of pending transmit buffers */ 139*37110Ssklower char pad[6]; /* make BUSADDR macros */ 140*37110Ssklower } ex_softc[NEX]; 141*37110Ssklower 142*37110Ssklower int ex_padcheck = sizeof (struct ex_softc); 143*37110Ssklower 144*37110Ssklower exprobe(reg, vi) 145*37110Ssklower caddr_t reg; 146*37110Ssklower struct vba_device *vi; 147*37110Ssklower { 148*37110Ssklower register br, cvec; /* r12, r11 value-result */ 149*37110Ssklower register struct exdevice *exaddr = (struct exdevice *)reg; 150*37110Ssklower int i; 151*37110Ssklower 152*37110Ssklower if (badaddr(exaddr, 2)) 153*37110Ssklower return 0; 154*37110Ssklower /* 155*37110Ssklower * Reset EXOS and run self-test (should complete within 2 seconds). 156*37110Ssklower */ 157*37110Ssklower movow(&exaddr->ex_porta, EX_RESET); 158*37110Ssklower for (i = 1000000; i; i--) { 159*37110Ssklower uncache(&(exaddr->ex_portb)); 160*37110Ssklower if (exaddr->ex_portb & EX_TESTOK) 161*37110Ssklower break; 162*37110Ssklower } 163*37110Ssklower if ((exaddr->ex_portb & EX_TESTOK) == 0) 164*37110Ssklower return 0; 165*37110Ssklower br = 0x15; 166*37110Ssklower cvec = --vi->ui_hd->vh_lastiv; 167*37110Ssklower ex_softc[vi->ui_unit].xs_cvec = cvec; 168*37110Ssklower ex_ncall++; 169*37110Ssklower return (sizeof(struct exdevice)); 170*37110Ssklower } 171*37110Ssklower 172*37110Ssklower /* 173*37110Ssklower * Interface exists: make available by filling in network interface record. 174*37110Ssklower * System will initialize the interface when it is ready to accept packets. 175*37110Ssklower * A NET_ADDRS command is done to get the ethernet address. 176*37110Ssklower */ 177*37110Ssklower exattach(ui) 178*37110Ssklower register struct vba_device *ui; 179*37110Ssklower { 180*37110Ssklower register struct ex_softc *xs = &ex_softc[ui->ui_unit]; 181*37110Ssklower register struct ifnet *ifp = &xs->xs_if; 182*37110Ssklower register struct exdevice *exaddr = (struct exdevice *)ui->ui_addr; 183*37110Ssklower register struct ex_msg *bp; 184*37110Ssklower 185*37110Ssklower ifp->if_unit = ui->ui_unit; 186*37110Ssklower ifp->if_name = "ex"; 187*37110Ssklower ifp->if_mtu = ETHERMTU; 188*37110Ssklower ifp->if_init = exinit; 189*37110Ssklower ifp->if_ioctl = exioctl; 190*37110Ssklower ifp->if_output = ether_output; 191*37110Ssklower ifp->if_reset = exreset; 192*37110Ssklower ifp->if_start = exstart; 193*37110Ssklower 194*37110Ssklower if (if_vbareserve(xs->xs_vbinfo, NVBI, EXMAXRBUF) == 0) 195*37110Ssklower return; 196*37110Ssklower /* 197*37110Ssklower * Temporarily map queues in order to configure EXOS 198*37110Ssklower */ 199*37110Ssklower xs->xs_qbaddr = INCORE_BASE(xs); 200*37110Ssklower exconfig(ui, 0); /* without interrupts */ 201*37110Ssklower if (xs->xs_cm.cm_cc) 202*37110Ssklower return; /* bad conf */ 203*37110Ssklower /* 204*37110Ssklower * Get Ethernet address. 205*37110Ssklower */ 206*37110Ssklower if ((bp = exgetcbuf(xs, LLNET_ADDRS)) == (struct ex_msg *)0) 207*37110Ssklower panic("exattach"); 208*37110Ssklower bp->mb_na.na_mask = READ_OBJ; 209*37110Ssklower bp->mb_na.na_slot = PHYSSLOT; 210*37110Ssklower bp->mb_status |= MH_EXOS; 211*37110Ssklower movow(&exaddr->ex_portb, EX_NTRUPT); 212*37110Ssklower bp = xs->xs_x2hnext; 213*37110Ssklower do { 214*37110Ssklower uncache(&bp->mb_status); 215*37110Ssklower } while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */ 216*37110Ssklower printf("ex%d: HW %c.%c NX %c.%c, hardware address %s\n", 217*37110Ssklower ui->ui_unit, xs->xs_cm.cm_vc[2], xs->xs_cm.cm_vc[3], 218*37110Ssklower xs->xs_cm.cm_vc[0], xs->xs_cm.cm_vc[1], 219*37110Ssklower ether_sprintf(bp->mb_na.na_addrs)); 220*37110Ssklower bcopy((caddr_t)bp->mb_na.na_addrs, (caddr_t)xs->xs_addr, 221*37110Ssklower sizeof(xs->xs_addr)); 222*37110Ssklower if_attach(ifp); 223*37110Ssklower } 224*37110Ssklower 225*37110Ssklower /* 226*37110Ssklower * Reset of interface after BUS reset. 227*37110Ssklower * If interface is on specified vba, reset its state. 228*37110Ssklower */ 229*37110Ssklower exreset(unit) 230*37110Ssklower int unit; 231*37110Ssklower { 232*37110Ssklower register struct vba_device *ui; 233*37110Ssklower 234*37110Ssklower if (unit >= NEX || (ui = exinfo[unit]) == 0 || ui->ui_alive == 0) 235*37110Ssklower return; 236*37110Ssklower printf(" ex%d", unit); 237*37110Ssklower ex_softc[unit].xs_if.if_flags &= ~IFF_RUNNING; 238*37110Ssklower ex_softc[unit].xs_flags &= ~EX_RUNNING; 239*37110Ssklower 240*37110Ssklower exinit(unit); 241*37110Ssklower } 242*37110Ssklower 243*37110Ssklower /* 244*37110Ssklower * Initialization of interface; clear recorded pending operations, and 245*37110Ssklower * reinitialize BUS usage. Called at boot time, and at ifconfig time via 246*37110Ssklower * exioctl, with interrupts disabled. 247*37110Ssklower */ 248*37110Ssklower exinit(unit) 249*37110Ssklower int unit; 250*37110Ssklower { 251*37110Ssklower register struct ex_softc *xs = &ex_softc[unit]; 252*37110Ssklower register struct vba_device *ui = exinfo[unit]; 253*37110Ssklower register struct exdevice *exaddr = (struct exdevice *)ui->ui_addr; 254*37110Ssklower register struct ifnet *ifp = &xs->xs_if; 255*37110Ssklower register struct sockaddr_in *sin; 256*37110Ssklower register struct ex_msg *bp; 257*37110Ssklower int s; 258*37110Ssklower 259*37110Ssklower /* not yet, if address still unknown */ 260*37110Ssklower if (ifp->if_addrlist == (struct ifaddr *)0) 261*37110Ssklower return; 262*37110Ssklower if (xs->xs_flags & EX_RUNNING) 263*37110Ssklower return; 264*37110Ssklower 265*37110Ssklower xs->xs_qbaddr = INCORE_BASE(xs); 266*37110Ssklower exconfig(ui, 4); /* with vectored interrupts*/ 267*37110Ssklower 268*37110Ssklower /* 269*37110Ssklower * Put EXOS on the Ethernet, using NET_MODE command 270*37110Ssklower */ 271*37110Ssklower if ((bp = exgetcbuf(xs, LLNET_MODE)) == (struct ex_msg *)0) 272*37110Ssklower panic("exinit"); 273*37110Ssklower bp->mb_nm.nm_mask = WRITE_OBJ; 274*37110Ssklower bp->mb_nm.nm_optn = 0; 275*37110Ssklower bp->mb_nm.nm_mode = MODE_PERF; 276*37110Ssklower bp->mb_status |= MH_EXOS; 277*37110Ssklower movow(&exaddr->ex_portb, EX_NTRUPT); 278*37110Ssklower bp = xs->xs_x2hnext; 279*37110Ssklower do { 280*37110Ssklower uncache(&bp->mb_status); 281*37110Ssklower } while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */ 282*37110Ssklower bp->mb_length = MBDATALEN; 283*37110Ssklower bp->mb_status |= MH_EXOS; /* free up buffer */ 284*37110Ssklower movow(&exaddr->ex_portb, EX_NTRUPT); 285*37110Ssklower xs->xs_x2hnext = xs->xs_x2hnext->mb_next; 286*37110Ssklower 287*37110Ssklower ifp->if_watchdog = exwatch; 288*37110Ssklower ifp->if_timer = EXWATCHINTVL; 289*37110Ssklower s = splimp(); /* are interrupts disabled here, anyway? */ 290*37110Ssklower exhangrcv(unit); 291*37110Ssklower xs->xs_if.if_flags |= IFF_RUNNING; 292*37110Ssklower xs->xs_flags |= EX_RUNNING; 293*37110Ssklower if (xs->xs_flags & EX_SETADDR) 294*37110Ssklower ex_setaddr((u_char *)0, unit); 295*37110Ssklower exstart(&ex_softc[unit].xs_if); /* start transmits */ 296*37110Ssklower splx(s); /* are interrupts disabled here, anyway? */ 297*37110Ssklower } 298*37110Ssklower 299*37110Ssklower /* 300*37110Ssklower * Reset, test, and configure EXOS. It is called by exinit, and exattach. 301*37110Ssklower * Returns 0 if successful, 1 if self-test failed. 302*37110Ssklower */ 303*37110Ssklower exconfig(ui, itype) 304*37110Ssklower struct vba_device *ui; 305*37110Ssklower int itype; 306*37110Ssklower { 307*37110Ssklower register int unit = ui->ui_unit; 308*37110Ssklower register struct ex_softc *xs = &ex_softc[unit]; 309*37110Ssklower register struct exdevice *exaddr = (struct exdevice *) ui->ui_addr; 310*37110Ssklower register struct confmsg *cm = &xs->xs_cm; 311*37110Ssklower register struct ex_msg *bp; 312*37110Ssklower register struct ifvba *pkb; 313*37110Ssklower int i; 314*37110Ssklower u_long shiftreg; 315*37110Ssklower static u_char cmaddr[8] = {0xFF, 0xFF, 0, 0}; 316*37110Ssklower 317*37110Ssklower xs->xs_flags = 0; 318*37110Ssklower /* 319*37110Ssklower * Reset EXOS, wait for self-test to complete 320*37110Ssklower */ 321*37110Ssklower movow(&exaddr->ex_porta, EX_RESET); 322*37110Ssklower do { 323*37110Ssklower uncache(&exaddr->ex_portb); 324*37110Ssklower } while ((exaddr->ex_portb & EX_TESTOK) == 0) ; 325*37110Ssklower /* 326*37110Ssklower * Set up configuration message. 327*37110Ssklower */ 328*37110Ssklower cm->cm_1rsrv = 1; 329*37110Ssklower cm->cm_cc = 0xFF; 330*37110Ssklower cm->cm_opmode = 0; /* link-level controller mode */ 331*37110Ssklower cm->cm_dfo = 0x0101; /* enable host data order conversion */ 332*37110Ssklower cm->cm_dcn1 = 1; 333*37110Ssklower cm->cm_2rsrv[0] = cm->cm_2rsrv[1] = 0; 334*37110Ssklower cm->cm_ham = 3; /* absolute address mode */ 335*37110Ssklower cm->cm_3rsrv = 0; 336*37110Ssklower cm->cm_mapsiz = 0; 337*37110Ssklower cm->cm_byteptrn[0] = 0x01; /* EXOS deduces data order of host */ 338*37110Ssklower cm->cm_byteptrn[1] = 0x03; /* by looking at this pattern */ 339*37110Ssklower cm->cm_byteptrn[2] = 0x07; 340*37110Ssklower cm->cm_byteptrn[3] = 0x0F; 341*37110Ssklower cm->cm_wordptrn[0] = 0x0103; 342*37110Ssklower cm->cm_wordptrn[1] = 0x070F; 343*37110Ssklower cm->cm_lwordptrn = 0x0103070F; 344*37110Ssklower for (i=0; i<20; i++) cm->cm_rsrvd[i] = 0; 345*37110Ssklower cm->cm_mba = 0xFFFFFFFF; 346*37110Ssklower cm->cm_nproc = 0xFF; 347*37110Ssklower cm->cm_nmbox = 0xFF; 348*37110Ssklower cm->cm_nmcast = 0xFF; 349*37110Ssklower cm->cm_nhost = 1; 350*37110Ssklower cm->cm_h2xba = P_BUSADDR(xs->xs_qbaddr); 351*37110Ssklower cm->cm_h2xhdr = H2XHDR_OFFSET; 352*37110Ssklower cm->cm_h2xtyp = 0; /* should never wait for rqst buffer */ 353*37110Ssklower cm->cm_x2hba = cm->cm_h2xba; 354*37110Ssklower cm->cm_x2hhdr = X2HHDR_OFFSET; 355*37110Ssklower cm->cm_x2htyp = itype; /* 0 for none, 4 for vectored */ 356*37110Ssklower cm->cm_x2haddr = xs->xs_cvec; /* ivec allocated in exprobe */ 357*37110Ssklower /* 358*37110Ssklower * Set up message queues and headers. 359*37110Ssklower * First the request queue 360*37110Ssklower */ 361*37110Ssklower for (bp = &xs->xs_h2xent[0]; bp < &xs->xs_h2xent[NH2X]; bp++) { 362*37110Ssklower bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs)); 363*37110Ssklower bp->mb_rsrv = 0; 364*37110Ssklower bp->mb_length = MBDATALEN; 365*37110Ssklower bp->mb_status = MH_HOST; 366*37110Ssklower bp->mb_next = bp+1; 367*37110Ssklower } 368*37110Ssklower xs->xs_h2xhdr = xs->xs_h2xent[NH2X-1].mb_link = (u_short)H2XENT_OFFSET; 369*37110Ssklower xs->xs_h2xnext = xs->xs_h2xent[NH2X-1].mb_next = xs->xs_h2xent; 370*37110Ssklower 371*37110Ssklower /* Now the reply queue. */ 372*37110Ssklower for (bp = &xs->xs_x2hent[0]; bp < &xs->xs_x2hent[NX2H]; bp++) { 373*37110Ssklower bp->mb_link = (u_short)((char *)(bp+1)-INCORE_BASE(xs)); 374*37110Ssklower bp->mb_rsrv = 0; 375*37110Ssklower bp->mb_length = MBDATALEN; 376*37110Ssklower bp->mb_status = MH_EXOS; 377*37110Ssklower bp->mb_next = bp+1; 378*37110Ssklower } 379*37110Ssklower xs->xs_x2hhdr = xs->xs_x2hent[NX2H-1].mb_link = (u_short)X2HENT_OFFSET; 380*37110Ssklower xs->xs_x2hnext = xs->xs_x2hent[NX2H-1].mb_next = xs->xs_x2hent; 381*37110Ssklower xs->xs_nrec = 0; 382*37110Ssklower xs->xs_ntrb = 0; 383*37110Ssklower xs->xs_pkblist = xs->xs_vbinfo + NVBI - 1; 384*37110Ssklower for (pkb = xs->xs_pkblist; pkb >= xs->xs_vbinfo; pkb--) 385*37110Ssklower pkb->iff_mbuf = (struct mbuf *)(pkb - 1); 386*37110Ssklower xs->xs_vbinfo[0].iff_mbuf = 0; 387*37110Ssklower 388*37110Ssklower /* 389*37110Ssklower * Write config msg address to EXOS and wait for configuration to 390*37110Ssklower * complete (guaranteed response within 2 seconds). 391*37110Ssklower */ 392*37110Ssklower shiftreg = P_BUSADDR(xs->xs_qbaddr) + CM_OFFSET; 393*37110Ssklower for (i = 4; i < 8; i++) { 394*37110Ssklower cmaddr[i] = (u_char)(shiftreg & 0xFF); 395*37110Ssklower shiftreg >>= 8; 396*37110Ssklower } 397*37110Ssklower for (i = 0; i < 8; i++) { 398*37110Ssklower do { 399*37110Ssklower uncache(&exaddr->ex_portb); 400*37110Ssklower } while (exaddr->ex_portb & EX_UNREADY) ; 401*37110Ssklower DELAY(500); 402*37110Ssklower movow(&exaddr->ex_portb, cmaddr[i]); 403*37110Ssklower } 404*37110Ssklower for (i = 500000; i; --i) { 405*37110Ssklower DELAY(10); 406*37110Ssklower uncache(&cm->cm_cc); 407*37110Ssklower if (cm->cm_cc != 0xFF) 408*37110Ssklower break; 409*37110Ssklower } 410*37110Ssklower if (cm->cm_cc) 411*37110Ssklower printf("ex%d: configuration failed; cc=%x\n", unit, cm->cm_cc); 412*37110Ssklower } 413*37110Ssklower 414*37110Ssklower /* 415*37110Ssklower * Start or re-start output on interface. Get another datagram to send off of 416*37110Ssklower * the interface queue, and map it to the interface before starting the output. 417*37110Ssklower * This routine is called by exinit(), exoutput(), and excdint(). In all cases, 418*37110Ssklower * interrupts by EXOS are disabled. 419*37110Ssklower */ 420*37110Ssklower exstart(ifp) 421*37110Ssklower struct ifnet *ifp; 422*37110Ssklower { 423*37110Ssklower int unit = ifp->if_unit; 424*37110Ssklower struct vba_device *ui = exinfo[unit]; 425*37110Ssklower register struct ex_softc *xs = &ex_softc[unit]; 426*37110Ssklower struct exdevice *exaddr = (struct exdevice *)ui->ui_addr; 427*37110Ssklower register struct ex_msg *bp; 428*37110Ssklower register struct mbuf *m; 429*37110Ssklower int len; 430*37110Ssklower register struct ifvba *pkb; 431*37110Ssklower struct mbuf *m0; 432*37110Ssklower register int nb, tlen; 433*37110Ssklower union l_util { 434*37110Ssklower u_long l; 435*37110Ssklower struct i86_long i; 436*37110Ssklower } l_util; 437*37110Ssklower 438*37110Ssklower if (xs->xs_ntrb >= NTRB) 439*37110Ssklower return; 440*37110Ssklower if (xs->xs_pkblist == 0) { 441*37110Ssklower printf("ex%d: vbinfo exhausted, would panic", unit); 442*37110Ssklower return; 443*37110Ssklower } 444*37110Ssklower IF_DEQUEUE(&xs->xs_if.if_snd, m); 445*37110Ssklower if (m == 0) 446*37110Ssklower return; 447*37110Ssklower /* 448*37110Ssklower * Get a transmit request. 449*37110Ssklower */ 450*37110Ssklower if ((bp = exgetcbuf(xs, LLRTRANSMIT)) == (struct ex_msg *)0) { 451*37110Ssklower m_freem(m); 452*37110Ssklower printf("exstart: no command buffers\n"); 453*37110Ssklower return; 454*37110Ssklower } 455*37110Ssklower xs->xs_ntrb++; 456*37110Ssklower bp->mb_pkb = pkb = xs->xs_pkblist; 457*37110Ssklower xs->xs_pkblist = (struct ifvba *)pkb->iff_mbuf; 458*37110Ssklower nb = 0; tlen = 0; m0 = 0; 459*37110Ssklower pkb->iff_mbuf = m; /* save mbuf pointer to free when done */ 460*37110Ssklower /* 461*37110Ssklower * point directly to the first group of mbufs to be transmitted. The 462*37110Ssklower * hardware can only support NFRAGMENTS descriptors. 463*37110Ssklower */ 464*37110Ssklower while (m && ((nb < NFRAGMENTS-1) || (m->m_next == 0)) ) { 465*37110Ssklower l_util.l = BUSADDR(mtod(m, char *)); 466*37110Ssklower bp->mb_et.et_blks[nb].bb_len = (u_short)m->m_len; 467*37110Ssklower bp->mb_et.et_blks[nb].bb_addr = l_util.i; 468*37110Ssklower tlen += m->m_len; 469*37110Ssklower m0 = m; 470*37110Ssklower m = m->m_next; 471*37110Ssklower nb++; 472*37110Ssklower } 473*37110Ssklower 474*37110Ssklower /* 0 end of chain pointed to by iff_mbuf, to be freed when xmit done */ 475*37110Ssklower if (m0) 476*37110Ssklower m0->m_next = 0; 477*37110Ssklower 478*37110Ssklower /* 479*37110Ssklower * if not all of the descriptors would fit then merge remaining data 480*37110Ssklower * into the transmit buffer, and point to it. Note: the mbufs are freed 481*37110Ssklower * during the merge, they do not have to be freed when we get the 482*37110Ssklower * transmit interrupt. 483*37110Ssklower */ 484*37110Ssklower if (m) { 485*37110Ssklower len = if_vbaput(pkb->iff_buffer, m); 486*37110Ssklower l_util.l = BUSADDR(pkb->iff_buffer); 487*37110Ssklower bp->mb_et.et_blks[nb].bb_len = (u_short)len; 488*37110Ssklower bp->mb_et.et_blks[nb].bb_addr = l_util.i; 489*37110Ssklower tlen += len; 490*37110Ssklower nb++; 491*37110Ssklower } 492*37110Ssklower 493*37110Ssklower /* 494*37110Ssklower * If the total length of the packet is too small, pad the last frag 495*37110Ssklower */ 496*37110Ssklower if (tlen - sizeof(struct ether_header) < ETHERMIN) { 497*37110Ssklower len = (ETHERMIN + sizeof(struct ether_header)) - tlen; 498*37110Ssklower bp->mb_et.et_blks[nb-1].bb_len += (u_short)len; 499*37110Ssklower tlen += len; 500*37110Ssklower } 501*37110Ssklower 502*37110Ssklower /* set number of fragments in descriptor */ 503*37110Ssklower bp->mb_et.et_nblock = nb; 504*37110Ssklower bp->mb_status |= MH_EXOS; 505*37110Ssklower movow(&exaddr->ex_portb, EX_NTRUPT); 506*37110Ssklower } 507*37110Ssklower 508*37110Ssklower /* 509*37110Ssklower * interrupt service routine. 510*37110Ssklower */ 511*37110Ssklower exintr(unit) 512*37110Ssklower int unit; 513*37110Ssklower { 514*37110Ssklower register struct ex_softc *xs = &ex_softc[unit]; 515*37110Ssklower register struct ex_msg *bp = xs->xs_x2hnext; 516*37110Ssklower struct vba_device *ui = exinfo[unit]; 517*37110Ssklower struct exdevice *exaddr = (struct exdevice *)ui->ui_addr; 518*37110Ssklower 519*37110Ssklower uncache(&bp->mb_status); 520*37110Ssklower while ((bp->mb_status & MH_OWNER) == MH_HOST) { 521*37110Ssklower switch (bp->mb_rqst) { 522*37110Ssklower case LLRECEIVE: 523*37110Ssklower if (--xs->xs_nrec < 0) 524*37110Ssklower xs->xs_nrec = 0; 525*37110Ssklower exrecv(unit, bp); 526*37110Ssklower FreePkBuf(bp->mb_pkb); 527*37110Ssklower bp->mb_pkb = (struct ifvba *)0; 528*37110Ssklower exhangrcv(unit); 529*37110Ssklower break; 530*37110Ssklower 531*37110Ssklower case LLTRANSMIT: 532*37110Ssklower case LLRTRANSMIT: 533*37110Ssklower if (--xs->xs_ntrb < 0) 534*37110Ssklower xs->xs_ntrb = 0; 535*37110Ssklower xs->xs_if.if_opackets++; 536*37110Ssklower if (bp->mb_rply == LL_OK) 537*37110Ssklower ; 538*37110Ssklower else if (bp->mb_rply & LLXM_1RTRY) 539*37110Ssklower xs->xs_if.if_collisions++; 540*37110Ssklower else if (bp->mb_rply & LLXM_RTRYS) 541*37110Ssklower xs->xs_if.if_collisions += 2; /* guess */ 542*37110Ssklower else if (bp->mb_rply & LLXM_ERROR) 543*37110Ssklower if (xs->xs_if.if_oerrors++ % 100 == 0) 544*37110Ssklower printf("ex%d: 100 transmit errors=%b\n", 545*37110Ssklower unit, bp->mb_rply, XMIT_BITS); 546*37110Ssklower if (bp->mb_pkb->iff_mbuf) { 547*37110Ssklower m_freem(bp->mb_pkb->iff_mbuf); 548*37110Ssklower bp->mb_pkb->iff_mbuf = (struct mbuf *)0; 549*37110Ssklower } 550*37110Ssklower FreePkBuf(bp->mb_pkb); 551*37110Ssklower bp->mb_pkb = (struct ifvba *)0; 552*37110Ssklower exstart(&ex_softc[unit].xs_if); 553*37110Ssklower exhangrcv(unit); 554*37110Ssklower break; 555*37110Ssklower 556*37110Ssklower case LLNET_STSTCS: 557*37110Ssklower xs->xs_if.if_ierrors += xs->xs_xsa.sa_crc; 558*37110Ssklower xs->xs_flags &= ~EX_STATPENDING; 559*37110Ssklower break; 560*37110Ssklower 561*37110Ssklower default: 562*37110Ssklower printf("ex%d: unknown reply 0x%x", unit, bp->mb_rqst); 563*37110Ssklower } 564*37110Ssklower bp->mb_length = MBDATALEN; 565*37110Ssklower bp->mb_status |= MH_EXOS; /* free up buffer */ 566*37110Ssklower movow(&exaddr->ex_portb, EX_NTRUPT); /* tell EXOS about it */ 567*37110Ssklower bp = bp->mb_next; 568*37110Ssklower uncache(&bp->mb_status); 569*37110Ssklower } 570*37110Ssklower xs->xs_x2hnext = bp; 571*37110Ssklower } 572*37110Ssklower 573*37110Ssklower /* 574*37110Ssklower * Get a request buffer, fill in standard values, advance pointer. 575*37110Ssklower */ 576*37110Ssklower struct ex_msg * 577*37110Ssklower exgetcbuf(xs, req) 578*37110Ssklower struct ex_softc *xs; 579*37110Ssklower int req; 580*37110Ssklower { 581*37110Ssklower register struct ex_msg *bp; 582*37110Ssklower struct ifvba *pkb; 583*37110Ssklower int s = splimp(); 584*37110Ssklower 585*37110Ssklower bp = xs->xs_h2xnext; 586*37110Ssklower uncache(&bp->mb_status); 587*37110Ssklower if ((bp->mb_status & MH_OWNER) == MH_EXOS) { 588*37110Ssklower splx(s); 589*37110Ssklower return (struct ex_msg *)0; 590*37110Ssklower } 591*37110Ssklower xs->xs_h2xnext = bp->mb_next; 592*37110Ssklower bp->mb_1rsrv = 0; 593*37110Ssklower bp->mb_rqst = req; 594*37110Ssklower bp->mb_length = MBDATALEN; 595*37110Ssklower bp->mb_pkb = (struct ifvba *)0; 596*37110Ssklower splx(s); 597*37110Ssklower return bp; 598*37110Ssklower } 599*37110Ssklower 600*37110Ssklower /* 601*37110Ssklower * Process Ethernet receive completion: If input error just drop packet, 602*37110Ssklower * otherwise examine packet to determine type. If can't determine length from 603*37110Ssklower * type, then have to drop packet, otherwise decapsulate packet based on type 604*37110Ssklower * and pass to type-specific higher-level input routine. 605*37110Ssklower */ 606*37110Ssklower exrecv(unit, bp) 607*37110Ssklower int unit; 608*37110Ssklower register struct ex_msg *bp; 609*37110Ssklower { 610*37110Ssklower register struct ex_softc *xs = &ex_softc[unit]; 611*37110Ssklower register struct ether_header *eh; 612*37110Ssklower register struct mbuf *m; 613*37110Ssklower int len, off, resid; 614*37110Ssklower register struct ifqueue *inq; 615*37110Ssklower int s; 616*37110Ssklower 617*37110Ssklower xs->xs_if.if_ipackets++; 618*37110Ssklower /* total length - header - crc */ 619*37110Ssklower len = bp->mb_er.er_blks[0].bb_len - sizeof(struct ether_header) - 4; 620*37110Ssklower if (bp->mb_rply != LL_OK) { 621*37110Ssklower if (xs->xs_if.if_ierrors++ % 100 == 0) 622*37110Ssklower printf("ex%d: 100 receive errors=%b\n", 623*37110Ssklower unit, bp->mb_rply, RECV_BITS); 624*37110Ssklower return; 625*37110Ssklower } 626*37110Ssklower eh = (struct ether_header *)(bp->mb_pkb->iff_buffer); 627*37110Ssklower 628*37110Ssklower /* 629*37110Ssklower * Deal with trailer protocol: if type is PUP trailer get true type from 630*37110Ssklower * first 16-bit word past data. Remember that type was trailer by 631*37110Ssklower * setting off. 632*37110Ssklower */ 633*37110Ssklower eh->ether_type = ntohs((u_short)eh->ether_type); 634*37110Ssklower #define exdataaddr(eh, off, type) ((type)(((caddr_t)((eh)+1)+(off)))) 635*37110Ssklower if (eh->ether_type >= ETHERTYPE_TRAIL && 636*37110Ssklower eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { 637*37110Ssklower off = (eh->ether_type - ETHERTYPE_TRAIL) * 512; 638*37110Ssklower if (off >= ETHERMTU) 639*37110Ssklower return; /* sanity */ 640*37110Ssklower eh->ether_type = ntohs(*exdataaddr(eh, off, u_short *)); 641*37110Ssklower resid = ntohs(*(exdataaddr(eh, off+2, u_short *))); 642*37110Ssklower if (off + resid > len) 643*37110Ssklower return; /* sanity */ 644*37110Ssklower len = off + resid; 645*37110Ssklower } else 646*37110Ssklower off = 0; 647*37110Ssklower if (len == 0) 648*37110Ssklower return; 649*37110Ssklower /* 650*37110Ssklower * Pull packet off interface. Off is nonzero if packet 651*37110Ssklower * has trailing header; if_vbaget will then force this header 652*37110Ssklower * information to be at the front, but we still have to drop 653*37110Ssklower * the type and length which are at the front of any trailer data. 654*37110Ssklower */ 655*37110Ssklower m = if_vbaget(bp->mb_pkb->iff_buffer, len, off, &xs->xs_if, 0); 656*37110Ssklower if (m == 0) 657*37110Ssklower return; 658*37110Ssklower ether_input(&xs->xs_if, eh, m); 659*37110Ssklower return; 660*37110Ssklower } 661*37110Ssklower 662*37110Ssklower /* 663*37110Ssklower * Hang a receive request. This routine is called by exinit and excdint, 664*37110Ssklower * with interrupts disabled in both cases. 665*37110Ssklower */ 666*37110Ssklower exhangrcv(unit) 667*37110Ssklower int unit; 668*37110Ssklower { 669*37110Ssklower register struct ex_softc *xs = &ex_softc[unit]; 670*37110Ssklower register struct ex_msg *bp; 671*37110Ssklower register struct ifvba *pkb; 672*37110Ssklower short mustint = 0; 673*37110Ssklower union l_util { 674*37110Ssklower u_long l; 675*37110Ssklower struct i86_long i; 676*37110Ssklower } l_util; 677*37110Ssklower 678*37110Ssklower while (xs->xs_nrec < NREC) { 679*37110Ssklower if (xs->xs_pkblist == (struct ifvba *)0) 680*37110Ssklower break; 681*37110Ssklower if ((bp = exgetcbuf(xs, LLRECEIVE)) == (struct ex_msg *)0) { 682*37110Ssklower break; 683*37110Ssklower } 684*37110Ssklower pkb = bp->mb_pkb = xs->xs_pkblist; 685*37110Ssklower xs->xs_pkblist = (struct ifvba *)bp->mb_pkb->iff_mbuf; 686*37110Ssklower 687*37110Ssklower xs->xs_nrec += 1; 688*37110Ssklower bp->mb_er.er_nblock = 1; 689*37110Ssklower bp->mb_er.er_blks[0].bb_len = EXMAXRBUF; 690*37110Ssklower l_util.l = BUSADDR(pkb->iff_buffer); 691*37110Ssklower bp->mb_er.er_blks[0].bb_addr = l_util.i; 692*37110Ssklower bp->mb_status |= MH_EXOS; 693*37110Ssklower mustint = 1; 694*37110Ssklower } 695*37110Ssklower if (mustint == 0) 696*37110Ssklower return; 697*37110Ssklower movow(&((struct exdevice *)exinfo[unit]->ui_addr)->ex_portb, EX_NTRUPT); 698*37110Ssklower } 699*37110Ssklower 700*37110Ssklower /* 701*37110Ssklower * Ethernet output routine is ether_output(). 702*37110Ssklower */ 703*37110Ssklower 704*37110Ssklower /* 705*37110Ssklower * Watchdog routine (currently not used). Might use this to get stats from EXOS. 706*37110Ssklower */ 707*37110Ssklower exwatch(unit) 708*37110Ssklower int unit; 709*37110Ssklower { 710*37110Ssklower struct exdevice *exaddr = (struct exdevice *)exinfo[unit]->ui_addr; 711*37110Ssklower register struct ex_softc *xs = &ex_softc[unit]; 712*37110Ssklower register struct ex_msg *bp; 713*37110Ssklower int s = splimp(); 714*37110Ssklower 715*37110Ssklower if (xs->xs_flags & EX_STATPENDING) 716*37110Ssklower goto exspnd; 717*37110Ssklower if ((bp = exgetcbuf(xs, LLNET_STSTCS)) == (struct ex_msg *)0) { 718*37110Ssklower splx(s); 719*37110Ssklower return; 720*37110Ssklower } 721*37110Ssklower xs->xs_flags |= EX_STATPENDING; 722*37110Ssklower bp->mb_ns.ns_mask = READ_OBJ; 723*37110Ssklower bp->mb_ns.ns_rsrv = 0; 724*37110Ssklower bp->mb_ns.ns_nobj = 8; 725*37110Ssklower bp->mb_ns.ns_xobj = 0; 726*37110Ssklower bp->mb_ns.ns_bufp = P_BUSADDR(xs->xs_qbaddr) + SA_OFFSET; 727*37110Ssklower bp->mb_status |= MH_EXOS; 728*37110Ssklower movow(&exaddr->ex_portb, EX_NTRUPT); 729*37110Ssklower exspnd: splx(s); 730*37110Ssklower xs->xs_if.if_timer = EXWATCHINTVL; 731*37110Ssklower } 732*37110Ssklower 733*37110Ssklower /* 734*37110Ssklower * Process an ioctl request. 735*37110Ssklower */ 736*37110Ssklower exioctl(ifp, cmd, data) 737*37110Ssklower register struct ifnet *ifp; 738*37110Ssklower int cmd; 739*37110Ssklower caddr_t data; 740*37110Ssklower { 741*37110Ssklower register struct ifaddr *ifa = (struct ifaddr *)data; 742*37110Ssklower register struct ex_softc *xs = &ex_softc[ifp->if_unit]; 743*37110Ssklower int s = splimp(), error = 0; 744*37110Ssklower 745*37110Ssklower switch (cmd) { 746*37110Ssklower 747*37110Ssklower case SIOCSIFADDR: 748*37110Ssklower ifp->if_flags |= IFF_UP; 749*37110Ssklower exinit(ifp->if_unit); 750*37110Ssklower 751*37110Ssklower switch (ifa->ifa_addr->sa_family) { 752*37110Ssklower #ifdef INET 753*37110Ssklower case AF_INET: 754*37110Ssklower ((struct arpcom *)ifp)->ac_ipaddr = 755*37110Ssklower IA_SIN(ifa)->sin_addr; 756*37110Ssklower arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); 757*37110Ssklower break; 758*37110Ssklower #endif 759*37110Ssklower #ifdef NS 760*37110Ssklower case AF_NS: 761*37110Ssklower { 762*37110Ssklower register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); 763*37110Ssklower 764*37110Ssklower if (ns_nullhost(*ina)) 765*37110Ssklower ina->x_host = *(union ns_host *)(xs->xs_addr); 766*37110Ssklower else 767*37110Ssklower ex_setaddr(ina->x_host.c_host,ifp->if_unit); 768*37110Ssklower break; 769*37110Ssklower } 770*37110Ssklower #endif 771*37110Ssklower } 772*37110Ssklower break; 773*37110Ssklower 774*37110Ssklower case SIOCSIFFLAGS: 775*37110Ssklower if ((ifp->if_flags & IFF_UP) == 0 && 776*37110Ssklower xs->xs_flags & EX_RUNNING) { 777*37110Ssklower movow(&((struct exdevice *) 778*37110Ssklower (exinfo[ifp->if_unit]->ui_addr))->ex_porta, EX_RESET); 779*37110Ssklower xs->xs_flags &= ~EX_RUNNING; 780*37110Ssklower } else if (ifp->if_flags & IFF_UP && 781*37110Ssklower (xs->xs_flags & EX_RUNNING) == 0) 782*37110Ssklower exinit(ifp->if_unit); 783*37110Ssklower break; 784*37110Ssklower 785*37110Ssklower default: 786*37110Ssklower error = EINVAL; 787*37110Ssklower } 788*37110Ssklower splx(s); 789*37110Ssklower return (error); 790*37110Ssklower } 791*37110Ssklower 792*37110Ssklower /* 793*37110Ssklower * set ethernet address for unit 794*37110Ssklower */ 795*37110Ssklower ex_setaddr(physaddr, unit) 796*37110Ssklower u_char *physaddr; 797*37110Ssklower int unit; 798*37110Ssklower { 799*37110Ssklower register struct ex_softc *xs = &ex_softc[unit]; 800*37110Ssklower struct vba_device *ui = exinfo[unit]; 801*37110Ssklower register struct exdevice *addr= (struct exdevice *)ui->ui_addr; 802*37110Ssklower register struct ex_msg *bp; 803*37110Ssklower 804*37110Ssklower if (physaddr) { 805*37110Ssklower xs->xs_flags |= EX_SETADDR; 806*37110Ssklower bcopy((caddr_t)physaddr, (caddr_t)xs->xs_addr, 6); 807*37110Ssklower } 808*37110Ssklower if (! (xs->xs_flags & EX_RUNNING)) 809*37110Ssklower return; 810*37110Ssklower bp = exgetcbuf(xs); 811*37110Ssklower bp->mb_rqst = LLNET_ADDRS; 812*37110Ssklower bp->mb_na.na_mask = READ_OBJ|WRITE_OBJ; 813*37110Ssklower bp->mb_na.na_slot = PHYSSLOT; 814*37110Ssklower bcopy((caddr_t)xs->xs_addr, (caddr_t)bp->mb_na.na_addrs, 6); 815*37110Ssklower bp->mb_status |= MH_EXOS; 816*37110Ssklower movow(&addr->ex_portb, EX_NTRUPT); 817*37110Ssklower bp = xs->xs_x2hnext; 818*37110Ssklower do { 819*37110Ssklower uncache(&bp->mb_status); 820*37110Ssklower } while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */ 821*37110Ssklower #ifdef DEBUG 822*37110Ssklower log(LOG_DEBUG, "ex%d: reset addr %s\n", ui->ui_unit, 823*37110Ssklower ether_sprintf(bp->mb_na.na_addrs)); 824*37110Ssklower #endif 825*37110Ssklower /* 826*37110Ssklower * Now, re-enable reception on phys slot. 827*37110Ssklower */ 828*37110Ssklower bp = exgetcbuf(xs); 829*37110Ssklower bp->mb_rqst = LLNET_RECV; 830*37110Ssklower bp->mb_nr.nr_mask = ENABLE_RCV|READ_OBJ|WRITE_OBJ; 831*37110Ssklower bp->mb_nr.nr_slot = PHYSSLOT; 832*37110Ssklower bp->mb_status |= MH_EXOS; 833*37110Ssklower movow(&addr->ex_portb, EX_NTRUPT); 834*37110Ssklower bp = xs->xs_x2hnext; 835*37110Ssklower do { 836*37110Ssklower uncache(&bp->mb_status); 837*37110Ssklower } while ((bp->mb_status & MH_OWNER) == MH_EXOS);/* poll for reply */ 838*37110Ssklower ; 839*37110Ssklower } 840*37110Ssklower #endif 841