linux/drivers/input/joystick/pxrc.c
<<
>>
Prefs
   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
  22static const struct usb_device_id pxrc_table[] = {
  23        { USB_DEVICE(PXRC_VENDOR_ID, PXRC_PRODUCT_ID) },
  24        { }
  25};
  26MODULE_DEVICE_TABLE(usb, pxrc_table);
  27
  28struct 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
  41static 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
  82exit:
  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
  91static 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
 108out:
 109        mutex_unlock(&pxrc->pm_mutex);
 110        return retval;
 111}
 112
 113static 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
 123static 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
 161error:
 162        return retval;
 163
 164
 165}
 166
 167static 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
 196static 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
 220err_free_urb:
 221        usb_free_urb(pxrc->urb);
 222
 223error:
 224        return retval;
 225}
 226
 227static 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
 235static 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
 247static 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
 260static 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
 269static 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
 282static int pxrc_reset_resume(struct usb_interface *intf)
 283{
 284        return pxrc_resume(intf);
 285}
 286
 287static 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
 299module_usb_driver(pxrc_driver);
 300
 301MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
 302MODULE_DESCRIPTION("PhoenixRC Flight Controller Adapter");
 303MODULE_LICENSE("GPL v2");
 304