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