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
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:
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
242 size_t rx_buffer_size = 128, size_t tx_buffer_size = 128, size_t tx_queue_size = 5,
246 const char* control_interface_string = CDCBase::DEFAULT_CONTROL_INTERFACE_STRING,
247 const char* data_interface_string = CDCBase::DEFAULT_DATA_INTERFACE_STRING)
248 : CDCBase(data_in_ep_num, data_out_ep_num, comm_ep_num, control_interface_string,
249 data_interface_string),
251 read_port_cdc_(rx_buffer_size, *this),
252 write_port_cdc_(tx_queue_size, tx_buffer_size),
254 {
255 read_port_cdc_ = ReadFun; // NOLINT
256 write_port_cdc_ = WriteFun; // NOLINT
257 }
258
267 {
268 auto& line_coding = GetLineCoding();
269
270 switch (cfg.stop_bits)
271 {
272 case 1:
273 line_coding.bCharFormat = 0;
274 break;
275 case 2:
276 line_coding.bCharFormat = 2;
277 break;
278 default:
279 return ErrorCode::ARG_ERR;
280 }
281
282 switch (cfg.parity)
283 {
285 line_coding.bParityType = 0;
286 break;
288 line_coding.bParityType = 1;
289 break;
291 line_coding.bParityType = 2;
292 break;
293 default:
294 return ErrorCode::ARG_ERR;
295 }
296
297 switch (cfg.data_bits)
298 {
299 case 5:
300 case 6:
301 case 7:
302 case 8:
303 case 16:
304 line_coding.bDataBits = static_cast<uint8_t>(cfg.data_bits);
305 break;
306 default:
307 return ErrorCode::ARG_ERR;
308 }
309
310 line_coding.dwDTERate = cfg.baudrate;
312 return ErrorCode::OK;
313 }
314
324 bool TryRearmOut(bool in_isr)
325 {
326 auto ep_data_out = GetDataOutEndpoint();
327 if (ep_data_out == nullptr)
328 {
329 return false;
330 }
331
332 const std::size_t MPS = ep_data_out->MaxPacketSize();
333 if (MPS == 0 || read_port_cdc_.queue_data_ == nullptr)
334 {
335 return false;
336 }
337
339 {
341 {
342 auto push_ans = read_port_cdc_.queue_data_->PushBatch(
343 reinterpret_cast<const uint8_t*>(read_port_cdc_.pending_data_.addr_),
345 if (push_ans == ErrorCode::OK)
346 {
348 }
349 else
350 {
351 return false;
352 }
353 }
354 else
355 {
356 return false;
357 }
358 }
359
360 if (ep_data_out->GetState() == Endpoint::State::BUSY)
361 {
362 return false;
363 }
364
365 auto ans = ep_data_out->Transfer(MPS);
366 if (ans == ErrorCode::OK)
367 {
369 return true;
370 }
371
372 return false;
373 }
374
375 protected:
383 void UnbindEndpoints(EndpointPool& endpoint_pool, bool in_isr) override
384 {
385 CDCBase::UnbindEndpoints(endpoint_pool, in_isr);
386
387 WriteInfoBlock info{};
388
389 write_port_cdc_.queue_data_->Reset();
390 tx_deq_.Reset();
391
392 while (write_port_cdc_.queue_info_->Pop(info) == ErrorCode::OK)
393 {
395 }
396
397 need_write_zlp_ = false;
398
400 read_port_cdc_.pending_data_ = {nullptr, 0};
401
403 }
404
422 static ErrorCode WriteFun(WritePort& port, bool in_isr)
423 {
424 UNUSED(in_isr);
425
426 auto* cdc = LibXR::ContainerOf(&port, &CDCUart::write_port_cdc_);
427
433 if (cdc->in_write_isr_.IsSet())
434 {
435 return ErrorCode::PENDING;
436 }
437
438 auto ep = cdc->GetDataInEndpoint();
439 if (ep == nullptr)
440 {
441 return ErrorCode::FAILED;
442 }
443
444 if (!cdc->Inited())
445 {
446 WriteInfoBlock info{};
447 auto pop_ans = port.queue_info_->Pop(info);
448 if (pop_ans != ErrorCode::OK)
449 {
450 return ErrorCode::EMPTY;
451 }
452
453 auto drop_ans = port.queue_data_->PopBatch(nullptr, info.data.size_);
454 UNUSED(drop_ans);
455 ASSERT(drop_ans == ErrorCode::OK);
456
457 return ErrorCode::INIT_ERR; // 非 PENDING -> 上层 finish 一次 / Non-PENDING
458 // triggers one finish upstream
459 }
460
466 if (ep->GetActiveLength() != 0)
467 {
468 return ErrorCode::PENDING;
469 }
470
476 if (cdc->tx_deq_.HasOp())
477 {
478 cdc->need_write_zlp_ = false;
479 }
480
486
487 // 预写第一段 / Prefill first segment
488 {
489 auto buffer = ep->GetBuffer();
490 std::size_t len = 0;
491
492 slot_ec =
493 cdc->tx_deq_.Take(reinterpret_cast<uint8_t*>(buffer.addr_), buffer.size_, len);
494 if (slot_ec == ErrorCode::EMPTY || len == 0)
495 {
496 return ErrorCode::PENDING;
497 }
498 if (slot_ec != ErrorCode::OK && slot_ec != ErrorCode::PENDING)
499 {
500 return slot_ec;
501 }
502
503 ep->SetActiveLength(len);
504 }
505
506 std::atomic_signal_fence(std::memory_order_seq_cst);
507
508 // 循环:可立即发送则发送;发送后预写下一段并继续检查 / Loop: send if possible; then
509 // prefill next segment
510 while (true)
511 {
512 const std::size_t TO_SEND = ep->GetActiveLength();
513
523 if (ep->GetState() != Endpoint::State::IDLE || TO_SEND == 0 ||
524 !cdc->tx_deq_.HasOp())
525 {
526 return ErrorCode::PENDING;
527 }
528
529 std::atomic_signal_fence(std::memory_order_seq_cst);
530
531 // 启动一次 Transfer / Kick one Transfer
532 ep->SetActiveLength(0);
533 auto ans = ep->Transfer(TO_SEND);
534 ASSERT(ans == ErrorCode::OK);
535
542 if (slot_ec == ErrorCode::OK && cdc->tx_deq_.HeadCompleted())
543 {
544 auto pop_ok = cdc->tx_deq_.PopCompleted(nullptr);
545 ASSERT(pop_ok == ErrorCode::OK);
546
547 // ZLP 判定。
548 // ZLP decision.
549 const std::size_t MPS = ep->MaxPacketSize();
550 if (MPS > 0 && (TO_SEND % MPS) == 0 && ep->GetActiveLength() == 0 &&
551 !cdc->tx_deq_.HasOp())
552 {
553 cdc->need_write_zlp_ = true;
554 }
555
556 return ErrorCode::OK; // 非 PENDING -> 上层完成一次 / Non-PENDING triggers one upstream finish
557 }
558
559 // 预写下一段。
560 // Prefill the next segment.
561 if (!cdc->tx_deq_.HasOp())
562 {
563 return ErrorCode::PENDING;
564 }
565
566 auto buffer = ep->GetBuffer();
567 std::size_t len2 = 0;
568
569 slot_ec =
570 cdc->tx_deq_.Take(reinterpret_cast<uint8_t*>(buffer.addr_), buffer.size_, len2);
571 if (slot_ec == ErrorCode::EMPTY || len2 == 0)
572 {
573 return ErrorCode::PENDING;
574 }
575 if (slot_ec != ErrorCode::OK && slot_ec != ErrorCode::PENDING)
576 {
577 return slot_ec;
578 }
579
580 ep->SetActiveLength(len2);
581 // 下一轮继续检查是否可立即发送。
582 // The next iteration checks whether sending can continue immediately.
583 }
584 }
585
586 static ErrorCode ReadFun(ReadPort&, bool) { return ErrorCode::PENDING; }
587
595 void OnDataOutComplete(bool in_isr, ConstRawData& data) override
596 {
597 if (data.size_ > 0)
598 {
599 auto push_ans = read_port_cdc_.queue_data_->PushBatch(
600 reinterpret_cast<const uint8_t*>(data.addr_), data.size_);
601 if (push_ans == ErrorCode::OK)
602 {
604 }
605 else
606 {
609 return;
610 }
611 }
612
613 (void)TryRearmOut(in_isr);
614 }
615
623 void OnDataInComplete(bool in_isr, ConstRawData& data) override
624 {
625 UNUSED(data);
626
628
629 auto ep = GetDataInEndpoint();
630 if (ep == nullptr)
631 {
632 return;
633 }
634
635 if (!Inited())
636 {
637 WriteInfoBlock info{};
638 tx_deq_.Reset();
639
640 while (write_port_cdc_.queue_info_->Pop(info) == ErrorCode::OK)
641 {
642 write_port_cdc_.queue_data_->Reset();
643 ASSERT(write_port_cdc_.queue_data_->Size() == 0);
645 }
646 return;
647 }
648
649 // ZLP:仅在此刻跨-op 无数据时发送。
650 // Send the ZLP only when there is no data left across ops at this moment.
651 if (need_write_zlp_)
652 {
653 if (ep->GetActiveLength() == 0 && !tx_deq_.HasOp())
654 {
655 auto z = ep->TransferZLP();
656 ASSERT(z == ErrorCode::OK);
657 need_write_zlp_ = false;
658 return;
659 }
660 need_write_zlp_ = false;
661 }
662
663 // ActiveLength==0 时不读取队列。
664 // Do not read queues when ActiveLength==0.
665 const std::size_t PENDING_LEN = ep->GetActiveLength();
666 if (PENDING_LEN == 0)
667 {
668 return;
669 }
670
671 // 1) 续发:本 ISR 仅启动一次 Transfer。
672 // 1) Continue: kick only one Transfer in this ISR.
673 ep->SetActiveLength(0);
674 auto ans = ep->Transfer(PENDING_LEN);
675 ASSERT(ans == ErrorCode::OK);
676
677 // 2) 若为 head op 最后一段:启动后 pop + Finish 一次。
678 // 2) If this is the last segment of the head op, pop + Finish once after kick-off.
680 {
681 WriteInfoBlock completed{};
682 auto pop_ok = tx_deq_.PopCompleted(&completed);
683 ASSERT(pop_ok == ErrorCode::OK);
684 write_port_cdc_.Finish(in_isr, ErrorCode::OK, completed);
685 }
686
687 // 3) 预写:仅在已启动 Transfer 后允许读取队列。
688 // 3) Prefill: queue reads are allowed only after a Transfer has been kicked.
689 bool primed = false;
690 if (tx_deq_.HasOp())
691 {
692 auto buffer = ep->GetBuffer();
693 std::size_t len2 = 0;
694
695 auto ec2 =
696 tx_deq_.Take(reinterpret_cast<uint8_t*>(buffer.addr_), buffer.size_, len2);
697 if ((ec2 == ErrorCode::OK || ec2 == ErrorCode::PENDING) && len2 > 0)
698 {
699 ep->SetActiveLength(len2);
700 primed = true;
701 }
702 }
703
704 // 4) ZLP 判定 / ZLP decision
705 const std::size_t MPS = ep->MaxPacketSize();
706 if (!primed && PENDING_LEN > 0 && MPS > 0 && (PENDING_LEN % MPS) == 0 &&
707 ep->GetActiveLength() == 0 && !tx_deq_.HasOp())
708 {
709 need_write_zlp_ = true;
710 }
711 }
712
713 private:
716
718
720
721 bool need_write_zlp_{false};
722};
723
724inline void CDCUartReadPort::OnRxDequeue(bool in_isr)
725{
726 if (!recv_pause_)
727 {
728 return;
729 }
730
731 // pending_data_ 回填 / Push pending_data_ back into queue
732 if (pending_data_.size_ > 0)
733 {
734 if (queue_data_->EmptySize() >= pending_data_.size_)
735 {
736 auto ans = queue_data_->PushBatch(
737 reinterpret_cast<const uint8_t*>(pending_data_.addr_), pending_data_.size_);
738 if (ans == ErrorCode::OK)
739 {
740 pending_data_ = {nullptr, 0};
741 ProcessPendingReads(in_isr);
742 }
743 else
744 {
745 return;
746 }
747 }
748 else
749 {
750 return;
751 }
752 }
753
754 // 尝试恢复 rearm / Try to rearm OUT
755 (void)owner_.TryRearmOut(in_isr);
756}
757
758} // 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
作用域标志管理器:构造时写入指定值,析构时恢复原值 / 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:379
void ProcessPendingReads(bool in_isr)
Processes pending reads.
Definition libxr_rw.cpp:193
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:188
virtual void UnbindEndpoints(EndpointPool &endpoint_pool, bool) override
反初始化CDC设备 Deinitialize CDC device
Definition cdc_base.hpp:393
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:714
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, const char *control_interface_string=CDCBase::DEFAULT_CONTROL_INTERFACE_STRING, const char *data_interface_string=CDCBase::DEFAULT_DATA_INTERFACE_STRING)
构造函数 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:324
void UnbindEndpoints(EndpointPool &endpoint_pool, bool in_isr) override
解绑端点(清理队列、状态与背压标志) Unbind endpoints (cleanup queues, states, and backpressure flags)
Definition cdc_uart.hpp:383
static ErrorCode WriteFun(WritePort &port, bool in_isr)
写端口回调(TX) Write port callback (TX)
Definition cdc_uart.hpp:422
void OnDataInComplete(bool in_isr, ConstRawData &data) override
IN 完成回调(TX) IN complete callback (TX)
Definition cdc_uart.hpp:623
LibXR::CDCUartTxOpDequeueHelper tx_deq_
TX 出队辅助器 / TX dequeue helper.
Definition cdc_uart.hpp:717
void OnDataOutComplete(bool in_isr, ConstRawData &data) override
OUT 完成回调(RX) OUT complete callback (RX)
Definition cdc_uart.hpp:595
bool need_write_zlp_
ZLP 需求标志 / ZLP required flag.
Definition cdc_uart.hpp:721
LibXR::WritePort write_port_cdc_
CDC TX 写端口 / CDC TX write port.
Definition cdc_uart.hpp:715
ErrorCode SetConfig(UART::Configuration cfg) override
设置 UART 配置(CDC Line Coding) Set UART configuration (CDC Line Coding)
Definition cdc_uart.hpp:266
Flag::Plain in_write_isr_
写 ISR 保护标志 / Write ISR guard flag
Definition cdc_uart.hpp:719
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:724
EPNumber
端点号 Endpoint number
Definition ep.hpp:42
@ EP_AUTO
自动分配端点号 / Auto allocate
USB端点池类 / USB endpoint pool class.
Definition ep_pool.hpp:23
WritePort class for handling write operations.
Definition libxr_rw.hpp:538
void Finish(bool in_isr, ErrorCode ans, WriteInfoBlock &info)
更新写入操作的状态。 Updates the status of the write operation.
Definition libxr_rw.cpp:294
void Reset()
Resets the WritePort.
Definition libxr_rw.cpp:467
LibXR 命名空间
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举
@ INIT_ERR
初始化错误 | Initialization error
@ FAILED
操作失败 | Operation failed
@ EMPTY
为空 | Empty
@ PENDING
等待中 | Pending
@ OK
操作成功 | Operation successful
@ ARG_ERR
参数错误 | Argument error
ErrorCode(* ReadFun)(ReadPort &port, bool in_isr)
Function pointer type for read operations.
Definition libxr_rw.hpp:356
ErrorCode(* WriteFun)(WritePort &port, bool in_isr)
Function pointer type for write operations.
Definition libxr_rw.hpp:352
OwnerType * ContainerOf(MemberType *ptr, MemberType OwnerType::*member) noexcept
通过成员指针恢复其所属对象指针
Definition libxr_def.hpp:79
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:370