libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
ch32_usb_otgfs.cpp
1// NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast,performance-no-int-to-ptr)
2// ch32_usb_otgfs.cpp (OTG FS)
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(USBFSD)
12
13namespace
14{
15
16constexpr uint8_t OTG_FS_CLEARABLE_MASK = USBFS_UIF_FIFO_OV | USBFS_UIF_HST_SOF |
17 USBFS_UIF_SUSPEND | USBFS_UIF_TRANSFER |
18 USBFS_UIF_DETECT | USBFS_UIF_BUS_RST;
19
20static void ClearPendingOtgFsInterrupts()
21{
22 while (true)
23 {
24 const uint16_t INTFGST = *reinterpret_cast<volatile uint16_t*>(
25 reinterpret_cast<uintptr_t>(&USBFSD->INT_FG));
26 const uint8_t INTFLAG = static_cast<uint8_t>(INTFGST & 0x00FFu);
27 const uint8_t PENDING = static_cast<uint8_t>(INTFLAG & OTG_FS_CLEARABLE_MASK);
28 if (PENDING == 0u)
29 {
30 break;
31 }
32 USBFSD->INT_FG = PENDING;
33
34 // This loop only drains the pending bits visible in the current INT_FG
35 // snapshot. Any later host event will relatch a fresh interrupt and be
36 // handled by the next IRQ entry.
37 // 这个循环只清当前 INT_FG 快照里可见的 pending 位;主机后续的新事件
38 // 会重新锁存成新的中断,并在下一次 IRQ 进入时处理。
39 }
40}
41
42} // namespace
43
44// NOLINTNEXTLINE(readability-identifier-naming)
45extern "C" __attribute__((interrupt("WCH-Interrupt-fast"))) void USBFS_IRQHandler(void)
46{
47 auto* usb = LibXR::CH32USBOtgFS::self_;
48 if (usb == nullptr || !usb->IsInited())
49 {
50 ClearPendingOtgFsInterrupts();
51 return;
52 }
53
54 auto& map = LibXR::CH32EndpointOtgFs::map_otg_fs_;
55
56 constexpr uint8_t OUT_IDX = static_cast<uint8_t>(LibXR::USB::Endpoint::Direction::OUT);
57 constexpr uint8_t IN_IDX = static_cast<uint8_t>(LibXR::USB::Endpoint::Direction::IN);
58 auto* out0 = map[0][OUT_IDX];
59 auto* in0 = map[0][IN_IDX];
60 ASSERT(out0 != nullptr);
61 ASSERT(in0 != nullptr);
62
63 // Handle order matches the control-transfer lifecycle:
64 // 1) bus-level recovery
65 // 2) token completion / setup dispatch
66 // 处理顺序与控制传输生命周期保持一致:
67 // 1) 总线级恢复
68 // 2) token 完成与 setup 分发
69 while (true)
70 {
71 const uint16_t INTFGST = *reinterpret_cast<volatile uint16_t*>(
72 reinterpret_cast<uintptr_t>(&USBFSD->INT_FG));
73
74 const uint8_t INTFLAG = static_cast<uint8_t>(INTFGST & 0x00FFu);
75 const uint8_t INTST = static_cast<uint8_t>((INTFGST >> 8) & 0x00FFu);
76
77 const uint8_t PENDING = static_cast<uint8_t>(INTFLAG & OTG_FS_CLEARABLE_MASK);
78 if (PENDING == 0)
79 {
80 break;
81 }
82
83 uint8_t clear_mask = 0;
84
85 // Reset rebuilds EP0 state and returns the device to "waiting for setup".
86 // reset 会重建 EP0 状态,并把设备恢复到“等待 setup”的初始形态。
87 if (PENDING & USBFS_UIF_BUS_RST)
88 {
89 USBFSD->DEV_ADDR = 0;
90
91 usb->Deinit(true);
92 usb->Init(true);
93
96 out0->tog_ = true;
97 in0->tog_ = true;
98
99 USBFSD->UEP0_TX_CTRL = USBFS_UEP_T_RES_NAK;
100 USBFSD->UEP0_RX_CTRL = USBFS_UEP_R_RES_NAK;
101
102 clear_mask |= USBFS_UIF_BUS_RST;
103 }
104
105 // Suspend follows the same EP0 recovery path; resume is observed later by the host.
106 // suspend 走与 reset 相同的 EP0 恢复路径;resume 由后续主机时序体现。
107 if (PENDING & USBFS_UIF_SUSPEND)
108 {
109 usb->Deinit(true);
110 usb->Init(true);
111
112 out0->SetState(LibXR::USB::Endpoint::State::IDLE);
114 out0->tog_ = true;
115 in0->tog_ = true;
116
117 USBFSD->UEP0_TX_CTRL = USBFS_UEP_T_RES_NAK;
118 USBFSD->UEP0_RX_CTRL = USBFS_UEP_R_RES_NAK;
119
120 clear_mask |= USBFS_UIF_SUSPEND;
121 }
122
123 if (PENDING & USBFS_UIF_TRANSFER)
124 {
125 const uint8_t TOKEN = INTST & USBFS_UIS_TOKEN_MASK;
126 const uint8_t EPNUM = INTST & USBFS_UIS_ENDP_MASK;
127
128 auto& ep = map[EPNUM];
129
130 switch (TOKEN)
131 {
132 case USBFS_UIS_TOKEN_SETUP:
133 {
134 // A fresh setup cancels the previous EP0 transaction, so both directions are
135 // reset to IDLE/TOG0 before handing the setup packet to DeviceCore.
136 // 新的 setup 会中断前一笔 EP0 事务,因此这里在把 setup 包交给 DeviceCore
137 // 之前,先把 EP0 的双向状态恢复到 IDLE/TOG0。
138 USBFSD->UEP0_TX_CTRL = USBFS_UEP_T_RES_NAK;
139 USBFSD->UEP0_RX_CTRL = USBFS_UEP_R_RES_NAK;
140
141 out0->SetState(LibXR::USB::Endpoint::State::IDLE);
143 out0->tog_ = true;
144 in0->tog_ = true;
145
146 usb->OnSetupPacket(
147 true, reinterpret_cast<const SetupPacket*>(out0->GetBuffer().addr_));
148 break;
149 }
150
151 case USBFS_UIS_TOKEN_OUT:
152 {
153 // OTGFS hardware already reports the completed RX length for this token.
154 // OTGFS 硬件已经给出了本次 token 的完成 RX 长度。
155 const uint16_t LEN = USBFSD->RX_LEN;
156 if (ep[OUT_IDX])
157 {
158 ep[OUT_IDX]->TransferComplete(LEN);
159 }
160 break;
161 }
162
163 case USBFS_UIS_TOKEN_IN:
164 {
165 // IN token completion has no payload length; completion itself is enough.
166 // IN token 完成不需要额外 payload 长度,事件本身就足够了。
167 if (ep[IN_IDX])
168 {
169 ep[IN_IDX]->TransferComplete(0);
170 }
171 break;
172 }
173
174 default:
175 break;
176 }
177
178 clear_mask |= USBFS_UIF_TRANSFER;
179 }
180
181 clear_mask |= static_cast<uint8_t>(PENDING & ~clear_mask);
182 USBFSD->INT_FG = clear_mask;
183 }
184}
185
186CH32USBOtgFS::CH32USBOtgFS(
187 const std::initializer_list<EPConfig> EP_CFGS,
188 USB::DeviceDescriptor::PacketSize0 packet_size, uint16_t vid, uint16_t pid,
189 uint16_t bcd,
190 const std::initializer_list<const USB::DescriptorStrings::LanguagePack*> LANG_LIST,
191 const std::initializer_list<const std::initializer_list<USB::ConfigDescriptorItem*>>
192 CONFIGS,
193 ConstRawData uid)
194 : USB::EndpointPool(EP_CFGS.size() * 2),
195 USB::DeviceCore(*this, USB::USBSpec::USB_2_1, USB::Speed::FULL, packet_size, vid,
196 pid, bcd, LANG_LIST, CONFIGS, uid)
197{
198 ASSERT(EP_CFGS.size() > 0 && EP_CFGS.size() <= CH32EndpointOtgFs::EP_OTG_FS_MAX_SIZE);
199
200 auto cfgs_itr = EP_CFGS.begin();
201
202 auto ep0_out =
203 new CH32EndpointOtgFs(USB::Endpoint::EPNumber::EP0, USB::Endpoint::Direction::OUT,
204 cfgs_itr->buffer, false);
205 auto ep0_in =
206 new CH32EndpointOtgFs(USB::Endpoint::EPNumber::EP0, USB::Endpoint::Direction::IN,
207 cfgs_itr->buffer, false);
208
209 USB::EndpointPool::SetEndpoint0(ep0_in, ep0_out);
210
211 USB::Endpoint::EPNumber ep_index = USB::Endpoint::EPNumber::EP1;
212
213 for (++cfgs_itr, ep_index = USB::Endpoint::EPNumber::EP1; cfgs_itr != EP_CFGS.end();
214 ++cfgs_itr, ep_index = USB::Endpoint::NextEPNumber(ep_index))
215 {
216 if (cfgs_itr->is_in == -1)
217 {
218 auto ep_out = new CH32EndpointOtgFs(ep_index, USB::Endpoint::Direction::OUT,
219 cfgs_itr->buffer, false);
220 USB::EndpointPool::Put(ep_out);
221
222 auto ep_in = new CH32EndpointOtgFs(ep_index, USB::Endpoint::Direction::IN,
223 cfgs_itr->buffer, false);
224 USB::EndpointPool::Put(ep_in);
225 }
226 else
227 {
228 auto ep = new CH32EndpointOtgFs(
229 ep_index,
230 cfgs_itr->is_in ? USB::Endpoint::Direction::IN : USB::Endpoint::Direction::OUT,
231 cfgs_itr->buffer, true);
232 USB::EndpointPool::Put(ep);
233 }
234 }
235}
236
237LibXR::ErrorCode CH32USBOtgFS::SetAddress(uint8_t address,
239{
240 if (context == USB::DeviceCore::Context::STATUS_IN_COMPLETE)
241 {
242 USBFSD->DEV_ADDR = (USBFSD->DEV_ADDR & USBFS_UDA_GP_BIT) | address;
243 USBFSD->UEP0_TX_CTRL = USBFS_UEP_T_RES_NAK;
244 USBFSD->UEP0_RX_CTRL = USBFS_UEP_R_RES_ACK;
245 }
247}
248
249void CH32USBOtgFS::Start(bool)
250{
251 // OTGFS uses the same shared USB 48 MHz clock selection as FSDEV.
252 // OTGFS 与 FSDEV 共用同一套 USB 48 MHz 时钟选择规则。
253 LibXR::CH32UsbRcc::ConfigureUsb48M();
254#if defined(RCC_USBCLK48MCLKSource_USBPHY) && defined(RCC_AHBPeriph_USBHS)
255 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_USBHS, ENABLE);
256#endif
257#if defined(RCC_AHBPeriph_USBFS)
258 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_USBFS, ENABLE);
259#elif defined(RCC_AHBPeriph_USBOTGFS)
260 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_USBOTGFS, ENABLE);
261#endif
262 USBFSH->BASE_CTRL = USBFS_UC_RESET_SIE | USBFS_UC_CLR_ALL;
263 USBFSH->BASE_CTRL = 0x00;
264 USBFSD->INT_EN = USBFS_UIE_SUSPEND | USBFS_UIE_BUS_RST | USBFS_UIE_TRANSFER;
265 USBFSD->BASE_CTRL = USBFS_UC_DEV_PU_EN | USBFS_UC_INT_BUSY | USBFS_UC_DMA_EN;
266 USBFSD->UDEV_CTRL = USBFS_UD_PD_DIS | USBFS_UD_PORT_EN;
267 NVIC_EnableIRQ(USBFS_IRQn);
268 self_ = this;
269}
270
271void CH32USBOtgFS::Stop(bool)
272{
273 USBFSH->BASE_CTRL = USBFS_UC_RESET_SIE | USBFS_UC_CLR_ALL;
274 USBFSD->BASE_CTRL = 0x00;
275 NVIC_DisableIRQ(USBFS_IRQn);
276 self_ = nullptr;
277}
278
279#endif // defined(USBFSD)
280
281// 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
PacketSize0
控制端点0最大包长度枚举 Packet size for endpoint 0 (bMaxPacketSize0)
Definition desc_dev.hpp:75
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
定义错误码枚举
@ FULL
已满 | Full
@ PENDING
等待中 | Pending
@ OK
操作成功 | Operation successful
USB 标准请求 SETUP 包(固定8字节) Standard USB setup packet (8 bytes)
Definition core.hpp:58