Merge lp:~charlesk/powerd/add-hardware-alarms into lp:powerd

Proposed by Charles Kerr
Status: Merged
Approved by: Ricardo Salveti
Approved revision: 143
Merged at revision: 139
Proposed branch: lp:~charlesk/powerd/add-hardware-alarms
Merge into: lp:powerd
Diff against target: 658 lines (+439/-12)
9 files modified
CMakeLists.txt (+3/-1)
cli/powerd-cli.c (+99/-0)
data/com.canonical.powerd.xml (+13/-0)
debian/control (+2/-0)
src/log.h (+23/-0)
src/powerd-internal.h (+8/-1)
src/powerd-object.c (+101/-10)
src/powerd.cpp (+2/-0)
src/wakeup.cpp (+188/-0)
To merge this branch: bzr merge lp:~charlesk/powerd/add-hardware-alarms
Reviewer Review Type Date Requested Status
Ricardo Salveti (community) Approve
PS Jenkins bot continuous-integration Approve
Review via email: mp+231302@code.launchpad.net

Commit message

Add hardware wakeup support.

Description of the change

Add hardware wakeup support. As discussed in #phablet, the u_hardware_alarm_*() API basically only allows one client, so powerd will be that one client and will act as a gatekeeper for hardware wakeups.

This adds a little new public API: two methods (requestWakeup() and clearWakeup()) and a broadcast signal (Wakeup).

Also added are wakeup and clear-wakeup commands in powerd-cli.

The wakeup.cpp implementation is fairly straightforward. One point that might be contentious is the new logging functions in log.h. I think they're pretty useful but don't mind removing them if they're too un-powerd-like.

See related MP for indicator-datetime to make use of this: <https://code.launchpad.net/~charlesk/indicator-datetime/add-powerd-alarm-support/+merge/231303>

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ricardo Salveti (rsalveti) wrote :

Code looks fine, just one inline comment for the packaging. Will do some further tests and then approve with the proper feedback.

Revision history for this message
Charles Kerr (charlesk) :
143. By Charles Kerr

in debian/control, remove [armhf i386 amd64] arch restrictions for libubuntu-platform-hardware-api-*

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Ricardo Salveti (rsalveti) wrote :

Looks good, works as expected.

Would be nice for powerd-cli list to also show the current list of the pending wakeup events, but that can be done later on.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2014-05-21 03:17:03 +0000
+++ CMakeLists.txt 2014-08-19 19:23:22 +0000
@@ -9,7 +9,7 @@
9endif()9endif()
1010
11set(CMAKE_C_FLAGS "-Wall")11set(CMAKE_C_FLAGS "-Wall")
12set(CMAKE_CXX_FLAGS "-Wall")12set(CMAKE_CXX_FLAGS "-Wall -std=c++11")
13set(CMAKE_C_FLAGS_DEBUG "-O0 -ggdb -g")13set(CMAKE_C_FLAGS_DEBUG "-O0 -ggdb -g")
14set(CMAKE_CXX_FLAGS_DEBUG "-O0 -ggdb -g")14set(CMAKE_CXX_FLAGS_DEBUG "-O0 -ggdb -g")
15set(CMAKE_C_FLAGS_RELEASE "-O3")15set(CMAKE_C_FLAGS_RELEASE "-O3")
@@ -56,6 +56,7 @@
56 src/spline.c56 src/spline.c
57 src/stats.c57 src/stats.c
58 src/util.c58 src/util.c
59 src/wakeup.cpp
59 src/${GDBUS_NAME}.c60 src/${GDBUS_NAME}.c
60)61)
6162
@@ -103,6 +104,7 @@
103 ${GIO_LIBRARIES}104 ${GIO_LIBRARIES}
104 ${GIO-UNIX_LIBRARIES}105 ${GIO-UNIX_LIBRARIES}
105 ${PHABLET_LIBRARIES}106 ${PHABLET_LIBRARIES}
107 "-lubuntu_platform_hardware_api"
106 "-lubuntu_application_api"108 "-lubuntu_application_api"
107 "-landroid-properties"109 "-landroid-properties"
108 "-lhardware"110 "-lhardware"
109111
=== modified file 'cli/powerd-cli.c'
--- cli/powerd-cli.c 2014-06-18 18:06:10 +0000
+++ cli/powerd-cli.c 2014-08-19 19:23:22 +0000
@@ -141,6 +141,55 @@
141}141}
142142
143static gboolean143static gboolean
144requestWakeup(const char *name,
145 uint64_t wakeup_time,
146 powerd_cookie_t setme_cookie)
147{
148 GVariant *ret;
149 GError *error;
150 gboolean success;
151
152 g_return_val_if_fail (setme_cookie != NULL, FALSE);
153
154 error = NULL;
155 ret = g_dbus_proxy_call_sync(powerd_proxy,
156 "requestWakeup",
157 g_variant_new("(st)", name, wakeup_time),
158 G_DBUS_CALL_FLAGS_NONE,
159 -1, /* use default timeout */
160 NULL, /* cancellable */
161 &error);
162
163 if (ret == NULL) {
164 cli_warn("%s failed: %s", G_STRFUNC, error->message);
165 success = FALSE;
166 }
167 else if (!g_variant_is_of_type(ret, G_VARIANT_TYPE("(s)"))) {
168 char * s = g_variant_print(ret, TRUE);
169 cli_warn("Unexpected response: %s", s);
170 g_free(s);
171 success = FALSE;
172 }
173 else {
174 const char* s = NULL;
175 g_variant_get(ret, "(&s)", &s);
176 if (strlen(s) != sizeof(powerd_cookie_t) - 1) {
177 cli_warn("Returned cookie has incorrect size");
178 success = FALSE;
179 }
180 else {
181 memcpy(setme_cookie, s, sizeof(powerd_cookie_t));
182 cli_debug("Got cookie: %s", setme_cookie);
183 success = TRUE;
184 }
185 }
186
187 g_clear_error(&error);
188 g_clear_pointer(&ret, g_variant_unref);
189 return success;
190}
191
192static gboolean
144requestDisplayState(int *cookie)193requestDisplayState(int *cookie)
145{194{
146 GVariant *ret = NULL;195 GVariant *ret = NULL;
@@ -286,6 +335,35 @@
286 }335 }
287}336}
288337
338static gboolean
339clearWakeup(const char* cookie_str)
340{
341 GVariant *ret;
342 GError *error;
343 gboolean success;
344
345 error = NULL;
346 ret = g_dbus_proxy_call_sync(powerd_proxy,
347 "clearWakeup",
348 g_variant_new("(s)", cookie_str),
349 G_DBUS_CALL_FLAGS_NONE,
350 -1, /* default timeout */
351 NULL, /* cancellable */
352 &error);
353
354 if (ret == NULL) {
355 cli_warn("%s failed: %s", G_STRFUNC, error->message);
356 success = FALSE;
357 } else {
358 cli_debug("%s success", G_STRFUNC);
359 success = TRUE;
360 }
361
362 g_clear_error(&error);
363 g_clear_pointer(&ret, g_variant_unref);
364 return success;
365}
366
289gboolean367gboolean
290clearSysState(powerd_cookie_t cookie)368clearSysState(powerd_cookie_t cookie)
291{369{
@@ -618,6 +696,8 @@
618 printf("listen\t- listen for signals from powerd. This runs a\n"\696 printf("listen\t- listen for signals from powerd. This runs a\n"\
619 "\t gmainloop and must be manually killed.\n");697 "\t gmainloop and must be manually killed.\n");
620 printf("stats\t- print request statistics.\n");698 printf("stats\t- print request statistics.\n");
699 printf("wakeup <n>\t- request a hardware wakeup n seconds from now.\n");
700 printf("clear-wakeup <cookie>\t- clear a hardware wakeup request given a cookie.\n");
621 printf("test\t- runs tests.\n");701 printf("test\t- runs tests.\n");
622}702}
623703
@@ -646,6 +726,8 @@
646 (strcmp(argv[1],"stats")) &&726 (strcmp(argv[1],"stats")) &&
647 (strcmp(argv[1],"active")) &&727 (strcmp(argv[1],"active")) &&
648 (strcmp(argv[1],"active-nc")) &&728 (strcmp(argv[1],"active-nc")) &&
729 (strcmp(argv[1],"wakeup")) &&
730 (strcmp(argv[1],"clear-wakeup")) &&
649 (strcmp(argv[1],"test")) &&731 (strcmp(argv[1],"test")) &&
650 (strcmp(argv[1],"dbusnametest")) &&732 (strcmp(argv[1],"dbusnametest")) &&
651 (strcmp(argv[1],"listen")) &&733 (strcmp(argv[1],"listen")) &&
@@ -751,6 +833,9 @@
751 if (!strcmp(argv[1],"clear-sys")) {833 if (!strcmp(argv[1],"clear-sys")) {
752 clearSysState(argv[2]);834 clearSysState(argv[2]);
753 }835 }
836 else if (!g_strcmp0(argv[1], "clear-wakeup")) {
837 clearWakeup(argv[2]);
838 }
754 else {839 else {
755 // Note: We should never get here due to earlier checks840 // Note: We should never get here due to earlier checks
756 fprintf(stderr,"Invalid option %s\n",argv[1]);841 fprintf(stderr,"Invalid option %s\n",argv[1]);
@@ -844,6 +929,20 @@
844 exit(-1);929 exit(-1);
845 }930 }
846 setUserBrightness((int)brightness);931 setUserBrightness((int)brightness);
932 } else if (!g_strcmp0(argv[1], "wakeup")) {
933 int n_seconds;
934 powerd_cookie_t cookie;
935 if (argc != 3) {
936 usage(argv[0]);
937 exit(-1);
938 }
939 n_seconds = atoi(argv[2]);
940 if (n_seconds < 1) {
941 fprintf(stderr, "Invalid number of seconds %s\n", argv[2]);
942 usage(argv[0]);
943 exit(-1);
944 }
945 requestWakeup(argv[0], time(NULL)+n_seconds, cookie);
847 }946 }
848 return 0;947 return 0;
849}948}
850949
=== modified file 'data/com.canonical.powerd.xml'
--- data/com.canonical.powerd.xml 2014-05-13 22:05:20 +0000
+++ data/com.canonical.powerd.xml 2014-08-19 19:23:22 +0000
@@ -14,6 +14,16 @@
14 <arg type="s" name="cookie" direction="in" />14 <arg type="s" name="cookie" direction="in" />
15 </method>15 </method>
1616
17 <method name="requestWakeup">
18 <arg type="s" name="name" direction="in" />
19 <arg type="t" name="time" direction="in" />
20 <arg type="s" name="cookie" direction="out" />
21 </method>
22
23 <method name="clearWakeup">
24 <arg type="s" name="cookie" direction="in" />
25 </method>
26
17 <method name="registerClient">27 <method name="registerClient">
18 <arg type="s" name="name" direction="in" />28 <arg type="s" name="name" direction="in" />
19 </method>29 </method>
@@ -55,5 +65,8 @@
55 <arg type="i" name="sysState" direction="out" />65 <arg type="i" name="sysState" direction="out" />
56 </signal>66 </signal>
5767
68 <signal name="Wakeup">
69 </signal>
70
58 </interface>71 </interface>
59</node>72</node>
6073
=== modified file 'debian/control'
--- debian/control 2014-06-13 15:56:20 +0000
+++ debian/control 2014-08-19 19:23:22 +0000
@@ -10,6 +10,8 @@
10 python:any,10 python:any,
11 libandroid-properties-dev,11 libandroid-properties-dev,
12 libubuntu-application-api-dev,12 libubuntu-application-api-dev,
13 libubuntu-platform-hardware-api-headers,
14 libubuntu-platform-hardware-api-dev,
13 libupower-glib-dev,15 libupower-glib-dev,
14 uuid-dev,16 uuid-dev,
15 libhardware-dev (>= 0.1.0+git20131207+e452e83-0ubuntu12),17 libhardware-dev (>= 0.1.0+git20131207+e452e83-0ubuntu12),
1618
=== modified file 'src/log.h'
--- src/log.h 2013-07-10 21:28:20 +0000
+++ src/log.h 2014-08-19 19:23:22 +0000
@@ -39,6 +39,29 @@
39 __attribute__ ((format (printf, 2, 3)));39 __attribute__ ((format (printf, 2, 3)));
40void powerd_log_init(void);40void powerd_log_init(void);
4141
42#define powerd_warn_if_fail(expr) \
43 do { \
44 if (!(expr)) \
45 powerd_warn("%s", #expr); \
46 } while (0)
47
48#define powerd_return_if_fail(expr) \
49 do { \
50 if (!(expr)) { \
51 powerd_warn("%s: assertion '%s' failed", __func__, #expr); \
52 return; \
53 } \
54 } while (0)
55
56#define powerd_return_val_if_fail(expr,val) \
57 do { \
58 if (!(expr)) { \
59 powerd_warn("%s: assertion '%s' failed", __func__, #expr); \
60 return val; \
61 } \
62 } while (0)
63
64
42#ifdef __cplusplus65#ifdef __cplusplus
43}66}
44#endif67#endif
4568
=== modified file 'src/powerd-internal.h'
--- src/powerd-internal.h 2014-07-02 16:24:51 +0000
+++ src/powerd-internal.h 2014-08-19 19:23:22 +0000
@@ -19,8 +19,8 @@
19#ifndef __POWERD_INTERNAL_H__19#ifndef __POWERD_INTERNAL_H__
20#define __POWERD_INTERNAL_H__20#define __POWERD_INTERNAL_H__
2121
22#include <time.h> /* time_t */
22#include <uuid/uuid.h>23#include <uuid/uuid.h>
23#include <glib.h>
24#include <gio/gio.h>24#include <gio/gio.h>
2525
26#include "powerd-object.h"26#include "powerd-object.h"
@@ -107,6 +107,7 @@
107enum SysPowerStates current_system_power_state(void);107enum SysPowerStates current_system_power_state(void);
108const gchar * state_to_string(int state);108const gchar * state_to_string(int state);
109void powerd_sys_state_signal_emit(enum SysPowerStates state);109void powerd_sys_state_signal_emit(enum SysPowerStates state);
110void powerd_wakeup_signal_emit(void);
110void clear_sys_state_by_owner(const char *owner);111void clear_sys_state_by_owner(const char *owner);
111gboolean powerd_suspend_active(void);112gboolean powerd_suspend_active(void);
112113
@@ -164,6 +165,12 @@
164int powerd_ps_init(void);165int powerd_ps_init(void);
165void powerd_ps_deinit(void);166void powerd_ps_deinit(void);
166167
168/* Hardware alarm functions */
169void wakeup_init(void);
170void wakeup_deinit(void);
171int wakeup_request(const char *name, time_t time, powerd_cookie_t setme_cookie);
172int wakeup_clear(const powerd_cookie_t cookie);
173
167/* Client functions */174/* Client functions */
168int powerd_client_register(const char *dbus_name, const char *name);175int powerd_client_register(const char *dbus_name, const char *name);
169void powerd_client_unregister(const char *dbus_name);176void powerd_client_unregister(const char *dbus_name);
170177
=== modified file 'src/powerd-object.c'
--- src/powerd-object.c 2014-07-02 16:24:51 +0000
+++ src/powerd-object.c 2014-08-19 19:23:22 +0000
@@ -19,6 +19,7 @@
19#include <glib.h>19#include <glib.h>
20#include <glib-object.h>20#include <glib-object.h>
21#include <gio/gio.h>21#include <gio/gio.h>
22#include <inttypes.h>
22#include <string.h>23#include <string.h>
23#include <stdio.h>24#include <stdio.h>
24#include <errno.h>25#include <errno.h>
@@ -135,11 +136,69 @@
135 return TRUE;136 return TRUE;
136}137}
137138
139static gboolean
140handle_request_wakeup(PowerdSource *obj,
141 GDBusMethodInvocation *invocation,
142 const char *name,
143 uint64_t wakeup_time)
144{
145 int err;
146 powerd_cookie_t cookie;
147
148 powerd_debug("handle_requestWakeup from %s - %s, %zu",
149 g_dbus_method_invocation_get_sender(invocation),
150 name,
151 (size_t)wakeup_time);
152
153 if ((err = wakeup_request(name, wakeup_time, cookie)))
154 {
155 g_dbus_method_invocation_return_error_literal(invocation,
156 G_DBUS_ERROR,
157 G_DBUS_ERROR_FAILED,
158 strerror(err));
159 }
160 else
161 {
162 g_dbus_method_invocation_return_value(invocation,
163 g_variant_new("(s)", cookie));
164 }
165
166 return TRUE;
167}
168
169static gboolean
170handle_clear_wakeup(PowerdSource *obj,
171 GDBusMethodInvocation *invocation,
172 const char* cookie)
173{
174 int err;
175
176 powerd_debug("handle_clearWakeup from %s, cookie: %s",
177 g_dbus_method_invocation_get_sender(invocation),
178 cookie);
179
180 if ((err = wakeup_clear(cookie)))
181 {
182 g_dbus_method_invocation_return_error_literal(invocation,
183 G_DBUS_ERROR,
184 G_DBUS_ERROR_FAILED,
185 strerror(err));
186 }
187 else
188 {
189 g_dbus_method_invocation_return_value(invocation, NULL);
190 }
191
192 return TRUE;
193}
194
195
138void196void
139powerd_bus_acquired_cb (GDBusConnection *connection,197powerd_bus_acquired_cb (GDBusConnection *connection,
140 const gchar *name,198 const gchar *name,
141 gpointer user_data)199 gpointer user_data)
142{200{
201 GObject * oskel;
143 GError *error = NULL;202 GError *error = NULL;
144203
145 powerd_debug("Bus acquired (guid %s)", g_dbus_connection_get_guid (connection));204 powerd_debug("Bus acquired (guid %s)", g_dbus_connection_get_guid (connection));
@@ -156,28 +215,35 @@
156 powerd_exit(-1);215 powerd_exit(-1);
157 }216 }
158217
159 g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-request-sys-state",218 oskel = G_OBJECT(powerd_source->priv->skel);
219
220 g_signal_connect(oskel, "handle-request-sys-state",
160 G_CALLBACK(handle_request_sys_state), powerd_source);221 G_CALLBACK(handle_request_sys_state), powerd_source);
161 g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-clear-sys-state",222 g_signal_connect(oskel, "handle-clear-sys-state",
162 G_CALLBACK(handle_clear_sys_state), powerd_source);223 G_CALLBACK(handle_clear_sys_state), powerd_source);
163 g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-list-sys-requests",224 g_signal_connect(oskel, "handle-list-sys-requests",
164 G_CALLBACK(handle_list_sys_requests), powerd_source);225 G_CALLBACK(handle_list_sys_requests), powerd_source);
165226
166 g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-register-client",227 g_signal_connect(oskel, "handle-register-client",
167 G_CALLBACK(handle_register_client), powerd_source);228 G_CALLBACK(handle_register_client), powerd_source);
168 g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-unregister-client",229 g_signal_connect(oskel, "handle-unregister-client",
169 G_CALLBACK(handle_unregister_client), powerd_source);230 G_CALLBACK(handle_unregister_client), powerd_source);
170 g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-ack-state-change",231 g_signal_connect(oskel, "handle-ack-state-change",
171 G_CALLBACK(handle_ack_state_change), powerd_source);232 G_CALLBACK(handle_ack_state_change), powerd_source);
172 g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-get-sys-request-stats",233 g_signal_connect(oskel, "handle-get-sys-request-stats",
173 G_CALLBACK(handle_get_sys_request_stats), powerd_source);234 G_CALLBACK(handle_get_sys_request_stats), powerd_source);
174 g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-user-autobrightness-enable",235 g_signal_connect(oskel, "handle-user-autobrightness-enable",
175 G_CALLBACK(handle_user_autobrightness_enable), powerd_source);236 G_CALLBACK(handle_user_autobrightness_enable), powerd_source);
176 g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-get-brightness-params",237 g_signal_connect(oskel, "handle-get-brightness-params",
177 G_CALLBACK(handle_get_brightness_params), powerd_source);238 G_CALLBACK(handle_get_brightness_params), powerd_source);
178 g_signal_connect(G_OBJECT(powerd_source->priv->skel), "handle-set-user-brightness",239 g_signal_connect(oskel, "handle-set-user-brightness",
179 G_CALLBACK(handle_set_user_brightness), powerd_source);240 G_CALLBACK(handle_set_user_brightness), powerd_source);
180241
242 g_signal_connect(oskel, "handle-request-wakeup",
243 G_CALLBACK(handle_request_wakeup), powerd_source);
244 g_signal_connect(oskel, "handle-clear-wakeup",
245 G_CALLBACK(handle_clear_wakeup), powerd_source);
246
181 powerd_dbus_init_complete();247 powerd_dbus_init_complete();
182}248}
183249
@@ -224,6 +290,31 @@
224}290}
225291
226void292void
293powerd_wakeup_signal_emit (void)
294{
295 GError *error = NULL;
296
297 /* Make sure dbus has been set up */
298 if (!powerd_source)
299 return;
300
301 powerd_debug("Emitting wakeup signal");
302
303 g_dbus_connection_emit_signal(
304 POWERD_SOURCE_GET_PRIVATE(powerd_source)->system_bus,
305 NULL, /* destination */
306 "/com/canonical/powerd",
307 "com.canonical.powerd",
308 "Wakeup",
309 NULL,
310 &error);
311 if (error) {
312 powerd_warn("Unable to emit wakeup signal: %s", error->message);
313 g_error_free(error);
314 }
315}
316
317void
227ofono_manager_proxy_connect_cb(GObject *source_object,318ofono_manager_proxy_connect_cb(GObject *source_object,
228 GAsyncResult *res,319 GAsyncResult *res,
229 gpointer user_data)320 gpointer user_data)
230321
=== modified file 'src/powerd.cpp'
--- src/powerd.cpp 2014-08-12 07:23:07 +0000
+++ src/powerd.cpp 2014-08-19 19:23:22 +0000
@@ -577,6 +577,7 @@
577 powerd_autobrightness_init();577 powerd_autobrightness_init();
578 powerd_sensors_init();578 powerd_sensors_init();
579 powerd_ps_init();579 powerd_ps_init();
580 wakeup_init();
580581
581 main_loop = g_main_loop_new (NULL, FALSE);582 main_loop = g_main_loop_new (NULL, FALSE);
582 signal(SIGTERM, sigterm_quit);583 signal(SIGTERM, sigterm_quit);
@@ -598,6 +599,7 @@
598 g_bus_unown_name(name_id);599 g_bus_unown_name(name_id);
599 g_main_loop_unref(main_loop);600 g_main_loop_unref(main_loop);
600601
602 wakeup_deinit();
601 powerd_ps_deinit();603 powerd_ps_deinit();
602 dbus_name_watch_deinit();604 dbus_name_watch_deinit();
603 powerd_autobrightness_deinit();605 powerd_autobrightness_deinit();
604606
=== added file 'src/wakeup.cpp'
--- src/wakeup.cpp 1970-01-01 00:00:00 +0000
+++ src/wakeup.cpp 2014-08-19 19:23:22 +0000
@@ -0,0 +1,188 @@
1/*
2 * Copyright 2014 Canonical Ltd.
3 *
4 * Authors:
5 * Charles Kerr: charles.kerr@canonical.com
6 *
7 * This file is part of powerd.
8 *
9 * powerd is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * powerd is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22
23#include <ctime>
24#include <map>
25#include <memory> // std::unique_ptr
26#include <string>
27#include <thread>
28
29#include <glib.h>
30
31#include <ubuntu/hardware/alarm.h>
32
33#include "powerd-internal.h"
34#include "log.h"
35
36namespace
37{
38
39struct Request
40{
41 std::string name;
42 time_t time;
43};
44
45std::map<std::string,Request> requests;
46
47UHardwareAlarm hardware_alarm = nullptr;
48
49// NB: only the main thread accesses 'worker'
50std::unique_ptr<std::thread> worker;
51
52void start_worker_thread();
53
54void set_wakeup_time(time_t wakeup_time, const std::string& name)
55{
56 powerd_return_if_fail(hardware_alarm != nullptr);
57 powerd_return_if_fail(wakeup_time >= time(nullptr));
58
59 struct tm tm;
60 char buf[32];
61 localtime_r(&wakeup_time, &tm);
62 strftime(buf, sizeof(buf), "%F %T", &tm);
63 powerd_debug("setting hardware wakeup time to %s for %s", buf, name.c_str());
64
65 struct timespec sleep_interval;
66 sleep_interval.tv_sec = wakeup_time;
67 sleep_interval.tv_nsec = 0;
68 u_hardware_alarm_set_relative_to_with_behavior(
69 hardware_alarm,
70 U_HARDWARE_ALARM_TIME_REFERENCE_RTC,
71 U_HARDWARE_ALARM_SLEEP_BEHAVIOR_WAKEUP_DEVICE,
72 &sleep_interval);
73}
74
75void reset_alarm_clock()
76{
77 // find request that will occur next
78 const time_t now = time(nullptr);
79 const Request* next = nullptr;
80 size_t n_left = 0;
81 for (const auto& it : requests) {
82 if (now < it.second.time) {
83 ++n_left;
84 if (!next || (it.second.time < next->time))
85 next = &it.second;
86 }
87 }
88
89 if (next != nullptr)
90 {
91 powerd_debug("%s found %zu remaining wakeup requests", G_STRFUNC, n_left);
92 set_wakeup_time(next->time, next->name);
93 if (!worker)
94 start_worker_thread();
95 }
96 else if (worker) // no pending requests...
97 {
98 powerd_debug("shortening wakeup_time so worker thread will finish soon");
99 set_wakeup_time(time(nullptr)+1, G_STRFUNC);
100 }
101}
102
103gboolean on_worker_thread_finished(gpointer /*unused*/)
104{
105 // tell the world that we woke up
106 powerd_wakeup_signal_emit();
107
108 // worker thread is done, so clean up our 'worker' variable
109 powerd_debug("worker thread is finished working; calling join()");
110 worker->join();
111 worker.reset();
112 powerd_debug("worker thread is destroyed.");
113
114 reset_alarm_clock();
115
116 return G_SOURCE_REMOVE;
117}
118
119void threadfunc(UHardwareAlarm hw_alarm)
120{
121 // wait for the next alarm
122 powerd_debug("calling wait_for_next_alarm");
123 UHardwareAlarmWaitResult wait_result;
124 auto rc = u_hardware_alarm_wait_for_next_alarm(hw_alarm, &wait_result);
125 powerd_debug("worker thread finished waiting for hw alarm");
126 powerd_warn_if_fail(rc == U_STATUS_SUCCESS);
127 u_hardware_alarm_unref(hw_alarm);
128
129 // kick back to the main loop
130 g_idle_add(on_worker_thread_finished, nullptr);
131}
132
133void start_worker_thread()
134{
135 powerd_return_if_fail(hardware_alarm != nullptr);
136
137 powerd_debug("starting hardware alarm worker thread");
138 u_hardware_alarm_ref(hardware_alarm);
139 worker.reset(new std::thread(threadfunc, hardware_alarm));
140}
141
142} // private impl in unnamed namespace
143
144void
145wakeup_init(void)
146{
147 hardware_alarm = u_hardware_alarm_create();
148}
149
150void
151wakeup_deinit(void)
152{
153 requests.clear();
154 reset_alarm_clock();
155 g_clear_pointer(&hardware_alarm, u_hardware_alarm_unref);
156}
157
158int
159wakeup_request(const char* name, time_t wakeup_time, powerd_cookie_t cookie)
160{
161 powerd_return_val_if_fail(hardware_alarm, ENOTSUP);
162 powerd_return_val_if_fail(wakeup_time > time(nullptr), EINVAL);
163 powerd_return_val_if_fail(cookie != NULL, EINVAL);
164
165 // generate a new uuid string
166 uuid_t tmp;
167 uuid_generate(tmp);
168 uuid_unparse(tmp, cookie);
169
170 requests[cookie] = { name, wakeup_time };
171
172 reset_alarm_clock();
173 return 0;
174}
175
176int
177wakeup_clear(const powerd_cookie_t cookie)
178{
179 powerd_return_val_if_fail(hardware_alarm, ENOTSUP);
180 auto it = requests.find(cookie);
181 powerd_return_val_if_fail(it != requests.end(), EINVAL);
182
183 requests.erase(it);
184
185 reset_alarm_clock();
186 return 0;
187}
188

Subscribers

People subscribed via source and target branches