On Sun, Jun 12, 2011 at 05:49:49PM -0700, Chris Moeller wrote: > This patch removes the non-functional bulk output URB method for setting > XBox360 Wireless Controller player number indicators on controller > activation, and replaces it with a functional IRQ output URB method. It > also implements the LED command control for these devices. > > Signed-off-by: Chris Moeller <kode54@xxxxxxxxx> > > --- > > I chose to duplicate the LED command setting function in the > xpad360w_process_packet function, as the other LED setting function is > designed to require mutex locking, which I found to deadlock the driver > when used in that manner. I will consider adding a lock, as testing with > a rumble flooding application collided with the LED control and > prevented it from setting the player number on connect. I'm not even > sure how the mutex could be deadlocking in the input packet handler, or > even what good it would do in that case, since the rumble setting > functions don't lock it. In fact, only the LED setting function locks > it. If 2 functions share the same URB then we need to arbitrate access to URB data buffers, etc, etc. I believe the patch below could be used as a starting point. Thanks. -- Dmitry Input: xpad - wireless LED setting From: Chris Moeller <kode54@xxxxxxxxx> This patch removes the non-functional bulk output URB method for setting XBox360 Wireless Controller player number indicators on controller activation, and replaces it with a functional IRQ output URB method. It also implements the LED command control for these devices. Signed-off-by: Chris Moeller <kode54@xxxxxxxxx> Signed-off-by: Dmitry Torokhov <dtor@xxxxxxx> --- drivers/input/joystick/xpad.c | 699 ++++++++++++++++++++++------------------- 1 files changed, 379 insertions(+), 320 deletions(-) diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index d728875..e2dbe54 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -253,23 +253,28 @@ struct usb_xpad { struct input_dev *dev; /* input device interface */ struct usb_device *udev; /* usb device */ - int pad_present; + int interface_number; struct urb *irq_in; /* urb for interrupt in report */ unsigned char *idata; /* input data */ dma_addr_t idata_dma; - struct urb *bulk_out; - unsigned char *bdata; - #if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS) 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; + bool irq_out_pending; + + bool led_pending; + int led_command; + + bool ff_pending; + u16 rumble_strong; + u16 rumble_weak; #endif -#if defined(CONFIG_JOYSTICK_XPAD_LEDS) +#ifdef CONFIG_JOYSTICK_XPAD_LEDS struct xpad_led *led; #endif @@ -279,6 +284,369 @@ struct usb_xpad { int xtype; /* type of xbox device */ }; +#ifdef CONFIG_JOYSTICK_XPAD_FF +static bool xpad_format_rumble(struct usb_xpad *xpad, u16 strong, u16 weak) +{ + switch (xpad->xtype) { + + case XTYPE_XBOX: + xpad->odata[0] = 0x00; + xpad->odata[1] = 0x06; + xpad->odata[2] = 0x00; + xpad->odata[3] = strong / 256; /* left actuator */ + xpad->odata[4] = 0x00; + xpad->odata[5] = weak / 256; /* right actuator */ + xpad->irq_out->transfer_buffer_length = 6; + + return true; + + case XTYPE_XBOX360: + xpad->odata[0] = 0x00; + xpad->odata[1] = 0x08; + xpad->odata[2] = 0x00; + xpad->odata[3] = strong / 256; /* left actuator? */ + xpad->odata[4] = weak / 256; /* right actuator? */ + xpad->odata[5] = 0x00; + xpad->odata[6] = 0x00; + xpad->odata[7] = 0x00; + xpad->irq_out->transfer_buffer_length = 8; + + return true; + + case XTYPE_XBOX360W: + xpad->odata[0] = 0x00; + xpad->odata[1] = 0x01; + xpad->odata[2] = 0x0F; + xpad->odata[3] = 0xC0; + xpad->odata[4] = 0x00; + xpad->odata[5] = strong / 256; + xpad->odata[6] = weak / 256; + 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; + + return true; + + default: + dbg("%s - rumble command sent to unsupported xpad type: %d", + __func__, xpad->xtype); + return false; + } +} + +static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) +{ + struct usb_xpad *xpad = input_get_drvdata(dev); + + if (effect->type == FF_RUMBLE) { + u16 strong = effect->u.rumble.strong_magnitude; + u16 weak = effect->u.rumble.weak_magnitude; + unsigned long flags; + + spin_lock_irqsave(&xpad->odata_lock, flags); + + if (xpad->irq_out_pending) { + xpad->rumble_strong = strong; + xpad->rumble_weak = weak; + xpad->ff_pending = true; + } else if (xpad_format_rumble(xpad, strong, weak)) { + xpad->irq_out_pending = true; + usb_submit_urb(xpad->irq_out, GFP_ATOMIC); + } + + spin_unlock_irqrestore(&xpad->odata_lock, flags); + } + + return 0; +} + +static int xpad_init_ff(struct usb_xpad *xpad) +{ + if (xpad->xtype == XTYPE_UNKNOWN) + return 0; + + input_set_capability(xpad->dev, EV_FF, FF_RUMBLE); + + return input_ff_create_memless(xpad->dev, NULL, xpad_play_effect); +} + +#else + +static int xpad_init_ff(struct usb_xpad *xpad) +{ + return 0; +} + +#ifdef CONFIG_JOYSTICK_XPAD_LEDS +static bool xpad_format_rumble(struct usb_xpad *xpad, u16 strong, u16 weak) +{ + return false; +} +#endif + +#endif + +#ifdef CONFIG_JOYSTICK_XPAD_LEDS +#include <linux/leds.h> + +struct xpad_led { + char name[16]; + struct led_classdev led_cdev; + struct usb_xpad *xpad; +}; + +static bool xpad_format_led_command(struct usb_xpad *xpad, int command) +{ + switch (xpad->xtype) { + case XTYPE_XBOX: + case XTYPE_XBOX360: + if (command >= 0 && command < 14) { + xpad->odata[0] = 0x01; + xpad->odata[1] = 0x03; + xpad->odata[2] = command; + xpad->irq_out->transfer_buffer_length = 3; + return true; + } + break; + + case XTYPE_XBOX360W: + if (command >= 0 && command <= 16) { + if (command == 16) + command = 2 + (xpad->interface_number & 6) / 2; + xpad->odata[0] = 0x00; + xpad->odata[1] = 0x00; + xpad->odata[2] = 0x08; + xpad->odata[3] = 0x40 + command; + 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; + return true; + } + break; + } + + return false; +} + +static void xpad_send_led_command(struct usb_xpad *xpad, int command) +{ + unsigned long flags; + + spin_lock_irqsave(&xpad->odata_lock, flags); + + if (xpad->irq_out_pending) { + xpad->led_command = command; + xpad->led_pending = true; + } else if (xpad_format_led_command(xpad, command)) { + xpad->irq_out_pending = true; + usb_submit_urb(xpad->irq_out, GFP_ATOMIC); + } + + spin_unlock_irqrestore(&xpad->odata_lock, flags); +} + +static void xpad_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct xpad_led *xpad_led = container_of(led_cdev, + struct xpad_led, led_cdev); + + xpad_send_led_command(xpad_led->xpad, value); +} + +static int xpad_led_probe(struct usb_xpad *xpad) +{ + static atomic_t led_seq = ATOMIC_INIT(0); + long led_no; + struct xpad_led *led; + struct led_classdev *led_cdev; + int error; + + if (xpad->xtype != XTYPE_XBOX360 && xpad->xtype != XTYPE_XBOX360W) + return 0; + + xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + led_no = (long)atomic_inc_return(&led_seq) - 1; + + snprintf(led->name, sizeof(led->name), "xpad%ld", led_no); + led->xpad = xpad; + + led_cdev = &led->led_cdev; + led_cdev->name = led->name; + led_cdev->brightness_set = xpad_led_set; + + error = led_classdev_register(&xpad->udev->dev, led_cdev); + if (error) { + kfree(led); + xpad->led = NULL; + return error; + } + + /* + * Light up the segment corresponding to controller number + */ + if (xpad->xtype == XTYPE_XBOX360) + xpad_send_led_command(xpad, (led_no % 4) + 2); + + return 0; +} + +static void xpad_led_disconnect(struct usb_xpad *xpad) +{ + struct xpad_led *xpad_led = xpad->led; + + if (xpad_led) { + led_classdev_unregister(&xpad_led->led_cdev); + kfree(xpad_led); + } +} + +#else + +static int xpad_led_probe(struct usb_xpad *xpad) +{ + return 0; +} + +static void xpad_led_disconnect(struct usb_xpad *xpad) +{ +} + +#ifdef CONFIG_JOYSTICK_XPAD_FF +static bool xpad_format_led_command(struct usb_xpad *xpad, int command) +{ + return false; +} +#endif + +static void xpad_send_led_command(struct usb_xpad *xpad, int command) +{ +} + +#endif + +#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS) +static void xpad_irq_out(struct urb *urb) +{ + struct usb_xpad *xpad = urb->context; + int status = urb->status; + int retval; + unsigned long flags; + + switch (status) { + case 0: + /* success */ + spin_lock_irqsave(&xpad->odata_lock, flags); + xpad->irq_out_pending = false; + + if (xpad->ff_pending) { + xpad->ff_pending = false; + if (xpad_format_rumble(xpad, + xpad->rumble_strong, + xpad->rumble_weak)) { + xpad->irq_out_pending = true; + usb_submit_urb(urb, GFP_ATOMIC); + } + } else if (xpad->led_pending) { + xpad->led_pending = false; + if (xpad_format_led_command(xpad, + xpad->led_command)) { + xpad->irq_out_pending = true; + usb_submit_urb(urb, GFP_ATOMIC); + } + } + + spin_unlock_irqrestore(&xpad->odata_lock, flags); + return; + + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", __func__, status); + return; + + default: + dbg("%s - nonzero urb status received: %d", __func__, status); + goto exit; + } + +exit: + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) + err("%s - usb_submit_urb failed with result %d", + __func__, retval); +} + +static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) +{ + struct usb_endpoint_descriptor *ep_irq_out; + int error; + + if (xpad->xtype == XTYPE_UNKNOWN) + return 0; + + spin_lock_init(&xpad->odata_lock); + + xpad->odata = usb_alloc_coherent(xpad->udev, XPAD_PKT_LEN, + GFP_KERNEL, &xpad->odata_dma); + if (!xpad->odata) { + error = -ENOMEM; + goto fail1; + } + + xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL); + if (!xpad->irq_out) { + error = -ENOMEM; + goto fail2; + } + + ep_irq_out = &intf->cur_altsetting->endpoint[1].desc; + usb_fill_int_urb(xpad->irq_out, xpad->udev, + usb_sndintpipe(xpad->udev, ep_irq_out->bEndpointAddress), + xpad->odata, XPAD_PKT_LEN, + xpad_irq_out, xpad, ep_irq_out->bInterval); + xpad->irq_out->transfer_dma = xpad->odata_dma; + xpad->irq_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + return 0; + + fail2: usb_free_coherent(xpad->udev, XPAD_PKT_LEN, xpad->odata, xpad->odata_dma); + fail1: return error; +} + +static void xpad_stop_output(struct usb_xpad *xpad) +{ + if (xpad->xtype != XTYPE_UNKNOWN) + usb_kill_urb(xpad->irq_out); +} + +static void xpad_deinit_output(struct usb_xpad *xpad) +{ + if (xpad->xtype != XTYPE_UNKNOWN) { + usb_free_urb(xpad->irq_out); + usb_free_coherent(xpad->udev, XPAD_PKT_LEN, + xpad->odata, xpad->odata_dma); + } +} +#else +static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) { return 0; } +static void xpad_deinit_output(struct usb_xpad *xpad) { } +static void xpad_stop_output(struct usb_xpad *xpad) { } +#endif + /* * xpad_process_packet * @@ -439,13 +807,8 @@ static void xpad360_process_packet(struct usb_xpad *xpad, static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data) { /* 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 ((data[0] & 0x08) && (data[1] & 0x80)) + xpad_send_led_command(xpad, 16); /* Valid pad data */ if (!(data[1] & 0x1)) @@ -496,271 +859,6 @@ exit: __func__, retval); } -static void xpad_bulk_out(struct urb *urb) -{ - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __func__, urb->status); - break; - default: - dbg("%s - nonzero urb status received: %d", __func__, urb->status); - } -} - -#if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS) -static void xpad_irq_out(struct urb *urb) -{ - int retval, status; - - status = urb->status; - - switch (status) { - case 0: - /* success */ - return; - - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __func__, status); - return; - - default: - dbg("%s - nonzero urb status received: %d", __func__, status); - goto exit; - } - -exit: - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - err("%s - usb_submit_urb failed with result %d", - __func__, retval); -} - -static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) -{ - struct usb_endpoint_descriptor *ep_irq_out; - int error; - - if (xpad->xtype == XTYPE_UNKNOWN) - return 0; - - xpad->odata = usb_alloc_coherent(xpad->udev, XPAD_PKT_LEN, - GFP_KERNEL, &xpad->odata_dma); - if (!xpad->odata) { - error = -ENOMEM; - goto fail1; - } - - mutex_init(&xpad->odata_mutex); - - xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL); - if (!xpad->irq_out) { - error = -ENOMEM; - goto fail2; - } - - ep_irq_out = &intf->cur_altsetting->endpoint[1].desc; - usb_fill_int_urb(xpad->irq_out, xpad->udev, - usb_sndintpipe(xpad->udev, ep_irq_out->bEndpointAddress), - xpad->odata, XPAD_PKT_LEN, - xpad_irq_out, xpad, ep_irq_out->bInterval); - xpad->irq_out->transfer_dma = xpad->odata_dma; - xpad->irq_out->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - - return 0; - - fail2: usb_free_coherent(xpad->udev, XPAD_PKT_LEN, xpad->odata, xpad->odata_dma); - fail1: return error; -} - -static void xpad_stop_output(struct usb_xpad *xpad) -{ - if (xpad->xtype != XTYPE_UNKNOWN) - usb_kill_urb(xpad->irq_out); -} - -static void xpad_deinit_output(struct usb_xpad *xpad) -{ - if (xpad->xtype != XTYPE_UNKNOWN) { - usb_free_urb(xpad->irq_out); - usb_free_coherent(xpad->udev, XPAD_PKT_LEN, - xpad->odata, xpad->odata_dma); - } -} -#else -static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) { return 0; } -static void xpad_deinit_output(struct usb_xpad *xpad) {} -static void xpad_stop_output(struct usb_xpad *xpad) {} -#endif - -#ifdef CONFIG_JOYSTICK_XPAD_FF -static int xpad_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) -{ - struct usb_xpad *xpad = input_get_drvdata(dev); - - if (effect->type == FF_RUMBLE) { - __u16 strong = effect->u.rumble.strong_magnitude; - __u16 weak = effect->u.rumble.weak_magnitude; - - switch (xpad->xtype) { - - case XTYPE_XBOX: - xpad->odata[0] = 0x00; - xpad->odata[1] = 0x06; - xpad->odata[2] = 0x00; - xpad->odata[3] = strong / 256; /* left actuator */ - xpad->odata[4] = 0x00; - xpad->odata[5] = weak / 256; /* right actuator */ - xpad->irq_out->transfer_buffer_length = 6; - - return usb_submit_urb(xpad->irq_out, GFP_ATOMIC); - - case XTYPE_XBOX360: - xpad->odata[0] = 0x00; - xpad->odata[1] = 0x08; - xpad->odata[2] = 0x00; - xpad->odata[3] = strong / 256; /* left actuator? */ - xpad->odata[4] = weak / 256; /* right actuator? */ - xpad->odata[5] = 0x00; - xpad->odata[6] = 0x00; - xpad->odata[7] = 0x00; - xpad->irq_out->transfer_buffer_length = 8; - - return usb_submit_urb(xpad->irq_out, GFP_ATOMIC); - - case XTYPE_XBOX360W: - xpad->odata[0] = 0x00; - xpad->odata[1] = 0x01; - xpad->odata[2] = 0x0F; - xpad->odata[3] = 0xC0; - xpad->odata[4] = 0x00; - xpad->odata[5] = strong / 256; - xpad->odata[6] = weak / 256; - 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; - - return usb_submit_urb(xpad->irq_out, GFP_ATOMIC); - - default: - dbg("%s - rumble command sent to unsupported xpad type: %d", - __func__, xpad->xtype); - return -1; - } - } - - return 0; -} - -static int xpad_init_ff(struct usb_xpad *xpad) -{ - if (xpad->xtype == XTYPE_UNKNOWN) - return 0; - - input_set_capability(xpad->dev, EV_FF, FF_RUMBLE); - - return input_ff_create_memless(xpad->dev, NULL, xpad_play_effect); -} - -#else -static int xpad_init_ff(struct usb_xpad *xpad) { return 0; } -#endif - -#if defined(CONFIG_JOYSTICK_XPAD_LEDS) -#include <linux/leds.h> - -struct xpad_led { - char name[16]; - struct led_classdev led_cdev; - struct usb_xpad *xpad; -}; - -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); - } -} - -static void xpad_led_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - struct xpad_led *xpad_led = container_of(led_cdev, - struct xpad_led, led_cdev); - - xpad_send_led_command(xpad_led->xpad, value); -} - -static int xpad_led_probe(struct usb_xpad *xpad) -{ - static atomic_t led_seq = ATOMIC_INIT(0); - long led_no; - struct xpad_led *led; - struct led_classdev *led_cdev; - int error; - - if (xpad->xtype != XTYPE_XBOX360) - return 0; - - xpad->led = led = kzalloc(sizeof(struct xpad_led), GFP_KERNEL); - if (!led) - return -ENOMEM; - - led_no = (long)atomic_inc_return(&led_seq) - 1; - - snprintf(led->name, sizeof(led->name), "xpad%ld", led_no); - led->xpad = xpad; - - led_cdev = &led->led_cdev; - led_cdev->name = led->name; - led_cdev->brightness_set = xpad_led_set; - - error = led_classdev_register(&xpad->udev->dev, led_cdev); - if (error) { - kfree(led); - xpad->led = NULL; - return error; - } - - /* - * Light up the segment corresponding to controller number - */ - xpad_send_led_command(xpad, (led_no % 4) + 2); - - return 0; -} - -static void xpad_led_disconnect(struct usb_xpad *xpad) -{ - struct xpad_led *xpad_led = xpad->led; - - if (xpad_led) { - led_classdev_unregister(&xpad_led->led_cdev); - kfree(xpad_led); - } -} -#else -static int xpad_led_probe(struct usb_xpad *xpad) { return 0; } -static void xpad_led_disconnect(struct usb_xpad *xpad) { } -#endif - static int xpad_open(struct input_dev *dev) { @@ -942,43 +1040,9 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id usb_set_intfdata(intf, xpad); - if (xpad->xtype == XTYPE_XBOX360W) { - /* - * Setup the message to set the LEDs on the - * controller when it shows up - */ - xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL); - if (!xpad->bulk_out) { - error = -ENOMEM; - goto fail7; - } - - xpad->bdata = kzalloc(XPAD_PKT_LEN, GFP_KERNEL); - if (!xpad->bdata) { - error = -ENOMEM; - goto fail8; - } - - xpad->bdata[2] = 0x08; - switch (intf->cur_altsetting->desc.bInterfaceNumber) { - case 0: - xpad->bdata[3] = 0x42; - break; - case 2: - xpad->bdata[3] = 0x43; - break; - case 4: - xpad->bdata[3] = 0x44; - break; - case 6: - xpad->bdata[3] = 0x45; - } - - ep_irq_in = &intf->cur_altsetting->endpoint[1].desc; - usb_fill_bulk_urb(xpad->bulk_out, udev, - usb_sndbulkpipe(udev, ep_irq_in->bEndpointAddress), - xpad->bdata, XPAD_PKT_LEN, xpad_bulk_out, xpad); + xpad->interface_number = intf->cur_altsetting->desc.bInterfaceNumber; + if (xpad->xtype == XTYPE_XBOX360W) { /* * Submit the int URB immediately rather than waiting for open * because we get status messages from the device whether @@ -989,13 +1053,11 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id xpad->irq_in->dev = xpad->udev; error = usb_submit_urb(xpad->irq_in, GFP_KERNEL); if (error) - goto fail9; + goto fail7; } return 0; - fail9: kfree(xpad->bdata); - fail8: usb_free_urb(xpad->bulk_out); fail7: input_unregister_device(input_dev); input_dev = NULL; fail6: xpad_led_disconnect(xpad); @@ -1019,8 +1081,6 @@ static void xpad_disconnect(struct usb_interface *intf) xpad_deinit_output(xpad); if (xpad->xtype == XTYPE_XBOX360W) { - usb_kill_urb(xpad->bulk_out); - usb_free_urb(xpad->bulk_out); usb_kill_urb(xpad->irq_in); } @@ -1028,7 +1088,6 @@ static void xpad_disconnect(struct usb_interface *intf) usb_free_coherent(xpad->udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma); - kfree(xpad->bdata); kfree(xpad); usb_set_intfdata(intf, NULL); -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html