xref: /csrg-svn/sys/vax/if/if_hdh.c (revision 25461)
1 /*	@(#)if_hdh.c	6.2 (Berkeley) 11/11/85 */
2 
3 
4 /************************************************************************\
5 
6      ________________________________________________________
7     /                                                        \
8    |          AAA          CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |
9    |         AAAAA        CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |
10    |        AAAAAAA       CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |
11    |       AAAA AAAA      CCCC              CCCC              |
12    |      AAAA   AAAA     CCCC              CCCC              |
13    |     AAAA     AAAA    CCCC              CCCC              |
14    |    AAAA       AAAA   CCCC              CCCC              |
15    |   AAAA  AAAAAAAAAAA  CCCCCCCCCCCCCCCCC CCCCCCCCCCCCCCCCC |
16    |  AAAA    AAAAAAAAAAA CCCCCCCCCCCCCCCC  CCCCCCCCCCCCCCCC  |
17    | AAAA      AAAAAAAAA   CCCCCCCCCCCCCC    CCCCCCCCCCCCCC   |
18     \________________________________________________________/
19 
20 	Copyright (c) 1984 by Advanced Computer Communications
21 	720 Santa Barbara Street, Santa Barbara, California  93101
22 	(805) 963-9431
23 
24 	This software may be duplicated and used on systems
25 	which are licensed to run U.C. Berkeley versions of
26 	the UNIX operating system.  Any duplication of any
27 	part of this software must include a copy of ACC's
28 	copyright notice.
29 
30 
31 File:
32 		if_hdh.c
33 
34 Author:
35 		Art Berggreen
36 
37 Project:
38 		4.2BSD HDH
39 
40 Function:
41 		Device specific driver for IF-11/HDH under 4.2BSD
42     		networking code.
43 
44 Revision History:
45 		31-Aug-1984: V1.0 - First Implementation. A.B.
46 		 6-Nov-1984: V1.1 - Supress extra "LINE DOWN" msgs. A.B.
47 		13-Jan-1984: V1.2 - Add conditionals for TWG. A.B.
48 
49 \************************************************************************/
50 
51 
52 
53 
54 /* $Header$ */
55 
56 #include "hdh.h"
57 #ifdef NHDH > 0
58 
59 /*
60  *
61  * ACC IF-11/HDH interface
62  *
63  */
64 
65 #include "../machine/pte.h"
66 
67 #include "param.h"
68 #include "systm.h"
69 #include "mbuf.h"
70 #include "buf.h"
71 #include "protosw.h"
72 #include "socket.h"
73 #include "vmmac.h"
74 
75 #include "../net/if.h"
76 #include "../netimp/if_imp.h"
77 
78 #include "../vax/cpu.h"
79 #include "../vax/mtpr.h"
80 #include "../vaxuba/ubareg.h"
81 #include "../vaxuba/ubavar.h"
82 
83 #include "if_hdhreg.h"
84 #include "if_uba.h"
85 
86 int     hdhprobe(), hdhattach(), hdhrint(), hdhxint();
87 struct  uba_device *hdhinfo[NHDH];
88 u_short hdhstd[] = { 0 };
89 struct  uba_driver hdhdriver =
90 	{ hdhprobe, 0, hdhattach, 0, hdhstd, "hdh", hdhinfo };
91 
92 #define	HDHUNIT(x)	minor(x)
93 
94 int	hdhinit(), hdhstart(), hdhreset();
95 
96 /*
97  * "Lower half" of IMP interface driver.
98  *
99  * Each IMP interface is handled by a common module which handles
100  * the IMP-host protocol and a hardware driver which manages the
101  * hardware specific details of talking with the IMP.
102  *
103  * The hardware portion of the IMP driver handles DMA and related
104  * management of UNIBUS resources.  The IMP protocol module interprets
105  * contents of these messages and "controls" the actions of the
106  * hardware module during IMP resets, but not, for instance, during
107  * UNIBUS resets.
108  *
109  * The two modules are coupled at "attach time", and ever after,
110  * through the imp interface structure.  Higher level protocols,
111  * e.g. IP, interact with the IMP driver, rather than the HDH.
112  */
113 
114 #define NHDHCH	2		/* no. of FDX channels for HDH */
115 #define SUPR	0		/* supervisor channel */
116 #define	DATA	1		/* data channel */
117 #define HDHSUPR	0		/* supervisor read */
118 #define HDHSUPW	1		/* supervisor write */
119 #define HDHDATR	2		/* data read */
120 #define HDHDATW	3		/* data write */
121 
122 #define HDH_UP		2	/* HDH protocol is up */
123 #define HDH_STARTED	1	/* HDH has been initialized */
124 
125 #define HCBUSY	1		/* HDH HDX channel busy flag */
126 
127 /*
128 /* The IF-11/HDH has four independent dath flow channels between the
129 /* front-end and the host.  Two are used for reading and writing
130 /* control messages and two are used for data flow.  Each IF-11/HDH
131 /* has a device dependent data structure (hdh_softc) which contains
132 /* an array of four channel dependent structures (hdh_chan) to maintain
133 /* the context of each channel.  Channel structures can be linked into
134 /* a queue of I/O requests pending for the hardware interface.
135 /* UNIBUS mapping resources are allocated for each channel pair.
136 */
137 
138 struct	hdh_chan {		/* HDH HDX channel structure */
139 	struct hdh_chan	*hc_next;	/* link for Start I/O queuing */
140 	char		hc_chan;	/* HDX chan number */
141 	char		hc_adx;		/* extended UNIBUS address bits */
142 	short		hc_addr;	/* lower UNIBUS address bits */
143 	short		hc_cnt;		/* byte count */
144 	char		hc_func;	/* UMC I/O function */
145 	char		hc_sbfc;	/* UMC I/O subfunction */
146 	short		hc_flags;	/* status flags */
147 };
148 
149 struct	hdh_sioq {		/* Start I/O queue head structure */
150 	struct hdh_chan *sioq_head;	/* pointer to queue head */
151 	struct hdh_chan *sioq_tail;	/* pointer to queue tail */
152 };
153 
154 struct	hdh_softc {		/* HDH device dependent structure */
155 	struct ifnet	*hdh_if;	/* pointer to IMP's ifnet struct */
156 	struct impcb	*hdh_ic;	/* data structure shared with IMP */
157 	struct ifuba	hdh_ifuba[NHDHCH]; /* UNIBUS resources */
158 	struct hdh_chan hdh_chan[2*NHDHCH]; /* HDX HDH channels */
159 	struct hdh_sioq hdh_sioq;	/* start i/o queue */
160 	short		hdh_flags;	/* various status conditions */
161 } hdh_softc[NHDH];
162 
163 
164 /*
165  * Normally, code goes here to cause the device to interrupt to determine its
166  * interrupt vector.  However, since the UMC must be told its vector in order
167  * to interrupt, we allocate and return an unused vector and initialize the
168  * UMC.
169  */
170 hdhprobe(reg)
171 caddr_t reg;
172 {
173 	register int br, cvec;
174 	struct hdhregs *addr = (struct hdhregs *)reg;
175 #ifdef lint
176 	br = 0; cvec = br; br = cvec;
177 #endif
178 
179 	br = 0x15;			/* priority 21 (5 on UNIBUS) */
180 
181 #ifdef HDHDEBUG
182 	cvec = 0270;			/* use constant for now ... */
183 #else
184 
185 #ifdef VAXVMS				/* if VMS */
186 	cvec = 0270;			/*   we can't allocate vectors */
187 #else
188 	cvec = (uba_hd[numuba].uh_lastiv -= 4);  /* available vector */
189 #endif VAXVMS
190 
191 #endif HDHDEBUG
192 
193 	addr->ioini = (char) 0;		/* init UMC regs */
194 	addr->staack = (char) 0;	/*   pass vector */
195 	addr->ionmi = (char) 0;		/*     and kick UMC */
196 	addr->iochn = (char) (cvec >> 2);
197 	addr->csr = (short) HDH_RST;
198 	addr->csr = (short) (HDH_IEN|HDH_DMA|HDH_WRT); /* set enables */
199 	DELAY(5000);			/* give the UMC some time */
200 	return(1);
201 }
202 
203 /*
204  * Call the IMP module to allow it to set up its internal
205  * state, then tie the two modules together by setting up
206  * the back pointers to common data structures.
207  */
208 hdhattach(ui)
209 	struct uba_device *ui;
210 {
211 	register struct hdh_softc *sc = &hdh_softc[ui->ui_unit];
212 	register struct impcb *ip;
213 	struct ifimpcb {
214 		struct	ifnet ifimp_if;
215 		struct	impcb ifimp_impcb;
216 	} *ifimp;
217 
218 	if ((ifimp = (struct ifimpcb *)impattach(ui, hdhreset)) == 0)
219 		return;;
220 	sc->hdh_if = &ifimp->ifimp_if;
221 	ip = &ifimp->ifimp_impcb;
222 	sc->hdh_ic = ip;
223 	ip->ic_init = hdhinit;
224 	ip->ic_start = hdhstart;
225 	sc->hdh_ifuba[ui->ui_unit].ifu_flags = UBA_CANTWAIT;
226 }
227 
228 /*
229  * Reset interface after UNIBUS reset.
230  */
231 hdhreset(unit, uban)
232 int unit, uban;
233 {
234 	register struct uba_device *ui = hdhinfo[unit];
235 	register struct hdh_softc *sc = &hdh_softc[unit];
236 
237 #ifdef HDHDEBUG
238 	printf("HDH RESET\n");
239 #endif HDHDEBUG
240 
241 	if ((unit >= NHDH) || (ui == 0) || (ui->ui_alive == 0)
242 	    || (ui->ui_ubanum != uban))
243 		return;
244 	printf(" hdh%d", unit);
245 	sc->hdh_if->if_flags &= ~IFF_RUNNING;
246 	sc->hdh_flags = 0;
247 	(*sc->hdh_if->if_init)(unit);
248 }
249 
250 /*
251  * Initialize the imp interface.
252  */
253 
254 static char init_blk[] =
255     {
256 	HDHINIT,		/* SYSINIT opcode			*/
257 	HDHRQUP & 0xff,		/* control code (LSB)			*/
258 	(HDHRQUP>>8) & 0xff,	/* control code (MSB)			*/
259 	10,			/* command extension len		*/
260 	0,			/* loopback mode (off)			*/
261 	3,			/* our address (3=DTE)			*/
262 	1,			/* their address (1=DCE)		*/
263 	3,			/* frame ack t1 timeout			*/
264 	3,			/* poll ack timeout			*/
265 	30,			/* adm wait timeout			*/
266 	3,			/* rej wait timeout			*/
267 	10,			/* max retries				*/
268 	3,			/* watchdog timeout			*/
269 	0xaa			/* baud rate (0xaa=38.4KB)		*/
270 				/*   (output on RS-232 pin 24,		*/
271 				/*    send/receive timing is always	*/
272 				/*    taken from pins 15/17)		*/
273     };
274 
275 hdhinit(unit)
276 int unit;
277 {
278 	register struct hdh_softc *sc;
279 	register struct hdhregs *addr;
280 	register struct uba_device *ui;
281 	register struct umc_chan *up;
282 	register struct mbuf *m, *n;
283 	int i, s, ubano;
284 
285 #ifdef HDHDEBUG
286 	printf("HDH INIT\n");
287 #endif HDHDEBUG
288 
289 	if (unit >= NHDH || (ui = hdhinfo[unit]) == NULL
290 	    || ui->ui_alive == 0) {
291 		printf("hdh%d: not alive\n", unit);
292 		return(0);
293 	}
294 	addr = (struct hdhregs *)ui->ui_addr;
295 	sc = &hdh_softc[unit];
296 
297 	if (sc->hdh_flags & HDH_STARTED)
298 		return(1);
299 
300 	/*
301 	 * Alloc uba resources
302 	 */
303 	for(i=0;i<NHDHCH;i++) {
304 		if (if_ubainit(&sc->hdh_ifuba[i], ui->ui_ubanum, 0,
305 		    (int)btoc(IMPMTU)) == 0) {
306 			printf("hdh%d: cannot get chan %d uba resources\n",
307 				unit, i);
308 			ui->ui_alive = 0;
309 			return(0);
310 		}
311 	}
312 
313 	sc->hdh_if->if_flags |= IFF_RUNNING;
314 	sc->hdh_flags = HDH_STARTED;
315 
316 	/*
317 	 * hang a supervisor read (for line status)
318 	 */
319 	hdh_iorq(unit, HDHSUPR, IMPMTU, HDHRDB);
320 
321 	/*
322 	 * hang a data read
323 	 */
324 	hdh_iorq(unit, HDHDATR, IMPMTU, HDHRDB+HDHSTR);
325 
326 	/*
327 	 * bring up line to IMP
328 	 */
329 
330 	snd_supr(unit, init_blk, sizeof(init_blk));
331 
332 	return(1);
333 }
334 
335 /*
336  * Start an output operation on an mbuf.
337  */
338 hdhstart(dev)
339 dev_t dev;
340 {
341 	int unit = HDHUNIT(dev);
342 	register struct hdh_softc *sc = &hdh_softc[unit];
343 	register struct mbuf *m;
344         int len;
345 
346 	/*
347 	 * If output isn't active, attempt to
348 	 * start sending a new packet.
349 	 */
350 
351 	if (sc->hdh_ic->ic_oactive) {
352 		printf("hdh%d: start on active unit\n", unit);
353 		return;
354 	}
355 
356 	if ((sc->hdh_flags & HDH_UP) == 0) {
357 		sc->hdh_ic->ic_oactive = 0;	/* Link not up, can't xmit */
358 		return;
359 	}
360 
361 	IF_DEQUEUE(&sc->hdh_if->if_snd, m);
362 	if (m == 0) {
363 		sc->hdh_ic->ic_oactive = 0;
364 		return;
365 	}
366 
367 	len = if_wubaput(&sc->hdh_ifuba[DATA], m);	/* copy data to mapped mem */
368 	sc->hdh_ic->ic_oactive = 1;
369 
370 	hdh_iorq(unit, HDHDATW, len, HDHWRT+HDHEOS);
371 }
372 
373 /*
374  * Start i/o operation on a UMC logical channel
375  */
376 hdh_iorq(unit, lcn, len, func)
377 int unit, lcn, len, func;
378 {
379 	register struct hdh_softc *sc = &hdh_softc[unit];
380 	register struct hdh_chan *hc = &sc->hdh_chan[lcn];
381 	register int info, s;
382 
383 	/*
384 	 * If channel is busy (shouldn't be), drop.
385 	 */
386 	if  (hc->hc_flags & HCBUSY) {
387 		printf("hdh%d: channel busy lcn=%d\n", unit, lcn);
388 		return;
389 	}
390 
391  	/* get appropriate UNIBUS mapping info */
392 
393 	if (lcn & 1)		/* read or write? */
394 		info = sc->hdh_ifuba[lcn>>1].ifu_w.ifrw_info;
395 	else
396 		info = sc->hdh_ifuba[lcn>>1].ifu_r.ifrw_info;
397 
398 	/* set channel info */
399 
400 	hc->hc_flags |= HCBUSY;
401 	hc->hc_chan = lcn;
402 	hc->hc_adx = (char)((info & 0x30000) >> 12);
403 	hc->hc_addr = (unsigned short)(info & 0xffff);
404 	hc->hc_cnt = len;
405 	hc->hc_func = (char)func;
406 	hc->hc_sbfc = 0;
407 
408 	s = splimp();
409 	/*
410 	 * If UMC comm regs busy, queue start i/o for later.
411 	 */
412 	if (sc->hdh_sioq.sioq_head) {
413 		(sc->hdh_sioq.sioq_tail)->hc_next = hc;
414 		sc->hdh_sioq.sioq_tail = hc;
415 		hc->hc_next = 0;
416 		splx(s);
417 		return;
418 	}
419 
420 	/* start i/o on channel now */
421 
422 	sc->hdh_sioq.sioq_head = hc;
423 	sc->hdh_sioq.sioq_tail = hc;
424 	hc->hc_next = 0;
425 	start_chn(unit);
426 	splx(s);
427 }
428 
429 start_chn(unit)
430 int unit;
431 {
432 	register struct hdh_softc *sc = &hdh_softc[unit];
433 	register struct hdh_chan *hc = sc->hdh_sioq.sioq_head;
434 	register struct hdhregs *addr = (struct hdhregs *)hdhinfo[unit]->ui_addr;
435 
436 	/*
437 	 * Set up comm regs.
438 	 */
439 	addr->iochn = hc->hc_chan;
440 	addr->ioadx = hc->hc_adx;
441 	addr->ioadl = hc->hc_addr;
442 	addr->iocnt = hc->hc_cnt;
443 	addr->iofcn = hc->hc_func;
444 	addr->iosbf = hc->hc_sbfc;
445 	addr->ioini = 1;
446 
447 	/* signal UMC if necessary */
448 
449 	if (!(addr->ionmi)) {
450 		addr->ionmi = 1;
451 		addr->csr = HDH_DMA|HDH_WRT|HDH_IEN|HDH_NMI;
452 	}
453 }
454 
455 /*
456  * IF-11/HDH interrupt handler
457  */
458 hdhintr(unit)
459 int unit;
460 {
461 	register struct hdh_softc *sc = &hdh_softc[unit];
462 	register struct hdh_chan *hc;
463 	register struct hdhregs *addr = (struct hdhregs *)hdhinfo[unit]->ui_addr;
464 	register struct mbuf *m;
465 	int lcn, type, cc, cnt, s;
466 
467 	/*
468 	 * Check for hardware errors.
469 	 */
470 	if (addr->csr & HDH_UER) {
471 		printf("hdh%d: hard error csr=%b\n", unit, addr->csr, HDH_BITS);
472 		addr->csr = 0;		/* disable i/f */
473 		return;
474 	}
475 	/*
476 	 * Get logical channel info.
477 	 */
478 	if ((lcn = addr->stachn) >= (NHDHCH*2)) {
479 		printf("hdh%d: unknown channel lcn=%d\n", unit, lcn);
480 		return;
481 	}
482 
483 	hc = &sc->hdh_chan[lcn];
484 
485 	type = addr->statyp;
486 	cc = addr->stacc;
487 	cnt = hc->hc_cnt - addr->stacnt;
488 
489 	/* Figure out what kind of interrupt it was */
490 
491 	switch(type) {
492 
493 	case HDHSACK:		/* start i/o accepted */
494 		if (hc != sc->hdh_sioq.sioq_head) {
495 			printf("hdh%d: STARTIO error lcn=%d hc=%x sq=%x\n",
496 				unit, lcn, hc, sc->hdh_sioq.sioq_head);
497 			return;
498 		}
499 
500 		/* try to start any queued i/o request */
501 
502 		if (sc->hdh_sioq.sioq_head = sc->hdh_sioq.sioq_head->hc_next) {
503 			start_chn(unit);
504 		}
505 		break;
506 
507 	case HDHDONE:		/* i/o completion */
508 		switch (cc) {
509 
510 		case HDHIOCABT:
511 			printf("hdh%d: I/O abort ", unit);
512 			goto daterr;
513 
514 		case HDHIOCERR:
515 			printf("hdh%d: program error ", unit);
516 			goto daterr;
517 
518 		case HDHIOCOVR:
519 			printf("hdh%d: overrun error ", unit);
520 			goto daterr;
521 
522 		case HDHIOCUBE:
523 			printf("hdh%d: NXM timeout or UB parity error ", unit);
524 
525 		daterr:
526 			printf("lcn=%d func=%x\n", lcn, hc->hc_func);
527 			if (hc->hc_func & HDHRDB)
528 				sc->hdh_if->if_ierrors++;
529 			else
530 				sc->hdh_if->if_oerrors++;
531 		}
532 
533 		hc->hc_flags &= ~HCBUSY;
534 
535 		/* was it supervisor or data traffic? */
536 
537 		if (lcn > HDHSUPW)
538 			hdh_data(unit, lcn, cc, cnt);
539 		else
540 			hdh_supr(unit, lcn, cc, cnt);
541 
542 	}
543 
544 	/*
545 	 * Ack the interrupt
546 	 */
547 	addr->staack = 1;
548 	if (!(addr->ionmi)) {
549 		addr->ionmi = 1;
550 		addr->csr = HDH_DMA|HDH_WRT|HDH_IEN|HDH_NMI;
551 	}
552 }
553 
554 /*
555  * data channel interrupt completion handler
556  */
557 hdh_data(unit, lcn, cc, rcnt)
558 int unit, lcn, cc, rcnt;
559 {
560 	register struct hdh_softc *sc = &hdh_softc[unit];
561 	register struct hdh_chan *hc = &sc->hdh_chan[lcn];
562 	register struct mbuf *m;
563 
564 
565 	/* was it read or write? */
566 
567 	if (hc->hc_func & HDHRDB) {
568 		if (cc == HDHIOCOK) {
569 			/*
570 			 * Queue good packet for input
571 			 */
572 			sc->hdh_if->if_ipackets++;
573 			m = if_rubaget(&sc->hdh_ifuba[lcn>>1], rcnt, 0,
574 				sc->hdh_if);
575 			impinput(unit, m);
576 		}
577 
578 		/* hang a new data read */
579 
580 		hdh_iorq(unit, lcn, IMPMTU, HDHRDB+HDHSTR);
581 
582 	} else {
583 		/*
584 		 * fire up next output
585 		 */
586 		sc->hdh_if->if_opackets++;
587 		sc->hdh_ic->ic_oactive = 0;
588 		hdhstart(unit);
589 	}
590 }
591 
592 /*
593  * supervisor channel interrupt completion handler
594  */
595 hdh_supr(unit, lcn, cc, rcnt)
596 int unit, lcn, cc, rcnt;
597 {
598 	register struct hdh_softc *sc = &hdh_softc[unit];
599 	register struct hdh_chan *hc = &sc->hdh_chan[lcn];
600 	register struct uba_device *ui;
601 	short *p;
602 	int i;
603 
604 
605 	/* was it read or write? */
606 
607 	if (hc->hc_func & HDHRDB) {
608 		if (cc == HDHIOCOK) {
609 			p = (short *)(sc->hdh_ifuba[lcn>>1].ifu_r.ifrw_addr);
610 
611 			/* figure out what kind of supervisor message */
612 
613 			switch (*p) {
614 
615 			case HDHIACK:
616 			case HDHLNACK:
617 				break;
618 
619 			case HDHLNUP:
620 				printf("hdh%d: LINE UP\n", unit);
621 				sc->hdh_flags |= HDH_UP;
622 				hdhstart(unit);
623 				break;
624 
625 			case HDHLNDN:
626 				if (sc->hdh_flags & HDH_UP)
627 					printf("hdh%d: LINE DOWN\n", unit);
628 				sc->hdh_flags &= ~HDH_UP;
629 				break;
630 
631 			case HDHLOOP:
632 				break;
633 
634 			case HDHSQERR:
635 				printf("hdh%d: HOST SEQUENCE ERROR\n", unit);
636 				break;
637 
638 			case HDHSQRCV:
639 				printf("hdh%d: IMP SEQUENCE ERROR\n", unit);
640 				break;
641 
642 			case HDHDTERR:
643 				printf("hdh%d: HOST DATA ERROR\n", unit);
644 				break;
645 
646 			case HDHTIMO:
647 				printf("hdh%d: TIMEOUT\n", unit);
648 				break;
649 
650 			default:
651 				printf("hdh%d: supervisor error, code=%x\n",
652 					unit, *p);
653 			}
654 		}
655 
656 		/* hang a new supr read */
657 
658 		hdh_iorq(unit, HDHSUPR, IMPMTU, HDHRDB+HDHSTR);
659 	}
660 }
661 
662 snd_supr(unit, msg, len)
663 int unit, len;
664 char *msg;
665 {
666 	register struct hdh_softc *sc = &hdh_softc[unit];
667 	register struct hdh_chan *hc = &sc->hdh_chan[HDHSUPW];
668 	register struct mbuf *m;
669 	register char *p;
670 	register int cnt;
671 
672 	if ((m = m_get(M_DONTWAIT, MT_DATA)) == NULL) {
673 		printf("hdh%d: cannot get supervisor cmnd buffer\n", unit);
674 			return(0);
675 	}
676 
677 	cnt = len;
678 	m->m_len = len;
679 	p = mtod(m, char *);
680 
681 	while(cnt--) *p++ = *msg++;
682 
683 	cnt = if_wubaput(&sc->hdh_ifuba[SUPR], m);
684 
685 	hdh_iorq(unit, HDHSUPW, cnt, HDHWRT+HDHEOS);
686 
687 	return(1);
688 }
689 
690 #endif NHDH
691