1pub mod error;
44pub mod ffi;
45
46pub use crate::error::{Error, Result};
47
48use std::borrow::Cow;
49use std::ffi::{CStr, CString};
50use std::marker::PhantomData;
51use std::mem::MaybeUninit;
52use std::os::raw::c_char;
53use std::ptr::{self, NonNull};
54use std::rc::Rc;
55
56pub struct Context {
61 raw: NonNull<ffi::nfc_context>,
62 _not_send_sync: PhantomData<Rc<()>>,
63}
64
65pub struct Device<'ctx> {
70 raw: NonNull<ffi::nfc_device>,
71 _context: PhantomData<&'ctx Context>,
72 _not_send_sync: PhantomData<Rc<()>>,
73}
74
75#[derive(Clone, Copy, Debug, Eq, PartialEq)]
80pub struct TimedResponse {
81 pub received: usize,
83 pub cycles: u32,
85}
86
87impl Context {
88 pub fn new() -> Result<Self> {
93 let mut raw = ptr::null_mut();
94 unsafe {
95 ffi::nfc_init(&mut raw);
96 }
97
98 let raw = NonNull::new(raw).ok_or_else(|| Error::new("libnfc returned a null context"))?;
99
100 Ok(Self {
101 raw,
102 _not_send_sync: PhantomData,
103 })
104 }
105
106 pub fn as_ptr(&self) -> *mut ffi::nfc_context {
110 self.raw.as_ptr()
111 }
112
113 pub fn open(&self, connstring: Option<&str>) -> Result<Device<'_>> {
120 let connstring = match connstring {
121 Some(value) => Some(
122 CString::new(value)
123 .map_err(|_| Error::new("connection string contains an interior NUL byte"))?,
124 ),
125 None => None,
126 };
127
128 let raw = unsafe {
129 ffi::nfc_open(
130 self.raw.as_ptr(),
131 connstring.as_ref().map_or(ptr::null(), |value| value.as_ptr()),
132 )
133 };
134
135 let raw = NonNull::new(raw).ok_or_else(|| Error::new("libnfc could not open an NFC device"))?;
136
137 Ok(Device {
138 raw,
139 _context: PhantomData,
140 _not_send_sync: PhantomData,
141 })
142 }
143
144 pub fn list_devices(&self, max: usize) -> Result<Vec<String>> {
149 if max == 0 {
150 return Ok(Vec::new());
151 }
152
153 let mut connstrings = vec![[0 as c_char; ffi::NFC_BUFSIZE_CONNSTRING]; max];
154 let count = unsafe {
155 ffi::nfc_list_devices(self.raw.as_ptr(), connstrings.as_mut_ptr(), connstrings.len())
156 };
157
158 Ok(connstrings
159 .into_iter()
160 .take(count)
161 .map(|connstring| c_char_array_to_string(&connstring))
162 .collect())
163 }
164}
165
166impl Drop for Context {
167 fn drop(&mut self) {
168 unsafe {
169 ffi::nfc_exit(self.raw.as_ptr());
170 }
171 }
172}
173
174impl Device<'_> {
175 pub fn as_ptr(&self) -> *mut ffi::nfc_device {
179 self.raw.as_ptr()
180 }
181
182 pub fn name_cstr(&self) -> &CStr {
187 unsafe { CStr::from_ptr(ffi::nfc_device_get_name(self.raw.as_ptr())) }
188 }
189
190 pub fn name(&self) -> Cow<'_, str> {
192 self.name_cstr().to_string_lossy()
193 }
194
195 pub fn connstring_cstr(&self) -> &CStr {
197 unsafe { CStr::from_ptr(ffi::nfc_device_get_connstring(self.raw.as_ptr())) }
198 }
199
200 pub fn connstring(&self) -> Cow<'_, str> {
202 self.connstring_cstr().to_string_lossy()
203 }
204
205 pub fn last_error_code(&self) -> i32 {
210 unsafe { ffi::nfc_device_get_last_error(self.raw.as_ptr()) }
211 }
212
213 pub fn last_error_message(&self) -> Cow<'_, str> {
215 unsafe { CStr::from_ptr(ffi::nfc_strerror(self.raw.as_ptr())).to_string_lossy() }
216 }
217
218 pub fn information_about(&self) -> Result<String> {
223 let mut buffer = ptr::null_mut();
224 check_code(
225 self.raw.as_ptr(),
226 unsafe { ffi::nfc_device_get_information_about(self.raw.as_ptr(), &mut buffer) },
227 )?;
228
229 unsafe { owned_libnfc_string(buffer) }
230 }
231
232 pub fn supported_modulations(
237 &self,
238 mode: ffi::nfc_mode,
239 ) -> Result<Vec<ffi::nfc_modulation_type>> {
240 let mut values = ptr::null();
241 check_code(
242 self.raw.as_ptr(),
243 unsafe { ffi::nfc_device_get_supported_modulation(self.raw.as_ptr(), mode, &mut values) },
244 )?;
245
246 decode_modulation_types(values)
247 }
248
249 pub fn supported_baud_rates(
251 &self,
252 modulation_type: ffi::nfc_modulation_type,
253 ) -> Result<Vec<ffi::nfc_baud_rate>> {
254 let mut values = ptr::null();
255 check_code(
256 self.raw.as_ptr(),
257 unsafe {
258 ffi::nfc_device_get_supported_baud_rate(self.raw.as_ptr(), modulation_type, &mut values)
259 },
260 )?;
261
262 decode_baud_rates(values)
263 }
264
265 pub fn supported_target_baud_rates(
267 &self,
268 modulation_type: ffi::nfc_modulation_type,
269 ) -> Result<Vec<ffi::nfc_baud_rate>> {
270 let mut values = ptr::null();
271 check_code(
272 self.raw.as_ptr(),
273 unsafe {
274 ffi::nfc_device_get_supported_baud_rate_target_mode(
275 self.raw.as_ptr(),
276 modulation_type,
277 &mut values,
278 )
279 },
280 )?;
281
282 decode_baud_rates(values)
283 }
284
285 pub fn set_property_int(&mut self, property: ffi::nfc_property, value: i32) -> Result<()> {
289 check_code(
290 self.raw.as_ptr(),
291 unsafe { ffi::nfc_device_set_property_int(self.raw.as_ptr(), property, value) },
292 )?;
293 Ok(())
294 }
295
296 pub fn set_property_bool(&mut self, property: ffi::nfc_property, enabled: bool) -> Result<()> {
300 check_code(
301 self.raw.as_ptr(),
302 unsafe { ffi::nfc_device_set_property_bool(self.raw.as_ptr(), property, enabled) },
303 )?;
304 Ok(())
305 }
306
307 pub fn initiator_init(&mut self) -> Result<()> {
312 check_code(self.raw.as_ptr(), unsafe { ffi::nfc_initiator_init(self.raw.as_ptr()) })?;
313 Ok(())
314 }
315
316 pub fn initiator_init_secure_element(&mut self) -> Result<()> {
318 check_code(
319 self.raw.as_ptr(),
320 unsafe { ffi::nfc_initiator_init_secure_element(self.raw.as_ptr()) },
321 )?;
322 Ok(())
323 }
324
325 pub fn select_passive_target(
330 &mut self,
331 modulation: ffi::nfc_modulation,
332 init_data: &[u8],
333 ) -> Result<Option<ffi::nfc_target>> {
334 let mut target = MaybeUninit::<ffi::nfc_target>::uninit();
335 let result = unsafe {
336 ffi::nfc_initiator_select_passive_target(
337 self.raw.as_ptr(),
338 modulation,
339 ptr_or_null(init_data),
340 init_data.len(),
341 target.as_mut_ptr(),
342 )
343 };
344
345 if result < 0 {
346 return Err(Error::from_device(self.raw.as_ptr(), result));
347 }
348
349 if result == 0 {
350 return Ok(None);
351 }
352
353 Ok(Some(unsafe { target.assume_init() }))
354 }
355
356 pub fn list_passive_targets(
361 &mut self,
362 modulation: ffi::nfc_modulation,
363 max_targets: usize,
364 ) -> Result<Vec<ffi::nfc_target>> {
365 let mut targets = Vec::<MaybeUninit<ffi::nfc_target>>::with_capacity(max_targets);
366 let result = unsafe {
367 ffi::nfc_initiator_list_passive_targets(
368 self.raw.as_ptr(),
369 modulation,
370 targets.as_mut_ptr().cast(),
371 max_targets,
372 )
373 };
374
375 if result < 0 {
376 return Err(Error::from_device(self.raw.as_ptr(), result));
377 }
378
379 let count = result as usize;
380 unsafe {
381 targets.set_len(count);
382 }
383
384 Ok(targets
385 .into_iter()
386 .map(|target| unsafe { target.assume_init() })
387 .collect())
388 }
389
390 pub fn poll_target(
395 &mut self,
396 modulations: &[ffi::nfc_modulation],
397 poll_count: u8,
398 period: u8,
399 ) -> Result<Option<ffi::nfc_target>> {
400 let mut target = MaybeUninit::<ffi::nfc_target>::uninit();
401 let result = unsafe {
402 ffi::nfc_initiator_poll_target(
403 self.raw.as_ptr(),
404 ptr_or_null(modulations),
405 modulations.len(),
406 poll_count,
407 period,
408 target.as_mut_ptr(),
409 )
410 };
411
412 if result < 0 {
413 return Err(Error::from_device(self.raw.as_ptr(), result));
414 }
415
416 if result == 0 {
417 return Ok(None);
418 }
419
420 Ok(Some(unsafe { target.assume_init() }))
421 }
422
423 pub fn select_dep_target(
428 &mut self,
429 mode: ffi::nfc_dep_mode,
430 baud_rate: ffi::nfc_baud_rate,
431 initiator_info: Option<&ffi::nfc_dep_info>,
432 timeout: i32,
433 ) -> Result<Option<ffi::nfc_target>> {
434 let mut target = MaybeUninit::<ffi::nfc_target>::uninit();
435 let result = unsafe {
436 ffi::nfc_initiator_select_dep_target(
437 self.raw.as_ptr(),
438 mode,
439 baud_rate,
440 initiator_info.map_or(ptr::null(), |value| value),
441 target.as_mut_ptr(),
442 timeout,
443 )
444 };
445
446 if result < 0 {
447 return Err(Error::from_device(self.raw.as_ptr(), result));
448 }
449
450 if result == 0 {
451 return Ok(None);
452 }
453
454 Ok(Some(unsafe { target.assume_init() }))
455 }
456
457 pub fn poll_dep_target(
459 &mut self,
460 mode: ffi::nfc_dep_mode,
461 baud_rate: ffi::nfc_baud_rate,
462 initiator_info: Option<&ffi::nfc_dep_info>,
463 timeout: i32,
464 ) -> Result<Option<ffi::nfc_target>> {
465 let mut target = MaybeUninit::<ffi::nfc_target>::uninit();
466 let result = unsafe {
467 ffi::nfc_initiator_poll_dep_target(
468 self.raw.as_ptr(),
469 mode,
470 baud_rate,
471 initiator_info.map_or(ptr::null(), |value| value),
472 target.as_mut_ptr(),
473 timeout,
474 )
475 };
476
477 if result < 0 {
478 return Err(Error::from_device(self.raw.as_ptr(), result));
479 }
480
481 if result == 0 {
482 return Ok(None);
483 }
484
485 Ok(Some(unsafe { target.assume_init() }))
486 }
487
488 pub fn deselect_target(&mut self) -> Result<()> {
490 check_code(
491 self.raw.as_ptr(),
492 unsafe { ffi::nfc_initiator_deselect_target(self.raw.as_ptr()) },
493 )?;
494 Ok(())
495 }
496
497 pub fn transceive_bytes(&mut self, tx: &[u8], rx: &mut [u8], timeout: i32) -> Result<usize> {
504 let result = unsafe {
505 ffi::nfc_initiator_transceive_bytes(
506 self.raw.as_ptr(),
507 ptr_or_null(tx),
508 tx.len(),
509 mut_ptr_or_null(rx),
510 rx.len(),
511 timeout,
512 )
513 };
514
515 Ok(check_code(self.raw.as_ptr(), result)? as usize)
516 }
517
518 pub fn transceive_bits(
525 &mut self,
526 tx: &[u8],
527 tx_bits: usize,
528 tx_parity: Option<&[u8]>,
529 rx: &mut [u8],
530 rx_parity: Option<&mut [u8]>,
531 ) -> Result<usize> {
532 validate_bit_frame_args(tx, tx_bits, tx_parity, rx, rx_parity.as_deref())?;
533
534 let result = unsafe {
535 ffi::nfc_initiator_transceive_bits(
536 self.raw.as_ptr(),
537 ptr_or_null(tx),
538 tx_bits,
539 tx_parity.map_or(ptr::null(), ptr_or_null),
540 mut_ptr_or_null(rx),
541 rx.len(),
542 rx_parity.map_or(ptr::null_mut(), mut_ptr_or_null),
543 )
544 };
545
546 Ok(check_code(self.raw.as_ptr(), result)? as usize)
547 }
548
549 pub fn transceive_bytes_timed(
551 &mut self,
552 tx: &[u8],
553 rx: &mut [u8],
554 ) -> Result<TimedResponse> {
555 let mut cycles = 0;
556 let result = unsafe {
557 ffi::nfc_initiator_transceive_bytes_timed(
558 self.raw.as_ptr(),
559 ptr_or_null(tx),
560 tx.len(),
561 mut_ptr_or_null(rx),
562 rx.len(),
563 &mut cycles,
564 )
565 };
566
567 Ok(TimedResponse {
568 received: check_code(self.raw.as_ptr(), result)? as usize,
569 cycles,
570 })
571 }
572
573 pub fn transceive_bits_timed(
575 &mut self,
576 tx: &[u8],
577 tx_bits: usize,
578 tx_parity: Option<&[u8]>,
579 rx: &mut [u8],
580 rx_parity: Option<&mut [u8]>,
581 ) -> Result<TimedResponse> {
582 validate_bit_frame_args(tx, tx_bits, tx_parity, rx, rx_parity.as_deref())?;
583
584 let mut cycles = 0;
585 let result = unsafe {
586 ffi::nfc_initiator_transceive_bits_timed(
587 self.raw.as_ptr(),
588 ptr_or_null(tx),
589 tx_bits,
590 tx_parity.map_or(ptr::null(), ptr_or_null),
591 mut_ptr_or_null(rx),
592 rx.len(),
593 rx_parity.map_or(ptr::null_mut(), mut_ptr_or_null),
594 &mut cycles,
595 )
596 };
597
598 Ok(TimedResponse {
599 received: check_code(self.raw.as_ptr(), result)? as usize,
600 cycles,
601 })
602 }
603
604 pub fn target_is_present(&mut self, target: &ffi::nfc_target) -> Result<bool> {
609 let result = unsafe { ffi::nfc_initiator_target_is_present(self.raw.as_ptr(), target) };
610 if result == ffi::NFC_ETGRELEASED {
611 return Ok(false);
612 }
613
614 check_code(self.raw.as_ptr(), result)?;
615 Ok(true)
616 }
617
618 pub fn target_init(
625 &mut self,
626 target: &mut ffi::nfc_target,
627 rx: &mut [u8],
628 timeout: i32,
629 ) -> Result<usize> {
630 let result = unsafe {
631 ffi::nfc_target_init(
632 self.raw.as_ptr(),
633 target,
634 mut_ptr_or_null(rx),
635 rx.len(),
636 timeout,
637 )
638 };
639
640 Ok(check_code(self.raw.as_ptr(), result)? as usize)
641 }
642
643 pub fn target_send_bytes(&mut self, tx: &[u8], timeout: i32) -> Result<usize> {
645 let result = unsafe {
646 ffi::nfc_target_send_bytes(
647 self.raw.as_ptr(),
648 ptr_or_null(tx),
649 tx.len(),
650 timeout,
651 )
652 };
653
654 Ok(check_code(self.raw.as_ptr(), result)? as usize)
655 }
656
657 pub fn target_receive_bytes(&mut self, rx: &mut [u8], timeout: i32) -> Result<usize> {
659 let result = unsafe {
660 ffi::nfc_target_receive_bytes(
661 self.raw.as_ptr(),
662 mut_ptr_or_null(rx),
663 rx.len(),
664 timeout,
665 )
666 };
667
668 Ok(check_code(self.raw.as_ptr(), result)? as usize)
669 }
670
671 pub fn target_send_bits(
673 &mut self,
674 tx: &[u8],
675 tx_bits: usize,
676 tx_parity: Option<&[u8]>,
677 ) -> Result<usize> {
678 validate_tx_bits_args(tx, tx_bits, tx_parity)?;
679
680 let result = unsafe {
681 ffi::nfc_target_send_bits(
682 self.raw.as_ptr(),
683 ptr_or_null(tx),
684 tx_bits,
685 tx_parity.map_or(ptr::null(), ptr_or_null),
686 )
687 };
688
689 Ok(check_code(self.raw.as_ptr(), result)? as usize)
690 }
691
692 pub fn target_receive_bits(
694 &mut self,
695 rx: &mut [u8],
696 rx_parity: Option<&mut [u8]>,
697 ) -> Result<usize> {
698 validate_rx_parity_args(rx, rx_parity.as_deref())?;
699
700 let result = unsafe {
701 ffi::nfc_target_receive_bits(
702 self.raw.as_ptr(),
703 mut_ptr_or_null(rx),
704 rx.len(),
705 rx_parity.map_or(ptr::null_mut(), mut_ptr_or_null),
706 )
707 };
708
709 Ok(check_code(self.raw.as_ptr(), result)? as usize)
710 }
711
712 pub fn idle(&mut self) -> Result<()> {
714 check_code(self.raw.as_ptr(), unsafe { ffi::nfc_idle(self.raw.as_ptr()) })?;
715 Ok(())
716 }
717
718 pub fn abort_command(&mut self) -> Result<()> {
720 check_code(
721 self.raw.as_ptr(),
722 unsafe { ffi::nfc_abort_command(self.raw.as_ptr()) },
723 )?;
724 Ok(())
725 }
726}
727
728impl Drop for Device<'_> {
729 fn drop(&mut self) {
730 unsafe {
731 ffi::nfc_close(self.raw.as_ptr());
732 }
733 }
734}
735
736pub fn version_cstr() -> &'static CStr {
740 unsafe { CStr::from_ptr(ffi::nfc_version()) }
741}
742
743pub fn version() -> Cow<'static, str> {
745 version_cstr().to_string_lossy()
746}
747
748pub fn modulation_type_name(value: ffi::nfc_modulation_type) -> Cow<'static, str> {
750 unsafe { CStr::from_ptr(ffi::str_nfc_modulation_type(value)).to_string_lossy() }
751}
752
753pub fn baud_rate_name(value: ffi::nfc_baud_rate) -> Cow<'static, str> {
755 unsafe { CStr::from_ptr(ffi::str_nfc_baud_rate(value)).to_string_lossy() }
756}
757
758pub fn target_to_string(target: &ffi::nfc_target, verbose: bool) -> Result<String> {
762 let mut buffer = ptr::null_mut();
763 let result = unsafe { ffi::str_nfc_target(&mut buffer, target, verbose) };
764 if result < 0 {
765 return Err(Error::new(format!("libnfc could not format target: error code {result}")));
766 }
767
768 unsafe { owned_libnfc_string(buffer) }
769}
770
771pub fn iso14443a_crc(data: &[u8]) -> [u8; 2] {
776 let mut owned = data.to_vec();
777 let mut crc = [0u8; 2];
778 unsafe {
779 ffi::iso14443a_crc(owned.as_mut_ptr(), data.len(), crc.as_mut_ptr());
780 }
781 crc
782}
783
784pub fn append_iso14443a_crc(data: &mut Vec<u8>) {
788 let payload_len = data.len();
789 data.resize(payload_len + 2, 0);
790 unsafe {
791 ffi::iso14443a_crc_append(data.as_mut_ptr(), payload_len);
792 }
793}
794
795pub fn iso14443b_crc(data: &[u8]) -> [u8; 2] {
797 let mut owned = data.to_vec();
798 let mut crc = [0u8; 2];
799 unsafe {
800 ffi::iso14443b_crc(owned.as_mut_ptr(), data.len(), crc.as_mut_ptr());
801 }
802 crc
803}
804
805pub fn append_iso14443b_crc(data: &mut Vec<u8>) {
809 let payload_len = data.len();
810 data.resize(payload_len + 2, 0);
811 unsafe {
812 ffi::iso14443b_crc_append(data.as_mut_ptr(), payload_len);
813 }
814}
815
816pub fn locate_iso14443a_historical_bytes(ats: &mut [u8]) -> Option<&mut [u8]> {
821 let mut historical_len = 0;
822 let ptr = unsafe {
823 ffi::iso14443a_locate_historical_bytes(ats.as_mut_ptr(), ats.len(), &mut historical_len)
824 };
825
826 if ptr.is_null() {
827 return None;
828 }
829
830 let start = unsafe { ptr.offset_from(ats.as_ptr()) };
831 if start < 0 {
832 return None;
833 }
834
835 let start = start as usize;
836 let end = start.checked_add(historical_len)?;
837 if end > ats.len() {
838 return None;
839 }
840
841 Some(&mut ats[start..end])
842}
843
844fn check_code(device: *mut ffi::nfc_device, code: i32) -> Result<i32> {
845 if code < 0 {
846 Err(Error::from_device(device, code))
847 } else {
848 Ok(code)
849 }
850}
851
852fn c_char_array_to_string(connstring: &ffi::nfc_connstring) -> String {
853 unsafe { CStr::from_ptr(connstring.as_ptr()).to_string_lossy().into_owned() }
854}
855
856unsafe fn owned_libnfc_string(buffer: *mut c_char) -> Result<String> {
857 if buffer.is_null() {
858 return Ok(String::new());
859 }
860
861 struct OwnedString(*mut c_char);
862
863 impl Drop for OwnedString {
864 fn drop(&mut self) {
865 unsafe {
866 ffi::nfc_free(self.0.cast());
867 }
868 }
869 }
870
871 let buffer = OwnedString(buffer);
872 Ok(CStr::from_ptr(buffer.0).to_string_lossy().into_owned())
873}
874
875fn decode_modulation_types(
876 mut values: *const ffi::nfc_modulation_type,
877) -> Result<Vec<ffi::nfc_modulation_type>> {
878 let mut out = Vec::new();
879 while !values.is_null() {
880 let raw = unsafe { *(values as *const i32) };
881 let value = match raw {
882 0 => break,
883 1 => ffi::nfc_modulation_type::NMT_ISO14443A,
884 2 => ffi::nfc_modulation_type::NMT_JEWEL,
885 3 => ffi::nfc_modulation_type::NMT_ISO14443B,
886 4 => ffi::nfc_modulation_type::NMT_ISO14443BI,
887 5 => ffi::nfc_modulation_type::NMT_ISO14443B2SR,
888 6 => ffi::nfc_modulation_type::NMT_ISO14443B2CT,
889 7 => ffi::nfc_modulation_type::NMT_FELICA,
890 8 => ffi::nfc_modulation_type::NMT_DEP,
891 9 => ffi::nfc_modulation_type::NMT_BARCODE,
892 10 => ffi::nfc_modulation_type::NMT_ISO14443BICLASS,
893 _ => return Err(Error::new(format!("libnfc returned an unknown modulation type: {raw}"))),
894 };
895 out.push(value);
896 values = unsafe { values.add(1) };
897 }
898 Ok(out)
899}
900
901fn decode_baud_rates(mut values: *const ffi::nfc_baud_rate) -> Result<Vec<ffi::nfc_baud_rate>> {
902 let mut out = Vec::new();
903 while !values.is_null() {
904 let raw = unsafe { *(values as *const i32) };
905 let value = match raw {
906 0 => break,
907 1 => ffi::nfc_baud_rate::NBR_106,
908 2 => ffi::nfc_baud_rate::NBR_212,
909 3 => ffi::nfc_baud_rate::NBR_424,
910 4 => ffi::nfc_baud_rate::NBR_847,
911 _ => return Err(Error::new(format!("libnfc returned an unknown baud rate: {raw}"))),
912 };
913 out.push(value);
914 values = unsafe { values.add(1) };
915 }
916 Ok(out)
917}
918
919fn ptr_or_null<T>(slice: &[T]) -> *const T {
920 if slice.is_empty() {
921 ptr::null()
922 } else {
923 slice.as_ptr()
924 }
925}
926
927fn mut_ptr_or_null<T>(slice: &mut [T]) -> *mut T {
928 if slice.is_empty() {
929 ptr::null_mut()
930 } else {
931 slice.as_mut_ptr()
932 }
933}
934
935fn validate_bit_frame_args(
936 tx: &[u8],
937 tx_bits: usize,
938 tx_parity: Option<&[u8]>,
939 rx: &[u8],
940 rx_parity: Option<&[u8]>,
941) -> Result<()> {
942 validate_tx_bits_args(tx, tx_bits, tx_parity)?;
943 validate_rx_parity_args(rx, rx_parity)
944}
945
946fn validate_tx_bits_args(tx: &[u8], tx_bits: usize, tx_parity: Option<&[u8]>) -> Result<()> {
947 let required_tx_bytes = tx_bits.div_ceil(8);
948 if required_tx_bytes > tx.len() {
949 return Err(Error::new(format!(
950 "tx_bits ({tx_bits}) requires at least {required_tx_bytes} bytes, but tx only has {}",
951 tx.len()
952 )));
953 }
954
955 if let Some(parity) = tx_parity {
956 if parity.len() < required_tx_bytes {
957 return Err(Error::new(format!(
958 "tx_parity must contain at least {required_tx_bytes} bytes, but only {} were provided",
959 parity.len()
960 )));
961 }
962 }
963
964 Ok(())
965}
966
967fn validate_rx_parity_args(rx: &[u8], rx_parity: Option<&[u8]>) -> Result<()> {
968 if let Some(parity) = rx_parity {
969 if parity.len() < rx.len() {
970 return Err(Error::new(format!(
971 "rx_parity must contain at least {} bytes to match rx, but only {} were provided",
972 rx.len(),
973 parity.len()
974 )));
975 }
976 }
977
978 Ok(())
979}
980
981#[cfg(test)]
982mod tests {
983 use super::*;
984
985 #[test]
986 fn version_is_non_empty() {
987 assert!(!version().is_empty());
988 }
989
990 #[test]
991 fn append_iso14443a_crc_matches_direct_crc() {
992 let mut payload = vec![0x01, 0x02, 0x03];
993 let crc = iso14443a_crc(&payload);
994 append_iso14443a_crc(&mut payload);
995
996 assert_eq!(&payload[3..], &crc);
997 }
998
999 #[test]
1000 fn append_iso14443b_crc_matches_direct_crc() {
1001 let mut payload = vec![0x04, 0x05, 0x06];
1002 let crc = iso14443b_crc(&payload);
1003 append_iso14443b_crc(&mut payload);
1004
1005 assert_eq!(&payload[3..], &crc);
1006 }
1007}