From: "Erik Håkansson" <erikhakan@gmail.com>
To: jikos@kernel.org, bentiss@kernel.org
Cc: lains@riseup.net, hadess@hadess.net, k8ie@mcld.eu,
linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
"Erik Håkansson" <erikhakan@gmail.com>
Subject: [PATCH v2 1/1] HID: logitech: add Bolt receiver support for Logitech HID++ devices
Date: Mon, 13 Jul 2026 22:12:01 +0200 [thread overview]
Message-ID: <20260713201201.391538-2-erikhakan@gmail.com> (raw)
In-Reply-To: <20260713201201.391538-1-erikhakan@gmail.com>
Add Logitech Bolt receiver support to the Logitech HID receiver and
HID++ drivers.
Handle Bolt receiver notifications in hid-logitech-dj and add a
Bolt-specific initialization path in hid-logitech-hidpp, separate from
the existing Unifying receiver path.
This allows Bolt-connected HID++ devices to expose battery information
through the kernel power_supply path, so userspace tools can report
their battery status with the correct device model.
Tested with:
- Logitech MX Keys for Business via Bolt receiver
Reported-by: Kateřina Medvědová <k8ie@mcld.eu>
Link: https://lore.kernel.org/linux-input/345f7347-30a8-4568-a7a6-c70f54a52a8d@mcld.eu/
Signed-off-by: Erik Håkansson <erikhakan@gmail.com>
---
Changes in v2:
- Handle only the Bolt receiver/control interface in hid-logitech-dj and
leave the other Bolt receiver interfaces to generic HID handling.
- Handle Bolt HID++ unpair notifications so child HID devices are removed
after unpairing.
drivers/hid/hid-logitech-dj.c | 48 +++++++++++++++++++++++++++++---
drivers/hid/hid-logitech-hidpp.c | 48 ++++++++++++++++++++++++++++++--
2 files changed, 89 insertions(+), 7 deletions(-)
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index 9c574ab8b60b..571d5caa5bb5 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -121,6 +121,7 @@ enum recvr_type {
recvr_type_27mhz,
recvr_type_bluetooth,
recvr_type_dinovo,
+ recvr_type_bolt,
};
struct dj_report {
@@ -1156,6 +1157,10 @@ static void logi_hidpp_recv_queue_notif(struct hid_device *hdev,
logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
workitem.reports_supported |= STD_KEYBOARD;
break;
+ case 0x10:
+ device_type = "Bolt";
+ logi_hidpp_dev_conn_notif_equad(hdev, hidpp_report, &workitem);
+ break;
}
/* custom receiver device (eg. powerplay) */
@@ -1745,6 +1750,24 @@ static int logi_dj_hidpp_event(struct hid_device *hdev,
dj_dev = djrcv_dev->paired_dj_devices[device_index];
+ /*
+ * Bolt receivers send explicit unpair notifications as HID++ events;
+ * queue device removal when we receive one.
+ */
+ if (djrcv_dev->type == recvr_type_bolt &&
+ hidpp_report->report_id == REPORT_ID_HIDPP_SHORT &&
+ hidpp_report->sub_id == REPORT_TYPE_NOTIF_DEVICE_UNPAIRED) {
+ struct dj_workitem workitem = {
+ .device_index = device_index,
+ .type = WORKITEM_TYPE_UNPAIRED,
+ };
+
+ kfifo_in(&djrcv_dev->notif_fifo, &workitem, sizeof(workitem));
+ schedule_work(&djrcv_dev->work);
+ spin_unlock_irqrestore(&djrcv_dev->lock, flags);
+ return false;
+ }
+
/*
* With 27 MHz receivers, we do not get an explicit unpair event,
* remove the old device if the user has paired a *different* device.
@@ -1884,6 +1907,9 @@ static int logi_dj_probe(struct hid_device *hdev,
* treat these as logitech-dj interfaces then this causes input events
* reported through this extra interface to not be reported correctly.
* To avoid this, we treat these as generic-hid devices.
+ *
+ * Bolt receivers only use LOGITECH_DJ_INTERFACE_NUMBER for receiver
+ * reporting. Treat all other Bolt interfaces as generic-hid devices.
*/
switch (id->driver_data) {
case recvr_type_dj: no_dj_interfaces = 3; break;
@@ -1897,10 +1923,20 @@ static int logi_dj_probe(struct hid_device *hdev,
}
if (hid_is_usb(hdev)) {
intf = to_usb_interface(hdev->dev.parent);
- if (intf && intf->altsetting->desc.bInterfaceNumber >=
- no_dj_interfaces) {
- hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
- return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ if (intf) {
+ bool generic_hid_interface;
+
+ if (id->driver_data == recvr_type_bolt)
+ generic_hid_interface =
+ intf->altsetting->desc.bInterfaceNumber !=
+ LOGITECH_DJ_INTERFACE_NUMBER;
+ else
+ generic_hid_interface =
+ intf->altsetting->desc.bInterfaceNumber >= no_dj_interfaces;
+ if (generic_hid_interface) {
+ hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
+ return hid_hw_start(hdev, HID_CONNECT_DEFAULT);
+ }
}
}
@@ -2103,6 +2139,10 @@ static const struct hid_device_id logi_dj_receivers[] = {
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_3),
.driver_data = recvr_type_gaming_hidpp_ls_1_3},
+ { /* Logitech Bolt receiver (0xc548) */
+ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
+ USB_DEVICE_ID_LOGITECH_BOLT_RECEIVER),
+ .driver_data = recvr_type_bolt},
{ /* Logitech lightspeed receiver (0xc54d) */
HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
USB_DEVICE_ID_LOGITECH_NANO_RECEIVER_LIGHTSPEED_1_4),
diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c
index 90b0184df777..0b5d0a322ae8 100644
--- a/drivers/hid/hid-logitech-hidpp.c
+++ b/drivers/hid/hid-logitech-hidpp.c
@@ -4161,8 +4161,50 @@ static int hidpp_initialize_battery(struct hidpp_device *hidpp)
return ret;
}
+static bool hidpp_is_bolt_child(struct hid_device *hdev)
+{
+ struct device *parent = hdev->dev.parent;
+ struct hid_device *receiver_hdev;
+
+ if (!parent)
+ return false;
+
+ receiver_hdev = to_hid_device(parent);
+ return receiver_hdev->vendor == USB_VENDOR_ID_LOGITECH &&
+ receiver_hdev->product == USB_DEVICE_ID_LOGITECH_BOLT_RECEIVER;
+}
+
+static int hidpp_bolt_init(struct hidpp_device *hidpp)
+{
+ struct hid_device *hdev = hidpp->hid_dev;
+ char *name;
+ int ret;
+
+ ret = hidpp_serial_init(hidpp);
+ if (ret)
+ return ret;
+
+ name = hidpp_get_device_name(hidpp);
+ if (!name)
+ return -EIO;
+
+ snprintf(hdev->name, sizeof(hdev->name), "%s", name);
+ dbg_hid("HID++ Bolt: Got name: %s\n", name);
+
+ kfree(name);
+ return 0;
+}
+
+static int hidpp_receiver_init(struct hidpp_device *hidpp)
+{
+ if (hidpp_is_bolt_child(hidpp->hid_dev))
+ return hidpp_bolt_init(hidpp);
+
+ return hidpp_unifying_init(hidpp);
+}
+
/* Get name + serial for USB and Bluetooth HID++ devices */
-static void hidpp_non_unifying_init(struct hidpp_device *hidpp)
+static void hidpp_non_receiver_init(struct hidpp_device *hidpp)
{
struct hid_device *hdev = hidpp->hid_dev;
char *name;
@@ -4510,9 +4552,9 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id)
/* Get name + serial, store in hdev->name + hdev->uniq */
if (id->group == HID_GROUP_LOGITECH_DJ_DEVICE)
- hidpp_unifying_init(hidpp);
+ hidpp_receiver_init(hidpp);
else
- hidpp_non_unifying_init(hidpp);
+ hidpp_non_receiver_init(hidpp);
if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
connect_mask &= ~HID_CONNECT_HIDINPUT;
--
2.55.0
next prev parent reply other threads:[~2026-07-13 20:12 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-12 0:30 [PATCH 0/1] HID: logitech: add Bolt receiver support for Logitech HID++ devices Erik Håkansson
2026-07-12 0:30 ` [PATCH 1/1] " Erik Håkansson
2026-07-12 18:03 ` Kateřina Medvědová
2026-07-13 20:10 ` Erik Håkansson
2026-07-13 20:12 ` [PATCH v2 0/1] " Erik Håkansson
2026-07-13 20:12 ` Erik Håkansson [this message]
2026-07-13 21:09 ` [PATCH v2 1/1] " Kateřina Medvědová
2026-07-14 0:54 ` Erik Håkansson
2026-07-14 10:32 ` Kateřina Medvědová
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=20260713201201.391538-2-erikhakan@gmail.com \
--to=erikhakan@gmail.com \
--cc=bentiss@kernel.org \
--cc=hadess@hadess.net \
--cc=jikos@kernel.org \
--cc=k8ie@mcld.eu \
--cc=lains@riseup.net \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox