add support for samsung power management - samsung
[platform/upstream/pulseaudio.git] / src / modules / module-suspend-on-idle.c
index 0585e51..6c0935e 100644 (file)
 #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");
@@ -47,6 +93,13 @@ static const char* const valid_modargs[] = {
     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;
@@ -70,6 +123,9 @@ struct userdata {
         *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 {
@@ -80,9 +136,79 @@ 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);
 
@@ -92,12 +218,33 @@ static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval
         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 */
     }
 }
 
@@ -117,17 +264,37 @@ static void restart(struct device_info *d) {
 }
 
 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);
     }
 }
@@ -448,6 +615,9 @@ int pa__init(pa_module*m) {
     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);