BSD 4 development
[unix-history] / usr / src / sys / dev / mx2.c
CommitLineData
2487aa9d
BJ
1/* mx2.c 4.2 11/9/80 */
2
3#include "../h/param.h"
4#include "../h/systm.h"
5#include "../h/dir.h"
6#include "../h/user.h"
7#include "../h/proc.h"
8#include "../h/tty.h"
9#include "../h/inode.h"
10#include "../h/mx.h"
11#include "../h/file.h"
12#include "../h/conf.h"
13#include "../h/buf.h"
14/*
15 * multiplexor driver
16 */
17struct chan chans[NCHANS];
18struct group *groups[NGROUPS];
19int mpxline;
20
21short cmask[16] ={
22 01, 02, 04,
23 010, 020, 040,
24 0100, 0200, 0400,
25 01000, 02000, 04000,
26 010000, 020000, 040000, 0100000
27};
28
29#define IOMOVE iomove
30#define FPEND &file[NFILE]
31struct chan *xcp(),*addch(),*nextcp();
32
33#define HIQ 100
34#define LOQ 20
35#define FP ((struct file *)cp)
36
37char mcdebugs[NDEBUGS];
38
39struct group *
40getmpx(dev)
41dev_t dev;
42{
43 register d;
44
45 d = minor(dev);
46 if (d >= NGROUPS) {
47 u.u_error = ENXIO;
48 return(NULL);
49 }
50 return(groups[d]);
51}
52
53
54/*ARGSUSED*/
55mxopen(dev, flag)
56{
57 register struct group *gp;
58 register struct file *fp;
59 register struct chan *cp;
60 int msg;
61
62 if ((gp=getmpx(dev)) == NULL) {
63 return;
64 }
65 if (!(gp->g_state&INUSE)) {
66 u.u_error = ENXIO;
67 return;
68 }
69 fp = u.u_ofile[u.u_r.r_val1];
70 if (fp->f_inode != gp->g_inode) {
71 u.u_error = ENXIO;
72 return;
73 }
74 if ((cp=addch(gp->g_inode,0)) == NULL) {
75 u.u_error = ENXIO;
76 return;
77 }
78
79 cp->c_flags = XGRP;
80 cp->c_ottyp = cp->c_ttyp = (struct tty *)cp;
81 cp->c_line = cp->c_oline = mpxline;
82
83 fp->f_flag |= FMPY;
84 fp->f_flag |= FREAD+FWRITE;
85 fp->f_un.f_chan = cp;
86
87 if (gp->g_inode == mpxip) {
88 plock(mpxip);
89 mpxname(cp);
90 msg = M_OPEN;
91 } else
92 msg = M_WATCH;
93
94 scontrol(cp, msg+(cp->c_index<<8), u.u_uid);
95 sleep((caddr_t)cp,TTIPRI);
96 if (cp->c_flags&NMBUF)
97 prele(mpxip);
98 if (cp->c_flags & WCLOSE) {
99 chdrain(cp);
100 chfree(cp);
101 u.u_error = ENXIO;
102 return;
103 }
104 cp->c_fy = fp;
105 cp->c_pgrp = u.u_procp->p_pgrp;
106}
107
108
109char mxnmbuf[NMSIZE];
110int nmsize;
111
112mpxname(cp)
113register struct chan *cp;
114{
115 register char *np;
116 register c;
117
118 np = mxnmbuf;
119 u.u_dirp = (caddr_t)u.u_arg[0];
120
121 while (np < &mxnmbuf[NMSIZE]) {
122 c = uchar();
123 if (c <= 0)
124 break;
125 *np++ = c;
126 }
127 *np++ = '\0';
128 nmsize = np - mxnmbuf;
129
130 cp->c_flags |= NMBUF;
131}
132
133
134mxclose(dev, flag, cp)
135dev_t dev;
136register struct chan *cp;
137{
138register struct group *gp;
139register struct inode *ip;
140register struct file *fp;
141int i, fmp;
142
143 fmp = flag&FMP;
144 if ((gp=getmpx(dev)) == NULL)
145 return;
146
147 ip = gp->g_inode;
148 if (ip==NULL || (ip->i_mode&IFMT)!=IFMPC) {
149 return;
150 }
151
152 /*
153 * close a channel
154 */
155 if (cp!=NULL && fmp && fmp!=FMP) {
156 for(fp=file; fp< FPEND; fp++)
157 if(fp->f_count && fp->f_flag&FMP && fp->f_un.f_chan==cp){
158 return;
159 }
160 chdrain(cp);
161 if ((cp->c_flags&WCLOSE)==0) {
162 scontrol(cp, M_CLOSE, 0);
163 cp->c_flags |= WCLOSE;
164 } else {
165 chfree(cp);
166 }
167 goto out;
168 }
169
170
171 for(fp=file; fp < FPEND; fp++) {
172 if (fp->f_count && (fp->f_flag&FMP)==FMP && fp->f_inode==ip)
173 return;
174 }
175
176 if (ip == mpxip) {
177 mpxip = NULL;
178 prele(ip);
179 }
180
181 for(i=0;i<NINDEX;i++)
182 (void) detach(gp->g_chans[i]);
183
184out:
185 if (ip->i_count == 1) {
186 groups[minor(dev)] = NULL;
187 plock(ip);
188 zero((caddr_t)gp, sizeof (struct group));
189 ip->i_mode = IFREG + 0666;
190 ip->i_un.i_rdev = 0;
191 ip->i_flag |= IUPD|ICHG;
192 iput(ip);
193 }
194}
195
196zero(s, cc)
197register char *s;
198register cc;
199{
200 while (cc--)
201 *s++ = 0;
202}
203
204char m_eot[] ={ M_EOT, 0, 0, 0};
205
206/*
207 * Mxread + mxwrite are entered from cdevsw
208 * for all read/write calls. Operations on
209 * an mpx file are handled here.
210 * Calls are made through linesw to handle actual
211 * data movement.
212 */
213mxread(dev)
214{
215 register struct group *gp;
216 register struct chan *cp;
217 register esc;
218 struct rh h;
219 caddr_t base;
220 unsigned count;
221 int s, xfr, more, fmp;
222
223 if ((gp=getmpx(dev))==NULL || (FP=getf(u.u_arg[0]))==NULL) {
224 u.u_error = ENXIO;
225 return;
226 }
227
228 fmp = FP->f_flag & FMP;
229 if (fmp != FMP) {
230 if (u.u_count == 0)
231 return;
232 msread(fmp, FP->f_un.f_chan);
233 return;
234 }
235
236 if ((int)u.u_base & 1) {
237 u.u_error = ENXIO;
238 return;
239 }
240
241 s = spl6();
242 if (u.u_count == 0)
243 {
244 if (gp->g_datq == 0)
245 u.u_error = ENXIO;
246 splx(s);
247 return;
248 }
249 while (gp->g_datq == 0) {
250 sleep((caddr_t)&gp->g_datq, TTIPRI);
251 }
252 splx(s);
253
254 while (gp->g_datq && u.u_count >= CNTLSIZ + 2) {
255 esc = 0;
256 cp = nextcp(gp);
257 if (cp==NULL) {
258 continue;
259 }
260 h.index = cpx(cp);
261 if (count = cp->c_ctlx.c_cc) {
262 count += CNTLSIZ;
263 if (cp->c_flags&NMBUF)
264 count += nmsize;
265 if (count > u.u_count) {
266 (void) sdata(cp);
267 return;
268 }
269 esc++;
270 }
271 base = u.u_base;
272 count = u.u_count;
273 u.u_base += sizeof h;
274 u.u_count -= sizeof h;
275 xfr = u.u_count;
276 if (esc) {
277 more = mcread(cp);
278 } else {
279 more = (*linesw[cp->c_line].l_read)(cp->c_ttyp);
280 }
281 if (more > 0)
282 (void) sdata(cp);
283 if (more < 0)
284 scontrol(cp, M_CLOSE, 0);
285 (void) spl0();
286 if (xfr == u.u_count) {
287 esc++;
288 IOMOVE((caddr_t)m_eot, sizeof m_eot, B_READ);
289 }
290 xfr -= u.u_count;
291 if (esc) {
292 h.count = 0;
293 h.ccount = xfr;
294 } else {
295 h.count = xfr;
296 h.ccount = 0;
297 mxrstrt(cp, &cp->cx.datq, BLOCK|ALT);
298 }
299 if (u.u_count && (xfr&1)) {
300 u.u_base++;
301 u.u_count--;
302 }
303 (void) copyout((caddr_t)&h, base, sizeof h);
304
305 }
306}
307
308
309mxwrite(dev)
310{
311register struct chan *cp;
312struct wh h;
313struct group *gp;
314int ucount, esc, fmp, burpcount;
315caddr_t ubase, hbase;
316
317 if ((gp=getmpx(dev))==NULL || (FP=getf(u.u_arg[0]))==NULL) {
318 return;
319 }
320 fmp = FP->f_flag & FMP;
321 if (fmp != FMP) {
322 mswrite(fmp, FP->f_un.f_chan);
323 return;
324 }
325
326 burpcount = 0;
327 while (u.u_count >= sizeof h) {
328 hbase = u.u_base;
329 IOMOVE((caddr_t)&h, sizeof h, B_WRITE);
330 if (u.u_error)
331 return;
332 esc = 0;
333 if (h.count==0) {
334 esc++;
335 h.count = h.ccount;
336 }
337 cp = xcp(gp, h.index);
338 if (cp==NULL || cp->c_flags&ISGRP) {
339 u.u_error = ENXIO;
340 return;
341 }
342 ucount = u.u_count;
343 ubase = u.u_base;
344 u.u_count = h.count;
345 u.u_base = h.data;
346
347 if (esc==0) {
348 struct tty *tp;
349 caddr_t waddr;
350 int line;
351
352 if (cp->c_flags&PORT) {
353 line = cp->c_line;
354 tp = cp->c_ttyp;
355 } else {
356 line = cp->c_oline;
357 tp = cp->c_ottyp;
358 }
359 loop:
360 waddr = (caddr_t)(*linesw[line].l_write)(tp);
361 if (u.u_count) {
362 if (gp->g_state&ENAMSG) {
363 burpcount++;
364 cp->c_flags |= BLKMSG;
365/*
366 scontrol(cp, M_BLK, u.u_count);
367*/
368 h.ccount = -1;
369 h.count = u.u_count;
370 h.data = u.u_base;
371 (void) copyout((caddr_t)&h, hbase, sizeof h);
372 } else {
373 if(waddr == 0) {
374 u.u_error = ENXIO;
375 return;
376 }
377 sleep(waddr, TTOPRI);
378 goto loop;
379 }
380 }
381 } else {
382 mxwcontrol(cp);
383 }
384 u.u_count = ucount;
385 u.u_base = ubase;
386 }
387 u.u_count = burpcount;
388}
389
390
391
392/*
393 * Mcread and mcwrite move data on an mpx file.
394 * Transfer addr and length is controlled by mxread/mxwrite.
395 * Kernel-to-Kernel and other special transfers are not
396 * yet in.
397 */
398mcread(cp)
399register struct chan *cp;
400{
401register struct clist *q;
402register char *np;
403
404
405 q = (cp->c_ctlx.c_cc) ? &cp->c_ctlx : &cp->cx.datq;
406 (void) mxmove(q, B_READ);
407
408 if (cp->c_flags&NMBUF && q == &cp->c_ctlx) {
409 np = mxnmbuf;
410 while (nmsize--)
411 (void) passc(*np++);
412 cp->c_flags &= ~NMBUF;
413 prele(mpxip);
414 }
415 if (cp->c_flags&PORT)
416 return(cp->c_ctlx.c_cc + cp->c_ttyp->t_rawq.c_cc); else
417 return(cp->c_ctlx.c_cc + cp->cx.datq.c_cc);
418
419}
420
421
422caddr_t
423mcwrite(cp)
424register struct chan *cp;
425{
426register struct clist *q;
427int s;
428
429 q = &cp->cy.datq;
430 while (u.u_count) {
431 s = spl6();
432 if (q->c_cc > HIQ || (cp->c_flags&EOTMARK)) {
433 cp->c_flags |= SIGBLK;
434 splx(s);
435 break;
436 }
437 splx(s);
438 (void) mxmove(q, B_WRITE);
439 }
440 wakeup((caddr_t)q);
441 return((caddr_t)q);
442}
443
444
445/*
446 * Msread and mswrite move bytes
447 * between user and non-multiplexed channel.
448 */
449msread(fmp, cp)
450register struct chan *cp;
451{
452register struct clist *q;
453int s;
454
455 q = (fmp&FMPX) ? &cp->cx.datq : &cp->cy.datq;
456 s = spl6();
457 while (q->c_cc == 0) {
458 if (cp->c_flags&WCLOSE) {
459 u.u_error = ENXIO;
460 goto out;
461 }
462 if (cp->c_flags & EOTMARK) {
463 cp->c_flags &= ~EOTMARK;
464 if(msgenab(cp))
465 scontrol(cp, M_UBLK, 0);
466 else {
467 wakeup((caddr_t)cp);
468 wakeup((caddr_t)q);
469 }
470 goto out;
471 }
472 sleep((caddr_t)q,TTIPRI);
473 }
474 if (cp->c_flags&WCLOSE) {
475 u.u_error = ENXIO;
476 goto out;
477 }
478 splx(s);
479 while (mxmove(q, B_READ) > 0)
480 ;
481 mxrstrt(cp, q, SIGBLK);
482 return;
483out:
484 splx(s);
485}
486
487
488mswrite(fmp, cp)
489register struct chan *cp;
490{
491 register struct clist *q;
492 register int cc;
493
494 q = (fmp&FMPX) ? &cp->cy.datq : &cp->cx.datq;
495 while (u.u_count) {
496 (void) spl6();
497 if (cp->c_flags&WCLOSE) {
498 gsignal(cp->c_pgrp, SIGPIPE);
499 (void) spl0();
500 return;
501 }
502 if (q->c_cc>= HIQ || cp->c_flags&FBLOCK) {
503 if (cp->c_flags&WCLOSE) {
504 gsignal(cp->c_pgrp, SIGPIPE);
505 (void) spl0();
506 return;
507 }
508 (void) sdata(cp);
509 cp->c_flags |= BLOCK;
510 sleep((caddr_t)q+1,TTOPRI);
511 (void) spl0();
512 continue;
513 }
514 (void) spl0();
515 cc = mxmove(q, B_WRITE);
516 if (cc < 0)
517 break;
518 }
519 if (fmp&FMPX) {
520 if (cp->c_flags&YGRP) (void) sdata(cp);
521 else wakeup((caddr_t)q);
522 } else {
523 if (cp->c_flags&XGRP) (void) sdata(cp);
524 else wakeup((caddr_t)q);
525 }
526}
527
528
529/*
530 * move chars between clist and user space.
531 */
532
533mxmove(q, dir)
534register struct clist *q;
535register dir;
536{
537register cc;
538char cbuf[HIQ];
539
540 cc = MIN(u.u_count, sizeof cbuf);
541 if (dir == B_READ)
542 cc = q_to_b(q, cbuf, cc);
543 if (cc <= 0)
544 return(cc);
545 IOMOVE((caddr_t)cbuf, (unsigned)cc, dir);
546 if (dir == B_WRITE)
547 cc = b_to_q(cbuf, cc, q);
548 return(cc);
549}
550
551
552
553mxrstrt(cp, q, b)
554register struct chan *cp;
555register struct clist *q;
556register b;
557{
558int s;
559
560 s = spl6();
561 if (cp->c_flags&b && q->c_cc<LOQ) {
562 cp->c_flags &= ~b;
563 if (b&ALT)
564 wakeup((caddr_t)q+1); else
565 mcstart(cp, (caddr_t)q);
566 }
567 if (cp->c_flags&WFLUSH)
568 wakeup((caddr_t)q+2);
569 splx(s);
570}
571
572
573
574/*
575 * called from driver start or xint routines
576 * to wakeup output sleeper.
577 */
578mcstart(cp, q)
579register struct chan *cp;
580register caddr_t q;
581{
582
583 if (cp->c_flags&(BLKMSG)) {
584 cp->c_flags &= ~BLKMSG;
585 scontrol(cp, M_UBLK, 0);
586 } else
587 wakeup((caddr_t)q);
588}
589
590
591mxwcontrol(cp)
592register struct chan *cp;
593{
594short cmd;
595struct sgttyb vec;
596int s;
597
598 IOMOVE((caddr_t)&cmd, sizeof cmd, B_WRITE);
599 if (u.u_error)
600 return;
601 switch(cmd) {
602 /*
603 * not ready to queue this up yet.
604 */
605 case M_EOT:
606 s = spl6();
607 while (cp->c_flags & EOTMARK)
608 if(msgenab(cp)){
609 scontrol(cp, M_BLK, 0);
610 goto out;
611 } else
612 sleep((caddr_t)cp, TTOPRI);
613 cp->c_flags |= EOTMARK;
614 out:
615 wakeup((caddr_t)&cp->cy.datq);
616 splx(s);
617 break;
618 case M_IOCTL:
619 break;
620 case M_IOANS:
621 if (cp->c_flags&SIOCTL) {
622 IOMOVE((caddr_t)&vec, sizeof vec, B_WRITE);
623 (void) b_to_q((caddr_t)&vec, sizeof vec, &cp->c_ctly);
624 cp->c_flags &= ~SIOCTL;
625 wakeup((caddr_t)cp);
626 }
627 break;
628 case M_BLK:
629 cp->c_flags |= FBLOCK;
630 break;
631 case M_UBLK:
632 cp->c_flags &= ~FBLOCK;
633 chwake(cp);
634 break;
635 default:
636 u.u_error = ENXIO;
637 }
638}
639
640
641
642/*ARGSUSED*/
643mxioctl(dev, cmd, addr, flag)
644caddr_t addr;
645{
646struct group *gp;
647int fmp;
648struct file *fp;
649struct {
650 short c_ctl;
651 short c_cmd;
652 struct sgttyb c_vec;
653} ctlbuf;
654
655 if ((gp=getmpx(dev))==NULL || (fp=getf(u.u_arg[0]))==NULL) {
656 return;
657 }
658
659 fmp = fp->f_flag & FMP;
660 if (fmp == FMP) {
661 switch(cmd) {
662
663 case MXLSTN:
664 if (mpxip == NULL) {
665 mpxip = gp->g_inode;
666 } else {
667 u.u_error = ENXIO;
668 return;
669 }
670 break;
671
672 case MXNBLK:
673 gp->g_state |= ENAMSG;
674 break;
675
676 default:
677 u.u_error = ENXIO;
678 return;
679 }
680 } else {
681 ctlbuf.c_ctl = M_IOCTL;
682 ctlbuf.c_cmd = cmd;
683 (void) copyin(addr, (caddr_t)&ctlbuf.c_vec, sizeof (struct sgttyb));
684 sioctl(fp->f_un.f_chan, (char *)&ctlbuf, sizeof ctlbuf);
685 (void) copyout((caddr_t)&ctlbuf, addr, sizeof (struct sgttyb));
686 }
687}
688
689
690chdrain(cp)
691register struct chan *cp;
692{
693register struct tty *tp;
694int wflag;
695
696 chwake(cp);
697
698 wflag = (cp->c_flags&WCLOSE)==0;
699 tp = cp->c_ttyp;
700 if (tp == NULL) /* prob not required */
701 return;
702 if (cp->c_flags&PORT && tp->t_chan == cp) {
703 cp->c_ttyp = NULL;
704 tp->t_chan = NULL;
705 return;
706 }
707 if (wflag)
708 wflush(cp,&cp->cx.datq); else
709 flush(&cp->cx.datq);
710 if (!(cp->c_flags&YGRP)) {
711 flush(&cp->cy.datq);
712 }
713}
714
715chwake(cp)
716register struct chan *cp;
717{
718register char *p;
719
720 wakeup((caddr_t)cp);
721 flush(&cp->c_ctlx);
722 p = (char *)&cp->cx.datq;
723 wakeup((caddr_t)p); wakeup((caddr_t)++p); wakeup((caddr_t)++p);
724 p = (char *)&cp->cy.datq;
725 wakeup((caddr_t)p); wakeup((caddr_t)++p); wakeup((caddr_t)++p);
726}
727
728
729chfree(cp)
730register struct chan *cp;
731{
732register struct group *gp;
733register i;
734
735 gp = cp->c_group;
736 if (gp==NULL)
737 return;
738 i = cp->c_index;
739 if (cp == gp->g_chans[i]) {
740 gp->g_chans[i] = NULL;
741 }
742 cp->c_group = NULL;
743 wakeup((caddr_t)gp);
744}
745
746
747flush(q)
748register struct clist *q;
749{
750
751 while(q->c_cc)
752 (void) getc(q);
753}
754
755
756wflush(cp,q)
757register struct chan *cp;
758register struct clist *q;
759{
760register s;
761
762 s = spl6();
763 if(q->c_cc && (cp->c_flags&WCLOSE) == 0) {
764 cp->c_flags |= WFLUSH;
765 (void) sdata(cp);
766 (void) tsleep((caddr_t)q+2, TTOPRI, 30);
767 }
768 flush(q);
769 cp->c_flags &= ~WFLUSH;
770 splx(s);
771}
772
773
774scontrol(cp,event,value)
775register struct chan *cp;
776short event,value;
777{
778register struct clist *q;
779int s;
780
781 q = &cp->c_ctlx;
782 s = spl6();
783 if (sdata(cp) == NULL)
784 return;
785 (void) putw(event,q);
786 (void) putw(value,q);
787 splx(s);
788}
789
790
791
792sioctl(cp, vec, cc)
793register struct chan *cp;
794char *vec;
795{
796register s;
797register struct clist *q;
798
799 s = spl6();
800 q = &cp->cx.datq;
801 while (q->c_cc) {
802 cp->c_flags |= BLOCK;
803 if (sdata(cp)==NULL) {
804 u.u_error = ENXIO;
805 return;
806 }
807 sleep((caddr_t)q+1, TTOPRI);
808 }
809 (void) b_to_q(vec, cc, &cp->c_ctlx);
810 cp->c_flags |= SIOCTL;
811 while (cp->c_flags&SIOCTL) {
812 if (cp->c_ctlx.c_cc)
813 if (sdata(cp)==NULL) {
814 u.u_error = ENXIO;
815 return;
816 }
817 sleep((caddr_t)cp, TTOPRI);
818 }
819 (void) q_to_b(&cp->c_ctly, vec, cp->c_ctly.c_cc);
820 splx(s);
821}
822
823sdata(cp)
824struct chan *cp;
825{
826 register struct group *gp = (struct group *)cp;
827 register struct group *ngp;
828 register int s;
829
830 ngp = gp->g_group;
831 if (ngp==NULL || (ngp->g_state&ISGRP)==0)
832 return(NULL);
833
834 s = spl6();
835 do {
836 ngp->g_datq |= cmask[gp->g_index];
837 wakeup((caddr_t)&ngp->g_datq);
838 gp = ngp;
839 } while(ngp=ngp->g_group);
840 splx(s);
841 return((int)gp);
842}
843
844
845
846struct chan *
847xcp(gp, x)
848register struct group *gp;
849register short x;
850{
851 register int i;
852
853 while (gp->g_group) gp=gp->g_group;
854 for (i=0;i<NLEVELS;i++) {
855 if ((x&017) >= NINDEX)
856 break;
857 if (gp==NULL || (gp->g_state&ISGRP)==0)
858 return((struct chan *)NULL);
859 gp = (struct group *)gp->g_chans[x&017];
860 x >>= 4;
861 }
862 return((struct chan *)gp);
863}
864
865cpx(cp)
866register struct chan *cp;
867{
868 register x;
869 register struct group *gp;
870
871 x = (-1<<4) + cp->c_index;
872 gp = cp->c_group;
873 while (gp->g_group) {
874 x <<= 4;
875 x |= gp->g_index;
876 gp = gp->g_group;
877 }
878 return(x);
879}
880
881
882struct chan *
883nextcp(gp)
884register struct group *gp;
885{
886 register struct group *lgp, *ngp;
887
888 do {
889 while ((gp->g_datq & cmask[gp->g_rot]) == 0) {
890 gp->g_rot = (gp->g_rot+1)%NINDEX;
891 }
892 lgp = gp;
893 gp = (struct group *)gp->g_chans[gp->g_rot];
894 } while (gp!=NULL && gp->g_state&ISGRP);
895
896 lgp->g_datq &= ~cmask[lgp->g_rot];
897 lgp->g_rot = (lgp->g_rot+1)%NINDEX;
898
899 while (ngp=lgp->g_group) {
900 ngp->g_datq &= ~cmask[lgp->g_index];
901 if (ngp->g_datq)
902 break;
903 lgp = ngp;
904 }
905 return((struct chan *)gp);
906}
907
908
909
910msgenab(cp)
911register struct chan *cp;
912{
913 register struct group *gp;
914
915 for(gp=cp->c_group;gp;gp=gp->g_group)
916 if(gp->g_state & ENAMSG)return(1);
917 return(0);
918}