1pub const LSS_MASTER_COB_ID: u16 = 0x7E5;
37pub const LSS_SLAVE_COB_ID: u16 = 0x7E4;
39
40pub type LssFrame = [u8; 8];
42
43pub const UNCONFIGURED_NODE_ID: u8 = 0xFF;
45
46const CS_SWITCH_GLOBAL: u8 = 0x04;
48const CS_CONFIGURE_NODE_ID: u8 = 0x11;
49const CS_STORE_CONFIGURATION: u8 = 0x17;
50const CS_SWITCH_SELECTIVE_VENDOR: u8 = 0x40;
51const CS_SWITCH_SELECTIVE_PRODUCT: u8 = 0x41;
52const CS_SWITCH_SELECTIVE_REVISION: u8 = 0x42;
53const CS_SWITCH_SELECTIVE_SERIAL: u8 = 0x43;
54const CS_SWITCH_SELECTIVE_RESPONSE: u8 = 0x44;
55const CS_INQUIRE_VENDOR: u8 = 0x5A;
56const CS_INQUIRE_PRODUCT: u8 = 0x5B;
57const CS_INQUIRE_REVISION: u8 = 0x5C;
58const CS_INQUIRE_SERIAL: u8 = 0x5D;
59const CS_INQUIRE_NODE_ID: u8 = 0x5E;
60const CS_IDENTIFY_SLAVE: u8 = 0x4F;
61const CS_FASTSCAN: u8 = 0x51;
62
63const MODE_WAITING: u8 = 0;
65const MODE_CONFIGURATION: u8 = 1;
66
67pub const FASTSCAN_INIT: u8 = 32;
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub struct LssAddress {
76 pub vendor_id: u32,
78 pub product_code: u32,
80 pub revision_number: u32,
82 pub serial_number: u32,
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
88pub enum LssState {
89 Waiting,
91 Configuration,
93}
94
95fn frame(cs: u8) -> LssFrame {
96 let mut f = [0u8; 8];
97 f[0] = cs;
98 f
99}
100
101fn frame_u32(cs: u8, value: u32) -> LssFrame {
102 let mut f = frame(cs);
103 f[1..5].copy_from_slice(&value.to_le_bytes());
104 f
105}
106
107fn u32_at(f: &LssFrame) -> u32 {
108 u32::from_le_bytes([f[1], f[2], f[3], f[4]])
109}
110
111pub fn encode_switch_global(configuration: bool) -> LssFrame {
116 let mut f = frame(CS_SWITCH_GLOBAL);
117 f[1] = if configuration {
118 MODE_CONFIGURATION
119 } else {
120 MODE_WAITING
121 };
122 f
123}
124
125pub fn encode_switch_selective(address: &LssAddress) -> [LssFrame; 4] {
128 [
129 frame_u32(CS_SWITCH_SELECTIVE_VENDOR, address.vendor_id),
130 frame_u32(CS_SWITCH_SELECTIVE_PRODUCT, address.product_code),
131 frame_u32(CS_SWITCH_SELECTIVE_REVISION, address.revision_number),
132 frame_u32(CS_SWITCH_SELECTIVE_SERIAL, address.serial_number),
133 ]
134}
135
136pub fn encode_configure_node_id(node_id: u8) -> LssFrame {
138 let mut f = frame(CS_CONFIGURE_NODE_ID);
139 f[1] = node_id;
140 f
141}
142
143pub fn encode_store() -> LssFrame {
145 frame(CS_STORE_CONFIGURATION)
146}
147
148pub fn encode_inquire_node_id() -> LssFrame {
150 frame(CS_INQUIRE_NODE_ID)
151}
152
153pub fn is_switch_selective_response(response: &LssFrame) -> bool {
158 response[0] == CS_SWITCH_SELECTIVE_RESPONSE
159}
160
161pub fn decode_configure_node_id_response(response: &LssFrame) -> Option<Result<(), u8>> {
164 if response[0] != CS_CONFIGURE_NODE_ID {
165 return None;
166 }
167 Some(if response[1] == 0 {
168 Ok(())
169 } else {
170 Err(response[1])
171 })
172}
173
174pub fn decode_inquire_node_id_response(response: &LssFrame) -> Option<u8> {
176 (response[0] == CS_INQUIRE_NODE_ID).then_some(response[1])
177}
178
179pub fn encode_fastscan(id_number: u32, bit_check: u8, lss_sub: u8, lss_next: u8) -> LssFrame {
189 let mut f = frame_u32(CS_FASTSCAN, id_number);
190 f[5] = bit_check;
191 f[6] = lss_sub;
192 f[7] = lss_next;
193 f
194}
195
196pub fn is_identify_slave_response(response: &LssFrame) -> bool {
200 response[0] == CS_IDENTIFY_SLAVE
201}
202
203#[derive(Debug)]
230pub struct FastscanMaster {
231 id: [u32; 4],
232 sub: u8, bit: i8, found: bool,
235}
236
237impl Default for FastscanMaster {
238 fn default() -> Self {
239 Self::new()
240 }
241}
242
243impl FastscanMaster {
244 pub const fn new() -> Self {
246 Self {
247 id: [0; 4],
248 sub: 0,
249 bit: -2,
250 found: false,
251 }
252 }
253
254 pub fn next_request(&self) -> Option<LssFrame> {
257 if self.sub > 3 {
258 return None;
259 }
260 let sub = self.sub;
261 let idx = sub as usize;
262 Some(match self.bit {
263 -2 => encode_fastscan(0, FASTSCAN_INIT, 0, 0),
264 -1 => encode_fastscan(self.id[idx], 0, sub, (sub + 1) & 3),
265 b => encode_fastscan(self.id[idx], b as u8, sub, sub),
266 })
267 }
268
269 pub fn on_response(&mut self, responded: bool) {
272 match self.bit {
273 -2 => {
274 if responded {
275 self.found = true;
276 self.bit = 31;
277 } else {
278 self.sub = 4; }
280 }
281 -1 => {
282 self.sub += 1;
284 self.bit = 31;
285 }
286 b => {
287 if !responded {
289 self.id[self.sub as usize] |= 1u32 << b;
290 }
291 self.bit -= 1; }
293 }
294 }
295
296 pub const fn found(&self) -> bool {
298 self.found
299 }
300
301 pub const fn is_complete(&self) -> bool {
303 self.sub > 3
304 }
305
306 pub const fn address(&self) -> LssAddress {
309 LssAddress {
310 vendor_id: self.id[0],
311 product_code: self.id[1],
312 revision_number: self.id[2],
313 serial_number: self.id[3],
314 }
315 }
316}
317
318#[derive(Debug)]
325pub struct LssSlave {
326 address: LssAddress,
327 node_id: u8,
328 pending_node_id: u8,
329 state: LssState,
330 selective: u8,
331 fs_sub: u8,
332}
333
334impl LssSlave {
335 pub const fn new(address: LssAddress, node_id: u8) -> Self {
338 Self {
339 address,
340 node_id,
341 pending_node_id: node_id,
342 state: LssState::Waiting,
343 selective: 0,
344 fs_sub: 0,
345 }
346 }
347
348 fn identity(&self, sub: u8) -> u32 {
350 match sub {
351 0 => self.address.vendor_id,
352 1 => self.address.product_code,
353 2 => self.address.revision_number,
354 _ => self.address.serial_number,
355 }
356 }
357
358 fn handle_fastscan(&mut self, req: &LssFrame) -> Option<LssFrame> {
364 if self.node_id != UNCONFIGURED_NODE_ID || self.state != LssState::Waiting {
365 return None;
366 }
367 let id_number = u32_at(req);
368 let bit_check = req[5];
369 let lss_sub = req[6];
370 let lss_next = req[7];
371
372 if bit_check >= FASTSCAN_INIT {
373 self.fs_sub = 0;
375 return Some(frame(CS_IDENTIFY_SLAVE));
376 }
377 if lss_sub != self.fs_sub {
378 return None;
379 }
380 let mask = 0xFFFF_FFFFu32 << bit_check;
382 if (id_number ^ self.identity(lss_sub)) & mask != 0 {
383 return None;
384 }
385 if bit_check == 0 && lss_next != lss_sub {
387 if lss_sub >= 3 {
388 self.state = LssState::Configuration;
389 } else {
390 self.fs_sub = lss_next;
391 }
392 }
393 Some(frame(CS_IDENTIFY_SLAVE))
394 }
395
396 pub const fn node_id(&self) -> u8 {
398 self.node_id
399 }
400
401 pub const fn pending_node_id(&self) -> u8 {
403 self.pending_node_id
404 }
405
406 pub const fn state(&self) -> LssState {
408 self.state
409 }
410
411 pub const fn address(&self) -> LssAddress {
413 self.address
414 }
415
416 pub fn adopt_pending(&mut self) {
419 self.node_id = self.pending_node_id;
420 }
421
422 pub fn handle(&mut self, req: &LssFrame) -> Option<LssFrame> {
424 let configuring = self.state == LssState::Configuration;
425 match req[0] {
426 CS_SWITCH_GLOBAL => {
427 self.selective = 0;
428 self.state = if req[1] == MODE_CONFIGURATION {
429 LssState::Configuration
430 } else {
431 LssState::Waiting
432 };
433 None
434 }
435 CS_SWITCH_SELECTIVE_VENDOR => {
436 self.selective = u8::from(u32_at(req) == self.address.vendor_id);
437 None
438 }
439 CS_SWITCH_SELECTIVE_PRODUCT => {
440 self.selective = if self.selective == 1 && u32_at(req) == self.address.product_code
441 {
442 2
443 } else {
444 0
445 };
446 None
447 }
448 CS_SWITCH_SELECTIVE_REVISION => {
449 self.selective =
450 if self.selective == 2 && u32_at(req) == self.address.revision_number {
451 3
452 } else {
453 0
454 };
455 None
456 }
457 CS_SWITCH_SELECTIVE_SERIAL => {
458 if self.selective == 3 && u32_at(req) == self.address.serial_number {
459 self.selective = 0;
460 self.state = LssState::Configuration;
461 Some(frame(CS_SWITCH_SELECTIVE_RESPONSE))
462 } else {
463 self.selective = 0;
464 None
465 }
466 }
467 CS_CONFIGURE_NODE_ID if configuring => {
468 let id = req[1];
469 let error = if (1..=127).contains(&id) || id == UNCONFIGURED_NODE_ID {
470 self.pending_node_id = id;
471 0
472 } else {
473 1 };
475 let mut f = frame(CS_CONFIGURE_NODE_ID);
476 f[1] = error;
477 Some(f)
478 }
479 CS_STORE_CONFIGURATION if configuring => Some(frame(CS_STORE_CONFIGURATION)),
480 CS_INQUIRE_NODE_ID if configuring => {
481 let mut f = frame(CS_INQUIRE_NODE_ID);
482 f[1] = self.node_id;
483 Some(f)
484 }
485 CS_INQUIRE_VENDOR if configuring => {
486 Some(frame_u32(CS_INQUIRE_VENDOR, self.address.vendor_id))
487 }
488 CS_INQUIRE_PRODUCT if configuring => {
489 Some(frame_u32(CS_INQUIRE_PRODUCT, self.address.product_code))
490 }
491 CS_INQUIRE_REVISION if configuring => {
492 Some(frame_u32(CS_INQUIRE_REVISION, self.address.revision_number))
493 }
494 CS_INQUIRE_SERIAL if configuring => {
495 Some(frame_u32(CS_INQUIRE_SERIAL, self.address.serial_number))
496 }
497 CS_FASTSCAN => self.handle_fastscan(req),
498 _ => None,
499 }
500 }
501}
502
503#[cfg(test)]
504mod tests {
505 use super::*;
506
507 fn address() -> LssAddress {
508 LssAddress {
509 vendor_id: 0x0000_001F,
510 product_code: 0x0000_002A,
511 revision_number: 0x0000_0001,
512 serial_number: 0x0000_1234,
513 }
514 }
515
516 fn slave() -> LssSlave {
517 LssSlave::new(address(), UNCONFIGURED_NODE_ID)
518 }
519
520 #[test]
522 fn switch_global_frames() {
523 assert_eq!(encode_switch_global(true), [0x04, 0x01, 0, 0, 0, 0, 0, 0]);
524 assert_eq!(encode_switch_global(false), [0x04, 0x00, 0, 0, 0, 0, 0, 0]);
525 }
526
527 #[test]
528 fn configure_node_id_frame() {
529 assert_eq!(
530 encode_configure_node_id(0x7F),
531 [0x11, 0x7F, 0, 0, 0, 0, 0, 0]
532 );
533 }
534
535 #[test]
536 fn switch_selective_sequence() {
537 let f = encode_switch_selective(&address());
538 assert_eq!(f[0], [0x40, 0x1F, 0x00, 0x00, 0x00, 0, 0, 0]); assert_eq!(f[3], [0x43, 0x34, 0x12, 0x00, 0x00, 0, 0, 0]); }
541
542 #[test]
544 fn switch_global_changes_state() {
545 let mut s = slave();
546 assert_eq!(s.state(), LssState::Waiting);
547 assert!(s.handle(&encode_switch_global(true)).is_none());
548 assert_eq!(s.state(), LssState::Configuration);
549 s.handle(&encode_switch_global(false));
550 assert_eq!(s.state(), LssState::Waiting);
551 }
552
553 #[test]
554 fn selective_switch_matches_address() {
555 let mut s = slave();
556 let seq = encode_switch_selective(&address());
557 assert!(s.handle(&seq[0]).is_none());
558 assert!(s.handle(&seq[1]).is_none());
559 assert!(s.handle(&seq[2]).is_none());
560 let resp = s.handle(&seq[3]).expect("selective response");
561 assert!(is_switch_selective_response(&resp));
562 assert_eq!(s.state(), LssState::Configuration);
563 }
564
565 #[test]
566 fn selective_switch_rejects_wrong_address() {
567 let mut s = slave();
568 let mut seq = encode_switch_selective(&address());
569 seq[3][1] = 0xFF; for f in &seq {
571 assert!(s.handle(f).is_none());
572 }
573 assert_eq!(s.state(), LssState::Waiting);
574 }
575
576 #[test]
577 fn configure_node_id_only_in_configuration() {
578 let mut s = slave();
579 assert!(s.handle(&encode_configure_node_id(0x20)).is_none());
581
582 s.handle(&encode_switch_global(true));
583 let resp = s.handle(&encode_configure_node_id(0x20)).unwrap();
584 assert_eq!(decode_configure_node_id_response(&resp), Some(Ok(())));
585 assert_eq!(s.pending_node_id(), 0x20);
586 assert_eq!(s.node_id(), UNCONFIGURED_NODE_ID); s.adopt_pending();
588 assert_eq!(s.node_id(), 0x20);
589 }
590
591 #[test]
592 fn configure_node_id_rejects_out_of_range() {
593 let mut s = slave();
594 s.handle(&encode_switch_global(true));
595 let resp = s.handle(&encode_configure_node_id(200)).unwrap();
596 assert_eq!(decode_configure_node_id_response(&resp), Some(Err(1)));
597 }
598
599 #[test]
600 fn inquiries_report_identity_in_configuration() {
601 let mut s = slave();
602 s.handle(&encode_switch_global(true));
603
604 let node = s.handle(&encode_inquire_node_id()).unwrap();
605 assert_eq!(
606 decode_inquire_node_id_response(&node),
607 Some(UNCONFIGURED_NODE_ID)
608 );
609
610 let vendor = s.handle(&frame(CS_INQUIRE_VENDOR)).unwrap();
611 assert_eq!(vendor, [0x5A, 0x1F, 0x00, 0x00, 0x00, 0, 0, 0]);
612 }
613
614 #[test]
615 fn store_acknowledges() {
616 let mut s = slave();
617 s.handle(&encode_switch_global(true));
618 assert_eq!(
619 s.handle(&encode_store()).unwrap(),
620 [0x17, 0, 0, 0, 0, 0, 0, 0]
621 );
622 }
623
624 #[test]
626 fn fastscan_frame_layout() {
627 assert_eq!(
629 encode_fastscan(0x1234_5678, 31, 0, 0),
630 [0x51, 0x78, 0x56, 0x34, 0x12, 31, 0, 0]
631 );
632 assert_eq!(
633 encode_fastscan(0, FASTSCAN_INIT, 0, 0),
634 [0x51, 0, 0, 0, 0, 32, 0, 0]
635 );
636 }
637
638 #[test]
639 fn fastscan_probe_only_answered_while_unconfigured() {
640 let mut s = slave();
641 let probe = encode_fastscan(0, FASTSCAN_INIT, 0, 0);
642 assert!(is_identify_slave_response(&s.handle(&probe).unwrap()));
643
644 let mut configured = LssSlave::new(address(), 0x20);
646 assert!(configured.handle(&probe).is_none());
647 }
648
649 fn run_fastscan(addr: LssAddress) -> (FastscanMaster, LssState) {
652 let mut slave = LssSlave::new(addr, UNCONFIGURED_NODE_ID);
653 let mut master = FastscanMaster::new();
654 let mut steps = 0;
655 while let Some(req) = master.next_request() {
656 let answered = slave.handle(&req).is_some();
657 master.on_response(answered);
658 steps += 1;
659 assert!(steps < 200, "fastscan did not converge");
660 }
661 (master, slave.state())
662 }
663
664 #[test]
665 fn fastscan_discovers_the_address() {
666 let (master, slave_state) = run_fastscan(address());
667 assert!(master.found());
668 assert!(master.is_complete());
669 assert_eq!(master.address(), address());
670 assert_eq!(slave_state, LssState::Configuration);
671 }
672
673 #[test]
674 fn fastscan_recovers_extreme_identities() {
675 for addr in [
676 LssAddress {
677 vendor_id: 0xFFFF_FFFF,
678 product_code: 0,
679 revision_number: 0x8000_0000,
680 serial_number: 0x0000_0001,
681 },
682 LssAddress {
683 vendor_id: 0xDEAD_BEEF,
684 product_code: 0xCAFE_F00D,
685 revision_number: 0x0BAD_C0DE,
686 serial_number: 0xFEED_FACE,
687 },
688 ] {
689 let (master, slave_state) = run_fastscan(addr);
690 assert_eq!(master.address(), addr);
691 assert_eq!(slave_state, LssState::Configuration);
692 }
693 }
694
695 #[test]
696 fn fastscan_reports_no_node() {
697 let mut master = FastscanMaster::new();
699 let _req = master.next_request().unwrap();
700 master.on_response(false);
701 assert!(!master.found());
702 assert!(master.is_complete());
703 assert!(master.next_request().is_none());
704 }
705
706 #[test]
707 fn fastscan_ignores_non_matching_slave_then_configures_match() {
708 let a = address();
711 let b = LssAddress {
712 serial_number: 0x9999,
713 ..address()
714 };
715 let mut sa = LssSlave::new(a, UNCONFIGURED_NODE_ID);
716 let mut sb = LssSlave::new(b, UNCONFIGURED_NODE_ID);
717 let mut master = FastscanMaster::new();
718 while let Some(req) = master.next_request() {
719 let ra = sa.handle(&req).is_some();
721 let rb = sb.handle(&req).is_some();
722 master.on_response(ra || rb);
723 }
724 assert_eq!(master.address(), a);
725 assert_eq!(sa.state(), LssState::Configuration);
726 assert_eq!(sb.state(), LssState::Waiting); }
728
729 fn lcg(state: &mut u32) -> u32 {
732 *state = state.wrapping_mul(1_103_515_245).wrapping_add(12_345);
733 *state
734 }
735
736 #[test]
737 fn fastscan_recovers_pathological_bit_patterns() {
738 for &v in &[0x0000_0000u32, 0xFFFF_FFFF, 0xAAAA_AAAA, 0x5555_5555] {
742 let addr = LssAddress {
743 vendor_id: v,
744 product_code: v,
745 revision_number: v,
746 serial_number: v,
747 };
748 let (master, slave_state) = run_fastscan(addr);
749 assert!(master.found());
750 assert_eq!(master.address(), addr, "failed to recover {addr:?}");
751 assert_eq!(slave_state, LssState::Configuration);
752 }
753
754 let mixed = LssAddress {
756 vendor_id: 0x0000_0000,
757 product_code: 0xFFFF_FFFF,
758 revision_number: 0xAAAA_AAAA,
759 serial_number: 0x5555_5555,
760 };
761 let (master, slave_state) = run_fastscan(mixed);
762 assert_eq!(master.address(), mixed);
763 assert_eq!(slave_state, LssState::Configuration);
764 }
765
766 #[test]
767 fn fastscan_recovers_pseudo_random_addresses() {
768 let mut seed = 0x1234_5678u32;
771 for _ in 0..512 {
772 let addr = LssAddress {
773 vendor_id: lcg(&mut seed),
774 product_code: lcg(&mut seed),
775 revision_number: lcg(&mut seed),
776 serial_number: lcg(&mut seed),
777 };
778 let (master, slave_state) = run_fastscan(addr);
779 assert!(master.found());
780 assert_eq!(master.address(), addr, "failed to recover {addr:?}");
781 assert_eq!(slave_state, LssState::Configuration);
782 }
783 }
784
785 #[test]
786 fn fastscan_converges_on_one_of_many_nodes() {
787 let addrs = [
791 LssAddress {
792 vendor_id: 0x1F,
793 product_code: 0x2A,
794 revision_number: 1,
795 serial_number: 0x1000,
796 },
797 LssAddress {
800 vendor_id: 0x1F,
801 product_code: 0x2A,
802 revision_number: 1,
803 serial_number: 0x2000,
804 },
805 LssAddress {
806 vendor_id: 0x1F,
807 product_code: 0x2A,
808 revision_number: 2,
809 serial_number: 0x3000,
810 },
811 LssAddress {
812 vendor_id: 0x20,
813 product_code: 0x01,
814 revision_number: 9,
815 serial_number: 0x4000,
816 },
817 LssAddress {
818 vendor_id: 0xDEAD_BEEF,
819 product_code: 0x0000_FEED,
820 revision_number: 0,
821 serial_number: 0xFFFF_FFFF,
822 },
823 ];
824 let mut slaves = [
825 LssSlave::new(addrs[0], UNCONFIGURED_NODE_ID),
826 LssSlave::new(addrs[1], UNCONFIGURED_NODE_ID),
827 LssSlave::new(addrs[2], UNCONFIGURED_NODE_ID),
828 LssSlave::new(addrs[3], UNCONFIGURED_NODE_ID),
829 LssSlave::new(addrs[4], UNCONFIGURED_NODE_ID),
830 ];
831 let mut master = FastscanMaster::new();
832 while let Some(req) = master.next_request() {
833 let mut answered = false;
834 for s in &mut slaves {
835 answered |= s.handle(&req).is_some();
836 }
837 master.on_response(answered);
838 }
839
840 assert!(master.found());
841 let found = master.address();
842 assert!(
843 addrs.contains(&found),
844 "converged on a phantom address {found:?}"
845 );
846 let mut configured = 0;
848 for (a, s) in addrs.iter().zip(&slaves) {
849 if *a == found {
850 assert_eq!(s.state(), LssState::Configuration);
851 configured += 1;
852 } else {
853 assert_eq!(s.state(), LssState::Waiting);
854 }
855 }
856 assert_eq!(configured, 1);
857 }
858
859 #[test]
860 fn fastscan_init_probe_resets_an_in_progress_scan() {
861 let mut s = slave();
864 let confirm_sub0 = encode_fastscan(address().vendor_id, 0, 0, 1);
866 assert!(is_identify_slave_response(
867 &s.handle(&confirm_sub0).unwrap()
868 ));
869 assert!(s.handle(&encode_fastscan(0, 31, 0, 0)).is_none());
871
872 let probe = encode_fastscan(0, FASTSCAN_INIT, 0, 0);
874 assert!(is_identify_slave_response(&s.handle(&probe).unwrap()));
875 assert!(s.handle(&encode_fastscan(0, 31, 0, 0)).is_some());
877 }
878
879 #[test]
880 fn fastscan_never_converges_on_a_phantom_address() {
881 let mut seed = 0x0BAD_F00Du32;
886 let rand_addr = |seed: &mut u32| LssAddress {
887 vendor_id: lcg(seed),
888 product_code: lcg(seed),
889 revision_number: lcg(seed),
890 serial_number: lcg(seed),
891 };
892 for _ in 0..256 {
893 let addrs = [
894 rand_addr(&mut seed),
895 rand_addr(&mut seed),
896 rand_addr(&mut seed),
897 rand_addr(&mut seed),
898 ];
899 let mut slaves = [
900 LssSlave::new(addrs[0], UNCONFIGURED_NODE_ID),
901 LssSlave::new(addrs[1], UNCONFIGURED_NODE_ID),
902 LssSlave::new(addrs[2], UNCONFIGURED_NODE_ID),
903 LssSlave::new(addrs[3], UNCONFIGURED_NODE_ID),
904 ];
905 let mut master = FastscanMaster::new();
906 while let Some(req) = master.next_request() {
907 let mut answered = false;
908 for s in &mut slaves {
909 answered |= s.handle(&req).is_some();
910 }
911 master.on_response(answered);
912 }
913 let found = master.address();
914 assert!(
915 addrs.contains(&found),
916 "phantom address {found:?} for population {addrs:?}"
917 );
918 let configured = slaves
919 .iter()
920 .filter(|s| s.state() == LssState::Configuration)
921 .count();
922 assert_eq!(configured, 1, "population {addrs:?}");
923 }
924 }
925
926 #[test]
927 fn fastscan_bit_check_at_boundaries() {
928 let addr = LssAddress {
931 vendor_id: 0x8000_0001, ..address()
933 };
934 let mut s = LssSlave::new(addr, UNCONFIGURED_NODE_ID);
935 assert!(s.handle(&encode_fastscan(0x0000_0000, 31, 0, 0)).is_none());
937 assert!(s.handle(&encode_fastscan(0x8000_0000, 31, 0, 0)).is_some());
939 assert!(s.handle(&encode_fastscan(0x8000_0000, 0, 0, 0)).is_none());
941 assert!(s.handle(&encode_fastscan(0x8000_0001, 0, 0, 0)).is_some());
942 }
943}