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