summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/sms/services/kopete_unix_serial.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/protocols/sms/services/kopete_unix_serial.cpp')
-rw-r--r--kopete/protocols/sms/services/kopete_unix_serial.cpp445
1 files changed, 445 insertions, 0 deletions
diff --git a/kopete/protocols/sms/services/kopete_unix_serial.cpp b/kopete/protocols/sms/services/kopete_unix_serial.cpp
new file mode 100644
index 00000000..b694ab22
--- /dev/null
+++ b/kopete/protocols/sms/services/kopete_unix_serial.cpp
@@ -0,0 +1,445 @@
+// *************************************************************************
+// * Taken from the GSM TA/ME library
+// *
+// * File: gsm_unix_port.cc
+// *
+// * Purpose: UNIX serial port implementation with extras
+// *
+// * Original Author: Peter Hofmann (software@pxh.de)
+// * Modified by: Justin Huff (jjhuff@mspin.net)
+// *
+// * Created: 10.5.1999
+// *************************************************************************
+#include "config.h"
+#ifdef INCLUDE_SMSGSM
+
+#include <gsmlib/gsm_util.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <iostream>
+#include <sstream>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <pthread.h>
+#include <cassert>
+#include <assert.h>
+
+#include <qsocketnotifier.h>
+
+#include "kopete_unix_serial.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 other
+
+static pthread_mutex_t timerMtx = PTHREAD_MUTEX_INITIALIZER;
+#define pthread_mutex_lock(x)
+#define pthread_mutex_unlock(x)
+
+// for non-GNU systems, define alarm()
+#ifndef HAVE_ALARM
+unsigned 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 timeout
+static void catchAlarm(int)
+{
+ // do nothing
+}
+
+// start timer
+static void startTimer()
+{
+ pthread_mutex_lock(&timerMtx);
+ struct sigaction newAction;
+ newAction.sa_handler = catchAlarm;
+ newAction.sa_flags = 0;
+ sigaction(SIGALRM, &newAction, NULL);
+ alarm(1);
+}
+
+// reset timer
+static void stopTimer()
+{
+ alarm(0);
+ sigaction(SIGALRM, NULL, NULL);
+ pthread_mutex_unlock(&timerMtx);
+}
+
+// KopeteUnixSerialPort members
+
+void KopeteUnixSerialPort::throwModemException(string message) throw(GsmException)
+{
+ ostringstream os;
+ os << message << " (errno: " << errno << "/" << strerror(errno) << ")";
+ throw GsmException(os.str(), OSError, errno);
+}
+
+void KopeteUnixSerialPort::putBack(unsigned char c)
+{
+ assert(_oldChar == -1);
+ _oldChar = c;
+}
+
+int KopeteUnixSerialPort::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)
+ throwModemException("end of file when reading from TA");
+ 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;
+}
+
+KopeteUnixSerialPort::KopeteUnixSerialPort(string device, speed_t lineSpeed,
+ string initString, bool swHandshake)
+ throw(GsmException) :
+ _oldChar(-1), _timeoutVal(TIMEOUT_SECS)
+{
+ _readNotifier = NULL;
+
+ struct termios t;
+
+ // open device
+ _fd = open(device.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
+ if (_fd == -1)
+ throwModemException("opening device");
+
+ // switch off non-blocking mode
+ int fdFlags;
+ if ((fdFlags = fcntl(_fd, F_GETFL)) == -1)
+ {
+ close(_fd);
+ throwModemException("getting file status flags failed");
+ }
+ fdFlags &= ~O_NONBLOCK;
+ if (fcntl(_fd, F_SETFL, fdFlags) == -1)
+ {
+ close(_fd);
+ throwModemException("switching of non-blocking mode failed");
+ }
+
+ // Set the close on exec flag
+ if ((fdFlags = fcntl(_fd, F_GETFD)) == -1)
+ {
+ close(_fd);
+ throwModemException("getting file status flags failed");
+ }
+ fdFlags |= FD_CLOEXEC;
+ if (fcntl(_fd, F_SETFD, fdFlags) == -1)
+ {
+ close(_fd);
+ 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 && errno != ENOTTY)
+ {
+ close(_fd);
+ throwModemException("clearing DTR failed");
+ }
+ // the waiting time for DTR toggling is increased with each loop
+ usleep(holdoff[initTries]);
+ if (ioctl(_fd, TIOCMBIS, &mctl) < 0 && errno != ENOTTY)
+ {
+ close(_fd);
+ throwModemException("setting DTR failed");
+ }
+ // get line modes
+ if (tcgetattr(_fd, &t) < 0)
+ {
+ close(_fd);
+ throwModemException("tcgetattr device");
+ }
+
+ // 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)
+ {
+ close(_fd);
+ throwModemException("tcsetattr device");
+ }
+ // 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
+ }
+ else if (s.find("ERROR") != string::npos)
+ readTries = 0; // error, 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)
+ {
+ _readNotifier = new QSocketNotifier(_fd, QSocketNotifier::Read);
+ connect( _readNotifier, SIGNAL(activated(int)), this, SIGNAL(activated()));
+ return; // found OK, return
+ }
+ }
+ }
+ }
+ catch (GsmException &e)
+ {
+ _timeoutVal = saveTimeoutVal;
+ if (initTries == 0)
+ {
+ close(_fd);
+ throw e;
+ }
+ }
+ }
+ // no response after 3 tries
+ close(_fd);
+ throwModemException("reset modem failed");
+}
+
+string KopeteUnixSerialPort::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 KopeteUnixSerialPort::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 KopeteUnixSerialPort::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 KopeteUnixSerialPort::setTimeOut(unsigned int timeout)
+{
+ _timeoutVal = timeout;
+}
+
+KopeteUnixSerialPort::~KopeteUnixSerialPort()
+{
+ delete _readNotifier;
+ _readNotifier = NULL;
+ if (_fd != -1)
+ close(_fd);
+}
+
+#include "kopete_unix_serial.moc"
+
+#endif