Files
iio-sensor-proxy/src/iio-sensor-proxy.c
Bastien Nocera 716ee37d51 main: Prefer buffer driver to polling driver
In c7fb43e0, we added a buffer driver for ambient light sensors, but we
added it to the list of drivers in a way that would favour polling
drivers. Since we started using IIO_SENSOR_PROXY_TYPE to tag devices,
and as those can support multiple drivers, we need to prefer the buffer
driver as it's the one that doesn't require constant monitoring.

This also fixes operations on some Dell laptops where the polling driver
is completely broken.

See https://gitlab.freedesktop.org/hadess/iio-sensor-proxy/-/merge_requests/352
2022-03-15 10:57:26 +01:00

1023 lines
28 KiB
C

/*
* Copyright (c) 2014-2016 Bastien Nocera <hadess@hadess.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published by
* the Free Software Foundation.
*
*/
#include "config.h"
#include <locale.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <gio/gio.h>
#include <gudev/gudev.h>
#include <polkit/polkit.h>
#include "drivers.h"
#include "orientation.h"
#include "iio-sensor-proxy-resources.h"
#define SENSOR_PROXY_DBUS_NAME "net.hadess.SensorProxy"
#define SENSOR_PROXY_DBUS_PATH "/net/hadess/SensorProxy"
#define SENSOR_PROXY_COMPASS_DBUS_PATH "/net/hadess/SensorProxy/Compass"
#define SENSOR_PROXY_IFACE_NAME SENSOR_PROXY_DBUS_NAME
#define SENSOR_PROXY_COMPASS_IFACE_NAME SENSOR_PROXY_DBUS_NAME ".Compass"
#define NUM_SENSOR_TYPES DRIVER_TYPE_PROXIMITY + 1
typedef struct {
GMainLoop *loop;
GUdevClient *client;
GDBusNodeInfo *introspection_data;
GDBusConnection *connection;
guint name_id;
int ret;
PolkitAuthority *auth;
SensorDriver *drivers[NUM_SENSOR_TYPES];
SensorDevice *devices[NUM_SENSOR_TYPES];
GUdevDevice *udev_devices[NUM_SENSOR_TYPES];
GHashTable *clients[NUM_SENSOR_TYPES]; /* key = D-Bus name, value = watch ID */
/* Accelerometer */
OrientationUp previous_orientation;
/* Light */
gdouble previous_level;
gboolean uses_lux;
/* Compass */
gdouble previous_heading;
/* Proximity */
gboolean previous_prox_near;
} SensorData;
static const SensorDriver * const drivers[] = {
&iio_buffer_accel,
&iio_poll_accel,
&input_accel,
&iio_buffer_light,
&iio_poll_light,
&hwmon_light,
&fake_compass,
&fake_light,
&iio_buffer_compass,
&iio_poll_proximity,
};
static ReadingsUpdateFunc driver_type_to_callback_func (DriverType type);
static const char *
driver_type_to_str (DriverType type)
{
switch (type) {
case DRIVER_TYPE_ACCEL:
return "accelerometer";
case DRIVER_TYPE_LIGHT:
return "ambient light sensor";
case DRIVER_TYPE_COMPASS:
return "compass";
case DRIVER_TYPE_PROXIMITY:
return "proximity";
default:
g_assert_not_reached ();
}
}
#define DRIVER_FOR_TYPE(driver_type) data->drivers[driver_type]
#define DEVICE_FOR_TYPE(driver_type) data->devices[driver_type]
#define UDEV_DEVICE_FOR_TYPE(driver_type) data->udev_devices[driver_type]
static void sensor_changes (GUdevClient *client,
gchar *action,
GUdevDevice *device,
SensorData *data);
static gboolean
driver_type_exists (SensorData *data,
DriverType driver_type)
{
return (DRIVER_FOR_TYPE(driver_type) != NULL);
}
static gboolean
find_sensors (GUdevClient *client,
SensorData *data)
{
GList *devices, *input, *platform, *l;
gboolean found = FALSE;
devices = g_udev_client_query_by_subsystem (client, "iio");
input = g_udev_client_query_by_subsystem (client, "input");
platform = g_udev_client_query_by_subsystem (client, "platform");
devices = g_list_concat (devices, input);
devices = g_list_concat (devices, platform);
/* Find the devices */
for (l = devices; l != NULL; l = l->next) {
GUdevDevice *dev = l->data;
guint i;
for (i = 0; i < G_N_ELEMENTS(drivers); i++) {
SensorDriver *driver = (SensorDriver *) drivers[i];
if (!driver_type_exists (data, driver->type) &&
driver_discover (driver, dev)) {
g_debug ("Found device %s of type %s at %s",
g_udev_device_get_sysfs_path (dev),
driver_type_to_str (driver->type),
driver->driver_name);
UDEV_DEVICE_FOR_TYPE(driver->type) = g_object_ref (dev);
DRIVER_FOR_TYPE(driver->type) = (SensorDriver *) driver;
found = TRUE;
}
}
if (driver_type_exists (data, DRIVER_TYPE_ACCEL) &&
driver_type_exists (data, DRIVER_TYPE_LIGHT) &&
driver_type_exists (data, DRIVER_TYPE_PROXIMITY) &&
driver_type_exists (data, DRIVER_TYPE_COMPASS))
break;
}
g_list_free_full (devices, g_object_unref);
return found;
}
static void
free_client_watch (gpointer data)
{
guint watch_id = GPOINTER_TO_UINT (data);
if (watch_id == 0)
return;
g_bus_unwatch_name (watch_id);
}
static GHashTable *
create_clients_hash_table (void)
{
return g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, free_client_watch);
}
typedef enum {
PROP_HAS_ACCELEROMETER = 1 << 0,
PROP_ACCELEROMETER_ORIENTATION = 1 << 1,
PROP_HAS_AMBIENT_LIGHT = 1 << 2,
PROP_LIGHT_LEVEL = 1 << 3,
PROP_HAS_COMPASS = 1 << 4,
PROP_COMPASS_HEADING = 1 << 5,
PROP_HAS_PROXIMITY = 1 << 6,
PROP_PROXIMITY_NEAR = 1 << 7,
} PropertiesMask;
#define PROP_ALL (PROP_HAS_ACCELEROMETER | \
PROP_ACCELEROMETER_ORIENTATION | \
PROP_HAS_AMBIENT_LIGHT | \
PROP_LIGHT_LEVEL | \
PROP_HAS_PROXIMITY | \
PROP_PROXIMITY_NEAR)
#define PROP_ALL_COMPASS (PROP_HAS_COMPASS | \
PROP_COMPASS_HEADING)
static PropertiesMask
mask_for_sensor_type (DriverType sensor_type)
{
switch (sensor_type) {
case DRIVER_TYPE_ACCEL:
return PROP_HAS_ACCELEROMETER |
PROP_ACCELEROMETER_ORIENTATION;
case DRIVER_TYPE_LIGHT:
return PROP_HAS_AMBIENT_LIGHT |
PROP_LIGHT_LEVEL;
case DRIVER_TYPE_COMPASS:
return PROP_HAS_COMPASS |
PROP_COMPASS_HEADING;
case DRIVER_TYPE_PROXIMITY:
return PROP_HAS_PROXIMITY |
PROP_PROXIMITY_NEAR;
default:
g_assert_not_reached ();
}
}
static void
send_dbus_event_for_client (SensorData *data,
const char *destination_bus_name,
PropertiesMask mask)
{
GVariantBuilder props_builder;
GVariant *props_changed = NULL;
g_return_if_fail (destination_bus_name != NULL);
g_variant_builder_init (&props_builder, G_VARIANT_TYPE ("a{sv}"));
if (mask & PROP_HAS_ACCELEROMETER) {
gboolean has_accel;
has_accel = driver_type_exists (data, DRIVER_TYPE_ACCEL);
g_variant_builder_add (&props_builder, "{sv}", "HasAccelerometer",
g_variant_new_boolean (has_accel));
/* Send the orientation when the device appears */
if (has_accel)
mask |= PROP_ACCELEROMETER_ORIENTATION;
else
data->previous_orientation = ORIENTATION_UNDEFINED;
}
if (mask & PROP_ACCELEROMETER_ORIENTATION) {
g_variant_builder_add (&props_builder, "{sv}", "AccelerometerOrientation",
g_variant_new_string (orientation_to_string (data->previous_orientation)));
}
if (mask & PROP_HAS_AMBIENT_LIGHT) {
gboolean has_als;
has_als = driver_type_exists (data, DRIVER_TYPE_LIGHT);
g_variant_builder_add (&props_builder, "{sv}", "HasAmbientLight",
g_variant_new_boolean (has_als));
/* Send the light level when the device appears */
if (has_als)
mask |= PROP_LIGHT_LEVEL;
}
if (mask & PROP_LIGHT_LEVEL) {
g_variant_builder_add (&props_builder, "{sv}", "LightLevelUnit",
g_variant_new_string (data->uses_lux ? "lux" : "vendor"));
g_variant_builder_add (&props_builder, "{sv}", "LightLevel",
g_variant_new_double (data->previous_level));
}
if (mask & PROP_HAS_COMPASS) {
gboolean has_compass;
has_compass = driver_type_exists (data, DRIVER_TYPE_COMPASS);
g_variant_builder_add (&props_builder, "{sv}", "HasCompass",
g_variant_new_boolean (has_compass));
/* Send the heading when the device appears */
if (has_compass)
mask |= PROP_COMPASS_HEADING;
}
if (mask & PROP_COMPASS_HEADING) {
g_variant_builder_add (&props_builder, "{sv}", "CompassHeading",
g_variant_new_double (data->previous_heading));
}
if (mask & PROP_HAS_PROXIMITY) {
gboolean has_proximity;
has_proximity = driver_type_exists (data, DRIVER_TYPE_PROXIMITY);
g_variant_builder_add (&props_builder, "{sv}", "HasProximity",
g_variant_new_boolean (has_proximity));
/* Send proximity information when the device appears */
if (has_proximity)
mask |= PROP_PROXIMITY_NEAR;
}
if (mask & PROP_PROXIMITY_NEAR) {
g_variant_builder_add (&props_builder, "{sv}", "ProximityNear",
g_variant_new_boolean (data->previous_prox_near));
}
props_changed = g_variant_new ("(s@a{sv}@as)", (mask & PROP_ALL) ? SENSOR_PROXY_IFACE_NAME : SENSOR_PROXY_COMPASS_IFACE_NAME,
g_variant_builder_end (&props_builder),
g_variant_new_strv (NULL, 0));
g_dbus_connection_emit_signal (data->connection,
destination_bus_name,
(mask & PROP_ALL) ? SENSOR_PROXY_DBUS_PATH : SENSOR_PROXY_COMPASS_DBUS_PATH,
"org.freedesktop.DBus.Properties",
"PropertiesChanged",
props_changed, NULL);
}
static void
send_dbus_event (SensorData *data,
PropertiesMask mask)
{
GHashTable *ht;
guint i;
GHashTableIter iter;
gpointer key, value;
g_assert (mask != 0);
g_assert (data->connection);
g_assert ((mask & PROP_ALL) == 0 || (mask & PROP_ALL_COMPASS) == 0);
/* Make a list of the events each client for each sensor
* is interested in */
ht = g_hash_table_new (g_str_hash, g_str_equal);
for (i = 0; i < NUM_SENSOR_TYPES; i++) {
GList *clients, *l;
clients = g_hash_table_get_keys (data->clients[i]);
for (l = clients; l != NULL; l = l->next) {
PropertiesMask m, new_mask;
/* Already have a mask? */
m = GPOINTER_TO_UINT (g_hash_table_lookup (ht, l->data));
new_mask = mask & mask_for_sensor_type (i);
m |= new_mask;
g_hash_table_insert (ht, l->data, GUINT_TO_POINTER (m));
}
}
g_hash_table_iter_init (&iter, ht);
while (g_hash_table_iter_next (&iter, &key, &value))
send_dbus_event_for_client (data, (const char *) key, GPOINTER_TO_UINT (value));
g_hash_table_destroy (ht);
}
static void
send_driver_changed_dbus_event (SensorData *data,
DriverType driver_type)
{
if (driver_type == DRIVER_TYPE_ACCEL)
send_dbus_event (data, PROP_HAS_ACCELEROMETER);
else if (driver_type == DRIVER_TYPE_LIGHT)
send_dbus_event (data, PROP_HAS_AMBIENT_LIGHT);
else if (driver_type == DRIVER_TYPE_PROXIMITY)
send_dbus_event (data, PROP_HAS_PROXIMITY);
else if (driver_type == DRIVER_TYPE_COMPASS)
send_dbus_event (data, PROP_HAS_COMPASS);
else
g_assert_not_reached ();
}
static gboolean
any_sensors_left (SensorData *data)
{
guint i;
gboolean exists = FALSE;
for (i = 0; i < NUM_SENSOR_TYPES; i++) {
if (driver_type_exists (data, i)) {
exists = TRUE;
break;
}
}
return exists;
}
static void
client_release (SensorData *data,
const char *sender,
DriverType driver_type)
{
GHashTable *ht;
guint watch_id;
ht = data->clients[driver_type];
watch_id = GPOINTER_TO_UINT (g_hash_table_lookup (ht, sender));
if (watch_id == 0) {
g_debug ("Sender '%s' already released device, no-op", sender);
return;
}
g_hash_table_remove (ht, sender);
if (driver_type_exists (data, driver_type) &&
g_hash_table_size (ht) == 0) {
SensorDevice *sensor_device = DEVICE_FOR_TYPE(driver_type);
driver_set_polling (sensor_device, FALSE);
}
}
static void
client_vanished_cb (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
SensorData *data = user_data;
guint i;
char *sender;
if (name == NULL)
return;
sender = g_strdup (name);
for (i = 0; i < NUM_SENSOR_TYPES; i++) {
GHashTable *ht;
guint watch_id;
ht = data->clients[i];
g_assert (ht);
watch_id = GPOINTER_TO_UINT (g_hash_table_lookup (ht, sender));
if (watch_id > 0)
client_release (data, sender, i);
}
g_free (sender);
}
static gboolean
check_claim_permission (SensorData *data,
const char *sender,
GError **error)
{
g_autoptr(GError) local_error = NULL;
g_autoptr(PolkitAuthorizationResult) result = NULL;
g_autoptr(PolkitSubject) subject = NULL;
subject = polkit_system_bus_name_new (sender);
result = polkit_authority_check_authorization_sync (data->auth,
subject,
"net.hadess.SensorProxy.claim-sensor",
NULL,
POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE,
NULL, &local_error);
if (result == NULL ||
!polkit_authorization_result_get_is_authorized (result))
{
g_set_error (error, G_DBUS_ERROR,
G_DBUS_ERROR_ACCESS_DENIED,
"Not Authorized: %s", local_error ? local_error->message : "Sensor claim not allowed");
return FALSE;
}
return TRUE;
}
static void
handle_generic_method_call (SensorData *data,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
DriverType driver_type)
{
GHashTable *ht;
guint watch_id;
g_debug ("Handling driver refcounting method '%s' for %s device",
method_name, driver_type_to_str (driver_type));
ht = data->clients[driver_type];
if (g_str_has_prefix (method_name, "Claim")) {
watch_id = GPOINTER_TO_UINT (g_hash_table_lookup (ht, sender));
if (watch_id > 0) {
g_debug ("Sender '%s' already claimed device, no-op", sender);
g_dbus_method_invocation_return_value (invocation, NULL);
return;
}
/* No other clients for this sensor? Start it */
if (driver_type_exists (data, driver_type) &&
g_hash_table_size (ht) == 0) {
SensorDevice *sensor_device = DEVICE_FOR_TYPE(driver_type);
driver_set_polling (sensor_device, TRUE);
}
watch_id = g_bus_watch_name_on_connection (data->connection,
sender,
G_BUS_NAME_WATCHER_FLAGS_NONE,
NULL,
client_vanished_cb,
data,
NULL);
g_hash_table_insert (ht, g_strdup (sender), GUINT_TO_POINTER (watch_id));
g_dbus_method_invocation_return_value (invocation, NULL);
} else if (g_str_has_prefix (method_name, "Release")) {
client_release (data, sender, driver_type);
g_dbus_method_invocation_return_value (invocation, NULL);
}
}
static void
handle_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
SensorData *data = user_data;
DriverType driver_type;
g_autoptr(GError) error = NULL;
if (g_strcmp0 (method_name, "ClaimAccelerometer") == 0 ||
g_strcmp0 (method_name, "ReleaseAccelerometer") == 0)
driver_type = DRIVER_TYPE_ACCEL;
else if (g_strcmp0 (method_name, "ClaimLight") == 0 ||
g_strcmp0 (method_name, "ReleaseLight") == 0)
driver_type = DRIVER_TYPE_LIGHT;
else if (g_strcmp0 (method_name, "ClaimProximity") == 0 ||
g_strcmp0 (method_name, "ReleaseProximity") == 0)
driver_type = DRIVER_TYPE_PROXIMITY;
else {
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_UNKNOWN_METHOD,
"Method '%s' does not exist on object %s",
method_name, object_path);
return;
}
if (!check_claim_permission (data, sender, &error)) {
g_dbus_method_invocation_return_gerror (invocation, error);
return;
}
handle_generic_method_call (data, sender, object_path,
interface_name, method_name,
parameters, invocation, driver_type);
}
static GVariant *
handle_get_property (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GError **error,
gpointer user_data)
{
SensorData *data = user_data;
g_assert (data->connection);
if (g_strcmp0 (property_name, "HasAccelerometer") == 0)
return g_variant_new_boolean (driver_type_exists (data, DRIVER_TYPE_ACCEL));
if (g_strcmp0 (property_name, "AccelerometerOrientation") == 0)
return g_variant_new_string (orientation_to_string (data->previous_orientation));
if (g_strcmp0 (property_name, "HasAmbientLight") == 0)
return g_variant_new_boolean (driver_type_exists (data, DRIVER_TYPE_LIGHT));
if (g_strcmp0 (property_name, "LightLevelUnit") == 0)
return g_variant_new_string (data->uses_lux ? "lux" : "vendor");
if (g_strcmp0 (property_name, "LightLevel") == 0)
return g_variant_new_double (data->previous_level);
if (g_strcmp0 (property_name, "HasProximity") == 0)
return g_variant_new_boolean (driver_type_exists (data, DRIVER_TYPE_PROXIMITY));
if (g_strcmp0 (property_name, "ProximityNear") == 0)
return g_variant_new_boolean (data->previous_prox_near);
return NULL;
}
static const GDBusInterfaceVTable interface_vtable =
{
handle_method_call,
handle_get_property,
NULL
};
static void
handle_compass_method_call (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *method_name,
GVariant *parameters,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
SensorData *data = user_data;
if (g_strcmp0 (method_name, "ClaimCompass") != 0 &&
g_strcmp0 (method_name, "ReleaseCompass") != 0) {
g_dbus_method_invocation_return_error (invocation,
G_DBUS_ERROR,
G_DBUS_ERROR_UNKNOWN_METHOD,
"Method '%s' does not exist on object %s",
method_name, object_path);
return;
}
handle_generic_method_call (data, sender, object_path,
interface_name, method_name,
parameters, invocation, DRIVER_TYPE_COMPASS);
}
static GVariant *
handle_compass_get_property (GDBusConnection *connection,
const gchar *sender,
const gchar *object_path,
const gchar *interface_name,
const gchar *property_name,
GError **error,
gpointer user_data)
{
SensorData *data = user_data;
g_assert (data->connection);
if (g_strcmp0 (property_name, "HasCompass") == 0)
return g_variant_new_boolean (data->drivers[DRIVER_TYPE_COMPASS] != NULL);
if (g_strcmp0 (property_name, "CompassHeading") == 0)
return g_variant_new_double (data->previous_heading);
return NULL;
}
static const GDBusInterfaceVTable compass_interface_vtable =
{
handle_compass_method_call,
handle_compass_get_property,
NULL
};
static void
name_lost_handler (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
g_debug ("iio-sensor-proxy is already running, or it cannot own its D-Bus name. Verify installation.");
exit (0);
}
static void
bus_acquired_handler (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
SensorData *data = user_data;
g_dbus_connection_register_object (connection,
SENSOR_PROXY_DBUS_PATH,
data->introspection_data->interfaces[0],
&interface_vtable,
data,
NULL,
NULL);
g_dbus_connection_register_object (connection,
SENSOR_PROXY_COMPASS_DBUS_PATH,
data->introspection_data->interfaces[1],
&compass_interface_vtable,
data,
NULL,
NULL);
data->connection = g_object_ref (connection);
}
static void
name_acquired_handler (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
SensorData *data = user_data;
const gchar * const subsystems[] = { "iio", "input", "platform", NULL };
guint i;
data->client = g_udev_client_new (subsystems);
if (!find_sensors (data->client, data))
goto bail;
g_signal_connect (G_OBJECT (data->client), "uevent",
G_CALLBACK (sensor_changes), data);
for (i = 0; i < NUM_SENSOR_TYPES; i++) {
SensorDevice *sensor_device;
data->clients[i] = create_clients_hash_table ();
if (!driver_type_exists (data, i))
continue;
sensor_device = driver_open (DRIVER_FOR_TYPE(i), UDEV_DEVICE_FOR_TYPE(i),
driver_type_to_callback_func (data->drivers[i]->type), data);
if (!sensor_device) {
DRIVER_FOR_TYPE(i) = NULL;
g_clear_object (&UDEV_DEVICE_FOR_TYPE(i));
continue;
}
DEVICE_FOR_TYPE(i) = sensor_device;
}
if (!any_sensors_left (data))
goto bail;
send_dbus_event (data, PROP_ALL);
send_dbus_event (data, PROP_ALL_COMPASS);
return;
bail:
data->ret = 0;
g_debug ("No sensors or missing kernel drivers for the sensors");
g_main_loop_quit (data->loop);
}
static gboolean
setup_dbus (SensorData *data,
gboolean replace)
{
GBytes *bytes;
GBusNameOwnerFlags flags;
bytes = g_resources_lookup_data ("/net/hadess/SensorProxy/net.hadess.SensorProxy.xml",
G_RESOURCE_LOOKUP_FLAGS_NONE,
NULL);
data->introspection_data = g_dbus_node_info_new_for_xml (g_bytes_get_data (bytes, NULL), NULL);
g_bytes_unref (bytes);
g_assert (data->introspection_data != NULL);
flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
if (replace)
flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE;
data->name_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
SENSOR_PROXY_DBUS_NAME,
flags,
bus_acquired_handler,
name_acquired_handler,
name_lost_handler,
data,
NULL);
return TRUE;
}
static void
accel_changed_func (SensorDevice *sensor_device,
gpointer readings_data,
gpointer user_data)
{
SensorData *data = user_data;
AccelReadings *readings = (AccelReadings *) readings_data;
OrientationUp orientation = data->previous_orientation;
//FIXME handle errors
g_debug ("Accel sent by driver (quirk applied): %d, %d, %d (scale: %lf,%lf,%lf)",
readings->accel_x, readings->accel_y, readings->accel_z,
readings->scale.x, readings->scale.y, readings->scale.z);
orientation = orientation_calc (data->previous_orientation,
readings->accel_x, readings->accel_y, readings->accel_z,
readings->scale);
if (data->previous_orientation != orientation) {
OrientationUp tmp;
tmp = data->previous_orientation;
data->previous_orientation = orientation;
send_dbus_event (data, PROP_ACCELEROMETER_ORIENTATION);
g_debug ("Emitted orientation changed: from %s to %s",
orientation_to_string (tmp),
orientation_to_string (data->previous_orientation));
}
}
static void
light_changed_func (SensorDevice *sensor_device,
gpointer readings_data,
gpointer user_data)
{
SensorData *data = user_data;
LightReadings *readings = (LightReadings *) readings_data;
//FIXME handle errors
g_debug ("Light level sent by driver (quirk applied): %lf (unit: %s)",
readings->level, data->uses_lux ? "lux" : "vendor");
if (data->previous_level != readings->level ||
data->uses_lux != readings->uses_lux) {
gdouble tmp;
tmp = data->previous_level;
data->previous_level = readings->level;
data->uses_lux = readings->uses_lux;
send_dbus_event (data, PROP_LIGHT_LEVEL);
g_debug ("Emitted light changed: from %lf to %lf",
tmp, data->previous_level);
}
}
static void
compass_changed_func (SensorDevice *sensor_device,
gpointer readings_data,
gpointer user_data)
{
SensorData *data = user_data;
CompassReadings *readings = (CompassReadings *) readings_data;
//FIXME handle errors
g_debug ("Heading sent by driver (quirk applied): %lf degrees",
readings->heading);
if (data->previous_heading != readings->heading) {
gdouble tmp;
tmp = data->previous_heading;
data->previous_heading = readings->heading;
send_dbus_event (data, PROP_COMPASS_HEADING);
g_debug ("Emitted heading changed: from %lf to %lf",
tmp, data->previous_heading);
}
}
static void
proximity_changed_func (SensorDevice *sensor_device,
gpointer readings_data,
gpointer user_data)
{
SensorData *data = user_data;
ProximityReadings *readings = (ProximityReadings *) readings_data;
gboolean near;
//FIXME handle errors
g_debug ("Proximity sent by driver: %d",
readings->is_near);
near = readings->is_near > 0;
if (data->previous_prox_near != near) {
ProximityNear tmp;
tmp = data->previous_prox_near;
data->previous_prox_near = near;
send_dbus_event (data, PROP_PROXIMITY_NEAR);
g_debug ("Emitted proximity changed: from %d to %d",
tmp, near);
}
}
static ReadingsUpdateFunc
driver_type_to_callback_func (DriverType type)
{
switch (type) {
case DRIVER_TYPE_ACCEL:
return accel_changed_func;
case DRIVER_TYPE_LIGHT:
return light_changed_func;
case DRIVER_TYPE_COMPASS:
return compass_changed_func;
case DRIVER_TYPE_PROXIMITY:
return proximity_changed_func;
default:
g_assert_not_reached ();
}
}
static void
free_sensor_data (SensorData *data)
{
guint i;
if (data == NULL)
return;
if (data->name_id != 0) {
g_bus_unown_name (data->name_id);
data->name_id = 0;
}
for (i = 0; i < NUM_SENSOR_TYPES; i++) {
if (driver_type_exists (data, i))
driver_close (DEVICE_FOR_TYPE(i));
g_clear_object (&UDEV_DEVICE_FOR_TYPE(i));
g_clear_pointer (&data->clients[i], g_hash_table_unref);
}
g_clear_object (&data->auth);
g_clear_pointer (&data->introspection_data, g_dbus_node_info_unref);
g_clear_object (&data->connection);
g_clear_object (&data->client);
g_clear_pointer (&data->loop, g_main_loop_unref);
g_free (data);
}
static void
sensor_changes (GUdevClient *client,
gchar *action,
GUdevDevice *device,
SensorData *data)
{
guint i;
g_debug ("Sensor changes: action = %s, device = %s",
action, g_udev_device_get_sysfs_path (device));
if (g_strcmp0 (action, "remove") == 0) {
for (i = 0; i < NUM_SENSOR_TYPES; i++) {
GUdevDevice *dev = UDEV_DEVICE_FOR_TYPE(i);
if (!dev)
continue;
if (g_strcmp0 (g_udev_device_get_sysfs_path (device), g_udev_device_get_sysfs_path (dev)) == 0) {
g_debug ("Sensor type %s got removed (%s)",
driver_type_to_str (i),
g_udev_device_get_sysfs_path (dev));
g_clear_object (&UDEV_DEVICE_FOR_TYPE(i));
driver_close (DEVICE_FOR_TYPE(i));
DEVICE_FOR_TYPE(i) = NULL;
DRIVER_FOR_TYPE(i) = NULL;
g_clear_pointer (&data->clients[i], g_hash_table_unref);
data->clients[i] = create_clients_hash_table ();
send_driver_changed_dbus_event (data, i);
}
}
if (!any_sensors_left (data))
g_main_loop_quit (data->loop);
} else if (g_strcmp0 (action, "add") == 0) {
for (i = 0; i < G_N_ELEMENTS(drivers); i++) {
SensorDriver *driver = (SensorDriver *) drivers[i];
if (!driver_type_exists (data, driver->type) &&
driver_discover (driver, device)) {
g_debug ("Found hotplugged device %s of type %s at %s",
g_udev_device_get_sysfs_path (device),
driver_type_to_str (driver->type),
driver->driver_name);
if (driver_open (driver, device,
driver_type_to_callback_func (driver->type), data)) {
GHashTable *ht;
UDEV_DEVICE_FOR_TYPE(driver->type) = g_object_ref (device);
DRIVER_FOR_TYPE(driver->type) = (SensorDriver *) driver;
send_driver_changed_dbus_event (data, driver->type);
ht = data->clients[driver->type];
if (g_hash_table_size (ht) > 0) {
SensorDevice *sensor_device = DEVICE_FOR_TYPE(driver->type);
driver_set_polling (sensor_device, TRUE);
}
}
break;
}
}
}
}
int main (int argc, char **argv)
{
SensorData *data;
g_autoptr(GOptionContext) option_context = NULL;
g_autoptr(GError) error = NULL;
gboolean verbose = FALSE;
gboolean replace = FALSE;
const GOptionEntry options[] = {
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Show extra debugging information", NULL },
{ "replace", 'r', 0, G_OPTION_ARG_NONE, &replace, "Replace the running instance of iio-sensor-proxy", NULL },
{ NULL}
};
int ret = 0;
setlocale (LC_ALL, "");
option_context = g_option_context_new ("");
g_option_context_add_main_entries (option_context, options, NULL);
ret = g_option_context_parse (option_context, &argc, &argv, &error);
if (!ret) {
g_print ("Failed to parse arguments: %s\n", error->message);
return EXIT_FAILURE;
}
if (verbose) {
g_setenv ("G_MESSAGES_DEBUG", "all", TRUE);
g_debug ("Starting iio-sensor-proxy version "VERSION);
}
data = g_new0 (SensorData, 1);
data->previous_orientation = ORIENTATION_UNDEFINED;
data->uses_lux = TRUE;
/* Set up D-Bus */
setup_dbus (data, replace);
data->auth = polkit_authority_get_sync (NULL, NULL);
data->loop = g_main_loop_new (NULL, TRUE);
g_main_loop_run (data->loop);
ret = data->ret;
free_sensor_data (data);
return ret;
}