libxr
Embedded C++ framework for bare-metal, RTOS and Linux
Loading...
Searching...
No Matches
esp_cdc_jtag.cpp
1#include "esp_cdc_jtag.hpp"
2
3#if SOC_USB_SERIAL_JTAG_SUPPORTED && \
4 ((defined(CONFIG_IDF_TARGET_ESP32C3) && CONFIG_IDF_TARGET_ESP32C3) || \
5 (defined(CONFIG_IDF_TARGET_ESP32C6) && CONFIG_IDF_TARGET_ESP32C6))
6
7#include <algorithm>
8
9#include "hal/usb_serial_jtag_ll.h"
10#include "soc/interrupts.h"
11
12namespace
13{
14constexpr uint32_t TX_INTR_MASK = USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY;
15constexpr uint32_t RX_INTR_MASK = USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT;
16constexpr uint32_t ALL_INTR_MASK = TX_INTR_MASK | RX_INTR_MASK;
17constexpr size_t ENDPOINT_SIZE = 64U;
18
19constexpr bool IsConfigSupported(const LibXR::UART::Configuration& config)
20{
21 return (config.data_bits == 8U) && (config.stop_bits == 1U) &&
23}
24} // namespace
25
26namespace LibXR
27{
28
30{
31 owner_.ResumeRx(in_isr);
32}
33
34ESP32CDCJtag::ESP32CDCJtag(size_t rx_buffer_size, size_t tx_buffer_size,
35 uint32_t tx_queue_size, UART::Configuration config)
36 : UART(&_read_port, &_write_port),
37 _read_port(rx_buffer_size, *this),
38 _write_port(tx_queue_size, tx_buffer_size)
39{
40 ASSERT(rx_buffer_size > 0U);
41 ASSERT(tx_buffer_size > 0U);
42 ASSERT(tx_queue_size > 0U);
43
44 INIT_CRIT_SECTION_LOCK_RUNTIME(&irq_lock_);
45
46 ASSERT(IsConfigSupported(config));
47 [[maybe_unused]] const auto init_hardware_result = InitHardware();
48 REQUIRE(init_hardware_result == ErrorCode::OK);
49
50 _write_port = WriteFun;
51}
52
53ErrorCode ESP32CDCJtag::SetConfig(UART::Configuration config, bool)
54{
55 return IsConfigSupported(config) ? ErrorCode::OK : ErrorCode::ARG_ERR;
56}
57
58ErrorCode ESP32CDCJtag::InitHardware()
59{
60 if (hw_inited_)
61 {
62 return ErrorCode::OK;
63 }
64
65 const esp_err_t intr_ans =
66 esp_intr_alloc(ETS_USB_SERIAL_JTAG_INTR_SOURCE, 0, IsrEntry, this, &intr_handle_);
67 if (intr_ans != ESP_OK)
68 {
69 intr_handle_ = nullptr;
70 return ErrorCode::INIT_ERR;
71 }
72
73 DisableAndClearInterrupt(ALL_INTR_MASK);
74 EnableInterrupt(RX_INTR_MASK);
75
76 hw_inited_ = true;
77 return ErrorCode::OK;
78}
79
80void ESP32CDCJtag::IsrEntry(void* arg)
81{
82 auto* cdc = static_cast<ESP32CDCJtag*>(arg);
83 if (cdc != nullptr)
84 {
85 cdc->HandleInterrupt();
86 }
87}
88
89void ESP32CDCJtag::WriteFun(WritePort& port, bool in_isr)
90{
91 auto* cdc = LibXR::ContainerOf(&port, &ESP32CDCJtag::_write_port);
92 cdc->service_.Invoke(EVENT_WRITE, in_isr, [cdc](uint32_t events, bool owner_isr)
93 { cdc->ServiceEvents(events, owner_isr); });
94}
95
96void ESP32CDCJtag::ResumeRx(bool in_isr)
97{
98 service_.Invoke(EVENT_RX_SPACE, in_isr, [this](uint32_t events, bool owner_isr)
99 { ServiceEvents(events, owner_isr); });
100}
101
102void ESP32CDCJtag::ServiceEvents(uint32_t events, bool in_isr)
103{
104 if ((events & EVENT_TX_EMPTY) != 0U)
105 {
106 tx_empty_interrupt_armed_ = false;
107 }
108 if ((events & (EVENT_RX_DATA | EVENT_RX_SPACE)) != 0U)
109 {
110 auto queue = _read_port.GetReadQueue(in_isr);
111 ServiceRx(queue, in_isr);
112 queue.Publish();
113 }
114
115 if ((events & (EVENT_WRITE | EVENT_TX_EMPTY)) != 0U)
116 {
117 ProgressTx(in_isr);
118 }
119}
120
121void ESP32CDCJtag::PushRxBytes(ReadPort::ReadQueue& queue, const uint8_t* data,
122 size_t size, bool in_isr)
123{
124 [[maybe_unused]] const auto push_batch_result = queue.PushBatch(data, size);
125 DEV_ASSERT_FROM_CALLBACK(push_batch_result == ErrorCode::OK, in_isr);
126}
127
128void ESP32CDCJtag::DrainRxToQueue(ReadPort::ReadQueue& queue, bool in_isr)
129{
130 while (usb_serial_jtag_ll_rxfifo_data_available())
131 {
132 const size_t free_space = queue.EmptySize();
133 if (free_space == 0U)
134 {
135 return;
136 }
137
138 uint8_t rx_tmp[ENDPOINT_SIZE] = {};
139 const size_t read_size = std::min(free_space, sizeof(rx_tmp));
140 const int got =
141 usb_serial_jtag_ll_read_rxfifo(rx_tmp, static_cast<uint32_t>(read_size));
142 if (got <= 0)
143 {
144 return;
145 }
146 DEV_ASSERT_FROM_CALLBACK(static_cast<size_t>(got) <= read_size, in_isr);
147 PushRxBytes(queue, rx_tmp, static_cast<size_t>(got), in_isr);
148 }
149}
150
151void ESP32CDCJtag::ServiceRx(ReadPort::ReadQueue& queue, bool in_isr)
152{
153 DisableInterrupt(RX_INTR_MASK);
154 DrainRxToQueue(queue, in_isr);
155
156 if (queue.EmptySize() != 0U)
157 {
158 EnableInterrupt(RX_INTR_MASK);
159 }
160}
161
162void ESP32CDCJtag::ArmTxEmptyInterrupt()
163{
164 if (!tx_empty_interrupt_armed_)
165 {
166 EnableInterrupt(TX_INTR_MASK);
167 tx_empty_interrupt_armed_ = true;
168 }
169}
170
171void ESP32CDCJtag::DisarmTxEmptyInterrupt()
172{
173 if (tx_empty_interrupt_armed_)
174 {
175 DisableAndClearInterrupt(TX_INTR_MASK);
176 tx_empty_interrupt_armed_ = false;
177 }
178 else
179 {
180 ClearInterrupt(TX_INTR_MASK);
181 }
182}
183
184void ESP32CDCJtag::FlushTxFifo()
185{
186 if (tx_flush_pending_)
187 {
188 usb_serial_jtag_ll_txfifo_flush();
189 tx_flush_pending_ = false;
190 }
191}
192
193size_t ESP32CDCJtag::FillTxFifo(WritePort::WriteQueue& queue, bool in_isr)
194{
195 return queue.PopWithWriter(
196 ENDPOINT_SIZE,
197 [this, in_isr](const uint8_t* first, size_t first_size, const uint8_t* second,
198 size_t second_size) -> size_t
199 {
200 auto write_span = [this, in_isr](const uint8_t* data, size_t size) -> size_t
201 {
202 if (size == 0U)
203 {
204 return 0U;
205 }
206
207 const int written =
208 usb_serial_jtag_ll_write_txfifo(data, static_cast<uint32_t>(size));
209 REQUIRE_FROM_CALLBACK(written >= 0, in_isr);
210 DEV_ASSERT_FROM_CALLBACK(static_cast<size_t>(written) <= size, in_isr);
211 return written > 0 ? static_cast<size_t>(written) : 0U;
212 };
213
214 const size_t first_written = write_span(first, first_size);
215 if (first_written != first_size)
216 {
217 tx_flush_pending_ = tx_flush_pending_ || first_written != 0U;
218 return first_written;
219 }
220
221 const size_t second_written = write_span(second, second_size);
222 const size_t accepted = first_written + second_written;
223 tx_flush_pending_ = tx_flush_pending_ || accepted != 0U;
224 return accepted;
225 });
226}
227
228void ESP32CDCJtag::ProgressTx(bool in_isr)
229{
230 if (!usb_serial_jtag_ll_txfifo_writable())
231 {
232 ArmTxEmptyInterrupt();
233 return;
234 }
235
236 size_t accepted = 0U;
237 {
238 auto queue = _write_port.GetWriteQueue(in_isr);
239 if (queue.Empty())
240 {
241 FlushTxFifo();
242 DisarmTxEmptyInterrupt();
243 return;
244 }
245
246 accepted = FillTxFifo(queue, in_isr);
247 }
248
249 if (accepted == 0U)
250 {
251 ArmTxEmptyInterrupt();
252 return;
253 }
254
255 if (_write_port.Size() == 0U)
256 {
257 if (usb_serial_jtag_ll_txfifo_writable())
258 {
259 FlushTxFifo();
260 DisarmTxEmptyInterrupt();
261 }
262 else
263 {
264 // 满包需要等下一次 FIFO 可写后再发送结束用的 ZLP。
265 // A full packet needs a later writable turn for its terminating ZLP.
266 ArmTxEmptyInterrupt();
267 }
268 }
269 else
270 {
271 if (usb_serial_jtag_ll_txfifo_writable())
272 {
273 FlushTxFifo();
274 }
275 ArmTxEmptyInterrupt();
276 }
277}
278
279void ESP32CDCJtag::HandleInterrupt()
280{
281 uint32_t status = CaptureInterrupt(ALL_INTR_MASK);
282 while (true)
283 {
284 const uint32_t handled = status & ALL_INTR_MASK;
285 if (handled == 0U)
286 {
287 return;
288 }
289
290 uint32_t events = 0U;
291 if ((handled & RX_INTR_MASK) != 0U)
292 {
293 events |= EVENT_RX_DATA;
294 }
295 if ((handled & TX_INTR_MASK) != 0U)
296 {
297 events |= EVENT_TX_EMPTY;
298 }
299
300 service_.Invoke(events, true, [this](uint32_t owner_events, bool owner_isr)
301 { ServiceEvents(owner_events, owner_isr); });
302
303 status = CaptureInterrupt(ALL_INTR_MASK);
304 }
305}
306
307void ESP32CDCJtag::EnableInterrupt(uint32_t mask)
308{
309 esp_os_enter_critical_safe(&irq_lock_);
310 usb_serial_jtag_ll_ena_intr_mask(mask);
311 esp_os_exit_critical_safe(&irq_lock_);
312}
313
314void ESP32CDCJtag::DisableInterrupt(uint32_t mask)
315{
316 esp_os_enter_critical_safe(&irq_lock_);
317 usb_serial_jtag_ll_disable_intr_mask(mask);
318 esp_os_exit_critical_safe(&irq_lock_);
319}
320
321void ESP32CDCJtag::ClearInterrupt(uint32_t mask)
322{
323 esp_os_enter_critical_safe(&irq_lock_);
324 usb_serial_jtag_ll_clr_intsts_mask(mask);
325 esp_os_exit_critical_safe(&irq_lock_);
326}
327
328void ESP32CDCJtag::DisableAndClearInterrupt(uint32_t mask)
329{
330 esp_os_enter_critical_safe(&irq_lock_);
331 usb_serial_jtag_ll_disable_intr_mask(mask);
332 usb_serial_jtag_ll_clr_intsts_mask(mask);
333 esp_os_exit_critical_safe(&irq_lock_);
334}
335
336uint32_t ESP32CDCJtag::CaptureInterrupt(uint32_t mask)
337{
338 esp_os_enter_critical_safe(&irq_lock_);
339 const uint32_t handled = usb_serial_jtag_ll_get_intsts_mask() & mask;
340 if (handled != 0U)
341 {
342 usb_serial_jtag_ll_disable_intr_mask(handled);
343 usb_serial_jtag_ll_clr_intsts_mask(handled);
344 }
345 esp_os_exit_critical_safe(&irq_lock_);
346 return handled;
347}
348
349} // namespace LibXR
350
351#endif
ESP32CDCJtag(size_t rx_buffer_size=1024, size_t tx_buffer_size=512, uint32_t tx_queue_size=5, UART::Configuration config={115200, UART::Parity::NO_PARITY, 8, 1})
构造并初始化 USB Serial/JTAG 后端 / Construct the USB Serial/JTAG backend.
void OnReadQueueSpaceAvailable(bool in_isr) override
发布接收空间通知 / Publish RX space availability.
@ NO_PARITY
无校验 / No parity
LibXR 命名空间 / LibXR namespace.
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举 / Defines an enumeration for error codes
void(* WriteFun)(WritePort &port, bool in_isr)
写入进度通知函数类型 / Write progress notification function type.
OwnerType * ContainerOf(MemberType *ptr, MemberType OwnerType::*member) noexcept
通过成员指针恢复其所属对象指针 Recover the owning object pointer from a member pointer
UART 配置结构体 / UART configuration structure.
Definition uart.hpp:44
uint8_t stop_bits
停止位长度 / Number of stop bits
Definition uart.hpp:53