powerd-0.16+16.04.20160204.1/ 0000755 0000156 0000165 00000000000 12654662757 015462 5 ustar pbuser pbgroup 0000000 0000000 powerd-0.16+16.04.20160204.1/cmake/ 0000755 0000156 0000165 00000000000 12654662757 016542 5 ustar pbuser pbgroup 0000000 0000000 powerd-0.16+16.04.20160204.1/cmake/UseGSettings.cmake 0000644 0000156 0000165 00000003716 12654662605 022127 0 ustar pbuser pbgroup 0000000 0000000 # GSettings.cmake, CMake macros written for Marlin, feel free to re-use them.
option (GSETTINGS_LOCALINSTALL "Install GSettings Schemas locally instead of to the GLib prefix" ${LOCAL_INSTALL})
option (GSETTINGS_COMPILE "Compile GSettings Schemas after installation" ${GSETTINGS_LOCALINSTALL})
if(GSETTINGS_LOCALINSTALL)
message(STATUS "GSettings schemas will be installed locally.")
endif()
if(GSETTINGS_COMPILE)
message(STATUS "GSettings schemas will be compiled.")
endif()
macro(add_schema SCHEMA_NAME)
set(PKG_CONFIG_EXECUTABLE pkg-config)
# Have an option to not install the schema into where GLib is
if (GSETTINGS_LOCALINSTALL)
SET (GSETTINGS_DIR "${CMAKE_INSTALL_PREFIX}/share/glib-2.0/schemas/")
else (GSETTINGS_LOCALINSTALL)
execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} glib-2.0 --variable prefix OUTPUT_VARIABLE _glib_prefix OUTPUT_STRIP_TRAILING_WHITESPACE)
SET (GSETTINGS_DIR "${_glib_prefix}/share/glib-2.0/schemas/")
endif (GSETTINGS_LOCALINSTALL)
# Run the validator and error if it fails
execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} gio-2.0 --variable glib_compile_schemas OUTPUT_VARIABLE _glib_comple_schemas OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process (COMMAND ${_glib_comple_schemas} --dry-run --schema-file=${CMAKE_CURRENT_SOURCE_DIR}/${SCHEMA_NAME} ERROR_VARIABLE _schemas_invalid OUTPUT_STRIP_TRAILING_WHITESPACE)
if (_schemas_invalid)
message (SEND_ERROR "Schema validation error: ${_schemas_invalid}")
endif (_schemas_invalid)
# Actually install and recomple schemas
message (STATUS "GSettings schemas will be installed into ${GSETTINGS_DIR}")
install (FILES ${CMAKE_CURRENT_SOURCE_DIR}/${SCHEMA_NAME} DESTINATION ${GSETTINGS_DIR} OPTIONAL)
if (GSETTINGS_COMPILE)
install (CODE "message (STATUS \"Compiling GSettings schemas\")")
install (CODE "execute_process (COMMAND ${_glib_comple_schemas} ${GSETTINGS_DIR})")
endif ()
endmacro()
powerd-0.16+16.04.20160204.1/cmake/UseGdbusCodegen.cmake 0000644 0000156 0000165 00000002142 12654662605 022541 0 ustar pbuser pbgroup 0000000 0000000 cmake_minimum_required(VERSION 2.6)
if(POLICY CMP0011)
cmake_policy(SET CMP0011 NEW)
endif(POLICY CMP0011)
find_program(GDBUS_CODEGEN NAMES gdbus-codegen DOC "gdbus-codegen executable")
if(NOT GDBUS_CODEGEN)
message(FATAL_ERROR "Excutable gdbus-codegen not found")
endif()
function(add_gdbus_codegen)
set(_one_value OUTFILES NAME PREFIX NAMESPACE SERVICE_XML)
set(_multi_value DEPENDS)
cmake_parse_arguments (arg "" "${_one_value}" "${_multi_value}" ${ARGN})
if(arg_PREFIX)
set(PREFIX --interface-prefix ${arg_PREFIX})
endif()
if(arg_NAMESPACE)
set(NAMESPACE --c-namespace ${arg_NAMESPACE})
endif()
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${arg_NAME}.h" "${CMAKE_CURRENT_BINARY_DIR}/${arg_NAME}.c"
COMMAND "${GDBUS_CODEGEN}"
--generate-c-code "${arg_NAME}"
${PREFIX}
${NAMESPACE}
"${arg_SERVICE_XML}"
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${arg_DEPENDS} "${arg_SERVICE_XML}"
)
set(${arg_OUTFILES} ${${arg_OUTFILES}} "${CMAKE_CURRENT_BINARY_DIR}/${arg_NAME}.c" PARENT_SCOPE)
endfunction(add_gdbus_codegen)
powerd-0.16+16.04.20160204.1/cmake/COPYING-CMAKE-SCRIPTS 0000644 0000156 0000165 00000002457 12654662605 021540 0 ustar pbuser pbgroup 0000000 0000000 Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
powerd-0.16+16.04.20160204.1/src/ 0000755 0000156 0000165 00000000000 12654662757 016251 5 ustar pbuser pbgroup 0000000 0000000 powerd-0.16+16.04.20160204.1/src/stats.c 0000644 0000156 0000165 00000021365 12654662605 017552 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013 Canonical Ltd.
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "powerd-internal.h"
#include "powerd-dbus.h"
#include "log.h"
#define USECS_PER_SEC 1000000
#define NSECS_PER_USEC 1000
struct client_stats {
const char *dbus_name;
GSList *sys_stats;
GSList *disp_stats;
};
struct sys_request_stats {
const char *name;
unsigned active_count;
guint64 active_time;
guint64 max_active_time;
guint64 active_since;
};
GHashTable *stats_hash;
static guint64 get_usecs(void)
{
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
powerd_error("Could not get monotonic time: %s", strerror(errno));
return 0;
}
return (guint64)ts.tv_sec * USECS_PER_SEC +
(ts.tv_nsec + NSECS_PER_USEC / 2) / NSECS_PER_USEC;
}
static struct client_stats *alloc_client(const char *dbus_name)
{
struct client_stats *client = calloc(1, sizeof(*client));
if (!client) {
powerd_error("Error allocating client for statistics");
return NULL;
}
client->dbus_name = strdup(dbus_name);
if (!client->dbus_name) {
powerd_error("Error duplicating client dbus name for statistics");
free(client);
return NULL;
}
return client;
}
static struct sys_request_stats *alloc_sys_stats(const char *name)
{
struct sys_request_stats *stats = calloc(1, sizeof(*stats));
if (!stats) {
powerd_error("Error allocating memory for system request statistics");
return NULL;
}
stats->name = strdup(name);
if (!stats->name) {
powerd_error("Error duplicating request name for statistics");
free(stats);
return NULL;
}
return stats;
}
static gint sys_stats_comp(gconstpointer a, gconstpointer b)
{
const struct sys_request_stats *stats = a;
const char *name = b;
return strcmp(stats->name, name);
}
static struct client_stats *lookup_client(const char *dbus_name,
gboolean alloc)
{
struct client_stats *client;
if (!stats_hash)
return NULL;
client = g_hash_table_lookup(stats_hash, dbus_name);
if (!client && alloc) {
client = alloc_client(dbus_name);
if (client)
g_hash_table_insert(stats_hash, (gpointer)client->dbus_name,
client);
}
return client;
}
void powerd_account_request_sys_state(const char *dbus_name, const char *name)
{
struct client_stats *client;
struct sys_request_stats *stats;
GSList *item;
client = lookup_client(dbus_name, TRUE);
if (!client)
return;
item = g_slist_find_custom(client->sys_stats, name, sys_stats_comp);
if (item) {
stats = item->data;
} else {
stats = alloc_sys_stats(name);
if (!stats)
return;
client->sys_stats = g_slist_prepend(client->sys_stats, stats);
}
stats->active_count++;
stats->active_since = get_usecs();
}
void powerd_account_clear_sys_state(const char *dbus_name, const char *name)
{
struct client_stats *client;
struct sys_request_stats *stats;
GSList *item;
guint64 duration;
if (!stats_hash)
return;
client = lookup_client(dbus_name, FALSE);
if (!client)
return;
item = g_slist_find_custom(client->sys_stats, name, sys_stats_comp);
if (!item)
return;
stats = item->data;
duration = get_usecs() - stats->active_since;
stats->active_since = 0;
stats->active_time += duration;
if (duration > stats->max_active_time)
stats->max_active_time = duration;
}
static void log_sys_req_stats(struct client_stats *client)
{
const char *dbus_name = client->dbus_name;
GSList *cur;
guint64 us;
us = get_usecs();
for (cur = client->sys_stats; cur; cur = g_slist_next(cur)) {
struct sys_request_stats *stats = cur->data;
guint64 active_time, max_active_time;
/*
* Aggregate currently held requests into active_time, and
* consider whether this is greater than the current
* max_active_time.
*/
active_time = stats->active_time;
max_active_time = stats->max_active_time;
if (stats->active_since != 0) {
guint64 duration = us - stats->active_since;
active_time += duration;
if (duration > max_active_time)
max_active_time = duration;
}
powerd_info(" Name: %s", stats->name);
powerd_info(" Owner: %s", dbus_name);
powerd_info(" Active Count: %u", stats->active_count);
powerd_info(" Active Time: %f", (double)active_time / 1000000.0f);
powerd_info(" Max Active Time: %f",
(double)max_active_time / 1000000.0f);
powerd_info(" Active Since: %f",
(double)stats->active_since / 1000000.0f);
}
}
void powerd_log_stats(void)
{
struct sysinfo si;
GHashTableIter iter;
gpointer key, value;
if (!sysinfo(&si))
powerd_info("Uptime: %ld", si.uptime);
if (stats_hash) {
powerd_info("System Request Statistics:");
g_hash_table_iter_init(&iter, stats_hash);
while (g_hash_table_iter_next(&iter, &key, &value))
log_sys_req_stats(value);
}
}
static void sys_stats_list_destroy(gpointer data)
{
struct sys_request_stats *stats = data;
free((void *)stats->name);
free(stats);
}
static void stats_hash_destroy(gpointer data)
{
struct client_stats *client = data;
g_slist_free_full(client->sys_stats, sys_stats_list_destroy);
free((void *)client->dbus_name);
free(client);
}
int powerd_stats_init(void)
{
stats_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
stats_hash_destroy);
if (!stats_hash) {
powerd_warn("Unable to allocate stats hash table");
return -ENOMEM;
}
return 0;
}
void powerd_stats_deinit(void)
{
if (stats_hash)
g_hash_table_destroy(stats_hash);
}
/* Dbus interface support */
static int build_sys_request_list(GVariantBuilder *builder,
struct client_stats *client)
{
const char *dbus_name = client->dbus_name;
GSList *cur;
guint64 us;
int count = 0;
us = get_usecs();
for (cur = client->sys_stats; cur; cur = g_slist_next(cur)) {
struct sys_request_stats *stats = cur->data;
guint64 active_time, max_active_time;
/*
* Aggregate currently held requests into active_time, and
* consider whether this is greater than the current
* max_active_time.
*/
active_time = stats->active_time;
max_active_time = stats->max_active_time;
if (stats->active_since != 0) {
guint64 duration = us - stats->active_since;
active_time += duration;
if (duration > max_active_time)
max_active_time = duration;
}
g_variant_builder_add(builder, "(ssuttt)", dbus_name, stats->name,
stats->active_count, active_time,
max_active_time, stats->active_since);
count++;
}
return count;
}
gboolean handle_get_sys_request_stats(PowerdSource *obj,
GDBusMethodInvocation *invocation)
{
GVariantBuilder *builder;
GVariant *list, *tuple;
GHashTableIter iter;
gpointer key, value;
int count = 0;
builder = g_variant_builder_new(G_VARIANT_TYPE("a(ssuttt)"));
if (stats_hash) {
g_hash_table_iter_init(&iter, stats_hash);
while (g_hash_table_iter_next(&iter, &key, &value))
count += build_sys_request_list(builder, value);
}
if (count == 0) {
g_variant_builder_clear(builder);
list = g_variant_new_array(G_VARIANT_TYPE("a(ssuttt)"), NULL, 0);
} else {
list = g_variant_builder_end(builder);
}
tuple = g_variant_new_tuple(&list, 1);
g_dbus_method_invocation_return_value(invocation, tuple);
g_variant_builder_unref(builder);
return TRUE;
}
powerd-0.16+16.04.20160204.1/src/powerd.h 0000644 0000156 0000165 00000003103 12654662605 017707 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013 Canonical Ltd.
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __POWERD_H__
#define __POWERD_H__
#ifdef __cplusplus
extern "C" {
#endif
enum SysPowerStates {
//Note that callers will be notified of suspend state changes
//but may not request this state.
POWERD_SYS_STATE_SUSPEND = 0,
//The Active state will prevent system suspend
POWERD_SYS_STATE_ACTIVE,
//Substate of Active with disabled proximity based blanking
POWERD_SYS_STATE_ACTIVE_BLANK_ON_PROXIMITY,
POWERD_NUM_POWER_STATES
};
/*
* Flags that can be applied to power states
* TODO Create dedicated power states instead?
*/
enum SysPowerStateFlags {
POWERD_POWER_STATE_FLAG_MASK = 0xFFFF0000,
// disables display blanking on proximity events
POWERD_DISABLE_PROXIMITY_SENSOR_BLANKING = 0x10000,
};
/*
* Clients should treat this as an opaque type. Though this isn't really
* possible until the client library is written.
*/
typedef char powerd_cookie_t[37];
#ifdef __cplusplus
}
#endif
#endif
powerd-0.16+16.04.20160204.1/src/display.c 0000644 0000156 0000165 00000007044 12654662605 020057 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013-2014 Canonical Ltd.
*
* Authors:
* Michael Frey: michael.frey@canonical.com
* Matthew Fischer: matthew.fischer@canonical.com
* Seth Forshee: seth.forshee@canonical.com
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include "powerd-internal.h"
#include "log.h"
static GDBusProxy* uscreen_proxy_get()
{
static GMutex mutex;
static GDBusProxy *uscreen_proxy = NULL;
GDBusProxy *uscreen_proxy_tmp = NULL;
g_mutex_lock(&mutex);
if (uscreen_proxy == NULL)
{
GError *error = NULL;
uscreen_proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
"com.canonical.Unity.Screen",
"/com/canonical/Unity/Screen",
"com.canonical.Unity.Screen",
NULL,
&error);
if (error != NULL) {
powerd_warn("could not connect to Unity.Screen: %s", error->message);
g_error_free(error);
uscreen_proxy = NULL;
}
}
uscreen_proxy_tmp = uscreen_proxy;
g_mutex_unlock(&mutex);
return uscreen_proxy_tmp;
}
static void request_keep_display_on(GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
gpointer user_data)
{
GDBusProxy *uscreen_proxy = NULL;
GVariant *ret = NULL;
GError *error = NULL;
int request_id;
powerd_debug("request_keep_display_on");
uscreen_proxy = uscreen_proxy_get();
if (uscreen_proxy == NULL)
{
powerd_warn("failed creating unity screen proxy to keep display on");
return;
}
ret = g_dbus_proxy_call_sync(uscreen_proxy,
"keepDisplayOn",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
&error);
if (ret == NULL) {
powerd_warn("keepDisplayOn failed: %s", error->message);
g_error_free(error);
return;
}
g_variant_get(ret, "(i)", &request_id);
powerd_debug("keepDisplayOn request succeeded - id: %d", request_id);
g_variant_unref(ret);
}
gboolean turn_display_on(gboolean on, enum DisplayStateChangeReason reason)
{
GDBusProxy *uscreen_proxy = NULL;
powerd_debug("turn_display_on(%d)", on);
if (on)
powerd_hal_signal_activity();
uscreen_proxy = uscreen_proxy_get();
if (uscreen_proxy == NULL)
return FALSE;
g_dbus_proxy_call(uscreen_proxy,
"setScreenPowerMode",
g_variant_new("(si)", (on ? "on" : "off"), reason),
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
NULL,
NULL);
return TRUE;
}
void keep_display_on()
{
powerd_debug("issuing keep_display_on request");
g_bus_watch_name(G_BUS_TYPE_SYSTEM,
"com.canonical.Unity.Screen",
G_BUS_NAME_WATCHER_FLAGS_NONE,
request_keep_display_on,
NULL,
NULL,
NULL);
}
powerd-0.16+16.04.20160204.1/src/log.c 0000644 0000156 0000165 00000002353 12654662605 017171 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013 Canonical Ltd.
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include "log.h"
static int debug = 0;
void powerd_log(int level, const char *fmt, ...)
{
va_list args;
if (level == POWERD_LOG_DEBUG && !debug)
return;
va_start(args, fmt);
vsyslog(level, fmt, args);
va_end(args);
}
void powerd_log_init(void)
{
char *debug_str;
openlog("powerd", LOG_PID, LOG_DAEMON);
debug_str = getenv("POWERD_DEBUG");
if (debug_str) {
if (!strcmp(debug_str, "1")) {
debug = 1;
powerd_debug("debug enabled");
}
}
}
powerd-0.16+16.04.20160204.1/src/autobrightness.c 0000644 0000156 0000165 00000025462 12654662605 021457 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013 Canonical Ltd.
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include "powerd-internal.h"
#include "device-config.h"
#include "log.h"
#include "spline.h"
/*
* Autobrightness Control Algorithm
*
* Automatic backlight adjustments should respond quickly to changes in
* the ambient brightness without constantly adjusting for minor or
* transient changes in ambient brightness. The following mechanisms are
* employed to maintain this balance.
*
* EXPONENTIAL SMOOTHING
*
* Rather than processing incoming lux values directly, an exponential
* moving average is calculated using an algorithm based off of the
* formulation known as "Brown's simple exponential smoothing" [1]:
*
* S(n) = a * x(n) + (1 - a) * S(n - 1)
* = S(n - 1) + a * (x(n) - S(n - 1))
*
* where 0 <= a <= 1. To make smoothing time-based, a is calculated
* based on the amount of time spent at a given lux value.
*
* delta_t = t(n) - t(n - 1)
* a(n) = delta_t / (C + delta_t)
*
* This gives decreasing weight to older samples. Smaller values of C
* will cause faster decay of old samples, giving a faster response
* time, while larger values of C create slower decay and give more
* filtering of transient events.
*
* This code usese two moving averages, one which responds slowly to
* changes and another which responds quickly. The slow average is used
* to detect the trend with less sensitivity to transient events, while
* the fast average is used for picking the brightness once we've
* decided to change it.
*
* HYSTERESIS
*
* To avoid making constant adjustments to screen brightness in response
* to small changes in ambient lighting conditions, the change in lux is
* required to exceed some threshold before changing screen brightness.
*
* DEBOUNCING
*
* Changes in ambient brightness which exceed the hysteresis threshold
* must do so for some minimum amount of time before any changes in
* screen brightness will be made. This "debounces" the incoming lux
* values and also helps to filter out transient changes in ambient
* conditions.
*
* [1] http://en.wikipedia.org/wiki/Exponential_smoothing
*/
#define SMOOTHING_FACTOR_SLOW 2000.0f
#define SMOOTHING_FACTOR_FAST 200.0f
#define HYSTERESIS_FACTOR 0.10f
#define DEBOUNCE_MS 4000
enum ab_state {
AB_STATE_DISABLED,
AB_STATE_INITIALIZING,
AB_STATE_IDLE, /* Idle, nothing to do */
AB_STATE_DEBOUNCE, /* Debouncing lux change */
};
struct ab_status {
enum ab_state state;
guint source_id;
double last_lux, applied_lux;
double average_slow, average_fast;
gint64 last_lux_ms;
struct ab_spline *spline;
};
static gboolean ab_supported = FALSE;
static struct ab_status ab_status;
struct spline *ab_spline;
static gint64 get_ms(void)
{
return g_get_monotonic_time() / 1000;
}
static double aggregate_lux(double old_average, double new_lux,
double smoothing_factor, double time_delta)
{
double alpha;
if (time_delta < 0.0f)
time_delta = 0.0f;
alpha = time_delta / (smoothing_factor + time_delta);
return old_average + alpha * (new_lux - old_average);
}
static enum ab_state process_state_debounce(guint *delay_ms)
{
gint64 now;
double time_delta;
double slow_lux, fast_lux;
double hysteresis, slow_delta, fast_delta;
enum ab_state next;
next = AB_STATE_IDLE;
*delay_ms = 0;
now = get_ms();
time_delta = (double)(now - ab_status.last_lux_ms);
slow_lux = aggregate_lux(ab_status.average_slow, ab_status.last_lux,
SMOOTHING_FACTOR_SLOW, time_delta);
fast_lux = aggregate_lux(ab_status.average_fast, ab_status.last_lux,
SMOOTHING_FACTOR_FAST, time_delta);
powerd_debug("%" PRId64 " slow avg %f fast avg %f last %f",
now, slow_lux, fast_lux, ab_status.last_lux);
/*
* Require that both slow and fast averages exceed hysteresis,
* and in the same direction.
*/
hysteresis = ab_status.applied_lux * HYSTERESIS_FACTOR;
if (hysteresis < 2)
hysteresis = 2;
slow_delta = slow_lux - ab_status.applied_lux;
fast_delta = fast_lux - ab_status.applied_lux;
if ((slow_delta >= hysteresis && fast_delta >= hysteresis) ||
(-slow_delta >= hysteresis && -fast_delta >= hysteresis)) {
int brightness = (int)(spline_interpolate(ab_spline, fast_lux) + 0.5);
powerd_debug("set brightness %d", brightness);
powerd_set_brightness(brightness);
ab_status.applied_lux = fast_lux;
}
hysteresis = ab_status.last_lux * HYSTERESIS_FACTOR;
if (hysteresis < 2)
hysteresis = 2;
if (fabs(fast_lux - ab_status.last_lux) >= hysteresis) {
/*
* Average should settle near the last reported lux. If
* it hasn't made it there yet, continue debouncing.
*/
next = AB_STATE_DEBOUNCE;
*delay_ms = DEBOUNCE_MS;
}
return next;
}
static gboolean ab_process(gpointer unused)
{
guint delay = 0;
ab_status.source_id = 0;
switch (ab_status.state) {
case AB_STATE_DISABLED:
case AB_STATE_INITIALIZING:
case AB_STATE_IDLE:
/* Nothing to do */
break;
case AB_STATE_DEBOUNCE:
ab_status.state = process_state_debounce(&delay);
break;
default:
powerd_warn("Unexpected autobrightness state %d\n", ab_status.state);
ab_status.state = AB_STATE_IDLE;
break;
}
if (delay != 0 && ab_status.state != AB_STATE_IDLE)
ab_status.source_id = g_timeout_add(delay, ab_process, NULL);
return FALSE;
}
static gboolean handle_new_lux(gpointer data)
{
gint64 now;
double lux;
if (!ab_supported)
return FALSE;
now = get_ms();
lux = *(double *)data;
g_free(data);
if (ab_status.state == AB_STATE_DISABLED)
return FALSE;
if (ab_status.state == AB_STATE_INITIALIZING) {
int brightness = (int)(spline_interpolate(ab_spline, lux) + 0.5);
powerd_debug("set brightness %d", brightness);
powerd_set_brightness(brightness);
ab_status.average_slow = lux;
ab_status.average_fast = lux;
ab_status.applied_lux = lux;
ab_status.state = AB_STATE_IDLE;
} else {
double time_delta;
/* Ignore duplicates */
if (lux == ab_status.last_lux)
return FALSE;
time_delta = (double)(now - ab_status.last_lux_ms);
ab_status.average_slow = aggregate_lux(ab_status.average_slow, lux,
SMOOTHING_FACTOR_SLOW,
time_delta);
ab_status.average_fast = aggregate_lux(ab_status.average_fast, lux,
SMOOTHING_FACTOR_FAST,
time_delta);
}
ab_status.last_lux = lux;
ab_status.last_lux_ms = now;
if (ab_status.state == AB_STATE_IDLE) {
ab_status.state = AB_STATE_DEBOUNCE;
ab_status.source_id = g_timeout_add(DEBOUNCE_MS, ab_process, NULL);
}
return FALSE;
}
void powerd_new_als_event(double lux)
{
double *p_lux = g_memdup(&lux, sizeof(lux));
g_timeout_add(0, handle_new_lux, p_lux);
}
/* Must be run from main loop */
void powerd_autobrightness_enable(void)
{
/* Must not leave disabled state if we can't map lux to brightness */
if (!ab_supported)
return;
if (ab_status.state == AB_STATE_DISABLED) {
ab_status.state = AB_STATE_INITIALIZING;
powerd_sensors_als_enable();
}
}
/* Must be run from main loop */
void powerd_autobrightness_disable(void)
{
if (ab_status.state != AB_STATE_DISABLED) {
ab_status.state = AB_STATE_DISABLED;
powerd_sensors_als_disable();
if (ab_status.source_id != 0)
g_source_remove(ab_status.source_id);
}
}
gboolean powerd_autobrightness_available(void)
{
return ab_supported;
}
int powerd_autobrightness_init(void)
{
GValue v = G_VALUE_INIT;
GArray *levels = NULL, *lux = NULL;
double (*mappings)[2] = NULL;
int i;
int ret = -ENODEV;
if (device_config_get("automatic_brightness_available", &v)) {
powerd_info("Could not determine platform autobrightness capability, disabling");
goto error;
}
if (!G_VALUE_HOLDS_BOOLEAN(&v) || !g_value_get_boolean(&v)) {
powerd_info("Platform does not support autobrightness, disabling");
goto error;
}
g_value_unset(&v);
if (device_config_get("autoBrightnessLevels", &v)) {
powerd_info("No device-specific autobrightness lux table defined, disabling");
goto error;
}
if (G_VALUE_HOLDS_BOXED(&v))
lux = g_value_dup_boxed(&v);
g_value_unset(&v);
if (!lux || lux->len == 0) {
powerd_info("Invalid autobrighness lux levels, disabling");
goto error;
}
if (device_config_get("autoBrightnessLcdBacklightValues", &v)) {
powerd_info("No device-specific autobrightness backlight levels defined, disabling");
goto error;
}
if (G_VALUE_HOLDS_BOXED(&v))
levels = g_value_dup_boxed(&v);
g_value_unset(&v);
if (!levels || levels->len == 0) {
powerd_info("Invalid autobrighness backlight levels, disabling");
goto error;
}
/* We should have one more backlight level than lux values */
if (levels->len != lux->len + 1) {
powerd_info("Invalid lux->brightness mappings, autobrightness disabled");
goto error;
}
mappings = g_malloc(2 * sizeof(double) * levels->len);
for (i = 0; i < levels->len; i++) {
mappings[i][0] = (double)((i == 0) ? 0 : g_array_index(lux, guint, i-1));
mappings[i][1] = (double)g_array_index(levels, guint, i);
}
ab_status.state = AB_STATE_DISABLED;
ab_spline = spline_new(mappings, levels->len);
if (!ab_spline) {
ret = -ENOMEM;
goto error;
}
g_free(mappings);
ab_supported = TRUE;
return 0;
error:
if (levels)
g_array_unref(levels);
if (lux)
g_array_unref(lux);
if (mappings)
g_free(mappings);
return ret;
}
void powerd_autobrightness_deinit(void)
{
powerd_autobrightness_disable();
ab_supported = FALSE;
if (ab_spline)
spline_free(ab_spline);
}
powerd-0.16+16.04.20160204.1/src/spline.h 0000644 0000156 0000165 00000001547 12654662605 017713 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013 Canonical Ltd.
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __SPLINE_H__
#define __SPLINE_H__
struct spline;
struct spline *spline_new(double (*points)[2], int num_points);
void spline_free(struct spline *s);
double spline_interpolate(struct spline *s, double x);
#endif
powerd-0.16+16.04.20160204.1/src/powerd-sensors.cpp 0000644 0000156 0000165 00000007306 12654662605 021745 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013 Canonical Ltd.
*
* Authors:
* Ricardo Mendoza: ricardo.mendoza@canonical.com
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "powerd-internal.h"
#include "powerd-sensors.h"
#include "log.h"
#include
#include
#include
namespace
{
UASensorsProximity* prox_sensor;
UASensorsLight* light_sensor;
GMainLoop* main_loop = NULL;
std::atomic allow_synthetic{false};
enum class Proximity { unknown, near, far };
std::atomic current_proximity{Proximity::unknown};
void update_and_emit_proximity(Proximity proximity)
{
current_proximity = proximity;
if (proximity == Proximity::near)
powerd_proximity_event(TRUE);
else if (proximity == Proximity::far)
powerd_proximity_event(FALSE);
}
gboolean emit_synthetic_proximity_far(void *context)
{
if (allow_synthetic)
update_and_emit_proximity(Proximity::far);
return FALSE;
}
void on_new_proximity_event(UASProximityEvent *event, void *context)
{
allow_synthetic = false;
switch (uas_proximity_event_get_distance(event))
{
case U_PROXIMITY_NEAR:
{
update_and_emit_proximity(Proximity::near);
break;
}
case U_PROXIMITY_FAR:
{
update_and_emit_proximity(Proximity::far);
break;
}
}
}
}
void powerd_sensors_proximity_enable(void)
{
//FIXME: Some proximity sensors do not
//send an initial event when enabled and nothing is close
//To work around this, we schedule a synthetic proximity
//far event in case no event has been emitted 500ms
//after enabling the proximity sensor
allow_synthetic = true;
current_proximity = Proximity::unknown;
g_timeout_add(500, emit_synthetic_proximity_far, NULL);
ua_sensors_proximity_enable(prox_sensor);
}
void powerd_sensors_proximity_disable(void)
{
allow_synthetic = false;
ua_sensors_proximity_disable(prox_sensor);
current_proximity = Proximity::unknown;
}
void powerd_sensors_proximity_emit(void)
{
update_and_emit_proximity(current_proximity);
}
void on_new_als_event(UASLightEvent *event, void *context)
{
float lux;
uas_light_event_get_light(event, &lux);
powerd_new_als_event(lux);
}
void powerd_sensors_als_enable(void)
{
if (light_sensor)
ua_sensors_light_enable(light_sensor);
}
void powerd_sensors_als_disable(void)
{
if (light_sensor)
ua_sensors_light_disable(light_sensor);
}
void powerd_sensors_init(GMainLoop *ml)
{
main_loop = ml;
prox_sensor = ua_sensors_proximity_new();
if (prox_sensor != NULL) {
ua_sensors_proximity_set_reading_cb(prox_sensor,
on_new_proximity_event,
NULL);
} else {
powerd_warn("Failed to allocate proximity sensor");
}
light_sensor = ua_sensors_light_new();
if (light_sensor) {
ua_sensors_light_set_reading_cb(light_sensor, on_new_als_event,
NULL);
} else {
powerd_warn("Failed to allocate ambient light sensor");
}
}
powerd-0.16+16.04.20160204.1/src/device-config.h 0000644 0000156 0000165 00000001641 12654662605 021116 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013 Canonical Ltd.
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __DEVICE_CONFIG_H__
#define __DEVICE_CONFIG_H__
#ifdef __cplusplus
extern "C" {
#endif
void device_config_init(void);
void device_config_deinit(void);
int device_config_get(const char *name, GValue *value);
#ifdef __cplusplus
}
#endif
#endif /* __DEVICE_CONFIG_H__ */
powerd-0.16+16.04.20160204.1/src/powerd-client.c 0000644 0000156 0000165 00000010005 12654662605 021155 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013 Canonical Ltd.
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include "powerd-internal.h"
#include "log.h"
struct powerd_client {
const char *dbus_name;
const char *name;
gboolean ack_pending;
};
static GHashTable *client_hash;
static int acks_needed;
static int ack_state;
static void client_ack(struct powerd_client *client)
{
if (!client->ack_pending)
return;
client->ack_pending = FALSE;
if (acks_needed <= 0) {
powerd_error("Client needs ack while total acks needed is %d",
acks_needed);
return;
}
if (--acks_needed == 0)
power_request_transition_acked();
}
/* Must be called from main loop */
int powerd_client_register(const char *dbus_name, const char *name)
{
struct powerd_client *client;
if (!dbus_name || !name)
return -EINVAL;
client = g_new0(struct powerd_client, 1);
if (!client)
return -ENOMEM;
client->dbus_name = g_strdup(dbus_name);
client->name = g_strdup(name);
g_hash_table_insert(client_hash, (gpointer)client->dbus_name, client);
powerd_dbus_name_watch_add(dbus_name);
return 0;
}
/* Must be called from main loop */
void powerd_client_unregister(const char *dbus_name)
{
struct powerd_client *client;
client = g_hash_table_lookup(client_hash, dbus_name);
if (!client)
return;
client_ack(client);
powerd_dbus_name_watch_remove(client->dbus_name);
g_hash_table_remove(client_hash, client->dbus_name);
}
/*
* Must be called from main loop. Returns TRUE if caller should
* wait for acks.
*/
gboolean powerd_client_transition_start(int state)
{
GHashTableIter iter;
gpointer key, value;
acks_needed = 0;
ack_state = state;
g_hash_table_iter_init(&iter, client_hash);
while (g_hash_table_iter_next(&iter, &key, &value)) {
struct powerd_client *client = value;
client->ack_pending = TRUE;
acks_needed++;
}
return acks_needed != 0;
}
/* Must be called from main loop */
void powerd_client_transition_finish(int state)
{
GHashTableIter iter;
gpointer key, value;
if (state != ack_state) {
powerd_warn("Attempt to finish client transition with wrong state");
return;
}
if (acks_needed)
return;
acks_needed = 0;
g_hash_table_iter_init(&iter, client_hash);
while (g_hash_table_iter_next(&iter, &key, &value)) {
struct powerd_client *client = value;
if (client->ack_pending) {
powerd_warn("Client %s (%s) failed to acknowledge state change",
client->name, client->dbus_name);
client->ack_pending = FALSE;
}
}
}
/* Must be called from main loop */
int powerd_client_ack(const char *dbus_name, int state)
{
struct powerd_client *client;
if (state != ack_state)
return -EINVAL;
client = g_hash_table_lookup(client_hash, dbus_name);
if (!client)
return -ENODEV;
client_ack(client);
return 0;
}
static void client_hash_destroy(gpointer data)
{
struct powerd_client *client = data;
g_free((gpointer)client->dbus_name);
g_free((gpointer)client->name);
g_free(client);
}
int powerd_client_init(void)
{
client_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
client_hash_destroy);
return 0;
}
void powerd_client_deinit(void)
{
g_hash_table_destroy(client_hash);
}
powerd-0.16+16.04.20160204.1/src/powerd-object.h 0000644 0000156 0000165 00000003436 12654662605 021164 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013 Canonical Ltd.
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __POWERD_OBJECT_H__
#define __POWERD_OBJECT_H__
#include
#ifdef __cplusplus
extern "C" {
#endif
G_BEGIN_DECLS
#define POWERD_TYPE_SOURCE (powerd_source_get_type ())
#define POWERD_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), POWERD_TYPE_SOURCE, PowerdSource))
#define POWERD_IS_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), POWERD_TYPE_SOURCE))
#define POWERD_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), POWERD_TYPE_SOURCE, PowerdSourceClass))
#define POWERD_IS_SOURCE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), POWERD_TYPE_SOURCE))
#define POWERD_SOURCE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), POWERD_TYPE_SOURCE, PowerdSourceClass))
struct _PowerdSourcePrivate;
typedef struct _PowerdSource PowerdSource;
typedef struct _PowerdSourceClass PowerdSourceClass;
typedef struct _PowerdSourcePrivate PowerdSourcePrivate;
struct _PowerdSourceClass {
GObjectClass parent_class;
};
struct _PowerdSource {
GObject parent;
PowerdSourcePrivate * priv;
};
GType powerd_source_get_type (void);
G_END_DECLS
#ifdef __cplusplus
}
#endif
#endif
powerd-0.16+16.04.20160204.1/src/log.h 0000644 0000156 0000165 00000003457 12654662605 017204 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013 Canonical Ltd.
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __LOG_H__
#define __LOG_H__
#include
#ifdef __cplusplus
extern "C" {
#endif
#define POWERD_LOG_ERROR LOG_ERR
#define POWERD_LOG_WARN LOG_WARNING
#define POWERD_LOG_INFO LOG_INFO
#define POWERD_LOG_DEBUG LOG_DEBUG
#define powerd_error(f, a...) powerd_log(POWERD_LOG_ERROR, f, ##a)
#define powerd_warn(f, a...) powerd_log(POWERD_LOG_WARN, f, ##a)
#define powerd_info(f, a...) powerd_log(POWERD_LOG_INFO, f, ##a)
#define powerd_debug(f, a...) powerd_log(POWERD_LOG_DEBUG, f, ##a)
void powerd_log(int level, const char *fmt, ...)
__attribute__ ((format (printf, 2, 3)));
void powerd_log_init(void);
#define powerd_warn_if_fail(expr) \
do { \
if (!(expr)) \
powerd_warn("%s", #expr); \
} while (0)
#define powerd_return_if_fail(expr) \
do { \
if (!(expr)) { \
powerd_warn("%s: assertion '%s' failed", __func__, #expr); \
return; \
} \
} while (0)
#define powerd_return_val_if_fail(expr,val) \
do { \
if (!(expr)) { \
powerd_warn("%s: assertion '%s' failed", __func__, #expr); \
return val; \
} \
} while (0)
#ifdef __cplusplus
}
#endif
#endif /* __LOG_H__ */
powerd-0.16+16.04.20160204.1/src/spline.c 0000644 0000156 0000165 00000007174 12654662605 017710 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013 Canonical Ltd.
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
struct spline_params {
double k, m;
};
struct spline {
double (*points)[2];
int n;
struct spline_params params[0];
};
int spline_sort_comp(const void *a, const void *b)
{
double x1 = *(double *)a;
double x2 = *(double *)b;
return (int)(x1 - x2);
}
/*
* Initializes a monotone cubic Hermite spline for interpolating
* between the input points, using the Fritsch–Carlson method.
*
* http://en.wikipedia.org/wiki/Monotone_cubic_interpolation
*
* The interpolated points are guaranteed to have piecewise
* monotonicity, and will be fully monotone if the input points
* are monotone.
*/
struct spline *spline_new(double (*points)[2], int num_points)
{
struct spline *s;
struct spline_params *param;
double (*p)[2];
int i;
double alpha, beta, prev_beta;
s = malloc(sizeof(struct spline) +
sizeof(struct spline_params) * num_points);
if (!s)
return NULL;
s->points = malloc(2 * sizeof(double) * num_points);
if (!s->points) {
free(s);
return NULL;
}
s->n = num_points;
/*
* Input points are likely to already be in order of increasing
* x, but sort them just in case.
*/
memcpy(s->points, points, 2 * sizeof(double) * num_points);
qsort(s->points, num_points, 2 * sizeof(double), spline_sort_comp);
p = s->points;
param = s->params;
for (i = 0; i < s->n - 1; i++)
param[i].k = (p[i+1][1] - p[i][1]) / (p[i+1][0] - p[i][0]);
param[0].m = param[0].k;
for (i = 1; i < s->n; i++)
param[i].m = (param[i-1].k + param[i].k) / 2;
param[s->n - 1].m = param[s->n - 2].k;
beta = 0;
for (i = 0; i < s->n - 1; i++) {
if (p[i][1] == p[i+1][1]) {
param[i].m = 0;
param[++i].m = 0;
beta = 0;
continue;
}
prev_beta = beta;
alpha = param[i].m / param[i].k;
beta = param[i+1].m / param[i].k;
if (alpha < 0 || prev_beta < 0) {
param[i].m = 0;
continue;
}
if (alpha > 3)
param[i].m = 3 * param[i].k;
if (beta > 3)
param[i+1].m = 3 * param[i].k;
}
return s;
}
void spline_free(struct spline *s)
{
free(s->points);
free(s);
}
double spline_interpolate(struct spline *s, double x)
{
int i;
double h, t, ret;
for (i = 0; i < s->n - 1; i++) {
if (x >= s->points[i][0] && x < s->points[i+1][0])
break;
}
/* Handle out-of-bounds and boundary cases */
if (i == 0 && x <= s->points[0][0])
return s->points[0][1];
if (i >= s->n - 1)
return s->points[s->n - 1][1];
h = s->points[i+1][0] - s->points[i][0];
t = (x - s->points[i][0]) / h;
ret = s->points[i][1] * (1 + t * t * (2 * t - 3));
ret += h * s->params[i].m * t * (1 + t * (t - 2));
ret += s->points[i+1][1] * t * t * (3 - 2 * t);
ret += h * s->params[i+1].m * t * t * (t - 1);
return ret;
}
powerd-0.16+16.04.20160204.1/src/backlight.c 0000644 0000156 0000165 00000021301 12654662605 020332 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013 Canonical Ltd.
*
* Authors:
* Michael Frey: michael.frey@canonical.com
* Matthew Fischer: matthew.fischer@canonical.com
* Seth Forshee: seth.forshee@canonical.com
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include "powerd-internal.h"
#include "device-config.h"
#include "log.h"
struct light_device_t *light_dev;
enum bl_state {
BL_OFF,
BL_ON,
BL_AUTO,
NUM_BL_STATES
};
/*
* Defaults are Android default settings. They may not work if we
* can't read the per-device values, but they're as good as
* anything else.
*/
int min_user_brightness = 10;
int max_brightness = 255;
int default_brightness = 102;
int dim_brightness = 10;
int current_brightness = -1;
/* Used when setting up brightness */
const int set_brightness_usleep = 5000;
const int set_brightness_step = 2;
static int user_brightness;
int powerd_get_brightness(void)
{
return current_brightness;
}
int powerd_get_max_brightness(void)
{
return max_brightness;
}
static int set_brightness_hal(int brightness)
{
struct light_state_t state = { .flashMode = LIGHT_FLASH_NONE,
.brightnessMode = BRIGHTNESS_MODE_USER };
int ret;
/* color is ARGB, docs specifiy that alpha should be 0xff,
* although it also instructs callers to ignore it. */
state.color = (int)((0xffU << 24) | (brightness << 16) |
(brightness << 8) | brightness);
ret = light_dev->set_light(light_dev, &state);
if (ret != 0)
powerd_error("light_dev: failed to set brightness to %i", brightness);
return ret;
}
int powerd_set_brightness(int brightness)
{
int ret, i;
if (!light_dev)
return -ENODEV;
if (brightness < 0 || brightness > max_brightness)
return -EINVAL;
if (brightness == current_brightness)
return 0;
powerd_debug("light_dev: setting brightness to %i", brightness);
/* If current < min, it means we're comming from a resume */
if (current_brightness >= min_user_brightness) {
/* Set brightness in a progressive way, per step */
if (brightness > current_brightness) {
for (i = current_brightness; i < brightness;
i = i + set_brightness_step) {
set_brightness_hal(i);
usleep(set_brightness_usleep);
}
} else {
for (i = current_brightness; i > brightness;
i = i - set_brightness_step) {
set_brightness_hal(i);
usleep(set_brightness_usleep);
}
}
}
/* Now set the final value (only care about error here as if
* the last set works, we're good) */
ret = set_brightness_hal(brightness);
if (!ret) {
current_brightness = brightness;
powerd_update_bus_brightness();
}
else
powerd_error("light_dev: failed to set brightness to %i", brightness);
return ret;
}
int powerd_set_user_brightness(int brightness)
{
return powerd_set_brightness(brightness < min_user_brightness ?
min_user_brightness : brightness);
}
int powerd_backlight_init(void)
{
GValue v = G_VALUE_INIT;
const struct hw_module_t *hwmod = NULL;
if (!device_config_get("screenBrightnessDim", &v)) {
if (G_VALUE_HOLDS_UINT(&v))
dim_brightness = (int)g_value_get_uint(&v);
g_value_unset(&v);
}
if (!device_config_get("screenBrightnessSettingMinimum", &v)) {
if (G_VALUE_HOLDS_UINT(&v))
min_user_brightness = (int)g_value_get_uint(&v);
g_value_unset(&v);
}
if (!device_config_get("screenBrightnessSettingMaximum", &v)) {
if (G_VALUE_HOLDS_UINT(&v))
max_brightness = (int)g_value_get_uint(&v);
g_value_unset(&v);
}
if (!device_config_get("screenBrightnessSettingDefault", &v)) {
if (G_VALUE_HOLDS_UINT(&v))
default_brightness = (int)g_value_get_uint(&v);
g_value_unset(&v);
}
/* Sanity check default brightness value */
if (default_brightness < min_user_brightness)
default_brightness = min_user_brightness;
else if (default_brightness > max_brightness)
default_brightness = max_brightness;
if (hw_get_module(LIGHTS_HARDWARE_MODULE_ID, &hwmod)) {
powerd_warn("Could not get lights module");
return -ENODEV;
}
if (hwmod->methods->open(hwmod, LIGHT_ID_BACKLIGHT,
(hw_device_t **)&light_dev)) {
powerd_warn("Could not open backlight, brightness adjustment will be disabled");
light_dev = NULL;
return -ENODEV;
}
/* XXX: May want to eliminate this when integration with shell
* is complete and just wait for it to apply user settings.
* Unfortunately the HAL provides no way for us to read the
* current brightness when we start. */
powerd_set_brightness(default_brightness);
user_brightness = default_brightness;
return 0;
}
void powerd_backlight_deinit(void)
{
light_dev = NULL;
}
static void set_user_brightness(int brightness)
{
int max = powerd_get_max_brightness();
if (brightness > max)
brightness = max;
user_brightness = brightness;
}
static void set_backlight(enum bl_state state)
{
static enum bl_state current_state = BL_ON;
static int restore_brightness = 0;
if (state >= NUM_BL_STATES) {
powerd_warn("Unknown backlight state %d\n", state);
return;
}
if (state != BL_AUTO)
powerd_autobrightness_disable();
/*
* When autobrightness is enabled, it takes a second to
* settle on a brightness level after enabling. This delay
* becomes very noticeable when going from the dim to bright
* state. To avoid this lag, save off the current brightness
* and restore it when transitioning to AUTO mode again.
*/
if (current_state != BL_OFF && state == BL_OFF)
restore_brightness = powerd_get_brightness();
switch (state) {
case BL_OFF:
powerd_set_brightness(0);
break;
case BL_ON:
powerd_set_user_brightness(user_brightness);
break;
case BL_AUTO:
if (current_state == BL_OFF && restore_brightness > 0)
powerd_set_brightness(restore_brightness);
powerd_autobrightness_enable();
break;
default:
/* default case is to satisfy static analysis tools, should
* never actually get here */
powerd_error("Unknwown backlight state %d\n", state);
return;
}
current_state = state;
}
/** dbus interfaces **/
gboolean handle_get_brightness_params(PowerdSource *obj,
GDBusMethodInvocation *invocation)
{
gboolean ab_available = powerd_autobrightness_available();
g_dbus_method_invocation_return_value(invocation,
g_variant_new("((iiiib))",
dim_brightness,
min_user_brightness,
max_brightness,
default_brightness,
ab_available));
return TRUE;
}
gboolean handle_user_autobrightness_enable(PowerdSource *obj,
GDBusMethodInvocation *invocation,
gboolean enable)
{
if (powerd_autobrightness_available() && enable)
set_backlight(BL_AUTO);
else
set_backlight(BL_ON);
g_dbus_method_invocation_return_value(invocation, NULL);
return TRUE;
}
gboolean handle_set_user_brightness(PowerdSource *obj,
GDBusMethodInvocation *invocation,
gint brightness)
{
if (brightness == 0)
{
set_backlight(BL_OFF);
}
else
{
set_user_brightness(brightness);
set_backlight(BL_ON);
}
g_dbus_method_invocation_return_value(invocation, NULL);
return TRUE;
}
powerd-0.16+16.04.20160204.1/src/powerd.cpp 0000644 0000156 0000165 00000034603 12654662605 020253 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013 Canonical Ltd.
*
* Authors:
* Michael Frey: michael.frey@canonical.com
* Matthew Fischer: matthew.fischer@canonical.com
* Seth Forshee: seth.forshee@canonical.com
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "powerd-internal.h"
#include "powerd-object.h"
#include "powerd-dbus.h"
#include "powerd-sensors.h"
#include "device-config.h"
#include "log.h"
#include
#include "libsuspend.h"
#include
static GThread *powerd_mainloop_thread;
namespace
{
static GMainLoop *main_loop = NULL;
static guint name_id;
static int g_exit_code = 0;
static int call_added = 0;
static uuid_t screen_cookie;
/* Used to track modem interfaces */
struct modem_data {
char *obj_name;
GDBusProxy *message_proxy;
GDBusProxy *voice_proxy;
GDBusProxy *ussd_proxy;
GDBusProxy *radiosettings_proxy;
};
static void free_modem_data(void *data)
{
struct modem_data *modem = (struct modem_data *) data;
g_free(modem->obj_name);
if (modem->message_proxy)
g_object_unref(modem->message_proxy);
if (modem->voice_proxy)
g_object_unref(modem->voice_proxy);
if (modem->ussd_proxy)
g_object_unref(modem->ussd_proxy);
if (modem->radiosettings_proxy)
g_object_unref(modem->radiosettings_proxy);
}
static gint modem_data_cmp(gconstpointer a, gconstpointer b)
{
const struct modem_data *modem = (const struct modem_data *) a;
const char *modem_name = (const char *) b;
return strcmp(modem->obj_name, modem_name);
}
/* List with detected modems */
static GSList *g_modems = NULL;
static GDBusProxy *g_ofono_proxy;
static GDBusProxy *g_unity_proxy;
static struct power_module* _power_module;
static void
sigterm_quit(int signal)
{
powerd_warn("SIGTERM recieved, cleaning up");
powerd_exit(0);
}
} //namespace
void powerd_hal_signal_activity(void)
{
powerd_warn("signalling activity via HAL");
if (_power_module && _power_module->powerHint)
_power_module->powerHint(_power_module, POWER_HINT_INTERACTION, NULL);
}
/*
* Once dbus is ready, force the screen on to make the system state
* match the powerd iternal state
*/
void powerd_dbus_init_complete(void)
{
}
static void watch_modem(struct modem_data *modem)
{
/* for incoming SMS signals */
g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
NULL,
"org.ofono",
modem->obj_name,
"org.ofono.MessageManager",
NULL,
(GAsyncReadyCallback)ofono_proxy_connect_cb,
&modem->message_proxy);
/* for incoming calls Added/Removed signals */
g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
NULL,
"org.ofono",
modem->obj_name,
"org.ofono.VoiceCallManager",
NULL,
(GAsyncReadyCallback)ofono_proxy_connect_cb,
&modem->voice_proxy);
/* for USSD notifications/network requests */
g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
NULL,
"org.ofono",
modem->obj_name,
"org.ofono.SupplementaryServices",
NULL,
(GAsyncReadyCallback)ofono_proxy_connect_cb,
&modem->ussd_proxy);
/* To set/unset low power mode */
g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
NULL,
"org.ofono",
modem->obj_name,
"org.ofono.RadioSettings",
NULL,
(GAsyncReadyCallback)ofono_proxy_connect_cb,
&modem->radiosettings_proxy);
}
void
ofono_get_modems_cb(GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
GDBusProxy *client = G_DBUS_PROXY(source_object);
GVariant *result;
GVariant *item;
GError *error = NULL;
GVariantIter *iter;
result = g_dbus_proxy_call_finish(client, res, &error);
if (result == NULL) {
powerd_warn("%s: call error", __func__);
return;
}
g_variant_get(result, "(a(oa{sv}))", &iter);
while ((item = g_variant_iter_next_value(iter))) {
const char *obj_path;
g_variant_get_child(item, 0, "&o", &obj_path);
if (g_slist_find_custom(g_modems, obj_path, modem_data_cmp)
== NULL) {
struct modem_data *modem;
powerd_debug("active ofono modem %s", obj_path);
modem = (struct modem_data *) calloc(1, sizeof(*modem));
modem->obj_name = g_strdup(obj_path);
g_modems = g_slist_prepend(g_modems, modem);
watch_modem(modem);
}
g_variant_unref(item);
}
g_variant_unref(result);
}
void on_ofono_manager_signal(GDBusProxy *proxy, gchar *sender_name,
gchar *signal_name, GVariant *parameters, gpointer user_data)
{
const gchar *object_path;
GVariant *tmp;
GSList *node;
tmp = g_variant_get_child_value(parameters, 0);
object_path = g_variant_get_string(tmp, NULL);
node = g_slist_find_custom(g_modems, object_path, modem_data_cmp);
if (strcmp("ModemAdded", signal_name) == 0) {
/* Add if not already in list */
if (node == NULL) {
struct modem_data *modem;
powerd_debug("watching ofono modem %s", object_path);
modem = (struct modem_data *) calloc(1, sizeof(*modem));
modem->obj_name = g_strdup(object_path);
g_modems = g_slist_prepend(g_modems, modem);
watch_modem(modem);
}
} else if(strcmp("ModemRemoved", signal_name) == 0) {
/* Remove if found in list */
if (node != NULL) {
powerd_debug("stop watch on ofono modem %s", object_path);
free_modem_data(g_modems->data);
g_modems = g_slist_delete_link(g_modems, node);
}
}
g_variant_unref(tmp);
}
void
on_ofono_voicecallmanager_signal (GDBusProxy *proxy,
gchar *sender_name,
gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
if (!strcmp(signal_name, "CallAdded") && call_added++ <= 0) {
turn_display_on(TRUE, UNITY_SCREEN_REASON_SNAP_DECISION);
powerd_info("incoming call");
} else if (!strcmp(signal_name, "CallRemoved")) {
call_added--;
//Turn the display back on when all calls are over
if (call_added == 0)
turn_display_on(TRUE, UNITY_SCREEN_REASON_CALL_DONE);
powerd_info("call removed");
}
}
void
on_ofono_messagemanager_signal (GDBusProxy *proxy,
gchar *sender_name,
gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
powerd_debug("we get signal from %s: %s", sender_name, signal_name);
/* from org.ofono.MessageManager */
if (((!strcmp(signal_name, "IncomingMessage")) ||
(!strcmp(signal_name, "ImmediateMessage"))) &&
(call_added == 0)) {
powerd_debug("Waking up the device - Incoming SMS");
turn_display_on(TRUE, UNITY_SCREEN_REASON_NOTIFICATION);
}
}
void on_ofono_ussd_signal(GDBusProxy *proxy,
gchar *sender_name,
gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
powerd_debug("we get signal from %s: %s", sender_name, signal_name);
/* from org.ofono.MessageManager */
if (((!strcmp(signal_name, "NotificationReceived")) ||
(!strcmp(signal_name, "RequestReceived"))) &&
(call_added == 0)) {
powerd_debug("Waking up the device - Incoming USSD");
turn_display_on(TRUE, UNITY_SCREEN_REASON_NORMAL);
}
}
void on_unity_signal(GDBusProxy *proxy, gchar *sender_name,
gchar *signal_name, GVariant *parameters, gpointer user_data)
{
powerd_debug("we get signal from %s: %s", sender_name, signal_name);
/* From com.canonical.Unity.Screen */
if (strcmp(signal_name, "DisplayPowerStateChange") == 0) {
int state, reason;
GSList *it;
g_variant_get(parameters, "(ii)", &state, &reason);
powerd_debug("Received %s: state=%d flags=%d\n",
signal_name, state, reason);
/* Set modem low power mode when reason is not proximity sensor */
if (reason == UNITY_SCREEN_REASON_PROXIMITY)
return;
for (it = g_modems; it; it = it->next) {
struct modem_data *modem = (struct modem_data *) it->data;
GVariant *mode = g_variant_new_boolean(state ? FALSE : TRUE);
g_dbus_proxy_call(modem->radiosettings_proxy, "SetProperty",
g_variant_new("(sv)", "FastDormancy", mode),
G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
}
}
}
void
unity_screen_name_appeared_cb(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
if (!uuid_is_null(screen_cookie)) {
clear_sys_state_internal(screen_cookie);
uuid_clear(screen_cookie);
}
}
void
unity_screen_name_vanished_cb(GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
if (uuid_is_null(screen_cookie)) {
request_sys_state_internal("shutdown-request",
POWERD_SYS_STATE_ACTIVE,
&screen_cookie, NULL);
}
}
/* Must be first to run on main loop */
static gboolean main_init(gpointer unused)
{
powerd_mainloop_thread = g_thread_self();
return FALSE;
}
/* Returns TRUE if current thread of execution is the default main loop */
int powerd_is_mainloop(void)
{
return (g_thread_self() == powerd_mainloop_thread);
}
/* If running in emulator, make sure we keep the display on and
* block suspend (can easily corrupt the data, not properly supported) */
static gboolean check_emulator(gpointer unused)
{
char propbuf[PROP_VALUE_MAX];
static uuid_t emulator_request_cookie;
if (property_get("ro.kernel.qemu", propbuf, NULL) > 0) {
powerd_info("Running in emulator, forcing display, blocking suspend.");
keep_display_on();
/* Block suspend */
request_sys_state_internal("emulator-request",
POWERD_SYS_STATE_ACTIVE,
&emulator_request_cookie, NULL);
}
return FALSE;
}
void powerd_shutdown(void)
{
static char poweroff_cmd[] = "poweroff";
char *argv[] = {poweroff_cmd, NULL};
g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
}
void powerd_exit(int exit_code)
{
g_exit_code = exit_code;
g_main_loop_quit(main_loop);
}
int main(int argc, char** argv)
{
powerd_log_init();
powerd_info("Initializing powerd.");
name_id = g_bus_own_name(G_BUS_TYPE_SYSTEM, "com.canonical.powerd",
G_BUS_NAME_OWNER_FLAGS_REPLACE, powerd_bus_acquired_cb,
powerd_name_acquired_cb, powerd_name_lost_cb, NULL, NULL);
powerd_debug("owner id: %u", name_id);
/* Listen to modem creation */
g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
NULL,
"org.ofono",
"/",
"org.ofono.Manager",
NULL,
(GAsyncReadyCallback) ofono_manager_proxy_connect_cb,
&g_ofono_proxy);
/* Listen to unity signals */
g_dbus_proxy_new_for_bus(G_BUS_TYPE_SYSTEM,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
NULL,
"com.canonical.Unity.Screen",
"/com/canonical/Unity/Screen",
"com.canonical.Unity.Screen",
NULL,
(GAsyncReadyCallback) unity_proxy_connect_cb,
&g_unity_proxy);
/* Initialize screen cookie before setting up listener */
uuid_clear(screen_cookie);
/* Register for com.canonical.Unity.Screen name ownership changes */
g_bus_watch_name(G_BUS_TYPE_SYSTEM, "com.canonical.Unity.Screen",
G_BUS_NAME_WATCHER_FLAGS_NONE,
(GBusNameAppearedCallback)unity_screen_name_appeared_cb,
(GBusNameVanishedCallback)unity_screen_name_vanished_cb,
NULL, NULL);
main_loop = g_main_loop_new (NULL, FALSE);
signal(SIGTERM, sigterm_quit);
/* Init this first, data is used by other inits */
device_config_init();
libsuspend_init(0);
powerd_info("libsuspend: detect module: %s.", libsuspend_getname());
powerd_stats_init();
powerd_client_init();
power_request_init();
dbus_name_watch_init();
powerd_backlight_init();
powerd_autobrightness_init();
powerd_sensors_init(main_loop);
powerd_ps_init();
wakeup_init();
int err = hw_get_module(POWER_HARDWARE_MODULE_ID,
(hw_module_t const**)&_power_module);
if (!err)
_power_module->init(_power_module);
/* Config use should be done during init, okay to free now */
device_config_deinit();
/* This needs to be the first thing to run on the main loop */
g_idle_add_full(G_PRIORITY_HIGH, main_init, NULL, NULL);
g_idle_add(check_emulator, NULL);
g_main_loop_run(main_loop);
g_bus_unown_name(name_id);
g_main_loop_unref(main_loop);
wakeup_deinit();
powerd_ps_deinit();
dbus_name_watch_deinit();
powerd_autobrightness_deinit();
powerd_backlight_deinit();
power_request_deinit();
powerd_client_deinit();
powerd_stats_deinit();
g_slist_free_full(g_modems, free_modem_data);
if (g_ofono_proxy)
g_object_unref(g_ofono_proxy);
if (g_unity_proxy)
g_object_unref(g_unity_proxy);
return g_exit_code;
}
powerd-0.16+16.04.20160204.1/src/powerd-sensors.h 0000644 0000156 0000165 00000001754 12654662605 021413 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013 Canonical Ltd.
*
* Authors:
* Ricardo Mendoza: ricardo.mendoza@canonical.com
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef __POWERD_SENSORS_H__
#define __POWERD_SENSORS_H__
#include
void powerd_sensors_init(GMainLoop* main_loop);
void powerd_sensors_proximity_enable(void);
void powerd_sensors_proximity_disable(void);
void powerd_sensors_proximity_emit(void);
#endif /* __POWERD_SENSORS_H */
powerd-0.16+16.04.20160204.1/src/util.c 0000644 0000156 0000165 00000005672 12654662605 017374 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013 Canonical Ltd.
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include "powerd-internal.h"
struct sync_work_data {
GCond cond;
GMutex mutex;
gboolean done;
int (*func)(gpointer);
gpointer data;
int return_val;
};
static gboolean run_mainloop_sync_worker(gpointer data)
{
struct sync_work_data *work_data = data;
g_mutex_lock(&work_data->mutex);
work_data->return_val = work_data->func(work_data->data);
work_data->done = TRUE;
g_cond_signal(&work_data->cond);
g_mutex_unlock(&work_data->mutex);
return FALSE;
}
static int __powerd_run_mainloop_sync(int (*func)(gpointer), gpointer data)
{
struct sync_work_data work_data;
g_cond_init(&work_data.cond);
g_mutex_init(&work_data.mutex);
work_data.done = FALSE;
work_data.func = func;
work_data.data = data;
g_mutex_lock(&work_data.mutex);
g_timeout_add(0, run_mainloop_sync_worker, &work_data);
while (!work_data.done)
g_cond_wait(&work_data.cond, &work_data.mutex);
g_mutex_unlock(&work_data.mutex);
g_mutex_clear(&work_data.mutex);
g_cond_clear(&work_data.cond);
return work_data.return_val;
}
/*
* Run the supplied function on the main loop, passing it the
* supplied data, and wait for it to complete. If currently
* executing on the main loop then the function is simply called
* inline.
*/
int powerd_run_mainloop_sync(int (*func)(gpointer), gpointer data)
{
if (powerd_is_mainloop())
return func(data);
return __powerd_run_mainloop_sync(func, data);
}
/*
* Calculate a hash from a UUID. For use with GHashTables.
*/
guint powerd_uuid_hash(gconstpointer key)
{
guint *data = (guint *)key;
guint hash, i;
/*
* Treat the UUID as an array of guints and do a simple xor
* hash. uuid_t is an even multiple of the size of guint for
* 32- or 64-bit integers, but even if it weren't this
* implementation would just leave off the trailing bytes and
* still produce a reasonable hash.
*/
hash = data[0];
for (i = 1; i < sizeof(uuid_t) / sizeof(hash); i++)
hash ^= data[i];
return hash;
}
/*
* Compare two UUIDs and return TRUE if equal and FALSE
* otherwise. For use with GHashTables.
*/
gboolean powerd_uuid_equal(gconstpointer a, gconstpointer b)
{
return !memcmp(a, b, sizeof(uuid_t));
}
powerd-0.16+16.04.20160204.1/src/power-source.c 0000644 0000156 0000165 00000015035 12654662605 021043 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013 Canonical Ltd.
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include "powerd-internal.h"
#include "device-config.h"
#include "log.h"
static UpClient *up_client;
static double shutdown_temp = 68.0f;
static double battery_log_level = 4.0f;
static gboolean stats_logged;
static GHashTable *battery_state_hash;
static void up_device_changed_cb(UpClient *client,
#if UP_CHECK_VERSION(0,99,0)
GParamSpec *pspec,
#else
UpDevice *device,
#endif
gpointer unused)
{
gboolean on_battery;
GPtrArray *devices;
int i;
gboolean log_stats = TRUE;
gboolean low_batt_shutdown = TRUE;
gboolean thermal_shutdown = FALSE;
g_object_get(up_client,
"on_battery", &on_battery,
NULL);
if (!on_battery)
low_batt_shutdown = FALSE;
devices = up_client_get_devices(up_client);
for (i = 0; i < devices->len; i++) {
UpDevice *device = g_ptr_array_index(devices, i);
UpDeviceKind kind;
gboolean is_present;
g_object_get(device,
"kind", &kind,
"is-present", &is_present,
NULL);
if (kind == UP_DEVICE_KIND_BATTERY && is_present) {
double percentage, temp;
guint state;
guint prev_state;
gchar *native_path;
g_object_get(device,
"percentage", &percentage,
"temperature", &temp,
"state", &state,
"native-path", &native_path,
NULL);
gpointer hvalue = g_hash_table_lookup(battery_state_hash,
native_path);
if (hvalue == NULL)
g_hash_table_insert(battery_state_hash,
g_strdup(native_path), GUINT_TO_POINTER(state));
else {
prev_state = GPOINTER_TO_UINT(hvalue);
if (prev_state != state) {
if (state == UP_DEVICE_STATE_DISCHARGING ||
(prev_state == UP_DEVICE_STATE_DISCHARGING && (
state == UP_DEVICE_STATE_CHARGING ||
state == UP_DEVICE_STATE_FULLY_CHARGED))) {
powerd_debug("Turning screen on, battery state changes");
turn_display_on(TRUE, UNITY_SCREEN_REASON_NORMAL);
}
g_hash_table_replace(battery_state_hash,
g_strdup(native_path),
GUINT_TO_POINTER(state));
}
}
g_free(native_path);
/*
* Log battery stats a little before emergency
* shutdown is triggered to avoid any delays
* when we actually need to shut down.
*/
if (percentage > battery_log_level) {
log_stats = FALSE;
stats_logged = FALSE;
}
/*
* Android shuts down when percentage reaches 0. We
* use 1% to leave a little more headroom.
*/
if (percentage > 1.0)
low_batt_shutdown = FALSE;
if (temp >= shutdown_temp) {
powerd_warn("Critical battery temperature %f\n", temp);
thermal_shutdown = TRUE;
break;
}
}
}
if (log_stats && !stats_logged) {
powerd_log_stats();
stats_logged = TRUE;
}
if (low_batt_shutdown || thermal_shutdown) {
powerd_warn("Initiating emergency shutdown");
powerd_shutdown();
}
}
int powerd_ps_init(void)
{
GValue v = G_VALUE_INIT;
/*
* Override default shutdown temperature with device-specific
* value, if avaialable
*/
if (!device_config_get("shutdownBatteryTemperature", &v) &&
G_VALUE_HOLDS_UINT(&v)) {
shutdown_temp = (double)g_value_get_uint(&v) / 10.0f;
g_value_unset(&v);
}
if (!device_config_get("criticalBatteryWarningLevel", &v) &&
G_VALUE_HOLDS_UINT(&v)) {
battery_log_level = (double)g_value_get_uint(&v);
g_value_unset(&v);
}
up_client = up_client_new();
if (!up_client) {
powerd_warn("Could not allocate upower client");
return -ENODEV;
}
/* Hash to track the state for the battery devices */
battery_state_hash = g_hash_table_new(g_str_hash, g_str_equal);
GPtrArray *devices;
int i;
devices = up_client_get_devices(up_client);
for (i = 0; i < devices->len; i++) {
UpDevice *device = g_ptr_array_index(devices, i);
UpDeviceKind kind;
gboolean is_present;
guint state;
gchar *native_path;
g_object_get(device,
"kind", &kind,
"is-present", &is_present,
"state", &state,
"native-path", &native_path,
NULL);
if (kind == UP_DEVICE_KIND_BATTERY && is_present) {
g_hash_table_insert(battery_state_hash,
g_strdup(native_path), GUINT_TO_POINTER(state));
}
g_free(native_path);
}
#if UP_CHECK_VERSION(0,99,0)
g_signal_connect(up_client, "notify",
G_CALLBACK(up_device_changed_cb), NULL);
#else
gboolean ret;
ret = up_client_enumerate_devices_sync(up_client, NULL, NULL);
if (!ret)
powerd_warn("Could not enumerate upower devices");
g_signal_connect(up_client, "device-changed",
G_CALLBACK(up_device_changed_cb), NULL);
#endif
return 0;
}
void powerd_ps_deinit(void)
{
if (up_client)
g_object_unref(up_client);
if (battery_state_hash) {
g_hash_table_destroy(battery_state_hash);
battery_state_hash = NULL;
}
}
powerd-0.16+16.04.20160204.1/src/power-request.c 0000644 0000156 0000165 00000051355 12654662605 021240 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2013 Canonical Ltd.
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "powerd-internal.h"
#include "powerd-dbus.h"
#include "log.h"
#include "libsuspend.h"
#define NOTIFICATION_TIMEOUT_MSECS 2000
struct SysStateRequest {
const char *name;
const char *owner;
uuid_t cookie;
enum SysPowerStates state;
int power_state_flags;
};
/*
* Current system power state. Assume we are suspended to start. We will
* be forced into the active state during initialization, and this
* ensures that the system state is consistent with our internal state.
*/
static enum SysPowerStates current_system_state = POWERD_SYS_STATE_SUSPEND;
/*
* System state requests are stored in a hash. This doesn't allow for
* quickly finding requests corresponding to a given state, so a
* per-state count of active requests is also maintained.
*/
static GHashTable *state_request_hash;
static gint state_request_count[POWERD_NUM_POWER_STATES - 1];
/*
* States for "state transition" state machine.
*
* When no state transition is in progress we're in the idle state. When
* a state transition is initiated we proceed through the other states
* in order. When finished, we start at the begining if another state
* transition is pending or return to idle otherwise.
*/
enum state_transition_state {
STATE_TRANSITION_IDLE, /* No transition in progress */
STATE_TRANSITION_DEQUEUE, /* Dequeueing next state transition */
STATE_TRANSITION_NOTIFY, /* Notifying clients / waiting for acks */
STATE_TRANSITION_COMPLETE, /* Completing the state transition */
NUM_STATE_TRANSITIONS
};
static enum state_transition_state cur_state_transition_state = STATE_TRANSITION_IDLE;
#define INVALID_STATE -1
static GQueue queued_state_changes;
static enum SysPowerStates pending_system_state = INVALID_STATE;
static guint state_machine_source_id;
static gint update_pending;
static gboolean ack_pending;
static gboolean suspend_active = FALSE;
static const char power_request_wakelock_name[] = "powerd_power_request";
gint suspend_block_count;
static void state_transition_process(void);
enum SysPowerStates current_system_power_state(void)
{
return current_system_state;
}
/* simple helper for debugging */
const gchar *
state_to_string(int state)
{
switch (state) {
case POWERD_SYS_STATE_ACTIVE: return "ACTIVE";
case POWERD_SYS_STATE_SUSPEND: return "SUSPEND";
default: return "UNKNOWN";
}
}
/*
* block_suspend and unblock_suspend are only for use when new requests
* are received, to ensure that autosuspend doesn't suspend the system
* before the main loop can disable it after an active state request
* is received.
*/
static void block_suspend(void)
{
int ret;
if (g_atomic_int_add(&suspend_block_count, 1) == 0) {
powerd_debug("libsuspend: acquire_wake_lock: %s", power_request_wakelock_name);
ret = libsuspend_acquire_wake_lock(power_request_wakelock_name);
if (ret)
powerd_warn("Could not acquire wake lock");
}
}
static void unblock_suspend(void)
{
gint old_count;
int ret;
gboolean retry;
do {
old_count = g_atomic_int_get(&suspend_block_count);
if (old_count == 0) {
powerd_warn("Suspend not blocked, refusing to unblock");
return;
}
retry = !g_atomic_int_compare_and_exchange(&suspend_block_count,
old_count, old_count - 1);
if (!retry && old_count == 1) {
powerd_debug("libsuspend: release_wake_lock: %s", power_request_wakelock_name);
ret = libsuspend_release_wake_lock(power_request_wakelock_name);
if (ret)
powerd_warn("Could not release wake lock");
}
} while (retry);
}
/* Returns TRUE if the state request is valid, FALSE otherwise.
* Nobody can request the SUSPEND state. */
static gboolean
is_valid_state_request(int state)
{
return state > POWERD_SYS_STATE_SUSPEND && state < POWERD_NUM_POWER_STATES;
}
/*
* Must only be called from main loop.
*
* internal callers should pass in NULL for the builder, its only used
* by dbus callers. Returns a count of the total number of active
* requests.
*/
static guint
list_sys_requests_internal(GVariantBuilder *builder)
{
GHashTableIter iter;
gpointer key, value;
guint count = 0;
g_hash_table_iter_init(&iter, state_request_hash);
while (g_hash_table_iter_next(&iter, &key, &value)) {
struct SysStateRequest *req = value;
g_variant_builder_add(builder, "(ssi)", req->name, req->owner,
req->state);
count++;
}
return count;
}
/* Dump the requests to stdout and return the list via dbus */
gboolean
handle_list_sys_requests(PowerdSource *obj, GDBusMethodInvocation *invocation)
{
GVariant *list, *tuple = NULL;
GVariantBuilder *builder;
guint count = 0;
builder = g_variant_builder_new(G_VARIANT_TYPE("a(ssi)"));
count = list_sys_requests_internal(builder);
if (count>0) {
list = g_variant_builder_end(builder);
}
else {
g_variant_builder_clear(builder);
list = g_variant_new_array(G_VARIANT_TYPE("(ssi)"), NULL, 0);
}
tuple = g_variant_new_tuple(&list,1);
g_dbus_method_invocation_return_value(invocation, tuple);
g_variant_builder_unref(builder);
return TRUE;
}
/* Should be called only on main loop */
static int request_sys_state_worker(gpointer data)
{
struct SysStateRequest *sr = data;
g_hash_table_insert(state_request_hash, sr->cookie, sr);
state_request_count[sr->state - 1] += 1;
powerd_dbus_name_watch_add(sr->owner);
powerd_account_request_sys_state(sr->owner, sr->name);
return 0;
}
/* Note: owner is NULL for internal usage */
gboolean
request_sys_state_internal(const char *name, int state, uuid_t *cookie,
const gchar *owner)
{
struct SysStateRequest *sr = NULL;
if (cookie == NULL) {
powerd_error("you need to pass in memory for a cookie");
return FALSE;
}
if (!is_valid_state_request(state))
{
powerd_warn("invalid state requested: %d",state);
return FALSE;
}
sr = g_new(struct SysStateRequest, 1);
sr->name = g_strdup(name);
sr->state = (enum SysPowerStates)state;
if (owner) {
sr->owner = g_strdup(owner);
}
else {
sr->owner = g_strdup("internal");
}
uuid_generate(sr->cookie);
memcpy(*cookie, sr->cookie, sizeof(sr->cookie));
powerd_run_mainloop_sync(request_sys_state_worker, sr);
update_system_state();
return TRUE;
}
gboolean
handle_request_sys_state(PowerdSource *obj, GDBusMethodInvocation *invocation,
const char *name, int state)
{
gboolean retval;
const char *owner;
uuid_t cookie;
char ext_cookie[UUID_STR_LEN];
owner = g_dbus_method_invocation_get_sender(invocation);
powerd_debug("handle_requestSysState from %s (%s) - %s (%d)", owner,
name, state_to_string(state), state);
retval = request_sys_state_internal(name, state, &cookie, owner);
if (retval == TRUE) {
powerd_debug("handle_requestSysState - SUCCESS");
uuid_unparse(cookie, ext_cookie);
g_dbus_method_invocation_return_value(invocation,
g_variant_new("(s)", ext_cookie));
} else {
g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS, "Invalid state request");
}
return TRUE;
}
static void __clear_sys_request(struct SysStateRequest *req)
{
powerd_debug("clear_sys_request: %s - %s", req->name, req->owner);
powerd_dbus_name_watch_remove(req->owner);
state_request_count[req->state - 1] -= 1;
powerd_account_clear_sys_state(req->owner, req->name);
}
/* Must only be called from main loop */
static int
clear_sys_state_worker(gpointer data)
{
unsigned char *uuid = data;
struct SysStateRequest *sr;
int found = FALSE;
/*
* This involves two lookups into the hash, one to find the
* request so we can retrieve the state and another to remove
* it. GHashTable doesn't seem to provide any more efficient
* way to do this; too bad g_hash_table_steal() doesn't return
* a pointer to the data.
*/
sr = g_hash_table_lookup(state_request_hash, uuid);
if (sr) {
/* We need to remove it from our watch hash before we remove it
* from the state hash or the sr->owner memory will be freed
* before we try to use it.
*/
__clear_sys_request(sr);
found = g_hash_table_remove(state_request_hash, uuid);
if (!found)
powerd_warn("State request found on lookup but not on remove");
}
return found;
}
gboolean
clear_sys_state_internal(uuid_t cookie)
{
char cookie_str[UUID_STR_LEN];
int found;
found = powerd_run_mainloop_sync(clear_sys_state_worker, cookie);
if (!found) {
uuid_unparse(cookie, cookie_str);
powerd_warn("request (%s) not found", cookie_str);
return FALSE;
}
update_system_state();
return TRUE;
}
gboolean
handle_clear_sys_state(PowerdSource *obj, GDBusMethodInvocation *invocation,
gchar *ext_cookie)
{
gboolean retval;
uuid_t cookie;
powerd_debug("handle_clearSysState from %s, cookie: %s",
g_dbus_method_invocation_get_sender(invocation), ext_cookie);
if (uuid_parse(ext_cookie, cookie)) {
g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"Invalid cookie: %s", ext_cookie);
return TRUE;
}
retval = clear_sys_state_internal(cookie);
if (retval == TRUE) {
g_dbus_method_invocation_return_value(invocation, NULL);
} else {
g_dbus_method_invocation_return_error(invocation, G_DBUS_ERROR,
G_DBUS_ERROR_INVALID_ARGS,
"Invalid cookie: %s", ext_cookie);
}
return TRUE;
}
static int enter_suspend(void)
{
powerd_debug("libsuspend: calling enter_suspend");
int ret = libsuspend_enter_suspend();
if (!ret) {
powerd_debug("libsuspend: enter_suspend succeeded");
suspend_active = TRUE;
}
return ret;
}
static int exit_suspend(void)
{
powerd_debug("libsuspend: calling exit_suspend");
int ret = libsuspend_exit_suspend();
if (!ret) {
powerd_debug("libsuspend: exit_suspend succeeded");
suspend_active = FALSE;
}
return ret;
}
gboolean powerd_suspend_active(void)
{
return suspend_active;
}
/* Must only be called from main loop */
static enum SysPowerStates max_requested_state(void)
{
enum SysPowerStates ret = POWERD_SYS_STATE_SUSPEND;
int i;
for (i = POWERD_NUM_POWER_STATES - 1; i > POWERD_SYS_STATE_SUSPEND; i--) {
if (state_request_count[i - 1] != 0) {
ret = i;
break;
}
};
return ret;
}
static gboolean delayed_state_transition_process(gpointer unused)
{
state_transition_process();
return FALSE;
}
static enum state_transition_state process_state_dequeue(guint *delay)
{
int ret;
*delay = 0;
pending_system_state = (enum SysPowerStates)g_queue_pop_head(&queued_state_changes);
if (pending_system_state == current_system_state) {
powerd_debug("pending state == current state, discarding");
pending_system_state = INVALID_STATE;
return STATE_TRANSITION_IDLE;
}
if (current_system_state == POWERD_SYS_STATE_SUSPEND) {
powerd_debug("exiting suspend");
ret = exit_suspend();
if (ret)
powerd_warn("Failed to exit suspend: %d", ret);
}
/*
* XXX: If going to suspend we may want to delay a few seconds here
* in case the user immediately tries to wake the device again after
* an activity timeout.
*/
/* Must call libsuspend_prepare_suspend() _before_ emitting signal */
if (pending_system_state == POWERD_SYS_STATE_SUSPEND) {
powerd_debug("libsuspend: prepare_suspend.");
ret = libsuspend_prepare_suspend();
if (ret)
powerd_warn("Failed to prepare for suspend: %d", ret);
}
ack_pending = powerd_client_transition_start(pending_system_state);
powerd_sys_state_signal_emit(pending_system_state);
*delay = ack_pending ? NOTIFICATION_TIMEOUT_MSECS : 0;
return STATE_TRANSITION_NOTIFY;
}
static enum state_transition_state process_state_notify(guint *delay)
{
if (ack_pending) {
powerd_warn("Timeout waiting for acks on state transition");
ack_pending = FALSE;
}
powerd_client_transition_finish(pending_system_state);
*delay = 0;
return STATE_TRANSITION_COMPLETE;
}
static enum state_transition_state process_state_complete(guint *delay)
{
int ret;
gboolean allow_suspend;
*delay = 0;
/*
* If more requests are in the queue or new requests are present
* above our current target state, do not suspend so that those
* requests can be processed.
*/
allow_suspend = !(!g_queue_is_empty(&queued_state_changes) ||
max_requested_state() > pending_system_state);
if (pending_system_state == POWERD_SYS_STATE_SUSPEND && allow_suspend) {
powerd_debug("entering suspend");
ret = enter_suspend();
if (ret)
powerd_warn("Failed to enter suspend: %d", ret);
}
powerd_debug("Transition to %s complete",
state_to_string(pending_system_state));
current_system_state = pending_system_state;
pending_system_state = INVALID_STATE;
/*
* If state changes are queued, go back to the dequeue state. If
* nothing is queued and max_requested_state() > pending_system_state
* there should be another run of update_system_state() scheduled to
* take care of this.
*/
return g_queue_is_empty(&queued_state_changes) ?
STATE_TRANSITION_IDLE : STATE_TRANSITION_DEQUEUE;
}
static void state_transition_process(void)
{
guint delay = 0;
state_machine_source_id = 0;
do {
switch (cur_state_transition_state) {
case STATE_TRANSITION_DEQUEUE:
cur_state_transition_state = process_state_dequeue(&delay);
break;
case STATE_TRANSITION_NOTIFY:
cur_state_transition_state = process_state_notify(&delay);
break;
case STATE_TRANSITION_COMPLETE:
cur_state_transition_state = process_state_complete(&delay);
break;
default:
powerd_error("Invalid transition state, setting to idle");
cur_state_transition_state = STATE_TRANSITION_IDLE;
break;
}
} while (delay == 0 &&
cur_state_transition_state != STATE_TRANSITION_IDLE);
if (delay != 0 && cur_state_transition_state != STATE_TRANSITION_IDLE) {
state_machine_source_id = g_timeout_add(delay,
delayed_state_transition_process,
NULL);
}
}
/* Must be called from main loop */
void power_request_transition_acked(void)
{
if (cur_state_transition_state != STATE_TRANSITION_NOTIFY) {
powerd_error("%s called while not waiting for acks", __func__);
return;
}
powerd_debug("All acks received for state transition");
ack_pending = FALSE;
if (state_machine_source_id) {
g_source_remove(state_machine_source_id);
state_machine_source_id = g_timeout_add(0,
delayed_state_transition_process,
NULL);
}
}
static void check_queued_state_changes(void)
{
/*
* If state change in progress we'll process the next queued state
* change when finished.
*/
if (cur_state_transition_state != STATE_TRANSITION_IDLE) {
powerd_debug("state change in progress, delaying");
return;
}
if (g_queue_is_empty(&queued_state_changes)) {
powerd_debug("no pending state changes");
return;
}
/* Start processing state transition */
cur_state_transition_state = STATE_TRANSITION_DEQUEUE;
state_transition_process();
}
/*
* XXX: Currently the coalescing is very simplistic and may need
* improvement.
*/
static void enqueue_state_change(enum SysPowerStates state)
{
gboolean queue_empty;
powerd_debug("Enqueue state change to %s", state_to_string(state));
/*
* If the new request is the same as the one we're currently
* processing we can drop all pending requests (including the
* new one).
*/
if (pending_system_state == state) {
powerd_debug("State == pending state, discarding");
g_queue_clear(&queued_state_changes);
return;
} else if (pending_system_state == INVALID_STATE) {
queue_empty = g_queue_is_empty(&queued_state_changes);
/*
* If no state changes are pending or in progress and the
* requested state is the same as the current state, do nothing.
*/
if (queue_empty && state == current_system_state) {
powerd_debug("queue empty && state == current, discarding");
return;
}
/*
* If we aren't processing a request and the current request is
* the same as the next reques in the queue, all intermediate
* requests can be dropped. We do this by clearing the queue and
* then adding in the new request.
*
* XXX: This situation is probably impossible currently.
*/
if (!queue_empty &&
(enum SysPowerStates)g_queue_peek_tail(&queued_state_changes) == state) {
powerd_debug("new state == next queued state, discarding all intermediate requests");
g_queue_clear(&queued_state_changes);
}
}
g_queue_push_tail(&queued_state_changes, (gpointer)state);
check_queued_state_changes();
}
static gboolean update_system_state_handler(gpointer unused)
{
/*
* We must clear update_pending just before calling max_requested_state()
* to avoid races with adding new requests. We may end up with an
* occassional redundant system state update, but that is harmless.
*/
g_atomic_int_set(&update_pending, 0);
enqueue_state_change(max_requested_state());
/*
* We assume that after enqueue_state_change() completes autosuspend
* will have been disabled if any active requests are present. This
* is currently true, but if the assumption is ever broken it will
* expose us to potential races.
*/
unblock_suspend();
return FALSE;
}
void update_system_state(void)
{
if (g_atomic_int_compare_and_exchange(&update_pending, 0, 1)) {
block_suspend();
g_timeout_add(0, update_system_state_handler, NULL);
}
}
/* Destructor for the state request object */
static void sys_state_request_destroy(gpointer data)
{
struct SysStateRequest *sr = (struct SysStateRequest *)data;
g_free((gpointer)sr->name);
g_free((gpointer)sr->owner);
g_free(sr);
}
void power_request_init(void)
{
state_request_hash = g_hash_table_new_full(powerd_uuid_hash,
powerd_uuid_equal,
NULL, sys_state_request_destroy);
g_queue_init(&queued_state_changes);
}
void power_request_deinit(void)
{
g_hash_table_destroy(state_request_hash);
g_queue_clear(&queued_state_changes);
}
/*
* Callers may use this to clear all requests that the specified owner
* is holding. This is used when the owner drops off of dbus
*/
void
clear_sys_state_by_owner(const char *owner)
{
GHashTableIter iter;
gpointer key, value;
g_hash_table_iter_init(&iter, state_request_hash);
while (g_hash_table_iter_next(&iter, &key, &value)) {
struct SysStateRequest *req = value;
if (!strcmp(owner, req->owner)) {
__clear_sys_request(req);
g_hash_table_iter_remove(&iter);
}
}
update_system_state();
}
powerd-0.16+16.04.20160204.1/src/wakeup.cpp 0000644 0000156 0000165 00000011412 12654662611 020235 0 ustar pbuser pbgroup 0000000 0000000 /*
* Copyright 2014 Canonical Ltd.
*
* Authors:
* Charles Kerr: charles.kerr@canonical.com
*
* This file is part of powerd.
*
* powerd is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 3.
*
* powerd 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include