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