libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
esp_usb.cpp
1#include "esp_usb.hpp"
2
3#if SOC_USB_OTG_SUPPORTED && defined(CONFIG_IDF_TARGET_ESP32S3) && \
4 CONFIG_IDF_TARGET_ESP32S3
5
6#include <algorithm>
7#include <cstring>
8
9#include "esp_err.h"
10#include "esp_heap_caps.h"
11#include "esp_memory_utils.h"
12#include "hal/cache_hal.h"
13
14namespace LibXR::ESPUSBDetail
15{
16
17#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE || SOC_PSRAM_DMA_CAPABLE
18extern "C" esp_err_t esp_cache_msync(void* addr, size_t size, int flags);
19
20constexpr int kCacheSyncFlagUnaligned = (1 << 1);
21constexpr int kCacheSyncFlagDirC2M = (1 << 2);
22constexpr int kCacheSyncFlagDirM2C = (1 << 3);
23
24bool CacheSyncDmaBuffer(const void* addr, size_t size, bool cache_to_mem,
25 bool allow_unaligned)
26{
27 if ((addr == nullptr) || (size == 0U))
28 {
29 return true;
30 }
31
32 uint32_t cache_level = 0;
33 uint32_t cache_id = 0;
34 const bool cache_supported = cache_hal_vaddr_to_cache_level_id(
35 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(addr)), size, &cache_level,
36 &cache_id);
37 if (!cache_supported)
38 {
39 return true;
40 }
41
42 int flags = cache_to_mem ? kCacheSyncFlagDirC2M : kCacheSyncFlagDirM2C;
43 if (allow_unaligned && cache_to_mem)
44 {
45 flags |= kCacheSyncFlagUnaligned;
46 }
47 return esp_cache_msync(const_cast<void*>(addr), size, flags) == ESP_OK;
48}
49#else
50bool CacheSyncDmaBuffer(const void*, size_t, bool, bool) { return true; }
51#endif
52
53size_t AlignUp(size_t value, size_t align)
54{
55 if (align <= 1U)
56 {
57 return value;
58 }
59 return ((value + align - 1U) / align) * align;
60}
61
62esp_dma_mem_info_t UsbDmaMemInfo()
63{
64 esp_dma_mem_info_t info = {};
65 info.extra_heap_caps = MALLOC_CAP_INTERNAL;
66 info.dma_alignment_bytes = kWordSize;
67 return info;
68}
69
70bool CanUseDirectInDmaBuffer(const void* ptr, size_t size)
71{
72 if ((ptr == nullptr) || (size == 0U))
73 {
74 return size == 0U;
75 }
76
77 auto* start = static_cast<const uint8_t*>(ptr);
78 auto* end = start + size - 1U;
79 return esp_ptr_dma_capable(start) && esp_ptr_dma_capable(end) &&
80 esp_ptr_word_aligned(ptr);
81}
82
83bool CanUseDirectOutDmaBuffer(const void* ptr, size_t size)
84{
85 if ((ptr == nullptr) || (size == 0U))
86 {
87 return false;
88 }
89
90 auto* start = static_cast<const uint8_t*>(ptr);
91 auto* end = start + size - 1U;
92 if (!esp_ptr_dma_capable(start) || !esp_ptr_dma_capable(end) ||
93 !esp_ptr_word_aligned(ptr) ||
94 !esp_dma_is_buffer_alignment_satisfied(ptr, size, UsbDmaMemInfo()))
95 {
96 return false;
97 }
98
99 uint32_t cache_level = 0;
100 uint32_t cache_id = 0;
101 const bool cache_supported = cache_hal_vaddr_to_cache_level_id(
102 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(ptr)), size, &cache_level,
103 &cache_id);
104 if (!cache_supported)
105 {
106 return true;
107 }
108
109 const uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
110 return ((addr % kUsbDmaAlignment) == 0U) && (AlignUp(size, kUsbDmaAlignment) == size);
111}
112
113uint16_t CalcRxFifoWords(uint16_t largest_packet_size, uint8_t ep_count)
114{
115 return static_cast<uint16_t>(13U + 1U + 2U * (((largest_packet_size + 3U) / 4U) + 1U) +
116 2U * ep_count);
117}
118
119uint16_t CalcConfiguredRxFifoWords(uint16_t largest_packet_size, uint8_t ep_count,
120 bool dma_enabled)
121{
122 uint16_t words = CalcRxFifoWords(largest_packet_size, ep_count);
123 if (dma_enabled)
124 {
125 words = std::max<uint16_t>(words, kEsp32SxFsDmaMinRxFifoWords);
126 }
127 return words;
128}
129
130uint16_t GetHardwareFifoDepthWords()
131{
132 auto* dev = reinterpret_cast<usb_dwc_dev_t*>(kDwc2FsRegBase);
133 return static_cast<uint16_t>(dev->ghwcfg3_reg.dfifodepth);
134}
135
136uint8_t EncodeEp0Mps(uint16_t packet_size)
137{
138 switch (packet_size)
139 {
140 case 8:
141 return 3U;
142 case 16:
143 return 2U;
144 case 32:
145 return 1U;
146 case 64:
147 default:
148 return 0U;
149 }
150}
151
152uint16_t ClampPacketSize(LibXR::USB::Endpoint::Type type, uint16_t requested)
153{
154 switch (type)
155 {
157 return static_cast<uint16_t>(std::min<uint16_t>(requested, 64U));
159 return static_cast<uint16_t>(std::min<uint16_t>(requested, 64U));
161 return static_cast<uint16_t>(std::min<uint16_t>(requested, 64U));
163 return static_cast<uint16_t>(std::min<uint16_t>(requested, 1023U));
164 default:
165 return requested;
166 }
167}
168
169uint16_t CalcTxFifoWords(uint16_t packet_size, bool dma_enabled)
170{
171 uint16_t words = static_cast<uint16_t>((packet_size + 3U) / 4U);
172 if (dma_enabled)
173 {
174 words = std::max<uint16_t>(words, kEsp32SxFsMinTxFifoWords);
175 }
176 return words;
177}
178
179volatile uint32_t* GetEndpointFifo(usb_dwc_dev_t* dev, uint8_t ep_num)
180{
181 uintptr_t base = reinterpret_cast<uintptr_t>(dev);
182 return reinterpret_cast<volatile uint32_t*>(
183 base + kFifoBaseOffset + static_cast<uintptr_t>(ep_num) * kFifoStride);
184}
185
186void WriteFifoPacket(volatile uint32_t* fifo, const uint8_t* src, size_t size)
187{
188 for (size_t offset = 0; offset < size; offset += kWordSize)
189 {
190 uint32_t word = 0U;
191 const size_t chunk = std::min(kWordSize, size - offset);
192 std::memcpy(&word, src + offset, chunk);
193 fifo[0] = word;
194 }
195}
196
197void ReadFifoPacket(const volatile uint32_t* fifo, uint8_t* dst, size_t size)
198{
199 for (size_t offset = 0; offset < size; offset += kWordSize)
200 {
201 const uint32_t word = fifo[0];
202 const size_t chunk = std::min(kWordSize, size - offset);
203 std::memcpy(dst + offset, &word, chunk);
204 }
205}
206
207uint16_t PacketCount(size_t size, uint16_t max_packet_size)
208{
209 if (size == 0U)
210 {
211 return 1U;
212 }
213 return static_cast<uint16_t>((size + max_packet_size - 1U) / max_packet_size);
214}
215
216void FlushTxFifo(usb_dwc_dev_t* dev, uint8_t fifo_num)
217{
218 dev->grstctl_reg.txfnum = fifo_num;
219 dev->grstctl_reg.txfflsh = 1;
220 while (dev->grstctl_reg.txfflsh)
221 {
222 }
223}
224
225void DisableInEndpointAndWait(usb_dwc_dev_t* dev)
226{
227 auto& ctl = dev->diepctl0_reg;
228 auto& intr = dev->diepint0_reg;
229 if (!ctl.epena)
230 {
231 return;
232 }
233
234 ctl.snak = 1;
235 while (!intr.inepnakeff)
236 {
237 }
238
239 usb_dwc_diepint0_reg_t clear_nak = {};
240 clear_nak.inepnakeff = 1;
241 intr.val = clear_nak.val;
242
243 ctl.epdis = 1;
244 while (!intr.epdisbld)
245 {
246 }
247
248 usb_dwc_diepint0_reg_t clear_dis = {};
249 clear_dis.epdisbld = 1;
250 intr.val = clear_dis.val;
251 FlushTxFifo(dev, 0U);
252}
253
254void DisableInEndpointAndWait(usb_dwc_dev_t* dev, uint8_t ep_num)
255{
256 auto& ctl = dev->in_eps[ep_num - 1U].diepctl_reg;
257 auto& intr = dev->in_eps[ep_num - 1U].diepint_reg;
258 if (!ctl.epena)
259 {
260 return;
261 }
262
263 ctl.snak = 1;
264 while (!intr.inepnakeff)
265 {
266 }
267
268 usb_dwc_diepint_reg_t clear_nak = {};
269 clear_nak.inepnakeff = 1;
270 intr.val = clear_nak.val;
271
272 ctl.epdis = 1;
273 while (!intr.epdisbld)
274 {
275 }
276
277 usb_dwc_diepint_reg_t clear_dis = {};
278 clear_dis.epdisbld = 1;
279 intr.val = clear_dis.val;
280 FlushTxFifo(dev, ep_num);
281}
282
283} // namespace LibXR::ESPUSBDetail
284
285#endif
Type
端点类型 Endpoint type
Definition ep.hpp:69
@ ISOCHRONOUS
等时端点 / Isochronous
@ BULK
批量端点 / Bulk
@ INTERRUPT
中断端点 / Interrupt
@ CONTROL
控制端点 / Control