libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
cdc_uart.hpp
1#pragma once
2
3#include "cdc_base.hpp"
4#include "ep.hpp"
5#include "flag.hpp"
6#include "libxr_def.hpp"
7#include "libxr_rw.hpp"
8
9namespace LibXR
10{
16{
17 public:
24 explicit CDCUartTxOpDequeueHelper(WritePort& port) : port_(port) {}
25
30 void Reset()
31 {
32 head_valid_ = false;
33 offset_ = 0;
34 }
35
44 bool HasOp() { return head_valid_ || (port_.queue_info_->Size() > 0); }
45
56 ErrorCode Take(uint8_t* dst, std::size_t cap, std::size_t& out_len)
57 {
58 auto ec = EnsureHead();
59 if (ec != ErrorCode::OK)
60 {
61 out_len = 0;
62 return ec; // EMPTY / FAILED
63 }
64
65 const std::size_t REMAINING = Remaining();
66 if (REMAINING == 0)
67 {
68 out_len = 0;
69 return ErrorCode::FAILED;
70 }
71
72 const std::size_t TAKE = (REMAINING < cap) ? REMAINING : cap;
73
74 if (port_.queue_data_->PopBatch(dst, TAKE) != ErrorCode::OK)
75 {
76 out_len = 0;
77 return ErrorCode::FAILED;
78 }
79
80 offset_ += TAKE;
81 out_len = TAKE;
82
83 return (offset_ == head_.data.size_) ? ErrorCode::OK : ErrorCode::PENDING;
84 }
85
93 bool HeadCompleted() const { return head_valid_ && (offset_ == head_.data.size_); }
94
103 ErrorCode PopCompleted(WriteInfoBlock* completed_info = nullptr)
104 {
105 if (!HeadCompleted())
106 {
107 return ErrorCode::FAILED;
108 }
109
110 WriteInfoBlock popped{};
111 auto ans = port_.queue_info_->Pop(popped);
112 ASSERT(ans == ErrorCode::OK);
113
114 if (completed_info)
115 {
116 *completed_info = popped;
117 }
118
119 Reset();
120 return ErrorCode::OK;
121 }
122
123 private:
131 ErrorCode EnsureHead()
132 {
133 if (head_valid_)
134 {
135 return ErrorCode::OK;
136 }
137
138 WriteInfoBlock info{};
139 if (port_.queue_info_->Peek(info) != ErrorCode::OK)
140 {
141 return ErrorCode::EMPTY;
142 }
143
144 head_ = info;
145 head_valid_ = true;
146 offset_ = 0;
147 return ErrorCode::OK;
148 }
149
156 std::size_t Remaining() const
157 {
158 ASSERT(head_valid_);
159 ASSERT(head_.data.size_ >= offset_);
160 return head_.data.size_ - offset_;
161 }
162
163 private:
165 bool head_valid_ = false;
167 std::size_t offset_ = 0;
168};
169
170} // namespace LibXR
171
172namespace LibXR::USB
173{
174
175class CDCUart;
176
182{
183 public:
191 explicit CDCUartReadPort(uint32_t size, CDCUart& owner) : ReadPort(size), owner_(owner)
192 {
193 }
194
201 void OnRxDequeue(bool in_isr) override;
202
203 CDCUartReadPort& operator=(ReadFun fun)
204 {
206 return *this;
207 }
208
210
212 false;
214 nullptr,
215 0};
216};
217
222class CDCUart : public CDCBase, public LibXR::UART
223{
224 public:
225 using LibXR::UART::Read;
227 using LibXR::UART::Write;
229
241 CDCUart(size_t rx_buffer_size = 128, size_t tx_buffer_size = 128,
242 size_t tx_queue_size = 5,
246 : CDCBase(data_in_ep_num, data_out_ep_num, comm_ep_num),
248 read_port_cdc_(rx_buffer_size, *this),
249 write_port_cdc_(tx_queue_size, tx_buffer_size),
251 {
252 read_port_cdc_ = ReadFun; // NOLINT
253 write_port_cdc_ = WriteFun; // NOLINT
254 }
255
263 ErrorCode SetConfig(UART::Configuration cfg) override
264 {
265 auto& line_coding = GetLineCoding();
266
267 switch (cfg.stop_bits)
268 {
269 case 1:
270 line_coding.bCharFormat = 0;
271 break;
272 case 2:
273 line_coding.bCharFormat = 2;
274 break;
275 default:
276 return ErrorCode::ARG_ERR;
277 }
278
279 switch (cfg.parity)
280 {
282 line_coding.bParityType = 0;
283 break;
285 line_coding.bParityType = 1;
286 break;
288 line_coding.bParityType = 2;
289 break;
290 default:
291 return ErrorCode::ARG_ERR;
292 }
293
294 switch (cfg.data_bits)
295 {
296 case 5:
297 case 6:
298 case 7:
299 case 8:
300 case 16:
301 line_coding.bDataBits = static_cast<uint8_t>(cfg.data_bits);
302 break;
303 default:
304 return ErrorCode::ARG_ERR;
305 }
306
307 line_coding.dwDTERate = cfg.baudrate;
309 return ErrorCode::OK;
310 }
311
321 bool TryRearmOut(bool in_isr)
322 {
323 auto ep_data_out = GetDataOutEndpoint();
324 if (ep_data_out == nullptr)
325 {
326 return false;
327 }
328
329 const std::size_t MPS = ep_data_out->MaxPacketSize();
330 if (MPS == 0 || read_port_cdc_.queue_data_ == nullptr)
331 {
332 return false;
333 }
334
336 {
338 {
339 auto push_ans = read_port_cdc_.queue_data_->PushBatch(
340 reinterpret_cast<const uint8_t*>(read_port_cdc_.pending_data_.addr_),
342 if (push_ans == ErrorCode::OK)
343 {
345 }
346 else
347 {
348 return false;
349 }
350 }
351 else
352 {
353 return false;
354 }
355 }
356
357 if (ep_data_out->GetState() == Endpoint::State::BUSY)
358 {
359 return false;
360 }
361
362 auto ans = ep_data_out->Transfer(MPS);
363 if (ans == ErrorCode::OK)
364 {
366 return true;
367 }
368
369 return false;
370 }
371
372 protected:
380 void UnbindEndpoints(EndpointPool& endpoint_pool, bool in_isr) override
381 {
382 CDCBase::UnbindEndpoints(endpoint_pool, in_isr);
383
384 WriteInfoBlock info{};
385
386 write_port_cdc_.queue_data_->Reset();
387 tx_deq_.Reset();
388
389 while (write_port_cdc_.queue_info_->Pop(info) == ErrorCode::OK)
390 {
391 write_port_cdc_.Finish(in_isr, ErrorCode::INIT_ERR, info);
392 }
393
394 need_write_zlp_ = false;
395
397 read_port_cdc_.pending_data_ = {nullptr, 0};
398
400 }
401
419 static ErrorCode WriteFun(WritePort& port, bool in_isr)
420 {
421 UNUSED(in_isr);
422
423 CDCUart* cdc = CONTAINER_OF(&port, CDCUart, write_port_cdc_);
424
430 if (cdc->in_write_isr_.IsSet())
431 {
432 return ErrorCode::PENDING;
433 }
434
435 auto ep = cdc->GetDataInEndpoint();
436 if (ep == nullptr)
437 {
438 return ErrorCode::FAILED;
439 }
440
441 if (!cdc->Inited())
442 {
443 WriteInfoBlock info{};
444 auto pop_ans = port.queue_info_->Pop(info);
445 if (pop_ans != ErrorCode::OK)
446 {
447 return ErrorCode::EMPTY;
448 }
449
450 auto drop_ans = port.queue_data_->PopBatch(nullptr, info.data.size_);
451 UNUSED(drop_ans);
452 ASSERT(drop_ans == ErrorCode::OK);
453
454 return ErrorCode::INIT_ERR; // 非 PENDING -> 上层 finish 一次 / Non-PENDING
455 // triggers one finish upstream
456 }
457
463 if (ep->GetActiveLength() != 0)
464 {
465 return ErrorCode::PENDING;
466 }
467
473 if (cdc->tx_deq_.HasOp())
474 {
475 cdc->need_write_zlp_ = false;
476 }
477
482 ErrorCode slot_ec = ErrorCode::PENDING;
483
484 // 预写第一段 / Prefill first segment
485 {
486 auto buffer = ep->GetBuffer();
487 std::size_t len = 0;
488
489 slot_ec =
490 cdc->tx_deq_.Take(reinterpret_cast<uint8_t*>(buffer.addr_), buffer.size_, len);
491 if (slot_ec == ErrorCode::EMPTY || len == 0)
492 {
493 return ErrorCode::PENDING;
494 }
495 if (slot_ec != ErrorCode::OK && slot_ec != ErrorCode::PENDING)
496 {
497 return slot_ec;
498 }
499
500 ep->SetActiveLength(len);
501 }
502
503 std::atomic_signal_fence(std::memory_order_seq_cst);
504
505 // 循环:可立即发送则发送;发送后预写下一段并继续检查 / Loop: send if possible; then
506 // prefill next segment
507 while (true)
508 {
509 const std::size_t TO_SEND = ep->GetActiveLength();
510
520 if (ep->GetState() != Endpoint::State::IDLE || TO_SEND == 0 ||
521 !cdc->tx_deq_.HasOp())
522 {
523 return ErrorCode::PENDING;
524 }
525
526 std::atomic_signal_fence(std::memory_order_seq_cst);
527
528 // 启动一次 Transfer / Kick one Transfer
529 ep->SetActiveLength(0);
530 auto ans = ep->Transfer(TO_SEND);
531 ASSERT(ans == ErrorCode::OK);
532
539 if (slot_ec == ErrorCode::OK && cdc->tx_deq_.HeadCompleted())
540 {
541 auto pop_ok = cdc->tx_deq_.PopCompleted(nullptr);
542 ASSERT(pop_ok == ErrorCode::OK);
543
544 // ZLP 判定 / ZLP decision
545 const std::size_t MPS = ep->MaxPacketSize();
546 if (MPS > 0 && (TO_SEND % MPS) == 0 && ep->GetActiveLength() == 0 &&
547 !cdc->tx_deq_.HasOp())
548 {
549 cdc->need_write_zlp_ = true;
550 }
551
552 return ErrorCode::OK; // 非 PENDING -> 上层 finish 一次 / Non-PENDING triggers
553 // one finish upstream
554 }
555
556 // 预写下一段 / Prefill next segment
557 if (!cdc->tx_deq_.HasOp())
558 {
559 return ErrorCode::PENDING;
560 }
561
562 auto buffer = ep->GetBuffer();
563 std::size_t len2 = 0;
564
565 slot_ec =
566 cdc->tx_deq_.Take(reinterpret_cast<uint8_t*>(buffer.addr_), buffer.size_, len2);
567 if (slot_ec == ErrorCode::EMPTY || len2 == 0)
568 {
569 return ErrorCode::PENDING;
570 }
571 if (slot_ec != ErrorCode::OK && slot_ec != ErrorCode::PENDING)
572 {
573 return slot_ec;
574 }
575
576 ep->SetActiveLength(len2);
577 // 下一轮继续检查是否可立即发送 / Next iteration checks sendability again
578 }
579 }
580
581 static ErrorCode ReadFun(ReadPort&, bool) { return ErrorCode::PENDING; }
582
590 void OnDataOutComplete(bool in_isr, ConstRawData& data) override
591 {
592 if (data.size_ > 0)
593 {
594 auto push_ans = read_port_cdc_.queue_data_->PushBatch(
595 reinterpret_cast<const uint8_t*>(data.addr_), data.size_);
596 if (push_ans == ErrorCode::OK)
597 {
599 }
600 else
601 {
604 return;
605 }
606 }
607
608 (void)TryRearmOut(in_isr);
609 }
610
618 void OnDataInComplete(bool in_isr, ConstRawData& data) override
619 {
620 UNUSED(data);
621
623
624 auto ep = GetDataInEndpoint();
625 if (ep == nullptr)
626 {
627 return;
628 }
629
630 if (!Inited())
631 {
632 WriteInfoBlock info{};
633 tx_deq_.Reset();
634
635 while (write_port_cdc_.queue_info_->Pop(info) == ErrorCode::OK)
636 {
637 write_port_cdc_.queue_data_->Reset();
638 ASSERT(write_port_cdc_.queue_data_->Size() == 0);
639 write_port_cdc_.Finish(in_isr, ErrorCode::INIT_ERR, info);
640 }
641 return;
642 }
643
644 // ZLP:仅在此刻跨-op 无数据时发送 / ZLP: send only if no data across ops at this
645 // moment
646 if (need_write_zlp_)
647 {
648 if (ep->GetActiveLength() == 0 && !tx_deq_.HasOp())
649 {
650 auto z = ep->TransferZLP();
651 ASSERT(z == ErrorCode::OK);
652 need_write_zlp_ = false;
653 return;
654 }
655 need_write_zlp_ = false;
656 }
657
658 // ActiveLength==0 时不读取队列 / Do not read queues when ActiveLength==0
659 const std::size_t PENDING_LEN = ep->GetActiveLength();
660 if (PENDING_LEN == 0)
661 {
662 return;
663 }
664
665 // 1) 续发:本 ISR 仅启动一次 Transfer / Continue: only one Transfer is kicked in this
666 // ISR
667 ep->SetActiveLength(0);
668 auto ans = ep->Transfer(PENDING_LEN);
669 ASSERT(ans == ErrorCode::OK);
670
671 // 2) 若为 head op 最后一段:启动后 pop+Finish(一次)/ If last segment: pop+Finish
672 // once
674 {
675 WriteInfoBlock completed{};
676 auto pop_ok = tx_deq_.PopCompleted(&completed);
677 ASSERT(pop_ok == ErrorCode::OK);
678 write_port_cdc_.Finish(in_isr, ErrorCode::OK, completed);
679 }
680
681 // 3) 预写:仅在已启动 Transfer 后允许读取队列 / Prefill: allowed only after kicking
682 // Transfer
683 bool primed = false;
684 if (tx_deq_.HasOp())
685 {
686 auto buffer = ep->GetBuffer();
687 std::size_t len2 = 0;
688
689 auto ec2 =
690 tx_deq_.Take(reinterpret_cast<uint8_t*>(buffer.addr_), buffer.size_, len2);
691 if ((ec2 == ErrorCode::OK || ec2 == ErrorCode::PENDING) && len2 > 0)
692 {
693 ep->SetActiveLength(len2);
694 primed = true;
695 }
696 }
697
698 // 4) ZLP 判定 / ZLP decision
699 const std::size_t MPS = ep->MaxPacketSize();
700 if (!primed && PENDING_LEN > 0 && MPS > 0 && (PENDING_LEN % MPS) == 0 &&
701 ep->GetActiveLength() == 0 && !tx_deq_.HasOp())
702 {
703 need_write_zlp_ = true;
704 }
705 }
706
707 private:
710
712
714
715 bool need_write_zlp_{false};
716};
717
718inline void CDCUartReadPort::OnRxDequeue(bool in_isr)
719{
720 if (!recv_pause_)
721 {
722 return;
723 }
724
725 // pending_data_ 回填 / Push pending_data_ back into queue
726 if (pending_data_.size_ > 0)
727 {
728 if (queue_data_->EmptySize() >= pending_data_.size_)
729 {
730 auto ans = queue_data_->PushBatch(
731 reinterpret_cast<const uint8_t*>(pending_data_.addr_), pending_data_.size_);
732 if (ans == ErrorCode::OK)
733 {
734 pending_data_ = {nullptr, 0};
735 ProcessPendingReads(in_isr);
736 }
737 else
738 {
739 return;
740 }
741 }
742 else
743 {
744 return;
745 }
746 }
747
748 // 尝试恢复 rearm / Try to rearm OUT
749 (void)owner_.TryRearmOut(in_isr);
750}
751
752} // namespace LibXR::USB
WritePort(info 队列 + data 队列)的“单 op 不跨界”出队辅助器 Dequeue helper for WritePort (info + data) without cross...
Definition cdc_uart.hpp:16
WritePort & port_
写端口引用 / Write port reference
Definition cdc_uart.hpp:164
bool HeadCompleted() const
head op 是否已全部出队 Whether the cached head op is fully dequeued
Definition cdc_uart.hpp:93
CDCUartTxOpDequeueHelper(WritePort &port)
构造函数 Constructor
Definition cdc_uart.hpp:24
bool head_valid_
head 缓存有效标志 / Cached head valid flag
Definition cdc_uart.hpp:165
void Reset()
重置内部状态(head 缓存与偏移) Reset internal state (cached head and offset)
Definition cdc_uart.hpp:30
ErrorCode EnsureHead()
确保 head 缓存可用(必要时 Peek info) Ensure cached head is valid (Peek info if needed)
Definition cdc_uart.hpp:131
WriteInfoBlock head_
缓存的 head info / Cached head info
Definition cdc_uart.hpp:166
ErrorCode PopCompleted(WriteInfoBlock *completed_info=nullptr)
在 head 完成后 pop info 并重置状态 Pop info after head completes and reset state
Definition cdc_uart.hpp:103
bool HasOp()
是否存在可处理的 op Whether any op exists
Definition cdc_uart.hpp:44
std::size_t offset_
当前 op 已出队偏移 / Dequeued offset within current op
Definition cdc_uart.hpp:167
std::size_t Remaining() const
当前 op 剩余未出队字节数 Remaining bytes of current op
Definition cdc_uart.hpp:156
ErrorCode Take(uint8_t *dst, std::size_t cap, std::size_t &out_len)
从 data 队列搬运数据到目标 buffer,并推进 offset(不 pop info) Dequeue bytes from data queue into destination buffer ...
Definition cdc_uart.hpp:56
常量原始数据封装类。 A class for encapsulating constant raw data.
size_t size_
数据大小(字节)。 The size of the data (in bytes).
const void * addr_
数据存储地址(常量)。 The storage address of the data (constant).
普通标志位(非原子)/ Non-atomic flag
Definition flag.hpp:115
bool IsSet() const noexcept
判断是否已置位 / Check whether the flag is set
Definition flag.hpp:138
作用域标志管理器:构造时写入指定值,析构时恢复原值 / Scoped flag restorer: set on entry, restore on exit
Definition flag.hpp:199
void Reset()
重置队列 / Resets the queue
ErrorCode PushBatch(const Data *data, size_t size)
批量推入数据 / Pushes multiple elements into the queue
size_t EmptySize()
计算队列剩余可用空间 / Calculates the remaining available space in the queue
size_t Size() const
获取当前队列中的元素数量 / Returns the number of elements currently in the queue
ErrorCode PopBatch(Data *data, size_t size)
批量弹出数据 / Pops multiple elements from the queue
ReadPort class for handling read operations.
Definition libxr_rw.hpp:272
void ProcessPendingReads(bool in_isr)
Processes pending reads.
Definition libxr_rw.cpp:127
ReadPort & operator=(ReadFun fun)
赋值运算符重载,用于设置读取函数。 Overloaded assignment operator to set the read function.
Definition libxr_rw.cpp:32
通用异步收发传输(UART)基类 / Abstract base class for Universal Asynchronous Receiver-Transmitter (UART)
Definition uart.hpp:19
ReadPort * read_port_
读取端口 / Read port
Definition uart.hpp:53
@ NO_PARITY
无校验 / No parity
@ ODD
奇校验 / Odd parity
@ EVEN
偶校验 / Even parity
WritePort * write_port_
写入端口 / Write port
Definition uart.hpp:54
USB CDC ACM (Abstract Control Model) 设备类实现 USB CDC ACM (Abstract Control Model) device class implemen...
Definition cdc_base.hpp:24
ErrorCode SendSerialState()
发送串行状态通知 Send serial state notification
Definition cdc_base.hpp:165
virtual void UnbindEndpoints(EndpointPool &endpoint_pool, bool) override
反初始化CDC设备 Deinitialize CDC device
Definition cdc_base.hpp:355
USB CDC-ACM UART 适配器 USB CDC-ACM UART adapter.
Definition cdc_uart.hpp:223
CDCUartReadPort read_port_cdc_
CDC RX 读端口 / CDC RX read port.
Definition cdc_uart.hpp:708
CDCUart(size_t rx_buffer_size=128, size_t tx_buffer_size=128, size_t tx_queue_size=5, Endpoint::EPNumber data_in_ep_num=Endpoint::EPNumber::EP_AUTO, Endpoint::EPNumber data_out_ep_num=Endpoint::EPNumber::EP_AUTO, Endpoint::EPNumber comm_ep_num=Endpoint::EPNumber::EP_AUTO)
构造函数 Constructor
Definition cdc_uart.hpp:241
bool TryRearmOut(bool in_isr)
尝试 rearm OUT(背压恢复/持续接收) Try to rearm OUT endpoint (backpressure recovery / continuous RX)
Definition cdc_uart.hpp:321
void UnbindEndpoints(EndpointPool &endpoint_pool, bool in_isr) override
解绑端点(清理队列、状态与背压标志) Unbind endpoints (cleanup queues, states, and backpressure flags)
Definition cdc_uart.hpp:380
static ErrorCode WriteFun(WritePort &port, bool in_isr)
写端口回调(TX) Write port callback (TX)
Definition cdc_uart.hpp:419
void OnDataInComplete(bool in_isr, ConstRawData &data) override
IN 完成回调(TX) IN complete callback (TX)
Definition cdc_uart.hpp:618
LibXR::CDCUartTxOpDequeueHelper tx_deq_
TX 出队辅助器 / TX dequeue helper.
Definition cdc_uart.hpp:711
void OnDataOutComplete(bool in_isr, ConstRawData &data) override
OUT 完成回调(RX) OUT complete callback (RX)
Definition cdc_uart.hpp:590
bool need_write_zlp_
ZLP 需求标志 / ZLP required flag.
Definition cdc_uart.hpp:715
LibXR::WritePort write_port_cdc_
CDC TX 写端口 / CDC TX write port.
Definition cdc_uart.hpp:709
ErrorCode SetConfig(UART::Configuration cfg) override
设置 UART 配置(CDC Line Coding) Set UART configuration (CDC Line Coding)
Definition cdc_uart.hpp:263
Flag::Plain in_write_isr_
写 ISR 保护标志 / Write ISR guard flag
Definition cdc_uart.hpp:713
CDC UART 读端口(背压 + pending 缓存) CDC UART read port (backpressure + pending cache)
Definition cdc_uart.hpp:182
CDCUartReadPort(uint32_t size, CDCUart &owner)
构造函数 Constructor
Definition cdc_uart.hpp:191
ConstRawData pending_data_
pending 数据(指向底层 USB buffer)/ Pending data pointing to USB buffer
Definition cdc_uart.hpp:213
CDCUart & owner_
所属 CDCUart / Owning CDCUart
Definition cdc_uart.hpp:209
bool recv_pause_
背压标志:true 表示 OUT 未 rearm / Backpressure flag: OUT not rearmed
Definition cdc_uart.hpp:211
void OnRxDequeue(bool in_isr) override
数据队列被消费时回调(解除背压并尝试恢复 OUT rearm) Called when RX queue is dequeued (lift backpressure and try to rearm ...
Definition cdc_uart.hpp:718
EPNumber
端点号 Endpoint number
Definition ep.hpp:41
@ EP_AUTO
自动分配端点号 / Auto allocate
USB端点池类 / USB endpoint pool class.
Definition ep_pool.hpp:23
WritePort class for handling write operations.
Definition libxr_rw.hpp:413
void Finish(bool in_isr, ErrorCode ans, WriteInfoBlock &info)
更新写入操作的状态。 Updates the status of the write operation.
Definition libxr_rw.cpp:191
void Reset()
Resets the WritePort.
Definition libxr_rw.cpp:281
LibXR 命名空间
Definition ch32_can.hpp:14
ErrorCode(* ReadFun)(ReadPort &port, bool in_isr)
Function pointer type for read operations.
Definition libxr_rw.hpp:249
ErrorCode(* WriteFun)(WritePort &port, bool in_isr)
Function pointer type for write operations.
Definition libxr_rw.hpp:245
UART 配置结构体 / UART configuration structure.
Definition uart.hpp:44
uint8_t stop_bits
停止位长度 / Number of stop bits
Definition uart.hpp:50
Parity parity
校验模式 / Parity mode
Definition uart.hpp:47
uint8_t data_bits
数据位长度 / Number of data bits
Definition uart.hpp:48
uint32_t baudrate
波特率 / Baud rate
Definition uart.hpp:45
ConstRawData data
Data buffer. 数据缓冲区。
Definition libxr_rw.hpp:263