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