linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [ANNOUNCE] System Inactivity Monitor v1.0
@ 2007-01-18 19:29 Alessandro Di Marco
  2007-01-19  7:38 ` Arjan van de Ven
                   ` (2 more replies)
  0 siblings, 3 replies; 46+ messages in thread
From: Alessandro Di Marco @ 2007-01-18 19:29 UTC (permalink / raw)
  To: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 501 bytes --]

Hi all,

this is a new 2.6.20 module implementing a user inactivity trigger. Basically
it acts as an event sniffer, issuing an ACPI event when no user activity is
detected for more than a certain amount of time. This event can be successively
grabbed and managed by an user-level daemon such as acpid, blanking the screen,
dimming the lcd-panel light à la mac, etc...

For the interested guys I have included a bash configuration helper (called
gentable) that covers the details.

Best,


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: System Inactivity Monitor v1.0 --]
[-- Type: text/x-patch, Size: 36146 bytes --]

diff -uN SIN/Makefile SIN.new/Makefile
--- SIN/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ SIN.new/Makefile	2007-01-18 19:20:59.000000000 +0100
@@ -0,0 +1,41 @@
+MODLPATH = kernel/drivers/char
+
+MODL = sinmod
+OBJS = sin.o procfs.o table.o input_enumerator.o acpi_enumerator.o
+
+SRCS := $(patsubst %.o,%.c,$(OBJS))
+HDRS := $(patsubst %.o,%.h,$(OBJS))
+CMDS := $(patsubst %.o,.%.o.cmd,$(OBJS))
+
+ifneq ($(KERNELRELEASE),)
+	EXTRA_CFLAGS := $(DEBUG)
+	obj-m := $(MODL).o
+	$(MODL)-objs := $(OBJS)
+else
+	KDIR := /lib/modules/$(shell uname -r)/build
+	PWD := $(shell pwd)
+
+all:	$(MODL).ko
+
+$(MODL).ko:	$(SRCS) $(HDRS)
+	@$(MAKE) -C $(KDIR) M=$(PWD) modules
+
+im:	$(MODL).ko
+	@sudo insmod $(MODL).ko
+
+rm:
+	@sudo rmmod $(MODL)
+
+rmf:
+	@sudo rmmod -f $(MODL)
+
+install:
+	@sudo $(MAKE) INSTALL_MOD_DIR=$(MODLPATH) -C $(KDIR) M=$(PWD) modules_install
+
+modules_install:
+	@$(MAKE) INSTALL_MOD_DIR=$(MODLPATH) -C $(KDIR) M=$(PWD) modules_install
+
+clean:
+	@$(MAKE) -C $(KDIR) M=$(PWD) clean
+	@rm -f Module.symvers
+endif
diff -uN SIN/acpi_enumerator.c SIN.new/acpi_enumerator.c
--- SIN/acpi_enumerator.c	1970-01-01 01:00:00.000000000 +0100
+++ SIN.new/acpi_enumerator.c	2007-01-18 19:20:53.000000000 +0100
@@ -0,0 +1,114 @@
+/*
+ *  Copyright (C) 2007 Alessandro Di Marco
+ */
+
+/*
+ *  This file is part of SIN.
+ *
+ *  SIN is free software; you can redistribute it and/or modify it under the
+ *  terms of the GNU General Public License as published by the Free Software
+ *  Foundation; version 2 of the License.
+ *
+ *  SIN 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 General Public License for more
+ *  details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with SIN; if not, write to the Free Software Foundation, Inc., 51 Franklin
+ *  St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+
+#include "acpi_enumerator.h"
+
+static struct acpi_handlers *ah;
+static int ahsize;
+
+static char *page;
+static size_t size;
+
+int get_handlers(void)
+{
+	return ahsize;
+}
+
+char *get_hardware_id(int handle)
+{
+	return ah[handle].hardware_id;
+}
+
+static int acpi_store(struct acpi_device *device, struct acpi_driver *driver)
+{
+	if (device->flags.hardware_id) {
+		strcpy(ah[ahsize++].hardware_id,
+		       acpi_device_hid(device));
+	}
+
+	return -ENOENT;
+}
+
+static int acpi_show(struct acpi_device *device, struct acpi_driver *driver)
+{
+	if (device->flags.hardware_id && size < PAGE_SIZE) {
+		int err;
+
+		err = snprintf(&page[size],
+			       PAGE_SIZE - size,
+			       "%d: %s [%s, %lx]\n",
+			       ahsize++,
+			       acpi_device_hid(device),
+			       acpi_device_bid(device),
+			       acpi_device_adr(device));
+
+		if (err >= PAGE_SIZE - size) {
+			err = PAGE_SIZE - size;
+		}
+
+		if (err > 0) {
+			size += err;
+		}
+	}
+
+	return -ENOENT;
+}
+
+int acpi_enum(char *buf)
+{
+	struct acpi_driver ad;
+
+	page = buf;
+
+	ad.ops.match = acpi_show;
+	(void) acpi_bus_register_driver(&ad);
+	acpi_bus_unregister_driver(&ad);
+
+	if (!ahsize) {
+		printk(KERN_NOTICE "no acpi handlers found\n");
+		return -ENODEV;
+	}
+
+	ah = kmalloc(ahsize * sizeof (struct acpi_handlers), GFP_KERNEL);
+
+	ahsize = 0;
+
+	if (!ah) {
+		return -ENOMEM;
+	}
+
+	ad.ops.match = acpi_store;
+	(void) acpi_bus_register_driver(&ad);
+	acpi_bus_unregister_driver(&ad);
+
+	return size;
+}
+
+void free_acpi_enum(void)
+{
+	if (ahsize) {
+		ahsize = 0;
+		kfree(ah);
+	}
+}
diff -uN SIN/acpi_enumerator.h SIN.new/acpi_enumerator.h
--- SIN/acpi_enumerator.h	1970-01-01 01:00:00.000000000 +0100
+++ SIN.new/acpi_enumerator.h	2007-01-18 19:20:53.000000000 +0100
@@ -0,0 +1,36 @@
+/*
+ *  Copyright (C) 2007 Alessandro Di Marco
+ */
+
+/*
+ *  This file is part of SIN.
+ *
+ *  SIN is free software; you can redistribute it and/or modify it under the
+ *  terms of the GNU General Public License as published by the Free Software
+ *  Foundation; version 2 of the License.
+ *
+ *  SIN 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 General Public License for more
+ *  details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with SIN; if not, write to the Free Software Foundation, Inc., 51 Franklin
+ *  St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef ACPI_ENUMERATOR_H
+#define ACPI_ENUMERATOR_H
+
+#include <acpi/acpi_bus.h>
+
+struct acpi_handlers {
+	acpi_hardware_id hardware_id;
+};
+
+extern int get_handlers(void);
+extern char *get_hardware_id(int handle);
+extern int acpi_enum(char *page);
+extern void free_acpi_enum(void);
+
+#endif /* ACPI_ENUMERATOR_H */
diff -uN SIN/gentable SIN.new/gentable
--- SIN/gentable	1970-01-01 01:00:00.000000000 +0100
+++ SIN.new/gentable	2007-01-18 19:21:06.000000000 +0100
@@ -0,0 +1,170 @@
+#!/bin/bash
+
+# This file is part of SIN.
+#
+# SIN is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; version 2 of the License.
+#
+# SIN 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# SIN; if not, write to the Free Software Foundation, Inc., 51 Franklin St,
+# Fifth Floor, Boston, MA 02110-1301 USA
+
+function input {
+    echo -n -e "$1 "
+    read -r $2
+}
+
+if (( $# == 0 )); then
+   echo "$0 <table>"
+   exit
+fi
+
+if [ ! -d "/proc/sin" ]; then
+    echo "/proc/sin not found, has sinmod been loaded?"
+    exit
+fi
+
+cat <<EOF
+
+SIN wakes up periodically and checks for user activity occurred in the
+meantime; this options lets you to specify how much frequently SIN should be
+woken-up. Its value is expressed in tenth of seconds.
+
+EOF
+
+input "Pace ticks?" pace
+
+if [ -z "${pace}" ]; then
+    pace="10"
+fi
+
+cat <<EOF
+
+Asleep or not, SIN constantly monitors the input devices searching for user
+activity. This option lets you choose which device have to be monitored. At
+least one device is needed and please avoid the duplicates.
+
+EOF
+
+echo -e "Specify the the input devices you want to monitor from the list below:\n"
+cat /proc/sin/input
+
+echo
+input "Please digit the corresponding numbers separated by spaces" devs
+
+if [ -z "${devs}" ]; then
+    devs="0"
+fi
+
+devices=(${devs})
+
+cat <<EOF
+
+SIN produces ACPI events depending on the user activity. To do this you have to
+specify a suitable handler that will be used as originator.
+
+EOF
+
+echo -e "Specify the acpi handler you want to use from the list below:\n"
+cat /proc/sin/acpi
+
+echo
+input "Please digit the corresponding number" handle
+
+if [ -z "${handle}" ]; then
+    handle="0"
+fi
+
+cat <<EOF
+
+SIN produces events in base to rules. Each rule is a triple composed by a
+"counter", a "type" and a "data". Once awaken, a global counter is increased if
+SIN detects no user activity and reset to zero, otherwise. When this global
+counter reaches the value specified in the counter field of a rule, an event is
+generated with the corresponding "type" and "data". Clearly, different rules
+should have different "type" and "data" fields to convey different signals to
+the user space daemon.
+
+For example, the rule "60 1 19" produces the ACPI event "XXXX 00000001
+00000019" right after one minute of user inactivity (assuming pace=10.)
+
+Please specify each rule as a space-separated triple; to finish just press
+enter.
+
+EOF
+
+for (( i = 0; ; i++ )); do
+    input "Rule ${i}?" rule
+
+    if [ -z "${rule}" ] ; then
+	break;
+    fi
+
+    rules[${i}]=${rule}
+done
+
+if (( ${i} == 0 )); then
+    rules[0]="60 1 2"
+fi
+
+cat <<EOF
+
+A special event has been provided to better help those of us who wanna use SIN
+as a screen-blanker. It will be generated as soon as some user activity is
+detected, but only after one or more rule have been triggered.
+
+EOF
+
+input "Special event \"type\" and \"data\"?" resume
+
+if [ -z "${resume}" ]; then
+    resume="2 1"
+fi
+
+cat <<EOF
+
+Usually a rule-list terminates with dead-end actions such as suspend or
+hibernate, requiring the user interaction to wake-up the system. Unfortunately
+this activity occurs when SIN, as well as the kernel, are not ready to capture
+these events yet. As a consequence, no special event will ever be generated and
+the system will remain in the state associated with the next-to-last rule
+(e.g. blanked screen, wireless powered off, etc.) In such cases this field
+forces the special event generation, resetting simultaneously the global
+counter to an arbitrary value, so to reinstate the rule-list evaluation to the
+beginning. Possible value ranges are described below, where N is the maximum
+counter in the rule list:
+
+    [0, N]    => reset the global counter to the specified value
+    otherwise => do nothing, the global counter goes on and on and on...
+
+EOF
+
+input "Reset value?" reset
+
+if [ -z "${reset}" ]; then
+    reset="-1"
+fi
+
+echo -e "0\n${pace}\n${#devices[@]} ${#rules[@]}\n${devices[@]}\n${handle}\n${reset}\n${resume}" > $1
+
+for (( i = 0; ${i}<${#rules[@]}; i++ )); do
+    echo "${rules[${i}]}" >> $1
+done
+
+cat <<EOF
+
+All done. Now you can try your newly generated table as follows:
+
+# modprobe sinmod
+# echo $1 >/proc/sin/table
+
+An "Invalid argument" error signals a mismatch in the table file, usually due
+to wrong acpi or input device specified. In these cases restart from scratch
+double checking your answers. Have fun!
+
+EOF
diff -uN SIN/input_enumerator.c SIN.new/input_enumerator.c
--- SIN/input_enumerator.c	1970-01-01 01:00:00.000000000 +0100
+++ SIN.new/input_enumerator.c	2007-01-18 19:20:53.000000000 +0100
@@ -0,0 +1,180 @@
+/*
+ *  Copyright (C) 2007 Alessandro Di Marco
+ */
+
+/*
+ *  This file is part of SIN.
+ *
+ *  SIN is free software; you can redistribute it and/or modify it under the
+ *  terms of the GNU General Public License as published by the Free Software
+ *  Foundation; version 2 of the License.
+ *
+ *  SIN 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 General Public License for more
+ *  details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with SIN; if not, write to the Free Software Foundation, Inc., 51 Franklin
+ *  St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+
+#include "input_enumerator.h"
+
+static struct input_devices *id;
+static int idsize;
+
+static char *page;
+static size_t size;
+
+int get_devices(void)
+{
+	return idsize;
+}
+
+void fill_input_device(struct input_device_id *idi, int device)
+{
+	idi->flags = MATCH_MODEL;
+
+	idi->bustype = id[device].bustype;
+	idi->vendor = id[device].vendor;
+	idi->product = id[device].product;
+	idi->version = id[device].version;
+}
+
+static struct input_handle *input_store(struct input_handler *handler,
+					struct input_dev *dev,
+					const struct input_device_id *idi)
+{
+	if (dev->name || dev->phys || dev->uniq) {
+		struct input_devices *idev = &id[idsize++];
+
+		idev->bustype = dev->id.bustype;
+		idev->vendor = dev->id.vendor;
+		idev->product = dev->id.product;
+		idev->version = dev->id.version;
+	}
+
+	return NULL;
+}
+
+static struct input_handle *input_show(struct input_handler *handler,
+				       struct input_dev *dev,
+				       const struct input_device_id *idi)
+{
+	const int left = PAGE_SIZE - size;
+
+	int flags = 0;
+	int err;
+
+	flags |= dev->name ? 1 : 0;
+	flags |= dev->phys ? 2 : 0;
+	flags |= dev->uniq ? 4 : 0;
+
+	switch (flags) {
+	case 7:
+		err = snprintf(&page[size], left,
+			       "%d: %s [%s #%s]\n",
+			       idsize, dev->name, dev->phys, dev->uniq);
+		break;
+
+	case 6:
+		err = snprintf(&page[size], left,
+			       "%d: [%s #%s]\n",
+			       idsize, dev->phys, dev->uniq);
+		break;
+
+	case 5:
+		err = snprintf(&page[size], left,
+			       "%d: %s [#%s]\n",
+			       idsize, dev->name, dev->uniq);
+		break;
+
+	case 4:
+		err = snprintf(&page[size], left,
+			       "%d: [#%s]\n",
+			       idsize, dev->uniq);
+		break;
+
+	case 3:
+		err = snprintf(&page[size], left,
+			       "%d: %s [%s]\n",
+			       idsize, dev->name, dev->phys);
+		break;
+
+	case 2:
+		err = snprintf(&page[size], left,
+			       "%d: [%s]\n",
+			       idsize, dev->phys);
+		break;
+
+	case 1:
+		err = snprintf(&page[size], left,
+			       "%d: %s\n",
+			       idsize, dev->name);
+		break;
+
+	default:
+		goto skip;
+	}
+
+	idsize++;
+
+	if (err >= left) {
+		err = left;
+	}
+
+	if (err > 0) {
+		size += err;
+	}
+
+skip:
+	return NULL;
+}
+
+int input_enum(char *buf)
+{
+	const struct input_device_id idi[] = {
+		{ .driver_info = 1 },	/* matches all devices */
+		{ },
+	};
+
+	struct input_handler ih = {
+		.name =		"input enumerator",
+		.id_table =	idi,
+	};
+
+	page = buf;
+
+	ih.connect = input_show;
+	(void) input_register_handler(&ih);
+
+	if (!idsize) {
+		printk(KERN_NOTICE "no input devices found\n");
+		return -ENODEV;
+	}
+
+	id = kmalloc(idsize * sizeof (struct input_devices), GFP_KERNEL);
+
+	idsize = 0;
+
+	if (!id) {
+		return -ENOMEM;
+	}
+
+	ih.connect = input_store;
+	(void) input_register_handler(&ih);
+
+	return size;
+}
+
+void free_input_enum(void)
+{
+	if (idsize) {
+		idsize = 0;
+		kfree(id);
+	}
+}
diff -uN SIN/input_enumerator.h SIN.new/input_enumerator.h
--- SIN/input_enumerator.h	1970-01-01 01:00:00.000000000 +0100
+++ SIN.new/input_enumerator.h	2007-01-18 19:20:53.000000000 +0100
@@ -0,0 +1,45 @@
+/*
+ *  Copyright (C) 2007 Alessandro Di Marco
+ */
+
+/*
+ *  This file is part of SIN.
+ *
+ *  SIN is free software; you can redistribute it and/or modify it under the
+ *  terms of the GNU General Public License as published by the Free Software
+ *  Foundation; version 2 of the License.
+ *
+ *  SIN 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 General Public License for more
+ *  details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with SIN; if not, write to the Free Software Foundation, Inc., 51 Franklin
+ *  St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef INPUT_ENUMERATOR_H
+#define INPUT_ENUMERATOR_H
+
+#include <linux/input.h>
+
+struct input_devices {
+	__u16 bustype;
+	__u16 vendor;
+	__u16 product;
+	__u16 version;
+};
+
+#define MATCH_MODEL (INPUT_DEVICE_ID_MATCH_BUS		\
+		     |INPUT_DEVICE_ID_MATCH_VENDOR	\
+		     |INPUT_DEVICE_ID_MATCH_PRODUCT	\
+		     |INPUT_DEVICE_ID_MATCH_VERSION)
+
+extern int get_devices(void);
+extern void fill_input_device(struct input_device_id *idi, int i);
+
+extern int input_enum(char *page);
+extern void free_input_enum(void);
+
+#endif /* INPUT_ENUMERATOR_H */
diff -uN SIN/procfs.c SIN.new/procfs.c
--- SIN/procfs.c	1970-01-01 01:00:00.000000000 +0100
+++ SIN.new/procfs.c	2007-01-18 19:20:53.000000000 +0100
@@ -0,0 +1,215 @@
+/*
+ *  Copyright (C) 2007 Alessandro Di Marco
+ */
+
+/*
+ *  This file is part of SIN.
+ *
+ *  SIN is free software; you can redistribute it and/or modify it under the
+ *  terms of the GNU General Public License as published by the Free Software
+ *  Foundation; version 2 of the License.
+ *
+ *  SIN 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 General Public License for more
+ *  details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with SIN; if not, write to the Free Software Foundation, Inc., 51 Franklin
+ *  St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+
+#include "sin.h"
+#include "table.h"
+#include "procfs.h"
+#include "acpi_enumerator.h"
+#include "input_enumerator.h"
+
+static struct procfs_bs ibs, abs, tbs;
+static struct proc_dir_entry *rootdir;
+
+static int read_proc(char *page, char **start,
+		     off_t off, int count, int *eof, void *data)
+{
+	struct procfs_bs *bs = data;
+	int left = bs->size - off;
+
+	if (count > left) {
+		count = left;
+	}
+
+	memcpy(page + off, bs->page, count);
+
+	if (!count) {
+		*eof = 1;
+	}
+
+	return off + count;
+}
+
+static int write_proc(struct file *file, const __user char *ubuf,
+		      unsigned long count, void *data)
+{
+	struct procfs_bs *bs = data;
+	char *buf;
+	int err;
+
+	buf = kmalloc(count * sizeof (char), GFP_KERNEL);
+	if (!buf) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	if (copy_from_user(buf, ubuf, count + 1)) {
+		err = -EFAULT;
+		goto cleanout;
+	}
+
+	buf[count] = '\0';
+
+	err = push_table(buf, count);
+	if (err < 0) {
+		goto cleanout;
+	}
+
+	kfree(bs->page);
+
+	bs->size = pull_table(&bs->page);
+	if (bs->size < 0) {
+		err = bs->size;
+	}
+
+cleanout:
+	kfree(buf);
+out:
+	return err;
+}
+
+int start_procfs(void)
+{
+	struct proc_dir_entry *inputd, *acpid, *table, *ilink, *alink;
+
+	int err = -ENOMEM;
+
+	ibs.page = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!ibs.page) {
+		goto out;
+	}
+
+	ibs.size = input_enum(ibs.page);
+	if (ibs.size < 0) {
+		err = ibs.size;
+		goto cleanout1;
+	}
+
+	abs.page = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!abs.page) {
+		goto cleanout2;
+	}
+
+	abs.size = acpi_enum(abs.page);
+	if (abs.size < 0) {
+		err = abs.size;
+		goto cleanout3;
+	}
+
+	tbs.size = sizeof RT_HELP;
+
+	tbs.page = kmalloc(tbs.size, GFP_KERNEL);
+	if (!tbs.page) {
+		goto cleanout4;
+	}
+
+	memcpy(tbs.page, RT_HELP, tbs.size);
+
+	rootdir = proc_mkdir(MODULE_NAME, NULL);
+	if (!rootdir) {
+		goto cleanout5;
+	}
+
+	rootdir->owner = THIS_MODULE;
+
+	inputd = create_proc_read_entry("input", 0444,
+					rootdir, read_proc, &ibs);
+	if (!inputd) {
+		goto cleanout6;
+	}
+
+	inputd->owner = THIS_MODULE;
+
+	acpid = create_proc_read_entry("acpi", 0444,
+				       rootdir, read_proc, &abs);
+	if (!acpid) {
+		goto cleanout7;
+	}
+
+	acpid->owner = THIS_MODULE;
+
+	table = create_proc_entry("table", 0644, rootdir);
+	if (!table) {
+		goto cleanout8;
+	}
+
+	table->data = &tbs;
+	table->read_proc = read_proc;
+	table->write_proc = write_proc;
+	table->owner = THIS_MODULE;
+
+	ilink = proc_symlink("sources", rootdir, "input");
+	if (!ilink) {
+		goto cleanout9;
+	}
+
+	ilink->owner = THIS_MODULE;
+
+	alink = proc_symlink("destinations", rootdir, "acpi");
+	if (!alink) {
+		goto cleanout10;
+	}
+
+	alink->owner = THIS_MODULE;
+
+	return 0;
+
+cleanout10:
+	remove_proc_entry("sources", rootdir);
+cleanout9:
+	remove_proc_entry("table", rootdir);
+cleanout8:
+	remove_proc_entry("acpi", rootdir);
+cleanout7:
+	remove_proc_entry("input", rootdir);
+cleanout6:
+	remove_proc_entry(MODULE_NAME, NULL);
+cleanout5:
+	kfree(tbs.page);
+cleanout4:
+	free_acpi_enum();
+cleanout3:
+	kfree(abs.page);
+cleanout2:
+	free_input_enum();
+cleanout1:
+	kfree(ibs.page);
+out:
+	return err;
+}
+
+void stop_procfs(void)
+{
+	remove_proc_entry("destinations", rootdir);
+	remove_proc_entry("sources", rootdir);
+	remove_proc_entry("table", rootdir);
+	remove_proc_entry("acpi", rootdir);
+	remove_proc_entry("input", rootdir);
+	remove_proc_entry(MODULE_NAME, NULL);
+	kfree(tbs.page);
+	free_acpi_enum();
+	kfree(abs.page);
+	free_input_enum();
+	kfree(ibs.page);
+}
diff -uN SIN/procfs.h SIN.new/procfs.h
--- SIN/procfs.h	1970-01-01 01:00:00.000000000 +0100
+++ SIN.new/procfs.h	2007-01-18 19:20:53.000000000 +0100
@@ -0,0 +1,35 @@
+/*
+ *  Copyright (C) 2007 Alessandro Di Marco
+ */
+
+/*
+ *  This file is part of Procfs.
+ *
+ *  Procfs is free software; you can redistribute it and/or modify it under the
+ *  terms of the GNU General Public License as published by the Free Software
+ *  Foundation; version 2 of the License.
+ *
+ *  Procfs 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 General Public License for more
+ *  details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with Procfs; if not, write to the Free Software Foundation, Inc., 51
+ *  Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef PROCFS_H
+#define PROCFS_H
+
+#define RT_HELP "<debug>\n<pace>\n<N> <M>\n<input1> ... <inputN>\n<acpi>\n<reset>\n<special type> <special data>\n<counter1> <type1> <data1>\n...\n<counterM> <typeM> <dataM>\n"
+
+struct procfs_bs {
+	char *page;
+	int size;
+};
+
+extern int start_procfs(void);
+extern void stop_procfs(void);
+
+#endif /* PROCFS_H */
diff -uN SIN/sin.c SIN.new/sin.c
--- SIN/sin.c	1970-01-01 01:00:00.000000000 +0100
+++ SIN.new/sin.c	2007-01-18 19:20:53.000000000 +0100
@@ -0,0 +1,190 @@
+/*
+ *  Copyright (C) 2007 Alessandro Di Marco
+ */
+
+/*
+ *  This file is part of SIN.
+ *
+ *  SIN is free software; you can redistribute it and/or modify it under the
+ *  terms of the GNU General Public License as published by the Free Software
+ *  Foundation; version 2 of the License.
+ *
+ *  SIN 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 General Public License for more
+ *  details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with SIN; if not, write to the Free Software Foundation, Inc., 51 Franklin
+ *  St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/acpi.h>
+#include <linux/mutex.h>
+
+#include "sin.h"
+#include "table.h"
+#include "procfs.h"
+
+MODULE_AUTHOR("Alessandro Di Marco <dmr@c0nc3pt.com>");
+MODULE_DESCRIPTION("System Inactivity Notifier");
+MODULE_LICENSE("GPL v2");
+
+MODULE_ALIAS("blanker");
+
+MODULE_VERSION("1.0");
+
+static struct acpi_device *acpi_device;
+
+static atomic_t interactions;
+static unsigned long notify;
+
+static struct timer_list timer;
+static int shutdown;
+
+static struct input_handler ih;
+
+static DEFINE_MUTEX(runlock);
+static int running;
+
+static void event(struct input_handle *handle,
+		  unsigned int type, unsigned int code, int value)
+{
+	if (unlikely(test_and_clear_bit(0, &notify))) {
+		clear_bit(1, &notify);
+		occasionally_generate_event(acpi_device);
+	}
+
+	atomic_inc(&interactions);
+}
+
+static struct input_handle *connect(struct input_handler *handler,
+				    struct input_dev *dev,
+				    const struct input_device_id *id)
+{
+	struct input_handle *handle;
+
+	if (!(handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL))) {
+		return NULL;
+	}
+
+	handle->handler = handler;
+	handle->dev = dev;
+	handle->name = MODULE_NAME;
+
+	input_open_device(handle);
+
+	return handle;
+}
+
+static void disconnect(struct input_handle *handle)
+{
+	input_close_device(handle);
+	kfree(handle);
+}
+
+void timer_fn(unsigned long pace)
+{
+	if (!shutdown) {
+		if (unlikely(test_and_clear_bit(1, &notify) &&
+			     test_and_clear_bit(0, &notify))) {
+			occasionally_generate_event(acpi_device);
+		}
+
+		timely_generate_event(acpi_device,
+				      atomic_read(&interactions), &notify);
+
+		atomic_set(&interactions, 0);
+
+		timer.expires = jiffies + pace;
+		add_timer(&timer);
+	}
+}
+
+static int acpi_match(struct acpi_device *device, struct acpi_driver *driver)
+{
+	if (device->flags.hardware_id &&
+	    strstr(driver->ids, device->pnp.hardware_id)) {
+		acpi_device = device;
+	}
+
+	return -ENOENT;
+}
+
+int start_monitor(char *ids, struct input_device_id *idi, unsigned long pace)
+{
+	struct acpi_driver ad = {
+		.ids = ids,
+		.ops = { .match = acpi_match }
+	};
+
+	int err;
+
+	mutex_lock(&runlock);
+
+	atomic_set(&interactions, 0);
+	notify = 0;
+
+	if (acpi_bus_register_driver(&ad) < 0 || !acpi_device) {
+		printk("couldn't find system ACPI device\n");
+		return -ENODEV;
+	}
+
+	acpi_bus_unregister_driver(&ad);
+
+	ih.event = event;
+	ih.connect = connect;
+	ih.disconnect =	disconnect;
+	ih.name = MODULE_NAME;
+	ih.id_table = idi;
+
+	err = input_register_handler(&ih);
+	if  (err < 0) {
+		return err;
+	}
+
+	setup_timer(&timer, timer_fn, pace);
+
+	timer.expires = jiffies + pace;
+
+	shutdown = 0;
+	add_timer(&timer);
+
+	running = 1;
+
+	mutex_unlock(&runlock);
+
+	return 0;
+}
+
+void stop_monitor(void)
+{
+	mutex_lock(&runlock);
+
+	if (running) {
+		shutdown = 1;
+		del_timer_sync(&timer);
+		input_unregister_handler(&ih);
+		kfree(ih.id_table);
+		running = 0;
+	}
+
+	mutex_unlock(&runlock);
+}
+
+static int __init sih_init(void)
+{
+	printk("System Inactivity Notifier 1.0 - (c) Alessandro Di Marco <dmr@c0nc3pt.com>\n");
+	return start_procfs();
+}
+
+static void __exit sih_exit(void)
+{
+	stop_procfs();
+	stop_monitor();
+}
+
+module_init(sih_init);
+module_exit(sih_exit);
diff -uN SIN/sin.h SIN.new/sin.h
--- SIN/sin.h	1970-01-01 01:00:00.000000000 +0100
+++ SIN.new/sin.h	2007-01-18 19:20:53.000000000 +0100
@@ -0,0 +1,32 @@
+/*
+ *  Copyright (C) 2007 Alessandro Di Marco
+ */
+
+/*
+ *  This file is part of SIN.
+ *
+ *  SIN is free software; you can redistribute it and/or modify it under the
+ *  terms of the GNU General Public License as published by the Free Software
+ *  Foundation; version 2 of the License.
+ *
+ *  SIN 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 General Public License for more
+ *  details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with SIN; if not, write to the Free Software Foundation, Inc., 51 Franklin
+ *  St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SIN_H
+#define SIN_H
+
+#include <linux/input.h>
+
+#define MODULE_NAME "sin"
+
+extern int start_monitor(char *ids, struct input_device_id *idi, unsigned long pace);
+extern void stop_monitor(void);
+
+#endif /* SIN_H */
diff -uN SIN/table.c SIN.new/table.c
--- SIN/table.c	1970-01-01 01:00:00.000000000 +0100
+++ SIN.new/table.c	2007-01-18 19:20:53.000000000 +0100
@@ -0,0 +1,274 @@
+/*
+ *  Copyright (C) 2007 Alessandro Di Marco
+ */
+
+/*
+ *  This file is part of SIN.
+ *
+ *  SIN is free software; you can redistribute it and/or modify it under the
+ *  terms of the GNU General Public License as published by the Free Software
+ *  Foundation; version 2 of the License.
+ *
+ *  SIN 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 General Public License for more
+ *  details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with SIN; if not, write to the Free Software Foundation, Inc., 51 Franklin
+ *  St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/acpi.h>
+#include <linux/sort.h>
+
+#include "sin.h"
+#include "uniq.h"
+#include "table.h"
+
+#include "input_enumerator.h"
+#include "acpi_enumerator.h"
+
+static struct table rt;
+static int debug;
+
+/*
+ * WARNING: sonypi, buttons and others issue a spurious event when removed from
+ * the system, fooling the event counter. Make sure to _not_ remove them in
+ * event service routines (e.g. /etc/acpid/default.sh).
+ */
+
+void occasionally_generate_event(struct acpi_device *acpi_device)
+{
+	if (unlikely(debug)) {
+		printk("generating special event [%d, %d]\n",
+		       rt.rules[rt.rnum].type, rt.rules[rt.rnum].data);
+	}
+
+	(void) acpi_bus_generate_event(acpi_device, rt.rules[rt.rnum].type,
+				       rt.rules[rt.rnum].data);
+}
+
+void timely_generate_event(struct acpi_device *acpi_device,
+			   int interactions, unsigned long *notify)
+{
+	static int counter, action;
+
+	if (interactions && counter) {
+		if (unlikely(debug)) {
+			printk("user activity detected, counter reset!\n");
+		}
+
+		counter = action = 0;
+	}
+
+	if (unlikely(debug)) {
+		printk("global counter %d, next rule is [%d %d %d]\n",
+		       counter,
+		       rt.rules[action].counter,
+		       rt.rules[action].type,
+		       rt.rules[action].data);
+	}
+
+	while (action < rt.rnum && rt.rules[action].counter == counter) {
+		if (unlikely(debug)) {
+			printk("generating event [%d, %d]\n",
+			       rt.rules[action].type,
+			       rt.rules[action].data);
+		}
+
+		(void) acpi_bus_generate_event(acpi_device,
+					       rt.rules[action].type,
+					       rt.rules[action].data);
+		action++;
+		set_bit(0, notify);
+	}
+
+	if (rt.raction >= 0 && action == rt.rnum) {
+		if (unlikely(debug)) {
+			printk("last rule reached, restarting from %d\n",
+			       rt.rcounter);
+		}
+
+		counter = rt.rcounter;
+		action = rt.raction;
+		set_bit(1, notify);
+
+	} else {
+		counter++;
+	}
+}
+
+#define parse_num(endp) ({				\
+			char *cp = endp;		\
+							\
+			while (*cp && isspace(*cp)) {	\
+				++cp;			\
+			}				\
+							\
+			simple_strtol(cp, &endp, 10);	\
+		})
+
+static int cmp(const void *l, const void *r)
+{
+	int lc = ((struct rule *) l)->counter;
+	int rc = ((struct rule *) r)->counter;
+
+	return lc < rc ? -1 : lc > rc ? 1 : 0;
+}
+
+static void swap(void *l, void *r, int size)
+{
+	struct rule t = *((struct rule *) l);
+
+	*((struct rule *) l) = *((struct rule *) r);
+	*((struct rule *) r) = t;
+}
+
+int push_table(char *buf, unsigned long count)
+{
+	struct input_device_id *idi;
+	struct uniq uniq;
+	int devices;
+
+	int i, err = -ENOMEM;
+
+	devices = get_devices();
+
+	debug = parse_num(buf);
+
+	rt.pace = (parse_num(buf) * HZ) / 10;
+	rt.dnum = parse_num(buf);
+	rt.rnum = parse_num(buf);
+
+	if (out_of_range(1, rt.pace, 1000000) ||
+	    out_of_range(0, rt.dnum, devices)) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	rt.devices = kmalloc(rt.dnum * sizeof (int), GFP_KERNEL);
+	if (!rt.devices) {
+		goto out;
+	}
+
+	rt.rules = kmalloc((rt.rnum + 1) * sizeof (struct rule), GFP_KERNEL);
+	if (!rt.rules) {
+		goto cleanout1;
+	}
+
+	if (uniq_alloc(&uniq, devices) < 0) {
+		goto cleanout2;
+	}
+
+	for (i = 0; i < rt.dnum; i++) {
+		rt.devices[i] = parse_num(buf);
+		if (uniq_check(&uniq, rt.devices[i])) {
+			break;
+		}
+	}
+
+	uniq_free(&uniq);
+
+	if (i < rt.dnum) {
+		err = -EINVAL;
+		goto cleanout2;
+	}
+
+	rt.handle = parse_num(buf);
+	if (out_of_range(0, rt.handle, get_handlers())) {
+		err = -EINVAL;
+		goto cleanout2;
+	}
+
+	rt.rcounter = parse_num(buf);
+
+	rt.rules[rt.rnum].counter = -1;
+	rt.rules[rt.rnum].type = parse_num(buf);
+	rt.rules[rt.rnum].data = parse_num(buf);
+
+	for (i = 0; i < rt.rnum; i++) {
+		rt.rules[i].counter = parse_num(buf);
+		if (rt.rules[i].counter < 0) {
+			err = -EINVAL;
+			goto cleanout2;
+		}
+
+		rt.rules[i].type = parse_num(buf);
+		rt.rules[i].data = parse_num(buf);
+	}
+
+	sort(rt.rules, rt.rnum, sizeof (struct rule), cmp, swap);
+
+	rt.raction = -1;
+
+	if (rt.rcounter >= 0) {
+		for (i = 0; i < rt.rnum; i++) {
+			if (rt.rules[i].counter >= rt.rcounter) {
+				rt.raction = i;
+				break;
+			}
+		}
+	}
+
+	stop_monitor();
+
+	idi = kzalloc((rt.dnum + 1) *
+		      sizeof (struct input_device_id), GFP_KERNEL);
+	if (!idi) {
+		goto cleanout2;
+	}
+
+	for (i = 0; i < rt.dnum; i++) {
+		fill_input_device(&idi[i], rt.devices[i]);
+	}
+
+	err = start_monitor(get_hardware_id(rt.handle), idi, rt.pace);
+	if (err < 0) {
+		goto cleanout3;
+	}
+
+	return count;
+
+cleanout3:
+	kfree(idi);
+cleanout2:
+	kfree(rt.rules);
+cleanout1:
+	kfree(rt.devices);
+out:
+	return err;
+}
+
+int pull_table(char **buf)
+{
+	char *b;
+	int i;
+
+	*buf = b = kmalloc(TABLE_BUFFER_SIZE, GFP_KERNEL);
+	if (!b) {
+		return -EFAULT;
+	}
+
+	b += sprintf(b, "%d\n%lu\n%d %d\n", debug,
+		     (rt.pace * 10) / HZ, rt.dnum, rt.rnum);
+
+	for (i = 0; i < rt.dnum; i++) {
+		b += sprintf(b, "%d ", rt.devices[i]);
+	}
+
+	b--;
+
+	b += sprintf(b, "\n%d\n%d\n%d %d\n",
+		     rt.handle, rt.rcounter,
+		     rt.rules[rt.rnum].type, rt.rules[rt.rnum].data);
+
+	for (i = 0; i < rt.rnum; i++) {
+		b += sprintf(b, "%d %d %d\n", rt.rules[i].counter,
+			     rt.rules[i].type, rt.rules[i].data);
+	}
+
+	return b - *buf;
+}
diff -uN SIN/table.h SIN.new/table.h
--- SIN/table.h	1970-01-01 01:00:00.000000000 +0100
+++ SIN.new/table.h	2007-01-18 19:20:53.000000000 +0100
@@ -0,0 +1,54 @@
+/*
+ *  Copyright (C) 2007 Alessandro Di Marco
+ */
+
+/*
+ *  This file is part of SIN.
+ *
+ *  SIN is free software; you can redistribute it and/or modify it under the
+ *  terms of the GNU General Public License as published by the Free Software
+ *  Foundation; version 2 of the License.
+ *
+ *  SIN 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 General Public License for more
+ *  details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with SIN; if not, write to the Free Software Foundation, Inc., 51 Franklin
+ *  St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef TABLE_H
+#define TABLE_H
+
+#include <acpi/acpi_bus.h>
+
+struct rule {
+	int counter;
+	int type;
+	int data;
+};
+
+struct table {
+	unsigned long pace;
+	int dnum, rnum;
+	int *devices;
+	int handle;
+	int rcounter, raction;
+	struct rule *rules;
+};
+
+#define TABLE_SIZE (sizeof (struct table) - 2 * sizeof (void *)	  \
+		    + rt.dnum * sizeof (int)			  \
+		    + rt.rnum * sizeof (struct rule))
+
+#define TABLE_BUFFER_SIZE (9 + rt.dnum + rt.rnum * 3 + (TABLE_SIZE << 3) / 3)
+
+extern void occasionally_generate_event(struct acpi_device *acpi_device);
+extern void timely_generate_event(struct acpi_device *acpi_device, int interactions, unsigned long *notify);
+
+extern int push_table(char *buf, unsigned long count);
+extern int pull_table(char **buf);
+
+#endif /* TABLE_H */
diff -uN SIN/uniq.h SIN.new/uniq.h
--- SIN/uniq.h	1970-01-01 01:00:00.000000000 +0100
+++ SIN.new/uniq.h	2007-01-18 19:20:53.000000000 +0100
@@ -0,0 +1,59 @@
+/*
+ *  Copyright (C) 2007 Alessandro Di Marco
+ */
+
+/*
+ *  This file is part of SIN.
+ *
+ *  SIN is free software; you can redistribute it and/or modify it under the
+ *  terms of the GNU General Public License as published by the Free Software
+ *  Foundation; version 2 of the License.
+ *
+ *  SIN 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 General Public License for more
+ *  details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with SIN; if not, write to the Free Software Foundation, Inc., 51 Franklin
+ *  St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef UNIQ_H
+#define UNIQ_H
+
+#include <linux/bitops.h>
+
+#define out_of_range(l, x, u) ((x) < (l) || (x) >= (u))
+
+struct uniq {
+	int elements;
+	unsigned long *bitmap;
+};
+
+static inline int uniq_alloc(struct uniq *ci, int elm)
+{
+	int size = (1 + elm / sizeof (unsigned long)) * sizeof (unsigned long);
+
+	ci->elements = elm;
+
+	ci->bitmap = kzalloc(size, GFP_KERNEL);
+	if (!ci->bitmap) {
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static inline void uniq_free(struct uniq *ci)
+{
+	kfree(ci->bitmap);
+}
+
+static inline int uniq_check(struct uniq *ci, int index)
+{
+	return out_of_range(0, index, ci->elements)
+		|| test_and_set_bit(index, ci->bitmap);
+}
+
+#endif /* UNIQ_H */

[-- Attachment #3: Type: text/plain, Size: 85 bytes --]


-- 
Man is the only animal that laughs and has a state legislature. - Samuel Butler

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-18 19:29 [ANNOUNCE] System Inactivity Monitor v1.0 Alessandro Di Marco
@ 2007-01-19  7:38 ` Arjan van de Ven
  2007-01-19 14:49   ` Alessandro Di Marco
  2007-01-19 10:11 ` Pavel Machek
  2007-01-19 21:18 ` Bill Davidsen
  2 siblings, 1 reply; 46+ messages in thread
From: Arjan van de Ven @ 2007-01-19  7:38 UTC (permalink / raw)
  To: Alessandro Di Marco; +Cc: linux-kernel

On Thu, 2007-01-18 at 20:29 +0100, Alessandro Di Marco wrote:
> Hi all,
> 
> this is a new 2.6.20 module implementing a user inactivity trigger. Basically
> it acts as an event sniffer, issuing an ACPI event when no user activity is
> detected for more than a certain amount of time. This event can be successively
> grabbed and managed by an user-level daemon such as acpid, blanking the screen,
> dimming the lcd-panel light à la mac, etc...


Hi,

why did you chose an ACPI event? I'd expect a uevent (which dbus
captures etc) to be a more logical choice..

Greetings,
   Arjan van de Ven


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-18 19:29 [ANNOUNCE] System Inactivity Monitor v1.0 Alessandro Di Marco
  2007-01-19  7:38 ` Arjan van de Ven
@ 2007-01-19 10:11 ` Pavel Machek
  2007-01-21 21:04   ` Jan Engelhardt
  2007-01-22 12:46   ` Alessandro Di Marco
  2007-01-19 21:18 ` Bill Davidsen
  2 siblings, 2 replies; 46+ messages in thread
From: Pavel Machek @ 2007-01-19 10:11 UTC (permalink / raw)
  To: Alessandro Di Marco; +Cc: linux-kernel

HiQ

> this is a new 2.6.20 module implementing a user inactivity trigger. Basically
> it acts as an event sniffer, issuing an ACPI event when no user activity is
> detected for more than a certain amount of time. This event can be successively
> grabbed and managed by an user-level daemon such as acpid, blanking the screen,
> dimming the lcd-panel light ? la mac, etc...

While functionality is extremely interesting.... does it really have
to be in kernel?


> +if [ ! -d "/proc/sin" ]; then
> +    echo "/proc/sin not found, has sinmod been loaded?"
> +    exit
> +fi

No new /proc files, please.

> +cat <<EOF
> +
> +SIN wakes up periodically and checks for user activity occurred in the
> +meantime; this options lets you to specify how much frequently SIN should be
> +woken-up. Its value is expressed in tenth of seconds.

Heh. We'll waste power trying to save it. If you have to hook it into
kernel, can you at least do it properly?

						Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-19  7:38 ` Arjan van de Ven
@ 2007-01-19 14:49   ` Alessandro Di Marco
  2007-01-19 17:45     ` Scott Preece
  0 siblings, 1 reply; 46+ messages in thread
From: Alessandro Di Marco @ 2007-01-19 14:49 UTC (permalink / raw)
  To: Arjan van de Ven; +Cc: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1009 bytes --]

Arjan van de Ven <arjan@infradead.org> writes:

   On Thu, 2007-01-18 at 20:29 +0100, Alessandro Di Marco wrote:
   > Hi all,
   > 
   > this is a new 2.6.20 module implementing a user inactivity trigger. Basically
   > it acts as an event sniffer, issuing an ACPI event when no user activity is
   > detected for more than a certain amount of time. This event can be successively
   > grabbed and managed by an user-level daemon such as acpid, blanking the screen,
   > dimming the lcd-panel light à la mac, etc...


   Hi,

   why did you chose an ACPI event? I'd expect a uevent (which dbus
   captures etc) to be a more logical choice..

Laziness... :) Just an idea realized in a hurry to dim my laptop panel when it
is in idle (I don't use X11, so no xscreensaver et simila.)  

Anyway I can accommodate it, if someone of you thinks that it is interesting
enough to be carried on the business.

The patch in attachment fixes some silly bugs of the previous version.

Regards,


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: sin-1.2.patch --]
[-- Type: text/x-patch, Size: 10525 bytes --]

diff -uN old/procfs.c new/procfs.c
--- old/procfs.c	2007-01-19 15:24:12.000000000 +0100
+++ new/procfs.c	2007-01-19 15:20:26.000000000 +0100
@@ -89,9 +89,19 @@
 	return err;
 }
 
+static int fake_write_proc(struct file *file, const __user char *ubuf,
+			   unsigned long count, void *data)
+{
+	void (*func)(void) = data;
+
+	func();
+	return count;
+}
+
 int start_procfs(void)
 {
-	struct proc_dir_entry *inputd, *acpid, *table, *ilink, *alink;
+	struct proc_dir_entry *inputd, *acpid,
+		*table, *interact, *ilink, *alink;
 
 	int err = -ENOMEM;
 
@@ -159,24 +169,35 @@
 	table->write_proc = write_proc;
 	table->owner = THIS_MODULE;
 
+	interact = create_proc_entry("interact", 0200, rootdir);
+	if (!interact) {
+		goto cleanout9;
+	}
+
+	interact->data = (void *) simulate_interaction;
+	interact->write_proc = fake_write_proc;
+	interact->owner = THIS_MODULE;
+
 	ilink = proc_symlink("sources", rootdir, "input");
 	if (!ilink) {
-		goto cleanout9;
+		goto cleanout10;
 	}
 
 	ilink->owner = THIS_MODULE;
 
 	alink = proc_symlink("destinations", rootdir, "acpi");
 	if (!alink) {
-		goto cleanout10;
+		goto cleanout11;
 	}
 
 	alink->owner = THIS_MODULE;
 
 	return 0;
 
-cleanout10:
+cleanout11:
 	remove_proc_entry("sources", rootdir);
+cleanout10:
+	remove_proc_entry("interact", rootdir);
 cleanout9:
 	remove_proc_entry("table", rootdir);
 cleanout8:
@@ -203,6 +224,7 @@
 {
 	remove_proc_entry("destinations", rootdir);
 	remove_proc_entry("sources", rootdir);
+	remove_proc_entry("interact", rootdir);
 	remove_proc_entry("table", rootdir);
 	remove_proc_entry("acpi", rootdir);
 	remove_proc_entry("input", rootdir);
diff -uN old/sin.c new/sin.c
--- old/sin.c	2007-01-19 15:24:12.000000000 +0100
+++ new/sin.c	2007-01-19 15:20:26.000000000 +0100
@@ -34,7 +34,7 @@
 
 MODULE_ALIAS("blanker");
 
-MODULE_VERSION("1.0");
+MODULE_VERSION("1.2");
 
 static struct acpi_device *acpi_device;
 
@@ -49,13 +49,18 @@
 static DEFINE_MUTEX(runlock);
 static int running;
 
-static void event(struct input_handle *handle,
-		  unsigned int type, unsigned int code, int value)
+inline void signal_interaction(void)
 {
 	if (unlikely(test_and_clear_bit(0, &notify))) {
 		clear_bit(1, &notify);
 		occasionally_generate_event(acpi_device);
 	}
+}
+
+static void event(struct input_handle *handle,
+		  unsigned int type, unsigned int code, int value)
+{
+	signal_interaction();
 
 	atomic_inc(&interactions);
 }
@@ -166,8 +171,13 @@
 	if (running) {
 		shutdown = 1;
 		del_timer_sync(&timer);
+
 		input_unregister_handler(&ih);
 		kfree(ih.id_table);
+
+		signal_interaction();
+		cleanup_table();
+
 		running = 0;
 	}
 
@@ -176,14 +186,14 @@
 
 static int __init sih_init(void)
 {
-	printk("System Inactivity Notifier 1.0 - (c) Alessandro Di Marco <dmr@c0nc3pt.com>\n");
+	printk("System Inactivity Notifier 1.2 - (c) Alessandro Di Marco <dmr@c0nc3pt.com>\n");
 	return start_procfs();
 }
 
 static void __exit sih_exit(void)
 {
 	stop_procfs();
-	stop_monitor();
+	(void) stop_monitor();
 }
 
 module_init(sih_init);
diff -uN old/sin.h new/sin.h
--- old/sin.h	2007-01-19 15:24:12.000000000 +0100
+++ new/sin.h	2007-01-19 15:20:26.000000000 +0100
@@ -26,6 +26,8 @@
 
 #define MODULE_NAME "sin"
 
+extern void signal_interaction(void);
+
 extern int start_monitor(char *ids, struct input_device_id *idi, unsigned long pace);
 extern void stop_monitor(void);
 
diff -uN old/table.c new/table.c
--- old/table.c	2007-01-19 15:24:12.000000000 +0100
+++ new/table.c	2007-01-19 15:20:26.000000000 +0100
@@ -32,7 +32,7 @@
 #include "acpi_enumerator.h"
 
 static struct table rt;
-static int debug;
+static int counter, action;
 
 /*
  * WARNING: sonypi, buttons and others issue a spurious event when removed from
@@ -42,7 +42,7 @@
 
 void occasionally_generate_event(struct acpi_device *acpi_device)
 {
-	if (unlikely(debug)) {
+	if (unlikely(rt.debug)) {
 		printk("generating special event [%d, %d]\n",
 		       rt.rules[rt.rnum].type, rt.rules[rt.rnum].data);
 	}
@@ -54,17 +54,15 @@
 void timely_generate_event(struct acpi_device *acpi_device,
 			   int interactions, unsigned long *notify)
 {
-	static int counter, action;
-
 	if (interactions && counter) {
-		if (unlikely(debug)) {
+		if (unlikely(rt.debug)) {
 			printk("user activity detected, counter reset!\n");
 		}
 
 		counter = action = 0;
 	}
 
-	if (unlikely(debug)) {
+	if (unlikely(rt.debug)) {
 		printk("global counter %d, next rule is [%d %d %d]\n",
 		       counter,
 		       rt.rules[action].counter,
@@ -73,7 +71,7 @@
 	}
 
 	while (action < rt.rnum && rt.rules[action].counter == counter) {
-		if (unlikely(debug)) {
+		if (unlikely(rt.debug)) {
 			printk("generating event [%d, %d]\n",
 			       rt.rules[action].type,
 			       rt.rules[action].data);
@@ -87,7 +85,7 @@
 	}
 
 	if (rt.raction >= 0 && action == rt.rnum) {
-		if (unlikely(debug)) {
+		if (unlikely(rt.debug)) {
 			printk("last rule reached, restarting from %d\n",
 			       rt.rcounter);
 		}
@@ -101,6 +99,12 @@
 	}
 }
 
+void simulate_interaction(void)
+{
+	signal_interaction();
+	counter = action = 0;
+}
+
 #define parse_num(endp) ({				\
 			char *cp = endp;		\
 							\
@@ -129,6 +133,7 @@
 
 int push_table(char *buf, unsigned long count)
 {
+	struct table nrt;
 	struct input_device_id *idi;
 	struct uniq uniq;
 	int devices;
@@ -137,25 +142,25 @@
 
 	devices = get_devices();
 
-	debug = parse_num(buf);
+	nrt.debug = parse_num(buf);
 
-	rt.pace = (parse_num(buf) * HZ) / 10;
-	rt.dnum = parse_num(buf);
-	rt.rnum = parse_num(buf);
+	nrt.pace = (parse_num(buf) * HZ) / 10;
+	nrt.dnum = parse_num(buf);
+	nrt.rnum = parse_num(buf);
 
-	if (out_of_range(1, rt.pace, 1000000) ||
-	    out_of_range(0, rt.dnum, devices)) {
+	if (out_of_range(1, nrt.pace, 1000000) ||
+	    out_of_range(0, nrt.dnum, devices)) {
 		err = -EINVAL;
 		goto out;
 	}
 
-	rt.devices = kmalloc(rt.dnum * sizeof (int), GFP_KERNEL);
-	if (!rt.devices) {
+	nrt.devices = kmalloc(nrt.dnum * sizeof (int), GFP_KERNEL);
+	if (!nrt.devices) {
 		goto out;
 	}
 
-	rt.rules = kmalloc((rt.rnum + 1) * sizeof (struct rule), GFP_KERNEL);
-	if (!rt.rules) {
+	nrt.rules = kmalloc((nrt.rnum + 1) * sizeof (struct rule), GFP_KERNEL);
+	if (!nrt.rules) {
 		goto cleanout1;
 	}
 
@@ -163,69 +168,76 @@
 		goto cleanout2;
 	}
 
-	for (i = 0; i < rt.dnum; i++) {
-		rt.devices[i] = parse_num(buf);
-		if (uniq_check(&uniq, rt.devices[i])) {
+	for (i = 0; i < nrt.dnum; i++) {
+		nrt.devices[i] = parse_num(buf);
+		if (uniq_check(&uniq, nrt.devices[i])) {
 			break;
 		}
 	}
 
 	uniq_free(&uniq);
 
-	if (i < rt.dnum) {
+	if (i < nrt.dnum) {
 		err = -EINVAL;
 		goto cleanout2;
 	}
 
-	rt.handle = parse_num(buf);
-	if (out_of_range(0, rt.handle, get_handlers())) {
+	nrt.handle = parse_num(buf);
+	if (out_of_range(0, nrt.handle, get_handlers())) {
 		err = -EINVAL;
 		goto cleanout2;
 	}
 
-	rt.rcounter = parse_num(buf);
+	nrt.rcounter = parse_num(buf);
 
-	rt.rules[rt.rnum].counter = -1;
-	rt.rules[rt.rnum].type = parse_num(buf);
-	rt.rules[rt.rnum].data = parse_num(buf);
-
-	for (i = 0; i < rt.rnum; i++) {
-		rt.rules[i].counter = parse_num(buf);
-		if (rt.rules[i].counter < 0) {
+	nrt.rules[nrt.rnum].counter = -1;
+	nrt.rules[nrt.rnum].type = parse_num(buf);
+	nrt.rules[nrt.rnum].data = parse_num(buf);
+
+	for (i = 0; i < nrt.rnum; i++) {
+		nrt.rules[i].counter = parse_num(buf);
+		if (nrt.rules[i].counter < 0) {
 			err = -EINVAL;
 			goto cleanout2;
 		}
 
-		rt.rules[i].type = parse_num(buf);
-		rt.rules[i].data = parse_num(buf);
+		nrt.rules[i].type = parse_num(buf);
+		nrt.rules[i].data = parse_num(buf);
 	}
 
-	sort(rt.rules, rt.rnum, sizeof (struct rule), cmp, swap);
+	sort(nrt.rules, nrt.rnum, sizeof (struct rule), cmp, swap);
 
-	rt.raction = -1;
+	nrt.raction = -1;
 
-	if (rt.rcounter >= 0) {
-		for (i = 0; i < rt.rnum; i++) {
-			if (rt.rules[i].counter >= rt.rcounter) {
-				rt.raction = i;
+	if (nrt.rcounter >= 0) {
+		for (i = 0; i < nrt.rnum; i++) {
+			if (nrt.rules[i].counter >= nrt.rcounter) {
+				nrt.raction = i;
 				break;
 			}
 		}
 	}
 
+	if (!tablecmp(&rt, &nrt)) {
+		err = count;
+		goto cleanout2;
+	}
+
 	stop_monitor();
 
-	idi = kzalloc((rt.dnum + 1) *
+	idi = kzalloc((nrt.dnum + 1) *
 		      sizeof (struct input_device_id), GFP_KERNEL);
 	if (!idi) {
 		goto cleanout2;
 	}
 
-	for (i = 0; i < rt.dnum; i++) {
-		fill_input_device(&idi[i], rt.devices[i]);
+	for (i = 0; i < nrt.dnum; i++) {
+		fill_input_device(&idi[i], nrt.devices[i]);
 	}
 
-	err = start_monitor(get_hardware_id(rt.handle), idi, rt.pace);
+	memcpy(&rt, &nrt, sizeof (struct table));
+
+	err = start_monitor(get_hardware_id(rt.handle), idi, nrt.pace);
 	if (err < 0) {
 		goto cleanout3;
 	}
@@ -235,9 +247,9 @@
 cleanout3:
 	kfree(idi);
 cleanout2:
-	kfree(rt.rules);
+	kfree(nrt.rules);
 cleanout1:
-	kfree(rt.devices);
+	kfree(nrt.devices);
 out:
 	return err;
 }
@@ -252,7 +264,7 @@
 		return -EFAULT;
 	}
 
-	b += sprintf(b, "%d\n%lu\n%d %d\n", debug,
+	b += sprintf(b, "%d\n%lu\n%d %d\n", rt.debug,
 		     (rt.pace * 10) / HZ, rt.dnum, rt.rnum);
 
 	for (i = 0; i < rt.dnum; i++) {
@@ -272,3 +284,12 @@
 
 	return b - *buf;
 }
+
+void cleanup_table(void)
+{
+	kfree(rt.devices);
+	kfree(rt.rules);
+	memset(&rt, 0, sizeof (struct table));
+
+	counter = action = 0;
+}
diff -uN old/table.h new/table.h
--- old/table.h	2007-01-19 15:24:12.000000000 +0100
+++ new/table.h	2007-01-19 15:20:26.000000000 +0100
@@ -31,6 +31,7 @@
 };
 
 struct table {
+	int debug;
 	unsigned long pace;
 	int dnum, rnum;
 	int *devices;
@@ -39,6 +40,28 @@
 	struct rule *rules;
 };
 
+static inline int tablecmp(struct table *l, struct table *r)
+{
+	if (l->debug != r->debug ||
+	    l->pace != r->pace ||
+	    l->dnum != r->dnum ||
+	    l->handle != r->handle ||
+	    l->rcounter != r->rcounter ||
+	    l->raction != r->raction) {
+		return 1;
+	}
+
+	if (memcmp(l->devices, r->devices, l->dnum * sizeof (int))) {
+		return 1;
+	}
+
+	if (memcmp(l->rules, r->rules, l->rnum * sizeof (struct rule))) {
+		return 1;
+	}
+
+	return 0;
+}
+
 #define TABLE_SIZE (sizeof (struct table) - 2 * sizeof (void *)	  \
 		    + rt.dnum * sizeof (int)			  \
 		    + rt.rnum * sizeof (struct rule))
@@ -48,7 +71,10 @@
 extern void occasionally_generate_event(struct acpi_device *acpi_device);
 extern void timely_generate_event(struct acpi_device *acpi_device, int interactions, unsigned long *notify);
 
+void simulate_interaction(void);
+
 extern int push_table(char *buf, unsigned long count);
 extern int pull_table(char **buf);
+extern void cleanup_table(void);
 
 #endif /* TABLE_H */

[-- Attachment #3: Type: text/plain, Size: 159 bytes --]


-- 
Ethics is not definable, is not implementable, because it is not conscious; it
involves not only our thinking, but also our feeling. - Valdemar W. Setzer

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-19 14:49   ` Alessandro Di Marco
@ 2007-01-19 17:45     ` Scott Preece
  2007-01-19 22:21       ` Jan Engelhardt
  0 siblings, 1 reply; 46+ messages in thread
From: Scott Preece @ 2007-01-19 17:45 UTC (permalink / raw)
  To: Alessandro Di Marco; +Cc: Arjan van de Ven, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 267 bytes --]

On 1/19/07, Alessandro Di Marco <dmr@gmx.it> wrote:

> The patch in attachment fixes some silly bugs of the previous version.
>
> Regards,

Hi, attached is a patch for your gentable file, rewriting some of the
user prompts to make them more readable.

Regards,
scott

[-- Attachment #2: gentable.patch --]
[-- Type: text/x-patch, Size: 5310 bytes --]

--- gentable	2007-01-19 11:39:28.000000000 -0600
+++ gentable-new	2007-01-19 11:39:28.000000000 -0600
@@ -31,9 +31,9 @@
 
 cat <<EOF
 
-SIN wakes up periodically and checks for user activity occurred in the
-meantime; this options lets you to specify how much frequently SIN should be
-woken-up. Its value is expressed in tenth of seconds.
+SIN wakes up periodically and checks whether user activity has occurred
+since it last ran; the next option lets you to specify how frequently
+SIN should wake up. Its value is expressed in tenth of seconds.
 
 EOF
 
@@ -45,9 +45,9 @@
 
 cat <<EOF
 
-Asleep or not, SIN constantly monitors the input devices searching for user
-activity. This option lets you choose which device have to be monitored. At
-least one device is needed and please avoid the duplicates.
+Asleep or not, SIN constantly monitors the input devices watching for user
+activity. The next option lets you choose which device have to be monitored.
+You must specify at least one device and must not specify duplicates.
 
 EOF
 
@@ -65,7 +65,7 @@
 
 cat <<EOF
 
-SIN produces ACPI events depending on the user activity. To do this you have to
+SIN produces ACPI events depending on the user activity. You must
 specify a suitable handler that will be used as originator.
 
 EOF
@@ -74,7 +74,7 @@
 cat /proc/sin/acpi
 
 echo
-input "Please digit the corresponding number" handle
+input "Please enter the number corresponding to the handler" handle
 
 if [ -z "${handle}" ]; then
     handle="0"
@@ -82,19 +82,19 @@
 
 cat <<EOF
 
-SIN produces events in base to rules. Each rule is a triple composed by a
-"counter", a "type" and a "data". Once awaken, a global counter is increased if
-SIN detects no user activity and reset to zero, otherwise. When this global
-counter reaches the value specified in the counter field of a rule, an event is
-generated with the corresponding "type" and "data". Clearly, different rules
-should have different "type" and "data" fields to convey different signals to
-the user space daemon.
+SIN produces events based on rules. Each rule is a triple composed by a
+"counter", a "type", and a "data" value. When SIN awakens, a global counter
+is increased if SIN detects no user activity and reset to zero, otherwise.
+When this global counter reaches the value specified in the counter field
+of a rule, an event is generated with the corresponding "type" and "data".
+Different rules should have different "type" and "data" fields to convey
+different signals to the user space daemon.
 
 For example, the rule "60 1 19" produces the ACPI event "XXXX 00000001
-00000019" right after one minute of user inactivity (assuming pace=10.)
+00000019" when SIN recognizes one minute of user inactivity (assuming pace=10.)
 
-Please specify each rule as a space-separated triple; to finish just press
-enter.
+Please specify each rule as a space-separated triple on a separate line;
+when finished, just press enter.
 
 EOF
 
@@ -114,9 +114,9 @@
 
 cat <<EOF
 
-A special event has been provided to better help those of us who wanna use SIN
+A special event has been provided to simplify using SIN
 as a screen-blanker. It will be generated as soon as some user activity is
-detected, but only after one or more rule have been triggered.
+detected, but only after one or more rules have been triggered.
 
 EOF
 
@@ -128,16 +128,16 @@
 
 cat <<EOF
 
-Usually a rule-list terminates with dead-end actions such as suspend or
-hibernate, requiring the user interaction to wake-up the system. Unfortunately
-this activity occurs when SIN, as well as the kernel, are not ready to capture
-these events yet. As a consequence, no special event will ever be generated and
+Often an SIN event results in suspending or hibernating the system,
+hibernate, requiring user interaction to wake-up the system. Unfortunately
+that interaction occurs when SIN, as well as the kernel, cannot capture
+it. As a consequence, no event will ever be generated and
 the system will remain in the state associated with the next-to-last rule
-(e.g. blanked screen, wireless powered off, etc.) In such cases this field
-forces the special event generation, resetting simultaneously the global
-counter to an arbitrary value, so to reinstate the rule-list evaluation to the
-beginning. Possible value ranges are described below, where N is the maximum
-counter in the rule list:
+(e.g. blanked screen, wireless powered off, etc.). The next option
+allows you to request a special event, resetting the global
+counter to an arbitrary value, so to restart the rule-list evaluation.
+Possible value ranges are described below, where N is the maximum
+counter in the current rule list:
 
     [0, N]    => reset the global counter to the specified value
     otherwise => do nothing, the global counter goes on and on and on...
@@ -163,8 +163,8 @@
 # modprobe sinmod
 # echo $1 >/proc/sin/table
 
-An "Invalid argument" error signals a mismatch in the table file, usually due
-to wrong acpi or input device specified. In these cases restart from scratch
-double checking your answers. Have fun!
+An "Invalid argument" error indicates a mismatch in the table file, usually
+due to specifying an invalid acpi or input device. In that case, restart from
+scratch, double checking your inputs. Have fun!
 
 EOF

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-18 19:29 [ANNOUNCE] System Inactivity Monitor v1.0 Alessandro Di Marco
  2007-01-19  7:38 ` Arjan van de Ven
  2007-01-19 10:11 ` Pavel Machek
@ 2007-01-19 21:18 ` Bill Davidsen
  2007-01-20 15:37   ` Alessandro Di Marco
  2 siblings, 1 reply; 46+ messages in thread
From: Bill Davidsen @ 2007-01-19 21:18 UTC (permalink / raw)
  To: Alessandro Di Marco, Linux Kernel mailing List

Alessandro Di Marco wrote:
> Hi all,
> 
> this is a new 2.6.20 module implementing a user inactivity trigger. Basically
> it acts as an event sniffer, issuing an ACPI event when no user activity is
> detected for more than a certain amount of time. This event can be successively
> grabbed and managed by an user-level daemon such as acpid, blanking the screen,
> dimming the lcd-panel light à la mac, etc...

Any idea how much power this saves? And for the vast rest of us who do 
run X, this seems to parallel the work of a well-tuned screensaver.

-- 
bill davidsen <davidsen@tmr.com>
   CTO TMR Associates, Inc
   Doing interesting things with small computers since 1979


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-19 17:45     ` Scott Preece
@ 2007-01-19 22:21       ` Jan Engelhardt
  2007-01-19 22:30         ` Scott Preece
  0 siblings, 1 reply; 46+ messages in thread
From: Jan Engelhardt @ 2007-01-19 22:21 UTC (permalink / raw)
  To: Scott Preece; +Cc: Alessandro Di Marco, Arjan van de Ven, linux-kernel


On Jan 19 2007 11:45, Scott Preece wrote:
> Hi, attached is a patch for your gentable file, rewriting some of the
> user prompts to make them more readable.

I still don't get why this is called "SIN" in the Kconfig and code texts 
though the acronym for System Inactivity Monitor would be "SIM".

	-`J'
-- 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-19 22:21       ` Jan Engelhardt
@ 2007-01-19 22:30         ` Scott Preece
  0 siblings, 0 replies; 46+ messages in thread
From: Scott Preece @ 2007-01-19 22:30 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Alessandro Di Marco, Arjan van de Ven, linux-kernel

On 1/19/07, Jan Engelhardt <jengelh@linux01.gwdg.de> wrote:
>
> On Jan 19 2007 11:45, Scott Preece wrote:
> > Hi, attached is a patch for your gentable file, rewriting some of the
> > user prompts to make them more readable.
>
> I still don't get why this is called "SIN" in the Kconfig and code texts
> though the acronym for System Inactivity Monitor would be "SIM".
---

The code calls it "System Inactivity Notifier".

scott

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-19 21:18 ` Bill Davidsen
@ 2007-01-20 15:37   ` Alessandro Di Marco
  0 siblings, 0 replies; 46+ messages in thread
From: Alessandro Di Marco @ 2007-01-20 15:37 UTC (permalink / raw)
  To: Bill Davidsen; +Cc: linux-kernel

Bill Davidsen <davidsen@tmr.com> writes:

   Alessandro Di Marco wrote:
   > Hi all,
   >
   > this is a new 2.6.20 module implementing a user inactivity trigger. Basically
   > it acts as an event sniffer, issuing an ACPI event when no user activity is
   > detected for more than a certain amount of time. This event can be successively
   > grabbed and managed by an user-level daemon such as acpid, blanking the screen,
   > dimming the lcd-panel light à la mac, etc...

   Any idea how much power this saves? And for the vast rest of us who do run X,
   this seems to parallel the work of a well-tuned screensaver.

This is just a notifier; to make it work as a screensaver you'll have to rely
on some external programs. Personally I use smartdimmer to dim my vaio panel.

Obviously you can keep your toaster flying, if you like, simply calling the
flying-toaster module instead of smartdimmer. Anyway I would use the latter on
battery. ;-)

Best,

-- 
"What made the deepest impression upon you?" inquired a friend one day of
Lincoln, "when you stood in the presence of the Falls of Niagara, the greatest
of natural wonders?" ---- "The thing that stuck me most forcibly when I saw the
Falls," Lincoln responded with the characteristic deliberation, "was where in
the world did all that water come from?" - Author Unknown

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-19 10:11 ` Pavel Machek
@ 2007-01-21 21:04   ` Jan Engelhardt
  2007-01-23  9:38     ` Pavel Machek
  2007-01-22 12:46   ` Alessandro Di Marco
  1 sibling, 1 reply; 46+ messages in thread
From: Jan Engelhardt @ 2007-01-21 21:04 UTC (permalink / raw)
  To: Pavel Machek; +Cc: Alessandro Di Marco, linux-kernel


On Jan 19 2007 10:11, Pavel Machek wrote:
>
>> this is a new 2.6.20 module implementing a user inactivity trigger. Basically
>> it acts as an event sniffer, issuing an ACPI event when no user activity is
>> detected for more than a certain amount of time. This event can be successively
>> grabbed and managed by an user-level daemon such as acpid, blanking the screen,
>> dimming the lcd-panel light ? la mac, etc...
>
>While functionality is extremely interesting.... does it really have
>to be in kernel?

I think so. While the X server grabs off any keyboard and mouse activity
on its own, there is no such thing for the [read "all"] console[s].
Mouse movement (e.g. GPM) is actually implemented in the kernel,
and screen blanking (the one controlled with "\e[9;x]") is too.


	-`J'
-- 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-19 10:11 ` Pavel Machek
  2007-01-21 21:04   ` Jan Engelhardt
@ 2007-01-22 12:46   ` Alessandro Di Marco
  2007-01-23  9:41     ` Pavel Machek
  1 sibling, 1 reply; 46+ messages in thread
From: Alessandro Di Marco @ 2007-01-22 12:46 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1121 bytes --]

Pavel Machek <pavel@ucw.cz> writes:

   > +if [ ! -d "/proc/sin" ]; then
   > +    echo "/proc/sin not found, has sinmod been loaded?"
   > +    exit
   > +fi

   No new /proc files, please.

This was merely a prototype realized in a hurry, not a production
driver. Really, I did't think it could be interesting for anybody.

Would be /sys ok?

   > +cat <<EOF
   > +
   > +SIN wakes up periodically and checks for user activity occurred in the
   > +meantime; this options lets you to specify how much frequently SIN should be
   > +woken-up. Its value is expressed in tenth of seconds.

   Heh. We'll waste power trying to save it.

Well, not just a power saver. For example I use SIN to auto-logoff my bash
session as well (detaching the screen session.)

   If you have to hook it into kernel, can you at least do it properly?

Of course. You can find attached a patch fixing this. Now SIN wakes up just
when it expects to do something: if in the meantime the user interacts with the
system, SIN simply recalculates the next wake-up time on the basis of the last
user's activity date and goes to sleep again.

Best,


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: deadline.patch --]
[-- Type: text/x-patch, Size: 18713 bytes --]

---
 gentable |   72 +++++++++++++---------------------
 procfs.c |    2 +-
 sin.c    |   68 ++++++++++++++++++++------------
 sin.h    |   36 ++++++++++++++++-
 table.c  |  132 ++++++++++++++++++++++++++++----------------------------------
 table.h  |   21 +++++-----
 6 files changed, 176 insertions(+), 155 deletions(-)

diff --git a/gentable b/gentable
index 44b4f77..3a322df 100755
--- a/gentable
+++ b/gentable
@@ -31,23 +31,9 @@ fi

 cat <<EOF

-SIN wakes up periodically and checks whether user activity has occurred
-since it last ran; the next option lets you to specify how frequently
-SIN should wake up. Its value is expressed in tenth of seconds.
-
-EOF
-
-input "Pace ticks?" pace
-
-if [ -z "${pace}" ]; then
-    pace="10"
-fi
-
-cat <<EOF
-
-Asleep or not, SIN constantly monitors the input devices watching for user
-activity. The next option lets you choose which device have to be monitored.
-You must specify at least one device and must not specify duplicates.
+SIN constantly monitors the input devices watching for user activity. This
+option lets you choose which device have to be monitored. You must specify at
+least one device and must not specify duplicates.

 EOF

@@ -65,8 +51,8 @@ devices=(${devs})

 cat <<EOF

-SIN produces ACPI events depending on the user activity. You must
-specify a suitable handler that will be used as originator.
+SIN produces ACPI events depending on the user activity. You must specify a
+suitable handler that will be used as originator.

 EOF

@@ -83,18 +69,17 @@ fi
 cat <<EOF

 SIN produces events based on rules. Each rule is a triple composed by a
-"counter", a "type", and a "data" value. When SIN awakens, a global counter
-is increased if SIN detects no user activity and reset to zero, otherwise.
-When this global counter reaches the value specified in the counter field
-of a rule, an event is generated with the corresponding "type" and "data".
-Different rules should have different "type" and "data" fields to convey
-different signals to the user space daemon.
+"target", a "type", and a "data" value. The "target" field is a timeout in
+tenth of seconds specifying the minimum period of user inactivity needed to
+trigger the rule. When a rule triggers, an event is generated with the
+corresponding "type" and "data".  Different rules should have different "type"
+and "data" fields to convey different signals to the user space daemon.

-For example, the rule "60 1 19" produces the ACPI event "XXXX 00000001
-00000019" when SIN recognizes one minute of user inactivity (assuming pace=10.)
+For example, the rule "600 1 19" produces the ACPI event "XXXX 00000001
+00000019" when SIN recognizes one minute of user inactivity.

-Please specify each rule as a space-separated triple on a separate line;
-when finished, just press enter.
+Please specify each rule as a space-separated triple on a separate line; when
+finished, just press enter.

 EOF

@@ -114,9 +99,9 @@ fi

 cat <<EOF

-A special event has been provided to simplify using SIN
-as a screen-blanker. It will be generated as soon as some user activity is
-detected, but only after one or more rules have been triggered.
+A special event has been provided to simplify using SIN as a screen-blanker. It
+will be generated as soon as some user activity is detected, but only after one
+or more rules have been triggered.

 EOF

@@ -128,15 +113,14 @@ fi

 cat <<EOF

-Often an SIN event results in suspending or hibernating the system,
-hibernate, requiring user interaction to wake-up the system. Unfortunately
-that interaction occurs when SIN, as well as the kernel, cannot capture
-it. As a consequence, no event will ever be generated and
-the system will remain in the state associated with the next-to-last rule
-(e.g. blanked screen, wireless powered off, etc.). The next option
-allows you to request a special event, resetting the global
-counter to an arbitrary value, so to restart the rule-list evaluation.
-Possible value ranges are described below, where N is the maximum
+Often an SIN event results in suspending or hibernating the system, hibernate,
+requiring user interaction to wake-up the system. Unfortunately that
+interaction occurs when SIN, as well as the kernel, cannot capture it. As a
+consequence, no event will ever be generated and the system will remain in the
+state associated with the next-to-last rule (e.g. blanked screen, wireless
+powered off, etc.). The next option allows you to request a special event,
+resetting the global counter to an arbitrary value, so to restart the rule-list
+evaluation.  Possible value ranges are described below, where N is the maximum
 counter in the current rule list:

     [0, N]    => reset the global counter to the specified value
@@ -150,7 +134,7 @@ if [ -z "${reset}" ]; then
     reset="-1"
 fi

-echo -e "0\n${pace}\n${#devices[@]} ${#rules[@]}\n${devices[@]}\n${handle}\n${reset}\n${resume}" > $1
+echo -e "0\n${#devices[@]} ${#rules[@]}\n${devices[@]}\n${handle}\n${reset}\n${resume}" > $1

 for (( i = 0; ${i}<${#rules[@]}; i++ )); do
     echo "${rules[${i}]}" >> $1
@@ -163,8 +147,8 @@ All done. Now you can try your newly generated table as follows:
 # modprobe sinmod
 # echo $1 >/proc/sin/table

-An "Invalid argument" error indicates a mismatch in the table file, usually
-due to specifying an invalid acpi or input device. In that case, restart from
+An "Invalid argument" error indicates a mismatch in the table file, usually due
+to specifying an invalid acpi or input device. In that case, restart from
 scratch, double checking your inputs. Have fun!

 EOF
diff --git a/procfs.c b/procfs.c
index 4424645..5929f90 100644
--- a/procfs.c
+++ b/procfs.c
@@ -174,7 +174,7 @@ int start_procfs(void)
 		goto cleanout9;
 	}

-	interact->data = (void *) simulate_interaction;
+	interact->data = (void *) simulate_event;
 	interact->write_proc = fake_write_proc;
 	interact->owner = THIS_MODULE;

diff --git a/sin.c b/sin.c
index c490daa..0d9b9c4 100644
--- a/sin.c
+++ b/sin.c
@@ -28,18 +28,19 @@
 #include "table.h"
 #include "procfs.h"

-MODULE_AUTHOR("Alessandro Di Marco <dmr@c0nc3pt.com>");
+MODULE_AUTHOR("Alessandro Di Marco <dmr@gmx.it>");
 MODULE_DESCRIPTION("System Inactivity Notifier");
 MODULE_LICENSE("GPL v2");

-MODULE_ALIAS("blanker");
-
-MODULE_VERSION("1.2");
+MODULE_VERSION("1.3");

 static struct acpi_device *acpi_device;

-static atomic_t interactions;
-static unsigned long notify;
+static struct user_activity uact = {
+	.lock = SPIN_LOCK_UNLOCKED,
+};
+
+static unsigned long status;

 static struct timer_list timer;
 static int shutdown;
@@ -49,20 +50,31 @@ static struct input_handler ih;
 static DEFINE_MUTEX(runlock);
 static int running;

+inline unsigned long simulate_activity(void)
+{
+	return register_activity(&uact);
+}
+
 inline void signal_interaction(void)
 {
-	if (unlikely(test_and_clear_bit(0, &notify))) {
-		clear_bit(1, &notify);
+	if (unlikely(test_bit(RULE_LOCK, &status))) {
+		set_bit(RULE_MARK, &status);
+	} else if (unlikely(test_and_clear_bit(RULE_TRIG, &status))) {
+		clear_bit(RULE_WRAP, &status);
 		occasionally_generate_event(acpi_device);
 	}
 }

-static void event(struct input_handle *handle,
-		  unsigned int type, unsigned int code, int value)
+inline void simulate_event(void)
 {
 	signal_interaction();
+	(void) simulate_activity();
+}

-	atomic_inc(&interactions);
+static void event(struct input_handle *handle,
+		  unsigned int type, unsigned int code, int value)
+{
+	simulate_event();
 }

 static struct input_handle *connect(struct input_handler *handler,
@@ -90,20 +102,23 @@ static void disconnect(struct input_handle *handle)
 	kfree(handle);
 }

-void timer_fn(unsigned long pace)
+void timer_fn(unsigned long data)
 {
 	if (!shutdown) {
-		if (unlikely(test_and_clear_bit(1, &notify) &&
-			     test_and_clear_bit(0, &notify))) {
-			occasionally_generate_event(acpi_device);
-		}
+		unsigned long next;

-		timely_generate_event(acpi_device,
-				      atomic_read(&interactions), &notify);
+		set_bit(RULE_LOCK, &status);

-		atomic_set(&interactions, 0);
+		next = timely_generate_event(acpi_device,
+					     last_activity(&uact), &status);

-		timer.expires = jiffies + pace;
+		clear_bit(RULE_LOCK, &status);
+
+		if (unlikely(test_and_clear_bit(RULE_MARK, &status))) {
+			signal_interaction();
+		}
+
+		timer.expires = next;
 		add_timer(&timer);
 	}
 }
@@ -118,7 +133,7 @@ static int acpi_match(struct acpi_device *device, struct acpi_driver *driver)
 	return -ENOENT;
 }

-int start_monitor(char *ids, struct input_device_id *idi, unsigned long pace)
+int start_monitor(char *ids, struct input_device_id *idi)
 {
 	struct acpi_driver ad = {
 		.ids = ids,
@@ -129,8 +144,7 @@ int start_monitor(char *ids, struct input_device_id *idi, unsigned long pace)

 	mutex_lock(&runlock);

-	atomic_set(&interactions, 0);
-	notify = 0;
+	status = 0;

 	if (acpi_bus_register_driver(&ad) < 0 || !acpi_device) {
 		printk("couldn't find system ACPI device\n");
@@ -150,9 +164,11 @@ int start_monitor(char *ids, struct input_device_id *idi, unsigned long pace)
 		return err;
 	}

-	setup_timer(&timer, timer_fn, pace);
+	setup_timer(&timer, timer_fn, 0);

-	timer.expires = jiffies + pace;
+	timer.expires =
+		timely_generate_event(acpi_device,
+				      register_activity(&uact), &status);

 	shutdown = 0;
 	add_timer(&timer);
@@ -186,7 +202,7 @@ void stop_monitor(void)

 static int __init sih_init(void)
 {
-	printk("System Inactivity Notifier 1.2 - (c) Alessandro Di Marco <dmr@c0nc3pt.com>\n");
+	printk("System Inactivity Notifier 1.3 - (c) Alessandro Di Marco <dmr@c0nc3pt.com>\n");
 	return start_procfs();
 }

diff --git a/sin.h b/sin.h
index 249021c..95161b2 100644
--- a/sin.h
+++ b/sin.h
@@ -26,9 +26,43 @@

 #define MODULE_NAME "sin"

+#define RULE_TRIG 0
+#define RULE_WRAP 1
+#define RULE_LOCK 2
+#define RULE_MARK 3
+
+struct user_activity {
+	spinlock_t lock;
+	unsigned long last;
+};
+
+static inline unsigned long register_activity(struct user_activity *uact)
+{
+	unsigned long last;
+
+	spin_lock(&uact->lock);
+	last = uact->last = jiffies;
+	spin_unlock(&uact->lock);
+
+	return last;
+}
+
+static inline unsigned long last_activity(struct user_activity *uact)
+{
+	unsigned long last;
+
+	spin_lock(&uact->lock);
+	last = uact->last;
+	spin_unlock(&uact->lock);
+
+	return last;
+}
+
+extern unsigned long simulate_activity(void);
 extern void signal_interaction(void);
+extern void simulate_event(void);

-extern int start_monitor(char *ids, struct input_device_id *idi, unsigned long pace);
+extern int start_monitor(char *ids, struct input_device_id *idi);
 extern void stop_monitor(void);

 #endif /* SIN_H */
diff --git a/table.c b/table.c
index c9c8af6..658636f 100644
--- a/table.c
+++ b/table.c
@@ -32,7 +32,7 @@
 #include "acpi_enumerator.h"

 static struct table rt;
-static int counter, action;
+static int next_rule;

 /*
  * WARNING: sonypi, buttons and others issue a spurious event when removed from
@@ -42,67 +42,52 @@ static int counter, action;

 void occasionally_generate_event(struct acpi_device *acpi_device)
 {
-	if (unlikely(rt.debug)) {
-		printk("generating special event [%d, %d]\n",
-		       rt.rules[rt.rnum].type, rt.rules[rt.rnum].data);
-	}
+	printd("generating special event [%d, %d]\n",
+	       rt.rules[rt.rnum].type, rt.rules[rt.rnum].data);

 	(void) acpi_bus_generate_event(acpi_device, rt.rules[rt.rnum].type,
 				       rt.rules[rt.rnum].data);
+
+	next_rule = 0;
 }

-void timely_generate_event(struct acpi_device *acpi_device,
-			   int interactions, unsigned long *notify)
+unsigned long timely_generate_event(struct acpi_device *acpi_device,
+				    unsigned long last, unsigned long *status)
 {
-	if (interactions && counter) {
-		if (unlikely(rt.debug)) {
-			printk("user activity detected, counter reset!\n");
+	printd("last %lu [status %lu], now %lu -> next target is %lu (%d)\n",
+	       last, *status, jiffies,
+	       last + rt.rules[next_rule].target, next_rule);
+
+	for (; next_rule < rt.rnum &&
+		     time_after_eq(jiffies, last + rt.rules[next_rule].target);
+	     next_rule++) {
+		if (unlikely(test_and_clear_bit(RULE_WRAP, status))) {
+			printd("passive wrap, generating special event\n");
+
+			(void) acpi_bus_generate_event(acpi_device,
+						       rt.rules[rt.rnum].type,
+						       rt.rules[rt.rnum].data);
 		}

-		counter = action = 0;
-	}
-
-	if (unlikely(rt.debug)) {
-		printk("global counter %d, next rule is [%d %d %d]\n",
-		       counter,
-		       rt.rules[action].counter,
-		       rt.rules[action].type,
-		       rt.rules[action].data);
-	}
-
-	while (action < rt.rnum && rt.rules[action].counter == counter) {
-		if (unlikely(rt.debug)) {
-			printk("generating event [%d, %d]\n",
-			       rt.rules[action].type,
-			       rt.rules[action].data);
-		}
+		printd("generating event [%d, %d]\n",
+		       rt.rules[next_rule].type, rt.rules[next_rule].data);

 		(void) acpi_bus_generate_event(acpi_device,
-					       rt.rules[action].type,
-					       rt.rules[action].data);
-		action++;
-		set_bit(0, notify);
+					       rt.rules[next_rule].type,
+					       rt.rules[next_rule].data);
+		set_bit(RULE_TRIG, status);
 	}

-	if (rt.raction >= 0 && action == rt.rnum) {
-		if (unlikely(rt.debug)) {
-			printk("last rule reached, restarting from %d\n",
-			       rt.rcounter);
-		}
+	if (rt.rwrap >= 0 && next_rule == rt.rnum) {
+		printd("last rule reached, restarting from %d\n", rt.rwrap);

-		counter = rt.rcounter;
-		action = rt.raction;
-		set_bit(1, notify);
+		next_rule = rt.rwrap;
+		set_bit(RULE_WRAP, status);

-	} else {
-		counter++;
+		last = simulate_activity();
 	}
-}

-void simulate_interaction(void)
-{
-	signal_interaction();
-	counter = action = 0;
+	return last + rt.rules[next_rule].target;
 }

 #define parse_num(endp) ({				\
@@ -117,10 +102,11 @@ void simulate_interaction(void)

 static int cmp(const void *l, const void *r)
 {
-	int lc = ((struct rule *) l)->counter;
-	int rc = ((struct rule *) r)->counter;
+	long lt = ((struct rule *) l)->target;
+	long rt = ((struct rule *) r)->target;
+	long dd = lt - rt;

-	return lc < rc ? -1 : lc > rc ? 1 : 0;
+	return dd < 0 ? -1 : dd > 0 ? 1 : 0;
 }

 static void swap(void *l, void *r, int size)
@@ -136,6 +122,7 @@ int push_table(char *buf, unsigned long count)
 	struct table nrt;
 	struct input_device_id *idi;
 	struct uniq uniq;
+
 	int devices;

 	int i, err = -ENOMEM;
@@ -144,12 +131,15 @@ int push_table(char *buf, unsigned long count)

 	nrt.debug = parse_num(buf);

-	nrt.pace = (parse_num(buf) * HZ) / 10;
 	nrt.dnum = parse_num(buf);
 	nrt.rnum = parse_num(buf);

-	if (out_of_range(1, nrt.pace, 1000000) ||
-	    out_of_range(0, nrt.dnum, devices)) {
+	if (out_of_range(0, nrt.dnum, devices)) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	if (nrt.rnum <= 0) {
 		err = -EINVAL;
 		goto out;
 	}
@@ -188,36 +178,32 @@ int push_table(char *buf, unsigned long count)
 		goto cleanout2;
 	}

-	nrt.rcounter = parse_num(buf);
+	nrt.rwrap = parse_num(buf);
+	if (out_of_range(0, nrt.rwrap, nrt.rnum)) {
+		err = -EINVAL;
+		goto cleanout2;
+	}

-	nrt.rules[nrt.rnum].counter = -1;
+	nrt.rules[nrt.rnum].target = MAX_JIFFY_OFFSET;
 	nrt.rules[nrt.rnum].type = parse_num(buf);
 	nrt.rules[nrt.rnum].data = parse_num(buf);

 	for (i = 0; i < nrt.rnum; i++) {
-		nrt.rules[i].counter = parse_num(buf);
-		if (nrt.rules[i].counter < 0) {
+		unsigned int msecs;
+
+		msecs = 100 * parse_num(buf);
+		if (out_of_range(0, msecs, 172800000 /* 48 hrs */)) {
 			err = -EINVAL;
 			goto cleanout2;
 		}

+		nrt.rules[i].target = msecs_to_jiffies(msecs);
 		nrt.rules[i].type = parse_num(buf);
 		nrt.rules[i].data = parse_num(buf);
 	}

 	sort(nrt.rules, nrt.rnum, sizeof (struct rule), cmp, swap);

-	nrt.raction = -1;
-
-	if (nrt.rcounter >= 0) {
-		for (i = 0; i < nrt.rnum; i++) {
-			if (nrt.rules[i].counter >= nrt.rcounter) {
-				nrt.raction = i;
-				break;
-			}
-		}
-	}
-
 	if (!tablecmp(&rt, &nrt)) {
 		err = count;
 		goto cleanout2;
@@ -237,7 +223,7 @@ int push_table(char *buf, unsigned long count)

 	memcpy(&rt, &nrt, sizeof (struct table));

-	err = start_monitor(get_hardware_id(rt.handle), idi, nrt.pace);
+	err = start_monitor(get_hardware_id(rt.handle), idi);
 	if (err < 0) {
 		goto cleanout3;
 	}
@@ -264,8 +250,7 @@ int pull_table(char **buf)
 		return -EFAULT;
 	}

-	b += sprintf(b, "%d\n%lu\n%d %d\n", rt.debug,
-		     (rt.pace * 10) / HZ, rt.dnum, rt.rnum);
+	b += sprintf(b, "%d\n%d %d\n", rt.debug, rt.dnum, rt.rnum);

 	for (i = 0; i < rt.dnum; i++) {
 		b += sprintf(b, "%d ", rt.devices[i]);
@@ -274,11 +259,12 @@ int pull_table(char **buf)
 	b--;

 	b += sprintf(b, "\n%d\n%d\n%d %d\n",
-		     rt.handle, rt.rcounter,
+		     rt.handle, rt.rwrap,
 		     rt.rules[rt.rnum].type, rt.rules[rt.rnum].data);

 	for (i = 0; i < rt.rnum; i++) {
-		b += sprintf(b, "%d %d %d\n", rt.rules[i].counter,
+		b += sprintf(b, "%d %d %d\n",
+			     jiffies_to_msecs(rt.rules[i].target) / 100,
 			     rt.rules[i].type, rt.rules[i].data);
 	}

@@ -291,5 +277,5 @@ void cleanup_table(void)
 	kfree(rt.rules);
 	memset(&rt, 0, sizeof (struct table));

-	counter = action = 0;
+	next_rule = 0;
 }
diff --git a/table.h b/table.h
index 413b8eb..71b19d2 100644
--- a/table.h
+++ b/table.h
@@ -25,29 +25,32 @@
 #include <acpi/acpi_bus.h>

 struct rule {
-	int counter;
+	unsigned long target; /* jiffies */
 	int type;
 	int data;
 };

 struct table {
 	int debug;
-	unsigned long pace;
 	int dnum, rnum;
 	int *devices;
 	int handle;
-	int rcounter, raction;
+	int rwrap;
 	struct rule *rules;
 };

+#define printd(fmt...)				\
+	if (unlikely(rt.debug)) {		\
+		printk(fmt);			\
+	}
+
 static inline int tablecmp(struct table *l, struct table *r)
 {
 	if (l->debug != r->debug ||
-	    l->pace != r->pace ||
 	    l->dnum != r->dnum ||
+	    l->rnum != r->rnum ||
 	    l->handle != r->handle ||
-	    l->rcounter != r->rcounter ||
-	    l->raction != r->raction) {
+	    l->rwrap != r->rwrap) {
 		return 1;
 	}

@@ -66,12 +69,10 @@ static inline int tablecmp(struct table *l, struct table *r)
 		    + rt.dnum * sizeof (int)			  \
 		    + rt.rnum * sizeof (struct rule))

-#define TABLE_BUFFER_SIZE (9 + rt.dnum + rt.rnum * 3 + (TABLE_SIZE << 3) / 3)
+#define TABLE_BUFFER_SIZE (8 + rt.dnum + rt.rnum * 3 + (TABLE_SIZE << 3) / 3)

 extern void occasionally_generate_event(struct acpi_device *acpi_device);
-extern void timely_generate_event(struct acpi_device *acpi_device, int interactions, unsigned long *notify);
-
-void simulate_interaction(void);
+extern unsigned long timely_generate_event(struct acpi_device *acpi_device, unsigned long last, unsigned long *notify);

 extern int push_table(char *buf, unsigned long count);
 extern int pull_table(char **buf);
--
1.4.4.4


[-- Attachment #3: Type: text/plain, Size: 111 bytes --]


-- 
Of one thing I am certain, the body is not the measure of healing - peace is
the measure. - George Melton

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-21 21:04   ` Jan Engelhardt
@ 2007-01-23  9:38     ` Pavel Machek
  0 siblings, 0 replies; 46+ messages in thread
From: Pavel Machek @ 2007-01-23  9:38 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Alessandro Di Marco, linux-kernel, vojtech

Hi!

> >While functionality is extremely interesting.... does it really have
> >to be in kernel?
> 
> I think so. While the X server grabs off any keyboard and mouse activity
> on its own, there is no such thing for the [read "all"] console[s].

I believe input subsystem can already do that.
							Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-22 12:46   ` Alessandro Di Marco
@ 2007-01-23  9:41     ` Pavel Machek
  2007-01-23 14:14       ` Alessandro Di Marco
  0 siblings, 1 reply; 46+ messages in thread
From: Pavel Machek @ 2007-01-23  9:41 UTC (permalink / raw)
  To: Alessandro Di Marco; +Cc: linux-kernel

Hi!

>    > +if [ ! -d "/proc/sin" ]; then
>    > +    echo "/proc/sin not found, has sinmod been loaded?"
>    > +    exit
>    > +fi
> 
>    No new /proc files, please.
> 
> This was merely a prototype realized in a hurry, not a production
> driver. Really, I did't think it could be interesting for anybody.
> 
> Would be /sys ok?

If it has to be in kernel, /sys/power is probably ok. But I still
believe it can be out.

Yes, I believe it is interesting: on modern machine, you want to
control keyboard illumination, lower backlight after short inactivity,
turn screen off after longer one, etc...

>    Heh. We'll waste power trying to save it.
> 
> Well, not just a power saver. For example I use SIN to auto-logoff my bash
> session as well (detaching the screen session.)

Ok, ok :-).

>    If you have to hook it into kernel, can you at least do it properly?
> 
> Of course. You can find attached a patch fixing this. Now SIN wakes up just
> when it expects to do something: if in the meantime the user interacts with the
> system, SIN simply recalculates the next wake-up time on the basis of the last
> user's activity date and goes to sleep again.

Good.
							Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-23  9:41     ` Pavel Machek
@ 2007-01-23 14:14       ` Alessandro Di Marco
  2007-01-23 16:34         ` Pavel Machek
  0 siblings, 1 reply; 46+ messages in thread
From: Alessandro Di Marco @ 2007-01-23 14:14 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-kernel

[-- Attachment #1: Type: text/plain, Size: 858 bytes --]

Pavel Machek <pavel@ucw.cz> writes:

   Hi!

   >    > +if [ ! -d "/proc/sin" ]; then
   >    > +    echo "/proc/sin not found, has sinmod been loaded?"
   >    > +    exit
   >    > +fi
   >
   >    No new /proc files, please.
   >
   > This was merely a prototype realized in a hurry, not a production
   > driver. Really, I did't think it could be interesting for anybody.
   >
   > Would be /sys ok?

   If it has to be in kernel, /sys/power is probably ok.

Doh! The attached patch introduces /sys/class/misc (along with some bug-fixes.)
In case I'll change it again.

Ah, there is still a time-warp bug pending. In practice I have to turn off the
timer just before suspending, hibernating, etc or it will give you hell on the
next system resume...

   But I still believe it can be out.

Do you believe it could be a user-space daemon or what?

Best,


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: sysfs.patch --]
[-- Type: text/x-patch, Size: 26729 bytes --]

>From f97e411d29d5771e3ac3d9c8e8aa4ad3ae0a27e2 Mon Sep 17 00:00:00 2001
From: Alessandro Di Marco <dmr@c0nc3pt.com>
Date: Tue, 23 Jan 2007 02:21:07 +0100
Subject: [PATCH] Added sysfs support; various timer bugfix, but time warp bug is still with us.

---
 Makefile           |    2 +-
 acpi_enumerator.c  |   27 +++++++--
 acpi_enumerator.h  |    5 +-
 gentable           |   17 +++---
 input_enumerator.c |   28 ++++++++--
 input_enumerator.h |    4 +-
 procfs.c           |  157 ++++++++++++++++++++--------------------------------
 procfs.h           |   12 ++--
 sin.c              |   86 ++++++++++++++++++++++++++--
 sin.h              |    9 ++-
 sysfs.c            |  146 ++++++++++++++++++++++++++++++++++++++++++++++++
 sysfs.h            |   28 +++++++++
 table.c            |  128 ++++++++++++++++++++++++++++--------------
 table.h            |    8 ++-
 14 files changed, 476 insertions(+), 181 deletions(-)

diff --git a/Makefile b/Makefile
index a72fb3c..d45fa58 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 MODLPATH = kernel/drivers/char
 
 MODL = sinmod
-OBJS = sin.o procfs.o table.o input_enumerator.o acpi_enumerator.o
+OBJS = sin.o procfs.o sysfs.o table.o input_enumerator.o acpi_enumerator.o
 
 SRCS := $(patsubst %.o,%.c,$(OBJS))
 HDRS := $(patsubst %.o,%.h,$(OBJS))
diff --git a/acpi_enumerator.c b/acpi_enumerator.c
index c9033e5..c25a268 100644
--- a/acpi_enumerator.c
+++ b/acpi_enumerator.c
@@ -35,6 +35,12 @@ int get_handlers(void)
 	return ahsize;
 }
 
+size_t get_handlers_desc(char **buf)
+{
+	*buf = page;
+	return size;
+}
+
 char *get_hardware_id(int handle)
 {
 	return ah[handle].hardware_id;
@@ -75,11 +81,15 @@ static int acpi_show(struct acpi_device *device, struct acpi_driver *driver)
 	return -ENOENT;
 }
 
-int acpi_enum(char *buf)
+int acpi_enum(void)
 {
 	struct acpi_driver ad;
+	int err = -ENOMEM;
 
-	page = buf;
+	page = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!page) {
+		goto out;
+	}
 
 	ad.ops.match = acpi_show;
 	(void) acpi_bus_register_driver(&ad);
@@ -87,7 +97,8 @@ int acpi_enum(char *buf)
 
 	if (!ahsize) {
 		printk(KERN_NOTICE "no acpi handlers found\n");
-		return -ENODEV;
+		err = -ENODEV;
+		goto cleanout;
 	}
 
 	ah = kmalloc(ahsize * sizeof (struct acpi_handlers), GFP_KERNEL);
@@ -95,14 +106,19 @@ int acpi_enum(char *buf)
 	ahsize = 0;
 
 	if (!ah) {
-		return -ENOMEM;
+		goto cleanout;
 	}
 
 	ad.ops.match = acpi_store;
 	(void) acpi_bus_register_driver(&ad);
 	acpi_bus_unregister_driver(&ad);
 
-	return size;
+	return 0;
+
+cleanout:
+	kfree(page);
+out:
+	return err;
 }
 
 void free_acpi_enum(void)
@@ -110,5 +126,6 @@ void free_acpi_enum(void)
 	if (ahsize) {
 		ahsize = 0;
 		kfree(ah);
+		kfree(page);
 	}
 }
diff --git a/acpi_enumerator.h b/acpi_enumerator.h
index 998db65..0b75a09 100644
--- a/acpi_enumerator.h
+++ b/acpi_enumerator.h
@@ -29,8 +29,11 @@ struct acpi_handlers {
 };
 
 extern int get_handlers(void);
+extern size_t get_handlers_desc(char **buf);
+
 extern char *get_hardware_id(int handle);
-extern int acpi_enum(char *page);
+
+extern int acpi_enum(void);
 extern void free_acpi_enum(void);
 
 #endif /* ACPI_ENUMERATOR_H */
diff --git a/gentable b/gentable
index 3a322df..49af1f4 100755
--- a/gentable
+++ b/gentable
@@ -119,22 +119,21 @@ interaction occurs when SIN, as well as the kernel, cannot capture it. As a
 consequence, no event will ever be generated and the system will remain in the
 state associated with the next-to-last rule (e.g. blanked screen, wireless
 powered off, etc.). The next option allows you to request a special event,
-resetting the global counter to an arbitrary value, so to restart the rule-list
-evaluation.  Possible value ranges are described below, where N is the maximum
-counter in the current rule list:
+restarting the rule-list evaluation from an arbitrary position.  Possible value
+ranges are described below, where N is the rule-list size:
 
-    [0, N]    => reset the global counter to the specified value
-    otherwise => do nothing, the global counter goes on and on and on...
+    [0, N - 1]	=> jump to the given rule
+    N		=> go to sleep and wait for user interaction
 
 EOF
 
-input "Reset value?" reset
+input "Wrap value?" wrap
 
-if [ -z "${reset}" ]; then
-    reset="-1"
+if [ -z "${wrap}" ]; then
+    wrap="-1"
 fi
 
-echo -e "0\n${#devices[@]} ${#rules[@]}\n${devices[@]}\n${handle}\n${reset}\n${resume}" > $1
+echo -e "0\n${#devices[@]} ${#rules[@]}\n${devices[@]}\n${handle}\n${wrap}\n${resume}" > $1
 
 for (( i = 0; ${i}<${#rules[@]}; i++ )); do
     echo "${rules[${i}]}" >> $1
diff --git a/input_enumerator.c b/input_enumerator.c
index b046cb4..6fd7827 100644
--- a/input_enumerator.c
+++ b/input_enumerator.c
@@ -35,6 +35,12 @@ int get_devices(void)
 	return idsize;
 }
 
+size_t get_devices_desc(char **buf)
+{
+	*buf = page;
+	return size;
+}
+
 void fill_input_device(struct input_device_id *idi, int device)
 {
 	idi->flags = MATCH_MODEL;
@@ -135,8 +141,10 @@ skip:
 	return NULL;
 }
 
-int input_enum(char *buf)
+int input_enum(void)
 {
+	int err = -ENOMEM;
+
 	const struct input_device_id idi[] = {
 		{ .driver_info = 1 },	/* matches all devices */
 		{ },
@@ -147,14 +155,18 @@ int input_enum(char *buf)
 		.id_table =	idi,
 	};
 
-	page = buf;
+	page = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!page) {
+		goto out;
+	}
 
 	ih.connect = input_show;
 	(void) input_register_handler(&ih);
 
 	if (!idsize) {
 		printk(KERN_NOTICE "no input devices found\n");
-		return -ENODEV;
+		err = -ENODEV;
+		goto cleanout;
 	}
 
 	id = kmalloc(idsize * sizeof (struct input_devices), GFP_KERNEL);
@@ -162,13 +174,18 @@ int input_enum(char *buf)
 	idsize = 0;
 
 	if (!id) {
-		return -ENOMEM;
+		goto cleanout;
 	}
 
 	ih.connect = input_store;
 	(void) input_register_handler(&ih);
 
-	return size;
+	return 0;
+
+cleanout:
+	kfree(page);
+out:
+	return err;
 }
 
 void free_input_enum(void)
@@ -176,5 +193,6 @@ void free_input_enum(void)
 	if (idsize) {
 		idsize = 0;
 		kfree(id);
+		kfree(page);
 	}
 }
diff --git a/input_enumerator.h b/input_enumerator.h
index 0e87207..45ffe93 100644
--- a/input_enumerator.h
+++ b/input_enumerator.h
@@ -37,9 +37,11 @@ struct input_devices {
 		     |INPUT_DEVICE_ID_MATCH_VERSION)
 
 extern int get_devices(void);
+extern size_t get_devices_desc(char **buf);
+
 extern void fill_input_device(struct input_device_id *idi, int i);
 
-extern int input_enum(char *page);
+extern int input_enum(void);
 extern void free_input_enum(void);
 
 #endif /* INPUT_ENUMERATOR_H */
diff --git a/procfs.c b/procfs.c
index 5929f90..7f42dde 100644
--- a/procfs.c
+++ b/procfs.c
@@ -29,7 +29,7 @@
 #include "acpi_enumerator.h"
 #include "input_enumerator.h"
 
-static struct procfs_bs ibs, abs, tbs;
+static struct procfs_bs ibs, abs;
 static struct proc_dir_entry *rootdir;
 
 static int read_proc(char *page, char **start,
@@ -44,21 +44,61 @@ static int read_proc(char *page, char **start,
 
 	memcpy(page + off, bs->page, count);
 
-	if (!count) {
+	if (count == left) {
 		*eof = 1;
 	}
 
 	return off + count;
 }
 
-static int write_proc(struct file *file, const __user char *ubuf,
-		      unsigned long count, void *data)
+static int read_table_proc(char *page, char **start,
+			   off_t off, int count, int *eof, void *data)
+{
+	char *buf;
+	size_t size;
+
+	int left;
+	int err = -ENOMEM;
+
+	size = pull_table(&buf);
+	if (!buf) {
+		goto out;
+	}
+
+	if (size > PAGE_SIZE) {
+		size = PAGE_SIZE;
+	}
+
+	left = size - off;
+	if (left < 0) {
+		err = -ESPIPE;
+		goto out;
+	}
+
+	if (count > left) {
+		count = left;
+	}
+
+	memcpy(page + off, buf, count);
+
+	if (count == left) {
+		page[size] = '\0';
+		*eof = 1;
+	}
+
+	return off + count;
+
+out:
+	return err;
+}
+
+static int write_table_proc(struct file *file, const __user char *ubuf,
+			    unsigned long count, void *data)
 {
-	struct procfs_bs *bs = data;
 	char *buf;
 	int err;
 
-	buf = kmalloc(count * sizeof (char), GFP_KERNEL);
+	buf = kmalloc(count, GFP_KERNEL);
 	if (!buf) {
 		err = -ENOMEM;
 		goto out;
@@ -72,16 +112,6 @@ static int write_proc(struct file *file, const __user char *ubuf,
 	buf[count] = '\0';
 
 	err = push_table(buf, count);
-	if (err < 0) {
-		goto cleanout;
-	}
-
-	kfree(bs->page);
-
-	bs->size = pull_table(&bs->page);
-	if (bs->size < 0) {
-		err = bs->size;
-	}
 
 cleanout:
 	kfree(buf);
@@ -100,45 +130,15 @@ static int fake_write_proc(struct file *file, const __user char *ubuf,
 
 int start_procfs(void)
 {
-	struct proc_dir_entry *inputd, *acpid,
-		*table, *interact, *ilink, *alink;
-
+	struct proc_dir_entry *inputd, *acpid, *table, *interact;
 	int err = -ENOMEM;
 
-	ibs.page = kmalloc(PAGE_SIZE, GFP_KERNEL);
-	if (!ibs.page) {
-		goto out;
-	}
-
-	ibs.size = input_enum(ibs.page);
-	if (ibs.size < 0) {
-		err = ibs.size;
-		goto cleanout1;
-	}
-
-	abs.page = kmalloc(PAGE_SIZE, GFP_KERNEL);
-	if (!abs.page) {
-		goto cleanout2;
-	}
-
-	abs.size = acpi_enum(abs.page);
-	if (abs.size < 0) {
-		err = abs.size;
-		goto cleanout3;
-	}
-
-	tbs.size = sizeof RT_HELP;
-
-	tbs.page = kmalloc(tbs.size, GFP_KERNEL);
-	if (!tbs.page) {
-		goto cleanout4;
-	}
-
-	memcpy(tbs.page, RT_HELP, tbs.size);
+	ibs.size = get_devices_desc(&ibs.page);
+	abs.size = get_handlers_desc(&abs.page);
 
 	rootdir = proc_mkdir(MODULE_NAME, NULL);
 	if (!rootdir) {
-		goto cleanout5;
+		goto out;
 	}
 
 	rootdir->owner = THIS_MODULE;
@@ -146,7 +146,7 @@ int start_procfs(void)
 	inputd = create_proc_read_entry("input", 0444,
 					rootdir, read_proc, &ibs);
 	if (!inputd) {
-		goto cleanout6;
+		goto cleanout1;
 	}
 
 	inputd->owner = THIS_MODULE;
@@ -154,84 +154,49 @@ int start_procfs(void)
 	acpid = create_proc_read_entry("acpi", 0444,
 				       rootdir, read_proc, &abs);
 	if (!acpid) {
-		goto cleanout7;
+		goto cleanout2;
 	}
 
 	acpid->owner = THIS_MODULE;
 
 	table = create_proc_entry("table", 0644, rootdir);
 	if (!table) {
-		goto cleanout8;
+		goto cleanout3;
 	}
 
-	table->data = &tbs;
-	table->read_proc = read_proc;
-	table->write_proc = write_proc;
+	table->data = NULL;
+	table->read_proc = read_table_proc;
+	table->write_proc = write_table_proc;
 	table->owner = THIS_MODULE;
 
 	interact = create_proc_entry("interact", 0200, rootdir);
 	if (!interact) {
-		goto cleanout9;
+		goto cleanout4;
 	}
 
 	interact->data = (void *) simulate_event;
 	interact->write_proc = fake_write_proc;
 	interact->owner = THIS_MODULE;
 
-	ilink = proc_symlink("sources", rootdir, "input");
-	if (!ilink) {
-		goto cleanout10;
-	}
-
-	ilink->owner = THIS_MODULE;
-
-	alink = proc_symlink("destinations", rootdir, "acpi");
-	if (!alink) {
-		goto cleanout11;
-	}
-
-	alink->owner = THIS_MODULE;
-
 	return 0;
 
-cleanout11:
-	remove_proc_entry("sources", rootdir);
-cleanout10:
-	remove_proc_entry("interact", rootdir);
-cleanout9:
-	remove_proc_entry("table", rootdir);
-cleanout8:
-	remove_proc_entry("acpi", rootdir);
-cleanout7:
-	remove_proc_entry("input", rootdir);
-cleanout6:
-	remove_proc_entry(MODULE_NAME, NULL);
-cleanout5:
-	kfree(tbs.page);
 cleanout4:
-	free_acpi_enum();
+	remove_proc_entry("table", rootdir);
 cleanout3:
-	kfree(abs.page);
+	remove_proc_entry("acpi", rootdir);
 cleanout2:
-	free_input_enum();
+	remove_proc_entry("input", rootdir);
 cleanout1:
-	kfree(ibs.page);
+	remove_proc_entry(MODULE_NAME, NULL);
 out:
 	return err;
 }
 
 void stop_procfs(void)
 {
-	remove_proc_entry("destinations", rootdir);
-	remove_proc_entry("sources", rootdir);
 	remove_proc_entry("interact", rootdir);
 	remove_proc_entry("table", rootdir);
 	remove_proc_entry("acpi", rootdir);
 	remove_proc_entry("input", rootdir);
 	remove_proc_entry(MODULE_NAME, NULL);
-	kfree(tbs.page);
-	free_acpi_enum();
-	kfree(abs.page);
-	free_input_enum();
-	kfree(ibs.page);
 }
diff --git a/procfs.h b/procfs.h
index 890d1ce..ea164be 100644
--- a/procfs.h
+++ b/procfs.h
@@ -3,27 +3,25 @@
  */
 
 /*
- *  This file is part of Procfs.
+ *  This file is part of SIN.
  *
- *  Procfs is free software; you can redistribute it and/or modify it under the
+ *  SIN is free software; you can redistribute it and/or modify it under the
  *  terms of the GNU General Public License as published by the Free Software
  *  Foundation; version 2 of the License.
  *
- *  Procfs is distributed in the hope that it will be useful, but WITHOUT ANY
+ *  SIN 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 General Public License for more
  *  details.
  *
  *  You should have received a copy of the GNU General Public License along
- *  with Procfs; if not, write to the Free Software Foundation, Inc., 51
- *  Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *  with SIN; if not, write to the Free Software Foundation, Inc., 51 Franklin
+ *  St, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
 #ifndef PROCFS_H
 #define PROCFS_H
 
-#define RT_HELP "<debug>\n<pace>\n<N> <M>\n<input1> ... <inputN>\n<acpi>\n<reset>\n<special type> <special data>\n<counter1> <type1> <data1>\n...\n<counterM> <typeM> <dataM>\n"
-
 struct procfs_bs {
 	char *page;
 	int size;
diff --git a/sin.c b/sin.c
index 0d9b9c4..c3633ff 100644
--- a/sin.c
+++ b/sin.c
@@ -23,16 +23,20 @@
 #include <linux/input.h>
 #include <linux/acpi.h>
 #include <linux/mutex.h>
+#include <linux/miscdevice.h>
 
 #include "sin.h"
 #include "table.h"
 #include "procfs.h"
+#include "sysfs.h"
+#include "acpi_enumerator.h"
+#include "input_enumerator.h"
 
 MODULE_AUTHOR("Alessandro Di Marco <dmr@gmx.it>");
 MODULE_DESCRIPTION("System Inactivity Notifier");
 MODULE_LICENSE("GPL v2");
 
-MODULE_VERSION("1.3");
+MODULE_VERSION("1.4");
 
 static struct acpi_device *acpi_device;
 
@@ -60,8 +64,19 @@ inline void signal_interaction(void)
 	if (unlikely(test_bit(RULE_LOCK, &status))) {
 		set_bit(RULE_MARK, &status);
 	} else if (unlikely(test_and_clear_bit(RULE_TRIG, &status))) {
+		unsigned long next;
+
+		WARN_ON(test_bit(RULE_OVER, &status) && timer_pending(&timer));
+
 		clear_bit(RULE_WRAP, &status);
-		occasionally_generate_event(acpi_device);
+		clear_bit(RULE_OVER, &status);
+
+		next = occasionally_generate_event(acpi_device,
+						   last_activity(&uact));
+
+		if (!shutdown) {
+			(void) mod_timer(&timer, next);
+		}
 	}
 }
 
@@ -118,8 +133,10 @@ void timer_fn(unsigned long data)
 			signal_interaction();
 		}
 
-		timer.expires = next;
-		add_timer(&timer);
+		if (!test_and_clear_bit(RULE_OVER, &status)) {
+			timer.expires = next;
+			add_timer(&timer);
+		}
 	}
 }
 
@@ -200,16 +217,73 @@ void stop_monitor(void)
 	mutex_unlock(&runlock);
 }
 
+static const struct file_operations sin_miscfops = {
+	.owner		= THIS_MODULE,
+};
+
+static struct miscdevice sin_miscdev = {
+	.minor		= MISC_DYNAMIC_MINOR,
+	.name		= MODULE_NAME,
+	.fops		= &sin_miscfops,
+};
+
 static int __init sih_init(void)
 {
-	printk("System Inactivity Notifier 1.3 - (c) Alessandro Di Marco <dmr@c0nc3pt.com>\n");
-	return start_procfs();
+	int err;
+
+	err = misc_register(&sin_miscdev);
+	if (err < 0) {
+		goto out;
+	}
+
+	err = input_enum();
+	if (err < 0) {
+		goto cleanout1;
+	}
+
+	err = acpi_enum();
+	if (err < 0) {
+		goto cleanout2;
+	}
+
+	err = start_sysfs(sin_miscdev.this_device);
+	if (err < 0) {
+		printk(KERN_ERR "sin: sysfs initialization failed\n");
+		goto cleanout3;
+	}
+
+	err = start_procfs();
+	if (err < 0) {
+		printk(KERN_ERR "sin: procfs initialization failed\n");
+		goto cleanout4;
+	}
+
+	printk("System Inactivity Notifier 1.4 - (c) Alessandro Di Marco <dmr@gmx.it>\n");
+
+	return 0;
+
+cleanout4:
+	stop_sysfs();
+cleanout3:
+	free_acpi_enum();
+cleanout2:
+	free_input_enum();
+cleanout1:
+	misc_deregister(&sin_miscdev);
+out:
+	return err;
 }
 
 static void __exit sih_exit(void)
 {
 	stop_procfs();
+	stop_sysfs();
+	free_acpi_enum();
+	free_input_enum();
+
 	(void) stop_monitor();
+
+	misc_deregister(&sin_miscdev);
 }
 
 module_init(sih_init);
diff --git a/sin.h b/sin.h
index 95161b2..1ef52d2 100644
--- a/sin.h
+++ b/sin.h
@@ -26,10 +26,11 @@
 
 #define MODULE_NAME "sin"
 
-#define RULE_TRIG 0
-#define RULE_WRAP 1
-#define RULE_LOCK 2
-#define RULE_MARK 3
+#define RULE_TRIG 0 /* a rule has been triggered */
+#define RULE_WRAP 1 /* rule-evaluation has been restarted */
+#define RULE_LOCK 2 /* evaluating rule-list, don't mess */
+#define RULE_MARK 3 /* event seen while evaluating rule-list */
+#define RULE_OVER 4 /* no more rules, please disconnect timer */
 
 struct user_activity {
 	spinlock_t lock;
diff --git a/sysfs.c b/sysfs.c
new file mode 100644
index 0000000..28004a4
--- /dev/null
+++ b/sysfs.c
@@ -0,0 +1,146 @@
+/*
+ *  Copyright (C) 2007 Alessandro Di Marco
+ */
+
+/*
+ *  This file is part of SIN.
+ *
+ *  SIN is free software; you can redistribute it and/or modify it under the
+ *  terms of the GNU General Public License as published by the Free Software
+ *  Foundation; version 2 of the License.
+ *
+ *  SIN 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 General Public License for more
+ *  details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with SIN; if not, write to the Free Software Foundation, Inc., 51 Franklin
+ *  St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/device.h>
+#include <linux/err.h>
+
+#include "sin.h"
+#include "table.h"
+#include "sysfs.h"
+#include "acpi_enumerator.h"
+#include "input_enumerator.h"
+
+static ssize_t show_input(struct device *dev,
+			  struct device_attribute *attr, char *pbuf)
+{
+	char *buf;
+	size_t size;
+
+	size = get_devices_desc(&buf);
+
+	memcpy(pbuf, buf, size);
+
+	return size;
+}
+
+static DEVICE_ATTR(input, 0444, show_input, NULL);
+
+static ssize_t show_acpi(struct device *dev,
+			 struct device_attribute *attr, char *pbuf)
+{
+	char *buf;
+	size_t size;
+
+	size = get_handlers_desc(&buf);
+
+	memcpy(pbuf, buf, size);
+
+	return size;
+}
+
+static DEVICE_ATTR(acpi, 0444, show_acpi, NULL);
+
+static ssize_t show_table(struct device *dev,
+			  struct device_attribute *attr, char *pbuf)
+{
+	char *buf;
+	size_t size;
+
+	size = pull_table(&buf);
+	if (buf) {
+		if (size > PAGE_SIZE) {
+			size = PAGE_SIZE;
+		}
+
+		memcpy(pbuf, buf, size);
+		pbuf[size] = '\0';
+	}
+
+	return size;
+}
+
+static ssize_t store_table(struct device *dev, struct device_attribute *attr,
+			   const char *pbuf, size_t count)
+{
+	return push_table(pbuf, count);
+}
+
+static DEVICE_ATTR(table, 0644, show_table, store_table);
+
+static ssize_t store_interact(struct device *dev,
+			      struct device_attribute *attr,
+			      const char *pbuf, size_t count)
+{
+	simulate_event();
+	return count;
+}
+
+static DEVICE_ATTR(interact, 0200, NULL, store_interact);
+
+static struct device *sindev;
+
+int start_sysfs(struct device *dev)
+{
+	int err;
+
+	sindev = dev;
+
+	err = device_create_file(sindev, &dev_attr_input);
+	if (err < 0) {
+		goto out;
+	}
+
+	err = device_create_file(sindev, &dev_attr_acpi);
+	if (err < 0) {
+		goto cleanout1;
+	}
+
+	err = device_create_file(sindev, &dev_attr_table);
+	if (err < 0) {
+		goto cleanout2;
+	}
+
+	err = device_create_file(sindev, &dev_attr_interact);
+	if (err < 0) {
+		goto cleanout3;
+	}
+
+	return 0;
+
+cleanout3:
+	device_remove_file(sindev, &dev_attr_table);
+cleanout2:
+	device_remove_file(sindev, &dev_attr_acpi);
+cleanout1:
+	device_remove_file(sindev, &dev_attr_input);
+out:
+	return err;
+}
+
+void stop_sysfs(void)
+{
+	device_remove_file(sindev, &dev_attr_interact);
+	device_remove_file(sindev, &dev_attr_table);
+	device_remove_file(sindev, &dev_attr_acpi);
+	device_remove_file(sindev, &dev_attr_input);
+}
diff --git a/sysfs.h b/sysfs.h
new file mode 100644
index 0000000..465258a
--- /dev/null
+++ b/sysfs.h
@@ -0,0 +1,28 @@
+/*
+ *  Copyright (C) 2007 Alessandro Di Marco
+ */
+
+/*
+ *  This file is part of SIN.
+ *
+ *  SIN is free software; you can redistribute it and/or modify it under the
+ *  terms of the GNU General Public License as published by the Free Software
+ *  Foundation; version 2 of the License.
+ *
+ *  SIN 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 General Public License for more
+ *  details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with SIN; if not, write to the Free Software Foundation, Inc., 51 Franklin
+ *  St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef SYSFS_H
+#define SYSFS_H
+
+extern int start_sysfs(struct device *dev);
+extern void stop_sysfs(void);
+
+#endif /* SYSFS_H */
diff --git a/table.c b/table.c
index 658636f..b51e905 100644
--- a/table.c
+++ b/table.c
@@ -27,7 +27,6 @@
 #include "sin.h"
 #include "uniq.h"
 #include "table.h"
-
 #include "input_enumerator.h"
 #include "acpi_enumerator.h"
 
@@ -40,7 +39,8 @@ static int next_rule;
  * event service routines (e.g. /etc/acpid/default.sh).
  */
 
-void occasionally_generate_event(struct acpi_device *acpi_device)
+unsigned long occasionally_generate_event(struct acpi_device *acpi_device,
+					  unsigned long last)
 {
 	printd("generating special event [%d, %d]\n",
 	       rt.rules[rt.rnum].type, rt.rules[rt.rnum].data);
@@ -48,7 +48,7 @@ void occasionally_generate_event(struct acpi_device *acpi_device)
 	(void) acpi_bus_generate_event(acpi_device, rt.rules[rt.rnum].type,
 				       rt.rules[rt.rnum].data);
 
-	next_rule = 0;
+	return last + rt.rules[next_rule = 0].target;
 }
 
 unsigned long timely_generate_event(struct acpi_device *acpi_device,
@@ -78,26 +78,31 @@ unsigned long timely_generate_event(struct acpi_device *acpi_device,
 		set_bit(RULE_TRIG, status);
 	}
 
-	if (rt.rwrap >= 0 && next_rule == rt.rnum) {
-		printd("last rule reached, restarting from %d\n", rt.rwrap);
+	if (next_rule == rt.rnum) {
+		if (rt.rwrap < rt.rnum) {
+			printd("last rule, restarting from %d\n", rt.rwrap);
 
-		next_rule = rt.rwrap;
-		set_bit(RULE_WRAP, status);
+			next_rule = rt.rwrap;
+			set_bit(RULE_WRAP, status);
 
-		last = simulate_activity();
+			last = simulate_activity();
+		} else {
+			printd("reached the last rule, disconnecting timer\n");
+			set_bit(RULE_OVER, status);
+		}
 	}
 
 	return last + rt.rules[next_rule].target;
 }
 
-#define parse_num(endp) ({				\
-			char *cp = endp;		\
-							\
-			while (*cp && isspace(*cp)) {	\
-				++cp;			\
-			}				\
-							\
-			simple_strtol(cp, &endp, 10);	\
+#define parse_num(endp) ({					\
+			const char *cp = endp;			\
+								\
+			while (*cp && isspace(*cp)) {		\
+				++cp;				\
+			}					\
+								\
+			simple_strtol(cp, (char **) &endp, 10);	\
 		})
 
 static int cmp(const void *l, const void *r)
@@ -117,7 +122,45 @@ static void swap(void *l, void *r, int size)
 	*((struct rule *) r) = t;
 }
 
-int push_table(char *buf, unsigned long count)
+static char *table;
+static size_t size;
+
+static int regen_table(void)
+{
+	char *t;
+	int i;
+
+	kfree(table);
+
+	table = t = kmalloc(TABLE_BUFFER_SIZE, GFP_KERNEL);
+	if (!table) {
+		return -EFAULT;
+	}
+
+	t += sprintf(t, "%d\n%d %d\n", rt.debug, rt.dnum, rt.rnum);
+
+	for (i = 0; i < rt.dnum; i++) {
+		t += sprintf(t, "%d ", rt.devices[i]);
+	}
+
+	t--;
+
+	t += sprintf(t, "\n%d\n%d\n%d %d\n",
+		     rt.handle, rt.rwrap,
+		     rt.rules[rt.rnum].type, rt.rules[rt.rnum].data);
+
+	for (i = 0; i < rt.rnum; i++) {
+		t += sprintf(t, "%d %d %d\n",
+			     jiffies_to_msecs(rt.rules[i].target) / 100,
+			     rt.rules[i].type, rt.rules[i].data);
+	}
+
+	size = t - table;
+
+	return 0;
+}
+
+int push_table(const char *buf, unsigned long count)
 {
 	struct table nrt;
 	struct input_device_id *idi;
@@ -179,7 +222,7 @@ int push_table(char *buf, unsigned long count)
 	}
 
 	nrt.rwrap = parse_num(buf);
-	if (out_of_range(0, nrt.rwrap, nrt.rnum)) {
+	if (out_of_range(0, nrt.rwrap, nrt.rnum + 1)) {
 		err = -EINVAL;
 		goto cleanout2;
 	}
@@ -223,6 +266,11 @@ int push_table(char *buf, unsigned long count)
 
 	memcpy(&rt, &nrt, sizeof (struct table));
 
+	err = regen_table();
+	if (err < 0) {
+		goto cleanout3;
+	}
+
 	err = start_monitor(get_hardware_id(rt.handle), idi);
 	if (err < 0) {
 		goto cleanout3;
@@ -232,6 +280,9 @@ int push_table(char *buf, unsigned long count)
 
 cleanout3:
 	kfree(idi);
+	cleanup_table();
+	goto out;
+
 cleanout2:
 	kfree(nrt.rules);
 cleanout1:
@@ -240,42 +291,33 @@ out:
 	return err;
 }
 
-int pull_table(char **buf)
+size_t pull_table(char **buf)
 {
-	char *b;
-	int i;
-
-	*buf = b = kmalloc(TABLE_BUFFER_SIZE, GFP_KERNEL);
-	if (!b) {
-		return -EFAULT;
-	}
-
-	b += sprintf(b, "%d\n%d %d\n", rt.debug, rt.dnum, rt.rnum);
-
-	for (i = 0; i < rt.dnum; i++) {
-		b += sprintf(b, "%d ", rt.devices[i]);
+	if (!table) {
+		size = sizeof TABLE_HELP;
+
+		table = kmalloc(size, GFP_KERNEL);
+		if (table) {
+			memcpy(table, TABLE_HELP, size);
+		} else {
+			size = 0;
+		}
 	}
 
-	b--;
-
-	b += sprintf(b, "\n%d\n%d\n%d %d\n",
-		     rt.handle, rt.rwrap,
-		     rt.rules[rt.rnum].type, rt.rules[rt.rnum].data);
-
-	for (i = 0; i < rt.rnum; i++) {
-		b += sprintf(b, "%d %d %d\n",
-			     jiffies_to_msecs(rt.rules[i].target) / 100,
-			     rt.rules[i].type, rt.rules[i].data);
-	}
+	*buf = table;
 
-	return b - *buf;
+	return size;
 }
 
 void cleanup_table(void)
 {
 	kfree(rt.devices);
 	kfree(rt.rules);
+
 	memset(&rt, 0, sizeof (struct table));
 
 	next_rule = 0;
+
+	kfree(table);
+	table = NULL;
 }
diff --git a/table.h b/table.h
index 71b19d2..fd6b91f 100644
--- a/table.h
+++ b/table.h
@@ -65,17 +65,19 @@ static inline int tablecmp(struct table *l, struct table *r)
 	return 0;
 }
 
+#define TABLE_HELP "<debug>\n<N> <M>\n<input1> ... <inputN>\n<acpi>\n<wrap>\n<special type> <special data>\n<trigger1> <type1> <data1>\n...\n<triggerM> <typeM> <dataM>\n"
+
 #define TABLE_SIZE (sizeof (struct table) - 2 * sizeof (void *)	  \
 		    + rt.dnum * sizeof (int)			  \
 		    + rt.rnum * sizeof (struct rule))
 
 #define TABLE_BUFFER_SIZE (8 + rt.dnum + rt.rnum * 3 + (TABLE_SIZE << 3) / 3)
 
-extern void occasionally_generate_event(struct acpi_device *acpi_device);
+extern unsigned long occasionally_generate_event(struct acpi_device *acpi_device, unsigned long last);
 extern unsigned long timely_generate_event(struct acpi_device *acpi_device, unsigned long last, unsigned long *notify);
 
-extern int push_table(char *buf, unsigned long count);
-extern int pull_table(char **buf);
+extern int push_table(const char *buf, unsigned long count);
+extern size_t pull_table(char **buf);
 extern void cleanup_table(void);
 
 #endif /* TABLE_H */
-- 
1.4.4.4


[-- Attachment #3: Type: text/plain, Size: 108 bytes --]


--
As you grow older, you'll find the only things you regret are the things you
didn't do. - Zachary Scott

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-23 14:14       ` Alessandro Di Marco
@ 2007-01-23 16:34         ` Pavel Machek
  2007-01-23 17:11           ` Alessandro Di Marco
                             ` (2 more replies)
  0 siblings, 3 replies; 46+ messages in thread
From: Pavel Machek @ 2007-01-23 16:34 UTC (permalink / raw)
  To: Alessandro Di Marco; +Cc: linux-kernel, vojtech

Hi1

>    >    > +if [ ! -d "/proc/sin" ]; then
>    >    > +    echo "/proc/sin not found, has sinmod been loaded?"
>    >    > +    exit
>    >    > +fi
>    >
>    >    No new /proc files, please.
>    >
>    > This was merely a prototype realized in a hurry, not a production
>    > driver. Really, I did't think it could be interesting for anybody.
>    >
>    > Would be /sys ok?
> 
>    If it has to be in kernel, /sys/power is probably ok.
> 
> Doh! The attached patch introduces /sys/class/misc (along with some bug-fixes.)
> In case I'll change it again.

No, just leave it where it is. Maybe better location will be invented,
but mine idea was just first shot.

>    But I still believe it can be out.
> 
> Do you believe it could be a user-space daemon or what?

Yes, what prevents userspace daemon watching /dev/input/event* to
provide this functionality?
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-23 16:34         ` Pavel Machek
@ 2007-01-23 17:11           ` Alessandro Di Marco
  2007-01-23 18:44             ` Pavel Machek
  2007-01-23 19:01           ` Mattia Dongili
  2007-01-23 19:34           ` Scott Preece
  2 siblings, 1 reply; 46+ messages in thread
From: Alessandro Di Marco @ 2007-01-23 17:11 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-kernel, vojtech

Pavel Machek <pavel@ucw.cz> writes:

   >    But I still believe it can be out.
   >
   > Do you believe it could be a user-space daemon or what?

   Yes, what prevents userspace daemon watching /dev/input/event* to
   provide this functionality?

Well that was my first attempt. Just an hack, but it works. Nonetheless I found
the kernel-level approach fitting better the problem: it is elegant, simpler
and more efficient than user-level one.

For example, the subsystem provides at kernel-level handy
hiberante/suspend/resume callbacks that I use to turn on/off the timer,
avoiding the time-warp problem. Doing that at user-level would be far more
messy...

Bye

--
An age is called Dark not because the light fails to shine, but because people
refuse to see it. - James Michener, Space

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-23 17:11           ` Alessandro Di Marco
@ 2007-01-23 18:44             ` Pavel Machek
  2007-01-24  2:51               ` Alessandro Di Marco
  2007-01-24 18:08               ` Alessandro Di Marco
  0 siblings, 2 replies; 46+ messages in thread
From: Pavel Machek @ 2007-01-23 18:44 UTC (permalink / raw)
  To: Alessandro Di Marco; +Cc: linux-kernel, vojtech

Hi!

>    >    But I still believe it can be out.
>    >
>    > Do you believe it could be a user-space daemon or what?
> 
>    Yes, what prevents userspace daemon watching /dev/input/event* to
>    provide this functionality?
> 
> Well that was my first attempt. Just an hack, but it works. Nonetheless I found
> the kernel-level approach fitting better the problem: it is elegant, simpler
> and more efficient than user-level one.

Well, unfortunately it is also wrong thing to do :-(.

> For example, the subsystem provides at kernel-level handy
> hiberante/suspend/resume callbacks that I use to turn on/off the timer,
> avoiding the time-warp problem. Doing that at user-level would be far more
> messy...

Imagine for a moment that we solve time-warp somehow. Any other
problems? [I'd really like to get "is user idle" solved, but it really
should not be in kernel unless it _has_ to. And time-warp probably
causes problems not only for your daemon.]
								Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-23 16:34         ` Pavel Machek
  2007-01-23 17:11           ` Alessandro Di Marco
@ 2007-01-23 19:01           ` Mattia Dongili
  2007-01-23 19:02             ` Pavel Machek
  2007-01-23 19:34           ` Scott Preece
  2 siblings, 1 reply; 46+ messages in thread
From: Mattia Dongili @ 2007-01-23 19:01 UTC (permalink / raw)
  To: Pavel Machek; +Cc: Alessandro Di Marco, linux-kernel, vojtech

On Tue, Jan 23, 2007 at 05:34:42PM +0100, Pavel Machek wrote:
[...]
> > Do you believe it could be a user-space daemon or what?
> 
> Yes, what prevents userspace daemon watching /dev/input/event* to
> provide this functionality?

hmmm... EVIOCGRAB for example? the synaptics Xorg driver is using
it, maybe others too?
I don't want to start a new discussion about it, just provide an answer
to your question :)

-- 
mattia
:wq!

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-23 19:01           ` Mattia Dongili
@ 2007-01-23 19:02             ` Pavel Machek
  2007-01-23 20:07               ` Mattia Dongili
  0 siblings, 1 reply; 46+ messages in thread
From: Pavel Machek @ 2007-01-23 19:02 UTC (permalink / raw)
  To: Alessandro Di Marco, linux-kernel, vojtech

On Tue 2007-01-23 20:01:07, Mattia Dongili wrote:
> On Tue, Jan 23, 2007 at 05:34:42PM +0100, Pavel Machek wrote:
> [...]
> > > Do you believe it could be a user-space daemon or what?
> > 
> > Yes, what prevents userspace daemon watching /dev/input/event* to
> > provide this functionality?
> 
> hmmm... EVIOCGRAB for example? the synaptics Xorg driver is using
> it, maybe others too?
> I don't want to start a new discussion about it, just provide an answer
> to your question :)

I do not know how EVIOCGRAB works, I believe event* will _still_ see
the events. Anyway, if not, I guess EVIOCGRAB can be fixed/removed/ we
can add EVIOCGIVEMEALLTHEDATAREALLYIWANTIT.
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-23 16:34         ` Pavel Machek
  2007-01-23 17:11           ` Alessandro Di Marco
  2007-01-23 19:01           ` Mattia Dongili
@ 2007-01-23 19:34           ` Scott Preece
  2007-01-24  2:02             ` Alessandro Di Marco
  2007-01-24 14:01             ` Pavel Machek
  2 siblings, 2 replies; 46+ messages in thread
From: Scott Preece @ 2007-01-23 19:34 UTC (permalink / raw)
  To: Pavel Machek; +Cc: Alessandro Di Marco, linux-kernel, vojtech

On 1/23/07, Pavel Machek <pavel@ucw.cz> wrote:
> Hi1
> ...
> >
>
> >    But I still believe it can be out.
> >
> > Do you believe it could be a user-space daemon or what?
>
> Yes, what prevents userspace daemon watching /dev/input/event* to
> provide this functionality?
>                                                                         Pavel
---

One possible argument is to allow integrating "input-like" user events
with other kinds of system-level events that you might want to have
treated like user activity. For instance, our definition of user
activity includes: button presses, opening-closing the cover (on a
phone), and plugging in or removing memory cards, accessories, or
cables. We actually use a mix of kernel and user-space monitoring,
today, but would prefer an integrated monitor.

A user-space monitor also has more opportunity for races - for
instance, deciding that the inactivity timeout has occurred between
the time that the user does something and the time that the kernel
gets a notification up to user space.

My own hot button is making sure that the definition of what
constitutes user activity is managed in exactly one place, whether in
the kernel or not. My naive model would be to put the response at user
level, but to provide a single point of definition in the kernel (say,
/dev/useractivity or the equivalent) that the user-level daemon could
listen to.

scott

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-23 19:02             ` Pavel Machek
@ 2007-01-23 20:07               ` Mattia Dongili
  0 siblings, 0 replies; 46+ messages in thread
From: Mattia Dongili @ 2007-01-23 20:07 UTC (permalink / raw)
  To: Pavel Machek; +Cc: Alessandro Di Marco, linux-kernel, vojtech

On Tue, Jan 23, 2007 at 08:02:57PM +0100, Pavel Machek wrote:
> On Tue 2007-01-23 20:01:07, Mattia Dongili wrote:
> > On Tue, Jan 23, 2007 at 05:34:42PM +0100, Pavel Machek wrote:
> > [...]
> > > > Do you believe it could be a user-space daemon or what?
> > > 
> > > Yes, what prevents userspace daemon watching /dev/input/event* to
> > > provide this functionality?
> > 
> > hmmm... EVIOCGRAB for example? the synaptics Xorg driver is using
> > it, maybe others too?
> > I don't want to start a new discussion about it, just provide an answer
> > to your question :)
> 
> I do not know how EVIOCGRAB works, I believe event* will _still_ see

AFAIK once an opener has eviocgrab-ed the device no other opener will
receive events.

> the events. Anyway, if not, I guess EVIOCGRAB can be fixed/removed/ we
> can add EVIOCGIVEMEALLTHEDATAREALLYIWANTIT.

then probably pbuttonsd is already doing something similar to SI[NM]
hmmm... was it pbuttonsd? oh, yes:
http://pbbuttons.sourceforge.net/projects/pbbuttonsd/
-- 
mattia
:wq!

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-23 19:34           ` Scott Preece
@ 2007-01-24  2:02             ` Alessandro Di Marco
  2007-01-24 14:01             ` Pavel Machek
  1 sibling, 0 replies; 46+ messages in thread
From: Alessandro Di Marco @ 2007-01-24  2:02 UTC (permalink / raw)
  To: Scott Preece; +Cc: Pavel Machek, linux-kernel, vojtech

"Scott Preece" <sepreece@gmail.com> writes:

   My own hot button is making sure that the definition of what
   constitutes user activity is managed in exactly one place, whether in
   the kernel or not. My naive model would be to put the response at user
   level, but to provide a single point of definition in the kernel (say,
   /dev/useractivity or the equivalent) that the user-level daemon could
   listen to.

Unfortunately the term "user activity" seems a bit too vague for this. IMHO
different users (but also applications) present different needs that you cannot
fit with a single device node.

Example. The user expects that the keyboard light is turned off after 10
minutes of keyboard inactivity, disregarding the mouse movements. This makes
sense since the glare at the bottom disturb the Quake sessions. :-) However when
the screen is blanked, the user expects that either keyboard or mouse events
can unblank it...

-- 
All of the significant battles are waged within the self. - Sheldon Kopp

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-23 18:44             ` Pavel Machek
@ 2007-01-24  2:51               ` Alessandro Di Marco
  2007-01-26 17:15                 ` Pavel Machek
  2007-01-24 18:08               ` Alessandro Di Marco
  1 sibling, 1 reply; 46+ messages in thread
From: Alessandro Di Marco @ 2007-01-24  2:51 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-kernel, vojtech

Pavel Machek <pavel@ucw.cz> writes:

   Imagine for a moment that we solve time-warp somehow. Any other
   problems? 

Well, a user-level daemon have to process a lot of data just to detect user
interaction. Considering that the trackpad bandwidth is nearly 5KB/sec,
probably would be better to leave my panel alone... :-/

   I'd really like to get "is user idle" solved, but it really should not be in
   kernel unless it _has_ to. And time-warp probably causes problems not only
   for your daemon.

IMHO signal the user-space is a kernel duty and no user-space daemon will ever
make it better. There are plenty of PM daemons out there, but Linux still lacks
of a decent power management. Thanks to SIN, acpid and a silly bash script I'm
able to save a lot of battery and furthermore, when my friends see the dimming
panel they break out: WOW, how do you succeed in running MacOS on your Vaio!?!
:-(

Best,

-- 
Gratitude is not only the greatest of virtues, but the parent of all the
others. - Cicero

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-23 19:34           ` Scott Preece
  2007-01-24  2:02             ` Alessandro Di Marco
@ 2007-01-24 14:01             ` Pavel Machek
  1 sibling, 0 replies; 46+ messages in thread
From: Pavel Machek @ 2007-01-24 14:01 UTC (permalink / raw)
  To: Scott Preece; +Cc: Alessandro Di Marco, linux-kernel, vojtech

Hi!
> >
> >>    But I still believe it can be out.
> >>
> >> Do you believe it could be a user-space daemon or 
> >what?
> >
> >Yes, what prevents userspace daemon watching 
> >/dev/input/event* to
> >provide this functionality?
> >                                                                        Pavel
> ---
> 
> One possible argument is to allow integrating 
> "input-like" user events
> with other kinds of system-level events that you might 
> want to have
> treated like user activity. For instance, our definition 
> of user
> activity includes: button presses, opening-closing the 
> cover (on a
> phone), and plugging in or removing memory cards, 
> accessories, or
> cables. We actually use a mix of kernel and user-space 
> monitoring,

Well... input already has 'pseudokey' for lid, and yes, you
probably can monitor cover, memory cards and cables from userspace,
already... as you do. Cover, and maybe even cards/cables could be
integrated with input infrastructure, too.

(Still waiting for you to start selling those cool phones in czech
republic :-).

> A user-space monitor also has more opportunity for races 
> - for
> instance, deciding that the inactivity timeout has 
> occurred between
> the time that the user does something and the time that 
> the kernel
> gets a notification up to user space.

Same races are inside kernel, too.

> My own hot button is making sure that the definition of 
> what
> constitutes user activity is managed in exactly one 
> place, whether in
> the kernel or not. My naive model would be to put the 
> response at user
> level, but to provide a single point of definition in 
> the kernel (say,
> /dev/useractivity or the equivalent) that the user-level 
> daemon could
> listen to.

Actually, I believe right solution is to provide one, unified,
monitoring daemon, using whatever interfaces are available. (+ add
missing functionality to the kernel, if neccessary).

							Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-23 18:44             ` Pavel Machek
  2007-01-24  2:51               ` Alessandro Di Marco
@ 2007-01-24 18:08               ` Alessandro Di Marco
  1 sibling, 0 replies; 46+ messages in thread
From: Alessandro Di Marco @ 2007-01-24 18:08 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-kernel, vojtech

[-- Attachment #1: Type: text/plain, Size: 705 bytes --]

Pavel Machek <pavel@ucw.cz> writes:

   > For example, the subsystem provides at kernel-level handy
   > hiberante/suspend/resume callbacks that I use to turn on/off the timer,
   > avoiding the time-warp problem. Doing that at user-level would be far more
   > messy...

   Imagine for a moment that we solve time-warp somehow. Any other
   problems? [I'd really like to get "is user idle" solved, but it really
   should not be in kernel unless it _has_ to. And time-warp probably
   causes problems not only for your daemon.]

Hi, 
the two patches in attachment fix the time-warp problem and remove the procfs
support. Feel free to contact me when you'll find a better way, maybe I could
help.

Best,


[-- Attachment #2: 0001-warp.txt --]
[-- Type: text/plain, Size: 12315 bytes --]

>From 602a3340fa5b11e6cfff91719f85668980bb338b Mon Sep 17 00:00:00 2001
From: Alessandro Di Marco <dmr@c0nc3pt.com>
Date: Wed, 24 Jan 2007 18:28:47 +0100
Subject: [PATCH] Time-warp bug fixed

---
 Makefile |    2 +
 debug.h  |   36 +++++++++++++
 sin.c    |  174 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 sin.h    |   13 +++++
 table.c  |   45 +++++++++-------
 table.h  |    6 +--
 6 files changed, 239 insertions(+), 37 deletions(-)

diff --git a/Makefile b/Makefile
index d45fa58..8b80de6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,7 @@
 MODLPATH = kernel/drivers/char
 
+#DEBUG="-D SIN_DEBUG"
+
 MODL = sinmod
 OBJS = sin.o procfs.o sysfs.o table.o input_enumerator.o acpi_enumerator.o
 
diff --git a/debug.h b/debug.h
new file mode 100644
index 0000000..e1c4bcc
--- /dev/null
+++ b/debug.h
@@ -0,0 +1,36 @@
+/*
+ *  Copyright (C) 2007 Alessandro Di Marco
+ */
+
+/*
+ *  This file is part of SIN.
+ *
+ *  SIN is free software; you can redistribute it and/or modify it under the
+ *  terms of the GNU General Public License as published by the Free Software
+ *  Foundation; version 2 of the License.
+ *
+ *  SIN 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 General Public License for more
+ *  details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with SIN; if not, write to the Free Software Foundation, Inc., 51 Franklin
+ *  St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#ifdef SIN_DEBUG
+extern int debug;
+#define set_debug(val) debug = (val)
+#define printd(fmt...) if (unlikely(debug)) { printk("SIN: " fmt); }
+#else
+#define SORRY "SIN: debugging support was disabled at compile time, sorry!\n"
+
+#define set_debug(val) if (val) { printk(KERN_DEBUG SORRY); }
+#define printd(fmt...)
+#endif
+
+#endif /* DEBUG_H */
diff --git a/sin.c b/sin.c
index c3633ff..bf06999 100644
--- a/sin.c
+++ b/sin.c
@@ -24,6 +24,9 @@
 #include <linux/acpi.h>
 #include <linux/mutex.h>
 #include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+
+#include "debug.h"
 
 #include "sin.h"
 #include "table.h"
@@ -36,7 +39,11 @@ MODULE_AUTHOR("Alessandro Di Marco <dmr@gmx.it>");
 MODULE_DESCRIPTION("System Inactivity Notifier");
 MODULE_LICENSE("GPL v2");
 
-MODULE_VERSION("1.4");
+MODULE_VERSION("1.5");
+
+#ifdef SIN_DEBUG
+int debug;
+#endif
 
 static struct acpi_device *acpi_device;
 
@@ -68,27 +75,33 @@ inline void signal_interaction(void)
 
 		WARN_ON(test_bit(RULE_OVER, &status) && timer_pending(&timer));
 
-		clear_bit(RULE_WRAP, &status);
-		clear_bit(RULE_OVER, &status);
-
 		next = occasionally_generate_event(acpi_device,
 						   last_activity(&uact));
 
 		if (!shutdown) {
-			(void) mod_timer(&timer, next);
+			printd("mod_timer() last = %lu, next = %lu\n",
+			       last_activity(&uact) / HZ, next / HZ);
+
+			if (likely(mod_timer(&timer, next))) {
+				WARN_ON(test_bit(RULE_OVER, &status));
+			}
 		}
+
+		clear_bit(RULE_WRAP, &status);
+		clear_bit(RULE_OVER, &status);
 	}
 }
 
 inline void simulate_event(void)
 {
-	signal_interaction();
 	(void) simulate_activity();
+	signal_interaction();
 }
 
 static void event(struct input_handle *handle,
 		  unsigned int type, unsigned int code, int value)
 {
+	printd("user interaction at %lu\n", jiffies / HZ);
 	simulate_event();
 }
 
@@ -122,6 +135,8 @@ void timer_fn(unsigned long data)
 	if (!shutdown) {
 		unsigned long next;
 
+		printd(">>>>>>>>>>>> timer_fn()\n");
+
 		set_bit(RULE_LOCK, &status);
 
 		next = timely_generate_event(acpi_device,
@@ -133,10 +148,15 @@ void timer_fn(unsigned long data)
 			signal_interaction();
 		}
 
-		if (!test_and_clear_bit(RULE_OVER, &status)) {
+		if (!test_bit(RULE_OVER, &status)) {
+			printd("add_timer() now %lu, timer set to %lu\n",
+			       jiffies / HZ, next / HZ);
+
 			timer.expires = next;
 			add_timer(&timer);
 		}
+
+		printd("timer_fn() >>>>>>>>>>>>\n");
 	}
 }
 
@@ -227,38 +247,41 @@ static struct miscdevice sin_miscdev = {
 	.fops		= &sin_miscfops,
 };
 
-static int __init sih_init(void)
+static int __devinit sin_probe(struct platform_device *dev)
 {
 	int err;
 
 	err = misc_register(&sin_miscdev);
 	if (err < 0) {
+		printk(KERN_ERR "SIN: miscdev initialization failed\n");
 		goto out;
 	}
 
 	err = input_enum();
 	if (err < 0) {
+		printk(KERN_ERR "SIN: input enumeration failed\n");
 		goto cleanout1;
 	}
 
 	err = acpi_enum();
 	if (err < 0) {
+		printk(KERN_ERR "SIN: acpi enumeration failed\n");
 		goto cleanout2;
 	}
 
 	err = start_sysfs(sin_miscdev.this_device);
 	if (err < 0) {
-		printk(KERN_ERR "sin: sysfs initialization failed\n");
+		printk(KERN_ERR "SIN: sysfs initialization failed\n");
 		goto cleanout3;
 	}
 
 	err = start_procfs();
 	if (err < 0) {
-		printk(KERN_ERR "sin: procfs initialization failed\n");
+		printk(KERN_ERR "SIN: procfs initialization failed\n");
 		goto cleanout4;
 	}
 
-	printk("System Inactivity Notifier 1.4 - (c) Alessandro Di Marco <dmr@gmx.it>\n");
+	printk(KERN_DEBUG "System Inactivity Notifier 1.5 - (c) Alessandro Di Marco <dmr@gmx.it>\n");
 
 	return 0;
 
@@ -274,7 +297,7 @@ out:
 	return err;
 }
 
-static void __exit sih_exit(void)
+static int __devexit sin_remove(struct platform_device *dev)
 {
 	stop_procfs();
 	stop_sysfs();
@@ -284,6 +307,133 @@ static void __exit sih_exit(void)
 	(void) stop_monitor();
 
 	misc_deregister(&sin_miscdev);
+
+	return 0;
+}
+
+static void sin_shutdown(struct platform_device *dev)
+{
+	printd("shutdown() >>>>>>>>>>>>\n");
+	printd(">>>>>>>>>>>> shutdown()\n");
+}
+
+#ifdef CONFIG_PM
+static unsigned long left, right, now;
+
+static int sin_suspend(struct platform_device *dev, pm_message_t state)
+{
+	printd("suspend() >>>>>>>>>>>>\n");
+
+	if (running) {
+		printd("stopping timer!\n");
+
+		shutdown = 1;
+		del_timer(&timer);
+
+		now = jiffies;
+
+		left = (long) now - (long) last_activity(&uact);
+		right = (long) timer.expires - (long) now;
+
+		printd("left %lu, now %lu, right %lu\n",
+		       left / HZ, now / HZ, right / HZ);
+	} else {
+		printd("not running!\n");
+	}
+
+	printd(">>>>>>>>>>>> suspend()\n");
+
+	return 0;
+}
+
+static int sin_resume(struct platform_device *dev)
+{
+	printd("resume() >>>>>>>>>>>>\n");
+
+	shutdown = 0;
+
+	if (running) {
+		if (!test_bit(RULE_OVER, &status)) {
+			printd("restarting timer!\n");
+
+			if (test_and_clear_bit(RULE_WRAP, &status)) {
+				printd("wrapped rule found\n");
+				special_event(acpi_device);
+			}
+
+			now = trim_activity(&uact, left);
+
+			timer.expires = now + right;
+
+			printd("last = %lu, now is %lu, timer set to %lu\n",
+			       last_activity(&uact) / HZ,
+			       now / HZ, timer.expires / HZ);
+
+			add_timer(&timer);
+		} else {
+			printd("rule over found\n");
+		}
+	} else {
+		printd("not running!\n");
+	}
+
+	printd(">>>>>>>>>>>> resume()\n");
+
+	return 0;
+}
+#else
+#define sin_suspend NULL
+#define sin_resume NULL
+#endif
+
+static struct platform_driver sin_driver = {
+	.driver		= {
+		.name	= MODULE_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= sin_probe,
+	.remove		= __devexit_p(sin_remove),
+	.shutdown	= sin_shutdown,
+	.suspend	= sin_suspend,
+	.resume		= sin_resume,
+};
+
+static struct platform_device *sin_platform_device;
+
+static int __init sih_init(void)
+{
+	int err;
+
+	err = platform_driver_register(&sin_driver);
+	if (err < 0) {
+		goto out;
+	}
+
+	sin_platform_device = platform_device_alloc(MODULE_NAME, -1);
+	if (!sin_platform_device) {
+		err = -ENOMEM;
+		goto cleanout1;
+	}
+
+	err = platform_device_add(sin_platform_device);
+	if (err < 0) {
+		goto cleanout2;
+	}
+
+	return 0;
+
+cleanout2:
+	platform_device_put(sin_platform_device);
+cleanout1:
+	platform_driver_unregister(&sin_driver);
+out:
+	return err;
+}
+
+static void __exit sih_exit(void)
+{
+	platform_device_unregister(sin_platform_device);
+	platform_driver_unregister(&sin_driver);
 }
 
 module_init(sih_init);
diff --git a/sin.h b/sin.h
index 1ef52d2..929b1e5 100644
--- a/sin.h
+++ b/sin.h
@@ -59,6 +59,19 @@ static inline unsigned long last_activity(struct user_activity *uact)
 	return last;
 }
 
+static inline unsigned long trim_activity(struct user_activity *uact,
+					  unsigned long offset)
+{
+	unsigned long last;
+
+	spin_lock(&uact->lock);
+	last = jiffies;
+	uact->last = (long) last - (long) offset;
+	spin_unlock(&uact->lock);
+
+	return last;
+}
+
 extern unsigned long simulate_activity(void);
 extern void signal_interaction(void);
 extern void simulate_event(void);
diff --git a/table.c b/table.c
index b51e905..c55f230 100644
--- a/table.c
+++ b/table.c
@@ -24,6 +24,8 @@
 #include <linux/acpi.h>
 #include <linux/sort.h>
 
+#include "debug.h"
+
 #include "sin.h"
 #include "uniq.h"
 #include "table.h"
@@ -39,15 +41,24 @@ static int next_rule;
  * event service routines (e.g. /etc/acpid/default.sh).
  */
 
-unsigned long occasionally_generate_event(struct acpi_device *acpi_device,
-					  unsigned long last)
+static inline void generate_event(struct acpi_device *acpi_device, int rnum)
 {
-	printd("generating special event [%d, %d]\n",
-	       rt.rules[rt.rnum].type, rt.rules[rt.rnum].data);
+	struct rule *rule = &rt.rules[rnum];
+
+	printd("generating event [%d, %d]\n", rule->type, rule->data);
 
-	(void) acpi_bus_generate_event(acpi_device, rt.rules[rt.rnum].type,
-				       rt.rules[rt.rnum].data);
+	(void) acpi_bus_generate_event(acpi_device, rule->type, rule->data);
+}
+
+inline void special_event(struct acpi_device *acpi_device)
+{
+	generate_event(acpi_device, rt.rnum);
+}
 
+unsigned long occasionally_generate_event(struct acpi_device *acpi_device,
+					  unsigned long last)
+{
+	special_event(acpi_device);
 	return last + rt.rules[next_rule = 0].target;
 }
 
@@ -55,26 +66,18 @@ unsigned long timely_generate_event(struct acpi_device *acpi_device,
 				    unsigned long last, unsigned long *status)
 {
 	printd("last %lu [status %lu], now %lu -> next target is %lu (%d)\n",
-	       last, *status, jiffies,
-	       last + rt.rules[next_rule].target, next_rule);
+	       last / HZ, *status, jiffies / HZ,
+	       (last + rt.rules[next_rule].target) / HZ, next_rule);
 
 	for (; next_rule < rt.rnum &&
 		     time_after_eq(jiffies, last + rt.rules[next_rule].target);
 	     next_rule++) {
 		if (unlikely(test_and_clear_bit(RULE_WRAP, status))) {
-			printd("passive wrap, generating special event\n");
-
-			(void) acpi_bus_generate_event(acpi_device,
-						       rt.rules[rt.rnum].type,
-						       rt.rules[rt.rnum].data);
+			printd("passive wrap, user forgot to interact!\n");
+			special_event(acpi_device);
 		}
 
-		printd("generating event [%d, %d]\n",
-		       rt.rules[next_rule].type, rt.rules[next_rule].data);
-
-		(void) acpi_bus_generate_event(acpi_device,
-					       rt.rules[next_rule].type,
-					       rt.rules[next_rule].data);
+		generate_event(acpi_device, next_rule);
 		set_bit(RULE_TRIG, status);
 	}
 
@@ -87,7 +90,7 @@ unsigned long timely_generate_event(struct acpi_device *acpi_device,
 
 			last = simulate_activity();
 		} else {
-			printd("reached the last rule, disconnecting timer\n");
+			printd("last rule, disconnecting the timer\n");
 			set_bit(RULE_OVER, status);
 		}
 	}
@@ -271,6 +274,8 @@ int push_table(const char *buf, unsigned long count)
 		goto cleanout3;
 	}
 
+	set_debug(rt.debug);
+
 	err = start_monitor(get_hardware_id(rt.handle), idi);
 	if (err < 0) {
 		goto cleanout3;
diff --git a/table.h b/table.h
index fd6b91f..d2a86ae 100644
--- a/table.h
+++ b/table.h
@@ -39,11 +39,6 @@ struct table {
 	struct rule *rules;
 };
 
-#define printd(fmt...)				\
-	if (unlikely(rt.debug)) {		\
-		printk(fmt);			\
-	}
-
 static inline int tablecmp(struct table *l, struct table *r)
 {
 	if (l->debug != r->debug ||
@@ -73,6 +68,7 @@ static inline int tablecmp(struct table *l, struct table *r)
 
 #define TABLE_BUFFER_SIZE (8 + rt.dnum + rt.rnum * 3 + (TABLE_SIZE << 3) / 3)
 
+extern void special_event(struct acpi_device *acpi_device);
 extern unsigned long occasionally_generate_event(struct acpi_device *acpi_device, unsigned long last);
 extern unsigned long timely_generate_event(struct acpi_device *acpi_device, unsigned long last, unsigned long *notify);
 
-- 
1.4.4.4


[-- Attachment #3: 0002-procfs.txt --]
[-- Type: text/plain, Size: 9244 bytes --]

>From 3769b408303337db46b7e7621d5749b98ad9b198 Mon Sep 17 00:00:00 2001
From: Alessandro Di Marco <dmr@c0nc3pt.com>
Date: Wed, 24 Jan 2007 18:45:40 +0100
Subject: [PATCH] Removed procfs support

---
 Makefile |    2 +-
 gentable |   21 ++++---
 procfs.c |  202 --------------------------------------------------------------
 procfs.h |   33 ----------
 sin.c    |   10 ---
 5 files changed, 14 insertions(+), 254 deletions(-)

diff --git a/Makefile b/Makefile
index 8b80de6..62b1c43 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ MODLPATH = kernel/drivers/char
 #DEBUG="-D SIN_DEBUG"
 
 MODL = sinmod
-OBJS = sin.o procfs.o sysfs.o table.o input_enumerator.o acpi_enumerator.o
+OBJS = sin.o sysfs.o table.o input_enumerator.o acpi_enumerator.o
 
 SRCS := $(patsubst %.o,%.c,$(OBJS))
 HDRS := $(patsubst %.o,%.h,$(OBJS))
diff --git a/gentable b/gentable
index 49af1f4..a8fe292 100755
--- a/gentable
+++ b/gentable
@@ -24,8 +24,8 @@ if (( $# == 0 )); then
    exit
 fi
 
-if [ ! -d "/proc/sin" ]; then
-    echo "/proc/sin not found, has sinmod been loaded?"
+if [ ! -d "/sys/class/misc/sin" ]; then
+    echo "/sys/class/misc/sin not found, has sinmod been loaded?"
     exit
 fi
 
@@ -38,7 +38,7 @@ least one device and must not specify duplicates.
 EOF
 
 echo -e "Specify the the input devices you want to monitor from the list below:\n"
-cat /proc/sin/input
+cat /sys/class/misc/sin/input
 
 echo
 input "Please digit the corresponding numbers separated by spaces" devs
@@ -57,7 +57,7 @@ suitable handler that will be used as originator.
 EOF
 
 echo -e "Specify the acpi handler you want to use from the list below:\n"
-cat /proc/sin/acpi
+cat /sys/class/misc/sin/acpi
 
 echo
 input "Please enter the number corresponding to the handler" handle
@@ -83,6 +83,8 @@ finished, just press enter.
 
 EOF
 
+declare -i i
+
 for (( i = 0; ; i++ )); do
     input "Rule ${i}?" rule
 
@@ -95,6 +97,7 @@ done
 
 if (( ${i} == 0 )); then
     rules[0]="60 1 2"
+    i=${i}+1
 fi
 
 cat <<EOF
@@ -111,6 +114,8 @@ if [ -z "${resume}" ]; then
     resume="2 1"
 fi
 
+declare -i j=${i}-1
+
 cat <<EOF
 
 Often an SIN event results in suspending or hibernating the system, hibernate,
@@ -120,10 +125,10 @@ consequence, no event will ever be generated and the system will remain in the
 state associated with the next-to-last rule (e.g. blanked screen, wireless
 powered off, etc.). The next option allows you to request a special event,
 restarting the rule-list evaluation from an arbitrary position.  Possible value
-ranges are described below, where N is the rule-list size:
+ranges are described below:
 
-    [0, N - 1]	=> jump to the given rule
-    N		=> go to sleep and wait for user interaction
+    [0, ${j}]	=> jump to the given rule
+    ${i}		=> go to sleep and wait for user interaction
 
 EOF
 
@@ -144,7 +149,7 @@ cat <<EOF
 All done. Now you can try your newly generated table as follows:
 
 # modprobe sinmod
-# echo $1 >/proc/sin/table
+# echo $1 >/sys/class/misc/sin/table
 
 An "Invalid argument" error indicates a mismatch in the table file, usually due
 to specifying an invalid acpi or input device. In that case, restart from
diff --git a/procfs.c b/procfs.c
deleted file mode 100644
index 7f42dde..0000000
--- a/procfs.c
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- *  Copyright (C) 2007 Alessandro Di Marco
- */
-
-/*
- *  This file is part of SIN.
- *
- *  SIN is free software; you can redistribute it and/or modify it under the
- *  terms of the GNU General Public License as published by the Free Software
- *  Foundation; version 2 of the License.
- *
- *  SIN 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 General Public License for more
- *  details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with SIN; if not, write to the Free Software Foundation, Inc., 51 Franklin
- *  St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <linux/module.h>
-#include <linux/proc_fs.h>
-#include <linux/uaccess.h>
-
-#include "sin.h"
-#include "table.h"
-#include "procfs.h"
-#include "acpi_enumerator.h"
-#include "input_enumerator.h"
-
-static struct procfs_bs ibs, abs;
-static struct proc_dir_entry *rootdir;
-
-static int read_proc(char *page, char **start,
-		     off_t off, int count, int *eof, void *data)
-{
-	struct procfs_bs *bs = data;
-	int left = bs->size - off;
-
-	if (count > left) {
-		count = left;
-	}
-
-	memcpy(page + off, bs->page, count);
-
-	if (count == left) {
-		*eof = 1;
-	}
-
-	return off + count;
-}
-
-static int read_table_proc(char *page, char **start,
-			   off_t off, int count, int *eof, void *data)
-{
-	char *buf;
-	size_t size;
-
-	int left;
-	int err = -ENOMEM;
-
-	size = pull_table(&buf);
-	if (!buf) {
-		goto out;
-	}
-
-	if (size > PAGE_SIZE) {
-		size = PAGE_SIZE;
-	}
-
-	left = size - off;
-	if (left < 0) {
-		err = -ESPIPE;
-		goto out;
-	}
-
-	if (count > left) {
-		count = left;
-	}
-
-	memcpy(page + off, buf, count);
-
-	if (count == left) {
-		page[size] = '\0';
-		*eof = 1;
-	}
-
-	return off + count;
-
-out:
-	return err;
-}
-
-static int write_table_proc(struct file *file, const __user char *ubuf,
-			    unsigned long count, void *data)
-{
-	char *buf;
-	int err;
-
-	buf = kmalloc(count, GFP_KERNEL);
-	if (!buf) {
-		err = -ENOMEM;
-		goto out;
-	}
-
-	if (copy_from_user(buf, ubuf, count + 1)) {
-		err = -EFAULT;
-		goto cleanout;
-	}
-
-	buf[count] = '\0';
-
-	err = push_table(buf, count);
-
-cleanout:
-	kfree(buf);
-out:
-	return err;
-}
-
-static int fake_write_proc(struct file *file, const __user char *ubuf,
-			   unsigned long count, void *data)
-{
-	void (*func)(void) = data;
-
-	func();
-	return count;
-}
-
-int start_procfs(void)
-{
-	struct proc_dir_entry *inputd, *acpid, *table, *interact;
-	int err = -ENOMEM;
-
-	ibs.size = get_devices_desc(&ibs.page);
-	abs.size = get_handlers_desc(&abs.page);
-
-	rootdir = proc_mkdir(MODULE_NAME, NULL);
-	if (!rootdir) {
-		goto out;
-	}
-
-	rootdir->owner = THIS_MODULE;
-
-	inputd = create_proc_read_entry("input", 0444,
-					rootdir, read_proc, &ibs);
-	if (!inputd) {
-		goto cleanout1;
-	}
-
-	inputd->owner = THIS_MODULE;
-
-	acpid = create_proc_read_entry("acpi", 0444,
-				       rootdir, read_proc, &abs);
-	if (!acpid) {
-		goto cleanout2;
-	}
-
-	acpid->owner = THIS_MODULE;
-
-	table = create_proc_entry("table", 0644, rootdir);
-	if (!table) {
-		goto cleanout3;
-	}
-
-	table->data = NULL;
-	table->read_proc = read_table_proc;
-	table->write_proc = write_table_proc;
-	table->owner = THIS_MODULE;
-
-	interact = create_proc_entry("interact", 0200, rootdir);
-	if (!interact) {
-		goto cleanout4;
-	}
-
-	interact->data = (void *) simulate_event;
-	interact->write_proc = fake_write_proc;
-	interact->owner = THIS_MODULE;
-
-	return 0;
-
-cleanout4:
-	remove_proc_entry("table", rootdir);
-cleanout3:
-	remove_proc_entry("acpi", rootdir);
-cleanout2:
-	remove_proc_entry("input", rootdir);
-cleanout1:
-	remove_proc_entry(MODULE_NAME, NULL);
-out:
-	return err;
-}
-
-void stop_procfs(void)
-{
-	remove_proc_entry("interact", rootdir);
-	remove_proc_entry("table", rootdir);
-	remove_proc_entry("acpi", rootdir);
-	remove_proc_entry("input", rootdir);
-	remove_proc_entry(MODULE_NAME, NULL);
-}
diff --git a/procfs.h b/procfs.h
deleted file mode 100644
index ea164be..0000000
--- a/procfs.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- *  Copyright (C) 2007 Alessandro Di Marco
- */
-
-/*
- *  This file is part of SIN.
- *
- *  SIN is free software; you can redistribute it and/or modify it under the
- *  terms of the GNU General Public License as published by the Free Software
- *  Foundation; version 2 of the License.
- *
- *  SIN 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 General Public License for more
- *  details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with SIN; if not, write to the Free Software Foundation, Inc., 51 Franklin
- *  St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef PROCFS_H
-#define PROCFS_H
-
-struct procfs_bs {
-	char *page;
-	int size;
-};
-
-extern int start_procfs(void);
-extern void stop_procfs(void);
-
-#endif /* PROCFS_H */
diff --git a/sin.c b/sin.c
index bf06999..7b96c61 100644
--- a/sin.c
+++ b/sin.c
@@ -30,7 +30,6 @@
 
 #include "sin.h"
 #include "table.h"
-#include "procfs.h"
 #include "sysfs.h"
 #include "acpi_enumerator.h"
 #include "input_enumerator.h"
@@ -275,18 +274,10 @@ static int __devinit sin_probe(struct platform_device *dev)
 		goto cleanout3;
 	}
 
-	err = start_procfs();
-	if (err < 0) {
-		printk(KERN_ERR "SIN: procfs initialization failed\n");
-		goto cleanout4;
-	}
-
 	printk(KERN_DEBUG "System Inactivity Notifier 1.5 - (c) Alessandro Di Marco <dmr@gmx.it>\n");
 
 	return 0;
 
-cleanout4:
-	stop_sysfs();
 cleanout3:
 	free_acpi_enum();
 cleanout2:
@@ -299,7 +290,6 @@ out:
 
 static int __devexit sin_remove(struct platform_device *dev)
 {
-	stop_procfs();
 	stop_sysfs();
 	free_acpi_enum();
 	free_input_enum();
-- 
1.4.4.4


[-- Attachment #4: Type: text/plain, Size: 154 bytes --]


-- 
Advertising is a valuable economic factor because it is the cheapest way of
selling goods, particularly if the goods are worthless. - Sinclair Lewis

^ permalink raw reply related	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-24  2:51               ` Alessandro Di Marco
@ 2007-01-26 17:15                 ` Pavel Machek
  2007-01-26 17:55                   ` Alessandro Di Marco
  0 siblings, 1 reply; 46+ messages in thread
From: Pavel Machek @ 2007-01-26 17:15 UTC (permalink / raw)
  To: Alessandro Di Marco; +Cc: linux-kernel, vojtech

Hi!

>    Imagine for a moment that we solve time-warp somehow. Any other
>    problems? 
> 
> Well, a user-level daemon have to process a lot of data just to detect user
> interaction. Considering that the trackpad bandwidth is nearly 5KB/sec,
> probably would be better to leave my panel alone... :-/

Ok, so we may want to introduce something like "tell me if some data
came from last time"... Actually, 5KB/sec is pretty much okay, and you
probably could get around actually reading that data.

When you know user is moving the touchpad, you could just sleep for 5
seconds (assuming user activity) and only then start monitoring it
again?

>    I'd really like to get "is user idle" solved, but it really should not be in
>    kernel unless it _has_ to. And time-warp probably causes problems not only
>    for your daemon.
> 
> IMHO signal the user-space is a kernel duty and no user-space daemon will ever
> make it better. There are plenty of PM daemons out there, but Linux still lacks

Well, I do not think your kernel code is mergeable. But bits to enable
similar functionality in userspace probably would be mergeable.

								Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-26 17:15                 ` Pavel Machek
@ 2007-01-26 17:55                   ` Alessandro Di Marco
  2007-01-27 17:45                     ` Pavel Machek
  0 siblings, 1 reply; 46+ messages in thread
From: Alessandro Di Marco @ 2007-01-26 17:55 UTC (permalink / raw)
  To: Pavel Machek; +Cc: linux-kernel, vojtech

[-- Attachment #1: Type: text/plain, Size: 746 bytes --]

Pavel Machek <pavel@ucw.cz> writes:

   Well, I do not think your kernel code is mergeable. But bits to enable
   similar functionality in userspace probably would be mergeable.

You said it :-)

This patch exports to the user space the inactivity time (in msecs) of a given
input device. Example follows:

<0> $ cat /proc/bus/input/activity
0011 0001 0001 ab41     1
0011 0002 0008 0000     3160799
0011 0002 0008 7321     549991
0019 0000 0005 0000     3160799
0019 0000 0001 0000     3454901
0010 104d 0000 0000     3160799
0010 104d 0000 0000     2162833

The device ordering matches the /proc/bus/input/devices one, anyway I reported
also vendor, product, etc. Now the daemon is trivial...

Bye

Ah, the interesting column is the fifth ;-)


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: input-activity.patch --]
[-- Type: text/x-patch, Size: 2782 bytes --]

diff -ur OLD/drivers/input/input.c NEW/drivers/input/input.c
--- OLD/drivers/input/input.c	2007-01-26 16:59:36.000000000 +0100
+++ NEW/drivers/input/input.c	2007-01-26 17:04:38.000000000 +0100
@@ -49,6 +49,8 @@
 {
 	struct input_handle *handle;
 
+	dev->last_activity = jiffies;
+
 	if (type > EV_MAX || !test_bit(type, dev->evbit))
 		return;
 
@@ -482,6 +484,30 @@
 	return seq_open(file, &input_devices_seq_ops);
 }
 
+static int input_activity_seq_show(struct seq_file *seq, void *v)
+{
+	struct input_dev *dev = container_of(v, struct input_dev, node);
+
+	seq_printf(seq, "%04x %04x %04x %04x\t%u\n",
+		   dev->id.bustype, dev->id.vendor,
+		   dev->id.product, dev->id.version,
+		   jiffies_to_msecs((long) jiffies - (long) dev->last_activity));
+
+	return 0;
+}
+
+static struct seq_operations input_activity_seq_ops = {
+	.start	= input_devices_seq_start,
+	.next	= input_devices_seq_next,
+	.stop	= input_devices_seq_stop,
+	.show	= input_activity_seq_show,
+};
+
+static int input_proc_activity_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &input_activity_seq_ops);
+}
+
 static struct file_operations input_devices_fileops = {
 	.owner		= THIS_MODULE,
 	.open		= input_proc_devices_open,
@@ -491,6 +517,15 @@
 	.release	= seq_release,
 };
 
+static struct file_operations input_activity_fileops = {
+	.owner		= THIS_MODULE,
+	.open		= input_proc_activity_open,
+	.poll		= input_proc_devices_poll,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
 static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos)
 {
 	/* acquire lock here ... Yes, we do need locking, I knowi, I know... */
@@ -558,15 +593,23 @@
 	entry->owner = THIS_MODULE;
 	entry->proc_fops = &input_devices_fileops;
 
-	entry = create_proc_entry("handlers", 0, proc_bus_input_dir);
+	entry = create_proc_entry("activity", 0, proc_bus_input_dir);
 	if (!entry)
 		goto fail2;
 
 	entry->owner = THIS_MODULE;
+	entry->proc_fops = &input_activity_fileops;
+
+	entry = create_proc_entry("handlers", 0, proc_bus_input_dir);
+	if (!entry)
+		goto fail3;
+
+	entry->owner = THIS_MODULE;
 	entry->proc_fops = &input_handlers_fileops;
 
 	return 0;
 
+ fail3:	remove_proc_entry("activity", proc_bus_input_dir);
  fail2:	remove_proc_entry("devices", proc_bus_input_dir);
  fail1: remove_proc_entry("input", proc_bus);
 	return -ENOMEM;
diff -ur OLD/include/linux/input.h NEW/include/linux/input.h
--- OLD/include/linux/input.h	2007-01-26 16:59:38.000000000 +0100
+++ NEW/include/linux/input.h	2007-01-26 17:31:29.000000000 +0100
@@ -949,6 +949,8 @@
 	const char *uniq;
 	struct input_id id;
 
+	unsigned long last_activity;
+	
 	unsigned long evbit[NBITS(EV_MAX)];
 	unsigned long keybit[NBITS(KEY_MAX)];
 	unsigned long relbit[NBITS(REL_MAX)];

[-- Attachment #3: Type: text/plain, Size: 102 bytes --]


-- 
I don't think anyone should write their autobiography until after they're
dead. - Samuel Goldwyn

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-26 17:55                   ` Alessandro Di Marco
@ 2007-01-27 17:45                     ` Pavel Machek
  2007-01-27 19:20                       ` Vojtech Pavlik
  2007-01-29  8:24                       ` [ANNOUNCE] System Inactivity Monitor v1.0 Stefan Seyfried
  0 siblings, 2 replies; 46+ messages in thread
From: Pavel Machek @ 2007-01-27 17:45 UTC (permalink / raw)
  To: Alessandro Di Marco; +Cc: linux-kernel, vojtech

Hi!

>    Well, I do not think your kernel code is mergeable. But bits to enable
>    similar functionality in userspace probably would be mergeable.
> 
> You said it :-)
> 
> This patch exports to the user space the inactivity time (in msecs) of a given
> input device. Example follows:

Looks okay to me. I guess you should sign it off, and ask Dmitry
(input maintainer) for a merge?


> <0> $ cat /proc/bus/input/activity
> 0011 0001 0001 ab41     1
> 0011 0002 0008 0000     3160799
> 0011 0002 0008 7321     549991
> 0019 0000 0005 0000     3160799
> 0019 0000 0001 0000     3454901
> 0010 104d 0000 0000     3160799
> 0010 104d 0000 0000     2162833
> 
> The device ordering matches the /proc/bus/input/devices one, anyway I reported
> also vendor, product, etc. Now the daemon is trivial...

> @@ -482,6 +484,30 @@
>  	return seq_open(file, &input_devices_seq_ops);
>  }
>  
> +static int input_activity_seq_show(struct seq_file *seq, void *v)
> +{
> +	struct input_dev *dev = container_of(v, struct input_dev, node);
> +
> +	seq_printf(seq, "%04x %04x %04x %04x\t%u\n",
> +		   dev->id.bustype, dev->id.vendor,
> +		   dev->id.product, dev->id.version,
> +		   jiffies_to_msecs((long) jiffies - (long) dev->last_activity));
> +
> +	return 0;
> +}
> +
> +static struct seq_operations input_activity_seq_ops = {
> +	.start	= input_devices_seq_start,
> +	.next	= input_devices_seq_next,
> +	.stop	= input_devices_seq_stop,
> +	.show	= input_activity_seq_show,
> +};
> +
> +static int input_proc_activity_open(struct inode *inode, struct file *file)
> +{
> +	return seq_open(file, &input_activity_seq_ops);
> +}
> +
>  static struct file_operations input_devices_fileops = {
>  	.owner		= THIS_MODULE,
>  	.open		= input_proc_devices_open,
> @@ -491,6 +517,15 @@
>  	.release	= seq_release,
>  };
>  
> +static struct file_operations input_activity_fileops = {
> +	.owner		= THIS_MODULE,
> +	.open		= input_proc_activity_open,
> +	.poll		= input_proc_devices_poll,
> +	.read		= seq_read,
> +	.llseek		= seq_lseek,
> +	.release	= seq_release,
> +};
> +
>  static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos)
>  {
>  	/* acquire lock here ... Yes, we do need locking, I knowi, I know... */
> @@ -558,15 +593,23 @@
>  	entry->owner = THIS_MODULE;
>  	entry->proc_fops = &input_devices_fileops;
>  
> -	entry = create_proc_entry("handlers", 0, proc_bus_input_dir);
> +	entry = create_proc_entry("activity", 0, proc_bus_input_dir);
>  	if (!entry)
>  		goto fail2;
>  
>  	entry->owner = THIS_MODULE;
> +	entry->proc_fops = &input_activity_fileops;
> +
> +	entry = create_proc_entry("handlers", 0, proc_bus_input_dir);
> +	if (!entry)
> +		goto fail3;
> +
> +	entry->owner = THIS_MODULE;
>  	entry->proc_fops = &input_handlers_fileops;
>  
>  	return 0;
>  
> + fail3:	remove_proc_entry("activity", proc_bus_input_dir);
>   fail2:	remove_proc_entry("devices", proc_bus_input_dir);
>   fail1: remove_proc_entry("input", proc_bus);
>  	return -ENOMEM;
> diff -ur OLD/include/linux/input.h NEW/include/linux/input.h
> --- OLD/include/linux/input.h	2007-01-26 16:59:38.000000000 +0100
> +++ NEW/include/linux/input.h	2007-01-26 17:31:29.000000000 +0100
> @@ -949,6 +949,8 @@
>  	const char *uniq;
>  	struct input_id id;
>  
> +	unsigned long last_activity;
> +	
>  	unsigned long evbit[NBITS(EV_MAX)];
>  	unsigned long keybit[NBITS(KEY_MAX)];
>  	unsigned long relbit[NBITS(REL_MAX)];

> 
> -- 
> I don't think anyone should write their autobiography until after they're
> dead. - Samuel Goldwyn


-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-27 17:45                     ` Pavel Machek
@ 2007-01-27 19:20                       ` Vojtech Pavlik
  2007-01-29 13:58                         ` Alessandro Di Marco
  2007-01-29  8:24                       ` [ANNOUNCE] System Inactivity Monitor v1.0 Stefan Seyfried
  1 sibling, 1 reply; 46+ messages in thread
From: Vojtech Pavlik @ 2007-01-27 19:20 UTC (permalink / raw)
  To: Pavel Machek; +Cc: Alessandro Di Marco, linux-kernel

On Sat, Jan 27, 2007 at 05:45:25PM +0000, Pavel Machek wrote:
> Hi!
> 
> >    Well, I do not think your kernel code is mergeable. But bits to enable
> >    similar functionality in userspace probably would be mergeable.
> > 
> > You said it :-)
> > 
> > This patch exports to the user space the inactivity time (in msecs) of a given
> > input device. Example follows:
> 
> Looks okay to me. I guess you should sign it off, and ask Dmitry
> (input maintainer) for a merge?

The /proc/bus/input/devices has an extensible structure. You can just
add an "A:" line (for Activity) instead of adding a new proc file.
Anyway, I believe this should be also available through sysfs, if not
only there.

Also, the activity counters should IMO coincide with the event times
passed through /dev/input/event, and should not be jiffies based.
Ideally, both should be based on clock_gettime(CLOCK_MONOTONIC).

> > <0> $ cat /proc/bus/input/activity
> > 0011 0001 0001 ab41     1
> > 0011 0002 0008 0000     3160799
> > 0011 0002 0008 7321     549991
> > 0019 0000 0005 0000     3160799
> > 0019 0000 0001 0000     3454901
> > 0010 104d 0000 0000     3160799
> > 0010 104d 0000 0000     2162833
> > 
> > The device ordering matches the /proc/bus/input/devices one, anyway I reported
> > also vendor, product, etc. Now the daemon is trivial...
> 
> > @@ -482,6 +484,30 @@
> >  	return seq_open(file, &input_devices_seq_ops);
> >  }
> >  
> > +static int input_activity_seq_show(struct seq_file *seq, void *v)
> > +{
> > +	struct input_dev *dev = container_of(v, struct input_dev, node);
> > +
> > +	seq_printf(seq, "%04x %04x %04x %04x\t%u\n",
> > +		   dev->id.bustype, dev->id.vendor,
> > +		   dev->id.product, dev->id.version,
> > +		   jiffies_to_msecs((long) jiffies - (long) dev->last_activity));
> > +
> > +	return 0;
> > +}
> > +
> > +static struct seq_operations input_activity_seq_ops = {
> > +	.start	= input_devices_seq_start,
> > +	.next	= input_devices_seq_next,
> > +	.stop	= input_devices_seq_stop,
> > +	.show	= input_activity_seq_show,
> > +};
> > +
> > +static int input_proc_activity_open(struct inode *inode, struct file *file)
> > +{
> > +	return seq_open(file, &input_activity_seq_ops);
> > +}
> > +
> >  static struct file_operations input_devices_fileops = {
> >  	.owner		= THIS_MODULE,
> >  	.open		= input_proc_devices_open,
> > @@ -491,6 +517,15 @@
> >  	.release	= seq_release,
> >  };
> >  
> > +static struct file_operations input_activity_fileops = {
> > +	.owner		= THIS_MODULE,
> > +	.open		= input_proc_activity_open,
> > +	.poll		= input_proc_devices_poll,
> > +	.read		= seq_read,
> > +	.llseek		= seq_lseek,
> > +	.release	= seq_release,
> > +};
> > +
> >  static void *input_handlers_seq_start(struct seq_file *seq, loff_t *pos)
> >  {
> >  	/* acquire lock here ... Yes, we do need locking, I knowi, I know... */
> > @@ -558,15 +593,23 @@
> >  	entry->owner = THIS_MODULE;
> >  	entry->proc_fops = &input_devices_fileops;
> >  
> > -	entry = create_proc_entry("handlers", 0, proc_bus_input_dir);
> > +	entry = create_proc_entry("activity", 0, proc_bus_input_dir);
> >  	if (!entry)
> >  		goto fail2;
> >  
> >  	entry->owner = THIS_MODULE;
> > +	entry->proc_fops = &input_activity_fileops;
> > +
> > +	entry = create_proc_entry("handlers", 0, proc_bus_input_dir);
> > +	if (!entry)
> > +		goto fail3;
> > +
> > +	entry->owner = THIS_MODULE;
> >  	entry->proc_fops = &input_handlers_fileops;
> >  
> >  	return 0;
> >  
> > + fail3:	remove_proc_entry("activity", proc_bus_input_dir);
> >   fail2:	remove_proc_entry("devices", proc_bus_input_dir);
> >   fail1: remove_proc_entry("input", proc_bus);
> >  	return -ENOMEM;
> > diff -ur OLD/include/linux/input.h NEW/include/linux/input.h
> > --- OLD/include/linux/input.h	2007-01-26 16:59:38.000000000 +0100
> > +++ NEW/include/linux/input.h	2007-01-26 17:31:29.000000000 +0100
> > @@ -949,6 +949,8 @@
> >  	const char *uniq;
> >  	struct input_id id;
> >  
> > +	unsigned long last_activity;
> > +	
> >  	unsigned long evbit[NBITS(EV_MAX)];
> >  	unsigned long keybit[NBITS(KEY_MAX)];
> >  	unsigned long relbit[NBITS(REL_MAX)];
> 
> > 
> > -- 
> > I don't think anyone should write their autobiography until after they're
> > dead. - Samuel Goldwyn
> 
> 
> -- 
> (english) http://www.livejournal.com/~pavelmachek
> (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
> 

-- 
Vojtech Pavlik
Director SuSE Labs

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-27 17:45                     ` Pavel Machek
  2007-01-27 19:20                       ` Vojtech Pavlik
@ 2007-01-29  8:24                       ` Stefan Seyfried
  1 sibling, 0 replies; 46+ messages in thread
From: Stefan Seyfried @ 2007-01-29  8:24 UTC (permalink / raw)
  To: Pavel Machek; +Cc: Alessandro Di Marco, linux-kernel, vojtech

On Sat, Jan 27, 2007 at 05:45:25PM +0000, Pavel Machek wrote:
> > This patch exports to the user space the inactivity time (in msecs) of a given
> > input device. Example follows:
> 
> Looks okay to me. I guess you should sign it off, and ask Dmitry
> (input maintainer) for a merge?

Hey, come on, "no new proc files", please ;-)

> > <0> $ cat /proc/bus/input/activity
-- 
Stefan Seyfried
QA / R&D Team Mobile Devices        |              "Any ideas, John?"
SUSE LINUX Products GmbH, Nürnberg  | "Well, surrounding them's out." 

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-27 19:20                       ` Vojtech Pavlik
@ 2007-01-29 13:58                         ` Alessandro Di Marco
  2007-01-29 22:28                           ` Pavel Machek
  0 siblings, 1 reply; 46+ messages in thread
From: Alessandro Di Marco @ 2007-01-29 13:58 UTC (permalink / raw)
  To: Vojtech Pavlik; +Cc: Pavel Machek, linux-kernel

Vojtech Pavlik <vojtech@suse.cz> writes:

   On Sat, Jan 27, 2007 at 05:45:25PM +0000, Pavel Machek wrote:
   > Hi!
   >
   > >    Well, I do not think your kernel code is mergeable. But bits to enable
   > >    similar functionality in userspace probably would be mergeable.
   > >
   > > You said it :-)
   > >
   > > This patch exports to the user space the inactivity time (in msecs) of a given
   > > input device. Example follows:
   >
   > Looks okay to me. I guess you should sign it off, and ask Dmitry
   > (input maintainer) for a merge?

Pavel, the submitted patch was not meant for production use: it still suffers
of the time-warp problem. To fix it I need to know when the system goes to
sleep/resumes. In SIN I've solved via the platform driver, introducing
suspend() resume() callbacks. What do you think about?

   The /proc/bus/input/devices has an extensible structure. You can just
   add an "A:" line (for Activity) instead of adding a new proc file.

I know, but IMO there is too much stuff to parse in there. Activity counters
are frequently accessed by daemons, and four or five concurrent daemons are the
norm in a typical X11 linux box...

   Anyway, I believe this should be also available through sysfs, if not
   only there.

Pavel gives me clearance for only bits of code, so I've recycled something
already done. No problem for me to switch /sys.

   Also, the activity counters should IMO coincide with the event times
   passed through /dev/input/event, and should not be jiffies based.
   Ideally, both should be based on clock_gettime(CLOCK_MONOTONIC).

In evdev.c do_gettimeofday() is used. Anyway I just need of a monotonic
counter, so get_jiffies_64() wouldn't be better? It isn't affected by wrapping
issues and it is probably faster than do_gtod().

Best,

--
Ambition is a poor excuse for not having sense enough to be lazy. - Edgar Bergen

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-29 13:58                         ` Alessandro Di Marco
@ 2007-01-29 22:28                           ` Pavel Machek
  2007-01-29 22:42                             ` Alessandro Di Marco
  0 siblings, 1 reply; 46+ messages in thread
From: Pavel Machek @ 2007-01-29 22:28 UTC (permalink / raw)
  To: Alessandro Di Marco; +Cc: Vojtech Pavlik, linux-kernel

Hi!

>    The /proc/bus/input/devices has an extensible structure. You can just
>    add an "A:" line (for Activity) instead of adding a new proc file.
> 
> I know, but IMO there is too much stuff to parse in there. Activity counters
> are frequently accessed by daemons, and four or five concurrent daemons are the
> norm in a typical X11 linux box...

Syscalls are fast enough, and the file is _very_ easy (=> fast) to parse.

>    Also, the activity counters should IMO coincide with the event times
>    passed through /dev/input/event, and should not be jiffies based.
>    Ideally, both should be based on clock_gettime(CLOCK_MONOTONIC).
> 
> In evdev.c do_gettimeofday() is used. Anyway I just need of a monotonic
> counter, so get_jiffies_64() wouldn't be better? It isn't affected by wrapping
> issues and it is probably faster than do_gtod().

Just use same time source rest of inputs already do...
									Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-29 22:28                           ` Pavel Machek
@ 2007-01-29 22:42                             ` Alessandro Di Marco
  2007-01-30  0:03                               ` Pavel Machek
  2007-01-30  9:42                               ` Vojtech Pavlik
  0 siblings, 2 replies; 46+ messages in thread
From: Alessandro Di Marco @ 2007-01-29 22:42 UTC (permalink / raw)
  To: Pavel Machek; +Cc: Vojtech Pavlik, linux-kernel

Pavel Machek <pavel@ucw.cz> writes:

   Hi!

   >    The /proc/bus/input/devices has an extensible structure. You can just
   >    add an "A:" line (for Activity) instead of adding a new proc file.
   > 
   > I know, but IMO there is too much stuff to parse in there. Activity counters
   > are frequently accessed by daemons, and four or five concurrent daemons are the
   > norm in a typical X11 linux box...

   Syscalls are fast enough, and the file is _very_ easy (=> fast) to parse.

   >    Also, the activity counters should IMO coincide with the event times
   >    passed through /dev/input/event, and should not be jiffies based.
   >    Ideally, both should be based on clock_gettime(CLOCK_MONOTONIC).
   > 
   > In evdev.c do_gettimeofday() is used. Anyway I just need of a monotonic
   > counter, so get_jiffies_64() wouldn't be better? It isn't affected by wrapping
   > issues and it is probably faster than do_gtod().

   Just use same time source rest of inputs already do...

OK, but what about the time-warp problem?. To fix it I need to know when the
system goes to sleep/resumes. In SIN I've solved via the platform driver,
introducing suspend() resume() callbacks...

greets,

-- 
Technology is dominated by two types of people: those who understand what they
do not manage, and those who manage what they do not understand. - Putt's Law

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-29 22:42                             ` Alessandro Di Marco
@ 2007-01-30  0:03                               ` Pavel Machek
  2007-01-30  9:42                               ` Vojtech Pavlik
  1 sibling, 0 replies; 46+ messages in thread
From: Pavel Machek @ 2007-01-30  0:03 UTC (permalink / raw)
  To: Alessandro Di Marco; +Cc: Vojtech Pavlik, linux-kernel

Hi!
>    >    The /proc/bus/input/devices has an extensible structure. You can just
>    >    add an "A:" line (for Activity) instead of adding a new proc file.
>    > 
>    > I know, but IMO there is too much stuff to parse in there. Activity counters
>    > are frequently accessed by daemons, and four or five concurrent daemons are the
>    > norm in a typical X11 linux box...
> 
>    Syscalls are fast enough, and the file is _very_ easy (=> fast) to parse.
> 
>    >    Also, the activity counters should IMO coincide with the event times
>    >    passed through /dev/input/event, and should not be jiffies based.
>    >    Ideally, both should be based on clock_gettime(CLOCK_MONOTONIC).
>    > 
>    > In evdev.c do_gettimeofday() is used. Anyway I just need of a monotonic
>    > counter, so get_jiffies_64() wouldn't be better? It isn't affected by wrapping
>    > issues and it is probably faster than do_gtod().
> 
>    Just use same time source rest of inputs already do...
> 
> OK, but what about the time-warp problem?. To fix it I need to know when the
> system goes to sleep/resumes. In SIN I've solved via the platform driver,
> introducing suspend() resume() callbacks...

input drivers will already have suspend() resume() callbacks... could
you reuse those?

...hmm, I see no easy place where to hook these. You could reuse your
platform device trick, I guess... but maybe there's a better way?

Vojtech, do we have some "global" hooks for input suspend/resume? This
code is global to all the devices, but still needs to know about
suspend/resume...

Or we could create notifier list, where interested parties would be
informed of suspend/resume...
								Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-29 22:42                             ` Alessandro Di Marco
  2007-01-30  0:03                               ` Pavel Machek
@ 2007-01-30  9:42                               ` Vojtech Pavlik
  2007-01-30 12:33                                 ` Alessandro Di Marco
                                                   ` (4 more replies)
  1 sibling, 5 replies; 46+ messages in thread
From: Vojtech Pavlik @ 2007-01-30  9:42 UTC (permalink / raw)
  To: Alessandro Di Marco; +Cc: Pavel Machek, linux-kernel

On Mon, Jan 29, 2007 at 11:42:08PM +0100, Alessandro Di Marco wrote:
> Pavel Machek <pavel@ucw.cz> writes:
> 
>    Hi!
> 
>    >    The /proc/bus/input/devices has an extensible structure. You can just
>    >    add an "A:" line (for Activity) instead of adding a new proc file.
>    > 
>    > I know, but IMO there is too much stuff to parse in there. Activity counters
>    > are frequently accessed by daemons, and four or five concurrent daemons are the
>    > norm in a typical X11 linux box...
> 
>    Syscalls are fast enough, and the file is _very_ easy (=> fast) to parse.
> 
>    >    Also, the activity counters should IMO coincide with the event times
>    >    passed through /dev/input/event, and should not be jiffies based.
>    >    Ideally, both should be based on clock_gettime(CLOCK_MONOTONIC).
>    > 
>    > In evdev.c do_gettimeofday() is used. Anyway I just need of a monotonic
>    > counter, so get_jiffies_64() wouldn't be better? It isn't affected by wrapping
>    > issues and it is probably faster than do_gtod().
> 
>    Just use same time source rest of inputs already do...
> 
> OK, but what about the time-warp problem?. To fix it I need to know when the
> system goes to sleep/resumes. In SIN I've solved via the platform driver,
> introducing suspend() resume() callbacks...
 
Well, you just need to make sure that a resume() actually is a visible
event ...

-- 
Vojtech Pavlik
Director SuSE Labs

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-30  9:42                               ` Vojtech Pavlik
@ 2007-01-30 12:33                                 ` Alessandro Di Marco
  2007-01-30 13:09                                   ` Vojtech Pavlik
  2009-01-27  0:52                                 ` [PATCH] input: Activity counters Alessandro Di Marco
                                                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 46+ messages in thread
From: Alessandro Di Marco @ 2007-01-30 12:33 UTC (permalink / raw)
  To: Vojtech Pavlik; +Cc: Pavel Machek, linux-kernel

Vojtech Pavlik <vojtech@suse.cz> writes:

   On Mon, Jan 29, 2007 at 11:42:08PM +0100, Alessandro Di Marco wrote:

   > OK, but what about the time-warp problem?. To fix it I need to know when the
   > system goes to sleep/resumes. In SIN I've solved via the platform driver,
   > introducing suspend() resume() callbacks...

   Well, you just need to make sure that a resume() actually is a visible
   event ...

Sorry, but I don't see the point. Visible to what? 

Mine problem here is that the input device doesn't care about suspend/resume
cycles (it is a straight char driver), probably because it doesn't need to (so
far.) Low-level drivers (kbd & co) on the contrary are all bus or platform
drivers, hooking directly into suspend/resume callbacks.

Do you mean that I should back-propagate a suspend/resume event from the
low-level drivers to the input one?

-- 
There is only one thing a philosopher can be relied upon to do, and that is to
contradict other philosophers. - William James

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-30 12:33                                 ` Alessandro Di Marco
@ 2007-01-30 13:09                                   ` Vojtech Pavlik
  2007-01-30 15:22                                     ` Alessandro Di Marco
  0 siblings, 1 reply; 46+ messages in thread
From: Vojtech Pavlik @ 2007-01-30 13:09 UTC (permalink / raw)
  To: Alessandro Di Marco; +Cc: Pavel Machek, linux-kernel

On Tue, Jan 30, 2007 at 01:33:10PM +0100, Alessandro Di Marco wrote:
> Vojtech Pavlik <vojtech@suse.cz> writes:
> 
>    On Mon, Jan 29, 2007 at 11:42:08PM +0100, Alessandro Di Marco wrote:
> 
>    > OK, but what about the time-warp problem?. To fix it I need to know when the
>    > system goes to sleep/resumes. In SIN I've solved via the platform driver,
>    > introducing suspend() resume() callbacks...
> 
>    Well, you just need to make sure that a resume() actually is a visible
>    event ...
> 
> Sorry, but I don't see the point. Visible to what? 

Counted as activity.

> Mine problem here is that the input device doesn't care about suspend/resume
> cycles (it is a straight char driver), probably because it doesn't need to (so
> far.) Low-level drivers (kbd & co) on the contrary are all bus or platform
> drivers, hooking directly into suspend/resume callbacks.
> 
> Do you mean that I should back-propagate a suspend/resume event from the
> low-level drivers to the input one?
 
Yes, but not as a callback, but instead as an input event. 

-- 
Vojtech Pavlik
Director SuSE Labs

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-30 13:09                                   ` Vojtech Pavlik
@ 2007-01-30 15:22                                     ` Alessandro Di Marco
  0 siblings, 0 replies; 46+ messages in thread
From: Alessandro Di Marco @ 2007-01-30 15:22 UTC (permalink / raw)
  To: Vojtech Pavlik; +Cc: Pavel Machek, linux-kernel

Vojtech Pavlik <vojtech@suse.cz> writes:

   > Mine problem here is that the input device doesn't care about suspend/resume
   > cycles (it is a straight char driver), probably because it doesn't need to (so
   > far.) Low-level drivers (kbd & co) on the contrary are all bus or platform
   > drivers, hooking directly into suspend/resume callbacks.
   > 
   > Do you mean that I should back-propagate a suspend/resume event from the
   > low-level drivers to the input one?

   Yes, but not as a callback, but instead as an input event. 

Hum. Usually I'm not so dumb, really.

Problem) I need to know when the system goes to sleep in
drivers/input/input.c. For example, as a consequence of 'echo mem
>/sys/power/state'.

Solution 1) My ideal input layer model would be:

               | SUBSYSTEM INPUT DEVICE* |
                   ^  ^      ^       ^
            +------+  |      +----+  +------+ events 
            |         |           |         |
        |KEYBOARD| |MOUSE | |TOUCHSCREEN| |MISC  |
        |DEVICE  | |DEVICE| |DEVICE     | |DEVICE|

               | SUBSYSTEM INPUT DEVICE* |
                   |  |      |       |
            +------+  |      +----+  +------+ suspend()/resume() callbacks
            v         v           v         v
        |KEYBOARD| |MOUSE | |TOUCHSCREEN| |MISC  |
        |DEVICE  | |DEVICE| |DEVICE     | |DEVICE|

Solution 2) On the contrary, you are suggesting to do this:

               |          INPUT DEVICE          |
                   ^     ^      ^           ^
            +------+     |      +----+      +------+ events plus
            |            |           |             | suspend/resume
        |KEYBOARD | |MOUSE    | |TOUCHSCREEN| |MISC      |
        |SUBSYSTEM| |SUBSYSTEM| |SUBSYSTEM  | |SUBSYSTEM |
        |DEVICE*  | |DEVICE*  | |DEVICE*    | |DEVICE*   |

Right?

* or whatever provides suspend/resume callbacks

-- 
>From their experience or from the recorded experience of others (history), men
learn only what their passions and their metaphysical prejudices allow them to
learn. - Aldous Huxley

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] input: Activity counters
  2007-01-30  9:42                               ` Vojtech Pavlik
  2007-01-30 12:33                                 ` Alessandro Di Marco
@ 2009-01-27  0:52                                 ` Alessandro Di Marco
  2009-01-27  0:54                                 ` Alessandro Di Marco
                                                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 46+ messages in thread
From: Alessandro Di Marco @ 2009-01-27  0:52 UTC (permalink / raw)
  To: Vojtech Pavlik; +Cc: Pavel Machek, linux-kernel

Hi. I think to have sorted out every issues at last; maybe you are still
interested in merging activity counters.

Best,

Vojtech Pavlik <vojtech@suse.cz> writes:

   On Mon, Jan 29, 2007 at 11:42:08PM +0100, Alessandro Di Marco wrote:
   > Pavel Machek <pavel@ucw.cz> writes:
   > 
   >    Hi!
   > 
   >    >    The /proc/bus/input/devices has an extensible structure. You can just
   >    >    add an "A:" line (for Activity) instead of adding a new proc file.
   >    > 
   >    > I know, but IMO there is too much stuff to parse in there. Activity counters
   >    > are frequently accessed by daemons, and four or five concurrent daemons are the
   >    > norm in a typical X11 linux box...
   > 
   >    Syscalls are fast enough, and the file is _very_ easy (=> fast) to parse.
   > 
   >    >    Also, the activity counters should IMO coincide with the event times
   >    >    passed through /dev/input/event, and should not be jiffies based.
   >    >    Ideally, both should be based on clock_gettime(CLOCK_MONOTONIC).
   >    > 
   >    > In evdev.c do_gettimeofday() is used. Anyway I just need of a monotonic
   >    > counter, so get_jiffies_64() wouldn't be better? It isn't affected by wrapping
   >    > issues and it is probably faster than do_gtod().
   > 
   >    Just use same time source rest of inputs already do...
   > 
   > OK, but what about the time-warp problem?. To fix it I need to know when the
   > system goes to sleep/resumes. In SIN I've solved via the platform driver,
   > introducing suspend() resume() callbacks...

   Well, you just need to make sure that a resume() actually is a visible
   event ...

Hello,

the attached patch* introduces activity counters into input device drivers so
that a proper user-space daemon leveraging these counters can implement event
notification facilities (e.g. screen savers, intrusion detection, etc.)  At
this purpose, a second patch has been also attached: it provides a working Perl
daemon simulating a screen saver activity.

I had submitted something similar a couple of years ago, by way of a kernel
module named SIN (http://lkml.org/lkml/2007/1/18/138)

Despite SIN has been properly maintained in this period, it revealed
soon some design weaknesses which urged me towards alternative
approaches. The resulting patch is quite straightforward yet provides
good features, such as:

1) daemons retain the whole control logic

2) daemons are EVIOCGRAB hassle free

3) daemons do not get biased towards a specific environment (e.g. one monitor,
   one keyboard...)

That's it.

Cheers,
Alessandro

* 2.6.28.1 friendly

Signed-off-by: Alessandro Di Marco <dmr@c0nc3pt.com>

==============================================================================
diff -uprN old/drivers/input/input.c new/drivers/input/input.c
--- old/drivers/input/input.c	2009-01-21 01:30:10.000000000 +0100
+++ new/drivers/input/input.c	2009-01-21 01:30:10.000000000 +0100
@@ -72,6 +72,12 @@ static void input_pass_event(struct inpu
 			     unsigned int type, unsigned int code, int value)
 {
 	struct input_handle *handle;
+	
+	dev->activity = ktime_get();
+
+	if (dev->activity_notifier) {
+		sysfs_notify_dirent(dev->activity_notifier);
+	}

 	rcu_read_lock();

@@ -1012,11 +1018,25 @@ static ssize_t input_dev_show_modalias(s
 }
 static DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL);

+static ssize_t input_dev_show_activity(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct input_dev *id = to_input_dev(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "A: %Ld\nD: %Ld\n",
+			 ktime_to_ns(id->activity),
+			 ktime_to_us(ktime_sub(ktime_get(), id->activity)));
+}
+
+static DEVICE_ATTR(activity, S_IRUGO, input_dev_show_activity, NULL);
+
 static struct attribute *input_dev_attrs[] = {
 	&dev_attr_name.attr,
 	&dev_attr_phys.attr,
 	&dev_attr_uniq.attr,
 	&dev_attr_modalias.attr,
+	&dev_attr_activity.attr,
 	NULL
 };

@@ -1227,10 +1247,34 @@ static int input_dev_uevent(struct devic
 	return 0;
 }

+static inline int warp_activity(struct device *dev) 
+{
+	struct input_dev *id = to_input_dev(dev);
+
+	id->activity = ktime_sub(ktime_get(), id->activity);
+	return 0;
+}
+
+int input_dev_suspend(struct device *dev, pm_message_t state)
+{
+	if (state.event & (PM_EVENT_FREEZE|PM_EVENT_SUSPEND)) {
+		(void) warp_activity(dev);
+	}
+
+	return 0;
+}
+
+int input_dev_resume(struct device *dev)
+{
+	return warp_activity(dev);
+}
+
 static struct device_type input_dev_type = {
 	.groups		= input_dev_attr_groups,
 	.release	= input_dev_release,
 	.uevent		= input_dev_uevent,
+	.suspend	= input_dev_suspend,
+	.resume		= input_dev_resume,
 };

 struct class input_class = {
@@ -1401,6 +1445,8 @@ int input_register_device(struct input_d
 		dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
 	kfree(path);

+	dev->activity_notifier = sysfs_get_dirent(dev->dev.kobj.sd, "activity");
+	
 	error = mutex_lock_interruptible(&input_mutex);
 	if (error) {
 		device_del(&dev->dev);
@@ -1446,6 +1492,9 @@ void input_unregister_device(struct inpu

 	mutex_unlock(&input_mutex);

+	sysfs_put(dev->activity_notifier);
+	dev->activity_notifier = NULL;
+
 	device_unregister(&dev->dev);
 }
 EXPORT_SYMBOL(input_unregister_device);
diff -uprN old/include/linux/input.h new/include/linux/input.h
--- old/include/linux/input.h	2009-01-21 01:30:10.000000000 +0100
+++ new/include/linux/input.h	2009-01-21 01:30:10.000000000 +0100
@@ -1094,6 +1094,9 @@ struct input_dev {

 	struct input_handle *grab;

+	struct sysfs_dirent *activity_notifier;
+	ktime_t activity;
+	
 	spinlock_t event_lock;
 	struct mutex mutex;

==============================================================================
diff -uprN old/SIN.pl new/SIN.pl
--- old/SIN.pl	2009-01-21 01:38:07.000000000 +0100
+++ new/SIN.pl	2009-01-21 01:38:01.000000000 +0100
@@ -0,0 +1,259 @@
+#!/usr/bin/perl -w
+
+# Copyright (C) 2009 Alessandro Di Marco <dmr@c0nc3pt.com>
+
+# This file is part of SIN.
+
+# SIN is free software: you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+
+# SIN 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 General Public License for more details.
+
+# You should have received a copy of the GNU General Public License along with
+# SIN.  If not, see <http://www.gnu.org/licenses/>.
+
+use strict;
+
+use IO::File;
+use IO::Select;
+use Getopt::Long;
+use Pod::Usage;
+
+use Storable;
+
+use constant CONFIG => 'SIN.conf';
+use constant ENROLL_TIMEOUT => 5;
+
+my @script = ( { timeout => 5, action => 'echo "dim screen"' },
+	       { timeout => 10, action => 'echo "blank screen"' },
+	       { timeout => 20, action => 'echo "system suspend"' } );
+
+use constant RESUME => 'echo "restore screen"';
+
+sub devices {
+    open my $devices, "/proc/bus/input/devices"
+	or die "uhm, no input devices?!?";
+
+    my $name;
+    my $devs;
+
+    while (<$devices>) {
+	$name = $1 if $_ =~ /^N: Name=\"(.*)\"\n$/;
+	$devs->{$1} = $name if $_ =~ /S: Sysfs=(.*)\n$/;
+    }
+
+    $devs;
+}
+
+sub activity {
+    my ($enrolled) = @_;
+
+    $enrolled = $_ unless defined $enrolled;
+
+    my $activities;
+
+    foreach (keys %$enrolled) {
+	open my $handle, "/sys$_/activity"
+	    or die "missing activity on $_; please apply the patch!";
+
+	my @data = <$handle>;
+
+	$data[0] =~ /A: (0|[1-9][0-9]*)\n$/;
+	my $last = $1;
+
+	$data[1] =~ /D: (0|[1-9][0-9]*)\n$/;
+	my $delta = $1;
+
+	$activities->{$handle} = {
+	    'path' => $_,
+	    'handle' => $handle,
+	    'last' => $last,
+	    'delta' => $delta,
+	};
+    }
+
+    $activities;
+}
+
+sub latest {
+    my ($activities) = @_;
+
+    $activities = $_ unless defined $activities;
+
+    foreach (values %$activities) {
+	my $handle = $_->{'handle'};
+
+	seek $handle, 0, SEEK_SET;
+
+	my @data = <$handle>;
+
+	$data[0] =~ /A: (0|[1-9][0-9]*)\n$/;
+	$_->{'last'} = $1;
+
+	$data[1] =~ /D: (0|[1-9][0-9]*)\n$/;
+	$_->{'delta'} = $1;
+    }
+
+    $activities;
+}
+
+sub configure {
+    my ($devs) = @_;
+
+    select(undef, undef, undef, 0.1);
+
+    print "Input device enrolling:\n";
+    print "  please interact with the desired input devices;\n";
+    print "  the process will terminate after " . ENROLL_TIMEOUT
+	. " seconds from the last enrollment.\n";
+
+    my $activities = activity($devs);
+
+    my $select = new IO::Select(map { $_->{'handle'} } values %$activities);
+
+    while ($select->handles) {
+	last unless my @picked = $select->can_read(ENROLL_TIMEOUT);
+
+    	foreach (@picked) {
+    	    print "Enrolled $devs->{$activities->{$_}->{'path'}}\n";
+    	}
+
+    	$select->remove(@picked);
+    }
+
+    delete $activities->{$_} foreach ($select->handles);
+
+    my $enrolled;
+
+    foreach (map { $_->{'path'} } values %$activities) {
+	$enrolled->{$_} = $devs->{$_};
+    }
+
+    $enrolled;
+}
+
+my %opts = ();
+
+GetOptions('configure' => \$opts{'configure'},
+	   'help|?' => \$opts{'help'},
+	   'man' => \$opts{'man'})
+    or pod2usage(2);
+
+pod2usage(1) if $opts{'help'};
+pod2usage('-exitstatus' => 0, '-verbose' => 2) if $opts{'man'};
+
+my $enrolled;
+
+if ($opts{'configure'}) {
+    $enrolled = configure devices;
+    store $enrolled, CONFIG;
+    exit 0;
+}
+
+die "No usable configuration found" unless -e CONFIG;
+
+$enrolled = retrieve(CONFIG)
+    or die "uhm, invalid configuration?!?\n";
+
+my $devs = devices;
+
+foreach (keys %$enrolled) {
+    die "uhm, stored configuration doesn't match?!?\n"
+	unless defined $devs->{$_};
+}
+
+sub latter_of {
+    my ($activities) = @_;
+
+    $activities = $_ unless defined $activities;
+
+    my $last = 0;
+    my $me;
+
+    foreach (values %$activities) {
+	if ($_->{'last'} > $last) {
+	    $last = $_->{'last'};
+	    $me = $_;
+	}
+    }
+
+    $me;
+}
+
+sub warp {
+    (($_[0]->{'timeout'} * 1000000) - $_[1]->{'delta'}) / 1000000;
+}
+
+my $activities = activity $enrolled;
+my $select = new IO::Select(map { $_->{'handle'} } values %$activities);
+
+start:
+    while (1) {
+	my $last = latter_of latest $activities;
+	my $trig = 0;
+
+	for (my $i = 0; $i < @script; $i++) {
+	    if ($trig) {
+		if ($select->can_read(warp($script[$i], $last))) {
+		    system RESUME;
+		    next start;
+		}
+
+		$last = latter_of latest $activities;
+	    } else {
+		while ((my $timeout = warp($script[$i], $last)) > 0) {
+		    select(undef, undef, undef, $timeout);
+		    $last = latter_of latest $activities;
+		}
+
+		$trig = 1;
+	    }
+
+	    system $script[$i]->{'action'};
+	}
+
+	$select->can_read;
+
+	system RESUME;
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+SIN - System Inactivity Notifier
+
+=head1 SYNOPSIS
+
+B<SIN> [B<--configure>] [B<--help>] [B<--man>]
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<--configure>
+
+Let the user configure SIN.
+
+=item B<--help>
+
+Print a brief help message and exits.
+
+=item B<--man>
+
+Prints the manual page and exits.
+
+=back
+
+=head1 DESCRIPTION
+
+B<SIN> is an inactivity notifier. This means that you can instruct SIN to
+perform different actions depending on user's activity.
+
+=cut

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] input: Activity counters
  2007-01-30  9:42                               ` Vojtech Pavlik
  2007-01-30 12:33                                 ` Alessandro Di Marco
  2009-01-27  0:52                                 ` [PATCH] input: Activity counters Alessandro Di Marco
@ 2009-01-27  0:54                                 ` Alessandro Di Marco
  2009-01-27  0:54                                 ` Alessandro Di Marco
  2009-01-27  0:54                                 ` Alessandro Di Marco
  4 siblings, 0 replies; 46+ messages in thread
From: Alessandro Di Marco @ 2009-01-27  0:54 UTC (permalink / raw)
  To: Vojtech Pavlik; +Cc: Pavel Machek, linux-kernel

Hi. I think to have sorted out every issues at last; maybe you are still
interested in merging activity counters.

Best,

Vojtech Pavlik <vojtech@suse.cz> writes:

   On Mon, Jan 29, 2007 at 11:42:08PM +0100, Alessandro Di Marco wrote:
   > Pavel Machek <pavel@ucw.cz> writes:
   > 
   >    Hi!
   > 
   >    >    The /proc/bus/input/devices has an extensible structure. You can just
   >    >    add an "A:" line (for Activity) instead of adding a new proc file.
   >    > 
   >    > I know, but IMO there is too much stuff to parse in there. Activity counters
   >    > are frequently accessed by daemons, and four or five concurrent daemons are the
   >    > norm in a typical X11 linux box...
   > 
   >    Syscalls are fast enough, and the file is _very_ easy (=> fast) to parse.
   > 
   >    >    Also, the activity counters should IMO coincide with the event times
   >    >    passed through /dev/input/event, and should not be jiffies based.
   >    >    Ideally, both should be based on clock_gettime(CLOCK_MONOTONIC).
   >    > 
   >    > In evdev.c do_gettimeofday() is used. Anyway I just need of a monotonic
   >    > counter, so get_jiffies_64() wouldn't be better? It isn't affected by wrapping
   >    > issues and it is probably faster than do_gtod().
   > 
   >    Just use same time source rest of inputs already do...
   > 
   > OK, but what about the time-warp problem?. To fix it I need to know when the
   > system goes to sleep/resumes. In SIN I've solved via the platform driver,
   > introducing suspend() resume() callbacks...

   Well, you just need to make sure that a resume() actually is a visible
   event ...

Hello,

the attached patch* introduces activity counters into input device drivers so
that a proper user-space daemon leveraging these counters can implement event
notification facilities (e.g. screen savers, intrusion detection, etc.)  At
this purpose, a second patch has been also attached: it provides a working Perl
daemon simulating a screen saver activity.

I had submitted something similar a couple of years ago, by way of a kernel
module named SIN (http://lkml.org/lkml/2007/1/18/138)

Despite SIN has been properly maintained in this period, it revealed
soon some design weaknesses which urged me towards alternative
approaches. The resulting patch is quite straightforward yet provides
good features, such as:

1) daemons retain the whole control logic

2) daemons are EVIOCGRAB hassle free

3) daemons do not get biased towards a specific environment (e.g. one monitor,
   one keyboard...)

That's it.

Cheers,
Alessandro

* 2.6.28.1 friendly

Signed-off-by: Alessandro Di Marco <dmr@c0nc3pt.com>

==============================================================================
diff -uprN old/drivers/input/input.c new/drivers/input/input.c
--- old/drivers/input/input.c	2009-01-21 01:30:10.000000000 +0100
+++ new/drivers/input/input.c	2009-01-21 01:30:10.000000000 +0100
@@ -72,6 +72,12 @@ static void input_pass_event(struct inpu
 			     unsigned int type, unsigned int code, int value)
 {
 	struct input_handle *handle;
+	
+	dev->activity = ktime_get();
+
+	if (dev->activity_notifier) {
+		sysfs_notify_dirent(dev->activity_notifier);
+	}

 	rcu_read_lock();

@@ -1012,11 +1018,25 @@ static ssize_t input_dev_show_modalias(s
 }
 static DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL);

+static ssize_t input_dev_show_activity(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct input_dev *id = to_input_dev(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "A: %Ld\nD: %Ld\n",
+			 ktime_to_ns(id->activity),
+			 ktime_to_us(ktime_sub(ktime_get(), id->activity)));
+}
+
+static DEVICE_ATTR(activity, S_IRUGO, input_dev_show_activity, NULL);
+
 static struct attribute *input_dev_attrs[] = {
 	&dev_attr_name.attr,
 	&dev_attr_phys.attr,
 	&dev_attr_uniq.attr,
 	&dev_attr_modalias.attr,
+	&dev_attr_activity.attr,
 	NULL
 };

@@ -1227,10 +1247,34 @@ static int input_dev_uevent(struct devic
 	return 0;
 }

+static inline int warp_activity(struct device *dev) 
+{
+	struct input_dev *id = to_input_dev(dev);
+
+	id->activity = ktime_sub(ktime_get(), id->activity);
+	return 0;
+}
+
+int input_dev_suspend(struct device *dev, pm_message_t state)
+{
+	if (state.event & (PM_EVENT_FREEZE|PM_EVENT_SUSPEND)) {
+		(void) warp_activity(dev);
+	}
+
+	return 0;
+}
+
+int input_dev_resume(struct device *dev)
+{
+	return warp_activity(dev);
+}
+
 static struct device_type input_dev_type = {
 	.groups		= input_dev_attr_groups,
 	.release	= input_dev_release,
 	.uevent		= input_dev_uevent,
+	.suspend	= input_dev_suspend,
+	.resume		= input_dev_resume,
 };

 struct class input_class = {
@@ -1401,6 +1445,8 @@ int input_register_device(struct input_d
 		dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
 	kfree(path);

+	dev->activity_notifier = sysfs_get_dirent(dev->dev.kobj.sd, "activity");
+	
 	error = mutex_lock_interruptible(&input_mutex);
 	if (error) {
 		device_del(&dev->dev);
@@ -1446,6 +1492,9 @@ void input_unregister_device(struct inpu

 	mutex_unlock(&input_mutex);

+	sysfs_put(dev->activity_notifier);
+	dev->activity_notifier = NULL;
+
 	device_unregister(&dev->dev);
 }
 EXPORT_SYMBOL(input_unregister_device);
diff -uprN old/include/linux/input.h new/include/linux/input.h
--- old/include/linux/input.h	2009-01-21 01:30:10.000000000 +0100
+++ new/include/linux/input.h	2009-01-21 01:30:10.000000000 +0100
@@ -1094,6 +1094,9 @@ struct input_dev {

 	struct input_handle *grab;

+	struct sysfs_dirent *activity_notifier;
+	ktime_t activity;
+	
 	spinlock_t event_lock;
 	struct mutex mutex;

==============================================================================
diff -uprN old/SIN.pl new/SIN.pl
--- old/SIN.pl	2009-01-21 01:38:07.000000000 +0100
+++ new/SIN.pl	2009-01-21 01:38:01.000000000 +0100
@@ -0,0 +1,259 @@
+#!/usr/bin/perl -w
+
+# Copyright (C) 2009 Alessandro Di Marco <dmr@c0nc3pt.com>
+
+# This file is part of SIN.
+
+# SIN is free software: you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+
+# SIN 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 General Public License for more details.
+
+# You should have received a copy of the GNU General Public License along with
+# SIN.  If not, see <http://www.gnu.org/licenses/>.
+
+use strict;
+
+use IO::File;
+use IO::Select;
+use Getopt::Long;
+use Pod::Usage;
+
+use Storable;
+
+use constant CONFIG => 'SIN.conf';
+use constant ENROLL_TIMEOUT => 5;
+
+my @script = ( { timeout => 5, action => 'echo "dim screen"' },
+	       { timeout => 10, action => 'echo "blank screen"' },
+	       { timeout => 20, action => 'echo "system suspend"' } );
+
+use constant RESUME => 'echo "restore screen"';
+
+sub devices {
+    open my $devices, "/proc/bus/input/devices"
+	or die "uhm, no input devices?!?";
+
+    my $name;
+    my $devs;
+
+    while (<$devices>) {
+	$name = $1 if $_ =~ /^N: Name=\"(.*)\"\n$/;
+	$devs->{$1} = $name if $_ =~ /S: Sysfs=(.*)\n$/;
+    }
+
+    $devs;
+}
+
+sub activity {
+    my ($enrolled) = @_;
+
+    $enrolled = $_ unless defined $enrolled;
+
+    my $activities;
+
+    foreach (keys %$enrolled) {
+	open my $handle, "/sys$_/activity"
+	    or die "missing activity on $_; please apply the patch!";
+
+	my @data = <$handle>;
+
+	$data[0] =~ /A: (0|[1-9][0-9]*)\n$/;
+	my $last = $1;
+
+	$data[1] =~ /D: (0|[1-9][0-9]*)\n$/;
+	my $delta = $1;
+
+	$activities->{$handle} = {
+	    'path' => $_,
+	    'handle' => $handle,
+	    'last' => $last,
+	    'delta' => $delta,
+	};
+    }
+
+    $activities;
+}
+
+sub latest {
+    my ($activities) = @_;
+
+    $activities = $_ unless defined $activities;
+
+    foreach (values %$activities) {
+	my $handle = $_->{'handle'};
+
+	seek $handle, 0, SEEK_SET;
+
+	my @data = <$handle>;
+
+	$data[0] =~ /A: (0|[1-9][0-9]*)\n$/;
+	$_->{'last'} = $1;
+
+	$data[1] =~ /D: (0|[1-9][0-9]*)\n$/;
+	$_->{'delta'} = $1;
+    }
+
+    $activities;
+}
+
+sub configure {
+    my ($devs) = @_;
+
+    select(undef, undef, undef, 0.1);
+
+    print "Input device enrolling:\n";
+    print "  please interact with the desired input devices;\n";
+    print "  the process will terminate after " . ENROLL_TIMEOUT
+	. " seconds from the last enrollment.\n";
+
+    my $activities = activity($devs);
+
+    my $select = new IO::Select(map { $_->{'handle'} } values %$activities);
+
+    while ($select->handles) {
+	last unless my @picked = $select->can_read(ENROLL_TIMEOUT);
+
+    	foreach (@picked) {
+    	    print "Enrolled $devs->{$activities->{$_}->{'path'}}\n";
+    	}
+
+    	$select->remove(@picked);
+    }
+
+    delete $activities->{$_} foreach ($select->handles);
+
+    my $enrolled;
+
+    foreach (map { $_->{'path'} } values %$activities) {
+	$enrolled->{$_} = $devs->{$_};
+    }
+
+    $enrolled;
+}
+
+my %opts = ();
+
+GetOptions('configure' => \$opts{'configure'},
+	   'help|?' => \$opts{'help'},
+	   'man' => \$opts{'man'})
+    or pod2usage(2);
+
+pod2usage(1) if $opts{'help'};
+pod2usage('-exitstatus' => 0, '-verbose' => 2) if $opts{'man'};
+
+my $enrolled;
+
+if ($opts{'configure'}) {
+    $enrolled = configure devices;
+    store $enrolled, CONFIG;
+    exit 0;
+}
+
+die "No usable configuration found" unless -e CONFIG;
+
+$enrolled = retrieve(CONFIG)
+    or die "uhm, invalid configuration?!?\n";
+
+my $devs = devices;
+
+foreach (keys %$enrolled) {
+    die "uhm, stored configuration doesn't match?!?\n"
+	unless defined $devs->{$_};
+}
+
+sub latter_of {
+    my ($activities) = @_;
+
+    $activities = $_ unless defined $activities;
+
+    my $last = 0;
+    my $me;
+
+    foreach (values %$activities) {
+	if ($_->{'last'} > $last) {
+	    $last = $_->{'last'};
+	    $me = $_;
+	}
+    }
+
+    $me;
+}
+
+sub warp {
+    (($_[0]->{'timeout'} * 1000000) - $_[1]->{'delta'}) / 1000000;
+}
+
+my $activities = activity $enrolled;
+my $select = new IO::Select(map { $_->{'handle'} } values %$activities);
+
+start:
+    while (1) {
+	my $last = latter_of latest $activities;
+	my $trig = 0;
+
+	for (my $i = 0; $i < @script; $i++) {
+	    if ($trig) {
+		if ($select->can_read(warp($script[$i], $last))) {
+		    system RESUME;
+		    next start;
+		}
+
+		$last = latter_of latest $activities;
+	    } else {
+		while ((my $timeout = warp($script[$i], $last)) > 0) {
+		    select(undef, undef, undef, $timeout);
+		    $last = latter_of latest $activities;
+		}
+
+		$trig = 1;
+	    }
+
+	    system $script[$i]->{'action'};
+	}
+
+	$select->can_read;
+
+	system RESUME;
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+SIN - System Inactivity Notifier
+
+=head1 SYNOPSIS
+
+B<SIN> [B<--configure>] [B<--help>] [B<--man>]
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<--configure>
+
+Let the user configure SIN.
+
+=item B<--help>
+
+Print a brief help message and exits.
+
+=item B<--man>
+
+Prints the manual page and exits.
+
+=back
+
+=head1 DESCRIPTION
+
+B<SIN> is an inactivity notifier. This means that you can instruct SIN to
+perform different actions depending on user's activity.
+
+=cut

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] input: Activity counters
  2007-01-30  9:42                               ` Vojtech Pavlik
                                                   ` (2 preceding siblings ...)
  2009-01-27  0:54                                 ` Alessandro Di Marco
@ 2009-01-27  0:54                                 ` Alessandro Di Marco
  2009-01-27  0:54                                 ` Alessandro Di Marco
  4 siblings, 0 replies; 46+ messages in thread
From: Alessandro Di Marco @ 2009-01-27  0:54 UTC (permalink / raw)
  To: Vojtech Pavlik; +Cc: Pavel Machek, linux-kernel

Hi. I think to have sorted out every issues at last; maybe you are still
interested in merging activity counters.

Best,

Vojtech Pavlik <vojtech@suse.cz> writes:

   On Mon, Jan 29, 2007 at 11:42:08PM +0100, Alessandro Di Marco wrote:
   > Pavel Machek <pavel@ucw.cz> writes:
   > 
   >    Hi!
   > 
   >    >    The /proc/bus/input/devices has an extensible structure. You can just
   >    >    add an "A:" line (for Activity) instead of adding a new proc file.
   >    > 
   >    > I know, but IMO there is too much stuff to parse in there. Activity counters
   >    > are frequently accessed by daemons, and four or five concurrent daemons are the
   >    > norm in a typical X11 linux box...
   > 
   >    Syscalls are fast enough, and the file is _very_ easy (=> fast) to parse.
   > 
   >    >    Also, the activity counters should IMO coincide with the event times
   >    >    passed through /dev/input/event, and should not be jiffies based.
   >    >    Ideally, both should be based on clock_gettime(CLOCK_MONOTONIC).
   >    > 
   >    > In evdev.c do_gettimeofday() is used. Anyway I just need of a monotonic
   >    > counter, so get_jiffies_64() wouldn't be better? It isn't affected by wrapping
   >    > issues and it is probably faster than do_gtod().
   > 
   >    Just use same time source rest of inputs already do...
   > 
   > OK, but what about the time-warp problem?. To fix it I need to know when the
   > system goes to sleep/resumes. In SIN I've solved via the platform driver,
   > introducing suspend() resume() callbacks...

   Well, you just need to make sure that a resume() actually is a visible
   event ...

Hello,

the attached patch* introduces activity counters into input device drivers so
that a proper user-space daemon leveraging these counters can implement event
notification facilities (e.g. screen savers, intrusion detection, etc.)  At
this purpose, a second patch has been also attached: it provides a working Perl
daemon simulating a screen saver activity.

I had submitted something similar a couple of years ago, by way of a kernel
module named SIN (http://lkml.org/lkml/2007/1/18/138)

Despite SIN has been properly maintained in this period, it revealed
soon some design weaknesses which urged me towards alternative
approaches. The resulting patch is quite straightforward yet provides
good features, such as:

1) daemons retain the whole control logic

2) daemons are EVIOCGRAB hassle free

3) daemons do not get biased towards a specific environment (e.g. one monitor,
   one keyboard...)

That's it.

Cheers,
Alessandro

* 2.6.28.1 friendly

Signed-off-by: Alessandro Di Marco <dmr@c0nc3pt.com>

==============================================================================
diff -uprN old/drivers/input/input.c new/drivers/input/input.c
--- old/drivers/input/input.c	2009-01-21 01:30:10.000000000 +0100
+++ new/drivers/input/input.c	2009-01-21 01:30:10.000000000 +0100
@@ -72,6 +72,12 @@ static void input_pass_event(struct inpu
 			     unsigned int type, unsigned int code, int value)
 {
 	struct input_handle *handle;
+	
+	dev->activity = ktime_get();
+
+	if (dev->activity_notifier) {
+		sysfs_notify_dirent(dev->activity_notifier);
+	}

 	rcu_read_lock();

@@ -1012,11 +1018,25 @@ static ssize_t input_dev_show_modalias(s
 }
 static DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL);

+static ssize_t input_dev_show_activity(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct input_dev *id = to_input_dev(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "A: %Ld\nD: %Ld\n",
+			 ktime_to_ns(id->activity),
+			 ktime_to_us(ktime_sub(ktime_get(), id->activity)));
+}
+
+static DEVICE_ATTR(activity, S_IRUGO, input_dev_show_activity, NULL);
+
 static struct attribute *input_dev_attrs[] = {
 	&dev_attr_name.attr,
 	&dev_attr_phys.attr,
 	&dev_attr_uniq.attr,
 	&dev_attr_modalias.attr,
+	&dev_attr_activity.attr,
 	NULL
 };

@@ -1227,10 +1247,34 @@ static int input_dev_uevent(struct devic
 	return 0;
 }

+static inline int warp_activity(struct device *dev) 
+{
+	struct input_dev *id = to_input_dev(dev);
+
+	id->activity = ktime_sub(ktime_get(), id->activity);
+	return 0;
+}
+
+int input_dev_suspend(struct device *dev, pm_message_t state)
+{
+	if (state.event & (PM_EVENT_FREEZE|PM_EVENT_SUSPEND)) {
+		(void) warp_activity(dev);
+	}
+
+	return 0;
+}
+
+int input_dev_resume(struct device *dev)
+{
+	return warp_activity(dev);
+}
+
 static struct device_type input_dev_type = {
 	.groups		= input_dev_attr_groups,
 	.release	= input_dev_release,
 	.uevent		= input_dev_uevent,
+	.suspend	= input_dev_suspend,
+	.resume		= input_dev_resume,
 };

 struct class input_class = {
@@ -1401,6 +1445,8 @@ int input_register_device(struct input_d
 		dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
 	kfree(path);

+	dev->activity_notifier = sysfs_get_dirent(dev->dev.kobj.sd, "activity");
+	
 	error = mutex_lock_interruptible(&input_mutex);
 	if (error) {
 		device_del(&dev->dev);
@@ -1446,6 +1492,9 @@ void input_unregister_device(struct inpu

 	mutex_unlock(&input_mutex);

+	sysfs_put(dev->activity_notifier);
+	dev->activity_notifier = NULL;
+
 	device_unregister(&dev->dev);
 }
 EXPORT_SYMBOL(input_unregister_device);
diff -uprN old/include/linux/input.h new/include/linux/input.h
--- old/include/linux/input.h	2009-01-21 01:30:10.000000000 +0100
+++ new/include/linux/input.h	2009-01-21 01:30:10.000000000 +0100
@@ -1094,6 +1094,9 @@ struct input_dev {

 	struct input_handle *grab;

+	struct sysfs_dirent *activity_notifier;
+	ktime_t activity;
+	
 	spinlock_t event_lock;
 	struct mutex mutex;

==============================================================================
diff -uprN old/SIN.pl new/SIN.pl
--- old/SIN.pl	2009-01-21 01:38:07.000000000 +0100
+++ new/SIN.pl	2009-01-21 01:38:01.000000000 +0100
@@ -0,0 +1,259 @@
+#!/usr/bin/perl -w
+
+# Copyright (C) 2009 Alessandro Di Marco <dmr@c0nc3pt.com>
+
+# This file is part of SIN.
+
+# SIN is free software: you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+
+# SIN 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 General Public License for more details.
+
+# You should have received a copy of the GNU General Public License along with
+# SIN.  If not, see <http://www.gnu.org/licenses/>.
+
+use strict;
+
+use IO::File;
+use IO::Select;
+use Getopt::Long;
+use Pod::Usage;
+
+use Storable;
+
+use constant CONFIG => 'SIN.conf';
+use constant ENROLL_TIMEOUT => 5;
+
+my @script = ( { timeout => 5, action => 'echo "dim screen"' },
+	       { timeout => 10, action => 'echo "blank screen"' },
+	       { timeout => 20, action => 'echo "system suspend"' } );
+
+use constant RESUME => 'echo "restore screen"';
+
+sub devices {
+    open my $devices, "/proc/bus/input/devices"
+	or die "uhm, no input devices?!?";
+
+    my $name;
+    my $devs;
+
+    while (<$devices>) {
+	$name = $1 if $_ =~ /^N: Name=\"(.*)\"\n$/;
+	$devs->{$1} = $name if $_ =~ /S: Sysfs=(.*)\n$/;
+    }
+
+    $devs;
+}
+
+sub activity {
+    my ($enrolled) = @_;
+
+    $enrolled = $_ unless defined $enrolled;
+
+    my $activities;
+
+    foreach (keys %$enrolled) {
+	open my $handle, "/sys$_/activity"
+	    or die "missing activity on $_; please apply the patch!";
+
+	my @data = <$handle>;
+
+	$data[0] =~ /A: (0|[1-9][0-9]*)\n$/;
+	my $last = $1;
+
+	$data[1] =~ /D: (0|[1-9][0-9]*)\n$/;
+	my $delta = $1;
+
+	$activities->{$handle} = {
+	    'path' => $_,
+	    'handle' => $handle,
+	    'last' => $last,
+	    'delta' => $delta,
+	};
+    }
+
+    $activities;
+}
+
+sub latest {
+    my ($activities) = @_;
+
+    $activities = $_ unless defined $activities;
+
+    foreach (values %$activities) {
+	my $handle = $_->{'handle'};
+
+	seek $handle, 0, SEEK_SET;
+
+	my @data = <$handle>;
+
+	$data[0] =~ /A: (0|[1-9][0-9]*)\n$/;
+	$_->{'last'} = $1;
+
+	$data[1] =~ /D: (0|[1-9][0-9]*)\n$/;
+	$_->{'delta'} = $1;
+    }
+
+    $activities;
+}
+
+sub configure {
+    my ($devs) = @_;
+
+    select(undef, undef, undef, 0.1);
+
+    print "Input device enrolling:\n";
+    print "  please interact with the desired input devices;\n";
+    print "  the process will terminate after " . ENROLL_TIMEOUT
+	. " seconds from the last enrollment.\n";
+
+    my $activities = activity($devs);
+
+    my $select = new IO::Select(map { $_->{'handle'} } values %$activities);
+
+    while ($select->handles) {
+	last unless my @picked = $select->can_read(ENROLL_TIMEOUT);
+
+    	foreach (@picked) {
+    	    print "Enrolled $devs->{$activities->{$_}->{'path'}}\n";
+    	}
+
+    	$select->remove(@picked);
+    }
+
+    delete $activities->{$_} foreach ($select->handles);
+
+    my $enrolled;
+
+    foreach (map { $_->{'path'} } values %$activities) {
+	$enrolled->{$_} = $devs->{$_};
+    }
+
+    $enrolled;
+}
+
+my %opts = ();
+
+GetOptions('configure' => \$opts{'configure'},
+	   'help|?' => \$opts{'help'},
+	   'man' => \$opts{'man'})
+    or pod2usage(2);
+
+pod2usage(1) if $opts{'help'};
+pod2usage('-exitstatus' => 0, '-verbose' => 2) if $opts{'man'};
+
+my $enrolled;
+
+if ($opts{'configure'}) {
+    $enrolled = configure devices;
+    store $enrolled, CONFIG;
+    exit 0;
+}
+
+die "No usable configuration found" unless -e CONFIG;
+
+$enrolled = retrieve(CONFIG)
+    or die "uhm, invalid configuration?!?\n";
+
+my $devs = devices;
+
+foreach (keys %$enrolled) {
+    die "uhm, stored configuration doesn't match?!?\n"
+	unless defined $devs->{$_};
+}
+
+sub latter_of {
+    my ($activities) = @_;
+
+    $activities = $_ unless defined $activities;
+
+    my $last = 0;
+    my $me;
+
+    foreach (values %$activities) {
+	if ($_->{'last'} > $last) {
+	    $last = $_->{'last'};
+	    $me = $_;
+	}
+    }
+
+    $me;
+}
+
+sub warp {
+    (($_[0]->{'timeout'} * 1000000) - $_[1]->{'delta'}) / 1000000;
+}
+
+my $activities = activity $enrolled;
+my $select = new IO::Select(map { $_->{'handle'} } values %$activities);
+
+start:
+    while (1) {
+	my $last = latter_of latest $activities;
+	my $trig = 0;
+
+	for (my $i = 0; $i < @script; $i++) {
+	    if ($trig) {
+		if ($select->can_read(warp($script[$i], $last))) {
+		    system RESUME;
+		    next start;
+		}
+
+		$last = latter_of latest $activities;
+	    } else {
+		while ((my $timeout = warp($script[$i], $last)) > 0) {
+		    select(undef, undef, undef, $timeout);
+		    $last = latter_of latest $activities;
+		}
+
+		$trig = 1;
+	    }
+
+	    system $script[$i]->{'action'};
+	}
+
+	$select->can_read;
+
+	system RESUME;
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+SIN - System Inactivity Notifier
+
+=head1 SYNOPSIS
+
+B<SIN> [B<--configure>] [B<--help>] [B<--man>]
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<--configure>
+
+Let the user configure SIN.
+
+=item B<--help>
+
+Print a brief help message and exits.
+
+=item B<--man>
+
+Prints the manual page and exits.
+
+=back
+
+=head1 DESCRIPTION
+
+B<SIN> is an inactivity notifier. This means that you can instruct SIN to
+perform different actions depending on user's activity.
+
+=cut

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH] input: Activity counters
  2007-01-30  9:42                               ` Vojtech Pavlik
                                                   ` (3 preceding siblings ...)
  2009-01-27  0:54                                 ` Alessandro Di Marco
@ 2009-01-27  0:54                                 ` Alessandro Di Marco
  4 siblings, 0 replies; 46+ messages in thread
From: Alessandro Di Marco @ 2009-01-27  0:54 UTC (permalink / raw)
  To: Vojtech Pavlik; +Cc: Pavel Machek, linux-kernel

Hi. I think to have sorted out every issues at last; maybe you are still
interested in merging activity counters.

Best,

Vojtech Pavlik <vojtech@suse.cz> writes:

   On Mon, Jan 29, 2007 at 11:42:08PM +0100, Alessandro Di Marco wrote:
   > Pavel Machek <pavel@ucw.cz> writes:
   > 
   >    Hi!
   > 
   >    >    The /proc/bus/input/devices has an extensible structure. You can just
   >    >    add an "A:" line (for Activity) instead of adding a new proc file.
   >    > 
   >    > I know, but IMO there is too much stuff to parse in there. Activity counters
   >    > are frequently accessed by daemons, and four or five concurrent daemons are the
   >    > norm in a typical X11 linux box...
   > 
   >    Syscalls are fast enough, and the file is _very_ easy (=> fast) to parse.
   > 
   >    >    Also, the activity counters should IMO coincide with the event times
   >    >    passed through /dev/input/event, and should not be jiffies based.
   >    >    Ideally, both should be based on clock_gettime(CLOCK_MONOTONIC).
   >    > 
   >    > In evdev.c do_gettimeofday() is used. Anyway I just need of a monotonic
   >    > counter, so get_jiffies_64() wouldn't be better? It isn't affected by wrapping
   >    > issues and it is probably faster than do_gtod().
   > 
   >    Just use same time source rest of inputs already do...
   > 
   > OK, but what about the time-warp problem?. To fix it I need to know when the
   > system goes to sleep/resumes. In SIN I've solved via the platform driver,
   > introducing suspend() resume() callbacks...

   Well, you just need to make sure that a resume() actually is a visible
   event ...

Hello,

the attached patch* introduces activity counters into input device drivers so
that a proper user-space daemon leveraging these counters can implement event
notification facilities (e.g. screen savers, intrusion detection, etc.)  At
this purpose, a second patch has been also attached: it provides a working Perl
daemon simulating a screen saver activity.

I had submitted something similar a couple of years ago, by way of a kernel
module named SIN (http://lkml.org/lkml/2007/1/18/138)

Despite SIN has been properly maintained in this period, it revealed
soon some design weaknesses which urged me towards alternative
approaches. The resulting patch is quite straightforward yet provides
good features, such as:

1) daemons retain the whole control logic

2) daemons are EVIOCGRAB hassle free

3) daemons do not get biased towards a specific environment (e.g. one monitor,
   one keyboard...)

That's it.

Cheers,
Alessandro

* 2.6.28.1 friendly

Signed-off-by: Alessandro Di Marco <dmr@c0nc3pt.com>

==============================================================================
diff -uprN old/drivers/input/input.c new/drivers/input/input.c
--- old/drivers/input/input.c	2009-01-21 01:30:10.000000000 +0100
+++ new/drivers/input/input.c	2009-01-21 01:30:10.000000000 +0100
@@ -72,6 +72,12 @@ static void input_pass_event(struct inpu
 			     unsigned int type, unsigned int code, int value)
 {
 	struct input_handle *handle;
+	
+	dev->activity = ktime_get();
+
+	if (dev->activity_notifier) {
+		sysfs_notify_dirent(dev->activity_notifier);
+	}

 	rcu_read_lock();

@@ -1012,11 +1018,25 @@ static ssize_t input_dev_show_modalias(s
 }
 static DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL);

+static ssize_t input_dev_show_activity(struct device *dev,
+				       struct device_attribute *attr,
+				       char *buf)
+{
+	struct input_dev *id = to_input_dev(dev);
+
+	return scnprintf(buf, PAGE_SIZE, "A: %Ld\nD: %Ld\n",
+			 ktime_to_ns(id->activity),
+			 ktime_to_us(ktime_sub(ktime_get(), id->activity)));
+}
+
+static DEVICE_ATTR(activity, S_IRUGO, input_dev_show_activity, NULL);
+
 static struct attribute *input_dev_attrs[] = {
 	&dev_attr_name.attr,
 	&dev_attr_phys.attr,
 	&dev_attr_uniq.attr,
 	&dev_attr_modalias.attr,
+	&dev_attr_activity.attr,
 	NULL
 };

@@ -1227,10 +1247,34 @@ static int input_dev_uevent(struct devic
 	return 0;
 }

+static inline int warp_activity(struct device *dev) 
+{
+	struct input_dev *id = to_input_dev(dev);
+
+	id->activity = ktime_sub(ktime_get(), id->activity);
+	return 0;
+}
+
+int input_dev_suspend(struct device *dev, pm_message_t state)
+{
+	if (state.event & (PM_EVENT_FREEZE|PM_EVENT_SUSPEND)) {
+		(void) warp_activity(dev);
+	}
+
+	return 0;
+}
+
+int input_dev_resume(struct device *dev)
+{
+	return warp_activity(dev);
+}
+
 static struct device_type input_dev_type = {
 	.groups		= input_dev_attr_groups,
 	.release	= input_dev_release,
 	.uevent		= input_dev_uevent,
+	.suspend	= input_dev_suspend,
+	.resume		= input_dev_resume,
 };

 struct class input_class = {
@@ -1401,6 +1445,8 @@ int input_register_device(struct input_d
 		dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
 	kfree(path);

+	dev->activity_notifier = sysfs_get_dirent(dev->dev.kobj.sd, "activity");
+	
 	error = mutex_lock_interruptible(&input_mutex);
 	if (error) {
 		device_del(&dev->dev);
@@ -1446,6 +1492,9 @@ void input_unregister_device(struct inpu

 	mutex_unlock(&input_mutex);

+	sysfs_put(dev->activity_notifier);
+	dev->activity_notifier = NULL;
+
 	device_unregister(&dev->dev);
 }
 EXPORT_SYMBOL(input_unregister_device);
diff -uprN old/include/linux/input.h new/include/linux/input.h
--- old/include/linux/input.h	2009-01-21 01:30:10.000000000 +0100
+++ new/include/linux/input.h	2009-01-21 01:30:10.000000000 +0100
@@ -1094,6 +1094,9 @@ struct input_dev {

 	struct input_handle *grab;

+	struct sysfs_dirent *activity_notifier;
+	ktime_t activity;
+	
 	spinlock_t event_lock;
 	struct mutex mutex;

==============================================================================
diff -uprN old/SIN.pl new/SIN.pl
--- old/SIN.pl	2009-01-21 01:38:07.000000000 +0100
+++ new/SIN.pl	2009-01-21 01:38:01.000000000 +0100
@@ -0,0 +1,259 @@
+#!/usr/bin/perl -w
+
+# Copyright (C) 2009 Alessandro Di Marco <dmr@c0nc3pt.com>
+
+# This file is part of SIN.
+
+# SIN is free software: you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+
+# SIN 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 General Public License for more details.
+
+# You should have received a copy of the GNU General Public License along with
+# SIN.  If not, see <http://www.gnu.org/licenses/>.
+
+use strict;
+
+use IO::File;
+use IO::Select;
+use Getopt::Long;
+use Pod::Usage;
+
+use Storable;
+
+use constant CONFIG => 'SIN.conf';
+use constant ENROLL_TIMEOUT => 5;
+
+my @script = ( { timeout => 5, action => 'echo "dim screen"' },
+	       { timeout => 10, action => 'echo "blank screen"' },
+	       { timeout => 20, action => 'echo "system suspend"' } );
+
+use constant RESUME => 'echo "restore screen"';
+
+sub devices {
+    open my $devices, "/proc/bus/input/devices"
+	or die "uhm, no input devices?!?";
+
+    my $name;
+    my $devs;
+
+    while (<$devices>) {
+	$name = $1 if $_ =~ /^N: Name=\"(.*)\"\n$/;
+	$devs->{$1} = $name if $_ =~ /S: Sysfs=(.*)\n$/;
+    }
+
+    $devs;
+}
+
+sub activity {
+    my ($enrolled) = @_;
+
+    $enrolled = $_ unless defined $enrolled;
+
+    my $activities;
+
+    foreach (keys %$enrolled) {
+	open my $handle, "/sys$_/activity"
+	    or die "missing activity on $_; please apply the patch!";
+
+	my @data = <$handle>;
+
+	$data[0] =~ /A: (0|[1-9][0-9]*)\n$/;
+	my $last = $1;
+
+	$data[1] =~ /D: (0|[1-9][0-9]*)\n$/;
+	my $delta = $1;
+
+	$activities->{$handle} = {
+	    'path' => $_,
+	    'handle' => $handle,
+	    'last' => $last,
+	    'delta' => $delta,
+	};
+    }
+
+    $activities;
+}
+
+sub latest {
+    my ($activities) = @_;
+
+    $activities = $_ unless defined $activities;
+
+    foreach (values %$activities) {
+	my $handle = $_->{'handle'};
+
+	seek $handle, 0, SEEK_SET;
+
+	my @data = <$handle>;
+
+	$data[0] =~ /A: (0|[1-9][0-9]*)\n$/;
+	$_->{'last'} = $1;
+
+	$data[1] =~ /D: (0|[1-9][0-9]*)\n$/;
+	$_->{'delta'} = $1;
+    }
+
+    $activities;
+}
+
+sub configure {
+    my ($devs) = @_;
+
+    select(undef, undef, undef, 0.1);
+
+    print "Input device enrolling:\n";
+    print "  please interact with the desired input devices;\n";
+    print "  the process will terminate after " . ENROLL_TIMEOUT
+	. " seconds from the last enrollment.\n";
+
+    my $activities = activity($devs);
+
+    my $select = new IO::Select(map { $_->{'handle'} } values %$activities);
+
+    while ($select->handles) {
+	last unless my @picked = $select->can_read(ENROLL_TIMEOUT);
+
+    	foreach (@picked) {
+    	    print "Enrolled $devs->{$activities->{$_}->{'path'}}\n";
+    	}
+
+    	$select->remove(@picked);
+    }
+
+    delete $activities->{$_} foreach ($select->handles);
+
+    my $enrolled;
+
+    foreach (map { $_->{'path'} } values %$activities) {
+	$enrolled->{$_} = $devs->{$_};
+    }
+
+    $enrolled;
+}
+
+my %opts = ();
+
+GetOptions('configure' => \$opts{'configure'},
+	   'help|?' => \$opts{'help'},
+	   'man' => \$opts{'man'})
+    or pod2usage(2);
+
+pod2usage(1) if $opts{'help'};
+pod2usage('-exitstatus' => 0, '-verbose' => 2) if $opts{'man'};
+
+my $enrolled;
+
+if ($opts{'configure'}) {
+    $enrolled = configure devices;
+    store $enrolled, CONFIG;
+    exit 0;
+}
+
+die "No usable configuration found" unless -e CONFIG;
+
+$enrolled = retrieve(CONFIG)
+    or die "uhm, invalid configuration?!?\n";
+
+my $devs = devices;
+
+foreach (keys %$enrolled) {
+    die "uhm, stored configuration doesn't match?!?\n"
+	unless defined $devs->{$_};
+}
+
+sub latter_of {
+    my ($activities) = @_;
+
+    $activities = $_ unless defined $activities;
+
+    my $last = 0;
+    my $me;
+
+    foreach (values %$activities) {
+	if ($_->{'last'} > $last) {
+	    $last = $_->{'last'};
+	    $me = $_;
+	}
+    }
+
+    $me;
+}
+
+sub warp {
+    (($_[0]->{'timeout'} * 1000000) - $_[1]->{'delta'}) / 1000000;
+}
+
+my $activities = activity $enrolled;
+my $select = new IO::Select(map { $_->{'handle'} } values %$activities);
+
+start:
+    while (1) {
+	my $last = latter_of latest $activities;
+	my $trig = 0;
+
+	for (my $i = 0; $i < @script; $i++) {
+	    if ($trig) {
+		if ($select->can_read(warp($script[$i], $last))) {
+		    system RESUME;
+		    next start;
+		}
+
+		$last = latter_of latest $activities;
+	    } else {
+		while ((my $timeout = warp($script[$i], $last)) > 0) {
+		    select(undef, undef, undef, $timeout);
+		    $last = latter_of latest $activities;
+		}
+
+		$trig = 1;
+	    }
+
+	    system $script[$i]->{'action'};
+	}
+
+	$select->can_read;
+
+	system RESUME;
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+SIN - System Inactivity Notifier
+
+=head1 SYNOPSIS
+
+B<SIN> [B<--configure>] [B<--help>] [B<--man>]
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<--configure>
+
+Let the user configure SIN.
+
+=item B<--help>
+
+Print a brief help message and exits.
+
+=item B<--man>
+
+Prints the manual page and exits.
+
+=back
+
+=head1 DESCRIPTION
+
+B<SIN> is an inactivity notifier. This means that you can instruct SIN to
+perform different actions depending on user's activity.
+
+=cut

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-25 15:43                 ` Alessandro Di Marco
@ 2007-01-25 16:03                   ` Scott Preece
  0 siblings, 0 replies; 46+ messages in thread
From: Scott Preece @ 2007-01-25 16:03 UTC (permalink / raw)
  To: Alessandro Di Marco; +Cc: 7eggert, Pavel Machek, linux-kernel, vojtech

On 1/25/07, Alessandro Di Marco <dmr@gmx.it> wrote:
> "Scott Preece" <sepreece@gmail.com> writes:
>
>    On 1/25/07, Bodo Eggert <7eggert@gmx.de> wrote:
>    > Imagine one computer serving two users. Two monitors, two keyboards ...
>    ---
>
>    Good point! Of late I've been working on single-user systems, so it
>    was not at the front of my brain, despite years of building and using
>    multi-user systems.
>
>    It's a point that multi-user systems have struggled with forever (when
>    somebody inserts a CR in the drive mounted in the system box, which user do
>    you pop up a media player for?).
>
> sed s/user X's screensaver/suspend to disk/g <<EOF
>
>    I tend to think it's not a kernel-vs-user-space issue, though. To
>    solve it you need, somewhere, a notion of a "user session" and you
>    need some way to separate system-level issues (like low-battery) from
>    user-level issues (like activiating user X's screensaver).
>
> EOF
>
> Are you sure?
---

Well, a screensaver is associated with a particular user (and screen),
but suspend-to-disk is a system-wide activity that would affect all
the users. So you need to be able to separate those notions in
deciding what actions to take.

scott

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-25 15:18               ` Scott Preece
@ 2007-01-25 15:43                 ` Alessandro Di Marco
  2007-01-25 16:03                   ` Scott Preece
  0 siblings, 1 reply; 46+ messages in thread
From: Alessandro Di Marco @ 2007-01-25 15:43 UTC (permalink / raw)
  To: Scott Preece; +Cc: 7eggert, Pavel Machek, linux-kernel, vojtech

"Scott Preece" <sepreece@gmail.com> writes:

   On 1/25/07, Bodo Eggert <7eggert@gmx.de> wrote:
   > Imagine one computer serving two users. Two monitors, two keyboards ...
   ---

   Good point! Of late I've been working on single-user systems, so it
   was not at the front of my brain, despite years of building and using
   multi-user systems.

   It's a point that multi-user systems have struggled with forever (when
   somebody inserts a CR in the drive mounted in the system box, which user do
   you pop up a media player for?).

sed s/user X's screensaver/suspend to disk/g <<EOF

   I tend to think it's not a kernel-vs-user-space issue, though. To
   solve it you need, somewhere, a notion of a "user session" and you
   need some way to separate system-level issues (like low-battery) from
   user-level issues (like activiating user X's screensaver).

EOF

Are you sure?

best,

-- 
Experience teaches slowly and at the cost of mistakes. - James A. Froude

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
  2007-01-25 12:28             ` Bodo Eggert
@ 2007-01-25 15:18               ` Scott Preece
  2007-01-25 15:43                 ` Alessandro Di Marco
  0 siblings, 1 reply; 46+ messages in thread
From: Scott Preece @ 2007-01-25 15:18 UTC (permalink / raw)
  To: 7eggert; +Cc: Pavel Machek, Alessandro Di Marco, linux-kernel, vojtech

On 1/25/07, Bodo Eggert <7eggert@gmx.de> wrote:
> Scott Preece <sepreece@gmail.com> wrote:
>
> > My own hot button is making sure that the definition of what
> > constitutes user activity is managed in exactly one place, whether in
> > the kernel or not. My naive model would be to put the response at user
> > level, but to provide a single point of definition in the kernel (say,
> > /dev/useractivity or the equivalent) that the user-level daemon could
> > listen to.
>
> Imagine one computer serving two users. Two monitors, two keyboards ...
---

Good point! Of late I've been working on single-user systems, so it
was not at the front of my brain, despite years of building and using
multi-user systems.

It's a point that multi-user systems have struggled with forever (when
somebody inserts a CR in the drive mounted in the system box, which
user do you pop up a media player for?).

I tend to think it's not a kernel-vs-user-space issue, though. To
solve it you need, somewhere, a notion of a "user session" and you
need some way to separate system-level issues (like low-battery) from
user-level issues (like activiating user X's screensaver).

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [ANNOUNCE] System Inactivity Monitor v1.0
       [not found]           ` <7GzF3-4L6-47@gated-at.bofh.it>
@ 2007-01-25 12:28             ` Bodo Eggert
  2007-01-25 15:18               ` Scott Preece
  0 siblings, 1 reply; 46+ messages in thread
From: Bodo Eggert @ 2007-01-25 12:28 UTC (permalink / raw)
  To: Scott Preece, Pavel Machek, Alessandro Di Marco, linux-kernel, vojtech

Scott Preece <sepreece@gmail.com> wrote:

> My own hot button is making sure that the definition of what
> constitutes user activity is managed in exactly one place, whether in
> the kernel or not. My naive model would be to put the response at user
> level, but to provide a single point of definition in the kernel (say,
> /dev/useractivity or the equivalent) that the user-level daemon could
> listen to.

Imagine one computer serving two users. Two monitors, two keyboards ...
-- 
Funny quotes:
38. Last night I played a blank tape at full blast. The mime next door went
    nuts.
Friß, Spammer: hEOzVVU@voJ.7eggert.dyndns.org

^ permalink raw reply	[flat|nested] 46+ messages in thread

end of thread, other threads:[~2009-01-27  1:17 UTC | newest]

Thread overview: 46+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-01-18 19:29 [ANNOUNCE] System Inactivity Monitor v1.0 Alessandro Di Marco
2007-01-19  7:38 ` Arjan van de Ven
2007-01-19 14:49   ` Alessandro Di Marco
2007-01-19 17:45     ` Scott Preece
2007-01-19 22:21       ` Jan Engelhardt
2007-01-19 22:30         ` Scott Preece
2007-01-19 10:11 ` Pavel Machek
2007-01-21 21:04   ` Jan Engelhardt
2007-01-23  9:38     ` Pavel Machek
2007-01-22 12:46   ` Alessandro Di Marco
2007-01-23  9:41     ` Pavel Machek
2007-01-23 14:14       ` Alessandro Di Marco
2007-01-23 16:34         ` Pavel Machek
2007-01-23 17:11           ` Alessandro Di Marco
2007-01-23 18:44             ` Pavel Machek
2007-01-24  2:51               ` Alessandro Di Marco
2007-01-26 17:15                 ` Pavel Machek
2007-01-26 17:55                   ` Alessandro Di Marco
2007-01-27 17:45                     ` Pavel Machek
2007-01-27 19:20                       ` Vojtech Pavlik
2007-01-29 13:58                         ` Alessandro Di Marco
2007-01-29 22:28                           ` Pavel Machek
2007-01-29 22:42                             ` Alessandro Di Marco
2007-01-30  0:03                               ` Pavel Machek
2007-01-30  9:42                               ` Vojtech Pavlik
2007-01-30 12:33                                 ` Alessandro Di Marco
2007-01-30 13:09                                   ` Vojtech Pavlik
2007-01-30 15:22                                     ` Alessandro Di Marco
2009-01-27  0:52                                 ` [PATCH] input: Activity counters Alessandro Di Marco
2009-01-27  0:54                                 ` Alessandro Di Marco
2009-01-27  0:54                                 ` Alessandro Di Marco
2009-01-27  0:54                                 ` Alessandro Di Marco
2007-01-29  8:24                       ` [ANNOUNCE] System Inactivity Monitor v1.0 Stefan Seyfried
2007-01-24 18:08               ` Alessandro Di Marco
2007-01-23 19:01           ` Mattia Dongili
2007-01-23 19:02             ` Pavel Machek
2007-01-23 20:07               ` Mattia Dongili
2007-01-23 19:34           ` Scott Preece
2007-01-24  2:02             ` Alessandro Di Marco
2007-01-24 14:01             ` Pavel Machek
2007-01-19 21:18 ` Bill Davidsen
2007-01-20 15:37   ` Alessandro Di Marco
     [not found] <7ELhf-4rC-9@gated-at.bofh.it>
     [not found] ` <7FKM6-7Gy-1@gated-at.bofh.it>
     [not found]   ` <7G6ME-1g2-11@gated-at.bofh.it>
     [not found]     ` <7GqrZ-6YY-1@gated-at.bofh.it>
     [not found]       ` <7GuFj-5pj-5@gated-at.bofh.it>
     [not found]         ` <7GwQL-h3-7@gated-at.bofh.it>
     [not found]           ` <7GzF3-4L6-47@gated-at.bofh.it>
2007-01-25 12:28             ` Bodo Eggert
2007-01-25 15:18               ` Scott Preece
2007-01-25 15:43                 ` Alessandro Di Marco
2007-01-25 16:03                   ` Scott Preece

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).