Skip to main content

hidpp/feature/wireless_device_status/
mod.rs

1//! Implements the `WirelessDeviceStatus` feature (ID `0x1d4b`) that notifies
2//! the host about device reconnections.
3
4use std::sync::Arc;
5
6use num_enum::{FromPrimitive, IntoPrimitive};
7
8use crate::{
9    channel::{HidppChannel, MessageListenerGuard},
10    event::EventEmitter,
11    feature::{CreatableFeature, EmittingFeature, Feature, event_payload},
12};
13
14/// Implements the `WirelessDeviceStatus` / `0x1d4b` feature.
15pub struct WirelessDeviceStatusFeature {
16    /// The emitter used to emit events.
17    emitter: Arc<EventEmitter<WirelessDeviceStatusEvent>>,
18
19    /// Removes the message listener when the feature is dropped.
20    _msg_listener: MessageListenerGuard,
21}
22
23impl CreatableFeature for WirelessDeviceStatusFeature {
24    const ID: u16 = 0x1d4b;
25    const STARTING_VERSION: u8 = 0;
26
27    fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
28        let emitter = Arc::new(EventEmitter::new());
29
30        let listener = chan.add_msg_listener_guarded({
31            let emitter = Arc::clone(&emitter);
32
33            move |raw, matched| {
34                let Some((func, payload)) =
35                    event_payload(raw, matched, device_index, feature_index)
36                else {
37                    return;
38                };
39                // The reconnection broadcast is the only event and carries sub-id 0.
40                if func.to_lo() != 0 {
41                    return;
42                }
43
44                // This broadcast is the device's (re)connection signal; an
45                // unrecognised field value must not swallow it, so every
46                // field decodes infallibly and carries unknown raw bytes.
47                emitter.emit(WirelessDeviceStatusEvent::StatusBroadcast(
48                    WirelessDeviceStatusBroadcast {
49                        status: WirelessDeviceStatus::from(payload[0]),
50                        request: WirelessDeviceStatusRequest::from(payload[1]),
51                        reason: WirelessDeviceStatusReason::from(payload[2]),
52                    },
53                ));
54            }
55        });
56
57        Self {
58            emitter,
59            _msg_listener: listener,
60        }
61    }
62}
63
64impl Feature for WirelessDeviceStatusFeature {}
65
66impl EmittingFeature<WirelessDeviceStatusEvent> for WirelessDeviceStatusFeature {
67    fn listen(&self) -> async_channel::Receiver<WirelessDeviceStatusEvent> {
68        self.emitter.create_receiver()
69    }
70}
71
72/// Represents an event emitted by the [`WirelessDeviceStatusFeature`]
73/// feature.
74#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
75#[cfg_attr(feature = "serde", derive(serde::Serialize))]
76#[non_exhaustive]
77pub enum WirelessDeviceStatusEvent {
78    /// Is emitted whenever a device (re)connects to the host.
79    ///
80    /// This event is always enabled.
81    StatusBroadcast(WirelessDeviceStatusBroadcast),
82}
83
84/// Represents the data of the [`WirelessDeviceStatusEvent::StatusBroadcast`]
85/// event.
86#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
87#[cfg_attr(feature = "serde", derive(serde::Serialize))]
88#[non_exhaustive]
89pub struct WirelessDeviceStatusBroadcast {
90    /// The status the device reports to be in.
91    pub status: WirelessDeviceStatus,
92
93    /// The request the devices expresses towards the host.
94    pub request: WirelessDeviceStatusRequest,
95
96    /// The reason for the status broadcast.
97    pub reason: WirelessDeviceStatusReason,
98}
99
100/// Represents a device status as reported in
101/// [`WirelessDeviceStatusBroadcast::status`].
102#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, FromPrimitive)]
103#[cfg_attr(feature = "serde", derive(serde::Serialize))]
104#[non_exhaustive]
105#[repr(u8)]
106pub enum WirelessDeviceStatus {
107    /// Unknown wireless device status.
108    Unknown = 0x00,
109    /// Device is reconnecting.
110    Reconnection = 0x01,
111    /// A status value this crate does not model; carries the raw byte.
112    #[num_enum(catch_all)]
113    Other(u8),
114}
115
116/// Represents a request as reported in
117/// [`WirelessDeviceStatusBroadcast::request`].
118#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, FromPrimitive)]
119#[cfg_attr(feature = "serde", derive(serde::Serialize))]
120#[non_exhaustive]
121#[repr(u8)]
122pub enum WirelessDeviceStatusRequest {
123    /// No host action requested.
124    NoRequest = 0x00,
125    /// Host software must reconfigure the device.
126    SoftwareReconfigurationNeeded = 0x01,
127    /// A request value this crate does not model; carries the raw byte.
128    #[num_enum(catch_all)]
129    Other(u8),
130}
131
132/// Represents a broadcast reason as reported in
133/// [`WirelessDeviceStatusBroadcast::reason`].
134#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, FromPrimitive)]
135#[cfg_attr(feature = "serde", derive(serde::Serialize))]
136#[non_exhaustive]
137#[repr(u8)]
138pub enum WirelessDeviceStatusReason {
139    /// Unknown broadcast reason.
140    Unknown = 0x00,
141    /// Broadcast was caused by the device power switch.
142    PowerSwitchActivated = 0x01,
143    /// A reason value this crate does not model; carries the raw byte.
144    #[num_enum(catch_all)]
145    Other(u8),
146}