device: Add simple temperature model for devices
This temperature model has three states: * COLD * WARM * HOT Device drivers can define the time it requires for the device to get HOT and COLD. The underlying model assumes an exponential warming and cooling process and enforces a cool-off time after the device has reached the HOT state. This cool down period is however shorter than the specified time in the driver.
This commit is contained in:
parent
96e5888110
commit
a2d950044d
8 changed files with 243 additions and 0 deletions
|
@ -1121,6 +1121,7 @@ fpi_device_elanmoc_class_init (FpiDeviceElanmocClass *klass)
|
|||
dev_class->scan_type = FP_SCAN_TYPE_PRESS;
|
||||
dev_class->id_table = id_table;
|
||||
dev_class->nr_enroll_stages = ELAN_MOC_ENROLL_TIMES;
|
||||
dev_class->temp_hot_seconds = -1;
|
||||
|
||||
dev_class->open = elanmoc_open;
|
||||
dev_class->close = elanmoc_close;
|
||||
|
|
|
@ -1565,6 +1565,7 @@ fpi_device_goodixmoc_class_init (FpiDeviceGoodixMocClass *klass)
|
|||
dev_class->scan_type = FP_SCAN_TYPE_PRESS;
|
||||
dev_class->id_table = id_table;
|
||||
dev_class->nr_enroll_stages = DEFAULT_ENROLL_SAMPLES;
|
||||
dev_class->temp_hot_seconds = -1;
|
||||
|
||||
dev_class->open = gx_fp_init;
|
||||
dev_class->close = gx_fp_exit;
|
||||
|
|
|
@ -1385,6 +1385,7 @@ fpi_device_synaptics_class_init (FpiDeviceSynapticsClass *klass)
|
|||
dev_class->scan_type = FP_SCAN_TYPE_PRESS;
|
||||
dev_class->id_table = id_table;
|
||||
dev_class->nr_enroll_stages = ENROLL_SAMPLES;
|
||||
dev_class->temp_hot_seconds = -1;
|
||||
|
||||
dev_class->open = dev_init;
|
||||
dev_class->close = dev_exit;
|
||||
|
|
|
@ -22,6 +22,23 @@
|
|||
|
||||
#include "fpi-device.h"
|
||||
|
||||
/* Chosen so that if we turn on after WARM -> COLD, it takes exactly one time
|
||||
* constant to go from COLD -> HOT.
|
||||
* TEMP_COLD_THRESH = 1 / (e + 1)
|
||||
*/
|
||||
#define TEMP_COLD_THRESH (0.26894142136999512075)
|
||||
#define TEMP_WARM_HOT_THRESH (1.0 - TEMP_COLD_THRESH)
|
||||
#define TEMP_HOT_WARM_THRESH (0.5)
|
||||
|
||||
/* Delay updates by 100ms to avoid hitting the border exactly */
|
||||
#define TEMP_DELAY_SECONDS 0.1
|
||||
|
||||
/* Hopefully 3min is long enough to not get in the way, while also not
|
||||
* properly overheating any devices.
|
||||
*/
|
||||
#define DEFAULT_TEMP_HOT_SECONDS (3 * 60)
|
||||
#define DEFAULT_TEMP_COLD_SECONDS (9 * 60)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FpDeviceType type;
|
||||
|
@ -58,6 +75,15 @@ typedef struct
|
|||
/* State for tasks */
|
||||
gboolean wait_for_finger;
|
||||
FpFingerStatusFlags finger_status;
|
||||
|
||||
/* Device temperature model information and state */
|
||||
GSource *temp_timeout;
|
||||
FpTemperature temp_current;
|
||||
gint32 temp_hot_seconds;
|
||||
gint32 temp_cold_seconds;
|
||||
gint64 temp_last_update;
|
||||
gboolean temp_last_active;
|
||||
gdouble temp_current_ratio;
|
||||
} FpDevicePrivate;
|
||||
|
||||
|
||||
|
@ -88,3 +114,6 @@ typedef struct
|
|||
} FpMatchData;
|
||||
|
||||
void match_data_free (FpMatchData *match_data);
|
||||
|
||||
void fpi_device_update_temp (FpDevice *device,
|
||||
gboolean is_active);
|
||||
|
|
|
@ -48,6 +48,7 @@ enum {
|
|||
PROP_NR_ENROLL_STAGES,
|
||||
PROP_SCAN_TYPE,
|
||||
PROP_FINGER_STATUS,
|
||||
PROP_TEMPERATURE,
|
||||
PROP_FPI_ENVIRON,
|
||||
PROP_FPI_USB_DEVICE,
|
||||
PROP_FPI_UDEV_DATA_SPIDEV,
|
||||
|
@ -151,6 +152,36 @@ fp_device_constructed (GObject *object)
|
|||
priv->device_name = g_strdup (cls->full_name);
|
||||
priv->device_id = g_strdup ("0");
|
||||
|
||||
if (cls->temp_hot_seconds > 0)
|
||||
{
|
||||
priv->temp_hot_seconds = cls->temp_hot_seconds;
|
||||
priv->temp_cold_seconds = cls->temp_cold_seconds;
|
||||
g_assert (priv->temp_cold_seconds > 0);
|
||||
}
|
||||
else if (cls->temp_hot_seconds == 0)
|
||||
{
|
||||
priv->temp_hot_seconds = DEFAULT_TEMP_HOT_SECONDS;
|
||||
priv->temp_cold_seconds = DEFAULT_TEMP_COLD_SECONDS;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Temperature management disabled */
|
||||
priv->temp_hot_seconds = -1;
|
||||
priv->temp_cold_seconds = -1;
|
||||
}
|
||||
|
||||
/* Start out at not completely cold (i.e. assume we are only at the upper
|
||||
* bound of COLD).
|
||||
* To be fair, the warm-up from 0 to WARM should be really short either way.
|
||||
*
|
||||
* Note that a call to fpi_device_update_temp() is not needed here as no
|
||||
* timeout must be registered.
|
||||
*/
|
||||
priv->temp_current = FP_TEMPERATURE_COLD;
|
||||
priv->temp_current_ratio = TEMP_COLD_THRESH;
|
||||
priv->temp_last_update = g_get_monotonic_time ();
|
||||
priv->temp_last_active = FALSE;
|
||||
|
||||
G_OBJECT_CLASS (fp_device_parent_class)->constructed (object);
|
||||
}
|
||||
|
||||
|
@ -165,6 +196,8 @@ fp_device_finalize (GObject *object)
|
|||
if (priv->is_open)
|
||||
g_warning ("User destroyed open device! Not cleaning up properly!");
|
||||
|
||||
g_clear_pointer (&priv->temp_timeout, g_source_destroy);
|
||||
|
||||
g_slist_free_full (priv->sources, (GDestroyNotify) g_source_destroy);
|
||||
|
||||
g_clear_pointer (&priv->current_idle_cancel_source, g_source_destroy);
|
||||
|
@ -204,6 +237,10 @@ fp_device_get_property (GObject *object,
|
|||
g_value_set_flags (value, priv->finger_status);
|
||||
break;
|
||||
|
||||
case PROP_TEMPERATURE:
|
||||
g_value_set_enum (value, priv->temp_current);
|
||||
break;
|
||||
|
||||
case PROP_DRIVER:
|
||||
g_value_set_static_string (value, FP_DEVICE_GET_CLASS (self)->id);
|
||||
break;
|
||||
|
@ -358,6 +395,13 @@ fp_device_class_init (FpDeviceClass *klass)
|
|||
FP_TYPE_FINGER_STATUS_FLAGS, FP_FINGER_STATUS_NONE,
|
||||
G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
|
||||
|
||||
properties[PROP_TEMPERATURE] =
|
||||
g_param_spec_enum ("temperature",
|
||||
"Temperature",
|
||||
"The temperature estimation for device to prevent overheating.",
|
||||
FP_TYPE_TEMPERATURE, FP_TEMPERATURE_COLD,
|
||||
G_PARAM_STATIC_STRINGS | G_PARAM_READABLE);
|
||||
|
||||
properties[PROP_DRIVER] =
|
||||
g_param_spec_string ("driver",
|
||||
"Driver",
|
||||
|
@ -616,6 +660,25 @@ fp_device_get_nr_enroll_stages (FpDevice *device)
|
|||
return priv->nr_enroll_stages;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_device_get_temperature:
|
||||
* @device: A #FpDevice
|
||||
*
|
||||
* Retrieves simple temperature information for device. It is not possible
|
||||
* to use a device when this is #FP_TEMPERATURE_HOT.
|
||||
*
|
||||
* Returns: The current temperature estimation.
|
||||
*/
|
||||
FpTemperature
|
||||
fp_device_get_temperature (FpDevice *device)
|
||||
{
|
||||
FpDevicePrivate *priv = fp_device_get_instance_private (device);
|
||||
|
||||
g_return_val_if_fail (FP_IS_DEVICE (device), -1);
|
||||
|
||||
return priv->temp_current;
|
||||
}
|
||||
|
||||
/**
|
||||
* fp_device_supports_identify:
|
||||
* @device: A #FpDevice
|
||||
|
@ -902,6 +965,8 @@ fp_device_enroll (FpDevice *device,
|
|||
priv->current_task = g_steal_pointer (&task);
|
||||
maybe_cancel_on_cancelled (device, cancellable);
|
||||
|
||||
fpi_device_update_temp (device, TRUE);
|
||||
|
||||
data = g_new0 (FpEnrollData, 1);
|
||||
data->print = g_object_ref_sink (template_print);
|
||||
data->enroll_progress_cb = progress_cb;
|
||||
|
@ -995,6 +1060,8 @@ fp_device_verify (FpDevice *device,
|
|||
priv->current_task = g_steal_pointer (&task);
|
||||
maybe_cancel_on_cancelled (device, cancellable);
|
||||
|
||||
fpi_device_update_temp (device, TRUE);
|
||||
|
||||
data = g_new0 (FpMatchData, 1);
|
||||
data->enrolled_print = g_object_ref (enrolled_print);
|
||||
data->match_cb = match_cb;
|
||||
|
@ -1114,6 +1181,8 @@ fp_device_identify (FpDevice *device,
|
|||
priv->current_task = g_steal_pointer (&task);
|
||||
maybe_cancel_on_cancelled (device, cancellable);
|
||||
|
||||
fpi_device_update_temp (device, TRUE);
|
||||
|
||||
data = g_new0 (FpMatchData, 1);
|
||||
/* We cannot store the gallery directly, because the ptr array may not own
|
||||
* a reference to each print. Also, the caller could in principle modify the
|
||||
|
@ -1231,6 +1300,8 @@ fp_device_capture (FpDevice *device,
|
|||
priv->current_task = g_steal_pointer (&task);
|
||||
maybe_cancel_on_cancelled (device, cancellable);
|
||||
|
||||
fpi_device_update_temp (device, TRUE);
|
||||
|
||||
priv->wait_for_finger = wait_for_finger;
|
||||
|
||||
cls->capture (device);
|
||||
|
|
|
@ -81,6 +81,23 @@ typedef enum {
|
|||
FP_SCAN_TYPE_PRESS,
|
||||
} FpScanType;
|
||||
|
||||
/**
|
||||
* FpTemperature:
|
||||
* @FP_TEMPERATURE_COLD: Sensor is considered cold.
|
||||
* @FP_TEMPERATURE_WARM: Sensor is warm, usage time may be limited.
|
||||
* @FP_TEMPERATURE_HOT: Sensor is hot and cannot be used.
|
||||
*
|
||||
* When a device is created, it is assumed to be cold. Applications such as
|
||||
* fprintd may want to ensure all devices on the system are cold before
|
||||
* shutting down in order to ensure that the cool-off period is not violated
|
||||
* because the internal libfprint state about the device is lost.
|
||||
*/
|
||||
typedef enum {
|
||||
FP_TEMPERATURE_COLD,
|
||||
FP_TEMPERATURE_WARM,
|
||||
FP_TEMPERATURE_HOT,
|
||||
} FpTemperature;
|
||||
|
||||
/**
|
||||
* FpDeviceRetry:
|
||||
* @FP_DEVICE_RETRY_GENERAL: The scan did not succeed due to poor scan quality
|
||||
|
@ -201,6 +218,7 @@ gboolean fp_device_is_open (FpDevice *device);
|
|||
FpScanType fp_device_get_scan_type (FpDevice *device);
|
||||
FpFingerStatusFlags fp_device_get_finger_status (FpDevice *device);
|
||||
gint fp_device_get_nr_enroll_stages (FpDevice *device);
|
||||
FpTemperature fp_device_get_temperature (FpDevice *device);
|
||||
|
||||
FpDeviceFeature fp_device_get_features (FpDevice *device);
|
||||
gboolean fp_device_has_feature (FpDevice *device,
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
*/
|
||||
|
||||
#define FP_COMPONENT "device"
|
||||
#include <math.h>
|
||||
|
||||
#include "fpi-log.h"
|
||||
|
||||
#include "fp-device-private.h"
|
||||
|
@ -851,6 +853,8 @@ fp_device_task_return_in_idle_cb (gpointer user_data)
|
|||
priv->current_action = FPI_DEVICE_ACTION_NONE;
|
||||
priv->current_task_idle_return_source = NULL;
|
||||
|
||||
fpi_device_update_temp (data->device, FALSE);
|
||||
|
||||
if (action == FPI_DEVICE_ACTION_OPEN &&
|
||||
data->type != FP_DEVICE_TASK_RETURN_ERROR)
|
||||
{
|
||||
|
@ -1685,3 +1689,113 @@ fpi_device_report_finger_status_changes (FpDevice *device,
|
|||
|
||||
return fpi_device_report_finger_status (device, finger_status);
|
||||
}
|
||||
|
||||
static void
|
||||
update_temp_timeout (FpDevice *device, gpointer user_data)
|
||||
{
|
||||
FpDevicePrivate *priv = fp_device_get_instance_private (device);
|
||||
|
||||
fpi_device_update_temp (device, priv->temp_last_active);
|
||||
}
|
||||
|
||||
/**
|
||||
* fpi_device_update_temp:
|
||||
* @device: The #FpDevice
|
||||
* @is_active: Whether the device is now active
|
||||
*
|
||||
* Purely internal function to update the temperature. Also ensure that the
|
||||
* state is updated once a threshold is reached.
|
||||
*/
|
||||
void
|
||||
fpi_device_update_temp (FpDevice *device, gboolean is_active)
|
||||
{
|
||||
FpDevicePrivate *priv = fp_device_get_instance_private (device);
|
||||
gint64 now = g_get_monotonic_time ();
|
||||
gdouble passed_seconds;
|
||||
gdouble alpha;
|
||||
gdouble next_threshold;
|
||||
gdouble old_ratio;
|
||||
FpTemperature old_temp;
|
||||
g_autofree char *old_temp_str = NULL;
|
||||
g_autofree char *new_temp_str = NULL;
|
||||
|
||||
if (priv->temp_hot_seconds < 0)
|
||||
{
|
||||
g_debug ("Not updating temperature model, device can run continuously!");
|
||||
return;
|
||||
}
|
||||
|
||||
passed_seconds = (now - priv->temp_last_update) / 1e6;
|
||||
old_ratio = priv->temp_current_ratio;
|
||||
|
||||
if (priv->temp_last_active)
|
||||
{
|
||||
alpha = exp (-passed_seconds / priv->temp_hot_seconds);
|
||||
priv->temp_current_ratio = alpha * priv->temp_current_ratio + 1 - alpha;
|
||||
}
|
||||
else
|
||||
{
|
||||
alpha = exp (-passed_seconds / priv->temp_cold_seconds);
|
||||
priv->temp_current_ratio = alpha * priv->temp_current_ratio;
|
||||
}
|
||||
|
||||
priv->temp_last_active = is_active;
|
||||
priv->temp_last_update = now;
|
||||
|
||||
old_temp = priv->temp_current;
|
||||
if (priv->temp_current_ratio < TEMP_COLD_THRESH)
|
||||
{
|
||||
priv->temp_current = FP_TEMPERATURE_COLD;
|
||||
next_threshold = is_active ? TEMP_COLD_THRESH : -1.0;
|
||||
}
|
||||
else if (priv->temp_current_ratio < TEMP_HOT_WARM_THRESH)
|
||||
{
|
||||
priv->temp_current = FP_TEMPERATURE_WARM;
|
||||
next_threshold = is_active ? TEMP_WARM_HOT_THRESH : TEMP_COLD_THRESH;
|
||||
}
|
||||
else if (priv->temp_current_ratio < TEMP_WARM_HOT_THRESH)
|
||||
{
|
||||
/* Keep HOT until we reach TEMP_HOT_WARM_THRESH */
|
||||
if (priv->temp_current != FP_TEMPERATURE_HOT)
|
||||
priv->temp_current = FP_TEMPERATURE_WARM;
|
||||
|
||||
next_threshold = is_active ? TEMP_WARM_HOT_THRESH : TEMP_HOT_WARM_THRESH;
|
||||
}
|
||||
else
|
||||
{
|
||||
priv->temp_current = FP_TEMPERATURE_HOT;
|
||||
next_threshold = is_active ? -1.0 : TEMP_HOT_WARM_THRESH;
|
||||
}
|
||||
|
||||
old_temp_str = g_enum_to_string (FP_TYPE_TEMPERATURE, old_temp);
|
||||
new_temp_str = g_enum_to_string (FP_TYPE_TEMPERATURE, priv->temp_current);
|
||||
g_debug ("Updated temperature model after %0.2f seconds, ratio %0.2f -> %0.2f, active %d -> %d, %s -> %s",
|
||||
passed_seconds,
|
||||
old_ratio,
|
||||
priv->temp_current_ratio,
|
||||
priv->temp_last_active,
|
||||
is_active,
|
||||
old_temp_str,
|
||||
new_temp_str);
|
||||
|
||||
if (priv->temp_current != old_temp)
|
||||
g_object_notify (G_OBJECT (device), "temperature");
|
||||
|
||||
g_clear_pointer (&priv->temp_timeout, g_source_destroy);
|
||||
|
||||
if (next_threshold < 0)
|
||||
return;
|
||||
|
||||
/* Set passed_seconds to the time until the next update is needed */
|
||||
if (is_active)
|
||||
passed_seconds = -priv->temp_hot_seconds * log ((next_threshold - 1.0) / (priv->temp_current_ratio - 1.0));
|
||||
else
|
||||
passed_seconds = -priv->temp_cold_seconds * log (next_threshold / priv->temp_current_ratio);
|
||||
|
||||
passed_seconds += TEMP_DELAY_SECONDS;
|
||||
|
||||
priv->temp_timeout = fpi_device_add_timeout (device,
|
||||
passed_seconds * 1000,
|
||||
update_temp_timeout,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
|
|
@ -82,6 +82,10 @@ struct _FpIdEntry
|
|||
* fpi_device_set_nr_enroll_stages() from @probe if this is dynamic.
|
||||
* @scan_type: The scan type of supported devices; use
|
||||
* fpi_device_set_scan_type() from @probe if this is dynamic.
|
||||
* @temp_hot_seconds: Assumed time in seconds for the device to become too hot
|
||||
* after being mostly cold. Set to -1 if the device can be always-on.
|
||||
* @temp_cold_seconds: Assumed time in seconds for the device to be mostly cold
|
||||
* after having been too hot to operate.
|
||||
* @usb_discover: Class method to check whether a USB device is supported by
|
||||
* the driver. Should return 0 if the device is unsupported and a positive
|
||||
* score otherwise. The default score is 50 and the driver with the highest
|
||||
|
@ -142,6 +146,10 @@ struct _FpDeviceClass
|
|||
gint nr_enroll_stages;
|
||||
FpScanType scan_type;
|
||||
|
||||
/* Simple device temperature model constants */
|
||||
gint32 temp_hot_seconds;
|
||||
gint32 temp_cold_seconds;
|
||||
|
||||
/* Callbacks */
|
||||
gint (*usb_discover) (GUsbDevice *usb_device);
|
||||
void (*probe) (FpDevice *device);
|
||||
|
|
Loading…
Reference in a new issue