From: Dmitry Torokhov <dmitry.torokhov@gmail.com>
To: Zhengping Jiang <jiangzp@google.com>
Cc: Jiri Kosina <jikos@kernel.org>,
Benjamin Tissoires <bentiss@kernel.org>,
Stephen Boyd <swboyd@chromium.org>,
Archie Pusaka <apusaka@google.com>,
Shashikirana H K <shashikiranah@chromium.org>,
linux-input@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v3] HID: vivaldi: avoid redundant feature report reads per usage
Date: Wed, 14 Oct 2026 22:32:52 -0700 [thread overview]
Message-ID: <01c8cf7d695261ba8270ff57aa8f2a6f@gmail.com> (raw)
In-Reply-To: <20261013192318.1828453-1-jiangzp@google.com>
Hi Zhengping,
On Tue, Oct 13, 2026 at 07:23:18PM +0000, Zhengping Jiang wrote:
> vivaldi_feature_mapping() is invoked by hidinput_configure_usages() in
> ascending usage_index order for each usage in the top-row physical keymap
> field. Because fetching the feature report at usage_index == 0 already
> reads the entire report from hardware and populates field->value[] for all
> usages in the field, calling hid_hw_raw_request() again for subsequent
> usages redundantly reads the same report N times (e.g., 12 sequential BLE
> GATT reads taking ~2s on Vivaldi BLE keyboards).
A minor note on the commit description: drv->feature_mapping() is invoked
by report_features(), not hidinput_configure_usages().
> diff --git a/drivers/hid/hid-vivaldi-common.c b/drivers/hid/hid-vivaldi-common.c
> index c3c9fe9e697b..2d85555289d8 100644
> --- a/drivers/hid/hid-vivaldi-common.c
> +++ b/drivers/hid/hid-vivaldi-common.c
> @@ -33,21 +33,22 @@ void vivaldi_feature_mapping(struct hid_device *hdev,
> {
> struct vivaldi_data *data = hid_get_drvdata(hdev);
> struct hid_report *report = field->report;
> - u8 *report_data, *buf;
> + u8 *report_data, *buf = NULL;
> u32 report_len;
> unsigned int fn_key;
> int ret;
>
> - if (field->logical != HID_USAGE_FN_ROW_PHYSMAP ||
> - (usage->hid & HID_USAGE_PAGE) != HID_UP_ORDINAL)
> + if (field->logical != HID_USAGE_FN_ROW_PHYSMAP)
> return;
>
> - fn_key = usage->hid & HID_USAGE;
> - if (fn_key < MIN_FN_ROW_KEY || fn_key > MAX_FN_ROW_KEY)
> - return;
> -
> - if (fn_key > data->num_function_row_keys)
> - data->num_function_row_keys = fn_key;
> + /*
> + * Read the feature report only at usage_index == 0, as a single read
> + * populates field->value[] for all usages in the field. If the read
> + * fails, subsequent usages will not re-read the report and will rely
> + * on field->value[] being zero-initialized.
> + */
> + if (usage->usage_index)
> + goto parse_usage;
>
> report_data = buf = hid_alloc_report_buf(report, GFP_KERNEL);
> if (!report_data)
> @@ -72,7 +73,7 @@ void vivaldi_feature_mapping(struct hid_device *hdev,
> if (ret < 0) {
> dev_warn(&hdev->dev, "failed to fetch feature %d\n",
> field->report->id);
> - goto out;
> + goto parse_usage;
> }
>
> if (!report->id) {
> @@ -86,11 +87,20 @@ void vivaldi_feature_mapping(struct hid_device *hdev,
>
> ret = __hid_report_raw_event(hdev, HID_FEATURE_REPORT, report_data,
> report_len, report_len, 0);
It looks like this patch was generated against a downstream ChromeOS
tree (__hid_report_raw_event() does not exist upstream; mainline uses
hid_report_raw_event()), so it does not apply cleanly.
> - if (ret) {
> + if (ret)
> dev_warn(&hdev->dev, "failed to report feature %d\n",
> field->report->id);
> +
> +parse_usage:
> + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_ORDINAL)
> goto out;
> - }
> +
> + fn_key = usage->hid & HID_USAGE;
> + if (fn_key < MIN_FN_ROW_KEY || fn_key > MAX_FN_ROW_KEY)
> + goto out;
> +
> + if (fn_key > data->num_function_row_keys)
> + data->num_function_row_keys = fn_key;
>
> data->function_row_physmap[fn_key - MIN_FN_ROW_KEY] =
> field->value[usage->usage_index];
If reading or parsing the feature report fails, we should not be
updating data->num_function_row_keys or populating
data->function_row_physmap[] with zeros: vivaldi_is_visible() checks
data->num_function_row_keys to decide whether to expose the
function_row_physmap sysfs attribute, and on a failed read we do not
want to expose a bogus all-zero keymap to userspace.
Also, keeping "out: kfree(buf);" at the very end of the function across
parse_usage makes the control flow and cleanup awkward (having to
initialize buf to NULL, jumping over the allocation on usage_index > 0,
and using goto out in parse_usage even though buf is no longer needed
once the report is processed), and it prevents using __free(kfree) for
buf.
Since vivaldi_feature_mapping() already has the entire field in hand,
why don't we bail out immediately when usage->usage_index != 0, fetch
the report once at usage_index == 0 using __free(kfree), and iterate
over all usages in the field right there?
Please rebase your patch on top of Shashikirana's patch extracting the
feature report value directly via hid_field_extract() before
resubmitting:
https://lore.kernel.org/r/20261013-upstream-vivaldi-fix-v2-1-ecc20329d24c@chromium.org
With that in place, vivaldi_feature_mapping() can simply become:
if (field->logical != HID_USAGE_FN_ROW_PHYSMAP || usage->usage_index)
return;
u8 *buf __free(kfree) = hid_alloc_report_buf(report, GFP_KERNEL);
if (!buf)
return;
report_len = hid_report_len(report) + !report->id;
ret = hid_hw_raw_request(hdev, report->id, buf, report_len,
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
if (ret != report_len) {
dev_warn(&hdev->dev, "failed to fetch feature %d: %d\n",
report->id, ret);
return;
}
for (i = 0; i < field->maxusage && i < field->report_count; i++) {
usage = &field->usage[i];
if ((usage->hid & HID_USAGE_PAGE) != HID_UP_ORDINAL)
continue;
fn_key = usage->hid & HID_USAGE;
if (fn_key < MIN_FN_ROW_KEY || fn_key > MAX_FN_ROW_KEY)
continue;
if (fn_key > data->num_function_row_keys)
data->num_function_row_keys = fn_key;
offset = field->report_offset + i * field->report_size;
data->function_row_physmap[fn_key - MIN_FN_ROW_KEY] =
hid_field_extract(hdev, buf + 1, offset,
field->report_size);
}
Thanks.
--
Dmitry
next prev parent reply other threads:[~2026-10-15 5:32 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-12 21:29 [PATCH] HID: vivaldi: avoid redundant feature report reads per usage Zhengping Jiang
2026-10-12 21:37 ` sashiko-bot
2026-10-12 22:02 ` [PATCH v2] " Zhengping Jiang
2026-10-13 19:23 ` [PATCH v3] " Zhengping Jiang
2026-10-15 5:32 ` Dmitry Torokhov [this message]
2026-10-15 6:42 ` [PATCH v4] " Zhengping Jiang
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=01c8cf7d695261ba8270ff57aa8f2a6f@gmail.com \
--to=dmitry.torokhov@gmail.com \
--cc=apusaka@google.com \
--cc=bentiss@kernel.org \
--cc=jiangzp@google.com \
--cc=jikos@kernel.org \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=shashikiranah@chromium.org \
--cc=swboyd@chromium.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.