|
@@ -1,8 +1,9 @@
|
|
/*
|
|
/*
|
|
- * X-Box gamepad - v0.0.5
|
|
|
|
|
|
+ * X-Box gamepad - v0.0.6
|
|
*
|
|
*
|
|
* Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de>
|
|
* Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de>
|
|
- *
|
|
|
|
|
|
+ * 2005 Dominic Cerquetti <binary1230@yahoo.com>
|
|
|
|
+ * 2006 Adam Buchbinder <adam.buchbinder@gmail.com>
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* modify it under the terms of the GNU General Public License as
|
|
@@ -30,9 +31,10 @@
|
|
* - Greg Kroah-Hartman - usb-skeleton driver
|
|
* - Greg Kroah-Hartman - usb-skeleton driver
|
|
*
|
|
*
|
|
* TODO:
|
|
* TODO:
|
|
- * - fine tune axes
|
|
|
|
|
|
+ * - fine tune axes (especially trigger axes)
|
|
* - fix "analog" buttons (reported as digital now)
|
|
* - fix "analog" buttons (reported as digital now)
|
|
* - get rumble working
|
|
* - get rumble working
|
|
|
|
+ * - need USB IDs for other dance pads
|
|
*
|
|
*
|
|
* History:
|
|
* History:
|
|
*
|
|
*
|
|
@@ -57,25 +59,40 @@
|
|
#include <linux/kernel.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/init.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/slab.h>
|
|
|
|
+#include <linux/stat.h>
|
|
#include <linux/module.h>
|
|
#include <linux/module.h>
|
|
|
|
+#include <linux/moduleparam.h>
|
|
#include <linux/smp_lock.h>
|
|
#include <linux/smp_lock.h>
|
|
#include <linux/usb/input.h>
|
|
#include <linux/usb/input.h>
|
|
|
|
|
|
-#define DRIVER_VERSION "v0.0.5"
|
|
|
|
|
|
+#define DRIVER_VERSION "v0.0.6"
|
|
#define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>"
|
|
#define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>"
|
|
#define DRIVER_DESC "X-Box pad driver"
|
|
#define DRIVER_DESC "X-Box pad driver"
|
|
|
|
|
|
#define XPAD_PKT_LEN 32
|
|
#define XPAD_PKT_LEN 32
|
|
|
|
|
|
|
|
+/* xbox d-pads should map to buttons, as is required for DDR pads
|
|
|
|
+ but we map them to axes when possible to simplify things */
|
|
|
|
+#define MAP_DPAD_TO_BUTTONS 0
|
|
|
|
+#define MAP_DPAD_TO_AXES 1
|
|
|
|
+#define MAP_DPAD_UNKNOWN -1
|
|
|
|
+
|
|
|
|
+static int dpad_to_buttons;
|
|
|
|
+module_param(dpad_to_buttons, bool, S_IRUGO);
|
|
|
|
+MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for unknown pads");
|
|
|
|
+
|
|
static const struct xpad_device {
|
|
static const struct xpad_device {
|
|
u16 idVendor;
|
|
u16 idVendor;
|
|
u16 idProduct;
|
|
u16 idProduct;
|
|
char *name;
|
|
char *name;
|
|
|
|
+ u8 dpad_mapping;
|
|
} xpad_device[] = {
|
|
} xpad_device[] = {
|
|
- { 0x045e, 0x0202, "Microsoft X-Box pad (US)" },
|
|
|
|
- { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)" },
|
|
|
|
- { 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)" },
|
|
|
|
- { 0x0000, 0x0000, "X-Box pad" }
|
|
|
|
|
|
+ { 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", MAP_DPAD_TO_AXES },
|
|
|
|
+ { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", MAP_DPAD_TO_AXES },
|
|
|
|
+ { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", MAP_DPAD_TO_AXES },
|
|
|
|
+ { 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", MAP_DPAD_TO_AXES },
|
|
|
|
+ { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS },
|
|
|
|
+ { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN }
|
|
};
|
|
};
|
|
|
|
|
|
static const signed short xpad_btn[] = {
|
|
static const signed short xpad_btn[] = {
|
|
@@ -84,11 +101,23 @@ static const signed short xpad_btn[] = {
|
|
-1 /* terminating entry */
|
|
-1 /* terminating entry */
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+/* only used if MAP_DPAD_TO_BUTTONS */
|
|
|
|
+static const signed short xpad_btn_pad[] = {
|
|
|
|
+ BTN_LEFT, BTN_RIGHT, /* d-pad left, right */
|
|
|
|
+ BTN_0, BTN_1, /* d-pad up, down (XXX names??) */
|
|
|
|
+ -1 /* terminating entry */
|
|
|
|
+};
|
|
|
|
+
|
|
static const signed short xpad_abs[] = {
|
|
static const signed short xpad_abs[] = {
|
|
ABS_X, ABS_Y, /* left stick */
|
|
ABS_X, ABS_Y, /* left stick */
|
|
ABS_RX, ABS_RY, /* right stick */
|
|
ABS_RX, ABS_RY, /* right stick */
|
|
ABS_Z, ABS_RZ, /* triggers left/right */
|
|
ABS_Z, ABS_RZ, /* triggers left/right */
|
|
- ABS_HAT0X, ABS_HAT0Y, /* digital pad */
|
|
|
|
|
|
+ -1 /* terminating entry */
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/* only used if MAP_DPAD_TO_AXES */
|
|
|
|
+static const signed short xpad_abs_pad[] = {
|
|
|
|
+ ABS_HAT0X, ABS_HAT0Y, /* d-pad axes */
|
|
-1 /* terminating entry */
|
|
-1 /* terminating entry */
|
|
};
|
|
};
|
|
|
|
|
|
@@ -100,14 +129,16 @@ static struct usb_device_id xpad_table [] = {
|
|
MODULE_DEVICE_TABLE (usb, xpad_table);
|
|
MODULE_DEVICE_TABLE (usb, xpad_table);
|
|
|
|
|
|
struct usb_xpad {
|
|
struct usb_xpad {
|
|
- struct input_dev *dev; /* input device interface */
|
|
|
|
- struct usb_device *udev; /* usb device */
|
|
|
|
|
|
+ struct input_dev *dev; /* input device interface */
|
|
|
|
+ struct usb_device *udev; /* usb device */
|
|
|
|
|
|
- struct urb *irq_in; /* urb for interrupt in report */
|
|
|
|
- unsigned char *idata; /* input data */
|
|
|
|
|
|
+ struct urb *irq_in; /* urb for interrupt in report */
|
|
|
|
+ unsigned char *idata; /* input data */
|
|
dma_addr_t idata_dma;
|
|
dma_addr_t idata_dma;
|
|
|
|
|
|
- char phys[65]; /* physical device path */
|
|
|
|
|
|
+ char phys[65]; /* physical device path */
|
|
|
|
+
|
|
|
|
+ int dpad_mapping; /* map d-pad to buttons or to axes */
|
|
};
|
|
};
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -137,14 +168,21 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
|
|
input_report_abs(dev, ABS_RZ, data[11]);
|
|
input_report_abs(dev, ABS_RZ, data[11]);
|
|
|
|
|
|
/* digital pad */
|
|
/* digital pad */
|
|
- input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04));
|
|
|
|
- input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01));
|
|
|
|
|
|
+ if (xpad->dpad_mapping == MAP_DPAD_TO_AXES) {
|
|
|
|
+ input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04));
|
|
|
|
+ input_report_abs(dev, ABS_HAT0Y, !!(data[2] & 0x02) - !!(data[2] & 0x01));
|
|
|
|
+ } else /* xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS */ {
|
|
|
|
+ input_report_key(dev, BTN_LEFT, data[2] & 0x04);
|
|
|
|
+ input_report_key(dev, BTN_RIGHT, data[2] & 0x08);
|
|
|
|
+ input_report_key(dev, BTN_0, data[2] & 0x01); // up
|
|
|
|
+ input_report_key(dev, BTN_1, data[2] & 0x02); // down
|
|
|
|
+ }
|
|
|
|
|
|
/* start/back buttons and stick press left/right */
|
|
/* start/back buttons and stick press left/right */
|
|
- input_report_key(dev, BTN_START, (data[2] & 0x10) >> 4);
|
|
|
|
- input_report_key(dev, BTN_BACK, (data[2] & 0x20) >> 5);
|
|
|
|
- input_report_key(dev, BTN_THUMBL, (data[2] & 0x40) >> 6);
|
|
|
|
- input_report_key(dev, BTN_THUMBR, data[2] >> 7);
|
|
|
|
|
|
+ input_report_key(dev, BTN_START, data[2] & 0x10);
|
|
|
|
+ input_report_key(dev, BTN_BACK, data[2] & 0x20);
|
|
|
|
+ input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
|
|
|
|
+ input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
|
|
|
|
|
|
/* "analog" buttons A, B, X, Y */
|
|
/* "analog" buttons A, B, X, Y */
|
|
input_report_key(dev, BTN_A, data[4]);
|
|
input_report_key(dev, BTN_A, data[4]);
|
|
@@ -206,6 +244,28 @@ static void xpad_close (struct input_dev *dev)
|
|
usb_kill_urb(xpad->irq_in);
|
|
usb_kill_urb(xpad->irq_in);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
|
|
|
|
+{
|
|
|
|
+ set_bit(abs, input_dev->absbit);
|
|
|
|
+
|
|
|
|
+ switch (abs) {
|
|
|
|
+ case ABS_X:
|
|
|
|
+ case ABS_Y:
|
|
|
|
+ case ABS_RX:
|
|
|
|
+ case ABS_RY: /* the two sticks */
|
|
|
|
+ input_set_abs_params(input_dev, abs, -32768, 32767, 16, 128);
|
|
|
|
+ break;
|
|
|
|
+ case ABS_Z:
|
|
|
|
+ case ABS_RZ: /* the triggers */
|
|
|
|
+ input_set_abs_params(input_dev, abs, 0, 255, 0, 0);
|
|
|
|
+ break;
|
|
|
|
+ case ABS_HAT0X:
|
|
|
|
+ case ABS_HAT0Y: /* the d-pad (only if MAP_DPAD_TO_AXES) */
|
|
|
|
+ input_set_abs_params(input_dev, abs, -1, 1, 0, 0);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
|
static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
|
{
|
|
{
|
|
struct usb_device *udev = interface_to_usbdev (intf);
|
|
struct usb_device *udev = interface_to_usbdev (intf);
|
|
@@ -235,6 +295,9 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
|
goto fail2;
|
|
goto fail2;
|
|
|
|
|
|
xpad->udev = udev;
|
|
xpad->udev = udev;
|
|
|
|
+ xpad->dpad_mapping = xpad_device[i].dpad_mapping;
|
|
|
|
+ if (xpad->dpad_mapping == MAP_DPAD_UNKNOWN)
|
|
|
|
+ xpad->dpad_mapping = dpad_to_buttons;
|
|
xpad->dev = input_dev;
|
|
xpad->dev = input_dev;
|
|
usb_make_path(udev, xpad->phys, sizeof(xpad->phys));
|
|
usb_make_path(udev, xpad->phys, sizeof(xpad->phys));
|
|
strlcat(xpad->phys, "/input0", sizeof(xpad->phys));
|
|
strlcat(xpad->phys, "/input0", sizeof(xpad->phys));
|
|
@@ -249,32 +312,19 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
|
|
|
|
|
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
|
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
|
|
|
|
|
|
|
|
+ /* set up buttons */
|
|
for (i = 0; xpad_btn[i] >= 0; i++)
|
|
for (i = 0; xpad_btn[i] >= 0; i++)
|
|
set_bit(xpad_btn[i], input_dev->keybit);
|
|
set_bit(xpad_btn[i], input_dev->keybit);
|
|
|
|
+ if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS)
|
|
|
|
+ for (i = 0; xpad_btn_pad[i] >= 0; i++)
|
|
|
|
+ set_bit(xpad_btn_pad[i], input_dev->keybit);
|
|
|
|
|
|
- for (i = 0; xpad_abs[i] >= 0; i++) {
|
|
|
|
-
|
|
|
|
- signed short t = xpad_abs[i];
|
|
|
|
-
|
|
|
|
- set_bit(t, input_dev->absbit);
|
|
|
|
-
|
|
|
|
- switch (t) {
|
|
|
|
- case ABS_X:
|
|
|
|
- case ABS_Y:
|
|
|
|
- case ABS_RX:
|
|
|
|
- case ABS_RY: /* the two sticks */
|
|
|
|
- input_set_abs_params(input_dev, t, -32768, 32767, 16, 128);
|
|
|
|
- break;
|
|
|
|
- case ABS_Z:
|
|
|
|
- case ABS_RZ: /* the triggers */
|
|
|
|
- input_set_abs_params(input_dev, t, 0, 255, 0, 0);
|
|
|
|
- break;
|
|
|
|
- case ABS_HAT0X:
|
|
|
|
- case ABS_HAT0Y: /* the d-pad */
|
|
|
|
- input_set_abs_params(input_dev, t, -1, 1, 0, 0);
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ /* set up axes */
|
|
|
|
+ for (i = 0; xpad_abs[i] >= 0; i++)
|
|
|
|
+ xpad_set_up_abs(input_dev, xpad_abs[i]);
|
|
|
|
+ if (xpad->dpad_mapping == MAP_DPAD_TO_AXES)
|
|
|
|
+ for (i = 0; xpad_abs_pad[i] >= 0; i++)
|
|
|
|
+ xpad_set_up_abs(input_dev, xpad_abs_pad[i]);
|
|
|
|
|
|
ep_irq_in = &intf->cur_altsetting->endpoint[0].desc;
|
|
ep_irq_in = &intf->cur_altsetting->endpoint[0].desc;
|
|
usb_fill_int_urb(xpad->irq_in, udev,
|
|
usb_fill_int_urb(xpad->irq_in, udev,
|
|
@@ -305,7 +355,8 @@ static void xpad_disconnect(struct usb_interface *intf)
|
|
usb_kill_urb(xpad->irq_in);
|
|
usb_kill_urb(xpad->irq_in);
|
|
input_unregister_device(xpad->dev);
|
|
input_unregister_device(xpad->dev);
|
|
usb_free_urb(xpad->irq_in);
|
|
usb_free_urb(xpad->irq_in);
|
|
- usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
|
|
|
|
|
|
+ usb_buffer_free(interface_to_usbdev(intf), XPAD_PKT_LEN,
|
|
|
|
+ xpad->idata, xpad->idata_dma);
|
|
kfree(xpad);
|
|
kfree(xpad);
|
|
}
|
|
}
|
|
}
|
|
}
|