[ANNOUNCE] System Inactivity Monitor v1.0
From: Alessandro Di Marco
Date: Thu Jan 18 2007 - 14:29:40 EST
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,
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@xxxxxxxxxxx>");
+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, ¬ify))) {
+ clear_bit(1, ¬ify);
+ 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, ¬ify) &&
+ test_and_clear_bit(0, ¬ify))) {
+ occasionally_generate_event(acpi_device);
+ }
+
+ timely_generate_event(acpi_device,
+ atomic_read(&interactions), ¬ify);
+
+ 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@xxxxxxxxxxx>\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 */
--
Man is the only animal that laughs and has a state legislature. - Samuel Butler