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