Path: blob/master/drivers/input/joystick/iforce/iforce-usb.c
15115 views
/*1* Copyright (c) 2000-2002 Vojtech Pavlik <[email protected]>2* Copyright (c) 2001-2002, 2007 Johann Deneux <[email protected]>3*4* USB/RS232 I-Force joysticks and wheels.5*/67/*8* This program is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License as published by10* the Free Software Foundation; either version 2 of the License, or11* (at your option) any later version.12*13* This program is distributed in the hope that it will be useful,14* but WITHOUT ANY WARRANTY; without even the implied warranty of15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16* GNU General Public License for more details.17*18* You should have received a copy of the GNU General Public License19* along with this program; if not, write to the Free Software20* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA21*22* Should you need to contact me, the author, you can do so either by23* e-mail - mail your message to <[email protected]>, or by paper mail:24* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic25*/2627#include "iforce.h"2829void iforce_usb_xmit(struct iforce *iforce)30{31int n, c;32unsigned long flags;3334spin_lock_irqsave(&iforce->xmit_lock, flags);3536if (iforce->xmit.head == iforce->xmit.tail) {37clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);38spin_unlock_irqrestore(&iforce->xmit_lock, flags);39return;40}4142((char *)iforce->out->transfer_buffer)[0] = iforce->xmit.buf[iforce->xmit.tail];43XMIT_INC(iforce->xmit.tail, 1);44n = iforce->xmit.buf[iforce->xmit.tail];45XMIT_INC(iforce->xmit.tail, 1);4647iforce->out->transfer_buffer_length = n + 1;48iforce->out->dev = iforce->usbdev;4950/* Copy rest of data then */51c = CIRC_CNT_TO_END(iforce->xmit.head, iforce->xmit.tail, XMIT_SIZE);52if (n < c) c=n;5354memcpy(iforce->out->transfer_buffer + 1,55&iforce->xmit.buf[iforce->xmit.tail],56c);57if (n != c) {58memcpy(iforce->out->transfer_buffer + 1 + c,59&iforce->xmit.buf[0],60n-c);61}62XMIT_INC(iforce->xmit.tail, n);6364if ( (n=usb_submit_urb(iforce->out, GFP_ATOMIC)) ) {65clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);66dev_warn(&iforce->dev->dev, "usb_submit_urb failed %d\n", n);67}6869/* The IFORCE_XMIT_RUNNING bit is not cleared here. That's intended.70* As long as the urb completion handler is not called, the transmiting71* is considered to be running */72spin_unlock_irqrestore(&iforce->xmit_lock, flags);73}7475static void iforce_usb_irq(struct urb *urb)76{77struct iforce *iforce = urb->context;78int status;7980switch (urb->status) {81case 0:82/* success */83break;84case -ECONNRESET:85case -ENOENT:86case -ESHUTDOWN:87/* this urb is terminated, clean up */88dbg("%s - urb shutting down with status: %d",89__func__, urb->status);90return;91default:92dbg("%s - urb has status of: %d", __func__, urb->status);93goto exit;94}9596iforce_process_packet(iforce,97(iforce->data[0] << 8) | (urb->actual_length - 1), iforce->data + 1);9899exit:100status = usb_submit_urb (urb, GFP_ATOMIC);101if (status)102err ("%s - usb_submit_urb failed with result %d",103__func__, status);104}105106static void iforce_usb_out(struct urb *urb)107{108struct iforce *iforce = urb->context;109110if (urb->status) {111clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);112dbg("urb->status %d, exiting", urb->status);113return;114}115116iforce_usb_xmit(iforce);117118wake_up(&iforce->wait);119}120121static void iforce_usb_ctrl(struct urb *urb)122{123struct iforce *iforce = urb->context;124if (urb->status) return;125iforce->ecmd = 0xff00 | urb->actual_length;126wake_up(&iforce->wait);127}128129static int iforce_usb_probe(struct usb_interface *intf,130const struct usb_device_id *id)131{132struct usb_device *dev = interface_to_usbdev(intf);133struct usb_host_interface *interface;134struct usb_endpoint_descriptor *epirq, *epout;135struct iforce *iforce;136int err = -ENOMEM;137138interface = intf->cur_altsetting;139140epirq = &interface->endpoint[0].desc;141epout = &interface->endpoint[1].desc;142143if (!(iforce = kzalloc(sizeof(struct iforce) + 32, GFP_KERNEL)))144goto fail;145146if (!(iforce->irq = usb_alloc_urb(0, GFP_KERNEL)))147goto fail;148149if (!(iforce->out = usb_alloc_urb(0, GFP_KERNEL)))150goto fail;151152if (!(iforce->ctrl = usb_alloc_urb(0, GFP_KERNEL)))153goto fail;154155iforce->bus = IFORCE_USB;156iforce->usbdev = dev;157158iforce->cr.bRequestType = USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_INTERFACE;159iforce->cr.wIndex = 0;160iforce->cr.wLength = cpu_to_le16(16);161162usb_fill_int_urb(iforce->irq, dev, usb_rcvintpipe(dev, epirq->bEndpointAddress),163iforce->data, 16, iforce_usb_irq, iforce, epirq->bInterval);164165usb_fill_int_urb(iforce->out, dev, usb_sndintpipe(dev, epout->bEndpointAddress),166iforce + 1, 32, iforce_usb_out, iforce, epout->bInterval);167168usb_fill_control_urb(iforce->ctrl, dev, usb_rcvctrlpipe(dev, 0),169(void*) &iforce->cr, iforce->edata, 16, iforce_usb_ctrl, iforce);170171err = iforce_init_device(iforce);172if (err)173goto fail;174175usb_set_intfdata(intf, iforce);176return 0;177178fail:179if (iforce) {180usb_free_urb(iforce->irq);181usb_free_urb(iforce->out);182usb_free_urb(iforce->ctrl);183kfree(iforce);184}185186return err;187}188189static void iforce_usb_disconnect(struct usb_interface *intf)190{191struct iforce *iforce = usb_get_intfdata(intf);192193usb_set_intfdata(intf, NULL);194195input_unregister_device(iforce->dev);196197usb_free_urb(iforce->irq);198usb_free_urb(iforce->out);199usb_free_urb(iforce->ctrl);200201kfree(iforce);202}203204static struct usb_device_id iforce_usb_ids [] = {205{ USB_DEVICE(0x044f, 0xa01c) }, /* Thrustmaster Motor Sport GT */206{ USB_DEVICE(0x046d, 0xc281) }, /* Logitech WingMan Force */207{ USB_DEVICE(0x046d, 0xc291) }, /* Logitech WingMan Formula Force */208{ USB_DEVICE(0x05ef, 0x020a) }, /* AVB Top Shot Pegasus */209{ USB_DEVICE(0x05ef, 0x8884) }, /* AVB Mag Turbo Force */210{ USB_DEVICE(0x05ef, 0x8888) }, /* AVB Top Shot FFB Racing Wheel */211{ USB_DEVICE(0x061c, 0xc0a4) }, /* ACT LABS Force RS */212{ USB_DEVICE(0x061c, 0xc084) }, /* ACT LABS Force RS */213{ USB_DEVICE(0x06f8, 0x0001) }, /* Guillemot Race Leader Force Feedback */214{ USB_DEVICE(0x06f8, 0x0003) }, /* Guillemot Jet Leader Force Feedback */215{ USB_DEVICE(0x06f8, 0x0004) }, /* Guillemot Force Feedback Racing Wheel */216{ USB_DEVICE(0x06f8, 0xa302) }, /* Guillemot Jet Leader 3D */217{ } /* Terminating entry */218};219220MODULE_DEVICE_TABLE (usb, iforce_usb_ids);221222struct usb_driver iforce_usb_driver = {223.name = "iforce",224.probe = iforce_usb_probe,225.disconnect = iforce_usb_disconnect,226.id_table = iforce_usb_ids,227};228229230