Untested patch for Ir (NOT IrDA!) connections
[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     case GCT_Infrared:strcpy(text+strlen(text),"infrared");break;
440 #ifndef UCLINUX
441     case GCT_Irda    :strcpy(text+strlen(text),"irda sockets");break;
442     case GCT_MBUS    :strcpy(text+strlen(text),"MBUS");break;
443     case GCT_DLR3    :strcpy(text+strlen(text),"DLR3");break;
444     case GCT_Tekram  :strcpy(text+strlen(text),"Tekram");break;
445     case GCT_AT      :strcpy(text+strlen(text),"AT");break;
446     default          :strcpy(text+strlen(text),"unknown");break;
447 #endif /* UCLINUX */
448   }
449   strcpy(text+strlen(text),"\n");
450   AppendLogText(text,false);
451
452   /* Ralf Thelen: In this moment there is NO method of communication,
453    which require keepalive packets and GSM->KeepAlive is
454    always NULL_KeepAlive, I comment this thread, */
455
456   /* Create and start main thread. */
457 //  rtn = pthread_create(&Thread, NULL,(void*)GSM->KeepAlive, (void *)NULL);
458 //
459 //  if (rtn != 0) {
460 //    fprintf(stdout,_("Error\n"));  
461 //    return false;
462 //  }
463
464 #ifndef UCLINUX
465 #if defined(__svr4__) || defined(__FreeBSD__)
466 #else
467         /* Set up and install handler before enabling async IO on port. */
468         sig_io.sa_handler = SigHandler;
469         sig_io.sa_flags = 0;
470         sigaction (SIGIO, &sig_io, NULL);
471 #endif
472 #endif /* UCLINUX */
473
474     /* Open device. */
475     result = device_open(PortDevice, with_odd_parity);
476     
477     if (!result) {
478       fprintf(stdout,_("Error\n"));  
479       return false;
480     }
481
482 #ifndef UCLINUX
483 #if defined(__svr4__) || defined(__FreeBSD__)
484     /* create a thread to handle incoming data from mobile phone */
485     rtn=pthread_create(&selThread,NULL,(void*)SelectLoop,(void*)NULL);
486     if (rtn != 0) {
487       fprintf(stdout,_("Error\n"));  
488       return false;
489     }
490 #endif
491 #endif /* UCLINUX */
492
493   return true;
494 }
495
496 /* ---------------- RTH:   #ifdef WIN32 ------------------ */  
497
498 #else
499
500   extern HANDLE hPhone;
501
502 void device_close(void)
503 {
504   AppendLogText("CLOSE\n",false);
505
506   CloseConnection();
507
508   PortDevice[0]=0x00;
509 }
510
511 #ifdef DEBUG
512 void device_dumpserial(void)
513 {
514   DCB dcb;
515   
516   dcb.DCBlength = sizeof(DCB);
517   GetCommState(hPhone, &dcb);
518
519   fprintf(stdout, _("Serial device:"));
520   fprintf(stdout,  _(" DTR is "));
521   switch (dcb.fDtrControl) {
522     case DTR_CONTROL_ENABLE   : fprintf(stdout,  _("up"));       break;
523     case DTR_CONTROL_DISABLE  : fprintf(stdout,  _("down"));     break;
524     case DTR_CONTROL_HANDSHAKE: fprintf(stdout,  _("handshake"));break;
525   }
526   fprintf(stdout, _(", RTS is "));
527   switch (dcb.fRtsControl) {
528     case RTS_CONTROL_ENABLE   : fprintf(stdout,  _("up\n"));       break;
529     case RTS_CONTROL_DISABLE  : fprintf(stdout,  _("down\n"));     break;
530     case RTS_CONTROL_HANDSHAKE: fprintf(stdout,  _("handshake\n"));break;
531     case RTS_CONTROL_TOGGLE   : fprintf(stdout,  _("toggle\n"));   break;
532   }
533 }
534 #endif /* DEBUG */
535
536 void device_setdtrrts(int __dtr, int __rts)
537 {
538   DCB dcb;
539   
540   dcb.DCBlength = sizeof(DCB);
541   GetCommState(hPhone, &dcb);
542
543   if (__dtr==1) dcb.fDtrControl = DTR_CONTROL_ENABLE;
544            else dcb.fDtrControl = DTR_CONTROL_DISABLE;
545
546    if (__rts==1) dcb.fRtsControl = RTS_CONTROL_ENABLE;
547             else dcb.fRtsControl = RTS_CONTROL_DISABLE;
548
549    SetCommState(hPhone, &dcb);
550
551 #ifdef DEBUG
552   device_dumpserial();
553 #endif
554 }
555
556 void device_changespeed(int __speed)
557 {
558
559   DCB dcb;
560
561   dcb.DCBlength = sizeof(DCB);
562   GetCommState(hPhone, &dcb);
563
564   switch (__speed) {
565     case 115200: dcb.BaudRate=CBR_115200; break;
566     case 19200 : dcb.BaudRate=CBR_19200;  break;
567     case 9600  : dcb.BaudRate=CBR_9600;   break;
568    }
569    
570    SetCommState(hPhone, &dcb);
571
572 #ifdef DEBUG
573    fprintf(stdout,_("Serial device: changing speed to %i\n"),__speed);
574 #endif
575 }
576
577 bool StartConnection (char *port_device, bool with_odd_parity, GSM_ConnectionType con)
578 {
579   DCB dcb;
580   char text[100];
581
582   int rtn;
583
584   if (PortDevice[0]!=0x00) return true;  
585
586   duringwrite=false;
587   strncpy(PortDevice, port_device, GSM_MAX_DEVICE_NAME_LENGTH);
588
589 #ifdef DEBUG
590   fprintf(stdout,_("Opening \"%s\" device...\n"),PortDevice);
591 #endif
592
593   strcpy(text,"\n\n\nMygnokii ");
594   sprintf(text+strlen(text), "%s",VERSION);
595   strcpy(text+strlen(text),"\n");
596   AppendLogText(text,false);
597
598   strcpy(text,"Port ");
599   strcpy(text+strlen(text),PortDevice);
600   strcpy(text+strlen(text),"\n");
601   AppendLogText(text,false);
602
603   strcpy(text,"Connection ");
604   switch (con) {
605     case GCT_FBUS    :strcpy(text+strlen(text),"FBUS");break;
606     case GCT_Infrared:strcpy(text+strlen(text),"infrared");break;
607 #ifndef UCLINUX
608     case GCT_Irda    :strcpy(text+strlen(text),"irda sockets");break;
609     case GCT_MBUS    :strcpy(text+strlen(text),"MBUS");break;
610     case GCT_DLR3    :strcpy(text+strlen(text),"DLR3");break;
611     case GCT_Tekram  :strcpy(text+strlen(text),"Tekram");break;
612     case GCT_AT      :strcpy(text+strlen(text),"AT");break;
613 #endif /* UCLINUX */
614     default          :strcpy(text+strlen(text),"unknown");break;
615   }
616   strcpy(text+strlen(text),"\n");
617   AppendLogText(text,false);
618
619   CurrentDisableKeepAlive = true;
620
621   /* Create and start main thread. */
622   rtn = ! OpenConnection(PortDevice, Protocol->StateMachine, GSM->KeepAlive);
623
624   if (rtn != 0) {
625
626     fprintf(stdout,_("Error\n"));  
627     return false;
628   } else {
629     CurrentDisableKeepAlive = false;
630   }
631
632   if (with_odd_parity) {
633     dcb.DCBlength = sizeof(DCB);
634     GetCommState(hPhone, &dcb);
635
636     dcb.Parity=ODDPARITY;
637
638     SetCommState(hPhone, &dcb);
639   }
640   
641   return true;
642 }
643
644 size_t device_write(const __ptr_t __buf, size_t __n) {
645   int i;
646   while (duringwrite) {}
647   duringwrite=true;
648   AppendLog(__buf,__n,true);
649   i=WriteCommBlock(__buf,__n);
650   duringwrite=false;
651   if (i) return __n; else return 0;
652 }
653
654 #endif /*WIN32*/