1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
/*
* This file is Copyright 2010 by the GPSD project
* SPDX-License-Identifier: BSD-2-clause
*/
#include "../include/gpsd_config.h" // must be before all includes
#if defined(DBUS_EXPORT_ENABLE)
#include <dbus/dbus.h>
#include <errno.h>
#include <libgen.h>
#include <math.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
#include "../include/gps.h"
#include "../include/libgps.h"
#include "../include/os_compat.h"
#include "../include/timespec.h"
/*
* Unpleasant that we have to declare a static context pointer here - means
* you can't have multiple DBUS sessions open (not that this matters
* much in practice). The problem is the DBUS API lacks some hook
* arguments that it ought to have.
*/
static struct gps_data_t *share_gpsdata;
static DBusConnection *connection;
static DBusHandlerResult handle_gps_fix(DBusMessage * message)
{
DBusError error;
const char *gpsd_devname = NULL;
double fix_time;
dbus_error_init(&error);
dbus_message_get_args(message,
&error,
DBUS_TYPE_DOUBLE, &fix_time,
DBUS_TYPE_INT32, &share_gpsdata->fix.mode,
DBUS_TYPE_DOUBLE, &share_gpsdata->fix.ept,
DBUS_TYPE_DOUBLE, &share_gpsdata->fix.latitude,
DBUS_TYPE_DOUBLE, &share_gpsdata->fix.longitude,
DBUS_TYPE_DOUBLE, &share_gpsdata->fix.eph,
/* The dbus doc does not seem to specify
* altHAE or altMSL */
DBUS_TYPE_DOUBLE, &share_gpsdata->fix.altHAE,
DBUS_TYPE_DOUBLE, &share_gpsdata->fix.epv,
DBUS_TYPE_DOUBLE, &share_gpsdata->fix.track,
DBUS_TYPE_DOUBLE, &share_gpsdata->fix.epd,
DBUS_TYPE_DOUBLE, &share_gpsdata->fix.speed,
DBUS_TYPE_DOUBLE, &share_gpsdata->fix.eps,
DBUS_TYPE_DOUBLE, &share_gpsdata->fix.climb,
DBUS_TYPE_DOUBLE, &share_gpsdata->fix.epc,
DBUS_TYPE_STRING, &gpsd_devname, DBUS_TYPE_INVALID);
// convert time as double back to timespec_t, potential loss of precision.
DTOTS(&share_gpsdata->fix.time, fix_time);
if (MODE_NO_FIX < share_gpsdata->fix.mode) {
share_gpsdata->fix.status = STATUS_GPS;
} else {
share_gpsdata->fix.status = STATUS_UNK;
}
dbus_error_free(&error);
PRIVATE(share_gpsdata)->handler(share_gpsdata);
return DBUS_HANDLER_RESULT_HANDLED;
}
/*
* Message dispatching function
*
*/
static DBusHandlerResult signal_handler(DBusConnection * connection UNUSED,
DBusMessage * message,
void *user_data UNUSED)
{
if (dbus_message_is_signal(message, "org.gpsd", "fix"))
return handle_gps_fix(message);
/*
* ignore all other messages
*/
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
int gps_dbus_open(struct gps_data_t *gpsdata)
{
DBusError error;
gpsdata->privdata =
(struct privdata_t *)calloc(1, sizeof(struct privdata_t));
if (NULL == gpsdata->privdata) {
return -1;
}
dbus_error_init(&error);
connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
if (dbus_error_is_set(&error)) {
syslog(LOG_CRIT, "%s: %s", error.name, error.message);
dbus_error_free(&error);
return 3;
}
dbus_bus_add_match(connection, "type='signal'", &error);
if (dbus_error_is_set(&error)) {
syslog(LOG_CRIT, "unable to add match for signals %s: %s", error.name,
error.message);
dbus_error_free(&error);
return 4;
}
if (!dbus_connection_add_filter
(connection, (DBusHandleMessageFunction) signal_handler, NULL,
NULL)) {
syslog(LOG_CRIT, "unable to register filter with the connection");
return 5;
}
#ifndef USE_QT
gpsdata->gps_fd = DBUS_PSEUDO_FD;
#else
gpsdata->gps_fd = (void *)(intptr_t)DBUS_PSEUDO_FD;
#endif // USE_QT
share_gpsdata = gpsdata;
return 0;
}
/* run a DBUS main loop with a specified handler
*
* timeout is in micro seconds
*
* Returns: -1 on timeout
* -2 on error or disconnect
* FIXME: read error should return different than timeout
*/
int gps_dbus_mainloop(struct gps_data_t *gpsdata,
int timeout,
void (*hook)(struct gps_data_t *))
{
struct timespec ts_from, ts_to;
double d_timeout;
d_timeout = (double)timeout / 1000000; // timeout in seconds
share_gpsdata = gpsdata;
PRIVATE(share_gpsdata)->handler = (void (*)(struct gps_data_t *))hook;
for (;;) {
bool status;
double diff;
if (0 != clock_gettime(CLOCK_REALTIME, &ts_from)) {
return -2;
}
status = dbus_connection_read_write_dispatch(connection,
(int)(timeout/1000));
if (FALSE == status) {
// lost connection
break;
}
if (0 != clock_gettime(CLOCK_REALTIME, &ts_to)) {
return -2;
}
diff = TS_SUB_D(&ts_to, &ts_from);
if (d_timeout <= diff) {
// timeout
return -1;
}
}
return -2;
}
#endif // defined(DBUS_EXPORT_ENABLE)
// vim: set expandtab shiftwidth=4
|