openlogi_device/channel.rs
1//! HID++ transport and channel lifecycle.
2//!
3//! Resolving a [`route::DeviceRoute`] to an open channel, and the strategies
4//! that keep one open: [`ChannelPool`] for sessions that open on demand,
5//! [`ChannelRegistry`] for channels owned by the inventory enumerator, and
6//! [`SharedChannel`] handles lent out to this crate's read/write entry points.
7//!
8//! Opening itself belongs to a [`crate::backend::HidBackend`]; nothing here
9//! names a HID stack.
10
11use std::sync::Arc;
12
13use hidpp::channel::HidppChannel;
14
15use route::DeviceRoute;
16
17pub(crate) mod pool;
18pub(crate) mod registry;
19pub(crate) mod route;
20#[cfg(test)]
21pub(crate) mod scripted;
22
23pub use pool::ChannelPool;
24pub use registry::ChannelRegistry;
25
26/// An open HID++ channel to a device, shared so route-addressed reads and writes
27/// can reuse an inventory- or capture-owned connection instead of
28/// re-enumerating and opening a fresh channel each time (which costs ~100ms+).
29///
30/// Cheap to clone (an `Arc` plus the [`DeviceRoute`] it points at). Built by
31/// the inventory registry or a standalone capture session.
32#[derive(Clone)]
33pub struct SharedChannel {
34 channel: Arc<HidppChannel>,
35 route: DeviceRoute,
36}
37
38impl SharedChannel {
39 /// Wrap an open channel that reaches `route`.
40 #[must_use]
41 pub(crate) fn new(channel: Arc<HidppChannel>, route: DeviceRoute) -> Self {
42 Self { channel, route }
43 }
44
45 /// Whether this channel reaches `route` — so the write path only reuses it
46 /// for the device it actually points at.
47 #[must_use]
48 pub fn matches(&self, route: &DeviceRoute) -> bool {
49 self.route == *route
50 }
51
52 pub(crate) fn channel(&self) -> &Arc<HidppChannel> {
53 &self.channel
54 }
55
56 pub(crate) fn device_index(&self) -> u8 {
57 self.route.device_index()
58 }
59}