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.
56 #include <sys/types.h>
62 #define DBUS_API_SUBJECT_TO_CHANGE
63 #include <dbus/dbus-glib.h>
69 /* Set following #if to 1 if you want to use automatic bt dev disconnect.
70 * This does not seem to work correctly currently (disconnects too fast)
73 #define USE_AUTOMATIC_DISCONNECT
75 #undef USE_AUTOMATIC_DISCONNECT
78 /* default control socket (not used by default) */
79 #define CTRL_SOCK "/tmp/.gpsd_ctrl_sock"
81 /* Note that 10 second timeout should not be lowered, because
82 * then there might be GPS device connection errors if there
83 * are multiple GPS devices that the user is using.
84 * E.g., if the timeout is set to 5 secs and there are three
85 * GPS devices that the user is using, it is possible that
86 * if the 1st device fails, the other devices might also fail
87 * if the timeout is lower than 10 secs.
90 #define DEFAULT_TIMEOUT (1000*10) /* 10 second timeout (in ms) */
92 #define DEFAULT_TIMEOUT (1000*60) /* 60 second timeout (in ms) */
95 /* BT dbus service location */
96 #define BASE_PATH "/org/bluez"
97 #define BASE_INTERFACE "org.bluez"
98 #define ADAPTER_PATH BASE_PATH
99 #define ADAPTER_INTERFACE BASE_INTERFACE ".Adapter"
100 #define MANAGER_PATH BASE_PATH
101 #define MANAGER_INTERFACE BASE_INTERFACE ".Manager"
102 #define ERROR_INTERFACE BASE_INTERFACE ".Error"
103 #define SECURITY_INTERFACE BASE_INTERFACE ".Security"
104 #define RFCOMM_INTERFACE BASE_INTERFACE ".RFCOMM"
105 #define BLUEZ_DBUS BASE_INTERFACE
107 #define LIST_ADAPTERS "ListAdapters"
108 #define LIST_BONDINGS "ListBondings"
109 #define CREATE_BONDING "CreateBonding"
110 #define GET_REMOTE_NAME "GetRemoteName"
111 #define GET_REMOTE_SERVICE_CLASSES "GetRemoteServiceClasses"
113 #define BTCOND_PATH "/com/nokia/btcond/request"
114 #define BTCOND_BASE "com.nokia.btcond"
115 #define BTCOND_INTERFACE BTCOND_BASE ".request"
116 #define BTCOND_REQUEST BTCOND_INTERFACE
117 #define BTCOND_CONNECT "rfcomm_connect"
118 #define BTCOND_DISCONNECT "rfcomm_disconnect"
119 #define BTCOND_DBUS BTCOND_BASE
121 #define GPS_SERVICE_CLASS_STR "positioning"
124 char *adapter; /* do not free this, it is freed somewhere else */
125 char *bonding; /* allocated from heap, you must free this */
129 /* Not all gps bt devices set the positioning bit in service class to 1,
130 * so we need a way to figure out whether the device supports positioning
131 * or not. The array contains sub-string for devices that we know to
132 * support positioning. The sub-string will be matched against device
133 * remote name that we get using dbus GetRemoteName call. The name is
134 * fetched only if we have no BT device in the system that has the
135 * positioning bit activated. Note that the listed string is case
138 static const char const *gps_device_names[] = {
139 "HOLUX GR-", /* like Holux GR-231 or similar */
140 "Nokia LD-", /* for Nokia LD-1W, note that LD-3W has positioning bit set */
141 "GPS", /* Insmat BT-GPS-3244C8 or TomTom Wireless GPS MkII */
142 "i-Blue", /* i-Blue line of GPS receivers. */
143 0 /* must be at the end */
147 /* ----------------------------------------------------------------------- */
148 static int debug_level;
151 #if (__GNUC__ > 2) && ((__GNUC__ > 3) || (__GNUC_MINOR__ > 2))
152 #define PDEBUG(fmt...) do { \
155 gettimeofday(&tv, 0); \
156 printf("DEBUG[%d]:%ld.%ld:%s:%s():%d: ", \
158 tv.tv_sec, tv.tv_usec, \
159 __FILE__, __FUNCTION__, __LINE__); \
165 #define PDEBUG(fmt...) do { \
168 gettimeofday(&tv, 0); \
169 printf("DEBUG[%d]:%ld.%ld:%s:%s():%d: ", \
171 tv.tv_sec, tv.tv_usec, \
172 __FILE__, __FUNCTION__, __LINE__); \
179 #define PDEBUG(fmt...)
183 /* ----------------------------------------------------------------------- */
184 static int check_device_name(char *name)
188 while (gps_device_names[i]) {
189 if (strstr(name, gps_device_names[i])) {
200 /* ----------------------------------------------------------------------- */
201 static inline DBusGConnection *get_dbus_gconn(GError **error)
203 DBusGConnection *conn;
205 conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, error);
210 /* ----------------------------------------------------------------------- */
211 static int set_error_msg(char *errbuf,
225 st = vsnprintf(errbuf, errbuf_max_len, msg, args);
228 /* Remove \n if it is at the end of the line (so that caller can
229 * print the string as it wishes)
231 len = strlen(errbuf);
232 if (len>0 && errbuf[len-1]=='\n')
239 /* ----------------------------------------------------------------------- */
240 extern int gpsbt_start(char *bda,
242 int gpsd_debug_level,
245 int error_buf_max_len,
249 int i, j, k, st, num_bondings = 0,
250 bonding_cnt = 0, num_classes = 0, num_rfcomms = 0,
252 GError *error = NULL;
253 DBusGConnection *bus = NULL;
254 DBusGProxy *proxy = NULL;
255 char **str_iter = NULL;
256 char **tmp_bondings = 0, **tmp_classes = 0;
259 bonding_t *bondings = 0; /* points to array of bonding_t */
260 bonding_t *posdev = 0; /* bondings with positioning bit on */
263 const char const *spp="SPP";
266 char *gpsd_ctrl_sock;
269 #if (__GNUC__ > 2) && ((__GNUC__ > 3) || (__GNUC_MINOR__ > 2))
270 #define ERRSTR(fmt, args...) \
271 if (error_buf && error_buf_max_len>0) { \
272 set_error_msg(error_buf, error_buf_max_len, fmt, args); \
277 #define ERRSTR(fmt, args...) \
278 if (error_buf && error_buf_max_len>0) { \
279 set_error_msg(error_buf, error_buf_max_len, fmt, ##args); \
281 PDEBUG(fmt, ##args); \
290 debug_level = our_debug_level;
292 /* context & error needs to be clean */
293 memset(ctx, 0, sizeof(gpsmgr_t));
296 timeout = DEFAULT_TIMEOUT;
297 else if (timeout_ms < 0)
300 timeout = timeout_ms;
302 ctx->timeout = timeout;
304 /* Caller can override the name of the gpsd program and
305 * the used control socket.
307 gpsd_prog = getenv("GPSD_PROG");
308 gpsd_ctrl_sock = getenv("GPSD_CTRL_SOCK");
314 gpsd_ctrl_sock = CTRL_SOCK;
316 /* First find out what BT devices are paired with us, then figure out
317 * which one of those are GPS devices (positioning bit in class is
318 * set). If found, create a rfcommX device for GPS BT and pass that
319 * as a parameter when starting gpsd.
322 /* But first things first, if gpsd is already running then it is
323 * no point trying to find out the GPS BT devices because some program
324 * has already figured it out.
326 st = gpsmgr_is_gpsd_running(&ctx->mgr, NULL, GPSMGR_MODE_LOCK_IF_POSSIBLE);
329 if (st!=2) { /* gpsd is running, value 2 would mean that gpsd is not
330 * running but we have a lock acquired
333 st = gpsmgr_start(gpsd_prog, NULL, gpsd_ctrl_sock,
334 gpsd_debug_level, port, &ctx->mgr);
336 /* everything is ok */
337 PDEBUG("%s already running, doing nothing\n",gpsd_prog);
341 PDEBUG("gpsmgr_start() returned %d [%s, %d]\n",st,strerror(errno),errno);
342 /* Note, no exit or return here, let the API do its magic */
347 /* Use the dbus interface to get the BT information */
350 bus = get_dbus_gconn(&error);
353 errno = ECONNREFUSED; /* close enough :) */
354 ERRSTR("%s", error->message);
355 PDEBUG("Cannot get reply message [%s]\n", error->message);
359 #define CHECK_ERROR(s,o,i,m,t) \
361 ERRSTR("Cannot send msg (service=%s, object=%s, interface=%s, " \
362 "method=%s) [%s]\n", s, o, i, m, \
363 error.message ? error.message : "<no error msg>"); \
368 /* We need BT information only if the caller does not specify
369 * the BT address. If address is defined, it is assumed that
370 * it is already bonded and we just create RFCOMM connection
374 proxy = dbus_g_proxy_new_for_name(bus,
375 BLUEZ_DBUS, MANAGER_PATH, MANAGER_INTERFACE);
378 if(!dbus_g_proxy_call(proxy, LIST_ADAPTERS, &error, G_TYPE_INVALID,
379 G_TYPE_STRV, &adapters, G_TYPE_INVALID)
380 || error || !adapters || !*adapters || !**adapters) {
381 PDEBUG("No adapters found.\n");
385 g_object_unref(proxy);
387 /* For each adapter, get bondings */
389 while (adapters[i]) {
390 proxy = dbus_g_proxy_new_for_name(bus,
391 BLUEZ_DBUS, adapters[i], ADAPTER_INTERFACE);
394 if(!dbus_g_proxy_call(proxy, LIST_BONDINGS, &error, G_TYPE_INVALID,
395 G_TYPE_STRV, &tmp_bondings, G_TYPE_INVALID)
396 || error || !tmp_bondings || !*tmp_bondings || !**tmp_bondings) {
397 PDEBUG("Call to LIST_BONDINGS failed (error=%s, tmp_bondings[0]=%s\n",
398 error ? error->message : "<null>",
399 tmp_bondings ? tmp_bondings[0] : "<null>");
401 /* Count the bondings. */
402 for(num_bondings = 0, str_iter = tmp_bondings; *str_iter;
403 str_iter++, num_bondings++);
405 /* Allocate bondings array, note that we DO allocate one extra array
406 * element for marking end of array.
408 bondings = (bonding_t *)realloc(bondings,
409 (bonding_cnt+num_bondings+1)*sizeof(bonding_t));
415 bondings[bonding_cnt+num_bondings].bonding=
416 bondings[bonding_cnt+num_bondings].adapter=NULL; /* just in case */
420 while (j<num_bondings && tmp_bondings[j]) {
421 bondings[bonding_cnt].bonding = strdup(tmp_bondings[j]);
422 free(tmp_bondings[j]);
424 bondings[bonding_cnt].adapter = adapters[i]; /* no allocation! */
426 PDEBUG("Bondings[%d]=%s (adapter=%s)\n", bonding_cnt,
427 bondings[bonding_cnt].bonding, bondings[bonding_cnt].adapter);
429 /* Get all remote service classes for this bonding. */
431 if(dbus_g_proxy_call(proxy, GET_REMOTE_SERVICE_CLASSES, &error,
432 G_TYPE_STRING, bondings[bonding_cnt].bonding, G_TYPE_INVALID,
433 G_TYPE_STRV, &tmp_classes, G_TYPE_INVALID)
434 && !error && tmp_classes && *tmp_classes && **tmp_classes) {
437 while (tmp_classes[k]) {
438 if (!strcasecmp(tmp_classes[k], GPS_SERVICE_CLASS_STR)) {
439 /* match found, this device claims to be able to provide
442 posdev = (bonding_t *)realloc(posdev,
443 (num_posdev+1)*sizeof(bonding_t));
450 posdev[num_posdev].bonding=strdup(
451 bondings[bonding_cnt].bonding);
452 posdev[num_posdev].adapter=bondings[bonding_cnt].adapter;
458 PDEBUG("Addr=%s, Class[%d]=%s (adapter=%s), "
459 "positioning bit %s\n",
460 bondings[bonding_cnt].bonding, k, tmp_classes[k],
461 bondings[bonding_cnt].adapter, onoff);
462 free(tmp_classes[k]);
479 g_object_unref(proxy);
488 PDEBUG("Bondings [%d]:\n", bonding_cnt);
489 while (bondings[i].bonding && i<bonding_cnt) {
490 PDEBUG("\t%s\n", bondings[i].bonding);
494 PDEBUG("No bondings exists.\n");
500 /* We have bonded devices but none has positioning bit set. Try
501 * to find out if any of the devices is known to be a BT device.
503 if (bonding_cnt>0 && num_posdev == 0) {
505 /* For each bonded device, get its name */
507 while (i<bonding_cnt && bondings[i].bonding) {
508 proxy = dbus_g_proxy_new_for_name(bus,
509 BLUEZ_DBUS, bondings[i].adapter, ADAPTER_INTERFACE);
513 if(dbus_g_proxy_call(proxy, GET_REMOTE_NAME, &error,
514 G_TYPE_STRING, bondings[i].bonding, G_TYPE_INVALID,
515 G_TYPE_STRING, &tmp, G_TYPE_INVALID)
516 && !error && tmp && *tmp) {
517 PDEBUG("Checking device name: %s\n", tmp);
518 if (check_device_name(tmp)) {
520 /* Found a GPS device */
521 posdev = (bonding_t *)realloc(posdev,
522 (num_posdev+1)*sizeof(bonding_t));
529 posdev[num_posdev].bonding=strdup(bondings[i].bonding);
530 posdev[num_posdev].adapter=bondings[i].adapter;
533 PDEBUG("Addr=%s, (adapter=%s), Name=\"%s\"\n",
534 bondings[i].bonding, bondings[i].adapter, tmp);
537 g_object_unref(proxy);
543 } else { /* if (!bda) */
545 /* Caller supplied BT address so use it */
547 posdev = calloc(1, sizeof(bonding_t *));
553 posdev[0].bonding = strdup(bda);
555 /* Adapter information is not needed */
556 posdev[0].adapter = "<not avail>";
560 /* For each bondend BT GPS device, try to create rfcomm */
561 for (i=0; i<num_posdev; i++) {
562 /* Note that bluez does not provide this interface (its defined but not
563 * yet implemented) so we use btcond for creating rfcomm device(s)
565 proxy = dbus_g_proxy_new_for_name(bus,
566 BTCOND_DBUS, BTCOND_PATH, BTCOND_INTERFACE);
570 if(!dbus_g_proxy_call(proxy, BTCOND_CONNECT, &error,
571 G_TYPE_STRING, posdev[i].bonding,
573 #ifdef USE_AUTOMATIC_DISCONNECT
574 G_TYPE_BOOLEAN, TRUE,
576 G_TYPE_BOOLEAN, FALSE,
581 || error || !tmp || !*tmp) {
582 PDEBUG("dbus_g_proxy_call returned an error: (error=(%d,%s), tmp=%s\n",
583 error ? error->code : -1,
584 error ? error->message : "<null>",
585 tmp ? tmp : "<null>");
587 /* No error if already connected */
588 if (error && !strstr(error->message,
589 "com.nokia.btcond.error.connected")) {
592 ERRSTR("Cannot send msg (service=%s, object=%s, interface=%s, "
598 error->message ? error->message : "<no error msg>");
601 } else if(!tmp || !*tmp) {
603 /* hack: rfcommX device name is at the end of error message */
604 char *last_space = strstr(error->message, " rfcomm");
610 tmp = g_strdup_printf("/dev/%s", last_space+1);
613 g_object_unref(proxy);
616 rfcomms = (char **)realloc(rfcomms, (num_rfcomms+1)*sizeof(char *));
622 rfcomms[num_rfcomms] = tmp;
625 PDEBUG("BT addr=%s, RFCOMM %s now exists (adapter=%s)\n",
626 posdev[i].bonding, tmp, posdev[i].adapter);
635 if (num_rfcomms==0) {
636 /* serial device creation failed */
637 ERRSTR("No rfcomm %s\n", "created");
643 /* Add null at the end */
644 rfcomms = (char **)realloc(rfcomms, (num_rfcomms+1)*sizeof(char *));
651 rfcomms[num_rfcomms] = NULL;
653 #ifndef USE_AUTOMATIC_DISCONNECT
654 ctx->rfcomms = rfcomms; /* freed in gpsbt_stop() */
657 /* Just start the beast (to be done if everything is ok) */
658 st = gpsmgr_start(gpsd_prog, rfcomms, gpsd_ctrl_sock,
659 gpsd_debug_level, port, &ctx->mgr);
661 /* everything is ok */
669 g_strfreev(adapters);
673 for (i=0; i<num_posdev; i++) {
674 if (posdev[i].bonding) {
675 free(posdev[i].bonding);
676 memset(&posdev[i], 0, sizeof(bonding_t)); /* just in case */
684 for (i=0; i<num_bondings; i++) {
685 if (bondings[i].bonding) {
686 free(bondings[i].bonding);
687 memset(&bondings[i], 0, sizeof(bonding_t)); /* just in case */
694 #ifdef USE_AUTOMATIC_DISCONNECT
696 for (i=0; i<num_rfcomms; i++) {
708 dbus_g_connection_unref(bus);
715 /* ----------------------------------------------------------------------- */
716 extern int gpsbt_stop(gpsbt_t *ctx)
725 st = gpsmgr_stop(&ctx->mgr);
728 #ifndef USE_AUTOMATIC_DISCONNECT
729 /* We need to disconnect from rfcomm device */
733 DBusGConnection *bus = NULL;
734 DBusGProxy *proxy = NULL;
735 GError *error = NULL;
737 bus = get_dbus_gconn(&error);
739 errno = ECONNREFUSED; /* close enough :) */
740 PDEBUG("Cannot get reply message [%s]\n", error->message);
745 /* Make sure there is no other user for gpsd, if there is then
746 * we must not delete rfcomm devices. The st==0 would mean that
747 * we are the only one using the dev.
751 PDEBUG("Skipping rfcomm device deletion as we are "
752 "not the only location user\n");
756 while (ctx->rfcomms[i]) {
759 /* Disconnect the device */
760 proxy = dbus_g_proxy_new_for_name(bus,
761 BTCOND_DBUS, BTCOND_PATH, BTCOND_INTERFACE);
763 if(!dbus_g_proxy_call(proxy, BTCOND_DISCONNECT, &error,
764 G_TYPE_STRING, ctx->rfcomms[i], G_TYPE_INVALID, G_TYPE_INVALID)
766 PDEBUG("Cannot send msg (service=%s, object=%s, interface=%s, "
772 error->message ? error->message : "<no error msg>");
774 g_object_unref(proxy);
777 free(ctx->rfcomms[i]);
783 dbus_g_connection_unref(bus);
789 #endif /* automatic disconnect */