@@ -263,41 +263,76 @@ static int execute(
263263}
264264
265265static int execute_s2h (const SleepConfig * sleep_config ) {
266- _cleanup_close_ int tfd = -1 ;
267- struct itimerspec ts = {};
268266 int r ;
269267
270268 assert (sleep_config );
271269
272- tfd = timerfd_create (CLOCK_BOOTTIME_ALARM , TFD_NONBLOCK |TFD_CLOEXEC );
273- if (tfd < 0 )
274- return log_error_errno (errno , "Error creating timerfd: %m" );
275-
276- log_debug ("Set timerfd wake alarm for %s" ,
277- FORMAT_TIMESPAN (sleep_config -> hibernate_delay_sec , USEC_PER_SEC ));
278-
279- timespec_store (& ts .it_value , sleep_config -> hibernate_delay_sec );
280-
281- r = timerfd_settime (tfd , 0 , & ts , NULL );
282- if (r < 0 )
283- return log_error_errno (errno , "Error setting hibernate timer: %m" );
284-
285- r = execute (sleep_config , SLEEP_SUSPEND , NULL );
286- if (r < 0 )
287- return r ;
288-
289- r = fd_wait_for_event (tfd , POLLIN , 0 );
290- if (r < 0 )
291- return log_error_errno (r , "Error polling timerfd: %m" );
292- if (!FLAGS_SET (r , POLLIN )) /* We woke up before the alarm time, we are done. */
293- return 0 ;
270+ while (battery_is_low () == 0 ) {
271+ _cleanup_close_ int tfd = -1 ;
272+ struct itimerspec ts = {};
273+ usec_t suspend_interval = sleep_config -> hibernate_delay_sec , before_timestamp = 0 , after_timestamp = 0 ;
274+ bool woken_by_timer ;
275+ int last_capacity = 0 , current_capacity = 0 , estimated_discharge_rate = 0 ;
276+
277+ tfd = timerfd_create (CLOCK_BOOTTIME_ALARM , TFD_NONBLOCK |TFD_CLOEXEC );
278+ if (tfd < 0 )
279+ return log_error_errno (errno , "Error creating timerfd: %m" );
280+
281+ /* Store current battery capacity and current time before suspension */
282+ r = read_battery_capacity_percentage ();
283+ if (r >= 0 ) {
284+ last_capacity = r ;
285+ log_debug ("Current battery charge percentage: %d%%" , last_capacity );
286+ before_timestamp = now (CLOCK_BOOTTIME );
287+ } else if (r == - ENOENT )
288+ /* In case of no battery, system suspend interval will be set to HibernateDelaySec=. */
289+ log_debug_errno (r , "Suspend Interval value set to %s: %m" , FORMAT_TIMESPAN (suspend_interval , USEC_PER_SEC ));
290+ else
291+ return log_error_errno (r , "Error fetching battery capacity percentage: %m" );
292+
293+ log_debug ("Set timerfd wake alarm for %s" , FORMAT_TIMESPAN (suspend_interval , USEC_PER_SEC ));
294+ /* Wake alarm for system with or without battery to hibernate or estimate discharge rate whichever is applicable */
295+ timespec_store (& ts .it_value , suspend_interval );
296+
297+ if (timerfd_settime (tfd , 0 , & ts , NULL ) < 0 )
298+ return log_error_errno (errno , "Error setting battery estimate timer: %m" );
299+
300+ r = execute (sleep_config , SLEEP_SUSPEND , NULL );
301+ if (r < 0 )
302+ return r ;
294303
295- tfd = safe_close (tfd );
304+ r = fd_wait_for_event (tfd , POLLIN , 0 );
305+ if (r < 0 )
306+ return log_error_errno (r , "Error polling timerfd: %m" );
307+ /* Store fd_wait status */
308+ woken_by_timer = FLAGS_SET (r , POLLIN );
309+
310+ r = read_battery_capacity_percentage ();
311+ if (r >= 0 ) {
312+ current_capacity = r ;
313+ log_debug ("Current battery charge percentage after wakeup: %d%%" , current_capacity );
314+ } else if (r == - ENOENT ) {
315+ /* In case of no battery, system will be hibernated after 1st cycle of suspend */
316+ log_debug_errno (r , "Battery capacity percentage unavailable, cannot estimate discharge rate: %m" );
317+ break ;
318+ } else
319+ return log_error_errno (r , "Error fetching battery capacity percentage: %m" );
320+
321+ if (current_capacity >= last_capacity )
322+ log_debug ("Battery was not discharged during suspension" );
323+ else {
324+ after_timestamp = now (CLOCK_BOOTTIME );
325+ log_debug ("Attempting to estimate battery discharge rate after wakeup from %s sleep" , FORMAT_TIMESPAN (after_timestamp - before_timestamp , USEC_PER_HOUR ));
326+
327+ estimated_discharge_rate = (last_capacity - current_capacity ) * USEC_PER_HOUR / (after_timestamp - before_timestamp );
328+ }
296329
297- /* If woken up after alarm time, hibernate */
298- log_debug ("Attempting to hibernate after waking from %s timer" ,
299- FORMAT_TIMESPAN (sleep_config -> hibernate_delay_sec , USEC_PER_SEC ));
330+ if (!woken_by_timer )
331+ /* Return as manual wakeup done. This also will return in case battery was charged during suspension */
332+ return 0 ;
333+ }
300334
335+ log_debug ("Attempting to hibernate" );
301336 r = execute (sleep_config , SLEEP_HIBERNATE , NULL );
302337 if (r < 0 ) {
303338 log_notice ("Couldn't hibernate, will try to suspend again." );
0 commit comments