libxr
Embedded C++ framework for bare-metal, RTOS and Linux
Loading...
Searching...
No Matches
ch32_usb_endpoint_devfs.cpp
1// NOLINTBEGIN(cppcoreguidelines-pro-type-cstyle-cast,performance-no-int-to-ptr)
2// ch32_usb_endpoint_dev_fs.cpp
3#include <cstdint>
4#include <cstring>
5
6#include "ch32_usb_endpoint.hpp"
7#include "ch32_usbcan_shared.hpp"
8#include "ep.hpp"
9
10using namespace LibXR;
11
12#if defined(RCC_APB1Periph_USB)
13
14#ifdef USB_BASE
15static constexpr uintptr_t REG_BASE = USB_BASE;
16#else
17static constexpr uintptr_t REG_BASE = 0x40005C00UL;
18#endif
19
20#ifdef PMAAddr
21static constexpr uintptr_t PMA_BASE = PMAAddr;
22#else
23static constexpr uintptr_t PMA_BASE = 0x40006000UL;
24#endif
25
26static inline uintptr_t pma_phys(uint16_t pma_off_bytes)
27{
28 return PMA_BASE + (static_cast<uintptr_t>(pma_off_bytes) << 1);
29}
30
31static inline volatile uint16_t* btable()
32{
33 return reinterpret_cast<volatile uint16_t*>(REG_BASE + 0x50U);
34}
35
36static inline volatile uint16_t* ep_reg(uint8_t ep)
37{
38 return reinterpret_cast<volatile uint16_t*>(REG_BASE +
39 (static_cast<uintptr_t>(ep) * 4U));
40}
41
42#if !defined(USB_EP_CTR_RX) && defined(EP_CTR_RX)
43#define USB_EP_CTR_RX EP_CTR_RX
44#endif
45#if !defined(USB_EP_DTOG_RX) && defined(EP_DTOG_RX)
46#define USB_EP_DTOG_RX EP_DTOG_RX
47#endif
48#if !defined(USB_EPRX_STAT) && defined(EPRX_STAT)
49#define USB_EPRX_STAT EPRX_STAT
50#endif
51#if !defined(USB_EP_SETUP) && defined(EP_SETUP)
52#define USB_EP_SETUP EP_SETUP
53#endif
54#if !defined(USB_EP_T_FIELD) && defined(EP_T_FIELD)
55#define USB_EP_T_FIELD EP_T_FIELD
56#endif
57#if !defined(USB_EP_KIND) && defined(EP_KIND)
58#define USB_EP_KIND EP_KIND
59#endif
60#if !defined(USB_EP_CTR_TX) && defined(EP_CTR_TX)
61#define USB_EP_CTR_TX EP_CTR_TX
62#endif
63#if !defined(USB_EP_DTOG_TX) && defined(EP_DTOG_TX)
64#define USB_EP_DTOG_TX EP_DTOG_TX
65#endif
66#if !defined(USB_EPTX_STAT) && defined(EPTX_STAT)
67#define USB_EPTX_STAT EPTX_STAT
68#endif
69#if !defined(USB_EPADDR_FIELD) && defined(EPADDR_FIELD)
70#define USB_EPADDR_FIELD EPADDR_FIELD
71#endif
72
73#ifndef USB_EP_CTR_RX
74#define USB_EP_CTR_RX ((uint16_t)0x8000U)
75#endif
76#ifndef USB_EP_DTOG_RX
77#define USB_EP_DTOG_RX ((uint16_t)0x4000U)
78#endif
79#ifndef USB_EPRX_STAT
80#define USB_EPRX_STAT ((uint16_t)0x3000U)
81#endif
82#ifndef USB_EP_SETUP
83#define USB_EP_SETUP ((uint16_t)0x0800U)
84#endif
85#ifndef USB_EP_T_FIELD
86#define USB_EP_T_FIELD ((uint16_t)0x0600U)
87#endif
88#ifndef USB_EP_KIND
89#define USB_EP_KIND ((uint16_t)0x0100U)
90#endif
91#ifndef USB_EP_CTR_TX
92#define USB_EP_CTR_TX ((uint16_t)0x0080U)
93#endif
94#ifndef USB_EP_DTOG_TX
95#define USB_EP_DTOG_TX ((uint16_t)0x0040U)
96#endif
97#ifndef USB_EPTX_STAT
98#define USB_EPTX_STAT ((uint16_t)0x0030U)
99#endif
100#ifndef USB_EPADDR_FIELD
101#define USB_EPADDR_FIELD ((uint16_t)0x000FU)
102#endif
103
104#ifndef USB_EP_CONTROL
105#define USB_EP_CONTROL ((uint16_t)0x0200U)
106#endif
107#ifndef USB_EP_BULK
108#define USB_EP_BULK ((uint16_t)0x0000U)
109#endif
110#ifndef USB_EP_INTERRUPT
111#define USB_EP_INTERRUPT ((uint16_t)0x0600U)
112#endif
113#ifndef USB_EP_ISOCHRONOUS
114#define USB_EP_ISOCHRONOUS ((uint16_t)0x0400U)
115#endif
116
117#ifndef USB_EP_TX_DIS
118#define USB_EP_TX_DIS ((uint16_t)0x0000U)
119#endif
120#ifndef USB_EP_TX_STALL
121#define USB_EP_TX_STALL ((uint16_t)0x0010U)
122#endif
123#ifndef USB_EP_TX_NAK
124#define USB_EP_TX_NAK ((uint16_t)0x0020U)
125#endif
126#ifndef USB_EP_TX_VALID
127#define USB_EP_TX_VALID ((uint16_t)0x0030U)
128#endif
129
130#ifndef USB_EP_RX_DIS
131#define USB_EP_RX_DIS ((uint16_t)0x0000U)
132#endif
133#ifndef USB_EP_RX_STALL
134#define USB_EP_RX_STALL ((uint16_t)0x1000U)
135#endif
136#ifndef USB_EP_RX_NAK
137#define USB_EP_RX_NAK ((uint16_t)0x2000U)
138#endif
139#ifndef USB_EP_RX_VALID
140#define USB_EP_RX_VALID ((uint16_t)0x3000U)
141#endif
142
143#ifndef USB_EPREG_MASK
144#define USB_EPREG_MASK \
145 (USB_EP_CTR_RX | USB_EP_SETUP | USB_EP_T_FIELD | USB_EP_KIND | USB_EP_CTR_TX | \
146 USB_EPADDR_FIELD)
147#endif
148
149#ifndef USB_EPTX_DTOGMASK
150#define USB_EPTX_DTOGMASK (USB_EPTX_STAT | USB_EPREG_MASK)
151#endif
152#ifndef USB_EPRX_DTOGMASK
153#define USB_EPRX_DTOGMASK (USB_EPRX_STAT | USB_EPREG_MASK)
154#endif
155
156struct BTableEntry
157{
158 uint16_t addr_tx;
159 uint16_t _r0;
160 uint16_t count_tx;
161 uint16_t _r1;
162 uint16_t addr_rx;
163 uint16_t _r2;
164 uint16_t count_rx;
165 uint16_t _r3;
166};
167
168static inline volatile BTableEntry* btable_entries()
169{
170 const uint16_t BTABLE_OFF = static_cast<uint16_t>((*btable()) & 0xFFF8U);
171 return reinterpret_cast<volatile BTableEntry*>(pma_phys(BTABLE_OFF));
172}
173
174static inline void pma_write(uint16_t pma_offset, const void* src, size_t len)
175{
176 const uint8_t* s = reinterpret_cast<const uint8_t*>(src);
177 volatile uint16_t* p = reinterpret_cast<volatile uint16_t*>(pma_phys(pma_offset));
178
179 for (size_t i = 0; i < len; i += 2)
180 {
181 uint16_t w = s[i];
182 if (i + 1 < len)
183 {
184 w |= static_cast<uint16_t>(s[i + 1]) << 8;
185 }
186 *p = w;
187 p += 2; // stride=2 on CH32 FSDEV PMA
188 }
189}
190
191static inline void pma_read(void* dst, uint16_t pma_offset, size_t len)
192{
193 uint8_t* d = reinterpret_cast<uint8_t*>(dst);
194 const volatile uint16_t* p =
195 reinterpret_cast<const volatile uint16_t*>(pma_phys(pma_offset));
196
197 for (size_t i = 0; i < len; i += 2)
198 {
199 const uint16_t W = *p;
200 p += 2;
201
202 d[i] = static_cast<uint8_t>(W & 0xFFU);
203 if (i + 1 < len)
204 {
205 d[i + 1] = static_cast<uint8_t>((W >> 8) & 0xFFU);
206 }
207 }
208}
209
210static inline void set_tx_status(uint8_t ep, uint16_t desired_stat)
211{
212 const uint16_t CUR = *ep_reg(ep);
213 uint16_t reg = static_cast<uint16_t>(CUR & USB_EPREG_MASK);
214
215 // FSDEV EPxR STAT bits are write-1-to-toggle; write the delta to reach target.
216 const uint16_t TARGET = static_cast<uint16_t>(desired_stat & USB_EPTX_STAT);
217 reg |= static_cast<uint16_t>((CUR ^ TARGET) & USB_EPTX_STAT);
218 reg |= static_cast<uint16_t>(USB_EP_CTR_RX | USB_EP_CTR_TX);
219 *ep_reg(ep) = reg;
220}
221
222static inline void set_rx_status(uint8_t ep, uint16_t desired_stat)
223{
224 const uint16_t CUR = *ep_reg(ep);
225 uint16_t reg = static_cast<uint16_t>(CUR & USB_EPREG_MASK);
226
227 // FSDEV EPxR STAT bits are write-1-to-toggle; write the delta to reach target.
228 const uint16_t TARGET = static_cast<uint16_t>(desired_stat & USB_EPRX_STAT);
229 reg |= static_cast<uint16_t>((CUR ^ TARGET) & USB_EPRX_STAT);
230 reg |= static_cast<uint16_t>(USB_EP_CTR_RX | USB_EP_CTR_TX);
231 *ep_reg(ep) = reg;
232}
233
234static inline void clear_ctr_tx(uint8_t ep)
235{
236 uint16_t reg = static_cast<uint16_t>((*ep_reg(ep)) & USB_EPREG_MASK);
237 reg |= static_cast<uint16_t>(USB_EP_CTR_TX | USB_EP_CTR_RX);
238 reg = static_cast<uint16_t>(reg & ~USB_EP_CTR_TX);
239 *ep_reg(ep) = reg;
240}
241
242static inline void clear_ctr_rx(uint8_t ep)
243{
244 uint16_t reg = static_cast<uint16_t>((*ep_reg(ep)) & USB_EPREG_MASK);
245 reg |= static_cast<uint16_t>(USB_EP_CTR_TX | USB_EP_CTR_RX);
246 reg = static_cast<uint16_t>(reg & ~USB_EP_CTR_RX);
247 *ep_reg(ep) = reg;
248}
249
250static inline uint16_t get_rx_count_from_btable(uint8_t ep)
251{
252 return static_cast<uint16_t>(btable_entries()[ep].count_rx & 0x03FFU);
253}
254
255static inline uint16_t encode_rx_count(uint16_t mps)
256{
257 if (mps <= 62)
258 {
259 const uint16_t BLOCKS = static_cast<uint16_t>((mps + 1U) / 2U);
260 return static_cast<uint16_t>(BLOCKS << 10);
261 }
262
263 const uint16_t BLOCKS = static_cast<uint16_t>((mps + 31U) / 32U);
264 return static_cast<uint16_t>(0x8000U | ((BLOCKS - 1U) << 10));
265}
266
267static constexpr uint16_t PMA_ALLOC_BASE =
268 static_cast<uint16_t>(CH32EndpointDevFs::EP_DEV_FS_MAX_SIZE) * 8U;
269static uint16_t g_pma_next = PMA_ALLOC_BASE;
270static uint16_t g_pma_limit = LibXR::CH32UsbCanShared::USBD_PMA_BYTES_SOLO;
271
272void CH32EndpointDevFs::ResetPMAAllocator()
273{
274 const uint16_t LIMIT_BYTES = static_cast<uint16_t>(
275 LibXR::CH32UsbCanShared::usb_pma_limit_bytes() & static_cast<uint16_t>(~1U));
276 g_pma_limit = LIMIT_BYTES;
277
278 g_pma_next = PMA_ALLOC_BASE;
279 g_pma_next = static_cast<uint16_t>((g_pma_next + 1U) & ~1U);
280 ASSERT(g_pma_next <= g_pma_limit);
281}
282
283static inline uint16_t alloc_pma(size_t bytes)
284{
285 // 首次 USB reset 前也可能分配端点,必须立即使用当前 CAN 预留后的上限。
286 // Init may allocate endpoints before the first USB bus reset.
287 g_pma_limit = LibXR::CH32UsbCanShared::usb_pma_limit_bytes();
288 // PMA 最大只有 512 字节;端点缓冲区配置不得超过它(Debug 断言)。
289 // PMA is at most 512 bytes; the endpoint buffer configuration must fit in it
290 // (Debug assertion).
291 ASSERT(bytes <= g_pma_limit);
292
293 const uint16_t ADDR = g_pma_next;
294 const uint32_t INC = (static_cast<uint32_t>(bytes) + 1U) & ~1U;
295 const uint32_t END = static_cast<uint32_t>(ADDR) + INC;
296 ASSERT(END <= g_pma_limit);
297
298 LibXR::CH32UsbCanShared::usb_pma_configured.store(true, std::memory_order_release);
299 g_pma_next = static_cast<uint16_t>(END);
300 return ADDR;
301}
302
303CH32EndpointDevFs::CH32EndpointDevFs(EPNumber ep_num, Direction dir,
304 LibXR::RawData buffer, bool is_isochronous)
305 : Endpoint(ep_num, dir, buffer), is_isochronous_(is_isochronous)
306{
307 const uint8_t EP_I = static_cast<uint8_t>(EPNumberToInt8(GetNumber()));
308 map_dev_fs_[EP_I][static_cast<uint8_t>(dir)] = this;
309}
310
311void CH32EndpointDevFs::Configure(const Config& cfg)
312{
313 ASSERT(cfg.direction == Direction::IN || cfg.direction == Direction::OUT);
314 ASSERT(cfg.direction == GetDirection());
315
316 auto& ep_cfg = GetConfig();
317 ep_cfg = cfg;
318
319 const uint8_t EP_I = static_cast<uint8_t>(EPNumberToInt8(GetNumber()));
320
321 size_t packet_size_limit = 64;
322 if (cfg.type == Type::ISOCHRONOUS)
323 {
324 packet_size_limit = 1023;
325 }
326
327 const auto BUF = GetBuffer();
328 if (packet_size_limit > BUF.size_)
329 {
330 packet_size_limit = BUF.size_;
331 }
332
333 size_t max_packet_size = cfg.max_packet_size;
334 if (max_packet_size > packet_size_limit)
335 {
336 max_packet_size = packet_size_limit;
337 }
338 if (max_packet_size < 8)
339 {
340 max_packet_size = 8;
341 }
342 if (max_packet_size > BUF.size_)
343 {
344 max_packet_size = BUF.size_;
345 }
346
347 ep_cfg.max_packet_size = static_cast<uint16_t>(max_packet_size);
348
349 if (pma_addr_ == 0 || pma_addr_ < PMA_ALLOC_BASE)
350 {
351 const uint16_t ADDR = alloc_pma(BUF.size_);
352 ASSERT(ADDR >= PMA_ALLOC_BASE);
353 pma_addr_ = ADDR;
354 }
355
356 volatile BTableEntry* bt = btable_entries();
357
359 {
360 bt[EP_I].addr_tx = pma_addr_;
361 bt[EP_I].count_tx = 0;
362 }
363 else
364 {
365 bt[EP_I].addr_rx = pma_addr_;
366 bt[EP_I].count_rx = encode_rx_count(ep_cfg.max_packet_size);
367 }
368
369 uint16_t epr = static_cast<uint16_t>((*ep_reg(EP_I)) & USB_EPREG_MASK);
370
371 epr = static_cast<uint16_t>((epr & ~USB_EPADDR_FIELD) | (EP_I & 0x0FU));
372 epr = static_cast<uint16_t>(epr & ~USB_EP_T_FIELD);
373
374 switch (ep_cfg.type)
375 {
376 case Type::CONTROL:
377 epr |= USB_EP_CONTROL;
378 break;
379 case Type::BULK:
380 epr |= USB_EP_BULK;
381 break;
382 case Type::INTERRUPT:
383 epr |= USB_EP_INTERRUPT;
384 break;
386 epr |= USB_EP_ISOCHRONOUS;
387 break;
388 default:
389 epr |= USB_EP_BULK;
390 break;
391 }
392
393 *ep_reg(EP_I) = epr;
394
396 {
397 set_tx_status(EP_I, USB_EP_TX_NAK);
398 }
399 else
400 {
401 set_rx_status(EP_I, USB_EP_RX_NAK);
402 }
403
405}
406
408{
409 const uint8_t EP_I = static_cast<uint8_t>(EPNumberToInt8(GetNumber()));
411 {
412 set_tx_status(EP_I, USB_EP_TX_DIS);
413 }
414 else
415 {
416 set_rx_status(EP_I, USB_EP_RX_DIS);
417 }
418}
419
421{
422 if (size > GetBuffer().size_)
423 {
425 }
426
428
429 const uint8_t EP_I = static_cast<uint8_t>(EPNumberToInt8(GetNumber()));
430 last_transfer_size_ = size;
431
433 {
434 auto buffer = GetBuffer();
435 pma_write(pma_addr_, buffer.addr_, size);
436
437 // Keep the current transfer on the old active block and switch to the next block
438 // for producer writes, matching STM32/HAL timing.
439 if (UseDoubleBuffer() && size > 0)
440 {
441 Endpoint::SwitchBuffer();
442 }
443
444 btable_entries()[EP_I].count_tx = static_cast<uint16_t>(size);
445 set_tx_status(EP_I, USB_EP_TX_VALID);
446 }
447 else
448 {
449 volatile BTableEntry* bt = btable_entries();
450 bt[EP_I].addr_rx = pma_addr_;
451 bt[EP_I].count_rx = encode_rx_count(GetConfig().max_packet_size);
452 set_rx_status(EP_I, USB_EP_RX_VALID);
453 }
454
455 return ErrorCode::OK;
456}
457
458void CH32EndpointDevFs::CopyRxDataToBuffer(size_t size)
459{
460 if (size > GetBuffer().size_)
461 {
462 size = GetBuffer().size_;
463 }
464 pma_read(GetBuffer().addr_, pma_addr_, size);
465}
466
467void CH32EndpointDevFs::TransferComplete(size_t size)
468{
469 const uint8_t EP_I = static_cast<uint8_t>(EPNumberToInt8(GetNumber()));
470
472 {
473 const uint16_t RX_CNT = get_rx_count_from_btable(EP_I);
474 size_t n = RX_CNT;
475 if (n > GetBuffer().size_)
476 {
477 n = GetBuffer().size_;
478 }
479 pma_read(GetBuffer().addr_, pma_addr_, n);
480 size = n;
481 set_rx_status(EP_I, USB_EP_RX_NAK);
482 }
483 else
484 {
485 set_tx_status(EP_I, USB_EP_TX_NAK);
486 size = last_transfer_size_;
487 }
488
489 OnTransferCompleteCallback(true, size);
490}
491
493{
494 const uint8_t EP_I = static_cast<uint8_t>(EPNumberToInt8(GetNumber()));
496 {
497 set_tx_status(EP_I, USB_EP_TX_STALL);
498 }
499 else
500 {
501 set_rx_status(EP_I, USB_EP_RX_STALL);
502 }
503 return ErrorCode::OK;
504}
505
507{
508 const uint8_t EP_I = static_cast<uint8_t>(EPNumberToInt8(GetNumber()));
510 {
511 set_tx_status(EP_I, USB_EP_TX_NAK);
512 }
513 else
514 {
515 set_rx_status(EP_I, USB_EP_RX_NAK);
516 }
517 return ErrorCode::OK;
518}
519
520void CH32EndpointDevFs::SwitchBuffer() { Endpoint::SwitchBuffer(); }
521
522void CH32EndpointDevFs::SetEpTxStatus(uint8_t ep, uint16_t status)
523{
524 set_tx_status(ep, status);
525}
526void CH32EndpointDevFs::SetEpRxStatus(uint8_t ep, uint16_t status)
527{
528 set_rx_status(ep, status);
529}
530void CH32EndpointDevFs::ClearEpCtrTx(uint8_t ep) { clear_ctr_tx(ep); }
531void CH32EndpointDevFs::ClearEpCtrRx(uint8_t ep) { clear_ctr_rx(ep); }
532uint16_t CH32EndpointDevFs::GetRxCount(uint8_t ep)
533{
534 return get_rx_count_from_btable(ep);
535}
536
537#endif // defined(RCC_APB1Periph_USB)
538
539// NOLINTEND(cppcoreguidelines-pro-type-cstyle-cast,performance-no-int-to-ptr)
ErrorCode Transfer(size_t size) override
启动一次传输 / Start a transfer
void SwitchBuffer() override
切换双缓冲 / Switch double buffer
ErrorCode ClearStall() override
清除 STALL / Clear stall
ErrorCode Stall() override
置 STALL / Stall endpoint
void Configure(const Config &cfg) override
配置端点协议参数 / Configure endpoint protocol parameters
void Close() override
关闭端点 / Close endpoint
可写原始数据视图 / Mutable raw data view
size_t size_
数据字节数 / Data size in bytes
void * addr_
数据起始地址 / Data start address
static constexpr uint8_t EPNumberToInt8(EPNumber ep)
端点号转换为 uint8_t / Convert endpoint number to uint8_t
Definition ep.hpp:93
@ IN
输入方向 / IN direction
@ OUT
输出方向 / OUT direction
Direction GetDirection() const
获取当前端点方向 / Get current endpoint direction
Definition ep.hpp:181
bool UseDoubleBuffer() const
是否启用双缓冲 / Whether double buffer is enabled
Definition ep.hpp:239
Config & GetConfig()
获取当前配置引用 / Get endpoint config reference
Definition ep.hpp:515
void OnTransferCompleteCallback(bool in_isr, size_t actual_transfer_size)
由底层在传输完成时调用 / Called by low-level driver when transfer completes
Definition ep.hpp:392
@ ISOCHRONOUS
等时端点 / Isochronous
@ BULK
批量端点 / Bulk
@ INTERRUPT
中断端点 / Interrupt
@ CONTROL
控制端点 / Control
EPNumber GetNumber() const
获取端点号 / Get endpoint number
Definition ep.hpp:169
void SetState(State state)
设置端点状态 / Set endpoint state
Definition ep.hpp:213
RawData GetBuffer() const
获取当前可用于传输的缓冲区 / Get current transfer buffer
Definition ep.hpp:245
LibXR 命名空间 / LibXR namespace.
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举 / Defines an enumeration for error codes
@ OUT_OF_RANGE
超出范围 / Out of range
@ OK
操作成功 / Operation successful