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