Another SIGIO-removal fixe
[gnokii.git] / common / devices / device.c
1 /*
2
3   G N O K I I
4
5   A Linux/Unix toolset and driver for Nokia mobile phones.
6
7   Released under the terms of the GNU GPL, see file COPYING for more details.
8
9 */
10
11 #include "config.h"
12
13 #ifndef WIN32
14   #include "devices/unixserial.h"
15   #include <sys/ioctl.h>
16   #include <termios.h>
17   #include <signal.h>
18   #include <errno.h>
19 #else
20   #include <windows.h>
21   #include "devices/winserial.h"
22 #endif
23
24 #include "gsm-api.h"
25 #include "devices/device.h"
26
27 #include <string.h>
28
29 #ifdef VC6
30   /* for VC6 make scripts save VERSION constant in mversion.h file */
31   #include "mversion.h"
32 #endif
33
34 static char PortDevice[GSM_MAX_DEVICE_NAME_LENGTH]={0x00};
35
36 static bool duringwrite;
37
38 #ifndef WIN32
39
40 //pthread_t Thread;
41 #if defined(__svr4__) || defined(__FreeBSD__)
42   pthread_t selThread;
43 #endif
44
45 int device_portfd = -1;
46
47 /*
48  * Structure to store the filedescriptor we use.
49  *
50  */
51 #ifdef DEBUG
52 static int device_getfd(void)
53 {
54   return device_portfd;
55 }
56 #endif /* DEBUG */
57
58 static int device_open(__const char *__file, int __with_odd_parity) {
59
60   switch (CurrentConnectionType) {
61     default:
62       device_portfd = serial_opendevice(__file, __with_odd_parity, true, false);
63       break;
64   }
65   
66   return (device_portfd >= 0);
67 }
68
69 void device_close(void)
70 {
71   AppendLogText("CLOSE\n",false);
72
73   /* Now wait for thread to terminate. */
74   //pthread_join(Thread, NULL);
75
76   switch (CurrentConnectionType) {
77     default        : serial_close(device_portfd); break;
78   }
79
80   PortDevice[0]=0x00;
81 }
82
83 #ifndef UCLINUX
84
85 void device_reset(void) {
86 }
87
88 #endif /* UCLINUX */
89
90 #ifdef DEBUG
91 static void device_dumpserial(void);
92 #endif /* DEBUG */
93
94 void device_setdtrrts(int __dtr, int __rts)
95 {
96   switch (CurrentConnectionType) {
97     default:
98       serial_setdtrrts(device_portfd, __dtr, __rts);
99 #ifdef DEBUG
100       device_dumpserial();
101 #endif
102       break;
103   }
104 }
105
106 void device_changespeed(int __speed)
107 {
108   switch (CurrentConnectionType) {
109     default:
110       serial_changespeed(device_portfd, __speed);
111 #ifdef DEBUG
112       fprintf(stdout,_("Serial device: changing speed to %i\n"),__speed);
113 #endif
114       break;
115   }
116 }
117
118 static size_t device_read(__ptr_t __buf, size_t __nbytes)
119 {
120   switch (CurrentConnectionType) {
121     default        : return (serial_read(device_portfd, __buf, __nbytes)); break;
122   }
123 }
124
125 size_t device_write(__const __ptr_t __buf, size_t __n) {
126   u8 buffer[300];
127   size_t mysize;
128   
129   while (duringwrite) {
130     fprintf(stderr,"device_write: reentrance violation!\n");
131     _exit(1);
132   }
133
134   memcpy(buffer,__buf,__n);
135   AppendLog(buffer,__n,true);
136
137   duringwrite=true;
138   switch (CurrentConnectionType) {
139     default        : mysize = serial_write(device_portfd, __buf, __n); break;
140   }
141   duringwrite=false;
142   if (mysize!=__n)
143     fprintf(stderr,"WARNING: device_write(__n=%ld)=%ld\n",(long)__n,(long)mysize);
144   return mysize;
145 }
146
147 #ifdef DEBUG
148 static void device_dumpserial(void)
149 {
150   int PortFD;
151   unsigned int Flags=0;
152
153   PortFD = device_getfd();
154
155   ioctl(PortFD, TIOCMGET, &Flags);
156
157   fprintf(stdout, _("Serial device:"));
158   fprintf(stdout,  _(" DTR is %s"), Flags&TIOCM_DTR?_("up"):_("down"));
159   fprintf(stdout, _(", RTS is %s"), Flags&TIOCM_RTS?_("up"):_("down"));
160   fprintf(stdout, _(", CAR is %s"), Flags&TIOCM_CAR?_("up"):_("down"));
161   fprintf(stdout, _(", CTS is %s\n"), Flags&TIOCM_CTS?_("up"):_("down"));
162 }
163 #endif /* DEBUG */
164
165 static char SigHandler_buffer[255];
166
167 void SigHandler(int status)
168 {
169   int count, res;
170
171   LIVE_DISABLE;
172   LIVE;
173
174   res = device_read(SigHandler_buffer, sizeof(SigHandler_buffer));
175
176   for (count = 0; count < res ; count ++) {
177     Protocol->StateMachine(SigHandler_buffer[count]);
178   }
179   LIVE_ENABLE;
180 }
181
182 #ifdef UCLINUX
183 void usleep_watchdevice(unsigned long usecs)
184 {
185 int err;
186 fd_set readfds;
187 struct timeval target,timeout;
188 long timeout_sec,timeout_usec; /* signed! */
189
190   if (gettimeofday(&target/*tv*/,NULL/*tz*/))
191     perror("usleep_watchdevice()/gettimeofday(2) init");
192   target.tv_usec+=usecs%1000000;
193   target.tv_sec +=usecs/1000000 + (target.tv_usec/1000000);
194   target.tv_usec%=1000000;
195
196   for (;;) {
197     if (gettimeofday(&timeout/*tv*/,NULL/*tz*/))
198       perror("usleep_watchdevice()/gettimeofday(2) loop");
199
200     timeout_sec =target.tv_sec -(long)timeout.tv_sec ;
201     timeout_usec=target.tv_usec-(long)timeout.tv_usec;
202
203     if (timeout_usec<0) {
204       timeout_sec--;
205       timeout_usec+=1000000;
206     }
207     if (timeout_sec<0)
208       return;
209
210     timeout.tv_sec =timeout_sec ;
211     timeout.tv_usec=timeout_usec;
212
213     FD_ZERO(&readfds);
214     if (device_portfd>=0)
215       FD_SET(device_portfd,&readfds);
216
217     err=select((device_portfd<0 ? 0 : device_portfd+1),&readfds,NULL,NULL,&timeout);
218     if ( err > 0 ) {
219       if (device_portfd>=0 && FD_ISSET(device_portfd,&readfds)) {
220         /* call singal handler to process incoming data */
221         SigHandler(0);
222       }
223     } else {
224       if (err == -1)
225       perror("Error in SelectLoop");
226     }
227   }
228 }
229 #endif
230
231 #if defined(__svr4__) || defined(__FreeBSD__)
232 /* thread for handling incoming data */
233 void SelectLoop() {
234   int err;
235   fd_set readfds;
236   struct timeval timeout;
237
238   FD_ZERO(&readfds);
239   FD_SET(device_portfd,&readfds);
240   /* set timeout to 15 seconds */
241   timeout.tv_sec=15;
242   timeout.tv_usec=0;
243   while (!CurrentRequestTerminate) {
244     err=select(device_portfd+1,&readfds,NULL,NULL,&timeout);
245     if ( err > 0 ) {
246       /* call singal handler to process incoming data */
247       SigHandler(0);
248       /* refresh timeout, just for linux :-( */
249       /* required for irda */
250       timeout.tv_sec=15;
251     } else {
252       if (err == -1)
253       perror("Error in SelectLoop");
254     }
255   }
256 }
257 #endif
258
259 bool StartConnection (char *port_device, bool with_odd_parity, GSM_ConnectionType con)
260 {
261   int result;
262
263   char text[100];
264
265 #ifndef UCLINUX
266 #if defined(__svr4__) || defined(__FreeBSD__)
267   int rtn;
268 #else
269   struct sigaction sig_io;
270 #endif
271 #endif /* UCLINUX */
272
273 #ifndef UCLINUX
274 #ifdef DEBUG
275       if ((strstr(GSM_Info->IrdaModels,"decode")!=NULL) &&  (CurrentConnectionType == GCT_Irda))
276        {
277          printf("DEBUG and Irda decode Model -> not realy open ;-)\n");
278          return true;
279        }
280 #endif 
281 #endif /* UCLINUX */
282
283   if (PortDevice[0]!=0x00) return true;
284
285   duringwrite=false;
286   
287   strncpy(PortDevice, port_device, GSM_MAX_DEVICE_NAME_LENGTH);
288
289 #ifdef DEBUG
290   fprintf(stdout,_("Opening \"%s\" device...\n"),PortDevice);
291 #endif
292
293   strcpy(text,"\n\n\nMygnokii ");
294   sprintf(text+strlen(text), "%s",VERSION);
295   strcpy(text+strlen(text),"\n");
296   AppendLogText(text,false);
297
298   strcpy(text,"Port ");
299   strcpy(text+strlen(text),PortDevice);
300   strcpy(text+strlen(text),"\n");
301   AppendLogText(text,false);
302
303   strcpy(text,"Connection ");
304   switch (con) {
305     case GCT_FBUS    :strcpy(text+strlen(text),"FBUS");break;
306 #ifndef UCLINUX
307     case GCT_MBUS    :strcpy(text+strlen(text),"MBUS");break;
308     case GCT_DLR3    :strcpy(text+strlen(text),"DLR3");break;
309     case GCT_AT      :strcpy(text+strlen(text),"AT");break;
310     default          :strcpy(text+strlen(text),"unknown");break;
311 #endif /* UCLINUX */
312   }
313   strcpy(text+strlen(text),"\n");
314   AppendLogText(text,false);
315
316   /* Ralf Thelen: In this moment there is NO method of communication,
317    which require keepalive packets and GSM->KeepAlive is
318    always NULL_KeepAlive, I comment this thread, */
319
320   /* Create and start main thread. */
321 //  rtn = pthread_create(&Thread, NULL,(void*)GSM->KeepAlive, (void *)NULL);
322 //
323 //  if (rtn != 0) {
324 //    fprintf(stdout,_("Error\n"));  
325 //    return false;
326 //  }
327
328 #ifndef UCLINUX
329 #if defined(__svr4__) || defined(__FreeBSD__)
330 #else
331         /* Set up and install handler before enabling async IO on port. */
332         sig_io.sa_handler = SigHandler;
333         sig_io.sa_flags = 0;
334         sigaction (SIGIO, &sig_io, NULL);
335 #endif
336 #endif /* UCLINUX */
337
338     /* Open device. */
339     result = device_open(PortDevice, with_odd_parity);
340     
341     if (!result) {
342       fprintf(stdout,_("Error\n"));  
343       return false;
344     }
345
346 #ifndef UCLINUX
347 #if defined(__svr4__) || defined(__FreeBSD__)
348     /* create a thread to handle incoming data from mobile phone */
349     rtn=pthread_create(&selThread,NULL,(void*)SelectLoop,(void*)NULL);
350     if (rtn != 0) {
351       fprintf(stdout,_("Error\n"));  
352       return false;
353     }
354 #endif
355 #endif /* UCLINUX */
356
357   return true;
358 }
359
360 /* ---------------- RTH:   #ifdef WIN32 ------------------ */  
361
362 #else
363
364   extern HANDLE hPhone;
365
366 void device_close(void)
367 {
368   AppendLogText("CLOSE\n",false);
369
370   CloseConnection();
371
372   PortDevice[0]=0x00;
373 }
374
375 #ifdef DEBUG
376 void device_dumpserial(void)
377 {
378   DCB dcb;
379   
380   dcb.DCBlength = sizeof(DCB);
381   GetCommState(hPhone, &dcb);
382
383   fprintf(stdout, _("Serial device:"));
384   fprintf(stdout,  _(" DTR is "));
385   switch (dcb.fDtrControl) {
386     case DTR_CONTROL_ENABLE   : fprintf(stdout,  _("up"));       break;
387     case DTR_CONTROL_DISABLE  : fprintf(stdout,  _("down"));     break;
388     case DTR_CONTROL_HANDSHAKE: fprintf(stdout,  _("handshake"));break;
389   }
390   fprintf(stdout, _(", RTS is "));
391   switch (dcb.fRtsControl) {
392     case RTS_CONTROL_ENABLE   : fprintf(stdout,  _("up\n"));       break;
393     case RTS_CONTROL_DISABLE  : fprintf(stdout,  _("down\n"));     break;
394     case RTS_CONTROL_HANDSHAKE: fprintf(stdout,  _("handshake\n"));break;
395     case RTS_CONTROL_TOGGLE   : fprintf(stdout,  _("toggle\n"));   break;
396   }
397 }
398 #endif /* DEBUG */
399
400 void device_setdtrrts(int __dtr, int __rts)
401 {
402   DCB dcb;
403   
404   dcb.DCBlength = sizeof(DCB);
405   GetCommState(hPhone, &dcb);
406
407   if (__dtr==1) dcb.fDtrControl = DTR_CONTROL_ENABLE;
408            else dcb.fDtrControl = DTR_CONTROL_DISABLE;
409
410    if (__rts==1) dcb.fRtsControl = RTS_CONTROL_ENABLE;
411             else dcb.fRtsControl = RTS_CONTROL_DISABLE;
412
413    SetCommState(hPhone, &dcb);
414
415 #ifdef DEBUG
416   device_dumpserial();
417 #endif
418 }
419
420 void device_changespeed(int __speed)
421 {
422
423   DCB dcb;
424
425   dcb.DCBlength = sizeof(DCB);
426   GetCommState(hPhone, &dcb);
427
428   switch (__speed) {
429     case 115200: dcb.BaudRate=CBR_115200; break;
430     case 19200 : dcb.BaudRate=CBR_19200;  break;
431     case 9600  : dcb.BaudRate=CBR_9600;   break;
432    }
433    
434    SetCommState(hPhone, &dcb);
435
436 #ifdef DEBUG
437    fprintf(stdout,_("Serial device: changing speed to %i\n"),__speed);
438 #endif
439 }
440
441 bool StartConnection (char *port_device, bool with_odd_parity, GSM_ConnectionType con)
442 {
443   DCB dcb;
444   char text[100];
445
446   int rtn;
447
448   if (PortDevice[0]!=0x00) return true;  
449
450   duringwrite=false;
451   strncpy(PortDevice, port_device, GSM_MAX_DEVICE_NAME_LENGTH);
452
453 #ifdef DEBUG
454   fprintf(stdout,_("Opening \"%s\" device...\n"),PortDevice);
455 #endif
456
457   strcpy(text,"\n\n\nMygnokii ");
458   sprintf(text+strlen(text), "%s",VERSION);
459   strcpy(text+strlen(text),"\n");
460   AppendLogText(text,false);
461
462   strcpy(text,"Port ");
463   strcpy(text+strlen(text),PortDevice);
464   strcpy(text+strlen(text),"\n");
465   AppendLogText(text,false);
466
467   strcpy(text,"Connection ");
468   switch (con) {
469     case GCT_FBUS    :strcpy(text+strlen(text),"FBUS");break;
470     case GCT_MBUS    :strcpy(text+strlen(text),"MBUS");break;
471     case GCT_DLR3    :strcpy(text+strlen(text),"DLR3");break;
472     case GCT_AT      :strcpy(text+strlen(text),"AT");break;
473     default          :strcpy(text+strlen(text),"unknown");break;
474   }
475   strcpy(text+strlen(text),"\n");
476   AppendLogText(text,false);
477
478   CurrentDisableKeepAlive = true;
479
480   /* Create and start main thread. */
481   rtn = ! OpenConnection(PortDevice, Protocol->StateMachine, GSM->KeepAlive);
482
483   if (rtn != 0) {
484
485     fprintf(stdout,_("Error\n"));  
486     return false;
487   } else {
488     CurrentDisableKeepAlive = false;
489   }
490
491   if (with_odd_parity) {
492     dcb.DCBlength = sizeof(DCB);
493     GetCommState(hPhone, &dcb);
494
495     dcb.Parity=ODDPARITY;
496
497     SetCommState(hPhone, &dcb);
498   }
499   
500   return true;
501 }
502
503 size_t device_write(const __ptr_t __buf, size_t __n) {
504   int i;
505   while (duringwrite) {}
506   duringwrite=true;
507   AppendLog(__buf,__n,true);
508   i=WriteCommBlock(__buf,__n);
509   duringwrite=false;
510   if (i) return __n; else return 0;
511 }
512
513 #endif /*WIN32*/