From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
To: Jiri Kosina <jikos@kernel.org>,
Benjamin Tissoires <bentiss@kernel.org>,
Gideon Heinz <gideonheinz02@gmail.com>
Cc: "Filipe Laíns" <lains@riseup.net>,
"Bastien Nocera" <hadess@hadess.net>,
"Hans de Goede" <hansg@kernel.org>,
"Edwin Velds" <e.velds@gmail.com>,
"Stuart Hayhurst" <stuart.a.hayhurst@gmail.com>,
"Andrey Smirnov" <andrew.smirnov@gmail.com>,
linux-input@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v2 18/33] HID: logitech-hidpp: rework G920 force-feedback initialization
Date: Tue, 10 Nov 2026 22:48:16 -0800 [thread overview]
Message-ID: <20261110-hid-logitech-hidpp-v2-18-30743e91d83b@gmail.com> (raw)
In-Reply-To: <20261110-hid-logitech-hidpp-v2-0-30743e91d83b@gmail.com>
Querying G920 force-feedback capabilities inside .input_configured()
requires toggling device I/O mid-registration and conflates device
configuration with input setup. At the same time, the single allocated
state structure mixes per-device wheel settings with the lifetime of the
input force-feedback queue, complicating error handling and cleanup.
Exclude generic USB PID force feedback for validated HID++ devices and
query the G920 force-feedback configuration during probe while device
I/O is already active. Keep both probe query failures and force-feedback
initialization errors non-fatal so basic wheel and pedal inputs remain
usable without force feedback, while ensuring failed initialization
leaves input capabilities clean.
Split the overloaded force-feedback state by lifetime so device-level
G920 wheel settings are devm-managed for the bound device lifetime,
while the input force-feedback queue and slot tracking are allocated
with a flexible array and owned strictly by the force-feedback device.
Tested-by: Gideon Heinz <gideonheinz02@gmail.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/hid/hid-logitech-hidpp.c | 210 +++++++++++++++++++--------------------
1 file changed, 101 insertions(+), 109 deletions(-)
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index cf42626ab98d..bad6739c618c 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -2341,15 +2341,19 @@ static void hidpp_touchpad_raw_xy_event(struct hidpp_device *hidpp_dev,
struct hidpp_ff_private_data {
struct hidpp_device *hidpp;
+ struct workqueue_struct *wq;
+ atomic_t workqueue_size;
+ u8 num_effects;
+ int effect_ids[] __counted_by(num_effects);
+};
+
+struct g920_data {
+ struct hidpp_ff_private_data *ff;
u8 feature_index;
- u8 version;
- u16 gain;
- s16 range;
u8 slot_autocenter;
u8 num_effects;
- int *effect_ids;
- struct workqueue_struct *wq;
- atomic_t workqueue_size;
+ u16 gain;
+ u16 range;
};
struct hidpp_ff_work_data {
@@ -2413,6 +2417,7 @@ static void hidpp_ff_work_handler(struct work_struct *w)
{
struct hidpp_ff_work_data *wd = container_of(w, struct hidpp_ff_work_data, work);
struct hidpp_ff_private_data *data = wd->data;
+ struct g920_data *g920 = data->hidpp->private_data;
struct hidpp_report response;
u8 slot;
int ret;
@@ -2420,7 +2425,7 @@ static void hidpp_ff_work_handler(struct work_struct *w)
/* add slot number if needed */
switch (wd->effect_id) {
case HIDPP_FF_EFFECTID_AUTOCENTER:
- wd->params[0] = data->slot_autocenter;
+ wd->params[0] = g920->slot_autocenter;
break;
case HIDPP_FF_EFFECTID_NONE:
/* leave slot as zero */
@@ -2432,8 +2437,9 @@ static void hidpp_ff_work_handler(struct work_struct *w)
}
/* send command and wait for reply */
- ret = hidpp_send_fap_command_sync(data->hidpp, data->feature_index,
- wd->command, wd->params, wd->size, &response);
+ ret = hidpp_send_fap_command_sync(data->hidpp, g920->feature_index,
+ wd->command, wd->params, wd->size,
+ &response);
if (ret) {
hid_err(data->hidpp->hid_dev, "Failed to send command to device!\n");
@@ -2450,7 +2456,7 @@ static void hidpp_ff_work_handler(struct work_struct *w)
data->effect_ids[slot-1] = wd->effect_id;
else if (wd->effect_id >= HIDPP_FF_EFFECTID_AUTOCENTER)
/* autocenter spring uploaded */
- data->slot_autocenter = slot;
+ g920->slot_autocenter = slot;
}
break;
case HIDPP_FF_DESTROY_EFFECT:
@@ -2461,14 +2467,14 @@ static void hidpp_ff_work_handler(struct work_struct *w)
data->effect_ids[slot-1] = -1;
else if (wd->effect_id >= HIDPP_FF_EFFECTID_AUTOCENTER)
/* autocenter spring destroyed */
- data->slot_autocenter = 0;
+ g920->slot_autocenter = 0;
}
break;
case HIDPP_FF_SET_GLOBAL_GAINS:
- data->gain = (wd->params[0] << 8) + wd->params[1];
+ g920->gain = (wd->params[0] << 8) + wd->params[1];
break;
case HIDPP_FF_SET_APERTURE:
- data->range = (wd->params[0] << 8) + wd->params[1];
+ g920->range = (wd->params[0] << 8) + wd->params[1];
break;
default:
/* no action needed */
@@ -2715,19 +2721,17 @@ static void hidpp_ff_set_gain(struct input_dev *dev, u16 gain)
static ssize_t hidpp_ff_range_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct hid_device *hid = to_hid_device(dev);
- struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
- struct input_dev *idev = hidinput->input;
- struct hidpp_ff_private_data *data = idev->ff->private;
+ struct hidpp_device *hidpp = hid_get_drvdata(hid);
+ struct g920_data *g920 = hidpp->private_data;
- return scnprintf(buf, PAGE_SIZE, "%u\n", data->range);
+ return scnprintf(buf, PAGE_SIZE, "%u\n", g920->range);
}
static ssize_t hidpp_ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct hid_device *hid = to_hid_device(dev);
- struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
- struct input_dev *idev = hidinput->input;
- struct hidpp_ff_private_data *data = idev->ff->private;
+ struct hidpp_device *hidpp = hid_get_drvdata(hid);
+ struct g920_data *g920 = hidpp->private_data;
u8 params[2];
int range = simple_strtoul(buf, NULL, 10);
@@ -2736,7 +2740,7 @@ static ssize_t hidpp_ff_range_store(struct device *dev, struct device_attribute
params[0] = range >> 8;
params[1] = range & 0x00FF;
- hidpp_ff_queue_work(data, -1, HIDPP_FF_SET_APERTURE, params, ARRAY_SIZE(params));
+ hidpp_ff_queue_work(g920->ff, -1, HIDPP_FF_SET_APERTURE, params, ARRAY_SIZE(params));
return count;
}
@@ -2746,13 +2750,15 @@ static DEVICE_ATTR(range, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH, hidpp
static void hidpp_ff_destroy(struct ff_device *ff)
{
struct hidpp_ff_private_data *data = ff->private;
- struct hid_device *hid = data->hidpp->hid_dev;
+ struct hidpp_device *hidpp = data->hidpp;
+ struct hid_device *hid = hidpp->hid_dev;
+ struct g920_data *g920 = hidpp->private_data;
hid_info(hid, "Unloading HID++ force feedback.\n");
device_remove_file(&hid->dev, &dev_attr_range);
destroy_workqueue(data->wq);
- kfree(data->effect_ids);
+ g920->ff = NULL;
}
static int g920_input_open(struct input_dev *dev)
@@ -2771,16 +2777,14 @@ static void g920_input_close(struct input_dev *dev)
hid_hw_close(hid);
}
-static int hidpp_ff_init(struct hidpp_device *hidpp,
- struct hidpp_ff_private_data *data)
+static int hidpp_ff_init(struct hidpp_device *hidpp, struct input_dev *dev)
{
struct hid_device *hid = hidpp->hid_dev;
- struct hid_input *hidinput;
- struct input_dev *dev;
+ struct g920_data *g920 = hidpp->private_data;
struct usb_device_descriptor *udesc;
u16 bcdDevice;
struct ff_device *ff;
- int error, j, num_slots = data->num_effects;
+ int error, j, num_slots = g920->num_effects;
u8 version;
if (!hid_is_usb(hid)) {
@@ -2788,17 +2792,22 @@ static int hidpp_ff_init(struct hidpp_device *hidpp,
return -ENODEV;
}
- if (list_empty(&hid->inputs)) {
- hid_err(hid, "no inputs found\n");
- return -ENODEV;
- }
- hidinput = list_entry(hid->inputs.next, struct hid_input, list);
- dev = hidinput->input;
+ struct hidpp_ff_private_data *data __free(kfree) =
+ kzalloc_flex(*data, effect_ids, num_slots);
+ if (!data)
+ return -ENOMEM;
- if (!dev) {
- hid_err(hid, "Struct input_dev not set!\n");
- return -EINVAL;
- }
+ data->hidpp = hidpp;
+ data->num_effects = num_slots;
+ for (j = 0; j < num_slots; j++)
+ data->effect_ids[j] = -1;
+
+ /* init the hardware command queue */
+ atomic_set(&data->workqueue_size, 0);
+
+ data->wq = create_singlethread_workqueue("hidpp-ff-sendqueue");
+ if (!data->wq)
+ return -ENOMEM;
/* Get firmware release */
udesc = &(hid_to_usb_dev(hid)->descriptor);
@@ -2813,38 +2822,16 @@ static int hidpp_ff_init(struct hidpp_device *hidpp,
set_bit(hidpp_ff_effects_v2[j], dev->ffbit);
error = input_ff_create(dev, num_slots - HIDPP_FF_RESERVED_SLOTS);
-
if (error) {
- hid_err(dev, "Failed to create FF device!\n");
+ hid_err(hid, "Failed to create FF device!\n");
+ bitmap_zero(dev->ffbit, FF_CNT);
+ destroy_workqueue(data->wq);
return error;
}
- /*
- * Create a copy of passed data, so we can transfer memory
- * ownership to FF core
- */
- data = kmemdup(data, sizeof(*data), GFP_KERNEL);
- if (!data) {
- error = -ENOMEM;
- goto err_destroy_ff;
- }
- data->effect_ids = kzalloc_objs(int, num_slots);
- if (!data->effect_ids) {
- error = -ENOMEM;
- goto err_free_data;
- }
- data->wq = create_singlethread_workqueue("hidpp-ff-sendqueue");
- if (!data->wq) {
- error = -ENOMEM;
- goto err_free_effect_ids;
- }
-
- data->hidpp = hidpp;
- data->version = version;
- for (j = 0; j < num_slots; j++)
- data->effect_ids[j] = -1;
ff = dev->ff;
- ff->private = data;
+ g920->ff = data;
+ ff->private = no_free_ptr(data);
ff->upload = hidpp_ff_upload_effect;
ff->erase = hidpp_ff_erase_effect;
@@ -2861,21 +2848,10 @@ static int hidpp_ff_init(struct hidpp_device *hidpp,
if (error)
hid_warn(hidpp->hid_dev, "Unable to create sysfs interface for \"range\", errno %d!\n", error);
- /* init the hardware command queue */
- atomic_set(&data->workqueue_size, 0);
-
hid_info(hid, "Force feedback support loaded (firmware release %d).\n",
version);
return 0;
-
-err_free_effect_ids:
- kfree(data->effect_ids);
-err_free_data:
- kfree(data);
-err_destroy_ff:
- input_ff_destroy(dev);
- return error;
}
/* ************************************************************************** */
@@ -3376,7 +3352,7 @@ static int k400_connect(struct hid_device *hdev)
#define HIDPP_PAGE_G920_FORCE_FEEDBACK 0x8123
static int g920_ff_set_autocenter(struct hidpp_device *hidpp,
- struct hidpp_ff_private_data *data)
+ struct g920_data *data)
{
struct hidpp_report response;
u8 params[HIDPP_AUTOCENTER_PARAMS_LENGTH] = {
@@ -3391,71 +3367,83 @@ static int g920_ff_set_autocenter(struct hidpp_device *hidpp,
HIDPP_FF_DOWNLOAD_EFFECT,
params, ARRAY_SIZE(params),
&response);
- if (ret)
+ if (ret) {
hid_warn(hidpp->hid_dev, "Failed to autocenter device!\n");
- else
- data->slot_autocenter = response.fap.params[0];
+ return ret;
+ }
- return ret;
+ data->slot_autocenter = response.fap.params[0];
+ return 0;
}
-static int g920_get_config(struct hidpp_device *hidpp,
- struct hidpp_ff_private_data *data)
+static int g920_get_config(struct hidpp_device *hidpp)
{
+ struct g920_data data = {};
struct hidpp_report response;
int ret;
- memset(data, 0, sizeof(*data));
-
/* Find feature and store for later use */
ret = hidpp_root_get_feature(hidpp, HIDPP_PAGE_G920_FORCE_FEEDBACK,
- &data->feature_index);
+ &data.feature_index);
if (ret)
return ret;
/* Read number of slots available in device */
- ret = hidpp_send_fap_command_sync(hidpp, data->feature_index,
+ ret = hidpp_send_fap_command_sync(hidpp, data.feature_index,
HIDPP_FF_GET_INFO,
NULL, 0,
&response);
if (ret)
return ret;
- data->num_effects = response.fap.params[0];
+ if (response.fap.params[0] <= HIDPP_FF_RESERVED_SLOTS ||
+ response.fap.params[0] > FF_MAX_EFFECTS + HIDPP_FF_RESERVED_SLOTS) {
+ hid_err(hidpp->hid_dev,
+ "Invalid force feedback slot count %u\n",
+ response.fap.params[0]);
+ return -EINVAL;
+ }
+
+ data.num_effects = response.fap.params[0];
/* reset all forces */
- ret = hidpp_send_fap_command_sync(hidpp, data->feature_index,
+ ret = hidpp_send_fap_command_sync(hidpp, data.feature_index,
HIDPP_FF_RESET_ALL,
NULL, 0,
&response);
if (ret)
hid_warn(hidpp->hid_dev, "Failed to reset all forces!\n");
- ret = hidpp_send_fap_command_sync(hidpp, data->feature_index,
+ ret = hidpp_send_fap_command_sync(hidpp, data.feature_index,
HIDPP_FF_GET_APERTURE,
NULL, 0,
&response);
- if (ret) {
+ if (ret)
hid_warn(hidpp->hid_dev,
"Failed to read range from device!\n");
- }
- data->range = ret ?
+ data.range = ret ?
900 : get_unaligned_be16(&response.fap.params[0]);
/* Read the current gain values */
- ret = hidpp_send_fap_command_sync(hidpp, data->feature_index,
+ ret = hidpp_send_fap_command_sync(hidpp, data.feature_index,
HIDPP_FF_GET_GLOBAL_GAINS,
NULL, 0,
&response);
if (ret)
hid_warn(hidpp->hid_dev,
"Failed to read gain values from device!\n");
- data->gain = ret ?
+ data.gain = ret ?
0xffff : get_unaligned_be16(&response.fap.params[0]);
/* ignore boost value at response.fap.params[2] */
- return g920_ff_set_autocenter(hidpp, data);
+ ret = g920_ff_set_autocenter(hidpp, &data);
+ if (ret)
+ return ret;
+
+ hidpp->private_data = devm_kmemdup(&hidpp->hid_dev->dev, &data,
+ sizeof(data), GFP_KERNEL);
+ return hidpp->private_data ? 0 : -ENOMEM;
}
/* -------------------------------------------------------------------------- */
@@ -4040,21 +4028,15 @@ static int hidpp_input_configured(struct hid_device *hdev, struct hid_input *hid
return 0;
if (hidpp->quirks & HIDPP_QUIRK_CLASS_G920) {
- struct hidpp_ff_private_data data;
-
if (!list_is_first(&hidinput->list, &hdev->inputs))
return 0;
- hid_device_io_start(hdev);
- ret = g920_get_config(hidpp, &data);
- hid_device_io_stop(hdev);
- if (!ret)
- ret = hidpp_ff_init(hidpp, &data);
-
- if (ret) {
- hid_warn(hidpp->hid_dev,
- "Unable to initialize force feedback support, errno %d\n",
- ret);
+ if (hidpp->private_data) {
+ ret = hidpp_ff_init(hidpp, input);
+ if (ret)
+ hid_warn(hdev,
+ "Unable to initialize force feedback support, errno %d\n",
+ ret);
}
}
@@ -4633,7 +4615,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct hidpp_device *hidpp;
int ret;
- unsigned int connect_mask = HID_CONNECT_DEFAULT;
+ unsigned int connect_mask;
/* report_fixup needs drvdata to be set before we call hid_parse */
hidpp = devm_kzalloc_obj(&hdev->dev, *hidpp);
@@ -4723,6 +4705,16 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
else
hidpp_non_unifying_init(hidpp);
+ if ((hidpp->quirks & HIDPP_QUIRK_CLASS_G920) && hid_is_usb(hdev)) {
+ ret = g920_get_config(hidpp);
+ if (ret)
+ hid_warn(hdev,
+ "Unable to query force feedback config, errno %d\n",
+ ret);
+ }
+
+ /* None of the HID++ devices use generic USB PID force-feedback. */
+ connect_mask = HID_CONNECT_DEFAULT & ~HID_CONNECT_FF;
if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
connect_mask &= ~HID_CONNECT_HIDINPUT;
--
2.56.0.542.g3ef89b7020-goog
next prev parent reply other threads:[~2026-11-11 6:56 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 6:47 [PATCH v2 00/33] HID: logitech-hidpp: G920 force-feedback fixes, rework, and cleanups Dmitry Torokhov
2026-11-11 6:47 ` [PATCH v2 01/33] HID: logitech-hidpp: fix G920 force-feedback initialization timeout Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 02/33] HID: logitech-hidpp: use GFP_ATOMIC in hidpp_ff_queue_work() Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 03/33] HID: logitech-hidpp: drain G920 FF workqueue before closing transport Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 04/33] HID: logitech-hidpp: disable works before stopping transport Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 05/33] HID: logitech-hidpp: do not store ERR_PTR in battery->ps Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 06/33] HID: logitech-hidpp: avoid NULL subtraction in consumer key report fixup Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 07/33] HID: logitech-hidpp: fix off-by-one in G920 force-feedback slot tracking Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 08/33] HID: logitech-hidpp: do not retry timeouts and transport errors in hidpp_send_message_sync() Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 09/33] HID: logitech-hidpp: initialize response buffer before publishing send_receive_buf Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 10/33] HID: logitech-hidpp: fix HID++ 1.0 battery mileage event parsing Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 11/33] HID: logitech-hidpp: handle negative errors in hidpp20_get_adc_measurement_1f20() Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 12/33] HID: logitech-hidpp: mark device online on unified battery 0x1004 events Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 13/33] HID: logitech-hidpp: fix K750 solar battery feature lookup and reconnect Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 14/33] HID: logitech-hidpp: expose charge_type property for battery voltage feature Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 15/33] HID: logitech-hidpp: keep connect work disabled until hid_connect() completes Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 16/33] HID: logitech-hidpp: preserve transport error codes in __hidpp_send_report() Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 17/33] HID: logitech-hidpp: return negative error codes from hidpp_send_message_sync() Dmitry Torokhov
2026-11-11 6:48 ` Dmitry Torokhov [this message]
2026-11-11 6:48 ` [PATCH v2 19/33] HID: logitech-hidpp: use sysfs_emit() in hidpp_ff_range_show() Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 20/33] HID: logitech-hidpp: manage G920 range sysfs attribute via dev_groups Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 21/33] HID: logitech-hidpp: execute G920 range sysfs store synchronously Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 22/33] HID: logitech-hidpp: shut down G920 force feedback on input unregister Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 23/33] HID: logitech-hidpp: index G920 force-feedback slots by effect ID Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 24/33] HID: logitech-hidpp: execute G920 effect upload and erase synchronously Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 25/33] HID: logitech-hidpp: clean up G920 force-feedback helpers Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 26/33] HID: logitech-hidpp: use unaligned accessors for multi-byte values Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 27/33] HID: logitech-hidpp: store device serial as a byte array Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 28/33] HID: logitech-hidpp: use guard() for send_mutex Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 29/33] HID: logitech-hidpp: use __free(kfree) for temporary allocations Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 30/33] HID: logitech-hidpp: use alloc_ordered_workqueue() for FF queue Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 31/33] HID: logitech-hidpp: manage send_mutex with devm_mutex_init() Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 32/33] HID: logitech-hidpp: manage ps_attribute_group with devm_device_add_group() Dmitry Torokhov
2026-11-11 6:48 ` [PATCH v2 33/33] HID: logitech-hidpp: remove unused kfifo and clean up minor helpers Dmitry Torokhov
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=20261110-hid-logitech-hidpp-v2-18-30743e91d83b@gmail.com \
--to=dmitry.torokhov@gmail.com \
--cc=andrew.smirnov@gmail.com \
--cc=bentiss@kernel.org \
--cc=e.velds@gmail.com \
--cc=gideonheinz02@gmail.com \
--cc=hadess@hadess.net \
--cc=hansg@kernel.org \
--cc=jikos@kernel.org \
--cc=lains@riseup.net \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=stuart.a.hayhurst@gmail.com \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®