Here is a cleaned up version of the maple mouse driver - Dmitry, I tried
using open and close functions as per the joystick but they seemed to
get in the way of proper mouse operation. As it is the joystick does
null out the callback pointer on removal of the driver, which seems to
me to be the correct behaviour.
Add support for the Maple mouse on the SEGA Dreamcast.
Signed-off-by: Adrian McMenamin <[email protected]>
---
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 4e99342..71cc98a 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -286,4 +286,15 @@ config MOUSE_GPIO
To compile this driver as a module, choose M here: the
module will be called gpio_mouse.
+config MOUSE_MAPLE
+ tristate "Maple mouse (for the Dreamcast)"
+ depends on MAPLE
+ help
+ This driver supports the Maple mouse on the SEGA Dreamcast.
+
+ Most Dreamcast users, who have a mouse, will say Y here.
+
+ To compile this driver as a module choose M here: the module will be
+ called maplemouse.
+
endif
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 96f1dd8..0272f5d 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
+obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o
psmouse-objs := psmouse-base.o synaptics.o
diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c
new file mode 100644
index 0000000..c714fa3
--- /dev/null
+++ b/drivers/input/mouse/maplemouse.c
@@ -0,0 +1,136 @@
+/*
+ * SEGA Dreamcast mouse driver
+ * Based on drivers/usb/usbmouse.c
+ *
+ * Copyright Yaegashi Takeshi, 2001
+ * Adrian McMenamin, 2008
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/maple.h>
+
+MODULE_AUTHOR("Adrian McMenamin <[email protected]>");
+MODULE_DESCRIPTION("SEGA Dreamcast mouse driver");
+MODULE_LICENSE("GPL");
+
+struct dc_mouse {
+ struct input_dev *dev;
+ struct maple_device *mdev;
+};
+
+static void dc_mouse_callback(struct mapleq *mq)
+{
+ int buttons, relx, rely, relz;
+ struct maple_device *mapledev = mq->dev;
+ struct dc_mouse *mse = maple_get_drvdata(mapledev);
+ struct input_dev *dev = mse->dev;
+ unsigned char *res = mq->recvbuf;
+
+ buttons = ~res[8];
+ relx = *(unsigned short *)(res + 12) - 512;
+ rely = *(unsigned short *)(res + 14) - 512;
+ relz = *(unsigned short *)(res + 16) - 512;
+
+ input_report_key(dev, BTN_LEFT, buttons & 4);
+ input_report_key(dev, BTN_MIDDLE, buttons & 9);
+ input_report_key(dev, BTN_RIGHT, buttons & 2);
+ input_report_rel(dev, REL_X, relx);
+ input_report_rel(dev, REL_Y, rely);
+ input_report_rel(dev, REL_WHEEL, relz);
+ input_sync(dev);
+}
+
+/* allow the mouse to be used */
+static int __devinit probe_maple_mouse(struct device *dev)
+{
+ struct maple_device *mdev;
+ struct maple_driver *mdrv;
+ int error;
+ struct input_dev *input_dev;
+ struct dc_mouse *mse;
+
+ mdev = to_maple_dev(dev);
+ mdrv = to_maple_driver(dev->driver);
+
+ mse = kzalloc(sizeof(struct dc_mouse), GFP_KERNEL);
+ if (!mse) {
+ error = -ENOMEM;
+ goto fail;
+ }
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ error = -ENOMEM;
+ goto fail_nomem;
+ }
+
+ mse->dev = input_dev;
+ mse->mdev = mdev;
+
+ input_set_drvdata(input_dev, mse);
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
+ input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) |
+ BIT_MASK(REL_WHEEL);
+ input_dev->name = mdev->product_name;
+ input_dev->id.bustype = BUS_HOST;
+ error = input_register_device(input_dev);
+ if (error)
+ goto fail_register;
+
+ mdev->driver = mdrv;
+ maple_set_drvdata(mdev, mse);
+
+ maple_getcond_callback(mdev, dc_mouse_callback, HZ/50,
+ MAPLE_FUNC_MOUSE);
+
+ return error;
+
+fail_register:
+ input_free_device(input_dev);
+fail_nomem:
+ kfree(mse);
+fail:
+ return error;
+
+}
+
+static int remove_maple_mouse(struct device *dev)
+{
+ struct maple_device *mdev = to_maple_dev(dev);
+ struct dc_mouse *mse = maple_get_drvdata(mdev);
+
+ mdev->callback = NULL;
+ input_unregister_device(mse->dev);
+ maple_set_drvdata(mdev, NULL);
+ kfree(mse);
+ return 0;
+}
+
+static struct maple_driver dc_mouse_driver = {
+ .function = MAPLE_FUNC_MOUSE,
+ .drv = {
+ .name = "Dreamcast_mouse",
+ .probe = probe_maple_mouse,
+ .remove = remove_maple_mouse,
+ },
+};
+
+static int __init dc_mouse_init(void)
+{
+ return maple_driver_register(&dc_mouse_driver);
+}
+
+static void __exit dc_mouse_exit(void)
+{
+ maple_driver_unregister(&dc_mouse_driver);
+}
+
+module_init(dc_mouse_init);
+module_exit(dc_mouse_exit);
On Monday 29 December 2008 14:17:25 Adrian McMenamin wrote:
> +static int __devinit probe_maple_mouse(struct device *dev)
> +static int remove_maple_mouse(struct device *dev)
i thought you were going the rout of not labeling any of the probe/remove
functions ... but if you label the prob with __devinit, then the remove should
be __devexit. i.e. whichever way you go, the two should be consistent.
-mike
Adding support for Maple mouse
Signed-off-by: Adrian McMenamin <[email protected]>
---
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 4e99342..71cc98a 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -286,4 +286,15 @@ config MOUSE_GPIO
To compile this driver as a module, choose M here: the
module will be called gpio_mouse.
+config MOUSE_MAPLE
+ tristate "Maple mouse (for the Dreamcast)"
+ depends on MAPLE
+ help
+ This driver supports the Maple mouse on the SEGA Dreamcast.
+
+ Most Dreamcast users, who have a mouse, will say Y here.
+
+ To compile this driver as a module choose M here: the module will be
+ called maplemouse.
+
endif
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 96f1dd8..0272f5d 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
+obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o
psmouse-objs := psmouse-base.o synaptics.o
diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c
new file mode 100644
index 0000000..1c0830b
--- /dev/null
+++ b/drivers/input/mouse/maplemouse.c
@@ -0,0 +1,135 @@
+/*
+ * SEGA Dreamcast mouse driver
+ * Based on drivers/usb/usbmouse.c
+ *
+ * Copyright Yaegashi Takeshi, 2001
+ * Adrian McMenamin, 2008
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/maple.h>
+
+MODULE_AUTHOR("Adrian McMenamin <[email protected]>");
+MODULE_DESCRIPTION("SEGA Dreamcast mouse driver");
+MODULE_LICENSE("GPL");
+
+struct dc_mouse {
+ struct input_dev *dev;
+ struct maple_device *mdev;
+};
+
+static void dc_mouse_callback(struct mapleq *mq)
+{
+ int buttons, relx, rely, relz;
+ struct maple_device *mapledev = mq->dev;
+ struct dc_mouse *mse = maple_get_drvdata(mapledev);
+ struct input_dev *dev = mse->dev;
+ unsigned char *res = mq->recvbuf;
+
+ buttons = ~res[8];
+ relx = *(unsigned short *)(res + 12) - 512;
+ rely = *(unsigned short *)(res + 14) - 512;
+ relz = *(unsigned short *)(res + 16) - 512;
+
+ input_report_key(dev, BTN_LEFT, buttons & 4);
+ input_report_key(dev, BTN_MIDDLE, buttons & 9);
+ input_report_key(dev, BTN_RIGHT, buttons & 2);
+ input_report_rel(dev, REL_X, relx);
+ input_report_rel(dev, REL_Y, rely);
+ input_report_rel(dev, REL_WHEEL, relz);
+ input_sync(dev);
+}
+
+/* allow the mouse to be used */
+static int __devinit probe_maple_mouse(struct device *dev)
+{
+ struct maple_device *mdev;
+ struct maple_driver *mdrv;
+ int error;
+ struct input_dev *input_dev;
+ struct dc_mouse *mse;
+
+ mdev = to_maple_dev(dev);
+ mdrv = to_maple_driver(dev->driver);
+
+ mse = kzalloc(sizeof(struct dc_mouse), GFP_KERNEL);
+ if (!mse) {
+ error = -ENOMEM;
+ goto fail;
+ }
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ error = -ENOMEM;
+ goto fail_nomem;
+ }
+
+ mse->dev = input_dev;
+ mse->mdev = mdev;
+
+ input_set_drvdata(input_dev, mse);
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
+ input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) |
+ BIT_MASK(REL_WHEEL);
+ input_dev->name = mdev->product_name;
+ input_dev->id.bustype = BUS_HOST;
+ error = input_register_device(input_dev);
+ if (error)
+ goto fail_register;
+
+ mdev->driver = mdrv;
+ maple_set_drvdata(mdev, mse);
+
+ maple_getcond_callback(mdev, dc_mouse_callback, HZ/50,
+ MAPLE_FUNC_MOUSE);
+
+ return error;
+
+fail_register:
+ input_free_device(input_dev);
+fail_nomem:
+ kfree(mse);
+fail:
+ return error;
+}
+
+static int __devexit remove_maple_mouse(struct device *dev)
+{
+ struct maple_device *mdev = to_maple_dev(dev);
+ struct dc_mouse *mse = maple_get_drvdata(mdev);
+
+ mdev->callback = NULL;
+ input_unregister_device(mse->dev);
+ maple_set_drvdata(mdev, NULL);
+ kfree(mse);
+ return 0;
+}
+
+static struct maple_driver dc_mouse_driver = {
+ .function = MAPLE_FUNC_MOUSE,
+ .drv = {
+ .name = "Dreamcast_mouse",
+ .probe = probe_maple_mouse,
+ .remove = remove_maple_mouse,
+ },
+};
+
+static int __init dc_mouse_init(void)
+{
+ return maple_driver_register(&dc_mouse_driver);
+}
+
+static void __exit dc_mouse_exit(void)
+{
+ maple_driver_unregister(&dc_mouse_driver);
+}
+
+module_init(dc_mouse_init);
+module_exit(dc_mouse_exit);
On Monday 29 December 2008 16:30:14 Adrian McMenamin wrote:
> +static int __devexit remove_maple_mouse(struct device *dev)
> +static struct maple_driver dc_mouse_driver = {
> + .drv = {
> + .remove = remove_maple_mouse,
does that really build warning free ? usually you need __devexit_p() in the
.remove assignment ...
-mike
On Mon, 2008-12-29 at 16:36 -0500, Mike Frysinger wrote:
> On Monday 29 December 2008 16:30:14 Adrian McMenamin wrote:
> > +static int __devexit remove_maple_mouse(struct device *dev)
> > +static struct maple_driver dc_mouse_driver = {
> > + .drv = {
> > + .remove = remove_maple_mouse,
>
> does that really build warning free ? usually you need __devexit_p() in the
> .remove assignment ...
> -mike
Yep
adrian@bossclass:~/linux-2.6$ make ARCH=sh
CROSS_COMPILE=/home/adrian/dreamy-linux/buildroot/build_sh4/staging_dir/bin/sh4-linux- zImage modules -j4
scripts/kconfig/conf -s arch/sh/Kconfig
CHK include/linux/version.h
SYMLINK include/asm -> include/asm-sh
make[1]: `include/asm-sh/machtypes.h' is up to date.
CHK include/linux/utsrelease.h
UPD include/linux/utsrelease.h
CALL scripts/checksyscalls.sh
CHK include/linux/compile.h
CC init/version.o
CC kernel/module.o
LD init/built-in.o
CC [M] drivers/input/mouse/maplemouse.o
LD kernel/built-in.o
On Mon, Dec 29, 2008 at 09:38:34PM +0000, Adrian McMenamin wrote:
> On Mon, 2008-12-29 at 16:36 -0500, Mike Frysinger wrote:
> > On Monday 29 December 2008 16:30:14 Adrian McMenamin wrote:
> > > +static int __devexit remove_maple_mouse(struct device *dev)
> > > +static struct maple_driver dc_mouse_driver = {
> > > + .drv = {
> > > + .remove = remove_maple_mouse,
> >
> > does that really build warning free ? usually you need __devexit_p() in the
> > .remove assignment ...
> > -mike
>
> Yep
>
> adrian@bossclass:~/linux-2.6$ make ARCH=sh
> CROSS_COMPILE=/home/adrian/dreamy-linux/buildroot/build_sh4/staging_dir/bin/sh4-linux- zImage modules -j4
> scripts/kconfig/conf -s arch/sh/Kconfig
> CHK include/linux/version.h
> SYMLINK include/asm -> include/asm-sh
> make[1]: `include/asm-sh/machtypes.h' is up to date.
> CHK include/linux/utsrelease.h
> UPD include/linux/utsrelease.h
> CALL scripts/checksyscalls.sh
> CHK include/linux/compile.h
> CC init/version.o
> CC kernel/module.o
> LD init/built-in.o
> CC [M] drivers/input/mouse/maplemouse.o
> LD kernel/built-in.o
Look for an increase in section mismatch warning or try to
build with:
make CONFIG_DEBUG_SECTION_MISMATCH=y
Sam
On Mon, 2008-12-29 at 22:46 +0100, Sam Ravnborg wrote:
> Look for an increase in section mismatch warning or try to
> build with:
> make CONFIG_DEBUG_SECTION_MISMATCH=y
>
Just rebuilt the whole thing from scratch with that turned on and no
extra warnings at all.
On Monday 29 December 2008 17:17:53 Adrian McMenamin wrote:
> On Mon, 2008-12-29 at 22:46 +0100, Sam Ravnborg wrote:
> > Look for an increase in section mismatch warning or try to
> > build with:
> > make CONFIG_DEBUG_SECTION_MISMATCH=y
>
> Just rebuilt the whole thing from scratch with that turned on and no
> extra warnings at all.
regardless, please add the aforementioned wrapping. this comes up from time
to time in various configurations.
-mike
On Monday 29 December 2008 14:19:42 Mike Frysinger wrote:
> On Monday 29 December 2008 17:17:53 Adrian McMenamin wrote:
> > On Mon, 2008-12-29 at 22:46 +0100, Sam Ravnborg wrote:
> > > Look for an increase in section mismatch warning or try to
> > > build with:
> > > make CONFIG_DEBUG_SECTION_MISMATCH=y
> >
> > Just rebuilt the whole thing from scratch with that turned on and no
> > extra warnings at all.
>
> regardless, please add the aforementioned wrapping. this comes up from
> time to time in various configurations.
It could be that neither of the platforms this driver is running on defines
CONFIG_HOTPLUG and then I think it is harmless but yes, we better have
__devexit_p() there.
--
Dmitry
Adding maple mouse with proper __devexit_p() handling
Signed-off-by: Adrian McMenamin <[email protected]>
---
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 4e99342..71cc98a 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -286,4 +286,15 @@ config MOUSE_GPIO
To compile this driver as a module, choose M here: the
module will be called gpio_mouse.
+config MOUSE_MAPLE
+ tristate "Maple mouse (for the Dreamcast)"
+ depends on MAPLE
+ help
+ This driver supports the Maple mouse on the SEGA Dreamcast.
+
+ Most Dreamcast users, who have a mouse, will say Y here.
+
+ To compile this driver as a module choose M here: the module will be
+ called maplemouse.
+
endif
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 96f1dd8..0272f5d 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
+obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o
psmouse-objs := psmouse-base.o synaptics.o
diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c
new file mode 100644
index 0000000..103e5a0
--- /dev/null
+++ b/drivers/input/mouse/maplemouse.c
@@ -0,0 +1,135 @@
+/*
+ * SEGA Dreamcast mouse driver
+ * Based on drivers/usb/usbmouse.c
+ *
+ * Copyright Yaegashi Takeshi, 2001
+ * Adrian McMenamin, 2008
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/maple.h>
+
+MODULE_AUTHOR("Adrian McMenamin <[email protected]>");
+MODULE_DESCRIPTION("SEGA Dreamcast mouse driver");
+MODULE_LICENSE("GPL");
+
+struct dc_mouse {
+ struct input_dev *dev;
+ struct maple_device *mdev;
+};
+
+static void dc_mouse_callback(struct mapleq *mq)
+{
+ int buttons, relx, rely, relz;
+ struct maple_device *mapledev = mq->dev;
+ struct dc_mouse *mse = maple_get_drvdata(mapledev);
+ struct input_dev *dev = mse->dev;
+ unsigned char *res = mq->recvbuf;
+
+ buttons = ~res[8];
+ relx = *(unsigned short *)(res + 12) - 512;
+ rely = *(unsigned short *)(res + 14) - 512;
+ relz = *(unsigned short *)(res + 16) - 512;
+
+ input_report_key(dev, BTN_LEFT, buttons & 4);
+ input_report_key(dev, BTN_MIDDLE, buttons & 9);
+ input_report_key(dev, BTN_RIGHT, buttons & 2);
+ input_report_rel(dev, REL_X, relx);
+ input_report_rel(dev, REL_Y, rely);
+ input_report_rel(dev, REL_WHEEL, relz);
+ input_sync(dev);
+}
+
+/* allow the mouse to be used */
+static int __devinit probe_maple_mouse(struct device *dev)
+{
+ struct maple_device *mdev;
+ struct maple_driver *mdrv;
+ int error;
+ struct input_dev *input_dev;
+ struct dc_mouse *mse;
+
+ mdev = to_maple_dev(dev);
+ mdrv = to_maple_driver(dev->driver);
+
+ mse = kzalloc(sizeof(struct dc_mouse), GFP_KERNEL);
+ if (!mse) {
+ error = -ENOMEM;
+ goto fail;
+ }
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ error = -ENOMEM;
+ goto fail_nomem;
+ }
+
+ mse->dev = input_dev;
+ mse->mdev = mdev;
+
+ input_set_drvdata(input_dev, mse);
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+ input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
+ input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) |
+ BIT_MASK(REL_WHEEL);
+ input_dev->name = mdev->product_name;
+ input_dev->id.bustype = BUS_HOST;
+ error = input_register_device(input_dev);
+ if (error)
+ goto fail_register;
+
+ mdev->driver = mdrv;
+ maple_set_drvdata(mdev, mse);
+
+ maple_getcond_callback(mdev, dc_mouse_callback, HZ/50,
+ MAPLE_FUNC_MOUSE);
+
+ return error;
+
+fail_register:
+ input_free_device(input_dev);
+fail_nomem:
+ kfree(mse);
+fail:
+ return error;
+}
+
+static int __devexit remove_maple_mouse(struct device *dev)
+{
+ struct maple_device *mdev = to_maple_dev(dev);
+ struct dc_mouse *mse = maple_get_drvdata(mdev);
+
+ mdev->callback = NULL;
+ input_unregister_device(mse->dev);
+ maple_set_drvdata(mdev, NULL);
+ kfree(mse);
+ return 0;
+}
+
+static struct maple_driver dc_mouse_driver = {
+ .function = MAPLE_FUNC_MOUSE,
+ .drv = {
+ .name = "Dreamcast_mouse",
+ .probe = probe_maple_mouse,
+ .remove = __devexit_p(remove_maple_mouse),
+ },
+};
+
+static int __init dc_mouse_init(void)
+{
+ return maple_driver_register(&dc_mouse_driver);
+}
+
+static void __exit dc_mouse_exit(void)
+{
+ maple_driver_unregister(&dc_mouse_driver);
+}
+
+module_init(dc_mouse_init);
+module_exit(dc_mouse_exit);
On Tuesday 30 December 2008 15:27:05 Adrian McMenamin wrote:
> Adding maple mouse with proper __devexit_p() handling
this kind of patch revision info typically goes below the --- marker and the
patch itself gets "v2", "v3", etc... header markers
otherwise, driver looks OK to me. thanks for your dreamcast work.
Acked-by: Mike Frysinger <[email protected]>
> + mse = kzalloc(sizeof(struct dc_mouse), GFP_KERNEL);
personally i find this style less likely to rot:
mse = kzalloc(sizeof(*mse), GFP_KERNEL);
-mike