File Index
Files and directories
Sign in
FILE

iforce-usb.c

File preview with syntax highlighting.

linux-6.19.14/drivers/input/joystick/iforce/iforce-usb.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2 /*
  3 *  Copyright (c) 2000-2002 Vojtech Pavlik <vojtech@ucw.cz>
  4 *  Copyright (c) 2001-2002, 2007 Johann Deneux <johann.deneux@gmail.com>
  5 *
  6 *  USB/RS232 I-Force joysticks and wheels.
  7 */
  8
  9#include <linux/usb.h>
 10#include "iforce.h"
 11
 12struct iforce_usb {
 13	struct iforce iforce;
 14
 15	struct usb_device *usbdev;
 16	struct usb_interface *intf;
 17	struct urb *irq, *out;
 18
 19	u8 data_in[IFORCE_MAX_LENGTH] ____cacheline_aligned;
 20	u8 data_out[IFORCE_MAX_LENGTH] ____cacheline_aligned;
 21};
 22
 23static void __iforce_usb_xmit(struct iforce *iforce)
 24{
 25	struct iforce_usb *iforce_usb = container_of(iforce, struct iforce_usb,
 26						     iforce);
 27	int n, c;
 28
 29	guard(spinlock_irqsave)(&iforce->xmit_lock);
 30
 31	if (iforce->xmit.head == iforce->xmit.tail) {
 32		iforce_clear_xmit_and_wake(iforce);
 33		return;
 34	}
 35
 36	((char *)iforce_usb->out->transfer_buffer)[0] = iforce->xmit.buf[iforce->xmit.tail];
 37	XMIT_INC(iforce->xmit.tail, 1);
 38	n = iforce->xmit.buf[iforce->xmit.tail];
 39	XMIT_INC(iforce->xmit.tail, 1);
 40
 41	iforce_usb->out->transfer_buffer_length = n + 1;
 42	iforce_usb->out->dev = iforce_usb->usbdev;
 43
 44	/* Copy rest of data then */
 45	c = CIRC_CNT_TO_END(iforce->xmit.head, iforce->xmit.tail, XMIT_SIZE);
 46	if (n < c)
 47		c = n;
 48
 49	memcpy(iforce_usb->out->transfer_buffer + 1,
 50	       &iforce->xmit.buf[iforce->xmit.tail],
 51	       c);
 52	if (n != c) {
 53		memcpy(iforce_usb->out->transfer_buffer + 1 + c,
 54		       &iforce->xmit.buf[0],
 55		       n - c);
 56	}
 57	XMIT_INC(iforce->xmit.tail, n);
 58
 59	n=usb_submit_urb(iforce_usb->out, GFP_ATOMIC);
 60	if (n) {
 61		dev_warn(&iforce_usb->intf->dev,
 62			 "usb_submit_urb failed %d\n", n);
 63		iforce_clear_xmit_and_wake(iforce);
 64	}
 65
 66	/* The IFORCE_XMIT_RUNNING bit is not cleared here. That's intended.
 67	 * As long as the urb completion handler is not called, the transmiting
 68	 * is considered to be running */
 69}
 70
 71static void iforce_usb_xmit(struct iforce *iforce)
 72{
 73	if (!test_and_set_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags))
 74		__iforce_usb_xmit(iforce);
 75}
 76
 77static int iforce_usb_get_id(struct iforce *iforce, u8 id,
 78			     u8 *response_data, size_t *response_len)
 79{
 80	struct iforce_usb *iforce_usb = container_of(iforce, struct iforce_usb,
 81						     iforce);
 82	u8 *buf;
 83	int status;
 84
 85	buf = kmalloc(IFORCE_MAX_LENGTH, GFP_KERNEL);
 86	if (!buf)
 87		return -ENOMEM;
 88
 89	status = usb_control_msg(iforce_usb->usbdev,
 90				 usb_rcvctrlpipe(iforce_usb->usbdev, 0),
 91				 id,
 92				 USB_TYPE_VENDOR | USB_DIR_IN |
 93					USB_RECIP_INTERFACE,
 94				 0, 0, buf, IFORCE_MAX_LENGTH, 1000);
 95	if (status < 0) {
 96		dev_err(&iforce_usb->intf->dev,
 97			"usb_submit_urb failed: %d\n", status);
 98	} else if (buf[0] != id) {
 99		status = -EIO;
100	} else {
101		memcpy(response_data, buf, status);
102		*response_len = status;
103		status = 0;
104	}
105
106	kfree(buf);
107	return status;
108}
109
110static int iforce_usb_start_io(struct iforce *iforce)
111{
112	struct iforce_usb *iforce_usb = container_of(iforce, struct iforce_usb,
113						     iforce);
114
115	if (usb_submit_urb(iforce_usb->irq, GFP_KERNEL))
116		return -EIO;
117
118	return 0;
119}
120
121static void iforce_usb_stop_io(struct iforce *iforce)
122{
123	struct iforce_usb *iforce_usb = container_of(iforce, struct iforce_usb,
124						     iforce);
125
126	usb_kill_urb(iforce_usb->irq);
127	usb_kill_urb(iforce_usb->out);
128}
129
130static const struct iforce_xport_ops iforce_usb_xport_ops = {
131	.xmit		= iforce_usb_xmit,
132	.get_id		= iforce_usb_get_id,
133	.start_io	= iforce_usb_start_io,
134	.stop_io	= iforce_usb_stop_io,
135};
136
137static void iforce_usb_irq(struct urb *urb)
138{
139	struct iforce_usb *iforce_usb = urb->context;
140	struct iforce *iforce = &iforce_usb->iforce;
141	struct device *dev = &iforce_usb->intf->dev;
142	int status;
143
144	switch (urb->status) {
145	case 0:
146		/* success */
147		break;
148	case -ECONNRESET:
149	case -ENOENT:
150	case -ESHUTDOWN:
151		/* this urb is terminated, clean up */
152		dev_dbg(dev, "%s - urb shutting down with status: %d\n",
153			__func__, urb->status);
154		return;
155	default:
156		dev_dbg(dev, "%s - urb has status of: %d\n",
157			__func__, urb->status);
158		goto exit;
159	}
160
161	iforce_process_packet(iforce, iforce_usb->data_in[0],
162			      iforce_usb->data_in + 1, urb->actual_length - 1);
163
164exit:
165	status = usb_submit_urb(urb, GFP_ATOMIC);
166	if (status)
167		dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
168			__func__, status);
169}
170
171static void iforce_usb_out(struct urb *urb)
172{
173	struct iforce_usb *iforce_usb = urb->context;
174	struct iforce *iforce = &iforce_usb->iforce;
175
176	if (urb->status) {
177		dev_dbg(&iforce_usb->intf->dev, "urb->status %d, exiting\n",
178			urb->status);
179		iforce_clear_xmit_and_wake(iforce);
180		return;
181	}
182
183	__iforce_usb_xmit(iforce);
184
185	wake_up_all(&iforce->wait);
186}
187
188static int iforce_usb_probe(struct usb_interface *intf,
189				const struct usb_device_id *id)
190{
191	struct usb_device *dev = interface_to_usbdev(intf);
192	struct usb_host_interface *interface;
193	struct usb_endpoint_descriptor *epirq, *epout;
194	struct iforce_usb *iforce_usb;
195	int err = -ENOMEM;
196
197	interface = intf->cur_altsetting;
198
199	if (interface->desc.bNumEndpoints < 2)
200		return -ENODEV;
201
202	epirq = &interface->endpoint[0].desc;
203	if (!usb_endpoint_is_int_in(epirq))
204		return -ENODEV;
205
206	epout = &interface->endpoint[1].desc;
207	if (!usb_endpoint_is_int_out(epout))
208		return -ENODEV;
209
210	iforce_usb = kzalloc(sizeof(*iforce_usb), GFP_KERNEL);
211	if (!iforce_usb)
212		goto fail;
213
214	iforce_usb->irq = usb_alloc_urb(0, GFP_KERNEL);
215	if (!iforce_usb->irq)
216		goto fail;
217
218	iforce_usb->out = usb_alloc_urb(0, GFP_KERNEL);
219	if (!iforce_usb->out)
220		goto fail;
221
222	iforce_usb->iforce.xport_ops = &iforce_usb_xport_ops;
223
224	iforce_usb->usbdev = dev;
225	iforce_usb->intf = intf;
226
227	usb_fill_int_urb(iforce_usb->irq, dev,
228			 usb_rcvintpipe(dev, epirq->bEndpointAddress),
229			 iforce_usb->data_in, sizeof(iforce_usb->data_in),
230			 iforce_usb_irq, iforce_usb, epirq->bInterval);
231
232	usb_fill_int_urb(iforce_usb->out, dev,
233			 usb_sndintpipe(dev, epout->bEndpointAddress),
234			 iforce_usb->data_out, sizeof(iforce_usb->data_out),
235			 iforce_usb_out, iforce_usb, epout->bInterval);
236
237	err = iforce_init_device(&intf->dev, BUS_USB, &iforce_usb->iforce);
238	if (err)
239		goto fail;
240
241	usb_set_intfdata(intf, iforce_usb);
242	return 0;
243
244fail:
245	if (iforce_usb) {
246		usb_free_urb(iforce_usb->irq);
247		usb_free_urb(iforce_usb->out);
248		kfree(iforce_usb);
249	}
250
251	return err;
252}
253
254static void iforce_usb_disconnect(struct usb_interface *intf)
255{
256	struct iforce_usb *iforce_usb = usb_get_intfdata(intf);
257
258	usb_set_intfdata(intf, NULL);
259
260	input_unregister_device(iforce_usb->iforce.dev);
261
262	usb_free_urb(iforce_usb->irq);
263	usb_free_urb(iforce_usb->out);
264
265	kfree(iforce_usb);
266}
267
268static const struct usb_device_id iforce_usb_ids[] = {
269	{ USB_DEVICE(0x044f, 0xa01c) },		/* Thrustmaster Motor Sport GT */
270	{ USB_DEVICE(0x046d, 0xc281) },		/* Logitech WingMan Force */
271	{ USB_DEVICE(0x046d, 0xc291) },		/* Logitech WingMan Formula Force */
272	{ USB_DEVICE(0x05ef, 0x020a) },		/* AVB Top Shot Pegasus */
273	{ USB_DEVICE(0x05ef, 0x8884) },		/* AVB Mag Turbo Force */
274	{ USB_DEVICE(0x05ef, 0x8888) },		/* AVB Top Shot FFB Racing Wheel */
275	{ USB_DEVICE(0x061c, 0xc0a4) },         /* ACT LABS Force RS */
276	{ USB_DEVICE(0x061c, 0xc084) },         /* ACT LABS Force RS */
277	{ USB_DEVICE(0x06a3, 0xff04) },		/* Saitek R440 Force Wheel */
278	{ USB_DEVICE(0x06f8, 0x0001) },		/* Guillemot Race Leader Force Feedback */
279	{ USB_DEVICE(0x06f8, 0x0003) },		/* Guillemot Jet Leader Force Feedback */
280	{ USB_DEVICE(0x06f8, 0x0004) },		/* Guillemot Force Feedback Racing Wheel */
281	{ USB_DEVICE(0x06f8, 0xa302) },		/* Guillemot Jet Leader 3D */
282	{ }					/* Terminating entry */
283};
284
285MODULE_DEVICE_TABLE (usb, iforce_usb_ids);
286
287struct usb_driver iforce_usb_driver = {
288	.name =		"iforce",
289	.probe =	iforce_usb_probe,
290	.disconnect =	iforce_usb_disconnect,
291	.id_table =	iforce_usb_ids,
292};
293
294module_usb_driver(iforce_usb_driver);
295
296MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>, Johann Deneux <johann.deneux@gmail.com>");
297MODULE_DESCRIPTION("USB I-Force joysticks and wheels driver");
298MODULE_LICENSE("GPL");
~/ Read-only file preview.