This commit is contained in:
2024-08-05 20:57:09 +08:00
commit 46d9ee7795
3020 changed files with 1725767 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
menuconfig LWP_USING_TERMINAL
bool "Terminal I/O Subsystem"
depends on RT_USING_SMART
default y
if LWP_USING_TERMINAL
config LWP_PTY_MAX_PARIS_LIMIT
int "Max number of pty devices registered at the same time"
default 64
help
This upper limit is set to protect kernel memory from draining
out by the application if it keeps allocating pty devices.
endif

View File

@@ -0,0 +1,741 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-11-13 Shell Add compatible layer for FreeBSD
*/
#ifndef __LWP_TTY_BSD_PORTING_H__
#define __LWP_TTY_BSD_PORTING_H__
#include <rtthread.h>
#include <lwp_internal.h>
#define _KERNEL
#ifndef __unused
#define __unused __attribute__((__unused__))
#endif
/* functionability of bsd tty layer */
#if 0
#define USING_BSD_HOOK
#endif
/* Only for devfs d_close() flags. */
#define FLASTCLOSE O_DIRECTORY
#define FREVOKE 0x00200000
/*
* Output flags - software output processing
*/
#if !((OPOST | OLCUC | ONLCR) & 0x8)
#define ONOEOT 0x0008 /* discard EOT's (^D) on output) */
#endif
/*
* Kernel encoding of open mode; separate read and write bits that are
* independently testable: 1 greater than the above.
*
* XXX
* FREAD and FWRITE are excluded from the #ifdef _KERNEL so that TIOCFLUSH,
* which was documented to use FREAD/FWRITE, continues to work.
*/
#define FREAD 0x0001
#define FWRITE 0x0002
/*
* Flags to memory allocation functions.
*/
#define M_NOWAIT 0x0001 /* do not block */
#define M_WAITOK 0x0002 /* ok to block */
#define M_NORECLAIM 0x0080 /* do not reclaim after failure */
#define M_ZERO 0x0100 /* bzero the allocation */
#define M_NOVM 0x0200 /* don't ask VM for pages */
#define M_USE_RESERVE 0x0400 /* can alloc out of reserve memory */
#define M_NODUMP 0x0800 /* don't dump pages in this allocation */
#define M_FIRSTFIT 0x1000 /* only for vmem, fast fit */
#define M_BESTFIT 0x2000 /* only for vmem, low fragmentation */
#define M_EXEC 0x4000 /* allocate executable space */
#define M_NEXTFIT 0x8000 /* only for vmem, follow cursor */
#define M_VERSION 2020110501
/*
* The INVARIANTS-enabled mtx_assert() functionality.
*
* The constants need to be defined for INVARIANT_SUPPORT infrastructure
* support as _mtx_assert() itself uses them and the latter implies that
* _mtx_assert() must build.
*/
#define MA_OWNED (1)
#define MA_NOTOWNED (2)
#define MA_RECURSED (4)
#define MA_NOTRECURSED (8)
/*
* Indentification of modem control signals. These definitions match
* the TIOCMGET definitions in <sys/ttycom.h> shifted a bit down, and
* that identity is enforced with CTASSERT at the bottom of kern/tty.c
* Both the modem bits and delta bits must fit in 16 bit.
*/
#define SER_DTR 0x0001 /* data terminal ready */
#define SER_RTS 0x0002 /* request to send */
#define SER_STX 0x0004 /* secondary transmit */
#define SER_SRX 0x0008 /* secondary receive */
#define SER_CTS 0x0010 /* clear to send */
#define SER_DCD 0x0020 /* data carrier detect */
#define SER_RI 0x0040 /* ring indicate */
#define SER_DSR 0x0080 /* data set ready */
#define SER_MASK_STATE 0x00ff
/*
* Flags for ioflag. (high 16 bits used to ask for read-ahead and
* help with write clustering)
* NB: IO_NDELAY and IO_DIRECT are linked to fcntl.h
*/
#if 0
#define IO_UNIT 0x0001 /* do I/O as atomic unit */
#define IO_APPEND 0x0002 /* append write to end */
#endif /* not porting */
#define IO_NDELAY 0x0004 /* FNDELAY flag set in file table */
#if 0
#define IO_NODELOCKED 0x0008 /* underlying node already locked */
#define IO_ASYNC 0x0010 /* bawrite rather then bdwrite */
#define IO_VMIO 0x0020 /* data already in VMIO space */
#define IO_INVAL 0x0040 /* invalidate after I/O */
#define IO_SYNC 0x0080 /* do I/O synchronously */
#define IO_DIRECT 0x0100 /* attempt to bypass buffer cache */
#define IO_NOREUSE 0x0200 /* VMIO data won't be reused */
#define IO_EXT 0x0400 /* operate on external attributes */
#define IO_NORMAL 0x0800 /* operate on regular data */
#define IO_NOMACCHECK 0x1000 /* MAC checks unnecessary */
#define IO_BUFLOCKED 0x2000 /* ffs flag; indir buf is locked */
#define IO_RANGELOCKED 0x4000 /* range locked */
#define IO_DATASYNC 0x8000 /* do only data I/O synchronously */
#define IO_SEQMAX 0x7F /* seq heuristic max value */
#define IO_SEQSHIFT 16 /* seq heuristic in upper 16 bits */
#endif /* not porting */
/** Used to distinguish between normal, callout, lock and init devices.
* Note: this is not used in smart system.
*/
#define TTYUNIT_INIT 0x1
#define TTYUNIT_LOCK 0x2
#define TTYUNIT_CALLOUT 0x4
/*
* TTY privileges.
*/
#define PRIV_TTY_CONSOLE 250 /* Set console to tty. */
#define PRIV_TTY_DRAINWAIT 251 /* Set tty drain wait time. */
#define PRIV_TTY_DTRWAIT 252 /* Set DTR wait on tty. */
#define PRIV_TTY_EXCLUSIVE 253 /* Override tty exclusive flag. */
#define _PRIV_TTY_PRISON 254 /* Removed. */
#define PRIV_TTY_STI 255 /* Simulate input on another tty. */
#define PRIV_TTY_SETA 256 /* Set tty termios structure. */
#define MPASS(ex) RT_ASSERT(ex)
#if !defined(MIN)
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#if !defined(MAX)
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
#define curthread rt_thread_self()
#ifdef USING_BSD_HOOK
#define ttyhook_hashook(tp, hook) \
((tp)->t_hook != NULL && (tp)->t_hook->th_##hook != NULL)
#else
#define ttyhook_hashook(tp, hook) (RT_FALSE)
#endif
/* condvar API */
#include <rtdevice.h>
#define cv_init(cvp, name) rt_condvar_init(cvp, name)
#define cv_destroy(cvp) rt_condvar_detach(cvp)
#define cv_wait(cvp, mp) \
rt_condvar_timedwait(cvp, mp, RT_KILLABLE, RT_WAITING_FOREVER)
#define cv_wait_sig(cvp, mp) \
rt_condvar_timedwait(cvp, mp, RT_INTERRUPTIBLE, RT_WAITING_FOREVER)
#define cv_signal(cvp) rt_condvar_signal(cvp)
#define cv_broadcast(cvp) rt_condvar_broadcast(cvp)
#define cv_timedwait(cvp, mp, t) rt_condvar_timedwait(cvp, mp, RT_KILLABLE, t)
#define cv_timedwait_sig(cvp, mp, t) \
rt_condvar_timedwait(cvp, mp, RT_INTERRUPTIBLE, t)
struct lwp_tty;
struct uio;
/* TODO: just a place holder since devfs is not capable of doing this currently
*/
struct file
{
};
typedef rt_base_t sbintime_t;
typedef rt_ubase_t vm_offset_t;
typedef rt_base_t vm_ooffset_t;
typedef rt_ubase_t vm_paddr_t;
typedef rt_ubase_t vm_pindex_t;
typedef rt_ubase_t vm_size_t;
typedef char *rt_caddr_t;
/*
* The exact set of memory attributes is machine dependent. However,
* every machine is required to define VM_MEMATTR_DEFAULT and
* VM_MEMATTR_UNCACHEABLE.
*/
typedef char vm_memattr_t; /* memory attribute codes */
typedef int d_open_t(struct lwp_tty *tp, int oflags, int devtype,
struct rt_thread *td);
typedef int d_fdopen_t(struct lwp_tty *tp, int oflags, struct rt_thread *td,
struct file *fp);
typedef int d_close_t(struct lwp_tty *tp, int fflag, int devtype,
struct rt_thread *td);
#ifdef USING_BSD_DEVICE_STRATEGY
typedef void d_strategy_t(struct bio *bp);
#endif
typedef int d_ioctl_t(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data,
int fflag, struct rt_thread *td);
typedef int d_read_t(struct lwp_tty *tp, struct uio *uio, int ioflag);
typedef int d_write_t(struct lwp_tty *tp, struct uio *uio, int ioflag);
typedef int d_poll_t(struct lwp_tty *tp, rt_pollreq_t *req,
struct rt_thread *td);
#ifdef USING_BSD_KNOTE
typedef int d_kqfilter_t(struct lwp_tty *tp, struct knote *kn);
#endif /* USING_BSD_KNOTE */
typedef int d_mmap_t(struct lwp_tty *tp, vm_ooffset_t offset, vm_paddr_t *paddr,
int nprot, vm_memattr_t *memattr);
#ifdef USING_BSD_MMAP_SINGLE
typedef int d_mmap_single_t(struct cdev *cdev, vm_ooffset_t *offset,
vm_size_t size, struct vm_object **object,
int nprot);
#endif /* USING_BSD_MMAP_SINGLE */
typedef void d_purge_t(struct lwp_tty *tp);
/*
* Character device switch table
*/
struct cdevsw
{
#ifdef USING_BSD_RAW_CDEVSW
int d_version;
u_int d_flags;
const char *d_name;
#endif /* USING_BSD_RAW_CDEVSW */
d_open_t *d_open;
d_fdopen_t *d_fdopen;
d_close_t *d_close;
d_read_t *d_read;
d_write_t *d_write;
d_ioctl_t *d_ioctl;
d_poll_t *d_poll;
d_mmap_t *d_mmap;
#ifdef USING_BSD_DEVICE_STRATEGY
d_strategy_t *d_strategy;
#endif /* USING_BSD_DEVICE_STRATEGY */
#ifdef USING_BSD_RAW_CDEVSW
void *d_spare0;
d_kqfilter_t *d_kqfilter;
d_purge_t *d_purge;
d_mmap_single_t *d_mmap_single;
int32_t d_spare1[3];
void *d_spare2[3];
/* These fields should not be messed with by drivers */
LIST_HEAD(, cdev) d_devs;
int d_spare3;
union
{
struct cdevsw *gianttrick;
SLIST_ENTRY(cdevsw) postfree_list;
} __d_giant;
#endif
};
struct iovec
{
void *iov_base; /* Base address. */
size_t iov_len; /* Length. */
};
enum uio_rw
{
UIO_READ,
UIO_WRITE
};
struct uio
{
struct iovec *uio_iov; /* scatter/gather list */
int uio_iovcnt; /* length of scatter/gather list */
off_t uio_offset; /* offset in target object */
ssize_t uio_resid; /* remaining bytes to process */
#ifdef USING_BSD_UIO
enum uio_seg uio_segflg; /* address space */
#endif
enum uio_rw uio_rw; /* operation */
#ifdef USING_BSD_UIO
struct rt_thread *uio_td; /* owner */
#endif /* USING_BSD_UIO */
};
#include <lwp_user_mm.h>
rt_inline int uiomove(void *operand, int n, struct uio *uio)
{
switch (uio->uio_rw)
{
case UIO_READ:
memcpy(uio->uio_iov->iov_base, operand, n);
break;
case UIO_WRITE:
memcpy(operand, uio->uio_iov->iov_base, n);
break;
default:
return -1;
}
uio->uio_iov->iov_base += n;
uio->uio_iov->iov_len--;
uio->uio_offset += n;
uio->uio_resid -= n;
return 0;
}
/* privileges checking: 0 if okay */
rt_inline int priv_check(struct rt_thread *td, int priv)
{
return 0;
}
/* Disable console redirection to a tty. */
rt_inline int constty_clear(struct lwp_tty *tp)
{
// rt_kprintf("\nTODO: %s unimplemented!\n", __func__);
return 0;
}
rt_inline int constty_set(struct lwp_tty *tp)
{
// rt_kprintf("\nTODO: %s unimplemented!\n", __func__);
return 0;
}
/**
* UMA (Universal Memory Allocator)
*/
#define UMA_ALIGN_PTR (sizeof(void *) - 1) /* Alignment fit for ptr */
typedef int (*uma_ctor)(void *mem, int size, void *arg, int flags);
typedef void (*uma_dtor)(void *mem, int size, void *arg);
typedef int (*uma_init)(void *mem, int size, int flags);
typedef void (*uma_fini)(void *mem, int size);
struct uma_zone
{
char *name;
int align;
int size;
};
/* Opaque type used as a handle to the zone */
typedef struct uma_zone *uma_zone_t;
rt_inline uma_zone_t uma_zcreate(char *name, int size, uma_ctor ctor,
uma_dtor dtor, uma_init zinit, uma_fini zfini,
int align, uint16_t flags)
{
uma_zone_t zone = rt_malloc(sizeof(struct uma_zone));
if (zone)
{
RT_ASSERT(ctor == RT_NULL);
RT_ASSERT(dtor == RT_NULL);
RT_ASSERT(zinit == RT_NULL);
RT_ASSERT(zfini == RT_NULL);
zone->size = size;
zone->name = name;
zone->align = align;
}
return zone;
}
rt_inline void *uma_zalloc(uma_zone_t zone, int flags)
{
void *buf = rt_malloc_align(zone->size, zone->align + 1);
if (buf)
rt_memset(buf, 0, sizeof(zone->size));
return buf;
}
rt_inline void uma_zfree(uma_zone_t zone, void *item)
{
rt_free_align(item);
}
/**
* bsd type of speed to linux type.
* Note: with switch blocks, compiler can generate the optimized version for us
*/
#include <termios.h>
rt_inline long bsd_speed_to_integer(speed_t speed)
{
long speed_value;
switch (speed)
{
case B0:
speed_value = 0;
break;
case B50:
speed_value = 50;
break;
case B75:
speed_value = 75;
break;
case B110:
speed_value = 110;
break;
case B134:
speed_value = 134;
break;
case B150:
speed_value = 150;
break;
case B200:
speed_value = 200;
break;
case B300:
speed_value = 300;
break;
case B600:
speed_value = 600;
break;
case B1200:
speed_value = 1200;
break;
case B1800:
speed_value = 1800;
break;
case B2400:
speed_value = 2400;
break;
case B4800:
speed_value = 4800;
break;
case B9600:
speed_value = 9600;
break;
case B19200:
speed_value = 19200;
break;
case B38400:
speed_value = 38400;
break;
case B57600:
speed_value = 57600;
break;
case B115200:
speed_value = 115200;
break;
case B230400:
speed_value = 230400;
break;
case B460800:
speed_value = 460800;
break;
case B500000:
speed_value = 500000;
break;
case B576000:
speed_value = 576000;
break;
case B921600:
speed_value = 921600;
break;
case B1000000:
speed_value = 1000000;
break;
case B1152000:
speed_value = 1152000;
break;
case B1500000:
speed_value = 1500000;
break;
case B2000000:
speed_value = 2000000;
break;
case B2500000:
speed_value = 2500000;
break;
case B3000000:
speed_value = 3000000;
break;
case B3500000:
speed_value = 3500000;
break;
case B4000000:
speed_value = 4000000;
break;
default:
speed_value = -1; // invalid speed
break;
}
return speed_value;
}
/* time.h */
/* Operations on timevals. */
#define timevalclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0)
#define timevalisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
#define timevalcmp(tvp, uvp, cmp) \
(((tvp)->tv_sec == (uvp)->tv_sec) ? ((tvp)->tv_usec cmp(uvp)->tv_usec) \
: ((tvp)->tv_sec cmp(uvp)->tv_sec))
rt_inline void getmicrotime(struct timeval *now)
{
gettimeofday(now, RT_NULL);
}
rt_inline void timevalfix(struct timeval *tv)
{
if (tv->tv_usec < 0)
{
tv->tv_sec--;
tv->tv_usec += 1000000;
}
if (tv->tv_usec >= 1000000)
{
tv->tv_sec++;
tv->tv_usec -= 1000000;
}
}
rt_inline void timevaladd(struct timeval *op1, const struct timeval *op2)
{
op1->tv_sec += op2->tv_sec;
op1->tv_usec += op2->tv_usec;
timevalfix(op1);
}
rt_inline void timevalsub(struct timeval *op1, const struct timeval *op2)
{
op1->tv_sec -= op2->tv_sec;
op1->tv_usec -= op2->tv_usec;
timevalfix(op1);
}
rt_inline rt_tick_t tvtohz(struct timeval *tv)
{
rt_tick_t rc;
rc = tv->tv_sec * RT_TICK_PER_SECOND;
rc += tv->tv_usec * RT_TICK_PER_SECOND / MICROSECOND_PER_SECOND;
return rc;
}
/* ioctl */
#define _BSD_TIOCTL(val) ((val) << 16)
enum bsd_ioctl_cmd
{
BSD_TIOCDRAIN = 1,
BSD_TIOCFLUSH,
BSD_TIOCSTART,
BSD_TIOCSTOP,
BSD_TIOCSTAT,
BSD_TIOCGDRAINWAIT,
BSD_TIOCSDRAINWAIT,
BSD_TIOCSDTR,
BSD_TIOCCDTR,
};
#ifndef TIOCGETA /* get termios struct */
#define TIOCGETA TCGETS
#endif
#ifndef TIOCSETA /* set termios struct */
#define TIOCSETA TCSETS
#endif
#ifndef TIOCSETAW /* drain output, set */
#define TIOCSETAW TCSETSW
#endif
#ifndef TIOCSETAF /* drn out, fls in, set */
#define TIOCSETAF TCSETSF
#endif
#ifndef TIOCDRAIN /* wait till output drained */
#define TIOCDRAIN _BSD_TIOCTL(BSD_TIOCDRAIN)
#endif
#ifndef TIOCFLUSH /* flush buffers */
#define TIOCFLUSH _BSD_TIOCTL(BSD_TIOCFLUSH)
#endif
#ifndef TIOCSTART /* start output, like ^Q */
#define TIOCSTART _BSD_TIOCTL(BSD_TIOCSTART)
#endif
#ifndef TIOCSTOP /* stop output, like ^S */
#define TIOCSTOP _BSD_TIOCTL(BSD_TIOCSTOP)
#endif
#ifndef TIOCSTAT /* simulate ^T status message */
#define TIOCSTAT _BSD_TIOCTL(BSD_TIOCSTAT)
#endif
#ifndef TIOCGDRAINWAIT /* get ttywait timeout */
#define TIOCGDRAINWAIT _BSD_TIOCTL(BSD_TIOCGDRAINWAIT)
#endif
#ifndef TIOCSDRAINWAIT /* set ttywait timeout */
#define TIOCSDRAINWAIT _BSD_TIOCTL(BSD_TIOCSDRAINWAIT)
#endif
#ifndef TIOCSDTR /* set data terminal ready */
#define TIOCSDTR _BSD_TIOCTL(BSD_TIOCSDTR)
#endif
#ifndef TIOCCDTR /* clear data terminal ready */
#define TIOCCDTR _BSD_TIOCTL(BSD_TIOCCDTR)
#endif
#define ENOIOCTL ENOSYS
#define NO_PID -1
/* line discipline */
#define TTYDISC 0 /* termios tty line discipline */
#define SLIPDISC 4 /* serial IP discipline */
#define PPPDISC 5 /* PPP discipline */
#define NETGRAPHDISC 6 /* Netgraph tty node discipline */
#define H4DISC 7 /* Netgraph Bluetooth H4 discipline */
/*
* Control flags - hardware control of terminal
*/
#if __BSD_VISIBLE
#define CIGNORE 0x00000001 /* ignore control flags */
#define CCTS_OFLOW 0x00010000 /* CTS flow control of output */
#define CRTSCTS (CCTS_OFLOW | CRTS_IFLOW)
#define CRTS_IFLOW 0x00020000 /* RTS flow control of input */
#define CDTR_IFLOW 0x00040000 /* DTR flow control of input */
#define CDSR_OFLOW 0x00080000 /* DSR flow control of output */
#define CCAR_OFLOW 0x00100000 /* DCD flow control of output */
#define CNO_RTSDTR 0x00200000 /* Do not assert RTS or DTR automatically */
#else
#define CIGNORE 0 /* ignore control flags */
#define CCTS_OFLOW 0 /* CTS flow control of output */
#define CRTS_IFLOW 0 /* RTS flow control of input */
#define CDTR_IFLOW 0 /* DTR flow control of input */
#define CDSR_OFLOW 0 /* DSR flow control of output */
#define CCAR_OFLOW 0 /* DCD flow control of output */
#define CNO_RTSDTR 0 /* Do not assert RTS or DTR automatically */
#endif
#ifndef CRTSCTS
#define CRTSCTS (CCTS_OFLOW | CRTS_IFLOW)
#endif
#ifndef howmany
#define howmany(x, y) (((x) + ((y)-1)) / (y))
#endif
struct ucred
{
};
#define NOCRED ((struct ucred *)0) /* no credential available */
#define FSCRED ((struct ucred *)-1) /* filesystem credential */
/* convert from open() flags to/from fflags; convert O_RD/WR to FREAD/FWRITE */
#include <fcntl.h>
#define FFLAGS(oflags) ((oflags)&O_EXEC ? (oflags) : (oflags) + 1)
#define OFLAGS(fflags) \
(((fflags) & (O_EXEC | O_PATH)) != 0 ? (fflags) : (fflags)-1)
typedef int fo_rdwr_t(struct lwp_tty *tp, struct uio *uio,
struct ucred *active_cred, int flags,
struct rt_thread *td);
typedef int fo_truncate_t(struct lwp_tty *tp, off_t length,
struct ucred *active_cred, struct rt_thread *td);
typedef int fo_ioctl_t(struct lwp_tty *tp, rt_ubase_t com, void *data,
struct ucred *active_cred, int fflags, struct rt_thread *td);
typedef int fo_poll_t(struct lwp_tty *tp, struct rt_pollreq *rq, struct ucred *active_cred,
struct rt_thread *td);
typedef int fo_stat_t(struct lwp_tty *tp, struct stat *sb,
struct ucred *active_cred);
typedef int fo_close_t(struct lwp_tty *tp, struct rt_thread *td);
#ifdef USING_BSD_FO_EXT
typedef int fo_chmod_t(struct file *fp, mode_t mode, struct ucred *active_cred,
struct rt_thread *td);
typedef int fo_chown_t(struct file *fp, uid_t uid, gid_t gid,
struct ucred *active_cred, struct rt_thread *td);
typedef int fo_sendfile_t(struct file *fp, int sockfd, struct uio *hdr_uio,
struct uio *trl_uio, off_t offset, size_t nbytes,
off_t *sent, int flags, struct rt_thread *td);
typedef int fo_seek_t(struct file *fp, off_t offset, int whence,
struct rt_thread *td);
typedef int fo_kqfilter_t(struct file *fp, struct knote *kn);
typedef int fo_fill_kinfo_t(struct file *fp, struct kinfo_file *kif,
struct filedesc *fdp);
typedef int fo_mmap_t(struct file *fp, vm_map_t map, vm_offset_t *addr,
vm_size_t size, vm_prot_t prot, vm_prot_t cap_maxprot,
int flags, vm_ooffset_t foff, struct rt_thread *td);
typedef int fo_aio_queue_t(struct file *fp, struct kaiocb *job);
typedef int fo_add_seals_t(struct file *fp, int flags);
typedef int fo_get_seals_t(struct file *fp, int *flags);
typedef int fo_fallocate_t(struct file *fp, off_t offset, off_t len,
struct rt_thread *td);
typedef int fo_fspacectl_t(struct file *fp, int cmd, off_t *offset,
off_t *length, int flags, struct ucred *active_cred,
struct rt_thread *td);
typedef int fo_spare_t(struct file *fp);
#endif /* USING_BSD_FO_EXT */
typedef int fo_flags_t;
struct bsd_fileops
{
fo_rdwr_t *fo_read;
fo_rdwr_t *fo_write;
fo_truncate_t *fo_truncate;
fo_ioctl_t *fo_ioctl;
fo_poll_t *fo_poll;
fo_stat_t *fo_stat;
fo_close_t *fo_close;
#ifdef USING_BSD_FO_EXT
fo_chmod_t *fo_chmod;
fo_chown_t *fo_chown;
fo_sendfile_t *fo_sendfile;
fo_seek_t *fo_seek;
fo_kqfilter_t *fo_kqfilter;
fo_fill_kinfo_t *fo_fill_kinfo;
fo_mmap_t *fo_mmap;
fo_aio_queue_t *fo_aio_queue;
fo_add_seals_t *fo_add_seals;
fo_get_seals_t *fo_get_seals;
fo_fallocate_t *fo_fallocate;
fo_fspacectl_t *fo_fspacectl;
fo_spare_t *fo_spares[8]; /* Spare slots */
#endif
fo_flags_t fo_flags; /* DFLAG_* below */
};
#define DFLAG_PASSABLE 0x01 /* may be passed via unix sockets. */
#define DFLAG_SEEKABLE 0x02 /* seekable / nonsequential */
#endif /* __LWP_TTY_BSD_PORTING_H__ */

View File

@@ -0,0 +1,81 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-11-13 Shell init ver.
*/
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
* All rights reserved.
*
* Portions of this software were developed under sponsorship from Snow
* B.V., the Netherlands.
*
* 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.
*/
#ifndef _SYS_TTYDISC_H_
#define _SYS_TTYDISC_H_
#ifndef __LWP_TERMINAL_H__
#error "can only be included through <terminal.h>"
#endif /* !__LWP_TERMINAL_H__ */
#include <rtdef.h>
struct rt_wqueue;
struct rt_thread;
struct lwp_tty;
struct uio;
/* Top half routines. */
void ttydisc_open(struct lwp_tty *tp);
void ttydisc_close(struct lwp_tty *tp);
int ttydisc_read(struct lwp_tty *tp, struct uio *uio, int ioflag);
int ttydisc_write(struct lwp_tty *tp, struct uio *uio, int ioflag);
void ttydisc_optimize(struct lwp_tty *tp);
/* Bottom half routines. */
void ttydisc_modem(struct lwp_tty *tp, int open);
#define ttydisc_can_bypass(tp) ((tp)->t_flags & TF_BYPASS)
int ttydisc_rint(struct lwp_tty *tp, char c, int flags);
size_t ttydisc_rint_simple(struct lwp_tty *tp, const void *buf, size_t len);
size_t ttydisc_rint_bypass(struct lwp_tty *tp, const void *buf, size_t len);
void ttydisc_rint_done(struct lwp_tty *tp);
size_t ttydisc_rint_poll(struct lwp_tty *tp);
size_t ttydisc_getc(struct lwp_tty *tp, void *buf, size_t len);
int ttydisc_getc_uio(struct lwp_tty *tp, struct uio *uio);
size_t ttydisc_getc_poll(struct lwp_tty *tp);
/* Error codes for ttydisc_rint(). */
#define TRE_FRAMING 0x01
#define TRE_PARITY 0x02
#define TRE_OVERRUN 0x04
#define TRE_BREAK 0x08
#endif /* !_SYS_TTYDISC_H_ */

View File

@@ -0,0 +1,180 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-11-13 Shell init ver.
*/
#include "bsd_porting.h"
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
* All rights reserved.
*
* Portions of this software were developed under sponsorship from Snow
* B.V., the Netherlands.
*
* 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.
*/
#ifndef _SYS_TTYQUEUE_H_
#define _SYS_TTYQUEUE_H_
#ifndef __LWP_TERMINAL_H__
#error "can only be included through <teminal.h>"
#endif /* !__LWP_TERMINAL_H__ */
struct lwp_tty;
struct ttyinq_block;
struct ttyoutq_block;
struct uio;
/* Data input queue. */
struct ttyinq
{
struct ttyinq_block *ti_firstblock;
struct ttyinq_block *ti_startblock;
struct ttyinq_block *ti_reprintblock;
struct ttyinq_block *ti_lastblock;
unsigned int ti_begin;
unsigned int ti_linestart;
unsigned int ti_reprint;
unsigned int ti_end;
unsigned int ti_nblocks;
unsigned int ti_quota;
};
#define TTYINQ_DATASIZE 128
/* Data output queue. */
struct ttyoutq
{
struct ttyoutq_block *to_firstblock;
struct ttyoutq_block *to_lastblock;
unsigned int to_begin;
unsigned int to_end;
unsigned int to_nblocks;
unsigned int to_quota;
};
#define TTYOUTQ_DATASIZE (256 - sizeof(struct ttyoutq_block *))
/* Input queue handling routines. */
int ttyinq_setsize(struct ttyinq *ti, struct lwp_tty *tp, size_t len);
void ttyinq_free(struct ttyinq *ti);
int ttyinq_read_uio(struct ttyinq *ti, struct lwp_tty *tp, struct uio *uio,
size_t readlen, size_t flushlen);
size_t ttyinq_write(struct ttyinq *ti, const void *buf, size_t len, int quote);
int ttyinq_write_nofrag(struct ttyinq *ti, const void *buf, size_t len,
int quote);
void ttyinq_canonicalize(struct ttyinq *ti);
size_t ttyinq_findchar(struct ttyinq *ti, const char *breakc, size_t maxlen,
char *lastc);
void ttyinq_flush(struct ttyinq *ti);
int ttyinq_peekchar(struct ttyinq *ti, char *c, int *quote);
void ttyinq_unputchar(struct ttyinq *ti);
void ttyinq_reprintpos_set(struct ttyinq *ti);
void ttyinq_reprintpos_reset(struct ttyinq *ti);
rt_inline size_t ttyinq_getsize(struct ttyinq *ti)
{
return (ti->ti_nblocks * TTYINQ_DATASIZE);
}
rt_inline size_t ttyinq_getallocatedsize(struct ttyinq *ti)
{
return (ti->ti_quota * TTYINQ_DATASIZE);
}
rt_inline size_t ttyinq_bytesleft(struct ttyinq *ti)
{
size_t len;
/* Make sure the usage never exceeds the length. */
len = ti->ti_nblocks * TTYINQ_DATASIZE;
MPASS(len >= ti->ti_end);
return (len - ti->ti_end);
}
rt_inline size_t ttyinq_bytescanonicalized(struct ttyinq *ti)
{
MPASS(ti->ti_begin <= ti->ti_linestart);
return (ti->ti_linestart - ti->ti_begin);
}
rt_inline size_t ttyinq_bytesline(struct ttyinq *ti)
{
MPASS(ti->ti_linestart <= ti->ti_end);
return (ti->ti_end - ti->ti_linestart);
}
/* Input buffer iteration. */
typedef void ttyinq_line_iterator_t(void *data, char c, int flags);
void ttyinq_line_iterate_from_linestart(struct ttyinq *ti,
ttyinq_line_iterator_t *iterator,
void *data);
void ttyinq_line_iterate_from_reprintpos(struct ttyinq *ti,
ttyinq_line_iterator_t *iterator,
void *data);
/* Output queue handling routines. */
void ttyoutq_flush(struct ttyoutq *to);
int ttyoutq_setsize(struct ttyoutq *to, struct lwp_tty *tp, size_t len);
void ttyoutq_free(struct ttyoutq *to);
size_t ttyoutq_read(struct ttyoutq *to, void *buf, size_t len);
int ttyoutq_read_uio(struct ttyoutq *to, struct lwp_tty *tp, struct uio *uio);
size_t ttyoutq_write(struct ttyoutq *to, const void *buf, size_t len);
int ttyoutq_write_nofrag(struct ttyoutq *to, const void *buf, size_t len);
rt_inline size_t ttyoutq_getsize(struct ttyoutq *to)
{
return (to->to_nblocks * TTYOUTQ_DATASIZE);
}
rt_inline size_t ttyoutq_getallocatedsize(struct ttyoutq *to)
{
return (to->to_quota * TTYOUTQ_DATASIZE);
}
rt_inline size_t ttyoutq_bytesleft(struct ttyoutq *to)
{
size_t len;
/* Make sure the usage never exceeds the length. */
len = to->to_nblocks * TTYOUTQ_DATASIZE;
MPASS(len >= to->to_end);
return (len - to->to_end);
}
rt_inline size_t ttyoutq_bytesused(struct ttyoutq *to)
{
return (to->to_end - to->to_begin);
}
#endif /* !_SYS_TTYQUEUE_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,713 @@
/*
* 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;
cfsetospeed(&tios, tios.__c_ispeed);
error = _copy_to_user(args, &tios, sizeof(tios));
break;
case TCSETS:
error = _copy_from_user(&tios, args, sizeof(tios));
if (error)
break;
tios.__c_ispeed = tios.__c_ospeed = cfgetospeed(&tios);
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;
#endif /* USING_BSD_IOCTL_EXT */
/* TIOCGSOFTCAR */
/* TIOCSSOFTCAR */
case FIONREAD: /* TIOCINQ */
error = (IOCTL(FIONREAD, args, fflags, td));
break;
#ifdef USING_BSD_IOCTL_EXT
/* 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);
}

View File

@@ -0,0 +1,507 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-11-13 Shell init ver.
*/
#include "../bsd_porting.h"
#include "../terminal.h"
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
* All rights reserved.
*
* Portions of this software were developed under sponsorship from Snow
* B.V., the Netherlands.
*
* 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.
*/
/*
* TTY input queue buffering.
*
* Unlike the output queue, the input queue has more features that are
* needed to properly implement various features offered by the TTY
* interface:
*
* - Data can be removed from the tail of the queue, which is used to
* implement backspace.
* - Once in a while, input has to be `canonicalized'. When ICANON is
* turned on, this will be done after a CR has been inserted.
* Otherwise, it should be done after any character has been inserted.
* - The input queue can store one bit per byte, called the quoting bit.
* This bit is used by TTYDISC to make backspace work on quoted
* characters.
*
* In most cases, there is probably less input than output, so unlike
* the outq, we'll stick to 128 byte blocks here.
*/
static int ttyinq_flush_secure = 1;
#define TTYINQ_QUOTESIZE (TTYINQ_DATASIZE / BMSIZE)
#define BMSIZE 32
#define GETBIT(tib, boff) \
((tib)->tib_quotes[(boff) / BMSIZE] & (1 << ((boff) % BMSIZE)))
#define SETBIT(tib, boff) \
((tib)->tib_quotes[(boff) / BMSIZE] |= (1 << ((boff) % BMSIZE)))
#define CLRBIT(tib, boff) \
((tib)->tib_quotes[(boff) / BMSIZE] &= ~(1 << ((boff) % BMSIZE)))
struct ttyinq_block
{
struct ttyinq_block *tib_prev;
struct ttyinq_block *tib_next;
uint32_t tib_quotes[TTYINQ_QUOTESIZE];
char tib_data[TTYINQ_DATASIZE];
};
static uma_zone_t ttyinq_zone;
#define TTYINQ_INSERT_TAIL(ti, tib) \
do \
{ \
if (ti->ti_end == 0) \
{ \
tib->tib_prev = NULL; \
tib->tib_next = ti->ti_firstblock; \
ti->ti_firstblock = tib; \
} \
else \
{ \
tib->tib_prev = ti->ti_lastblock; \
tib->tib_next = ti->ti_lastblock->tib_next; \
ti->ti_lastblock->tib_next = tib; \
} \
if (tib->tib_next != NULL) \
tib->tib_next->tib_prev = tib; \
ti->ti_nblocks++; \
} while (0)
#define TTYINQ_REMOVE_HEAD(ti) \
do \
{ \
ti->ti_firstblock = ti->ti_firstblock->tib_next; \
if (ti->ti_firstblock != NULL) \
ti->ti_firstblock->tib_prev = NULL; \
ti->ti_nblocks--; \
} while (0)
#define TTYINQ_RECYCLE(ti, tib) \
do \
{ \
if (ti->ti_quota <= ti->ti_nblocks) \
uma_zfree(ttyinq_zone, tib); \
else \
TTYINQ_INSERT_TAIL(ti, tib); \
} while (0)
int ttyinq_setsize(struct ttyinq *ti, struct lwp_tty *tp, size_t size)
{
struct ttyinq_block *tib;
ti->ti_quota = howmany(size, TTYINQ_DATASIZE);
while (ti->ti_quota > ti->ti_nblocks)
{
/*
* List is getting bigger.
* Add new blocks to the tail of the list.
*
* We must unlock the TTY temporarily, because we need
* to allocate memory. This won't be a problem, because
* in the worst case, another thread ends up here, which
* may cause us to allocate too many blocks, but this
* will be caught by the loop below.
*/
tty_unlock(tp);
tib = uma_zalloc(ttyinq_zone, M_WAITOK);
tty_lock(tp);
if (tty_gone(tp))
{
uma_zfree(ttyinq_zone, tib);
return -ENXIO;
}
TTYINQ_INSERT_TAIL(ti, tib);
}
return 0;
}
void ttyinq_free(struct ttyinq *ti)
{
struct ttyinq_block *tib;
ttyinq_flush(ti);
ti->ti_quota = 0;
while ((tib = ti->ti_firstblock) != NULL)
{
TTYINQ_REMOVE_HEAD(ti);
uma_zfree(ttyinq_zone, tib);
}
MPASS(ti->ti_nblocks == 0);
}
int ttyinq_read_uio(struct ttyinq *ti, struct lwp_tty *tp, struct uio *uio,
size_t rlen, size_t flen)
{
MPASS(rlen <= uio->uio_resid);
while (rlen > 0)
{
int error;
struct ttyinq_block *tib;
size_t cbegin, cend, clen;
/* See if there still is data. */
if (ti->ti_begin == ti->ti_linestart)
return 0;
tib = ti->ti_firstblock;
if (tib == NULL)
return 0;
/*
* The end address should be the lowest of these three:
* - The write pointer
* - The blocksize - we can't read beyond the block
* - The end address if we could perform the full read
*/
cbegin = ti->ti_begin;
cend = MIN(MIN(ti->ti_linestart, ti->ti_begin + rlen), TTYINQ_DATASIZE);
clen = cend - cbegin;
MPASS(clen >= flen);
rlen -= clen;
/*
* We can prevent buffering in some cases:
* - We need to read the block until the end.
* - We don't need to read the block until the end, but
* there is no data beyond it, which allows us to move
* the write pointer to a new block.
*/
if (cend == TTYINQ_DATASIZE || cend == ti->ti_end)
{
/*
* Fast path: zero copy. Remove the first block,
* so we can unlock the TTY temporarily.
*/
TTYINQ_REMOVE_HEAD(ti);
ti->ti_begin = 0;
/*
* Because we remove the first block, we must
* fix up the block offsets.
*/
#define CORRECT_BLOCK(t) \
do \
{ \
if (t <= TTYINQ_DATASIZE) \
t = 0; \
else \
t -= TTYINQ_DATASIZE; \
} while (0)
CORRECT_BLOCK(ti->ti_linestart);
CORRECT_BLOCK(ti->ti_reprint);
CORRECT_BLOCK(ti->ti_end);
#undef CORRECT_BLOCK
/*
* Temporary unlock and copy the data to
* userspace. We may need to flush trailing
* bytes, like EOF characters.
*/
tty_unlock(tp);
error = uiomove(tib->tib_data + cbegin, clen - flen, uio);
tty_lock(tp);
/* Block can now be readded to the list. */
TTYINQ_RECYCLE(ti, tib);
}
else
{
char ob[TTYINQ_DATASIZE - 1];
/*
* Slow path: store data in a temporary buffer.
*/
memcpy(ob, tib->tib_data + cbegin, clen - flen);
ti->ti_begin += clen;
MPASS(ti->ti_begin < TTYINQ_DATASIZE);
/* Temporary unlock and copy the data to userspace. */
tty_unlock(tp);
error = uiomove(ob, clen - flen, uio);
tty_lock(tp);
}
if (error != 0)
return error;
if (tty_gone(tp))
return -ENXIO;
}
return 0;
}
rt_inline void ttyinq_set_quotes(struct ttyinq_block *tib, size_t offset,
size_t length, int value)
{
if (value)
{
/* Set the bits. */
for (; length > 0; length--, offset++) SETBIT(tib, offset);
}
else
{
/* Unset the bits. */
for (; length > 0; length--, offset++) CLRBIT(tib, offset);
}
}
size_t ttyinq_write(struct ttyinq *ti, const void *buf, size_t nbytes,
int quote)
{
const char *cbuf = buf;
struct ttyinq_block *tib;
unsigned int boff;
size_t l;
while (nbytes > 0)
{
boff = ti->ti_end % TTYINQ_DATASIZE;
if (ti->ti_end == 0)
{
/* First time we're being used or drained. */
MPASS(ti->ti_begin == 0);
tib = ti->ti_firstblock;
if (tib == NULL)
{
/* Queue has no blocks. */
break;
}
ti->ti_lastblock = tib;
}
else if (boff == 0)
{
/* We reached the end of this block on last write. */
tib = ti->ti_lastblock->tib_next;
if (tib == NULL)
{
/* We've reached the watermark. */
break;
}
ti->ti_lastblock = tib;
}
else
{
tib = ti->ti_lastblock;
}
/* Don't copy more than was requested. */
l = MIN(nbytes, TTYINQ_DATASIZE - boff);
MPASS(l > 0);
memcpy(tib->tib_data + boff, cbuf, l);
/* Set the quoting bits for the proper region. */
ttyinq_set_quotes(tib, boff, l, quote);
cbuf += l;
nbytes -= l;
ti->ti_end += l;
}
return (cbuf - (const char *)buf);
}
int ttyinq_write_nofrag(struct ttyinq *ti, const void *buf, size_t nbytes,
int quote)
{
size_t ret __unused;
if (ttyinq_bytesleft(ti) < nbytes)
return -1;
/* We should always be able to write it back. */
ret = ttyinq_write(ti, buf, nbytes, quote);
MPASS(ret == nbytes);
return 0;
}
void ttyinq_canonicalize(struct ttyinq *ti)
{
ti->ti_linestart = ti->ti_reprint = ti->ti_end;
ti->ti_startblock = ti->ti_reprintblock = ti->ti_lastblock;
}
size_t ttyinq_findchar(struct ttyinq *ti, const char *breakc, size_t maxlen,
char *lastc)
{
struct ttyinq_block *tib = ti->ti_firstblock;
unsigned int boff = ti->ti_begin;
unsigned int bend =
MIN(MIN(TTYINQ_DATASIZE, ti->ti_linestart), ti->ti_begin + maxlen);
MPASS(maxlen > 0);
if (tib == NULL)
return 0;
while (boff < bend)
{
if (strchr(breakc, tib->tib_data[boff]) && !GETBIT(tib, boff))
{
*lastc = tib->tib_data[boff];
return (boff - ti->ti_begin + 1);
}
boff++;
}
/* Not found - just process the entire block. */
return (bend - ti->ti_begin);
}
void ttyinq_flush(struct ttyinq *ti)
{
struct ttyinq_block *tib;
ti->ti_begin = 0;
ti->ti_linestart = 0;
ti->ti_reprint = 0;
ti->ti_end = 0;
/* Zero all data in the input queue to get rid of passwords. */
if (ttyinq_flush_secure)
{
for (tib = ti->ti_firstblock; tib != NULL; tib = tib->tib_next)
memset(&tib->tib_data, 0, sizeof tib->tib_data);
}
}
int ttyinq_peekchar(struct ttyinq *ti, char *c, int *quote)
{
unsigned int boff;
struct ttyinq_block *tib = ti->ti_lastblock;
if (ti->ti_linestart == ti->ti_end)
return -1;
MPASS(ti->ti_end > 0);
boff = (ti->ti_end - 1) % TTYINQ_DATASIZE;
*c = tib->tib_data[boff];
*quote = GETBIT(tib, boff);
return 0;
}
void ttyinq_unputchar(struct ttyinq *ti)
{
MPASS(ti->ti_linestart < ti->ti_end);
if (--ti->ti_end % TTYINQ_DATASIZE == 0)
{
/* Roll back to the previous block. */
ti->ti_lastblock = ti->ti_lastblock->tib_prev;
/*
* This can only fail if we are unputchar()'ing the
* first character in the queue.
*/
MPASS((ti->ti_lastblock == NULL) == (ti->ti_end == 0));
}
}
void ttyinq_reprintpos_set(struct ttyinq *ti)
{
ti->ti_reprint = ti->ti_end;
ti->ti_reprintblock = ti->ti_lastblock;
}
void ttyinq_reprintpos_reset(struct ttyinq *ti)
{
ti->ti_reprint = ti->ti_linestart;
ti->ti_reprintblock = ti->ti_startblock;
}
static void ttyinq_line_iterate(struct ttyinq *ti,
ttyinq_line_iterator_t *iterator, void *data,
unsigned int offset, struct ttyinq_block *tib)
{
unsigned int boff;
/* Use the proper block when we're at the queue head. */
if (offset == 0)
tib = ti->ti_firstblock;
/* Iterate all characters and call the iterator function. */
for (; offset < ti->ti_end; offset++)
{
boff = offset % TTYINQ_DATASIZE;
MPASS(tib != NULL);
/* Call back the iterator function. */
iterator(data, tib->tib_data[boff], GETBIT(tib, boff));
/* Last byte iterated - go to the next block. */
if (boff == TTYINQ_DATASIZE - 1)
tib = tib->tib_next;
}
}
void ttyinq_line_iterate_from_linestart(struct ttyinq *ti,
ttyinq_line_iterator_t *iterator,
void *data)
{
ttyinq_line_iterate(ti, iterator, data, ti->ti_linestart,
ti->ti_startblock);
}
void ttyinq_line_iterate_from_reprintpos(struct ttyinq *ti,
ttyinq_line_iterator_t *iterator,
void *data)
{
ttyinq_line_iterate(ti, iterator, data, ti->ti_reprint,
ti->ti_reprintblock);
}
static int ttyinq_startup(void)
{
ttyinq_zone = uma_zcreate("ttyinq", sizeof(struct ttyinq_block), NULL, NULL,
NULL, NULL, UMA_ALIGN_PTR, 0);
return 0;
}
INIT_PREV_EXPORT(ttyinq_startup);
#if 0
SYSINIT(ttyinq, SI_SUB_DRIVERS, SI_ORDER_FIRST, ttyinq_startup, NULL);
#endif

View File

@@ -0,0 +1,370 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-11-13 Shell init ver.
*/
#include "../bsd_porting.h"
#include "../terminal.h"
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
* All rights reserved.
*
* Portions of this software were developed under sponsorship from Snow
* B.V., the Netherlands.
*
* 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.
*/
/*
* TTY output queue buffering.
*
* The previous design of the TTY layer offered the so-called clists.
* These clists were used for both the input queues and the output
* queue. We don't use certain features on the output side, like quoting
* bits for parity marking and such. This mechanism is similar to the
* old clists, but only contains the features we need to buffer the
* output.
*/
struct ttyoutq_block
{
struct ttyoutq_block *tob_next;
char tob_data[TTYOUTQ_DATASIZE];
};
static uma_zone_t ttyoutq_zone;
#define TTYOUTQ_INSERT_TAIL(to, tob) \
do \
{ \
if (to->to_end == 0) \
{ \
tob->tob_next = to->to_firstblock; \
to->to_firstblock = tob; \
} \
else \
{ \
tob->tob_next = to->to_lastblock->tob_next; \
to->to_lastblock->tob_next = tob; \
} \
to->to_nblocks++; \
} while (0)
#define TTYOUTQ_REMOVE_HEAD(to) \
do \
{ \
to->to_firstblock = to->to_firstblock->tob_next; \
to->to_nblocks--; \
} while (0)
#define TTYOUTQ_RECYCLE(to, tob) \
do \
{ \
if (to->to_quota <= to->to_nblocks) \
uma_zfree(ttyoutq_zone, tob); \
else \
TTYOUTQ_INSERT_TAIL(to, tob); \
} while (0)
void ttyoutq_flush(struct ttyoutq *to)
{
to->to_begin = 0;
to->to_end = 0;
}
int ttyoutq_setsize(struct ttyoutq *to, struct lwp_tty *tp, size_t size)
{
struct ttyoutq_block *tob;
to->to_quota = howmany(size, TTYOUTQ_DATASIZE);
while (to->to_quota > to->to_nblocks)
{
/*
* List is getting bigger.
* Add new blocks to the tail of the list.
*
* We must unlock the TTY temporarily, because we need
* to allocate memory. This won't be a problem, because
* in the worst case, another thread ends up here, which
* may cause us to allocate too many blocks, but this
* will be caught by the loop below.
*/
tty_unlock(tp);
tob = uma_zalloc(ttyoutq_zone, M_WAITOK);
tty_lock(tp);
if (tty_gone(tp))
{
uma_zfree(ttyoutq_zone, tob);
return -ENXIO;
}
TTYOUTQ_INSERT_TAIL(to, tob);
}
return 0;
}
void ttyoutq_free(struct ttyoutq *to)
{
struct ttyoutq_block *tob;
ttyoutq_flush(to);
to->to_quota = 0;
while ((tob = to->to_firstblock) != NULL)
{
TTYOUTQ_REMOVE_HEAD(to);
uma_zfree(ttyoutq_zone, tob);
}
MPASS(to->to_nblocks == 0);
}
size_t ttyoutq_read(struct ttyoutq *to, void *buf, size_t len)
{
char *cbuf = buf;
while (len > 0)
{
struct ttyoutq_block *tob;
size_t cbegin, cend, clen;
/* See if there still is data. */
if (to->to_begin == to->to_end)
break;
tob = to->to_firstblock;
if (tob == NULL)
break;
/*
* The end address should be the lowest of these three:
* - The write pointer
* - The blocksize - we can't read beyond the block
* - The end address if we could perform the full read
*/
cbegin = to->to_begin;
cend = MIN(MIN(to->to_end, to->to_begin + len), TTYOUTQ_DATASIZE);
clen = cend - cbegin;
/* Copy the data out of the buffers. */
memcpy(cbuf, tob->tob_data + cbegin, clen);
cbuf += clen;
len -= clen;
if (cend == to->to_end)
{
/* Read the complete queue. */
to->to_begin = 0;
to->to_end = 0;
}
else if (cend == TTYOUTQ_DATASIZE)
{
/* Read the block until the end. */
TTYOUTQ_REMOVE_HEAD(to);
to->to_begin = 0;
to->to_end -= TTYOUTQ_DATASIZE;
TTYOUTQ_RECYCLE(to, tob);
}
else
{
/* Read the block partially. */
to->to_begin += clen;
}
}
return cbuf - (char *)buf;
}
/*
* An optimized version of ttyoutq_read() which can be used in pseudo
* TTY drivers to directly copy data from the outq to userspace, instead
* of buffering it.
*
* We can only copy data directly if we need to read the entire block
* back to the user, because we temporarily remove the block from the
* queue. Otherwise we need to copy it to a temporary buffer first, to
* make sure data remains in the correct order.
*/
int ttyoutq_read_uio(struct ttyoutq *to, struct lwp_tty *tp, struct uio *uio)
{
while (uio->uio_resid > 0)
{
int error;
struct ttyoutq_block *tob;
size_t cbegin, cend, clen;
/* See if there still is data. */
if (to->to_begin == to->to_end)
return 0;
tob = to->to_firstblock;
if (tob == NULL)
return 0;
/*
* The end address should be the lowest of these three:
* - The write pointer
* - The blocksize - we can't read beyond the block
* - The end address if we could perform the full read
*/
cbegin = to->to_begin;
cend = MIN(MIN(to->to_end, to->to_begin + uio->uio_resid),
TTYOUTQ_DATASIZE);
clen = cend - cbegin;
/*
* We can prevent buffering in some cases:
* - We need to read the block until the end.
* - We don't need to read the block until the end, but
* there is no data beyond it, which allows us to move
* the write pointer to a new block.
*/
if (cend == TTYOUTQ_DATASIZE || cend == to->to_end)
{
/*
* Fast path: zero copy. Remove the first block,
* so we can unlock the TTY temporarily.
*/
TTYOUTQ_REMOVE_HEAD(to);
to->to_begin = 0;
if (to->to_end <= TTYOUTQ_DATASIZE)
to->to_end = 0;
else
to->to_end -= TTYOUTQ_DATASIZE;
/* Temporary unlock and copy the data to userspace. */
tty_unlock(tp);
error = uiomove(tob->tob_data + cbegin, clen, uio);
tty_lock(tp);
/* Block can now be readded to the list. */
TTYOUTQ_RECYCLE(to, tob);
}
else
{
char ob[TTYOUTQ_DATASIZE - 1];
/*
* Slow path: store data in a temporary buffer.
*/
memcpy(ob, tob->tob_data + cbegin, clen);
to->to_begin += clen;
MPASS(to->to_begin < TTYOUTQ_DATASIZE);
/* Temporary unlock and copy the data to userspace. */
tty_unlock(tp);
error = uiomove(ob, clen, uio);
tty_lock(tp);
}
if (error != 0)
return error;
}
return 0;
}
size_t ttyoutq_write(struct ttyoutq *to, const void *buf, size_t nbytes)
{
const char *cbuf = buf;
struct ttyoutq_block *tob;
unsigned int boff;
size_t l;
while (nbytes > 0)
{
boff = to->to_end % TTYOUTQ_DATASIZE;
if (to->to_end == 0)
{
/* First time we're being used or drained. */
MPASS(to->to_begin == 0);
tob = to->to_firstblock;
if (tob == NULL)
{
/* Queue has no blocks. */
break;
}
to->to_lastblock = tob;
}
else if (boff == 0)
{
/* We reached the end of this block on last write. */
tob = to->to_lastblock->tob_next;
if (tob == NULL)
{
/* We've reached the watermark. */
break;
}
to->to_lastblock = tob;
}
else
{
tob = to->to_lastblock;
}
/* Don't copy more than was requested. */
l = MIN(nbytes, TTYOUTQ_DATASIZE - boff);
MPASS(l > 0);
memcpy(tob->tob_data + boff, cbuf, l);
cbuf += l;
nbytes -= l;
to->to_end += l;
}
return (cbuf - (const char *)buf);
}
int ttyoutq_write_nofrag(struct ttyoutq *to, const void *buf, size_t nbytes)
{
size_t ret __unused;
if (ttyoutq_bytesleft(to) < nbytes)
return -1;
/* We should always be able to write it back. */
ret = ttyoutq_write(to, buf, nbytes);
MPASS(ret == nbytes);
return 0;
}
static int ttyoutq_startup(void)
{
ttyoutq_zone = uma_zcreate("ttyoutq", sizeof(struct ttyoutq_block), NULL,
NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
return 0;
}
INIT_PREV_EXPORT(ttyoutq_startup);
#if 0
SYSINIT(ttyoutq, SI_SUB_DRIVERS, SI_ORDER_FIRST, ttyoutq_startup, NULL);
#endif

View File

@@ -0,0 +1,837 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-12-07 Shell init ver.
*/
#include <ipc/condvar.h>
#include <rid_bitmap.h>
#include <terminal/terminal.h>
#include <terminal/tty_internal.h>
#include <ptyfs.h>
#include <rtthread.h>
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
* All rights reserved.
*
* Portions of this software were developed under sponsorship from Snow
* B.V., the Netherlands.
*
* 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.
*/
#define PTS_EXTERNAL
/*
* Per-PTS structure.
*
* List of locks
* (t) locked by tty_lock()
* (c) const until freeing
*/
struct pts_softc
{
int pts_unit; /* (c) Device unit number. */
unsigned int pts_flags; /* (t) Device flags. */
#define PTS_PKT 0x1 /* Packet mode. */
#define PTS_FINISHED 0x2 /* Return errors on read()/write(). */
#define PTS_PTLOCKED 0x4 /* ioctl %TIOCSPTLCK/%TIOCGPTLCK */
char pts_pkt; /* (t) Unread packet mode data. */
struct rt_condvar pts_inwait; /* (t) Blocking write() on master. */
struct rt_wqueue pts_inpoll; /* (t) Select queue for write(). */
struct rt_condvar pts_outwait; /* (t) Blocking read() on master. */
struct rt_wqueue pts_outpoll; /* (t) Select queue for read(). */
struct ucred *pts_cred; /* (c) Resource limit. */
rt_device_t pts_master; /** (c) Master device.
* (Note: in rtsmart kernel, we support
* multi-instance ptmx )
*/
};
/**
* Controller-side file operations.
* (P)seudo-(T)erminal (M)ultiple(X)er
*/
static int ptsdev_read(struct lwp_tty *tp, struct uio *uio,
struct ucred *active_cred, int oflags,
struct rt_thread *td)
{
struct pts_softc *psc = tty_softc(tp);
int error = 0;
char pkt;
if (uio->uio_resid == 0)
return (0);
tty_lock(tp);
for (;;)
{
/*
* Implement packet mode. When packet mode is turned on,
* the first byte contains a bitmask of events that
* occurred (start, stop, flush, window size, etc).
*/
if (psc->pts_flags & PTS_PKT && psc->pts_pkt)
{
pkt = psc->pts_pkt;
psc->pts_pkt = 0;
tty_unlock(tp);
error = uiomove(&pkt, 1, uio);
return (error);
}
/*
* Transmit regular data.
*
* XXX: We shouldn't use ttydisc_getc_poll()! Even
* though in this implementation, there is likely going
* to be data, we should just call ttydisc_getc_uio()
* and use its return value to sleep.
*/
if (ttydisc_getc_poll(tp))
{
if (psc->pts_flags & PTS_PKT)
{
/*
* XXX: Small race. Fortunately PTY
* consumers aren't multithreaded.
*/
tty_unlock(tp);
pkt = TIOCPKT_DATA;
error = uiomove(&pkt, 1, uio);
if (error)
return (error);
tty_lock(tp);
}
error = ttydisc_getc_uio(tp, uio);
break;
}
/* Maybe the device isn't used anyway. */
if (psc->pts_flags & PTS_FINISHED)
break;
/* Wait for more data. */
if (oflags & O_NONBLOCK)
{
error = EWOULDBLOCK;
break;
}
error = cv_wait_sig(&psc->pts_outwait, tp->t_mtx);
if (error != 0)
break;
}
tty_unlock(tp);
return (error);
}
static int ptsdev_write(struct lwp_tty *tp, struct uio *uio,
struct ucred *active_cred, int oflags,
struct rt_thread *td)
{
struct pts_softc *psc = tty_softc(tp);
char ib[256], *ibstart;
size_t iblen, rintlen;
int error = 0;
if (uio->uio_resid == 0)
return (0);
for (;;)
{
ibstart = ib;
iblen = MIN(uio->uio_resid, sizeof ib);
error = uiomove(ib, iblen, uio);
tty_lock(tp);
if (error != 0)
{
iblen = 0;
goto done;
}
/*
* When possible, avoid the slow path. rint_bypass()
* copies all input to the input queue at once.
*/
MPASS(iblen > 0);
do
{
rintlen = ttydisc_rint_simple(tp, ibstart, iblen);
ibstart += rintlen;
iblen -= rintlen;
if (iblen == 0)
{
/* All data written. */
break;
}
/* Maybe the device isn't used anyway. */
if (psc->pts_flags & PTS_FINISHED)
{
error = -EIO;
goto done;
}
/* Wait for more data. */
if (oflags & O_NONBLOCK)
{
error = -EWOULDBLOCK;
goto done;
}
/* Wake up users on the slave side. */
ttydisc_rint_done(tp);
error = cv_wait_sig(&psc->pts_inwait, tp->t_mtx);
if (error != 0)
goto done;
} while (iblen > 0);
if (uio->uio_resid == 0)
break;
tty_unlock(tp);
}
done:
ttydisc_rint_done(tp);
tty_unlock(tp);
/*
* Don't account for the part of the buffer that we couldn't
* pass to the TTY.
*/
uio->uio_resid += iblen;
return (error);
}
static int ptsdev_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, void *data,
struct ucred *active_cred, int fflags,
struct rt_thread *td)
{
struct pts_softc *psc = tty_softc(tp);
int error = 0, sig;
switch (cmd)
{
#ifdef USING_BSD_IOCTL_EXT
case FIODTYPE:
*(int *)data = D_TTY;
return (0);
#endif
case FIONBIO:
/* This device supports non-blocking operation. */
return (0);
case FIONREAD:
tty_lock(tp);
if (psc->pts_flags & PTS_FINISHED)
{
/* Force read() to be called. */
*(int *)data = 1;
}
else
{
*(int *)data = ttydisc_getc_poll(tp);
}
tty_unlock(tp);
return (0);
#ifdef USING_BSD_IOCTL_EXT
case FIODGNAME:
#ifdef COMPAT_FREEBSD32
case FIODGNAME_32:
#endif
{
struct fiodgname_arg *fgn;
const char *p;
int i;
/* Reverse device name lookups, for ptsname() and ttyname(). */
fgn = data;
p = tty_devname(tp);
i = strlen(p) + 1;
if (i > fgn->len)
return -EINVAL;
return (copyout(p, fiodgname_buf_get_ptr(fgn, cmd), i));
}
#endif
/*
* We need to implement TIOCGPGRP and TIOCGSID here again. When
* called on the pseudo-terminal master, it should not check if
* the terminal is the foreground terminal of the calling
* process.
*
* TIOCGETA is also implemented here. Various Linux PTY routines
* often call isatty(), which is implemented by tcgetattr().
*/
case TIOCGETA:
/* Obtain terminal flags through tcgetattr(). */
tty_lock(tp);
*(struct termios *)data = tp->t_termios;
tty_unlock(tp);
return (0);
case TIOCSETAF:
case TIOCSETAW:
/*
* We must make sure we turn tcsetattr() calls of TCSAFLUSH and
* TCSADRAIN into something different. If an application would
* call TCSAFLUSH or TCSADRAIN on the master descriptor, it may
* deadlock waiting for all data to be read.
*/
cmd = TIOCSETA;
break;
case TIOCGPTN:
/*
* Get the device unit number.
*/
if (psc->pts_unit < 0)
return -ENOTTY;
*(unsigned int *)data = psc->pts_unit;
return (0);
case TIOCGPGRP:
/* Get the foreground process group ID. */
tty_lock(tp);
if (tp->t_pgrp != NULL)
*(int *)data = tp->t_pgrp->pgid;
else
*(int *)data = NO_PID;
tty_unlock(tp);
return (0);
case TIOCGSID:
/* Get the session leader process ID. */
tty_lock(tp);
if (tp->t_session == NULL)
error = -ENOTTY;
else
*(int *)data = tp->t_session->sid;
tty_unlock(tp);
return (error);
#ifdef USING_BSD_IOCTL_EXT
case TIOCPTMASTER:
/* Yes, we are a pseudo-terminal master. */
return (0);
#endif /* USING_BSD_IOCTL_EXT */
case TIOCSIG:
/* Signal the foreground process group. */
sig = *(int *)data;
if (sig < 1 || sig >= _LWP_NSIG)
return -EINVAL;
tty_lock(tp);
lwp_tty_signal_pgrp(tp, sig);
tty_unlock(tp);
return (0);
case TIOCPKT:
/* Enable/disable packet mode. */
tty_lock(tp);
if (*(int *)data)
psc->pts_flags |= PTS_PKT;
else
psc->pts_flags &= ~PTS_PKT;
tty_unlock(tp);
return (0);
}
/* Just redirect this ioctl to the slave device. */
tty_lock(tp);
error = tty_ioctl(tp, cmd, data, fflags, td);
tty_unlock(tp);
if (error == -ENOIOCTL)
error = -ENOTTY;
return error;
}
static int ptsdev_poll(struct lwp_tty *tp, struct rt_pollreq *req,
struct ucred *active_cred, struct rt_thread *td)
{
struct pts_softc *psc = tty_softc(tp);
int revents = 0;
int events = req->_key;
tty_lock(tp);
if (psc->pts_flags & PTS_FINISHED)
{
/* Slave device is not opened. */
tty_unlock(tp);
return ((events & (POLLIN | POLLRDNORM)) | POLLHUP);
}
if (events & (POLLIN | POLLRDNORM))
{
/* See if we can getc something. */
if (ttydisc_getc_poll(tp) || (psc->pts_flags & PTS_PKT && psc->pts_pkt))
revents |= events & (POLLIN | POLLRDNORM);
}
if (events & (POLLOUT | POLLWRNORM))
{
/* See if we can rint something. */
if (ttydisc_rint_poll(tp))
revents |= events & (POLLOUT | POLLWRNORM);
}
/*
* No need to check for POLLHUP here. This device cannot be used
* as a callout device, which means we always have a carrier,
* because the master is.
*/
if (revents == 0)
{
/*
* This code might look misleading, but the naming of
* poll events on this side is the opposite of the slave
* device.
*/
if (events & (POLLIN | POLLRDNORM))
rt_poll_add(&psc->pts_outpoll, req);
if (events & (POLLOUT | POLLWRNORM))
rt_poll_add(&psc->pts_inpoll, req);
}
tty_unlock(tp);
return (revents);
}
#if USING_BSD_KQUEUE
/*
* kqueue support.
*/
static void pts_kqops_read_detach(struct knote *kn)
{
struct file *fp = kn->kn_fp;
struct lwp_tty *tp = fp->f_data;
struct pts_softc *psc = tty_softc(tp);
knlist_remove(&psc->pts_outpoll.si_note, kn, 0);
}
static int pts_kqops_read_event(struct knote *kn, long hint)
{
struct file *fp = kn->kn_fp;
struct lwp_tty *tp = fp->f_data;
struct pts_softc *psc = tty_softc(tp);
if (psc->pts_flags & PTS_FINISHED)
{
kn->kn_flags |= EV_EOF;
return (1);
}
else
{
kn->kn_data = ttydisc_getc_poll(tp);
return (kn->kn_data > 0);
}
}
static void pts_kqops_write_detach(struct knote *kn)
{
struct file *fp = kn->kn_fp;
struct lwp_tty *tp = fp->f_data;
struct pts_softc *psc = tty_softc(tp);
knlist_remove(&psc->pts_inpoll.si_note, kn, 0);
}
static int pts_kqops_write_event(struct knote *kn, long hint)
{
struct file *fp = kn->kn_fp;
struct lwp_tty *tp = fp->f_data;
struct pts_softc *psc = tty_softc(tp);
if (psc->pts_flags & PTS_FINISHED)
{
kn->kn_flags |= EV_EOF;
return (1);
}
else
{
kn->kn_data = ttydisc_rint_poll(tp);
return (kn->kn_data > 0);
}
}
static struct filterops pts_kqops_read = {
.f_isfd = 1,
.f_detach = pts_kqops_read_detach,
.f_event = pts_kqops_read_event,
};
static struct filterops pts_kqops_write = {
.f_isfd = 1,
.f_detach = pts_kqops_write_detach,
.f_event = pts_kqops_write_event,
};
static int ptsdev_kqfilter(struct file *fp, struct knote *kn)
{
struct lwp_tty *tp = fp->f_data;
struct pts_softc *psc = tty_softc(tp);
int error = 0;
tty_lock(tp);
switch (kn->kn_filter)
{
case EVFILT_READ:
kn->kn_fop = &pts_kqops_read;
knlist_add(&psc->pts_outpoll.si_note, kn, 1);
break;
case EVFILT_WRITE:
kn->kn_fop = &pts_kqops_write;
knlist_add(&psc->pts_inpoll.si_note, kn, 1);
break;
default:
error = EINVAL;
break;
}
tty_unlock(tp);
return (error);
}
#endif
#if USING_BSD_STAT
static int ptsdev_stat(struct file *fp, struct stat *sb,
struct ucred *active_cred)
{
struct lwp_tty *tp = fp->f_data;
#ifdef PTS_EXTERNAL
struct pts_softc *psc = tty_softc(tp);
#endif /* PTS_EXTERNAL */
struct cdev *dev = tp->t_dev;
/*
* According to POSIX, we must implement an fstat(). This also
* makes this implementation compatible with Linux binaries,
* because Linux calls fstat() on the pseudo-terminal master to
* obtain st_rdev.
*
* XXX: POSIX also mentions we must fill in st_dev, but how?
*/
bzero(sb, sizeof *sb);
#ifdef PTS_EXTERNAL
if (psc->pts_cdev != NULL)
sb->st_ino = sb->st_rdev = dev2udev(psc->pts_cdev);
else
#endif /* PTS_EXTERNAL */
sb->st_ino = sb->st_rdev = tty_udev(tp);
sb->st_atim = dev->si_atime;
sb->st_ctim = dev->si_ctime;
sb->st_mtim = dev->si_mtime;
sb->st_uid = dev->si_uid;
sb->st_gid = dev->si_gid;
sb->st_mode = dev->si_mode | S_IFCHR;
return (0);
}
#endif
static int ptsdev_close(struct lwp_tty *tp, struct rt_thread *td)
{
/* Deallocate TTY device. */
tty_lock(tp);
tty_rel_gone(tp);
#ifdef USING_BSD_VNODE
/* TODO: consider the vnode operation on DFS */
/*
* Open of /dev/ptmx or /dev/ptyXX changes the type of file
* from DTYPE_VNODE to DTYPE_PTS. vn_open() increases vnode
* use count, we need to decrement it, and possibly do other
* required cleanup.
*/
if (fp->f_vnode != NULL)
return (vnops.fo_close(fp, td));
#endif /* USING_BSD_VNODE */
return 0;
}
#ifdef USING_BSD_KINFO
static int ptsdev_fill_kinfo(struct file *fp, struct kinfo_file *kif,
struct filedesc *fdp)
{
struct lwp_tty *tp;
kif->kf_type = KF_TYPE_PTS;
tp = fp->f_data;
kif->kf_un.kf_pts.kf_pts_dev = tty_udev(tp);
kif->kf_un.kf_pts.kf_pts_dev_freebsd11 =
kif->kf_un.kf_pts.kf_pts_dev; /* truncate */
strlcpy(kif->kf_path, tty_devname(tp), sizeof(kif->kf_path));
return (0);
}
#endif
struct bsd_fileops bsd_ptsdev_methods = {
.fo_read = ptsdev_read,
.fo_write = ptsdev_write,
// .fo_truncate = invfo_truncate,
.fo_ioctl = ptsdev_ioctl,
.fo_poll = ptsdev_poll,
// .fo_kqfilter = ptsdev_kqfilter,
// .fo_stat = ptsdev_stat,
.fo_close = ptsdev_close,
// .fo_chmod = invfo_chmod,
// .fo_chown = invfo_chown,
// .fo_sendfile = invfo_sendfile,
// .fo_fill_kinfo = ptsdev_fill_kinfo,
.fo_flags = DFLAG_PASSABLE,
};
/*
* Driver-side hooks.
*/
static void ptsdrv_outwakeup(struct lwp_tty *tp)
{
struct pts_softc *psc = tty_softc(tp);
cv_broadcast(&psc->pts_outwait);
rt_wqueue_wakeup_all(&psc->pts_outpoll, (void *)POLLIN);
}
static void ptsdrv_inwakeup(struct lwp_tty *tp)
{
struct pts_softc *psc = tty_softc(tp);
cv_broadcast(&psc->pts_inwait);
rt_wqueue_wakeup_all(&psc->pts_inpoll, (void *)POLLOUT);
}
static int ptsdrv_open(struct lwp_tty *tp)
{
struct pts_softc *psc = tty_softc(tp);
/* for ioctl(TIOCSPTLCK) */
if (psc->pts_flags & PTS_PTLOCKED)
return -EIO;
psc->pts_flags &= ~PTS_FINISHED;
return 0;
}
static void ptsdrv_close(struct lwp_tty *tp)
{
struct pts_softc *psc = tty_softc(tp);
/* Wake up any blocked readers/writers. */
psc->pts_flags |= PTS_FINISHED;
ptsdrv_outwakeup(tp);
ptsdrv_inwakeup(tp);
}
static void ptsdrv_pktnotify(struct lwp_tty *tp, char event)
{
struct pts_softc *psc = tty_softc(tp);
/*
* Clear conflicting flags.
*/
switch (event)
{
case TIOCPKT_STOP:
psc->pts_pkt &= ~TIOCPKT_START;
break;
case TIOCPKT_START:
psc->pts_pkt &= ~TIOCPKT_STOP;
break;
case TIOCPKT_NOSTOP:
psc->pts_pkt &= ~TIOCPKT_DOSTOP;
break;
case TIOCPKT_DOSTOP:
psc->pts_pkt &= ~TIOCPKT_NOSTOP;
break;
}
psc->pts_pkt |= event;
/**
* Note: on smart, we don't wakeup master until it's willing to accept
* packet event. Because on poll, we setup POLLIN for PTS_PKT only. So There
* is a chance when we wakeup ipc but we can't wakeup user again. Since
* current wakeup will remove the wakequeue node on the meanwhile
*/
if (psc->pts_flags & PTS_PKT)
ptsdrv_outwakeup(tp);
}
static void ptsdrv_free(void *softc)
{
struct pts_softc *psc = softc;
/* Make device number available again. */
if (psc->pts_unit >= 0)
ptyfs_unregister_pts(psc->pts_master, psc->pts_unit);
#ifdef USING_BSD_UCRED
chgptscnt(psc->pts_cred->cr_ruidinfo, -1, 0);
racct_sub_cred(psc->pts_cred, RACCT_NPTS, 1);
crfree(psc->pts_cred);
#endif
rt_wqueue_wakeup_all(&psc->pts_inpoll, (void *)POLLHUP);
rt_wqueue_wakeup_all(&psc->pts_outpoll, (void *)POLLHUP);
rt_free(psc);
}
static struct lwp_ttydevsw pts_class = {
.tsw_flags = TF_NOPREFIX,
.tsw_outwakeup = ptsdrv_outwakeup,
.tsw_inwakeup = ptsdrv_inwakeup,
.tsw_open = ptsdrv_open,
.tsw_close = ptsdrv_close,
.tsw_pktnotify = ptsdrv_pktnotify,
.tsw_free = ptsdrv_free,
};
int pts_alloc(int fflags, struct rt_thread *td, struct dfs_file *ptm_file)
{
int unit;
struct lwp_tty *tp;
struct pts_softc *psc;
char name_buf[DIRENT_NAME_MAX];
const char *rootpath;
rt_device_t ptmx_device = ptm_file->vnode->data;
#ifdef USING_BSD_UCRED
struct rt_lwp *p = td->lwp;
int ok, error;
struct ucred *cred = td->td_ucred;
#endif
/* Resource limiting. */
#ifdef USING_BSD_UCRED
LWP_LOCK(p);
error = racct_add(p, RACCT_NPTS, 1);
if (error != 0)
{
LWP_UNLOCK(p);
return -EAGAIN;
}
ok = chgptscnt(cred->cr_ruidinfo, 1, lim_cur(td, RLIMIT_NPTS));
if (!ok)
{
racct_sub(p, RACCT_NPTS, 1);
LWP_UNLOCK(p);
return -EAGAIN;
}
LWP_UNLOCK(p);
#endif
/* Allocate TTY and softc. */
psc = rt_calloc(1, sizeof(struct pts_softc));
cv_init(&psc->pts_inwait, "ptsin");
cv_init(&psc->pts_outwait, "ptsout");
rt_wqueue_init(&psc->pts_inpoll);
rt_wqueue_init(&psc->pts_outpoll);
psc->pts_master = ptmx_device;
#ifdef USING_BSD_UCRED
psc->pts_cred = crhold(cred);
#else
psc->pts_cred = 0;
#endif
tp = lwp_tty_create(&pts_class, psc);
if (!tp)
{
rt_free(psc);
rt_condvar_detach(&psc->pts_inwait);
rt_condvar_detach(&psc->pts_outwait);
return -ENOMEM;
}
/* Try to allocate a new pts uint*/
unit = ptyfs_register_pts(ptmx_device, &tp->parent);
if (unit < 0)
{
#ifdef USING_BSD_UCRED
racct_sub(p, RACCT_NPTS, 1);
chgptscnt(cred->cr_ruidinfo, -1, 0);
#endif
lwp_tty_delete(tp);
return -EAGAIN;
}
psc->pts_unit = unit;
/* Expose the slave device as well. */
#ifdef USING_BSD_UCRED
tty_makedev(tp, td->td_ucred, "pts/%u", psc->pts_unit);
#else
rootpath = ptyfs_get_rootpath(ptmx_device);
RT_ASSERT(rootpath[strlen(rootpath) - 1] != '/');
snprintf(name_buf, DIRENT_NAME_MAX, "%s/%d", rootpath, psc->pts_unit);
/* setup the pts */
lwp_tty_register(tp, name_buf);
/* now this file operating on new pty */
ptm_file->data = tp;
#endif
return 0;
}
void pts_set_lock(lwp_tty_t pts, rt_bool_t is_lock)
{
struct pts_softc *psc = tty_softc(pts);
if (is_lock)
psc->pts_flags |= PTS_PTLOCKED;
else
psc->pts_flags &= ~PTS_PTLOCKED;
}
rt_bool_t pts_is_locked(lwp_tty_t pts)
{
struct pts_softc *psc = tty_softc(pts);
return !!(psc->pts_flags & PTS_PTLOCKED);
}
int pts_get_pktmode(lwp_tty_t pts)
{
struct pts_softc *psc = tty_softc(pts);
return !!(psc->pts_flags & PTS_PKT);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,414 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-11-13 Shell init ver.
*/
#ifndef __LWP_TERMINAL_H__
#define __LWP_TERMINAL_H__
#include "bsd_ttyqueue.h"
#include "bsd_ttydisc.h"
#ifdef USING_BSD_HOOK
#include "bsd_ttyhook.h"
#endif
#include <lwp.h>
#include <rtdef.h>
/* include kernel header for termios base definitions */
#include <termios.h>
/* for _POSIX_VDISABLE */
#include <unistd.h>
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
* All rights reserved.
*
* Portions of this software were developed under sponsorship from Snow
* B.V., the Netherlands.
*
* 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.
*/
struct lwp_tty;
/*
* Driver routines that are called from the line discipline to adjust
* hardware parameters and such.
*/
typedef int tsw_open_t(struct lwp_tty *tp);
typedef void tsw_close_t(struct lwp_tty *tp);
typedef void tsw_outwakeup_t(struct lwp_tty *tp);
typedef void tsw_inwakeup_t(struct lwp_tty *tp);
typedef int tsw_ioctl_t(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data,
struct rt_thread *td);
typedef int tsw_cioctl_t(struct lwp_tty *tp, int unit, rt_ubase_t cmd, rt_caddr_t data,
struct rt_thread *td);
typedef int tsw_param_t(struct lwp_tty *tp, struct termios *t);
typedef int tsw_modem_t(struct lwp_tty *tp, int sigon, int sigoff);
typedef int tsw_mmap_t(struct lwp_tty *tp, vm_ooffset_t offset,
vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr);
typedef void tsw_pktnotify_t(struct lwp_tty *tp, char event);
typedef void tsw_free_t(void *softc);
typedef rt_bool_t tsw_busy_t(struct lwp_tty *tp);
struct lwp_ttydevsw
{
unsigned int tsw_flags; /* Default TTY flags. */
tsw_open_t *tsw_open; /* Device opening. */
tsw_close_t *tsw_close; /* Device closure. */
tsw_outwakeup_t *tsw_outwakeup; /* Output available. */
tsw_inwakeup_t *tsw_inwakeup; /* Input can be stored again. */
tsw_ioctl_t *tsw_ioctl; /* ioctl() hooks. */
tsw_cioctl_t *tsw_cioctl; /* ioctl() on control devices. */
tsw_param_t *tsw_param; /* TIOCSETA device parameter setting. */
tsw_modem_t *tsw_modem; /* Modem sigon/sigoff. */
tsw_mmap_t *tsw_mmap; /* mmap() hooks. */
tsw_pktnotify_t *tsw_pktnotify; /* TIOCPKT events. */
tsw_free_t *tsw_free; /* Destructor. */
tsw_busy_t *tsw_busy; /* Draining output. */
void *tsw_spare[3]; /* For future use. */
};
typedef struct lwp_ttydevsw *lwp_ttydevsw_t;
struct lwp_tty
{
struct rt_device parent; /* inherit from Class:RT_Device */
struct rt_mutex *t_mtx; /* TTY lock. */
struct rt_mutex t_mtxobj; /* Per-TTY lock (when not borrowing). */
rt_list_t t_list; /* (l) TTY list entry. */
int t_drainwait; /* (t) TIOCDRAIN timeout seconds. */
unsigned int t_flags; /* (t) Terminal option flags. */
/* Keep flags in sync with db_show_tty and pstat(8). */
#define TF_NOPREFIX 0x00001 /* Don't prepend "tty" to device name. */
#define TF_INITLOCK 0x00002 /* Create init/lock state devices. */
#define TF_CALLOUT 0x00004 /* Create "cua" devices. */
#define TF_OPENED_IN 0x00008 /* "tty" node is in use. */
#define TF_OPENED_OUT 0x00010 /* "cua" node is in use. */
#define TF_OPENED_CONS 0x00020 /* Device in use as console. */
#define TF_OPENED (TF_OPENED_IN | TF_OPENED_OUT | TF_OPENED_CONS)
#define TF_GONE 0x00040 /* Device node is gone. */
#define TF_OPENCLOSE 0x00080 /* Device is in open()/close(). */
#define TF_ASYNC 0x00100 /* Asynchronous I/O enabled. */
#define TF_LITERAL 0x00200 /* Accept the next character literally. */
#define TF_HIWAT_IN 0x00400 /* We've reached the input watermark. */
#define TF_HIWAT_OUT 0x00800 /* We've reached the output watermark. */
#define TF_HIWAT (TF_HIWAT_IN | TF_HIWAT_OUT)
#define TF_STOPPED 0x01000 /* Output flow control - stopped. */
#define TF_EXCLUDE 0x02000 /* Exclusive access. */
#define TF_BYPASS 0x04000 /* Optimized input path. */
#define TF_ZOMBIE 0x08000 /* Modem disconnect received. */
#define TF_HOOK 0x10000 /* TTY has hook attached. */
#define TF_BUSY_IN 0x20000 /* Process busy in read() -- not supported. */
#define TF_BUSY_OUT 0x40000 /* Process busy in write(). */
#define TF_BUSY (TF_BUSY_IN | TF_BUSY_OUT)
unsigned int t_revokecnt; /* (t) revoke() count. */
/* Buffering mechanisms. */
struct ttyinq t_inq; /* (t) Input queue. */
size_t t_inlow; /* (t) Input low watermark. */
struct ttyoutq t_outq; /* (t) Output queue. */
size_t t_outlow; /* (t) Output low watermark. */
/* Sleeping mechanisms. */
struct rt_condvar t_inwait; /* (t) Input wait queue. */
struct rt_condvar t_outwait; /* (t) Output wait queue. */
struct rt_condvar t_outserwait; /* (t) Serial output wait queue. */
struct rt_condvar t_bgwait; /* (t) Background wait queue. */
struct rt_condvar t_dcdwait; /* (t) Carrier Detect wait queue. */
struct rt_wqueue t_inpoll; /* (t) Input poll queue. */
struct rt_wqueue t_outpoll; /* (t) Output poll queue. */
#ifdef USING_BSD_AIO
struct sigio *t_sigio; /* (t) Asynchronous I/O. */
#endif
struct termios t_termios; /* (t) I/O processing flags. */
struct winsize t_winsize; /* (t) Window size. */
unsigned int t_column; /* (t) Current cursor position. */
unsigned int t_writepos; /* (t) Where input was interrupted. */
int t_compatflags; /* (t) COMPAT_43TTY flags. */
/* Init/lock-state devices. */
struct termios t_termios_init_in; /* tty%s.init. */
struct termios t_termios_lock_in; /* tty%s.lock. */
#ifdef USING_BSD_INIT_LOCK_DEVICE
struct termios t_termios_init_out; /* cua%s.init. */
struct termios t_termios_lock_out; /* cua%s.lock. */
#endif /* USING_BSD_INIT_LOCK_DEVICE */
struct lwp_ttydevsw *t_devsw; /* (c) Driver hooks. */
#ifdef USING_BSD_HOOK
struct lwp_ttyhook *t_hook; /* (t) Capture/inject hook. */
#endif
/* Process signal delivery. */
struct rt_processgroup *t_pgrp; /* (t) Foreground process group. */
struct rt_session *t_session; /* (t) Associated session. */
unsigned int t_sessioncnt; /* (t) Backpointing sessions. */
void *t_devswsoftc; /* (c) Soft config, for drivers. */
#ifdef USING_BSD_HOOK
void *t_hooksoftc; /* (t) Soft config, for hooks. */
#endif
#ifdef USING_BSD_CHAR_DEVICE
struct cdev *t_dev; /* (c) Primary character device. */
#endif /* USING_BSD_CHAR_DEVICE */
#ifdef USING_BSD_SIGINFO
size_t t_prbufsz; /* (t) SIGINFO buffer size. */
char t_prbuf[]; /* (t) SIGINFO buffer. */
#endif /* USING_BSD_SIGINFO */
};
typedef struct lwp_tty *lwp_tty_t;
/* Allocation and deallocation. */
void tty_rel_pgrp(struct lwp_tty *tp, struct rt_processgroup *pgrp);
void tty_rel_sess(struct lwp_tty *tp, struct rt_session *sess);
void tty_rel_gone(struct lwp_tty *tp);
/* tty locking mechanism */
#define tty_getlock(tp) ((tp)->t_mtx)
#define tty_lock(tp) rt_mutex_take(tty_getlock(tp), RT_WAITING_FOREVER);
#define tty_unlock(tp) rt_mutex_release(tty_getlock(tp))
#define tty_lock_owned(tp) \
(rt_mutex_get_owner(tty_getlock(tp)) == rt_thread_self())
#define tty_lock_notrecused(tp) (rt_mutex_get_hold(tty_getlock(tp)) == 1)
#define tty_assert_locked(tp) RT_ASSERT(tty_lock_owned(tp))
#define tty_lock_assert(tp, option) \
RT_ASSERT(((option) == (MA_OWNED | MA_NOTRECURSED)) && \
(tty_lock_owned(tp) && tty_lock_notrecused(tp)))
/* System messages. */
int tty_checkoutq(struct lwp_tty *tp);
int tty_putchar(struct lwp_tty *tp, char c);
int tty_putstrn(struct lwp_tty *tp, const char *p, size_t n);
int tty_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, void *data, int fflag,
struct rt_thread *td);
int tty_ioctl_compat(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data, int fflag,
struct rt_thread *td);
void tty_set_winsize(struct lwp_tty *tp, const struct winsize *wsz);
void tty_init_console(struct lwp_tty *tp, speed_t speed);
void tty_flush(struct lwp_tty *tp, int flags);
void tty_hiwat_in_block(struct lwp_tty *tp);
void tty_hiwat_in_unblock(struct lwp_tty *tp);
dev_t tty_udev(struct lwp_tty *tp);
/* tesing on tty */
#define tty_opened(tp) ((tp)->t_flags & TF_OPENED)
#define tty_gone(tp) ((tp)->t_flags & TF_GONE)
#define tty_softc(tp) ((tp)->t_devswsoftc)
#define tty_devname(tp) ((tp)->parent.parent.name)
/**
* @brief TTY registeration on device subsystem
*
* @warning It's the duty of the caller to ensure that the name is not
* identical to any existed registered devices.
*
* @param terminal the target tty device
* @param name name of the device (must be exclusive)
* @return rt_err_t RT_EOK on success
*/
rt_err_t lwp_tty_register(lwp_tty_t terminal, const char *name);
/**
* @brief TTY allocation and deallocation. TTY devices can be deallocated when
* the driver doesn't use it anymore, when the TTY isn't a session's
* controlling TTY and when the device node isn't opened through devfs.
*
* @param handle device handle of tty
* @param softc device configuration binding on tty
* @param prefix device name prefix
* @param cutom_mtx the lock provided to protect tty
* @return lwp_tty_t NULL on failure
*/
lwp_tty_t lwp_tty_create_ext(lwp_ttydevsw_t handle, void *softc,
rt_mutex_t custom_mtx);
/**
* @brief Handful version of lwp_tty_create_ext
*
* @param softc device configuration binding on tty
* @param cutom_mtx the lock provided to protect tty
* @param prefix device name prefix
* @return lwp_tty_t NULL on failure
*/
lwp_tty_t lwp_tty_create(lwp_ttydevsw_t handle, void *softc);
void lwp_tty_delete(lwp_tty_t tp);
void lwp_tty_signal_sessleader(struct lwp_tty *tp, int sig);
void lwp_tty_signal_pgrp(struct lwp_tty *tp, int sig);
/**
* @brief Create a new pseudo-terminal multiplexer
*
* @param root_path path of root mount point of ptyfs
* @return rt_device_t new device object if succeed, otherwise NULL
*/
rt_err_t lwp_ptmx_init(rt_device_t ptmx_device, const char *root_path);
#define LWP_CONSOLE_LOWEST_PRIOR 0
#define LWP_CONSOLE_HIGHEST_PRIO INT_MAX
/**
* @brief Register an alternative backend tty device as console
*/
rt_err_t lwp_console_register_backend(struct rt_device *bakdev, int prio);
rt_inline int ttydevsw_open(struct lwp_tty *tp)
{
tty_assert_locked(tp);
MPASS(!tty_gone(tp));
return (tp->t_devsw->tsw_open(tp));
}
rt_inline void ttydevsw_close(struct lwp_tty *tp)
{
tty_assert_locked(tp);
MPASS(!tty_gone(tp));
tp->t_devsw->tsw_close(tp);
}
rt_inline void ttydevsw_outwakeup(struct lwp_tty *tp)
{
tty_assert_locked(tp);
MPASS(!tty_gone(tp));
/* Prevent spurious wakeups. */
if (ttydisc_getc_poll(tp) == 0)
return;
tp->t_devsw->tsw_outwakeup(tp);
}
rt_inline void ttydevsw_inwakeup(struct lwp_tty *tp)
{
tty_assert_locked(tp);
MPASS(!tty_gone(tp));
/* Prevent spurious wakeups. */
if (tp->t_flags & TF_HIWAT_IN)
return;
tp->t_devsw->tsw_inwakeup(tp);
}
rt_inline int ttydevsw_ioctl(struct lwp_tty *tp, rt_ubase_t cmd, rt_caddr_t data,
struct rt_thread *td)
{
tty_assert_locked(tp);
MPASS(!tty_gone(tp));
return (tp->t_devsw->tsw_ioctl(tp, cmd, data, td));
}
rt_inline int ttydevsw_cioctl(struct lwp_tty *tp, int unit, rt_ubase_t cmd,
rt_caddr_t data, struct rt_thread *td)
{
tty_assert_locked(tp);
MPASS(!tty_gone(tp));
return (tp->t_devsw->tsw_cioctl(tp, unit, cmd, data, td));
}
rt_inline int ttydevsw_param(struct lwp_tty *tp, struct termios *t)
{
MPASS(!tty_gone(tp));
return (tp->t_devsw->tsw_param(tp, t));
}
rt_inline int ttydevsw_modem(struct lwp_tty *tp, int sigon, int sigoff)
{
MPASS(!tty_gone(tp));
return (tp->t_devsw->tsw_modem(tp, sigon, sigoff));
}
rt_inline int ttydevsw_mmap(struct lwp_tty *tp, vm_ooffset_t offset,
vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr)
{
MPASS(!tty_gone(tp));
return (tp->t_devsw->tsw_mmap(tp, offset, paddr, nprot, memattr));
}
rt_inline void ttydevsw_pktnotify(struct lwp_tty *tp, char event)
{
tty_assert_locked(tp);
MPASS(!tty_gone(tp));
tp->t_devsw->tsw_pktnotify(tp, event);
}
rt_inline void ttydevsw_free(struct lwp_tty *tp)
{
MPASS(tty_gone(tp));
tp->t_devsw->tsw_free(tty_softc(tp));
}
rt_inline rt_bool_t ttydevsw_busy(struct lwp_tty *tp)
{
tty_assert_locked(tp);
MPASS(!tty_gone(tp));
return (tp->t_devsw->tsw_busy(tp));
}
rt_inline size_t ttydisc_read_poll(struct lwp_tty *tp)
{
tty_assert_locked(tp);
return ttyinq_bytescanonicalized(&tp->t_inq);
}
rt_inline size_t ttydisc_write_poll(struct lwp_tty *tp)
{
tty_assert_locked(tp);
return ttyoutq_bytesleft(&tp->t_outq);
}
#endif /* __LWP_TERMINAL_H__ */

View File

@@ -0,0 +1,136 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-11-13 Shell init ver.
*/
#ifndef __TTY_CONFIG_H__
#define __TTY_CONFIG_H__
/* default buffer size of tty siginfo */
#define LWP_TTY_PRBUF_SIZE 256
/*
* System wide defaults for terminal state.
*/
/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 1982, 1986, 1993
* The Regents of the University of California. All rights reserved.
* (c) UNIX System Laboratories, Inc.
* All or some portions of this file are derived from material licensed
* to the University of California by American Telephone and Telegraph
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
* the permission of UNIX System Laboratories, Inc.
*
* 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.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*
* @(#)ttydefaults.h 8.4 (Berkeley) 1/21/94
*/
/*
* Defaults on "first" open.
*/
#define TTYDEF_IFLAG (BRKINT | ICRNL | IMAXBEL | IXON | IXANY | IUTF8)
#define TTYDEF_OFLAG (OPOST | ONLCR)
#define TTYDEF_LFLAG_NOECHO (ICANON | ISIG | IEXTEN)
#define TTYDEF_LFLAG_ECHO \
(TTYDEF_LFLAG_NOECHO | ECHO | ECHOE | ECHOKE | ECHOCTL)
#define TTYDEF_LFLAG TTYDEF_LFLAG_ECHO
#define TTYDEF_CFLAG (CREAD | CS8 | HUPCL)
#define TTYDEF_SPEED (B9600)
/*
* Control Character Defaults
*/
/*
* XXX: A lot of code uses lowercase characters, but control-character
* conversion is actually only valid when applied to uppercase
* characters. We just treat lowercase characters as if they were
* inserted as uppercase.
*/
#define _CONTROL(c) \
((c) >= 'a' && (c) <= 'z' ? ((c) - 'a' + 1) : (((c) - 'A' + 1) & 0x7f))
#define CEOF _CONTROL('D')
#define CEOL 0xff /* XXX avoid _POSIX_VDISABLE */
#define CERASE 0x7f
#define CERASE2 _CONTROL('H')
#define CINTR _CONTROL('C')
#define CSTATUS _CONTROL('T')
#define CKILL _CONTROL('U')
#define CMIN 1
#define CQUIT _CONTROL('\\')
#define CSUSP _CONTROL('Z')
#define CTIME 0
#define CDSUSP _CONTROL('Y')
#define CSTART _CONTROL('Q')
#define CSTOP _CONTROL('S')
#define CLNEXT _CONTROL('V')
#define CDISCARD _CONTROL('O')
#define CWERASE _CONTROL('W')
#define CREPRINT _CONTROL('R')
#define CEOT CEOF
/* compat */
#define CBRK CEOL
#define CRPRNT CREPRINT
#define CFLUSH CDISCARD
/* PROTECTED INCLUSION ENDS HERE */
#endif /* !__TTY_CONFIG_H__ */
/*
* #define TTY_CONF_INCLUDE_CCHARS to include an array of default control
* characters.
*/
#ifdef TTY_CONF_INCLUDE_CCHARS
#include <rtdef.h>
#include <termios.h>
#include <unistd.h>
static const cc_t tty_ctrl_charset[NCCS] = {
[VINTR] = CINTR,
[VQUIT] = CQUIT,
[VERASE] = CERASE,
[VKILL] = CKILL,
[VEOF] = CEOF,
[VSTART] = CSTART,
[VSTOP] = CSTOP,
[VSUSP] = CSUSP,
[VREPRINT] = CREPRINT,
[VDISCARD] = CDISCARD,
[VWERASE] = CWERASE,
[VLNEXT] = CLNEXT,
[VMIN] = CMIN
#undef _CONTROL
};
#undef TTY_CONF_INCLUDE_CCHARS
#endif /* __TTY_CONFIG_H__ */

View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-12-11 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 "bsd_porting.h"
#include "terminal.h"
#include <fcntl.h>
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops cons_rtdev_ops;
#endif
struct backend_entry
{
rt_list_t bakend_list_node;
int prio;
rt_device_t bakdev;
};
static rt_list_t _bakend_list;
static void _bent_enqueue(struct backend_entry *bent)
{
struct backend_entry *idx;
rt_bool_t inserted = RT_FALSE;
rt_list_for_each_entry(idx, &_bakend_list, bakend_list_node)
{
if (idx->prio < bent->prio)
{
rt_list_insert_before(&idx->bakend_list_node, &bent->bakend_list_node);
inserted = RT_TRUE;
break;
}
}
if (!inserted)
rt_list_insert_after(&_bakend_list, &bent->bakend_list_node);
return ;
}
rt_err_t lwp_console_register_backend(struct rt_device *bakdev, int prio)
{
rt_err_t ret = RT_EOK;
struct backend_entry *bent;
bent = rt_malloc(sizeof(struct backend_entry));
if (bent)
{
rt_list_init(&bent->bakend_list_node);
bent->prio = prio;
bent->bakdev = bakdev;
_bent_enqueue(bent);
}
else
{
ret = -RT_ENOMEM;
}
return ret;
}
static struct rt_device _cons_rtdev;
static int fops_open(struct dfs_file *file)
{
return -EINVAL;
}
static struct dfs_file_ops _cons_fops = {
.open = fops_open,
};
static rt_err_t _cons_readlink(struct rt_device *dev, char *buf, int len)
{
int rc = -EIO;
struct backend_entry *bent;
if (!rt_list_isempty(&_bakend_list))
{
bent = rt_list_first_entry(&_bakend_list, struct backend_entry, bakend_list_node);
if (bent)
{
RT_ASSERT(bent->bakdev);
strncpy(buf, bent->bakdev->parent.name, MIN(len, RT_NAME_MAX));
LOG_D("%s: backend device %s", __func__, buf);
rc = 0;
}
}
if (rc != 0)
{
LOG_W("%s: No backend device", __func__);
}
return rc;
}
static int _cons_init(void)
{
rt_err_t rc;
rt_list_init(&_bakend_list);
/* setup system level device */
_cons_rtdev.type = RT_Device_Class_Char;
_cons_rtdev.ops = &cons_rtdev_ops;
rc = rt_device_register(&_cons_rtdev, "console", RT_DEVICE_FLAG_DYNAMIC);
if (rc == RT_EOK)
{
_cons_rtdev.readlink = &_cons_readlink;
_cons_rtdev.fops = &_cons_fops;
}
return rc;
}
INIT_DEVICE_EXPORT(_cons_init);

View File

@@ -0,0 +1,106 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-11-30 Shell init ver.
*/
#define DBG_TAG "lwp.ctty"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#define TTY_CONF_INCLUDE_CCHARS
#include "tty_config.h"
#include "tty_internal.h"
#include "terminal.h"
static int fops_open(struct dfs_file *file)
{
return -EINVAL;
}
static rt_err_t ctty_readlink(struct rt_device *dev, char *buf, int len)
{
int rc = -ENXIO;
lwp_tty_t tp;
rt_session_t sess;
rt_processgroup_t pgrp;
rt_lwp_t lwp;
lwp = lwp_self();
if (lwp)
{
pgrp = lwp->pgrp;
if (pgrp)
{
sess = pgrp->session;
if (sess)
{
tp = sess->ctty;
if (tp)
{
tty_lock(tp);
if (lwp->pgrp == pgrp && pgrp->session == sess && sess->ctty == tp)
{
rt_strncpy(buf, tp->parent.parent.name, len);
rc = RT_EOK;
}
tty_unlock(tp);
}
}
}
}
return rc;
}
static struct dfs_file_ops ctty_file_ops = {
.open = fops_open,
};
/* character device for tty */
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops tty_dev_ops = {
/* IO directly through device is not allowed */
};
#else
#error Must enable RT_USING_DEVICE_OPS in Kconfig
#endif
rt_inline void device_setup(rt_device_t ctty)
{
ctty->type = RT_Device_Class_Char;
#ifdef RT_USING_DEVICE_OPS
ctty->ops = &tty_dev_ops;
#else
#error Must enable RT_USING_DEVICE_OPS in Kconfig
#endif
}
/* register device to DFS */
static int lwp_ctty_register(rt_device_t ctty)
{
rt_err_t rc = -RT_ENOMEM;
const char *tty_name = "tty";
device_setup(ctty);
rc = rt_device_register(ctty, tty_name, RT_DEVICE_FLAG_DYNAMIC);
if (rc == RT_EOK)
{
ctty->readlink = &ctty_readlink;
ctty->fops = &ctty_file_ops;
}
return rc;
}
static struct rt_device ctty;
static int lwp_ctty_init(void)
{
return lwp_ctty_register(&ctty);
}
INIT_DEVICE_EXPORT(lwp_ctty_init);

View File

@@ -0,0 +1,456 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-11-13 Shell init ver.
*/
#define DBG_TAG "lwp.tty"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#define TTY_CONF_INCLUDE_CCHARS
#include "tty_config.h"
#include "tty_internal.h"
#include "terminal.h"
/* configure option: timeout of tty drain wait */
static int tty_drainwait = 5 * 60;
#define TTY_NAME_PREFIX "tty"
static char *alloc_device_name(const char *name)
{
char *tty_dev_name;
long name_buf_len = (sizeof(TTY_NAME_PREFIX) - 1) /* raw prefix */
+ rt_strlen(name) /* custom name */
+ 1; /* tailing \0 */
tty_dev_name = rt_malloc(name_buf_len);
if (tty_dev_name)
sprintf(tty_dev_name, "%s%s", TTY_NAME_PREFIX, name);
return tty_dev_name;
}
/* character device for tty */
#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops tty_dev_ops = {
/* IO directly through device is not allowed */
};
#else
#error Must enable RT_USING_DEVICE_OPS in Kconfig
#endif
static int tty_fops_open(struct dfs_file *file)
{
int rc;
lwp_tty_t tp;
rt_device_t device;
int devtype = 0; /* unused */
if (file->vnode && file->vnode->data)
{
if (file->vnode->ref_count != 1)
{
rc = 0;
}
else
{
device = (rt_device_t)file->vnode->data;
tp = rt_container_of(device, struct lwp_tty, parent);
rc = bsd_ttydev_methods.d_open(tp, file->flags, devtype,
rt_thread_self());
}
}
else
{
rc = -EINVAL;
}
return rc;
}
static int tty_fops_close(struct dfs_file *file)
{
int rc;
lwp_tty_t tp;
rt_device_t device;
int fflags = FFLAGS(file->flags);
int devtype = 0; /* unused */
if (file->vnode && file->vnode->data)
{
if (file->vnode->ref_count != 1)
{
rc = 0;
}
else
{
device = (rt_device_t)file->vnode->data;
tp = rt_container_of(device, struct lwp_tty, parent);
rc = bsd_ttydev_methods.d_close(tp, fflags, devtype, rt_thread_self());
}
}
else
{
rc = -EINVAL;
}
return rc;
}
static int tty_fops_ioctl(struct dfs_file *file, int cmd, void *arg)
{
int rc;
lwp_tty_t tp;
rt_device_t device;
if (file->vnode && file->vnode->data)
{
device = (rt_device_t)file->vnode->data;
tp = rt_container_of(device, struct lwp_tty, parent);
rc = lwp_tty_ioctl_adapter(tp, cmd, file->flags, arg, rt_thread_self());
}
else
{
rc = -EINVAL;
}
return rc;
}
static ssize_t tty_fops_read(struct dfs_file *file, void *buf, size_t count,
off_t *pos)
{
ssize_t rc = 0;
int error;
struct uio uio;
struct iovec iov;
rt_device_t device;
struct lwp_tty *tp;
int ioflags;
int oflags = file->flags;
if (file->vnode && file->vnode->data)
{
device = (rt_device_t)file->vnode->data;
tp = rt_container_of(device, struct lwp_tty, parent);
/* setup ioflags */
ioflags = 0;
if (oflags & O_NONBLOCK)
ioflags |= IO_NDELAY;
/* setup uio parameters */
iov.iov_base = (void *)buf;
iov.iov_len = count;
uio.uio_offset = file->fpos;
uio.uio_resid = count;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_rw = UIO_READ;
rc = count;
error = bsd_ttydev_methods.d_read(tp, &uio, ioflags);
rc -= uio.uio_resid;
if (error)
{
LOG_D("%s: failed to write %d bytes of data. error code %d",
__func__, uio.uio_resid, error);
rc = error;
}
/* reset file context */
file->fpos = uio.uio_offset;
}
if (rc)
LOG_D("%s(len=%d, buf=%c \"%d\")", __func__, rc, *((char *)buf),
*((char *)buf));
return rc;
}
static ssize_t tty_fops_write(struct dfs_file *file, const void *buf,
size_t count, off_t *pos)
{
ssize_t rc = 0;
int error;
struct uio uio;
struct iovec iov;
rt_device_t device;
struct lwp_tty *tp;
int ioflags;
int oflags = file->flags;
if (file->vnode && file->vnode->data)
{
device = (rt_device_t)file->vnode->data;
tp = rt_container_of(device, struct lwp_tty, parent);
/* setup ioflags */
ioflags = 0;
if (oflags & O_NONBLOCK)
ioflags |= IO_NDELAY;
/* setup uio parameters */
iov.iov_base = (void *)buf;
iov.iov_len = count;
uio.uio_offset = file->fpos;
uio.uio_resid = count;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_rw = UIO_WRITE;
rc = count;
error = bsd_ttydev_methods.d_write(tp, &uio, ioflags);
if (error)
{
rc = error;
LOG_D("%s: failed to write %d bytes of data. error code %d",
__func__, uio.uio_resid, error);
}
else
{
rc -= uio.uio_resid;
}
/* reset file context */
file->fpos = uio.uio_offset;
}
return rc;
}
static int tty_fops_flush(struct dfs_file *file)
{
return -EINVAL;
}
static off_t tty_fops_lseek(struct dfs_file *file, off_t offset, int wherece)
{
return -EINVAL;
}
static int tty_fops_truncate(struct dfs_file *file, off_t offset)
{
/**
* regarding to POSIX.1, TRUNC is not supported for tty device.
* return 0 always to make filesystem happy
*/
return 0;
}
static int tty_fops_poll(struct dfs_file *file, struct rt_pollreq *req)
{
int rc;
rt_device_t device;
struct lwp_tty *tp;
if (file->vnode && file->vnode->data)
{
device = (rt_device_t)file->vnode->data;
tp = rt_container_of(device, struct lwp_tty, parent);
rc = bsd_ttydev_methods.d_poll(tp, req, rt_thread_self());
}
else
{
rc = -1;
}
return rc;
}
static int tty_fops_mmap(struct dfs_file *file, struct lwp_avl_struct *mmap)
{
return -EINVAL;
}
static int tty_fops_lock(struct dfs_file *file, struct file_lock *flock)
{
return -EINVAL;
}
static int tty_fops_flock(struct dfs_file *file, int operation, struct file_lock *flock)
{
return -EINVAL;
}
static struct dfs_file_ops tty_file_ops = {
.open = tty_fops_open,
.close = tty_fops_close,
.ioctl = tty_fops_ioctl,
.read = tty_fops_read,
.write = tty_fops_write,
.flush = tty_fops_flush,
.lseek = tty_fops_lseek,
.truncate = tty_fops_truncate,
.poll = tty_fops_poll,
.mmap = tty_fops_mmap,
.lock = tty_fops_lock,
.flock = tty_fops_flock,
};
rt_inline void device_setup(lwp_tty_t terminal)
{
terminal->parent.type = RT_Device_Class_Char;
#ifdef RT_USING_DEVICE_OPS
terminal->parent.ops = &tty_dev_ops;
#else
#error Must enable RT_USING_DEVICE_OPS in Kconfig
#endif
}
/* register TTY device */
rt_err_t lwp_tty_register(lwp_tty_t terminal, const char *name)
{
rt_err_t rc = -RT_ENOMEM;
const char *tty_name;
char *alloc_name;
if (terminal->t_devsw->tsw_flags & TF_NOPREFIX)
{
alloc_name = RT_NULL;
tty_name = name;
}
else
{
alloc_name = alloc_device_name(name);
tty_name = alloc_name;
}
if (tty_name)
{
device_setup(terminal);
rc = rt_device_register(&terminal->parent, tty_name, 0);
if (rc == RT_EOK)
{
terminal->parent.fops = &tty_file_ops;
LOG_D("%s() /dev/%s device registered", __func__, tty_name);
}
rt_free(alloc_name);
}
return rc;
}
static void tty_init_termios(lwp_tty_t tp)
{
struct termios *t = &tp->t_termios_init_in;
t->c_cflag = TTYDEF_CFLAG;
t->c_iflag = TTYDEF_IFLAG;
t->c_lflag = TTYDEF_LFLAG;
t->c_oflag = TTYDEF_OFLAG;
t->__c_ispeed = TTYDEF_SPEED;
t->__c_ospeed = TTYDEF_SPEED;
memcpy(&t->c_cc, tty_ctrl_charset,
sizeof(tty_ctrl_charset) / sizeof(tty_ctrl_charset[0]));
#ifdef USING_BSD_INIT_LOCK_DEVICE
tp->t_termios_init_out = *t;
#endif /* USING_BSD_INIT_LOCK_DEVICE */
}
lwp_tty_t lwp_tty_create_ext(lwp_ttydevsw_t handle, void *softc,
rt_mutex_t custom_mtx)
{
lwp_tty_t tp;
tp = rt_calloc(1, sizeof(struct lwp_tty)
#ifdef USING_BSD_SIGINFO
+ LWP_TTY_PRBUF_SIZE
#endif
);
if (!tp)
return tp;
bsd_devsw_init(handle);
#ifdef USING_BSD_SIGINFO
tp->t_prbufsz = LWP_TTY_PRBUF_SIZE;
#endif
tp->t_devsw = handle;
tp->t_devswsoftc = softc;
tp->t_flags = handle->tsw_flags;
tp->t_drainwait = tty_drainwait;
tty_init_termios(tp);
cv_init(&tp->t_inwait, "ttyin");
cv_init(&tp->t_outwait, "ttyout");
cv_init(&tp->t_outserwait, "ttyosr");
cv_init(&tp->t_bgwait, "ttybg");
cv_init(&tp->t_dcdwait, "ttydcd");
rt_wqueue_init(&tp->t_inpoll);
rt_wqueue_init(&tp->t_outpoll);
/* Allow drivers to use a custom mutex to lock the TTY. */
if (custom_mtx != NULL)
{
tp->t_mtx = custom_mtx;
}
else
{
tp->t_mtx = &tp->t_mtxobj;
rt_mutex_init(&tp->t_mtxobj, "ttydev", RT_IPC_FLAG_PRIO);
}
#ifdef USING_BSD_POLL
knlist_init_mtx(&tp->t_inpoll.si_note, tp->t_mtx);
knlist_init_mtx(&tp->t_outpoll.si_note, tp->t_mtx);
#endif
return tp;
}
lwp_tty_t lwp_tty_create(lwp_ttydevsw_t handle, void *softc)
{
return lwp_tty_create_ext(handle, softc, NULL);
}
void lwp_tty_delete(lwp_tty_t tp)
{
/*
* ttyydev_leave() usually frees the i/o queues earlier, but it is
* not always called between queue allocation and here. The queues
* may be allocated by ioctls on a pty control device without the
* corresponding pty slave device ever being open, or after it is
* closed.
*/
ttyinq_free(&tp->t_inq);
ttyoutq_free(&tp->t_outq);
rt_wqueue_wakeup_all(&tp->t_inpoll, (void *)POLLHUP);
rt_wqueue_wakeup_all(&tp->t_outpoll, (void *)POLLHUP);
#ifdef USING_BSD_POLL
knlist_destroy(&tp->t_inpoll.si_note);
knlist_destroy(&tp->t_outpoll.si_note);
#endif
cv_destroy(&tp->t_inwait);
cv_destroy(&tp->t_outwait);
cv_destroy(&tp->t_bgwait);
cv_destroy(&tp->t_dcdwait);
cv_destroy(&tp->t_outserwait);
if (tp->t_mtx == &tp->t_mtxobj)
rt_mutex_detach(&tp->t_mtxobj);
ttydevsw_free(tp);
rt_device_unregister(&tp->parent);
rt_free(tp);
}
/*
* Report on state of foreground process group.
*/
void tty_info(struct lwp_tty *tp)
{
/* TODO */
return;
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-11-13 Shell init ver.
*/
#ifndef __LWP_TTY_INTERNAL_H__
#define __LWP_TTY_INTERNAL_H__
#include "lwp.h"
#include "terminal.h"
extern struct cdevsw bsd_ttydev_methods;
extern struct bsd_fileops bsd_ptsdev_methods;
/* bsd devsw porting */
void bsd_devsw_init(struct lwp_ttydevsw *tsw);
/**
* Do not assert RTS or DTR automatically. If CNO_RTSDTR is set then the RTS and
* DTR lines will not be asserted when the device is opened. As a result, this
* flag is only useful on initial-state devices.
*
* Note: this feature is not using on smart system, so this flag is always 0.
*/
#define CNO_RTSDTR 0
/* Waking up readers/writers. */
int tty_wait(struct lwp_tty *tp, struct rt_condvar *cv);
int tty_wait_background(struct lwp_tty *tp, struct rt_thread *td, int sig);
int tty_timedwait(struct lwp_tty *tp, struct rt_condvar *cv, rt_tick_t timeout);
void tty_wakeup(struct lwp_tty *tp, int flags);
void tty_info(struct lwp_tty *tp);
void pts_set_lock(lwp_tty_t pts, rt_bool_t is_lock);
rt_bool_t pts_is_locked(lwp_tty_t pts);
int pts_get_pktmode(lwp_tty_t pts);
int pts_alloc(int fflags, struct rt_thread *td, struct dfs_file *ptm_file);
int lwp_tty_ioctl_adapter(lwp_tty_t tp, int cmd, int oflags, void *args, rt_thread_t td);
int lwp_tty_set_ctrl_proc(lwp_tty_t tp, rt_thread_t td);
int lwp_tty_assign_foreground(lwp_tty_t tp, rt_thread_t td, int pgid);
int lwp_tty_bg_stop(struct lwp_tty *tp, struct rt_condvar *cv);
rt_inline rt_bool_t is_sess_leader(rt_lwp_t p)
{
/**
* Note: a pgrp leader is never lose its group, so once it's
* true then it's always true
*/
return p->pid == p->sid;
}
rt_inline int tty_is_ctty(struct lwp_tty *tp, struct rt_lwp *p)
{
tty_assert_locked(tp);
return p->pgrp->session == tp->t_session && p->term_ctrlterm;
}
#endif /* __LWP_TTY_INTERNAL_H__ */

View File

@@ -0,0 +1,350 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-12-07 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 "bsd_porting.h"
#include "terminal.h"
#include <fcntl.h>
static struct dfs_file_ops ptm_fops;
static int ptm_fops_open(struct dfs_file *file)
{
int rc;
rt_uint32_t oflags = file->flags;
rt_thread_t cur_thr = rt_thread_self();
/* we don't check refcnt because each open will create a new device */
if (file->vnode && file->vnode->data)
{
/**
* Filter out illegal flags
*/
if ((oflags & ~(O_RDWR | O_NOCTTY | O_CLOEXEC | O_LARGEFILE)) == 0)
{
rc = pts_alloc(FFLAGS(oflags & O_ACCMODE), cur_thr, file);
/* detached operation from devfs */
if (rc == 0)
file->vnode->fops = &ptm_fops;
}
else
{
rc = -EINVAL;
}
}
else
{
rc = -EINVAL;
}
return rc;
}
static int ptm_fops_close(struct dfs_file *file)
{
int rc;
lwp_tty_t tp;
rt_device_t device;
if (file->data)
{
if (file->vnode->ref_count != 1)
{
rc = 0;
}
else
{
device = (rt_device_t)file->data;
tp = rt_container_of(device, struct lwp_tty, parent);
rc = bsd_ptsdev_methods.fo_close(tp, rt_thread_self());
}
}
else
{
rc = -EINVAL;
}
return rc;
}
static ssize_t ptm_fops_read(struct dfs_file *file, void *buf, size_t count,
off_t *pos)
{
ssize_t rc = 0;
int error;
struct uio uio;
struct iovec iov;
rt_device_t device;
struct lwp_tty *tp;
int oflags = file->flags;
if (file->data)
{
device = (rt_device_t)file->data;
tp = rt_container_of(device, struct lwp_tty, parent);
/* setup uio parameters */
iov.iov_base = (void *)buf;
iov.iov_len = count;
uio.uio_offset = file->fpos;
uio.uio_resid = count;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_rw = UIO_READ;
rc = count;
error =
bsd_ptsdev_methods.fo_read(tp, &uio, 0, oflags, rt_thread_self());
rc -= uio.uio_resid;
if (error)
{
rc = error;
}
/* reset file context */
file->fpos = uio.uio_offset;
}
return rc;
}
static ssize_t ptm_fops_write(struct dfs_file *file, const void *buf,
size_t count, off_t *pos)
{
ssize_t rc = 0;
int error;
struct uio uio;
struct iovec iov;
rt_device_t device;
struct lwp_tty *tp;
int oflags = file->flags;
if (file->data)
{
device = (rt_device_t)file->data;
tp = rt_container_of(device, struct lwp_tty, parent);
/* setup uio parameters */
iov.iov_base = (void *)buf;
iov.iov_len = count;
uio.uio_offset = file->fpos;
uio.uio_resid = count;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_rw = UIO_WRITE;
rc = count;
error =
bsd_ptsdev_methods.fo_write(tp, &uio, 0, oflags, rt_thread_self());
if (error)
{
rc = error;
}
else
{
rc -= uio.uio_resid;
}
/* reset file context */
file->fpos = uio.uio_offset;
}
return rc;
}
static int ptm_fops_ioctl(struct dfs_file *file, int cmd, void *arg)
{
int rc;
lwp_tty_t tp;
rt_device_t device;
rt_ubase_t cmd_normal = (unsigned int)cmd;
if (file->data)
{
device = (rt_device_t)file->data;
tp = rt_container_of(device, struct lwp_tty, parent);
switch (cmd_normal)
{
case TIOCSPTLCK:
{
int is_lock;
if (lwp_get_from_user(&is_lock, arg, sizeof(int)) != sizeof(int))
return -EFAULT;
pts_set_lock(tp, is_lock);
rc = 0;
}
break;
case TIOCGPTLCK:
{
int is_lock = pts_is_locked(tp);
if (lwp_put_to_user(arg, &is_lock, sizeof(int)) != sizeof(int))
return -EFAULT;
rc = 0;
}
break;
case TIOCGPKT:
{
int pktmode = pts_get_pktmode(tp);
if (lwp_put_to_user(arg, &pktmode, sizeof(int)) != sizeof(int))
return -EFAULT;
rc = 0;
}
break;
default:
rc = bsd_ptsdev_methods.fo_ioctl(
tp, cmd_normal, arg, 0, FFLAGS(file->flags), rt_thread_self());
break;
}
}
else
{
rc = -EINVAL;
}
return rc;
}
static int ptm_fops_flush(struct dfs_file *file)
{
return -EINVAL;
}
static off_t ptm_fops_lseek(struct dfs_file *file, off_t offset, int wherece)
{
return -EINVAL;
}
static int ptm_fops_truncate(struct dfs_file *file, off_t offset)
{
return -EINVAL;
}
static int ptm_fops_poll(struct dfs_file *file, struct rt_pollreq *req)
{
int rc;
rt_device_t device;
struct lwp_tty *tp;
if (file->data)
{
device = (rt_device_t)file->data;
tp = rt_container_of(device, struct lwp_tty, parent);
rc = bsd_ptsdev_methods.fo_poll(tp, req, 0, rt_thread_self());
}
else
{
rc = -1;
}
return rc;
}
static int ptm_fops_mmap(struct dfs_file *file, struct lwp_avl_struct *mmap)
{
return -EINVAL;
}
static int ptm_fops_lock(struct dfs_file *file, struct file_lock *flock)
{
return -EINVAL;
}
static int ptm_fops_flock(struct dfs_file *file, int operation, struct file_lock *flock)
{
return -EINVAL;
}
static struct dfs_file_ops ptm_fops = {
.open = ptm_fops_open,
.close = ptm_fops_close,
.ioctl = ptm_fops_ioctl,
.read = ptm_fops_read,
.write = ptm_fops_write,
.flush = ptm_fops_flush,
.lseek = ptm_fops_lseek,
.truncate = ptm_fops_truncate,
.poll = ptm_fops_poll,
.mmap = ptm_fops_mmap,
.lock = ptm_fops_lock,
.flock = ptm_fops_flock,
};
rt_err_t lwp_ptmx_init(rt_device_t ptmx_device, const char *root_path)
{
char *device_name;
int root_len;
const char *dev_rel_path;
rt_err_t rc;
root_len = strlen(root_path);
dev_rel_path = "/ptmx";
device_name = rt_malloc(root_len + sizeof("/ptmx"));
if (device_name)
{
/* Register device */
sprintf(device_name, "%s%s", root_path, dev_rel_path);
rt_device_register(ptmx_device, device_name, 0);
/* Setup fops */
ptmx_device->fops = &ptm_fops;
rt_free(device_name);
rc = RT_EOK;
}
else
{
rc = -RT_ENOMEM;
}
return rc;
}
/* system level ptmx */
static struct rt_device sysptmx;
static struct dfs_file_ops sysptmx_file_ops;
static rt_err_t sysptmx_readlink(struct rt_device *dev, char *buf, int len)
{
int rc = 0;
/* TODO: support multi-root ? */
strncpy(buf, "pts/ptmx", len);
return rc;
}
static int _sys_ptmx_init(void)
{
rt_err_t rc;
rt_device_t sysptmx_rtdev = &sysptmx;
/* setup system level device */
sysptmx_rtdev->type = RT_Device_Class_Char;
sysptmx_rtdev->ops = RT_NULL;
rc = rt_device_register(sysptmx_rtdev, "ptmx", RT_DEVICE_FLAG_DYNAMIC);
if (rc == RT_EOK)
{
sysptmx_rtdev->readlink = &sysptmx_readlink;
sysptmx_rtdev->fops = &sysptmx_file_ops;
}
return rc;
}
INIT_DEVICE_EXPORT(_sys_ptmx_init);