diff options
Diffstat (limited to 'drivers/input/joystick')
-rw-r--r-- | drivers/input/joystick/Kconfig | 11 | ||||
-rw-r--r-- | drivers/input/joystick/Makefile | 5 | ||||
-rw-r--r-- | drivers/input/joystick/fsia6b.c | 231 | ||||
-rw-r--r-- | drivers/input/joystick/psxpad-spi.c | 64 | ||||
-rw-r--r-- | drivers/input/joystick/sidewinder.c | 2 |
5 files changed, 279 insertions, 34 deletions
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig index 72b932901d00..940b744639c7 100644 --- a/drivers/input/joystick/Kconfig +++ b/drivers/input/joystick/Kconfig @@ -334,7 +334,6 @@ config JOYSTICK_MAPLE config JOYSTICK_PSXPAD_SPI tristate "PlayStation 1/2 joypads via SPI interface" depends on SPI - select INPUT_POLLDEV help Say Y here if you wish to connect PlayStation 1/2 joypads via SPI interface. @@ -362,4 +361,14 @@ config JOYSTICK_PXRC To compile this driver as a module, choose M here: the module will be called pxrc. +config JOYSTICK_FSIA6B + tristate "FlySky FS-iA6B RC Receiver" + select SERIO + help + Say Y here if you use a FlySky FS-i6 RC remote control along with the + FS-iA6B RC receiver as a joystick input device. + + To compile this driver as a module, choose M here: the + module will be called fsia6b. + endif diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile index dd0492ebbed7..8656023f6ef5 100644 --- a/drivers/input/joystick/Makefile +++ b/drivers/input/joystick/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_JOYSTICK_AS5011) += as5011.o obj-$(CONFIG_JOYSTICK_ANALOG) += analog.o obj-$(CONFIG_JOYSTICK_COBRA) += cobra.o obj-$(CONFIG_JOYSTICK_DB9) += db9.o +obj-$(CONFIG_JOYSTICK_FSIA6B) += fsia6b.o obj-$(CONFIG_JOYSTICK_GAMECON) += gamecon.o obj-$(CONFIG_JOYSTICK_GF2K) += gf2k.o obj-$(CONFIG_JOYSTICK_GRIP) += grip.o @@ -23,7 +24,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_PXRC) += pxrc.o obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o @@ -32,7 +33,7 @@ obj-$(CONFIG_JOYSTICK_TMDC) += tmdc.o obj-$(CONFIG_JOYSTICK_TURBOGRAFX) += turbografx.o obj-$(CONFIG_JOYSTICK_TWIDJOY) += twidjoy.o obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o +obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o -obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o diff --git a/drivers/input/joystick/fsia6b.c b/drivers/input/joystick/fsia6b.c new file mode 100644 index 000000000000..e78c4c768990 --- /dev/null +++ b/drivers/input/joystick/fsia6b.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * FS-iA6B iBus RC receiver driver + * + * This driver provides all 14 channels of the FlySky FS-ia6B RC receiver + * as analog values. + * + * Additionally, the channels can be converted to discrete switch values. + * By default, it is configured for the offical FS-i6 remote control. + * If you use a different hardware configuration, you can configure it + * using the `switch_config` parameter. + */ + +#include <linux/device.h> +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/serio.h> +#include <linux/slab.h> +#include <linux/types.h> + +#define DRIVER_DESC "FS-iA6B iBus RC receiver" + +MODULE_AUTHOR("Markus Koch <markus@notsyncing.net>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +#define IBUS_SERVO_COUNT 14 + +static char *switch_config = "00000022320000"; +module_param(switch_config, charp, 0444); +MODULE_PARM_DESC(switch_config, + "Amount of switch positions per channel (14 characters, 0-3)"); + +static int fsia6b_axes[IBUS_SERVO_COUNT] = { + ABS_X, ABS_Y, + ABS_Z, ABS_RX, + ABS_RY, ABS_RZ, + ABS_HAT0X, ABS_HAT0Y, + ABS_HAT1X, ABS_HAT1Y, + ABS_HAT2X, ABS_HAT2Y, + ABS_HAT3X, ABS_HAT3Y +}; + +enum ibus_state { SYNC, COLLECT, PROCESS }; + +struct ibus_packet { + enum ibus_state state; + + int offset; + u16 ibuf; + u16 channel[IBUS_SERVO_COUNT]; +}; + +struct fsia6b { + struct input_dev *dev; + struct ibus_packet packet; + + char phys[32]; +}; + +static irqreturn_t fsia6b_serio_irq(struct serio *serio, + unsigned char data, unsigned int flags) +{ + struct fsia6b *fsia6b = serio_get_drvdata(serio); + int i; + int sw_state; + int sw_id = BTN_0; + + fsia6b->packet.ibuf = (data << 8) | ((fsia6b->packet.ibuf >> 8) & 0xFF); + + switch (fsia6b->packet.state) { + case SYNC: + if (fsia6b->packet.ibuf == 0x4020) + fsia6b->packet.state = COLLECT; + break; + + case COLLECT: + fsia6b->packet.state = PROCESS; + break; + + case PROCESS: + fsia6b->packet.channel[fsia6b->packet.offset] = + fsia6b->packet.ibuf; + fsia6b->packet.offset++; + + if (fsia6b->packet.offset == IBUS_SERVO_COUNT) { + fsia6b->packet.offset = 0; + fsia6b->packet.state = SYNC; + for (i = 0; i < IBUS_SERVO_COUNT; ++i) { + input_report_abs(fsia6b->dev, fsia6b_axes[i], + fsia6b->packet.channel[i]); + + sw_state = 0; + if (fsia6b->packet.channel[i] > 1900) + sw_state = 1; + else if (fsia6b->packet.channel[i] < 1100) + sw_state = 2; + + switch (switch_config[i]) { + case '3': + input_report_key(fsia6b->dev, + sw_id++, + sw_state == 0); + /* fall-through */ + case '2': + input_report_key(fsia6b->dev, + sw_id++, + sw_state == 1); + /* fall-through */ + case '1': + input_report_key(fsia6b->dev, + sw_id++, + sw_state == 2); + } + } + input_sync(fsia6b->dev); + } else { + fsia6b->packet.state = COLLECT; + } + break; + } + + return IRQ_HANDLED; +} + +static int fsia6b_serio_connect(struct serio *serio, struct serio_driver *drv) +{ + struct fsia6b *fsia6b; + struct input_dev *input_dev; + int err; + int i, j; + int sw_id = 0; + + fsia6b = kzalloc(sizeof(*fsia6b), GFP_KERNEL); + if (!fsia6b) + return -ENOMEM; + + fsia6b->packet.ibuf = 0; + fsia6b->packet.offset = 0; + fsia6b->packet.state = SYNC; + + serio_set_drvdata(serio, fsia6b); + + input_dev = input_allocate_device(); + if (!input_dev) { + err = -ENOMEM; + goto fail1; + } + fsia6b->dev = input_dev; + + snprintf(fsia6b->phys, sizeof(fsia6b->phys), "%s/input0", serio->phys); + + input_dev->name = DRIVER_DESC; + input_dev->phys = fsia6b->phys; + input_dev->id.bustype = BUS_RS232; + input_dev->id.vendor = SERIO_FSIA6B; + input_dev->id.product = serio->id.id; + input_dev->id.version = 0x0100; + input_dev->dev.parent = &serio->dev; + + for (i = 0; i < IBUS_SERVO_COUNT; i++) + input_set_abs_params(input_dev, fsia6b_axes[i], + 1000, 2000, 2, 2); + + /* Register switch configuration */ + for (i = 0; i < IBUS_SERVO_COUNT; i++) { + if (switch_config[i] < '0' || switch_config[i] > '3') { + dev_err(&fsia6b->dev->dev, + "Invalid switch configuration supplied for fsia6b.\n"); + err = -EINVAL; + goto fail2; + } + + for (j = '1'; j <= switch_config[i]; j++) { + input_set_capability(input_dev, EV_KEY, BTN_0 + sw_id); + sw_id++; + } + } + + err = serio_open(serio, drv); + if (err) + goto fail2; + + err = input_register_device(fsia6b->dev); + if (err) + goto fail3; + + return 0; + +fail3: serio_close(serio); +fail2: input_free_device(input_dev); +fail1: serio_set_drvdata(serio, NULL); + kfree(fsia6b); + return err; +} + +static void fsia6b_serio_disconnect(struct serio *serio) +{ + struct fsia6b *fsia6b = serio_get_drvdata(serio); + + serio_close(serio); + serio_set_drvdata(serio, NULL); + input_unregister_device(fsia6b->dev); + kfree(fsia6b); +} + +static const struct serio_device_id fsia6b_serio_ids[] = { + { + .type = SERIO_RS232, + .proto = SERIO_FSIA6B, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, + { 0 } +}; + +MODULE_DEVICE_TABLE(serio, fsia6b_serio_ids); + +static struct serio_driver fsia6b_serio_drv = { + .driver = { + .name = "fsia6b" + }, + .description = DRIVER_DESC, + .id_table = fsia6b_serio_ids, + .interrupt = fsia6b_serio_irq, + .connect = fsia6b_serio_connect, + .disconnect = fsia6b_serio_disconnect +}; + +module_serio_driver(fsia6b_serio_drv) diff --git a/drivers/input/joystick/psxpad-spi.c b/drivers/input/joystick/psxpad-spi.c index 7eee1b0e360f..a32656064f39 100644 --- a/drivers/input/joystick/psxpad-spi.c +++ b/drivers/input/joystick/psxpad-spi.c @@ -22,7 +22,6 @@ #include <linux/kernel.h> #include <linux/device.h> #include <linux/input.h> -#include <linux/input-polldev.h> #include <linux/module.h> #include <linux/spi/spi.h> #include <linux/types.h> @@ -60,7 +59,7 @@ static const u8 PSX_CMD_ENABLE_MOTOR[] = { struct psxpad { struct spi_device *spi; - struct input_polled_dev *pdev; + struct input_dev *idev; char phys[0x20]; bool motor1enable; bool motor2enable; @@ -140,8 +139,7 @@ static void psxpad_set_motor_level(struct psxpad *pad, static int psxpad_spi_play_effect(struct input_dev *idev, void *data, struct ff_effect *effect) { - struct input_polled_dev *pdev = input_get_drvdata(idev); - struct psxpad *pad = pdev->private; + struct psxpad *pad = input_get_drvdata(idev); switch (effect->type) { case FF_RUMBLE: @@ -158,10 +156,9 @@ static int psxpad_spi_init_ff(struct psxpad *pad) { int err; - input_set_capability(pad->pdev->input, EV_FF, FF_RUMBLE); + input_set_capability(pad->idev, EV_FF, FF_RUMBLE); - err = input_ff_create_memless(pad->pdev->input, NULL, - psxpad_spi_play_effect); + err = input_ff_create_memless(pad->idev, NULL, psxpad_spi_play_effect); if (err) { dev_err(&pad->spi->dev, "input_ff_create_memless() failed: %d\n", err); @@ -189,24 +186,25 @@ static inline int psxpad_spi_init_ff(struct psxpad *pad) } #endif /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */ -static void psxpad_spi_poll_open(struct input_polled_dev *pdev) +static int psxpad_spi_poll_open(struct input_dev *input) { - struct psxpad *pad = pdev->private; + struct psxpad *pad = input_get_drvdata(input); pm_runtime_get_sync(&pad->spi->dev); + + return 0; } -static void psxpad_spi_poll_close(struct input_polled_dev *pdev) +static void psxpad_spi_poll_close(struct input_dev *input) { - struct psxpad *pad = pdev->private; + struct psxpad *pad = input_get_drvdata(input); pm_runtime_put_sync(&pad->spi->dev); } -static void psxpad_spi_poll(struct input_polled_dev *pdev) +static void psxpad_spi_poll(struct input_dev *input) { - struct psxpad *pad = pdev->private; - struct input_dev *input = pdev->input; + struct psxpad *pad = input_get_drvdata(input); u8 b_rsp3, b_rsp4; int err; @@ -284,7 +282,6 @@ static void psxpad_spi_poll(struct input_polled_dev *pdev) static int psxpad_spi_probe(struct spi_device *spi) { struct psxpad *pad; - struct input_polled_dev *pdev; struct input_dev *idev; int err; @@ -292,31 +289,26 @@ static int psxpad_spi_probe(struct spi_device *spi) if (!pad) return -ENOMEM; - pdev = input_allocate_polled_device(); - if (!pdev) { + idev = devm_input_allocate_device(&spi->dev); + if (!idev) { dev_err(&spi->dev, "failed to allocate input device\n"); return -ENOMEM; } /* input poll device settings */ - pad->pdev = pdev; + pad->idev = idev; pad->spi = spi; - pdev->private = pad; - pdev->open = psxpad_spi_poll_open; - pdev->close = psxpad_spi_poll_close; - pdev->poll = psxpad_spi_poll; - /* poll interval is about 60fps */ - pdev->poll_interval = 16; - pdev->poll_interval_min = 8; - pdev->poll_interval_max = 32; - /* input device settings */ - idev = pdev->input; + input_set_drvdata(idev, pad); + idev->name = "PlayStation 1/2 joypad"; snprintf(pad->phys, sizeof(pad->phys), "%s/input", dev_name(&spi->dev)); idev->id.bustype = BUS_SPI; + idev->open = psxpad_spi_poll_open; + idev->close = psxpad_spi_poll_close; + /* key/value map settings */ input_set_abs_params(idev, ABS_X, 0, 255, 0, 0); input_set_abs_params(idev, ABS_Y, 0, 255, 0, 0); @@ -354,11 +346,23 @@ static int psxpad_spi_probe(struct spi_device *spi) /* pad settings */ psxpad_set_motor_level(pad, 0, 0); + + err = input_setup_polling(idev, psxpad_spi_poll); + if (err) { + dev_err(&spi->dev, "failed to set up polling: %d\n", err); + return err; + } + + /* poll interval is about 60fps */ + input_set_poll_interval(idev, 16); + input_set_min_poll_interval(idev, 8); + input_set_max_poll_interval(idev, 32); + /* register input poll device */ - err = input_register_polled_device(pdev); + err = input_register_device(idev); if (err) { dev_err(&spi->dev, - "failed to register input poll device: %d\n", err); + "failed to register input device: %d\n", err); return err; } diff --git a/drivers/input/joystick/sidewinder.c b/drivers/input/joystick/sidewinder.c index 0284da874a2b..1777e68c9f02 100644 --- a/drivers/input/joystick/sidewinder.c +++ b/drivers/input/joystick/sidewinder.c @@ -223,7 +223,7 @@ static __u64 sw_get_bits(unsigned char *buf, int pos, int num, char bits) static void sw_init_digital(struct gameport *gameport) { - int seq[] = { 140, 140+725, 140+300, 0 }; + static const int seq[] = { 140, 140+725, 140+300, 0 }; unsigned long flags; int i, t; |