xref: /original-bsd/sys/vax/if/if_acc.c (revision 7f22226e)
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  */
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  */
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  */
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  */
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 
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  */
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 
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  */
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  */
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  */
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