diff -Naurp linux-2.6.18/drivers/usb/input/btp2118.c linux-2.6.18/drivers/usb/input.new/btp2118.c --- linux-2.6.18/drivers/usb/input/btp2118.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-2.6.18/drivers/usb/input.new/btp2118.c 2006-09-28 09:28:15.000000000 +0800 @@ -0,0 +1,434 @@ +/* + * Betop BTP-2118 joystick force-feedback driver + * + * Version: 0.1.0 + * + * Copyright (c) 2006 Li Yu + */ + +/* + * 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 +#include +#include +#include "hid.h" +#include "hid-simple.h" + +#define USB_ID_VENDOR 0x12bd +#define USB_ID_PRODUCT 0xc003 + +#define USAGE_PAGE_BTP_FF 0x008c0000 /* Bar Code Scanner ? */ +#define USAGE_BTP_FF 0x0002 + +#define BTP_EFFECT_NONE (-1) + +/* usb_btp_info->flags list */ +#define BTP_DISCONNECTING 0 +#define BTP_URB_DONE 1 +#define BTP_BUSY 2 /* avoid to resend urb */ + +#define IS_BTP_DISCONNECTING(info) (test_bit(BTP_DISCONNECTING, &(info)->flags)) +#define IS_BTP_URB_DONE(info) \ + (test_bit(BTP_URB_DONE|BTP_DISCONNECTING, &(info)->flags)) +#define IS_BTP_BUSY(info) (test_bit(BTP_BUSY, &(info)->flags)) + +#define BTP_SET_DISCONNECTING(info) (set_bit(BTP_DISCONNECTING, &(info)->flags)) +#define BTP_SET_URB_DONE(info) (set_bit(BTP_URB_DONE, &(info)->flags)) +#define BTP_SET_BUSY(info) (set_bit(BTP_BUSY, &(info)->flags)) + +#define BTP_CLR_BUSY(info) (clear_bit(BTP_BUSY, &(info)->flags)) + +static spinlock_t btp_lock; + +struct usb_btp_info { + struct timer_list timer; + unsigned long flags; + /* default shock strength */ + unsigned char left_strength; + unsigned char right_strength; + unsigned char start_packet[8]; + unsigned char stop_packet[8]; + /* ff data */ + struct ff_effect effects[8]; + int running_effect; + int repeat; + /* usb data */ + struct usb_ctrlrequest req; + struct usb_device *dev; + struct urb *ctrl0; + unsigned int pipe0; + int timeout; +}; + +static struct usb_device_id btp_id_table[] = { + { + USB_DEVICE(USB_ID_VENDOR, USB_ID_PRODUCT) + }, + {} +}; + +MODULE_DEVICE_TABLE(usb, btp_id_table); + +static char driver_name[] = "BETOP Vibration Gamepad (BTP-2118) Driver"; + +/* This gamepad reports three usages, but all are same. */ +static struct usage_block btp_ff_usage_block[] = { + USAGE_BLOCK(USAGE_BTP_FF, 0, EV_FF, FF_GAIN, 0), + USAGE_BLOCK(USAGE_BTP_FF, 0, EV_FF, FF_RUMBLE, 0), + USAGE_BLOCK(USAGE_BTP_FF, 0, EV_FF, FF_CONSTANT, 0), + USAGE_BLOCK(USAGE_BTP_FF, 0, EV_FF, FF_SPRING, 0), + USAGE_BLOCK(USAGE_BTP_FF, 0, EV_FF, FF_FRICTION, 0), + USAGE_BLOCK(USAGE_BTP_FF, 0, EV_FF, FF_DAMPER, 0), + USAGE_BLOCK(USAGE_BTP_FF, 0, EV_FF, FF_INERTIA, 0), + USAGE_BLOCK(USAGE_BTP_FF, 0, EV_FF, FF_RAMP, 0), + USAGE_BLOCK(USAGE_BTP_FF, 0, EV_FF_STATUS, FF_STATUS_STOPPED, 0), + USAGE_BLOCK(USAGE_BTP_FF, 0, EV_FF_STATUS, FF_STATUS_PLAYING, 0), + USAGE_BLOCK_NULL +}; + +static struct usage_page_block btp_ff_usage_page_blockes[] = { + USAGE_PAGE_BLOCK(USAGE_PAGE_BTP_FF, btp_ff_usage_block), + USAGE_PAGE_BLOCK_NULL +}; + +/* usb_complete_t */ +static void urb_complete(struct urb *urb, struct pt_regs *regs) +{ + struct usb_btp_info *info = urb->context; + + usb_unlink_urb(urb); + if (IS_BTP_DISCONNECTING(info)) + BTP_SET_URB_DONE(info); + BTP_CLR_BUSY(info); +} + +static int inline btp_effect_request(struct usb_btp_info *info, char *packet) +{ + if (IS_BTP_BUSY(info)) + return -EBUSY; + + usb_fill_control_urb (info->ctrl0, info->dev, info->pipe0, + (char*)&info->req, + packet, + info->req.wLength, + urb_complete, + info); + BTP_SET_BUSY(info); + usb_submit_urb(info->ctrl0, GFP_ATOMIC); + return 0; +} + +/* run by usb_btp_info->timer */ +static void btp_effect_repeat(unsigned long data) +{ + struct usb_btp_info *info = (struct usb_btp_info*)data; + + if (IS_BTP_DISCONNECTING(info)) + return; + + spin_lock(&btp_lock); + if (!info->repeat) { + info->running_effect = BTP_EFFECT_NONE; + } else { + mod_timer(&info->timer, jiffies+4*HZ); + BTP_CLR_BUSY(info); + btp_effect_request(info, info->start_packet); + --info->repeat; + } + spin_unlock(&btp_lock); +} + +/* the caller must hold btp_lock first */ +static int btp_effect_start(struct hid_input *hidinput) +{ + struct usb_btp_info *info; + + info = hidinput->private; + if (info) + return btp_effect_request(info, info->start_packet); + return -ENODEV; +} + +/* the caller must hold btp_lock first */ +static int btp_effect_stop(struct hid_input *hidinput) +{ + struct usb_btp_info *info; + + info = hidinput->private; + if (info) + return btp_effect_request(info, info->stop_packet); + return -ENODEV; +} + +static int btp_connect(struct hid_device *hid, struct hid_input *hidinput) +{ + struct usb_btp_info *info; + + info = kzalloc(sizeof(struct usb_btp_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + info->ctrl0 = usb_alloc_urb(0, GFP_KERNEL); + if (!info->ctrl0) { + kfree(info); + return -ENOMEM; + } + /* Betop-2118 joystick default parameter */ + info->left_strength = '\x14'; + info->right_strength = '\x14'; + info->req.bRequest = 0x9; + info->req.bRequestType = (USB_TYPE_CLASS|USB_RECIP_INTERFACE); + info->req.wIndex = 0x0; + info->req.wValue = 0x200; + info->req.wLength = 0x3; + info->timeout = USB_CTRL_SET_TIMEOUT; + info->dev = hid->dev; + info->pipe0 = usb_sndctrlpipe(hid->dev, 0); + info->start_packet[0] = info->left_strength; + info->start_packet[1] = info->right_strength; + info->running_effect = BTP_EFFECT_NONE; + init_timer(&info->timer); + info->timer.data = (unsigned long)info; + info->timer.function = btp_effect_repeat; + hidinput->private = info; + spin_lock_init(&btp_lock); + return 0; +} + +static void inline btp_wait_for_effect(struct usb_btp_info *info) +{ + while ( info->flags && !IS_BTP_URB_DONE(info) ) + schedule(); +} + +static void btp_disconnect(struct hid_device *hid, struct hid_input *hidinput) +{ + struct usb_btp_info *info; + unsigned long flags; + + if (!hidinput) + return; + + spin_lock_irqsave(&btp_lock, flags); + info = hidinput->private; + if (IS_BTP_BUSY(info)) + BTP_SET_DISCONNECTING(info); + info->repeat = 0; + spin_unlock_irqrestore(&btp_lock, flags); + + del_timer_sync(&info->timer); + btp_wait_for_effect(info); + + spin_lock_irqsave(&btp_lock, flags); + hidinput->private = NULL; + spin_unlock_irqrestore(&btp_lock, flags); + + usb_free_urb(info->ctrl0); + kfree(info); +} + +static void usb_btp_update_effect(struct hid_input *hidinput, int offset) +{ + struct usb_btp_info *info; + + spin_lock(&btp_lock); + info = hidinput->private; + if (offset == info->running_effect) { + if (btp_effect_stop(hidinput)) + goto exit; + if (info->effects[offset].type) { + if (btp_effect_start(hidinput)) + goto exit; + } + else /* remove effect */ + del_timer(&info->timer); + } +exit: + spin_unlock(&btp_lock); +} + +static int btp_FF_GAIN_handler(struct hid_input *hidinput, int value) +{ + int offset; + unsigned char strength; + struct usb_btp_info *info; + + if (value < 0 || value > 99) + return -EINVAL; + offset = (value>49); /*0 - left motor, 1 - right motor */ + if (offset) + value -= 50; + if (!value) { + strength = 0; + goto exit0; + } + /* the range of value is from 1 to 50 */ + /* this mapping value to shock strength (from 0xa to 0x1f) */ + strength = ((21*value)+459)/48; +exit0: + spin_lock(&btp_lock); + info = hidinput->private; + if (info) + info->start_packet[offset] = strength; + spin_unlock(&btp_lock); + return 0; +} + +static int btp_ff_event(struct input_dev *dev, int type, int code, int value) +{ + struct hid_input *hidinput; + struct usb_btp_info *info; + int ret = 0; + int running_effect; + + hidinput = hidinput_simple_inputdev_to_hidinput(dev); + if (!hidinput) + return -ENODEV; + + spin_lock(&btp_lock); + info = hidinput->private; + if (!info || IS_BTP_DISCONNECTING(info)) { + ret = -ENODEV; + goto unlock_exit; + } + running_effect = info->running_effect; + spin_unlock(&btp_lock); + + if (type == EV_FF_STATUS) { + if (code == running_effect) + input_report_ff_status(dev, code, FF_STATUS_PLAYING); + else + input_report_ff_status(dev, code, FF_STATUS_STOPPED); + } + + if (type != EV_FF) + return -EINVAL; + + switch (code) { + case FF_GAIN: + return btp_FF_GAIN_handler(hidinput, value); + default: + spin_lock(&btp_lock); + info = hidinput->private; + if (!info) { + ret = -ENODEV; + goto unlock_exit; + } + info->repeat = value; + if (value) { + if (btp_effect_start(hidinput)) + goto unlock_exit; + if (value>1) { + del_timer(&info->timer); + mod_timer(&info->timer, jiffies+4*HZ); + } + info->running_effect = code; + } + else { + del_timer(&info->timer); + if (btp_effect_stop(hidinput)) + goto unlock_exit; + info->running_effect = BTP_EFFECT_NONE; + } + } + +unlock_exit: + spin_unlock(&btp_lock); + return ret; +} + +static int inline btp_new_effect_id(void) +{ + static atomic_t effect_id= ATOMIC_INIT(0); + + atomic_inc(&effect_id); + return atomic_read(&effect_id); +} + +static int btp_upload_effect(struct input_dev *dev, struct ff_effect *effect) +{ + struct hid_input *hidinput; + struct usb_btp_info *info; + int offset; + + hidinput = hidinput_simple_inputdev_to_hidinput(dev); + if (!hidinput) { + return -ENODEV; + } + offset = effect->type - FF_RUMBLE; + effect->id = ((effect->id != -1) ?: btp_new_effect_id()); + if (offset>=0 && offset<8) { + spin_lock(&btp_lock); + info = hidinput->private; + if (!info || IS_BTP_DISCONNECTING(info)) { + spin_unlock(&btp_lock); + return -ENODEV; + } + memcpy(info->effects+offset, effect, sizeof(struct ff_effect)); + spin_unlock(&btp_lock); + usb_btp_update_effect(hidinput, effect->type); + return 0; + } + return -EINVAL; +} + +static int btp_erase_effect(struct input_dev *dev, int effect_id) +{ + struct hid_input *hidinput; + struct usb_btp_info *info; + int offset; + + hidinput = hidinput_simple_inputdev_to_hidinput(dev); + if (!hidinput) + return -ENODEV; + + spin_lock(&btp_lock); + info = hidinput->private; + if (!info || IS_BTP_DISCONNECTING(info)) { + spin_unlock(&btp_lock); + return -ENODEV; + } + for (offset=0; offset<8; ++offset) { + if (effect_id == info->effects[offset].id) { + memset(info->effects+offset, 0, sizeof(struct ff_effect)); + break; + } + } + spin_unlock(&btp_lock); + usb_btp_update_effect(hidinput, offset); + return 0; +} + +static struct hidinput_simple_driver btp_driver = { + .owner = THIS_MODULE, + .name = driver_name, + .connect = btp_connect, + .disconnect = btp_disconnect, + .ff_event = btp_ff_event, + .upload_effect = btp_upload_effect, + .erase_effect = btp_erase_effect, + .id_table = btp_id_table, + .usage_page_table = btp_ff_usage_page_blockes, + .private = NULL, +}; + +static int __init btp_init(void) +{ + return hidinput_register_simple_driver(&btp_driver); +} + +static void __exit btp_exit(void) +{ + hidinput_unregister_simple_driver(&btp_driver); +} + +module_init(btp_init); +module_exit(btp_exit); + +MODULE_LICENSE("GPL"); diff -Naurp linux-2.6.18/drivers/usb/input/hid-core.c linux-2.6.18/drivers/usb/input.new/hid-core.c --- linux-2.6.18/drivers/usb/input/hid-core.c 2006-09-20 11:42:06.000000000 +0800 +++ linux-2.6.18/drivers/usb/input.new/hid-core.c 2006-09-28 09:30:47.000000000 +0800 @@ -26,6 +26,7 @@ #include #include #include +#include #undef DEBUG #undef DEBUG_DATA @@ -33,6 +34,7 @@ #include #include "hid.h" +#include "hid-simple.h" #include /* @@ -46,6 +48,15 @@ static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick", "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"}; + +/* + * The global data structure for simple device driver interface. + */ +static DEFINE_MUTEX(matched_devices_lock); +static DEFINE_MUTEX(simple_drivers_lock); +static struct list_head matched_devices_list; +static struct list_head simple_drivers_list; + /* * Module parameters. */ @@ -785,8 +796,18 @@ static __inline__ int search(__s32 *arra static void hid_process_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value, int interrupt, struct pt_regs *regs) { hid_dump_input(usage, value); - if (hid->claimed & HID_CLAIMED_INPUT) + if (hid->claimed & HID_CLAIMED_INPUT) { + /* event filter here */ + if (hid->simple) { + if (hid->simple->pre_event && + !hid->simple->pre_event(hid, field, usage, + value, regs)) + return; + } hidinput_hid_event(hid, field, usage, value, regs); + if (hid->simple && hid->simple->post_event) + hid->simple->post_event(hid, field, usage, value, regs); + } if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt) hiddev_hid_event(hid, field, usage, value, regs); } @@ -832,7 +853,6 @@ static void hid_input_field(struct hid_d && field->usage[field->value[n] - min].hid && search(value, field->value[n], count)) hid_process_event(hid, field, &field->usage[field->value[n] - min], 0, interrupt, regs); - if (value[n] >= min && value[n] <= max && field->usage[value[n] - min].hid && search(field->value, value[n], count)) @@ -2031,8 +2051,33 @@ static void hid_disconnect(struct usb_in del_timer_sync(&hid->io_retry); flush_scheduled_work(); - if (hid->claimed & HID_CLAIMED_INPUT) + if (hid->claimed & HID_CLAIMED_INPUT) { + struct list_head *node; + struct matched_device *matched; + + matched = NULL; + mutex_lock(&matched_devices_lock); + list_for_each(node, &matched_devices_list) { + matched = list_entry(node, struct matched_device, node); + if (matched->intf == intf) { + list_del(&matched->node); + break; + } + matched = NULL; + } + mutex_unlock(&matched_devices_lock); + /* disconnect simple device driver if need */ + if (matched && hid->simple) { + hidinput_simple_driver_disconnect(hid); + module_put(hid->simple->owner); + hidinput_simple_driver_pop(hid, matched); + } + if (matched) { + matched->intf = NULL; + kfree(matched); + } hidinput_disconnect(hid); + } if (hid->claimed & HID_CLAIMED_HIDDEV) hiddev_disconnect(hid); @@ -2045,6 +2090,141 @@ static void hid_disconnect(struct usb_in hid_free_device(hid); } +#ifdef CONFIG_HID_SIMPLE +static int hidinput_simple_driver_bind_one(struct hidinput_simple_driver *simple, + struct hid_device *hid, + struct matched_device *matched) +{ + int ret; + + if (!try_module_get(simple->owner)) + return -ENODEV; + ret = hidinput_simple_driver_connect(simple, hid); + if (ret) + goto exit_err; + ret = hidinput_simple_driver_push(hid, simple, matched); + if (ret) + goto exit_err; + hidinput_simple_driver_setup_usage(hid); + printk(KERN_INFO "The Simple HID driver \'%s\' attach on \'%s\'\n", + simple->name, hid->name); + goto exit; +exit_err: + module_put(simple->owner); +exit: + return ret; +} + +static void hidinput_simple_driver_bind_foreach_simple( + struct matched_device *matched) +{ + struct hidinput_simple_driver *simple; + struct list_head *node; + struct hid_device *hid; + + if (!matched->intf) + return; + hid = usb_get_intfdata (matched->intf); + if (!hid || hid->simple) + return; + + mutex_lock(&simple_drivers_lock); + list_for_each(node, &simple_drivers_list) { + simple = list_entry(node, struct hidinput_simple_driver, node); + if (test_bit(HIDINPUT_SIMPLE_CONNECTED, &simple->flags)) + continue; + if (usb_match_id(matched->intf, simple->id_table)) { + hidinput_simple_driver_bind_one(simple, hid, matched); + break; + } + } + mutex_unlock(&simple_drivers_lock); +} + +static void hidinput_simple_driver_bind_foreach(void) +{ + struct hidinput_simple_driver *simple; + struct matched_device *matched=NULL; + struct list_head *matched_node = NULL; + struct list_head *simple_node = NULL; + struct hid_device *hid=NULL; + + mutex_lock(&matched_devices_lock); + list_for_each(matched_node, &matched_devices_list) { + matched = list_entry(matched_node, struct matched_device, node); + hid = usb_get_intfdata(matched->intf); + if (hid->simple) + continue; + mutex_lock(&simple_drivers_lock); + list_for_each(simple_node, &simple_drivers_list) { + simple = list_entry(simple_node, struct hidinput_simple_driver, node); + + if (test_bit(HIDINPUT_SIMPLE_CONNECTED, &simple->flags)) + continue; + if (!usb_match_id(matched->intf, simple->id_table)) + continue; + hidinput_simple_driver_bind_one(simple, hid, matched); + } + mutex_unlock(&simple_drivers_lock); + } + mutex_unlock(&matched_devices_lock); +} + +static void hidinput_simple_driver_bind_foreach_matched( + struct hidinput_simple_driver *simple) +{ + struct list_head *node=NULL; + struct matched_device *matched; + struct hid_device *hid=NULL; + + if (!simple || test_bit(HIDINPUT_SIMPLE_CONNECTED, &simple->flags)) + return; + + mutex_lock(&matched_devices_lock); + list_for_each(node, &matched_devices_list) { + matched = list_entry(node, struct matched_device, node); + hid = usb_get_intfdata (matched->intf); + if (hid->simple) + continue; + if (!usb_match_id(matched->intf, simple->id_table)) + continue; + hidinput_simple_driver_bind_one(simple, hid, matched); + } + mutex_unlock(&matched_devices_lock); +} + +int hidinput_register_simple_driver(struct hidinput_simple_driver *simple) +{ + if (!simple || !simple->name) + return -EINVAL; + + printk(KERN_INFO "The Simple HID driver \'%s\' register.\n", + simple->name); + hidinput_simple_driver_init(simple); + hidinput_simple_driver_bind_foreach_matched(simple); + + mutex_lock(&simple_drivers_lock); + list_add(&simple->node, &simple_drivers_list); + mutex_unlock(&simple_drivers_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(hidinput_register_simple_driver); + +void hidinput_unregister_simple_driver(struct hidinput_simple_driver *simple) +{ + printk(KERN_INFO "The Simple HID driver \'%s\' unregister.\n", + simple->name); + hidinput_simple_driver_clear(simple); + mutex_lock(&simple_drivers_lock); + list_del(&simple->node); + mutex_unlock(&simple_drivers_lock); + /* to active simple device driver that it is waiting */ + hidinput_simple_driver_bind_foreach(); +} +EXPORT_SYMBOL_GPL(hidinput_unregister_simple_driver); +#endif + static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct hid_device *hid; @@ -2061,15 +2241,25 @@ static int hid_probe(struct usb_interfac hid_init_reports(hid); hid_dump_device(hid); - if (!hidinput_connect(hid)) + usb_set_intfdata(intf, hid); + + if (!hidinput_connect(hid)) { + struct matched_device *matched; + matched = kmalloc(sizeof(struct matched_device), GFP_KERNEL); + if (matched) { + matched->intf = intf; + mutex_lock(&matched_devices_lock); + list_add(&matched->node, &matched_devices_list); + mutex_unlock(&matched_devices_lock); + hidinput_simple_driver_bind_foreach_simple(matched); + } hid->claimed |= HID_CLAIMED_INPUT; + } if (!hiddev_connect(hid)) hid->claimed |= HID_CLAIMED_HIDDEV; - usb_set_intfdata(intf, hid); - if (!hid->claimed) { - printk ("HID device not claimed by input or hiddev\n"); + printk (KERN_INFO "HID device not claimed by input or hiddev\n"); hid_disconnect(intf); return -ENODEV; } @@ -2168,6 +2358,8 @@ static int __init hid_init(void) retval = hiddev_init(); if (retval) goto hiddev_init_fail; + INIT_LIST_HEAD(&matched_devices_list); + INIT_LIST_HEAD(&simple_drivers_list); retval = usb_register(&hid_driver); if (retval) goto usb_register_fail; @@ -2182,7 +2374,15 @@ hiddev_init_fail: static void __exit hid_exit(void) { + struct list_head *node, *tmp; + struct matched_device *matched; + usb_deregister(&hid_driver); + list_for_each_safe(node, tmp, &matched_devices_list) { + matched = list_entry(node, struct matched_device, node); + list_del(&matched->node); + kfree(matched); + } hiddev_exit(); } diff -Naurp linux-2.6.18/drivers/usb/input/hid.h linux-2.6.18/drivers/usb/input.new/hid.h --- linux-2.6.18/drivers/usb/input/hid.h 2006-09-20 11:42:06.000000000 +0800 +++ linux-2.6.18/drivers/usb/input.new/hid.h 2006-09-28 09:28:15.000000000 +0800 @@ -389,8 +389,11 @@ struct hid_input { struct list_head list; struct hid_report *report; struct input_dev *input; + void *private; }; +struct hidinput_simple_driver; + struct hid_device { /* device report descriptor */ __u8 *rdesc; unsigned rsize; @@ -454,6 +457,8 @@ struct hid_device { /* device repo int (*ff_event)(struct hid_device *hid, struct input_dev *input, unsigned int type, unsigned int code, int value); + struct hidinput_simple_driver *simple; + #ifdef CONFIG_USB_HIDINPUT_POWERBOOK unsigned long pb_pressed_fn[NBITS(KEY_MAX)]; unsigned long pb_pressed_numlock[NBITS(KEY_MAX)]; diff -Naurp linux-2.6.18/drivers/usb/input/hid-input.c linux-2.6.18/drivers/usb/input.new/hid-input.c --- linux-2.6.18/drivers/usb/input/hid-input.c 2006-09-20 11:42:06.000000000 +0800 +++ linux-2.6.18/drivers/usb/input.new/hid-input.c 2006-09-28 09:28:15.000000000 +0800 @@ -34,7 +34,7 @@ #undef DEBUG #include "hid.h" - +#include "hid-simple.h" #define unk KEY_UNKNOWN static const unsigned char hid_keyboard[256] = { @@ -61,6 +61,8 @@ static const struct { __s32 y; } hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; +typedef void (*do_usage_t)(struct hid_field *, struct hid_usage *); + #define map_abs(c) do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0) #define map_rel(c) do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0) #define map_key(c) do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0) @@ -747,8 +749,13 @@ static int hidinput_input_event(struct i struct hid_field *field; int offset; - if (type == EV_FF) + if (type == EV_FF) { + if (hid->simple && hid->simple->ff_event) { + if (!hid->simple->ff_event(dev, type, code, value)); + return 0; + } return hid_ff_event(hid, dev, type, code, value); + } if (type != EV_LED) return -1; @@ -767,12 +774,22 @@ static int hidinput_input_event(struct i static int hidinput_open(struct input_dev *dev) { struct hid_device *hid = dev->private; - return hid_open(hid); + int ret = 0; + + if (hid->simple && hid->simple->open) + ret = hid->simple->open(dev); + if (!ret) + return hid_open(hid); + else + return ret; } static void hidinput_close(struct input_dev *dev) { struct hid_device *hid = dev->private; + + if (hid->simple && hid->simple->close) + hid->simple->close(dev); hid_close(hid); } @@ -821,7 +838,7 @@ int hidinput_connect(struct hid_device * input_dev->event = hidinput_input_event; input_dev->open = hidinput_open; input_dev->close = hidinput_close; - + hidinput_simple_driver_ff_init(input_dev); input_dev->name = hid->name; input_dev->phys = hid->phys; input_dev->uniq = hid->uniq; diff -Naurp linux-2.6.18/drivers/usb/input/hid-simple.c linux-2.6.18/drivers/usb/input.new/hid-simple.c --- linux-2.6.18/drivers/usb/input/hid-simple.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-2.6.18/drivers/usb/input.new/hid-simple.c 2006-09-29 09:49:33.000000000 +0800 @@ -0,0 +1,315 @@ +/* + * HID Simple Driver Interface v0.3.2 + * + * Copyright (c) 2006 Li Yu + */ + +/* + * 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 + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include + +#include "hid.h" +#include "hid-simple.h" + +typedef void (*do_usage_t)(struct hid_field *, struct hid_usage *); + +int hidinput_simple_driver_init(struct hidinput_simple_driver *drv) +{ + if (unlikely(!drv)) + return -EINVAL; + INIT_LIST_HEAD(&drv->node); + INIT_LIST_HEAD(&drv->raw_devices); + drv->flags = 0; + return 0; +} + +static void inline hidinput_simple_configure_one_usage(int op, + struct input_dev *input, + struct hid_usage *usage, + struct usage_block *usage_block) +{ + unsigned long *bits; + int flag; + struct hid_device *hid; + + hid = input->private; + switch (usage_block->event) { + case EV_KEY: + flag = HIDINPUT_SIMPLE_KEYBIT; + bits = input->keybit; + break; + case EV_REL: + flag = HIDINPUT_SIMPLE_RELBIT; + bits = input->relbit; + break; + case EV_ABS: + flag = HIDINPUT_SIMPLE_ABSBIT; + bits = input->relbit; + break; + case EV_MSC: + flag = HIDINPUT_SIMPLE_MSCBIT; + bits = input->absbit; + break; + case EV_SW: + flag = HIDINPUT_SIMPLE_SWBIT; + bits = input->swbit; + break; + case EV_LED: + flag = HIDINPUT_SIMPLE_LEDBIT; + bits = input->ledbit; + break; + case EV_SND: + flag = HIDINPUT_SIMPLE_SNDBIT; + bits = input->sndbit; + break; + case EV_FF: + flag = HIDINPUT_SIMPLE_FFBIT; + bits = input->ffbit; + break; + case EV_FF_STATUS: + flag = HIDINPUT_SIMPLE_FFSTSBIT; + bits = NULL; + break; + default: + return; + } + + if (__OP_SET_BIT == op) { + usage->code = usage_block->code; + usage->type = usage_block->event; + if (bits) + set_bit(usage_block->code, bits); + /* if this event bit is set by us first, remember it */ + if (!test_and_set_bit(usage_block->event, input->evbit)) + set_bit(flag, &hid->simple->flags); + } + else if (__OP_CLR_BIT == op) { + usage->code = 0; + usage->type = 0; + if (bits) + clear_bit(usage_block->code, bits); + /* clear event bit, only if it's set by us. */ + if (test_and_clear_bit(flag, &hid->simple->flags)) + clear_bit(usage_block->event, input->evbit); + } +} + +static do_usage_t hidinput_simple_driver_configure_usage_prep( + struct hid_device *hid, + int *op) +{ + do_usage_t do_usage; + + if (!hid->simple) + return NULL; + + if (test_bit(HIDINPUT_SIMPLE_SETUP_USAGE, &hid->simple->flags)) { + do_usage = hid->simple->setup_usage; + *op = __OP_SET_BIT; + } + else { + do_usage = hid->simple->clear_usage; + *op = __OP_CLR_BIT; + } + return do_usage; +} + +static void __hidinput_simple_driver_configure_usage(int op, + struct hid_field *field, + struct hid_usage *hid_usage) +{ + struct input_dev *input = field->hidinput->input; + struct hid_device *hid = input->private; + struct usage_block *usage_block; + struct usage_page_block *page_block; + int page; + int usage; + + page = (hid_usage->hid & HID_USAGE_PAGE); + usage = (hid_usage->hid & HID_USAGE); + page_block = hid->simple->usage_page_table; + for (;page_block && page_block->usage_blockes;page_block++) { + if (page_block->page != page) + continue; + usage_block = page_block->usage_blockes; + for (; usage_block && usage_block->usage; usage_block++) { + if (usage_block->usage != usage) + continue; + hidinput_simple_configure_one_usage(op, input, + hid_usage, usage_block); + } + } +} + +/* + * To give one simple device a configure usage chance. + * The framework of this function come from hidinput_connect() + */ +void hidinput_simple_driver_configure_usage(struct hid_device *hid) +{ + struct hid_report *report; + int i, j, k; + do_usage_t do_usage; + int op; + + if (!hid->simple) + return; + do_usage = hidinput_simple_driver_configure_usage_prep(hid, &op); + + for (i = 0; i < hid->maxcollection; i++) + if (hid->collection[i].type == HID_COLLECTION_APPLICATION || + hid->collection[i].type==HID_COLLECTION_PHYSICAL) + if (IS_INPUT_APPLICATION(hid->collection[i].usage)) + break; + + if (i == hid->maxcollection) + return; + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) + list_for_each_entry(report, &hid->report_enum[k].report_list, list) { + if (!report->maxfield) + continue; + + for (i = 0; i < report->maxfield; i++) + for (j = 0; j < report->field[i]->maxusage; j++) { + __hidinput_simple_driver_configure_usage(op, report->field[i], report->field[i]->usage + j); + if (do_usage) + do_usage(report->field[i], + report->field[i]->usage + j); + } + } + + return; +} + +int hidinput_simple_driver_push(struct hid_device *hid, + struct hidinput_simple_driver *simple, + struct matched_device *matched) +{ + struct raw_simple_device *raw_simple; + + raw_simple = kmalloc(sizeof(struct raw_simple_device), GFP_KERNEL); + if (!raw_simple) + return -ENOMEM; + raw_simple->intf = matched->intf; + hid = usb_get_intfdata(matched->intf); + hid->simple = simple; + list_add(&raw_simple->node, &simple->raw_devices); + return 0; +} + +void hidinput_simple_driver_pop(struct hid_device *hid, + struct matched_device *matched) +{ + struct list_head *node; + struct raw_simple_device *raw_simple=NULL; + + list_for_each (node, &hid->simple->raw_devices) { + raw_simple = list_entry(node, struct raw_simple_device, node); + if (raw_simple && raw_simple->intf == matched->intf) { + hid->simple = NULL; + list_del(&raw_simple->node); + kfree(raw_simple); + return; + } + } +} + +void hidinput_simple_driver_clear(struct hidinput_simple_driver *simple) +{ + struct raw_simple_device *raw_simple; + struct hid_device *hid; + + while (!list_empty_careful(&simple->raw_devices)) { + raw_simple = list_entry(simple->raw_devices.next, + struct raw_simple_device, node); + hid = usb_get_intfdata (raw_simple->intf); + if (hid) { + if (hid->simple) + BUG_ON(hid->simple != simple); + hidinput_simple_driver_clear_usage(hid); + wmb(); + hidinput_simple_driver_disconnect(hid); + hid->simple = NULL; + } + list_del_init(simple->raw_devices.next); + } +} + +int hidinput_simple_driver_connect(struct hidinput_simple_driver *simple, + struct hid_device *hid) +{ + struct hid_input *hidinput, *next; + int ret = -ENODEV; + + if (!simple) + goto exit; + if (!simple->connect) { + ret = 0; + goto exit; + } + + list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { + if (!simple->connect(hid, hidinput)) { + hid->simple = simple; + ret = 0; + } + } +exit: + set_bit(HIDINPUT_SIMPLE_CONNECTED, &simple->flags); + return ret; +} + + +void hidinput_simple_driver_disconnect(struct hid_device *hid) +{ + struct hid_input *hidinput, *next; + + if (!hid || !hid->simple) + return; + + if (!hid->simple->disconnect) + goto exit; + list_for_each_entry_safe(hidinput, next, &hid->inputs, list) { + hid->simple->disconnect(hid, hidinput); + } +exit: + clear_bit(HIDINPUT_SIMPLE_CONNECTED, &hid->simple->flags); + return; +} + +struct hid_input* hidinput_simple_inputdev_to_hidinput(struct input_dev *dev) +{ + struct hid_device *hid = dev->private; + struct list_head *iter; + struct hid_input *hidinput; + + list_for_each(iter, &hid->inputs) { + hidinput = list_entry(iter, struct hid_input, list); + if (hidinput->input == dev) + return hidinput; + } + return NULL; +} +EXPORT_SYMBOL_GPL(hidinput_simple_inputdev_to_hidinput); diff -Naurp linux-2.6.18/drivers/usb/input/hid-simple.h linux-2.6.18/drivers/usb/input.new/hid-simple.h --- linux-2.6.18/drivers/usb/input/hid-simple.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-2.6.18/drivers/usb/input.new/hid-simple.h 2006-09-28 09:28:15.000000000 +0800 @@ -0,0 +1,232 @@ +/* + * NOTE: + * For use me , you must include hid.h in your source first. + */ +#ifndef __HID_SIMPLE_H +#define __HID_SIMPLE_H + +#ifdef __KERNEL__ + +#include + +/***** The public interface for simple device driver *****/ +struct usage_block { + int usage; /* usage code */ + int value; /* not used, for F_EVENT_BY_VALUE in future */ + int event; /* input event type, e.g. EV_KEY. */ + int code; /* input subsystem code, e.g. KEY_F1. */ + int flags; /* not used */ +}; + +struct usage_page_block { + int page; /* usage page code */ + int flags; /* not used */ + struct usage_block *usage_blockes; +}; + +/* usage_block flags list */ +#define F_EVENT_BY_VALUE (0x1) /* submit input event by usage_block.value, + not implement yet */ + +#define USAGE_BLOCK(i_usage, i_value, i_event, i_code, i_flags) \ + {\ + .usage = (i_usage),\ + .value = (i_value),\ + .event = (i_event),\ + .code = (i_code),\ + .flags = (i_flags),\ + } + +#define USAGE_BLOCK_NULL {} + +#define USAGE_PAGE_BLOCK(i_page, i_usage_blockes) \ + {\ + .page = (i_page),\ + .usage_blockes = (i_usage_blockes),\ + } + +#define USAGE_PAGE_BLOCK_NULL {} + +#define __SIMPLE_DRIVER_INIT \ + .owner = THIS_MODULE, + +struct hidinput_simple_driver { +/* private */ + struct list_head node; /* link with simple_drivers_list */ + struct list_head raw_devices; + unsigned long flags; +/* public */ + struct module *owner; + char *name; + int (*connect)(struct hid_device *, struct hid_input *); + void (*disconnect)(struct hid_device *, struct hid_input *); + void (*setup_usage)(struct hid_field *, struct hid_usage *); + void (*clear_usage)(struct hid_field *, struct hid_usage *); + int (*pre_event)(const struct hid_device *, const struct hid_field *, + const struct hid_usage *, const __s32, + const struct pt_regs *regs); + int (*post_event)(const struct hid_device *, const struct hid_field *, + const struct hid_usage *, const __s32, + const struct pt_regs *regs); + int (*open)(struct input_dev *dev); + void (*close)(struct input_dev *dev); + int (*upload_effect)(struct input_dev *dev, struct ff_effect *effect); + int (*erase_effect)(struct input_dev *dev, int effect_id); + int (*flush)(struct input_dev *dev, struct file *file); + int (*ff_event)(struct input_dev *dev, int type, int code, int value); + struct usb_device_id *id_table; + struct usage_page_block *usage_page_table; + void *private; +}; + + +int hidinput_register_simple_driver(struct hidinput_simple_driver *device); +void hidinput_unregister_simple_driver(struct hidinput_simple_driver *device); +struct hid_input* hidinput_simple_inputdev_to_hidinput(struct input_dev *dev); + +/********************* The public section end ***********/ + +/***** The private section for simple device driver implement only *****/ + +/* simple driver internal flags */ +#define HIDINPUT_SIMPLE_SETUP_USAGE (0x0) +#define HIDINPUT_SIMPLE_KEYBIT (0x1) +#define HIDINPUT_SIMPLE_RELBIT (0x2) +#define HIDINPUT_SIMPLE_ABSBIT (0x3) +#define HIDINPUT_SIMPLE_MSCBIT (0x4) +#define HIDINPUT_SIMPLE_SWBIT (0x5) +#define HIDINPUT_SIMPLE_LEDBIT (0x6) +#define HIDINPUT_SIMPLE_SNDBIT (0x7) +#define HIDINPUT_SIMPLE_FFBIT (0x8) +#define HIDINPUT_SIMPLE_FFSTSBIT (0x9) +#define HIDINPUT_SIMPLE_CONNECTED (0x1f) + +/* used in hidinput_simple_driver_configure_usage() */ +#define __OP_SET_BIT (1) +#define __OP_CLR_BIT (0) + +/* + * matched_device record one device which hid-subsystem handle, it may + * be one simple device can not handle. + * + * The element of matched_device list is inserted at hidinput_connect(), + * and is removed at hidinput_disconnect(). + */ +struct matched_device { + struct usb_interface *intf; + struct list_head node; +}; + +/* + * raw_simple_driver record one device which hid simple device handle. + * It used as one member of hid_simple_driver. + */ + +struct raw_simple_device { + struct usb_interface *intf; + struct list_head node; +}; + +#ifdef CONFIG_HID_SIMPLE +extern void hidinput_simple_driver_configure_usage(struct hid_device *hid); +extern int hidinput_simple_driver_init(struct hidinput_simple_driver *simple); +extern int hidinput_simple_driver_push(struct hid_device *hid, + struct hidinput_simple_driver *simple, + struct matched_device *dev); +extern void hidinput_simple_driver_pop(struct hid_device *hid, + struct matched_device *dev); +extern void hidinput_simple_driver_clear(struct hidinput_simple_driver *simple); +extern int hidinput_simple_driver_connect(struct hidinput_simple_driver *simple, + struct hid_device *hid); +extern void hidinput_simple_driver_disconnect(struct hid_device *hid); +static void inline hidinput_simple_driver_setup_usage(struct hid_device *hid) +{ + if (hid && hid->simple) { + set_bit(HIDINPUT_SIMPLE_SETUP_USAGE, &hid->simple->flags); + hidinput_simple_driver_configure_usage(hid); + } +} + +static void inline hidinput_simple_driver_clear_usage(struct hid_device *hid) +{ + if (hid && hid->simple) { + clear_bit(HIDINPUT_SIMPLE_SETUP_USAGE, &hid->simple->flags); + hidinput_simple_driver_configure_usage(hid); + } +} + +#ifdef CONFIG_HID_SIMPLE_FF +static inline int hidinput_upload_effect(struct input_dev *dev, + struct ff_effect *effect) +{ + struct hid_device *hid; + + hid = dev->private; + if (hid->simple && hid->simple->upload_effect) + return hid->simple->upload_effect(dev, effect); + return 0; +} + +static inline int hidinput_erase_effect(struct input_dev *dev, int effect_id) +{ + struct hid_device *hid; + + hid = dev->private; + if (hid->simple && hid->simple->erase_effect) + return hid->simple->erase_effect(dev, effect_id); + return 0; +} + +static inline int hidinput_flush(struct input_dev *dev, struct file *filep) +{ + struct hid_device *hid; + + hid = dev->private; + if (hid->simple && hid->simple->flush) + return hid->simple->flush(dev, filep); + return 0; +} + +static void inline hidinput_simple_driver_ff_init(struct input_dev *input_dev) +{ + input_dev->upload_effect = hidinput_upload_effect; + input_dev->erase_effect = hidinput_erase_effect; + input_dev->flush = hidinput_flush; +} +#else /* CONFIG_HID_SIMPLE_FF */ +static void inline hidinput_simple_driver_ff_init(struct input_dev *input_dev) {} +#endif /* CONFIG_HID_SIMPLE_FF */ +#else /* CONFIG_HID_SIMPLE */ +static inline void hidinput_simple_driver_bind_foreach_simple( + struct matched_device *matched) {} +static inline void hidinput_simple_driver_configure_usage(struct hid_device *hid) +{} +static inline int hidinput_simple_driver_init(struct hidinput_simple_driver *simple) +{ + return 0; +} +static inline int hidinput_simple_driver_push(struct hid_device *hid, + struct hidinput_simple_driver *simple, + struct matched_device *dev) +{ + return 0; +} +static inline void hidinput_simple_driver_pop(struct hid_device *hid, + struct matched_device *dev) {} +static inline void hidinput_simple_driver_clear( + struct hidinput_simple_driver *simple) {} +static inline int hidinput_simple_driver_connect( + struct hidinput_simple_driver *simple, + struct hid_device *hid) +{ + return 0; +} +static inline void hidinput_simple_driver_disconnect(struct hid_device *hid) {} +static inline void hidinput_simple_driver_setup_usage(struct hid_device *hid) {} +static inline void hidinput_simple_driver_clear_usage(struct hid_device *hid) {} +static void inline hidinput_simple_driver_ff_init(struct input_dev *input_dev) {} +#endif + +/***** The private section end. *****/ +#endif /* __KERNEL__ */ +#endif /* __HID_SIMPLE_H */ diff -Naurp linux-2.6.18/drivers/usb/input/Kconfig linux-2.6.18/drivers/usb/input.new/Kconfig --- linux-2.6.18/drivers/usb/input/Kconfig 2006-09-20 11:42:06.000000000 +0800 +++ linux-2.6.18/drivers/usb/input.new/Kconfig 2006-09-28 09:28:15.000000000 +0800 @@ -326,3 +326,31 @@ config USB_APPLETOUCH To compile this driver as a module, choose M here: the module will be called appletouch. + +config HID_SIMPLE + bool "HID simple driver interface" + depends on USB_HIDINPUT + help + This simple interface let the writing HID driver more easier. Moreover, + this allow you write force-feedback driver without touch HID input + implementation code. Also, it can be used as input filter. + +config HID_SIMPLE_MSNEK4K + tristate "Microsoft Natural Ergonomic Keyboard 4000 Driver" + depends on HID_SIMPLE + help + Microsoft Natural Ergonomic Keyboard 4000 extend keys support. These + may not work without change user space configration, e.g, XKB conf- + iguration in X. + +config HID_SIMPLE_FF + bool "HID simple driver interface force feedback support" + depends on HID_SIMPLE && !HID_FF + help + This feature can not be compatible with "Force feedback support" (HID_FF). + +config HID_SIMPLE_FF_BTP2118 + tristate "Betop 2118 joystick force feedback support" + depends on HID_SIMPLE_FF + help + This can enable Betop 2118 joystick force feedback feature. diff -Naurp linux-2.6.18/drivers/usb/input/Makefile linux-2.6.18/drivers/usb/input.new/Makefile --- linux-2.6.18/drivers/usb/input/Makefile 2006-09-20 11:42:06.000000000 +0800 +++ linux-2.6.18/drivers/usb/input.new/Makefile 2006-09-28 09:28:15.000000000 +0800 @@ -25,6 +25,9 @@ endif ifeq ($(CONFIG_HID_FF),y) usbhid-objs += hid-ff.o endif +ifeq ($(CONFIG_HID_SIMPLE),y) + usbhid-objs += hid-simple.o +endif obj-$(CONFIG_USB_AIPTEK) += aiptek.o obj-$(CONFIG_USB_ATI_REMOTE) += ati_remote.o @@ -44,6 +47,8 @@ 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_SIMPLE_MSNEK4K) += usbnek4k.o +obj-$(CONFIG_HID_SIMPLE_FF_BTP2118) += btp2118.o ifeq ($(CONFIG_USB_DEBUG),y) EXTRA_CFLAGS += -DDEBUG diff -Naurp linux-2.6.18/drivers/usb/input/usbnek4k.c linux-2.6.18/drivers/usb/input.new/usbnek4k.c --- linux-2.6.18/drivers/usb/input/usbnek4k.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-2.6.18/drivers/usb/input.new/usbnek4k.c 2006-09-29 11:21:02.000000000 +0800 @@ -0,0 +1,210 @@ +/* + * Microsoft Natural Ergonomic Keyboard 4000 Driver + * + * Version: 0.3.2 + * + * Copyright (c) 2006 Li Yu + */ + +/* + * 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 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_CUSTOM_RELEASE 0x0 +#define USAGE_CUSTOM_1 0x1 +#define USAGE_CUSTOM_2 0x2 +#define USAGE_CUSTOM_3 0x4 +#define USAGE_CUSTOM_4 0x8 +#define USAGE_CUSTOM_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_PAREN 0xb6 +#define USAGE_KEYPAD_RIGHT_PAREN 0xb7 + +#define MSNEK4K_ID_VENDOR 0x045e +#define MSNEK4K_ID_PRODUCT 0x00db + +MODULE_DEVICE_TABLE(usb, nek4k_id_table); + +static char driver_name[] = "Microsoft Natural Ergonomic Keyboard 4000"; + +struct usage_block consumer_usage_block[] = { + USAGE_BLOCK(USAGE_ZOOM_IN, 0, EV_KEY, KEY_F13, 0), + USAGE_BLOCK(USAGE_ZOOM_OUT, 0, EV_KEY, KEY_F14, 0), + USAGE_BLOCK(USAGE_HOME, 0, EV_KEY, KEY_HOMEPAGE, 0), + USAGE_BLOCK(USAGE_SEARCH, 0, EV_KEY, KEY_SEARCH, 0), + USAGE_BLOCK(USAGE_EMAIL, 0, EV_KEY, KEY_EMAIL, 0), + USAGE_BLOCK(USAGE_FAVORITES, 0, EV_KEY, KEY_F16, 0), + USAGE_BLOCK(USAGE_FAVORITES, 0, EV_KEY, KEY_FAVORITES, 0), + USAGE_BLOCK(USAGE_MUTE, 0, EV_KEY, KEY_MUTE, 0), + USAGE_BLOCK(USAGE_VOLUME_DOWN, 0, EV_KEY, KEY_VOLUMEDOWN, 0), + USAGE_BLOCK(USAGE_VOLUME_UP, 0, EV_KEY, KEY_VOLUMEUP, 0), + USAGE_BLOCK(USAGE_PLAY_PAUSE, 0, EV_KEY, KEY_PLAYPAUSE, 0), + USAGE_BLOCK(USAGE_CALCULATOR, 0, EV_KEY, KEY_CALC, 0), + USAGE_BLOCK(USAGE_BACK, 0, EV_KEY, KEY_BACK, 0), + USAGE_BLOCK(USAGE_FORWARD, 0, EV_KEY, KEY_FORWARD, 0), + USAGE_BLOCK(USAGE_HELP, 0, EV_KEY, KEY_HELP, 0), + USAGE_BLOCK(USAGE_UNDO, 0, EV_KEY, KEY_UNDO, 0), + USAGE_BLOCK(USAGE_REDO, 0, EV_KEY, KEY_F17, 0), + USAGE_BLOCK(USAGE_REDO, 0, EV_KEY, KEY_REDO, 0), + USAGE_BLOCK(USAGE_NEW, 0, EV_KEY, KEY_NEW, 0), + USAGE_BLOCK(USAGE_OPEN, 0, EV_KEY, KEY_OPEN, 0), + USAGE_BLOCK(USAGE_CLOSE, 0, EV_KEY, KEY_CLOSE, 0), + USAGE_BLOCK(USAGE_REPLY, 0, EV_KEY, KEY_REPLY, 0), + USAGE_BLOCK(USAGE_FWD, 0, EV_KEY, KEY_FORWARDMAIL, 0), + USAGE_BLOCK(USAGE_SEND, 0, EV_KEY, KEY_SEND, 0), + USAGE_BLOCK(USAGE_SPELL, 0, EV_KEY, KEY_F15, 0), + USAGE_BLOCK(USAGE_SAVE, 0, EV_KEY, KEY_SAVE, 0), + USAGE_BLOCK(USAGE_PRINT, 0, EV_KEY, KEY_PRINT, 0), + USAGE_BLOCK_NULL +}; + +struct usage_block msvendor_usage_block[] = { + USAGE_BLOCK(USAGE_CUSTOM, USAGE_CUSTOM_1, EV_KEY, KEY_FN_F1, 0), + USAGE_BLOCK(USAGE_CUSTOM, USAGE_CUSTOM_2, EV_KEY, KEY_FN_F2, 0), + USAGE_BLOCK(USAGE_CUSTOM, USAGE_CUSTOM_3, EV_KEY, KEY_FN_F3, 0), + USAGE_BLOCK(USAGE_CUSTOM, USAGE_CUSTOM_4, EV_KEY, KEY_FN_F4, 0), + USAGE_BLOCK(USAGE_CUSTOM, USAGE_CUSTOM_5, EV_KEY, KEY_FN_F5, 0), + USAGE_BLOCK_NULL +}; + +struct usage_block keyboard_usage_block[] = { + USAGE_BLOCK(USAGE_KEYPAD_EQUAL, 0, EV_KEY, KEY_KPEQUAL, 0), + USAGE_BLOCK(USAGE_KEYPAD_LEFT_PAREN, 0, EV_KEY, KEY_KPLEFTPAREN, 0), + USAGE_BLOCK(USAGE_KEYPAD_RIGHT_PAREN, 0, EV_KEY, KEY_KPRIGHTPAREN, 0), + USAGE_BLOCK_NULL +}; + +struct usage_page_block nek4k_usage_page_blockes[] = { + USAGE_PAGE_BLOCK(HID_UP_CONSUMER, consumer_usage_block), + USAGE_PAGE_BLOCK(HID_UP_MSVENDOR, msvendor_usage_block), + USAGE_PAGE_BLOCK(HID_UP_KEYBOARD, keyboard_usage_block), + USAGE_PAGE_BLOCK_NULL +}; + +static int nek4k_pre_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, ascii_keycode = 0, ev_value; + + ev_value = value?1:0; + + switch (usage->hid&HID_USAGE) { + case USAGE_FAVORITES: + code = KEY_FAVORITES; + ascii_keycode = KEY_F16; + goto exit; + case USAGE_REDO: + code = KEY_REDO; + ascii_keycode = KEY_F17; + goto exit; + }; + + if ((usage->hid&HID_USAGE) != USAGE_CUSTOM) + /* let hid core continue to process them */ + return (!0); + + switch (value) { + case USAGE_CUSTOM_RELEASE: + code = get_keycode(hidinput->private); + ascii_keycode = KEY_F18+(code-KEY_FN_F1); + break; + case USAGE_CUSTOM_1: + code = KEY_FN_F1; + ascii_keycode = KEY_F18; + break; + case USAGE_CUSTOM_2: + code = KEY_FN_F2; + ascii_keycode = KEY_F19; + break; + case USAGE_CUSTOM_3: + code = KEY_FN_F3; + ascii_keycode = KEY_F20; + break; + case USAGE_CUSTOM_4: + code = KEY_FN_F4; + ascii_keycode = KEY_F21; + break; + case USAGE_CUSTOM_5: + code = KEY_FN_F5; + ascii_keycode = KEY_F22; + break; + } +exit: + if (code) { + hidinput->private = (void*)code; + input_event(input, EV_KEY, code, ev_value); + input_sync(input); + } + if (ascii_keycode) { + input_event(input, EV_KEY, ascii_keycode, ev_value); + input_sync(input); + } + return 0; +} + +static struct hidinput_simple_driver nek4k_driver = { + .owner = THIS_MODULE, + .name = driver_name, + .pre_event = nek4k_pre_event, + .id_table = nek4k_id_table, + .usage_page_table = nek4k_usage_page_blockes, + .private = NULL +}; + +static int __init nek4k_init(void) +{ + return hidinput_register_simple_driver(&nek4k_driver); +} + +static void __exit nek4k_exit(void) +{ + hidinput_unregister_simple_driver(&nek4k_driver); +} + +module_init(nek4k_init); +module_exit(nek4k_exit); + +MODULE_LICENSE("GPL");