From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qv1-f53.google.com (mail-qv1-f53.google.com [209.85.219.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C81982D47E9 for ; Fri, 27 Feb 2026 23:50:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236249; cv=none; b=QM9Fiqk9uVlp79JD+GFO54PnRPGmZBYATdzUUJd4GHazdHy1E+HRDlS3M4oL2zUfKqDctlehNRQPOQB0aOWjDy4v8/wZqe4BfYTyXmSkZXvARWjcswK8XbPMrexdEteW1Iy024u3LKaaueF+o51K+qbEAWqyc+6rhrGpVs6lOzI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236249; c=relaxed/simple; bh=s+WidDgBLF9kLXXn8dGwBA9EPQ87Q1ovYlZ6tuz3ICQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=NjkrNcHAJANCp73udL3xG9/hnbveoAtFJUixSgkPBtLHidR+i+aJPviJo9c1sRD6L7VG5OfhQQ0R0UBxol7Cz5lQwPpJ8Pvmh8eJHerQ9/ReeUsaIIZp0QxtTUcim0U0hyWPY/L9vXWBkQhBcUtuShRpJRIo+TuI0YHKiQ8DZTg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=IXdZFnwK; arc=none smtp.client-ip=209.85.219.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="IXdZFnwK" Received: by mail-qv1-f53.google.com with SMTP id 6a1803df08f44-899c97c5afeso18220526d6.1 for ; Fri, 27 Feb 2026 15:50:47 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236247; x=1772841047; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=QQdREBEObX79yVzADeo7mQ1BiSM/IKbQDs531YvSTqg=; b=IXdZFnwKEli79HVncv59WxVX03TgXUvz6Y0hgmiv6DAMKRg4/TOQ8XNw9JW7QZ9QQ9 39XxO8LSaPlzrfqxvU8OJTlwDayqT1ZTZga0Llox4NsvFh0L+xe5b8t75QrLBoGelkeJ MOgjADTh5/ABprRcFJf9wkISIdroF2ujUe7nFzId0TMjylBDMZALom2jw+PFEz8eeRP2 H68LgE+C0VvMFp4mRXOWiboWve3w79mceX2jjOSoYdz9umCmGpeenpvs/dGoRB9ElrQk JAMw5NwQ3lSQWerAJnhAEvolo3yJX5yN80HfhJm4+K9YoU4KAyYdfrTXV7rLjLroUURP tp0w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236247; x=1772841047; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=QQdREBEObX79yVzADeo7mQ1BiSM/IKbQDs531YvSTqg=; b=I5p8Sbai9MmAvLVlWY8/saI5i882rt348NxN9NaElT9o6cQ2BeXUPze6pYU8aeq9mY 7ITpwHvzGGiPbQHP4NG1DSvGM+viuG43hxJEsC6cTVlUPL7AO55p1TKD2jEU5Q4cdXyS F6Y5DcrsFiASSfPh0dtLx0WYARpUr1zHUqycdEQOKkKHBiDkDbXA6HLSsM4p1cN1uaw/ 6Px0FkuZ8vLdVEP3h3ATqB55P53/KNcMCORrQYnu+c0ytEolzakT3GBcTb3IFh3GVJkr IpP36RH6HwO4z0h1ixmVRi+CcyALuZIG0o1l1xlWQ9s7MzxiKAgC7qZnM28PSkfaiBho aAaw== X-Forwarded-Encrypted: i=1; AJvYcCV8vkCyrQL0tbXZ92IX+MrFDPzpN/QzYHIRrj0BcE9Y4iAk8Kcq6+Z9y7ByCow9RICwmrakkb73oZGbQic=@vger.kernel.org X-Gm-Message-State: AOJu0YxBAnieEtjuT9YD1DZTF5FbbaP5TvcPEnL5WTHkxLe0C+slT6r5 3xDMjQ7eCqSmQfJjAQiabpH5l6Uw8B/ka4L8xGd4jMd1RkDZOho2xmY9 X-Gm-Gg: ATEYQzw7+/FIautY1Q8OeOYR49VD437Q9X0kdaKyieonFVwtGVPBLFntH5QZ70i7eO1 +CQ2dCc19Q8/UeaV6bFWW2/52TFlC3CkeNfvrw1Bf72A6jABw8EuOWguQL8mgH8STv+6IRY2jOW vh4fO/8JEGEHbZGZqBu32ESTBEc4chwDlPIo5jEjHniQVaT55oQuy7nGKlTPP9u+a3xv5WcFeRT xu0UIKFQJ7cmPI9VcKeDU3KCvGDoHvGS7oVhPd/YWfCUtbP3gexTbhz1OtFzCxkQZRk6gZ0QbG+ rRdGYApq+wwgwzhWnalM7wIFVjWrLT84Ju9BkwsPX7BTD4GhZpL5hbEm2egxLaP78YUb0JgxL4J UARl804GCM2Ox6uo70MHivRRXdOal36S95vSBtjLYePvoHlQliW/Y7qPTmDHBn+IA7dHv7CA/VU h1UKcqQ6+sXjpDLfQv/zDezwLKG7gHwWfw6vWLpYG5nAhtrGVUTA== X-Received: by 2002:a05:6214:f2b:b0:899:b383:5200 with SMTP id 6a1803df08f44-899d1dc0fb9mr71386296d6.25.1772236246684; Fri, 27 Feb 2026 15:50:46 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:46 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 01/18] HID: steelseries: Fix ARCTIS_1_X device mislabeling Date: Fri, 27 Feb 2026 18:50:25 -0500 Message-ID: <20260227235042.410062-2-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The SteelSeries Arctis 1 Wireless for Xbox (device ID 0x12b6) was previously mislabeled as the regular Arctis 1 Wireless (device ID 0x12b3). This commit corrects the labeling by introducing a new STEELSERIES_ARCTIS_1_X quirk flag and device table entry. Both the Arctis 1 and Arctis 1 Wireless for Xbox share the same battery reporting protocol, so they are handled identically in the battery fetch and raw event processing functions. Changes: - Add STEELSERIES_ARCTIS_1_X quirk flag definition - Shift STEELSERIES_ARCTIS_9 quirk flag bit accordingly - Add device table entry for USB_DEVICE_ID_STEELSERIES_ARCTIS_1_X - Update steelseries_headset_fetch_battery() to handle both Arctis 1 variants - Update steelseries_headset_raw_event() to handle both Arctis 1 variants - Update device comment to clarify Arctis 1 vs Arctis 1 Wireless for Xbox Signed-off-by: Sriman Achanta --- drivers/hid/hid-ids.h | 5 +++-- drivers/hid/hid-quirks.c | 1 + drivers/hid/hid-steelseries.c | 19 +++++++++++++------ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 3e299a30dcde..b01704f37142 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1329,8 +1329,9 @@ =20 #define USB_VENDOR_ID_STEELSERIES 0x1038 #define USB_DEVICE_ID_STEELSERIES_SRWS1 0x1410 -#define USB_DEVICE_ID_STEELSERIES_ARCTIS_1 0x12b6 -#define USB_DEVICE_ID_STEELSERIES_ARCTIS_9 0x12c2 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_1 0x12b3 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_1_X 0x12b6 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_9 0x12c2 =20 #define USB_VENDOR_ID_SUN 0x0430 #define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index edc4339adb50..17349eac5c3e 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -713,6 +713,7 @@ static const struct hid_device_id hid_have_special_driv= er[] =3D { #if IS_ENABLED(CONFIG_HID_STEELSERIES) { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRW= S1) }, { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_1_X) }, { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_9) }, #endif #if IS_ENABLED(CONFIG_HID_SUNPLUS) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index f98435631aa1..d3711022bf86 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -19,7 +19,8 @@ =20 #define STEELSERIES_SRWS1 BIT(0) #define STEELSERIES_ARCTIS_1 BIT(1) -#define STEELSERIES_ARCTIS_9 BIT(2) +#define STEELSERIES_ARCTIS_1_X BIT(2) +#define STEELSERIES_ARCTIS_9 BIT(3) =20 struct steelseries_device { struct hid_device *hdev; @@ -97,7 +98,7 @@ static const __u8 steelseries_srws1_rdesc_fixed[] =3D { 0x29, 0x11, /* Usage Maximum (11h), */ 0x95, 0x11, /* Report Count (17), */ 0x81, 0x02, /* Input (Variable), */ - /* ---- Dial patch starts here ---- */ + /* ---- Dial patch starts here ---- */ 0x05, 0x01, /* Usage Page (Desktop), */ 0x09, 0x33, /* Usage (RX), */ 0x75, 0x04, /* Report Size (4), */ @@ -110,7 +111,7 @@ static const __u8 steelseries_srws1_rdesc_fixed[] =3D { 0x95, 0x01, /* Report Count (1), */ 0x25, 0x03, /* Logical Maximum (3), */ 0x81, 0x02, /* Input (Variable), */ - /* ---- Dial patch ends here ---- */ + /* ---- Dial patch ends here ---- */ 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */ 0x09, 0x01, /* Usage (01h), */ 0x75, 0x04, /* Changed Report Size (4), */ @@ -374,7 +375,8 @@ static void steelseries_headset_fetch_battery(struct hi= d_device *hdev) { int ret =3D 0; =20 - if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_ARCTIS_1) + if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_ARCTIS_1 || + hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_ARCTIS_1_X) ret =3D steelseries_headset_request_battery(hdev, arctis_1_battery_request, sizeof(arctis_1_battery_request)); else if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_ARCTIS_9) @@ -638,7 +640,8 @@ static int steelseries_headset_raw_event(struct hid_dev= ice *hdev, if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_SRWS1) return 0; =20 - if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_ARCTIS_1) { + if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_ARCTIS_1 || + hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_ARCTIS_1_X) { hid_dbg(sd->hdev, "Parsing raw event for Arctis 1 headset (%*ph)\n", size, read_buf); if (size < ARCTIS_1_BATTERY_RESPONSE_LEN || @@ -724,10 +727,14 @@ static const struct hid_device_id steelseries_devices= [] =3D { { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRW= S1), .driver_data =3D STEELSERIES_SRWS1 }, =20 - { /* SteelSeries Arctis 1 Wireless for XBox */ + { /* SteelSeries Arctis 1 Wireless */ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_1), .driver_data =3D STEELSERIES_ARCTIS_1 }, =20 + { /* SteelSeries Arctis 1 Wireless for XBox */ + HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_1_X), + .driver_data =3D STEELSERIES_ARCTIS_1_X }, + { /* SteelSeries Arctis 9 Wireless for XBox */ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_9), .driver_data =3D STEELSERIES_ARCTIS_9 }, --=20 2.53.0 From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qv1-f43.google.com (mail-qv1-f43.google.com [209.85.219.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 875D52309B2 for ; Fri, 27 Feb 2026 23:50:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236249; cv=none; b=dfzeYANVBiTCPFlDwGhxJpInIvbXIkLOT69ObJsJ9p11Mwjp+BqPgVcq1192L9XgSpS5rl91FTGcgF6pnTeZfVpxtKEwoYTSkOwxxSPfvndaP1CFh1CnrveYaZhsGimBDz+iHrXqkeSU2Pklhf52I7fP9QTm0PjSEt/CXyEhV6U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236249; c=relaxed/simple; bh=xT83cWCgboG9zohOsFn2S0kQSxg4yq2xuEKYrXMDZO0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Tx5VHAX4B3u68oWyZumfc6rcjKDO9EsVon0G9HsEyYvcP7k6jZzDOnp2wx4XrVMhpwlYscZKGR+ndT/s54fn3GSH/JoRkOr0HhTXWJvzTvqeFIA5UjN8a7FUNfKAcAE5NM4ibUAbl34Dpg0NvH+mWO/aGstfAvJr6mW2SX9/+TU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=fid/X7P7; arc=none smtp.client-ip=209.85.219.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="fid/X7P7" Received: by mail-qv1-f43.google.com with SMTP id 6a1803df08f44-899a9f445cbso33665196d6.0 for ; Fri, 27 Feb 2026 15:50:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236247; x=1772841047; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=/t1rtMqAuIEi8Clsq4S655yskvEX9mAp8Bj+8ESGKPY=; b=fid/X7P7CgePiWcC12spbue1uM20wHDr+vb2yADBHBAcqeydmg170n0XRp6m9qR9c9 hzvKejf+TFWZVlqdqd/1Uu4Wj1sYScNwlAKG8qKxLisz0Goags6YcyjQCJaRf62Mhh3R DQEFRhUrD9pA20mmugAK2pLp8V53ScsBd8QRoyB8Mu/Mr0/6hx2RBkncG2iTiVo87cOt bwxKGoJYRIn1J7rEN12gC1GEloPj98mhWkhn43IB8Xld6se8YJM90P1SKbAOPhPZCf98 yoTmfg1Qm58DfKfrEGyJcrZQrgEmSJX48XDii6QTyjVjJqvCdFjBcK5o0Vw8Qjtkg3v7 04TA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236247; x=1772841047; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=/t1rtMqAuIEi8Clsq4S655yskvEX9mAp8Bj+8ESGKPY=; b=UO+GV9Dxo+1CHnaAix1v9EInF6VhaMYBBdhPh22/fxooCFLa5MZbpyGR6wj98RM3rQ qUXY7RhNZiqjf/ERVPeQWI922icDsSRakM1s761yB+mw2WX0n1IyCAovfc9DnfIAva1u /38m8dxHeVnar1oudOEgHtwYBKrh2fMZqr60GNonVm7PlzlWnrvOpSkJO78QhW3+popX 0aGL2L7ks0/Ky7p2nWx/tDTDQKITwD5XjVOUw4oRfxY1LNrsOFnpBeNY4UZqJthkBUlz bNHz9Ntn2d+4fB1zwWOm6Le+6GbUoWFyNAG5eHb1xu/chXcx4iIwQYbmOvyZFfsId6sz 5sNw== X-Forwarded-Encrypted: i=1; AJvYcCUuO+k/XUMV6s4YyfsQ/Bpa/YG1YJ1r/sXxkSBuAh+AQ+Otm1kjydrg6PyclwQ1N3/MeiJM4IsgXU72JFU=@vger.kernel.org X-Gm-Message-State: AOJu0YzC2GafaufKjYk1T0t1Vbwpe4hIzSj04DW58c7nx2nooLqaQQ8y tjI8nvwh9fcDDiF7fQwEbQn2+lcHCmz6fxUYOLCH0g1pGwpAoJn9FYje X-Gm-Gg: ATEYQzxPUFssx5IsDl5eJxEgtyogP+8O4a03CRVqbe97eivyAmg1H737kNzKavr5dtH UwONHkqk4QhQ1LNd4KvutYvQS9LrT5srjRevxSQpR8g/NQYIfJWobd/5QvBi5tCDbaIfhwH49Du 9s2foS6aD5jhCXH99jPD8/wu1UeiDMEeCgn4QmNv9UqFQ4IBm1FJNU2F4esQNrIBX/wkhc1EEeR ADZFzw1PDXtZ20QGb0h0wNT/aakoyl0JW0UEZAPGDdbPmBDQ1frPvrPf1Xg30l7a4lNdyBHx+Jb n1LCOX2KiL/jYSoPB0SyT3bZLKbbf+F7U8tdd3COhIS6Ux4NW1kHZv9dd/vEJ9ouWAdfRiaELjG WezpjeARMNu3g3QUTyrvGakVSH3VJcPQ3hO+neE3PEwAi/X4mclIwLzDxCY3aDFKtYaESAHl2To 4HQiNzhPiAA6ee2lrK/3zjtrwo7Sug7vVHWgtbWnnu6bAWVaGbJpKT+KSwP6NE X-Received: by 2002:ad4:5d68:0:b0:895:3b03:ab50 with SMTP id 6a1803df08f44-899d1f0b0a1mr66645176d6.65.1772236247534; Fri, 27 Feb 2026 15:50:47 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:47 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 02/18] HID: hid-ids: Add SteelSeries Arctis headset device IDs Date: Fri, 27 Feb 2026 18:50:26 -0500 Message-ID: <20260227235042.410062-3-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add USB device IDs for the complete SteelSeries Arctis headset lineup, including: - Arctis 1, 1 Wireless, 7, 7P, 7X variants - Arctis 7+ series (PS5, Xbox, Destiny editions) - Arctis 9 Wireless - Arctis Pro Wireless - Arctis Nova 3, 3P, 3X - Arctis Nova 5, 5X - Arctis Nova 7 series (multiple variants and special editions) - Arctis Nova Pro Wireless and Pro X These IDs will be used by the updated hid-steelseries driver to provide battery monitoring, sidetone control, and other device-specific features for these wireless gaming headsets. Signed-off-by: Sriman Achanta --- drivers/hid/hid-ids.h | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index b01704f37142..e16b8dc5edd0 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1327,11 +1327,37 @@ #define USB_DEVICE_ID_STEAM_CONTROLLER_WIRELESS 0x1142 #define USB_DEVICE_ID_STEAM_DECK 0x1205 =20 -#define USB_VENDOR_ID_STEELSERIES 0x1038 -#define USB_DEVICE_ID_STEELSERIES_SRWS1 0x1410 -#define USB_DEVICE_ID_STEELSERIES_ARCTIS_1 0x12b3 -#define USB_DEVICE_ID_STEELSERIES_ARCTIS_1_X 0x12b6 -#define USB_DEVICE_ID_STEELSERIES_ARCTIS_9 0x12c2 +#define USB_VENDOR_ID_STEELSERIES 0x1038 +#define USB_DEVICE_ID_STEELSERIES_SRWS1 0x1410 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_1 0x12b3 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_1_X 0x12b6 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_7 0x1260 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_7_P 0x12d5 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_7_X 0x12d7 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_7_GEN2 0x12ad +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_7_PLUS 0x220e +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_7_PLUS_P 0x2212 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_7_PLUS_X 0x2216 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_7_PLUS_DESTINY 0x2236 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_9 0x12c2 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_3_P 0x2269 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_3_X 0x226d +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_5 0x2232 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_5_X 0x2253 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7 0x2202 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_2 0x22a1 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_P 0x220a +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X 0x2206 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_2 0x22a4 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_3 0x22a5 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_DIABLO 0x223a +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_DIABLO_2 0x22a9 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_WOW 0x227a +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_GEN2 0x227e +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_GEN2 0x229e +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_GEN2_2 0x2258 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_PRO 0x12e0 +#define USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_PRO_X 0x12e5 =20 #define USB_VENDOR_ID_SUN 0x0430 #define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab --=20 2.53.0 From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qv1-f44.google.com (mail-qv1-f44.google.com [209.85.219.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 70A30346E5E for ; Fri, 27 Feb 2026 23:50:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236250; cv=none; b=pbbeap+Ysmjctg8CJSAhKpgautkL6DxgS2l4JhIlD1ykDQFCa+rOYINpLPZwLc7DOz4/OOUaRzgL1gqiUBUj8RaHSiVqNNauf1UnEnxt4uiiiqhmgxqnB2gGe4bhodHIGIpNqpKPisgHAcbmZ8hQc2whxXBglvuctNNthlwflXc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236250; c=relaxed/simple; bh=NbbI/3xTJIVp9rP8bTvgfcJaLoAVOjM37WP3ObO/V4k=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=BEnOx7xAhDLpoW//QP85qBqv2Ej4QhUcrfC+xUQakoUt7r0YjWkESB4hmUEvRJixrHOKckv36cV38DvcLyGdjDHbR/PhEfz0QqzeYnsyPvX7HiQPTMiWeNzPSBMTHoUCHuoCEDFZ7jPI3Ordp3MOUJxq315P4A1t+wdxYCG4n00= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=GrGXq32E; arc=none smtp.client-ip=209.85.219.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GrGXq32E" Received: by mail-qv1-f44.google.com with SMTP id 6a1803df08f44-899ba9a699cso15400936d6.2 for ; Fri, 27 Feb 2026 15:50:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236248; x=1772841048; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=bl9i/tPAcm9jn+eDgOCz9wrF4aIKmWaaUz0y8PMJrEc=; b=GrGXq32ENdoNBNOnoZ/dEbPGrfYSMvktXHCJsdVYeDG1pTf4GtglIiwVfoCc69Nr50 RU+4odVWSh3W4r8ATwdA6nBGRvpZNQIZQY457jcyfCH0MR3Azq0booCgWEjRpoF0Y9QM ZBl7+r+qcmxVj1leyz3nDayOMKB3IDaLSpvAf0qq/Zy/wRG+D2AMD4d/hdd6Z1LQaOCw BLtpWdX1+wLJIaU6xL3IZVfnw1KGAsf6+Q8jXxAES0IO9icOVKqKieV+HHjd/YxzGWUm VXK5YsNNng34gBCBwXnY3XLBD0mCPtMx3VchEvs9nf1YCz/DCtG/g8sTI9JfEnQrV8vb JASg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236248; x=1772841048; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=bl9i/tPAcm9jn+eDgOCz9wrF4aIKmWaaUz0y8PMJrEc=; b=cpBeyPj3uKiobGa3jOPUy3y+pCBGNPz4bskyeb2ErDfN9j40NM5Y33Y0hKXJkRJXBO MAhAF9dopu/cT/IWtTb8h3Jue0+4m4lqVOeaIlFyt0Ur4H8i/9BWRcv414LSgsdBoRjv BUkNc1oCdsF3Q9c5+O+W6QvEXgEi1dp2o/NAM/dj8Ah84gKpsc4qusqIpTlacVC+KaSZ jK1PemYVNEDvNJkVlKN46mW9tiO41Jnzaah6gDzJ4XeGDnAHIgFJ/nJudqnSogmpFBwy X8bg29WNnAcRV4q2xotctyguQGsUju65TDukOaLOSgkZg3HB/rYRQM6RTRj8CYVvyyc4 Zngg== X-Forwarded-Encrypted: i=1; AJvYcCUwKqWGfPW1EsWehW1J3U0T5tFp3mRJtLno2EPw863NerJMB5y75UV4nIpDegcwKrV5QXxkfAI24KiyUKc=@vger.kernel.org X-Gm-Message-State: AOJu0YwQ8waWRbsFeMQOzMAbnlNtYfEgxOqxPPrpVO1DpT5T65RH6y9W SlLOBbM6lZ9qAhfzjSHsu2a1W893UZMSEbDVg7lOYm8L3RElejCBRL4t X-Gm-Gg: ATEYQzx5BvRUQmWm4+yQOzMv7udFsoGn08m+yNg/Er9EisnbcGBmoDHWg07BRQ4Dhj2 bEB7M0y0iZHhpfcX9hYQ5QLRJLWVDHyINB24y6def/bbX0X9feQSfjBfe/1+yivaZJ8/xuGsYeS nStCuFwQ8Gcf+KrjX3bA9mLRP6+jjy2VDaxZAbET1n5UYf0HmYg1wNc7ZmR3Vm++i2HEvFZJGlO 6AwXJvNujRstOW7QV6ApLua6TIHysayVGmsl8RUODFxqzixEt4DK/2C/wrLFd9QdWEuvEHxkMmN 9KwsWWu2oa1B/wV7TcwLsH9YsEbzmvzRrf70cBMeZNqm5TprQrO9R+IchXPET2oZF1iasQCFGNa 3luRDN86FRktnhGRcfV75xfg1ixiLaMur0eC2i66ueCVj25UKRdLnBqTVV7Z7Yfiid/O9NcUZEZ mnJgMo5/yH4VEUhSIVWvyImCLb3Ehle08szIC1HRanvZXRXK6eaA== X-Received: by 2002:a05:6214:410c:b0:896:f6d5:c73c with SMTP id 6a1803df08f44-899d1e3433bmr73135466d6.41.1772236248367; Fri, 27 Feb 2026 15:50:48 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:47 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 03/18] HID: quirks: Add additional Arctis headset device IDs Date: Fri, 27 Feb 2026 18:50:27 -0500 Message-ID: <20260227235042.410062-4-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add support for additional SteelSeries Arctis headset models to the HID quirks table. This enables proper device recognition and handling for: - Arctis 7 series (7, 7P, 7X, 7 Gen2) - Arctis 7 Plus series (7 Plus, 7 Plus P, 7 Plus X, 7 Plus Destiny) - Arctis Pro - Arctis Nova 3 series (3, 3P, 3X) - Arctis Nova 5 series (5, 5X) - Arctis Nova 7 series (7, 7X, 7P, 7X Rev2, 7 Diablo, 7 WoW, 7 Gen2, 7X Gen2) - Arctis Nova Pro series (Pro, Pro X) Signed-off-by: Sriman Achanta --- drivers/hid/hid-quirks.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c index 17349eac5c3e..65a6f6ab30b9 100644 --- a/drivers/hid/hid-quirks.c +++ b/drivers/hid/hid-quirks.c @@ -714,7 +714,33 @@ static const struct hid_device_id hid_have_special_dri= ver[] =3D { { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRW= S1) }, { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_1) }, { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_1_X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_7) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_7_P) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_7_X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_7_GEN2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_7_PLUS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_7_PLUS_P) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_7_PLUS_X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_7_PLUS_DESTINY) }, { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_9) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_3_P) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_3_X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_5) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_5_X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_7) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_7_2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_7_P) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_7_X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_7_X_2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_7_X_3) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_7_DIABLO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_7_DIABLO_2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_7_WOW) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_7_GEN2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_7_X_GEN2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_7_X_GEN2_2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_PRO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_NOVA_PRO_X) }, #endif #if IS_ENABLED(CONFIG_HID_SUNPLUS) { HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) }, --=20 2.53.0 From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qv1-f44.google.com (mail-qv1-f44.google.com [209.85.219.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 73115330B2C for ; Fri, 27 Feb 2026 23:50:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236253; cv=none; b=filqttJmRkdb8DXnCxTd/4tls3ek7TrB/Nt4U+mKTEWR/+cRVZkKffo9Xhy1e5AljcvxW1yv+g5n7CPIcw+VO82mUbI37PzXwhN51AFLyfD96bioQ9v6AmYsCn1PaK1MRzWmkxpRjHoNwzrsr9suHiiZS2fkl5Bg3SEvKCkurCQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236253; c=relaxed/simple; bh=kOkif7+fE4TYzKo1+TOGZXA3p0qj7Keq7jj98H8lqZY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IaFXwnlJPZGVexHqTp+t5DxQ44lN241x3lxJTo7BZxGwKB11u0Mdr37NYMJ4gy1Fa63ttCoX+CIAkoDUNTzBlH/bCJ6wj8nZs/U7uWKZKPGDpCc+oVjuk7ARTGUCDFAFOgpwuaNnOw5Dchty0H/wO3e4WC2ohVtDSD6oKyIQehQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=CfvmJgXx; arc=none smtp.client-ip=209.85.219.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="CfvmJgXx" Received: by mail-qv1-f44.google.com with SMTP id 6a1803df08f44-896f632d206so45050246d6.0 for ; Fri, 27 Feb 2026 15:50:50 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236249; x=1772841049; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=V+jo6LHfaDJ8jIHPSyQKeG3Pw+NKLGAR5lYiLVb37oI=; b=CfvmJgXxcNJq2a8y3kuG82GjHmwHmQ6Ok58bS/8JS+dr3ihmik8FXFlAIvZ7v5GPpr VMdiFirlEX2oX5RsnADHz40CQvKOBr9UypoRzKuiEBjlPNjaxgtE/fcd+zW1V1ym3y7a O3Mp3dZQ5tXVD1+cLk6QQ8sGx2THknEiR3hkzR3/U5UZrMbd4hpi3Cf9SX3C83Tvw0Py VFR9ZG5KYabA5Dk2eVQjat0/QDEykAb5syqBoU8FdSrgtO7+TTbvBbz3y5rtTkYAppAq 0cR4kuEimwVwVaadpvFlmvQ/JT1etUu0nmpAGE473ObZvJgDRRZuTBKEdg8Nc5BcGK17 NH5g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236249; x=1772841049; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=V+jo6LHfaDJ8jIHPSyQKeG3Pw+NKLGAR5lYiLVb37oI=; b=JX0x83pMptCG4uynAThzU7Ql37bLAn9Jfo1oLJEJwjCauIHwQHF3HAgNby2ap6PlGD drT+qQ+RjP7Z4fcLQgt83+t6iaHVHipMdOC2a+SIba2JEE5o06qcZ7K2T5ysVSDH94F+ fEEYgghS9JntUcSnuqrvW7zd84POLnC+4hErfhQG8CY/la1zwbRQ9pbRl+h7sXAVPvqW y5IHTkfoZ3FnzSDibrGTvOdEHcBVL4IbLWbA+3TAlDxsSBP2thaX2Gg7hGqwSO1iZnyj Qu+UDPdMHVPOvclCnE/1am0rHnrwV3dOucuxvGOWfUdeM4QaTbTspQIb67JngC0PyzJF 0AEA== X-Forwarded-Encrypted: i=1; AJvYcCW4VUe6ju39feYw2lQnfZxVcQo0nF2ncwoyP4oCnNEfgpABoEwJwVCl+1iHv5gl5DeMh94Y5fwtZTfQzq4=@vger.kernel.org X-Gm-Message-State: AOJu0YxNIl6cjpXWqJhVwGuDs5sda1hgH3NbtMI79XVjwBqfrv9vH0QF YGUUMSFkpYXdYkg9WDnyTBkrUQCHT0SgPrM1U7DlIYPqDQJOu/XqJ3F6 X-Gm-Gg: ATEYQzzLQHjdmFWRlgxA824g/NKKHdTzqV2ibJs7BS2FfR+Ch+dJEzNWDSSv7/soQ50 btNpNY0lLhR69Wn5l2pHKT+2MeFjql7eJLDSTXOxI7Y/Xnv4pn5LJ7iI9lBx2ZANg2kQ3zuCbdV thRxxOOvs2ExH4OalBBf+xfZOSUhUJoBNeFqqd3Z0/OrtXd/aztZ/a4YwZYX9qw29sQGb/eixBI WX10ywzL0RhLKMIz8Y0vxoKYILB9+XZQ9Vx3LtJZC32Y5jbyDWvCmYl5KJArVjgjTirnX3yeJPx CCDI9WmBnDPHAFvKkmLOe/h6kHNXhnOp+z0m8K/MfQ8X00rGUkz+SFm14iszKCuhJyR/t5J/cZA VY6rUHQaetCOneE5rNy9dugPqk5JO4jeMIg4zS008TMJaBsFtrUhdcJFGdbY0xmxeUu+sZL/26U ZBTRCjjJeTNeKz5qE9eru9fV7TKU+L3Xv0J5R4obCg3UCt32BEUQ== X-Received: by 2002:ad4:5d67:0:b0:896:fa81:5652 with SMTP id 6a1803df08f44-899c6834181mr115269446d6.34.1772236249372; Fri, 27 Feb 2026 15:50:49 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.48 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:48 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 04/18] HID: steelseries: Add async support and unify device definitions Date: Fri, 27 Feb 2026 18:50:28 -0500 Message-ID: <20260227235042.410062-5-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Refactor the SteelSeries driver to improve scalability and support the modern Arctis Nova headset lineup along with legacy models. - Replace the bitmap-based quirk system with `struct steelseries_device_info` to encapsulate device-specific traits (product ID, name, capabilities, interfaces). - Implement asynchronous battery monitoring. Devices that support async updates (like the Nova series) now rely on interrupt events rather than periodic polling, reducing overhead. - Add support for complex multi-interface devices (e.g., Nova 7) where battery events arrive on a separate asynchronous interface. - Consolidate battery request and report parsing logic. New helpers `steelseries_send_feature_report` and `steelseries_send_output_report` simplify command dispatch. - Add support for over 20 new devices including the entire Arctis Nova series (3, 5, 7, Pro) and various Arctis 7/9/Pro variants. - Clean up naming conventions (e.g., removing `_headset_` prefix from general functions) and improve locking in the battery timer. Signed-off-by: Sriman Achanta --- drivers/hid/hid-steelseries.c | 894 +++++++++++++++++++++++++--------- 1 file changed, 653 insertions(+), 241 deletions(-) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index d3711022bf86..d8ece8449255 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -4,37 +4,54 @@ * * Copyright (c) 2013 Simon Wood * Copyright (c) 2023 Bastien Nocera + * Copyright (c) 2025 Sriman Achanta */ =20 -/* - */ - +#include #include #include #include #include #include +#include +#include +#include =20 #include "hid-ids.h" =20 -#define STEELSERIES_SRWS1 BIT(0) -#define STEELSERIES_ARCTIS_1 BIT(1) -#define STEELSERIES_ARCTIS_1_X BIT(2) -#define STEELSERIES_ARCTIS_9 BIT(3) +#define SS_CAP_BATTERY BIT(0) + +#define SS_QUIRK_STATUS_SYNC_POLL BIT(0) + +struct steelseries_device; + +struct steelseries_device_info { + unsigned long capabilities; + unsigned long quirks; + + u8 sync_interface; + u8 async_interface; + + int (*request_status)(struct hid_device *hdev); + void (*parse_status)(struct steelseries_device *sd, u8 *data, int size); +}; =20 struct steelseries_device { struct hid_device *hdev; - unsigned long quirks; + const struct steelseries_device_info *info; =20 - struct delayed_work battery_work; - spinlock_t lock; - bool removed; + bool use_async_protocol; + + struct delayed_work status_work; =20 struct power_supply_desc battery_desc; struct power_supply *battery; - uint8_t battery_capacity; bool headset_connected; + u8 battery_capacity; bool battery_charging; + + spinlock_t lock; + bool removed; }; =20 #if IS_BUILTIN(CONFIG_LEDS_CLASS) || \ @@ -341,53 +358,118 @@ static int steelseries_srws1_probe(struct hid_device= *hdev, } #endif =20 -#define STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS 3000 +static const __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev, + __u8 *rdesc, unsigned int *rsize) +{ + if (hdev->vendor !=3D USB_VENDOR_ID_STEELSERIES || + hdev->product !=3D USB_DEVICE_ID_STEELSERIES_SRWS1) + return rdesc; =20 -#define ARCTIS_1_BATTERY_RESPONSE_LEN 8 -#define ARCTIS_9_BATTERY_RESPONSE_LEN 64 -static const char arctis_1_battery_request[] =3D { 0x06, 0x12 }; -static const char arctis_9_battery_request[] =3D { 0x00, 0x20 }; + if (*rsize >=3D 115 && rdesc[11] =3D=3D 0x02 && rdesc[13] =3D=3D 0xc8 + && rdesc[29] =3D=3D 0xbb && rdesc[40] =3D=3D 0xc5) { + hid_info(hdev, "Fixing up Steelseries SRW-S1 report descriptor\n"); + *rsize =3D sizeof(steelseries_srws1_rdesc_fixed); + return steelseries_srws1_rdesc_fixed; + } + return rdesc; +} =20 -static int steelseries_headset_request_battery(struct hid_device *hdev, - const char *request, size_t len) +/* + * Headset report helpers + */ + +static int steelseries_send_report(struct hid_device *hdev, const u8 *data, + int len, enum hid_report_type type) { - u8 *write_buf; + u8 *buf; int ret; =20 - /* Request battery information */ - write_buf =3D kmemdup(request, len, GFP_KERNEL); - if (!write_buf) + buf =3D kmemdup(data, len, GFP_KERNEL); + if (!buf) return -ENOMEM; =20 - hid_dbg(hdev, "Sending battery request report"); - ret =3D hid_hw_raw_request(hdev, request[0], write_buf, len, - HID_OUTPUT_REPORT, HID_REQ_SET_REPORT); - if (ret < (int)len) { - hid_err(hdev, "hid_hw_raw_request() failed with %d\n", ret); - ret =3D -ENODATA; - } + ret =3D hid_hw_raw_request(hdev, data[0], buf, len, type, + HID_REQ_SET_REPORT); + kfree(buf); =20 - kfree(write_buf); - return ret; + if (ret < 0) + return ret; + if (ret < len) + return -EIO; + + return 0; } =20 -static void steelseries_headset_fetch_battery(struct hid_device *hdev) +static inline int steelseries_send_feature_report(struct hid_device *hdev, + const u8 *data, int len) { - int ret =3D 0; + return steelseries_send_report(hdev, data, len, HID_FEATURE_REPORT); +} =20 - if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_ARCTIS_1 || - hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_ARCTIS_1_X) - ret =3D steelseries_headset_request_battery(hdev, - arctis_1_battery_request, sizeof(arctis_1_battery_request)); - else if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_ARCTIS_9) - ret =3D steelseries_headset_request_battery(hdev, - arctis_9_battery_request, sizeof(arctis_9_battery_request)); +static inline int steelseries_send_output_report(struct hid_device *hdev, + const u8 *data, int len) +{ + return steelseries_send_report(hdev, data, len, HID_OUTPUT_REPORT); +} =20 - if (ret < 0) - hid_dbg(hdev, - "Battery query failed (err: %d)\n", ret); +/* + * Headset status request functions + */ + +static int steelseries_arctis_1_request_status(struct hid_device *hdev) +{ + const u8 data[] =3D { 0x06, 0x12 }; + + return steelseries_send_feature_report(hdev, data, sizeof(data)); +} + +static int steelseries_arctis_7_request_status(struct hid_device *hdev) +{ + int ret; + const u8 connection_data[] =3D { 0x06, 0x14 }; + const u8 battery_data[] =3D { 0x06, 0x18 }; + + ret =3D steelseries_send_feature_report(hdev, connection_data, sizeof(con= nection_data)); + if (ret) + return ret; + + msleep(10); + + return steelseries_send_feature_report(hdev, battery_data, sizeof(battery= _data)); +} + +static int steelseries_arctis_9_request_status(struct hid_device *hdev) +{ + const u8 data[] =3D { 0x00, 0x20 }; + + return steelseries_send_feature_report(hdev, data, sizeof(data)); } =20 +static int steelseries_arctis_nova_request_status(struct hid_device *hdev) +{ + const u8 data[] =3D { 0x00, 0xb0 }; + + return steelseries_send_output_report(hdev, data, sizeof(data)); +} + +static int steelseries_arctis_nova_3p_request_status(struct hid_device *hd= ev) +{ + const u8 data[] =3D { 0xb0 }; + + return steelseries_send_output_report(hdev, data, sizeof(data)); +} + +static int steelseries_arctis_nova_pro_request_status(struct hid_device *h= dev) +{ + const u8 data[] =3D { 0x06, 0xb0 }; + + return steelseries_send_output_report(hdev, data, sizeof(data)); +} + +/* + * Headset battery helpers + */ + static int battery_capacity_to_level(int capacity) { if (capacity >=3D 50) @@ -397,19 +479,247 @@ static int battery_capacity_to_level(int capacity) return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; } =20 -static void steelseries_headset_battery_timer_tick(struct work_struct *wor= k) +static u8 steelseries_map_capacity(u8 capacity, u8 min_in, u8 max_in) +{ + if (capacity >=3D max_in) + return 100; + if (capacity <=3D min_in) + return 0; + return (capacity - min_in) * 100 / (max_in - min_in); +} + +/* + * Headset status parse functions + */ + +static void steelseries_arctis_1_parse_status(struct steelseries_device *s= d, + u8 *data, int size) +{ + if (size < 4) + return; + + sd->headset_connected =3D (data[2] !=3D 0x01); + sd->battery_capacity =3D data[3]; +} + +static void steelseries_arctis_7_parse_status(struct steelseries_device *s= d, + u8 *data, int size) +{ + if (size < 3) + return; + + if (data[0] =3D=3D 0x06) { + if (data[1] =3D=3D 0x14) + sd->headset_connected =3D (data[2] =3D=3D 0x03); + else if (data[1] =3D=3D 0x18) + sd->battery_capacity =3D data[2]; + } +} + +static void steelseries_arctis_7_plus_parse_status(struct steelseries_devi= ce *sd, + u8 *data, int size) +{ + if (size < 4) + return; + + if (data[0] =3D=3D 0xb0) { + sd->headset_connected =3D !(data[1] =3D=3D 0x01); + sd->battery_capacity =3D steelseries_map_capacity(data[2], 0x00, 0x04); + sd->battery_charging =3D (data[3] =3D=3D 0x01); + } +} + +static void steelseries_arctis_9_parse_status(struct steelseries_device *s= d, + u8 *data, int size) +{ + if (size < 5) + return; + + if (data[0] =3D=3D 0xaa) { + sd->headset_connected =3D (data[1] =3D=3D 0x01); + sd->battery_charging =3D (data[4] =3D=3D 0x01); + sd->battery_capacity =3D steelseries_map_capacity(data[3], 0x64, 0x9A); + } +} + +static void steelseries_arctis_nova_3p_parse_status(struct steelseries_dev= ice *sd, + u8 *data, int size) +{ + if (size < 4) + return; + + if (data[0] =3D=3D 0xb0) { + sd->headset_connected =3D !(data[1] =3D=3D 0x02); + sd->battery_capacity =3D steelseries_map_capacity(data[3], 0x00, 0x64); + } +} + +static void steelseries_arctis_nova_5_parse_status(struct steelseries_devi= ce *sd, + u8 *data, int size) +{ + if (size < 5) + return; + + if (data[0] =3D=3D 0xb0) { + sd->headset_connected =3D !(data[1] =3D=3D 0x02); + sd->battery_capacity =3D data[3]; + sd->battery_charging =3D (data[4] =3D=3D 0x01); + } +} + +static void steelseries_arctis_nova_7_parse_status(struct steelseries_devi= ce *sd, + u8 *data, int size) +{ + if (size < 4) + return; + + if (data[0] =3D=3D 0xb0) { + sd->headset_connected =3D (data[1] =3D=3D 0x03); + sd->battery_capacity =3D steelseries_map_capacity(data[2], 0x00, 0x04); + sd->battery_charging =3D (data[3] =3D=3D 0x01); + } +} + +static void steelseries_arctis_nova_7_gen2_parse_status(struct steelseries= _device *sd, + u8 *data, int size) +{ + if (size < 4) + return; + + switch (data[0]) { + case 0xb0: + sd->headset_connected =3D (data[1] =3D=3D 0x03); + sd->battery_capacity =3D data[2]; + sd->battery_charging =3D (data[3] =3D=3D 0x01); + break; + case 0xb7: + sd->battery_capacity =3D data[1]; + break; + case 0xb9: + sd->headset_connected =3D (data[1] =3D=3D 0x03); + break; + case 0xbb: + sd->battery_charging =3D (data[1] =3D=3D 0x01); + break; + } +} + +static void steelseries_arctis_nova_pro_parse_status(struct steelseries_de= vice *sd, + u8 *data, int size) +{ + if (size < 16) + return; + + if (data[0] =3D=3D 0x06 && data[1] =3D=3D 0xb0) { + sd->headset_connected =3D (data[15] =3D=3D 0x08 || data[15] =3D=3D 0x02); + sd->battery_capacity =3D steelseries_map_capacity(data[6], 0x00, 0x08); + sd->battery_charging =3D (data[15] =3D=3D 0x02); + } +} + +/* + * Device info definitions + */ + +static const struct steelseries_device_info srws1_info =3D { }; + +static const struct steelseries_device_info arctis_1_info =3D { + .sync_interface =3D 3, + .capabilities =3D SS_CAP_BATTERY, + .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .request_status =3D steelseries_arctis_1_request_status, + .parse_status =3D steelseries_arctis_1_parse_status, +}; + +static const struct steelseries_device_info arctis_7_info =3D { + .sync_interface =3D 5, + .capabilities =3D SS_CAP_BATTERY, + .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .request_status =3D steelseries_arctis_7_request_status, + .parse_status =3D steelseries_arctis_7_parse_status, +}; + +static const struct steelseries_device_info arctis_7_plus_info =3D { + .sync_interface =3D 3, + .capabilities =3D SS_CAP_BATTERY, + .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .request_status =3D steelseries_arctis_nova_request_status, + .parse_status =3D steelseries_arctis_7_plus_parse_status, +}; + +static const struct steelseries_device_info arctis_9_info =3D { + .sync_interface =3D 0, + .capabilities =3D SS_CAP_BATTERY, + .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .request_status =3D steelseries_arctis_9_request_status, + .parse_status =3D steelseries_arctis_9_parse_status, +}; + +static const struct steelseries_device_info arctis_nova_3p_info =3D { + .sync_interface =3D 4, + .capabilities =3D SS_CAP_BATTERY, + .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .request_status =3D steelseries_arctis_nova_3p_request_status, + .parse_status =3D steelseries_arctis_nova_3p_parse_status, +}; + +static const struct steelseries_device_info arctis_nova_5_info =3D { + .sync_interface =3D 3, + .capabilities =3D SS_CAP_BATTERY, + .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .request_status =3D steelseries_arctis_nova_request_status, + .parse_status =3D steelseries_arctis_nova_5_parse_status, +}; + +static const struct steelseries_device_info arctis_nova_7_info =3D { + .sync_interface =3D 3, + .capabilities =3D SS_CAP_BATTERY, + .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .request_status =3D steelseries_arctis_nova_request_status, + .parse_status =3D steelseries_arctis_nova_7_parse_status, +}; + +static const struct steelseries_device_info arctis_nova_7_gen2_info =3D { + .sync_interface =3D 3, + .async_interface =3D 5, + .capabilities =3D SS_CAP_BATTERY, + .request_status =3D steelseries_arctis_nova_request_status, + .parse_status =3D steelseries_arctis_nova_7_gen2_parse_status, +}; + +static const struct steelseries_device_info arctis_nova_pro_info =3D { + .sync_interface =3D 4, + .capabilities =3D SS_CAP_BATTERY, + .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .request_status =3D steelseries_arctis_nova_pro_request_status, + .parse_status =3D steelseries_arctis_nova_pro_parse_status, +}; + +/* + * Headset wireless status and battery infrastructure + */ + +#define STEELSERIES_HEADSET_STATUS_TIMEOUT_MS 3000 + +static void +steelseries_headset_set_wireless_status(struct hid_device *hdev, + bool connected) { - struct steelseries_device *sd =3D container_of(work, - struct steelseries_device, battery_work.work); - struct hid_device *hdev =3D sd->hdev; + struct usb_interface *intf; + + if (!hid_is_usb(hdev)) + return; =20 - steelseries_headset_fetch_battery(hdev); + intf =3D to_usb_interface(hdev->dev.parent); + usb_set_wireless_status(intf, connected ? + USB_WIRELESS_STATUS_CONNECTED : + USB_WIRELESS_STATUS_DISCONNECTED); } =20 #define STEELSERIES_PREFIX "SteelSeries " #define STEELSERIES_PREFIX_LEN strlen(STEELSERIES_PREFIX) =20 -static int steelseries_headset_battery_get_property(struct power_supply *p= sy, +static int steelseries_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { @@ -452,22 +762,7 @@ static int steelseries_headset_battery_get_property(st= ruct power_supply *psy, return ret; } =20 -static void -steelseries_headset_set_wireless_status(struct hid_device *hdev, - bool connected) -{ - struct usb_interface *intf; - - if (!hid_is_usb(hdev)) - return; - - intf =3D to_usb_interface(hdev->dev.parent); - usb_set_wireless_status(intf, connected ? - USB_WIRELESS_STATUS_CONNECTED : - USB_WIRELESS_STATUS_DISCONNECTED); -} - -static enum power_supply_property steelseries_headset_battery_props[] =3D { +static enum power_supply_property steelseries_battery_props[] =3D { POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, POWER_SUPPLY_PROP_PRESENT, @@ -477,7 +772,26 @@ static enum power_supply_property steelseries_headset_= battery_props[] =3D { POWER_SUPPLY_PROP_CAPACITY_LEVEL, }; =20 -static int steelseries_headset_battery_register(struct steelseries_device = *sd) +/* + * Delayed work handlers for status polling and settings requests + */ + +static void steelseries_status_timer_work_handler(struct work_struct *work) +{ + struct steelseries_device *sd =3D container_of( + work, struct steelseries_device, status_work.work); + unsigned long flags; + + sd->info->request_status(sd->hdev); + + spin_lock_irqsave(&sd->lock, flags); + if (!sd->removed && !sd->use_async_protocol) + schedule_delayed_work(&sd->status_work, + msecs_to_jiffies(STEELSERIES_HEADSET_STATUS_TIMEOUT_MS)); + spin_unlock_irqrestore(&sd->lock, flags); +} + +static int steelseries_battery_register(struct steelseries_device *sd) { static atomic_t battery_no =3D ATOMIC_INIT(0); struct power_supply_config battery_cfg =3D { .drv_data =3D sd, }; @@ -485,9 +799,9 @@ static int steelseries_headset_battery_register(struct = steelseries_device *sd) int ret; =20 sd->battery_desc.type =3D POWER_SUPPLY_TYPE_BATTERY; - sd->battery_desc.properties =3D steelseries_headset_battery_props; - sd->battery_desc.num_properties =3D ARRAY_SIZE(steelseries_headset_batter= y_props); - sd->battery_desc.get_property =3D steelseries_headset_battery_get_propert= y; + sd->battery_desc.properties =3D steelseries_battery_props; + sd->battery_desc.num_properties =3D ARRAY_SIZE(steelseries_battery_props); + sd->battery_desc.get_property =3D steelseries_battery_get_property; sd->battery_desc.use_for_apm =3D 0; n =3D atomic_inc_return(&battery_no) - 1; sd->battery_desc.name =3D devm_kasprintf(&sd->hdev->dev, GFP_KERNEL, @@ -496,14 +810,16 @@ static int steelseries_headset_battery_register(struc= t steelseries_device *sd) return -ENOMEM; =20 /* avoid the warning of 0% battery while waiting for the first info */ - steelseries_headset_set_wireless_status(sd->hdev, false); sd->battery_capacity =3D 100; sd->battery_charging =3D false; + sd->headset_connected =3D false; + steelseries_headset_set_wireless_status(sd->hdev, false); =20 sd->battery =3D devm_power_supply_register(&sd->hdev->dev, &sd->battery_desc, &battery_cfg); if (IS_ERR(sd->battery)) { ret =3D PTR_ERR(sd->battery); + sd->battery =3D NULL; hid_err(sd->hdev, "%s:power_supply_register failed with error %d\n", __func__, ret); @@ -511,68 +827,185 @@ static int steelseries_headset_battery_register(stru= ct steelseries_device *sd) } power_supply_powers(sd->battery, &sd->hdev->dev); =20 - INIT_DELAYED_WORK(&sd->battery_work, steelseries_headset_battery_timer_ti= ck); - steelseries_headset_fetch_battery(sd->hdev); + return 0; +} + +static int steelseries_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct steelseries_device *sd =3D hid_get_drvdata(hdev); + u8 old_capacity; + bool old_connected; + bool old_charging; + bool is_async_interface =3D false; + + if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_SRWS1) + return 0; + + if (!sd) + return 0; + + old_capacity =3D sd->battery_capacity; + old_connected =3D sd->headset_connected; + old_charging =3D sd->battery_charging; + + if (hid_is_usb(hdev)) { + struct usb_interface *intf =3D to_usb_interface(hdev->dev.parent); + + is_async_interface =3D (intf->cur_altsetting->desc.bInterfaceNumber =3D= =3D + sd->info->async_interface); + } + + sd->info->parse_status(sd, data, size); + + if (sd->headset_connected !=3D old_connected) { + hid_dbg(hdev, + "Connected status changed from %sconnected to %sconnected\n", + old_connected ? "" : "not ", + sd->headset_connected ? "" : "not "); + + if (sd->headset_connected && !old_connected && + sd->use_async_protocol && is_async_interface) { + schedule_delayed_work(&sd->status_work, 0); + } =20 - if (sd->quirks & STEELSERIES_ARCTIS_9) { - /* The first fetch_battery request can remain unanswered in some cases */ - schedule_delayed_work(&sd->battery_work, - msecs_to_jiffies(STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS)); + if (sd->battery) { + steelseries_headset_set_wireless_status(sd->hdev, + sd->headset_connected); + power_supply_changed(sd->battery); + } + } + + if (sd->battery_capacity !=3D old_capacity) { + hid_dbg(hdev, "Battery capacity changed from %d%% to %d%%\n", + old_capacity, sd->battery_capacity); + if (sd->battery) + power_supply_changed(sd->battery); + } + + if (sd->battery_charging !=3D old_charging) { + hid_dbg(hdev, + "Battery charging status changed from %scharging to %scharging\n", + old_charging ? "" : "not ", + sd->battery_charging ? "" : "not "); + if (sd->battery) + power_supply_changed(sd->battery); } =20 return 0; } =20 -static bool steelseries_is_vendor_usage_page(struct hid_device *hdev, uint= 8_t usage_page) +static struct hid_device *steelseries_get_sibling_hdev(struct hid_device *= hdev, + int interface_num) { - return hdev->rdesc[0] =3D=3D 0x06 && - hdev->rdesc[1] =3D=3D usage_page && - hdev->rdesc[2] =3D=3D 0xff; + struct usb_interface *intf =3D to_usb_interface(hdev->dev.parent); + struct usb_device *usb_dev =3D interface_to_usbdev(intf); + struct usb_interface *sibling_intf; + struct hid_device *sibling_hdev; + + sibling_intf =3D usb_ifnum_to_if(usb_dev, interface_num); + if (!sibling_intf) + return NULL; + + sibling_hdev =3D usb_get_intfdata(sibling_intf); + + return sibling_hdev; } =20 -static int steelseries_probe(struct hid_device *hdev, const struct hid_dev= ice_id *id) +static int steelseries_probe(struct hid_device *hdev, + const struct hid_device_id *id) { + const struct steelseries_device_info *info =3D + (const struct steelseries_device_info *)id->driver_data; struct steelseries_device *sd; + struct usb_interface *intf; + struct hid_device *master_hdev; + u8 interface_num; int ret; =20 if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_SRWS1) { #if IS_BUILTIN(CONFIG_LEDS_CLASS) || \ - (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES)) + (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES)) return steelseries_srws1_probe(hdev, id); #else return -ENODEV; #endif } =20 - sd =3D devm_kzalloc(&hdev->dev, sizeof(*sd), GFP_KERNEL); - if (!sd) - return -ENOMEM; - hid_set_drvdata(hdev, sd); - sd->hdev =3D hdev; - sd->quirks =3D id->driver_data; + if (hid_is_usb(hdev)) { + intf =3D to_usb_interface(hdev->dev.parent); + interface_num =3D intf->cur_altsetting->desc.bInterfaceNumber; + } else { + return -ENODEV; + } =20 ret =3D hid_parse(hdev); if (ret) return ret; =20 - if (sd->quirks & STEELSERIES_ARCTIS_9 && - !steelseries_is_vendor_usage_page(hdev, 0xc0)) - return -ENODEV; + /* Let hid-generic handle non-vendor or unknown interfaces */ + if (interface_num !=3D info->sync_interface && + (!info->async_interface || interface_num !=3D info->async_interface)) + return hid_hw_start(hdev, HID_CONNECT_DEFAULT); =20 - spin_lock_init(&sd->lock); + if (interface_num =3D=3D info->sync_interface) { + sd =3D devm_kzalloc(&hdev->dev, sizeof(*sd), GFP_KERNEL); + if (!sd) + return -ENOMEM; =20 - ret =3D hid_hw_start(hdev, HID_CONNECT_DEFAULT); - if (ret) - return ret; + sd->hdev =3D hdev; + sd->info =3D info; + spin_lock_init(&sd->lock); =20 - ret =3D hid_hw_open(hdev); - if (ret) - return ret; + hid_set_drvdata(hdev, sd); =20 - if (steelseries_headset_battery_register(sd) < 0) - hid_err(sd->hdev, - "Failed to register battery for headset\n"); + ret =3D hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) + return ret; + + ret =3D hid_hw_open(hdev); + if (ret) + goto err_stop; + + sd->use_async_protocol =3D !(info->quirks & SS_QUIRK_STATUS_SYNC_POLL); + + if (info->capabilities & SS_CAP_BATTERY) { + ret =3D steelseries_battery_register(sd); + if (ret < 0) + hid_warn(hdev, "Failed to register battery: %d\n", ret); + } + + INIT_DELAYED_WORK(&sd->status_work, steelseries_status_timer_work_handle= r); + schedule_delayed_work(&sd->status_work, msecs_to_jiffies(100)); + + return 0; + } + + if (info->async_interface && interface_num =3D=3D info->async_interface) { + master_hdev =3D steelseries_get_sibling_hdev(hdev, info->sync_interface); =20 + if (!master_hdev || !hid_get_drvdata(master_hdev)) + return -EPROBE_DEFER; + + sd =3D hid_get_drvdata(master_hdev); + hid_set_drvdata(hdev, sd); + + ret =3D hid_hw_start(hdev, HID_CONNECT_DEFAULT); + if (ret) + return ret; + + ret =3D hid_hw_open(hdev); + if (ret) { + hid_hw_stop(hdev); + return ret; + } + return 0; + } + + return -ENODEV; + +err_stop: + hid_hw_stop(hdev); return ret; } =20 @@ -580,166 +1013,144 @@ static void steelseries_remove(struct hid_device *= hdev) { struct steelseries_device *sd; unsigned long flags; + struct usb_interface *intf; + u8 interface_num; =20 if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_SRWS1) { #if IS_BUILTIN(CONFIG_LEDS_CLASS) || \ - (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES)) + (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES)) hid_hw_stop(hdev); #endif return; } =20 - sd =3D hid_get_drvdata(hdev); - - spin_lock_irqsave(&sd->lock, flags); - sd->removed =3D true; - spin_unlock_irqrestore(&sd->lock, flags); - - cancel_delayed_work_sync(&sd->battery_work); - - hid_hw_close(hdev); - hid_hw_stop(hdev); -} - -static const __u8 *steelseries_srws1_report_fixup(struct hid_device *hdev, - __u8 *rdesc, unsigned int *rsize) -{ - if (hdev->vendor !=3D USB_VENDOR_ID_STEELSERIES || - hdev->product !=3D USB_DEVICE_ID_STEELSERIES_SRWS1) - return rdesc; - - if (*rsize >=3D 115 && rdesc[11] =3D=3D 0x02 && rdesc[13] =3D=3D 0xc8 - && rdesc[29] =3D=3D 0xbb && rdesc[40] =3D=3D 0xc5) { - hid_info(hdev, "Fixing up Steelseries SRW-S1 report descriptor\n"); - *rsize =3D sizeof(steelseries_srws1_rdesc_fixed); - return steelseries_srws1_rdesc_fixed; + if (hid_is_usb(hdev)) { + intf =3D to_usb_interface(hdev->dev.parent); + interface_num =3D intf->cur_altsetting->desc.bInterfaceNumber; + } else { + return; } - return rdesc; -} =20 -static uint8_t steelseries_headset_map_capacity(uint8_t capacity, uint8_t = min_in, uint8_t max_in) -{ - if (capacity >=3D max_in) - return 100; - if (capacity <=3D min_in) - return 0; - return (capacity - min_in) * 100 / (max_in - min_in); -} - -static int steelseries_headset_raw_event(struct hid_device *hdev, - struct hid_report *report, u8 *read_buf, - int size) -{ - struct steelseries_device *sd =3D hid_get_drvdata(hdev); - int capacity =3D sd->battery_capacity; - bool connected =3D sd->headset_connected; - bool charging =3D sd->battery_charging; - unsigned long flags; - - /* Not a headset */ - if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_SRWS1) - return 0; + sd =3D hid_get_drvdata(hdev); =20 - if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_ARCTIS_1 || - hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_ARCTIS_1_X) { - hid_dbg(sd->hdev, - "Parsing raw event for Arctis 1 headset (%*ph)\n", size, read_buf); - if (size < ARCTIS_1_BATTERY_RESPONSE_LEN || - memcmp(read_buf, arctis_1_battery_request, sizeof(arctis_1_battery_r= equest))) { - if (!delayed_work_pending(&sd->battery_work)) - goto request_battery; - return 0; - } - if (read_buf[2] =3D=3D 0x01) { - connected =3D false; - capacity =3D 100; - } else { - connected =3D true; - capacity =3D read_buf[3]; - } + if (!sd) { + hid_hw_stop(hdev); + return; } =20 - if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_ARCTIS_9) { - hid_dbg(sd->hdev, - "Parsing raw event for Arctis 9 headset (%*ph)\n", size, read_buf); - if (size < ARCTIS_9_BATTERY_RESPONSE_LEN) { - if (!delayed_work_pending(&sd->battery_work)) - goto request_battery; - return 0; - } + if (interface_num =3D=3D sd->info->sync_interface) { + if (sd->info->async_interface) { + struct hid_device *sibling; =20 - if (read_buf[0] =3D=3D 0xaa && read_buf[1] =3D=3D 0x01) { - connected =3D true; - charging =3D read_buf[4] =3D=3D 0x01; - - /* - * Found no official documentation about min and max. - * Values defined by testing. - */ - capacity =3D steelseries_headset_map_capacity(read_buf[3], 0x68, 0x9d); - } else { - /* - * Device is off and sends the last known status read_buf[1] =3D=3D 0x0= 3 or - * there is no known status of the device read_buf[0] =3D=3D 0x55 - */ - connected =3D false; - charging =3D false; + sibling =3D steelseries_get_sibling_hdev(hdev, + sd->info->async_interface); + if (sibling) + hid_set_drvdata(sibling, NULL); } - } =20 - if (connected !=3D sd->headset_connected) { - hid_dbg(sd->hdev, - "Connected status changed from %sconnected to %sconnected\n", - sd->headset_connected ? "" : "not ", - connected ? "" : "not "); - sd->headset_connected =3D connected; - steelseries_headset_set_wireless_status(hdev, connected); - } + spin_lock_irqsave(&sd->lock, flags); + sd->removed =3D true; + spin_unlock_irqrestore(&sd->lock, flags); =20 - if (capacity !=3D sd->battery_capacity) { - hid_dbg(sd->hdev, - "Battery capacity changed from %d%% to %d%%\n", - sd->battery_capacity, capacity); - sd->battery_capacity =3D capacity; - power_supply_changed(sd->battery); - } - - if (charging !=3D sd->battery_charging) { - hid_dbg(sd->hdev, - "Battery charging status changed from %scharging to %scharging\n", - sd->battery_charging ? "" : "not ", - charging ? "" : "not "); - sd->battery_charging =3D charging; - power_supply_changed(sd->battery); + cancel_delayed_work_sync(&sd->status_work); } =20 -request_battery: - spin_lock_irqsave(&sd->lock, flags); - if (!sd->removed) - schedule_delayed_work(&sd->battery_work, - msecs_to_jiffies(STEELSERIES_HEADSET_BATTERY_TIMEOUT_MS)); - spin_unlock_irqrestore(&sd->lock, flags); - - return 0; + hid_hw_close(hdev); + hid_hw_stop(hdev); } =20 static const struct hid_device_id steelseries_devices[] =3D { - { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRW= S1), - .driver_data =3D STEELSERIES_SRWS1 }, - - { /* SteelSeries Arctis 1 Wireless */ - HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_1), - .driver_data =3D STEELSERIES_ARCTIS_1 }, - - { /* SteelSeries Arctis 1 Wireless for XBox */ - HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_1_X), - .driver_data =3D STEELSERIES_ARCTIS_1_X }, - - { /* SteelSeries Arctis 9 Wireless for XBox */ - HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARC= TIS_9), - .driver_data =3D STEELSERIES_ARCTIS_9 }, - - { } + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_SRWS1), + .driver_data =3D (unsigned long)&srws1_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_1), + .driver_data =3D (unsigned long)&arctis_1_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_1_X), + .driver_data =3D (unsigned long)&arctis_1_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_7), + .driver_data =3D (unsigned long)&arctis_7_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_7_P), + .driver_data =3D (unsigned long)&arctis_1_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_7_X), + .driver_data =3D (unsigned long)&arctis_1_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_7_GEN2), + .driver_data =3D (unsigned long)&arctis_7_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_7_PLUS), + .driver_data =3D (unsigned long)&arctis_7_plus_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_7_PLUS_P), + .driver_data =3D (unsigned long)&arctis_7_plus_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_7_PLUS_X), + .driver_data =3D (unsigned long)&arctis_7_plus_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_7_PLUS_DESTINY), + .driver_data =3D (unsigned long)&arctis_7_plus_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_9), + .driver_data =3D (unsigned long)&arctis_9_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_3_P), + .driver_data =3D (unsigned long)&arctis_nova_3p_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_3_X), + .driver_data =3D (unsigned long)&arctis_nova_3p_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_5), + .driver_data =3D (unsigned long)&arctis_nova_5_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_5_X), + .driver_data =3D (unsigned long)&arctis_nova_5_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7), + .driver_data =3D (unsigned long)&arctis_nova_7_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_2), + .driver_data =3D (unsigned long)&arctis_nova_7_gen2_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_P), + .driver_data =3D (unsigned long)&arctis_nova_7_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X), + .driver_data =3D (unsigned long)&arctis_nova_7_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_2), + .driver_data =3D (unsigned long)&arctis_nova_7_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_3), + .driver_data =3D (unsigned long)&arctis_nova_7_gen2_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_DIABLO), + .driver_data =3D (unsigned long)&arctis_nova_7_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_DIABLO_2), + .driver_data =3D (unsigned long)&arctis_nova_7_gen2_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_WOW), + .driver_data =3D (unsigned long)&arctis_nova_7_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_GEN2), + .driver_data =3D (unsigned long)&arctis_nova_7_gen2_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_GEN2), + .driver_data =3D (unsigned long)&arctis_nova_7_gen2_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X_GEN2_2), + .driver_data =3D (unsigned long)&arctis_nova_7_gen2_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_PRO), + .driver_data =3D (unsigned long)&arctis_nova_pro_info }, + { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, + USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_PRO_X), + .driver_data =3D (unsigned long)&arctis_nova_pro_info }, + {} }; MODULE_DEVICE_TABLE(hid, steelseries_devices); =20 @@ -749,7 +1160,7 @@ static struct hid_driver steelseries_driver =3D { .probe =3D steelseries_probe, .remove =3D steelseries_remove, .report_fixup =3D steelseries_srws1_report_fixup, - .raw_event =3D steelseries_headset_raw_event, + .raw_event =3D steelseries_raw_event, }; =20 module_hid_driver(steelseries_driver); @@ -758,3 +1169,4 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Bastien Nocera "); MODULE_AUTHOR("Simon Wood "); MODULE_AUTHOR("Christian Mayer "); +MODULE_AUTHOR("Sriman Achanta "); --=20 2.53.0 From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qv1-f49.google.com (mail-qv1-f49.google.com [209.85.219.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9B7C73B8BA9 for ; Fri, 27 Feb 2026 23:50:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236253; cv=none; b=Q74BdXCzBcNITIkyAawGpp3n9Vfhnmmj5xYSd/vdsEGRoFNnwxaHc1qzwopGnzBoEroY5a7+mZXnKPNPqGMQu+0u7ZG+Bj6jU0Jc++LcYuJVfvf+SFRqOFjAyh7KRoeyhLwtlLfX350BB+1SNjB3YBy+OZ41WDT9phYoCYtkc3A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236253; c=relaxed/simple; bh=izLNzxbsr1aPEJ1kfO4J3cElOdE5JNlIeAUDXYIIamI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dlvCQEe8bvlfs6+st40Aqlv/fn7twIM9JbQcHKnmShwShVlw6lymBGVwt0zkK/g1DYCNvCNE00i+FmuHogi+/5iqPLSNF1rnDkRHhP+O/+F81iFqHfVfMTENB7pB6a1O9LYJwajQIbyVcsUTJCCnNkT3Lj0WVNR6WSvleth0QZ0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=GJXi7T4C; arc=none smtp.client-ip=209.85.219.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GJXi7T4C" Received: by mail-qv1-f49.google.com with SMTP id 6a1803df08f44-8954c9daaeaso31261926d6.1 for ; Fri, 27 Feb 2026 15:50:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236250; x=1772841050; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=525zY6xasiLGk1IKp9hGTD1dTZkiwSaXW6y/mnA7iXs=; b=GJXi7T4CotIiox2lNGXsh4ezWBuOtUrp6Hlgetk6THyzBH3/X27yOgtIHP2vMUDe3l ynaVhPO5U1HwXEtOdLRTeeDLrsNzUjn9fSmMnfqssr6U5372rld+pR/6JQubeUOll8Pn 5NYdIvdHpCn0qd1rvur1uOseaX+acyaIZdVgQ+uj75NDMxmDl7AZVZT3J2MW/zlCh++E fwXWHbVsEs35TnI4b/J/PcfeBBxVUTo9lAOkHs3f3GuVo2FprTn10vo/NcwY1y2bnzoF MzVwXuuFZWFdl4tAZbD1x5XSfhPyQP6LelwVVI+1qQ+9A477tP9Ll2r7GQscrhAMRCgO kuaQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236250; x=1772841050; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=525zY6xasiLGk1IKp9hGTD1dTZkiwSaXW6y/mnA7iXs=; b=WOtC9AHHvhYyneApOnmd0MyoDKUCIMVXXhKQOPO+RVuL/9bNZyhNZzIld6BmkAWzpe reS7VaQO83jVoz29Yj8EkLb94BI+nFf6pK/eKKdpQszz2WAnS+cZsKao3HqRLqSCqUDK KpFggwHPdBzdcp9cxWKZKZ9k6PlA1i+srFKGw4DeYiiJ5QBU09UtrQO4aSiIb+DjXC3N 1FlNmGNBOAEygopnSea9qz9GAs2T6wu6JtcJr3HBT1jFwzalXQZE3XdAVUJxhozJVo3w O+ZJQkrlbMfshMlmJOMdcciJzMiFJvraq/uygFc2TumAgVoiMVcqZT7xlmdhUXg2fZAm 7luA== X-Forwarded-Encrypted: i=1; AJvYcCUi2qfy+XtQJD6mK7UlhJD29hdNqp3hD4ee7+FxCboRxGzhPbZwYA47D3K0OLsAIUQbQh13rFvjw8OBPNI=@vger.kernel.org X-Gm-Message-State: AOJu0YzYRjA1/cSGa0XbEPp70b9kcJ6CyA/m+WZVRSBm4oApWhnkjLsF 7ivb1aAGNz/MsoJIhBXUURdm/lOoJ+/juGyuqu+W5J9ceaWcquEwKJpB X-Gm-Gg: ATEYQzy5D2Ja1hWgmqIrC0i/4ef/JfhdSgr/wrYeoxBaMreQjj4AqEDzDWJ3D8S1m1w Wr5QW9gHkWyIglO/iGTcq7L2cAMAs7Xvo0YMyZw0OOKNooAJIEpEpQdJRKbz332s+TmiWBsZCBM czWfbhcSBNesuAR2ZLVziR7aZRrbWTtdzVAT+BRv6OTzNvW2X6fINeSTdkegBJ2KT3Xkzzx18Zv HPxB9HOrHA/ge6u5uEqJPHqAg5Dthr6VU/NQ3ebJXtMXBRziFmCAIE9LwUIgoZuuu1QeNg35e4+ gUMh1xGwGitN2PCBu4EFv3LgNUeeDh/CrqFZJJpuGCYNFPHN43QFLgg4yiiZshnk57VrGTFQOsR YMZz8X4dbteF2FvgFLYFT/pqhRUMtSGY1c+4Ro5pqT+g0Uwlk9Fqq0tBfl/L+6zw9HDo6dFUxbj 9JiNAY50kMXbFlQ/HcmjQ+JXT6PvbsAZCWJuY+Z3jy6RsVFasoqw== X-Received: by 2002:a05:6214:21e1:b0:894:2c12:aed9 with SMTP id 6a1803df08f44-899d1d83d6emr71564676d6.5.1772236250506; Fri, 27 Feb 2026 15:50:50 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:50 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 05/18] HID: steelseries: Update Kconfig help text for expanded headset support Date: Fri, 27 Feb 2026 18:50:29 -0500 Message-ID: <20260227235042.410062-6-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The previous help text only mentioned the SRW-S1 steering wheel and Arctis = 1 Wireless for Xbox. Update it to reflect the full Arctis headset family no= w supported. Signed-off-by: Sriman Achanta --- drivers/hid/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index c1d9f7c6a5f2..29f05d4b7e30 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1142,7 +1142,8 @@ config HID_STEELSERIES depends on USB_HID help Support for Steelseries SRW-S1 steering wheel, and the Steelseries - Arctis 1 Wireless for XBox headset. + Arctis headset family (Arctis 1, Arctis 7, Arctis 7+, Arctis 9, + Arctis Nova 3, Arctis Nova 5, Arctis Nova 7, and Arctis Nova Pro). =20 config HID_SUNPLUS tristate "Sunplus wireless desktop" --=20 2.53.0 From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qv1-f41.google.com (mail-qv1-f41.google.com [209.85.219.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 638CA3A1A26 for ; Fri, 27 Feb 2026 23:50:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236255; cv=none; b=HPzjxI4DoCDHwwvRY9zlT0ISdbggRbhewZIxg0MTHT5uvZ4+z65nwipftCuFmEr2d1eukqjJDBRLcd6dYqbH1b3qY86V1Sicre+BcMeRZPSfjk+MsMkNHIiIzxtOdZRIRp6NmJ7BHaqBKwGUOeX2AhZVgs6bVOcSKLq2+HXJ4eM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236255; c=relaxed/simple; bh=VBgz8dWv/RZOqIZ1UTqkTSxvp3qtfDtnWzIqImJJKts=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=fHwH6VrMOI7eI9coq8pfZBFZHjLdjTH4gP1/edHDLSqJhnSDYGLILU5aKl/DK9rG2LrE1j+XDjyCuwlHtJvuN/73qU5FrzkFR3lNVgeNPhrESldf/ozCeu9gGNQDVq0uiURa6HXc6sAtMcEaH9qrYk699lZDBYCaVUQE062HdpA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=DNMWZAgI; arc=none smtp.client-ip=209.85.219.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="DNMWZAgI" Received: by mail-qv1-f41.google.com with SMTP id 6a1803df08f44-899ab9d13d0so21240446d6.0 for ; Fri, 27 Feb 2026 15:50:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236251; x=1772841051; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=FCNbZ5x3qYj3OeX10z/DckLa4xIjiGzODPbDZWrKbDM=; b=DNMWZAgI78ziyA61g1WKSDKQamUUhJOWAtg8hxPgDk0QVyE5OG0Mnng2ffnd7wMD9B GYpXKf477damO57KPdfd2+d/BwyAY0tXK1sGEHu+VErsPvxcQiBILTP6hMahOVOcgTJ7 1QdiVpshLF62gz0+cOjrxo/n2F2IN6mUPAQt+vsK2GLS3IPGDdOEkeI8nxHZ4C8vMrqD f/2vofrigctZErVuBjYuy9etSYH67CjMD6+wBB+t718hPiM7jOSRVuUTFp9lDGASvEOB JGNDpAeaq6xs8qlfwwe3NJeyWAG4Eavf8Z5D/yuiW8gxBHVw7C7+j3VKOpaA2iBiSoJV 34dw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236251; x=1772841051; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=FCNbZ5x3qYj3OeX10z/DckLa4xIjiGzODPbDZWrKbDM=; b=Zf9lQotTlYvwfod45kg9Giv1f9mISbRo3woI5sbNvOmRXADgHylfapJlCQ5mprq+bP ovtbN8qw3iWTeY9nmIkSSA1F+N9HFHSKw8/o4nJ4qPwZq8xX/x49Q4wUe2Fp8itMU9d2 Gnly0z7rt8wZr5BJyGZd2al/8ricUx4hLxKmTYLO4icjSzUy+VifMIMn361G/epaHJtz Qt1c1GSoqAx39+DePPpvESBUw7cDCJ2JQ9oio4ODs6NHLXq38B/pyWzhJHW5vREuxaEk kZlkP0XLoStMWWpLs/fFiit9JLNqhsoacKS7o17xyEwEmuYhYGD7ly/sCOwHEtYz7dGk RjUQ== X-Forwarded-Encrypted: i=1; AJvYcCWJnmBG4dy18SyzJdPvw56fgwvt8u36eZpfA/4aGaCxMdaDEjNxH7wERRYX4/eTWsAwvjhQQIaFtg/a6Yw=@vger.kernel.org X-Gm-Message-State: AOJu0Yxc24Rl11mmhHJBAxFz5iwMRhHSvoxkrhZfPiuxKk8fhopoXb1C 7vnF5OXujp/XRiOyu2tU4QLHGGWg+fF1GsrFHLAYdfpMXpaYJOPCn1Nck5jNrPjI X-Gm-Gg: ATEYQzzdNhSGj75xxnEJ1pJ+PysExSZvHPZeZFfjSV6FMG6L7J7QLwaPxjgZtgoBDpl p7Rb3/06GsUQVycqod7o0fNbUu/SyY3a5HN5SpdjrdhCMOfGQCG1WpGQ0tF7gT3MPDJAwxFsZa9 OZGeDbNcGyEnJjufK7760bfodtYmnHCj2S+U6lYCVm1eg4d9XAW+8qaWfy1sXPY+No+ty+KteE1 DINW7qEhWQpIj0WlSYxNBbCptTpqHHYSKLQiU5BjLnyRdJP2daxRbCMGHXxM0bVB0QsClWEgfQS FCT12p2tx+2WVkzKiMBONeIZYsrGDwm9m0+BYOu9p+sFfX2HRjCOIZvYo4cRbpDhldK3mfEHIqi 8BwglV1MCWgZgCHViGr6OOgXM8pOi0/6znpUF69EJRdO216XRDlKF0lCySllIAm6rVilPY/10RT 3qwGZ4E4VGZhjZiMzQE7xwNroaZG4hcFZIS/O9wcAZcxIxVqTUrv5ffoXrqiZx X-Received: by 2002:a05:6214:2503:b0:899:a778:6446 with SMTP id 6a1803df08f44-899d1e34c77mr78681266d6.32.1772236251363; Fri, 27 Feb 2026 15:50:51 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:50 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 06/18] HID: steelseries: Add ALSA sound card infrastructure Date: Fri, 27 Feb 2026 18:50:30 -0500 Message-ID: <20260227235042.410062-7-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Register an ALSA sound card for each supported Arctis headset to expose headset-specific audio controls to userspace. The card is created in steelseries_snd_register() and freed in steelseries_snd_unregister(), both guarded by a module compatibility check so that registration only occurs when both SND and HID_STEELSERIES are built-in or are both modules, avoiding a dependency mismatch. The Kconfig entry is updated to add SND as a dependency. Subsequent commits build on this infrastructure to register mixer controls. Signed-off-by: Sriman Achanta --- drivers/hid/Kconfig | 2 +- drivers/hid/hid-steelseries.c | 52 +++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 29f05d4b7e30..fcdb5406159a 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1139,7 +1139,7 @@ config STEAM_FF =20 config HID_STEELSERIES tristate "Steelseries devices support" - depends on USB_HID + depends on USB_HID && SND help Support for Steelseries SRW-S1 steering wheel, and the Steelseries Arctis headset family (Arctis 1, Arctis 7, Arctis 7+, Arctis 9, diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index d8ece8449255..b7f932cde98d 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -16,6 +16,7 @@ #include #include #include +#include =20 #include "hid-ids.h" =20 @@ -50,6 +51,8 @@ struct steelseries_device { u8 battery_capacity; bool battery_charging; =20 + struct snd_card *card; + spinlock_t lock; bool removed; }; @@ -830,6 +833,43 @@ static int steelseries_battery_register(struct steelse= ries_device *sd) return 0; } =20 +#if IS_BUILTIN(CONFIG_SND) || \ + (IS_MODULE(CONFIG_SND) && IS_MODULE(CONFIG_HID_STEELSERIES)) + +static int steelseries_snd_register(struct steelseries_device *sd) +{ + struct hid_device *hdev =3D sd->hdev; + int ret; + + ret =3D snd_card_new(&hdev->dev, -1, "SteelSeries", THIS_MODULE, + 0, &sd->card); + if (ret < 0) + return ret; + + sd->card->private_data =3D sd; + strscpy(sd->card->driver, "SteelSeries"); + strscpy(sd->card->shortname, hdev->name); + snprintf(sd->card->longname, sizeof(sd->card->longname), + "%s at USB %s", hdev->name, dev_name(&hdev->dev)); + + ret =3D snd_card_register(sd->card); + if (ret < 0) { + snd_card_free(sd->card); + sd->card =3D NULL; + return ret; + } + + return 0; +} + +static void steelseries_snd_unregister(struct steelseries_device *sd) +{ + if (sd->card) + snd_card_free(sd->card); +} + +#endif + static int steelseries_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { @@ -975,6 +1015,13 @@ static int steelseries_probe(struct hid_device *hdev, hid_warn(hdev, "Failed to register battery: %d\n", ret); } =20 +#if IS_BUILTIN(CONFIG_SND) || \ + (IS_MODULE(CONFIG_SND) && IS_MODULE(CONFIG_HID_STEELSERIES)) + ret =3D steelseries_snd_register(sd); + if (ret < 0) + hid_warn(hdev, "Failed to register sound card: %d\n", ret); +#endif + INIT_DELAYED_WORK(&sd->status_work, steelseries_status_timer_work_handle= r); schedule_delayed_work(&sd->status_work, msecs_to_jiffies(100)); =20 @@ -1048,6 +1095,11 @@ static void steelseries_remove(struct hid_device *hd= ev) hid_set_drvdata(sibling, NULL); } =20 +#if IS_BUILTIN(CONFIG_SND) || \ + (IS_MODULE(CONFIG_SND) && IS_MODULE(CONFIG_HID_STEELSERIES)) + steelseries_snd_unregister(sd); +#endif + spin_lock_irqsave(&sd->lock, flags); sd->removed =3D true; spin_unlock_irqrestore(&sd->lock, flags); --=20 2.53.0 From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qv1-f47.google.com (mail-qv1-f47.google.com [209.85.219.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5A999315D23 for ; Fri, 27 Feb 2026 23:50:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236255; cv=none; b=uZzJOUkkt10hncnBSXkRqJU5Q0zMV0zKEdxqQt4umiAJ+ew/gQ3upR2fWKG/+MXjuUdMR+tuf7t6aTbjjQJdTDpdKWeER7B/+ptMbfGBkp8ElYiuMyQOYjar3Tr+GldirI6y1ZZbsNmscu9Fm7CDI+2ACmIZz1qFPYqsZ+RU6wU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236255; c=relaxed/simple; bh=MqlkadY+BqRsCH3z7WySAsNH+ZSgs+fgIau8N6s87mk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Uw8p2+uElLoS1fAB+/BIxIdzPqMsJWgpgTkkKDkHxpse3+TMuuvAb52fDHHQM+iOrafpjinMjAIMQglovNEJ1nrqWdMWE8ULFbYHQdHZnPinyWha7z5iYUC5EJg4xmFAIznq7HeZMwPFITJA1aGgWIethnTJGOasNyS9EXoh/eI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=TzJ/tIZr; arc=none smtp.client-ip=209.85.219.47 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="TzJ/tIZr" Received: by mail-qv1-f47.google.com with SMTP id 6a1803df08f44-899c97c5afeso18220906d6.1 for ; Fri, 27 Feb 2026 15:50:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236252; x=1772841052; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=LR+mX76A/V5MFsVgIz9CElTVA/FBjHFWr96ghbRQi/o=; b=TzJ/tIZrLjEHTSaUZ8BNf3DiH/L3cyIduhoq41GSpwu0H1+1DTLtEHo91c4oXNuQ39 lLN1HZSdoQ8Xb52tr67dUMWG/qrq0bNXllh+nWRSsE930e7QVVfkEoV7V4TRLx6FSdi2 Q1hu62wdbN82fgjKLJoafUv8g2I1tkCHe4oeF1FvQy0O1ruV/CWB1HAsJU8jr72icX6s 8JCiex/rX2d5FvKo0S/WYKl9ldhSb5wKk3Q1QsqCiQpn+w2mKwKOla/6/x7g2TCtSpHg WPhxSdDWnet1dJsS7OyTgfeR5tAr2GCdfGlDxb7qkkNRAf2TMWSqw0MSGCYiUXEGcBTx aaXg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236252; x=1772841052; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=LR+mX76A/V5MFsVgIz9CElTVA/FBjHFWr96ghbRQi/o=; b=XcEUhWj4wSF5MU/DEVwt8XP21Rwryj81wh9r5vILqkQOj7GbxpoZvmwa4BI7iF/DaZ 4DwcwcHBXAPzK4bhbJSlQNErpxXJA8O61sG2f5UKnnNDWa/IZm/ijbKfF9J55iq7mdMa MZF5wncOpXiOp7THwiPL8n9K5EiVxTZMuMi+RJApVSsJk7iX3hgQINoPluxi8r1AlCxM 034vm+Y8pK1YXiNQCYWQC08ei3YMpIMVeRcYGfKND7aAPl+7UZAiKaenutJGv6CF0+pW +Q5ZDI/jeui07tX7MfwjsxN+x94KpDL2aejH6pvlY7VSCfghzLHFejNZjnnh/rjN3oRq IWbg== X-Forwarded-Encrypted: i=1; AJvYcCWzFjcwC6kujIZ7k1iG4g7r7HWPSe606p/SxGFInh0ZddkL+rqcelQMgVQwV4th4bNfip6I4zLoAn4wqNU=@vger.kernel.org X-Gm-Message-State: AOJu0Yw1CYjKMdUHwC6GJ87479yTO6ssTmbanjIpb8YMp3oLvyErL0hI 9BVtOdgsZraAXzBRhsRSlZFv8sSriSb35p0wMgkTKsg5PrYoMQM0lxH/ X-Gm-Gg: ATEYQzxdxP4SANtb/SHpXT5NzGB2RRNeOjK4LTBS+a3fekPtrTVjv4XM9f1y+XF2xeo VWRf8MtXJ10Par6L4mdQ0K+id+WDb2TAQ1IbSvzejN6j506cEnphAOs4Z6Fitr+mxJBezAd3uGg v9caJcIGtSNfA9+4BNv+tj0wg8Qwg+5wOMYuuGaj+F/zfKWmeQelow7/WxBvNvn4voWDZM7pDH6 z1vNfogTrs4S1AKPwdv4IK1p4+jEQL8jgG6B2KwoYZjlsK8U4vfKdna6/bzha05LXEkfXx6S+KT 6EEu8MN/ZdZ3hgBvplbdsekym1tjxEes3NGz18+q245FIGs2GQ9eyN73OPeGVqvW3C/Pk2h5Buz W46SOTVW7pRQs2i6d6AFnbvVPJhfZ0pKLY3OeIUDdG4D1q7yv68ogPqQM5iMm3YhTtSDbjtGSvY xh0OLYKh1rGVbFyOfZXLBZDIE/btrsLR9CfTFeEiUxvr3MJ1iiHQ== X-Received: by 2002:a05:6214:1c0a:b0:899:ad54:423e with SMTP id 6a1803df08f44-899d1dc1c95mr76326596d6.23.1772236252227; Fri, 27 Feb 2026 15:50:52 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:51 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 07/18] HID: steelseries: Add ChatMix ALSA mixer controls Date: Fri, 27 Feb 2026 18:50:31 -0500 Message-ID: <20260227235042.410062-8-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" ChatMix is a hardware feature on Arctis headsets that independently mixes game and chat audio streams. Expose the per-channel levels as read-only volatile ALSA integer mixer controls ("ChatMix Chat" and "ChatMix Game"), scaled to a 0-100 range. Controls are updated via snd_ctl_notify() whenever the underlying values change in a HID event. Each device family encodes the levels differently in HID reports; parsing is added for the Arctis 7, 7+, 9, Nova 5X, Nova 7, Nova 7 Gen2, and Nova Pro. The Nova 7 request path is extended to also fetch ChatMix data (0x06, 0x24). The Nova 5X gets its own device info entry as it uses a different status packet layout, and the Nova 7P is split into a separate entry since it does not expose ChatMix. Signed-off-by: Sriman Achanta --- drivers/hid/hid-steelseries.c | 189 ++++++++++++++++++++++++++++++---- 1 file changed, 169 insertions(+), 20 deletions(-) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index b7f932cde98d..30ee9da1deac 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -16,11 +16,13 @@ #include #include #include +#include #include =20 #include "hid-ids.h" =20 #define SS_CAP_BATTERY BIT(0) +#define SS_CAP_CHATMIX BIT(1) =20 #define SS_QUIRK_STATUS_SYNC_POLL BIT(0) =20 @@ -52,6 +54,10 @@ struct steelseries_device { bool battery_charging; =20 struct snd_card *card; + struct snd_ctl_elem_id chatmix_chat_id; + struct snd_ctl_elem_id chatmix_game_id; + u8 chatmix_chat; + u8 chatmix_game; =20 spinlock_t lock; bool removed; @@ -431,6 +437,7 @@ static int steelseries_arctis_7_request_status(struct h= id_device *hdev) int ret; const u8 connection_data[] =3D { 0x06, 0x14 }; const u8 battery_data[] =3D { 0x06, 0x18 }; + const u8 chatmix_data[] =3D { 0x06, 0x24 }; =20 ret =3D steelseries_send_feature_report(hdev, connection_data, sizeof(con= nection_data)); if (ret) @@ -438,7 +445,13 @@ static int steelseries_arctis_7_request_status(struct = hid_device *hdev) =20 msleep(10); =20 - return steelseries_send_feature_report(hdev, battery_data, sizeof(battery= _data)); + ret =3D steelseries_send_feature_report(hdev, battery_data, sizeof(batter= y_data)); + if (ret) + return ret; + + msleep(10); + + return steelseries_send_feature_report(hdev, chatmix_data, sizeof(chatmix= _data)); } =20 static int steelseries_arctis_9_request_status(struct hid_device *hdev) @@ -508,40 +521,52 @@ static void steelseries_arctis_1_parse_status(struct = steelseries_device *sd, static void steelseries_arctis_7_parse_status(struct steelseries_device *s= d, u8 *data, int size) { - if (size < 3) + if (size < 4) return; =20 if (data[0] =3D=3D 0x06) { - if (data[1] =3D=3D 0x14) + switch (data[1]) { + case 0x14: sd->headset_connected =3D (data[2] =3D=3D 0x03); - else if (data[1] =3D=3D 0x18) + break; + case 0x18: sd->battery_capacity =3D data[2]; + break; + case 0x24: + sd->chatmix_game =3D steelseries_map_capacity(data[2], 0xbf, 0xff); + sd->chatmix_chat =3D steelseries_map_capacity(data[3], 0xbf, 0xff); + break; + } } } =20 static void steelseries_arctis_7_plus_parse_status(struct steelseries_devi= ce *sd, u8 *data, int size) { - if (size < 4) + if (size < 6) return; =20 if (data[0] =3D=3D 0xb0) { sd->headset_connected =3D !(data[1] =3D=3D 0x01); sd->battery_capacity =3D steelseries_map_capacity(data[2], 0x00, 0x04); sd->battery_charging =3D (data[3] =3D=3D 0x01); + sd->chatmix_game =3D steelseries_map_capacity(data[4], 0x00, 0x64); + sd->chatmix_chat =3D steelseries_map_capacity(data[5], 0x00, 0x64); } } =20 static void steelseries_arctis_9_parse_status(struct steelseries_device *s= d, u8 *data, int size) { - if (size < 5) + if (size < 11) return; =20 if (data[0] =3D=3D 0xaa) { sd->headset_connected =3D (data[1] =3D=3D 0x01); sd->battery_charging =3D (data[4] =3D=3D 0x01); sd->battery_capacity =3D steelseries_map_capacity(data[3], 0x64, 0x9A); + sd->chatmix_game =3D steelseries_map_capacity(data[9], 0x00, 0x13); + sd->chatmix_chat =3D steelseries_map_capacity(data[10], 0x00, 0x13); } } =20 @@ -570,23 +595,40 @@ static void steelseries_arctis_nova_5_parse_status(st= ruct steelseries_device *sd } } =20 +static void steelseries_arctis_nova_5x_parse_status(struct steelseries_dev= ice *sd, + u8 *data, int size) +{ + if (size < 7) + return; + + if (data[0] =3D=3D 0xb0) { + sd->headset_connected =3D !(data[1] =3D=3D 0x02); + sd->battery_capacity =3D data[3]; + sd->battery_charging =3D (data[4] =3D=3D 0x01); + sd->chatmix_chat =3D data[5]; + sd->chatmix_game =3D data[6]; + } +} + static void steelseries_arctis_nova_7_parse_status(struct steelseries_devi= ce *sd, u8 *data, int size) { - if (size < 4) + if (size < 6) return; =20 if (data[0] =3D=3D 0xb0) { sd->headset_connected =3D (data[1] =3D=3D 0x03); sd->battery_capacity =3D steelseries_map_capacity(data[2], 0x00, 0x04); sd->battery_charging =3D (data[3] =3D=3D 0x01); + sd->chatmix_game =3D data[4]; + sd->chatmix_chat =3D data[5]; } } =20 static void steelseries_arctis_nova_7_gen2_parse_status(struct steelseries= _device *sd, u8 *data, int size) { - if (size < 4) + if (size < 6) return; =20 switch (data[0]) { @@ -594,6 +636,8 @@ static void steelseries_arctis_nova_7_gen2_parse_status= (struct steelseries_devic sd->headset_connected =3D (data[1] =3D=3D 0x03); sd->battery_capacity =3D data[2]; sd->battery_charging =3D (data[3] =3D=3D 0x01); + sd->chatmix_game =3D data[4]; + sd->chatmix_chat =3D data[5]; break; case 0xb7: sd->battery_capacity =3D data[1]; @@ -604,6 +648,10 @@ static void steelseries_arctis_nova_7_gen2_parse_statu= s(struct steelseries_devic case 0xbb: sd->battery_charging =3D (data[1] =3D=3D 0x01); break; + case 0x45: + sd->chatmix_game =3D data[1]; + sd->chatmix_chat =3D data[2]; + break; } } =20 @@ -617,6 +665,9 @@ static void steelseries_arctis_nova_pro_parse_status(st= ruct steelseries_device * sd->headset_connected =3D (data[15] =3D=3D 0x08 || data[15] =3D=3D 0x02); sd->battery_capacity =3D steelseries_map_capacity(data[6], 0x00, 0x08); sd->battery_charging =3D (data[15] =3D=3D 0x02); + } else if (data[0] =3D=3D 0x07 && data[1] =3D=3D 0x45) { + sd->chatmix_game =3D data[2]; + sd->chatmix_chat =3D data[3]; } } =20 @@ -636,7 +687,7 @@ static const struct steelseries_device_info arctis_1_in= fo =3D { =20 static const struct steelseries_device_info arctis_7_info =3D { .sync_interface =3D 5, - .capabilities =3D SS_CAP_BATTERY, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .request_status =3D steelseries_arctis_7_request_status, .parse_status =3D steelseries_arctis_7_parse_status, @@ -644,7 +695,7 @@ static const struct steelseries_device_info arctis_7_in= fo =3D { =20 static const struct steelseries_device_info arctis_7_plus_info =3D { .sync_interface =3D 3, - .capabilities =3D SS_CAP_BATTERY, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_7_plus_parse_status, @@ -652,7 +703,7 @@ static const struct steelseries_device_info arctis_7_pl= us_info =3D { =20 static const struct steelseries_device_info arctis_9_info =3D { .sync_interface =3D 0, - .capabilities =3D SS_CAP_BATTERY, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .request_status =3D steelseries_arctis_9_request_status, .parse_status =3D steelseries_arctis_9_parse_status, @@ -674,7 +725,23 @@ static const struct steelseries_device_info arctis_nov= a_5_info =3D { .parse_status =3D steelseries_arctis_nova_5_parse_status, }; =20 +static const struct steelseries_device_info arctis_nova_5x_info =3D { + .sync_interface =3D 3, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX, + .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .request_status =3D steelseries_arctis_nova_request_status, + .parse_status =3D steelseries_arctis_nova_5x_parse_status, +}; + static const struct steelseries_device_info arctis_nova_7_info =3D { + .sync_interface =3D 3, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX, + .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .request_status =3D steelseries_arctis_nova_request_status, + .parse_status =3D steelseries_arctis_nova_7_parse_status, +}; + +static const struct steelseries_device_info arctis_nova_7p_info =3D { .sync_interface =3D 3, .capabilities =3D SS_CAP_BATTERY, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, @@ -685,14 +752,14 @@ static const struct steelseries_device_info arctis_no= va_7_info =3D { static const struct steelseries_device_info arctis_nova_7_gen2_info =3D { .sync_interface =3D 3, .async_interface =3D 5, - .capabilities =3D SS_CAP_BATTERY, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_7_gen2_parse_status, }; =20 static const struct steelseries_device_info arctis_nova_pro_info =3D { .sync_interface =3D 4, - .capabilities =3D SS_CAP_BATTERY, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .request_status =3D steelseries_arctis_nova_pro_request_status, .parse_status =3D steelseries_arctis_nova_pro_parse_status, @@ -836,6 +903,57 @@ static int steelseries_battery_register(struct steelse= ries_device *sd) #if IS_BUILTIN(CONFIG_SND) || \ (IS_MODULE(CONFIG_SND) && IS_MODULE(CONFIG_HID_STEELSERIES)) =20 +static int steelseries_chatmix_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type =3D SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count =3D 1; + uinfo->value.integer.min =3D 0; + uinfo->value.integer.max =3D 100; + uinfo->value.integer.step =3D 1; + return 0; +} + +static int steelseries_chatmix_chat_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct steelseries_device *sd =3D snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&sd->lock, flags); + ucontrol->value.integer.value[0] =3D sd->chatmix_chat; + spin_unlock_irqrestore(&sd->lock, flags); + return 0; +} + +static int steelseries_chatmix_game_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct steelseries_device *sd =3D snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&sd->lock, flags); + ucontrol->value.integer.value[0] =3D sd->chatmix_game; + spin_unlock_irqrestore(&sd->lock, flags); + return 0; +} + +static const struct snd_kcontrol_new steelseries_chatmix_chat_control =3D { + .iface =3D SNDRV_CTL_ELEM_IFACE_MIXER, + .name =3D "ChatMix Chat", + .access =3D SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info =3D steelseries_chatmix_info, + .get =3D steelseries_chatmix_chat_get, +}; + +static const struct snd_kcontrol_new steelseries_chatmix_game_control =3D { + .iface =3D SNDRV_CTL_ELEM_IFACE_MIXER, + .name =3D "ChatMix Game", + .access =3D SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info =3D steelseries_chatmix_info, + .get =3D steelseries_chatmix_game_get, +}; + static int steelseries_snd_register(struct steelseries_device *sd) { struct hid_device *hdev =3D sd->hdev; @@ -852,14 +970,32 @@ static int steelseries_snd_register(struct steelserie= s_device *sd) snprintf(sd->card->longname, sizeof(sd->card->longname), "%s at USB %s", hdev->name, dev_name(&hdev->dev)); =20 - ret =3D snd_card_register(sd->card); - if (ret < 0) { - snd_card_free(sd->card); - sd->card =3D NULL; - return ret; + if (sd->info->capabilities & SS_CAP_CHATMIX) { + struct snd_kcontrol *kctl; + + kctl =3D snd_ctl_new1(&steelseries_chatmix_chat_control, sd); + ret =3D snd_ctl_add(sd->card, kctl); + if (ret < 0) + goto err_free_card; + sd->chatmix_chat_id =3D kctl->id; + + kctl =3D snd_ctl_new1(&steelseries_chatmix_game_control, sd); + ret =3D snd_ctl_add(sd->card, kctl); + if (ret < 0) + goto err_free_card; + sd->chatmix_game_id =3D kctl->id; } =20 + ret =3D snd_card_register(sd->card); + if (ret < 0) + goto err_free_card; + return 0; + +err_free_card: + snd_card_free(sd->card); + sd->card =3D NULL; + return ret; } =20 static void steelseries_snd_unregister(struct steelseries_device *sd) @@ -877,6 +1013,8 @@ static int steelseries_raw_event(struct hid_device *hd= ev, u8 old_capacity; bool old_connected; bool old_charging; + u8 old_chatmix_chat; + u8 old_chatmix_game; bool is_async_interface =3D false; =20 if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_SRWS1) @@ -888,6 +1026,8 @@ static int steelseries_raw_event(struct hid_device *hd= ev, old_capacity =3D sd->battery_capacity; old_connected =3D sd->headset_connected; old_charging =3D sd->battery_charging; + old_chatmix_chat =3D sd->chatmix_chat; + old_chatmix_game =3D sd->chatmix_game; =20 if (hid_is_usb(hdev)) { struct usb_interface *intf =3D to_usb_interface(hdev->dev.parent); @@ -932,6 +1072,15 @@ static int steelseries_raw_event(struct hid_device *h= dev, power_supply_changed(sd->battery); } =20 + if (sd->card) { + if (sd->chatmix_chat !=3D old_chatmix_chat) + snd_ctl_notify(sd->card, SNDRV_CTL_EVENT_MASK_VALUE, + &sd->chatmix_chat_id); + if (sd->chatmix_game !=3D old_chatmix_game) + snd_ctl_notify(sd->card, SNDRV_CTL_EVENT_MASK_VALUE, + &sd->chatmix_game_id); + } + return 0; } =20 @@ -1159,7 +1308,7 @@ static const struct hid_device_id steelseries_devices= [] =3D { .driver_data =3D (unsigned long)&arctis_nova_5_info }, { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_5_X), - .driver_data =3D (unsigned long)&arctis_nova_5_info }, + .driver_data =3D (unsigned long)&arctis_nova_5x_info }, { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7), .driver_data =3D (unsigned long)&arctis_nova_7_info }, @@ -1168,7 +1317,7 @@ static const struct hid_device_id steelseries_devices= [] =3D { .driver_data =3D (unsigned long)&arctis_nova_7_gen2_info }, { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_P), - .driver_data =3D (unsigned long)&arctis_nova_7_info }, + .driver_data =3D (unsigned long)&arctis_nova_7p_info }, { HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_ARCTIS_NOVA_7_X), .driver_data =3D (unsigned long)&arctis_nova_7_info }, --=20 2.53.0 From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qk1-f180.google.com (mail-qk1-f180.google.com [209.85.222.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DCEC1361DBB for ; Fri, 27 Feb 2026 23:50:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236256; cv=none; b=orysBtI6CFnSMtOlWiIYUY0WQGOY+e6LFPayuE0cG7crN7xs5SDBIZZEGmtd5ws94ERf3ByAQuX95nbA19re0u+jIs3BmThBRzuwJjfBOxXCvLVi+WqaMMlXKkbsjAOcNiu3XQpZ9UzYthGZq1cnSat17tqd9asC/WiDx5SEkW0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236256; c=relaxed/simple; bh=2FHKYmksRBuIqU2g2Bvt3OP3ihliPWz86+bn9cWbCxg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PlWoKf5sOivftQ5Vr1JC2Wjs81cCDI6NPu3y3iRTHQUuRuyn2i2ioug9suF34XH6y/NQACai46WpFJh2AtlnTA3E49wJp0umTKyeNrc2/NQUbih6Rf+DSjmkwZEvNpmGEZs6CJcUKSMvaDYgy8IIPjHosvlHVvC9sB0PfpbUSmQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=CaKsZWnH; arc=none smtp.client-ip=209.85.222.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="CaKsZWnH" Received: by mail-qk1-f180.google.com with SMTP id af79cd13be357-8c711959442so320410685a.0 for ; Fri, 27 Feb 2026 15:50:53 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236253; x=1772841053; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=GDE5LaOTr3gg6toPYO3Z0KqvP/5l8l60DfJezBp2lfM=; b=CaKsZWnHn2tCOSepSUlSaalqKdHS8vjKHNj2iqRAhjycOKSsYRDgrwQIy7S0VxDW8H cJR2KtwOJMe/eDTEyBN+7qW1aGK5vQ4hw0cOr/sjYRkHBmBGydN5YQA+9xKlz/ci/Ee3 g9k63+p95pz2ywiq4n+5fxaVkB7XXYrOGNRFBw3U6s10wBOrOaYx1MFa9Dapkh14mH2Q h8Md2iTMSSnfI9P/mh1/M1n2xzJiITe4tnTX4izaKPRrAud/ePTw2uzyCTKJNFvJW5w1 bRJ9We9Il5f9JOYn4ifFK/Qlu8U5DRtv9UBPG8ok9TECJb40j8wYaPIhiYEe6LHhyIZf iXSw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236253; x=1772841053; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=GDE5LaOTr3gg6toPYO3Z0KqvP/5l8l60DfJezBp2lfM=; b=li/uPfzJ0qzrqZ3X7AZcr+EMHQeiu6ojGweMr/DoC/0BJdLerUWWQY7jzATa3ItDRo f+rYUauXTv1V4fzk8Er1YLGSjvVR3PQSJIE0XnRBHKW5n0nu2LMJIJ/+iXS2ErdklNHF rk66zHraOfBrI3z5XfV9nOd4jjaK0cRuWyvxTmskLA82NbozFBY0RvJ9MSyogqu2NQ7r Ale2XwTBVkuDZMPUSWhGsAJrDZzLdT4RPwncDXp1sxO/fSPd1h997q+xkxiJ3CdfBswp 2I1VEqhPD5UPcHW3Mb8dQ85f47c8oBYwH4zkQTMmmA5Vqtdf7oGkxF6aCMi8yoB+TTbM rqbg== X-Forwarded-Encrypted: i=1; AJvYcCVtloeFUfG6QEqf/Evy294Pp01oMYmzbAM2fzQkgFmc7EQfWRnTCME7+2Wq8bRuAxpGXcBYo7NutS22rAc=@vger.kernel.org X-Gm-Message-State: AOJu0YwCeON7rGfXoEM05t6QhY4UHy7KfoszfaXb77jhNj63sj3yXZRZ B4A2je3RsVaO7ZBpEx6L24+4vKbXZjxqXuBTVjWbCm+K6nbgic1YQGge X-Gm-Gg: ATEYQzxIBJo/HNTcqVvDTKinl6EokA2lJ/8U5+CQ2Hc1fng+x83WZOl+cPoiyMCIC3H 0NYgszJU8W5HBZN6nAQNA1QRqIJTHTjJWVmdIbj6qs1IXLtA67CVGSDyiEKfLFDIN+asBKDLsgb v8FZSl9i1G/Xt3hRJI6EDGy4sKh9Db4QHUznL1NtyZlHiH5O8z+S8rIQkYgxxJkJ+Uzn8IxnAD3 Ym7KaOEYLTa1Lqmh3ziFQpOvv8OHteb6LUm/kUuswfHjnQMrQblG6azhfN5P+aBy7PxanzJpvTn OwLEGn8yz0n08Ya0Ypm8THO/eeF7ape3ijKv87auOwcBv1/2u54RV4uZuKVgpM4lXE4bLNje1c+ YiFqDNBNnE/syTYBNOfZ4vK5KeIU75OmloByobAL3YupzhUDorKV6c9a0wENcRUOOM5406KPOPZ s6RCZlzd1b4tj9JSDmymYtSM7ysn3m5GRHLvXfg5jirzRdEtYrcg== X-Received: by 2002:a05:620a:4586:b0:8c6:a587:377f with SMTP id af79cd13be357-8cbbf3f632dmr1054563185a.36.1772236252890; Fri, 27 Feb 2026 15:50:52 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:52 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 08/18] HID: steelseries: Add mic mute ALSA mixer control Date: Fri, 27 Feb 2026 18:50:32 -0500 Message-ID: <20260227235042.410062-9-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Expose the hardware microphone mute button state as a read-only volatile ALSA boolean mixer control ("Mic Muted"). State is decoded from HID events reported by the Arctis Nova 7 Gen2 (0xb0 status packet and 0x52 async event) and the Nova Pro (initial status packet). Changes are propagated to userspace via snd_ctl_notify(). Signed-off-by: Sriman Achanta --- drivers/hid/hid-steelseries.c | 60 +++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index 30ee9da1deac..3de8e1555263 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -23,6 +23,7 @@ =20 #define SS_CAP_BATTERY BIT(0) #define SS_CAP_CHATMIX BIT(1) +#define SS_CAP_MIC_MUTE BIT(2) =20 #define SS_QUIRK_STATUS_SYNC_POLL BIT(0) =20 @@ -56,8 +57,10 @@ struct steelseries_device { struct snd_card *card; struct snd_ctl_elem_id chatmix_chat_id; struct snd_ctl_elem_id chatmix_game_id; + struct snd_ctl_elem_id mic_muted_id; u8 chatmix_chat; u8 chatmix_game; + bool mic_muted; =20 spinlock_t lock; bool removed; @@ -628,7 +631,7 @@ static void steelseries_arctis_nova_7_parse_status(stru= ct steelseries_device *sd static void steelseries_arctis_nova_7_gen2_parse_status(struct steelseries= _device *sd, u8 *data, int size) { - if (size < 6) + if (size < 10) return; =20 switch (data[0]) { @@ -638,6 +641,7 @@ static void steelseries_arctis_nova_7_gen2_parse_status= (struct steelseries_devic sd->battery_charging =3D (data[3] =3D=3D 0x01); sd->chatmix_game =3D data[4]; sd->chatmix_chat =3D data[5]; + sd->mic_muted =3D (data[9] =3D=3D 0x01); break; case 0xb7: sd->battery_capacity =3D data[1]; @@ -652,6 +656,9 @@ static void steelseries_arctis_nova_7_gen2_parse_status= (struct steelseries_devic sd->chatmix_game =3D data[1]; sd->chatmix_chat =3D data[2]; break; + case 0x52: + sd->mic_muted =3D (data[2] =3D=3D 0x01); + break; } } =20 @@ -665,6 +672,7 @@ static void steelseries_arctis_nova_pro_parse_status(st= ruct steelseries_device * sd->headset_connected =3D (data[15] =3D=3D 0x08 || data[15] =3D=3D 0x02); sd->battery_capacity =3D steelseries_map_capacity(data[6], 0x00, 0x08); sd->battery_charging =3D (data[15] =3D=3D 0x02); + sd->mic_muted =3D (data[9] =3D=3D 0x01); } else if (data[0] =3D=3D 0x07 && data[1] =3D=3D 0x45) { sd->chatmix_game =3D data[2]; sd->chatmix_chat =3D data[3]; @@ -752,14 +760,14 @@ static const struct steelseries_device_info arctis_no= va_7p_info =3D { static const struct steelseries_device_info arctis_nova_7_gen2_info =3D { .sync_interface =3D 3, .async_interface =3D 5, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_MIC_MUTE, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_7_gen2_parse_status, }; =20 static const struct steelseries_device_info arctis_nova_pro_info =3D { .sync_interface =3D 4, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_MIC_MUTE, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .request_status =3D steelseries_arctis_nova_pro_request_status, .parse_status =3D steelseries_arctis_nova_pro_parse_status, @@ -938,6 +946,29 @@ static int steelseries_chatmix_game_get(struct snd_kco= ntrol *kcontrol, return 0; } =20 +static int steelseries_mic_muted_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type =3D SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count =3D 1; + uinfo->value.integer.min =3D 0; + uinfo->value.integer.max =3D 1; + uinfo->value.integer.step =3D 1; + return 0; +} + +static int steelseries_mic_muted_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct steelseries_device *sd =3D snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&sd->lock, flags); + ucontrol->value.integer.value[0] =3D sd->mic_muted; + spin_unlock_irqrestore(&sd->lock, flags); + return 0; +} + static const struct snd_kcontrol_new steelseries_chatmix_chat_control =3D { .iface =3D SNDRV_CTL_ELEM_IFACE_MIXER, .name =3D "ChatMix Chat", @@ -954,6 +985,14 @@ static const struct snd_kcontrol_new steelseries_chatm= ix_game_control =3D { .get =3D steelseries_chatmix_game_get, }; =20 +static const struct snd_kcontrol_new steelseries_mic_muted_control =3D { + .iface =3D SNDRV_CTL_ELEM_IFACE_MIXER, + .name =3D "Mic Muted", + .access =3D SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info =3D steelseries_mic_muted_info, + .get =3D steelseries_mic_muted_get, +}; + static int steelseries_snd_register(struct steelseries_device *sd) { struct hid_device *hdev =3D sd->hdev; @@ -986,6 +1025,16 @@ static int steelseries_snd_register(struct steelserie= s_device *sd) sd->chatmix_game_id =3D kctl->id; } =20 + if (sd->info->capabilities & SS_CAP_MIC_MUTE) { + struct snd_kcontrol *kctl; + + kctl =3D snd_ctl_new1(&steelseries_mic_muted_control, sd); + ret =3D snd_ctl_add(sd->card, kctl); + if (ret < 0) + goto err_free_card; + sd->mic_muted_id =3D kctl->id; + } + ret =3D snd_card_register(sd->card); if (ret < 0) goto err_free_card; @@ -1015,6 +1064,7 @@ static int steelseries_raw_event(struct hid_device *h= dev, bool old_charging; u8 old_chatmix_chat; u8 old_chatmix_game; + bool old_mic_muted; bool is_async_interface =3D false; =20 if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_SRWS1) @@ -1028,6 +1078,7 @@ static int steelseries_raw_event(struct hid_device *h= dev, old_charging =3D sd->battery_charging; old_chatmix_chat =3D sd->chatmix_chat; old_chatmix_game =3D sd->chatmix_game; + old_mic_muted =3D sd->mic_muted; =20 if (hid_is_usb(hdev)) { struct usb_interface *intf =3D to_usb_interface(hdev->dev.parent); @@ -1079,6 +1130,9 @@ static int steelseries_raw_event(struct hid_device *h= dev, if (sd->chatmix_game !=3D old_chatmix_game) snd_ctl_notify(sd->card, SNDRV_CTL_EVENT_MASK_VALUE, &sd->chatmix_game_id); + if (sd->mic_muted !=3D old_mic_muted) + snd_ctl_notify(sd->card, SNDRV_CTL_EVENT_MASK_VALUE, + &sd->mic_muted_id); } =20 return 0; --=20 2.53.0 From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qt1-f170.google.com (mail-qt1-f170.google.com [209.85.160.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B2A453E9F8C for ; Fri, 27 Feb 2026 23:50:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236256; cv=none; b=lFY0hTHg9so8O4hT2eyjS5fhzQgdbtJOdb0WVogUkHcE8QE/Uj/tYoKJTEulmh7x3Q1NuWhDDGac9hMA+073Ds27hZxp6d0Z2k2HnBMfSMvNHAmaFXdBuI2xkQMH8rN+uW70IIwS5LHTZWAVARvO5CVtLIUFnhMeoeyHFQGcQWw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236256; c=relaxed/simple; bh=ApFHIa2OvHLaRNNl97SlTyuVHsFXhVbG+hJD/eSsgL4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=BYK8P9bIK9hZPo8M2OSuV80Qksbx+gUPnt4EIg9R8zJtyoW3+TH0X3+6qkNCu92kLHzXHi7Ym87Gu7CABf6FVrYXLt/gSMJDFX9KdVP0hSytF918gHeUn5duVFJ3DAA/G4NLLEPCt5sBgwz/pr+k1y/Pqab+Bkn7Ob+YkILLrOU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=jhXVC0zA; arc=none smtp.client-ip=209.85.160.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="jhXVC0zA" Received: by mail-qt1-f170.google.com with SMTP id d75a77b69052e-507373bffd9so23388401cf.2 for ; Fri, 27 Feb 2026 15:50:54 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236254; x=1772841054; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=/HYWiMRGVYGT7asuCB2h3B0PDkbjhuyxsF7A0cun8+o=; b=jhXVC0zAd8ZH8SDkDtRA4MqzZVlGv5Mhx50fTwfvzT2ftGksfkq0Ra6FwczOUZqG4P D+zppPsJkmgighAqIvnWCPn1zT7X3CjbgrIR0EXECpAIxXE8Caoa7ZdL4vKBY6mpGnOk xi4+PsUAw9DX2GZoAGs22Kr/LJrcz8whWFUygwHLCxUAoA3WRy6U0vmbvp1yIBvIEpQr Svq60k6EztFOZom8WSk/ET+P/HQTJRuwe7xyh/yrbuuLca3D/3VQKNvmRMuKf6IoaUtG n8ksrFYHiWaYXqvakuXn6Jeyw4LMkanNMB2CNcPjJMfIB6w2BwQU+MNThz3MM5OONvuM cwDw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236254; x=1772841054; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=/HYWiMRGVYGT7asuCB2h3B0PDkbjhuyxsF7A0cun8+o=; b=lQDqUVyVek2M99wp6TndEnrtU6V0wxcGjUcPXcVA/+zpNObxkVMXjCEcESOZ18rEkF rJKa7X8a9FA95VrWNpMlXuhxUq6oCXs6GZCwtwaRmhV24WjVUBpPr2BHQU+HraApgnOj xTJw1OXQNkCiZxdl8m0eewSKa9tEhVTeA5OJrH+msx3uMPkW4wlrN3WGJ8KX4hxBs4Jf nZC9aN/NjF8iL4KtGE42DLSq8JhmDdVwZOdOvjYWBeOw/i+5jX+e/TDPqI4zbHCz5C/J dZktotaaKdcGGkyi4kczvAV3Ubjw0391gNJ9r6Dzldq70yZViSDjWOGYvuOjVnAWqfED Xz5Q== X-Forwarded-Encrypted: i=1; AJvYcCUGUgE3qLIKw0yNax05dsTIDbpUh90Z/xqHrk1sj/OHPwRiw7DH2p/fZTWXKKxh2rHV8e1e208/jiIFZdE=@vger.kernel.org X-Gm-Message-State: AOJu0YzOvrFGnrH/Dh9PAg7/nT5TTTxmRWpb8Glz7cC6LwwS7Q31ntBI ljNDMQUNzxxEEXCcnwzL+fYD6b6zlJWJmw+GwisgIQ68aSnVz/mZekn0 X-Gm-Gg: ATEYQzxVjbCq5BwCNxiUQ1/6HDXvFgHGfW+b+Oan2R3RTdiKj7B5uFIUHpgxXbfLkGu bWIRYGQ0VhPkers+SyHDHRebLDX+RkzhmhQYUfww7GX5QotopvQWk7F2pqYsLySpnHj+kXXdckg Ep6YgJA7rUUujDHdAaqpgqNzgn/trtcB05kKTsU0bq3EGvoilY93dgCjWgYSW0wBuoyDYAMW+jq mCyPXNrbsnIS/FRCgp8LQYEPNSQ36/qkIfqafyX/2wKcaIcTd5FYqum01L3dywea0xl1MQTUcmr cnLp2mDIK75T0iAufb9LBngBNT80h8CTa81yjNTg1PsN3+gRWzvfxPZGDZ7mqYjYGQQ42QXjGrA 1/PE78jmmA/AM4nTgw91kZbXoxW9gHX66ejdCzDLflAUfgTBJuD3JKJntq8E619ifnnxFfoB8Jq iEVY661Lgu9fQeqKA2wLheI6yJlKKEq+zJn0bPy41Tvo+6da33sg== X-Received: by 2002:ac8:7f90:0:b0:506:20bc:f66a with SMTP id d75a77b69052e-5075290ba3dmr62486901cf.34.1772236253671; Fri, 27 Feb 2026 15:50:53 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:53 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 09/18] HID: steelseries: Add Bluetooth state sysfs attributes Date: Fri, 27 Feb 2026 18:50:33 -0500 Message-ID: <20260227235042.410062-10-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add read-only sysfs attributes bt_enabled and bt_device_connected that reflect the current Bluetooth radio state for headsets that support it. Attributes are registered via an attribute group with an is_visible callback so they only appear on capable devices. Bluetooth state is decoded from the following HID reports: - Arctis Nova 7 Gen2: 0xb0 initial status packet and 0xb5 async events - Arctis Nova Pro: initial 0x06/0x14 status packet Returns -ENODEV if the headset is not currently connected. Signed-off-by: Sriman Achanta --- drivers/hid/hid-steelseries.c | 111 +++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index 3de8e1555263..8c6116d02f19 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -24,6 +24,8 @@ #define SS_CAP_BATTERY BIT(0) #define SS_CAP_CHATMIX BIT(1) #define SS_CAP_MIC_MUTE BIT(2) +#define SS_CAP_BT_ENABLED BIT(3) +#define SS_CAP_BT_DEVICE_CONNECTED BIT(4) =20 #define SS_QUIRK_STATUS_SYNC_POLL BIT(0) =20 @@ -62,6 +64,9 @@ struct steelseries_device { u8 chatmix_game; bool mic_muted; =20 + bool bt_enabled; + bool bt_device_connected; + spinlock_t lock; bool removed; }; @@ -641,6 +646,20 @@ static void steelseries_arctis_nova_7_gen2_parse_statu= s(struct steelseries_devic sd->battery_charging =3D (data[3] =3D=3D 0x01); sd->chatmix_game =3D data[4]; sd->chatmix_chat =3D data[5]; + switch (data[6]) { + case 0x00: + sd->bt_enabled =3D false; + sd->bt_device_connected =3D false; + break; + case 0x03: + sd->bt_enabled =3D true; + sd->bt_device_connected =3D false; + break; + case 0x02: + sd->bt_enabled =3D true; + sd->bt_device_connected =3D true; + break; + } sd->mic_muted =3D (data[9] =3D=3D 0x01); break; case 0xb7: @@ -659,6 +678,15 @@ static void steelseries_arctis_nova_7_gen2_parse_statu= s(struct steelseries_devic case 0x52: sd->mic_muted =3D (data[2] =3D=3D 0x01); break; + case 0xb5: + if (data[1] =3D=3D 0x01) { + sd->bt_enabled =3D false; + sd->bt_device_connected =3D false; + } else if (data[1] =3D=3D 0x04) { + sd->bt_enabled =3D true; + sd->bt_device_connected =3D (data[2] =3D=3D 0x01); + } + break; } } =20 @@ -673,6 +701,8 @@ static void steelseries_arctis_nova_pro_parse_status(st= ruct steelseries_device * sd->battery_capacity =3D steelseries_map_capacity(data[6], 0x00, 0x08); sd->battery_charging =3D (data[15] =3D=3D 0x02); sd->mic_muted =3D (data[9] =3D=3D 0x01); + sd->bt_enabled =3D (data[4] =3D=3D 0x00); + sd->bt_device_connected =3D (data[5] =3D=3D 0x01); } else if (data[0] =3D=3D 0x07 && data[1] =3D=3D 0x45) { sd->chatmix_game =3D data[2]; sd->chatmix_chat =3D data[3]; @@ -760,14 +790,16 @@ static const struct steelseries_device_info arctis_no= va_7p_info =3D { static const struct steelseries_device_info arctis_nova_7_gen2_info =3D { .sync_interface =3D 3, .async_interface =3D 5, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_MIC_MUTE, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_MIC_MUTE | + SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_7_gen2_parse_status, }; =20 static const struct steelseries_device_info arctis_nova_pro_info =3D { .sync_interface =3D 4, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_MIC_MUTE, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_MIC_MUTE | + SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .request_status =3D steelseries_arctis_nova_pro_request_status, .parse_status =3D steelseries_arctis_nova_pro_parse_status, @@ -908,6 +940,70 @@ static int steelseries_battery_register(struct steelse= ries_device *sd) return 0; } =20 +/* + * Sysfs attributes for device state + */ + +static ssize_t bt_enabled_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hid_device *hdev =3D to_hid_device(dev); + struct steelseries_device *sd =3D hid_get_drvdata(hdev); + + if (!sd->headset_connected) + return -ENODEV; + + return sysfs_emit(buf, "%d\n", sd->bt_enabled); +} + +static ssize_t bt_device_connected_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hid_device *hdev =3D to_hid_device(dev); + struct steelseries_device *sd =3D hid_get_drvdata(hdev); + + if (!sd->headset_connected) + return -ENODEV; + + return sysfs_emit(buf, "%d\n", sd->bt_device_connected); +} + +static DEVICE_ATTR_RO(bt_enabled); +static DEVICE_ATTR_RO(bt_device_connected); + +static struct attribute *steelseries_headset_attrs[] =3D { + &dev_attr_bt_enabled.attr, + &dev_attr_bt_device_connected.attr, + NULL, +}; + +static umode_t steelseries_headset_attr_is_visible(struct kobject *kobj, + struct attribute *attr, + int index) +{ + struct device *dev =3D kobj_to_dev(kobj); + struct hid_device *hdev =3D to_hid_device(dev); + struct steelseries_device *sd =3D hid_get_drvdata(hdev); + unsigned long caps; + + if (!sd) + return 0; + + caps =3D sd->info->capabilities; + + if (attr =3D=3D &dev_attr_bt_enabled.attr) + return (caps & SS_CAP_BT_ENABLED) ? attr->mode : 0; + if (attr =3D=3D &dev_attr_bt_device_connected.attr) + return (caps & SS_CAP_BT_DEVICE_CONNECTED) ? attr->mode : 0; + + return 0; +} + +static const struct attribute_group steelseries_headset_attr_group =3D { + .attrs =3D steelseries_headset_attrs, + .is_visible =3D steelseries_headset_attr_is_visible, +}; + #if IS_BUILTIN(CONFIG_SND) || \ (IS_MODULE(CONFIG_SND) && IS_MODULE(CONFIG_HID_STEELSERIES)) =20 @@ -1218,6 +1314,13 @@ static int steelseries_probe(struct hid_device *hdev, hid_warn(hdev, "Failed to register battery: %d\n", ret); } =20 + if (info->capabilities & (SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED= )) { + ret =3D sysfs_create_group(&hdev->dev.kobj, + &steelseries_headset_attr_group); + if (ret) + hid_warn(hdev, "Failed to create sysfs group: %d\n", ret); + } + #if IS_BUILTIN(CONFIG_SND) || \ (IS_MODULE(CONFIG_SND) && IS_MODULE(CONFIG_HID_STEELSERIES)) ret =3D steelseries_snd_register(sd); @@ -1289,6 +1392,10 @@ static void steelseries_remove(struct hid_device *hd= ev) } =20 if (interface_num =3D=3D sd->info->sync_interface) { + if (sd->info->capabilities & (SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNE= CTED)) + sysfs_remove_group(&hdev->dev.kobj, + &steelseries_headset_attr_group); + if (sd->info->async_interface) { struct hid_device *sibling; =20 --=20 2.53.0 From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qt1-f173.google.com (mail-qt1-f173.google.com [209.85.160.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6F1063C196A for ; Fri, 27 Feb 2026 23:50:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236257; cv=none; b=DVOTN+dUxgfel/89boT8W197KMXdg6UbqsZq1DDFY92gU8b5mSJtxGbBoomW58WM4+UO5sOkCsFHiU3d7REv9PWUvyPucNYpurF+TaFazWAaLJNoOcDOQ6qpQj01O9LgxYpsdeQjaJR09DVSMKsKeA//ZlpCfntRviFk1YlF854= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236257; c=relaxed/simple; bh=A15BqvscuB/5RlxbCSq+CxiKXhvoqtQlKONYCcIV1X8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=qZDEsvSRdcieFNhVlmOZelU2uEpEO5yA0dgNW65TUbq7VphWEHR4CBRbGNnOiS/MELYNONWNZQmH/6DstYS841QJNATwXj0Bk1cURXT2KN3CKvQ7PMprK1GUZlk0zj0j/Mszmr4Kl4LbxUr7VfyyUmQn1ju7QIbHlJHbbTUJLb4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=YPW5TmSK; arc=none smtp.client-ip=209.85.160.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="YPW5TmSK" Received: by mail-qt1-f173.google.com with SMTP id d75a77b69052e-506bcb23a78so23336411cf.3 for ; Fri, 27 Feb 2026 15:50:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236254; x=1772841054; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=RRZhtJE4k765hzYv6/aXtPlhnTj2a1sOWdrMMQeHH0s=; b=YPW5TmSKg5iO14lpLa8bW3Ct4axoRWnH8WWePHeAaLhA1FI3GpP3pT8Mm4Q3q22r/u 90K+DSnkbSjEobK0vD7GdMzEETe7Klikzeo6IPUisJkSrsDzkJjo+DgB6ZswpfJMrzro 2n/3m9n+0xzIzNDBNBllHqnaGBkifZdIvZn4y+6Imt8hELHnBiSXIarQbNmyyCMdEMa+ tcVPKa0PlcIJ2QgSe5EGP8giJu4A9AaPA3Ypi9aIkggG0W1zxrmUDUfONGp8Vc6H+itk uIdF7k+iHY1YkTRq0NeRF/T90aNAUj0m0M1gyylRAIOiJxKfcRCYRkhTRyngpa8YZlTn Ry8A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236254; x=1772841054; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=RRZhtJE4k765hzYv6/aXtPlhnTj2a1sOWdrMMQeHH0s=; b=t0qUZOfq8B3yO8CREbomX/pYCD97BFNTdClegL+OcyoTQi0nfuiG0pVLRcgLa55PiT tYGXiMQ138gDR1hJxMG+6RQUe+s4hORsfQdVGU8Ml3JgawkAmfsqRRLYm3lNE454szyG q0E98kQZM4B1bw/dnMqf7+iZUONLoKmZJ5HTIpjpPsqlP6pioRRtaXnZUQF7CXi2T5Jt Vm0oLcp8H21eVyCTurPL97tVXu2tGWr9XDU6Ce3Cc/rr0TTm6qzfFr7NibZqFFxfMMa6 fpry9mSm8g0rfNTgO/o0/rcemL6WrFNndzt8Z4Lt+Cr3n6kFI/WyHEuXma0FX8herS9L SmTQ== X-Forwarded-Encrypted: i=1; AJvYcCW0NUH6cSBOV+mfXhmqBMGGM4qRy36gk3NnNQMnQcl1Yhm2Esp+gMDCwo4yak7vAi5FmCP+52YYF96dlDE=@vger.kernel.org X-Gm-Message-State: AOJu0Yzz1pIJUX3/jfcPPky74rh9VDaBalCXRXraSIyoTD3g8b77zVz5 FBRJJEPYC7AfIqh2vqp0xyxGO8OHraG1lunKhKvbOOIJAfuqKvfMH0ML X-Gm-Gg: ATEYQzzlMWD2N7piWzXEWqZIhPXWyBTnBHltmzXT2QsR/6PwaH+1KG2LTStwUeX1Xnq Gv8NQNNvy2RE8NUbEKW4GrlcFEd15zqkl/PKQMsPDs+M7kfZyqCFoYehvLreqSyDjHyZzX+toqZ JTbxuE9BCdAN1/5m7wCYQ96qekzUbceMoI7Haoq9q6kkW8RwSP6JvIxDNL/r/MKLiQdc1GMz+n4 5AzZwZpArnk+O+gQVNMzFHbxaGVQrzz2H72mGwJ9edcSyVexnzvzge67ZJtD0yhlqH1gpmVF3pR YHDuS85cDXnjLMEkRpjr+3Ww7Y6PwmK4pblQyrxYa3xqMdaKAq2Nct7UixyaY10jSJnBa7lNOo4 m0fYq27Za/RV4N9N1ZECBu0YWh0g/m+IMvmIzQPzgQLz5Hk82MT80mR7krk1fVnjoYg/+IfHcGO SjDYSbhqwgbLkwTZrh4sm91BaJFQXExSM+j6oJXSoHNUzbvnSEdQ== X-Received: by 2002:ac8:5f4e:0:b0:501:3e36:1513 with SMTP id d75a77b69052e-507526b9e5amr67620731cf.6.1772236254291; Fri, 27 Feb 2026 15:50:54 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:53 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 10/18] HID: steelseries: Add settings poll infrastructure Date: Fri, 27 Feb 2026 18:50:34 -0500 Message-ID: <20260227235042.410062-11-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Some headset settings (sidetone level, mic volume, etc.) are not reported spontaneously but must be explicitly requested from the device. Introduce a separate delayed work item (settings_work) for fetching these persistent settings, independent of the existing status work. Settings are requested once at probe time and again whenever the headset reconnects after being disconnected. Device info structs gain request_settings and parse_settings hooks for model-specific implementations. The SS_CAP_EXTERNAL_CONFIG capability flag marks devices whose writable controls can also be changed from the headset hardware directly; writable ALSA controls on such devices will be marked volatile. The initial implementation adds the Arctis Nova 7 Gen2 audio settings request (0x00, 0x20). Signed-off-by: Sriman Achanta --- drivers/hid/hid-steelseries.c | 37 ++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index 8c6116d02f19..f2423c350154 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -26,6 +26,7 @@ #define SS_CAP_MIC_MUTE BIT(2) #define SS_CAP_BT_ENABLED BIT(3) #define SS_CAP_BT_DEVICE_CONNECTED BIT(4) +#define SS_CAP_EXTERNAL_CONFIG BIT(5) =20 #define SS_QUIRK_STATUS_SYNC_POLL BIT(0) =20 @@ -40,6 +41,9 @@ struct steelseries_device_info { =20 int (*request_status)(struct hid_device *hdev); void (*parse_status)(struct steelseries_device *sd, u8 *data, int size); + + int (*request_settings)(struct hid_device *hdev); + void (*parse_settings)(struct steelseries_device *sd, u8 *data, int size); }; =20 struct steelseries_device { @@ -49,6 +53,7 @@ struct steelseries_device { bool use_async_protocol; =20 struct delayed_work status_work; + struct delayed_work settings_work; =20 struct power_supply_desc battery_desc; struct power_supply *battery; @@ -690,6 +695,14 @@ static void steelseries_arctis_nova_7_gen2_parse_statu= s(struct steelseries_devic } } =20 +static int steelseries_arctis_nova_7_gen2_request_settings(struct hid_devi= ce *hdev) +{ + const u8 data[] =3D { 0x00, 0x20 }; + + return steelseries_send_output_report(hdev, data, sizeof(data)); +} + + static void steelseries_arctis_nova_pro_parse_status(struct steelseries_de= vice *sd, u8 *data, int size) { @@ -791,9 +804,11 @@ static const struct steelseries_device_info arctis_nov= a_7_gen2_info =3D { .sync_interface =3D 3, .async_interface =3D 5, .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_MIC_MUTE | - SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED, + SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED | + SS_CAP_EXTERNAL_CONFIG, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_7_gen2_parse_status, + .request_settings =3D steelseries_arctis_nova_7_gen2_request_settings, }; =20 static const struct steelseries_device_info arctis_nova_pro_info =3D { @@ -901,6 +916,15 @@ static void steelseries_status_timer_work_handler(stru= ct work_struct *work) spin_unlock_irqrestore(&sd->lock, flags); } =20 +static void steelseries_settings_work_handler(struct work_struct *work) +{ + struct steelseries_device *sd =3D container_of( + work, struct steelseries_device, settings_work.work); + + if (sd->info->request_settings) + sd->info->request_settings(sd->hdev); +} + static int steelseries_battery_register(struct steelseries_device *sd) { static atomic_t battery_no =3D ATOMIC_INIT(0); @@ -1185,6 +1209,9 @@ static int steelseries_raw_event(struct hid_device *h= dev, =20 sd->info->parse_status(sd, data, size); =20 + if (sd->info->parse_settings) + sd->info->parse_settings(sd, data, size); + if (sd->headset_connected !=3D old_connected) { hid_dbg(hdev, "Connected status changed from %sconnected to %sconnected\n", @@ -1194,6 +1221,9 @@ static int steelseries_raw_event(struct hid_device *h= dev, if (sd->headset_connected && !old_connected && sd->use_async_protocol && is_async_interface) { schedule_delayed_work(&sd->status_work, 0); + if (sd->info->request_settings) + schedule_delayed_work(&sd->settings_work, + msecs_to_jiffies(10)); } =20 if (sd->battery) { @@ -1329,7 +1359,11 @@ static int steelseries_probe(struct hid_device *hdev, #endif =20 INIT_DELAYED_WORK(&sd->status_work, steelseries_status_timer_work_handle= r); + INIT_DELAYED_WORK(&sd->settings_work, steelseries_settings_work_handler); + schedule_delayed_work(&sd->status_work, msecs_to_jiffies(100)); + if (info->request_settings) + schedule_delayed_work(&sd->settings_work, msecs_to_jiffies(200)); =20 return 0; } @@ -1415,6 +1449,7 @@ static void steelseries_remove(struct hid_device *hde= v) spin_unlock_irqrestore(&sd->lock, flags); =20 cancel_delayed_work_sync(&sd->status_work); + cancel_delayed_work_sync(&sd->settings_work); } =20 hid_hw_close(hdev); --=20 2.53.0 From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qv1-f43.google.com (mail-qv1-f43.google.com [209.85.219.43]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0576C320CCC for ; Fri, 27 Feb 2026 23:50:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236259; cv=none; b=TJ/HNpl3JHhNUH79sp3kfBsFKccb82cYI6BlJij/KZAifSJC5WomS+LMOEVdBwqEIyktCk8VF0WZt83buXwM/8L+z+vvfo7C8qFmkTlsPTOuMdPlpE9WRYHmyitQ/StR9jrm5N3SbeNlq/Yt6LIl8C1u7d9d+ao50teKukDtMLg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236259; c=relaxed/simple; bh=CdJHxwVs43WkuxxBet5oaKrNOBp6EH40urjiOMU/P6M=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=HDi5SVTmy1iNMAYFn+8zMwmzY7SU0OP53ciwMjQbuE5w0tya7gplCS/Pcbj0eUy4UypSCZfg5APc3XwMW7y5Cr6OpCFXfAcOnoAMmycn7lvxOYmypFHlGFeCiqEVi8A7TpmswS/OJRlIU5G7L1oQ4lavuqYxdpYidVRCJUGIoK4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=cvjTC4Nl; arc=none smtp.client-ip=209.85.219.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="cvjTC4Nl" Received: by mail-qv1-f43.google.com with SMTP id 6a1803df08f44-896ff127650so40881336d6.3 for ; Fri, 27 Feb 2026 15:50:55 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236255; x=1772841055; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=uGOi621dnDpJ00aU6lEUxLiXvOJu51CQWm6bO2XxDMg=; b=cvjTC4NlTkTJvgCQKKa79A3HRKmDVq6VO0H2nstOQ1cYlcKLhHwmJY/NMtDk94yfDk Bp/UEsqK016EBjUmrMwYnlHyUTHJKtCgB14RD2MisjhClHN/32CDkbIL2Oa93wgXXl5R ceafhPOM0MQFJ6e74+X/ysocf9LGk0Ju1nNs26pD4A60Obj1jYwWLopSdJgIA+V823A4 1PUaSp44fzuSWTE03jhNr7uRoml+2rmXPQuG7rmmcr76/cDE0Zk7b9YVOrPpzDDbbeIt BHgYJjUwYnOwFWaZ8v1884NToPzVpi39SbO/nvqzOoK00GLLuK/zwcUj3ShFu1EyguJp rNfw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236255; x=1772841055; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=uGOi621dnDpJ00aU6lEUxLiXvOJu51CQWm6bO2XxDMg=; b=fG8DEGja1r3kOnRYnV0MK6oBCxNssFeipcNHKUqkpWfwSL1d3HwzaOxau2vCW6wRRR ArzjB72YvA8BzKD8d6JAA6QKfdqxnzYLM267bIqwOpSZ02/bZ4Hmbuhl8YKx1vTXbmBC I3Y792obRviW9um+If5VvCjbY7PbPyuPiVeTKYLN9qhKVVx8DOz3dOHAlIDffodvZoI/ /XCa7ZoMRoM31vJU+VvUPWp75jUupsn5phE7FgP7pgiqSKL9sTl7i3G1bDqSQzd8z5mu kyaPpPXuZJmGgZ+VpHj4lMeMDO5CDynhHfwgQZKl2ecJ7qJ+TnK9bl/sipjkwgzHHurJ HaIg== X-Forwarded-Encrypted: i=1; AJvYcCV3UDnThwk1UcEov9SrH6Gk7DFwY2iPoPdCjWpxLp94o8XE6Ol9g6cC9Oq4UuXS6sHzno7s75b2uHYqAao=@vger.kernel.org X-Gm-Message-State: AOJu0YwsRvOiKPTfMmDyZah64zRKKJX1rXvOO6ukA88qbmtkwXglywKS gLf6z2Sz0FrR8v8JW9FDCp2sIaQ7jKBJ459oFAztWXi6+ukYsNrp/DyM X-Gm-Gg: ATEYQzw9uaTn4nCwsyt3+zlD8lawpT0dMpv9K9GDXKHYDj/xkCZnoNTzQS6eHQpQSqS G1WNHEgaKHyxwJxlGrSs7/MokIwk1gjVYEiDMxbPTlqvb6VrU6Nvy8+ShTzfVU/6+UAK4mBfPVx x9fKWHSMovqWBA4WlSlyC7hRqg/H2LEjf7XN/Bma4XyzSYySO7Q14aRQSnjvae9noTz6eAueELX anBWtA2TXT6Aw3t12XhUHsv420gXBgucMU0JuWabqzsO1MvpLdnrKvSjVpg+SF5KMXtBxAC2Qpv BjHWqR6m6eQe/dwqJjeTYMnLgccy0TrNJdAbL9LRtPmAHHKjH5xbp57JLdzfFU81/9i3n/GctJ5 u+ZNz2YSTSEPdKJu17glENayEPDmlRwb5qFixb9qrvgRpTA1GegTQBqX+oLy/8HirMl32lzIfTx ppdPB5IaV1dpN5CWGLDh+fW4y3I1bUeKW6Lm57hupBIzrjsI6rIw== X-Received: by 2002:a05:6214:c63:b0:890:2480:f02e with SMTP id 6a1803df08f44-899d1dbcd78mr70340336d6.28.1772236255026; Fri, 27 Feb 2026 15:50:55 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:54 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 11/18] HID: steelseries: Add sidetone ALSA mixer control Date: Fri, 27 Feb 2026 18:50:35 -0500 Message-ID: <20260227235042.410062-12-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Expose sidetone level as a writable ALSA integer mixer control ("Sidetone Volume"). The valid range is device-specific and stored in the sidetone_max field of the device info struct. The write protocol differs per family: - Arctis 1/7: HID feature report with a separate two-byte command when disabling (value =3D=3D 0) versus a five-byte enable command otherwise - Arctis 9: single feature report with the value offset by 0xc0 - Nova 3P: output report followed by a save-to-flash command (0x09) - Nova 5/7/Pro: output report followed by model-specific save commands For devices with SS_CAP_EXTERNAL_CONFIG, the control is marked volatile as the headset can modify the value independently and the current level is recovered via the settings poll. Signed-off-by: Sriman Achanta --- drivers/hid/hid-steelseries.c | 270 ++++++++++++++++++++++++++++++++-- 1 file changed, 260 insertions(+), 10 deletions(-) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index f2423c350154..2bdf772432d0 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -27,9 +27,12 @@ #define SS_CAP_BT_ENABLED BIT(3) #define SS_CAP_BT_DEVICE_CONNECTED BIT(4) #define SS_CAP_EXTERNAL_CONFIG BIT(5) +#define SS_CAP_SIDETONE BIT(6) =20 #define SS_QUIRK_STATUS_SYNC_POLL BIT(0) =20 +#define SS_SETTING_SIDETONE 0 + struct steelseries_device; =20 struct steelseries_device_info { @@ -39,11 +42,14 @@ struct steelseries_device_info { u8 sync_interface; u8 async_interface; =20 + u8 sidetone_max; + int (*request_status)(struct hid_device *hdev); void (*parse_status)(struct steelseries_device *sd, u8 *data, int size); =20 int (*request_settings)(struct hid_device *hdev); void (*parse_settings)(struct steelseries_device *sd, u8 *data, int size); + int (*write_setting)(struct hid_device *hdev, u8 setting, u8 value); }; =20 struct steelseries_device { @@ -65,9 +71,11 @@ struct steelseries_device { struct snd_ctl_elem_id chatmix_chat_id; struct snd_ctl_elem_id chatmix_game_id; struct snd_ctl_elem_id mic_muted_id; + struct snd_ctl_elem_id sidetone_id; u8 chatmix_chat; u8 chatmix_game; bool mic_muted; + u8 sidetone; =20 bool bt_enabled; bool bt_device_connected; @@ -434,6 +442,127 @@ static inline int steelseries_send_output_report(stru= ct hid_device *hdev, return steelseries_send_report(hdev, data, len, HID_OUTPUT_REPORT); } =20 +/* + * Headset settings write functions + */ + +static int steelseries_arctis_1_write_setting(struct hid_device *hdev, + u8 setting, u8 value) +{ + switch (setting) { + case SS_SETTING_SIDETONE: + if (value =3D=3D 0) { + const u8 data[] =3D { 0x06, 0x35 }; + + return steelseries_send_feature_report(hdev, data, + sizeof(data)); + } else { + const u8 data[] =3D { 0x06, 0x35, 0x01, 0x00, value }; + + return steelseries_send_feature_report(hdev, data, + sizeof(data)); + } + default: + return -EINVAL; + } +} + +static int steelseries_arctis_9_write_setting(struct hid_device *hdev, + u8 setting, u8 value) +{ + switch (setting) { + case SS_SETTING_SIDETONE: { + const u8 data[] =3D { 0x06, 0x00, value + 0xc0 }; + + return steelseries_send_feature_report(hdev, data, sizeof(data)); + } + default: + return -EINVAL; + } +} + +static int steelseries_arctis_nova_3p_write_setting(struct hid_device *hde= v, + u8 setting, u8 value) +{ + const u8 save[] =3D { 0x09 }; + u8 cmd; + int ret; + u8 data[2]; + + switch (setting) { + case SS_SETTING_SIDETONE: + cmd =3D 0x39; + break; + default: + return -EINVAL; + } + + data[0] =3D cmd; + data[1] =3D value; + + ret =3D steelseries_send_feature_report(hdev, data, sizeof(data)); + if (ret) + return ret; + + return steelseries_send_feature_report(hdev, save, sizeof(save)); +} + +static int steelseries_arctis_nova_5_write_setting(struct hid_device *hdev, + u8 setting, u8 value) +{ + const u8 save[] =3D { 0x00, 0x09 }; + u8 cmd; + int ret; + u8 data[3]; + + switch (setting) { + case SS_SETTING_SIDETONE: + cmd =3D 0x39; + break; + default: + return -EINVAL; + } + + data[0] =3D 0x00; + data[1] =3D cmd; + data[2] =3D value; + + ret =3D steelseries_send_output_report(hdev, data, sizeof(data)); + if (ret) + return ret; + + msleep(10); + + return steelseries_send_output_report(hdev, save, sizeof(save)); +} + +static int steelseries_arctis_nova_pro_write_setting(struct hid_device *hd= ev, + u8 setting, u8 value) +{ + const u8 save[] =3D { 0x06, 0x09 }; + u8 cmd; + int ret; + u8 data[3]; + + switch (setting) { + case SS_SETTING_SIDETONE: + cmd =3D 0x39; + break; + default: + return -EINVAL; + } + + data[0] =3D 0x06; + data[1] =3D cmd; + data[2] =3D value; + + ret =3D steelseries_send_output_report(hdev, data, sizeof(data)); + if (ret) + return ret; + + return steelseries_send_output_report(hdev, save, sizeof(save)); +} + /* * Headset status request functions */ @@ -702,6 +831,21 @@ static int steelseries_arctis_nova_7_gen2_request_sett= ings(struct hid_device *hd return steelseries_send_output_report(hdev, data, sizeof(data)); } =20 +static void steelseries_arctis_nova_7_gen2_parse_settings( + struct steelseries_device *sd, u8 *data, int size) +{ + if (size < 3) + return; + + switch (data[0]) { + case 0x20: + sd->sidetone =3D data[2]; + break; + case 0x39: + sd->sidetone =3D data[1]; + break; + } +} =20 static void steelseries_arctis_nova_pro_parse_status(struct steelseries_de= vice *sd, u8 *data, int size) @@ -730,66 +874,82 @@ static const struct steelseries_device_info srws1_inf= o =3D { }; =20 static const struct steelseries_device_info arctis_1_info =3D { .sync_interface =3D 3, - .capabilities =3D SS_CAP_BATTERY, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_SIDETONE, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .sidetone_max =3D 18, .request_status =3D steelseries_arctis_1_request_status, .parse_status =3D steelseries_arctis_1_parse_status, + .write_setting =3D steelseries_arctis_1_write_setting, }; =20 static const struct steelseries_device_info arctis_7_info =3D { .sync_interface =3D 5, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .sidetone_max =3D 18, .request_status =3D steelseries_arctis_7_request_status, .parse_status =3D steelseries_arctis_7_parse_status, + .write_setting =3D steelseries_arctis_1_write_setting, }; =20 static const struct steelseries_device_info arctis_7_plus_info =3D { .sync_interface =3D 3, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .sidetone_max =3D 3, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_7_plus_parse_status, + .write_setting =3D steelseries_arctis_nova_5_write_setting, }; =20 static const struct steelseries_device_info arctis_9_info =3D { .sync_interface =3D 0, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .sidetone_max =3D 61, .request_status =3D steelseries_arctis_9_request_status, .parse_status =3D steelseries_arctis_9_parse_status, + .write_setting =3D steelseries_arctis_9_write_setting, }; =20 static const struct steelseries_device_info arctis_nova_3p_info =3D { .sync_interface =3D 4, - .capabilities =3D SS_CAP_BATTERY, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_SIDETONE, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .sidetone_max =3D 10, .request_status =3D steelseries_arctis_nova_3p_request_status, .parse_status =3D steelseries_arctis_nova_3p_parse_status, + .write_setting =3D steelseries_arctis_nova_3p_write_setting, }; =20 static const struct steelseries_device_info arctis_nova_5_info =3D { .sync_interface =3D 3, - .capabilities =3D SS_CAP_BATTERY, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_SIDETONE, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .sidetone_max =3D 10, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_5_parse_status, + .write_setting =3D steelseries_arctis_nova_5_write_setting, }; =20 static const struct steelseries_device_info arctis_nova_5x_info =3D { .sync_interface =3D 3, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .sidetone_max =3D 10, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_5x_parse_status, + .write_setting =3D steelseries_arctis_nova_5_write_setting, }; =20 static const struct steelseries_device_info arctis_nova_7_info =3D { .sync_interface =3D 3, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .sidetone_max =3D 3, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_7_parse_status, + .write_setting =3D steelseries_arctis_nova_5_write_setting, }; =20 static const struct steelseries_device_info arctis_nova_7p_info =3D { @@ -805,19 +965,25 @@ static const struct steelseries_device_info arctis_no= va_7_gen2_info =3D { .async_interface =3D 5, .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_MIC_MUTE | SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED | - SS_CAP_EXTERNAL_CONFIG, + SS_CAP_EXTERNAL_CONFIG | SS_CAP_SIDETONE, + .sidetone_max =3D 3, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_7_gen2_parse_status, .request_settings =3D steelseries_arctis_nova_7_gen2_request_settings, + .parse_settings =3D steelseries_arctis_nova_7_gen2_parse_settings, + .write_setting =3D steelseries_arctis_nova_5_write_setting, }; =20 static const struct steelseries_device_info arctis_nova_pro_info =3D { .sync_interface =3D 4, .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_MIC_MUTE | - SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED, + SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED | + SS_CAP_SIDETONE, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .sidetone_max =3D 3, .request_status =3D steelseries_arctis_nova_pro_request_status, .parse_status =3D steelseries_arctis_nova_pro_parse_status, + .write_setting =3D steelseries_arctis_nova_pro_write_setting, }; =20 /* @@ -1113,6 +1279,70 @@ static const struct snd_kcontrol_new steelseries_mic= _muted_control =3D { .get =3D steelseries_mic_muted_get, }; =20 +static int steelseries_sidetone_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct steelseries_device *sd =3D snd_kcontrol_chip(kcontrol); + + uinfo->type =3D SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count =3D 1; + uinfo->value.integer.min =3D 0; + uinfo->value.integer.max =3D sd->info->sidetone_max; + uinfo->value.integer.step =3D 1; + return 0; +} + +static int steelseries_sidetone_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct steelseries_device *sd =3D snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&sd->lock, flags); + ucontrol->value.integer.value[0] =3D sd->sidetone; + spin_unlock_irqrestore(&sd->lock, flags); + return 0; +} + +static int steelseries_sidetone_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct steelseries_device *sd =3D snd_kcontrol_chip(kcontrol); + unsigned long flags; + u8 new_sidetone; + int ret; + + new_sidetone =3D ucontrol->value.integer.value[0]; + if (new_sidetone > sd->info->sidetone_max) + return -EINVAL; + + spin_lock_irqsave(&sd->lock, flags); + if (sd->sidetone =3D=3D new_sidetone) { + spin_unlock_irqrestore(&sd->lock, flags); + return 0; + } + spin_unlock_irqrestore(&sd->lock, flags); + + ret =3D sd->info->write_setting(sd->hdev, SS_SETTING_SIDETONE, + new_sidetone); + if (ret) + return ret; + + spin_lock_irqsave(&sd->lock, flags); + sd->sidetone =3D new_sidetone; + spin_unlock_irqrestore(&sd->lock, flags); + + return 1; +} + +static const struct snd_kcontrol_new steelseries_sidetone_control =3D { + .iface =3D SNDRV_CTL_ELEM_IFACE_MIXER, + .name =3D "Sidetone Volume", + .info =3D steelseries_sidetone_info, + .get =3D steelseries_sidetone_get, + .put =3D steelseries_sidetone_put, +}; + static int steelseries_snd_register(struct steelseries_device *sd) { struct hid_device *hdev =3D sd->hdev; @@ -1155,6 +1385,21 @@ static int steelseries_snd_register(struct steelseri= es_device *sd) sd->mic_muted_id =3D kctl->id; } =20 + if (sd->info->capabilities & SS_CAP_SIDETONE) { + struct snd_kcontrol *kctl; + struct snd_kcontrol_new sidetone_ctl =3D steelseries_sidetone_control; + + sidetone_ctl.access =3D SNDRV_CTL_ELEM_ACCESS_READWRITE; + if (sd->info->capabilities & SS_CAP_EXTERNAL_CONFIG) + sidetone_ctl.access |=3D SNDRV_CTL_ELEM_ACCESS_VOLATILE; + + kctl =3D snd_ctl_new1(&sidetone_ctl, sd); + ret =3D snd_ctl_add(sd->card, kctl); + if (ret < 0) + goto err_free_card; + sd->sidetone_id =3D kctl->id; + } + ret =3D snd_card_register(sd->card); if (ret < 0) goto err_free_card; @@ -1185,6 +1430,7 @@ static int steelseries_raw_event(struct hid_device *h= dev, u8 old_chatmix_chat; u8 old_chatmix_game; bool old_mic_muted; + u8 old_sidetone; bool is_async_interface =3D false; =20 if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_SRWS1) @@ -1199,6 +1445,7 @@ static int steelseries_raw_event(struct hid_device *h= dev, old_chatmix_chat =3D sd->chatmix_chat; old_chatmix_game =3D sd->chatmix_game; old_mic_muted =3D sd->mic_muted; + old_sidetone =3D sd->sidetone; =20 if (hid_is_usb(hdev)) { struct usb_interface *intf =3D to_usb_interface(hdev->dev.parent); @@ -1259,6 +1506,9 @@ static int steelseries_raw_event(struct hid_device *h= dev, if (sd->mic_muted !=3D old_mic_muted) snd_ctl_notify(sd->card, SNDRV_CTL_EVENT_MASK_VALUE, &sd->mic_muted_id); + if (sd->sidetone !=3D old_sidetone) + snd_ctl_notify(sd->card, SNDRV_CTL_EVENT_MASK_VALUE, + &sd->sidetone_id); } =20 return 0; --=20 2.53.0 From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qv1-f54.google.com (mail-qv1-f54.google.com [209.85.219.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E51353EFD12 for ; Fri, 27 Feb 2026 23:50:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236259; cv=none; b=jFVIltSxqvghM+NwoDtu0H4XIT/WpM7zya11EB+hdCtel862Q0id09NdotOR7BFxIm+4gDVaNbojVg825+AF/MFOUzEcLv/PiP+7SkbkkCzBtjYEY+hik3ZDXrTDJrFjn0k4FJGKuJurd5jwUVgxypzh7AxxkmMMBVTMrSTrVpE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236259; c=relaxed/simple; bh=LINZzjkNXcR//uhObtuT6l57p2ZuCZQdmQ+ds+iTdHg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=lfRAAHIL6I72qO6DIBVFrqpNFtRkST60enLwzcJdxhiQ/VgVdkU0fIJe6dwI8CniCkFxgo3+4H/w28mrGYcICb0uniPeiOcFxD65pt88R0hs3iGRX0MCM4gw+RhjqVRQj8w+IBHOe8V55rJU09c1T2vhE+MzrQnznhWuZnPreF4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=aCsNpK0P; arc=none smtp.client-ip=209.85.219.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="aCsNpK0P" Received: by mail-qv1-f54.google.com with SMTP id 6a1803df08f44-896f5af3d8aso39698196d6.1 for ; Fri, 27 Feb 2026 15:50:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236256; x=1772841056; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=wVqVKskRD6Dk7+Y8KsoAvkjKrzzikKhtPqfB8TrOqao=; b=aCsNpK0Ps6ZFaGUiG12eN5YiPLSg6R/ulfQ3bnNs3JFmu4uV/bvcDjQ6/HUJAZdGt8 AZx0RVAVxGfmh1POdmXciq1Q5Rn1dyKLHzmRy0pp5ph2IltKyE3FSie5q+ccvUpbR9Oj H8GHQJ+7D+w37WbC874s4OSQNrphLihX9yfBsv/fK2HntD3l6n+5e5PwICsTeTQL0H/C I3u1ET+U1F+i9xdMGlSphWXGZlmCzUeW+znBkMFa496lJd28PVJ4QOHaSwbGM31wRSsf I93qmHrP5QuwmPTXsxPy/BI6PPFOK1ibxZ/aQTf00Fy1aBMYcT0Kd6Bs9onzfzaGpXfQ ssiA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236256; x=1772841056; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=wVqVKskRD6Dk7+Y8KsoAvkjKrzzikKhtPqfB8TrOqao=; b=WG8rjFDwAStuBiTbesnnNfgcDppA0wFXviQEa+6pGucM8e4j0ewg69Orldt1FT2ctF 9pR5IFze46wW+gBKwmd+NPxoKrcbFxKrcrFpaDQeDrxoF+H3kdtjFXugbr4H4rMI2yiq o7WUOO9FL+sIh2fmAQ8AxpYZ0kuQON5q37gCh0OHAua6bfdThSODflDCfTY+l2VcX1tX aujG5sjtde3Zebnx3b0pc0bSuRPw5oBPUTP+2vOJBi9+pcIvTh+IyswQWLaTjirBez/O tynQpeen6GLb+ZRh5mM3uWYvIUD/GRVA+CEp+k5QwHE0ezYuFef/9/OTAzeJbfep/nxc jAWQ== X-Forwarded-Encrypted: i=1; AJvYcCWx6CVf2LASrg/zJl0Lh9Xr13A+hEVxHuFTWdWbdXAeUpnT8QdGnwEOZIJW/N5N+V3V761zCdw91ex9b/4=@vger.kernel.org X-Gm-Message-State: AOJu0YytkeFxgr5XqBQE5zjTbUZQ9qrfIznonK9OMZFXAyZ69c5JRNMd eqE+AB4UFnw9+d7ZH5DeSAUbJMr9dAmNciTMC61FYwGHoyw9l2ijBUiL X-Gm-Gg: ATEYQzwtZQbTn+8W8ErOF24rx5+UmBr2SlY+Q3PsMblJMPnxX235x3Q0AWsHeBzRdwh rwvNihEiRvfYyNHz8/guJXdcDCUnPNcPSrkACXTjq2fzr6XHgV7H2vywON8RU9zkCKXAjg8WljS 69SjgQClU0HN9QDJcZ1USLd15MoMB5StjrpZLmfSvEavMsNOEmCnDp3jqEOnqpGJSPbVu9FcSjz K4gUEk5VQ+XaOW4gwKf2GFmCa4OfYroZ1e+tX6LPvYLuHSIiv3u0ludSM2da1M6ttmYR6fhpxkr ak/VxU5UI1lSAve0xE4jA6SfYUKnzccGtHfyAwSdbuJux3gr51es0W1KToesshwuTAc5PkDNTS2 TztIwrHlACqYSZ2uhBJFEhvZ0QAvknUGTd7N0szWBs8AVZW1NZ96vNdrD2vbky1uxt7AQaCp7Yy 0+ynCySJ9D6EUpLKCGNRCagNCFOtPWnZYEDfQiAOTmLW3d7Bhpe3pu+03IaBjv X-Received: by 2002:ad4:5ce7:0:b0:899:c555:9e65 with SMTP id 6a1803df08f44-899d1dc0635mr69980056d6.30.1772236255823; Fri, 27 Feb 2026 15:50:55 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:55 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 12/18] HID: steelseries: Add mic volume ALSA mixer control Date: Fri, 27 Feb 2026 18:50:36 -0500 Message-ID: <20260227235042.410062-13-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Expose microphone gain as a writable ALSA integer mixer control ("Mic Volume"). The valid range is per-device; mic_volume_min and mic_volume_max are added to the device info struct to accommodate models with a non-zero minimum (e.g. the Arctis Nova Pro, which has a range of 1-10). The write command (0x37) is added to the Nova 3P, Nova 5, and Nova Pro write handlers. On the Nova Pro, the current mic volume is recovered from the 0x06/0xb0 settings response via a new parse_settings hook. Signed-off-by: Sriman Achanta --- drivers/hid/hid-steelseries.c | 141 ++++++++++++++++++++++++++++++++-- 1 file changed, 134 insertions(+), 7 deletions(-) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index 2bdf772432d0..1339f965f67f 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -28,10 +28,12 @@ #define SS_CAP_BT_DEVICE_CONNECTED BIT(4) #define SS_CAP_EXTERNAL_CONFIG BIT(5) #define SS_CAP_SIDETONE BIT(6) +#define SS_CAP_MIC_VOLUME BIT(7) =20 #define SS_QUIRK_STATUS_SYNC_POLL BIT(0) =20 #define SS_SETTING_SIDETONE 0 +#define SS_SETTING_MIC_VOLUME 1 =20 struct steelseries_device; =20 @@ -43,6 +45,8 @@ struct steelseries_device_info { u8 async_interface; =20 u8 sidetone_max; + u8 mic_volume_min; + u8 mic_volume_max; =20 int (*request_status)(struct hid_device *hdev); void (*parse_status)(struct steelseries_device *sd, u8 *data, int size); @@ -72,10 +76,12 @@ struct steelseries_device { struct snd_ctl_elem_id chatmix_game_id; struct snd_ctl_elem_id mic_muted_id; struct snd_ctl_elem_id sidetone_id; + struct snd_ctl_elem_id mic_volume_id; u8 chatmix_chat; u8 chatmix_game; bool mic_muted; u8 sidetone; + u8 mic_volume; =20 bool bt_enabled; bool bt_device_connected; @@ -493,6 +499,9 @@ static int steelseries_arctis_nova_3p_write_setting(str= uct hid_device *hdev, case SS_SETTING_SIDETONE: cmd =3D 0x39; break; + case SS_SETTING_MIC_VOLUME: + cmd =3D 0x37; + break; default: return -EINVAL; } @@ -519,6 +528,9 @@ static int steelseries_arctis_nova_5_write_setting(stru= ct hid_device *hdev, case SS_SETTING_SIDETONE: cmd =3D 0x39; break; + case SS_SETTING_MIC_VOLUME: + cmd =3D 0x37; + break; default: return -EINVAL; } @@ -548,6 +560,9 @@ static int steelseries_arctis_nova_pro_write_setting(st= ruct hid_device *hdev, case SS_SETTING_SIDETONE: cmd =3D 0x39; break; + case SS_SETTING_MIC_VOLUME: + cmd =3D 0x37; + break; default: return -EINVAL; } @@ -839,14 +854,28 @@ static void steelseries_arctis_nova_7_gen2_parse_sett= ings( =20 switch (data[0]) { case 0x20: + sd->mic_volume =3D data[1]; sd->sidetone =3D data[2]; break; + case 0x37: + sd->mic_volume =3D data[1]; + break; case 0x39: sd->sidetone =3D data[1]; break; } } =20 +static void steelseries_arctis_nova_pro_parse_settings( + struct steelseries_device *sd, u8 *data, int size) +{ + if (size < 10) + return; + + if (data[0] =3D=3D 0x06 && data[1] =3D=3D 0xb0) + sd->mic_volume =3D data[9]; +} + static void steelseries_arctis_nova_pro_parse_status(struct steelseries_de= vice *sd, u8 *data, int size) { @@ -914,9 +943,10 @@ static const struct steelseries_device_info arctis_9_i= nfo =3D { =20 static const struct steelseries_device_info arctis_nova_3p_info =3D { .sync_interface =3D 4, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_SIDETONE, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 10, + .mic_volume_max =3D 14, .request_status =3D steelseries_arctis_nova_3p_request_status, .parse_status =3D steelseries_arctis_nova_3p_parse_status, .write_setting =3D steelseries_arctis_nova_3p_write_setting, @@ -924,9 +954,10 @@ static const struct steelseries_device_info arctis_nov= a_3p_info =3D { =20 static const struct steelseries_device_info arctis_nova_5_info =3D { .sync_interface =3D 3, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_SIDETONE, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 10, + .mic_volume_max =3D 15, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_5_parse_status, .write_setting =3D steelseries_arctis_nova_5_write_setting, @@ -934,9 +965,11 @@ static const struct steelseries_device_info arctis_nov= a_5_info =3D { =20 static const struct steelseries_device_info arctis_nova_5x_info =3D { .sync_interface =3D 3, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE | + SS_CAP_MIC_VOLUME, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 10, + .mic_volume_max =3D 15, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_5x_parse_status, .write_setting =3D steelseries_arctis_nova_5_write_setting, @@ -944,9 +977,11 @@ static const struct steelseries_device_info arctis_nov= a_5x_info =3D { =20 static const struct steelseries_device_info arctis_nova_7_info =3D { .sync_interface =3D 3, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE | + SS_CAP_MIC_VOLUME, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 3, + .mic_volume_max =3D 7, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_7_parse_status, .write_setting =3D steelseries_arctis_nova_5_write_setting, @@ -954,10 +989,12 @@ static const struct steelseries_device_info arctis_no= va_7_info =3D { =20 static const struct steelseries_device_info arctis_nova_7p_info =3D { .sync_interface =3D 3, - .capabilities =3D SS_CAP_BATTERY, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_MIC_VOLUME, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, + .mic_volume_max =3D 7, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_7_parse_status, + .write_setting =3D steelseries_arctis_nova_5_write_setting, }; =20 static const struct steelseries_device_info arctis_nova_7_gen2_info =3D { @@ -965,8 +1002,10 @@ static const struct steelseries_device_info arctis_no= va_7_gen2_info =3D { .async_interface =3D 5, .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_MIC_MUTE | SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED | - SS_CAP_EXTERNAL_CONFIG | SS_CAP_SIDETONE, + SS_CAP_EXTERNAL_CONFIG | SS_CAP_SIDETONE | + SS_CAP_MIC_VOLUME, .sidetone_max =3D 3, + .mic_volume_max =3D 7, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_7_gen2_parse_status, .request_settings =3D steelseries_arctis_nova_7_gen2_request_settings, @@ -978,11 +1017,14 @@ static const struct steelseries_device_info arctis_n= ova_pro_info =3D { .sync_interface =3D 4, .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_MIC_MUTE | SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED | - SS_CAP_SIDETONE, + SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 3, + .mic_volume_min =3D 1, + .mic_volume_max =3D 10, .request_status =3D steelseries_arctis_nova_pro_request_status, .parse_status =3D steelseries_arctis_nova_pro_parse_status, + .parse_settings =3D steelseries_arctis_nova_pro_parse_settings, .write_setting =3D steelseries_arctis_nova_pro_write_setting, }; =20 @@ -1343,6 +1385,71 @@ static const struct snd_kcontrol_new steelseries_sid= etone_control =3D { .put =3D steelseries_sidetone_put, }; =20 +static int steelseries_mic_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct steelseries_device *sd =3D snd_kcontrol_chip(kcontrol); + + uinfo->type =3D SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count =3D 1; + uinfo->value.integer.min =3D sd->info->mic_volume_min; + uinfo->value.integer.max =3D sd->info->mic_volume_max; + uinfo->value.integer.step =3D 1; + return 0; +} + +static int steelseries_mic_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct steelseries_device *sd =3D snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&sd->lock, flags); + ucontrol->value.integer.value[0] =3D sd->mic_volume; + spin_unlock_irqrestore(&sd->lock, flags); + return 0; +} + +static int steelseries_mic_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct steelseries_device *sd =3D snd_kcontrol_chip(kcontrol); + unsigned long flags; + u8 new_mic_volume; + int ret; + + new_mic_volume =3D ucontrol->value.integer.value[0]; + if (new_mic_volume < sd->info->mic_volume_min || + new_mic_volume > sd->info->mic_volume_max) + return -EINVAL; + + spin_lock_irqsave(&sd->lock, flags); + if (sd->mic_volume =3D=3D new_mic_volume) { + spin_unlock_irqrestore(&sd->lock, flags); + return 0; + } + spin_unlock_irqrestore(&sd->lock, flags); + + ret =3D sd->info->write_setting(sd->hdev, SS_SETTING_MIC_VOLUME, + new_mic_volume); + if (ret) + return ret; + + spin_lock_irqsave(&sd->lock, flags); + sd->mic_volume =3D new_mic_volume; + spin_unlock_irqrestore(&sd->lock, flags); + + return 1; +} + +static const struct snd_kcontrol_new steelseries_mic_volume_control =3D { + .iface =3D SNDRV_CTL_ELEM_IFACE_MIXER, + .name =3D "Mic Volume", + .info =3D steelseries_mic_volume_info, + .get =3D steelseries_mic_volume_get, + .put =3D steelseries_mic_volume_put, +}; + static int steelseries_snd_register(struct steelseries_device *sd) { struct hid_device *hdev =3D sd->hdev; @@ -1400,6 +1507,21 @@ static int steelseries_snd_register(struct steelseri= es_device *sd) sd->sidetone_id =3D kctl->id; } =20 + if (sd->info->capabilities & SS_CAP_MIC_VOLUME) { + struct snd_kcontrol *kctl; + struct snd_kcontrol_new mic_vol_ctl =3D steelseries_mic_volume_control; + + mic_vol_ctl.access =3D SNDRV_CTL_ELEM_ACCESS_READWRITE; + if (sd->info->capabilities & SS_CAP_EXTERNAL_CONFIG) + mic_vol_ctl.access |=3D SNDRV_CTL_ELEM_ACCESS_VOLATILE; + + kctl =3D snd_ctl_new1(&mic_vol_ctl, sd); + ret =3D snd_ctl_add(sd->card, kctl); + if (ret < 0) + goto err_free_card; + sd->mic_volume_id =3D kctl->id; + } + ret =3D snd_card_register(sd->card); if (ret < 0) goto err_free_card; @@ -1431,6 +1553,7 @@ static int steelseries_raw_event(struct hid_device *h= dev, u8 old_chatmix_game; bool old_mic_muted; u8 old_sidetone; + u8 old_mic_volume; bool is_async_interface =3D false; =20 if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_SRWS1) @@ -1446,6 +1569,7 @@ static int steelseries_raw_event(struct hid_device *h= dev, old_chatmix_game =3D sd->chatmix_game; old_mic_muted =3D sd->mic_muted; old_sidetone =3D sd->sidetone; + old_mic_volume =3D sd->mic_volume; =20 if (hid_is_usb(hdev)) { struct usb_interface *intf =3D to_usb_interface(hdev->dev.parent); @@ -1509,6 +1633,9 @@ static int steelseries_raw_event(struct hid_device *h= dev, if (sd->sidetone !=3D old_sidetone) snd_ctl_notify(sd->card, SNDRV_CTL_EVENT_MASK_VALUE, &sd->sidetone_id); + if (sd->mic_volume !=3D old_mic_volume) + snd_ctl_notify(sd->card, SNDRV_CTL_EVENT_MASK_VALUE, + &sd->mic_volume_id); } =20 return 0; --=20 2.53.0 From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qt1-f177.google.com (mail-qt1-f177.google.com [209.85.160.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 948693F23A6 for ; Fri, 27 Feb 2026 23:50:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236261; cv=none; b=Oocjwq6/ZahN5f6yij1IxVOeyfTy70mAnOSRdfluKgHMXgb0YcEumUYv7dsl1F944kwk/nIIXEkyDTx7sRYeGEz9GTjKzKRD6xjOfHdQyWzpieQL4wT/wf4xbUizeBtSDak666r7a9XPTzNaIlnL8QhOa8mShuBhlquH+bWzufY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236261; c=relaxed/simple; bh=tIgu/W1Vw1MtIn1d2yUvm4K3lF1alZK5lWzx0kjY2Dk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=dtKmdlf6H9gF8ZWeq4ccvsAfC242SJcxxVlsh9WLOIECPPBLR8RoYGBwFOadqHlPYhv92atFYC1xcGWIWoAoaa9ALl4MzVtSWfKLKWBndpUXOPaJfByfivayDOKtj0jynPrdSH/mCnkjZn61MX+9DNfEG+PzfmSAMABjVRbhekc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=hANaiHyz; arc=none smtp.client-ip=209.85.160.177 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hANaiHyz" Received: by mail-qt1-f177.google.com with SMTP id d75a77b69052e-5069b3e0c66so50542891cf.1 for ; Fri, 27 Feb 2026 15:50:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236256; x=1772841056; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=fiCPPOg7Ij3vGkIzTm4Dp+OQ39FoDVXoCpIrzjwwrl4=; b=hANaiHyzUESOsvDoiT7hLj7SB+qC243dg/p8AlHJaycirng/X/jwwe4M6HgeZy4dwH tdQ7FPKBUYn0OeYktPCnodJB/XjBh7mPSrjtrPwHoOeh0D0B+fPPXrAue7eHQgsoJBmt eJV1elv1DQ0JEX7me43bofWqbq4+Eo7QRDUjaGIKoBr4ogW241hZIcRM14fx4P6ooPAx ygSEpXVoVPFIpmTTQ31EnDqxkx3ArmelWDjYwvcmX4OhMJlBiY9M6hvE5s8sKW4gqT+6 2CjQAV487TW4U3xgY6raKjn9kTli1Z37Yu6vocaJ6/vX6bmvI1LuyB0w25VcNmIpDXnC ximw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236256; x=1772841056; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=fiCPPOg7Ij3vGkIzTm4Dp+OQ39FoDVXoCpIrzjwwrl4=; b=YhyMcJ9qchkHjazssTA8151LAb397Vu8dSCNNoERQCLjnBJ60rOQvzaMHsg9wWU+Cb bmq9XwTIGHbku4qkjQZLf6DTMMlnWoLuLmNrFKTIVmvfUB2KfExwVCbG+UWcLvarRsCy bGff/aXYXOfx32t6Sb+A/6rnPz2TdUgGOYgQdVR99Cf/zMBcTU5MxWY4OLwgKP62wUrc LyPC7QaIacp4kXaoE8iogK4tsMN3QNqGvgk0TcqfULPuZtIyA0MyWA1/9CZKqAngGpvM LvlS/XbLW72XmIBoyh7yKaBn5P6WcAshvQtnJLJnj5P8LpRwemcKtFdE6oG8NoiIfUiy QoYg== X-Forwarded-Encrypted: i=1; AJvYcCVdV6OZ5W3zE/rW5dl2y3lZueQlNa8gEWSdlHSrQ5MLBZzL+PodmAebhWpGkcVEBR/MupS8Jz6uOU9tYKg=@vger.kernel.org X-Gm-Message-State: AOJu0YyLRPiiNNoKS0Wk7For13x59IKa0+SpAO5Y/X02OWmAusx33pMy 5RlCJ3dV3FrW0eqRhzi7oNC9kVywuuIJnd2OvAXorK9mWYJlmLpr2xax X-Gm-Gg: ATEYQzyGqiBQcxIvv9TqyEC8XSsneCNLVYW28jhd1e8fvER766bncbsT1rBnk/XONwf RZ8w8OiaSLurJbBhhIt42pzpo92dDKS7HpIcy0lT9q7tcOXuElY7mhTXOHeEl5S1VZXdcL7knyo 7doj6epbwYczMAkFQnaL2151lxMqIG85m4sHNzhucBgfN54P4+4g2CMDPaqu63vWnHsSiunwtUE wCiMxG8qD2vBmIl7g4kdwPmD/CE2zNjS7Qc6N5xJtE5OyCaDxlhrjwGZ995GROZ8MlfGKgnme8b PW9vRfm6EDZlbOj+9I+KvRU70BnKV/p4Gqkram+ZMTatdWQ/zcy986+zH8fP65DJrQStzeBM9ek 2zSHUW/3kIGYfvVbgr7T/5tCorHXKx/W06nbZUCj2tUz/IQO1SyX1AxhMDG7pMG3kPo7YiNt+bO KphV9/l4g161HumP+1gRiucsA4pYiNCf1daRLJlGshD2HsQ/OevQ== X-Received: by 2002:a05:622a:18a3:b0:505:e529:11e9 with SMTP id d75a77b69052e-507444404c7mr114667511cf.36.1772236256466; Fri, 27 Feb 2026 15:50:56 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:56 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 13/18] HID: steelseries: Add volume limiter ALSA mixer control Date: Fri, 27 Feb 2026 18:50:37 -0500 Message-ID: <20260227235042.410062-14-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Expose the maximum output volume cap as a writable ALSA boolean mixer control ("Volume Limiter"). The Nova 7 family uses command 0x3a for this setting whereas the Nova 5 family uses 0x27, so a dedicated steelseries_arctis_nova_7_write_setting() is introduced and the Nova 7, Nova 7P, and Nova 7 Gen2 entries are updated to use it instead of the Nova 5 handler. Signed-off-by: Sriman Achanta --- drivers/hid/hid-steelseries.c | 145 +++++++++++++++++++++++++++++++--- 1 file changed, 136 insertions(+), 9 deletions(-) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index 1339f965f67f..47ffec481571 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -29,11 +29,13 @@ #define SS_CAP_EXTERNAL_CONFIG BIT(5) #define SS_CAP_SIDETONE BIT(6) #define SS_CAP_MIC_VOLUME BIT(7) +#define SS_CAP_VOLUME_LIMITER BIT(8) =20 #define SS_QUIRK_STATUS_SYNC_POLL BIT(0) =20 #define SS_SETTING_SIDETONE 0 #define SS_SETTING_MIC_VOLUME 1 +#define SS_SETTING_VOLUME_LIMITER 2 =20 struct steelseries_device; =20 @@ -77,11 +79,13 @@ struct steelseries_device { struct snd_ctl_elem_id mic_muted_id; struct snd_ctl_elem_id sidetone_id; struct snd_ctl_elem_id mic_volume_id; + struct snd_ctl_elem_id volume_limiter_id; u8 chatmix_chat; u8 chatmix_game; bool mic_muted; u8 sidetone; u8 mic_volume; + bool volume_limiter; =20 bool bt_enabled; bool bt_device_connected; @@ -531,6 +535,44 @@ static int steelseries_arctis_nova_5_write_setting(str= uct hid_device *hdev, case SS_SETTING_MIC_VOLUME: cmd =3D 0x37; break; + case SS_SETTING_VOLUME_LIMITER: + cmd =3D 0x27; + break; + default: + return -EINVAL; + } + + data[0] =3D 0x00; + data[1] =3D cmd; + data[2] =3D value; + + ret =3D steelseries_send_output_report(hdev, data, sizeof(data)); + if (ret) + return ret; + + msleep(10); + + return steelseries_send_output_report(hdev, save, sizeof(save)); +} + +static int steelseries_arctis_nova_7_write_setting(struct hid_device *hdev, + u8 setting, u8 value) +{ + const u8 save[] =3D { 0x00, 0x09 }; + u8 cmd; + int ret; + u8 data[3]; + + switch (setting) { + case SS_SETTING_SIDETONE: + cmd =3D 0x39; + break; + case SS_SETTING_MIC_VOLUME: + cmd =3D 0x37; + break; + case SS_SETTING_VOLUME_LIMITER: + cmd =3D 0x3a; + break; default: return -EINVAL; } @@ -849,13 +891,14 @@ static int steelseries_arctis_nova_7_gen2_request_set= tings(struct hid_device *hd static void steelseries_arctis_nova_7_gen2_parse_settings( struct steelseries_device *sd, u8 *data, int size) { - if (size < 3) + if (size < 4) return; =20 switch (data[0]) { case 0x20: sd->mic_volume =3D data[1]; sd->sidetone =3D data[2]; + sd->volume_limiter =3D data[3]; break; case 0x37: sd->mic_volume =3D data[1]; @@ -863,6 +906,9 @@ static void steelseries_arctis_nova_7_gen2_parse_settin= gs( case 0x39: sd->sidetone =3D data[1]; break; + case 0x3a: + sd->volume_limiter =3D data[1]; + break; } } =20 @@ -954,7 +1000,8 @@ static const struct steelseries_device_info arctis_nov= a_3p_info =3D { =20 static const struct steelseries_device_info arctis_nova_5_info =3D { .sync_interface =3D 3, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME | + SS_CAP_VOLUME_LIMITER, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 10, .mic_volume_max =3D 15, @@ -966,7 +1013,7 @@ static const struct steelseries_device_info arctis_nov= a_5_info =3D { static const struct steelseries_device_info arctis_nova_5x_info =3D { .sync_interface =3D 3, .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE | - SS_CAP_MIC_VOLUME, + SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 10, .mic_volume_max =3D 15, @@ -978,23 +1025,23 @@ static const struct steelseries_device_info arctis_n= ova_5x_info =3D { static const struct steelseries_device_info arctis_nova_7_info =3D { .sync_interface =3D 3, .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE | - SS_CAP_MIC_VOLUME, + SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 3, .mic_volume_max =3D 7, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_7_parse_status, - .write_setting =3D steelseries_arctis_nova_5_write_setting, + .write_setting =3D steelseries_arctis_nova_7_write_setting, }; =20 static const struct steelseries_device_info arctis_nova_7p_info =3D { .sync_interface =3D 3, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_MIC_VOLUME, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMI= TER, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .mic_volume_max =3D 7, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_7_parse_status, - .write_setting =3D steelseries_arctis_nova_5_write_setting, + .write_setting =3D steelseries_arctis_nova_7_write_setting, }; =20 static const struct steelseries_device_info arctis_nova_7_gen2_info =3D { @@ -1003,14 +1050,14 @@ static const struct steelseries_device_info arctis_= nova_7_gen2_info =3D { .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_MIC_MUTE | SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED | SS_CAP_EXTERNAL_CONFIG | SS_CAP_SIDETONE | - SS_CAP_MIC_VOLUME, + SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER, .sidetone_max =3D 3, .mic_volume_max =3D 7, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_7_gen2_parse_status, .request_settings =3D steelseries_arctis_nova_7_gen2_request_settings, .parse_settings =3D steelseries_arctis_nova_7_gen2_parse_settings, - .write_setting =3D steelseries_arctis_nova_5_write_setting, + .write_setting =3D steelseries_arctis_nova_7_write_setting, }; =20 static const struct steelseries_device_info arctis_nova_pro_info =3D { @@ -1450,6 +1497,66 @@ static const struct snd_kcontrol_new steelseries_mic= _volume_control =3D { .put =3D steelseries_mic_volume_put, }; =20 +static int steelseries_volume_limiter_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type =3D SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count =3D 1; + uinfo->value.integer.min =3D 0; + uinfo->value.integer.max =3D 1; + uinfo->value.integer.step =3D 1; + return 0; +} + +static int steelseries_volume_limiter_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct steelseries_device *sd =3D snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&sd->lock, flags); + ucontrol->value.integer.value[0] =3D sd->volume_limiter; + spin_unlock_irqrestore(&sd->lock, flags); + return 0; +} + +static int steelseries_volume_limiter_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct steelseries_device *sd =3D snd_kcontrol_chip(kcontrol); + unsigned long flags; + bool new_volume_limiter; + int ret; + + new_volume_limiter =3D !!ucontrol->value.integer.value[0]; + + spin_lock_irqsave(&sd->lock, flags); + if (sd->volume_limiter =3D=3D new_volume_limiter) { + spin_unlock_irqrestore(&sd->lock, flags); + return 0; + } + spin_unlock_irqrestore(&sd->lock, flags); + + ret =3D sd->info->write_setting(sd->hdev, SS_SETTING_VOLUME_LIMITER, + new_volume_limiter ? 1 : 0); + if (ret) + return ret; + + spin_lock_irqsave(&sd->lock, flags); + sd->volume_limiter =3D new_volume_limiter; + spin_unlock_irqrestore(&sd->lock, flags); + + return 1; +} + +static const struct snd_kcontrol_new steelseries_volume_limiter_control = =3D { + .iface =3D SNDRV_CTL_ELEM_IFACE_MIXER, + .name =3D "Volume Limiter", + .info =3D steelseries_volume_limiter_info, + .get =3D steelseries_volume_limiter_get, + .put =3D steelseries_volume_limiter_put, +}; + static int steelseries_snd_register(struct steelseries_device *sd) { struct hid_device *hdev =3D sd->hdev; @@ -1522,6 +1629,21 @@ static int steelseries_snd_register(struct steelseri= es_device *sd) sd->mic_volume_id =3D kctl->id; } =20 + if (sd->info->capabilities & SS_CAP_VOLUME_LIMITER) { + struct snd_kcontrol *kctl; + struct snd_kcontrol_new vol_lim_ctl =3D steelseries_volume_limiter_contr= ol; + + vol_lim_ctl.access =3D SNDRV_CTL_ELEM_ACCESS_READWRITE; + if (sd->info->capabilities & SS_CAP_EXTERNAL_CONFIG) + vol_lim_ctl.access |=3D SNDRV_CTL_ELEM_ACCESS_VOLATILE; + + kctl =3D snd_ctl_new1(&vol_lim_ctl, sd); + ret =3D snd_ctl_add(sd->card, kctl); + if (ret < 0) + goto err_free_card; + sd->volume_limiter_id =3D kctl->id; + } + ret =3D snd_card_register(sd->card); if (ret < 0) goto err_free_card; @@ -1554,6 +1676,7 @@ static int steelseries_raw_event(struct hid_device *h= dev, bool old_mic_muted; u8 old_sidetone; u8 old_mic_volume; + bool old_volume_limiter; bool is_async_interface =3D false; =20 if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_SRWS1) @@ -1570,6 +1693,7 @@ static int steelseries_raw_event(struct hid_device *h= dev, old_mic_muted =3D sd->mic_muted; old_sidetone =3D sd->sidetone; old_mic_volume =3D sd->mic_volume; + old_volume_limiter =3D sd->volume_limiter; =20 if (hid_is_usb(hdev)) { struct usb_interface *intf =3D to_usb_interface(hdev->dev.parent); @@ -1636,6 +1760,9 @@ static int steelseries_raw_event(struct hid_device *h= dev, if (sd->mic_volume !=3D old_mic_volume) snd_ctl_notify(sd->card, SNDRV_CTL_EVENT_MASK_VALUE, &sd->mic_volume_id); + if (sd->volume_limiter !=3D old_volume_limiter) + snd_ctl_notify(sd->card, SNDRV_CTL_EVENT_MASK_VALUE, + &sd->volume_limiter_id); } =20 return 0; --=20 2.53.0 From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qk1-f181.google.com (mail-qk1-f181.google.com [209.85.222.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5EFD044CAC1 for ; Fri, 27 Feb 2026 23:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236260; cv=none; b=MZzxoAzfiLy9+ReiP53UQbbTJFhtcUFfUfYq2a9OCPgRISuXlhkLOlaEgmPniwEksTJOQdg8aoTH7nsZe3gdhLyTwGFB3lpmWA1ayftcF/+Q06Yw4TM0w1SGfxKOTt90/XzY9s7Bz54elOXMrVqGTOZxaxuuI6ijchdb9nSwZRE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236260; c=relaxed/simple; bh=fk+HCmEkn2V+zvnzFzgNb3hpRODSPoydmio0BhTwl+8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cdNATNPOnyqXx/6d8Exuhk+gu/X84mb8iMewnY5/e2W3k0Ey95b1XRafXOYocSoXuoCWFaSaCVF+xGLZqSU5Lhk0ugWbgcDBfagOvW31rK2S1B5A2DZpXtFtpWy1+eTaCvq2guZekNgZ9o3FtD88K4vho6J5a8oNNjdgYCMZlA8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=VikZI4lv; arc=none smtp.client-ip=209.85.222.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="VikZI4lv" Received: by mail-qk1-f181.google.com with SMTP id af79cd13be357-8cb3e0093e3so248031685a.0 for ; Fri, 27 Feb 2026 15:50:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236257; x=1772841057; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=49K9+iLpiU/4K4saCdS/M+tHxG5D6Umg8q9Uy2xKVVU=; b=VikZI4lvA83qtTrWkZ64hBCjuhcDq0VRsC35/utRFOfMnmvtZAJClnMOt+oAjPJ/bw v0W6wk7GeKYyu5Ryog5J0AssjoFuGNeAKAGaaepFbAhvO/ppRIT+tFRV4axfYcjC3Xfq 9bA6qT66AfR6wXEWjqFNjtg6Heo3fzCrZ9Iq1E8uvDi6R7FzTnGCSWA/A0oulwMb2gSg 1wHNAadzCPhnVqplnH9/7/gUdYLZa0zQ3dQAhkg/9AkIlqVQQiIwCmSCvB6sJZrMP/66 0hK38L/GvKjT2HxlvpoXXSvEQwATU0RKgcCtiYhdfIEi5hXaswhhOI3Y1W+91gNYzl0P 7cTA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236257; x=1772841057; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=49K9+iLpiU/4K4saCdS/M+tHxG5D6Umg8q9Uy2xKVVU=; b=XZM/uhuvsHHzS3XYpJKK/fCFGzeRKaWo+RyBfRnYC0KTBgbMDZip1uRE7+VsJnWAzd /Qg82N23+VhqqMz0Jl00Jx47VzmghbphWWUZtB4Z8lK+Li4hDsQmXux8KChC8MV1vbKO lyt7lmlFV5LwMYhVvQdDHY273XmGfzBGb8a+MfSBjd7xAtGLyKieL3vynQv0ueBd7SRC 56SBLS+xKAfdu3Vi2IYX27NDdWXfHpLkMU3BK3/fJagd2ViScoUNrLaLRXxlddmQa1LZ QjYldnTOiY1RUw600o9DudY8h7A+qAz1OqcpmeyBJOScUF02JdjhhruqN9gSRnVe53Di 5bYw== X-Forwarded-Encrypted: i=1; AJvYcCUeqRKPOlIABNn3+A2SAVLjlrybp+7ImSKOdLrVR77GoUcpjrJp4PtLULKZUl6HSXyEB3ZCoZvIFGMVCMo=@vger.kernel.org X-Gm-Message-State: AOJu0YwGt+yJq26QpZHrLrWR84SKBCErDlhRX/n0ANo6MOs9cP0OL0R0 suKcX/dixUGXjyv92m0G3AVtFmNi1TQfc03TlyFDUBD5ehRl7ui4vIEI4kzaBg== X-Gm-Gg: ATEYQzwClihAN3k39C1LtNQlbiq11iomZfWmhZNazRtOAH5H8wu9goHYebWZQIp2sLf osusNxY50eV9Bre57rE15iRSq3g/K807Zn3rk8SBs6mR8nkza0CLR/Aap9tdC6xy7P7DGmZZPe/ /c2/rDd0kp+Yfx5mHZvEEcofcwNlwAS+CO1Fiitr8FClGyFbauIgenozf1K4dhcWJBx4TN8SjYL wvb9OgFxu2WfGGBhVqxciFDT8YKcAVD6uwS7wSV78iQTQQJJXmVgJ5MPz2to2ekvDKvpCjRNB40 P1SgPchCqCTZ/lcFPfM/ypaUP6rd6teTQSnMW9CkkHpDROeZXvl/0Y51FJEKmL+TNmt0ywSNw3e ycfQXYpovXNO4O3JcFWXNXb/xFP4OBrUiDqMGbeI9cuzYn8HLR8nUraFKBe0/Y3J7J8EIr+Sy4k viZydgdXQMpsesqQAezFnrQNvy24TM6+zwUAocuE0uLoklwRPP0g== X-Received: by 2002:a05:620a:29d3:b0:8b1:7c0c:e27f with SMTP id af79cd13be357-8cbc8e54d43mr525413385a.82.1772236257123; Fri, 27 Feb 2026 15:50:57 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:56 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 14/18] HID: steelseries: Add Bluetooth call audio ducking control Date: Fri, 27 Feb 2026 18:50:38 -0500 Message-ID: <20260227235042.410062-15-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Expose Bluetooth call audio ducking behavior as a writable ALSA enumerated mixer control ("Bluetooth Call Audio Ducking"), with three options: off, lower game audio by 12 dB, or mute game audio entirely. On the Arctis Nova 7 Gen2, this setting is stored alongside inactive timeout and Bluetooth auto-enable in a dedicated device configuration block. The settings request is expanded to also send a 0x00/0xa0 device query in addition to the existing 0x00/0x20 audio settings query. Signed-off-by: Sriman Achanta --- drivers/hid/hid-steelseries.c | 120 ++++++++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index 47ffec481571..bb9abbb0b6f8 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -30,12 +30,14 @@ #define SS_CAP_SIDETONE BIT(6) #define SS_CAP_MIC_VOLUME BIT(7) #define SS_CAP_VOLUME_LIMITER BIT(8) +#define SS_CAP_BT_CALL_DUCKING BIT(9) =20 #define SS_QUIRK_STATUS_SYNC_POLL BIT(0) =20 #define SS_SETTING_SIDETONE 0 #define SS_SETTING_MIC_VOLUME 1 #define SS_SETTING_VOLUME_LIMITER 2 +#define SS_SETTING_BT_CALL_DUCKING 3 =20 struct steelseries_device; =20 @@ -80,12 +82,14 @@ struct steelseries_device { struct snd_ctl_elem_id sidetone_id; struct snd_ctl_elem_id mic_volume_id; struct snd_ctl_elem_id volume_limiter_id; + struct snd_ctl_elem_id bt_call_ducking_id; u8 chatmix_chat; u8 chatmix_game; bool mic_muted; u8 sidetone; u8 mic_volume; bool volume_limiter; + u8 bt_call_ducking; =20 bool bt_enabled; bool bt_device_connected; @@ -573,6 +577,9 @@ static int steelseries_arctis_nova_7_write_setting(stru= ct hid_device *hdev, case SS_SETTING_VOLUME_LIMITER: cmd =3D 0x3a; break; + case SS_SETTING_BT_CALL_DUCKING: + cmd =3D 0xb3; + break; default: return -EINVAL; } @@ -883,15 +890,23 @@ static void steelseries_arctis_nova_7_gen2_parse_stat= us(struct steelseries_devic =20 static int steelseries_arctis_nova_7_gen2_request_settings(struct hid_devi= ce *hdev) { - const u8 data[] =3D { 0x00, 0x20 }; + const u8 audio_data[] =3D { 0x00, 0x20 }; + const u8 device_data[] =3D { 0x00, 0xa0 }; + int ret; =20 - return steelseries_send_output_report(hdev, data, sizeof(data)); + ret =3D steelseries_send_output_report(hdev, audio_data, sizeof(audio_dat= a)); + if (ret) + return ret; + + msleep(10); + + return steelseries_send_output_report(hdev, device_data, sizeof(device_da= ta)); } =20 static void steelseries_arctis_nova_7_gen2_parse_settings( struct steelseries_device *sd, u8 *data, int size) { - if (size < 4) + if (size < 5) return; =20 switch (data[0]) { @@ -900,6 +915,9 @@ static void steelseries_arctis_nova_7_gen2_parse_settin= gs( sd->sidetone =3D data[2]; sd->volume_limiter =3D data[3]; break; + case 0xa0: + sd->bt_call_ducking =3D data[4]; + break; case 0x37: sd->mic_volume =3D data[1]; break; @@ -909,6 +927,9 @@ static void steelseries_arctis_nova_7_gen2_parse_settin= gs( case 0x3a: sd->volume_limiter =3D data[1]; break; + case 0xb3: + sd->bt_call_ducking =3D data[1]; + break; } } =20 @@ -1025,7 +1046,8 @@ static const struct steelseries_device_info arctis_no= va_5x_info =3D { static const struct steelseries_device_info arctis_nova_7_info =3D { .sync_interface =3D 3, .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE | - SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER, + SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER | + SS_CAP_BT_CALL_DUCKING, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 3, .mic_volume_max =3D 7, @@ -1036,7 +1058,8 @@ static const struct steelseries_device_info arctis_no= va_7_info =3D { =20 static const struct steelseries_device_info arctis_nova_7p_info =3D { .sync_interface =3D 3, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMI= TER, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMI= TER | + SS_CAP_BT_CALL_DUCKING, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .mic_volume_max =3D 7, .request_status =3D steelseries_arctis_nova_request_status, @@ -1050,7 +1073,8 @@ static const struct steelseries_device_info arctis_no= va_7_gen2_info =3D { .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_MIC_MUTE | SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED | SS_CAP_EXTERNAL_CONFIG | SS_CAP_SIDETONE | - SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER, + SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER | + SS_CAP_BT_CALL_DUCKING, .sidetone_max =3D 3, .mic_volume_max =3D 7, .request_status =3D steelseries_arctis_nova_request_status, @@ -1557,6 +1581,70 @@ static const struct snd_kcontrol_new steelseries_vol= ume_limiter_control =3D { .put =3D steelseries_volume_limiter_put, }; =20 +static const char *const bt_call_ducking_texts[] =3D { + "Off", + "Lower Volume (-12dB)", + "Mute Game", +}; + +static int steelseries_bt_call_ducking_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(bt_call_ducking_texts), + bt_call_ducking_texts); +} + +static int steelseries_bt_call_ducking_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct steelseries_device *sd =3D snd_kcontrol_chip(kcontrol); + unsigned long flags; + + spin_lock_irqsave(&sd->lock, flags); + ucontrol->value.enumerated.item[0] =3D sd->bt_call_ducking; + spin_unlock_irqrestore(&sd->lock, flags); + return 0; +} + +static int steelseries_bt_call_ducking_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct steelseries_device *sd =3D snd_kcontrol_chip(kcontrol); + unsigned long flags; + u8 new_value; + int ret; + + new_value =3D ucontrol->value.enumerated.item[0]; + if (new_value >=3D ARRAY_SIZE(bt_call_ducking_texts)) + return -EINVAL; + + spin_lock_irqsave(&sd->lock, flags); + if (sd->bt_call_ducking =3D=3D new_value) { + spin_unlock_irqrestore(&sd->lock, flags); + return 0; + } + spin_unlock_irqrestore(&sd->lock, flags); + + ret =3D sd->info->write_setting(sd->hdev, SS_SETTING_BT_CALL_DUCKING, + new_value); + if (ret) + return ret; + + spin_lock_irqsave(&sd->lock, flags); + sd->bt_call_ducking =3D new_value; + spin_unlock_irqrestore(&sd->lock, flags); + + return 1; +} + +static const struct snd_kcontrol_new steelseries_bt_call_ducking_control = =3D { + .iface =3D SNDRV_CTL_ELEM_IFACE_MIXER, + .name =3D "Bluetooth Call Audio Ducking", + .info =3D steelseries_bt_call_ducking_info, + .get =3D steelseries_bt_call_ducking_get, + .put =3D steelseries_bt_call_ducking_put, +}; + static int steelseries_snd_register(struct steelseries_device *sd) { struct hid_device *hdev =3D sd->hdev; @@ -1644,6 +1732,21 @@ static int steelseries_snd_register(struct steelseri= es_device *sd) sd->volume_limiter_id =3D kctl->id; } =20 + if (sd->info->capabilities & SS_CAP_BT_CALL_DUCKING) { + struct snd_kcontrol *kctl; + struct snd_kcontrol_new ducking_ctl =3D steelseries_bt_call_ducking_cont= rol; + + ducking_ctl.access =3D SNDRV_CTL_ELEM_ACCESS_READWRITE; + if (sd->info->capabilities & SS_CAP_EXTERNAL_CONFIG) + ducking_ctl.access |=3D SNDRV_CTL_ELEM_ACCESS_VOLATILE; + + kctl =3D snd_ctl_new1(&ducking_ctl, sd); + ret =3D snd_ctl_add(sd->card, kctl); + if (ret < 0) + goto err_free_card; + sd->bt_call_ducking_id =3D kctl->id; + } + ret =3D snd_card_register(sd->card); if (ret < 0) goto err_free_card; @@ -1677,6 +1780,7 @@ static int steelseries_raw_event(struct hid_device *h= dev, u8 old_sidetone; u8 old_mic_volume; bool old_volume_limiter; + u8 old_bt_call_ducking; bool is_async_interface =3D false; =20 if (hdev->product =3D=3D USB_DEVICE_ID_STEELSERIES_SRWS1) @@ -1694,6 +1798,7 @@ static int steelseries_raw_event(struct hid_device *h= dev, old_sidetone =3D sd->sidetone; old_mic_volume =3D sd->mic_volume; old_volume_limiter =3D sd->volume_limiter; + old_bt_call_ducking =3D sd->bt_call_ducking; =20 if (hid_is_usb(hdev)) { struct usb_interface *intf =3D to_usb_interface(hdev->dev.parent); @@ -1763,6 +1868,9 @@ static int steelseries_raw_event(struct hid_device *h= dev, if (sd->volume_limiter !=3D old_volume_limiter) snd_ctl_notify(sd->card, SNDRV_CTL_EVENT_MASK_VALUE, &sd->volume_limiter_id); + if (sd->bt_call_ducking !=3D old_bt_call_ducking) + snd_ctl_notify(sd->card, SNDRV_CTL_EVENT_MASK_VALUE, + &sd->bt_call_ducking_id); } =20 return 0; --=20 2.53.0 From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qt1-f180.google.com (mail-qt1-f180.google.com [209.85.160.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EAC90453493 for ; Fri, 27 Feb 2026 23:50:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236262; cv=none; b=PIdpAxjIbpB++vZzndP6gaHyrqBpi1Z7XBOZtOoWKxgCSeqEfHx/z+ymLb+gdRguIRgxDsno7/FFWcUsk8BR7G93tl21tvdzvPbKRaDzq3P9xpvXV6l93HVarUgh8RnB647kvlAGd0rlAbHH+TnPpE+uMyQt/qF3hBPZjaDBUks= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236262; c=relaxed/simple; bh=AG4QH/z0Fsk/9w3aS4C3kaPKUCn8ChtL1KaFOQ88o58=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=p6B3Wij0lN6K82tkFVUhCow97q2rLoeL81LRPc6p14dyaoRIX294Fve1x6cDUL1cfGv+Z9pcGJNJcwPORZBf5TOOrwO/tAUeWcVw9Tc9i+yBJh24uKI6Gsmk14eIUA05N9lbqNbjxP8wS2QJ9g14bMRggVQSwe/yoq3lVAm38q4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=OChYf+/d; arc=none smtp.client-ip=209.85.160.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="OChYf+/d" Received: by mail-qt1-f180.google.com with SMTP id d75a77b69052e-506cb1b63d0so31701021cf.2 for ; Fri, 27 Feb 2026 15:50:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236258; x=1772841058; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=iCBvGrhUnuglIWeduAltHTKHljsgtFcgUvxGu0q8LnU=; b=OChYf+/d8VD1MEPZFNegtP2Pv3hx+wnKt1R0TydRQtmOFeASVQx5U2Tu/dybQ05voy UbJmnqslRH3/KhQ3vFBPx4GhQbmvdKGJxQ0rMK6f+kh17Z7BgePZsrEZtcQaO6701qFp CCcvcljhUPCItyJaDU9y0y/u9zHmgTm9UfgsfD8MrYFte7New/euaZPRwt1KJkvEUZik mpp8DfUQi1G/p8lj1MmhLGvkctBN7nUzNXPgZjfL3XzOJAiUCjoDWPG9iYO3DqKzBE1d TG6S32SrYi0QAh2t285EeG+o//ctReGKPGSJ3sL2NxBl4U9FcXJKYf8B1arlEkJI9NRn xxzw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236258; x=1772841058; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=iCBvGrhUnuglIWeduAltHTKHljsgtFcgUvxGu0q8LnU=; b=iv6z/0n50gudBkM26c/N0HeaHak8Uk0jVFKq04qGvLTIluf1DKPpaW3Ra5TYc5IKkC Dby+TpaGenqBL822QkS98d62hPHTEwHU4nYoP+koMJzzbE3OZHQIIpKOAS2WXNhsDzkV cJGvO0R9UYQd6HWAIfzYPV/xoQoyh6QEZY6SNRkxr6sFDhH/wiT/At0I+KYBh4Sh8fdF 4CuoaylCC/xXO6R201aamPcXMV5jiq/gFw6ro6XrPju8J+LyY3s/9tZjblomUZ/xOPa5 Xtf3C2XhMNcI+niGBpsQ+DUShiGFtoVuSAf1zJOvSGzeJU8bPeiU7eDYD/dYCF8/8GKg 6gYA== X-Forwarded-Encrypted: i=1; AJvYcCXS0aKc+zOoaIbFU5uNpo4I4B8bD6sP9KRPq2U1eSp0FqNX7gideuKZ7gSmJFZ/NQAbvWEaehIM/05ECm0=@vger.kernel.org X-Gm-Message-State: AOJu0Yw45ySup8qSLQSH7Ge0zzOYC2KoxSUhOwwCucBgrCzLVt8lTHQS gUOlSgbQEQcjew4/U4Uv7+Xy+Hmqg6RHO05wPYSBFxnx5HodjWb2P+8B X-Gm-Gg: ATEYQzwemQ2oMnXvl7wYAYbdeivRJLCu+M1FVN0JEFHEs6oiTr+lt7/kKQS6irUAN0A Oh6mm1/pdIfZ0heoeQGYiemCjC4+pEpOn0X28SYubQLw1cy+xpQW97+bVspysU1FU2gIq34PWP8 j6zcPqmy9o0uMQkppcZnrdJgSxyPHFxnrzNhjhJmGWk1b5reGbFAdNiA11y3ljnJocAsbNfk2/z MUljU+GJ4fUGIJOseruShiKXH+4r1L+LObuNbpnBRfsxlSDx0n7svZuYjMyPCAM5oi5ohKMDWUF 6dZ5pkA0227GvozxoIgVc8djFFYM8Yoc4YtU39xcu3ltYI7FuVpNJu2NvAm+gQnA7RCW99g0jdu 9bGrSW+k7zPrLS4bjqb2chOYTgMh/M6xmYJBz3m/m7wAJDwRlr/xrSLROgq6F+EPWSSAmUosQFU FFxO9lS8txOl3LZ4hxIYW2Qify5jeOfD9guayyMlkZVZ6LFtZWag== X-Received: by 2002:ac8:5f4a:0:b0:506:1edb:2cef with SMTP id d75a77b69052e-507527245d9mr60691191cf.2.1772236257784; Fri, 27 Feb 2026 15:50:57 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:57 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 15/18] HID: steelseries: Add inactive time sysfs attribute Date: Fri, 27 Feb 2026 18:50:39 -0500 Message-ID: <20260227235042.410062-16-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Expose the headset auto-shutoff timer as a read/write sysfs attribute (inactive_time), in minutes. Writing the attribute immediately sends the new value to the device; reading it returns the last value reported by the firmware. The wire encoding differs per family: - Arctis 1: HID feature report 0x06/0x53 with the value in minutes - Arctis 7: HID feature report 0x06/0x51; split into its own write function as the command byte differs from the Arctis 1 - Arctis 9: converts minutes to seconds in a big-endian u16 - Nova 3P: rounds down to the nearest value in a discrete set {0,1,5,10,15,30,45,60,75,90} before sending command 0xa3 - Nova 5/7: output report with command 0xa3, no rounding required - Nova Pro: maps minutes to six firmware-defined level indices via command 0xc1 The inactive_time_max field is added to the device info struct to enforce the per-device maximum at write time. Signed-off-by: Sriman Achanta --- drivers/hid/hid-steelseries.c | 183 +++++++++++++++++++++++++++++++--- 1 file changed, 167 insertions(+), 16 deletions(-) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index bb9abbb0b6f8..f076a0ef8af1 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -31,6 +31,7 @@ #define SS_CAP_MIC_VOLUME BIT(7) #define SS_CAP_VOLUME_LIMITER BIT(8) #define SS_CAP_BT_CALL_DUCKING BIT(9) +#define SS_CAP_INACTIVE_TIME BIT(10) =20 #define SS_QUIRK_STATUS_SYNC_POLL BIT(0) =20 @@ -38,6 +39,7 @@ #define SS_SETTING_MIC_VOLUME 1 #define SS_SETTING_VOLUME_LIMITER 2 #define SS_SETTING_BT_CALL_DUCKING 3 +#define SS_SETTING_INACTIVE_TIME 4 =20 struct steelseries_device; =20 @@ -51,6 +53,7 @@ struct steelseries_device_info { u8 sidetone_max; u8 mic_volume_min; u8 mic_volume_max; + u8 inactive_time_max; =20 int (*request_status)(struct hid_device *hdev); void (*parse_status)(struct steelseries_device *sd, u8 *data, int size); @@ -93,6 +96,7 @@ struct steelseries_device { =20 bool bt_enabled; bool bt_device_connected; + u8 inactive_timeout; =20 spinlock_t lock; bool removed; @@ -476,6 +480,37 @@ static int steelseries_arctis_1_write_setting(struct h= id_device *hdev, return steelseries_send_feature_report(hdev, data, sizeof(data)); } + case SS_SETTING_INACTIVE_TIME: { + const u8 data[] =3D { 0x06, 0x53, value }; + + return steelseries_send_feature_report(hdev, data, sizeof(data)); + } + default: + return -EINVAL; + } +} + +static int steelseries_arctis_7_write_setting(struct hid_device *hdev, + u8 setting, u8 value) +{ + switch (setting) { + case SS_SETTING_SIDETONE: + if (value =3D=3D 0) { + const u8 data[] =3D { 0x06, 0x35 }; + + return steelseries_send_feature_report(hdev, data, + sizeof(data)); + } else { + const u8 data[] =3D { 0x06, 0x35, 0x01, 0x00, value }; + + return steelseries_send_feature_report(hdev, data, + sizeof(data)); + } + case SS_SETTING_INACTIVE_TIME: { + const u8 data[] =3D { 0x06, 0x51, value }; + + return steelseries_send_feature_report(hdev, data, sizeof(data)); + } default: return -EINVAL; } @@ -490,11 +525,30 @@ static int steelseries_arctis_9_write_setting(struct = hid_device *hdev, =20 return steelseries_send_feature_report(hdev, data, sizeof(data)); } + case SS_SETTING_INACTIVE_TIME: { + u16 seconds =3D (u16)value * 60; + const u8 data[] =3D { 0x04, 0x00, seconds >> 8, seconds & 0xff }; + + return steelseries_send_feature_report(hdev, data, sizeof(data)); + } default: return -EINVAL; } } =20 +static u8 steelseries_arctis_nova_3p_round_inactive_time(u8 minutes) +{ + static const u8 supported[] =3D { 0, 1, 5, 10, 15, 30, 45, 60, 75, 90 }; + int i; + + for (i =3D ARRAY_SIZE(supported) - 1; i > 0; i--) { + if (minutes >=3D supported[i]) + return supported[i]; + } + + return 0; +} + static int steelseries_arctis_nova_3p_write_setting(struct hid_device *hde= v, u8 setting, u8 value) { @@ -510,6 +564,10 @@ static int steelseries_arctis_nova_3p_write_setting(st= ruct hid_device *hdev, case SS_SETTING_MIC_VOLUME: cmd =3D 0x37; break; + case SS_SETTING_INACTIVE_TIME: + cmd =3D 0xa3; + value =3D steelseries_arctis_nova_3p_round_inactive_time(value); + break; default: return -EINVAL; } @@ -542,6 +600,9 @@ static int steelseries_arctis_nova_5_write_setting(stru= ct hid_device *hdev, case SS_SETTING_VOLUME_LIMITER: cmd =3D 0x27; break; + case SS_SETTING_INACTIVE_TIME: + cmd =3D 0xa3; + break; default: return -EINVAL; } @@ -580,6 +641,9 @@ static int steelseries_arctis_nova_7_write_setting(stru= ct hid_device *hdev, case SS_SETTING_BT_CALL_DUCKING: cmd =3D 0xb3; break; + case SS_SETTING_INACTIVE_TIME: + cmd =3D 0xa3; + break; default: return -EINVAL; } @@ -612,6 +676,24 @@ static int steelseries_arctis_nova_pro_write_setting(s= truct hid_device *hdev, case SS_SETTING_MIC_VOLUME: cmd =3D 0x37; break; + case SS_SETTING_INACTIVE_TIME: + cmd =3D 0xc1; + /* Map minutes to firmware level */ + if (value >=3D 45) + value =3D 6; /* 60 min */ + else if (value >=3D 23) + value =3D 5; /* 30 min */ + else if (value >=3D 13) + value =3D 4; /* 15 min */ + else if (value >=3D 8) + value =3D 3; /* 10 min */ + else if (value >=3D 3) + value =3D 2; /* 5 min */ + else if (value > 0) + value =3D 1; /* 1 min */ + else + value =3D 0; /* disabled */ + break; default: return -EINVAL; } @@ -916,6 +998,7 @@ static void steelseries_arctis_nova_7_gen2_parse_settin= gs( sd->volume_limiter =3D data[3]; break; case 0xa0: + sd->inactive_timeout =3D data[1]; sd->bt_call_ducking =3D data[4]; break; case 0x37: @@ -927,6 +1010,9 @@ static void steelseries_arctis_nova_7_gen2_parse_setti= ngs( case 0x3a: sd->volume_limiter =3D data[1]; break; + case 0xa3: + sd->inactive_timeout =3D data[1]; + break; case 0xb3: sd->bt_call_ducking =3D data[1]; break; @@ -936,11 +1022,13 @@ static void steelseries_arctis_nova_7_gen2_parse_set= tings( static void steelseries_arctis_nova_pro_parse_settings( struct steelseries_device *sd, u8 *data, int size) { - if (size < 10) + if (size < 13) return; =20 - if (data[0] =3D=3D 0x06 && data[1] =3D=3D 0xb0) + if (data[0] =3D=3D 0x06 && data[1] =3D=3D 0xb0) { sd->mic_volume =3D data[9]; + sd->inactive_timeout =3D data[12]; + } } =20 static void steelseries_arctis_nova_pro_parse_status(struct steelseries_de= vice *sd, @@ -970,9 +1058,10 @@ static const struct steelseries_device_info srws1_inf= o =3D { }; =20 static const struct steelseries_device_info arctis_1_info =3D { .sync_interface =3D 3, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_SIDETONE, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_SIDETONE | SS_CAP_INACTIVE_TIME, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 18, + .inactive_time_max =3D 90, .request_status =3D steelseries_arctis_1_request_status, .parse_status =3D steelseries_arctis_1_parse_status, .write_setting =3D steelseries_arctis_1_write_setting, @@ -980,19 +1069,23 @@ static const struct steelseries_device_info arctis_1= _info =3D { =20 static const struct steelseries_device_info arctis_7_info =3D { .sync_interface =3D 5, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE | + SS_CAP_INACTIVE_TIME, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 18, + .inactive_time_max =3D 90, .request_status =3D steelseries_arctis_7_request_status, .parse_status =3D steelseries_arctis_7_parse_status, - .write_setting =3D steelseries_arctis_1_write_setting, + .write_setting =3D steelseries_arctis_7_write_setting, }; =20 static const struct steelseries_device_info arctis_7_plus_info =3D { .sync_interface =3D 3, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE | + SS_CAP_INACTIVE_TIME, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 3, + .inactive_time_max =3D 90, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_7_plus_parse_status, .write_setting =3D steelseries_arctis_nova_5_write_setting, @@ -1000,9 +1093,11 @@ static const struct steelseries_device_info arctis_7= _plus_info =3D { =20 static const struct steelseries_device_info arctis_9_info =3D { .sync_interface =3D 0, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE | + SS_CAP_INACTIVE_TIME, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 61, + .inactive_time_max =3D 255, .request_status =3D steelseries_arctis_9_request_status, .parse_status =3D steelseries_arctis_9_parse_status, .write_setting =3D steelseries_arctis_9_write_setting, @@ -1010,10 +1105,12 @@ static const struct steelseries_device_info arctis_= 9_info =3D { =20 static const struct steelseries_device_info arctis_nova_3p_info =3D { .sync_interface =3D 4, - .capabilities =3D SS_CAP_BATTERY | SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME, + .capabilities =3D SS_CAP_BATTERY | SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME | + SS_CAP_INACTIVE_TIME, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 10, .mic_volume_max =3D 14, + .inactive_time_max =3D 90, .request_status =3D steelseries_arctis_nova_3p_request_status, .parse_status =3D steelseries_arctis_nova_3p_parse_status, .write_setting =3D steelseries_arctis_nova_3p_write_setting, @@ -1022,10 +1119,11 @@ static const struct steelseries_device_info arctis_= nova_3p_info =3D { static const struct steelseries_device_info arctis_nova_5_info =3D { .sync_interface =3D 3, .capabilities =3D SS_CAP_BATTERY | SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME | - SS_CAP_VOLUME_LIMITER, + SS_CAP_VOLUME_LIMITER | SS_CAP_INACTIVE_TIME, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 10, .mic_volume_max =3D 15, + .inactive_time_max =3D 255, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_5_parse_status, .write_setting =3D steelseries_arctis_nova_5_write_setting, @@ -1034,10 +1132,12 @@ static const struct steelseries_device_info arctis_= nova_5_info =3D { static const struct steelseries_device_info arctis_nova_5x_info =3D { .sync_interface =3D 3, .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE | - SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER, + SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER | + SS_CAP_INACTIVE_TIME, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 10, .mic_volume_max =3D 15, + .inactive_time_max =3D 255, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_5x_parse_status, .write_setting =3D steelseries_arctis_nova_5_write_setting, @@ -1047,10 +1147,11 @@ static const struct steelseries_device_info arctis_= nova_7_info =3D { .sync_interface =3D 3, .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER | - SS_CAP_BT_CALL_DUCKING, + SS_CAP_BT_CALL_DUCKING | SS_CAP_INACTIVE_TIME, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 3, .mic_volume_max =3D 7, + .inactive_time_max =3D 255, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_7_parse_status, .write_setting =3D steelseries_arctis_nova_7_write_setting, @@ -1059,9 +1160,10 @@ static const struct steelseries_device_info arctis_n= ova_7_info =3D { static const struct steelseries_device_info arctis_nova_7p_info =3D { .sync_interface =3D 3, .capabilities =3D SS_CAP_BATTERY | SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMI= TER | - SS_CAP_BT_CALL_DUCKING, + SS_CAP_BT_CALL_DUCKING | SS_CAP_INACTIVE_TIME, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .mic_volume_max =3D 7, + .inactive_time_max =3D 255, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_7_parse_status, .write_setting =3D steelseries_arctis_nova_7_write_setting, @@ -1074,9 +1176,10 @@ static const struct steelseries_device_info arctis_n= ova_7_gen2_info =3D { SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED | SS_CAP_EXTERNAL_CONFIG | SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER | - SS_CAP_BT_CALL_DUCKING, + SS_CAP_BT_CALL_DUCKING | SS_CAP_INACTIVE_TIME, .sidetone_max =3D 3, .mic_volume_max =3D 7, + .inactive_time_max =3D 255, .request_status =3D steelseries_arctis_nova_request_status, .parse_status =3D steelseries_arctis_nova_7_gen2_parse_status, .request_settings =3D steelseries_arctis_nova_7_gen2_request_settings, @@ -1088,11 +1191,12 @@ static const struct steelseries_device_info arctis_= nova_pro_info =3D { .sync_interface =3D 4, .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_MIC_MUTE | SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED | - SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME, + SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME | SS_CAP_INACTIVE_TIME, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 3, .mic_volume_min =3D 1, .mic_volume_max =3D 10, + .inactive_time_max =3D 60, .request_status =3D steelseries_arctis_nova_pro_request_status, .parse_status =3D steelseries_arctis_nova_pro_parse_status, .parse_settings =3D steelseries_arctis_nova_pro_parse_settings, @@ -1271,12 +1375,55 @@ static ssize_t bt_device_connected_show(struct devi= ce *dev, return sysfs_emit(buf, "%d\n", sd->bt_device_connected); } =20 +static ssize_t inactive_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hid_device *hdev =3D to_hid_device(dev); + struct steelseries_device *sd =3D hid_get_drvdata(hdev); + + if (!sd->headset_connected) + return -ENODEV; + + return sysfs_emit(buf, "%d\n", sd->inactive_timeout); +} + +static ssize_t inactive_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct hid_device *hdev =3D to_hid_device(dev); + struct steelseries_device *sd =3D hid_get_drvdata(hdev); + unsigned int value; + int ret; + + if (!sd->headset_connected) + return -ENODEV; + + ret =3D kstrtouint(buf, 10, &value); + if (ret) + return ret; + + if (value > sd->info->inactive_time_max) + return -EINVAL; + + ret =3D sd->info->write_setting(sd->hdev, SS_SETTING_INACTIVE_TIME, + value); + if (ret) + return ret; + + sd->inactive_timeout =3D value; + + return count; +} + static DEVICE_ATTR_RO(bt_enabled); static DEVICE_ATTR_RO(bt_device_connected); +static DEVICE_ATTR_RW(inactive_time); =20 static struct attribute *steelseries_headset_attrs[] =3D { &dev_attr_bt_enabled.attr, &dev_attr_bt_device_connected.attr, + &dev_attr_inactive_time.attr, NULL, }; =20 @@ -1298,6 +1445,8 @@ static umode_t steelseries_headset_attr_is_visible(st= ruct kobject *kobj, return (caps & SS_CAP_BT_ENABLED) ? attr->mode : 0; if (attr =3D=3D &dev_attr_bt_device_connected.attr) return (caps & SS_CAP_BT_DEVICE_CONNECTED) ? attr->mode : 0; + if (attr =3D=3D &dev_attr_inactive_time.attr) + return (caps & SS_CAP_INACTIVE_TIME) ? attr->mode : 0; =20 return 0; } @@ -1956,7 +2105,8 @@ static int steelseries_probe(struct hid_device *hdev, hid_warn(hdev, "Failed to register battery: %d\n", ret); } =20 - if (info->capabilities & (SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED= )) { + if (info->capabilities & (SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED= | + SS_CAP_INACTIVE_TIME)) { ret =3D sysfs_create_group(&hdev->dev.kobj, &steelseries_headset_attr_group); if (ret) @@ -2038,7 +2188,8 @@ static void steelseries_remove(struct hid_device *hde= v) } =20 if (interface_num =3D=3D sd->info->sync_interface) { - if (sd->info->capabilities & (SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNE= CTED)) + if (sd->info->capabilities & (SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNE= CTED | + SS_CAP_INACTIVE_TIME)) sysfs_remove_group(&hdev->dev.kobj, &steelseries_headset_attr_group); =20 --=20 2.53.0 From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qv1-f52.google.com (mail-qv1-f52.google.com [209.85.219.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 76EC4477E5D for ; Fri, 27 Feb 2026 23:50:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236261; cv=none; b=CdrDms5+qfksgG10Mu5hAZk7CS2KcPUx0wslEcNil7mXjeMNFhgMf3q9WYoGkPTDvC23fTvUkIJgavCDVDTW96aSl9qBI/Xf6eBNO2HXQ2Vs46hZsFJJ4Pz0GCEf1gawnuZ/jQ0mjkRK0aCDyRdEF5q/orcUGMyxgcgbs8BRCj8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236261; c=relaxed/simple; bh=LbujEYa9xoRLHSlUYndNgtlMJuRqK9FWNuK7En9isu0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IdkJ/DXEDsUSKm+RF4Ie82XCiNZ/hpIoelhGqBZkkxSSQi+lLnIzwpboQcpJN2EKE9iqS0B7XR8xFODIWYg2e5W9xFePKJQoWfL7g7VT9MkaCUwBvYjNCYdCjFrFhDpwb+EpnKHIxy2AKxaxPiPCAHB6+8Ib8Ov1DgKh8alPmhY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=UlyqjzGZ; arc=none smtp.client-ip=209.85.219.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="UlyqjzGZ" Received: by mail-qv1-f52.google.com with SMTP id 6a1803df08f44-8954a050c19so31119226d6.3 for ; Fri, 27 Feb 2026 15:50:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236258; x=1772841058; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=payWMY6Loq24vrolvebCCPCb0PjAuPj9S7qipv+bO2Q=; b=UlyqjzGZe6W5d5xUy17OZd6lKuDBkaPLheUoyOkFm90UIRPqjbjObiy3Jg993LFNXF lZxqcFEu6DfTd0xh8l2wwWbV6AfiqOB3fa4v6ZT+hv/SJPJFtmROVO26iLvmpplOa37L rTPpSwUJAw0mmVsSIFXPLsxGtUzuCJe9bYFfNSqjc9KmRiWweRBMrFuVs2lPA+BXNVPq slQkJ807fjpr5aMyV47QmjougeIABeXly4K/Uj7aynoO7UD/IcXaB/Ju7K3rqRDpK4T/ 2ROD36Eo2xRe+qLCvm2fAdPfR8JxePn22newNJik0fpTkzb5Pbg0EoG7wzFJKbNXiknf 3C+Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236258; x=1772841058; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=payWMY6Loq24vrolvebCCPCb0PjAuPj9S7qipv+bO2Q=; b=I9JNbVM1dKb6mEWs0Ht8bMxxgtVj0DxVnmV6sfMBbY7ik17ONunq/i0VlyeiLSYQYO 4pNNYphk49Qr7h7MXgbIx3e5AvcJnZbn13s/LqhqOZ7KBXjX6jUNHrSaqJf6wvDxKK/l 01u8t0kgX6YpVh84nrB90Y0n/HdyQxcJalHVtmsglJJuzaM2lrfkiZviu2K9eXchDOYV ujJQMm6ey+a7PlvJvtjv5xcizwSR/FdXLdM086U54vZRN2yGqayTcPP5iEfyLl7ntjcI imeVNdKA3rLEAc2+GqERpnzkasKmglWhvs/+J9GRhSPNalSvbKSYoCmTr3VPLkPYUV23 uhAw== X-Forwarded-Encrypted: i=1; AJvYcCW7UFOAtJChul+qRa11JZJVIZIGbR0zNkpAfBVQt2DfJHL5WXZfpV+/MI9tkb6Q9CJepfNbKBVrVLXUa1w=@vger.kernel.org X-Gm-Message-State: AOJu0Yx21UUq8QtbczlF73crUEoXG7ZrxczX7IcA6c4ZrBamsCf9szG5 Ecq3wtYuY2r6VT1nfIqsfRiqJrZbdhKXM4TcYKxJjLhw7SYOKsxp+nNz X-Gm-Gg: ATEYQzxYTpPOOueoSzvBnKOynstfaRfj82OL2S9AhEq1SbA/PrkQIz6wQTztIvcL/Tt MHg9uv9GhAQf25XrOKnlUmUPxfu6mFF5P8n1sIgfY3MW+P8YMRnT0oT0O+rLdo9LLKVpwLrVGTz bhzSxatNLOChTv0IA42iwxDF+qJTL0dxsSklDzCd0N0AHyIfcY6aRygn3w9IUxalR1CfNvzKdLR zoChQWk7JMxfKL5PU3lNxe1CSDlCGNiEWK/8epyEVOA7IPO2xDmrP6H24B5UmhwyV842FvjcitS n5RW6gljh2dAkmWN3ld1/IkO78k50nk1yQSpt8RoTW3m4E7uDgn7P1MZLBzcwFJq0Y52gP/uVLt N0gi84og/JE+7KAzfAbIK+Y3gXmFuQS2KAV8JheJPbXANfqZjNAXE/dDXwzILUmzXv+iiRuCXEy agHqdR03eVQugEZwtmn1ocqpVBLu0dI+MY3qL+vSVXwzoLbofdnw== X-Received: by 2002:ad4:596f:0:b0:896:fe42:e88f with SMTP id 6a1803df08f44-899d1e863e0mr70702846d6.61.1772236258404; Fri, 27 Feb 2026 15:50:58 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:58 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 16/18] HID: steelseries: Add Bluetooth auto-enable sysfs attribute Date: Fri, 27 Feb 2026 18:50:40 -0500 Message-ID: <20260227235042.410062-17-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Expose the Bluetooth auto-enable setting as a read/write sysfs boolean attribute (bt_auto_enable). When enabled, the headset activates its Bluetooth radio automatically on power-on. Currently supported on the Arctis Nova 7, Nova 7P, and Nova 7 Gen2 via write command 0xb2. On the Nova 7 Gen2, the current value is recovered from the 0xa0 device settings response alongside inactive timeout and call audio ducking. Signed-off-by: Sriman Achanta --- drivers/hid/hid-steelseries.c | 65 ++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index f076a0ef8af1..a794af01e15a 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -32,6 +32,7 @@ #define SS_CAP_VOLUME_LIMITER BIT(8) #define SS_CAP_BT_CALL_DUCKING BIT(9) #define SS_CAP_INACTIVE_TIME BIT(10) +#define SS_CAP_BT_AUTO_ENABLE BIT(11) =20 #define SS_QUIRK_STATUS_SYNC_POLL BIT(0) =20 @@ -40,6 +41,7 @@ #define SS_SETTING_VOLUME_LIMITER 2 #define SS_SETTING_BT_CALL_DUCKING 3 #define SS_SETTING_INACTIVE_TIME 4 +#define SS_SETTING_BT_AUTO_ENABLE 5 =20 struct steelseries_device; =20 @@ -97,6 +99,7 @@ struct steelseries_device { bool bt_enabled; bool bt_device_connected; u8 inactive_timeout; + bool bt_auto_enable; =20 spinlock_t lock; bool removed; @@ -644,6 +647,9 @@ static int steelseries_arctis_nova_7_write_setting(stru= ct hid_device *hdev, case SS_SETTING_INACTIVE_TIME: cmd =3D 0xa3; break; + case SS_SETTING_BT_AUTO_ENABLE: + cmd =3D 0xb2; + break; default: return -EINVAL; } @@ -999,6 +1005,7 @@ static void steelseries_arctis_nova_7_gen2_parse_setti= ngs( break; case 0xa0: sd->inactive_timeout =3D data[1]; + sd->bt_auto_enable =3D data[3]; sd->bt_call_ducking =3D data[4]; break; case 0x37: @@ -1013,6 +1020,9 @@ static void steelseries_arctis_nova_7_gen2_parse_sett= ings( case 0xa3: sd->inactive_timeout =3D data[1]; break; + case 0xb2: + sd->bt_auto_enable =3D data[1]; + break; case 0xb3: sd->bt_call_ducking =3D data[1]; break; @@ -1147,7 +1157,8 @@ static const struct steelseries_device_info arctis_no= va_7_info =3D { .sync_interface =3D 3, .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER | - SS_CAP_BT_CALL_DUCKING | SS_CAP_INACTIVE_TIME, + SS_CAP_BT_CALL_DUCKING | SS_CAP_INACTIVE_TIME | + SS_CAP_BT_AUTO_ENABLE, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 3, .mic_volume_max =3D 7, @@ -1160,7 +1171,8 @@ static const struct steelseries_device_info arctis_no= va_7_info =3D { static const struct steelseries_device_info arctis_nova_7p_info =3D { .sync_interface =3D 3, .capabilities =3D SS_CAP_BATTERY | SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMI= TER | - SS_CAP_BT_CALL_DUCKING | SS_CAP_INACTIVE_TIME, + SS_CAP_BT_CALL_DUCKING | SS_CAP_INACTIVE_TIME | + SS_CAP_BT_AUTO_ENABLE, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .mic_volume_max =3D 7, .inactive_time_max =3D 255, @@ -1176,7 +1188,8 @@ static const struct steelseries_device_info arctis_no= va_7_gen2_info =3D { SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED | SS_CAP_EXTERNAL_CONFIG | SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER | - SS_CAP_BT_CALL_DUCKING | SS_CAP_INACTIVE_TIME, + SS_CAP_BT_CALL_DUCKING | SS_CAP_INACTIVE_TIME | + SS_CAP_BT_AUTO_ENABLE, .sidetone_max =3D 3, .mic_volume_max =3D 7, .inactive_time_max =3D 255, @@ -1416,14 +1429,53 @@ static ssize_t inactive_time_store(struct device *d= ev, return count; } =20 +static ssize_t bt_auto_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hid_device *hdev =3D to_hid_device(dev); + struct steelseries_device *sd =3D hid_get_drvdata(hdev); + + if (!sd->headset_connected) + return -ENODEV; + + return sysfs_emit(buf, "%d\n", sd->bt_auto_enable); +} + +static ssize_t bt_auto_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct hid_device *hdev =3D to_hid_device(dev); + struct steelseries_device *sd =3D hid_get_drvdata(hdev); + bool value; + int ret; + + if (!sd->headset_connected) + return -ENODEV; + + ret =3D kstrtobool(buf, &value); + if (ret) + return ret; + + ret =3D sd->info->write_setting(sd->hdev, SS_SETTING_BT_AUTO_ENABLE, valu= e); + if (ret) + return ret; + + sd->bt_auto_enable =3D value; + + return count; +} + static DEVICE_ATTR_RO(bt_enabled); static DEVICE_ATTR_RO(bt_device_connected); static DEVICE_ATTR_RW(inactive_time); +static DEVICE_ATTR_RW(bt_auto_enable); =20 static struct attribute *steelseries_headset_attrs[] =3D { &dev_attr_bt_enabled.attr, &dev_attr_bt_device_connected.attr, &dev_attr_inactive_time.attr, + &dev_attr_bt_auto_enable.attr, NULL, }; =20 @@ -1447,6 +1499,8 @@ static umode_t steelseries_headset_attr_is_visible(st= ruct kobject *kobj, return (caps & SS_CAP_BT_DEVICE_CONNECTED) ? attr->mode : 0; if (attr =3D=3D &dev_attr_inactive_time.attr) return (caps & SS_CAP_INACTIVE_TIME) ? attr->mode : 0; + if (attr =3D=3D &dev_attr_bt_auto_enable.attr) + return (caps & SS_CAP_BT_AUTO_ENABLE) ? attr->mode : 0; =20 return 0; } @@ -2106,7 +2160,8 @@ static int steelseries_probe(struct hid_device *hdev, } =20 if (info->capabilities & (SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNECTED= | - SS_CAP_INACTIVE_TIME)) { + SS_CAP_INACTIVE_TIME | + SS_CAP_BT_AUTO_ENABLE)) { ret =3D sysfs_create_group(&hdev->dev.kobj, &steelseries_headset_attr_group); if (ret) @@ -2189,7 +2244,7 @@ static void steelseries_remove(struct hid_device *hde= v) =20 if (interface_num =3D=3D sd->info->sync_interface) { if (sd->info->capabilities & (SS_CAP_BT_ENABLED | SS_CAP_BT_DEVICE_CONNE= CTED | - SS_CAP_INACTIVE_TIME)) + SS_CAP_INACTIVE_TIME | SS_CAP_BT_AUTO_ENABLE)) sysfs_remove_group(&hdev->dev.kobj, &steelseries_headset_attr_group); =20 --=20 2.53.0 From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qv1-f54.google.com (mail-qv1-f54.google.com [209.85.219.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 673A947AF51 for ; Fri, 27 Feb 2026 23:51:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236264; cv=none; b=HlWT6iJklupE8P66Hj+6C3Sk2Z8rf66nnc/CAhdHqOu9rsOWjsaDAeus0f+b78GpTg2IrM1lnjuQSSkpZnl6B+2hdjoJsi04acO/EkOrNGi7ACqpOjY921NTbAzGFwBijUJJut86hLyoRjmUDE9i7qTiGHbQv82ohLftrrHYf7U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236264; c=relaxed/simple; bh=mBZqah/z1wulloQTRI3OolpKj+WogIb7BoHQidJ5hGY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=SHPWPX3SC6lkPmaWo8BlQyU+7oQpQhnw3Jn6iS3P/m0AG2DwTO/+lW919+52khbm39qvTlYZIqZI6Slq0Ik7B+GusZvQ8vEIAV6XNCcVVcuYfIpbbRIeFL/XUSDG8q3QxS30iVkDS76zO8dB4ZjV/aieTVBic4wRPk2pJO9HyTA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=ZBfGT9ry; arc=none smtp.client-ip=209.85.219.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ZBfGT9ry" Received: by mail-qv1-f54.google.com with SMTP id 6a1803df08f44-899cbb1bea0so27576856d6.0 for ; Fri, 27 Feb 2026 15:51:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236259; x=1772841059; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=QV1Tk2OA0n0OwLrdEamwoemBkZmA63rjCMFz+5AR9F0=; b=ZBfGT9ryBWsAyw0n29OtjSdIDVYY17SyfPY1yAw3OiJRr3/IpDK+2u2KfZXWL0Z8jX b+fV49SE0yeKMfguAZHhGlEOy5eraAD3mKu9O0fObWZnp8X/SVSbT5C5dWuv47HpQbzf qjYzf0E4R3T9HshmZJLx1BkxtIWeyxplee5dHEUfLYvXKwCEi/yNHQzk2ZG1mqC/P+2f 3ROUblCwZlvleFdkFn8/HlkMN6Od93PvA6xqYmYcCNzNoPpKzeg28he5gdSrt7VGhQix ajWq22quM/9asq0EiIeE2daj0rxwFYvqLHo8Zz4nUtPe2RlMjAVItB539SJiJonjOkfE U23A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236259; x=1772841059; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=QV1Tk2OA0n0OwLrdEamwoemBkZmA63rjCMFz+5AR9F0=; b=czPbWFaJaAJNPKFeW0q15PuyQOK4OVa21hl1mwzk3dd6YJ6BpzIcU1jIzm8r/wAMzb wpJCURHFSWWeq36KJ+r3dfN0NtJcvHWgx1+KomuxrxZ5k/vymPWKv2dqCXZpszHCVsgF fbGCYA921saWbmq7ETEq8Mx7F+t0XDlEivDdxManq5TchJyYnjlsmeiCA5WQene8gvXl KhMMgCDI0+aQK/ej0/DPHDWuBO7Qd8GYJQfJvTYJfaGfRtFIi7INISociFDmVeGxEGhp B4VHwheKc0q8UZVwToAXCLJaptcaWPBxv6xRlt6Lngme7K4SkvHXBdgqzaTXu6oja8fs WfCA== X-Forwarded-Encrypted: i=1; AJvYcCXp/eSODa2Ya+aOSJSOw1wlrWcjEDUDUZd7/+rvlleNHrwHIaxRLmBqeJYAlrUw/1o4yq+icTz56QtcKoA=@vger.kernel.org X-Gm-Message-State: AOJu0Yz3YAVltrRq8bYT/aBcscW3Mhr4/tTq5QQJW9ifh8LvkwhABV6D b69vUq/5T18Ex/xa9+LuoluAWlc7bLEUVMAXkToPEN+9go+0fjaU+fOV X-Gm-Gg: ATEYQzwUhFJOdUMQJDh4rgZnejpDBgu+NH4i836Zrls5GYn/6Rhm1n0zKdTs7brR4r3 QvqbDueFYBIH4BBIS+wJsLzAeJhwPpkdrt7Mly2R+I5iNKKUgRKp41fxbOjpi0Wk5z2YL+ZGWnW XSKSSmByuecvir9imlvBdIbKjwnCn2pS8OKxmK7XTtX8+ARcqqreDcMC/nQkdjlQ5zjA2POijiL AwmnHOvaihv5h03ugtom33kCO5HU2DnhlF2ga+kpC3ecInuvnED4NgrnyfPwthobghEcAuH8QYf Qkjx5SYvkdlg/PUMyrsr01bsJHLQ4zWvf3YnQzojzbpZvSp+NoiV0TxD8n8aYg6iGUU8s5bPgMg DOdlMAf7tSKvLEPEsmMSLefredVb6C4oREcxcyv01lhjQq3dW6Qc3MLvKP/sT8nO+EhXSdHPGQC XtsNEEAfdyS9y68PGT4jc/J+vueTDd7xk2H0n1op7lJG5l3KWTzA== X-Received: by 2002:ad4:5fc5:0:b0:899:bda9:c711 with SMTP id 6a1803df08f44-899d1e3b403mr67063556d6.39.1772236259207; Fri, 27 Feb 2026 15:50:59 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:58 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 17/18] HID: steelseries: Add mic mute LED brightness control Date: Fri, 27 Feb 2026 18:50:41 -0500 Message-ID: <20260227235042.410062-18-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Register the microphone mute LED as an LED class device named "::micmute", following the standard LED naming convention. The brightness range is 0-3 representing off, low, medium, and high. On the Arctis Nova 5 family, the discrete levels map to non-linear hardware values expected by the firmware (0, 1, 4, 10). The Nova 7 family uses a direct linear mapping. On the Nova 7 Gen2, the current brightness is recovered from the 0xa0 settings poll response. Registration is guarded by a LEDS_CLASS module compatibility check analogous to the existing SND guard. Signed-off-by: Sriman Achanta --- drivers/hid/hid-steelseries.c | 119 ++++++++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 5 deletions(-) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index a794af01e15a..dcd34c61cccd 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -33,6 +33,7 @@ #define SS_CAP_BT_CALL_DUCKING BIT(9) #define SS_CAP_INACTIVE_TIME BIT(10) #define SS_CAP_BT_AUTO_ENABLE BIT(11) +#define SS_CAP_MIC_MUTE_BRIGHTNESS BIT(12) =20 #define SS_QUIRK_STATUS_SYNC_POLL BIT(0) =20 @@ -42,6 +43,7 @@ #define SS_SETTING_BT_CALL_DUCKING 3 #define SS_SETTING_INACTIVE_TIME 4 #define SS_SETTING_BT_AUTO_ENABLE 5 +#define SS_SETTING_MIC_MUTE_BRIGHTNESS 6 =20 struct steelseries_device; =20 @@ -100,6 +102,12 @@ struct steelseries_device { bool bt_device_connected; u8 inactive_timeout; bool bt_auto_enable; + u8 mic_mute_brightness; + +#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \ + (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES)) + struct led_classdev *mic_mute_led; +#endif =20 spinlock_t lock; bool removed; @@ -606,6 +614,14 @@ static int steelseries_arctis_nova_5_write_setting(str= uct hid_device *hdev, case SS_SETTING_INACTIVE_TIME: cmd =3D 0xa3; break; + case SS_SETTING_MIC_MUTE_BRIGHTNESS: + cmd =3D 0xae; + /* Hardware uses non-linear values: 0=3Doff, 1=3Dlow, 4=3Dmedium, 10=3Dh= igh */ + if (value =3D=3D 2) + value =3D 0x04; + else if (value =3D=3D 3) + value =3D 0x0a; + break; default: return -EINVAL; } @@ -650,6 +666,9 @@ static int steelseries_arctis_nova_7_write_setting(stru= ct hid_device *hdev, case SS_SETTING_BT_AUTO_ENABLE: cmd =3D 0xb2; break; + case SS_SETTING_MIC_MUTE_BRIGHTNESS: + cmd =3D 0xae; + break; default: return -EINVAL; } @@ -1005,6 +1024,7 @@ static void steelseries_arctis_nova_7_gen2_parse_sett= ings( break; case 0xa0: sd->inactive_timeout =3D data[1]; + sd->mic_mute_brightness =3D data[2]; sd->bt_auto_enable =3D data[3]; sd->bt_call_ducking =3D data[4]; break; @@ -1020,6 +1040,9 @@ static void steelseries_arctis_nova_7_gen2_parse_sett= ings( case 0xa3: sd->inactive_timeout =3D data[1]; break; + case 0xae: + sd->mic_mute_brightness =3D data[1]; + break; case 0xb2: sd->bt_auto_enable =3D data[1]; break; @@ -1129,7 +1152,8 @@ static const struct steelseries_device_info arctis_no= va_3p_info =3D { static const struct steelseries_device_info arctis_nova_5_info =3D { .sync_interface =3D 3, .capabilities =3D SS_CAP_BATTERY | SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME | - SS_CAP_VOLUME_LIMITER | SS_CAP_INACTIVE_TIME, + SS_CAP_VOLUME_LIMITER | SS_CAP_INACTIVE_TIME | + SS_CAP_MIC_MUTE_BRIGHTNESS, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 10, .mic_volume_max =3D 15, @@ -1143,7 +1167,7 @@ static const struct steelseries_device_info arctis_no= va_5x_info =3D { .sync_interface =3D 3, .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER | - SS_CAP_INACTIVE_TIME, + SS_CAP_INACTIVE_TIME | SS_CAP_MIC_MUTE_BRIGHTNESS, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 10, .mic_volume_max =3D 15, @@ -1158,7 +1182,7 @@ static const struct steelseries_device_info arctis_no= va_7_info =3D { .capabilities =3D SS_CAP_BATTERY | SS_CAP_CHATMIX | SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER | SS_CAP_BT_CALL_DUCKING | SS_CAP_INACTIVE_TIME | - SS_CAP_BT_AUTO_ENABLE, + SS_CAP_BT_AUTO_ENABLE | SS_CAP_MIC_MUTE_BRIGHTNESS, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .sidetone_max =3D 3, .mic_volume_max =3D 7, @@ -1172,7 +1196,7 @@ static const struct steelseries_device_info arctis_no= va_7p_info =3D { .sync_interface =3D 3, .capabilities =3D SS_CAP_BATTERY | SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMI= TER | SS_CAP_BT_CALL_DUCKING | SS_CAP_INACTIVE_TIME | - SS_CAP_BT_AUTO_ENABLE, + SS_CAP_BT_AUTO_ENABLE | SS_CAP_MIC_MUTE_BRIGHTNESS, .quirks =3D SS_QUIRK_STATUS_SYNC_POLL, .mic_volume_max =3D 7, .inactive_time_max =3D 255, @@ -1189,7 +1213,7 @@ static const struct steelseries_device_info arctis_no= va_7_gen2_info =3D { SS_CAP_EXTERNAL_CONFIG | SS_CAP_SIDETONE | SS_CAP_MIC_VOLUME | SS_CAP_VOLUME_LIMITER | SS_CAP_BT_CALL_DUCKING | SS_CAP_INACTIVE_TIME | - SS_CAP_BT_AUTO_ENABLE, + SS_CAP_BT_AUTO_ENABLE | SS_CAP_MIC_MUTE_BRIGHTNESS, .sidetone_max =3D 3, .mic_volume_max =3D 7, .inactive_time_max =3D 255, @@ -1970,6 +1994,82 @@ static void steelseries_snd_unregister(struct steels= eries_device *sd) =20 #endif =20 +/* + * Mic mute LED + */ + +#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \ + (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES)) + +#define SS_MIC_MUTE_BRIGHTNESS_MAX 3 + +static int steelseries_mic_mute_led_brightness_set(struct led_classdev *le= d_cdev, + enum led_brightness brightness) +{ + struct device *dev =3D led_cdev->dev->parent; + struct hid_device *hdev =3D to_hid_device(dev); + struct steelseries_device *sd =3D hid_get_drvdata(hdev); + unsigned long flags; + int ret; + + if (brightness > SS_MIC_MUTE_BRIGHTNESS_MAX) + brightness =3D SS_MIC_MUTE_BRIGHTNESS_MAX; + + spin_lock_irqsave(&sd->lock, flags); + if (sd->removed) { + spin_unlock_irqrestore(&sd->lock, flags); + return 0; + } + spin_unlock_irqrestore(&sd->lock, flags); + + ret =3D sd->info->write_setting(sd->hdev, SS_SETTING_MIC_MUTE_BRIGHTNESS, + brightness); + if (ret) + return ret; + + sd->mic_mute_brightness =3D brightness; + + return 0; +} + +static enum led_brightness +steelseries_mic_mute_led_brightness_get(struct led_classdev *led_cdev) +{ + struct device *dev =3D led_cdev->dev->parent; + struct hid_device *hdev =3D to_hid_device(dev); + struct steelseries_device *sd =3D hid_get_drvdata(hdev); + + return sd->mic_mute_brightness; +} + +static int steelseries_mic_mute_led_register(struct steelseries_device *sd) +{ + struct hid_device *hdev =3D sd->hdev; + struct led_classdev *led; + size_t name_size; + char *name; + + name_size =3D strlen(dev_name(&hdev->dev)) + 16; + + led =3D devm_kzalloc(&hdev->dev, sizeof(*led) + name_size, GFP_KERNEL); + if (!led) + return -ENOMEM; + + name =3D (void *)(&led[1]); + snprintf(name, name_size, "%s::micmute", dev_name(&hdev->dev)); + led->name =3D name; + led->brightness =3D 0; + led->max_brightness =3D SS_MIC_MUTE_BRIGHTNESS_MAX; + led->brightness_get =3D steelseries_mic_mute_led_brightness_get; + led->brightness_set_blocking =3D steelseries_mic_mute_led_brightness_set; + + sd->mic_mute_led =3D led; + + return devm_led_classdev_register(&hdev->dev, led); +} + +#endif + static int steelseries_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { @@ -2175,6 +2275,15 @@ static int steelseries_probe(struct hid_device *hdev, hid_warn(hdev, "Failed to register sound card: %d\n", ret); #endif =20 +#if IS_BUILTIN(CONFIG_LEDS_CLASS) || \ + (IS_MODULE(CONFIG_LEDS_CLASS) && IS_MODULE(CONFIG_HID_STEELSERIES)) + if (info->capabilities & SS_CAP_MIC_MUTE_BRIGHTNESS) { + ret =3D steelseries_mic_mute_led_register(sd); + if (ret < 0) + hid_warn(hdev, "Failed to register mic mute LED: %d\n", ret); + } +#endif + INIT_DELAYED_WORK(&sd->status_work, steelseries_status_timer_work_handle= r); INIT_DELAYED_WORK(&sd->settings_work, steelseries_settings_work_handler); =20 --=20 2.53.0 From nobody Sat Apr 18 11:08:47 2026 Received: from mail-qv1-f53.google.com (mail-qv1-f53.google.com [209.85.219.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2FC4547B400 for ; Fri, 27 Feb 2026 23:51:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236264; cv=none; b=GSABxtm+NPlB2VM+S7fpawQUzkAIvLYHXRKGnM0FMuWpo/uz916dhbjdBpM5P273wdjx7dHWyy7n0S/Fl6BfXL9OZnibTiseFTTqgNrAOAXzBVHj4S6HmFVSTV3rXUc9xyrzLma903BnfmQ79oBumAFnr95xPFJbQs0J9AIH8HE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772236264; c=relaxed/simple; bh=eGkRrkmCJtnCk/bFLLTyry3WFhETRAcalD+Y6Caor/s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nnshNLVRV/v/AM4/aN0YjrCfIwnQPff/hRRXhEnjm5VrL7d8eNmI9T7QOV6sIBl/atFoTstRj2qpa72B4sMsXlI6GdGvqip7pVc4oS3vDnLD7Sw0KOuO6nnNJ5vz0nf+8uSENOsNTZEDGPSm6g/NBQwjqc+j2gVSHo6QkB9m7WY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=GVNwtG6X; arc=none smtp.client-ip=209.85.219.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GVNwtG6X" Received: by mail-qv1-f53.google.com with SMTP id 6a1803df08f44-899d6b7b073so11989286d6.2 for ; Fri, 27 Feb 2026 15:51:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772236260; x=1772841060; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=5uoTaHRHQOOyyAXgJyEhLyvh97GNJstHquvGuNdc7XI=; b=GVNwtG6XJTyLJThPIory/UEk/nAGhjXoEfF0Lu7J+mfQpZnWhKwSeydki2gy/5sq08 ykr008ZvfP31pgdj8gmy5kxXhMFwLqN7pv1QKGQLHZNrqc56oDHebVGIayI9dZ+PApMF kQ/WHykqbrhD5gyAdCYa2RBoPZWxeqCvVpM/9wVz6lvwe9fA+XgSO2PuLLqOlwhZM3H+ /6XAi4ZwcHsnlmHFMv9/Ulzahe3DOANLAi7WqYkcd+YZ3GXIxTMoAluFVKt1S4k2qJ4C zaEp+RIPeSQnQ+6Tf0LrDMSjCBmvdSKCOXL74kpMOoItVJMcWQ3+JljY0lYd0KbBbJ/Z SuIg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772236260; x=1772841060; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=5uoTaHRHQOOyyAXgJyEhLyvh97GNJstHquvGuNdc7XI=; b=YHm46miaEHOKaUCpBKFlh53WKFrqbLwp3da6GQxV6e8tb8sXcPbDafBU4JKI8re/Wg 92/4MYavRxkbza0pXOJL/K0uCe+Rl4jFe4oqtxmuGRgDrsUGATev7+o6JgZ5JbNVRMaF iogaa3xrYqoxCxQElmD+NGBPXjtQcewIZBIKojVLrOkn8lFaRVJTxocnGlmW5BjneZ/C ZADJs1h+45Kuwayu4R6Y5pZP/mIxU3sCIaVOO7VWB0tw71GqRv0y8t0m+NEJ8cSWgwI6 ZlM57yX2XuuTh7Ps4S+KwN85Sk+7secML5MsQBUgiZTce/qMfQpkw49FDcK+0MIwKiDQ K5XQ== X-Forwarded-Encrypted: i=1; AJvYcCVdTiwTYY46nx3mqfbhtQFp3pjWd7j4xzzwIsCZC2OhH4EXiEqUJsIHdMWBd7/0+Ym7sVmg13A/LmOv1M0=@vger.kernel.org X-Gm-Message-State: AOJu0Yy3xhGG9uAF8wjv4y+WjO4Vtg9307eldj0L6LiuLmqVZo3Pa4xP t97sP22OL8vQdVND0theHJjPLTNhlzuTv26bzgn1qS/+VV66VnOCpf3WNHb6SQ== X-Gm-Gg: ATEYQzx4CeW0raDscSc97BNg49IM5oPqjA8kh7zo7Z0u7LtQJPnNddevRxc7FS1HvL5 v6xt0tnBuFsZmVOOvg7Uj7qmHYa1zqQh5s7WicDWAc4nyjtYwRwsfITC0VMaD87HZr+jLG3kWEG mhnZO99R3RAsDwYwcMauBLxJhxpz7bgo0UYio6n0P87RU4ynNbQvDZT9P+mm4mUOTRXfgwv/kZ2 mgjMfz/EcTruz23FKI/JTBdxgV10XKfkYzsb7IFWQYBFSzOzP1go16bYdMwMyuneVKxvOspCo6V bbI407vgWTCAdO0w6vWWhxxIBkYPBovgO3gW8R7Q3pw2/VBB/jj2PN2Ik7DNR7Ai7RY5LcIG4MZ p/jGYQQj5X6ZGeykHroV3EWBzh31A44tlfeRYuGb/IT/4oXbMXdR3A7OQvToYub2I+4r7tcAsgz zkfRpJLNrQBv5oZTdEW5Qpl2Jhg6o2QIxKyJ2rFcJ0zQJRHuT1Qw== X-Received: by 2002:a05:6214:da6:b0:894:6ff2:191c with SMTP id 6a1803df08f44-899d1e2b732mr71580246d6.33.1772236260060; Fri, 27 Feb 2026 15:51:00 -0800 (PST) Received: from achantapc.tail227c81.ts.net ([128.172.224.28]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-899c715a87esm52397446d6.4.2026.02.27.15.50.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 27 Feb 2026 15:50:59 -0800 (PST) From: Sriman Achanta To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Bastien Nocera , Simon Wood , Christian Mayer , Sriman Achanta Subject: [PATCH v3 18/18] HID: steelseries: Document sysfs ABI Date: Fri, 27 Feb 2026 18:50:42 -0500 Message-ID: <20260227235042.410062-19-srimanachanta@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260227235042.410062-1-srimanachanta@gmail.com> References: <20260227235042.410062-1-srimanachanta@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add Documentation/ABI/testing/sysfs-driver-hid-steelseries documenting the sysfs attributes and LED class device exposed by the driver: - bt_enabled, bt_device_connected: read-only Bluetooth radio state - inactive_time: read/write auto-shutoff timer in minutes - bt_auto_enable: read/write Bluetooth radio power-on behavior - ::micmute/brightness: mic mute LED brightness via LED class Signed-off-by: Sriman Achanta --- .../ABI/testing/sysfs-driver-hid-steelseries | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-hid-steelseries diff --git a/Documentation/ABI/testing/sysfs-driver-hid-steelseries b/Docum= entation/ABI/testing/sysfs-driver-hid-steelseries new file mode 100644 index 000000000000..7b8d29282ed6 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-steelseries @@ -0,0 +1,87 @@ +What: /sys/bus/hid/drivers/steelseries//bt_enabled +Date: February 2026 +KernelVersion: 6.20 +Contact: Sriman Achanta +Description: (RO) Whether the Bluetooth radio on the headset is currently + enabled. + + * 0 =3D Bluetooth radio off + * 1 =3D Bluetooth radio on + + Returns -ENODEV if the headset is not connected to the + receiver. + + Supported on: Arctis Nova 7 Gen2, Arctis Nova Pro Wireless + +What: /sys/bus/hid/drivers/steelseries//bt_device_connected +Date: February 2026 +KernelVersion: 6.20 +Contact: Sriman Achanta +Description: (RO) Whether a Bluetooth device is currently connected to + the headset. + + * 0 =3D no Bluetooth device connected + * 1 =3D Bluetooth device connected + + Returns -ENODEV if the headset is not connected to the + receiver. + + Supported on: Arctis Nova 7 Gen2, Arctis Nova Pro Wireless + +What: /sys/bus/hid/drivers/steelseries//inactive_time +Date: February 2026 +KernelVersion: 6.20 +Contact: Sriman Achanta +Description: (RW) Auto-shutoff timer for the headset, in minutes. A + value of 0 disables the timer. The maximum accepted value + is device-specific. + + The encoding sent to the firmware varies by device family: + the Arctis 9 converts the value to seconds, the Nova 3P + rounds down to its nearest supported discrete step, and the + Nova Pro maps to six firmware-defined level indices. For all + other devices the value is sent in minutes directly. + + Reading the attribute returns the last value reported by the + firmware. Writing immediately sends the new timeout to the + device. + + Returns -ENODEV if the headset is not connected to the + receiver. + + Supported on: Arctis 1 Wireless, Arctis 7, Arctis 7+, + Arctis 9, Arctis Nova 3P, Arctis Nova 5, Arctis Nova 5X, + Arctis Nova 7, Arctis Nova 7P, Arctis Nova 7 Gen2, + Arctis Nova Pro Wireless + +What: /sys/bus/hid/drivers/steelseries//bt_auto_enable +Date: February 2026 +KernelVersion: 6.20 +Contact: Sriman Achanta +Description: (RW) Whether the headset automatically enables its + Bluetooth radio on power-on. + + * 0 =3D Bluetooth radio stays off at power-on + * 1 =3D Bluetooth radio activates automatically at power-on + + Returns -ENODEV if the headset is not connected to the + receiver. + + Supported on: Arctis Nova 7, Arctis Nova 7P, + Arctis Nova 7 Gen2 + +What: /sys/class/leds/::micmute/brightness +Date: February 2026 +KernelVersion: 6.20 +Contact: Sriman Achanta +Description: (RW) Brightness of the microphone mute status LED. + is the HID device node name (e.g. + 0003:1038:12AE.0001). + + * 0 =3D off + * 1 =3D low + * 2 =3D medium + * 3 =3D high + + Supported on: Arctis Nova 5, Arctis Nova 5X, Arctis Nova 7, + Arctis Nova 7P, Arctis Nova 7 Gen2 --=20 2.53.0