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 * %sccs.include.redist.c%
9 *
10 * @(#)vx.c 7.13 (Berkeley) 05/16/91
11 */
12
13 #include "vx.h"
14 #if NVX > 0
15 /*
16 * VIOC-X driver
17 */
18 #ifdef VXPERF
19 #define DOSCOPE
20 #endif
21
22 #include "sys/param.h"
23 #include "sys/ioctl.h"
24 #include "sys/tty.h"
25 #include "sys/user.h"
26 #include "sys/map.h"
27 #include "sys/buf.h"
28 #include "sys/conf.h"
29 #include "sys/file.h"
30 #include "sys/proc.h"
31 #include "sys/vm.h"
32 #include "sys/kernel.h"
33 #include "sys/syslog.h"
34
35 #include "../include/pte.h"
36
37 #include "../vba/vbavar.h"
38 #include "../vba/vbaparam.h"
39 #include "../vba/vxreg.h"
40 #include "../vba/scope.h"
41
42 #ifdef VX_DEBUG
43 long vxintr4 = 0;
44 #define VXERR4 1
45 #define VXNOBUF 2
46 long vxdebug = 0;
47 #define VXVCM 1
48 #define VXVCC 2
49 #define VXVCX 4
50 #endif
51
52 /*
53 * Interrupt type bits passed to vinthandl().
54 */
55 #define CMDquals 0 /* command completed interrupt */
56 #define RSPquals 1 /* command response interrupt */
57 #define UNSquals 2 /* unsolicited interrupt */
58
59 #define VXUNIT(n) ((n) >> 4)
60 #define VXPORT(n) ((n) & 0xf)
61
62 struct tty vx_tty[NVX*16];
63 #ifndef lint
64 int nvx = NVX*16;
65 #endif
66 int vxstart(), ttrstrt();
67 struct vxcmd *vobtain(), *nextcmd();
68
69 /*
70 * Driver information for auto-configuration stuff.
71 */
72 int vxprobe(), vxattach(), vxrint();
73 struct vba_device *vxinfo[NVX];
74 long vxstd[] = { 0 };
75 struct vba_driver vxdriver =
76 { vxprobe, 0, vxattach, 0, vxstd, "vx", vxinfo };
77
78 struct vx_softc {
79 struct vxdevice *vs_addr; /* H/W address */
80 u_char vs_type; /* 0: viox-x/vioc-b, 1: vioc-bop */
81 u_char vs_bop; /* bop board # for vioc-bop's */
82 u_char vs_loport; /* low port nbr */
83 u_char vs_hiport; /* high port nbr */
84 u_short vs_nbr; /* viocx number */
85 u_short vs_maxcmd; /* max number of concurrent cmds */
86 u_short vs_silosiz; /* silo size */
87 short vs_vers; /* vioc/pvioc version */
88 #define VXV_OLD 0 /* PVIOCX | VIOCX */
89 #define VXV_NEW 1 /* NPVIOCX | NVIOCX */
90 short vs_state; /* controller state */
91 #define VXS_READY 0 /* ready for commands */
92 #define VXS_RESET 1 /* in process of reseting */
93 u_short vs_softCAR; /* soft carrier */
94 u_int vs_ivec; /* interrupt vector base */
95 caddr_t vs_mricmd; /* most recent issued cmd */
96 /* The remaining fields are zeroed on reset... */
97 #define vs_zero vs_xmtcnt
98 int vs_xmtcnt; /* xmit commands pending */
99 struct vxcmd *vs_avail;/* next available command buffer */
100 struct vxcmd *vs_build;
101 struct vxcmd vs_lst[NVCXBUFS];
102 struct vcmds vs_cmds;
103 } vx_softc[NVX];
104
105 struct speedtab vxspeedtab[] = {
106 EXTA, V19200,
107 EXTB, V19200,
108 19200, V19200,
109 9600, 13,
110 4800, 12,
111 2400, 11,
112 1800, 10,
113 1200, 9,
114 600, 8,
115 300, 7,
116 200, 6,
117 150, 5,
118 134, 4,
119 110, 3,
120 75, 2,
121 50, 1,
122 0, 0,
123 -1, -1,
124 };
125
vxprobe(reg,vi)126 vxprobe(reg, vi)
127 caddr_t reg;
128 struct vba_device *vi;
129 {
130 register int br, cvec; /* must be r12, r11 */
131 register struct vxdevice *vp;
132 register struct vx_softc *vs;
133 struct pte *dummypte;
134
135 #ifdef lint
136 br = 0; cvec = br; br = cvec;
137 vackint(0); vunsol(0); vcmdrsp(0);
138 #ifdef VX_DEBUG
139 vxfreset(0);
140 #endif
141 #endif /* lint */
142 /*
143 * If on an HCX-9, the device has a 32-bit address,
144 * and we receive that address so we can set up a map.
145 * On VERSAbus devices, the address is 24-bit, and is
146 * already mapped (into vmem[]) by autoconf.
147 */
148 if (!(reg >= vmem && reg < &vmem[ctob(VBIOSIZE)]) && /* XXX */
149 !vbmemalloc(16, reg, &dummypte, ®)) {
150 printf("vx%d: vbmemalloc failed.\n", vi->ui_unit);
151 return(0);
152 }
153 vp = (struct vxdevice *)reg;
154 if (badaddr((caddr_t)vp, 1))
155 return (0);
156 vp->v_fault = 0;
157 vp->v_vioc = V_BSY;
158 vp->v_hdwre = V_RESET; /* reset interrupt */
159 DELAY(4000000);
160 if (vp->v_fault != VXF_READY)
161 return (0);
162 vs = &vx_softc[vi->ui_unit];
163 #ifdef notdef
164 /*
165 * Align vioc interrupt vector base to 4 vector
166 * boundary and fitting in 8 bits (is this necessary,
167 * wish we had documentation).
168 */
169 if ((vi->ui_hd->vh_lastiv -= 3) > 0xff)
170 vi->ui_hd->vh_lastiv = 0xff;
171 vs->vs_ivec = vi->ui_hd->vh_lastiv = vi->ui_hd->vh_lastiv &~ 0x3;
172 #else
173 vs->vs_ivec = 0x40+vi->ui_unit*4;
174 #endif
175 br = 0x18, cvec = vs->vs_ivec; /* XXX */
176 return (sizeof (struct vxdevice));
177 }
178
vxattach(vi)179 vxattach(vi)
180 register struct vba_device *vi;
181 {
182 register struct vx_softc *vs = &vx_softc[vi->ui_unit];
183
184 vs->vs_softCAR = vi->ui_flags;
185 vs->vs_addr = (struct vxdevice *)vi->ui_addr;
186 vxinit(vi->ui_unit, 1);
187 }
188
189 /*
190 * Open a VX line.
191 */
192 /*ARGSUSED*/
vxopen(dev,flag)193 vxopen(dev, flag)
194 dev_t dev;
195 int flag;
196 {
197 register struct tty *tp; /* pointer to tty struct for port */
198 register struct vx_softc *vs;
199 register struct vba_device *vi;
200 int unit, vx, s, error = 0;
201 int vxparam();
202
203 unit = minor(dev);
204 vx = VXUNIT(unit);
205 if (vx >= NVX || (vi = vxinfo[vx])== 0 || vi->ui_alive == 0)
206 return (ENXIO);
207 vs = &vx_softc[vx];
208 tp = &vx_tty[unit];
209 unit = VXPORT(unit);
210 if (tp->t_state&TS_XCLUDE && u.u_uid != 0)
211 return (EBUSY);
212 if (unit < vs->vs_loport || unit > vs->vs_hiport)
213 return (ENXIO);
214 tp->t_addr = (caddr_t)vs;
215 tp->t_oproc = vxstart;
216 tp->t_param = vxparam;
217 tp->t_dev = dev;
218 s = spl8();
219 if ((tp->t_state&TS_ISOPEN) == 0) {
220 tp->t_state |= TS_WOPEN;
221 ttychars(tp);
222 if (tp->t_ispeed == 0) {
223 tp->t_iflag = TTYDEF_IFLAG;
224 tp->t_oflag = TTYDEF_OFLAG;
225 tp->t_lflag = TTYDEF_LFLAG;
226 tp->t_cflag = TTYDEF_CFLAG;
227 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
228 }
229 vxparam(tp, &tp->t_termios);
230 ttsetwater(tp);
231 }
232 vcmodem(dev, VMOD_ON);
233 while (!(flag&O_NONBLOCK) && !(tp->t_cflag&CLOCAL) &&
234 (tp->t_state&TS_CARR_ON) == 0) {
235 tp->t_state |= TS_WOPEN;
236 if (error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH,
237 ttopen, 0))
238 break;
239 }
240 if (error == 0)
241 error = (*linesw[tp->t_line].l_open)(dev,tp);
242 splx(s);
243 return (error);
244 }
245
246 /*
247 * Close a VX line.
248 */
249 /*ARGSUSED*/
vxclose(dev,flag,mode,p)250 vxclose(dev, flag, mode, p)
251 dev_t dev;
252 int flag, mode;
253 struct proc *p;
254 {
255 register struct tty *tp;
256 int unit, s, error = 0;
257
258 unit = minor(dev);
259 tp = &vx_tty[unit];
260 s = spl8();
261 (*linesw[tp->t_line].l_close)(tp, flag);
262 if (tp->t_cflag & HUPCL || (tp->t_state & TS_ISOPEN) == 0)
263 vcmodem(dev, VMOD_OFF);
264 /* wait for the last response */
265 while (tp->t_state&TS_FLUSH && error == 0)
266 error = tsleep((caddr_t)&tp->t_state, TTOPRI | PCATCH,
267 ttclos, 0);
268 splx(s);
269 if (error)
270 return (error);
271 return (ttyclose(tp));
272 }
273
274 /*
275 * Read from a VX line.
276 */
vxread(dev,uio,flag)277 vxread(dev, uio, flag)
278 dev_t dev;
279 struct uio *uio;
280 {
281 struct tty *tp = &vx_tty[minor(dev)];
282
283 return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
284 }
285
286 /*
287 * write on a VX line
288 */
vxwrite(dev,uio,flag)289 vxwrite(dev, uio, flag)
290 dev_t dev;
291 struct uio *uio;
292 {
293 register struct tty *tp = &vx_tty[minor(dev)];
294
295 return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
296 }
297
298 /*
299 * VIOCX unsolicited interrupt.
300 */
vxrint(vx)301 vxrint(vx)
302 register vx;
303 {
304 register struct tty *tp, *tp0;
305 register struct vxdevice *addr;
306 register struct vx_softc *vs;
307 struct vba_device *vi;
308 register int nc, c;
309 register struct silo {
310 u_char data, port;
311 } *sp;
312 short *osp;
313 int overrun = 0;
314
315 vi = vxinfo[vx];
316 if (vi == 0 || vi->ui_alive == 0)
317 return;
318 addr = (struct vxdevice *)vi->ui_addr;
319 switch (addr->v_uqual&037) {
320 case 0:
321 break;
322 case 2:
323 if (addr->v_ustat == VP_SILO_OFLOW)
324 log(LOG_ERR, "vx%d: input silo overflow\n", vx);
325 else {
326 printf("vx%d: vc proc err, ustat %x\n",
327 vx, addr->v_ustat);
328 vxstreset(vx);
329 }
330 return;
331 case 3:
332 vcmintr(vx);
333 return;
334 case 4:
335 return;
336 default:
337 printf("vx%d: vc uqual err, uqual %x\n", vx, addr->v_uqual);
338 vxstreset(vx);
339 return;
340 }
341 vs = &vx_softc[vx];
342 if (vs->vs_vers == VXV_NEW)
343 sp = (struct silo *)((caddr_t)addr + *(short *)addr->v_usdata);
344 else
345 sp = (struct silo *)((caddr_t)addr+VX_SILO+(addr->v_usdata[0]<<6));
346 nc = *(osp = (short *)sp);
347 if (nc == 0)
348 return;
349 if (vs->vs_vers == VXV_NEW && nc > vs->vs_silosiz) {
350 printf("vx%d: %d exceeds silo size\n", nc);
351 nc = vs->vs_silosiz;
352 }
353 tp0 = &vx_tty[vx*16];
354 sp = (struct silo *)(((short *)sp)+1);
355 for (; nc > 0; nc--, sp = (struct silo *)(((short *)sp)+1)) {
356 c = sp->port & 017;
357 if (vs->vs_loport > c || c > vs->vs_hiport)
358 continue;
359 tp = tp0 + c;
360 if( (tp->t_state&TS_ISOPEN) == 0) {
361 wakeup((caddr_t)&tp->t_rawq);
362 continue;
363 }
364 c = sp->data&((tp->t_cflag&CSIZE)==CS8 ? 0xff : 0x7f);
365 if ((sp->port&VX_RO) == VX_RO && !overrun) {
366 log(LOG_ERR, "vx%d: receiver overrun\n", vi->ui_unit);
367 overrun = 1;
368 continue;
369 }
370 if (sp->port&VX_PE)
371 c |= TTY_PE;
372 if (sp->port&VX_FE)
373 c |= TTY_FE;
374 (*linesw[tp->t_line].l_rint)(c, tp);
375 }
376 *osp = 0;
377 }
378
379 /*
380 * Ioctl for VX.
381 */
vxioctl(dev,cmd,data,flag)382 vxioctl(dev, cmd, data, flag)
383 dev_t dev;
384 caddr_t data;
385 {
386 register struct tty *tp;
387 int error;
388
389 tp = &vx_tty[minor(dev)];
390 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
391 if (error >= 0)
392 return (error);
393 error = ttioctl(tp, cmd, data, flag);
394 if (error >= 0)
395 return (error);
396 return (ENOTTY);
397 }
398
399 vxparam(tp, t)
400 struct tty *tp;
401 struct termios *t;
402 {
403
404 return (vxcparam(tp, t, 1));
405 }
406
407 /*
408 * Set parameters from open or stty into the VX hardware
409 * registers.
410 */
411 vxcparam(tp, t, wait)
412 struct tty *tp;
413 struct termios *t;
414 int wait;
415 {
416 register struct vx_softc *vs;
417 register struct vxcmd *cp;
418 int s, error = 0;
419 int speedcode = ttspeedtab(t->c_ospeed, vxspeedtab);
420
421 if (speedcode < 0 || (t->c_ispeed != t->c_ospeed && t->c_ispeed))
422 return (EINVAL);
423 vs = (struct vx_softc *)tp->t_addr;
424 cp = vobtain(vs);
425 s = spl8();
426 /*
427 * Construct ``load parameters'' command block
428 * to setup baud rates, xon-xoff chars, parity,
429 * and stop bits for the specified port.
430 */
431 cp->cmd = VXC_LPARAX;
432 cp->par[1] = VXPORT(minor(tp->t_dev));
433 /*
434 * note: if the hardware does flow control, ^V doesn't work
435 * to escape ^S
436 */
437 if (t->c_iflag&IXON) {
438 if (t->c_cc[VSTART] == _POSIX_VDISABLE)
439 cp->par[2] = 0;
440 else
441 cp->par[2] = t->c_cc[VSTART];
442 if (t->c_cc[VSTOP] == _POSIX_VDISABLE)
443 cp->par[3] = 0;
444 else
445 cp->par[3] = t->c_cc[VSTOP];
446 } else
447 cp->par[2] = cp->par[3] = 0;
448 #ifdef notnow
449 switch (t->c_cflag & CSIZE) { /* XXX */
450 case CS8:
451 #endif
452 cp->par[4] = BITS8; /* 8 bits of data */
453 #ifdef notnow
454 break;
455 case CS7:
456 cp->par[4] = BITS7; /* 7 bits of data */
457 break;
458 case CS6:
459 cp->par[4] = BITS6; /* 6 bits of data */
460 break;
461 case CS5:
462 cp->par[4] = BITS5; /* 5 bits of data */
463 break;
464 }
465 if ((t->c_cflag & PARENB) == 0) /* XXX */
466 #endif
467 cp->par[7] = VNOPARITY; /* no parity */
468 #ifdef notnow
469 else if (t->c_cflag&PARODD)
470 cp->par[7] = VODDP; /* odd parity */
471 else
472 cp->par[7] = VEVENP; /* even parity */
473 #endif
474 cp->par[5] = (t->c_cflag&CSTOPB) ? VSTOP2 : VSTOP1;
475 cp->par[6] = speedcode;
476 if (vcmd((int)vs->vs_nbr, (caddr_t)&cp->cmd) && wait)
477 error = tsleep((caddr_t)cp, TTIPRI | PCATCH, ttyout, 0);
478 if ((t->c_ospeed)==0) {
479 tp->t_cflag |= HUPCL;
480 vcmodem(tp->t_dev, VMOD_OFF);
481 }
482 splx(s);
483 return (error);
484 }
485
486 /*
487 * VIOCX command response interrupt.
488 * For transmission, restart output to any active port.
489 * For all other commands, just clean up.
490 */
vxxint(vx,cp)491 vxxint(vx, cp)
492 register int vx;
493 register struct vxcmd *cp;
494 {
495 register struct vxmit *vp;
496 register struct tty *tp, *tp0;
497 register struct vx_softc *vs;
498
499 vs = &vx_softc[vx];
500 cp = (struct vxcmd *)((long *)cp-1);
501
502 switch (cp->cmd&0xff00) {
503
504 case VXC_LIDENT: /* initialization complete */
505 if (vs->vs_state == VXS_RESET) {
506 vxfnreset(vx, cp);
507 vinthandl(vx, ((V_BSY|RSPquals) << 8)|V_INTR);
508 }
509 cp->cmd++;
510 return;
511
512 case VXC_XMITDTA:
513 case VXC_XMITIMM:
514 break;
515
516 case VXC_LPARAX:
517 wakeup((caddr_t)cp);
518 /* fall thru... */
519 default: /* VXC_MDMCTL or VXC_FDTATOX */
520 vrelease(vs, cp);
521 if (vs->vs_state == VXS_RESET)
522 vinthandl(vx, ((V_BSY|RSPquals) << 8)|V_INTR);
523 return;
524 }
525 tp0 = &vx_tty[vx*16];
526 vp = (struct vxmit *)(cp->par + (cp->cmd & 07)*sizeof (struct vxmit));
527 for (; vp >= (struct vxmit *)cp->par; vp--) {
528 tp = tp0 + (vp->line & 017);
529 tp->t_state &= ~TS_BUSY;
530 if (tp->t_state & TS_FLUSH) {
531 tp->t_state &= ~TS_FLUSH;
532 wakeup((caddr_t)&tp->t_state);
533 } else
534 ndflush(&tp->t_outq, vp->bcount+1);
535 }
536 vrelease(vs, cp);
537 if (vs->vs_vers == VXV_NEW)
538 (*linesw[tp->t_line].l_start)(tp);
539 else {
540 tp0 = &vx_tty[vx*16 + vs->vs_hiport];
541 for(tp = &vx_tty[vx*16 + vs->vs_loport]; tp <= tp0; tp++)
542 (*linesw[tp->t_line].l_start)(tp);
543 if ((cp = nextcmd(vs)) != NULL) { /* command to send? */
544 vs->vs_xmtcnt++;
545 (void) vcmd(vx, (caddr_t)&cp->cmd);
546 }
547 }
548 vs->vs_xmtcnt--;
549 }
550
551 /*
552 * Force out partial XMIT command after timeout
553 */
vxforce(vs)554 vxforce(vs)
555 register struct vx_softc *vs;
556 {
557 register struct vxcmd *cp;
558 int s;
559
560 s = spl8();
561 if ((cp = nextcmd(vs)) != NULL) {
562 vs->vs_xmtcnt++;
563 (void) vcmd((int)vs->vs_nbr, (caddr_t)&cp->cmd);
564 }
565 splx(s);
566 }
567
568 /*
569 * Start (restart) transmission on the given VX line.
570 */
vxstart(tp)571 vxstart(tp)
572 register struct tty *tp;
573 {
574 register short n;
575 register struct vx_softc *vs;
576 int s, port;
577
578 s = spl8();
579 port = VXPORT(minor(tp->t_dev));
580 vs = (struct vx_softc *)tp->t_addr;
581 if ((tp->t_state&(TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) == 0) {
582 if (tp->t_outq.c_cc <= tp->t_lowat) {
583 if (tp->t_state&TS_ASLEEP) {
584 tp->t_state &= ~TS_ASLEEP;
585 wakeup((caddr_t)&tp->t_outq);
586 }
587 if (tp->t_wsel) {
588 selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
589 tp->t_wsel = 0;
590 tp->t_state &= ~TS_WCOLL;
591 }
592 }
593 if (tp->t_outq.c_cc == 0) {
594 splx(s);
595 return;
596 }
597 scope_out(3);
598 if (1 || !(tp->t_oflag&OPOST)) /* XXX */
599 n = ndqb(&tp->t_outq, 0);
600 else {
601 n = ndqb(&tp->t_outq, 0200);
602 if (n == 0) {
603 n = getc(&tp->t_outq);
604 timeout(ttrstrt, (caddr_t)tp, (n&0177)+6);
605 tp->t_state |= TS_TIMEOUT;
606 n = 0;
607 }
608 }
609 if (n) {
610 tp->t_state |= TS_BUSY;
611 vsetq(vs, port, (char *)tp->t_outq.c_cf, n);
612 }
613 }
614 splx(s);
615 }
616
617 /*
618 * Stop output on a line.
619 */
vxstop(tp)620 vxstop(tp)
621 register struct tty *tp;
622 {
623 int s;
624
625 s = spl8();
626 if (tp->t_state&TS_BUSY)
627 if ((tp->t_state&TS_TTSTOP) == 0)
628 tp->t_state |= TS_FLUSH;
629 splx(s);
630 }
631
632 static int vxbbno = -1;
633 /*
634 * VIOCX Initialization. Makes free lists of command buffers.
635 * Resets all viocx's. Issues a LIDENT command to each
636 * viocx to establish interrupt vectors and logical port numbers.
637 */
vxinit(vx,wait)638 vxinit(vx, wait)
639 register int vx;
640 int wait;
641 {
642 register struct vx_softc *vs;
643 register struct vxdevice *addr;
644 register struct vxcmd *cp;
645 register char *resp;
646 register int j;
647 char type, *typestring;
648
649 vs = &vx_softc[vx];
650 addr = vs->vs_addr;
651 type = addr->v_ident;
652 vs->vs_vers = (type&VXT_NEW) ? VXV_NEW : VXV_OLD;
653 if (vs->vs_vers == VXV_NEW)
654 vs->vs_silosiz = addr->v_maxsilo;
655 switch (type) {
656
657 case VXT_VIOCX:
658 case VXT_VIOCX|VXT_NEW:
659 typestring = "VIOC-X";
660 /* set soft carrier for printer ports */
661 for (j = 0; j < 16; j++)
662 if (vs->vs_softCAR & (1 << j) ||
663 addr->v_portyp[j] == VXT_PARALLEL) {
664 vs->vs_softCAR |= 1 << j;
665 addr->v_dcd |= 1 << j;
666 }
667 break;
668
669 case VXT_PVIOCX:
670 case VXT_PVIOCX|VXT_NEW:
671 typestring = "VIOC-X (old connector panel)";
672 break;
673 case VXT_VIOCBOP: /* VIOC-BOP */
674 vs->vs_type = 1;
675 vs->vs_bop = ++vxbbno;
676 printf("VIOC-BOP no. %d at %x\n", vs->vs_bop, addr);
677 goto unsup;
678 default:
679 printf("vx%d: unknown type %x\n", vx, type);
680 unsup:
681 vxinfo[vx]->ui_alive = 0;
682 return;
683 }
684 vs->vs_nbr = vx; /* assign board number */
685 vs->vs_maxcmd = (vs->vs_vers == VXV_NEW) ? 24 : 4;
686 /*
687 * Initialize all cmd buffers by linking them
688 * into a free list.
689 */
690 for (j = 0; j < NVCXBUFS; j++) {
691 cp = &vs->vs_lst[j];
692 cp->c_fwd = &vs->vs_lst[j+1];
693 }
694 vs->vs_avail = &vs->vs_lst[0]; /* set idx to 1st free buf */
695 cp->c_fwd = (struct vxcmd *)0; /* mark last buf in free list */
696
697 /*
698 * Establish the interrupt vectors and define the port numbers.
699 */
700 cp = vobtain(vs);
701 cp->cmd = VXC_LIDENT;
702 cp->par[0] = vs->vs_ivec; /* ack vector */
703 cp->par[1] = cp->par[0]+1; /* cmd resp vector */
704 cp->par[3] = cp->par[0]+2; /* unsol intr vector */
705 cp->par[4] = 15; /* max ports, no longer used */
706 cp->par[5] = 0; /* set 1st port number */
707 (void) vcmd(vx, (caddr_t)&cp->cmd);
708 if (!wait)
709 return;
710
711 for (j = 0; cp->cmd == VXC_LIDENT && j < 4000000; j++)
712 ;
713 if (j >= 4000000)
714 printf("vx%d: didn't respond to LIDENT\n", vx);
715
716 /* calculate address of response buffer */
717 resp = (char *)addr + (addr->v_rspoff&0x3fff);
718 if (resp[0] != 0 && (resp[0]&0177) != 3) {
719 vrelease(vs, cp); /* init failed */
720 return;
721 }
722 vs->vs_loport = cp->par[5];
723 vs->vs_hiport = cp->par[7];
724 printf("vx%d: %s%s, ports %d-%d\n", vx,
725 (vs->vs_vers == VXV_NEW) ? "" : "old ", typestring,
726 vs->vs_loport, vs->vs_hiport);
727 vrelease(vs, cp);
728 }
729
730 /*
731 * Obtain a command buffer
732 */
733 struct vxcmd *
vobtain(vs)734 vobtain(vs)
735 register struct vx_softc *vs;
736 {
737 register struct vxcmd *p;
738 int s;
739
740 s = spl8();
741 p = vs->vs_avail;
742 if (p == (struct vxcmd *)0) {
743 #ifdef VX_DEBUG
744 if (vxintr4&VXNOBUF)
745 vxintr4 &= ~VXNOBUF;
746 #endif
747 printf("vx%d: no buffers\n", vs->vs_nbr);
748 vxstreset(vs->vs_nbr);
749 splx(s);
750 return (vobtain(vs));
751 }
752 vs->vs_avail = p->c_fwd;
753 splx(s);
754 return ((struct vxcmd *)p);
755 }
756
757 /*
758 * Release a command buffer
759 */
vrelease(vs,cp)760 vrelease(vs, cp)
761 register struct vx_softc *vs;
762 register struct vxcmd *cp;
763 {
764 int s;
765
766 #ifdef VX_DEBUG
767 if (vxintr4&VXNOBUF)
768 return;
769 #endif
770 s = spl8();
771 cp->c_fwd = vs->vs_avail;
772 vs->vs_avail = cp;
773 splx(s);
774 }
775
776 struct vxcmd *
nextcmd(vs)777 nextcmd(vs)
778 register struct vx_softc *vs;
779 {
780 register struct vxcmd *cp;
781 int s;
782
783 s = spl8();
784 cp = vs->vs_build;
785 vs->vs_build = (struct vxcmd *)0;
786 splx(s);
787 return (cp);
788 }
789
790 /*
791 * Assemble transmits into a multiple command;
792 * up to 8 transmits to 8 lines can be assembled together
793 * (on PVIOCX only).
794 */
vsetq(vs,line,addr,n)795 vsetq(vs, line, addr, n)
796 register struct vx_softc *vs;
797 caddr_t addr;
798 {
799 register struct vxcmd *cp;
800 register struct vxmit *mp;
801
802 /*
803 * Grab a new command buffer or append
804 * to the current one being built.
805 */
806 cp = vs->vs_build;
807 if (cp == (struct vxcmd *)0) {
808 cp = vobtain(vs);
809 vs->vs_build = cp;
810 cp->cmd = VXC_XMITDTA;
811 } else {
812 if ((cp->cmd & 07) == 07 || vs->vs_vers == VXV_NEW) {
813 printf("vx%d: setq overflow\n", vs-vx_softc);
814 vxstreset((int)vs->vs_nbr);
815 return;
816 }
817 cp->cmd++;
818 }
819 /*
820 * Select the next vxmit buffer and copy the
821 * characters into the buffer (if there's room
822 * and the device supports ``immediate mode'',
823 * or store an indirect pointer to the data.
824 */
825 mp = (struct vxmit *)(cp->par + (cp->cmd & 07)*sizeof (struct vxmit));
826 mp->bcount = n-1;
827 mp->line = line;
828 if (vs->vs_vers == VXV_NEW && n <= sizeof (mp->ostream)) {
829 cp->cmd = VXC_XMITIMM;
830 bcopy(addr, mp->ostream, (unsigned)n);
831 } else {
832 /* get system address of clist block */
833 addr = (caddr_t)vtoph((struct proc *)0, (unsigned)addr);
834 bcopy((caddr_t)&addr, mp->ostream, sizeof (addr));
835 }
836 /*
837 * We send the data immediately if a VIOCX,
838 * the command buffer is full, or if we've nothing
839 * currently outstanding. If we don't send it,
840 * set a timeout to force the data to be sent soon.
841 */
842 if (vs->vs_vers == VXV_NEW || (cp->cmd & 07) == 7 ||
843 vs->vs_xmtcnt == 0) {
844 vs->vs_xmtcnt++;
845 (void) vcmd((int)vs->vs_nbr, (char *)&cp->cmd);
846 vs->vs_build = 0;
847 } else
848 timeout(vxforce, (caddr_t)vs, 3);
849 }
850
851 /*
852 * Write a command out to the VIOC
853 */
vcmd(vx,cmdad)854 vcmd(vx, cmdad)
855 register int vx;
856 register caddr_t cmdad;
857 {
858 register struct vcmds *cp;
859 register struct vx_softc *vs = &vx_softc[vx];
860 int s;
861
862 s = spl8();
863 /*
864 * When the vioc is resetting, don't process
865 * anything other than VXC_LIDENT commands.
866 */
867 if (vs->vs_state == VXS_RESET && cmdad != NULL) {
868 struct vxcmd *vcp = (struct vxcmd *)(cmdad-sizeof (vcp->c_fwd));
869
870 if (vcp->cmd != VXC_LIDENT) {
871 vrelease(vs, vcp);
872 return (0);
873 }
874 }
875 cp = &vs->vs_cmds;
876 if (cmdad != (caddr_t)0) {
877 cp->cmdbuf[cp->v_fill] = cmdad;
878 if (++cp->v_fill >= VC_CMDBUFL)
879 cp->v_fill = 0;
880 if (cp->v_fill == cp->v_empty) {
881 printf("vx%d: cmd q overflow\n", vx);
882 vxstreset(vx);
883 splx(s);
884 return (0);
885 }
886 cp->v_cmdsem++;
887 }
888 if (cp->v_cmdsem && cp->v_curcnt < vs->vs_maxcmd) {
889 cp->v_cmdsem--;
890 cp->v_curcnt++;
891 vinthandl(vx, ((V_BSY|CMDquals) << 8)|V_INTR);
892 }
893 splx(s);
894 return (1);
895 }
896
897 /*
898 * VIOC acknowledge interrupt. The VIOC has received the new
899 * command. If no errors, the new command becomes one of 16 (max)
900 * current commands being executed.
901 */
vackint(vx)902 vackint(vx)
903 register vx;
904 {
905 register struct vxdevice *vp;
906 register struct vcmds *cp;
907 struct vx_softc *vs;
908 int s;
909
910 scope_out(5);
911 vs = &vx_softc[vx];
912 if (vs->vs_type) /* Its a BOP */
913 return;
914 s = spl8();
915 vp = vs->vs_addr;
916 cp = &vs->vs_cmds;
917 if (vp->v_vcid&V_ERR) {
918 register char *resp;
919 register i;
920
921 printf("vx%d: ackint error type %x v_dcd %x\n", vx,
922 vp->v_vcid & 07, vp->v_dcd & 0xff);
923 resp = (char *)vs->vs_mricmd;
924 for (i = 0; i < 16; i++)
925 printf("%x ", resp[i]&0xff);
926 printf("\n");
927 splx(s);
928 vxstreset(vx);
929 return;
930 }
931 if ((vp->v_hdwre&017) == CMDquals) {
932 #ifdef VX_DEBUG
933 if (vxintr4 & VXERR4) { /* causes VIOC INTR ERR 4 */
934 struct vxcmd *cp1, *cp0;
935
936 cp0 = (struct vxcmd *)
937 ((caddr_t)cp->cmdbuf[cp->v_empty]-sizeof (cp0->c_fwd));
938 if (cp0->cmd == VXC_XMITDTA || cp0->cmd == VXC_XMITIMM) {
939 cp1 = vobtain(vs);
940 *cp1 = *cp0;
941 vxintr4 &= ~VXERR4;
942 (void) vcmd(vx, &cp1->cmd);
943 }
944 }
945 #endif
946 cp->v_curcmd[vp->v_vcid & VCMDLEN-1] = cp->cmdbuf[cp->v_empty];
947 if (++cp->v_empty >= VC_CMDBUFL)
948 cp->v_empty = 0;
949 }
950 if (++cp->v_itrempt >= VC_IQLEN)
951 cp->v_itrempt = 0;
952 vintempt(vx);
953 splx(s);
954 (void) vcmd(vx, (caddr_t)0); /* queue next cmd, if any */
955 }
956
957 /*
958 * Command Response interrupt. The Vioc has completed
959 * a command. The command may now be returned to
960 * the appropriate device driver.
961 */
vcmdrsp(vx)962 vcmdrsp(vx)
963 register vx;
964 {
965 register struct vxdevice *vp;
966 register struct vcmds *cp;
967 register caddr_t cmd;
968 register struct vx_softc *vs;
969 register char *resp;
970 register k;
971 register int s;
972
973 scope_out(6);
974 vs = &vx_softc[vx];
975 if (vs->vs_type) { /* Its a BOP */
976 printf("vx%d: vcmdrsp interrupt\n", vx);
977 return;
978 }
979 s = spl8();
980 vp = vs->vs_addr;
981 cp = &vs->vs_cmds;
982 resp = (char *)vp + (vp->v_rspoff&0x7fff);
983 if (((k = resp[1])&V_UNBSY) == 0) {
984 printf("vx%d: cmdresp debug\n", vx);
985 splx(s);
986 vxstreset(vx);
987 return;
988 }
989 k &= VCMDLEN-1;
990 cmd = cp->v_curcmd[k];
991 cp->v_curcmd[k] = (caddr_t)0;
992 cp->v_curcnt--;
993 k = *((short *)&resp[4]); /* cmd operation code */
994 if ((k&0xff00) == VXC_LIDENT) /* want hiport number */
995 for (k = 0; k < VRESPLEN; k++)
996 cmd[k] = resp[k+4];
997 resp[1] = 0;
998 vxxint(vx, (struct vxcmd *)cmd);
999 if (vs->vs_state == VXS_READY)
1000 vinthandl(vx, ((V_BSY|RSPquals) << 8)|V_INTR);
1001 splx(s);
1002 }
1003
1004 /*
1005 * Unsolicited interrupt.
1006 */
vunsol(vx)1007 vunsol(vx)
1008 register vx;
1009 {
1010 register struct vxdevice *vp;
1011 struct vx_softc *vs;
1012 int s;
1013
1014 scope_out(1);
1015 vs = &vx_softc[vx];
1016 if (vs->vs_type) { /* Its a BOP */
1017 printf("vx%d: vunsol from BOP\n", vx);
1018 return;
1019 }
1020 s = spl8();
1021 vp = vs->vs_addr;
1022 if (vp->v_uqual&V_UNBSY) {
1023 vxrint(vx);
1024 vinthandl(vx, ((V_BSY|UNSquals) << 8)|V_INTR);
1025 #ifdef notdef
1026 } else {
1027 printf("vx%d: unsolicited interrupt error\n", vx);
1028 splx(s);
1029 vxstreset(vx);
1030 #endif
1031 }
1032 splx(s);
1033 }
1034
1035 /*
1036 * Enqueue an interrupt.
1037 */
vinthandl(vx,item)1038 vinthandl(vx, item)
1039 register int vx;
1040 register item;
1041 {
1042 register struct vcmds *cp;
1043 int empty;
1044
1045 cp = &vx_softc[vx].vs_cmds;
1046 empty = (cp->v_itrfill == cp->v_itrempt);
1047 cp->v_itrqueu[cp->v_itrfill] = item;
1048 if (++cp->v_itrfill >= VC_IQLEN)
1049 cp->v_itrfill = 0;
1050 if (cp->v_itrfill == cp->v_itrempt) {
1051 printf("vx%d: interrupt q overflow\n", vx);
1052 vxstreset(vx);
1053 } else if (empty)
1054 vintempt(vx);
1055 }
1056
vintempt(vx)1057 vintempt(vx)
1058 int vx;
1059 {
1060 register struct vcmds *cp;
1061 register struct vxdevice *vp;
1062 register struct vx_softc *vs;
1063 register short item;
1064 register short *intr;
1065
1066 vs = &vx_softc[vx];
1067 vp = vs->vs_addr;
1068 if (vp->v_vioc&V_BSY)
1069 return;
1070 cp = &vs->vs_cmds;
1071 if (cp->v_itrempt == cp->v_itrfill)
1072 return;
1073 item = cp->v_itrqueu[cp->v_itrempt];
1074 intr = (short *)&vp->v_vioc;
1075 switch ((item >> 8)&03) {
1076
1077 case CMDquals: { /* command */
1078 int phys;
1079
1080 if (cp->v_empty == cp->v_fill || vp->v_vcbsy&V_BSY)
1081 break;
1082 vs->vs_mricmd = (caddr_t)cp->cmdbuf[cp->v_empty];
1083 phys = vtoph((struct proc *)0,
1084 (unsigned)cp->cmdbuf[cp->v_empty]);
1085 vp->v_vcp[0] = ((short *)&phys)[0];
1086 vp->v_vcp[1] = ((short *)&phys)[1];
1087 vp->v_vcbsy = V_BSY;
1088 *intr = item;
1089 scope_out(4);
1090 break;
1091 }
1092
1093 case RSPquals: /* command response */
1094 *intr = item;
1095 scope_out(7);
1096 break;
1097
1098 case UNSquals: /* unsolicited interrupt */
1099 vp->v_uqual = 0;
1100 *intr = item;
1101 scope_out(2);
1102 break;
1103 }
1104 }
1105
1106 /*
1107 * Start a reset on a vioc after error (hopefully)
1108 */
vxstreset(vx)1109 vxstreset(vx)
1110 register int vx;
1111 {
1112 register struct vx_softc *vs;
1113 register struct vxdevice *vp;
1114 register struct vxcmd *cp;
1115 register int j;
1116 extern int vxinreset();
1117 int s;
1118
1119 vs = &vx_softc[vx];
1120 s = spl8();
1121 if (vs->vs_state == VXS_RESET) { /* avoid recursion */
1122 splx(s);
1123 return;
1124 }
1125 vp = vs->vs_addr;
1126 /*
1127 * Zero out the vioc structures, mark the vioc as being
1128 * reset, reinitialize the free command list, reset the vioc
1129 * and start a timer to check on the progress of the reset.
1130 */
1131 bzero((caddr_t)&vs->vs_zero,
1132 (unsigned)((caddr_t)(vs + 1) - (caddr_t)&vs->vs_zero));
1133
1134 /*
1135 * Setting VXS_RESET prevents others from issuing
1136 * commands while allowing currently queued commands to
1137 * be passed to the VIOC.
1138 */
1139 vs->vs_state = VXS_RESET;
1140 /* init all cmd buffers */
1141 for (j = 0; j < NVCXBUFS; j++) {
1142 cp = &vs->vs_lst[j];
1143 cp->c_fwd = &vs->vs_lst[j+1];
1144 }
1145 vs->vs_avail = &vs->vs_lst[0];
1146 cp->c_fwd = (struct vxcmd *)0;
1147 printf("vx%d: reset...", vx);
1148 vp->v_fault = 0;
1149 vp->v_vioc = V_BSY;
1150 vp->v_hdwre = V_RESET; /* generate reset interrupt */
1151 timeout(vxinreset, (caddr_t)vx, hz*5);
1152 splx(s);
1153 }
1154
1155 /* continue processing a reset on a vioc after an error (hopefully) */
vxinreset(vx)1156 vxinreset(vx)
1157 int vx;
1158 {
1159 register struct vxdevice *vp;
1160 int s = spl8();
1161
1162 vp = vx_softc[vx].vs_addr;
1163 /*
1164 * See if the vioc has reset.
1165 */
1166 if (vp->v_fault != VXF_READY) {
1167 printf(" vxreset failed\n");
1168 splx(s);
1169 return;
1170 }
1171 /*
1172 * Send a LIDENT to the vioc and mess with carrier flags
1173 * on parallel printer ports.
1174 */
1175 vxinit(vx, 0);
1176 splx(s);
1177 }
1178
1179 /*
1180 * Finish the reset on the vioc after an error (hopefully).
1181 *
1182 * Restore modem control, parameters and restart output.
1183 * Since the vioc can handle no more then 24 commands at a time
1184 * and we could generate as many as 48 commands, we must do this in
1185 * phases, issuing no more then 16 commands at a time.
1186 */
vxfnreset(vx,cp)1187 vxfnreset(vx, cp)
1188 register int vx;
1189 register struct vxcmd *cp;
1190 {
1191 register struct vx_softc *vs;
1192 register struct vxdevice *vp;
1193 register struct tty *tp, *tp0;
1194 register int i;
1195 #ifdef notdef
1196 register int on;
1197 #endif
1198 extern int vxrestart();
1199 int s = spl8();
1200
1201 vs = &vx_softc[vx];
1202 vrelease(vs, cp);
1203 vs->vs_state = VXS_READY;
1204
1205 vp = vs->vs_addr;
1206 vp->v_vcid = 0;
1207
1208 /*
1209 * Restore modem information and control.
1210 */
1211 tp0 = &vx_tty[vx*16];
1212 for (i = vs->vs_loport; i <= vs->vs_hiport; i++) {
1213 tp = tp0 + i;
1214 if (tp->t_state&(TS_ISOPEN|TS_WOPEN)) {
1215 tp->t_state &= ~TS_CARR_ON;
1216 vcmodem(tp->t_dev, VMOD_ON);
1217 if (tp->t_state&TS_CARR_ON)
1218 (void)(*linesw[tp->t_line].l_modem)(tp, 1);
1219 else if (tp->t_state & TS_ISOPEN)
1220 (void)(*linesw[tp->t_line].l_modem)(tp, 0);
1221 }
1222 #ifdef notdef
1223 /*
1224 * If carrier has changed while we were resetting,
1225 * take appropriate action.
1226 */
1227 on = vp->v_dcd & 1<<i;
1228 if (on && (tp->t_state&TS_CARR_ON) == 0)
1229 (void)(*linesw[tp->t_line].l_modem)(tp, 1);
1230 else if (!on && tp->t_state&TS_CARR_ON)
1231 (void)(*linesw[tp->t_line].l_modem)(tp, 0);
1232 #endif
1233 }
1234 vs->vs_state = VXS_RESET;
1235 timeout(vxrestart, (caddr_t)vx, hz);
1236 splx(s);
1237 }
1238
1239 /*
1240 * Restore a particular aspect of the VIOC.
1241 */
vxrestart(vx)1242 vxrestart(vx)
1243 int vx;
1244 {
1245 register struct tty *tp, *tp0;
1246 register struct vx_softc *vs;
1247 register int i, count;
1248 int s = spl8();
1249
1250 count = vx >> 8;
1251 vx &= 0xff;
1252 vs = &vx_softc[vx];
1253 vs->vs_state = VXS_READY;
1254 tp0 = &vx_tty[vx*16];
1255 for (i = vs->vs_loport; i <= vs->vs_hiport; i++) {
1256 tp = tp0 + i;
1257 if (count != 0) {
1258 tp->t_state &= ~(TS_BUSY|TS_TIMEOUT);
1259 if (tp->t_state&(TS_ISOPEN|TS_WOPEN))
1260 vxstart(tp); /* restart pending output */
1261 } else {
1262 if (tp->t_state&(TS_WOPEN|TS_ISOPEN))
1263 vxcparam(tp, &tp->t_termios, 0);
1264 }
1265 }
1266 if (count == 0) {
1267 vs->vs_state = VXS_RESET;
1268 timeout(vxrestart, (caddr_t)(vx + 1*256), hz);
1269 } else
1270 printf(" vx reset done\n");
1271 splx(s);
1272 }
1273
vxreset(dev)1274 vxreset(dev)
1275 dev_t dev;
1276 {
1277
1278 vxstreset((int)VXUNIT(minor(dev))); /* completes asynchronously */
1279 }
1280
1281 #ifdef VX_DEBUG
vxfreset(vx)1282 vxfreset(vx)
1283 register int vx;
1284 {
1285 struct vba_device *vi;
1286
1287 if ((unsigned)vx > NVX || (vi = vxinfo[vx]) == 0 || vi->ui_addr == 0)
1288 return (ENODEV);
1289 vx_softc[vx].vs_state = VXS_READY;
1290 vxstreset(vx);
1291 return (0); /* completes asynchronously */
1292 }
1293 #endif
1294
vcmodem(dev,flag)1295 vcmodem(dev, flag)
1296 dev_t dev;
1297 {
1298 struct tty *tp;
1299 register struct vxcmd *cp;
1300 register struct vx_softc *vs;
1301 register struct vxdevice *kp;
1302 register port;
1303 int unit;
1304
1305 unit = minor(dev);
1306 tp = &vx_tty[unit];
1307 vs = (struct vx_softc *)tp->t_addr;
1308 if (vs->vs_state != VXS_READY)
1309 return;
1310 cp = vobtain(vs);
1311 kp = vs->vs_addr;
1312
1313 port = VXPORT(unit);
1314 /*
1315 * Issue MODEM command
1316 */
1317 cp->cmd = VXC_MDMCTL;
1318 if (flag == VMOD_ON) {
1319 if (vs->vs_softCAR & (1 << port)) {
1320 cp->par[0] = V_MANUAL | V_DTR_ON | V_RTS;
1321 kp->v_dcd |= (1 << port);
1322 } else
1323 cp->par[0] = V_AUTO | V_DTR_ON;
1324 } else
1325 cp->par[0] = V_DTR_OFF;
1326 cp->par[1] = port;
1327 (void) vcmd((int)vs->vs_nbr, (caddr_t)&cp->cmd);
1328 if ((kp->v_dcd | vs->vs_softCAR) & (1 << port) && flag == VMOD_ON)
1329 tp->t_state |= TS_CARR_ON;
1330 }
1331
1332 /*
1333 * VCMINTR called when an unsolicited interrupt occurs signaling
1334 * some change of modem control state.
1335 */
vcmintr(vx)1336 vcmintr(vx)
1337 register vx;
1338 {
1339 register struct vxdevice *kp;
1340 register struct tty *tp;
1341 register port;
1342 register struct vx_softc *vs;
1343
1344 vs = &vx_softc[vx];
1345 kp = vs->vs_addr;
1346 port = kp->v_usdata[0] & 017;
1347 tp = &vx_tty[vx*16+port];
1348
1349 if (kp->v_ustat & DCD_ON)
1350 (void)(*linesw[tp->t_line].l_modem)(tp, 1);
1351 else if ((kp->v_ustat & DCD_OFF) &&
1352 ((vs->vs_softCAR & (1 << port))) == 0 &&
1353 (*linesw[tp->t_line].l_modem)(tp, 0) == 0) {
1354 register struct vcmds *cp;
1355 register struct vxcmd *cmdp;
1356
1357 /* clear all pending transmits */
1358 if (tp->t_state&(TS_BUSY|TS_FLUSH) &&
1359 vs->vs_vers == VXV_NEW) {
1360 int i, cmdfound = 0;
1361
1362 cp = &vs->vs_cmds;
1363 for (i = cp->v_empty; i != cp->v_fill; ) {
1364 cmdp = (struct vxcmd *)((long *)cp->cmdbuf[i]-1);
1365 if ((cmdp->cmd == VXC_XMITDTA ||
1366 cmdp->cmd == VXC_XMITIMM) &&
1367 ((struct vxmit *)cmdp->par)->line == port) {
1368 cmdfound++;
1369 cmdp->cmd = VXC_FDTATOX;
1370 cmdp->par[1] = port;
1371 }
1372 if (++i >= VC_CMDBUFL)
1373 i = 0;
1374 }
1375 if (cmdfound)
1376 tp->t_state &= ~(TS_BUSY|TS_FLUSH);
1377 /* cmd is already in vioc, have to flush it */
1378 else {
1379 cmdp = vobtain(vs);
1380 cmdp->cmd = VXC_FDTATOX;
1381 cmdp->par[1] = port;
1382 (void) vcmd(vx, (caddr_t)&cmdp->cmd);
1383 }
1384 }
1385 } else if ((kp->v_ustat&BRK_CHR) && (tp->t_state&TS_ISOPEN)) {
1386 (*linesw[tp->t_line].l_rint)(TTY_FE, tp);
1387 return;
1388 }
1389 }
1390 #endif
1391