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