This commit was manufactured by cvs2svn to create branch 'uc'.
[gnokii.git] / common / data / virtmodem.c
diff --git a/common/data/virtmodem.c b/common/data/virtmodem.c
new file mode 100644 (file)
index 0000000..7c7f4e2
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+  $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);
+}
+