1 /*
2 * Copyright (c) 1982, 1986 Regents of the University of California.
3 * All rights reserved. The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 *
6 * @(#)dz.c 7.14 (Berkeley) 05/16/91
7 */
8
9 #include "dz.h"
10 #if NDZ > 0
11 /*
12 * DZ-11/DZ-32 Driver
13 *
14 * This driver mimics dh.c; see it for explanation of common code.
15 */
16 #include "sys/param.h"
17 #include "sys/systm.h"
18 #include "sys/ioctl.h"
19 #include "sys/tty.h"
20 #include "sys/user.h"
21 #include "sys/proc.h"
22 #include "sys/map.h"
23 #include "sys/buf.h"
24 #include "sys/vm.h"
25 #include "sys/conf.h"
26 #include "sys/file.h"
27 #include "sys/uio.h"
28 #include "sys/kernel.h"
29 #include "sys/syslog.h"
30
31 #include "pdma.h"
32 #include "ubavar.h"
33 #include "dzreg.h"
34 #include "../include/pte.h"
35
36 /*
37 * Driver information for auto-configuration stuff.
38 */
39 int dzprobe(), dzattach(), dzrint();
40 struct uba_device *dzinfo[NDZ];
41 u_short dzstd[] = { 0 };
42 struct uba_driver dzdriver =
43 { dzprobe, 0, dzattach, 0, dzstd, "dz", dzinfo };
44
45 #define NDZLINE (NDZ*8)
46 #define FASTTIMER (hz/30) /* rate to drain silos, when in use */
47
48 int dzstart(), dzxint(), dzdma();
49 int ttrstrt();
50 struct tty dz_tty[NDZLINE];
51 int dz_cnt = { NDZLINE };
52 int dzact;
53 int dzsilos; /* mask of dz's with silo in use */
54 int dzchars[NDZ]; /* recent input count */
55 int dzrate[NDZ]; /* smoothed input count */
56 int dztimerintvl; /* time interval for dztimer */
57 int dzhighrate = 100; /* silo on if dzchars > dzhighrate */
58 int dzlowrate = 75; /* silo off if dzrate < dzlowrate */
59
60 #define dzwait(x) while (((x)->dzlcs & DZ_ACK) == 0)
61
62 /*
63 * Software copy of dzbrk since it isn't readable
64 */
65 char dz_brk[NDZ];
66 char dzsoftCAR[NDZ];
67 char dz_lnen[NDZ]; /* saved line enable bits for DZ32 */
68
69 /*
70 * The dz11 doesn't interrupt on carrier transitions, so
71 * we have to use a timer to watch it.
72 */
73 char dz_timer; /* timer started? */
74
75 /*
76 * Pdma structures for fast output code
77 */
78 struct pdma dzpdma[NDZLINE];
79
80 struct speedtab dzspeedtab[] = {
81 0, 0,
82 50, 020,
83 75, 021,
84 110, 022,
85 134, 023,
86 150, 024,
87 300, 025,
88 600, 026,
89 1200, 027,
90 1800, 030,
91 2400, 032,
92 4800, 034,
93 9600, 036,
94 19200, 037,
95 EXTA, 037,
96 -1, -1
97 };
98
99 #ifndef PORTSELECTOR
100 #define ISPEED TTYDEF_SPEED
101 #define LFLAG TTYDEF_LFLAG
102 #else
103 #define ISPEED B4800
104 #define LFLAG (TTYDEF_LFLAG&~ECHO)
105 #endif
106
dzprobe(reg)107 dzprobe(reg)
108 caddr_t reg;
109 {
110 register int br, cvec;
111 register struct dzdevice *dzaddr = (struct dzdevice *)reg;
112
113 #ifdef lint
114 br = 0; cvec = br; br = cvec;
115 dzrint(0); dzxint((struct tty *)0);
116 #endif
117 dzaddr->dzcsr = DZ_TIE|DZ_MSE|DZ_32;
118 if (dzaddr->dzcsr & DZ_32)
119 dzaddr->dzlnen = 1;
120 else
121 dzaddr->dztcr = 1; /* enable any line */
122 DELAY(100000);
123 dzaddr->dzcsr = DZ_CLR|DZ_32; /* reset everything */
124 if (cvec && cvec != 0x200)
125 cvec -= 4;
126 return (sizeof (struct dzdevice));
127 }
128
dzattach(ui)129 dzattach(ui)
130 register struct uba_device *ui;
131 {
132 register struct pdma *pdp = &dzpdma[ui->ui_unit*8];
133 register struct tty *tp = &dz_tty[ui->ui_unit*8];
134 register int cntr;
135 extern dzscan();
136
137 for (cntr = 0; cntr < 8; cntr++) {
138 pdp->p_addr = (struct dzdevice *)ui->ui_addr;
139 pdp->p_arg = (int)tp;
140 pdp->p_fcn = dzxint;
141 pdp++, tp++;
142 }
143 dzsoftCAR[ui->ui_unit] = ui->ui_flags;
144 if (dz_timer == 0) {
145 dz_timer++;
146 timeout(dzscan, (caddr_t)0, hz);
147 dztimerintvl = FASTTIMER;
148 }
149 }
150
151 /*ARGSUSED*/
dzopen(dev,flag)152 dzopen(dev, flag)
153 dev_t dev;
154 {
155 register struct tty *tp;
156 register int unit;
157 int error = 0, dzparam();
158
159 unit = minor(dev);
160 if (unit >= dz_cnt || dzpdma[unit].p_addr == 0)
161 return (ENXIO);
162 tp = &dz_tty[unit];
163 tp->t_addr = (caddr_t)&dzpdma[unit];
164 tp->t_oproc = dzstart;
165 tp->t_param = dzparam;
166 tp->t_dev = dev;
167 if ((tp->t_state & TS_ISOPEN) == 0) {
168 tp->t_state |= TS_WOPEN;
169 ttychars(tp);
170 #ifndef PORTSELECTOR
171 if (tp->t_ispeed == 0) {
172 #endif
173 tp->t_iflag = TTYDEF_IFLAG;
174 tp->t_oflag = TTYDEF_OFLAG;
175 tp->t_cflag = TTYDEF_CFLAG;
176 tp->t_lflag = LFLAG;
177 tp->t_ispeed = tp->t_ospeed = ISPEED;
178 #ifdef PORTSELECTOR
179 tp->t_cflag |= HUPCL;
180 #else
181 }
182 #endif
183 dzparam(tp, &tp->t_termios);
184 ttsetwater(tp);
185 } else if (tp->t_state&TS_XCLUDE && u.u_uid != 0)
186 return (EBUSY);
187 (void) dzmctl(dev, DZ_ON, DMSET);
188 (void) spl5();
189 while ((flag&O_NONBLOCK) == 0 && (tp->t_cflag&CLOCAL) == 0 &&
190 (tp->t_state & TS_CARR_ON) == 0) {
191 tp->t_state |= TS_WOPEN;
192 if (error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH,
193 ttopen, 0))
194 break;
195 }
196 (void) spl0();
197 if (error)
198 return (error);
199 return ((*linesw[tp->t_line].l_open)(dev, tp));
200 }
201
202 /*ARGSUSED*/
dzclose(dev,flag,mode,p)203 dzclose(dev, flag, mode, p)
204 dev_t dev;
205 int flag, mode;
206 struct proc *p;
207 {
208 register struct tty *tp;
209 register int unit;
210 register struct dzdevice *dzaddr;
211 int dz;
212
213 unit = minor(dev);
214 dz = unit >> 3;
215 tp = &dz_tty[unit];
216 (*linesw[tp->t_line].l_close)(tp, flag);
217 dzaddr = dzpdma[unit].p_addr;
218 if (dzaddr->dzcsr&DZ_32)
219 (void) dzmctl(dev, DZ_BRK, DMBIC);
220 else
221 dzaddr->dzbrk = (dz_brk[dz] &= ~(1 << (unit&07)));
222 if (tp->t_cflag&HUPCL || tp->t_state&TS_WOPEN ||
223 (tp->t_state&TS_ISOPEN) == 0)
224 (void) dzmctl(dev, DZ_OFF, DMSET);
225 return (ttyclose(tp));
226 }
227
dzread(dev,uio,flag)228 dzread(dev, uio, flag)
229 dev_t dev;
230 struct uio *uio;
231 {
232 register struct tty *tp;
233
234 tp = &dz_tty[minor(dev)];
235 return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
236 }
237
dzwrite(dev,uio,flag)238 dzwrite(dev, uio, flag)
239 dev_t dev;
240 struct uio *uio;
241 {
242 register struct tty *tp;
243
244 tp = &dz_tty[minor(dev)];
245 return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
246 }
247
248 /*ARGSUSED*/
dzrint(dz)249 dzrint(dz)
250 int dz;
251 {
252 register struct tty *tp;
253 register int c, cc;
254 register struct dzdevice *dzaddr;
255 register struct tty *tp0;
256 register int unit;
257 int overrun = 0;
258
259 if ((dzact & (1<<dz)) == 0)
260 return;
261 unit = dz * 8;
262 dzaddr = dzpdma[unit].p_addr;
263 tp0 = &dz_tty[unit];
264 dzaddr->dzcsr &= ~(DZ_RIE|DZ_MIE); /* the manual says this song */
265 dzaddr->dzcsr |= DZ_RIE|DZ_MIE; /* and dance is necessary */
266 while (dzaddr->dzcsr & DZ_MSC) { /* DZ32 modem change interrupt */
267 c = dzaddr->dzmtsr;
268 tp = tp0 + (c&7);
269 if (tp >= &dz_tty[dz_cnt])
270 break;
271 dzaddr->dzlcs = c&7; /* get status of modem lines */
272 dzwait(dzaddr); /* wait for them */
273 if (c & DZ_CD) /* carrier status change? */
274 if (dzaddr->dzlcs & DZ_CD) { /* carrier up? */
275 /* carrier present */
276 (void)(*linesw[tp->t_line].l_modem)(tp, 1);
277 } else if ((*linesw[tp->t_line].l_modem)(tp, 0) == 0)
278 dzaddr->dzlcs = DZ_ACK|(c&7);
279 }
280 while ((c = dzaddr->dzrbuf) < 0) { /* char present */
281 cc = c&0xff;
282 dzchars[dz]++;
283 tp = tp0 + ((c>>8)&07);
284 if (tp >= &dz_tty[dz_cnt])
285 continue;
286 if ((tp->t_state & TS_ISOPEN) == 0) {
287 wakeup((caddr_t)&tp->t_rawq);
288 #ifdef PORTSELECTOR
289 if ((tp->t_state&TS_WOPEN) == 0)
290 #endif
291 continue;
292 }
293 if (c&DZ_FE)
294 cc |= TTY_FE;
295 if (c&DZ_DO && overrun == 0) {
296 log(LOG_WARNING, "dz%d,%d: silo overflow\n", dz, (c>>8)&7);
297 overrun = 1;
298 }
299 if (c&DZ_PE)
300 cc |= TTY_PE;
301 (*linesw[tp->t_line].l_rint)(cc, tp);
302 }
303 }
304
305 /*ARGSUSED*/
dzioctl(dev,cmd,data,flag)306 dzioctl(dev, cmd, data, flag)
307 dev_t dev;
308 caddr_t data;
309 {
310 register struct tty *tp;
311 register int unit = minor(dev);
312 register int dz = unit >> 3;
313 register struct dzdevice *dzaddr;
314 int error;
315
316 tp = &dz_tty[unit];
317 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
318 if (error >= 0)
319 return (error);
320 error = ttioctl(tp, cmd, data, flag);
321 if (error >= 0)
322 return (error);
323
324 switch (cmd) {
325
326 case TIOCSBRK:
327 dzaddr = ((struct pdma *)(tp->t_addr))->p_addr;
328 if (dzaddr->dzcsr&DZ_32)
329 (void) dzmctl(dev, DZ_BRK, DMBIS);
330 else
331 dzaddr->dzbrk = (dz_brk[dz] |= 1 << (unit&07));
332 break;
333
334 case TIOCCBRK:
335 dzaddr = ((struct pdma *)(tp->t_addr))->p_addr;
336 if (dzaddr->dzcsr&DZ_32)
337 (void) dzmctl(dev, DZ_BRK, DMBIC);
338 else
339 dzaddr->dzbrk = (dz_brk[dz] &= ~(1 << (unit&07)));
340 break;
341
342 case TIOCSDTR:
343 (void) dzmctl(dev, DZ_DTR|DZ_RTS, DMBIS);
344 break;
345
346 case TIOCCDTR:
347 (void) dzmctl(dev, DZ_DTR|DZ_RTS, DMBIC);
348 break;
349
350 case TIOCMSET:
351 (void) dzmctl(dev, dmtodz(*(int *)data), DMSET);
352 break;
353
354 case TIOCMBIS:
355 (void) dzmctl(dev, dmtodz(*(int *)data), DMBIS);
356 break;
357
358 case TIOCMBIC:
359 (void) dzmctl(dev, dmtodz(*(int *)data), DMBIC);
360 break;
361
362 case TIOCMGET:
363 *(int *)data = dztodm(dzmctl(dev, 0, DMGET));
364 break;
365
366 default:
367 return (ENOTTY);
368 }
369 return (0);
370 }
371
dmtodz(bits)372 dmtodz(bits)
373 register int bits;
374 {
375 register int b;
376
377 b = (bits >>1) & 0370;
378 if (bits & DML_ST) b |= DZ_ST;
379 if (bits & DML_RTS) b |= DZ_RTS;
380 if (bits & DML_DTR) b |= DZ_DTR;
381 if (bits & DML_LE) b |= DZ_LE;
382 return(b);
383 }
384
dztodm(bits)385 dztodm(bits)
386 register int bits;
387 {
388 register int b;
389
390 b = (bits << 1) & 0360;
391 if (bits & DZ_DSR) b |= DML_DSR;
392 if (bits & DZ_DTR) b |= DML_DTR;
393 if (bits & DZ_ST) b |= DML_ST;
394 if (bits & DZ_RTS) b |= DML_RTS;
395 return(b);
396 }
397
dzparam(tp,t)398 dzparam(tp, t)
399 register struct tty *tp;
400 register struct termios *t;
401 {
402 register struct dzdevice *dzaddr;
403 register int lpr;
404 register int cflag = t->c_cflag;
405 int unit = minor(tp->t_dev);
406 int ospeed = ttspeedtab(t->c_ospeed, dzspeedtab);
407
408 /* check requested parameters */
409 if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed) ||
410 (cflag&CSIZE) == CS5 || (cflag&CSIZE) == CS6)
411 return(EINVAL);
412 /* and copy to tty */
413 tp->t_ispeed = t->c_ispeed;
414 tp->t_ospeed = t->c_ospeed;
415 tp->t_cflag = cflag;
416
417 dzaddr = dzpdma[unit].p_addr;
418 if (dzsilos & (1 << (unit >> 3)))
419 dzaddr->dzcsr = DZ_IEN | DZ_SAE;
420 else
421 dzaddr->dzcsr = DZ_IEN;
422 dzact |= (1<<(unit>>3));
423 if (ospeed == 0)
424 (void) dzmctl(unit, DZ_OFF, DMSET); /* hang up line */
425 else {
426 lpr = (ospeed<<8) | (unit & 07);
427 if ((cflag&CSIZE) == CS7)
428 lpr |= BITS7;
429 else
430 lpr |= BITS8;
431 if (cflag&PARENB)
432 lpr |= PENABLE;
433 if (cflag&PARODD)
434 lpr |= OPAR;
435 if (cflag&CSTOPB)
436 lpr |= TWOSB;
437 dzaddr->dzlpr = lpr;
438 }
439 return(0);
440 }
441
dzxint(tp)442 dzxint(tp)
443 register struct tty *tp;
444 {
445 register struct pdma *dp;
446 register dz, unit;
447
448 dp = (struct pdma *)tp->t_addr;
449 tp->t_state &= ~TS_BUSY;
450 if (tp->t_state & TS_FLUSH)
451 tp->t_state &= ~TS_FLUSH;
452 else {
453 ndflush(&tp->t_outq, dp->p_mem-tp->t_outq.c_cf);
454 dp->p_end = dp->p_mem = tp->t_outq.c_cf;
455 }
456 if (tp->t_line)
457 (*linesw[tp->t_line].l_start)(tp);
458 else
459 dzstart(tp);
460 dz = minor(tp->t_dev) >> 3;
461 unit = minor(tp->t_dev) & 7;
462 if (tp->t_outq.c_cc == 0 || (tp->t_state&TS_BUSY)==0)
463 if (dp->p_addr->dzcsr & DZ_32)
464 dp->p_addr->dzlnen = (dz_lnen[dz] &= ~(1<<unit));
465 else
466 dp->p_addr->dztcr &= ~(1<<unit);
467 }
468
dzstart(tp)469 dzstart(tp)
470 register struct tty *tp;
471 {
472 register struct pdma *dp;
473 register struct dzdevice *dzaddr;
474 register int cc;
475 int s, dz, unit;
476
477 dp = (struct pdma *)tp->t_addr;
478 dzaddr = dp->p_addr;
479 s = spl5();
480 if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))
481 goto out;
482 if (tp->t_outq.c_cc <= tp->t_lowat) {
483 if (tp->t_state&TS_ASLEEP) {
484 tp->t_state &= ~TS_ASLEEP;
485 wakeup((caddr_t)&tp->t_outq);
486 }
487 if (tp->t_wsel) {
488 selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
489 tp->t_wsel = 0;
490 tp->t_state &= ~TS_WCOLL;
491 }
492 }
493 if (tp->t_outq.c_cc == 0)
494 goto out;
495 if (tp->t_flags & (RAW|LITOUT))
496 cc = ndqb(&tp->t_outq, 0);
497 else {
498 cc = ndqb(&tp->t_outq, 0200);
499 if (cc == 0) {
500 cc = getc(&tp->t_outq);
501 timeout(ttrstrt, (caddr_t)tp, (cc&0x7f) + 6);
502 tp->t_state |= TS_TIMEOUT;
503 goto out;
504 }
505 }
506 tp->t_state |= TS_BUSY;
507 dp->p_end = dp->p_mem = tp->t_outq.c_cf;
508 dp->p_end += cc;
509 dz = minor(tp->t_dev) >> 3;
510 unit = minor(tp->t_dev) & 7;
511 if (dzaddr->dzcsr & DZ_32)
512 dzaddr->dzlnen = (dz_lnen[dz] |= (1<<unit));
513 else
514 dzaddr->dztcr |= (1<<unit);
515 out:
516 splx(s);
517 }
518
519 /*
520 * Stop output on a line.
521 */
522 /*ARGSUSED*/
dzstop(tp,flag)523 dzstop(tp, flag)
524 register struct tty *tp;
525 {
526 register struct pdma *dp;
527 register int s;
528
529 dp = (struct pdma *)tp->t_addr;
530 s = spl5();
531 if (tp->t_state & TS_BUSY) {
532 dp->p_end = dp->p_mem;
533 if ((tp->t_state&TS_TTSTOP)==0)
534 tp->t_state |= TS_FLUSH;
535 }
536 splx(s);
537 }
538
dzmctl(dev,bits,how)539 dzmctl(dev, bits, how)
540 dev_t dev;
541 int bits, how;
542 {
543 register struct dzdevice *dzaddr;
544 register int unit, mbits;
545 int b, s;
546
547 unit = minor(dev);
548 b = 1<<(unit&7);
549 dzaddr = dzpdma[unit].p_addr;
550 s = spl5();
551 if (dzaddr->dzcsr & DZ_32) {
552 dzwait(dzaddr)
553 DELAY(100); /* IS 100 TOO MUCH? */
554 dzaddr->dzlcs = unit&7;
555 DELAY(100);
556 dzwait(dzaddr)
557 DELAY(100);
558 mbits = dzaddr->dzlcs;
559 mbits &= 0177770;
560 } else {
561 mbits = (dzaddr->dzdtr & b) ? DZ_DTR : 0;
562 mbits |= (dzaddr->dzmsr & b) ? DZ_CD : 0;
563 mbits |= (dzaddr->dztbuf & b) ? DZ_RI : 0;
564 }
565 switch (how) {
566 case DMSET:
567 mbits = bits;
568 break;
569
570 case DMBIS:
571 mbits |= bits;
572 break;
573
574 case DMBIC:
575 mbits &= ~bits;
576 break;
577
578 case DMGET:
579 (void) splx(s);
580 return(mbits);
581 }
582 if (dzaddr->dzcsr & DZ_32) {
583 mbits |= DZ_ACK|(unit&7);
584 dzaddr->dzlcs = mbits;
585 } else {
586 if (mbits & DZ_DTR)
587 dzaddr->dzdtr |= b;
588 else
589 dzaddr->dzdtr &= ~b;
590 }
591 if (mbits & DZ_DTR && dzsoftCAR[unit >> 3] & b)
592 dz_tty[unit].t_state |= TS_CARR_ON;
593 (void) splx(s);
594 return(mbits);
595 }
596
597 int dztransitions, dzfasttimers; /*DEBUG*/
dzscan()598 dzscan()
599 {
600 register i;
601 register struct dzdevice *dzaddr;
602 register bit;
603 register struct tty *tp;
604 register car;
605 int olddzsilos = dzsilos;
606 int dztimer();
607
608 for (i = 0; i < dz_cnt ; i++) {
609 dzaddr = dzpdma[i].p_addr;
610 if (dzaddr == 0)
611 continue;
612 tp = &dz_tty[i];
613 bit = 1<<(i&07);
614 car = 0;
615 if (dzsoftCAR[i>>3]&bit)
616 car = 1;
617 else if (dzaddr->dzcsr & DZ_32) {
618 dzaddr->dzlcs = i&07;
619 dzwait(dzaddr);
620 car = dzaddr->dzlcs & DZ_CD;
621 } else
622 car = dzaddr->dzmsr&bit;
623 if (car) {
624 /* carrier present */
625 if ((tp->t_state & TS_CARR_ON) == 0)
626 (void)(*linesw[tp->t_line].l_modem)(tp, 1);
627 } else if ((tp->t_state&TS_CARR_ON) &&
628 (*linesw[tp->t_line].l_modem)(tp, 0) == 0)
629 dzaddr->dzdtr &= ~bit;
630 }
631 for (i = 0; i < NDZ; i++) {
632 ave(dzrate[i], dzchars[i], 8);
633 if (dzchars[i] > dzhighrate && ((dzsilos & (1 << i)) == 0)) {
634 dzpdma[i << 3].p_addr->dzcsr = DZ_IEN | DZ_SAE;
635 dzsilos |= (1 << i);
636 dztransitions++; /*DEBUG*/
637 } else if ((dzsilos & (1 << i)) && (dzrate[i] < dzlowrate)) {
638 dzpdma[i << 3].p_addr->dzcsr = DZ_IEN;
639 dzsilos &= ~(1 << i);
640 }
641 dzchars[i] = 0;
642 }
643 if (dzsilos && !olddzsilos)
644 timeout(dztimer, (caddr_t)0, dztimerintvl);
645 timeout(dzscan, (caddr_t)0, hz);
646 }
647
dztimer()648 dztimer()
649 {
650 register int dz;
651 register int s;
652
653 if (dzsilos == 0)
654 return;
655 s = spl5();
656 dzfasttimers++; /*DEBUG*/
657 for (dz = 0; dz < NDZ; dz++)
658 if (dzsilos & (1 << dz))
659 dzrint(dz);
660 splx(s);
661 timeout(dztimer, (caddr_t) 0, dztimerintvl);
662 }
663
664 /*
665 * Reset state of driver if UBA reset was necessary.
666 * Reset parameters and restart transmission on open lines.
667 */
dzreset(uban)668 dzreset(uban)
669 int uban;
670 {
671 register int unit;
672 register struct tty *tp;
673 register struct uba_device *ui;
674
675 for (unit = 0; unit < NDZLINE; unit++) {
676 ui = dzinfo[unit >> 3];
677 if (ui == 0 || ui->ui_ubanum != uban || ui->ui_alive == 0)
678 continue;
679 if (unit%8 == 0)
680 printf(" dz%d", unit>>3);
681 tp = &dz_tty[unit];
682 if (tp->t_state & (TS_ISOPEN|TS_WOPEN)) {
683 dzparam(unit);
684 (void) dzmctl(unit, DZ_ON, DMSET);
685 tp->t_state &= ~TS_BUSY;
686 dzstart(tp);
687 }
688 }
689 }
690 #endif
691