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 /* 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
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"
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
123 #define GPS_SERVICE_CLASS_STR "positioning"
126 char *adapter; /* do not free this, it is freed somewhere else */
127 char *bonding; /* allocated from heap, you must free this */
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
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 */
149 /* ----------------------------------------------------------------------- */
150 static int debug_level;
153 #if (__GNUC__ > 2) && ((__GNUC__ > 3) || (__GNUC_MINOR__ > 2))
154 #define PDEBUG(fmt...) do { \
157 gettimeofday(&tv, 0); \
158 printf("DEBUG[%d]:%ld.%ld:%s:%s():%d: ", \
160 tv.tv_sec, tv.tv_usec, \
161 __FILE__, __FUNCTION__, __LINE__); \
167 #define PDEBUG(fmt...) do { \
170 gettimeofday(&tv, 0); \
171 printf("DEBUG[%d]:%ld.%ld:%s:%s():%d: ", \
173 tv.tv_sec, tv.tv_usec, \
174 __FILE__, __FUNCTION__, __LINE__); \
181 #define PDEBUG(fmt...)
185 /* ----------------------------------------------------------------------- */
186 static int check_device_name(char *name)
190 while (gps_device_names[i]) {
191 if (strstr(name, gps_device_names[i])) {
202 /* ----------------------------------------------------------------------- */
203 static inline DBusGConnection *get_dbus_gconn(GError **error)
205 DBusGConnection *conn;
207 conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, error);
212 /* ----------------------------------------------------------------------- */
213 static int set_error_msg(char *errbuf,
227 st = vsnprintf(errbuf, errbuf_max_len, msg, args);
230 /* Remove \n if it is at the end of the line (so that caller can
231 * print the string as it wishes)
233 len = strlen(errbuf);
234 if (len>0 && errbuf[len-1]=='\n')
241 /* ----------------------------------------------------------------------- */
242 extern int gpsbt_start(char *bda,
244 int gpsd_debug_level,
247 int error_buf_max_len,
251 int i, j, k, st, num_bondings = 0,
252 bonding_cnt = 0, num_classes = 0, num_rfcomms = 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;
261 bonding_t *bondings = 0; /* points to array of bonding_t */
262 bonding_t *posdev = 0; /* bondings with positioning bit on */
265 const char const *spp="SPP";
268 char *gpsd_ctrl_sock;
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); \
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); \
283 PDEBUG(fmt, ##args); \
292 debug_level = our_debug_level;
294 /* context & error needs to be clean */
295 memset(ctx, 0, sizeof(gpsmgr_t));
298 timeout = DEFAULT_TIMEOUT;
299 else if (timeout_ms < 0)
302 timeout = timeout_ms;
304 ctx->timeout = timeout;
306 /* Caller can override the name of the gpsd program and
307 * the used control socket.
309 gpsd_prog = getenv("GPSD_PROG");
310 gpsd_ctrl_sock = getenv("GPSD_CTRL_SOCK");
316 gpsd_ctrl_sock = CTRL_SOCK;
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.
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.
328 st = gpsmgr_is_gpsd_running(&ctx->mgr, NULL, GPSMGR_MODE_LOCK_IF_POSSIBLE);
331 if (st!=2) { /* gpsd is running, value 2 would mean that gpsd is not
332 * running but we have a lock acquired
335 st = gpsmgr_start(gpsd_prog, NULL, gpsd_ctrl_sock,
336 gpsd_debug_level, port, &ctx->mgr);
338 /* everything is ok */
339 PDEBUG("%s already running, doing nothing\n",gpsd_prog);
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 */
349 /* Use the dbus interface to get the BT information */
352 bus = get_dbus_gconn(&error);
355 errno = ECONNREFUSED; /* close enough :) */
356 ERRSTR("%s", error->message);
357 PDEBUG("Cannot get reply message [%s]\n", error->message);
361 #define CHECK_ERROR(s,o,i,m,t) \
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>"); \
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
376 proxy = dbus_g_proxy_new_for_name(bus,
377 BLUEZ_DBUS, MANAGER_PATH, MANAGER_INTERFACE);
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");
387 g_object_unref(proxy);
389 /* For each adapter, get bondings */
391 while (adapters[i]) {
392 proxy = dbus_g_proxy_new_for_name(bus,
393 BLUEZ_DBUS, adapters[i], ADAPTER_INTERFACE);
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>");
403 /* Count the bondings. */
404 for(num_bondings = 0, str_iter = tmp_bondings; *str_iter;
405 str_iter++, num_bondings++);
407 /* Allocate bondings array, note that we DO allocate one extra array
408 * element for marking end of array.
410 bondings = (bonding_t *)realloc(bondings,
411 (bonding_cnt+num_bondings+1)*sizeof(bonding_t));
417 bondings[bonding_cnt+num_bondings].bonding=
418 bondings[bonding_cnt+num_bondings].adapter=NULL; /* just in case */
422 while (j<num_bondings && tmp_bondings[j]) {
423 bondings[bonding_cnt].bonding = strdup(tmp_bondings[j]);
424 free(tmp_bondings[j]);
426 bondings[bonding_cnt].adapter = adapters[i]; /* no allocation! */
428 PDEBUG("Bondings[%d]=%s (adapter=%s)\n", bonding_cnt,
429 bondings[bonding_cnt].bonding, bondings[bonding_cnt].adapter);
431 /* Get all remote service classes for this bonding. */
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) {
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
444 posdev = (bonding_t *)realloc(posdev,
445 (num_posdev+1)*sizeof(bonding_t));
452 posdev[num_posdev].bonding=strdup(
453 bondings[bonding_cnt].bonding);
454 posdev[num_posdev].adapter=bondings[bonding_cnt].adapter;
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]);
481 g_object_unref(proxy);
490 PDEBUG("Bondings [%d]:\n", bonding_cnt);
491 while (bondings[i].bonding && i<bonding_cnt) {
492 PDEBUG("\t%s\n", bondings[i].bonding);
496 PDEBUG("No bondings exists.\n");
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.
505 if (bonding_cnt>0 && num_posdev == 0) {
507 /* For each bonded device, get its name */
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);
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)) {
522 /* Found a GPS device */
523 posdev = (bonding_t *)realloc(posdev,
524 (num_posdev+1)*sizeof(bonding_t));
531 posdev[num_posdev].bonding=strdup(bondings[i].bonding);
532 posdev[num_posdev].adapter=bondings[i].adapter;
535 PDEBUG("Addr=%s, (adapter=%s), Name=\"%s\"\n",
536 bondings[i].bonding, bondings[i].adapter, tmp);
539 g_object_unref(proxy);
545 } else { /* if (!bda) */
547 /* Caller supplied BT address so use it */
549 posdev = calloc(1, sizeof(bonding_t *));
555 posdev[0].bonding = strdup(bda);
557 /* Adapter information is not needed */
558 posdev[0].adapter = "<not avail>";
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)
567 proxy = dbus_g_proxy_new_for_name(bus,
568 BTCOND_DBUS, BTCOND_PATH, BTCOND_INTERFACE);
572 if(!dbus_g_proxy_call(proxy, BTCOND_CONNECT, &error,
573 G_TYPE_STRING, posdev[i].bonding,
575 #ifdef USE_AUTOMATIC_DISCONNECT
576 G_TYPE_BOOLEAN, TRUE,
578 G_TYPE_BOOLEAN, FALSE,
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>");
589 /* No error if already connected */
590 if (error && !strstr(error->message,
591 "com.nokia.btcond.error.connected")) {
594 ERRSTR("Cannot send msg (service=%s, object=%s, interface=%s, "
600 error->message ? error->message : "<no error msg>");
603 } else if(!tmp || !*tmp) {
605 /* hack: rfcommX device name is at the end of error message */
606 char *last_space = strstr(error->message, " rfcomm");
612 tmp = g_strdup_printf("/dev/%s", last_space+1);
615 g_object_unref(proxy);
618 rfcomms = (char **)realloc(rfcomms, (num_rfcomms+1)*sizeof(char *));
624 rfcomms[num_rfcomms] = tmp;
627 PDEBUG("BT addr=%s, RFCOMM %s now exists (adapter=%s)\n",
628 posdev[i].bonding, tmp, posdev[i].adapter);
637 if (num_rfcomms==0) {
638 /* serial device creation failed */
639 ERRSTR("No rfcomm %s\n", "created");
645 /* Add null at the end */
646 rfcomms = (char **)realloc(rfcomms, (num_rfcomms+1)*sizeof(char *));
653 rfcomms[num_rfcomms] = NULL;
655 #ifndef USE_AUTOMATIC_DISCONNECT
656 ctx->rfcomms = rfcomms; /* freed in gpsbt_stop() */
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);
663 /* everything is ok */
671 g_strfreev(adapters);
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 */
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 */
696 #ifdef USE_AUTOMATIC_DISCONNECT
698 for (i=0; i<num_rfcomms; i++) {
710 dbus_g_connection_unref(bus);
717 /* ----------------------------------------------------------------------- */
718 extern int gpsbt_stop(gpsbt_t *ctx)
727 st = gpsmgr_stop(&ctx->mgr);
730 #ifndef USE_AUTOMATIC_DISCONNECT
731 /* We need to disconnect from rfcomm device */
735 DBusGConnection *bus = NULL;
736 DBusGProxy *proxy = NULL;
737 GError *error = NULL;
739 bus = get_dbus_gconn(&error);
741 errno = ECONNREFUSED; /* close enough :) */
742 PDEBUG("Cannot get reply message [%s]\n", error->message);
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.
753 PDEBUG("Skipping rfcomm device deletion as we are "
754 "not the only location user\n");
758 while (ctx->rfcomms[i]) {
761 /* Disconnect the device */
762 proxy = dbus_g_proxy_new_for_name(bus,
763 BTCOND_DBUS, BTCOND_PATH, BTCOND_INTERFACE);
765 if(!dbus_g_proxy_call(proxy, BTCOND_DISCONNECT, &error,
766 G_TYPE_STRING, ctx->rfcomms[i], G_TYPE_INVALID, G_TYPE_INVALID)
768 PDEBUG("Cannot send msg (service=%s, object=%s, interface=%s, "
774 error->message ? error->message : "<no error msg>");
776 g_object_unref(proxy);
779 free(ctx->rfcomms[i]);
785 dbus_g_connection_unref(bus);
791 #endif /* automatic disconnect */
798 #endif /* HAVE_LIBGPSBT */