Some SIGIO-removal fixes
[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
189   if (gettimeofday(&target/*tv*/,NULL/*tz*/))
190     perror("usleep_watchdevice()/gettimeofday(2) init");
191   target.tv_usec+=usecs%1000000;
192   target.tv_sec +=usecs/1000000 + (target.tv_usec/1000000);
193   target.tv_usec%=1000000;
194
195   target.tv_usec+=1000000;
196   target.tv_sec --;
197
198   for (;;)
199     FD_ZERO(&readfds);
200     if (device_portfd>=0)
201       FD_SET(device_portfd,&readfds);
202
203     if (gettimeofday(&timeout/*tv*/,NULL/*tz*/))
204       perror("usleep_watchdevice()/gettimeofday(2) loop");
205     if (target.tv_sec  < timeout.tv_sec)
206       return;
207     timeout.tv_sec =target.tv_sec - timeout.tv_sec;
208     timeout.tv_usec=target.tv_usec - timeout.tv_usec;
209     if (timeout.tv_usec>=1000000) {
210       timeout.tv_usec-=1000000;
211       if (!timeout.tv_sec)
212         return;
213       timeout.tv_sec--;
214       }
215
216     err=select((device_portfd<0 ? 0 : device_portfd+1),&readfds,NULL,NULL,&timeout);
217     if ( err > 0 ) {
218       if (device_portfd>=0 && FD_ISSET(device_portfd,&readfds)) {
219         /* call singal handler to process incoming data */
220         SigHandler(0);
221       }
222     } else {
223       if (err == -1)
224       perror("Error in SelectLoop");
225     }
226 }
227 #endif
228
229 #if defined(__svr4__) || defined(__FreeBSD__)
230 /* thread for handling incoming data */
231 void SelectLoop() {
232   int err;
233   fd_set readfds;
234   struct timeval timeout;
235
236   FD_ZERO(&readfds);
237   FD_SET(device_portfd,&readfds);
238   /* set timeout to 15 seconds */
239   timeout.tv_sec=15;
240   timeout.tv_usec=0;
241   while (!CurrentRequestTerminate) {
242     err=select(device_portfd+1,&readfds,NULL,NULL,&timeout);
243     if ( err > 0 ) {
244       /* call singal handler to process incoming data */
245       SigHandler(0);
246       /* refresh timeout, just for linux :-( */
247       /* required for irda */
248       timeout.tv_sec=15;
249     } else {
250       if (err == -1)
251       perror("Error in SelectLoop");
252     }
253   }
254 }
255 #endif
256
257 bool StartConnection (char *port_device, bool with_odd_parity, GSM_ConnectionType con)
258 {
259   int result;
260
261   char text[100];
262
263 #ifndef UCLINUX
264 #if defined(__svr4__) || defined(__FreeBSD__)
265   int rtn;
266 #else
267   struct sigaction sig_io;
268 #endif
269 #endif /* UCLINUX */
270
271 #ifndef UCLINUX
272 #ifdef DEBUG
273       if ((strstr(GSM_Info->IrdaModels,"decode")!=NULL) &&  (CurrentConnectionType == GCT_Irda))
274        {
275          printf("DEBUG and Irda decode Model -> not realy open ;-)\n");
276          return true;
277        }
278 #endif 
279 #endif /* UCLINUX */
280
281   if (PortDevice[0]!=0x00) return true;
282
283   duringwrite=false;
284   
285   strncpy(PortDevice, port_device, GSM_MAX_DEVICE_NAME_LENGTH);
286
287 #ifdef DEBUG
288   fprintf(stdout,_("Opening \"%s\" device...\n"),PortDevice);
289 #endif
290
291   strcpy(text,"\n\n\nMygnokii ");
292   sprintf(text+strlen(text), "%s",VERSION);
293   strcpy(text+strlen(text),"\n");
294   AppendLogText(text,false);
295
296   strcpy(text,"Port ");
297   strcpy(text+strlen(text),PortDevice);
298   strcpy(text+strlen(text),"\n");
299   AppendLogText(text,false);
300
301   strcpy(text,"Connection ");
302   switch (con) {
303     case GCT_FBUS    :strcpy(text+strlen(text),"FBUS");break;
304 #ifndef UCLINUX
305     case GCT_MBUS    :strcpy(text+strlen(text),"MBUS");break;
306     case GCT_DLR3    :strcpy(text+strlen(text),"DLR3");break;
307     case GCT_AT      :strcpy(text+strlen(text),"AT");break;
308     default          :strcpy(text+strlen(text),"unknown");break;
309 #endif /* UCLINUX */
310   }
311   strcpy(text+strlen(text),"\n");
312   AppendLogText(text,false);
313
314   /* Ralf Thelen: In this moment there is NO method of communication,
315    which require keepalive packets and GSM->KeepAlive is
316    always NULL_KeepAlive, I comment this thread, */
317
318   /* Create and start main thread. */
319 //  rtn = pthread_create(&Thread, NULL,(void*)GSM->KeepAlive, (void *)NULL);
320 //
321 //  if (rtn != 0) {
322 //    fprintf(stdout,_("Error\n"));  
323 //    return false;
324 //  }
325
326 #ifndef UCLINUX
327 #if defined(__svr4__) || defined(__FreeBSD__)
328 #else
329         /* Set up and install handler before enabling async IO on port. */
330         sig_io.sa_handler = SigHandler;
331         sig_io.sa_flags = 0;
332         sigaction (SIGIO, &sig_io, NULL);
333 #endif
334 #endif /* UCLINUX */
335
336     /* Open device. */
337     result = device_open(PortDevice, with_odd_parity);
338     
339     if (!result) {
340       fprintf(stdout,_("Error\n"));  
341       return false;
342     }
343
344 #ifndef UCLINUX
345 #if defined(__svr4__) || defined(__FreeBSD__)
346     /* create a thread to handle incoming data from mobile phone */
347     rtn=pthread_create(&selThread,NULL,(void*)SelectLoop,(void*)NULL);
348     if (rtn != 0) {
349       fprintf(stdout,_("Error\n"));  
350       return false;
351     }
352 #endif
353 #endif /* UCLINUX */
354
355   return true;
356 }
357
358 /* ---------------- RTH:   #ifdef WIN32 ------------------ */  
359
360 #else
361
362   extern HANDLE hPhone;
363
364 void device_close(void)
365 {
366   AppendLogText("CLOSE\n",false);
367
368   CloseConnection();
369
370   PortDevice[0]=0x00;
371 }
372
373 #ifdef DEBUG
374 void device_dumpserial(void)
375 {
376   DCB dcb;
377   
378   dcb.DCBlength = sizeof(DCB);
379   GetCommState(hPhone, &dcb);
380
381   fprintf(stdout, _("Serial device:"));
382   fprintf(stdout,  _(" DTR is "));
383   switch (dcb.fDtrControl) {
384     case DTR_CONTROL_ENABLE   : fprintf(stdout,  _("up"));       break;
385     case DTR_CONTROL_DISABLE  : fprintf(stdout,  _("down"));     break;
386     case DTR_CONTROL_HANDSHAKE: fprintf(stdout,  _("handshake"));break;
387   }
388   fprintf(stdout, _(", RTS is "));
389   switch (dcb.fRtsControl) {
390     case RTS_CONTROL_ENABLE   : fprintf(stdout,  _("up\n"));       break;
391     case RTS_CONTROL_DISABLE  : fprintf(stdout,  _("down\n"));     break;
392     case RTS_CONTROL_HANDSHAKE: fprintf(stdout,  _("handshake\n"));break;
393     case RTS_CONTROL_TOGGLE   : fprintf(stdout,  _("toggle\n"));   break;
394   }
395 }
396 #endif /* DEBUG */
397
398 void device_setdtrrts(int __dtr, int __rts)
399 {
400   DCB dcb;
401   
402   dcb.DCBlength = sizeof(DCB);
403   GetCommState(hPhone, &dcb);
404
405   if (__dtr==1) dcb.fDtrControl = DTR_CONTROL_ENABLE;
406            else dcb.fDtrControl = DTR_CONTROL_DISABLE;
407
408    if (__rts==1) dcb.fRtsControl = RTS_CONTROL_ENABLE;
409             else dcb.fRtsControl = RTS_CONTROL_DISABLE;
410
411    SetCommState(hPhone, &dcb);
412
413 #ifdef DEBUG
414   device_dumpserial();
415 #endif
416 }
417
418 void device_changespeed(int __speed)
419 {
420
421   DCB dcb;
422
423   dcb.DCBlength = sizeof(DCB);
424   GetCommState(hPhone, &dcb);
425
426   switch (__speed) {
427     case 115200: dcb.BaudRate=CBR_115200; break;
428     case 19200 : dcb.BaudRate=CBR_19200;  break;
429     case 9600  : dcb.BaudRate=CBR_9600;   break;
430    }
431    
432    SetCommState(hPhone, &dcb);
433
434 #ifdef DEBUG
435    fprintf(stdout,_("Serial device: changing speed to %i\n"),__speed);
436 #endif
437 }
438
439 bool StartConnection (char *port_device, bool with_odd_parity, GSM_ConnectionType con)
440 {
441   DCB dcb;
442   char text[100];
443
444   int rtn;
445
446   if (PortDevice[0]!=0x00) return true;  
447
448   duringwrite=false;
449   strncpy(PortDevice, port_device, GSM_MAX_DEVICE_NAME_LENGTH);
450
451 #ifdef DEBUG
452   fprintf(stdout,_("Opening \"%s\" device...\n"),PortDevice);
453 #endif
454
455   strcpy(text,"\n\n\nMygnokii ");
456   sprintf(text+strlen(text), "%s",VERSION);
457   strcpy(text+strlen(text),"\n");
458   AppendLogText(text,false);
459
460   strcpy(text,"Port ");
461   strcpy(text+strlen(text),PortDevice);
462   strcpy(text+strlen(text),"\n");
463   AppendLogText(text,false);
464
465   strcpy(text,"Connection ");
466   switch (con) {
467     case GCT_FBUS    :strcpy(text+strlen(text),"FBUS");break;
468     case GCT_MBUS    :strcpy(text+strlen(text),"MBUS");break;
469     case GCT_DLR3    :strcpy(text+strlen(text),"DLR3");break;
470     case GCT_AT      :strcpy(text+strlen(text),"AT");break;
471     default          :strcpy(text+strlen(text),"unknown");break;
472   }
473   strcpy(text+strlen(text),"\n");
474   AppendLogText(text,false);
475
476   CurrentDisableKeepAlive = true;
477
478   /* Create and start main thread. */
479   rtn = ! OpenConnection(PortDevice, Protocol->StateMachine, GSM->KeepAlive);
480
481   if (rtn != 0) {
482
483     fprintf(stdout,_("Error\n"));  
484     return false;
485   } else {
486     CurrentDisableKeepAlive = false;
487   }
488
489   if (with_odd_parity) {
490     dcb.DCBlength = sizeof(DCB);
491     GetCommState(hPhone, &dcb);
492
493     dcb.Parity=ODDPARITY;
494
495     SetCommState(hPhone, &dcb);
496   }
497   
498   return true;
499 }
500
501 size_t device_write(const __ptr_t __buf, size_t __n) {
502   int i;
503   while (duringwrite) {}
504   duringwrite=true;
505   AppendLog(__buf,__n,true);
506   i=WriteCommBlock(__buf,__n);
507   duringwrite=false;
508   if (i) return __n; else return 0;
509 }
510
511 #endif /*WIN32*/