From e99e88a9d2b067465adaa9c111ada99a041bef9a Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 16 Oct 2017 14:43:17 -0700 Subject: treewide: setup_timer() -> timer_setup() This converts all remaining cases of the old setup_timer() API into using timer_setup(), where the callback argument is the structure already holding the struct timer_list. These should have no behavioral changes, since they just change which pointer is passed into the callback with the same available pointers after conversion. It handles the following examples, in addition to some other variations. Casting from unsigned long: void my_callback(unsigned long data) { struct something *ptr = (struct something *)data; ... } ... setup_timer(&ptr->my_timer, my_callback, ptr); and forced object casts: void my_callback(struct something *ptr) { ... } ... setup_timer(&ptr->my_timer, my_callback, (unsigned long)ptr); become: void my_callback(struct timer_list *t) { struct something *ptr = from_timer(ptr, t, my_timer); ... } ... timer_setup(&ptr->my_timer, my_callback, 0); Direct function assignments: void my_callback(unsigned long data) { struct something *ptr = (struct something *)data; ... } ... ptr->my_timer.function = my_callback; have a temporary cast added, along with converting the args: void my_callback(struct timer_list *t) { struct something *ptr = from_timer(ptr, t, my_timer); ... } ... ptr->my_timer.function = (TIMER_FUNC_TYPE)my_callback; And finally, callbacks without a data assignment: void my_callback(unsigned long data) { ... } ... setup_timer(&ptr->my_timer, my_callback, 0); have their argument renamed to verify they're unused during conversion: void my_callback(struct timer_list *unused) { ... } ... timer_setup(&ptr->my_timer, my_callback, 0); The conversion is done with the following Coccinelle script: spatch --very-quiet --all-includes --include-headers \ -I ./arch/x86/include -I ./arch/x86/include/generated \ -I ./include -I ./arch/x86/include/uapi \ -I ./arch/x86/include/generated/uapi -I ./include/uapi \ -I ./include/generated/uapi --include ./include/linux/kconfig.h \ --dir . \ --cocci-file ~/src/data/timer_setup.cocci @fix_address_of@ expression e; @@ setup_timer( -&(e) +&e , ...) // Update any raw setup_timer() usages that have a NULL callback, but // would otherwise match change_timer_function_usage, since the latter // will update all function assignments done in the face of a NULL // function initialization in setup_timer(). @change_timer_function_usage_NULL@ expression _E; identifier _timer; type _cast_data; @@ ( -setup_timer(&_E->_timer, NULL, _E); +timer_setup(&_E->_timer, NULL, 0); | -setup_timer(&_E->_timer, NULL, (_cast_data)_E); +timer_setup(&_E->_timer, NULL, 0); | -setup_timer(&_E._timer, NULL, &_E); +timer_setup(&_E._timer, NULL, 0); | -setup_timer(&_E._timer, NULL, (_cast_data)&_E); +timer_setup(&_E._timer, NULL, 0); ) @change_timer_function_usage@ expression _E; identifier _timer; struct timer_list _stl; identifier _callback; type _cast_func, _cast_data; @@ ( -setup_timer(&_E->_timer, _callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, &_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, &_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)&_callback, _E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, (_cast_func)&_callback, (_cast_data)_E); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E._timer, _callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, &_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, &_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)_E); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, (_cast_func)&_callback, (_cast_data)&_E); +timer_setup(&_E._timer, _callback, 0); | _E->_timer@_stl.function = _callback; | _E->_timer@_stl.function = &_callback; | _E->_timer@_stl.function = (_cast_func)_callback; | _E->_timer@_stl.function = (_cast_func)&_callback; | _E._timer@_stl.function = _callback; | _E._timer@_stl.function = &_callback; | _E._timer@_stl.function = (_cast_func)_callback; | _E._timer@_stl.function = (_cast_func)&_callback; ) // callback(unsigned long arg) @change_callback_handle_cast depends on change_timer_function_usage@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _origtype; identifier _origarg; type _handletype; identifier _handle; @@ void _callback( -_origtype _origarg +struct timer_list *t ) { ( ... when != _origarg _handletype *_handle = -(_handletype *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle = -(void *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle; ... when != _handle _handle = -(_handletype *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg | ... when != _origarg _handletype *_handle; ... when != _handle _handle = -(void *)_origarg; +from_timer(_handle, t, _timer); ... when != _origarg ) } // callback(unsigned long arg) without existing variable @change_callback_handle_cast_no_arg depends on change_timer_function_usage && !change_callback_handle_cast@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _origtype; identifier _origarg; type _handletype; @@ void _callback( -_origtype _origarg +struct timer_list *t ) { + _handletype *_origarg = from_timer(_origarg, t, _timer); + ... when != _origarg - (_handletype *)_origarg + _origarg ... when != _origarg } // Avoid already converted callbacks. @match_callback_converted depends on change_timer_function_usage && !change_callback_handle_cast && !change_callback_handle_cast_no_arg@ identifier change_timer_function_usage._callback; identifier t; @@ void _callback(struct timer_list *t) { ... } // callback(struct something *handle) @change_callback_handle_arg depends on change_timer_function_usage && !match_callback_converted && !change_callback_handle_cast && !change_callback_handle_cast_no_arg@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _handletype; identifier _handle; @@ void _callback( -_handletype *_handle +struct timer_list *t ) { + _handletype *_handle = from_timer(_handle, t, _timer); ... } // If change_callback_handle_arg ran on an empty function, remove // the added handler. @unchange_callback_handle_arg depends on change_timer_function_usage && change_callback_handle_arg@ identifier change_timer_function_usage._callback; identifier change_timer_function_usage._timer; type _handletype; identifier _handle; identifier t; @@ void _callback(struct timer_list *t) { - _handletype *_handle = from_timer(_handle, t, _timer); } // We only want to refactor the setup_timer() data argument if we've found // the matching callback. This undoes changes in change_timer_function_usage. @unchange_timer_function_usage depends on change_timer_function_usage && !change_callback_handle_cast && !change_callback_handle_cast_no_arg && !change_callback_handle_arg@ expression change_timer_function_usage._E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type change_timer_function_usage._cast_data; @@ ( -timer_setup(&_E->_timer, _callback, 0); +setup_timer(&_E->_timer, _callback, (_cast_data)_E); | -timer_setup(&_E._timer, _callback, 0); +setup_timer(&_E._timer, _callback, (_cast_data)&_E); ) // If we fixed a callback from a .function assignment, fix the // assignment cast now. @change_timer_function_assignment depends on change_timer_function_usage && (change_callback_handle_cast || change_callback_handle_cast_no_arg || change_callback_handle_arg)@ expression change_timer_function_usage._E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type _cast_func; typedef TIMER_FUNC_TYPE; @@ ( _E->_timer.function = -_callback +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -&_callback +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -(_cast_func)_callback; +(TIMER_FUNC_TYPE)_callback ; | _E->_timer.function = -(_cast_func)&_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -&_callback; +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -(_cast_func)_callback +(TIMER_FUNC_TYPE)_callback ; | _E._timer.function = -(_cast_func)&_callback +(TIMER_FUNC_TYPE)_callback ; ) // Sometimes timer functions are called directly. Replace matched args. @change_timer_function_calls depends on change_timer_function_usage && (change_callback_handle_cast || change_callback_handle_cast_no_arg || change_callback_handle_arg)@ expression _E; identifier change_timer_function_usage._timer; identifier change_timer_function_usage._callback; type _cast_data; @@ _callback( ( -(_cast_data)_E +&_E->_timer | -(_cast_data)&_E +&_E._timer | -_E +&_E->_timer ) ) // If a timer has been configured without a data argument, it can be // converted without regard to the callback argument, since it is unused. @match_timer_function_unused_data@ expression _E; identifier _timer; identifier _callback; @@ ( -setup_timer(&_E->_timer, _callback, 0); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, 0L); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E->_timer, _callback, 0UL); +timer_setup(&_E->_timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0L); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_E._timer, _callback, 0UL); +timer_setup(&_E._timer, _callback, 0); | -setup_timer(&_timer, _callback, 0); +timer_setup(&_timer, _callback, 0); | -setup_timer(&_timer, _callback, 0L); +timer_setup(&_timer, _callback, 0); | -setup_timer(&_timer, _callback, 0UL); +timer_setup(&_timer, _callback, 0); | -setup_timer(_timer, _callback, 0); +timer_setup(_timer, _callback, 0); | -setup_timer(_timer, _callback, 0L); +timer_setup(_timer, _callback, 0); | -setup_timer(_timer, _callback, 0UL); +timer_setup(_timer, _callback, 0); ) @change_callback_unused_data depends on match_timer_function_unused_data@ identifier match_timer_function_unused_data._callback; type _origtype; identifier _origarg; @@ void _callback( -_origtype _origarg +struct timer_list *unused ) { ... when != _origarg } Signed-off-by: Kees Cook --- drivers/input/joystick/db9.c | 6 +++--- drivers/input/joystick/gamecon.c | 6 +++--- drivers/input/joystick/turbografx.c | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers/input/joystick') diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c index f4ad83eab67f..de0dd4756c84 100644 --- a/drivers/input/joystick/db9.c +++ b/drivers/input/joystick/db9.c @@ -364,9 +364,9 @@ static int db9_saturn(int mode, struct parport *port, struct input_dev *devs[]) return 0; } -static void db9_timer(unsigned long private) +static void db9_timer(struct timer_list *t) { - struct db9 *db9 = (void *) private; + struct db9 *db9 = from_timer(db9, t, timer); struct parport *port = db9->pd->port; struct input_dev *dev = db9->dev[0]; struct input_dev *dev2 = db9->dev[1]; @@ -609,7 +609,7 @@ static void db9_attach(struct parport *pp) db9->pd = pd; db9->mode = mode; db9->parportno = pp->number; - setup_timer(&db9->timer, db9_timer, (long)db9); + timer_setup(&db9->timer, db9_timer, 0); for (i = 0; i < (min(db9_mode->n_pads, DB9_MAX_DEVICES)); i++) { diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c index ca734ea97e53..2ffb2e8bdc3b 100644 --- a/drivers/input/joystick/gamecon.c +++ b/drivers/input/joystick/gamecon.c @@ -743,9 +743,9 @@ static void gc_psx_process_packet(struct gc *gc) * gc_timer() initiates reads of console pads data. */ -static void gc_timer(unsigned long private) +static void gc_timer(struct timer_list *t) { - struct gc *gc = (void *) private; + struct gc *gc = from_timer(gc, t, timer); /* * N64 pads - must be read first, any read confuses them for 200 us @@ -974,7 +974,7 @@ static void gc_attach(struct parport *pp) mutex_init(&gc->mutex); gc->pd = pd; gc->parportno = pp->number; - setup_timer(&gc->timer, gc_timer, (long) gc); + timer_setup(&gc->timer, gc_timer, 0); for (i = 0; i < n_pads && i < GC_MAX_DEVICES; i++) { if (!pads[i]) diff --git a/drivers/input/joystick/turbografx.c b/drivers/input/joystick/turbografx.c index a1fdc75a438d..e2685753e460 100644 --- a/drivers/input/joystick/turbografx.c +++ b/drivers/input/joystick/turbografx.c @@ -89,9 +89,9 @@ static struct tgfx { * tgfx_timer() reads and analyzes TurboGraFX joystick data. */ -static void tgfx_timer(unsigned long private) +static void tgfx_timer(struct timer_list *t) { - struct tgfx *tgfx = (void *) private; + struct tgfx *tgfx = from_timer(tgfx, t, timer); struct input_dev *dev; int data1, data2, i; @@ -200,7 +200,7 @@ static void tgfx_attach(struct parport *pp) mutex_init(&tgfx->sem); tgfx->pd = pd; tgfx->parportno = pp->number; - setup_timer(&tgfx->timer, tgfx_timer, (long)tgfx); + timer_setup(&tgfx->timer, tgfx_timer, 0); for (i = 0; i < n_devs; i++) { if (n_buttons[i] < 1) -- cgit v1.2.3 From 68ef4836cd3ca283b89843d6ad603ce258ba087d Mon Sep 17 00:00:00 2001 From: Marcus Folkesson Date: Sat, 17 Mar 2018 11:00:58 -0700 Subject: Input: pxrc - new driver for PhoenixRC Flight Controller Adapter This driver let you plug in your RC controller to the adapter and use it as input device in various RC simulators. Signed-off-by: Marcus Folkesson Signed-off-by: Dmitry Torokhov --- Documentation/input/devices/pxrc.rst | 57 +++++++ drivers/input/joystick/Kconfig | 10 ++ drivers/input/joystick/Makefile | 1 + drivers/input/joystick/pxrc.c | 303 +++++++++++++++++++++++++++++++++++ 4 files changed, 371 insertions(+) create mode 100644 Documentation/input/devices/pxrc.rst create mode 100644 drivers/input/joystick/pxrc.c (limited to 'drivers/input/joystick') diff --git a/Documentation/input/devices/pxrc.rst b/Documentation/input/devices/pxrc.rst new file mode 100644 index 000000000000..ca11f646bae8 --- /dev/null +++ b/Documentation/input/devices/pxrc.rst @@ -0,0 +1,57 @@ +======================================================= +pxrc - PhoenixRC Flight Controller Adapter +======================================================= + +:Author: Marcus Folkesson + +This driver let you use your own RC controller plugged into the +adapter that comes with PhoenixRC [1]_ or other compatible adapters. + +The adapter supports 7 analog channels and 1 digital input switch. + +Notes +===== + +Many RC controllers is able to configure which stick goes to which channel. +This is also configurable in most simulators, so a matching is not necessary. + +The driver is generating the following input event for analog channels: + ++---------+----------------+ +| Channel | Event | ++=========+================+ +| 1 | ABS_X | ++---------+----------------+ +| 2 | ABS_Y | ++---------+----------------+ +| 3 | ABS_RX | ++---------+----------------+ +| 4 | ABS_RY | ++---------+----------------+ +| 5 | ABS_RUDDER | ++---------+----------------+ +| 6 | ABS_THROTTLE | ++---------+----------------+ +| 7 | ABS_MISC | ++---------+----------------+ + +The digital input switch is generated as an `BTN_A` event. + +Manual Testing +============== + +To test this driver's functionality you may use `input-event` which is part of +the `input layer utilities` suite [2]_. + +For example:: + + > modprobe pxrc + > input-events + +To print all input events from input `devnr`. + +References +========== + +.. [1] http://www.phoenix-sim.com/ +.. [2] https://www.kraxel.org/cgit/input/ diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig index f3c2f6ea8b44..9591fc04a8ab 100644 --- a/drivers/input/joystick/Kconfig +++ b/drivers/input/joystick/Kconfig @@ -351,4 +351,14 @@ config JOYSTICK_PSXPAD_SPI_FF To drive rumble motor a dedicated power supply is required. +config JOYSTICK_PXRC + tristate "PhoenixRC Flight Controller Adapter" + depends on USB_ARCH_HAS_HCD + select USB + help + Say Y here if you want to use the PhoenixRC Flight Controller Adapter. + + To compile this driver as a module, choose M here: the + module will be called pxrc. + endif diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile index 67651efda2e1..dd0492ebbed7 100644 --- a/drivers/input/joystick/Makefile +++ b/drivers/input/joystick/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o obj-$(CONFIG_JOYSTICK_MAPLE) += maplecontrol.o obj-$(CONFIG_JOYSTICK_PSXPAD_SPI) += psxpad-spi.o +obj-$(CONFIG_JOYSTICK_PXRC) += pxrc.o obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o diff --git a/drivers/input/joystick/pxrc.c b/drivers/input/joystick/pxrc.c new file mode 100644 index 000000000000..07a0dbd3ced2 --- /dev/null +++ b/drivers/input/joystick/pxrc.c @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Phoenix RC Flight Controller Adapter + * + * Copyright (C) 2018 Marcus Folkesson + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PXRC_VENDOR_ID (0x1781) +#define PXRC_PRODUCT_ID (0x0898) + +static const struct usb_device_id pxrc_table[] = { + { USB_DEVICE(PXRC_VENDOR_ID, PXRC_PRODUCT_ID) }, + { } +}; +MODULE_DEVICE_TABLE(usb, pxrc_table); + +struct pxrc { + struct input_dev *input; + struct usb_device *udev; + struct usb_interface *intf; + struct urb *urb; + struct mutex pm_mutex; + bool is_open; + __u8 epaddr; + char phys[64]; + unsigned char *data; + size_t bsize; +}; + +static void pxrc_usb_irq(struct urb *urb) +{ + struct pxrc *pxrc = urb->context; + int error; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ETIME: + /* this urb is timing out */ + dev_dbg(&pxrc->intf->dev, + "%s - urb timed out - was the device unplugged?\n", + __func__); + return; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + case -EPIPE: + /* this urb is terminated, clean up */ + dev_dbg(&pxrc->intf->dev, "%s - urb shutting down with status: %d\n", + __func__, urb->status); + return; + default: + dev_dbg(&pxrc->intf->dev, "%s - nonzero urb status received: %d\n", + __func__, urb->status); + goto exit; + } + + if (urb->actual_length == 8) { + input_report_abs(pxrc->input, ABS_X, pxrc->data[0]); + input_report_abs(pxrc->input, ABS_Y, pxrc->data[2]); + input_report_abs(pxrc->input, ABS_RX, pxrc->data[3]); + input_report_abs(pxrc->input, ABS_RY, pxrc->data[4]); + input_report_abs(pxrc->input, ABS_RUDDER, pxrc->data[5]); + input_report_abs(pxrc->input, ABS_THROTTLE, pxrc->data[6]); + input_report_abs(pxrc->input, ABS_MISC, pxrc->data[7]); + + input_report_key(pxrc->input, BTN_A, pxrc->data[1]); + } + +exit: + /* Resubmit to fetch new fresh URBs */ + error = usb_submit_urb(urb, GFP_ATOMIC); + if (error && error != -EPERM) + dev_err(&pxrc->intf->dev, + "%s - usb_submit_urb failed with result: %d", + __func__, error); +} + +static int pxrc_open(struct input_dev *input) +{ + struct pxrc *pxrc = input_get_drvdata(input); + int retval; + + mutex_lock(&pxrc->pm_mutex); + retval = usb_submit_urb(pxrc->urb, GFP_KERNEL); + if (retval) { + dev_err(&pxrc->intf->dev, + "%s - usb_submit_urb failed, error: %d\n", + __func__, retval); + retval = -EIO; + goto out; + } + + pxrc->is_open = true; + +out: + mutex_unlock(&pxrc->pm_mutex); + return retval; +} + +static void pxrc_close(struct input_dev *input) +{ + struct pxrc *pxrc = input_get_drvdata(input); + + mutex_lock(&pxrc->pm_mutex); + usb_kill_urb(pxrc->urb); + pxrc->is_open = false; + mutex_unlock(&pxrc->pm_mutex); +} + +static int pxrc_usb_init(struct pxrc *pxrc) +{ + struct usb_endpoint_descriptor *epirq; + unsigned int pipe; + int retval; + + /* Set up the endpoint information */ + /* This device only has an interrupt endpoint */ + retval = usb_find_common_endpoints(pxrc->intf->cur_altsetting, + NULL, NULL, &epirq, NULL); + if (retval) { + dev_err(&pxrc->intf->dev, + "Could not find endpoint\n"); + goto error; + } + + pxrc->bsize = usb_endpoint_maxp(epirq); + pxrc->epaddr = epirq->bEndpointAddress; + pxrc->data = devm_kmalloc(&pxrc->intf->dev, pxrc->bsize, GFP_KERNEL); + if (!pxrc->data) { + retval = -ENOMEM; + goto error; + } + + usb_set_intfdata(pxrc->intf, pxrc); + usb_make_path(pxrc->udev, pxrc->phys, sizeof(pxrc->phys)); + strlcat(pxrc->phys, "/input0", sizeof(pxrc->phys)); + + pxrc->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pxrc->urb) { + retval = -ENOMEM; + goto error; + } + + pipe = usb_rcvintpipe(pxrc->udev, pxrc->epaddr), + usb_fill_int_urb(pxrc->urb, pxrc->udev, pipe, pxrc->data, pxrc->bsize, + pxrc_usb_irq, pxrc, 1); + +error: + return retval; + + +} + +static int pxrc_input_init(struct pxrc *pxrc) +{ + pxrc->input = devm_input_allocate_device(&pxrc->intf->dev); + if (pxrc->input == NULL) { + dev_err(&pxrc->intf->dev, "couldn't allocate input device\n"); + return -ENOMEM; + } + + pxrc->input->name = "PXRC Flight Controller Adapter"; + pxrc->input->phys = pxrc->phys; + usb_to_input_id(pxrc->udev, &pxrc->input->id); + + pxrc->input->open = pxrc_open; + pxrc->input->close = pxrc_close; + + input_set_capability(pxrc->input, EV_KEY, BTN_A); + input_set_abs_params(pxrc->input, ABS_X, 0, 255, 0, 0); + input_set_abs_params(pxrc->input, ABS_Y, 0, 255, 0, 0); + input_set_abs_params(pxrc->input, ABS_RX, 0, 255, 0, 0); + input_set_abs_params(pxrc->input, ABS_RY, 0, 255, 0, 0); + input_set_abs_params(pxrc->input, ABS_RUDDER, 0, 255, 0, 0); + input_set_abs_params(pxrc->input, ABS_THROTTLE, 0, 255, 0, 0); + input_set_abs_params(pxrc->input, ABS_MISC, 0, 255, 0, 0); + + input_set_drvdata(pxrc->input, pxrc); + + return input_register_device(pxrc->input); +} + +static int pxrc_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct pxrc *pxrc; + int retval; + + pxrc = devm_kzalloc(&intf->dev, sizeof(*pxrc), GFP_KERNEL); + if (!pxrc) + return -ENOMEM; + + mutex_init(&pxrc->pm_mutex); + pxrc->udev = usb_get_dev(interface_to_usbdev(intf)); + pxrc->intf = intf; + + retval = pxrc_usb_init(pxrc); + if (retval) + goto error; + + retval = pxrc_input_init(pxrc); + if (retval) + goto err_free_urb; + + return 0; + +err_free_urb: + usb_free_urb(pxrc->urb); + +error: + return retval; +} + +static void pxrc_disconnect(struct usb_interface *intf) +{ + struct pxrc *pxrc = usb_get_intfdata(intf); + + usb_free_urb(pxrc->urb); + usb_set_intfdata(intf, NULL); +} + +static int pxrc_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct pxrc *pxrc = usb_get_intfdata(intf); + + mutex_lock(&pxrc->pm_mutex); + if (pxrc->is_open) + usb_kill_urb(pxrc->urb); + mutex_unlock(&pxrc->pm_mutex); + + return 0; +} + +static int pxrc_resume(struct usb_interface *intf) +{ + struct pxrc *pxrc = usb_get_intfdata(intf); + int retval = 0; + + mutex_lock(&pxrc->pm_mutex); + if (pxrc->is_open && usb_submit_urb(pxrc->urb, GFP_KERNEL) < 0) + retval = -EIO; + + mutex_unlock(&pxrc->pm_mutex); + return retval; +} + +static int pxrc_pre_reset(struct usb_interface *intf) +{ + struct pxrc *pxrc = usb_get_intfdata(intf); + + mutex_lock(&pxrc->pm_mutex); + usb_kill_urb(pxrc->urb); + return 0; +} + +static int pxrc_post_reset(struct usb_interface *intf) +{ + struct pxrc *pxrc = usb_get_intfdata(intf); + int retval = 0; + + if (pxrc->is_open && usb_submit_urb(pxrc->urb, GFP_KERNEL) < 0) + retval = -EIO; + + mutex_unlock(&pxrc->pm_mutex); + + return retval; +} + +static int pxrc_reset_resume(struct usb_interface *intf) +{ + return pxrc_resume(intf); +} + +static struct usb_driver pxrc_driver = { + .name = "pxrc", + .probe = pxrc_probe, + .disconnect = pxrc_disconnect, + .id_table = pxrc_table, + .suspend = pxrc_suspend, + .resume = pxrc_resume, + .pre_reset = pxrc_pre_reset, + .post_reset = pxrc_post_reset, + .reset_resume = pxrc_reset_resume, +}; + +module_usb_driver(pxrc_driver); + +MODULE_AUTHOR("Marcus Folkesson "); +MODULE_DESCRIPTION("PhoenixRC Flight Controller Adapter"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3