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;
21
22use crate::nibble::U4;
23
24const MAX_REPORT_DESCRIPTOR_LENGTH: usize = 4096;
28
29const MAX_REPORT_LENGTH: usize = LONG_REPORT_LENGTH;
32
33pub const SEND_RESPONSE_TIMEOUT: Duration = Duration::from_secs(5);
37
38pub const SHORT_REPORT_ID: u8 = 0x10;
40
41pub const SHORT_REPORT_USAGE_PAGE: u16 = 0xff00;
43
44pub const SHORT_REPORT_USAGE: u16 = 0x0001;
46
47pub const SHORT_REPORT_LENGTH: usize = 7;
49
50pub const LONG_REPORT_ID: u8 = 0x11;
52
53pub const LONG_REPORT_USAGE_PAGE: u16 = 0xff00;
55
56pub const LONG_REPORT_USAGE: u16 = 0x0002;
58
59pub const LONG_REPORT_LENGTH: usize = 20;
61
62#[async_trait]
70pub trait RawHidChannel: Sync + Send + 'static {
71 fn vendor_id(&self) -> u16;
73
74 fn product_id(&self) -> u16;
76
77 async fn write_report(&self, src: &[u8]) -> Result<usize, Box<dyn Error + Sync + Send>>;
81
82 async fn read_report(&self, buf: &mut [u8]) -> Result<usize, Box<dyn Error + Sync + Send>>;
97
98 fn supports_short_long_hidpp(&self) -> Option<(bool, bool)>;
104
105 async fn get_report_descriptor(
111 &self,
112 buf: &mut [u8],
113 ) -> Result<usize, Box<dyn Error + Sync + Send>>;
114}
115
116async fn supports_short_long_hidpp(
118 chan: &impl RawHidChannel,
119) -> Result<(bool, bool), ChannelError> {
120 if let Some((supports_short, supports_long)) = chan.supports_short_long_hidpp() {
121 return Ok((supports_short, supports_long));
122 }
123
124 let mut raw_descriptor = vec![0u8; MAX_REPORT_DESCRIPTOR_LENGTH];
125 let descriptor_size = chan.get_report_descriptor(&mut raw_descriptor).await?;
126
127 let descriptor = match ReportDescriptor::try_from(&raw_descriptor[..descriptor_size]) {
128 Ok(val) => val,
129 Err(err) => return Err(ChannelError::ReportDescriptor(err)),
130 };
131
132 let supports_short = descriptor
133 .find_input_report(&[SHORT_REPORT_ID])
134 .and_then(|report| report.fields().first())
135 .and_then(|field| match field {
136 Field::Array(arr) => Some(arr.usage_range()),
137 _ => None,
138 })
139 .is_some_and(|range| {
140 range
141 .lookup_usage(&Usage::from_page_and_id(
142 UsagePage::from(SHORT_REPORT_USAGE_PAGE),
143 UsageId::from(SHORT_REPORT_USAGE),
144 ))
145 .is_some()
146 });
147
148 let supports_long = descriptor
149 .find_input_report(&[LONG_REPORT_ID])
150 .and_then(|report| report.fields().first())
151 .and_then(|field| match field {
152 Field::Array(arr) => Some(arr.usage_range()),
153 _ => None,
154 })
155 .is_some_and(|range| {
156 range
157 .lookup_usage(&Usage::from_page_and_id(
158 UsagePage::from(LONG_REPORT_USAGE_PAGE),
159 UsageId::from(LONG_REPORT_USAGE),
160 ))
161 .is_some()
162 });
163
164 Ok((supports_short, supports_long))
165}
166
167#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
169pub enum HidppMessage {
170 Short([u8; SHORT_REPORT_LENGTH - 1]),
175
176 Long([u8; LONG_REPORT_LENGTH - 1]),
181}
182
183impl HidppMessage {
184 pub fn read_raw(data: &[u8]) -> Option<Self> {
186 let (&report_id, rest) = data.split_first()?;
187
188 if report_id == SHORT_REPORT_ID
190 && let Some((&payload, [])) = rest.split_first_chunk()
191 {
192 Some(HidppMessage::Short(payload))
193 } else if report_id == LONG_REPORT_ID
194 && let Some((&payload, [])) = rest.split_first_chunk()
195 {
196 Some(HidppMessage::Long(payload))
197 } else {
198 None
199 }
200 }
201
202 pub fn write_raw(&self, buf: &mut [u8]) -> usize {
206 match self {
207 Self::Short(payload) => {
208 buf[0] = SHORT_REPORT_ID;
209 buf[1..SHORT_REPORT_LENGTH].copy_from_slice(payload);
210 SHORT_REPORT_LENGTH
211 }
212 Self::Long(payload) => {
213 buf[0] = LONG_REPORT_ID;
214 buf[1..LONG_REPORT_LENGTH].copy_from_slice(payload);
215 LONG_REPORT_LENGTH
216 }
217 }
218 }
219}
220
221type MessageListener = Arc<dyn Fn(HidppMessage, bool) + Send + Sync + 'static>;
222
223pub struct MessageListenerGuard {
225 message_listeners: Weak<Mutex<HashMap<u32, MessageListener>>>,
226 hdl: u32,
227}
228
229impl Drop for MessageListenerGuard {
230 fn drop(&mut self) {
231 if let Some(message_listeners) = self.message_listeners.upgrade() {
232 message_listeners.lock().unwrap().remove(&self.hdl);
233 }
234 }
235}
236
237pub struct HidppChannel {
239 pub supports_short: bool,
241
242 pub supports_long: bool,
244
245 pub vendor_id: u16,
247
248 pub product_id: u16,
250
251 raw_channel: Arc<dyn RawHidChannel>,
253
254 rotate_software_id: AtomicBool,
256
257 software_id: AtomicU8,
259
260 pending_messages: Arc<Mutex<VecDeque<PendingMessage>>>,
262
263 pending_message_id: AtomicU64,
265
266 message_listeners: Arc<Mutex<HashMap<u32, MessageListener>>>,
269
270 read_thread_close: Option<oneshot::Sender<()>>,
272
273 read_thread_hdl: Option<JoinHandle<()>>,
276}
277
278impl Drop for HidppChannel {
279 fn drop(&mut self) {
280 if let Some(read_thread_close) = self.read_thread_close.take() {
281 let _ = read_thread_close.send(());
286 }
287
288 if let Some(read_thread_hdl) = self.read_thread_hdl.take() {
289 read_thread_hdl.join().unwrap();
290 }
291 }
292}
293
294struct PendingMessage {
296 id: u64,
298
299 response_predicate: Box<dyn Fn(&HidppMessage) -> bool + Send>,
302
303 sender: oneshot::Sender<HidppMessage>,
306}
307
308impl HidppChannel {
309 pub async fn from_raw_channel(raw: impl RawHidChannel) -> Result<Self, ChannelError> {
314 let (supports_short, supports_long) = supports_short_long_hidpp(&raw).await?;
315
316 if !supports_short && !supports_long {
317 return Err(ChannelError::HidppNotSupported);
318 }
319
320 let raw_channel_rc = Arc::new(raw);
321 let pending_messages_rc = Arc::new(Mutex::new(VecDeque::<PendingMessage>::new()));
322 let message_listeners_rc = Arc::new(Mutex::new(HashMap::<u32, MessageListener>::new()));
323
324 let (close_sender, mut close_receiver) = oneshot::channel::<()>();
325
326 let read_thread_hdl = thread::spawn({
327 let raw_channel = Arc::clone(&raw_channel_rc);
328 let pending_messages = Arc::clone(&pending_messages_rc);
329 let message_listeners = Arc::clone(&message_listeners_rc);
330
331 move || {
332 futures::executor::block_on(async {
333 let mut buf = [0u8; MAX_REPORT_LENGTH];
334
335 loop {
336 let res = select! {
337 _ = close_receiver => {
338 break;
339 },
340 res = raw_channel.read_report(&mut buf).fuse() => res
341 };
342
343 let Ok(len) = res else {
344 continue;
345 };
346
347 let Some(msg) = HidppMessage::read_raw(&buf[..len]) else {
348 continue;
349 };
350
351 let mut matched = false;
352 {
353 let mut msgs = pending_messages.lock().unwrap();
354 if let Some(pos) =
355 msgs.iter().position(|elem| (elem.response_predicate)(&msg))
356 {
357 let waiting = msgs.remove(pos).unwrap();
358 let _ = waiting.sender.send(msg);
359 matched = true;
360 }
361 }
362
363 let listeners: Vec<_> = message_listeners
364 .lock()
365 .unwrap()
366 .values()
367 .cloned()
368 .collect();
369 for listener in listeners {
370 listener(msg, matched);
371 }
372 }
373 });
374 }
375 });
376
377 Ok(Self {
378 supports_short,
379 supports_long,
380 vendor_id: raw_channel_rc.vendor_id(),
381 product_id: raw_channel_rc.product_id(),
382 raw_channel: raw_channel_rc,
383 rotate_software_id: AtomicBool::new(false),
384 software_id: AtomicU8::new(0x01),
385 pending_messages: pending_messages_rc,
386 pending_message_id: AtomicU64::new(1),
387 message_listeners: message_listeners_rc,
388 read_thread_close: Some(close_sender),
389 read_thread_hdl: Some(read_thread_hdl),
390 })
391 }
392
393 pub fn set_sw_id(&self, sw_id: U4) {
399 self.software_id.store(sw_id.to_lo(), Ordering::SeqCst);
400 }
401
402 pub fn set_rotating_sw_id(&self, enable: bool) {
411 self.rotate_software_id.store(enable, Ordering::SeqCst);
412 }
413
414 pub fn get_sw_id(&self) -> U4 {
420 if self.rotate_software_id.load(Ordering::SeqCst) {
421 U4::from_lo(
422 self.software_id
423 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |old| {
424 Some(if old & 0x0f == 0x0f {
425 0x01
426 } else {
427 old.wrapping_add(1)
428 })
429 })
430 .unwrap(),
431 )
432 } else {
433 U4::from_lo(self.software_id.load(Ordering::SeqCst))
434 }
435 }
436
437 pub fn supports_msg(&self, msg: &HidppMessage) -> bool {
439 match msg {
440 HidppMessage::Short(_) => self.supports_short,
441 HidppMessage::Long(_) => self.supports_long,
442 }
443 }
444
445 fn normalize_outgoing(&self, msg: HidppMessage) -> HidppMessage {
455 match msg {
456 HidppMessage::Short(payload) if !self.supports_short && self.supports_long => {
457 HidppMessage::Long(short_payload_as_long(&payload))
458 }
459 other => other,
460 }
461 }
462
463 pub async fn send(
472 &self,
473 msg: HidppMessage,
474 response_predicate: impl Fn(&HidppMessage) -> bool + Send + 'static,
475 ) -> Result<HidppMessage, ChannelError> {
476 self.send_with_timeout(msg, response_predicate, SEND_RESPONSE_TIMEOUT)
477 .await
478 }
479
480 pub async fn send_with_timeout(
494 &self,
495 msg: HidppMessage,
496 response_predicate: impl Fn(&HidppMessage) -> bool + Send + 'static,
497 timeout: Duration,
498 ) -> Result<HidppMessage, ChannelError> {
499 let msg = self.normalize_outgoing(msg);
500 if !self.supports_msg(&msg) {
501 return Err(ChannelError::MessageTypeNotSupported);
502 }
503
504 let (sender, receiver) = oneshot::channel::<HidppMessage>();
505 let pending_id = self.pending_message_id.fetch_add(1, Ordering::SeqCst);
506
507 {
508 let mut pending = self.pending_messages.lock().unwrap();
509 pending.retain(|m| !m.sender.is_canceled());
518 pending.push_back(PendingMessage {
519 id: pending_id,
520 response_predicate: Box::new(response_predicate),
521 sender,
522 });
523 }
524
525 let mut request = std::pin::pin!(
529 async {
530 self.send_and_forget(msg).await?;
531 receiver.await.map_err(|_| ChannelError::NoResponse)
532 }
533 .fuse()
534 );
535
536 let result = select! {
537 result = request => result,
538 _ = futures_timer::Delay::new(timeout).fuse() => Err(ChannelError::Timeout),
539 };
540
541 if result.is_err() {
542 self.remove_pending_message(pending_id);
546 }
547
548 result
549 }
550
551 fn remove_pending_message(&self, id: u64) {
552 let mut pending = self.pending_messages.lock().unwrap();
553 if let Some(pos) = pending.iter().position(|msg| msg.id == id) {
554 pending.remove(pos);
555 }
556 }
557
558 pub async fn send_and_forget(&self, msg: HidppMessage) -> Result<(), ChannelError> {
563 let msg = self.normalize_outgoing(msg);
564 if !self.supports_msg(&msg) {
565 return Err(ChannelError::MessageTypeNotSupported);
566 }
567
568 let mut buf = [0u8; LONG_REPORT_LENGTH];
569 let len = msg.write_raw(&mut buf);
570 self.raw_channel
571 .write_report(&buf[..len])
572 .await
573 .map(|_| ())
574 .map_err(ChannelError::Implementation)
575 }
576
577 pub fn add_msg_listener(
582 &self,
583 listener: impl Fn(HidppMessage, bool) + Send + Sync + 'static,
584 ) -> u32 {
585 let mut listeners = self.message_listeners.lock().unwrap();
586
587 let mut rng = rand::rng();
588 let mut hdl = rng.random::<u32>();
589 while listeners.contains_key(&hdl) {
590 hdl = rng.random::<u32>();
591 }
592
593 listeners.insert(hdl, Arc::new(listener));
594 hdl
595 }
596
597 pub fn add_msg_listener_guarded(
600 &self,
601 listener: impl Fn(HidppMessage, bool) + Send + Sync + 'static,
602 ) -> MessageListenerGuard {
603 let hdl = self.add_msg_listener(listener);
604 MessageListenerGuard {
605 message_listeners: Arc::downgrade(&self.message_listeners),
606 hdl,
607 }
608 }
609
610 pub fn remove_msg_listener(&self, hdl: u32) -> bool {
614 self.message_listeners
615 .lock()
616 .unwrap()
617 .remove(&hdl)
618 .is_some()
619 }
620}
621
622#[derive(Debug, Error)]
625#[non_exhaustive]
626pub enum ChannelError {
627 #[error("the HID channel implementation returned an error")]
630 Implementation(#[from] Box<dyn Error + Sync + Send>),
631
632 #[error("the report descriptor could not be parsed")]
634 ReportDescriptor(hidreport::ParserError),
635
636 #[error("the HID channel does not support HID++")]
638 HidppNotSupported,
639
640 #[error("the channel does not support the given HID++ message type")]
643 MessageTypeNotSupported,
644
645 #[error("the device did not respond to the request")]
647 NoResponse,
648
649 #[error("the request timed out before the device responded")]
653 Timeout,
654}
655
656fn short_payload_as_long(payload: &[u8; SHORT_REPORT_LENGTH - 1]) -> [u8; LONG_REPORT_LENGTH - 1] {
662 let mut long = [0u8; LONG_REPORT_LENGTH - 1];
663 long[..payload.len()].copy_from_slice(payload);
664 long
665}
666
667#[cfg(test)]
668mod tests {
669 use super::*;
670 use std::{
671 io,
672 sync::{
673 Arc, Mutex,
674 atomic::{AtomicUsize, Ordering},
675 },
676 time::{Duration, Instant},
677 };
678
679 use crate::{
680 nibble,
681 protocol::v20::{self, ErrorType, Hidpp20Error},
682 };
683
684 #[test]
685 fn short_payload_widens_preserving_header_and_padding() {
686 let short = [0xff, 0x05, 0x1e, 0xaa, 0xbb, 0xcc];
688 let long = short_payload_as_long(&short);
689 assert_eq!(&long[..short.len()], &short[..]); assert!(long[short.len()..].iter().all(|&b| b == 0)); assert_eq!(long.len(), LONG_REPORT_LENGTH - 1);
692 }
693
694 #[test]
695 fn send_returns_response_before_timeout() {
696 futures::executor::block_on(async {
697 let (raw, handle) = MockRawHidChannel::new();
698 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
699
700 let request = short_msg(0x10);
701 let response = short_msg(0x20);
702 handle.queue_response(response);
703
704 let actual = channel
705 .send_with_timeout(
706 request,
707 move |candidate| *candidate == response,
708 Duration::from_secs(1),
709 )
710 .await
711 .unwrap();
712
713 assert_eq!(actual, response);
714 assert_eq!(handle.written_reports().len(), 1);
715 assert_pending_empty(&channel);
716 });
717 }
718
719 #[test]
720 fn send_times_out_and_removes_pending_message() {
721 futures::executor::block_on(async {
722 let (raw, handle) = MockRawHidChannel::new();
723 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
724 let request = short_msg(0x10);
725 let response = short_msg(0x20);
726
727 let started = Instant::now();
728 let err = channel
729 .send_with_timeout(
730 request,
731 move |candidate| *candidate == response,
732 Duration::from_millis(25),
733 )
734 .await
735 .unwrap_err();
736
737 assert!(matches!(err, ChannelError::Timeout));
738 assert!(started.elapsed() < Duration::from_secs(1));
739 assert_eq!(handle.written_reports().len(), 1);
740 assert_pending_empty(&channel);
741 });
742 }
743
744 #[test]
745 fn timeout_removes_only_its_own_pending_message() {
746 futures::executor::block_on(async {
747 let (raw, handle) = MockRawHidChannel::new();
748 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
749
750 let never_answered = short_msg(0x20);
751 let slow_response = short_msg(0x21);
752
753 let timed_out = channel.send_with_timeout(
754 short_msg(0x10),
755 move |candidate| *candidate == never_answered,
756 Duration::from_millis(25),
757 );
758 let answered = channel.send_with_timeout(
759 short_msg(0x11),
760 move |candidate| *candidate == slow_response,
761 Duration::from_secs(1),
762 );
763 let respond_late = async {
766 futures_timer::Delay::new(Duration::from_millis(100)).await;
767 handle.send_incoming(slow_response).await;
768 };
769
770 let (timed_out, answered, ()) = futures::join!(timed_out, answered, respond_late);
771
772 assert!(matches!(timed_out.unwrap_err(), ChannelError::Timeout));
773 assert_eq!(answered.unwrap(), slow_response);
774 assert_pending_empty(&channel);
775 });
776 }
777
778 #[test]
779 fn late_response_after_timeout_is_ignored() {
780 futures::executor::block_on(async {
781 let (raw, handle) = MockRawHidChannel::new();
782 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
783 let events = Arc::new(Mutex::new(Vec::new()));
784 let listener_events = Arc::clone(&events);
785 channel.add_msg_listener(move |msg, matched| {
786 listener_events.lock().unwrap().push((msg, matched));
787 });
788
789 let request = short_msg(0x10);
790 let late_response = short_msg(0x20);
791 let err = channel
792 .send_with_timeout(
793 request,
794 move |candidate| *candidate == late_response,
795 Duration::from_millis(25),
796 )
797 .await
798 .unwrap_err();
799
800 assert!(matches!(err, ChannelError::Timeout));
801 assert_pending_empty(&channel);
802
803 handle.send_incoming(late_response).await;
804 wait_for_event_count(&events, 1).await;
805 assert_eq!(events.lock().unwrap()[0], (late_response, false));
806 assert_pending_empty(&channel);
807
808 let later_request = short_msg(0x30);
809 let later_response = short_msg(0x40);
810 handle.queue_response(later_response);
811 let actual = channel
812 .send_with_timeout(
813 later_request,
814 move |candidate| *candidate == later_response,
815 Duration::from_secs(1),
816 )
817 .await
818 .unwrap();
819
820 assert_eq!(actual, later_response);
821 wait_for_event_count(&events, 2).await;
822 assert_eq!(events.lock().unwrap()[1], (later_response, true));
823 assert_pending_empty(&channel);
824 });
825 }
826
827 #[test]
828 fn send_and_forget_writes_without_pending_message() {
829 futures::executor::block_on(async {
830 let (raw, handle) = MockRawHidChannel::new();
831 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
832
833 channel.send_and_forget(short_msg(0x10)).await.unwrap();
834
835 assert_eq!(handle.written_reports().len(), 1);
836 assert_pending_empty(&channel);
837 });
838 }
839
840 #[test]
841 fn listener_can_remove_another_listener_during_dispatch() {
842 futures::executor::block_on(async {
843 let (raw, handle) = MockRawHidChannel::new();
844 let channel = Arc::new(HidppChannel::from_raw_channel(raw).await.unwrap());
845 let removed_listener_calls = Arc::new(AtomicUsize::new(0));
846 let removing_listener_calls = Arc::new(AtomicUsize::new(0));
847
848 let removed_listener_calls_for_listener = Arc::clone(&removed_listener_calls);
849 let removed_hdl = channel.add_msg_listener(move |_, _| {
850 removed_listener_calls_for_listener.fetch_add(1, Ordering::SeqCst);
851 });
852
853 let channel_for_listener = Arc::clone(&channel);
854 let removing_listener_calls_for_listener = Arc::clone(&removing_listener_calls);
855 channel.add_msg_listener(move |_, _| {
856 removing_listener_calls_for_listener.fetch_add(1, Ordering::SeqCst);
857 channel_for_listener.remove_msg_listener(removed_hdl);
858 });
859
860 handle.send_incoming(short_msg(0x20)).await;
861 wait_for_atomic_count(&removing_listener_calls, 1).await;
862 wait_for_atomic_count(&removed_listener_calls, 1).await;
863
864 handle.send_incoming(short_msg(0x21)).await;
865 wait_for_atomic_count(&removing_listener_calls, 2).await;
866
867 assert_eq!(removed_listener_calls.load(Ordering::SeqCst), 1);
868 });
869 }
870
871 #[test]
881 fn send_v20_matches_response_by_header_ignoring_unrelated_messages() {
882 futures::executor::block_on(async {
883 let (raw, handle) = MockRawHidChannel::new();
884 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
885
886 let header = v20::MessageHeader {
887 device_index: 0x01,
888 feature_index: 0x05,
889 function_id: U4::from_lo(0x2),
890 software_id: U4::from_lo(0x3),
891 };
892 let request = v20::Message::Short(header, [0x00, 0x00, 0x00]);
893 let response = v20::Message::Short(header, [0xaa, 0xbb, 0xcc]);
894
895 let wrong_device = v20::Message::Short(
898 v20::MessageHeader {
899 device_index: 0x02,
900 ..header
901 },
902 [0, 0, 0],
903 );
904 let wrong_feature = v20::Message::Short(
905 v20::MessageHeader {
906 feature_index: 0x06,
907 ..header
908 },
909 [0, 0, 0],
910 );
911 let wrong_sw_id = v20::Message::Short(
912 v20::MessageHeader {
913 software_id: U4::from_lo(0x4),
914 ..header
915 },
916 [0, 0, 0],
917 );
918
919 let send_fut = channel.send_v20(request);
920 let feed_fut = async {
921 handle.send_incoming(wrong_device.into()).await;
922 handle.send_incoming(wrong_feature.into()).await;
923 handle.send_incoming(wrong_sw_id.into()).await;
924 handle.send_incoming(response.into()).await;
925 };
926
927 let (result, ()) = futures::join!(send_fut, feed_fut);
928
929 assert_eq!(result.unwrap(), response);
930 assert_pending_empty(&channel);
931 });
932 }
933
934 #[test]
935 fn send_v20_broadcast_event_does_not_resolve_pending_request() {
936 futures::executor::block_on(async {
937 let (raw, handle) = MockRawHidChannel::new();
938 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
939 let events = Arc::new(Mutex::new(Vec::new()));
940 let listener_events = Arc::clone(&events);
941 channel.add_msg_listener(move |msg, matched| {
942 listener_events.lock().unwrap().push((msg, matched));
943 });
944
945 let header = v20::MessageHeader {
946 device_index: 0x01,
947 feature_index: 0x05,
948 function_id: U4::from_lo(0x2),
949 software_id: U4::from_lo(0x3),
950 };
951 let request = v20::Message::Short(header, [0, 0, 0]);
952 let response = v20::Message::Short(header, [0xaa, 0xbb, 0xcc]);
953
954 let event = v20::Message::Short(
960 v20::MessageHeader {
961 software_id: U4::from_lo(0x0),
962 ..header
963 },
964 [0x01, 0x02, 0x03],
965 );
966
967 let send_fut = channel.send_v20(request);
968 let feed_fut = async {
969 handle.send_incoming(event.into()).await;
970 wait_for_event_count(&events, 1).await;
971 handle.send_incoming(response.into()).await;
972 };
973
974 let (result, ()) = futures::join!(send_fut, feed_fut);
975
976 assert_eq!(result.unwrap(), response);
977 wait_for_event_count(&events, 2).await;
980 let recorded = events.lock().unwrap().clone();
981 assert_eq!(
982 recorded,
983 vec![
984 (HidppMessage::from(event), false),
985 (HidppMessage::from(response), true),
986 ]
987 );
988 assert_pending_empty(&channel);
989 });
990 }
991
992 #[test]
993 fn send_v20_response_may_arrive_as_a_different_report_width() {
994 futures::executor::block_on(async {
995 let (raw, handle) = MockRawHidChannel::new();
996 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
997
998 let header = v20::MessageHeader {
999 device_index: 0x01,
1000 feature_index: 0x05,
1001 function_id: U4::from_lo(0x2),
1002 software_id: U4::from_lo(0x3),
1003 };
1004 let request = v20::Message::Short(header, [0, 0, 0]);
1005 let response = v20::Message::Long(header, [0xaa; 16]);
1010 handle.queue_response(response.into());
1011
1012 let result = channel.send_v20(request).await.unwrap();
1013
1014 assert_eq!(result, response);
1015 assert_pending_empty(&channel);
1016 });
1017 }
1018
1019 #[test]
1020 fn send_v20_error_frame_resolves_to_feature_error() {
1021 futures::executor::block_on(async {
1022 let (raw, handle) = MockRawHidChannel::new();
1023 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
1024
1025 let header = v20::MessageHeader {
1026 device_index: 0x01,
1027 feature_index: 0x05,
1028 function_id: U4::from_lo(0x2),
1029 software_id: U4::from_lo(0x3),
1030 };
1031 let request = v20::Message::Short(header, [0, 0, 0]);
1032 let error_response = v20_error_frame(header, ErrorType::InvalidArgument.into());
1033 handle.queue_response(error_response.into());
1034
1035 let err = channel.send_v20(request).await.unwrap_err();
1036
1037 assert!(matches!(
1038 err,
1039 Hidpp20Error::Feature(ErrorType::InvalidArgument)
1040 ));
1041 assert_pending_empty(&channel);
1042 });
1043 }
1044
1045 #[test]
1046 fn send_v20_error_frame_with_unmapped_code_is_unsupported_response() {
1047 futures::executor::block_on(async {
1048 let (raw, handle) = MockRawHidChannel::new();
1049 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
1050
1051 let header = v20::MessageHeader {
1052 device_index: 0x01,
1053 feature_index: 0x05,
1054 function_id: U4::from_lo(0x2),
1055 software_id: U4::from_lo(0x3),
1056 };
1057 let request = v20::Message::Short(header, [0, 0, 0]);
1058 let error_response = v20_error_frame(header, 0xfe);
1060 handle.queue_response(error_response.into());
1061
1062 let err = channel.send_v20(request).await.unwrap_err();
1063
1064 assert!(matches!(err, Hidpp20Error::UnsupportedResponse));
1065 assert_pending_empty(&channel);
1066 });
1067 }
1068
1069 fn v20_error_frame(request_header: v20::MessageHeader, error_code: u8) -> v20::Message {
1074 let error_header = v20::MessageHeader {
1075 device_index: request_header.device_index,
1076 feature_index: 0xff,
1077 function_id: U4::from_hi(request_header.feature_index),
1078 software_id: U4::from_lo(request_header.feature_index),
1079 };
1080 let mut payload = [0u8; 3];
1081 payload[0] = nibble::combine(request_header.function_id, request_header.software_id);
1082 payload[1] = error_code;
1083 v20::Message::Short(error_header, payload)
1084 }
1085
1086 #[derive(Clone)]
1087 struct MockRawHidHandle {
1088 incoming_tx: async_channel::Sender<Vec<u8>>,
1089 written_reports: Arc<Mutex<Vec<Vec<u8>>>>,
1090 responses_on_write: Arc<Mutex<VecDeque<Vec<u8>>>>,
1091 }
1092
1093 impl MockRawHidHandle {
1094 fn queue_response(&self, msg: HidppMessage) {
1095 self.responses_on_write
1096 .lock()
1097 .unwrap()
1098 .push_back(raw_report(msg));
1099 }
1100
1101 async fn send_incoming(&self, msg: HidppMessage) {
1102 self.incoming_tx.send(raw_report(msg)).await.unwrap();
1103 }
1104
1105 fn written_reports(&self) -> Vec<Vec<u8>> {
1106 self.written_reports.lock().unwrap().clone()
1107 }
1108 }
1109
1110 struct MockRawHidChannel {
1111 incoming_tx: async_channel::Sender<Vec<u8>>,
1112 incoming_rx: async_channel::Receiver<Vec<u8>>,
1113 written_reports: Arc<Mutex<Vec<Vec<u8>>>>,
1114 responses_on_write: Arc<Mutex<VecDeque<Vec<u8>>>>,
1115 }
1116
1117 impl MockRawHidChannel {
1118 fn new() -> (Self, MockRawHidHandle) {
1119 let (incoming_tx, incoming_rx) = async_channel::unbounded();
1120 let written_reports = Arc::new(Mutex::new(Vec::new()));
1121 let responses_on_write = Arc::new(Mutex::new(VecDeque::new()));
1122
1123 let handle = MockRawHidHandle {
1124 incoming_tx: incoming_tx.clone(),
1125 written_reports: Arc::clone(&written_reports),
1126 responses_on_write: Arc::clone(&responses_on_write),
1127 };
1128
1129 (
1130 Self {
1131 incoming_tx,
1132 incoming_rx,
1133 written_reports,
1134 responses_on_write,
1135 },
1136 handle,
1137 )
1138 }
1139 }
1140
1141 #[async_trait]
1142 impl RawHidChannel for MockRawHidChannel {
1143 fn vendor_id(&self) -> u16 {
1144 0x046d
1145 }
1146
1147 fn product_id(&self) -> u16 {
1148 0xc539
1149 }
1150
1151 async fn write_report(&self, src: &[u8]) -> Result<usize, Box<dyn Error + Sync + Send>> {
1152 self.written_reports.lock().unwrap().push(src.to_vec());
1153 let response = self.responses_on_write.lock().unwrap().pop_front();
1154 if let Some(response) = response {
1155 self.incoming_tx.send(response).await.unwrap();
1156 }
1157
1158 Ok(src.len())
1159 }
1160
1161 async fn read_report(&self, buf: &mut [u8]) -> Result<usize, Box<dyn Error + Sync + Send>> {
1162 let report = self.incoming_rx.recv().await.map_err(|_| mock_error())?;
1163 let len = report.len().min(buf.len());
1164 buf[..len].copy_from_slice(&report[..len]);
1165 Ok(len)
1166 }
1167
1168 fn supports_short_long_hidpp(&self) -> Option<(bool, bool)> {
1169 Some((true, true))
1170 }
1171
1172 async fn get_report_descriptor(
1173 &self,
1174 _buf: &mut [u8],
1175 ) -> Result<usize, Box<dyn Error + Sync + Send>> {
1176 unreachable!("mock declares HID++ support")
1177 }
1178 }
1179
1180 fn short_msg(marker: u8) -> HidppMessage {
1181 HidppMessage::Short([0xff, marker, 0x10, marker, marker, marker])
1182 }
1183
1184 fn raw_report(msg: HidppMessage) -> Vec<u8> {
1185 let mut buf = [0u8; LONG_REPORT_LENGTH];
1186 let len = msg.write_raw(&mut buf);
1187 buf[..len].to_vec()
1188 }
1189
1190 fn assert_pending_empty(channel: &HidppChannel) {
1191 assert!(channel.pending_messages.lock().unwrap().is_empty());
1192 }
1193
1194 async fn wait_for_event_count(events: &Arc<Mutex<Vec<(HidppMessage, bool)>>>, count: usize) {
1195 let started = Instant::now();
1196 while started.elapsed() < Duration::from_secs(1) {
1197 if events.lock().unwrap().len() >= count {
1198 return;
1199 }
1200 futures_timer::Delay::new(Duration::from_millis(10)).await;
1201 }
1202
1203 panic!("timed out waiting for {count} listener events");
1204 }
1205
1206 async fn wait_for_atomic_count(count: &AtomicUsize, expected: usize) {
1207 let started = Instant::now();
1208 while started.elapsed() < Duration::from_secs(1) {
1209 if count.load(Ordering::SeqCst) >= expected {
1210 return;
1211 }
1212 futures_timer::Delay::new(Duration::from_millis(10)).await;
1213 }
1214
1215 panic!("timed out waiting for atomic count {expected}");
1216 }
1217
1218 fn mock_error() -> Box<dyn Error + Sync + Send> {
1219 Box::new(io::Error::new(
1220 io::ErrorKind::BrokenPipe,
1221 "mock channel closed",
1222 ))
1223 }
1224}