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 "[EMAIL PROTECTED] [EMAIL PROTECTED]@]}\n${handle}\n${reset}\n${resume}" > $1 + +for (( i = 0; ${i}<[EMAIL PROTECTED]; 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 <[EMAIL PROTECTED]>"); +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 <[EMAIL PROTECTED]>\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