esp_hal/
otg_fs.rs

1//! # USB On-The-Go (USB OTG)
2//!
3//! ## Overview
4//! The USB OTG Full-speed (FS) peripheral allows communication
5//! with USB devices using either blocking (usb-device) or asynchronous
6//! (embassy-usb) APIs.
7//!
8//! It can operate as either a USB Host or Device, and supports full-speed (FS)
9//! and low-speed (LS) data rates of the USB 2.0 specification.
10//!
11//! The blocking driver uses the `esp_synopsys_usb_otg` crate, which provides
12//! the `USB bus` implementation and `USB peripheral traits`.
13//!
14//! The asynchronous driver uses the `embassy_usb_synopsys_otg` crate, which
15//! provides the `USB bus` and `USB device` implementations.
16//!
17//! The module also relies on other peripheral modules, such as `GPIO`,
18//! `system`, and `clock control`, to configure and enable the `USB` peripheral.
19//!
20//! ## Configuration
21//! To use the USB OTG Full-speed peripheral driver, you need to initialize the
22//! peripheral and configure its settings. The [`Usb`] struct represents the USB
23//! peripheral and requires the GPIO pins that implement [`UsbDp`], and
24//! [`UsbDm`], which define the specific types used for USB pin selection.
25//!
26//! The returned `Usb` instance can be used with the `usb-device` crate, or it
27//! can be further configured with [`asynch::Driver`] to be used with the
28//! `embassy-usb` crate.
29//!
30//! ## Examples
31//! Visit the [USB Serial] example for an example of using the USB OTG
32//! peripheral.
33//!
34//! [USB Serial]: https://github.com/esp-rs/esp-hal/blob/main/examples/src/bin/usb_serial.rs
35//!
36//! ## Implementation State
37//! - Low-speed (LS) is not supported.
38
39pub use esp_synopsys_usb_otg::UsbBus;
40use esp_synopsys_usb_otg::UsbPeripheral;
41
42use crate::{
43    gpio::InputSignal,
44    peripherals,
45    system::{GenericPeripheralGuard, Peripheral as PeripheralEnable},
46};
47
48#[doc(hidden)]
49/// Trait representing the USB D+ (data plus) pin.
50pub trait UsbDp: crate::private::Sealed {}
51
52#[doc(hidden)]
53/// Trait representing the USB D- (data minus) pin.
54pub trait UsbDm: crate::private::Sealed {}
55
56for_each_analog_function! {
57    (USB_DM, $gpio:ident) => {
58        impl UsbDm for crate::peripherals::$gpio<'_> {}
59    };
60    (USB_DP, $gpio:ident) => {
61        impl UsbDp for crate::peripherals::$gpio<'_> {}
62    };
63}
64
65/// USB peripheral.
66pub struct Usb<'d> {
67    _usb0: peripherals::USB0<'d>,
68    _guard: GenericPeripheralGuard<{ PeripheralEnable::Usb as u8 }>,
69}
70
71impl<'d> Usb<'d> {
72    /// Creates a new `Usb` instance.
73    pub fn new(
74        usb0: peripherals::USB0<'d>,
75        _usb_dp: impl UsbDp + 'd,
76        _usb_dm: impl UsbDm + 'd,
77    ) -> Self {
78        let guard = GenericPeripheralGuard::new();
79
80        Self {
81            _usb0: usb0,
82            _guard: guard,
83        }
84    }
85
86    fn _enable() {
87        peripherals::USB_WRAP::regs().otg_conf().modify(|_, w| {
88            w.usb_pad_enable().set_bit();
89            w.phy_sel().clear_bit();
90            w.clk_en().set_bit();
91            w.ahb_clk_force_on().set_bit();
92            w.phy_clk_force_on().set_bit()
93        });
94
95        #[cfg(esp32s3)]
96        peripherals::LPWR::regs().usb_conf().modify(|_, w| {
97            w.sw_hw_usb_phy_sel().set_bit();
98            w.sw_usb_phy_sel().set_bit()
99        });
100
101        use crate::gpio::Level;
102
103        InputSignal::USB_OTG_IDDIG.connect_to(&Level::High); // connected connector is mini-B side
104        InputSignal::USB_SRP_BVALID.connect_to(&Level::High); // HIGH to force USB device mode
105        InputSignal::USB_OTG_VBUSVALID.connect_to(&Level::High); // receiving a valid Vbus from device
106        InputSignal::USB_OTG_AVALID.connect_to(&Level::Low);
107    }
108
109    fn _disable() {
110        // TODO
111    }
112}
113
114unsafe impl Sync for Usb<'_> {}
115
116unsafe impl UsbPeripheral for Usb<'_> {
117    const REGISTERS: *const () = peripherals::USB0::PTR.cast();
118
119    const HIGH_SPEED: bool = false;
120    const FIFO_DEPTH_WORDS: usize = 256;
121    const ENDPOINT_COUNT: usize = 5;
122
123    fn enable() {
124        Self::_enable();
125    }
126
127    fn ahb_frequency_hz(&self) -> u32 {
128        // unused
129        80_000_000
130    }
131}
132/// Async functionality
133pub mod asynch {
134    use embassy_usb_driver::{
135        EndpointAddress,
136        EndpointAllocError,
137        EndpointType,
138        Event,
139        Unsupported,
140    };
141    pub use embassy_usb_synopsys_otg::Config;
142    use embassy_usb_synopsys_otg::{
143        Bus as OtgBus,
144        ControlPipe,
145        Driver as OtgDriver,
146        Endpoint,
147        In,
148        OtgInstance,
149        Out,
150        PhyType,
151        State,
152        on_interrupt,
153        otg_v1::Otg,
154    };
155    use procmacros::handler;
156
157    use super::*;
158    use crate::system::Cpu;
159
160    // From ESP32-S3 TRM:
161    // Six additional endpoints (endpoint numbers 1 to 6), configurable as IN or OUT
162    const MAX_EP_COUNT: usize = 7;
163
164    static STATE: State<MAX_EP_COUNT> = State::new();
165
166    /// Asynchronous USB driver.
167    pub struct Driver<'d> {
168        inner: OtgDriver<'d, MAX_EP_COUNT>,
169        _usb: Usb<'d>,
170    }
171
172    impl<'d> Driver<'d> {
173        const REGISTERS: Otg = unsafe { Otg::from_ptr(Usb::REGISTERS.cast_mut()) };
174
175        /// Initializes USB OTG peripheral with internal Full-Speed PHY, for
176        /// asynchronous operation.
177        ///
178        /// # Arguments
179        ///
180        /// * `ep_out_buffer` - An internal buffer used to temporarily store received packets.
181        ///
182        /// Must be large enough to fit all OUT endpoint max packet sizes.
183        /// Endpoint allocation will fail if it is too small.
184        pub fn new(peri: Usb<'d>, ep_out_buffer: &'d mut [u8], config: Config) -> Self {
185            // From `synopsys-usb-otg` crate:
186            // This calculation doesn't correspond to one in a Reference Manual.
187            // In fact, the required number of words is higher than indicated in RM.
188            // The following numbers are pessimistic and were figured out empirically.
189            const RX_FIFO_EXTRA_SIZE_WORDS: u16 = 30;
190
191            let instance = OtgInstance {
192                regs: Self::REGISTERS,
193                state: &STATE,
194                fifo_depth_words: Usb::FIFO_DEPTH_WORDS as u16,
195                extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS,
196                endpoint_count: Usb::ENDPOINT_COUNT,
197                phy_type: PhyType::InternalFullSpeed,
198                calculate_trdt_fn: |_| 5,
199            };
200            Self {
201                inner: OtgDriver::new(ep_out_buffer, instance, config),
202                _usb: peri,
203            }
204        }
205    }
206
207    impl<'d> embassy_usb_driver::Driver<'d> for Driver<'d> {
208        type EndpointOut = Endpoint<'d, Out>;
209        type EndpointIn = Endpoint<'d, In>;
210        type ControlPipe = ControlPipe<'d>;
211        type Bus = Bus<'d>;
212
213        fn alloc_endpoint_in(
214            &mut self,
215            ep_type: EndpointType,
216            max_packet_size: u16,
217            interval_ms: u8,
218        ) -> Result<Self::EndpointIn, EndpointAllocError> {
219            self.inner
220                .alloc_endpoint_in(ep_type, max_packet_size, interval_ms)
221        }
222
223        fn alloc_endpoint_out(
224            &mut self,
225            ep_type: EndpointType,
226            max_packet_size: u16,
227            interval_ms: u8,
228        ) -> Result<Self::EndpointOut, EndpointAllocError> {
229            self.inner
230                .alloc_endpoint_out(ep_type, max_packet_size, interval_ms)
231        }
232
233        fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
234            let (bus, cp) = self.inner.start(control_max_packet_size);
235
236            let mut bus = Bus {
237                inner: bus,
238                _usb: self._usb,
239            };
240
241            bus.init();
242
243            (bus, cp)
244        }
245    }
246
247    /// Asynchronous USB bus mainly used internally by `embassy-usb`.
248    // We need a custom wrapper implementation to handle custom initialization.
249    pub struct Bus<'d> {
250        inner: OtgBus<'d, MAX_EP_COUNT>,
251        _usb: Usb<'d>,
252    }
253
254    impl Bus<'_> {
255        fn init(&mut self) {
256            Usb::_enable();
257
258            let r = Driver::REGISTERS;
259
260            // Wait for AHB ready.
261            while !r.grstctl().read().ahbidl() {}
262
263            // Configure as device.
264            r.gusbcfg().modify(|w| {
265                // Force device mode
266                w.set_fdmod(true);
267                w.set_srpcap(false);
268            });
269
270            // Perform core soft-reset
271            while !r.grstctl().read().ahbidl() {}
272            r.grstctl().modify(|w| w.set_csrst(true));
273            while r.grstctl().read().csrst() {}
274
275            self.inner.config_v1();
276
277            // Enable PHY clock
278            r.pcgcctl().write(|w| w.0 = 0);
279
280            unsafe {
281                crate::interrupt::bind_interrupt(
282                    crate::peripherals::Interrupt::USB,
283                    interrupt_handler.handler(),
284                );
285            }
286            unwrap!(crate::interrupt::enable(
287                crate::peripherals::Interrupt::USB,
288                interrupt_handler.priority(),
289            ));
290        }
291
292        fn disable(&mut self) {
293            crate::interrupt::disable(Cpu::ProCpu, peripherals::Interrupt::USB);
294
295            #[cfg(multi_core)]
296            crate::interrupt::disable(Cpu::AppCpu, peripherals::Interrupt::USB);
297
298            Usb::_disable();
299        }
300    }
301
302    impl embassy_usb_driver::Bus for Bus<'_> {
303        async fn poll(&mut self) -> Event {
304            self.inner.poll().await
305        }
306
307        fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
308            self.inner.endpoint_set_stalled(ep_addr, stalled)
309        }
310
311        fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {
312            self.inner.endpoint_is_stalled(ep_addr)
313        }
314
315        fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {
316            self.inner.endpoint_set_enabled(ep_addr, enabled)
317        }
318
319        async fn enable(&mut self) {
320            self.inner.enable().await
321        }
322
323        async fn disable(&mut self) {
324            self.inner.disable().await
325        }
326
327        async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {
328            self.inner.remote_wakeup().await
329        }
330    }
331
332    impl Drop for Bus<'_> {
333        fn drop(&mut self) {
334            Bus::disable(self);
335        }
336    }
337
338    #[handler(priority = crate::interrupt::Priority::max())]
339    fn interrupt_handler() {
340        unsafe { on_interrupt(Driver::REGISTERS, &STATE, Usb::ENDPOINT_COUNT) }
341    }
342}