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 #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> 16 17 #include <linux/usb.h> 18 #include <linux/usb/input.h> 19 20 #define PXRC_VENDOR_ID 0x1781 21 #define PXRC_PRODUCT_ID 0x0898 22 23 struct pxrc { 24 struct input_dev *input; 25 struct usb_interface *intf; 26 struct urb *urb; 27 struct mutex pm_mutex; 28 bool is_open; 29 char phys[64]; 30 }; 31 32 static void pxrc_usb_irq(struct urb *urb) 33 { 34 struct pxrc *pxrc = urb->context; 35 u8 *data = urb->transfer_buffer; 36 int error; 37 38 switch (urb->status) { 39 case 0: 40 /* success */ 41 break; 42 case -ETIME: 43 /* this urb is timing out */ 44 dev_dbg(&pxrc->intf->dev, 45 "%s - urb timed out - was the device unplugged?\n", 46 __func__); 47 return; 48 case -ECONNRESET: 49 case -ENOENT: 50 case -ESHUTDOWN: 51 case -EPIPE: 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); 55 return; 56 default: 57 dev_dbg(&pxrc->intf->dev, "%s - nonzero urb status received: %d\n", 58 __func__, urb->status); 59 goto exit; 60 } 61 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]); 70 71 input_report_key(pxrc->input, BTN_A, data[1]); 72 } 73 74 exit: 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", 80 __func__, error); 81 } 82 83 static int pxrc_open(struct input_dev *input) 84 { 85 struct pxrc *pxrc = input_get_drvdata(input); 86 int error; 87 88 guard(mutex)(&pxrc->pm_mutex); 89 error = usb_submit_urb(pxrc->urb, GFP_KERNEL); 90 if (error) { 91 dev_err(&pxrc->intf->dev, 92 "%s - usb_submit_urb failed, error: %d\n", 93 __func__, error); 94 return -EIO; 95 } 96 97 pxrc->is_open = true; 98 return 0; 99 } 100 101 static void pxrc_close(struct input_dev *input) 102 { 103 struct pxrc *pxrc = input_get_drvdata(input); 104 105 guard(mutex)(&pxrc->pm_mutex); 106 usb_kill_urb(pxrc->urb); 107 pxrc->is_open = false; 108 } 109 110 static void pxrc_free_urb(void *_pxrc) 111 { 112 struct pxrc *pxrc = _pxrc; 113 114 usb_free_urb(pxrc->urb); 115 } 116 117 static int pxrc_probe(struct usb_interface *intf, 118 const struct usb_device_id *id) 119 { 120 struct usb_device *udev = interface_to_usbdev(intf); 121 struct pxrc *pxrc; 122 struct usb_endpoint_descriptor *epirq; 123 size_t xfer_size; 124 void *xfer_buf; 125 int error; 126 127 /* 128 * Locate the endpoint information. This device only has an 129 * interrupt endpoint. 130 */ 131 error = usb_find_common_endpoints(intf->cur_altsetting, 132 NULL, NULL, &epirq, NULL); 133 if (error) { 134 dev_err(&intf->dev, "Could not find endpoint\n"); 135 return error; 136 } 137 138 pxrc = devm_kzalloc(&intf->dev, sizeof(*pxrc), GFP_KERNEL); 139 if (!pxrc) 140 return -ENOMEM; 141 142 mutex_init(&pxrc->pm_mutex); 143 pxrc->intf = intf; 144 145 usb_set_intfdata(pxrc->intf, pxrc); 146 147 xfer_size = usb_endpoint_maxp(epirq); 148 xfer_buf = devm_kmalloc(&intf->dev, xfer_size, GFP_KERNEL); 149 if (!xfer_buf) 150 return -ENOMEM; 151 152 pxrc->urb = usb_alloc_urb(0, GFP_KERNEL); 153 if (!pxrc->urb) 154 return -ENOMEM; 155 156 error = devm_add_action_or_reset(&intf->dev, pxrc_free_urb, pxrc); 157 if (error) 158 return error; 159 160 usb_fill_int_urb(pxrc->urb, udev, 161 usb_rcvintpipe(udev, epirq->bEndpointAddress), 162 xfer_buf, xfer_size, pxrc_usb_irq, pxrc, 1); 163 164 pxrc->input = devm_input_allocate_device(&intf->dev); 165 if (!pxrc->input) { 166 dev_err(&intf->dev, "couldn't allocate input device\n"); 167 return -ENOMEM; 168 } 169 170 pxrc->input->name = "PXRC Flight Controller Adapter"; 171 172 usb_make_path(udev, pxrc->phys, sizeof(pxrc->phys)); 173 strlcat(pxrc->phys, "/input0", sizeof(pxrc->phys)); 174 pxrc->input->phys = pxrc->phys; 175 176 usb_to_input_id(udev, &pxrc->input->id); 177 178 pxrc->input->open = pxrc_open; 179 pxrc->input->close = pxrc_close; 180 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); 189 190 input_set_drvdata(pxrc->input, pxrc); 191 192 error = input_register_device(pxrc->input); 193 if (error) 194 return error; 195 196 return 0; 197 } 198 199 static void pxrc_disconnect(struct usb_interface *intf) 200 { 201 /* All driver resources are devm-managed. */ 202 } 203 204 static int pxrc_suspend(struct usb_interface *intf, pm_message_t message) 205 { 206 struct pxrc *pxrc = usb_get_intfdata(intf); 207 208 guard(mutex)(&pxrc->pm_mutex); 209 if (pxrc->is_open) 210 usb_kill_urb(pxrc->urb); 211 212 return 0; 213 } 214 215 static int pxrc_resume(struct usb_interface *intf) 216 { 217 struct pxrc *pxrc = usb_get_intfdata(intf); 218 219 guard(mutex)(&pxrc->pm_mutex); 220 if (pxrc->is_open && usb_submit_urb(pxrc->urb, GFP_KERNEL) < 0) 221 return -EIO; 222 223 return 0; 224 } 225 226 static int pxrc_pre_reset(struct usb_interface *intf) 227 { 228 struct pxrc *pxrc = usb_get_intfdata(intf); 229 230 mutex_lock(&pxrc->pm_mutex); 231 usb_kill_urb(pxrc->urb); 232 return 0; 233 } 234 235 static int pxrc_post_reset(struct usb_interface *intf) 236 { 237 struct pxrc *pxrc = usb_get_intfdata(intf); 238 int retval = 0; 239 240 if (pxrc->is_open && usb_submit_urb(pxrc->urb, GFP_KERNEL) < 0) 241 retval = -EIO; 242 243 mutex_unlock(&pxrc->pm_mutex); 244 245 return retval; 246 } 247 248 static int pxrc_reset_resume(struct usb_interface *intf) 249 { 250 return pxrc_resume(intf); 251 } 252 253 static const struct usb_device_id pxrc_table[] = { 254 { USB_DEVICE(PXRC_VENDOR_ID, PXRC_PRODUCT_ID) }, 255 { } 256 }; 257 MODULE_DEVICE_TABLE(usb, pxrc_table); 258 259 static struct usb_driver pxrc_driver = { 260 .name = "pxrc", 261 .probe = pxrc_probe, 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, 269 }; 270 271 module_usb_driver(pxrc_driver); 272 273 MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>"); 274 MODULE_DESCRIPTION("PhoenixRC Flight Controller Adapter"); 275 MODULE_LICENSE("GPL v2"); 276