/** * @file mce-sensorfw.c * * Mode Control Entity - Interprocess communication with sensord * *

* * Copyright (C) 2013-2014 Jolla Ltd. * *

* * @author Simo Piiroinen * * mce is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License * version 2.1 as published by the Free Software Foundation. * * mce is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with mce. If not, see . */ #include "mce-sensorfw.h" #include "mce.h" #include "mce-log.h" #include "mce-dbus.h" #include "libwakelock.h" #include #include #include #include #include #include #include #include #include /* ========================================================================= * * * Internal Layering - Top-Down * * ========================================================================= * * * SENSORFW_MODULE: * - implements internal mce interface defined in mce-sensorfw.h * * SENSORFW_SERVICE: * - state machine that tracks availablity of sensord on system bus * - availalibility changes trigger activity in SENSORFW_PLUGIN * * SENSORFW_PLUGIN: * - state machine that takes care of sensord plugin loading ipc * - one instance / sensor * - finishing plugin loading triggers activity in SENSORFW_SESSION * * SENSORFW_SESSION: * - state machine that takes care of data session start ipc * - one instance / sensor * - acquiring a session id triggers activity in SENSORFW_CONNECTION * * SENSORFW_CONNECTION: * - state machine that takes care of data connection with sensord * - one instance / sensor * - establishing a connection triggers activity in SENSORFW_OVERRIDE * and SENSORFW_REPORTING * * SENSORFW_OVERRIDE: * - state machine that takes care of standby override ipc * - one instance / sensor * * * SENSORFW_REPORTING: * - state machine that takes care of sensor start/stop ipc * - one instance / sensor * - value changes reported via sensor specific SENSORFW_BACKEND * * SENSORFW_BACKEND: * - contains all sensor specific constants and logic * * SENSORFW_NOTIFY: * - manages cached sensor state * - provides default values used when sensord is not available * - feeds sensor data to upper level logic when state changes * and/or listening callbacks are registered * * Error handling: * - The layer where error occurred makes transition to ERROR state * and all layers below that are reset to IDLE state and sensor * states are reverted to sensord-is-not-available defaults. * - The failed operation is reattempted after brief delay until it * succeeds * - Sensord restart might still be needed for full recovery. * * State transitions: * - state transition diagram can be generated from mce-sensorfw.dot * via installing graphviz package and running from command line * dot -Tpng mce-sensorfw.dot -o mce-sensorfw.png * * ========================================================================= * * * Rough Data/Control Flow Diagram - Error Handling and EVDEV Input Excluded * * ========================================================================= * * * .--------------------. .--------------------. * | MCE INIT/EXIT | | MCE SENSOR LOGIC | * `--------------------' `--------------------' * | | * | init/quit enable/disable | * | | * v v * .--------------------------------------------------------. * | SENSORFW_MODULE | * `--------------------------------------------------------' * | | | * | start | probe/reset enable/disable | * | | | * | v v * | .---------------------------------------------------. * | | SENSORFW_SERVICE | * | `---------------------------------------------------' * | | | | | * | | start | load/reset set/unset | | * | | | | | start/stop * | | v | | * | | .------------------------------. | | * | | | SENSORFW_PLUGIN | | | * | | `------------------------------' | | * | | | | | * | | | start/reset | | * | | | | | * | | v | | * | | .------------------------------. | | * | | | SENSORFW_SESSION | | | * | | `------------------------------' | | * | | | | | * | | | connect/reset | | * | | | | | * | | v | | * | | .------------------------------. | | * | | | SENSORFW_CONNECTION | | | * | | `------------------------------' | | * | | | | | | * | | | rethink/reset | | | * | | | | | | * | | | rethink/reset | | | * | | | | | | * | | | v v | * | | | .--------------------------------. | * | | | | SENSORFW_OVERRIDE | | * | | | `--------------------------------' | * | | v v * | | .--------------------------------------------. * | | | SENSORFW_REPORTING | * | | `--------------------------------------------' * | | | * | | | initial/change/reset * | | | * | | v * | | .--------------------------------------------. * | | | SENSORFW_BACKEND | * | | `--------------------------------------------' * v v | * .--------------------. | sensor state * | SENSORFW_EXCEPTION | | * `--------------------' | * | | * active | | * v v * .--------------------------------------------. * | SENSORFW_NOTIFY | * `--------------------------------------------' * | * | sensor state * | * v * .--------------------------------------------. * | MCE SENSOR LOGIC | * `--------------------------------------------' * * ========================================================================= */ /** ID numbers for sensors supported by mce-sensorfwd module */ typedef enum { SFW_SENSOR_ID_PS, SFW_SENSOR_ID_ALS, SFW_SENSOR_ID_ORIENT, SFW_SENSOR_ID_ACCELEROMETER, SFW_SENSOR_ID_COMPASS, SFW_SENSOR_ID_GYROSCOPE, SFW_SENSOR_ID_LID, SFW_SENSOR_ID_HUMIDITY, SFW_SENSOR_ID_MAGNETOMETER, SFW_SENSOR_ID_PRESSURE, SFW_SENSOR_ID_ROTATION, SFW_SENSOR_ID_STEPCOUNTER, SFW_SENSOR_ID_TAP, SFW_SENSOR_ID_TEMPERATURE, SFW_SENSOR_ID_COUNT } sensor_id_t; static bool sensor_id_available(sensor_id_t id) { bool available = true; switch( id ) { case SFW_SENSOR_ID_PS: case SFW_SENSOR_ID_ALS: case SFW_SENSOR_ID_ORIENT: break; default: available = mce_in_sensortest_mode(); break; } return available; } /* ========================================================================= * * D-BUS CONSTANTS FOR D-BUS DAEMON * ========================================================================= */ /** org.freedesktop.DBus.NameOwnerChanged D-Bus signal */ #define DBUS_SIGNAL_NAME_OWNER_CHANGED "NameOwnerChanged" #define DBUS_METHOD_GET_NAME_OWNER "GetNameOwner" /* ========================================================================= * * D-BUS CONSTANTS FOR SENSORD SERVICE * ========================================================================= */ /** How long to wait before retrying failed sensorfw dbus requests */ #define SENSORFW_RETRY_DELAY_MS 10000 /** D-Bus name of the sensord service */ #define SENSORFW_SERVICE "com.nokia.SensorService" // ---------------------------------------------------------------- /** D-Bus object path for sensord sensor manager */ #define SENSORFW_MANAGER_OBJECT "/SensorManager" /** D-Bus interface used by sensor manager */ #define SENSORFW_MANAGER_INTEFCACE "local.SensorManager" /** D-Bus method for loading sensor plugin */ #define SENSORFW_MANAGER_METHOD_LOAD_PLUGIN "loadPlugin" /** D-Bus method for starting sensor session */ #define SENSORFW_MANAGER_METHOD_START_SESSION "requestSensor" // ---------------------------------------------------------------- /* D-Bus interfaces for accessing sensor specific data * * Note: Sensorfwd uses separate interface for each sensor. */ #define SFW_SENSOR_INTERFACE_PS "local.ProximitySensor" #define SFW_SENSOR_INTERFACE_ALS "local.ALSSensor" #define SFW_SENSOR_INTERFACE_ORIENT "local.OrientationSensor" #define SFW_SENSOR_INTERFACE_ACCELEROMETER "local.AccelerometerSensor" #define SFW_SENSOR_INTERFACE_COMPASS "local.CompassSensor" #define SFW_SENSOR_INTERFACE_GYROSCOPE "local.GyroscopeSensor" #define SFW_SENSOR_INTERFACE_LID "local.LidSensor" #define SFW_SENSOR_INTERFACE_HUMIDITY "local.HumiditySensor" #define SFW_SENSOR_INTERFACE_MAGNETOMETER "local.MagnetometerSensor" #define SFW_SENSOR_INTERFACE_PRESSURE "local.PressureSensor" #define SFW_SENSOR_INTERFACE_ROTATION "local.RotationSensor" #define SFW_SENSOR_INTERFACE_STEPCOUNTER "local.StepcounterSensor" #define SFW_SENSOR_INTERFACE_TAP "local.TapSensor" #define SFW_SENSOR_INTERFACE_TEMPERATURE "local.TemperatureSensor" /** D-Bus method for enabling sensor * * @Note All sensor specific interfaces have start method. */ #define SFW_SENSOR_METHOD_START "start" /** D-Bus method for disabling sensor * * @Note All sensor specific interfaces have stop method. */ #define SFW_SENSOR_METHOD_STOP "stop" /** D-Bus method for changing sensor standby override * * @Note All sensor specific interfaces have have this method, * but unless it is actually explicitly implemented, a * virtual dummy method call handler gets invoked and * it will just return "false". */ #define SFW_SENSOR_METHOD_SET_OVERRIDE "setStandbyOverride" /* D-Bus methods for getting current sensor value * * The method name and returned data varies from one sensor to * another, and in some cases there is no "current state" to * query (such as tap detection sensor which emits events). */ #define SFW_SENSOR_METHOD_READ_PS "proximity" #define SFW_SENSOR_METHOD_READ_ALS "lux" #define SFW_SENSOR_METHOD_READ_ORIENT "orientation" #define SFW_SENSOR_METHOD_READ_ACCELEROMETER "xyz" #define SFW_SENSOR_METHOD_READ_COMPASS "value" // or "declinationvalue"? #define SFW_SENSOR_METHOD_READ_GYROSCOPE "value" #define SFW_SENSOR_METHOD_READ_LID "closed" #define SFW_SENSOR_METHOD_READ_HUMIDITY "relativeHumidity" #define SFW_SENSOR_METHOD_READ_MAGNETOMETER "magneticField" #define SFW_SENSOR_METHOD_READ_PRESSURE "pressure" #define SFW_SENSOR_METHOD_READ_ROTATION "rotation" #define SFW_SENSOR_METHOD_READ_STEPCOUNTER "steps" #define SFW_SENSOR_METHOD_READ_TAP NULL // event, not state #define SFW_SENSOR_METHOD_READ_TEMPERATURE "temperature" // ---------------------------------------------------------------- /** Connect path to sensord data unix domain socket */ #define SENSORFW_DATA_SOCKET "/run/sensord.sock" // ---------------------------------------------------------------- /* Sensor names used by senseofwd */ #define SFW_SENSOR_NAME_PS "proximitysensor" #define SFW_SENSOR_NAME_ALS "alssensor" #define SFW_SENSOR_NAME_ORIENT "orientationsensor" #define SFW_SENSOR_NAME_ACCELEROMETER "accelerometersensor" #define SFW_SENSOR_NAME_COMPASS "compasssensor" #define SFW_SENSOR_NAME_GYROSCOPE "gyroscopesensor" #define SFW_SENSOR_NAME_LID "lidsensor" #define SFW_SENSOR_NAME_HUMIDITY "humiditysensor" #define SFW_SENSOR_NAME_MAGNETOMETER "magnetometersensor" #define SFW_SENSOR_NAME_PRESSURE "pressuresensor" #define SFW_SENSOR_NAME_ROTATION "rotationsensor" #define SFW_SENSOR_NAME_STEPCOUNTER "stepcountersensor" #define SFW_SENSOR_NAME_TAP "tapsensor" #define SFW_SENSOR_NAME_TEMPERATURE "temperaturesensor" // ---------------------------------------------------------------- /* ========================================================================= * * FORWARD_DECLARATIONS * ========================================================================= */ typedef struct sfw_service_t sfw_service_t; typedef struct sfw_plugin_t sfw_plugin_t; typedef struct sfw_session_t sfw_session_t; typedef struct sfw_connection_t sfw_connection_t; typedef struct sfw_override_t sfw_override_t; typedef struct sfw_reporting_t sfw_reporting_t; typedef struct sfw_backend_t sfw_backend_t; typedef struct sfw_sample_als_t sfw_sample_als_t; typedef struct sfw_sample_ps_t sfw_sample_ps_t; typedef struct sfw_sample_orient_t sfw_sample_orient_t; typedef struct sfw_sample_accelerometer_t sfw_sample_accelerometer_t; typedef struct sfw_sample_compass_t sfw_sample_compass_t; typedef struct sfw_sample_gyroscope_t sfw_sample_gyroscope_t; typedef struct sfw_sample_lid_t sfw_sample_lid_t; typedef struct sfw_sample_humidity_t sfw_sample_humidity_t; typedef struct sfw_sample_magnetometer_t sfw_sample_magnetometer_t; typedef struct sfw_sample_pressure_t sfw_sample_pressure_t; typedef struct sfw_sample_rotation_t sfw_sample_rotation_t; typedef struct sfw_sample_stepcounter_t sfw_sample_stepcounter_t; typedef struct sfw_sample_tap_t sfw_sample_tap_t; typedef struct sfw_sample_temperature_t sfw_sample_temperature_t; /* ========================================================================= * * SENSORFW_NOTIFY * ========================================================================= */ /** Proximity state to use when sensor can't be enabled * * This must be "uncovered" so that absense of or failures within * sensord do not make the device unusable due to proximity values * blocking display power up and touch input. */ #define SFW_NOTIFY_DEFAULT_PS false /** Proximity state to use when waiting for sensord startup * * This must be "covered" so that we do not prematurely unblock * touch input / allow display power up during bootup and during * sensord restarts. */ #define SFW_NOTIFY_EXCEPTION_PS true /** Ambient light level [lux] to use when sensor can't be enabled */ #define SFW_NOTIFY_DEFAULT_ALS 400 /** Orientation state to use when sensor can't be enabled */ #define SFW_NOTIFY_DEFAULT_ORIENT MCE_ORIENTATION_UNDEFINED /** Dummy sensor value to use when re-sending cached state data */ #define SFW_NOTIFY_DUMMY 0 typedef enum { /** Cached sensor state should be reset to default value */ NOTIFY_RESET, /** Cached sensor state should be restored from last-known value */ NOTIFY_RESTORE, /** Cached sensor state should be sent again */ NOTIFY_REPEAT, /** Sensor state was received from evdev source */ NOTIFY_EVDEV, /** Sensor state was received from sensord */ NOTIFY_SENSORD, /** Flush cached state */ NOTIFY_FORGET, NOTIFY_NUMTYPES } sfw_notify_t; static const char *sfw_notify_name (sfw_notify_t type); static gboolean sfw_name_owner_changed_cb (DBusMessage *const msg); /* ========================================================================= * * SENSORD_DATA_TYPES * ========================================================================= */ /** ALS data block as sensord sends them */ struct sfw_sample_als_t { /** microseconds, monotonic */ uint64_t als_timestamp; /** amount of light [lux] */ uint32_t als_value; }; static const char *sfw_sample_als_repr(const sfw_sample_als_t *self); /** PS data block as sensord sends them */ struct sfw_sample_ps_t { /** microseconds, monotonic */ uint64_t ps_timestamp; /** distance of blocking object [cm] */ uint32_t ps_value; /** sensor covered [bool] * * This should be the size of a C++ bool on the same platform. * Unfortunately there's no way to find out in a C program */ uint8_t ps_withinProximity; }; static const char *sfw_sample_ps_repr(const sfw_sample_ps_t *self); /** Orientation data block as sensord sends them */ struct sfw_sample_orient_t { /* microseconds, monotonic */ uint64_t orient_timestamp; /* orientation [enum orientation_state_t] */ int32_t orient_state; }; static const char *sfw_sample_orient_repr(const sfw_sample_orient_t *self); typedef enum sfw_lid_type_t { SFW_LID_TYPE_UNKNOWN = -1, // UnknownLid SFW_LID_TYPE_FRONT = 0, // FrontLid SFW_LID_TYPE_BACK = 1, // BackLid } sfw_lid_type_t; static const char *sfw_lid_type_repr(sfw_lid_type_t type); typedef enum sfw_tap_direction_t { /** Left or right side tapped */ SFW_TAP_DIRECTION_X = 0, /** Top or down side tapped */ SFW_TAP_DIRECTION_Y = 1, /** Face or bottom tapped */ SFW_TAP_DIRECTION_Z = 2, /** Tapped from left to right */ SFW_TAP_DIRECTION_LEFT_RIGHT = 3, /** Tapped from right to left */ SFW_TAP_DIRECTION_RIGHT_LEFT = 4, /** Tapped from top to bottom */ SFW_TAP_DIRECTION_TOP_BOTTOM = 5, /** Tapped from bottom to top */ SFW_TAP_DIRECTION_BOTTOM_TOP = 6, /** Tapped from face to back */ SFW_TAP_DIRECTION_FACE_BACK = 7, /** Tapped from back to face */ SFW_TAP_DIRECTION_BACK_FACE = 8, } sfw_tap_direction_t; static const char *sfw_tap_direction_repr(sfw_tap_direction_t direction); typedef enum sfw_tap_type_t { /** placeholder */ SFW_TAP_TYPE_NONE = -1, /** Double tap. */ SFW_TAP_TYPE_DOUBLE_TAP = 0, /**< Single tap. */ SFW_TAP_TYPE_SINGLE_TAP = 1, } sfw_tap_type_t; static const char * sfw_tap_type_repr(sfw_tap_type_t type); typedef struct sfw_xyz_t { int32_t x,y,z; } sfw_xyz_t; struct sfw_sample_accelerometer_t { /** microseconds, monotonic */ uint64_t accelerometer_timestamp; /** amount of TBD */ sfw_xyz_t accelerometer_xyz; }; static const char *sfw_sample_accelerometer_repr(const sfw_sample_accelerometer_t *self); struct sfw_sample_compass_t { /** microseconds, monotonic */ uint64_t compass_timestamp; /** Angle to north which may be declination corrected or not. This is the value apps should use */ int32_t compass_degrees; /** Angle to north without declination correction */ int32_t compass_raw_degrees; /** Declination corrected angle to north */ int32_t compass_corrected_degrees; /** Magnetometer calibration level. Higher value means better calibration. */ int32_t compass_level; }; static const char *sfw_sample_compass_repr(const sfw_sample_compass_t *self); struct sfw_sample_gyroscope_t { /** microseconds, monotonic */ uint64_t gyroscope_timestamp; /** amount of TBD */ sfw_xyz_t gyroscope_xyz; }; static const char *sfw_sample_gyroscope_repr(const sfw_sample_gyroscope_t *self); struct sfw_sample_lid_t { /** microseconds, monotonic */ uint64_t lid_timestamp; /** amount of TBD */ int32_t lid_type; // sfw_lid_type_t uint32_t lid_value; }; static const char *sfw_sample_lid_repr(const sfw_sample_lid_t *self); struct sfw_sample_humidity_t { /** microseconds, monotonic */ uint64_t humidity_timestamp; /** amount of TBD */ uint32_t humidity_value; }; static const char *sfw_sample_humidity_repr(const sfw_sample_humidity_t *self); struct sfw_sample_magnetometer_t { /** microseconds, monotonic */ uint64_t magnetometer_timestamp; /** X coordinate value */ int32_t magnetometer_x; /** Y coordinate value */ int32_t magnetometer_y; /** Z coordinate value */ int32_t magnetometer_z; /** raw X coordinate value */ int32_t magnetometer_rx; /** raw Y coordinate value */ int32_t magnetometer_ry; /** raw Z coordinate value */ int32_t magnetometer_rz; /** Magnetometer calibration level. Higher value means better calibration. */ int32_t magnetometer_level; }; static const char *sfw_sample_magnetometer_repr(const sfw_sample_magnetometer_t *self); struct sfw_sample_pressure_t { /** microseconds, monotonic */ uint64_t pressure_timestamp; /** amount of TBD */ uint32_t pressure_value; }; static const char *sfw_sample_pressure_repr(const sfw_sample_pressure_t *self); struct sfw_sample_rotation_t { /** microseconds, monotonic */ uint64_t rotation_timestamp; /** amount of TBD */ sfw_xyz_t rotation_xyz; }; static const char *sfw_sample_rotation_repr(const sfw_sample_rotation_t *self); struct sfw_sample_stepcounter_t { /** microseconds, monotonic */ uint64_t stepcounter_timestamp; /** amount of TBD */ uint32_t stepcounter_value; }; static const char *sfw_sample_stepcounter_repr(const sfw_sample_stepcounter_t *self); struct sfw_sample_tap_t { /** microseconds, monotonic */ uint64_t tap_timestamp; /** amount of TBD */ uint32_t tap_direction; // sfw_tap_direction_t int32_t tap_type; // sfw_tap_type_t }; static const char *sfw_sample_tap_repr(const sfw_sample_tap_t *self); struct sfw_sample_temperature_t { /** microseconds, monotonic */ uint64_t temperature_timestamp; /** amount of TBD */ uint32_t temperature_value; }; static const char *sfw_sample_temperature_repr(const sfw_sample_temperature_t *self); /* ========================================================================= * * SENSORFW_BACKEND * ========================================================================= */ /** Callback function type: parsing initial value reply */ typedef bool (*sfw_value_fn)(sfw_plugin_t *plugin, DBusMessageIter *data); /** Callback function type: value change reporting */ typedef void (*sfw_sample_fn)(sfw_plugin_t *plugin, sfw_notify_t type, const void *sample); /** Sensor specific data and callbacks */ struct sfw_backend_t { /** Name of the sensor as expected by sensord */ const char *be_sensor_name; /** D-Bus object path for the sensor, or NULL to construct default one */ const char *be_sensor_object; /** D-Bus interface for the sensor */ const char *be_sensor_interface; /** Size of sensor data blobs sensord will be sending */ size_t be_sample_size; /** Callback for handling initial value reply */ sfw_value_fn be_value_cb; /** Callback for handling sensor data blob */ sfw_sample_fn be_sample_cb; /** D-Bus method name for querying the initial sensor value */ const char *be_value_method; }; static bool sfw_backend_parse_data (DBusMessageIter *data, int arg_type, ...); static bool sfw_backend_als_value_cb (sfw_plugin_t *plugin, DBusMessageIter *data); static bool sfw_backend_ps_value_cb (sfw_plugin_t *plugin, DBusMessageIter *data); static bool sfw_backend_orient_value_cb (sfw_plugin_t *plugin, DBusMessageIter *data); static bool sfw_backend_accelerometer_value_cb (sfw_plugin_t *plugin, DBusMessageIter *data); static bool sfw_backend_compass_value_cb (sfw_plugin_t *plugin, DBusMessageIter *data); static bool sfw_backend_gyroscope_value_cb (sfw_plugin_t *plugin, DBusMessageIter *data); static bool sfw_backend_lid_value_cb (sfw_plugin_t *plugin, DBusMessageIter *data); static bool sfw_backend_humidity_value_cb (sfw_plugin_t *plugin, DBusMessageIter *data); static bool sfw_backend_magnetometer_value_cb (sfw_plugin_t *plugin, DBusMessageIter *data); static bool sfw_backend_pressure_value_cb (sfw_plugin_t *plugin, DBusMessageIter *data); static bool sfw_backend_rotation_value_cb (sfw_plugin_t *plugin, DBusMessageIter *data); static bool sfw_backend_stepcounter_value_cb (sfw_plugin_t *plugin, DBusMessageIter *data); static bool sfw_backend_tap_value_cb (sfw_plugin_t *plugin, DBusMessageIter *data); static bool sfw_backend_temperature_value_cb (sfw_plugin_t *plugin, DBusMessageIter *data); static void sfw_backend_als_sample_cb (sfw_plugin_t *plugin, sfw_notify_t type, const void *sample); static void sfw_backend_ps_sample_cb (sfw_plugin_t *plugin, sfw_notify_t type, const void *sample); static void sfw_backend_orient_sample_cb (sfw_plugin_t *plugin, sfw_notify_t type, const void *sample); static void sfw_backend_accelerometer_sample_cb (sfw_plugin_t *plugin, sfw_notify_t type, const void *sample); static void sfw_backend_compass_sample_cb (sfw_plugin_t *plugin, sfw_notify_t type, const void *sample); static void sfw_backend_gyroscope_sample_cb (sfw_plugin_t *plugin, sfw_notify_t type, const void *sample); static void sfw_backend_lid_sample_cb (sfw_plugin_t *plugin, sfw_notify_t type, const void *sample); static void sfw_backend_humidity_sample_cb (sfw_plugin_t *plugin, sfw_notify_t type, const void *sample); static void sfw_backend_magnetometer_sample_cb (sfw_plugin_t *plugin, sfw_notify_t type, const void *sample); static void sfw_backend_pressure_sample_cb (sfw_plugin_t *plugin, sfw_notify_t type, const void *sample); static void sfw_backend_rotation_sample_cb (sfw_plugin_t *plugin, sfw_notify_t type, const void *sample); static void sfw_backend_stepcounter_sample_cb (sfw_plugin_t *plugin, sfw_notify_t type, const void *sample); static void sfw_backend_tap_sample_cb (sfw_plugin_t *plugin, sfw_notify_t type, const void *sample); static void sfw_backend_temperature_sample_cb (sfw_plugin_t *plugin, sfw_notify_t type, const void *sample); /* ========================================================================= * * SENSORFW_HELPERS * ========================================================================= */ static bool sfw_socket_set_blocking (int fd, bool blocking); static int sfw_socket_open (void); static guint sfw_socket_add_notify (int fd, bool close_on_unref, GIOCondition cnd, GIOFunc io_cb, gpointer aptr); /* ========================================================================= * * SENSORFW_REPORTING * ========================================================================= */ typedef enum { /** Initial state used before availability of sensord is known */ REPORTING_INITIAL, /** Sensord is not available */ REPORTING_IDLE, /** Choose between start() and stop() */ REPORTING_RETHINK, /** Waiting a reply to start() */ REPORTING_ENABLING, /** Sensor started succesfully */ REPORTING_ENABLED, /** Waiting a reply to stop() */ REPORTING_DISABLING, /** Sensor stopped succesfully */ REPORTING_DISABLED, /** Something went wrong */ REPORTING_ERROR, REPORTING_NUMSTATES } sfw_reporting_state_t; /** State machine for handling sensor start/stop */ struct sfw_reporting_t { /** Pointer to containing plugin object */ sfw_plugin_t *rep_plugin; /** Current reporting state */ sfw_reporting_state_t rep_state; /** Pending start/stop dbus method call */ DBusPendingCall *rep_change_pc; /** Pending initial value dbus method call */ DBusPendingCall *rep_value_pc; /** Flag for: MCE wants sensor to be enabled */ bool rep_target; /** Timer for: Retry after ipc error */ guint rep_retry_id; }; static const char *sfw_reporting_state_name (sfw_reporting_state_t state); static sfw_reporting_t *sfw_reporting_create (sfw_plugin_t *plugin); static void sfw_reporting_delete (sfw_reporting_t *self); static void sfw_reporting_cancel_change (sfw_reporting_t *self); static void sfw_reporting_cancel_value (sfw_reporting_t *self); static void sfw_reporting_cancel_retry (sfw_reporting_t *self); static void sfw_reporting_trans (sfw_reporting_t *self, sfw_reporting_state_t state); static void sfw_reporting_change_cb (DBusPendingCall *pc, void *aptr); static void sfw_reporting_value_cb (DBusPendingCall *pc, void *aptr); static gboolean sfw_reporting_retry_cb (gpointer aptr); static void sfw_reporting_do_rethink (sfw_reporting_t *self); static void sfw_reporting_do_start (sfw_reporting_t *self); static void sfw_reporting_do_reset (sfw_reporting_t *self); static void sfw_reporting_set_target (sfw_reporting_t *self, bool enable); /* ========================================================================= * * SENSORFW_OVERRIDE * ========================================================================= */ typedef enum { /** Initial state used before availability of sensord is known */ OVERRIDE_INITIAL, /** Sensord is not available */ OVERRIDE_IDLE, /** Choose between standby override set/unset */ OVERRIDE_RETHINK, /** Waiting for a reply to set standby override */ OVERRIDE_ENABLING, /** Standby override succesfully set */ OVERRIDE_ENABLED, /** Waiting for a reply to unset standby override */ OVERRIDE_DISABLING, /** Standby override succesfully unset */ OVERRIDE_DISABLED, /** Something went wrong */ OVERRIDE_ERROR, /** Not supported */ OVERRIDE_NA, OVERRIDE_NUMSTATES } sfw_override_state_t; /** State machine for handling sensor standby override */ struct sfw_override_t { /** Pointer to containing plugin object */ sfw_plugin_t *ovr_plugin; /** Current override state */ sfw_override_state_t ovr_state; /** Pending standby override set/unset method call */ DBusPendingCall *ovr_start_pc; /** Flag for: MCE wants standby override to be set */ bool ovr_target; /** Timer for: Retry after ipc error */ guint ovr_retry_id; }; static const char *sfw_override_state_name (sfw_override_state_t state); static sfw_override_t *sfw_override_create (sfw_plugin_t *plugin); static void sfw_override_delete (sfw_override_t *self); static void sfw_override_cancel_start (sfw_override_t *self); static void sfw_override_cancel_retry (sfw_override_t *self); static void sfw_override_trans (sfw_override_t *self, sfw_override_state_t state); static void sfw_override_start_cb (DBusPendingCall *pc, void *aptr); static gboolean sfw_override_retry_cb (gpointer aptr); static void sfw_override_do_rethink (sfw_override_t *self); static void sfw_override_do_start (sfw_override_t *self); static void sfw_override_do_reset (sfw_override_t *self); static void sfw_override_set_target (sfw_override_t *self, bool enable); /* ========================================================================= * * SENSORFW_CONNECTION * ========================================================================= */ typedef enum { /** Initial state used before availability of sensord is known */ CONNECTION_INITIAL, /** Sensord is not available */ CONNECTION_IDLE, /** Waiting for data socket to come writable */ CONNECTION_CONNECTING, /** Waiting for handshake reply */ CONNECTION_REGISTERING, /** Waiting for sensor data */ CONNECTION_CONNECTED, /** Something went wrong */ CONNECTION_ERROR, CONNECTION_NUMSTATES } sfw_connection_state_t; static const char *sfw_connection_state_name (sfw_connection_state_t state); /** State machine for handling sensor data connection */ struct sfw_connection_t { /** Pointer to containing plugin object */ sfw_plugin_t *con_plugin; /** Current connection state */ sfw_connection_state_t con_state; /** File descriptor for data socket */ int con_fd; /** I/O watch id id for: ready to write */ guint con_rx_id; /** I/O watch id id for: data available */ guint con_tx_id; /** Timer for: Retry after ipc error */ guint con_retry_id; }; static sfw_connection_t *sfw_connection_create (sfw_plugin_t *plugin); static void sfw_connection_delete (sfw_connection_t *self); static bool sfw_connection_handle_samples (sfw_connection_t *self, char *data, size_t size); static int sfw_connection_get_session_id (const sfw_connection_t *self); static bool sfw_connection_tx_req (sfw_connection_t *self); static bool sfw_connection_rx_ack (sfw_connection_t *self); static bool sfw_connection_rx_dta (sfw_connection_t *self); static gboolean sfw_connection_tx_cb (GIOChannel *chn, GIOCondition cnd, gpointer aptr); static gboolean sfw_connection_rx_cb (GIOChannel *chn, GIOCondition cnd, gpointer aptr); static void sfw_connection_remove_tx_notify(sfw_connection_t *self); static void sfw_connection_remove_rx_notify(sfw_connection_t *self); static void sfw_connection_close_socket (sfw_connection_t *self); static bool sfw_connection_open_socket (sfw_connection_t *self); static gboolean sfw_connection_retry_cb (gpointer aptr); static void sfw_connection_cancel_retry (sfw_connection_t *self); static void sfw_connection_trans (sfw_connection_t *self, sfw_connection_state_t state); static void sfw_connection_do_start (sfw_connection_t *self); static void sfw_connection_do_reset (sfw_connection_t *self); /* ========================================================================= * * SENSORFW_SESSION * ========================================================================= */ typedef enum { /** Initial state used before availability of sensord is known */ SESSION_INITIAL, /** Sensord is not available */ SESSION_IDLE, /** Waiting for session id reply */ SESSION_REQUESTING, /** Have a session id */ SESSION_ACTIVE, /** Something went wrong */ SESSION_ERROR, /** Sensor is not supported */ SESSION_INVALID, SESSION_NUMSTATES } sfw_session_state_t; /** State machine for handling sensor data session */ struct sfw_session_t { /** Pointer to containing plugin object */ sfw_plugin_t *ses_plugin; /** Current session state */ sfw_session_state_t ses_state; /** Pending session start dbus method call */ DBusPendingCall *ses_start_pc; /** Session ID received from sensord */ dbus_int32_t ses_id; /** Timer for: Retry after ipc error */ guint ses_retry_id; }; /** Sensor not supported session id from sensorfwd */ #define SESSION_ID_INVALID (-1) /** Placeholder value for session id not parsed yet */ #define SESSION_ID_UNKNOWN (-2) static const char *sfw_session_state_name (sfw_session_state_t state); static sfw_session_t *sfw_session_create (sfw_plugin_t *plugin); static void sfw_session_delete (sfw_session_t *self); static int sfw_session_get_id (const sfw_session_t *self); static void sfw_session_cancel_start (sfw_session_t *self); static void sfw_session_cancel_retry (sfw_session_t *self); static void sfw_session_trans (sfw_session_t *self, sfw_session_state_t state); static void sfw_session_start_cb (DBusPendingCall *pc, void *aptr); static gboolean sfw_session_retry_cb (gpointer aptr); static void sfw_session_do_start (sfw_session_t *self); static void sfw_session_do_reset (sfw_session_t *self); /* ========================================================================= * * SENSORFW_PLUGIN * ========================================================================= */ typedef enum { /** Initial state used before availability of sensord is known */ PLUGIN_INITIAL, /** Sensord is not available */ PLUGIN_IDLE, /** Waiting for a reply to plugin load method call */ PLUGIN_LOADING, /** Sensor plugin is loaded */ PLUGIN_LOADED, /** Something went wrong */ PLUGIN_ERROR, /** Plugin not available */ PLUGIN_NA, PLUGIN_NUMSTATES } sfw_plugin_state_t; /** State machine for handling sensor plugin loading */ struct sfw_plugin_t { /** Sensor specific backend data & callbacks */ const sfw_backend_t *plg_backend; /** Default sensor specific D-BUs object path * * Constructed if backend does not define one explicitly */ char *plg_sensor_object; /** Current plugin state */ sfw_plugin_state_t plg_state; /** Pending plugin load dbus method call */ DBusPendingCall *plg_load_pc; /** Session state machine object */ sfw_session_t *plg_session; /** Connection state machine object */ sfw_connection_t *plg_connection; /** Standby override state machine object */ sfw_override_t *plg_override; /** Sensor reporting state machine object */ sfw_reporting_t *plg_reporting; /** Timer for: Retry after ipc error */ guint plg_retry_id; }; static const char *sfw_plugin_state_name (sfw_plugin_state_t state); static sfw_plugin_t *sfw_plugin_create (const sfw_backend_t *backend); static void sfw_plugin_delete (sfw_plugin_t *self); static void sfw_plugin_enable_sensor (sfw_plugin_t *self, bool enable); static const char *sfw_plugin_get_sensor_name (const sfw_plugin_t *self); static const char *sfw_plugin_get_sensor_object (const sfw_plugin_t *self); static const char *sfw_plugin_get_sensor_interface(const sfw_plugin_t *self); static int sfw_plugin_get_session_id (const sfw_plugin_t *self); static const char *sfw_plugin_get_value_method (const sfw_plugin_t *self); static size_t sfw_plugin_get_sample_size (const sfw_plugin_t *self); static void sfw_plugin_handle_sample (sfw_plugin_t *self, const void *sample); static bool sfw_plugin_handle_value (sfw_plugin_t *self, DBusMessageIter *data); static void sfw_plugin_reset_value (sfw_plugin_t *self); static void sfw_plugin_repeat_value (sfw_plugin_t *self); static void sfw_plugin_restore_value (sfw_plugin_t *self); static void sfw_plugin_forget_value (sfw_plugin_t *self); static void sfw_plugin_cancel_load (sfw_plugin_t *self); static void sfw_plugin_cancel_retry (sfw_plugin_t *self); static void sfw_plugin_trans (sfw_plugin_t *self, sfw_plugin_state_t state); static void sfw_plugin_load_cb (DBusPendingCall *pc, void *aptr); static gboolean sfw_plugin_retry_cb (gpointer aptr); static void sfw_plugin_do_load (sfw_plugin_t *self); static void sfw_plugin_do_reset (sfw_plugin_t *self); static void sfw_plugin_do_session_start (sfw_plugin_t *self); static void sfw_plugin_do_session_reset (sfw_plugin_t *self); static void sfw_plugin_do_connection_start (sfw_plugin_t *self); static void sfw_plugin_do_connection_reset (sfw_plugin_t *self); static void sfw_plugin_do_override_start (sfw_plugin_t *self); static void sfw_plugin_do_override_reset (sfw_plugin_t *self); static void sfw_plugin_do_reporting_start (sfw_plugin_t *self); static void sfw_plugin_do_reporting_reset (sfw_plugin_t *self); /* ========================================================================= * * SENSORFW_SERVICE * ========================================================================= */ typedef enum { /** Sensord availability is not known */ SERVICE_UNKNOWN, /** Waiting for a reply to name owner query */ SERVICE_QUERYING, /** Sensord service is available */ SERVICE_RUNNING, /** Sensord service is not available */ SERVICE_STOPPED, SERVICE_NUMSTATES } sfw_service_state_t; /** State machine for handling sensord service availability tracking */ struct sfw_service_t { /** Sensord service tracking state */ sfw_service_state_t srv_state; /** Pending name owner dbus method call */ DBusPendingCall *srv_query_pc; /** State machine objects for sensors */ sfw_plugin_t *srv_plugin[SFW_SENSOR_ID_COUNT]; }; static const char *sfw_service_state_name (sfw_service_state_t state); static sfw_service_t *sfw_service_create (void); static void sfw_service_delete (sfw_service_t *self); static void sfw_service_cancel_query (sfw_service_t *self); static void sfw_service_query_cb (DBusPendingCall *pc, void *aptr); static void sfw_service_trans (sfw_service_t *self, sfw_service_state_t state); static void sfw_service_do_start (sfw_service_t *self); static void sfw_service_do_stop (sfw_service_t *self); static void sfw_service_do_query (sfw_service_t *self); static sfw_plugin_t *sfw_service_plugin (const sfw_service_t *self, sensor_id_t id); static void sfw_service_set_sensor (const sfw_service_t *self, sensor_id_t id, bool enable); /* ========================================================================= * * SENSORFW_EXCEPTION * ========================================================================= */ /** Durations for enforcing use of exceptional sensor values */ typedef enum { /** Expected time until sensord availability is known */ SFW_EXCEPTION_LENGTH_MCE_STARTING_UP = 30 * 1000, /** Expected time until data from sensord is available */ SFW_EXCEPTION_LENGTH_SENSORD_RUNNING = 2 * 1000, /** Expected time until sensord gets (re)started */ SFW_EXCEPTION_LENGTH_SENSORD_STOPPED = 5 * 1000, } sfw_exception_delay_t; static gboolean sfw_exception_timer_cb (gpointer aptr); static void sfw_exception_cancel (void); static void sfw_exception_start (sfw_exception_delay_t delay_ms); static bool sfw_exception_is_active (void); /* ========================================================================= * * SENSORFW_MODULE * ========================================================================= */ /** Sensord availability tracking state machine object */ static sfw_service_t *sfw_service = 0; /** Proximity change callback used for notifying upper level logic */ static void (*sfw_notify_ps_cb)(bool covered) = 0; /** Ambient light change callback used for notifying upper level logic */ static void (*sfw_notify_als_cb)(int lux) = 0; /** Orientation change callback used for notifying upper level logic */ static void (*sfw_notify_orient_cb)(int state) = 0; // (exported API defined in "mce-sensorfw.h") static gboolean mce_sensorfw_evdev_cb (GIOChannel *chn, GIOCondition cnd, gpointer aptr); static void mce_sensorfw_als_detach (void); static void mce_sensorfw_ps_detach (void); static bool als_from_evdev(void); static bool ps_from_evdev(void); /* ========================================================================= * * SENSORD_DATA_TYPES * ========================================================================= */ static const char * sfw_sample_als_repr(const sfw_sample_als_t *self) { static char buf[64]; snprintf(buf, sizeof buf, "time=%"PRIu64" lux=%"PRIu32, self->als_timestamp, self->als_value); return buf; } static const char * sfw_sample_ps_repr(const sfw_sample_ps_t *self) { static char buf[64]; snprintf(buf, sizeof buf, "time=%"PRIu64" distance=%"PRIu32" proximity=%s", self->ps_timestamp, self->ps_value, self->ps_withinProximity ? "true" : "false"); return buf; } static const char * sfw_sample_orient_repr(const sfw_sample_orient_t *self) { static char buf[64]; snprintf(buf, sizeof buf, "time=%"PRIu64" state=%s", self->orient_timestamp, orientation_state_repr(self->orient_state)); return buf; } static const char * sfw_sample_accelerometer_repr(const sfw_sample_accelerometer_t *self) { static char buf[64]; snprintf(buf, sizeof buf, "time=%"PRIu64" x=%"PRId32" y=%"PRId32" z=%"PRId32, self->accelerometer_timestamp, self->accelerometer_xyz.x, self->accelerometer_xyz.y, self->accelerometer_xyz.z); return buf; } static const char * sfw_sample_compass_repr(const sfw_sample_compass_t *self) { static char buf[64]; snprintf(buf, sizeof buf, "time=%"PRIu64" deg=%"PRId32" raw=%"PRId32" cor=%"PRId32" lev=%"PRId32, self->compass_timestamp, self->compass_degrees, self->compass_raw_degrees, self->compass_corrected_degrees, self->compass_level); return buf; } static const char * sfw_sample_gyroscope_repr(const sfw_sample_gyroscope_t *self) { static char buf[64]; snprintf(buf, sizeof buf, "time=%"PRIu64" x=%"PRId32" y=%"PRId32" z=%"PRId32, self->gyroscope_timestamp, self->gyroscope_xyz.x, self->gyroscope_xyz.y, self->gyroscope_xyz.z); return buf; } static const char * sfw_lid_type_repr(sfw_lid_type_t type) { const char *repr = "INVALID"; switch( type ) { case SFW_LID_TYPE_UNKNOWN: repr = "UNKNOWN"; break; case SFW_LID_TYPE_FRONT: repr = "FRONT"; break; case SFW_LID_TYPE_BACK: repr = "BACK"; break; default: break; } return repr; } static const char * sfw_sample_lid_repr(const sfw_sample_lid_t *self) { static char buf[64]; snprintf(buf, sizeof buf, "time=%"PRIu64" type=%s value=%"PRIu32, self->lid_timestamp, sfw_lid_type_repr(self->lid_type), self->lid_value); return buf; } static const char * sfw_sample_humidity_repr(const sfw_sample_humidity_t *self) { static char buf[64]; snprintf(buf, sizeof buf, "time=%"PRIu64" humidity=%"PRIu32, self->humidity_timestamp, self->humidity_value); return buf; } static const char * sfw_sample_magnetometer_repr(const sfw_sample_magnetometer_t *self) { static char buf[64]; snprintf(buf, sizeof buf, "time=%"PRIu64 " x=%"PRId32" y=%"PRId32" z=%"PRId32 " rx=%"PRId32" ry=%"PRId32" rz=%"PRId32 " level=%"PRId32, self->magnetometer_timestamp, self->magnetometer_x, self->magnetometer_y, self->magnetometer_z, self->magnetometer_rx, self->magnetometer_ry, self->magnetometer_rz, self->magnetometer_level); return buf; } static const char * sfw_sample_pressure_repr(const sfw_sample_pressure_t *self) { static char buf[64]; snprintf(buf, sizeof buf, "time=%"PRIu64" pressure=%"PRIu32, self->pressure_timestamp, self->pressure_value); return buf; } static const char * sfw_sample_rotation_repr(const sfw_sample_rotation_t *self) { static char buf[64]; snprintf(buf, sizeof buf, "time=%"PRIu64" x=%"PRId32" y=%"PRId32" z=%"PRId32, self->rotation_timestamp, self->rotation_xyz.x, self->rotation_xyz.y, self->rotation_xyz.z); return buf; } static const char * sfw_sample_stepcounter_repr(const sfw_sample_stepcounter_t *self) { static char buf[64]; snprintf(buf, sizeof buf, "time=%"PRIu64" stepcount=%"PRIu32, self->stepcounter_timestamp, self->stepcounter_value); return buf; } static const char * sfw_tap_direction_repr(sfw_tap_direction_t direction) { const char *repr = "INVALID"; switch( direction ) { case SFW_TAP_DIRECTION_X: repr = "X"; break; case SFW_TAP_DIRECTION_Y: repr = "Y"; break; case SFW_TAP_DIRECTION_Z: repr = "Z"; break; case SFW_TAP_DIRECTION_LEFT_RIGHT: repr = "LEFT_RIGHT"; break; case SFW_TAP_DIRECTION_RIGHT_LEFT: repr = "RIGHT_LEFT"; break; case SFW_TAP_DIRECTION_TOP_BOTTOM: repr = "TOP_BOTTOM"; break; case SFW_TAP_DIRECTION_BOTTOM_TOP: repr = "BOTTOM_TOP"; break; case SFW_TAP_DIRECTION_FACE_BACK: repr = "FACE_BACK"; break; case SFW_TAP_DIRECTION_BACK_FACE: repr = "BACK_FACE"; break; default: break; } return repr; } static const char * sfw_tap_type_repr(sfw_tap_type_t type) { const char *repr = "INVALID"; switch( type ) { case SFW_TAP_TYPE_NONE: repr = "NONE"; break; case SFW_TAP_TYPE_DOUBLE_TAP: repr = "DOUBLE_TAP"; break; case SFW_TAP_TYPE_SINGLE_TAP: repr = "SINGLE_TAP"; break; default: break; } return repr; } static const char * sfw_sample_tap_repr(const sfw_sample_tap_t *self) { static char buf[64]; snprintf(buf, sizeof buf, "time=%"PRIu64" direction=%s type=%s", self->tap_timestamp, sfw_tap_direction_repr(self->tap_direction), sfw_tap_type_repr(self->tap_type)); return buf; } static const char * sfw_sample_temperature_repr(const sfw_sample_temperature_t *self) { static char buf[64]; snprintf(buf, sizeof buf, "time=%"PRIu64" temperature=%"PRIu32, self->temperature_timestamp, self->temperature_value); return buf; } /* ========================================================================= * * SENSORFW_BACKEND * ========================================================================= */ /* ------------------------------------------------------------------------- * * callbacks for interpreting sensor specific current value replies * ------------------------------------------------------------------------- */ static bool sfw_backend_parse_data(DBusMessageIter *data, int arg_type, ...) { bool ack = false; va_list va; va_start(va, arg_type); while( !mce_dbus_iter_at_end(data) ) { if( !mce_dbus_iter_get_basic(data, va_arg(va, void *), arg_type) ) goto EXIT; arg_type = va_arg(va, int); } ack = true; EXIT: va_end(va); return ack; } static bool sfw_backend_als_value_cb(sfw_plugin_t *plugin, DBusMessageIter *data) { bool ack = false; dbus_uint64_t tck = 0; dbus_uint32_t val = 0; if( !sfw_backend_parse_data(data, DBUS_TYPE_UINT64, &tck, DBUS_TYPE_UINT32, &val, DBUS_TYPE_INVALID) ) goto EXIT; const sfw_sample_als_t sample = { .als_timestamp = tck, .als_value = val, }; sfw_plugin_handle_sample(plugin, &sample); ack = true; EXIT: return ack; } static bool sfw_backend_ps_value_cb(sfw_plugin_t *plugin, DBusMessageIter *data) { bool ack = false; dbus_uint64_t tck = 0; dbus_uint32_t val = 0; if( !sfw_backend_parse_data(data, DBUS_TYPE_UINT64, &tck, DBUS_TYPE_UINT32, &val, DBUS_TYPE_INVALID) ) goto EXIT; const sfw_sample_ps_t sample = { .ps_timestamp = tck, .ps_value = (val < 1) ? 0 : 10, .ps_withinProximity = (val < 1) ? true : false, }; sfw_plugin_handle_sample(plugin, &sample); ack = true; EXIT: return ack; } static bool sfw_backend_orient_value_cb(sfw_plugin_t *plugin, DBusMessageIter *data) { bool ack = false; dbus_uint64_t tck = 0; dbus_uint32_t val = 0; if( !sfw_backend_parse_data(data, DBUS_TYPE_UINT64, &tck, DBUS_TYPE_UINT32, &val, DBUS_TYPE_INVALID) ) goto EXIT; const sfw_sample_orient_t sample = { .orient_timestamp = tck, .orient_state = val, }; sfw_plugin_handle_sample(plugin, &sample); ack = true; EXIT: return ack; } static bool sfw_backend_accelerometer_value_cb(sfw_plugin_t *plugin, DBusMessageIter *data) { bool ack = false; dbus_uint64_t tck = 0; dbus_int32_t xval = 0; dbus_int32_t yval = 0; dbus_int32_t zval = 0; if( !sfw_backend_parse_data(data, DBUS_TYPE_UINT64, &tck, DBUS_TYPE_INT32, &xval, DBUS_TYPE_INT32, &yval, DBUS_TYPE_INT32, &zval, DBUS_TYPE_INVALID) ) goto EXIT; const sfw_sample_accelerometer_t sample = { .accelerometer_timestamp = tck, .accelerometer_xyz = { .x = xval, .y = yval, .z = zval, }, }; sfw_plugin_handle_sample(plugin, &sample); ack = true; EXIT: return ack; } static bool sfw_backend_compass_value_cb(sfw_plugin_t *plugin, DBusMessageIter *data) { bool ack = false; dbus_uint64_t tck = 0; dbus_int32_t deg = 0; dbus_int32_t raw = 0; dbus_int32_t cor = 0; dbus_int32_t lev = 0; if( !sfw_backend_parse_data(data, DBUS_TYPE_UINT64, &tck, DBUS_TYPE_INT32, °, DBUS_TYPE_INT32, &raw, DBUS_TYPE_INT32, &cor, DBUS_TYPE_INT32, &lev, DBUS_TYPE_INVALID) ) goto EXIT; const sfw_sample_compass_t sample = { .compass_timestamp = tck, .compass_degrees = deg, .compass_raw_degrees = raw, .compass_corrected_degrees = cor, .compass_level = lev, }; sfw_plugin_handle_sample(plugin, &sample); ack = true; EXIT: return ack; } static bool sfw_backend_gyroscope_value_cb(sfw_plugin_t *plugin, DBusMessageIter *data) { bool ack = false; dbus_uint64_t tck = 0; dbus_int32_t xval = 0; dbus_int32_t yval = 0; dbus_int32_t zval = 0; if( !sfw_backend_parse_data(data, DBUS_TYPE_UINT64, &tck, DBUS_TYPE_INT32, &xval, DBUS_TYPE_INT32, &yval, DBUS_TYPE_INT32, &zval, DBUS_TYPE_INVALID) ) goto EXIT; const sfw_sample_gyroscope_t sample = { .gyroscope_timestamp = tck, .gyroscope_xyz = { .x = xval, .y = yval, .z = zval, }, }; sfw_plugin_handle_sample(plugin, &sample); ack = true; EXIT: return ack; } static bool sfw_backend_lid_value_cb(sfw_plugin_t *plugin, DBusMessageIter *data) { bool ack = false; dbus_uint64_t tck = 0; dbus_int32_t type = 0; dbus_uint32_t val = 0; if( !sfw_backend_parse_data(data, DBUS_TYPE_UINT64, &tck, DBUS_TYPE_INT32, &type, DBUS_TYPE_UINT32, &val, DBUS_TYPE_INVALID) ) goto EXIT; const sfw_sample_lid_t sample = { .lid_timestamp = tck, .lid_type = type, .lid_value = val, }; sfw_plugin_handle_sample(plugin, &sample); ack = true; EXIT: return ack; } static bool sfw_backend_humidity_value_cb(sfw_plugin_t *plugin, DBusMessageIter *data) { bool ack = false; dbus_uint64_t tck = 0; dbus_uint32_t val = 0; if( !sfw_backend_parse_data(data, DBUS_TYPE_UINT64, &tck, DBUS_TYPE_UINT32, &val, DBUS_TYPE_INVALID) ) goto EXIT; const sfw_sample_humidity_t sample = { .humidity_timestamp = tck, .humidity_value = val, }; sfw_plugin_handle_sample(plugin, &sample); ack = true; EXIT: return ack; } static bool sfw_backend_magnetometer_value_cb(sfw_plugin_t *plugin, DBusMessageIter *data) { bool ack = false; dbus_uint64_t tck = 0; dbus_int32_t x = 0; dbus_int32_t y = 0; dbus_int32_t z = 0; dbus_int32_t rx = 0; dbus_int32_t ry = 0; dbus_int32_t rz = 0; dbus_int32_t lev = 0; if( !sfw_backend_parse_data(data, DBUS_TYPE_UINT64, &tck, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, DBUS_TYPE_INT32, &z, DBUS_TYPE_INT32, &rx, DBUS_TYPE_INT32, &ry, DBUS_TYPE_INT32, &rz, DBUS_TYPE_INT32, &lev, DBUS_TYPE_INVALID) ) goto EXIT; const sfw_sample_magnetometer_t sample = { .magnetometer_timestamp = tck, .magnetometer_x = x, .magnetometer_y = y, .magnetometer_z = z, .magnetometer_rx = rx, .magnetometer_ry = ry, .magnetometer_rz = rz, .magnetometer_level = lev, }; sfw_plugin_handle_sample(plugin, &sample); ack = true; EXIT: return ack; } static bool sfw_backend_pressure_value_cb(sfw_plugin_t *plugin, DBusMessageIter *data) { bool ack = false; dbus_uint64_t tck = 0; dbus_uint32_t val = 0; if( !sfw_backend_parse_data(data, DBUS_TYPE_UINT64, &tck, DBUS_TYPE_UINT32, &val, DBUS_TYPE_INVALID) ) goto EXIT; const sfw_sample_pressure_t sample = { .pressure_timestamp = tck, .pressure_value = val, }; sfw_plugin_handle_sample(plugin, &sample); ack = true; EXIT: return ack; } static bool sfw_backend_rotation_value_cb(sfw_plugin_t *plugin, DBusMessageIter *data) { bool ack = false; dbus_uint64_t tck = 0; dbus_int32_t xval = 0; dbus_int32_t yval = 0; dbus_int32_t zval = 0; if( !sfw_backend_parse_data(data, DBUS_TYPE_UINT64, &tck, DBUS_TYPE_INT32, &xval, DBUS_TYPE_INT32, &yval, DBUS_TYPE_INT32, &zval, DBUS_TYPE_INVALID) ) goto EXIT; const sfw_sample_rotation_t sample = { .rotation_timestamp = tck, .rotation_xyz = { .x = xval, .y = yval, .z = zval, }, }; sfw_plugin_handle_sample(plugin, &sample); ack = true; EXIT: return ack; } static bool sfw_backend_stepcounter_value_cb(sfw_plugin_t *plugin, DBusMessageIter *data) { bool ack = false; dbus_uint64_t tck = 0; dbus_uint32_t val = 0; if( !sfw_backend_parse_data(data, DBUS_TYPE_UINT64, &tck, DBUS_TYPE_UINT32, &val, DBUS_TYPE_INVALID) ) goto EXIT; const sfw_sample_stepcounter_t sample = { .stepcounter_timestamp = tck, .stepcounter_value = val, }; sfw_plugin_handle_sample(plugin, &sample); ack = true; EXIT: return ack; } static bool sfw_backend_tap_value_cb(sfw_plugin_t *plugin, DBusMessageIter *data) { bool ack = false; dbus_uint64_t tck = 0; dbus_uint32_t direction = 0; dbus_uint32_t type = 0; if( !sfw_backend_parse_data(data, DBUS_TYPE_UINT64, &tck, DBUS_TYPE_UINT32, &direction, DBUS_TYPE_INT32, &type, DBUS_TYPE_INVALID) ) goto EXIT; const sfw_sample_tap_t sample = { .tap_timestamp = tck, .tap_direction = direction, .tap_type = type, }; sfw_plugin_handle_sample(plugin, &sample); ack = true; EXIT: return ack; } static bool sfw_backend_temperature_value_cb(sfw_plugin_t *plugin, DBusMessageIter *data) { bool ack = false; dbus_uint64_t tck = 0; dbus_uint32_t val = 0; if( !sfw_backend_parse_data(data, DBUS_TYPE_UINT64, &tck, DBUS_TYPE_UINT32, &val, DBUS_TYPE_INVALID) ) goto EXIT; const sfw_sample_temperature_t sample = { .temperature_timestamp = tck, .temperature_value = val, }; sfw_plugin_handle_sample(plugin, &sample); ack = true; EXIT: return ack; } /* ------------------------------------------------------------------------- * * Callbacks for interpreting sensor specific change notifications * ------------------------------------------------------------------------- */ /** Callback for handling ambient light events from sensord */ static void sfw_backend_als_sample_cb(sfw_plugin_t *plugin, sfw_notify_t type, const void *sampledata) { (void)plugin; const sfw_sample_als_t *sample = sampledata; static const sfw_sample_als_t default_value = { .als_value = SFW_NOTIFY_DEFAULT_ALS, }; static sfw_sample_als_t cached_value = { .als_value = SFW_NOTIFY_DEFAULT_ALS, }; static bool tracking_active = false; if( sample ) { mce_log(LL_DEBUG, "ALS: UPDATE %s %s", sfw_notify_name(type), sfw_sample_als_repr(sample)); } switch( type ) { default: case NOTIFY_REPEAT: break; case NOTIFY_FORGET: case NOTIFY_RESET: tracking_active = false; break; case NOTIFY_RESTORE: tracking_active = true; break; case NOTIFY_EVDEV: cached_value = *sample; break; case NOTIFY_SENSORD: if( als_from_evdev() ) mce_log(LL_DEBUG, "ignoring sensord input: %s", sfw_sample_als_repr(sample)); else cached_value = *sample; break; } /* Default value is used unless we are in fully working state */ sample = tracking_active ? &cached_value : &default_value ; if( sfw_notify_als_cb ) { mce_log(LL_DEBUG, "ALS: NOTIFY %s %s", sfw_notify_name(type), sfw_sample_als_repr(sample)); sfw_notify_als_cb(sample->als_value); } return; } /** Callback for handling proximity events from sensord */ static void sfw_backend_ps_sample_cb(sfw_plugin_t *plugin, sfw_notify_t type, const void *sampledata) { (void)plugin; const sfw_sample_ps_t *sample = sampledata; static const sfw_sample_ps_t exception_value = { .ps_withinProximity = SFW_NOTIFY_EXCEPTION_PS, }; static const sfw_sample_ps_t default_value = { .ps_withinProximity = SFW_NOTIFY_DEFAULT_PS, }; static sfw_sample_ps_t cached_value = { .ps_value = 10, .ps_withinProximity = SFW_NOTIFY_DEFAULT_PS, }; static bool tracking_active = false; if( sample ) { mce_log(LL_DEBUG, "PS: UPDATE %s %s", sfw_notify_name(type), sfw_sample_ps_repr(sample)); } switch( type ) { default: case NOTIFY_FORGET: case NOTIFY_REPEAT: break; case NOTIFY_RESET: tracking_active = false; break; case NOTIFY_RESTORE: tracking_active = true; break; case NOTIFY_EVDEV: cached_value = *sample; break; case NOTIFY_SENSORD: if( ps_from_evdev() ) mce_log(LL_DEBUG, "ignoring sensord input: %s", sfw_sample_ps_repr(sample)); else cached_value = *sample; break; } if( sfw_exception_is_active() ) { // ps=covered while waiting for sensorfwd to show up sample = &exception_value; } else if( !tracking_active ) { // ps=not-covered while sensorfwd is not running sample = &default_value; } else { // ps=as-reported by sensorfwd or kernel sample = &cached_value; } if( sfw_notify_ps_cb ) { mce_log(LL_DEBUG, "PS: NOTIFY %s %s", sfw_notify_name(type), sfw_sample_ps_repr(sample)); sfw_notify_ps_cb(sample->ps_withinProximity); } return; } /** Callback for handling orientation events from sensord */ static void sfw_backend_orient_sample_cb(sfw_plugin_t *plugin, sfw_notify_t type, const void *sampledata) { static const sfw_sample_orient_t default_value = { .orient_state = SFW_NOTIFY_DEFAULT_ORIENT, }; static sfw_sample_orient_t cached_value = { .orient_state = SFW_NOTIFY_DEFAULT_ORIENT, }; static bool tracking_active = false; (void)plugin; const sfw_sample_orient_t *sample = sampledata; if( sample ) { mce_log(LL_DEBUG, "ORIENT: UPDATE %s %s", sfw_notify_name(type), sfw_sample_orient_repr(sample)); } switch( type ) { default: case NOTIFY_REPEAT: break; case NOTIFY_FORGET: case NOTIFY_RESET: tracking_active = false; cached_value = default_value; break; case NOTIFY_RESTORE: tracking_active = true; break; case NOTIFY_EVDEV: case NOTIFY_SENSORD: cached_value = *sample; break; } /* Default value is used unless we are in fully working state */ sample = tracking_active ? &cached_value : &default_value ; if( sfw_notify_orient_cb ) { mce_log(LL_DEBUG, "ORIENT: NOTIFY %s %s", sfw_notify_name(type), sfw_sample_orient_repr(sample)); sfw_notify_orient_cb(sample->orient_state); } return; } /** Callback for handling accelerometer events from sensord */ static void sfw_backend_accelerometer_sample_cb(sfw_plugin_t *plugin, sfw_notify_t type, const void *sampledata) { (void)plugin; const sfw_sample_accelerometer_t *sample = sampledata; static const sfw_sample_accelerometer_t default_value = { .accelerometer_xyz = { .x = 0, .y = 0, .z = 0, }, }; static sfw_sample_accelerometer_t cached_value = { }; static bool tracking_active = false; switch( type ) { default: case NOTIFY_REPEAT: break; case NOTIFY_FORGET: case NOTIFY_RESET: tracking_active = false; cached_value = default_value; break; case NOTIFY_RESTORE: tracking_active = true; break; case NOTIFY_EVDEV: case NOTIFY_SENSORD: cached_value = *sample; break; } sample = tracking_active ? &cached_value : &default_value; mce_log(LL_DEBUG, "ACCELEROMETER: UPDATE %s %s", sfw_notify_name(type), sfw_sample_accelerometer_repr(sample)); return; } static void sfw_backend_compass_sample_cb(sfw_plugin_t *plugin, sfw_notify_t type, const void *sampledata) { (void)plugin; const sfw_sample_compass_t *sample = sampledata; static const sfw_sample_compass_t default_value = { }; static sfw_sample_compass_t cached_value = { }; static bool tracking_active = false; switch( type ) { default: case NOTIFY_REPEAT: break; case NOTIFY_FORGET: case NOTIFY_RESET: tracking_active = false; cached_value = default_value; break; case NOTIFY_RESTORE: tracking_active = true; break; case NOTIFY_EVDEV: case NOTIFY_SENSORD: cached_value = *sample; break; } sample = tracking_active ? &cached_value : &default_value; mce_log(LL_DEBUG, "COMPASS: UPDATE %s %s", sfw_notify_name(type), sfw_sample_compass_repr(sample)); return; } static void sfw_backend_gyroscope_sample_cb(sfw_plugin_t *plugin, sfw_notify_t type, const void *sampledata) { (void)plugin; const sfw_sample_gyroscope_t *sample = sampledata; static const sfw_sample_gyroscope_t default_value = { }; static sfw_sample_gyroscope_t cached_value = { }; static bool tracking_active = false; switch( type ) { default: case NOTIFY_REPEAT: break; case NOTIFY_FORGET: case NOTIFY_RESET: tracking_active = false; cached_value = default_value; break; case NOTIFY_RESTORE: tracking_active = true; break; case NOTIFY_EVDEV: case NOTIFY_SENSORD: cached_value = *sample; break; } sample = tracking_active ? &cached_value : &default_value; mce_log(LL_DEBUG, "GYROSCOPE: UPDATE %s %s", sfw_notify_name(type), sfw_sample_gyroscope_repr(sample)); return; } static void sfw_backend_lid_sample_cb(sfw_plugin_t *plugin, sfw_notify_t type, const void *sampledata) { (void)plugin; const sfw_sample_lid_t *sample = sampledata; static const sfw_sample_lid_t default_value = { }; static sfw_sample_lid_t cached_value = { }; static bool tracking_active = false; switch( type ) { default: case NOTIFY_REPEAT: break; case NOTIFY_FORGET: case NOTIFY_RESET: tracking_active = false; cached_value = default_value; break; case NOTIFY_RESTORE: tracking_active = true; break; case NOTIFY_EVDEV: case NOTIFY_SENSORD: cached_value = *sample; break; } sample = tracking_active ? &cached_value : &default_value; mce_log(LL_DEBUG, "LID: UPDATE %s %s", sfw_notify_name(type), sfw_sample_lid_repr(sample)); return; } static void sfw_backend_humidity_sample_cb(sfw_plugin_t *plugin, sfw_notify_t type, const void *sampledata) { (void)plugin; const sfw_sample_humidity_t *sample = sampledata; static const sfw_sample_humidity_t default_value = { }; static sfw_sample_humidity_t cached_value = { }; static bool tracking_active = false; switch( type ) { default: case NOTIFY_REPEAT: break; case NOTIFY_FORGET: case NOTIFY_RESET: tracking_active = false; cached_value = default_value; break; case NOTIFY_RESTORE: tracking_active = true; break; case NOTIFY_EVDEV: case NOTIFY_SENSORD: cached_value = *sample; break; } sample = tracking_active ? &cached_value : &default_value; mce_log(LL_DEBUG, "HUMIDITY: UPDATE %s %s", sfw_notify_name(type), sfw_sample_humidity_repr(sample)); return; } static void sfw_backend_magnetometer_sample_cb(sfw_plugin_t *plugin, sfw_notify_t type, const void *sampledata) { (void)plugin; const sfw_sample_magnetometer_t *sample = sampledata; static const sfw_sample_magnetometer_t default_value = { }; static sfw_sample_magnetometer_t cached_value = { }; static bool tracking_active = false; switch( type ) { default: case NOTIFY_REPEAT: break; case NOTIFY_FORGET: case NOTIFY_RESET: tracking_active = false; cached_value = default_value; break; case NOTIFY_RESTORE: tracking_active = true; break; case NOTIFY_EVDEV: case NOTIFY_SENSORD: cached_value = *sample; break; } sample = tracking_active ? &cached_value : &default_value; mce_log(LL_DEBUG, "MAGNETOMETER: UPDATE %s %s", sfw_notify_name(type), sfw_sample_magnetometer_repr(sample)); return; } static void sfw_backend_pressure_sample_cb(sfw_plugin_t *plugin, sfw_notify_t type, const void *sampledata) { (void)plugin; const sfw_sample_pressure_t *sample = sampledata; static const sfw_sample_pressure_t default_value = { }; static sfw_sample_pressure_t cached_value = { }; static bool tracking_active = false; switch( type ) { default: case NOTIFY_REPEAT: break; case NOTIFY_FORGET: case NOTIFY_RESET: tracking_active = false; cached_value = default_value; break; case NOTIFY_RESTORE: tracking_active = true; break; case NOTIFY_EVDEV: case NOTIFY_SENSORD: cached_value = *sample; break; } sample = tracking_active ? &cached_value : &default_value; mce_log(LL_DEBUG, "PRESSURE: UPDATE %s %s", sfw_notify_name(type), sfw_sample_pressure_repr(sample)); return; } static void sfw_backend_rotation_sample_cb(sfw_plugin_t *plugin, sfw_notify_t type, const void *sampledata) { (void)plugin; const sfw_sample_rotation_t *sample = sampledata; static const sfw_sample_rotation_t default_value = { }; static sfw_sample_rotation_t cached_value = { }; static bool tracking_active = false; switch( type ) { default: case NOTIFY_REPEAT: break; case NOTIFY_FORGET: case NOTIFY_RESET: tracking_active = false; cached_value = default_value; break; case NOTIFY_RESTORE: tracking_active = true; break; case NOTIFY_EVDEV: case NOTIFY_SENSORD: cached_value = *sample; break; } sample = tracking_active ? &cached_value : &default_value; mce_log(LL_DEBUG, "ROTATION: UPDATE %s %s", sfw_notify_name(type), sfw_sample_rotation_repr(sample)); return; } static void sfw_backend_stepcounter_sample_cb(sfw_plugin_t *plugin, sfw_notify_t type, const void *sampledata) { (void)plugin; const sfw_sample_stepcounter_t *sample = sampledata; static const sfw_sample_stepcounter_t default_value = { }; static sfw_sample_stepcounter_t cached_value = { }; static bool tracking_active = false; switch( type ) { default: case NOTIFY_REPEAT: break; case NOTIFY_FORGET: case NOTIFY_RESET: tracking_active = false; cached_value = default_value; break; case NOTIFY_RESTORE: tracking_active = true; break; case NOTIFY_EVDEV: case NOTIFY_SENSORD: cached_value = *sample; break; } sample = tracking_active ? &cached_value : &default_value; mce_log(LL_DEBUG, "STEPCOUNTER: UPDATE %s %s", sfw_notify_name(type), sfw_sample_stepcounter_repr(sample)); return; } static void sfw_backend_tap_sample_cb(sfw_plugin_t *plugin, sfw_notify_t type, const void *sampledata) { (void)plugin; const sfw_sample_tap_t *sample = sampledata; static const sfw_sample_tap_t default_value = { }; static sfw_sample_tap_t cached_value = { }; static bool tracking_active = false; switch( type ) { default: case NOTIFY_REPEAT: break; case NOTIFY_FORGET: case NOTIFY_RESET: tracking_active = false; cached_value = default_value; break; case NOTIFY_RESTORE: tracking_active = true; break; case NOTIFY_EVDEV: case NOTIFY_SENSORD: cached_value = *sample; break; } sample = tracking_active ? &cached_value : &default_value; mce_log(LL_DEBUG, "TAP: UPDATE %s %s", sfw_notify_name(type), sfw_sample_tap_repr(sample)); return; } static void sfw_backend_temperature_sample_cb(sfw_plugin_t *plugin, sfw_notify_t type, const void *sampledata) { (void)plugin; const sfw_sample_temperature_t *sample = sampledata; static const sfw_sample_temperature_t default_value = { }; static sfw_sample_temperature_t cached_value = { }; static bool tracking_active = false; switch( type ) { default: case NOTIFY_REPEAT: break; case NOTIFY_FORGET: case NOTIFY_RESET: tracking_active = false; cached_value = default_value; break; case NOTIFY_RESTORE: tracking_active = true; break; case NOTIFY_EVDEV: case NOTIFY_SENSORD: cached_value = *sample; break; } sample = tracking_active ? &cached_value : &default_value; mce_log(LL_DEBUG, "TEMPERATURE: UPDATE %s %s", sfw_notify_name(type), sfw_sample_temperature_repr(sample)); return; } // ---------------------------------------------------------------- /** Data and callback functions for all sensors */ static const sfw_backend_t sfw_backend_lut[SFW_SENSOR_ID_COUNT] = { [SFW_SENSOR_ID_PS] = { .be_sensor_name = SFW_SENSOR_NAME_PS, .be_sensor_object = 0, .be_sensor_interface = SFW_SENSOR_INTERFACE_PS, .be_sample_size = sizeof(sfw_sample_ps_t), .be_value_cb = sfw_backend_ps_value_cb, .be_sample_cb = sfw_backend_ps_sample_cb, .be_value_method = SFW_SENSOR_METHOD_READ_PS, }, [SFW_SENSOR_ID_ALS] = { .be_sensor_name = SFW_SENSOR_NAME_ALS, .be_sensor_object = 0, .be_sensor_interface = SFW_SENSOR_INTERFACE_ALS, .be_sample_size = sizeof(sfw_sample_als_t), .be_value_cb = sfw_backend_als_value_cb, .be_sample_cb = sfw_backend_als_sample_cb, .be_value_method = SFW_SENSOR_METHOD_READ_ALS, }, [SFW_SENSOR_ID_ORIENT] = { .be_sensor_name = SFW_SENSOR_NAME_ORIENT, .be_sensor_object = 0, .be_sensor_interface = SFW_SENSOR_INTERFACE_ORIENT, .be_sample_size = sizeof(sfw_sample_orient_t), .be_value_cb = sfw_backend_orient_value_cb, .be_sample_cb = sfw_backend_orient_sample_cb, .be_value_method = SFW_SENSOR_METHOD_READ_ORIENT, }, [SFW_SENSOR_ID_ACCELEROMETER] = { .be_sensor_name = SFW_SENSOR_NAME_ACCELEROMETER, .be_sensor_object = 0, .be_sensor_interface = SFW_SENSOR_INTERFACE_ACCELEROMETER, .be_sample_size = sizeof(sfw_sample_accelerometer_t), .be_value_cb = sfw_backend_accelerometer_value_cb, .be_sample_cb = sfw_backend_accelerometer_sample_cb, .be_value_method = SFW_SENSOR_METHOD_READ_ACCELEROMETER, }, [SFW_SENSOR_ID_COMPASS] = { .be_sensor_name = SFW_SENSOR_NAME_COMPASS, .be_sensor_object = 0, .be_sensor_interface = SFW_SENSOR_INTERFACE_COMPASS, .be_sample_size = sizeof(sfw_sample_compass_t), .be_value_cb = sfw_backend_compass_value_cb, .be_sample_cb = sfw_backend_compass_sample_cb, .be_value_method = SFW_SENSOR_METHOD_READ_COMPASS, }, [SFW_SENSOR_ID_GYROSCOPE] = { .be_sensor_name = SFW_SENSOR_NAME_GYROSCOPE, .be_sensor_object = 0, .be_sensor_interface = SFW_SENSOR_INTERFACE_GYROSCOPE, .be_sample_size = sizeof(sfw_sample_gyroscope_t), .be_value_cb = sfw_backend_gyroscope_value_cb, .be_sample_cb = sfw_backend_gyroscope_sample_cb, .be_value_method = SFW_SENSOR_METHOD_READ_GYROSCOPE, }, [SFW_SENSOR_ID_LID] = { .be_sensor_name = SFW_SENSOR_NAME_LID, .be_sensor_object = 0, .be_sensor_interface = SFW_SENSOR_INTERFACE_LID, .be_sample_size = sizeof(sfw_sample_lid_t), .be_value_cb = sfw_backend_lid_value_cb, .be_sample_cb = sfw_backend_lid_sample_cb, .be_value_method = SFW_SENSOR_METHOD_READ_LID, }, [SFW_SENSOR_ID_HUMIDITY] = { .be_sensor_name = SFW_SENSOR_NAME_HUMIDITY, .be_sensor_object = 0, .be_sensor_interface = SFW_SENSOR_INTERFACE_HUMIDITY, .be_sample_size = sizeof(sfw_sample_humidity_t), .be_value_cb = sfw_backend_humidity_value_cb, .be_sample_cb = sfw_backend_humidity_sample_cb, .be_value_method = SFW_SENSOR_METHOD_READ_HUMIDITY, }, [SFW_SENSOR_ID_MAGNETOMETER] = { .be_sensor_name = SFW_SENSOR_NAME_MAGNETOMETER, .be_sensor_object = 0, .be_sensor_interface = SFW_SENSOR_INTERFACE_MAGNETOMETER, .be_sample_size = sizeof(sfw_sample_magnetometer_t), .be_value_cb = sfw_backend_magnetometer_value_cb, .be_sample_cb = sfw_backend_magnetometer_sample_cb, .be_value_method = SFW_SENSOR_METHOD_READ_MAGNETOMETER, }, [SFW_SENSOR_ID_PRESSURE] = { .be_sensor_name = SFW_SENSOR_NAME_PRESSURE, .be_sensor_object = 0, .be_sensor_interface = SFW_SENSOR_INTERFACE_PRESSURE, .be_sample_size = sizeof(sfw_sample_pressure_t), .be_value_cb = sfw_backend_pressure_value_cb, .be_sample_cb = sfw_backend_pressure_sample_cb, .be_value_method = SFW_SENSOR_METHOD_READ_PRESSURE, }, [SFW_SENSOR_ID_ROTATION] = { .be_sensor_name = SFW_SENSOR_NAME_ROTATION, .be_sensor_object = 0, .be_sensor_interface = SFW_SENSOR_INTERFACE_ROTATION, .be_sample_size = sizeof(sfw_sample_rotation_t), .be_value_cb = sfw_backend_rotation_value_cb, .be_sample_cb = sfw_backend_rotation_sample_cb, .be_value_method = SFW_SENSOR_METHOD_READ_ROTATION, }, [SFW_SENSOR_ID_STEPCOUNTER] = { .be_sensor_name = SFW_SENSOR_NAME_STEPCOUNTER, .be_sensor_object = 0, .be_sensor_interface = SFW_SENSOR_INTERFACE_STEPCOUNTER, .be_sample_size = sizeof(sfw_sample_stepcounter_t), .be_value_cb = sfw_backend_stepcounter_value_cb, .be_sample_cb = sfw_backend_stepcounter_sample_cb, .be_value_method = SFW_SENSOR_METHOD_READ_STEPCOUNTER, }, [SFW_SENSOR_ID_TAP] = { .be_sensor_name = SFW_SENSOR_NAME_TAP, .be_sensor_object = 0, .be_sensor_interface = SFW_SENSOR_INTERFACE_TAP, .be_sample_size = sizeof(sfw_sample_tap_t), .be_value_cb = sfw_backend_tap_value_cb, .be_sample_cb = sfw_backend_tap_sample_cb, .be_value_method = SFW_SENSOR_METHOD_READ_TAP, }, [SFW_SENSOR_ID_TEMPERATURE] = { .be_sensor_name = SFW_SENSOR_NAME_TEMPERATURE, .be_sensor_object = 0, .be_sensor_interface = SFW_SENSOR_INTERFACE_TEMPERATURE, .be_sample_size = sizeof(sfw_sample_temperature_t), .be_value_cb = sfw_backend_temperature_value_cb, .be_sample_cb = sfw_backend_temperature_sample_cb, .be_value_method = SFW_SENSOR_METHOD_READ_TEMPERATURE, }, }; /* ========================================================================= * * SENSORFW_HELPERS * ========================================================================= */ /* Set file descriptor to blocking/unblocking state */ static bool sfw_socket_set_blocking(int fd, bool blocking) { bool ok = false; int flags = 0; if( (flags = fcntl(fd, F_GETFL, 0)) == -1 ) { mce_log(LL_ERR, "could not get socket flags"); goto EXIT; } if( blocking ) flags &= ~O_NONBLOCK; else flags |= O_NONBLOCK; if( fcntl(fd, F_SETFL, flags) == -1 ){ mce_log(LL_ERR, "could not set socket flags"); goto EXIT; } ok = true; EXIT: return ok; } /** Get a sensord data socket connection file descriptor */ static int sfw_socket_open(void) { bool ok = false; int fd = -1; struct sockaddr_un sa; socklen_t sa_len; /* create socket */ if( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ) { mce_log(LL_ERR, "could not open local domain socket"); goto EXIT; } /* make it non-blocking -> connect() will return immediately */ if( !sfw_socket_set_blocking(fd, false) ) goto EXIT; /* connect to daemon */ memset(&sa, 0, sizeof sa); sa.sun_family = AF_UNIX; snprintf(sa.sun_path, sizeof sa.sun_path, "%s", SENSORFW_DATA_SOCKET); sa_len = strchr(sa.sun_path, 0) + 1 - (char *)&sa; if( connect(fd, (struct sockaddr *)&sa, sa_len) == -1 ) { mce_log(LL_ERR, "could not connect to %s: %m", SENSORFW_DATA_SOCKET); goto EXIT; } ok = true; EXIT: /* all or nothing */ if( !ok && fd != -1 ) close(fd), fd = -1; return fd; } /** Add a glib I/O notification for a file descriptor */ static guint sfw_socket_add_notify(int fd, bool close_on_unref, GIOCondition cnd, GIOFunc io_cb, gpointer aptr) { guint wid = 0; GIOChannel *chn = 0; if( !(chn = g_io_channel_unix_new(fd)) ) { goto EXIT; } g_io_channel_set_close_on_unref(chn, close_on_unref); cnd |= G_IO_ERR | G_IO_HUP | G_IO_NVAL; if( !(wid = g_io_add_watch(chn, cnd, io_cb, aptr)) ) goto EXIT; EXIT: if( chn != 0 ) g_io_channel_unref(chn); return wid; } /* ========================================================================= * * SENSORFW_REPORTING * ========================================================================= */ /** Translate reporting state to human readable form */ static const char * sfw_reporting_state_name(sfw_reporting_state_t state) { static const char *const lut[REPORTING_NUMSTATES] = { [REPORTING_INITIAL] = "INITIAL", [REPORTING_IDLE] = "IDLE", [REPORTING_RETHINK] = "RETHINK", [REPORTING_ENABLING] = "ENABLING", [REPORTING_ENABLED] = "ENABLED", [REPORTING_DISABLING] = "DISABLING", [REPORTING_DISABLED] = "DISABLED", [REPORTING_ERROR] = "ERROR", }; return (state < REPORTING_NUMSTATES) ? lut[state] : 0; } /** Create sensor start/stop state machine object */ static sfw_reporting_t * sfw_reporting_create(sfw_plugin_t *plugin) { sfw_reporting_t *self = calloc(1, sizeof *self); self->rep_plugin = plugin; self->rep_state = REPORTING_INITIAL; self->rep_change_pc = 0; self->rep_value_pc = 0; self->rep_target = false; self->rep_retry_id = 0; return self; } /** Delete sensor start/stop state machine object */ static void sfw_reporting_delete(sfw_reporting_t *self) { // using NULL self pointer explicitly allowed if( self ) { sfw_reporting_trans(self, REPORTING_INITIAL); self->rep_plugin = 0; free(self); } } /** Set sensor start/stop target state */ static void sfw_reporting_set_target(sfw_reporting_t *self, bool enable) { if( self->rep_target != enable ) { self->rep_target = enable; sfw_reporting_do_rethink(self); } } /** Cancel pending sensor start/stop method call */ static void sfw_reporting_cancel_change(sfw_reporting_t *self) { if( self->rep_change_pc ) { dbus_pending_call_cancel(self->rep_change_pc); dbus_pending_call_unref(self->rep_change_pc); self->rep_change_pc = 0; } } /** Cancel pending sensor initial value method call */ static void sfw_reporting_cancel_value(sfw_reporting_t *self) { if( self->rep_value_pc ) { dbus_pending_call_cancel(self->rep_value_pc); dbus_pending_call_unref(self->rep_value_pc); self->rep_value_pc = 0; } } /** Cancel pending ipc retry timer */ static void sfw_reporting_cancel_retry(sfw_reporting_t *self) { if( self->rep_retry_id ) { g_source_remove(self->rep_retry_id); self->rep_retry_id = 0; } } /** Make a state transition */ static void sfw_reporting_trans(sfw_reporting_t *self, sfw_reporting_state_t state) { dbus_int32_t sid = sfw_plugin_get_session_id(self->rep_plugin); if( self->rep_state == state ) goto EXIT; sfw_reporting_cancel_change(self); sfw_reporting_cancel_value(self); sfw_reporting_cancel_retry(self); mce_log(LL_DEBUG, "reporting(%s): %s -> %s", sfw_plugin_get_sensor_name(self->rep_plugin), sfw_reporting_state_name(self->rep_state), sfw_reporting_state_name(state)); self->rep_state = state; switch( self->rep_state ) { case REPORTING_RETHINK: if( self->rep_target ) sfw_reporting_trans(self, REPORTING_ENABLING); else sfw_reporting_trans(self, REPORTING_DISABLING); break; case REPORTING_ENABLING: // resend last known state sfw_plugin_restore_value(self->rep_plugin); dbus_send_ex(SENSORFW_SERVICE, sfw_plugin_get_sensor_object(self->rep_plugin), sfw_plugin_get_sensor_interface(self->rep_plugin), SFW_SENSOR_METHOD_START, sfw_reporting_change_cb, self, 0, &self->rep_change_pc, DBUS_TYPE_INT32, &sid, DBUS_TYPE_INVALID); break; case REPORTING_DISABLING: // optional: switch to sensor stopped value sfw_plugin_forget_value(self->rep_plugin); dbus_send_ex(SENSORFW_SERVICE, sfw_plugin_get_sensor_object(self->rep_plugin), sfw_plugin_get_sensor_interface(self->rep_plugin), SFW_SENSOR_METHOD_STOP, sfw_reporting_change_cb, self, 0, &self->rep_change_pc, DBUS_TYPE_INT32, &sid, DBUS_TYPE_INVALID); break; case REPORTING_ENABLED: if( !sfw_plugin_get_value_method(self->rep_plugin) ) { mce_log(LL_DEBUG, "reporting(%s): skip state query", sfw_plugin_get_sensor_name(self->rep_plugin)); break; } dbus_send_ex(SENSORFW_SERVICE, sfw_plugin_get_sensor_object(self->rep_plugin), sfw_plugin_get_sensor_interface(self->rep_plugin), sfw_plugin_get_value_method(self->rep_plugin), sfw_reporting_value_cb, self, 0, &self->rep_value_pc, DBUS_TYPE_INVALID); break; case REPORTING_DISABLED: // NOP break; case REPORTING_ERROR: self->rep_retry_id = g_timeout_add(SENSORFW_RETRY_DELAY_MS, sfw_reporting_retry_cb, self); break; default: case REPORTING_IDLE: case REPORTING_INITIAL: // reset sensor value to default state sfw_plugin_reset_value(self->rep_plugin); break; } EXIT: return; } /** Handle reply to initial sensor value query */ static void sfw_reporting_value_cb(DBusPendingCall *pc, void *aptr) { sfw_reporting_t *self = aptr; DBusMessage *rsp = 0; DBusError err = DBUS_ERROR_INIT; bool ack = false; if( pc != self->rep_value_pc ) goto EXIT; dbus_pending_call_unref(self->rep_value_pc), self->rep_value_pc = 0; if( !(rsp = dbus_pending_call_steal_reply(pc)) ) { mce_log(LL_ERR, "reporting(%s): no reply", sfw_plugin_get_sensor_name(self->rep_plugin)); goto EXIT; } if( dbus_set_error_from_message(&err, rsp) ) { mce_log(LL_ERR, "reporting(%s): error reply: %s: %s", sfw_plugin_get_sensor_name(self->rep_plugin), err.name, err.message); goto EXIT; } mce_log(LL_DEBUG, "reporting(%s): reply received", sfw_plugin_get_sensor_name(self->rep_plugin)); DBusMessageIter body, data; if( !dbus_message_iter_init(rsp, &body) ) goto PARSE_FAILED; if( !mce_dbus_iter_get_struct(&body, &data) ) goto PARSE_FAILED; if( !(ack = sfw_plugin_handle_value(self->rep_plugin, &data)) ) goto PARSE_FAILED; ack = true; goto EXIT; PARSE_FAILED: mce_log(LL_ERR, "reporting(%s): parse error", sfw_plugin_get_sensor_name(self->rep_plugin)); EXIT: if( rsp ) dbus_message_unref(rsp); dbus_error_free(&err); if( !ack && self->rep_state == REPORTING_ENABLED ) sfw_reporting_trans(self, REPORTING_ERROR); return; } /** Handle reply to start/stop sensor request */ static void sfw_reporting_change_cb(DBusPendingCall *pc, void *aptr) { sfw_reporting_t *self = aptr; DBusMessage *rsp = 0; DBusError err = DBUS_ERROR_INIT; dbus_bool_t ack = FALSE; if( pc != self->rep_change_pc ) goto EXIT; dbus_pending_call_unref(self->rep_change_pc), self->rep_change_pc = 0; if( !(rsp = dbus_pending_call_steal_reply(pc)) ) { mce_log(LL_ERR, "reporting(%s): no reply", sfw_plugin_get_sensor_name(self->rep_plugin)); goto EXIT; } if( dbus_set_error_from_message(&err, rsp) ) { mce_log(LL_ERR, "reporting(%s): error reply: %s: %s", sfw_plugin_get_sensor_name(self->rep_plugin), err.name, err.message); goto EXIT; } /* there is no payload in the reply, so if we do not get * an error reply -> assume everything is ok */ ack = true; EXIT: mce_log(LL_DEBUG, "reporting(%s): ack=%d", sfw_plugin_get_sensor_name(self->rep_plugin), ack); if( self->rep_state == REPORTING_ENABLING || self->rep_state == REPORTING_DISABLING ) { if( !ack ) sfw_reporting_trans(self, REPORTING_ERROR); else if( self->rep_state == REPORTING_ENABLING ) sfw_reporting_trans(self, REPORTING_ENABLED); else sfw_reporting_trans(self, REPORTING_DISABLED); } if( rsp ) dbus_message_unref(rsp); dbus_error_free(&err); return; } /** Handle triggering of start/stop retry timer */ static gboolean sfw_reporting_retry_cb(gpointer aptr) { sfw_reporting_t *self = aptr; if( !self->rep_retry_id ) goto EXIT; self->rep_retry_id = 0; mce_log(LL_WARN, "reporting(%s): retry", sfw_plugin_get_sensor_name(self->rep_plugin)); if( self->rep_state == REPORTING_ERROR ) sfw_reporting_trans(self, REPORTING_RETHINK); EXIT: return FALSE; } /** Choose between sensor start() and stop() */ static void sfw_reporting_do_rethink(sfw_reporting_t *self) { switch( self->rep_state ) { case REPORTING_IDLE: case REPORTING_INITIAL: // nop break; default: sfw_reporting_trans(self, REPORTING_RETHINK); break; } } /** Initiate sensor start() request */ static void sfw_reporting_do_start(sfw_reporting_t *self) { switch( self->rep_state ) { case REPORTING_IDLE: case REPORTING_INITIAL: sfw_reporting_trans(self, REPORTING_RETHINK); break; default: // nop break; } } /** Initiate sensor stop() request */ static void sfw_reporting_do_reset(sfw_reporting_t *self) { sfw_reporting_trans(self, REPORTING_IDLE); } /* ========================================================================= * * SENSORFW_OVERRIDE * ========================================================================= */ /** Translate override state to human readable form */ static const char * sfw_override_state_name(sfw_override_state_t state) { static const char *const lut[OVERRIDE_NUMSTATES] = { [OVERRIDE_INITIAL] = "INITIAL", [OVERRIDE_IDLE] = "IDLE", [OVERRIDE_RETHINK] = "RETHINK", [OVERRIDE_ENABLING] = "ENABLING", [OVERRIDE_ENABLED] = "ENABLED", [OVERRIDE_DISABLING] = "DISABLING", [OVERRIDE_DISABLED] = "DISABLED", [OVERRIDE_ERROR] = "ERROR", [OVERRIDE_NA] = "NA", }; return (state < OVERRIDE_NUMSTATES) ? lut[state] : 0; } /** Create sensor standby set/unset state machine object */ static sfw_override_t * sfw_override_create(sfw_plugin_t *plugin) { sfw_override_t *self = calloc(1, sizeof *self); self->ovr_plugin = plugin; self->ovr_state = OVERRIDE_INITIAL; self->ovr_start_pc = 0; self->ovr_target = false; self->ovr_retry_id = 0; return self; } /** Delete sensor standby set/unset state machine object */ static void sfw_override_delete(sfw_override_t *self) { // using NULL self pointer explicitly allowed if( self ) { sfw_override_trans(self, OVERRIDE_INITIAL); self->ovr_plugin = 0; free(self); } } /** Set sensor standby override set/unset target state */ static void sfw_override_set_target(sfw_override_t *self, bool enable) { if( self->ovr_target != enable ) { self->ovr_target = enable; sfw_override_do_rethink(self); } } /** Cancel pending sensor standby override set/unset method call */ static void sfw_override_cancel_start(sfw_override_t *self) { if( self->ovr_start_pc ) { dbus_pending_call_cancel(self->ovr_start_pc); dbus_pending_call_unref(self->ovr_start_pc); self->ovr_start_pc = 0; } } /** Cancel pending ipc retry timer */ static void sfw_override_cancel_retry(sfw_override_t *self) { if( self->ovr_retry_id ) { g_source_remove(self->ovr_retry_id); self->ovr_retry_id = 0; } } /** Make a state transition */ static void sfw_override_trans(sfw_override_t *self, sfw_override_state_t state) { dbus_int32_t sid = sfw_plugin_get_session_id(self->ovr_plugin); dbus_bool_t val = self->ovr_target; if( self->ovr_state == state ) goto EXIT; sfw_override_cancel_start(self); sfw_override_cancel_retry(self); mce_log(LL_DEBUG, "override(%s): %s -> %s", sfw_plugin_get_sensor_name(self->ovr_plugin), sfw_override_state_name(self->ovr_state), sfw_override_state_name(state)); self->ovr_state = state; switch( self->ovr_state ) { case OVERRIDE_RETHINK: if( self->ovr_target ) sfw_override_trans(self, OVERRIDE_ENABLING); else sfw_override_trans(self, OVERRIDE_DISABLING); break; case OVERRIDE_ENABLING: case OVERRIDE_DISABLING: dbus_send_ex(SENSORFW_SERVICE, sfw_plugin_get_sensor_object(self->ovr_plugin), sfw_plugin_get_sensor_interface(self->ovr_plugin), SFW_SENSOR_METHOD_SET_OVERRIDE, sfw_override_start_cb, self, 0, &self->ovr_start_pc, DBUS_TYPE_INT32, &sid, DBUS_TYPE_BOOLEAN, &val, DBUS_TYPE_INVALID); break; case OVERRIDE_ENABLED: case OVERRIDE_DISABLED: // NOP break; case OVERRIDE_ERROR: self->ovr_retry_id = g_timeout_add(SENSORFW_RETRY_DELAY_MS, sfw_override_retry_cb, self); break; default: case OVERRIDE_NA: case OVERRIDE_IDLE: case OVERRIDE_INITIAL: // NOP break; } EXIT: return; } /** Handle reply to sensor standby override set/unset method call */ static void sfw_override_start_cb(DBusPendingCall *pc, void *aptr) { sfw_override_t *self = aptr; DBusMessage *rsp = 0; DBusError err = DBUS_ERROR_INIT; dbus_bool_t ack = FALSE; dbus_bool_t nak = FALSE; if( pc != self->ovr_start_pc ) goto EXIT; dbus_pending_call_unref(self->ovr_start_pc), self->ovr_start_pc = 0; if( !(rsp = dbus_pending_call_steal_reply(pc)) ) { mce_log(LL_ERR, "override(%s): no reply", sfw_plugin_get_sensor_name(self->ovr_plugin)); } else if( dbus_set_error_from_message(&err, rsp) ) { mce_log(LL_ERR, "override(%s): error reply: %s: %s", sfw_plugin_get_sensor_name(self->ovr_plugin), err.name, err.message); } else if( !dbus_message_get_args(rsp, &err, DBUS_TYPE_BOOLEAN, &ack, DBUS_TYPE_INVALID) ) { mce_log(LL_ERR, "override(%s): parse error: %s: %s", sfw_plugin_get_sensor_name(self->ovr_plugin), err.name, err.message); } else if( !ack ) { mce_log(LL_ERR, "override(%s): failed", sfw_plugin_get_sensor_name(self->ovr_plugin)); nak = true; } else { mce_log(LL_DEBUG, "override(%s): succeeded", sfw_plugin_get_sensor_name(self->ovr_plugin)); } if( self->ovr_state == OVERRIDE_ENABLING || self->ovr_state == OVERRIDE_DISABLING ) { if( nak ) sfw_override_trans(self, OVERRIDE_NA); else if( !ack ) sfw_override_trans(self, OVERRIDE_ERROR); else if( self->ovr_state == OVERRIDE_ENABLING ) sfw_override_trans(self, OVERRIDE_ENABLED); else sfw_override_trans(self, OVERRIDE_DISABLED); } EXIT: if( rsp ) dbus_message_unref(rsp); dbus_error_free(&err); return; } /** Handle triggering of start/stop retry timer */ static gboolean sfw_override_retry_cb(gpointer aptr) { sfw_override_t *self = aptr; if( !self->ovr_retry_id ) goto EXIT; self->ovr_retry_id = 0; mce_log(LL_WARN, "override(%s): retry", sfw_plugin_get_sensor_name(self->ovr_plugin)); if( self->ovr_state == OVERRIDE_ERROR ) sfw_override_trans(self, OVERRIDE_RETHINK); EXIT: return FALSE; } /** Choose between sensor standby override set/unset */ static void sfw_override_do_rethink(sfw_override_t *self) { switch( self->ovr_state ) { case OVERRIDE_IDLE: case OVERRIDE_INITIAL: // nop break; default: sfw_override_trans(self, OVERRIDE_RETHINK); break; } } /** Initiate sensor standby override set request */ static void sfw_override_do_start(sfw_override_t *self) { switch( self->ovr_state ) { case OVERRIDE_IDLE: case OVERRIDE_INITIAL: sfw_override_trans(self, OVERRIDE_RETHINK); break; default: // nop break; } } /** Initiate sensor standby override unset request */ static void sfw_override_do_reset(sfw_override_t *self) { sfw_override_trans(self, OVERRIDE_IDLE); } /* ========================================================================= * * SENSORFW_CONNECTION * ========================================================================= */ /** Translate connection state to human readable form */ static const char * sfw_connection_state_name(sfw_connection_state_t state) { static const char *const lut[CONNECTION_NUMSTATES] = { [CONNECTION_INITIAL] = "INITIAL", [CONNECTION_IDLE] = "IDLE", [CONNECTION_CONNECTING] = "CONNECTING", [CONNECTION_REGISTERING] = "REGISTERING", [CONNECTION_CONNECTED] = "CONNECTED", [CONNECTION_ERROR] = "ERROR", }; return (state < CONNECTION_NUMSTATES) ? lut[state] : 0; } /** Get cached session id granted by sensord */ static int sfw_connection_get_session_id(const sfw_connection_t *self) { return sfw_plugin_get_session_id(self->con_plugin); } /** Handle array of sensor events sent by sensord */ static bool sfw_connection_handle_samples(sfw_connection_t *self, char *data, size_t size) { bool res = false; uint32_t count = 0; uint32_t block = sfw_plugin_get_sample_size(self->con_plugin); void *sample = 0; while( size > 0 ) { if( size < sizeof count ) { mce_log(LL_ERR, "connection(%s): received invalid packet", sfw_plugin_get_sensor_name(self->con_plugin)); goto EXIT; } memcpy(&count, data, sizeof count); data += sizeof count; size -= sizeof count; if( size < count * block ) { mce_log(LL_ERR, "connection(%s): received invalid sample", sfw_plugin_get_sensor_name(self->con_plugin)); goto EXIT; } if( count > 0 ) { sample = data + block * (count - 1); data += block * count; size -= block * count; } } if( !sample ) { mce_log(LL_ERR, "connection(%s): no sample was received", sfw_plugin_get_sensor_name(self->con_plugin)); goto EXIT; } res = true; sfw_plugin_handle_sample(self->con_plugin, sample); EXIT: return res; } /** Do a handshake with sensord after opening a data connection */ static bool sfw_connection_tx_req(sfw_connection_t *self) { bool res = false; int32_t sid = sfw_connection_get_session_id(self); errno = 0; if( write(self->con_fd, &sid, sizeof sid) != sizeof sid ) { mce_log(LL_ERR, "connection(%s): handshake write failed: %m", sfw_plugin_get_sensor_name(self->con_plugin)); goto EXIT; } res = true; EXIT: return res; } /** Handle reply to handshake from sensord */ static bool sfw_connection_rx_ack(sfw_connection_t *self) { bool res = false; char ack = 0; errno = 0; if( read(self->con_fd, &ack, sizeof ack) != sizeof ack ) { mce_log(LL_ERR, "connection(%s): handshake read failed: %m", sfw_plugin_get_sensor_name(self->con_plugin)); goto EXIT; } if( ack != '\n' ) { mce_log(LL_ERR, "connection(%s): invalid handshake received", sfw_plugin_get_sensor_name(self->con_plugin)); goto EXIT; } res = true; EXIT: return res; } /** Handle sensor events received over data connection */ static bool sfw_connection_rx_dta(sfw_connection_t *self) { bool res = false; char dta[1024]; if( self->con_fd == -1 ) goto EXIT; errno = 0; int rc = read(self->con_fd, dta, sizeof dta); if( rc == 0 ) { mce_log(LL_ERR, "connection(%s): received EOF", sfw_plugin_get_sensor_name(self->con_plugin)); goto EXIT; } if( rc == -1 ) { if( errno != EAGAIN && errno != EINTR ) mce_log(LL_ERR, "connection(%s): received ERR; %m", sfw_plugin_get_sensor_name(self->con_plugin)); else res = true; goto EXIT; } mce_log(LL_DEBUG, "connection(%s): received %d bytes", sfw_plugin_get_sensor_name(self->con_plugin), rc); /* Note: Writing smallish chunks to unix domain socket * either fully succeeds or fails/sender blocks. * * With this in mind it is assumed that a succesful * read will contain fully valid sample data. However, * depending on how sensord actually does the sending, * there is a slight hazard point here... * * It might be that separate state needs to be * introduced for handling header (sample count) * and payload (sensor samples) separately. */ res = sfw_connection_handle_samples(self, dta, rc); EXIT: return res; } /** Handle sensor data connection is writable conditions * * Used for initial session handshake only. */ static gboolean sfw_connection_tx_cb(GIOChannel *chn, GIOCondition cnd, gpointer aptr) { (void)chn; (void)cnd; sfw_connection_t *self = aptr; gboolean keep_going = FALSE; bool success = false; if( self->con_state != CONNECTION_CONNECTING ) goto EXIT; success = sfw_connection_tx_req(self); EXIT: if( !keep_going ) { mce_log(LL_DEBUG, "connection(%s): disabling tx notify", sfw_plugin_get_sensor_name(self->con_plugin)); self->con_tx_id = 0; } if( success ) sfw_connection_trans(self, CONNECTION_REGISTERING); else sfw_connection_trans(self, CONNECTION_ERROR); return keep_going; } /** Handle data available from data connection * * Used 1st for handling handshake reply and then for * receiving sensor event packets. */ static gboolean sfw_connection_rx_cb(GIOChannel *chn, GIOCondition cnd, gpointer aptr) { (void)chn; (void)cnd; sfw_connection_t *self = aptr; sfw_connection_state_t next_state = self->con_state; switch( self->con_state ) { case CONNECTION_REGISTERING: if( sfw_connection_rx_ack(self) ) next_state = CONNECTION_CONNECTED; else next_state = CONNECTION_ERROR; break; case CONNECTION_CONNECTED: if( !sfw_connection_rx_dta(self) ) next_state = CONNECTION_ERROR; break; default: next_state = CONNECTION_ERROR; break; } bool keep_going = (next_state != CONNECTION_ERROR); if( !keep_going ) { mce_log(LL_CRIT, "connection(%s): disabling rx notify", sfw_plugin_get_sensor_name(self->con_plugin)); self->con_rx_id = 0; } sfw_connection_trans(self, next_state); return keep_going; } /** Remove data available glib I/O watch */ static void sfw_connection_remove_rx_notify(sfw_connection_t *self) { if( self->con_rx_id ) { mce_log(LL_DEBUG, "connection(%s): removing rx notify", sfw_plugin_get_sensor_name(self->con_plugin)); g_source_remove(self->con_rx_id), self->con_rx_id = 0; } } /** Remove ready to send glib I/O watch */ static void sfw_connection_remove_tx_notify(sfw_connection_t *self) { if( self->con_tx_id ) { mce_log(LL_DEBUG, "connection(%s): removing tx notify", sfw_plugin_get_sensor_name(self->con_plugin)); g_source_remove(self->con_tx_id), self->con_tx_id = 0; } } /** Close sensord data connection */ static void sfw_connection_close_socket(sfw_connection_t *self) { sfw_connection_remove_tx_notify(self); sfw_connection_remove_rx_notify(self); if( self->con_fd != -1 ) { mce_log(LL_DEBUG, "connection(%s): closing socket", sfw_plugin_get_sensor_name(self->con_plugin)); close(self->con_fd), self->con_fd = -1; } } /** Open sensord data connection */ static bool sfw_connection_open_socket(sfw_connection_t *self) { bool res = false; sfw_connection_close_socket(self); if( (self->con_fd = sfw_socket_open()) == -1 ) goto EXIT; mce_log(LL_DEBUG, "connection(%s): opened socket", sfw_plugin_get_sensor_name(self->con_plugin)); self->con_tx_id = sfw_socket_add_notify(self->con_fd, false, G_IO_OUT, sfw_connection_tx_cb, self); if( !self->con_tx_id ) goto EXIT; mce_log(LL_DEBUG, "connection(%s): added tx notify", sfw_plugin_get_sensor_name(self->con_plugin)); self->con_rx_id = sfw_socket_add_notify(self->con_fd, false, G_IO_IN, sfw_connection_rx_cb, self); if( !self->con_rx_id ) goto EXIT; mce_log(LL_DEBUG, "connection(%s): added rx notify", sfw_plugin_get_sensor_name(self->con_plugin)); res = true; EXIT: /* all or nothing */ if( !res ) sfw_connection_close_socket(self); return res; } /** Handle triggering of ipc retry timer */ static gboolean sfw_connection_retry_cb(gpointer aptr) { sfw_connection_t *self = aptr; if( !self->con_retry_id ) goto EXIT; self->con_retry_id = 0; mce_log(LL_WARN, "connection(%s): retry", sfw_plugin_get_sensor_name(self->con_plugin)); if( self->con_state == CONNECTION_ERROR ) sfw_connection_trans(self, CONNECTION_CONNECTING); EXIT: return FALSE; } /** Cancel pending ipc retry timer */ static void sfw_connection_cancel_retry(sfw_connection_t *self) { if( self->con_retry_id ) { g_source_remove(self->con_retry_id); self->con_retry_id = 0; } } /** Make a state transition */ static void sfw_connection_trans(sfw_connection_t *self, sfw_connection_state_t state) { if( self->con_state == state ) goto EXIT; mce_log(LL_DEBUG, "connection(%s): %s -> %s", sfw_plugin_get_sensor_name(self->con_plugin), sfw_connection_state_name(self->con_state), sfw_connection_state_name(state)); self->con_state = state; sfw_connection_cancel_retry(self); switch( self->con_state ) { case CONNECTION_CONNECTING: if( !sfw_connection_open_socket(self) ) sfw_connection_trans(self, CONNECTION_ERROR); break; case CONNECTION_REGISTERING: sfw_connection_remove_tx_notify(self); break; case CONNECTION_CONNECTED: sfw_plugin_do_override_start(self->con_plugin); sfw_plugin_do_reporting_start(self->con_plugin); break; default: case CONNECTION_IDLE: case CONNECTION_ERROR: case CONNECTION_INITIAL: sfw_plugin_do_reporting_reset(self->con_plugin); sfw_plugin_do_override_reset(self->con_plugin); sfw_connection_close_socket(self); if( self->con_state == CONNECTION_ERROR ) self->con_retry_id = g_timeout_add(SENSORFW_RETRY_DELAY_MS, sfw_connection_retry_cb, self); break; } EXIT: return; } /** Create sensord data connection state machine object */ static sfw_connection_t * sfw_connection_create(sfw_plugin_t *plugin) { sfw_connection_t *self = calloc(1, sizeof *self); self->con_plugin = plugin; self->con_state = CONNECTION_INITIAL; self->con_fd = -1; self->con_rx_id = 0; self->con_tx_id = 0; self->con_retry_id = 0; return self; } /** Delete sensord data connection state machine object */ static void sfw_connection_delete(sfw_connection_t *self) { // using NULL self pointer explicitly allowed if( self ) { sfw_connection_trans(self, CONNECTION_INITIAL); self->con_plugin = 0; free(self); } } /** Initiate data connection */ static void sfw_connection_do_start(sfw_connection_t *self) { switch( self->con_state ) { case CONNECTION_IDLE: case CONNECTION_INITIAL: sfw_connection_trans(self, CONNECTION_CONNECTING); break; default: // nop break; } } /** Close data connection */ static void sfw_connection_do_reset(sfw_connection_t *self) { sfw_connection_trans(self, CONNECTION_IDLE); } /* ========================================================================= * * SENSORFW_SESSION * ========================================================================= */ /** Translate session state to human readable form */ static const char * sfw_session_state_name(sfw_session_state_t state) { static const char *const lut[SESSION_NUMSTATES] = { [SESSION_INITIAL] = "INITIAL", [SESSION_IDLE] = "IDLE", [SESSION_REQUESTING] = "REQUESTING", [SESSION_ACTIVE] = "ACTIVE", [SESSION_ERROR] = "ERROR", [SESSION_INVALID] = "INVALID", }; return (state < SESSION_NUMSTATES) ? lut[state] : 0; } /** Create sensor data session state machine object */ static sfw_session_t * sfw_session_create(sfw_plugin_t *plugin) { sfw_session_t *self = calloc(1, sizeof *self); self->ses_plugin = plugin; self->ses_state = SESSION_INITIAL; self->ses_id = SESSION_ID_INVALID; self->ses_start_pc = 0; self->ses_retry_id = 0; return self; } /** Delete sensor data session state machine object */ static void sfw_session_delete(sfw_session_t *self) { // using NULL self pointer explicitly allowed if( self ) { sfw_session_trans(self, SESSION_INITIAL); self->ses_plugin = 0; free(self); } } /** Get session id granted by sensord */ static int sfw_session_get_id(const sfw_session_t *self) { // Explicitly allow NULL self pointer to be used return self ? self->ses_id : SESSION_ID_INVALID; } /** Cancel pending session start method call */ static void sfw_session_cancel_start(sfw_session_t *self) { if( self->ses_start_pc ) { dbus_pending_call_cancel(self->ses_start_pc); dbus_pending_call_unref(self->ses_start_pc); self->ses_start_pc = 0; } } /** Cancel pending ipc retry timer */ static void sfw_session_cancel_retry(sfw_session_t *self) { if( self->ses_retry_id ) { g_source_remove(self->ses_retry_id); self->ses_retry_id = 0; } } /** Make a state transition */ static void sfw_session_trans(sfw_session_t *self, sfw_session_state_t state) { if( self->ses_state == state ) goto EXIT; sfw_session_cancel_start(self); sfw_session_cancel_retry(self); mce_log(LL_DEBUG, "session(%s): %s -> %s", sfw_plugin_get_sensor_name(self->ses_plugin), sfw_session_state_name(self->ses_state), sfw_session_state_name(state)); self->ses_state = state; switch( self->ses_state ) { case SESSION_REQUESTING: { dbus_int64_t pid = getpid(); const char *name = sfw_plugin_get_sensor_name(self->ses_plugin); dbus_send_ex(SENSORFW_SERVICE, SENSORFW_MANAGER_OBJECT, SENSORFW_MANAGER_INTEFCACE, SENSORFW_MANAGER_METHOD_START_SESSION, sfw_session_start_cb, self, 0, &self->ses_start_pc, DBUS_TYPE_STRING, &name, DBUS_TYPE_INT64, &pid, DBUS_TYPE_INVALID); } break; case SESSION_ACTIVE: sfw_plugin_do_connection_start(self->ses_plugin); break; default: case SESSION_IDLE: case SESSION_ERROR: case SESSION_INVALID: case SESSION_INITIAL: sfw_plugin_do_connection_reset(self->ses_plugin); if( self->ses_state == SESSION_ERROR ) self->ses_retry_id = g_timeout_add(SENSORFW_RETRY_DELAY_MS, sfw_session_retry_cb, self); break; } EXIT: return; } /** Handle reply to data session start request */ static void sfw_session_start_cb(DBusPendingCall *pc, void *aptr) { sfw_session_t *self = aptr; DBusMessage *rsp = 0; DBusError err = DBUS_ERROR_INIT; dbus_int32_t ses = SESSION_ID_UNKNOWN; if( pc != self->ses_start_pc ) goto EXIT; dbus_pending_call_unref(self->ses_start_pc), self->ses_start_pc = 0; if( !(rsp = dbus_pending_call_steal_reply(pc)) ) { mce_log(LL_ERR, "session(%s): no reply", sfw_plugin_get_sensor_name(self->ses_plugin)); goto EXIT; } if( dbus_set_error_from_message(&err, rsp) ) { mce_log(LL_ERR, "session(%s): error reply: %s: %s", sfw_plugin_get_sensor_name(self->ses_plugin), err.name, err.message); goto EXIT; } if( !dbus_message_get_args(rsp, &err, DBUS_TYPE_INT32, &ses, DBUS_TYPE_INVALID) ) { mce_log(LL_ERR, "session(%s): parse error: %s: %s", sfw_plugin_get_sensor_name(self->ses_plugin), err.name, err.message); goto EXIT; } EXIT: mce_log(LL_DEBUG, "session(%s): sid=%d", sfw_plugin_get_sensor_name(self->ses_plugin), (int)ses); switch( ses ) { case SESSION_ID_INVALID: self->ses_id = SESSION_ID_INVALID; sfw_session_trans(self, SESSION_INVALID); break; case SESSION_ID_UNKNOWN: self->ses_id = SESSION_ID_INVALID; sfw_session_trans(self, SESSION_ERROR); break; default: self->ses_id = ses; sfw_session_trans(self, SESSION_ACTIVE); } if( rsp ) dbus_message_unref(rsp); dbus_error_free(&err); return; } /** Handle triggering of start/stop retry timer */ static gboolean sfw_session_retry_cb(gpointer aptr) { sfw_session_t *self = aptr; if( !self->ses_retry_id ) goto EXIT; self->ses_retry_id = 0; mce_log(LL_WARN, "session(%s): retry", sfw_plugin_get_sensor_name(self->ses_plugin)); if( self->ses_state == SESSION_ERROR ) sfw_session_trans(self, SESSION_REQUESTING); EXIT: return FALSE; } /** Initiate data session */ static void sfw_session_do_start(sfw_session_t *self) { switch( self->ses_state ) { case SESSION_IDLE: case SESSION_INITIAL: sfw_session_trans(self, SESSION_REQUESTING); break; default: // nop break; } } /** Close data session */ static void sfw_session_do_reset(sfw_session_t *self) { sfw_session_trans(self, SESSION_IDLE); } /* ========================================================================= * * SENSORFW_PLUGIN * ========================================================================= */ /** Translate plugin state to human readable form */ static const char * sfw_plugin_state_name(sfw_plugin_state_t state) { static const char *const lut[PLUGIN_NUMSTATES] = { [PLUGIN_INITIAL] = "INITIAL", [PLUGIN_IDLE] = "IDLE", [PLUGIN_LOADING] = "LOADING", [PLUGIN_LOADED] = "LOADED", [PLUGIN_ERROR] = "ERROR", [PLUGIN_NA] = "NA", }; return (state < PLUGIN_NUMSTATES) ? lut[state] : 0; } /** Get sensord compatible name of the sensor */ static const char * sfw_plugin_get_sensor_name(const sfw_plugin_t *self) { // Explicitly allow NULL self pointer to be used return self ? self->plg_backend->be_sensor_name : 0; } /** Get sensord compatible D-Bus object path for the sensor */ static const char * sfw_plugin_get_sensor_object(const sfw_plugin_t *self) { // Explicitly allow NULL self pointer to be used if( !self ) return 0; return self->plg_sensor_object ?: self->plg_backend->be_sensor_object; } /** Get sensord compatible D-Bus interface for the sensor */ static const char * sfw_plugin_get_sensor_interface(const sfw_plugin_t *self) { // Explicitly allow NULL self pointer to be used return self ? self->plg_backend->be_sensor_interface : 0; } /** Get cached session id granted by sensord */ static int sfw_plugin_get_session_id(const sfw_plugin_t *self) { // Explicitly allow NULL self pointer to be used return self ? sfw_session_get_id(self->plg_session) : SESSION_ID_INVALID; } static void sfw_plugin_notify(sfw_plugin_t *self, sfw_notify_t type, const void *sample) { if( self && self->plg_backend && self->plg_backend->be_sample_cb ) self->plg_backend->be_sample_cb(self, type, sample); } /** Handle sensor specific change event received from data connection */ static void sfw_plugin_handle_sample(sfw_plugin_t *self, const void *sample) { sfw_plugin_notify(self, NOTIFY_SENSORD, sample); } /** Handle sensor specific initial value received via dbus query */ static bool sfw_plugin_handle_value(sfw_plugin_t *self, DBusMessageIter *data) { bool ack = false; if( self->plg_backend->be_value_cb ) ack = self->plg_backend->be_value_cb(self, data); return ack; } /** Reset sensor state to assumed default state * * Used when sensord drops from D-Bus or sensor start() or stop() * ceases to function. */ static void sfw_plugin_reset_value(sfw_plugin_t *self) { mce_log(LL_DEBUG, "plugin(%s): reset", sfw_plugin_get_sensor_name(self)); sfw_plugin_notify(self, NOTIFY_RESET, 0); } /** Repeat sensor state notification * * Used when tracking callbacks are changed */ static void sfw_plugin_repeat_value(sfw_plugin_t *self) { mce_log(LL_DEBUG, "plugin(%s): repeat", sfw_plugin_get_sensor_name(self)); sfw_plugin_notify(self, NOTIFY_REPEAT, 0); } /** Restore sensor state to last seen state * * Used when sensord comes to D-Bus. */ static void sfw_plugin_restore_value(sfw_plugin_t *self) { mce_log(LL_DEBUG, "plugin(%s): restore", sfw_plugin_get_sensor_name(self)); sfw_plugin_notify(self, NOTIFY_RESTORE, 0); } /** Set sensor state when stopped * * Used when sensor tracking is stopped */ static void sfw_plugin_forget_value(sfw_plugin_t *self) { mce_log(LL_DEBUG, "plugin(%s): forget", sfw_plugin_get_sensor_name(self)); sfw_plugin_notify(self, NOTIFY_FORGET, 0); } /** Get size of sensor specific change event */ static size_t sfw_plugin_get_sample_size(const sfw_plugin_t *self) { return self->plg_backend->be_sample_size; } /** Get name of sensor method for querying initial value */ static const char * sfw_plugin_get_value_method(const sfw_plugin_t *self) { return self->plg_backend->be_value_method; } /** Cancel pending plugin load() dbus method call */ static void sfw_plugin_cancel_load(sfw_plugin_t *self) { if( self->plg_load_pc ) { dbus_pending_call_cancel(self->plg_load_pc); dbus_pending_call_unref(self->plg_load_pc); self->plg_load_pc = 0; } } /** Cancel pending ipc retry timer */ static void sfw_plugin_cancel_retry(sfw_plugin_t *self) { if( self->plg_retry_id ) { g_source_remove(self->plg_retry_id); self->plg_retry_id = 0; } } /** Make a state transition */ static void sfw_plugin_trans(sfw_plugin_t *self, sfw_plugin_state_t state) { if( !self ) goto EXIT; if( self->plg_state == state ) goto EXIT; mce_log(LL_DEBUG, "plugin(%s): %s -> %s", sfw_plugin_get_sensor_name(self), sfw_plugin_state_name(self->plg_state), sfw_plugin_state_name(state)); self->plg_state = state; sfw_plugin_cancel_load(self); sfw_plugin_cancel_retry(self); switch( self->plg_state ) { case PLUGIN_IDLE: case PLUGIN_ERROR: case PLUGIN_INITIAL: case PLUGIN_NA: sfw_plugin_do_session_reset(self); if( self->plg_state == PLUGIN_ERROR ) self->plg_retry_id = g_timeout_add(SENSORFW_RETRY_DELAY_MS, sfw_plugin_retry_cb, self); break; case PLUGIN_LOADING: { const char *sensor_name = sfw_plugin_get_sensor_name(self); dbus_send_ex(SENSORFW_SERVICE, SENSORFW_MANAGER_OBJECT, SENSORFW_MANAGER_INTEFCACE, SENSORFW_MANAGER_METHOD_LOAD_PLUGIN, sfw_plugin_load_cb, self, 0, &self->plg_load_pc, DBUS_TYPE_STRING, &sensor_name, DBUS_TYPE_INVALID); } break; case PLUGIN_LOADED: sfw_plugin_do_session_start(self); break; default: break; } EXIT: return; } /** Create sensor plugin load/unload state machine object */ static sfw_plugin_t * sfw_plugin_create(const sfw_backend_t *backend) { sfw_plugin_t *self = calloc(1, sizeof *self); self->plg_state = PLUGIN_INITIAL; self->plg_backend = backend; self->plg_sensor_object = 0; self->plg_load_pc = 0; self->plg_retry_id = 0; /* If backend does not define object path, construct it * from manager object path + sensor name */ if( !self->plg_backend->be_sensor_object ) { self->plg_sensor_object = g_strdup_printf("%s/%s", SENSORFW_MANAGER_OBJECT, self->plg_backend->be_sensor_name); } mce_log(LL_DEBUG, "Initializing sensor plugin"); mce_log(LL_DEBUG, "sensor: %s", sfw_plugin_get_sensor_name(self)); mce_log(LL_DEBUG, "object: %s", sfw_plugin_get_sensor_object(self)); mce_log(LL_DEBUG, "interface: %s", sfw_plugin_get_sensor_interface(self)); self->plg_session = sfw_session_create(self); self->plg_connection = sfw_connection_create(self); self->plg_override = sfw_override_create(self); self->plg_reporting = sfw_reporting_create(self); return self; } /** Set sensor plugin target state */ static void sfw_plugin_enable_sensor(sfw_plugin_t *self, bool enable) { if( self ) { bool forced = false; if( !enable && mce_in_sensortest_mode() ) forced = enable = true; mce_log(LL_DEBUG, "%s: %s%s", sfw_plugin_get_sensor_name(self), enable ? "enable" : "disable", forced ? " (forced)" : ""); if( self->plg_override ) sfw_override_set_target(self->plg_override, enable); if( self->plg_reporting ) sfw_reporting_set_target(self->plg_reporting, enable); } } /** Delete sensor plugin load/unload state machine object */ static void sfw_plugin_delete(sfw_plugin_t *self) { // using NULL self pointer explicitly allowed if( self ) { sfw_plugin_trans(self, PLUGIN_IDLE); sfw_reporting_delete(self->plg_reporting), self->plg_reporting = 0; sfw_override_delete(self->plg_override), self->plg_override = 0; sfw_connection_delete(self->plg_connection), self->plg_connection = 0; sfw_session_delete(self->plg_session), self->plg_session = 0; g_free(self->plg_sensor_object), self->plg_sensor_object = 0; free(self); } } /** Handle reply to plugin load() dbus method call */ static void sfw_plugin_load_cb(DBusPendingCall *pc, void *aptr) { sfw_plugin_t *self = aptr; DBusMessage *rsp = 0; DBusError err = DBUS_ERROR_INIT; dbus_bool_t ack = FALSE; bool nak = true; if( pc != self->plg_load_pc ) goto EXIT; dbus_pending_call_unref(self->plg_load_pc), self->plg_load_pc = 0; if( !(rsp = dbus_pending_call_steal_reply(pc)) ) { mce_log(LL_ERR, "plugin(%s): no reply", sfw_plugin_get_sensor_name(self)); goto EXIT; } if( dbus_set_error_from_message(&err, rsp) ) { mce_log(LL_ERR, "plugin(%s): error reply: %s: %s", sfw_plugin_get_sensor_name(self), err.name, err.message); goto EXIT; } if( !dbus_message_get_args(rsp, &err, DBUS_TYPE_BOOLEAN, &ack, DBUS_TYPE_INVALID) ) { mce_log(LL_ERR, "plugin(%s): parse error: %s: %s", sfw_plugin_get_sensor_name(self), err.name, err.message); goto EXIT; } nak = false; EXIT: if( self->plg_state == PLUGIN_LOADING) { if( nak ) sfw_plugin_trans(self, PLUGIN_ERROR); else if( ack ) sfw_plugin_trans(self, PLUGIN_LOADED); else sfw_plugin_trans(self, PLUGIN_NA); } if( rsp ) dbus_message_unref(rsp); dbus_error_free(&err); return; } /** Handle triggering of ipc retry timer */ static gboolean sfw_plugin_retry_cb(gpointer aptr) { sfw_plugin_t *self = aptr; if( !self->plg_retry_id ) goto EXIT; self->plg_retry_id = 0; mce_log(LL_WARN, "plugin(%s): retry", sfw_plugin_get_sensor_name(self)); if( self->plg_state == PLUGIN_ERROR ) sfw_plugin_trans(self, PLUGIN_LOADING); EXIT: return FALSE; } /** Initiate plugin loading */ static void sfw_plugin_do_load(sfw_plugin_t *self) { if( !self ) goto EXIT; switch( self->plg_state ) { case PLUGIN_IDLE: case PLUGIN_INITIAL: sfw_plugin_trans(self, PLUGIN_LOADING); break; default: // nop break; } EXIT: return; } /** Unload plugin */ static void sfw_plugin_do_reset(sfw_plugin_t *self) { sfw_plugin_trans(self, PLUGIN_IDLE); } /** Initiate data session */ static void sfw_plugin_do_session_start(sfw_plugin_t *self) { // using NULL self pointer explicitly allowed if( self && self->plg_session ) sfw_session_do_start(self->plg_session); } /** Close data session */ static void sfw_plugin_do_session_reset(sfw_plugin_t *self) { // using NULL self pointer explicitly allowed if( self && self->plg_session ) sfw_session_do_reset(self->plg_session); } /** Initiate data connection */ static void sfw_plugin_do_connection_start(sfw_plugin_t *self) { // using NULL self pointer explicitly allowed if( self && self->plg_connection ) sfw_connection_do_start(self->plg_connection); } /** Close data connection */ static void sfw_plugin_do_connection_reset(sfw_plugin_t *self) { // using NULL self pointer explicitly allowed if( self && self->plg_connection ) sfw_connection_do_reset(self->plg_connection); } /** Initiate standby override handling */ static void sfw_plugin_do_override_start(sfw_plugin_t *self) { // using NULL self pointer explicitly allowed if( self && self->plg_override ) sfw_override_do_start(self->plg_override); } /** Cease standby override handling */ static void sfw_plugin_do_override_reset(sfw_plugin_t *self) { // using NULL self pointer explicitly allowed if( self && self->plg_override ) sfw_override_do_reset(self->plg_override); } /** Initiate sensor start/stop handling */ static void sfw_plugin_do_reporting_start(sfw_plugin_t *self) { // using NULL self pointer explicitly allowed if( self && self->plg_reporting ) sfw_reporting_do_start(self->plg_reporting); } /** Cease sensor start/stop handling */ static void sfw_plugin_do_reporting_reset(sfw_plugin_t *self) { // using NULL self pointer explicitly allowed if( self && self->plg_reporting ) sfw_reporting_do_reset(self->plg_reporting); } /* ========================================================================= * * SENSORFW_SERVICE * ========================================================================= */ /** Translate service state to human readable form */ static const char * sfw_service_state_name(sfw_service_state_t state) { static const char *const lut[SERVICE_NUMSTATES] = { [SERVICE_UNKNOWN] = "INITIAL", [SERVICE_QUERYING] = "QUERYING", [SERVICE_RUNNING] = "RUNNING", [SERVICE_STOPPED] = "STOPPED", }; return (state < SERVICE_NUMSTATES) ? lut[state] : 0; } /** Create sensord availability tracking state machine object */ static sfw_service_t * sfw_service_create(void) { sfw_service_t *self = calloc(1, sizeof *self); self->srv_state = SERVICE_UNKNOWN; self->srv_query_pc = 0; for( sensor_id_t id = 0; id < SFW_SENSOR_ID_COUNT; ++id ) { if( sensor_id_available(id) ) self->srv_plugin[id] = sfw_plugin_create(sfw_backend_lut + id); else self->srv_plugin[id] = 0; } return self; } /** Delete sensord availability tracking state machine object */ static void sfw_service_delete(sfw_service_t *self) { // using NULL self pointer explicitly allowed if( self ) { sfw_service_trans(self, SERVICE_UNKNOWN); for( sensor_id_t id = 0; id < SFW_SENSOR_ID_COUNT; ++id ) sfw_plugin_delete(self->srv_plugin[id]), self->srv_plugin[id] = 0; free(self); } } /** Cancel pending sensord name owner dbus method call */ static void sfw_service_cancel_query(sfw_service_t *self) { if( self->srv_query_pc ) { dbus_pending_call_cancel(self->srv_query_pc); dbus_pending_call_unref(self->srv_query_pc); self->srv_query_pc = 0; } } /** Handle reply to sensord name owner dbus method call */ static void sfw_service_query_cb(DBusPendingCall *pc, void *aptr) { sfw_service_t *self = aptr; DBusMessage *rsp = 0; const char *owner = 0; DBusError err = DBUS_ERROR_INIT; if( pc != self->srv_query_pc ) goto EXIT; dbus_pending_call_unref(self->srv_query_pc), self->srv_query_pc = 0; if( !(rsp = dbus_pending_call_steal_reply(pc)) ) goto EXIT; if( dbus_set_error_from_message(&err, rsp) || !dbus_message_get_args(rsp, &err, DBUS_TYPE_STRING, &owner, DBUS_TYPE_INVALID) ) { if( strcmp(err.name, DBUS_ERROR_NAME_HAS_NO_OWNER) ) mce_log(LL_WARN, "%s: %s", err.name, err.message); owner = 0; } EXIT: if( self->srv_state == SERVICE_QUERYING ) { if( owner && *owner ) sfw_service_trans(self, SERVICE_RUNNING); else sfw_service_trans(self, SERVICE_STOPPED); } if( rsp ) dbus_message_unref(rsp); dbus_error_free(&err); } /** Make a state transition */ static void sfw_service_trans(sfw_service_t *self, sfw_service_state_t state) { static const char *service_name = SENSORFW_SERVICE; if( self->srv_state == state ) goto EXIT; mce_log(LL_DEBUG, "service: %s -> %s", sfw_service_state_name(self->srv_state), sfw_service_state_name(state)); self->srv_state = state; sfw_service_cancel_query(self); switch( self->srv_state ) { case SERVICE_QUERYING: dbus_send_ex(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, DBUS_METHOD_GET_NAME_OWNER, sfw_service_query_cb, self, 0, &self->srv_query_pc, DBUS_TYPE_STRING, &service_name, DBUS_TYPE_INVALID); break; case SERVICE_RUNNING: sfw_exception_start(SFW_EXCEPTION_LENGTH_SENSORD_RUNNING); for( sensor_id_t id = 0; id < SFW_SENSOR_ID_COUNT; ++id ) sfw_plugin_do_load(self->srv_plugin[id]); break; case SERVICE_UNKNOWN: case SERVICE_STOPPED: sfw_exception_start(SFW_EXCEPTION_LENGTH_SENSORD_STOPPED); for( sensor_id_t id = 0; id < SFW_SENSOR_ID_COUNT; ++id ) sfw_plugin_do_reset(self->srv_plugin[id]); break; default: break; } // TODO: broadcast sensord status over datapipe? EXIT: return; } /** Set sensord tracking to available state */ static void sfw_service_do_start(sfw_service_t *self) { sfw_service_trans(self, SERVICE_RUNNING); } /** Set sensord tracking to not available state */ static void sfw_service_do_stop(sfw_service_t *self) { sfw_service_trans(self, SERVICE_STOPPED); } /** Initiate sensord name owner tracking */ static void sfw_service_do_query(sfw_service_t *self) { if( self->srv_state == SERVICE_UNKNOWN ) sfw_service_trans(self, SERVICE_QUERYING); } /** Lookup plugin by sensor id */ static sfw_plugin_t * sfw_service_plugin(const sfw_service_t *self, sensor_id_t id) { sfw_plugin_t *plugin = 0; if( self ) { plugin = self->srv_plugin[id]; } return plugin; } /** Enable/disable sensor by id */ static void sfw_service_set_sensor(const sfw_service_t *self, sensor_id_t id, bool enable) { sfw_plugin_t *plugin = sfw_service_plugin(self, id); sfw_plugin_enable_sensor(plugin, enable); } /* ========================================================================= * * SENSORFW_NOTIFY * ========================================================================= */ /** io watch id for PS evdev file descriptor * * If this is non-zero, proximity data received from sensord is ignored. */ static guint ps_evdev_id = 0; static bool ps_from_evdev(void) { return ps_evdev_id != 0; } /** io watch id for ALS evdev file descriptor * * If this is non-zero, ambient light data received from sensord is ignored. */ static guint als_evdev_id = 0; static bool als_from_evdev(void) { return als_evdev_id != 0; } /** Translate notification type to human readable form */ static const char * sfw_notify_name(sfw_notify_t type) { static const char *const lut[NOTIFY_NUMTYPES] = { [NOTIFY_RESET] = "RESET", [NOTIFY_RESTORE] = "RESTORE", [NOTIFY_REPEAT] = "REPEAT", [NOTIFY_EVDEV] = "EVDEV", [NOTIFY_SENSORD] = "SENSORD", [NOTIFY_FORGET] = "FORGET", }; return (type < NOTIFY_NUMTYPES) ? lut[type] : 0; } /* ========================================================================= * * SENSORFW_EXCEPTION * ========================================================================= */ /** Flag for: mce is starting up, use exceptional reporting */ static bool sfw_exception_inititial = true; /** Timer id for: end of exceptional reporting state */ static guint sfw_exception_timer_id = 0; /** Timer callback for ending exception state * * Switch back to reporting actual or built-in default sensor values * * @return TRUE (to stop timer from repeating) */ static gboolean sfw_exception_timer_cb(gpointer aptr) { (void)aptr; if( !sfw_exception_timer_id ) goto EXIT; sfw_exception_timer_id = 0; mce_log(LL_DEBUG, "exceptional reporting ended"); sfw_plugin_t *plugin = sfw_service_plugin(sfw_service, SFW_SENSOR_ID_PS); sfw_plugin_repeat_value(plugin); EXIT: return FALSE; } /** Predicate for: exceptional sensor values should be reported * * Currently this should affect only proximity sensor which * 1) needs to be assumed "covered" while we do not know whether * sensord is going to be available or not (device bootup and * mce/sensord restarts) * 2) must be considered "uncovered" if it becomes clear that * sensord will not be available or fails to function (act dead * mode, unfinished porting to new hw platform, etc) * * @return true if exceptional values should be used, false otherwise */ static bool sfw_exception_is_active(void) { return sfw_exception_timer_id || sfw_exception_inititial; } /** Start exceptional value reporting period * * @delay_ms length of exceptional reporting period */ static void sfw_exception_start(sfw_exception_delay_t delay_ms) { mce_log(LL_DEBUG, "exceptional reporting for %d ms", delay_ms); if( sfw_exception_timer_id ) g_source_remove(sfw_exception_timer_id); sfw_exception_timer_id = g_timeout_add(delay_ms, sfw_exception_timer_cb, 0); sfw_exception_inititial = false; sfw_plugin_t *plugin = sfw_service_plugin(sfw_service, SFW_SENSOR_ID_PS); sfw_plugin_repeat_value(plugin); } /** Cancel exceptional value reporting period */ static void sfw_exception_cancel(void) { if( sfw_exception_timer_id ) { g_source_remove(sfw_exception_timer_id), sfw_exception_timer_id = 0; mce_log(LL_DEBUG, "exceptional reporting canceled"); } } /* ========================================================================= * * SENSORFW_MODULE * ========================================================================= */ // ---------------------------------------------------------------- /** Prepare sensors for suspending */ void mce_sensorfw_suspend(void) { // DUMMY } /** Rethink sensors after resuming */ void mce_sensorfw_resume(void) { // DUMMY } // ---------------------------------------------------------------- /** Set ALS notification callback * * @param cb function to call when ALS events are received */ void mce_sensorfw_als_set_notify(void (*cb)(int lux)) { if( (sfw_notify_als_cb = cb) ) { sfw_plugin_t *plugin = sfw_service_plugin(sfw_service, SFW_SENSOR_ID_ALS); sfw_plugin_repeat_value(plugin); } } /** Try to enable ALS input */ void mce_sensorfw_als_enable(void) { sfw_service_set_sensor(sfw_service, SFW_SENSOR_ID_ALS, true); } /** Try to disable ALS input */ void mce_sensorfw_als_disable(void) { sfw_service_set_sensor(sfw_service, SFW_SENSOR_ID_ALS, false); } // ---------------------------------------------------------------- /** Set PS notification callback * * @param cb function to call when PS events are received */ void mce_sensorfw_ps_set_notify(void (*cb)(bool covered)) { if( (sfw_notify_ps_cb = cb) ) { sfw_plugin_t *plugin = sfw_service_plugin(sfw_service, SFW_SENSOR_ID_PS); sfw_plugin_repeat_value(plugin); } } /** Try to enable PS input */ void mce_sensorfw_ps_enable(void) { sfw_service_set_sensor(sfw_service, SFW_SENSOR_ID_PS, true); } /** Try to disable PS input */ void mce_sensorfw_ps_disable(void) { sfw_service_set_sensor(sfw_service, SFW_SENSOR_ID_PS, false); } // ---------------------------------------------------------------- /** Set Orientation notification callback * * @param cb function to call when Orientation events are received */ void mce_sensorfw_orient_set_notify(void (*cb)(int state)) { if( (sfw_notify_orient_cb = cb) ) { sfw_plugin_t *plugin = sfw_service_plugin(sfw_service, SFW_SENSOR_ID_ORIENT); sfw_plugin_repeat_value(plugin); } } /** Try to enable Orientation input */ void mce_sensorfw_orient_enable(void) { sfw_service_set_sensor(sfw_service, SFW_SENSOR_ID_ORIENT, true); } /** Try to disable Orientation input */ void mce_sensorfw_orient_disable(void) { sfw_service_set_sensor(sfw_service, SFW_SENSOR_ID_ORIENT, false); } // ---------------------------------------------------------------- /** Callback function for processing evdev events * * @param chn io channel * @param cnd conditions to handle * @param aptr pointer to io watch * * @return TRUE to keep io watch active, FALSE to stop it */ static gboolean mce_sensorfw_evdev_cb(GIOChannel *chn, GIOCondition cnd, gpointer aptr) { gboolean keep = FALSE; int *id = aptr; int fd = g_io_channel_unix_get_fd(chn); int als = -1; int ps = -1; int rc; struct input_event eve[256]; /* wakelock must be taken before reading the data */ wakelock_lock("mce_input_handler", -1); if( cnd & (G_IO_ERR | G_IO_HUP | G_IO_NVAL) ) { goto EXIT; } rc = read(fd, eve, sizeof eve); if( rc == -1 ) { if( errno == EINTR || errno == EAGAIN ) keep = TRUE; else mce_log(LL_ERR, "read events: %m"); goto EXIT; } if( rc == 0 ) { mce_log(LL_ERR, "read events: EOF"); goto EXIT; } keep = TRUE; size_t n = rc / sizeof *eve; for( size_t i = 0; i < n; ++i ) { if( eve[i].type != EV_ABS ) continue; switch( eve[i].code ) { case ABS_MISC: als = eve[i].value; break; case ABS_DISTANCE: ps = eve[i].value; break; default: break; } } if( als != -1 ) { sfw_sample_als_t sample = { .als_value = als, }; sfw_plugin_t *plugin = sfw_service_plugin(sfw_service, SFW_SENSOR_ID_ALS); sfw_plugin_notify(plugin, NOTIFY_EVDEV, &sample); } if( ps != -1 ) { sfw_sample_ps_t sample = { .ps_value = (ps < 1) ? 0 : 10, .ps_withinProximity = (ps < 1) ? true : false, }; sfw_plugin_t *plugin = sfw_service_plugin(sfw_service, SFW_SENSOR_ID_PS); sfw_plugin_notify(plugin, NOTIFY_EVDEV, &sample); } EXIT: if( !keep && *id ) { *id = 0; mce_log(LL_CRIT, "stopping io watch"); } /* wakelock must be released when we are done with the data */ wakelock_unlock("mce_input_handler"); return keep; } /** Stop I/O watch for als evdev device node */ static void mce_sensorfw_als_detach(void) { if( als_evdev_id ) { g_source_remove(als_evdev_id), als_evdev_id = 0; } } /** Use evdev file descriptor as ALS data source * * Called from evdev probing if ALS device node is detected. * * Caller expects the file descriptor to be owned by * sensor module after the call and it must thus be closed * if input monitoring is not succesfully initiated. * * @param fd file descriptor */ void mce_sensorfw_als_attach(int fd) { if( fd == -1 ) goto EXIT; mce_sensorfw_als_detach(); struct input_absinfo info; memset(&info, 0, sizeof info); if( ioctl(fd, EVIOCGABS(ABS_MISC), &info) == -1 ) { mce_log(LL_ERR, "EVIOCGABS(%s): %m", "ABS_MISC"); goto EXIT; } /* Note: als_evdev_id must be set before calling als_notify() */ als_evdev_id = sfw_socket_add_notify(fd, true, G_IO_IN, mce_sensorfw_evdev_cb, &als_evdev_id); if( !als_evdev_id ) goto EXIT; /* The I/O watch owns the file descriptor now */ fd = -1; mce_log(LL_INFO, "ALS: %d (initial)", info.value); sfw_sample_als_t sample = { .als_value = info.value, }; sfw_plugin_t *plugin = sfw_service_plugin(sfw_service, SFW_SENSOR_ID_ALS); sfw_plugin_notify(plugin, NOTIFY_EVDEV, &sample); EXIT: if( fd != -1 ) close(fd); return; } /** Stop I/O watch for ps evdev device node */ static void mce_sensorfw_ps_detach(void) { if( ps_evdev_id ) { g_source_remove(ps_evdev_id), ps_evdev_id = 0; } } /** Use evdev file descriptor as PS data source * * Called from evdev probing if PS device node is detected. * * Caller expects the file descriptor to be owned by * sensor module after the call and it must thus be closed * if input monitoring is not succesfully initiated. * * @param fd file descriptor */ void mce_sensorfw_ps_attach(int fd) { if( fd == -1 ) goto EXIT; mce_sensorfw_ps_detach(); struct input_absinfo info; memset(&info, 0, sizeof info); if( ioctl(fd, EVIOCGABS(ABS_DISTANCE), &info) == -1 ) { mce_log(LL_ERR, "EVIOCGABS(%s): %m", "ABS_DISTANCE"); goto EXIT; } /* Note: ps_evdev_id must be set before calling ps_notify() */ ps_evdev_id = sfw_socket_add_notify(fd, true, G_IO_IN, mce_sensorfw_evdev_cb, &ps_evdev_id); if( !ps_evdev_id ) goto EXIT; /* The I/O watch owns the file descriptor now */ fd = -1; mce_log(LL_NOTICE, "PS: %d (initial)", info.value); int ps = info.value; sfw_sample_ps_t sample = { .ps_value = (ps < 1) ? 0 : 10, .ps_withinProximity = (ps < 1) ? true : false, }; sfw_plugin_t *plugin = sfw_service_plugin(sfw_service, SFW_SENSOR_ID_PS); sfw_plugin_notify(plugin, NOTIFY_EVDEV, &sample); EXIT: if( fd != -1 ) close(fd); } // ---------------------------------------------------------------- /** Handle name owner changed signals for SENSORFW_SERVICE */ static gboolean sfw_name_owner_changed_cb(DBusMessage *const msg) { DBusError err = DBUS_ERROR_INIT; const char *name = 0; const char *prev = 0; const char *curr = 0; if( !msg ) goto EXIT; if( !dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &prev, DBUS_TYPE_STRING, &curr, DBUS_TYPE_INVALID) ) { mce_log(LL_ERR, "Failed to parse name owner signal: %s: %s", err.name, err.message); goto EXIT; } if( !name || strcmp(name, SENSORFW_SERVICE) ) goto EXIT; if( !sfw_service ) goto EXIT; if( curr && *curr ) sfw_service_do_start(sfw_service); else sfw_service_do_stop(sfw_service); EXIT: dbus_error_free(&err); return TRUE; } /** Array of dbus message handlers */ static mce_dbus_handler_t sfw_dbus_handlers[] = { /* signals */ { .interface = DBUS_INTERFACE_DBUS, .name = DBUS_SIGNAL_NAME_OWNER_CHANGED, .rules = "arg0='"SENSORFW_SERVICE"'", .type = DBUS_MESSAGE_TYPE_SIGNAL, .callback = sfw_name_owner_changed_cb, }, /* sentinel */ { .interface = 0 } }; // ---------------------------------------------------------------- /** Initialize mce sensorfw module */ bool mce_sensorfw_init(void) { /* Register D-Bus handlers */ mce_dbus_handler_register_array(sfw_dbus_handlers); /* Start tracking sensord availablity */ sfw_service = sfw_service_create(); sfw_service_do_query(sfw_service); /* From proximity sensor reporting point of view MCE start up * needs to be handled as a special case. */ sfw_exception_start(SFW_EXCEPTION_LENGTH_MCE_STARTING_UP); /* In sensor test mode, enable all known sensors */ if( mce_in_sensortest_mode() ) { for( sensor_id_t id = 0; id < SFW_SENSOR_ID_COUNT; ++id ) sfw_service_set_sensor(sfw_service, id, true); } return true; } /** Cleanup mce sensorfw module */ void mce_sensorfw_quit(void) { /* Remove evdev I/O watches */ mce_sensorfw_ps_detach(); mce_sensorfw_als_detach(); /* Remove D-Bus handlers */ mce_dbus_handler_unregister_array(sfw_dbus_handlers); /* Stop tracking sensord availablity */ sfw_service_delete(sfw_service), sfw_service = 0; sfw_exception_cancel(); }