1use crate::error::{Result, TargetDriverError};
21use fidl_fuchsia_hardware_serial::{
22 CharacterWidth, Class, Config, DeviceMarker, DeviceProxy, DeviceProxy_Marker, FlowControl,
23 Parity, StopWidth,
24};
25use futures::channel::mpsc;
26use futures::prelude::*;
27use futures::stream::FusedStream;
28use log::{error, info, warn};
29use std::time::Duration;
30use uart_fpl::{
31 AckTracker, CONTROL_CHANNEL_ID, FrameParser, FrameType, ProtocolId, TargetHandshake,
32 encode_frame,
33};
34
35const SERIAL_DEV_DIR: &str = "/dev/class/serial";
36const MAX_WRITE_ATTEMPTS: usize = 5;
37const WRITE_RETRY_DELAY: Duration = Duration::from_millis(50);
38const WRITE_BATCH_CAPACITY: usize = 4096;
39
40#[allow(async_fn_in_trait)]
42pub trait SerialRead {
43 async fn serial_read(&mut self) -> Result<Vec<u8>>;
47}
48
49#[allow(async_fn_in_trait)]
51pub trait SerialWrite {
52 async fn serial_write(&mut self, data: &[u8]) -> Result<()>;
54}
55
56impl SerialRead for DeviceProxy {
57 async fn serial_read(&mut self) -> Result<Vec<u8>> {
58 let res = self.read().await?;
59 res.map_err(TargetDriverError::DriverStatus)
60 }
61}
62
63impl SerialWrite for DeviceProxy {
64 async fn serial_write(&mut self, data: &[u8]) -> Result<()> {
65 let res = self.write(data).await?;
66 res.map_err(TargetDriverError::DriverStatus)?;
67 Ok(())
68 }
69}
70
71async fn handle_handshake_frame<W: SerialWrite>(
72 handshake: &TargetHandshake,
73 frame: &uart_fpl::Frame,
74 device: &mut W,
75) -> Result<ProtocolId> {
76 let (selected, resp_payload) = handshake.handle_request(&frame.payload)?;
77 let resp_frame = encode_frame(
78 frame.session_id,
79 CONTROL_CHANNEL_ID,
80 1,
81 FrameType::NegotiateResp,
82 &resp_payload,
83 )?;
84 write_frame_to_device(device, &resp_frame).await?;
85 if let Some(proto) = selected {
86 Ok(proto)
87 } else {
88 warn!("Handshake negotiation failed: no common protocol");
89 Err(TargetDriverError::NoCommonProtocol)
90 }
91}
92
93pub async fn run_target_handshake<S: SerialRead + SerialWrite>(
105 device: &mut S,
106) -> Result<(ProtocolId, u32, Vec<u8>)> {
107 run_target_handshake_with_initial_data(device, &[]).await
108}
109
110pub async fn run_target_handshake_with_initial_data<S: SerialRead + SerialWrite>(
123 device: &mut S,
124 initial_data: &[u8],
125) -> Result<(ProtocolId, u32, Vec<u8>)> {
126 let handshake = TargetHandshake::new(vec![ProtocolId::ResendSP]);
127 let mut parser = FrameParser::new();
128 if !initial_data.is_empty() {
129 parser.feed(initial_data);
130 }
131 loop {
132 while let Some(frame) = parser.next_frame() {
133 if frame.channel_id == CONTROL_CHANNEL_ID && frame.frame_type == FrameType::NegotiateReq
134 {
135 if frame.session_id == 0 {
136 warn!("Ignoring NegotiateReq with invalid session_id 0");
137 continue;
138 }
139 match handle_handshake_frame(&handshake, &frame, device).await {
140 Ok(proto) => return Ok((proto, frame.session_id, parser.take_unconsumed())),
141 Err(
142 e @ (TargetDriverError::NoCommonProtocol | TargetDriverError::Handshake(_)),
143 ) => {
144 warn!(
145 "Ignoring rejected NegotiateReq ({:?}); waiting for next handshake",
146 e
147 );
148 continue;
149 }
150 Err(e) => return Err(e),
151 }
152 }
153 }
154 let bytes = device.serial_read().await?;
155 if bytes.is_empty() {
156 return Err(TargetDriverError::EmptyRead);
157 }
158 parser.feed(&bytes);
159 }
160}
161
162#[derive(Debug)]
169pub struct SerialReader<R> {
170 client: R,
171 parser: FrameParser,
172}
173
174impl<R: SerialRead> SerialReader<R> {
175 pub fn new(client: R) -> Self {
177 Self::with_initial_data(client, &[])
178 }
179
180 pub fn with_initial_data(client: R, initial_data: &[u8]) -> Self {
182 let mut parser = FrameParser::new();
183 if !initial_data.is_empty() {
184 parser.feed(initial_data);
185 }
186 Self { client, parser }
187 }
188
189 pub fn take_unconsumed(&mut self) -> Vec<u8> {
191 self.parser.take_unconsumed()
192 }
193
194 pub fn requeue_frame(&mut self, frame: &uart_fpl::Frame) {
196 match encode_frame(
197 frame.session_id,
198 frame.channel_id,
199 frame.seq,
200 frame.frame_type,
201 &frame.payload,
202 ) {
203 Ok(raw) => {
204 let trailing = self.parser.take_unconsumed();
205 self.parser.feed(&raw);
206 if !trailing.is_empty() {
207 self.parser.feed(&trailing);
208 }
209 }
210 Err(e) => {
211 warn!("Failed to re-encode frame for requeue: {:?}", e);
212 }
213 }
214 }
215
216 pub async fn next_frame(&mut self) -> Result<uart_fpl::Frame> {
223 loop {
224 if let Some(frame) = self.parser.next_frame() {
225 return Ok(frame);
226 }
227 let data = self.client.serial_read().await?;
228 if data.is_empty() {
229 return Err(TargetDriverError::Eof);
230 }
231 self.parser.feed(&data);
232 }
233 }
234}
235
236async fn probe_serial_path(path: &str) -> Option<(DeviceProxy, Class)> {
237 let proxy_client =
242 match fuchsia_component::client::connect_to_protocol_at_path::<DeviceProxy_Marker>(path) {
243 Ok(proxy) => proxy,
244 Err(e) => {
245 warn!("Failed to connect to DeviceProxy at {}: {:?}", path, e);
246 return None;
247 }
248 };
249 let (client, server) = fidl::endpoints::create_proxy::<DeviceMarker>();
250 if let Err(e) = proxy_client.get_channel(server) {
251 warn!("Failed to call get_channel for serial device at {}: {:?}", path, e);
252 return None;
253 }
254 match client.get_class().await {
255 Ok(class) => Some((client, class)),
256 Err(e) => {
257 warn!("Failed to get_class for serial device at {}: {:?}", path, e);
258 None
259 }
260 }
261}
262
263fn collect_sorted_serial_paths() -> Result<Vec<String>> {
264 let mut paths = Vec::new();
265 for entry in std::fs::read_dir(SERIAL_DEV_DIR)? {
266 let path_buf = entry?.path();
267 let path = path_buf.to_str().ok_or(TargetDriverError::NonUtf8Path)?.to_owned();
268 paths.push(path);
269 }
270 paths.sort();
271 Ok(paths)
272}
273
274async fn discover_serial_port() -> Result<(DeviceProxy, String)> {
275 let paths = collect_sorted_serial_paths()?;
276 let mut console_port = None;
277 for path in paths {
278 if let Some((client, class)) = probe_serial_path(&path).await {
279 match class {
284 Class::Generic => return Ok((client, path)),
285 Class::Console => {
286 console_port.get_or_insert((client, path));
287 }
288 _ => {}
289 }
290 }
291 }
292 console_port.ok_or(TargetDriverError::NoSerialPorts)
293}
294
295pub async fn open_serial_device(baud_rate: u32) -> Result<DeviceProxy> {
304 let (client, path) = discover_serial_port().await?;
305 let config = Config {
306 character_width: CharacterWidth::Bits8,
307 stop_width: StopWidth::Bits1,
308 parity: Parity::None,
309 control_flow: FlowControl::None,
310 baud_rate,
311 };
312 let status = client.set_config(&config).await?;
313 if status != 0 {
314 warn!("SetConfig on {} returned non-OK status: {}", path, status);
317 }
318 info!("Successfully opened and configured serial device at {}", path);
319 Ok(client)
320}
321
322async fn write_frame_to_device<W: SerialWrite>(device: &mut W, frame: &[u8]) -> Result<()> {
323 let mut attempts = 0;
324 loop {
325 match device.serial_write(frame).await {
326 Ok(()) => return Ok(()),
327 Err(e) => error!("Serial write error: {:?}, attempt={}", e, attempts),
328 }
329 attempts += 1;
330 if attempts >= MAX_WRITE_ATTEMPTS {
331 return Err(TargetDriverError::WriteRetriesExhausted);
332 }
333 fuchsia_async::Timer::new(WRITE_RETRY_DELAY).await;
334 }
335}
336
337async fn write_ack_frame<W: SerialWrite>(device: &mut W, session_id: u32, seq: u8) -> Result<()> {
338 let ack_frame = encode_frame(session_id, CONTROL_CHANNEL_ID, seq, FrameType::Ack, &[])?;
339 write_frame_to_device(device, &ack_frame).await
340}
341
342async fn flush_frame_batch<W: SerialWrite>(
343 device: &mut W,
344 first_frame: &[u8],
345 data_rx: &mut mpsc::Receiver<Vec<u8>>,
346 batch_buf: &mut Vec<u8>,
347) -> Result<()> {
348 batch_buf.clear();
349 batch_buf.extend_from_slice(first_frame);
350 while let Ok(next_frame) = data_rx.try_recv() {
351 batch_buf.extend_from_slice(&next_frame);
352 if batch_buf.len() >= WRITE_BATCH_CAPACITY {
353 break;
354 }
355 }
356 write_frame_to_device(device, batch_buf).await
357}
358
359pub async fn writer_task<W: SerialWrite>(
367 mut device: W,
368 ack_tracker: AckTracker,
369 mut data_rx: mpsc::Receiver<Vec<u8>>,
370) -> Result<()> {
371 let mut batch_buf = Vec::with_capacity(WRITE_BATCH_CAPACITY);
372 loop {
373 if let Some((session_id, seq)) = ack_tracker.take_ack() {
374 write_ack_frame(&mut device, session_id, seq).await?;
375 continue;
376 }
377 if data_rx.is_terminated() {
378 break;
379 }
380 futures::select_biased! {
381 (session_id, seq) = ack_tracker.wait_ack().fuse() => {
382 write_ack_frame(&mut device, session_id, seq).await?;
383 }
384 frame = data_rx.next() => match frame {
385 Some(first_frame) => {
386 flush_frame_batch(&mut device, &first_frame, &mut data_rx, &mut batch_buf)
387 .await?;
388 }
389 None => break,
390 },
391 complete => break,
392 }
393 }
394 Ok(())
395}
396
397#[cfg(test)]
398pub(crate) struct MockSerial {
399 pub(crate) read_data: Vec<Vec<u8>>,
400 pub(crate) write_data: Vec<Vec<u8>>,
401}
402
403#[cfg(test)]
404impl SerialRead for MockSerial {
405 async fn serial_read(&mut self) -> Result<Vec<u8>> {
406 if self.read_data.is_empty() {
407 return Ok(Vec::new());
408 }
409 Ok(self.read_data.remove(0))
410 }
411}
412
413#[cfg(test)]
414impl SerialWrite for MockSerial {
415 async fn serial_write(&mut self, data: &[u8]) -> Result<()> {
416 self.write_data.push(data.to_vec());
417 Ok(())
418 }
419}
420
421#[cfg(test)]
422mod tests {
423 use super::*;
424 use std::cell::RefCell;
425 use std::rc::Rc;
426 use uart_fpl::{HandshakeResponse, HandshakeStatus, HostHandshake};
427
428 struct SharedWriteData {
429 data: RefCell<Vec<Vec<u8>>>,
430 }
431
432 #[derive(Clone)]
433 struct MockSerialWriter {
434 shared: Rc<SharedWriteData>,
435 }
436
437 impl SerialWrite for MockSerialWriter {
438 async fn serial_write(&mut self, data: &[u8]) -> Result<()> {
439 self.shared.data.borrow_mut().push(data.to_vec());
440 Ok(())
441 }
442 }
443
444 #[fuchsia::test]
445 async fn test_writer_task_prioritizes_acks() {
446 let shared = Rc::new(SharedWriteData { data: RefCell::new(Vec::new()) });
447 let writer = MockSerialWriter { shared: shared.clone() };
448 let ack_tracker = AckTracker::new();
449 let (mut data_tx, data_rx) = mpsc::channel(10);
450 let session_id = 12345u32;
451 let data_frame_1 = encode_frame(session_id, 1, 0, FrameType::Data, &[1]).unwrap();
452 let data_frame_2 = encode_frame(session_id, 1, 1, FrameType::Data, &[2]).unwrap();
453 let ack_frame =
454 encode_frame(session_id, CONTROL_CHANNEL_ID, 0, FrameType::Ack, &[]).unwrap();
455 data_tx.send(data_frame_1.clone()).await.unwrap();
456 data_tx.send(data_frame_2.clone()).await.unwrap();
457 ack_tracker.set_ack(session_id, 0);
458 let writer_handle =
459 fuchsia_async::Task::local(
460 async move { writer_task(writer, ack_tracker, data_rx).await },
461 );
462 drop(data_tx);
463 writer_handle.await.unwrap();
464 let written = shared.data.borrow().clone();
465 let expected_data = [data_frame_1, data_frame_2].concat();
466 assert_eq!(written, vec![ack_frame, expected_data]);
467 }
468
469 #[fuchsia::test]
470 async fn test_run_target_handshake_mock_serial() {
471 let session_id = 0xCAFEBABE;
472 let host = HostHandshake::new(vec![ProtocolId::ResendSP]);
473 let (frame_type, payload) = host.start().unwrap();
474 let req_frame =
475 encode_frame(session_id, CONTROL_CHANNEL_ID, 0, frame_type, &payload).unwrap();
476 let mut mock_serial = MockSerial { read_data: vec![req_frame], write_data: Vec::new() };
477 let (proto, negotiated_sid, unconsumed) =
478 run_target_handshake(&mut mock_serial).await.expect("handshake should succeed");
479 assert_eq!(proto, ProtocolId::ResendSP);
480 assert_eq!(negotiated_sid, session_id);
481 assert!(unconsumed.is_empty());
482 assert_eq!(mock_serial.write_data.len(), 1);
483 let mut parser = FrameParser::new();
484 parser.feed(&mock_serial.write_data[0]);
485 let resp_frame = parser.next_frame().expect("valid response frame");
486 assert_eq!(resp_frame.session_id, session_id);
487 assert_eq!(resp_frame.channel_id, CONTROL_CHANNEL_ID);
488 assert_eq!(resp_frame.seq, 1);
489 assert_eq!(resp_frame.frame_type, FrameType::NegotiateResp);
490 let resp = HandshakeResponse::try_from(resp_frame.payload.as_slice()).unwrap();
491 assert_eq!(resp.status, HandshakeStatus::Success);
492 assert_eq!(resp.selected, Some(ProtocolId::ResendSP));
493 }
494
495 #[fuchsia::test]
496 async fn test_run_target_handshake_ignores_zero_session_id() {
497 let valid_sid = 0xCAFEBABE;
498 let host = HostHandshake::new(vec![ProtocolId::ResendSP]);
499 let (frame_type, payload) = host.start().unwrap();
500 let zero_req = encode_frame(0, CONTROL_CHANNEL_ID, 0, frame_type, &payload).unwrap();
501 let valid_req =
502 encode_frame(valid_sid, CONTROL_CHANNEL_ID, 0, frame_type, &payload).unwrap();
503 let mut mock_serial =
504 MockSerial { read_data: vec![zero_req, valid_req], write_data: Vec::new() };
505 let (proto, negotiated_sid, _) =
506 run_target_handshake(&mut mock_serial).await.expect("handshake should succeed");
507 assert_eq!(proto, ProtocolId::ResendSP);
508 assert_eq!(negotiated_sid, valid_sid);
509 assert_eq!(mock_serial.write_data.len(), 1);
510 }
511
512 #[fuchsia::test]
513 async fn test_serial_reader_with_initial_data() {
514 let session_id = 12345u32;
515 let frame_bytes =
516 encode_frame(session_id, 42, 0, FrameType::Data, b"buffered_stream_data").unwrap();
517 let mock_serial = MockSerial { read_data: Vec::new(), write_data: Vec::new() };
518 let mut reader = SerialReader::with_initial_data(mock_serial, &frame_bytes);
519 let frame = reader.next_frame().await.expect("Should parse frame from initial_data");
520 assert_eq!(frame.session_id, session_id);
521 assert_eq!(frame.channel_id, 42);
522 assert_eq!(frame.payload, b"buffered_stream_data");
523 }
524
525 #[fuchsia::test]
526 async fn test_serial_reader_requeue_frame_and_take_unconsumed() {
527 let session_id = 12345u32;
528 let frame1_bytes = encode_frame(session_id, 1, 0, FrameType::Data, b"first").unwrap();
529 let frame2_bytes = encode_frame(session_id, 1, 1, FrameType::Data, b"second").unwrap();
530 let mock_serial = MockSerial {
531 read_data: vec![[frame1_bytes.clone(), frame2_bytes.clone()].concat()],
532 write_data: Vec::new(),
533 };
534 let mut reader = SerialReader::new(mock_serial);
535 let parsed_frame1 = reader.next_frame().await.unwrap();
536 reader.requeue_frame(&parsed_frame1);
537 let unconsumed = reader.take_unconsumed();
538 assert_eq!(unconsumed, [frame1_bytes, frame2_bytes].concat());
539 }
540
541 #[fuchsia::test]
542 async fn test_run_target_handshake_retains_unconsumed_data() {
543 let session_id = 0x12345678;
544 let host = HostHandshake::new(vec![ProtocolId::ResendSP]);
545 let (frame_type, payload) = host.start().unwrap();
546 let req_frame =
547 encode_frame(session_id, CONTROL_CHANNEL_ID, 0, frame_type, &payload).unwrap();
548 let trailing = encode_frame(session_id, 1, 0, FrameType::Data, b"initial payload").unwrap();
549 let combined = [req_frame, trailing.clone()].concat();
550 let mut mock_serial = MockSerial { read_data: vec![combined], write_data: Vec::new() };
551 let (proto, negotiated_sid, unconsumed) =
552 run_target_handshake(&mut mock_serial).await.expect("handshake should succeed");
553 assert_eq!(proto, ProtocolId::ResendSP);
554 assert_eq!(negotiated_sid, session_id);
555 assert_eq!(unconsumed, trailing);
556 let mut reader = SerialReader::with_initial_data(mock_serial, &unconsumed);
557 let parsed = reader.next_frame().await.expect("Should parse unconsumed frame");
558 assert_eq!(parsed.channel_id, 1);
559 assert_eq!(parsed.payload, b"initial payload");
560 }
561
562 #[fuchsia::test]
563 async fn test_run_target_handshake_ignores_rejected_requests_and_retains_trailing() {
564 let unsupported_req = encode_frame(
565 0x22334455,
566 CONTROL_CHANNEL_ID,
567 0,
568 FrameType::NegotiateReq,
569 &[1, 0, 0, 0, 99],
570 )
571 .unwrap();
572 let malformed_req =
573 encode_frame(0x22334456, CONTROL_CHANNEL_ID, 0, FrameType::NegotiateReq, &[1]).unwrap();
574 let valid_sid = 0x22334457;
575 let host = HostHandshake::new(vec![ProtocolId::ResendSP]);
576 let (frame_type, payload) = host.start().unwrap();
577 let valid_req =
578 encode_frame(valid_sid, CONTROL_CHANNEL_ID, 0, frame_type, &payload).unwrap();
579 let combined = [unsupported_req, malformed_req, valid_req].concat();
582 let mut mock_serial = MockSerial { read_data: vec![combined], write_data: Vec::new() };
583 let (proto, negotiated_sid, unconsumed) =
584 run_target_handshake(&mut mock_serial).await.expect("handshake should succeed");
585 assert_eq!(proto, ProtocolId::ResendSP);
586 assert_eq!(negotiated_sid, valid_sid);
587 assert!(unconsumed.is_empty());
588 assert_eq!(mock_serial.write_data.len(), 2);
590 }
591
592 #[fuchsia::test]
593 async fn test_run_target_handshake_retries_transient_write_error() {
594 struct FlakyWriteSerial {
595 read_data: Vec<Vec<u8>>,
596 write_data: Vec<Vec<u8>>,
597 remaining_write_failures: usize,
598 }
599
600 impl SerialRead for FlakyWriteSerial {
601 async fn serial_read(&mut self) -> Result<Vec<u8>> {
602 if self.read_data.is_empty() {
603 return Ok(Vec::new());
604 }
605 Ok(self.read_data.remove(0))
606 }
607 }
608
609 impl SerialWrite for FlakyWriteSerial {
610 async fn serial_write(&mut self, data: &[u8]) -> Result<()> {
611 if self.remaining_write_failures > 0 {
612 self.remaining_write_failures -= 1;
613 return Err(TargetDriverError::DriverStatus(-27));
614 }
615 self.write_data.push(data.to_vec());
616 Ok(())
617 }
618 }
619
620 let session_id = 0x11223344;
621 let host = HostHandshake::new(vec![ProtocolId::ResendSP]);
622 let (frame_type, payload) = host.start().unwrap();
623 let req_frame =
624 encode_frame(session_id, CONTROL_CHANNEL_ID, 0, frame_type, &payload).unwrap();
625 let mut flaky = FlakyWriteSerial {
626 read_data: vec![req_frame],
627 write_data: Vec::new(),
628 remaining_write_failures: 2,
629 };
630 let (proto, negotiated_sid, _) =
631 run_target_handshake(&mut flaky).await.expect("handshake should succeed after retry");
632 assert_eq!(proto, ProtocolId::ResendSP);
633 assert_eq!(negotiated_sid, session_id);
634 assert_eq!(flaky.remaining_write_failures, 0);
635 assert_eq!(flaky.write_data.len(), 1);
636 }
637}