mirror of https://github.com/Desuuuu/klipper.git
194 lines
4.6 KiB
C
194 lines
4.6 KiB
C
// TTY based IO
|
|
//
|
|
// Copyright (C) 2017-2021 Kevin O'Connor <kevin@koconnor.net>
|
|
//
|
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
|
|
|
#define _GNU_SOURCE
|
|
#include <errno.h> // errno
|
|
#include <fcntl.h> // fcntl
|
|
#include <poll.h> // ppoll
|
|
#include <pty.h> // openpty
|
|
#include <stdio.h> // fprintf
|
|
#include <string.h> // memmove
|
|
#include <sys/stat.h> // chmod
|
|
#include <time.h> // struct timespec
|
|
#include <unistd.h> // ttyname
|
|
#include "board/irq.h" // irq_wait
|
|
#include "board/misc.h" // console_sendf
|
|
#include "command.h" // command_find_block
|
|
#include "internal.h" // console_setup
|
|
#include "sched.h" // sched_wake_task
|
|
|
|
static struct pollfd main_pfd[1];
|
|
#define MP_TTY_IDX 0
|
|
|
|
// Report 'errno' in a message written to stderr
|
|
void
|
|
report_errno(char *where, int rc)
|
|
{
|
|
int e = errno;
|
|
fprintf(stderr, "Got error %d in %s: (%d)%s\n", rc, where, e, strerror(e));
|
|
}
|
|
|
|
|
|
/****************************************************************
|
|
* Setup
|
|
****************************************************************/
|
|
|
|
int
|
|
set_non_blocking(int fd)
|
|
{
|
|
int flags = fcntl(fd, F_GETFL);
|
|
if (flags < 0) {
|
|
report_errno("fcntl getfl", flags);
|
|
return -1;
|
|
}
|
|
int ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
|
if (ret < 0) {
|
|
report_errno("fcntl setfl", flags);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
set_close_on_exec(int fd)
|
|
{
|
|
int ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
|
|
if (ret < 0) {
|
|
report_errno("fcntl set cloexec", ret);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
console_setup(char *name)
|
|
{
|
|
// Open pseudo-tty
|
|
struct termios ti;
|
|
memset(&ti, 0, sizeof(ti));
|
|
int mfd, sfd, ret = openpty(&mfd, &sfd, NULL, &ti, NULL);
|
|
if (ret) {
|
|
report_errno("openpty", ret);
|
|
return -1;
|
|
}
|
|
ret = set_non_blocking(mfd);
|
|
if (ret)
|
|
return -1;
|
|
ret = set_close_on_exec(mfd);
|
|
if (ret)
|
|
return -1;
|
|
ret = set_close_on_exec(sfd);
|
|
if (ret)
|
|
return -1;
|
|
main_pfd[MP_TTY_IDX].fd = mfd;
|
|
main_pfd[MP_TTY_IDX].events = POLLIN;
|
|
|
|
// Create symlink to tty
|
|
unlink(name);
|
|
char *tname = ttyname(sfd);
|
|
if (!tname) {
|
|
report_errno("ttyname", 0);
|
|
return -1;
|
|
}
|
|
ret = symlink(tname, name);
|
|
if (ret) {
|
|
report_errno("symlink", ret);
|
|
return -1;
|
|
}
|
|
ret = chmod(tname, 0660);
|
|
if (ret) {
|
|
report_errno("chmod", ret);
|
|
return -1;
|
|
}
|
|
|
|
// Make sure stderr is non-blocking
|
|
ret = set_non_blocking(STDERR_FILENO);
|
|
if (ret)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/****************************************************************
|
|
* Console handling
|
|
****************************************************************/
|
|
|
|
static struct task_wake console_wake;
|
|
static uint8_t receive_buf[4096];
|
|
static int receive_pos;
|
|
|
|
void *
|
|
console_receive_buffer(void)
|
|
{
|
|
return receive_buf;
|
|
}
|
|
|
|
// Process any incoming commands
|
|
void
|
|
console_task(void)
|
|
{
|
|
if (!sched_check_wake(&console_wake))
|
|
return;
|
|
|
|
// Read data
|
|
int ret = read(main_pfd[MP_TTY_IDX].fd, &receive_buf[receive_pos]
|
|
, sizeof(receive_buf) - receive_pos);
|
|
if (ret < 0) {
|
|
if (errno == EWOULDBLOCK) {
|
|
ret = 0;
|
|
} else {
|
|
report_errno("read", ret);
|
|
return;
|
|
}
|
|
}
|
|
if (ret == 15 && receive_buf[receive_pos+14] == '\n'
|
|
&& memcmp(&receive_buf[receive_pos], "FORCE_SHUTDOWN\n", 15) == 0)
|
|
shutdown("Force shutdown command");
|
|
|
|
// Find and dispatch message blocks in the input
|
|
int len = receive_pos + ret;
|
|
uint_fast8_t pop_count, msglen = len > MESSAGE_MAX ? MESSAGE_MAX : len;
|
|
ret = command_find_and_dispatch(receive_buf, msglen, &pop_count);
|
|
if (ret) {
|
|
len -= pop_count;
|
|
if (len) {
|
|
memmove(receive_buf, &receive_buf[pop_count], len);
|
|
sched_wake_task(&console_wake);
|
|
}
|
|
}
|
|
receive_pos = len;
|
|
}
|
|
DECL_TASK(console_task);
|
|
|
|
// Encode and transmit a "response" message
|
|
void
|
|
console_sendf(const struct command_encoder *ce, va_list args)
|
|
{
|
|
// Generate message
|
|
uint8_t buf[MESSAGE_MAX];
|
|
uint_fast8_t msglen = command_encode_and_frame(buf, ce, args);
|
|
|
|
// Transmit message
|
|
int ret = write(main_pfd[MP_TTY_IDX].fd, buf, msglen);
|
|
if (ret < 0)
|
|
report_errno("write", ret);
|
|
}
|
|
|
|
// Sleep until a signal received (waking early for console input if needed)
|
|
void
|
|
console_sleep(sigset_t *sigset)
|
|
{
|
|
int ret = ppoll(main_pfd, ARRAY_SIZE(main_pfd), NULL, sigset);
|
|
if (ret <= 0) {
|
|
if (errno != EINTR)
|
|
report_errno("ppoll main_pfd", ret);
|
|
return;
|
|
}
|
|
if (main_pfd[MP_TTY_IDX].revents)
|
|
sched_wake_task(&console_wake);
|
|
}
|