344 lines
9.5 KiB
C
344 lines
9.5 KiB
C
/*
|
|
* Support for tty settings using termios
|
|
*
|
|
* (c) 2016 Steve Bennett <steveb@workware.net.au>
|
|
*
|
|
*/
|
|
|
|
/* termios support is required */
|
|
|
|
#include <jim-tty.h>
|
|
#include <termios.h>
|
|
|
|
static const struct {
|
|
unsigned baud;
|
|
speed_t speed;
|
|
} baudtable[] = {
|
|
{ 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 B230400
|
|
{ 230400, B230400 },
|
|
#endif
|
|
#ifdef B460800
|
|
{ 460800, B460800 }
|
|
#endif
|
|
};
|
|
|
|
struct flag_name_map {
|
|
const char *name;
|
|
unsigned value;
|
|
};
|
|
|
|
static const struct flag_name_map parity_map[] = {
|
|
{ "none", 0 },
|
|
{ "even", PARENB },
|
|
{ "odd", PARENB | PARODD },
|
|
};
|
|
static const struct flag_name_map data_size_map[] = {
|
|
{ "5", CS5 },
|
|
{ "6", CS6 },
|
|
{ "7", CS7 },
|
|
{ "8", CS8 },
|
|
};
|
|
static const struct flag_name_map stop_size_map[] = {
|
|
{ "1", 0 },
|
|
{ "2", CSTOPB },
|
|
};
|
|
static const struct flag_name_map input_map[] = {
|
|
{ "raw", 0 },
|
|
{ "cooked", ICANON },
|
|
};
|
|
static const struct flag_name_map output_map[] = {
|
|
{ "raw", 0 },
|
|
{ "cooked", OPOST },
|
|
};
|
|
|
|
static const char * const tty_settings_names[] = {
|
|
"baud",
|
|
"data",
|
|
"handshake",
|
|
"input",
|
|
"output",
|
|
"parity",
|
|
"stop",
|
|
"vmin",
|
|
"vtime",
|
|
"echo",
|
|
NULL
|
|
};
|
|
|
|
enum {
|
|
OPT_BAUD,
|
|
OPT_DATA,
|
|
OPT_HANDSHAKE,
|
|
OPT_INPUT,
|
|
OPT_OUTPUT,
|
|
OPT_PARITY,
|
|
OPT_STOP,
|
|
OPT_VMIN,
|
|
OPT_VTIME,
|
|
OPT_ECHO
|
|
};
|
|
|
|
|
|
#define ARRAYSIZE(A) (sizeof(A)/sizeof(*(A)))
|
|
|
|
/**
|
|
* Search the flag/name map for an entry with the given name.
|
|
* (Actually, just matching the first char)
|
|
* Returns a pointer to the entry if found, or NULL if not.
|
|
*/
|
|
static const struct flag_name_map *flag_name_to_value(const struct flag_name_map *map, int len, const char *name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
/* Only need to compare the first character since all names are unique in the first char */
|
|
if (*name == *map[i].name) {
|
|
return &map[i];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Search the flag/name map for an entry with the matching value.
|
|
* Returns the corresponding name if found, or NULL if no match.
|
|
*/
|
|
static const char *flag_value_to_name(const struct flag_name_map *map, int len, unsigned value)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (value == map[i].value) {
|
|
return map[i].name;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* If 'str2' is not NULL, appends 'str1' and 'str2' to the list.
|
|
* Otherwise does nothing.
|
|
*/
|
|
static void JimListAddPair(Jim_Interp *interp, Jim_Obj *listObjPtr, const char *str1, const char *str2)
|
|
{
|
|
if (str2) {
|
|
Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, str1, -1));
|
|
Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, str2, -1));
|
|
}
|
|
}
|
|
|
|
Jim_Obj *Jim_GetTtySettings(Jim_Interp *interp, int fd)
|
|
{
|
|
struct termios tio;
|
|
size_t i;
|
|
const char *p;
|
|
Jim_Obj *listObjPtr;
|
|
speed_t speed;
|
|
int baud;
|
|
|
|
if (tcgetattr(fd, &tio) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
listObjPtr = Jim_NewListObj(interp, NULL, 0);
|
|
|
|
p = flag_value_to_name(parity_map, ARRAYSIZE(parity_map), tio.c_cflag & (PARENB | PARODD));
|
|
JimListAddPair(interp, listObjPtr, "parity", p);
|
|
p = flag_value_to_name(data_size_map, ARRAYSIZE(data_size_map), tio.c_cflag & CSIZE);
|
|
JimListAddPair(interp, listObjPtr, "data", p);
|
|
p = flag_value_to_name(stop_size_map, ARRAYSIZE(stop_size_map), tio.c_cflag & CSTOPB);
|
|
JimListAddPair(interp, listObjPtr, "stop", p);
|
|
if (tio.c_iflag & (IXON | IXOFF)) {
|
|
p = "xonxoff";
|
|
}
|
|
else if (tio.c_cflag & CRTSCTS) {
|
|
p = "rtscts";
|
|
}
|
|
else {
|
|
p = "none";
|
|
}
|
|
JimListAddPair(interp, listObjPtr, "handshake", p);
|
|
p = flag_value_to_name(input_map, ARRAYSIZE(input_map), tio.c_lflag & ICANON);
|
|
JimListAddPair(interp, listObjPtr, "input", p);
|
|
p = flag_value_to_name(output_map, ARRAYSIZE(output_map), tio.c_oflag & OPOST);
|
|
JimListAddPair(interp, listObjPtr, "output", p);
|
|
|
|
Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, "vmin", -1));
|
|
Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, tio.c_cc[VMIN]));
|
|
Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, "vtime", -1));
|
|
Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, tio.c_cc[VTIME]));
|
|
|
|
speed = cfgetispeed(&tio);
|
|
baud = 0;
|
|
for (i = 0; i < sizeof(baudtable) / sizeof(*baudtable); i++) {
|
|
if (baudtable[i].speed == speed) {
|
|
baud = baudtable[i].baud;
|
|
break;
|
|
}
|
|
}
|
|
Jim_ListAppendElement(interp, listObjPtr, Jim_NewStringObj(interp, "baud", -1));
|
|
Jim_ListAppendElement(interp, listObjPtr, Jim_NewIntObj(interp, baud));
|
|
|
|
return listObjPtr;
|
|
}
|
|
|
|
int Jim_SetTtySettings(Jim_Interp *interp, int fd, Jim_Obj *dictObjPtr)
|
|
{
|
|
int len = Jim_ListLength(interp, dictObjPtr);
|
|
int i;
|
|
|
|
struct termios tio;
|
|
|
|
if (tcgetattr(fd, &tio) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < len; i += 2) {
|
|
Jim_Obj *nameObj = Jim_ListGetIndex(interp, dictObjPtr, i);
|
|
Jim_Obj *valueObj = Jim_ListGetIndex(interp, dictObjPtr, i + 1);
|
|
int opt;
|
|
const struct flag_name_map *p;
|
|
long l;
|
|
size_t j;
|
|
|
|
if (Jim_GetEnum(interp, nameObj, tty_settings_names, &opt, "setting", JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) {
|
|
return JIM_ERR;
|
|
}
|
|
|
|
switch (opt) {
|
|
case OPT_BAUD:
|
|
if (Jim_GetLong(interp, valueObj, &l) != JIM_OK) {
|
|
goto badvalue;
|
|
}
|
|
for (j = 0; j < ARRAYSIZE(baudtable); j++) {
|
|
if (baudtable[j].baud == l) {
|
|
break;
|
|
}
|
|
}
|
|
if (j == ARRAYSIZE(baudtable)) {
|
|
goto badvalue;
|
|
}
|
|
cfsetospeed(&tio, baudtable[j].speed);
|
|
cfsetispeed(&tio, baudtable[j].speed);
|
|
break;
|
|
|
|
case OPT_PARITY:
|
|
p = flag_name_to_value(parity_map, ARRAYSIZE(parity_map), Jim_String(valueObj));
|
|
if (p == NULL) {
|
|
badvalue:
|
|
Jim_SetResultFormatted(interp, "bad value for %#s: %#s", nameObj, valueObj);
|
|
return JIM_ERR;
|
|
}
|
|
tio.c_cflag &= ~(PARENB | PARODD);
|
|
tio.c_cflag |= p->value;
|
|
break;
|
|
|
|
case OPT_STOP:
|
|
p = flag_name_to_value(stop_size_map, ARRAYSIZE(stop_size_map), Jim_String(valueObj));
|
|
if (p == NULL) {
|
|
goto badvalue;
|
|
}
|
|
tio.c_cflag &= ~CSTOPB;
|
|
tio.c_cflag |= p->value;
|
|
break;
|
|
|
|
case OPT_DATA:
|
|
p = flag_name_to_value(data_size_map, ARRAYSIZE(data_size_map), Jim_String(valueObj));
|
|
if (p == NULL) {
|
|
goto badvalue;
|
|
}
|
|
tio.c_cflag &= ~CSIZE;
|
|
tio.c_cflag |= p->value;
|
|
break;
|
|
|
|
case OPT_HANDSHAKE:
|
|
tio.c_iflag &= ~(IXON | IXOFF);
|
|
tio.c_cflag &= ~(CRTSCTS);
|
|
if (Jim_CompareStringImmediate(interp, valueObj, "xonxoff")) {
|
|
tio.c_iflag |= (IXON | IXOFF);
|
|
}
|
|
else if (Jim_CompareStringImmediate(interp, valueObj, "rtscts")) {
|
|
tio.c_cflag |= CRTSCTS;
|
|
}
|
|
else if (!Jim_CompareStringImmediate(interp, valueObj, "none")) {
|
|
goto badvalue;
|
|
}
|
|
break;
|
|
|
|
case OPT_VMIN:
|
|
if (Jim_GetLong(interp, valueObj, &l) != JIM_OK) {
|
|
goto badvalue;
|
|
}
|
|
tio.c_cc[VMIN] = l;
|
|
break;
|
|
|
|
case OPT_VTIME:
|
|
if (Jim_GetLong(interp, valueObj, &l) != JIM_OK) {
|
|
goto badvalue;
|
|
}
|
|
tio.c_cc[VTIME] = l;
|
|
break;
|
|
|
|
case OPT_OUTPUT:
|
|
p = flag_name_to_value(output_map, ARRAYSIZE(output_map), Jim_String(valueObj));
|
|
if (p == NULL) {
|
|
goto badvalue;
|
|
}
|
|
tio.c_oflag &= ~OPOST;
|
|
tio.c_oflag |= p->value;
|
|
break;
|
|
|
|
case OPT_INPUT:
|
|
p = flag_name_to_value(input_map, ARRAYSIZE(input_map), Jim_String(valueObj));
|
|
if (p == NULL) {
|
|
goto badvalue;
|
|
}
|
|
if (p->value) {
|
|
tio.c_lflag |= (ECHO | ECHOE | ECHOK | ECHONL | ICANON | IEXTEN | ISIG | NOFLSH | TOSTOP);
|
|
tio.c_iflag |= ICRNL;
|
|
}
|
|
else {
|
|
tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON | IEXTEN | ISIG | NOFLSH | TOSTOP);
|
|
tio.c_iflag &= ~ICRNL;
|
|
}
|
|
break;
|
|
|
|
case OPT_ECHO:
|
|
if (Jim_GetLong(interp, valueObj, &l) != JIM_OK) {
|
|
goto badvalue;
|
|
}
|
|
if (l) {
|
|
tio.c_lflag |= ECHO;
|
|
}
|
|
else {
|
|
tio.c_lflag &= ~ECHO;
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
if (tcsetattr(fd, TCSAFLUSH, &tio) < 0) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|