|
@@ -82,6 +82,7 @@
|
|
|
#include <linux/stat.h>
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/usb/input.h>
|
|
|
+#include <linux/usb/quirks.h>
|
|
|
|
|
|
#define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>"
|
|
|
#define DRIVER_DESC "X-Box pad driver"
|
|
@@ -346,9 +347,10 @@ struct usb_xpad {
|
|
|
dma_addr_t idata_dma;
|
|
|
|
|
|
struct urb *irq_out; /* urb for interrupt out report */
|
|
|
+ struct usb_anchor irq_out_anchor;
|
|
|
bool irq_out_active; /* we must not use an active URB */
|
|
|
- unsigned char *odata; /* output data */
|
|
|
u8 odata_serial; /* serial number for xbox one protocol */
|
|
|
+ unsigned char *odata; /* output data */
|
|
|
dma_addr_t odata_dma;
|
|
|
spinlock_t odata_lock;
|
|
|
|
|
@@ -764,11 +766,13 @@ static int xpad_try_sending_next_out_packet(struct usb_xpad *xpad)
|
|
|
int error;
|
|
|
|
|
|
if (!xpad->irq_out_active && xpad_prepare_next_out_packet(xpad)) {
|
|
|
+ usb_anchor_urb(xpad->irq_out, &xpad->irq_out_anchor);
|
|
|
error = usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
|
|
|
if (error) {
|
|
|
dev_err(&xpad->intf->dev,
|
|
|
"%s - usb_submit_urb failed with result %d\n",
|
|
|
__func__, error);
|
|
|
+ usb_unanchor_urb(xpad->irq_out);
|
|
|
return -EIO;
|
|
|
}
|
|
|
|
|
@@ -811,11 +815,13 @@ static void xpad_irq_out(struct urb *urb)
|
|
|
}
|
|
|
|
|
|
if (xpad->irq_out_active) {
|
|
|
+ usb_anchor_urb(urb, &xpad->irq_out_anchor);
|
|
|
error = usb_submit_urb(urb, GFP_ATOMIC);
|
|
|
if (error) {
|
|
|
dev_err(dev,
|
|
|
"%s - usb_submit_urb failed with result %d\n",
|
|
|
__func__, error);
|
|
|
+ usb_unanchor_urb(urb);
|
|
|
xpad->irq_out_active = false;
|
|
|
}
|
|
|
}
|
|
@@ -832,6 +838,8 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
|
|
|
if (xpad->xtype == XTYPE_UNKNOWN)
|
|
|
return 0;
|
|
|
|
|
|
+ init_usb_anchor(&xpad->irq_out_anchor);
|
|
|
+
|
|
|
xpad->odata = usb_alloc_coherent(xpad->udev, XPAD_PKT_LEN,
|
|
|
GFP_KERNEL, &xpad->odata_dma);
|
|
|
if (!xpad->odata) {
|
|
@@ -866,8 +874,14 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
|
|
|
|
|
|
static void xpad_stop_output(struct usb_xpad *xpad)
|
|
|
{
|
|
|
- if (xpad->xtype != XTYPE_UNKNOWN)
|
|
|
- usb_kill_urb(xpad->irq_out);
|
|
|
+ if (xpad->xtype != XTYPE_UNKNOWN) {
|
|
|
+ if (!usb_wait_anchor_empty_timeout(&xpad->irq_out_anchor,
|
|
|
+ 5000)) {
|
|
|
+ dev_warn(&xpad->intf->dev,
|
|
|
+ "timed out waiting for output URB to complete, killing\n");
|
|
|
+ usb_kill_anchored_urbs(&xpad->irq_out_anchor);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static void xpad_deinit_output(struct usb_xpad *xpad)
|
|
@@ -1196,32 +1210,73 @@ static void xpad_led_disconnect(struct usb_xpad *xpad) { }
|
|
|
static void xpad_identify_controller(struct usb_xpad *xpad) { }
|
|
|
#endif
|
|
|
|
|
|
-static int xpad_open(struct input_dev *dev)
|
|
|
+static int xpad_start_input(struct usb_xpad *xpad)
|
|
|
{
|
|
|
- struct usb_xpad *xpad = input_get_drvdata(dev);
|
|
|
-
|
|
|
- /* URB was submitted in probe */
|
|
|
- if (xpad->xtype == XTYPE_XBOX360W)
|
|
|
- return 0;
|
|
|
+ int error;
|
|
|
|
|
|
- xpad->irq_in->dev = xpad->udev;
|
|
|
if (usb_submit_urb(xpad->irq_in, GFP_KERNEL))
|
|
|
return -EIO;
|
|
|
|
|
|
- if (xpad->xtype == XTYPE_XBOXONE)
|
|
|
- return xpad_start_xbox_one(xpad);
|
|
|
+ if (xpad->xtype == XTYPE_XBOXONE) {
|
|
|
+ error = xpad_start_xbox_one(xpad);
|
|
|
+ if (error) {
|
|
|
+ usb_kill_urb(xpad->irq_in);
|
|
|
+ return error;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static void xpad_close(struct input_dev *dev)
|
|
|
+static void xpad_stop_input(struct usb_xpad *xpad)
|
|
|
{
|
|
|
- struct usb_xpad *xpad = input_get_drvdata(dev);
|
|
|
+ usb_kill_urb(xpad->irq_in);
|
|
|
+}
|
|
|
+
|
|
|
+static int xpad360w_start_input(struct usb_xpad *xpad)
|
|
|
+{
|
|
|
+ int error;
|
|
|
+
|
|
|
+ error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
|
|
|
+ if (error)
|
|
|
+ return -EIO;
|
|
|
|
|
|
- if (xpad->xtype != XTYPE_XBOX360W)
|
|
|
+ /*
|
|
|
+ * Send presence packet.
|
|
|
+ * This will force the controller to resend connection packets.
|
|
|
+ * This is useful in the case we activate the module after the
|
|
|
+ * adapter has been plugged in, as it won't automatically
|
|
|
+ * send us info about the controllers.
|
|
|
+ */
|
|
|
+ error = xpad_inquiry_pad_presence(xpad);
|
|
|
+ if (error) {
|
|
|
usb_kill_urb(xpad->irq_in);
|
|
|
+ return error;
|
|
|
+ }
|
|
|
|
|
|
- xpad_stop_output(xpad);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void xpad360w_stop_input(struct usb_xpad *xpad)
|
|
|
+{
|
|
|
+ usb_kill_urb(xpad->irq_in);
|
|
|
+
|
|
|
+ /* Make sure we are done with presence work if it was scheduled */
|
|
|
+ flush_work(&xpad->work);
|
|
|
+}
|
|
|
+
|
|
|
+static int xpad_open(struct input_dev *dev)
|
|
|
+{
|
|
|
+ struct usb_xpad *xpad = input_get_drvdata(dev);
|
|
|
+
|
|
|
+ return xpad_start_input(xpad);
|
|
|
+}
|
|
|
+
|
|
|
+static void xpad_close(struct input_dev *dev)
|
|
|
+{
|
|
|
+ struct usb_xpad *xpad = input_get_drvdata(dev);
|
|
|
+
|
|
|
+ xpad_stop_input(xpad);
|
|
|
}
|
|
|
|
|
|
static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
|
|
@@ -1276,8 +1331,10 @@ static int xpad_init_input(struct usb_xpad *xpad)
|
|
|
|
|
|
input_set_drvdata(input_dev, xpad);
|
|
|
|
|
|
- input_dev->open = xpad_open;
|
|
|
- input_dev->close = xpad_close;
|
|
|
+ if (xpad->xtype != XTYPE_XBOX360W) {
|
|
|
+ input_dev->open = xpad_open;
|
|
|
+ input_dev->close = xpad_close;
|
|
|
+ }
|
|
|
|
|
|
__set_bit(EV_KEY, input_dev->evbit);
|
|
|
|
|
@@ -1445,21 +1502,17 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
|
|
* exactly the message that a controller has arrived that
|
|
|
* we're waiting for.
|
|
|
*/
|
|
|
- xpad->irq_in->dev = xpad->udev;
|
|
|
- error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
|
|
|
+ error = xpad360w_start_input(xpad);
|
|
|
if (error)
|
|
|
goto err_deinit_output;
|
|
|
-
|
|
|
/*
|
|
|
- * Send presence packet.
|
|
|
- * This will force the controller to resend connection packets.
|
|
|
- * This is useful in the case we activate the module after the
|
|
|
- * adapter has been plugged in, as it won't automatically
|
|
|
- * send us info about the controllers.
|
|
|
+ * Wireless controllers require RESET_RESUME to work properly
|
|
|
+ * after suspend. Ideally this quirk should be in usb core
|
|
|
+ * quirk list, but we have too many vendors producing these
|
|
|
+ * controllers and we'd need to maintain 2 identical lists
|
|
|
+ * here in this driver and in usb core.
|
|
|
*/
|
|
|
- error = xpad_inquiry_pad_presence(xpad);
|
|
|
- if (error)
|
|
|
- goto err_kill_in_urb;
|
|
|
+ udev->quirks |= USB_QUIRK_RESET_RESUME;
|
|
|
} else {
|
|
|
error = xpad_init_input(xpad);
|
|
|
if (error)
|
|
@@ -1467,8 +1520,6 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
|
|
}
|
|
|
return 0;
|
|
|
|
|
|
-err_kill_in_urb:
|
|
|
- usb_kill_urb(xpad->irq_in);
|
|
|
err_deinit_output:
|
|
|
xpad_deinit_output(xpad);
|
|
|
err_free_in_urb:
|
|
@@ -1478,35 +1529,83 @@ err_free_idata:
|
|
|
err_free_mem:
|
|
|
kfree(xpad);
|
|
|
return error;
|
|
|
-
|
|
|
}
|
|
|
|
|
|
static void xpad_disconnect(struct usb_interface *intf)
|
|
|
{
|
|
|
- struct usb_xpad *xpad = usb_get_intfdata (intf);
|
|
|
+ struct usb_xpad *xpad = usb_get_intfdata(intf);
|
|
|
|
|
|
if (xpad->xtype == XTYPE_XBOX360W)
|
|
|
- usb_kill_urb(xpad->irq_in);
|
|
|
-
|
|
|
- cancel_work_sync(&xpad->work);
|
|
|
+ xpad360w_stop_input(xpad);
|
|
|
|
|
|
xpad_deinit_input(xpad);
|
|
|
|
|
|
+ /*
|
|
|
+ * Now that both input device and LED device are gone we can
|
|
|
+ * stop output URB.
|
|
|
+ */
|
|
|
+ xpad_stop_output(xpad);
|
|
|
+
|
|
|
+ xpad_deinit_output(xpad);
|
|
|
+
|
|
|
usb_free_urb(xpad->irq_in);
|
|
|
usb_free_coherent(xpad->udev, XPAD_PKT_LEN,
|
|
|
xpad->idata, xpad->idata_dma);
|
|
|
|
|
|
- xpad_deinit_output(xpad);
|
|
|
-
|
|
|
kfree(xpad);
|
|
|
|
|
|
usb_set_intfdata(intf, NULL);
|
|
|
}
|
|
|
|
|
|
+static int xpad_suspend(struct usb_interface *intf, pm_message_t message)
|
|
|
+{
|
|
|
+ struct usb_xpad *xpad = usb_get_intfdata(intf);
|
|
|
+ struct input_dev *input = xpad->dev;
|
|
|
+
|
|
|
+ if (xpad->xtype == XTYPE_XBOX360W) {
|
|
|
+ /*
|
|
|
+ * Wireless controllers always listen to input so
|
|
|
+ * they are notified when controller shows up
|
|
|
+ * or goes away.
|
|
|
+ */
|
|
|
+ xpad360w_stop_input(xpad);
|
|
|
+ } else {
|
|
|
+ mutex_lock(&input->mutex);
|
|
|
+ if (input->users)
|
|
|
+ xpad_stop_input(xpad);
|
|
|
+ mutex_unlock(&input->mutex);
|
|
|
+ }
|
|
|
+
|
|
|
+ xpad_stop_output(xpad);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int xpad_resume(struct usb_interface *intf)
|
|
|
+{
|
|
|
+ struct usb_xpad *xpad = usb_get_intfdata(intf);
|
|
|
+ struct input_dev *input = xpad->dev;
|
|
|
+ int retval = 0;
|
|
|
+
|
|
|
+ if (xpad->xtype == XTYPE_XBOX360W) {
|
|
|
+ retval = xpad360w_start_input(xpad);
|
|
|
+ } else {
|
|
|
+ mutex_lock(&input->mutex);
|
|
|
+ if (input->users)
|
|
|
+ retval = xpad_start_input(xpad);
|
|
|
+ mutex_unlock(&input->mutex);
|
|
|
+ }
|
|
|
+
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
static struct usb_driver xpad_driver = {
|
|
|
.name = "xpad",
|
|
|
.probe = xpad_probe,
|
|
|
.disconnect = xpad_disconnect,
|
|
|
+ .suspend = xpad_suspend,
|
|
|
+ .resume = xpad_resume,
|
|
|
+ .reset_resume = xpad_resume,
|
|
|
.id_table = xpad_table,
|
|
|
};
|
|
|
|