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