* Ansify function definitions.
[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.12 2005/12/11 01:54:08 swildner 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/thread2.h>
48 #include <sys/tty.h>
49 #include <sys/conf.h>
50 #include <sys/fcntl.h>
51 #include <sys/poll.h>
52 #include <sys/kernel.h>
53 #include <sys/vnode.h>
54 #include <sys/signalvar.h>
55 #include <sys/malloc.h>
56
57 MALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures");
58
59 static void nmdmstart (struct tty *tp);
60 static void nmdmstop (struct tty *tp, int rw);
61 static void wakeup_other (struct tty *tp, int flag);
62 static void nmdminit (int n);
63
64 static  d_open_t        nmdmopen;
65 static  d_close_t       nmdmclose;
66 static  d_read_t        nmdmread;
67 static  d_write_t       nmdmwrite;
68 static  d_ioctl_t       nmdmioctl;
69
70 #define CDEV_MAJOR      18
71 static struct cdevsw nmdm_cdevsw = {
72         /* name */      "pts",
73         /* maj */       CDEV_MAJOR,
74         /* flags */     D_TTY,
75         /* port */      NULL,
76         /* clone */     NULL,
77
78         /* open */      nmdmopen,
79         /* close */     nmdmclose,
80         /* read */      nmdmread,
81         /* write */     nmdmwrite,
82         /* ioctl */     nmdmioctl,
83         /* poll */      ttypoll,
84         /* mmap */      nommap,
85         /* strategy */  nostrategy,
86         /* dump */      nodump,
87         /* psize */     nopsize
88 };
89
90 #define BUFSIZ 100              /* Chunk size iomoved to/from user */
91
92 struct softpart {
93         struct tty nm_tty;
94         dev_t   dev;
95         int     modemsignals;   /* bits defined in sys/ttycom.h */
96         int     gotbreak;
97 };
98
99 struct  nm_softc {
100         int     pt_flags;
101         struct softpart part1, part2;
102         struct  prison *pt_prison;
103 };
104
105 #define PF_STOPPED      0x10            /* user told stopped */
106
107 static void
108 nmdm_crossover(struct nm_softc *pti,
109                 struct softpart *ourpart,
110                 struct softpart *otherpart);
111
112 #define GETPARTS(tp, ourpart, otherpart) \
113 do {    \
114         struct nm_softc *pti = tp->t_dev->si_drv1; \
115         if (tp == &pti->part1.nm_tty) { \
116                 ourpart = &pti->part1; \
117                 otherpart = &pti->part2; \
118         } else { \
119                 ourpart = &pti->part2; \
120                 otherpart = &pti->part1; \
121         }  \
122 } while (0)
123
124 /*
125  * This function creates and initializes a pair of ttys.
126  */
127 static void
128 nmdminit(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_t dev, struct uio *uio, int flag)
307 {
308         int error = 0;
309         struct tty *tp, *tp2;
310         struct softpart *ourpart, *otherpart;
311
312         tp = dev->si_tty;
313         GETPARTS(tp, ourpart, otherpart);
314         tp2 = &otherpart->nm_tty;
315
316 #if 0
317         if (tp2->t_state & TS_ISOPEN) {
318                 error = (*linesw[tp->t_line].l_read)(tp, uio, flag);
319                 wakeup_other(tp, FWRITE);
320         } else {
321                 if (flag & IO_NDELAY) {
322                         return (EWOULDBLOCK);
323                 }
324                 error = tsleep(TSA_PTC_READ(tp), PCATCH, "nmdout", 0);
325                 }
326         }
327 #else
328         if ((error = (*linesw[tp->t_line].l_read)(tp, uio, flag)) == 0)
329                 wakeup_other(tp, FWRITE);
330 #endif
331         return (error);
332 }
333
334 /*
335  * Write to pseudo-tty.
336  * Wakeups of controlling tty will happen
337  * indirectly, when tty driver calls nmdmstart.
338  */
339 static  int
340 nmdmwrite(dev_t dev, struct uio *uio, int flag)
341 {
342         u_char *cp = 0;
343         int cc = 0;
344         u_char locbuf[BUFSIZ];
345         int cnt = 0;
346         int error = 0;
347         struct tty *tp1, *tp;
348         struct softpart *ourpart, *otherpart;
349
350         tp1 = dev->si_tty;
351         /*
352          * Get the other tty struct.
353          * basically we are writing into the INPUT side of the other device.
354          */
355         GETPARTS(tp1, ourpart, otherpart);
356         tp = &otherpart->nm_tty;
357
358 again:
359         if ((tp->t_state & TS_ISOPEN) == 0) 
360                 return (EIO);
361         while (uio->uio_resid > 0 || cc > 0) {
362                 /*
363                  * Fill up the buffer if it's empty
364                  */
365                 if (cc == 0) {
366                         cc = min(uio->uio_resid, BUFSIZ);
367                         cp = locbuf;
368                         error = uiomove((caddr_t)cp, cc, uio);
369                         if (error)
370                                 return (error);
371                         /* check again for safety */
372                         if ((tp->t_state & TS_ISOPEN) == 0) {
373                                 /* adjust for data copied in but not written */
374                                 uio->uio_resid += cc;
375                                 return (EIO);
376                         }
377                 }
378                 while (cc > 0) {
379                         if (((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= (TTYHOG-2))
380                         && ((tp->t_canq.c_cc > 0) || !(tp->t_iflag&ICANON))) {
381                                 /*
382                                  * Come here to wait for space in outq,
383                                  * or space in rawq, or an empty canq.
384                                  */
385                                 wakeup(TSA_HUP_OR_INPUT(tp));
386                                 if ((tp->t_state & TS_CONNECTED) == 0) {
387                                         /*
388                                          * Data piled up because not connected.
389                                          * Adjust for data copied in but
390                                          * not written.
391                                          */
392                                         uio->uio_resid += cc;
393                                         return (EIO);
394                                 }
395                                 if (flag & IO_NDELAY) {
396                                         /*
397                                          * Don't wait if asked not to.
398                                          * Adjust for data copied in but
399                                          * not written.
400                                          */
401                                         uio->uio_resid += cc;
402                                         if (cnt == 0)
403                                                 return (EWOULDBLOCK);
404                                         return (0);
405                                 }
406                                 error = tsleep(TSA_PTC_WRITE(tp),
407                                                 PCATCH, "nmdout", 0);
408                                 if (error) {
409                                         /*
410                                          * Tsleep returned (signal?).
411                                          * Go find out what the user wants.
412                                          * adjust for data copied in but
413                                          * not written
414                                          */
415                                         uio->uio_resid += cc;
416                                         return (error);
417                                 }
418                                 goto again;
419                         }
420                         (*linesw[tp->t_line].l_rint)(*cp++, tp);
421                         cnt++;
422                         cc--;
423                 }
424                 cc = 0;
425         }
426         return (0);
427 }
428
429 /*
430  * Start output on pseudo-tty.
431  * Wake up process selecting or sleeping for input from controlling tty.
432  */
433 static void
434 nmdmstart(struct tty *tp)
435 {
436         struct nm_softc *pti = tp->t_dev->si_drv1;
437
438         if (tp->t_state & TS_TTSTOP)
439                 return;
440         pti->pt_flags &= ~PF_STOPPED;
441         wakeup_other(tp, FREAD);
442 }
443
444 /* Wakes up the OTHER tty;*/
445 static void
446 wakeup_other(struct tty *tp, int flag)
447 {
448         struct softpart *ourpart, *otherpart;
449
450         GETPARTS(tp, ourpart, otherpart);
451         if (flag & FREAD) {
452                 selwakeup(&otherpart->nm_tty.t_rsel);
453                 wakeup(TSA_PTC_READ((&otherpart->nm_tty)));
454         }
455         if (flag & FWRITE) {
456                 selwakeup(&otherpart->nm_tty.t_wsel);
457                 wakeup(TSA_PTC_WRITE((&otherpart->nm_tty)));
458         }
459 }
460
461 static  void
462 nmdmstop(struct tty *tp, int flush)
463 {
464         struct nm_softc *pti = tp->t_dev->si_drv1;
465         int flag;
466
467         /* note: FLUSHREAD and FLUSHWRITE already ok */
468         if (flush == 0) {
469                 flush = TIOCPKT_STOP;
470                 pti->pt_flags |= PF_STOPPED;
471         } else
472                 pti->pt_flags &= ~PF_STOPPED;
473         /* change of perspective */
474         flag = 0;
475         if (flush & FREAD)
476                 flag |= FWRITE;
477         if (flush & FWRITE)
478                 flag |= FREAD;
479         wakeup_other(tp, flag);
480 }
481
482 /*ARGSUSED*/
483 static  int
484 nmdmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
485 {
486         struct tty *tp = dev->si_tty;
487         struct nm_softc *pti = dev->si_drv1;
488         int error;
489         struct tty *tp2;
490         struct softpart *ourpart, *otherpart;
491
492         crit_enter();
493         GETPARTS(tp, ourpart, otherpart);
494         tp2 = &otherpart->nm_tty;
495
496         error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td);
497         if (error == ENOIOCTL)
498                  error = ttioctl(tp, cmd, data, flag);
499         if (error == ENOIOCTL) {
500                 switch (cmd) {
501                 case TIOCSBRK:
502                         otherpart->gotbreak = 1;
503                         break;
504                 case TIOCCBRK:
505                         break;
506                 case TIOCSDTR:
507                         ourpart->modemsignals |= TIOCM_DTR;
508                         break;
509                 case TIOCCDTR:
510                         ourpart->modemsignals &= TIOCM_DTR;
511                         break;
512                 case TIOCMSET:
513                         ourpart->modemsignals = *(int *)data;
514                         otherpart->modemsignals = *(int *)data;
515                         break;
516                 case TIOCMBIS:
517                         ourpart->modemsignals |= *(int *)data;
518                         break;
519                 case TIOCMBIC:
520                         ourpart->modemsignals &= ~(*(int *)data);
521                         otherpart->modemsignals &= ~(*(int *)data);
522                         break;
523                 case TIOCMGET:
524                         *(int *)data = ourpart->modemsignals;
525                         break;
526                 case TIOCMSDTRWAIT:
527                         break;
528                 case TIOCMGDTRWAIT:
529                         *(int *)data = 0;
530                         break;
531                 case TIOCTIMESTAMP:
532                 case TIOCDCDTIMESTAMP:
533                 default:
534                         crit_exit();
535                         error = ENOTTY;
536                         return (error);
537                 }
538                 error = 0;
539                 nmdm_crossover(pti, ourpart, otherpart);
540         }
541         crit_exit();
542         return (error);
543 }
544
545 static void
546 nmdm_crossover(struct nm_softc *pti,
547                 struct softpart *ourpart,
548                 struct softpart *otherpart)
549 {
550         otherpart->modemsignals &= ~(TIOCM_CTS|TIOCM_CAR);
551         if (ourpart->modemsignals & TIOCM_RTS)
552                 otherpart->modemsignals |= TIOCM_CTS;
553         if (ourpart->modemsignals & TIOCM_DTR)
554                 otherpart->modemsignals |= TIOCM_CAR;
555 }
556
557
558
559 static void nmdm_drvinit (void *unused);
560
561 static void
562 nmdm_drvinit(void *unused)
563 {
564         /* XXX: Gross hack for DEVFS */
565         nmdminit(0);
566 }
567
568 SYSINIT(nmdmdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,nmdm_drvinit,NULL)