public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Alessandro Di Marco <dmr@gmx.it>
To: Vojtech Pavlik <vojtech@suse.cz>
Cc: Pavel Machek <pavel@ucw.cz>, linux-kernel@vger.kernel.org
Subject: Re: [PATCH] input: Activity counters
Date: Tue, 27 Jan 2009 01:54:34 +0100	[thread overview]
Message-ID: <87ocxtzgxl.fsf@gmx.it> (raw)
In-Reply-To: 20070130094205.GA2888@suse.cz

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

Best,

Vojtech Pavlik <vojtech@suse.cz> writes:

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

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

Hello,

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

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

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

1) daemons retain the whole control logic

2) daemons are EVIOCGRAB hassle free

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

That's it.

Cheers,
Alessandro

* 2.6.28.1 friendly

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

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

 	rcu_read_lock();

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

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

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

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

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

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

 	mutex_unlock(&input_mutex);

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

 	struct input_handle *grab;

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

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

  parent reply	other threads:[~2009-01-27  1:17 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-01-18 19:29 [ANNOUNCE] System Inactivity Monitor v1.0 Alessandro Di Marco
2007-01-19  7:38 ` Arjan van de Ven
2007-01-19 14:49   ` Alessandro Di Marco
2007-01-19 17:45     ` Scott Preece
2007-01-19 22:21       ` Jan Engelhardt
2007-01-19 22:30         ` Scott Preece
2007-01-19 10:11 ` Pavel Machek
2007-01-21 21:04   ` Jan Engelhardt
2007-01-23  9:38     ` Pavel Machek
2007-01-22 12:46   ` Alessandro Di Marco
2007-01-23  9:41     ` Pavel Machek
2007-01-23 14:14       ` Alessandro Di Marco
2007-01-23 16:34         ` Pavel Machek
2007-01-23 17:11           ` Alessandro Di Marco
2007-01-23 18:44             ` Pavel Machek
2007-01-24  2:51               ` Alessandro Di Marco
2007-01-26 17:15                 ` Pavel Machek
2007-01-26 17:55                   ` Alessandro Di Marco
2007-01-27 17:45                     ` Pavel Machek
2007-01-27 19:20                       ` Vojtech Pavlik
2007-01-29 13:58                         ` Alessandro Di Marco
2007-01-29 22:28                           ` Pavel Machek
2007-01-29 22:42                             ` Alessandro Di Marco
2007-01-30  0:03                               ` Pavel Machek
2007-01-30  9:42                               ` Vojtech Pavlik
2007-01-30 12:33                                 ` Alessandro Di Marco
2007-01-30 13:09                                   ` Vojtech Pavlik
2007-01-30 15:22                                     ` Alessandro Di Marco
2009-01-27  0:52                                 ` [PATCH] input: Activity counters Alessandro Di Marco
2009-01-27  0:54                                 ` Alessandro Di Marco [this message]
2009-01-27  0:54                                 ` Alessandro Di Marco
2009-01-27  0:54                                 ` Alessandro Di Marco
2007-01-29  8:24                       ` [ANNOUNCE] System Inactivity Monitor v1.0 Stefan Seyfried
2007-01-24 18:08               ` Alessandro Di Marco
2007-01-23 19:01           ` Mattia Dongili
2007-01-23 19:02             ` Pavel Machek
2007-01-23 20:07               ` Mattia Dongili
2007-01-23 19:34           ` Scott Preece
2007-01-24  2:02             ` Alessandro Di Marco
2007-01-24 14:01             ` Pavel Machek
2007-01-19 21:18 ` Bill Davidsen
2007-01-20 15:37   ` Alessandro Di Marco

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87ocxtzgxl.fsf@gmx.it \
    --to=dmr@gmx.it \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pavel@ucw.cz \
    --cc=vojtech@suse.cz \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox