aboutsummaryrefslogtreecommitdiff
path: root/src/protocol.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/protocol.rs')
-rw-r--r--src/protocol.rs55
1 files changed, 34 insertions, 21 deletions
diff --git a/src/protocol.rs b/src/protocol.rs
index 3a24195..d6fce5f 100644
--- a/src/protocol.rs
+++ b/src/protocol.rs
@@ -36,6 +36,15 @@ pub const LINK_DEBOUNCE_MISSES: u32 = 2;
/// How long to wait for a link response after sending the query.
const LINK_RESPONSE_TIMEOUT: Duration = Duration::from_millis(400);
+/// Input report id the dongle answers with when the headset is linked.
+const LINK_RESPONSE_REPORT: u8 = 0x3c;
+/// Selector byte we query with; the response echoes it back.
+const LINK_SELECTOR: u8 = 0x01;
+/// Command prefix at the start of the query payload.
+const CMD_PREFIX: [u8; 2] = [0x09, 0x22];
+/// Link query frame size: report id + 60-byte payload.
+const LINK_QUERY_LEN: usize = 61;
+
/// Headset link state as seen through the dongle.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LinkState {
@@ -52,20 +61,20 @@ pub enum Transition {
ToDisconnected { initial: bool },
}
-/// Builds the 61-byte link query output report (report id `0x3a` + payload).
-pub fn link_query_frame() -> Vec<u8> {
- let mut frame = vec![0u8; 61];
+/// Builds the link query output report (report id `0x3a` + payload).
+pub fn link_query_frame() -> [u8; LINK_QUERY_LEN] {
+ let mut frame = [0u8; LINK_QUERY_LEN];
frame[0] = reports::OUT_60_C;
- frame[1] = 0x09;
- frame[2] = 0x22;
- frame[3] = 0x01;
+ frame[1] = CMD_PREFIX[0];
+ frame[2] = CMD_PREFIX[1];
+ frame[3] = LINK_SELECTOR;
frame
}
/// True if `data` is a link-status response report (`0x3c` echoing the
/// query selector).
pub fn is_link_response(data: &[u8]) -> bool {
- data.len() >= 2 && data[0] == 0x3c && data[1] == 0x01
+ data.len() >= 2 && data[0] == LINK_RESPONSE_REPORT && data[1] == LINK_SELECTOR
}
/// Queries the dongle once for the headset link state. Returns
@@ -116,25 +125,29 @@ impl StateTracker {
if self.state == Some(LinkState::Connected) {
return None;
}
- let initial = self.state.is_none();
- self.state = Some(LinkState::Connected);
- if initial && !self.fire_initial {
- None
- } else {
- Some(Transition::ToConnected { initial })
- }
+ self.settle(LinkState::Connected)
} else {
self.misses += 1;
if self.misses < LINK_DEBOUNCE_MISSES || self.state == Some(LinkState::Disconnected) {
return None;
}
- let initial = self.state.is_none();
- self.state = Some(LinkState::Disconnected);
- if initial && !self.fire_initial {
- None
- } else {
- Some(Transition::ToDisconnected { initial })
- }
+ self.settle(LinkState::Disconnected)
+ }
+ }
+
+ /// Pin the state, producing the transition unless it is the first
+ /// determination and initial hooks are suppressed.
+ fn settle(&mut self, state: LinkState) -> Option<Transition> {
+ let initial = self.state.is_none();
+ self.state = Some(state);
+ let transition = match state {
+ LinkState::Connected => Transition::ToConnected { initial },
+ LinkState::Disconnected => Transition::ToDisconnected { initial },
+ };
+ if initial && !self.fire_initial {
+ None
+ } else {
+ Some(transition)
}
}
}