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