712 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			712 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
|  * 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);
 | |
| }
 |