From: clicedvly768 <harepro29@gmail.com>
To: ojeda@kernel.org, dmitry.torokhov@gmail.com
Cc: rust-for-linux@vger.kernel.org, linux-input@vger.kernel.org,
clicedvly768 <harepro29@gmail.com>
Subject: [PATCH 3/3] input: joystick: stinger: port Gravis Stinger driver to safe Rust
Date: Sat, 7 Nov 2026 00:17:14 +0500 [thread overview]
Message-ID: <20261106191714.334055-4-harepro29@gmail.com> (raw)
In-Reply-To: <20261106191714.334055-1-harepro29@gmail.com>
Rewrite the legacy Gravis Stinger serial gamepad driver in safe Rust,
replacing drivers/input/joystick/stinger.c with stinger.rs.
The new implementation:
- Uses the newly introduced `kernel::input` and `kernel::serio`
abstractions.
- Contains zero `unsafe` blocks in the driver code itself.
- Preserves full protocol compatibility with the Gravis Stinger hardware
(9600 baud, 8N1, 4-byte packet format with 10 buttons and 2 axes).
- Adds `depends on RUST` to CONFIG_JOYSTICK_STINGER in Kconfig.
Signed-off-by: clicedvly768 <harepro29@gmail.com>
---
drivers/input/joystick/Kconfig | 1 +
drivers/input/joystick/stinger.c | 191 ------------------------------
drivers/input/joystick/stinger.rs | 174 +++++++++++++++++++++++++++
3 files changed, 175 insertions(+), 191 deletions(-)
delete mode 100644 drivers/input/joystick/stinger.c
create mode 100644 drivers/input/joystick/stinger.rs
diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index 7755e5b454d2..e672a3bc6dcc 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -188,6 +188,7 @@ config JOYSTICK_SPACEBALL
module will be called spaceball.
config JOYSTICK_STINGER
+ depends on RUST
tristate "Gravis Stinger gamepad"
select SERIO
help
diff --git a/drivers/input/joystick/stinger.c b/drivers/input/joystick/stinger.c
deleted file mode 100644
index 011f9b137dca..000000000000
--- a/drivers/input/joystick/stinger.c
+++ /dev/null
@@ -1,191 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * Copyright (c) 2000-2001 Vojtech Pavlik
- * Copyright (c) 2000 Mark Fletcher
- */
-
-/*
- * Gravis Stinger gamepad driver for Linux
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/input.h>
-#include <linux/serio.h>
-
-#define DRIVER_DESC "Gravis Stinger gamepad driver"
-
-MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL");
-
-/*
- * Constants.
- */
-
-#define STINGER_MAX_LENGTH 8
-
-/*
- * Per-Stinger data.
- */
-
-struct stinger {
- struct input_dev *dev;
- int idx;
- unsigned char data[STINGER_MAX_LENGTH];
- char phys[32];
-};
-
-/*
- * stinger_process_packet() decodes packets the driver receives from the
- * Stinger. It updates the data accordingly.
- */
-
-static void stinger_process_packet(struct stinger *stinger)
-{
- struct input_dev *dev = stinger->dev;
- unsigned char *data = stinger->data;
-
- if (!stinger->idx) return;
-
- input_report_key(dev, BTN_A, ((data[0] & 0x20) >> 5));
- input_report_key(dev, BTN_B, ((data[0] & 0x10) >> 4));
- input_report_key(dev, BTN_C, ((data[0] & 0x08) >> 3));
- input_report_key(dev, BTN_X, ((data[0] & 0x04) >> 2));
- input_report_key(dev, BTN_Y, ((data[3] & 0x20) >> 5));
- input_report_key(dev, BTN_Z, ((data[3] & 0x10) >> 4));
- input_report_key(dev, BTN_TL, ((data[3] & 0x08) >> 3));
- input_report_key(dev, BTN_TR, ((data[3] & 0x04) >> 2));
- input_report_key(dev, BTN_SELECT, ((data[3] & 0x02) >> 1));
- input_report_key(dev, BTN_START, (data[3] & 0x01));
-
- input_report_abs(dev, ABS_X, (data[1] & 0x3F) - ((data[0] & 0x01) << 6));
- input_report_abs(dev, ABS_Y, ((data[0] & 0x02) << 5) - (data[2] & 0x3F));
-
- input_sync(dev);
-
- return;
-}
-
-/*
- * stinger_interrupt() is called by the low level driver when characters
- * are ready for us. We then buffer them for further processing, or call the
- * packet processing routine.
- */
-
-static irqreturn_t stinger_interrupt(struct serio *serio,
- unsigned char data, unsigned int flags)
-{
- struct stinger *stinger = serio_get_drvdata(serio);
-
- /* All Stinger packets are 4 bytes */
-
- if (stinger->idx < STINGER_MAX_LENGTH)
- stinger->data[stinger->idx++] = data;
-
- if (stinger->idx == 4) {
- stinger_process_packet(stinger);
- stinger->idx = 0;
- }
-
- return IRQ_HANDLED;
-}
-
-/*
- * stinger_disconnect() is the opposite of stinger_connect()
- */
-
-static void stinger_disconnect(struct serio *serio)
-{
- struct stinger *stinger = serio_get_drvdata(serio);
-
- serio_close(serio);
- serio_set_drvdata(serio, NULL);
- input_unregister_device(stinger->dev);
- kfree(stinger);
-}
-
-/*
- * stinger_connect() is the routine that is called when someone adds a
- * new serio device that supports Stinger protocol and registers it as
- * an input device.
- */
-
-static int stinger_connect(struct serio *serio, struct serio_driver *drv)
-{
- struct stinger *stinger;
- struct input_dev *input_dev;
- int err = -ENOMEM;
-
- stinger = kmalloc_obj(*stinger);
- input_dev = input_allocate_device();
- if (!stinger || !input_dev)
- goto fail1;
-
- stinger->dev = input_dev;
- snprintf(stinger->phys, sizeof(stinger->phys), "%s/serio0", serio->phys);
-
- input_dev->name = "Gravis Stinger";
- input_dev->phys = stinger->phys;
- input_dev->id.bustype = BUS_RS232;
- input_dev->id.vendor = SERIO_STINGER;
- input_dev->id.product = 0x0001;
- input_dev->id.version = 0x0100;
- input_dev->dev.parent = &serio->dev;
-
- input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- input_dev->keybit[BIT_WORD(BTN_A)] = BIT_MASK(BTN_A) | BIT_MASK(BTN_B) |
- BIT_MASK(BTN_C) | BIT_MASK(BTN_X) | BIT_MASK(BTN_Y) |
- BIT_MASK(BTN_Z) | BIT_MASK(BTN_TL) | BIT_MASK(BTN_TR) |
- BIT_MASK(BTN_START) | BIT_MASK(BTN_SELECT);
- input_set_abs_params(input_dev, ABS_X, -64, 64, 0, 4);
- input_set_abs_params(input_dev, ABS_Y, -64, 64, 0, 4);
-
- serio_set_drvdata(serio, stinger);
-
- err = serio_open(serio, drv);
- if (err)
- goto fail2;
-
- err = input_register_device(stinger->dev);
- if (err)
- goto fail3;
-
- return 0;
-
- fail3: serio_close(serio);
- fail2: serio_set_drvdata(serio, NULL);
- fail1: input_free_device(input_dev);
- kfree(stinger);
- return err;
-}
-
-/*
- * The serio driver structure.
- */
-
-static const struct serio_device_id stinger_serio_ids[] = {
- {
- .type = SERIO_RS232,
- .proto = SERIO_STINGER,
- .id = SERIO_ANY,
- .extra = SERIO_ANY,
- },
- { 0 }
-};
-
-MODULE_DEVICE_TABLE(serio, stinger_serio_ids);
-
-static struct serio_driver stinger_drv = {
- .driver = {
- .name = "stinger",
- },
- .description = DRIVER_DESC,
- .id_table = stinger_serio_ids,
- .interrupt = stinger_interrupt,
- .connect = stinger_connect,
- .disconnect = stinger_disconnect,
-};
-
-module_serio_driver(stinger_drv);
diff --git a/drivers/input/joystick/stinger.rs b/drivers/input/joystick/stinger.rs
new file mode 100644
index 000000000000..daa199463631
--- /dev/null
+++ b/drivers/input/joystick/stinger.rs
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+//! Gravis Stinger gamepad driver written in safe Rust.
+//!
+//! Rust port of legacy C driver `drivers/input/joystick/stinger.c`.
+//!
+//! Authors:
+//! - Original C driver: Vojtech Pavlik <vojtech@ucw.cz>, Mark Fletcher
+//! - Rust port: Rust for Linux Contributors
+
+use kernel::{
+ bindings,
+ input::{bus, event_type, AllocatedDevice, RegisteredDevice},
+ prelude::*,
+ serio,
+ types::ForeignOwnable,
+};
+
+const DRIVER_NAME: &CStr = c"stinger";
+const DRIVER_DESC: &CStr = c"Gravis Stinger gamepad";
+
+const STINGER_MAX_LENGTH: usize = 8;
+
+/// Per-device state for an attached Gravis Stinger gamepad.
+struct Stinger {
+ input: RegisteredDevice,
+ idx: usize,
+ data: [u8; STINGER_MAX_LENGTH],
+}
+
+impl Stinger {
+ /// Decodes incoming 4-byte packets from the Gravis Stinger and reports input events.
+ fn process_packet(&self) {
+ if self.idx == 0 {
+ return;
+ }
+
+ let d0 = i32::from(self.data[0]);
+ let d1 = i32::from(self.data[1]);
+ let d2 = i32::from(self.data[2]);
+ let d3 = i32::from(self.data[3]);
+
+ // Report button states (active high bits in packet)
+ self.input
+ .report_key(bindings::BTN_A, ((d0 & 0x20) >> 5) != 0);
+ self.input
+ .report_key(bindings::BTN_B, ((d0 & 0x10) >> 4) != 0);
+ self.input
+ .report_key(bindings::BTN_C, ((d0 & 0x08) >> 3) != 0);
+ self.input
+ .report_key(bindings::BTN_X, ((d0 & 0x04) >> 2) != 0);
+ self.input
+ .report_key(bindings::BTN_Y, ((d3 & 0x20) >> 5) != 0);
+ self.input
+ .report_key(bindings::BTN_Z, ((d3 & 0x10) >> 4) != 0);
+ self.input
+ .report_key(bindings::BTN_TL, ((d3 & 0x08) >> 3) != 0);
+ self.input
+ .report_key(bindings::BTN_TR, ((d3 & 0x04) >> 2) != 0);
+ self.input
+ .report_key(bindings::BTN_SELECT, ((d3 & 0x02) >> 1) != 0);
+ self.input.report_key(bindings::BTN_START, (d3 & 0x01) != 0);
+
+ // Report 2-axis position: coordinates in range -64..64
+ self.input
+ .report_abs(bindings::ABS_X, (d1 & 0x3f) - ((d0 & 0x01) << 6));
+ self.input
+ .report_abs(bindings::ABS_Y, ((d0 & 0x02) << 5) - (d2 & 0x3f));
+
+ self.input.sync();
+ }
+}
+
+/// The safe serio driver implementation.
+struct StingerDriver;
+
+impl serio::Driver for StingerDriver {
+ type Data = KBox<Stinger>;
+
+ const ID_TABLE: &'static [serio::DeviceId] = &[
+ serio::DeviceId::new(
+ bindings::SERIO_RS232 as u8,
+ bindings::SERIO_ANY as u8,
+ bindings::SERIO_ANY as u8,
+ bindings::SERIO_STINGER as u8,
+ ),
+ serio::DeviceId::new(0, 0, 0, 0),
+ ];
+
+ fn connect(dev: &mut serio::Device) -> Result<Self::Data> {
+ let mut input_alloc = AllocatedDevice::new()?;
+
+ input_alloc.set_name(DRIVER_DESC);
+ input_alloc.set_phys(dev.phys().as_ptr());
+ input_alloc.set_id(bus::RS232, bindings::SERIO_STINGER as u16, 0x0001, 0x0100);
+
+ // Configure supported buttons
+ for btn in [
+ bindings::BTN_A,
+ bindings::BTN_B,
+ bindings::BTN_C,
+ bindings::BTN_X,
+ bindings::BTN_Y,
+ bindings::BTN_Z,
+ bindings::BTN_TL,
+ bindings::BTN_TR,
+ bindings::BTN_START,
+ bindings::BTN_SELECT,
+ ] {
+ input_alloc.set_capability(event_type::KEY, btn);
+ }
+
+ // Configure 2-axis joystick with flat margin of 4
+ input_alloc.set_capability(event_type::ABS, bindings::ABS_X);
+ input_alloc.set_capability(event_type::ABS, bindings::ABS_Y);
+ input_alloc.set_abs_params(bindings::ABS_X, -64, 64, 0, 4);
+ input_alloc.set_abs_params(bindings::ABS_Y, -64, 64, 0, 4);
+
+ let registered = input_alloc.register()?;
+
+ let stinger = KBox::new(
+ Stinger {
+ input: registered,
+ idx: 0,
+ data: [0; STINGER_MAX_LENGTH],
+ },
+ GFP_KERNEL,
+ )?;
+
+ Ok(stinger)
+ }
+
+ fn interrupt(
+ stinger: &mut <Self::Data as ForeignOwnable>::BorrowedMut<'_>,
+ data: u8,
+ _flags: u32,
+ ) -> serio::IrqReturn {
+ if stinger.idx < STINGER_MAX_LENGTH {
+ stinger.data[stinger.idx] = data;
+ stinger.idx += 1;
+ }
+
+ if stinger.idx == 4 {
+ stinger.process_packet();
+ stinger.idx = 0;
+ }
+
+ serio::IrqReturn::Handled
+ }
+
+ fn disconnect(_data: Self::Data) {
+ // Automatically drops `stinger`, unregistering and freeing the input device.
+ }
+}
+
+module! {
+ type: StingerModule,
+ name: "stinger",
+ authors: ["Rust for Linux Contributors"],
+ description: "Gravis Stinger gamepad driver in safe Rust",
+ license: "GPL",
+}
+
+struct StingerModule {
+ _reg: serio::Registration<StingerDriver>,
+}
+
+impl kernel::Module for StingerModule {
+ fn init(_module: &'static ThisModule) -> Result<Self> {
+ let reg = serio::Registration::<StingerDriver>::new(DRIVER_NAME)?;
+ pr_info!("Gravis Stinger safe Rust driver initialized\n");
+ Ok(Self { _reg: reg })
+ }
+}
--
2.56.0
next prev parent reply other threads:[~2026-11-06 19:17 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-06 19:17 [PATCH 0/3] rust: input: add input/serio abstractions and port Gravis Stinger driver clicedvly768
2026-11-06 19:17 ` [PATCH 1/3] rust: input: add input device abstractions clicedvly768
2026-11-06 19:29 ` sashiko-bot
2026-11-06 19:17 ` [PATCH 2/3] rust: serio: add serio bus abstractions clicedvly768
2026-11-06 19:35 ` sashiko-bot
2026-11-06 19:17 ` clicedvly768 [this message]
2026-11-06 19:42 ` [PATCH 3/3] input: joystick: stinger: port Gravis Stinger driver to safe Rust sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261106191714.334055-4-harepro29@gmail.com \
--to=harepro29@gmail.com \
--cc=dmitry.torokhov@gmail.com \
--cc=linux-input@vger.kernel.org \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.