diff -Naurp input.orig/Kconfig input/Kconfig --- input.orig/Kconfig 2006-07-16 03:00:43.000000000 +0800 +++ input/Kconfig 2006-07-24 14:04:56.000000000 +0800 @@ -326,3 +326,11 @@ config USB_APPLETOUCH To compile this driver as a module, choose M here: the module will be called appletouch. + +config HID_MSNEK4K + tristate "Microsoft Natural Ergonomic Keyboard 4000 Driver" + depends on USB && USB_HID + ---help--- + Microsoft Natural Ergonomic Keyboard 4000 driver. These extend keys + may not work without change user space configration, e.g, XKB conf- + iguration in X. diff -Naurp input.orig/Makefile input/Makefile --- input.orig/Makefile 2006-07-16 03:00:43.000000000 +0800 +++ input/Makefile 2006-07-24 14:04:46.000000000 +0800 @@ -44,6 +44,7 @@ obj-$(CONFIG_USB_ACECAD) += acecad.o obj-$(CONFIG_USB_YEALINK) += yealink.o obj-$(CONFIG_USB_XPAD) += xpad.o obj-$(CONFIG_USB_APPLETOUCH) += appletouch.o +obj-$(CONFIG_HID_MSNEK4K) += usbnek4k.o ifeq ($(CONFIG_USB_DEBUG),y) EXTRA_CFLAGS += -DDEBUG diff -Naurp input.orig/usbnek4k.c input/usbnek4k.c --- input.orig/usbnek4k.c 1970-01-01 08:00:00.000000000 +0800 +++ input/usbnek4k.c 2006-07-24 14:27:10.000000000 +0800 @@ -0,0 +1,400 @@ +/* + * Microsoft Natural Ergonomic Keyboard 4000 Driver + * + * Version: 0.2.0 + * + * TODO: + * Refactoring to escape from switch {case ...} hell. + * + * Copyright (c) 2006 Liyu + */ + +/* + * 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 +#include +#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");