mirror of
https://github.com/UzixLS/picocom.git
synced 2025-07-19 07:21:18 +03:00

CMSPAR is defined by bits/termios.h, and glibc has two flavours of it: a generic one and an architecture-specific one. glibc will install the architecture-specific one if it exists, otherwise it will install the generic one. Only Alpha, MIPS, PPC and Sparc have their own bits/termios.h. The generic bits/termios.h, as well as the Alpha, PPC and Sparc flavours do define CMSPAR. However, the MIPS flavour does not define it. Define CMSPAR to the value from the generic value, which is also the value known to the Linux kernel for MIPS. Patch inspired by: http://git.buildroot.net/buildroot/commit/?id=78cd32631e959e04b1a2f18be7b0757e21482438 Signed-off-by: Vicente Olivert Riera <Vincent.Riera@imgtec.com>
1570 lines
25 KiB
C
1570 lines
25 KiB
C
/* vi: set sw=4 ts=4:
|
||
*
|
||
* term.c
|
||
*
|
||
* General purpose terminal handling library.
|
||
*
|
||
* Nick Patavalis (npat@inaccessnetworks.com)
|
||
*
|
||
* originaly by Pantelis Antoniou (panto@intranet.gr), Nick Patavalis
|
||
*
|
||
* Documentation can be found in the header file "term.h".
|
||
*
|
||
* 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 <stdio.h>
|
||
#include <string.h>
|
||
#include <errno.h>
|
||
#include <unistd.h>
|
||
#include <termios.h>
|
||
|
||
/* glibc for MIPS has its own bits/termios.h which does not define
|
||
* CMSPAR, so we use the value from the generic bits/termios.h
|
||
*/
|
||
#ifndef CMSPAR
|
||
#define CMSPAR 010000000000
|
||
#endif
|
||
|
||
#ifdef __linux__
|
||
#include <sys/ioctl.h>
|
||
#endif
|
||
|
||
#ifdef USE_CUSTOM_BAUD
|
||
/* only works for linux, recent kernels */
|
||
#include "termios2.h"
|
||
#endif
|
||
|
||
#include "term.h"
|
||
|
||
/***************************************************************************/
|
||
|
||
static struct term_s {
|
||
int init;
|
||
int fd[MAX_TERMS];
|
||
struct termios origtermios[MAX_TERMS];
|
||
struct termios currtermios[MAX_TERMS];
|
||
struct termios nexttermios[MAX_TERMS];
|
||
} term;
|
||
|
||
/***************************************************************************/
|
||
|
||
int term_errno;
|
||
|
||
static const char * const term_err_str[] = {
|
||
[TERM_EOK] = "No error",
|
||
[TERM_ENOINIT] = "Framework is uninitialized",
|
||
[TERM_EFULL] = "Framework is full",
|
||
[TERM_ENOTFOUND] = "Filedes not in the framework",
|
||
[TERM_EEXISTS] = "Filedes already in the framework",
|
||
[TERM_EATEXIT] = "Cannot install atexit handler",
|
||
[TERM_EISATTY] = "Filedes is not a tty",
|
||
[TERM_EFLUSH] = "Cannot flush the device",
|
||
[TERM_EGETATTR] = "Cannot get the device attributes",
|
||
[TERM_ESETATTR] = "Cannot set the device attributes",
|
||
[TERM_EBAUD] = "Invalid baud rate",
|
||
[TERM_ESETOSPEED] = "Cannot set the output speed",
|
||
[TERM_ESETISPEED] = "Cannot set the input speed",
|
||
[TERM_EGETSPEED] = "Cannot decode speed",
|
||
[TERM_EPARITY] = "Invalid parity mode",
|
||
[TERM_EDATABITS] = "Invalid number of databits",
|
||
[TERM_ESTOPBITS] = "Invalid number of stopbits",
|
||
[TERM_EFLOW] = "Invalid flowcontrol mode",
|
||
[TERM_EDTRDOWN] = "Cannot lower DTR",
|
||
[TERM_EDTRUP] = "Cannot raise DTR",
|
||
[TERM_EMCTL] = "Cannot get mctl status",
|
||
[TERM_EDRAIN] = "Cannot drain the device",
|
||
[TERM_EBREAK] = "Cannot send break sequence"
|
||
};
|
||
|
||
static char term_err_buff[1024];
|
||
|
||
const char *
|
||
term_strerror (int terrnum, int errnum)
|
||
{
|
||
const char *rval;
|
||
|
||
switch(terrnum) {
|
||
case TERM_EFLUSH:
|
||
case TERM_EGETATTR:
|
||
case TERM_ESETATTR:
|
||
case TERM_ESETOSPEED:
|
||
case TERM_ESETISPEED:
|
||
case TERM_EDRAIN:
|
||
case TERM_EBREAK:
|
||
snprintf(term_err_buff, sizeof(term_err_buff),
|
||
"%s: %s", term_err_str[terrnum], strerror(errnum));
|
||
rval = term_err_buff;
|
||
break;
|
||
case TERM_EOK:
|
||
case TERM_ENOINIT:
|
||
case TERM_EFULL:
|
||
case TERM_ENOTFOUND:
|
||
case TERM_EEXISTS:
|
||
case TERM_EATEXIT:
|
||
case TERM_EISATTY:
|
||
case TERM_EBAUD:
|
||
case TERM_EPARITY:
|
||
case TERM_EDATABITS:
|
||
case TERM_ESTOPBITS:
|
||
case TERM_EFLOW:
|
||
case TERM_EDTRDOWN:
|
||
case TERM_EDTRUP:
|
||
case TERM_EMCTL:
|
||
snprintf(term_err_buff, sizeof(term_err_buff),
|
||
"%s", term_err_str[terrnum]);
|
||
rval = term_err_buff;
|
||
break;
|
||
default:
|
||
rval = NULL;
|
||
break;
|
||
}
|
||
|
||
return rval;
|
||
}
|
||
|
||
int
|
||
term_perror (const char *prefix)
|
||
{
|
||
return fprintf(stderr, "%s %s\n",
|
||
prefix, term_strerror(term_errno, errno));
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
#define BNONE 0xFFFFFFFF
|
||
|
||
struct baud_codes {
|
||
int speed;
|
||
speed_t code;
|
||
} baud_table[] = {
|
||
{ 0, B0 },
|
||
{ 50, B50 },
|
||
{ 75, B75 },
|
||
{ 110, B110 },
|
||
{ 134, B134 },
|
||
{ 150, B150 },
|
||
{ 200, B200 },
|
||
{ 300, B300 },
|
||
{ 600, B600 },
|
||
{ 1200, B1200 },
|
||
{ 1800, B1800 },
|
||
{ 2400, B2400 },
|
||
{ 4800, B4800 },
|
||
{ 9600, B9600 },
|
||
{ 19200, B19200 },
|
||
{ 38400, B38400 },
|
||
{ 57600, B57600 },
|
||
{ 115200, B115200 },
|
||
#ifdef HIGH_BAUD
|
||
#ifdef B230400
|
||
{ 230400, B230400 },
|
||
#endif
|
||
#ifdef B460800
|
||
{ 460800, B460800 },
|
||
#endif
|
||
#ifdef B500000
|
||
{ 500000, B500000 },
|
||
#endif
|
||
#ifdef B576000
|
||
{ 576000, B576000 },
|
||
#endif
|
||
#ifdef B921600
|
||
{ 921600, B921600 },
|
||
#endif
|
||
#ifdef B1000000
|
||
{ 1000000, B1000000 },
|
||
#endif
|
||
#ifdef B1152000
|
||
{ 1152000, B1152000 },
|
||
#endif
|
||
#ifdef B1500000
|
||
{ 1500000, B1500000 },
|
||
#endif
|
||
#ifdef B2000000
|
||
{ 2000000, B2000000 },
|
||
#endif
|
||
#ifdef B2500000
|
||
{ 2500000, B2500000 },
|
||
#endif
|
||
#ifdef B3000000
|
||
{ 3000000, B3000000 },
|
||
#endif
|
||
#ifdef B3500000
|
||
{ 3500000, B3500000 },
|
||
#endif
|
||
#ifdef B4000000
|
||
{ 4000000, B4000000 },
|
||
#endif
|
||
#endif /* of HIGH_BAUD */
|
||
};
|
||
|
||
#define BAUD_TABLE_SZ (sizeof(baud_table) / sizeof(baud_table[0]))
|
||
|
||
int
|
||
term_baud_up (int baud)
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < BAUD_TABLE_SZ; i++) {
|
||
if ( baud >= baud_table[i].speed )
|
||
continue;
|
||
else {
|
||
baud = baud_table[i].speed;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return baud;
|
||
}
|
||
|
||
int
|
||
term_baud_down (int baud)
|
||
{
|
||
int i;
|
||
|
||
for (i = BAUD_TABLE_SZ - 1; i >= 0; i--) {
|
||
if ( baud <= baud_table[i].speed )
|
||
continue;
|
||
else {
|
||
baud = baud_table[i].speed;
|
||
break;
|
||
}
|
||
}
|
||
|
||
return baud;
|
||
}
|
||
|
||
static speed_t
|
||
Bcode(int speed)
|
||
{
|
||
speed_t code = BNONE;
|
||
int i;
|
||
|
||
for (i = 0; i < BAUD_TABLE_SZ; i++) {
|
||
if ( baud_table[i].speed == speed ) {
|
||
code = baud_table[i].code;
|
||
break;
|
||
}
|
||
}
|
||
return code;
|
||
}
|
||
|
||
static int
|
||
Bspeed(speed_t code)
|
||
{
|
||
int speed = -1, i;
|
||
|
||
for (i = 0; i < BAUD_TABLE_SZ; i++) {
|
||
if ( baud_table[i].code == code ) {
|
||
speed = baud_table[i].speed;
|
||
break;
|
||
}
|
||
}
|
||
return speed;
|
||
}
|
||
|
||
int
|
||
term_baud_ok(int baud)
|
||
{
|
||
#ifndef USE_CUSTOM_BAUD
|
||
return (Bcode(baud) != BNONE) ? 1 : 0;
|
||
#else
|
||
return (baud >= 0);
|
||
#endif
|
||
}
|
||
|
||
/**************************************************************************/
|
||
|
||
static int
|
||
term_find_next_free (void)
|
||
{
|
||
int rval, i;
|
||
|
||
do { /* dummy */
|
||
if ( ! term.init ) {
|
||
term_errno = TERM_ENOINIT;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
for (i = 0; i < MAX_TERMS; i++)
|
||
if ( term.fd[i] == -1 ) break;
|
||
|
||
if ( i == MAX_TERMS ) {
|
||
term_errno = TERM_EFULL;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
rval = i;
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
static int
|
||
term_find (int fd)
|
||
{
|
||
int rval, i;
|
||
|
||
do { /* dummy */
|
||
if ( ! term.init ) {
|
||
term_errno = TERM_ENOINIT;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
for (i = 0; i < MAX_TERMS; i++)
|
||
if (term.fd[i] == fd) break;
|
||
|
||
if ( i == MAX_TERMS ) {
|
||
term_errno = TERM_ENOTFOUND;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
rval = i;
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
static void
|
||
term_exitfunc (void)
|
||
{
|
||
int r, i;
|
||
|
||
do { /* dummy */
|
||
if ( ! term.init )
|
||
break;
|
||
|
||
for (i = 0; i < MAX_TERMS; i++) {
|
||
if (term.fd[i] == -1)
|
||
continue;
|
||
tcflush(term.fd[i], TCIOFLUSH);
|
||
do {
|
||
r = tcsetattr(term.fd[i], TCSANOW, &term.origtermios[i]);
|
||
} while ( r < 0 && errno == EINTR );
|
||
if ( r < 0 ) {
|
||
char *tname;
|
||
|
||
tname = ttyname(term.fd[i]);
|
||
if ( ! tname ) tname = "UNKNOWN";
|
||
fprintf(stderr, "%s: reset failed for dev %s: %s\n",
|
||
__FUNCTION__, tname, strerror(errno));
|
||
}
|
||
term.fd[i] = -1;
|
||
}
|
||
} while (0);
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_lib_init (void)
|
||
{
|
||
int rval, r, i;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
if ( term.init ) {
|
||
/* reset all terms back to their original settings */
|
||
for (i = 0; i < MAX_TERMS; i++) {
|
||
if (term.fd[i] == -1)
|
||
continue;
|
||
tcflush(term.fd[i], TCIOFLUSH);
|
||
do {
|
||
r = tcsetattr(term.fd[i], TCSANOW, &term.origtermios[i]);
|
||
} while ( r < 0 && errno == EINTR );
|
||
if ( r < 0 ) {
|
||
char *tname;
|
||
|
||
tname = ttyname(term.fd[i]);
|
||
if ( ! tname ) tname = "UNKNOWN";
|
||
fprintf(stderr, "%s: reset failed for dev %s: %s\n",
|
||
__FUNCTION__, tname, strerror(errno));
|
||
}
|
||
term.fd[i] = -1;
|
||
}
|
||
} else {
|
||
/* initialize term structure. */
|
||
for (i = 0; i < MAX_TERMS; i++)
|
||
term.fd[i] = -1;
|
||
if ( atexit(term_exitfunc) != 0 ) {
|
||
term_errno = TERM_EATEXIT;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
/* ok. term struct is now initialized. */
|
||
term.init = 1;
|
||
}
|
||
} while(0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_add (int fd)
|
||
{
|
||
int rval, r, i;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
i = term_find(fd);
|
||
if ( i >= 0 ) {
|
||
term_errno = TERM_EEXISTS;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
if ( ! isatty(fd) ) {
|
||
term_errno = TERM_EISATTY;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
i = term_find_next_free();
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
r = tcgetattr(fd, &term.origtermios[i]);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_EGETATTR;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
term.currtermios[i] = term.origtermios[i];
|
||
term.nexttermios[i] = term.origtermios[i];
|
||
term.fd[i] = fd;
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_remove(int fd)
|
||
{
|
||
int rval, r, i;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
do { /* dummy */
|
||
r = tcflush(term.fd[i], TCIOFLUSH);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_EFLUSH;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
r = tcsetattr(term.fd[i], TCSANOW, &term.origtermios[i]);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_ESETATTR;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
} while (0);
|
||
|
||
term.fd[i] = -1;
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_erase(int fd)
|
||
{
|
||
int rval, i;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
term.fd[i] = -1;
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_replace (int oldfd, int newfd)
|
||
{
|
||
int rval, r, i;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(oldfd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
r = tcsetattr(newfd, TCSANOW, &term.currtermios[i]);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_ESETATTR;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
r = tcgetattr(newfd, &term.currtermios[i]);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_EGETATTR;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
term.fd[i] = newfd;
|
||
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_reset (int fd)
|
||
{
|
||
int rval, r, i;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
r = tcflush(term.fd[i], TCIOFLUSH);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_EFLUSH;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
r = tcsetattr(term.fd[i], TCSANOW, &term.origtermios[i]);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_ESETATTR;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
r = tcgetattr(term.fd[i], &term.currtermios[i]);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_EGETATTR;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
term.nexttermios[i] = term.currtermios[i];
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_revert (int fd)
|
||
{
|
||
int rval, i;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
term.nexttermios[i] = term.currtermios[i];
|
||
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_refresh (int fd)
|
||
{
|
||
int rval, r, i;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
r = tcgetattr(fd, &term.currtermios[i]);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_EGETATTR;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_apply (int fd, int now)
|
||
{
|
||
int when, rval, r, i;
|
||
|
||
when = now ? TCSANOW : TCSAFLUSH;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
r = tcsetattr(term.fd[i], when, &term.nexttermios[i]);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_ESETATTR;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
r = tcgetattr(term.fd[i], &term.nexttermios[i]);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_EGETATTR;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
term.currtermios[i] = term.nexttermios[i];
|
||
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_set_raw (int fd)
|
||
{
|
||
int rval, i;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
/* BSD raw mode */
|
||
cfmakeraw(&term.nexttermios[i]);
|
||
/* one byte at a time, no timer */
|
||
term.nexttermios[i].c_cc[VMIN] = 1;
|
||
term.nexttermios[i].c_cc[VTIME] = 0;
|
||
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_set_baudrate (int fd, int baudrate)
|
||
{
|
||
int rval, r, i;
|
||
speed_t spd;
|
||
struct termios tio;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
tio = term.nexttermios[i];
|
||
spd = Bcode(baudrate);
|
||
if ( spd != BNONE ) {
|
||
r = cfsetospeed(&tio, spd);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_ESETOSPEED;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
cfsetispeed(&tio, B0);
|
||
} else {
|
||
#ifdef USE_CUSTOM_BAUD
|
||
r = cfsetospeed_custom(&tio, baudrate);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_ESETOSPEED;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
cfsetispeed(&tio, B0);
|
||
#else /* ! defined USE_CUSTOM_BAUD */
|
||
term_errno = TERM_EBAUD;
|
||
rval = -1;
|
||
break;
|
||
#endif /* of USE_CUSTOM_BAUD */
|
||
}
|
||
|
||
term.nexttermios[i] = tio;
|
||
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
int
|
||
term_get_baudrate (int fd, int *ispeed)
|
||
{
|
||
speed_t code;
|
||
int i, ospeed;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
ospeed = -1;
|
||
break;
|
||
}
|
||
|
||
if ( ispeed ) {
|
||
code = cfgetispeed(&term.currtermios[i]);
|
||
*ispeed = Bspeed(code);
|
||
#ifdef USE_CUSTOM_BAUD
|
||
if ( *ispeed < 0 ) {
|
||
*ispeed = cfgetispeed_custom(&term.currtermios[i]);
|
||
}
|
||
#endif
|
||
}
|
||
code = cfgetospeed(&term.currtermios[i]);
|
||
ospeed = Bspeed(code);
|
||
if ( ospeed < 0 ) {
|
||
#ifdef USE_CUSTOM_BAUD
|
||
ospeed = cfgetospeed_custom(&term.currtermios[i]);
|
||
if ( ospeed < 0 ) {
|
||
term_errno = TERM_EGETSPEED;
|
||
}
|
||
#else
|
||
term_errno = TERM_EGETSPEED;
|
||
#endif
|
||
}
|
||
|
||
} while (0);
|
||
|
||
return ospeed;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_set_parity (int fd, enum parity_e parity)
|
||
{
|
||
int rval, i;
|
||
struct termios *tiop;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
tiop = &term.nexttermios[i];
|
||
|
||
switch (parity) {
|
||
case P_EVEN:
|
||
tiop->c_cflag &= ~(PARODD | CMSPAR);
|
||
tiop->c_cflag |= PARENB;
|
||
break;
|
||
case P_ODD:
|
||
tiop->c_cflag &= ~CMSPAR;
|
||
tiop->c_cflag |= PARENB | PARODD;
|
||
break;
|
||
case P_MARK:
|
||
tiop->c_cflag |= PARENB | PARODD | CMSPAR;
|
||
break;
|
||
case P_SPACE:
|
||
tiop->c_cflag &= ~PARODD;
|
||
tiop->c_cflag |= PARENB | CMSPAR;
|
||
break;
|
||
case P_NONE:
|
||
tiop->c_cflag &= ~(PARENB | PARODD | CMSPAR);
|
||
break;
|
||
default:
|
||
term_errno = TERM_EPARITY;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
if ( rval < 0 ) break;
|
||
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
enum parity_e
|
||
term_get_parity (int fd)
|
||
{
|
||
tcflag_t flg;
|
||
int i, parity;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
parity = -1;
|
||
break;
|
||
}
|
||
|
||
flg = term.currtermios[i].c_cflag;
|
||
if ( ! (flg & PARENB) ) {
|
||
parity = P_NONE;
|
||
} else if ( flg & CMSPAR ) {
|
||
parity = (flg & PARODD) ? P_MARK : P_SPACE;
|
||
} else {
|
||
parity = (flg & PARODD) ? P_ODD : P_EVEN;
|
||
}
|
||
|
||
} while (0);
|
||
|
||
return parity;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_set_databits (int fd, int databits)
|
||
{
|
||
int rval, i;
|
||
struct termios *tiop;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
tiop = &term.nexttermios[i];
|
||
|
||
switch (databits) {
|
||
case 5:
|
||
tiop->c_cflag = (tiop->c_cflag & ~CSIZE) | CS5;
|
||
break;
|
||
case 6:
|
||
tiop->c_cflag = (tiop->c_cflag & ~CSIZE) | CS6;
|
||
break;
|
||
case 7:
|
||
tiop->c_cflag = (tiop->c_cflag & ~CSIZE) | CS7;
|
||
break;
|
||
case 8:
|
||
tiop->c_cflag = (tiop->c_cflag & ~CSIZE) | CS8;
|
||
break;
|
||
default:
|
||
term_errno = TERM_EDATABITS;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
if ( rval < 0 ) break;
|
||
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
int
|
||
term_get_databits (int fd)
|
||
{
|
||
tcflag_t flg;
|
||
int i, bits;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
bits = -1;
|
||
break;
|
||
}
|
||
|
||
flg = term.currtermios[i].c_cflag & CSIZE;
|
||
switch (flg) {
|
||
case CS5:
|
||
bits = 5;
|
||
break;
|
||
case CS6:
|
||
bits = 6;
|
||
break;
|
||
case CS7:
|
||
bits = 7;
|
||
break;
|
||
case CS8:
|
||
default:
|
||
bits = 8;
|
||
break;
|
||
}
|
||
|
||
} while (0);
|
||
|
||
return bits;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_set_stopbits (int fd, int stopbits)
|
||
{
|
||
int rval, i;
|
||
struct termios *tiop;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
tiop = &term.nexttermios[i];
|
||
|
||
switch (stopbits) {
|
||
case 1:
|
||
tiop->c_cflag &= ~CSTOPB;
|
||
break;
|
||
case 2:
|
||
tiop->c_cflag |= CSTOPB;
|
||
break;
|
||
default:
|
||
term_errno = TERM_ESTOPBITS;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
if ( rval < 0 ) break;
|
||
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
int
|
||
term_get_stopbits (int fd)
|
||
{
|
||
int i, bits;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
bits = -1;
|
||
break;
|
||
}
|
||
|
||
bits = (term.currtermios[i].c_cflag & CSTOPB) ? 2 : 1;
|
||
|
||
} while (0);
|
||
|
||
return bits;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_set_flowcntrl (int fd, enum flowcntrl_e flowcntl)
|
||
{
|
||
int rval, i;
|
||
struct termios *tiop;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
tiop = &term.nexttermios[i];
|
||
|
||
switch (flowcntl) {
|
||
case FC_RTSCTS:
|
||
tiop->c_cflag |= CRTSCTS;
|
||
tiop->c_iflag &= ~(IXON | IXOFF | IXANY);
|
||
break;
|
||
case FC_XONXOFF:
|
||
tiop->c_cflag &= ~(CRTSCTS);
|
||
tiop->c_iflag |= IXON | IXOFF;
|
||
break;
|
||
case FC_NONE:
|
||
tiop->c_cflag &= ~(CRTSCTS);
|
||
tiop->c_iflag &= ~(IXON | IXOFF | IXANY);
|
||
break;
|
||
default:
|
||
term_errno = TERM_EFLOW;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
if ( rval < 0 ) break;
|
||
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
enum flowcntrl_e
|
||
term_get_flowcntrl (int fd)
|
||
{
|
||
int i, flow;
|
||
int rtscts, xoff, xon;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
flow = -1;
|
||
break;
|
||
}
|
||
|
||
rtscts = (term.currtermios[i].c_cflag & CRTSCTS) ? 1 : 0;
|
||
xoff = (term.currtermios[i].c_iflag & IXOFF) ? 1 : 0;
|
||
xon = (term.currtermios[i].c_iflag & (IXON | IXANY)) ? 1 : 0;
|
||
|
||
if ( rtscts && ! xoff && ! xon ) {
|
||
flow = FC_RTSCTS;
|
||
} else if ( ! rtscts && xoff && xon ) {
|
||
flow = FC_XONXOFF;
|
||
} else if ( ! rtscts && ! xoff && ! xon ) {
|
||
flow = FC_NONE;
|
||
} else {
|
||
flow = FC_OTHER;
|
||
}
|
||
|
||
} while (0);
|
||
|
||
return flow;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_set_local(int fd, int local)
|
||
{
|
||
int rval, i;
|
||
struct termios *tiop;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
tiop = &term.nexttermios[i];
|
||
|
||
if ( local )
|
||
tiop->c_cflag |= CLOCAL;
|
||
else
|
||
tiop->c_cflag &= ~CLOCAL;
|
||
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_set_hupcl (int fd, int on)
|
||
{
|
||
int rval, i;
|
||
struct termios *tiop;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
tiop = &term.nexttermios[i];
|
||
|
||
if ( on )
|
||
tiop->c_cflag |= HUPCL;
|
||
else
|
||
tiop->c_cflag &= ~HUPCL;
|
||
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_set(int fd,
|
||
int raw,
|
||
int baud,
|
||
enum parity_e parity,
|
||
int databits, int stopbits,
|
||
enum flowcntrl_e fc,
|
||
int local, int hup_close)
|
||
{
|
||
int rval, r, i, ni;
|
||
struct termios tio;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
ni = term_add(fd);
|
||
if ( ni < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
} else {
|
||
ni = i;
|
||
}
|
||
|
||
tio = term.nexttermios[ni];
|
||
|
||
do { /* dummy */
|
||
|
||
if (raw) {
|
||
r = term_set_raw(fd);
|
||
if ( r < 0 ) { rval = -1; break; }
|
||
}
|
||
|
||
r = term_set_baudrate(fd, baud);
|
||
if ( r < 0 ) { rval = -1; break; }
|
||
|
||
r = term_set_parity(fd, parity);
|
||
if ( r < 0 ) { rval = -1; break; }
|
||
|
||
r = term_set_databits(fd, databits);
|
||
if ( r < 0 ) { rval = -1; break; }
|
||
|
||
r = term_set_stopbits(fd, stopbits);
|
||
if ( r < 0 ) { rval = -1; break; }
|
||
|
||
r = term_set_flowcntrl(fd, fc);
|
||
if ( r < 0 ) { rval = -1; break; }
|
||
|
||
r = term_set_local(fd, local);
|
||
if ( r < 0 ) { rval = -1; break; }
|
||
|
||
r = term_set_hupcl(fd, hup_close);
|
||
if ( r < 0 ) { rval = -1; break; }
|
||
|
||
} while (0);
|
||
|
||
if ( rval < 0 ) {
|
||
if ( i < 0 )
|
||
/* new addition. must be removed */
|
||
term.fd[ni] = -1;
|
||
else
|
||
/* just revert to previous settings */
|
||
term.nexttermios[ni] = tio;
|
||
}
|
||
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_pulse_dtr (int fd)
|
||
{
|
||
int rval, r, i;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
#ifdef __linux__
|
||
{
|
||
int opins = TIOCM_DTR;
|
||
|
||
r = ioctl(fd, TIOCMBIC, &opins);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_EDTRDOWN;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
sleep(1);
|
||
|
||
r = ioctl(fd, TIOCMBIS, &opins);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_EDTRUP;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
}
|
||
#else
|
||
{
|
||
struct termios tio, tioold;
|
||
|
||
r = tcgetattr(fd, &tio);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_ESETATTR;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
tioold = tio;
|
||
|
||
cfsetospeed(&tio, B0);
|
||
cfsetispeed(&tio, B0);
|
||
r = tcsetattr(fd, TCSANOW, &tio);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_ESETATTR;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
sleep(1);
|
||
|
||
r = tcsetattr(fd, TCSANOW, &tioold);
|
||
if ( r < 0 ) {
|
||
term.currtermios[i] = tio;
|
||
term_errno = TERM_ESETATTR;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
}
|
||
#endif /* of __linux__ */
|
||
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_raise_dtr(int fd)
|
||
{
|
||
int rval, r, i;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
#ifdef __linux__
|
||
{
|
||
int opins = TIOCM_DTR;
|
||
|
||
r = ioctl(fd, TIOCMBIS, &opins);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_EDTRUP;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
}
|
||
#else
|
||
r = tcsetattr(fd, TCSANOW, &term.currtermios[i]);
|
||
if ( r < 0 ) {
|
||
/* FIXME: perhaps try to update currtermios */
|
||
term_errno = TERM_ESETATTR;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
#endif /* of __linux__ */
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
|
||
int
|
||
term_lower_dtr(int fd)
|
||
{
|
||
int rval, r, i;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
#ifdef __linux__
|
||
{
|
||
int opins = TIOCM_DTR;
|
||
|
||
r = ioctl(fd, TIOCMBIC, &opins);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_EDTRDOWN;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
}
|
||
#else
|
||
{
|
||
struct termios tio;
|
||
|
||
r = tcgetattr(fd, &tio);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_EGETATTR;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
term.currtermios[i] = tio;
|
||
|
||
cfsetospeed(&tio, B0);
|
||
cfsetispeed(&tio, B0);
|
||
|
||
r = tcsetattr(fd, TCSANOW, &tio);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_ESETATTR;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
}
|
||
#endif /* of __linux__ */
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_get_mctl (int fd)
|
||
{
|
||
int mctl, i;
|
||
|
||
do { /* dummy */
|
||
|
||
i = term_find(fd);
|
||
if ( i < 0 ) {
|
||
mctl = -1;
|
||
break;
|
||
}
|
||
|
||
#ifdef __linux__
|
||
{
|
||
int r, pmctl;
|
||
|
||
r = ioctl(fd, TIOCMGET, &pmctl);
|
||
if (r < 0) {
|
||
mctl = -1;
|
||
break;
|
||
}
|
||
mctl = 0;
|
||
if (pmctl & TIOCM_DTR) mctl |= MCTL_DTR;
|
||
if (pmctl & TIOCM_DSR) mctl |= MCTL_DSR;
|
||
if (pmctl & TIOCM_CD) mctl |= MCTL_DCD;
|
||
if (pmctl & TIOCM_RTS) mctl |= MCTL_RTS;
|
||
if (pmctl & TIOCM_CTS) mctl |= MCTL_CTS;
|
||
if (pmctl & TIOCM_RI) mctl |= MCTL_RI;
|
||
}
|
||
#else
|
||
mctl = MCTL_UNAVAIL;
|
||
#endif
|
||
} while(0);
|
||
|
||
return mctl;
|
||
}
|
||
|
||
int
|
||
term_drain(int fd)
|
||
{
|
||
int rval, r;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
r = term_find(fd);
|
||
if ( r < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
do {
|
||
#ifdef __BIONIC__
|
||
/* See: http://dan.drown.org/android/src/gdb/no-tcdrain */
|
||
r = ioctl(fd, TCSBRK, 1);
|
||
#else
|
||
r = tcdrain(fd);
|
||
#endif
|
||
} while ( r < 0 && errno == EINTR);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_EDRAIN;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_flush(int fd)
|
||
{
|
||
int rval, r;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
r = term_find(fd);
|
||
if ( r < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
r = tcflush(fd, TCIOFLUSH);
|
||
if ( r < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int
|
||
term_break(int fd)
|
||
{
|
||
int rval, r;
|
||
|
||
rval = 0;
|
||
|
||
do { /* dummy */
|
||
|
||
r = term_find(fd);
|
||
if ( r < 0 ) {
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
r = tcsendbreak(fd, 0);
|
||
if ( r < 0 ) {
|
||
term_errno = TERM_EBREAK;
|
||
rval = -1;
|
||
break;
|
||
}
|
||
|
||
} while (0);
|
||
|
||
return rval;
|
||
}
|
||
|
||
/**************************************************************************/
|
||
|
||
/*
|
||
* Local Variables:
|
||
* mode:c
|
||
* tab-width: 4
|
||
* c-basic-offset: 4
|
||
* End:
|
||
*/
|