245 lines
5.3 KiB
C
245 lines
5.3 KiB
C
/** minimal terminal using pollcat
|
|
*
|
|
* This file is part of the Pollcat Library.
|
|
* Copyright (C) 2022 Expatria Technologies Inc.
|
|
* Contact: Morgan Hughes <morgan@expatria.ca>
|
|
*
|
|
* The Pollcat Library is free software: you can redistribute it and/or modify it under
|
|
* the terms of the the GNU Lesser General Public License as published by the Free
|
|
* Software Foundation; either version 3 of the License, or (at your option) any later
|
|
* version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received copies of the GNU General Public License and the GNU Lesser
|
|
* General Public License along with the Pollcat Library. If not, see
|
|
* https://www.gnu.org/licenses/
|
|
*
|
|
* vim:ts=4:noexpandtab
|
|
*/
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <termios.h>
|
|
|
|
#include <pollcat.h>
|
|
|
|
#include "terminal.h"
|
|
|
|
|
|
static int fd;
|
|
|
|
|
|
static void service (struct pollcat *cat)
|
|
{
|
|
char buff[4096];
|
|
int ret;
|
|
int idx;
|
|
|
|
if ( !(pollcat_revents(fd) & POLLIN) )
|
|
return;
|
|
|
|
while ( (ret = read(fd, buff, sizeof(buff))) > 0 )
|
|
tty_write(buff, ret);
|
|
}
|
|
|
|
static const struct pollcat_ops ops =
|
|
{
|
|
.service = service,
|
|
};
|
|
|
|
static struct pollcat cat =
|
|
{
|
|
.ops = &ops,
|
|
};
|
|
|
|
static int serial_fl;
|
|
static int serial_fd;
|
|
static struct termios serial_tio;
|
|
|
|
int serial_init (const char *node, const char *setup)
|
|
{
|
|
speed_t spd;
|
|
char *mod;
|
|
|
|
if ( (fd = open(node, O_RDWR, 0)) < 0 )
|
|
return -1;
|
|
|
|
// set stdin nonblocking
|
|
if ( (serial_fl = fcntl(fd, F_GETFL)) < 0 )
|
|
return -1;
|
|
if ( fcntl(fd, F_SETFL, serial_fl | O_NONBLOCK) < 0 )
|
|
return -1;
|
|
|
|
// set stdin nonblocking
|
|
if ( (serial_fd = fcntl(fd, F_GETFD)) < 0 )
|
|
return -1;
|
|
if ( fcntl(fd, F_SETFD, serial_fd | FD_CLOEXEC) < 0 )
|
|
return -1;
|
|
|
|
// save old terminal modes and make raw
|
|
if ( tcgetattr(fd, &serial_tio) )
|
|
return -1;
|
|
|
|
struct termios tio = serial_tio;
|
|
cfmakeraw(&tio);
|
|
|
|
switch ( strtoul(setup, &mod, 10) )
|
|
{
|
|
case 50: spd = B50; break;
|
|
case 75: spd = B75; break;
|
|
case 110: spd = B110; break;
|
|
case 134: spd = B134; break;
|
|
case 150: spd = B150; break;
|
|
case 200: spd = B200; break;
|
|
case 300: spd = B300; break;
|
|
case 600: spd = B600; break;
|
|
case 1200: spd = B1200; break;
|
|
case 1800: spd = B1800; break;
|
|
case 2400: spd = B2400; break;
|
|
case 4800: spd = B4800; break;
|
|
case 9600: spd = B9600; break;
|
|
case 19200: spd = B19200; break;
|
|
case 38400: spd = B38400; break;
|
|
case 57600: spd = B57600; break;
|
|
case 115200: spd = B115200; break;
|
|
#ifdef B230400
|
|
case 230400: spd = B230400; break;
|
|
#endif
|
|
#ifdef B460800
|
|
case 460800: spd = B460800; break;
|
|
#endif
|
|
#ifdef B500000
|
|
case 500000: spd = B500000; break;
|
|
#endif
|
|
#ifdef B576000
|
|
case 576000: spd = B576000; break;
|
|
#endif
|
|
#ifdef B921600
|
|
case 921600: spd = B921600; break;
|
|
#endif
|
|
#ifdef B1000000
|
|
case 1000000: spd = B1000000; break;
|
|
#endif
|
|
#ifdef B1152000
|
|
case 1152000: spd = B1152000; break;
|
|
#endif
|
|
#ifdef B1500000
|
|
case 1500000: spd = B1500000; break;
|
|
#endif
|
|
#ifdef B2000000
|
|
case 2000000: spd = B2000000; break;
|
|
#endif
|
|
#ifdef B2500000
|
|
case 2500000: spd = B2500000; break;
|
|
#endif
|
|
#ifdef B3000000
|
|
case 3000000: spd = B3000000; break;
|
|
#endif
|
|
#ifdef B3500000
|
|
case 3500000: spd = B3500000; break;
|
|
#endif
|
|
#ifdef B4000000
|
|
case 4000000: spd = B4000000; break;
|
|
#endif
|
|
default:
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
if ( cfsetispeed(&tio, spd) || cfsetospeed(&tio, spd) )
|
|
return -1;
|
|
|
|
while ( *mod )
|
|
{
|
|
switch ( toupper(*mod) )
|
|
{
|
|
// data bits
|
|
case '8': tio.c_cflag &= ~CSIZE; tio.c_cflag |= CS8; break;
|
|
case '7': tio.c_cflag &= ~CSIZE; tio.c_cflag |= CS7; break;
|
|
case '6': tio.c_cflag &= ~CSIZE; tio.c_cflag |= CS6; break;
|
|
case '5': tio.c_cflag &= ~CSIZE; tio.c_cflag |= CS5; break;
|
|
|
|
// stop bits
|
|
case '2': tio.c_cflag |= CSTOPB; break;
|
|
case '1': tio.c_cflag &= ~CSTOPB; break;
|
|
|
|
// no parity
|
|
case 'N':
|
|
tio.c_iflag |= IGNPAR;
|
|
tio.c_iflag &= ~INPCK;
|
|
tio.c_cflag &= ~(PARENB | PARODD | CMSPAR);
|
|
break;
|
|
|
|
// odd parity
|
|
case 'O':
|
|
tio.c_iflag |= INPCK | PARMRK;
|
|
tio.c_iflag &= ~IGNPAR;
|
|
tio.c_cflag |= PARENB | PARODD;
|
|
tio.c_cflag &= ~CMSPAR;
|
|
break;
|
|
|
|
// even parity
|
|
case 'E':
|
|
tio.c_iflag |= INPCK | PARMRK;
|
|
tio.c_iflag &= ~IGNPAR;
|
|
tio.c_cflag |= PARENB;
|
|
tio.c_cflag &= ~(PARODD | CMSPAR);
|
|
break;
|
|
|
|
// mark parity
|
|
case 'M':
|
|
tio.c_iflag |= INPCK | PARMRK;
|
|
tio.c_iflag &= ~IGNPAR;
|
|
tio.c_cflag |= PARENB | CMSPAR | PARODD;
|
|
break;
|
|
|
|
// space parity
|
|
case 'S':
|
|
tio.c_iflag |= INPCK | PARMRK;
|
|
tio.c_iflag &= ~IGNPAR;
|
|
tio.c_cflag |= PARENB | CMSPAR;
|
|
tio.c_cflag &= ~PARODD;
|
|
break;
|
|
|
|
default:
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
mod++;
|
|
}
|
|
|
|
if ( tcsetattr(fd, TCSANOW, &tio) )
|
|
return -1;
|
|
|
|
pollcat_fd_add(fd, POLLIN);
|
|
pollcat_iter_add(&cat);
|
|
return 0;
|
|
}
|
|
|
|
|
|
void serial_done (void)
|
|
{
|
|
pollcat_iter_remove(&cat);
|
|
pollcat_fd_remove(fd);
|
|
|
|
if ( fcntl(fd, F_SETFL, serial_fl) < 0 )
|
|
perror("fcntl");
|
|
|
|
if ( fcntl(fd, F_SETFD, serial_fd) < 0 )
|
|
perror("fcntl");
|
|
|
|
if ( tcsetattr(fd, TCSANOW, &serial_tio) )
|
|
perror("tcsetattr");
|
|
|
|
close(fd);
|
|
}
|
|
|
|
void serial_write (const void *buff, int len)
|
|
{
|
|
write(fd, buff, len);
|
|
}
|