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);
|
||
|
}
|