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