power: Process video decode and screen off hints.
Process video decode hints and display-off hints
in the power HAL. Also move to using the perflock
API to request system-level parameter changes.
Change-Id: I42e33d6d22016b02d85572a69fee64aadf36da58
diff --git a/power.c b/power.c
index 5c01857..3182b75 100644
--- a/power.c
+++ b/power.c
@@ -27,6 +27,8 @@
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#define LOG_NIDEBUG 0
+
#include <errno.h>
#include <string.h>
#include <sys/types.h>
@@ -36,32 +38,23 @@
#include <stdlib.h>
#define LOG_TAG "QCOM PowerHAL"
-
#include <utils/Log.h>
-#include <cutils/properties.h>
-
#include <hardware/hardware.h>
#include <hardware/power.h>
+#include "utils.h"
#include "metadata-defs.h"
+#include "hint-data.h"
+#include "performance.h"
#define NODE_MAX (64)
#define SCALING_GOVERNOR_PATH "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
-#define ONDEMAND_PATH "/sys/devices/system/cpu/cpufreq/ondemand/"
-#define ONDEMAND_IO_BUSY_PATH "/sys/devices/system/cpu/cpufreq/ondemand/io_is_busy"
-#define ONDEMAND_SAMPLING_DOWN_PATH "/sys/devices/system/cpu/cpufreq/ondemand/sampling_down_factor"
#define DCVS_CPU0_SLACK_MAX_NODE "/sys/module/msm_dcvs/cores/cpu0/slack_time_max_us"
#define DCVS_CPU0_SLACK_MIN_NODE "/sys/module/msm_dcvs/cores/cpu0/slack_time_min_us"
#define MPDECISION_SLACK_MAX_NODE "/sys/module/msm_mpdecision/slack_time_max_us"
#define MPDECISION_SLACK_MIN_NODE "/sys/module/msm_mpdecision/slack_time_min_us"
-static int (*perf_vote_turnoff_ondemand_io_busy)(int vote);
-static int perf_vote_ondemand_io_busy_unavailable;
-static int (*perf_vote_lower_ondemand_sdf)(int vote);
-static int perf_vote_ondemand_sdf_unavailable;
-static void *qcopt_handle;
-static int qcopt_handle_unavailable;
static int saved_ondemand_sampling_down_factor = 4;
static int saved_ondemand_io_is_busy_status = 1;
static int saved_dcvs_cpu0_slack_max = -1;
@@ -71,116 +64,67 @@
static int saved_interactive_mode = -1;
static int slack_node_rw_failed = 0;
-static void *get_qcopt_handle()
-{
- if (qcopt_handle_unavailable) {
- return NULL;
- }
-
- if (!qcopt_handle) {
- char qcopt_lib_path[PATH_MAX] = {0};
- dlerror();
-
- if (property_get("ro.vendor.extension_library", qcopt_lib_path,
- NULL) != 0) {
- if((qcopt_handle = dlopen(qcopt_lib_path, RTLD_NOW)) == NULL) {
- qcopt_handle_unavailable = 1;
- ALOGE("Unable to open %s: %s\n", qcopt_lib_path,
- dlerror());
- }
- } else {
- qcopt_handle_unavailable = 1;
- ALOGE("Property ro.vendor.extension_library does not exist.");
- }
- }
-
- return qcopt_handle;
-}
-
-static int sysfs_read(char *path, char *s, int num_bytes)
-{
- char buf[80];
- int count;
- int ret = 0;
- int fd = open(path, O_RDONLY);
-
- if (fd < 0) {
- strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error opening %s: %s\n", path, buf);
-
- return -1;
- }
-
- if ((count = read(fd, s, num_bytes - 1)) < 0) {
- strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error writing to %s: %s\n", path, buf);
-
- ret = -1;
- } else {
- s[count] = '\0';
- }
-
- close(fd);
-
- return ret;
-}
-
-static int sysfs_write(char *path, char *s)
-{
- char buf[80];
- int len;
- int ret = 0;
- int fd = open(path, O_WRONLY);
-
- if (fd < 0) {
- strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error opening %s: %s\n", path, buf);
- return -1 ;
- }
-
- len = write(fd, s, strlen(s));
- if (len < 0) {
- strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error writing to %s: %s\n", path, buf);
-
- ret = -1;
- }
-
- close(fd);
-
- return ret;
-}
-
static struct hw_module_methods_t power_module_methods = {
.open = NULL,
};
-void power_init(struct power_module *module)
+static void power_init(struct power_module *module)
{
ALOGI("QCOM power HAL initing.");
}
-static int get_scaling_governor(char governor[], int size) {
- if (sysfs_read(SCALING_GOVERNOR_PATH, governor,
- size) == -1) {
- // Can't obtain the scaling governor. Return.
- return -1;
- } else {
- // Strip newline at the end.
- int len = strlen(governor);
+static void process_video_decode_hint(void *metadata)
+{
+ char governor[80];
+ struct video_decode_metadata_t video_decode_metadata;
- len--;
+ if (get_scaling_governor(governor, sizeof(governor)) == -1) {
+ ALOGE("Can't obtain scaling governor.");
- while (len >= 0 && (governor[len] == '\n' || governor[len] == '\r'))
- governor[len--] = '\0';
+ return;
}
- return 0;
+ if (metadata) {
+ ALOGI("Processing video decode hint. Metadata: %s", (char *)metadata);
+ }
+
+ /* Initialize encode metadata struct fields. */
+ memset(&video_decode_metadata, 0, sizeof(struct video_decode_metadata_t));
+ video_decode_metadata.state = -1;
+ video_decode_metadata.hint_id = DEFAULT_VIDEO_DECODE_HINT_ID;
+
+ if (metadata) {
+ if (parse_video_decode_metadata((char *)metadata, &video_decode_metadata) ==
+ -1) {
+ ALOGE("Error occurred while parsing metadata.");
+ return;
+ }
+ } else {
+ return;
+ }
+
+ if (video_decode_metadata.state == 1) {
+ if ((strlen(governor) == strlen("ondemand")) &&
+ (strncmp(governor, "ondemand", strlen("ondemand")) == 0)) {
+ } else if ((strlen(governor) == strlen("interactive")) &&
+ (strncmp(governor, "interactive", strlen("interactive")) == 0)) {
+ int resource_values[] = {TR_MS_30, HISPEED_LOAD_90, HS_FREQ_1026};
+
+ perform_hint_action(video_decode_metadata.hint_id,
+ resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
+ }
+ } else if (video_decode_metadata.state == 0) {
+ if ((strlen(governor) == strlen("ondemand")) &&
+ (strncmp(governor, "ondemand", strlen("ondemand")) == 0)) {
+ } else if ((strlen(governor) == strlen("interactive")) &&
+ (strncmp(governor, "interactive", strlen("interactive")) == 0)) {
+ undo_hint_action(video_decode_metadata.hint_id);
+ }
+ }
}
static void process_video_encode_hint(void *metadata)
{
- void *handle;
char governor[80];
struct video_encode_metadata_t video_encode_metadata;
@@ -191,11 +135,12 @@
}
/* Initialize encode metadata struct fields. */
- memset(&video_encode_metadata, 0, sizeof(video_encode_metadata));
+ memset(&video_encode_metadata, 0, sizeof(struct video_encode_metadata_t));
video_encode_metadata.state = -1;
+ video_encode_metadata.hint_id = DEFAULT_VIDEO_ENCODE_HINT_ID;
if (metadata) {
- if (parse_video_metadata((char *)metadata, &video_encode_metadata) ==
+ if (parse_video_encode_metadata((char *)metadata, &video_encode_metadata) ==
-1) {
ALOGE("Error occurred while parsing metadata.");
return;
@@ -204,64 +149,27 @@
return;
}
- if ((handle = get_qcopt_handle())) {
- if (video_encode_metadata.state == 1) {
- if ((strlen(governor) == strlen("ondemand")) &&
- (strncmp(governor, "ondemand", strlen("ondemand")) == 0)) {
- if (!perf_vote_ondemand_io_busy_unavailable) {
- perf_vote_turnoff_ondemand_io_busy = dlsym(handle,
- "perf_vote_turnoff_ondemand_io_busy");
+ if (video_encode_metadata.state == 1) {
+ if ((strlen(governor) == strlen("ondemand")) &&
+ (strncmp(governor, "ondemand", strlen("ondemand")) == 0)) {
+ int resource_values[] = {IO_BUSY_OFF, SAMPLING_DOWN_FACTOR_1};
- if (perf_vote_turnoff_ondemand_io_busy) {
- /* Vote to turn io_is_busy off */
- perf_vote_turnoff_ondemand_io_busy(1);
- } else {
- perf_vote_ondemand_io_busy_unavailable = 1;
- ALOGE("Can't set io_busy_status.");
- }
- }
+ perform_hint_action(video_encode_metadata.hint_id,
+ resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
+ } else if ((strlen(governor) == strlen("interactive")) &&
+ (strncmp(governor, "interactive", strlen("interactive")) == 0)) {
+ int resource_values[] = {TR_MS_30, HISPEED_LOAD_90, HS_FREQ_1026};
- if (!perf_vote_ondemand_sdf_unavailable) {
- perf_vote_lower_ondemand_sdf = dlsym(handle,
- "perf_vote_lower_ondemand_sdf");
-
- if (perf_vote_lower_ondemand_sdf) {
- perf_vote_lower_ondemand_sdf(1);
- } else {
- perf_vote_ondemand_sdf_unavailable = 1;
- ALOGE("Can't set sampling_down_factor.");
- }
- }
- }
- } else if (video_encode_metadata.state == 0) {
- if ((strlen(governor) == strlen("ondemand")) &&
- (strncmp(governor, "ondemand", strlen("ondemand")) == 0)) {
- if (!perf_vote_ondemand_io_busy_unavailable) {
- perf_vote_turnoff_ondemand_io_busy = dlsym(handle,
- "perf_vote_turnoff_ondemand_io_busy");
-
- if (perf_vote_turnoff_ondemand_io_busy) {
- /* Remove vote to turn io_busy off. */
- perf_vote_turnoff_ondemand_io_busy(0);
- } else {
- perf_vote_ondemand_io_busy_unavailable = 1;
- ALOGE("Can't set io_busy_status.");
- }
- }
-
- if (!perf_vote_ondemand_sdf_unavailable) {
- perf_vote_lower_ondemand_sdf = dlsym(handle,
- "perf_vote_lower_ondemand_sdf");
-
- if (perf_vote_lower_ondemand_sdf) {
- /* Remove vote to lower sampling down factor. */
- perf_vote_lower_ondemand_sdf(0);
- } else {
- perf_vote_ondemand_sdf_unavailable = 1;
- ALOGE("Can't set sampling_down_factor.");
- }
- }
- }
+ perform_hint_action(video_encode_metadata.hint_id,
+ resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
+ }
+ } else if (video_encode_metadata.state == 0) {
+ if ((strlen(governor) == strlen("ondemand")) &&
+ (strncmp(governor, "ondemand", strlen("ondemand")) == 0)) {
+ undo_hint_action(video_encode_metadata.hint_id);
+ } else if ((strlen(governor) == strlen("interactive")) &&
+ (strncmp(governor, "interactive", strlen("interactive")) == 0)) {
+ undo_hint_action(video_encode_metadata.hint_id);
}
}
}
@@ -289,168 +197,203 @@
case POWER_HINT_VIDEO_ENCODE:
process_video_encode_hint(data);
break;
+ case POWER_HINT_VIDEO_DECODE:
+ process_video_decode_hint(data);
+ break;
+
}
}
-static void set_interactive(struct power_module *module, int on)
+int __attribute__ ((weak)) set_interactive_override(struct power_module *module, int on)
+{
+ return -1;
+}
+
+void set_interactive(struct power_module *module, int on)
{
char governor[80];
char tmp_str[NODE_MAX];
- int rc = 0;
+ struct video_encode_metadata_t video_encode_metadata;
+ int rc;
+
+ if (set_interactive_override(module, on) == 0) {
+ return;
+ }
if (get_scaling_governor(governor, sizeof(governor)) == -1) {
- if (!slack_node_rw_failed) {
- ALOGE("Can't obtain scaling governor.");
- slack_node_rw_failed = 1;
- }
+ ALOGE("Can't obtain scaling governor.");
return;
}
- if ((strlen(governor) == strlen("msm-dcvs")) &&
- (strncmp(governor, "msm-dcvs", strlen("msm-dcvs")) == 0)) {
- if (on && (saved_interactive_mode == -1 || saved_interactive_mode == 0)) {
- /* Display turned on. Restore if possible. */
- if (saved_dcvs_cpu0_slack_max != -1) {
- snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_max);
+ if (!on) {
+ /* Display off. */
+ if ((strlen(governor) == strlen("ondemand")) &&
+ (strncmp(governor, "ondemand", strlen("ondemand")) == 0)) {
+ } else if ((strlen(governor) == strlen("interactive")) &&
+ (strncmp(governor, "interactive", strlen("interactive")) == 0)) {
+ int resource_values[] = {TR_MS_500};
- if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) {
+ perform_hint_action(DISPLAY_STATE_HINT_ID,
+ resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
+ } else if ((strlen(governor) == strlen("msm-dcvs")) &&
+ (strncmp(governor, "msm-dcvs", strlen("msm-dcvs")) == 0)) {
+ if (saved_interactive_mode == 1){
+ /* Display turned off. */
+ if (sysfs_read(DCVS_CPU0_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) {
if (!slack_node_rw_failed) {
- ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE);
+ ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MAX_NODE);
}
rc = 1;
+ } else {
+ saved_dcvs_cpu0_slack_max = atoi(tmp_str);
}
- }
- if (saved_dcvs_cpu0_slack_max != -1) {
- snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_min);
-
- if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) {
+ if (sysfs_read(DCVS_CPU0_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) {
if (!slack_node_rw_failed) {
- ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE);
+ ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MIN_NODE);
}
rc = 1;
+ } else {
+ saved_dcvs_cpu0_slack_min = atoi(tmp_str);
}
- }
- if (saved_mpdecision_slack_max != -1) {
- snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_max);
-
- if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) {
+ if (sysfs_read(MPDECISION_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) {
if (!slack_node_rw_failed) {
- ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE);
+ ALOGE("Failed to read from %s", MPDECISION_SLACK_MAX_NODE);
}
rc = 1;
- }
- }
-
- if (saved_mpdecision_slack_min != -1) {
- snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_min);
-
- if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) {
- if (!slack_node_rw_failed) {
- ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE);
- }
-
- rc = 1;
- }
- }
- } else if (!on && saved_interactive_mode == 1){
- /* Display turned off. */
- if (sysfs_read(DCVS_CPU0_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) {
- if (!slack_node_rw_failed) {
- ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MAX_NODE);
+ } else {
+ saved_mpdecision_slack_max = atoi(tmp_str);
}
- rc = 1;
- } else {
- saved_dcvs_cpu0_slack_max = atoi(tmp_str);
- }
-
- if (sysfs_read(DCVS_CPU0_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) {
- if (!slack_node_rw_failed) {
- ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MIN_NODE);
- }
-
- rc = 1;
- } else {
- saved_dcvs_cpu0_slack_min = atoi(tmp_str);
- }
-
- if (sysfs_read(MPDECISION_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) {
- if (!slack_node_rw_failed) {
- ALOGE("Failed to read from %s", MPDECISION_SLACK_MAX_NODE);
- }
-
- rc = 1;
- } else {
- saved_mpdecision_slack_max = atoi(tmp_str);
- }
-
- if (sysfs_read(MPDECISION_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) {
- if(!slack_node_rw_failed) {
- ALOGE("Failed to read from %s", MPDECISION_SLACK_MIN_NODE);
- }
-
- rc = 1;
- } else {
- saved_mpdecision_slack_min = atoi(tmp_str);
- }
-
- /* Write new values. */
- if (saved_dcvs_cpu0_slack_max != -1) {
- snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_max);
-
- if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) {
- if (!slack_node_rw_failed) {
- ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE);
- }
-
- rc = 1;
- }
- }
-
- if (saved_dcvs_cpu0_slack_max != -1) {
- snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_min);
-
- if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) {
+ if (sysfs_read(MPDECISION_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) {
if(!slack_node_rw_failed) {
- ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE);
+ ALOGE("Failed to read from %s", MPDECISION_SLACK_MIN_NODE);
}
rc = 1;
+ } else {
+ saved_mpdecision_slack_min = atoi(tmp_str);
}
- }
- if (saved_mpdecision_slack_max != -1) {
- snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_max);
+ /* Write new values. */
+ if (saved_dcvs_cpu0_slack_max != -1) {
+ snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_max);
- if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) {
- if(!slack_node_rw_failed) {
- ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE);
+ if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) {
+ if (!slack_node_rw_failed) {
+ ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE);
+ }
+
+ rc = 1;
}
-
- rc = 1;
}
- }
- if (saved_mpdecision_slack_min != -1) {
- snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_min);
+ if (saved_dcvs_cpu0_slack_max != -1) {
+ snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_min);
- if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) {
- if(!slack_node_rw_failed) {
- ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE);
+ if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) {
+ if(!slack_node_rw_failed) {
+ ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE);
+ }
+
+ rc = 1;
}
+ }
- rc = 1;
+ if (saved_mpdecision_slack_max != -1) {
+ snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_max);
+
+ if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) {
+ if(!slack_node_rw_failed) {
+ ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE);
+ }
+
+ rc = 1;
+ }
+ }
+
+ if (saved_mpdecision_slack_min != -1) {
+ snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_min);
+
+ if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) {
+ if(!slack_node_rw_failed) {
+ ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE);
+ }
+
+ rc = 1;
+ }
}
}
+
+ slack_node_rw_failed = rc;
}
+ } else {
+ /* Display on. */
+ if ((strlen(governor) == strlen("ondemand")) &&
+ (strncmp(governor, "ondemand", strlen("ondemand")) == 0)) {
+ } else if ((strlen(governor) == strlen("interactive")) &&
+ (strncmp(governor, "interactive", strlen("interactive")) == 0)) {
+ undo_hint_action(DISPLAY_STATE_HINT_ID);
+ } else if ((strlen(governor) == strlen("msm-dcvs")) &&
+ (strncmp(governor, "mms-dcvs", strlen("msm-dcvs")) == 0)) {
+ if (saved_interactive_mode == -1 || saved_interactive_mode == 0) {
+ /* Display turned on. Restore if possible. */
+ if (saved_dcvs_cpu0_slack_max != -1) {
+ snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_max);
- slack_node_rw_failed = rc;
+ if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) {
+ if (!slack_node_rw_failed) {
+ ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE);
+ }
+
+ rc = 1;
+ }
+ }
+
+ if (saved_dcvs_cpu0_slack_max != -1) {
+ snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_min);
+
+ if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) {
+ if (!slack_node_rw_failed) {
+ ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE);
+ }
+
+ rc = 1;
+ }
+ }
+
+ if (saved_mpdecision_slack_max != -1) {
+ snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_max);
+
+ if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) {
+ if (!slack_node_rw_failed) {
+ ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE);
+ }
+
+ rc = 1;
+ }
+ }
+
+ if (saved_mpdecision_slack_min != -1) {
+ snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_min);
+
+ if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) {
+ if (!slack_node_rw_failed) {
+ ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE);
+ }
+
+ rc = 1;
+ }
+ }
+ }
+
+ slack_node_rw_failed = rc;
+ }
}
saved_interactive_mode = !!on;