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
56/// USB peripheral.
57pub struct Usb<'d> {
58    _usb0: peripherals::USB0<'d>,
59    _guard: GenericPeripheralGuard<{ PeripheralEnable::Usb as u8 }>,
60}
61
62impl<'d> Usb<'d> {
63    /// Creates a new `Usb` instance.
64    pub fn new(
65        usb0: peripherals::USB0<'d>,
66        _usb_dp: impl UsbDp + 'd,
67        _usb_dm: impl UsbDm + 'd,
68    ) -> Self {
69        let guard = GenericPeripheralGuard::new();
70
71        Self {
72            _usb0: usb0,
73            _guard: guard,
74        }
75    }
76
77    fn _enable() {
78        peripherals::USB_WRAP::regs().otg_conf().modify(|_, w| {
79            w.usb_pad_enable().set_bit();
80            w.phy_sel().clear_bit();
81            w.clk_en().set_bit();
82            w.ahb_clk_force_on().set_bit();
83            w.phy_clk_force_on().set_bit()
84        });
85
86        #[cfg(esp32s3)]
87        peripherals::LPWR::regs().usb_conf().modify(|_, w| {
88            w.sw_hw_usb_phy_sel().set_bit();
89            w.sw_usb_phy_sel().set_bit()
90        });
91
92        use crate::gpio::Level;
93
94        InputSignal::USB_OTG_IDDIG.connect_to(&Level::High); // connected connector is mini-B side
95        InputSignal::USB_SRP_BVALID.connect_to(&Level::High); // HIGH to force USB device mode
96        InputSignal::USB_OTG_VBUSVALID.connect_to(&Level::High); // receiving a valid Vbus from device
97        InputSignal::USB_OTG_AVALID.connect_to(&Level::Low);
98    }
99
100    fn _disable() {
101        // TODO
102    }
103}
104
105unsafe impl Sync for Usb<'_> {}
106
107unsafe impl UsbPeripheral for Usb<'_> {
108    const REGISTERS: *const () = peripherals::USB0::PTR.cast();
109
110    const HIGH_SPEED: bool = false;
111    const FIFO_DEPTH_WORDS: usize = 256;
112    const ENDPOINT_COUNT: usize = 5;
113
114    fn enable() {
115        Self::_enable();
116    }
117
118    fn ahb_frequency_hz(&self) -> u32 {
119        // unused
120        80_000_000
121    }
122}
123/// Async functionality
124pub mod asynch {
125    use embassy_usb_driver::{
126        EndpointAddress,
127        EndpointAllocError,
128        EndpointType,
129        Event,
130        Unsupported,
131    };
132    pub use embassy_usb_synopsys_otg::Config;
133    use embassy_usb_synopsys_otg::{
134        Bus as OtgBus,
135        ControlPipe,
136        Driver as OtgDriver,
137        Endpoint,
138        In,
139        OtgInstance,
140        Out,
141        PhyType,
142        State,
143        on_interrupt,
144        otg_v1::Otg,
145    };
146    use procmacros::handler;
147
148    use super::*;
149    use crate::system::Cpu;
150
151    // From ESP32-S3 TRM:
152    // Six additional endpoints (endpoint numbers 1 to 6), configurable as IN or OUT
153    const MAX_EP_COUNT: usize = 7;
154
155    static STATE: State<MAX_EP_COUNT> = State::new();
156
157    /// Asynchronous USB driver.
158    pub struct Driver<'d> {
159        inner: OtgDriver<'d, MAX_EP_COUNT>,
160        _usb: Usb<'d>,
161    }
162
163    impl<'d> Driver<'d> {
164        const REGISTERS: Otg = unsafe { Otg::from_ptr(Usb::REGISTERS.cast_mut()) };
165
166        /// Initializes USB OTG peripheral with internal Full-Speed PHY, for
167        /// asynchronous operation.
168        ///
169        /// # Arguments
170        ///
171        /// * `ep_out_buffer` - An internal buffer used to temporarily store
172        ///   received packets.
173        ///
174        /// Must be large enough to fit all OUT endpoint max packet sizes.
175        /// Endpoint allocation will fail if it is too small.
176        pub fn new(peri: Usb<'d>, ep_out_buffer: &'d mut [u8], config: Config) -> Self {
177            // From `synopsys-usb-otg` crate:
178            // This calculation doesn't correspond to one in a Reference Manual.
179            // In fact, the required number of words is higher than indicated in RM.
180            // The following numbers are pessimistic and were figured out empirically.
181            const RX_FIFO_EXTRA_SIZE_WORDS: u16 = 30;
182
183            let instance = OtgInstance {
184                regs: Self::REGISTERS,
185                state: &STATE,
186                fifo_depth_words: Usb::FIFO_DEPTH_WORDS as u16,
187                extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS,
188                endpoint_count: Usb::ENDPOINT_COUNT,
189                phy_type: PhyType::InternalFullSpeed,
190                calculate_trdt_fn: |_| 5,
191            };
192            Self {
193                inner: OtgDriver::new(ep_out_buffer, instance, config),
194                _usb: peri,
195            }
196        }
197    }
198
199    impl<'d> embassy_usb_driver::Driver<'d> for Driver<'d> {
200        type EndpointOut = Endpoint<'d, Out>;
201        type EndpointIn = Endpoint<'d, In>;
202        type ControlPipe = ControlPipe<'d>;
203        type Bus = Bus<'d>;
204
205        fn alloc_endpoint_in(
206            &mut self,
207            ep_type: EndpointType,
208            max_packet_size: u16,
209            interval_ms: u8,
210        ) -> Result<Self::EndpointIn, EndpointAllocError> {
211            self.inner
212                .alloc_endpoint_in(ep_type, max_packet_size, interval_ms)
213        }
214
215        fn alloc_endpoint_out(
216            &mut self,
217            ep_type: EndpointType,
218            max_packet_size: u16,
219            interval_ms: u8,
220        ) -> Result<Self::EndpointOut, EndpointAllocError> {
221            self.inner
222                .alloc_endpoint_out(ep_type, max_packet_size, interval_ms)
223        }
224
225        fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
226            let (bus, cp) = self.inner.start(control_max_packet_size);
227
228            let mut bus = Bus {
229                inner: bus,
230                _usb: self._usb,
231            };
232
233            bus.init();
234
235            (bus, cp)
236        }
237    }
238
239    /// Asynchronous USB bus mainly used internally by `embassy-usb`.
240    // We need a custom wrapper implementation to handle custom initialization.
241    pub struct Bus<'d> {
242        inner: OtgBus<'d, MAX_EP_COUNT>,
243        _usb: Usb<'d>,
244    }
245
246    impl Bus<'_> {
247        fn init(&mut self) {
248            Usb::_enable();
249
250            let r = Driver::REGISTERS;
251
252            // Wait for AHB ready.
253            while !r.grstctl().read().ahbidl() {}
254
255            // Configure as device.
256            r.gusbcfg().modify(|w| {
257                // Force device mode
258                w.set_fdmod(true);
259                w.set_srpcap(false);
260            });
261
262            // Perform core soft-reset
263            while !r.grstctl().read().ahbidl() {}
264            r.grstctl().modify(|w| w.set_csrst(true));
265            while r.grstctl().read().csrst() {}
266
267            self.inner.config_v1();
268
269            // Enable PHY clock
270            r.pcgcctl().write(|w| w.0 = 0);
271
272            unsafe {
273                crate::interrupt::bind_interrupt(
274                    crate::peripherals::Interrupt::USB,
275                    interrupt_handler.handler(),
276                );
277            }
278            unwrap!(crate::interrupt::enable(
279                crate::peripherals::Interrupt::USB,
280                interrupt_handler.priority(),
281            ));
282        }
283
284        fn disable(&mut self) {
285            crate::interrupt::disable(Cpu::ProCpu, peripherals::Interrupt::USB);
286
287            #[cfg(multi_core)]
288            crate::interrupt::disable(Cpu::AppCpu, peripherals::Interrupt::USB);
289
290            Usb::_disable();
291        }
292    }
293
294    impl embassy_usb_driver::Bus for Bus<'_> {
295        async fn poll(&mut self) -> Event {
296            self.inner.poll().await
297        }
298
299        fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
300            self.inner.endpoint_set_stalled(ep_addr, stalled)
301        }
302
303        fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {
304            self.inner.endpoint_is_stalled(ep_addr)
305        }
306
307        fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {
308            self.inner.endpoint_set_enabled(ep_addr, enabled)
309        }
310
311        async fn enable(&mut self) {
312            self.inner.enable().await
313        }
314
315        async fn disable(&mut self) {
316            self.inner.disable().await
317        }
318
319        async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {
320            self.inner.remote_wakeup().await
321        }
322    }
323
324    impl Drop for Bus<'_> {
325        fn drop(&mut self) {
326            Bus::disable(self);
327        }
328    }
329
330    #[handler(priority = crate::interrupt::Priority::max())]
331    fn interrupt_handler() {
332        unsafe { on_interrupt(Driver::REGISTERS, &STATE, Usb::ENDPOINT_COUNT) }
333    }
334}