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/kernel.h>
9 #include <linux/errno.h>
10 #include <linux/slab.h>
11 #include <linux/module.h>
12 #include <linux/uaccess.h>
13 #include <linux/usb.h>
14 #include <linux/usb/input.h>
15 #include <linux/mutex.h>
16 #include <linux/input.h>
18 #define PXRC_VENDOR_ID 0x1781
19 #define PXRC_PRODUCT_ID 0x0898
22 struct input_dev
*input
;
23 struct usb_interface
*intf
;
25 struct mutex pm_mutex
;
30 static void pxrc_usb_irq(struct urb
*urb
)
32 struct pxrc
*pxrc
= urb
->context
;
33 u8
*data
= urb
->transfer_buffer
;
36 switch (urb
->status
) {
41 /* this urb is timing out */
42 dev_dbg(&pxrc
->intf
->dev
,
43 "%s - urb timed out - was the device unplugged?\n",
50 /* this urb is terminated, clean up */
51 dev_dbg(&pxrc
->intf
->dev
, "%s - urb shutting down with status: %d\n",
52 __func__
, urb
->status
);
55 dev_dbg(&pxrc
->intf
->dev
, "%s - nonzero urb status received: %d\n",
56 __func__
, urb
->status
);
60 if (urb
->actual_length
== 8) {
61 input_report_abs(pxrc
->input
, ABS_X
, data
[0]);
62 input_report_abs(pxrc
->input
, ABS_Y
, data
[2]);
63 input_report_abs(pxrc
->input
, ABS_RX
, data
[3]);
64 input_report_abs(pxrc
->input
, ABS_RY
, data
[4]);
65 input_report_abs(pxrc
->input
, ABS_RUDDER
, data
[5]);
66 input_report_abs(pxrc
->input
, ABS_THROTTLE
, data
[6]);
67 input_report_abs(pxrc
->input
, ABS_MISC
, data
[7]);
69 input_report_key(pxrc
->input
, BTN_A
, data
[1]);
73 /* Resubmit to fetch new fresh URBs */
74 error
= usb_submit_urb(urb
, GFP_ATOMIC
);
75 if (error
&& error
!= -EPERM
)
76 dev_err(&pxrc
->intf
->dev
,
77 "%s - usb_submit_urb failed with result: %d",
81 static int pxrc_open(struct input_dev
*input
)
83 struct pxrc
*pxrc
= input_get_drvdata(input
);
86 mutex_lock(&pxrc
->pm_mutex
);
87 retval
= usb_submit_urb(pxrc
->urb
, GFP_KERNEL
);
89 dev_err(&pxrc
->intf
->dev
,
90 "%s - usb_submit_urb failed, error: %d\n",
99 mutex_unlock(&pxrc
->pm_mutex
);
103 static void pxrc_close(struct input_dev
*input
)
105 struct pxrc
*pxrc
= input_get_drvdata(input
);
107 mutex_lock(&pxrc
->pm_mutex
);
108 usb_kill_urb(pxrc
->urb
);
109 pxrc
->is_open
= false;
110 mutex_unlock(&pxrc
->pm_mutex
);
113 static void pxrc_free_urb(void *_pxrc
)
115 struct pxrc
*pxrc
= _pxrc
;
117 usb_free_urb(pxrc
->urb
);
120 static int pxrc_probe(struct usb_interface
*intf
,
121 const struct usb_device_id
*id
)
123 struct usb_device
*udev
= interface_to_usbdev(intf
);
125 struct usb_endpoint_descriptor
*epirq
;
131 * Locate the endpoint information. This device only has an
132 * interrupt endpoint.
134 error
= usb_find_common_endpoints(intf
->cur_altsetting
,
135 NULL
, NULL
, &epirq
, NULL
);
137 dev_err(&intf
->dev
, "Could not find endpoint\n");
141 pxrc
= devm_kzalloc(&intf
->dev
, sizeof(*pxrc
), GFP_KERNEL
);
145 mutex_init(&pxrc
->pm_mutex
);
148 usb_set_intfdata(pxrc
->intf
, pxrc
);
150 xfer_size
= usb_endpoint_maxp(epirq
);
151 xfer_buf
= devm_kmalloc(&intf
->dev
, xfer_size
, GFP_KERNEL
);
155 pxrc
->urb
= usb_alloc_urb(0, GFP_KERNEL
);
159 error
= devm_add_action_or_reset(&intf
->dev
, pxrc_free_urb
, pxrc
);
163 usb_fill_int_urb(pxrc
->urb
, udev
,
164 usb_rcvintpipe(udev
, epirq
->bEndpointAddress
),
165 xfer_buf
, xfer_size
, pxrc_usb_irq
, pxrc
, 1);
167 pxrc
->input
= devm_input_allocate_device(&intf
->dev
);
169 dev_err(&intf
->dev
, "couldn't allocate input device\n");
173 pxrc
->input
->name
= "PXRC Flight Controller Adapter";
175 usb_make_path(udev
, pxrc
->phys
, sizeof(pxrc
->phys
));
176 strlcat(pxrc
->phys
, "/input0", sizeof(pxrc
->phys
));
177 pxrc
->input
->phys
= pxrc
->phys
;
179 usb_to_input_id(udev
, &pxrc
->input
->id
);
181 pxrc
->input
->open
= pxrc_open
;
182 pxrc
->input
->close
= pxrc_close
;
184 input_set_capability(pxrc
->input
, EV_KEY
, BTN_A
);
185 input_set_abs_params(pxrc
->input
, ABS_X
, 0, 255, 0, 0);
186 input_set_abs_params(pxrc
->input
, ABS_Y
, 0, 255, 0, 0);
187 input_set_abs_params(pxrc
->input
, ABS_RX
, 0, 255, 0, 0);
188 input_set_abs_params(pxrc
->input
, ABS_RY
, 0, 255, 0, 0);
189 input_set_abs_params(pxrc
->input
, ABS_RUDDER
, 0, 255, 0, 0);
190 input_set_abs_params(pxrc
->input
, ABS_THROTTLE
, 0, 255, 0, 0);
191 input_set_abs_params(pxrc
->input
, ABS_MISC
, 0, 255, 0, 0);
193 input_set_drvdata(pxrc
->input
, pxrc
);
195 error
= input_register_device(pxrc
->input
);
202 static void pxrc_disconnect(struct usb_interface
*intf
)
204 /* All driver resources are devm-managed. */
207 static int pxrc_suspend(struct usb_interface
*intf
, pm_message_t message
)
209 struct pxrc
*pxrc
= usb_get_intfdata(intf
);
211 mutex_lock(&pxrc
->pm_mutex
);
213 usb_kill_urb(pxrc
->urb
);
214 mutex_unlock(&pxrc
->pm_mutex
);
219 static int pxrc_resume(struct usb_interface
*intf
)
221 struct pxrc
*pxrc
= usb_get_intfdata(intf
);
224 mutex_lock(&pxrc
->pm_mutex
);
225 if (pxrc
->is_open
&& usb_submit_urb(pxrc
->urb
, GFP_KERNEL
) < 0)
228 mutex_unlock(&pxrc
->pm_mutex
);
232 static int pxrc_pre_reset(struct usb_interface
*intf
)
234 struct pxrc
*pxrc
= usb_get_intfdata(intf
);
236 mutex_lock(&pxrc
->pm_mutex
);
237 usb_kill_urb(pxrc
->urb
);
241 static int pxrc_post_reset(struct usb_interface
*intf
)
243 struct pxrc
*pxrc
= usb_get_intfdata(intf
);
246 if (pxrc
->is_open
&& usb_submit_urb(pxrc
->urb
, GFP_KERNEL
) < 0)
249 mutex_unlock(&pxrc
->pm_mutex
);
254 static int pxrc_reset_resume(struct usb_interface
*intf
)
256 return pxrc_resume(intf
);
259 static const struct usb_device_id pxrc_table
[] = {
260 { USB_DEVICE(PXRC_VENDOR_ID
, PXRC_PRODUCT_ID
) },
263 MODULE_DEVICE_TABLE(usb
, pxrc_table
);
265 static struct usb_driver pxrc_driver
= {
268 .disconnect
= pxrc_disconnect
,
269 .id_table
= pxrc_table
,
270 .suspend
= pxrc_suspend
,
271 .resume
= pxrc_resume
,
272 .pre_reset
= pxrc_pre_reset
,
273 .post_reset
= pxrc_post_reset
,
274 .reset_resume
= pxrc_reset_resume
,
277 module_usb_driver(pxrc_driver
);
279 MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
280 MODULE_DESCRIPTION("PhoenixRC Flight Controller Adapter");
281 MODULE_LICENSE("GPL v2");