xref: /original-bsd/sys/i386/isa/if_apx.c (revision 7bd39f15)
1 /*
2  * Copyright (c) 1982, 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  *
7  *	@(#)if_apx.c	7.3 (Berkeley) 02/25/92
8  */
9 
10 /*
11  * Driver for SGS-THOMSON MK5025 based Link level controller.
12  * The chip will do LAPB in hardware, although this driver only
13  * attempts to use it for HDLC framing.
14  *
15  * Driver written by Keith Sklower, based on lance AMD7990
16  * driver by Van Jacobsen, and information graciously supplied
17  * by the ADAX corporation of Berkeley, CA.
18  */
19 
20 #include "apx.h"
21 #if NAPX > 0
22 
23 #include "param.h"
24 #include "mbuf.h"
25 #include "socket.h"
26 #include "ioctl.h"
27 #include "errno.h"
28 #include "syslog.h"
29 
30 #include "net/if.h"
31 #include "net/netisr.h"
32 #include "net/if_types.h"
33 #include "netccitt/x25.h"
34 
35 #include "if_apxreg.h"
36 
37 int	apxprobe(), apxattach(), apxstart(), apx_uprim(), apx_meminit();
38 int	apxinit(), x25_ifoutput(), x25_rtrequest(), apxioctl(), apxreset();
39 int	apxctr();
40 void	apx_ifattach(), apxinput(), apxintr(), apxtint(), apaxrint();
41 
42 struct apx_softc {
43 	struct	ifnet apx_if;
44 	caddr_t	apx_device;		/* e.g. isa_device */
45 	u_short	apx_csr4;		/* byte gender, set in mach dep code */
46 	struct	apc_reg *apx_reg;	/* control regs for both subunits */
47 	struct	apc_mem *apx_hmem;	/* Host addr for shared memory */
48 	struct	apc_mem *apx_dmem;	/* Device (chip) addr for shared mem */
49 	struct	sgcp *apx_sgcp;		/* IO control port for this subunit */
50 	struct	apc_modes apx_modes;	/* Parameters, as amended by ioctls */
51 	int	apx_rxnum;		/* Last receiver dx we looked at */
52 	int	apx_txnum;		/* Last tranmistter dx we stomped on */
53 	int	apx_txcnt;		/* Number of packets queued for tx*/
54 } apx_softc[2 * NAPX], *apx_lastsoftc = apx_softc;
55 
56 struct apxstat {
57 	int	nulltx;
58 	int	pint;
59 } apxstat;
60 
61 /* default operating paramters for devices */
62 struct	apc_modes apx_default_modes = {
63  { 1,		/* apm_sgob.lsaddr; */
64    3,		/* apm_sgob.rsaddr; */
65    -SGMTU,	/* apm_sgob.n1; */
66    ((-10)<<8),	/* apm_sgob.n2_scale; */
67    -1250,	/* apm_sgob.t1; */
68    -10000,	/* apm_sgob.t3; */
69    -80,		/* apm_sgob.tp; */
70  },
71  2,		/* apm_txwin; */
72  5,		/* apm_apxmode; */
73  0,		/* apm_apxaltmode; */
74  IFT_X25,	/* apm_iftype; */
75 };
76 
77 /* Begin bus & endian dependence */
78 
79 #include "isa_device.h"
80 
81 struct	isa_driver apxdriver = {
82 	apxprobe, apxattach, "apx",
83 };
84 
85 #define SG_RCSR(apx, csrnum) \
86 	 (outw(&(apx->apx_sgcp->sgcp_rap), csrnum << 1), \
87 	  inw(&(apx->apx_sgcp->sgcp_rdp)))
88 
89 #define SG_WCSR(apx, csrnum, data) \
90 	 (outw(&(apx->apx_sgcp->sgcp_rap), csrnum << 1), \
91 	  outw(&(apx->apx_sgcp->sgcp_rdp), data))
92 
93 #define APX_RCSR(apx, csrname) inb(&(apx->apx_reg->csrname))
94 #define APX_WCSR(apx, csrname, data) outb(&(apx->apx_reg->csrname), data)
95 
96 #define TIMO 10000 /* used in apx_uprim */
97 
98 apxprobe(id)
99 	register struct	isa_device *id;
100 {
101 	int	moffset, subunit, unit = id->id_unit << 1;
102 	struct	apc_reg *reg = (struct apc_reg *)id->id_iobase;
103 	register struct	apx_softc *apx = apx_softc + unit;
104 
105 	apx->apx_if.if_unit = unit;
106 	apx->apx_reg = reg;
107 	if (apxctr(apx) == 0) {
108 		apxerror(apx, "no response from timer chip", 0);
109 		return 0;			/* No board present */
110 	}
111 	for (subunit = 0; subunit < 2; subunit++, apx++) {
112 		/* Set and read DTR mode to test present of SGS thompson chip */
113 		apx->apx_if.if_unit = unit++;
114 		apx->apx_sgcp = reg->axr_sgcp + subunit;
115 		SG_WCSR(apx, 5, 0x08);
116 		if (((SG_RCSR(apx, 5) & 0xff08) != 0x08)) {
117 			apxerror(apx, "no mk5025 for channel", subunit);
118 			continue;
119 		}
120 		moffset = subunit ? id->id_msize >> 1 : 0;
121 		apx->apx_hmem	= (struct apc_mem *) (id->id_maddr + moffset);
122 		apx->apx_dmem	= (struct apc_mem *) (moffset);
123 		apx->apx_modes	= apx_default_modes;
124 		apx->apx_device = (caddr_t) id;
125 		apx->apx_reg	= reg;
126 		apx->apx_csr4	= 0x0110;	/* no byte swapping for PC-AT */
127 	}
128 	return 1;
129 }
130 
131 apxattach(id)
132 	register struct isa_device *id;
133 {
134 	int	unit = id->id_unit + id->id_unit;
135 
136 	apx_ifattach(unit);
137 	apx_ifattach(unit + 1);
138 	return (0);
139 }
140 /* End bus & endian dependence */
141 
142 /*
143  * Interface exists: make available by filling in network interface
144  * record.  System will initialize the interface when it is ready
145  * to accept packets.
146  */
147 void
148 apx_ifattach(unit)
149 {
150 	register struct ifnet *ifp = &(apx_softc[unit].apx_if);
151 	/*
152 	 * Initialize ifnet structure
153 	 */
154 	if (apx_softc[unit].apx_device == 0)
155 		return;
156 	ifp->if_name = "apc";
157 	ifp->if_mtu = SGMTU;
158 	ifp->if_init = apxinit;
159 	ifp->if_output = x25_ifoutput;
160 	ifp->if_start = apxstart;
161 	ifp->if_ioctl = apxioctl;
162 	ifp->if_reset = apxreset;
163 	ifp->if_type = apx_default_modes.apm_iftype;
164 	ifp->if_hdrlen = 5;
165 	ifp->if_addrlen = 8;
166 	if_attach(ifp);
167 }
168 /*
169  * Initialization of interface
170  */
171 apxinit(unit)
172 	int unit;
173 {
174 	struct ifnet *ifp = &apx_softc[unit].apx_if;
175 	int s = splimp();
176 
177 	ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
178 	if (apxreset(unit) && (ifp->if_flags & IFF_UP)) {
179 		ifp->if_flags |= IFF_RUNNING;
180 		(void)apxstart(ifp);
181 	}
182 	splx(s);
183 	return 0;
184 }
185 
186 apxctr(apx)
187 	register struct apx_softc *apx;
188 {
189 	APX_WCSR(apx, axr_ccr, 0xb0); /* select ctr 2, write lsb+msb, mode 0 */
190 	APX_WCSR(apx, axr_cnt2, 0x1);
191 	APX_WCSR(apx, axr_cnt2, 0x0);
192 	DELAY(50);
193 	APX_WCSR(apx, axr_ccr, 0xD4); /* latch status, ctr 2; */
194 	return APX_RCSR(apx, axr_cnt2);
195 }
196 
197 apxreset(unit)
198 	int	unit;
199 {
200 	register struct apx_softc *apx = &apx_softc[unit ^ 1];
201 	u_char apm_apxmode = 0, apm_apxaltmode = 0;
202 #define MODE(m) (m |= apx->apx_modes.m << ((apx->apx_if.if_unit & 1) ? 1 : 0))
203 
204 	MODE(apm_apxmode);
205 	MODE(apm_apxaltmode);
206 	apx = apx_softc + unit;
207 	MODE(apm_apxmode);
208 	MODE(apm_apxaltmode);
209 	APX_WCSR(apx, axr_mode, apm_apxmode);
210 	APX_WCSR(apx, axr_altmode, apm_apxaltmode);
211 	apx->apx_txnum = apx->apx_rxnum = apx->apx_txcnt = 0;
212 
213 	if (apx_uprim(apx, SG_STOP, "stop") ||
214 	    !(apx->apx_if.if_flags & IFF_UP))
215 		return 0;
216 	apx_meminit(apx->apx_hmem, apx); /* also sets CSR2 */
217 	SG_WCSR(apx, 3, (int)apx->apx_dmem);
218 	SG_WCSR(apx, 4, apx->apx_csr4);
219 	if (apx_uprim(apx, SG_INIT, "init request") ||
220 	    apx_uprim(apx, SG_STAT, "status request") ||
221 	    apx_uprim(apx, SG_TRANS, "transparent mode"))
222 		return 0;
223 	(void) apxctr(apx);
224 	SG_WCSR(apx, 0, SG_INEA);
225 	return 1;
226 }
227 
228 apx_uprim(apx, request, ident)
229 	int request;
230 	char *ident;
231 	register struct apx_softc *apx;
232 {
233 	register int timo = 0;
234 	int reply = SG_RCSR(apx, 1);
235 
236 	if (reply & 0x8040)
237 		SG_WCRS(1, 0x8040); /* Magic! */
238 	SG_WCSR(apx, 1, request | SG_UAV);
239 	do {
240 		reply = SG_RCRS(1);
241 		if (timo++ >= TIMO | reply & 0x8000) {
242 			apxerror(apx, ident, reply);
243 			return 1;
244 		}
245 	} while (reply & SG_UAV);
246 	return 0;
247 }
248 
249 apx_meminit(apc, apx)
250 	register struct apc_mem *apc;
251 	struct apx_softc *apx;
252 {
253 	register struct apc_mem *apcbase = apx->apx_dmem;
254 	register int i;
255 #define LOWADDR(e) (((u_long)&(apcbase->e)) & 0xffff)
256 #define HIADDR(e) ((((u_long)&(apcbase->e)) >> 16) & 0xff)
257 #define SET_SGDX(dx, f, a, b, m) \
258 	{ (dx).sgdx_addr = LOWADDR(a); (dx).sgdx_bcnt = (b);\
259 	  (dx).sgdx_mcnt = (m); (dx).sgdx_flags = (f) | HIADDR(a); }
260 
261 	bzero((caddr_t)apc, LOWADDR(apc_rxmd[0]));
262 	apc->apc_mode = 0x8040;	/* 2 flag spacing, trans mode, 16bit FCS */
263 	apc->apc_sgop = apx->apx_modes.apm_sgop;
264 	apc->apc_rlen = SG_RLEN | HIADDR(apc_rxmd[0]);
265 	apc->apc_rdra = LOWADDR(apc_rxmd[0]);
266 	apc->apc_rlen = SG_TLEN | apx->apx_modes.apm_txwin |HIADDR(apc_txmd[0]);
267 	apc->apc_tdra = LOWADDR(apc_txmd[0]);
268 	SET_SGDX(apc->apc_rxtid, SG_OWN, apc_rxidbuf, -SGMTU, 0);
269 	SET_SGDX(apc->apc_txtid, 0, apc_txidbuf, -SGMTU, 0);
270 	apc->apc_stathi = HIADDR(apc_sgsb);
271 	apc->apc_statlo = LOWADDR(apc_sgsb);
272 	for (i = 0; i < SGRBUF; i++)
273 		 SET_SGDX(apc->apc_rxmd[i], SG_OWN, apc_rbuf[i][0], -SGMTU, 0)
274 	for (i = 0; i < SGTBUF; i++)
275 		 SET_SGDX(apc->apc_txmd[i], SG_TUI, apc_tbuf[i][0], 0, 0)
276 	SG_WCSR(apx, 2, SG_UIE | SG_PROM | HIADDR(apc_mode));
277 }
278 
279 /*
280  * Start output on interface.  Get another datagram to send
281  * off of the interface queue, and copy it to the interface
282  * before starting the output.
283  */
284 apxstart(ifp)
285 	struct ifnet *ifp;
286 {
287 	register struct apx_softc *apx = &apx_softc[ifp->if_unit];
288 	register struct sgdx *dx;
289 	struct apc_mem *apc = apx->apx_hmem;
290 	struct mbuf *m;
291 	int len;
292 
293 	if ((ifp->if_flags & IFF_RUNNING) == 0)
294 		return (0);
295 	do {
296 		dx = apc->apc_txmd + apx->apx_txnum;
297 		if (dx->sgdx_flags & SG_OWN)
298 			return (0);
299 		IF_DEQUEUE(&ifp->if_snd, m);
300 		if (m == 0)
301 			return (0);
302 		len = min(m->m_pkthdr.len, SGMTU);
303 		m_copydata(m, 0, len, apc->apc_tbuf[apx->apx_txnum]);
304 		dx->sgdx_mcnt = -len;
305 		dx->sgdx_flags = SG_OWN | SG_TUI | (0xff & dx->sgdx_flags);
306 		SG_WCSR(apx, 0, SG_INEA | SG_TDMD);
307 		if (++apx->apx_txnum >= SGTBUF)
308 			apx->apx_txnum = 0;
309 	} while (++apx->apx_txcnt < SGTBUF);
310 	apx->apx_txcnt = SGTBUF;
311 	ifp->if_flags |= IFF_OACTIVE;
312 	return (0);
313 }
314 
315 void
316 apxintr()
317 {
318 	register struct apx_softc *apx = apx_lastsoftc;
319 	struct apx_softc *apxlim = apx_softc + NAPX + NAPX;
320 	int reply;
321 
322 	do {
323 		if (apx->apx_if.if_flags & IFF_UP)
324 		    /* Try to turn off interrupt cause */
325 		    while ((reply = SG_RCSR(apx, 0)) & 0xff) {
326 			SG_WCSR(apx, 0, SG_INEA | 0xfe);
327 			if (reply & (SG_MERR|SG_TUR|SG_ROR)) {
328 				apxerror(apx, "mem, rx, or tx error", reply);
329 				apxinit(apx->apx_if.if_unit);
330 				break;
331 			}
332 			if (reply & SG_RINT)
333 				apxrint(apx);
334 			if (reply & SG_TINT)
335 				apxtint(apx);
336 			if (reply & SG_PINT)
337 				apxstat.pint++;
338 		}
339 		if (++apx >= apxlim)
340 			apx = apx_softc;
341 	} while (apx != apx_lastsoftc);
342 }
343 
344 void
345 apxtint(apx)
346 	register struct apx_softc *apx;
347 {
348 	register struct apc_mem *apc = apx->apx_hmem;
349 	int i, loopcount = 0;
350 
351 	do {
352 		if ((i = apx->apx_txnum - apx->apx_txcnt) < 0)
353 			i += SGTBUF;
354 		if (apc->apc_txmd[i].sgdx_flags & SG_OWN) {
355 			if (loopcount)
356 				break;
357 			apxstat.nulltx++;
358 			return;
359 		}
360 		loopcount++;
361 		apx->apx_if.if_flags &= ~IFF_OACTIVE;
362 	} while (--apx->apx_txcnt > 0);
363 	apxstart(&apx->apx_if);
364 }
365 
366 apxrint(apx)
367 	register struct apx_softc *apx;
368 {
369 	register struct apc_mem *apc = apx->apx_hmem;
370 	register struct sgdx *dx = apc->apc_rxmd + apx->apx_rxnum;
371 #define SGNEXTRXMD \
372 dx = ++apx->apx_rxnum == SGRBUF ? &apc->apc_rxmd[apx->apx_rxnum = 0] : dx + 1;
373 
374 	/*
375 	 * Out of sync with hardware, should never happen?
376 	 */
377 	if (dx->sgdx_flags & SG_OWN) {
378 		apxerror(apx, "out of sync");
379 		return;
380 	}
381 	/*
382 	 * Process all buffers with valid data
383 	 */
384 	while ((dx->sgdx_flags & SG_OWN) == 0) {
385 		if ((dx->sgdx_flags & (SG_SLF|SG_ELF)) != (SG_SLF|SG_ELF)) {
386 			/*
387 			 * Find the end of the packet so we can see how long
388 			 * it was.  We still throw it away.
389 			 */
390 			apxerror(apx, "chained buffer", dx->sgdx_flags);
391 			do {
392 				dx->sgdx_bcnt = 0;
393 				dx->sgdx_flags = SG_OWN | (0xff&dx->sgdx_flags);
394 				SGNEXTRXMD;
395 			} while (!(dx->sgdx_flags & (SG_OWN|SG_SLF|SG_ELF)));
396 			/*
397 			 * If search terminated without successful completion
398 			 * we reset the hardware (conservative).
399 			 */
400 			if ((dx->sgdx_flags & (SG_OWN|SG_SLF|SG_ELF)) !=
401 			    SG_ELF) {
402 				apxreset(apx->apx_if.if_unit);
403 				return;
404 			}
405 		} else
406 			apxinput(&apx->apx_if, apc->apc_rbuf[apx->apx_rxnum],
407 					-dx->sgdx_bcnt);
408 		dx->sgdx_bcnt = 0;
409 		dx->sgdx_flags = SG_OWN | (0xff & dx->sgdx_flags);
410 		SGNEXTRXMD;
411 	}
412 }
413 
414 void
415 apxinput(ifp, buffer, len)
416 register struct ifnet *ifp;
417 caddr_t buffer;
418 {
419 	register struct ifqueue *inq;
420 	struct mbuf *m, *m_devget();
421 	extern struct ifqueue hdintrq, ipintrq;
422 	int isr;
423 
424 	ifp->if_ipackets++;
425     {
426 	register u_char *cp = (u_char *)buffer;
427 
428 	if (cp[0] == 0xff && cp[1] == 0x3) {
429 		/* This is a UI HDLC Packet, so we'll assume PPP
430 		   protocol.  for now, IP only. */
431 		buffer += 4;
432 		len -= 4;
433 		inq = &ipintrq;
434 		isr = NETISR_IP;
435 	} else {
436 		inq = &hdintrq;
437 		isr = NETISR_CCITT;
438 	}
439     }
440 	if (len <= 0)
441 		return;
442 
443 	m = m_devget(buffer, len, 0, ifp, (void (*)())0);
444 	if (m == 0)
445 		return;
446 
447 	if(IF_QFULL(inq)) {
448 		IF_DROP(inq);
449 		m_freem(m);
450 	} else {
451 		IF_ENQUEUE(inq, m);
452 		schednetisr(isr);
453 	}
454 }
455 
456 /*
457  * Process an ioctl request.
458  */
459 apxioctl(ifp, cmd, data)
460 	register struct ifnet *ifp;
461 	int cmd;
462 	caddr_t data;
463 {
464 	register struct ifaddr *ifa = (struct ifaddr *)data;
465 	int s = splimp(), error = 0;
466 	struct apx_softc *apx = &apx_softc[ifp->if_unit];
467 
468 	switch (cmd) {
469 	case SIOCSIFCONF_X25:
470 		ifp->if_flags |= IFF_UP;
471 		error = hd_ctlinput(PRC_IFUP, ifa->ifa_addr);
472 		if (error == 0)
473 			apxinit(ifp->if_unit);
474 		break;
475 
476 	case SIOCSIFADDR:
477 		ifa->ifa_rtrequest = x25_rtrequest;
478 		break;
479 
480 	case SIOCSIFFLAGS:
481 		if (((ifp->if_flags & IFF_UP) == 0 &&
482 		     (ifp->if_flags & IFF_RUNNING)) ||
483 		    (ifp->if_flags & IFF_UP) &&
484 		     (ifp->if_flags & IFF_RUNNING) == 0)
485 			apxinit(ifp->if_unit);
486 		break;
487 
488 	case SIOCSIFMODE:
489 		if ((ifp->if_flags & IFF_UP) == 0)
490 			apx->apx_modes = *(struct apc_modes *)data;
491 		else
492 	default:
493 			error = EINVAL;
494 
495 	}
496 	splx(s);
497 	return (error);
498 }
499 
500 apxerror(apx, msg, data)
501 	register struct	apx_softc *apx;
502 	char	*msg;
503 {
504 	log(LOG_WARNING, "apc%d: %s, stat=0x%x\n",
505 		apx->apx_if.if_unit, msg, data);
506 }
507 #endif /* NAPX */
508