diff -Naur linux-3.15.6.orig/drivers/input/joystick/xpad.c linux-3.15.6/drivers/input/joystick/xpad.c --- linux-3.15.6.orig/drivers/input/joystick/xpad.c 2014-08-01 20:37:09.039708016 -0700 +++ linux-3.15.6/drivers/input/joystick/xpad.c 2014-08-01 19:44:20.521111000 -0700 @@ -281,17 +281,21 @@ 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; }; /* @@ -435,6 +439,109 @@ 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 @@ -456,11 +563,35 @@ /* 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_KERNEL); + 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_KERNEL); + 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)) @@ -476,6 +607,8 @@ int retval, status; status = urb->status; + +// printk("xpad_irq_in %i\n", status); switch (status) { case 0: @@ -584,8 +717,6 @@ goto fail1; } - mutex_init(&xpad->odata_mutex); - xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL); if (!xpad->irq_out) { error = -ENOMEM; @@ -714,15 +845,38 @@ static void xpad_send_led_command(struct usb_xpad *xpad, int command) { - if (command >= 0 && command < 14) { - mutex_lock(&xpad->odata_mutex); - xpad->odata[0] = 0x01; - xpad->odata[1] = 0x03; - xpad->odata[2] = command; - xpad->irq_out->transfer_buffer_length = 3; - usb_submit_urb(xpad->irq_out, GFP_KERNEL); - mutex_unlock(&xpad->odata_mutex); + if ((unsigned)command > 15) + return; + + spin_lock(&xpad->odata_lock); + + switch (xpad->xtype) { + + case XTYPE_XBOX360: + xpad->odata[0] = 0x01; + xpad->odata[1] = 0x03; + xpad->odata[2] = command; + xpad->irq_out->transfer_buffer_length = 3; + break; + case XTYPE_XBOX360W: + xpad->odata[0] = 0x00; + xpad->odata[1] = 0x00; + xpad->odata[2] = 0x08; + xpad->odata[3] = 0x40 + (command % 0x0e); + 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; + break; } + + usb_submit_urb(xpad->irq_out, GFP_KERNEL); + spin_unlock(&xpad->odata_lock); } static void xpad_led_set(struct led_classdev *led_cdev, @@ -741,8 +895,10 @@ struct xpad_led *led; struct led_classdev *led_cdev; int error; + +// printk("xpad_led_probe\n"); - if (xpad->xtype != XTYPE_XBOX360) + if (xpad->xtype != XTYPE_XBOX360 && xpad->xtype != XTYPE_XBOX360W) return 0; xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL); @@ -765,11 +921,6 @@ return error; } - /* - * Light up the segment corresponding to controller number - */ - xpad_send_led_command(xpad, (led_no % 4) + 2); - return 0; } @@ -791,6 +942,7 @@ 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) @@ -839,23 +991,24 @@ { 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) && (le16_to_cpu(udev->descriptor.idProduct) == xpad_device[i].idProduct)) break; } - + 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); if (!xpad->idata) { @@ -891,65 +1044,12 @@ 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) @@ -963,10 +1063,6 @@ 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) { @@ -974,6 +1070,7 @@ * 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; @@ -1015,23 +1112,55 @@ */ 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; @@ -1041,8 +1170,14 @@ { 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) {