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