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