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>
8 #include <linux/cleanup.h>
9 #include <linux/errno.h>
10 #include <linux/input.h>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/mutex.h>
14 #include <linux/slab.h>
15 #include <linux/uaccess.h>
17 #include <linux/usb.h>
18 #include <linux/usb/input.h>
20 #define PXRC_VENDOR_ID 0x1781
21 #define PXRC_PRODUCT_ID 0x0898
24 struct input_dev
*input
;
25 struct usb_interface
*intf
;
27 struct mutex pm_mutex
;
32 static void pxrc_usb_irq(struct urb
*urb
)
34 struct pxrc
*pxrc
= urb
->context
;
35 u8
*data
= urb
->transfer_buffer
;
38 switch (urb
->status
) {
43 /* this urb is timing out */
44 dev_dbg(&pxrc
->intf
->dev
,
45 "%s - urb timed out - was the device unplugged?\n",
52 /* this urb is terminated, clean up */
53 dev_dbg(&pxrc
->intf
->dev
, "%s - urb shutting down with status: %d\n",
54 __func__
, urb
->status
);
57 dev_dbg(&pxrc
->intf
->dev
, "%s - nonzero urb status received: %d\n",
58 __func__
, urb
->status
);
62 if (urb
->actual_length
== 8) {
63 input_report_abs(pxrc
->input
, ABS_X
, data
[0]);
64 input_report_abs(pxrc
->input
, ABS_Y
, data
[2]);
65 input_report_abs(pxrc
->input
, ABS_RX
, data
[3]);
66 input_report_abs(pxrc
->input
, ABS_RY
, data
[4]);
67 input_report_abs(pxrc
->input
, ABS_RUDDER
, data
[5]);
68 input_report_abs(pxrc
->input
, ABS_THROTTLE
, data
[6]);
69 input_report_abs(pxrc
->input
, ABS_MISC
, data
[7]);
71 input_report_key(pxrc
->input
, BTN_A
, data
[1]);
75 /* Resubmit to fetch new fresh URBs */
76 error
= usb_submit_urb(urb
, GFP_ATOMIC
);
77 if (error
&& error
!= -EPERM
)
78 dev_err(&pxrc
->intf
->dev
,
79 "%s - usb_submit_urb failed with result: %d",
83 static int pxrc_open(struct input_dev
*input
)
85 struct pxrc
*pxrc
= input_get_drvdata(input
);
88 guard(mutex
)(&pxrc
->pm_mutex
);
89 error
= usb_submit_urb(pxrc
->urb
, GFP_KERNEL
);
91 dev_err(&pxrc
->intf
->dev
,
92 "%s - usb_submit_urb failed, error: %d\n",
101 static void pxrc_close(struct input_dev
*input
)
103 struct pxrc
*pxrc
= input_get_drvdata(input
);
105 guard(mutex
)(&pxrc
->pm_mutex
);
106 usb_kill_urb(pxrc
->urb
);
107 pxrc
->is_open
= false;
110 static void pxrc_free_urb(void *_pxrc
)
112 struct pxrc
*pxrc
= _pxrc
;
114 usb_free_urb(pxrc
->urb
);
117 static int pxrc_probe(struct usb_interface
*intf
,
118 const struct usb_device_id
*id
)
120 struct usb_device
*udev
= interface_to_usbdev(intf
);
122 struct usb_endpoint_descriptor
*epirq
;
128 * Locate the endpoint information. This device only has an
129 * interrupt endpoint.
131 error
= usb_find_common_endpoints(intf
->cur_altsetting
,
132 NULL
, NULL
, &epirq
, NULL
);
134 dev_err(&intf
->dev
, "Could not find endpoint\n");
138 pxrc
= devm_kzalloc(&intf
->dev
, sizeof(*pxrc
), GFP_KERNEL
);
142 mutex_init(&pxrc
->pm_mutex
);
145 usb_set_intfdata(pxrc
->intf
, pxrc
);
147 xfer_size
= usb_endpoint_maxp(epirq
);
148 xfer_buf
= devm_kmalloc(&intf
->dev
, xfer_size
, GFP_KERNEL
);
152 pxrc
->urb
= usb_alloc_urb(0, GFP_KERNEL
);
156 error
= devm_add_action_or_reset(&intf
->dev
, pxrc_free_urb
, pxrc
);
160 usb_fill_int_urb(pxrc
->urb
, udev
,
161 usb_rcvintpipe(udev
, epirq
->bEndpointAddress
),
162 xfer_buf
, xfer_size
, pxrc_usb_irq
, pxrc
, 1);
164 pxrc
->input
= devm_input_allocate_device(&intf
->dev
);
166 dev_err(&intf
->dev
, "couldn't allocate input device\n");
170 pxrc
->input
->name
= "PXRC Flight Controller Adapter";
172 usb_make_path(udev
, pxrc
->phys
, sizeof(pxrc
->phys
));
173 strlcat(pxrc
->phys
, "/input0", sizeof(pxrc
->phys
));
174 pxrc
->input
->phys
= pxrc
->phys
;
176 usb_to_input_id(udev
, &pxrc
->input
->id
);
178 pxrc
->input
->open
= pxrc_open
;
179 pxrc
->input
->close
= pxrc_close
;
181 input_set_capability(pxrc
->input
, EV_KEY
, BTN_A
);
182 input_set_abs_params(pxrc
->input
, ABS_X
, 0, 255, 0, 0);
183 input_set_abs_params(pxrc
->input
, ABS_Y
, 0, 255, 0, 0);
184 input_set_abs_params(pxrc
->input
, ABS_RX
, 0, 255, 0, 0);
185 input_set_abs_params(pxrc
->input
, ABS_RY
, 0, 255, 0, 0);
186 input_set_abs_params(pxrc
->input
, ABS_RUDDER
, 0, 255, 0, 0);
187 input_set_abs_params(pxrc
->input
, ABS_THROTTLE
, 0, 255, 0, 0);
188 input_set_abs_params(pxrc
->input
, ABS_MISC
, 0, 255, 0, 0);
190 input_set_drvdata(pxrc
->input
, pxrc
);
192 error
= input_register_device(pxrc
->input
);
199 static void pxrc_disconnect(struct usb_interface
*intf
)
201 /* All driver resources are devm-managed. */
204 static int pxrc_suspend(struct usb_interface
*intf
, pm_message_t message
)
206 struct pxrc
*pxrc
= usb_get_intfdata(intf
);
208 guard(mutex
)(&pxrc
->pm_mutex
);
210 usb_kill_urb(pxrc
->urb
);
215 static int pxrc_resume(struct usb_interface
*intf
)
217 struct pxrc
*pxrc
= usb_get_intfdata(intf
);
219 guard(mutex
)(&pxrc
->pm_mutex
);
220 if (pxrc
->is_open
&& usb_submit_urb(pxrc
->urb
, GFP_KERNEL
) < 0)
226 static int pxrc_pre_reset(struct usb_interface
*intf
)
228 struct pxrc
*pxrc
= usb_get_intfdata(intf
);
230 mutex_lock(&pxrc
->pm_mutex
);
231 usb_kill_urb(pxrc
->urb
);
235 static int pxrc_post_reset(struct usb_interface
*intf
)
237 struct pxrc
*pxrc
= usb_get_intfdata(intf
);
240 if (pxrc
->is_open
&& usb_submit_urb(pxrc
->urb
, GFP_KERNEL
) < 0)
243 mutex_unlock(&pxrc
->pm_mutex
);
248 static int pxrc_reset_resume(struct usb_interface
*intf
)
250 return pxrc_resume(intf
);
253 static const struct usb_device_id pxrc_table
[] = {
254 { USB_DEVICE(PXRC_VENDOR_ID
, PXRC_PRODUCT_ID
) },
257 MODULE_DEVICE_TABLE(usb
, pxrc_table
);
259 static struct usb_driver pxrc_driver
= {
262 .disconnect
= pxrc_disconnect
,
263 .id_table
= pxrc_table
,
264 .suspend
= pxrc_suspend
,
265 .resume
= pxrc_resume
,
266 .pre_reset
= pxrc_pre_reset
,
267 .post_reset
= pxrc_post_reset
,
268 .reset_resume
= pxrc_reset_resume
,
271 module_usb_driver(pxrc_driver
);
273 MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
274 MODULE_DESCRIPTION("PhoenixRC Flight Controller Adapter");
275 MODULE_LICENSE("GPL v2");