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/peripheral/usb_serial/src/main.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            ep_addr: Option<EndpointAddress>,
217            max_packet_size: u16,
218            interval_ms: u8,
219        ) -> Result<Self::EndpointIn, EndpointAllocError> {
220            self.inner
221                .alloc_endpoint_in(ep_type, ep_addr, max_packet_size, interval_ms)
222        }
223
224        fn alloc_endpoint_out(
225            &mut self,
226            ep_type: EndpointType,
227            ep_addr: Option<EndpointAddress>,
228            max_packet_size: u16,
229            interval_ms: u8,
230        ) -> Result<Self::EndpointOut, EndpointAllocError> {
231            self.inner
232                .alloc_endpoint_out(ep_type, ep_addr, max_packet_size, interval_ms)
233        }
234
235        fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
236            let (bus, cp) = self.inner.start(control_max_packet_size);
237
238            let mut bus = Bus {
239                inner: bus,
240                _usb: self._usb,
241            };
242
243            bus.init();
244
245            (bus, cp)
246        }
247    }
248
249    /// Asynchronous USB bus mainly used internally by `embassy-usb`.
250    // We need a custom wrapper implementation to handle custom initialization.
251    pub struct Bus<'d> {
252        inner: OtgBus<'d, MAX_EP_COUNT>,
253        _usb: Usb<'d>,
254    }
255
256    impl Bus<'_> {
257        fn init(&mut self) {
258            Usb::_enable();
259
260            let r = Driver::REGISTERS;
261
262            // Wait for AHB ready.
263            while !r.grstctl().read().ahbidl() {}
264
265            // Configure as device.
266            r.gusbcfg().modify(|w| {
267                // Force device mode
268                w.set_fdmod(true);
269                w.set_srpcap(false);
270            });
271
272            // Perform core soft-reset
273            while !r.grstctl().read().ahbidl() {}
274            r.grstctl().modify(|w| w.set_csrst(true));
275            while r.grstctl().read().csrst() {}
276
277            self.inner.config_v1();
278
279            // Enable PHY clock
280            r.pcgcctl().write(|w| w.0 = 0);
281
282            unsafe {
283                crate::interrupt::bind_interrupt(
284                    crate::peripherals::Interrupt::USB,
285                    interrupt_handler.handler(),
286                );
287            }
288            unwrap!(crate::interrupt::enable(
289                crate::peripherals::Interrupt::USB,
290                interrupt_handler.priority(),
291            ));
292        }
293
294        fn disable(&mut self) {
295            crate::interrupt::disable(Cpu::ProCpu, peripherals::Interrupt::USB);
296
297            #[cfg(multi_core)]
298            crate::interrupt::disable(Cpu::AppCpu, peripherals::Interrupt::USB);
299
300            Usb::_disable();
301        }
302    }
303
304    impl embassy_usb_driver::Bus for Bus<'_> {
305        async fn poll(&mut self) -> Event {
306            self.inner.poll().await
307        }
308
309        fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) {
310            self.inner.endpoint_set_stalled(ep_addr, stalled)
311        }
312
313        fn endpoint_is_stalled(&mut self, ep_addr: EndpointAddress) -> bool {
314            self.inner.endpoint_is_stalled(ep_addr)
315        }
316
317        fn endpoint_set_enabled(&mut self, ep_addr: EndpointAddress, enabled: bool) {
318            self.inner.endpoint_set_enabled(ep_addr, enabled)
319        }
320
321        async fn enable(&mut self) {
322            self.inner.enable().await
323        }
324
325        async fn disable(&mut self) {
326            self.inner.disable().await
327        }
328
329        async fn remote_wakeup(&mut self) -> Result<(), Unsupported> {
330            self.inner.remote_wakeup().await
331        }
332    }
333
334    impl Drop for Bus<'_> {
335        fn drop(&mut self) {
336            Bus::disable(self);
337        }
338    }
339
340    #[handler(priority = crate::interrupt::Priority::max())]
341    fn interrupt_handler() {
342        unsafe { on_interrupt(Driver::REGISTERS, &STATE, Usb::ENDPOINT_COUNT) }
343    }
344}