diff -Nru /tmp/f1JdKl2Py5/hotkey-setup-0.1/debian/changelog /tmp/yMNtjIJqGa/hotkey-setup-0.1/debian/changelog --- /tmp/f1JdKl2Py5/hotkey-setup-0.1/debian/changelog 2006-01-06 18:31:53.000000000 +0000 +++ /tmp/yMNtjIJqGa/hotkey-setup-0.1/debian/changelog 2006-02-01 08:42:53.000000000 +0000 @@ -1,3 +1,10 @@ +hotkey-setup (0.1-13ubuntu1) unstable; urgency=low + + * On IBM's, load 'thinkpad-keys' /dev/input keyboard emulator + - AccessIBM/Thinkpad, Volume keys and Zoom (Fn+Space) from /dev/nvram + + -- Paul Sladen Wed, 01 Feb 2006 08:41:37 +0000 + hotkey-setup (0.1-12) unstable; urgency=low * Fix Dell hibernate and HP sound keys diff -Nru /tmp/f1JdKl2Py5/hotkey-setup-0.1/debian/control /tmp/yMNtjIJqGa/hotkey-setup-0.1/debian/control --- /tmp/f1JdKl2Py5/hotkey-setup-0.1/debian/control 2005-12-24 01:33:02.000000000 +0000 +++ /tmp/yMNtjIJqGa/hotkey-setup-0.1/debian/control 2006-02-01 08:47:31.000000000 +0000 @@ -2,7 +2,7 @@ Section: misc Priority: optional Maintainer: Matthew Garrett -Build-Depends: debhelper (>= 4.0.0) +Build-Depends: debhelper (>= 4.0.0), linux-kernel-headers (>= 2.6.11) Standards-Version: 3.6.1 Package: hotkey-setup diff -Nru /tmp/f1JdKl2Py5/hotkey-setup-0.1/debian/init.d /tmp/yMNtjIJqGa/hotkey-setup-0.1/debian/init.d --- /tmp/f1JdKl2Py5/hotkey-setup-0.1/debian/init.d 2005-12-24 01:12:56.000000000 +0000 +++ /tmp/yMNtjIJqGa/hotkey-setup-0.1/debian/init.d 2006-02-01 08:38:33.000000000 +0000 @@ -36,6 +36,8 @@ ;; IBM*) . /usr/share/hotkey-setup/ibm.hk + /usr/sbin/thinkpad-keys + touch /var/run/hotkey-setup.thinkpad-keys ;; *) . /usr/share/hotkey-setup/default.hk @@ -43,6 +45,9 @@ . /usr/share/hotkey-setup/generic.hk ;; stop) + if [ -f /var/run/hotkey-setup.thinkpad-keys ]; then + kill `pidof thinkpad-keys` + fi if [ -f /var/run/hotkey-setup ]; then cat /var/run/hotkey-setup | while read scancode keycode; do setkeycodes $scancode $keycode; diff -Nru /tmp/f1JdKl2Py5/hotkey-setup-0.1/debian/rules /tmp/yMNtjIJqGa/hotkey-setup-0.1/debian/rules --- /tmp/f1JdKl2Py5/hotkey-setup-0.1/debian/rules 2005-09-24 18:52:04.000000000 +0100 +++ /tmp/yMNtjIJqGa/hotkey-setup-0.1/debian/rules 2006-02-01 08:45:04.000000000 +0000 @@ -30,6 +30,7 @@ cp $(CURDIR)/*.hk $(CURDIR)/debian/hotkey-setup/usr/share/hotkey-setup mkdir $(CURDIR)/debian/hotkey-setup/usr/sbin cp $(CURDIR)/dumpkeycodes $(CURDIR)/debian/hotkey-setup/usr/sbin + cp $(CURDIR)/thinkpad-keys $(CURDIR)/debian/hotkey-setup/usr/sbin # Build architecture-independent files here. binary-indep: build install diff -Nru /tmp/f1JdKl2Py5/hotkey-setup-0.1/Makefile /tmp/yMNtjIJqGa/hotkey-setup-0.1/Makefile --- /tmp/f1JdKl2Py5/hotkey-setup-0.1/Makefile 2005-09-24 18:46:50.000000000 +0100 +++ /tmp/yMNtjIJqGa/hotkey-setup-0.1/Makefile 2006-02-01 08:46:07.000000000 +0000 @@ -1,7 +1,12 @@ +all: dumpkeycodes thinkpad-keys + dumpkeycodes: dumpkeycodes.c gcc -o dumpkeycodes dumpkeycodes.c +thinkpad-keys: thinkpad-keys.c + $(CC) -o $@ $< + clean: - - rm dumpkeycodes + - rm dumpkeycodes thinkpad-keys + -all: dumpkeycodes diff -Nru /tmp/f1JdKl2Py5/hotkey-setup-0.1/thinkpad-keys.c /tmp/yMNtjIJqGa/hotkey-setup-0.1/thinkpad-keys.c --- /tmp/f1JdKl2Py5/hotkey-setup-0.1/thinkpad-keys.c 1970-01-01 01:00:00.000000000 +0100 +++ /tmp/yMNtjIJqGa/hotkey-setup-0.1/thinkpad-keys.c 2006-02-01 08:19:21.000000000 +0000 @@ -0,0 +1,259 @@ +/* + * Program to generate faked keyboard input using udev / uinput + * Copyright (c) 2006 Paul Sladen + * + * Ideas from 'kbdd' and 'tpb'. + * Copyright (C) 2004,2005 Nils Faerber + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define _GNU_SOURCE +#include + +#define UINPUT_DEVICE "/dev/input/uinput" +#define NVRAM_DEVICE "/dev/nvram" +#define POLL_DELAY 50 /* milliseconds */ + +static int init_nvram(void) +{ + int fd; + + if((fd = open(NVRAM_DEVICE, O_RDONLY|O_NONBLOCK)) < 0) + fd = 0; + + return fd; +} + +struct record { + unsigned char thinkpad, zoom, volume_moved, muted, volume; +}; + +enum { + THINKPAD = 1, + ZOOM = 2, + VOLUME_UP = 4, + VOLUME_DOWN = 8, + VOLUME_MUTE = 16, +}; + +struct keys { + unsigned char mask; + unsigned short keycode; +} codes[] = { + { THINKPAD, KEY_PROG1 }, + { ZOOM, KEY_PROG2 }, + { VOLUME_DOWN, KEY_VOLUMEDOWN }, + { VOLUME_UP, KEY_VOLUMEUP }, + { VOLUME_MUTE, KEY_MUTE }, + { 0, 0 } +}; + +/* Retrieve the interesting parts of the nvram contents */ +static void grok_nvram(int fd, struct record *where) +{ + unsigned char c; + int thinkpad, zoom, down, up, mute, volume; + lseek(fd, 0x57, SEEK_SET); + read(fd, &c, 1); + + where->thinkpad = c & 0x08; + where->zoom = c & 0x20; + + lseek(fd, 0x60, SEEK_SET); + read(fd, &c, 1); + + where->volume_moved = c & 0xc0; + where->muted = c & 0x40; + where->volume = c & 0x0f; +} + +static void compare_nvram(int *result, struct record *a, struct record *b) +{ + int r = 0; + if(a->thinkpad != b->thinkpad) + r |= THINKPAD; + if(a->zoom != b->zoom) + r |= ZOOM; + if(a->volume_moved != b->volume_moved) + if(b->muted) + { + r |= VOLUME_MUTE; + /* This HACK means that pressing MUTE, when already MUTED, stays MUTED */ + if(a->muted) + r |= VOLUME_UP; + } + else if(a->volume > b->volume || !b->volume) + r |= VOLUME_DOWN; + else + r |= VOLUME_UP; + *result = r; +} + +static int init_uinput(const char *name, struct keys *table) +{ + int fd, i; + struct uinput_user_dev dev = { + .id = { + .bustype = BUS_HOST, + .vendor = 0x1014, /* IBM */ + .product = 0x5450, /* "TP" */ + .version = 0x0001, + } + }; + + strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE); + + if((fd = open(UINPUT_DEVICE, O_WRONLY)) < 0) + { + perror("Cannot open " UINPUT_DEVICE); + goto shutdown; + } + + if(write(fd, &dev, sizeof(dev)) < sizeof(dev)) + { + perror("Cannot create a uinput device"); + goto release; + } + + if(ioctl(fd, UI_SET_EVBIT, EV_KEY)) + goto release; + +#if 1 + /* Only setup the actual keys we're going to use */ + for(i = 0; table[i].mask; i++) + if(ioctl(fd, UI_SET_KEYBIT, table[i].keycode)) + goto release; +#else + /* Setup all possible key codes */ + for(i = 0; i <= KEY_MAX; i++) + if(ioctl(fd, UI_SET_KEYBIT, i)) + goto release; +#endif + + if(ioctl(fd, UI_DEV_CREATE)) + goto release; + + return fd; + + release: + ioctl(fd, UI_DEV_DESTROY); + shutdown: + close(fd); + return 0; +} + +static int cleanup_uinput_fd = 0; +static __sighandler_t cleanup_uinput_previous = NULL; + +void cleanup_uinput(int signal) +{ + if(cleanup_uinput_fd) + ioctl(cleanup_uinput_fd, UI_DEV_DESTROY); + if(cleanup_uinput_previous) + (*cleanup_uinput_previous)(signal); + else + exit(EXIT_SUCCESS); +} + +static void send_key(int fd, unsigned short code) +{ + struct input_event ev = { + .type = EV_KEY, + .code = code, + .time = {0, } + }; + + if(code > KEY_MAX) + return; + + ev.value = 1; // press... + write(fd, &ev, sizeof(ev)); + + ev.value = 0; // then release + write(fd, &ev, sizeof(ev)); +} + +static void punt_keycodes(int fd, int buttons, struct keys *table) +{ + int i; + for(i = 0; table[i].mask; i++) + if(buttons & table[i].mask) + send_key(fd, table[i].keycode); +} + +int main(int argc, char **argv) +{ + int nvram, uinput; + struct record state[2]; + int buttons, counter = 1; + +#if 1 + /* Software volume control */ + int mask = THINKPAD|ZOOM|VOLUME_UP|VOLUME_DOWN|VOLUME_MUTE; +#else + /* Hardware mixer present */ + int mask = THINKPAD|ZOOM; +#endif + + if(!(nvram = init_nvram())) + { + perror("Could not open nvram device"); + return 1; + } + if(!(uinput = init_uinput(*argv, codes))) + { + perror("Could not open uinput device"); + close(nvram); + return 2; + } + + /* Daemonise into the background */ + if(fork()) + exit(EXIT_SUCCESS); + + /* Clean up nicely */ + cleanup_uinput_previous = signal(SIGTERM, cleanup_uinput); + + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + + grok_nvram(nvram, state); + + for(;; counter ^= 1) + { + grok_nvram(nvram, state + counter); + compare_nvram(&buttons, state + (counter^1), state + counter); + if(buttons &= mask) + punt_keycodes(uinput, buttons, codes); + usleep(POLL_DELAY*1000); + } + + cleanup_uinput(SIGTERM); +} +