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