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