1use std::sync::Arc;
11use std::time::Duration;
12
13use bytes::{BufMut, BytesMut};
14use viva_gencp::{CommandFlags, OpCode, PENDING_ACK_COMMAND, StatusCode};
15
16use crate::U3vError;
17use crate::usb::UsbTransfer;
18
19const CMD_PREFIX: u32 = 0x4356_3355;
21const ACK_PREFIX: u32 = 0x4356_3341;
24
25const PREFIX_SIZE: usize = 12;
27
28const MAX_PENDING_RETRIES: usize = 100;
30
31const MAX_PENDING_WAIT: Duration = Duration::from_millis(10_000);
37
38const DEFAULT_PENDING_WAIT_MS: u64 = 100;
40
41fn pending_ack_timeout_ms(scd: &[u8]) -> u64 {
54 if scd.len() < 4 {
55 return DEFAULT_PENDING_WAIT_MS;
56 }
57 let requested = u16::from_le_bytes([scd[2], scd[3]]) as u64;
58 if requested == 0 {
59 return DEFAULT_PENDING_WAIT_MS;
60 }
61 requested.min(MAX_PENDING_WAIT.as_millis() as u64)
62}
63
64const DEFAULT_TIMEOUT: Duration = Duration::from_millis(1000);
66
67pub struct ControlChannel<T: UsbTransfer> {
72 transport: Arc<T>,
73 ep_in: u8,
74 ep_out: u8,
75 request_id: u16,
76 max_cmd_transfer: u32,
77 max_ack_transfer: u32,
78 timeout: Duration,
79}
80
81impl<T: UsbTransfer> ControlChannel<T> {
82 pub fn new(
87 transport: Arc<T>,
88 ep_in: u8,
89 ep_out: u8,
90 max_cmd_transfer: u32,
91 max_ack_transfer: u32,
92 ) -> Self {
93 Self {
94 transport,
95 ep_in,
96 ep_out,
97 request_id: 0,
98 max_cmd_transfer,
99 max_ack_transfer,
100 timeout: DEFAULT_TIMEOUT,
101 }
102 }
103
104 pub fn set_timeout(&mut self, timeout: Duration) {
106 self.timeout = timeout;
107 }
108
109 pub fn transport(&self) -> &Arc<T> {
111 &self.transport
112 }
113
114 pub fn read_register(&mut self, addr: u64) -> Result<u32, U3vError> {
116 let mut payload = BytesMut::with_capacity(8);
117 payload.put_u64(addr);
118 let ack = self.transact(OpCode::ReadRegister, &payload)?;
119 if ack.len() < 4 {
120 return Err(U3vError::Protocol(format!(
121 "ReadRegister ack too short: {} bytes",
122 ack.len()
123 )));
124 }
125 Ok(u32::from_be_bytes([ack[0], ack[1], ack[2], ack[3]]))
126 }
127
128 pub fn write_register(&mut self, addr: u64, value: u32) -> Result<(), U3vError> {
130 let mut payload = BytesMut::with_capacity(12);
131 payload.put_u64(addr);
132 payload.put_u32(value);
133 let _ack = self.transact(OpCode::WriteRegister, &payload)?;
134 Ok(())
135 }
136
137 pub fn read_mem(&mut self, addr: u64, len: usize) -> Result<Vec<u8>, U3vError> {
140 let max_payload = self.max_read_chunk();
141 let mut result = Vec::with_capacity(len);
142 let mut offset = 0usize;
143
144 while offset < len {
145 let chunk = (len - offset).min(max_payload);
146 let mut payload = BytesMut::with_capacity(12);
147 payload.put_u64(addr + offset as u64);
148 payload.put_u16(0); payload.put_u16(chunk as u16);
151 let ack = self.transact(OpCode::ReadMem, &payload)?;
152 if ack.len() != chunk {
153 return Err(U3vError::Protocol(format!(
154 "ReadMem ACK length mismatch at offset {offset}: \
155 requested {chunk} bytes, got {}",
156 ack.len()
157 )));
158 }
159 result.extend_from_slice(&ack);
160 offset += chunk;
161 }
162 Ok(result)
163 }
164
165 pub fn write_mem(&mut self, addr: u64, data: &[u8]) -> Result<(), U3vError> {
168 let max_payload = self.max_write_chunk();
169 let mut offset = 0usize;
170
171 while offset < data.len() {
172 let chunk = (data.len() - offset).min(max_payload);
173 let mut payload = BytesMut::with_capacity(8 + chunk);
174 payload.put_u64(addr + offset as u64);
175 payload.extend_from_slice(&data[offset..offset + chunk]);
176 let _ack = self.transact(OpCode::WriteMem, &payload)?;
177 offset += chunk;
178 }
179 Ok(())
180 }
181
182 fn max_read_chunk(&self) -> usize {
184 (self.max_ack_transfer as usize).saturating_sub(PREFIX_SIZE)
185 }
186
187 fn max_write_chunk(&self) -> usize {
189 (self.max_cmd_transfer as usize).saturating_sub(PREFIX_SIZE + 8)
190 }
191
192 fn transact(&mut self, opcode: OpCode, payload: &[u8]) -> Result<Vec<u8>, U3vError> {
197 let request_id = self.next_request_id();
198 let packet = encode_command(opcode, CommandFlags::ACK_REQUIRED, request_id, payload);
199 self.transport
200 .bulk_write(self.ep_out, &packet, self.timeout)?;
201
202 let mut ack_buf = vec![0u8; self.max_ack_transfer as usize];
204 for _ in 0..MAX_PENDING_RETRIES {
205 let n = self
206 .transport
207 .bulk_read(self.ep_in, &mut ack_buf, self.timeout)?;
208 let ack = decode_ack(&ack_buf[..n])?;
209
210 if ack.request_id != request_id {
211 return Err(U3vError::Protocol(format!(
212 "request ID mismatch: expected {request_id:#06x}, got {:#06x}",
213 ack.request_id
214 )));
215 }
216
217 if ack.command == PENDING_ACK_COMMAND {
223 let wait_ms = pending_ack_timeout_ms(&ack.payload);
224 tracing::debug!(wait_ms, "PENDING_ACK, extending deadline");
225 std::thread::sleep(Duration::from_millis(wait_ms));
226 continue;
227 }
228
229 if ack.command != opcode.ack_code() {
233 return Err(U3vError::Protocol(format!(
234 "unexpected acknowledgement command {:#06x}, expected {:#06x}",
235 ack.command,
236 opcode.ack_code()
237 )));
238 }
239
240 match ack.status {
241 StatusCode::Success => return Ok(ack.payload),
242 status => return Err(U3vError::Status { status }),
243 }
244 }
245 Err(U3vError::Timeout)
246 }
247
248 fn next_request_id(&mut self) -> u16 {
249 let id = self.request_id;
250 self.request_id = self.request_id.wrapping_add(1);
251 id
252 }
253}
254
255fn encode_command(opcode: OpCode, flags: CommandFlags, request_id: u16, payload: &[u8]) -> Vec<u8> {
271 let mut buf = BytesMut::with_capacity(PREFIX_SIZE + payload.len());
272 buf.put_u32_le(CMD_PREFIX);
273 buf.put_u16_le(flags.bits());
274 buf.put_u16_le(opcode.command_code());
275 buf.put_u16_le(payload.len() as u16);
276 buf.put_u16_le(request_id);
277 buf.extend_from_slice(payload);
278 buf.to_vec()
279}
280
281struct AckPacket {
283 status: StatusCode,
284 command: u16,
291 request_id: u16,
292 payload: Vec<u8>,
293}
294
295fn decode_ack(buf: &[u8]) -> Result<AckPacket, U3vError> {
297 if buf.len() < PREFIX_SIZE {
298 return Err(U3vError::Protocol(format!(
299 "ack too short: {} bytes, need at least {PREFIX_SIZE}",
300 buf.len()
301 )));
302 }
303
304 let prefix = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
305 if prefix != ACK_PREFIX {
306 return Err(U3vError::Protocol(format!(
307 "bad ack prefix: {prefix:#010x}, expected {ACK_PREFIX:#010x}"
308 )));
309 }
310
311 let status_raw = u16::from_le_bytes([buf[4], buf[5]]);
312 let command = u16::from_le_bytes([buf[6], buf[7]]);
313 let length = u16::from_le_bytes([buf[8], buf[9]]) as usize;
314 let request_id = u16::from_le_bytes([buf[10], buf[11]]);
315
316 let expected = PREFIX_SIZE + length;
317 if buf.len() < expected {
318 return Err(U3vError::Protocol(format!(
319 "ack truncated: got {} bytes, header says {expected}",
320 buf.len()
321 )));
322 }
323
324 let status = StatusCode::from_raw(status_raw);
325 let payload = buf[PREFIX_SIZE..PREFIX_SIZE + length].to_vec();
326
327 Ok(AckPacket {
328 status,
329 command,
330 request_id,
331 payload,
332 })
333}
334
335#[cfg(test)]
340mod tests {
341 use super::*;
342 use crate::usb::MockUsbTransfer;
343
344 const EP_OUT: u8 = 0x01;
345 const EP_IN: u8 = 0x81;
346
347 const ACK_READ_REG: u16 = OpCode::ReadRegister.ack_code();
351 const ACK_WRITE_REG: u16 = OpCode::WriteRegister.ack_code();
352 const ACK_READ_MEM: u16 = OpCode::ReadMem.ack_code();
353 const ACK_WRITE_MEM: u16 = OpCode::WriteMem.ack_code();
354
355 fn build_ack(command: u16, status: StatusCode, request_id: u16, payload: &[u8]) -> Vec<u8> {
363 let mut buf = BytesMut::with_capacity(PREFIX_SIZE + payload.len());
364 buf.put_u32_le(ACK_PREFIX);
365 buf.put_u16_le(status.to_raw());
366 buf.put_u16_le(command);
367 buf.put_u16_le(payload.len() as u16);
368 buf.put_u16_le(request_id);
369 buf.extend_from_slice(payload);
370 buf.to_vec()
371 }
372
373 fn make_channel(mock: &Arc<MockUsbTransfer>) -> ControlChannel<MockUsbTransfer> {
374 ControlChannel::new(Arc::clone(mock), EP_IN, EP_OUT, 1024, 1024)
375 }
376
377 #[test]
378 fn encode_command_format() {
379 let payload = [0xAA, 0xBB, 0xCC, 0xDD];
380 let pkt = encode_command(
381 OpCode::ReadMem,
382 CommandFlags::ACK_REQUIRED,
383 0x0042,
384 &payload,
385 );
386 assert_eq!(pkt.len(), PREFIX_SIZE + 4);
387
388 assert_eq!(
390 u32::from_le_bytes([pkt[0], pkt[1], pkt[2], pkt[3]]),
391 CMD_PREFIX
392 );
393 assert_eq!(
395 u16::from_le_bytes([pkt[4], pkt[5]]),
396 CommandFlags::ACK_REQUIRED.bits()
397 );
398 assert_eq!(
400 u16::from_le_bytes([pkt[6], pkt[7]]),
401 OpCode::ReadMem.command_code()
402 );
403 assert_eq!(u16::from_le_bytes([pkt[8], pkt[9]]), 4);
405 assert_eq!(u16::from_le_bytes([pkt[10], pkt[11]]), 0x0042);
407 assert_eq!(&pkt[12..], &payload);
409 }
410
411 #[test]
412 fn decode_ack_success() {
413 let payload = vec![0x01, 0x02, 0x03, 0x04];
414 let buf = build_ack(ACK_READ_MEM, StatusCode::Success, 0x0042, &payload);
415 let ack = decode_ack(&buf).unwrap();
416 assert_eq!(ack.status, StatusCode::Success);
417 assert_eq!(ack.request_id, 0x0042);
418 assert_eq!(ack.payload, payload);
419 }
420
421 #[test]
422 fn decode_ack_bad_prefix() {
423 let mut buf = build_ack(ACK_READ_MEM, StatusCode::Success, 0x0001, &[]);
424 buf[0] = 0xFF;
426 assert!(decode_ack(&buf).is_err());
427 }
428
429 #[test]
430 fn decode_ack_truncated() {
431 let buf = build_ack(ACK_READ_MEM, StatusCode::Success, 0x0001, &[1, 2, 3, 4]);
432 assert!(decode_ack(&buf[..PREFIX_SIZE]).is_err());
434 }
435
436 #[test]
437 fn read_register_roundtrip() {
438 let mock = Arc::new(MockUsbTransfer::new());
439 let mut ch = make_channel(&mock);
440
441 let value: u32 = 0xDEAD_BEEF;
443 let ack = build_ack(
444 ACK_READ_REG,
445 StatusCode::Success,
446 0x0000,
447 &value.to_be_bytes(),
448 );
449 mock.enqueue_read(EP_IN, ack);
450
451 let result = ch.read_register(0x0000_1000).unwrap();
452 assert_eq!(result, value);
453
454 let writes = mock.take_writes(EP_OUT);
456 assert_eq!(writes.len(), 1);
457 assert_eq!(writes[0].len(), PREFIX_SIZE + 8); }
459
460 #[test]
461 fn write_register_roundtrip() {
462 let mock = Arc::new(MockUsbTransfer::new());
463 let mut ch = make_channel(&mock);
464
465 let ack = build_ack(ACK_WRITE_REG, StatusCode::Success, 0x0000, &[]);
467 mock.enqueue_read(EP_IN, ack);
468
469 ch.write_register(0x0000_1000, 0x1234_5678).unwrap();
470
471 let writes = mock.take_writes(EP_OUT);
472 assert_eq!(writes.len(), 1);
473 assert_eq!(writes[0].len(), PREFIX_SIZE + 12); }
475
476 #[test]
477 fn read_mem_single_chunk() {
478 let mock = Arc::new(MockUsbTransfer::new());
479 let mut ch = make_channel(&mock);
480
481 let data = vec![0xAA; 64];
482 let ack = build_ack(ACK_READ_MEM, StatusCode::Success, 0x0000, &data);
483 mock.enqueue_read(EP_IN, ack);
484
485 let result = ch.read_mem(0x0000_2000, 64).unwrap();
486 assert_eq!(result, data);
487 }
488
489 #[test]
490 fn read_mem_chunked() {
491 let mock = Arc::new(MockUsbTransfer::new());
494 let mut ch = ControlChannel::new(Arc::clone(&mock), EP_IN, EP_OUT, 1024, 48);
495
496 let chunk1 = vec![0xAA; 36];
497 let chunk2 = vec![0xBB; 28];
498 mock.enqueue_read(
499 EP_IN,
500 build_ack(ACK_READ_MEM, StatusCode::Success, 0x0000, &chunk1),
501 );
502 mock.enqueue_read(
503 EP_IN,
504 build_ack(ACK_READ_MEM, StatusCode::Success, 0x0001, &chunk2),
505 );
506
507 let result = ch.read_mem(0x0000_3000, 64).unwrap();
508 assert_eq!(result.len(), 64);
509 assert_eq!(&result[..36], &chunk1[..]);
510 assert_eq!(&result[36..], &chunk2[..]);
511
512 let writes = mock.take_writes(EP_OUT);
514 assert_eq!(writes.len(), 2);
515 }
516
517 #[test]
518 fn write_mem_chunked() {
519 let mock = Arc::new(MockUsbTransfer::new());
520 let mut ch = ControlChannel::new(Arc::clone(&mock), EP_IN, EP_OUT, 48, 1024);
522
523 mock.enqueue_read(
524 EP_IN,
525 build_ack(ACK_WRITE_MEM, StatusCode::Success, 0x0000, &[]),
526 );
527 mock.enqueue_read(
528 EP_IN,
529 build_ack(ACK_WRITE_MEM, StatusCode::Success, 0x0001, &[]),
530 );
531
532 let data = vec![0xCC; 50]; ch.write_mem(0x0000_4000, &data).unwrap();
534
535 let writes = mock.take_writes(EP_OUT);
536 assert_eq!(writes.len(), 2);
537 }
538
539 #[test]
540 fn pending_ack_retry() {
541 let mock = Arc::new(MockUsbTransfer::new());
542 let mut ch = make_channel(&mock);
543
544 let pending_scd = [0x00, 0x00, 0x00, 0x00];
548 mock.enqueue_read(
549 EP_IN,
550 build_ack(
551 PENDING_ACK_COMMAND,
552 StatusCode::Success,
553 0x0000,
554 &pending_scd,
555 ),
556 );
557 let value = 0x1234_5678u32;
559 mock.enqueue_read(
560 EP_IN,
561 build_ack(
562 ACK_READ_REG,
563 StatusCode::Success,
564 0x0000,
565 &value.to_be_bytes(),
566 ),
567 );
568
569 let result = ch.read_register(0x0000_5000).unwrap();
570 assert_eq!(result, value);
571 }
572
573 #[test]
574 fn device_error_status() {
575 let mock = Arc::new(MockUsbTransfer::new());
576 let mut ch = make_channel(&mock);
577
578 mock.enqueue_read(
579 EP_IN,
580 build_ack(ACK_READ_REG, StatusCode::InvalidAddress, 0x0000, &[]),
581 );
582
583 let err = ch.read_register(0xFFFF_FFFF).unwrap_err();
584 assert!(matches!(
585 err,
586 U3vError::Status {
587 status: StatusCode::InvalidAddress
588 }
589 ));
590 }
591
592 #[test]
593 fn pending_ack_scd_is_a_little_endian_u16_after_two_reserved_bytes() {
594 assert_eq!(pending_ack_timeout_ms(&[0x00, 0x00, 0xE8, 0x03]), 1000);
596 assert_ne!(
599 pending_ack_timeout_ms(&[0x00, 0x00, 0xE8, 0x03]),
600 u32::from_be_bytes([0x00, 0x00, 0xE8, 0x03]) as u64
601 );
602 assert_eq!(pending_ack_timeout_ms(&[0x00]), DEFAULT_PENDING_WAIT_MS);
604 assert_eq!(
605 pending_ack_timeout_ms(&[0x00, 0x00, 0x00, 0x00]),
606 DEFAULT_PENDING_WAIT_MS
607 );
608 assert_eq!(
610 pending_ack_timeout_ms(&[0x00, 0x00, 0xFF, 0xFF]),
611 MAX_PENDING_WAIT.as_millis() as u64
612 );
613 }
614
615 #[test]
616 fn access_denied_is_reported_not_retried_as_pending() {
617 let mock = Arc::new(MockUsbTransfer::new());
621 let mut ch = make_channel(&mock);
622
623 mock.enqueue_read(
624 EP_IN,
625 build_ack(ACK_READ_REG, StatusCode::AccessDenied, 0x0000, &[]),
626 );
627
628 let err = ch.read_register(0x0000_1000).unwrap_err();
629 assert!(matches!(
630 err,
631 U3vError::Status {
632 status: StatusCode::AccessDenied
633 }
634 ));
635 assert_eq!(mock.take_writes(EP_OUT).len(), 1);
637 }
638
639 #[test]
640 fn acknowledgement_for_another_command_is_rejected() {
641 let mock = Arc::new(MockUsbTransfer::new());
644 let mut ch = make_channel(&mock);
645
646 mock.enqueue_read(
647 EP_IN,
648 build_ack(
649 ACK_WRITE_MEM,
650 StatusCode::Success,
651 0x0000,
652 &0xDEAD_BEEFu32.to_be_bytes(),
653 ),
654 );
655
656 let err = ch.read_register(0x0000_1000).unwrap_err();
657 assert!(
658 matches!(err, U3vError::Protocol(ref msg) if msg.contains("unexpected acknowledgement command")),
659 "expected a protocol error naming the command, got {err:?}"
660 );
661 }
662
663 #[test]
664 fn request_id_increments() {
665 let mock = Arc::new(MockUsbTransfer::new());
666 let mut ch = make_channel(&mock);
667
668 mock.enqueue_read(
670 EP_IN,
671 build_ack(ACK_READ_REG, StatusCode::Success, 0x0000, &[0; 4]),
672 );
673 mock.enqueue_read(
674 EP_IN,
675 build_ack(ACK_READ_REG, StatusCode::Success, 0x0001, &[0; 4]),
676 );
677
678 ch.read_register(0x1000).unwrap();
679 ch.read_register(0x2000).unwrap();
680
681 let writes = mock.take_writes(EP_OUT);
682 assert_eq!(writes.len(), 2);
683 assert_eq!(u16::from_le_bytes([writes[0][10], writes[0][11]]), 0x0000);
685 assert_eq!(u16::from_le_bytes([writes[1][10], writes[1][11]]), 0x0001);
687 }
688}