diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-01-19 16:01:02 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-01-19 16:01:02 +0100 |
| commit | 1e16661e0a17cdb9ad044ef9a38dacbaf35c1d10 (patch) | |
| tree | de77ef8c0c60d00808a761e74abcbbce3236332d | |
| parent | 686069b4c97b201e4fd444785d784b9836ee2271 (diff) | |
| parent | 5e08bb8bc3a7e0e308cbada03c9c363cdf5223b9 (diff) | |
Merge pull request #2311 from jamesmunns/james/read_to_break
Add a basic RP "read to break" function
| -rw-r--r-- | embassy-rp/src/uart/mod.rs | 222 |
1 files changed, 211 insertions, 11 deletions
diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 99fce0fc9..9f5ba4e8a 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs | |||
| @@ -118,6 +118,17 @@ pub enum Error { | |||
| 118 | Framing, | 118 | Framing, |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | /// Read To Break error | ||
| 122 | #[derive(Debug, Eq, PartialEq, Copy, Clone)] | ||
| 123 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 124 | #[non_exhaustive] | ||
| 125 | pub enum ReadToBreakError { | ||
| 126 | /// Read this many bytes, but never received a line break. | ||
| 127 | MissingBreak(usize), | ||
| 128 | /// Other, standard issue with the serial request | ||
| 129 | Other(Error), | ||
| 130 | } | ||
| 131 | |||
| 121 | /// Internal DMA state of UART RX. | 132 | /// Internal DMA state of UART RX. |
| 122 | pub struct DmaState { | 133 | pub struct DmaState { |
| 123 | rx_err_waker: AtomicWaker, | 134 | rx_err_waker: AtomicWaker, |
| @@ -274,14 +285,17 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { | |||
| 274 | 285 | ||
| 275 | /// Read from UART RX blocking execution until done. | 286 | /// Read from UART RX blocking execution until done. |
| 276 | pub fn blocking_read(&mut self, mut buffer: &mut [u8]) -> Result<(), Error> { | 287 | pub fn blocking_read(&mut self, mut buffer: &mut [u8]) -> Result<(), Error> { |
| 277 | while buffer.len() > 0 { | 288 | while !buffer.is_empty() { |
| 278 | let received = self.drain_fifo(buffer)?; | 289 | let received = self.drain_fifo(buffer).map_err(|(_i, e)| e)?; |
| 279 | buffer = &mut buffer[received..]; | 290 | buffer = &mut buffer[received..]; |
| 280 | } | 291 | } |
| 281 | Ok(()) | 292 | Ok(()) |
| 282 | } | 293 | } |
| 283 | 294 | ||
| 284 | fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { | 295 | /// Returns Ok(len) if no errors occurred. Returns Err((len, err)) if an error was |
| 296 | /// encountered. in both cases, `len` is the number of *good* bytes copied into | ||
| 297 | /// `buffer`. | ||
| 298 | fn drain_fifo(&mut self, buffer: &mut [u8]) -> Result<usize, (usize, Error)> { | ||
| 285 | let r = T::regs(); | 299 | let r = T::regs(); |
| 286 | for (i, b) in buffer.iter_mut().enumerate() { | 300 | for (i, b) in buffer.iter_mut().enumerate() { |
| 287 | if r.uartfr().read().rxfe() { | 301 | if r.uartfr().read().rxfe() { |
| @@ -291,13 +305,13 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { | |||
| 291 | let dr = r.uartdr().read(); | 305 | let dr = r.uartdr().read(); |
| 292 | 306 | ||
| 293 | if dr.oe() { | 307 | if dr.oe() { |
| 294 | return Err(Error::Overrun); | 308 | return Err((i, Error::Overrun)); |
| 295 | } else if dr.be() { | 309 | } else if dr.be() { |
| 296 | return Err(Error::Break); | 310 | return Err((i, Error::Break)); |
| 297 | } else if dr.pe() { | 311 | } else if dr.pe() { |
| 298 | return Err(Error::Parity); | 312 | return Err((i, Error::Parity)); |
| 299 | } else if dr.fe() { | 313 | } else if dr.fe() { |
| 300 | return Err(Error::Framing); | 314 | return Err((i, Error::Framing)); |
| 301 | } else { | 315 | } else { |
| 302 | *b = dr.data(); | 316 | *b = dr.data(); |
| 303 | } | 317 | } |
| @@ -389,7 +403,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { | |||
| 389 | } { | 403 | } { |
| 390 | Ok(len) if len < buffer.len() => &mut buffer[len..], | 404 | Ok(len) if len < buffer.len() => &mut buffer[len..], |
| 391 | Ok(_) => return Ok(()), | 405 | Ok(_) => return Ok(()), |
| 392 | Err(e) => return Err(e), | 406 | Err((_i, e)) => return Err(e), |
| 393 | }; | 407 | }; |
| 394 | 408 | ||
| 395 | // start a dma transfer. if errors have happened in the interim some error | 409 | // start a dma transfer. if errors have happened in the interim some error |
| @@ -426,13 +440,25 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { | |||
| 426 | .await; | 440 | .await; |
| 427 | 441 | ||
| 428 | let errors = match transfer_result { | 442 | let errors = match transfer_result { |
| 429 | Either::First(()) => return Ok(()), | 443 | Either::First(()) => { |
| 430 | Either::Second(e) => e, | 444 | // We're here because the DMA finished, BUT if an error occurred on the LAST |
| 445 | // byte, then we may still need to grab the error state! | ||
| 446 | Uartris(T::dma_state().rx_errs.swap(0, Ordering::Relaxed) as u32) | ||
| 447 | } | ||
| 448 | Either::Second(e) => { | ||
| 449 | // We're here because we errored, which means this is the error that | ||
| 450 | // was problematic. | ||
| 451 | e | ||
| 452 | } | ||
| 431 | }; | 453 | }; |
| 432 | 454 | ||
| 455 | // If we got no error, just return at this point | ||
| 433 | if errors.0 == 0 { | 456 | if errors.0 == 0 { |
| 434 | return Ok(()); | 457 | return Ok(()); |
| 435 | } else if errors.oeris() { | 458 | } |
| 459 | |||
| 460 | // If we DID get an error, we need to figure out which one it was. | ||
| 461 | if errors.oeris() { | ||
| 436 | return Err(Error::Overrun); | 462 | return Err(Error::Overrun); |
| 437 | } else if errors.beris() { | 463 | } else if errors.beris() { |
| 438 | return Err(Error::Break); | 464 | return Err(Error::Break); |
| @@ -443,6 +469,173 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { | |||
| 443 | } | 469 | } |
| 444 | unreachable!("unrecognized rx error"); | 470 | unreachable!("unrecognized rx error"); |
| 445 | } | 471 | } |
| 472 | |||
| 473 | /// Read from the UART, waiting for a line break. | ||
| 474 | /// | ||
| 475 | /// We read until one of the following occurs: | ||
| 476 | /// | ||
| 477 | /// * We read `buffer.len()` bytes without a line break | ||
| 478 | /// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))` | ||
| 479 | /// * We read `n` bytes then a line break occurs | ||
| 480 | /// * returns `Ok(n)` | ||
| 481 | /// * We encounter some error OTHER than a line break | ||
| 482 | /// * returns `Err(ReadToBreakError::Other(error))` | ||
| 483 | /// | ||
| 484 | /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected | ||
| 485 | /// message to reliably detect the framing on one single call to `read_to_break()`. | ||
| 486 | /// | ||
| 487 | /// * If you expect a message of 20 bytes + line break, and provide a 20-byte buffer: | ||
| 488 | /// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))` | ||
| 489 | /// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" line break | ||
| 490 | /// * If you expect a message of 20 bytes + line break, and provide a 21-byte buffer: | ||
| 491 | /// * The first call to `read_to_break()` will return `Ok(20)`. | ||
| 492 | /// * The next call to `read_to_break()` will work as expected | ||
| 493 | pub async fn read_to_break(&mut self, buffer: &mut [u8]) -> Result<usize, ReadToBreakError> { | ||
| 494 | // clear error flags before we drain the fifo. errors that have accumulated | ||
| 495 | // in the flags will also be present in the fifo. | ||
| 496 | T::dma_state().rx_errs.store(0, Ordering::Relaxed); | ||
| 497 | T::regs().uarticr().write(|w| { | ||
| 498 | w.set_oeic(true); | ||
| 499 | w.set_beic(true); | ||
| 500 | w.set_peic(true); | ||
| 501 | w.set_feic(true); | ||
| 502 | }); | ||
| 503 | |||
| 504 | // then drain the fifo. we need to read at most 32 bytes. errors that apply | ||
| 505 | // to fifo bytes will be reported directly. | ||
| 506 | let sbuffer = match { | ||
| 507 | let limit = buffer.len().min(32); | ||
| 508 | self.drain_fifo(&mut buffer[0..limit]) | ||
| 509 | } { | ||
| 510 | // Drained fifo, still some room left! | ||
| 511 | Ok(len) if len < buffer.len() => &mut buffer[len..], | ||
| 512 | // Drained (some/all of the fifo), no room left | ||
| 513 | Ok(len) => return Err(ReadToBreakError::MissingBreak(len)), | ||
| 514 | // We got a break WHILE draining the FIFO, return what we did get before the break | ||
| 515 | Err((i, Error::Break)) => return Ok(i), | ||
| 516 | // Some other error, just return the error | ||
| 517 | Err((_i, e)) => return Err(ReadToBreakError::Other(e)), | ||
| 518 | }; | ||
| 519 | |||
| 520 | // start a dma transfer. if errors have happened in the interim some error | ||
| 521 | // interrupt flags will have been raised, and those will be picked up immediately | ||
| 522 | // by the interrupt handler. | ||
| 523 | let mut ch = self.rx_dma.as_mut().unwrap(); | ||
| 524 | T::regs().uartimsc().write_set(|w| { | ||
| 525 | w.set_oeim(true); | ||
| 526 | w.set_beim(true); | ||
| 527 | w.set_peim(true); | ||
| 528 | w.set_feim(true); | ||
| 529 | }); | ||
| 530 | T::regs().uartdmacr().write_set(|reg| { | ||
| 531 | reg.set_rxdmae(true); | ||
| 532 | reg.set_dmaonerr(true); | ||
| 533 | }); | ||
| 534 | let transfer = unsafe { | ||
| 535 | // If we don't assign future to a variable, the data register pointer | ||
| 536 | // is held across an await and makes the future non-Send. | ||
| 537 | crate::dma::read(&mut ch, T::regs().uartdr().as_ptr() as *const _, sbuffer, T::RX_DREQ) | ||
| 538 | }; | ||
| 539 | |||
| 540 | // wait for either the transfer to complete or an error to happen. | ||
| 541 | let transfer_result = select( | ||
| 542 | transfer, | ||
| 543 | poll_fn(|cx| { | ||
| 544 | T::dma_state().rx_err_waker.register(cx.waker()); | ||
| 545 | match T::dma_state().rx_errs.swap(0, Ordering::Relaxed) { | ||
| 546 | 0 => Poll::Pending, | ||
| 547 | e => Poll::Ready(Uartris(e as u32)), | ||
| 548 | } | ||
| 549 | }), | ||
| 550 | ) | ||
| 551 | .await; | ||
| 552 | |||
| 553 | // Figure out our error state | ||
| 554 | let errors = match transfer_result { | ||
| 555 | Either::First(()) => { | ||
| 556 | // We're here because the DMA finished, BUT if an error occurred on the LAST | ||
| 557 | // byte, then we may still need to grab the error state! | ||
| 558 | Uartris(T::dma_state().rx_errs.swap(0, Ordering::Relaxed) as u32) | ||
| 559 | } | ||
| 560 | Either::Second(e) => { | ||
| 561 | // We're here because we errored, which means this is the error that | ||
| 562 | // was problematic. | ||
| 563 | e | ||
| 564 | } | ||
| 565 | }; | ||
| 566 | |||
| 567 | if errors.0 == 0 { | ||
| 568 | // No errors? That means we filled the buffer without a line break. | ||
| 569 | // For THIS function, that's a problem. | ||
| 570 | return Err(ReadToBreakError::MissingBreak(buffer.len())); | ||
| 571 | } else if errors.beris() { | ||
| 572 | // We got a Line Break! By this point, we've finished/aborted the DMA | ||
| 573 | // transaction, which means that we need to figure out where it left off | ||
| 574 | // by looking at the write_addr. | ||
| 575 | // | ||
| 576 | // First, we do a sanity check to make sure the write value is within the | ||
| 577 | // range of DMA we just did. | ||
| 578 | let sval = buffer.as_ptr() as usize; | ||
| 579 | let eval = sval + buffer.len(); | ||
| 580 | |||
| 581 | // This is the address where the DMA would write to next | ||
| 582 | let next_addr = ch.regs().write_addr().read() as usize; | ||
| 583 | |||
| 584 | // If we DON'T end up inside the range, something has gone really wrong. | ||
| 585 | // Note that it's okay that `eval` is one past the end of the slice, as | ||
| 586 | // this is where the write pointer will end up at the end of a full | ||
| 587 | // transfer. | ||
| 588 | if (next_addr < sval) || (next_addr > eval) { | ||
| 589 | unreachable!("UART DMA reported invalid `write_addr`"); | ||
| 590 | } | ||
| 591 | |||
| 592 | let regs = T::regs(); | ||
| 593 | let all_full = next_addr == eval; | ||
| 594 | |||
| 595 | // NOTE: This is off label usage of RSR! See the issue below for | ||
| 596 | // why I am not checking if there is an "extra" FIFO byte, and why | ||
| 597 | // I am checking RSR directly (it seems to report the status of the LAST | ||
| 598 | // POPPED value, rather than the NEXT TO POP value like the datasheet | ||
| 599 | // suggests!) | ||
| 600 | // | ||
| 601 | // issue: https://github.com/raspberrypi/pico-feedback/issues/367 | ||
| 602 | let last_was_break = regs.uartrsr().read().be(); | ||
| 603 | |||
| 604 | return match (all_full, last_was_break) { | ||
| 605 | (true, true) | (false, _) => { | ||
| 606 | // We got less than the full amount + a break, or the full amount | ||
| 607 | // and the last byte was a break. Subtract the break off. | ||
| 608 | Ok((next_addr - 1) - sval) | ||
| 609 | } | ||
| 610 | (true, false) => { | ||
| 611 | // We finished the whole DMA, and the last DMA'd byte was NOT a break | ||
| 612 | // character. This is an error. | ||
| 613 | // | ||
| 614 | // NOTE: we COULD potentially return Ok(buffer.len()) here, since we | ||
| 615 | // know a line break occured at SOME POINT after the DMA completed. | ||
| 616 | // | ||
| 617 | // However, we have no way of knowing if there was extra data BEFORE | ||
| 618 | // that line break, so instead return an Err to signal to the caller | ||
| 619 | // that there are "leftovers", and they'll catch the actual line break | ||
| 620 | // on the next call. | ||
| 621 | // | ||
| 622 | // Doing it like this also avoids racyness: now whether you finished | ||
| 623 | // the full read BEFORE the line break occurred or AFTER the line break | ||
| 624 | // occurs, you still get `MissingBreak(buffer.len())` instead of sometimes | ||
| 625 | // getting `Ok(buffer.len())` if you were "late enough" to observe the | ||
| 626 | // line break. | ||
| 627 | Err(ReadToBreakError::MissingBreak(buffer.len())) | ||
| 628 | } | ||
| 629 | }; | ||
| 630 | } else if errors.oeris() { | ||
| 631 | return Err(ReadToBreakError::Other(Error::Overrun)); | ||
| 632 | } else if errors.peris() { | ||
| 633 | return Err(ReadToBreakError::Other(Error::Parity)); | ||
| 634 | } else if errors.feris() { | ||
| 635 | return Err(ReadToBreakError::Other(Error::Framing)); | ||
| 636 | } | ||
| 637 | unreachable!("unrecognized rx error"); | ||
| 638 | } | ||
| 446 | } | 639 | } |
| 447 | 640 | ||
| 448 | impl<'d, T: Instance> Uart<'d, T, Blocking> { | 641 | impl<'d, T: Instance> Uart<'d, T, Blocking> { |
| @@ -743,6 +936,13 @@ impl<'d, T: Instance> Uart<'d, T, Async> { | |||
| 743 | pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { | 936 | pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { |
| 744 | self.rx.read(buffer).await | 937 | self.rx.read(buffer).await |
| 745 | } | 938 | } |
| 939 | |||
| 940 | /// Read until the buffer is full or a line break occurs. | ||
| 941 | /// | ||
| 942 | /// See [`UartRx::read_to_break()`] for more details | ||
| 943 | pub async fn read_to_break<'a>(&mut self, buf: &'a mut [u8]) -> Result<usize, ReadToBreakError> { | ||
| 944 | self.rx.read_to_break(buf).await | ||
| 945 | } | ||
| 746 | } | 946 | } |
| 747 | 947 | ||
| 748 | impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read<u8> for UartRx<'d, T, M> { | 948 | impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read<u8> for UartRx<'d, T, M> { |
