diff -ur --new-file SIN/Makefile SIN-NEW/Makefile
--- SIN/Makefile 1970-01-01 01:00:00.000000000 +0100
+++ SIN-NEW/Makefile 2007-01-25 14:07:44.000000000 +0100
@@ -0,0 +1,43 @@
+MODLPATH = kernel/drivers/char
+
+#DEBUG="-D SIN_DEBUG"
+
+MODL = sinmod
+OBJS = sin.o sysfs.o table.o input_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 -ur --new-file SIN/debug.h SIN-NEW/debug.h
--- SIN/debug.h 1970-01-01 01:00:00.000000000 +0100
+++ SIN-NEW/debug.h 2007-01-25 14:07:39.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 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 -ur --new-file SIN/etc/sin/rules.sh SIN-NEW/etc/sin/rules.sh
--- SIN/etc/sin/rules.sh 1970-01-01 01:00:00.000000000 +0100
+++ SIN-NEW/etc/sin/rules.sh 2007-01-25 14:16:30.000000000 +0100
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+set $*
+
+case "${HINT}" in
+ resume)
+# if [ -f /dev/shm/blanked ] ; then
+# XAUTHORITY=/home/`/bin/ps x | /bin/grep tty11 | /bin/grep auth | /usr/bin/cut -d / -f 3`/.Xauthority xset -display :0 dpms force on
+# /usr/bin/smartdimmer -f `cat /dev/shm/light` -e ${UNBLANK_SPEED}
+# rm -f /dev/shm/light
+# rm -f /dev/shm/blanked
+# else
+# /usr/bin/smartdimmer -f `cat /dev/shm/light` -e ${FADING_SPEED}
+# rm -f /dev/shm/light
+# fi
+
+ logger "resuming..."
+ ;;
+
+ dim)
+# /usr/bin/smartdimmer -g | /usr/bin/cut -d ':' -f 2 > /dev/shm/light
+# /usr/bin/smartdimmer -f 1 -e ${FADING_SPEED}
+
+ logger "dimming..."
+ ;;
+
+ blank)
+# XAUTHORITY=/home/`/bin/ps x | /bin/grep tty11 | /bin/grep auth | /usr/bin/cut -d / -f 3`/.Xauthority xset -display :0 dpms force off
+# touch /dev/shm/blanked
+
+ logger "blanking..."
+ ;;
+
+ sleep)
+# echo mem >/sys/power/state
+
+ logger "suspending..."
+ ;;
+
+ *)
+ logger "SIN hint unhandled: ${HINT}"
+ ;;
+esac
diff -ur --new-file SIN/etc/sin/setup.sh SIN-NEW/etc/sin/setup.sh
--- SIN/etc/sin/setup.sh 1970-01-01 01:00:00.000000000 +0100
+++ SIN-NEW/etc/sin/setup.sh 2007-01-25 14:13:59.000000000 +0100
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+set $*
+
+STATE="`cat /proc/acpi/ac_adapter/ACAD/state | grep off-line`"
+
+if [ -n "${STATE}" ] ; then
+ cat /etc/sin/table.bat >/sys/class/misc/sin/table
+else
+ cat /etc/sin/table.ac >/sys/class/misc/sin/table
+fi
diff -ur --new-file SIN/etc/sin/table.ac SIN-NEW/etc/sin/table.ac
--- SIN/etc/sin/table.ac 1970-01-01 01:00:00.000000000 +0100
+++ SIN-NEW/etc/sin/table.ac 2007-01-25 14:14:15.000000000 +0100
@@ -0,0 +1,8 @@
+0
+2 3
+0 1
+0
+resume
+1200 dim
+3500 blank
+27000 sleep
diff -ur --new-file SIN/etc/sin/table.bat SIN-NEW/etc/sin/table.bat
--- SIN/etc/sin/table.bat 1970-01-01 01:00:00.000000000 +0100
+++ SIN-NEW/etc/sin/table.bat 2007-01-25 14:14:11.000000000 +0100
@@ -0,0 +1,8 @@
+0
+2 3
+0 1
+0
+resume
+500 dim
+900 blank
+3000 sleep
diff -ur --new-file SIN/etc/udev/rules.d/99-sin.rules SIN-NEW/etc/udev/rules.d/99-sin.rules
--- SIN/etc/udev/rules.d/99-sin.rules 1970-01-01 01:00:00.000000000 +0100
+++ SIN-NEW/etc/udev/rules.d/99-sin.rules 2007-01-25 14:13:32.000000000 +0100
@@ -0,0 +1,2 @@
+KERNEL=="sin", SUBSYSTEM=="misc", ACTION=="add", RUN+="/etc/sin/setup.sh"
+KERNEL=="sin", SUBSYSTEM=="misc", ACTION=="change", RUN+="/etc/sin/rules.sh"
diff -ur --new-file SIN/gentable.sh SIN-NEW/gentable.sh
--- SIN/gentable.sh 1970-01-01 01:00:00.000000000 +0100
+++ SIN-NEW/gentable.sh 2007-01-25 14:11:05.000000000 +0100
@@ -0,0 +1,146 @@
+#!/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 -t 25 -r $2
+
+ if [ "$?" == "1" ]; then
+ echo -e "DEFAULT VALUE"
+ fi
+}
+
+if (( $# == 0 )); then
+ echo "$0
"
+ exit
+fi
+
+if [ ! -d "/sys/class/misc/sin" ]; then
+ echo "/sys/class/misc/sin not found, has sinmod been loaded?"
+ exit
+fi
+
+cat <
+and a string. The 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 uevent is generated with the string
+"HINT=" in its environment.
+
+For example, the rule "600 blank" produces the uevent "HINT=blank" when SIN
+recognizes one minute of user inactivity.
+
+Please specify each rule as a space-separated pair on a separate line; when
+finished, just press enter.
+
+EOF
+
+declare -i i
+
+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]="600 dim"
+ rules[1]="1200 blank"
+ rules[2]="12000 sleep"
+ i=3
+fi
+
+cat < jump to the given rule
+ ${i} => sleep forever awaiting for user interaction
+
+EOF
+
+input "Wrap value?" wrap
+
+if [ -z "${wrap}" ]; then
+ wrap="0"
+fi
+
+echo -e "0\n${#devices[@]} ${#rules[@]}\n${devices[@]}\n${wrap}\n${resume}" > $1
+
+for (( i = 0; ${i}<${#rules[@]}; i++ )); do
+ echo "${rules[${i}]}" >> $1
+done
+
+cat </sys/class/misc/sin/table
+
+An "Invalid argument" error indicates a mismatch in the table file, usually due
+to specifying an invalid input device. In that case, restart from scratch,
+double checking your inputs. Have fun!
+
+EOF
diff -ur --new-file 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-25 14:07:39.000000000 +0100
@@ -0,0 +1,198 @@
+/*
+ * 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
+#include
+
+#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;
+}
+
+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;
+
+ 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(void)
+{
+ int err = -ENOMEM;
+
+ const struct input_device_id idi[] = {
+ { .driver_info = 1 }, /* matches all devices */
+ { },
+ };
+
+ struct input_handler ih = {
+ .name = "input enumerator",
+ .id_table = idi,
+ };
+
+ 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");
+ err = -ENODEV;
+ goto cleanout;
+ }
+
+ id = kmalloc(idsize * sizeof (struct input_devices), GFP_KERNEL);
+
+ idsize = 0;
+
+ if (!id) {
+ goto cleanout;
+ }
+
+ ih.connect = input_store;
+ (void) input_register_handler(&ih);
+
+ return 0;
+
+cleanout:
+ kfree(page);
+out:
+ return err;
+}
+
+void free_input_enum(void)
+{
+ if (idsize) {
+ idsize = 0;
+ kfree(id);
+ kfree(page);
+ }
+}
diff -ur --new-file 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-25 14:07:39.000000000 +0100
@@ -0,0 +1,47 @@
+/*
+ * 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
+
+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 size_t get_devices_desc(char **buf);
+
+extern void fill_input_device(struct input_device_id *idi, int i);
+
+extern int input_enum(void);
+extern void free_input_enum(void);
+
+#endif /* INPUT_ENUMERATOR_H */
diff -ur --new-file SIN/sin.c SIN-NEW/sin.c
--- SIN/sin.c 1970-01-01 01:00:00.000000000 +0100
+++ SIN-NEW/sin.c 2007-01-25 14:07:39.000000000 +0100
@@ -0,0 +1,390 @@
+/*
+ * 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
+#include
+#include
+#include
+#include
+
+#include "debug.h"
+
+#include "sin.h"
+#include "table.h"
+#include "sysfs.h"
+#include "input_enumerator.h"
+
+MODULE_AUTHOR("Alessandro Di Marco ");
+MODULE_DESCRIPTION("System Inactivity Notifier");
+MODULE_LICENSE("GPL v2");
+
+MODULE_VERSION("1.6");
+
+#ifdef SIN_DEBUG
+int debug;
+#endif
+
+static struct user_activity uact = {
+ .lock = SPIN_LOCK_UNLOCKED,
+};
+
+static unsigned long status;
+
+static struct timer_list timer;
+static int shutdown;
+
+static struct input_handler ih;
+
+static DEFINE_MUTEX(runlock);
+static int running;
+
+static struct kobject *kobj;
+
+inline unsigned long simulate_activity(void)
+{
+ return register_activity(&uact);
+}
+
+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));
+
+ next = occasionally_generate_event(kobj, last_activity(&uact));
+
+ if (!shutdown) {
+ 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)
+{
+ (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();
+}
+
+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 data)
+{
+ if (!shutdown) {
+ unsigned long next;
+
+ printd(">>>>>>>>>>>> timer_fn()\n");
+
+ set_bit(RULE_LOCK, &status);
+
+ next = timely_generate_event(kobj,
+ last_activity(&uact), &status);
+
+ clear_bit(RULE_LOCK, &status);
+
+ if (unlikely(test_and_clear_bit(RULE_MARK, &status))) {
+ signal_interaction();
+ }
+
+ 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");
+ }
+}
+
+int start_monitor(struct input_device_id *idi)
+{
+ int err;
+
+ mutex_lock(&runlock);
+
+ status = 0;
+
+ 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, 0);
+
+ timer.expires =
+ timely_generate_event(kobj, register_activity(&uact), &status);
+
+ 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);
+
+ signal_interaction();
+ cleanup_table();
+
+ running = 0;
+ }
+
+ 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 __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 = start_sysfs(sin_miscdev.this_device);
+ if (err < 0) {
+ printk(KERN_ERR "SIN: sysfs initialization failed\n");
+ goto cleanout2;
+ }
+
+ kobj = &sin_miscdev.this_device->kobj;
+
+ printk(KERN_DEBUG "System Inactivity Notifier 1.6 - (c) Alessandro Di Marco \n");
+
+ return 0;
+
+cleanout2:
+ free_input_enum();
+cleanout1:
+ misc_deregister(&sin_miscdev);
+out:
+ return err;
+}
+
+static int __devexit sin_remove(struct platform_device *dev)
+{
+ stop_sysfs();
+ free_input_enum();
+
+ (void) stop_monitor();
+
+ misc_deregister(&sin_miscdev);
+
+ return 0;
+}
+
+#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(kobj);
+ }
+
+ 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),
+ .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);
+module_exit(sih_exit);
diff -ur --new-file SIN/sin.h SIN-NEW/sin.h
--- SIN/sin.h 1970-01-01 01:00:00.000000000 +0100
+++ SIN-NEW/sin.h 2007-01-25 14:07:39.000000000 +0100
@@ -0,0 +1,82 @@
+/*
+ * 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
+
+#define MODULE_NAME "sin"
+
+#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;
+ 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;
+}
+
+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);
+
+extern int start_monitor(struct input_device_id *idi);
+extern void stop_monitor(void);
+
+#endif /* SIN_H */
diff -ur --new-file SIN/sysfs.c SIN-NEW/sysfs.c
--- SIN/sysfs.c 1970-01-01 01:00:00.000000000 +0100
+++ SIN-NEW/sysfs.c 2007-01-25 14:07:39.000000000 +0100
@@ -0,0 +1,122 @@
+/*
+ * 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
+#include
+#include
+#include
+
+#include "sin.h"
+#include "table.h"
+#include "sysfs.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_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_table);
+ if (err < 0) {
+ goto cleanout1;
+ }
+
+ err = device_create_file(sindev, &dev_attr_interact);
+ if (err < 0) {
+ goto cleanout2;
+ }
+
+ return 0;
+
+cleanout2:
+ device_remove_file(sindev, &dev_attr_table);
+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_input);
+}
diff -ur --new-file SIN/sysfs.h SIN-NEW/sysfs.h
--- SIN/sysfs.h 1970-01-01 01:00:00.000000000 +0100
+++ SIN-NEW/sysfs.h 2007-01-25 14:07:39.000000000 +0100
@@ -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 -ur --new-file SIN/table.c SIN-NEW/table.c
--- SIN/table.c 1970-01-01 01:00:00.000000000 +0100
+++ SIN-NEW/table.c 2007-01-25 14:07:39.000000000 +0100
@@ -0,0 +1,353 @@
+/*
+ * 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
+#include
+#include
+#include
+
+#include "debug.h"
+
+#include "sin.h"
+#include "uniq.h"
+#include "table.h"
+#include "input_enumerator.h"
+
+static struct table rt;
+static int next_rule;
+
+static inline void generate_event(struct kobject *kobj, int rnum)
+{
+ struct rule *rule = &rt.rules[rnum];
+
+ char *envp_ext[] = {
+ rule->hint,
+ NULL
+ };
+
+ printd("sending hint '%s'\n", rule->hint + sizeof HINT - 1);
+
+ if (kobject_uevent_env(kobj, KOBJ_CHANGE, envp_ext) < 0) {
+ printk(KERN_ERR "SIN: hint '%s' failed\n",
+ rule->hint + sizeof HINT - 1);
+ }
+}
+
+inline void special_event(struct kobject *kobj)
+{
+ generate_event(kobj, rt.rnum);
+}
+
+unsigned long occasionally_generate_event(struct kobject *kobj,
+ unsigned long last)
+{
+ special_event(kobj);
+ return last + rt.rules[next_rule = 0].target;
+}
+
+unsigned long timely_generate_event(struct kobject *kobj,
+ unsigned long last, unsigned long *status)
+{
+ printd("last %lu [status %lu], now %lu -> next target is %lu (%d)\n",
+ 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, user forgot to interact!\n");
+ special_event(kobj);
+ }
+
+ generate_event(kobj, next_rule);
+ set_bit(RULE_TRIG, status);
+ }
+
+ 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);
+
+ last = simulate_activity();
+ } else {
+ printd("last rule, disconnecting the timer\n");
+ set_bit(RULE_OVER, status);
+ }
+ }
+
+ return last + rt.rules[next_rule].target;
+}
+
+#define parse_num(endp) ({ \
+ const char *cp = (endp); \
+ \
+ while (*cp && isspace(*cp)) { \
+ ++cp; \
+ } \
+ \
+ simple_strtol(cp, (char **) &(endp), 10); \
+ })
+
+#define hint_value(key, hint) ((hint) + sizeof (key) - 1)
+
+#define parse_hint(key, endp) ({ \
+ const char *l, *r; \
+ char *h; \
+ \
+ l = endp; \
+ \
+ while (*l && isspace(*l)) { \
+ ++l; \
+ } \
+ \
+ r = l; \
+ \
+ while (isalnum(*r)) { \
+ ++r; \
+ } \
+ \
+ (endp) = r; \
+ \
+ h = kmalloc(hint_value(key, r - l + 1), \
+ GFP_KERNEL); \
+ if (h) { \
+ memcpy(h, (key), hint_value(key, 0)); \
+ memcpy(hint_value(key, h), l, r - l); \
+ \
+ h[hint_value(key, r - l)] = '\0'; \
+ } \
+ \
+ h; \
+ })
+
+static int cmp(const void *l, const void *r)
+{
+ long lt = ((struct rule *) l)->target;
+ long rt = ((struct rule *) r)->target;
+ long dd = lt - rt;
+
+ return dd < 0 ? -1 : dd > 0 ? 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;
+}
+
+static char *table;
+static size_t size;
+
+static int regen_table(void)
+{
+ char *t;
+ int i;
+
+ kfree(table);
+
+ table = t = kmalloc(PAGE_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%s\n",
+ rt.rwrap,
+ hint_value(HINT, rt.rules[rt.rnum].hint));
+
+ for (i = 0; i < rt.rnum; i++) {
+ t += sprintf(t, "%d %s\n",
+ jiffies_to_msecs(rt.rules[i].target) / 100,
+ hint_value(HINT, rt.rules[i].hint));
+ }
+
+ size = t - table;
+
+ return 0;
+}
+
+int push_table(const char *buf, unsigned long count)
+{
+ struct table nrt;
+ struct input_device_id *idi;
+ struct uniq uniq;
+
+ int devices;
+
+ int i, err = -ENOMEM;
+
+ devices = get_devices();
+
+ nrt.debug = parse_num(buf);
+
+ nrt.dnum = parse_num(buf);
+ nrt.rnum = parse_num(buf);
+
+ if (out_of_range(0, nrt.dnum, devices)) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (nrt.rnum <= 0) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ nrt.devices = kmalloc(nrt.dnum * sizeof (int), GFP_KERNEL);
+ if (!nrt.devices) {
+ goto out;
+ }
+
+ nrt.rules = kmalloc((nrt.rnum + 1) * sizeof (struct rule), GFP_KERNEL);
+ if (!nrt.rules) {
+ goto cleanout1;
+ }
+
+ if (uniq_alloc(&uniq, devices) < 0) {
+ goto cleanout2;
+ }
+
+ 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 < nrt.dnum) {
+ err = -EINVAL;
+ goto cleanout2;
+ }
+
+ nrt.rwrap = parse_num(buf);
+ if (out_of_range(0, nrt.rwrap, nrt.rnum + 1)) {
+ err = -EINVAL;
+ goto cleanout2;
+ }
+
+ nrt.rules[nrt.rnum].target = MAX_JIFFY_OFFSET;
+ nrt.rules[nrt.rnum].hint = parse_hint(HINT, buf);
+
+ for (i = 0; i < nrt.rnum; i++) {
+ 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].hint = parse_hint(HINT, buf);
+ }
+
+ sort(nrt.rules, nrt.rnum, sizeof (struct rule), cmp, swap);
+
+ if (!tablecmp(&rt, &nrt)) {
+ err = count;
+ goto cleanout2;
+ }
+
+ stop_monitor();
+
+ idi = kzalloc((nrt.dnum + 1) *
+ sizeof (struct input_device_id), GFP_KERNEL);
+ if (!idi) {
+ goto cleanout2;
+ }
+
+ for (i = 0; i < nrt.dnum; i++) {
+ fill_input_device(&idi[i], nrt.devices[i]);
+ }
+
+ memcpy(&rt, &nrt, sizeof (struct table));
+
+ err = regen_table();
+ if (err < 0) {
+ goto cleanout3;
+ }
+
+ set_debug(rt.debug);
+
+ err = start_monitor(idi);
+ if (err < 0) {
+ goto cleanout3;
+ }
+
+ return count;
+
+cleanout3:
+ kfree(idi);
+ cleanup_table();
+ goto out;
+
+cleanout2:
+ kfree(nrt.rules);
+cleanout1:
+ kfree(nrt.devices);
+out:
+ return err;
+}
+
+size_t pull_table(char **buf)
+{
+ if (!table) {
+ size = sizeof TABLE_HELP;
+
+ table = kmalloc(size, GFP_KERNEL);
+ if (table) {
+ memcpy(table, TABLE_HELP, size);
+ } else {
+ size = 0;
+ }
+ }
+
+ *buf = table;
+
+ 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 -ur --new-file SIN/table.h SIN-NEW/table.h
--- SIN/table.h 1970-01-01 01:00:00.000000000 +0100
+++ SIN-NEW/table.h 2007-01-25 14:07:39.000000000 +0100
@@ -0,0 +1,79 @@
+/*
+ * 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
+
+#define HINT "HINT="
+
+struct rule {
+ unsigned long target; /* jiffies */
+ char *hint;
+};
+
+struct table {
+ int debug;
+ int dnum, rnum;
+ int *devices;
+ int rwrap;
+ struct rule *rules;
+};
+
+static inline int tablecmp(struct table *l, struct table *r)
+{
+ int i, err = 0;
+
+ if (l->debug != r->debug ||
+ l->dnum != r->dnum ||
+ l->rnum != r->rnum ||
+ l->rwrap != r->rwrap) {
+ err = 1;
+ goto out;
+ }
+
+ if (memcmp(l->devices, r->devices, l->dnum * sizeof (int))) {
+ err = 1;
+ goto out;
+ }
+
+ for (i = 0; i < l->rnum; i++) {
+ if (l->rules[i].target != r->rules[i].target
+ || strcmp(l->rules[i].hint, r->rules[i].hint)) {
+ err = 1;
+ goto out;
+ }
+ }
+
+out:
+ return err;
+}
+
+#define TABLE_HELP "\n \n ... \n\n\n \n...\n \n"
+
+extern void special_event(struct kobject *kobj);
+extern unsigned long occasionally_generate_event(struct kobject *kobj, unsigned long last);
+extern unsigned long timely_generate_event(struct kobject *kobj, unsigned long last, unsigned long *notify);
+
+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 */
diff -ur --new-file SIN/uniq.h SIN-NEW/uniq.h
--- SIN/uniq.h 1970-01-01 01:00:00.000000000 +0100
+++ SIN-NEW/uniq.h 2007-01-25 14:07:39.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
+
+#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 */