2 * Copyright (C) 2006, 2007 John Costigan.
4 * POI and GPS-Info code originally written by Cezary Jackiewicz.
6 * Default map data provided by http://www.openstreetmap.org/
8 * This file is part of Maemo Mapper.
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.
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.
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/>.
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.
29 Copyright (C) 2006 Nokia Corporation. All rights reserved.
31 Author: Jukka Rissanen <jukka.rissanen@nokia.com>
33 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
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.
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.
58 #include <sys/types.h>
64 #define DBUS_API_SUBJECT_TO_CHANGE
65 #include <dbus/dbus-glib.h>
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)
75 #define USE_AUTOMATIC_DISCONNECT
77 #undef USE_AUTOMATIC_DISCONNECT
80 /* default control socket (not used by default) */
81 #define CTRL_SOCK "/tmp/.gpsd_ctrl_sock"
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.
92 #define DEFAULT_TIMEOUT (1000*10) /* 10 second timeout (in ms) */
94 #define DEFAULT_TIMEOUT (1000*60) /* 60 second timeout (in ms) */
97 #define GPS_SERVICE_CLASS_STR "positioning"
100 char *adapter; /* do not free this, it is freed somewhere else */
101 char *bonding; /* allocated from heap, you must free this */
105 /* Not all gps bt devices set the positioning bit in service class to 1,
106 * so we need a way to figure out whether the device supports positioning
107 * or not. The array contains sub-string for devices that we know to
108 * support positioning. The sub-string will be matched against device
109 * remote name that we get using dbus GetRemoteName call. The name is
110 * fetched only if we have no BT device in the system that has the
111 * positioning bit activated. Note that the listed string is case
114 static const char const *gps_device_names[] = {
115 "HOLUX GR-", /* like Holux GR-231 or similar */
116 "Nokia LD-", /* for Nokia LD-1W, note that LD-3W has positioning bit set */
117 "GPS", /* Insmat BT-GPS-3244C8 or TomTom Wireless GPS MkII */
118 "i-Blue", /* i-Blue line of GPS receivers. */
119 0 /* must be at the end */
123 /* ----------------------------------------------------------------------- */
124 static int debug_level;
127 #if (__GNUC__ > 2) && ((__GNUC__ > 3) || (__GNUC_MINOR__ > 2))
128 #define PDEBUG(fmt...) do { \
131 gettimeofday(&tv, 0); \
132 printf("DEBUG[%d]:%ld.%ld:%s:%s():%d: ", \
134 tv.tv_sec, tv.tv_usec, \
135 __FILE__, __FUNCTION__, __LINE__); \
141 #define PDEBUG(fmt...) do { \
144 gettimeofday(&tv, 0); \
145 printf("DEBUG[%d]:%ld.%ld:%s:%s():%d: ", \
147 tv.tv_sec, tv.tv_usec, \
148 __FILE__, __FUNCTION__, __LINE__); \
155 #define PDEBUG(fmt...)
159 /* ----------------------------------------------------------------------- */
160 static int check_device_name(char *name)
164 while (gps_device_names[i]) {
165 if (strstr(name, gps_device_names[i])) {
176 /* ----------------------------------------------------------------------- */
177 static inline DBusGConnection *get_dbus_gconn(GError **error)
179 DBusGConnection *conn;
181 conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, error);
186 /* ----------------------------------------------------------------------- */
187 static int set_error_msg(char *errbuf,
201 st = vsnprintf(errbuf, errbuf_max_len, msg, args);
204 /* Remove \n if it is at the end of the line (so that caller can
205 * print the string as it wishes)
207 len = strlen(errbuf);
208 if (len>0 && errbuf[len-1]=='\n')
215 /* ----------------------------------------------------------------------- */
216 extern int gpsbt_start(char *bda,
218 int gpsd_debug_level,
221 int error_buf_max_len,
225 int i, j, k, st, num_bondings = 0,
226 bonding_cnt = 0, num_classes = 0, num_rfcomms = 0,
228 GError *error = NULL;
229 DBusGConnection *bus = NULL;
230 DBusGProxy *proxy = NULL;
231 char **str_iter = NULL;
232 char **tmp_bondings = 0, **tmp_classes = 0;
235 bonding_t *bondings = 0; /* points to array of bonding_t */
236 bonding_t *posdev = 0; /* bondings with positioning bit on */
239 const char const *spp="SPP";
242 char *gpsd_ctrl_sock;
245 #if (__GNUC__ > 2) && ((__GNUC__ > 3) || (__GNUC_MINOR__ > 2))
246 #define ERRSTR(fmt, args...) \
247 if (error_buf && error_buf_max_len>0) { \
248 set_error_msg(error_buf, error_buf_max_len, fmt, args); \
253 #define ERRSTR(fmt, args...) \
254 if (error_buf && error_buf_max_len>0) { \
255 set_error_msg(error_buf, error_buf_max_len, fmt, ##args); \
257 PDEBUG(fmt, ##args); \
266 debug_level = our_debug_level;
268 /* context & error needs to be clean */
269 memset(ctx, 0, sizeof(gpsmgr_t));
272 timeout = DEFAULT_TIMEOUT;
273 else if (timeout_ms < 0)
276 timeout = timeout_ms;
278 ctx->timeout = timeout;
280 /* Caller can override the name of the gpsd program and
281 * the used control socket.
283 gpsd_prog = getenv("GPSD_PROG");
284 gpsd_ctrl_sock = getenv("GPSD_CTRL_SOCK");
290 gpsd_ctrl_sock = CTRL_SOCK;
292 /* First find out what BT devices are paired with us, then figure out
293 * which one of those are GPS devices (positioning bit in class is
294 * set). If found, create a rfcommX device for GPS BT and pass that
295 * as a parameter when starting gpsd.
298 /* But first things first, if gpsd is already running then it is
299 * no point trying to find out the GPS BT devices because some program
300 * has already figured it out.
302 st = gpsmgr_is_gpsd_running(&ctx->mgr, NULL, GPSMGR_MODE_LOCK_IF_POSSIBLE);
305 if (st!=2) { /* gpsd is running, value 2 would mean that gpsd is not
306 * running but we have a lock acquired
309 st = gpsmgr_start(gpsd_prog, NULL, gpsd_ctrl_sock,
310 gpsd_debug_level, port, &ctx->mgr);
312 /* everything is ok */
313 PDEBUG("%s already running, doing nothing\n",gpsd_prog);
317 PDEBUG("gpsmgr_start() returned %d [%s, %d]\n",st,strerror(errno),errno);
318 /* Note, no exit or return here, let the API do its magic */
323 /* Use the dbus interface to get the BT information */
326 bus = get_dbus_gconn(&error);
329 errno = ECONNREFUSED; /* close enough :) */
330 ERRSTR("%s", error->message);
331 PDEBUG("Cannot get reply message [%s]\n", error->message);
335 #define CHECK_ERROR(s,o,i,m,t) \
337 ERRSTR("Cannot send msg (service=%s, object=%s, interface=%s, " \
338 "method=%s) [%s]\n", s, o, i, m, \
339 error.message ? error.message : "<no error msg>"); \
344 /* We need BT information only if the caller does not specify
345 * the BT address. If address is defined, it is assumed that
346 * it is already bonded and we just create RFCOMM connection
350 proxy = dbus_g_proxy_new_for_name(bus,
351 BLUEZ_DBUS, MANAGER_PATH, MANAGER_INTERFACE);
354 if(!dbus_g_proxy_call(proxy, LIST_ADAPTERS, &error, G_TYPE_INVALID,
355 G_TYPE_STRV, &adapters, G_TYPE_INVALID)
356 || error || !adapters || !*adapters || !**adapters) {
357 PDEBUG("No adapters found.\n");
361 g_object_unref(proxy);
363 /* For each adapter, get bondings */
365 while (adapters[i]) {
366 proxy = dbus_g_proxy_new_for_name(bus,
367 BLUEZ_DBUS, adapters[i], ADAPTER_INTERFACE);
370 if(!dbus_g_proxy_call(proxy, LIST_BONDINGS, &error, G_TYPE_INVALID,
371 G_TYPE_STRV, &tmp_bondings, G_TYPE_INVALID)
372 || error || !tmp_bondings || !*tmp_bondings || !**tmp_bondings) {
373 PDEBUG("Call to LIST_BONDINGS failed (error=%s, tmp_bondings[0]=%s\n",
374 error ? error->message : "<null>",
375 tmp_bondings ? tmp_bondings[0] : "<null>");
377 /* Count the bondings. */
378 for(num_bondings = 0, str_iter = tmp_bondings; *str_iter;
379 str_iter++, num_bondings++);
381 /* Allocate bondings array, note that we DO allocate one extra array
382 * element for marking end of array.
384 bondings = (bonding_t *)realloc(bondings,
385 (bonding_cnt+num_bondings+1)*sizeof(bonding_t));
391 bondings[bonding_cnt+num_bondings].bonding=
392 bondings[bonding_cnt+num_bondings].adapter=NULL; /* just in case */
396 while (j<num_bondings && tmp_bondings[j]) {
397 bondings[bonding_cnt].bonding = strdup(tmp_bondings[j]);
398 free(tmp_bondings[j]);
400 bondings[bonding_cnt].adapter = adapters[i]; /* no allocation! */
402 PDEBUG("Bondings[%d]=%s (adapter=%s)\n", bonding_cnt,
403 bondings[bonding_cnt].bonding, bondings[bonding_cnt].adapter);
405 /* Get all remote service classes for this bonding. */
407 if(dbus_g_proxy_call(proxy, GET_REMOTE_SERVICE_CLASSES, &error,
408 G_TYPE_STRING, bondings[bonding_cnt].bonding, G_TYPE_INVALID,
409 G_TYPE_STRV, &tmp_classes, G_TYPE_INVALID)
410 && !error && tmp_classes && *tmp_classes && **tmp_classes) {
413 while (tmp_classes[k]) {
414 if (!strcasecmp(tmp_classes[k], GPS_SERVICE_CLASS_STR)) {
415 /* match found, this device claims to be able to provide
418 posdev = (bonding_t *)realloc(posdev,
419 (num_posdev+1)*sizeof(bonding_t));
426 posdev[num_posdev].bonding=strdup(
427 bondings[bonding_cnt].bonding);
428 posdev[num_posdev].adapter=bondings[bonding_cnt].adapter;
434 PDEBUG("Addr=%s, Class[%d]=%s (adapter=%s), "
435 "positioning bit %s\n",
436 bondings[bonding_cnt].bonding, k, tmp_classes[k],
437 bondings[bonding_cnt].adapter, onoff);
438 free(tmp_classes[k]);
455 g_object_unref(proxy);
464 PDEBUG("Bondings [%d]:\n", bonding_cnt);
465 while (bondings[i].bonding && i<bonding_cnt) {
466 PDEBUG("\t%s\n", bondings[i].bonding);
470 PDEBUG("No bondings exists.\n");
476 /* We have bonded devices but none has positioning bit set. Try
477 * to find out if any of the devices is known to be a BT device.
479 if (bonding_cnt>0 && num_posdev == 0) {
481 /* For each bonded device, get its name */
483 while (i<bonding_cnt && bondings[i].bonding) {
484 proxy = dbus_g_proxy_new_for_name(bus,
485 BLUEZ_DBUS, bondings[i].adapter, ADAPTER_INTERFACE);
489 if(dbus_g_proxy_call(proxy, GET_REMOTE_NAME, &error,
490 G_TYPE_STRING, bondings[i].bonding, G_TYPE_INVALID,
491 G_TYPE_STRING, &tmp, G_TYPE_INVALID)
492 && !error && tmp && *tmp) {
493 PDEBUG("Checking device name: %s\n", tmp);
494 if (check_device_name(tmp)) {
496 /* Found a GPS device */
497 posdev = (bonding_t *)realloc(posdev,
498 (num_posdev+1)*sizeof(bonding_t));
505 posdev[num_posdev].bonding=strdup(bondings[i].bonding);
506 posdev[num_posdev].adapter=bondings[i].adapter;
509 PDEBUG("Addr=%s, (adapter=%s), Name=\"%s\"\n",
510 bondings[i].bonding, bondings[i].adapter, tmp);
513 g_object_unref(proxy);
519 } else { /* if (!bda) */
521 /* Caller supplied BT address so use it */
523 posdev = calloc(1, sizeof(bonding_t *));
529 posdev[0].bonding = strdup(bda);
531 /* Adapter information is not needed */
532 posdev[0].adapter = "<not avail>";
536 /* For each bondend BT GPS device, try to create rfcomm */
537 for (i=0; i<num_posdev; i++) {
538 /* Note that bluez does not provide this interface (its defined but not
539 * yet implemented) so we use btcond for creating rfcomm device(s)
541 proxy = dbus_g_proxy_new_for_name(bus,
542 BTCOND_DBUS, BTCOND_PATH, BTCOND_INTERFACE);
546 if(!dbus_g_proxy_call(proxy, BTCOND_CONNECT, &error,
547 G_TYPE_STRING, posdev[i].bonding,
549 #ifdef USE_AUTOMATIC_DISCONNECT
550 G_TYPE_BOOLEAN, TRUE,
552 G_TYPE_BOOLEAN, FALSE,
557 || error || !tmp || !*tmp) {
558 PDEBUG("dbus_g_proxy_call returned an error: (error=(%d,%s), tmp=%s\n",
559 error ? error->code : -1,
560 error ? error->message : "<null>",
561 tmp ? tmp : "<null>");
563 /* No error if already connected */
564 if (error && !strstr(error->message,
565 "com.nokia.btcond.error.connected")) {
568 ERRSTR("Cannot send msg (service=%s, object=%s, interface=%s, "
574 error->message ? error->message : "<no error msg>");
577 } else if(!tmp || !*tmp) {
579 /* hack: rfcommX device name is at the end of error message */
580 char *last_space = strstr(error->message, " rfcomm");
586 tmp = g_strdup_printf("/dev/%s", last_space+1);
589 g_object_unref(proxy);
592 rfcomms = (char **)realloc(rfcomms, (num_rfcomms+1)*sizeof(char *));
598 rfcomms[num_rfcomms] = tmp;
601 PDEBUG("BT addr=%s, RFCOMM %s now exists (adapter=%s)\n",
602 posdev[i].bonding, tmp, posdev[i].adapter);
611 if (num_rfcomms==0) {
612 /* serial device creation failed */
613 ERRSTR("No rfcomm %s\n", "created");
619 /* Add null at the end */
620 rfcomms = (char **)realloc(rfcomms, (num_rfcomms+1)*sizeof(char *));
627 rfcomms[num_rfcomms] = NULL;
629 #ifndef USE_AUTOMATIC_DISCONNECT
630 ctx->rfcomms = rfcomms; /* freed in gpsbt_stop() */
633 /* Just start the beast (to be done if everything is ok) */
634 st = gpsmgr_start(gpsd_prog, rfcomms, gpsd_ctrl_sock,
635 gpsd_debug_level, port, &ctx->mgr);
637 /* everything is ok */
645 g_strfreev(adapters);
649 for (i=0; i<num_posdev; i++) {
650 if (posdev[i].bonding) {
651 free(posdev[i].bonding);
652 memset(&posdev[i], 0, sizeof(bonding_t)); /* just in case */
660 for (i=0; i<num_bondings; i++) {
661 if (bondings[i].bonding) {
662 free(bondings[i].bonding);
663 memset(&bondings[i], 0, sizeof(bonding_t)); /* just in case */
670 #ifdef USE_AUTOMATIC_DISCONNECT
672 for (i=0; i<num_rfcomms; i++) {
684 dbus_g_connection_unref(bus);
691 /* ----------------------------------------------------------------------- */
692 extern int gpsbt_stop(gpsbt_t *ctx)
701 st = gpsmgr_stop(&ctx->mgr);
704 #ifndef USE_AUTOMATIC_DISCONNECT
705 /* We need to disconnect from rfcomm device */
709 DBusGConnection *bus = NULL;
710 DBusGProxy *proxy = NULL;
711 GError *error = NULL;
713 bus = get_dbus_gconn(&error);
715 errno = ECONNREFUSED; /* close enough :) */
716 PDEBUG("Cannot get reply message [%s]\n", error->message);
721 /* Make sure there is no other user for gpsd, if there is then
722 * we must not delete rfcomm devices. The st==0 would mean that
723 * we are the only one using the dev.
727 PDEBUG("Skipping rfcomm device deletion as we are "
728 "not the only location user\n");
732 while (ctx->rfcomms[i]) {
735 /* Disconnect the device */
736 proxy = dbus_g_proxy_new_for_name(bus,
737 BTCOND_DBUS, BTCOND_PATH, BTCOND_INTERFACE);
739 if(!dbus_g_proxy_call(proxy, BTCOND_DISCONNECT, &error,
740 G_TYPE_STRING, ctx->rfcomms[i], G_TYPE_INVALID, G_TYPE_INVALID)
742 PDEBUG("Cannot send msg (service=%s, object=%s, interface=%s, "
748 error->message ? error->message : "<no error msg>");
750 g_object_unref(proxy);
753 free(ctx->rfcomms[i]);
759 dbus_g_connection_unref(bus);
765 #endif /* automatic disconnect */
772 #endif /* HAVE_LIBGPSBT */