1
0
mirror of https://github.com/UzixLS/picocom.git synced 2025-07-19 07:21:18 +03:00
Files
picocom/ttylocal.c
Nick Patavalis cbcf13133d Moved native tty term_ops to ttylocal.c
Also, for better separation, the modem_XXX operations now communicate
using term.[ch] pin names (MCTL_xxx), not system pin names (TIOCM_xxx).
2018-03-01 17:51:34 +02:00

313 lines
7.1 KiB
C

/* vi: set sw=4 ts=4:
*
* ttylocal.c
*
* General purpose terminal handling library. Local (native) tty
* handling.
*
* by Nick Patavalis (npat@efault.net)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 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 a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*
* $Id$
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <termios.h>
/* On these systems, use the TIOCM[BIS|BIC|GET] ioctls to manipulate
* the modem control lines (DTR / RTS) */
#if defined(__linux__) || \
defined(__FreeBSD__) || defined(__OpenBSD__) || \
defined(__NetBSD__) || defined(__DragonFly__) || \
defined(__APPLE__)
#define USE_IOCTL
#endif
#ifdef USE_IOCTL
#include <sys/ioctl.h>
#endif
#include "custbaud.h"
#ifdef USE_CUSTOM_BAUD
#include CUSTOM_BAUD_HEAD
#endif
/* Time to wait for UART to clear after a drain (in usec). */
#define DRAIN_DELAY 200000
#include "term.h"
#include "termint.h"
/***************************************************************************/
static int
ttylocal_init(struct term_s *t)
{
int rval = 0;
const char *n;
do { /* dummy */
if ( ! isatty(t->fd) ) {
term_errno = TERM_EISATTY;
rval = -1;
break;
}
if ( ! t->name ) {
n = ttyname(t->fd);
if ( n ) t->name = strdup(n);
}
} while (0);
return rval;
}
static int
ttylocal_tcgetattr(struct term_s *t, struct termios *termios_out)
{
int r;
r = tcgetattr(t->fd, termios_out);
if ( r < 0 ) term_errno = TERM_EGETATTR;
return r;
}
static int
ttylocal_tcsetattr(struct term_s *t, int when, const struct termios *termios)
{
int r;
do {
r = tcsetattr(t->fd, when, termios);
} while ( r < 0 && errno == EINTR );
if ( r < 0 ) term_errno = TERM_ESETATTR;
return r;
}
#ifdef USE_IOCTL
static int
sys2term (int sys)
{
int term = 0;
if (sys & TIOCM_DTR) term |= MCTL_DTR;
if (sys & TIOCM_DSR) term |= MCTL_DSR;
if (sys & TIOCM_CD) term |= MCTL_DCD;
if (sys & TIOCM_RTS) term |= MCTL_RTS;
if (sys & TIOCM_CTS) term |= MCTL_CTS;
if (sys & TIOCM_RI) term |= MCTL_RI;
return term;
}
static int
term2sys (int term)
{
int sys = 0;
if (term & MCTL_DTR) sys |= TIOCM_DTR;
if (term & MCTL_DSR) sys |= TIOCM_DSR;
if (term & MCTL_DCD) sys |= TIOCM_CD;
if (term & MCTL_RTS) sys |= TIOCM_RTS;
if (term & MCTL_CTS) sys |= TIOCM_CTS;
if (term & MCTL_RI) sys |= TIOCM_RI;
return sys;
}
static int
ttylocal_modem_get(struct term_s *t, int *pins)
{
int r, syspins;
r = ioctl(t->fd, TIOCMGET, &syspins);
if ( r < 0 ) { term_errno = TERM_EGETMCTL; return r; }
*pins = sys2term(syspins);
return 0;
}
static int
ttylocal_modem_bis(struct term_s *t, const int *pins)
{
int r, syspins;
syspins = term2sys(*pins);
r = ioctl(t->fd, TIOCMBIS, &syspins);
if ( r < 0 ) term_errno = TERM_ESETMCTL;
return r;
}
static int
ttylocal_modem_bic(struct term_s *t, const int *pins)
{
int r, syspins;
syspins = term2sys(*pins);
r = ioctl(t->fd, TIOCMBIC, &syspins);
if ( r < 0 ) term_errno = TERM_ESETMCTL;
return r;
}
#endif /* of USE_IOCTL */
static int
ttylocal_send_break(struct term_s *t)
{
int r;
do {
r = tcsendbreak(t->fd, 0);
} while (r < 0 && errno == EINTR );
if ( r < 0 ) term_errno = TERM_EBREAK;
return r;
}
static int
ttylocal_flush(struct term_s *t, int selector)
{
int r;
r = tcflush(t->fd, selector);
if ( r < 0 ) term_errno = TERM_EFLUSH;
return r;
}
static int
ttylocal_fake_flush(struct term_s *t)
{
struct termios tio, tio_orig;
int term_errno_hold = 0, errno_hold = 0;
int r, rval = 0;
do { /* dummy */
/* Get current termios */
r = t->ops->tcgetattr(t, &tio);
if ( r < 0 ) {
rval = -1;
break;
}
tio_orig = tio;
/* Set flow-control to none */
tio.c_cflag &= ~(CRTSCTS);
tio.c_iflag &= ~(IXON | IXOFF | IXANY);
/* Apply termios */
r = t->ops->tcsetattr(t, TCSANOW, &tio);
if ( r < 0 ) {
rval = -1;
break;
}
/* Wait for output to drain. Without flow-control this should
complete in finite time. */
r = t->ops->drain(t);
if ( r < 0 ) {
term_errno_hold = term_errno;
errno_hold = errno;
rval = -1;
/* continue */
}
/* Reset flow-control to original setting. */
r = t->ops->tcsetattr(t, TCSANOW, &tio_orig);
if ( r < 0 ) {
rval = -1;
break;
}
} while(0);
if ( term_errno_hold ) {
term_errno = term_errno_hold;
errno = errno_hold;
}
return rval;
}
static int
ttylocal_drain(struct term_s *t)
{
int r;
do {
#ifdef __BIONIC__
/* See: http://dan.drown.org/android/src/gdb/no-tcdrain */
r = ioctl(t->fd, TCSBRK, 1);
#else
r = tcdrain(t->fd);
#endif
} while ( r < 0 && errno == EINTR);
if ( r < 0 ) {
term_errno = TERM_EDRAIN;
return r;
}
/* Give some time to the UART to transmit everything. Some
systems and / or drivers corrupt the last character(s) if
the port is immediately reconfigured, even after a
drain. (I guess, drain does not wait for everything to
actually be transitted on the wire). */
if ( DRAIN_DELAY ) usleep(DRAIN_DELAY);
return 0;
}
int
ttylocal_read(struct term_s *t, void *buf, unsigned bufsz)
{
int r;
r = read(t->fd, buf, bufsz);
if ( r < 0 ) term_errno = TERM_EINPUT;
return r;
}
int
ttylocal_write(struct term_s *t, const void *buf, unsigned bufsz)
{
int r;
r = write(t->fd, buf, bufsz);
if ( r < 0 ) term_errno = TERM_EOUTPUT;
return r;
}
const struct term_ops ttylocal_term_ops = {
.init = ttylocal_init,
.fini = NULL,
.tcgetattr = ttylocal_tcgetattr,
.tcsetattr = ttylocal_tcsetattr,
#ifdef USE_IOCTL
.modem_get = ttylocal_modem_get,
.modem_bis = ttylocal_modem_bis,
.modem_bic = ttylocal_modem_bic,
#else
.modem_get = NULL,
.modem_bis = NULL,
.modem_bic = NULL,
#endif
.send_break = ttylocal_send_break,
.flush = ttylocal_flush,
.fake_flush = ttylocal_fake_flush,
.drain = ttylocal_drain,
.read = ttylocal_read,
.write = ttylocal_write,
};
/***************************************************************************/
/*
* Local Variables:
* mode:c
* tab-width: 4
* c-basic-offset: 4
* End:
*/