Skip to main content

ra_hal/usb/
control_pipe.rs

1//! USB Control Pipe, for operations that work on the control endpoint (EP0).
2
3use core::{marker::PhantomData, sync::atomic::Ordering, task::Poll};
4
5use embassy_usb_driver::{ControlPipe as DriverControlPipe, EndpointError};
6
7use crate::{
8    pac::usbfs::vals::{CfifoselCurpipe, DcpctrPid},
9    usb::{Instance, STATE},
10};
11
12/// USB control endpoint IN+OUT.
13pub struct ControlPipe<'a, I: Instance> {
14    pub(crate) _phantom: PhantomData<&'a I>,
15    pub(crate) max_packet_size: u16,
16}
17
18impl<'a, I: Instance> DriverControlPipe for ControlPipe<'a, I> {
19    fn max_packet_size(&self) -> usize {
20        self.max_packet_size as _
21    }
22
23    async fn setup(&mut self) -> [u8; 8] {
24        let pkt = core::future::poll_fn(|cx| {
25            STATE.ep_wakers[0].register(cx.waker());
26
27            if STATE.setup_ready.load(Ordering::Acquire) {
28                STATE.setup_ready.store(false, Ordering::Release);
29                let lo = STATE.setup_lo.load(Ordering::Acquire);
30                let hi = STATE.setup_hi.load(Ordering::Acquire);
31                let pkt = [
32                    lo as _,
33                    (lo >> 8) as _,
34                    (lo >> 16) as _,
35                    (lo >> 24) as _,
36                    hi as _,
37                    (hi >> 8) as _,
38                    (hi >> 16) as _,
39                    (hi >> 24) as _,
40                ];
41                Poll::Ready(pkt)
42            } else {
43                Poll::Pending
44            }
45        })
46        .await;
47
48        debug!(
49            "USB: setup [{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}]",
50            pkt[0], pkt[1], pkt[2], pkt[3], pkt[4], pkt[5], pkt[6], pkt[7]
51        );
52        pkt
53    }
54
55    async fn data_out(
56        &mut self,
57        buf: &mut [u8],
58        _first: bool,
59        _last: bool,
60    ) -> Result<usize, EndpointError> {
61        trace!("USB: ctrl data_out waiting (buf={})", buf.len());
62        let r = I::regs();
63
64        // Point CFIFO at the DCP (default control pipe) receive buffer
65        r.cfifosel().modify(|r| {
66            r.set_curpipe(CfifoselCurpipe::Default);
67            // OUT direction (rx)
68            r.set_isel(false);
69        });
70
71        // After the SETUP stage, DCPCTR.PID is NAK. Set BUF so the hardware
72        // accepts the host's DATA OUT token; without this BRDY never fires.
73        r.dcpctr().modify(|r| r.set_pid(DcpctrPid::Buffer));
74
75        // Wait for DCP BRDY (host sent OUT data)
76        core::future::poll_fn(|cx| {
77            STATE.ep_wakers[0].register(cx.waker());
78            if STATE.pipe_brdy.load(Ordering::Acquire) & 1 != 0 {
79                STATE.pipe_brdy.fetch_and(!1, Ordering::AcqRel);
80                Poll::Ready(())
81            } else {
82                Poll::Pending
83            }
84        })
85        .await;
86
87        // Wait for FIFO to be ready
88        while !r.cfifoctr().read().frdy() {
89            cortex_m::asm::nop();
90        }
91
92        let data_len = r.cfifoctr().read().dtln() as usize;
93        let read_len = data_len.min(buf.len());
94        if data_len > read_len {
95            // Overflow, clear buffer and signal
96            r.cfifoctr().modify(|r| r.set_bclr(true));
97            return Err(EndpointError::BufferOverflow);
98        }
99
100        for byte in buf[..read_len].iter_mut() {
101            *byte = r.cfifol().read().fifoport();
102        }
103
104        // Discard remaining
105        if data_len > read_len {
106            r.cfifoctr().modify(|r| r.set_bclr(true));
107        }
108
109        trace!("USB: ctrl data_out read {} bytes", read_len);
110        Ok(read_len)
111    }
112
113    async fn data_in(
114        &mut self,
115        data: &[u8],
116        _first: bool,
117        last: bool,
118    ) -> Result<(), EndpointError> {
119        trace!("USB: ctrl data_in {} bytes, last={}", data.len(), last);
120        let r = I::regs();
121
122        if data.is_empty() && last {
123            // Zero-length status response: PID=Buffer + CCPL
124            r.dcpctr().modify(|r| {
125                r.set_pid(DcpctrPid::Buffer);
126                r.set_ccpl(true);
127            });
128            return Ok(());
129        }
130
131        /*
132         * Discard any stale BEMP that may have been set while the DCP was idle (e.g. the inital
133         * empty buffer notification from `start()`).
134         */
135        STATE.pipe_bemp.fetch_and(!1, Ordering::AcqRel);
136
137        // Select DCP TX direction on CFIFO
138        r.cfifosel().modify(|r| {
139            r.set_curpipe(CfifoselCurpipe::Default);
140            r.set_isel(true);
141        });
142
143        // Wait for the TX FIFO to be ready
144        while !r.cfifoctr().read().frdy() {
145            cortex_m::asm::nop();
146        }
147
148        for &byte in data {
149            r.cfifol().write(|w| w.set_fifoport(byte));
150        }
151
152        // Commit the buffer
153        r.cfifoctr().modify(|r| r.set_bval(true));
154
155        // Enable auto-ACK of the host STATUS stage OUT before starting TX
156        if last {
157            r.dcpctr().modify(|r| r.set_ccpl(true));
158        }
159
160        // Start transmission: PID=BUF lets hardware respond to IN tokens.
161        r.dcpctr().modify(|r| r.set_pid(DcpctrPid::Buffer));
162
163        core::future::poll_fn(|cx| {
164            STATE.ep_wakers[0].register(cx.waker());
165            if STATE.pipe_bemp.load(Ordering::Acquire) & 1 != 0 {
166                STATE.pipe_bemp.fetch_and(!1, Ordering::AcqRel);
167                return Poll::Ready(());
168            } else {
169                Poll::Pending
170            }
171        })
172        .await;
173
174        Ok(())
175    }
176
177    async fn accept(&mut self) {
178        debug!("USB: ctrl accept");
179        let r = I::regs();
180        r.dcpctr().modify(|r| {
181            r.set_pid(DcpctrPid::Buffer);
182            /*
183             * Page 634: When CCPL is set to 1 while the associated PID is set to BUF, the USBFS
184             * completes the control transfer status stage.
185             */
186            r.set_ccpl(true);
187        })
188    }
189
190    async fn reject(&mut self) {
191        debug!("USB: ctrl reject");
192        let r = I::regs();
193        r.dcpctr().modify(|r| r.set_pid(DcpctrPid::Stall2));
194    }
195
196    async fn accept_set_address(&mut self, addr: u8) {
197        debug!("USB: ctrl accept_set_address={}", addr);
198        /*
199         * Page 634: On detecting a SET_ADDRESS request, the USBFS operates in auto-response mode
200         * from the setup stage up to status stage completion regardless of the CCPL bit.
201         */
202        self.accept().await;
203    }
204}