PSX pads can be connected directly to the SPI bus.
Signed-off-by: Tomohiro Yoshidomi <[email protected]>
---
drivers/input/joystick/Kconfig | 17 ++
drivers/input/joystick/Makefile | 1 +
drivers/input/joystick/psxpad-spi.c | 564 ++++++++++++++++++++++++++++++++++++
3 files changed, 582 insertions(+)
create mode 100644 drivers/input/joystick/psxpad-spi.c
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index 4215b538..bfbda643 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -330,4 +330,21 @@ config JOYSTICK_MAPLE
To compile this as a module choose M here: the module will be called
maplecontrol.
+config JOYSTICK_PSXPAD_SPI
+ tristate "PSX (Play Station 1/2) pad with SPI Bus Driver"
+ depends on SPI
+ select INPUT_POLLDEV
+ help
+ Say Y here if you connect PSX (PS1/2) pad with SPI Interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called psxpad-spi.
+
+config JOYSTICK_PSXPAD_SPI_FF
+ bool "PSX pad with SPI Bus rumble support"
+ depends on JOYSTICK_PSXPAD_SPI
+ select INPUT_FF_MEMLESS
+ help
+ Say Y here if you want to take advantage of PSX pad rumble features.
+
endif
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
index 92dc0de9..496fd56b 100644
--- a/drivers/input/joystick/Makefile
+++ b/drivers/input/joystick/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_JOYSTICK_INTERACT) += interact.o
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_SIDEWINDER) += sidewinder.o
obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o
obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o
diff --git a/drivers/input/joystick/psxpad-spi.c b/drivers/input/joystick/psxpad-spi.c
new file mode 100644
index 00000000..8236ba60
--- /dev/null
+++ b/drivers/input/joystick/psxpad-spi.c
@@ -0,0 +1,564 @@
+/*
+ * PSX (Play Station 1/2) pad (SPI Interface)
+ *
+ * Copyright (C) 2017 AZO <[email protected]>
+ * Licensed under the GPL-2 or later.
+ *
+ * PSX pad plug (not socket)
+ * 123 456 789
+ * (...|...|...)
+ *
+ * 1: DAT -> MISO (pullup with 1k owm to 3.3V)
+ * 2: CMD -> MOSI
+ * 3: 9V (for motor, if not use N.C.)
+ * 4: GND
+ * 5: 3.3V
+ * 6: Attention -> CS(SS)
+ * 7: SCK -> SCK
+ * 8: N.C.
+ * 9: ACK -> N.C.
+ */
+
+#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>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+
+//#define PSXPAD_ENABLE_ANALOG2
+
+#define PSXPAD_DEFAULT_SPI_DELAY 100
+#define PSXPAD_DEFAULT_INTERVAL 16
+#define PSXPAD_DEFAULT_INTERVAL_MIN 8
+#define PSXPAD_DEFAULT_INTERVAL_MAX 32
+#define PSXPAD_DEFAULT_INPUT_PHYSIZE 32
+
+#define REVERSE_BIT(x) ((((x) & 0x80) >> 7) | (((x) & 0x40) >> 5) | (((x) & 0x20) >> 3) | (((x) & 0x10) >> 1) | (((x) & 0x08) << 1) | (((x) & 0x04) << 3) | (((x) & 0x02) << 5) | (((x) & 0x01) << 7))
+
+enum {
+ PSXPAD_KEYSTATE_TYPE_DIGITAL = 0,
+ PSXPAD_KEYSTATE_TYPE_ANALOG1,
+ PSXPAD_KEYSTATE_TYPE_ANALOG2,
+ PSXPAD_KEYSTATE_TYPE_UNKNOWN
+};
+
+static const u8 PSX_CMD_INIT_PRESSURE[] = {0x01, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00};
+static const u8 PSX_CMD_ALL_PRESSURE[] = {0x01, 0x4F, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00};
+static const u8 PSX_CMD_POLL[] = {0x01, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static const u8 PSX_CMD_ENTER_CFG[] = {0x01, 0x43, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
+static const u8 PSX_CMD_EXIT_CFG[] = {0x01, 0x43, 0x00, 0x00, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A};
+static const u8 PSX_CMD_ENABLE_MOTOR[] = {0x01, 0x4D, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+static const u8 PSX_CMD_AD_MODE[] = {0x01, 0x44, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+struct psxpad {
+ struct spi_device *spi;
+ struct input_polled_dev *pdev;
+ struct input_dev *idev;
+ char phys[PSXPAD_DEFAULT_INPUT_PHYSIZE];
+ u16 spi_delay;
+ bool analog_mode;
+ bool mode_lock;
+ bool motor1enable;
+ bool motor2enable;
+ u8 motor1level;
+ u8 motor2level;
+
+ /* for suspend/resume */
+ bool sus_analog_mode;
+ bool sus_mode_lock;
+ bool sus_motor1enable;
+ bool sus_motor2enable;
+ u8 sus_motor1level;
+ u8 sus_motor2level;
+
+ u8 spi_speed;
+ u8 poolcmd[sizeof(PSX_CMD_POLL)];
+ u8 enablemotor[sizeof(PSX_CMD_ENABLE_MOTOR)];
+ u8 admode[sizeof(PSX_CMD_AD_MODE)];
+ u8 sendbuf[0x20] ____cacheline_aligned;
+ u8 response[sizeof(PSX_CMD_POLL)] ____cacheline_aligned;
+};
+
+static int psxpad_command(struct psxpad *pad, const u8 sendcmd[], u8 response[], const u8 sendcmdlen)
+{
+ struct spi_transfer xfers = {
+ .tx_buf = pad->sendbuf,
+ .rx_buf = response,
+ .len = sendcmdlen,
+ .delay_usecs = pad->spi_delay,
+ };
+ struct spi_message msg;
+ int err;
+ u8 loc;
+
+ spi_message_init(&msg);
+
+ for (loc = 0; loc < sendcmdlen; loc++)
+ pad->sendbuf[loc] = REVERSE_BIT(sendcmd[loc]);
+
+ err = spi_sync_transfer(pad->spi, &xfers, 1);
+ if (err) {
+ dev_err(&pad->idev->dev, "psxpad-spi: failed SPI xfers!!\n");
+ goto err_end;
+ }
+
+ for (loc = 0; loc < sendcmdlen; loc++)
+ response[loc] = REVERSE_BIT(response[loc]);
+
+ return 0;
+
+ err_end:
+
+ return err;
+}
+
+static void psxpad_setadmode(struct psxpad *pad, const bool analog_mode, const bool mode_lock)
+{
+ int err;
+
+ pad->analog_mode = analog_mode;
+ pad->mode_lock = mode_lock;
+
+ pad->admode[3] = pad->analog_mode ? 0x01 : 0x00;
+ pad->admode[4] = pad->mode_lock ? 0x03 : 0x00;
+
+ err = psxpad_command(pad, PSX_CMD_ENTER_CFG, pad->response, sizeof(PSX_CMD_ENTER_CFG));
+ if (err) {
+ dev_err(&pad->idev->dev, "psxpad-spi: setadmode: enter cfg failed!!\n");
+ return;
+ }
+ err = psxpad_command(pad, pad->admode, pad->response, sizeof(PSX_CMD_AD_MODE));
+ if (err) {
+ dev_err(&pad->idev->dev, "psxpad-spi: setadmode: set admode failed!!\n");
+ return;
+ }
+#ifdef PSXPAD_ENABLE_ANALOG2
+ err = psxpad_command(pad, PSX_CMD_INIT_PRESSURE, pad->response, sizeof(PSX_CMD_INIT_PRESSURE));
+ if (err) {
+ dev_err(&pad->idev->dev, "psxpad-spi: setadmode: init pressure failed!!\n");
+ return;
+ }
+ err = psxpad_command(pad, PSX_CMD_ALL_PRESSURE, pad->response, sizeof(PSX_CMD_ALL_PRESSURE));
+ if (err) {
+ dev_err(&pad->idev->dev, "psxpad-spi: setadmode: set all pressure failed!!\n");
+ return;
+ }
+#endif /* PSXPAD_ENABLE_ANALOG2 */
+ err = psxpad_command(pad, PSX_CMD_EXIT_CFG, pad->response, sizeof(PSX_CMD_EXIT_CFG));
+ if (err) {
+ dev_err(&pad->idev->dev, "psxpad-spi: setadmode: exit config failed!!\n");
+ return;
+ }
+}
+
+#ifdef CONFIG_JOYSTICK_PSXPAD_SPI_FF
+static void psxpad_setenablemotor(struct psxpad *pad, const bool motor1enable, const bool motor2enable)
+{
+ int err;
+
+ pad->motor1enable = motor1enable;
+ pad->motor2enable = motor2enable;
+
+ pad->enablemotor[3] = pad->motor1enable ? 0x00 : 0xFF;
+ pad->enablemotor[4] = pad->motor2enable ? 0x01 : 0xFF;
+
+ err = psxpad_command(pad, PSX_CMD_ENTER_CFG, pad->response, sizeof(PSX_CMD_ENTER_CFG));
+ if (err) {
+ dev_err(&pad->idev->dev, "psxpad-spi: setenablemotor: enter cfg failed!!\n");
+ return;
+ }
+ err = psxpad_command(pad, pad->enablemotor, pad->response, sizeof(PSX_CMD_ENABLE_MOTOR));
+ if (err) {
+ dev_err(&pad->idev->dev, "psxpad-spi: setenablemotor: enable motor failed!!\n");
+ return;
+ }
+ err = psxpad_command(pad, PSX_CMD_EXIT_CFG, pad->response, sizeof(PSX_CMD_EXIT_CFG));
+ if (err) {
+ dev_err(&pad->idev->dev, "psxpad-spi: setenablemotor: exit config failed!!\n");
+ return;
+ }
+}
+
+static void psxpad_setmotorlevel(struct psxpad *pad, const u8 motor1level, const u8 motor2level)
+{
+ pad->motor1level = motor1level ? 0xFF : 0x00;
+ pad->motor2level = motor2level;
+
+ pad->poolcmd[3] = pad->motor1level;
+ pad->poolcmd[4] = pad->motor2level;
+}
+#else /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */
+static void psxpad_setenablemotor(struct psxpad *pad, const bool motor1enable, const bool motor2enable) { }
+
+static void psxpad_setmotorlevel(struct psxpad *pad, const u8 motor1level, const u8 motor2level) { }
+#endif /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */
+
+static void psxpad_spi_poll_open(struct input_polled_dev *pdev)
+{
+ struct psxpad *pad = pdev->private;
+
+ pm_runtime_get_sync(&pad->spi->dev);
+}
+
+static void psxpad_spi_poll_close(struct input_polled_dev *pdev)
+{
+ struct psxpad *pad = pdev->private;
+
+ pm_runtime_put_sync(&pad->spi->dev);
+}
+
+static void psxpad_spi_poll(struct input_polled_dev *pdev)
+{
+ struct psxpad *pad = pdev->private;
+ int err;
+
+ err = psxpad_command(pad, pad->poolcmd, pad->response, sizeof(PSX_CMD_POLL));
+ if (err) {
+ dev_err(&pad->idev->dev, "psxpad-spi: poll: poll cmd failed!!\n");
+ return;
+ }
+
+ switch (pad->response[1]) {
+#ifdef PSXPAD_ENABLE_ANALOG2
+ case 0x79: /* analog 2 */
+ input_report_abs(pad->idev, ABS_HAT0Y, pad->response[11]);
+ input_report_abs(pad->idev, ABS_HAT1Y, pad->response[12]);
+ input_report_abs(pad->idev, ABS_HAT0X, pad->response[10]);
+ input_report_abs(pad->idev, ABS_HAT1X, pad->response[9]);
+ input_report_abs(pad->idev, ABS_MISC, pad->response[13]);
+ input_report_abs(pad->idev, ABS_PRESSURE, pad->response[14]);
+ input_report_abs(pad->idev, ABS_BRAKE, pad->response[15]);
+ input_report_abs(pad->idev, ABS_THROTTLE, pad->response[16]);
+ input_report_abs(pad->idev, ABS_HAT2X, pad->response[17]);
+ input_report_abs(pad->idev, ABS_HAT3X, pad->response[18]);
+ input_report_abs(pad->idev, ABS_HAT2Y, pad->response[19]);
+ input_report_abs(pad->idev, ABS_HAT3Y, pad->response[20]);
+ input_report_abs(pad->idev, ABS_X, pad->response[7]);
+ input_report_abs(pad->idev, ABS_Y, pad->response[8]);
+ input_report_abs(pad->idev, ABS_RX, pad->response[5]);
+ input_report_abs(pad->idev, ABS_RY, pad->response[6]);
+ input_report_key(pad->idev, BTN_DPAD_UP, false);
+ input_report_key(pad->idev, BTN_DPAD_DOWN, false);
+ input_report_key(pad->idev, BTN_DPAD_LEFT, false);
+ input_report_key(pad->idev, BTN_DPAD_RIGHT, false);
+ input_report_key(pad->idev, BTN_X, false);
+ input_report_key(pad->idev, BTN_A, false);
+ input_report_key(pad->idev, BTN_B, false);
+ input_report_key(pad->idev, BTN_Y, false);
+ input_report_key(pad->idev, BTN_TL, false);
+ input_report_key(pad->idev, BTN_TR, false);
+ input_report_key(pad->idev, BTN_TL2, false);
+ input_report_key(pad->idev, BTN_TR2, false);
+ input_report_key(pad->idev, BTN_THUMBL, (pad->response[3] & 0x02U) ? false : true);
+ input_report_key(pad->idev, BTN_THUMBR, (pad->response[3] & 0x04U) ? false : true);
+ input_report_key(pad->idev, BTN_SELECT, (pad->response[3] & 0x01U) ? false : true);
+ input_report_key(pad->idev, BTN_START, (pad->response[3] & 0x08U) ? false : true);
+ break;
+#endif /* PSXPAD_ENABLE_ANALOG2 */
+
+ case 0x73: /* analog 1 */
+#ifdef PSXPAD_ENABLE_ANALOG2
+ input_report_abs(pad->idev, ABS_HAT0Y, 0);
+ input_report_abs(pad->idev, ABS_HAT1Y, 0);
+ input_report_abs(pad->idev, ABS_HAT0X, 0);
+ input_report_abs(pad->idev, ABS_HAT1X, 0);
+ input_report_abs(pad->idev, ABS_MISC, 0);
+ input_report_abs(pad->idev, ABS_PRESSURE, 0);
+ input_report_abs(pad->idev, ABS_BRAKE, 0);
+ input_report_abs(pad->idev, ABS_THROTTLE, 0);
+ input_report_abs(pad->idev, ABS_HAT2X, 0);
+ input_report_abs(pad->idev, ABS_HAT3X, 0);
+ input_report_abs(pad->idev, ABS_HAT2Y, 0);
+ input_report_abs(pad->idev, ABS_HAT3Y, 0);
+#endif /* PSXPAD_ENABLE_ANALOG2 */
+ input_report_abs(pad->idev, ABS_X, pad->response[7]);
+ input_report_abs(pad->idev, ABS_Y, pad->response[8]);
+ input_report_abs(pad->idev, ABS_RX, pad->response[5]);
+ input_report_abs(pad->idev, ABS_RY, pad->response[6]);
+ input_report_key(pad->idev, BTN_DPAD_UP, (pad->response[3] & 0x10U) ? false : true);
+ input_report_key(pad->idev, BTN_DPAD_DOWN, (pad->response[3] & 0x40U) ? false : true);
+ input_report_key(pad->idev, BTN_DPAD_LEFT, (pad->response[3] & 0x80U) ? false : true);
+ input_report_key(pad->idev, BTN_DPAD_RIGHT, (pad->response[3] & 0x20U) ? false : true);
+ input_report_key(pad->idev, BTN_X, (pad->response[4] & 0x10U) ? false : true);
+ input_report_key(pad->idev, BTN_A, (pad->response[4] & 0x20U) ? false : true);
+ input_report_key(pad->idev, BTN_B, (pad->response[4] & 0x40U) ? false : true);
+ input_report_key(pad->idev, BTN_Y, (pad->response[4] & 0x80U) ? false : true);
+ input_report_key(pad->idev, BTN_TL, (pad->response[4] & 0x04U) ? false : true);
+ input_report_key(pad->idev, BTN_TR, (pad->response[4] & 0x08U) ? false : true);
+ input_report_key(pad->idev, BTN_TL2, (pad->response[4] & 0x01U) ? false : true);
+ input_report_key(pad->idev, BTN_TR2, (pad->response[4] & 0x02U) ? false : true);
+ input_report_key(pad->idev, BTN_THUMBL, (pad->response[3] & 0x02U) ? false : true);
+ input_report_key(pad->idev, BTN_THUMBR, (pad->response[3] & 0x04U) ? false : true);
+ input_report_key(pad->idev, BTN_SELECT, (pad->response[3] & 0x01U) ? false : true);
+ input_report_key(pad->idev, BTN_START, (pad->response[3] & 0x08U) ? false : true);
+ break;
+
+ case 0x41: /* digital */
+#ifdef PSXPAD_ENABLE_ANALOG2
+ input_report_abs(pad->idev, ABS_HAT0Y, 0);
+ input_report_abs(pad->idev, ABS_HAT1Y, 0);
+ input_report_abs(pad->idev, ABS_HAT0X, 0);
+ input_report_abs(pad->idev, ABS_HAT1X, 0);
+ input_report_abs(pad->idev, ABS_MISC, 0);
+ input_report_abs(pad->idev, ABS_PRESSURE, 0);
+ input_report_abs(pad->idev, ABS_BRAKE, 0);
+ input_report_abs(pad->idev, ABS_THROTTLE, 0);
+ input_report_abs(pad->idev, ABS_HAT2X, 0);
+ input_report_abs(pad->idev, ABS_HAT3X, 0);
+ input_report_abs(pad->idev, ABS_HAT2Y, 0);
+ input_report_abs(pad->idev, ABS_HAT3Y, 0);
+#endif /* PSXPAD_ENABLE_ANALOG2 */
+ input_report_abs(pad->idev, ABS_X, 0x80);
+ input_report_abs(pad->idev, ABS_Y, 0x80);
+ input_report_abs(pad->idev, ABS_RX, 0x80);
+ input_report_abs(pad->idev, ABS_RY, 0x80);
+ input_report_key(pad->idev, BTN_DPAD_UP, (pad->response[3] & 0x10U) ? false : true);
+ input_report_key(pad->idev, BTN_DPAD_DOWN, (pad->response[3] & 0x40U) ? false : true);
+ input_report_key(pad->idev, BTN_DPAD_LEFT, (pad->response[3] & 0x80U) ? false : true);
+ input_report_key(pad->idev, BTN_DPAD_RIGHT, (pad->response[3] & 0x20U) ? false : true);
+ input_report_key(pad->idev, BTN_X, (pad->response[4] & 0x10U) ? false : true);
+ input_report_key(pad->idev, BTN_A, (pad->response[4] & 0x20U) ? false : true);
+ input_report_key(pad->idev, BTN_B, (pad->response[4] & 0x40U) ? false : true);
+ input_report_key(pad->idev, BTN_Y, (pad->response[4] & 0x80U) ? false : true);
+ input_report_key(pad->idev, BTN_TL, (pad->response[4] & 0x04U) ? false : true);
+ input_report_key(pad->idev, BTN_TR, (pad->response[4] & 0x08U) ? false : true);
+ input_report_key(pad->idev, BTN_TL2, (pad->response[4] & 0x01U) ? false : true);
+ input_report_key(pad->idev, BTN_TR2, (pad->response[4] & 0x02U) ? false : true);
+ input_report_key(pad->idev, BTN_THUMBL, false);
+ input_report_key(pad->idev, BTN_THUMBR, false);
+ input_report_key(pad->idev, BTN_SELECT, (pad->response[3] & 0x01U) ? false : true);
+ input_report_key(pad->idev, BTN_START, (pad->response[3] & 0x08U) ? false : true);
+ break;
+ }
+
+ input_sync(pad->idev);
+ psxpad_setenablemotor(pad, true, true);
+}
+
+#ifdef CONFIG_JOYSTICK_PSXPAD_SPI_FF
+static int psxpad_spi_ff(struct input_dev *idev, void *data, struct ff_effect *effect)
+{
+ struct psxpad *pad = idev->dev.platform_data;
+
+ switch (effect->type) {
+ case FF_RUMBLE:
+ psxpad_setmotorlevel(pad, (effect->u.rumble.weak_magnitude >> 8) & 0xFFU, (effect->u.rumble.strong_magnitude >> 8) & 0xFFU);
+ break;
+ }
+
+ return 0;
+}
+
+static int psxpad_spi_init_ff(struct psxpad *pad)
+{
+ int err;
+
+ input_set_capability(pad->idev, EV_FF, FF_RUMBLE);
+ err = input_ff_create_memless(pad->idev, NULL, psxpad_spi_ff);
+ if (err) {
+ dev_err(&pad->idev->dev, "psxpad-spi: init_ff: ff alloc failed!!\n");
+ err = -ENOMEM;
+ }
+
+ return err;
+}
+
+static void psxpad_spi_deinit_ff(struct psxpad *pad)
+{
+ input_ff_destroy(pad->idev);
+}
+#else /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */
+static inline int psxpad_spi_init_ff(struct psxpad *pad)
+{
+ return 0;
+}
+
+static void psxpad_spi_deinit_ff(struct psxpad *pad) { }
+#endif /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */
+
+static int psxpad_spi_probe(struct spi_device *spi)
+{
+ struct psxpad *pad;
+ struct input_polled_dev *pdev;
+ struct input_dev *idev;
+ int err, i;
+
+ pad = devm_kzalloc(&spi->dev, sizeof(struct psxpad), GFP_KERNEL);
+ if (!pad) {
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+ pdev = input_allocate_polled_device();
+ if (!pdev) {
+ dev_err(&spi->dev, "psxpad-spi: probe: pdev alloc failed!!\n");
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+ for (i = 0; i < sizeof(PSX_CMD_POLL); i++)
+ pad->poolcmd[i] = PSX_CMD_POLL[i];
+ for (i = 0; i < sizeof(PSX_CMD_ENABLE_MOTOR); i++)
+ pad->enablemotor[i] = PSX_CMD_ENABLE_MOTOR[i];
+ for (i = 0; i < sizeof(PSX_CMD_AD_MODE); i++)
+ pad->admode[i] = PSX_CMD_AD_MODE[i];
+ pad->spi_delay = PSXPAD_DEFAULT_SPI_DELAY;
+
+ /* input pool device settings */
+ pad->pdev = pdev;
+ pad->spi = spi;
+ pdev->private = pad;
+ pdev->open = psxpad_spi_poll_open;
+ pdev->close = psxpad_spi_poll_close;
+ pdev->poll = psxpad_spi_poll;
+ pdev->poll_interval = PSXPAD_DEFAULT_INTERVAL;
+ pdev->poll_interval_min = PSXPAD_DEFAULT_INTERVAL_MIN;
+ pdev->poll_interval_max = PSXPAD_DEFAULT_INTERVAL_MAX;
+
+ /* input device settings */
+ idev = pdev->input;
+ pad->idev = idev;
+ idev->name = "PSX (PS1/2) pad";
+ snprintf(pad->phys, sizeof(pad->phys), "%s/input", dev_name(&spi->dev));
+ idev->id.bustype = BUS_SPI;
+ idev->dev.parent = &spi->dev;
+ idev->dev.platform_data = pad;
+
+ /* 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);
+ input_set_abs_params(idev, ABS_RX, 0, 255, 0, 0);
+ input_set_abs_params(idev, ABS_RY, 0, 255, 0, 0);
+#ifdef PSXPAD_ENABLE_ANALOG2
+ input_set_abs_params(idev, ABS_HAT0Y, 0, 255, 0, 0); /* up */
+ input_set_abs_params(idev, ABS_HAT1Y, 0, 255, 0, 0); /* down */
+ input_set_abs_params(idev, ABS_HAT0X, 0, 255, 0, 0); /* left */
+ input_set_abs_params(idev, ABS_HAT1X, 0, 255, 0, 0); /* right */
+ input_set_abs_params(idev, ABS_MISC, 0, 255, 0, 0);
+ input_set_abs_params(idev, ABS_PRESSURE, 0, 255, 0, 0);
+ input_set_abs_params(idev, ABS_BRAKE, 0, 255, 0, 0);
+ input_set_abs_params(idev, ABS_THROTTLE, 0, 255, 0, 0);
+ input_set_abs_params(idev, ABS_HAT2X, 0, 255, 0, 0); /* L1 */
+ input_set_abs_params(idev, ABS_HAT3X, 0, 255, 0, 0); /* R1 */
+ input_set_abs_params(idev, ABS_HAT2Y, 0, 255, 0, 0); /* L2 */
+ input_set_abs_params(idev, ABS_HAT3Y, 0, 255, 0, 0); /* R2 */
+#endif /* PSXPAD_ENABLE_ANALOG2 */
+ input_set_capability(idev, EV_KEY, BTN_DPAD_UP);
+ input_set_capability(idev, EV_KEY, BTN_DPAD_DOWN);
+ input_set_capability(idev, EV_KEY, BTN_DPAD_LEFT);
+ input_set_capability(idev, EV_KEY, BTN_DPAD_RIGHT);
+ input_set_capability(idev, EV_KEY, BTN_A);
+ input_set_capability(idev, EV_KEY, BTN_B);
+ input_set_capability(idev, EV_KEY, BTN_X);
+ input_set_capability(idev, EV_KEY, BTN_Y);
+ input_set_capability(idev, EV_KEY, BTN_TL);
+ input_set_capability(idev, EV_KEY, BTN_TR);
+ input_set_capability(idev, EV_KEY, BTN_TL2);
+ input_set_capability(idev, EV_KEY, BTN_TR2);
+ input_set_capability(idev, EV_KEY, BTN_THUMBL);
+ input_set_capability(idev, EV_KEY, BTN_THUMBR);
+ input_set_capability(idev, EV_KEY, BTN_SELECT);
+ input_set_capability(idev, EV_KEY, BTN_START);
+
+ /* force feedback */
+ err = psxpad_spi_init_ff(pad);
+ if (err) {
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ /* SPI settings */
+ spi->mode = SPI_MODE_3;
+ spi->bits_per_word = 8;
+ /* (PSX pad might be possible works 250kHz/500kHz) */
+ spi->master->min_speed_hz = 125000;
+ spi->master->max_speed_hz = 125000;
+ spi_setup(spi);
+
+ /* pad settings */
+ psxpad_setmotorlevel(pad, 0, 0);
+
+ /* register input pool device */
+ err = input_register_polled_device(pdev);
+ if (err) {
+ dev_err(&spi->dev, "psxpad-spi: probe: failed register!!\n");
+ input_free_polled_device(pdev);
+ goto err_free_mem;
+ }
+
+ pm_runtime_enable(&spi->dev);
+
+ return 0;
+
+ err_free_mem:
+ if (pdev) {
+ psxpad_spi_deinit_ff(pad);
+ input_free_polled_device(pdev);
+ }
+ devm_kfree(&spi->dev, pad);
+
+ return err;
+}
+
+
+static int psxpad_spi_remove(struct spi_device *spi)
+{
+ return 0;
+}
+
+static int __maybe_unused psxpad_spi_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct psxpad *pad = spi_get_drvdata(spi);
+
+ pad->sus_analog_mode = pad->analog_mode;
+ pad->sus_mode_lock = pad->mode_lock;
+ pad->sus_motor1enable = pad->motor1enable;
+ pad->sus_motor2enable = pad->motor2enable;
+ pad->sus_motor1level = pad->motor1level;
+ pad->sus_motor2level = pad->motor2level;
+
+ psxpad_setadmode(pad, false, false);
+ psxpad_setmotorlevel(pad, 0, 0);
+ psxpad_setenablemotor(pad, false, false);
+
+ return 0;
+}
+
+static int __maybe_unused psxpad_spi_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct psxpad *pad = spi_get_drvdata(spi);
+
+ psxpad_setadmode(pad, pad->sus_analog_mode, pad->sus_mode_lock);
+ psxpad_setmotorlevel(pad, pad->sus_motor1enable, pad->sus_motor2enable);
+ psxpad_setenablemotor(pad, pad->sus_motor1level, pad->sus_motor2level);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(psxpad_spi_pm, psxpad_spi_suspend, psxpad_spi_resume);
+
+static const struct spi_device_id psxpad_spi_id[] = {
+ { "psxpad-spi", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, psxpad_spi_id);
+
+static struct spi_driver psxpad_spi_driver = {
+ .driver = {
+ .name = "psxpad-spi",
+ .pm = &psxpad_spi_pm,
+ },
+ .id_table = psxpad_spi_id,
+ .probe = psxpad_spi_probe,
+ .remove = psxpad_spi_remove,
+};
+
+module_spi_driver(psxpad_spi_driver);
+
+MODULE_AUTHOR("AZO <[email protected]>");
+MODULE_DESCRIPTION("PSX (Play Station 1/2) pad with SPI Bus Driver");
+MODULE_LICENSE("GPL");
--
2.11.0
Hi Tomohiro,
[auto build test WARNING on input/next]
[also build test WARNING on v4.11-rc8 next-20170428]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Tomohiro-Yoshidomi/Input-psxpad-spi-Add-PSX-Play-Station-1-2-pad-SPI-driver/20170429-014135
base: https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next
config: ia64-allmodconfig (attached as .config)
compiler: ia64-linux-gcc (GCC) 6.2.0
reproduce:
wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=ia64
Note: it may well be a FALSE warning. FWIW you are at least aware of it now.
http://gcc.gnu.org/wiki/Better_Uninitialized_Warnings
All warnings (new ones prefixed by >>):
drivers/input//joystick/psxpad-spi.c: In function 'psxpad_spi_probe':
>> drivers/input//joystick/psxpad-spi.c:496:5: warning: 'pdev' may be used uninitialized in this function [-Wmaybe-uninitialized]
if (pdev) {
^
vim +/pdev +496 drivers/input//joystick/psxpad-spi.c
480 /* pad settings */
481 psxpad_setmotorlevel(pad, 0, 0);
482
483 /* register input pool device */
484 err = input_register_polled_device(pdev);
485 if (err) {
486 dev_err(&spi->dev, "psxpad-spi: probe: failed register!!\n");
487 input_free_polled_device(pdev);
488 goto err_free_mem;
489 }
490
491 pm_runtime_enable(&spi->dev);
492
493 return 0;
494
495 err_free_mem:
> 496 if (pdev) {
497 psxpad_spi_deinit_ff(pad);
498 input_free_polled_device(pdev);
499 }
500 devm_kfree(&spi->dev, pad);
501
502 return err;
503 }
504
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
On 04/27/2017 07:31 PM, Tomohiro Yoshidomi wrote:
> PSX pads can be connected directly to the SPI bus.
>
> Signed-off-by: Tomohiro Yoshidomi <[email protected]>
> ---
> drivers/input/joystick/Kconfig | 17 ++
> drivers/input/joystick/Makefile | 1 +
> drivers/input/joystick/psxpad-spi.c | 564 ++++++++++++++++++++++++++++++++++++
> 3 files changed, 582 insertions(+)
> create mode 100644 drivers/input/joystick/psxpad-spi.c
>
> diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
> index 4215b538..bfbda643 100644
> --- a/drivers/input/joystick/Kconfig
> +++ b/drivers/input/joystick/Kconfig
> @@ -330,4 +330,21 @@ config JOYSTICK_MAPLE
> To compile this as a module choose M here: the module will be called
> maplecontrol.
>
> +config JOYSTICK_PSXPAD_SPI
> + tristate "PSX (Play Station 1/2) pad with SPI Bus Driver"
> + depends on SPI
> + select INPUT_POLLDEV
> + help
> + Say Y here if you connect PSX (PS1/2) pad with SPI Interface.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called psxpad-spi.
> +
> +config JOYSTICK_PSXPAD_SPI_FF
> + bool "PSX pad with SPI Bus rumble support"
> + depends on JOYSTICK_PSXPAD_SPI
> + select INPUT_FF_MEMLESS
> + help
> + Say Y here if you want to take advantage of PSX pad rumble features.
> +
> endif
> diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
> index 92dc0de9..496fd56b 100644
> --- a/drivers/input/joystick/Makefile
> +++ b/drivers/input/joystick/Makefile
> @@ -21,6 +21,7 @@ obj-$(CONFIG_JOYSTICK_INTERACT) += interact.o
> 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_SIDEWINDER) += sidewinder.o
> obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o
> obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o
> diff --git a/drivers/input/joystick/psxpad-spi.c b/drivers/input/joystick/psxpad-spi.c
> new file mode 100644
> index 00000000..8236ba60
> --- /dev/null
> +++ b/drivers/input/joystick/psxpad-spi.c
> @@ -0,0 +1,564 @@
> +/*
> + * PSX (Play Station 1/2) pad (SPI Interface)
> + *
> + * Copyright (C) 2017 AZO <[email protected]>
> + * Licensed under the GPL-2 or later.
> + *
> + * PSX pad plug (not socket)
> + * 123 456 789
> + * (...|...|...)
> + *
> + * 1: DAT -> MISO (pullup with 1k owm to 3.3V)
> + * 2: CMD -> MOSI
> + * 3: 9V (for motor, if not use N.C.)
> + * 4: GND
> + * 5: 3.3V
> + * 6: Attention -> CS(SS)
> + * 7: SCK -> SCK
> + * 8: N.C.
> + * 9: ACK -> N.C.
> + */
> +
> +#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>
> +#include <linux/pm.h>
> +#include <linux/pm_runtime.h>
> +
> +//#define PSXPAD_ENABLE_ANALOG2
The fact that a single #define in your module can completely change
the mapping of the gamepad without changing any device ID, name, or
such is going to pose problems for software like SDL which rely on
these characteristics to choose mappings.
> +
> +#define PSXPAD_DEFAULT_SPI_DELAY 100
> +#define PSXPAD_DEFAULT_INTERVAL 16
> +#define PSXPAD_DEFAULT_INTERVAL_MIN 8
> +#define PSXPAD_DEFAULT_INTERVAL_MAX 32
> +#define PSXPAD_DEFAULT_INPUT_PHYSIZE 32
> +
> +#define REVERSE_BIT(x) ((((x) & 0x80) >> 7) | (((x) & 0x40) >> 5) | (((x) & 0x20) >> 3) | (((x) & 0x10) >> 1) | (((x) & 0x08) << 1) | (((x) & 0x04) << 3) | (((x) & 0x02) << 5) | (((x) & 0x01) << 7))
> +
> +enum {
> + PSXPAD_KEYSTATE_TYPE_DIGITAL = 0,
> + PSXPAD_KEYSTATE_TYPE_ANALOG1,
> + PSXPAD_KEYSTATE_TYPE_ANALOG2,
> + PSXPAD_KEYSTATE_TYPE_UNKNOWN
> +};
> +
> +static const u8 PSX_CMD_INIT_PRESSURE[] = {0x01, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00};
> +static const u8 PSX_CMD_ALL_PRESSURE[] = {0x01, 0x4F, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00};
> +static const u8 PSX_CMD_POLL[] = {0x01, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
> +static const u8 PSX_CMD_ENTER_CFG[] = {0x01, 0x43, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
> +static const u8 PSX_CMD_EXIT_CFG[] = {0x01, 0x43, 0x00, 0x00, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A};
> +static const u8 PSX_CMD_ENABLE_MOTOR[] = {0x01, 0x4D, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
> +static const u8 PSX_CMD_AD_MODE[] = {0x01, 0x44, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
> +
> +struct psxpad {
> + struct spi_device *spi;
> + struct input_polled_dev *pdev;
> + struct input_dev *idev;
> + char phys[PSXPAD_DEFAULT_INPUT_PHYSIZE];
> + u16 spi_delay;
> + bool analog_mode;
> + bool mode_lock;
> + bool motor1enable;
> + bool motor2enable;
> + u8 motor1level;
> + u8 motor2level;
> +
> + /* for suspend/resume */
> + bool sus_analog_mode;
> + bool sus_mode_lock;
> + bool sus_motor1enable;
> + bool sus_motor2enable;
> + u8 sus_motor1level;
> + u8 sus_motor2level;
> +
> + u8 spi_speed;
> + u8 poolcmd[sizeof(PSX_CMD_POLL)];
pollcmd
> + u8 enablemotor[sizeof(PSX_CMD_ENABLE_MOTOR)];
> + u8 admode[sizeof(PSX_CMD_AD_MODE)];
> + u8 sendbuf[0x20] ____cacheline_aligned;
> + u8 response[sizeof(PSX_CMD_POLL)] ____cacheline_aligned;
> +};
> +
> +static int psxpad_command(struct psxpad *pad, const u8 sendcmd[], u8 response[], const u8 sendcmdlen)
> +{
> + struct spi_transfer xfers = {
> + .tx_buf = pad->sendbuf,
> + .rx_buf = response,
> + .len = sendcmdlen,
> + .delay_usecs = pad->spi_delay,
> + };
> + struct spi_message msg;
> + int err;
> + u8 loc;
> +
> + spi_message_init(&msg);
> +
> + for (loc = 0; loc < sendcmdlen; loc++)
> + pad->sendbuf[loc] = REVERSE_BIT(sendcmd[loc]);
> +
> + err = spi_sync_transfer(pad->spi, &xfers, 1);
> + if (err) {
> + dev_err(&pad->idev->dev, "psxpad-spi: failed SPI xfers!!\n");
> + goto err_end;
> + }
> +
> + for (loc = 0; loc < sendcmdlen; loc++)
> + response[loc] = REVERSE_BIT(response[loc]);
> +
> + return 0;
> +
> + err_end:
> +
> + return err;
> +}
> +
> +static void psxpad_setadmode(struct psxpad *pad, const bool analog_mode, const bool mode_lock)
> +{
> + int err;
> +
> + pad->analog_mode = analog_mode;
> + pad->mode_lock = mode_lock;
> +
> + pad->admode[3] = pad->analog_mode ? 0x01 : 0x00;
> + pad->admode[4] = pad->mode_lock ? 0x03 : 0x00;
> +
> + err = psxpad_command(pad, PSX_CMD_ENTER_CFG, pad->response, sizeof(PSX_CMD_ENTER_CFG));
> + if (err) {
> + dev_err(&pad->idev->dev, "psxpad-spi: setadmode: enter cfg failed!!\n");
> + return;
> + }
> + err = psxpad_command(pad, pad->admode, pad->response, sizeof(PSX_CMD_AD_MODE));
> + if (err) {
> + dev_err(&pad->idev->dev, "psxpad-spi: setadmode: set admode failed!!\n");
> + return;
> + }
> +#ifdef PSXPAD_ENABLE_ANALOG2
> + err = psxpad_command(pad, PSX_CMD_INIT_PRESSURE, pad->response, sizeof(PSX_CMD_INIT_PRESSURE));
> + if (err) {
> + dev_err(&pad->idev->dev, "psxpad-spi: setadmode: init pressure failed!!\n");
> + return;
> + }
> + err = psxpad_command(pad, PSX_CMD_ALL_PRESSURE, pad->response, sizeof(PSX_CMD_ALL_PRESSURE));
> + if (err) {
> + dev_err(&pad->idev->dev, "psxpad-spi: setadmode: set all pressure failed!!\n");
> + return;
> + }
> +#endif /* PSXPAD_ENABLE_ANALOG2 */
> + err = psxpad_command(pad, PSX_CMD_EXIT_CFG, pad->response, sizeof(PSX_CMD_EXIT_CFG));
> + if (err) {
> + dev_err(&pad->idev->dev, "psxpad-spi: setadmode: exit config failed!!\n");
> + return;
> + }
> +}
> +
> +#ifdef CONFIG_JOYSTICK_PSXPAD_SPI_FF
> +static void psxpad_setenablemotor(struct psxpad *pad, const bool motor1enable, const bool motor2enable)
> +{
> + int err;
> +
> + pad->motor1enable = motor1enable;
> + pad->motor2enable = motor2enable;
> +
> + pad->enablemotor[3] = pad->motor1enable ? 0x00 : 0xFF;
> + pad->enablemotor[4] = pad->motor2enable ? 0x01 : 0xFF;
> +
> + err = psxpad_command(pad, PSX_CMD_ENTER_CFG, pad->response, sizeof(PSX_CMD_ENTER_CFG));
> + if (err) {
> + dev_err(&pad->idev->dev, "psxpad-spi: setenablemotor: enter cfg failed!!\n");
> + return;
> + }
> + err = psxpad_command(pad, pad->enablemotor, pad->response, sizeof(PSX_CMD_ENABLE_MOTOR));
> + if (err) {
> + dev_err(&pad->idev->dev, "psxpad-spi: setenablemotor: enable motor failed!!\n");
> + return;
> + }
> + err = psxpad_command(pad, PSX_CMD_EXIT_CFG, pad->response, sizeof(PSX_CMD_EXIT_CFG));
> + if (err) {
> + dev_err(&pad->idev->dev, "psxpad-spi: setenablemotor: exit config failed!!\n");
> + return;
> + }
> +}
> +
> +static void psxpad_setmotorlevel(struct psxpad *pad, const u8 motor1level, const u8 motor2level)
> +{
> + pad->motor1level = motor1level ? 0xFF : 0x00;
> + pad->motor2level = motor2level;
> +
> + pad->poolcmd[3] = pad->motor1level;
> + pad->poolcmd[4] = pad->motor2level;
> +}
> +#else /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */
> +static void psxpad_setenablemotor(struct psxpad *pad, const bool motor1enable, const bool motor2enable) { }
> +
> +static void psxpad_setmotorlevel(struct psxpad *pad, const u8 motor1level, const u8 motor2level) { }
> +#endif /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */
> +
> +static void psxpad_spi_poll_open(struct input_polled_dev *pdev)
> +{
> + struct psxpad *pad = pdev->private;
> +
> + pm_runtime_get_sync(&pad->spi->dev);
> +}
> +
> +static void psxpad_spi_poll_close(struct input_polled_dev *pdev)
> +{
> + struct psxpad *pad = pdev->private;
> +
> + pm_runtime_put_sync(&pad->spi->dev);
> +}
> +
> +static void psxpad_spi_poll(struct input_polled_dev *pdev)
> +{
> + struct psxpad *pad = pdev->private;
> + int err;
> +
> + err = psxpad_command(pad, pad->poolcmd, pad->response, sizeof(PSX_CMD_POLL));
> + if (err) {
> + dev_err(&pad->idev->dev, "psxpad-spi: poll: poll cmd failed!!\n");
> + return;
> + }
Does the PSX gamepad send "digital" responses when the analog button is off and
"analog 1/2" responses when the analog button is on? I'm not sure it's really
ideal to be radically altering the mapping of the gamepad by simply pressing
the analog button to get the analog sticks working.
I'm probably leaning toward just exposing buttons as digital always, even if
analog is set. Most games/applications aren't written with pressure-sensitive
buttons in mind, and may not be able to map an axis to a button.
What are your thoughts?
> +
> + switch (pad->response[1]) {
> +#ifdef PSXPAD_ENABLE_ANALOG2
> + case 0x79: /* analog 2 */
> + input_report_abs(pad->idev, ABS_HAT0Y, pad->response[11]);
> + input_report_abs(pad->idev, ABS_HAT1Y, pad->response[12]);
> + input_report_abs(pad->idev, ABS_HAT0X, pad->response[10]);
> + input_report_abs(pad->idev, ABS_HAT1X, pad->response[9]);
> + input_report_abs(pad->idev, ABS_MISC, pad->response[13]);
> + input_report_abs(pad->idev, ABS_PRESSURE, pad->response[14]);
> + input_report_abs(pad->idev, ABS_BRAKE, pad->response[15]);
> + input_report_abs(pad->idev, ABS_THROTTLE, pad->response[16]);
> + input_report_abs(pad->idev, ABS_HAT2X, pad->response[17]);
> + input_report_abs(pad->idev, ABS_HAT3X, pad->response[18]);
> + input_report_abs(pad->idev, ABS_HAT2Y, pad->response[19]);
> + input_report_abs(pad->idev, ABS_HAT3Y, pad->response[20]);
> + input_report_abs(pad->idev, ABS_X, pad->response[7]);
> + input_report_abs(pad->idev, ABS_Y, pad->response[8]);
> + input_report_abs(pad->idev, ABS_RX, pad->response[5]);
> + input_report_abs(pad->idev, ABS_RY, pad->response[6]);
> + input_report_key(pad->idev, BTN_DPAD_UP, false);
> + input_report_key(pad->idev, BTN_DPAD_DOWN, false);
> + input_report_key(pad->idev, BTN_DPAD_LEFT, false);
> + input_report_key(pad->idev, BTN_DPAD_RIGHT, false);
> + input_report_key(pad->idev, BTN_X, false);
> + input_report_key(pad->idev, BTN_A, false);
> + input_report_key(pad->idev, BTN_B, false);
> + input_report_key(pad->idev, BTN_Y, false);
> + input_report_key(pad->idev, BTN_TL, false);
> + input_report_key(pad->idev, BTN_TR, false);
> + input_report_key(pad->idev, BTN_TL2, false);
> + input_report_key(pad->idev, BTN_TR2, false);
> + input_report_key(pad->idev, BTN_THUMBL, (pad->response[3] & 0x02U) ? false : true);
> + input_report_key(pad->idev, BTN_THUMBR, (pad->response[3] & 0x04U) ? false : true);
> + input_report_key(pad->idev, BTN_SELECT, (pad->response[3] & 0x01U) ? false : true);
> + input_report_key(pad->idev, BTN_START, (pad->response[3] & 0x08U) ? false : true);
> + break;
> +#endif /* PSXPAD_ENABLE_ANALOG2 */
> +
> + case 0x73: /* analog 1 */
> +#ifdef PSXPAD_ENABLE_ANALOG2
> + input_report_abs(pad->idev, ABS_HAT0Y, 0);
> + input_report_abs(pad->idev, ABS_HAT1Y, 0);
> + input_report_abs(pad->idev, ABS_HAT0X, 0);
> + input_report_abs(pad->idev, ABS_HAT1X, 0);
> + input_report_abs(pad->idev, ABS_MISC, 0);
> + input_report_abs(pad->idev, ABS_PRESSURE, 0);
> + input_report_abs(pad->idev, ABS_BRAKE, 0);
> + input_report_abs(pad->idev, ABS_THROTTLE, 0);
> + input_report_abs(pad->idev, ABS_HAT2X, 0);
> + input_report_abs(pad->idev, ABS_HAT3X, 0);
> + input_report_abs(pad->idev, ABS_HAT2Y, 0);
> + input_report_abs(pad->idev, ABS_HAT3Y, 0);
> +#endif /* PSXPAD_ENABLE_ANALOG2 */
> + input_report_abs(pad->idev, ABS_X, pad->response[7]);
> + input_report_abs(pad->idev, ABS_Y, pad->response[8]);
> + input_report_abs(pad->idev, ABS_RX, pad->response[5]);
> + input_report_abs(pad->idev, ABS_RY, pad->response[6]);
> + input_report_key(pad->idev, BTN_DPAD_UP, (pad->response[3] & 0x10U) ? false : true);
> + input_report_key(pad->idev, BTN_DPAD_DOWN, (pad->response[3] & 0x40U) ? false : true);
> + input_report_key(pad->idev, BTN_DPAD_LEFT, (pad->response[3] & 0x80U) ? false : true);
> + input_report_key(pad->idev, BTN_DPAD_RIGHT, (pad->response[3] & 0x20U) ? false : true);
> + input_report_key(pad->idev, BTN_X, (pad->response[4] & 0x10U) ? false : true);
> + input_report_key(pad->idev, BTN_A, (pad->response[4] & 0x20U) ? false : true);
> + input_report_key(pad->idev, BTN_B, (pad->response[4] & 0x40U) ? false : true);
> + input_report_key(pad->idev, BTN_Y, (pad->response[4] & 0x80U) ? false : true);
> + input_report_key(pad->idev, BTN_TL, (pad->response[4] & 0x04U) ? false : true);
> + input_report_key(pad->idev, BTN_TR, (pad->response[4] & 0x08U) ? false : true);
> + input_report_key(pad->idev, BTN_TL2, (pad->response[4] & 0x01U) ? false : true);
> + input_report_key(pad->idev, BTN_TR2, (pad->response[4] & 0x02U) ? false : true);
> + input_report_key(pad->idev, BTN_THUMBL, (pad->response[3] & 0x02U) ? false : true);
> + input_report_key(pad->idev, BTN_THUMBR, (pad->response[3] & 0x04U) ? false : true);
> + input_report_key(pad->idev, BTN_SELECT, (pad->response[3] & 0x01U) ? false : true);
> + input_report_key(pad->idev, BTN_START, (pad->response[3] & 0x08U) ? false : true);
> + break;
> +
> + case 0x41: /* digital */
> +#ifdef PSXPAD_ENABLE_ANALOG2
> + input_report_abs(pad->idev, ABS_HAT0Y, 0);
> + input_report_abs(pad->idev, ABS_HAT1Y, 0);
> + input_report_abs(pad->idev, ABS_HAT0X, 0);
> + input_report_abs(pad->idev, ABS_HAT1X, 0);
> + input_report_abs(pad->idev, ABS_MISC, 0);
> + input_report_abs(pad->idev, ABS_PRESSURE, 0);
> + input_report_abs(pad->idev, ABS_BRAKE, 0);
> + input_report_abs(pad->idev, ABS_THROTTLE, 0);
> + input_report_abs(pad->idev, ABS_HAT2X, 0);
> + input_report_abs(pad->idev, ABS_HAT3X, 0);
> + input_report_abs(pad->idev, ABS_HAT2Y, 0);
> + input_report_abs(pad->idev, ABS_HAT3Y, 0);
> +#endif /* PSXPAD_ENABLE_ANALOG2 */
> + input_report_abs(pad->idev, ABS_X, 0x80);
> + input_report_abs(pad->idev, ABS_Y, 0x80);
> + input_report_abs(pad->idev, ABS_RX, 0x80);
> + input_report_abs(pad->idev, ABS_RY, 0x80);
> + input_report_key(pad->idev, BTN_DPAD_UP, (pad->response[3] & 0x10U) ? false : true);
> + input_report_key(pad->idev, BTN_DPAD_DOWN, (pad->response[3] & 0x40U) ? false : true);
> + input_report_key(pad->idev, BTN_DPAD_LEFT, (pad->response[3] & 0x80U) ? false : true);
> + input_report_key(pad->idev, BTN_DPAD_RIGHT, (pad->response[3] & 0x20U) ? false : true);
> + input_report_key(pad->idev, BTN_X, (pad->response[4] & 0x10U) ? false : true);
> + input_report_key(pad->idev, BTN_A, (pad->response[4] & 0x20U) ? false : true);
> + input_report_key(pad->idev, BTN_B, (pad->response[4] & 0x40U) ? false : true);
> + input_report_key(pad->idev, BTN_Y, (pad->response[4] & 0x80U) ? false : true);
> + input_report_key(pad->idev, BTN_TL, (pad->response[4] & 0x04U) ? false : true);
> + input_report_key(pad->idev, BTN_TR, (pad->response[4] & 0x08U) ? false : true);
> + input_report_key(pad->idev, BTN_TL2, (pad->response[4] & 0x01U) ? false : true);
> + input_report_key(pad->idev, BTN_TR2, (pad->response[4] & 0x02U) ? false : true);
> + input_report_key(pad->idev, BTN_THUMBL, false);
> + input_report_key(pad->idev, BTN_THUMBR, false);
> + input_report_key(pad->idev, BTN_SELECT, (pad->response[3] & 0x01U) ? false : true);
> + input_report_key(pad->idev, BTN_START, (pad->response[3] & 0x08U) ? false : true);
> + break;
> + }
> +
> + input_sync(pad->idev);
> + psxpad_setenablemotor(pad, true, true);
> +}
> +
> +#ifdef CONFIG_JOYSTICK_PSXPAD_SPI_FF
> +static int psxpad_spi_ff(struct input_dev *idev, void *data, struct ff_effect *effect)
> +{
> + struct psxpad *pad = idev->dev.platform_data;
> +
> + switch (effect->type) {
> + case FF_RUMBLE:
> + psxpad_setmotorlevel(pad, (effect->u.rumble.weak_magnitude >> 8) & 0xFFU, (effect->u.rumble.strong_magnitude >> 8) & 0xFFU);
> + break;
> + }
> +
> + return 0;
> +}
> +
> +static int psxpad_spi_init_ff(struct psxpad *pad)
> +{
> + int err;
> +
> + input_set_capability(pad->idev, EV_FF, FF_RUMBLE);
> + err = input_ff_create_memless(pad->idev, NULL, psxpad_spi_ff);
> + if (err) {
> + dev_err(&pad->idev->dev, "psxpad-spi: init_ff: ff alloc failed!!\n");
> + err = -ENOMEM;
> + }
> +
> + return err;
> +}
> +
> +static void psxpad_spi_deinit_ff(struct psxpad *pad)
> +{
> + input_ff_destroy(pad->idev);
> +}
> +#else /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */
> +static inline int psxpad_spi_init_ff(struct psxpad *pad)
> +{
> + return 0;
> +}
> +
> +static void psxpad_spi_deinit_ff(struct psxpad *pad) { }
> +#endif /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */
> +
> +static int psxpad_spi_probe(struct spi_device *spi)
> +{
> + struct psxpad *pad;
> + struct input_polled_dev *pdev;
> + struct input_dev *idev;
> + int err, i;
> +
> + pad = devm_kzalloc(&spi->dev, sizeof(struct psxpad), GFP_KERNEL);
> + if (!pad) {
> + err = -ENOMEM;
> + goto err_free_mem;
> + }
> + pdev = input_allocate_polled_device();
> + if (!pdev) {
> + dev_err(&spi->dev, "psxpad-spi: probe: pdev alloc failed!!\n");
> + err = -ENOMEM;
> + goto err_free_mem;
> + }
> + for (i = 0; i < sizeof(PSX_CMD_POLL); i++)
> + pad->poolcmd[i] = PSX_CMD_POLL[i];
> + for (i = 0; i < sizeof(PSX_CMD_ENABLE_MOTOR); i++)
> + pad->enablemotor[i] = PSX_CMD_ENABLE_MOTOR[i];
> + for (i = 0; i < sizeof(PSX_CMD_AD_MODE); i++)
> + pad->admode[i] = PSX_CMD_AD_MODE[i];
> + pad->spi_delay = PSXPAD_DEFAULT_SPI_DELAY;
> +
> + /* input pool device settings */
> + pad->pdev = pdev;
> + pad->spi = spi;
> + pdev->private = pad;
> + pdev->open = psxpad_spi_poll_open;
> + pdev->close = psxpad_spi_poll_close;
> + pdev->poll = psxpad_spi_poll;
> + pdev->poll_interval = PSXPAD_DEFAULT_INTERVAL;
> + pdev->poll_interval_min = PSXPAD_DEFAULT_INTERVAL_MIN;
> + pdev->poll_interval_max = PSXPAD_DEFAULT_INTERVAL_MAX;
> +
> + /* input device settings */
> + idev = pdev->input;
> + pad->idev = idev;
> + idev->name = "PSX (PS1/2) pad";
> + snprintf(pad->phys, sizeof(pad->phys), "%s/input", dev_name(&spi->dev));
> + idev->id.bustype = BUS_SPI;
> + idev->dev.parent = &spi->dev;
> + idev->dev.platform_data = pad;
> +
> + /* 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);
> + input_set_abs_params(idev, ABS_RX, 0, 255, 0, 0);
> + input_set_abs_params(idev, ABS_RY, 0, 255, 0, 0);
> +#ifdef PSXPAD_ENABLE_ANALOG2
> + input_set_abs_params(idev, ABS_HAT0Y, 0, 255, 0, 0); /* up */
> + input_set_abs_params(idev, ABS_HAT1Y, 0, 255, 0, 0); /* down */
> + input_set_abs_params(idev, ABS_HAT0X, 0, 255, 0, 0); /* left */
> + input_set_abs_params(idev, ABS_HAT1X, 0, 255, 0, 0); /* right */
You should map up and down to ABS_HAT0Y and left and right to ABS_HAT0X.
Using 2 separate sets of hat axes for a single d-pad will likely confuse userspace.
> + input_set_abs_params(idev, ABS_MISC, 0, 255, 0, 0);
> + input_set_abs_params(idev, ABS_PRESSURE, 0, 255, 0, 0);
> + input_set_abs_params(idev, ABS_BRAKE, 0, 255, 0, 0);
> + input_set_abs_params(idev, ABS_THROTTLE, 0, 255, 0, 0);
What buttons/axes do these represent?
> + input_set_abs_params(idev, ABS_HAT2X, 0, 255, 0, 0); /* L1 */
> + input_set_abs_params(idev, ABS_HAT3X, 0, 255, 0, 0); /* R1 */
> + input_set_abs_params(idev, ABS_HAT2Y, 0, 255, 0, 0); /* L2 */
> + input_set_abs_params(idev, ABS_HAT3Y, 0, 255, 0, 0); /* R2 */
L2 and R2 are conventionally ABS_BRAKE and ABS_GAS or ABS_Z and ABS_RZ.
> +#endif /* PSXPAD_ENABLE_ANALOG2 */
> + input_set_capability(idev, EV_KEY, BTN_DPAD_UP);
> + input_set_capability(idev, EV_KEY, BTN_DPAD_DOWN);
> + input_set_capability(idev, EV_KEY, BTN_DPAD_LEFT);
> + input_set_capability(idev, EV_KEY, BTN_DPAD_RIGHT);
> + input_set_capability(idev, EV_KEY, BTN_A);
> + input_set_capability(idev, EV_KEY, BTN_B);
> + input_set_capability(idev, EV_KEY, BTN_X);
> + input_set_capability(idev, EV_KEY, BTN_Y);
> + input_set_capability(idev, EV_KEY, BTN_TL);
> + input_set_capability(idev, EV_KEY, BTN_TR);
> + input_set_capability(idev, EV_KEY, BTN_TL2);
> + input_set_capability(idev, EV_KEY, BTN_TR2);
> + input_set_capability(idev, EV_KEY, BTN_THUMBL);
> + input_set_capability(idev, EV_KEY, BTN_THUMBR);
> + input_set_capability(idev, EV_KEY, BTN_SELECT);
> + input_set_capability(idev, EV_KEY, BTN_START);
> +
> + /* force feedback */
> + err = psxpad_spi_init_ff(pad);
> + if (err) {
> + err = -ENOMEM;
> + goto err_free_mem;
> + }
> +
> + /* SPI settings */
> + spi->mode = SPI_MODE_3;
> + spi->bits_per_word = 8;
> + /* (PSX pad might be possible works 250kHz/500kHz) */
> + spi->master->min_speed_hz = 125000;
> + spi->master->max_speed_hz = 125000;
> + spi_setup(spi);
> +
> + /* pad settings */
> + psxpad_setmotorlevel(pad, 0, 0);
> +
> + /* register input pool device */
> + err = input_register_polled_device(pdev);
> + if (err) {
> + dev_err(&spi->dev, "psxpad-spi: probe: failed register!!\n");
> + input_free_polled_device(pdev);
> + goto err_free_mem;
> + }
> +
> + pm_runtime_enable(&spi->dev);
> +
> + return 0;
> +
> + err_free_mem:
> + if (pdev) {
> + psxpad_spi_deinit_ff(pad);
> + input_free_polled_device(pdev);
> + }
> + devm_kfree(&spi->dev, pad);
> +
> + return err;
> +}
> +
> +
> +static int psxpad_spi_remove(struct spi_device *spi)
> +{
> + return 0;
> +}
> +
> +static int __maybe_unused psxpad_spi_suspend(struct device *dev)
> +{
> + struct spi_device *spi = to_spi_device(dev);
> + struct psxpad *pad = spi_get_drvdata(spi);
> +
> + pad->sus_analog_mode = pad->analog_mode;
> + pad->sus_mode_lock = pad->mode_lock;
> + pad->sus_motor1enable = pad->motor1enable;
> + pad->sus_motor2enable = pad->motor2enable;
> + pad->sus_motor1level = pad->motor1level;
> + pad->sus_motor2level = pad->motor2level;
Is there a reason you need a separate set of fields for these?
> +
> + psxpad_setadmode(pad, false, false);
> + psxpad_setmotorlevel(pad, 0, 0);
> + psxpad_setenablemotor(pad, false, false);
> +
> + return 0;
> +}
> +
> +static int __maybe_unused psxpad_spi_resume(struct device *dev)
> +{
> + struct spi_device *spi = to_spi_device(dev);
> + struct psxpad *pad = spi_get_drvdata(spi);
> +
> + psxpad_setadmode(pad, pad->sus_analog_mode, pad->sus_mode_lock);
> + psxpad_setmotorlevel(pad, pad->sus_motor1enable, pad->sus_motor2enable);
> + psxpad_setenablemotor(pad, pad->sus_motor1level, pad->sus_motor2level);
> +
> + return 0;
> +}
> +
> +static SIMPLE_DEV_PM_OPS(psxpad_spi_pm, psxpad_spi_suspend, psxpad_spi_resume);
> +
> +static const struct spi_device_id psxpad_spi_id[] = {
> + { "psxpad-spi", 0 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(spi, psxpad_spi_id);
> +
> +static struct spi_driver psxpad_spi_driver = {
> + .driver = {
> + .name = "psxpad-spi",
> + .pm = &psxpad_spi_pm,
> + },
> + .id_table = psxpad_spi_id,
> + .probe = psxpad_spi_probe,
> + .remove = psxpad_spi_remove,
> +};
> +
> +module_spi_driver(psxpad_spi_driver);
> +
> +MODULE_AUTHOR("AZO <[email protected]>");
> +MODULE_DESCRIPTION("PSX (Play Station 1/2) pad with SPI Bus Driver");
> +MODULE_LICENSE("GPL");
>
Mr.Gutman
Thanks to the opinion.
I thought that PC applications can flexibly set button and axis settings.
However, there are many applications that can not be set.
To briefly explain the operation of the PSX pad,
1. When analog button is off, PSX gamepad send "digital" responses.
2. When analog button is on, PSX gamepad send "analog1" responses.
3. PSX master sets to pad (ALL_PRESSURE mode), PSX gamepad send
"analog2" responses.
4. Unless poll the pad at regular time intervals, the setting of
"rumble" and "analog2" is canceled.
Especially "analog2" is currently unstable and can not realize the function.
And its use is also unknown.
Implementation should be changed if SDL is adversely affected.
Given your opinion, "analog2" should be omitting...
I just thought that analog 2 has something to do.
So, because it gets confused, I'll delete "analog2".
Regarding the "rumble" motor, it is set by polling so as to be always effective.
2017-04-29 7:04 GMT+09:00 Cameron Gutman <[email protected]>:
> On 04/27/2017 07:31 PM, Tomohiro Yoshidomi wrote:
>> PSX pads can be connected directly to the SPI bus.
>>
>> Signed-off-by: Tomohiro Yoshidomi <[email protected]>
>> ---
>> drivers/input/joystick/Kconfig | 17 ++
>> drivers/input/joystick/Makefile | 1 +
>> drivers/input/joystick/psxpad-spi.c | 564 ++++++++++++++++++++++++++++++++++++
>> 3 files changed, 582 insertions(+)
>> create mode 100644 drivers/input/joystick/psxpad-spi.c
>>
>> diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
>> index 4215b538..bfbda643 100644
>> --- a/drivers/input/joystick/Kconfig
>> +++ b/drivers/input/joystick/Kconfig
>> @@ -330,4 +330,21 @@ config JOYSTICK_MAPLE
>> To compile this as a module choose M here: the module will be called
>> maplecontrol.
>>
>> +config JOYSTICK_PSXPAD_SPI
>> + tristate "PSX (Play Station 1/2) pad with SPI Bus Driver"
>> + depends on SPI
>> + select INPUT_POLLDEV
>> + help
>> + Say Y here if you connect PSX (PS1/2) pad with SPI Interface.
>> +
>> + To compile this driver as a module, choose M here: the
>> + module will be called psxpad-spi.
>> +
>> +config JOYSTICK_PSXPAD_SPI_FF
>> + bool "PSX pad with SPI Bus rumble support"
>> + depends on JOYSTICK_PSXPAD_SPI
>> + select INPUT_FF_MEMLESS
>> + help
>> + Say Y here if you want to take advantage of PSX pad rumble features.
>> +
>> endif
>> diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
>> index 92dc0de9..496fd56b 100644
>> --- a/drivers/input/joystick/Makefile
>> +++ b/drivers/input/joystick/Makefile
>> @@ -21,6 +21,7 @@ obj-$(CONFIG_JOYSTICK_INTERACT) += interact.o
>> 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_SIDEWINDER) += sidewinder.o
>> obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o
>> obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o
>> diff --git a/drivers/input/joystick/psxpad-spi.c b/drivers/input/joystick/psxpad-spi.c
>> new file mode 100644
>> index 00000000..8236ba60
>> --- /dev/null
>> +++ b/drivers/input/joystick/psxpad-spi.c
>> @@ -0,0 +1,564 @@
>> +/*
>> + * PSX (Play Station 1/2) pad (SPI Interface)
>> + *
>> + * Copyright (C) 2017 AZO <[email protected]>
>> + * Licensed under the GPL-2 or later.
>> + *
>> + * PSX pad plug (not socket)
>> + * 123 456 789
>> + * (...|...|...)
>> + *
>> + * 1: DAT -> MISO (pullup with 1k owm to 3.3V)
>> + * 2: CMD -> MOSI
>> + * 3: 9V (for motor, if not use N.C.)
>> + * 4: GND
>> + * 5: 3.3V
>> + * 6: Attention -> CS(SS)
>> + * 7: SCK -> SCK
>> + * 8: N.C.
>> + * 9: ACK -> N.C.
>> + */
>> +
>> +#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>
>> +#include <linux/pm.h>
>> +#include <linux/pm_runtime.h>
>> +
>> +//#define PSXPAD_ENABLE_ANALOG2
>
> The fact that a single #define in your module can completely change
> the mapping of the gamepad without changing any device ID, name, or
> such is going to pose problems for software like SDL which rely on
> these characteristics to choose mappings.
>
>> +
>> +#define PSXPAD_DEFAULT_SPI_DELAY 100
>> +#define PSXPAD_DEFAULT_INTERVAL 16
>> +#define PSXPAD_DEFAULT_INTERVAL_MIN 8
>> +#define PSXPAD_DEFAULT_INTERVAL_MAX 32
>> +#define PSXPAD_DEFAULT_INPUT_PHYSIZE 32
>> +
>> +#define REVERSE_BIT(x) ((((x) & 0x80) >> 7) | (((x) & 0x40) >> 5) | (((x) & 0x20) >> 3) | (((x) & 0x10) >> 1) | (((x) & 0x08) << 1) | (((x) & 0x04) << 3) | (((x) & 0x02) << 5) | (((x) & 0x01) << 7))
>> +
>> +enum {
>> + PSXPAD_KEYSTATE_TYPE_DIGITAL = 0,
>> + PSXPAD_KEYSTATE_TYPE_ANALOG1,
>> + PSXPAD_KEYSTATE_TYPE_ANALOG2,
>> + PSXPAD_KEYSTATE_TYPE_UNKNOWN
>> +};
>> +
>> +static const u8 PSX_CMD_INIT_PRESSURE[] = {0x01, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00};
>> +static const u8 PSX_CMD_ALL_PRESSURE[] = {0x01, 0x4F, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00};
>> +static const u8 PSX_CMD_POLL[] = {0x01, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
>> +static const u8 PSX_CMD_ENTER_CFG[] = {0x01, 0x43, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
>> +static const u8 PSX_CMD_EXIT_CFG[] = {0x01, 0x43, 0x00, 0x00, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A};
>> +static const u8 PSX_CMD_ENABLE_MOTOR[] = {0x01, 0x4D, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
>> +static const u8 PSX_CMD_AD_MODE[] = {0x01, 0x44, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
>> +
>> +struct psxpad {
>> + struct spi_device *spi;
>> + struct input_polled_dev *pdev;
>> + struct input_dev *idev;
>> + char phys[PSXPAD_DEFAULT_INPUT_PHYSIZE];
>> + u16 spi_delay;
>> + bool analog_mode;
>> + bool mode_lock;
>> + bool motor1enable;
>> + bool motor2enable;
>> + u8 motor1level;
>> + u8 motor2level;
>> +
>> + /* for suspend/resume */
>> + bool sus_analog_mode;
>> + bool sus_mode_lock;
>> + bool sus_motor1enable;
>> + bool sus_motor2enable;
>> + u8 sus_motor1level;
>> + u8 sus_motor2level;
>> +
>> + u8 spi_speed;
>> + u8 poolcmd[sizeof(PSX_CMD_POLL)];
>
> pollcmd
Ah...I'll fix.
>> + u8 enablemotor[sizeof(PSX_CMD_ENABLE_MOTOR)];
>> + u8 admode[sizeof(PSX_CMD_AD_MODE)];
>> + u8 sendbuf[0x20] ____cacheline_aligned;
>> + u8 response[sizeof(PSX_CMD_POLL)] ____cacheline_aligned;
>> +};
>> +
>> +static int psxpad_command(struct psxpad *pad, const u8 sendcmd[], u8 response[], const u8 sendcmdlen)
>> +{
>> + struct spi_transfer xfers = {
>> + .tx_buf = pad->sendbuf,
>> + .rx_buf = response,
>> + .len = sendcmdlen,
>> + .delay_usecs = pad->spi_delay,
>> + };
>> + struct spi_message msg;
>> + int err;
>> + u8 loc;
>> +
>> + spi_message_init(&msg);
>> +
>> + for (loc = 0; loc < sendcmdlen; loc++)
>> + pad->sendbuf[loc] = REVERSE_BIT(sendcmd[loc]);
>> +
>> + err = spi_sync_transfer(pad->spi, &xfers, 1);
>> + if (err) {
>> + dev_err(&pad->idev->dev, "psxpad-spi: failed SPI xfers!!\n");
>> + goto err_end;
>> + }
>> +
>> + for (loc = 0; loc < sendcmdlen; loc++)
>> + response[loc] = REVERSE_BIT(response[loc]);
>> +
>> + return 0;
>> +
>> + err_end:
>> +
>> + return err;
>> +}
>> +
>> +static void psxpad_setadmode(struct psxpad *pad, const bool analog_mode, const bool mode_lock)
>> +{
>> + int err;
>> +
>> + pad->analog_mode = analog_mode;
>> + pad->mode_lock = mode_lock;
>> +
>> + pad->admode[3] = pad->analog_mode ? 0x01 : 0x00;
>> + pad->admode[4] = pad->mode_lock ? 0x03 : 0x00;
>> +
>> + err = psxpad_command(pad, PSX_CMD_ENTER_CFG, pad->response, sizeof(PSX_CMD_ENTER_CFG));
>> + if (err) {
>> + dev_err(&pad->idev->dev, "psxpad-spi: setadmode: enter cfg failed!!\n");
>> + return;
>> + }
>> + err = psxpad_command(pad, pad->admode, pad->response, sizeof(PSX_CMD_AD_MODE));
>> + if (err) {
>> + dev_err(&pad->idev->dev, "psxpad-spi: setadmode: set admode failed!!\n");
>> + return;
>> + }
>> +#ifdef PSXPAD_ENABLE_ANALOG2
>> + err = psxpad_command(pad, PSX_CMD_INIT_PRESSURE, pad->response, sizeof(PSX_CMD_INIT_PRESSURE));
>> + if (err) {
>> + dev_err(&pad->idev->dev, "psxpad-spi: setadmode: init pressure failed!!\n");
>> + return;
>> + }
>> + err = psxpad_command(pad, PSX_CMD_ALL_PRESSURE, pad->response, sizeof(PSX_CMD_ALL_PRESSURE));
>> + if (err) {
>> + dev_err(&pad->idev->dev, "psxpad-spi: setadmode: set all pressure failed!!\n");
>> + return;
>> + }
>> +#endif /* PSXPAD_ENABLE_ANALOG2 */
>> + err = psxpad_command(pad, PSX_CMD_EXIT_CFG, pad->response, sizeof(PSX_CMD_EXIT_CFG));
>> + if (err) {
>> + dev_err(&pad->idev->dev, "psxpad-spi: setadmode: exit config failed!!\n");
>> + return;
>> + }
>> +}
>> +
>> +#ifdef CONFIG_JOYSTICK_PSXPAD_SPI_FF
>> +static void psxpad_setenablemotor(struct psxpad *pad, const bool motor1enable, const bool motor2enable)
>> +{
>> + int err;
>> +
>> + pad->motor1enable = motor1enable;
>> + pad->motor2enable = motor2enable;
>> +
>> + pad->enablemotor[3] = pad->motor1enable ? 0x00 : 0xFF;
>> + pad->enablemotor[4] = pad->motor2enable ? 0x01 : 0xFF;
>> +
>> + err = psxpad_command(pad, PSX_CMD_ENTER_CFG, pad->response, sizeof(PSX_CMD_ENTER_CFG));
>> + if (err) {
>> + dev_err(&pad->idev->dev, "psxpad-spi: setenablemotor: enter cfg failed!!\n");
>> + return;
>> + }
>> + err = psxpad_command(pad, pad->enablemotor, pad->response, sizeof(PSX_CMD_ENABLE_MOTOR));
>> + if (err) {
>> + dev_err(&pad->idev->dev, "psxpad-spi: setenablemotor: enable motor failed!!\n");
>> + return;
>> + }
>> + err = psxpad_command(pad, PSX_CMD_EXIT_CFG, pad->response, sizeof(PSX_CMD_EXIT_CFG));
>> + if (err) {
>> + dev_err(&pad->idev->dev, "psxpad-spi: setenablemotor: exit config failed!!\n");
>> + return;
>> + }
>> +}
>> +
>> +static void psxpad_setmotorlevel(struct psxpad *pad, const u8 motor1level, const u8 motor2level)
>> +{
>> + pad->motor1level = motor1level ? 0xFF : 0x00;
>> + pad->motor2level = motor2level;
>> +
>> + pad->poolcmd[3] = pad->motor1level;
>> + pad->poolcmd[4] = pad->motor2level;
>> +}
>> +#else /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */
>> +static void psxpad_setenablemotor(struct psxpad *pad, const bool motor1enable, const bool motor2enable) { }
>> +
>> +static void psxpad_setmotorlevel(struct psxpad *pad, const u8 motor1level, const u8 motor2level) { }
>> +#endif /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */
>> +
>> +static void psxpad_spi_poll_open(struct input_polled_dev *pdev)
>> +{
>> + struct psxpad *pad = pdev->private;
>> +
>> + pm_runtime_get_sync(&pad->spi->dev);
>> +}
>> +
>> +static void psxpad_spi_poll_close(struct input_polled_dev *pdev)
>> +{
>> + struct psxpad *pad = pdev->private;
>> +
>> + pm_runtime_put_sync(&pad->spi->dev);
>> +}
>> +
>> +static void psxpad_spi_poll(struct input_polled_dev *pdev)
>> +{
>> + struct psxpad *pad = pdev->private;
>> + int err;
>> +
>> + err = psxpad_command(pad, pad->poolcmd, pad->response, sizeof(PSX_CMD_POLL));
>> + if (err) {
>> + dev_err(&pad->idev->dev, "psxpad-spi: poll: poll cmd failed!!\n");
>> + return;
>> + }
>
> Does the PSX gamepad send "digital" responses when the and
> "analog 1/2" responses when the analog button is on? I'm not sure it's really
> ideal to be radically altering the mapping of the gamepad by simply pressing
> the analog button to get the analog sticks working.
>
> I'm probably leaning toward just exposing buttons as digital always, even if
> analog is set. Most games/applications aren't written with pressure-sensitive
> buttons in mind, and may not be able to map an axis to a button.
>
> What are your thoughts?
>
>> +
>> + switch (pad->response[1]) {
>> +#ifdef PSXPAD_ENABLE_ANALOG2
>> + case 0x79: /* analog 2 */
>> + input_report_abs(pad->idev, ABS_HAT0Y, pad->response[11]);
>> + input_report_abs(pad->idev, ABS_HAT1Y, pad->response[12]);
>> + input_report_abs(pad->idev, ABS_HAT0X, pad->response[10]);
>> + input_report_abs(pad->idev, ABS_HAT1X, pad->response[9]);
>> + input_report_abs(pad->idev, ABS_MISC, pad->response[13]);
>> + input_report_abs(pad->idev, ABS_PRESSURE, pad->response[14]);
>> + input_report_abs(pad->idev, ABS_BRAKE, pad->response[15]);
>> + input_report_abs(pad->idev, ABS_THROTTLE, pad->response[16]);
>> + input_report_abs(pad->idev, ABS_HAT2X, pad->response[17]);
>> + input_report_abs(pad->idev, ABS_HAT3X, pad->response[18]);
>> + input_report_abs(pad->idev, ABS_HAT2Y, pad->response[19]);
>> + input_report_abs(pad->idev, ABS_HAT3Y, pad->response[20]);
>> + input_report_abs(pad->idev, ABS_X, pad->response[7]);
>> + input_report_abs(pad->idev, ABS_Y, pad->response[8]);
>> + input_report_abs(pad->idev, ABS_RX, pad->response[5]);
>> + input_report_abs(pad->idev, ABS_RY, pad->response[6]);
>> + input_report_key(pad->idev, BTN_DPAD_UP, false);
>> + input_report_key(pad->idev, BTN_DPAD_DOWN, false);
>> + input_report_key(pad->idev, BTN_DPAD_LEFT, false);
>> + input_report_key(pad->idev, BTN_DPAD_RIGHT, false);
>> + input_report_key(pad->idev, BTN_X, false);
>> + input_report_key(pad->idev, BTN_A, false);
>> + input_report_key(pad->idev, BTN_B, false);
>> + input_report_key(pad->idev, BTN_Y, false);
>> + input_report_key(pad->idev, BTN_TL, false);
>> + input_report_key(pad->idev, BTN_TR, false);
>> + input_report_key(pad->idev, BTN_TL2, false);
>> + input_report_key(pad->idev, BTN_TR2, false);
>> + input_report_key(pad->idev, BTN_THUMBL, (pad->response[3] & 0x02U) ? false : true);
>> + input_report_key(pad->idev, BTN_THUMBR, (pad->response[3] & 0x04U) ? false : true);
>> + input_report_key(pad->idev, BTN_SELECT, (pad->response[3] & 0x01U) ? false : true);
>> + input_report_key(pad->idev, BTN_START, (pad->response[3] & 0x08U) ? false : true);
>> + break;
>> +#endif /* PSXPAD_ENABLE_ANALOG2 */
>> +
>> + case 0x73: /* analog 1 */
>> +#ifdef PSXPAD_ENABLE_ANALOG2
>> + input_report_abs(pad->idev, ABS_HAT0Y, 0);
>> + input_report_abs(pad->idev, ABS_HAT1Y, 0);
>> + input_report_abs(pad->idev, ABS_HAT0X, 0);
>> + input_report_abs(pad->idev, ABS_HAT1X, 0);
>> + input_report_abs(pad->idev, ABS_MISC, 0);
>> + input_report_abs(pad->idev, ABS_PRESSURE, 0);
>> + input_report_abs(pad->idev, ABS_BRAKE, 0);
>> + input_report_abs(pad->idev, ABS_THROTTLE, 0);
>> + input_report_abs(pad->idev, ABS_HAT2X, 0);
>> + input_report_abs(pad->idev, ABS_HAT3X, 0);
>> + input_report_abs(pad->idev, ABS_HAT2Y, 0);
>> + input_report_abs(pad->idev, ABS_HAT3Y, 0);
>> +#endif /* PSXPAD_ENABLE_ANALOG2 */
>> + input_report_abs(pad->idev, ABS_X, pad->response[7]);
>> + input_report_abs(pad->idev, ABS_Y, pad->response[8]);
>> + input_report_abs(pad->idev, ABS_RX, pad->response[5]);
>> + input_report_abs(pad->idev, ABS_RY, pad->response[6]);
>> + input_report_key(pad->idev, BTN_DPAD_UP, (pad->response[3] & 0x10U) ? false : true);
>> + input_report_key(pad->idev, BTN_DPAD_DOWN, (pad->response[3] & 0x40U) ? false : true);
>> + input_report_key(pad->idev, BTN_DPAD_LEFT, (pad->response[3] & 0x80U) ? false : true);
>> + input_report_key(pad->idev, BTN_DPAD_RIGHT, (pad->response[3] & 0x20U) ? false : true);
>> + input_report_key(pad->idev, BTN_X, (pad->response[4] & 0x10U) ? false : true);
>> + input_report_key(pad->idev, BTN_A, (pad->response[4] & 0x20U) ? false : true);
>> + input_report_key(pad->idev, BTN_B, (pad->response[4] & 0x40U) ? false : true);
>> + input_report_key(pad->idev, BTN_Y, (pad->response[4] & 0x80U) ? false : true);
>> + input_report_key(pad->idev, BTN_TL, (pad->response[4] & 0x04U) ? false : true);
>> + input_report_key(pad->idev, BTN_TR, (pad->response[4] & 0x08U) ? false : true);
>> + input_report_key(pad->idev, BTN_TL2, (pad->response[4] & 0x01U) ? false : true);
>> + input_report_key(pad->idev, BTN_TR2, (pad->response[4] & 0x02U) ? false : true);
>> + input_report_key(pad->idev, BTN_THUMBL, (pad->response[3] & 0x02U) ? false : true);
>> + input_report_key(pad->idev, BTN_THUMBR, (pad->response[3] & 0x04U) ? false : true);
>> + input_report_key(pad->idev, BTN_SELECT, (pad->response[3] & 0x01U) ? false : true);
>> + input_report_key(pad->idev, BTN_START, (pad->response[3] & 0x08U) ? false : true);
>> + break;
>> +
>> + case 0x41: /* digital */
>> +#ifdef PSXPAD_ENABLE_ANALOG2
>> + input_report_abs(pad->idev, ABS_HAT0Y, 0);
>> + input_report_abs(pad->idev, ABS_HAT1Y, 0);
>> + input_report_abs(pad->idev, ABS_HAT0X, 0);
>> + input_report_abs(pad->idev, ABS_HAT1X, 0);
>> + input_report_abs(pad->idev, ABS_MISC, 0);
>> + input_report_abs(pad->idev, ABS_PRESSURE, 0);
>> + input_report_abs(pad->idev, ABS_BRAKE, 0);
>> + input_report_abs(pad->idev, ABS_THROTTLE, 0);
>> + input_report_abs(pad->idev, ABS_HAT2X, 0);
>> + input_report_abs(pad->idev, ABS_HAT3X, 0);
>> + input_report_abs(pad->idev, ABS_HAT2Y, 0);
>> + input_report_abs(pad->idev, ABS_HAT3Y, 0);
>> +#endif /* PSXPAD_ENABLE_ANALOG2 */
>> + input_report_abs(pad->idev, ABS_X, 0x80);
>> + input_report_abs(pad->idev, ABS_Y, 0x80);
>> + input_report_abs(pad->idev, ABS_RX, 0x80);
>> + input_report_abs(pad->idev, ABS_RY, 0x80);
>> + input_report_key(pad->idev, BTN_DPAD_UP, (pad->response[3] & 0x10U) ? false : true);
>> + input_report_key(pad->idev, BTN_DPAD_DOWN, (pad->response[3] & 0x40U) ? false : true);
>> + input_report_key(pad->idev, BTN_DPAD_LEFT, (pad->response[3] & 0x80U) ? false : true);
>> + input_report_key(pad->idev, BTN_DPAD_RIGHT, (pad->response[3] & 0x20U) ? false : true);
>> + input_report_key(pad->idev, BTN_X, (pad->response[4] & 0x10U) ? false : true);
>> + input_report_key(pad->idev, BTN_A, (pad->response[4] & 0x20U) ? false : true);
>> + input_report_key(pad->idev, BTN_B, (pad->response[4] & 0x40U) ? false : true);
>> + input_report_key(pad->idev, BTN_Y, (pad->response[4] & 0x80U) ? false : true);
>> + input_report_key(pad->idev, BTN_TL, (pad->response[4] & 0x04U) ? false : true);
>> + input_report_key(pad->idev, BTN_TR, (pad->response[4] & 0x08U) ? false : true);
>> + input_report_key(pad->idev, BTN_TL2, (pad->response[4] & 0x01U) ? false : true);
>> + input_report_key(pad->idev, BTN_TR2, (pad->response[4] & 0x02U) ? false : true);
>> + input_report_key(pad->idev, BTN_THUMBL, false);
>> + input_report_key(pad->idev, BTN_THUMBR, false);
>> + input_report_key(pad->idev, BTN_SELECT, (pad->response[3] & 0x01U) ? false : true);
>> + input_report_key(pad->idev, BTN_START, (pad->response[3] & 0x08U) ? false : true);
>> + break;
>> + }
>> +
>> + input_sync(pad->idev);
>> + psxpad_setenablemotor(pad, true, true);
>> +}
>> +
>> +#ifdef CONFIG_JOYSTICK_PSXPAD_SPI_FF
>> +static int psxpad_spi_ff(struct input_dev *idev, void *data, struct ff_effect *effect)
>> +{
>> + struct psxpad *pad = idev->dev.platform_data;
>> +
>> + switch (effect->type) {
>> + case FF_RUMBLE:
>> + psxpad_setmotorlevel(pad, (effect->u.rumble.weak_magnitude >> 8) & 0xFFU, (effect->u.rumble.strong_magnitude >> 8) & 0xFFU);
>> + break;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int psxpad_spi_init_ff(struct psxpad *pad)
>> +{
>> + int err;
>> +
>> + input_set_capability(pad->idev, EV_FF, FF_RUMBLE);
>> + err = input_ff_create_memless(pad->idev, NULL, psxpad_spi_ff);
>> + if (err) {
>> + dev_err(&pad->idev->dev, "psxpad-spi: init_ff: ff alloc failed!!\n");
>> + err = -ENOMEM;
>> + }
>> +
>> + return err;
>> +}
>> +
>> +static void psxpad_spi_deinit_ff(struct psxpad *pad)
>> +{
>> + input_ff_destroy(pad->idev);
>> +}
>> +#else /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */
>> +static inline int psxpad_spi_init_ff(struct psxpad *pad)
>> +{
>> + return 0;
>> +}
>> +
>> +static void psxpad_spi_deinit_ff(struct psxpad *pad) { }
>> +#endif /* CONFIG_JOYSTICK_PSXPAD_SPI_FF */
>> +
>> +static int psxpad_spi_probe(struct spi_device *spi)
>> +{
>> + struct psxpad *pad;
>> + struct input_polled_dev *pdev;
>> + struct input_dev *idev;
>> + int err, i;
>> +
>> + pad = devm_kzalloc(&spi->dev, sizeof(struct psxpad), GFP_KERNEL);
>> + if (!pad) {
>> + err = -ENOMEM;
>> + goto err_free_mem;
>> + }
>> + pdev = input_allocate_polled_device();
>> + if (!pdev) {
>> + dev_err(&spi->dev, "psxpad-spi: probe: pdev alloc failed!!\n");
>> + err = -ENOMEM;
>> + goto err_free_mem;
>> + }
>> + for (i = 0; i < sizeof(PSX_CMD_POLL); i++)
>> + pad->poolcmd[i] = PSX_CMD_POLL[i];
>> + for (i = 0; i < sizeof(PSX_CMD_ENABLE_MOTOR); i++)
>> + pad->enablemotor[i] = PSX_CMD_ENABLE_MOTOR[i];
>> + for (i = 0; i < sizeof(PSX_CMD_AD_MODE); i++)
>> + pad->admode[i] = PSX_CMD_AD_MODE[i];
>> + pad->spi_delay = PSXPAD_DEFAULT_SPI_DELAY;
>> +
>> + /* input pool device settings */
>> + pad->pdev = pdev;
>> + pad->spi = spi;
>> + pdev->private = pad;
>> + pdev->open = psxpad_spi_poll_open;
>> + pdev->close = psxpad_spi_poll_close;
>> + pdev->poll = psxpad_spi_poll;
>> + pdev->poll_interval = PSXPAD_DEFAULT_INTERVAL;
>> + pdev->poll_interval_min = PSXPAD_DEFAULT_INTERVAL_MIN;
>> + pdev->poll_interval_max = PSXPAD_DEFAULT_INTERVAL_MAX;
>> +
>> + /* input device settings */
>> + idev = pdev->input;
>> + pad->idev = idev;
>> + idev->name = "PSX (PS1/2) pad";
>> + snprintf(pad->phys, sizeof(pad->phys), "%s/input", dev_name(&spi->dev));
>> + idev->id.bustype = BUS_SPI;
>> + idev->dev.parent = &spi->dev;
>> + idev->dev.platform_data = pad;
>> +
>> + /* 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);
>> + input_set_abs_params(idev, ABS_RX, 0, 255, 0, 0);
>> + input_set_abs_params(idev, ABS_RY, 0, 255, 0, 0);
>> +#ifdef PSXPAD_ENABLE_ANALOG2
>> + input_set_abs_params(idev, ABS_HAT0Y, 0, 255, 0, 0); /* up */
>> + input_set_abs_params(idev, ABS_HAT1Y, 0, 255, 0, 0); /* down */
>> + input_set_abs_params(idev, ABS_HAT0X, 0, 255, 0, 0); /* left */
>> + input_set_abs_params(idev, ABS_HAT1X, 0, 255, 0, 0); /* right */
>
> You should map up and down to ABS_HAT0Y and left and right to ABS_HAT0X.
> Using 2 separate sets of hat axes for a single d-pad will likely confuse userspace.
>
>> + input_set_abs_params(idev, ABS_MISC, 0, 255, 0, 0);
>> + input_set_abs_params(idev, ABS_PRESSURE, 0, 255, 0, 0);
>> + input_set_abs_params(idev, ABS_BRAKE, 0, 255, 0, 0);
>> + input_set_abs_params(idev, ABS_THROTTLE, 0, 255, 0, 0);
>
> What buttons/axes do these represent?
>
>> + input_set_abs_params(idev, ABS_HAT2X, 0, 255, 0, 0); /* L1 */
>> + input_set_abs_params(idev, ABS_HAT3X, 0, 255, 0, 0); /* R1 */
>> + input_set_abs_params(idev, ABS_HAT2Y, 0, 255, 0, 0); /* L2 */
>> + input_set_abs_params(idev, ABS_HAT3Y, 0, 255, 0, 0); /* R2 */
>
> L2 and R2 are conventionally ABS_BRAKE and ABS_GAS or ABS_Z and ABS_RZ.
>
>> +#endif /* PSXPAD_ENABLE_ANALOG2 */
>> + input_set_capability(idev, EV_KEY, BTN_DPAD_UP);
>> + input_set_capability(idev, EV_KEY, BTN_DPAD_DOWN);
>> + input_set_capability(idev, EV_KEY, BTN_DPAD_LEFT);
>> + input_set_capability(idev, EV_KEY, BTN_DPAD_RIGHT);
>> + input_set_capability(idev, EV_KEY, BTN_A);
>> + input_set_capability(idev, EV_KEY, BTN_B);
>> + input_set_capability(idev, EV_KEY, BTN_X);
>> + input_set_capability(idev, EV_KEY, BTN_Y);
>> + input_set_capability(idev, EV_KEY, BTN_TL);
>> + input_set_capability(idev, EV_KEY, BTN_TR);
>> + input_set_capability(idev, EV_KEY, BTN_TL2);
>> + input_set_capability(idev, EV_KEY, BTN_TR2);
>> + input_set_capability(idev, EV_KEY, BTN_THUMBL);
>> + input_set_capability(idev, EV_KEY, BTN_THUMBR);
>> + input_set_capability(idev, EV_KEY, BTN_SELECT);
>> + input_set_capability(idev, EV_KEY, BTN_START);
>> +
>> + /* force feedback */
>> + err = psxpad_spi_init_ff(pad);
>> + if (err) {
>> + err = -ENOMEM;
>> + goto err_free_mem;
>> + }
>> +
>> + /* SPI settings */
>> + spi->mode = SPI_MODE_3;
>> + spi->bits_per_word = 8;
>> + /* (PSX pad might be possible works 250kHz/500kHz) */
>> + spi->master->min_speed_hz = 125000;
>> + spi->master->max_speed_hz = 125000;
>> + spi_setup(spi);
>> +
>> + /* pad settings */
>> + psxpad_setmotorlevel(pad, 0, 0);
>> +
>> + /* register input pool device */
>> + err = input_register_polled_device(pdev);
>> + if (err) {
>> + dev_err(&spi->dev, "psxpad-spi: probe: failed register!!\n");
>> + input_free_polled_device(pdev);
>> + goto err_free_mem;
>> + }
>> +
>> + pm_runtime_enable(&spi->dev);
>> +
>> + return 0;
>> +
>> + err_free_mem:
>> + if (pdev) {
>> + psxpad_spi_deinit_ff(pad);
>> + input_free_polled_device(pdev);
>> + }
>> + devm_kfree(&spi->dev, pad);
>> +
>> + return err;
>> +}
>> +
>> +
>> +static int psxpad_spi_remove(struct spi_device *spi)
>> +{
>> + return 0;
>> +}
>> +
>> +static int __maybe_unused psxpad_spi_suspend(struct device *dev)
>> +{
>> + struct spi_device *spi = to_spi_device(dev);
>> + struct psxpad *pad = spi_get_drvdata(spi);
>> +
>> + pad->sus_analog_mode = pad->analog_mode;
>> + pad->sus_mode_lock = pad->mode_lock;
>> + pad->sus_motor1enable = pad->motor1enable;
>> + pad->sus_motor2enable = pad->motor2enable;
>> + pad->sus_motor1level = pad->motor1level;
>> + pad->sus_motor2level = pad->motor2level;
>
> Is there a reason you need a separate set of fields for these?
It is my mistake.
I was confused by misunderstanding the memory state in suspend.
I'll fix.
>> +
>> + psxpad_setadmode(pad, false, false);
>> + psxpad_setmotorlevel(pad, 0, 0);
>> + psxpad_setenablemotor(pad, false, false);
>> +
>> + return 0;
>> +}
>> +
>> +static int __maybe_unused psxpad_spi_resume(struct device *dev)
>> +{
>> + struct spi_device *spi = to_spi_device(dev);
>> + struct psxpad *pad = spi_get_drvdata(spi);
>> +
>> + psxpad_setadmode(pad, pad->sus_analog_mode, pad->sus_mode_lock);
>> + psxpad_setmotorlevel(pad, pad->sus_motor1enable, pad->sus_motor2enable);
>> + psxpad_setenablemotor(pad, pad->sus_motor1level, pad->sus_motor2level);
>> +
>> + return 0;
>> +}
>> +
>> +static SIMPLE_DEV_PM_OPS(psxpad_spi_pm, psxpad_spi_suspend, psxpad_spi_resume);
>> +
>> +static const struct spi_device_id psxpad_spi_id[] = {
>> + { "psxpad-spi", 0 },
>> + { }
>> +};
>> +MODULE_DEVICE_TABLE(spi, psxpad_spi_id);
>> +
>> +static struct spi_driver psxpad_spi_driver = {
>> + .driver = {
>> + .name = "psxpad-spi",
>> + .pm = &psxpad_spi_pm,
>> + },
>> + .id_table = psxpad_spi_id,
>> + .probe = psxpad_spi_probe,
>> + .remove = psxpad_spi_remove,
>> +};
>> +
>> +module_spi_driver(psxpad_spi_driver);
>> +
>> +MODULE_AUTHOR("AZO <[email protected]>");
>> +MODULE_DESCRIPTION("PSX (Play Station 1/2) pad with SPI Bus Driver");
>> +MODULE_LICENSE("GPL");
>>
Regard.
---
Tomohiro Yoshidomi <[email protected]>