summaryrefslogtreecommitdiff
path: root/io-module/buttons.c
diff options
context:
space:
mode:
Diffstat (limited to 'io-module/buttons.c')
-rw-r--r--io-module/buttons.c281
1 files changed, 281 insertions, 0 deletions
diff --git a/io-module/buttons.c b/io-module/buttons.c
new file mode 100644
index 0000000..e7ed9ac
--- /dev/null
+++ b/io-module/buttons.c
@@ -0,0 +1,281 @@
+
+#include "buttons.h"
+
+extern ssize_t mts_attr_show_gpio_pin(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+extern struct gpio_pin *gpio_pin_by_attr_name(const char *name);
+
+static button_info_pt *buttons = NULL;
+
+struct gpio_pin *gpio_pin_by_button_name(const char *button_name)
+{
+ button_info_pt *pbutton;
+
+ for (pbutton = buttons; *pbutton != NULL; pbutton++) {
+ if (!strcmp(pbutton[0]->name, button_name)) {
+ return gpio_pin_by_attr_name(pbutton[0]->label_pin);
+ }
+ }
+
+ log_error("Button named %s not found", button_name);
+ return NULL;
+}
+
+static button_info_t *button_by_monitor_name(const char *label_monitor)
+{
+ button_info_pt *pbutton;
+
+ for (pbutton = buttons; *pbutton != NULL; pbutton++) {
+ if (!strcmp(pbutton[0]->label_monitor, label_monitor)) {
+ return pbutton[0];
+ }
+ }
+
+ log_error("Button with %s monitor label is not found", label_monitor);
+ return NULL;
+}
+
+static button_info_t *button_by_monitor_intervals_name(const char *label_monitor_intervals)
+{
+ button_info_pt *pbutton;
+
+ for (pbutton = buttons; *pbutton != NULL; pbutton++) {
+ if (!strcmp(pbutton[0]->label_monitor_intervals, label_monitor_intervals)) {
+ return pbutton[0];
+ }
+ }
+
+ log_error("Button with %s monitor intervals label is not found", label_monitor_intervals);
+ return NULL;
+}
+
+ssize_t mts_attr_show_button_monitor_intervals(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int ret;
+
+ button_info_t *button = button_by_monitor_intervals_name(attr->attr.name);
+
+ if (!button) {
+ return -ENODEV;
+ }
+
+ mutex_lock(&mts_io_mutex);
+
+ ret = sprintf(buf, "%d %d\n", button->short_interval / BUTTON_CHECK_PER_SEC, button->long_interval / BUTTON_CHECK_PER_SEC);
+
+ mutex_unlock(&mts_io_mutex);
+
+ return ret;
+}
+
+ssize_t mts_attr_store_button_monitor_intervals(struct device *dev, struct device_attribute *attr, char *buf, size_t count)
+{
+ int short_int;
+ int long_int;
+ button_info_t *button = button_by_monitor_intervals_name(attr->attr.name);
+
+ if (sscanf(buf, "%i %i", &short_int, &long_int) != 2) {
+ return -EINVAL;
+ }
+
+ if (!button) {
+ return -ENODEV;
+ }
+
+ mutex_lock(&mts_io_mutex);
+
+ button->short_interval = short_int * BUTTON_CHECK_PER_SEC;
+ button->long_interval = long_int * BUTTON_CHECK_PER_SEC;
+
+ mutex_unlock(&mts_io_mutex);
+
+ return count;
+}
+
+ssize_t mts_attr_show_button_monitor(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+
+ button_info_t *button = button_by_monitor_name(attr->attr.name);
+
+ if (!button) {
+ return -ENODEV;
+ }
+
+ mutex_lock(&mts_io_mutex);
+
+ ret = sprintf(buf, "%d %d %d %d\n", button->pid, button->short_signal, button->long_signal, button->extra_long_signal);
+
+ mutex_unlock(&mts_io_mutex);
+
+ return ret;
+}
+
+ssize_t mts_attr_store_button_monitor(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ pid_t pid;
+ int short_signal;
+ int long_signal;
+ int extra_long_signal;
+ int result = sscanf(buf, "%i %i %i %i", &pid, &short_signal, &long_signal, &extra_long_signal);
+
+ button_info_t *button = button_by_monitor_name(attr->attr.name);
+
+ if (!button) {
+ return -ENODEV;
+ }
+
+ if (result < 3 || result > 4) {
+ return -EINVAL;
+ }
+
+ if(result == 3) {
+ mutex_lock(&mts_io_mutex);
+
+ button->pid = pid;
+ button->short_signal = short_signal;
+ button->long_signal = long_signal;
+
+ mutex_unlock(&mts_io_mutex);
+ } else {
+ mutex_lock(&mts_io_mutex);
+
+ button->pid = pid;
+ button->short_signal = short_signal;
+ button->long_signal = long_signal;
+ button->extra_long_signal = extra_long_signal;
+
+ mutex_unlock(&mts_io_mutex);
+ }
+
+ return count;
+}
+
+
+static void button_worker(struct work_struct *ignored);
+
+static DECLARE_DELAYED_WORK(button_work, button_worker);
+
+static void button_worker(struct work_struct *ignored)
+{
+ struct gpio_pin *pin;
+ struct pid *vpid;
+ int button_pressed = 0;
+
+ button_info_pt *pbutton;
+
+ mutex_lock(&mts_io_mutex);
+
+ for (pbutton = buttons; *pbutton != NULL; pbutton++) {
+ button_pressed = 0;
+ vpid = NULL;
+
+ pin = gpio_pin_by_button_name(pbutton[0]->name);
+
+ if (pin) {
+ button_pressed = !gpio_get_value(pin->pin.gpio);
+ }
+
+ if (pbutton[0]->pid > 0) {
+ vpid = find_vpid(pbutton[0]->pid);
+ }
+
+ if (vpid) {
+ if (button_pressed) {
+ pbutton[0]->pressed_count++;
+ }
+ else {
+ // The button is not pressed
+ if (pbutton[0]->pressed_count > 0 && pbutton[0]->pressed_count < pbutton[0]->short_interval) {
+ log_debug("Button %s short signal", pbutton[0]->name);
+ kill_pid(vpid, pbutton[0]->short_signal, 1);
+ } else if (pbutton[0]->pressed_count >= pbutton[0]->short_interval && pbutton[0]->pressed_count < pbutton[0]->long_interval) {
+ log_debug("Button %s long signal", pbutton[0]->name);
+ kill_pid(vpid, pbutton[0]->long_signal, 1);
+ }
+
+ pbutton[0]->pressed_count = 0;
+ pbutton[0]->sent_extra_long = false;
+ }
+ if (pbutton[0]->pressed_count >= pbutton[0]->long_interval && ! pbutton[0]->sent_extra_long) {
+ log_debug("Button %s extra long signal", pbutton[0]->name);
+ kill_pid(vpid, pbutton[0]->extra_long_signal, 1);
+ pbutton[0]->sent_extra_long = true;
+ }
+ } else {
+ pbutton[0]->pressed_count = 0;
+ }
+ }
+
+ mutex_unlock(&mts_io_mutex);
+
+ schedule_delayed_work(&button_work, BUTTON_INTERVAL);
+}
+
+int set_buttons (button_info_pt* platform_buttons) {
+ if (platform_buttons == NULL) {
+ log_error("Null pointer error");
+ return -EINVAL;
+ }
+ mutex_lock(&mts_io_mutex);
+ if (buttons != NULL) {
+ log_warning("Buttons structure was initialized more then once");
+ }
+ buttons = platform_buttons;
+ mutex_unlock(&mts_io_mutex);
+ return 0;
+}
+
+void init_buttons(void) {
+ if (buttons == NULL) {
+ log_error("Button structure hasn't been set yet");
+ return;
+ }
+ button_worker(NULL);
+}
+
+void cleanup_buttons(void) {
+ cancel_delayed_work_sync(&button_work);
+}
+
+// Reset button is common for all devices
+
+button_info_t reset_button = {
+ .name = "Reset Button",
+ .label_pin = "reset",
+ .label_monitor = "reset-monitor",
+ .label_monitor_intervals = "reset-monitor-intervals",
+
+ /* Signals */
+ .short_signal = SIGUSR1,
+ .long_signal = SIGUSR2,
+ .short_signal = SIGHUP,
+
+ /* Intervals */
+ .short_interval = BUTTON_HOLD_COUNT,
+ .long_interval = BUTTON_LONG_HOLD_COUNT,
+};
+
+DEVICE_ATTR_MTS(dev_attr_reset_monitor_intervals,
+ reset_button.label_monitor_intervals,
+ mts_attr_show_button_monitor_intervals,
+ mts_attr_store_button_monitor_intervals);
+
+DEVICE_ATTR_MTS(dev_attr_reset_monitor,
+ reset_button.label_monitor,
+ mts_attr_show_button_monitor,
+ mts_attr_store_button_monitor);
+
+DEVICE_ATTR_RO_MTS(dev_attr_reset,
+ reset_button.label_pin,
+ mts_attr_show_gpio_pin);
+
+
+button_info_pt default_buttons[] = {
+ &reset_button,
+ NULL,
+};