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 Ok(len) = res else {
380 continue;
381 };
382
383 let Some(msg) = HidppMessage::read_raw(&buf[..len]) else {
384 continue;
385 };
386
387 let mut matched = false;
388 {
389 let mut msgs = pending_messages.lock().unwrap();
390 if let Some(pos) =
391 msgs.iter().position(|elem| (elem.response_predicate)(&msg))
392 {
393 let waiting = msgs.remove(pos).unwrap();
394 let _ = waiting.sender.send(msg);
395 matched = true;
396 }
397 }
398
399 let listeners: Vec<_> = message_listeners
400 .lock()
401 .unwrap()
402 .values()
403 .cloned()
404 .collect();
405 for listener in listeners {
406 listener(msg, matched);
407 }
408 }
409 });
410 }
411 });
412
413 Ok(Self {
414 supports_short,
415 supports_long,
416 vendor_id: raw_channel_rc.vendor_id(),
417 product_id: raw_channel_rc.product_id(),
418 raw_channel: raw_channel_rc,
419 rotate_software_id: AtomicBool::new(false),
420 software_id: AtomicU8::new(0x01),
421 pending_messages: pending_messages_rc,
422 pending_message_id: AtomicU64::new(1),
423 message_listeners: message_listeners_rc,
424 read_thread_close: Some(close_sender),
425 read_thread_hdl: Some(read_thread_hdl),
426 sw_id_lease: None,
427 })
428 }
429
430 pub fn is_connected(&self) -> bool {
432 self.raw_channel.is_connected()
433 }
434
435 pub fn set_sw_id(&self, sw_id: U4) {
441 self.software_id.store(sw_id.to_lo(), Ordering::SeqCst);
442 }
443
444 pub fn set_rotating_sw_id(&self, enable: bool) {
453 self.rotate_software_id.store(enable, Ordering::SeqCst);
454 }
455
456 pub fn set_sw_id_lease(&mut self, id: u8, free: fn(u8)) {
463 self.sw_id_lease = Some((id, free));
464 }
465
466 pub fn get_sw_id(&self) -> U4 {
472 if self.rotate_software_id.load(Ordering::SeqCst) {
473 U4::from_lo(
474 self.software_id
475 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |old| {
476 Some(if old & 0x0f == 0x0f {
477 0x01
478 } else {
479 old.wrapping_add(1)
480 })
481 })
482 .unwrap(),
483 )
484 } else {
485 U4::from_lo(self.software_id.load(Ordering::SeqCst))
486 }
487 }
488
489 pub fn supports_msg(&self, msg: &HidppMessage) -> bool {
491 match msg {
492 HidppMessage::Short(_) => self.supports_short,
493 HidppMessage::Long(_) => self.supports_long,
494 }
495 }
496
497 fn normalize_outgoing(&self, msg: HidppMessage) -> HidppMessage {
507 match msg {
508 HidppMessage::Short(payload) if !self.supports_short && self.supports_long => {
509 HidppMessage::Long(short_payload_as_long(&payload))
510 }
511 other => other,
512 }
513 }
514
515 pub async fn send(
524 &self,
525 msg: HidppMessage,
526 response_predicate: impl Fn(&HidppMessage) -> bool + Send + 'static,
527 ) -> Result<HidppMessage, ChannelError> {
528 self.send_with_timeout(msg, response_predicate, SEND_RESPONSE_TIMEOUT)
529 .await
530 }
531
532 pub async fn send_with_timeout(
546 &self,
547 msg: HidppMessage,
548 response_predicate: impl Fn(&HidppMessage) -> bool + Send + 'static,
549 timeout: Duration,
550 ) -> Result<HidppMessage, ChannelError> {
551 let msg = self.normalize_outgoing(msg);
552 if !self.supports_msg(&msg) {
553 return Err(ChannelError::MessageTypeNotSupported);
554 }
555
556 let (dev, feat, func) = msg.header();
560 trace!(dev, feat, func, "hidpp request");
561
562 let (sender, receiver) = oneshot::channel::<HidppMessage>();
563 let pending_id = self.pending_message_id.fetch_add(1, Ordering::SeqCst);
564
565 {
566 let mut pending = self.pending_messages.lock().unwrap();
567 pending.retain(|m| !m.sender.is_canceled());
576 pending.push_back(PendingMessage {
577 id: pending_id,
578 response_predicate: Box::new(response_predicate),
579 sender,
580 });
581 }
582
583 let mut request = std::pin::pin!(
587 async {
588 self.send_and_forget(msg).await?;
589 receiver.await.map_err(|_| ChannelError::NoResponse)
590 }
591 .fuse()
592 );
593
594 let result = select! {
595 result = request => result,
596 _ = futures_timer::Delay::new(timeout).fuse() => Err(ChannelError::Timeout),
597 };
598
599 match &result {
600 Ok(_) => trace!(dev, feat, "hidpp response"),
601 Err(e) => trace!(dev, feat, error = ?e, "hidpp no response"),
602 }
603
604 if result.is_err() {
605 self.remove_pending_message(pending_id);
609 }
610
611 result
612 }
613
614 fn remove_pending_message(&self, id: u64) {
615 let mut pending = self.pending_messages.lock().unwrap();
616 if let Some(pos) = pending.iter().position(|msg| msg.id == id) {
617 pending.remove(pos);
618 }
619 }
620
621 pub async fn send_and_forget(&self, msg: HidppMessage) -> Result<(), ChannelError> {
626 let msg = self.normalize_outgoing(msg);
627 if !self.supports_msg(&msg) {
628 return Err(ChannelError::MessageTypeNotSupported);
629 }
630
631 let mut buf = [0u8; LONG_REPORT_LENGTH];
632 let len = msg.write_raw(&mut buf);
633 self.raw_channel
634 .write_report(&buf[..len])
635 .await
636 .map(|_| ())
637 .map_err(ChannelError::Implementation)
638 }
639
640 pub async fn write_raw_report(&self, report: &[u8]) -> Result<usize, ChannelError> {
648 self.write_raw_report_with_timeout(report, SEND_RESPONSE_TIMEOUT)
649 .await
650 }
651
652 async fn write_raw_report_with_timeout(
653 &self,
654 report: &[u8],
655 timeout: Duration,
656 ) -> Result<usize, ChannelError> {
657 if !(1..=MAX_RAW_REPORT_LENGTH).contains(&report.len()) {
658 return Err(ChannelError::InvalidRawReportLength(report.len()));
659 }
660
661 let mut write = std::pin::pin!(self.raw_channel.write_report(report).fuse());
662 select! {
663 result = write => result.map_err(ChannelError::Implementation),
664 _ = futures_timer::Delay::new(timeout).fuse() => Err(ChannelError::Timeout),
665 }
666 }
667
668 pub fn add_msg_listener(
673 &self,
674 listener: impl Fn(HidppMessage, bool) + Send + Sync + 'static,
675 ) -> u32 {
676 let mut listeners = self.message_listeners.lock().unwrap();
677
678 let mut rng = rand::rng();
679 let mut hdl = rng.random::<u32>();
680 while listeners.contains_key(&hdl) {
681 hdl = rng.random::<u32>();
682 }
683
684 listeners.insert(hdl, Arc::new(listener));
685 hdl
686 }
687
688 pub fn add_msg_listener_guarded(
691 &self,
692 listener: impl Fn(HidppMessage, bool) + Send + Sync + 'static,
693 ) -> MessageListenerGuard {
694 let hdl = self.add_msg_listener(listener);
695 MessageListenerGuard {
696 message_listeners: Arc::downgrade(&self.message_listeners),
697 hdl,
698 }
699 }
700
701 pub fn remove_msg_listener(&self, hdl: u32) -> bool {
705 self.message_listeners
706 .lock()
707 .unwrap()
708 .remove(&hdl)
709 .is_some()
710 }
711}
712
713#[derive(Debug, Error)]
716#[non_exhaustive]
717pub enum ChannelError {
718 #[error("the HID channel implementation returned an error")]
721 Implementation(#[from] Box<dyn Error + Sync + Send>),
722
723 #[error("the report descriptor could not be parsed")]
725 ReportDescriptor(hidreport::ParserError),
726
727 #[error("the HID channel does not support HID++")]
729 HidppNotSupported,
730
731 #[error("the channel does not support the given HID++ message type")]
734 MessageTypeNotSupported,
735
736 #[error("raw HID reports must contain 1..=64 bytes, got {0}")]
738 InvalidRawReportLength(usize),
739
740 #[error("the device did not respond to the request")]
742 NoResponse,
743
744 #[error("the HID channel operation timed out")]
750 Timeout,
751}
752
753fn short_payload_as_long(payload: &[u8; SHORT_REPORT_LENGTH - 1]) -> [u8; LONG_REPORT_LENGTH - 1] {
759 let mut long = [0u8; LONG_REPORT_LENGTH - 1];
760 long[..payload.len()].copy_from_slice(payload);
761 long
762}
763
764#[cfg(test)]
765pub(crate) mod tests {
766 use super::*;
767 use std::{
768 io,
769 sync::{
770 Arc, Mutex,
771 atomic::{AtomicBool, AtomicUsize, Ordering},
772 },
773 time::{Duration, Instant},
774 };
775
776 use crate::{
777 nibble,
778 protocol::v20::{self, ErrorType, Hidpp20Error},
779 };
780
781 #[test]
782 fn short_payload_widens_preserving_header_and_padding() {
783 let short = [0xff, 0x05, 0x1e, 0xaa, 0xbb, 0xcc];
785 let long = short_payload_as_long(&short);
786 assert_eq!(&long[..short.len()], &short[..]); assert!(long[short.len()..].iter().all(|&b| b == 0)); assert_eq!(long.len(), LONG_REPORT_LENGTH - 1);
789 }
790
791 #[test]
792 fn send_returns_response_before_timeout() {
793 futures::executor::block_on(async {
794 let (raw, handle) = MockRawHidChannel::new();
795 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
796
797 let request = short_msg(0x10);
798 let response = short_msg(0x20);
799 handle.queue_response(response);
800
801 let actual = channel
802 .send_with_timeout(
803 request,
804 move |candidate| *candidate == response,
805 Duration::from_secs(1),
806 )
807 .await
808 .unwrap();
809
810 assert_eq!(actual, response);
811 assert_eq!(handle.written_reports().len(), 1);
812 assert_pending_empty(&channel);
813 });
814 }
815
816 #[test]
817 fn send_times_out_and_removes_pending_message() {
818 futures::executor::block_on(async {
819 let (raw, handle) = MockRawHidChannel::new();
820 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
821 let request = short_msg(0x10);
822 let response = short_msg(0x20);
823
824 let started = Instant::now();
825 let err = channel
826 .send_with_timeout(
827 request,
828 move |candidate| *candidate == response,
829 Duration::from_millis(25),
830 )
831 .await
832 .unwrap_err();
833
834 assert!(matches!(err, ChannelError::Timeout));
835 assert!(started.elapsed() < Duration::from_secs(1));
836 assert_eq!(handle.written_reports().len(), 1);
837 assert_pending_empty(&channel);
838 });
839 }
840
841 #[test]
842 fn timeout_removes_only_its_own_pending_message() {
843 futures::executor::block_on(async {
844 let (raw, handle) = MockRawHidChannel::new();
845 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
846
847 let never_answered = short_msg(0x20);
848 let slow_response = short_msg(0x21);
849
850 let timed_out = channel.send_with_timeout(
851 short_msg(0x10),
852 move |candidate| *candidate == never_answered,
853 Duration::from_millis(25),
854 );
855 let answered = channel.send_with_timeout(
856 short_msg(0x11),
857 move |candidate| *candidate == slow_response,
858 Duration::from_secs(1),
859 );
860 let respond_late = async {
863 futures_timer::Delay::new(Duration::from_millis(100)).await;
864 handle.send_incoming(slow_response).await;
865 };
866
867 let (timed_out, answered, ()) = futures::join!(timed_out, answered, respond_late);
868
869 assert!(matches!(timed_out.unwrap_err(), ChannelError::Timeout));
870 assert_eq!(answered.unwrap(), slow_response);
871 assert_pending_empty(&channel);
872 });
873 }
874
875 #[test]
876 fn late_response_after_timeout_is_ignored() {
877 futures::executor::block_on(async {
878 let (raw, handle) = MockRawHidChannel::new();
879 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
880 let events = Arc::new(Mutex::new(Vec::new()));
881 let listener_events = Arc::clone(&events);
882 channel.add_msg_listener(move |msg, matched| {
883 listener_events.lock().unwrap().push((msg, matched));
884 });
885
886 let request = short_msg(0x10);
887 let late_response = short_msg(0x20);
888 let err = channel
889 .send_with_timeout(
890 request,
891 move |candidate| *candidate == late_response,
892 Duration::from_millis(25),
893 )
894 .await
895 .unwrap_err();
896
897 assert!(matches!(err, ChannelError::Timeout));
898 assert_pending_empty(&channel);
899
900 handle.send_incoming(late_response).await;
901 wait_for_event_count(&events, 1).await;
902 assert_eq!(events.lock().unwrap()[0], (late_response, false));
903 assert_pending_empty(&channel);
904
905 let later_request = short_msg(0x30);
906 let later_response = short_msg(0x40);
907 handle.queue_response(later_response);
908 let actual = channel
909 .send_with_timeout(
910 later_request,
911 move |candidate| *candidate == later_response,
912 Duration::from_secs(1),
913 )
914 .await
915 .unwrap();
916
917 assert_eq!(actual, later_response);
918 wait_for_event_count(&events, 2).await;
919 assert_eq!(events.lock().unwrap()[1], (later_response, true));
920 assert_pending_empty(&channel);
921 });
922 }
923
924 #[test]
925 fn send_and_forget_writes_without_pending_message() {
926 futures::executor::block_on(async {
927 let (raw, handle) = MockRawHidChannel::new();
928 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
929
930 channel.send_and_forget(short_msg(0x10)).await.unwrap();
931
932 assert_eq!(handle.written_reports().len(), 1);
933 assert_pending_empty(&channel);
934 });
935 }
936
937 #[test]
938 fn raw_report_write_forwards_exact_bytes_and_length() {
939 futures::executor::block_on(async {
940 let (raw, handle) = MockRawHidChannel::new();
941 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
942 let report = [0x12; MAX_RAW_REPORT_LENGTH];
943
944 let written = channel.write_raw_report(&report).await.unwrap();
945
946 assert_eq!(written, report.len());
947 assert_eq!(handle.written_reports(), [report.to_vec()]);
948 });
949 }
950
951 #[test]
952 fn raw_report_write_rejects_empty_and_oversized_inputs_without_io() {
953 futures::executor::block_on(async {
954 let (raw, handle) = MockRawHidChannel::new();
955 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
956
957 let empty = channel.write_raw_report(&[]).await.unwrap_err();
958 let oversized = channel
959 .write_raw_report(&[0; MAX_RAW_REPORT_LENGTH + 1])
960 .await
961 .unwrap_err();
962
963 assert!(matches!(empty, ChannelError::InvalidRawReportLength(0)));
964 assert!(matches!(
965 oversized,
966 ChannelError::InvalidRawReportLength(65)
967 ));
968 assert!(handle.written_reports().is_empty());
969 });
970 }
971
972 #[test]
973 fn raw_report_write_times_out_when_the_transport_parks() {
974 futures::executor::block_on(async {
975 let (raw, handle) = MockRawHidChannel::new();
976 handle.park_writes();
977 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
978 let started = Instant::now();
979
980 let error = channel
981 .write_raw_report_with_timeout(&[LONG_REPORT_ID], Duration::from_millis(25))
982 .await
983 .unwrap_err();
984
985 assert!(matches!(error, ChannelError::Timeout));
986 assert!(started.elapsed() < Duration::from_secs(1));
987 });
988 }
989
990 #[test]
991 fn listener_can_remove_another_listener_during_dispatch() {
992 futures::executor::block_on(async {
993 let (raw, handle) = MockRawHidChannel::new();
994 let channel = Arc::new(HidppChannel::from_raw_channel(raw).await.unwrap());
995 let removed_listener_calls = Arc::new(AtomicUsize::new(0));
996 let removing_listener_calls = Arc::new(AtomicUsize::new(0));
997
998 let removed_listener_calls_for_listener = Arc::clone(&removed_listener_calls);
999 let removed_hdl = channel.add_msg_listener(move |_, _| {
1000 removed_listener_calls_for_listener.fetch_add(1, Ordering::SeqCst);
1001 });
1002
1003 let channel_for_listener = Arc::clone(&channel);
1004 let removing_listener_calls_for_listener = Arc::clone(&removing_listener_calls);
1005 channel.add_msg_listener(move |_, _| {
1006 removing_listener_calls_for_listener.fetch_add(1, Ordering::SeqCst);
1007 channel_for_listener.remove_msg_listener(removed_hdl);
1008 });
1009
1010 handle.send_incoming(short_msg(0x20)).await;
1011 wait_for_atomic_count(&removing_listener_calls, 1).await;
1012 wait_for_atomic_count(&removed_listener_calls, 1).await;
1013
1014 handle.send_incoming(short_msg(0x21)).await;
1015 wait_for_atomic_count(&removing_listener_calls, 2).await;
1016
1017 assert_eq!(removed_listener_calls.load(Ordering::SeqCst), 1);
1018 });
1019 }
1020
1021 #[test]
1031 fn send_v20_matches_response_by_header_ignoring_unrelated_messages() {
1032 futures::executor::block_on(async {
1033 let (raw, handle) = MockRawHidChannel::new();
1034 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
1035
1036 let header = v20::MessageHeader {
1037 device_index: 0x01,
1038 feature_index: 0x05,
1039 function_id: U4::from_lo(0x2),
1040 software_id: U4::from_lo(0x3),
1041 };
1042 let request = v20::Message::Short(header, [0x00, 0x00, 0x00]);
1043 let response = v20::Message::Short(header, [0xaa, 0xbb, 0xcc]);
1044
1045 let wrong_device = v20::Message::Short(
1048 v20::MessageHeader {
1049 device_index: 0x02,
1050 ..header
1051 },
1052 [0, 0, 0],
1053 );
1054 let wrong_feature = v20::Message::Short(
1055 v20::MessageHeader {
1056 feature_index: 0x06,
1057 ..header
1058 },
1059 [0, 0, 0],
1060 );
1061 let wrong_sw_id = v20::Message::Short(
1062 v20::MessageHeader {
1063 software_id: U4::from_lo(0x4),
1064 ..header
1065 },
1066 [0, 0, 0],
1067 );
1068
1069 let send_fut = channel.send_v20(request);
1070 let feed_fut = async {
1071 handle.send_incoming(wrong_device.into()).await;
1072 handle.send_incoming(wrong_feature.into()).await;
1073 handle.send_incoming(wrong_sw_id.into()).await;
1074 handle.send_incoming(response.into()).await;
1075 };
1076
1077 let (result, ()) = futures::join!(send_fut, feed_fut);
1078
1079 assert_eq!(result.unwrap(), response);
1080 assert_pending_empty(&channel);
1081 });
1082 }
1083
1084 #[test]
1085 fn send_v20_broadcast_event_does_not_resolve_pending_request() {
1086 futures::executor::block_on(async {
1087 let (raw, handle) = MockRawHidChannel::new();
1088 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
1089 let events = Arc::new(Mutex::new(Vec::new()));
1090 let listener_events = Arc::clone(&events);
1091 channel.add_msg_listener(move |msg, matched| {
1092 listener_events.lock().unwrap().push((msg, matched));
1093 });
1094
1095 let header = v20::MessageHeader {
1096 device_index: 0x01,
1097 feature_index: 0x05,
1098 function_id: U4::from_lo(0x2),
1099 software_id: U4::from_lo(0x3),
1100 };
1101 let request = v20::Message::Short(header, [0, 0, 0]);
1102 let response = v20::Message::Short(header, [0xaa, 0xbb, 0xcc]);
1103
1104 let event = v20::Message::Short(
1110 v20::MessageHeader {
1111 software_id: U4::from_lo(0x0),
1112 ..header
1113 },
1114 [0x01, 0x02, 0x03],
1115 );
1116
1117 let send_fut = channel.send_v20(request);
1118 let feed_fut = async {
1119 handle.send_incoming(event.into()).await;
1120 wait_for_event_count(&events, 1).await;
1121 handle.send_incoming(response.into()).await;
1122 };
1123
1124 let (result, ()) = futures::join!(send_fut, feed_fut);
1125
1126 assert_eq!(result.unwrap(), response);
1127 wait_for_event_count(&events, 2).await;
1130 let recorded = events.lock().unwrap().clone();
1131 assert_eq!(
1132 recorded,
1133 vec![
1134 (HidppMessage::from(event), false),
1135 (HidppMessage::from(response), true),
1136 ]
1137 );
1138 assert_pending_empty(&channel);
1139 });
1140 }
1141
1142 #[test]
1143 fn send_v20_response_may_arrive_as_a_different_report_width() {
1144 futures::executor::block_on(async {
1145 let (raw, handle) = MockRawHidChannel::new();
1146 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
1147
1148 let header = v20::MessageHeader {
1149 device_index: 0x01,
1150 feature_index: 0x05,
1151 function_id: U4::from_lo(0x2),
1152 software_id: U4::from_lo(0x3),
1153 };
1154 let request = v20::Message::Short(header, [0, 0, 0]);
1155 let response = v20::Message::Long(header, [0xaa; 16]);
1160 handle.queue_response(response.into());
1161
1162 let result = channel.send_v20(request).await.unwrap();
1163
1164 assert_eq!(result, response);
1165 assert_pending_empty(&channel);
1166 });
1167 }
1168
1169 #[test]
1170 fn send_v20_error_frame_resolves_to_feature_error() {
1171 futures::executor::block_on(async {
1172 let (raw, handle) = MockRawHidChannel::new();
1173 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
1174
1175 let header = v20::MessageHeader {
1176 device_index: 0x01,
1177 feature_index: 0x05,
1178 function_id: U4::from_lo(0x2),
1179 software_id: U4::from_lo(0x3),
1180 };
1181 let request = v20::Message::Short(header, [0, 0, 0]);
1182 let error_response = v20_error_frame(header, ErrorType::InvalidArgument.into());
1183 handle.queue_response(error_response.into());
1184
1185 let err = channel.send_v20(request).await.unwrap_err();
1186
1187 assert!(matches!(
1188 err,
1189 Hidpp20Error::Feature(ErrorType::InvalidArgument)
1190 ));
1191 assert_pending_empty(&channel);
1192 });
1193 }
1194
1195 #[test]
1196 fn send_v20_error_frame_with_unmapped_code_is_unsupported_response() {
1197 futures::executor::block_on(async {
1198 let (raw, handle) = MockRawHidChannel::new();
1199 let channel = HidppChannel::from_raw_channel(raw).await.unwrap();
1200
1201 let header = v20::MessageHeader {
1202 device_index: 0x01,
1203 feature_index: 0x05,
1204 function_id: U4::from_lo(0x2),
1205 software_id: U4::from_lo(0x3),
1206 };
1207 let request = v20::Message::Short(header, [0, 0, 0]);
1208 let error_response = v20_error_frame(header, 0xfe);
1210 handle.queue_response(error_response.into());
1211
1212 let err = channel.send_v20(request).await.unwrap_err();
1213
1214 assert!(matches!(err, Hidpp20Error::UnsupportedResponse));
1215 assert_pending_empty(&channel);
1216 });
1217 }
1218
1219 fn v20_error_frame(request_header: v20::MessageHeader, error_code: u8) -> v20::Message {
1224 let error_header = v20::MessageHeader {
1225 device_index: request_header.device_index,
1226 feature_index: 0xff,
1227 function_id: U4::from_hi(request_header.feature_index),
1228 software_id: U4::from_lo(request_header.feature_index),
1229 };
1230 let mut payload = [0u8; 3];
1231 payload[0] = nibble::combine(request_header.function_id, request_header.software_id);
1232 payload[1] = error_code;
1233 v20::Message::Short(error_header, payload)
1234 }
1235
1236 #[derive(Clone)]
1237 pub(crate) struct MockRawHidHandle {
1238 incoming_tx: async_channel::Sender<Vec<u8>>,
1239 written_reports: Arc<Mutex<Vec<Vec<u8>>>>,
1240 responses_on_write: Arc<Mutex<VecDeque<Vec<u8>>>>,
1241 park_writes: Arc<AtomicBool>,
1242 }
1243
1244 impl MockRawHidHandle {
1245 pub(crate) fn queue_response(&self, msg: HidppMessage) {
1246 self.responses_on_write
1247 .lock()
1248 .unwrap()
1249 .push_back(raw_report(msg));
1250 }
1251
1252 async fn send_incoming(&self, msg: HidppMessage) {
1253 self.incoming_tx.send(raw_report(msg)).await.unwrap();
1254 }
1255
1256 pub(crate) fn written_reports(&self) -> Vec<Vec<u8>> {
1257 self.written_reports.lock().unwrap().clone()
1258 }
1259
1260 fn park_writes(&self) {
1261 self.park_writes.store(true, Ordering::SeqCst);
1262 }
1263 }
1264
1265 pub(crate) struct MockRawHidChannel {
1266 incoming_tx: async_channel::Sender<Vec<u8>>,
1267 incoming_rx: async_channel::Receiver<Vec<u8>>,
1268 written_reports: Arc<Mutex<Vec<Vec<u8>>>>,
1269 responses_on_write: Arc<Mutex<VecDeque<Vec<u8>>>>,
1270 park_writes: Arc<AtomicBool>,
1271 }
1272
1273 impl MockRawHidChannel {
1274 pub(crate) fn new() -> (Self, MockRawHidHandle) {
1275 let (incoming_tx, incoming_rx) = async_channel::unbounded();
1276 let written_reports = Arc::new(Mutex::new(Vec::new()));
1277 let responses_on_write = Arc::new(Mutex::new(VecDeque::new()));
1278 let park_writes = Arc::new(AtomicBool::new(false));
1279
1280 let handle = MockRawHidHandle {
1281 incoming_tx: incoming_tx.clone(),
1282 written_reports: Arc::clone(&written_reports),
1283 responses_on_write: Arc::clone(&responses_on_write),
1284 park_writes: Arc::clone(&park_writes),
1285 };
1286
1287 (
1288 Self {
1289 incoming_tx,
1290 incoming_rx,
1291 written_reports,
1292 responses_on_write,
1293 park_writes,
1294 },
1295 handle,
1296 )
1297 }
1298 }
1299
1300 #[async_trait]
1301 impl RawHidChannel for MockRawHidChannel {
1302 fn vendor_id(&self) -> u16 {
1303 0x046d
1304 }
1305
1306 fn product_id(&self) -> u16 {
1307 0xc539
1308 }
1309
1310 async fn write_report(&self, src: &[u8]) -> Result<usize, Box<dyn Error + Sync + Send>> {
1311 self.written_reports.lock().unwrap().push(src.to_vec());
1312 if self.park_writes.load(Ordering::SeqCst) {
1313 return std::future::pending().await;
1314 }
1315 let response = self.responses_on_write.lock().unwrap().pop_front();
1316 if let Some(response) = response {
1317 self.incoming_tx.send(response).await.unwrap();
1318 }
1319
1320 Ok(src.len())
1321 }
1322
1323 async fn read_report(&self, buf: &mut [u8]) -> Result<usize, Box<dyn Error + Sync + Send>> {
1324 let report = self.incoming_rx.recv().await.map_err(|_| mock_error())?;
1325 let len = report.len().min(buf.len());
1326 buf[..len].copy_from_slice(&report[..len]);
1327 Ok(len)
1328 }
1329
1330 fn supports_short_long_hidpp(&self) -> Option<(bool, bool)> {
1331 Some((true, true))
1332 }
1333
1334 async fn get_report_descriptor(
1335 &self,
1336 _buf: &mut [u8],
1337 ) -> Result<usize, Box<dyn Error + Sync + Send>> {
1338 unreachable!("mock declares HID++ support")
1339 }
1340 }
1341
1342 fn short_msg(marker: u8) -> HidppMessage {
1343 HidppMessage::Short([0xff, marker, 0x10, marker, marker, marker])
1344 }
1345
1346 fn raw_report(msg: HidppMessage) -> Vec<u8> {
1347 let mut buf = [0u8; LONG_REPORT_LENGTH];
1348 let len = msg.write_raw(&mut buf);
1349 buf[..len].to_vec()
1350 }
1351
1352 fn assert_pending_empty(channel: &HidppChannel) {
1353 assert!(channel.pending_messages.lock().unwrap().is_empty());
1354 }
1355
1356 async fn wait_for_event_count(events: &Arc<Mutex<Vec<(HidppMessage, bool)>>>, count: usize) {
1357 let started = Instant::now();
1358 while started.elapsed() < Duration::from_secs(1) {
1359 if events.lock().unwrap().len() >= count {
1360 return;
1361 }
1362 futures_timer::Delay::new(Duration::from_millis(10)).await;
1363 }
1364
1365 panic!("timed out waiting for {count} listener events");
1366 }
1367
1368 async fn wait_for_atomic_count(count: &AtomicUsize, expected: usize) {
1369 let started = Instant::now();
1370 while started.elapsed() < Duration::from_secs(1) {
1371 if count.load(Ordering::SeqCst) >= expected {
1372 return;
1373 }
1374 futures_timer::Delay::new(Duration::from_millis(10)).await;
1375 }
1376
1377 panic!("timed out waiting for atomic count {expected}");
1378 }
1379
1380 fn mock_error() -> Box<dyn Error + Sync + Send> {
1381 Box::new(io::Error::new(
1382 io::ErrorKind::BrokenPipe,
1383 "mock channel closed",
1384 ))
1385 }
1386}