xref: /csrg-svn/sys/vax/if/if_hdh.c (revision 26310)
1 /*	@(#)if_hdh.c	6.5 (Berkeley) 02/21/86 */
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 uba_device *ui;
280 	int i;
281 
282 #ifdef HDHDEBUG
283 	printf("HDH INIT\n");
284 #endif HDHDEBUG
285 
286 	if (unit >= NHDH || (ui = hdhinfo[unit]) == NULL
287 	    || ui->ui_alive == 0) {
288 		printf("hdh%d: not alive\n", unit);
289 		return(0);
290 	}
291 	sc = &hdh_softc[unit];
292 
293 	if (sc->hdh_flags & HDH_STARTED)
294 		return(1);
295 
296 	/*
297 	 * Alloc uba resources
298 	 */
299 	for(i=0;i<NHDHCH;i++) {
300 		if (if_ubainit(&sc->hdh_ifuba[i], ui->ui_ubanum, 0,
301 		    (int)btoc(IMPMTU)) == 0) {
302 			printf("hdh%d: cannot get chan %d uba resources\n",
303 				unit, i);
304 			ui->ui_alive = 0;
305 			return(0);
306 		}
307 	}
308 
309 	sc->hdh_if->if_flags |= IFF_RUNNING;
310 	sc->hdh_flags = HDH_STARTED;
311 
312 	/*
313 	 * hang a supervisor read (for line status)
314 	 */
315 	hdh_iorq(unit, HDHSUPR, IMPMTU, HDHRDB);
316 
317 	/*
318 	 * hang a data read
319 	 */
320 	hdh_iorq(unit, HDHDATR, IMPMTU, HDHRDB+HDHSTR);
321 
322 	/*
323 	 * bring up line to IMP
324 	 */
325 
326 	snd_supr(unit, init_blk, sizeof(init_blk));
327 
328 	return(1);
329 }
330 
331 /*
332  * Start an output operation on an mbuf.
333  */
334 hdhstart(dev)
335 dev_t dev;
336 {
337 	int unit = HDHUNIT(dev);
338 	register struct hdh_softc *sc = &hdh_softc[unit];
339 	register struct mbuf *m;
340         int len;
341 
342 	/*
343 	 * If output isn't active, attempt to
344 	 * start sending a new packet.
345 	 */
346 
347 	if (sc->hdh_ic->ic_oactive) {
348 		printf("hdh%d: start on active unit\n", unit);
349 		return;
350 	}
351 
352 	if ((sc->hdh_flags & HDH_UP) == 0) {
353 		sc->hdh_ic->ic_oactive = 0;	/* Link not up, can't xmit */
354 		return;
355 	}
356 
357 	IF_DEQUEUE(&sc->hdh_if->if_snd, m);
358 	if (m == 0) {
359 		sc->hdh_ic->ic_oactive = 0;
360 		return;
361 	}
362 
363 	len = if_wubaput(&sc->hdh_ifuba[DATA], m);	/* copy data to mapped mem */
364 	sc->hdh_ic->ic_oactive = 1;
365 
366 	hdh_iorq(unit, HDHDATW, len, HDHWRT+HDHEOS);
367 }
368 
369 /*
370  * Start i/o operation on a UMC logical channel
371  */
372 hdh_iorq(unit, lcn, len, func)
373 int unit, lcn, len, func;
374 {
375 	register struct hdh_softc *sc = &hdh_softc[unit];
376 	register struct hdh_chan *hc = &sc->hdh_chan[lcn];
377 	register int info, s;
378 
379 	/*
380 	 * If channel is busy (shouldn't be), drop.
381 	 */
382 	if  (hc->hc_flags & HCBUSY) {
383 		printf("hdh%d: channel busy lcn=%d\n", unit, lcn);
384 		return;
385 	}
386 
387  	/* get appropriate UNIBUS mapping info */
388 
389 	if (lcn & 1)		/* read or write? */
390 		info = sc->hdh_ifuba[lcn>>1].ifu_w.ifrw_info;
391 	else
392 		info = sc->hdh_ifuba[lcn>>1].ifu_r.ifrw_info;
393 
394 	/* set channel info */
395 
396 	hc->hc_flags |= HCBUSY;
397 	hc->hc_chan = lcn;
398 	hc->hc_adx = (char)((info & 0x30000) >> 12);
399 	hc->hc_addr = (unsigned short)(info & 0xffff);
400 	hc->hc_cnt = len;
401 	hc->hc_func = (char)func;
402 	hc->hc_sbfc = 0;
403 
404 	s = splimp();
405 	/*
406 	 * If UMC comm regs busy, queue start i/o for later.
407 	 */
408 	if (sc->hdh_sioq.sioq_head) {
409 		(sc->hdh_sioq.sioq_tail)->hc_next = hc;
410 		sc->hdh_sioq.sioq_tail = hc;
411 		hc->hc_next = 0;
412 		splx(s);
413 		return;
414 	}
415 
416 	/* start i/o on channel now */
417 
418 	sc->hdh_sioq.sioq_head = hc;
419 	sc->hdh_sioq.sioq_tail = hc;
420 	hc->hc_next = 0;
421 	start_chn(unit);
422 	splx(s);
423 }
424 
425 start_chn(unit)
426 int unit;
427 {
428 	register struct hdh_softc *sc = &hdh_softc[unit];
429 	register struct hdh_chan *hc = sc->hdh_sioq.sioq_head;
430 	register struct hdhregs *addr = (struct hdhregs *)hdhinfo[unit]->ui_addr;
431 
432 	/*
433 	 * Set up comm regs.
434 	 */
435 	addr->iochn = hc->hc_chan;
436 	addr->ioadx = hc->hc_adx;
437 	addr->ioadl = hc->hc_addr;
438 	addr->iocnt = hc->hc_cnt;
439 	addr->iofcn = hc->hc_func;
440 	addr->iosbf = hc->hc_sbfc;
441 	addr->ioini = 1;
442 
443 	/* signal UMC if necessary */
444 
445 	if (!(addr->ionmi)) {
446 		addr->ionmi = 1;
447 		addr->csr = HDH_DMA|HDH_WRT|HDH_IEN|HDH_NMI;
448 	}
449 }
450 
451 /*
452  * IF-11/HDH interrupt handler
453  */
454 hdhintr(unit)
455 int unit;
456 {
457 	register struct hdh_softc *sc = &hdh_softc[unit];
458 	register struct hdh_chan *hc;
459 	register struct hdhregs *addr = (struct hdhregs *)hdhinfo[unit]->ui_addr;
460 	int lcn, type, cc, cnt;
461 
462 	/*
463 	 * Check for hardware errors.
464 	 */
465 	if (addr->csr & HDH_UER) {
466 		printf("hdh%d: hard error csr=%b\n", unit, addr->csr, HDH_BITS);
467 		addr->csr = 0;		/* disable i/f */
468 		return;
469 	}
470 	/*
471 	 * Get logical channel info.
472 	 */
473 	if ((lcn = addr->stachn) >= (NHDHCH*2)) {
474 		printf("hdh%d: unknown channel lcn=%d\n", unit, lcn);
475 		return;
476 	}
477 
478 	hc = &sc->hdh_chan[lcn];
479 
480 	type = addr->statyp;
481 	cc = addr->stacc;
482 	cnt = hc->hc_cnt - addr->stacnt;
483 
484 	/* Figure out what kind of interrupt it was */
485 
486 	switch(type) {
487 
488 	case HDHSACK:		/* start i/o accepted */
489 		if (hc != sc->hdh_sioq.sioq_head) {
490 			printf("hdh%d: STARTIO error lcn=%d hc=%x sq=%x\n",
491 				unit, lcn, hc, sc->hdh_sioq.sioq_head);
492 			return;
493 		}
494 
495 		/* try to start any queued i/o request */
496 
497 		if (sc->hdh_sioq.sioq_head = sc->hdh_sioq.sioq_head->hc_next) {
498 			start_chn(unit);
499 		}
500 		break;
501 
502 	case HDHDONE:		/* i/o completion */
503 		switch (cc) {
504 
505 		case HDHIOCABT:
506 			printf("hdh%d: I/O abort ", unit);
507 			goto daterr;
508 
509 		case HDHIOCERR:
510 			printf("hdh%d: program error ", unit);
511 			goto daterr;
512 
513 		case HDHIOCOVR:
514 			printf("hdh%d: overrun error ", unit);
515 			goto daterr;
516 
517 		case HDHIOCUBE:
518 			printf("hdh%d: NXM timeout or UB parity error ", unit);
519 
520 		daterr:
521 			printf("lcn=%d func=%x\n", lcn, hc->hc_func);
522 			if (hc->hc_func & HDHRDB)
523 				sc->hdh_if->if_ierrors++;
524 			else
525 				sc->hdh_if->if_oerrors++;
526 		}
527 
528 		hc->hc_flags &= ~HCBUSY;
529 
530 		/* was it supervisor or data traffic? */
531 
532 		if (lcn > HDHSUPW)
533 			hdh_data(unit, lcn, cc, cnt);
534 		else
535 			hdh_supr(unit, lcn, cc);
536 
537 	}
538 
539 	/*
540 	 * Ack the interrupt
541 	 */
542 	addr->staack = 1;
543 	if (!(addr->ionmi)) {
544 		addr->ionmi = 1;
545 		addr->csr = HDH_DMA|HDH_WRT|HDH_IEN|HDH_NMI;
546 	}
547 }
548 
549 /*
550  * data channel interrupt completion handler
551  */
552 hdh_data(unit, lcn, cc, rcnt)
553 int unit, lcn, cc, rcnt;
554 {
555 	register struct hdh_softc *sc = &hdh_softc[unit];
556 	register struct hdh_chan *hc = &sc->hdh_chan[lcn];
557 	register struct mbuf *m;
558 
559 
560 	/* was it read or write? */
561 
562 	if (hc->hc_func & HDHRDB) {
563 		if (cc == HDHIOCOK) {
564 			/*
565 			 * Queue good packet for input
566 			 */
567 			sc->hdh_if->if_ipackets++;
568 			m = if_rubaget(&sc->hdh_ifuba[lcn>>1], rcnt, 0,
569 				sc->hdh_if);
570 			impinput(unit, m);
571 		}
572 
573 		/* hang a new data read */
574 
575 		hdh_iorq(unit, lcn, IMPMTU, HDHRDB+HDHSTR);
576 
577 	} else {
578 		/*
579 		 * fire up next output
580 		 */
581 		sc->hdh_if->if_opackets++;
582 		sc->hdh_ic->ic_oactive = 0;
583 		hdhstart(unit);
584 	}
585 }
586 
587 /*
588  * supervisor channel interrupt completion handler
589  */
590 hdh_supr(unit, lcn, cc)
591 int unit, lcn, cc;
592 {
593 	register struct hdh_softc *sc = &hdh_softc[unit];
594 	register struct hdh_chan *hc = &sc->hdh_chan[lcn];
595 	short *p;
596 
597 
598 	/* was it read or write? */
599 
600 	if (hc->hc_func & HDHRDB) {
601 		if (cc == HDHIOCOK) {
602 			p = (short *)(sc->hdh_ifuba[lcn>>1].ifu_r.ifrw_addr);
603 
604 			/* figure out what kind of supervisor message */
605 
606 			switch (*p) {
607 
608 			case HDHIACK:
609 			case HDHLNACK:
610 				break;
611 
612 			case HDHLNUP:
613 				printf("hdh%d: LINE UP\n", unit);
614 				sc->hdh_flags |= HDH_UP;
615 				hdhstart(unit);
616 				break;
617 
618 			case HDHLNDN:
619 				if (sc->hdh_flags & HDH_UP)
620 					printf("hdh%d: LINE DOWN\n", unit);
621 				sc->hdh_flags &= ~HDH_UP;
622 				break;
623 
624 			case HDHLOOP:
625 				break;
626 
627 			case HDHSQERR:
628 				printf("hdh%d: HOST SEQUENCE ERROR\n", unit);
629 				break;
630 
631 			case HDHSQRCV:
632 				printf("hdh%d: IMP SEQUENCE ERROR\n", unit);
633 				break;
634 
635 			case HDHDTERR:
636 				printf("hdh%d: HOST DATA ERROR\n", unit);
637 				break;
638 
639 			case HDHTIMO:
640 				printf("hdh%d: TIMEOUT\n", unit);
641 				break;
642 
643 			default:
644 				printf("hdh%d: supervisor error, code=%x\n",
645 					unit, *p);
646 			}
647 		}
648 
649 		/* hang a new supr read */
650 
651 		hdh_iorq(unit, HDHSUPR, IMPMTU, HDHRDB+HDHSTR);
652 	}
653 }
654 
655 snd_supr(unit, msg, len)
656 int unit, len;
657 char *msg;
658 {
659 	register struct hdh_softc *sc = &hdh_softc[unit];
660 	register struct mbuf *m;
661 	register char *p;
662 	register int cnt;
663 
664 	if ((m = m_get(M_DONTWAIT, MT_DATA)) == NULL) {
665 		printf("hdh%d: cannot get supervisor cmnd buffer\n", unit);
666 			return(0);
667 	}
668 
669 	cnt = len;
670 	m->m_len = len;
671 	p = mtod(m, char *);
672 
673 	while(cnt--) *p++ = *msg++;
674 
675 	cnt = if_wubaput(&sc->hdh_ifuba[SUPR], m);
676 
677 	hdh_iorq(unit, HDHSUPW, cnt, HDHWRT+HDHEOS);
678 
679 	return(1);
680 }
681 #endif NHDH
682