/* * linux/drivers/char/keystrokes.c * * A port of the 2.4 keystroke counter patch to 2.6+module * * Originally written for the Linux 2.4 kernel by kevin@desertsol.com * http://www.desertsol.com/~kevin * * Port to 2.6+module done by Dan Foster and offered in the Gentoo Forums * http://forums.gentoo.org/ * * Some of the added code by Dan was cribbed from the 2.6 kernel's * linux/drivers/char/keyboard.c and also from linux/drivers/char/i8k.c * Good stuff -- thank you, dear Very Cool And Generous Linux Developers. * * Dan Foster takes the blame for any observed bugs. :) * */ /* * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * ALTERNATIVELY, this product may be distributed under the terms of * the GNU General Public License, in which case the provisions of the GPL are * required INSTEAD OF the above restrictions. (This clause is * necessary due to a potential bad interaction between the GPL and * the restrictions contained in a BSD-style copyright.) * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. */ /* * TODO: Add rollover code handling when keystrokes counter hits max. * * Not currently considered a big deal since we'd have to generate about * 18.4 million trillion (2^64) keystrokes before hitting this limit! */ /* --------------------- Headers -------------------- */ #include #include #include #include #include #include /* --------------------- Function prototypes -------------------- */ /* * Hey, this stuff is ESPECIALLY important when doing kernel code, * since this better helps us catch silly mistakes during compile time * instead of as a fatal kernel panic or something! */ static void ks_event(struct input_handle *, unsigned int, unsigned int, int); static struct input_handle *ks_connect(struct input_handler *, struct input_dev *, struct input_device_id *); static void ks_disconnect(struct input_handle *); /* --------------------- Variables and data structures -------------------- */ static struct input_handler ks_handler; static char kbd_name[] = "keystrokes"; #define KEYSTROKES_DRIVER_VERSION "1.0.1" #define KEYPRESS_REPEAT 2 #define KEYPRESS_DOWN 0 #define NULL_EVENT 0 /* * Some global variables to allow us to eliminate one type of key repeats */ unsigned int oldeventcode=0; int oldvalue=0; /* * We chose an unsigned 64-bit type so that we don't end up limiting * really, really power users that might hit the keyboard heavily and * use their super-ultra-stable Linux system for years on end. :-) * * Note: that's slightly tongue-in-cheek as 2 to the 64th power is * approximately, oh, 18.4 million trillion. It's more likely the * keyboard will break long before, and system retired, too. :-) */ static u_int64_t keystrokes=0; /* * We're only interested in dealing with devices that generates key events */ static struct input_device_id ks_keybd_devids[] = { { .flags = INPUT_DEVICE_ID_MATCH_EVBIT, .evbit = { BIT(EV_KEY) }, }, { }, /* Terminating entry */ }; MODULE_DEVICE_TABLE(input, ks_keybd_devids); /* * Names of functions the generic input handler code should call when * we're up at bat. */ static struct input_handler ks_handler = { .event = ks_event, .connect = ks_connect, .disconnect = ks_disconnect, .name = "keystrokes", .id_table = ks_keybd_devids, }; /* * For safety. We also don't run around with scissors, too. */ static DECLARE_MUTEX(keystrokes_mutex); static DECLARE_MUTEX(oldcodes_mutex); /* * Module declarations */ MODULE_AUTHOR("Kevin Geiss ; updated by Dan Foster"); MODULE_DESCRIPTION("Driver for counting keystrokes since power-on"); MODULE_LICENSE("GPL"); MODULE_VERSION(KEYSTROKES_DRIVER_VERSION); /* * Set debug to 0 unless overridden by loading as a module with debug=1 * * (Yeah, I know, we don't actually use it, but it's good as a skeleton * template for future use.) */ int debug=0; module_param(debug, bool, 0644); /* --------------------- Functions -------------------- */ /* * The top half handler. BE **VERY** QUICK AND BRIEF HERE! * * Note: we won't have a bottom half handler for this module. * (We just don't have anything we need to do in a BH.) */ static void ks_event(struct input_handle *handle, unsigned int event_type, unsigned int event_code, int value) { /* * If it looks strange... bail out pronto. Also, we only process * key events and none of the other event types. */ if ( (event_code > NR_KEYS) || (event_type != EV_KEY) ) return; /* * List all the conditions that we want to ignore! If no match * for any of them THEN bump up the keystrokes counter. * * Note: we ignore value=KEYPRESS_DOWN because keystrokes * generates *TWO* events per key press -- one is downwards, * one is the bounce back upwards. * * We use mutexes to avoid shredding stuff on a SMP system. * */ if ( ! ((value == KEYPRESS_REPEAT) || (event_code == NULL_EVENT) || (value == KEYPRESS_DOWN) || (event_code == oldeventcode)) ) { down(&keystrokes_mutex); keystrokes++; up(&keystrokes_mutex); } /* * Remember current values so we can avoid processing a key * repeat event the next time we come back here. */ down(&oldcodes_mutex); oldeventcode = event_code; oldvalue = value; up(&oldcodes_mutex); } /* * When a keyboard (or other input device) is found, the ks_connect * function is called. The function then looks at the device, and if it * likes it, it can open it and get events from it. In this (ks_connect) * function, we should decide which VT to bind that keyboard to initially. */ static struct input_handle *ks_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id) { struct input_handle *handle; int i; for (i = KEY_RESERVED; i < BTN_MISC; i++) if (test_bit(i, dev->keybit)) break; if ((i == BTN_MISC) && !test_bit(EV_SND, dev->evbit)) return NULL; if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL))) return NULL; memset(handle, 0, sizeof(struct input_handle)); handle->dev = dev; handle->handler = handler; handle->name = kbd_name; input_open_device(handle); return handle; } /* * Play nicely should we ever have a reason to disconnect ourself from * the input handler list. */ static void ks_disconnect(struct input_handle *handle) { input_close_device(handle); kfree(handle); } /* * Print the information for /proc/keystrokes. */ static int ks_proc_get_info(char *buffer, char **start, off_t fpos, int length) { int written=0; /* * l = long (32-bit) so ll = long long, aka 64-bit. * d = decimal (base 10), not octal, hex, binary, etc. */ written = sprintf( buffer, "%lld\n", keystrokes ); return (written<0) ? 0 : written; } /* * Return contents of /proc/keystrokes */ static ssize_t ks_proc_read(struct file *f, char __user *buffer, size_t len, loff_t *fpos) { int n; char info[20]; n = ks_proc_get_info(info, NULL, 0, 20); if (n <= 0) { return n; } if (*fpos >= n) { return 0; } if ((*fpos + len) >= n) { len = n - *fpos; } if (copy_to_user(buffer, info, len) != 0) { return -EFAULT; } *fpos += len; return len; } /* * Do one-time initialization stuff */ static int __init keystrokes_init(void) { /* Define stuff relating to the /proc file */ struct proc_dir_entry *proc_keystrokes=NULL; static struct file_operations keystrokes_fops = { .owner = THIS_MODULE, .read = ks_proc_read, }; /* Tell the kernel we'd like to be notified of input events */ input_register_handler(&ks_handler); /* Register the proc entry */ proc_keystrokes = create_proc_info_entry("keystrokes", 0, NULL, ks_proc_get_info); /* Couldn't create proc file? Return error */ if (!proc_keystrokes) { return -ENOENT; } /* Set up proc data structure to point to our functions */ proc_keystrokes->proc_fops = &keystrokes_fops; proc_keystrokes->owner = THIS_MODULE; /* Print something so we know something is happening! */ printk( KERN_INFO "Keypress counting driver v%s. Kevin Geiss ; updated by Dan Foster\n", KEYSTROKES_DRIVER_VERSION); return 0; } /* * Do clean-up stuff before exiting */ static void __exit keystrokes_exit(void) { /* Remove the proc entry */ remove_proc_entry("keystrokes", NULL); /* * Unregister the input handler so kernel won't try * to use our handler after we've exited. * * (BIG CRASH if we don't do this. Guaranteed.) * * Not much we can do about errors at this point. * Just pray we don't have problems. :) */ input_unregister_handler(&ks_handler); printk(KERN_INFO "keystrokes: module unloaded\n"); } /* * This is a 2.6 kernel thing; it will be ignored if not built as a * module. Else, if it is, it'll Do The Right Thing(tm). */ module_init(keystrokes_init); module_exit(keystrokes_exit);