diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-09-16 19:51:52 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-09-16 19:51:52 +0000 |
| commit | ae8caf3f55d91579234f199458c369536fd39bb1 (patch) | |
| tree | 43e2180b0292cece5d4f590f06f4717cc97c9b09 | |
| parent | e90b3bc4494682ae23d5839328ab950e34ca7cfe (diff) | |
| parent | a8ca6713e6e9b7ad5dd53f9b46bcf5e893adda1e (diff) | |
Merge pull request #3314 from elagil/add_iso_endpoint_support
Add ISO endpoint support
| -rw-r--r-- | embassy-stm32/src/usb/usb.rs | 211 | ||||
| -rw-r--r-- | embassy-usb-synopsys-otg/src/lib.rs | 30 | ||||
| -rw-r--r-- | embassy-usb-synopsys-otg/src/otg_v1.rs | 16 | ||||
| -rw-r--r-- | embassy-usb/src/builder.rs | 140 | ||||
| -rw-r--r-- | embassy-usb/src/descriptor.rs | 91 |
5 files changed, 430 insertions, 58 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 |
diff --git a/embassy-usb-synopsys-otg/src/lib.rs b/embassy-usb-synopsys-otg/src/lib.rs index 5fb82db42..b145f4aa8 100644 --- a/embassy-usb-synopsys-otg/src/lib.rs +++ b/embassy-usb-synopsys-otg/src/lib.rs | |||
| @@ -1071,6 +1071,21 @@ impl<'d> embassy_usb_driver::EndpointOut for Endpoint<'d, Out> { | |||
| 1071 | w.set_pktcnt(1); | 1071 | w.set_pktcnt(1); |
| 1072 | }); | 1072 | }); |
| 1073 | 1073 | ||
| 1074 | if self.info.ep_type == EndpointType::Isochronous { | ||
| 1075 | // Isochronous endpoints must set the correct even/odd frame bit to | ||
| 1076 | // correspond with the next frame's number. | ||
| 1077 | let frame_number = self.regs.dsts().read().fnsof(); | ||
| 1078 | let frame_is_odd = frame_number & 0x01 == 1; | ||
| 1079 | |||
| 1080 | self.regs.doepctl(index).modify(|r| { | ||
| 1081 | if frame_is_odd { | ||
| 1082 | r.set_sd0pid_sevnfrm(true); | ||
| 1083 | } else { | ||
| 1084 | r.set_sd1pid_soddfrm(true); | ||
| 1085 | } | ||
| 1086 | }); | ||
| 1087 | } | ||
| 1088 | |||
| 1074 | // Clear NAK to indicate we are ready to receive more data | 1089 | // Clear NAK to indicate we are ready to receive more data |
| 1075 | self.regs.doepctl(index).modify(|w| { | 1090 | self.regs.doepctl(index).modify(|w| { |
| 1076 | w.set_cnak(true); | 1091 | w.set_cnak(true); |
| @@ -1158,6 +1173,21 @@ impl<'d> embassy_usb_driver::EndpointIn for Endpoint<'d, In> { | |||
| 1158 | w.set_xfrsiz(buf.len() as _); | 1173 | w.set_xfrsiz(buf.len() as _); |
| 1159 | }); | 1174 | }); |
| 1160 | 1175 | ||
| 1176 | if self.info.ep_type == EndpointType::Isochronous { | ||
| 1177 | // Isochronous endpoints must set the correct even/odd frame bit to | ||
| 1178 | // correspond with the next frame's number. | ||
| 1179 | let frame_number = self.regs.dsts().read().fnsof(); | ||
| 1180 | let frame_is_odd = frame_number & 0x01 == 1; | ||
| 1181 | |||
| 1182 | self.regs.diepctl(index).modify(|r| { | ||
| 1183 | if frame_is_odd { | ||
| 1184 | r.set_sd0pid_sevnfrm(true); | ||
| 1185 | } else { | ||
| 1186 | r.set_sd1pid_soddfrm(true); | ||
| 1187 | } | ||
| 1188 | }); | ||
| 1189 | } | ||
| 1190 | |||
| 1161 | // Enable endpoint | 1191 | // Enable endpoint |
| 1162 | self.regs.diepctl(index).modify(|w| { | 1192 | self.regs.diepctl(index).modify(|w| { |
| 1163 | w.set_cnak(true); | 1193 | w.set_cnak(true); |
diff --git a/embassy-usb-synopsys-otg/src/otg_v1.rs b/embassy-usb-synopsys-otg/src/otg_v1.rs index a8530980c..d3abc328d 100644 --- a/embassy-usb-synopsys-otg/src/otg_v1.rs +++ b/embassy-usb-synopsys-otg/src/otg_v1.rs | |||
| @@ -795,15 +795,15 @@ pub mod regs { | |||
| 795 | pub fn set_sd0pid_sevnfrm(&mut self, val: bool) { | 795 | pub fn set_sd0pid_sevnfrm(&mut self, val: bool) { |
| 796 | self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize); | 796 | self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize); |
| 797 | } | 797 | } |
| 798 | #[doc = "SODDFRM/SD1PID"] | 798 | #[doc = "SD1PID/SODDFRM"] |
| 799 | #[inline(always)] | 799 | #[inline(always)] |
| 800 | pub const fn soddfrm_sd1pid(&self) -> bool { | 800 | pub const fn sd1pid_soddfrm(&self) -> bool { |
| 801 | let val = (self.0 >> 29usize) & 0x01; | 801 | let val = (self.0 >> 29usize) & 0x01; |
| 802 | val != 0 | 802 | val != 0 |
| 803 | } | 803 | } |
| 804 | #[doc = "SODDFRM/SD1PID"] | 804 | #[doc = "SD1PID/SODDFRM"] |
| 805 | #[inline(always)] | 805 | #[inline(always)] |
| 806 | pub fn set_soddfrm_sd1pid(&mut self, val: bool) { | 806 | pub fn set_sd1pid_soddfrm(&mut self, val: bool) { |
| 807 | self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize); | 807 | self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize); |
| 808 | } | 808 | } |
| 809 | #[doc = "EPDIS"] | 809 | #[doc = "EPDIS"] |
| @@ -1174,15 +1174,15 @@ pub mod regs { | |||
| 1174 | pub fn set_sd0pid_sevnfrm(&mut self, val: bool) { | 1174 | pub fn set_sd0pid_sevnfrm(&mut self, val: bool) { |
| 1175 | self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize); | 1175 | self.0 = (self.0 & !(0x01 << 28usize)) | (((val as u32) & 0x01) << 28usize); |
| 1176 | } | 1176 | } |
| 1177 | #[doc = "SODDFRM"] | 1177 | #[doc = "SD1PID/SODDFRM"] |
| 1178 | #[inline(always)] | 1178 | #[inline(always)] |
| 1179 | pub const fn soddfrm(&self) -> bool { | 1179 | pub const fn sd1pid_soddfrm(&self) -> bool { |
| 1180 | let val = (self.0 >> 29usize) & 0x01; | 1180 | let val = (self.0 >> 29usize) & 0x01; |
| 1181 | val != 0 | 1181 | val != 0 |
| 1182 | } | 1182 | } |
| 1183 | #[doc = "SODDFRM"] | 1183 | #[doc = "SD1PID/SODDFRM"] |
| 1184 | #[inline(always)] | 1184 | #[inline(always)] |
| 1185 | pub fn set_soddfrm(&mut self, val: bool) { | 1185 | pub fn set_sd1pid_soddfrm(&mut self, val: bool) { |
| 1186 | self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize); | 1186 | self.0 = (self.0 & !(0x01 << 29usize)) | (((val as u32) & 0x01) << 29usize); |
| 1187 | } | 1187 | } |
| 1188 | #[doc = "EPDIS"] | 1188 | #[doc = "EPDIS"] |
diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 7168e077c..e1bf8041f 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | use heapless::Vec; | 1 | use heapless::Vec; |
| 2 | 2 | ||
| 3 | use crate::config::MAX_HANDLER_COUNT; | 3 | use crate::config::MAX_HANDLER_COUNT; |
| 4 | use crate::descriptor::{BosWriter, DescriptorWriter}; | 4 | use crate::descriptor::{BosWriter, DescriptorWriter, SynchronizationType, UsageType}; |
| 5 | use crate::driver::{Driver, Endpoint, EndpointType}; | 5 | use crate::driver::{Driver, Endpoint, EndpointInfo, EndpointType}; |
| 6 | use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; | 6 | use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; |
| 7 | use crate::types::{InterfaceNumber, StringIndex}; | 7 | use crate::types::{InterfaceNumber, StringIndex}; |
| 8 | use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; | 8 | use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; |
| @@ -414,7 +414,7 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | |||
| 414 | /// Descriptors are written in the order builder functions are called. Note that some | 414 | /// Descriptors are written in the order builder functions are called. Note that some |
| 415 | /// classes care about the order. | 415 | /// classes care about the order. |
| 416 | pub fn descriptor(&mut self, descriptor_type: u8, descriptor: &[u8]) { | 416 | pub fn descriptor(&mut self, descriptor_type: u8, descriptor: &[u8]) { |
| 417 | self.builder.config_descriptor.write(descriptor_type, descriptor); | 417 | self.builder.config_descriptor.write(descriptor_type, descriptor, &[]); |
| 418 | } | 418 | } |
| 419 | 419 | ||
| 420 | /// Add a custom Binary Object Store (BOS) descriptor to this alternate setting. | 420 | /// Add a custom Binary Object Store (BOS) descriptor to this alternate setting. |
| @@ -422,26 +422,80 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | |||
| 422 | self.builder.bos_descriptor.capability(capability_type, capability); | 422 | self.builder.bos_descriptor.capability(capability_type, capability); |
| 423 | } | 423 | } |
| 424 | 424 | ||
| 425 | fn endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { | 425 | /// Write a custom endpoint descriptor for a certain endpoint. |
| 426 | /// | ||
| 427 | /// This can be necessary, if the endpoint descriptors can only be written | ||
| 428 | /// after the endpoint was created. As an example, an endpoint descriptor | ||
| 429 | /// may contain the address of an endpoint that was allocated earlier. | ||
| 430 | pub fn endpoint_descriptor( | ||
| 431 | &mut self, | ||
| 432 | endpoint: &EndpointInfo, | ||
| 433 | synchronization_type: SynchronizationType, | ||
| 434 | usage_type: UsageType, | ||
| 435 | extra_fields: &[u8], | ||
| 436 | ) { | ||
| 437 | self.builder | ||
| 438 | .config_descriptor | ||
| 439 | .endpoint(endpoint, synchronization_type, usage_type, extra_fields); | ||
| 440 | } | ||
| 441 | |||
| 442 | /// Allocate an IN endpoint, without writing its descriptor. | ||
| 443 | /// | ||
| 444 | /// Used for granular control over the order of endpoint and descriptor creation. | ||
| 445 | pub fn alloc_endpoint_in(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { | ||
| 426 | let ep = self | 446 | let ep = self |
| 427 | .builder | 447 | .builder |
| 428 | .driver | 448 | .driver |
| 429 | .alloc_endpoint_in(ep_type, max_packet_size, interval_ms) | 449 | .alloc_endpoint_in(ep_type, max_packet_size, interval_ms) |
| 430 | .expect("alloc_endpoint_in failed"); | 450 | .expect("alloc_endpoint_in failed"); |
| 431 | 451 | ||
| 432 | self.builder.config_descriptor.endpoint(ep.info()); | 452 | ep |
| 453 | } | ||
| 454 | |||
| 455 | fn endpoint_in( | ||
| 456 | &mut self, | ||
| 457 | ep_type: EndpointType, | ||
| 458 | max_packet_size: u16, | ||
| 459 | interval_ms: u8, | ||
| 460 | synchronization_type: SynchronizationType, | ||
| 461 | usage_type: UsageType, | ||
| 462 | extra_fields: &[u8], | ||
| 463 | ) -> D::EndpointIn { | ||
| 464 | let ep = self.alloc_endpoint_in(ep_type, max_packet_size, interval_ms); | ||
| 465 | self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields); | ||
| 433 | 466 | ||
| 434 | ep | 467 | ep |
| 435 | } | 468 | } |
| 436 | 469 | ||
| 437 | fn endpoint_out(&mut self, ep_type: EndpointType, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { | 470 | /// Allocate an OUT endpoint, without writing its descriptor. |
| 471 | /// | ||
| 472 | /// Use for granular control over the order of endpoint and descriptor creation. | ||
| 473 | pub fn alloc_endpoint_out( | ||
| 474 | &mut self, | ||
| 475 | ep_type: EndpointType, | ||
| 476 | max_packet_size: u16, | ||
| 477 | interval_ms: u8, | ||
| 478 | ) -> D::EndpointOut { | ||
| 438 | let ep = self | 479 | let ep = self |
| 439 | .builder | 480 | .builder |
| 440 | .driver | 481 | .driver |
| 441 | .alloc_endpoint_out(ep_type, max_packet_size, interval_ms) | 482 | .alloc_endpoint_out(ep_type, max_packet_size, interval_ms) |
| 442 | .expect("alloc_endpoint_out failed"); | 483 | .expect("alloc_endpoint_out failed"); |
| 443 | 484 | ||
| 444 | self.builder.config_descriptor.endpoint(ep.info()); | 485 | ep |
| 486 | } | ||
| 487 | |||
| 488 | fn endpoint_out( | ||
| 489 | &mut self, | ||
| 490 | ep_type: EndpointType, | ||
| 491 | max_packet_size: u16, | ||
| 492 | interval_ms: u8, | ||
| 493 | synchronization_type: SynchronizationType, | ||
| 494 | usage_type: UsageType, | ||
| 495 | extra_fields: &[u8], | ||
| 496 | ) -> D::EndpointOut { | ||
| 497 | let ep = self.alloc_endpoint_out(ep_type, max_packet_size, interval_ms); | ||
| 498 | self.endpoint_descriptor(ep.info(), synchronization_type, usage_type, extra_fields); | ||
| 445 | 499 | ||
| 446 | ep | 500 | ep |
| 447 | } | 501 | } |
| @@ -451,7 +505,14 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | |||
| 451 | /// Descriptors are written in the order builder functions are called. Note that some | 505 | /// Descriptors are written in the order builder functions are called. Note that some |
| 452 | /// classes care about the order. | 506 | /// classes care about the order. |
| 453 | pub fn endpoint_bulk_in(&mut self, max_packet_size: u16) -> D::EndpointIn { | 507 | pub fn endpoint_bulk_in(&mut self, max_packet_size: u16) -> D::EndpointIn { |
| 454 | self.endpoint_in(EndpointType::Bulk, max_packet_size, 0) | 508 | self.endpoint_in( |
| 509 | EndpointType::Bulk, | ||
| 510 | max_packet_size, | ||
| 511 | 0, | ||
| 512 | SynchronizationType::NoSynchronization, | ||
| 513 | UsageType::DataEndpoint, | ||
| 514 | &[], | ||
| 515 | ) | ||
| 455 | } | 516 | } |
| 456 | 517 | ||
| 457 | /// Allocate a BULK OUT endpoint and write its descriptor. | 518 | /// Allocate a BULK OUT endpoint and write its descriptor. |
| @@ -459,7 +520,14 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | |||
| 459 | /// Descriptors are written in the order builder functions are called. Note that some | 520 | /// Descriptors are written in the order builder functions are called. Note that some |
| 460 | /// classes care about the order. | 521 | /// classes care about the order. |
| 461 | pub fn endpoint_bulk_out(&mut self, max_packet_size: u16) -> D::EndpointOut { | 522 | pub fn endpoint_bulk_out(&mut self, max_packet_size: u16) -> D::EndpointOut { |
| 462 | self.endpoint_out(EndpointType::Bulk, max_packet_size, 0) | 523 | self.endpoint_out( |
| 524 | EndpointType::Bulk, | ||
| 525 | max_packet_size, | ||
| 526 | 0, | ||
| 527 | SynchronizationType::NoSynchronization, | ||
| 528 | UsageType::DataEndpoint, | ||
| 529 | &[], | ||
| 530 | ) | ||
| 463 | } | 531 | } |
| 464 | 532 | ||
| 465 | /// Allocate a INTERRUPT IN endpoint and write its descriptor. | 533 | /// Allocate a INTERRUPT IN endpoint and write its descriptor. |
| @@ -467,24 +535,66 @@ impl<'a, 'd, D: Driver<'d>> InterfaceAltBuilder<'a, 'd, D> { | |||
| 467 | /// Descriptors are written in the order builder functions are called. Note that some | 535 | /// Descriptors are written in the order builder functions are called. Note that some |
| 468 | /// classes care about the order. | 536 | /// classes care about the order. |
| 469 | pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { | 537 | pub fn endpoint_interrupt_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { |
| 470 | self.endpoint_in(EndpointType::Interrupt, max_packet_size, interval_ms) | 538 | self.endpoint_in( |
| 539 | EndpointType::Interrupt, | ||
| 540 | max_packet_size, | ||
| 541 | interval_ms, | ||
| 542 | SynchronizationType::NoSynchronization, | ||
| 543 | UsageType::DataEndpoint, | ||
| 544 | &[], | ||
| 545 | ) | ||
| 471 | } | 546 | } |
| 472 | 547 | ||
| 473 | /// Allocate a INTERRUPT OUT endpoint and write its descriptor. | 548 | /// Allocate a INTERRUPT OUT endpoint and write its descriptor. |
| 474 | pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { | 549 | pub fn endpoint_interrupt_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { |
| 475 | self.endpoint_out(EndpointType::Interrupt, max_packet_size, interval_ms) | 550 | self.endpoint_out( |
| 551 | EndpointType::Interrupt, | ||
| 552 | max_packet_size, | ||
| 553 | interval_ms, | ||
| 554 | SynchronizationType::NoSynchronization, | ||
| 555 | UsageType::DataEndpoint, | ||
| 556 | &[], | ||
| 557 | ) | ||
| 476 | } | 558 | } |
| 477 | 559 | ||
| 478 | /// Allocate a ISOCHRONOUS IN endpoint and write its descriptor. | 560 | /// Allocate a ISOCHRONOUS IN endpoint and write its descriptor. |
| 479 | /// | 561 | /// |
| 480 | /// Descriptors are written in the order builder functions are called. Note that some | 562 | /// Descriptors are written in the order builder functions are called. Note that some |
| 481 | /// classes care about the order. | 563 | /// classes care about the order. |
| 482 | pub fn endpoint_isochronous_in(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointIn { | 564 | pub fn endpoint_isochronous_in( |
| 483 | self.endpoint_in(EndpointType::Isochronous, max_packet_size, interval_ms) | 565 | &mut self, |
| 566 | max_packet_size: u16, | ||
| 567 | interval_ms: u8, | ||
| 568 | synchronization_type: SynchronizationType, | ||
| 569 | usage_type: UsageType, | ||
| 570 | extra_fields: &[u8], | ||
| 571 | ) -> D::EndpointIn { | ||
| 572 | self.endpoint_in( | ||
| 573 | EndpointType::Isochronous, | ||
| 574 | max_packet_size, | ||
| 575 | interval_ms, | ||
| 576 | synchronization_type, | ||
| 577 | usage_type, | ||
| 578 | extra_fields, | ||
| 579 | ) | ||
| 484 | } | 580 | } |
| 485 | 581 | ||
| 486 | /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor. | 582 | /// Allocate a ISOCHRONOUS OUT endpoint and write its descriptor. |
| 487 | pub fn endpoint_isochronous_out(&mut self, max_packet_size: u16, interval_ms: u8) -> D::EndpointOut { | 583 | pub fn endpoint_isochronous_out( |
| 488 | self.endpoint_out(EndpointType::Isochronous, max_packet_size, interval_ms) | 584 | &mut self, |
| 585 | max_packet_size: u16, | ||
| 586 | interval_ms: u8, | ||
| 587 | synchronization_type: SynchronizationType, | ||
| 588 | usage_type: UsageType, | ||
| 589 | extra_fields: &[u8], | ||
| 590 | ) -> D::EndpointOut { | ||
| 591 | self.endpoint_out( | ||
| 592 | EndpointType::Isochronous, | ||
| 593 | max_packet_size, | ||
| 594 | interval_ms, | ||
| 595 | synchronization_type, | ||
| 596 | usage_type, | ||
| 597 | extra_fields, | ||
| 598 | ) | ||
| 489 | } | 599 | } |
| 490 | } | 600 | } |
diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs index c4d79e39f..06ebe0481 100644 --- a/embassy-usb/src/descriptor.rs +++ b/embassy-usb/src/descriptor.rs | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | //! Utilities for writing USB descriptors. | 1 | //! Utilities for writing USB descriptors. |
| 2 | use embassy_usb_driver::EndpointType; | ||
| 2 | 3 | ||
| 3 | use crate::builder::Config; | 4 | use crate::builder::Config; |
| 4 | use crate::driver::EndpointInfo; | 5 | use crate::driver::EndpointInfo; |
| @@ -38,6 +39,40 @@ pub mod capability_type { | |||
| 38 | pub const PLATFORM: u8 = 5; | 39 | pub const PLATFORM: u8 = 5; |
| 39 | } | 40 | } |
| 40 | 41 | ||
| 42 | /// USB endpoint synchronization type. The values of this enum can be directly | ||
| 43 | /// cast into `u8` to get the bmAttributes synchronization type bits. | ||
| 44 | /// Values other than `NoSynchronization` are only allowed on isochronous endpoints. | ||
| 45 | #[repr(u8)] | ||
| 46 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 47 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 48 | pub enum SynchronizationType { | ||
| 49 | /// No synchronization is used. | ||
| 50 | NoSynchronization = 0b00, | ||
| 51 | /// Unsynchronized, although sinks provide data rate feedback. | ||
| 52 | Asynchronous = 0b01, | ||
| 53 | /// Synchronized using feedback or feedforward data rate information. | ||
| 54 | Adaptive = 0b10, | ||
| 55 | /// Synchronized to the USB’s SOF. | ||
| 56 | Synchronous = 0b11, | ||
| 57 | } | ||
| 58 | |||
| 59 | /// USB endpoint usage type. The values of this enum can be directly cast into | ||
| 60 | /// `u8` to get the bmAttributes usage type bits. | ||
| 61 | /// Values other than `DataEndpoint` are only allowed on isochronous endpoints. | ||
| 62 | #[repr(u8)] | ||
| 63 | #[derive(Copy, Clone, Eq, PartialEq, Debug)] | ||
| 64 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 65 | pub enum UsageType { | ||
| 66 | /// Use the endpoint for regular data transfer. | ||
| 67 | DataEndpoint = 0b00, | ||
| 68 | /// Endpoint conveys explicit feedback information for one or more data endpoints. | ||
| 69 | FeedbackEndpoint = 0b01, | ||
| 70 | /// A data endpoint that also serves as an implicit feedback endpoint for one or more data endpoints. | ||
| 71 | ImplicitFeedbackDataEndpoint = 0b10, | ||
| 72 | /// Reserved usage type. | ||
| 73 | Reserved = 0b11, | ||
| 74 | } | ||
| 75 | |||
| 41 | /// A writer for USB descriptors. | 76 | /// A writer for USB descriptors. |
| 42 | pub(crate) struct DescriptorWriter<'a> { | 77 | pub(crate) struct DescriptorWriter<'a> { |
| 43 | pub buf: &'a mut [u8], | 78 | pub buf: &'a mut [u8], |
| @@ -65,23 +100,26 @@ impl<'a> DescriptorWriter<'a> { | |||
| 65 | self.position | 100 | self.position |
| 66 | } | 101 | } |
| 67 | 102 | ||
| 68 | /// Writes an arbitrary (usually class-specific) descriptor. | 103 | /// Writes an arbitrary (usually class-specific) descriptor with optional extra fields. |
| 69 | pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8]) { | 104 | pub fn write(&mut self, descriptor_type: u8, descriptor: &[u8], extra_fields: &[u8]) { |
| 70 | let length = descriptor.len(); | 105 | let descriptor_length = descriptor.len(); |
| 106 | let extra_fields_length = extra_fields.len(); | ||
| 107 | let total_length = descriptor_length + extra_fields_length; | ||
| 71 | 108 | ||
| 72 | assert!( | 109 | assert!( |
| 73 | (self.position + 2 + length) <= self.buf.len() && (length + 2) <= 255, | 110 | (self.position + 2 + total_length) <= self.buf.len() && (total_length + 2) <= 255, |
| 74 | "Descriptor buffer full" | 111 | "Descriptor buffer full" |
| 75 | ); | 112 | ); |
| 76 | 113 | ||
| 77 | self.buf[self.position] = (length + 2) as u8; | 114 | self.buf[self.position] = (total_length + 2) as u8; |
| 78 | self.buf[self.position + 1] = descriptor_type; | 115 | self.buf[self.position + 1] = descriptor_type; |
| 79 | 116 | ||
| 80 | let start = self.position + 2; | 117 | let start = self.position + 2; |
| 81 | 118 | ||
| 82 | self.buf[start..start + length].copy_from_slice(descriptor); | 119 | self.buf[start..start + descriptor_length].copy_from_slice(descriptor); |
| 120 | self.buf[start + descriptor_length..start + total_length].copy_from_slice(extra_fields); | ||
| 83 | 121 | ||
| 84 | self.position = start + length; | 122 | self.position = start + total_length; |
| 85 | } | 123 | } |
| 86 | 124 | ||
| 87 | pub(crate) fn configuration(&mut self, config: &Config) { | 125 | pub(crate) fn configuration(&mut self, config: &Config) { |
| @@ -99,6 +137,7 @@ impl<'a> DescriptorWriter<'a> { | |||
| 99 | | if config.supports_remote_wakeup { 0x20 } else { 0x00 }, // bmAttributes | 137 | | if config.supports_remote_wakeup { 0x20 } else { 0x00 }, // bmAttributes |
| 100 | (config.max_power / 2) as u8, // bMaxPower | 138 | (config.max_power / 2) as u8, // bMaxPower |
| 101 | ], | 139 | ], |
| 140 | &[], | ||
| 102 | ); | 141 | ); |
| 103 | } | 142 | } |
| 104 | 143 | ||
| @@ -145,6 +184,7 @@ impl<'a> DescriptorWriter<'a> { | |||
| 145 | function_protocol, | 184 | function_protocol, |
| 146 | 0, | 185 | 0, |
| 147 | ], | 186 | ], |
| 187 | &[], | ||
| 148 | ); | 188 | ); |
| 149 | } | 189 | } |
| 150 | 190 | ||
| @@ -195,6 +235,7 @@ impl<'a> DescriptorWriter<'a> { | |||
| 195 | interface_protocol, // bInterfaceProtocol | 235 | interface_protocol, // bInterfaceProtocol |
| 196 | str_index, // iInterface | 236 | str_index, // iInterface |
| 197 | ], | 237 | ], |
| 238 | &[], | ||
| 198 | ); | 239 | ); |
| 199 | } | 240 | } |
| 200 | 241 | ||
| @@ -204,21 +245,50 @@ impl<'a> DescriptorWriter<'a> { | |||
| 204 | /// | 245 | /// |
| 205 | /// * `endpoint` - Endpoint previously allocated with | 246 | /// * `endpoint` - Endpoint previously allocated with |
| 206 | /// [`UsbDeviceBuilder`](crate::bus::UsbDeviceBuilder). | 247 | /// [`UsbDeviceBuilder`](crate::bus::UsbDeviceBuilder). |
| 207 | pub fn endpoint(&mut self, endpoint: &EndpointInfo) { | 248 | /// * `synchronization_type` - The synchronization type of the endpoint. |
| 249 | /// * `usage_type` - The usage type of the endpoint. | ||
| 250 | /// * `extra_fields` - Additional, class-specific entries at the end of the endpoint descriptor. | ||
| 251 | pub fn endpoint( | ||
| 252 | &mut self, | ||
| 253 | endpoint: &EndpointInfo, | ||
| 254 | synchronization_type: SynchronizationType, | ||
| 255 | usage_type: UsageType, | ||
| 256 | extra_fields: &[u8], | ||
| 257 | ) { | ||
| 208 | match self.num_endpoints_mark { | 258 | match self.num_endpoints_mark { |
| 209 | Some(mark) => self.buf[mark] += 1, | 259 | Some(mark) => self.buf[mark] += 1, |
| 210 | None => panic!("you can only call `endpoint` after `interface/interface_alt`."), | 260 | None => panic!("you can only call `endpoint` after `interface/interface_alt`."), |
| 211 | }; | 261 | }; |
| 212 | 262 | ||
| 263 | let mut bm_attributes = endpoint.ep_type as u8; | ||
| 264 | |||
| 265 | // Synchronization types other than `NoSynchronization`, | ||
| 266 | // and usage types other than `DataEndpoint` | ||
| 267 | // are only allowed for isochronous endpoints. | ||
| 268 | if endpoint.ep_type != EndpointType::Isochronous { | ||
| 269 | assert_eq!(synchronization_type, SynchronizationType::NoSynchronization); | ||
| 270 | assert_eq!(usage_type, UsageType::DataEndpoint); | ||
| 271 | } else { | ||
| 272 | if usage_type == UsageType::FeedbackEndpoint { | ||
| 273 | assert_eq!(synchronization_type, SynchronizationType::NoSynchronization) | ||
| 274 | } | ||
| 275 | |||
| 276 | let synchronization_bm_attibutes: u8 = (synchronization_type as u8) << 2; | ||
| 277 | let usage_bm_attibutes: u8 = (usage_type as u8) << 4; | ||
| 278 | |||
| 279 | bm_attributes |= usage_bm_attibutes | synchronization_bm_attibutes; | ||
| 280 | } | ||
| 281 | |||
| 213 | self.write( | 282 | self.write( |
| 214 | descriptor_type::ENDPOINT, | 283 | descriptor_type::ENDPOINT, |
| 215 | &[ | 284 | &[ |
| 216 | endpoint.addr.into(), // bEndpointAddress | 285 | endpoint.addr.into(), // bEndpointAddress |
| 217 | endpoint.ep_type as u8, // bmAttributes | 286 | bm_attributes, // bmAttributes |
| 218 | endpoint.max_packet_size as u8, | 287 | endpoint.max_packet_size as u8, |
| 219 | (endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize | 288 | (endpoint.max_packet_size >> 8) as u8, // wMaxPacketSize |
| 220 | endpoint.interval_ms, // bInterval | 289 | endpoint.interval_ms, // bInterval |
| 221 | ], | 290 | ], |
| 291 | extra_fields, | ||
| 222 | ); | 292 | ); |
| 223 | } | 293 | } |
| 224 | 294 | ||
| @@ -318,6 +388,7 @@ impl<'a> BosWriter<'a> { | |||
| 318 | 0x00, 0x00, // wTotalLength | 388 | 0x00, 0x00, // wTotalLength |
| 319 | 0x00, // bNumDeviceCaps | 389 | 0x00, // bNumDeviceCaps |
| 320 | ], | 390 | ], |
| 391 | &[], | ||
| 321 | ); | 392 | ); |
| 322 | 393 | ||
| 323 | self.capability(capability_type::USB_2_0_EXTENSION, &[0; 4]); | 394 | self.capability(capability_type::USB_2_0_EXTENSION, &[0; 4]); |
