]> git.itanic.dy.fi Git - maemo-mapper/blob - src/gpsbt.c
5dad65befdc26a7d05d082aa847d7b31ce0b030e
[maemo-mapper] / src / gpsbt.c
1 /*
2  * Copyright (C) 2006, 2007 John Costigan.
3  *
4  * POI and GPS-Info code originally written by Cezary Jackiewicz.
5  *
6  * Default map data provided by http://www.openstreetmap.org/
7  *
8  * This file is part of Maemo Mapper.
9  *
10  * Maemo Mapper is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * Maemo Mapper is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Maemo Mapper.  If not, see <http://www.gnu.org/licenses/>.
22  */
23
24 /*
25 GPS BT management API. The API is used by those applications that
26 wish to use services provided by gps daemon i.e., they wish to receive
27 GPS data from the daemon. See README file for more details.
28
29 Copyright (C) 2006 Nokia Corporation. All rights reserved.
30
31 Author: Jukka Rissanen <jukka.rissanen@nokia.com>
32
33 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
34
35 Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
36 Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
37 The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
38
39 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40
41 */
42
43 /* $Id:$ */
44
45 #ifdef HAVE_CONFIG_H
46 #    include "config.h"
47 #endif
48
49 #ifdef HAVE_LIBGPSBT
50
51 #define _GNU_SOURCE
52
53 #include <errno.h>
54 #include <unistd.h>
55 #include <fcntl.h>
56 #include <string.h>
57 #include <signal.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <sys/wait.h>
61 #include <sys/time.h>
62 #include <time.h>
63
64 #define DBUS_API_SUBJECT_TO_CHANGE
65 #include <dbus/dbus-glib.h>
66
67 #include "gpsbt.h"
68
69 #ifdef LEGACY
70
71 /* Set following #if to 1 if you want to use automatic bt dev disconnect.
72  * This does not seem to work correctly currently (disconnects too fast)
73  */
74 #if 0
75 #define USE_AUTOMATIC_DISCONNECT
76 #else
77 #undef USE_AUTOMATIC_DISCONNECT
78 #endif
79
80 /* default control socket (not used by default) */
81 #define CTRL_SOCK "/tmp/.gpsd_ctrl_sock"
82
83 /* Note that 10 second timeout should not be lowered, because
84  * then there might be GPS device connection errors if there
85  * are multiple GPS devices that the user is using.
86  * E.g., if the timeout is set to 5 secs and there are three
87  * GPS devices that the user is using, it is possible that
88  * if the 1st device fails, the other devices might also fail
89  * if the timeout is lower than 10 secs.
90  */
91 #if 0
92 #define DEFAULT_TIMEOUT (1000*10) /* 10 second timeout (in ms) */
93 #else
94 #define DEFAULT_TIMEOUT (1000*60) /* 60 second timeout (in ms) */
95 #endif
96
97 /* BT dbus service location */
98 #define BASE_PATH                "/org/bluez"
99 #define BASE_INTERFACE           "org.bluez"
100 #define ADAPTER_PATH             BASE_PATH
101 #define ADAPTER_INTERFACE        BASE_INTERFACE ".Adapter"
102 #define MANAGER_PATH             BASE_PATH
103 #define MANAGER_INTERFACE        BASE_INTERFACE ".Manager"
104 #define ERROR_INTERFACE          BASE_INTERFACE ".Error"
105 #define SECURITY_INTERFACE       BASE_INTERFACE ".Security"
106 #define RFCOMM_INTERFACE         BASE_INTERFACE ".RFCOMM"
107 #define BLUEZ_DBUS               BASE_INTERFACE
108
109 #define LIST_ADAPTERS            "ListAdapters"
110 #define LIST_BONDINGS            "ListBondings"
111 #define CREATE_BONDING           "CreateBonding"
112 #define GET_REMOTE_NAME          "GetRemoteName"
113 #define GET_REMOTE_SERVICE_CLASSES "GetRemoteServiceClasses"
114
115 #define BTCOND_PATH              "/com/nokia/btcond/request"
116 #define BTCOND_BASE              "com.nokia.btcond"
117 #define BTCOND_INTERFACE         BTCOND_BASE ".request"
118 #define BTCOND_REQUEST           BTCOND_INTERFACE
119 #define BTCOND_CONNECT           "rfcomm_connect"
120 #define BTCOND_DISCONNECT        "rfcomm_disconnect"
121 #define BTCOND_DBUS              BTCOND_BASE
122
123 #define GPS_SERVICE_CLASS_STR "positioning"
124
125 typedef struct {
126   char *adapter;  /* do not free this, it is freed somewhere else */
127   char *bonding;  /* allocated from heap, you must free this */
128 } bonding_t;
129
130
131 /* Not all gps bt devices set the positioning bit in service class to 1,
132  * so we need a way to figure out whether the device supports positioning
133  * or not. The array contains sub-string for devices that we know to
134  * support positioning. The sub-string will be matched against device
135  * remote name that we get using dbus GetRemoteName call. The name is
136  * fetched only if we have no BT device in the system that has the
137  * positioning bit activated. Note that the listed string is case
138  * sensitive.
139  */
140 static const char const *gps_device_names[] = {
141   "HOLUX GR-",   /* like Holux GR-231 or similar */
142   "Nokia LD-",   /* for Nokia LD-1W, note that LD-3W has positioning bit set */
143   "GPS",         /* Insmat BT-GPS-3244C8 or TomTom Wireless GPS MkII */
144   "i-Blue",      /* i-Blue line of GPS receivers. */
145   0  /* must be at the end */
146 };
147
148
149 /* ----------------------------------------------------------------------- */
150 static int debug_level;
151
152 #ifdef DEBUG
153 #if (__GNUC__ > 2) && ((__GNUC__ > 3) || (__GNUC_MINOR__ > 2))
154 #define PDEBUG(fmt...) do {                                                \
155     if (debug_level) {                                                     \
156       struct timeval tv;                                                   \
157       gettimeofday(&tv, 0);                                                \
158       printf("DEBUG[%d]:%ld.%ld:%s:%s():%d: ",                             \
159              getpid(),                                                     \
160              tv.tv_sec, tv.tv_usec,                                        \
161              __FILE__, __FUNCTION__, __LINE__);                            \
162       printf(fmt);                                                         \
163       fflush(stdout);                                                      \
164     }                                                                      \
165   }while(0)
166 #else
167 #define PDEBUG(fmt...) do {                                                \
168     if (debug_level) {                                                     \
169       struct timeval tv;                                                   \
170       gettimeofday(&tv, 0);                                                \
171       printf("DEBUG[%d]:%ld.%ld:%s:%s():%d: ",                             \
172              getpid(),                                                     \
173              tv.tv_sec, tv.tv_usec,                                        \
174              __FILE__, __FUNCTION__, __LINE__);                            \
175       printf(##fmt);                                                       \
176       fflush(stdout);                                                      \
177     }                                                                      \
178   }while(0)
179 #endif
180 #else
181 #define PDEBUG(fmt...)
182 #endif
183
184
185 /* ----------------------------------------------------------------------- */
186 static int check_device_name(char *name)
187 {
188   int i=0, st=0;
189
190   while (gps_device_names[i]) {
191     if (strstr(name, gps_device_names[i])) {
192       st = 1;
193       break;
194     }
195     i++;
196   }
197
198   return st;
199 }
200
201
202 /* ----------------------------------------------------------------------- */
203 static inline DBusGConnection *get_dbus_gconn(GError **error)
204 {
205   DBusGConnection *conn;
206
207   conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, error);
208   return conn;
209 }
210
211
212 /* ----------------------------------------------------------------------- */
213 static int set_error_msg(char *errbuf,
214                          int errbuf_max_len,
215                          char *msg,
216                          ...)
217 {
218   int st, len;
219   va_list args;
220
221   if (!errbuf) {
222     errno = EINVAL;
223     return -1;
224   }
225
226   va_start(args, msg);
227   st = vsnprintf(errbuf, errbuf_max_len, msg, args);
228   va_end(args);
229
230   /* Remove \n if it is at the end of the line (so that caller can
231    * print the string as it wishes)
232    */
233   len = strlen(errbuf);
234   if (len>0 && errbuf[len-1]=='\n')
235     errbuf[len-1]='\0';
236
237   return st;
238 }
239
240
241 /* ----------------------------------------------------------------------- */
242 extern int gpsbt_start(char *bda,
243                        int our_debug_level,
244                        int gpsd_debug_level,
245                        short port,
246                        char *error_buf,
247                        int error_buf_max_len,
248                        int timeout_ms,
249                        gpsbt_t *ctx)
250 {
251   int i, j, k, st, num_bondings = 0,
252     bonding_cnt = 0, num_classes = 0, num_rfcomms = 0,
253     num_posdev = 0;
254   GError *error = NULL;
255   DBusGConnection *bus = NULL;
256   DBusGProxy *proxy = NULL;
257   char **str_iter = NULL;
258   char **tmp_bondings = 0, **tmp_classes = 0;
259   char **adapters = 0;
260   char **rfcomms = 0;
261   bonding_t *bondings = 0; /* points to array of bonding_t */
262   bonding_t *posdev = 0; /* bondings with positioning bit on */
263   char *tmp;
264   char *onoff;
265   const char const *spp="SPP";
266   int timeout;
267   char *gpsd_prog;
268   char *gpsd_ctrl_sock;
269
270
271 #if (__GNUC__ > 2) && ((__GNUC__ > 3) || (__GNUC_MINOR__ > 2))
272 #define ERRSTR(fmt, args...)                                     \
273   if (error_buf && error_buf_max_len>0) {                        \
274     set_error_msg(error_buf, error_buf_max_len, fmt, args);      \
275   } else {                                                       \
276     PDEBUG(fmt, args);                                           \
277   }
278 #else
279 #define ERRSTR(fmt, args...)                                     \
280   if (error_buf && error_buf_max_len>0) {                        \
281     set_error_msg(error_buf, error_buf_max_len, fmt, ##args);    \
282   } else {                                                       \
283     PDEBUG(fmt, ##args);                                         \
284   }
285 #endif  
286
287   if (!ctx) {
288     errno = EINVAL;
289     return -1;
290   }
291
292   debug_level = our_debug_level;
293
294   /* context & error needs to be clean */
295   memset(ctx, 0, sizeof(gpsmgr_t));
296
297   if (timeout_ms==0)
298     timeout = DEFAULT_TIMEOUT;
299   else if (timeout_ms < 0)
300     timeout = -1;
301   else
302     timeout = timeout_ms;
303
304   ctx->timeout = timeout;
305
306   /* Caller can override the name of the gpsd program and
307    * the used control socket.
308    */
309   gpsd_prog = getenv("GPSD_PROG");
310   gpsd_ctrl_sock = getenv("GPSD_CTRL_SOCK");
311
312   if (!gpsd_prog)
313     gpsd_prog = "gpsd";
314
315   if (!gpsd_ctrl_sock)
316     gpsd_ctrl_sock = CTRL_SOCK;
317
318   /* First find out what BT devices are paired with us, then figure out
319    * which one of those are GPS devices (positioning bit in class is
320    * set). If found, create a rfcommX device for GPS BT and pass that
321    * as a parameter when starting gpsd.
322    */
323
324   /* But first things first, if gpsd is already running then it is
325    * no point trying to find out the GPS BT devices because some program
326    * has already figured it out.
327    */
328   st = gpsmgr_is_gpsd_running(&ctx->mgr, NULL, GPSMGR_MODE_LOCK_IF_POSSIBLE);
329   if (st) {
330
331     if (st!=2) { /* gpsd is running, value 2 would mean that gpsd is not
332                   * running but we have a lock acquired
333                   */
334
335       st = gpsmgr_start(gpsd_prog, NULL, gpsd_ctrl_sock,
336                         gpsd_debug_level, port, &ctx->mgr);
337       if (!st) {
338         /* everything is ok */
339         PDEBUG("%s already running, doing nothing\n",gpsd_prog);
340         goto OUT;
341       }
342
343       PDEBUG("gpsmgr_start() returned %d [%s, %d]\n",st,strerror(errno),errno);
344       /* Note, no exit or return here, let the API do its magic */
345     }
346   }
347
348
349   /* Use the dbus interface to get the BT information */
350
351   error = NULL;
352   bus = get_dbus_gconn(&error);
353   if (error) {
354     st = -1;
355     errno = ECONNREFUSED; /* close enough :) */
356     ERRSTR("%s", error->message);
357     PDEBUG("Cannot get reply message [%s]\n", error->message);
358     goto OUT;
359   }
360
361 #define CHECK_ERROR(s,o,i,m,t)                                             \
362   if (st<0) {                                                              \
363     ERRSTR("Cannot send msg (service=%s, object=%s, interface=%s, "        \
364            "method=%s) [%s]\n", s, o, i, m,                                \
365            error.message ? error.message : "<no error msg>");              \
366     goto OUT;                                                              \
367   }
368
369
370   /* We need BT information only if the caller does not specify
371    * the BT address. If address is defined, it is assumed that
372    * it is already bonded and we just create RFCOMM connection
373    * to it.
374    */
375   if (!bda) {
376     proxy = dbus_g_proxy_new_for_name(bus,
377         BLUEZ_DBUS, MANAGER_PATH, MANAGER_INTERFACE);
378
379     error = NULL;
380     if(!dbus_g_proxy_call(proxy, LIST_ADAPTERS, &error, G_TYPE_INVALID,
381           G_TYPE_STRV, &adapters, G_TYPE_INVALID)
382         || error || !adapters || !*adapters || !**adapters) {
383       PDEBUG("No adapters found.\n");
384       st = -1;
385       goto OUT;
386     }
387     g_object_unref(proxy);
388
389     /* For each adapter, get bondings */
390     i = 0;
391     while (adapters[i]) {
392       proxy = dbus_g_proxy_new_for_name(bus,
393           BLUEZ_DBUS, adapters[i], ADAPTER_INTERFACE);
394
395       error = NULL;
396       if(!dbus_g_proxy_call(proxy, LIST_BONDINGS, &error, G_TYPE_INVALID,
397             G_TYPE_STRV, &tmp_bondings, G_TYPE_INVALID)
398           || error || !tmp_bondings || !*tmp_bondings || !**tmp_bondings) {
399         PDEBUG("Call to LIST_BONDINGS failed (error=%s, tmp_bondings[0]=%s\n",
400             error ? error->message : "<null>",
401             tmp_bondings ? tmp_bondings[0] : "<null>");
402       } else {
403         /* Count the bondings. */
404         for(num_bondings = 0, str_iter = tmp_bondings; *str_iter;
405             str_iter++, num_bondings++);
406
407         /* Allocate bondings array, note that we DO allocate one extra array
408          * element for marking end of array.
409          */
410         bondings = (bonding_t *)realloc(bondings,
411                 (bonding_cnt+num_bondings+1)*sizeof(bonding_t));
412         if (!bondings) {
413           st = -1;
414           errno = ENOMEM;
415           goto OUT;
416         }
417         bondings[bonding_cnt+num_bondings].bonding=
418           bondings[bonding_cnt+num_bondings].adapter=NULL; /* just in case */
419
420
421         j = 0;
422         while (j<num_bondings && tmp_bondings[j]) {
423           bondings[bonding_cnt].bonding = strdup(tmp_bondings[j]);
424           free(tmp_bondings[j]);
425
426           bondings[bonding_cnt].adapter = adapters[i]; /* no allocation! */
427           
428           PDEBUG("Bondings[%d]=%s (adapter=%s)\n", bonding_cnt,
429                  bondings[bonding_cnt].bonding, bondings[bonding_cnt].adapter);
430
431           /* Get all remote service classes for this bonding. */
432           error = NULL;
433           if(dbus_g_proxy_call(proxy, GET_REMOTE_SERVICE_CLASSES, &error,
434                 G_TYPE_STRING, bondings[bonding_cnt].bonding, G_TYPE_INVALID,
435                 G_TYPE_STRV, &tmp_classes, G_TYPE_INVALID)
436               && !error && tmp_classes && *tmp_classes && **tmp_classes) {
437
438             k = 0;
439             while (tmp_classes[k]) {
440               if (!strcasecmp(tmp_classes[k], GPS_SERVICE_CLASS_STR)) {
441                 /* match found, this device claims to be able to provide
442                  * positioning data
443                  */
444                 posdev = (bonding_t *)realloc(posdev,
445                     (num_posdev+1)*sizeof(bonding_t));
446                 if (!posdev) {
447                   st = -1;
448                   errno = ENOMEM;
449                   goto OUT;
450                 }
451               
452                 posdev[num_posdev].bonding=strdup(
453                     bondings[bonding_cnt].bonding);
454                 posdev[num_posdev].adapter=bondings[bonding_cnt].adapter;
455                 num_posdev++;
456                 onoff = "ON";
457               } else {
458                 onoff = "OFF";
459               }
460               PDEBUG("Addr=%s, Class[%d]=%s (adapter=%s), "
461                       "positioning bit %s\n",
462                      bondings[bonding_cnt].bonding, k, tmp_classes[k],
463                      bondings[bonding_cnt].adapter, onoff);
464               free(tmp_classes[k]);
465               
466               k++;
467             }
468
469             free(tmp_classes);
470             tmp_classes=0;
471           }
472
473           num_classes = 0;
474           bonding_cnt++;
475           j++;
476         }
477       }
478
479       free(tmp_bondings);
480       tmp_bondings=0;
481       g_object_unref(proxy);
482       i++;
483     }
484
485 #if 0
486 #ifdef DEBUG
487     if (debug_level) {
488       int i=0;
489       if (bonding_cnt) {
490         PDEBUG("Bondings [%d]:\n", bonding_cnt);
491         while (bondings[i].bonding && i<bonding_cnt) {
492           PDEBUG("\t%s\n", bondings[i].bonding);
493           i++;
494         }
495       } else {
496         PDEBUG("No bondings exists.\n");
497       }
498     }
499 #endif
500 #endif
501
502     /* We have bonded devices but none has positioning bit set. Try
503      * to find out if any of the devices is known to be a BT device.
504      */
505     if (bonding_cnt>0 && num_posdev == 0) {
506
507       /* For each bonded device, get its name */
508       i = 0;
509       while (i<bonding_cnt && bondings[i].bonding) {
510         proxy = dbus_g_proxy_new_for_name(bus,
511             BLUEZ_DBUS, bondings[i].adapter, ADAPTER_INTERFACE);
512
513         tmp = NULL;
514         error = NULL;
515         if(dbus_g_proxy_call(proxy, GET_REMOTE_NAME, &error,
516               G_TYPE_STRING, bondings[i].bonding, G_TYPE_INVALID,
517               G_TYPE_STRING, &tmp, G_TYPE_INVALID)
518             && !error && tmp && *tmp) {
519           PDEBUG("Checking device name: %s\n", tmp);
520           if (check_device_name(tmp)) {
521
522             /* Found a GPS device */
523             posdev = (bonding_t *)realloc(posdev,
524                     (num_posdev+1)*sizeof(bonding_t));
525             if (!posdev) {
526               st = -1;
527               errno = ENOMEM;
528               goto OUT;
529             }
530         
531             posdev[num_posdev].bonding=strdup(bondings[i].bonding);
532             posdev[num_posdev].adapter=bondings[i].adapter;
533             num_posdev++;
534
535             PDEBUG("Addr=%s, (adapter=%s), Name=\"%s\"\n",
536                    bondings[i].bonding, bondings[i].adapter, tmp);
537           }
538         }
539         g_object_unref(proxy);
540         i++;
541       }
542     }
543
544
545   } else {  /* if (!bda) */
546
547     /* Caller supplied BT address so use it */
548     num_posdev = 1;
549     posdev = calloc(1, sizeof(bonding_t *));
550     if (!posdev) {
551       st = -1;
552       errno = ENOMEM;
553       goto OUT;
554     }
555     posdev[0].bonding = strdup(bda);
556
557     /* Adapter information is not needed */
558     posdev[0].adapter = "<not avail>";
559   }
560
561
562   /* For each bondend BT GPS device, try to create rfcomm */
563   for (i=0; i<num_posdev; i++) {
564     /* Note that bluez does not provide this interface (its defined but not
565      * yet implemented) so we use btcond for creating rfcomm device(s)
566      */
567     proxy = dbus_g_proxy_new_for_name(bus,
568         BTCOND_DBUS, BTCOND_PATH, BTCOND_INTERFACE);
569
570     error = NULL;
571     tmp = NULL;
572     if(!dbus_g_proxy_call(proxy, BTCOND_CONNECT, &error,
573           G_TYPE_STRING, posdev[i].bonding,
574           G_TYPE_STRING, spp,
575 #ifdef USE_AUTOMATIC_DISCONNECT
576                        G_TYPE_BOOLEAN, TRUE,
577 #else
578                        G_TYPE_BOOLEAN, FALSE,
579 #endif
580           G_TYPE_INVALID,
581           G_TYPE_STRING, &tmp,
582           G_TYPE_INVALID)
583         || error || !tmp || !*tmp) {
584       PDEBUG("dbus_g_proxy_call returned an error: (error=(%d,%s), tmp=%s\n",
585           error ? error->code : -1,
586           error ? error->message : "<null>",
587           tmp ? tmp : "<null>");
588
589       /* No error if already connected */
590       if (error && !strstr(error->message,
591             "com.nokia.btcond.error.connected")) {
592
593       ERROR:
594         ERRSTR("Cannot send msg (service=%s, object=%s, interface=%s, "
595                "method=%s) [%s]\n",
596                BTCOND_DBUS,
597                BTCOND_PATH,
598                BTCOND_INTERFACE,
599                BTCOND_CONNECT,
600                error->message ? error->message : "<no error msg>");
601         continue;
602
603       } else if(!tmp || !*tmp) {
604
605         /* hack: rfcommX device name is at the end of error message */
606         char *last_space = strstr(error->message, " rfcomm");
607         if (!last_space) {
608           goto ERROR;
609         }
610
611         g_free(tmp);
612         tmp = g_strdup_printf("/dev/%s", last_space+1);
613       }
614     }
615     g_object_unref(proxy);
616
617     if (tmp && tmp[0]) {
618       rfcomms = (char **)realloc(rfcomms, (num_rfcomms+1)*sizeof(char *));
619       if (!rfcomms) {
620         st = -1;
621         errno = ENOMEM;
622         goto OUT;
623       }
624       rfcomms[num_rfcomms] = tmp;
625       num_rfcomms++;
626
627       PDEBUG("BT addr=%s, RFCOMM %s now exists (adapter=%s)\n",
628               posdev[i].bonding, tmp, posdev[i].adapter);
629
630       tmp = NULL;
631     }
632     else {
633       g_free(tmp);
634     }
635   }
636
637   if (num_rfcomms==0) {
638     /* serial device creation failed */
639     ERRSTR("No rfcomm %s\n", "created");
640     st = -1;
641     errno = EINVAL;
642
643   } else {
644
645     /* Add null at the end */
646     rfcomms = (char **)realloc(rfcomms, (num_rfcomms+1)*sizeof(char *));
647     if (!rfcomms) {
648       st = -1;
649       errno = ENOMEM;
650
651     } else {
652
653       rfcomms[num_rfcomms] = NULL;
654
655 #ifndef USE_AUTOMATIC_DISCONNECT
656       ctx->rfcomms = rfcomms; /* freed in gpsbt_stop() */
657 #endif
658
659       /* Just start the beast (to be done if everything is ok) */
660       st = gpsmgr_start(gpsd_prog, rfcomms, gpsd_ctrl_sock,
661               gpsd_debug_level, port, &ctx->mgr);
662       if (!st) {
663         /* everything is ok */
664         goto OUT;
665       }
666     }
667   }
668
669  OUT:
670   if (adapters) {
671     g_strfreev(adapters);
672   }
673
674   if (posdev) {
675     for (i=0; i<num_posdev; i++) {
676       if (posdev[i].bonding) {
677         free(posdev[i].bonding);
678         memset(&posdev[i], 0, sizeof(bonding_t)); /* just in case */
679       }
680     }
681     free(posdev);
682     posdev = 0;
683   }
684
685   if (bondings) {
686     for (i=0; i<num_bondings; i++) {
687       if (bondings[i].bonding) {
688         free(bondings[i].bonding);
689         memset(&bondings[i], 0, sizeof(bonding_t)); /* just in case */
690       }
691     }
692     free(bondings);
693     bondings = 0;
694   }
695
696 #ifdef USE_AUTOMATIC_DISCONNECT
697   if (rfcomms) {
698     for (i=0; i<num_rfcomms; i++) {
699       if (rfcomms[i]) {
700         free(rfcomms[i]);
701         rfcomms[i]=0;
702       }
703     }
704     free(rfcomms);
705     rfcomms = 0;
706   }
707 #endif
708
709   if (bus) {
710     dbus_g_connection_unref(bus);
711   }
712
713   return st;
714 }
715
716
717 /* ----------------------------------------------------------------------- */
718 extern int gpsbt_stop(gpsbt_t *ctx)
719 {
720   int st;
721
722   if (!ctx) {
723     errno = EINVAL;
724     return -1;
725   }
726
727   st = gpsmgr_stop(&ctx->mgr);
728
729
730 #ifndef USE_AUTOMATIC_DISCONNECT
731   /* We need to disconnect from rfcomm device */
732   if (ctx->rfcomms) {
733     int i = 0;
734     int skip_dbus = 0;
735     DBusGConnection *bus = NULL;
736     DBusGProxy *proxy = NULL;
737     GError *error = NULL;
738
739     bus = get_dbus_gconn(&error);
740     if (!bus) {
741       errno = ECONNREFUSED; /* close enough :) */
742       PDEBUG("Cannot get reply message [%s]\n", error->message);
743       skip_dbus = 1;
744     }
745
746     if (!skip_dbus) {
747       /* Make sure there is no other user for gpsd, if there is then
748        * we must not delete rfcomm devices. The st==0 would mean that
749        * we are the only one using the dev.
750        */
751       if (st>0) {
752         skip_dbus = 1;
753         PDEBUG("Skipping rfcomm device deletion as we are "
754                 "not the only location user\n");
755       }
756     }
757
758     while (ctx->rfcomms[i]) {
759
760       if (!skip_dbus) {
761         /* Disconnect the device */
762         proxy = dbus_g_proxy_new_for_name(bus,
763             BTCOND_DBUS, BTCOND_PATH, BTCOND_INTERFACE);
764         error = NULL;
765         if(!dbus_g_proxy_call(proxy, BTCOND_DISCONNECT, &error,
766               G_TYPE_STRING, ctx->rfcomms[i], G_TYPE_INVALID, G_TYPE_INVALID)
767             || error){
768           PDEBUG("Cannot send msg (service=%s, object=%s, interface=%s, "
769            "method=%s) [%s]\n",
770                  BTCOND_DBUS,
771                  BTCOND_PATH,
772                  BTCOND_INTERFACE,
773                  BTCOND_DISCONNECT,
774                  error->message ? error->message : "<no error msg>");
775         }
776         g_object_unref(proxy);
777       }
778
779       free(ctx->rfcomms[i]);
780       ctx->rfcomms[i]=0;
781       i++;
782     }
783
784     if (bus) {
785       dbus_g_connection_unref(bus);
786     }
787
788     free(ctx->rfcomms);
789     ctx->rfcomms = 0;
790   }
791 #endif /* automatic disconnect */
792
793   return st;
794 }
795
796 #endif
797
798 #endif /* HAVE_LIBGPSBT */