Skip to main content

openlogi_device_registry/
litra.rs

1//! Logitech Litra raw-HID identities.
2
3use crate::LOGITECH_VENDOR_ID;
4
5/// Stable driver-family identifier carried by standalone Litra inventory.
6pub const LITRA_DRIVER_ID: &str = "litra";
7/// Litra Glow product ID.
8pub const LITRA_GLOW_PRODUCT_ID: u16 = 0xc900;
9/// Litra Beam product ID.
10pub const LITRA_BEAM_PRODUCT_ID: u16 = 0xc901;
11/// Litra vendor usage page.
12pub const LITRA_USAGE_PAGE: u16 = 0xff43;
13/// Litra vendor usage ID.
14pub const LITRA_USAGE_ID: u16 = 0x0202;
15
16/// A supported Litra product model.
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum LitraModel {
19    /// Logitech Litra Glow.
20    Glow,
21    /// Logitech Litra Beam.
22    Beam,
23}
24
25/// A known Litra raw-HID identity and its static product metadata.
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct LitraDescriptor {
28    /// USB vendor ID.
29    pub vendor_id: u16,
30    /// USB product ID.
31    pub product_id: u16,
32    /// HID usage page of the writable vendor collection.
33    pub usage_page: u16,
34    /// HID usage ID of the writable vendor collection.
35    pub usage_id: u16,
36    /// Product model selected by this identity.
37    pub model: LitraModel,
38    /// Stable standalone-driver family identifier.
39    pub driver_id: &'static str,
40    /// Exact model identifier used by the OpenLogi asset registry.
41    pub registry_model_id: &'static str,
42}
43
44impl LitraDescriptor {
45    const fn logitech(product_id: u16, model: LitraModel, registry_model_id: &'static str) -> Self {
46        Self {
47            vendor_id: LOGITECH_VENDOR_ID,
48            product_id,
49            usage_page: LITRA_USAGE_PAGE,
50            usage_id: LITRA_USAGE_ID,
51            model,
52            driver_id: LITRA_DRIVER_ID,
53            registry_model_id,
54        }
55    }
56}
57
58/// All standalone Litra raw-HID identities supported by OpenLogi.
59pub const LITRA_DEVICES: &[LitraDescriptor] = &[
60    LitraDescriptor::logitech(LITRA_GLOW_PRODUCT_ID, LitraModel::Glow, "8c900"),
61    LitraDescriptor::logitech(LITRA_BEAM_PRODUCT_ID, LitraModel::Beam, "8c901"),
62];
63
64/// Finds a Litra descriptor by its complete writable raw-HID identity.
65#[must_use]
66pub fn find_litra(
67    vendor_id: u16,
68    product_id: u16,
69    usage_page: u16,
70    usage_id: u16,
71) -> Option<&'static LitraDescriptor> {
72    LITRA_DEVICES.iter().find(|device| {
73        device.vendor_id == vendor_id
74            && device.product_id == product_id
75            && device.usage_page == usage_page
76            && device.usage_id == usage_id
77    })
78}
79
80/// Whether an HID descriptor identifies a supported Litra interface.
81#[must_use]
82pub fn matches_litra(vendor_id: u16, product_id: u16, usage_page: u16, usage_id: u16) -> bool {
83    find_litra(vendor_id, product_id, usage_page, usage_id).is_some()
84}
85
86#[cfg(test)]
87mod tests {
88    use super::{LITRA_DEVICES, LITRA_DRIVER_ID, LitraModel, find_litra};
89    use crate::LOGITECH_VENDOR_ID;
90
91    #[test]
92    fn litra_identities_are_unique() {
93        for (index, device) in LITRA_DEVICES.iter().enumerate() {
94            assert!(
95                LITRA_DEVICES[..index].iter().all(|other| {
96                    (
97                        other.vendor_id,
98                        other.product_id,
99                        other.usage_page,
100                        other.usage_id,
101                    ) != (
102                        device.vendor_id,
103                        device.product_id,
104                        device.usage_page,
105                        device.usage_id,
106                    )
107                }),
108                "duplicate Litra identity {:04x}:{:04x}/{:04x}:{:04x}",
109                device.vendor_id,
110                device.product_id,
111                device.usage_page,
112                device.usage_id
113            );
114        }
115    }
116
117    #[test]
118    fn complete_identity_selects_glow_metadata() {
119        let glow =
120            find_litra(LOGITECH_VENDOR_ID, 0xc900, 0xff43, 0x0202).expect("Litra Glow identity");
121
122        assert_eq!(glow.model, LitraModel::Glow);
123        assert_eq!(glow.driver_id, LITRA_DRIVER_ID);
124        assert_eq!(glow.registry_model_id, "8c900");
125        assert!(find_litra(LOGITECH_VENDOR_ID, 0xc900, 0xff43, 0x0203).is_none());
126    }
127
128    #[test]
129    fn complete_identity_selects_beam_metadata() {
130        let beam =
131            find_litra(LOGITECH_VENDOR_ID, 0xc901, 0xff43, 0x0202).expect("Litra Beam identity");
132
133        assert_eq!(beam.model, LitraModel::Beam);
134        assert_eq!(beam.registry_model_id, "8c901");
135    }
136}