1 // SPDX-License-Identifier: GPL-2.0
3 * Driver for Phoenix RC Flight Controller Adapter
5 * Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
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>
19 #define PXRC_VENDOR_ID (0x1781)
20 #define PXRC_PRODUCT_ID (0x0898)
22 static const struct usb_device_id pxrc_table
[] = {
23 { USB_DEVICE(PXRC_VENDOR_ID
, PXRC_PRODUCT_ID
) },
26 MODULE_DEVICE_TABLE(usb
, pxrc_table
);
29 struct input_dev
*input
;
30 struct usb_device
*udev
;
31 struct usb_interface
*intf
;
33 struct mutex pm_mutex
;
41 static void pxrc_usb_irq(struct urb
*urb
)
43 struct pxrc
*pxrc
= urb
->context
;
46 switch (urb
->status
) {
51 /* this urb is timing out */
52 dev_dbg(&pxrc
->intf
->dev
,
53 "%s - urb timed out - was the device unplugged?\n",
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
);
65 dev_dbg(&pxrc
->intf
->dev
, "%s - nonzero urb status received: %d\n",
66 __func__
, urb
->status
);
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]);
79 input_report_key(pxrc
->input
, BTN_A
, pxrc
->data
[1]);
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",
91 static int pxrc_open(struct input_dev
*input
)
93 struct pxrc
*pxrc
= input_get_drvdata(input
);
96 mutex_lock(&pxrc
->pm_mutex
);
97 retval
= usb_submit_urb(pxrc
->urb
, GFP_KERNEL
);
99 dev_err(&pxrc
->intf
->dev
,
100 "%s - usb_submit_urb failed, error: %d\n",
106 pxrc
->is_open
= true;
109 mutex_unlock(&pxrc
->pm_mutex
);
113 static void pxrc_close(struct input_dev
*input
)
115 struct pxrc
*pxrc
= input_get_drvdata(input
);
117 mutex_lock(&pxrc
->pm_mutex
);
118 usb_kill_urb(pxrc
->urb
);
119 pxrc
->is_open
= false;
120 mutex_unlock(&pxrc
->pm_mutex
);
123 static int pxrc_usb_init(struct pxrc
*pxrc
)
125 struct usb_endpoint_descriptor
*epirq
;
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
);
134 dev_err(&pxrc
->intf
->dev
,
135 "Could not find endpoint\n");
139 pxrc
->bsize
= usb_endpoint_maxp(epirq
);
140 pxrc
->epaddr
= epirq
->bEndpointAddress
;
141 pxrc
->data
= devm_kmalloc(&pxrc
->intf
->dev
, pxrc
->bsize
, GFP_KERNEL
);
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
));
151 pxrc
->urb
= usb_alloc_urb(0, GFP_KERNEL
);
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);
167 static int pxrc_input_init(struct pxrc
*pxrc
)
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");
175 pxrc
->input
->name
= "PXRC Flight Controller Adapter";
176 pxrc
->input
->phys
= pxrc
->phys
;
177 usb_to_input_id(pxrc
->udev
, &pxrc
->input
->id
);
179 pxrc
->input
->open
= pxrc_open
;
180 pxrc
->input
->close
= pxrc_close
;
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);
191 input_set_drvdata(pxrc
->input
, pxrc
);
193 return input_register_device(pxrc
->input
);
196 static int pxrc_probe(struct usb_interface
*intf
,
197 const struct usb_device_id
*id
)
202 pxrc
= devm_kzalloc(&intf
->dev
, sizeof(*pxrc
), GFP_KERNEL
);
206 mutex_init(&pxrc
->pm_mutex
);
207 pxrc
->udev
= usb_get_dev(interface_to_usbdev(intf
));
210 retval
= pxrc_usb_init(pxrc
);
214 retval
= pxrc_input_init(pxrc
);
221 usb_free_urb(pxrc
->urb
);
227 static void pxrc_disconnect(struct usb_interface
*intf
)
229 struct pxrc
*pxrc
= usb_get_intfdata(intf
);
231 usb_free_urb(pxrc
->urb
);
232 usb_set_intfdata(intf
, NULL
);
235 static int pxrc_suspend(struct usb_interface
*intf
, pm_message_t message
)
237 struct pxrc
*pxrc
= usb_get_intfdata(intf
);
239 mutex_lock(&pxrc
->pm_mutex
);
241 usb_kill_urb(pxrc
->urb
);
242 mutex_unlock(&pxrc
->pm_mutex
);
247 static int pxrc_resume(struct usb_interface
*intf
)
249 struct pxrc
*pxrc
= usb_get_intfdata(intf
);
252 mutex_lock(&pxrc
->pm_mutex
);
253 if (pxrc
->is_open
&& usb_submit_urb(pxrc
->urb
, GFP_KERNEL
) < 0)
256 mutex_unlock(&pxrc
->pm_mutex
);
260 static int pxrc_pre_reset(struct usb_interface
*intf
)
262 struct pxrc
*pxrc
= usb_get_intfdata(intf
);
264 mutex_lock(&pxrc
->pm_mutex
);
265 usb_kill_urb(pxrc
->urb
);
269 static int pxrc_post_reset(struct usb_interface
*intf
)
271 struct pxrc
*pxrc
= usb_get_intfdata(intf
);
274 if (pxrc
->is_open
&& usb_submit_urb(pxrc
->urb
, GFP_KERNEL
) < 0)
277 mutex_unlock(&pxrc
->pm_mutex
);
282 static int pxrc_reset_resume(struct usb_interface
*intf
)
284 return pxrc_resume(intf
);
287 static struct usb_driver pxrc_driver
= {
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
,
299 module_usb_driver(pxrc_driver
);
301 MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
302 MODULE_DESCRIPTION("PhoenixRC Flight Controller Adapter");
303 MODULE_LICENSE("GPL v2");