[PATCH 1/3] usbhid: Driver for microsoft natural ergonomic keyboard 4000

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



    This driver use "HID device simple driver interface", you must
install that patch first. This new version get some improvements.

    The patch is core of driver.

    I am sorry for sendding patches in the attachment, beacause of my mail client always break TAB into some spaces.

    Good luck

-Liyu

    First, this driver use "HID device simple driver interface", you must 
apply that patch before use me.

    As Greg KH words, I split this driver into some more littler patches, and
break some long line in source.

    This new version get some improvements:
   
    1. These five "My Favorites" keys map FN_F{1,2,3,4,5}_KEY directly,
instead of map them to KEY_VENDOR hacking means.

    2. Support left paren key "(", right paren key ")", equal key "=" on
right-top keypad. In fact, this keyboard generate KEYPAD_XXX usage code
for them, but I find many applications can not handle them on default
configuration, especially X.org. To get the most best usability, I use a
bit magic here: map them to "Shift+9" and "Shift+0".

    This patch is main part of driver.

    First, this driver use "HID device simple driver interface", you must apply that patch before use me.

    Signed-off-by:  Yu Li <[email protected]>

diff -Naurp linux-2.6.17.6/drivers/usb/input.orig/usbnek4k.c linux-2.6.17.6/drivers/usb/input/usbnek4k.c
--- linux-2.6.17.6/drivers/usb/input.orig/usbnek4k.c	1970-01-01 08:00:00.000000000 +0800
+++ linux-2.6.17.6/drivers/usb/input/usbnek4k.c	2006-07-26 09:42:01.000000000 +0800
@@ -0,0 +1,420 @@
+/*
+ *  Microsoft Natural Ergonomic Keyboard 4000 Driver
+ *
+ *  Version:	0.2.0
+ *
+ *  TODO:	
+ *	Refactoring to escape from switch {case ...} hell.
+ *
+ *  Copyright (c) 2006 Liyu <[email protected]> 	
+ */
+
+/*
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include "hid.h"
+#include "hid-simple.h"
+
+#define map_key(c) \
+	do { \
+		usage->code = c; \
+		usage->type = EV_KEY; \
+		set_bit(c,input->keybit); \
+	} while (0)
+
+#define clear_key(c) \
+	do { \
+		usage->code = 0; \
+		usage->type = 0; \
+		clear_bit(c,input->keybit); \
+	} while (0)
+
+#define clear_usage(c)	\
+	do { \
+		usage->code = 0; \
+		usage->type = 0; \
+	} while (0)
+
+#define LEFTP_PRESSED	0x1
+#define RIGHTP_PRESSED	0x2
+#define KPP_PRESSED_MASK	(LEFTP_PRESSED|RIGHTP_PRESSED)
+
+#define set_leftp_pressed(hiddev) \
+	set_bit(LEFTP_PRESSED, (unsigned long*)(&hiddev->simple->private))
+#define set_rightp_pressed(hiddev) \
+	set_bit(RIGHTP_PRESSED, (unsigned long*)(&hiddev->simple->private))
+#define clear_leftp_pressed(hiddev) \
+	clear_bit(LEFTP_PRESSED, (unsigned long*)(&hiddev->simple->private))
+#define clear_rightp_pressed(hiddev) \
+	clear_bit(RIGHTP_PRESSED, (unsigned long*)(&hiddev->simple->private))
+
+#define is_kpp_pressed(hiddev) \
+	((unsigned long)(hiddev->simple->private) & KPP_PRESSED_MASK)
+#define is_leftp_pressed(hiddev) \
+	((unsigned long)(hiddev->simple->private) & LEFTP_PRESSED)
+#define is_rightp_pressed(hiddev) \
+	((unsigned long)(hiddev->simple->private) & RIGHTP_PRESSED)
+
+
+
+#define USAGE_ZOOM_IN 0x22d
+#define USAGE_ZOOM_OUT 0x22e
+#define USAGE_HOME	0x223
+#define USAGE_SEARCH	0x221
+#define USAGE_EMAIL	0x18a
+#define USAGE_FAVORITES	0x182
+#define USAGE_MUTE	0xe2
+#define USAGE_VOLUME_DOWN	0xea
+#define USAGE_VOLUME_UP	0xe9
+#define USAGE_PLAY_PAUSE	0xcd
+#define USAGE_CALCULATOR	0x192
+#define USAGE_BACK	0x224
+#define USAGE_FORWARD	0x225
+#define USAGE_CUSTOM	0xff05
+
+#define USAGE_CUSTEM_1	0x1
+#define USAGE_CUSTEM_2	0x2
+#define USAGE_CUSTEM_3	0x4
+#define USAGE_CUSTEM_4	0x8
+#define USAGE_CUSTEM_5	0x10
+
+#define USAGE_HELP	0x95
+#define USAGE_UNDO	0x21a
+#define USAGE_REDO	0x279
+#define USAGE_NEW	0x201
+#define USAGE_OPEN	0x202
+#define USAGE_CLOSE	0x203
+
+#define USAGE_REPLY	0x289
+#define USAGE_FWD	0x28b
+#define USAGE_SEND	0x28c
+#define USAGE_SPELL	0x1ab
+#define USAGE_SAVE	0x207
+#define USAGE_PRINT	0x208
+
+#define USAGE_KEYPAD_EQUAL 0x67
+#define USAGE_KEYPAD_LEFT_PARENTHESES 0xb6
+#define USAGE_KEYPAD_RIGHT_PARENTHESES 0xb7
+
+
+#define MSNEK4K_ID_VENDOR	0x045e
+#define MSNEK4K_ID_PRODUCT	0x00db
+
+
+static struct usb_device_id __id[] = {
+	{
+		USB_DEVICE(MSNEK4K_ID_VENDOR, MSNEK4K_ID_PRODUCT)
+	},
+	{0, }
+};
+
+MODULE_DEVICE_TABLE(usb, __id);
+
+static char driver_name[] = "Microsoft Natural Ergonomic Keyboard 4000";
+
+static void nek4k_setup_usage(struct hid_field *field,	struct hid_usage *usage)
+{	
+	struct hid_input *hidinput = field->hidinput;
+	struct input_dev *input = hidinput->input;
+
+	if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) {
+		switch (usage->hid & HID_USAGE) {
+			case USAGE_ZOOM_IN:
+				map_key(KEY_F13);break;
+			case USAGE_ZOOM_OUT:
+				map_key(KEY_F14);break;
+			case USAGE_HOME:
+				map_key(KEY_HOMEPAGE);break;
+			case USAGE_SEARCH:
+				map_key(KEY_SEARCH);break;
+			case USAGE_EMAIL:
+				map_key(KEY_EMAIL);break;
+			case USAGE_FAVORITES:
+				map_key(KEY_FAVORITES);break;
+			case USAGE_MUTE:
+				map_key(KEY_MUTE);break;
+			case USAGE_VOLUME_DOWN:
+				map_key(KEY_VOLUMEDOWN);break;
+			case USAGE_VOLUME_UP:
+				map_key(KEY_VOLUMEUP);	break;
+			case USAGE_PLAY_PAUSE:
+				map_key(KEY_PLAYPAUSE);break;
+			case USAGE_CALCULATOR:
+				map_key(KEY_CALC);break;
+			case USAGE_BACK:
+				map_key(KEY_BACK);break;
+			case USAGE_FORWARD:
+				map_key(KEY_FORWARD);break;
+			case USAGE_HELP:
+				map_key(KEY_HELP);break;
+			case USAGE_UNDO:
+				map_key(KEY_UNDO);break;
+			case USAGE_REDO:
+				map_key(KEY_REDO);break;
+			case USAGE_NEW:
+				map_key(KEY_NEW);break;
+			case USAGE_OPEN:
+				map_key(KEY_OPEN);break;
+			case USAGE_CLOSE:
+				map_key(KEY_CLOSE);break;
+			case USAGE_REPLY:
+				map_key(KEY_REPLY);break;
+			case USAGE_FWD:
+				map_key(KEY_FORWARDMAIL);break;
+			case USAGE_SEND:
+				map_key(KEY_SEND);break;
+			case USAGE_SPELL:
+				map_key(KEY_F13);break;
+			case USAGE_SAVE:
+				map_key(KEY_SAVE);break;
+			case USAGE_PRINT:
+				map_key(KEY_PRINT);break;
+			default:	
+				return;
+		}
+	} else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR) {
+		if ((usage->hid & HID_USAGE) == USAGE_CUSTOM) {
+			map_key(KEY_FN_F1);
+			map_key(KEY_FN_F2);
+			map_key(KEY_FN_F3);
+			map_key(KEY_FN_F4);
+			map_key(KEY_FN_F5);
+		}
+	} else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_KEYBOARD) {
+		switch (usage->hid & HID_USAGE) {
+			case USAGE_KEYPAD_EQUAL:
+				map_key(KEY_EQUAL);
+				break;
+			case USAGE_KEYPAD_LEFT_PARENTHESES:
+				map_key(KEY_9);
+				break;
+			case USAGE_KEYPAD_RIGHT_PARENTHESES:
+				map_key(KEY_0);
+				break;
+			default:
+				return;
+		}
+	}
+	else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_LED)
+		return;
+	else
+		printk(KERN_WARNING"Unknown usage page 0x%x in %s:%d\n",
+			usage->hid & HID_USAGE_PAGE, __FUNCTION__, __LINE__);
+	return;
+}
+
+static void nek4k_clear_usage(struct hid_field *field,	struct hid_usage *usage)
+{
+	struct hid_input *hidinput = field->hidinput;
+	struct input_dev *input = hidinput->input;
+
+	if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER) {
+		switch (usage->hid & HID_USAGE) {
+			case USAGE_ZOOM_IN:
+				clear_key(KEY_F13);break;
+			case USAGE_ZOOM_OUT:
+				clear_key(KEY_F14);break;
+			case USAGE_HOME:
+				clear_key(KEY_HOMEPAGE);break;
+			case USAGE_SEARCH:
+				clear_key(KEY_SEARCH);break;
+			case USAGE_EMAIL:
+				clear_key(KEY_EMAIL);break;
+			case USAGE_FAVORITES:
+				clear_key(KEY_FAVORITES);break;
+			case USAGE_MUTE:
+				clear_key(KEY_MUTE);break;
+			case USAGE_VOLUME_DOWN:
+				clear_key(KEY_VOLUMEDOWN);break;
+			case USAGE_VOLUME_UP:
+				clear_key(KEY_VOLUMEUP);	break;
+			case USAGE_PLAY_PAUSE:
+				clear_key(KEY_PLAYPAUSE);break;
+			case USAGE_CALCULATOR:
+				clear_key(KEY_CALC);break;
+			case USAGE_BACK:
+				clear_key(KEY_BACK);break;
+			case USAGE_FORWARD:
+				clear_key(KEY_FORWARD);break;
+			case USAGE_HELP:
+				clear_key(KEY_HELP);break;
+			case USAGE_UNDO:
+				clear_key(KEY_UNDO);break;
+			case USAGE_REDO:
+				clear_key(KEY_REDO);break;
+			case USAGE_NEW:
+				clear_key(KEY_NEW);break;
+			case USAGE_OPEN:
+				clear_key(KEY_OPEN);break;
+			case USAGE_CLOSE:
+				clear_key(KEY_CLOSE);break;
+			case USAGE_REPLY:
+				clear_key(KEY_REPLY);break;
+			case USAGE_FWD:
+				clear_key(KEY_FORWARDMAIL);break;
+			case USAGE_SEND:
+				clear_key(KEY_SEND);break;
+			case USAGE_SPELL:
+				clear_key(KEY_F13);break;
+			case USAGE_SAVE:
+				clear_key(KEY_SAVE);break;
+			case USAGE_PRINT:
+				clear_key(KEY_PRINT);break;
+			default:	
+				return;
+		}
+	} else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR) {
+		if ((usage->hid & HID_USAGE) == USAGE_CUSTOM) {
+			clear_bit(KEY_FN_F1,input->keybit);
+			clear_bit(KEY_FN_F2,input->keybit);
+			clear_bit(KEY_FN_F3,input->keybit);
+			clear_bit(KEY_FN_F4,input->keybit);
+			clear_bit(KEY_FN_F5,input->keybit);
+			return;
+		}
+	} else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_KEYBOARD) {
+		clear_usage(USAGE_EQUAL);
+		return;
+	} else 
+		printk(KERN_ERR"Unknown usage page 0x%x in %s:%d\n",
+			usage->hid & HID_USAGE_PAGE, __FUNCTION__, __LINE__);
+
+}
+
+static int
+nek4k_hid_event(const struct hid_device *hid, const struct hid_field *field,
+			const struct hid_usage * usage, const __s32 value,
+						const struct pt_regs *regs)
+{
+	struct hid_input *hidinput = field->hidinput;
+	struct input_dev *input = hidinput->input;
+	int code=0;
+
+	if ( (usage->hid&HID_USAGE_PAGE)==HID_UP_MSVENDOR &&
+	     (usage->hid&HID_USAGE) == USAGE_CUSTOM ) {
+		switch (value) {
+			case 0x0:
+				input_event(input, EV_KEY, code, 0);
+				input_sync(input);
+				return 0;
+			case USAGE_CUSTEM_1:	
+				code = KEY_FN_F1;
+				break;
+			case USAGE_CUSTEM_2:
+				code = KEY_FN_F2;
+				break;
+			case USAGE_CUSTEM_3:
+				code = KEY_FN_F3;
+				break;
+			case USAGE_CUSTEM_4:
+				code = KEY_FN_F4;
+				break;
+			case USAGE_CUSTEM_5:
+				code = KEY_FN_F5;
+				break;
+			default:
+				return -EINVAL;
+		};
+		input_event(input, EV_KEY, code, 1);
+		input_sync(input);
+	}
+	else if ( (usage->hid&HID_USAGE_PAGE) == HID_UP_KEYBOARD ) {
+		if ((usage->hid&HID_USAGE) == USAGE_KEYPAD_LEFT_PARENTHESES)
+			code = KEY_9;
+		else if ((usage->hid&HID_USAGE) == USAGE_KEYPAD_RIGHT_PARENTHESES)
+			code = KEY_0;
+		else 
+			return -EINVAL;
+		if (1 == value) {
+			if (!is_kpp_pressed(hid)) {
+				input_event(input, EV_KEY, KEY_LEFTSHIFT, 1);
+				input_sync(hidinput->input);
+			}
+			input_event(input, EV_KEY, code, 1);
+			input_sync(hidinput->input);
+			if (KEY_9 == code)
+				set_leftp_pressed(hid);
+			else if (KEY_0 == code)
+				set_rightp_pressed(hid);
+		} else if (0 == value) {
+			input_event(input, EV_KEY, code, 0);
+			input_sync(hidinput->input);
+			if (KEY_9 == code)
+				clear_leftp_pressed(hid);
+			else if (KEY_0 == code) 
+				clear_rightp_pressed(hid);
+			if (!is_kpp_pressed(hid)) {
+				input_event(input, EV_KEY, KEY_LEFTSHIFT, 0);
+				input_sync(input);
+			}
+		}
+	}
+	return 0;
+}
+
+static void nek4k_disconnect(struct hid_device *hid, struct hid_input *hidinput)
+{
+	if (is_leftp_pressed(hid)) {
+		input_event(hidinput->input, EV_KEY, KEY_9, 0);
+		input_sync(hidinput->input);
+		input_event(hidinput->input, EV_KEY, KEY_LEFTSHIFT, 0);
+	}
+	if (is_rightp_pressed(hid)) {
+		input_event(hidinput->input, EV_KEY, KEY_0, 0);
+		input_sync(hidinput->input);
+		input_event(hidinput->input, EV_KEY, KEY_LEFTSHIFT, 0);
+	}
+	input_sync(hidinput->input);
+}
+
+static struct usb_device_id my_id_table[] = {
+	{ USB_DEVICE(MSNEK4K_ID_VENDOR, MSNEK4K_ID_PRODUCT) },
+	{}
+};
+
+static struct hidinput_simple_driver nek4k_driver = {
+	.name = driver_name,
+	.setup_usage = nek4k_setup_usage,
+	.clear_usage = nek4k_clear_usage,
+	.pre_event = nek4k_hid_event,
+	.disconnect = nek4k_disconnect,
+	.id_table = my_id_table,
+	.private = NULL
+};
+
+static int __init nek4k_init(void)
+{
+	int result;
+
+	result = hidinput_register_simple_driver(&nek4k_driver);
+	switch (result) {
+		case 0:
+			goto ok;
+		case -ENOMEM:
+		case -EBUSY:
+			goto ok;
+		case -EINVAL:
+			return result;
+		default:
+			return -ENODEV;
+	};
+
+	ok:
+	return 0;
+}
+
+static void __exit nek4k_exit(void)
+{
+	hidinput_unregister_simple_driver(&nek4k_driver);
+}
+
+module_init(nek4k_init);
+module_exit(nek4k_exit);
+
+MODULE_LICENSE("GPL");




[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux