[PATCH 2/5]:hidbus prototype 070409:core

"Li Yu" <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
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/

-------------------------------------------------------------------------
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
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.