[PATCH] hid: hid bus prototype 20070416 in splited patches.

Li Yu <[email protected]>
Newsgroups gmane.linux.usb.devel,gmane.linux.kernel
Message-ID <[email protected]>
Hi, 

	There are the HID bus prototype 20070416 in splited patch form in the attachment.

	I am sorry there are in the attachment, if not so, their indentation will be mangling by thunderbird.

	These patches should be can apply on 2.6.21-rc6-mm1.

	Good luck.

- Li Yu

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
hidbus.core.patch (text/x-patch, 37.8 KB)
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/hid-al.c linux-2.6.21-rc6-mm1.new/drivers/hid/hid-al.c
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/hid-al.c	1970-01-01 08:00:00.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/hid-al.c	2007-04-15 21:34:44.000000000 +0800
@@ -0,0 +1,505 @@
+/*
+ *  HID abstract layer for Linux driver model
+ *
+ *  Copyright (c) 2007 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 <linux/device.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
+#include <linux/wait.h>
+
+#include "hid-inter.h"
+
+static spinlock_t hid_lock;
+
+struct hid_transport* hid_transports[BUS_MAX];
+static unsigned long hid_transports_exiting[BUS_MAX];
+#define set_hid_transport_exiting(tl)	(set_bit(1, hid_transports_exiting+(tl)->bus))
+#define is_hid_transport_exiting(tl)	(test_bit(1, hid_transports_exiting+(tl)->bus))
+#define clear_hid_transport_exiting(tl)	(clear_bit(1, hid_transports_exiting+(tl)->bus))
+EXPORT_SYMBOL(hid_transports);
+
+
+struct hid_driver* hid_sticky_drivers[NR_STICKY];
+static DECLARE_WAIT_QUEUE_HEAD(hid_sticky_clean_barrier);
+static unsigned long hid_sticky_exiting[NR_STICKY];
+#define set_hid_sticky_exiting(i)	(set_bit(1, hid_sticky_exiting+i))
+#define is_hid_sticky_exiting(i)	(test_bit(1, hid_sticky_exiting+i))
+#define clear_hid_sticky_exiting(i)	(clear_bit(1, hid_sticky_exiting+i))
+
+static int hid_sticky_get(struct hid_driver *hid_drv)
+{
+	return try_module_get(hid_drv->module);
+}
+
+static void hid_sticky_put(struct hid_driver *hid_drv)
+{
+	module_put(hid_drv->module);
+	if(!module_refcount(hid_drv->module))
+		wake_up(&hid_sticky_clean_barrier);
+}
+
+int hid_sticky_driver_exit(int i)
+{
+	if (is_hid_sticky_exiting(i)) {
+		hid_sticky_put(hid_sticky_drivers[i]);
+		return 1;
+	}
+	return 0;
+}
+
+static int hid_driver_skip_device_helper(struct device *dev, void *data)
+{
+	struct skip_id *skip = data;
+	int mask = skip->mask;
+	struct hid_device *hid_dev = to_hid_device(dev);
+	
+	if ((mask & HID_IGNORE_VENDOR) &&
+				(hid_dev->vendor != skip->vendor))
+		return 0;
+	if ((mask & HID_IGNORE_PRODUCT) && 
+				(hid_dev->product != skip->product))
+		return 0;
+	if ((mask & HID_IGNORE_VERSION) && 
+				(hid_dev->version != skip->version))
+		return 0;
+	if ((mask & HID_IGNORE_COUNTRY) &&
+				(hid_dev->country != skip->country))
+		return 0;
+
+	return 1;
+}
+
+static int hid_driver_match_skip_table(struct hid_driver *hid_drv,
+						struct hid_device *hid_dev)
+{
+	struct skip_id *skip;
+
+	list_for_each_entry(skip, &hid_drv->skip_table, node) {
+		if (hid_driver_skip_device_helper(&hid_dev->device, skip))
+			return 1;
+	}
+	return 0;
+}
+static int hid_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct hid_driver *hid_drv;
+	struct hid_device *hid_dev;
+	
+	hid_drv = to_hid_driver(drv);
+	hid_dev = to_hid_device(dev);
+
+	if (is_hid_driver_sticky(hid_drv))
+		/* the sticky driver match device do not pass here. */
+		return 0;
+	if (hid_dev->bus != hid_drv->bus)
+		return 0;
+	if (hid_driver_match_skip_table(hid_drv, hid_dev))
+		return 0;
+	if (!hid_drv->match || hid_drv->match(hid_drv, hid_dev)) {
+		hid_dev->driver = hid_drv;
+		return 1;
+	}
+	return 0;
+}
+
+static ssize_t hid_driver_show_version(struct device_driver *drv, char *buf)
+{
+	struct hid_driver *hid_drv;
+
+	hid_drv = to_hid_driver(drv);
+	return snprintf(buf, PAGE_SIZE, "%s\n", hid_drv->version);
+}
+
+static ssize_t hid_driver_show_sticky(struct device_driver *drv, char *buf)
+{
+	struct hid_driver *hid_drv;
+
+	hid_drv = to_hid_driver(drv);
+	return snprintf(buf, PAGE_SIZE, "%d\n", is_hid_driver_sticky(hid_drv));
+}
+
+static ssize_t hid_driver_show_skip(struct device_driver *drv, char *buf)
+{
+	/* FIXME: we really need this? */
+	return snprintf(buf, PAGE_SIZE, "\n");
+}
+
+static void hid_driver_skip_device(struct hid_driver *hid_drv,
+					struct skip_id *skip)
+{
+	struct device *dev;
+	
+	do {
+		dev = driver_find_device(&hid_drv->driver, NULL,
+				skip, hid_driver_skip_device_helper);
+		if (!dev)
+			return;
+		device_release_driver(dev);
+	} while (1);
+}
+
+static ssize_t hid_driver_store_skip(struct device_driver *drv,
+						const char *buf, size_t count)
+{
+	struct hid_driver *hid_drv;
+	int vendor = 0, product = 0, version = 0, country = 0, mask = 0;
+	struct skip_id *skip;
+	int n;
+	
+	hid_drv = to_hid_driver(drv);
+	n = sscanf(buf, "skip:0x%x,0x%x,0x%x,0x%x,0x%x\n",
+		&vendor, &product, &version, &country, &mask);
+	if (n<2)
+		return -EINVAL;
+	skip = kzalloc(sizeof(struct skip_id), GFP_KERNEL);
+	if (!skip)
+		return -ENOMEM;
+	if (!mask)
+		mask = HID_IGNORE_VENDOR|HID_IGNORE_PRODUCT;
+	skip->vendor = vendor;
+	skip->product = product;
+	skip->version = version;
+	skip->country = country;
+	skip->mask = mask;
+	spin_lock(&hid_lock);
+	list_add_tail(&skip->node, &hid_drv->skip_table);
+	spin_unlock(&hid_lock);
+	hid_driver_skip_device(hid_drv, skip);
+	return count;
+}
+
+static struct driver_attribute hidbus_driver_attrs[] = {
+	__ATTR(version, 0444, hid_driver_show_version, NULL),
+	__ATTR(sticky, 0444, hid_driver_show_sticky, NULL),
+	__ATTR(skip_id, 0644, hid_driver_show_skip, hid_driver_store_skip),
+	__ATTR_NULL
+};
+
+struct bus_type hid_bus_type = {
+	.name = "hid",
+	.match = hid_bus_match,
+	.drv_attrs = hidbus_driver_attrs,
+};
+
+static void hid_dummy_release(struct device *dev)
+{
+}
+
+struct device hid_bus = {
+	.bus_id = "hidbus0",
+	.release = hid_dummy_release
+};
+
+static void hid_device_create_links(struct hid_device *hid_dev)
+{
+	struct hid_input *hidinput;
+	int ret;
+	
+	list_for_each_entry(hidinput, &hid_dev->inputs, list)
+		ret = sysfs_create_link(&hid_dev->device.kobj,
+					&hidinput->input->dev.kobj,
+					hidinput->input->dev.bus_id);
+	ret = sysfs_create_link(&hid_dev->device.kobj,
+				&hid_dev->dev->kobj, "transport_device");
+}
+
+static void hid_device_remove_links(struct hid_device *hid_dev)
+{
+	struct hid_input *hidinput;
+	
+	list_for_each_entry(hidinput, &hid_dev->inputs, list)
+		sysfs_remove_link(&hid_dev->device.kobj,
+					hidinput->input->dev.bus_id);
+
+	sysfs_remove_link(&hid_dev->device.kobj, "transport_device");
+}
+
+static int hid_drv_probe(struct device *dev)
+{
+	struct hid_device *hid_dev;
+	struct hid_driver *hid_drv;
+	struct hid_transport *tl;
+	int i;
+	
+	hid_dev = to_hid_device(dev);
+	hid_drv = hid_dev->driver;
+	tl = hid_transports[hid_dev->bus];
+	
+	if (is_hid_transport_exiting(tl))
+		return -ENODEV;
+	if (!try_module_get(tl->module))
+		return -ENODEV;
+
+	if (!hid_drv->probe)
+		if (!hidinput_connect(hid_dev))
+			goto ok;
+	if (hid_drv->probe(hid_dev)) {
+		hid_dev->driver = NULL;
+		return -ENODEV;
+	}
+ok:
+	for (i=0; i<NR_STICKY; ++i) {
+		hid_drv = hid_sticky_drivers[i];
+		if (!hid_drv || is_hid_sticky_exiting(i))
+			continue;
+		if (!hid_sticky_get(hid_drv))
+			continue;
+		if (!try_module_get(tl->module)) {
+			hid_sticky_put(hid_drv);
+			continue;
+		}
+		if (!hid_drv->probe)
+			set_bit(i, &hid_dev->sticky_driver);
+		else if (!hid_drv->probe(hid_dev))
+			set_bit(i, &hid_dev->sticky_driver);
+		else {
+			clear_bit(i, &hid_dev->sticky_driver);
+			hid_sticky_put(hid_drv);
+			module_put(tl->module);
+		}
+	}
+	hid_device_create_links(hid_dev);
+	return 0;
+}
+
+static int hid_drv_remove(struct device *dev)
+{
+	struct hid_device *hid_dev;
+	struct hid_driver *hid_drv;
+	struct hid_transport *tl;
+	int i;
+	
+	hid_dev = to_hid_device(dev);
+	hid_drv = hid_dev->driver;
+	tl = hid_transports[hid_dev->bus];
+	
+	hid_device_remove_links(hid_dev);
+	module_put(tl->module);
+	if (!hid_drv->remove)
+		hidinput_disconnect(hid_dev);
+	else
+		hid_drv->remove(hid_dev);
+		
+	for (i=0; i<NR_STICKY; ++i) {
+		hid_drv = hid_sticky_drivers[i];
+		if (!hid_drv)
+			continue;
+		if (hid_drv->remove)
+			hid_drv->remove(hid_dev);
+		clear_bit(i, &hid_dev->sticky_driver);
+		hid_sticky_put(hid_drv);
+		module_put(tl->module);
+	}
+	return 0;
+}
+
+static void hid_drv_shutdown(struct device *dev)
+{
+	struct hid_device *hid_dev;
+
+	hid_dev = to_hid_device(dev);
+	
+	if (!hid_dev->driver);
+		return;
+	if (hid_dev->driver->shutdown)
+		hid_dev->driver->shutdown(hid_dev);
+}
+
+static inline int hid_check_transport(int bus)
+{
+	if (bus<=0 || bus>=BUS_MAX)
+		return 0;
+	
+	return (int)hid_transports[bus];
+}
+
+static int match_device_for_transport(struct device *dev, void *bus)
+{
+	struct hid_device *hid_dev = to_hid_device(dev);
+	
+	if (hid_dev->bus != (int)bus)
+		return 0;
+	device_release_driver(dev);
+	return 0;
+}
+
+static void unbind_devices_for_transport(int bus)
+{
+	struct device *dev;
+	
+restart:
+	dev = bus_find_device(&hid_bus_type, NULL,
+				(void*)bus, match_device_for_transport);
+	if (!dev)
+		return;
+	goto restart;
+}
+
+int hid_register_device(struct hid_device *hid_dev)
+{
+	if (!hid_dev || !hid_check_transport(hid_dev->bus))
+		return -EINVAL;
+		
+	hid_dev->device.bus = &hid_bus_type;
+	hid_dev->device.release = hid_dummy_release;
+	hid_dev->device.parent = &hid_bus;
+	INIT_LIST_HEAD(&hid_dev->inputs);
+	hid_transports[hid_dev->bus]->update_busid(hid_dev);
+	return device_register(&hid_dev->device);
+}
+EXPORT_SYMBOL_GPL(hid_register_device);
+
+void hid_unregister_device(struct hid_device *hid_dev)
+{
+	device_unregister(&hid_dev->device);
+}
+EXPORT_SYMBOL_GPL(hid_unregister_device);
+
+int hid_register_driver(struct hid_driver *hid_drv)
+{
+	int ret, i;
+	
+	if (!hid_drv)
+		return -EINVAL;
+	if (!hid_check_transport(hid_drv->bus))
+		return -EINVAL;
+	if (!try_module_get(hid_drv->module))
+		return -EINVAL;
+	hid_drv->driver.bus = &hid_bus_type;
+	hid_drv->driver.name = hid_drv->name;
+	hid_drv->driver.probe = hid_drv_probe;
+	hid_drv->driver.remove = hid_drv_remove;
+	hid_drv->driver.shutdown = hid_drv_shutdown;
+	INIT_LIST_HEAD(&hid_drv->skip_table);
+	ret = driver_register(&hid_drv->driver);
+	if (!is_hid_driver_sticky(hid_drv))
+		return ret;
+	spin_lock(&hid_lock);
+	for (i=0; i<NR_STICKY; ++i) {
+		if (!hid_sticky_drivers[i]) {
+			clear_hid_sticky_exiting(i);
+			hid_sticky_drivers[i] = hid_drv;
+			break;
+		}
+	}
+	spin_unlock(&hid_lock);
+	if (i<NR_STICKY)
+		return 0;
+	module_put(hid_drv->module);
+	driver_unregister(&hid_drv->driver);
+	return -EBUSY;
+}
+EXPORT_SYMBOL_GPL(hid_register_driver);
+
+void hid_unregister_driver(struct hid_driver *hid_drv)
+{
+	struct skip_id *skip, *next;
+	int i;
+	
+	if (!is_hid_driver_sticky(hid_drv))
+		goto skip_sticky;
+	for (i=0; i<NR_STICKY; ++i)
+		if (hid_drv == hid_sticky_drivers[i]) {
+			set_hid_sticky_exiting(i);
+			break;
+		}
+	if (i>=NR_STICKY)
+		return;
+	module_put(hid_drv->module);
+	wait_event(hid_sticky_clean_barrier, !module_refcount(hid_drv->module));
+	spin_lock(&hid_lock);
+	hid_sticky_drivers[i] = NULL;
+	spin_unlock(&hid_lock);
+
+skip_sticky:
+	driver_unregister(&hid_drv->driver);
+	list_for_each_entry_safe(skip, next, &hid_drv->skip_table, node) {
+		list_del(&skip->node);
+		kfree(skip);
+	}
+}
+EXPORT_SYMBOL_GPL(hid_unregister_driver);
+
+int hid_register_transport(struct hid_transport *tl)
+{
+	int ret;
+
+	if (!tl || tl->bus>=BUS_MAX || tl->bus<=0)
+		return -EINVAL;
+	if (!try_module_get(tl->module))
+		return -EINVAL;
+	ret = 0;
+	spin_lock(&hid_lock);
+	if (!hid_transports[tl->bus]) {
+		hid_transports[tl->bus] = tl;
+		clear_hid_transport_exiting(tl);
+	}
+	else
+		ret = -EBUSY;
+	spin_unlock(&hid_lock);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(hid_register_transport);
+
+void hid_unregister_transport(struct hid_transport *tl)
+{
+	if (!tl || tl->bus>=BUS_MAX || tl->bus<=0 || tl != hid_transports[tl->bus])
+		return;
+	set_hid_transport_exiting(tl);
+	module_put(tl->module);
+	unbind_devices_for_transport(tl->bus);
+	spin_lock(&hid_lock);
+	hid_transports[tl->bus] = NULL;
+	spin_unlock(&hid_lock);
+	return;
+}
+EXPORT_SYMBOL_GPL(hid_unregister_transport);
+
+static ssize_t hid_bus_show_version(struct bus_type *bus, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0.1.0\n");
+}
+
+BUS_ATTR(version, 0444, hid_bus_show_version, NULL);
+
+static int __init hid_init(void)
+{
+	int ret;
+	
+	spin_lock_init(&hid_lock);
+	ret = device_register(&hid_bus);
+	if (ret)
+		return ret;
+	ret = bus_register(&hid_bus_type);
+	if (ret) {
+		device_unregister(&hid_bus);
+		return ret;
+	}
+	ret = bus_create_file(&hid_bus_type, &bus_attr_version);
+	return 0;
+}
+
+static void __exit hid_exit(void)
+{
+	bus_remove_file(&hid_bus_type, &bus_attr_version);
+	device_unregister(&hid_bus);
+	bus_unregister(&hid_bus_type);
+}
+
+module_init(hid_init);
+module_exit(hid_exit);
+MODULE_LICENSE("GPL");
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/hid-core.c linux-2.6.21-rc6-mm1.new/drivers/hid/hid-core.c
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/hid-core.c	2007-04-10 09:16:46.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/hid-core.c	2007-04-11 09:12:02.000000000 +0800
@@ -28,9 +28,8 @@
 #include <linux/vmalloc.h>
 
 #include <linux/hid.h>
-#include <linux/hiddev.h>
 #include <linux/hid-debug.h>
-#include <linux/hidraw.h>
+#include "hid-inter.h"
 
 /*
  * Version Information
@@ -39,7 +38,6 @@
 #define DRIVER_VERSION "v2.6"
 #define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik"
 #define DRIVER_DESC "HID core driver"
-#define DRIVER_LICENSE "GPL"
 
 /*
  * Register a new report for a device.
@@ -634,7 +632,7 @@ struct hid_device *hid_parse_report(__u8
 		hid_parser_reserved
 	};
 
-	if (!(device = kzalloc(sizeof(struct hid_device), GFP_KERNEL)))
+	if (!(device = hid_new_device(GFP_KERNEL)))
 		return NULL;
 
 	if (!(device->collection = kzalloc(sizeof(struct hid_collection) *
@@ -802,11 +800,17 @@ 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 hid_hook *hook;
+
+	if (!hid->driver)
+		return;
+		
+	hook = hid->driver->hook;	
 	hid_dump_input(usage, value);
-	if (hid->claimed & HID_CLAIMED_INPUT)
-		hidinput_hid_event(hid, field, usage, value);
-	if (hid->claimed & HID_CLAIMED_HIDDEV && interrupt && hid->hiddev_hid_event)
-		hid->hiddev_hid_event(hid, field, usage, value);
+	if (hook && hook->hid_event && 
+			!hook->hid_event(field, usage, value, interrupt))
+		return;
+	hidinput_hid_event(hid, field, usage, value);
 }
 
 /*
@@ -825,11 +829,13 @@ void hid_input_field(struct hid_device *
 	__s32 max = field->logical_maximum;
 	__s32 *value;
 
+	if (!hid->driver)
+		return;
+
 	if (!(value = kmalloc(sizeof(__s32) * count, GFP_ATOMIC)))
 		return;
 
 	for (n = 0; n < count; n++) {
-
 			value[n] = min < 0 ? snto32(extract(data, offset + n * size, size), size) :
 						    extract(data, offset + n * size, size);
 
@@ -931,14 +937,110 @@ int hid_set_field(struct hid_field *fiel
 }
 EXPORT_SYMBOL_GPL(hid_set_field);
 
+static int hid_raw_event(struct hid_device *hid, int type, u8 *data,
+							int size, int interrupt)
+{
+	struct hid_hook *hook;
+	int ret, i;
+	
+	if (!hid->driver)
+		return 0;
+
+	hook = hid->driver->hook;
+	if (hook && hook->raw_event) {
+		if (!hook->raw_event(hid, type, data, size, interrupt))
+			return 0;
+	}
+	
+	ret = -ENODEV;
+	for (i=0; i<NR_STICKY; ++i) {
+		if (!hid_sticky_drivers[i] || !test_bit(i, &hid->sticky_driver))
+			continue;
+		if (hid_sticky_driver_exit(i)) {
+			clear_bit(i, &hid->sticky_driver);
+			continue;
+		}
+		hook = hid_sticky_drivers[i]->hook;
+		if (hook && hook->raw_event) {
+			ret = hook->raw_event(hid, type, data, size, interrupt);
+			if (!ret)
+				break;
+		}
+	}
+	return ret;
+}
+
+static int hid_report_pre_event(struct hid_device *hid,
+					struct hid_report *report, int interrupt)
+{
+	struct hid_hook *hook;
+	int ret, i;
+
+	if (!hid->driver)
+		return 0;
+
+	hook = hid->driver->hook;
+	if (hook && hook->pre_report_event) {
+		if (!hook->pre_report_event(report, interrupt))
+			return 0;
+	}
+
+	ret = -ENODEV;
+	for (i=0; i<NR_STICKY; ++i) {
+		if (!hid_sticky_drivers[i] || !test_bit(i, &hid->sticky_driver))
+			continue;
+		if (hid_sticky_driver_exit(i)) {
+			clear_bit(i, &hid->sticky_driver);
+			continue;
+		}
+		hook = hid_sticky_drivers[i]->hook;
+		if (hook && hook->pre_report_event) {
+			ret = hook->pre_report_event(report, interrupt);
+			if (!ret)
+				break;
+		}
+	}
+	return ret;
+}
+
+static void hid_report_event(struct hid_device *hid,
+					struct hid_report *report, int interrupt)
+{
+	struct hid_hook *hook;
+	int i;
+
+	if (!hid->driver)
+		return;
+		
+	hook = hid->driver->hook;
+	if (hook && hook->report_event) {
+		hook->report_event(report, interrupt);
+		return;
+	}
+	
+	hidinput_report_event(hid, report);
+	
+	for (i=0; i<NR_STICKY; ++i) {
+		if (!hid_sticky_drivers[i] || !test_bit(i, &hid->sticky_driver))
+			continue;
+		if (hid_sticky_driver_exit(i)) {
+			clear_bit(i, &hid->sticky_driver);
+			continue;
+		}
+		hook = hid_sticky_drivers[i]->hook;
+		if (hook && hook->report_event)
+			hook->report_event(report, interrupt);
+	}
+}
+
 int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int interrupt)
 {
 	struct hid_report_enum *report_enum = hid->report_enum + type;
 	struct hid_report *report;
 	int n, rsize;
-
-	if (!hid)
-		return -ENODEV;
+	
+	if (!hid || !hid->driver)
+		return -1;
 
 	if (!size) {
 		dbg("empty report");
@@ -976,34 +1078,14 @@ int hid_input_report(struct hid_device *
 		dbg("report %d is too short, (%d < %d)", report->id, size, rsize);
 		memset(data + size, 0, rsize - size);
 	}
-
-	if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event)
-		hid->hiddev_report_event(hid, report);
-	if (hid->claimed & HID_CLAIMED_HIDRAW)
-		hidraw_report_event(hid, data, size);
-
+	hid_raw_event(hid, type, data, size, interrupt);
+	if (!hid_report_pre_event(hid, report, interrupt))
+		return 0;
 	for (n = 0; n < report->maxfield; n++)
 		hid_input_field(hid, report->field[n], data, interrupt);
-
-	if (hid->claimed & HID_CLAIMED_INPUT)
-		hidinput_report_event(hid, report);
+	hid_report_event(hid, report, interrupt);
 
 	return 0;
 }
 EXPORT_SYMBOL_GPL(hid_input_report);
 
-static int __init hid_init(void)
-{
-	return hidraw_init();
-}
-
-static void __exit hid_exit(void)
-{
-	hidraw_exit();
-}
-
-module_init(hid_init);
-module_exit(hid_exit);
-
-MODULE_LICENSE(DRIVER_LICENSE);
-
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/hid-input.c linux-2.6.21-rc6-mm1.new/drivers/hid/hid-input.c
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/hid-input.c	2007-04-10 09:16:46.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/hid-input.c	2007-04-15 21:35:15.000000000 +0800
@@ -33,11 +33,7 @@
 
 #include <linux/hid.h>
 #include <linux/hid-debug.h>
-
-static int hid_pb_fnmode = 1;
-module_param_named(pb_fnmode, hid_pb_fnmode, int, 0644);
-MODULE_PARM_DESC(pb_fnmode,
-		"Mode of fn key on PowerBooks (0 = disabled, 1 = fkeyslast, 2 = fkeysfirst)");
+#include "hid-inter.h"
 
 #define unk	KEY_UNKNOWN
 
@@ -73,183 +69,13 @@ static const struct {
 #define map_abs_clear(c)	do { map_abs(c); clear_bit(c, bit); } while (0)
 #define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
 
-#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
-
-struct hidinput_key_translation {
-	u16 from;
-	u16 to;
-	u8 flags;
-};
-
-#define POWERBOOK_FLAG_FKEY 0x01
-
-static struct hidinput_key_translation powerbook_fn_keys[] = {
-	{ KEY_BACKSPACE, KEY_DELETE },
-	{ KEY_F1,       KEY_BRIGHTNESSDOWN,     POWERBOOK_FLAG_FKEY },
-	{ KEY_F2,       KEY_BRIGHTNESSUP,       POWERBOOK_FLAG_FKEY },
-	{ KEY_F3,       KEY_MUTE,               POWERBOOK_FLAG_FKEY },
-	{ KEY_F4,       KEY_VOLUMEDOWN,         POWERBOOK_FLAG_FKEY },
-	{ KEY_F5,       KEY_VOLUMEUP,           POWERBOOK_FLAG_FKEY },
-	{ KEY_F6,       KEY_NUMLOCK,            POWERBOOK_FLAG_FKEY },
-	{ KEY_F7,       KEY_SWITCHVIDEOMODE,    POWERBOOK_FLAG_FKEY },
-	{ KEY_F8,       KEY_KBDILLUMTOGGLE,     POWERBOOK_FLAG_FKEY },
-	{ KEY_F9,       KEY_KBDILLUMDOWN,       POWERBOOK_FLAG_FKEY },
-	{ KEY_F10,      KEY_KBDILLUMUP,         POWERBOOK_FLAG_FKEY },
-	{ KEY_UP,       KEY_PAGEUP },
-	{ KEY_DOWN,     KEY_PAGEDOWN },
-	{ KEY_LEFT,     KEY_HOME },
-	{ KEY_RIGHT,    KEY_END },
-	{ }
-};
-
-static struct hidinput_key_translation powerbook_numlock_keys[] = {
-	{ KEY_J,        KEY_KP1 },
-	{ KEY_K,        KEY_KP2 },
-	{ KEY_L,        KEY_KP3 },
-	{ KEY_U,        KEY_KP4 },
-	{ KEY_I,        KEY_KP5 },
-	{ KEY_O,        KEY_KP6 },
-	{ KEY_7,        KEY_KP7 },
-	{ KEY_8,        KEY_KP8 },
-	{ KEY_9,        KEY_KP9 },
-	{ KEY_M,        KEY_KP0 },
-	{ KEY_DOT,      KEY_KPDOT },
-	{ KEY_SLASH,    KEY_KPPLUS },
-	{ KEY_SEMICOLON, KEY_KPMINUS },
-	{ KEY_P,        KEY_KPASTERISK },
-	{ KEY_MINUS,    KEY_KPEQUAL },
-	{ KEY_0,        KEY_KPSLASH },
-	{ KEY_F6,       KEY_NUMLOCK },
-	{ KEY_KPENTER,  KEY_KPENTER },
-	{ KEY_BACKSPACE, KEY_BACKSPACE },
-	{ }
-};
-
-static struct hidinput_key_translation powerbook_iso_keyboard[] = {
-	{ KEY_GRAVE,    KEY_102ND },
-	{ KEY_102ND,    KEY_GRAVE },
-	{ }
-};
-
-static struct hidinput_key_translation *find_translation(struct hidinput_key_translation *table, u16 from)
-{
-	struct hidinput_key_translation *trans;
-
-	/* Look for the translation */
-	for (trans = table; trans->from; trans++)
-		if (trans->from == from)
-			return trans;
-
-	return NULL;
-}
-
-static int hidinput_pb_event(struct hid_device *hid, struct input_dev *input,
-		struct hid_usage *usage, __s32 value)
-{
-	struct hidinput_key_translation *trans;
-
-	if (usage->code == KEY_FN) {
-		if (value) hid->quirks |=  HID_QUIRK_POWERBOOK_FN_ON;
-		else       hid->quirks &= ~HID_QUIRK_POWERBOOK_FN_ON;
-
-		input_event(input, usage->type, usage->code, value);
-
-		return 1;
-	}
-
-	if (hid_pb_fnmode) {
-		int do_translate;
-
-		trans = find_translation(powerbook_fn_keys, usage->code);
-		if (trans) {
-			if (test_bit(usage->code, hid->pb_pressed_fn))
-				do_translate = 1;
-			else if (trans->flags & POWERBOOK_FLAG_FKEY)
-				do_translate =
-					(hid_pb_fnmode == 2 &&  (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)) ||
-					(hid_pb_fnmode == 1 && !(hid->quirks & HID_QUIRK_POWERBOOK_FN_ON));
-			else
-				do_translate = (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON);
-
-			if (do_translate) {
-				if (value)
-					set_bit(usage->code, hid->pb_pressed_fn);
-				else
-					clear_bit(usage->code, hid->pb_pressed_fn);
-
-				input_event(input, usage->type, trans->to, value);
-
-				return 1;
-			}
-		}
-
-		if (test_bit(usage->code, hid->pb_pressed_numlock) ||
-				test_bit(LED_NUML, input->led)) {
-			trans = find_translation(powerbook_numlock_keys, usage->code);
-
-			if (trans) {
-				if (value)
-					set_bit(usage->code, hid->pb_pressed_numlock);
-				else
-					clear_bit(usage->code, hid->pb_pressed_numlock);
-
-				input_event(input, usage->type, trans->to, value);
-			}
-
-			return 1;
-		}
-	}
-
-	if (hid->quirks & HID_QUIRK_POWERBOOK_ISO_KEYBOARD) {
-		trans = find_translation(powerbook_iso_keyboard, usage->code);
-		if (trans) {
-			input_event(input, usage->type, trans->to, value);
-			return 1;
-		}
-	}
-
-	return 0;
-}
-
-static void hidinput_pb_setup(struct input_dev *input)
-{
-	struct hidinput_key_translation *trans;
-
-	set_bit(KEY_NUMLOCK, input->keybit);
-
-	/* Enable all needed keys */
-	for (trans = powerbook_fn_keys; trans->from; trans++)
-		set_bit(trans->to, input->keybit);
-
-	for (trans = powerbook_numlock_keys; trans->from; trans++)
-		set_bit(trans->to, input->keybit);
-
-	for (trans = powerbook_iso_keyboard; trans->from; trans++)
-		set_bit(trans->to, input->keybit);
-
-}
-#else
-static inline int hidinput_pb_event(struct hid_device *hid, struct input_dev *input,
-		struct hid_usage *usage, __s32 value)
-{
-	return 0;
-}
-
-static inline void hidinput_pb_setup(struct input_dev *input)
-{
-}
-#endif
-
-static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
+static void hidinput_configure_usage(struct input_dev *input, struct hid_field *field,
 				     struct hid_usage *usage)
 {
-	struct input_dev *input = hidinput->input;
 	struct hid_device *device = input->private;
 	int max = 0, code;
 	unsigned long *bit = NULL;
 
-	field->hidinput = hidinput;
-
 #ifdef CONFIG_HID_DEBUG
 	printk(KERN_DEBUG "Mapping: ");
 	hid_resolv_usage(usage->hid);
@@ -606,17 +432,7 @@ static void hidinput_configure_usage(str
 			goto ignore;
 
 		case HID_UP_CUSTOM: /* Reported on Logitech and Powerbook USB keyboards */
-
 			set_bit(EV_REP, input->evbit);
-			switch(usage->hid & HID_USAGE) {
-				case 0x003:
-					/* The fn key on Apple PowerBooks */
-					map_key_clear(KEY_FN);
-					hidinput_pb_setup(input);
-					break;
-
-				default:    goto ignore;
-			}
 			break;
 
 		case HID_UP_LOGIVENDOR: /* Reported on Logitech Ultra X Media Remote */
@@ -746,8 +562,87 @@ ignore:
 	return;
 }
 
+static void inline hid_driver_hook_usage(struct input_dev *input,
+						struct hid_usage *usage,
+						struct usage_block *usage_block)
+{
+	unsigned long *bits;
+	struct hid_device *hid;
+
+	hid = input->private;
+	switch (usage_block->event) {
+	case EV_KEY:
+		bits = input->keybit;
+		break;
+	case EV_REL:
+		bits = input->relbit;
+		break;
+	case EV_ABS:
+		bits = input->relbit;
+		break;
+	case EV_MSC:
+		bits = input->absbit;
+		break;
+	case EV_SW:
+		bits = input->swbit;
+		break;
+	case EV_LED:
+		bits = input->ledbit;
+		break;
+	case EV_SND:
+		bits = input->sndbit;
+		break;
+	case EV_FF:
+		bits = input->ffbit;
+		break;
+	case EV_FF_STATUS:
+		bits = NULL;
+		break;
+	default:
+		return;
+	}
+
+	usage->code = usage_block->code;
+	usage->type = usage_block->event;
+	set_bit(usage_block->event, input->evbit);
+	if (bits)
+		set_bit(usage_block->code, bits);
+}
+
+static void hid_driver_hook_field(struct hid_driver *driver,
+						struct hid_field *field,
+						struct hid_usage *hid_usage)
+{
+	struct hid_hook *hook = driver->hook;
+	struct input_dev *input = field->hidinput->input;
+	struct usage_block *usage_block;
+	struct usage_page_block *page_block;
+	int page;
+	int usage;
+
+	if (!hook)
+		return;
+		
+	page = (hid_usage->hid & HID_USAGE_PAGE);
+	usage = (hid_usage->hid & HID_USAGE);
+	page_block = hook->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;
+			hid_driver_hook_usage(input, hid_usage, usage_block);
+		}
+	}
+	if (hook->setup_usage)
+		hook->setup_usage(field, hid_usage);
+}
+
 void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
 {
+	struct hid_input *hidinput;
 	struct input_dev *input;
 	int *quirks = &hid->quirks;
 
@@ -759,6 +654,8 @@ void hidinput_hid_event(struct hid_devic
 	if (!usage->type)
 		return;
 
+	hidinput = field->hidinput;
+
 	if (((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005))
 		|| ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007))) {
 		if (value) hid->quirks |=  HID_QUIRK_2WHEEL_MOUSE_HACK_ON;
@@ -776,9 +673,6 @@ void hidinput_hid_event(struct hid_devic
 		return;
 	}
 
-	if ((hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) && hidinput_pb_event(hid, input, usage, value))
-		return;
-
 	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
 		int hat_dir = usage->hat_dir;
 		if (!hat_dir)
@@ -828,10 +722,14 @@ void hidinput_hid_event(struct hid_devic
 	if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY))
 		input_event(input, usage->type, usage->code, 0);
 }
+EXPORT_SYMBOL(hidinput_hid_event);
 
 void hidinput_report_event(struct hid_device *hid, struct hid_report *report)
 {
 	struct hid_input *hidinput;
+	
+	if (!hid->driver)
+		return;
 
 	list_for_each_entry(hidinput, &hid->inputs, list)
 		input_sync(hidinput->input);
@@ -855,16 +753,81 @@ int hidinput_find_field(struct hid_devic
 }
 EXPORT_SYMBOL_GPL(hidinput_find_field);
 
+static int hidinput_input_event(struct input_dev *dev, unsigned int type,
+						unsigned int code, int value)
+{
+	struct hid_device *hid = dev->private;
+	struct hid_transport *tl;
+	int ret;
+
+	if (!hid || !hid->driver)
+		return 0;
+	if (hid->driver->event)
+		return hid->driver->event(dev, type, code, value);
+	ret = 0;
+	tl = hid_transports[hid->bus];
+	if (tl->event)
+		ret = tl->event(dev, type, code, value);
+	return ret;
+}
+
+int hid_open(struct hid_device *hid)
+{
+	struct hid_transport *tl;
+	int ret;
+	
+	if (hid->driver->open)
+		return hid->driver->open(hid);
+	ret = 0;
+	tl = hid_transports[hid->bus];
+	if (tl->open)
+		ret = tl->open(hid);
+	return ret;
+}
+EXPORT_SYMBOL(hid_open);
+
 static int hidinput_open(struct input_dev *dev)
 {
 	struct hid_device *hid = dev->private;
-	return hid->hid_open(hid);
+	
+	if (!hid || !hid->driver)
+		return 0;
+	return hid_open(hid);
 }
 
+void hid_close(struct hid_device *hid)
+{
+	struct hid_transport *tl;
+	
+	if (hid->driver->close) {
+		hid->driver->close(hid);
+		return;
+	}
+	tl = hid_transports[hid->bus];
+	if (tl->close)
+		tl->close(hid);
+}
+EXPORT_SYMBOL(hid_close);
+
 static void hidinput_close(struct input_dev *dev)
 {
 	struct hid_device *hid = dev->private;
-	hid->hid_close(hid);
+	
+	if (!hid || !hid->driver)
+		return;
+	hid_close(hid);
+	return;
+}
+
+static inline void hidinput_register_inputdev(struct hid_driver *driver,
+						struct input_dev *input)
+{
+	struct hid_hook *hook = driver->hook;
+	
+	if (hook && hook->register_inputdev)
+		hook->register_inputdev(input);
+	else
+		input_register_device(input);
 }
 
 /*
@@ -875,13 +838,18 @@ static void hidinput_close(struct input_
 
 int hidinput_connect(struct hid_device *hid)
 {
+	struct hid_driver *drv;
 	struct hid_report *report;
 	struct hid_input *hidinput = NULL;
 	struct input_dev *input_dev;
 	int i, j, k;
 	int max_report_type = HID_OUTPUT_REPORT;
 
-	INIT_LIST_HEAD(&hid->inputs);
+	printk("connect %p\n", hid);
+
+	drv = hid->driver;
+	if (!drv)
+		return -1;
 
 	for (i = 0; i < hid->maxcollection; i++)
 		if (hid->collection[i].type == HID_COLLECTION_APPLICATION ||
@@ -912,7 +880,7 @@ int hidinput_connect(struct hid_device *
 				}
 
 				input_dev->private = hid;
-				input_dev->event = hid->hidinput_input_event;
+				input_dev->event = hidinput_input_event;
 				input_dev->open = hidinput_open;
 				input_dev->close = hidinput_close;
 
@@ -928,10 +896,15 @@ int hidinput_connect(struct hid_device *
 				list_add_tail(&hidinput->list, &hid->inputs);
 			}
 
-			for (i = 0; i < report->maxfield; i++)
+			for (i = 0; i < report->maxfield; i++) {
+				report->field[i]->hidinput = hidinput;
 				for (j = 0; j < report->field[i]->maxusage; j++)
-					hidinput_configure_usage(hidinput, report->field[i],
+					hidinput_configure_usage(hidinput->input, report->field[i],
 								 report->field[i]->usage + j);
+					hid_driver_hook_field(drv,
+								report->field[i],
+							report->field[i]->usage + j);
+			}
 
 			if (hid->quirks & HID_QUIRK_MULTI_INPUT) {
 				/* This will leave hidinput NULL, so that it
@@ -940,13 +913,13 @@ int hidinput_connect(struct hid_device *
 				 * UGCI) cram a lot of unrelated inputs into the
 				 * same interface. */
 				hidinput->report = report;
-				input_register_device(hidinput->input);
+				hidinput_register_inputdev(drv, hidinput->input);
 				hidinput = NULL;
 			}
 		}
 
 	if (hidinput)
-		input_register_device(hidinput->input);
+		hidinput_register_inputdev(drv, hidinput->input);
 
 	return 0;
 }
@@ -956,6 +929,10 @@ void hidinput_disconnect(struct hid_devi
 {
 	struct hid_input *hidinput, *next;
 
+	BUG_ON(!hid->driver);
+
+	printk("disconnect %p\n", hid);
+
 	list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
 		list_del(&hidinput->list);
 		input_unregister_device(hidinput->input);
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/hid-inter.h linux-2.6.21-rc6-mm1.new/drivers/hid/hid-inter.h
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/hid-inter.h	1970-01-01 08:00:00.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/hid-inter.h	2007-04-11 09:24:54.000000000 +0800
@@ -0,0 +1,26 @@
+/*
+ *  HID abstract layer for Linux driver model
+ *
+ *  Copyright (c) 2007 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.
+ */
+
+#ifndef HID_INTERNAL_H__
+#define HID_INTERNAL_H__
+
+#include <linux/input.h> /* for BUS_MAX */
+
+#define NR_STICKY 32
+
+extern struct hid_transport* hid_transports[BUS_MAX];
+extern struct hid_driver* hid_sticky_drivers[NR_STICKY];
+extern int hid_sticky_driver_exit(int i);
+
+#endif
+
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/hidraw.c linux-2.6.21-rc6-mm1.new/drivers/hid/hidraw.c
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/hidraw.c	2007-04-10 09:16:46.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/hidraw.c	2007-04-15 21:30:58.000000000 +0800
@@ -32,6 +32,7 @@
 #include <linux/mutex.h>
 
 #include <linux/hidraw.h>
+#include "hid-inter.h"
 
 static int hidraw_major;
 static struct cdev hidraw_cdev;
@@ -105,12 +106,17 @@ static ssize_t hidraw_write(struct file 
 {
 	unsigned int minor = iminor(file->f_path.dentry->d_inode);
 	struct hid_device *dev = hidraw_table[minor]->hid;
+	struct hid_driver *drv = dev->driver;
+	struct hid_hook *hook = drv->hook;
 	__u8 *buf;
 	int ret = 0;
 
-	if (!dev->hid_output_raw_report)
+	if (hid_transports[dev->bus]->raw_report)
+		goto really_start;
+	if (!hook || !hook->raw_report)
 		return -ENODEV;
 
+really_start:
 	if (count > HID_MIN_BUFFER_SIZE) {
 		printk(KERN_WARNING "hidraw: pid %d passed too large report\n",
 				current->pid);
@@ -132,7 +138,11 @@ static ssize_t hidraw_write(struct file 
 		goto out;
 	}
 
-	ret = dev->hid_output_raw_report(dev, buf, count);
+	if (hook && hook->raw_report)
+		ret = hook->raw_report(dev, buf, count);
+	else
+		ret = hid_transports[dev->bus]->raw_report(dev, buf, count);
+	
 out:
 	kfree(buf);
 	return ret;
@@ -178,7 +188,7 @@ static int hidraw_open(struct inode *ino
 
 	dev = hidraw_table[minor];
 	if (!dev->open++)
-		dev->hid->hid_open(dev->hid);
+		err = hid_open(dev->hid);
 
 out_unlock:
 	spin_unlock(&minors_lock);
@@ -203,7 +213,7 @@ static int hidraw_release(struct inode *
 	dev = hidraw_table[minor];
 	if (!dev->open--) {
 		if (list->hidraw->exist)
-			dev->hid->hid_close(dev->hid);
+			dev->hid->driver->close(dev->hid);
 		else
 			kfree(list->hidraw);
 	}
@@ -264,11 +274,15 @@ static const struct file_operations hidr
 	.ioctl =        hidraw_ioctl,
 };
 
-void hidraw_report_event(struct hid_device *hid, u8 *data, int len)
+static int hidraw_raw_event(struct hid_device *hid, int type, u8 *data,
+							int len, int interrupt)
 {
 	struct hidraw *dev = hid->hidraw;
 	struct hidraw_list *list;
 
+	if (!dev)
+		return -1;
+
 	list_for_each_entry(list, &dev->list, node) {
 		list->buffer[list->head].value = kmemdup(data, len, GFP_ATOMIC);
 		list->buffer[list->head].len = len;
@@ -277,10 +291,10 @@ void hidraw_report_event(struct hid_devi
 	}
 
 	wake_up_interruptible(&dev->wait);
+	return 0;
 }
-EXPORT_SYMBOL_GPL(hidraw_report_event);
 
-int hidraw_connect(struct hid_device *hid)
+static int hidraw_connect(struct hid_device *hid)
 {
 	int minor, result = -EINVAL;
 	struct hidraw *dev;
@@ -332,12 +346,14 @@ out:
 	return result;
 
 }
-EXPORT_SYMBOL_GPL(hidraw_connect);
 
-void hidraw_disconnect(struct hid_device *hid)
+static void hidraw_disconnect(struct hid_device *hid)
 {
 	struct hidraw *hidraw = hid->hidraw;
 
+	if (!hidraw)
+		return;
+
 	hidraw->exist = 0;
 
 	spin_lock(&minors_lock);
@@ -347,19 +363,36 @@ void hidraw_disconnect(struct hid_device
 	device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
 
 	if (hidraw->open) {
-		hid->hid_close(hid);
+		hid_close(hid);
 		wake_up_interruptible(&hidraw->wait);
 	} else {
 		kfree(hidraw);
 	}
 }
-EXPORT_SYMBOL_GPL(hidraw_disconnect);
+
+static struct hid_hook hidraw_hook = {
+	.raw_event = hidraw_raw_event,
+};
+
+static struct hid_driver hid_hidraw_driver = {
+	.name = "hidraw",
+	.version = "0.1.0",
+	.module = THIS_MODULE,
+	.probe = hidraw_connect,
+	.remove = hidraw_disconnect,
+	.hook = &hidraw_hook,
+};
 
 int __init hidraw_init(void)
 {
 	int result;
 	dev_t dev_id;
 
+	set_hid_driver_sticky(&hid_hidraw_driver);
+	result = hid_register_driver(&hid_hidraw_driver);
+	if (result)
+		goto out;
+
 	result = alloc_chrdev_region(&dev_id, HIDRAW_FIRST_MINOR,
 			HIDRAW_MAX_DEVICES, "hidraw");
 
@@ -388,8 +421,13 @@ void __exit hidraw_exit(void)
 {
 	dev_t dev_id = MKDEV(hidraw_major, 0);
 
+	hid_unregister_driver(&hid_hidraw_driver);
 	cdev_del(&hidraw_cdev);
 	class_destroy(hidraw_class);
 	unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
 
 }
+
+module_init(hidraw_init);
+module_exit(hidraw_exit);
+MODULE_LICENSE("GPL");
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/Kconfig linux-2.6.21-rc6-mm1.new/drivers/hid/Kconfig
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/Kconfig	2007-04-10 09:16:46.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/Kconfig	2007-04-16 13:44:21.000000000 +0800
@@ -37,7 +37,7 @@ config HID_DEBUG
 	If unsure, say N
 
 config HIDRAW
-	bool "/dev/hidraw raw HID device support"
+	tristate "/dev/hidraw raw HID device support"
 	depends on HID
 	---help---
 	Say Y here if you want to support HID devices (from the USB
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/Makefile linux-2.6.21-rc6-mm1.new/drivers/hid/Makefile
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/Makefile	2007-04-10 09:16:46.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/Makefile	2007-04-17 09:23:27.000000000 +0800
@@ -1,7 +1,7 @@
 #
 # Makefile for the HID driver
 #
-hid-objs			:= hid-core.o hid-input.o
+hid-objs			:= hid-core.o hid-input.o hid-al.o
 
 obj-$(CONFIG_HID)		+= hid.o
 
@@ -9,6 +9,13 @@ hid-$(CONFIG_HID_DEBUG)		+= hid-debug.o
 hid-$(CONFIG_HIDRAW)		+= hidraw.o
 
 obj-$(CONFIG_USB_HID)		+= usbhid/
+obj-$(CONFIG_HID_PID)		+= usbhid/
+obj-$(CONFIG_THRUSTMASTER_FF)   += usbhid/
+obj-$(CONFIG_ZEROPLUS_FF)       += usbhid/
+obj-$(CONFIG_POWERBOOK)		+= usbhid/
+obj-$(CONFIG_LOGITECH_FF)       += usbhid/
+obj-$(CONFIG_PANTHERLORD_FF)    += usbhid/
+obj-$(CONFIG_USB_HIDDEV)	+= usbhid/
 obj-$(CONFIG_USB_MOUSE)		+= usbhid/
 obj-$(CONFIG_USB_KBD)		+= usbhid/
hidbus.headers.patch (text/x-patch, 10.5 KB)
diff -Naurp linux-2.6.21-rc6-mm1.orig/include/linux/hiddev.h linux-2.6.21-rc6-mm1.new/include/linux/hiddev.h
--- linux-2.6.21-rc6-mm1.orig/include/linux/hiddev.h	2007-02-05 02:44:54.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/include/linux/hiddev.h	2007-04-11 09:05:29.000000000 +0800
@@ -218,23 +218,5 @@ struct hid_usage;
 struct hid_field;
 struct hid_report;
 
-#ifdef CONFIG_USB_HIDDEV
-int hiddev_connect(struct hid_device *);
-void hiddev_disconnect(struct hid_device *);
-void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
-		      struct hid_usage *usage, __s32 value);
-void hiddev_report_event(struct hid_device *hid, struct hid_report *report);
-int __init hiddev_init(void);
-void hiddev_exit(void);
-#else
-static inline int hiddev_connect(struct hid_device *hid) { return -1; }
-static inline void hiddev_disconnect(struct hid_device *hid) { }
-static inline void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
-		      struct hid_usage *usage, __s32 value) { }
-static inline void hiddev_report_event(struct hid_device *hid, struct hid_report *report) { }
-static inline int hiddev_init(void) { return 0; }
-static inline void hiddev_exit(void) { }
-#endif
-
 #endif
 #endif
diff -Naurp linux-2.6.21-rc6-mm1.orig/include/linux/hid.h linux-2.6.21-rc6-mm1.new/include/linux/hid.h
--- linux-2.6.21-rc6-mm1.orig/include/linux/hid.h	2007-04-10 09:16:50.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/include/linux/hid.h	2007-04-16 13:09:51.000000000 +0800
@@ -35,6 +35,7 @@
 #include <linux/timer.h>
 #include <linux/workqueue.h>
 #include <linux/input.h>
+#include <asm/bitops.h>
 
 /*
  * USB HID (Human Interface Device) interface class code
@@ -270,6 +271,7 @@ struct hid_item {
 #define HID_QUIRK_LOGITECH_DESCRIPTOR		0x00100000
 #define HID_QUIRK_DUPLICATE_USAGES		0x00200000
 #define HID_QUIRK_RESET_LEDS			0x00400000
+#define HID_QUIRK_SKIP				0x80000000
 
 /*
  * This is the global environment of the parser. This information is
@@ -403,55 +405,112 @@ struct hid_input {
 	struct input_dev *input;
 };
 
-struct hid_device {							/* device report descriptor */
+struct hid_driver;
+struct hid_device {	/* device report descriptor */
+	struct device device;
+	struct module *module;
+	struct hid_driver *driver;
+	unsigned long sticky_driver;
+	unsigned long flags;
+	void *tl_data;	/* Transports layer data. */
+	void *hidraw;	/* It will be remove soon. */
+	int quirks;
+
 	 __u8 *rdesc;
 	unsigned rsize;
-	struct hid_collection *collection;				/* List of HID collections */
-	unsigned collection_size;					/* Number of allocated hid_collections */
-	unsigned maxcollection;						/* Number of parsed collections */
-	unsigned maxapplication;					/* Number of applications */
-	unsigned short bus;                                             /* BUS ID */
-	unsigned short vendor;                                          /* Vendor ID */
-	unsigned short product;                                         /* Product ID */
-	unsigned version;						/* HID version */
-	unsigned country;						/* HID country */
+	struct hid_collection *collection;	/* List of HID collections */
+	unsigned collection_size;	/* Number of allocated hid_collections */
+	unsigned maxcollection;			/* Number of parsed collections */
+	unsigned maxapplication;		/* Number of applications */
+	unsigned short bus;			/* BUS ID */
+	unsigned short vendor;			/* Vendor ID */
+	unsigned short product;			/* Product ID */
+	unsigned version;			/* HID version */
+	unsigned country;			/* HID country */
+	int open;				/* Is the device open by anyone? */
 	struct hid_report_enum report_enum[HID_REPORT_TYPES];
+	void *private;				/* Data for specific driver */
 
-	struct device *dev;						/* device */
-
-	unsigned claimed;						/* Claimed by hidinput, hiddev? */
-	unsigned quirks;						/* Various quirks the device can pull on us */
+	void (*shutdown)(struct hid_device *dev);
 
-	struct list_head inputs;					/* The list of inputs */
-	void *hiddev;							/* The hiddev structure */
-	void *hidraw;
-	int minor;							/* Hiddev minor number */
-
-	wait_queue_head_t wait;						/* For sleeping */
-
-	int open;							/* is the device open by anyone? */
-	char name[128];							/* Device name */
-	char phys[64];							/* Device physical location */
-	char uniq[64];							/* Device unique identifier (serial #) */
-
-	void *driver_data;
-
-	/* device-specific function pointers */
-	int (*hidinput_input_event) (struct input_dev *, unsigned int, unsigned int, int);
-	int (*hid_open) (struct hid_device *);
-	void (*hid_close) (struct hid_device *);
-
-	/* hiddev event handler */
-	void (*hiddev_hid_event) (struct hid_device *, struct hid_field *field,
-				  struct hid_usage *, __s32);
-	void (*hiddev_report_event) (struct hid_device *, struct hid_report *);
-
-	/* handler for raw output data, used by hidraw */
-	int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t);
-#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
-	unsigned long pb_pressed_fn[NBITS(KEY_MAX)];
-	unsigned long pb_pressed_numlock[NBITS(KEY_MAX)];
-#endif
+	struct device *dev;			/* Physical device(USB, Bluetooth, etc.) */
+	char name[128];				/* Device name */
+	char phys[64];				/* Device physical location */
+	char uniq[64];				/* Device unique identifier (serial #) */
+	struct list_head inputs;		/* The list of inputs */
+};
+#define to_hid_device(dev) container_of(dev, struct hid_device, device);
+
+#define HID_IGNORE_VENDOR	1
+#define HID_IGNORE_PRODUCT	2
+#define HID_IGNORE_VERSION	4
+#define HID_IGNORE_COUNTRY	8
+
+struct skip_id {
+	struct list_head node;
+	unsigned int vendor;
+	unsigned int product;
+	unsigned int version;
+	unsigned int country;
+	unsigned int mask;
+};
+
+struct hid_hook;
+struct hid_driver {
+	char name[64];
+	char version[64];
+	struct module *module;
+	struct device_driver driver;
+	unsigned long flags;
+	
+	unsigned short bus;
+	struct hid_hook *hook;
+	struct list_head skip_table;
+
+	void *private;
+
+	int (*match)(struct hid_driver *, struct hid_device *);
+	int (*probe)(struct hid_device *);
+	void (*remove)(struct hid_device *);
+	void (*shutdown)(struct hid_device *);
+	int (*event)(struct input_dev *, unsigned int, unsigned int, int);
+	int (*open)(struct hid_device *);
+	void (*close)(struct hid_device *);
+};
+#define to_hid_driver(drv) container_of(drv, struct hid_driver, driver);
+
+struct hid_transport {
+	struct module *module;
+	unsigned short bus;
+	void (*update_busid)(struct hid_device *);
+	int (*event)(struct input_dev *, unsigned int, unsigned int, int);
+	int (*open)(struct hid_device *);
+	void (*close)(struct hid_device *);
+	int (*raw_report)(struct hid_device *, u8 *, size_t);
+	void *private;
+};
+
+struct usage_page_block;
+struct hid_hook {
+	void (*setup_usage)(struct hid_field*, struct hid_usage*);
+	void (*register_inputdev)(struct input_dev*);
+	int (*hid_event)(struct hid_field*, struct hid_usage*, __s32, int);
+	int (*pre_report_event)(struct hid_report*, int);
+	int (*report_event)(struct hid_report*, int);
+	int (*raw_event)(struct hid_device *, int, u8 *, int, int);
+	int (*raw_report)(struct hid_device *, u8 *, size_t);
+	struct usage_page_block *usage_page_table;
+};
+
+struct usage_block {
+	int usage; /* usage code */
+	int event; /* input event type, e.g. EV_KEY. */
+	int code;  /* input subsystem code, e.g. KEY_F1. */
+};
+
+struct usage_page_block {
+	int page; /* usage page code */
+	struct usage_block *usage_blockes;
 };
 
 #define HID_GLOBAL_STACK_SIZE 4
@@ -500,22 +559,38 @@ void hid_output_report(struct hid_report
 void hid_free_device(struct hid_device *device);
 struct hid_device *hid_parse_report(__u8 *start, unsigned size);
 
-#ifdef CONFIG_HID_FF
-int hid_ff_init(struct hid_device *hid);
-
-int hid_lgff_init(struct hid_device *hid);
-int hid_plff_init(struct hid_device *hid);
-int hid_tmff_init(struct hid_device *hid);
-int hid_zpff_init(struct hid_device *hid);
-#ifdef CONFIG_HID_PID
-int hid_pidff_init(struct hid_device *hid);
-#else
-static inline int hid_pidff_init(struct hid_device *hid) { return -ENODEV; }
-#endif
+#define hid_flag_accessor(DD, name, FLAG)	\
+static inline void set_##DD##_##name(struct DD* dd)\
+{\
+	set_bit(FLAG, &dd->flags);\
+}\
+static inline void clear_##DD##_##name(struct DD* dd)\
+{\
+	clear_bit(FLAG, &dd->flags);\
+}\
+static inline int is_##DD##_##name(struct DD* dd)\
+{\
+	return test_bit(FLAG, &dd->flags);\
+}
+
+/* set_hid_driver_sticky()/is_hid_driver_sticky() */
+#define HID_DRV_STICKY	(0x1UL)
+hid_flag_accessor(hid_driver, sticky, HID_DRV_STICKY)
+
+static inline struct hid_device *hid_new_device(int gfp)
+{
+	return kzalloc(sizeof(struct hid_device), gfp);
+}
+
+extern int hid_register_transport(struct hid_transport *);
+extern void hid_unregister_transport(struct hid_transport *);
+extern int hid_register_device(struct hid_device *);
+extern void hid_unregister_device(struct hid_device *);
+extern int hid_register_driver(struct hid_driver *);
+extern void hid_unregister_driver(struct hid_driver *);
+extern int hid_open(struct hid_device *);
+extern void hid_close(struct hid_device *);
 
-#else
-static inline int hid_ff_init(struct hid_device *hid) { return -1; }
-#endif
 #ifdef DEBUG
 #define dbg(format, arg...) printk(KERN_DEBUG "%s: " format "\n" , \
 		__FILE__ , ## arg)
diff -Naurp linux-2.6.21-rc6-mm1.orig/include/linux/hidraw.h linux-2.6.21-rc6-mm1.new/include/linux/hidraw.h
--- linux-2.6.21-rc6-mm1.orig/include/linux/hidraw.h	2007-04-10 09:16:50.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/include/linux/hidraw.h	2007-04-11 09:05:31.000000000 +0800
@@ -67,20 +67,6 @@ struct hidraw_list {
 	struct mutex read_mutex;
 };
 
-#ifdef CONFIG_HIDRAW
-int hidraw_init(void);
-void hidraw_exit(void);
-void hidraw_report_event(struct hid_device *, u8 *, int);
-int hidraw_connect(struct hid_device *);
-void hidraw_disconnect(struct hid_device *);
-#else
-static inline int hidraw_init(void) { return 0; }
-static inline void hidraw_exit(void) { }
-static inline void hidraw_report_event(struct hid_device *hid, u8 *data, int len) { }
-static inline int hidraw_connect(struct hid_device *hid) { return -1; }
-static inline void hidraw_disconnect(struct hid_device *hid) { }
-#endif
-
 #endif
 
 #endif
diff -Naurp linux-2.6.21-rc6-mm1.orig/include/linux/input.h linux-2.6.21-rc6-mm1.new/include/linux/input.h
--- linux-2.6.21-rc6-mm1.orig/include/linux/input.h	2007-04-10 09:16:50.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/include/linux/input.h	2007-04-10 10:24:51.000000000 +0800
@@ -679,7 +679,7 @@ struct input_absinfo {
 #define BUS_I2C			0x18
 #define BUS_HOST		0x19
 #define BUS_GSC			0x1A
-
+#define BUS_MAX			(BUS_GSC+1)
 /*
  * Values describing the status of a force-feedback effect
  */
hidbus.usb.core.patch (text/x-patch, 19.6 KB)
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hid-core.c linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hid-core.c
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hid-core.c	2007-04-10 09:16:46.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hid-core.c	2007-04-15 21:29:31.000000000 +0800
@@ -29,10 +29,6 @@
 
 #include <linux/usb.h>
 
-#include <linux/hid.h>
-#include <linux/hiddev.h>
-#include <linux/hid-debug.h>
-#include <linux/hidraw.h>
 #include "usbhid.h"
 
 /*
@@ -299,6 +295,39 @@ static const struct hid_blacklist {
 	__u32 quirks;
 } hid_blacklist[] = {
 
+	/* the hid-zpff.c will these two devices */
+	{ 0x46d, 0x0005, HID_QUIRK_SKIP },
+	{ 0xc12, 0x0030, HID_QUIRK_SKIP },
+
+	/* the hid-tmff.c will handle this */
+	{ 0x044f, 0xb304, HID_QUIRK_SKIP },
+	
+	/* the hid-plff.c will handle this */
+	{ 0x0810, 0x0001, HID_QUIRK_SKIP },
+
+	/* the hid-lgff.c will handle follow these five devices */
+	{ USB_VENDOR_ID_LOGITECH, 0xc221, HID_QUIRK_SKIP },
+	{ USB_VENDOR_ID_LOGITECH, 0xc219, HID_QUIRK_SKIP },
+	{ USB_VENDOR_ID_LOGITECH, 0xc283, HID_QUIRK_SKIP },
+	{ USB_VENDOR_ID_LOGITECH, 0xc294, HID_QUIRK_SKIP },
+	{ USB_VENDOR_ID_LOGITECH, 0xc295, HID_QUIRK_SKIP },
+	{ USB_VENDOR_ID_LOGITECH, 0xca03, HID_QUIRK_SKIP },
+
+	/* the hid-pb.c will handle these thirteen devices */
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_SKIP },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO, HID_QUIRK_SKIP },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI, HID_QUIRK_SKIP },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO, HID_QUIRK_SKIP },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS, HID_QUIRK_SKIP },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI, HID_QUIRK_SKIP },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO, HID_QUIRK_SKIP },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS, HID_QUIRK_SKIP },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI, HID_QUIRK_SKIP },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO, HID_QUIRK_SKIP },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_SKIP },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_SKIP },
+	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_SKIP },
+
 	{ USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 },
 	{ USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 },
 
@@ -306,7 +335,6 @@ static const struct hid_blacklist {
 
 	{ USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV },
 
-
 	{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD },
 	{ USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD },
 	{ USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD },
@@ -462,25 +490,28 @@ static const struct hid_blacklist {
 	{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
 	{ USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
 
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-	{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
-
 	{ USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS },
 
 	{ 0, 0 }
 };
 
+static void usbhid_update_busid(struct hid_device *dev)
+{
+	static atomic_t usb_hiddev_idx = ATOMIC_INIT(-1);
+
+	snprintf(dev->device.bus_id, BUS_ID_SIZE, "U:%04x:%04x:%d",
+					le16_to_cpu(dev->vendor),
+					le16_to_cpu(dev->product),
+				atomic_add_return(1, &usb_hiddev_idx));
+}
+
+static inline void usbhid_wake_up(struct hid_device *hid)
+{
+	struct usbhid_device *usbhid = (struct usbhid_device*)hid->tl_data;
+
+	wake_up(&usbhid->wait);
+}
+
 /*
  * Input submission and I/O error handler.
  */
@@ -492,7 +523,7 @@ static int hid_start_in(struct hid_devic
 {
 	unsigned long flags;
 	int rc = 0;
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct usbhid_device *usbhid = hid->tl_data;
 
 	spin_lock_irqsave(&usbhid->inlock, flags);
 	if (hid->open > 0 && !test_bit(HID_SUSPENDED, &usbhid->iofl) &&
@@ -509,7 +540,9 @@ static int hid_start_in(struct hid_devic
 static void hid_retry_timeout(unsigned long _hid)
 {
 	struct hid_device *hid = (struct hid_device *) _hid;
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct usbhid_device *usbhid = hid->tl_data;
+
+	BUG_ON(!usbhid);
 
 	dev_dbg(&usbhid->intf->dev, "retrying intr urb\n");
 	if (hid_start_in(hid))
@@ -564,7 +597,9 @@ static void hid_reset(struct work_struct
 static void hid_io_error(struct hid_device *hid)
 {
 	unsigned long flags;
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct usbhid_device *usbhid = hid->tl_data;
+
+	BUG_ON(!usbhid);
 
 	spin_lock_irqsave(&usbhid->inlock, flags);
 
@@ -606,7 +641,7 @@ done:
 static void hid_irq_in(struct urb *urb)
 {
 	struct hid_device	*hid = urb->context;
-	struct usbhid_device 	*usbhid = hid->driver_data;
+	struct usbhid_device 	*usbhid = hid->tl_data;
 	int			status;
 
 	switch (urb->status) {
@@ -653,7 +688,7 @@ static void hid_irq_in(struct urb *urb)
 static int hid_submit_out(struct hid_device *hid)
 {
 	struct hid_report *report;
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct usbhid_device *usbhid = hid->tl_data;
 
 	report = usbhid->out[usbhid->outtail];
 
@@ -676,7 +711,7 @@ static int hid_submit_ctrl(struct hid_de
 	struct hid_report *report;
 	unsigned char dir;
 	int len;
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct usbhid_device *usbhid = hid->tl_data;
 
 	report = usbhid->ctrl[usbhid->ctrltail].report;
 	dir = usbhid->ctrl[usbhid->ctrltail].dir;
@@ -727,10 +762,12 @@ static int hid_submit_ctrl(struct hid_de
 static void hid_irq_out(struct urb *urb)
 {
 	struct hid_device *hid = urb->context;
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct usbhid_device *usbhid = hid->tl_data;
 	unsigned long flags;
 	int unplug = 0;
 
+	BUG_ON(!usbhid);
+
 	switch (urb->status) {
 		case 0:			/* success */
 			break;
@@ -755,7 +792,7 @@ static void hid_irq_out(struct urb *urb)
 	if (usbhid->outhead != usbhid->outtail) {
 		if (hid_submit_out(hid)) {
 			clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
-			wake_up(&hid->wait);
+			usbhid_wake_up(hid);
 		}
 		spin_unlock_irqrestore(&usbhid->outlock, flags);
 		return;
@@ -763,7 +800,7 @@ static void hid_irq_out(struct urb *urb)
 
 	clear_bit(HID_OUT_RUNNING, &usbhid->iofl);
 	spin_unlock_irqrestore(&usbhid->outlock, flags);
-	wake_up(&hid->wait);
+	usbhid_wake_up(hid);
 }
 
 #define USB_VENDOR_ID_BELKIN		0x050d
@@ -776,10 +813,11 @@ static void hid_irq_out(struct urb *urb)
 static void hid_ctrl(struct urb *urb)
 {
 	struct hid_device *hid = urb->context;
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct usbhid_device *usbhid = hid->tl_data;
 	unsigned long flags;
 	int unplug = 0;
 
+		
 	spin_lock_irqsave(&usbhid->ctrllock, flags);
 
 	switch (urb->status) {
@@ -808,7 +846,7 @@ static void hid_ctrl(struct urb *urb)
 	if (usbhid->ctrlhead != usbhid->ctrltail) {
 		if (hid_submit_ctrl(hid)) {
 			clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
-			wake_up(&hid->wait);
+			usbhid_wake_up(hid);
 		}
 		spin_unlock_irqrestore(&usbhid->ctrllock, flags);
 		return;
@@ -816,14 +854,16 @@ static void hid_ctrl(struct urb *urb)
 
 	clear_bit(HID_CTRL_RUNNING, &usbhid->iofl);
 	spin_unlock_irqrestore(&usbhid->ctrllock, flags);
-	wake_up(&hid->wait);
+	usbhid_wake_up(hid);
 }
 
 void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
 {
 	int head;
 	unsigned long flags;
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct usbhid_device *usbhid = hid->tl_data;
+
+	BUG_ON(!usbhid);
 
 	if ((hid->quirks & HID_QUIRK_NOGET) && dir == USB_DIR_IN)
 		return;
@@ -867,6 +907,7 @@ void usbhid_submit_report(struct hid_dev
 
 	spin_unlock_irqrestore(&usbhid->ctrllock, flags);
 }
+EXPORT_SYMBOL_GPL(usbhid_submit_report);
 
 static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
 {
@@ -893,9 +934,9 @@ static int usb_hidinput_input_event(stru
 
 int usbhid_wait_io(struct hid_device *hid)
 {
-	struct usbhid_device *usbhid = hid->driver_data;
-
-	if (!wait_event_timeout(hid->wait, (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl) &&
+	struct usbhid_device *usbhid = hid->tl_data;
+	
+	if (!wait_event_timeout(usbhid->wait, (!test_bit(HID_CTRL_RUNNING, &usbhid->iofl) &&
 					!test_bit(HID_OUT_RUNNING, &usbhid->iofl)),
 					10*HZ)) {
 		dbg("timeout waiting for ctrl or out queue to clear");
@@ -904,6 +945,7 @@ int usbhid_wait_io(struct hid_device *hi
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(usbhid_wait_io);
 
 static int hid_set_idle(struct usb_device *dev, int ifnum, int report, int idle)
 {
@@ -935,23 +977,24 @@ int usbhid_open(struct hid_device *hid)
 		hid_io_error(hid);
 	return 0;
 }
+EXPORT_SYMBOL_GPL(usbhid_open);
 
 void usbhid_close(struct hid_device *hid)
 {
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct usbhid_device *usbhid = hid->tl_data;
 
 	if (!--hid->open)
 		usb_kill_urb(usbhid->urbin);
 }
+EXPORT_SYMBOL_GPL(usbhid_close);
 
 /*
  * Initialize all reports
  */
-
 void usbhid_init_reports(struct hid_device *hid)
 {
 	struct hid_report *report;
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct usbhid_device *usbhid = hid->tl_data;
 	int err, ret;
 
 	list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list)
@@ -974,6 +1017,7 @@ void usbhid_init_reports(struct hid_devi
 	if (err)
 		warn("timeout initializing reports");
 }
+EXPORT_SYMBOL_GPL(usbhid_init_reports);
 
 /*
  * Reset LEDs which BIOS might have left on. For now, just NumLock (0x01).
@@ -1032,7 +1076,7 @@ static void hid_find_max_report(struct h
 
 static int hid_alloc_buffers(struct usb_device *dev, struct hid_device *hid)
 {
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct usbhid_device *usbhid = hid->tl_data;
 
 	if (!(usbhid->inbuf = usb_buffer_alloc(dev, usbhid->bufsize, GFP_ATOMIC, &usbhid->inbuf_dma)))
 		return -1;
@@ -1046,9 +1090,9 @@ static int hid_alloc_buffers(struct usb_
 	return 0;
 }
 
-static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t count)
+static int usbhid_raw_report(struct hid_device *hid, __u8 *buf, size_t count)
 {
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct usbhid_device *usbhid = hid->tl_data;
 	struct usb_device *dev = hid_to_usb_dev(hid);
 	struct usb_interface *intf = usbhid->intf;
 	struct usb_host_interface *interface = intf->cur_altsetting;
@@ -1070,7 +1114,7 @@ static int usbhid_output_raw_report(stru
 
 static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
 {
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct usbhid_device *usbhid = hid->tl_data;
 
 	if (usbhid->inbuf)
 		usb_buffer_free(dev, usbhid->bufsize, usbhid->inbuf, usbhid->inbuf_dma);
@@ -1179,7 +1223,6 @@ static struct hid_device *usb_hid_config
 		(interface->desc.bInterfaceProtocol == USB_INTERFACE_PROTOCOL_MOUSE))
 			return NULL;
 
-
 	if (usb_get_extra_descriptor(interface, HID_DT_HID, &hdesc) &&
 	    (!interface->desc.bNumEndpoints ||
 	     usb_get_extra_descriptor(&interface->endpoint[0], HID_DT_HID, &hdesc))) {
@@ -1234,8 +1277,9 @@ static struct hid_device *usb_hid_config
 	if (!(usbhid = kzalloc(sizeof(struct usbhid_device), GFP_KERNEL)))
 		goto fail;
 
-	hid->driver_data = usbhid;
+	hid->tl_data = usbhid;
 	usbhid->hid = hid;
+	init_waitqueue_head(&usbhid->wait);
 
 	usbhid->bufsize = HID_MIN_BUFFER_SIZE;
 	hid_find_max_report(hid, HID_INPUT_REPORT, &usbhid->bufsize);
@@ -1299,8 +1343,6 @@ static struct hid_device *usb_hid_config
 		goto fail;
 	}
 
-	init_waitqueue_head(&hid->wait);
-
 	INIT_WORK(&usbhid->reset_work, hid_reset);
 	setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
 
@@ -1353,14 +1395,6 @@ static struct hid_device *usb_hid_config
 	usbhid->urbctrl->setup_dma = usbhid->cr_dma;
 	usbhid->urbctrl->transfer_dma = usbhid->ctrlbuf_dma;
 	usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
-	hid->hidinput_input_event = usb_hidinput_input_event;
-	hid->hid_open = usbhid_open;
-	hid->hid_close = usbhid_close;
-#ifdef CONFIG_USB_HIDDEV
-	hid->hiddev_hid_event = hiddev_hid_event;
-	hid->hiddev_report_event = hiddev_report_event;
-#endif
-	hid->hid_output_raw_report = usbhid_output_raw_report;
 	return hid;
 
 fail:
@@ -1373,18 +1407,17 @@ fail:
 	return NULL;
 }
 
-static void hid_disconnect(struct usb_interface *intf)
+static void hid_clean(struct hid_device *hid)
 {
-	struct hid_device *hid = usb_get_intfdata (intf);
 	struct usbhid_device *usbhid;
 
 	if (!hid)
 		return;
 
-	usbhid = hid->driver_data;
+	usbhid = hid->tl_data;
 
 	spin_lock_irq(&usbhid->inlock);	/* Sync with error handler */
-	usb_set_intfdata(intf, NULL);
+	usb_set_intfdata(usbhid->intf, NULL);
 	spin_unlock_irq(&usbhid->inlock);
 	usb_kill_urb(usbhid->urbin);
 	usb_kill_urb(usbhid->urbout);
@@ -1393,13 +1426,6 @@ static void hid_disconnect(struct usb_in
 	del_timer_sync(&usbhid->io_retry);
 	flush_scheduled_work();
 
-	if (hid->claimed & HID_CLAIMED_INPUT)
-		hidinput_disconnect(hid);
-	if (hid->claimed & HID_CLAIMED_HIDDEV)
-		hiddev_disconnect(hid);
-	if (hid->claimed & HID_CLAIMED_HIDRAW)
-		hidraw_disconnect(hid);
-
 	usb_free_urb(usbhid->urbin);
 	usb_free_urb(usbhid->urbctrl);
 	usb_free_urb(usbhid->urbout);
@@ -1408,6 +1434,16 @@ static void hid_disconnect(struct usb_in
 	hid_free_device(hid);
 }
 
+static void hid_disconnect(struct usb_interface *intf)
+{
+	struct hid_device *hid = usb_get_intfdata (intf);
+
+	if (!hid)
+		return;
+	hid_unregister_device(hid);
+	hid_clean(hid);
+}
+
 static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
 	struct hid_device *hid;
@@ -1426,43 +1462,12 @@ static int hid_probe(struct usb_interfac
 	if (hid->quirks & HID_QUIRK_RESET_LEDS)
 		usbhid_set_leds(hid);
 
-	if (!hidinput_connect(hid))
-		hid->claimed |= HID_CLAIMED_INPUT;
-	if (!hiddev_connect(hid))
-		hid->claimed |= HID_CLAIMED_HIDDEV;
-	if (!hidraw_connect(hid))
-		hid->claimed |= HID_CLAIMED_HIDRAW;
-
 	usb_set_intfdata(intf, hid);
 
-	if (!hid->claimed) {
-		printk ("HID device claimed by neither input, hiddev nor hidraw\n");
-		hid_disconnect(intf);
-		return -ENODEV;
-	}
-
-	if ((hid->claimed & HID_CLAIMED_INPUT))
-		hid_ff_init(hid);
-
 	if (hid->quirks & HID_QUIRK_SONY_PS3_CONTROLLER)
 		hid_fixup_sony_ps3_controller(interface_to_usbdev(intf),
 			intf->cur_altsetting->desc.bInterfaceNumber);
 
-	printk(KERN_INFO);
-
-	if (hid->claimed & HID_CLAIMED_INPUT)
-		printk("input");
-	if ((hid->claimed & HID_CLAIMED_INPUT) && ((hid->claimed & HID_CLAIMED_HIDDEV) ||
-				hid->claimed & HID_CLAIMED_HIDRAW))
-		printk(",");
-	if (hid->claimed & HID_CLAIMED_HIDDEV)
-		printk("hiddev%d", hid->minor);
-	if ((hid->claimed & HID_CLAIMED_INPUT) && (hid->claimed & HID_CLAIMED_HIDDEV) &&
-			(hid->claimed & HID_CLAIMED_HIDRAW))
-		printk(",");
-	if (hid->claimed & HID_CLAIMED_HIDRAW)
-		printk("hidraw%d", ((struct hidraw*)hid->hidraw)->minor);
-
 	c = "Device";
 	for (i = 0; i < hid->maxcollection; i++) {
 		if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
@@ -1475,16 +1480,21 @@ static int hid_probe(struct usb_interfac
 
 	usb_make_path(interface_to_usbdev(intf), path, 63);
 
-	printk(": USB HID v%x.%02x %s [%s] on %s\n",
+	printk(KERN_INFO"USB HID v%x.%02x %s [%s] on %s\n",
 		hid->version >> 8, hid->version & 0xff, c, hid->name, path);
 
+	hid->module = THIS_MODULE;
+	if (hid_register_device(hid)) {
+		hid_clean(hid);
+		return -ENODEV;
+	}
 	return 0;
 }
 
 static int hid_suspend(struct usb_interface *intf, pm_message_t message)
 {
 	struct hid_device *hid = usb_get_intfdata (intf);
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct usbhid_device *usbhid = hid->tl_data;
 
 	spin_lock_irq(&usbhid->inlock);	/* Sync with error handler */
 	set_bit(HID_SUSPENDED, &usbhid->iofl);
@@ -1498,7 +1508,7 @@ static int hid_suspend(struct usb_interf
 static int hid_resume(struct usb_interface *intf)
 {
 	struct hid_device *hid = usb_get_intfdata (intf);
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct usbhid_device *usbhid = hid->tl_data;
 	int status;
 
 	clear_bit(HID_SUSPENDED, &usbhid->iofl);
@@ -1525,6 +1535,18 @@ static void hid_post_reset(struct usb_in
 	hid_resume(intf);
 }
 
+static int usbhid_probe(struct hid_device *hid)
+{
+	int n;
+
+	for (n = 0; hid_blacklist[n].idVendor; n++)
+		if ((hid_blacklist[n].idVendor == hid->vendor) &&
+			(hid_blacklist[n].idProduct == hid->product) &&
+			hid_blacklist[n].quirks == HID_QUIRK_SKIP)
+			return -ENODEV;
+	return hidinput_connect(hid);
+}
+
 static struct usb_device_id hid_usb_ids [] = {
 	{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
 		.bInterfaceClass = USB_INTERFACE_CLASS_HID },
@@ -1533,7 +1555,7 @@ static struct usb_device_id hid_usb_ids 
 
 MODULE_DEVICE_TABLE (usb, hid_usb_ids);
 
-static struct usb_driver hid_driver = {
+static struct usb_driver usb_hid_driver = {
 	.name =		"usbhid",
 	.probe =	hid_probe,
 	.disconnect =	hid_disconnect,
@@ -1544,28 +1566,53 @@ static struct usb_driver hid_driver = {
 	.id_table =	hid_usb_ids,
 };
 
+static struct hid_driver hid_usb_driver = {
+	.name = "hidusb",
+	.version = DRIVER_VERSION,
+	.bus = BUS_USB,
+	.module = THIS_MODULE,
+	.probe = usbhid_probe
+};
+
+static struct hid_transport hid_usb_transport = {
+	.bus = BUS_USB,
+	.module = THIS_MODULE,
+	.event = usb_hidinput_input_event,
+	.open = usbhid_open,
+	.close = usbhid_close,
+	.update_busid = usbhid_update_busid,
+	.raw_report = usbhid_raw_report,
+};
+
 static int __init hid_init(void)
 {
 	int retval;
-	retval = hiddev_init();
+
+	retval = hid_register_transport(&hid_usb_transport);
+	if (retval)
+		goto hidtl_register_fail;
+	retval = hid_register_driver(&hid_usb_driver);
 	if (retval)
-		goto hiddev_init_fail;
-	retval = usb_register(&hid_driver);
+		goto hiddrv_register_fail;
+	retval = usb_register(&usb_hid_driver);
 	if (retval)
 		goto usb_register_fail;
 	info(DRIVER_VERSION ":" DRIVER_DESC);
 
 	return 0;
 usb_register_fail:
-	hiddev_exit();
-hiddev_init_fail:
+	hid_unregister_driver(&hid_usb_driver);
+hiddrv_register_fail:
+	hid_unregister_transport(&hid_usb_transport);
+hidtl_register_fail:
 	return retval;
 }
 
 static void __exit hid_exit(void)
 {
-	usb_deregister(&hid_driver);
-	hiddev_exit();
+	hid_unregister_driver(&hid_usb_driver);
+	hid_unregister_transport(&hid_usb_transport);
+	usb_deregister(&usb_hid_driver);
 }
 
 module_init(hid_init);
hidbus.usb.patch (text/x-patch, 34 KB)
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hiddev.c linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hiddev.c
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hiddev.c	2007-04-10 09:16:46.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hiddev.c	2007-04-15 21:29:44.000000000 +0800
@@ -36,6 +36,11 @@
 #include <linux/hiddev.h>
 #include "usbhid.h"
 
+#define DRIVER_VERSION "v0.9.0"
+#define DRIVER_AUTHOR "Paul Stewart, Vojtech Pavlik"
+#define DRIVER_DESC "USB HIDDEV driver"
+#define DRIVER_LICENSE "GPL"
+
 #ifdef CONFIG_USB_DYNAMIC_MINORS
 #define HIDDEV_MINOR_BASE	0
 #define HIDDEV_MINORS		256
@@ -48,9 +53,11 @@
 struct hiddev {
 	int exist;
 	int open;
+	int minor;
 	wait_queue_head_t wait;
 	struct hid_device *hid;
 	struct list_head list;
+	void *private;
 };
 
 struct hiddev_list {
@@ -159,7 +166,7 @@ hiddev_lookup_usage(struct hid_device *h
 static void hiddev_send_event(struct hid_device *hid,
 			      struct hiddev_usage_ref *uref)
 {
-	struct hiddev *hiddev = hid->hiddev;
+	struct hiddev *hiddev = hid->private;
 	struct hiddev_list *list;
 
 	list_for_each_entry(list, &hiddev->list, node) {
@@ -179,9 +186,10 @@ static void hiddev_send_event(struct hid
  * This is where hid.c calls into hiddev to pass an event that occurred over
  * the interrupt pipe
  */
-void hiddev_hid_event(struct hid_device *hid, struct hid_field *field,
-		      struct hid_usage *usage, __s32 value)
+static int hiddev_hid_event(struct hid_field *field, struct hid_usage *usage,
+							__s32 value, int interrupt)
 {
+	struct hid_device *hid = field->report->device;
 	unsigned type = field->report_type;
 	struct hiddev_usage_ref uref;
 
@@ -196,6 +204,8 @@ void hiddev_hid_event(struct hid_device 
 	uref.value = value;
 
 	hiddev_send_event(hid, &uref);
+	/* We always let HID core continue to dispatch this event here. */
+	return 1;
 }
 EXPORT_SYMBOL_GPL(hiddev_hid_event);
 
@@ -393,7 +403,7 @@ static int hiddev_ioctl(struct inode *in
 	struct hiddev_devinfo dinfo;
 	struct hid_report *report;
 	struct hid_field *field;
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct usbhid_device *usbhid = hid->tl_data;
 	void __user *user_arg = (void __user *)arg;
 	int i;
 
@@ -742,15 +752,15 @@ static struct usb_class_driver hiddev_cl
 	.minor_base =	HIDDEV_MINOR_BASE,
 };
 
-/*
- * This is where hid.c calls us to connect a hid device to the hiddev driver
- */
-int hiddev_connect(struct hid_device *hid)
+static int hiddev_connect(struct hid_device *hid)
 {
 	struct hiddev *hiddev;
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct usbhid_device *usbhid = hid->tl_data;
 	int i;
 	int retval;
+	
+	if (hid->bus != BUS_USB)
+		return -1;
 
 	for (i = 0; i < hid->maxcollection; i++)
 		if (hid->collection[i].type ==
@@ -776,8 +786,8 @@ int hiddev_connect(struct hid_device *hi
 	hiddev->hid = hid;
 	hiddev->exist = 1;
 
-	hid->minor = usbhid->intf->minor;
-	hid->hiddev = hiddev;
+	hiddev->minor = usbhid->intf->minor;
+	hid->private = hiddev;
 
 	hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev;
 
@@ -788,15 +798,17 @@ int hiddev_connect(struct hid_device *hi
  * This is where hid.c calls us to disconnect a hiddev device from the
  * corresponding hid device (usually because the usb device has disconnected)
  */
-static struct usb_class_driver hiddev_class;
-void hiddev_disconnect(struct hid_device *hid)
+static void hiddev_disconnect(struct hid_device *hid)
 {
-	struct hiddev *hiddev = hid->hiddev;
-	struct usbhid_device *usbhid = hid->driver_data;
+	struct hiddev *hiddev = hid->private;
+	struct usbhid_device *usbhid = hid->tl_data;
+
+	if (hid->bus != BUS_USB)
+		return;
 
 	hiddev->exist = 0;
 
-	hiddev_table[hiddev->hid->minor - HIDDEV_MINOR_BASE] = NULL;
+	hiddev_table[hiddev->minor - HIDDEV_MINOR_BASE] = NULL;
 	usb_deregister_dev(usbhid->intf, &hiddev_class);
 
 	if (hiddev->open) {
@@ -807,41 +819,34 @@ void hiddev_disconnect(struct hid_device
 	}
 }
 
-/* Currently this driver is a USB driver.  It's not a conventional one in
- * the sense that it doesn't probe at the USB level.  Instead it waits to
- * be connected by HID through the hiddev_connect / hiddev_disconnect
- * routines.  The reason to register as a USB device is to gain part of the
- * minor number space from the USB major.
- *
- * In theory, should the HID code be generalized to more than one physical
- * medium (say, IEEE 1384), this driver will probably need to register its
- * own major number, and in doing so, no longer need to register with USB.
- * At that point the probe routine and hiddev_driver struct below will no
- * longer be useful.
- */
-
-
-/* We never attach in this manner, and rely on HID to connect us.  This
- * is why there is no disconnect routine defined in the usb_driver either.
- */
-static int hiddev_usbd_probe(struct usb_interface *intf,
-			     const struct usb_device_id *hiddev_info)
-{
-	return -ENODEV;
-}
-
+static struct hid_hook hiddev_hook = {
+	.hid_event = hiddev_hid_event,
+};
 
-static /* const */ struct usb_driver hiddev_driver = {
-	.name =		"hiddev",
-	.probe =	hiddev_usbd_probe,
+static struct hid_driver hid_hiddev_driver = {
+	.name = "hiddev",
+	.version = DRIVER_VERSION,
+	.bus = BUS_USB,
+	.module = THIS_MODULE,
+	.probe = hiddev_connect,
+	.remove = hiddev_disconnect,
+	.hook = &hiddev_hook,
 };
 
 int __init hiddev_init(void)
 {
-	return usb_register(&hiddev_driver);
+	set_hid_driver_sticky(&hid_hiddev_driver); 
+	return hid_register_driver(&hid_hiddev_driver);
 }
 
-void hiddev_exit(void)
+void __exit hiddev_exit(void)
 {
-	usb_deregister(&hiddev_driver);
+	hid_unregister_driver(&hid_hiddev_driver);
 }
+
+module_init(hiddev_init);
+module_exit(hiddev_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hid-ff.c linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hid-ff.c
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hid-ff.c	2007-04-10 09:16:46.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hid-ff.c	1970-01-01 08:00:00.000000000 +0800
@@ -1,90 +0,0 @@
-/*
- * $Id: hid-ff.c,v 1.2 2002/04/18 22:02:47 jdeneux Exp $
- *
- *  Force feedback support for hid devices.
- *  Not all hid devices use the same protocol. For example, some use PID,
- *  other use their own proprietary procotol.
- *
- *  Copyright (c) 2002-2004 Johann Deneux
- */
-
-/*
- * 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 by
- * e-mail - mail your message to <[email protected]>
- */
-
-#include <linux/input.h>
-
-#undef DEBUG
-#include <linux/usb.h>
-
-#include <linux/hid.h>
-#include "usbhid.h"
-
-/*
- * This table contains pointers to initializers. To add support for new
- * devices, you need to add the USB vendor and product ids here.
- */
-struct hid_ff_initializer {
-	u16 idVendor;
-	u16 idProduct;
-	int (*init)(struct hid_device*);
-};
-
-/*
- * We try pidff when no other driver is found because PID is the
- * standards compliant way of implementing force feedback in HID.
- * pidff_init() will quickly abort if the device doesn't appear to
- * be a PID device
- */
-static struct hid_ff_initializer inits[] = {
-#ifdef CONFIG_LOGITECH_FF
-	{ 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */
-	{ 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */
-	{ 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */
-	{ 0x46d, 0xc294, hid_lgff_init }, /* Logitech Formula Force EX */
-	{ 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */
-	{ 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */
-#endif
-#ifdef CONFIG_PANTHERLORD_FF
-	{ 0x810, 0x0001, hid_plff_init },
-#endif
-#ifdef CONFIG_THRUSTMASTER_FF
-	{ 0x44f, 0xb300, hid_tmff_init },
-	{ 0x44f, 0xb304, hid_tmff_init },
-#endif
-#ifdef CONFIG_ZEROPLUS_FF
-	{ 0xc12, 0x0005, hid_zpff_init },
-	{ 0xc12, 0x0030, hid_zpff_init },
-#endif
-	{ 0,	 0,	 hid_pidff_init}  /* Matches anything */
-};
-
-int hid_ff_init(struct hid_device* hid)
-{
-	struct hid_ff_initializer *init;
-	int vendor = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idVendor);
-	int product = le16_to_cpu(hid_to_usb_dev(hid)->descriptor.idProduct);
-
-	for (init = inits; init->idVendor; init++)
-		if (init->idVendor == vendor && init->idProduct == product)
-			break;
-
-	return init->init(hid);
-}
-EXPORT_SYMBOL_GPL(hid_ff_init);
-
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hid-lgff.c linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hid-lgff.c
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hid-lgff.c	2007-04-10 09:16:46.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hid-lgff.c	2007-04-15 21:29:54.000000000 +0800
@@ -29,9 +29,10 @@
 
 #include <linux/input.h>
 #include <linux/usb.h>
-#include <linux/hid.h>
 #include "usbhid.h"
 
+#define DRIVER_VERSION	"0.1.0"
+
 struct dev_type {
 	u16 idVendor;
 	u16 idProduct;
@@ -99,7 +100,7 @@ static int hid_lgff_play(struct input_de
 	return 0;
 }
 
-int hid_lgff_init(struct hid_device* hid)
+static int hid_lgff_init_device(struct hid_device* hid)
 {
 	struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
 	struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
@@ -148,3 +149,53 @@ int hid_lgff_init(struct hid_device* hid
 
 	return 0;
 }
+
+static struct usb_device_id hid_lgff_ids[] = {
+	{ USB_DEVICE(0x46d, 0xc211) }, /* Logitech Cordless rumble pad */
+	{ USB_DEVICE(0x46d, 0xc219) }, /* Logitech Cordless rumble pad 2 */
+	{ USB_DEVICE(0x46d, 0xc283) }, /* Logitech Wingman Force 3d */
+	{ USB_DEVICE(0x46d, 0xc294) }, /* Logitech Formula Force EX */
+	{ USB_DEVICE(0x46d, 0xc295) }, /* Logitech MOMO force wheel */
+	{ USB_DEVICE(0x46d, 0xca03) }, /* Logitech MOMO force wheel */
+	{}
+};
+
+MODULE_DEVICE_TABLE (usb, hid_lgff_ids);
+
+static int hid_lgff_match(struct hid_driver *driver, struct hid_device *device)
+{
+	struct usbhid_device *usbhid;
+
+	usbhid = device->tl_data;
+	return (int)usb_match_id(usbhid->intf, hid_lgff_ids);
+}
+
+static int hid_lgff_probe(struct hid_device *device)
+{
+	if (hidinput_connect(device))
+		return -ENODEV;
+	return hid_lgff_init_device(device);
+}
+
+static struct hid_driver hid_lgff_driver = {
+	.name = "hid-lgff",
+	.version = DRIVER_VERSION,
+	.bus = BUS_USB,
+	.module = THIS_MODULE,
+	.match = hid_lgff_match,
+	.probe = hid_lgff_probe,
+};
+
+static int __init hid_lgff_init(void)
+{
+	return hid_register_driver(&hid_lgff_driver);
+}
+
+static void __exit hid_lgff_exit(void)
+{
+	hid_unregister_driver(&hid_lgff_driver);
+}
+
+module_init(hid_lgff_init);
+module_exit(hid_lgff_exit);
+MODULE_LICENSE("GPL");
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hid-pb.c linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hid-pb.c
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hid-pb.c	1970-01-01 08:00:00.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hid-pb.c	2007-04-15 21:30:02.000000000 +0800
@@ -0,0 +1,334 @@
+/*
+ * $Id: hid-input.c,v 1.2 2002/04/23 00:59:25 rdamazio Exp $
+ *
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *  Copyright (c) 2006-2007 Jiri Kosina
+ *
+ *  iBook/PowerBook special keys support.
+ */
+
+/*
+ * 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 <[email protected]>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/usb.h>
+#include <linux/input.h>
+
+#include "usbhid.h"
+
+#define DRIVER_VERSION	"0.1.0"
+
+static int hid_pb_fnmode = 1;
+module_param_named(pb_fnmode, hid_pb_fnmode, int, 0644);
+MODULE_PARM_DESC(pb_fnmode,
+		"Mode of fn key on PowerBooks (0 = disabled, 1 = fkeyslast, 2 = fkeysfirst)");
+
+#define USB_VENDOR_ID_APPLE		0x05ac
+#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE	0x0304
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI	0x020e
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO	0x020f
+#define USB_DEVICE_ID_APPLE_GEYSER_ANSI	0x0214
+#define USB_DEVICE_ID_APPLE_GEYSER_ISO	0x0215
+#define USB_DEVICE_ID_APPLE_GEYSER_JIS	0x0216
+#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI	0x0217
+#define USB_DEVICE_ID_APPLE_GEYSER3_ISO	0x0218
+#define USB_DEVICE_ID_APPLE_GEYSER3_JIS	0x0219
+#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI	0x021a
+#define USB_DEVICE_ID_APPLE_GEYSER4_ISO	0x021b
+#define USB_DEVICE_ID_APPLE_GEYSER4_JIS	0x021c
+#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY	0x030a
+#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY	0x030b
+#define USB_DEVICE_ID_APPLE_IR		0x8240
+
+#define USB_VENDOR_ID_CHERRY		0x046a
+#define USB_DEVICE_ID_CHERRY_CYMOTION	0x0023
+
+struct hids_pb_data {
+	unsigned long pressed_fn[NBITS(KEY_MAX)];
+	unsigned long pressed_numlock[NBITS(KEY_MAX)];
+};
+
+static const struct pb_feature {
+	__u16 idProduct;
+	unsigned quirks;
+} pb_feature_list[] = {
+	{ USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_IGNORE_MOUSE },
+	{ USB_DEVICE_ID_APPLE_FOUNTAIN_ISO,  HID_QUIRK_IGNORE_MOUSE },
+	{ USB_DEVICE_ID_APPLE_GEYSER_ANSI,  HID_QUIRK_IGNORE_MOUSE },
+	{ USB_DEVICE_ID_APPLE_GEYSER_ISO,  HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
+	{ USB_DEVICE_ID_APPLE_GEYSER_JIS,  HID_QUIRK_IGNORE_MOUSE },
+	{ USB_DEVICE_ID_APPLE_GEYSER3_ANSI,  HID_QUIRK_IGNORE_MOUSE },
+	{ USB_DEVICE_ID_APPLE_GEYSER3_ISO,  HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
+	{ USB_DEVICE_ID_APPLE_GEYSER3_JIS,  HID_QUIRK_IGNORE_MOUSE },
+	{ USB_DEVICE_ID_APPLE_GEYSER4_ANSI,  HID_QUIRK_IGNORE_MOUSE },
+	{ USB_DEVICE_ID_APPLE_GEYSER4_ISO,  HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD},
+	{ USB_DEVICE_ID_APPLE_GEYSER4_JIS,  HID_QUIRK_IGNORE_MOUSE },
+	{ USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY,  HID_QUIRK_IGNORE_MOUSE },
+	{ USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY,  HID_QUIRK_IGNORE_MOUSE },
+	{ 0, 0 }
+};
+
+struct hidinput_key_translation {
+	u16 from;
+	u16 to;
+	u8 flags;
+};
+
+#define POWERBOOK_FLAG_FKEY 0x01
+
+static struct hidinput_key_translation powerbook_fn_keys[] = {
+	{ KEY_BACKSPACE, KEY_DELETE },
+	{ KEY_F1,       KEY_BRIGHTNESSDOWN,     POWERBOOK_FLAG_FKEY },
+	{ KEY_F2,       KEY_BRIGHTNESSUP,       POWERBOOK_FLAG_FKEY },
+	{ KEY_F3,       KEY_MUTE,               POWERBOOK_FLAG_FKEY },
+	{ KEY_F4,       KEY_VOLUMEDOWN,         POWERBOOK_FLAG_FKEY },
+	{ KEY_F5,       KEY_VOLUMEUP,           POWERBOOK_FLAG_FKEY },
+	{ KEY_F6,       KEY_NUMLOCK,            POWERBOOK_FLAG_FKEY },
+	{ KEY_F7,       KEY_SWITCHVIDEOMODE,    POWERBOOK_FLAG_FKEY },
+	{ KEY_F8,       KEY_KBDILLUMTOGGLE,     POWERBOOK_FLAG_FKEY },
+	{ KEY_F9,       KEY_KBDILLUMDOWN,       POWERBOOK_FLAG_FKEY },
+	{ KEY_F10,      KEY_KBDILLUMUP,         POWERBOOK_FLAG_FKEY },
+	{ KEY_UP,       KEY_PAGEUP },
+	{ KEY_DOWN,     KEY_PAGEDOWN },
+	{ KEY_LEFT,     KEY_HOME },
+	{ KEY_RIGHT,    KEY_END },
+	{ }
+};
+
+static struct hidinput_key_translation powerbook_numlock_keys[] = {
+	{ KEY_J,        KEY_KP1 },
+	{ KEY_K,        KEY_KP2 },
+	{ KEY_L,        KEY_KP3 },
+	{ KEY_U,        KEY_KP4 },
+	{ KEY_I,        KEY_KP5 },
+	{ KEY_O,        KEY_KP6 },
+	{ KEY_7,        KEY_KP7 },
+	{ KEY_8,        KEY_KP8 },
+	{ KEY_9,        KEY_KP9 },
+	{ KEY_M,        KEY_KP0 },
+	{ KEY_DOT,      KEY_KPDOT },
+	{ KEY_SLASH,    KEY_KPPLUS },
+	{ KEY_SEMICOLON, KEY_KPMINUS },
+	{ KEY_P,        KEY_KPASTERISK },
+	{ KEY_MINUS,    KEY_KPEQUAL },
+	{ KEY_0,        KEY_KPSLASH },
+	{ KEY_F6,       KEY_NUMLOCK },
+	{ KEY_KPENTER,  KEY_KPENTER },
+	{ KEY_BACKSPACE, KEY_BACKSPACE },
+	{ }
+};
+
+static struct hidinput_key_translation powerbook_iso_keyboard[] = {
+	{ KEY_GRAVE,    KEY_102ND },
+	{ KEY_102ND,    KEY_GRAVE },
+	{ }
+};
+
+static struct hidinput_key_translation *find_translation(struct hidinput_key_translation *table, u16 from)
+{
+	struct hidinput_key_translation *trans;
+
+	/* Look for the translation */
+	for (trans = table; trans->from; trans++)
+		if (trans->from == from)
+			return trans;
+
+	return NULL;
+}
+
+static int hids_pb_hid_event(struct hid_field *field, struct hid_usage *usage,
+							__s32 value, int interrupt)
+{
+	struct hidinput_key_translation *trans;
+	struct hid_input *hidinput = field->hidinput;
+	struct hid_device *hid = field->report->device;
+	struct hids_pb_data *pb_data = hid->driver->private;
+	
+	if (!pb_data)
+		return -1;
+	
+	if (usage->code == KEY_FN) {
+		if (value) hid->quirks |=  HID_QUIRK_POWERBOOK_FN_ON;
+		else       hid->quirks &= ~HID_QUIRK_POWERBOOK_FN_ON;
+
+		input_event(hidinput->input, usage->type, usage->code, value);
+		return 0;
+	}
+
+	if (hid_pb_fnmode) {
+		int do_translate;
+
+		trans = find_translation(powerbook_fn_keys, usage->code);
+		if (trans) {
+			if (test_bit(usage->code, pb_data->pressed_fn))
+				do_translate = 1;
+			else if (trans->flags & POWERBOOK_FLAG_FKEY)
+				do_translate =
+					(hid_pb_fnmode == 2 &&  (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)) ||
+					(hid_pb_fnmode == 1 && !(hid->quirks & HID_QUIRK_POWERBOOK_FN_ON));
+			else
+				do_translate = (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON);
+
+			if (do_translate) {
+				if (value)
+					set_bit(usage->code, pb_data->pressed_fn);
+				else
+					clear_bit(usage->code, pb_data->pressed_fn);
+
+				input_event(hidinput->input, usage->type,
+								trans->to, value);
+
+				return 0;
+			}
+		}
+
+		if (test_bit(usage->code, pb_data->pressed_numlock) ||
+				test_bit(LED_NUML, hidinput->input->led)) {
+			trans = find_translation(powerbook_numlock_keys, usage->code);
+
+			if (trans) {
+				if (value)
+					set_bit(usage->code, pb_data->pressed_numlock);
+				else
+					clear_bit(usage->code, pb_data->pressed_numlock);
+
+				input_event(hidinput->input, usage->type, trans->to, value);
+			}
+			return 0;
+		}
+	}
+
+	if (hid->quirks & HID_QUIRK_POWERBOOK_ISO_KEYBOARD) {
+		trans = find_translation(powerbook_iso_keyboard, usage->code);
+		if (trans) {
+			input_event(hidinput->input, usage->type, trans->to, value);
+			return 0;
+		}
+	}
+	
+	hidinput_hid_event(hid, field, usage, value);
+	return 0;
+}
+
+static void hidinput_pb_setup(struct input_dev *input)
+{
+	struct hidinput_key_translation *trans;
+
+	set_bit(KEY_NUMLOCK, input->keybit);
+
+	/* Enable all needed keys */
+	for (trans = powerbook_fn_keys; trans->from; trans++)
+		set_bit(trans->to, input->keybit);
+
+	for (trans = powerbook_numlock_keys; trans->from; trans++)
+		set_bit(trans->to, input->keybit);
+
+	for (trans = powerbook_iso_keyboard; trans->from; trans++)
+		set_bit(trans->to, input->keybit);
+}
+
+static struct usb_device_id hids_pb_ids[] = {
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) },
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI) },
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO) },
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS) },	
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI) },
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO) },
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS) },
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI) },
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO) },	
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS) },
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY) },
+	{ USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) },
+	{}
+};
+MODULE_DEVICE_TABLE (usb, hids_pb_ids);
+
+static int hid_pb_match(struct hid_driver *driver, struct hid_device *device)
+{
+	struct usbhid_device *usbhid;
+
+	if (!driver || !device)
+		return 0;
+	usbhid = device->tl_data;
+	return !!usb_match_id(usbhid->intf, hids_pb_ids);
+}
+
+static int hid_pb_probe(struct hid_device *dev)
+{
+	int i;
+	struct hid_driver *drv = dev->driver;
+
+	for (i=0; pb_feature_list[i].quirks; ++i) {
+		if (pb_feature_list[i].idProduct == dev->product)
+			dev->quirks = pb_feature_list[i].quirks;
+	}
+	drv->private = kzalloc(sizeof(struct hids_pb_data), GFP_KERNEL);
+	if (!drv->private)
+		return 0;
+	return hidinput_connect(dev);
+}
+
+static void hids_pb_setup_usage(struct hid_field *field, struct hid_usage *usage)
+{
+	unsigned long *bit;
+	struct input_dev* input = field->hidinput->input;
+	
+	if (0x003 != (usage->hid & HID_USAGE))
+		return;
+	usage->code = KEY_FN;
+	usage->type = EV_KEY;
+	bit = input->keybit;
+	clear_bit(KEY_FN, input->keybit);
+	hidinput_pb_setup(input);
+}
+
+static struct hid_hook hid_pb_hook = {
+	.hid_event = hids_pb_hid_event,
+	.setup_usage = hids_pb_setup_usage,
+};
+
+static struct hid_driver hids_pb_driver = {
+	.name = "hid-pb",
+	.version = DRIVER_VERSION,
+	.bus = BUS_USB,
+	.module = THIS_MODULE,
+	.match = hid_pb_match,
+	.probe = hid_pb_probe,
+	.hook = &hid_pb_hook,
+};
+
+static int __init hids_pb_init(void)
+{
+	return hid_register_driver(&hids_pb_driver);
+}
+
+static void __exit hids_pb_exit(void)
+{
+	hid_unregister_driver(&hids_pb_driver);
+}
+
+module_init(hids_pb_init);
+module_exit(hids_pb_exit);
+MODULE_LICENSE("GPL");
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hid-pidff.c linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hid-pidff.c
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hid-pidff.c	2007-04-10 09:16:46.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hid-pidff.c	2007-04-15 21:30:14.000000000 +0800
@@ -27,10 +27,10 @@
 #include <linux/input.h>
 #include <linux/usb.h>
 
-#include <linux/hid.h>
-
 #include "usbhid.h"
 
+#define DRIVER_VERSION	"0.1.0"
+
 #define	PID_EFFECTS_MAX		64
 
 /* Report usage table used to put reports into an array */
@@ -1236,7 +1236,7 @@ static int pidff_check_autocenter(struct
 /*
  * Check if the device is PID and initialize it
  */
-int hid_pidff_init(struct hid_device *hid)
+int hid_pidff_init_device(struct hid_device *hid)
 {
 	struct pidff_device *pidff;
 	struct hid_input *hidinput = list_entry(hid->inputs.next,
@@ -1329,3 +1329,33 @@ int hid_pidff_init(struct hid_device *hi
 	kfree(pidff);
 	return error;
 }
+
+static int hid_pidff_probe(struct hid_device *device)
+{
+	if (hidinput_connect(device))
+		return -ENODEV;
+	return hid_pidff_init_device(device);
+}
+
+static struct hid_driver hid_pidff_driver = {
+	.name = "hid-pidff",
+	.version = DRIVER_VERSION,
+	.bus = BUS_USB,
+	.module = THIS_MODULE,
+	.probe = hid_pidff_probe,
+};
+
+static int __init hid_pidff_init(void)
+{
+	set_hid_driver_sticky(&hid_pidff_driver);
+	return hid_register_driver(&hid_pidff_driver);
+}
+
+static void __exit hid_pidff_exit(void)
+{
+	hid_unregister_driver(&hid_pidff_driver);
+}
+
+module_init(hid_pidff_init);
+module_exit(hid_pidff_exit);
+MODULE_LICENSE("GPL");
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hid-plff.c linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hid-plff.c
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hid-plff.c	2007-04-10 09:16:46.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hid-plff.c	2007-04-15 21:30:26.000000000 +0800
@@ -27,9 +27,10 @@
 
 #include <linux/input.h>
 #include <linux/usb.h>
-#include <linux/hid.h>
 #include "usbhid.h"
 
+#define DRIVER_VERSION	"0.1.0"
+
 struct plff_device {
 	struct hid_report *report;
 };
@@ -56,7 +57,7 @@ static int hid_plff_play(struct input_de
 	return 0;
 }
 
-int hid_plff_init(struct hid_device *hid)
+int hid_plff_init_device(struct hid_device *hid)
 {
 	struct plff_device *plff;
 	struct hid_report *report;
@@ -127,3 +128,48 @@ int hid_plff_init(struct hid_device *hid
 
 	return 0;
 }
+
+static struct usb_device_id hid_plff_ids[] = {
+	{ USB_DEVICE(0x810, 0x0001) },
+	{}
+};
+
+MODULE_DEVICE_TABLE (usb, hid_plff_ids);
+
+static int hid_plff_match(struct hid_driver *driver, struct hid_device *device)
+{
+	struct usbhid_device *usbhid;
+
+	usbhid = device->tl_data;
+	return (int)usb_match_id(usbhid->intf, hid_plff_ids);
+}
+
+static int hid_plff_probe(struct hid_device *device)
+{
+	if (hidinput_connect(device))
+		return -ENODEV;
+	return hid_plff_init_device(device);
+}
+
+static struct hid_driver hid_plff_driver = {
+	.name = "hid-plff",
+	.version = DRIVER_VERSION,
+	.bus = BUS_USB,
+	.module = THIS_MODULE,
+	.match = hid_plff_match,
+	.probe = hid_plff_probe,
+};
+
+static int __init hid_plff_init(void)
+{
+	return hid_register_driver(&hid_plff_driver);
+}
+
+static void __exit hid_plff_exit(void)
+{
+	hid_unregister_driver(&hid_plff_driver);
+}
+
+module_init(hid_plff_init);
+module_exit(hid_plff_exit);
+MODULE_LICENSE("GPL");
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hid-tmff.c linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hid-tmff.c
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hid-tmff.c	2007-04-10 09:16:46.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hid-tmff.c	2007-04-15 21:30:35.000000000 +0800
@@ -32,9 +32,10 @@
 #undef DEBUG
 #include <linux/usb.h>
 
-#include <linux/hid.h>
 #include "usbhid.h"
 
+#define DRIVER_VERSION "0.1.0"
+
 /* Usages for thrustmaster devices I know about */
 #define THRUSTMASTER_USAGE_RUMBLE_LR	(HID_UP_GENDESK | 0xbb)
 
@@ -76,7 +77,7 @@ static int hid_tmff_play(struct input_de
 	return 0;
 }
 
-int hid_tmff_init(struct hid_device *hid)
+int hid_tmff_init_device(struct hid_device *hid)
 {
 	struct tmff_device *tmff;
 	struct list_head *pos;
@@ -145,3 +146,48 @@ int hid_tmff_init(struct hid_device *hid
 	return 0;
 }
 
+
+static struct usb_device_id hid_tmff_ids[] = {
+	{ USB_DEVICE(0x44f, 0xb304) },
+	{}
+};
+
+MODULE_DEVICE_TABLE (usb, hid_tmff_ids);
+
+static int hid_tmff_match(struct hid_driver *driver, struct hid_device *device)
+{
+	struct usbhid_device *usbhid;
+
+	usbhid = device->tl_data;
+	return (int)usb_match_id(usbhid->intf, hid_tmff_ids);
+}
+
+static int hid_tmff_probe(struct hid_device *device)
+{
+	if (hidinput_connect(device))
+		return -ENODEV;
+	return hid_tmff_init_device(device);
+}
+
+static struct hid_driver hid_tmff_driver = {
+	.name = "hid-tmff",
+	.version = DRIVER_VERSION,
+	.bus = BUS_USB,
+	.module = THIS_MODULE,
+	.match = hid_tmff_match,
+	.probe = hid_tmff_probe,
+};
+
+static int __init hid_tmff_init(void)
+{
+	return hid_register_driver(&hid_tmff_driver);
+}
+
+static void __exit hid_tmff_exit(void)
+{
+	hid_unregister_driver(&hid_tmff_driver);
+}
+
+module_init(hid_tmff_init);
+module_exit(hid_tmff_exit);
+MODULE_LICENSE("GPL");
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hid-zpff.c linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hid-zpff.c
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/hid-zpff.c	2007-04-10 09:16:46.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/hid-zpff.c	2007-04-15 21:30:44.000000000 +0800
@@ -27,9 +27,10 @@
 
 #include <linux/input.h>
 #include <linux/usb.h>
-#include <linux/hid.h>
 #include "usbhid.h"
 
+#define DRIVER_VERSION	"0.1.0"
+
 struct zpff_device {
 	struct hid_report *report;
 };
@@ -62,7 +63,7 @@ static int hid_zpff_play(struct input_de
 	return 0;
 }
 
-int hid_zpff_init(struct hid_device *hid)
+int hid_zpff_init_device(struct hid_device *hid)
 {
 	struct zpff_device *zpff;
 	struct hid_report *report;
@@ -109,3 +110,50 @@ int hid_zpff_init(struct hid_device *hid
 
 	return 0;
 }
+
+static struct usb_device_id hid_zpff_ids[] = {
+	{ USB_DEVICE(0x46d, 0x0005) },
+	{ USB_DEVICE(0xc12, 0x0030) },
+	{}
+};
+
+MODULE_DEVICE_TABLE (usb, hid_zpff_ids);
+
+static int hid_zpff_match(struct hid_driver *driver, struct hid_device *device)
+{
+	struct usbhid_device *usbhid;
+
+	usbhid = device->tl_data;
+	return (int)usb_match_id(usbhid->intf, hid_zpff_ids);
+}
+
+static int hid_zpff_probe(struct hid_device *device)
+{
+	if (hidinput_connect(device))
+		return -ENODEV;
+	return hid_zpff_init_device(device);
+}
+
+
+static struct hid_driver hid_zpff_driver = {
+	.name = "hid-zpff",
+	.version = DRIVER_VERSION,
+	.bus = BUS_USB,
+	.module = THIS_MODULE,
+	.match = hid_zpff_match,
+	.probe = hid_zpff_probe,
+};
+
+static int __init hid_zpff_init(void)
+{
+	return hid_register_driver(&hid_zpff_driver);
+}
+
+static void __exit hid_zpff_exit(void)
+{
+	hid_unregister_driver(&hid_zpff_driver);
+}
+
+module_init(hid_zpff_init);
+module_exit(hid_zpff_exit);
+MODULE_LICENSE("GPL");
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/Kconfig linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/Kconfig
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/Kconfig	2007-04-10 09:16:46.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/Kconfig	2007-04-10 10:24:51.000000000 +0800
@@ -24,8 +24,8 @@ config USB_HID
 comment "Input core support is needed for USB HID input layer or HIDBP support"
 	depends on USB_HID && INPUT=n
 
-config USB_HIDINPUT_POWERBOOK
-	bool "Enable support for iBook/PowerBook special keys"
+config POWERBOOK
+	tristate "Enable support for iBook/PowerBook special keys"
 	default n
 	depends on USB_HID
 	help
@@ -34,29 +34,17 @@ config USB_HIDINPUT_POWERBOOK
 
 	  If unsure, say N.
 
-config HID_FF
-	bool "Force feedback support (EXPERIMENTAL)"
-	depends on USB_HID && EXPERIMENTAL
-	help
-	  Say Y here is you want force feedback support for a few HID devices.
-	  See below for a list of supported devices.
-
-	  See <file:Documentation/input/ff.txt> for a description of the force
-	  feedback API.
-
-	  If unsure, say N.
-
 config HID_PID
-	bool "PID device support"
-	depends on HID_FF
+	tristate "PID device support"
+	depends on USB_HID
 	help
 	  Say Y here if you have a PID-compliant device and wish to enable force
 	  feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such
 	  devices.
 
 config LOGITECH_FF
-	bool "Logitech devices support"
-	depends on HID_FF
+	tristate "Logitech devices support"
+	depends on USB_HID
 	select INPUT_FF_MEMLESS if USB_HID
 	help
 	  Say Y here if you have one of these devices:
@@ -71,16 +59,16 @@ config LOGITECH_FF
 	  force feedback.
 
 config PANTHERLORD_FF
-	bool "PantherLord USB/PS2 2in1 Adapter support"
-	depends on HID_FF
+	tristate "PantherLord USB/PS2 2in1 Adapter support"
+	depends on USB_HID
 	select INPUT_FF_MEMLESS if USB_HID
 	help
 	  Say Y here if you have a PantherLord USB/PS2 2in1 Adapter and want
 	  to enable force feedback support for it.
 
 config THRUSTMASTER_FF
-	bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)"
-	depends on HID_FF && EXPERIMENTAL
+	tristate "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)"
+	depends on USB_HID && EXPERIMENTAL
 	select INPUT_FF_MEMLESS if USB_HID
 	help
 	  Say Y here if you have a THRUSTMASTER FireStore Dual Power 2,
@@ -89,15 +77,15 @@ config THRUSTMASTER_FF
 	  force feedback.
 
 config ZEROPLUS_FF
-	bool "Zeroplus based game controller support"
-	depends on HID_FF
+	tristate "Zeroplus based game controller support"
+	depends on USB_HID
 	select INPUT_FF_MEMLESS if USB_HID
 	help
 	  Say Y here if you have a Zeroplus based game controller and want to
 	  enable force feedback for it.
 
 config USB_HIDDEV
-	bool "/dev/hiddev raw HID device support"
+	tristate "/dev/hiddev raw HID device support"
 	depends on USB_HID
 	help
 	  Say Y here if you want to support HID devices (from the USB
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/Makefile linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/Makefile
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/Makefile	2007-04-10 09:16:46.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/Makefile	2007-04-17 09:23:33.000000000 +0800
@@ -6,29 +6,13 @@
 usbhid-objs	:= hid-core.o
 
 # Optional parts of multipart objects.
-
-ifeq ($(CONFIG_USB_HIDDEV),y)
-	usbhid-objs	+= hiddev.o
-endif
-ifeq ($(CONFIG_HID_PID),y)
-	usbhid-objs	+= hid-pidff.o
-endif
-ifeq ($(CONFIG_LOGITECH_FF),y)
-	usbhid-objs	+= hid-lgff.o
-endif
-ifeq ($(CONFIG_PANTHERLORD_FF),y)
-	usbhid-objs	+= hid-plff.o
-endif
-ifeq ($(CONFIG_THRUSTMASTER_FF),y)
-	usbhid-objs	+= hid-tmff.o
-endif
-ifeq ($(CONFIG_ZEROPLUS_FF),y)
-	usbhid-objs	+= hid-zpff.o
-endif
-ifeq ($(CONFIG_HID_FF),y)
-	usbhid-objs	+= hid-ff.o
-endif
-
+obj-$(CONFIG_HID_PID)		+= hid-pidff.o
+obj-$(CONFIG_THRUSTMASTER_FF)   += hid-tmff.o
+obj-$(CONFIG_ZEROPLUS_FF)       += hid-zpff.o
+obj-$(CONFIG_POWERBOOK)		+= hid-pb.o
+obj-$(CONFIG_LOGITECH_FF)       += hid-lgff.o
+obj-$(CONFIG_PANTHERLORD_FF)    += hid-plff.o
+obj-$(CONFIG_USB_HIDDEV)	+= hiddev.o
 obj-$(CONFIG_USB_HID)		+= usbhid.o
 obj-$(CONFIG_USB_KBD)		+= usbkbd.o
 obj-$(CONFIG_USB_MOUSE)		+= usbmouse.o
diff -Naurp linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/usbhid.h linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/usbhid.h
--- linux-2.6.21-rc6-mm1.orig/drivers/hid/usbhid/usbhid.h	2007-04-10 09:16:46.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/drivers/hid/usbhid/usbhid.h	2007-04-10 10:24:51.000000000 +0800
@@ -30,6 +30,9 @@
 #include <linux/timer.h>
 #include <linux/workqueue.h>
 #include <linux/input.h>
+#include <linux/wait.h>
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
 
 /*  API provided by hid-core.c for USB HID drivers */
 int usbhid_wait_io(struct hid_device* hid);
@@ -77,6 +80,7 @@ struct usbhid_device {
 	unsigned long stop_retry;                                       /* Time to give up, in jiffies */
 	unsigned int retry_delay;                                       /* Delay length in ms */
 	struct work_struct reset_work;                                  /* Task context for resets */
+	wait_queue_head_t wait;
 
 };
hidbus.bluetooth.patch (text/x-patch, 4.9 KB)
diff -Naurp linux-2.6.21-rc6-mm1.orig/net/bluetooth/hidp/core.c linux-2.6.21-rc6-mm1.new/net/bluetooth/hidp/core.c
--- linux-2.6.21-rc6-mm1.orig/net/bluetooth/hidp/core.c	2007-04-10 09:16:51.000000000 +0800
+++ linux-2.6.21-rc6-mm1.new/net/bluetooth/hidp/core.c	2007-04-17 09:19:27.000000000 +0800
@@ -54,6 +54,7 @@
 
 #define VERSION "1.2"
 
+static DECLARE_WAIT_QUEUE_HEAD(hidp_session_clean_barrier);
 static DECLARE_RWSEM(hidp_session_sem);
 static LIST_HEAD(hidp_session_list);
 
@@ -78,6 +79,15 @@ static unsigned char hidp_keycode[256] =
 
 static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 };
 
+static void hidp_update_busid(struct hid_device *dev)
+{
+	static atomic_t hidp_hiddev_idx = ATOMIC_INIT(-1);
+
+	snprintf(dev->device.bus_id, BUS_ID_SIZE, "B:%04x:%04x:%d",
+					dev->vendor, dev->product,
+				atomic_add_return(1, &hidp_hiddev_idx));
+}
+
 static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr)
 {
 	struct hidp_session *session;
@@ -176,7 +186,7 @@ static inline int hidp_queue_event(struc
 static int hidp_hidinput_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
 {
 	struct hid_device *hid = dev->private;
-	struct hidp_session *session = hid->driver_data;
+	struct hidp_session *session = hid->tl_data;
 
 	return hidp_queue_event(session, dev, type, code, value);
 }
@@ -544,6 +554,11 @@ static int hidp_session(void *arg)
 	if (session->hid) {
 		vendor  = session->hid->vendor;
 		product = session->hid->product;
+		session->hid->module = THIS_MODULE;
+		if (hid_register_device(session->hid)) {
+			hid_free_device(session->hid);
+			session->hid = NULL;
+		}
 	}
 
 	daemonize("khidpd_%04x%04x", vendor, product);
@@ -598,14 +613,17 @@ static int hidp_session(void *arg)
 	}
 
 	if (session->hid) {
-		if (session->hid->claimed & HID_CLAIMED_INPUT)
-			hidinput_disconnect(session->hid);
+		hid_unregister_device(session->hid);
 		hid_free_device(session->hid);
 	}
 
 	up_write(&hidp_session_sem);
 
 	kfree(session);
+
+	if (list_empty(&hidp_session_list))
+		wake_up(&hidp_session_clean_barrier);
+
 	return 0;
 }
 
@@ -711,7 +729,7 @@ static inline void hidp_setup_hid(struct
 	baswap(&src, &bt_sk(session->ctrl_sock->sk)->src);
 	baswap(&dst, &bt_sk(session->ctrl_sock->sk)->dst);
 
-	hid->driver_data = session;
+	hid->tl_data = session;
 
 	hid->country = req->country;
 
@@ -726,11 +744,6 @@ static inline void hidp_setup_hid(struct
 
 	hid->dev = hidp_get_device(session);
 
-	hid->hid_open  = hidp_open;
-	hid->hid_close = hidp_close;
-
-	hid->hidinput_input_event = hidp_hidinput_event;
-
 	hidp_setup_quirks(hid);
 
 	list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT].report_list, list)
@@ -738,11 +751,6 @@ static inline void hidp_setup_hid(struct
 
 	list_for_each_entry(report, &hid->report_enum[HID_FEATURE_REPORT].report_list, list)
 		hidp_send_report(session, report);
-
-	if (hidinput_connect(hid) == 0) {
-		hid->claimed |= HID_CLAIMED_INPUT;
-		hid_ff_init(hid);
-	}
 }
 
 int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
@@ -868,6 +876,10 @@ failed:
 
 	kfree(session->input);
 	kfree(session);
+
+	if (list_empty(&hidp_session_list))
+		wake_up(&hidp_session_clean_barrier);
+
 	return err;
 }
 
@@ -951,8 +963,57 @@ int hidp_get_conninfo(struct hidp_connin
 	return err;
 }
 
+static struct hid_driver hid_bt_driver = {
+	.name = "hidbt",
+	.version = "0.9.0",
+	.bus = BUS_BLUETOOTH,
+	.module = THIS_MODULE,
+	.match = NULL, /* default match any bluetooth hidp device. */
+	.probe = NULL, /* default is hidinput_connect() */
+	.remove = NULL, /* default is hidinput_disconnect() */
+};
+
+static struct hid_transport hid_bt_transport = {
+	.bus = BUS_BLUETOOTH,
+	.event = hidp_hidinput_event,
+	.open = hidp_open,
+	.close = hidp_close,
+	.update_busid = hidp_update_busid,
+	.module = THIS_MODULE,
+};
+
+static int is_hidp_session_list_empty(void)
+{
+	int ret;
+
+	down_read(&hidp_session_sem);
+	ret = list_empty(&hidp_session_list);
+	up_read(&hidp_session_sem);
+	return ret;
+}
+
+static void hidp_session_clean(void)
+{
+	struct hidp_session *session;
+	
+	down_read(&hidp_session_sem);
+	list_for_each_entry(session, &hidp_session_list, list) {
+		atomic_inc(&session->terminate);
+		hidp_schedule(session);
+	}
+	up_read(&hidp_session_sem);
+}
+
+
 static int __init hidp_init(void)
 {
+	if (hid_register_transport(&hid_bt_transport))
+		return -ENODEV;
+	if (hid_register_driver(&hid_bt_driver)) {
+		hid_unregister_transport(&hid_bt_transport);
+		return -ENODEV;
+	}
+
 	l2cap_load();
 
 	BT_INFO("HIDP (Human Interface Emulation) ver %s", VERSION);
@@ -962,6 +1023,11 @@ static int __init hidp_init(void)
 
 static void __exit hidp_exit(void)
 {
+	hidp_session_clean();
+	wait_event(hidp_session_clean_barrier, is_hidp_session_list_empty());
+
+	hid_unregister_transport(&hid_bt_transport);
+	hid_unregister_driver(&hid_bt_driver);
 	hidp_cleanup_sockets();
 }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.