xref: /openbmc/linux/drivers/input/joystick/pxrc.c (revision fca3aa16)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Driver for Phoenix RC Flight Controller Adapter
4  *
5  * Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
6  *
7  */
8 
9 #include <linux/kernel.h>
10 #include <linux/errno.h>
11 #include <linux/slab.h>
12 #include <linux/module.h>
13 #include <linux/uaccess.h>
14 #include <linux/usb.h>
15 #include <linux/usb/input.h>
16 #include <linux/mutex.h>
17 #include <linux/input.h>
18 
19 #define PXRC_VENDOR_ID	(0x1781)
20 #define PXRC_PRODUCT_ID	(0x0898)
21 
22 static const struct usb_device_id pxrc_table[] = {
23 	{ USB_DEVICE(PXRC_VENDOR_ID, PXRC_PRODUCT_ID) },
24 	{ }
25 };
26 MODULE_DEVICE_TABLE(usb, pxrc_table);
27 
28 struct pxrc {
29 	struct input_dev	*input;
30 	struct usb_device	*udev;
31 	struct usb_interface	*intf;
32 	struct urb		*urb;
33 	struct mutex		pm_mutex;
34 	bool			is_open;
35 	__u8			epaddr;
36 	char			phys[64];
37 	unsigned char           *data;
38 	size_t			bsize;
39 };
40 
41 static void pxrc_usb_irq(struct urb *urb)
42 {
43 	struct pxrc *pxrc = urb->context;
44 	int error;
45 
46 	switch (urb->status) {
47 	case 0:
48 		/* success */
49 		break;
50 	case -ETIME:
51 		/* this urb is timing out */
52 		dev_dbg(&pxrc->intf->dev,
53 			"%s - urb timed out - was the device unplugged?\n",
54 			__func__);
55 		return;
56 	case -ECONNRESET:
57 	case -ENOENT:
58 	case -ESHUTDOWN:
59 	case -EPIPE:
60 		/* this urb is terminated, clean up */
61 		dev_dbg(&pxrc->intf->dev, "%s - urb shutting down with status: %d\n",
62 			__func__, urb->status);
63 		return;
64 	default:
65 		dev_dbg(&pxrc->intf->dev, "%s - nonzero urb status received: %d\n",
66 			__func__, urb->status);
67 		goto exit;
68 	}
69 
70 	if (urb->actual_length == 8) {
71 		input_report_abs(pxrc->input, ABS_X, pxrc->data[0]);
72 		input_report_abs(pxrc->input, ABS_Y, pxrc->data[2]);
73 		input_report_abs(pxrc->input, ABS_RX, pxrc->data[3]);
74 		input_report_abs(pxrc->input, ABS_RY, pxrc->data[4]);
75 		input_report_abs(pxrc->input, ABS_RUDDER, pxrc->data[5]);
76 		input_report_abs(pxrc->input, ABS_THROTTLE, pxrc->data[6]);
77 		input_report_abs(pxrc->input, ABS_MISC, pxrc->data[7]);
78 
79 		input_report_key(pxrc->input, BTN_A, pxrc->data[1]);
80 	}
81 
82 exit:
83 	/* Resubmit to fetch new fresh URBs */
84 	error = usb_submit_urb(urb, GFP_ATOMIC);
85 	if (error && error != -EPERM)
86 		dev_err(&pxrc->intf->dev,
87 			"%s - usb_submit_urb failed with result: %d",
88 			__func__, error);
89 }
90 
91 static int pxrc_open(struct input_dev *input)
92 {
93 	struct pxrc *pxrc = input_get_drvdata(input);
94 	int retval;
95 
96 	mutex_lock(&pxrc->pm_mutex);
97 	retval = usb_submit_urb(pxrc->urb, GFP_KERNEL);
98 	if (retval) {
99 		dev_err(&pxrc->intf->dev,
100 			"%s - usb_submit_urb failed, error: %d\n",
101 			__func__, retval);
102 		retval = -EIO;
103 		goto out;
104 	}
105 
106 	pxrc->is_open = true;
107 
108 out:
109 	mutex_unlock(&pxrc->pm_mutex);
110 	return retval;
111 }
112 
113 static void pxrc_close(struct input_dev *input)
114 {
115 	struct pxrc *pxrc = input_get_drvdata(input);
116 
117 	mutex_lock(&pxrc->pm_mutex);
118 	usb_kill_urb(pxrc->urb);
119 	pxrc->is_open = false;
120 	mutex_unlock(&pxrc->pm_mutex);
121 }
122 
123 static int pxrc_usb_init(struct pxrc *pxrc)
124 {
125 	struct usb_endpoint_descriptor *epirq;
126 	unsigned int pipe;
127 	int retval;
128 
129 	/* Set up the endpoint information */
130 	/* This device only has an interrupt endpoint */
131 	retval = usb_find_common_endpoints(pxrc->intf->cur_altsetting,
132 			NULL, NULL, &epirq, NULL);
133 	if (retval) {
134 		dev_err(&pxrc->intf->dev,
135 			"Could not find endpoint\n");
136 		goto error;
137 	}
138 
139 	pxrc->bsize = usb_endpoint_maxp(epirq);
140 	pxrc->epaddr = epirq->bEndpointAddress;
141 	pxrc->data = devm_kmalloc(&pxrc->intf->dev, pxrc->bsize, GFP_KERNEL);
142 	if (!pxrc->data) {
143 		retval = -ENOMEM;
144 		goto error;
145 	}
146 
147 	usb_set_intfdata(pxrc->intf, pxrc);
148 	usb_make_path(pxrc->udev, pxrc->phys, sizeof(pxrc->phys));
149 	strlcat(pxrc->phys, "/input0", sizeof(pxrc->phys));
150 
151 	pxrc->urb = usb_alloc_urb(0, GFP_KERNEL);
152 	if (!pxrc->urb) {
153 		retval = -ENOMEM;
154 		goto error;
155 	}
156 
157 	pipe = usb_rcvintpipe(pxrc->udev, pxrc->epaddr),
158 	usb_fill_int_urb(pxrc->urb, pxrc->udev, pipe, pxrc->data, pxrc->bsize,
159 						pxrc_usb_irq, pxrc, 1);
160 
161 error:
162 	return retval;
163 
164 
165 }
166 
167 static int pxrc_input_init(struct pxrc *pxrc)
168 {
169 	pxrc->input = devm_input_allocate_device(&pxrc->intf->dev);
170 	if (pxrc->input == NULL) {
171 		dev_err(&pxrc->intf->dev, "couldn't allocate input device\n");
172 		return -ENOMEM;
173 	}
174 
175 	pxrc->input->name = "PXRC Flight Controller Adapter";
176 	pxrc->input->phys = pxrc->phys;
177 	usb_to_input_id(pxrc->udev, &pxrc->input->id);
178 
179 	pxrc->input->open = pxrc_open;
180 	pxrc->input->close = pxrc_close;
181 
182 	input_set_capability(pxrc->input, EV_KEY, BTN_A);
183 	input_set_abs_params(pxrc->input, ABS_X, 0, 255, 0, 0);
184 	input_set_abs_params(pxrc->input, ABS_Y, 0, 255, 0, 0);
185 	input_set_abs_params(pxrc->input, ABS_RX, 0, 255, 0, 0);
186 	input_set_abs_params(pxrc->input, ABS_RY, 0, 255, 0, 0);
187 	input_set_abs_params(pxrc->input, ABS_RUDDER, 0, 255, 0, 0);
188 	input_set_abs_params(pxrc->input, ABS_THROTTLE, 0, 255, 0, 0);
189 	input_set_abs_params(pxrc->input, ABS_MISC, 0, 255, 0, 0);
190 
191 	input_set_drvdata(pxrc->input, pxrc);
192 
193 	return input_register_device(pxrc->input);
194 }
195 
196 static int pxrc_probe(struct usb_interface *intf,
197 		      const struct usb_device_id *id)
198 {
199 	struct pxrc *pxrc;
200 	int retval;
201 
202 	pxrc = devm_kzalloc(&intf->dev, sizeof(*pxrc), GFP_KERNEL);
203 	if (!pxrc)
204 		return -ENOMEM;
205 
206 	mutex_init(&pxrc->pm_mutex);
207 	pxrc->udev = usb_get_dev(interface_to_usbdev(intf));
208 	pxrc->intf = intf;
209 
210 	retval = pxrc_usb_init(pxrc);
211 	if (retval)
212 		goto error;
213 
214 	retval = pxrc_input_init(pxrc);
215 	if (retval)
216 		goto err_free_urb;
217 
218 	return 0;
219 
220 err_free_urb:
221 	usb_free_urb(pxrc->urb);
222 
223 error:
224 	return retval;
225 }
226 
227 static void pxrc_disconnect(struct usb_interface *intf)
228 {
229 	struct pxrc *pxrc = usb_get_intfdata(intf);
230 
231 	usb_free_urb(pxrc->urb);
232 	usb_set_intfdata(intf, NULL);
233 }
234 
235 static int pxrc_suspend(struct usb_interface *intf, pm_message_t message)
236 {
237 	struct pxrc *pxrc = usb_get_intfdata(intf);
238 
239 	mutex_lock(&pxrc->pm_mutex);
240 	if (pxrc->is_open)
241 		usb_kill_urb(pxrc->urb);
242 	mutex_unlock(&pxrc->pm_mutex);
243 
244 	return 0;
245 }
246 
247 static int pxrc_resume(struct usb_interface *intf)
248 {
249 	struct pxrc *pxrc = usb_get_intfdata(intf);
250 	int retval = 0;
251 
252 	mutex_lock(&pxrc->pm_mutex);
253 	if (pxrc->is_open && usb_submit_urb(pxrc->urb, GFP_KERNEL) < 0)
254 		retval = -EIO;
255 
256 	mutex_unlock(&pxrc->pm_mutex);
257 	return retval;
258 }
259 
260 static int pxrc_pre_reset(struct usb_interface *intf)
261 {
262 	struct pxrc *pxrc = usb_get_intfdata(intf);
263 
264 	mutex_lock(&pxrc->pm_mutex);
265 	usb_kill_urb(pxrc->urb);
266 	return 0;
267 }
268 
269 static int pxrc_post_reset(struct usb_interface *intf)
270 {
271 	struct pxrc *pxrc = usb_get_intfdata(intf);
272 	int retval = 0;
273 
274 	if (pxrc->is_open && usb_submit_urb(pxrc->urb, GFP_KERNEL) < 0)
275 		retval = -EIO;
276 
277 	mutex_unlock(&pxrc->pm_mutex);
278 
279 	return retval;
280 }
281 
282 static int pxrc_reset_resume(struct usb_interface *intf)
283 {
284 	return pxrc_resume(intf);
285 }
286 
287 static struct usb_driver pxrc_driver = {
288 	.name =		"pxrc",
289 	.probe =	pxrc_probe,
290 	.disconnect =	pxrc_disconnect,
291 	.id_table =	pxrc_table,
292 	.suspend	= pxrc_suspend,
293 	.resume		= pxrc_resume,
294 	.pre_reset	= pxrc_pre_reset,
295 	.post_reset	= pxrc_post_reset,
296 	.reset_resume	= pxrc_reset_resume,
297 };
298 
299 module_usb_driver(pxrc_driver);
300 
301 MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
302 MODULE_DESCRIPTION("PhoenixRC Flight Controller Adapter");
303 MODULE_LICENSE("GPL v2");
304