Not sure what happened with earlier submissions, so here is an up-to-date
patch for the Dreamcast Maple controlled.
Add support for the SEGA Dreamcast Maple controller as a joystick
Signed-off-by: Adrian McMenamin <[email protected]>
---
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index be5c14a..ca7ce42 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -294,4 +294,17 @@ config JOYSTICK_XPAD_LEDS
This option enables support for the LED which surrounds the Big X on
XBox 360 controller.
+config JOYSTICK_MAPLE
+ tristate "Dreamcast control pad"
+ depends on SH_DREAMCAST
+ select MAPLE
+ help
+ Say Y here if you have a SEGA Dreamcast and want to use your
+ controller as a joystick.
+
+ Most Dreamcast users will say Y.
+
+ To compile this as a module choose M here: the module will be called
+ maplecontrol.
+
endif
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
index fdbf8c4..bfd848d 100644
--- a/drivers/input/joystick/Makefile
+++ b/drivers/input/joystick/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_JOYSTICK_AMIGA) += amijoy.o
obj-$(CONFIG_JOYSTICK_ANALOG) += analog.o
obj-$(CONFIG_JOYSTICK_COBRA) += cobra.o
obj-$(CONFIG_JOYSTICK_DB9) += db9.o
+obj-$(CONFIG_JOYSTICK_MAPLE) += maplecontrol.o
obj-$(CONFIG_JOYSTICK_GAMECON) += gamecon.o
obj-$(CONFIG_JOYSTICK_GF2K) += gf2k.o
obj-$(CONFIG_JOYSTICK_GRIP) += grip.o
diff --git a/drivers/input/joystick/maplecontrol.c b/drivers/input/joystick/maplecontrol.c
new file mode 100644
index 0000000..e4308d8
--- /dev/null
+++ b/drivers/input/joystick/maplecontrol.c
@@ -0,0 +1,194 @@
+/*
+ * SEGA Dreamcast controller driver
+ * Based on drivers/usb/iforce.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 controller driver");
+MODULE_LICENSE("GPL");
+
+struct dc_pad {
+ struct input_dev *dev;
+ struct maple_device *mdev;
+};
+
+static void dc_pad_callback(struct mapleq *mq)
+{
+ unsigned short buttons;
+ struct maple_device *mapledev = mq->dev;
+ struct dc_pad *pad = maple_get_drvdata(mapledev);
+ struct input_dev *dev = pad->dev;
+ unsigned char *res = mq->recvbuf;
+
+ buttons = ~cpu_to_le16(*(unsigned short *)(res + 8));
+
+ input_report_abs(dev, ABS_HAT0Y,
+ (buttons & 0x0010 ? -1:0) + (buttons & 0x0020 ? 1:0));
+ input_report_abs(dev, ABS_HAT0X,
+ (buttons & 0x0040 ? -1:0) + (buttons & 0x0080 ? 1:0));
+ input_report_abs(dev, ABS_HAT1Y,
+ (buttons & 0x1000 ? -1:0) + (buttons & 0x2000 ? 1:0));
+ input_report_abs(dev, ABS_HAT1X,
+ (buttons & 0x4000 ? -1:0) + (buttons & 0x8000 ? 1:0));
+
+ input_report_key(dev, BTN_C, buttons & 0x0001);
+ input_report_key(dev, BTN_B, buttons & 0x0002);
+ input_report_key(dev, BTN_A, buttons & 0x0004);
+ input_report_key(dev, BTN_START, buttons & 0x0008);
+ input_report_key(dev, BTN_Z, buttons & 0x0100);
+ input_report_key(dev, BTN_Y, buttons & 0x0200);
+ input_report_key(dev, BTN_X, buttons & 0x0400);
+ input_report_key(dev, BTN_SELECT, buttons & 0x0800);
+
+ input_report_abs(dev, ABS_GAS, res[10]);
+ input_report_abs(dev, ABS_BRAKE, res[11]);
+ input_report_abs(dev, ABS_X, res[12]);
+ input_report_abs(dev, ABS_Y, res[13]);
+ input_report_abs(dev, ABS_RX, res[14]);
+ input_report_abs(dev, ABS_RY, res[15]);
+}
+
+static int dc_pad_open(struct input_dev *dev)
+{
+ struct dc_pad *pad;
+
+ pad = dev->dev.platform_data;
+ maple_getcond_callback(pad->mdev, dc_pad_callback, HZ/20,
+ MAPLE_FUNC_CONTROLLER);
+ return 0;
+}
+
+static void dc_pad_close(struct input_dev *dev)
+{
+ struct dc_pad *pad;
+
+ pad = dev->dev.platform_data;
+ maple_getcond_callback(pad->mdev, dc_pad_callback, 0,
+ MAPLE_FUNC_CONTROLLER);
+}
+
+/* allow the controller to be used */
+static int probe_maple_controller(struct device *dev)
+{
+ struct maple_device *mdev = to_maple_dev(dev);
+ struct maple_driver *mdrv = to_maple_driver(dev->driver);
+ int i, error;
+ struct dc_pad *pad;
+ struct input_dev *idev;
+ unsigned long data = be32_to_cpu(mdev->devinfo.function_data[0]);
+
+ const short btn_bit[32] = {
+ BTN_C, BTN_B, BTN_A, BTN_START, -1, -1, -1, -1,
+ BTN_Z, BTN_Y, BTN_X, BTN_SELECT, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+
+ const short abs_bit[32] = {
+ -1, -1, -1, -1, ABS_HAT0Y, ABS_HAT0Y, ABS_HAT0X, ABS_HAT0X,
+ -1, -1, -1, -1, ABS_HAT1Y, ABS_HAT1Y, ABS_HAT1X, ABS_HAT1X,
+ ABS_GAS, ABS_BRAKE, ABS_X, ABS_Y, ABS_RX, ABS_RY, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+
+ pad = kzalloc(sizeof(struct dc_pad), GFP_KERNEL);
+ idev = input_allocate_device();
+ if (!pad || !idev){
+ error = ENOMEM;
+ goto fail;
+ }
+
+ pad->dev = idev;
+ pad->mdev = mdev;
+
+ idev->open = dc_pad_open;
+ idev->close = dc_pad_close;
+
+ for (i = 0; i < 32; i++)
+ if (data & (1<<i) && btn_bit[i] >= 0)
+ idev->keybit[BIT_WORD(BTN_JOYSTICK)] |= BIT_MASK(btn_bit[i]);
+
+ if (idev->keybit[BTN_JOYSTICK/32])
+ idev->evbit[0] |= BIT_MASK(EV_KEY);
+
+ for (i = 0; i < 32; i++)
+ if (data & (1<<i) && abs_bit[i] >= 0)
+ idev->absbit[0] |= BIT_MASK(abs_bit[i]);
+
+ if (idev->absbit[0])
+ idev->evbit[0] |= BIT_MASK(EV_ABS);
+
+ for (i = ABS_X; i <= ABS_BRAKE; i++)
+ input_set_abs_params(idev, i, 0, 255, 0, 0);
+
+ for (i = ABS_HAT0X; i <= ABS_HAT3Y; i++)
+ input_set_abs_params(idev, i, 1, -1, 0, 0);
+
+ idev->dev.platform_data = pad;
+ idev->event = NULL;
+ idev->dev.parent = &mdev->dev;
+ idev->name = mdev->product_name;
+ idev->id.bustype = BUS_HOST;
+ input_set_drvdata(idev, pad);
+
+ error = input_register_device(idev);
+ if (error)
+ goto fail;
+
+ mdev->driver = mdrv;
+ maple_set_drvdata(mdev, pad);
+
+ return error;
+
+fail:
+ input_free_device(pad->dev);
+ kfree(pad);
+ maple_set_drvdata(mdev, NULL);
+ return -error;
+}
+
+static int remove_maple_controller(struct device *dev)
+{
+ struct maple_device *mdev = to_maple_dev(dev);
+ struct dc_pad *pad = maple_get_drvdata(mdev);
+
+ mdev->callback = NULL;
+ input_unregister_device(pad->dev);
+ maple_set_drvdata(mdev, NULL);
+ kfree(pad);
+ return 0;
+}
+
+static struct maple_driver dc_pad_driver = {
+ .function = MAPLE_FUNC_CONTROLLER,
+ .drv = {
+ .name = "Dreamcast_controller",
+ .probe = probe_maple_controller,
+ .remove = remove_maple_controller,
+ },
+};
+
+static int __init dc_pad_init(void)
+{
+ return maple_driver_register(&dc_pad_driver);
+}
+
+static void __exit dc_pad_exit(void)
+{
+ maple_driver_unregister(&dc_pad_driver);
+}
+
+module_init(dc_pad_init);
+module_exit(dc_pad_exit);
On Fri, Dec 19, 2008 at 11:15:42PM +0000, Adrian McMenamin wrote:
>
> Add support for the SEGA Dreamcast Maple controller as a joystick
>
[snip]
> + struct dc_pad *pad = maple_get_drvdata(mapledev);
> + struct input_dev *dev = pad->dev;
> + unsigned char *res = mq->recvbuf;
> +
> + buttons = ~cpu_to_le16(*(unsigned short *)(res + 8));
> +
I may be wrong but shouldn't this use the I/O accessor functions?
> +
> + pad = kzalloc(sizeof(struct dc_pad), GFP_KERNEL);
> + idev = input_allocate_device();
> + if (!pad || !idev){
> + error = ENOMEM;
> + goto fail;
> + }
[snip]
> +fail:
> + input_free_device(pad->dev);
Possible NULL pointer dereference?
On Sat, 2008-12-20 at 15:16 +0000, Matt Fleming wrote:
> On Fri, Dec 19, 2008 at 11:15:42PM +0000, Adrian McMenamin wrote:
> >
> > Add support for the SEGA Dreamcast Maple controller as a joystick
> >
>
> [snip]
>
> > + struct dc_pad *pad = maple_get_drvdata(mapledev);
> > + struct input_dev *dev = pad->dev;
> > + unsigned char *res = mq->recvbuf;
> > +
> > + buttons = ~cpu_to_le16(*(unsigned short *)(res + 8));
> > +
>
> I may be wrong but shouldn't this use the I/O accessor functions?
>
It's not iomemory, so I don't think so. The maple bus writes to a buffer
in the physical RAM.
> > +
> > + pad = kzalloc(sizeof(struct dc_pad), GFP_KERNEL);
> > + idev = input_allocate_device();
> > + if (!pad || !idev){
> > + error = ENOMEM;
> > + goto fail;
> > + }
>
> [snip]
>
> > +fail:
> > + input_free_device(pad->dev);
>
> Possible NULL pointer dereference?
No, because input_free_device is:
void input_free_device(struct input_dev *dev)
{
if (dev)
input_put_device(dev);
}
EXPORT_SYMBOL(input_free_device);
On Saturday 20 December 2008 08:07:27 Adrian McMenamin wrote:
> On Sat, 2008-12-20 at 15:16 +0000, Matt Fleming wrote:
> > On Fri, Dec 19, 2008 at 11:15:42PM +0000, Adrian McMenamin wrote:
> > > Add support for the SEGA Dreamcast Maple controller as a joystick
> >
> > [snip]
> >
> > > + struct dc_pad *pad = maple_get_drvdata(mapledev);
> > > + struct input_dev *dev = pad->dev;
> > > + unsigned char *res = mq->recvbuf;
> > > +
> > > + buttons = ~cpu_to_le16(*(unsigned short *)(res + 8));
> > > +
> >
> > I may be wrong but shouldn't this use the I/O accessor functions?
>
> It's not iomemory, so I don't think so. The maple bus writes to a buffer
> in the physical RAM.
>
Hmm, but why we are going _from_ cpu order to le16??? I'd expect we
wanted to do conversion the other way around. I pulled it out of 'next'
for now.
> > > +
> > > + pad = kzalloc(sizeof(struct dc_pad), GFP_KERNEL);
> > > + idev = input_allocate_device();
> > > + if (!pad || !idev){
> > > + error = ENOMEM;
> > > + goto fail;
> > > + }
> >
> > [snip]
> >
> > > +fail:
> > > + input_free_device(pad->dev);
> >
> > Possible NULL pointer dereference?
>
> No, because input_free_device is:
>
> void input_free_device(struct input_dev *dev)
> {
> if (dev)
> input_put_device(dev);
> }
> EXPORT_SYMBOL(input_free_device);
But we need to be freeing idev, not pad->dev because pad might
be NULL.
--
Dmitry
On Sat, 2008-12-20 at 12:19 -0800, Dmitry Torokhov wrote:
> On Saturday 20 December 2008 08:07:27 Adrian McMenamin wrote:
> > On Sat, 2008-12-20 at 15:16 +0000, Matt Fleming wrote:
> > > On Fri, Dec 19, 2008 at 11:15:42PM +0000, Adrian McMenamin wrote:
> > > > Add support for the SEGA Dreamcast Maple controller as a joystick
> > >
> > > [snip]
> > >
> > > > + struct dc_pad *pad = maple_get_drvdata(mapledev);
> > > > + struct input_dev *dev = pad->dev;
> > > > + unsigned char *res = mq->recvbuf;
> > > > +
> > > > + buttons = ~cpu_to_le16(*(unsigned short *)(res + 8));
> > > > +
> > >
> > > I may be wrong but shouldn't this use the I/O accessor functions?
> >
> > It's not iomemory, so I don't think so. The maple bus writes to a buffer
> > in the physical RAM.
> >
>
> Hmm, but why we are going _from_ cpu order to le16??? I'd expect we
> wanted to do conversion the other way around. I pulled it out of 'next'
> for now.
>
The data is transmitted here by the bus in 32 bit little endian blocks
but makes sense in le16 bit lumps. Off is 1 and on is 0 - hence the ~
The first 16 bits are the button data the next 16 bits are the (start
of) the axis data. Both cpu and bus data (in this case - it can vary)
are little endian.
On Dec 20, 2008, at 12:47 PM, Adrian McMenamin <[email protected]
> wrote:
> On Sat, 2008-12-20 at 12:19 -0800, Dmitry Torokhov wrote:
>> On Saturday 20 December 2008 08:07:27 Adrian McMenamin wrote:
>>> On Sat, 2008-12-20 at 15:16 +0000, Matt Fleming wrote:
>>>> On Fri, Dec 19, 2008 at 11:15:42PM +0000, Adrian McMenamin wrote:
>>>>> Add support for the SEGA Dreamcast Maple controller as a joystick
>>>>
>>>> [snip]
>>>>
>>>>> + struct dc_pad *pad = maple_get_drvdata(mapledev);
>>>>> + struct input_dev *dev = pad->dev;
>>>>> + unsigned char *res = mq->recvbuf;
>>>>> +
>>>>> + buttons = ~cpu_to_le16(*(unsigned short *)(res + 8));
>>>>> +
>>>>
>>>> I may be wrong but shouldn't this use the I/O accessor functions?
>>>
>>> It's not iomemory, so I don't think so. The maple bus writes to a
>>> buffer
>>> in the physical RAM.
>>>
>>
>> Hmm, but why we are going _from_ cpu order to le16??? I'd expect we
>> wanted to do conversion the other way around. I pulled it out of
>> 'next'
>> for now.
>>
> The data is transmitted here by the bus in 32 bit little endian blocks
> but makes sense in le16 bit lumps. Off is 1 and on is 0 - hence the ~
>
> The first 16 bits are the button data the next 16 bits are the (start
> of) the axis data. Both cpu and bus data (in this case - it can vary)
> are little endian.
Right, so I think we need le16_to_cpu here, you want the data in
native CPU order to manipulate it.
--
Dmitry
On Fri, Dec 19, 2008 at 11:15:42PM +0000, Adrian McMenamin wrote:
> Not sure what happened with earlier submissions, so here is an up-to-date
> patch for the Dreamcast Maple controlled.
>
>
> Add support for the SEGA Dreamcast Maple controller as a joystick
>
> Signed-off-by: Adrian McMenamin <[email protected]>
> ---
>
> diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
> index be5c14a..ca7ce42 100644
> --- a/drivers/input/joystick/Kconfig
> +++ b/drivers/input/joystick/Kconfig
> @@ -294,4 +294,17 @@ config JOYSTICK_XPAD_LEDS
> This option enables support for the LED which surrounds the Big X on
> XBox 360 controller.
>
> +config JOYSTICK_MAPLE
> + tristate "Dreamcast control pad"
> + depends on SH_DREAMCAST
> + select MAPLE
> + help
Please don't do this kind of dependency garbage. The only dependency this
device has is on the maple bus, there is nothing inherently dreamcast
specific about it.
CONFIG_MAPLE already has the dreamcast dependency in this case, and I
would speculate the only reason that you added the dreamcast dependency
was to work around the fact that select would complain on plaforms where
the MAPLE symbol is not visible.
This is an indicator that select is not the thing you should be using
here in the first place. So, just change this to depends on MAPLE and be
done with it. As soon as you have to start mixing and matching
depends/selects that already have a dependency on each other, you have
already lost.
On Fri, Dec 19, 2008 at 11:15:42PM +0000, Adrian McMenamin wrote:
> Add support for the SEGA Dreamcast Maple controller as a joystick
[snip]
> +
> + pad = kzalloc(sizeof(struct dc_pad), GFP_KERNEL);
> + idev = input_allocate_device();
> + if (!pad || !idev){
> + error = ENOMEM;
> + goto fail;
> + }
> +
[snip]
> +
> + error = input_register_device(idev);
> + if (error)
> + goto fail;
> +
> + mdev->driver = mdrv;
> + maple_set_drvdata(mdev, pad);
> +
> + return error;
> +
> +fail:
> + input_free_device(pad->dev);
> + kfree(pad);
> + maple_set_drvdata(mdev, NULL);
> + return -error;
> +}
This will incorrectly negate the already negative error code from
input_register_device.
Rabin
On Fri, 19 Dec 2008 23:15:42 +0000
Adrian McMenamin <[email protected]> wrote:
> Not sure what happened with earlier submissions, so here is an up-to-date
> patch for the Dreamcast Maple controlled.
>
>
> Add support for the SEGA Dreamcast Maple controller as a joystick
>
> ...
>
> +static void dc_pad_callback(struct mapleq *mq)
> +{
> + unsigned short buttons;
> + struct maple_device *mapledev = mq->dev;
> + struct dc_pad *pad = maple_get_drvdata(mapledev);
> + struct input_dev *dev = pad->dev;
> + unsigned char *res = mq->recvbuf;
> +
> + buttons = ~cpu_to_le16(*(unsigned short *)(res + 8));
hm, could that be simplified?
> + input_report_abs(dev, ABS_HAT0Y,
> + (buttons & 0x0010 ? -1:0) + (buttons & 0x0020 ? 1:0));
> + input_report_abs(dev, ABS_HAT0X,
> + (buttons & 0x0040 ? -1:0) + (buttons & 0x0080 ? 1:0));
> + input_report_abs(dev, ABS_HAT1Y,
> + (buttons & 0x1000 ? -1:0) + (buttons & 0x2000 ? 1:0));
> + input_report_abs(dev, ABS_HAT1X,
> + (buttons & 0x4000 ? -1:0) + (buttons & 0x8000 ? 1:0));
> +
> + input_report_key(dev, BTN_C, buttons & 0x0001);
> + input_report_key(dev, BTN_B, buttons & 0x0002);
> + input_report_key(dev, BTN_A, buttons & 0x0004);
> + input_report_key(dev, BTN_START, buttons & 0x0008);
> + input_report_key(dev, BTN_Z, buttons & 0x0100);
> + input_report_key(dev, BTN_Y, buttons & 0x0200);
> + input_report_key(dev, BTN_X, buttons & 0x0400);
> + input_report_key(dev, BTN_SELECT, buttons & 0x0800);
> +
> + input_report_abs(dev, ABS_GAS, res[10]);
> + input_report_abs(dev, ABS_BRAKE, res[11]);
> + input_report_abs(dev, ABS_X, res[12]);
> + input_report_abs(dev, ABS_Y, res[13]);
> + input_report_abs(dev, ABS_RX, res[14]);
> + input_report_abs(dev, ABS_RY, res[15]);
> +}
> +
> +static int dc_pad_open(struct input_dev *dev)
> +{
> + struct dc_pad *pad;
> +
> + pad = dev->dev.platform_data;
> + maple_getcond_callback(pad->mdev, dc_pad_callback, HZ/20,
> + MAPLE_FUNC_CONTROLLER);
> + return 0;
> +}
> +
> +static void dc_pad_close(struct input_dev *dev)
> +{
> + struct dc_pad *pad;
> +
> + pad = dev->dev.platform_data;
> + maple_getcond_callback(pad->mdev, dc_pad_callback, 0,
> + MAPLE_FUNC_CONTROLLER);
> +}
> +
> +/* allow the controller to be used */
> +static int probe_maple_controller(struct device *dev)
> +{
> + struct maple_device *mdev = to_maple_dev(dev);
> + struct maple_driver *mdrv = to_maple_driver(dev->driver);
> + int i, error;
> + struct dc_pad *pad;
> + struct input_dev *idev;
> + unsigned long data = be32_to_cpu(mdev->devinfo.function_data[0]);
> +
> + const short btn_bit[32] = {
> + BTN_C, BTN_B, BTN_A, BTN_START, -1, -1, -1, -1,
> + BTN_Z, BTN_Y, BTN_X, BTN_SELECT, -1, -1, -1, -1,
> + -1, -1, -1, -1, -1, -1, -1, -1,
> + -1, -1, -1, -1, -1, -1, -1, -1,
> + };
> +
> + const short abs_bit[32] = {
> + -1, -1, -1, -1, ABS_HAT0Y, ABS_HAT0Y, ABS_HAT0X, ABS_HAT0X,
> + -1, -1, -1, -1, ABS_HAT1Y, ABS_HAT1Y, ABS_HAT1X, ABS_HAT1X,
> + ABS_GAS, ABS_BRAKE, ABS_X, ABS_Y, ABS_RX, ABS_RY, -1, -1,
> + -1, -1, -1, -1, -1, -1, -1, -1,
> + };
These could be made static.
There's a risk that the compiler will go and assemble these arrays on
the stack each time this function is called. gcc (some versions, at
least) will actually internally turn these into static arrays
(assembled at compile time) but it's best to do this explicitly.
> + pad = kzalloc(sizeof(struct dc_pad), GFP_KERNEL);
> + idev = input_allocate_device();
> + if (!pad || !idev){
> + error = ENOMEM;
>
> ...
>
> + return -error;
Please don't do this. People occasionally grep the tree for instances
of 'EFOO' which are missing the "-" sign, which is a useful service.
The above trick just makes things harder for them.
>
> ...
>
On Mon, 2008-12-22 at 15:06 -0800, Andrew Morton wrote:
> On Fri, 19 Dec 2008 23:15:42 +0000
> Adrian McMenamin <[email protected]> wrote:
>
> > Not sure what happened with earlier submissions, so here is an up-to-date
> > patch for the Dreamcast Maple controlled.
> >
> >
> > Add support for the SEGA Dreamcast Maple controller as a joystick
> >
> > ...
> >
> > +static void dc_pad_callback(struct mapleq *mq)
> > +{
> > + unsigned short buttons;
> > + struct maple_device *mapledev = mq->dev;
> > + struct dc_pad *pad = maple_get_drvdata(mapledev);
> > + struct input_dev *dev = pad->dev;
> > + unsigned char *res = mq->recvbuf;
> > +
> > + buttons = ~cpu_to_le16(*(unsigned short *)(res + 8));
>
> hm, could that be simplified?
And as a bonus fix the obvious sparse warning:
buttons = ~le16_to_cpup((__le16 *)(res + 8));
Cheers,
Harvey
On Mon, Dec 22, 2008 at 05:07:35PM -0800, Harvey Harrison wrote:
> On Mon, 2008-12-22 at 15:06 -0800, Andrew Morton wrote:
> > On Fri, 19 Dec 2008 23:15:42 +0000
> > Adrian McMenamin <[email protected]> wrote:
> >
> > > Not sure what happened with earlier submissions, so here is an up-to-date
> > > patch for the Dreamcast Maple controlled.
> > >
> > >
> > > Add support for the SEGA Dreamcast Maple controller as a joystick
> > >
> > > ...
> > >
> > > +static void dc_pad_callback(struct mapleq *mq)
> > > +{
> > > + unsigned short buttons;
> > > + struct maple_device *mapledev = mq->dev;
> > > + struct dc_pad *pad = maple_get_drvdata(mapledev);
> > > + struct input_dev *dev = pad->dev;
> > > + unsigned char *res = mq->recvbuf;
> > > +
> > > + buttons = ~cpu_to_le16(*(unsigned short *)(res + 8));
> >
> > hm, could that be simplified?
>
> And as a bonus fix the obvious sparse warning:
>
> buttons = ~le16_to_cpup((__le16 *)(res + 8));
>
Below is what I woudl like to apply unless you see more issues with the
patch. Andrew, if you could test it on your hardware to make sure it
still works that woudl be great.
Thanks!
--
Dmitry
Input: add support for Maple controller as a joystick
From: Adrian McMenamin <[email protected]>
Add support for the SEGA Dreamcast Maple controller as a joystick
Signed-off-by: Adrian McMenamin <[email protected]>
Signed-off-by: Dmitry Torokhov <[email protected]>
---
drivers/input/joystick/Kconfig | 12 ++
drivers/input/joystick/Makefile | 1
drivers/input/joystick/maplecontrol.c | 193 +++++++++++++++++++++++++++++++++
3 files changed, 206 insertions(+), 0 deletions(-)
create mode 100644 drivers/input/joystick/maplecontrol.c
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index 268dd3f..b114195 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -306,4 +306,16 @@ config JOYSTICK_WALKERA0701
To compile this driver as a module, choose M here: the
module will be called walkera0701.
+config JOYSTICK_MAPLE
+ tristate "Dreamcast control pad"
+ depends on MAPLE
+ help
+ Say Y here if you have a SEGA Dreamcast and want to use your
+ controller as a joystick.
+
+ Most Dreamcast users will say Y.
+
+ To compile this as a module choose M here: the module will be called
+ maplecontrol.
+
endif
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
index 7230362..f397ec0 100644
--- a/drivers/input/joystick/Makefile
+++ b/drivers/input/joystick/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_JOYSTICK_AMIGA) += amijoy.o
obj-$(CONFIG_JOYSTICK_ANALOG) += analog.o
obj-$(CONFIG_JOYSTICK_COBRA) += cobra.o
obj-$(CONFIG_JOYSTICK_DB9) += db9.o
+obj-$(CONFIG_JOYSTICK_MAPLE) += maplecontrol.o
obj-$(CONFIG_JOYSTICK_GAMECON) += gamecon.o
obj-$(CONFIG_JOYSTICK_GF2K) += gf2k.o
obj-$(CONFIG_JOYSTICK_GRIP) += grip.o
diff --git a/drivers/input/joystick/maplecontrol.c b/drivers/input/joystick/maplecontrol.c
new file mode 100644
index 0000000..c2e8160
--- /dev/null
+++ b/drivers/input/joystick/maplecontrol.c
@@ -0,0 +1,193 @@
+/*
+ * SEGA Dreamcast controller driver
+ * Based on drivers/usb/iforce.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 controller driver");
+MODULE_LICENSE("GPL");
+
+struct dc_pad {
+ struct input_dev *dev;
+ struct maple_device *mdev;
+};
+
+static void dc_pad_callback(struct mapleq *mq)
+{
+ unsigned short buttons;
+ struct maple_device *mapledev = mq->dev;
+ struct dc_pad *pad = maple_get_drvdata(mapledev);
+ struct input_dev *dev = pad->dev;
+ unsigned char *res = mq->recvbuf;
+
+ buttons = ~le16_to_cpup((__le16 *)(res + 8));
+
+ input_report_abs(dev, ABS_HAT0Y,
+ (buttons & 0x0010 ? -1 : 0) + (buttons & 0x0020 ? 1 : 0));
+ input_report_abs(dev, ABS_HAT0X,
+ (buttons & 0x0040 ? -1 : 0) + (buttons & 0x0080 ? 1 : 0));
+ input_report_abs(dev, ABS_HAT1Y,
+ (buttons & 0x1000 ? -1 : 0) + (buttons & 0x2000 ? 1 : 0));
+ input_report_abs(dev, ABS_HAT1X,
+ (buttons & 0x4000 ? -1 : 0) + (buttons & 0x8000 ? 1 : 0));
+
+ input_report_key(dev, BTN_C, buttons & 0x0001);
+ input_report_key(dev, BTN_B, buttons & 0x0002);
+ input_report_key(dev, BTN_A, buttons & 0x0004);
+ input_report_key(dev, BTN_START, buttons & 0x0008);
+ input_report_key(dev, BTN_Z, buttons & 0x0100);
+ input_report_key(dev, BTN_Y, buttons & 0x0200);
+ input_report_key(dev, BTN_X, buttons & 0x0400);
+ input_report_key(dev, BTN_SELECT, buttons & 0x0800);
+
+ input_report_abs(dev, ABS_GAS, res[10]);
+ input_report_abs(dev, ABS_BRAKE, res[11]);
+ input_report_abs(dev, ABS_X, res[12]);
+ input_report_abs(dev, ABS_Y, res[13]);
+ input_report_abs(dev, ABS_RX, res[14]);
+ input_report_abs(dev, ABS_RY, res[15]);
+}
+
+static int dc_pad_open(struct input_dev *dev)
+{
+ struct dc_pad *pad = dev->dev.platform_data;
+
+ maple_getcond_callback(pad->mdev, dc_pad_callback, HZ/20,
+ MAPLE_FUNC_CONTROLLER);
+
+ return 0;
+}
+
+static void dc_pad_close(struct input_dev *dev)
+{
+ struct dc_pad *pad = dev->dev.platform_data;
+
+ maple_getcond_callback(pad->mdev, dc_pad_callback, 0,
+ MAPLE_FUNC_CONTROLLER);
+}
+
+/* allow the controller to be used */
+static int probe_maple_controller(struct device *dev)
+{
+ static const short btn_bit[32] = {
+ BTN_C, BTN_B, BTN_A, BTN_START, -1, -1, -1, -1,
+ BTN_Z, BTN_Y, BTN_X, BTN_SELECT, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+
+ static const short abs_bit[32] = {
+ -1, -1, -1, -1, ABS_HAT0Y, ABS_HAT0Y, ABS_HAT0X, ABS_HAT0X,
+ -1, -1, -1, -1, ABS_HAT1Y, ABS_HAT1Y, ABS_HAT1X, ABS_HAT1X,
+ ABS_GAS, ABS_BRAKE, ABS_X, ABS_Y, ABS_RX, ABS_RY, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ };
+
+ struct maple_device *mdev = to_maple_dev(dev);
+ struct maple_driver *mdrv = to_maple_driver(dev->driver);
+ int i, error;
+ struct dc_pad *pad;
+ struct input_dev *idev;
+ unsigned long data = be32_to_cpu(mdev->devinfo.function_data[0]);
+
+ pad = kzalloc(sizeof(struct dc_pad), GFP_KERNEL);
+ idev = input_allocate_device();
+ if (!pad || !idev) {
+ error = -ENOMEM;
+ goto fail;
+ }
+
+ pad->dev = idev;
+ pad->mdev = mdev;
+
+ idev->open = dc_pad_open;
+ idev->close = dc_pad_close;
+
+ for (i = 0; i < 32; i++) {
+ if (data & (1 << i)) {
+ if (btn_bit[i] >= 0)
+ __set_bit(btn_bit[i], idev->keybit);
+ else if (abs_bit[i] >= 0)
+ __set_bit(abs_bit[i], idev->absbit);
+ }
+ }
+
+ if (idev->keybit[BIT_WORD(BTN_JOYSTICK)])
+ idev->evbit[0] |= BIT_MASK(EV_KEY);
+
+ if (idev->absbit[0])
+ idev->evbit[0] |= BIT_MASK(EV_ABS);
+
+ for (i = ABS_X; i <= ABS_BRAKE; i++)
+ input_set_abs_params(idev, i, 0, 255, 0, 0);
+
+ for (i = ABS_HAT0X; i <= ABS_HAT3Y; i++)
+ input_set_abs_params(idev, i, 1, -1, 0, 0);
+
+ idev->dev.platform_data = pad;
+ idev->dev.parent = &mdev->dev;
+ idev->name = mdev->product_name;
+ idev->id.bustype = BUS_HOST;
+ input_set_drvdata(idev, pad);
+
+ error = input_register_device(idev);
+ if (error)
+ goto fail;
+
+ mdev->driver = mdrv;
+ maple_set_drvdata(mdev, pad);
+
+ return 0;
+
+fail:
+ input_free_device(idev);
+ kfree(pad);
+ maple_set_drvdata(mdev, NULL);
+ return error;
+}
+
+static int remove_maple_controller(struct device *dev)
+{
+ struct maple_device *mdev = to_maple_dev(dev);
+ struct dc_pad *pad = maple_get_drvdata(mdev);
+
+ mdev->callback = NULL;
+ input_unregister_device(pad->dev);
+ maple_set_drvdata(mdev, NULL);
+ kfree(pad);
+
+ return 0;
+}
+
+static struct maple_driver dc_pad_driver = {
+ .function = MAPLE_FUNC_CONTROLLER,
+ .drv = {
+ .name = "Dreamcast_controller",
+ .probe = probe_maple_controller,
+ .remove = remove_maple_controller,
+ },
+};
+
+static int __init dc_pad_init(void)
+{
+ return maple_driver_register(&dc_pad_driver);
+}
+
+static void __exit dc_pad_exit(void)
+{
+ maple_driver_unregister(&dc_pad_driver);
+}
+
+module_init(dc_pad_init);
+module_exit(dc_pad_exit);
On Tue, 2008-12-23 at 03:59 -0500, Dmitry Torokhov wrote:
> On Mon, Dec 22, 2008 at 05:07:35PM -0800, Harvey Harrison wrote:
> > On Mon, 2008-12-22 at 15:06 -0800, Andrew Morton wrote:
> > > On Fri, 19 Dec 2008 23:15:42 +0000
> > > Adrian McMenamin <[email protected]> wrote:
> > >
> > > > Not sure what happened with earlier submissions, so here is an up-to-date
> > > > patch for the Dreamcast Maple controlled.
> > > >
> > > >
> > > > Add support for the SEGA Dreamcast Maple controller as a joystick
> > > >
> > > > ...
> > > >
> > > > +static void dc_pad_callback(struct mapleq *mq)
> > > > +{
> > > > + unsigned short buttons;
> > > > + struct maple_device *mapledev = mq->dev;
> > > > + struct dc_pad *pad = maple_get_drvdata(mapledev);
> > > > + struct input_dev *dev = pad->dev;
> > > > + unsigned char *res = mq->recvbuf;
> > > > +
> > > > + buttons = ~cpu_to_le16(*(unsigned short *)(res + 8));
> > >
> > > hm, could that be simplified?
> >
> > And as a bonus fix the obvious sparse warning:
> >
> > buttons = ~le16_to_cpup((__le16 *)(res + 8));
> >
>
> Below is what I woudl like to apply unless you see more issues with the
> patch. Andrew, if you could test it on your hardware to make sure it
> still works that woudl be great.
>
> Thanks!
>
Just to say I've tested this with jstest now and it seems to be in good
working order. There is a calibration issue on one axis, but I think
that's my hardware :-)