diff --git a/xpad.c b/xpad.c index 3aa2f3f..fee2da4 100644 --- a/xpad.c +++ b/xpad.c @@ -78,6 +78,7 @@ #include #include #include +#include #define DRIVER_AUTHOR "Marko Friedemann " #define DRIVER_DESC "X-Box pad driver" @@ -332,7 +333,16 @@ 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; + + int odata_busy; + + spinlock_t pend_lock; + + unsigned pend_rum; + unsigned char rum_data[XPAD_PKT_LEN]; + + unsigned pend_led; + unsigned char led_data[XPAD_PKT_LEN]; #if defined(CONFIG_JOYSTICK_XPAD_LEDS) struct xpad_led *led; @@ -691,12 +701,37 @@ static void xpad_irq_out(struct urb *urb) struct usb_xpad *xpad = urb->context; struct device *dev = &xpad->intf->dev; int retval, status; + unsigned long flags; status = urb->status; switch (status) { case 0: /* success */ + spin_lock_irqsave(&xpad->pend_lock, flags); + xpad->irq_out->transfer_buffer_length = 0; + + if (xpad->pend_rum != 0) { + memcpy(xpad->odata, xpad->rum_data, xpad->pend_rum); + xpad->irq_out->transfer_buffer_length = xpad->pend_rum; + xpad->pend_rum = 0; + } else if (xpad->pend_led != 0) { + memcpy(xpad->odata, xpad->led_data, xpad->pend_led); + xpad->irq_out->transfer_buffer_length = xpad->pend_led; + xpad->pend_led = 0; + } + + if (xpad->irq_out->transfer_buffer_length != 0) { + spin_unlock_irqrestore(&xpad->pend_lock, flags); + if (usb_submit_urb(xpad->irq_out, GFP_ATOMIC) != 0) { + spin_lock_irqsave(&xpad->pend_lock, flags); + xpad->odata_busy = 0; + spin_unlock_irqrestore(&xpad->pend_lock, flags); + } + } else { + xpad->odata_busy = 0; + spin_unlock_irqrestore(&xpad->pend_lock, flags); + } return; case -ECONNRESET: @@ -705,19 +740,27 @@ static void xpad_irq_out(struct urb *urb) /* this urb is terminated, clean up */ dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status); + + spin_lock_irqsave(&xpad->pend_lock, flags); + xpad->odata_busy = 0; + spin_unlock_irqrestore(&xpad->pend_lock, flags); return; default: dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status); - goto exit; - } -exit: - retval = usb_submit_urb(urb, GFP_ATOMIC); - if (retval) - dev_err(dev, "%s - usb_submit_urb failed with result %d\n", - __func__, retval); + retval = usb_submit_urb(urb, GFP_ATOMIC); + if (retval) { + dev_err(dev, "%s - usb_submit_urb failed with result %d\n", + __func__, retval); + spin_lock_irqsave(&xpad->pend_lock, flags); + xpad->odata_busy = 0; + spin_unlock_irqrestore(&xpad->pend_lock, flags); + } + + return; + } } static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) @@ -736,7 +779,12 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad) goto fail1; } - mutex_init(&xpad->odata_mutex); + xpad->odata_busy = 0; + + spin_lock_init(&xpad->pend_lock); + + xpad->pend_rum = 0; + xpad->pend_led = 0; xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL); if (!xpad->irq_out) { @@ -777,61 +825,91 @@ static void xpad_deinit_output(struct usb_xpad *xpad) } #ifdef CONFIG_JOYSTICK_XPAD_FF +static int xpad_make_rum_data(struct usb_xpad *xpad, __u16 strong, __u16 weak) +{ + switch (xpad->xtype) { + + case XTYPE_XBOX: + xpad->rum_data[0] = 0x00; + xpad->rum_data[1] = 0x06; + xpad->rum_data[2] = 0x00; + xpad->rum_data[3] = strong / 256; /* left actuator */ + xpad->rum_data[4] = 0x00; + xpad->rum_data[5] = weak / 256; /* right actuator */ + xpad->pend_rum = 6; + return 0; + + + case XTYPE_XBOX360: + xpad->rum_data[0] = 0x00; + xpad->rum_data[1] = 0x08; + xpad->rum_data[2] = 0x00; + xpad->rum_data[3] = strong / 256; /* left actuator? */ + xpad->rum_data[4] = weak / 256; /* right actuator? */ + xpad->rum_data[5] = 0x00; + xpad->rum_data[6] = 0x00; + xpad->rum_data[7] = 0x00; + xpad->pend_rum = 8; + return 0; + + case XTYPE_XBOX360W: + xpad->rum_data[0] = 0x00; + xpad->rum_data[1] = 0x01; + xpad->rum_data[2] = 0x0F; + xpad->rum_data[3] = 0xC0; + xpad->rum_data[4] = 0x00; + xpad->rum_data[5] = strong / 256; + xpad->rum_data[6] = weak / 256; + xpad->rum_data[7] = 0x00; + xpad->rum_data[8] = 0x00; + xpad->rum_data[9] = 0x00; + xpad->rum_data[10] = 0x00; + xpad->rum_data[11] = 0x00; + xpad->pend_rum = 12; + return 0; + + } + return -1; +} + 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: + unsigned long flags; + int mrdrv; + + spin_lock_irqsave(&xpad->pend_lock, flags); + mrdrv = xpad_make_rum_data(xpad, effect->u.rumble.strong_magnitude, + effect->u.rumble.weak_magnitude); + + if (mrdrv == 0 && !xpad->odata_busy) { + memcpy(xpad->odata, xpad->rum_data, xpad->pend_rum); + xpad->irq_out->transfer_buffer_length = xpad->pend_rum; + xpad->pend_rum = 0; + xpad->odata_busy = 1; + spin_unlock_irqrestore(&xpad->pend_lock, flags); + + if (usb_submit_urb(xpad->irq_out, GFP_ATOMIC) != 0) { + spin_lock_irqsave(&xpad->pend_lock, flags); + xpad->odata_busy = 0; + spin_unlock_irqrestore(&xpad->pend_lock, flags); + } + } else { + spin_unlock_irqrestore(&xpad->pend_lock, flags); + + if (mrdrv == 0) { + dev_dbg(&xpad->dev->dev, + "%s - rumble while urb busy\n", __func__); + } + } + + if (mrdrv != 0) { dev_dbg(&xpad->dev->dev, "%s - rumble command sent to unsupported xpad type: %d\n", __func__, xpad->xtype); + return -1; } } @@ -865,13 +943,30 @@ struct xpad_led { 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); + unsigned long flags; + + spin_lock_irqsave(&xpad->pend_lock, flags); + xpad->led_data[0] = 0x01; + xpad->led_data[1] = 0x03; + xpad->led_data[2] = command; + xpad->pend_led = 3; + + if (!xpad->odata_busy) { + memcpy(xpad->odata, xpad->led_data, xpad->pend_led); + xpad->irq_out->transfer_buffer_length = xpad->pend_led; + xpad->pend_led = 0; + xpad->odata_busy = 1; + spin_unlock_irqrestore(&xpad->pend_lock, flags); + + if (usb_submit_urb(xpad->irq_out, GFP_KERNEL) != 0) { + spin_lock_irqsave(&xpad->pend_lock, flags); + xpad->odata_busy = 0; + spin_unlock_irqrestore(&xpad->pend_lock, flags); + } + } else { + spin_unlock_irqrestore(&xpad->pend_lock, flags); + dev_dbg(&xpad->dev->dev, "%s - led while urb busy\n", __func__); + } } }