--- /dev/null
+/*
+ $Id$
+
+ G N O K I I
+
+ A Linux/Unix toolset and driver for Nokia mobile phones.
+
+ Released under the terms of the GNU GPL, see file COPYING for more details.
+
+ This file provides a virtual modem interface to the GSM phone by calling
+ code in gsm-api.c, at-emulator.c and datapump.c. The code here provides
+ the overall framework and coordinates switching between command mode
+ (AT-emulator) and "online" mode where the data pump code translates data
+ from/to the GSM handset and the modem data/fax stream.
+
+*/
+
+#define __virtmodem_c
+
+#include <config.h>
+
+/* This is the right way to include stdlib with __USE_XOPEN defined */
+#ifdef USE_UNIX98PTYS
+# define _XOPEN_SOURCE 500
+# include <features.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <termios.h>
+#include <grp.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include "misc.h"
+#include "gsm-api.h"
+#include "gsm-common.h"
+#include "data/at-emulator.h"
+#include "data/datapump.h"
+#include "data/virtmodem.h"
+#include "data/rlp-common.h"
+
+ /* Global variables */
+
+//extern bool TerminateThread;
+int ConnectCount;
+
+ /* Local variables */
+
+int PtyRDFD; /* File descriptor for reading and writing to/from */
+int PtyWRFD; /* pty interface - only different in debug mode. */
+
+bool UseSTDIO; /* Use STDIO for debugging purposes instead of pty */
+bool CommandMode;
+
+pthread_t Thread;
+bool RequestTerminate;
+
+
+ /* If initialised in debug mode, stdin/out is used instead
+ of ptys for interface. */
+bool VM_Initialise(char *model,char *port, char *initlength, GSM_ConnectionType connection, char *bindir, bool debug_mode, bool GSMInit,char *synchronizetime)
+{
+ int rtn;
+
+ CommandMode = true;
+
+ RequestTerminate = false;
+
+ if (debug_mode == true) {
+ UseSTDIO = true;
+ }
+ else {
+ UseSTDIO = false;
+ }
+
+ if (GSMInit) {
+#ifdef DEBUG
+ fprintf (stderr , "Initialising GSM\n");
+#endif /* DEBUG */
+ if ((VM_GSMInitialise(model, port, initlength, connection, synchronizetime) != GE_NONE)) {
+ fprintf (stderr, _("VM_Initialise - VM_GSMInitialise failed!\n"));
+ return (false);
+
+ }
+ }
+ GSMInit=false;
+
+ if (VM_PtySetup(bindir) < 0) {
+ fprintf (stderr, _("VM_Initialise - VM_PtySetup failed!\n"));
+ return (false);
+ }
+
+ if (ATEM_Initialise(PtyRDFD, PtyWRFD, model, port) != true) {
+ fprintf (stderr, _("VM_Initialise - ATEM_Initialise failed!\n"));
+ return (false);
+ }
+
+ if (DP_Initialise(PtyRDFD, PtyWRFD) != true) {
+ fprintf (stderr, _("VM_Initialise - DP_Initialise failed!\n"));
+ return (false);
+ }
+
+ /* Create and start thread, */
+ rtn = pthread_create(&Thread, NULL, (void *) VM_ThreadLoop, (void *)NULL);
+
+ if (rtn == EAGAIN || rtn == EINVAL) {
+ return (false);
+ }
+ return (true);
+}
+
+void VM_ThreadLoop(void)
+{
+ int res;
+ struct pollfd ufds;
+
+ /* Note we can't use signals here as they are already used
+ in the FBUS code. This may warrant changing the FBUS
+ code around sometime to use select instead to free up
+ the SIGIO handler for mainline code. */
+
+ ufds.fd=PtyRDFD;
+ ufds.events=POLLIN;
+
+ while (!RequestTerminate) {
+ if (!CommandMode) {
+ sleep(1);
+ } else { /* If we are in data mode, leave it to datapump to get the data */
+
+ res=poll(&ufds,1,500);
+
+ switch (res) {
+ case 0: /* Timeout */
+ break;
+
+ case -1:
+ perror("VM_ThreadLoop - select");
+ exit (-1);
+
+ default:
+ if (ufds.revents==POLLIN) {
+ VM_CharHandler();
+ } else usleep(500); /* Probably the file has been closed */
+ break;
+ }
+ }
+ }
+
+}
+
+ /* Application should call VM_Terminate to shut down
+ the virtual modem thread */
+void VM_Terminate(void)
+{
+
+ /* Request termination of thread */
+ RequestTerminate = true;
+
+ /* Now wait for thread to terminate. */
+ pthread_join(Thread, NULL);
+
+ if (!UseSTDIO) {
+ close (PtyRDFD);
+ close (PtyWRFD);
+ }
+}
+
+ /* Open pseudo tty interface and (in due course create a symlink
+ to be /dev/gnokii etc. ) */
+
+int VM_PtySetup(char *bindir)
+{
+ int err;
+ char mgnokiidev[200];
+ char *slave_name;
+ char cmdline[200];
+
+ if (bindir) {
+ strncpy(mgnokiidev, bindir, 200);
+ strcat(mgnokiidev, "/");
+ }
+ strncat(mgnokiidev, "mgnokiidev", 200 - strlen(bindir));
+
+ if (UseSTDIO) {
+ PtyRDFD = STDIN_FILENO;
+ PtyWRFD = STDOUT_FILENO;
+ return (0);
+ }
+
+ PtyRDFD = VM_GetMasterPty(&slave_name);
+ if (PtyRDFD < 0) {
+ fprintf (stderr, _("Couldn't open pty!\n"));
+ return(-1);
+ }
+ PtyWRFD = PtyRDFD;
+
+ /* Check we haven't been installed setuid root for some reason
+ if so, don't create /dev/gnokii */
+ if (getuid() != geteuid()) {
+ fprintf(stderr, _("gnokiid should not be installed setuid root!\n"));
+ return (0);
+ }
+
+#ifdef DEBUG
+ fprintf (stderr, _("Slave pty is %s, calling %s to create /dev/gnokii.\n"), slave_name, mgnokiidev);
+#endif /* DEBUG */
+
+ /* Create command line, something line ./mkgnokiidev ttyp0 */
+ sprintf(cmdline, "%s %s", mgnokiidev, slave_name);
+
+ /* And use system to call it. */
+ err = system (cmdline);
+
+ return (err);
+
+}
+
+ /* Handler called when characters received from serial port.
+ calls state machine code to process it. */
+void VM_CharHandler(void)
+{
+ unsigned char buffer[255];
+ int res;
+
+
+ /* If we are in command mode, get the character, otherwise leave it */
+
+ if (CommandMode && ATEM_Initialised) {
+
+ res = read(PtyRDFD, buffer, 255);
+
+ /* A returned value of -1 means something serious has gone wrong - so quit!! */
+ /* Note that file closure etc. should have been dealt with in ThreadLoop */
+
+ if (res < 0) {
+// TerminateThread=true;
+ return;
+ }
+
+ ATEM_HandleIncomingData(buffer, res);
+ }
+}
+
+ /* Initialise GSM interface, returning GSM_Error as appropriate */
+GSM_Error VM_GSMInitialise(char *model, char *port, char *initlength, GSM_ConnectionType connection, char *synchronizetime)
+{
+ int count=0;
+ GSM_Error error;
+
+ /* Initialise the code for the GSM interface. */
+
+ error = GSM_Initialise(model,port, initlength, connection, RLP_DisplayF96Frame, synchronizetime);
+
+ if (error != GE_NONE) {
+ fprintf(stderr, _("GSM/FBUS init failed! (Unknown model ?). Quitting.\n"));
+ return (error);
+ }
+
+ /* First (and important!) wait for GSM link to be active. We allow 10
+ seconds... */
+
+ while (count++ < 200 && *GSM_LinkOK == false) {
+ usleep(50000);
+ }
+
+ if (*GSM_LinkOK == false) {
+ fprintf (stderr, _("Hmmm... GSM_LinkOK never went true. Quitting. \n"));
+ return (GE_NOLINK);
+ }
+
+ return (GE_NONE);
+}
+
+/* VM_GetMasterPty()
+ Takes a double-indirect character pointer in which to put a slave
+ name, and returns an integer file descriptor. If it returns < 0, an
+ error has occurred. Otherwise, it has returned the master pty
+ file descriptor, and fills in *name with the name of the
+ corresponding slave pty. Once the slave pty has been opened,
+ you are responsible to free *name. Code is from Developling Linux
+ Applications by Troan and Johnson */
+
+
+int VM_GetMasterPty(char **name) {
+
+#ifdef USE_UNIX98PTYS
+ int master, err;
+
+ master = open("/dev/ptmx", O_RDWR | O_NOCTTY | O_NONBLOCK);
+ if (master >= 0) {
+ err = grantpt(master);
+ err = err || unlockpt(master);
+ if (!err) {
+ *name = ptsname(master);
+ } else {
+ return(-1);
+ }
+ }
+#else /* USE_UNIX98PTYS */
+ int i = 0 , j = 0;
+ /* default to returning error */
+ int master = -1;
+
+ /* create a dummy name to fill in */
+ *name = strdup("/dev/ptyXX");
+
+ /* search for an unused pty */
+ for (i=0; i<16 && master <= 0; i++) {
+ for (j=0; j<16 && master <= 0; j++) {
+ (*name)[8] = "pqrstuvwxyzPQRST"[i];
+ (*name)[9] = "0123456789abcdef"[j];
+ /* open the master pty */
+ if ((master = open(*name, O_RDWR | O_NOCTTY | O_NONBLOCK )) < 0) {
+ if (errno == ENOENT) {
+ /* we are out of pty devices */
+ free (*name);
+ return (master);
+ }
+ }
+ }
+ }
+ if ((master < 0) && (i == 16) && (j == 16)) {
+ /* must have tried every pty unsuccessfully */
+ free (*name);
+ return (master);
+ }
+
+ /* By substituting a letter, we change the master pty
+ * name into the slave pty name.
+ */
+ (*name)[5] = 't';
+
+#endif /* USE_UNIX98PTYS */
+
+ return (master);
+}
+