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