Re: Using keyboard's _multimedia_ keys ?

Pavel Machek (pavel@bug.ucw.cz)
Tue, 20 Jul 1999 00:50:02 +0200


Hi!

> I'd just bought a new keyboard with some (14) extra _multimedia_ keys
> (like Vol+,Vol- etc.), and i was unable to find a proper solution.
>
> I would like to bind such key to a program executed, but running with user
> ID which typed that key.

That's impossible unless your computer has small camera just above the
keyboard to see just who typed it. Anyway, here's one of my old
patches.

Hi!

I've created simple way for keyboard to talk to userland. I used to
use kerneld, but there is no such thing any more. It allows for
example pasting from keyboard (really usefull - mouse is usefull for
text selection, but why touch it for pasting of text?), I also use it
for mouse emulation on keyboard.

It also includes my previous blank-from-keyboard patch, and it does
mirror cleaning up of peaces it touches.

Pavel
PS: relative to 2.1.90.

--- /dev/null Sun Jun 2 15:30:20 1996
+++ linux/drivers/char/keylink.c Sun Mar 22 16:26:16 1998
@@ -0,0 +1,128 @@
+/*
+ * Keylink - for making keyboard combinations call userland
+ *
+ * Copyright 1998 Pavel Machek <pavel@ucw.cz>, distribute
+ * under terms of GPL
+ *
+ * Loosely based on msbusmouse.c.
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+
+static struct wait_queue *wait;
+static int active = 0;
+
+#define RING 0x100
+static char ringbuf[ RING ];
+static int head = 0, tail = 0;
+
+#define INC( a ) a = (a+1) & (RING-1)
+
+void keylink_pushkey( char c )
+{
+ INC( head );
+ if (head == tail)
+ printk( "keylink: ring buffer overrun, keys lost\n" );
+ ringbuf[ head ] = c;
+ wake_up_interruptible(&wait);
+}
+
+static int release_keylink(struct inode * inode, struct file * file)
+{
+ if (--active)
+ return 0;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int open_keylink(struct inode * inode, struct file * file)
+{
+ if (active++)
+ return 0;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static ssize_t write_keylink(struct file * file,
+ const char * buffer, size_t count, loff_t *ppos)
+{
+ return -EINVAL;
+}
+
+static ssize_t read_keylink(struct file * file,
+ char * buffer, size_t count, loff_t *ppos)
+{
+ int i;
+
+ if (head==tail)
+ interruptible_sleep_on( &wait );
+ for (i = 0; i < count; i++) {
+ if (head==tail)
+ break;
+ INC( tail );
+ put_user(ringbuf[tail], buffer + i);
+ }
+ return i;
+}
+
+static unsigned int keylink_poll(struct file *file, poll_table * local_wait)
+{
+ poll_wait(file, &wait, local_wait);
+ return 0;
+}
+
+struct file_operations keylink_fops = {
+ NULL, /* keylink_seek */
+ read_keylink,
+ write_keylink,
+ NULL, /* keylink_readdir */
+ keylink_poll, /* keylink_poll */
+ NULL, /* keylink_ioctl */
+ NULL, /* keylink_mmap */
+ open_keylink,
+ release_keylink,
+ NULL,
+};
+
+static struct miscdevice misc_keylink = {
+ KEYLINK_MINOR, "keylink", &keylink_fops
+};
+
+__initfunc(int keylink_init(void))
+{
+ head = tail = 0;
+ wait = NULL;
+
+
+ printk(KERN_INFO "Keylink installed.\n");
+ misc_register(&misc_keylink);
+ return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+ return keylink_init();
+}
+
+void cleanup_module(void)
+{
+ misc_deregister(&misc_keylink);
+}
+#endif
+
--- clean/drivers/char/Config.in Wed Mar 18 12:56:09 1998
+++ linux/drivers/char/Config.in Sun Mar 22 19:18:46 1998
@@ -7,6 +7,7 @@
bool 'Virtual terminal' CONFIG_VT
if [ "$CONFIG_VT" = "y" ]; then
bool 'Support for console on virtual terminal' CONFIG_VT_CONSOLE
+ bool 'Keylink support' CONFIG_KEYLINK
fi
tristate 'Standard/generic (dumb) serial support' CONFIG_SERIAL
if [ "$CONFIG_SERIAL" = "y" ]; then
diff -u clean/drivers/char/Makefile linux/drivers/char/Makefile
--- clean/drivers/char/Makefile Wed Jan 14 21:45:43 1998
+++ linux/drivers/char/Makefile Sun Mar 22 19:20:49 1998
@@ -39,6 +39,10 @@

ifndef CONFIG_SUN_KEYBOARD
ifdef CONFIG_VT
+ifdef CONFIG_KEYLINK
+L_OBJS += keylink.o
+M = y
+endif
L_OBJS += keyboard.o
endif
ifneq ($(ARCH),m68k)
diff -u clean/drivers/char/keyboard.c linux/drivers/char/keyboard.c
--- clean/drivers/char/keyboard.c Thu Feb 12 12:32:35 1998
+++ linux/drivers/char/keyboard.c Sun Mar 22 21:24:31 1998
@@ -19,6 +19,8 @@
* parts by Geert Uytterhoeven, May 1997
*
* 27-05-97: Added support for the Magic SysRq Key (Martin Mares)
+ *
+ * 29-07-97: Added blank console key and possiblity to call userland (Pavel Machek)
*/

#include <linux/config.h>
@@ -104,31 +107,33 @@

static k_handfn
do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
- do_meta, do_ascii, do_lock, do_lowercase, do_slock, do_ignore;
+ do_meta, do_ascii, do_lock, do_lowercase, do_slock, do_keylink, do_ignore;

static k_hand key_handler[16] = {
do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
do_meta, do_ascii, do_lock, do_lowercase, do_slock,
- do_ignore, do_ignore, do_ignore
+ do_keylink, do_ignore, do_ignore
};

/* Key types processed even in raw modes */

-#define TYPES_ALLOWED_IN_RAW_MODE ((1 << KT_SPEC) | (1 << KT_SHIFT))
+#define TYPES_ALLOWED_IN_RAW_MODE ((1 << KT_SPEC) | (1 << KT_SHIFT) | (1 << 13))

typedef void (*void_fnp)(void);
typedef void (void_fn)(void);

static void_fn do_null, enter, show_ptregs, send_intr, lastcons, caps_toggle,
num, hold, scroll_forw, scroll_back, boot_it, caps_on, compose,
- SAK, decr_console, incr_console, spawn_console, bare_num;
+ SAK, decr_console, incr_console, spawn_console, bare_num,
+ blank_now;

static void_fnp spec_fn_table[] = {
do_null, enter, show_ptregs, show_mem,
show_state, send_intr, lastcons, caps_toggle,
num, hold, scroll_forw, scroll_back,
boot_it, caps_on, compose, SAK,
- decr_console, incr_console, spawn_console, bare_num
+ decr_console, incr_console, spawn_console, bare_num,
+ blank_now
};

#define SPECIALS_ALLOWED_IN_RAW_MODE (1 << KVAL(K_SAK))
@@ -138,7 +143,7 @@
255, SIZE(func_table) - 1, SIZE(spec_fn_table) - 1, NR_PAD - 1,
NR_DEAD - 1, 255, 3, NR_SHIFT - 1,
255, NR_ASCII - 1, NR_LOCK - 1, 255,
- NR_LOCK - 1
+ NR_LOCK - 1, 255
};

const int NR_TYPES = SIZE(max_vals);
@@ -195,8 +200,11 @@
char up_flag; /* 0 or 0200 */
char raw_mode;

- do_poke_blanked_console = 1;
- mark_bh(CONSOLE_BH);
+ if (scancode < 0x80)
+ {
+ do_poke_blanked_console = 1;
+ mark_bh(CONSOLE_BH);
+ }
add_keyboard_randomness(scancode);

tty = ttytab[fg_console];
@@ -406,6 +414,11 @@
if (!rep)
chg_vc_kbd_led(kbd,VC_NUMLOCK);
}
+static void blank_now(void)
+{
+ do_poke_blanked_console = 0;
+ do_blank_screen(0);
+}

static void lastcons(void)
{
@@ -490,6 +503,14 @@
#endif
}

+static void do_keylink(unsigned char value, char up_flag)
+{
+#ifdef CONFIG_KEYLINK
+ if (!up_flag)
+ keylink_pushkey( value );
+#endif
+}
+
static void do_ignore(unsigned char value, char up_flag)
{
}
@@ -511,6 +532,10 @@
spec_fn_table[value]();
}

+/*
+ * You do not want to clear this procedure and reuse its place...
+ * this is where normal characters reside
+ */
static void do_lowercase(unsigned char value, char up_flag)
{
printk(KERN_ERR "keyboard.c: do_lowercase was called - impossible\n");
diff -u clean/drivers/char/mem.c linux/drivers/char/mem.c
--- clean/drivers/char/mem.c Sun Mar 8 12:49:55 1998
+++ linux/drivers/char/mem.c Sun Mar 22 21:18:02 1998
@@ -32,9 +32,6 @@
#ifdef CONFIG_ISDN
int isdn_init(void);
#endif
-#ifdef CONFIG_PCWATCHDOG
-int pcwatchdog_init(void);
-#endif
#ifdef CONFIG_VIDEO_DEV
extern int videodev_init(void);
#endif
@@ -532,7 +529,8 @@
defined (CONFIG_AMIGAMOUSE) || defined (CONFIG_ATARIMOUSE) || \
defined (CONFIG_PCWATCHDOG) || \
defined (CONFIG_APM) || defined (CONFIG_RTC) || \
- defined (CONFIG_SUN_MOUSE) || defined (CONFIG_NVRAM)
+ defined (CONFIG_SUN_MOUSE) || defined (CONFIG_NVRAM) || \
+ defined (CONFIG_KEYLINK)
misc_init();
#endif
#ifdef CONFIG_SOUND
diff -u clean/drivers/char/misc.c linux/drivers/char/misc.c
--- clean/drivers/char/misc.c Wed Mar 18 12:56:09 1998
+++ linux/drivers/char/misc.c Sun Mar 22 21:16:05 1998
@@ -81,9 +85,7 @@
extern int nvram_init(void);
extern int radio_init(void);
extern void hfmodem_init(void);
-#ifdef CONFIG_PC110_PAD
extern int pc110pad_init(void);
-#endif

#ifdef CONFIG_PROC_FS
static int misc_read_proc(char *buf, char **start, off_t offset,
@@ -269,6 +271,9 @@
#endif
#ifdef CONFIG_HFMODEM
hfmodem_init();
+#endif
+#ifdef CONFIG_KEYLINK
+ keylink_init();
#endif
#endif /* !MODULE */
if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) {
--- clean/include/linux/miscdevice.h Wed Jan 14 21:46:06 1998
+++ linux/include/linux/miscdevice.h Sun Mar 22 00:05:47 1998
@@ -9,6 +9,7 @@
#define ATARIMOUSE_MINOR 5
#define SUN_MOUSE_MINOR 6
#define PC110PAD_MINOR 9
+#define KEYLINK_MINOR 127
#define WATCHDOG_MINOR 130 /* Watchdog timer */
#define TEMP_MINOR 131 /* Temperature Sensor */
#define RTC_MINOR 135
--- kbd-0.93/src/ksyms.c.ofic Fri Apr 4 22:14:02 1997
+++ kbd-0.93/src/ksyms.c Sun Mar 22 13:44:49 1998
@@ -714,7 +714,7 @@
};

static char *spec_syms[] = {
- "VoidSymbol",
+ "VoidSymbol", /* 0 */
"Return",
"Show_Registers",
"Show_Memory",
@@ -724,7 +724,7 @@
"Caps_Lock",
"Num_Lock",
"Scroll_Lock",
- "Scroll_Forward",
+ "Scroll_Forward", /* 10 */
"Scroll_Backward",
"Boot",
"Caps_On",
@@ -733,7 +733,17 @@
"Decr_Console",
"Incr_Console",
"Spawn_Console",
- "Bare_Num_Lock"
+ "Bare_Num_Lock",
+ "Blank_Now", /* 20 */
+ "__Spec_21",
+ "__Spec_22",
+ "__Spec_23",
+ "__Spec_24",
+ "__Spec_25",
+ "__Spec_26",
+ "__Spec_27",
+ "__Spec_28",
+ "__Spec_29"
};

static char *pad_syms[] = {
@@ -1033,6 +1043,265 @@
"SCtrlR"
};

+static char *keylink_syms[] = {
+ "Keylink_Paste", /* 0 */
+ "Keylink_LeftDown",
+ "Keylink_Down",
+ "Keylink_RightDown",
+ "Keylink_Left",
+ "Keylink_5", /* 5 */
+ "Keylink_Right",
+ "Keylink_LeftUp",
+ "Keylink_Up",
+ "Keylink_RightUp",
+ "Keylink_Fast_LeftDown",/* 10 */
+ "Keylink_Fast_Down",
+ "Keylink_Fast_RightDown",
+ "Keylink_Fast_Left",
+ "Keylink_14",
+ "Keylink_Fast_Right", /* 15 */
+ "Keylink_Fast_LeftUp",
+ "Keylink_Fast_Up",
+ "Keylink_Fast_RightUp",
+ "Keylink_19",
+ "Keylink_Click_1", /* 20 */
+ "Keylink_Click_2",
+ "Keylink_Click_3",
+ "Keylink_Toggle_1",
+ "Keylink_Toggle_2",
+ "Keylink_Toggle_3",
+ "Keylink_DoubleClick_1",
+ "Keylink_DoubleClick_2",
+ "Keylink_DoubleClick_3",
+ "Keylink_29",
+ "Keylink_30",
+ "Keylink_31",
+ "Keylink_32",
+ "Keylink_33",
+ "Keylink_34",
+ "Keylink_35",
+ "Keylink_36",
+ "Keylink_37",
+ "Keylink_38",
+ "Keylink_39",
+ "Keylink_40",
+ "Keylink_41",
+ "Keylink_42",
+ "Keylink_43",
+ "Keylink_44",
+ "Keylink_45",
+ "Keylink_46",
+ "Keylink_47",
+ "Keylink_48",
+ "Keylink_49",
+ "Keylink_50",
+ "Keylink_51",
+ "Keylink_52",
+ "Keylink_53",
+ "Keylink_54",
+ "Keylink_55",
+ "Keylink_56",
+ "Keylink_57",
+ "Keylink_58",
+ "Keylink_59",
+ "Keylink_60",
+ "Keylink_61",
+ "Keylink_62",
+ "Keylink_63",
+ "Keylink_64",
+ "Keylink_65",
+ "Keylink_66",
+ "Keylink_67",
+ "Keylink_68",
+ "Keylink_69",
+ "Keylink_70",
+ "Keylink_71",
+ "Keylink_72",
+ "Keylink_73",
+ "Keylink_74",
+ "Keylink_75",
+ "Keylink_76",
+ "Keylink_77",
+ "Keylink_78",
+ "Keylink_79",
+ "Keylink_80",
+ "Keylink_81",
+ "Keylink_82",
+ "Keylink_83",
+ "Keylink_84",
+ "Keylink_85",
+ "Keylink_86",
+ "Keylink_87",
+ "Keylink_88",
+ "Keylink_89",
+ "Keylink_90",
+ "Keylink_91",
+ "Keylink_92",
+ "Keylink_93",
+ "Keylink_94",
+ "Keylink_95",
+ "Keylink_96",
+ "Keylink_97",
+ "Keylink_98",
+ "Keylink_99",
+ "Keylink_100",
+ "Keylink_101",
+ "Keylink_102",
+ "Keylink_103",
+ "Keylink_104",
+ "Keylink_105",
+ "Keylink_106",
+ "Keylink_107",
+ "Keylink_108",
+ "Keylink_109",
+ "Keylink_110",
+ "Keylink_111",
+ "Keylink_112",
+ "Keylink_113",
+ "Keylink_114",
+ "Keylink_115",
+ "Keylink_116",
+ "Keylink_117",
+ "Keylink_118",
+ "Keylink_119",
+ "Keylink_120",
+ "Keylink_121",
+ "Keylink_122",
+ "Keylink_123",
+ "Keylink_124",
+ "Keylink_125",
+ "Keylink_126",
+ "Keylink_127",
+ "Keylink_128",
+ "Keylink_129",
+ "Keylink_130",
+ "Keylink_131",
+ "Keylink_132",
+ "Keylink_133",
+ "Keylink_134",
+ "Keylink_135",
+ "Keylink_136",
+ "Keylink_137",
+ "Keylink_138",
+ "Keylink_139",
+ "Keylink_140",
+ "Keylink_141",
+ "Keylink_142",
+ "Keylink_143",
+ "Keylink_144",
+ "Keylink_145",
+ "Keylink_146",
+ "Keylink_147",
+ "Keylink_148",
+ "Keylink_149",
+ "Keylink_150",
+ "Keylink_151",
+ "Keylink_152",
+ "Keylink_153",
+ "Keylink_154",
+ "Keylink_155",
+ "Keylink_156",
+ "Keylink_157",
+ "Keylink_158",
+ "Keylink_159",
+ "Keylink_160",
+ "Keylink_161",
+ "Keylink_162",
+ "Keylink_163",
+ "Keylink_164",
+ "Keylink_165",
+ "Keylink_166",
+ "Keylink_167",
+ "Keylink_168",
+ "Keylink_169",
+ "Keylink_170",
+ "Keylink_171",
+ "Keylink_172",
+ "Keylink_173",
+ "Keylink_174",
+ "Keylink_175",
+ "Keylink_176",
+ "Keylink_177",
+ "Keylink_178",
+ "Keylink_179",
+ "Keylink_180",
+ "Keylink_181",
+ "Keylink_182",
+ "Keylink_183",
+ "Keylink_184",
+ "Keylink_185",
+ "Keylink_186",
+ "Keylink_187",
+ "Keylink_188",
+ "Keylink_189",
+ "Keylink_190",
+ "Keylink_191",
+ "Keylink_192",
+ "Keylink_193",
+ "Keylink_194",
+ "Keylink_195",
+ "Keylink_196",
+ "Keylink_197",
+ "Keylink_198",
+ "Keylink_199",
+ "Keylink_200",
+ "Keylink_201",
+ "Keylink_202",
+ "Keylink_203",
+ "Keylink_204",
+ "Keylink_205",
+ "Keylink_206",
+ "Keylink_207",
+ "Keylink_208",
+ "Keylink_209",
+ "Keylink_210",
+ "Keylink_211",
+ "Keylink_212",
+ "Keylink_213",
+ "Keylink_214",
+ "Keylink_215",
+ "Keylink_216",
+ "Keylink_217",
+ "Keylink_218",
+ "Keylink_219",
+ "Keylink_220",
+ "Keylink_221",
+ "Keylink_222",
+ "Keylink_223",
+ "Keylink_224",
+ "Keylink_225",
+ "Keylink_226",
+ "Keylink_227",
+ "Keylink_228",
+ "Keylink_229",
+ "Keylink_230",
+ "Keylink_231",
+ "Keylink_232",
+ "Keylink_233",
+ "Keylink_234",
+ "Keylink_235",
+ "Keylink_236",
+ "Keylink_237",
+ "Keylink_238",
+ "Keylink_239",
+ "Keylink_240",
+ "Keylink_241",
+ "Keylink_242",
+ "Keylink_243",
+ "Keylink_244",
+ "Keylink_245",
+ "Keylink_246",
+ "Keylink_247",
+ "Keylink_248",
+ "Keylink_249",
+ "Keylink_250",
+ "Keylink_251",
+ "Keylink_252",
+ "Keylink_253",
+ "Keylink_254",
+ "Keylink_255" };
+
+
#define E(x) { x, sizeof(x) / sizeof(char **) }

syms_entry syms[] = {
@@ -1048,7 +1317,8 @@
E(ascii_syms),
E(lock_syms),
{ 0, 0 },
- E(sticky_syms)
+ E(sticky_syms),
+ E(keylink_syms)
};

#undef E

And here's program to use that patch

#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>

void
die( char *s )
{
printf( s );
exit(1);
}

int b, x, y; /* Our emulated mouse */
int mousefd;

void
sendupdate( void )
{
char packet[5];
packet[0] = 0x87^b;
packet[1] = packet[3] = x;
packet[2] = packet[4] = y;
x = y = 0;
if (mousefd != -1) {
int res;
if ((res =write( mousefd, packet, 5 ))!=5) {
syslog( LOG_ERR, "Write returned %d (%m)\n", res );
}
}
}

void
arrow( int w, int n )
{
w--;
if ((w % 3) == 0) x-=n;
if ((w % 3) == 2) x+=n;
if ((w / 3) == 0) y-=n;
if ((w / 3) == 2) y+=n;
sendupdate();
}

void
button( int w, int n )
{
w = 4-w;
w = (1 << (w-1));
if (!n) { b ^= w; sendupdate(); }
if (n==2) { b |= w; sendupdate(); b &= ~w; sendupdate(); }
if (n) { b |= w; sendupdate(); b &= ~w; sendupdate(); }
}

int
main( int argc, char *argv )
{
int keylink = open( "/dev/keylink", O_RDONLY );
if (keylink==-1) die( "Could not open keylink: %m\n" );
if ((mousefd = open( "/dev/keymouse", O_RDWR | O_NONBLOCK ))==-1)
printf( "Warning: Could not open keymouse: %m\n" );

chdir( "/" );
if (fork())
return 0;
while(1) {
char c;
read( keylink, &c, 1 );
switch (c) {
case 0: { /* Paste */
int fd;
char func = 3;
fd=open("/dev/tty0",O_WRONLY);
if (fd<0) syslog( LOG_ERR, "keylinkd/paste: can't open /dev/tty0: %m.\n" );
if (ioctl(fd, TIOCLINUX, &func)<0)
syslog( LOG_ERR, "keylinkd/paste: can't ioctl: %m.\n" );
close(fd);
break;
}
case 1: case 2: case 3: case 4: case 6: case 7: case 8: case 9:
arrow( c, 1 );
break;
case 10: case 11: case 12: case 13: case 15: case 16: case 17: case 18:
arrow( c-9, 5 );
break;
case 20: case 21: case 22:
button( c-19, 1 );
break;
case 23: case 24: case 25:
button( c-22, 0 );
break;
case 26: case 27: case 28:
button( c-25, 2 );
break;
default:
syslog( LOG_ERR, "keylinkd: Unknown value from kernel: %d\n", (int) c );
}
}

}

-- 
I'm really pavel@ucw.cz. Look at http://195.113.31.123/~pavel.  Pavel
Hi! I'm a .signature virus! Copy me into your ~/.signature, please!

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/