xref: /csrg-svn/sys/tahoe/vba/mp.c (revision 33995)
1 /*	mp.c	1.4	88/04/14	*/
2 
3 #include "mp.h"
4 #if NMP > 0
5 /*
6  * Multi Protocol Communications Controller (MPCC).
7  * Asynchronous Terminal Protocol Support.
8  */
9 #include "../machine/pte.h"
10 #include "../machine/mtpr.h"
11 
12 #include "param.h"
13 #include "ioctl.h"
14 #include "tty.h"
15 #include "dir.h"
16 #include "user.h"
17 #include "map.h"
18 #include "buf.h"
19 #include "conf.h"
20 #include "file.h"
21 #include "uio.h"
22 #include "errno.h"
23 #include "syslog.h"
24 #include "vmmac.h"
25 #include "kernel.h"
26 #include "clist.h"
27 
28 #include "../tahoevba/vbavar.h"
29 #include "../tahoevba/mpreg.h"
30 
31 #define	MPCHUNK	16
32 #define	MPPORT(n)	((n) & 0xf)
33 #define	MPUNIT(n)	((n) >> 4)
34 
35 /*
36  * Driver information for auto-configuration stuff.
37  */
38 int     mpprobe(), mpattach(), mpintr();
39 struct  vba_device *mpinfo[NMP];
40 long    mpstd[] = { 0 };
41 struct  vba_driver mpdriver =
42     { mpprobe, 0, mpattach, 0, mpstd, "mp", mpinfo };
43 
44 int	mpstart();
45 struct	mpevent *mpparam();
46 struct	mpevent *mp_getevent();
47 
48 /*
49  * The following structure is needed to deal with mpcc's convoluted
50  * method for locating it's mblok structures (hold your stomach).
51  * When an mpcc is reset at boot time it searches host memory
52  * looking for a string that says ``ThIs Is MpCc''.  The mpcc
53  * then reads the structure to locate the pointer to it's mblok
54  * structure (you can wretch now).
55  */
56 struct mpbogus {
57 	char	s[12];			/* `ThIs Is MpCc'' */
58 	u_char	status;
59 	u_char	unused;
60 	u_short	magic;
61 	struct	mblok *mb;
62 	struct	mblok *mbloks[NMP];	/* can support at most 16 mpcc's */
63 } mpbogus = { 'T','h','I','s',' ','I','s',' ','M','p','C','c' };
64 
65 /*
66  * Software state per unit.
67  */
68 struct	mpsoftc {
69 	u_int	ms_ivec;		/* interrupt vector */
70 	u_int	ms_softCAR;		/* software carrier for async */
71 	struct	mblok *ms_mb;		/* mpcc status area */
72 	struct	vb_buf ms_buf;		/* vba resources for ms_mb */
73 	struct	hxmtl ms_hxl[MPMAXPORT];/* host transmit list */
74 	struct	asyncparam ms_async[MPMAXPORT][MPINSET];/* async structs */
75 	char	ms_cbuf[MPMAXPORT][MPOUTSET][CBSIZE];/* input character buffers */
76 } mp_softc[NMP];
77 
78 struct	tty mp_tty[NMP*MPCHUNK];
79 #ifndef lint
80 int	nmp = NMP*MPCHUNK;
81 #endif
82 
83 int	ttrstrt();
84 
85 mpprobe(reg, vi)
86 	caddr_t reg;
87 	struct vba_device *vi;
88 {
89 	register int br, cvec;
90 	register struct mpsoftc *ms;
91 
92 #ifdef lint
93 	br = 0; cvec = br; br = cvec;
94 	mpintr(0);
95 #endif
96 	if (badaddr(reg, 2))
97 		return (0);
98 	ms = &mp_softc[vi->ui_unit];
99 	/*
100 	 * Allocate page tables and mblok
101 	 * structure (mblok in non-cached memory).
102 	 */
103 	if (vbainit(&ms->ms_buf, sizeof (struct mblok), VB_32BIT) == 0) {
104 		printf("mp%d: vbainit failed\n", vi->ui_unit);
105 		return (0);
106 	}
107 	ms->ms_mb = (struct mblok *)ms->ms_buf.vb_rawbuf;
108 	ms->ms_ivec = MPINTRBASE + 2*vi->ui_unit;	/* XXX */
109 	br = 0x14, cvec = ms->ms_ivec;			/* XXX */
110 	return (sizeof (struct mblok));
111 }
112 
113 mpattach(vi)
114 	register struct vba_device *vi;
115 {
116 	register struct mpsoftc *ms = &mp_softc[vi->ui_unit];
117 
118 	ms->ms_softCAR = vi->ui_flags;
119 	/*
120 	 * Setup pointer to mblok, initialize bogus
121 	 * status block used by mpcc to locate the pointer
122 	 * and then poke the mpcc to get it to search host
123 	 * memory to find mblok pointer.
124 	 */
125 	mpbogus.mbloks[vi->ui_unit] = (struct mblok *)ms->ms_buf.vb_physbuf;
126 	*(short *)vi->ui_addr = 0x100;		/* magic */
127 }
128 
129 /*
130  * Open an mpcc port.
131  */
132 mpopen(dev, mode)
133 	dev_t dev;
134 {
135 	register struct tty *tp;
136 	register struct mpsoftc *ms;
137 	int error, s, port, unit, mpu;
138 	struct vba_device *vi;
139 	struct mpport *mp;
140 	struct mpevent *ev;
141 
142 	unit = minor(dev);
143 	mpu = MPUNIT(unit);
144 	if (mpu >= NMP || (vi = mpinfo[mpu]) == 0 || vi->ui_alive == 0)
145 		return (ENXIO);
146 	tp = &mp_tty[unit];
147 	if (tp->t_state & TS_XCLUDE && u.u_uid != 0)
148 		return (EBUSY);
149 	ms = &mp_softc[mpu];
150 	port = MPPORT(unit);
151 	if (ms->ms_mb->mb_proto[port] != MPPROTO_ASYNC ||
152 	    ms->ms_mb->mb_status != MP_OPOPEN)
153 		return (ENXIO);
154 	mp = &ms->ms_mb->mb_port[port];		/* host mpcc struct */
155 	s = spl8();
156 	while (mp->mp_flags & MP_PROGRESS)
157 		sleep((caddr_t)&tp->t_canq, TTIPRI);
158 	while (tp->t_state & TS_WOPEN)
159 		sleep((caddr_t)&tp->t_canq, TTIPRI);
160 	if (tp->t_state & TS_ISOPEN) {
161 		splx(s);
162 		return (0);
163 	}
164 	tp->t_state |= TS_WOPEN;
165 	tp->t_addr = (caddr_t)ms;
166 	tp->t_oproc = mpstart;
167 	tp->t_dev = dev;
168 	ttychars(tp);
169 	if (tp->t_ispeed == 0) {
170 		tp->t_ispeed = B9600;
171 		tp->t_ospeed = B9600;
172 		tp->t_flags |= ODDP|EVENP|ECHO;
173 	}
174 	/*
175 	 * Initialize port state: init MPCC interface
176 	 * structures for port and setup modem control.
177 	 */
178 	mp->mp_proto = MPPROTO_ASYNC;		/* XXX */
179 	error = mpportinit(ms, mp, port);
180 	if (error)
181 		goto bad;
182 	ev = mpparam(unit);
183 	if (ev == 0) {
184 		error = ENOBUFS;
185 		goto bad;
186 	}
187 	mpcmd(ev, EVCMD_OPEN, 0, ms->ms_mb, port);
188 	while ((tp->t_state & TS_CARR_ON) == 0)
189 		sleep((caddr_t)&tp->t_rawq, TTIPRI);
190 	error = mpmodem(unit, MMOD_ON);
191 	if (error)
192 		goto bad;
193 	while ((tp->t_state & TS_CARR_ON) == 0)
194 		sleep((caddr_t)&tp->t_rawq, TTIPRI);
195 	error = (*linesw[tp->t_line].l_open)(dev,tp);
196 done:
197 	splx(s);
198 	/* wakeup anyone waiting for open to complete */
199 	wakeup((caddr_t)&tp->t_canq);
200 
201 	return (error);
202 bad:
203 	tp->t_state &= ~TS_WOPEN;
204 	goto done;
205 }
206 
207 /*
208  * Close an mpcc port.
209  */
210 mpclose(dev)
211 	dev_t dev;
212 {
213 	register struct tty *tp;
214 	register struct mpport *mp;
215 	register struct mpevent *ev;
216 	int s, port, unit, error;
217 	struct mblok *mb;
218 
219 	unit = minor(dev);
220 	tp = &mp_tty[unit];
221 	port = MPPORT(unit);
222 	mb = mp_softc[MPUNIT(unit)].ms_mb;
223 	mp = &mb->mb_port[port];
224 	s = spl8();
225 	if (mp->mp_flags & MP_PROGRESS) {		/* close in progress */
226 		if (mp->mp_flags & MP_REMBSY) {
227 			mp->mp_flags &= ~MP_REMBSY;
228 			splx(s);
229 			return (0);
230 		}
231 		while (mp->mp_flags & MP_PROGRESS)
232 			sleep((caddr_t)&tp->t_canq,TTIPRI);
233 	}
234 	error = 0;
235 	mp->mp_flags |= MP_PROGRESS;
236 	(*linesw[tp->t_line].l_close)(tp);
237 	if (tp->t_state & TS_HUPCLS || (tp->t_state & TS_ISOPEN) == 0)
238 		if (error = mpmodem(unit, MMOD_OFF)) {
239 			mp->mp_flags &= ~MP_PROGRESS;
240 			goto out;
241 		}
242 	while (tp->t_state & TS_FLUSH)			/* ??? */
243 		sleep((caddr_t)&tp->t_state, TTOPRI);	/* ??? */
244 	ttyclose(tp);
245 	ev = mp_getevent(mp, unit);
246 	if (ev == 0) {
247 		 error = ENOBUFS;
248 		 goto out;
249 	}
250 	mpcmd(ev, EVCMD_CLOSE, 0, mb, port);
251 out:
252 	if (mp->mp_flags & MP_REMBSY)
253 		mpclean(mb, port);
254 	splx(s);
255 	return (error);
256 }
257 
258 /*
259  * Read from an mpcc port.
260  */
261 mpread(dev, uio)
262 	dev_t dev;
263 	struct uio *uio;
264 {
265 	struct tty *tp;
266 
267 	tp = &mp_tty[minor(dev)];
268 	return ((*linesw[tp->t_line].l_read)(tp, uio));
269 }
270 
271 /*
272  * Write to an mpcc port.
273  */
274 mpwrite(dev, uio)
275 	dev_t dev;
276 	struct uio *uio;
277 {
278 	struct tty *tp;
279 
280 	tp = &mp_tty[minor(dev)];
281 	return ((*linesw[tp->t_line].l_write)(tp, uio));
282 }
283 
284 /*
285  * Ioctl for a mpcc port
286  */
287 mpioctl(dev, cmd, data, flag)
288 	dev_t dev;
289 	caddr_t data;
290 {
291 	register struct tty *tp;
292 	register struct mpsoftc *ms;
293 	register struct mpevent *ev;
294 	register struct mpport *mp;
295 	int s, port, error, unit;
296 	struct mblok *mb;
297 
298 	unit = minor(dev);
299 	tp = &mp_tty[unit];
300 	ms = &mp_softc[MPUNIT(unit)];
301 	mb = ms->ms_mb;
302 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
303 	if (error >= 0)
304 		return (error);
305 	error = ttioctl(tp, cmd, data, flag);
306 	if (error >= 0) {
307 		if (cmd == TIOCSETP || cmd == TIOCSETN || cmd == TIOCLBIS ||
308 		    cmd == TIOCLBIC || cmd == TIOCLSET) {
309 			ev = mpparam(unit);
310 			if (ev == 0)
311 				error = ENOBUFS;
312 			else
313 				mpcmd(ev, EVCMD_IOCTL, A_CHGALL, mb,
314 				    MPPORT(unit));
315 		}
316 		return (error);
317 	}
318 	switch (cmd) {
319 	case TIOCSBRK:			/* send break */
320 	case TIOCCBRK:			/* clear break */
321 		port = MPPORT(unit);
322 		mp = &mb->mb_port[port];
323 		s = spl8();
324 		ev = mp_getevent(mp, unit);
325 		if (ev)
326 			mpcmd(ev, EVCMD_IOCTL,
327 			    (cmd == TIOCSBRK ? A_BRKON : A_BRKOFF),
328 			    mb, port);
329 		else
330 			error = ENOBUFS;
331 		splx(s);
332 		break;
333 	case TIOCSDTR:			/* set dtr control line */
334 		break;
335 	case TIOCCDTR:			/* clear dtr control line */
336 		break;
337 	default:
338 		error = ENOTTY;
339 		break;
340 	}
341 	return (error);
342 }
343 
344 struct mpevent *
345 mpparam(unit)
346 	int unit;
347 {
348 	register struct mpevent *ev;
349 	register struct mpport *mp;
350 	register struct tty *tp;
351 	struct mblok *mb;
352 	struct mpsoftc *ms;
353 	register struct asyncparam *asp;
354 	int port;
355 
356 	ms = &mp_softc[MPUNIT(unit)];
357 	mb = ms->ms_mb;
358 	port = MPPORT(unit);
359 	mp = &mb->mb_port[port];
360 	ev = mp_getevent(mp, unit);	/* XXX */
361 	if (ev == 0)
362 		return (ev);
363 	tp = &mp_tty[unit];
364 	/* YUCK */
365 	asp = &ms->ms_async[port][mp->mp_on?mp->mp_on-1:MPINSET-1];
366 	asp->ap_xon = tp->t_startc;
367 	asp->ap_xoff = tp->t_stopc;
368 	asp->ap_xena = ((tp->t_flags & RAW) ? MPA_DIS : MPA_ENA);
369 	asp->ap_xany = ((tp->t_flags & DECCTQ) ? MPA_DIS : MPA_ENA);
370 #ifdef notnow
371 	if (tp->t_flags & (RAW|LITOUT|PASS8)) {
372 #endif
373 		asp->ap_data = MPCHAR_8;
374 		asp->ap_parity = MPPAR_NONE;
375 #ifdef notnow
376 	} else {
377 		asp->ap_data = MPCHAR_7;
378 		if ((tp->t_flags & (EVENP|ODDP)) == ODDP)
379 			asp->ap_parity = MPPAR_ODD;
380 		else
381 			asp->ap_parity = MPPAR_EVEN;
382 	}
383 #endif
384 	if (tp->t_ospeed == B110)
385 		asp->ap_stop = MPSTOP_2;
386 	else
387 		asp->ap_stop = MPSTOP_1;
388 	if (tp->t_ospeed == EXTA || tp->t_ospeed == EXTB)
389 		asp->ap_baud = M19200;
390 	else
391 		asp->ap_baud = tp->t_ospeed;
392 	asp->ap_loop = MPA_DIS;		/* disable loopback */
393 	asp->ap_rtimer = A_RCVTIM;	/* default receive timer */
394 	if (ms->ms_softCAR & (1<<port))
395 		setm(&asp->ap_modem, A_DTR, ASSERT);
396 	else
397 		setm(&asp->ap_modem, A_DTR, AUTO);
398 	seti(&asp->ap_intena, A_DCD);
399 	return (ev);
400 }
401 
402 mpstart(tp)
403 	register struct tty *tp;
404 {
405 	register struct mpevent *ev;
406 	register struct mpport *mp;
407 	struct mblok *mb;
408 	struct mpsoftc *ms;
409 	int port, unit, xcnt, n, s, i;
410 	struct	hxmtl *hxp;
411 	struct clist outq;
412 
413 	s = spl8();
414 	unit = minor(tp->t_dev);
415 	ms = &mp_softc[MPUNIT(unit)];
416 	mb = ms->ms_mb;
417 	port = MPPORT(unit);
418 	mp = &mb->mb_port[port];
419 	hxp = &ms->ms_hxl[port];
420 	xcnt = 0;
421 	outq = tp->t_outq;
422 	for (i = 0; i < MPXMIT; i++) {
423 		if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))
424 			break;
425 		if (outq.c_cc <= TTLOWAT(tp)) {
426 			if (tp->t_state & TS_ASLEEP) {
427 				tp->t_state &= ~TS_ASLEEP;
428 				wakeup((caddr_t)&tp->t_outq);
429 			}
430 			if (tp->t_wsel) {
431 				selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
432 				tp->t_wsel = 0;
433 				tp->t_state &= ~TS_WCOLL;
434 			}
435 		}
436 		if (outq.c_cc == 0)
437 			break;
438 		/*
439 		 * If we're not currently busy outputting,
440 		 * and there is data to be output, set up
441 		 * port transmit structure to send to mpcc.
442 		 */
443 		if (tp->t_flags & (RAW|LITOUT))
444 			n = ndqb(&outq, 0);
445 		else {
446 			n = ndqb(&outq, 0200);
447 			if (n == 0) {
448 				n = getc(&outq);
449 				timeout(ttrstrt, (caddr_t)tp, (n&0177)+6);
450 				tp->t_state |= TS_TIMEOUT;
451 				break;
452 			}
453 		}
454 		hxp->dblock[i] = (caddr_t)vtoph(0, (int)outq.c_cf);
455 		hxp->size[i] = n;
456 		xcnt++;		/* count of xmts to send */
457 		ndadvance(&outq, n);
458 	}
459 	/*
460 	 * If data to send, poke mpcc.
461 	 */
462 	if (xcnt) {
463 		ev = mp_getevent(mp, unit);
464 		if (ev == 0) {
465 			tp->t_state &= ~(TS_BUSY|TS_TIMEOUT);
466 		} else {
467 			tp->t_state |= TS_BUSY;
468 			ev->ev_count = xcnt;
469 			mpcmd(ev, EVCMD_WRITE, 0, mb, MPPORT(unit));
470 		}
471 	}
472 	splx(s);
473 }
474 
475 /*
476  * Advance cc bytes from q  but don't free memory.
477  */
478 ndadvance(q, cc)
479 	register struct clist *q;
480 	register cc;
481 {
482 	register struct cblock *bp;
483 	char *end;
484 	int rem, s;
485 
486 	s = spltty();
487 	if (q->c_cc <= 0)
488 		goto out;
489 	while (cc>0 && q->c_cc) {
490 		bp = (struct cblock *)((int)q->c_cf & ~CROUND);
491 		if ((int)bp == (((int)q->c_cl-1) & ~CROUND)) {
492 			end = q->c_cl;
493 		} else {
494 			end = (char *)((int)bp + sizeof (struct cblock));
495 		}
496 		rem = end - q->c_cf;
497 		if (cc >= rem) {
498 			cc -= rem;
499 			q->c_cc -= rem;
500 			q->c_cf = bp->c_next->c_info;
501 		} else {
502 			q->c_cc -= cc;
503 			q->c_cf += cc;
504 			break;
505 		}
506 	}
507 	if (q->c_cc <= 0) {
508 		q->c_cf = q->c_cl = NULL;
509 		q->c_cc = 0;
510 	}
511 out:
512 	splx(s);
513 }
514 
515 /*
516  * Stop output on a line, e.g. for ^S/^Q or output flush.
517  */
518 mpstop(tp, rw)
519 	register struct tty *tp;
520 	int rw;
521 {
522 	int s, port;
523 	struct mpevent *ev;
524 	struct mblok *mb;
525 
526 	s = spl8();
527 	/* XXX: DISABLE TRANSMITTER */
528 	if (tp->t_state & TS_BUSY) {
529 		if ((tp->t_state & TS_TTSTOP) == 0)
530 			tp->t_state |= TS_FLUSH;
531 	}
532 	splx(s);
533 }
534 
535 /*
536  * Initialize an async port's MPCC state.
537  */
538 mpportinit(ms, mp, port)
539 	register struct mpsoftc *ms;
540 	register struct mpport *mp;
541 	int port;
542 {
543 	register struct mpevent *ev;
544 	register int i;
545 	caddr_t ptr;
546 
547 	mp->mp_on = mp->mp_off = 0;
548 	mp->mp_nextrcv = 0;
549 	mp->mp_flags = 0;
550 	ev = &mp->mp_recvq[0];
551 	for (i = 0; ev < &mp->mp_recvq[MPINSET]; ev++, i++) {
552 		ev->ev_status = EVSTATUS_FREE;
553 		ev->ev_cmd = 0;
554 		ev->ev_opts = 0;
555 		ev->ev_error = 0;
556 		ev->ev_flags = 0;
557 		ev->ev_count = 0;
558 		ev->ev_un.hxl = (struct hxmtl *) vtoph(0, &ms->ms_hxl[port]);
559 		ev->ev_params = (caddr_t) vtoph(0, &ms->ms_async[port][i]);
560 	}
561 	ev = &mp->mp_sendq[0];
562 	for (i = 0; ev < &mp->mp_sendq[MPOUTSET]; ev++, i++) {
563 		/* init so that L2 can't send any events */
564 		/* to host until open has completed      */
565 		ev->ev_status = EVSTATUS_FREE;
566 		ev->ev_cmd = 0;
567 		ev->ev_error = 0;
568 		ev->ev_flags = 0;
569 		ev->ev_count = 0;
570 		ptr = (caddr_t) &ms->ms_cbuf[port][i][0];
571 		ev->ev_un.rcvblk = (u_char *)vtoph(0, ptr);
572 		ev->ev_params = (caddr_t) vtoph(0, ptr);
573 	}
574 	return (0);
575 }
576 
577 /*
578  * Send an event to an mpcc.
579  */
580 mpcmd(ev, cmd, flags, mb, port)
581 	register struct mpevent *ev;
582 	struct mblok *mb;
583 {
584 	int s;
585 
586 	s = spl8();
587 	/* move host values to inbound entry */
588 	ev->ev_cmd = cmd;
589 	ev->ev_opts = flags;
590 	/* show event ready for mpcc */
591 	ev->ev_status = EVSTATUS_GO;
592 	mpintmpcc(mb, port);
593 	splx(s);
594 }
595 
596 /*
597  * Return the next available event entry for the indicated port.
598  */
599 struct mpevent *
600 mp_getevent(mp, unit)
601 	register struct mpport *mp;
602 	int unit;
603 {
604 	register struct mpevent *ev;
605 	int i, s;
606 
607 	s = spl8();
608 	ev = &mp->mp_recvq[mp->mp_on];
609 	if (ev->ev_status != EVSTATUS_FREE)
610 		goto bad;
611 	/*
612 	 * If not a close request, verify one extra
613 	 * event is available for closing the port.
614 	 */
615 	if ((mp->mp_flags && MP_PROGRESS) == 0) {
616 		if ((i = mp->mp_on + 1) >= MPINSET)
617 			i = 0;
618 		if (mp->mp_recvq[i].ev_status != EVSTATUS_FREE)
619 			goto bad;
620 	}
621 	/* init inbound fields marking this entry as busy */
622 	ev->ev_error = 0;
623 	ev->ev_flags = 0;
624 	ev->ev_count = 0;
625 	ev->ev_status = EVSTATUS_BUSY;
626 	/* adjust pointer to next available inbound entry */
627 	adjptr(mp->mp_on, MPINSET);
628 	splx(s);
629 	return (ev);
630 bad:
631 	splx(s);
632 	log(LOG_ERR, "mp%d: port%d, out of events", MPUNIT(unit), MPPORT(unit));
633 	return ((struct mpevent *)0);
634 }
635 
636 mpmodem(unit, flag)
637 	int unit, flag;
638 {
639 	struct mpsoftc *ms = &mp_softc[MPUNIT(unit)];
640 	int port = MPPORT(unit);
641 	register struct mpport *mp;
642 	register struct mpevent *ev;
643 	register struct asyncparam *asp;
644 
645 	mp = &ms->ms_mb->mb_port[port];
646 	ev = mp_getevent(mp, unit);
647 	if (ev == 0)
648 		return (ENOBUFS);
649 	/* YUCK */
650 	asp = &ms->ms_async[port][mp->mp_on?mp->mp_on-1:MPINSET-1];
651 	if (flag == MMOD_ON) {
652 		if (ms->ms_softCAR & (1 << port))
653 			setm(&asp->ap_modem, A_DTR, ASSERT);
654 		else
655 			setm(&asp->ap_modem, A_DTR, AUTO);
656 		seti(&asp->ap_intena, A_DCD);
657 	} else {
658 		setm(&asp->ap_modem, 0, DROP);
659 		seti(&asp->ap_intena, 0);
660 	}
661 	mpcmd(ev, EVCMD_IOCTL, A_MDMCHG, ms->ms_mb, port);
662 	return (0);
663 }
664 
665 /*
666  * Set up the modem control structure according to mask.
667  * Each set bit in the mask means assert the corresponding
668  * modem control line, otherwise, it will be dropped.
669  * RTS is special since it can either be asserted, dropped
670  * or put in auto mode for auto modem control.
671  */
672 static
673 setm(mc, mask, rts)
674 	register struct mdmctl *mc;
675 	register int mask;
676 {
677 
678 	mc->mc_rngdsr = (mask & A_RNGDSR) ? ASSERT : DROP;
679 	mc->mc_rate = (mask & A_RATE) ? ASSERT : DROP;
680 	mc->mc_dcd = (mask & A_DCD) ? ASSERT : DROP;
681 	mc->mc_sectx = (mask & A_SECTX) ? ASSERT : DROP;
682 	mc->mc_cts = (mask & A_CTS) ? ASSERT : DROP;
683 	mc->mc_secrx = (mask & A_SECRX) ? ASSERT : DROP;
684 	mc->mc_dtr = (mask & A_DTR) ? ASSERT : DROP;
685 	mc->mc_rts = rts;
686 }
687 
688 /*
689  * Set up the status change enable field from mask.
690  * When a signal is enabled in this structure and
691  * and a change in state on a corresponding modem
692  * control line occurs, a status change event will
693  * be delivered to the host.
694  */
695 static
696 seti(mc, mask)
697 	register struct mdmctl *mc;
698 	register int mask;
699 {
700 
701 	mc->mc_rngdsr = (mask & A_RNGDSR) ? MDM_ON : MDM_OFF;
702 	mc->mc_rate = (mask & A_RATE) ? MDM_ON : MDM_OFF;
703 	mc->mc_dcd = (mask & A_DCD) ? MDM_ON : MDM_OFF;
704 	mc->mc_sectx = (mask & A_SECTX) ? MDM_ON : MDM_OFF;
705 	mc->mc_cts = (mask & A_CTS) ? MDM_ON : MDM_OFF;
706 	mc->mc_secrx = (mask & A_SECRX) ? MDM_ON : MDM_OFF;
707 	mc->mc_dtr = (mask & A_DTR) ? MDM_ON : MDM_OFF;
708 	mc->mc_rts = (mask & A_RTS) ? MDM_ON : MDM_OFF;
709 }
710 
711 mpcleanport(mb, port)
712 	struct mblok *mb;
713 	int port;
714 {
715 	register struct mpport *mp;
716 	register struct tty *tp;
717 
718 	mp = &mb->mb_port[port];
719 	if (mp->mp_proto == MPPROTO_ASYNC) {
720 		mp->mp_flags = MP_REMBSY;
721 		/* flush I/O queues and send hangup signals */
722 		tp = &mp_tty[mb->mb_unit*MPCHUNK+port];
723 		tp->t_state &= ~TS_CARR_ON;
724 		ttyflush(tp, FREAD|FWRITE);
725 		gsignal(tp->t_pgrp, SIGHUP);
726 		gsignal(tp->t_pgrp, SIGKILL);
727 		mpclose(tp->t_dev, 0);
728 	}
729 }
730 
731 mpclean(mb, port)
732 	register struct mblok *mb;
733 	int port;
734 {
735 	register struct mpport *mp;
736 	register struct mpevent *ev;
737 	register int i;
738 	char list[2], *cp;
739 	int unit;
740 
741 	mp = &mb->mb_port[port];
742 	unit = mb->mb_unit;
743 	for (i = mp->mp_off; i != mp->mp_on; i = (i+1 % MPINSET)) {
744 		ev = &mp->mp_recvq[i];
745 		ev->ev_error = ENXIO;
746 		ev->ev_status = EVSTATUS_DONE;
747 	}
748 	list[0] = port, list[1] = MPPORT_EOL;
749 	mpxintr(unit, list);
750 	mprintr(unit, list);
751 	/* Clear async for port */
752 	mp->mp_proto = MPPROTO_UNUSED;
753 	mp->mp_flags = 0;
754 	mp->mp_on = 0;
755 	mp->mp_off = 0;
756 	mp->mp_nextrcv = 0;
757 
758 	mp_tty[unit*MPCHUNK + port].t_state = 0;
759 	for (ev = &mp->mp_sendq[0]; ev < &mp->mp_sendq[MPOUTSET]; ev++) {
760 		ev->ev_status = EVSTATUS_FREE;
761 		ev->ev_cmd = 0;
762 		ev->ev_error = 0;
763 		ev->ev_un.rcvblk = 0;
764 		ev->ev_params = 0;
765 	}
766 	for (ev = &mp->mp_recvq[0]; ev < &mp->mp_recvq[MPINSET]; ev++) {
767 		ev->ev_status = EVSTATUS_FREE;
768 		ev->ev_cmd = 0;
769 		ev->ev_error = 0;
770 		ev->ev_params = 0;
771 	}
772 }
773 
774 /*
775  * MPCC interrupt handler.
776  */
777 mpintr(mpcc)
778 	int mpcc;
779 {
780 	register struct mblok *mb;
781 	register struct his *his;
782 	register int i;
783 
784 	mb = mp_softc[mpcc].ms_mb;
785 	if (mb == 0) {
786 		printf("mp%d: stray interrupt\n", mpcc);
787 		return;
788 	}
789 	his = &mb->mb_hostint;
790 	his->semaphore &= ~MPSEMA_AVAILABLE;
791 	/*
792 	 * Check for events to be processed.
793 	 */
794 	if (his->proto[MPPROTO_ASYNC].outbdone[0] != MPPORT_EOL)
795 		mprintr(mpcc, his->proto[MPPROTO_ASYNC].outbdone);
796 	if (his->proto[MPPROTO_ASYNC].inbdone[0] != MPPORT_EOL)
797 		mpxintr(mpcc, his->proto[MPPROTO_ASYNC].inbdone);
798 	if (mb->mb_harderr || mb->mb_softerr)
799 		mperror(mb, mpcc);
800 	his->semaphore |= MPSEMA_AVAILABLE;
801 }
802 
803 /*
804  * Handler for processing completion of transmitted events.
805  */
806 mpxintr(unit, list)
807 	register char *list;
808 {
809 	register struct mpport *mp;
810 	register struct mpevent *ev;
811 	register struct mblok *mb;
812 	register struct tty *tp;
813 	register struct asyncparam *ap;
814 	struct mpsoftc *ms;
815 	int port, i, j;
816 
817 	ms = &mp_softc[unit];
818 	mb = mp_softc[unit].ms_mb;
819 	for (j = 0; j < MPMAXPORT && ((port = *list++) != MPPORT_EOL); j++) {
820 		/*
821 		 * Process each completed entry in the inbound queue.
822 		 */
823 		mp = &mb->mb_port[port];
824 		tp = &mp_tty[unit*MPCHUNK + port];
825 #define	nextevent(mp)	&mp->mp_recvq[mp->mp_off]
826 		ev = nextevent(mp);
827 		for(; ev->ev_status & EVSTATUS_DONE; ev = nextevent(mp)) {
828 			/* YUCK */
829 			ap = &ms->ms_async[port][mp->mp_off];
830 			mppurge(ap, sizeof (*ap));
831 			switch (ev->ev_cmd) {
832 			case EVCMD_OPEN:
833 				/*
834 				 * Open completion, start all reads and
835 				 * assert modem status information.
836 				 */
837 				for (i = 0; i < MPOUTSET; i++)
838 					mp->mp_sendq[i].ev_status = EVSTATUS_GO;
839 				(*linesw[tp->t_line].l_modem)
840 				    (tp, ap->ap_modem.mc_dcd == ASSERT);
841 				break;
842 			case EVCMD_CLOSE:
843 				/*
844 				 * Close completion, flush all pending
845 				 * transmissions, free resources, and
846 				 * cleanup mpcc port state.
847 				 */
848 				for (i = 0; i < MPOUTSET; i++) {
849 					mp->mp_sendq[i].ev_status =
850 					    EVSTATUS_FREE;
851 					mp->mp_sendq[i].ev_un.rcvblk = 0;
852 					mp->mp_sendq[i].ev_params = 0;
853 				}
854 				tp->t_state &= ~TS_CARR_ON;
855 				mp->mp_on = mp->mp_off = mp->mp_nextrcv = 0;
856 				mp->mp_flags &= ~MP_PROGRESS;
857 				mp->mp_proto = MPPROTO_UNUSED;
858 				wakeup((caddr_t)&tp->t_canq);	/* ??? */
859 				goto done;
860 			case EVCMD_IOCTL:
861 				/*
862 				 * Nothing to do, just pitch.
863 				 */
864 				break;
865 			case EVCMD_WRITE:
866 				/*
867 				 * Transmission completed, update tty
868 				 * state and restart output.
869 				 */
870 				tp->t_state &= ~TS_BUSY;
871 				if (tp->t_state & TS_FLUSH) {
872 					tp->t_state &= ~TS_FLUSH;
873 					wakeup((caddr_t)&tp->t_state);
874 				} else {
875 					register int cc = 0, i;
876 					struct hxmtl *hxp;
877 
878 					hxp = &ms->ms_hxl[port];
879 					for(i = 0; i < ev->ev_count; i++)
880 						cc += hxp->size[i];
881 					ndflush(&tp->t_outq, cc);
882 				}
883 				switch (ev->ev_error) {
884 				case A_SIZERR:  /*# error in xmt data size */
885 					mplog(unit, port, A_XSIZE, 0);
886 					break;
887 				case A_NXBERR:  /*# no more xmt evt buffers */
888 					mplog(unit, port, A_NOXBUF, 0);
889 					break;
890 				}
891 				mpstart(tp);
892 				break;
893 			default:
894 				mplog(unit, port, A_INVCMD, ev->ev_cmd);
895 				break;
896 			}
897 			/* re-init all values in this entry */
898 			ev->ev_cmd = 0;
899 			ev->ev_opts = 0;
900 			ev->ev_error = 0;
901 			ev->ev_flags = 0;
902 			ev->ev_count = 0;
903 			/* show this entry is available for use */
904 			ev->ev_status = EVSTATUS_FREE;
905 			adjptr(mp->mp_off, MPINSET);
906 #undef	nextevent
907 		}
908 done:
909 		;
910 	}
911 }
912 
913 /*
914  * Handler for processing received events.
915  */
916 mprintr(unit, list)
917 	char *list;
918 {
919 	register struct tty *tp;
920 	register struct mpport *mp;
921 	register struct mpevent *ev;
922 	struct mblok *mb;
923 	register int cc;
924 	register char *cp;
925 	struct mpsoftc *ms;
926 	caddr_t ptr;
927 	char *rcverr;
928 	int port, i;
929 
930 	ms = &mp_softc[unit];
931 	mb = mp_softc[unit].ms_mb;
932 	for (i = 0; i < MPMAXPORT && (port = *list++) != MPPORT_EOL; i++) {
933 		tp = &mp_tty[unit*MPCHUNK + port];
934 		mp = &mb->mb_port[port];
935 		ev = &mp->mp_sendq[mp->mp_nextrcv];
936 		while (ev->ev_status & EVSTATUS_DONE) {
937 			if (ev->ev_cmd != EVCMD_READ &&
938 			    ev->ev_cmd != EVCMD_STATUS) {
939 				mplog(unit, port, "unexpected command",
940 				    ev->ev_cmd);
941 				goto next;
942 			}
943 			if (ev->ev_cmd == EVCMD_STATUS) {
944 				/*
945 				 * Status change, look for carrier changes.
946 				 */
947 				if (ev->ev_opts == DCDASRT ||
948 				    ev->ev_opts == DCDDROP)
949 					(*linesw[tp->t_line].l_modem)
950 					    (tp, ev->ev_opts == DCDASRT);
951 				else
952 					mplog(unit, port,
953 					    "unexpect status command",
954 					    ev->ev_opts);
955 				goto next;
956 			}
957 			/*
958 			 * Process received data.
959 			 */
960 			if ((tp->t_state & (TS_ISOPEN|TS_WOPEN)) == 0)
961 				goto next;
962 			cc = ev->ev_count;
963 			if (cc == 0)
964 				goto next;
965 			/* YUCK */
966 			cp = ms->ms_cbuf[port][mp->mp_nextrcv];
967 			mppurge(cp, CBSIZE);
968 			while (cc-- > 0) {
969 				/*
970 				 * A null character is inserted, potentially
971 				 * when a break or framing error occurs.  If
972 				 * we're not in raw mode, substitute the
973 				 * interrupt character.
974 				 */
975 				if (*cp == 0 &&
976 				    (ev->ev_error == BRKASRT ||
977 				     ev->ev_error == FRAMERR))
978 					if ((tp->t_flags&RAW) == 0)
979 						*cp = tp->t_intrc;
980 				(*linesw[tp->t_line].l_rint)(*cp++, tp);
981 			}
982 			/* setup for next read */
983 			ptr = (caddr_t)&mp_softc[unit].ms_cbuf[port][mp->mp_nextrcv][0];
984 			ev->ev_un.rcvblk = (u_char *)vtoph(0, ptr);
985 			ev->ev_params = (caddr_t) vtoph(0, ptr);
986 			switch(ev->ev_error) {
987 			case RCVDTA:    /* Normal (good) rcv data */
988 					/* do not report the following */
989 					/* they are "normal" errors */
990 			case FRAMERR:   /* frame error */
991 			case BRKASRT:   /* Break condition */
992 			case PARERR:    /* parity error */
993 				rcverr = (char *)0;
994 				break;
995 			case OVRNERR:   /* Overrun error */
996 				rcverr = "overrun error";
997 				break;
998 			case OVFERR:    /* Overflow error */
999 				rcverr = "overflow error";
1000 				break;
1001 			default:
1002 				rcverr = "undefined rcv error";
1003 			}
1004 			if (rcverr != (char *)0)
1005 				mplog(unit, port, rcverr, ev->ev_error);
1006 		next:
1007 			ev->ev_cmd = 0;
1008 			ev->ev_opts = 0;
1009 			ev->ev_error = 0;
1010 			ev->ev_flags = 0;
1011 			ev->ev_status = EVSTATUS_GO;	/* start next read */
1012 			adjptr(mp->mp_nextrcv, MPOUTSET);
1013 			ev = &mp->mp_sendq[mp->mp_nextrcv];
1014 		}
1015 	}
1016 }
1017 
1018 /*
1019  * Log an mpcc diagnostic.
1020  */
1021 mplog(unit, port, cp, flags)
1022 	char *cp;
1023 {
1024 
1025 	if (flags)
1026 		log(LOG_ERR, "mp%d: port%d, %s (%d)\n",
1027 		    unit, port, cp, flags);
1028 	else
1029 		log(LOG_ERR, "mp%d: port%d, %s\n", unit, port, cp);
1030 }
1031 
1032 int	MPHOSTINT = 1;
1033 
1034 mptimeint(mb)
1035 	register struct mblok *mb;
1036 {
1037 
1038         mb->mb_mpintcnt = 0;
1039         mb->mb_mpintclk = (caddr_t)0;
1040 	*(u_short *)mpinfo[mb->mb_unit]->ui_addr = 2;
1041 }
1042 
1043 /*
1044  * Interupt mpcc
1045  */
1046 mpintmpcc(mb, port)
1047 	register struct mblok *mb;
1048 	u_short port;
1049 {
1050 
1051         mb->mb_intr[port] |= MPSEMA_WORK;
1052         if (++mb->mb_mpintcnt == MPHOSTINT) {
1053                 mb->mb_mpintcnt = 0;
1054 		*(u_short *)mpinfo[mb->mb_unit]->ui_addr = 2;
1055                 if (mb->mb_mpintclk) {
1056                         untimeout(mptimeint, mb);
1057                         mb->mb_mpintclk = 0;
1058                 }
1059         } else {
1060                 if (mb->mb_mpintclk == 0) {
1061                         timeout(mptimeint, mb, 4);
1062                         mb->mb_mpintclk = (caddr_t)1;
1063                 }
1064         }
1065 }
1066 
1067 static char *mpherrmsg[] = {
1068 	"",
1069 	"Bus error",				/* MPBUSERR */
1070 	"Address error",			/* ADDRERR */
1071 	"Undefined ecc interrupt",		/* UNDECC */
1072 	"Undefined interrupt",			/* UNDINT */
1073 	"Power failure occurred",		/* PWRFL */
1074 	"Stray transmit done interrupt",	/* NOXENTRY */
1075 	"Two fast timers on one port",		/* TWOFTMRS */
1076 	"Interrupt queue full",			/* INTQFULL */
1077 	"Interrupt queue ack error",		/* INTQERR */
1078 	"Uncorrectable dma parity error",	/* CBPERR */
1079 	"32 port ACAP failed power up",		/* ACPDEAD */
1080 };
1081 #define	NHERRS	(sizeof (mpherrmsg) / sizeof (mpherrmsg[0]))
1082 
1083 mperror(mb, unit)
1084 	register struct mblok *mb;
1085 	int unit;
1086 {
1087 	register char *cp;
1088 	register int i;
1089 
1090 	if (mb->mb_softerr) {
1091 		switch (mb->mb_softerr) {
1092 		case DMAPERR:   /* dma parity error */
1093 			cp = "dma parity error";
1094 			break;
1095 		case ECCERR:
1096 			cp = "local memory ecc error";
1097 			break;
1098 		default:
1099 			cp = "unknown error";
1100 			break;
1101 		}
1102 		log(LOG_ERR, "mp%d: soft error, %s", unit, cp);
1103 		mb->mb_softerr = 0;
1104 	}
1105 	if (mb->mb_harderr) {
1106 		if (mb->mb_harderr < NHERRS)
1107 			cp = mpherrmsg[mb->mb_harderr];
1108 		else
1109 			cp = "unknown error";
1110 		log(LOG_ERR, "mp%d: hard error, %s", unit, cp);
1111 		if (mb->mb_status == MP_OPOPEN) {
1112 			for (i = 0; i < MPMAXPORT; i++) {
1113 				mpcleanport(mb, i);
1114 				mb->mb_proto[i] = MPPROTO_UNUSED;
1115 			}
1116 		}
1117 		mb->mb_harderr = 0;
1118 		mb->mb_status = 0;
1119 	}
1120 }
1121 
1122 mppurge(addr, cc)
1123 	register caddr_t addr;
1124 	register int cc;
1125 {
1126 
1127 	for (; cc >= 0; addr += NBPG, cc -= NBPG)
1128 		mtpr(P1DC, addr);
1129 }
1130 
1131 /*
1132  * MPCC Download Pseudo-device.
1133  */
1134 char	mpdlbuf[MPDLBUFSIZE];
1135 int	mpdlbusy;		/* interlock on download buffer */
1136 int	mpdlerr;
1137 
1138 mpdlopen(dev)
1139 	dev_t dev;
1140 {
1141 	int unit, mpu;
1142 	struct vba_device *vi;
1143 
1144 	unit = minor(dev);
1145 	mpu = MPUNIT(unit);
1146 	if (mpu >= NMP || (vi = mpinfo[mpu]) == 0 || vi->ui_alive == 0)
1147 		return (ENODEV);
1148 	return (0);
1149 }
1150 
1151 mpdlwrite(dev, uio)
1152 	dev_t dev;
1153 	struct uio *uio;
1154 {
1155 	register struct mpsoftc *ms = &mp_softc[MPUNIT(minor(dev))];
1156 	register struct mpdl *dl;
1157 	int error;
1158 
1159 	if (ms->ms_mb == 0 || ms->ms_mb->mb_status != MP_DLOPEN)
1160 		return (EFAULT);
1161 	dl = &ms->ms_mb->mb_dl;
1162 	dl->mpdl_count = uio->uio_iov->iov_len;
1163 	dl->mpdl_data = (caddr_t) vtoph((struct proc *)0, mpdlbuf);
1164 	if (error = uiomove(mpdlbuf, dl->mpdl_count, UIO_WRITE, uio))
1165 		return (error);
1166 	uio->uio_resid -= dl->mpdl_count;    /* set up return from write */
1167 	dl->mpdl_cmd = MPDLCMD_NORMAL;
1168 	error = mpdlwait(dl);
1169 	return (error);
1170 }
1171 
1172 mpdlclose(dev)
1173 	dev_t dev;
1174 {
1175 	register struct mblok *mb = mp_softc[MPUNIT(minor(dev))].ms_mb;
1176 	int ret = 0;
1177 
1178 	if (mb == 0 || mb->mb_status != MP_DLDONE) {
1179 		mpbogus.status = 0;
1180 		if (mpbogus.mb == mpbogus.mbloks[MPUNIT(minor(dev))])
1181 			mpdlbusy--;
1182 		return (EEXIST);
1183 	}
1184 	mb->mb_status = MP_OPOPEN;
1185 	mpbogus.status = 0;
1186 	/* set to dead, for board handshake */
1187 	mb->mb_hostint.imok = MPIMOK_DEAD;
1188 	return (0);
1189 }
1190 
1191 mpreset(dev)
1192 	dev_t dev;
1193 {
1194 	/* XXX */
1195 }
1196 
1197 int	mpdltimeout();
1198 
1199 mpdlioctl(dev, cmd, data, flag)
1200 	dev_t dev;
1201 	caddr_t data;
1202 {
1203 	register struct mblok *mb;
1204 	register struct mpdl *dl;
1205 	int unit, error, s, i, j;
1206 
1207 	mb = mp_softc[unit=MPUNIT(minor(dev))].ms_mb;
1208 	if (mb == 0)
1209 		return (EEXIST);
1210 	dl = &mb->mb_dl;
1211 	error = 0;
1212 	switch (cmd) {
1213 	case MPIOPORTMAP:
1214 		bcopy(data, (caddr_t)mb->mb_proto, sizeof (mb->mb_proto));
1215 		break;
1216 	case MPIOHILO:
1217 		bcopy(data, (caddr_t)&mb->mb_hiport, 2*(sizeof(mb->mb_hiport)));
1218 		break;
1219 	case MPIOENDDL:
1220 		dl->mpdl_count = 0;
1221 		dl->mpdl_data = 0;
1222 		dl->mpdl_cmd = MPIOENDDL&IOCPARM_MASK;
1223 		error = mpdlwait(dl);
1224 		mpccinit(unit);
1225 		mb->mb_status = MP_DLDONE;
1226 		mpdlbusy--;
1227 		break;
1228 	case MPIOENDCODE:
1229 		dl->mpdl_count = 0;
1230 		dl->mpdl_data = 0;
1231 		dl->mpdl_cmd = MPIOENDCODE&IOCPARM_MASK;
1232 		error = mpdlwait(dl);
1233 		break;
1234 	case MPIOASYNCNF:
1235 		bcopy(data, mpdlbuf, sizeof (struct abdcf));
1236 		dl->mpdl_data = (caddr_t) vtoph((struct proc *)0, mpdlbuf);
1237 		dl->mpdl_count = sizeof (struct abdcf);
1238 		dl->mpdl_cmd = MPIOASYNCNF&IOCPARM_MASK;
1239 		error = mpdlwait(dl);
1240 		break;
1241 	case MPIOSTARTDL:
1242 		while (mpdlbusy)
1243 			sleep((caddr_t)&mpdlbusy, PZERO+1);
1244 		mpdlbusy++;
1245 		/* initialize the downloading interface */
1246 		mpbogus.magic = MPMAGIC;
1247 		mpbogus.mb = mpbogus.mbloks[unit];
1248 		mpbogus.status = 1;
1249 		dl->mpdl_status = EVSTATUS_FREE;
1250 		dl->mpdl_count = 0;
1251 		dl->mpdl_cmd = 0;
1252 		dl->mpdl_data = (char *) 0;
1253 		mpdlerr = 0;
1254 		mb->mb_magic = MPMAGIC;
1255         	mb->mb_ivec = mp_softc[unit].ms_ivec+1;	/* download vector */
1256 		mb->mb_status = MP_DLPEND;
1257 		mb->mb_diagswitch[0] = 'A';
1258 		mb->mb_diagswitch[1] = 'P';
1259 		s = spl8();
1260 		*(u_short *)mpinfo[unit]->ui_addr = 2;
1261 		timeout(mpdltimeout, mb, 30*hz);	/* approx 15 seconds */
1262 		sleep((caddr_t)&mb->mb_status, PZERO+1);
1263 		splx(s);
1264 		if (mb->mb_status == MP_DLOPEN) {
1265 			untimeout(mpdltimeout, mb);
1266 		} else if (mb->mb_status == MP_DLTIME) {
1267 			mpbogus.status = 0;
1268 			error = ETIMEDOUT;
1269 		} else {
1270 			mpbogus.status = 0;
1271 			error = ENXIO;
1272 			log(LOG_ERR, "mp%d: start download: unknown status %x",
1273 			    unit, mb->mb_status);
1274 		}
1275 		bzero(mb->mb_port, sizeof (mb->mb_port));
1276 		break;
1277 	case MPIORESETBOARD:
1278 		s = spl8();
1279 		if (mb->mb_imokclk)
1280 			mb->mb_imokclk = 0;
1281 		*(u_short *)mpinfo[unit]->ui_addr = 0x100;
1282 		if (mb->mb_status == MP_DLOPEN || mb->mb_status == MP_DLDONE) {
1283 			mpdlerr = MP_DLERROR;
1284 			dl->mpdl_status = EVSTATUS_FREE;
1285 			wakeup((caddr_t)&dl->mpdl_status);
1286 			mpbogus.status = 0;
1287 		}
1288 		for (i = 0; i < MPMAXPORT; i++) {
1289 			if (mb->mb_harderr || mb->mb_softerr)
1290 				mperror(mb, i);
1291 			mpcleanport(mb, i);
1292 			mb->mb_proto[i] = MPPROTO_UNUSED;
1293 		}
1294 		mb->mb_status = 0;
1295 		splx(s);
1296 		break;
1297 	default:
1298 		error = EINVAL;
1299 		break;
1300 	}
1301 	return (error);
1302 }
1303 
1304 mpccinit(unit)
1305 	int unit;
1306 {
1307         register struct mblok *mb = mp_softc[unit].ms_mb;
1308         register struct his *his;
1309         register int i, j;
1310 
1311         mb->mb_status = MP_DLDONE;
1312         mb->mb_ivec = mp_softc[unit].ms_ivec;
1313         mb->mb_magic = MPMAGIC;
1314         /* Init host interface structure */
1315         his = &mb->mb_hostint;
1316         his->semaphore = MPSEMA_AVAILABLE;
1317         for (i = 0; i < NMPPROTO; i++)
1318                 for (j = 0; j < MPMAXPORT; j++) {
1319                         his->proto[i].inbdone[j] = MPPORT_EOL;
1320                         his->proto[i].outbdone[j] = MPPORT_EOL;
1321                 }
1322         mb->mb_unit = unit;
1323 }
1324 
1325 mpdlintr(mpcc)
1326 	int mpcc;
1327 {
1328 	register struct mblok *mb;
1329 	register struct mpdl *dl;
1330 
1331 	mb = mp_softc[mpcc].ms_mb;
1332 	if (mb == 0) {
1333 		printf("mp%d: stray download interrupt\n", mpcc);
1334 		return;
1335 	}
1336 	dl = &mb->mb_dl;
1337 	switch (mb->mb_status) {
1338 	case MP_DLOPEN:
1339 		if (dl->mpdl_status != EVSTATUS_DONE)
1340 			mpdlerr = MP_DLERROR;
1341 		dl->mpdl_status = EVSTATUS_FREE;
1342 		wakeup((caddr_t)&dl->mpdl_status);
1343 		return;
1344 	case MP_DLPEND:
1345 		mb->mb_status = MP_DLOPEN;
1346 		wakeup(&mb->mb_status);
1347 		/* fall thru... */
1348 	case MP_DLTIME:
1349 		return;
1350 	case MP_OPOPEN:
1351 		if (mb->mb_imokclk)
1352 			mb->mb_imokclk = 0;
1353 		mb->mb_nointcnt = 0;		/* reset no interrupt count */
1354 		mb->mb_hostint.imok = MPIMOK_DEAD;
1355 		mb->mb_imokclk = (caddr_t)1;
1356 		break;
1357 	default:
1358 		log(LOG_ERR, "mp%d: mpdlintr, status %x\n",
1359 		    mpcc, mb->mb_status);
1360 		break;
1361 	}
1362 }
1363 
1364 mpdltimeout(mp)
1365 	struct mblok *mp;
1366 {
1367 
1368 	mp->mb_status = MP_DLTIME;
1369 	wakeup((caddr_t)&mp->mb_status);
1370 }
1371 
1372 /*
1373  * Wait for a transfer to complete or a timeout to occur.
1374  */
1375 mpdlwait(dl)
1376 	register struct mpdl *dl;
1377 {
1378 	int s, error = 0;
1379 
1380 	s = spl8();
1381 	dl->mpdl_status = EVSTATUS_GO;
1382 	while (dl->mpdl_status != EVSTATUS_FREE) {
1383 		sleep((caddr_t)&dl->mpdl_status, PZERO+1);
1384 		if (mpdlerr == MP_DLERROR)
1385 			error = EIO;
1386 	}
1387 	splx(s);
1388 	return (error);
1389 }
1390 #endif
1391