blob: 96b8acc24aaa43bd262e8ddcba4991fa4d90c8f1 [file] [log] [blame]
---
drivers/input/joystick/xpad.c | 273 ++++++++++++++++++++++++++++--------------
1 file changed, 188 insertions(+), 85 deletions(-)
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -282,17 +282,21 @@ struct usb_xpad {
struct urb *irq_out; /* urb for interrupt out report */
unsigned char *odata; /* output data */
dma_addr_t odata_dma;
- struct mutex odata_mutex;
+ spinlock_t odata_lock;
#endif
#if defined(CONFIG_JOYSTICK_XPAD_LEDS)
struct xpad_led *led;
#endif
+ int joydev_id;
+
char phys[64]; /* physical device path */
int mapping; /* map d-pad to buttons or to axes */
int xtype; /* type of xbox device */
+
+ const char *name;
};
/*
@@ -436,6 +440,109 @@ static void xpad360_process_packet(struc
input_sync(dev);
}
+static void xpad_send_led_command(struct usb_xpad *xpad, int command);
+static int xpad_open(struct input_dev *dev);
+static void xpad_close(struct input_dev *dev);
+static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs);
+static int xpad_init_ff(struct usb_xpad *xpad);
+static int xpad_find_joydev(struct device *dev, void *data)
+{
+ if (strstr(dev_name(dev), "js"))
+ return 1;
+
+ return 0;
+}
+
+static struct workqueue_struct *my_wq;
+
+typedef struct {
+ struct work_struct my_work;
+ struct usb_xpad *xpad;
+} my_work_t;
+
+static void my_wq_function( struct work_struct *work)
+{
+ my_work_t *my_work = (my_work_t *)work;
+
+ struct usb_xpad *xpad = my_work->xpad;
+
+ if (xpad->pad_present) {
+
+ struct input_dev *input_dev;
+ int i;
+
+ input_dev = input_allocate_device();
+
+ xpad->dev = input_dev;
+ input_dev->name = xpad->name;
+ input_dev->phys = xpad->phys;
+ usb_to_input_id(xpad->udev, &input_dev->id);
+ input_dev->dev.parent = &xpad->intf->dev;
+
+ input_set_drvdata(input_dev, xpad);
+
+ input_dev->open = xpad_open;
+ input_dev->close = xpad_close;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY);
+
+ if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
+ input_dev->evbit[0] |= BIT_MASK(EV_ABS);
+ /* set up axes */
+ for (i = 0; xpad_abs[i] >= 0; i++)
+ xpad_set_up_abs(input_dev, xpad_abs[i]);
+ }
+
+ /* set up standard buttons */
+ for (i = 0; xpad_common_btn[i] >= 0; i++)
+ __set_bit(xpad_common_btn[i], input_dev->keybit);
+
+ /* set up model-specific ones */
+ if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W) {
+ for (i = 0; xpad360_btn[i] >= 0; i++)
+ __set_bit(xpad360_btn[i], input_dev->keybit);
+ } else {
+ for (i = 0; xpad_btn[i] >= 0; i++)
+ __set_bit(xpad_btn[i], input_dev->keybit);
+ }
+
+ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
+ for (i = 0; xpad_btn_pad[i] >= 0; i++)
+ __set_bit(xpad_btn_pad[i], input_dev->keybit);
+ } else {
+ for (i = 0; xpad_abs_pad[i] >= 0; i++)
+ xpad_set_up_abs(input_dev, xpad_abs_pad[i]);
+ }
+
+ if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
+ for (i = 0; xpad_btn_triggers[i] >= 0; i++)
+ __set_bit(xpad_btn_triggers[i], input_dev->keybit);
+ } else {
+ for (i = 0; xpad_abs_triggers[i] >= 0; i++)
+ xpad_set_up_abs(input_dev, xpad_abs_triggers[i]);
+ }
+
+ input_register_device(xpad->dev);
+
+ {
+ struct device* joydev_dev = device_find_child(&xpad->dev->dev, NULL, xpad_find_joydev);
+
+ if (joydev_dev) {
+// printk("found joydev child with minor %i\n", MINOR(joydev_dev->devt));
+ xpad->joydev_id = MINOR(joydev_dev->devt);
+ xpad_send_led_command(xpad, (xpad->joydev_id % 4) + 2);
+ }
+ }
+
+ xpad_init_ff(xpad);
+ } else {
+ input_unregister_device(xpad->dev);
+ }
+
+ kfree( (void *)work );
+
+ return;
+}
/*
* xpad360w_process_packet
@@ -457,12 +564,32 @@ static void xpad360w_process_packet(stru
/* Presence change */
if (data[0] & 0x08) {
if (data[1] & 0x80) {
- xpad->pad_present = 1;
- usb_submit_urb(xpad->bulk_out, GFP_ATOMIC);
- } else
- xpad->pad_present = 0;
+ if (!xpad->pad_present) {
+ my_work_t * work;
+ xpad->pad_present = 1;
+ usb_submit_urb(xpad->bulk_out, GFP_ATOMIC);
+
+ work = (my_work_t *)kmalloc(sizeof(my_work_t), GFP_ATOMIC);
+ INIT_WORK( (struct work_struct *)work, my_wq_function );
+ work->xpad = xpad;
+ queue_work( my_wq, (struct work_struct *)work );
+ }
+ } else {
+ if (xpad->pad_present) {
+ my_work_t * work;
+ xpad->pad_present = 0;
+
+ work = (my_work_t *)kmalloc(sizeof(my_work_t), GFP_ATOMIC);
+ INIT_WORK( (struct work_struct *)work, my_wq_function );
+ work->xpad = xpad;
+ queue_work( my_wq, (struct work_struct *)work );
+ }
+// printk("got kill packet for id %i\n", xpad->joydev_id);
+ }
}
+// printk("xpad packet %hhX %hhX %hhX %hhX %hhX %hhX\n", data[0], data[1], data[2], data[3], data[4], data[5]);
+
/* Valid pad data */
if (!(data[1] & 0x1))
return;
@@ -585,8 +712,6 @@ static int xpad_init_output(struct usb_i
goto fail1;
}
- mutex_init(&xpad->odata_mutex);
-
xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL);
if (!xpad->irq_out) {
error = -ENOMEM;
@@ -718,7 +843,7 @@ static void xpad_send_led_command(struct
if (command > 15)
return;
- mutex_lock(&xpad->odata_mutex);
+ spin_lock(&xpad->odata_lock);
switch (xpad->xtype) {
case XTYPE_XBOX360:
@@ -745,7 +870,7 @@ static void xpad_send_led_command(struct
}
usb_submit_urb(xpad->irq_out, GFP_KERNEL);
- mutex_unlock(&xpad->odata_mutex);
+ spin_unlock(&xpad->odata_lock);
}
static void xpad_led_set(struct led_classdev *led_cdev,
@@ -788,11 +913,6 @@ static int xpad_led_probe(struct usb_xpa
return error;
}
- /*
- * Light up the segment corresponding to controller number
- */
- xpad_send_led_command(xpad, (led_no % 4) + 2);
-
return 0;
}
@@ -814,6 +934,7 @@ static void xpad_led_disconnect(struct u
static int xpad_open(struct input_dev *dev)
{
struct usb_xpad *xpad = input_get_drvdata(dev);
+// printk("xpad open driver data %x\n", (unsigned int)xpad);
/* URB was submitted in probe */
if (xpad->xtype == XTYPE_XBOX360W)
@@ -862,9 +983,13 @@ static int xpad_probe(struct usb_interfa
{
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_xpad *xpad;
- struct input_dev *input_dev;
struct usb_endpoint_descriptor *ep_irq_in;
int i, error;
+// struct input_dev *input_dev;
+
+ if (!my_wq) {
+ my_wq = create_workqueue("xpad_queue");
+ }
for (i = 0; xpad_device[i].idVendor; i++) {
if ((le16_to_cpu(udev->descriptor.idVendor) == xpad_device[i].idVendor) &&
@@ -873,11 +998,8 @@ static int xpad_probe(struct usb_interfa
}
xpad = kzalloc(sizeof(struct usb_xpad), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!xpad || !input_dev) {
- error = -ENOMEM;
- goto fail1;
- }
+
+ xpad->name = xpad_device[i].name;
xpad->idata = usb_alloc_coherent(udev, XPAD_PKT_LEN,
GFP_KERNEL, &xpad->idata_dma);
@@ -914,65 +1036,12 @@ static int xpad_probe(struct usb_interfa
xpad->mapping |= MAP_STICKS_TO_NULL;
}
- xpad->dev = input_dev;
- usb_make_path(udev, xpad->phys, sizeof(xpad->phys));
- strlcat(xpad->phys, "/input0", sizeof(xpad->phys));
-
- input_dev->name = xpad_device[i].name;
- input_dev->phys = xpad->phys;
- usb_to_input_id(udev, &input_dev->id);
- input_dev->dev.parent = &intf->dev;
-
- input_set_drvdata(input_dev, xpad);
-
- input_dev->open = xpad_open;
- input_dev->close = xpad_close;
-
- input_dev->evbit[0] = BIT_MASK(EV_KEY);
-
- if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
- input_dev->evbit[0] |= BIT_MASK(EV_ABS);
- /* set up axes */
- for (i = 0; xpad_abs[i] >= 0; i++)
- xpad_set_up_abs(input_dev, xpad_abs[i]);
- }
-
- /* set up standard buttons */
- for (i = 0; xpad_common_btn[i] >= 0; i++)
- __set_bit(xpad_common_btn[i], input_dev->keybit);
-
- /* set up model-specific ones */
- if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W) {
- for (i = 0; xpad360_btn[i] >= 0; i++)
- __set_bit(xpad360_btn[i], input_dev->keybit);
- } else {
- for (i = 0; xpad_btn[i] >= 0; i++)
- __set_bit(xpad_btn[i], input_dev->keybit);
- }
-
- if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
- for (i = 0; xpad_btn_pad[i] >= 0; i++)
- __set_bit(xpad_btn_pad[i], input_dev->keybit);
- } else {
- for (i = 0; xpad_abs_pad[i] >= 0; i++)
- xpad_set_up_abs(input_dev, xpad_abs_pad[i]);
- }
-
- if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
- for (i = 0; xpad_btn_triggers[i] >= 0; i++)
- __set_bit(xpad_btn_triggers[i], input_dev->keybit);
- } else {
- for (i = 0; xpad_abs_triggers[i] >= 0; i++)
- xpad_set_up_abs(input_dev, xpad_abs_triggers[i]);
- }
-
error = xpad_init_output(intf, xpad);
if (error)
goto fail3;
- error = xpad_init_ff(xpad);
- if (error)
- goto fail4;
+ usb_make_path(xpad->udev, xpad->phys, sizeof(xpad->phys));
+ strlcat(xpad->phys, "/input0", sizeof(xpad->phys));
error = xpad_led_probe(xpad);
if (error)
@@ -986,10 +1055,6 @@ static int xpad_probe(struct usb_interfa
xpad->irq_in->transfer_dma = xpad->idata_dma;
xpad->irq_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- error = input_register_device(xpad->dev);
- if (error)
- goto fail6;
-
usb_set_intfdata(intf, xpad);
if (xpad->xtype == XTYPE_XBOX360W) {
@@ -997,6 +1062,7 @@ static int xpad_probe(struct usb_interfa
* Setup the message to set the LEDs on the
* controller when it shows up
*/
+ spin_lock(&xpad->odata_lock);
xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL);
if (!xpad->bulk_out) {
error = -ENOMEM;
@@ -1048,23 +1114,55 @@ static int xpad_probe(struct usb_interfa
*/
xpad->irq_in->dev = xpad->udev;
error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
+
+ spin_unlock(&xpad->odata_lock);
if (error)
goto fail9;
+
+ // I don't know how to check controller state on driver load so just slam them
+ // off so that people have to turn them on, triggering a state update
+
+ // got the power off packet from an OSX reverse-engineered driver:
+ // http://tattiebogle.net/index.php/ProjectRoot/Xbox360Controller/OsxDriver#toc1
+ spin_lock(&xpad->odata_lock);
+ xpad->odata[0] = 0x00;
+ xpad->odata[1] = 0x00;
+ xpad->odata[2] = 0x08;
+ xpad->odata[3] = 0xC0;
+ xpad->odata[4] = 0x00;
+ xpad->odata[5] = 0x00;
+ xpad->odata[6] = 0x00;
+ xpad->odata[7] = 0x00;
+ xpad->odata[8] = 0x00;
+ xpad->odata[9] = 0x00;
+ xpad->odata[10] = 0x00;
+ xpad->odata[11] = 0x00;
+ xpad->irq_out->transfer_buffer_length = 12;
+ usb_submit_urb(xpad->irq_out, GFP_KERNEL);
+ spin_unlock(&xpad->odata_lock);
+ } else {
+ my_work_t *work;
+ xpad->pad_present = 1;
+
+ work = (my_work_t *)kmalloc(sizeof(my_work_t), GFP_KERNEL);
+ INIT_WORK( (struct work_struct *)work, my_wq_function );
+ work->xpad = xpad;
+ queue_work( my_wq, (struct work_struct *)work );
}
return 0;
fail9: kfree(xpad->bdata);
fail8: usb_free_urb(xpad->bulk_out);
- fail7: input_unregister_device(input_dev);
- input_dev = NULL;
+ fail7: //input_unregister_device(input_dev);
+ //input_dev = NULL;
fail6: xpad_led_disconnect(xpad);
- fail5: if (input_dev)
- input_ff_destroy(input_dev);
+ fail5: //if (input_dev)
+ //input_ff_destroy(input_dev);
fail4: xpad_deinit_output(xpad);
fail3: usb_free_urb(xpad->irq_in);
fail2: usb_free_coherent(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
- fail1: input_free_device(input_dev);
+ fail1: //input_free_device(input_dev);
kfree(xpad);
return error;
@@ -1074,8 +1172,13 @@ static void xpad_disconnect(struct usb_i
{
struct usb_xpad *xpad = usb_get_intfdata (intf);
+// printk("xpad_disconnect\n");
xpad_led_disconnect(xpad);
- input_unregister_device(xpad->dev);
+
+ if (xpad->pad_present) {
+ xpad->pad_present = 0;
+ input_unregister_device(xpad->dev);
+ }
xpad_deinit_output(xpad);
if (xpad->xtype == XTYPE_XBOX360W) {