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::{IntoPrimitive, TryFromPrimitive};
7
8use crate::{
9    channel::HidppChannel,
10    event::EventEmitter,
11    feature::{CreatableFeature, EmittingFeature, Feature, event_payload},
12};
13
14/// Implements the `WirelessDeviceStatus` / `0x1d4b` feature.
15pub struct WirelessDeviceStatusFeature {
16    /// The underlying HID++ channel.
17    chan: Arc<HidppChannel>,
18
19    /// The emitter used to emit events.
20    emitter: Arc<EventEmitter<WirelessDeviceStatusEvent>>,
21
22    /// The handle assigned to the message listener registered via
23    /// [`HidppChannel::add_msg_listener`].
24    /// This is used to remove the listener when the feature is dropped.
25    msg_listener_hdl: u32,
26}
27
28impl CreatableFeature for WirelessDeviceStatusFeature {
29    const ID: u16 = 0x1d4b;
30    const STARTING_VERSION: u8 = 0;
31
32    fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
33        let emitter = Arc::new(EventEmitter::new());
34
35        let hdl = chan.add_msg_listener({
36            let emitter = Arc::clone(&emitter);
37
38            move |raw, matched| {
39                let Some((func, payload)) =
40                    event_payload(raw, matched, device_index, feature_index)
41                else {
42                    return;
43                };
44                // The reconnection broadcast is the only event and carries sub-id 0.
45                if func.to_lo() != 0 {
46                    return;
47                }
48
49                let (Ok(status), Ok(request), Ok(reason)) = (
50                    WirelessDeviceStatus::try_from(payload[0]),
51                    WirelessDeviceStatusRequest::try_from(payload[1]),
52                    WirelessDeviceStatusReason::try_from(payload[2]),
53                ) else {
54                    return;
55                };
56
57                emitter.emit(WirelessDeviceStatusEvent::StatusBroadcast(
58                    WirelessDeviceStatusBroadcast {
59                        status,
60                        request,
61                        reason,
62                    },
63                ));
64            }
65        });
66
67        Self {
68            chan,
69            emitter,
70            msg_listener_hdl: hdl,
71        }
72    }
73}
74
75impl Feature for WirelessDeviceStatusFeature {}
76
77impl EmittingFeature<WirelessDeviceStatusEvent> for WirelessDeviceStatusFeature {
78    fn listen(&self) -> async_channel::Receiver<WirelessDeviceStatusEvent> {
79        self.emitter.create_receiver()
80    }
81}
82
83impl Drop for WirelessDeviceStatusFeature {
84    fn drop(&mut self) {
85        self.chan.remove_msg_listener(self.msg_listener_hdl);
86    }
87}
88
89/// Represents an event emitted by the [`WirelessDeviceStatusFeature`]
90/// feature.
91#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
92#[cfg_attr(feature = "serde", derive(serde::Serialize))]
93#[non_exhaustive]
94pub enum WirelessDeviceStatusEvent {
95    /// Is emitted whenever a device (re)connects to the host.
96    ///
97    /// This event is always enabled.
98    StatusBroadcast(WirelessDeviceStatusBroadcast),
99}
100
101/// Represents the data of the [`WirelessDeviceStatusEvent::StatusBroadcast`]
102/// event.
103#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
104#[cfg_attr(feature = "serde", derive(serde::Serialize))]
105#[non_exhaustive]
106pub struct WirelessDeviceStatusBroadcast {
107    /// The status the device reports to be in.
108    pub status: WirelessDeviceStatus,
109
110    /// The request the devices expresses towards the host.
111    pub request: WirelessDeviceStatusRequest,
112
113    /// The reason for the status broadcast.
114    pub reason: WirelessDeviceStatusReason,
115}
116
117/// Represents a device status as reported in
118/// [`WirelessDeviceStatusBroadcast::status`].
119#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
120#[cfg_attr(feature = "serde", derive(serde::Serialize))]
121#[non_exhaustive]
122#[repr(u8)]
123pub enum WirelessDeviceStatus {
124    Unknown = 0x00,
125    Reconnection = 0x01,
126}
127
128/// Represents a request as reported in
129/// [`WirelessDeviceStatusBroadcast::request`].
130#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
131#[cfg_attr(feature = "serde", derive(serde::Serialize))]
132#[non_exhaustive]
133#[repr(u8)]
134pub enum WirelessDeviceStatusRequest {
135    NoRequest = 0x00,
136    SoftwareReconfigurationNeeded = 0x01,
137}
138
139/// Represents a broadcast reason as reported in
140/// [`WirelessDeviceStatusBroadcast::reason`].
141#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
142#[cfg_attr(feature = "serde", derive(serde::Serialize))]
143#[non_exhaustive]
144#[repr(u8)]
145pub enum WirelessDeviceStatusReason {
146    Unknown = 0x00,
147    PowerSwitchActivated = 0x01,
148}