1use std::{
6 collections::{HashMap, VecDeque},
7 error::Error,
8 sync::{
9 Arc, Mutex, Weak,
10 atomic::{AtomicBool, AtomicU8, AtomicU64, Ordering},
11 },
12 thread::{self, JoinHandle},
13 time::Duration,
14};
15
16use async_trait::async_trait;
17use futures::{FutureExt, channel::oneshot, select};
18use hidreport::{Field, Report, ReportDescriptor, Usage, UsageId, UsagePage};
19use rand::Rng;
20use thiserror::Error;
21use tracing::trace;
22
23use crate::nibble::U4;
24
25const MAX_REPORT_DESCRIPTOR_LENGTH: usize = 4096;
29
30const MAX_REPORT_LENGTH: usize = LONG_REPORT_LENGTH;
33
34const MAX_RAW_REPORT_LENGTH: usize = 64;
37
38pub const SEND_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5);
42
43pub const SHORT_REPORT_ID: u8 = 0x10;
45
46pub const SHORT_REPORT_USAGE_PAGE: u16 = 0xff00;
48
49pub const SHORT_REPORT_USAGE: u16 = 0x0001;
51
52pub const SHORT_REPORT_LENGTH: usize = 7;
54
55pub const LONG_REPORT_ID: u8 = 0x11;
57
58pub const LONG_REPORT_USAGE_PAGE: u16 = 0xff00;
60
61pub const LONG_REPORT_USAGE: u16 = 0x0002;
63
64pub const LONG_REPORT_LENGTH: usize = 20;
66
67#[async_trait]
75pub trait RawHidChannel: Sync + Send + 'static {
76 fn vendor_id(&self) -> u16;
78
79 fn product_id(&self) -> u16;
81
82 async fn write_report(&self, src: &[u8]) -> Result<usize, Box<dyn Error + Sync + Send>>;
86
87 async fn read_report(&self, buf: &mut [u8]) -> Result<usize, Box<dyn Error + Sync + Send>>;
102
103 fn is_connected(&self) -> bool {
109 true
110 }
111
112 fn supports_short_long_hidpp(&self) -> Option<(bool, bool)>;
118
119 async fn get_report_descriptor(
125 &self,
126 buf: &mut [u8],
127 ) -> Result<usize, Box<dyn Error + Sync + Send>>;
128}
129
130async fn supports_short_long_hidpp(
132 chan: &impl RawHidChannel,
133) -> Result<(bool, bool), ChannelError> {
134 if let Some((supports_short, supports_long)) = chan.supports_short_long_hidpp() {
135 return Ok((supports_short, supports_long));
136 }
137
138 let mut raw_descriptor = vec![0u8; MAX_REPORT_DESCRIPTOR_LENGTH];
139 let descriptor_size = chan.get_report_descriptor(&mut raw_descriptor).await?;
140
141 let descriptor = match ReportDescriptor::try_from(&raw_descriptor[..descriptor_size]) {
142 Ok(val) => val,
143 Err(err) => return Err(ChannelError::ReportDescriptor(err)),
144 };
145
146 let supports_short = descriptor
147 .find_input_report(&[SHORT_REPORT_ID])
148 .and_then(|report| report.fields().first())
149 .and_then(|field| match field {
150 Field::Array(arr) => Some(arr.usage_range()),
151 _ => None,
152 })
153 .is_some_and(|range| {
154 range
155 .lookup_usage(&Usage::from_page_and_id(
156 UsagePage::from(SHORT_REPORT_USAGE_PAGE),
157 UsageId::from(SHORT_REPORT_USAGE),
158 ))
159 .is_some()
160 });
161
162 let supports_long = descriptor
163 .find_input_report(&[LONG_REPORT_ID])
164 .and_then(|report| report.fields().first())
165 .and_then(|field| match field {
166 Field::Array(arr) => Some(arr.usage_range()),
167 _ => None,
168 })
169 .is_some_and(|range| {
170 range
171 .lookup_usage(&Usage::from_page_and_id(
172 UsagePage::from(LONG_REPORT_USAGE_PAGE),
173 UsageId::from(LONG_REPORT_USAGE),
174 ))
175 .is_some()
176 });
177
178 Ok((supports_short, supports_long))
179}
180
181#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
183pub enum HidppMessage {
184 Short([u8; SHORT_REPORT_LENGTH - 1]),
189
190 Long([u8; LONG_REPORT_LENGTH - 1]),
195}
196
197impl HidppMessage {
198 pub fn read_raw(data: &[u8]) -> Option<Self> {
200 let (&report_id, rest) = data.split_first()?;
201
202 if report_id == SHORT_REPORT_ID
204 && let Some((&payload, [])) = rest.split_first_chunk()
205 {
206 Some(HidppMessage::Short(payload))
207 } else if report_id == LONG_REPORT_ID
208 && let Some((&payload, [])) = rest.split_first_chunk()
209 {
210 Some(HidppMessage::Long(payload))
211 } else {
212 None
213 }
214 }
215
216 pub fn write_raw(&self, buf: &mut [u8]) -> usize {
220 match self {
221 Self::Short(payload) => {
222 buf[0] = SHORT_REPORT_ID;
223 buf[1..SHORT_REPORT_LENGTH].copy_from_slice(payload);
224 SHORT_REPORT_LENGTH
225 }
226 Self::Long(payload) => {
227 buf[0] = LONG_REPORT_ID;
228 buf[1..LONG_REPORT_LENGTH].copy_from_slice(payload);
229 LONG_REPORT_LENGTH
230 }
231 }
232 }
233
234 fn header(&self) -> (u8, u8, u8) {
238 let payload: &[u8] = match self {
239 Self::Short(payload) => payload,
240 Self::Long(payload) => payload,
241 };
242 (payload[0], payload[1], payload[2])
243 }
244}
245
246type MessageListener = Arc<dyn Fn(HidppMessage, bool) + Send + Sync + 'static>;
247
248pub struct MessageListenerGuard {
250 message_listeners: Weak<Mutex<HashMap<u32, MessageListener>>>,
251 hdl: u32,
252}
253
254impl Drop for MessageListenerGuard {
255 fn drop(&mut self) {
256 if let Some(message_listeners) = self.message_listeners.upgrade() {
257 message_listeners.lock().unwrap().remove(&self.hdl);
258 }
259 }
260}
261
262pub struct HidppChannel {
264 pub supports_short: bool,
266
267 pub supports_long: bool,
269
270 pub vendor_id: u16,
272
273 pub product_id: u16,
275
276 raw_channel: Arc<dyn RawHidChannel>,
278
279 rotate_software_id: AtomicBool,
281
282 software_id: AtomicU8,
284
285 pending_messages: Arc<Mutex<VecDeque<PendingMessage>>>,
287
288 pending_message_id: AtomicU64,
290
291 message_listeners: Arc<Mutex<HashMap<u32, MessageListener>>>,
294
295 read_thread_close: Option<oneshot::Sender<()>>,
297
298 read_thread_hdl: Option<JoinHandle<()>>,
301
302 sw_id_lease: Option<(u8, fn(u8))>,
308}
309
310impl Drop for HidppChannel {
311 fn drop(&mut self) {
312 if let Some((id, free)) = self.sw_id_lease.take() {
313 free(id);
314 }
315
316 if let Some(read_thread_close) = self.read_thread_close.take() {
317 let _ = read_thread_close.send(());
322 }
323
324 if let Some(read_thread_hdl) = self.read_thread_hdl.take() {
325 read_thread_hdl.join().unwrap();
326 }
327 }
328}
329
330struct PendingMessage {
332 id: u64,
334
335 response_predicate: Box<dyn Fn(&HidppMessage) -> bool + Send>,
338
339 sender: oneshot::Sender<HidppMessage>,
342}
343
344impl HidppChannel {
345 pub async fn from_raw_channel(raw: impl RawHidChannel) -> Result<Self, ChannelError> {
350 let (supports_short, supports_long) = supports_short_long_hidpp(&raw).await?;
351
352 if !supports_short && !supports_long {
353 return Err(ChannelError::HidppNotSupported);
354 }
355
356 let raw_channel_rc = Arc::new(raw);
357 let pending_messages_rc = Arc::new(Mutex::new(VecDeque::<PendingMessage>::new()));
358 let message_listeners_rc = Arc::new(Mutex::new(HashMap::<u32, MessageListener>::new()));
359
360 let (close_sender, mut close_receiver) = oneshot::channel::<()>();
361
362 let read_thread_hdl = thread::spawn({
363 let raw_channel = Arc::clone(&raw_channel_rc);
364 let pending_messages = Arc::clone(&pending_messages_rc);
365 let message_listeners = Arc::clone(&message_listeners_rc);
366
367 move || {
368 futures::executor::block_on(async {
369 let mut buf = [0u8; MAX_REPORT_LENGTH];
370
371 loop {
372 let res = select! {
373 _ = close_receiver => {
374 break;
375 },
376 res = raw_channel.read_report(&mut buf).fuse() => res
377 };
378
379 let len = match res {
380 Ok(len) => len,
381 Err(error) => {
382 trace!(?error, "read_report error");
385 continue;
386 }
387 };
388
389 let Some(msg) = HidppMessage::read_raw(&buf[..len]) else {
390 trace!(len, "report not HID++ — dropped");
391 continue;
392 };
393
394 let mut matched = false;
395 let pending_count;
396 {
397 let mut msgs = pending_messages.lock().unwrap();
398 pending_count = msgs.len();
399 if let Some(pos) =
400 msgs.iter().position(|elem| (elem.response_predicate)(&msg))
401 {
402 let waiting = msgs.remove(pos).unwrap();
403 let _ = waiting.sender.send(msg);
404 matched = true;
405 }
406 }
407
408 trace!(
409 len,
410 matched,
411 pending_count,
412 payload = format!("{:02x?}", &buf[..len.min(16)]),
413 "raw report received"
414 );
415
416 let listeners: Vec<_> = message_listeners
417 .lock()
418 .unwrap()
419 .values()
420 .cloned()
421 .collect();
422 for listener in listeners {
423 listener(msg, matched);
424 }
425 }
426 });
427 }
428 });
429
430 Ok(Self {
431 supports_short,
432 supports_long,
433 vendor_id: raw_channel_rc.vendor_id(),
434 product_id: raw_channel_rc.product_id(),
435 raw_channel: raw_channel_rc,
436 rotate_software_id: AtomicBool::new(false),
437 software_id: AtomicU8::new(0x01),
438 pending_messages: pending_messages_rc,
439 pending_message_id: AtomicU64::new(1),
440 message_listeners: message_listeners_rc,
441 read_thread_close: Some(close_sender),
442 read_thread_hdl: Some(read_thread_hdl),
443 sw_id_lease: None,
444 })
445 }
446
447 pub fn is_connected(&self) -> bool {
449 self.raw_channel.is_connected()
450 }
451
452 pub fn set_sw_id(&self, sw_id: U4) {
458 self.software_id.store(sw_id.to_lo(), Ordering::SeqCst);
459 }
460
461 pub fn set_rotating_sw_id(&self, enable: bool) {
470 self.rotate_software_id.store(enable, Ordering::SeqCst);
471 }
472
473 pub fn set_sw_id_lease(&mut self, id: u8, free: fn(u8)) {
480 self.sw_id_lease = Some((id, free));
481 }
482
483 pub fn get_sw_id(&self) -> U4 {
489 if self.rotate_software_id.load(Ordering::SeqCst) {
490 U4::from_lo(
491 self.software_id
492 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |old| {
493 Some(if old & 0x0f == 0x0f {
494 0x01
495 } else {
496 old.wrapping_add(1)
497 })
498 })
499 .unwrap(),
500 )
501 } else {
502 U4::from_lo(self.software_id.load(Ordering::SeqCst))
503 }
504 }
505
506 pub fn supports_msg(&self, msg: &HidppMessage) -> bool {
508 match msg {
509 HidppMessage::Short(_) => self.supports_short,
510 HidppMessage::Long(_) => self.supports_long,
511 }
512 }
513
514 fn normalize_outgoing(&self, msg: HidppMessage) -> HidppMessage {
524 match msg {
525 HidppMessage::Short(payload) if !self.supports_short && self.supports_long => {
526 HidppMessage::Long(short_payload_as_long(&payload))
527 }
528 other => other,
529 }
530 }
531
532 pub async fn send(
541 &self,
542 msg: HidppMessage,
543 response_predicate: impl Fn(&HidppMessage) -> bool + Send + 'static,
544 ) -> Result<HidppMessage, ChannelError> {
545 self.send_with_timeout(msg, response_predicate, SEND_RESPONSE_TIMEOUT)
546 .await
547 }
548
549 pub async fn send_with_timeout(
563 &self,
564 msg: HidppMessage,
565 response_predicate: impl Fn(&HidppMessage) -> bool + Send + 'static,
566 timeout: Duration,
567 ) -> Result<HidppMessage, ChannelError> {
568 let msg = self.normalize_outgoing(msg);
569 if !self.supports_msg(&msg) {
570 return Err(ChannelError::MessageTypeNotSupported);
571 }
572
573 let (dev, feat, func) = msg.header();
577 trace!(dev, feat, func, "hidpp request");
578
579 let (sender, receiver) = oneshot::channel::<HidppMessage>();
580 let pending_id = self.pending_message_id.fetch_add(1, Ordering::SeqCst);
581
582 {
583 let mut pending = self.pending_messages.lock().unwrap();
584 pending.retain(|m| !m.sender.is_canceled());
593 pending.push_back(PendingMessage {
594 id: pending_id,
595 response_predicate: Box::new(response_predicate),
596 sender,
597 });
598 }
599
600 let mut request = std::pin::pin!(
604 async {
605 self.send_and_forget(msg).await?;
606 receiver.await.map_err(|_| ChannelError::NoResponse)
607 }
608 .fuse()
609 );
610
611 let result = select! {
612 result = request => result,
613 _ = futures_timer::Delay::new(timeout).fuse() => Err(ChannelError::Timeout),
614 };
615
616 match &result {
617 Ok(_) => trace!(dev, feat, "hidpp response"),
618 Err(e) => trace!(dev, feat, error = ?e, "hidpp no response"),
619 }
620
621 if result.is_err() {
622 self.remove_pending_message(pending_id);
626 }
627
628 result
629 }
630
631 fn remove_pending_message(&self, id: u64) {
632 let mut pending = self.pending_messages.lock().unwrap();
633 if let Some(pos) = pending.iter().position(|msg| msg.id == id) {
634 pending.remove(pos);
635 }
636 }
637
638 pub async fn send_and_forget(&self, msg: HidppMessage) -> Result<(), ChannelError> {
643 let msg = self.normalize_outgoing(msg);
644 if !self.supports_msg(&msg) {
645 return Err(ChannelError::MessageTypeNotSupported);
646 }
647
648 let mut buf = [0u8; LONG_REPORT_LENGTH];
649 let len = msg.write_raw(&mut buf);
650 self.raw_channel
651 .write_report(&buf[..len])
652 .await
653 .map(|_| ())
654 .map_err(ChannelError::Implementation)
655 }
656
657 pub async fn write_raw_report(&self, report: &[u8]) -> Result<usize, ChannelError> {
665 self.write_raw_report_with_timeout(report, SEND_RESPONSE_TIMEOUT)
666 .await
667 }
668
669 async fn write_raw_report_with_timeout(
670 &self,
671 report: &[u8],
672 timeout: Duration,
673 ) -> Result<usize, ChannelError> {
674 if !(1..=MAX_RAW_REPORT_LENGTH).contains(&report.len()) {
675 return Err(ChannelError::InvalidRawReportLength(report.len()));
676 }
677
678 let mut write = std::pin::pin!(self.raw_channel.write_report(report).fuse());
679 select! {
680 result = write => result.map_err(ChannelError::Implementation),
681 _ = futures_timer::Delay::new(timeout).fuse() => Err(ChannelError::Timeout),
682 }
683 }
684
685 pub fn add_msg_listener(
690 &self,
691 listener: impl Fn(HidppMessage, bool) + Send + Sync + 'static,
692 ) -> u32 {
693 let mut listeners = self.message_listeners.lock().unwrap();
694
695 let mut rng = rand::rng();
696 let mut hdl = rng.random::<u32>();
697 while listeners.contains_key(&hdl) {
698 hdl = rng.random::<u32>();
699 }
700
701 listeners.insert(hdl, Arc::new(listener));
702 hdl
703 }
704
705 pub fn add_msg_listener_guarded(
708 &self,
709 listener: impl Fn(HidppMessage, bool) + Send + Sync + 'static,
710 ) -> MessageListenerGuard {
711 let hdl = self.add_msg_listener(listener);
712 MessageListenerGuard {
713 message_listeners: Arc::downgrade(&self.message_listeners),
714 hdl,
715 }
716 }
717
718 pub fn remove_msg_listener(&self, hdl: u32) -> bool {
722 self.message_listeners
723 .lock()
724 .unwrap()
725 .remove(&hdl)
726 .is_some()
727 }
728}
729
730#[derive(Debug, Error)]
733#[non_exhaustive]
734pub enum ChannelError {
735 #[error("the HID channel implementation returned an error")]
738 Implementation(#[from] Box<dyn Error + Sync + Send>),
739
740 #[error("the report descriptor could not be parsed")]
742 ReportDescriptor(hidreport::ParserError),
743
744 #[error("the HID channel does not support HID++")]
746 HidppNotSupported,
747
748 #[error("the channel does not support the given HID++ message type")]
751 MessageTypeNotSupported,
752
753 #[error("raw HID reports must contain 1..=64 bytes, got {0}")]
755 InvalidRawReportLength(usize),
756
757 #[error("the device did not respond to the request")]
759 NoResponse,
760
761 #[error("the HID channel operation timed out")]
767 Timeout,
768}
769
770fn short_payload_as_long(payload: &[u8; SHORT_REPORT_LENGTH - 1]) -> [u8; LONG_REPORT_LENGTH - 1] {
776 let mut long = [0u8; LONG_REPORT_LENGTH - 1];
777 long[..payload.len()].copy_from_slice(payload);
778 long
779}
780
781#[cfg(test)]
782pub(crate) mod tests {
783 use super::*;
784 use std::{
785 io,
786 sync::{
787 Arc, Mutex,
788 atomic::{AtomicBool, AtomicUsize, Ordering},
789 },
790 time::{Duration, Instant},
791 };
792
793 use crate::{
794 nibble,
795 protocol::v20::{self, ErrorType, Hidpp20Error},
796 };
797
798 #[test]
799 fn short_payload_widens_preserving_header_and_padding() {
800 let short = [0xff, 0x05, 0x1e, 0xaa, 0xbb, 0xcc];
802 let long = short_payload_as_long(&short);
803 assert_eq!(&long[..short.len()], &short[..]); assert!(long[short.len()..].iter().all(|&b| b == 0)); assert_eq!(long.len(), LONG_REPORT_LENGTH - 1);
806 }
807
808 #[test]
809 fn send_returns_response_before_timeout() {
810 futures::executor::block_on(async {
811 let (raw, handle) = MockRawHidChannel::new();
812 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
813
814 let request = short_msg(0x10);
815 let response = short_msg(0x20);
816 handle.queue_response(response);
817
818 let actual = channel
819 .send_with_timeout(
820 request,
821 move |candidate| *candidate == response,
822 Duration::from_secs(1),
823 )
824 .await
825 .unwrap();
826
827 assert_eq!(actual, response);
828 assert_eq!(handle.written_reports().len(), 1);
829 assert_pending_empty(&channel);
830 });
831 }
832
833 #[test]
834 fn send_times_out_and_removes_pending_message() {
835 futures::executor::block_on(async {
836 let (raw, handle) = MockRawHidChannel::new();
837 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
838 let request = short_msg(0x10);
839 let response = short_msg(0x20);
840
841 let started = Instant::now();
842 let err = channel
843 .send_with_timeout(
844 request,
845 move |candidate| *candidate == response,
846 Duration::from_millis(25),
847 )
848 .await
849 .unwrap_err();
850
851 assert!(matches!(err, ChannelError::Timeout));
852 assert!(started.elapsed() < Duration::from_secs(1));
853 assert_eq!(handle.written_reports().len(), 1);
854 assert_pending_empty(&channel);
855 });
856 }
857
858 #[test]
859 fn timeout_removes_only_its_own_pending_message() {
860 futures::executor::block_on(async {
861 let (raw, handle) = MockRawHidChannel::new();
862 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
863
864 let never_answered = short_msg(0x20);
865 let slow_response = short_msg(0x21);
866
867 let timed_out = channel.send_with_timeout(
868 short_msg(0x10),
869 move |candidate| *candidate == never_answered,
870 Duration::from_millis(25),
871 );
872 let answered = channel.send_with_timeout(
873 short_msg(0x11),
874 move |candidate| *candidate == slow_response,
875 Duration::from_secs(1),
876 );
877 let respond_late = async {
880 futures_timer::Delay::new(Duration::from_millis(100)).await;
881 handle.send_incoming(slow_response).await;
882 };
883
884 let (timed_out, answered, ()) = futures::join!(timed_out, answered, respond_late);
885
886 assert!(matches!(timed_out.unwrap_err(), ChannelError::Timeout));
887 assert_eq!(answered.unwrap(), slow_response);
888 assert_pending_empty(&channel);
889 });
890 }
891
892 #[test]
893 fn late_response_after_timeout_is_ignored() {
894 futures::executor::block_on(async {
895 let (raw, handle) = MockRawHidChannel::new();
896 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
897 let events = Arc::new(Mutex::new(Vec::new()));
898 let listener_events = Arc::clone(&events);
899 channel.add_msg_listener(move |msg, matched| {
900 listener_events.lock().unwrap().push((msg, matched));
901 });
902
903 let request = short_msg(0x10);
904 let late_response = short_msg(0x20);
905 let err = channel
906 .send_with_timeout(
907 request,
908 move |candidate| *candidate == late_response,
909 Duration::from_millis(25),
910 )
911 .await
912 .unwrap_err();
913
914 assert!(matches!(err, ChannelError::Timeout));
915 assert_pending_empty(&channel);
916
917 handle.send_incoming(late_response).await;
918 wait_for_event_count(&events, 1).await;
919 assert_eq!(events.lock().unwrap()[0], (late_response, false));
920 assert_pending_empty(&channel);
921
922 let later_request = short_msg(0x30);
923 let later_response = short_msg(0x40);
924 handle.queue_response(later_response);
925 let actual = channel
926 .send_with_timeout(
927 later_request,
928 move |candidate| *candidate == later_response,
929 Duration::from_secs(1),
930 )
931 .await
932 .unwrap();
933
934 assert_eq!(actual, later_response);
935 wait_for_event_count(&events, 2).await;
936 assert_eq!(events.lock().unwrap()[1], (later_response, true));
937 assert_pending_empty(&channel);
938 });
939 }
940
941 #[test]
942 fn send_and_forget_writes_without_pending_message() {
943 futures::executor::block_on(async {
944 let (raw, handle) = MockRawHidChannel::new();
945 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
946
947 channel.send_and_forget(short_msg(0x10)).await.unwrap();
948
949 assert_eq!(handle.written_reports().len(), 1);
950 assert_pending_empty(&channel);
951 });
952 }
953
954 #[test]
955 fn raw_report_write_forwards_exact_bytes_and_length() {
956 futures::executor::block_on(async {
957 let (raw, handle) = MockRawHidChannel::new();
958 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
959 let report = [0x12; MAX_RAW_REPORT_LENGTH];
960
961 let written = channel.write_raw_report(&report).await.unwrap();
962
963 assert_eq!(written, report.len());
964 assert_eq!(handle.written_reports(), [report.to_vec()]);
965 });
966 }
967
968 #[test]
969 fn raw_report_write_rejects_empty_and_oversized_inputs_without_io() {
970 futures::executor::block_on(async {
971 let (raw, handle) = MockRawHidChannel::new();
972 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
973
974 let empty = channel.write_raw_report(&[]).await.unwrap_err();
975 let oversized = channel
976 .write_raw_report(&[0; MAX_RAW_REPORT_LENGTH + 1])
977 .await
978 .unwrap_err();
979
980 assert!(matches!(empty, ChannelError::InvalidRawReportLength(0)));
981 assert!(matches!(
982 oversized,
983 ChannelError::InvalidRawReportLength(65)
984 ));
985 assert!(handle.written_reports().is_empty());
986 });
987 }
988
989 #[test]
990 fn raw_report_write_times_out_when_the_transport_parks() {
991 futures::executor::block_on(async {
992 let (raw, handle) = MockRawHidChannel::new();
993 handle.park_writes();
994 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
995 let started = Instant::now();
996
997 let error = channel
998 .write_raw_report_with_timeout(&[LONG_REPORT_ID], Duration::from_millis(25))
999 .await
1000 .unwrap_err();
1001
1002 assert!(matches!(error, ChannelError::Timeout));
1003 assert!(started.elapsed() < Duration::from_secs(1));
1004 });
1005 }
1006
1007 #[test]
1008 fn listener_can_remove_another_listener_during_dispatch() {
1009 futures::executor::block_on(async {
1010 let (raw, handle) = MockRawHidChannel::new();
1011 let channel = Arc::new(HidppChannel::from_raw_channel(raw).await.unwrap());
1012 let removed_listener_calls = Arc::new(AtomicUsize::new(0));
1013 let removing_listener_calls = Arc::new(AtomicUsize::new(0));
1014
1015 let removed_listener_calls_for_listener = Arc::clone(&removed_listener_calls);
1016 let removed_hdl = channel.add_msg_listener(move |_, _| {
1017 removed_listener_calls_for_listener.fetch_add(1, Ordering::SeqCst);
1018 });
1019
1020 let channel_for_listener = Arc::clone(&channel);
1021 let removing_listener_calls_for_listener = Arc::clone(&removing_listener_calls);
1022 channel.add_msg_listener(move |_, _| {
1023 removing_listener_calls_for_listener.fetch_add(1, Ordering::SeqCst);
1024 channel_for_listener.remove_msg_listener(removed_hdl);
1025 });
1026
1027 handle.send_incoming(short_msg(0x20)).await;
1028 wait_for_atomic_count(&removing_listener_calls, 1).await;
1029 wait_for_atomic_count(&removed_listener_calls, 1).await;
1030
1031 handle.send_incoming(short_msg(0x21)).await;
1032 wait_for_atomic_count(&removing_listener_calls, 2).await;
1033
1034 assert_eq!(removed_listener_calls.load(Ordering::SeqCst), 1);
1035 });
1036 }
1037
1038 #[test]
1048 fn send_v20_matches_response_by_header_ignoring_unrelated_messages() {
1049 futures::executor::block_on(async {
1050 let (raw, handle) = MockRawHidChannel::new();
1051 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
1052
1053 let header = v20::MessageHeader {
1054 device_index: 0x01,
1055 feature_index: 0x05,
1056 function_id: U4::from_lo(0x2),
1057 software_id: U4::from_lo(0x3),
1058 };
1059 let request = v20::Message::Short(header, [0x00, 0x00, 0x00]);
1060 let response = v20::Message::Short(header, [0xaa, 0xbb, 0xcc]);
1061
1062 let wrong_device = v20::Message::Short(
1065 v20::MessageHeader {
1066 device_index: 0x02,
1067 ..header
1068 },
1069 [0, 0, 0],
1070 );
1071 let wrong_feature = v20::Message::Short(
1072 v20::MessageHeader {
1073 feature_index: 0x06,
1074 ..header
1075 },
1076 [0, 0, 0],
1077 );
1078 let wrong_sw_id = v20::Message::Short(
1079 v20::MessageHeader {
1080 software_id: U4::from_lo(0x4),
1081 ..header
1082 },
1083 [0, 0, 0],
1084 );
1085
1086 let send_fut = channel.send_v20(request);
1087 let feed_fut = async {
1088 handle.send_incoming(wrong_device.into()).await;
1089 handle.send_incoming(wrong_feature.into()).await;
1090 handle.send_incoming(wrong_sw_id.into()).await;
1091 handle.send_incoming(response.into()).await;
1092 };
1093
1094 let (result, ()) = futures::join!(send_fut, feed_fut);
1095
1096 assert_eq!(result.unwrap(), response);
1097 assert_pending_empty(&channel);
1098 });
1099 }
1100
1101 #[test]
1102 fn send_v20_broadcast_event_does_not_resolve_pending_request() {
1103 futures::executor::block_on(async {
1104 let (raw, handle) = MockRawHidChannel::new();
1105 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
1106 let events = Arc::new(Mutex::new(Vec::new()));
1107 let listener_events = Arc::clone(&events);
1108 channel.add_msg_listener(move |msg, matched| {
1109 listener_events.lock().unwrap().push((msg, matched));
1110 });
1111
1112 let header = v20::MessageHeader {
1113 device_index: 0x01,
1114 feature_index: 0x05,
1115 function_id: U4::from_lo(0x2),
1116 software_id: U4::from_lo(0x3),
1117 };
1118 let request = v20::Message::Short(header, [0, 0, 0]);
1119 let response = v20::Message::Short(header, [0xaa, 0xbb, 0xcc]);
1120
1121 let event = v20::Message::Short(
1127 v20::MessageHeader {
1128 software_id: U4::from_lo(0x0),
1129 ..header
1130 },
1131 [0x01, 0x02, 0x03],
1132 );
1133
1134 let send_fut = channel.send_v20(request);
1135 let feed_fut = async {
1136 handle.send_incoming(event.into()).await;
1137 wait_for_event_count(&events, 1).await;
1138 handle.send_incoming(response.into()).await;
1139 };
1140
1141 let (result, ()) = futures::join!(send_fut, feed_fut);
1142
1143 assert_eq!(result.unwrap(), response);
1144 wait_for_event_count(&events, 2).await;
1147 let recorded = events.lock().unwrap().clone();
1148 assert_eq!(
1149 recorded,
1150 vec![
1151 (HidppMessage::from(event), false),
1152 (HidppMessage::from(response), true),
1153 ]
1154 );
1155 assert_pending_empty(&channel);
1156 });
1157 }
1158
1159 #[test]
1160 fn send_v20_response_may_arrive_as_a_different_report_width() {
1161 futures::executor::block_on(async {
1162 let (raw, handle) = MockRawHidChannel::new();
1163 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
1164
1165 let header = v20::MessageHeader {
1166 device_index: 0x01,
1167 feature_index: 0x05,
1168 function_id: U4::from_lo(0x2),
1169 software_id: U4::from_lo(0x3),
1170 };
1171 let request = v20::Message::Short(header, [0, 0, 0]);
1172 let response = v20::Message::Long(header, [0xaa; 16]);
1177 handle.queue_response(response.into());
1178
1179 let result = channel.send_v20(request).await.unwrap();
1180
1181 assert_eq!(result, response);
1182 assert_pending_empty(&channel);
1183 });
1184 }
1185
1186 #[test]
1187 fn send_v20_error_frame_resolves_to_feature_error() {
1188 futures::executor::block_on(async {
1189 let (raw, handle) = MockRawHidChannel::new();
1190 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
1191
1192 let header = v20::MessageHeader {
1193 device_index: 0x01,
1194 feature_index: 0x05,
1195 function_id: U4::from_lo(0x2),
1196 software_id: U4::from_lo(0x3),
1197 };
1198 let request = v20::Message::Short(header, [0, 0, 0]);
1199 let error_response = v20_error_frame(header, ErrorType::InvalidArgument.into());
1200 handle.queue_response(error_response.into());
1201
1202 let err = channel.send_v20(request).await.unwrap_err();
1203
1204 assert!(matches!(
1205 err,
1206 Hidpp20Error::Feature(ErrorType::InvalidArgument)
1207 ));
1208 assert_pending_empty(&channel);
1209 });
1210 }
1211
1212 #[test]
1213 fn send_v20_error_frame_with_unmapped_code_is_unsupported_response() {
1214 futures::executor::block_on(async {
1215 let (raw, handle) = MockRawHidChannel::new();
1216 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
1217
1218 let header = v20::MessageHeader {
1219 device_index: 0x01,
1220 feature_index: 0x05,
1221 function_id: U4::from_lo(0x2),
1222 software_id: U4::from_lo(0x3),
1223 };
1224 let request = v20::Message::Short(header, [0, 0, 0]);
1225 let error_response = v20_error_frame(header, 0xfe);
1227 handle.queue_response(error_response.into());
1228
1229 let err = channel.send_v20(request).await.unwrap_err();
1230
1231 assert!(matches!(err, Hidpp20Error::UnsupportedResponse));
1232 assert_pending_empty(&channel);
1233 });
1234 }
1235
1236 fn v20_error_frame(request_header: v20::MessageHeader, error_code: u8) -> v20::Message {
1241 let error_header = v20::MessageHeader {
1242 device_index: request_header.device_index,
1243 feature_index: 0xff,
1244 function_id: U4::from_hi(request_header.feature_index),
1245 software_id: U4::from_lo(request_header.feature_index),
1246 };
1247 let mut payload = [0u8; 3];
1248 payload[0] = nibble::combine(request_header.function_id, request_header.software_id);
1249 payload[1] = error_code;
1250 v20::Message::Short(error_header, payload)
1251 }
1252
1253 #[derive(Clone)]
1254 pub(crate) struct MockRawHidHandle {
1255 incoming_tx: async_channel::Sender<Vec<u8>>,
1256 written_reports: Arc<Mutex<Vec<Vec<u8>>>>,
1257 responses_on_write: Arc<Mutex<VecDeque<Vec<u8>>>>,
1258 park_writes: Arc<AtomicBool>,
1259 }
1260
1261 impl MockRawHidHandle {
1262 pub(crate) fn queue_response(&self, msg: HidppMessage) {
1263 self.responses_on_write
1264 .lock()
1265 .unwrap()
1266 .push_back(raw_report(msg));
1267 }
1268
1269 async fn send_incoming(&self, msg: HidppMessage) {
1270 self.incoming_tx.send(raw_report(msg)).await.unwrap();
1271 }
1272
1273 pub(crate) fn written_reports(&self) -> Vec<Vec<u8>> {
1274 self.written_reports.lock().unwrap().clone()
1275 }
1276
1277 fn park_writes(&self) {
1278 self.park_writes.store(true, Ordering::SeqCst);
1279 }
1280 }
1281
1282 pub(crate) struct MockRawHidChannel {
1283 incoming_tx: async_channel::Sender<Vec<u8>>,
1284 incoming_rx: async_channel::Receiver<Vec<u8>>,
1285 written_reports: Arc<Mutex<Vec<Vec<u8>>>>,
1286 responses_on_write: Arc<Mutex<VecDeque<Vec<u8>>>>,
1287 park_writes: Arc<AtomicBool>,
1288 }
1289
1290 impl MockRawHidChannel {
1291 pub(crate) fn new() -> (Self, MockRawHidHandle) {
1292 let (incoming_tx, incoming_rx) = async_channel::unbounded();
1293 let written_reports = Arc::new(Mutex::new(Vec::new()));
1294 let responses_on_write = Arc::new(Mutex::new(VecDeque::new()));
1295 let park_writes = Arc::new(AtomicBool::new(false));
1296
1297 let handle = MockRawHidHandle {
1298 incoming_tx: incoming_tx.clone(),
1299 written_reports: Arc::clone(&written_reports),
1300 responses_on_write: Arc::clone(&responses_on_write),
1301 park_writes: Arc::clone(&park_writes),
1302 };
1303
1304 (
1305 Self {
1306 incoming_tx,
1307 incoming_rx,
1308 written_reports,
1309 responses_on_write,
1310 park_writes,
1311 },
1312 handle,
1313 )
1314 }
1315 }
1316
1317 #[async_trait]
1318 impl RawHidChannel for MockRawHidChannel {
1319 fn vendor_id(&self) -> u16 {
1320 0x046d
1321 }
1322
1323 fn product_id(&self) -> u16 {
1324 0xc539
1325 }
1326
1327 async fn write_report(&self, src: &[u8]) -> Result<usize, Box<dyn Error + Sync + Send>> {
1328 self.written_reports.lock().unwrap().push(src.to_vec());
1329 if self.park_writes.load(Ordering::SeqCst) {
1330 return std::future::pending().await;
1331 }
1332 let response = self.responses_on_write.lock().unwrap().pop_front();
1333 if let Some(response) = response {
1334 self.incoming_tx.send(response).await.unwrap();
1335 }
1336
1337 Ok(src.len())
1338 }
1339
1340 async fn read_report(&self, buf: &mut [u8]) -> Result<usize, Box<dyn Error + Sync + Send>> {
1341 let report = self.incoming_rx.recv().await.map_err(|_| mock_error())?;
1342 let len = report.len().min(buf.len());
1343 buf[..len].copy_from_slice(&report[..len]);
1344 Ok(len)
1345 }
1346
1347 fn supports_short_long_hidpp(&self) -> Option<(bool, bool)> {
1348 Some((true, true))
1349 }
1350
1351 async fn get_report_descriptor(
1352 &self,
1353 _buf: &mut [u8],
1354 ) -> Result<usize, Box<dyn Error + Sync + Send>> {
1355 unreachable!("mock declares HID++ support")
1356 }
1357 }
1358
1359 fn short_msg(marker: u8) -> HidppMessage {
1360 HidppMessage::Short([0xff, marker, 0x10, marker, marker, marker])
1361 }
1362
1363 fn raw_report(msg: HidppMessage) -> Vec<u8> {
1364 let mut buf = [0u8; LONG_REPORT_LENGTH];
1365 let len = msg.write_raw(&mut buf);
1366 buf[..len].to_vec()
1367 }
1368
1369 fn assert_pending_empty(channel: &HidppChannel) {
1370 assert!(channel.pending_messages.lock().unwrap().is_empty());
1371 }
1372
1373 async fn wait_for_event_count(events: &Arc<Mutex<Vec<(HidppMessage, bool)>>>, count: usize) {
1374 let started = Instant::now();
1375 while started.elapsed() < Duration::from_secs(1) {
1376 if events.lock().unwrap().len() >= count {
1377 return;
1378 }
1379 futures_timer::Delay::new(Duration::from_millis(10)).await;
1380 }
1381
1382 panic!("timed out waiting for {count} listener events");
1383 }
1384
1385 async fn wait_for_atomic_count(count: &AtomicUsize, expected: usize) {
1386 let started = Instant::now();
1387 while started.elapsed() < Duration::from_secs(1) {
1388 if count.load(Ordering::SeqCst) >= expected {
1389 return;
1390 }
1391 futures_timer::Delay::new(Duration::from_millis(10)).await;
1392 }
1393
1394 panic!("timed out waiting for atomic count {expected}");
1395 }
1396
1397 fn mock_error() -> Box<dyn Error + Sync + Send> {
1398 Box::new(io::Error::new(
1399 io::ErrorKind::BrokenPipe,
1400 "mock channel closed",
1401 ))
1402 }
1403}