1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//! Implements the `ChangeHost` feature (ID `0x1814`) that selects which host /
//! RF channel a multi-host device is connected to.
use openlogi_hidpp_derive::Feature;
use crate::{feature::FeatureEndpoint, protocol::v20::Hidpp20Error};
bitflags::bitflags! {
/// Host-switching capabilities reported by [`ChangeHostFeature::get_host_info`].
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ChangeHostCapabilities: u8 {
/// Enhanced host switching is enabled: on a failed connection the device
/// falls back to another host with a non-zero cookie before returning to
/// the original host.
const ENHANCED_HOST_SWITCH = 1 << 0;
}
}
/// Host configuration returned by [`ChangeHostFeature::get_host_info`].
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct ChangeHostInfo {
/// Number of hosts / RF channels.
pub host_count: u8,
/// Current host index, in `0..host_count`.
pub current_host: u8,
/// Host-switching capabilities.
pub capabilities: ChangeHostCapabilities,
}
/// Implements the `ChangeHost` / `0x1814` feature.
#[derive(Clone, Feature)]
#[creatable(id = 0x1814, version = 0)]
pub struct ChangeHostFeature {
/// The endpoint this feature talks to.
endpoint: FeatureEndpoint,
}
impl ChangeHostFeature {
/// Retrieves the host count, current host and host-switching flags.
pub async fn get_host_info(&self) -> Result<ChangeHostInfo, Hidpp20Error> {
let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
Ok(ChangeHostInfo {
host_count: payload[0],
current_host: payload[1],
capabilities: ChangeHostCapabilities::from_bits_retain(payload[2]),
})
}
/// Selects `host` as the current host.
///
/// This is sent fire-and-forget: a successful switch usually resets the
/// device, so no response is awaited. The device drops off the current host
/// once it acts on the request.
pub async fn set_current_host(&self, host: u8) -> Result<(), Hidpp20Error> {
self.endpoint.notify(1, [host, 0, 0]).await
}
/// Retrieves the persistent per-host cookie bytes.
///
/// `host_count` is the value from [`ChangeHostInfo::host_count`]; the device
/// returns one cookie byte per host and does not delimit the list.
pub async fn get_cookies(&self, host_count: u8) -> Result<Vec<u8>, Hidpp20Error> {
let count = usize::from(host_count);
let payload = self.endpoint.call(2, [0; 3]).await?.extend_payload();
if count > payload.len() {
return Err(Hidpp20Error::UnsupportedResponse);
}
Ok(payload[..count].to_vec())
}
/// Writes the persistent `cookie` byte for `host`.
///
/// Cookies are arbitrary software-defined bytes stored in the device's
/// non-volatile memory; the firmware clears a host's cookie when a new host
/// connects to that slot.
pub async fn set_cookie(&self, host: u8, cookie: u8) -> Result<(), Hidpp20Error> {
self.endpoint.call(3, [host, cookie, 0]).await?;
Ok(())
}
}