regular status reporting disabled back again
[gnokii.git] / common / data / virtmodem.c
1 /*
2   $Id$
3
4   G N O K I I
5
6   A Linux/Unix toolset and driver for Nokia mobile phones.
7
8   Released under the terms of the GNU GPL, see file COPYING for more details.
9
10   This file provides a virtual modem interface to the GSM phone by calling
11   code in gsm-api.c, at-emulator.c and datapump.c. The code here provides
12   the overall framework and coordinates switching between command mode
13   (AT-emulator) and "online" mode where the data pump code translates data
14   from/to the GSM handset and the modem data/fax stream.
15
16 */
17
18 #define         __virtmodem_c
19
20 #include <config.h>
21
22 /* This is the right way to include stdlib with __USE_XOPEN defined */
23 #ifdef USE_UNIX98PTYS
24 # define _XOPEN_SOURCE 500
25 # include <features.h>
26 #endif
27
28 #include <stdio.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <signal.h>
32 #include <termios.h>
33 #include <grp.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 #ifndef UCLINUX
39 #include <sys/poll.h>
40 #include <pthread.h>
41 #endif /* UCLINUX */
42 #include <unistd.h>
43
44 #include "misc.h"
45 #include "gsm-api.h"
46 #include "gsm-common.h"
47 #include "data/at-emulator.h"
48 #include "data/datapump.h"
49 #include "data/virtmodem.h"
50 #include "data/rlp-common.h"
51
52         /* Global variables */
53
54
55 #define GNOKII_DEV "/var/gnokii-dev"
56
57 //extern bool TerminateThread;
58
59         /* Local variables */
60
61 int             PtyRDFD;        /* File descriptor for reading and writing to/from */
62 int             PtyWRFD;        /* pty interface - only different in debug mode. */ 
63
64 static bool     UseSTDIO;       /* Use STDIO for debugging purposes instead of pty */
65 bool    CommandMode;
66
67 static GSM_Error        VM_GSMInitialise(char *model, char *port, char *initlength, GSM_ConnectionType connection, char *synchronizetime);
68 static int              VM_PtySetup(char *bindir);
69
70         /* If initialised in debug mode, stdin/out is used instead
71            of ptys for interface. */
72 bool VM_Initialise(char *model,char *port, char *initlength, GSM_ConnectionType connection, char *bindir, bool debug_mode, bool GSMInit,char *synchronizetime)
73 {
74         CommandMode = true;
75
76         if (debug_mode == true) {
77                 UseSTDIO = true;
78         }
79         else {
80                 UseSTDIO = false;
81         }
82
83         if (GSMInit) {
84 #ifdef DEBUG
85           fprintf (stderr , "Initialising GSM\n");
86 #endif /* DEBUG */
87           if ((VM_GSMInitialise(model, port, initlength, connection, synchronizetime) != GE_NONE)) {
88                 fprintf (stderr, _("VM_Initialise - VM_GSMInitialise failed!\n"));
89                 return (false);
90                 
91           }
92         }
93         GSMInit=false;
94
95         if (VM_PtySetup(bindir) < 0) {
96                 fprintf (stderr, _("VM_Initialise - VM_PtySetup failed!\n"));
97                 return (false);
98         }
99     
100         if (ATEM_Initialise(PtyRDFD, PtyWRFD, model, port) != true) {
101                 fprintf (stderr, _("VM_Initialise - ATEM_Initialise failed!\n"));
102                 return (false);
103         }
104
105         if (DP_Initialise(PtyRDFD, PtyWRFD) != true) {
106                 fprintf (stderr, _("VM_Initialise - DP_Initialise failed!\n"));
107                 return (false);
108         }
109
110 #ifndef UCLINUX
111         /* Create and start thread, */
112         return VM_ThreadLoop();
113 #else
114         return true;
115 #endif
116 }
117
118 static void    VM_CharHandler(void);
119 extern GSM_Error N6110_SendStatusRequest(void);
120
121 bool VM_ThreadLoop(void)
122 {
123         int res;
124         fd_set readfds;
125         struct timeval timeout;
126
127                 /* Note we can't use signals here as they are already used
128                    in the FBUS code.  This may warrant changing the FBUS
129                    code around sometime to use select instead to free up
130                    the SIGIO handler for mainline code. */
131
132         for (;;) {
133           if (!CommandMode) {
134             sleep(1);
135           } else {  /* If we are in data mode, leave it to datapump to get the data */
136
137                 FD_ZERO(&readfds);
138                 FD_SET(PtyRDFD,&readfds);
139                 timeout.tv_sec=2;
140                 timeout.tv_usec=0;/*500*1000;*/
141
142                 res = select(PtyRDFD+1,&readfds,NULL/*writefds*/,NULL/*exceptfds*/,&timeout);
143
144                 switch (res) {
145                         case 0: /* Timeout */
146 #if 0
147                                 N6110_SendStatusRequest();
148 #endif
149                                 break;
150
151                         case -1:
152                                 if (errno==EINTR)
153                                         continue;
154                                 perror("VM_ThreadLoop - select");
155                                 return (false);
156
157                         default:
158                           if (FD_ISSET(PtyRDFD,&readfds)) {
159                             VM_CharHandler();
160                           } else usleep(500); /* Probably the file has been closed */
161                           break;
162                 }
163           }
164         }
165         
166 }
167
168         /* Application should call VM_Terminate to shut down
169            the virtual modem thread */
170 void            VM_Terminate(void)
171 {
172         if (!UseSTDIO) {
173                 close (PtyRDFD);
174                 close (PtyWRFD);
175         }
176 }
177
178 static int      VM_GetMasterPty(char **name);
179
180         /* Open pseudo tty interface and (in due course create a symlink
181            to be /dev/gnokii etc. ) */
182
183 static int              VM_PtySetup(char *bindir)
184 {
185         int                     err;
186 #ifndef UCLINUX
187         char            mgnokiidev[200];
188 #endif /* UCLINUX */
189         char            *slave_name;
190 #ifndef UCLINUX
191         char            cmdline[200];
192 #endif /* UCLINUX */
193
194 #ifndef UCLINUX
195         if (bindir) {
196                 strncpy(mgnokiidev, bindir, 200);
197                 strcat(mgnokiidev, "/");
198         }
199         strncat(mgnokiidev, "mgnokiidev", 200 - strlen(bindir));
200 #endif /* UCLINUX */
201
202         if (UseSTDIO) {
203                 PtyRDFD = STDIN_FILENO;
204                 PtyWRFD = STDOUT_FILENO;
205                 return (0);
206         }
207         
208         PtyRDFD = VM_GetMasterPty(&slave_name);
209         if (PtyRDFD < 0) {
210                 fprintf (stderr, _("Couldn't open pty!\n"));
211                 return(-1);
212         }
213         PtyWRFD = PtyRDFD;
214
215 #ifndef UCLINUX
216                 /* Check we haven't been installed setuid root for some reason
217                    if so, don't create /dev/gnokii */
218         if (getuid() != geteuid()) {
219                 fprintf(stderr, _("gnokiid should not be installed setuid root!\n"));
220                 return (0);
221         }
222 #endif
223
224 #ifdef DEBUG
225         fprintf (stderr, _("Slave pty is %s, calling %s to create \"%s\".\n"), slave_name,
226 #ifndef UCLINUX
227                         mgnokiidev
228 #else /* UCLINUX */
229                         "<uClinux>"
230 #endif /* UCLINUX */
231                         ,GNOKII_DEV);
232 #endif /* DEBUG */
233
234 #ifndef UCLINUX
235                 /* Create command line, something line ./mkgnokiidev ttyp0 */
236         sprintf(cmdline, "%s %s", mgnokiidev, slave_name);
237
238                 /* And use system to call it. */        
239         err = system (cmdline);
240 #else /* UCLINUX */
241
242         /* Remove symlink in case it already exists. Don't care if it fails.  */
243         unlink (GNOKII_DEV);
244
245         /* Create symlink */
246         err = symlink(slave_name, GNOKII_DEV);
247
248 #endif /* UCLINUX */
249         
250         return (err);
251
252 }
253
254     /* Handler called when characters received from serial port.
255        calls state machine code to process it. */
256 static void    VM_CharHandler(void)
257 {
258     unsigned char   buffer[255];
259     int             res;
260
261
262     /* If we are in command mode, get the character, otherwise leave it */
263
264     if (CommandMode && ATEM_Initialised) {
265       
266       res = read(PtyRDFD, buffer, 255);
267       
268       /* A returned value of -1 means something serious has gone wrong - so quit!! */
269       /* Note that file closure etc. should have been dealt with in ThreadLoop */
270       
271       if (res < 0) {    
272 //          TerminateThread=true;
273             return;
274       }
275         
276       ATEM_HandleIncomingData(buffer, res);
277     }   
278 }     
279
280         /* Initialise GSM interface, returning GSM_Error as appropriate  */
281 static GSM_Error        VM_GSMInitialise(char *model, char *port, char *initlength, GSM_ConnectionType connection, char *synchronizetime)
282 {
283         int             count=0;
284         GSM_Error       error;
285
286                 /* Initialise the code for the GSM interface. */     
287
288         error = GSM_Initialise(model,port, initlength, connection, RLP_DisplayF96Frame, synchronizetime);
289
290         if (error != GE_NONE) {
291                 fprintf(stderr, _("GSM/FBUS init failed! (Unknown model ?). Quitting.\n"));
292         return (error);
293         }
294
295                 /* First (and important!) wait for GSM link to be active. We allow 10
296                    seconds... */
297
298         while (count++ < 200 && *GSM_LinkOK == false) {
299         usleep(50000);
300         }
301
302         if (*GSM_LinkOK == false) {
303                 fprintf (stderr, _("Hmmm... GSM_LinkOK never went true. Quitting. \n"));
304                 return (GE_NOLINK); 
305         }
306
307         return (GE_NONE);
308 }
309
310 /* VM_GetMasterPty()
311    Takes a double-indirect character pointer in which to put a slave
312    name, and returns an integer file descriptor.  If it returns < 0, an
313    error has occurred.  Otherwise, it has returned the master pty
314    file descriptor, and fills in *name with the name of the
315    corresponding slave pty.  Once the slave pty has been opened,
316    you are responsible to free *name.  Code is from Developling Linux
317    Applications by Troan and Johnson */
318
319
320 static int      VM_GetMasterPty(char **name) { 
321
322 #ifdef USE_UNIX98PTYS
323         int master, err;
324
325         master = open("/dev/ptmx", O_RDWR | O_NOCTTY | O_NONBLOCK);
326         if (master >= 0) {
327                 err = grantpt(master);
328                 err = err || unlockpt(master);
329                 if (!err) {
330                         *name = ptsname(master);
331                 } else {
332                         return(-1);
333                 }
334         }
335 #else /* USE_UNIX98PTYS */
336    int i = 0 , j = 0;
337    /* default to returning error */
338    int master = -1;
339
340    /* create a dummy name to fill in */
341    *name = strdup("/dev/ptyXX");
342
343    /* search for an unused pty */
344    for (i=0; i<16 && master <= 0; i++) {
345       for (j=0; j<16 && master <= 0; j++) {
346          (*name)[8] = "pqrstuvwxyzPQRST"[i];
347          (*name)[9] = "0123456789abcdef"[j];
348          /* open the master pty */
349          if ((master = open(*name, O_RDWR | O_NOCTTY | O_NONBLOCK )) < 0) {
350             if (errno == ENOENT) {
351                /* we are out of pty devices */
352                free (*name);
353                return (master);
354             }
355          }
356       }
357    }
358    if ((master < 0) && (i == 16) && (j == 16)) {
359       /* must have tried every pty unsuccessfully */
360       free (*name);
361       return (master);
362    }
363
364    /* By substituting a letter, we change the master pty
365     * name into the slave pty name.
366     */
367    (*name)[5] = 't';
368
369 #endif /* USE_UNIX98PTYS */
370
371    return (master);
372 }
373