libxr  1.0
Want to be the best embedded framework
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#include <cstring>
9#include <new>
10
11#include "esp_attr.h"
12#include "hal/usb_serial_jtag_ll.h"
13#include "soc/interrupts.h"
14
15namespace
16{
17constexpr uint32_t kTxIntrMask = USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY;
18constexpr uint32_t kRxIntrMask = USB_SERIAL_JTAG_INTR_SERIAL_OUT_RECV_PKT;
19constexpr uint32_t kAllIntrMask = kTxIntrMask | kRxIntrMask;
20} // namespace
21
22namespace LibXR
23{
24
25ESP32CDCJtag::ESP32CDCJtag(size_t rx_buffer_size, size_t tx_buffer_size,
26 uint32_t tx_queue_size, UART::Configuration config)
27 : UART(&_read_port, &_write_port),
28 config_(config),
29 tx_slot_storage_(new (std::nothrow) uint8_t[tx_buffer_size * 2U]),
30 tx_slot_size_(tx_buffer_size),
31 _read_port(rx_buffer_size),
32 _write_port(tx_queue_size, tx_buffer_size)
33{
34 ASSERT(tx_slot_storage_ != nullptr);
35 ASSERT(tx_slot_size_ > 0U);
36
37 tx_slot_a_ = tx_slot_storage_;
38 tx_slot_b_ = tx_slot_storage_ + tx_slot_size_;
39
40 _read_port = ReadFun;
41 _write_port = WriteFun;
42
43 if (SetConfig(config_) != ErrorCode::OK)
44 {
45 ASSERT(false);
46 return;
47 }
48
49 if (InitHardware() != ErrorCode::OK)
50 {
51 ASSERT(false);
52 }
53}
54
55ErrorCode ESP32CDCJtag::SetConfig(UART::Configuration config)
56{
57 if ((config.data_bits != 8) || (config.stop_bits != 1) ||
58 (config.parity != UART::Parity::NO_PARITY))
59 {
60 return ErrorCode::ARG_ERR;
61 }
62 config_ = config;
63 return ErrorCode::OK;
64}
65
66ErrorCode ESP32CDCJtag::InitHardware()
67{
68 if (hw_inited_)
69 {
70 return ErrorCode::OK;
71 }
72
73 ResetTxState(false);
74
75 const esp_err_t intr_ans = esp_intr_alloc(
76 ETS_USB_SERIAL_JTAG_INTR_SOURCE, ESP_INTR_FLAG_IRAM, IsrEntry, this, &intr_handle_);
77 intr_installed_ = (intr_ans == ESP_OK);
78 if (!intr_installed_)
79 {
80 intr_handle_ = nullptr;
81 return ErrorCode::INIT_ERR;
82 }
83
84 usb_serial_jtag_ll_clr_intsts_mask(kAllIntrMask);
85 usb_serial_jtag_ll_ena_intr_mask(kRxIntrMask);
86
87 hw_inited_ = true;
88 return ErrorCode::OK;
89}
90
91void IRAM_ATTR ESP32CDCJtag::IsrEntry(void* arg)
92{
93 auto* cdc = static_cast<ESP32CDCJtag*>(arg);
94 if (cdc != nullptr)
95 {
96 cdc->HandleInterrupt();
97 }
98}
99
100ErrorCode IRAM_ATTR ESP32CDCJtag::WriteFun(WritePort& port, bool in_isr)
101{
102 auto* cdc = CONTAINER_OF(&port, ESP32CDCJtag, _write_port);
103 return cdc->TryStartTx(in_isr);
104}
105
106ErrorCode ESP32CDCJtag::ReadFun(ReadPort&, bool) { return ErrorCode::PENDING; }
107
108void IRAM_ATTR ESP32CDCJtag::ClearActiveTx()
109{
110 tx_active_ptr_ = nullptr;
111 tx_active_size_ = 0;
112 tx_active_offset_ = 0;
113 tx_active_info_ = {};
114 tx_active_valid_ = false;
115}
116
117void IRAM_ATTR ESP32CDCJtag::ClearPendingTx()
118{
119 tx_pending_ptr_ = nullptr;
120 tx_pending_size_ = 0;
121 tx_pending_info_ = {};
122 tx_pending_valid_ = false;
123}
124
125void IRAM_ATTR ESP32CDCJtag::ResetTxState(bool)
126{
127 ClearActiveTx();
128 ClearPendingTx();
129 tx_busy_.Clear();
130}
131
132bool IRAM_ATTR ESP32CDCJtag::DequeueTxToSlot(uint8_t* slot, size_t& size,
133 WriteInfoBlock& info, bool in_isr)
134{
135 (void)in_isr;
136
137 WriteInfoBlock peek_info = {};
138 if (write_port_->queue_info_->Peek(peek_info) != ErrorCode::OK)
139 {
140 return false;
141 }
142
143 if (peek_info.data.size_ > tx_slot_size_)
144 {
145 ASSERT(false);
146 return false;
147 }
148
149 if (write_port_->queue_data_->PopBatch(slot, peek_info.data.size_) != ErrorCode::OK)
150 {
151 return false;
152 }
153
154 if (write_port_->queue_info_->Pop(info) != ErrorCode::OK)
155 {
156 return false;
157 }
158
159 size = peek_info.data.size_;
160 return true;
161}
162
163bool IRAM_ATTR ESP32CDCJtag::LoadActiveTxFromQueue(bool in_isr)
164{
165 if (tx_active_valid_)
166 {
167 return true;
168 }
169
170 size_t active_size = 0U;
171 if (!DequeueTxToSlot(tx_slot_a_, active_size, tx_active_info_, in_isr))
172 {
173 return false;
174 }
175
176 tx_active_ptr_ = tx_slot_a_;
177 tx_active_size_ = active_size;
178 tx_active_valid_ = true;
179 return true;
180}
181
182bool IRAM_ATTR ESP32CDCJtag::LoadPendingTxFromQueue(bool in_isr)
183{
184 if (tx_pending_valid_)
185 {
186 return false;
187 }
188
189 uint8_t* pending_slot = tx_slot_b_;
190 if (tx_active_ptr_ == tx_slot_b_)
191 {
192 pending_slot = tx_slot_a_;
193 }
194
195 size_t pending_size = 0U;
196 if (!DequeueTxToSlot(pending_slot, pending_size, tx_pending_info_, in_isr))
197 {
198 return false;
199 }
200
201 tx_pending_ptr_ = pending_slot;
202 tx_pending_size_ = pending_size;
203 tx_pending_valid_ = true;
204 return true;
205}
206
207bool IRAM_ATTR ESP32CDCJtag::PumpTx(bool)
208{
209 while (tx_busy_.IsSet())
210 {
211 if (!tx_active_valid_ || (tx_active_ptr_ == nullptr) ||
212 (tx_active_offset_ >= tx_active_size_))
213 {
214 tx_busy_.Clear();
215 return true;
216 }
217
218 const uint32_t remain = static_cast<uint32_t>(tx_active_size_ - tx_active_offset_);
219 const int written =
220 usb_serial_jtag_ll_write_txfifo(tx_active_ptr_ + tx_active_offset_, remain);
221 if (written <= 0)
222 {
223 return false;
224 }
225
226 tx_active_offset_ += static_cast<size_t>(written);
227 if (tx_active_offset_ >= tx_active_size_)
228 {
229 tx_busy_.Clear();
230 return true;
231 }
232 }
233
234 return false;
235}
236
237void IRAM_ATTR ESP32CDCJtag::PushRxBytes(const uint8_t* data, size_t size, bool in_isr)
238{
239 size_t offset = 0U;
240 bool pushed_any = false;
241
242 while (offset < size)
243 {
244 const size_t free_space = read_port_->queue_data_->EmptySize();
245 if (free_space == 0U)
246 {
247 break;
248 }
249
250 const size_t chunk = std::min(free_space, size - offset);
251 if (read_port_->queue_data_->PushBatch(data + offset, chunk) != ErrorCode::OK)
252 {
253 break;
254 }
255
256 offset += chunk;
257 pushed_any = true;
258 }
259
260 if (pushed_any)
261 {
262 read_port_->ProcessPendingReads(in_isr);
263 }
264}
265
266bool IRAM_ATTR ESP32CDCJtag::StartActiveTransfer(bool in_isr)
267{
268 if (!tx_active_valid_ || (tx_active_ptr_ == nullptr) || (tx_active_size_ == 0U))
269 {
270 return false;
271 }
272
273 if (tx_busy_.TestAndSet())
274 {
275 return true;
276 }
277
278 tx_active_offset_ = 0;
279 usb_serial_jtag_ll_ena_intr_mask(kTxIntrMask);
280 (void)PumpTx(in_isr);
281 return true;
282}
283
284bool IRAM_ATTR ESP32CDCJtag::StartAndReportActive(bool in_isr)
285{
286 if (!StartActiveTransfer(in_isr))
287 {
288 write_port_->Finish(in_isr, ErrorCode::FAILED, tx_active_info_);
289 ClearActiveTx();
290 return false;
291 }
292
293 // Keep aligned with STM/CH: once next op is kicked to HW, report it finished.
294 write_port_->Finish(in_isr, ErrorCode::OK, tx_active_info_);
295 if (!tx_busy_.IsSet() && tx_active_valid_)
296 {
297 OnTxTransferDone(in_isr, ErrorCode::OK);
298 }
299 return true;
300}
301
302void IRAM_ATTR ESP32CDCJtag::StopTxTransfer()
303{
304 usb_serial_jtag_ll_txfifo_flush();
305 usb_serial_jtag_ll_disable_intr_mask(kTxIntrMask);
306}
307
308void IRAM_ATTR ESP32CDCJtag::OnTxTransferDone(bool in_isr, ErrorCode result)
309{
310 Flag::ScopedRestore tx_flag(in_tx_isr_);
311 tx_busy_.Clear();
312
313 ClearActiveTx();
314
315 if ((result != ErrorCode::OK) && tx_pending_valid_)
316 {
317 write_port_->Finish(in_isr, ErrorCode::FAILED, tx_pending_info_);
318 ClearPendingTx();
319 }
320
321 if (result != ErrorCode::OK)
322 {
323 StopTxTransfer();
324 return;
325 }
326
327 if (tx_pending_valid_)
328 {
329 tx_active_ptr_ = tx_pending_ptr_;
330 tx_active_size_ = tx_pending_size_;
331 tx_active_info_ = tx_pending_info_;
332 tx_active_valid_ = true;
333 ClearPendingTx();
334 (void)StartAndReportActive(in_isr);
335 }
336 else
337 {
338 if (LoadActiveTxFromQueue(in_isr))
339 {
340 (void)StartAndReportActive(in_isr);
341 }
342 }
343
344 if (!tx_pending_valid_)
345 {
346 (void)LoadPendingTxFromQueue(in_isr);
347 }
348
349 if (!tx_busy_.IsSet() && !tx_active_valid_ && !tx_pending_valid_)
350 {
351 StopTxTransfer();
352 }
353}
354
355ErrorCode IRAM_ATTR ESP32CDCJtag::TryStartTx(bool in_isr)
356{
357 if (in_tx_isr_.IsSet())
358 {
359 return ErrorCode::PENDING;
360 }
361
362 if (!tx_active_valid_)
363 {
364 (void)LoadActiveTxFromQueue(in_isr);
365 }
366
367 if (!tx_busy_.IsSet() && tx_active_valid_)
368 {
369 if (!StartActiveTransfer(in_isr))
370 {
371 ClearActiveTx();
372 return ErrorCode::FAILED;
373 }
374
375 if (!tx_busy_.IsSet() && tx_active_valid_)
376 {
377 OnTxTransferDone(in_isr, ErrorCode::OK);
378 }
379
380 if (!tx_pending_valid_)
381 {
382 (void)LoadPendingTxFromQueue(in_isr);
383 }
384 return ErrorCode::OK;
385 }
386
387 if (!tx_pending_valid_)
388 {
389 (void)LoadPendingTxFromQueue(in_isr);
390 }
391
392 return ErrorCode::PENDING;
393}
394
395void IRAM_ATTR ESP32CDCJtag::HandleInterrupt()
396{
397 const uint32_t status = usb_serial_jtag_ll_get_intsts_mask();
398
399 const uint32_t rx_status = status & kRxIntrMask;
400 if (rx_status != 0U)
401 {
402 usb_serial_jtag_ll_clr_intsts_mask(rx_status);
403
404 uint8_t rx_tmp[64] = {};
405 const int got = usb_serial_jtag_ll_read_rxfifo(rx_tmp, sizeof(rx_tmp));
406 if (got > 0)
407 {
408 PushRxBytes(rx_tmp, static_cast<size_t>(got), true);
409 }
410 }
411
412 const uint32_t tx_status = status & kTxIntrMask;
413 if (tx_status == 0U)
414 {
415 return;
416 }
417
418 usb_serial_jtag_ll_clr_intsts_mask(tx_status);
419
420 Flag::ScopedRestore tx_flag(in_tx_isr_);
421 const bool was_busy = tx_busy_.IsSet();
422 (void)PumpTx(true);
423 if (was_busy && !tx_busy_.IsSet())
424 {
425 OnTxTransferDone(true, ErrorCode::OK);
426 }
427}
428
429} // namespace LibXR
430
431#endif
LibXR 命名空间
Definition ch32_can.hpp:14
ErrorCode
定义错误码枚举
Definition libxr_def.hpp:64
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