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