1#include "esp_cdc_jtag.hpp"
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))
12#include "hal/usb_serial_jtag_ll.h"
13#include "soc/interrupts.h"
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;
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),
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)
34 ASSERT(tx_slot_storage_ !=
nullptr);
35 ASSERT(tx_slot_size_ > 0U);
37 tx_slot_a_ = tx_slot_storage_;
38 tx_slot_b_ = tx_slot_storage_ + tx_slot_size_;
43 if (SetConfig(config_) != ErrorCode::OK)
49 if (InitHardware() != ErrorCode::OK)
55ErrorCode ESP32CDCJtag::SetConfig(UART::Configuration config)
57 if ((config.data_bits != 8) || (config.stop_bits != 1) ||
58 (config.parity != UART::Parity::NO_PARITY))
60 return ErrorCode::ARG_ERR;
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);
80 intr_handle_ =
nullptr;
81 return ErrorCode::INIT_ERR;
84 usb_serial_jtag_ll_clr_intsts_mask(kAllIntrMask);
85 usb_serial_jtag_ll_ena_intr_mask(kRxIntrMask);
91void IRAM_ATTR ESP32CDCJtag::IsrEntry(
void* arg)
93 auto* cdc =
static_cast<ESP32CDCJtag*
>(arg);
96 cdc->HandleInterrupt();
100ErrorCode IRAM_ATTR ESP32CDCJtag::WriteFun(WritePort& port,
bool in_isr)
102 auto* cdc = CONTAINER_OF(&port, ESP32CDCJtag, _write_port);
103 return cdc->TryStartTx(in_isr);
106ErrorCode ESP32CDCJtag::ReadFun(ReadPort&,
bool) {
return ErrorCode::PENDING; }
108void IRAM_ATTR ESP32CDCJtag::ClearActiveTx()
110 tx_active_ptr_ =
nullptr;
112 tx_active_offset_ = 0;
113 tx_active_info_ = {};
114 tx_active_valid_ =
false;
117void IRAM_ATTR ESP32CDCJtag::ClearPendingTx()
119 tx_pending_ptr_ =
nullptr;
120 tx_pending_size_ = 0;
121 tx_pending_info_ = {};
122 tx_pending_valid_ =
false;
125void IRAM_ATTR ESP32CDCJtag::ResetTxState(
bool)
132bool IRAM_ATTR ESP32CDCJtag::DequeueTxToSlot(uint8_t* slot,
size_t& size,
133 WriteInfoBlock& info,
bool in_isr)
137 WriteInfoBlock peek_info = {};
138 if (write_port_->queue_info_->Peek(peek_info) != ErrorCode::OK)
143 if (peek_info.data.size_ > tx_slot_size_)
149 if (write_port_->queue_data_->PopBatch(slot, peek_info.data.size_) != ErrorCode::OK)
154 if (write_port_->queue_info_->Pop(info) != ErrorCode::OK)
159 size = peek_info.data.size_;
163bool IRAM_ATTR ESP32CDCJtag::LoadActiveTxFromQueue(
bool in_isr)
165 if (tx_active_valid_)
170 size_t active_size = 0U;
171 if (!DequeueTxToSlot(tx_slot_a_, active_size, tx_active_info_, in_isr))
176 tx_active_ptr_ = tx_slot_a_;
177 tx_active_size_ = active_size;
178 tx_active_valid_ =
true;
182bool IRAM_ATTR ESP32CDCJtag::LoadPendingTxFromQueue(
bool in_isr)
184 if (tx_pending_valid_)
189 uint8_t* pending_slot = tx_slot_b_;
190 if (tx_active_ptr_ == tx_slot_b_)
192 pending_slot = tx_slot_a_;
195 size_t pending_size = 0U;
196 if (!DequeueTxToSlot(pending_slot, pending_size, tx_pending_info_, in_isr))
201 tx_pending_ptr_ = pending_slot;
202 tx_pending_size_ = pending_size;
203 tx_pending_valid_ =
true;
207bool IRAM_ATTR ESP32CDCJtag::PumpTx(
bool)
209 while (tx_busy_.IsSet())
211 if (!tx_active_valid_ || (tx_active_ptr_ ==
nullptr) ||
212 (tx_active_offset_ >= tx_active_size_))
218 const uint32_t remain =
static_cast<uint32_t
>(tx_active_size_ - tx_active_offset_);
220 usb_serial_jtag_ll_write_txfifo(tx_active_ptr_ + tx_active_offset_, remain);
226 tx_active_offset_ +=
static_cast<size_t>(written);
227 if (tx_active_offset_ >= tx_active_size_)
237void IRAM_ATTR ESP32CDCJtag::PushRxBytes(
const uint8_t* data,
size_t size,
bool in_isr)
240 bool pushed_any =
false;
242 while (offset < size)
244 const size_t free_space = read_port_->queue_data_->EmptySize();
245 if (free_space == 0U)
250 const size_t chunk = std::min(free_space, size - offset);
251 if (read_port_->queue_data_->PushBatch(data + offset, chunk) != ErrorCode::OK)
262 read_port_->ProcessPendingReads(in_isr);
266bool IRAM_ATTR ESP32CDCJtag::StartActiveTransfer(
bool in_isr)
268 if (!tx_active_valid_ || (tx_active_ptr_ ==
nullptr) || (tx_active_size_ == 0U))
273 if (tx_busy_.TestAndSet())
278 tx_active_offset_ = 0;
279 usb_serial_jtag_ll_ena_intr_mask(kTxIntrMask);
280 (void)PumpTx(in_isr);
284bool IRAM_ATTR ESP32CDCJtag::StartAndReportActive(
bool in_isr)
286 if (!StartActiveTransfer(in_isr))
288 write_port_->Finish(in_isr, ErrorCode::FAILED, tx_active_info_);
294 write_port_->Finish(in_isr, ErrorCode::OK, tx_active_info_);
295 if (!tx_busy_.IsSet() && tx_active_valid_)
297 OnTxTransferDone(in_isr, ErrorCode::OK);
302void IRAM_ATTR ESP32CDCJtag::StopTxTransfer()
304 usb_serial_jtag_ll_txfifo_flush();
305 usb_serial_jtag_ll_disable_intr_mask(kTxIntrMask);
308void IRAM_ATTR ESP32CDCJtag::OnTxTransferDone(
bool in_isr, ErrorCode result)
310 Flag::ScopedRestore tx_flag(in_tx_isr_);
315 if ((result != ErrorCode::OK) && tx_pending_valid_)
317 write_port_->Finish(in_isr, ErrorCode::FAILED, tx_pending_info_);
321 if (result != ErrorCode::OK)
327 if (tx_pending_valid_)
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;
334 (void)StartAndReportActive(in_isr);
338 if (LoadActiveTxFromQueue(in_isr))
340 (void)StartAndReportActive(in_isr);
344 if (!tx_pending_valid_)
346 (void)LoadPendingTxFromQueue(in_isr);
349 if (!tx_busy_.IsSet() && !tx_active_valid_ && !tx_pending_valid_)
355ErrorCode IRAM_ATTR ESP32CDCJtag::TryStartTx(
bool in_isr)
357 if (in_tx_isr_.IsSet())
359 return ErrorCode::PENDING;
362 if (!tx_active_valid_)
364 (void)LoadActiveTxFromQueue(in_isr);
367 if (!tx_busy_.IsSet() && tx_active_valid_)
369 if (!StartActiveTransfer(in_isr))
372 return ErrorCode::FAILED;
375 if (!tx_busy_.IsSet() && tx_active_valid_)
377 OnTxTransferDone(in_isr, ErrorCode::OK);
380 if (!tx_pending_valid_)
382 (void)LoadPendingTxFromQueue(in_isr);
384 return ErrorCode::OK;
387 if (!tx_pending_valid_)
389 (void)LoadPendingTxFromQueue(in_isr);
392 return ErrorCode::PENDING;
395void IRAM_ATTR ESP32CDCJtag::HandleInterrupt()
397 const uint32_t status = usb_serial_jtag_ll_get_intsts_mask();
399 const uint32_t rx_status = status & kRxIntrMask;
402 usb_serial_jtag_ll_clr_intsts_mask(rx_status);
404 uint8_t rx_tmp[64] = {};
405 const int got = usb_serial_jtag_ll_read_rxfifo(rx_tmp,
sizeof(rx_tmp));
408 PushRxBytes(rx_tmp,
static_cast<size_t>(got),
true);
412 const uint32_t tx_status = status & kTxIntrMask;
418 usb_serial_jtag_ll_clr_intsts_mask(tx_status);
420 Flag::ScopedRestore tx_flag(in_tx_isr_);
421 const bool was_busy = tx_busy_.IsSet();
423 if (was_busy && !tx_busy_.IsSet())
425 OnTxTransferDone(
true, ErrorCode::OK);
ErrorCode(* ReadFun)(ReadPort &port, bool in_isr)
Function pointer type for read operations.
ErrorCode(* WriteFun)(WritePort &port, bool in_isr)
Function pointer type for write operations.