1 /* 2 * Copyright (c) 1982,1986,1988 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that this notice is preserved and that due credit is given 7 * to the University of California at Berkeley. The name of the University 8 * may not be used to endorse or promote products derived from this 9 * software without specific prior written permission. This software 10 * is provided ``as is'' without express or implied warranty. 11 * 12 * @(#)if_acc.c 7.4 (Berkeley) 05/14/88 13 */ 14 15 #include "acc.h" 16 #if NACC > 0 17 18 /* 19 * ACC LH/DH ARPAnet IMP interface driver. 20 */ 21 #include "../machine/pte.h" 22 23 #include "param.h" 24 #include "systm.h" 25 #include "mbuf.h" 26 #include "buf.h" 27 #include "protosw.h" 28 #include "socket.h" 29 #include "vmmac.h" 30 31 #include "../net/if.h" 32 #include "../netimp/if_imp.h" 33 34 #include "../vax/cpu.h" 35 #include "../vax/mtpr.h" 36 #include "if_accreg.h" 37 #include "if_uba.h" 38 #include "../vaxuba/ubareg.h" 39 #include "../vaxuba/ubavar.h" 40 41 int accprobe(), accattach(), accrint(), accxint(); 42 struct uba_device *accinfo[NACC]; 43 u_short accstd[] = { 0 }; 44 struct uba_driver accdriver = 45 { accprobe, 0, accattach, 0, accstd, "acc", accinfo }; 46 47 int accinit(), accoutput(), accdown(), accreset(); 48 49 /* 50 * "Lower half" of IMP interface driver. 51 * 52 * Each IMP interface is handled by a common module which handles 53 * the IMP-host protocol and a hardware driver which manages the 54 * hardware specific details of talking with the IMP. 55 * 56 * The hardware portion of the IMP driver handles DMA and related 57 * management of UNIBUS resources. The IMP protocol module interprets 58 * contents of these messages and "controls" the actions of the 59 * hardware module during IMP resets, but not, for instance, during 60 * UNIBUS resets. 61 * 62 * The two modules are coupled at "attach time", and ever after, 63 * through the imp interface structure. Higher level protocols, 64 * e.g. IP, interact with the IMP driver, rather than the ACC. 65 */ 66 struct acc_softc { 67 struct imp_softc *acc_imp; /* data structure shared with IMP */ 68 struct ifuba acc_ifuba; /* UNIBUS resources */ 69 struct mbuf *acc_iq; /* input reassembly queue */ 70 short acc_olen; /* size of last message sent */ 71 char acc_flush; /* flush remainder of message */ 72 } acc_softc[NACC]; 73 74 /* 75 * Reset the IMP and cause a transmitter interrupt by 76 * performing a null DMA. 77 */ 78 accprobe(reg) 79 caddr_t reg; 80 { 81 register int br, cvec; /* r11, r10 value-result */ 82 register struct accdevice *addr = (struct accdevice *)reg; 83 84 #ifdef lint 85 br = 0; cvec = br; br = cvec; 86 accrint(0); accxint(0); 87 #endif 88 addr->icsr = ACC_RESET; DELAY(5000); 89 addr->ocsr = ACC_RESET; DELAY(5000); 90 addr->ocsr = OUT_BBACK; DELAY(5000); 91 addr->owc = 0; 92 addr->ocsr = ACC_IE | ACC_GO; DELAY(5000); 93 addr->ocsr = 0; 94 if (cvec && cvec != 0x200) /* transmit -> receive */ 95 cvec -= 4; 96 return (1); 97 } 98 99 /* 100 * Call the IMP module to allow it to set up its internal 101 * state, then tie the two modules together by setting up 102 * the back pointers to common data structures. 103 */ 104 accattach(ui) 105 register struct uba_device *ui; 106 { 107 register struct acc_softc *sc = &acc_softc[ui->ui_unit]; 108 register struct impcb *ip; 109 110 if ((sc->acc_imp = impattach(ui->ui_driver->ud_dname, ui->ui_unit, 111 accreset)) == 0) 112 return; 113 ip = &sc->acc_imp->imp_cb; 114 ip->ic_init = accinit; 115 ip->ic_output = accoutput; 116 ip->ic_down = accdown; 117 sc->acc_ifuba.ifu_flags = UBA_CANTWAIT; 118 #ifdef notdef 119 sc->acc_ifuba.ifu_flags |= UBA_NEEDBDP; 120 #endif 121 } 122 123 /* 124 * Reset interface after UNIBUS reset. 125 * If interface is on specified uba, reset its state. 126 */ 127 accreset(unit, uban) 128 int unit, uban; 129 { 130 register struct uba_device *ui; 131 struct acc_softc *sc; 132 133 if (unit >= NACC || (ui = accinfo[unit]) == 0 || ui->ui_alive == 0 || 134 ui->ui_ubanum != uban) 135 return; 136 printf(" acc%d", unit); 137 sc = &acc_softc[unit]; 138 sc->acc_imp->imp_if.if_flags &= ~IFF_RUNNING; 139 accoflush(unit); 140 /* must go through IMP to allow it to set state */ 141 (*sc->acc_imp->imp_if.if_init)(sc->acc_imp->imp_if.if_unit); 142 } 143 144 /* 145 * Initialize interface: clear recorded pending operations, 146 * and retrieve, and initialize UNIBUS resources. Note 147 * return value is used by IMP init routine to mark IMP 148 * unavailable for outgoing traffic. 149 */ 150 accinit(unit) 151 int unit; 152 { 153 register struct acc_softc *sc; 154 register struct uba_device *ui; 155 register struct accdevice *addr; 156 int info; 157 158 if (unit >= NACC || (ui = accinfo[unit]) == 0 || ui->ui_alive == 0) { 159 printf("acc%d: not alive\n", unit); 160 return (0); 161 } 162 sc = &acc_softc[unit]; 163 /* 164 * Header length is 0 since we have to passs 165 * the IMP leader up to the protocol interpretation 166 * routines. If we had the header length as 167 * sizeof(struct imp_leader), then the if_ routines 168 * would asssume we handle it on input and output. 169 */ 170 if ((sc->acc_imp->imp_if.if_flags & IFF_RUNNING) == 0 && 171 if_ubainit(&sc->acc_ifuba, ui->ui_ubanum, 0, 172 (int)btoc(IMP_RCVBUF)) == 0) { 173 printf("acc%d: can't initialize\n", unit); 174 sc->acc_imp->imp_if.if_flags &= ~(IFF_UP | IFF_RUNNING); 175 return (0); 176 } 177 sc->acc_imp->imp_if.if_flags |= IFF_RUNNING; 178 addr = (struct accdevice *)ui->ui_addr; 179 180 /* 181 * Reset the imp interface; 182 * the delays are pure guesswork. 183 */ 184 addr->ocsr = ACC_RESET; DELAY(5000); 185 addr->ocsr = OUT_BBACK; DELAY(5000); /* reset host master ready */ 186 addr->ocsr = 0; 187 if (accinputreset(addr, unit) == 0) { 188 ui->ui_alive = 0; 189 return (0); 190 } 191 192 /* 193 * Put up a read. We can't restart any outstanding writes 194 * until we're back in synch with the IMP (i.e. we've flushed 195 * the NOOPs it throws at us). 196 * Note: IMP_RCVBUF includes the leader. 197 */ 198 info = sc->acc_ifuba.ifu_r.ifrw_info; 199 addr->iba = (u_short)info; 200 addr->iwc = -((IMP_RCVBUF) >> 1); 201 #ifdef LOOPBACK 202 addr->ocsr |= OUT_BBACK; 203 #endif 204 addr->icsr = 205 IN_MRDY | ACC_IE | IN_WEN | ((info & 0x30000) >> 12) | ACC_GO; 206 return (1); 207 } 208 209 accinputreset(addr, unit) 210 register struct accdevice *addr; 211 register int unit; 212 { 213 register int i; 214 215 addr->icsr = ACC_RESET; DELAY(5000); 216 addr->icsr = IN_MRDY | IN_WEN; /* close the relay */ 217 DELAY(10000); 218 /* YECH!!! */ 219 for (i = 0; i < 500; i++) { 220 if ((addr->icsr & IN_HRDY) || 221 (addr->icsr & (IN_RMR | IN_IMPBSY)) == 0) 222 return (1); 223 addr->icsr = IN_MRDY | IN_WEN; DELAY(10000); 224 /* keep turning IN_RMR off */ 225 } 226 printf("acc%d: imp doesn't respond, icsr=%b\n", unit, 227 addr->icsr, ACC_INBITS); 228 return (0); 229 } 230 231 /* 232 * Drop the host ready line to mark host down. 233 */ 234 accdown(unit) 235 int unit; 236 { 237 register struct accdevice *addr; 238 239 addr = (struct accdevice *)(accinfo[unit]->ui_addr); 240 addr->ocsr = ACC_RESET; 241 DELAY(5000); 242 addr->ocsr = OUT_BBACK; /* reset host master ready */ 243 accoflush(unit); 244 return (1); 245 } 246 247 accoflush(unit) 248 int unit; 249 { 250 register struct acc_softc *sc = &acc_softc[unit]; 251 252 sc->acc_imp->imp_cb.ic_oactive = 0; 253 if (sc->acc_ifuba.ifu_xtofree) { 254 m_freem(sc->acc_ifuba.ifu_xtofree); 255 sc->acc_ifuba.ifu_xtofree = 0; 256 } 257 } 258 259 /* 260 * Start output on an interface. 261 */ 262 accoutput(unit, m) 263 int unit; 264 struct mbuf *m; 265 { 266 int info; 267 register struct acc_softc *sc = &acc_softc[unit]; 268 register struct accdevice *addr; 269 u_short cmd; 270 271 sc->acc_olen = if_wubaput(&sc->acc_ifuba, m); 272 /* 273 * Have request mapped to UNIBUS for 274 * transmission; start the output. 275 */ 276 if (sc->acc_ifuba.ifu_flags & UBA_NEEDBDP) 277 UBAPURGE(sc->acc_ifuba.ifu_uba, sc->acc_ifuba.ifu_w.ifrw_bdp); 278 addr = (struct accdevice *)accinfo[unit]->ui_addr; 279 info = sc->acc_ifuba.ifu_w.ifrw_info; 280 addr->oba = (u_short)info; 281 addr->owc = -((sc->acc_olen + 1) >> 1); 282 cmd = ACC_IE | OUT_ENLB | ((info & 0x30000) >> 12) | ACC_GO; 283 #ifdef LOOPBACK 284 cmd |= OUT_BBACK; 285 #endif 286 addr->ocsr = cmd; 287 sc->acc_imp->imp_cb.ic_oactive = 1; 288 } 289 290 /* 291 * Output interrupt handler. 292 */ 293 accxint(unit) 294 int unit; 295 { 296 register struct acc_softc *sc = &acc_softc[unit]; 297 register struct accdevice *addr; 298 299 addr = (struct accdevice *)accinfo[unit]->ui_addr; 300 if (sc->acc_imp->imp_cb.ic_oactive == 0) { 301 printf("acc%d: stray xmit interrupt, csr=%b\n", unit, 302 addr->ocsr, ACC_OUTBITS); 303 return; 304 } 305 sc->acc_imp->imp_if.if_opackets++; 306 sc->acc_imp->imp_cb.ic_oactive = 0; 307 if (addr->ocsr & ACC_ERR) { 308 printf("acc%d: output error, ocsr=%b, icsr=%b\n", unit, 309 addr->ocsr, ACC_OUTBITS, addr->icsr, ACC_INBITS); 310 sc->acc_imp->imp_if.if_oerrors++; 311 } 312 if (sc->acc_ifuba.ifu_xtofree) { 313 m_freem(sc->acc_ifuba.ifu_xtofree); 314 sc->acc_ifuba.ifu_xtofree = 0; 315 } 316 impstart(sc->acc_imp); 317 } 318 319 /* 320 * Input interrupt handler 321 */ 322 accrint(unit) 323 int unit; 324 { 325 register struct acc_softc *sc = &acc_softc[unit]; 326 register struct accdevice *addr; 327 struct mbuf *m; 328 int len, info; 329 330 addr = (struct accdevice *)accinfo[unit]->ui_addr; 331 sc->acc_imp->imp_if.if_ipackets++; 332 333 /* 334 * Purge BDP; flush message if error indicated. 335 */ 336 if (sc->acc_ifuba.ifu_flags & UBA_NEEDBDP) 337 UBAPURGE(sc->acc_ifuba.ifu_uba, sc->acc_ifuba.ifu_r.ifrw_bdp); 338 if (addr->icsr & ACC_ERR) { 339 printf("acc%d: input error, csr=%b\n", unit, 340 addr->icsr, ACC_INBITS); 341 sc->acc_imp->imp_if.if_ierrors++; 342 sc->acc_flush = 1; 343 } 344 345 if (sc->acc_flush) { 346 if (addr->icsr & IN_EOM) 347 sc->acc_flush = 0; 348 goto setup; 349 } 350 len = IMP_RCVBUF + (addr->iwc << 1); 351 if (len < 0 || len > IMP_RCVBUF) { 352 printf("acc%d: bad length=%d\n", unit, len); 353 sc->acc_imp->imp_if.if_ierrors++; 354 goto setup; 355 } 356 357 /* 358 * The offset parameter is always 0 since using 359 * trailers on the ARPAnet is insane. 360 */ 361 m = if_rubaget(&sc->acc_ifuba, len, 0, &sc->acc_imp->imp_if); 362 if (m == 0) 363 goto setup; 364 if ((addr->icsr & IN_EOM) == 0) { 365 if (sc->acc_iq) 366 m_cat(sc->acc_iq, m); 367 else 368 sc->acc_iq = m; 369 goto setup; 370 } 371 if (sc->acc_iq) { 372 m_cat(sc->acc_iq, m); 373 m = sc->acc_iq; 374 sc->acc_iq = 0; 375 } 376 impinput(unit, m); 377 378 setup: 379 /* 380 * Setup for next message. 381 */ 382 info = sc->acc_ifuba.ifu_r.ifrw_info; 383 addr->iba = (u_short)info; 384 addr->iwc = -((IMP_RCVBUF)>> 1); 385 addr->icsr = 386 IN_MRDY | ACC_IE | IN_WEN | ((info & 0x30000) >> 12) | ACC_GO; 387 } 388 #endif 389