Convert to critical section.
[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.11 2005/06/16 16:31:34 joerg 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(n)
129         int n;
130 {
131         dev_t dev1, dev2;
132         struct nm_softc *pt;
133
134         /*
135          * Simplified unit number, use low 8 bits of minor number
136          * (remember, the minor number mask is 0xffff00ff).
137          */
138         if (n & ~0x7f)
139                 return;
140
141         pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK);
142         bzero(pt, sizeof(*pt));
143         cdevsw_add(&nmdm_cdevsw, ~1, n << 1);
144         pt->part1.dev = dev1 = make_dev(&nmdm_cdevsw, n << 1,
145             0, 0, 0666, "nmdm%dA", n);
146         pt->part2.dev = dev2 = make_dev(&nmdm_cdevsw, (n << 1) + 1,
147             0, 0, 0666, "nmdm%dB", n);
148
149         dev1->si_drv1 = dev2->si_drv1 = pt;
150         dev1->si_tty = &pt->part1.nm_tty;
151         dev2->si_tty = &pt->part2.nm_tty;
152         ttyregister(&pt->part1.nm_tty);
153         ttyregister(&pt->part2.nm_tty);
154         pt->part1.nm_tty.t_oproc = nmdmstart;
155         pt->part2.nm_tty.t_oproc = nmdmstart;
156         pt->part1.nm_tty.t_stop = nmdmstop;
157         pt->part2.nm_tty.t_dev = dev1;
158         pt->part1.nm_tty.t_dev = dev2;
159         pt->part2.nm_tty.t_stop = nmdmstop;
160 }
161
162 /*ARGSUSED*/
163 static  int
164 nmdmopen(dev_t dev, int flag, int devtype, struct thread *td)
165 {
166         struct proc *p = td->td_proc;
167         struct tty *tp, *tp2;
168         int error;
169         int minr;
170 #if 0
171         dev_t nextdev;
172 #endif
173         struct nm_softc *pti;
174         int is_b;
175         int     pair;
176         struct  softpart *ourpart, *otherpart;
177
178         KKASSERT(p != NULL);
179
180         minr = lminor(dev);
181         pair = minr >> 1;
182         is_b = minr & 1;
183         
184 #if 0
185         /*
186          * XXX: Gross hack for DEVFS:
187          * If we openned this device, ensure we have the
188          * next one too, so people can open it.
189          */
190         if (pair < 127) {
191                 nextdev = makedev(major(dev), (pair+pair) + 1);
192                 if (!nextdev->si_drv1) {
193                         nmdminit(pair + 1);
194                 }
195         }
196 #endif
197         if (!dev->si_drv1)
198                 nmdminit(pair);
199
200         if (!dev->si_drv1)
201                 return(ENXIO);  
202
203         pti = dev->si_drv1;
204         if (is_b) 
205                 tp = &pti->part2.nm_tty;
206         else 
207                 tp = &pti->part1.nm_tty;
208         GETPARTS(tp, ourpart, otherpart);
209         tp2 = &otherpart->nm_tty;
210         ourpart->modemsignals |= TIOCM_LE;
211
212         if ((tp->t_state & TS_ISOPEN) == 0) {
213                 ttychars(tp);           /* Set up default chars */
214                 tp->t_iflag = TTYDEF_IFLAG;
215                 tp->t_oflag = TTYDEF_OFLAG;
216                 tp->t_lflag = TTYDEF_LFLAG;
217                 tp->t_cflag = TTYDEF_CFLAG;
218                 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
219         } else if (tp->t_state & TS_XCLUDE && suser(td)) {
220                 return (EBUSY);
221         } else if (pti->pt_prison != p->p_ucred->cr_prison) {
222                 return (EBUSY);
223         }
224
225         /*
226          * If the other side is open we have carrier
227          */
228         if (tp2->t_state & TS_ISOPEN) {
229                 (void)(*linesw[tp->t_line].l_modem)(tp, 1);
230         }
231
232         /*
233          * And the other side gets carrier as we are now open.
234          */
235         (void)(*linesw[tp2->t_line].l_modem)(tp2, 1);
236
237         /* External processing makes no sense here */
238         tp->t_lflag &= ~EXTPROC;
239
240         /* 
241          * Wait here if we don't have carrier.
242          */
243 #if 0
244         while ((tp->t_state & TS_CARR_ON) == 0) {
245                 if (flag & FNONBLOCK)
246                         break;
247                 error = ttysleep(tp, TSA_CARR_ON(tp), PCATCH, "nmdopn", 0);
248                 if (error)
249                         return (error);
250         }
251 #endif
252
253         /*
254          * Give the line disciplin a chance to set this end up.
255          */
256         error = (*linesw[tp->t_line].l_open)(dev, tp);
257
258         /*
259          * Wake up the other side.
260          * Theoretically not needed.
261          */
262         ourpart->modemsignals |= TIOCM_DTR;
263         nmdm_crossover(pti, ourpart, otherpart);
264         if (error == 0)
265                 wakeup_other(tp, FREAD|FWRITE); /* XXX */
266         return (error);
267 }
268
269 static  int
270 nmdmclose(dev_t dev, int flag, int mode, struct thread *td)
271 {
272         struct tty *tp, *tp2;
273         int err;
274         struct softpart *ourpart, *otherpart;
275
276         /*
277          * let the other end know that the game is up
278          */
279         tp = dev->si_tty;
280         GETPARTS(tp, ourpart, otherpart);
281         tp2 = &otherpart->nm_tty;
282         (void)(*linesw[tp2->t_line].l_modem)(tp2, 0);
283
284         /*
285          * XXX MDMBUF makes no sense for nmdms but would inhibit the above
286          * l_modem().  CLOCAL makes sense but isn't supported.   Special
287          * l_modem()s that ignore carrier drop make no sense for nmdms but
288          * may be in use because other parts of the line discipline make
289          * sense for nmdms.  Recover by doing everything that a normal
290          * ttymodem() would have done except for sending a SIGHUP.
291          */
292         if (tp2->t_state & TS_ISOPEN) {
293                 tp2->t_state &= ~(TS_CARR_ON | TS_CONNECTED);
294                 tp2->t_state |= TS_ZOMBIE;
295                 ttyflush(tp2, FREAD | FWRITE);
296         }
297
298         err = (*linesw[tp->t_line].l_close)(tp, flag);
299         ourpart->modemsignals &= ~TIOCM_DTR;
300         nmdm_crossover(dev->si_drv1, ourpart, otherpart);
301         nmdmstop(tp, FREAD|FWRITE);
302         (void) ttyclose(tp);
303         return (err);
304 }
305
306 static  int
307 nmdmread(dev, uio, flag)
308         dev_t dev;
309         struct uio *uio;
310         int flag;
311 {
312         int error = 0;
313         struct tty *tp, *tp2;
314         struct softpart *ourpart, *otherpart;
315
316         tp = dev->si_tty;
317         GETPARTS(tp, ourpart, otherpart);
318         tp2 = &otherpart->nm_tty;
319
320 #if 0
321         if (tp2->t_state & TS_ISOPEN) {
322                 error = (*linesw[tp->t_line].l_read)(tp, uio, flag);
323                 wakeup_other(tp, FWRITE);
324         } else {
325                 if (flag & IO_NDELAY) {
326                         return (EWOULDBLOCK);
327                 }
328                 error = tsleep(TSA_PTC_READ(tp), PCATCH, "nmdout", 0);
329                 }
330         }
331 #else
332         if ((error = (*linesw[tp->t_line].l_read)(tp, uio, flag)) == 0)
333                 wakeup_other(tp, FWRITE);
334 #endif
335         return (error);
336 }
337
338 /*
339  * Write to pseudo-tty.
340  * Wakeups of controlling tty will happen
341  * indirectly, when tty driver calls nmdmstart.
342  */
343 static  int
344 nmdmwrite(dev, uio, flag)
345         dev_t dev;
346         struct uio *uio;
347         int flag;
348 {
349         u_char *cp = 0;
350         int cc = 0;
351         u_char locbuf[BUFSIZ];
352         int cnt = 0;
353         int error = 0;
354         struct tty *tp1, *tp;
355         struct softpart *ourpart, *otherpart;
356
357         tp1 = dev->si_tty;
358         /*
359          * Get the other tty struct.
360          * basically we are writing into the INPUT side of the other device.
361          */
362         GETPARTS(tp1, ourpart, otherpart);
363         tp = &otherpart->nm_tty;
364
365 again:
366         if ((tp->t_state & TS_ISOPEN) == 0) 
367                 return (EIO);
368         while (uio->uio_resid > 0 || cc > 0) {
369                 /*
370                  * Fill up the buffer if it's empty
371                  */
372                 if (cc == 0) {
373                         cc = min(uio->uio_resid, BUFSIZ);
374                         cp = locbuf;
375                         error = uiomove((caddr_t)cp, cc, uio);
376                         if (error)
377                                 return (error);
378                         /* check again for safety */
379                         if ((tp->t_state & TS_ISOPEN) == 0) {
380                                 /* adjust for data copied in but not written */
381                                 uio->uio_resid += cc;
382                                 return (EIO);
383                         }
384                 }
385                 while (cc > 0) {
386                         if (((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= (TTYHOG-2))
387                         && ((tp->t_canq.c_cc > 0) || !(tp->t_iflag&ICANON))) {
388                                 /*
389                                  * Come here to wait for space in outq,
390                                  * or space in rawq, or an empty canq.
391                                  */
392                                 wakeup(TSA_HUP_OR_INPUT(tp));
393                                 if ((tp->t_state & TS_CONNECTED) == 0) {
394                                         /*
395                                          * Data piled up because not connected.
396                                          * Adjust for data copied in but
397                                          * not written.
398                                          */
399                                         uio->uio_resid += cc;
400                                         return (EIO);
401                                 }
402                                 if (flag & IO_NDELAY) {
403                                         /*
404                                          * Don't wait if asked not to.
405                                          * Adjust for data copied in but
406                                          * not written.
407                                          */
408                                         uio->uio_resid += cc;
409                                         if (cnt == 0)
410                                                 return (EWOULDBLOCK);
411                                         return (0);
412                                 }
413                                 error = tsleep(TSA_PTC_WRITE(tp),
414                                                 PCATCH, "nmdout", 0);
415                                 if (error) {
416                                         /*
417                                          * Tsleep returned (signal?).
418                                          * Go find out what the user wants.
419                                          * adjust for data copied in but
420                                          * not written
421                                          */
422                                         uio->uio_resid += cc;
423                                         return (error);
424                                 }
425                                 goto again;
426                         }
427                         (*linesw[tp->t_line].l_rint)(*cp++, tp);
428                         cnt++;
429                         cc--;
430                 }
431                 cc = 0;
432         }
433         return (0);
434 }
435
436 /*
437  * Start output on pseudo-tty.
438  * Wake up process selecting or sleeping for input from controlling tty.
439  */
440 static void
441 nmdmstart(tp)
442         struct tty *tp;
443 {
444         struct nm_softc *pti = tp->t_dev->si_drv1;
445
446         if (tp->t_state & TS_TTSTOP)
447                 return;
448         pti->pt_flags &= ~PF_STOPPED;
449         wakeup_other(tp, FREAD);
450 }
451
452 /* Wakes up the OTHER tty;*/
453 static void
454 wakeup_other(tp, flag)
455         struct tty *tp;
456         int flag;
457 {
458         struct softpart *ourpart, *otherpart;
459
460         GETPARTS(tp, ourpart, otherpart);
461         if (flag & FREAD) {
462                 selwakeup(&otherpart->nm_tty.t_rsel);
463                 wakeup(TSA_PTC_READ((&otherpart->nm_tty)));
464         }
465         if (flag & FWRITE) {
466                 selwakeup(&otherpart->nm_tty.t_wsel);
467                 wakeup(TSA_PTC_WRITE((&otherpart->nm_tty)));
468         }
469 }
470
471 static  void
472 nmdmstop(tp, flush)
473         struct tty *tp;
474         int flush;
475 {
476         struct nm_softc *pti = tp->t_dev->si_drv1;
477         int flag;
478
479         /* note: FLUSHREAD and FLUSHWRITE already ok */
480         if (flush == 0) {
481                 flush = TIOCPKT_STOP;
482                 pti->pt_flags |= PF_STOPPED;
483         } else
484                 pti->pt_flags &= ~PF_STOPPED;
485         /* change of perspective */
486         flag = 0;
487         if (flush & FREAD)
488                 flag |= FWRITE;
489         if (flush & FWRITE)
490                 flag |= FREAD;
491         wakeup_other(tp, flag);
492 }
493
494 /*ARGSUSED*/
495 static  int
496 nmdmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
497 {
498         struct tty *tp = dev->si_tty;
499         struct nm_softc *pti = dev->si_drv1;
500         int error;
501         struct tty *tp2;
502         struct softpart *ourpart, *otherpart;
503
504         crit_enter();
505         GETPARTS(tp, ourpart, otherpart);
506         tp2 = &otherpart->nm_tty;
507
508         error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td);
509         if (error == ENOIOCTL)
510                  error = ttioctl(tp, cmd, data, flag);
511         if (error == ENOIOCTL) {
512                 switch (cmd) {
513                 case TIOCSBRK:
514                         otherpart->gotbreak = 1;
515                         break;
516                 case TIOCCBRK:
517                         break;
518                 case TIOCSDTR:
519                         ourpart->modemsignals |= TIOCM_DTR;
520                         break;
521                 case TIOCCDTR:
522                         ourpart->modemsignals &= TIOCM_DTR;
523                         break;
524                 case TIOCMSET:
525                         ourpart->modemsignals = *(int *)data;
526                         otherpart->modemsignals = *(int *)data;
527                         break;
528                 case TIOCMBIS:
529                         ourpart->modemsignals |= *(int *)data;
530                         break;
531                 case TIOCMBIC:
532                         ourpart->modemsignals &= ~(*(int *)data);
533                         otherpart->modemsignals &= ~(*(int *)data);
534                         break;
535                 case TIOCMGET:
536                         *(int *)data = ourpart->modemsignals;
537                         break;
538                 case TIOCMSDTRWAIT:
539                         break;
540                 case TIOCMGDTRWAIT:
541                         *(int *)data = 0;
542                         break;
543                 case TIOCTIMESTAMP:
544                 case TIOCDCDTIMESTAMP:
545                 default:
546                         crit_exit();
547                         error = ENOTTY;
548                         return (error);
549                 }
550                 error = 0;
551                 nmdm_crossover(pti, ourpart, otherpart);
552         }
553         crit_exit();
554         return (error);
555 }
556
557 static void
558 nmdm_crossover(struct nm_softc *pti,
559                 struct softpart *ourpart,
560                 struct softpart *otherpart)
561 {
562         otherpart->modemsignals &= ~(TIOCM_CTS|TIOCM_CAR);
563         if (ourpart->modemsignals & TIOCM_RTS)
564                 otherpart->modemsignals |= TIOCM_CTS;
565         if (ourpart->modemsignals & TIOCM_DTR)
566                 otherpart->modemsignals |= TIOCM_CAR;
567 }
568
569
570
571 static void nmdm_drvinit (void *unused);
572
573 static void
574 nmdm_drvinit(unused)
575         void *unused;
576 {
577         /* XXX: Gross hack for DEVFS */
578         nmdminit(0);
579 }
580
581 SYSINIT(nmdmdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,nmdm_drvinit,NULL)