/**
* @file event-switches.c
* Switch event provider for the Mode Control Entity
*
* Copyright © 2007-2011 Nokia Corporation and/or its subsidiary(-ies).
* Copyright (C) 2014-2019 Jolla Ltd.
*
* @author David Weinehall
* @author Santtu Lakkala
* @author Simo Piiroinen
*
* mce is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* mce is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mce. If not, see .
*/
#include "event-switches.h"
#include "mce.h"
#include "mce-io.h"
#include
#include
#include
#include
/** ID for the lockkey I/O monitor */
static gconstpointer lockkey_iomon_id = NULL;
/** ID for the keyboard slide I/O monitor */
static gconstpointer kbd_slide_iomon_id = NULL;
/** ID for the cam focus I/O monitor */
static gconstpointer cam_focus_iomon_id = NULL;
/** Can the camera focus interrupt be disabled? */
static gboolean cam_focus_disable_exists = FALSE;
/** ID for the cam launch I/O monitor */
static gconstpointer cam_launch_iomon_id = NULL;
/** ID for the lid cover I/O monitor */
static gconstpointer lid_sensor_actual_iomon_id = NULL;
/** ID for the proximity sensor I/O monitor */
static gconstpointer proximity_sensor_iomon_id = NULL;
/** Can the proximity sensor interrupt be disabled? */
static gboolean proximity_sensor_disable_exists = FALSE;
/** ID for the MUSB OMAP3 usb cable I/O monitor */
static gconstpointer musb_omap3_usb_cable_iomon_id = NULL;
/** ID for the mmc0 cover I/O monitor */
static gconstpointer mmc0_cover_iomon_id = NULL;
/** ID for the mmc cover I/O monitor */
static gconstpointer mmc_cover_iomon_id = NULL;
/** ID for the lens cover I/O monitor */
static gconstpointer lens_cover_iomon_id = NULL;
/** ID for the battery cover I/O monitor */
static gconstpointer bat_cover_iomon_id = NULL;
/** Cached call state */
static call_state_t call_state = CALL_STATE_INVALID;
/** Cached alarm UI state */
static alarm_ui_state_t alarm_ui_state = MCE_ALARM_UI_INVALID_INT32;
/** Does the device have a flicker key? */
gboolean has_flicker_key = FALSE;
/**
* Generic I/O monitor callback that only generates activity
*
* @param data Unused
* @param bytes_read Unused
* @return Always returns FALSE to return remaining data (if any)
*/
static gboolean generic_activity_iomon_cb(mce_io_mon_t *iomon, gpointer data, gsize bytes_read)
{
(void)iomon;
(void)data;
(void)bytes_read;
/* Generate activity */
mce_datapipe_generate_activity();
return FALSE;
}
/**
* I/O monitor callback for the camera launch button
*
* @param data Unused
* @param bytes_read Unused
* @return Always returns FALSE to return remaining data (if any)
*/
static gboolean camera_launch_button_iomon_cb(mce_io_mon_t *iomon, gpointer data, gsize bytes_read)
{
camera_button_state_t camera_button_state;
(void)iomon;
(void)bytes_read;
if (!strncmp(data, MCE_CAM_LAUNCH_ACTIVE,
strlen(MCE_CAM_LAUNCH_ACTIVE))) {
camera_button_state = CAMERA_BUTTON_LAUNCH;
} else {
camera_button_state = CAMERA_BUTTON_UNPRESSED;
}
/* Generate activity */
mce_datapipe_generate_activity();
/* Update camera button state */
datapipe_exec_full(&camera_button_state_pipe,
GINT_TO_POINTER(camera_button_state));
return FALSE;
}
/**
* I/O monitor callback for the lock flicker key
*
* @param data The new data
* @param bytes_read Unused
* @return Always returns FALSE to return remaining data (if any)
*/
static gboolean lockkey_iomon_cb(mce_io_mon_t *iomon, gpointer data, gsize bytes_read)
{
key_state_t lockkey_state = KEY_STATE_RELEASED;
(void)iomon;
(void)bytes_read;
if (!strncmp(data, MCE_FLICKER_KEY_ACTIVE,
strlen(MCE_FLICKER_KEY_ACTIVE))) {
lockkey_state = KEY_STATE_PRESSED;
}
datapipe_exec_full(&lockkey_state_pipe,
GINT_TO_POINTER(lockkey_state));
return FALSE;
}
/**
* I/O monitor callback for the keyboard slide
*
* @param data The new data
* @param bytes_read Unused
* @return Always returns FALSE to return remaining data (if any)
*/
static gboolean kbd_slide_iomon_cb(mce_io_mon_t *iomon, gpointer data, gsize bytes_read)
{
cover_state_t slide_state;
(void)iomon;
(void)bytes_read;
if (!strncmp(data, MCE_KBD_SLIDE_OPEN, strlen(MCE_KBD_SLIDE_OPEN))) {
slide_state = COVER_OPEN;
/* Generate activity */
mce_datapipe_generate_activity();
} else {
slide_state = COVER_CLOSED;
}
datapipe_exec_full(&keyboard_slide_state_pipe,
GINT_TO_POINTER(slide_state));
return FALSE;
}
/**
* I/O monitor callback for the lid cover
*
* @param data The new data
* @param bytes_read Unused
* @return Always returns FALSE to return remaining data (if any)
*/
static gboolean lid_sensor_actual_iomon_cb(mce_io_mon_t *iomon, gpointer data, gsize bytes_read)
{
cover_state_t lid_state;
(void)iomon;
(void)bytes_read;
if (!strncmp(data, MCE_LID_COVER_OPEN, strlen(MCE_LID_COVER_OPEN))) {
lid_state = COVER_OPEN;
/* Generate activity */
mce_datapipe_generate_activity();
} else {
lid_state = COVER_CLOSED;
}
datapipe_exec_full(&lid_sensor_actual_pipe,
GINT_TO_POINTER(lid_state));
return FALSE;
}
/**
* I/O monitor callback for the proximity sensor
*
* @param data The new data
* @param bytes_read Unused
* @return Always returns FALSE to return remaining data (if any)
*/
static gboolean proximity_sensor_iomon_cb(mce_io_mon_t *iomon, gpointer data, gsize bytes_read)
{
cover_state_t proximity_sensor_actual;
(void)iomon;
(void)bytes_read;
if (!strncmp(data, MCE_PROXIMITY_SENSOR_OPEN,
strlen(MCE_PROXIMITY_SENSOR_OPEN))) {
proximity_sensor_actual = COVER_OPEN;
} else {
proximity_sensor_actual = COVER_CLOSED;
}
datapipe_exec_full(&proximity_sensor_actual_pipe,
GINT_TO_POINTER(proximity_sensor_actual));
return FALSE;
}
/**
* I/O monitor callback for the USB cable
*
* @param data The new data
* @param bytes_read Unused
* @return Always returns FALSE to return remaining data (if any)
*/
static gboolean usb_cable_iomon_cb(mce_io_mon_t *iomon, gpointer data, gsize bytes_read)
{
usb_cable_state_t cable_state;
(void)iomon;
(void)bytes_read;
if (!strncmp(data, MCE_MUSB_OMAP3_USB_CABLE_CONNECTED,
strlen(MCE_MUSB_OMAP3_USB_CABLE_CONNECTED))) {
cable_state = USB_CABLE_CONNECTED;
} else {
cable_state = USB_CABLE_DISCONNECTED;
}
/* Generate activity */
mce_datapipe_generate_activity();
datapipe_exec_full(&usb_cable_state_pipe,
GINT_TO_POINTER(cable_state));
return FALSE;
}
/**
* I/O monitor callback for the lens cover
*
* @param data The new data
* @param bytes_read Unused
* @return Always returns FALSE to return remaining data (if any)
*/
static gboolean lens_cover_iomon_cb(mce_io_mon_t *iomon, gpointer data, gsize bytes_read)
{
cover_state_t lens_cover_state;
(void)iomon;
(void)bytes_read;
if (!strncmp(data, MCE_LENS_COVER_OPEN, strlen(MCE_LENS_COVER_OPEN))) {
lens_cover_state = COVER_OPEN;
/* Generate activity */
mce_datapipe_generate_activity();
} else {
lens_cover_state = COVER_CLOSED;
}
datapipe_exec_full(&lens_cover_state_pipe,
GINT_TO_POINTER(lens_cover_state));
return FALSE;
}
/**
* Update the proximity state
*
* @note Only gives reasonable readings when the proximity sensor is enabled
* @return TRUE on success, FALSE on failure
*/
static gboolean update_proximity_sensor(void)
{
cover_state_t proximity_sensor_actual;
gboolean status = FALSE;
gchar *tmp = NULL;
if (mce_read_string_from_file(MCE_PROXIMITY_SENSOR_STATE_PATH,
&tmp) == FALSE) {
goto EXIT;
}
if (!strncmp(tmp, MCE_PROXIMITY_SENSOR_OPEN,
strlen(MCE_PROXIMITY_SENSOR_OPEN))) {
proximity_sensor_actual = COVER_OPEN;
} else {
proximity_sensor_actual = COVER_CLOSED;
}
datapipe_exec_full(&proximity_sensor_actual_pipe,
GINT_TO_POINTER(proximity_sensor_actual));
g_free(tmp);
EXIT:
return status;
}
/**
* Update the proximity monitoring
*/
static void update_proximity_monitor(void)
{
if (proximity_sensor_disable_exists == FALSE)
goto EXIT;
if ((call_state == CALL_STATE_RINGING) ||
(call_state == CALL_STATE_ACTIVE) ||
(alarm_ui_state == MCE_ALARM_UI_VISIBLE_INT32) ||
(alarm_ui_state == MCE_ALARM_UI_RINGING_INT32)) {
mce_write_string_to_file(MCE_PROXIMITY_SENSOR_DISABLE_PATH, "0");
update_proximity_sensor();
} else {
mce_write_string_to_file(MCE_PROXIMITY_SENSOR_DISABLE_PATH, "1");
}
EXIT:
return;
}
/**
* Handle call state change
*
* @param data The call state stored in a pointer
*/
static void call_state_trigger(gconstpointer const data)
{
call_state = GPOINTER_TO_INT(data);
update_proximity_monitor();
}
/**
* Handle alarm UI state change
*
* @param data The alarm state stored in a pointer
*/
static void alarm_ui_state_trigger(gconstpointer const data)
{
alarm_ui_state = GPOINTER_TO_INT(data);
update_proximity_monitor();
}
/**
* Handle submode change
*
* @param data The submode stored in a pointer
*/
static void submode_trigger(gconstpointer data)
{
static submode_t old_submode = MCE_SUBMODE_NORMAL;
submode_t submode = GPOINTER_TO_INT(data);
/* If the tklock is enabled, disable the camera focus interrupts,
* since we don't use them anyway
*/
if ((submode & MCE_SUBMODE_TKLOCK) != 0) {
if ((old_submode & MCE_SUBMODE_TKLOCK) == 0) {
if ((cam_focus_disable_exists == TRUE) &&
(cam_focus_iomon_id != NULL))
mce_write_string_to_file(MCE_CAM_FOCUS_DISABLE_PATH, "1");
}
} else {
if ((old_submode & MCE_SUBMODE_TKLOCK) != 0) {
if (cam_focus_disable_exists == TRUE)
mce_write_string_to_file(MCE_CAM_FOCUS_DISABLE_PATH, "0");
}
}
old_submode = submode;
}
/** List of active io monitors for switches */
static GSList *switch_iomon_list = NULL;
/** I/O monitor delete callback
*
* @param iomon io monitor that is about to get deleted
*/
static void mce_switches_rem_iomon_cb(mce_io_mon_t *iomon)
{
switch_iomon_list = g_slist_remove(switch_iomon_list, iomon);
}
/** Helper for adding io monitor for switch device
*
* @param path device path
* @param input_cb input handler callback
*
* @return io monitor id, or NULL in case of errors
*/
static gconstpointer mce_switches_add_iomon(const char *path, mce_io_mon_notify_cb input_cb)
{
mce_io_mon_t *iomon =
mce_io_mon_register_string(-1, path,
MCE_IO_ERROR_POLICY_IGNORE,
TRUE,
input_cb,
mce_switches_rem_iomon_cb);
if( iomon )
switch_iomon_list = g_slist_prepend(switch_iomon_list,
(gpointer)iomon);
return iomon;
}
/** Unregister all active io monitors for switches
*/
static void mce_switches_rem_iomon_all(void)
{
mce_io_mon_unregister_list(switch_iomon_list);
}
/** Array of datapipe handlers */
static datapipe_handler_t mce_switches_datapipe_handlers[] =
{
// input triggers
{
.datapipe = &call_state_pipe,
.input_cb = call_state_trigger,
},
{
.datapipe = &alarm_ui_state_pipe,
.input_cb = alarm_ui_state_trigger,
},
// output triggers
{
.datapipe = &submode_pipe,
.output_cb = submode_trigger,
},
// sentinel
{
.datapipe = 0,
}
};
static datapipe_bindings_t mce_switches_datapipe_bindings =
{
.module = "mce_switches",
.handlers = mce_switches_datapipe_handlers,
};
/** Append triggers/filters to datapipes
*/
static void mce_switches_datapipe_init(void)
{
mce_datapipe_init_bindings(&mce_switches_datapipe_bindings);
}
/** Remove triggers/filters from datapipes
*/
static void mce_switches_datapipe_quit(void)
{
mce_datapipe_quit_bindings(&mce_switches_datapipe_bindings);
}
/**
* Init function for the switches component
*
* @return TRUE on success, FALSE on failure
*/
gboolean mce_switches_init(void)
{
gboolean status = FALSE;
/* Append triggers/filters to datapipes */
mce_switches_datapipe_init();
/* Register I/O monitors */
lockkey_iomon_id =
mce_switches_add_iomon(MCE_FLICKER_KEY_STATE_PATH,
lockkey_iomon_cb);
kbd_slide_iomon_id =
mce_switches_add_iomon(MCE_KBD_SLIDE_STATE_PATH,
kbd_slide_iomon_cb);
cam_focus_iomon_id =
mce_switches_add_iomon(MCE_CAM_FOCUS_STATE_PATH,
generic_activity_iomon_cb);
cam_launch_iomon_id =
mce_switches_add_iomon(MCE_CAM_LAUNCH_STATE_PATH,
camera_launch_button_iomon_cb);
lid_sensor_actual_iomon_id =
mce_switches_add_iomon(MCE_LID_COVER_STATE_PATH,
lid_sensor_actual_iomon_cb);
proximity_sensor_iomon_id =
mce_switches_add_iomon(MCE_PROXIMITY_SENSOR_STATE_PATH,
proximity_sensor_iomon_cb);
musb_omap3_usb_cable_iomon_id =
mce_switches_add_iomon(MCE_MUSB_OMAP3_USB_CABLE_STATE_PATH,
usb_cable_iomon_cb);
lens_cover_iomon_id =
mce_switches_add_iomon(MCE_LENS_COVER_STATE_PATH,
lens_cover_iomon_cb);
mmc0_cover_iomon_id =
mce_switches_add_iomon(MCE_MMC0_COVER_STATE_PATH,
generic_activity_iomon_cb);
mmc_cover_iomon_id =
mce_switches_add_iomon(MCE_MMC_COVER_STATE_PATH,
generic_activity_iomon_cb);
bat_cover_iomon_id =
mce_switches_add_iomon(MCE_BATTERY_COVER_STATE_PATH,
generic_activity_iomon_cb);
update_proximity_monitor();
if (lockkey_iomon_id != NULL)
has_flicker_key = TRUE;
proximity_sensor_disable_exists =
(g_access(MCE_PROXIMITY_SENSOR_DISABLE_PATH, W_OK) == 0);
cam_focus_disable_exists =
(g_access(MCE_CAM_FOCUS_DISABLE_PATH, W_OK) == 0);
errno = 0;
status = TRUE;
return status;
}
/**
* Exit function for the switches component
*/
void mce_switches_exit(void)
{
/* Remove triggers/filters from datapipes */
mce_switches_datapipe_quit();
/* Unregister I/O monitors */
mce_switches_rem_iomon_all();
return;
}