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