mirror of
https://github.com/lbgos/rift-s-linux.git
synced 2026-10-27 22:00:10 +01:00
os/serial: Add serial abstraction with Linux implementation
Signed-off-by: Beyley Cardellio <ep1cm1n10n123@gmail.com> Part-of: <https://gitlab.freedesktop.org/monado/monado/-/merge_requests/2781>
This commit is contained in:
1 parent
238efdcd16
commit
ca76cd692f
4 files changed
+378
No files matched your search
@@ -14,6 +14,7 @@ add_library(
|
||||
os_documentation.h
|
||||
os_hid.h
|
||||
os_hid_hidraw.c
|
||||
os_serial.h
|
||||
os_threading.h
|
||||
os_time.cpp
|
||||
)
|
||||
@@ -24,6 +25,12 @@ if(WIN32)
|
||||
target_link_libraries(aux_os PRIVATE winmm)
|
||||
endif()
|
||||
|
||||
if(LINUX)
|
||||
target_sources(aux_os PRIVATE os_serial_linux.c)
|
||||
else()
|
||||
target_sources(aux_os PRIVATE os_serial_stubs.c)
|
||||
endif()
|
||||
|
||||
####
|
||||
# BLE library
|
||||
#
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
// Copyright 2019, Collabora, Ltd.
|
||||
// Copyright 2026, Beyley Cardellio
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief Wrapper around OS native serial functions.
|
||||
* @author Jakob Bornecrantz <jakob@collabora.com>
|
||||
* @author Rylie Pavlik <rylie.pavlik@collabora.com>
|
||||
* @author Beyley Cardellio <ep1cm1n10n123@gmail.com>
|
||||
*
|
||||
* @ingroup aux_os
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xrt/xrt_config_os.h"
|
||||
#include "xrt/xrt_compiler.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*!
|
||||
* @interface os_serial_device
|
||||
*
|
||||
* Representing a single serial interface on a device.
|
||||
*/
|
||||
struct os_serial_device
|
||||
{
|
||||
ssize_t (*read)(struct os_serial_device *serial_dev, uint8_t *data, size_t size, int milliseconds);
|
||||
|
||||
ssize_t (*write)(struct os_serial_device *serial_dev, const uint8_t *data, size_t size);
|
||||
|
||||
int (*set_line_control)(struct os_serial_device *serial_dev, bool dtr, bool rts);
|
||||
|
||||
void (*destroy)(struct os_serial_device *serial_dev);
|
||||
};
|
||||
|
||||
enum os_serial_parity_mode
|
||||
{
|
||||
OS_SERIAL_PARITY_NONE,
|
||||
OS_SERIAL_PARITY_EVEN,
|
||||
OS_SERIAL_PARITY_ODD,
|
||||
};
|
||||
|
||||
/*!
|
||||
* Represents the parameters for opening a serial device, such as baud rate, parity, etc.
|
||||
*/
|
||||
struct os_serial_parameters
|
||||
{
|
||||
uint32_t baud_rate;
|
||||
uint8_t data_bits; // must be 5, 6, 7, or 8 bits
|
||||
uint8_t stop_bits; // must be 1 or 2
|
||||
enum os_serial_parity_mode parity;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Read the next input report, if any, from the given serial device.
|
||||
*
|
||||
* If milliseconds are negative, this call blocks indefinitely, 0 polls,
|
||||
* and positive will block for that amount of milliseconds.
|
||||
*
|
||||
* @public @memberof os_serial_device
|
||||
*/
|
||||
static inline ssize_t
|
||||
os_serial_read(struct os_serial_device *serial_dev, uint8_t *data, size_t size, int milliseconds)
|
||||
{
|
||||
return serial_dev->read(serial_dev, data, size, milliseconds);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Write an output report to the given device.
|
||||
*
|
||||
* @public @memberof os_serial_device
|
||||
*/
|
||||
static inline ssize_t
|
||||
os_serial_write(struct os_serial_device *serial_dev, const uint8_t *data, size_t size)
|
||||
{
|
||||
return serial_dev->write(serial_dev, data, size);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Set the line control signals (DTR and RTS) for the given serial device.
|
||||
*
|
||||
* @public @memberof os_serial_device
|
||||
*/
|
||||
static inline int
|
||||
os_serial_set_line_control(struct os_serial_device *serial_dev, bool dtr, bool rts)
|
||||
{
|
||||
return serial_dev->set_line_control(serial_dev, dtr, rts);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Close and free the given device.
|
||||
*
|
||||
* @public @memberof os_serial_device
|
||||
*/
|
||||
static inline void
|
||||
os_serial_destroy(struct os_serial_device *serial_dev)
|
||||
{
|
||||
serial_dev->destroy(serial_dev);
|
||||
}
|
||||
|
||||
int
|
||||
os_serial_open(const char *path, const struct os_serial_parameters *parameters, struct os_serial_device **out_serial);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
@@ -0,0 +1,238 @@
|
||||
// Copyright 2019, Collabora, Ltd.
|
||||
// Copyright 2026, Beyley Cardellio
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief Serial implementation based on Linux termios
|
||||
* @author Jakob Bornecrantz <jakob@collabora.com>
|
||||
* @author Rylie Pavlik <rylie.pavlik@collabora.com>
|
||||
* @author Beyley Cardellio <ep1cm1n10n123@gmail.com>
|
||||
* @ingroup aux_os
|
||||
*/
|
||||
|
||||
#include "xrt/xrt_config_os.h"
|
||||
|
||||
#ifndef XRT_OS_LINUX
|
||||
#error Linux-specific file.
|
||||
#endif
|
||||
|
||||
#include <util/u_misc.h>
|
||||
#include <util/u_logging.h>
|
||||
|
||||
#include "os_serial.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
struct serial_linux
|
||||
{
|
||||
struct os_serial_device base;
|
||||
|
||||
int fd;
|
||||
};
|
||||
|
||||
static struct serial_linux *
|
||||
serial_linux(struct os_serial_device *serial_dev)
|
||||
{
|
||||
return (struct serial_linux *)serial_dev;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
linux_serial_read(struct os_serial_device *serial_dev, uint8_t *data, size_t size, int milliseconds)
|
||||
{
|
||||
struct serial_linux *serial = serial_linux(serial_dev);
|
||||
struct pollfd fds;
|
||||
int ret;
|
||||
|
||||
if (milliseconds >= 0) {
|
||||
fds.fd = serial->fd;
|
||||
fds.events = POLLIN;
|
||||
fds.revents = 0;
|
||||
ret = poll(&fds, 1, milliseconds);
|
||||
|
||||
if (ret == -1 || ret == 0) {
|
||||
// Error or timeout.
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (fds.revents & (POLLERR | POLLHUP | POLLNVAL)) {
|
||||
// Device disconnect?
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ret = read(serial->fd, data, size);
|
||||
|
||||
if (ret < 0 && (errno == EAGAIN || errno == EINPROGRESS)) {
|
||||
// Process most likely received a signal.
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
linux_serial_write(struct os_serial_device *serial_dev, const uint8_t *data, size_t size)
|
||||
{
|
||||
struct serial_linux *serial = serial_linux(serial_dev);
|
||||
|
||||
return write(serial->fd, data, size);
|
||||
}
|
||||
|
||||
static int
|
||||
linux_serial_set_line_control(struct os_serial_device *serial_dev, bool dtr, bool rts)
|
||||
{
|
||||
struct serial_linux *serial = serial_linux(serial_dev);
|
||||
int status;
|
||||
|
||||
// Get the current modem status bits
|
||||
if (ioctl(serial->fd, TIOCMGET, &status) == -1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (dtr) {
|
||||
status |= TIOCM_DTR;
|
||||
} else {
|
||||
status &= ~TIOCM_DTR;
|
||||
}
|
||||
|
||||
if (rts) {
|
||||
status |= TIOCM_RTS;
|
||||
} else {
|
||||
status &= ~TIOCM_RTS;
|
||||
}
|
||||
|
||||
// Set the updated modem status bits
|
||||
if (ioctl(serial->fd, TIOCMSET, &status) == -1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
linux_serial_destroy(struct os_serial_device *serial_dev)
|
||||
{
|
||||
struct serial_linux *serial = serial_linux(serial_dev);
|
||||
|
||||
if (serial->fd >= 0) {
|
||||
close(serial->fd);
|
||||
}
|
||||
|
||||
free(serial);
|
||||
}
|
||||
|
||||
int
|
||||
os_serial_open(const char *path, const struct os_serial_parameters *parameters, struct os_serial_device **out_serial)
|
||||
{
|
||||
int fd = open(path, O_RDWR | O_NOCTTY | O_NONBLOCK | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
struct termios tty;
|
||||
if (tcgetattr(fd, &tty) != 0) {
|
||||
U_LOG_E("Failed to get terminal attributes: %s", strerror(errno));
|
||||
close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
// Clear parity bits
|
||||
tty.c_cflag &= ~(PARENB | PARODD);
|
||||
|
||||
// Set according to PARODD mode
|
||||
switch (parameters->parity) {
|
||||
case OS_SERIAL_PARITY_NONE: break;
|
||||
case OS_SERIAL_PARITY_EVEN: tty.c_cflag |= PARENB; break;
|
||||
case OS_SERIAL_PARITY_ODD: tty.c_cflag |= PARENB | PARODD; break;
|
||||
default:
|
||||
U_LOG_E("Invalid parity mode: %d", parameters->parity);
|
||||
close(fd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
// Set stop bits
|
||||
switch (parameters->stop_bits) {
|
||||
case 1: tty.c_cflag &= ~CSTOPB; break;
|
||||
case 2: tty.c_cflag |= CSTOPB; break;
|
||||
default:
|
||||
U_LOG_E("Invalid stop bits: %d", parameters->stop_bits);
|
||||
close(fd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
// Clear data size bits
|
||||
tty.c_cflag &= ~CSIZE;
|
||||
// Set according to data bits
|
||||
switch (parameters->data_bits) {
|
||||
case 5: tty.c_cflag |= CS5; break;
|
||||
case 6: tty.c_cflag |= CS6; break;
|
||||
case 7: tty.c_cflag |= CS7; break;
|
||||
case 8: tty.c_cflag |= CS8; break;
|
||||
default:
|
||||
U_LOG_E("Invalid data bits: %d", parameters->data_bits);
|
||||
close(fd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tty.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines (CLOCAL = 1)
|
||||
|
||||
tty.c_lflag &= ~ICANON; // Disable canonical mode
|
||||
tty.c_lflag &= ~ECHO; // Disable echo
|
||||
tty.c_lflag &= ~ECHOE; // Disable erasure
|
||||
tty.c_lflag &= ~ECHONL; // Disable new-line echo
|
||||
tty.c_lflag &= ~ISIG; // Disable interpretation of INTR, QUIT and SUSP
|
||||
|
||||
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Turn off s/w flow ctrl
|
||||
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR |
|
||||
ICRNL); // Disable any special handling of received bytes
|
||||
|
||||
tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes (e.g. newline chars)
|
||||
tty.c_oflag &= ~ONLCR; // Prevent conversion of newline to carriage return/line feed
|
||||
|
||||
tty.c_cc[VTIME] = 1; // Set timeout to 0.1 seconds (1 decisecond)
|
||||
tty.c_cc[VMIN] = 0; // Do not wait for minimum number of characters
|
||||
|
||||
const speed_t baud_rate = (speed_t)parameters->baud_rate;
|
||||
|
||||
// Set output speed
|
||||
if (cfsetospeed(&tty, baud_rate) != 0) {
|
||||
U_LOG_E("Failed to set output speed: %s", strerror(errno));
|
||||
close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
// Set input speed
|
||||
if (cfsetispeed(&tty, baud_rate) != 0) {
|
||||
U_LOG_E("Failed to set input speed: %s", strerror(errno));
|
||||
close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
// Set final attributes
|
||||
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
|
||||
U_LOG_E("Failed to set terminal attributes: %s", strerror(errno));
|
||||
close(fd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
struct serial_linux *serial = U_TYPED_CALLOC(struct serial_linux);
|
||||
|
||||
serial->base.read = linux_serial_read;
|
||||
serial->base.write = linux_serial_write;
|
||||
serial->base.set_line_control = linux_serial_set_line_control;
|
||||
serial->base.destroy = linux_serial_destroy;
|
||||
|
||||
serial->fd = fd;
|
||||
|
||||
(*out_serial) = &serial->base;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2026, Beyley Cardellio
|
||||
// SPDX-License-Identifier: BSL-1.0
|
||||
/*!
|
||||
* @file
|
||||
* @brief Serial implementation stub
|
||||
* @author Beyley Cardellio <ep1cm1n10n123@gmail.com>
|
||||
* @ingroup aux_os
|
||||
*/
|
||||
|
||||
#include "util/u_logging.h"
|
||||
|
||||
#include "os_serial.h"
|
||||
|
||||
|
||||
int
|
||||
os_serial_open(const char *path, const struct os_serial_parameters *parameters, struct os_serial_device **out_serial)
|
||||
{
|
||||
U_LOG_E("Serial devices are not implemented on this platform.");
|
||||
return -1;
|
||||
}
|
||||
Reference in new issue
Block a user