1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2023-12-07     Shell        init ver.
9  */
10 
11 #include <ipc/condvar.h>
12 #include <rid_bitmap.h>
13 #include <terminal/terminal.h>
14 #include <terminal/tty_internal.h>
15 #include <ptyfs.h>
16 
17 #include <rtthread.h>
18 
19 /*-
20  * SPDX-License-Identifier: BSD-2-Clause
21  *
22  * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
23  * All rights reserved.
24  *
25  * Portions of this software were developed under sponsorship from Snow
26  * B.V., the Netherlands.
27  *
28  * Redistribution and use in source and binary forms, with or without
29  * modification, are permitted provided that the following conditions
30  * are met:
31  * 1. Redistributions of source code must retain the above copyright
32  *    notice, this list of conditions and the following disclaimer.
33  * 2. Redistributions in binary form must reproduce the above copyright
34  *    notice, this list of conditions and the following disclaimer in the
35  *    documentation and/or other materials provided with the distribution.
36  *
37  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
38  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
41  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
43  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
45  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
46  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  */
49 #define PTS_EXTERNAL
50 
51 /*
52  * Per-PTS structure.
53  *
54  * List of locks
55  * (t)  locked by tty_lock()
56  * (c)  const until freeing
57  */
58 struct pts_softc
59 {
60     int pts_unit;           /* (c) Device unit number. */
61     unsigned int pts_flags; /* (t) Device flags. */
62 #define PTS_PKT      0x1    /* Packet mode. */
63 #define PTS_FINISHED 0x2    /* Return errors on read()/write(). */
64 #define PTS_PTLOCKED 0x4    /* ioctl %TIOCSPTLCK/%TIOCGPTLCK */
65     char pts_pkt;           /* (t) Unread packet mode data. */
66 
67     struct rt_condvar pts_inwait;  /* (t) Blocking write() on master. */
68     struct rt_wqueue pts_inpoll;   /* (t) Select queue for write(). */
69     struct rt_condvar pts_outwait; /* (t) Blocking read() on master. */
70     struct rt_wqueue pts_outpoll;  /* (t) Select queue for read(). */
71 
72     struct ucred *pts_cred; /* (c) Resource limit. */
73     rt_device_t pts_master; /** (c) Master device.
74                              * (Note: in rtsmart kernel, we support
75                              * multi-instance ptmx )
76                              */
77 };
78 
79 /**
80  * Controller-side file operations.
81  * (P)seudo-(T)erminal (M)ultiple(X)er
82  */
83 
ptsdev_read(struct lwp_tty * tp,struct uio * uio,struct ucred * active_cred,int oflags,struct rt_thread * td)84 static int ptsdev_read(struct lwp_tty *tp, struct uio *uio,
85                        struct ucred *active_cred, int oflags,
86                        struct rt_thread *td)
87 {
88     struct pts_softc *psc = tty_softc(tp);
89     int error = 0;
90     char pkt;
91 
92     if (uio->uio_resid == 0)
93         return (0);
94 
95     tty_lock(tp);
96 
97     for (;;)
98     {
99         /*
100          * Implement packet mode. When packet mode is turned on,
101          * the first byte contains a bitmask of events that
102          * occurred (start, stop, flush, window size, etc).
103          */
104         if (psc->pts_flags & PTS_PKT && psc->pts_pkt)
105         {
106             pkt = psc->pts_pkt;
107             psc->pts_pkt = 0;
108             tty_unlock(tp);
109 
110             error = uiomove(&pkt, 1, uio);
111             return (error);
112         }
113 
114         /*
115          * Transmit regular data.
116          *
117          * XXX: We shouldn't use ttydisc_getc_poll()! Even
118          * though in this implementation, there is likely going
119          * to be data, we should just call ttydisc_getc_uio()
120          * and use its return value to sleep.
121          */
122         if (ttydisc_getc_poll(tp))
123         {
124             if (psc->pts_flags & PTS_PKT)
125             {
126                 /*
127                  * XXX: Small race. Fortunately PTY
128                  * consumers aren't multithreaded.
129                  */
130 
131                 tty_unlock(tp);
132                 pkt = TIOCPKT_DATA;
133                 error = uiomove(&pkt, 1, uio);
134                 if (error)
135                     return (error);
136                 tty_lock(tp);
137             }
138 
139             error = ttydisc_getc_uio(tp, uio);
140             break;
141         }
142 
143         /* Maybe the device isn't used anyway. */
144         if (psc->pts_flags & PTS_FINISHED)
145             break;
146 
147         /* Wait for more data. */
148         if (oflags & O_NONBLOCK)
149         {
150             error = EWOULDBLOCK;
151             break;
152         }
153         error = cv_wait_sig(&psc->pts_outwait, tp->t_mtx);
154         if (error != 0)
155             break;
156     }
157 
158     tty_unlock(tp);
159 
160     return (error);
161 }
162 
ptsdev_write(struct lwp_tty * tp,struct uio * uio,struct ucred * active_cred,int oflags,struct rt_thread * td)163 static int ptsdev_write(struct lwp_tty *tp, struct uio *uio,
164                         struct ucred *active_cred, int oflags,
165                         struct rt_thread *td)
166 {
167     struct pts_softc *psc = tty_softc(tp);
168     char ib[256], *ibstart;
169     size_t iblen, rintlen;
170     int error = 0;
171 
172     if (uio->uio_resid == 0)
173         return (0);
174 
175     for (;;)
176     {
177         ibstart = ib;
178         iblen = MIN(uio->uio_resid, sizeof ib);
179         error = uiomove(ib, iblen, uio);
180 
181         tty_lock(tp);
182         if (error != 0)
183         {
184             iblen = 0;
185             goto done;
186         }
187 
188         /*
189          * When possible, avoid the slow path. rint_bypass()
190          * copies all input to the input queue at once.
191          */
192         MPASS(iblen > 0);
193         do
194         {
195             rintlen = ttydisc_rint_simple(tp, ibstart, iblen);
196             ibstart += rintlen;
197             iblen -= rintlen;
198             if (iblen == 0)
199             {
200                 /* All data written. */
201                 break;
202             }
203 
204             /* Maybe the device isn't used anyway. */
205             if (psc->pts_flags & PTS_FINISHED)
206             {
207                 error = -EIO;
208                 goto done;
209             }
210 
211             /* Wait for more data. */
212             if (oflags & O_NONBLOCK)
213             {
214                 error = -EWOULDBLOCK;
215                 goto done;
216             }
217 
218             /* Wake up users on the slave side. */
219             ttydisc_rint_done(tp);
220             error = cv_wait_sig(&psc->pts_inwait, tp->t_mtx);
221             if (error != 0)
222                 goto done;
223         } while (iblen > 0);
224 
225         if (uio->uio_resid == 0)
226             break;
227         tty_unlock(tp);
228     }
229 
230 done:
231     ttydisc_rint_done(tp);
232     tty_unlock(tp);
233 
234     /*
235      * Don't account for the part of the buffer that we couldn't
236      * pass to the TTY.
237      */
238     uio->uio_resid += iblen;
239     return (error);
240 }
241 
ptsdev_ioctl(struct lwp_tty * tp,rt_ubase_t cmd,void * data,struct ucred * active_cred,int fflags,struct rt_thread * td)242 static int ptsdev_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, void *data,
243                         struct ucred *active_cred, int fflags,
244                         struct rt_thread *td)
245 {
246     struct pts_softc *psc = tty_softc(tp);
247     int error = 0, sig;
248 
249     switch (cmd)
250     {
251 #ifdef USING_BSD_IOCTL_EXT
252         case FIODTYPE:
253             *(int *)data = D_TTY;
254             return (0);
255 #endif
256         case FIONBIO:
257             /* This device supports non-blocking operation. */
258             return (0);
259         case FIONREAD:
260             tty_lock(tp);
261             if (psc->pts_flags & PTS_FINISHED)
262             {
263                 /* Force read() to be called. */
264                 *(int *)data = 1;
265             }
266             else
267             {
268                 *(int *)data = ttydisc_getc_poll(tp);
269             }
270             tty_unlock(tp);
271             return (0);
272 #ifdef USING_BSD_IOCTL_EXT
273         case FIODGNAME:
274 #ifdef COMPAT_FREEBSD32
275         case FIODGNAME_32:
276 #endif
277         {
278             struct fiodgname_arg *fgn;
279             const char *p;
280             int i;
281 
282             /* Reverse device name lookups, for ptsname() and ttyname(). */
283             fgn = data;
284             p = tty_devname(tp);
285             i = strlen(p) + 1;
286             if (i > fgn->len)
287                 return -EINVAL;
288             return (copyout(p, fiodgname_buf_get_ptr(fgn, cmd), i));
289         }
290 #endif
291         /*
292          * We need to implement TIOCGPGRP and TIOCGSID here again. When
293          * called on the pseudo-terminal master, it should not check if
294          * the terminal is the foreground terminal of the calling
295          * process.
296          *
297          * TIOCGETA is also implemented here. Various Linux PTY routines
298          * often call isatty(), which is implemented by tcgetattr().
299          */
300         case TIOCGETA:
301             /* Obtain terminal flags through tcgetattr(). */
302             tty_lock(tp);
303             *(struct termios *)data = tp->t_termios;
304             tty_unlock(tp);
305             return (0);
306         case TIOCSETAF:
307         case TIOCSETAW:
308             /*
309              * We must make sure we turn tcsetattr() calls of TCSAFLUSH and
310              * TCSADRAIN into something different. If an application would
311              * call TCSAFLUSH or TCSADRAIN on the master descriptor, it may
312              * deadlock waiting for all data to be read.
313              */
314             cmd = TIOCSETA;
315             break;
316         case TIOCGPTN:
317             /*
318              * Get the device unit number.
319              */
320             if (psc->pts_unit < 0)
321                 return -ENOTTY;
322             *(unsigned int *)data = psc->pts_unit;
323             return (0);
324         case TIOCGPGRP:
325             /* Get the foreground process group ID. */
326             tty_lock(tp);
327             if (tp->t_pgrp != NULL)
328                 *(int *)data = tp->t_pgrp->pgid;
329             else
330                 *(int *)data = NO_PID;
331             tty_unlock(tp);
332             return (0);
333         case TIOCGSID:
334             /* Get the session leader process ID. */
335             tty_lock(tp);
336             if (tp->t_session == NULL)
337                 error = -ENOTTY;
338             else
339                 *(int *)data = tp->t_session->sid;
340             tty_unlock(tp);
341             return (error);
342 #ifdef USING_BSD_IOCTL_EXT
343         case TIOCPTMASTER:
344             /* Yes, we are a pseudo-terminal master. */
345             return (0);
346 #endif /* USING_BSD_IOCTL_EXT */
347         case TIOCSIG:
348             /* Signal the foreground process group. */
349             sig = *(int *)data;
350             if (sig < 1 || sig >= _LWP_NSIG)
351                 return -EINVAL;
352 
353             tty_lock(tp);
354             lwp_tty_signal_pgrp(tp, sig);
355             tty_unlock(tp);
356             return (0);
357         case TIOCPKT:
358             /* Enable/disable packet mode. */
359             tty_lock(tp);
360             if (*(int *)data)
361                 psc->pts_flags |= PTS_PKT;
362             else
363                 psc->pts_flags &= ~PTS_PKT;
364             tty_unlock(tp);
365             return (0);
366     }
367 
368     /* Just redirect this ioctl to the slave device. */
369     tty_lock(tp);
370     error = tty_ioctl(tp, cmd, data, fflags, td);
371     tty_unlock(tp);
372     if (error == -ENOIOCTL)
373         error = -ENOTTY;
374 
375     return error;
376 }
377 
ptsdev_poll(struct lwp_tty * tp,struct rt_pollreq * req,struct ucred * active_cred,struct rt_thread * td)378 static int ptsdev_poll(struct lwp_tty *tp, struct rt_pollreq *req,
379                        struct ucred *active_cred, struct rt_thread *td)
380 {
381     struct pts_softc *psc = tty_softc(tp);
382     int revents = 0;
383     int events = req->_key;
384 
385     tty_lock(tp);
386 
387     if (psc->pts_flags & PTS_FINISHED)
388     {
389         /* Slave device is not opened. */
390         tty_unlock(tp);
391         return ((events & (POLLIN | POLLRDNORM)) | POLLHUP);
392     }
393 
394     if (events & (POLLIN | POLLRDNORM))
395     {
396         /* See if we can getc something. */
397         if (ttydisc_getc_poll(tp) || (psc->pts_flags & PTS_PKT && psc->pts_pkt))
398             revents |= events & (POLLIN | POLLRDNORM);
399     }
400     if (events & (POLLOUT | POLLWRNORM))
401     {
402         /* See if we can rint something. */
403         if (ttydisc_rint_poll(tp))
404             revents |= events & (POLLOUT | POLLWRNORM);
405     }
406 
407     /*
408      * No need to check for POLLHUP here. This device cannot be used
409      * as a callout device, which means we always have a carrier,
410      * because the master is.
411      */
412 
413     if (revents == 0)
414     {
415         /*
416          * This code might look misleading, but the naming of
417          * poll events on this side is the opposite of the slave
418          * device.
419          */
420         if (events & (POLLIN | POLLRDNORM))
421             rt_poll_add(&psc->pts_outpoll, req);
422         if (events & (POLLOUT | POLLWRNORM))
423             rt_poll_add(&psc->pts_inpoll, req);
424     }
425 
426     tty_unlock(tp);
427 
428     return (revents);
429 }
430 
431 #if USING_BSD_KQUEUE
432 /*
433  * kqueue support.
434  */
435 
pts_kqops_read_detach(struct knote * kn)436 static void pts_kqops_read_detach(struct knote *kn)
437 {
438     struct file *fp = kn->kn_fp;
439     struct lwp_tty *tp = fp->f_data;
440     struct pts_softc *psc = tty_softc(tp);
441 
442     knlist_remove(&psc->pts_outpoll.si_note, kn, 0);
443 }
444 
pts_kqops_read_event(struct knote * kn,long hint)445 static int pts_kqops_read_event(struct knote *kn, long hint)
446 {
447     struct file *fp = kn->kn_fp;
448     struct lwp_tty *tp = fp->f_data;
449     struct pts_softc *psc = tty_softc(tp);
450 
451     if (psc->pts_flags & PTS_FINISHED)
452     {
453         kn->kn_flags |= EV_EOF;
454         return (1);
455     }
456     else
457     {
458         kn->kn_data = ttydisc_getc_poll(tp);
459         return (kn->kn_data > 0);
460     }
461 }
462 
pts_kqops_write_detach(struct knote * kn)463 static void pts_kqops_write_detach(struct knote *kn)
464 {
465     struct file *fp = kn->kn_fp;
466     struct lwp_tty *tp = fp->f_data;
467     struct pts_softc *psc = tty_softc(tp);
468 
469     knlist_remove(&psc->pts_inpoll.si_note, kn, 0);
470 }
471 
pts_kqops_write_event(struct knote * kn,long hint)472 static int pts_kqops_write_event(struct knote *kn, long hint)
473 {
474     struct file *fp = kn->kn_fp;
475     struct lwp_tty *tp = fp->f_data;
476     struct pts_softc *psc = tty_softc(tp);
477 
478     if (psc->pts_flags & PTS_FINISHED)
479     {
480         kn->kn_flags |= EV_EOF;
481         return (1);
482     }
483     else
484     {
485         kn->kn_data = ttydisc_rint_poll(tp);
486         return (kn->kn_data > 0);
487     }
488 }
489 
490 static struct filterops pts_kqops_read = {
491     .f_isfd = 1,
492     .f_detach = pts_kqops_read_detach,
493     .f_event = pts_kqops_read_event,
494 };
495 static struct filterops pts_kqops_write = {
496     .f_isfd = 1,
497     .f_detach = pts_kqops_write_detach,
498     .f_event = pts_kqops_write_event,
499 };
500 
ptsdev_kqfilter(struct file * fp,struct knote * kn)501 static int ptsdev_kqfilter(struct file *fp, struct knote *kn)
502 {
503     struct lwp_tty *tp = fp->f_data;
504     struct pts_softc *psc = tty_softc(tp);
505     int error = 0;
506 
507     tty_lock(tp);
508 
509     switch (kn->kn_filter)
510     {
511         case EVFILT_READ:
512             kn->kn_fop = &pts_kqops_read;
513             knlist_add(&psc->pts_outpoll.si_note, kn, 1);
514             break;
515         case EVFILT_WRITE:
516             kn->kn_fop = &pts_kqops_write;
517             knlist_add(&psc->pts_inpoll.si_note, kn, 1);
518             break;
519         default:
520             error = EINVAL;
521             break;
522     }
523 
524     tty_unlock(tp);
525     return (error);
526 }
527 #endif
528 
529 #if USING_BSD_STAT
ptsdev_stat(struct file * fp,struct stat * sb,struct ucred * active_cred)530 static int ptsdev_stat(struct file *fp, struct stat *sb,
531                        struct ucred *active_cred)
532 {
533     struct lwp_tty *tp = fp->f_data;
534 #ifdef PTS_EXTERNAL
535     struct pts_softc *psc = tty_softc(tp);
536 #endif /* PTS_EXTERNAL */
537     struct cdev *dev = tp->t_dev;
538 
539     /*
540      * According to POSIX, we must implement an fstat(). This also
541      * makes this implementation compatible with Linux binaries,
542      * because Linux calls fstat() on the pseudo-terminal master to
543      * obtain st_rdev.
544      *
545      * XXX: POSIX also mentions we must fill in st_dev, but how?
546      */
547 
548     bzero(sb, sizeof *sb);
549 #ifdef PTS_EXTERNAL
550     if (psc->pts_cdev != NULL)
551         sb->st_ino = sb->st_rdev = dev2udev(psc->pts_cdev);
552     else
553 #endif /* PTS_EXTERNAL */
554         sb->st_ino = sb->st_rdev = tty_udev(tp);
555 
556     sb->st_atim = dev->si_atime;
557     sb->st_ctim = dev->si_ctime;
558     sb->st_mtim = dev->si_mtime;
559     sb->st_uid = dev->si_uid;
560     sb->st_gid = dev->si_gid;
561     sb->st_mode = dev->si_mode | S_IFCHR;
562 
563     return (0);
564 }
565 #endif
566 
ptsdev_close(struct lwp_tty * tp,struct rt_thread * td)567 static int ptsdev_close(struct lwp_tty *tp, struct rt_thread *td)
568 {
569     /* Deallocate TTY device. */
570     tty_lock(tp);
571     tty_rel_gone(tp);
572 
573 #ifdef USING_BSD_VNODE
574     /* TODO: consider the vnode operation on DFS */
575 
576     /*
577      * Open of /dev/ptmx or /dev/ptyXX changes the type of file
578      * from DTYPE_VNODE to DTYPE_PTS. vn_open() increases vnode
579      * use count, we need to decrement it, and possibly do other
580      * required cleanup.
581      */
582     if (fp->f_vnode != NULL)
583         return (vnops.fo_close(fp, td));
584 #endif /* USING_BSD_VNODE */
585 
586     return 0;
587 }
588 
589 #ifdef USING_BSD_KINFO
ptsdev_fill_kinfo(struct file * fp,struct kinfo_file * kif,struct filedesc * fdp)590 static int ptsdev_fill_kinfo(struct file *fp, struct kinfo_file *kif,
591                              struct filedesc *fdp)
592 {
593     struct lwp_tty *tp;
594 
595     kif->kf_type = KF_TYPE_PTS;
596     tp = fp->f_data;
597     kif->kf_un.kf_pts.kf_pts_dev = tty_udev(tp);
598     kif->kf_un.kf_pts.kf_pts_dev_freebsd11 =
599         kif->kf_un.kf_pts.kf_pts_dev; /* truncate */
600     strlcpy(kif->kf_path, tty_devname(tp), sizeof(kif->kf_path));
601     return (0);
602 }
603 #endif
604 
605 struct bsd_fileops bsd_ptsdev_methods = {
606     .fo_read = ptsdev_read,
607     .fo_write = ptsdev_write,
608     // .fo_truncate = invfo_truncate,
609     .fo_ioctl = ptsdev_ioctl,
610     .fo_poll = ptsdev_poll,
611     // .fo_kqfilter = ptsdev_kqfilter,
612     // .fo_stat = ptsdev_stat,
613     .fo_close = ptsdev_close,
614     // .fo_chmod = invfo_chmod,
615     // .fo_chown = invfo_chown,
616     // .fo_sendfile = invfo_sendfile,
617     // .fo_fill_kinfo = ptsdev_fill_kinfo,
618     .fo_flags = DFLAG_PASSABLE,
619 };
620 
621 /*
622  * Driver-side hooks.
623  */
624 
ptsdrv_outwakeup(struct lwp_tty * tp)625 static void ptsdrv_outwakeup(struct lwp_tty *tp)
626 {
627     struct pts_softc *psc = tty_softc(tp);
628 
629     cv_broadcast(&psc->pts_outwait);
630     rt_wqueue_wakeup_all(&psc->pts_outpoll, (void *)POLLIN);
631 }
632 
ptsdrv_inwakeup(struct lwp_tty * tp)633 static void ptsdrv_inwakeup(struct lwp_tty *tp)
634 {
635     struct pts_softc *psc = tty_softc(tp);
636 
637     cv_broadcast(&psc->pts_inwait);
638     rt_wqueue_wakeup_all(&psc->pts_inpoll, (void *)POLLOUT);
639 }
640 
ptsdrv_open(struct lwp_tty * tp)641 static int ptsdrv_open(struct lwp_tty *tp)
642 {
643     struct pts_softc *psc = tty_softc(tp);
644 
645     /* for ioctl(TIOCSPTLCK) */
646     if (psc->pts_flags & PTS_PTLOCKED)
647         return -EIO;
648 
649     psc->pts_flags &= ~PTS_FINISHED;
650 
651     return 0;
652 }
653 
ptsdrv_close(struct lwp_tty * tp)654 static void ptsdrv_close(struct lwp_tty *tp)
655 {
656     struct pts_softc *psc = tty_softc(tp);
657 
658     /* Wake up any blocked readers/writers. */
659     psc->pts_flags |= PTS_FINISHED;
660     ptsdrv_outwakeup(tp);
661     ptsdrv_inwakeup(tp);
662 }
663 
ptsdrv_pktnotify(struct lwp_tty * tp,char event)664 static void ptsdrv_pktnotify(struct lwp_tty *tp, char event)
665 {
666     struct pts_softc *psc = tty_softc(tp);
667 
668     /*
669      * Clear conflicting flags.
670      */
671 
672     switch (event)
673     {
674         case TIOCPKT_STOP:
675             psc->pts_pkt &= ~TIOCPKT_START;
676             break;
677         case TIOCPKT_START:
678             psc->pts_pkt &= ~TIOCPKT_STOP;
679             break;
680         case TIOCPKT_NOSTOP:
681             psc->pts_pkt &= ~TIOCPKT_DOSTOP;
682             break;
683         case TIOCPKT_DOSTOP:
684             psc->pts_pkt &= ~TIOCPKT_NOSTOP;
685             break;
686     }
687 
688     psc->pts_pkt |= event;
689 
690     /**
691      * Note: on smart, we don't wakeup master until it's willing to accept
692      * packet event. Because on poll, we setup POLLIN for PTS_PKT only. So There
693      * is a chance when we wakeup ipc but we can't wakeup user again. Since
694      * current wakeup will remove the wakequeue node on the meanwhile
695      */
696     if (psc->pts_flags & PTS_PKT)
697         ptsdrv_outwakeup(tp);
698 }
699 
ptsdrv_free(void * softc)700 static void ptsdrv_free(void *softc)
701 {
702     struct pts_softc *psc = softc;
703 
704     /* Make device number available again. */
705     if (psc->pts_unit >= 0)
706         ptyfs_unregister_pts(psc->pts_master, psc->pts_unit);
707 
708 #ifdef USING_BSD_UCRED
709     chgptscnt(psc->pts_cred->cr_ruidinfo, -1, 0);
710     racct_sub_cred(psc->pts_cred, RACCT_NPTS, 1);
711     crfree(psc->pts_cred);
712 #endif
713 
714     rt_wqueue_wakeup_all(&psc->pts_inpoll, (void *)POLLHUP);
715     rt_wqueue_wakeup_all(&psc->pts_outpoll, (void *)POLLHUP);
716 
717     rt_free(psc);
718 }
719 
720 static struct lwp_ttydevsw pts_class = {
721     .tsw_flags = TF_NOPREFIX,
722     .tsw_outwakeup = ptsdrv_outwakeup,
723     .tsw_inwakeup = ptsdrv_inwakeup,
724     .tsw_open = ptsdrv_open,
725     .tsw_close = ptsdrv_close,
726     .tsw_pktnotify = ptsdrv_pktnotify,
727     .tsw_free = ptsdrv_free,
728 };
729 
pts_alloc(int fflags,struct rt_thread * td,struct dfs_file * ptm_file)730 int pts_alloc(int fflags, struct rt_thread *td, struct dfs_file *ptm_file)
731 {
732     int unit;
733     struct lwp_tty *tp;
734     struct pts_softc *psc;
735     char name_buf[DIRENT_NAME_MAX];
736     const char *rootpath;
737     rt_device_t ptmx_device = ptm_file->vnode->data;
738 
739 #ifdef USING_BSD_UCRED
740     struct rt_lwp *p = td->lwp;
741     int ok, error;
742     struct ucred *cred = td->td_ucred;
743 #endif
744 
745     /* Resource limiting. */
746 #ifdef USING_BSD_UCRED
747     LWP_LOCK(p);
748     error = racct_add(p, RACCT_NPTS, 1);
749     if (error != 0)
750     {
751         LWP_UNLOCK(p);
752         return -EAGAIN;
753     }
754     ok = chgptscnt(cred->cr_ruidinfo, 1, lim_cur(td, RLIMIT_NPTS));
755     if (!ok)
756     {
757         racct_sub(p, RACCT_NPTS, 1);
758         LWP_UNLOCK(p);
759         return -EAGAIN;
760     }
761     LWP_UNLOCK(p);
762 #endif
763 
764     /* Allocate TTY and softc. */
765     psc = rt_calloc(1, sizeof(struct pts_softc));
766     cv_init(&psc->pts_inwait, "ptsin");
767     cv_init(&psc->pts_outwait, "ptsout");
768     rt_wqueue_init(&psc->pts_inpoll);
769     rt_wqueue_init(&psc->pts_outpoll);
770 
771     psc->pts_master = ptmx_device;
772 #ifdef USING_BSD_UCRED
773     psc->pts_cred = crhold(cred);
774 #else
775     psc->pts_cred = 0;
776 #endif
777 
778     tp = lwp_tty_create(&pts_class, psc);
779     if (!tp)
780     {
781         rt_free(psc);
782         rt_condvar_detach(&psc->pts_inwait);
783         rt_condvar_detach(&psc->pts_outwait);
784         return -ENOMEM;
785     }
786 
787     /* Try to allocate a new pts uint*/
788     unit = ptyfs_register_pts(ptmx_device, &tp->parent);
789     if (unit < 0)
790     {
791 #ifdef USING_BSD_UCRED
792         racct_sub(p, RACCT_NPTS, 1);
793         chgptscnt(cred->cr_ruidinfo, -1, 0);
794 #endif
795         lwp_tty_delete(tp);
796         return -EAGAIN;
797     }
798     psc->pts_unit = unit;
799 
800     /* Expose the slave device as well. */
801 #ifdef USING_BSD_UCRED
802     tty_makedev(tp, td->td_ucred, "pts/%u", psc->pts_unit);
803 #else
804     rootpath = ptyfs_get_rootpath(ptmx_device);
805     RT_ASSERT(rootpath[strlen(rootpath) - 1] != '/');
806     snprintf(name_buf, DIRENT_NAME_MAX, "%s/%d", rootpath, psc->pts_unit);
807 
808     /* setup the pts */
809     lwp_tty_register(tp, name_buf);
810 
811     /* now this file operating on new pty */
812     ptm_file->data = tp;
813 #endif
814 
815     return 0;
816 }
817 
pts_set_lock(lwp_tty_t pts,rt_bool_t is_lock)818 void pts_set_lock(lwp_tty_t pts, rt_bool_t is_lock)
819 {
820     struct pts_softc *psc = tty_softc(pts);
821     if (is_lock)
822         psc->pts_flags |= PTS_PTLOCKED;
823     else
824         psc->pts_flags &= ~PTS_PTLOCKED;
825 }
826 
pts_is_locked(lwp_tty_t pts)827 rt_bool_t pts_is_locked(lwp_tty_t pts)
828 {
829     struct pts_softc *psc = tty_softc(pts);
830     return !!(psc->pts_flags & PTS_PTLOCKED);
831 }
832 
pts_get_pktmode(lwp_tty_t pts)833 int pts_get_pktmode(lwp_tty_t pts)
834 {
835     struct pts_softc *psc = tty_softc(pts);
836     return !!(psc->pts_flags & PTS_PKT);
837 }
838