/* * BRLTTY - A background process providing access to the console screen (when in * text mode) for a blind person using a refreshable braille display. * * Copyright (C) 1995-2018 by The BRLTTY Developers. * * BRLTTY comes with ABSOLUTELY NO WARRANTY. * * This is free software, placed under the terms of the * GNU Lesser General Public License, as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) any * later version. Please see the file LICENSE-LGPL for details. * * Web Page: http://brltty.com/ * * This software is maintained by Dave Mielke . */ #include "prologue.h" #include #include #include #include #include "log.h" #include "io_misc.h" #include "serial_termios.h" #include "serial_internal.h" BEGIN_SERIAL_BAUD_TABLE #ifdef B50 {50, B50}, #endif /* B50 */ #ifdef B75 {75, B75}, #endif /* B75 */ #ifdef B110 {110, B110}, #endif /* B110 */ #ifdef B134 {134, B134}, #endif /* B134 */ #ifdef B150 {150, B150}, #endif /* B150 */ #ifdef B200 {200, B200}, #endif /* B200 */ #ifdef B300 {300, B300}, #endif /* B300 */ #ifdef B600 {600, B600}, #endif /* B600 */ #ifdef B1200 {1200, B1200}, #endif /* B1200 */ #ifdef B1800 {1800, B1800}, #endif /* B1800 */ #ifdef B2400 {2400, B2400}, #endif /* B2400 */ #ifdef B4800 {4800, B4800}, #endif /* B4800 */ #ifdef B9600 {9600, B9600}, #endif /* B9600 */ #ifdef B19200 {19200, B19200}, #endif /* B19200 */ #ifdef B38400 {38400, B38400}, #endif /* B38400 */ #ifdef B57600 {57600, B57600}, #endif /* B57600 */ #ifdef B115200 {115200, B115200}, #endif /* B115200 */ #ifdef B230400 {230400, B230400}, #endif /* B230400 */ #ifdef B460800 {460800, B460800}, #endif /* B460800 */ #ifdef B500000 {500000, B500000}, #endif /* B500000 */ #ifdef B576000 {576000, B576000}, #endif /* B576000 */ #ifdef B921600 {921600, B921600}, #endif /* B921600 */ #ifdef B1000000 {1000000, B1000000}, #endif /* B1000000 */ #ifdef B1152000 {1152000, B1152000}, #endif /* B1152000 */ #ifdef B1500000 {1500000, B1500000}, #endif /* B1500000 */ #ifdef B2000000 {2000000, B2000000}, #endif /* B2000000 */ #ifdef B2500000 {2500000, B2500000}, #endif /* B2500000 */ #ifdef B3000000 {3000000, B3000000}, #endif /* B3000000 */ #ifdef B3500000 {3500000, B3500000}, #endif /* B3500000 */ #ifdef B4000000 {4000000, B4000000}, #endif /* B4000000 */ END_SERIAL_BAUD_TABLE void serialPutInitialAttributes (SerialAttributes *attributes) { attributes->c_cflag = CREAD; attributes->c_iflag = IGNPAR | IGNBRK; #ifdef IEXTEN attributes->c_lflag |= IEXTEN; #endif /* IEXTEN */ #ifdef _POSIX_VDISABLE if (_POSIX_VDISABLE) { unsigned int i; for (i=0; ic_cc[i] = _POSIX_VDISABLE; } } #endif /* _POSIX_VDISABLE */ } int serialPutSpeed (SerialAttributes *attributes, SerialSpeed speed) { if (cfsetospeed(attributes, speed) != -1) { if (cfsetispeed(attributes, speed) != -1) { return 1; } else { logSystemError("cfsetispeed"); } } else { logSystemError("cfsetospeed"); } return 0; } int serialPutDataBits (SerialAttributes *attributes, unsigned int bits) { tcflag_t size; switch (bits) { #ifdef CS5 case 5: size = CS5; break; #endif /* CS5 */ #ifdef CS6 case 6: size = CS6; break; #endif /* CS6 */ #ifdef CS7 case 7: size = CS7; break; #endif /* CS7 */ #ifdef CS8 case 8: size = CS8; break; #endif /* CS8 */ default: return 0; } attributes->c_cflag &= ~CSIZE; attributes->c_cflag |= size; return 1; } int serialPutStopBits (SerialAttributes *attributes, SerialStopBits bits) { if (bits == SERIAL_STOP_1) { attributes->c_cflag &= ~CSTOPB; } else if (bits == SERIAL_STOP_2) { attributes->c_cflag |= CSTOPB; } else { return 0; } return 1; } int serialPutParity (SerialAttributes *attributes, SerialParity parity) { attributes->c_cflag &= ~(PARENB | PARODD); #ifdef PARSTK attributes->c_cflag &= ~PARSTK; #endif /* PARSTK */ if (parity != SERIAL_PARITY_NONE) { if (parity == SERIAL_PARITY_ODD) { attributes->c_cflag |= PARODD; } else #ifndef PARSTK #ifdef CMSPAR #define PARSTK CMSPAR #endif /* CMSPAR */ #endif /* PARSTK */ #ifdef PARSTK if (parity == SERIAL_PARITY_SPACE) { attributes->c_cflag |= PARSTK; } else if (parity == SERIAL_PARITY_MARK) { attributes->c_cflag |= PARSTK | PARODD; } else #endif /* PARSTK */ if (parity != SERIAL_PARITY_EVEN) { return 0; } attributes->c_cflag |= PARENB; } return 1; } SerialFlowControl serialPutFlowControl (SerialAttributes *attributes, SerialFlowControl flow) { typedef struct { tcflag_t *field; tcflag_t flag; SerialFlowControl flow; } FlowControlEntry; const FlowControlEntry flowControlTable[] = { #ifdef CRTSCTS {&attributes->c_cflag, CRTSCTS, SERIAL_FLOW_OUTPUT_RTS | SERIAL_FLOW_OUTPUT_CTS}, #endif /* CRTSCTS */ #ifdef IHFLOW {&attributes->c_cflag, IHFLOW, SERIAL_FLOW_INPUT_RTS}, #endif /* IHFLOW */ #ifdef OHFLOW {&attributes->c_cflag, OHFLOW, SERIAL_FLOW_OUTPUT_CTS}, #endif /* OHFLOW */ #ifdef IXOFF {&attributes->c_iflag, IXOFF, SERIAL_FLOW_INPUT_XON}, #endif /* IXOFF */ #ifdef IXON {&attributes->c_iflag, IXON, SERIAL_FLOW_OUTPUT_XON}, #endif /* IXON */ {NULL, 0, 0} }; const FlowControlEntry *entry = flowControlTable; while (entry->field) { if ((flow & entry->flow) == entry->flow) { flow &= ~entry->flow; *entry->field |= entry->flag; } else if (!(flow & entry->flow)) { *entry->field &= ~entry->flag; } entry += 1; } return flow; } int serialPutModemState (SerialAttributes *attributes, int enabled) { if (enabled) { attributes->c_cflag &= ~CLOCAL; } else { attributes->c_cflag |= CLOCAL; } return 1; } unsigned int serialGetDataBits (const SerialAttributes *attributes) { tcflag_t size = attributes->c_cflag & CSIZE; switch (size) { #ifdef CS5 case CS5: return 5; #endif /* CS5 */ #ifdef CS6 case CS6: return 6; #endif /* CS6 */ #ifdef CS7 case CS7: return 7; #endif /* CS7 */ #ifdef CS8 case CS8: return 8; #endif /* CS8 */ default: logMessage(LOG_WARNING, "unsupported serial data bits value: %lX", (unsigned long)size); return 0; } } unsigned int serialGetStopBits (const SerialAttributes *attributes) { return (attributes->c_cflag & CSTOPB)? 2: 1; } unsigned int serialGetParityBits (const SerialAttributes *attributes) { return (attributes->c_cflag & PARENB)? 1: 0; } int serialGetAttributes (SerialDevice *serial, SerialAttributes *attributes) { if (tcgetattr(serial->fileDescriptor, attributes) != -1) return 1; logSystemError("tcgetattr"); return 0; } int serialPutAttributes (SerialDevice *serial, const SerialAttributes *attributes) { if (tcsetattr(serial->fileDescriptor, TCSANOW, attributes) != -1) return 1; logSystemError("tcsetattr"); return 0; } int serialCancelInput (SerialDevice *serial) { if (tcflush(serial->fileDescriptor, TCIFLUSH) != -1) return 1; if (errno == EINVAL) return 1; logSystemError("TCIFLUSH"); return 0; } int serialCancelOutput (SerialDevice *serial) { if (tcflush(serial->fileDescriptor, TCOFLUSH) != -1) return 1; if (errno == EINVAL) return 1; logSystemError("TCOFLUSH"); return 0; } static void serialCancelInputMonitor (SerialDevice *serial) { if (serial->package.inputMonitor) { asyncCancelRequest(serial->package.inputMonitor); serial->package.inputMonitor = NULL; } } int serialMonitorInput (SerialDevice *serial, AsyncMonitorCallback *callback, void *data) { serialCancelInputMonitor(serial); if (!callback) return 1; return asyncMonitorFileInput(&serial->package.inputMonitor, serial->fileDescriptor, callback, data); } int serialPollInput (SerialDevice *serial, int timeout) { return awaitFileInput(serial->fileDescriptor, timeout); } int serialDrainOutput (SerialDevice *serial) { #ifdef HAVE_TCDRAIN do { if (tcdrain(serial->fileDescriptor) != -1) return 1; } while (errno == EINTR); #else /* HAVE_TCDRAIN */ errno = ENOSYS; #endif /* HAVE_TCDRAIN */ logSystemError("tcdrain"); return 0; } ssize_t serialGetData ( SerialDevice *serial, void *buffer, size_t size, int initialTimeout, int subsequentTimeout ) { return readFile(serial->fileDescriptor, buffer, size, initialTimeout, subsequentTimeout); } ssize_t serialPutData ( SerialDevice *serial, const void *data, size_t size ) { return writeFile(serial->fileDescriptor, data, size); } int serialGetLines (SerialDevice *serial) { #ifdef TIOCMGET if (ioctl(serial->fileDescriptor, TIOCMGET, &serial->linesState) == -1) { logSystemError("TIOCMGET"); return 0; } #else /* TIOCMGET */ #warning getting modem lines not supported on this platform serial->linesState = SERIAL_LINE_RTS | SERIAL_LINE_CTS | SERIAL_LINE_DTR | SERIAL_LINE_DSR | SERIAL_LINE_CAR; #endif /* TIOCMGET */ return 1; } int serialPutLines (SerialDevice *serial, SerialLines high, SerialLines low) { #ifdef TIOCMSET if (serialGetLines(serial)) { SerialLines lines = serial->linesState; lines |= high; lines &= ~low; if (ioctl(serial->fileDescriptor, TIOCMSET, &lines) != -1) return 1; logSystemError("TIOCMSET"); } #else /* TIOCMSET */ #warning setting modem lines not supported on this platform #endif /* TIOCMSET */ return 0; } int serialRegisterWaitLines (SerialDevice *serial, SerialLines lines) { return 1; } int serialMonitorWaitLines (SerialDevice *serial) { #ifdef TIOCMIWAIT if (ioctl(serial->fileDescriptor, TIOCMIWAIT, serial->waitLines) != -1) return 1; logSystemError("TIOCMIWAIT"); #else /* TIOCMIWAIT */ SerialLines old = serial->linesState & serial->waitLines; while (serialGetLines(serial)) { if ((serial->linesState & serial->waitLines) != old) return 1; } #endif /* TIOCMIWAIT */ return 0; } int serialConnectDevice (SerialDevice *serial, const char *device) { serial->package.inputMonitor = NULL; if ((serial->fileDescriptor = open(device, O_RDWR|O_NOCTTY|O_NONBLOCK)) != -1) { if (isatty(serial->fileDescriptor)) { if (serialPrepareDevice(serial)) { logMessage(LOG_CATEGORY(SERIAL_IO), "device opened: %s: fd=%d", device, serial->fileDescriptor); return 1; } } else { logMessage(LOG_ERR, "not a serial device: %s", device); } close(serial->fileDescriptor); } else { logMessage(((errno == ENOENT)? LOG_DEBUG: LOG_ERR), "cannot open serial device: %s: %s", device, strerror(errno)); } return 0; } void serialDisconnectDevice (SerialDevice *serial) { serialCancelInputMonitor(serial); } int serialEnsureFileDescriptor (SerialDevice *serial) { return 1; } void serialClearError (SerialDevice *serial) { }