diff options
Diffstat (limited to 'embassy-rp')
| -rw-r--r-- | embassy-rp/src/uart/mod.rs | 255 |
1 files changed, 155 insertions, 100 deletions
diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index f546abe71..d50f5b4d5 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs | |||
| @@ -490,6 +490,36 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { | |||
| 490 | /// * The first call to `read_to_break()` will return `Ok(20)`. | 490 | /// * The first call to `read_to_break()` will return `Ok(20)`. |
| 491 | /// * The next call to `read_to_break()` will work as expected | 491 | /// * The next call to `read_to_break()` will work as expected |
| 492 | pub async fn read_to_break(&mut self, buffer: &mut [u8]) -> Result<usize, ReadToBreakError> { | 492 | pub async fn read_to_break(&mut self, buffer: &mut [u8]) -> Result<usize, ReadToBreakError> { |
| 493 | self.read_to_break_with_count(buffer, 0).await | ||
| 494 | } | ||
| 495 | |||
| 496 | /// Read from the UART, waiting for a line break as soon as at least `min_count` bytes have been read. | ||
| 497 | /// | ||
| 498 | /// We read until one of the following occurs: | ||
| 499 | /// | ||
| 500 | /// * We read `buffer.len()` bytes without a line break | ||
| 501 | /// * returns `Err(ReadToBreakError::MissingBreak(buffer.len()))` | ||
| 502 | /// * We read `n > min_count` bytes then a line break occurs | ||
| 503 | /// * returns `Ok(n)` | ||
| 504 | /// * We encounter some error OTHER than a line break | ||
| 505 | /// * returns `Err(ReadToBreakError::Other(error))` | ||
| 506 | /// | ||
| 507 | /// If a line break occurs before `min_count` bytes have been read, the break will be ignored and the read will continue | ||
| 508 | /// | ||
| 509 | /// **NOTE**: you MUST provide a buffer one byte larger than your largest expected | ||
| 510 | /// message to reliably detect the framing on one single call to `read_to_break()`. | ||
| 511 | /// | ||
| 512 | /// * If you expect a message of 20 bytes + line break, and provide a 20-byte buffer: | ||
| 513 | /// * The first call to `read_to_break()` will return `Err(ReadToBreakError::MissingBreak(20))` | ||
| 514 | /// * The next call to `read_to_break()` will immediately return `Ok(0)`, from the "stale" line break | ||
| 515 | /// * If you expect a message of 20 bytes + line break, and provide a 21-byte buffer: | ||
| 516 | /// * The first call to `read_to_break()` will return `Ok(20)`. | ||
| 517 | /// * The next call to `read_to_break()` will work as expected | ||
| 518 | pub async fn read_to_break_with_count( | ||
| 519 | &mut self, | ||
| 520 | buffer: &mut [u8], | ||
| 521 | min_count: usize, | ||
| 522 | ) -> Result<usize, ReadToBreakError> { | ||
| 493 | // clear error flags before we drain the fifo. errors that have accumulated | 523 | // clear error flags before we drain the fifo. errors that have accumulated |
| 494 | // in the flags will also be present in the fifo. | 524 | // in the flags will also be present in the fifo. |
| 495 | T::dma_state().rx_errs.store(0, Ordering::Relaxed); | 525 | T::dma_state().rx_errs.store(0, Ordering::Relaxed); |
| @@ -502,7 +532,7 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { | |||
| 502 | 532 | ||
| 503 | // then drain the fifo. we need to read at most 32 bytes. errors that apply | 533 | // then drain the fifo. we need to read at most 32 bytes. errors that apply |
| 504 | // to fifo bytes will be reported directly. | 534 | // to fifo bytes will be reported directly. |
| 505 | let sbuffer = match { | 535 | let mut sbuffer = match { |
| 506 | let limit = buffer.len().min(32); | 536 | let limit = buffer.len().min(32); |
| 507 | self.drain_fifo(&mut buffer[0..limit]) | 537 | self.drain_fifo(&mut buffer[0..limit]) |
| 508 | } { | 538 | } { |
| @@ -511,7 +541,13 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { | |||
| 511 | // Drained (some/all of the fifo), no room left | 541 | // Drained (some/all of the fifo), no room left |
| 512 | Ok(len) => return Err(ReadToBreakError::MissingBreak(len)), | 542 | Ok(len) => return Err(ReadToBreakError::MissingBreak(len)), |
| 513 | // We got a break WHILE draining the FIFO, return what we did get before the break | 543 | // We got a break WHILE draining the FIFO, return what we did get before the break |
| 514 | Err((i, Error::Break)) => return Ok(i), | 544 | Err((len, Error::Break)) => { |
| 545 | if len < min_count && len < buffer.len() { | ||
| 546 | &mut buffer[len..] | ||
| 547 | } else { | ||
| 548 | return Ok(len); | ||
| 549 | } | ||
| 550 | } | ||
| 515 | // Some other error, just return the error | 551 | // Some other error, just return the error |
| 516 | Err((_i, e)) => return Err(ReadToBreakError::Other(e)), | 552 | Err((_i, e)) => return Err(ReadToBreakError::Other(e)), |
| 517 | }; | 553 | }; |
| @@ -530,110 +566,118 @@ impl<'d, T: Instance> UartRx<'d, T, Async> { | |||
| 530 | reg.set_rxdmae(true); | 566 | reg.set_rxdmae(true); |
| 531 | reg.set_dmaonerr(true); | 567 | reg.set_dmaonerr(true); |
| 532 | }); | 568 | }); |
| 533 | let transfer = unsafe { | ||
| 534 | // If we don't assign future to a variable, the data register pointer | ||
| 535 | // is held across an await and makes the future non-Send. | ||
| 536 | crate::dma::read(&mut ch, T::regs().uartdr().as_ptr() as *const _, sbuffer, T::RX_DREQ) | ||
| 537 | }; | ||
| 538 | 569 | ||
| 539 | // wait for either the transfer to complete or an error to happen. | 570 | loop { |
| 540 | let transfer_result = select( | 571 | let transfer = unsafe { |
| 541 | transfer, | 572 | // If we don't assign future to a variable, the data register pointer |
| 542 | poll_fn(|cx| { | 573 | // is held across an await and makes the future non-Send. |
| 543 | T::dma_state().rx_err_waker.register(cx.waker()); | 574 | crate::dma::read(&mut ch, T::regs().uartdr().as_ptr() as *const _, sbuffer, T::RX_DREQ) |
| 544 | match T::dma_state().rx_errs.swap(0, Ordering::Relaxed) { | 575 | }; |
| 545 | 0 => Poll::Pending, | ||
| 546 | e => Poll::Ready(Uartris(e as u32)), | ||
| 547 | } | ||
| 548 | }), | ||
| 549 | ) | ||
| 550 | .await; | ||
| 551 | |||
| 552 | // Figure out our error state | ||
| 553 | let errors = match transfer_result { | ||
| 554 | Either::First(()) => { | ||
| 555 | // We're here because the DMA finished, BUT if an error occurred on the LAST | ||
| 556 | // byte, then we may still need to grab the error state! | ||
| 557 | Uartris(T::dma_state().rx_errs.swap(0, Ordering::Relaxed) as u32) | ||
| 558 | } | ||
| 559 | Either::Second(e) => { | ||
| 560 | // We're here because we errored, which means this is the error that | ||
| 561 | // was problematic. | ||
| 562 | e | ||
| 563 | } | ||
| 564 | }; | ||
| 565 | |||
| 566 | if errors.0 == 0 { | ||
| 567 | // No errors? That means we filled the buffer without a line break. | ||
| 568 | // For THIS function, that's a problem. | ||
| 569 | return Err(ReadToBreakError::MissingBreak(buffer.len())); | ||
| 570 | } else if errors.beris() { | ||
| 571 | // We got a Line Break! By this point, we've finished/aborted the DMA | ||
| 572 | // transaction, which means that we need to figure out where it left off | ||
| 573 | // by looking at the write_addr. | ||
| 574 | // | ||
| 575 | // First, we do a sanity check to make sure the write value is within the | ||
| 576 | // range of DMA we just did. | ||
| 577 | let sval = buffer.as_ptr() as usize; | ||
| 578 | let eval = sval + buffer.len(); | ||
| 579 | |||
| 580 | // This is the address where the DMA would write to next | ||
| 581 | let next_addr = ch.regs().write_addr().read() as usize; | ||
| 582 | |||
| 583 | // If we DON'T end up inside the range, something has gone really wrong. | ||
| 584 | // Note that it's okay that `eval` is one past the end of the slice, as | ||
| 585 | // this is where the write pointer will end up at the end of a full | ||
| 586 | // transfer. | ||
| 587 | if (next_addr < sval) || (next_addr > eval) { | ||
| 588 | unreachable!("UART DMA reported invalid `write_addr`"); | ||
| 589 | } | ||
| 590 | 576 | ||
| 591 | let regs = T::regs(); | 577 | // wait for either the transfer to complete or an error to happen. |
| 592 | let all_full = next_addr == eval; | 578 | let transfer_result = select( |
| 593 | 579 | transfer, | |
| 594 | // NOTE: This is off label usage of RSR! See the issue below for | 580 | poll_fn(|cx| { |
| 595 | // why I am not checking if there is an "extra" FIFO byte, and why | 581 | T::dma_state().rx_err_waker.register(cx.waker()); |
| 596 | // I am checking RSR directly (it seems to report the status of the LAST | 582 | match T::dma_state().rx_errs.swap(0, Ordering::Relaxed) { |
| 597 | // POPPED value, rather than the NEXT TO POP value like the datasheet | 583 | 0 => Poll::Pending, |
| 598 | // suggests!) | 584 | e => Poll::Ready(Uartris(e as u32)), |
| 599 | // | 585 | } |
| 600 | // issue: https://github.com/raspberrypi/pico-feedback/issues/367 | 586 | }), |
| 601 | let last_was_break = regs.uartrsr().read().be(); | 587 | ) |
| 602 | 588 | .await; | |
| 603 | return match (all_full, last_was_break) { | 589 | |
| 604 | (true, true) | (false, _) => { | 590 | // Figure out our error state |
| 605 | // We got less than the full amount + a break, or the full amount | 591 | let errors = match transfer_result { |
| 606 | // and the last byte was a break. Subtract the break off by adding one to sval. | 592 | Either::First(()) => { |
| 607 | Ok(next_addr.saturating_sub(1 + sval)) | 593 | // We're here because the DMA finished, BUT if an error occurred on the LAST |
| 594 | // byte, then we may still need to grab the error state! | ||
| 595 | Uartris(T::dma_state().rx_errs.swap(0, Ordering::Relaxed) as u32) | ||
| 608 | } | 596 | } |
| 609 | (true, false) => { | 597 | Either::Second(e) => { |
| 610 | // We finished the whole DMA, and the last DMA'd byte was NOT a break | 598 | // We're here because we errored, which means this is the error that |
| 611 | // character. This is an error. | 599 | // was problematic. |
| 612 | // | 600 | e |
| 613 | // NOTE: we COULD potentially return Ok(buffer.len()) here, since we | ||
| 614 | // know a line break occured at SOME POINT after the DMA completed. | ||
| 615 | // | ||
| 616 | // However, we have no way of knowing if there was extra data BEFORE | ||
| 617 | // that line break, so instead return an Err to signal to the caller | ||
| 618 | // that there are "leftovers", and they'll catch the actual line break | ||
| 619 | // on the next call. | ||
| 620 | // | ||
| 621 | // Doing it like this also avoids racyness: now whether you finished | ||
| 622 | // the full read BEFORE the line break occurred or AFTER the line break | ||
| 623 | // occurs, you still get `MissingBreak(buffer.len())` instead of sometimes | ||
| 624 | // getting `Ok(buffer.len())` if you were "late enough" to observe the | ||
| 625 | // line break. | ||
| 626 | Err(ReadToBreakError::MissingBreak(buffer.len())) | ||
| 627 | } | 601 | } |
| 628 | }; | 602 | }; |
| 629 | } else if errors.oeris() { | 603 | |
| 630 | return Err(ReadToBreakError::Other(Error::Overrun)); | 604 | if errors.0 == 0 { |
| 631 | } else if errors.peris() { | 605 | // No errors? That means we filled the buffer without a line break. |
| 632 | return Err(ReadToBreakError::Other(Error::Parity)); | 606 | // For THIS function, that's a problem. |
| 633 | } else if errors.feris() { | 607 | return Err(ReadToBreakError::MissingBreak(buffer.len())); |
| 634 | return Err(ReadToBreakError::Other(Error::Framing)); | 608 | } else if errors.beris() { |
| 609 | // We got a Line Break! By this point, we've finished/aborted the DMA | ||
| 610 | // transaction, which means that we need to figure out where it left off | ||
| 611 | // by looking at the write_addr. | ||
| 612 | // | ||
| 613 | // First, we do a sanity check to make sure the write value is within the | ||
| 614 | // range of DMA we just did. | ||
| 615 | let sval = buffer.as_ptr() as usize; | ||
| 616 | let eval = sval + buffer.len(); | ||
| 617 | |||
| 618 | // This is the address where the DMA would write to next | ||
| 619 | let next_addr = ch.regs().write_addr().read() as usize; | ||
| 620 | |||
| 621 | // If we DON'T end up inside the range, something has gone really wrong. | ||
| 622 | // Note that it's okay that `eval` is one past the end of the slice, as | ||
| 623 | // this is where the write pointer will end up at the end of a full | ||
| 624 | // transfer. | ||
| 625 | if (next_addr < sval) || (next_addr > eval) { | ||
| 626 | unreachable!("UART DMA reported invalid `write_addr`"); | ||
| 627 | } | ||
| 628 | |||
| 629 | if (next_addr - sval) < min_count { | ||
| 630 | sbuffer = &mut buffer[(next_addr - sval)..]; | ||
| 631 | continue; | ||
| 632 | } | ||
| 633 | |||
| 634 | let regs = T::regs(); | ||
| 635 | let all_full = next_addr == eval; | ||
| 636 | |||
| 637 | // NOTE: This is off label usage of RSR! See the issue below for | ||
| 638 | // why I am not checking if there is an "extra" FIFO byte, and why | ||
| 639 | // I am checking RSR directly (it seems to report the status of the LAST | ||
| 640 | // POPPED value, rather than the NEXT TO POP value like the datasheet | ||
| 641 | // suggests!) | ||
| 642 | // | ||
| 643 | // issue: https://github.com/raspberrypi/pico-feedback/issues/367 | ||
| 644 | let last_was_break = regs.uartrsr().read().be(); | ||
| 645 | |||
| 646 | return match (all_full, last_was_break) { | ||
| 647 | (true, true) | (false, _) => { | ||
| 648 | // We got less than the full amount + a break, or the full amount | ||
| 649 | // and the last byte was a break. Subtract the break off by adding one to sval. | ||
| 650 | Ok(next_addr.saturating_sub(1 + sval)) | ||
| 651 | } | ||
| 652 | (true, false) => { | ||
| 653 | // We finished the whole DMA, and the last DMA'd byte was NOT a break | ||
| 654 | // character. This is an error. | ||
| 655 | // | ||
| 656 | // NOTE: we COULD potentially return Ok(buffer.len()) here, since we | ||
| 657 | // know a line break occured at SOME POINT after the DMA completed. | ||
| 658 | // | ||
| 659 | // However, we have no way of knowing if there was extra data BEFORE | ||
| 660 | // that line break, so instead return an Err to signal to the caller | ||
| 661 | // that there are "leftovers", and they'll catch the actual line break | ||
| 662 | // on the next call. | ||
| 663 | // | ||
| 664 | // Doing it like this also avoids racyness: now whether you finished | ||
| 665 | // the full read BEFORE the line break occurred or AFTER the line break | ||
| 666 | // occurs, you still get `MissingBreak(buffer.len())` instead of sometimes | ||
| 667 | // getting `Ok(buffer.len())` if you were "late enough" to observe the | ||
| 668 | // line break. | ||
| 669 | Err(ReadToBreakError::MissingBreak(buffer.len())) | ||
| 670 | } | ||
| 671 | }; | ||
| 672 | } else if errors.oeris() { | ||
| 673 | return Err(ReadToBreakError::Other(Error::Overrun)); | ||
| 674 | } else if errors.peris() { | ||
| 675 | return Err(ReadToBreakError::Other(Error::Parity)); | ||
| 676 | } else if errors.feris() { | ||
| 677 | return Err(ReadToBreakError::Other(Error::Framing)); | ||
| 678 | } | ||
| 679 | unreachable!("unrecognized rx error"); | ||
| 635 | } | 680 | } |
| 636 | unreachable!("unrecognized rx error"); | ||
| 637 | } | 681 | } |
| 638 | } | 682 | } |
| 639 | 683 | ||
| @@ -997,6 +1041,17 @@ impl<'d, T: Instance> Uart<'d, T, Async> { | |||
| 997 | pub async fn read_to_break<'a>(&mut self, buf: &'a mut [u8]) -> Result<usize, ReadToBreakError> { | 1041 | pub async fn read_to_break<'a>(&mut self, buf: &'a mut [u8]) -> Result<usize, ReadToBreakError> { |
| 998 | self.rx.read_to_break(buf).await | 1042 | self.rx.read_to_break(buf).await |
| 999 | } | 1043 | } |
| 1044 | |||
| 1045 | /// Read until the buffer is full or a line break occurs after at least `min_count` bytes have been read. | ||
| 1046 | /// | ||
| 1047 | /// See [`UartRx::read_to_break_with_count()`] for more details | ||
| 1048 | pub async fn read_to_break_with_count<'a>( | ||
| 1049 | &mut self, | ||
| 1050 | buf: &'a mut [u8], | ||
| 1051 | min_count: usize, | ||
| 1052 | ) -> Result<usize, ReadToBreakError> { | ||
| 1053 | self.rx.read_to_break_with_count(buf, min_count).await | ||
| 1054 | } | ||
| 1000 | } | 1055 | } |
| 1001 | 1056 | ||
| 1002 | impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read<u8> for UartRx<'d, T, M> { | 1057 | impl<'d, T: Instance, M: Mode> embedded_hal_02::serial::Read<u8> for UartRx<'d, T, M> { |
