]> git.ipfire.org Git - people/ms/linux.git/blobdiff - drivers/input/joystick/iforce/iforce-usb.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[people/ms/linux.git] / drivers / input / joystick / iforce / iforce-usb.c
index f1569ae8381bcb8f5a9e044e4f905c09056367af..ade376bfb79f5b5430db68741c6fd9068a0007c1 100644 (file)
@@ -9,10 +9,24 @@
 /*
  */
 
+#include <linux/usb.h>
 #include "iforce.h"
 
-void iforce_usb_xmit(struct iforce *iforce)
+struct iforce_usb {
+       struct iforce iforce;
+
+       struct usb_device *usbdev;
+       struct usb_interface *intf;
+       struct urb *irq, *out;
+
+       u8 data_in[IFORCE_MAX_LENGTH] ____cacheline_aligned;
+       u8 data_out[IFORCE_MAX_LENGTH] ____cacheline_aligned;
+};
+
+static void __iforce_usb_xmit(struct iforce *iforce)
 {
+       struct iforce_usb *iforce_usb = container_of(iforce, struct iforce_usb,
+                                                    iforce);
        int n, c;
        unsigned long flags;
 
@@ -24,31 +38,32 @@ void iforce_usb_xmit(struct iforce *iforce)
                return;
        }
 
-       ((char *)iforce->out->transfer_buffer)[0] = iforce->xmit.buf[iforce->xmit.tail];
+       ((char *)iforce_usb->out->transfer_buffer)[0] = iforce->xmit.buf[iforce->xmit.tail];
        XMIT_INC(iforce->xmit.tail, 1);
        n = iforce->xmit.buf[iforce->xmit.tail];
        XMIT_INC(iforce->xmit.tail, 1);
 
-       iforce->out->transfer_buffer_length = n + 1;
-       iforce->out->dev = iforce->usbdev;
+       iforce_usb->out->transfer_buffer_length = n + 1;
+       iforce_usb->out->dev = iforce_usb->usbdev;
 
        /* Copy rest of data then */
        c = CIRC_CNT_TO_END(iforce->xmit.head, iforce->xmit.tail, XMIT_SIZE);
        if (n < c) c=n;
 
-       memcpy(iforce->out->transfer_buffer + 1,
+       memcpy(iforce_usb->out->transfer_buffer + 1,
               &iforce->xmit.buf[iforce->xmit.tail],
               c);
        if (n != c) {
-               memcpy(iforce->out->transfer_buffer + 1 + c,
+               memcpy(iforce_usb->out->transfer_buffer + 1 + c,
                       &iforce->xmit.buf[0],
                       n-c);
        }
        XMIT_INC(iforce->xmit.tail, n);
 
-       if ( (n=usb_submit_urb(iforce->out, GFP_ATOMIC)) ) {
+       if ( (n=usb_submit_urb(iforce_usb->out, GFP_ATOMIC)) ) {
                clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
-               dev_warn(&iforce->intf->dev, "usb_submit_urb failed %d\n", n);
+               dev_warn(&iforce_usb->intf->dev,
+                        "usb_submit_urb failed %d\n", n);
        }
 
        /* The IFORCE_XMIT_RUNNING bit is not cleared here. That's intended.
@@ -57,10 +72,77 @@ void iforce_usb_xmit(struct iforce *iforce)
        spin_unlock_irqrestore(&iforce->xmit_lock, flags);
 }
 
+static void iforce_usb_xmit(struct iforce *iforce)
+{
+       if (!test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags))
+               __iforce_usb_xmit(iforce);
+}
+
+static int iforce_usb_get_id(struct iforce *iforce, u8 id,
+                            u8 *response_data, size_t *response_len)
+{
+       struct iforce_usb *iforce_usb = container_of(iforce, struct iforce_usb,
+                                                    iforce);
+       u8 *buf;
+       int status;
+
+       buf = kmalloc(IFORCE_MAX_LENGTH, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       status = usb_control_msg(iforce_usb->usbdev,
+                                usb_rcvctrlpipe(iforce_usb->usbdev, 0),
+                                id,
+                                USB_TYPE_VENDOR | USB_DIR_IN |
+                                       USB_RECIP_INTERFACE,
+                                0, 0, buf, IFORCE_MAX_LENGTH, HZ);
+       if (status < 0) {
+               dev_err(&iforce_usb->intf->dev,
+                       "usb_submit_urb failed: %d\n", status);
+       } else if (buf[0] != id) {
+               status = -EIO;
+       } else {
+               memcpy(response_data, buf, status);
+               *response_len = status;
+               status = 0;
+       }
+
+       kfree(buf);
+       return status;
+}
+
+static int iforce_usb_start_io(struct iforce *iforce)
+{
+       struct iforce_usb *iforce_usb = container_of(iforce, struct iforce_usb,
+                                                    iforce);
+
+       if (usb_submit_urb(iforce_usb->irq, GFP_KERNEL))
+               return -EIO;
+
+       return 0;
+}
+
+static void iforce_usb_stop_io(struct iforce *iforce)
+{
+       struct iforce_usb *iforce_usb = container_of(iforce, struct iforce_usb,
+                                                    iforce);
+
+       usb_kill_urb(iforce_usb->irq);
+       usb_kill_urb(iforce_usb->out);
+}
+
+static const struct iforce_xport_ops iforce_usb_xport_ops = {
+       .xmit           = iforce_usb_xmit,
+       .get_id         = iforce_usb_get_id,
+       .start_io       = iforce_usb_start_io,
+       .stop_io        = iforce_usb_stop_io,
+};
+
 static void iforce_usb_irq(struct urb *urb)
 {
-       struct iforce *iforce = urb->context;
-       struct device *dev = &iforce->intf->dev;
+       struct iforce_usb *iforce_usb = urb->context;
+       struct iforce *iforce = &iforce_usb->iforce;
+       struct device *dev = &iforce_usb->intf->dev;
        int status;
 
        switch (urb->status) {
@@ -80,11 +162,11 @@ static void iforce_usb_irq(struct urb *urb)
                goto exit;
        }
 
-       iforce_process_packet(iforce,
-               (iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1);
+       iforce_process_packet(iforce, iforce_usb->data_in[0],
+                             iforce_usb->data_in + 1, urb->actual_length - 1);
 
 exit:
-       status = usb_submit_urb (urb, GFP_ATOMIC);
+       status = usb_submit_urb(urb, GFP_ATOMIC);
        if (status)
                dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
                        __func__, status);
@@ -92,35 +174,28 @@ exit:
 
 static void iforce_usb_out(struct urb *urb)
 {
-       struct iforce *iforce = urb->context;
+       struct iforce_usb *iforce_usb = urb->context;
+       struct iforce *iforce = &iforce_usb->iforce;
 
        if (urb->status) {
                clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
-               dev_dbg(&iforce->intf->dev, "urb->status %d, exiting\n",
+               dev_dbg(&iforce_usb->intf->dev, "urb->status %d, exiting\n",
                        urb->status);
                return;
        }
 
-       iforce_usb_xmit(iforce);
+       __iforce_usb_xmit(iforce);
 
        wake_up(&iforce->wait);
 }
 
-static void iforce_usb_ctrl(struct urb *urb)
-{
-       struct iforce *iforce = urb->context;
-       if (urb->status) return;
-       iforce->ecmd = 0xff00 | urb->actual_length;
-       wake_up(&iforce->wait);
-}
-
 static int iforce_usb_probe(struct usb_interface *intf,
                                const struct usb_device_id *id)
 {
        struct usb_device *dev = interface_to_usbdev(intf);
        struct usb_host_interface *interface;
        struct usb_endpoint_descriptor *epirq, *epout;
-       struct iforce *iforce;
+       struct iforce_usb *iforce_usb;
        int err = -ENOMEM;
 
        interface = intf->cur_altsetting;
@@ -131,48 +206,45 @@ static int iforce_usb_probe(struct usb_interface *intf,
        epirq = &interface->endpoint[0].desc;
        epout = &interface->endpoint[1].desc;
 
-       if (!(iforce = kzalloc(sizeof(struct iforce) + 32, GFP_KERNEL)))
-               goto fail;
-
-       if (!(iforce->irq = usb_alloc_urb(0, GFP_KERNEL)))
+       iforce_usb = kzalloc(sizeof(*iforce_usb), GFP_KERNEL);
+       if (!iforce_usb)
                goto fail;
 
-       if (!(iforce->out = usb_alloc_urb(0, GFP_KERNEL)))
+       iforce_usb->irq = usb_alloc_urb(0, GFP_KERNEL);
+       if (!iforce_usb->irq)
                goto fail;
 
-       if (!(iforce->ctrl = usb_alloc_urb(0, GFP_KERNEL)))
+       iforce_usb->out = usb_alloc_urb(0, GFP_KERNEL);
+       if (!iforce_usb->out)
                goto fail;
 
-       iforce->bus = IFORCE_USB;
-       iforce->usbdev = dev;
-       iforce->intf = intf;
-
-       iforce->cr.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE;
-       iforce->cr.wIndex = 0;
-       iforce->cr.wLength = cpu_to_le16(16);
+       iforce_usb->iforce.xport_ops = &iforce_usb_xport_ops;
 
-       usb_fill_int_urb(iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress),
-                       iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval);
+       iforce_usb->usbdev = dev;
+       iforce_usb->intf = intf;
 
-       usb_fill_int_urb(iforce->out, dev, usb_sndintpipe(dev, epout->bEndpointAddress),
-                       iforce + 1, 32, iforce_usb_out, iforce, epout->bInterval);
+       usb_fill_int_urb(iforce_usb->irq, dev,
+                        usb_rcvintpipe(dev, epirq->bEndpointAddress),
+                        iforce_usb->data_in, sizeof(iforce_usb->data_in),
+                        iforce_usb_irq, iforce_usb, epirq->bInterval);
 
-       usb_fill_control_urb(iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0),
-                       (void*) &iforce->cr, iforce->edata, 16, iforce_usb_ctrl, iforce);
+       usb_fill_int_urb(iforce_usb->out, dev,
+                        usb_sndintpipe(dev, epout->bEndpointAddress),
+                        iforce_usb->data_out, sizeof(iforce_usb->data_out),
+                        iforce_usb_out, iforce_usb, epout->bInterval);
 
-       err = iforce_init_device(iforce);
+       err = iforce_init_device(&intf->dev, BUS_USB, &iforce_usb->iforce);
        if (err)
                goto fail;
 
-       usb_set_intfdata(intf, iforce);
+       usb_set_intfdata(intf, iforce_usb);
        return 0;
 
 fail:
-       if (iforce) {
-               usb_free_urb(iforce->irq);
-               usb_free_urb(iforce->out);
-               usb_free_urb(iforce->ctrl);
-               kfree(iforce);
+       if (iforce_usb) {
+               usb_free_urb(iforce_usb->irq);
+               usb_free_urb(iforce_usb->out);
+               kfree(iforce_usb);
        }
 
        return err;
@@ -180,17 +252,16 @@ fail:
 
 static void iforce_usb_disconnect(struct usb_interface *intf)
 {
-       struct iforce *iforce = usb_get_intfdata(intf);
+       struct iforce_usb *iforce_usb = usb_get_intfdata(intf);
 
        usb_set_intfdata(intf, NULL);
 
-       input_unregister_device(iforce->dev);
+       input_unregister_device(iforce_usb->iforce.dev);
 
-       usb_free_urb(iforce->irq);
-       usb_free_urb(iforce->out);
-       usb_free_urb(iforce->ctrl);
+       usb_free_urb(iforce_usb->irq);
+       usb_free_urb(iforce_usb->out);
 
-       kfree(iforce);
+       kfree(iforce_usb);
 }
 
 static const struct usb_device_id iforce_usb_ids[] = {
@@ -202,6 +273,7 @@ static const struct usb_device_id iforce_usb_ids[] = {
        { USB_DEVICE(0x05ef, 0x8888) },         /* AVB Top Shot FFB Racing Wheel */
        { USB_DEVICE(0x061c, 0xc0a4) },         /* ACT LABS Force RS */
        { USB_DEVICE(0x061c, 0xc084) },         /* ACT LABS Force RS */
+       { USB_DEVICE(0x06a3, 0xff04) },         /* Saitek R440 Force Wheel */
        { USB_DEVICE(0x06f8, 0x0001) },         /* Guillemot Race Leader Force Feedback */
        { USB_DEVICE(0x06f8, 0x0003) },         /* Guillemot Jet Leader Force Feedback */
        { USB_DEVICE(0x06f8, 0x0004) },         /* Guillemot Force Feedback Racing Wheel */
@@ -217,3 +289,9 @@ struct usb_driver iforce_usb_driver = {
        .disconnect =   iforce_usb_disconnect,
        .id_table =     iforce_usb_ids,
 };
+
+module_usb_driver(iforce_usb_driver);
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.com>");
+MODULE_DESCRIPTION("USB I-Force joysticks and wheels driver");
+MODULE_LICENSE("GPL");