=====================
HID device simple driver interface
=====================
Goal:
----------------
Let us write HID device driver more easier.
Basic idea:
---------------
Under current HID device driver development technique, We need
write one new interrupt handler to report event to input subsystem as
long as one new HID device come. However, the most of them have only
some extended keys, I think it seem break a fly on the wheel, which
write one new interrupt handler for this reason,
My idea is reuse the interrupt handler in hid-core.c. so we
write driver for new simple HID device will be more easier, and need not
touch hid core.
In essence, this interface just are some hooks in HID core.
Limitation:
----------------
The driver use this simple interface only can work with one
device at same time. In most time, this just is not a problem. if you
are going to make your driver can work with a bundle of devices at same
time, the I am sorry, this simple interface can not help you, and I am
afraid that driver is not one simple driver. Of course, any improvement
on this patch is welcome :)
Testing:
--------------
Tested on i386.
Usage:
---------------
Although this simple driver have not direct relation with device
driver core, but I still make its interface like
it on purpose.
The simple device has five methods:
1. int (*connect)(struct hid_device *);
2. void (*disconnect)(struct hid_device *);
When you simple device is connect with one real HID device, we
will call connect() method. To return 0 flag it complete its job
successfully. Any other value is looked as one error.
When the HID device that your simple device connect with is
down, or you unregister this simple device, we will call disconnect()
method.
3. void (*setup_usage)(struct hid_field *, struct hid_usage *);
4. void (*clear_usage)(struct hid_field *, struct hid_usage *);
The setup_usage() method is like hidinput_configure_usage() in
hid_input.c. You also can setup input_dev here. In most time, I think
you should
be fill the pointer slot for this method, elsewise the event() method do
not work for you at all.
The clear_usage() method is used to clear side-effect that came
from setup_usage() method, if they are there. Of course, you can do same
job in disconnect() method, but this method let your life more
simpler.
5. void (*event)(const struct hid_device *, const struct
hid_field *, const struct hid_usage *, const __s32, const struct pt_regs
*regs);
Its behavior is same with hidinput_hid_event() exactly. Note
again, if you do not correctly configure usage in setup_usage(), this
method do not work as you want.
All these method are optional, but if they are all NULL
pointers, what are you want *-)
Other information
............................
I am sorry that these patches are included in attachment, my
mail client alway transform TAB to eight spaces.
The attatchment include:
1. HID device simple driver interface
2. One sample use this interface.
3. Microsoft Natural Ergonomic Keyboard 4000 Driver use this
interface.
Happy new year.
diff -Naurp linux-2.6.15.orig/drivers/usb/input/hid-core.c linux-2.6.15/drivers/usb/input/hid-core.c
--- linux-2.6.15.orig/drivers/usb/input/hid-core.c 2006-01-03 11:21:10.000000000 +0800
+++ linux-2.6.15/drivers/usb/input/hid-core.c 2006-01-04 16:59:03.000000000 +0800
@@ -4,6 +4,8 @@
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <[email protected]>
* Copyright (c) 2005 Michael Haboustak <[email protected]> for Concept2, Inc
+ * Copyright (c) 2005 Liyu <[email protected]>
+ * To support simple HID device driver interface.
*/
/*
@@ -35,6 +37,8 @@
#include "hid.h"
#include <linux/hiddev.h>
+#include "hid-simple.h"
+
/*
* Version Information
*/
@@ -46,6 +50,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 spinlock_t matched_lock;
+static spinlock_t simple_lock;
+static struct list_head matched_devices_list;
+static struct list_head simple_devices_list;
+
/*
* Module parameters.
*/
@@ -793,8 +806,11 @@ 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) {
hidinput_hid_event(hid, field, usage, value, regs);
+ if (hid->simple && hid->simple->event)
+ hid->simple->event(hid, field, usage, value, regs);
+ }
if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt)
hiddev_hid_event(hid, field, usage, value, regs);
}
@@ -1820,6 +1836,8 @@ fail:
static void hid_disconnect(struct usb_interface *intf)
{
struct hid_device *hid = usb_get_intfdata (intf);
+ struct list_head *node;
+ struct matched_device *matched;
if (!hid)
return;
@@ -1829,8 +1847,38 @@ static void hid_disconnect(struct usb_in
usb_kill_urb(hid->urbout);
usb_kill_urb(hid->urbctrl);
- if (hid->claimed & HID_CLAIMED_INPUT)
- hidinput_disconnect(hid);
+ if (hid->claimed & HID_CLAIMED_INPUT) {
+ /* disconnect simple device if need */
+ if (hid->simple) {
+ if (hid->simple->disconnect)
+ hid->simple->disconnect(hid);
+ /* save them to matche other hid_device later */
+ spin_lock(&simple_lock);
+ list_add(&hid->simple->node, &simple_devices_list);
+ spin_unlock(&simple_lock);
+ hid->simple->intf = NULL;
+ hid->simple = NULL;
+ }
+ /* scan matched_devices_list to free our matched_device */
+ matched = NULL; /* shut up gcc */
+
+ spin_lock(&matched_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;
+ }
+ spin_unlock(&matched_lock);
+ if (matched) {
+ matched->intf = 0;
+ kfree(matched);
+ }
+ hidinput_disconnect(hid);
+ }
+
if (hid->claimed & HID_CLAIMED_HIDDEV)
hiddev_disconnect(hid);
@@ -1843,13 +1891,162 @@ static void hid_disconnect(struct usb_in
hid_free_device(hid);
}
+static void
+hidinput_simple_device_bind_foreach(void)
+{
+ struct hidinput_simple_device *simple=0;
+ struct matched_device *matched=0;
+ struct list_head *simple_node;
+ struct list_head *matched_node;
+ struct hid_device *hid;
+
+ spin_lock(&matched_lock);
+ list_for_each(matched_node, &matched_devices_list) {
+ matched = list_entry(matched_node, struct matched_device, node);
+ spin_lock(&simple_lock);
+ list_for_each(simple_node, &simple_devices_list) {
+ simple = list_entry(simple_node, struct hidinput_simple_device, node);
+ if (usb_match_id(matched->intf, simple->id))
+ break;
+ simple = 0;
+ }
+ spin_unlock(&simple_lock);
+ if (simple)
+ break;
+ }
+ spin_unlock(&matched_lock);
+ /* no such simple device match this hid_device, also ok */
+ if (0 == simple)
+ return;
+
+ hid = usb_get_intfdata(matched->intf);
+ if ((simple->connect && 0==simple->connect(hid)) || !simple->connect) {
+ simple->intf = matched->intf;
+ hid->simple = simple;
+ spin_lock(&simple_lock);
+ list_del(&simple->node);
+ spin_unlock(&simple_lock);
+ hidinput_simple_device_setup_usage(hid);
+ printk(KERN_INFO"The simple HID device \'%s\' attatch on \'%s\'\n", simple->name, hid->name);
+ }
+}
+
+static void
+hidinput_simple_device_bind(struct usb_interface *intf)
+{
+ struct hidinput_simple_device *simple;
+ struct list_head *node;
+ struct hid_device *hid;
+
+ if (!intf)
+ return;
+
+ simple = 0;
+ spin_lock(&simple_lock);
+ list_for_each(node, &simple_devices_list) {
+ simple = list_entry(node, struct hidinput_simple_device, node);
+ if (usb_match_id(intf, simple->id))
+ break;
+ simple = 0;
+ }
+ spin_unlock(&simple_lock);
+ /* no such simple device match this hid_device, also ok */
+ if (0 == simple)
+ return;
+
+ hid = usb_get_intfdata (intf);
+ if ((simple->connect && 0==simple->connect(hid)) || !simple->connect) {
+ simple->intf = intf;
+ hid->simple = simple;
+ spin_lock(&simple_lock);
+ list_del(&simple->node);
+ spin_unlock(&simple_lock);
+ hidinput_simple_device_setup_usage(hid);
+ printk(KERN_INFO"The simple HID device \'%s\' attatch on \'%s\'\n", simple->name, hid->name);
+ }
+}
+
+int
+hidinput_register_simple_device(struct hidinput_simple_device *simple)
+{
+ struct list_head *node;
+ struct matched_device *matched;
+
+ if (!simple || !simple->name || simple->intf)
+ return -1;
+
+ simple->flags = 0;
+ matched = 0;
+
+ spin_lock(&matched_lock);
+ list_for_each(node, &matched_devices_list) {
+ matched = list_entry(node, struct matched_device, node);
+ if (usb_match_id(matched->intf, simple->id))
+ break;
+ matched = 0;
+ }
+ spin_unlock(&matched_lock);
+
+ if (matched) {/* We called hid_probe() on this usb_interface ago */
+ struct hid_device *hid;
+
+ hid = usb_get_intfdata (matched->intf);
+ if (hid->simple)
+ goto device_busy;
+ if ((simple->connect && 0==simple->connect(hid)) || !simple->connect) {
+ simple->intf = matched->intf;
+ hid->simple = simple;
+ hidinput_simple_device_setup_usage(hid);
+ printk(KERN_INFO"The simple HID device \'%s\' attatch on \'%s\'\n", simple->name, hid->name);
+ return 0;
+ }
+ }
+ return 0;
+
+ device_busy:
+ spin_lock(&simple_lock);
+ list_add(&simple->node, &simple_devices_list);
+ spin_unlock(&simple_lock);
+ return 1;
+}
+
+EXPORT_SYMBOL(hidinput_register_simple_device);
+
+void
+hidinput_unregister_simple_device(struct hidinput_simple_device *simple)
+{
+ if (simple->intf) {
+ struct hid_device *hid;
+ hid = usb_get_intfdata (simple->intf);
+
+ if (hid->simple != simple)
+ printk(KERN_ERR"failed to check simple device for consistency at %s %d \n", __FUNCTION__, __LINE__);
+ hidinput_simple_device_clear_usage(hid);
+ if (simple->disconnect)
+ simple->disconnect(hid);
+ hid->simple = 0;
+ simple->intf = 0;
+ simple->flags = 0;
+ } else {
+ spin_lock(&simple_lock);
+ list_del(&simple->node);
+ spin_unlock(&simple_lock);
+ }
+ /* to active simple device that matched current HID device is waiting */
+ hidinput_simple_device_bind_foreach();
+ printk(KERN_INFO"The simple HID device \'%s\' unregistered.\n", simple->name);
+}
+
+EXPORT_SYMBOL(hidinput_unregister_simple_device);
+
static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct hid_device *hid;
char path[64];
int i;
char *c;
-
+ struct matched_device *matched;
+
dbg("HID probe called for ifnum %d",
intf->altsetting->desc.bInterfaceNumber);
@@ -1858,14 +2055,23 @@ 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)) {
+ matched = kmalloc(sizeof(struct matched_device), GFP_KERNEL);
+ if (matched) {
+ matched->intf = intf;
+ spin_lock(&matched_lock);
+ list_add(&matched->node, &matched_devices_list);
+ spin_unlock(&matched_lock);
+ hidinput_simple_device_bind(intf);
+ }
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");
hid_disconnect(intf);
@@ -1945,6 +2151,12 @@ static int __init hid_init(void)
retval = hiddev_init();
if (retval)
goto hiddev_init_fail;
+
+ spin_lock_init(&matched_lock);
+ spin_lock_init(&simple_lock);
+ INIT_LIST_HEAD(&matched_devices_list);
+ INIT_LIST_HEAD(&simple_devices_list);
+
retval = usb_register(&hid_driver);
if (retval)
goto usb_register_fail;
diff -Naurp linux-2.6.15.orig/drivers/usb/input/hid.h linux-2.6.15/drivers/usb/input/hid.h
--- linux-2.6.15.orig/drivers/usb/input/hid.h 2006-01-03 11:21:10.000000000 +0800
+++ linux-2.6.15/drivers/usb/input/hid.h 2006-01-04 10:17:10.000000000 +0800
@@ -6,6 +6,7 @@
*
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2001 Vojtech Pavlik
+ * Copyright (c) 2005 Liyu To support simple HID device.
*/
/*
@@ -374,6 +375,8 @@ struct hid_input {
struct input_dev *input;
};
+struct hidinput_simple_device;
+
struct hid_device { /* device report descriptor */
__u8 *rdesc;
unsigned rsize;
@@ -431,6 +434,7 @@ struct hid_device { /* device repo
void (*ff_exit)(struct hid_device*); /* Called by hid_exit_ff(hid) */
int (*ff_event)(struct hid_device *hid, struct input_dev *input,
unsigned int type, unsigned int code, int value);
+ struct hidinput_simple_device *simple;
};
#define HID_GLOBAL_STACK_SIZE 4
@@ -471,7 +475,6 @@ struct hid_descriptor {
#define resolv_event(a,b) do { } while (0)
#endif
-#endif
#ifdef CONFIG_USB_HIDINPUT
/* Applications from HID Usage Tables 4/8/99 Version 1.1 */
@@ -515,3 +518,5 @@ static inline int hid_ff_event(struct hi
return hid->ff_event(hid, input, type, code, value);
return -ENOSYS;
}
+
+#endif
diff -Naurp linux-2.6.15.orig/drivers/usb/input/hid-input.c linux-2.6.15/drivers/usb/input/hid-input.c
--- linux-2.6.15.orig/drivers/usb/input/hid-input.c 2006-01-03 11:21:10.000000000 +0800
+++ linux-2.6.15/drivers/usb/input/hid-input.c 2006-01-04 10:17:11.000000000 +0800
@@ -36,6 +36,7 @@
#undef DEBUG
#include "hid.h"
+#include "hid-simple.h"
#define unk KEY_UNKNOWN
@@ -676,6 +677,49 @@ int hidinput_connect(struct hid_device *
return 0;
}
+/*
+ * To give one simple device a configure usage chance.
+ * The most code of this function is copied from hidinput_connect()
+ */
+void hidinput_simple_device_configure_usage(struct hid_device *hid)
+{
+ struct hid_report *report;
+ int i, j, k;
+ void (*do_usage)(struct hid_field *, struct hid_usage *);
+
+ if (!hid->simple)
+ return;
+ do_usage = 0;
+ if (hid->simple->flags & HIDINPUT_SIMPLE_SETUP_USAGE)
+ do_usage = hid->simple->setup_usage;
+ else
+ do_usage = hid->simple->clear_usage;
+ if (!do_usage)
+ return;
+
+ 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++)
+ do_usage(report->field[i], report->field[i]->usage + j);
+ }
+
+ return;
+}
+
void hidinput_disconnect(struct hid_device *hid)
{
struct hid_input *hidinput, *next;
diff -Naurp linux-2.6.15.orig/drivers/usb/input/hid-simple.h linux-2.6.15/drivers/usb/input/hid-simple.h
--- linux-2.6.15.orig/drivers/usb/input/hid-simple.h 1970-01-01 08:00:00.000000000 +0800
+++ linux-2.6.15/drivers/usb/input/hid-simple.h 2006-01-04 10:17:11.000000000 +0800
@@ -0,0 +1,62 @@
+#ifndef __HID_SIMPLE_H
+#define __HID_SIMPLE_H
+
+#include <linux/usb.h>
+#include "hid.h"
+
+/************ The private section for simple device implement only **************/
+
+/* 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;
+};
+
+/* simple device internal flags */
+#define HIDINPUT_SIMPLE_SETUP_USAGE 0x1 /* the reverse is to call clear_usage */
+
+#define hidinput_simple_device_setup_usage(hid) \
+do {\
+ if (hid->simple) {\
+ hid->simple->flags |= HIDINPUT_SIMPLE_SETUP_USAGE; \
+ hidinput_simple_device_configure_usage(hid); \
+ }\
+} while (0)
+
+#define hidinput_simple_device_clear_usage(hid) \
+do {\
+ if (hid->simple) {\
+ hid->simple->flags &= (~HIDINPUT_SIMPLE_SETUP_USAGE); \
+ hidinput_simple_device_configure_usage(hid); \
+ }\
+} while (0)
+
+/* It is defined at hid_input.c, however is called at hid-core.c */
+void hidinput_simple_device_configure_usage(struct hid_device *hid);
+
+/******************** The private section end. *****************************/
+
+
+/********************* The public interface for simple device driver ***********/
+struct hidinput_simple_device {
+/* private */
+ struct list_head node;
+ struct usb_interface *intf;
+ int flags;
+/* public */
+ char *name;
+ int (*connect)(struct hid_device *);
+ void (*setup_usage)(struct hid_field *, struct hid_usage *);
+ void (*event)(const struct hid_device *, const struct hid_field *, const struct hid_usage *, const __s32, const struct pt_regs *regs);
+ void (*clear_usage)(struct hid_field *, struct hid_usage *);
+ void (*disconnect)(struct hid_device *);
+ struct usb_device_id id[2]; /* variable length member */
+};
+
+int hidinput_register_simple_device(struct hidinput_simple_device *device);
+void hidinput_unregister_simple_device(struct hidinput_simple_device *device);
+/********************* The public section end ***********/
+
+#endif /* __HID_SIMPLE_H */
[PATCH] usb/input: Microsoft Natural Ergonomic Keyboard 4000 Driver
=====================
Microsoft Natural Ergonomic Keyboard 4000 Driver
=====================
Date:
------------------
20050104
Source code base
------------------
2.6.15
Testing:
------------------
Tested on i386.
Other Tips
------------------
This driver use "HID device simple driver interface", so you need that patch first, you can find it in my last email.
The zoom-in handler is mapped to KEY_F13
The zoom-out handler is mapped to KEY_F14
The custom key #1 is mapped to KEY_FN_F1
The custom key #2 is mapped to KEY_FN_F2
The custom key #3 is mapped to KEY_FN_F3
The custom key #4 is mapped to KEY_FN_F4
The custom key #5 is mapped to KEY_FN_F5
Does such arrange suitable?
Signed-off-by: Liyu <[email protected]>
diff -Naurp linux-2.6.15.orig/drivers/usb/input/Kconfig linux-2.6.15/drivers/usb/input/Kconfig
--- linux-2.6.15.orig/drivers/usb/input/Kconfig 2006-01-03 11:21:10.000000000 +0800
+++ linux-2.6.15/drivers/usb/input/Kconfig 2006-01-04 16:30:44.000000000 +0800
@@ -306,3 +306,10 @@ 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
+ The driver of Microsoft Natural Ergonomic Keyboard 4000.
+
diff -Naurp linux-2.6.15.orig/drivers/usb/input/Makefile linux-2.6.15/drivers/usb/input/Makefile
--- linux-2.6.15.orig/drivers/usb/input/Makefile 2006-01-03 11:21:10.000000000 +0800
+++ linux-2.6.15/drivers/usb/input/Makefile 2006-01-04 16:29:59.000000000 +0800
@@ -42,6 +42,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 linux-2.6.15.orig/drivers/usb/input/usbnek4k.c linux-2.6.15/drivers/usb/input/usbnek4k.c
--- linux-2.6.15.orig/drivers/usb/input/usbnek4k.c 1970-01-01 08:00:00.000000000 +0800
+++ linux-2.6.15/drivers/usb/input/usbnek4k.c 2006-01-04 16:47:42.000000000 +0800
@@ -0,0 +1,328 @@
+/*
+ * Microsoft Natural Ergonomic Keyboard 4000 Driver
+ *
+ * Copyright (c) 2005 Liyu <[email protected]> The initial version.
+ */
+
+/*
+ * 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/version.h>
+#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 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 DRIVER_DESC "Microsoft Natural Ergonomic Keyboard 4000 driver"
+#define DRIVER_VERSION "0.1.0"
+
+#define MSNEK4K_ID_VENDOR 0x045e
+#define MSNEK4K_ID_PRODUCT 0x00db
+
+struct nek4k_device {
+ struct hidinput_simple_device device;
+};
+
+static struct usb_device_id __id[] = {
+ {
+ USB_DEVICE(MSNEK4K_ID_VENDOR, MSNEK4K_ID_PRODUCT)
+ },
+ {0, }
+};
+
+MODULE_DEVICE_TABLE(usb, __id);
+
+static char device_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;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15))
+ struct input_dev *input = hidinput->input;
+#else
+ struct input_dev *input = &hidinput->input;
+#endif
+
+ 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) {
+ /*
+ * These are custom feature key. they can not be
+ * distinguished by HID usage, nek4k_hid_event() handle
+ * them.
+ */
+ map_key(KEY_VENDOR);
+ set_bit(KEY_FN_F1,input->keybit);
+ set_bit(KEY_FN_F2,input->keybit);
+ set_bit(KEY_FN_F3,input->keybit);
+ set_bit(KEY_FN_F4,input->keybit);
+ set_bit(KEY_FN_F5,input->keybit);
+ return;
+ }
+ } else if ((usage->hid & HID_USAGE_PAGE) == HID_UP_KEYBOARD)
+ return;
+ else
+ printk(KERN_ERR"Unknown usage page 0x%x in %s:%d\n", usage->hid & HID_USAGE_PAGE, __FUNCTION__, __LINE__);
+
+}
+
+static void nek4k_clear_usage(struct hid_field *field, struct hid_usage *usage)
+{
+ struct hid_input *hidinput = field->hidinput;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15))
+ struct input_dev *input = hidinput->input;
+#else
+ struct input_dev *input = &hidinput->input;
+#endif
+
+ 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_key(KEY_VENDOR);
+ 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) {
+ return;
+ } else
+ printk(KERN_ERR"Unknown usage page 0x%x in %s:%d\n", usage->hid & HID_USAGE_PAGE, __FUNCTION__, __LINE__);
+
+}
+
+static void
+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;
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15))
+ struct input_dev *input = hidinput->input;
+#else
+ struct input_dev *input = &hidinput->input;
+#endif
+ int code;
+
+ if ( (usage->hid&HID_USAGE_PAGE)!=HID_UP_MSVENDOR ||
+ (usage->hid&HID_USAGE)!=USAGE_CUSTOM )
+ return;
+
+ switch (value) {
+ case 0x0:
+ return;
+ case 0x1:
+ code = KEY_FN_F1;
+ break;
+ case 0x2:
+ code = KEY_FN_F2;
+ break;
+ case 0x4:
+ code = KEY_FN_F3;
+ break;
+ case 0x8:
+ code = KEY_FN_F4;
+ break;
+ case 0x10:
+ code = KEY_FN_F5;
+ break;
+ default:
+ printk(KERN_ERR"Unknown hid event value 0x%x in %s:%d\n", value, __FUNCTION__, __LINE__);
+ return;
+ };
+
+ input_event(input, EV_KEY, code, 1);
+ input_event(input, EV_KEY, code, 0);
+ input_sync(input);
+}
+
+static struct nek4k_device nek4k_device = {
+ .device = {
+ .name = device_name,
+ .setup_usage = nek4k_setup_usage,
+ .clear_usage = nek4k_clear_usage,
+ .event = nek4k_hid_event,
+ .id = {
+ { USB_DEVICE(MSNEK4K_ID_VENDOR, MSNEK4K_ID_PRODUCT) }, {}
+ }
+ }
+};
+
+static int __init nek4k_init(void)
+{
+ int result = hidinput_register_simple_device(&nek4k_device.device);
+
+ if (result == 1) {
+ /*
+ * The device that matched is busy, this can see as one error,
+ * but we bypass it in this nek4k.
+ */
+ printk("hid device busy\n");
+ }
+ if (result >= 0)
+ info(DRIVER_DESC ":" DRIVER_VERSION);
+ else
+ return -ENODEV;
+ return 0;
+}
+
+static void __exit nek4k_exit(void)
+{
+ hidinput_unregister_simple_device(&nek4k_device.device);
+}
+
+module_init(nek4k_init);
+module_exit(nek4k_exit);
+
+MODULE_LICENSE("GPL");
+
#include <linux/kernel.h>
#include <linux/input.h>
#include "hid-simple.h"
#define DRIVER_DESC "HID simple device example #1"
#define DRIVER_VERSION "0.1.0"
/* For BETOP BTP-C033 joystick (A popluar product in China market) */
#define SAMPLE_ID_VENDOR 0x0e8f
#define SAMPLE_ID_PRODUCT 0x0003
struct sample_device {
struct hidinput_simple_device device;
};
static struct usb_device_id __id[] = {
{
USB_DEVICE(SAMPLE_ID_VENDOR, SAMPLE_ID_PRODUCT)
},
{0, }
};
MODULE_DEVICE_TABLE(usb, __id);
static char device_name[] = "The sample of HID simple device #1";
static void sample_setup_usage(struct hid_field *field, struct hid_usage *usage)
{
struct input_dev *input = &field->hidinput->input;
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON)
set_bit(EV_REP, input->evbit);
}
static void sample_clear_usage(struct hid_field *field, struct hid_usage *usage)
{
struct input_dev *input = &field->hidinput->input;
if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON)
clear_bit(EV_REP, input->evbit);
}
static struct sample_device sample_device = {
.device = {
.name = device_name,
.setup_usage = sample_setup_usage,
.clear_usage = sample_clear_usage,
.id = {
{ USB_DEVICE(SAMPLE_ID_VENDOR, SAMPLE_ID_PRODUCT) }, {}
}
}
};
static int __init sample_init(void)
{
int result = hidinput_register_simple_device(&sample_device.device);
if (result == 1) {
/*
* The device that matched is busy, this can see as one error,
* but we bypass it in this sample.
*/
printk("hid device busy\n");
}
if (result >= 0)
info(DRIVER_DESC ":" DRIVER_VERSION);
return 0;
}
static void __exit sample_exit(void)
{
hidinput_unregister_simple_device(&sample_device.device);
}
module_init(sample_init);
module_exit(sample_exit);
MODULE_LICENSE("GPL");
/* The sample showed, the sample use simple interface is simple :) */
[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]