7ddf20a36fd47660e7f78c5f2ff7489c2c5545b4
[dragonfly.git] / sys / dev / misc / nmdm / nmdm.c
1 /*
2  * Copyright (c) 1982, 1986, 1989, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * $FreeBSD: src/sys/dev/nmdm/nmdm.c,v 1.5.2.1 2001/08/11 00:54:14 mp Exp $
34  * $DragonFly: src/sys/dev/misc/nmdm/nmdm.c,v 1.10 2004/05/19 22:52:43 dillon Exp $
35  */
36
37 /*
38  * Pseudo-nulmodem Driver
39  */
40 #include "opt_compat.h"
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #if defined(COMPAT_43) || defined(COMPAT_SUNOS)
44 #include <sys/ioctl_compat.h>
45 #endif
46 #include <sys/proc.h>
47 #include <sys/tty.h>
48 #include <sys/conf.h>
49 #include <sys/fcntl.h>
50 #include <sys/poll.h>
51 #include <sys/kernel.h>
52 #include <sys/vnode.h>
53 #include <sys/signalvar.h>
54 #include <sys/malloc.h>
55
56 MALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures");
57
58 static void nmdmstart (struct tty *tp);
59 static void nmdmstop (struct tty *tp, int rw);
60 static void wakeup_other (struct tty *tp, int flag);
61 static void nmdminit (int n);
62
63 static  d_open_t        nmdmopen;
64 static  d_close_t       nmdmclose;
65 static  d_read_t        nmdmread;
66 static  d_write_t       nmdmwrite;
67 static  d_ioctl_t       nmdmioctl;
68
69 #define CDEV_MAJOR      18
70 static struct cdevsw nmdm_cdevsw = {
71         /* name */      "pts",
72         /* maj */       CDEV_MAJOR,
73         /* flags */     D_TTY,
74         /* port */      NULL,
75         /* clone */     NULL,
76
77         /* open */      nmdmopen,
78         /* close */     nmdmclose,
79         /* read */      nmdmread,
80         /* write */     nmdmwrite,
81         /* ioctl */     nmdmioctl,
82         /* poll */      ttypoll,
83         /* mmap */      nommap,
84         /* strategy */  nostrategy,
85         /* dump */      nodump,
86         /* psize */     nopsize
87 };
88
89 #define BUFSIZ 100              /* Chunk size iomoved to/from user */
90
91 struct softpart {
92         struct tty nm_tty;
93         dev_t   dev;
94         int     modemsignals;   /* bits defined in sys/ttycom.h */
95         int     gotbreak;
96 };
97
98 struct  nm_softc {
99         int     pt_flags;
100         struct softpart part1, part2;
101         struct  prison *pt_prison;
102 };
103
104 #define PF_STOPPED      0x10            /* user told stopped */
105
106 static void
107 nmdm_crossover(struct nm_softc *pti,
108                 struct softpart *ourpart,
109                 struct softpart *otherpart);
110
111 #define GETPARTS(tp, ourpart, otherpart) \
112 do {    \
113         struct nm_softc *pti = tp->t_dev->si_drv1; \
114         if (tp == &pti->part1.nm_tty) { \
115                 ourpart = &pti->part1; \
116                 otherpart = &pti->part2; \
117         } else { \
118                 ourpart = &pti->part2; \
119                 otherpart = &pti->part1; \
120         }  \
121 } while (0)
122
123 /*
124  * This function creates and initializes a pair of ttys.
125  */
126 static void
127 nmdminit(n)
128         int n;
129 {
130         dev_t dev1, dev2;
131         struct nm_softc *pt;
132
133         /*
134          * Simplified unit number, use low 8 bits of minor number
135          * (remember, the minor number mask is 0xffff00ff).
136          */
137         if (n & ~0x7f)
138                 return;
139
140         pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK);
141         bzero(pt, sizeof(*pt));
142         cdevsw_add(&nmdm_cdevsw, ~1, n << 1);
143         pt->part1.dev = dev1 = make_dev(&nmdm_cdevsw, n << 1,
144             0, 0, 0666, "nmdm%dA", n);
145         pt->part2.dev = dev2 = make_dev(&nmdm_cdevsw, (n << 1) + 1,
146             0, 0, 0666, "nmdm%dB", n);
147
148         dev1->si_drv1 = dev2->si_drv1 = pt;
149         dev1->si_tty = &pt->part1.nm_tty;
150         dev2->si_tty = &pt->part2.nm_tty;
151         ttyregister(&pt->part1.nm_tty);
152         ttyregister(&pt->part2.nm_tty);
153         pt->part1.nm_tty.t_oproc = nmdmstart;
154         pt->part2.nm_tty.t_oproc = nmdmstart;
155         pt->part1.nm_tty.t_stop = nmdmstop;
156         pt->part2.nm_tty.t_dev = dev1;
157         pt->part1.nm_tty.t_dev = dev2;
158         pt->part2.nm_tty.t_stop = nmdmstop;
159 }
160
161 /*ARGSUSED*/
162 static  int
163 nmdmopen(dev_t dev, int flag, int devtype, struct thread *td)
164 {
165         struct proc *p = td->td_proc;
166         struct tty *tp, *tp2;
167         int error;
168         int minr;
169 #if 0
170         dev_t nextdev;
171 #endif
172         struct nm_softc *pti;
173         int is_b;
174         int     pair;
175         struct  softpart *ourpart, *otherpart;
176
177         KKASSERT(p != NULL);
178
179         minr = lminor(dev);
180         pair = minr >> 1;
181         is_b = minr & 1;
182         
183 #if 0
184         /*
185          * XXX: Gross hack for DEVFS:
186          * If we openned this device, ensure we have the
187          * next one too, so people can open it.
188          */
189         if (pair < 127) {
190                 nextdev = makedev(major(dev), (pair+pair) + 1);
191                 if (!nextdev->si_drv1) {
192                         nmdminit(pair + 1);
193                 }
194         }
195 #endif
196         if (!dev->si_drv1)
197                 nmdminit(pair);
198
199         if (!dev->si_drv1)
200                 return(ENXIO);  
201
202         pti = dev->si_drv1;
203         if (is_b) 
204                 tp = &pti->part2.nm_tty;
205         else 
206                 tp = &pti->part1.nm_tty;
207         GETPARTS(tp, ourpart, otherpart);
208         tp2 = &otherpart->nm_tty;
209         ourpart->modemsignals |= TIOCM_LE;
210
211         if ((tp->t_state & TS_ISOPEN) == 0) {
212                 ttychars(tp);           /* Set up default chars */
213                 tp->t_iflag = TTYDEF_IFLAG;
214                 tp->t_oflag = TTYDEF_OFLAG;
215                 tp->t_lflag = TTYDEF_LFLAG;
216                 tp->t_cflag = TTYDEF_CFLAG;
217                 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
218         } else if (tp->t_state & TS_XCLUDE && suser(td)) {
219                 return (EBUSY);
220         } else if (pti->pt_prison != p->p_ucred->cr_prison) {
221                 return (EBUSY);
222         }
223
224         /*
225          * If the other side is open we have carrier
226          */
227         if (tp2->t_state & TS_ISOPEN) {
228                 (void)(*linesw[tp->t_line].l_modem)(tp, 1);
229         }
230
231         /*
232          * And the other side gets carrier as we are now open.
233          */
234         (void)(*linesw[tp2->t_line].l_modem)(tp2, 1);
235
236         /* External processing makes no sense here */
237         tp->t_lflag &= ~EXTPROC;
238
239         /* 
240          * Wait here if we don't have carrier.
241          */
242 #if 0
243         while ((tp->t_state & TS_CARR_ON) == 0) {
244                 if (flag & FNONBLOCK)
245                         break;
246                 error = ttysleep(tp, TSA_CARR_ON(tp), PCATCH, "nmdopn", 0);
247                 if (error)
248                         return (error);
249         }
250 #endif
251
252         /*
253          * Give the line disciplin a chance to set this end up.
254          */
255         error = (*linesw[tp->t_line].l_open)(dev, tp);
256
257         /*
258          * Wake up the other side.
259          * Theoretically not needed.
260          */
261         ourpart->modemsignals |= TIOCM_DTR;
262         nmdm_crossover(pti, ourpart, otherpart);
263         if (error == 0)
264                 wakeup_other(tp, FREAD|FWRITE); /* XXX */
265         return (error);
266 }
267
268 static  int
269 nmdmclose(dev_t dev, int flag, int mode, struct thread *td)
270 {
271         struct tty *tp, *tp2;
272         int err;
273         struct softpart *ourpart, *otherpart;
274
275         /*
276          * let the other end know that the game is up
277          */
278         tp = dev->si_tty;
279         GETPARTS(tp, ourpart, otherpart);
280         tp2 = &otherpart->nm_tty;
281         (void)(*linesw[tp2->t_line].l_modem)(tp2, 0);
282
283         /*
284          * XXX MDMBUF makes no sense for nmdms but would inhibit the above
285          * l_modem().  CLOCAL makes sense but isn't supported.   Special
286          * l_modem()s that ignore carrier drop make no sense for nmdms but
287          * may be in use because other parts of the line discipline make
288          * sense for nmdms.  Recover by doing everything that a normal
289          * ttymodem() would have done except for sending a SIGHUP.
290          */
291         if (tp2->t_state & TS_ISOPEN) {
292                 tp2->t_state &= ~(TS_CARR_ON | TS_CONNECTED);
293                 tp2->t_state |= TS_ZOMBIE;
294                 ttyflush(tp2, FREAD | FWRITE);
295         }
296
297         err = (*linesw[tp->t_line].l_close)(tp, flag);
298         ourpart->modemsignals &= ~TIOCM_DTR;
299         nmdm_crossover(dev->si_drv1, ourpart, otherpart);
300         nmdmstop(tp, FREAD|FWRITE);
301         (void) ttyclose(tp);
302         return (err);
303 }
304
305 static  int
306 nmdmread(dev, uio, flag)
307         dev_t dev;
308         struct uio *uio;
309         int flag;
310 {
311         int error = 0;
312         struct tty *tp, *tp2;
313         struct softpart *ourpart, *otherpart;
314
315         tp = dev->si_tty;
316         GETPARTS(tp, ourpart, otherpart);
317         tp2 = &otherpart->nm_tty;
318
319 #if 0
320         if (tp2->t_state & TS_ISOPEN) {
321                 error = (*linesw[tp->t_line].l_read)(tp, uio, flag);
322                 wakeup_other(tp, FWRITE);
323         } else {
324                 if (flag & IO_NDELAY) {
325                         return (EWOULDBLOCK);
326                 }
327                 error = tsleep(TSA_PTC_READ(tp), PCATCH, "nmdout", 0);
328                 }
329         }
330 #else
331         if ((error = (*linesw[tp->t_line].l_read)(tp, uio, flag)) == 0)
332                 wakeup_other(tp, FWRITE);
333 #endif
334         return (error);
335 }
336
337 /*
338  * Write to pseudo-tty.
339  * Wakeups of controlling tty will happen
340  * indirectly, when tty driver calls nmdmstart.
341  */
342 static  int
343 nmdmwrite(dev, uio, flag)
344         dev_t dev;
345         struct uio *uio;
346         int flag;
347 {
348         u_char *cp = 0;
349         int cc = 0;
350         u_char locbuf[BUFSIZ];
351         int cnt = 0;
352         int error = 0;
353         struct tty *tp1, *tp;
354         struct softpart *ourpart, *otherpart;
355
356         tp1 = dev->si_tty;
357         /*
358          * Get the other tty struct.
359          * basically we are writing into the INPUT side of the other device.
360          */
361         GETPARTS(tp1, ourpart, otherpart);
362         tp = &otherpart->nm_tty;
363
364 again:
365         if ((tp->t_state & TS_ISOPEN) == 0) 
366                 return (EIO);
367         while (uio->uio_resid > 0 || cc > 0) {
368                 /*
369                  * Fill up the buffer if it's empty
370                  */
371                 if (cc == 0) {
372                         cc = min(uio->uio_resid, BUFSIZ);
373                         cp = locbuf;
374                         error = uiomove((caddr_t)cp, cc, uio);
375                         if (error)
376                                 return (error);
377                         /* check again for safety */
378                         if ((tp->t_state & TS_ISOPEN) == 0) {
379                                 /* adjust for data copied in but not written */
380                                 uio->uio_resid += cc;
381                                 return (EIO);
382                         }
383                 }
384                 while (cc > 0) {
385                         if (((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= (TTYHOG-2))
386                         && ((tp->t_canq.c_cc > 0) || !(tp->t_iflag&ICANON))) {
387                                 /*
388                                  * Come here to wait for space in outq,
389                                  * or space in rawq, or an empty canq.
390                                  */
391                                 wakeup(TSA_HUP_OR_INPUT(tp));
392                                 if ((tp->t_state & TS_CONNECTED) == 0) {
393                                         /*
394                                          * Data piled up because not connected.
395                                          * Adjust for data copied in but
396                                          * not written.
397                                          */
398                                         uio->uio_resid += cc;
399                                         return (EIO);
400                                 }
401                                 if (flag & IO_NDELAY) {
402                                         /*
403                                          * Don't wait if asked not to.
404                                          * Adjust for data copied in but
405                                          * not written.
406                                          */
407                                         uio->uio_resid += cc;
408                                         if (cnt == 0)
409                                                 return (EWOULDBLOCK);
410                                         return (0);
411                                 }
412                                 error = tsleep(TSA_PTC_WRITE(tp),
413                                                 PCATCH, "nmdout", 0);
414                                 if (error) {
415                                         /*
416                                          * Tsleep returned (signal?).
417                                          * Go find out what the user wants.
418                                          * adjust for data copied in but
419                                          * not written
420                                          */
421                                         uio->uio_resid += cc;
422                                         return (error);
423                                 }
424                                 goto again;
425                         }
426                         (*linesw[tp->t_line].l_rint)(*cp++, tp);
427                         cnt++;
428                         cc--;
429                 }
430                 cc = 0;
431         }
432         return (0);
433 }
434
435 /*
436  * Start output on pseudo-tty.
437  * Wake up process selecting or sleeping for input from controlling tty.
438  */
439 static void
440 nmdmstart(tp)
441         struct tty *tp;
442 {
443         struct nm_softc *pti = tp->t_dev->si_drv1;
444
445         if (tp->t_state & TS_TTSTOP)
446                 return;
447         pti->pt_flags &= ~PF_STOPPED;
448         wakeup_other(tp, FREAD);
449 }
450
451 /* Wakes up the OTHER tty;*/
452 static void
453 wakeup_other(tp, flag)
454         struct tty *tp;
455         int flag;
456 {
457         struct softpart *ourpart, *otherpart;
458
459         GETPARTS(tp, ourpart, otherpart);
460         if (flag & FREAD) {
461                 selwakeup(&otherpart->nm_tty.t_rsel);
462                 wakeup(TSA_PTC_READ((&otherpart->nm_tty)));
463         }
464         if (flag & FWRITE) {
465                 selwakeup(&otherpart->nm_tty.t_wsel);
466                 wakeup(TSA_PTC_WRITE((&otherpart->nm_tty)));
467         }
468 }
469
470 static  void
471 nmdmstop(tp, flush)
472         struct tty *tp;
473         int flush;
474 {
475         struct nm_softc *pti = tp->t_dev->si_drv1;
476         int flag;
477
478         /* note: FLUSHREAD and FLUSHWRITE already ok */
479         if (flush == 0) {
480                 flush = TIOCPKT_STOP;
481                 pti->pt_flags |= PF_STOPPED;
482         } else
483                 pti->pt_flags &= ~PF_STOPPED;
484         /* change of perspective */
485         flag = 0;
486         if (flush & FREAD)
487                 flag |= FWRITE;
488         if (flush & FWRITE)
489                 flag |= FREAD;
490         wakeup_other(tp, flag);
491 }
492
493 /*ARGSUSED*/
494 static  int
495 nmdmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
496 {
497         struct tty *tp = dev->si_tty;
498         struct nm_softc *pti = dev->si_drv1;
499         int error, s;
500         struct tty *tp2;
501         struct softpart *ourpart, *otherpart;
502
503         s = spltty();
504         GETPARTS(tp, ourpart, otherpart);
505         tp2 = &otherpart->nm_tty;
506
507         error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td);
508         if (error == ENOIOCTL)
509                  error = ttioctl(tp, cmd, data, flag);
510         if (error == ENOIOCTL) {
511                 switch (cmd) {
512                 case TIOCSBRK:
513                         otherpart->gotbreak = 1;
514                         break;
515                 case TIOCCBRK:
516                         break;
517                 case TIOCSDTR:
518                         ourpart->modemsignals |= TIOCM_DTR;
519                         break;
520                 case TIOCCDTR:
521                         ourpart->modemsignals &= TIOCM_DTR;
522                         break;
523                 case TIOCMSET:
524                         ourpart->modemsignals = *(int *)data;
525                         otherpart->modemsignals = *(int *)data;
526                         break;
527                 case TIOCMBIS:
528                         ourpart->modemsignals |= *(int *)data;
529                         break;
530                 case TIOCMBIC:
531                         ourpart->modemsignals &= ~(*(int *)data);
532                         otherpart->modemsignals &= ~(*(int *)data);
533                         break;
534                 case TIOCMGET:
535                         *(int *)data = ourpart->modemsignals;
536                         break;
537                 case TIOCMSDTRWAIT:
538                         break;
539                 case TIOCMGDTRWAIT:
540                         *(int *)data = 0;
541                         break;
542                 case TIOCTIMESTAMP:
543                 case TIOCDCDTIMESTAMP:
544                 default:
545                         splx(s);
546                         error = ENOTTY;
547                         return (error);
548                 }
549                 error = 0;
550                 nmdm_crossover(pti, ourpart, otherpart);
551         }
552         splx(s);
553         return (error);
554 }
555
556 static void
557 nmdm_crossover(struct nm_softc *pti,
558                 struct softpart *ourpart,
559                 struct softpart *otherpart)
560 {
561         otherpart->modemsignals &= ~(TIOCM_CTS|TIOCM_CAR);
562         if (ourpart->modemsignals & TIOCM_RTS)
563                 otherpart->modemsignals |= TIOCM_CTS;
564         if (ourpart->modemsignals & TIOCM_DTR)
565                 otherpart->modemsignals |= TIOCM_CAR;
566 }
567
568
569
570 static void nmdm_drvinit (void *unused);
571
572 static void
573 nmdm_drvinit(unused)
574         void *unused;
575 {
576         /* XXX: Gross hack for DEVFS */
577         nmdminit(0);
578 }
579
580 SYSINIT(nmdmdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,nmdm_drvinit,NULL)