📄 gsm_unix_serial.cc
字号:
// *************************************************************************// * GSM TA/ME library// *// * File: gsm_unix_port.cc// *// * Purpose: UNIX serial port implementation// *// * Author: Peter Hofmann (software@pxh.de)// *// * Created: 10.5.1999// *************************************************************************#ifdef HAVE_CONFIG_H#include <gsm_config.h>#endif#include <gsmlib/gsm_nls.h>#include <gsmlib/gsm_unix_serial.h>#include <gsmlib/gsm_util.h>#include <termios.h>#include <fcntl.h>#include <iostream>#include <strstream>#include <errno.h>#include <stdio.h>#include <unistd.h>#include <sys/ioctl.h>#include <signal.h>#include <pthread.h>using namespace std;using namespace gsmlib;static const int holdoff[] = {2000000, 1000000, 400000};static const int holdoffArraySize = sizeof(holdoff)/sizeof(int); // alarm handling for socket read/write// the timerMtx is necessary since several threads cannot use the// timer indepently of each otherstatic pthread_mutex_t timerMtx = PTHREAD_MUTEX_INITIALIZER;// for non-GNU systems, define alarm()#ifndef HAVE_ALARMunsigned int alarm(unsigned int seconds){ struct itimerval old, newt; newt.it_interval.tv_usec = 0; newt.it_interval.tv_sec = 0; newt.it_value.tv_usec = 0; newt.it_value.tv_sec = (long int)seconds; if (setitimer(ITIMER_REAL, &newt, &old) < 0) return 0; else return old.it_value.tv_sec;}#endif// this routine is called in case of a timeoutstatic void catchAlarm(int){ // do nothing}// start timerstatic void startTimer(){ pthread_mutex_lock(&timerMtx); struct sigaction newAction; newAction.sa_handler = catchAlarm; newAction.sa_flags = 0; sigaction(SIGALRM, &newAction, NULL); alarm(1);}// reset timerstatic void stopTimer(){ alarm(0); sigaction(SIGALRM, NULL, NULL); pthread_mutex_unlock(&timerMtx);}// UnixSerialPort membersvoid UnixSerialPort::throwModemException(string message) throw(GsmException){ ostrstream os; os << message << " (errno: " << errno << "/" << strerror(errno) << ")" << ends; char *ss = os.str(); string s(ss); delete[] ss; throw GsmException(s, OSError, errno);}void UnixSerialPort::putBack(char c){ assert(_oldChar == -1); _oldChar = c;}int UnixSerialPort::readByte() throw(GsmException){ if (_oldChar != -1) { int result = _oldChar; _oldChar = -1; return result; } unsigned char c; int timeElapsed = 0; struct timeval oneSecond; bool readDone = false; while (! readDone && timeElapsed < _timeoutVal) { if (interrupted()) throwModemException(_("interrupted when reading from TA")); // setup fd_set data structure for select() fd_set fdSet; oneSecond.tv_sec = 1; oneSecond.tv_usec = 0; FD_ZERO(&fdSet); FD_SET(_fd, &fdSet); switch (select(FD_SETSIZE, &fdSet, NULL, NULL, &oneSecond)) { case 1: { int res = read(_fd, &c, 1); if (res != 1) return -1; else readDone = true; break; } case 0: ++timeElapsed; break; default: if (errno != EINTR) throwModemException(_("reading from TA")); break; } } if (! readDone) throwModemException(_("timeout when reading from TA"));#ifndef NDEBUG if (debugLevel() >= 2) { // some useful debugging code if (c == LF) cerr << "<LF>"; else if (c == CR) cerr << "<CR>"; else cerr << "<'" << (char) c << "'>"; cerr.flush(); }#endif return c;}UnixSerialPort::UnixSerialPort(string device, speed_t lineSpeed, string initString, bool swHandshake) throw(GsmException) : _oldChar(-1), _timeoutVal(TIMEOUT_SECS){ struct termios t; // open device _fd = open(device.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK); if (_fd == -1) throwModemException(stringPrintf(_("opening device '%s'"), device.c_str())); // switch off non-blocking mode int fdFlags; if ((fdFlags = fcntl(_fd, F_GETFL)) == -1) throwModemException(_("getting file status flags failed")); fdFlags &= ~O_NONBLOCK; if (fcntl(_fd, F_SETFL, fdFlags) == -1) throwModemException(_("switching of non-blocking mode failed")); long int saveTimeoutVal = _timeoutVal; _timeoutVal = 3; int initTries = holdoffArraySize; while (initTries-- > 0) { // flush all pending output tcflush(_fd, TCOFLUSH); // toggle DTR to reset modem int mctl = TIOCM_DTR; if (ioctl(_fd, TIOCMBIC, &mctl) < 0) throwModemException(_("clearing DTR failed")); // the waiting time for DTR toggling is increased with each loop usleep(holdoff[initTries]); if (ioctl(_fd, TIOCMBIS, &mctl) < 0) throwModemException(_("setting DTR failed")); // get line modes if (tcgetattr(_fd, &t) < 0) throwModemException(stringPrintf(_("tcgetattr device '%s'"), device.c_str())); // set line speed cfsetispeed(&t, lineSpeed); cfsetospeed(&t, lineSpeed); // set the device to a sane state t.c_iflag |= IGNPAR | (swHandshake ? IXON | IXOFF : 0); t.c_iflag &= ~(INPCK | ISTRIP | IMAXBEL | (swHandshake ? 0 : IXON | IXOFF) | IXANY | IGNCR | ICRNL | IMAXBEL | INLCR | IGNBRK); t.c_oflag &= ~(OPOST); // be careful, only touch "known" flags t.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | (swHandshake ? CRTSCTS : 0 )); t.c_cflag |= CS8 | CREAD | HUPCL | (swHandshake ? 0 : CRTSCTS) | CLOCAL; t.c_lflag &= ~(ECHO | ECHOE | ECHOPRT | ECHOK | ECHOKE | ECHONL | ECHOCTL | ISIG | IEXTEN | TOSTOP | FLUSHO | ICANON); t.c_lflag |= NOFLSH; t.c_cc[VMIN] = 1; t.c_cc[VTIME] = 0; t.c_cc[VSUSP] = 0; // write back if(tcsetattr (_fd, TCSANOW, &t) < 0) throwModemException(stringPrintf(_("tcsetattr device '%s'"), device.c_str())); // the waiting time for writing to the ME/TA is increased with each loop usleep(holdoff[initTries]); // flush all pending input tcflush(_fd, TCIFLUSH); try { // reset modem putLine("ATZ"); bool foundOK = false; int readTries = 5; while (readTries-- > 0) { // for the first call getLine() waits only 3 seconds // because of _timeoutVal = 3 string s = getLine(); if (s.find("OK") != string::npos || s.find("CABLE: GSM") != string::npos) { foundOK = true; readTries = 0; // found OK, exit loop } } // set getLine/putLine timeout back to old value _timeoutVal = saveTimeoutVal; if (foundOK) { // init modem readTries = 5; putLine("AT" + initString); while (readTries-- > 0) { string s = getLine(); if (s.find("OK") != string::npos || s.find("CABLE: GSM") != string::npos) return; // found OK, return } } } catch (GsmException &e) { _timeoutVal = saveTimeoutVal; if (initTries == 0) throw e; } } // no response after 3 tries throw GsmException(stringPrintf(_("reset modem failed '%s'"), device.c_str()), OtherError);}string UnixSerialPort::getLine() throw(GsmException){ string result; int c; while ((c = readByte()) >= 0) { while (c == CR) { c = readByte(); } if (c == LF) break; result += c; }#ifndef NDEBUG if (debugLevel() >= 1) cerr << "<-- " << result << endl;#endif return result;}void UnixSerialPort::putLine(string line, bool carriageReturn) throw(GsmException){#ifndef NDEBUG if (debugLevel() >= 1) cerr << "--> " << line << endl;#endif if (carriageReturn) line += CR; const char *l = line.c_str(); int timeElapsed = 0; struct timeval oneSecond; ssize_t bytesWritten = 0; while (bytesWritten < (ssize_t)line.length() && timeElapsed < _timeoutVal) { if (interrupted()) throwModemException(_("interrupted when writing to TA")); // setup fd_set data structure for select() fd_set fdSet; oneSecond.tv_sec = 1; oneSecond.tv_usec = 0; FD_ZERO(&fdSet); FD_SET(_fd, &fdSet); switch (select(FD_SETSIZE, NULL, &fdSet, NULL, &oneSecond)) { case 1: { ssize_t bw = write(_fd, l + bytesWritten, line.length() - bytesWritten); if (bw < 0) throwModemException(_("writing to TA")); bytesWritten += bw; break; } case 0: ++timeElapsed; break; default: if (errno != EINTR) throwModemException(_("writing to TA")); break; } } while (timeElapsed < _timeoutVal) { if (interrupted()) throwModemException(_("interrupted when writing to TA")); startTimer(); int res = tcdrain(_fd); // wait for output to be read by TA stopTimer(); if (res == 0) break; else { assert(errno == EINTR); ++timeElapsed; } } if (timeElapsed >= _timeoutVal) throwModemException(_("timeout when writing to TA")); // echo CR LF must be removed by higher layer functions in gsm_at because // in order to properly handle unsolicited result codes from the ME/TA}bool UnixSerialPort::wait(GsmTime timeout) throw(GsmException){ fd_set fds; FD_ZERO(&fds); FD_SET(_fd, &fds); return select(FD_SETSIZE, &fds, NULL, NULL, timeout) != 0;}// set timeout for read or write in seconds.void UnixSerialPort::setTimeOut(unsigned int timeout){ _timeoutVal = timeout;}UnixSerialPort::~UnixSerialPort(){ if (_fd != -1) close(_fd);}speed_t gsmlib::baudRateStrToSpeed(string baudrate) throw(GsmException){ if (baudrate == "300") return B300; else if (baudrate == "600") return B600; else if (baudrate == "1200") return B1200; else if (baudrate == "2400") return B2400; else if (baudrate == "4800") return B4800; else if (baudrate == "9600") return B9600; else if (baudrate == "19200") return B19200; else if (baudrate == "38400") return B38400;#ifdef B57600 else if (baudrate == "57600") return B57600;#endif#ifdef B115200 else if (baudrate == "115200") return B115200;#endif#ifdef B230400 else if (baudrate == "230400") return B230400;#endif#ifdef B460800 else if (baudrate == "460800") return B460800;#endif else throw GsmException(stringPrintf(_("unknown baudrate '%s'"), baudrate.c_str()), ParameterError);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -