xref: /csrg-svn/sys/vax/if/if_hdh.c (revision 32010)
1 /*	@(#)if_hdh.c	7.2 (Berkeley) 08/07/87 */
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(), hdhintr();
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 	hdhintr(0);
178 #endif
179 
180 	br = 0x15;			/* priority 21 (5 on UNIBUS) */
181 
182 #ifdef HDHDEBUG
183 	cvec = 0270;			/* use constant for now ... */
184 #else
185 
186 #ifdef VAXVMS				/* if VMS */
187 	cvec = 0270;			/*   we can't allocate vectors */
188 #else
189 	cvec = (uba_hd[numuba].uh_lastiv -= 4);  /* available vector */
190 #endif VAXVMS
191 
192 #endif HDHDEBUG
193 
194 	addr->ioini = (char) 0;		/* init UMC regs */
195 	addr->staack = (char) 0;	/*   pass vector */
196 	addr->ionmi = (char) 0;		/*     and kick UMC */
197 	addr->iochn = (char) (cvec >> 2);
198 	addr->csr = (short) HDH_RST;
199 	addr->csr = (short) (HDH_IEN|HDH_DMA|HDH_WRT); /* set enables */
200 	DELAY(5000);			/* give the UMC some time */
201 	return(1);
202 }
203 
204 /*
205  * Call the IMP module to allow it to set up its internal
206  * state, then tie the two modules together by setting up
207  * the back pointers to common data structures.
208  */
209 hdhattach(ui)
210 	struct uba_device *ui;
211 {
212 	register struct hdh_softc *sc = &hdh_softc[ui->ui_unit];
213 	register struct impcb *ip;
214 	struct ifimpcb {
215 		struct	ifnet ifimp_if;
216 		struct	impcb ifimp_impcb;
217 	} *ifimp;
218 
219 	if ((ifimp = (struct ifimpcb *)impattach(ui, hdhreset)) == 0)
220 		return;;
221 	sc->hdh_if = &ifimp->ifimp_if;
222 	ip = &ifimp->ifimp_impcb;
223 	sc->hdh_ic = ip;
224 	ip->ic_init = hdhinit;
225 	ip->ic_start = hdhstart;
226 	sc->hdh_ifuba[ui->ui_unit].ifu_flags = UBA_CANTWAIT;
227 }
228 
229 /*
230  * Reset interface after UNIBUS reset.
231  */
232 hdhreset(unit, uban)
233 int unit, uban;
234 {
235 	register struct uba_device *ui = hdhinfo[unit];
236 	register struct hdh_softc *sc = &hdh_softc[unit];
237 
238 #ifdef HDHDEBUG
239 	printf("HDH RESET\n");
240 #endif HDHDEBUG
241 
242 	if ((unit >= NHDH) || (ui == 0) || (ui->ui_alive == 0)
243 	    || (ui->ui_ubanum != uban))
244 		return;
245 	printf(" hdh%d", unit);
246 	sc->hdh_if->if_flags &= ~IFF_RUNNING;
247 	sc->hdh_flags = 0;
248 	(*sc->hdh_if->if_init)(unit);
249 }
250 
251 /*
252  * Initialize the imp interface.
253  */
254 
255 static char init_blk[] =
256     {
257 	HDHINIT,		/* SYSINIT opcode			*/
258 	HDHRQUP & 0xff,		/* control code (LSB)			*/
259 	(HDHRQUP>>8) & 0xff,	/* control code (MSB)			*/
260 	10,			/* command extension len		*/
261 	0,			/* loopback mode (off)			*/
262 	3,			/* our address (3=DTE)			*/
263 	1,			/* their address (1=DCE)		*/
264 	3,			/* frame ack t1 timeout			*/
265 	3,			/* poll ack timeout			*/
266 	30,			/* adm wait timeout			*/
267 	3,			/* rej wait timeout			*/
268 	10,			/* max retries				*/
269 	3,			/* watchdog timeout			*/
270 	0xaa			/* baud rate (0xaa=38.4KB)		*/
271 				/*   (output on RS-232 pin 24,		*/
272 				/*    send/receive timing is always	*/
273 				/*    taken from pins 15/17)		*/
274     };
275 
276 hdhinit(unit)
277 int unit;
278 {
279 	register struct hdh_softc *sc;
280 	register struct uba_device *ui;
281 	int i;
282 
283 #ifdef HDHDEBUG
284 	printf("HDH INIT\n");
285 #endif HDHDEBUG
286 
287 	if (unit >= NHDH || (ui = hdhinfo[unit]) == NULL
288 	    || ui->ui_alive == 0) {
289 		printf("hdh%d: not alive\n", unit);
290 		return(0);
291 	}
292 	sc = &hdh_softc[unit];
293 
294 	if (sc->hdh_flags & HDH_STARTED)
295 		return(1);
296 
297 	/*
298 	 * Alloc uba resources
299 	 */
300 	for(i=0;i<NHDHCH;i++) {
301 		if (if_ubainit(&sc->hdh_ifuba[i], ui->ui_ubanum, 0,
302 		    (int)btoc(IMPMTU + 2)) == 0) {
303 			printf("hdh%d: cannot get chan %d uba resources\n",
304 				unit, i);
305 			ui->ui_alive = 0;
306 			return(0);
307 		}
308 	}
309 
310 	sc->hdh_if->if_flags |= IFF_RUNNING;
311 	sc->hdh_flags = HDH_STARTED;
312 
313 	/*
314 	 * hang a supervisor read (for line status)
315 	 */
316 	hdh_iorq(unit, HDHSUPR, IMPMTU + 2, HDHRDB);
317 
318 	/*
319 	 * hang a data read
320 	 */
321 	hdh_iorq(unit, HDHDATR, IMPMTU + 2, HDHRDB+HDHSTR);
322 
323 	/*
324 	 * bring up line to IMP
325 	 */
326 
327 	snd_supr(unit, init_blk, sizeof(init_blk));
328 
329 	return(1);
330 }
331 
332 /*
333  * Start an output operation on an mbuf.
334  */
335 hdhstart(dev)
336 dev_t dev;
337 {
338 	int unit = HDHUNIT(dev);
339 	register struct hdh_softc *sc = &hdh_softc[unit];
340 	register struct mbuf *m;
341         int len;
342 
343 	/*
344 	 * If output isn't active, attempt to
345 	 * start sending a new packet.
346 	 */
347 
348 	if (sc->hdh_ic->ic_oactive) {
349 		printf("hdh%d: start on active unit\n", unit);
350 		return;
351 	}
352 
353 	if ((sc->hdh_flags & HDH_UP) == 0) {
354 		sc->hdh_ic->ic_oactive = 0;	/* Link not up, can't xmit */
355 		return;
356 	}
357 
358 	IF_DEQUEUE(&sc->hdh_if->if_snd, m);
359 	if (m == 0) {
360 		sc->hdh_ic->ic_oactive = 0;
361 		return;
362 	}
363 
364 	len = if_wubaput(&sc->hdh_ifuba[DATA], m);	/* copy data to mapped mem */
365 	sc->hdh_ic->ic_oactive = 1;
366 
367 	hdh_iorq(unit, HDHDATW, len, HDHWRT+HDHEOS);
368 }
369 
370 /*
371  * Start i/o operation on a UMC logical channel
372  */
373 hdh_iorq(unit, lcn, len, func)
374 int unit, lcn, len, func;
375 {
376 	register struct hdh_softc *sc = &hdh_softc[unit];
377 	register struct hdh_chan *hc = &sc->hdh_chan[lcn];
378 	register int info, s;
379 
380 	/*
381 	 * If channel is busy (shouldn't be), drop.
382 	 */
383 	if  (hc->hc_flags & HCBUSY) {
384 		printf("hdh%d: channel busy lcn=%d\n", unit, lcn);
385 		return;
386 	}
387 
388  	/* get appropriate UNIBUS mapping info */
389 
390 	if (lcn & 1)		/* read or write? */
391 		info = sc->hdh_ifuba[lcn>>1].ifu_w.ifrw_info;
392 	else
393 		info = sc->hdh_ifuba[lcn>>1].ifu_r.ifrw_info;
394 
395 	/* set channel info */
396 
397 	hc->hc_flags |= HCBUSY;
398 	hc->hc_chan = lcn;
399 	hc->hc_adx = (char)((info & 0x30000) >> 12);
400 	hc->hc_addr = (unsigned short)(info & 0xffff);
401 	hc->hc_cnt = len;
402 	hc->hc_func = (char)func;
403 	hc->hc_sbfc = 0;
404 
405 	s = splimp();
406 	/*
407 	 * If UMC comm regs busy, queue start i/o for later.
408 	 */
409 	if (sc->hdh_sioq.sioq_head) {
410 		(sc->hdh_sioq.sioq_tail)->hc_next = hc;
411 		sc->hdh_sioq.sioq_tail = hc;
412 		hc->hc_next = 0;
413 		splx(s);
414 		return;
415 	}
416 
417 	/* start i/o on channel now */
418 
419 	sc->hdh_sioq.sioq_head = hc;
420 	sc->hdh_sioq.sioq_tail = hc;
421 	hc->hc_next = 0;
422 	start_chn(unit);
423 	splx(s);
424 }
425 
426 start_chn(unit)
427 int unit;
428 {
429 	register struct hdh_softc *sc = &hdh_softc[unit];
430 	register struct hdh_chan *hc = sc->hdh_sioq.sioq_head;
431 	register struct hdhregs *addr = (struct hdhregs *)hdhinfo[unit]->ui_addr;
432 
433 	/*
434 	 * Set up comm regs.
435 	 */
436 	addr->iochn = hc->hc_chan;
437 	addr->ioadx = hc->hc_adx;
438 	addr->ioadl = hc->hc_addr;
439 	addr->iocnt = hc->hc_cnt;
440 	addr->iofcn = hc->hc_func;
441 	addr->iosbf = hc->hc_sbfc;
442 	addr->ioini = 1;
443 
444 	/* signal UMC if necessary */
445 
446 	if (!(addr->ionmi)) {
447 		addr->ionmi = 1;
448 		addr->csr = HDH_DMA|HDH_WRT|HDH_IEN|HDH_NMI;
449 	}
450 }
451 
452 /*
453  * IF-11/HDH interrupt handler
454  */
455 hdhintr(unit)
456 int unit;
457 {
458 	register struct hdh_softc *sc = &hdh_softc[unit];
459 	register struct hdh_chan *hc;
460 	register struct hdhregs *addr = (struct hdhregs *)hdhinfo[unit]->ui_addr;
461 	int lcn, type, cc, cnt;
462 
463 	/*
464 	 * Check for hardware errors.
465 	 */
466 	if (addr->csr & HDH_UER) {
467 		printf("hdh%d: hard error csr=%b\n", unit, addr->csr, HDH_BITS);
468 		addr->csr = 0;		/* disable i/f */
469 		return;
470 	}
471 	/*
472 	 * Get logical channel info.
473 	 */
474 	if ((lcn = addr->stachn) >= (NHDHCH*2)) {
475 		printf("hdh%d: unknown channel lcn=%d\n", unit, lcn);
476 		return;
477 	}
478 
479 	hc = &sc->hdh_chan[lcn];
480 
481 	type = addr->statyp;
482 	cc = addr->stacc;
483 	cnt = hc->hc_cnt - addr->stacnt;
484 
485 	/* Figure out what kind of interrupt it was */
486 
487 	switch(type) {
488 
489 	case HDHSACK:		/* start i/o accepted */
490 		if (hc != sc->hdh_sioq.sioq_head) {
491 			printf("hdh%d: STARTIO error lcn=%d hc=%x sq=%x\n",
492 				unit, lcn, hc, sc->hdh_sioq.sioq_head);
493 			return;
494 		}
495 
496 		/* try to start any queued i/o request */
497 
498 		if (sc->hdh_sioq.sioq_head = sc->hdh_sioq.sioq_head->hc_next) {
499 			start_chn(unit);
500 		}
501 		break;
502 
503 	case HDHDONE:		/* i/o completion */
504 		switch (cc) {
505 
506 		case HDHIOCABT:
507 			printf("hdh%d: I/O abort ", unit);
508 			goto daterr;
509 
510 		case HDHIOCERR:
511 			printf("hdh%d: program error ", unit);
512 			goto daterr;
513 
514 		case HDHIOCOVR:
515 			printf("hdh%d: overrun error ", unit);
516 			goto daterr;
517 
518 		case HDHIOCUBE:
519 			printf("hdh%d: NXM timeout or UB parity error ", unit);
520 
521 		daterr:
522 			printf("lcn=%d func=%x\n", lcn, hc->hc_func);
523 			if (hc->hc_func & HDHRDB)
524 				sc->hdh_if->if_ierrors++;
525 			else
526 				sc->hdh_if->if_oerrors++;
527 		}
528 
529 		hc->hc_flags &= ~HCBUSY;
530 
531 		/* was it supervisor or data traffic? */
532 
533 		if (lcn > HDHSUPW)
534 			hdh_data(unit, lcn, cc, cnt);
535 		else
536 			hdh_supr(unit, lcn, cc);
537 
538 	}
539 
540 	/*
541 	 * Ack the interrupt
542 	 */
543 	addr->staack = 1;
544 	if (!(addr->ionmi)) {
545 		addr->ionmi = 1;
546 		addr->csr = HDH_DMA|HDH_WRT|HDH_IEN|HDH_NMI;
547 	}
548 }
549 
550 /*
551  * data channel interrupt completion handler
552  */
553 hdh_data(unit, lcn, cc, rcnt)
554 int unit, lcn, cc, rcnt;
555 {
556 	register struct hdh_softc *sc = &hdh_softc[unit];
557 	register struct hdh_chan *hc = &sc->hdh_chan[lcn];
558 	register struct mbuf *m;
559 
560 
561 	/* was it read or write? */
562 
563 	if (hc->hc_func & HDHRDB) {
564 		if (cc == HDHIOCOK) {
565 			/*
566 			 * Queue good packet for input
567 			 */
568 			sc->hdh_if->if_ipackets++;
569 			m = if_rubaget(&sc->hdh_ifuba[lcn>>1], rcnt, 0,
570 				sc->hdh_if);
571 			impinput(unit, m);
572 		}
573 
574 		/* hang a new data read */
575 
576 		hdh_iorq(unit, lcn, IMPMTU + 2, HDHRDB+HDHSTR);
577 
578 	} else {
579 		/*
580 		 * fire up next output
581 		 */
582 		sc->hdh_if->if_opackets++;
583 		sc->hdh_ic->ic_oactive = 0;
584 		hdhstart(unit);
585 	}
586 }
587 
588 /*
589  * supervisor channel interrupt completion handler
590  */
591 hdh_supr(unit, lcn, cc)
592 int unit, lcn, cc;
593 {
594 	register struct hdh_softc *sc = &hdh_softc[unit];
595 	register struct hdh_chan *hc = &sc->hdh_chan[lcn];
596 	short *p;
597 
598 
599 	/* was it read or write? */
600 
601 	if (hc->hc_func & HDHRDB) {
602 		if (cc == HDHIOCOK) {
603 			p = (short *)(sc->hdh_ifuba[lcn>>1].ifu_r.ifrw_addr);
604 
605 			/* figure out what kind of supervisor message */
606 
607 			switch (*p) {
608 
609 			case HDHIACK:
610 			case HDHLNACK:
611 				break;
612 
613 			case HDHLNUP:
614 				printf("hdh%d: LINE UP\n", unit);
615 				sc->hdh_flags |= HDH_UP;
616 				hdhstart(unit);
617 				break;
618 
619 			case HDHLNDN:
620 				if (sc->hdh_flags & HDH_UP)
621 					printf("hdh%d: LINE DOWN\n", unit);
622 				sc->hdh_flags &= ~HDH_UP;
623 				break;
624 
625 			case HDHLOOP:
626 				break;
627 
628 			case HDHSQERR:
629 				printf("hdh%d: HOST SEQUENCE ERROR\n", unit);
630 				break;
631 
632 			case HDHSQRCV:
633 				printf("hdh%d: IMP SEQUENCE ERROR\n", unit);
634 				break;
635 
636 			case HDHDTERR:
637 				printf("hdh%d: HOST DATA ERROR\n", unit);
638 				break;
639 
640 			case HDHTIMO:
641 				printf("hdh%d: TIMEOUT\n", unit);
642 				break;
643 
644 			default:
645 				printf("hdh%d: supervisor error, code=%x\n",
646 					unit, *p);
647 			}
648 		}
649 
650 		/* hang a new supr read */
651 
652 		hdh_iorq(unit, HDHSUPR, IMPMTU + 2, HDHRDB+HDHSTR);
653 	}
654 }
655 
656 snd_supr(unit, msg, len)
657 int unit, len;
658 char *msg;
659 {
660 	register struct hdh_softc *sc = &hdh_softc[unit];
661 	register struct mbuf *m;
662 	register char *p;
663 	register int cnt;
664 
665 	if ((m = m_get(M_DONTWAIT, MT_DATA)) == NULL) {
666 		printf("hdh%d: cannot get supervisor cmnd buffer\n", unit);
667 			return;
668 	}
669 
670 	cnt = len;
671 	m->m_len = len;
672 	p = mtod(m, char *);
673 
674 	while(cnt--) *p++ = *msg++;
675 
676 	cnt = if_wubaput(&sc->hdh_ifuba[SUPR], m);
677 
678 	hdh_iorq(unit, HDHSUPW, cnt, HDHWRT+HDHEOS);
679 }
680 #endif NHDH
681