diff options
| author | elagil <[email protected]> | 2024-09-05 21:29:04 +0200 |
|---|---|---|
| committer | elagil <[email protected]> | 2024-09-05 21:29:04 +0200 |
| commit | ccf68d73910a68dc9e33beb666b20aba7430e015 (patch) | |
| tree | b589f8a5301f9a1514fba3836f394f20966f0a90 | |
| parent | b8fa5cdf06f54cda1895b6d5e8d3b436dbce08ac (diff) | |
feat(usb): add support for ISO endpoints
| -rw-r--r-- | embassy-stm32/src/usb/usb.rs | 211 |
1 files changed, 186 insertions, 25 deletions
diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 9384c8688..0ab2306c8 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs | |||
| @@ -80,6 +80,8 @@ impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandl | |||
| 80 | 80 | ||
| 81 | if istr.ctr() { | 81 | if istr.ctr() { |
| 82 | let index = istr.ep_id() as usize; | 82 | let index = istr.ep_id() as usize; |
| 83 | CTR_TRIGGERED[index].store(true, Ordering::Relaxed); | ||
| 84 | |||
| 83 | let mut epr = regs.epr(index).read(); | 85 | let mut epr = regs.epr(index).read(); |
| 84 | if epr.ctr_rx() { | 86 | if epr.ctr_rx() { |
| 85 | if index == 0 && epr.setup() { | 87 | if index == 0 && epr.setup() { |
| @@ -120,6 +122,10 @@ const USBRAM_ALIGN: usize = 4; | |||
| 120 | const NEW_AW: AtomicWaker = AtomicWaker::new(); | 122 | const NEW_AW: AtomicWaker = AtomicWaker::new(); |
| 121 | static BUS_WAKER: AtomicWaker = NEW_AW; | 123 | static BUS_WAKER: AtomicWaker = NEW_AW; |
| 122 | static EP0_SETUP: AtomicBool = AtomicBool::new(false); | 124 | static EP0_SETUP: AtomicBool = AtomicBool::new(false); |
| 125 | |||
| 126 | const NEW_CTR_TRIGGERED: AtomicBool = AtomicBool::new(false); | ||
| 127 | static CTR_TRIGGERED: [AtomicBool; EP_COUNT] = [NEW_CTR_TRIGGERED; EP_COUNT]; | ||
| 128 | |||
| 123 | static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; | 129 | static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; |
| 124 | static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; | 130 | static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; |
| 125 | static IRQ_RESET: AtomicBool = AtomicBool::new(false); | 131 | static IRQ_RESET: AtomicBool = AtomicBool::new(false); |
| @@ -163,20 +169,37 @@ fn calc_out_len(len: u16) -> (u16, u16) { | |||
| 163 | mod btable { | 169 | mod btable { |
| 164 | use super::*; | 170 | use super::*; |
| 165 | 171 | ||
| 166 | pub(super) fn write_in<T: Instance>(index: usize, addr: u16) { | 172 | pub(super) fn write_in_tx<T: Instance>(index: usize, addr: u16) { |
| 167 | USBRAM.mem(index * 4 + 0).write_value(addr); | 173 | USBRAM.mem(index * 4 + 0).write_value(addr); |
| 168 | } | 174 | } |
| 169 | 175 | ||
| 170 | pub(super) fn write_in_len<T: Instance>(index: usize, _addr: u16, len: u16) { | 176 | pub(super) fn write_in_rx<T: Instance>(index: usize, addr: u16) { |
| 177 | USBRAM.mem(index * 4 + 2).write_value(addr); | ||
| 178 | } | ||
| 179 | |||
| 180 | pub(super) fn write_in_len_rx<T: Instance>(index: usize, _addr: u16, len: u16) { | ||
| 181 | USBRAM.mem(index * 4 + 3).write_value(len); | ||
| 182 | } | ||
| 183 | |||
| 184 | pub(super) fn write_in_len_tx<T: Instance>(index: usize, _addr: u16, len: u16) { | ||
| 171 | USBRAM.mem(index * 4 + 1).write_value(len); | 185 | USBRAM.mem(index * 4 + 1).write_value(len); |
| 172 | } | 186 | } |
| 173 | 187 | ||
| 174 | pub(super) fn write_out<T: Instance>(index: usize, addr: u16, max_len_bits: u16) { | 188 | pub(super) fn write_out_rx<T: Instance>(index: usize, addr: u16, max_len_bits: u16) { |
| 175 | USBRAM.mem(index * 4 + 2).write_value(addr); | 189 | USBRAM.mem(index * 4 + 2).write_value(addr); |
| 176 | USBRAM.mem(index * 4 + 3).write_value(max_len_bits); | 190 | USBRAM.mem(index * 4 + 3).write_value(max_len_bits); |
| 177 | } | 191 | } |
| 178 | 192 | ||
| 179 | pub(super) fn read_out_len<T: Instance>(index: usize) -> u16 { | 193 | pub(super) fn write_out_tx<T: Instance>(index: usize, addr: u16, max_len_bits: u16) { |
| 194 | USBRAM.mem(index * 4 + 0).write_value(addr); | ||
| 195 | USBRAM.mem(index * 4 + 1).write_value(max_len_bits); | ||
| 196 | } | ||
| 197 | |||
| 198 | pub(super) fn read_out_len_tx<T: Instance>(index: usize) -> u16 { | ||
| 199 | USBRAM.mem(index * 4 + 1).read() | ||
| 200 | } | ||
| 201 | |||
| 202 | pub(super) fn read_out_len_rx<T: Instance>(index: usize) -> u16 { | ||
| 180 | USBRAM.mem(index * 4 + 3).read() | 203 | USBRAM.mem(index * 4 + 3).read() |
| 181 | } | 204 | } |
| 182 | } | 205 | } |
| @@ -184,19 +207,37 @@ mod btable { | |||
| 184 | mod btable { | 207 | mod btable { |
| 185 | use super::*; | 208 | use super::*; |
| 186 | 209 | ||
| 187 | pub(super) fn write_in<T: Instance>(_index: usize, _addr: u16) {} | 210 | pub(super) fn write_in_tx<T: Instance>(_index: usize, _addr: u16) {} |
| 188 | 211 | ||
| 189 | pub(super) fn write_in_len<T: Instance>(index: usize, addr: u16, len: u16) { | 212 | pub(super) fn write_in_rx<T: Instance>(_index: usize, _addr: u16) {} |
| 213 | |||
| 214 | pub(super) fn write_in_len_tx<T: Instance>(index: usize, addr: u16, len: u16) { | ||
| 190 | USBRAM.mem(index * 2).write_value((addr as u32) | ((len as u32) << 16)); | 215 | USBRAM.mem(index * 2).write_value((addr as u32) | ((len as u32) << 16)); |
| 191 | } | 216 | } |
| 192 | 217 | ||
| 193 | pub(super) fn write_out<T: Instance>(index: usize, addr: u16, max_len_bits: u16) { | 218 | pub(super) fn write_in_len_rx<T: Instance>(index: usize, addr: u16, len: u16) { |
| 219 | USBRAM | ||
| 220 | .mem(index * 2 + 1) | ||
| 221 | .write_value((addr as u32) | ((len as u32) << 16)); | ||
| 222 | } | ||
| 223 | |||
| 224 | pub(super) fn write_out_tx<T: Instance>(index: usize, addr: u16, max_len_bits: u16) { | ||
| 225 | USBRAM | ||
| 226 | .mem(index * 2) | ||
| 227 | .write_value((addr as u32) | ((max_len_bits as u32) << 16)); | ||
| 228 | } | ||
| 229 | |||
| 230 | pub(super) fn write_out_rx<T: Instance>(index: usize, addr: u16, max_len_bits: u16) { | ||
| 194 | USBRAM | 231 | USBRAM |
| 195 | .mem(index * 2 + 1) | 232 | .mem(index * 2 + 1) |
| 196 | .write_value((addr as u32) | ((max_len_bits as u32) << 16)); | 233 | .write_value((addr as u32) | ((max_len_bits as u32) << 16)); |
| 197 | } | 234 | } |
| 198 | 235 | ||
| 199 | pub(super) fn read_out_len<T: Instance>(index: usize) -> u16 { | 236 | pub(super) fn read_out_len_tx<T: Instance>(index: usize) -> u16 { |
| 237 | (USBRAM.mem(index * 2).read() >> 16) as u16 | ||
| 238 | } | ||
| 239 | |||
| 240 | pub(super) fn read_out_len_rx<T: Instance>(index: usize) -> u16 { | ||
| 200 | (USBRAM.mem(index * 2 + 1).read() >> 16) as u16 | 241 | (USBRAM.mem(index * 2 + 1).read() >> 16) as u16 |
| 201 | } | 242 | } |
| 202 | } | 243 | } |
| @@ -327,6 +368,13 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 327 | return false; // reserved for control pipe | 368 | return false; // reserved for control pipe |
| 328 | } | 369 | } |
| 329 | let used = ep.used_out || ep.used_in; | 370 | let used = ep.used_out || ep.used_in; |
| 371 | if used && (ep.ep_type == EndpointType::Isochronous || ep.ep_type == EndpointType::Bulk) { | ||
| 372 | // Isochronous and bulk endpoints are double-buffered. | ||
| 373 | // Their corresponding endpoint/channel registers are forced to be unidirectional. | ||
| 374 | // Do not reuse this index. | ||
| 375 | return false; | ||
| 376 | } | ||
| 377 | |||
| 330 | let used_dir = match D::dir() { | 378 | let used_dir = match D::dir() { |
| 331 | Direction::Out => ep.used_out, | 379 | Direction::Out => ep.used_out, |
| 332 | Direction::In => ep.used_in, | 380 | Direction::In => ep.used_in, |
| @@ -350,7 +398,11 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 350 | let addr = self.alloc_ep_mem(len); | 398 | let addr = self.alloc_ep_mem(len); |
| 351 | 399 | ||
| 352 | trace!(" len_bits = {:04x}", len_bits); | 400 | trace!(" len_bits = {:04x}", len_bits); |
| 353 | btable::write_out::<T>(index, addr, len_bits); | 401 | btable::write_out_rx::<T>(index, addr, len_bits); |
| 402 | |||
| 403 | if ep_type == EndpointType::Isochronous { | ||
| 404 | btable::write_out_tx::<T>(index, addr, len_bits); | ||
| 405 | } | ||
| 354 | 406 | ||
| 355 | EndpointBuffer { | 407 | EndpointBuffer { |
| 356 | addr, | 408 | addr, |
| @@ -366,7 +418,11 @@ impl<'d, T: Instance> Driver<'d, T> { | |||
| 366 | let addr = self.alloc_ep_mem(len); | 418 | let addr = self.alloc_ep_mem(len); |
| 367 | 419 | ||
| 368 | // ep_in_len is written when actually TXing packets. | 420 | // ep_in_len is written when actually TXing packets. |
| 369 | btable::write_in::<T>(index, addr); | 421 | btable::write_in_tx::<T>(index, addr); |
| 422 | |||
| 423 | if ep_type == EndpointType::Isochronous { | ||
| 424 | btable::write_in_rx::<T>(index, addr); | ||
| 425 | } | ||
| 370 | 426 | ||
| 371 | EndpointBuffer { | 427 | EndpointBuffer { |
| 372 | addr, | 428 | addr, |
| @@ -656,6 +712,18 @@ impl Dir for Out { | |||
| 656 | } | 712 | } |
| 657 | } | 713 | } |
| 658 | 714 | ||
| 715 | /// Selects the packet buffer. | ||
| 716 | /// | ||
| 717 | /// For double-buffered endpoints, both the `Rx` and `Tx` buffer from a channel are used for the same | ||
| 718 | /// direction of transfer. This is opposed to single-buffered endpoints, where one channel can serve | ||
| 719 | /// two directions at the same time. | ||
| 720 | enum PacketBuffer { | ||
| 721 | /// The RX buffer - must be used for single-buffered OUT endpoints | ||
| 722 | Rx, | ||
| 723 | /// The TX buffer - must be used for single-buffered IN endpoints | ||
| 724 | Tx, | ||
| 725 | } | ||
| 726 | |||
| 659 | /// USB endpoint. | 727 | /// USB endpoint. |
| 660 | pub struct Endpoint<'d, T: Instance, D> { | 728 | pub struct Endpoint<'d, T: Instance, D> { |
| 661 | _phantom: PhantomData<(&'d mut T, D)>, | 729 | _phantom: PhantomData<(&'d mut T, D)>, |
| @@ -664,15 +732,46 @@ pub struct Endpoint<'d, T: Instance, D> { | |||
| 664 | } | 732 | } |
| 665 | 733 | ||
| 666 | impl<'d, T: Instance, D> Endpoint<'d, T, D> { | 734 | impl<'d, T: Instance, D> Endpoint<'d, T, D> { |
| 667 | fn write_data(&mut self, buf: &[u8]) { | 735 | /// Write to a double-buffered endpoint. |
| 736 | /// | ||
| 737 | /// For double-buffered endpoints, the data buffers overlap, but we still need to write to the right counter field. | ||
| 738 | /// The DTOG_TX bit indicates the buffer that is currently in use by the USB peripheral, that is, the buffer in | ||
| 739 | /// which the next transmit packet will be stored, so we need to write the counter of the OTHER buffer, which is | ||
| 740 | /// where the last transmitted packet was stored. | ||
| 741 | fn write_data_double_buffered(&mut self, buf: &[u8], packet_buffer: PacketBuffer) { | ||
| 668 | let index = self.info.addr.index(); | 742 | let index = self.info.addr.index(); |
| 669 | self.buf.write(buf); | 743 | self.buf.write(buf); |
| 670 | btable::write_in_len::<T>(index, self.buf.addr, buf.len() as _); | 744 | |
| 745 | match packet_buffer { | ||
| 746 | PacketBuffer::Rx => btable::write_in_len_rx::<T>(index, self.buf.addr, buf.len() as _), | ||
| 747 | PacketBuffer::Tx => btable::write_in_len_tx::<T>(index, self.buf.addr, buf.len() as _), | ||
| 748 | } | ||
| 671 | } | 749 | } |
| 672 | 750 | ||
| 673 | fn read_data(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> { | 751 | /// Write to a single-buffered endpoint. |
| 752 | fn write_data(&mut self, buf: &[u8]) { | ||
| 753 | self.write_data_double_buffered(buf, PacketBuffer::Tx); | ||
| 754 | } | ||
| 755 | |||
| 756 | /// Read from a double-buffered endpoint. | ||
| 757 | /// | ||
| 758 | /// For double-buffered endpoints, the data buffers overlap, but we still need to read from the right counter field. | ||
| 759 | /// The DTOG_RX bit indicates the buffer that is currently in use by the USB peripheral, that is, the buffer in | ||
| 760 | /// which the next received packet will be stored, so we need to read the counter of the OTHER buffer, which is | ||
| 761 | /// where the last received packet was stored. | ||
| 762 | fn read_data_double_buffered( | ||
| 763 | &mut self, | ||
| 764 | buf: &mut [u8], | ||
| 765 | packet_buffer: PacketBuffer, | ||
| 766 | ) -> Result<usize, EndpointError> { | ||
| 674 | let index = self.info.addr.index(); | 767 | let index = self.info.addr.index(); |
| 675 | let rx_len = btable::read_out_len::<T>(index) as usize & 0x3FF; | 768 | |
| 769 | let rx_len = match packet_buffer { | ||
| 770 | PacketBuffer::Rx => btable::read_out_len_rx::<T>(index), | ||
| 771 | PacketBuffer::Tx => btable::read_out_len_tx::<T>(index), | ||
| 772 | } as usize | ||
| 773 | & 0x3FF; | ||
| 774 | |||
| 676 | trace!("READ DONE, rx_len = {}", rx_len); | 775 | trace!("READ DONE, rx_len = {}", rx_len); |
| 677 | if rx_len > buf.len() { | 776 | if rx_len > buf.len() { |
| 678 | return Err(EndpointError::BufferOverflow); | 777 | return Err(EndpointError::BufferOverflow); |
| @@ -680,6 +779,11 @@ impl<'d, T: Instance, D> Endpoint<'d, T, D> { | |||
| 680 | self.buf.read(&mut buf[..rx_len]); | 779 | self.buf.read(&mut buf[..rx_len]); |
| 681 | Ok(rx_len) | 780 | Ok(rx_len) |
| 682 | } | 781 | } |
| 782 | |||
| 783 | /// Read from a single-buffered endpoint. | ||
| 784 | fn read_data(&mut self, buf: &mut [u8]) -> Result<usize, EndpointError> { | ||
| 785 | self.read_data_double_buffered(buf, PacketBuffer::Rx) | ||
| 786 | } | ||
| 683 | } | 787 | } |
| 684 | 788 | ||
| 685 | impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { | 789 | impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { |
| @@ -734,25 +838,53 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> { | |||
| 734 | EP_OUT_WAKERS[index].register(cx.waker()); | 838 | EP_OUT_WAKERS[index].register(cx.waker()); |
| 735 | let regs = T::regs(); | 839 | let regs = T::regs(); |
| 736 | let stat = regs.epr(index).read().stat_rx(); | 840 | let stat = regs.epr(index).read().stat_rx(); |
| 737 | if matches!(stat, Stat::NAK | Stat::DISABLED) { | 841 | if self.info.ep_type == EndpointType::Isochronous { |
| 738 | Poll::Ready(stat) | 842 | // The isochronous endpoint does not change its `STAT_RX` field to `NAK` when receiving a packet. |
| 843 | // Therefore, this instead waits until the `CTR` interrupt was triggered. | ||
| 844 | if matches!(stat, Stat::DISABLED) || CTR_TRIGGERED[index].load(Ordering::Relaxed) { | ||
| 845 | Poll::Ready(stat) | ||
| 846 | } else { | ||
| 847 | Poll::Pending | ||
| 848 | } | ||
| 739 | } else { | 849 | } else { |
| 740 | Poll::Pending | 850 | if matches!(stat, Stat::NAK | Stat::DISABLED) { |
| 851 | Poll::Ready(stat) | ||
| 852 | } else { | ||
| 853 | Poll::Pending | ||
| 854 | } | ||
| 741 | } | 855 | } |
| 742 | }) | 856 | }) |
| 743 | .await; | 857 | .await; |
| 744 | 858 | ||
| 859 | CTR_TRIGGERED[index].store(false, Ordering::Relaxed); | ||
| 860 | |||
| 745 | if stat == Stat::DISABLED { | 861 | if stat == Stat::DISABLED { |
| 746 | return Err(EndpointError::Disabled); | 862 | return Err(EndpointError::Disabled); |
| 747 | } | 863 | } |
| 748 | 864 | ||
| 749 | let rx_len = self.read_data(buf)?; | ||
| 750 | |||
| 751 | let regs = T::regs(); | 865 | let regs = T::regs(); |
| 866 | |||
| 867 | let packet_buffer = if self.info.ep_type == EndpointType::Isochronous { | ||
| 868 | // Find the buffer, which is currently in use. Read from the OTHER buffer. | ||
| 869 | if regs.epr(index).read().dtog_rx() { | ||
| 870 | PacketBuffer::Rx | ||
| 871 | } else { | ||
| 872 | PacketBuffer::Tx | ||
| 873 | } | ||
| 874 | } else { | ||
| 875 | PacketBuffer::Rx | ||
| 876 | }; | ||
| 877 | |||
| 878 | let rx_len = self.read_data_double_buffered(buf, packet_buffer)?; | ||
| 879 | |||
| 752 | regs.epr(index).write(|w| { | 880 | regs.epr(index).write(|w| { |
| 753 | w.set_ep_type(convert_type(self.info.ep_type)); | 881 | w.set_ep_type(convert_type(self.info.ep_type)); |
| 754 | w.set_ea(self.info.addr.index() as _); | 882 | w.set_ea(self.info.addr.index() as _); |
| 755 | w.set_stat_rx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits())); | 883 | if self.info.ep_type == EndpointType::Isochronous { |
| 884 | w.set_stat_rx(Stat::from_bits(0)); // STAT_RX remains `VALID`. | ||
| 885 | } else { | ||
| 886 | w.set_stat_rx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits())); | ||
| 887 | } | ||
| 756 | w.set_stat_tx(Stat::from_bits(0)); | 888 | w.set_stat_tx(Stat::from_bits(0)); |
| 757 | w.set_ctr_rx(true); // don't clear | 889 | w.set_ctr_rx(true); // don't clear |
| 758 | w.set_ctr_tx(true); // don't clear | 890 | w.set_ctr_tx(true); // don't clear |
| @@ -776,25 +908,54 @@ impl<'d, T: Instance> driver::EndpointIn for Endpoint<'d, T, In> { | |||
| 776 | EP_IN_WAKERS[index].register(cx.waker()); | 908 | EP_IN_WAKERS[index].register(cx.waker()); |
| 777 | let regs = T::regs(); | 909 | let regs = T::regs(); |
| 778 | let stat = regs.epr(index).read().stat_tx(); | 910 | let stat = regs.epr(index).read().stat_tx(); |
| 779 | if matches!(stat, Stat::NAK | Stat::DISABLED) { | 911 | if self.info.ep_type == EndpointType::Isochronous { |
| 780 | Poll::Ready(stat) | 912 | // The isochronous endpoint does not change its `STAT_RX` field to `NAK` when receiving a packet. |
| 913 | // Therefore, this instead waits until the `CTR` interrupt was triggered. | ||
| 914 | if matches!(stat, Stat::DISABLED) || CTR_TRIGGERED[index].load(Ordering::Relaxed) { | ||
| 915 | Poll::Ready(stat) | ||
| 916 | } else { | ||
| 917 | Poll::Pending | ||
| 918 | } | ||
| 781 | } else { | 919 | } else { |
| 782 | Poll::Pending | 920 | if matches!(stat, Stat::NAK | Stat::DISABLED) { |
| 921 | Poll::Ready(stat) | ||
| 922 | } else { | ||
| 923 | Poll::Pending | ||
| 924 | } | ||
| 783 | } | 925 | } |
| 784 | }) | 926 | }) |
| 785 | .await; | 927 | .await; |
| 786 | 928 | ||
| 929 | CTR_TRIGGERED[index].store(false, Ordering::Relaxed); | ||
| 930 | |||
| 787 | if stat == Stat::DISABLED { | 931 | if stat == Stat::DISABLED { |
| 788 | return Err(EndpointError::Disabled); | 932 | return Err(EndpointError::Disabled); |
| 789 | } | 933 | } |
| 790 | 934 | ||
| 791 | self.write_data(buf); | 935 | let regs = T::regs(); |
| 936 | |||
| 937 | let packet_buffer = if self.info.ep_type == EndpointType::Isochronous { | ||
| 938 | // Find the buffer, which is currently in use. Write to the OTHER buffer. | ||
| 939 | if regs.epr(index).read().dtog_tx() { | ||
| 940 | PacketBuffer::Tx | ||
| 941 | } else { | ||
| 942 | PacketBuffer::Rx | ||
| 943 | } | ||
| 944 | } else { | ||
| 945 | PacketBuffer::Tx | ||
| 946 | }; | ||
| 947 | |||
| 948 | self.write_data_double_buffered(buf, packet_buffer); | ||
| 792 | 949 | ||
| 793 | let regs = T::regs(); | 950 | let regs = T::regs(); |
| 794 | regs.epr(index).write(|w| { | 951 | regs.epr(index).write(|w| { |
| 795 | w.set_ep_type(convert_type(self.info.ep_type)); | 952 | w.set_ep_type(convert_type(self.info.ep_type)); |
| 796 | w.set_ea(self.info.addr.index() as _); | 953 | w.set_ea(self.info.addr.index() as _); |
| 797 | w.set_stat_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits())); | 954 | if self.info.ep_type == EndpointType::Isochronous { |
| 955 | w.set_stat_tx(Stat::from_bits(0)); // STAT_TX remains `VALID`. | ||
| 956 | } else { | ||
| 957 | w.set_stat_tx(Stat::from_bits(Stat::NAK.to_bits() ^ Stat::VALID.to_bits())); | ||
| 958 | } | ||
| 798 | w.set_stat_rx(Stat::from_bits(0)); | 959 | w.set_stat_rx(Stat::from_bits(0)); |
| 799 | w.set_ctr_rx(true); // don't clear | 960 | w.set_ctr_rx(true); // don't clear |
| 800 | w.set_ctr_tx(true); // don't clear | 961 | w.set_ctr_tx(true); // don't clear |
