1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3
* Copyright (C) 2011 Landry Breuil <landry@openbsd.org>
5
* Licensed under the GNU General Public License Version 2
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22
#include "up-apm-native.h"
24
#include "up-backend.h"
25
#include "up-daemon.h"
26
#include "up-marshal.h"
27
#include "up-device.h"
28
#include <string.h> /* strcmp() */
30
#define UP_BACKEND_SUSPEND_COMMAND "/usr/sbin/zzz"
31
#define UP_BACKEND_POWERSAVE_TRUE_COMMAND "/usr/sbin/apm -C"
32
#define UP_BACKEND_POWERSAVE_FALSE_COMMAND "/usr/sbin/apm -A"
34
static void up_backend_class_init (UpBackendClass *klass);
35
static void up_backend_init (UpBackend *backend);
36
static void up_backend_finalize (GObject *object);
38
static gboolean up_backend_apm_get_power_info(struct apm_power_info*);
39
UpDeviceState up_backend_apm_get_battery_state_value(u_char battery_state);
40
static void up_backend_update_acpibat_state(UpDevice*, struct sensordev);
42
static gboolean up_apm_device_get_on_battery (UpDevice *device, gboolean *on_battery);
43
static gboolean up_apm_device_get_low_battery (UpDevice *device, gboolean *low_battery);
44
static gboolean up_apm_device_get_online (UpDevice *device, gboolean *online);
45
static gboolean up_apm_device_refresh (UpDevice *device);
47
#define UP_BACKEND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), UP_TYPE_BACKEND, UpBackendPrivate))
49
struct UpBackendPrivate
60
SIGNAL_DEVICE_REMOVED,
64
static guint signals [SIGNAL_LAST] = { 0 };
66
G_DEFINE_TYPE (UpBackend, up_backend, G_TYPE_OBJECT)
69
* functions called by upower daemon
73
/* those three ripped from freebsd/up-device-supply.c */
75
up_apm_device_get_on_battery (UpDevice *device, gboolean * on_battery)
81
g_return_val_if_fail (on_battery != NULL, FALSE);
86
"is-present", &is_present,
89
if (type != UP_DEVICE_KIND_BATTERY)
91
if (state == UP_DEVICE_STATE_UNKNOWN)
96
*on_battery = (state == UP_DEVICE_STATE_DISCHARGING);
100
up_apm_device_get_low_battery (UpDevice *device, gboolean * low_battery)
106
g_return_val_if_fail (low_battery != NULL, FALSE);
108
ret = up_apm_device_get_on_battery (device, &on_battery);
113
*low_battery = FALSE;
117
g_object_get (device, "percentage", &percentage, (void*) NULL);
118
*low_battery = (percentage < 10.0f);
123
up_apm_device_get_online (UpDevice *device, gboolean * online)
128
g_return_val_if_fail (online != NULL, FALSE);
130
g_object_get (device,
132
"online", &online_tmp,
135
if (type != UP_DEVICE_KIND_LINE_POWER)
138
*online = online_tmp;
143
* up_backend_coldplug:
144
* @backend: The %UpBackend class instance
145
* @daemon: The %UpDaemon controlling instance
147
* Finds all the devices already plugged in, and emits device-add signals for
150
* Return value: %TRUE for success
153
up_backend_coldplug (UpBackend *backend, UpDaemon *daemon)
155
UpApmNative *acnative = NULL;
156
UpApmNative *battnative = NULL;
157
backend->priv->daemon = g_object_ref (daemon);
158
/* XXX no way to get lid status atm */
159
up_daemon_set_lid_is_present (backend->priv->daemon, FALSE);
160
if (backend->priv->is_laptop)
162
acnative = up_apm_native_new("/ac");
163
if (!up_device_coldplug (backend->priv->ac, backend->priv->daemon, G_OBJECT(acnative)))
164
g_warning ("failed to coldplug ac");
166
g_signal_emit (backend, signals[SIGNAL_DEVICE_ADDED], 0, acnative, backend->priv->ac);
168
battnative = up_apm_native_new("/batt");
169
if (!up_device_coldplug (backend->priv->battery, backend->priv->daemon, G_OBJECT(battnative)))
170
g_warning ("failed to coldplug battery");
172
g_signal_emit (backend, signals[SIGNAL_DEVICE_ADDED], 0, battnative, backend->priv->battery);
180
* up_backend_get_powersave_command:
183
up_backend_get_powersave_command (UpBackend *backend, gboolean powersave)
186
return UP_BACKEND_POWERSAVE_TRUE_COMMAND;
187
return UP_BACKEND_POWERSAVE_FALSE_COMMAND;
191
* up_backend_get_suspend_command:
194
up_backend_get_suspend_command (UpBackend *backend)
196
return UP_BACKEND_SUSPEND_COMMAND;
200
* up_backend_get_hibernate_command:
203
up_backend_get_hibernate_command (UpBackend *backend)
209
up_backend_emits_resuming (UpBackend *backend)
215
* up_backend_kernel_can_suspend:
218
up_backend_kernel_can_suspend (UpBackend *backend)
224
* up_backend_kernel_can_hibernate:
227
up_backend_kernel_can_hibernate (UpBackend *backend)
233
up_backend_has_encrypted_swap (UpBackend *backend)
238
/* Return value: a percentage value */
240
up_backend_get_used_swap (UpBackend *backend)
246
* OpenBSD specific code
250
up_backend_apm_get_power_info(struct apm_power_info *bstate) {
251
bstate->battery_state = 255;
252
bstate->ac_state = 255;
253
bstate->battery_life = 0;
254
bstate->minutes_left = -1;
256
if (-1 == ioctl(up_apm_get_fd(), APM_IOC_GETPOWER, bstate)) {
257
g_error("ioctl on apm fd failed : %s", g_strerror(errno));
263
UpDeviceState up_backend_apm_get_battery_state_value(u_char battery_state) {
264
switch(battery_state) {
266
return UP_DEVICE_STATE_FULLY_CHARGED;
268
return UP_DEVICE_STATE_DISCHARGING; // XXXX
269
case APM_BATT_CRITICAL:
270
return UP_DEVICE_STATE_EMPTY;
271
case APM_BATT_CHARGING:
272
return UP_DEVICE_STATE_CHARGING;
273
case APM_BATTERY_ABSENT:
274
return UP_DEVICE_STATE_EMPTY;
275
case APM_BATT_UNKNOWN:
276
return UP_DEVICE_STATE_UNKNOWN;
282
up_backend_update_ac_state(UpDevice* device)
284
gboolean ret, new_is_online, cur_is_online;
285
struct apm_power_info a;
287
ret = up_backend_apm_get_power_info(&a);
291
g_object_get (device, "online", &cur_is_online, (void*) NULL);
292
/* XXX use acpiac0.indicator0 if available */
293
new_is_online = (a.ac_state == APM_AC_ON ? TRUE : FALSE);
294
if (cur_is_online != new_is_online)
296
g_object_set (device,
297
"online", new_is_online,
305
up_backend_update_battery_state(UpDevice* device)
309
struct sensordev sdev;
310
UpDeviceState cur_state, new_state;
311
gint64 cur_time_to_empty, new_time_to_empty;
312
struct apm_power_info a;
314
ret = up_backend_apm_get_power_info(&a);
318
g_object_get (device,
320
"percentage", &percentage,
321
"time-to-empty", &cur_time_to_empty,
324
/* XXX use acpibat0.raw0 if available */
325
new_state = up_backend_apm_get_battery_state_value(a.battery_state);
326
// if percentage/minutes goes down or ac is off, we're likely discharging..
327
if (percentage < a.battery_life || cur_time_to_empty < new_time_to_empty || a.ac_state == APM_AC_OFF)
328
new_state = UP_DEVICE_STATE_DISCHARGING;
329
if (a.ac_state == APM_AC_ON)
330
new_state = UP_DEVICE_STATE_CHARGING;
332
// zero out new_time_to empty if we're not discharging or minutes_left is negative
333
new_time_to_empty = (new_state == UP_DEVICE_STATE_DISCHARGING && a.minutes_left > 0 ? a.minutes_left : 0);
335
if (cur_state != new_state ||
336
percentage != (gdouble) a.battery_life ||
337
cur_time_to_empty != new_time_to_empty)
339
g_object_set (device,
341
"percentage", (gdouble) a.battery_life,
342
"time-to-empty", new_time_to_empty * 60,
344
if(up_native_get_sensordev("acpibat0", &sdev))
345
up_backend_update_acpibat_state(device, sdev);
351
/* update acpibat properties */
353
up_backend_update_acpibat_state(UpDevice* device, struct sensordev s)
355
enum sensor_type type;
357
gdouble bst_volt, bst_rate, bif_lastfullcap, bst_cap, bif_lowcap;
358
/* gdouble bif_dvolt, bif_dcap, capacity; */
360
size_t slen = sizeof(sens);
361
int mib[] = {CTL_HW, HW_SENSORS, 0, 0, 0};
364
for (type = 0; type < SENSOR_MAX_TYPES; type++) {
366
for (numt = 0; numt < s.maxnumt[type]; numt++) {
368
if (sysctl(mib, 5, &sens, &slen, NULL, 0) < 0)
369
g_error("failed to get sensor type %d(%s) numt %d on %s", type, sensor_type_s[type], numt, s.xname);
370
else if (slen > 0 && (sens.flags & SENSOR_FINVALID) == 0) {
371
if (sens.type == SENSOR_VOLTS_DC && !strcmp(sens.desc, "current voltage"))
372
bst_volt = sens.value / 1000000.0f;
373
if ((sens.type == SENSOR_AMPHOUR || sens.type == SENSOR_WATTHOUR) && !strcmp(sens.desc, "last full capacity")) {
374
bif_lastfullcap = (sens.type == SENSOR_AMPHOUR ? bst_volt : 1) * sens.value / 1000000.0f;
376
if ((sens.type == SENSOR_AMPHOUR || sens.type == SENSOR_WATTHOUR) && !strcmp(sens.desc, "low capacity")) {
377
bif_lowcap = (sens.type == SENSOR_AMPHOUR ? bst_volt : 1) * sens.value / 1000000.0f;
379
if ((sens.type == SENSOR_AMPHOUR || sens.type == SENSOR_WATTHOUR) && !strcmp(sens.desc, "remaining capacity")) {
380
bst_cap = (sens.type == SENSOR_AMPHOUR ? bst_volt : 1) * sens.value / 1000000.0f;
382
if ((sens.type == SENSOR_AMPS || sens.type == SENSOR_WATTS) && !strcmp(sens.desc, "rate")) {
383
bst_rate = (sens.type == SENSOR_AMPS ? bst_volt : 1) * sens.value / 1000000.0f;
386
bif_dvolt = "voltage" = unused ?
387
capacity = lastfull/dcap * 100 ?
388
amphour1 = warning capacity ?
394
g_object_set (device,
396
"energy-full", bif_lastfullcap,
397
"energy-rate", bst_rate,
398
"energy-empty", bif_lowcap,
403
/* callback updating the device */
405
up_backend_apm_powerchange_event_cb(gpointer object)
409
g_return_val_if_fail (UP_IS_BACKEND (object), FALSE);
410
backend = UP_BACKEND (object);
411
up_apm_device_refresh(backend->priv->ac);
412
up_apm_device_refresh(backend->priv->battery);
413
/* return false to not endless loop */
418
up_apm_device_refresh(UpDevice* device)
423
g_object_get (device, "type", &type, NULL);
426
case UP_DEVICE_KIND_LINE_POWER:
427
ret = up_backend_update_ac_state(device);
429
case UP_DEVICE_KIND_BATTERY:
430
ret = up_backend_update_battery_state(device);
433
g_assert_not_reached ();
438
g_get_current_time (&timeval);
439
g_object_set (device, "update-time", (guint64) timeval.tv_sec, NULL);
445
/* thread doing kqueue() on apm device */
447
up_backend_apm_event_thread(gpointer object)
451
struct timespec ts = {600, 0}, sts = {0, 0};
455
g_return_val_if_fail (UP_IS_BACKEND (object), NULL);
456
backend = UP_BACKEND (object);
458
g_debug("setting up apm thread");
463
EV_SET(&ev, up_apm_get_fd(), EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR,
466
if (kevent(kq, &ev, nevents, NULL, 0, &sts) < 0)
469
/* blocking wait on kqueue */
475
if ((rv = kevent(kq, NULL, 0, &ev, 1, &sts)) < 0)
479
if (ev.ident == (guint) up_apm_get_fd() && APM_EVENT_TYPE(ev.data) == APM_POWER_CHANGE ) {
480
/* g_idle_add the callback */
481
g_idle_add((GSourceFunc) up_backend_apm_powerchange_event_cb, backend);
485
/* shouldnt be reached ? */
489
* GObject class functions
495
* Return value: a new %UpBackend object.
498
up_backend_new (void)
501
backend = g_object_new (UP_TYPE_BACKEND, NULL);
502
return UP_BACKEND (backend);
506
* up_backend_class_init:
507
* @klass: The UpBackendClass
510
up_backend_class_init (UpBackendClass *klass)
512
GObjectClass *object_class = G_OBJECT_CLASS (klass);
513
object_class->finalize = up_backend_finalize;
515
signals [SIGNAL_DEVICE_ADDED] =
516
g_signal_new ("device-added",
517
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
518
G_STRUCT_OFFSET (UpBackendClass, device_added),
519
NULL, NULL, up_marshal_VOID__POINTER_POINTER,
520
G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
521
signals [SIGNAL_DEVICE_REMOVED] =
522
g_signal_new ("device-removed",
523
G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
524
G_STRUCT_OFFSET (UpBackendClass, device_removed),
525
NULL, NULL, up_marshal_VOID__POINTER_POINTER,
526
G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
528
g_type_class_add_private (klass, sizeof (UpBackendPrivate));
535
up_backend_init (UpBackend *backend)
539
UpDeviceClass *device_class;
541
backend->priv = UP_BACKEND_GET_PRIVATE (backend);
542
backend->priv->daemon = NULL;
543
backend->priv->is_laptop = up_native_is_laptop();
544
g_debug("is_laptop:%d",backend->priv->is_laptop);
545
if (backend->priv->is_laptop)
547
backend->priv->ac = UP_DEVICE(up_device_new());
548
backend->priv->battery = UP_DEVICE(up_device_new ());
549
device_class = UP_DEVICE_GET_CLASS (backend->priv->battery);
550
device_class->get_on_battery = up_apm_device_get_on_battery;
551
device_class->get_low_battery = up_apm_device_get_low_battery;
552
device_class->get_online = up_apm_device_get_online;
553
device_class->refresh = up_apm_device_refresh;
554
device_class = UP_DEVICE_GET_CLASS (backend->priv->ac);
555
device_class->get_on_battery = up_apm_device_get_on_battery;
556
device_class->get_low_battery = up_apm_device_get_low_battery;
557
device_class->get_online = up_apm_device_get_online;
558
device_class->refresh = up_apm_device_refresh;
559
g_thread_init (NULL);
561
if((backend->priv->apm_thread = (GThread*) g_thread_create((GThreadFunc)up_backend_apm_event_thread, (void*) backend, FALSE, &err) == NULL))
563
g_warning("Thread create failed: %s", err->message);
568
g_get_current_time (&timeval);
569
g_object_set (backend->priv->battery,
570
"type", UP_DEVICE_KIND_BATTERY,
571
"power-supply", TRUE,
573
"is-rechargeable", TRUE,
575
"state", UP_DEVICE_STATE_UNKNOWN,
577
"time-to-empty", (gint64) 0,
578
"update-time", (guint64) timeval.tv_sec,
580
g_object_set (backend->priv->ac,
581
"type", UP_DEVICE_KIND_LINE_POWER,
583
"power-supply", TRUE,
584
"update-time", (guint64) timeval.tv_sec,
587
backend->priv->ac = NULL;
588
backend->priv->battery = NULL;
592
* up_backend_finalize:
595
up_backend_finalize (GObject *object)
599
g_return_if_fail (UP_IS_BACKEND (object));
601
backend = UP_BACKEND (object);
603
if (backend->priv->daemon != NULL)
604
g_object_unref (backend->priv->daemon);
605
if (backend->priv->battery != NULL)
606
g_object_unref (backend->priv->battery);
607
if (backend->priv->ac != NULL)
608
g_object_unref (backend->priv->ac);
609
/* XXX stop apm_thread ? */
611
G_OBJECT_CLASS (up_backend_parent_class)->finalize (object);