imxrt_usbd/
td.rs

1//! Endpoint Transfer Descriptors (TD)
2//!
3//! The module implements a RAL-compatible interface for working
4//! with transfer descriptors.
5
6#![allow(non_snake_case, non_upper_case_globals)]
7
8use crate::{ral, vcell::VCell};
9
10#[repr(C)]
11pub struct Td {
12    NEXT: VCell<u32>,
13    TOKEN: VCell<u32>,
14    BUFFER_POINTERS: [VCell<u32>; 5],
15    // Reserved memory for other information
16    last_transfer_size: usize,
17}
18
19impl Td {
20    pub const fn new() -> Self {
21        Td {
22            NEXT: VCell::new(0),
23            TOKEN: VCell::new(0),
24            BUFFER_POINTERS: [
25                VCell::new(0),
26                VCell::new(0),
27                VCell::new(0),
28                VCell::new(0),
29                VCell::new(0),
30            ],
31            last_transfer_size: 0,
32        }
33    }
34
35    /// Prepare a transfer to / from the memory described by `ptr` and `size`
36    ///
37    /// Specifieds `size` as the total bytes expected to transfer. This may not
38    /// be what's fully transferred; check `bytes_transferred` after the transfer
39    /// completes.
40    pub fn set_buffer(&mut self, ptr: *mut u8, size: usize) {
41        ral::modify_reg!(crate::td, self, TOKEN, TOTAL_BYTES: size as u32);
42        self.last_transfer_size = size;
43
44        if size != 0 {
45            const PTR_ALIGNMENT: u32 = 4096;
46            const PTR_MASK: u32 = !(PTR_ALIGNMENT - 1);
47
48            self.BUFFER_POINTERS[0].write(ptr as u32);
49            for idx in 1..self.BUFFER_POINTERS.len() {
50                let mut ptr = self.BUFFER_POINTERS[idx - 1].read();
51                ptr &= PTR_MASK;
52                ptr += PTR_ALIGNMENT;
53                self.BUFFER_POINTERS[idx].write(ptr);
54            }
55        } else {
56            for buffer_pointer in self.BUFFER_POINTERS.iter_mut() {
57                buffer_pointer.write(0);
58            }
59        }
60    }
61
62    /// Returns the number of bytes transferred in the previous transfer
63    pub fn bytes_transferred(&self) -> usize {
64        let total_bytes = ral::read_reg!(crate::td, self, TOKEN, TOTAL_BYTES) as usize;
65        self.last_transfer_size - total_bytes
66    }
67
68    /// Read the status of the current / previous transfer
69    pub fn status(&self) -> Status {
70        let status = ral::read_reg!(crate::td, self, TOKEN, STATUS);
71        Status::from_bits_truncate(status)
72    }
73
74    /// Clear all status flags in this transfer descriptor
75    pub fn clear_status(&mut self) {
76        ral::modify_reg!(crate::td, self, TOKEN, STATUS: 0);
77    }
78
79    /// Set the terminate bit to indicate that this TD points to an invalid
80    /// next TD
81    pub fn set_terminate(&mut self) {
82        ral::write_reg!(crate::td, self, NEXT, 1);
83    }
84
85    /// Set the next TD pointed at by this TD
86    pub fn set_next(&mut self, next: *const Td) {
87        ral::write_reg!(crate::td, self, NEXT, next as u32);
88    }
89
90    /// Set the active flag
91    pub fn set_active(&mut self) {
92        ral::modify_reg!(crate::td, self, TOKEN, STATUS: ACTIVE);
93    }
94
95    /// Specify if transfer completion should be indicated as a
96    /// USB interrupt (irrespective of an actual ISR run)
97    pub fn set_interrupt_on_complete(&mut self, ioc: bool) {
98        ral::modify_reg!(crate::td, self, TOKEN, IOC: ioc as u32);
99    }
100
101    /// Clean and invalidate this TD from DCache
102    pub fn clean_invalidate_dcache(&self) {
103        crate::cache::clean_invalidate_dcache_by_address(
104            &self as *const _ as usize,
105            core::mem::size_of_val(self),
106        );
107    }
108}
109
110bitflags::bitflags! {
111    pub(crate) struct Status : u32 {
112        const ACTIVE = TOKEN::STATUS::RW::ACTIVE;
113        const HALTED = TOKEN::STATUS::RW::HALTED;
114        const DATA_BUFFER_ERROR = TOKEN::STATUS::RW::DATA_BUFFER_ERROR;
115        const TRANSACTION_ERROR = TOKEN::STATUS::RW::TRANSACTION_ERROR;
116    }
117}
118
119mod TOKEN {
120    pub mod STATUS {
121        pub const offset: u32 = 0;
122        pub const mask: u32 = 0xFF << offset;
123        pub mod RW {
124            pub const ACTIVE: u32 = 1 << 7;
125            pub const HALTED: u32 = 1 << 6;
126            pub const DATA_BUFFER_ERROR: u32 = 1 << 5;
127            pub const TRANSACTION_ERROR: u32 = 1 << 3;
128        }
129        pub mod R {}
130        pub mod W {}
131    }
132    pub mod IOC {
133        pub const offset: u32 = 15;
134        pub const mask: u32 = 1 << offset;
135        pub mod RW {}
136        pub mod R {}
137        pub mod W {}
138    }
139    pub mod TOTAL_BYTES {
140        pub const offset: u32 = 16;
141        pub const mask: u32 = 0x7FFF << offset;
142        pub mod RW {}
143        pub mod R {}
144        pub mod W {}
145    }
146}
147
148#[cfg(test)]
149mod test {
150    use super::Td;
151    use crate::ral;
152
153    #[test]
154    fn terminate() {
155        let mut td = Td::new();
156        td.set_terminate();
157        assert_eq!(td.NEXT.read(), 1);
158    }
159
160    #[test]
161    fn next() {
162        let mut td = Td::new();
163        td.set_terminate();
164
165        let other = u32::max_value() & !(31);
166        td.set_next(other as *const _);
167        assert_eq!(td.NEXT.read(), other);
168    }
169
170    #[test]
171    fn status() {
172        let mut td = Td::new();
173        ral::write_reg!(super, &mut td, TOKEN, STATUS: u32::max_value());
174        assert_eq!(td.TOKEN.read(), 0b11111111);
175    }
176
177    #[test]
178    fn ioc() {
179        let mut td = Td::new();
180        ral::write_reg!(super, &mut td, TOKEN, IOC: u32::max_value());
181        assert_eq!(td.TOKEN.read(), 1 << 15);
182    }
183
184    #[test]
185    fn total_bytes() {
186        let mut td = Td::new();
187        ral::write_reg!(super, &mut td, TOKEN, TOTAL_BYTES: u32::max_value());
188        assert_eq!(td.TOKEN.read(), 0x7FFF << 16);
189    }
190
191    #[test]
192    fn set_buffer() {
193        let mut td = Td::new();
194        let mut buffer = [0; 32];
195        td.set_buffer(buffer.as_mut_ptr(), buffer.len());
196        assert_eq!(td.NEXT.read(), 0);
197        assert_eq!(td.TOKEN.read(), (32 << 16));
198        assert!(td.status().is_empty());
199        for buffer_pointer in td.BUFFER_POINTERS.iter() {
200            assert!(buffer_pointer.read() != 0);
201        }
202    }
203}
204
205#[cfg(target_arch = "arm")]
206const _: [(); 1] = [(); (core::mem::size_of::<Td>() == 32) as usize];