#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
+#include <pulsecore/namereg.h>
#include "module-suspend-on-idle-symdef.h"
+//move to configure.ac
+//#define USE_PM_LOCK /* Enable as default */
+#ifdef USE_PM_LOCK
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <linux/limits.h>
+
+#define SOCK_PATH "/tmp/pm_sock"
+#define SHIFT_UNLOCK 4
+#define SHIFT_UNLOCK_PARAMETER 12
+#define SHIFT_CHANGE_STATE 8
+#define SHIFT_HOLD_KEY_BLOCK 16
+#define SHIFT_CHANGE_TIMEOUT 20
+#define TIMEOUT_RESET_BIT 0x80
+
+#define LCD_NORMAL 0x1 /**< NORMAL state */
+#define LCD_DIM 0x2 /**< LCD dimming state */
+#define LCD_OFF 0x4 /**< LCD off state */
+#define SUSPEND 0x8 /**< Sleep state */
+#define POWER_OFF 0x16 /**< Sleep state */
+#define SETALL (LCD_DIM | LCD_OFF | LCD_NORMAL) /*< select all state - not supported yet */
+
+/* parameters for pm_lock_state() */
+#define STAY_CUR_STATE 0x0
+#define GOTO_STATE_NOW 0x1
+#define HOLD_KEY_BLOCK 0x2
+
+/* paramters for pm_unlcok_state() - details are described at 162 line */
+#define PM_SLEEP_MARGIN 0x0 /**< keep guard time for unlock */
+#define PM_RESET_TIMER 0x1 /**< reset timer for unlock */
+#define PM_KEEP_TIMER 0x2 /**< keep timer for unlock */
+
+struct pwr_msg {
+ pid_t pid;
+ unsigned int cond;
+ unsigned int timeout;
+ unsigned int timeout2;
+};
+
+#endif /* USE_PM_LOCK */
PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("When a sink/source is idle for too long, suspend it");
NULL,
};
+#ifdef USE_PM_LOCK
+#define PM_TYPE_SINK 0x01
+#define PM_TYPE_SOURCE 0x02
+
+#define UPDATE_PM_LOCK(current,type) (current |= type)
+#define UPDATE_PM_UNLOCK(current,type) (current &= ~type)
+#endif /* USE_PM_LOCK */
struct userdata {
pa_core *core;
pa_usec_t timeout;
*source_output_move_finish_slot,
*sink_input_state_changed_slot,
*source_output_state_changed_slot;
+#ifdef USE_PM_LOCK
+ uint32_t pm_state;
+#endif /* USE_PM_LOCK */
};
struct device_info {
pa_time_event *time_event;
pa_usec_t timeout;
};
+#ifdef USE_PM_LOCK
+
+static int send_msg(unsigned int s_bits, unsigned int timeout, unsigned int timeout2)
+{
+ int rc = 0;
+ int sock;
+ struct pwr_msg p;
+ struct sockaddr_un remote;
+
+ p.pid = getpid();
+ p.cond = s_bits;
+ p.timeout = timeout;
+ p.timeout2 = timeout2;
+
+ sock = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (sock == -1) {
+ return -1;
+ }
+
+ remote.sun_family = AF_UNIX;
+ if(strlen(SOCK_PATH) >= sizeof(remote.sun_path)) {
+ return -1;
+ }
+ strncpy(remote.sun_path, SOCK_PATH, sizeof(remote.sun_path));
+
+ rc = sendto(sock, (void *)&p, sizeof(p), 0, (struct sockaddr *)&remote,
+ sizeof(struct sockaddr_un));
+
+ close(sock);
+ return rc;
+}
+
+static int pm_lock_state(unsigned int s_bits, unsigned int flag,
+ unsigned int timeout)
+{
+ switch (s_bits) {
+ case LCD_NORMAL:
+ case LCD_DIM:
+ case LCD_OFF:
+ break;
+ default:
+ return -1;
+ }
+ if (flag & GOTO_STATE_NOW)
+ /* if the flag is true, go to the locking state directly */
+ s_bits = s_bits | (s_bits << SHIFT_CHANGE_STATE);
+ if (flag & HOLD_KEY_BLOCK)
+ s_bits = s_bits | (1 << SHIFT_HOLD_KEY_BLOCK);
+
+ return send_msg(s_bits, timeout, 0);
+}
+
+static int pm_unlock_state(unsigned int s_bits, unsigned int flag)
+{
+ switch (s_bits) {
+ case LCD_NORMAL:
+ case LCD_DIM:
+ case LCD_OFF:
+ break;
+ default:
+ return -1;
+ }
+
+ s_bits = (s_bits << SHIFT_UNLOCK);
+ s_bits = (s_bits | (flag << SHIFT_UNLOCK_PARAMETER));
+ return send_msg(s_bits, 0, 0);
+}
+
+#endif
static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
struct device_info *d = userdata;
+ int ret = -1;
pa_assert(d);
pa_log_info("Sink %s idle for too long, suspending ...", d->sink->name);
pa_sink_suspend(d->sink, true, PA_SUSPEND_IDLE);
pa_core_maybe_vacuum(d->userdata->core);
+#ifdef USE_PM_LOCK
+ UPDATE_PM_UNLOCK(d->userdata->pm_state, PM_TYPE_SINK);
+ if(!(d->userdata->pm_state)) {
+ ret = pm_unlock_state(LCD_OFF, PM_SLEEP_MARGIN);
+ if(ret != -1)
+ pa_log_info("sink pm_unlock_state success [%d]", ret);
+ else
+ pa_log_error("sink pm_unlock_state failed [%d]", ret);
+ }
+#endif /* USE_PM_LOCK */
}
if (d->source && pa_source_check_suspend(d->source) <= 0 && !(d->source->suspend_cause & PA_SUSPEND_IDLE)) {
pa_log_info("Source %s idle for too long, suspending ...", d->source->name);
pa_source_suspend(d->source, true, PA_SUSPEND_IDLE);
pa_core_maybe_vacuum(d->userdata->core);
+#ifdef USE_PM_LOCK
+
+ UPDATE_PM_UNLOCK(d->userdata->pm_state, PM_TYPE_SOURCE);
+ if(!(d->userdata->pm_state)) {
+ ret = pm_unlock_state(LCD_OFF, PM_SLEEP_MARGIN);
+ if(ret != -1)
+ pa_log_info("source pm_unlock_state success [%d]", ret);
+ else
+ pa_log_error("source pm_unlock_state failed [%d]", ret);
+ }
+#endif /* USE_PM_LOCK */
}
}
}
static void resume(struct device_info *d) {
+ int ret = -1;
+
pa_assert(d);
d->userdata->core->mainloop->time_restart(d->time_event, NULL);
if (d->sink) {
- pa_log_debug("Sink %s becomes busy, resuming.", d->sink->name);
+#ifdef USE_PM_LOCK
+ UPDATE_PM_LOCK(d->userdata->pm_state, PM_TYPE_SINK);
+ ret = pm_lock_state(LCD_OFF, STAY_CUR_STATE, 0);
+ if(ret != -1) {
+ pa_log_info("sink pm_lock_state success [%d]", ret);
+ } else {
+ pa_log_error("sink pm_lock_state failed [%d]", ret);
+ }
+#endif /* USE_PM_LOCK */
+ pa_log_debug("Sink %s becomes busy.", d->sink->name);
pa_sink_suspend(d->sink, false, PA_SUSPEND_IDLE);
}
if (d->source) {
- pa_log_debug("Source %s becomes busy, resuming.", d->source->name);
+#ifdef USE_PM_LOCK
+ UPDATE_PM_LOCK(d->userdata->pm_state, PM_TYPE_SOURCE);
+ ret = pm_lock_state(LCD_OFF, STAY_CUR_STATE, 0);
+ if(ret != -1) {
+ pa_log_info("source pm_lock_state success [%d]", ret);
+ } else {
+ pa_log_error("source pm_lock_state failed [%d]", ret);
+ }
+#endif /* USE_PM_LOCK */
+ pa_log_debug("Source %s becomes busy.", d->source->name);
pa_source_suspend(d->source, false, PA_SUSPEND_IDLE);
}
}
u->core = m->core;
u->timeout = timeout * PA_USEC_PER_SEC;
u->device_infos = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL, (pa_free_cb_t) device_info_free);
+#ifdef USE_PM_LOCK
+ u->pm_state = 0x00;
+#endif /* USE_PM_LOCK */
PA_IDXSET_FOREACH(sink, m->core->sinks, idx)
device_new_hook_cb(m->core, PA_OBJECT(sink), u);