74 changes: 74 additions & 0 deletions libc/spec/posix.td
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ def PosixSpawnFileActionsTRestrictedPtr : RestrictedPtrType<PosixSpawnFileAction
def PosixSpawnAttrT : NamedType<"posix_spawnattr_t">;
def RestrictedPosixSpawnAttrTPtrType : RestrictedPtrType<PosixSpawnAttrT>;

def CcT : NamedType<"cc_t">;
def SpeedT : NamedType<"speed_t">;
def StructTermios : NamedType<"struct termios">;
def StructTermiosPtr : PtrType<StructTermios>;
def ConstStructTermiosPtr : ConstType<StructTermiosPtr>;
def TcFlagT : NamedType<"tcflag_t">;

def POSIX : StandardSpec<"POSIX"> {
PtrType CharPtr = PtrType<CharType>;
RestrictedPtrType RestrictedCharPtr = RestrictedPtrType<CharType>;
Expand Down Expand Up @@ -1100,6 +1107,72 @@ def POSIX : StandardSpec<"POSIX"> {
]
>;

HeaderSpec Termios = HeaderSpec<
"termios.h",
[
Macro<"NCCS">,
],
[CcT, PidT, SpeedT, StructTermios, TcFlagT], // Types
[], // Enumerations
[
FunctionSpec<
"cfgetispeed",
RetValSpec<SpeedT>,
[ArgSpec<ConstStructTermiosPtr>]
>,
FunctionSpec<
"cfgetospeed",
RetValSpec<SpeedT>,
[ArgSpec<ConstStructTermiosPtr>]
>,
FunctionSpec<
"cfsetispeed",
RetValSpec<SpeedT>,
[ArgSpec<StructTermiosPtr>, ArgSpec<SpeedT>]
>,
FunctionSpec<
"cfsetospeed",
RetValSpec<SpeedT>,
[ArgSpec<StructTermiosPtr>, ArgSpec<SpeedT>]
>,
FunctionSpec<
"tcdrain",
RetValSpec<IntType>,
[ArgSpec<IntType>]
>,
FunctionSpec<
"tcflow",
RetValSpec<IntType>,
[ArgSpec<IntType>, ArgSpec<IntType>]
>,
FunctionSpec<
"tcflush",
RetValSpec<IntType>,
[ArgSpec<IntType>, ArgSpec<IntType>]
>,
FunctionSpec<
"tcgetattr",
RetValSpec<IntType>,
[ArgSpec<IntType>, ArgSpec<StructTermiosPtr>]
>,
FunctionSpec<
"tcgetsid",
RetValSpec<PidT>,
[ArgSpec<IntType>]
>,
FunctionSpec<
"tcsendbreak",
RetValSpec<IntType>,
[ArgSpec<IntType>, ArgSpec<IntType>]
>,
FunctionSpec<
"tcsetattr",
RetValSpec<IntType>,
[ArgSpec<IntType>, ArgSpec<IntType>, ArgSpec<StructTermiosPtr>]
>,
]
>;

let Headers = [
CType,
Dirent,
Expand All @@ -1117,6 +1190,7 @@ def POSIX : StandardSpec<"POSIX"> {
SysUtsName,
SysWait,
Time,
Termios,
UniStd,
String
];
Expand Down
1 change: 1 addition & 0 deletions libc/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ if(${LIBC_TARGET_OS} STREQUAL "linux")
add_subdirectory(pthread)
add_subdirectory(sched)
add_subdirectory(sys)
add_subdirectory(termios)
add_subdirectory(unistd)
endif()

Expand Down
80 changes: 80 additions & 0 deletions libc/src/termios/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
endif()

add_entrypoint_object(
cfgetispeed
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.cfgetispeed
)

add_entrypoint_object(
cfsetispeed
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.cfsetispeed
)

add_entrypoint_object(
cfgetospeed
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.cfgetospeed
)

add_entrypoint_object(
cfsetospeed
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.cfsetospeed
)

add_entrypoint_object(
tcgetsid
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.tcgetsid
)

add_entrypoint_object(
tcdrain
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.tcdrain
)

add_entrypoint_object(
tcflow
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.tcflow
)

add_entrypoint_object(
tcflush
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.tcflush
)

add_entrypoint_object(
tcsendbreak
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.tcsendbreak
)

add_entrypoint_object(
tcgetattr
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.tcgetattr
)

add_entrypoint_object(
tcsetattr
ALIAS
DEPENDS
.${LIBC_TARGET_OS}.tcsetattr
)
20 changes: 20 additions & 0 deletions libc/src/termios/cfgetispeed.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for cfgetispeed -------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_UNISTD_CFGETISPEED_H
#define LLVM_LIBC_SRC_UNISTD_CFGETISPEED_H

#include <termios.h>

namespace __llvm_libc {

speed_t cfgetispeed(const struct termios *t);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_UNISTD_CFGETISPEED_H
20 changes: 20 additions & 0 deletions libc/src/termios/cfgetospeed.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for cfgetospeed -------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_UNISTD_CFGETOSPEED_H
#define LLVM_LIBC_SRC_UNISTD_CFGETOSPEED_H

#include <termios.h>

namespace __llvm_libc {

speed_t cfgetospeed(const struct termios *t);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_UNISTD_CFGETOSPEED_H
20 changes: 20 additions & 0 deletions libc/src/termios/cfsetispeed.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for cfsetispeed -------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_UNISTD_CFSETISPEED_H
#define LLVM_LIBC_SRC_UNISTD_CFSETISPEED_H

#include <termios.h>

namespace __llvm_libc {

int cfsetispeed(struct termios *t, speed_t speed);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_UNISTD_CFSETISPEED_H
20 changes: 20 additions & 0 deletions libc/src/termios/cfsetospeed.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for cfsetospeed -------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_UNISTD_CFSETOSPEED_H
#define LLVM_LIBC_SRC_UNISTD_CFSETOSPEED_H

#include <termios.h>

namespace __llvm_libc {

int cfsetospeed(struct termios *t, speed_t speed);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_UNISTD_CFSETOSPEED_H
144 changes: 144 additions & 0 deletions libc/src/termios/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# There is no difference between input and output speeds on Linux.
# However, since POSIX requires separate functions for setting and getting
# of the input and output speeds, we use different entrypoints wiht the
# same getter/setter logic.
add_entrypoint_object(
cfgetispeed
SRCS
cfgetispeed.cpp
HDRS
../cfgetispeed.h
DEPENDS
libc.include.termios
)

add_entrypoint_object(
cfsetispeed
SRCS
cfsetispeed.cpp
HDRS
../cfsetispeed.h
DEPENDS
libc.include.termios
libc.src.errno.errno
)

add_entrypoint_object(
cfgetospeed
SRCS
cfgetospeed.cpp
HDRS
../cfgetospeed.h
DEPENDS
libc.include.termios
)

add_entrypoint_object(
cfsetospeed
SRCS
cfsetospeed.cpp
HDRS
../cfsetospeed.h
DEPENDS
libc.include.termios
libc.src.errno.errno
)

add_entrypoint_object(
tcgetsid
SRCS
tcgetsid.cpp
HDRS
../tcgetsid.h
DEPENDS
libc.include.sys_syscall
libc.include.termios
libc.src.__support.OSUtil.osutil
libc.src.errno.errno
)

add_entrypoint_object(
tcdrain
SRCS
tcdrain.cpp
HDRS
../tcdrain.h
DEPENDS
libc.include.sys_syscall
libc.include.termios
libc.src.__support.OSUtil.osutil
libc.src.errno.errno
)

add_entrypoint_object(
tcflush
SRCS
tcflush.cpp
HDRS
../tcflush.h
DEPENDS
libc.include.sys_syscall
libc.include.termios
libc.src.__support.OSUtil.osutil
libc.src.errno.errno
)

add_entrypoint_object(
tcflow
SRCS
tcflow.cpp
HDRS
../tcflow.h
DEPENDS
libc.include.sys_syscall
libc.include.termios
libc.src.__support.OSUtil.osutil
libc.src.errno.errno
)

add_entrypoint_object(
tcsendbreak
SRCS
tcsendbreak.cpp
HDRS
../tcsendbreak.h
DEPENDS
libc.include.sys_syscall
libc.include.termios
libc.src.__support.OSUtil.osutil
libc.src.errno.errno
)

add_header_library(
kernel_termios
HDRS
kernel_termios.h
)

add_entrypoint_object(
tcgetattr
SRCS
tcgetattr.cpp
HDRS
../tcgetattr.h
DEPENDS
.kernel_termios
libc.include.sys_syscall
libc.include.termios
libc.src.__support.OSUtil.osutil
libc.src.errno.errno
)

add_entrypoint_object(
tcsetattr
SRCS
tcsetattr.cpp
HDRS
../tcsetattr.h
DEPENDS
.kernel_termios
libc.include.sys_syscall
libc.include.termios
libc.src.__support.OSUtil.osutil
libc.src.errno.errno
)
21 changes: 21 additions & 0 deletions libc/src/termios/linux/cfgetispeed.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Linux implementation of cfgetispeed -------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/termios/cfgetispeed.h"

#include "src/__support/common.h"

#include <termios.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(speed_t, cfgetispeed, (const struct termios *t)) {
return t->c_cflag & CBAUD;
}

} // namespace __llvm_libc
21 changes: 21 additions & 0 deletions libc/src/termios/linux/cfgetospeed.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//===-- Linux implementation of cfgetospeed -------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/termios/cfgetospeed.h"

#include "src/__support/common.h"

#include <termios.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(speed_t, cfgetospeed, (const struct termios *t)) {
return t->c_cflag & CBAUD;
}

} // namespace __llvm_libc
31 changes: 31 additions & 0 deletions libc/src/termios/linux/cfsetispeed.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//===-- Linux implementation of cfsetispeed -------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/termios/cfsetispeed.h"

#include "src/__support/common.h"

#include <errno.h>
#include <termios.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, cfsetispeed, (struct termios * t, speed_t speed)) {
constexpr speed_t NOT_SPEED_MASK = ~speed_t(CBAUD);
// A speed value is valid only if it is equal to one of the B<NN+> values.
if (t == nullptr || ((speed & NOT_SPEED_MASK) != 0)) {
errno = EINVAL;
return -1;
}

t->c_cflag = (t->c_cflag & NOT_SPEED_MASK) | speed;
t->c_ispeed = speed;
return 0;
}

} // namespace __llvm_libc
31 changes: 31 additions & 0 deletions libc/src/termios/linux/cfsetospeed.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//===-- Linux implementation of cfsetospeed -------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/termios/cfsetospeed.h"

#include "src/__support/common.h"

#include <errno.h>
#include <termios.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, cfsetospeed, (struct termios * t, speed_t speed)) {
constexpr speed_t NOT_SPEED_MASK = ~speed_t(CBAUD);
// A speed value is valid only if it is equal to one of the B<NN+> values.
if (t == nullptr || ((speed & NOT_SPEED_MASK) != 0)) {
errno = EINVAL;
return -1;
}

t->c_cflag = (t->c_cflag & NOT_SPEED_MASK) | speed;
t->c_ospeed = speed;
return 0;
}

} // namespace __llvm_libc
41 changes: 41 additions & 0 deletions libc/src/termios/linux/kernel_termios.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//===-- Definition of kernel's version of struct termios --------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_TERMIOS_LINUX_KERNEL_TERMIOS_H
#define LLVM_LIBC_SRC_TERMIOS_LINUX_KERNEL_TERMIOS_H

#include <stddef.h>
#include <termios.h>

namespace __llvm_libc {

// The kernel's struct termios is different from the libc's struct termios. The
// kernel's syscalls expect the size and layout of its definition of struct
// termios. So, we define a flavor of struct termios which matches that of the
// kernel so that we can translate between the libc version and the kernel
// version when passing struct termios objects to syscalls.

// NOTE: The definitions here are generic definitions valid for most target
// architectures including x86_64 and aarch64. Definitions on some architectures
// deviate from these generic definitions. Adjustments have to be made for those
// architectures.

constexpr size_t KERNEL_NCCS = 19;

struct kernel_termios {
tcflag_t c_iflag;
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lflag;
cc_t c_line;
cc_t c_cc[KERNEL_NCCS];
};

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_TERMIOS_LINUX_KERNEL_TERMIOS_H
30 changes: 30 additions & 0 deletions libc/src/termios/linux/tcdrain.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//===-- Linux implementation of tcdrain -----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/termios/tcdrain.h"

#include "src/__support/OSUtil/syscall.h"
#include "src/__support/common.h"

#include <asm/ioctls.h> // Safe to include without the risk of name pollution.
#include <errno.h>
#include <sys/syscall.h> // For syscall numbers
#include <termios.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, tcdrain, (int fd)) {
long ret = __llvm_libc::syscall_impl(SYS_ioctl, fd, TCSBRK, 1);
if (ret < 0) {
errno = -ret;
return -1;
}
return 0;
}

} // namespace __llvm_libc
30 changes: 30 additions & 0 deletions libc/src/termios/linux/tcflow.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//===-- Linux implementation of tcflow -----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/termios/tcflow.h"

#include "src/__support/OSUtil/syscall.h"
#include "src/__support/common.h"

#include <asm/ioctls.h> // Safe to include without the risk of name pollution.
#include <errno.h>
#include <sys/syscall.h> // For syscall numbers
#include <termios.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, tcflow, (int fd, int action)) {
long ret = __llvm_libc::syscall_impl(SYS_ioctl, fd, TCXONC, action);
if (ret < 0) {
errno = -ret;
return -1;
}
return 0;
}

} // namespace __llvm_libc
30 changes: 30 additions & 0 deletions libc/src/termios/linux/tcflush.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//===-- Linux implementation of tcflush -----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/termios/tcflush.h"

#include "src/__support/OSUtil/syscall.h"
#include "src/__support/common.h"

#include <asm/ioctls.h> // Safe to include without the risk of name pollution.
#include <errno.h>
#include <sys/syscall.h> // For syscall numbers
#include <termios.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, tcflush, (int fd, int queue_selector)) {
long ret = __llvm_libc::syscall_impl(SYS_ioctl, fd, TCFLSH, queue_selector);
if (ret < 0) {
errno = -ret;
return -1;
}
return 0;
}

} // namespace __llvm_libc
46 changes: 46 additions & 0 deletions libc/src/termios/linux/tcgetattr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//===-- Linux implementation of tcgetattr ---------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/termios/tcgetattr.h"
#include "kernel_termios.h"

#include "src/__support/OSUtil/syscall.h"
#include "src/__support/common.h"

#include <asm/ioctls.h> // Safe to include without the risk of name pollution.
#include <errno.h>
#include <sys/syscall.h> // For syscall numbers
#include <termios.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, tcgetattr, (int fd, struct termios *t)) {
__llvm_libc::kernel_termios kt;
long ret = __llvm_libc::syscall_impl(SYS_ioctl, fd, TCGETS, &kt);
if (ret < 0) {
errno = -ret;
return -1;
}
t->c_iflag = kt.c_iflag;
t->c_oflag = kt.c_oflag;
t->c_cflag = kt.c_cflag;
t->c_lflag = kt.c_lflag;
t->c_ispeed = kt.c_cflag & CBAUD;
t->c_ospeed = kt.c_cflag & CBAUD;

size_t nccs = KERNEL_NCCS <= NCCS ? KERNEL_NCCS : NCCS;
for (size_t i = 0; i < nccs; ++i)
t->c_cc[i] = kt.c_cc[i];
if (NCCS > nccs) {
for (size_t i = nccs; i < NCCS; ++i)
t->c_cc[i] = 0;
}
return 0;
}

} // namespace __llvm_libc
31 changes: 31 additions & 0 deletions libc/src/termios/linux/tcgetsid.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//===-- Linux implementation of tcgetsid ----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/termios/tcgetsid.h"

#include "src/__support/OSUtil/syscall.h"
#include "src/__support/common.h"

#include <asm/ioctls.h> // Safe to include without the risk of name pollution.
#include <errno.h>
#include <sys/syscall.h> // For syscall numbers
#include <termios.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(pid_t, tcgetsid, (int fd)) {
pid_t sid;
long ret = __llvm_libc::syscall_impl(SYS_ioctl, fd, TIOCGSID, &sid);
if (ret < 0) {
errno = -ret;
return -1;
}
return sid;
}

} // namespace __llvm_libc
33 changes: 33 additions & 0 deletions libc/src/termios/linux/tcsendbreak.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===-- Linux implementation of tcsendbreak -------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/termios/tcsendbreak.h"

#include "src/__support/OSUtil/syscall.h"
#include "src/__support/common.h"

#include <asm/ioctls.h> // Safe to include without the risk of name pollution.
#include <errno.h>
#include <sys/syscall.h> // For syscall numbers
#include <termios.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(pid_t, tcsendbreak, (int fd, int /* unused duration */)) {
// POSIX leaves the behavior for non-zero duration implementation dependent.
// Which means that the behavior can be the same as it is when duration is
// zero. So, we just pass zero to the syscall.
long ret = __llvm_libc::syscall_impl(SYS_ioctl, fd, TCSBRK, 0);
if (ret < 0) {
errno = -ret;
return -1;
}
return 0;
}

} // namespace __llvm_libc
62 changes: 62 additions & 0 deletions libc/src/termios/linux/tcsetattr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//===-- Linux implementation of tcsetattr ---------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/termios/tcsetattr.h"
#include "kernel_termios.h"

#include "src/__support/OSUtil/syscall.h"
#include "src/__support/common.h"

#include <asm/ioctls.h> // Safe to include without the risk of name pollution.
#include <errno.h>
#include <sys/syscall.h> // For syscall numbers
#include <termios.h>

namespace __llvm_libc {

LLVM_LIBC_FUNCTION(int, tcsetattr,
(int fd, int actions, const struct termios *t)) {
struct kernel_termios kt;
long cmd;

switch (actions) {
case TCSANOW:
cmd = TCSETS;
break;
case TCSADRAIN:
cmd = TCSETSW;
break;
case TCSAFLUSH:
cmd = TCSETSF;
break;
default:
errno = EINVAL;
return -1;
}

kt.c_iflag = t->c_iflag;
kt.c_oflag = t->c_oflag;
kt.c_cflag = t->c_cflag;
kt.c_lflag = t->c_lflag;
size_t nccs = KERNEL_NCCS <= NCCS ? KERNEL_NCCS : NCCS;
for (size_t i = 0; i < nccs; ++i)
kt.c_cc[i] = t->c_cc[i];
if (nccs < KERNEL_NCCS) {
for (size_t i = nccs; i < KERNEL_NCCS; ++i)
kt.c_cc[i] = 0;
}

long ret = __llvm_libc::syscall_impl(SYS_ioctl, fd, cmd, &kt);
if (ret < 0) {
errno = -ret;
return -1;
}
return 0;
}

} // namespace __llvm_libc
20 changes: 20 additions & 0 deletions libc/src/termios/tcdrain.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for tcdrain -----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_UNISTD_TCDRAIN_H
#define LLVM_LIBC_SRC_UNISTD_TCDRAIN_H

#include <termios.h>

namespace __llvm_libc {

int tcdrain(int fd);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_UNISTD_TCDRAIN_H
20 changes: 20 additions & 0 deletions libc/src/termios/tcflow.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for tcflow ------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_UNISTD_TCFLOW_H
#define LLVM_LIBC_SRC_UNISTD_TCFLOW_H

#include <termios.h>

namespace __llvm_libc {

int tcflow(int fd, int action);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_UNISTD_TCFLOW_H
20 changes: 20 additions & 0 deletions libc/src/termios/tcflush.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for tcflush -----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_UNISTD_TCFLUSH_H
#define LLVM_LIBC_SRC_UNISTD_TCFLUSH_H

#include <termios.h>

namespace __llvm_libc {

int tcflush(int fd, int queue_selector);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_UNISTD_TCFLUSH_H
20 changes: 20 additions & 0 deletions libc/src/termios/tcgetattr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for tcgetattr ---------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_TERMIOS_TCGETATTR_H
#define LLVM_LIBC_SRC_TERMIOS_TCGETATTR_H

#include <termios.h>

namespace __llvm_libc {

int tcgetattr(int fd, struct termios *t);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_TERMIOS_TCGETATTR_H
20 changes: 20 additions & 0 deletions libc/src/termios/tcgetsid.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for tcgetsid ----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_TERMIOS_TCGETSID_H
#define LLVM_LIBC_SRC_TERMIOS_TCGETSID_H

#include <termios.h>

namespace __llvm_libc {

pid_t tcgetsid(int fd);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_TERMIOS_TCGETSID_H
20 changes: 20 additions & 0 deletions libc/src/termios/tcsendbreak.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for tcsendbreak -------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_UNISTD_TCSENDBREAK_H
#define LLVM_LIBC_SRC_UNISTD_TCSENDBREAK_H

#include <termios.h>

namespace __llvm_libc {

int tcsendbreak(int fd, int duration);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_UNISTD_TCSENDBREAK_H
20 changes: 20 additions & 0 deletions libc/src/termios/tcsetattr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//===-- Implementation header for tcsetattr ---------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC_TERMIOS_TCSETATTR_H
#define LLVM_LIBC_SRC_TERMIOS_TCSETATTR_H

#include <termios.h>

namespace __llvm_libc {

int tcsetattr(int fd, int actions, const struct termios *t);

} // namespace __llvm_libc

#endif // LLVM_LIBC_SRC_TERMIOS_TCSETATTR_H
1 change: 1 addition & 0 deletions libc/test/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ if(${LIBC_TARGET_OS} STREQUAL "linux")
add_subdirectory(fcntl)
add_subdirectory(sched)
add_subdirectory(sys)
add_subdirectory(termios)
add_subdirectory(unistd)
endif()

Expand Down
22 changes: 22 additions & 0 deletions libc/test/src/termios/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
add_libc_testsuite(libc_termios_unittests)

add_libc_unittest(
termios_test
SUITE
libc_termios_unittests
SRCS
termios_test.cpp
DEPENDS
libc.include.errno
libc.include.termios
libc.src.fcntl.open
libc.src.termios.cfgetispeed
libc.src.termios.cfgetospeed
libc.src.termios.cfsetispeed
libc.src.termios.cfsetospeed
libc.src.termios.tcgetattr
libc.src.termios.tcgetsid
libc.src.termios.tcsetattr
libc.src.unistd.close
libc.test.errno_setter_matcher
)
61 changes: 61 additions & 0 deletions libc/test/src/termios/termios_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//===-- Unittests for a bunch of functions in termios.h -------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "src/fcntl/open.h"
#include "src/termios/cfgetispeed.h"
#include "src/termios/cfgetospeed.h"
#include "src/termios/cfsetispeed.h"
#include "src/termios/cfsetospeed.h"
#include "src/termios/tcgetattr.h"
#include "src/termios/tcgetsid.h"
#include "src/termios/tcsetattr.h"
#include "src/unistd/close.h"
#include "test/ErrnoSetterMatcher.h"
#include "utils/UnitTest/Test.h"

#include <errno.h>
#include <termios.h>

using __llvm_libc::testing::ErrnoSetterMatcher::Fails;
using __llvm_libc::testing::ErrnoSetterMatcher::Succeeds;

// We just list a bunch of smoke tests here as it is not possible to
// test functionality at the least because we want to run the tests
// from ninja/make which change the terminal behavior.

TEST(LlvmLibcTermiosTest, SpeedSmokeTest) {
struct termios t;
errno = 0;
ASSERT_THAT(__llvm_libc::cfsetispeed(&t, B50), Succeeds(0));
ASSERT_EQ(__llvm_libc::cfgetispeed(&t), speed_t(B50));
ASSERT_THAT(__llvm_libc::cfsetospeed(&t, B75), Succeeds(0));
ASSERT_EQ(__llvm_libc::cfgetospeed(&t), speed_t(B75));

errno = 0;
ASSERT_THAT(__llvm_libc::cfsetispeed(&t, ~CBAUD), Fails(EINVAL));
errno = 0;
ASSERT_THAT(__llvm_libc::cfsetospeed(&t, ~CBAUD), Fails(EINVAL));
}

TEST(LlvmLibcTermiosTest, GetAttrSmokeTest) {
struct termios t;
errno = 0;
int fd = __llvm_libc::open("/dev/tty", O_RDONLY);
ASSERT_EQ(errno, 0);
ASSERT_GT(fd, 0);
ASSERT_THAT(__llvm_libc::tcgetattr(fd, &t), Succeeds(0));
ASSERT_EQ(__llvm_libc::close(fd), 0);
}

TEST(LlvmLibcTermiosTest, TcGetSidSmokeTest) {
int fd = __llvm_libc::open("/dev/tty", O_RDONLY);
ASSERT_EQ(errno, 0);
ASSERT_GT(fd, 0);
ASSERT_GT(__llvm_libc::tcgetsid(fd), pid_t(0));
ASSERT_EQ(__llvm_libc::close(fd), 0);
}