libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
ch32_usb_otghs.cpp
1// NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast,performance-no-int-to-ptr)
2// ch32_usb_otghs.cpp (OTG HS)
3#include "ch32_usb_dev.hpp"
4#include "ch32_usb_endpoint.hpp"
5#include "ch32_usb_rcc.hpp"
6#include "ep.hpp"
7
8using namespace LibXR;
9using namespace LibXR::USB;
10
11#if defined(USBHSD)
12
13namespace
14{
15
16using OtgHsEndpointMap = decltype(LibXR::CH32EndpointOtgHs::map_otg_hs_);
17
18constexpr uint8_t OUT_IDX = static_cast<uint8_t>(LibXR::USB::Endpoint::Direction::OUT);
19constexpr uint8_t IN_IDX = static_cast<uint8_t>(LibXR::USB::Endpoint::Direction::IN);
20
21static void ResetEp0State(OtgHsEndpointMap& map)
22{
23 auto* out0 = map[0][OUT_IDX];
24 auto* in0 = map[0][IN_IDX];
25
26 ASSERT(out0 != nullptr);
27 ASSERT(in0 != nullptr);
28
30 out0->tog0_ = true;
31 out0->tog1_ = false;
33 in0->tog0_ = true;
34 in0->tog1_ = false;
35
36 // After reset/suspend recovery, EP0 must be ready for the next setup packet.
37 // reset/suspend 恢复后,EP0 必须回到等待下一包 setup 的状态。
38 USBHSD->UEP0_TX_CTRL = USBHS_UEP_T_TOG_DATA1 | USBHS_UEP_T_RES_NAK;
39 USBHSD->UEP0_RX_CTRL = USBHS_UEP_R_TOG_DATA1 | USBHS_UEP_R_RES_ACK;
40}
41
42static bool CompleteEp0InBeforeSetup(OtgHsEndpointMap& map, uint8_t int_flag,
43 uint8_t int_st)
44{
45 if ((int_flag & USBHS_UIF_SETUP_ACT) == 0u || (int_flag & USBHS_UIF_TRANSFER) == 0u)
46 {
47 return false;
48 }
49 if ((int_st & USBHS_UIS_ENDP_MASK) != 0u ||
50 (int_st & USBHS_UIS_TOKEN_MASK) != USBHS_UIS_TOKEN_IN)
51 {
52 return false;
53 }
54
55 auto* in0 = map[0][IN_IDX];
56 ASSERT(in0 != nullptr);
57 if (in0->GetState() != LibXR::USB::Endpoint::State::BUSY)
58 {
59 return false;
60 }
61
62 // Some setup races report "new setup" and "previous EP0 IN complete" together.
63 // Consume the old EP0 IN completion first, then let setup rebuild the control state.
64 // 部分 setup 竞争会把“新 setup 到来”和“上一笔 EP0 IN 完成”同时上报;
65 // 这里先消费旧的 EP0 IN 完成,再让 setup 重建控制传输状态。
66 in0->TransferComplete(0u);
67 return true;
68}
69
70static void PrepareEp0ForSetup(OtgHsEndpointMap& map)
71{
72 auto* out0 = map[0][OUT_IDX];
73 auto* in0 = map[0][IN_IDX];
74
75 ASSERT(out0 != nullptr);
76 ASSERT(in0 != nullptr);
77
78 // A fresh SETUP cancels the previous control transfer, so EP0 must be re-armed
79 // to the default control endpoint shape before the setup packet is dispatched.
80 // 新的 SETUP 会中断前一笔控制传输,因此在分发 setup 包之前,
81 // 必须先把 EP0 恢复成默认控制端点形态。
84 out0->tog0_ = true;
85 out0->tog1_ = false;
86 in0->tog0_ = true;
87 in0->tog1_ = false;
88 USBHSD->UEP0_TX_CTRL = USBHS_UEP_T_TOG_DATA1 | USBHS_UEP_T_RES_NAK;
89 USBHSD->UEP0_RX_CTRL = USBHS_UEP_R_TOG_DATA1 | USBHS_UEP_R_RES_NAK;
90}
91
92static void HandleTransferOut(CH32EndpointOtgHs* ep_out, uint8_t ep_num, uint8_t int_st)
93{
94 if (ep_out == nullptr)
95 {
96 return;
97 }
98
99 const bool is_nak = ((int_st & USBHS_UIS_IS_NAK) != 0u);
100 const bool busy = (ep_out->GetState() == LibXR::USB::Endpoint::State::BUSY);
101 const uint16_t rx_len = USBHSD->RX_LEN;
102
103 if (ep_num == 0u)
104 {
105 if (busy && ((rx_len > 0u) || !is_nak))
106 {
107 ep_out->TransferComplete(rx_len);
108 }
109 return;
110 }
111
112 if (!is_nak)
113 {
114 ep_out->TransferComplete(rx_len);
115 return;
116 }
117 if (!busy || rx_len == 0u)
118 {
119 return;
120 }
121
122 // Some non-EP0 OUT paths may still expose a usable late length snapshot while the
123 // endpoint stays BUSY. Keep that narrow recovery path here.
124 // 部分 non-EP0 OUT 路径在端点仍处于 BUSY 时,仍可能给出可用的晚到长度快照;
125 // 这里只保留这条窄恢复路径。
126 ep_out->TransferComplete(rx_len);
127}
128
129static void HandleTransferIn(CH32EndpointOtgHs* ep_in, uint8_t ep_num, uint8_t int_st)
130{
131 if (ep_in == nullptr)
132 {
133 return;
134 }
135
136 const bool is_nak = ((int_st & USBHS_UIS_IS_NAK) != 0u);
137 if (!is_nak)
138 {
139 ep_in->TransferComplete(0u);
140 return;
141 }
142
143 const bool busy = (ep_in->GetState() == LibXR::USB::Endpoint::State::BUSY);
144 const bool ep0_data_in_complete =
145 (ep_num == 0u) && busy && (ep_in->last_transfer_size_ > 0u);
146 if (!ep0_data_in_complete)
147 {
148 return;
149 }
150
151 // EP0 data IN may complete after setup arbitration without a visible ACK.
152 // Status ZLP must still stay strict, so only non-zero data stages use this path.
153 // EP0 的数据 IN 在 setup 仲裁后也可能没有可见 ACK 就结束;
154 // 但 status ZLP 仍然必须保持严格,因此这里只允许非零数据阶段走这条路径。
155 ep_in->TransferComplete(0u);
156}
157
158static void HandleTransferToken(OtgHsEndpointMap& map, uint8_t int_st)
159{
160 const uint8_t token = int_st & USBHS_UIS_TOKEN_MASK;
161 const uint8_t ep_num = int_st & USBHS_UIS_ENDP_MASK;
162 auto& ep = map[ep_num];
163
164 switch (token)
165 {
166 case USBHS_UIS_TOKEN_SETUP:
167 {
168 // CH32V30x USBHS reports SETUP through SETUP_ACT, so TOKEN_SETUP is
169 // informational only here and must not re-run the setup path.
170 // CH32V30x USBHS 通过 SETUP_ACT 上报 SETUP,因此这里的 TOKEN_SETUP
171 // 只是信息位,不能再次重复执行 setup 路径。
172 break;
173 }
174
175 case USBHS_UIS_TOKEN_OUT:
176 HandleTransferOut(ep[OUT_IDX], ep_num, int_st);
177 break;
178
179 case USBHS_UIS_TOKEN_IN:
180 HandleTransferIn(ep[IN_IDX], ep_num, int_st);
181 break;
182
183 default:
184 break;
185 }
186}
187
188static void ClearPendingOtgHsInterrupts()
189{
190 while (true)
191 {
192 const uint16_t INTFGST = *reinterpret_cast<volatile uint16_t*>(
193 reinterpret_cast<uintptr_t>(&USBHSD->INT_FG));
194 const uint8_t INTFLAG = static_cast<uint8_t>(INTFGST & 0x00FFu);
195 if (INTFLAG == 0u)
196 {
197 break;
198 }
199 USBHSD->INT_FG = INTFLAG;
200
201 // This loop drains only the bits already latched in INT_FG. If hardware
202 // observes another bus event later, it will assert a new IRQ and the next
203 // handler entry will clear it.
204 // 这个循环只清理 INT_FG 里已经锁存的位;如果之后又出现新的总线事件,
205 // 硬件会重新触发 IRQ,由下一次进入 handler 时再清。
206 }
207}
208
209} // namespace
210
211// NOLINTNEXTLINE(readability-identifier-naming)
212extern "C" __attribute__((interrupt("WCH-Interrupt-fast"))) void USBHS_IRQHandler(void)
213{
214 auto* usb = LibXR::CH32USBOtgHS::self_;
215 if (usb == nullptr || !usb->IsInited())
216 {
217 ClearPendingOtgHsInterrupts();
218 return;
219 }
220
221 auto& map = LibXR::CH32EndpointOtgHs::map_otg_hs_;
222
223 // Handle order matters: recover bus-level events first, then settle EP0 setup
224 // arbitration, and finally dispatch ordinary token completions.
225 // 处理顺序很重要:先恢复总线级事件,再收束 EP0 setup 仲裁,
226 // 最后分发普通 token 完成事件。
227 while (true)
228 {
229 const uint16_t INTFGST = *reinterpret_cast<volatile uint16_t*>(
230 reinterpret_cast<uintptr_t>(&USBHSD->INT_FG));
231
232 const uint8_t INTFLAG = static_cast<uint8_t>(INTFGST & 0x00FFu);
233 const uint8_t INTST = static_cast<uint8_t>((INTFGST >> 8) & 0x00FFu);
234
235 if (INTFLAG == 0)
236 {
237 break;
238 }
239
240 uint8_t clear_mask = 0;
241
242 // 1) Bus-level recovery: reset/suspend must rebuild EP0 first.
243 // 1) 总线级恢复:reset/suspend 必须优先重建 EP0。
244 if (INTFLAG & USBHS_UIF_BUS_RST)
245 {
246 USBHSD->DEV_AD = 0;
247 usb->Deinit(true);
248 usb->Init(true);
249 ResetEp0State(map);
250 clear_mask |= USBHS_UIF_BUS_RST;
251 }
252
253 if (INTFLAG & USBHS_UIF_SUSPEND)
254 {
255 usb->Deinit(true);
256 usb->Init(true);
257 ResetEp0State(map);
258 clear_mask |= USBHS_UIF_SUSPEND;
259 }
260
261 // 2) Setup arbitration: consume the previous EP0 IN completion before the
262 // new setup resets the control-transfer state.
263 // 2) Setup 仲裁:在新 setup 重置控制传输状态之前,
264 // 先消费上一笔 EP0 IN 完成。
265 if (CompleteEp0InBeforeSetup(map, INTFLAG, INTST))
266 {
267 clear_mask |= USBHS_UIF_TRANSFER;
268 }
269
270 if (INTFLAG & USBHS_UIF_SETUP_ACT)
271 {
272 PrepareEp0ForSetup(map);
273
274 auto* out0 = map[0][OUT_IDX];
275 ASSERT(out0 != nullptr);
276 const auto* setup =
277 reinterpret_cast<const LibXR::USB::SetupPacket*>(out0->GetBuffer().addr_);
278 usb->OnSetupPacket(true, setup);
279 clear_mask |= USBHS_UIF_SETUP_ACT;
280 }
281
282 // 3) Ordinary token completion: non-setup transfers arrive here.
283 // 3) 普通 token 完成:非 setup 传输统一在这里分发。
284 if ((INTFLAG & USBHS_UIF_TRANSFER) && ((clear_mask & USBHS_UIF_TRANSFER) == 0u))
285 {
286 HandleTransferToken(map, INTST);
287 clear_mask |= USBHS_UIF_TRANSFER;
288 }
289
290 clear_mask |= static_cast<uint8_t>(INTFLAG & ~(clear_mask));
291 USBHSD->INT_FG = clear_mask;
292 }
293}
294
295CH32USBOtgHS::CH32USBOtgHS(
296 const std::initializer_list<CH32USBOtgHS::EPConfig> EP_CFGS, uint16_t vid,
297 uint16_t pid, uint16_t bcd,
298 const std::initializer_list<const USB::DescriptorStrings::LanguagePack*> LANG_LIST,
299 const std::initializer_list<const std::initializer_list<USB::ConfigDescriptorItem*>>
300 CONFIGS,
301 ConstRawData uid)
302 : USB::EndpointPool(EP_CFGS.size() * 2),
303 USB::DeviceCore(*this, USB::USBSpec::USB_2_1, USB::Speed::HIGH,
304 USB::DeviceDescriptor::PacketSize0::SIZE_64, vid, pid, bcd,
305 LANG_LIST, CONFIGS, uid)
306{
307 ASSERT(EP_CFGS.size() > 0 && EP_CFGS.size() <= CH32EndpointOtgHs::EP_OTG_HS_MAX_SIZE);
308
309 auto cfgs_itr = EP_CFGS.begin();
310
311 ASSERT(cfgs_itr->buffer_tx.size_ >= 64 && cfgs_itr->buffer_rx.size_ >= 64 &&
312 cfgs_itr->double_buffer == false);
313
314 auto ep0_out =
315 new CH32EndpointOtgHs(USB::Endpoint::EPNumber::EP0, USB::Endpoint::Direction::OUT,
316 cfgs_itr->buffer_rx, false);
317 auto ep0_in =
318 new CH32EndpointOtgHs(USB::Endpoint::EPNumber::EP0, USB::Endpoint::Direction::IN,
319 cfgs_itr->buffer_tx, false);
320
321 USB::EndpointPool::SetEndpoint0(ep0_in, ep0_out);
322
323 USB::Endpoint::EPNumber ep_index = USB::Endpoint::EPNumber::EP1;
324
325 for (++cfgs_itr, ep_index = USB::Endpoint::EPNumber::EP1; cfgs_itr != EP_CFGS.end();
326 ++cfgs_itr, ep_index = USB::Endpoint::NextEPNumber(ep_index))
327 {
328 if (!cfgs_itr->double_buffer)
329 {
330 auto ep_out = new CH32EndpointOtgHs(ep_index, USB::Endpoint::Direction::OUT,
331 cfgs_itr->buffer_rx, false);
332 USB::EndpointPool::Put(ep_out);
333
334 auto ep_in = new CH32EndpointOtgHs(ep_index, USB::Endpoint::Direction::IN,
335 cfgs_itr->buffer_tx, false);
336 USB::EndpointPool::Put(ep_in);
337 }
338 else
339 {
340 auto ep = new CH32EndpointOtgHs(
341 ep_index,
342 cfgs_itr->is_in ? USB::Endpoint::Direction::IN : USB::Endpoint::Direction::OUT,
343 cfgs_itr->buffer_tx, true);
344 USB::EndpointPool::Put(ep);
345 }
346 }
347}
348
349LibXR::ErrorCode CH32USBOtgHS::SetAddress(uint8_t address,
351{
352 if (context == USB::DeviceCore::Context::STATUS_IN_COMPLETE)
353 {
354 USBHSD->DEV_AD = address;
355 }
357}
358
359void CH32USBOtgHS::Start(bool)
360{
361 // OTGHS selects the shared 48 MHz source first, then enables its own bus clock.
362 // OTGHS 先选择共享 48 MHz 时钟源,再打开 USBHS 自己的总线时钟。
363 LibXR::CH32UsbRcc::ConfigureUsb48M();
364#if defined(RCC_AHBPeriph_USBHS)
365 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_USBHS, ENABLE);
366#endif
367 USBHSD->CONTROL = USBHS_UC_CLR_ALL | USBHS_UC_RESET_SIE;
368 USBHSD->CONTROL &= ~USBHS_UC_RESET_SIE;
369 USBHSD->HOST_CTRL = USBHS_UH_PHY_SUSPENDM;
370 USBHSD->CONTROL = USBHS_UC_DMA_EN | USBHS_UC_INT_BUSY | USBHS_UC_SPEED_HIGH;
371 USBHSD->INT_EN =
372 USBHS_UIE_SETUP_ACT | USBHS_UIE_TRANSFER | USBHS_UIE_DETECT | USBHS_UIE_SUSPEND;
373 USBHSD->CONTROL |= USBHS_UC_DEV_PU_EN;
374 NVIC_EnableIRQ(USBHS_IRQn);
375 self_ = this;
376}
377
378void CH32USBOtgHS::Stop(bool)
379{
380 USBHSD->CONTROL = USBHS_UC_CLR_ALL | USBHS_UC_RESET_SIE;
381 USBHSD->CONTROL = 0;
382 NVIC_DisableIRQ(USBHS_IRQn);
383 self_ = nullptr;
384}
385
386#endif // defined(USBHSD)
387
388// NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast,performance-no-int-to-ptr)
只读原始数据视图 / Immutable raw data view
USB 设备协议栈核心:EP0 控制传输、描述符、配置、标准/类/厂商请求 USB device core: EP0 control transfer, descriptors,...
Definition dev_core.hpp:22
Context
控制传输上下文 / Control transfer context
Definition dev_core.hpp:31
USB描述符基类 USB descriptor base class.
Definition desc_dev.hpp:40
EPNumber
端点号 Endpoint number
Definition ep.hpp:42
@ IN
输入方向 / IN direction
@ OUT
输出方向 / OUT direction
static constexpr EPNumber NextEPNumber(EPNumber ep)
获取下一个端点号 / Get the next endpoint number
Definition ep.hpp:128
USB端点池类 / USB endpoint pool class.
Definition ep_pool.hpp:23
void SetEndpoint0(Endpoint *ep0_in, Endpoint *ep0_out)
设置端点0的IN/OUT对象 / Set Endpoint 0 IN/OUT objects
Definition ep_pool.cpp:96
LibXR 命名空间
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举
@ OK
操作成功 | Operation successful
USB 标准请求 SETUP 包(固定8字节) Standard USB setup packet (8 bytes)
Definition core.hpp:58