712 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			712 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | /*
 | ||
|  |  * Copyright (c) 2006-2023, RT-Thread Development Team | ||
|  |  * | ||
|  |  * SPDX-License-Identifier: Apache-2.0 | ||
|  |  * | ||
|  |  * (tty_compat.c) | ||
|  |  * The compatible layer which interacts with process management core (lwp) | ||
|  |  * | ||
|  |  * Change Logs: | ||
|  |  * Date           Author       Notes | ||
|  |  * 2023-11-13     Shell        init ver. | ||
|  |  */ | ||
|  | 
 | ||
|  | #define DBG_TAG "lwp.tty"
 | ||
|  | #define DBG_LVL DBG_INFO
 | ||
|  | #include <rtdbg.h>
 | ||
|  | 
 | ||
|  | #include "../tty_config.h"
 | ||
|  | #include "../tty_internal.h"
 | ||
|  | #include "../terminal.h"
 | ||
|  | 
 | ||
|  | /*-
 | ||
|  |  * SPDX-License-Identifier: BSD-2-Clause | ||
|  |  * | ||
|  |  * Copyright (c) 1994-1995 Søren Schmidt | ||
|  |  * All rights reserved. | ||
|  |  * | ||
|  |  * Redistribution and use in source and binary forms, with or without | ||
|  |  * modification, are permitted provided that the following conditions | ||
|  |  * are met: | ||
|  |  * 1. Redistributions of source code must retain the above copyright | ||
|  |  *    notice, this list of conditions and the following disclaimer. | ||
|  |  * 2. Redistributions in binary form must reproduce the above copyright | ||
|  |  *    notice, this list of conditions and the following disclaimer in the | ||
|  |  *    documentation and/or other materials provided with the distribution. | ||
|  |  * | ||
|  |  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | ||
|  |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
|  |  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | ||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
|  |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
|  |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
|  |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
|  |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
|  |  * SUCH DAMAGE. | ||
|  |  */ | ||
|  | 
 | ||
|  | /* is the tty and session leader already binding ? */ | ||
|  | static rt_bool_t _is_already_binding(lwp_tty_t tp, rt_lwp_t p) | ||
|  | { | ||
|  |     rt_bool_t rc; | ||
|  |     rt_processgroup_t pgrp = p->pgrp; | ||
|  | 
 | ||
|  |     /* lwp is already locked */ | ||
|  |     RT_ASSERT(pgrp); | ||
|  | 
 | ||
|  |     /* Note: pgrp->session is constant after process group is created */ | ||
|  |     if (tp->t_session && tp->t_session == pgrp->session) | ||
|  |     { | ||
|  |         rc = RT_TRUE; | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  |         rc = RT_FALSE; | ||
|  |     } | ||
|  |     return rc; | ||
|  | } | ||
|  | 
 | ||
|  | static rt_bool_t _is_tty_or_sess_busy(lwp_tty_t tp, rt_lwp_t p) | ||
|  | { | ||
|  |     rt_bool_t rc; | ||
|  |     rt_session_t sess = p->pgrp->session; | ||
|  | 
 | ||
|  |     SESS_LOCK(sess); | ||
|  |     if (sess->ctty) | ||
|  |     { | ||
|  |         rc = RT_TRUE; | ||
|  |     } | ||
|  |     else if (tp->t_session) | ||
|  |     { | ||
|  |         /**
 | ||
|  |          * TODO: allow TTY stolen if the sess leader is killed while resource | ||
|  |          * had not been collected | ||
|  |          */ | ||
|  |         if (tp->t_session->leader == RT_NULL) | ||
|  |             rc = RT_FALSE; | ||
|  |         else | ||
|  |             rc = RT_TRUE; | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  |         rc = RT_FALSE; | ||
|  |     } | ||
|  |     SESS_UNLOCK(sess); | ||
|  |     return rc; | ||
|  | } | ||
|  | 
 | ||
|  | int lwp_tty_bg_stop(struct lwp_tty *tp, struct rt_condvar *cv) | ||
|  | { | ||
|  |     int error; | ||
|  |     int revokecnt = tp->t_revokecnt; | ||
|  |     rt_lwp_t self_lwp; | ||
|  |     rt_thread_t header_thr; | ||
|  |     rt_thread_t cur_thr = rt_thread_self(); | ||
|  |     int jobctl_stopped; | ||
|  | 
 | ||
|  |     self_lwp = cur_thr->lwp; | ||
|  |     RT_ASSERT(self_lwp); | ||
|  | 
 | ||
|  |     jobctl_stopped = self_lwp->jobctl_stopped; | ||
|  | 
 | ||
|  |     tty_lock_assert(tp, MA_OWNED | MA_NOTRECURSED); | ||
|  |     MPASS(!tty_gone(tp)); | ||
|  | 
 | ||
|  |     LWP_LOCK(self_lwp); | ||
|  |     header_thr = rt_list_entry(self_lwp->t_grp.prev, struct rt_thread, sibling); | ||
|  |     if (!jobctl_stopped && header_thr == cur_thr && | ||
|  |         cur_thr->sibling.prev == &self_lwp->t_grp) | ||
|  |     { | ||
|  |         /* update lwp status */ | ||
|  |         jobctl_stopped = self_lwp->jobctl_stopped = RT_TRUE; | ||
|  |     } | ||
|  |     LWP_UNLOCK(self_lwp); | ||
|  | 
 | ||
|  |     error = cv_wait(cv, tp->t_mtx); | ||
|  | 
 | ||
|  |     if (jobctl_stopped) | ||
|  |     { | ||
|  |         self_lwp->jobctl_stopped = RT_FALSE; | ||
|  |     } | ||
|  | 
 | ||
|  |     /* Bail out when the device slipped away. */ | ||
|  |     if (tty_gone(tp)) | ||
|  |         return -ENXIO; | ||
|  | 
 | ||
|  |     /* Restart the system call when we may have been revoked. */ | ||
|  |     if (tp->t_revokecnt != revokecnt) | ||
|  |         return -ERESTART; | ||
|  | 
 | ||
|  |     return error; | ||
|  | } | ||
|  | 
 | ||
|  | /* process management */ | ||
|  | int lwp_tty_set_ctrl_proc(lwp_tty_t tp, rt_thread_t td) | ||
|  | { | ||
|  |     int rc = -1; | ||
|  |     struct rt_lwp *p = td->lwp; | ||
|  | 
 | ||
|  |     tty_unlock(tp); | ||
|  |     LWP_LOCK(p); | ||
|  |     tty_lock(tp); | ||
|  | 
 | ||
|  |     if (is_sess_leader(p)) | ||
|  |     { | ||
|  |         if (_is_already_binding(tp, p)) | ||
|  |         { | ||
|  |             rc = 0; | ||
|  |         } | ||
|  |         else if (_is_tty_or_sess_busy(tp, p)) | ||
|  |         { | ||
|  |             rc = -EPERM; | ||
|  |         } | ||
|  |         else | ||
|  |         { | ||
|  |             /**
 | ||
|  |              * Binding controlling process | ||
|  |              * note: p->pgrp is protected by lwp lock; | ||
|  |              *       pgrp->session is always constant. | ||
|  |              */ | ||
|  |             tp->t_session = p->pgrp->session; | ||
|  |             tp->t_session->ctty = tp; | ||
|  |             tp->t_sessioncnt++; | ||
|  | 
 | ||
|  |             /* Assign foreground process group */ | ||
|  |             tp->t_pgrp = p->pgrp; | ||
|  |             p->term_ctrlterm = RT_TRUE; | ||
|  | 
 | ||
|  |             LOG_D("%s(sid=%d)", __func__, tp->t_session->sid); | ||
|  |             rc = 0; | ||
|  |         } | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  |         rc = -EPERM; | ||
|  |     } | ||
|  | 
 | ||
|  |     LWP_UNLOCK(p); | ||
|  | 
 | ||
|  |     return rc; | ||
|  | } | ||
|  | 
 | ||
|  | int lwp_tty_assign_foreground(lwp_tty_t tp, rt_thread_t td, int pgid) | ||
|  | { | ||
|  |     struct rt_processgroup *pg; | ||
|  |     rt_lwp_t cur_lwp = td->lwp; | ||
|  | 
 | ||
|  |     tty_unlock(tp); | ||
|  |     pg = lwp_pgrp_find_and_inc_ref(pgid); | ||
|  |     if (pg == NULL || cur_lwp == NULL) | ||
|  |     { | ||
|  |         tty_lock(tp); | ||
|  |         return -EPERM; | ||
|  |     } | ||
|  |     else | ||
|  |     { | ||
|  |         PGRP_LOCK(pg); | ||
|  | 
 | ||
|  |         if (pg->sid != cur_lwp->sid) | ||
|  |         { | ||
|  |             PGRP_UNLOCK(pg); | ||
|  |             lwp_pgrp_dec_ref(pg); | ||
|  |             LOG_D("%s: NoPerm current process (pid=%d, pgid=%d, sid=%d), " | ||
|  |                   "tagget group (pgid=%d, sid=%d)", __func__, | ||
|  |                   cur_lwp->pid, cur_lwp->pgid, cur_lwp->sid, pgid, pg->sid); | ||
|  |             tty_lock(tp); | ||
|  |             return -EPERM; | ||
|  |         } | ||
|  |     } | ||
|  |     tty_lock(tp); | ||
|  | 
 | ||
|  |     /**
 | ||
|  |      * Determine if this TTY is the controlling TTY after | ||
|  |      * relocking the TTY. | ||
|  |      */ | ||
|  |     if (!tty_is_ctty(tp, td->lwp)) | ||
|  |     { | ||
|  |         PGRP_UNLOCK(pg); | ||
|  |         LOG_D("%s: NoCTTY current process (pid=%d, pgid=%d, sid=%d), " | ||
|  |               "tagget group (pgid=%d, sid=%d)", __func__, | ||
|  |               cur_lwp->pid, cur_lwp->pgid, cur_lwp->sid, pgid, pg->sid); | ||
|  |         return -ENOTTY; | ||
|  |     } | ||
|  |     tp->t_pgrp = pg; | ||
|  |     PGRP_UNLOCK(pg); | ||
|  |     lwp_pgrp_dec_ref(pg); | ||
|  | 
 | ||
|  |     /* Wake up the background process groups. */ | ||
|  |     cv_broadcast(&tp->t_bgwait); | ||
|  | 
 | ||
|  |     LOG_D("%s: Foreground group %p (pgid=%d)", __func__, tp->t_pgrp, | ||
|  |           tp->t_pgrp ? tp->t_pgrp->pgid : -1); | ||
|  | 
 | ||
|  |     return 0; | ||
|  | } | ||
|  | 
 | ||
|  | /**
 | ||
|  |  * Signalling processes. | ||
|  |  */ | ||
|  | 
 | ||
|  | void lwp_tty_signal_sessleader(struct lwp_tty *tp, int sig) | ||
|  | { | ||
|  |     struct rt_lwp *p; | ||
|  |     struct rt_session *s; | ||
|  | 
 | ||
|  |     tty_assert_locked(tp); | ||
|  |     MPASS(sig >= 1 && sig < _LWP_NSIG); | ||
|  | 
 | ||
|  |     /* Make signals start output again. */ | ||
|  |     tp->t_flags &= ~TF_STOPPED; | ||
|  |     tp->t_termios.c_lflag &= ~FLUSHO; | ||
|  | 
 | ||
|  |     /**
 | ||
|  |      * Load s.leader exactly once to avoid race where s.leader is | ||
|  |      * set to NULL by a concurrent invocation of killjobc() by the | ||
|  |      * session leader.  Note that we are not holding t_session's | ||
|  |      * lock for the read. | ||
|  |      */ | ||
|  |     if ((s = tp->t_session) != NULL && | ||
|  |         (p = (void *)rt_atomic_load((rt_atomic_t *)&s->leader)) != NULL) | ||
|  |     { | ||
|  |         lwp_signal_kill(p, sig, SI_KERNEL, 0); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | void lwp_tty_signal_pgrp(struct lwp_tty *tp, int sig) | ||
|  | { | ||
|  |     tty_assert_locked(tp); | ||
|  |     MPASS(sig >= 1 && sig < _LWP_NSIG); | ||
|  | 
 | ||
|  |     /* Make signals start output again. */ | ||
|  |     tp->t_flags &= ~TF_STOPPED; | ||
|  |     tp->t_termios.c_lflag &= ~FLUSHO; | ||
|  | 
 | ||
|  | #ifdef USING_BSD_SIGINFO
 | ||
|  |     if (sig == SIGINFO && !(tp->t_termios.c_lflag & NOKERNINFO)) | ||
|  |         tty_info(tp); | ||
|  | #endif /* USING_BSD_SIGINFO */
 | ||
|  | 
 | ||
|  |     if (tp->t_pgrp != NULL) | ||
|  |     { | ||
|  |         PGRP_LOCK(tp->t_pgrp); | ||
|  |         lwp_pgrp_signal_kill(tp->t_pgrp, sig, SI_KERNEL, 0); | ||
|  |         PGRP_UNLOCK(tp->t_pgrp); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | /* bsd_ttydev_methods.d_ioctl */ | ||
|  | 
 | ||
|  | rt_inline size_t _copy_to_user(void *to, void *from, size_t n) | ||
|  | { | ||
|  |     return lwp_put_to_user(to, from, n) == n ? 0 : -EFAULT; | ||
|  | } | ||
|  | 
 | ||
|  | rt_inline size_t _copy_from_user(void *to, void *from, size_t n) | ||
|  | { | ||
|  |     return lwp_get_from_user(to, from, n) == n ? 0 : -EFAULT; | ||
|  | } | ||
|  | 
 | ||
|  | static void termios_to_termio(struct termios *tios, struct termio *tio) | ||
|  | { | ||
|  |     memset(tio, 0, sizeof(*tio)); | ||
|  |     tio->c_iflag = tios->c_iflag; | ||
|  |     tio->c_oflag = tios->c_oflag; | ||
|  |     tio->c_cflag = tios->c_cflag; | ||
|  |     tio->c_lflag = tios->c_lflag; | ||
|  |     tio->c_line = tios->c_line; | ||
|  |     memcpy(tio->c_cc, tios->c_cc, NCC); | ||
|  | } | ||
|  | 
 | ||
|  | static void termio_to_termios(struct termio *tio, struct termios *tios) | ||
|  | { | ||
|  |     int i; | ||
|  | 
 | ||
|  |     tios->c_iflag = tio->c_iflag; | ||
|  |     tios->c_oflag = tio->c_oflag; | ||
|  |     tios->c_cflag = tio->c_cflag; | ||
|  |     tios->c_lflag = tio->c_lflag; | ||
|  |     for (i = NCC; i < NCCS; i++) | ||
|  |         tios->c_cc[i] = _POSIX_VDISABLE; | ||
|  |     memcpy(tios->c_cc, tio->c_cc, NCC); | ||
|  | } | ||
|  | 
 | ||
|  | #define IOCTL(cmd, data, fflags, td) \
 | ||
|  |     bsd_ttydev_methods.d_ioctl(tp, cmd, data, fflags, td) | ||
|  | 
 | ||
|  | int lwp_tty_ioctl_adapter(lwp_tty_t tp, int cmd, int oflags, void *args, rt_thread_t td) | ||
|  | { | ||
|  |     long fflags = FFLAGS(oflags); | ||
|  |     struct termios tios; | ||
|  |     struct termio tio; | ||
|  |     int error; | ||
|  | 
 | ||
|  |     LOG_D("%s(cmd=0x%x, args=%p)", __func__, cmd, args); | ||
|  |     switch (cmd & 0xffff) | ||
|  |     { | ||
|  |         case TCGETS: | ||
|  |             error = IOCTL(TIOCGETA, (rt_caddr_t)&tios, fflags, td); | ||
|  |             if (error) | ||
|  |                 break; | ||
|  |             error = _copy_to_user(args, &tios, sizeof(tios)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case TCSETS: | ||
|  |             error = _copy_from_user(&tios, args, sizeof(tios)); | ||
|  |             if (error) | ||
|  |                 break; | ||
|  |             error = (IOCTL(TIOCSETA, (rt_caddr_t)&tios, fflags, td)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case TCSETSW: | ||
|  |             error = _copy_from_user(&tios, args, sizeof(tios)); | ||
|  |             if (error) | ||
|  |                 break; | ||
|  |             error = (IOCTL(TIOCSETAW, (rt_caddr_t)&tios, fflags, td)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case TCSETSF: | ||
|  |             error = _copy_from_user(&tios, args, sizeof(tios)); | ||
|  |             if (error) | ||
|  |                 break; | ||
|  |             error = (IOCTL(TIOCSETAF, (rt_caddr_t)&tios, fflags, td)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case TCGETA: | ||
|  |             error = IOCTL(TIOCGETA, (rt_caddr_t)&tios, fflags, td); | ||
|  |             if (error) | ||
|  |                 break; | ||
|  |             termios_to_termio(&tios, &tio); | ||
|  |             error = _copy_to_user((void *)args, &tio, sizeof(tio)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case TCSETA: | ||
|  |             error = _copy_from_user(&tio, (void *)args, sizeof(tio)); | ||
|  |             if (error) | ||
|  |                 break; | ||
|  |             termio_to_termios(&tio, &tios); | ||
|  |             error = (IOCTL(TIOCSETA, (rt_caddr_t)&tios, fflags, td)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case TCSETAW: | ||
|  |             error = _copy_from_user(&tio, (void *)args, sizeof(tio)); | ||
|  |             if (error) | ||
|  |                 break; | ||
|  |             termio_to_termios(&tio, &tios); | ||
|  |             error = (IOCTL(TIOCSETAW, (rt_caddr_t)&tios, fflags, td)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case TCSETAF: | ||
|  |             error = _copy_from_user(&tio, (void *)args, sizeof(tio)); | ||
|  |             if (error) | ||
|  |                 break; | ||
|  |             termio_to_termios(&tio, &tios); | ||
|  |             error = (IOCTL(TIOCSETAF, (rt_caddr_t)&tios, fflags, td)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case TCSBRK: | ||
|  |             if (args != 0) | ||
|  |             { | ||
|  |                 /**
 | ||
|  |                  * Linux manual: SVr4, UnixWare, Solaris, and Linux treat | ||
|  |                  * tcsendbreak(fd,arg) with nonzero arg like tcdrain(fd). | ||
|  |                  */ | ||
|  |                 error = IOCTL(TIOCDRAIN, (rt_caddr_t)&tios, fflags, td); | ||
|  |             } | ||
|  |             else | ||
|  |             { | ||
|  |                 /**
 | ||
|  |                  * Linux manual: If the terminal is using asynchronous serial | ||
|  |                  * data transmission, and arg is zero, then send a break (a | ||
|  |                  * stream of zero bits) for between 0.25 and 0.5 seconds. | ||
|  |                  */ | ||
|  |                 LOG_D("%s: ioctl TCSBRK arg 0 not implemented", __func__); | ||
|  |                 error = -ENOSYS; | ||
|  |             } | ||
|  |             break; | ||
|  | 
 | ||
|  | #ifdef USING_BSD_IOCTL_EXT
 | ||
|  |         /* Software flow control */ | ||
|  |         case TCXONC: { | ||
|  |             switch (args->arg) | ||
|  |             { | ||
|  |                 case TCOOFF: | ||
|  |                     args->cmd = TIOCSTOP; | ||
|  |                     break; | ||
|  |                 case TCOON: | ||
|  |                     args->cmd = TIOCSTART; | ||
|  |                     break; | ||
|  |                 case TCIOFF: | ||
|  |                 case TCION: { | ||
|  |                     int c; | ||
|  |                     struct write_args wr; | ||
|  |                     error = IOCTL(TIOCGETA, (rt_caddr_t)&tios, fflags, | ||
|  |                                      td); | ||
|  |                     if (error) | ||
|  |                         break; | ||
|  |                     fdrop(fp, td); | ||
|  |                     c = (args->arg == TCIOFF) ? VSTOP : VSTART; | ||
|  |                     c = tios.c_cc[c]; | ||
|  |                     if (c != _POSIX_VDISABLE) | ||
|  |                     { | ||
|  |                         wr.fd = args->fd; | ||
|  |                         wr.buf = &c; | ||
|  |                         wr.nbyte = sizeof(c); | ||
|  |                         return (sys_write(td, &wr)); | ||
|  |                     } | ||
|  |                     else | ||
|  |                         return 0; | ||
|  |                 } | ||
|  |                 default: | ||
|  |                     fdrop(fp, td); | ||
|  |                     return -EINVAL; | ||
|  |             } | ||
|  |             args->arg = 0; | ||
|  |             error = (sys_ioctl(td, (struct ioctl_args *)args)); | ||
|  |             break; | ||
|  |         } | ||
|  | #endif /* USING_BSD_IOCTL_EXT */
 | ||
|  |         case TCFLSH: { | ||
|  |             int val; | ||
|  |             error = 0; | ||
|  |             switch ((rt_base_t)args) | ||
|  |             { | ||
|  |                 case TCIFLUSH: | ||
|  |                     val = FREAD; | ||
|  |                     break; | ||
|  |                 case TCOFLUSH: | ||
|  |                     val = FWRITE; | ||
|  |                     break; | ||
|  |                 case TCIOFLUSH: | ||
|  |                     val = FREAD | FWRITE; | ||
|  |                     break; | ||
|  |                 default: | ||
|  |                     error = -EINVAL; | ||
|  |                     break; | ||
|  |             } | ||
|  |             if (!error) | ||
|  |                 error = (IOCTL(TIOCFLUSH, (rt_caddr_t)&val, fflags, td)); | ||
|  |             break; | ||
|  |         } | ||
|  | 
 | ||
|  | #ifdef USING_BSD_IOCTL_EXT
 | ||
|  |         case TIOCEXCL: | ||
|  |             args->cmd = TIOCEXCL; | ||
|  |             error = (sys_ioctl(td, (struct ioctl_args *)args)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case TIOCNXCL: | ||
|  |             args->cmd = TIOCNXCL; | ||
|  |             error = (sys_ioctl(td, (struct ioctl_args *)args)); | ||
|  |             break; | ||
|  | #endif /* USING_BSD_IOCTL_EXT */
 | ||
|  | 
 | ||
|  |         /* Controlling terminal */ | ||
|  |         case TIOCSCTTY: | ||
|  |         case TIOCNOTTY: | ||
|  | 
 | ||
|  |         /* Process group and session ID */ | ||
|  |         case TIOCGPGRP: | ||
|  |         case TIOCSPGRP: | ||
|  |         case TIOCGSID: | ||
|  | 
 | ||
|  |             /* TIOCOUTQ */ | ||
|  |             /* TIOCSTI */ | ||
|  |         case TIOCGWINSZ: | ||
|  |         case TIOCSWINSZ: | ||
|  |             error = IOCTL(cmd, (rt_caddr_t)args, fflags, td); | ||
|  |             break; | ||
|  | #ifdef USING_BSD_IOCTL_EXT
 | ||
|  |         case TIOCMGET: | ||
|  |             args->cmd = TIOCMGET; | ||
|  |             error = (sys_ioctl(td, (struct ioctl_args *)args)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case TIOCMBIS: | ||
|  |             args->cmd = TIOCMBIS; | ||
|  |             error = (sys_ioctl(td, (struct ioctl_args *)args)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case TIOCMBIC: | ||
|  |             args->cmd = TIOCMBIC; | ||
|  |             error = (sys_ioctl(td, (struct ioctl_args *)args)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case TIOCMSET: | ||
|  |             args->cmd = TIOCMSET; | ||
|  |             error = (sys_ioctl(td, (struct ioctl_args *)args)); | ||
|  |             break; | ||
|  | 
 | ||
|  |             /* TIOCGSOFTCAR */ | ||
|  |             /* TIOCSSOFTCAR */ | ||
|  | 
 | ||
|  |         case FIONREAD: /* TIOCINQ */ | ||
|  |             args->cmd = FIONREAD; | ||
|  |             error = (sys_ioctl(td, (struct ioctl_args *)args)); | ||
|  |             break; | ||
|  | 
 | ||
|  |             /* TIOCLINUX */ | ||
|  | 
 | ||
|  |         case TIOCCONS: | ||
|  |             args->cmd = TIOCCONS; | ||
|  |             error = (sys_ioctl(td, (struct ioctl_args *)args)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case TIOCGSERIAL: { | ||
|  |             struct linux_serial_struct lss; | ||
|  | 
 | ||
|  |             bzero(&lss, sizeof(lss)); | ||
|  |             lss.type = PORT_16550A; | ||
|  |             lss.flags = 0; | ||
|  |             lss.close_delay = 0; | ||
|  |             error = copyout(&lss, (void *)args->arg, sizeof(lss)); | ||
|  |             break; | ||
|  |         } | ||
|  | 
 | ||
|  |         case TIOCSSERIAL: { | ||
|  |             struct linux_serial_struct lss; | ||
|  |             error = copyin((void *)args->arg, &lss, sizeof(lss)); | ||
|  |             if (error) | ||
|  |                 break; | ||
|  |             /* XXX - It really helps to have an implementation that
 | ||
|  |              * does nothing. NOT! | ||
|  |              */ | ||
|  |             error = 0; | ||
|  |             break; | ||
|  |         } | ||
|  | 
 | ||
|  |         case TIOCPKT: | ||
|  |             args->cmd = TIOCPKT; | ||
|  |             error = (sys_ioctl(td, (struct ioctl_args *)args)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case FIONBIO: | ||
|  |             args->cmd = FIONBIO; | ||
|  |             error = (sys_ioctl(td, (struct ioctl_args *)args)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case TIOCSETD: { | ||
|  |             int line; | ||
|  |             switch (args->arg) | ||
|  |             { | ||
|  |                 case N_TTY: | ||
|  |                     line = TTYDISC; | ||
|  |                     break; | ||
|  |                 case N_SLIP: | ||
|  |                     line = SLIPDISC; | ||
|  |                     break; | ||
|  |                 case N_PPP: | ||
|  |                     line = PPPDISC; | ||
|  |                     break; | ||
|  |                 default: | ||
|  |                     fdrop(fp, td); | ||
|  |                     return -EINVAL; | ||
|  |             } | ||
|  |             error = (ioctl_emit(TIOCSETD, (rt_caddr_t)&line, fflags, td)); | ||
|  |             break; | ||
|  |         } | ||
|  | 
 | ||
|  |         case TIOCGETD: { | ||
|  |             int linux_line; | ||
|  |             int bsd_line = TTYDISC; | ||
|  |             error = | ||
|  |                 ioctl_emit(TIOCGETD, (rt_caddr_t)&bsd_line, fflags, td); | ||
|  |             if (error) | ||
|  |                 break; | ||
|  |             switch (bsd_line) | ||
|  |             { | ||
|  |                 case TTYDISC: | ||
|  |                     linux_line = N_TTY; | ||
|  |                     break; | ||
|  |                 case SLIPDISC: | ||
|  |                     linux_line = N_SLIP; | ||
|  |                     break; | ||
|  |                 case PPPDISC: | ||
|  |                     linux_line = N_PPP; | ||
|  |                     break; | ||
|  |                 default: | ||
|  |                     fdrop(fp, td); | ||
|  |                     return -EINVAL; | ||
|  |             } | ||
|  |             error = (copyout(&linux_line, (void *)args->arg, sizeof(int))); | ||
|  |             break; | ||
|  |         } | ||
|  | 
 | ||
|  |             /* TCSBRKP */ | ||
|  |             /* TIOCTTYGSTRUCT */ | ||
|  | 
 | ||
|  |         case FIONCLEX: | ||
|  |             args->cmd = FIONCLEX; | ||
|  |             error = (sys_ioctl(td, (struct ioctl_args *)args)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case FIOCLEX: | ||
|  |             args->cmd = FIOCLEX; | ||
|  |             error = (sys_ioctl(td, (struct ioctl_args *)args)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case FIOASYNC: | ||
|  |             args->cmd = FIOASYNC; | ||
|  |             error = (sys_ioctl(td, (struct ioctl_args *)args)); | ||
|  |             break; | ||
|  | 
 | ||
|  |             /* TIOCSERCONFIG */ | ||
|  |             /* TIOCSERGWILD */ | ||
|  |             /* TIOCSERSWILD */ | ||
|  |             /* TIOCGLCKTRMIOS */ | ||
|  |             /* TIOCSLCKTRMIOS */ | ||
|  | 
 | ||
|  |         case TIOCSBRK: | ||
|  |             args->cmd = TIOCSBRK; | ||
|  |             error = (sys_ioctl(td, (struct ioctl_args *)args)); | ||
|  |             break; | ||
|  | 
 | ||
|  |         case TIOCCBRK: | ||
|  |             args->cmd = TIOCCBRK; | ||
|  |             error = (sys_ioctl(td, (struct ioctl_args *)args)); | ||
|  |             break; | ||
|  |         case TIOCGPTN: { | ||
|  |             int nb; | ||
|  | 
 | ||
|  |             error = ioctl_emit(TIOCGPTN, (rt_caddr_t)&nb, fflags, td); | ||
|  |             if (!error) | ||
|  |                 error = copyout(&nb, (void *)args->arg, sizeof(int)); | ||
|  |             break; | ||
|  |         } | ||
|  |         case TIOCGPTPEER: | ||
|  |             linux_msg(td, "unsupported ioctl TIOCGPTPEER"); | ||
|  |             error = -ENOIOCTL; | ||
|  |             break; | ||
|  |         case TIOCSPTLCK: | ||
|  |             /*
 | ||
|  |              * Our unlockpt() does nothing. Check that fd refers | ||
|  |              * to a pseudo-terminal master device. | ||
|  |              */ | ||
|  |             args->cmd = TIOCPTMASTER; | ||
|  |             error = (sys_ioctl(td, (struct ioctl_args *)args)); | ||
|  |             break; | ||
|  | #endif /* USING_BSD_IOCTL_EXT */
 | ||
|  | 
 | ||
|  |         /**
 | ||
|  |          * those are for current implementation of devfs, and we dont want to | ||
|  |          * log them | ||
|  |          */ | ||
|  |         case F_DUPFD: | ||
|  |         case F_DUPFD_CLOEXEC: | ||
|  |         case F_GETFD: | ||
|  |         case F_SETFD: | ||
|  |         case F_GETFL: | ||
|  |         case F_SETFL: | ||
|  |             /* fall back to fs */ | ||
|  |             error = -ENOIOCTL; | ||
|  |             break; | ||
|  |         default: | ||
|  |             LOG_I("%s: unhandle commands 0x%x", __func__, cmd); | ||
|  |             error = -ENOSYS; | ||
|  |             break; | ||
|  |     } | ||
|  | 
 | ||
|  |     return (error); | ||
|  | } |