Branch update for mygnokii2002_03_17_19_29nl
[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   #include <fcntl.h>
20 #else
21   #include <windows.h>
22   #include "devices/winserial.h"
23 #endif
24
25 #include "gsm-api.h"
26 #include "devices/device.h"
27 #include "files/cfgreader.h"
28
29 #include <string.h>
30
31 #ifdef VC6
32   /* for VC6 make scripts save VERSION constant in mversion.h file */
33   #include "mversion.h"
34 #endif
35
36 static char PortDevice[GSM_MAX_DEVICE_NAME_LENGTH]={0x00};
37
38 static bool duringwrite;
39
40 #ifndef WIN32
41
42 #ifndef UCLINUX
43 char *lockfile;
44 #endif /* UCLINUX */
45
46 //pthread_t Thread;
47 #if defined(__svr4__) || defined(__FreeBSD__)
48   pthread_t selThread;
49 #endif
50
51 int device_portfd = -1;
52
53 #define max_buf_len 128
54 #define lock_path "/var/lock/LCK.."
55
56 #ifndef UCLINUX
57
58 /* Lock the device. Return allocated string with a lock name */
59 char *lock_device(const char* port)
60 {
61         char *lock_file = NULL;
62         char buffer[max_buf_len];
63         const char *aux = rindex(port, '/');
64         int fd, len = strlen(aux) + strlen(lock_path);
65
66         /* Remove leading '/' */
67         if (aux) aux++;
68         else aux = port;
69
70         memset(buffer, 0, sizeof(buffer));
71         lock_file = calloc(len + 1, 1);
72         if (!lock_file) {
73                 fprintf(stderr, _("Out of memory error while locking device\n"));
74                 return NULL;
75         }
76         /* I think we don't need to use strncpy, as we should have enough
77          * buffer due to strlen results
78          */
79         strcpy(lock_file, lock_path);
80         strcat(lock_file, aux);
81
82         /* Check for the stale lockfile.
83          * The code taken from minicom by Miquel van Smoorenburg */
84         if ((fd = open(lock_file, O_RDONLY)) >= 0) {
85                 char buf[max_buf_len];
86                 int pid, n = 0;
87
88                 n = read(fd, buf, sizeof(buf) - 1);
89                 close(fd);
90                 if (n > 0) {
91                         pid = -1;
92                         if (n == 4)
93                                 /* Kermit-style lockfile. */
94                                 pid = *(int *)buf;
95                         else {
96                                 /* Ascii lockfile. */
97                                 buf[n] = 0;
98                                 pid = atoi(buf);
99                         }
100                         if (pid > 0 && kill((pid_t)pid, 0) < 0 && errno == ESRCH) {
101                                 fprintf(stderr, _("Lockfile %s is stale. Overriding it..\n"), lock_file);
102                                 sleep(1);
103                                 if (unlink(lock_file) == -1) {
104                                         fprintf(stderr, _("Overriding failed, please check the permissions\n"));
105                                         fprintf(stderr, _("Cannot lock device\n"));
106                                         goto failed;
107                                 }
108                         } else {
109                                 fprintf(stderr, _("Device already locked.\n"));
110                                 goto failed;
111                         }
112                 }
113                 /* this must not happen. because we could open the file   */
114                 /* no wrong permissions are set. only reason could be     */
115                 /* flock/lockf or a empty lockfile due to a broken binary */
116                 /* which is more likely (like gnokii 0.4.0pre11 ;-)       */
117                 if (n == 0) {
118                         fprintf(stderr, _("Unable to read lockfile %s.\n"), lock_file);
119                         fprintf(stderr, _("Please check for reason and remove the lockfile by hand.\n"));
120                         fprintf(stderr, _("Cannot lock device\n"));
121                         goto failed;
122                 }
123         }
124
125         /* Try to create a new file, with 0644 mode */
126         fd = open(lock_file, O_CREAT | O_EXCL | O_WRONLY, 0644);
127         if (fd == -1) {
128                 if (errno == EEXIST)
129                         fprintf(stderr, _("Device seems to be locked by unknown process\n"));
130                 else if (errno == EACCES)
131                         fprintf(stderr, _("Please check permission on lock directory\n"));
132                 else if (errno == ENOENT)
133                         fprintf(stderr, _("Cannot create lockfile %s. Please check for existence of path"), lock_file);
134                 goto failed;
135         }
136         sprintf(buffer, "%10ld gnokii\n", (long)getpid());
137         write(fd, buffer, strlen(buffer));
138         close(fd);
139         return lock_file;
140 failed:
141         free(lock_file);
142         return NULL;
143 }
144
145 /* Removes lock and frees memory */
146 bool unlock_device(char *lock_file)
147 {
148         int err;
149
150         if (!lock_file) {
151 //              fprintf(stderr, _("Cannot unlock device\n"));
152                 return false;
153         }
154         err = unlink(lock_file);
155         free(lock_file);
156         return (err + 1);
157 }
158
159 #endif /* UCLINUX */
160
161 /*
162  * Structure to store the filedescriptor we use.
163  *
164  */
165 #ifdef DEBUG
166 static int device_getfd(void)
167 {
168   return device_portfd;
169 }
170 #endif /* DEBUG */
171
172 static int device_open(__const char *__file, int __with_odd_parity) {
173 #ifndef UCLINUX
174   struct CFG_Header *cfg_info;
175   char *aux;    
176
177   cfg_info=CFG_FindGnokiirc();
178   if (cfg_info!=NULL) {
179     aux = CFG_Get(cfg_info, "global", "use_locking");
180     if (aux) {
181       if (!strcmp(aux, "yes")) {
182         lockfile = lock_device(__file);
183         if (!lockfile) return false;
184       }
185     }
186   }
187 #endif /* UCLINUX */
188   
189   switch (CurrentConnectionType) {
190     default:
191       device_portfd = serial_opendevice(__file, __with_odd_parity, true, false);
192       break;
193   }
194   
195   return (device_portfd >= 0);
196 }
197
198 void device_close(void)
199 {
200   AppendLogText("CLOSE\n",false);
201
202   /* Now wait for thread to terminate. */
203   //pthread_join(Thread, NULL);
204
205 #ifndef UCLINUX
206   unlock_device(lockfile);
207 #endif /* UCLINUX */
208   
209   switch (CurrentConnectionType) {
210     default        : serial_close(device_portfd); break;
211   }
212
213   PortDevice[0]=0x00;
214 }
215
216 #ifndef UCLINUX
217
218 void device_reset(void) {
219 }
220
221 #endif /* UCLINUX */
222
223 #ifdef DEBUG
224 static void device_dumpserial(void);
225 #endif /* DEBUG */
226
227 void device_setdtrrts(int __dtr, int __rts)
228 {
229   switch (CurrentConnectionType) {
230     default:
231       serial_setdtrrts(device_portfd, __dtr, __rts);
232 #ifdef DEBUG
233       device_dumpserial();
234 #endif
235       break;
236   }
237 }
238
239 void device_changespeed(int __speed)
240 {
241   switch (CurrentConnectionType) {
242     default:
243       serial_changespeed(device_portfd, __speed);
244 #ifdef DEBUG
245       fprintf(stdout,_("Serial device: changing speed to %i\n"),__speed);
246 #endif
247       break;
248   }
249 }
250
251 static size_t device_read(__ptr_t __buf, size_t __nbytes)
252 {
253   switch (CurrentConnectionType) {
254     default        : return (serial_read(device_portfd, __buf, __nbytes)); break;
255   }
256 }
257
258 size_t device_write(__const __ptr_t __buf, size_t __n) {
259   u8 buffer[300];
260   size_t mysize;
261   
262   while (duringwrite) {
263     fprintf(stderr,"device_write: reentrance violation!\n");
264     _exit(1);
265   }
266
267   memcpy(buffer,__buf,__n);
268   AppendLog(buffer,__n,true);
269
270   duringwrite=true;
271   switch (CurrentConnectionType) {
272     default        : mysize = serial_write(device_portfd, __buf, __n); break;
273   }
274   duringwrite=false;
275   if (mysize!=__n)
276     fprintf(stderr,"WARNING: device_write(__n=%ld)=%ld\n",(long)__n,(long)mysize);
277   return mysize;
278 }
279
280 #ifdef DEBUG
281 static void device_dumpserial(void)
282 {
283   int PortFD;
284   unsigned int Flags=0;
285
286   PortFD = device_getfd();
287
288   ioctl(PortFD, TIOCMGET, &Flags);
289
290   fprintf(stdout, _("Serial device:"));
291   fprintf(stdout,  _(" DTR is %s"), Flags&TIOCM_DTR?_("up"):_("down"));
292   fprintf(stdout, _(", RTS is %s"), Flags&TIOCM_RTS?_("up"):_("down"));
293   fprintf(stdout, _(", CAR is %s"), Flags&TIOCM_CAR?_("up"):_("down"));
294   fprintf(stdout, _(", CTS is %s\n"), Flags&TIOCM_CTS?_("up"):_("down"));
295 }
296 #endif /* DEBUG */
297
298 static char SigHandler_buffer[255];
299
300 void SigHandler(int status)
301 {
302   int count, res;
303
304   LIVE_DISABLE;
305   LIVE;
306
307   res = device_read(SigHandler_buffer, sizeof(SigHandler_buffer));
308
309   for (count = 0; count < res ; count ++) {
310     Protocol->StateMachine(SigHandler_buffer[count]);
311   }
312   LIVE_ENABLE;
313 }
314
315 #ifdef UCLINUX
316 void usleep_watchdevice(unsigned long usecs)
317 {
318 int err;
319 fd_set readfds;
320 struct timeval target,timeout;
321 long timeout_sec,timeout_usec; /* signed! */
322
323   if (gettimeofday(&target/*tv*/,NULL/*tz*/))
324     perror("usleep_watchdevice()/gettimeofday(2) init");
325   target.tv_usec+=usecs%1000000;
326   target.tv_sec +=usecs/1000000 + (target.tv_usec/1000000);
327   target.tv_usec%=1000000;
328
329   for (;;) {
330     if (gettimeofday(&timeout/*tv*/,NULL/*tz*/))
331       perror("usleep_watchdevice()/gettimeofday(2) loop");
332
333     timeout_sec =target.tv_sec -(long)timeout.tv_sec ;
334     timeout_usec=target.tv_usec-(long)timeout.tv_usec;
335
336     if (timeout_usec<0) {
337       timeout_sec--;
338       timeout_usec+=1000000;
339     }
340     if (timeout_sec<0)
341       return;
342
343     timeout.tv_sec =timeout_sec ;
344     timeout.tv_usec=timeout_usec;
345
346     FD_ZERO(&readfds);
347     if (device_portfd>=0)
348       FD_SET(device_portfd,&readfds);
349
350     err=select((device_portfd<0 ? 0 : device_portfd+1),&readfds,NULL,NULL,&timeout);
351     if ( err > 0 ) {
352       if (device_portfd>=0 && FD_ISSET(device_portfd,&readfds)) {
353         /* call singal handler to process incoming data */
354         SigHandler(0);
355       }
356     } else {
357       if (err == -1)
358       perror("Error in SelectLoop");
359     }
360   }
361 }
362 #endif
363
364 #if defined(__svr4__) || defined(__FreeBSD__)
365 /* thread for handling incoming data */
366 void SelectLoop() {
367   int err;
368   fd_set readfds;
369   struct timeval timeout;
370
371   FD_ZERO(&readfds);
372   FD_SET(device_portfd,&readfds);
373   /* set timeout to 15 seconds */
374   timeout.tv_sec=15;
375   timeout.tv_usec=0;
376   while (!CurrentRequestTerminate) {
377     err=select(device_portfd+1,&readfds,NULL,NULL,&timeout);
378     if ( err > 0 ) {
379       /* call singal handler to process incoming data */
380       SigHandler(0);
381       /* refresh timeout, just for linux :-( */
382       /* required for irda */
383       timeout.tv_sec=15;
384     } else {
385       if (err == -1)
386       perror("Error in SelectLoop");
387     }
388   }
389 }
390 #endif
391
392 bool StartConnection (char *port_device, bool with_odd_parity, GSM_ConnectionType con)
393 {
394   int result;
395
396   char text[100];
397
398 #ifndef UCLINUX
399 #if defined(__svr4__) || defined(__FreeBSD__)
400   int rtn;
401 #else
402   struct sigaction sig_io;
403 #endif
404 #endif /* UCLINUX */
405
406 #ifndef UCLINUX
407 #ifdef DEBUG
408       if ((strstr(GSM_Info->IrdaModels,"decode")!=NULL) &&  (CurrentConnectionType == GCT_Irda))
409        {
410          printf("DEBUG and Irda decode Model -> not realy open ;-)\n");
411          return true;
412        }
413 #endif 
414 #endif /* UCLINUX */
415
416   if (PortDevice[0]!=0x00) return true;
417
418   duringwrite=false;
419   
420   strncpy(PortDevice, port_device, GSM_MAX_DEVICE_NAME_LENGTH);
421
422 #ifdef DEBUG
423   fprintf(stdout,_("Opening \"%s\" device...\n"),PortDevice);
424 #endif
425
426   strcpy(text,"\n\n\nMygnokii ");
427   sprintf(text+strlen(text), "%s",VERSION);
428   strcpy(text+strlen(text),"\n");
429   AppendLogText(text,false);
430
431   strcpy(text,"Port ");
432   strcpy(text+strlen(text),PortDevice);
433   strcpy(text+strlen(text),"\n");
434   AppendLogText(text,false);
435
436   strcpy(text,"Connection ");
437   switch (con) {
438     case GCT_FBUS    :strcpy(text+strlen(text),"FBUS");break;
439 #ifndef UCLINUX
440     case GCT_MBUS    :strcpy(text+strlen(text),"MBUS");break;
441     case GCT_DLR3    :strcpy(text+strlen(text),"DLR3");break;
442     case GCT_AT      :strcpy(text+strlen(text),"AT");break;
443     default          :strcpy(text+strlen(text),"unknown");break;
444 #endif /* UCLINUX */
445   }
446   strcpy(text+strlen(text),"\n");
447   AppendLogText(text,false);
448
449   /* Ralf Thelen: In this moment there is NO method of communication,
450    which require keepalive packets and GSM->KeepAlive is
451    always NULL_KeepAlive, I comment this thread, */
452
453   /* Create and start main thread. */
454 //  rtn = pthread_create(&Thread, NULL,(void*)GSM->KeepAlive, (void *)NULL);
455 //
456 //  if (rtn != 0) {
457 //    fprintf(stdout,_("Error\n"));  
458 //    return false;
459 //  }
460
461 #ifndef UCLINUX
462 #if defined(__svr4__) || defined(__FreeBSD__)
463 #else
464         /* Set up and install handler before enabling async IO on port. */
465         sig_io.sa_handler = SigHandler;
466         sig_io.sa_flags = 0;
467         sigaction (SIGIO, &sig_io, NULL);
468 #endif
469 #endif /* UCLINUX */
470
471     /* Open device. */
472     result = device_open(PortDevice, with_odd_parity);
473     
474     if (!result) {
475       fprintf(stdout,_("Error\n"));  
476       return false;
477     }
478
479 #ifndef UCLINUX
480 #if defined(__svr4__) || defined(__FreeBSD__)
481     /* create a thread to handle incoming data from mobile phone */
482     rtn=pthread_create(&selThread,NULL,(void*)SelectLoop,(void*)NULL);
483     if (rtn != 0) {
484       fprintf(stdout,_("Error\n"));  
485       return false;
486     }
487 #endif
488 #endif /* UCLINUX */
489
490   return true;
491 }
492
493 /* ---------------- RTH:   #ifdef WIN32 ------------------ */  
494
495 #else
496
497   extern HANDLE hPhone;
498
499 void device_close(void)
500 {
501   AppendLogText("CLOSE\n",false);
502
503   CloseConnection();
504
505   PortDevice[0]=0x00;
506 }
507
508 #ifdef DEBUG
509 void device_dumpserial(void)
510 {
511   DCB dcb;
512   
513   dcb.DCBlength = sizeof(DCB);
514   GetCommState(hPhone, &dcb);
515
516   fprintf(stdout, _("Serial device:"));
517   fprintf(stdout,  _(" DTR is "));
518   switch (dcb.fDtrControl) {
519     case DTR_CONTROL_ENABLE   : fprintf(stdout,  _("up"));       break;
520     case DTR_CONTROL_DISABLE  : fprintf(stdout,  _("down"));     break;
521     case DTR_CONTROL_HANDSHAKE: fprintf(stdout,  _("handshake"));break;
522   }
523   fprintf(stdout, _(", RTS is "));
524   switch (dcb.fRtsControl) {
525     case RTS_CONTROL_ENABLE   : fprintf(stdout,  _("up\n"));       break;
526     case RTS_CONTROL_DISABLE  : fprintf(stdout,  _("down\n"));     break;
527     case RTS_CONTROL_HANDSHAKE: fprintf(stdout,  _("handshake\n"));break;
528     case RTS_CONTROL_TOGGLE   : fprintf(stdout,  _("toggle\n"));   break;
529   }
530 }
531 #endif /* DEBUG */
532
533 void device_setdtrrts(int __dtr, int __rts)
534 {
535   DCB dcb;
536   
537   dcb.DCBlength = sizeof(DCB);
538   GetCommState(hPhone, &dcb);
539
540   if (__dtr==1) dcb.fDtrControl = DTR_CONTROL_ENABLE;
541            else dcb.fDtrControl = DTR_CONTROL_DISABLE;
542
543    if (__rts==1) dcb.fRtsControl = RTS_CONTROL_ENABLE;
544             else dcb.fRtsControl = RTS_CONTROL_DISABLE;
545
546    SetCommState(hPhone, &dcb);
547
548 #ifdef DEBUG
549   device_dumpserial();
550 #endif
551 }
552
553 void device_changespeed(int __speed)
554 {
555
556   DCB dcb;
557
558   dcb.DCBlength = sizeof(DCB);
559   GetCommState(hPhone, &dcb);
560
561   switch (__speed) {
562     case 115200: dcb.BaudRate=CBR_115200; break;
563     case 19200 : dcb.BaudRate=CBR_19200;  break;
564     case 9600  : dcb.BaudRate=CBR_9600;   break;
565    }
566    
567    SetCommState(hPhone, &dcb);
568
569 #ifdef DEBUG
570    fprintf(stdout,_("Serial device: changing speed to %i\n"),__speed);
571 #endif
572 }
573
574 bool StartConnection (char *port_device, bool with_odd_parity, GSM_ConnectionType con)
575 {
576   DCB dcb;
577   char text[100];
578
579   int rtn;
580
581   if (PortDevice[0]!=0x00) return true;  
582
583   duringwrite=false;
584   strncpy(PortDevice, port_device, GSM_MAX_DEVICE_NAME_LENGTH);
585
586 #ifdef DEBUG
587   fprintf(stdout,_("Opening \"%s\" device...\n"),PortDevice);
588 #endif
589
590   strcpy(text,"\n\n\nMygnokii ");
591   sprintf(text+strlen(text), "%s",VERSION);
592   strcpy(text+strlen(text),"\n");
593   AppendLogText(text,false);
594
595   strcpy(text,"Port ");
596   strcpy(text+strlen(text),PortDevice);
597   strcpy(text+strlen(text),"\n");
598   AppendLogText(text,false);
599
600   strcpy(text,"Connection ");
601   switch (con) {
602     case GCT_FBUS    :strcpy(text+strlen(text),"FBUS");break;
603     case GCT_MBUS    :strcpy(text+strlen(text),"MBUS");break;
604     case GCT_DLR3    :strcpy(text+strlen(text),"DLR3");break;
605     case GCT_AT      :strcpy(text+strlen(text),"AT");break;
606     default          :strcpy(text+strlen(text),"unknown");break;
607   }
608   strcpy(text+strlen(text),"\n");
609   AppendLogText(text,false);
610
611   CurrentDisableKeepAlive = true;
612
613   /* Create and start main thread. */
614   rtn = ! OpenConnection(PortDevice, Protocol->StateMachine, GSM->KeepAlive);
615
616   if (rtn != 0) {
617
618     fprintf(stdout,_("Error\n"));  
619     return false;
620   } else {
621     CurrentDisableKeepAlive = false;
622   }
623
624   if (with_odd_parity) {
625     dcb.DCBlength = sizeof(DCB);
626     GetCommState(hPhone, &dcb);
627
628     dcb.Parity=ODDPARITY;
629
630     SetCommState(hPhone, &dcb);
631   }
632   
633   return true;
634 }
635
636 size_t device_write(const __ptr_t __buf, size_t __n) {
637   int i;
638   while (duringwrite) {}
639   duringwrite=true;
640   AppendLog(__buf,__n,true);
641   i=WriteCommBlock(__buf,__n);
642   duringwrite=false;
643   if (i) return __n; else return 0;
644 }
645
646 #endif /*WIN32*/