This commit was manufactured by cvs2svn to create tag
[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 #include <sys/poll.h>
39 #include <pthread.h>
40 #include <unistd.h>
41
42 #include "misc.h"
43 #include "gsm-api.h"
44 #include "gsm-common.h"
45 #include "data/at-emulator.h"
46 #include "data/datapump.h"
47 #include "data/virtmodem.h"
48 #include "data/rlp-common.h"
49
50         /* Global variables */
51
52 //extern bool TerminateThread;
53 int ConnectCount;
54
55         /* Local variables */
56
57 int             PtyRDFD;        /* File descriptor for reading and writing to/from */
58 int             PtyWRFD;        /* pty interface - only different in debug mode. */ 
59
60 bool    UseSTDIO;       /* Use STDIO for debugging purposes instead of pty */
61 bool    CommandMode;
62
63 pthread_t               Thread;
64 bool                    RequestTerminate;
65
66
67         /* If initialised in debug mode, stdin/out is used instead
68            of ptys for interface. */
69 bool    VM_Initialise(char *model,char *port, char *initlength, GSM_ConnectionType connection, char *bindir, bool debug_mode, bool GSMInit,char *synchronizetime)
70 {
71         int             rtn;
72
73         CommandMode = true;
74
75         RequestTerminate = false;
76
77         if (debug_mode == true) {
78                 UseSTDIO = true;
79         }
80         else {
81                 UseSTDIO = false;
82         }
83
84         if (GSMInit) {
85 #ifdef DEBUG
86           fprintf (stderr , "Initialising GSM\n");
87 #endif /* DEBUG */
88           if ((VM_GSMInitialise(model, port, initlength, connection, synchronizetime) != GE_NONE)) {
89                 fprintf (stderr, _("VM_Initialise - VM_GSMInitialise failed!\n"));
90                 return (false);
91                 
92           }
93         }
94         GSMInit=false;
95
96         if (VM_PtySetup(bindir) < 0) {
97                 fprintf (stderr, _("VM_Initialise - VM_PtySetup failed!\n"));
98                 return (false);
99         }
100     
101         if (ATEM_Initialise(PtyRDFD, PtyWRFD, model, port) != true) {
102                 fprintf (stderr, _("VM_Initialise - ATEM_Initialise failed!\n"));
103                 return (false);
104         }
105
106         if (DP_Initialise(PtyRDFD, PtyWRFD) != true) {
107                 fprintf (stderr, _("VM_Initialise - DP_Initialise failed!\n"));
108                 return (false);
109         }
110
111                 /* Create and start thread, */
112         rtn = pthread_create(&Thread, NULL, (void *) VM_ThreadLoop, (void *)NULL);
113
114     if (rtn == EAGAIN || rtn == EINVAL) {
115         return (false);
116     }
117         return (true);
118 }
119
120 void    VM_ThreadLoop(void)
121 {
122         int res;
123         struct pollfd ufds;
124
125                 /* Note we can't use signals here as they are already used
126                    in the FBUS code.  This may warrant changing the FBUS
127                    code around sometime to use select instead to free up
128                    the SIGIO handler for mainline code. */
129
130         ufds.fd=PtyRDFD;
131         ufds.events=POLLIN;
132
133         while (!RequestTerminate) {
134           if (!CommandMode) {
135             sleep(1);
136           } else {  /* If we are in data mode, leave it to datapump to get the data */
137
138                 res=poll(&ufds,1,500);
139
140                 switch (res) {
141                         case 0: /* Timeout */
142                                 break;
143
144                         case -1:
145                                 perror("VM_ThreadLoop - select");
146                                 exit (-1);
147
148                         default:
149                           if (ufds.revents==POLLIN) {
150                             VM_CharHandler();
151                           } else usleep(500); /* Probably the file has been closed */
152                           break;
153                 }
154           }
155         }
156         
157 }
158
159         /* Application should call VM_Terminate to shut down
160            the virtual modem thread */
161 void            VM_Terminate(void)
162 {
163      
164         /* Request termination of thread */
165         RequestTerminate = true;
166
167         /* Now wait for thread to terminate. */
168         pthread_join(Thread, NULL);
169
170         if (!UseSTDIO) {
171                 close (PtyRDFD);
172                 close (PtyWRFD);
173         }
174 }
175
176         /* Open pseudo tty interface and (in due course create a symlink
177            to be /dev/gnokii etc. ) */
178
179 int             VM_PtySetup(char *bindir)
180 {
181         int                     err;
182         char            mgnokiidev[200];
183         char            *slave_name;
184         char            cmdline[200];
185
186         if (bindir) {
187                 strncpy(mgnokiidev, bindir, 200);
188                 strcat(mgnokiidev, "/");
189         }
190         strncat(mgnokiidev, "mgnokiidev", 200 - strlen(bindir));
191
192         if (UseSTDIO) {
193                 PtyRDFD = STDIN_FILENO;
194                 PtyWRFD = STDOUT_FILENO;
195                 return (0);
196         }
197         
198         PtyRDFD = VM_GetMasterPty(&slave_name);
199         if (PtyRDFD < 0) {
200                 fprintf (stderr, _("Couldn't open pty!\n"));
201                 return(-1);
202         }
203         PtyWRFD = PtyRDFD;
204
205                 /* Check we haven't been installed setuid root for some reason
206                    if so, don't create /dev/gnokii */
207         if (getuid() != geteuid()) {
208                 fprintf(stderr, _("gnokiid should not be installed setuid root!\n"));
209                 return (0);
210         }
211
212 #ifdef DEBUG
213         fprintf (stderr, _("Slave pty is %s, calling %s to create /dev/gnokii.\n"), slave_name, mgnokiidev);
214 #endif /* DEBUG */
215
216                 /* Create command line, something line ./mkgnokiidev ttyp0 */
217         sprintf(cmdline, "%s %s", mgnokiidev, slave_name);
218
219                 /* And use system to call it. */        
220         err = system (cmdline);
221         
222         return (err);
223
224 }
225
226     /* Handler called when characters received from serial port.
227        calls state machine code to process it. */
228 void    VM_CharHandler(void)
229 {
230     unsigned char   buffer[255];
231     int             res;
232
233
234     /* If we are in command mode, get the character, otherwise leave it */
235
236     if (CommandMode && ATEM_Initialised) {
237       
238       res = read(PtyRDFD, buffer, 255);
239       
240       /* A returned value of -1 means something serious has gone wrong - so quit!! */
241       /* Note that file closure etc. should have been dealt with in ThreadLoop */
242       
243       if (res < 0) {    
244 //          TerminateThread=true;
245             return;
246       }
247         
248       ATEM_HandleIncomingData(buffer, res);
249     }   
250 }     
251
252         /* Initialise GSM interface, returning GSM_Error as appropriate  */
253 GSM_Error       VM_GSMInitialise(char *model, char *port, char *initlength, GSM_ConnectionType connection, char *synchronizetime)
254 {
255         int             count=0;
256         GSM_Error       error;
257
258                 /* Initialise the code for the GSM interface. */     
259
260         error = GSM_Initialise(model,port, initlength, connection, RLP_DisplayF96Frame, synchronizetime);
261
262         if (error != GE_NONE) {
263                 fprintf(stderr, _("GSM/FBUS init failed! (Unknown model ?). Quitting.\n"));
264         return (error);
265         }
266
267                 /* First (and important!) wait for GSM link to be active. We allow 10
268                    seconds... */
269
270         while (count++ < 200 && *GSM_LinkOK == false) {
271         usleep(50000);
272         }
273
274         if (*GSM_LinkOK == false) {
275                 fprintf (stderr, _("Hmmm... GSM_LinkOK never went true. Quitting. \n"));
276                 return (GE_NOLINK); 
277         }
278
279         return (GE_NONE);
280 }
281
282 /* VM_GetMasterPty()
283    Takes a double-indirect character pointer in which to put a slave
284    name, and returns an integer file descriptor.  If it returns < 0, an
285    error has occurred.  Otherwise, it has returned the master pty
286    file descriptor, and fills in *name with the name of the
287    corresponding slave pty.  Once the slave pty has been opened,
288    you are responsible to free *name.  Code is from Developling Linux
289    Applications by Troan and Johnson */
290
291
292 int     VM_GetMasterPty(char **name) { 
293
294 #ifdef USE_UNIX98PTYS
295         int master, err;
296
297         master = open("/dev/ptmx", O_RDWR | O_NOCTTY | O_NONBLOCK);
298         if (master >= 0) {
299                 err = grantpt(master);
300                 err = err || unlockpt(master);
301                 if (!err) {
302                         *name = ptsname(master);
303                 } else {
304                         return(-1);
305                 }
306         }
307 #else /* USE_UNIX98PTYS */
308    int i = 0 , j = 0;
309    /* default to returning error */
310    int master = -1;
311
312    /* create a dummy name to fill in */
313    *name = strdup("/dev/ptyXX");
314
315    /* search for an unused pty */
316    for (i=0; i<16 && master <= 0; i++) {
317       for (j=0; j<16 && master <= 0; j++) {
318          (*name)[8] = "pqrstuvwxyzPQRST"[i];
319          (*name)[9] = "0123456789abcdef"[j];
320          /* open the master pty */
321          if ((master = open(*name, O_RDWR | O_NOCTTY | O_NONBLOCK )) < 0) {
322             if (errno == ENOENT) {
323                /* we are out of pty devices */
324                free (*name);
325                return (master);
326             }
327          }
328       }
329    }
330    if ((master < 0) && (i == 16) && (j == 16)) {
331       /* must have tried every pty unsuccessfully */
332       free (*name);
333       return (master);
334    }
335
336    /* By substituting a letter, we change the master pty
337     * name into the slave pty name.
338     */
339    (*name)[5] = 't';
340
341 #endif /* USE_UNIX98PTYS */
342
343    return (master);
344 }
345